Merge pull request #7370 from jruesga:embedded-cea-708-support

PiperOrigin-RevId: 312090461
This commit is contained in:
Oliver Woodman 2020-05-18 22:38:10 +01:00
commit 3db703a983
3 changed files with 132 additions and 37 deletions

View File

@ -156,6 +156,7 @@
`http://dashif.org/guidelines/trickmode`) into the same `TrackGroup` as `http://dashif.org/guidelines/trickmode`) into the same `TrackGroup` as
the main adaptation sets to which they refer. Trick play tracks are the main adaptation sets to which they refer. Trick play tracks are
marked with the `C.ROLE_FLAG_TRICK_PLAY` flag. marked with the `C.ROLE_FLAG_TRICK_PLAY` flag.
* Enable support for embedded CEA-708.
* Fix assertion failure in `SampleQueue` when playing DASH streams with * Fix assertion failure in `SampleQueue` when playing DASH streams with
EMSG tracks ([#7273](https://github.com/google/ExoPlayer/issues/7273)). EMSG tracks ([#7273](https://github.com/google/ExoPlayer/issues/7273)).
* MP3: * MP3:

View File

@ -69,7 +69,11 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
SequenceableLoader.Callback<ChunkSampleStream<DashChunkSource>>, SequenceableLoader.Callback<ChunkSampleStream<DashChunkSource>>,
ChunkSampleStream.ReleaseCallback<DashChunkSource> { ChunkSampleStream.ReleaseCallback<DashChunkSource> {
// Defined by ANSI/SCTE 214-1 2016 7.2.3.
private static final Pattern CEA608_SERVICE_DESCRIPTOR_REGEX = Pattern.compile("CC([1-4])=(.+)"); private static final Pattern CEA608_SERVICE_DESCRIPTOR_REGEX = Pattern.compile("CC([1-4])=(.+)");
// Defined by ANSI/SCTE 214-1 2016 7.2.2.
private static final Pattern CEA708_SERVICE_DESCRIPTOR_REGEX =
Pattern.compile("([1-4])=lang:(\\w+)(,.+)?");
/* package */ final int id; /* package */ final int id;
private final DashChunkSource.Factory chunkSourceFactory; private final DashChunkSource.Factory chunkSourceFactory;
@ -835,49 +839,52 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
for (int j = 0; j < descriptors.size(); j++) { for (int j = 0; j < descriptors.size(); j++) {
Descriptor descriptor = descriptors.get(j); Descriptor descriptor = descriptors.get(j);
if ("urn:scte:dash:cc:cea-608:2015".equals(descriptor.schemeIdUri)) { if ("urn:scte:dash:cc:cea-608:2015".equals(descriptor.schemeIdUri)) {
@Nullable String value = descriptor.value; Format cea608Format =
if (value == null) { new Format.Builder()
// There are embedded CEA-608 tracks, but service information is not declared. .setSampleMimeType(MimeTypes.APPLICATION_CEA608)
return new Format[] {buildCea608TrackFormat(adaptationSet.id)}; .setId(adaptationSet.id + ":cea608")
} .build();
String[] services = Util.split(value, ";"); return parseClosedCaptionDescriptor(
Format[] formats = new Format[services.length]; descriptor, CEA608_SERVICE_DESCRIPTOR_REGEX, cea608Format);
for (int k = 0; k < services.length; k++) { } else if ("urn:scte:dash:cc:cea-708:2015".equals(descriptor.schemeIdUri)) {
Matcher matcher = CEA608_SERVICE_DESCRIPTOR_REGEX.matcher(services[k]); Format cea708Format =
if (!matcher.matches()) { new Format.Builder()
// If we can't parse service information for all services, assume a single track. .setSampleMimeType(MimeTypes.APPLICATION_CEA708)
return new Format[] {buildCea608TrackFormat(adaptationSet.id)}; .setId(adaptationSet.id + ":cea708")
} .build();
formats[k] = return parseClosedCaptionDescriptor(
buildCea608TrackFormat( descriptor, CEA708_SERVICE_DESCRIPTOR_REGEX, cea708Format);
adaptationSet.id,
/* language= */ matcher.group(2),
/* accessibilityChannel= */ Integer.parseInt(matcher.group(1)));
}
return formats;
} }
} }
} }
return new Format[0]; return new Format[0];
} }
private static Format buildCea608TrackFormat(int adaptationSetId) { private static Format[] parseClosedCaptionDescriptor(
return buildCea608TrackFormat( Descriptor descriptor, Pattern serviceDescriptorRegex, Format baseFormat) {
adaptationSetId, /* language= */ null, /* accessibilityChannel= */ Format.NO_VALUE); @Nullable String value = descriptor.value;
} if (value == null) {
// There are embedded closed caption tracks, but service information is not declared.
private static Format buildCea608TrackFormat( return new Format[] {baseFormat};
int adaptationSetId, @Nullable String language, int accessibilityChannel) { }
String id = String[] services = Util.split(value, ";");
adaptationSetId Format[] formats = new Format[services.length];
+ ":cea608" for (int i = 0; i < services.length; i++) {
+ (accessibilityChannel != Format.NO_VALUE ? ":" + accessibilityChannel : ""); Matcher matcher = serviceDescriptorRegex.matcher(services[i]);
return new Format.Builder() if (!matcher.matches()) {
.setId(id) // If we can't parse service information for all services, assume a single track.
.setSampleMimeType(MimeTypes.APPLICATION_CEA608) return new Format[] {baseFormat};
.setLanguage(language) }
.setAccessibilityChannel(accessibilityChannel) int accessibilityChannel = Integer.parseInt(matcher.group(1));
.build(); formats[i] =
baseFormat
.buildUpon()
.setId(baseFormat.id + ":" + accessibilityChannel)
.setAccessibilityChannel(accessibilityChannel)
.setLanguage(matcher.group(2))
.build();
}
return formats;
} }
// We won't assign the array to a variable that erases the generic type, and then write into it. // We won't assign the array to a variable that erases the generic type, and then write into it.

