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:
olly 2016-11-30 08:46:49 -08:00 committed by Oliver Woodman
parent f702568776
commit 45c68a2fd5
4 changed files with 19 additions and 10 deletions

View File

@ -621,8 +621,9 @@ import java.util.List;
MimeTypes.APPLICATION_TTML, null, Format.NO_VALUE, 0, language, drmInitData,
0 /* subsample timing is absolute */);
} else if (childAtomType == Atom.TYPE_c608) {
// Defined by the QuickTime File Format specification.
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;
} else if (childAtomType == Atom.TYPE_camm) {
out.format = Format.createSampleFormat(Integer.toString(trackId),

View File

@ -75,8 +75,8 @@ public interface SubtitleDecoderFactory {
throw new IllegalArgumentException("Attempted to create decoder for unsupported format");
}
if (clazz == Cea608Decoder.class) {
return clazz.asSubclass(SubtitleDecoder.class)
.getConstructor(Integer.TYPE).newInstance(format.accessibilityChannel);
return clazz.asSubclass(SubtitleDecoder.class).getConstructor(String.class, Integer.TYPE)
.newInstance(format.sampleMimeType, format.accessibilityChannel);
} else {
return clazz.asSubclass(SubtitleDecoder.class).getConstructor().newInstance();
}
@ -102,6 +102,7 @@ public interface SubtitleDecoderFactory {
case MimeTypes.APPLICATION_TX3G:
return Class.forName("com.google.android.exoplayer2.text.tx3g.Tx3gDecoder");
case MimeTypes.APPLICATION_CEA608:
case MimeTypes.APPLICATION_MP4CEA608:
return Class.forName("com.google.android.exoplayer2.text.cea.Cea608Decoder");
default:
return null;

View File

@ -21,6 +21,7 @@ import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.Subtitle;
import com.google.android.exoplayer2.text.SubtitleDecoder;
import com.google.android.exoplayer2.text.SubtitleInputBuffer;
import com.google.android.exoplayer2.util.MimeTypes;
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.
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
* 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 StringBuilder captionStringBuilder;
private final int packetLength;
private final int selectedField;
private int captionMode;
@ -179,10 +183,11 @@ public final class Cea608Decoder extends CeaDecoder {
private byte repeatableControlCc1;
private byte repeatableControlCc2;
public Cea608Decoder(int accessibilityChannel) {
public Cea608Decoder(String mimeType, int accessibilityChannel) {
ccData = new ParsableByteArray();
captionStringBuilder = new StringBuilder();
packetLength = MimeTypes.APPLICATION_MP4CEA608.equals(mimeType) ? 2 : 3;
switch (accessibilityChannel) {
case 3:
case 4:
@ -238,8 +243,9 @@ public final class Cea608Decoder extends CeaDecoder {
ccData.reset(inputBuffer.data.array(), inputBuffer.data.limit());
boolean captionDataProcessed = false;
boolean isRepeatableControl = false;
while (ccData.bytesLeft() > 0) {
byte ccDataHeader = (byte) ccData.readUnsignedByte();
while (ccData.bytesLeft() >= packetLength) {
byte ccDataHeader = packetLength == 2 ? CC_IMPLICIT_DATA_HEADER
: (byte) ccData.readUnsignedByte();
byte ccData1 = (byte) (ccData.readUnsignedByte() & 0x7F);
byte ccData2 = (byte) (ccData.readUnsignedByte() & 0x7F);

View File

@ -70,7 +70,8 @@ public final class MimeTypes {
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_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_VOBSUB = BASE_TYPE_APPLICATION + "/vobsub";
public static final String APPLICATION_PGS = BASE_TYPE_APPLICATION + "/pgs";