Use separate mimeType for CEA-608 embedded in MP4
When CEA-608 is embedded in MP4 each packet consists of cc_data_1 and cc_data_2 only. The marker_bits, cc_valid and cc_type are implicit. As a result playback of CEA-608 embedded in MP4 broke when we started passing the extra byte for the TS case (and adjusted the decoder to assume the byte was present). This change introduces a special mimeType for the case where the byte is implicit (!). An alternative option was to insert the extra byte every 2 bytes in the MP4 extractor, but this is really quite fiddly to get right. Also made the loops in the 608/708 decoders robust against input of the wrong length. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=140609304
This commit is contained in:
parent
f702568776
commit
45c68a2fd5
@ -621,8 +621,9 @@ import java.util.List;
|
|||||||
MimeTypes.APPLICATION_TTML, null, Format.NO_VALUE, 0, language, drmInitData,
|
MimeTypes.APPLICATION_TTML, null, Format.NO_VALUE, 0, language, drmInitData,
|
||||||
0 /* subsample timing is absolute */);
|
0 /* subsample timing is absolute */);
|
||||||
} else if (childAtomType == Atom.TYPE_c608) {
|
} else if (childAtomType == Atom.TYPE_c608) {
|
||||||
|
// Defined by the QuickTime File Format specification.
|
||||||
out.format = Format.createTextSampleFormat(Integer.toString(trackId),
|
out.format = Format.createTextSampleFormat(Integer.toString(trackId),
|
||||||
MimeTypes.APPLICATION_CEA608, null, Format.NO_VALUE, 0, language, drmInitData);
|
MimeTypes.APPLICATION_MP4CEA608, null, Format.NO_VALUE, 0, language, drmInitData);
|
||||||
out.requiredSampleTransformation = Track.TRANSFORMATION_CEA608_CDAT;
|
out.requiredSampleTransformation = Track.TRANSFORMATION_CEA608_CDAT;
|
||||||
} else if (childAtomType == Atom.TYPE_camm) {
|
} else if (childAtomType == Atom.TYPE_camm) {
|
||||||
out.format = Format.createSampleFormat(Integer.toString(trackId),
|
out.format = Format.createSampleFormat(Integer.toString(trackId),
|
||||||
|
@ -75,8 +75,8 @@ public interface SubtitleDecoderFactory {
|
|||||||
throw new IllegalArgumentException("Attempted to create decoder for unsupported format");
|
throw new IllegalArgumentException("Attempted to create decoder for unsupported format");
|
||||||
}
|
}
|
||||||
if (clazz == Cea608Decoder.class) {
|
if (clazz == Cea608Decoder.class) {
|
||||||
return clazz.asSubclass(SubtitleDecoder.class)
|
return clazz.asSubclass(SubtitleDecoder.class).getConstructor(String.class, Integer.TYPE)
|
||||||
.getConstructor(Integer.TYPE).newInstance(format.accessibilityChannel);
|
.newInstance(format.sampleMimeType, format.accessibilityChannel);
|
||||||
} else {
|
} else {
|
||||||
return clazz.asSubclass(SubtitleDecoder.class).getConstructor().newInstance();
|
return clazz.asSubclass(SubtitleDecoder.class).getConstructor().newInstance();
|
||||||
}
|
}
|
||||||
@ -102,6 +102,7 @@ public interface SubtitleDecoderFactory {
|
|||||||
case MimeTypes.APPLICATION_TX3G:
|
case MimeTypes.APPLICATION_TX3G:
|
||||||
return Class.forName("com.google.android.exoplayer2.text.tx3g.Tx3gDecoder");
|
return Class.forName("com.google.android.exoplayer2.text.tx3g.Tx3gDecoder");
|
||||||
case MimeTypes.APPLICATION_CEA608:
|
case MimeTypes.APPLICATION_CEA608:
|
||||||
|
case MimeTypes.APPLICATION_MP4CEA608:
|
||||||
return Class.forName("com.google.android.exoplayer2.text.cea.Cea608Decoder");
|
return Class.forName("com.google.android.exoplayer2.text.cea.Cea608Decoder");
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
|
@ -21,6 +21,7 @@ import com.google.android.exoplayer2.text.Cue;
|
|||||||
import com.google.android.exoplayer2.text.Subtitle;
|
import com.google.android.exoplayer2.text.Subtitle;
|
||||||
import com.google.android.exoplayer2.text.SubtitleDecoder;
|
import com.google.android.exoplayer2.text.SubtitleDecoder;
|
||||||
import com.google.android.exoplayer2.text.SubtitleInputBuffer;
|
import com.google.android.exoplayer2.text.SubtitleInputBuffer;
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,6 +53,10 @@ public final class Cea608Decoder extends CeaDecoder {
|
|||||||
// The default number of rows to display in roll-up captions mode.
|
// The default number of rows to display in roll-up captions mode.
|
||||||
private static final int DEFAULT_CAPTIONS_ROW_COUNT = 4;
|
private static final int DEFAULT_CAPTIONS_ROW_COUNT = 4;
|
||||||
|
|
||||||
|
// An implied first byte for packets that are only 2 bytes long, consisting of marker bits
|
||||||
|
// (0b11111) + valid bit (0b1) + NTSC field 1 type bits (0b00).
|
||||||
|
private static final byte CC_IMPLICIT_DATA_HEADER = (byte) 0xFC;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Command initiating pop-on style captioning. Subsequent data should be loaded into a
|
* Command initiating pop-on style captioning. Subsequent data should be loaded into a
|
||||||
* non-displayed memory and held there until the {@link #CTRL_END_OF_CAPTION} command is received,
|
* non-displayed memory and held there until the {@link #CTRL_END_OF_CAPTION} command is received,
|
||||||
@ -164,9 +169,8 @@ public final class Cea608Decoder extends CeaDecoder {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private final ParsableByteArray ccData;
|
private final ParsableByteArray ccData;
|
||||||
|
|
||||||
private final StringBuilder captionStringBuilder;
|
private final StringBuilder captionStringBuilder;
|
||||||
|
private final int packetLength;
|
||||||
private final int selectedField;
|
private final int selectedField;
|
||||||
|
|
||||||
private int captionMode;
|
private int captionMode;
|
||||||
@ -179,10 +183,11 @@ public final class Cea608Decoder extends CeaDecoder {
|
|||||||
private byte repeatableControlCc1;
|
private byte repeatableControlCc1;
|
||||||
private byte repeatableControlCc2;
|
private byte repeatableControlCc2;
|
||||||
|
|
||||||
public Cea608Decoder(int accessibilityChannel) {
|
public Cea608Decoder(String mimeType, int accessibilityChannel) {
|
||||||
ccData = new ParsableByteArray();
|
ccData = new ParsableByteArray();
|
||||||
|
|
||||||
captionStringBuilder = new StringBuilder();
|
captionStringBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
packetLength = MimeTypes.APPLICATION_MP4CEA608.equals(mimeType) ? 2 : 3;
|
||||||
switch (accessibilityChannel) {
|
switch (accessibilityChannel) {
|
||||||
case 3:
|
case 3:
|
||||||
case 4:
|
case 4:
|
||||||
@ -238,8 +243,9 @@ public final class Cea608Decoder extends CeaDecoder {
|
|||||||
ccData.reset(inputBuffer.data.array(), inputBuffer.data.limit());
|
ccData.reset(inputBuffer.data.array(), inputBuffer.data.limit());
|
||||||
boolean captionDataProcessed = false;
|
boolean captionDataProcessed = false;
|
||||||
boolean isRepeatableControl = false;
|
boolean isRepeatableControl = false;
|
||||||
while (ccData.bytesLeft() > 0) {
|
while (ccData.bytesLeft() >= packetLength) {
|
||||||
byte ccDataHeader = (byte) ccData.readUnsignedByte();
|
byte ccDataHeader = packetLength == 2 ? CC_IMPLICIT_DATA_HEADER
|
||||||
|
: (byte) ccData.readUnsignedByte();
|
||||||
byte ccData1 = (byte) (ccData.readUnsignedByte() & 0x7F);
|
byte ccData1 = (byte) (ccData.readUnsignedByte() & 0x7F);
|
||||||
byte ccData2 = (byte) (ccData.readUnsignedByte() & 0x7F);
|
byte ccData2 = (byte) (ccData.readUnsignedByte() & 0x7F);
|
||||||
|
|
||||||
|
@ -70,7 +70,8 @@ public final class MimeTypes {
|
|||||||
public static final String APPLICATION_TTML = BASE_TYPE_APPLICATION + "/ttml+xml";
|
public static final String APPLICATION_TTML = BASE_TYPE_APPLICATION + "/ttml+xml";
|
||||||
public static final String APPLICATION_M3U8 = BASE_TYPE_APPLICATION + "/x-mpegURL";
|
public static final String APPLICATION_M3U8 = BASE_TYPE_APPLICATION + "/x-mpegURL";
|
||||||
public static final String APPLICATION_TX3G = BASE_TYPE_APPLICATION + "/x-quicktime-tx3g";
|
public static final String APPLICATION_TX3G = BASE_TYPE_APPLICATION + "/x-quicktime-tx3g";
|
||||||
public static final String APPLICATION_MP4VTT = BASE_TYPE_APPLICATION + "/x-mp4vtt";
|
public static final String APPLICATION_MP4VTT = BASE_TYPE_APPLICATION + "/x-mp4-vtt";
|
||||||
|
public static final String APPLICATION_MP4CEA608 = BASE_TYPE_APPLICATION + "/x-mp4-cea-608";
|
||||||
public static final String APPLICATION_RAWCC = BASE_TYPE_APPLICATION + "/x-rawcc";
|
public static final String APPLICATION_RAWCC = BASE_TYPE_APPLICATION + "/x-rawcc";
|
||||||
public static final String APPLICATION_VOBSUB = BASE_TYPE_APPLICATION + "/vobsub";
|
public static final String APPLICATION_VOBSUB = BASE_TYPE_APPLICATION + "/vobsub";
|
||||||
public static final String APPLICATION_PGS = BASE_TYPE_APPLICATION + "/pgs";
|
public static final String APPLICATION_PGS = BASE_TYPE_APPLICATION + "/pgs";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user