Added support for multiple RawCC (CEA-608/CEA-708) tracks in DASH.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=138392065
This commit is contained in:
cdrolle 2016-11-07 08:11:12 -08:00 committed by Oliver Woodman
parent 0354ef24d4
commit a4935a9ba0
9 changed files with 218 additions and 49 deletions

View File

@ -64,8 +64,8 @@ public final class FormatTest extends TestCase {
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, 6, 44100,
C.ENCODING_PCM_24BIT, 1001, 1002, 0, "und", Format.OFFSET_SAMPLE_RELATIVE, INIT_DATA,
drmInitData, metadata);
C.ENCODING_PCM_24BIT, 1001, 1002, 0, "und", Format.NO_VALUE, Format.OFFSET_SAMPLE_RELATIVE,
INIT_DATA, drmInitData, metadata);
Parcel parcel = Parcel.obtain();
formatToParcel.writeToParcel(parcel, 0);

View File

@ -17,8 +17,10 @@ package com.google.android.exoplayer2.extractor.rawcc;
import android.annotation.TargetApi;
import android.test.InstrumentationTestCase;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.util.MimeTypes;
/**
* Tests for {@link RawCcExtractor}.
@ -27,12 +29,15 @@ import com.google.android.exoplayer2.testutil.TestUtil;
public final class RawCcExtractorTest extends InstrumentationTestCase {
public void testRawCcSample() throws Exception {
TestUtil.assertOutput(new TestUtil.ExtractorFactory() {
@Override
public Extractor create() {
return new RawCcExtractor();
}
}, "rawcc/sample.rawcc", getInstrumentation());
TestUtil.assertOutput(
new TestUtil.ExtractorFactory() {
@Override
public Extractor create() {
return new RawCcExtractor(
Format.createTextContainerFormat(null, null, MimeTypes.APPLICATION_CEA608,
"cea608", Format.NO_VALUE, 0, null, 1));
}
}, "rawcc/sample.rawcc", getInstrumentation());
}
}

View File

@ -17,6 +17,7 @@ package com.google.android.exoplayer2.source.dash.manifest;
import android.net.Uri;
import android.test.InstrumentationTestCase;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.testutil.TestUtil;
import java.io.IOException;
@ -68,4 +69,35 @@ public class DashManifestParserTest extends InstrumentationTestCase {
}
}
public void testParseCea608AccessibilityChannel() {
assertEquals(1, DashManifestParser.parseCea608AccessibilityChannel("CC1=eng"));
assertEquals(2, DashManifestParser.parseCea608AccessibilityChannel("CC2=eng"));
assertEquals(3, DashManifestParser.parseCea608AccessibilityChannel("CC3=eng"));
assertEquals(4, DashManifestParser.parseCea608AccessibilityChannel("CC4=eng"));
assertEquals(Format.NO_VALUE, DashManifestParser.parseCea608AccessibilityChannel(null));
assertEquals(Format.NO_VALUE, DashManifestParser.parseCea608AccessibilityChannel(""));
assertEquals(Format.NO_VALUE, DashManifestParser.parseCea608AccessibilityChannel("CC0=eng"));
assertEquals(Format.NO_VALUE, DashManifestParser.parseCea608AccessibilityChannel("CC5=eng"));
assertEquals(Format.NO_VALUE,
DashManifestParser.parseCea608AccessibilityChannel("Wrong format"));
}
public void testParseCea708AccessibilityChannel() {
assertEquals(1, DashManifestParser.parseCea708AccessibilityChannel("1=lang:eng"));
assertEquals(2, DashManifestParser.parseCea708AccessibilityChannel("2=lang:eng"));
assertEquals(3, DashManifestParser.parseCea708AccessibilityChannel("3=lang:eng"));
assertEquals(62, DashManifestParser.parseCea708AccessibilityChannel("62=lang:eng"));
assertEquals(63, DashManifestParser.parseCea708AccessibilityChannel("63=lang:eng"));
assertEquals(Format.NO_VALUE, DashManifestParser.parseCea708AccessibilityChannel(null));
assertEquals(Format.NO_VALUE, DashManifestParser.parseCea708AccessibilityChannel(""));
assertEquals(Format.NO_VALUE,
DashManifestParser.parseCea708AccessibilityChannel("0=lang:eng"));
assertEquals(Format.NO_VALUE,
DashManifestParser.parseCea708AccessibilityChannel("64=lang:eng"));
assertEquals(Format.NO_VALUE,
DashManifestParser.parseCea708AccessibilityChannel("Wrong format"));
}
}

View File

@ -178,6 +178,11 @@ public final class Format implements Parcelable {
*/
public final String language;
/**
* The Accessibility channel, or {@link #NO_VALUE} if not known or applicable.
*/
public final int accessibilityChannel;
// Lazily initialized hashcode and framework media format.
private int hashCode;
@ -190,7 +195,8 @@ public final class Format implements Parcelable {
float frameRate, List<byte[]> initializationData) {
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, width,
height, frameRate, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, 0, null, OFFSET_SAMPLE_RELATIVE, initializationData, null, null);
NO_VALUE, NO_VALUE, 0, null, NO_VALUE, OFFSET_SAMPLE_RELATIVE, initializationData, null,
null);
}
public static Format createVideoSampleFormat(String id, String sampleMimeType, String codecs,
@ -215,8 +221,8 @@ public final class Format implements Parcelable {
byte[] projectionData, @C.StereoMode int stereoMode, DrmInitData drmInitData) {
return new Format(id, null, sampleMimeType, codecs, bitrate, maxInputSize, width, height,
frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, NO_VALUE,
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, 0, null, OFFSET_SAMPLE_RELATIVE, initializationData,
drmInitData, null);
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, 0, null, NO_VALUE, OFFSET_SAMPLE_RELATIVE,
initializationData, drmInitData, null);
}
// Audio.
@ -226,8 +232,8 @@ public final class Format implements Parcelable {
List<byte[]> initializationData, @C.SelectionFlags int selectionFlags, String language) {
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, channelCount, sampleRate, NO_VALUE,
NO_VALUE, NO_VALUE, selectionFlags, language, OFFSET_SAMPLE_RELATIVE, initializationData,
null, null);
NO_VALUE, NO_VALUE, selectionFlags, language, NO_VALUE, OFFSET_SAMPLE_RELATIVE,
initializationData, null, null);
}
public static Format createAudioSampleFormat(String id, String sampleMimeType, String codecs,
@ -254,7 +260,7 @@ public final class Format implements Parcelable {
@C.SelectionFlags int selectionFlags, String language, Metadata metadata) {
return new Format(id, null, sampleMimeType, codecs, bitrate, maxInputSize, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, channelCount, sampleRate, pcmEncoding,
encoderDelay, encoderPadding, selectionFlags, language, OFFSET_SAMPLE_RELATIVE,
encoderDelay, encoderPadding, selectionFlags, language, NO_VALUE, OFFSET_SAMPLE_RELATIVE,
initializationData, drmInitData, metadata);
}
@ -263,23 +269,46 @@ public final class Format implements Parcelable {
public static Format createTextContainerFormat(String id, String containerMimeType,
String sampleMimeType, String codecs, int bitrate, @C.SelectionFlags int selectionFlags,
String language) {
return createTextContainerFormat(id, containerMimeType, sampleMimeType, codecs, bitrate,
selectionFlags, language, NO_VALUE);
}
public static Format createTextContainerFormat(String id, String containerMimeType,
String sampleMimeType, String codecs, int bitrate, @C.SelectionFlags int selectionFlags,
String language, int accessibilityChannel) {
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, selectionFlags, language, OFFSET_SAMPLE_RELATIVE, null, null, null);
NO_VALUE, NO_VALUE, selectionFlags, language, accessibilityChannel,
OFFSET_SAMPLE_RELATIVE, null, null, null);
}
public static Format createTextSampleFormat(String id, String sampleMimeType, String codecs,
int bitrate, @C.SelectionFlags int selectionFlags, String language, DrmInitData drmInitData) {
return createTextSampleFormat(id, sampleMimeType, codecs, bitrate, selectionFlags, language,
drmInitData, OFFSET_SAMPLE_RELATIVE);
NO_VALUE, drmInitData, OFFSET_SAMPLE_RELATIVE);
}
public static Format createTextSampleFormat(String id, String sampleMimeType, String codecs,
int bitrate, @C.SelectionFlags int selectionFlags, String language,
int accessibilityChannel, DrmInitData drmInitData) {
return createTextSampleFormat(id, sampleMimeType, codecs, bitrate, selectionFlags, language,
accessibilityChannel, drmInitData, OFFSET_SAMPLE_RELATIVE);
}
public static Format createTextSampleFormat(String id, String sampleMimeType, String codecs,
int bitrate, @C.SelectionFlags int selectionFlags, String language, DrmInitData drmInitData,
long subsampleOffsetUs) {
return createTextSampleFormat(id, sampleMimeType, codecs, bitrate, selectionFlags, language,
NO_VALUE, drmInitData, subsampleOffsetUs);
}
public static Format createTextSampleFormat(String id, String sampleMimeType, String codecs,
int bitrate, @C.SelectionFlags int selectionFlags, String language,
int accessibilityChannel, DrmInitData drmInitData, long subsampleOffsetUs) {
return new Format(id, null, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, selectionFlags, language, subsampleOffsetUs, null, drmInitData, null);
NO_VALUE, selectionFlags, language, accessibilityChannel, subsampleOffsetUs, null,
drmInitData, null);
}
// Image.
@ -288,7 +317,8 @@ public final class Format implements Parcelable {
int bitrate, List<byte[]> initializationData, String language, DrmInitData drmInitData) {
return new Format(id, null, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, 0, language, OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData, null);
NO_VALUE, 0, language, NO_VALUE, OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData,
null);
}
// Generic.
@ -297,14 +327,14 @@ public final class Format implements Parcelable {
String sampleMimeType, int bitrate) {
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, 0, null, OFFSET_SAMPLE_RELATIVE, null, null, null);
NO_VALUE, NO_VALUE, 0, null, NO_VALUE, OFFSET_SAMPLE_RELATIVE, null, null, null);
}
public static Format createSampleFormat(String id, String sampleMimeType, String codecs,
int bitrate, DrmInitData drmInitData) {
return new Format(id, null, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, 0, null, OFFSET_SAMPLE_RELATIVE, null, drmInitData, null);
NO_VALUE, 0, null, NO_VALUE, OFFSET_SAMPLE_RELATIVE, null, drmInitData, null);
}
/* package */ Format(String id, String containerMimeType, String sampleMimeType, String codecs,
@ -312,8 +342,8 @@ public final class Format implements Parcelable {
float pixelWidthHeightRatio, byte[] projectionData, @C.StereoMode int stereoMode,
int channelCount, int sampleRate, @C.PcmEncoding int pcmEncoding, int encoderDelay,
int encoderPadding, @C.SelectionFlags int selectionFlags, String language,
long subsampleOffsetUs, List<byte[]> initializationData, DrmInitData drmInitData,
Metadata metadata) {
int accessibilityChannel, long subsampleOffsetUs, List<byte[]> initializationData,
DrmInitData drmInitData, Metadata metadata) {
this.id = id;
this.containerMimeType = containerMimeType;
this.sampleMimeType = sampleMimeType;
@ -334,6 +364,7 @@ public final class Format implements Parcelable {
this.encoderPadding = encoderPadding;
this.selectionFlags = selectionFlags;
this.language = language;
this.accessibilityChannel = accessibilityChannel;
this.subsampleOffsetUs = subsampleOffsetUs;
this.initializationData = initializationData == null ? Collections.<byte[]>emptyList()
: initializationData;
@ -364,6 +395,7 @@ public final class Format implements Parcelable {
encoderPadding = in.readInt();
selectionFlags = in.readInt();
language = in.readString();
accessibilityChannel = in.readInt();
subsampleOffsetUs = in.readLong();
int initializationDataSize = in.readInt();
initializationData = new ArrayList<>(initializationDataSize);
@ -378,14 +410,16 @@ public final class Format implements Parcelable {
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize,
width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData,
stereoMode, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
selectionFlags, language, subsampleOffsetUs, initializationData, drmInitData, metadata);
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
drmInitData, metadata);
}
public Format copyWithSubsampleOffsetUs(long subsampleOffsetUs) {
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize,
width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData,
stereoMode, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
selectionFlags, language, subsampleOffsetUs, initializationData, drmInitData, metadata);
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
drmInitData, metadata);
}
public Format copyWithContainerInfo(String id, String codecs, int bitrate, int width, int height,
@ -393,7 +427,8 @@ public final class Format implements Parcelable {
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize,
width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData,
stereoMode, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
selectionFlags, language, subsampleOffsetUs, initializationData, drmInitData, metadata);
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
drmInitData, metadata);
}
public Format copyWithManifestFormatInfo(Format manifestFormat,
@ -409,28 +444,32 @@ public final class Format implements Parcelable {
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize, width,
height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode,
channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, selectionFlags,
language, subsampleOffsetUs, initializationData, drmInitData, metadata);
language, accessibilityChannel, subsampleOffsetUs, initializationData, drmInitData,
metadata);
}
public Format copyWithGaplessInfo(int encoderDelay, int encoderPadding) {
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize,
width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData,
stereoMode, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
selectionFlags, language, subsampleOffsetUs, initializationData, drmInitData, metadata);
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
drmInitData, metadata);
}
public Format copyWithDrmInitData(DrmInitData drmInitData) {
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize,
width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData,
stereoMode, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
selectionFlags, language, subsampleOffsetUs, initializationData, drmInitData, metadata);
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
drmInitData, metadata);
}
public Format copyWithMetadata(Metadata metadata) {
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize,
width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData,
stereoMode, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
selectionFlags, language, subsampleOffsetUs, initializationData, drmInitData, metadata);
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
drmInitData, metadata);
}
/**
@ -489,6 +528,7 @@ public final class Format implements Parcelable {
result = 31 * result + channelCount;
result = 31 * result + sampleRate;
result = 31 * result + (language == null ? 0 : language.hashCode());
result = 31 * result + accessibilityChannel;
result = 31 * result + (drmInitData == null ? 0 : drmInitData.hashCode());
result = 31 * result + (metadata == null ? 0 : metadata.hashCode());
hashCode = result;
@ -514,6 +554,7 @@ public final class Format implements Parcelable {
|| encoderPadding != other.encoderPadding || subsampleOffsetUs != other.subsampleOffsetUs
|| selectionFlags != other.selectionFlags || !Util.areEqual(id, other.id)
|| !Util.areEqual(language, other.language)
|| accessibilityChannel != other.accessibilityChannel
|| !Util.areEqual(containerMimeType, other.containerMimeType)
|| !Util.areEqual(sampleMimeType, other.sampleMimeType)
|| !Util.areEqual(codecs, other.codecs)
@ -584,6 +625,7 @@ public final class Format implements Parcelable {
dest.writeInt(encoderPadding);
dest.writeInt(selectionFlags);
dest.writeString(language);
dest.writeInt(accessibilityChannel);
dest.writeLong(subsampleOffsetUs);
int initializationDataSize = initializationData.size();
dest.writeInt(initializationDataSize);

View File

@ -24,7 +24,6 @@ import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException;
@ -45,6 +44,8 @@ public final class RawCcExtractor implements Extractor {
private static final int STATE_READING_TIMESTAMP_AND_COUNT = 1;
private static final int STATE_READING_SAMPLES = 2;
private final Format format;
private final ParsableByteArray dataScratch;
private TrackOutput trackOutput;
@ -55,7 +56,8 @@ public final class RawCcExtractor implements Extractor {
private int remainingSampleCount;
private int sampleBytesWritten;
public RawCcExtractor() {
public RawCcExtractor(Format format) {
this.format = format;
dataScratch = new ParsableByteArray(SCRATCH_SIZE);
parserState = STATE_READING_HEADER;
}
@ -65,9 +67,7 @@ public final class RawCcExtractor implements Extractor {
output.seekMap(new SeekMap.Unseekable(C.TIME_UNSET));
trackOutput = output.track(0);
output.endTracks();
trackOutput.format(Format.createTextSampleFormat(null, MimeTypes.APPLICATION_CEA608,
null, Format.NO_VALUE, 0, null, null));
trackOutput.format(format);
}
@Override

View File

@ -349,7 +349,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
boolean resendFormatOnInit = false;
Extractor extractor;
if (MimeTypes.APPLICATION_RAWCC.equals(containerMimeType)) {
extractor = new RawCcExtractor();
extractor = new RawCcExtractor(representation.format);
resendFormatOnInit = true;
} else if (mimeTypeIsWebm(containerMimeType)) {
extractor = new MatroskaExtractor();

View File

@ -59,6 +59,10 @@ public class DashManifestParser extends DefaultHandler
private static final Pattern FRAME_RATE_PATTERN = Pattern.compile("(\\d+)(?:/(\\d+))?");
private static final Pattern CEA_608_ACCESSIBILITY_PATTERN = Pattern.compile("CC([1-4])=.*");
private static final Pattern CEA_708_ACCESSIBILITY_PATTERN =
Pattern.compile("([1-9]|[1-5][0-9]|6[0-3])=.*");
private final String contentId;
private final XmlPullParserFactory xmlParserFactory;
@ -235,6 +239,7 @@ public class DashManifestParser extends DefaultHandler
int audioChannels = Format.NO_VALUE;
int audioSamplingRate = parseInt(xpp, "audioSamplingRate", Format.NO_VALUE);
String language = xpp.getAttributeValue(null, "lang");
int accessibilityChannel = Format.NO_VALUE;
ArrayList<SchemeData> drmSchemeDatas = new ArrayList<>();
List<RepresentationInfo> representationInfos = new ArrayList<>();
@ -256,12 +261,15 @@ public class DashManifestParser extends DefaultHandler
contentType = checkContentTypeConsistency(contentType, parseContentType(xpp));
} else if (XmlPullParserUtil.isStartTag(xpp, "Representation")) {
RepresentationInfo representationInfo = parseRepresentation(xpp, baseUrl, mimeType, codecs,
width, height, frameRate, audioChannels, audioSamplingRate, language, segmentBase);
width, height, frameRate, audioChannels, audioSamplingRate, language,
accessibilityChannel, segmentBase);
contentType = checkContentTypeConsistency(contentType,
getContentType(representationInfo.format));
representationInfos.add(representationInfo);
} else if (XmlPullParserUtil.isStartTag(xpp, "AudioChannelConfiguration")) {
audioChannels = parseAudioChannelConfiguration(xpp);
} else if (XmlPullParserUtil.isStartTag(xpp, "Accessibility")) {
accessibilityChannel = parseAccessibilityValue(xpp);
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentBase")) {
segmentBase = parseSegmentBase(xpp, (SingleSegmentBase) segmentBase);
} else if (XmlPullParserUtil.isStartTag(xpp, "SegmentList")) {
@ -365,7 +373,8 @@ public class DashManifestParser extends DefaultHandler
protected RepresentationInfo parseRepresentation(XmlPullParser xpp, String baseUrl,
String adaptationSetMimeType, String adaptationSetCodecs, int adaptationSetWidth,
int adaptationSetHeight, float adaptationSetFrameRate, int adaptationSetAudioChannels,
int adaptationSetAudioSamplingRate, String adaptationSetLanguage, SegmentBase segmentBase)
int adaptationSetAudioSamplingRate, String adaptationSetLanguage,
int adaptationSetAccessibilityChannel, SegmentBase segmentBase)
throws XmlPullParserException, IOException {
String id = xpp.getAttributeValue(null, "id");
int bandwidth = parseInt(xpp, "bandwidth", Format.NO_VALUE);
@ -404,7 +413,8 @@ public class DashManifestParser extends DefaultHandler
} while (!XmlPullParserUtil.isEndTag(xpp, "Representation"));
Format format = buildFormat(id, mimeType, width, height, frameRate, audioChannels,
audioSamplingRate, bandwidth, adaptationSetLanguage, codecs);
audioSamplingRate, bandwidth, adaptationSetLanguage, adaptationSetAccessibilityChannel,
codecs);
segmentBase = segmentBase != null ? segmentBase : new SingleSegmentBase(baseUrl);
return new RepresentationInfo(format, baseUrl, segmentBase, drmSchemeDatas);
@ -412,7 +422,7 @@ public class DashManifestParser extends DefaultHandler
protected Format buildFormat(String id, String containerMimeType, int width, int height,
float frameRate, int audioChannels, int audioSamplingRate, int bitrate, String language,
String codecs) {
int accessiblityChannel, String codecs) {
String sampleMimeType = getSampleMimeType(containerMimeType, codecs);
if (sampleMimeType != null) {
if (MimeTypes.isVideo(sampleMimeType)) {
@ -423,7 +433,10 @@ public class DashManifestParser extends DefaultHandler
bitrate, audioChannels, audioSamplingRate, null, 0, language);
} else if (mimeTypeIsRawText(sampleMimeType)) {
return Format.createTextContainerFormat(id, containerMimeType, sampleMimeType, codecs,
bitrate, 0, language);
bitrate, 0, language, accessiblityChannel);
} else if (containerMimeType.equals(MimeTypes.APPLICATION_RAWCC)) {
return Format.createTextContainerFormat(id, containerMimeType, sampleMimeType, codecs,
bitrate, 0, language, accessiblityChannel);
} else {
return Format.createContainerFormat(id, containerMimeType, codecs, sampleMimeType, bitrate);
}
@ -726,6 +739,54 @@ public class DashManifestParser extends DefaultHandler
}
}
private static int parseAccessibilityValue(XmlPullParser xpp)
throws IOException, XmlPullParserException {
String schemeIdUri = parseString(xpp, "schemeIdUri", null);
String valueString = parseString(xpp, "value", null);
int accessibilityValue;
if (schemeIdUri == null || valueString == null) {
accessibilityValue = Format.NO_VALUE;
} else if ("urn:scte:dash:cc:cea-608:2015".equals(schemeIdUri)) {
accessibilityValue = parseCea608AccessibilityChannel(valueString);
} else if ("urn:scte:dash:cc:cea-708:2015".equals(schemeIdUri)) {
accessibilityValue = parseCea708AccessibilityChannel(valueString);
} else {
accessibilityValue = Format.NO_VALUE;
}
do {
xpp.next();
} while (!XmlPullParserUtil.isEndTag(xpp, "Accessibility"));
return accessibilityValue;
}
static int parseCea608AccessibilityChannel(String accessibilityValueString) {
if (accessibilityValueString == null) {
return Format.NO_VALUE;
}
Matcher accessibilityValueMatcher =
CEA_608_ACCESSIBILITY_PATTERN.matcher(accessibilityValueString);
if (accessibilityValueMatcher.matches()) {
return Integer.parseInt(accessibilityValueMatcher.group(1));
} else {
Log.w(TAG, "Unable to parse channel number from " + accessibilityValueString);
return Format.NO_VALUE;
}
}
static int parseCea708AccessibilityChannel(String accessibilityValueString) {
if (accessibilityValueString == null) {
return Format.NO_VALUE;
}
Matcher accessibilityValueMatcher =
CEA_708_ACCESSIBILITY_PATTERN.matcher(accessibilityValueString);
if (accessibilityValueMatcher.matches()) {
return Integer.parseInt(accessibilityValueMatcher.group(1));
} else {
Log.w(TAG, "Unable to parse service block number from " + accessibilityValueString);
return Format.NO_VALUE;
}
}
protected static float parseFrameRate(XmlPullParser xpp, float defaultValue) {
float frameRate = defaultValue;
String frameRateAttribute = xpp.getAttributeValue(null, "frameRate");

View File

@ -74,7 +74,12 @@ public interface SubtitleDecoderFactory {
if (clazz == null) {
throw new IllegalArgumentException("Attempted to create decoder for unsupported format");
}
return clazz.asSubclass(SubtitleDecoder.class).getConstructor().newInstance();
if (clazz == Cea608Decoder.class) {
return clazz.asSubclass(SubtitleDecoder.class)
.getConstructor(Integer.TYPE).newInstance(format.accessibilityChannel);
} else {
return clazz.asSubclass(SubtitleDecoder.class).getConstructor().newInstance();
}
} catch (Exception e) {
throw new IllegalStateException("Unexpected error instantiating decoder", e);
}

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.text.cea;
import android.text.TextUtils;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.Subtitle;
import com.google.android.exoplayer2.text.SubtitleDecoder;
@ -27,9 +28,15 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
*/
public final class Cea608Decoder extends CeaDecoder {
private static final int NTSC_CC_FIELD_1 = 0x00;
private static final int CC_TYPE_MASK = 0x03;
private static final String TAG = "Cea608Decoder";
private static final int CC_VALID_FLAG = 0x04;
private static final int CC_TYPE_FLAG = 0x02;
private static final int CC_FIELD_FLAG = 0x01;
private static final int NTSC_CC_FIELD_1 = 0x00;
private static final int NTSC_CC_FIELD_2 = 0x01;
private static final int CC_VALID_608_ID = 0x04;
private static final int PAYLOAD_TYPE_CC = 4;
private static final int COUNTRY_CODE = 0xB5;
@ -160,6 +167,8 @@ public final class Cea608Decoder extends CeaDecoder {
private final StringBuilder captionStringBuilder;
private final int selectedField;
private int captionMode;
private int captionRowCount;
private String captionString;
@ -170,10 +179,21 @@ public final class Cea608Decoder extends CeaDecoder {
private byte repeatableControlCc1;
private byte repeatableControlCc2;
public Cea608Decoder() {
public Cea608Decoder(int accessibilityChannel) {
ccData = new ParsableByteArray();
captionStringBuilder = new StringBuilder();
switch (accessibilityChannel) {
case 3:
case 4:
selectedField = 2;
break;
case 1:
case 2:
case Format.NO_VALUE:
default:
selectedField = 1;
}
setCaptionMode(CC_MODE_UNKNOWN);
captionRowCount = DEFAULT_CAPTIONS_ROW_COUNT;
@ -219,14 +239,18 @@ public final class Cea608Decoder extends CeaDecoder {
boolean captionDataProcessed = false;
boolean isRepeatableControl = false;
while (ccData.bytesLeft() > 0) {
byte ccTypeAndValid = (byte) (ccData.readUnsignedByte() & 0x07);
byte ccDataHeader = (byte) ccData.readUnsignedByte();
byte ccData1 = (byte) (ccData.readUnsignedByte() & 0x7F);
byte ccData2 = (byte) (ccData.readUnsignedByte() & 0x7F);
// Only examine valid NTSC_CC_FIELD_1 packets
if ((ccTypeAndValid & CC_VALID_FLAG) == 0
|| (ccTypeAndValid & CC_TYPE_MASK) != NTSC_CC_FIELD_1) {
// TODO: Add support for NTSC_CC_FIELD_2 packets
// Only examine valid CEA-608 packets
if ((ccDataHeader & (CC_VALID_FLAG | CC_TYPE_FLAG)) != CC_VALID_608_ID) {
continue;
}
// Only examine packets within the selected field
if ((selectedField == 1 && (ccDataHeader & CC_FIELD_FLAG) != NTSC_CC_FIELD_1)
|| (selectedField == 2 && (ccDataHeader & CC_FIELD_FLAG) != NTSC_CC_FIELD_2)) {
continue;
}