View File

@ -256,6 +256,93 @@ public final class DashMediaPeriodTest {
MediaPeriodAsserts.assertTrackGroups(dashMediaPeriod, expectedTrackGroups); MediaPeriodAsserts.assertTrackGroups(dashMediaPeriod, expectedTrackGroups);
} }
@Test
public void cea608AccessibilityDescriptor_createsCea608TrackGroup() {
Descriptor descriptor =
new Descriptor("urn:scte:dash:cc:cea-608:2015", "CC1=eng;CC3=deu", /* id= */ null);
DashManifest manifest =
createDashManifest(
createPeriod(
new AdaptationSet(
/* id= */ 123,
C.TRACK_TYPE_VIDEO,
Arrays.asList(
createVideoRepresentation(/* bitrate= */ 0),
createVideoRepresentation(/* bitrate= */ 1)),
/* accessibilityDescriptors= */ Collections.singletonList(descriptor),
/* essentialProperties= */ Collections.emptyList(),
/* supplementalProperties= */ Collections.emptyList())));
DashMediaPeriod dashMediaPeriod = createDashMediaPeriod(manifest, 0);
List<AdaptationSet> adaptationSets = manifest.getPeriod(0).adaptationSets;
// We expect two adaptation sets. The first containing the video representations, and the second
// containing the embedded CEA-608 tracks.
Format.Builder cea608FormatBuilder =
new Format.Builder().setSampleMimeType(MimeTypes.APPLICATION_CEA608);
TrackGroupArray expectedTrackGroups =
new TrackGroupArray(
new TrackGroup(
adaptationSets.get(0).representations.get(0).format,
adaptationSets.get(0).representations.get(1).format),
new TrackGroup(
cea608FormatBuilder
.setId("123:cea608:1")
.setLanguage("eng")
.setAccessibilityChannel(1)
.build(),
cea608FormatBuilder
.setId("123:cea608:3")
.setLanguage("deu")
.setAccessibilityChannel(3)
.build()));
MediaPeriodAsserts.assertTrackGroups(dashMediaPeriod, expectedTrackGroups);
}
@Test
public void cea708AccessibilityDescriptor_createsCea708TrackGroup() {
Descriptor descriptor =
new Descriptor(
"urn:scte:dash:cc:cea-708:2015", "1=lang:eng;2=lang:deu,war:1,er:1", /* id= */ null);
DashManifest manifest =
createDashManifest(
createPeriod(
new AdaptationSet(
/* id= */ 123,
C.TRACK_TYPE_VIDEO,
Arrays.asList(
createVideoRepresentation(/* bitrate= */ 0),
createVideoRepresentation(/* bitrate= */ 1)),
/* accessibilityDescriptors= */ Collections.singletonList(descriptor),
/* essentialProperties= */ Collections.emptyList(),
/* supplementalProperties= */ Collections.emptyList())));
DashMediaPeriod dashMediaPeriod = createDashMediaPeriod(manifest, 0);
List<AdaptationSet> adaptationSets = manifest.getPeriod(0).adaptationSets;
// We expect two adaptation sets. The first containing the video representations, and the second
// containing the embedded CEA-708 tracks.
Format.Builder cea608FormatBuilder =
new Format.Builder().setSampleMimeType(MimeTypes.APPLICATION_CEA708);
TrackGroupArray expectedTrackGroups =
new TrackGroupArray(
new TrackGroup(
adaptationSets.get(0).representations.get(0).format,
adaptationSets.get(0).representations.get(1).format),
new TrackGroup(
cea608FormatBuilder
.setId("123:cea708:1")
.setLanguage("eng")
.setAccessibilityChannel(1)
.build(),
cea608FormatBuilder
.setId("123:cea708:2")
.setLanguage("deu")
.setAccessibilityChannel(2)
.build()));
MediaPeriodAsserts.assertTrackGroups(dashMediaPeriod, expectedTrackGroups);
}
private static DashMediaPeriod createDashMediaPeriod(DashManifest manifest, int periodIndex) { private static DashMediaPeriod createDashMediaPeriod(DashManifest manifest, int periodIndex) {
return new DashMediaPeriod( return new DashMediaPeriod(
/* id= */ periodIndex, /* id= */ periodIndex,