mirror of
https://github.com/androidx/media.git
synced 2025-05-15 03:29:53 +08:00
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:
parent
0354ef24d4
commit
a4935a9ba0
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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"));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user