Support CEA 608/708 captions in MPEG2 TS stream

[Problem] Sarnoff spec complaince tests streams are MPEG2 TS.
Currently, parsing CC data from MPEG2 TS is not supported

[Solution] Parsed CC data from MPEG2 stream because all Sarnoff streams are MPEG2
parent 18d208ab
...@@ -80,6 +80,12 @@ public final class C { ...@@ -80,6 +80,12 @@ public final class C {
/** The number of bits per byte. */ /** The number of bits per byte. */
public static final int BITS_PER_BYTE = 8; public static final int BITS_PER_BYTE = 8;
/** non-Wide aspect ratio */
public static final int NON_WIDE_ASPECT_RATIO_TYPE = 1;
/** Wide aspect ratio */
public static final int WIDE_ASPECT_RATIO_TYPE = 2;
/** /**
* The name of the ASCII charset. * The name of the ASCII charset.
*/ */
......
...@@ -15,9 +15,11 @@ ...@@ -15,9 +15,11 @@
*/ */
package com.google.android.exoplayer2; package com.google.android.exoplayer2;
import android.os.Bundle;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import com.google.android.exoplayer2.drm.DrmInitData; import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
...@@ -44,6 +46,7 @@ public final class Format implements Parcelable { ...@@ -44,6 +46,7 @@ public final class Format implements Parcelable {
*/ */
public static final long OFFSET_SAMPLE_RELATIVE = Long.MAX_VALUE; public static final long OFFSET_SAMPLE_RELATIVE = Long.MAX_VALUE;
/** An identifier for the format, or null if unknown or not applicable. */ /** An identifier for the format, or null if unknown or not applicable. */
public final @Nullable String id; public final @Nullable String id;
/** /**
...@@ -148,7 +151,16 @@ public final class Format implements Parcelable { ...@@ -148,7 +151,16 @@ public final class Format implements Parcelable {
*/ */
public final int encoderPadding; public final int encoderPadding;
/** A Bundle of custom parameters. Could be null or empty */
public final Bundle params;
// Audio and text specific. // Audio and text specific.
/**
* Indicates if the video is wide aspect ratio (16:9) or not (4:3)
* Only influences captions if the lines are not middle aligned.
* Values are {@link C#NON_WIDE_ASPECT_RATIO} or {@link C#WIDE_ASPECT_RATIO},
*/
public static final String KEY_ASPECT_RATIO_TYPE = "aspect-ratio-type";
/** /**
* Track selection flags. * Track selection flags.
...@@ -183,7 +195,7 @@ public final class Format implements Parcelable { ...@@ -183,7 +195,7 @@ public final class Format implements Parcelable {
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, width, return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, width,
height, frameRate, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, height, frameRate, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, selectionFlags, null, NO_VALUE, OFFSET_SAMPLE_RELATIVE, NO_VALUE, NO_VALUE, selectionFlags, null, NO_VALUE, OFFSET_SAMPLE_RELATIVE,
initializationData, null, null); initializationData, null, null, null);
} }
public static Format createVideoSampleFormat( public static Format createVideoSampleFormat(
...@@ -238,7 +250,7 @@ public final class Format implements Parcelable { ...@@ -238,7 +250,7 @@ public final class Format implements Parcelable {
return new Format(id, null, sampleMimeType, codecs, bitrate, maxInputSize, width, height, return new Format(id, null, sampleMimeType, codecs, bitrate, maxInputSize, width, height,
frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode,
colorInfo, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, 0, null, NO_VALUE, colorInfo, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, 0, null, NO_VALUE,
OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData, null); OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData, null, null);
} }
// Audio. // Audio.
...@@ -257,7 +269,7 @@ public final class Format implements Parcelable { ...@@ -257,7 +269,7 @@ public final class Format implements Parcelable {
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, channelCount, sampleRate, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, channelCount, sampleRate,
NO_VALUE, NO_VALUE, NO_VALUE, selectionFlags, language, NO_VALUE, OFFSET_SAMPLE_RELATIVE, NO_VALUE, NO_VALUE, NO_VALUE, selectionFlags, language, NO_VALUE, OFFSET_SAMPLE_RELATIVE,
initializationData, null, null); initializationData, null, null, null);
} }
public static Format createAudioSampleFormat( public static Format createAudioSampleFormat(
...@@ -313,7 +325,7 @@ public final class Format implements Parcelable { ...@@ -313,7 +325,7 @@ public final class Format implements Parcelable {
return new Format(id, null, sampleMimeType, codecs, bitrate, maxInputSize, NO_VALUE, NO_VALUE, return new Format(id, null, sampleMimeType, codecs, bitrate, maxInputSize, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, channelCount, sampleRate, pcmEncoding, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, channelCount, sampleRate, pcmEncoding,
encoderDelay, encoderPadding, selectionFlags, language, NO_VALUE, OFFSET_SAMPLE_RELATIVE, encoderDelay, encoderPadding, selectionFlags, language, NO_VALUE, OFFSET_SAMPLE_RELATIVE,
initializationData, drmInitData, metadata); initializationData, drmInitData, metadata, null);
} }
// Text. // Text.
...@@ -342,7 +354,7 @@ public final class Format implements Parcelable { ...@@ -342,7 +354,7 @@ public final class Format implements Parcelable {
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, NO_VALUE, selectionFlags, language, accessibilityChannel, NO_VALUE, NO_VALUE, NO_VALUE, selectionFlags, language, accessibilityChannel,
OFFSET_SAMPLE_RELATIVE, null, null, null); OFFSET_SAMPLE_RELATIVE, null, null, null, null);
} }
public static Format createTextSampleFormat( public static Format createTextSampleFormat(
...@@ -395,15 +407,34 @@ public final class Format implements Parcelable { ...@@ -395,15 +407,34 @@ public final class Format implements Parcelable {
@Nullable String codecs, @Nullable String codecs,
int bitrate, int bitrate,
@C.SelectionFlags int selectionFlags, @C.SelectionFlags int selectionFlags,
String language,
int accessibilityChannel,
DrmInitData drmInitData,
Bundle params) {
return new Format(id, null, sampleMimeType, codecs, bitrate,
NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, NO_VALUE,
null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, selectionFlags, language, accessibilityChannel, OFFSET_SAMPLE_RELATIVE,
null, drmInitData, null, params);
}
public static Format createTextSampleFormat(
@Nullable String id,
@Nullable String sampleMimeType,
@Nullable String codecs,
int bitrate,
@C.SelectionFlags int selectionFlags,
@Nullable String language, @Nullable String language,
int accessibilityChannel, int accessibilityChannel,
@Nullable DrmInitData drmInitData, @Nullable DrmInitData drmInitData,
long subsampleOffsetUs, long subsampleOffsetUs,
List<byte[]> initializationData) { List<byte[]> initializationData
) {
return new Format(id, null, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, return new Format(id, null, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, selectionFlags, language, accessibilityChannel, subsampleOffsetUs, NO_VALUE, selectionFlags, language, accessibilityChannel, subsampleOffsetUs,
initializationData, drmInitData, null); initializationData, drmInitData, null, null);
} }
// Image. // Image.
...@@ -443,7 +474,7 @@ public final class Format implements Parcelable { ...@@ -443,7 +474,7 @@ public final class Format implements Parcelable {
OFFSET_SAMPLE_RELATIVE, OFFSET_SAMPLE_RELATIVE,
initializationData, initializationData,
drmInitData, drmInitData,
null); null, null);
} }
// Generic. // Generic.
...@@ -459,14 +490,14 @@ public final class Format implements Parcelable { ...@@ -459,14 +490,14 @@ public final class Format implements Parcelable {
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, selectionFlags, language, NO_VALUE, OFFSET_SAMPLE_RELATIVE, null, null, NO_VALUE, NO_VALUE, selectionFlags, language, NO_VALUE, OFFSET_SAMPLE_RELATIVE, null, null,
null); null, null);
} }
public static Format createSampleFormat( public static Format createSampleFormat(
@Nullable String id, @Nullable String sampleMimeType, long subsampleOffsetUs) { @Nullable String id, @Nullable String sampleMimeType, long subsampleOffsetUs) {
return new Format(id, null, sampleMimeType, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, return new Format(id, null, sampleMimeType, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, 0, null, NO_VALUE, subsampleOffsetUs, null, null, null); NO_VALUE, 0, null, NO_VALUE, subsampleOffsetUs, null, null, null, null);
} }
public static Format createSampleFormat( public static Format createSampleFormat(
...@@ -477,7 +508,7 @@ public final class Format implements Parcelable { ...@@ -477,7 +508,7 @@ public final class Format implements Parcelable {
@Nullable DrmInitData drmInitData) { @Nullable DrmInitData drmInitData) {
return new Format(id, null, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, return new Format(id, null, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, 0, null, NO_VALUE, OFFSET_SAMPLE_RELATIVE, null, drmInitData, null); NO_VALUE, 0, null, NO_VALUE, OFFSET_SAMPLE_RELATIVE, null, drmInitData, null, null);
} }
/* package */ Format( /* package */ Format(
...@@ -506,7 +537,8 @@ public final class Format implements Parcelable { ...@@ -506,7 +537,8 @@ public final class Format implements Parcelable {
long subsampleOffsetUs, long subsampleOffsetUs,
@Nullable List<byte[]> initializationData, @Nullable List<byte[]> initializationData,
@Nullable DrmInitData drmInitData, @Nullable DrmInitData drmInitData,
@Nullable Metadata metadata) { @Nullable Metadata metadata,
Bundle params) {
this.id = id; this.id = id;
this.containerMimeType = containerMimeType; this.containerMimeType = containerMimeType;
this.sampleMimeType = sampleMimeType; this.sampleMimeType = sampleMimeType;
...@@ -535,6 +567,7 @@ public final class Format implements Parcelable { ...@@ -535,6 +567,7 @@ public final class Format implements Parcelable {
: initializationData; : initializationData;
this.drmInitData = drmInitData; this.drmInitData = drmInitData;
this.metadata = metadata; this.metadata = metadata;
this.params = params;
} }
@SuppressWarnings("ResourceType") @SuppressWarnings("ResourceType")
...@@ -570,6 +603,7 @@ public final class Format implements Parcelable { ...@@ -570,6 +603,7 @@ public final class Format implements Parcelable {
} }
drmInitData = in.readParcelable(DrmInitData.class.getClassLoader()); drmInitData = in.readParcelable(DrmInitData.class.getClassLoader());
metadata = in.readParcelable(Metadata.class.getClassLoader()); metadata = in.readParcelable(Metadata.class.getClassLoader());
params = in.readBundle(getClass().getClassLoader());
} }
public Format copyWithMaxInputSize(int maxInputSize) { public Format copyWithMaxInputSize(int maxInputSize) {
...@@ -577,7 +611,7 @@ public final class Format implements Parcelable { ...@@ -577,7 +611,7 @@ public final class Format implements Parcelable {
height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode,
colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData, selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
drmInitData, metadata); drmInitData, metadata, params);
} }
public Format copyWithSubsampleOffsetUs(long subsampleOffsetUs) { public Format copyWithSubsampleOffsetUs(long subsampleOffsetUs) {
...@@ -585,7 +619,7 @@ public final class Format implements Parcelable { ...@@ -585,7 +619,7 @@ public final class Format implements Parcelable {
height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode,
colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData, selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
drmInitData, metadata); drmInitData, metadata, params);
} }
public Format copyWithContainerInfo( public Format copyWithContainerInfo(
...@@ -601,7 +635,7 @@ public final class Format implements Parcelable { ...@@ -601,7 +635,7 @@ public final class Format implements Parcelable {
height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode,
colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData, selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
drmInitData, metadata); drmInitData, metadata, params);
} }
@SuppressWarnings("ReferenceEquality") @SuppressWarnings("ReferenceEquality")
...@@ -622,7 +656,7 @@ public final class Format implements Parcelable { ...@@ -622,7 +656,7 @@ public final class Format implements Parcelable {
height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode,
colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData, selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
drmInitData, metadata); drmInitData, metadata, params);
} }
public Format copyWithGaplessInfo(int encoderDelay, int encoderPadding) { public Format copyWithGaplessInfo(int encoderDelay, int encoderPadding) {
...@@ -630,7 +664,7 @@ public final class Format implements Parcelable { ...@@ -630,7 +664,7 @@ public final class Format implements Parcelable {
height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode,
colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData, selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
drmInitData, metadata); drmInitData, metadata, params);
} }
public Format copyWithDrmInitData(@Nullable DrmInitData drmInitData) { public Format copyWithDrmInitData(@Nullable DrmInitData drmInitData) {
...@@ -638,7 +672,7 @@ public final class Format implements Parcelable { ...@@ -638,7 +672,7 @@ public final class Format implements Parcelable {
height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode,
colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData, selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
drmInitData, metadata); drmInitData, metadata, params);
} }
public Format copyWithMetadata(@Nullable Metadata metadata) { public Format copyWithMetadata(@Nullable Metadata metadata) {
...@@ -646,7 +680,7 @@ public final class Format implements Parcelable { ...@@ -646,7 +680,7 @@ public final class Format implements Parcelable {
height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode,
colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData, selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
drmInitData, metadata); drmInitData, metadata, params);
} }
public Format copyWithRotationDegrees(int rotationDegrees) { public Format copyWithRotationDegrees(int rotationDegrees) {
...@@ -654,7 +688,7 @@ public final class Format implements Parcelable { ...@@ -654,7 +688,7 @@ public final class Format implements Parcelable {
height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode,
colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData, selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
drmInitData, metadata); drmInitData, metadata, params);
} }
/** /**
...@@ -825,6 +859,8 @@ public final class Format implements Parcelable { ...@@ -825,6 +859,8 @@ public final class Format implements Parcelable {
} }
dest.writeParcelable(drmInitData, 0); dest.writeParcelable(drmInitData, 0);
dest.writeParcelable(metadata, 0); dest.writeParcelable(metadata, 0);
dest.writeBundle(params);
} }
public static final Creator<Format> CREATOR = new Creator<Format>() { public static final Creator<Format> CREATOR = new Creator<Format>() {
......
...@@ -15,8 +15,11 @@ ...@@ -15,8 +15,11 @@
*/ */
package com.google.android.exoplayer2.extractor.ts; package com.google.android.exoplayer2.extractor.ts;
import android.os.Bundle;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import android.util.SparseArray; import android.util.SparseArray;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.EsInfo; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.EsInfo;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
...@@ -79,6 +82,13 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact ...@@ -79,6 +82,13 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
*/ */
public DefaultTsPayloadReaderFactory(@Flags int flags, List<Format> closedCaptionFormats) { public DefaultTsPayloadReaderFactory(@Flags int flags, List<Format> closedCaptionFormats) {
this.flags = flags; this.flags = flags;
if (!isSet(FLAG_OVERRIDE_CAPTION_DESCRIPTORS) && closedCaptionFormats.isEmpty()) {
closedCaptionFormats = new ArrayList();
closedCaptionFormats.add(Format.createTextSampleFormat(null,
MimeTypes.APPLICATION_CEA608, 0, null));
closedCaptionFormats.add(Format.createTextSampleFormat(null,
MimeTypes.APPLICATION_CEA708, 0, null));
}
this.closedCaptionFormats = closedCaptionFormats; this.closedCaptionFormats = closedCaptionFormats;
} }
...@@ -106,7 +116,7 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact ...@@ -106,7 +116,7 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
case TsExtractor.TS_STREAM_TYPE_HDMV_DTS: case TsExtractor.TS_STREAM_TYPE_HDMV_DTS:
return new PesReader(new DtsReader(esInfo.language)); return new PesReader(new DtsReader(esInfo.language));
case TsExtractor.TS_STREAM_TYPE_H262: case TsExtractor.TS_STREAM_TYPE_H262:
return new PesReader(new H262Reader()); return new PesReader(new H262Reader(buildUserDataReader(esInfo)));
case TsExtractor.TS_STREAM_TYPE_H264: case TsExtractor.TS_STREAM_TYPE_H264:
return isSet(FLAG_IGNORE_H264_STREAM) ? null return isSet(FLAG_IGNORE_H264_STREAM) ? null
: new PesReader(new H264Reader(buildSeiReader(esInfo), : new PesReader(new H264Reader(buildSeiReader(esInfo),
...@@ -136,8 +146,34 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact ...@@ -136,8 +146,34 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
* @return A {@link SeiReader} for closed caption tracks. * @return A {@link SeiReader} for closed caption tracks.
*/ */
private SeiReader buildSeiReader(EsInfo esInfo) { private SeiReader buildSeiReader(EsInfo esInfo) {
return new SeiReader(getCCformats(esInfo));
}
/**
* If {@link #FLAG_OVERRIDE_CAPTION_DESCRIPTORS} is set, returns a {@link UserDataReader} for
* {@link #closedCaptionFormats}. If unset, parses the PMT descriptor information and returns a
* {@link UserDataReader} for the declared formats, or {@link #closedCaptionFormats} if the descriptor
* is not present.
*
* @param esInfo The {@link EsInfo} passed to {@link #createPayloadReader(int, EsInfo)}.
* @return A {@link UserDataReader} for closed caption tracks.
*/
private UserDataReader buildUserDataReader(EsInfo esInfo) {
return new UserDataReader(getCCformats(esInfo));
}
/**
* If {@link #FLAG_OVERRIDE_CAPTION_DESCRIPTORS} is set, returns a {@link List<Format>} of
* {@link #closedCaptionFormats}. If unset, parses the PMT descriptor information and returns a
* {@link List<Format>} for the declared formats, or {@link #closedCaptionFormats} if the descriptor
* is not present.
*
* @param esInfo The {@link EsInfo} passed to {@link #createPayloadReader(int, EsInfo)}.
* @return A {@link List<Format>} containing list of closed caption formats.
*/
private List<Format> getCCformats(EsInfo esInfo) {
if (isSet(FLAG_OVERRIDE_CAPTION_DESCRIPTORS)) { if (isSet(FLAG_OVERRIDE_CAPTION_DESCRIPTORS)) {
return new SeiReader(closedCaptionFormats); return closedCaptionFormats;
} }
ParsableByteArray scratchDescriptorData = new ParsableByteArray(esInfo.descriptorBytes); ParsableByteArray scratchDescriptorData = new ParsableByteArray(esInfo.descriptorBytes);
List<Format> closedCaptionFormats = this.closedCaptionFormats; List<Format> closedCaptionFormats = this.closedCaptionFormats;
...@@ -162,17 +198,24 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact ...@@ -162,17 +198,24 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
mimeType = MimeTypes.APPLICATION_CEA608; mimeType = MimeTypes.APPLICATION_CEA608;
accessibilityChannel = 1; accessibilityChannel = 1;
} }
closedCaptionFormats.add(Format.createTextSampleFormat(null, mimeType, null,
Format.NO_VALUE, 0, language, accessibilityChannel, null));
// Skip easy_reader(1), wide_aspect_ratio(1), reserved(14). // Skip easy_reader(1), wide_aspect_ratio(1), reserved(14).
scratchDescriptorData.skipBytes(2); byte misc = (byte)scratchDescriptorData.readUnsignedByte();
boolean isWideAspectRatio = ((misc & 0x60) == 0x60);
Bundle params = new Bundle();
params.putInt(Format.KEY_ASPECT_RATIO_TYPE,
isWideAspectRatio ? C.WIDE_ASPECT_RATIO_TYPE: C.NON_WIDE_ASPECT_RATIO_TYPE);
closedCaptionFormats.add(Format.createTextSampleFormat(null, mimeType, null,
Format.NO_VALUE, 0, language, accessibilityChannel, null,
params));
scratchDescriptorData.skipBytes(1);
} }
} else { } else {
// Unknown descriptor. Ignore. // Unknown descriptor. Ignore.
} }
scratchDescriptorData.setPosition(nextDescriptorPosition); scratchDescriptorData.setPosition(nextDescriptorPosition);
} }
return new SeiReader(closedCaptionFormats);
return closedCaptionFormats;
} }
private boolean isSet(@Flags int flag) { private boolean isSet(@Flags int flag) {
......
...@@ -36,6 +36,7 @@ public final class H262Reader implements ElementaryStreamReader { ...@@ -36,6 +36,7 @@ public final class H262Reader implements ElementaryStreamReader {
private static final int START_SEQUENCE_HEADER = 0xB3; private static final int START_SEQUENCE_HEADER = 0xB3;
private static final int START_EXTENSION = 0xB5; private static final int START_EXTENSION = 0xB5;
private static final int START_GROUP = 0xB8; private static final int START_GROUP = 0xB8;
private static final int START_USER_DATA = 0xB2;
private String formatId; private String formatId;
private TrackOutput output; private TrackOutput output;
...@@ -62,16 +63,30 @@ public final class H262Reader implements ElementaryStreamReader { ...@@ -62,16 +63,30 @@ public final class H262Reader implements ElementaryStreamReader {
private long sampleTimeUs; private long sampleTimeUs;
private boolean sampleIsKeyframe; private boolean sampleIsKeyframe;
private boolean sampleHasPicture; private boolean sampleHasPicture;
private NalUnitTargetBuffer userData = null;
private UserDataReader userDataReader = null;
// Scratch variables to avoid allocations.
private ParsableByteArray userDataParsable = null;
public H262Reader() { public H262Reader() {
this(null);
}
public H262Reader(UserDataReader userDataReader) {
this.userDataReader = userDataReader;
prefixFlags = new boolean[4]; prefixFlags = new boolean[4];
csdBuffer = new CsdBuffer(128); csdBuffer = new CsdBuffer(128);
if (userDataReader != null) {
userData = new NalUnitTargetBuffer(START_USER_DATA, 128);
userDataParsable = new ParsableByteArray();
}
} }
@Override @Override
public void seek() { public void seek() {
NalUnitUtil.clearPrefixFlags(prefixFlags); NalUnitUtil.clearPrefixFlags(prefixFlags);
csdBuffer.reset(); csdBuffer.reset();
if (userData != null) {
userData.reset();
}
totalBytesWritten = 0; totalBytesWritten = 0;
startedFirstSample = false; startedFirstSample = false;
} }
...@@ -81,6 +96,9 @@ public final class H262Reader implements ElementaryStreamReader { ...@@ -81,6 +96,9 @@ public final class H262Reader implements ElementaryStreamReader {
idGenerator.generateNewId(); idGenerator.generateNewId();
formatId = idGenerator.getFormatId(); formatId = idGenerator.getFormatId();
output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_VIDEO); output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_VIDEO);
if (userDataReader != null) {
userDataReader.createTracks(extractorOutput, idGenerator);
}
} }
@Override @Override
...@@ -106,6 +124,9 @@ public final class H262Reader implements ElementaryStreamReader { ...@@ -106,6 +124,9 @@ public final class H262Reader implements ElementaryStreamReader {
if (!hasOutputFormat) { if (!hasOutputFormat) {
csdBuffer.onData(dataArray, offset, limit); csdBuffer.onData(dataArray, offset, limit);
} }
if (userData != null) {
userData.appendToNalUnit(dataArray, offset, limit);
}
return; return;
} }
...@@ -130,7 +151,25 @@ public final class H262Reader implements ElementaryStreamReader { ...@@ -130,7 +151,25 @@ public final class H262Reader implements ElementaryStreamReader {
hasOutputFormat = true; hasOutputFormat = true;
} }
} }
if (userDataReader != null && userData != null) {
int lengthToStartCode = startCodeOffset - offset;
int bytesAlreadyPassed = 0;
if (lengthToStartCode > 0) {
userData.appendToNalUnit(dataArray, offset, startCodeOffset);
} else {
bytesAlreadyPassed = -lengthToStartCode;
}
if (userData.endNalUnit(bytesAlreadyPassed)) {
int unescapedLength = NalUnitUtil.unescapeStream(userData.nalData, userData.nalLength);
userDataParsable.reset(userData.nalData, unescapedLength);
userDataReader.consume(sampleTimeUs, userDataParsable);
}
if (startCodeValue == START_USER_DATA && data.data[startCodeOffset + 2] == 0x1) {
userData.startNalUnit(startCodeValue);
}
}
if (startCodeValue == START_PICTURE || startCodeValue == START_SEQUENCE_HEADER) { if (startCodeValue == START_PICTURE || startCodeValue == START_SEQUENCE_HEADER) {
int bytesWrittenPastStartCode = limit - startCodeOffset; int bytesWrittenPastStartCode = limit - startCodeOffset;
if (startedFirstSample && sampleHasPicture && hasOutputFormat) { if (startedFirstSample && sampleHasPicture && hasOutputFormat) {
......
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.extractor.ts;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.util.List;
/**
* Consumes user data structure, outputting contained CEA-608/708 messages to a {@link TrackOutput}.
*/
/* package */ final class UserDataReader {
private final List<Format> closedCaptionFormats;
private final TrackOutput[] outputs;
private final int USER_DATA_START_CODE = 0x0001B2;
private final int USER_DATA_IDENTIFIER_GA94 = 0x47413934;
private final int USER_DATA_TYPE_CODE_MPEG_CC = 0x03;
public UserDataReader(List<Format> closedCaptionFormats) {
this.closedCaptionFormats = closedCaptionFormats;
outputs = new TrackOutput[closedCaptionFormats.size()];
}
public void createTracks(ExtractorOutput extractorOutput,
TsPayloadReader.TrackIdGenerator idGenerator) {
for (int i = 0; i < outputs.length; i++) {
idGenerator.generateNewId();
TrackOutput output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_TEXT);
Format channelFormat = closedCaptionFormats.get(i);
String channelMimeType = channelFormat.sampleMimeType;
Assertions.checkArgument(MimeTypes.APPLICATION_CEA608.equals(channelMimeType)
|| MimeTypes.APPLICATION_CEA708.equals(channelMimeType),
"Invalid closed caption mime type provided: " + channelMimeType);
output.format(Format.createTextSampleFormat(idGenerator.getFormatId(), channelMimeType, null,
Format.NO_VALUE, channelFormat.selectionFlags, channelFormat.language,
channelFormat.accessibilityChannel, null, channelFormat.params));
outputs[i] = output;
}
}
public void consume(long pesTimeUs, ParsableByteArray userDataPayload) {
if (userDataPayload.bytesLeft() < 9) {
return;
}
//check if payload is used_data_type (0x01B2)
int userDataStartCode = userDataPayload.readInt();
int userDataIdentifier = userDataPayload.readInt();
int userDataTypeCode = userDataPayload.readUnsignedByte();
if (userDataStartCode == USER_DATA_START_CODE && userDataIdentifier == USER_DATA_IDENTIFIER_GA94
&& userDataTypeCode == USER_DATA_TYPE_CODE_MPEG_CC) {
if (userDataPayload.bytesLeft() < 2) {
return;
}
// read cc_count and process_cc_data_flag byte.
int ccByte = userDataPayload.readUnsignedByte();
boolean processCCDataFlag = ((ccByte & 0x40) != 0);
int ccCount = (ccByte & 0x1F);
// skip reserved em_data byte of MPEG_CC structure
userDataPayload.skipBytes(1);
int payLoadSize = ccCount * 3;
if (processCCDataFlag && payLoadSize != 0) {
int ccPos = userDataPayload.getPosition();
for (TrackOutput output : outputs) {
output.sampleData(userDataPayload, payLoadSize);
output.sampleMetadata(pesTimeUs, C.BUFFER_FLAG_KEY_FRAME, payLoadSize, 0, null);
userDataPayload.setPosition(ccPos);
}
}
}
}
}
...@@ -2,7 +2,7 @@ seekMap: ...@@ -2,7 +2,7 @@ seekMap:
isSeekable = false isSeekable = false
duration = 66733 duration = 66733
getPosition(0) = [[timeUs=0, position=0]] getPosition(0) = [[timeUs=0, position=0]]
numberOfTracks = 2 numberOfTracks = 3
track 256: track 256:
format: format:
bitrate = -1 bitrate = -1
...@@ -76,4 +76,28 @@ track 257: ...@@ -76,4 +76,28 @@ track 257:
time = 100822 time = 100822
flags = 1 flags = 1
data = length 1254, hash 73FB07B8 data = length 1254, hash 73FB07B8
track 8448:
format:
bitrate = -1
id = 1/8448
containerMimeType = null
sampleMimeType = application/cea-608
maxInputSize = -1
width = -1
height = -1
frameRate = -1.0
rotationDegrees = 0
pixelWidthHeightRatio = 1.0
channelCount = -1
sampleRate = -1
pcmEncoding = -1
encoderDelay = 0
encoderPadding = 0
subsampleOffsetUs = 9223372036854775807
selectionFlags = 0
language = null
drmInitData = -
initializationData:
total output bytes = 0
sample count = 0
tracksEnded = true tracksEnded = true
...@@ -2,7 +2,7 @@ seekMap: ...@@ -2,7 +2,7 @@ seekMap:
isSeekable = false isSeekable = false
duration = UNSET TIME duration = UNSET TIME
getPosition(0) = [[timeUs=0, position=0]] getPosition(0) = [[timeUs=0, position=0]]
numberOfTracks = 2 numberOfTracks = 3
track 256: track 256:
format: format:
bitrate = -1 bitrate = -1
...@@ -76,4 +76,28 @@ track 257: ...@@ -76,4 +76,28 @@ track 257:
time = 100822 time = 100822
flags = 1 flags = 1
data = length 1254, hash 73FB07B8 data = length 1254, hash 73FB07B8
track 8448:
format:
bitrate = -1
id = 1/8448
containerMimeType = null
sampleMimeType = application/cea-608
maxInputSize = -1
width = -1
height = -1
frameRate = -1.0
rotationDegrees = 0
pixelWidthHeightRatio = 1.0
channelCount = -1
sampleRate = -1
pcmEncoding = -1
encoderDelay = 0
encoderPadding = 0
subsampleOffsetUs = 9223372036854775807
selectionFlags = 0
language = null
drmInitData = -
initializationData:
total output bytes = 0
sample count = 0
tracksEnded = true tracksEnded = true
...@@ -20,7 +20,9 @@ import static com.google.android.exoplayer2.util.MimeTypes.VIDEO_MP4; ...@@ -20,7 +20,9 @@ import static com.google.android.exoplayer2.util.MimeTypes.VIDEO_MP4;
import static com.google.android.exoplayer2.util.MimeTypes.VIDEO_WEBM; import static com.google.android.exoplayer2.util.MimeTypes.VIDEO_WEBM;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import android.os.Bundle;
import android.os.Parcel; import android.os.Parcel;
import com.google.android.exoplayer2.drm.DrmInitData; import com.google.android.exoplayer2.drm.DrmInitData;
import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.id3.TextInformationFrame; import com.google.android.exoplayer2.metadata.id3.TextInformationFrame;
...@@ -63,11 +65,12 @@ public final class FormatTest { ...@@ -63,11 +65,12 @@ public final class FormatTest {
new TextInformationFrame("id2", "description2", "value2")); new TextInformationFrame("id2", "description2", "value2"));
ColorInfo colorInfo = new ColorInfo(C.COLOR_SPACE_BT709, ColorInfo colorInfo = new ColorInfo(C.COLOR_SPACE_BT709,
C.COLOR_RANGE_LIMITED, C.COLOR_TRANSFER_SDR, new byte[] {1, 2, 3, 4, 5, 6, 7}); C.COLOR_RANGE_LIMITED, C.COLOR_TRANSFER_SDR, new byte[] {1, 2, 3, 4, 5, 6, 7});
Bundle params = new Bundle();
params.putInt(Format.KEY_ASPECT_RATIO_TYPE, 100);
Format formatToParcel = new Format("id", MimeTypes.VIDEO_MP4, MimeTypes.VIDEO_H264, null, Format formatToParcel = new Format("id", MimeTypes.VIDEO_MP4, MimeTypes.VIDEO_H264, null,
1024, 2048, 1920, 1080, 24, 90, 2, projectionData, C.STEREO_MODE_TOP_BOTTOM, colorInfo, 6, 1024, 2048, 1920, 1080, 24, 90, 2, projectionData, C.STEREO_MODE_TOP_BOTTOM, colorInfo, 6,
44100, C.ENCODING_PCM_24BIT, 1001, 1002, 0, "und", Format.NO_VALUE, 44100, C.ENCODING_PCM_24BIT, 1001, 1002, 0, "und", Format.NO_VALUE,
Format.OFFSET_SAMPLE_RELATIVE, INIT_DATA, drmInitData, metadata); Format.OFFSET_SAMPLE_RELATIVE, INIT_DATA, drmInitData, metadata, params);
Parcel parcel = Parcel.obtain(); Parcel parcel = Parcel.obtain();
formatToParcel.writeToParcel(parcel, 0); formatToParcel.writeToParcel(parcel, 0);
...@@ -75,6 +78,8 @@ public final class FormatTest { ...@@ -75,6 +78,8 @@ public final class FormatTest {
Format formatFromParcel = Format.CREATOR.createFromParcel(parcel); Format formatFromParcel = Format.CREATOR.createFromParcel(parcel);
assertThat(formatFromParcel).isEqualTo(formatToParcel); assertThat(formatFromParcel).isEqualTo(formatToParcel);
int aspectRatio = formatFromParcel.params.getInt(Format.KEY_ASPECT_RATIO_TYPE);
assertThat(aspectRatio).isEqualTo(100);
parcel.recycle(); parcel.recycle();
} }
......
...@@ -49,7 +49,7 @@ public class DefaultTrackNameProvider implements TrackNameProvider { ...@@ -49,7 +49,7 @@ public class DefaultTrackNameProvider implements TrackNameProvider {
} else { } else {
trackName = buildLanguageString(format); trackName = buildLanguageString(format);
} }
return trackName.length() == 0 ? resources.getString(R.string.exo_track_unknown) : trackName; return trackName.length() == 0 ? resources.getString(R.string.exo_track_unknown) : trackName + " - " + format.id;
} }
private String buildResolutionString(Format format) { private String buildResolutionString(Format format) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment