Skip to content

Commit

Permalink
Add support for CEA608 in MP4 and fMP4
Browse files Browse the repository at this point in the history
Issue: #1658
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=127312721
  • Loading branch information
AquilesCanta authored and ojw28 committed Jul 15, 2016
1 parent e4a3483 commit a0c1595
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
public static final int TYPE_tx3g = Util.getIntegerCodeForString("tx3g");
public static final int TYPE_wvtt = Util.getIntegerCodeForString("wvtt");
public static final int TYPE_stpp = Util.getIntegerCodeForString("stpp");
public static final int TYPE_c608 = Util.getIntegerCodeForString("c608");
public static final int TYPE_samr = Util.getIntegerCodeForString("samr");
public static final int TYPE_sawb = Util.getIntegerCodeForString("sawb");
public static final int TYPE_udta = Util.getIntegerCodeForString("udta");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
private static final int TYPE_text = Util.getIntegerCodeForString("text");
private static final int TYPE_sbtl = Util.getIntegerCodeForString("sbtl");
private static final int TYPE_subt = Util.getIntegerCodeForString("subt");
private static final int TYPE_clcp = Util.getIntegerCodeForString("clcp");

/**
* Parses a trak atom (defined in 14496-12).
Expand Down Expand Up @@ -84,8 +85,8 @@ public static Track parseTrak(Atom.ContainerAtom trak, Atom.LeafAtom mvhd, long
Pair<long[], long[]> edtsData = parseEdts(trak.getContainerAtomOfType(Atom.TYPE_edts));
return stsdData.format == null ? null
: new Track(tkhdData.id, trackType, mdhdData.first, movieTimescale, durationUs,
stsdData.format, stsdData.trackEncryptionBoxes, stsdData.nalUnitLengthFieldLength,
edtsData.first, edtsData.second);
stsdData.format, stsdData.requiredSampleTransformation, stsdData.trackEncryptionBoxes,
stsdData.nalUnitLengthFieldLength, edtsData.first, edtsData.second);
}

/**
Expand Down Expand Up @@ -544,7 +545,8 @@ private static int parseHdlr(ParsableByteArray hdlr) {
return C.TRACK_TYPE_AUDIO;
} else if (trackType == TYPE_vide) {
return C.TRACK_TYPE_VIDEO;
} else if (trackType == TYPE_text || trackType == TYPE_sbtl || trackType == TYPE_subt) {
} else if (trackType == TYPE_text || trackType == TYPE_sbtl || trackType == TYPE_subt
|| trackType == TYPE_clcp) {
return C.TRACK_TYPE_TEXT;
} else {
return C.TRACK_TYPE_UNKNOWN;
Expand Down Expand Up @@ -621,6 +623,10 @@ private static StsdData parseStsd(ParsableByteArray stsd, int trackId, int rotat
out.format = Format.createTextSampleFormat(Integer.toString(trackId),
MimeTypes.APPLICATION_TTML, null, Format.NO_VALUE, 0, language, drmInitData,
0 /* subsample timing is absolute */);
} else if (childAtomType == Atom.TYPE_c608) {
out.format = Format.createTextSampleFormat(Integer.toString(trackId),
MimeTypes.APPLICATION_EIA608, null, Format.NO_VALUE, 0, language, drmInitData);
out.requiredSampleTransformation = Track.TRANSFORMATION_CEA608_CDAT;
}
stsd.setPosition(childStartPosition + childAtomSize);
}
Expand Down Expand Up @@ -1173,10 +1179,12 @@ private static final class StsdData {

public Format format;
public int nalUnitLengthFieldLength;
public int requiredSampleTransformation;

public StsdData(int numberOfEntries) {
trackEncryptionBoxes = new TrackEncryptionBox[numberOfEntries];
nalUnitLengthFieldLength = -1;
requiredSampleTransformation = Track.TRANSFORMATION_NONE;
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,10 @@ private boolean readSample(ExtractorInput input) throws IOException, Interrupted
} else {
sampleBytesWritten = 0;
}
if (currentTrackBundle.track.sampleTransformation == Track.TRANSFORMATION_CEA608_CDAT) {
sampleSize -= Atom.HEADER_SIZE;
input.skipFully(Atom.HEADER_SIZE);
}
parserState = STATE_READING_SAMPLE_CONTINUE;
sampleCurrentNalBytesRemaining = 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,13 +387,19 @@ private int readSample(ExtractorInput input, PositionHolder positionHolder)
TrackOutput trackOutput = track.trackOutput;
int sampleIndex = track.sampleIndex;
long position = track.sampleTable.offsets[sampleIndex];
int sampleSize = track.sampleTable.sizes[sampleIndex];
if (track.track.sampleTransformation == Track.TRANSFORMATION_CEA608_CDAT) {
// The sample information is contained in a cdat atom. The header must be discarded for
// committing.
position += Atom.HEADER_SIZE;
sampleSize -= Atom.HEADER_SIZE;
}
long skipAmount = position - input.getPosition() + sampleBytesWritten;
if (skipAmount < 0 || skipAmount >= RELOAD_MINIMUM_SEEK_DISTANCE) {
positionHolder.position = position;
return RESULT_SEEK;
}
input.skipFully((int) skipAmount);
int sampleSize = track.sampleTable.sizes[sampleIndex];
if (track.track.nalUnitLengthFieldLength != -1) {
// Zero the top three bytes of the array that we'll use to parse nal unit lengths, in case
// they're only 1 or 2 bytes long.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@
*/
public final class Track {

/**
* A no-op sample transformation.
*/
public static final int TRANSFORMATION_NONE = 0;
/**
* A transformation for caption samples in cdat atoms.
*/
public static final int TRANSFORMATION_CEA608_CDAT = 1;

/**
* The track identifier.
*/
Expand Down Expand Up @@ -53,6 +62,12 @@ public final class Track {
*/
public final Format format;

/**
* One of {@code TRANSFORMATION_*}. Defines the transformation to apply before outputting each
* sample.
*/
public final int sampleTransformation;

/**
* Track encryption boxes for the different track sample descriptions. Entries may be null.
*/
Expand All @@ -75,14 +90,16 @@ public final class Track {
public final int nalUnitLengthFieldLength;

public Track(int id, int type, long timescale, long movieTimescale, long durationUs,
Format format, TrackEncryptionBox[] sampleDescriptionEncryptionBoxes,
int nalUnitLengthFieldLength, long[] editListDurations, long[] editListMediaTimes) {
Format format, int sampleTransformation,
TrackEncryptionBox[] sampleDescriptionEncryptionBoxes, int nalUnitLengthFieldLength,
long[] editListDurations, long[] editListMediaTimes) {
this.id = id;
this.type = type;
this.timescale = timescale;
this.movieTimescale = movieTimescale;
this.durationUs = durationUs;
this.format = format;
this.sampleTransformation = sampleTransformation;
this.sampleDescriptionEncryptionBoxes = sampleDescriptionEncryptionBoxes;
this.nalUnitLengthFieldLength = nalUnitLengthFieldLength;
this.editListDurations = editListDurations;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,26 @@ public void consume(long pesTimeUs, ParsableByteArray seiBuffer) {
} while (b == 0xFF);
// Process the payload.
if (Eia608Parser.isSeiMessageEia608(payloadType, payloadSize, seiBuffer)) {
output.sampleData(seiBuffer, payloadSize);
output.sampleMetadata(pesTimeUs, C.BUFFER_FLAG_KEY_FRAME, payloadSize, 0, null);
// Ignore country_code (1) + provider_code (2) + user_identifier (4)
// + user_data_type_code (1).
seiBuffer.skipBytes(8);
// Ignore first three bits: reserved (1) + process_cc_data_flag (1) + zero_bit (1).
int ccCount = seiBuffer.readUnsignedByte() & 0x1F;
seiBuffer.skipBytes(1);
int sampleBytes = 0;
for (int i = 0; i < ccCount; i++) {
int ccValidityAndType = seiBuffer.readUnsignedByte() & 0x07;
// Check that validity == 1 and type == 0.
if (ccValidityAndType != 0x04) {
seiBuffer.skipBytes(2);
} else {
sampleBytes += 2;
output.sampleData(seiBuffer, 2);
}
}
output.sampleMetadata(pesTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleBytes, 0, null);
// Ignore trailing information in SEI, if any.
seiBuffer.skipBytes(payloadSize - (10 + ccCount * 3));
} else {
seiBuffer.skipBytes(payloadSize);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ public SmoothStreamingChunkSource(Loader manifestLoader, SmoothStreamingManifest
for (int j = 0; j < formats.length; j++) {
int nalUnitLengthFieldLength = streamElement.type == C.TRACK_TYPE_VIDEO ? 4 : -1;
Track track = new Track(j, streamElement.type, streamElement.timescale, C.UNSET_TIME_US,
manifest.durationUs, formats[j], trackEncryptionBoxes, nalUnitLengthFieldLength,
null, null);
manifest.durationUs, formats[j], Track.TRANSFORMATION_NONE, trackEncryptionBoxes,
nalUnitLengthFieldLength, null, null);
FragmentedMp4Extractor extractor = new FragmentedMp4Extractor(
FragmentedMp4Extractor.FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME
| FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_TFDT_BOX, track);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,10 @@
import com.google.android.exoplayer2.text.SubtitleOutputBuffer;
import com.google.android.exoplayer2.text.SubtitleParser;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray;

import android.text.TextUtils;

import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.TreeSet;

Expand Down Expand Up @@ -168,7 +166,7 @@ public final class Eia608Parser implements SubtitleParser {
private final LinkedList<SubtitleOutputBuffer> availableOutputBuffers;
private final TreeSet<SubtitleInputBuffer> queuedInputBuffers;

private final ParsableBitArray seiBuffer;
private final ParsableByteArray ccData;

private final StringBuilder captionStringBuilder;

Expand Down Expand Up @@ -197,7 +195,7 @@ public Eia608Parser() {
}
queuedInputBuffers = new TreeSet<>();

seiBuffer = new ParsableBitArray();
ccData = new ParsableByteArray();

captionStringBuilder = new StringBuilder();

Expand Down Expand Up @@ -310,67 +308,38 @@ public void release() {
}

private void decode(SubtitleInputBuffer inputBuffer) {
ByteBuffer inputData = inputBuffer.data;
int inputSize = inputData.limit();
if (inputSize < 10) {
return;
}

seiBuffer.reset(inputData.array(), inputSize);
// country_code (8) + provider_code (16) + user_identifier (32) + user_data_type_code (8) +
// reserved (1) + process_cc_data_flag (1) + zero_bit (1)
seiBuffer.skipBits(67);
int ccCount = seiBuffer.readBits(5);
seiBuffer.skipBits(8);

ccData.reset(inputBuffer.data.array(), inputBuffer.data.limit());
boolean captionDataProcessed = false;
boolean isRepeatableControl = false;
for (int i = 0; i < ccCount; i++) {
seiBuffer.skipBits(5); // one_bit + reserved
boolean ccValid = seiBuffer.readBit();
if (!ccValid) {
seiBuffer.skipBits(18);
continue;
}
int ccType = seiBuffer.readBits(2);
if (ccType != 0) {
seiBuffer.skipBits(16);
continue;
}
seiBuffer.skipBits(1);
byte ccData1 = (byte) seiBuffer.readBits(7);
seiBuffer.skipBits(1);
byte ccData2 = (byte) seiBuffer.readBits(7);
while (ccData.bytesLeft() > 0) {
byte ccData1 = (byte) (ccData.readUnsignedByte() & 0x7F);
byte ccData2 = (byte) (ccData.readUnsignedByte() & 0x7F);

// Ignore empty captions.
if (ccData1 == 0 && ccData2 == 0) {
continue;
}

// If we've reached this point then there is data to process; flag that work has been done.
captionDataProcessed = true;

// Special North American character set.
// ccData2 - P|0|1|1|X|X|X|X
if ((ccData1 == 0x11 || ccData1 == 0x19)
&& ((ccData2 & 0x70) == 0x30)) {
if ((ccData1 == 0x11 || ccData1 == 0x19) && ((ccData2 & 0x70) == 0x30)) {
captionStringBuilder.append(getSpecialChar(ccData2));
continue;
}

// Extended Spanish/Miscellaneous and French character set.
// ccData2 - P|0|1|X|X|X|X|X
if ((ccData1 == 0x12 || ccData1 == 0x1A)
&& ((ccData2 & 0x60) == 0x20)) {
if ((ccData1 == 0x12 || ccData1 == 0x1A) && ((ccData2 & 0x60) == 0x20)) {
backspace(); // Remove standard equivalent of the special extended char.
captionStringBuilder.append(getExtendedEsFrChar(ccData2));
continue;
}

// Extended Portuguese and German/Danish character set.
// ccData2 - P|0|1|X|X|X|X|X
if ((ccData1 == 0x13 || ccData1 == 0x1B)
&& ((ccData2 & 0x60) == 0x20)) {
if ((ccData1 == 0x13 || ccData1 == 0x1B) && ((ccData2 & 0x60) == 0x20)) {
backspace(); // Remove standard equivalent of the special extended char.
captionStringBuilder.append(getExtendedPtDeChar(ccData2));
continue;
Expand Down

0 comments on commit a0c1595

Please sign in to comment.