Skip to content

Commit

Permalink
Parse trick-play role flags from DASH manifests
Browse files Browse the repository at this point in the history
Issue: #6054
PiperOrigin-RevId: 306641689
  • Loading branch information
ojw28 authored and icbaker committed Apr 15, 2020
1 parent 5092665 commit ba0028c
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 26 deletions.
Expand Up @@ -50,9 +50,10 @@ public class AdaptationSet {
*/
public final List<Descriptor> accessibilityDescriptors;

/**
* Supplemental properties in the adaptation set.
*/
/** Essential properties in the adaptation set. */
public final List<Descriptor> essentialProperties;

/** Supplemental properties in the adaptation set. */
public final List<Descriptor> supplementalProperties;

/**
Expand All @@ -62,21 +63,21 @@ public class AdaptationSet {
* {@code TRACK_TYPE_*} constants.
* @param representations {@link Representation}s in the adaptation set.
* @param accessibilityDescriptors Accessibility descriptors in the adaptation set.
* @param essentialProperties Essential properties in the adaptation set.
* @param supplementalProperties Supplemental properties in the adaptation set.
*/
public AdaptationSet(int id, int type, List<Representation> representations,
List<Descriptor> accessibilityDescriptors, List<Descriptor> supplementalProperties) {
public AdaptationSet(
int id,
int type,
List<Representation> representations,
List<Descriptor> accessibilityDescriptors,
List<Descriptor> essentialProperties,
List<Descriptor> supplementalProperties) {
this.id = id;
this.type = type;
this.representations = Collections.unmodifiableList(representations);
this.accessibilityDescriptors =
accessibilityDescriptors == null
? Collections.emptyList()
: Collections.unmodifiableList(accessibilityDescriptors);
this.supplementalProperties =
supplementalProperties == null
? Collections.emptyList()
: Collections.unmodifiableList(supplementalProperties);
this.accessibilityDescriptors = Collections.unmodifiableList(accessibilityDescriptors);
this.essentialProperties = Collections.unmodifiableList(essentialProperties);
this.supplementalProperties = Collections.unmodifiableList(supplementalProperties);
}

}
Expand Up @@ -224,9 +224,14 @@ private static ArrayList<AdaptationSet> copyAdaptationSets(
key = keys.poll();
} while (key.periodIndex == periodIndex && key.groupIndex == adaptationSetIndex);

copyAdaptationSets.add(new AdaptationSet(adaptationSet.id, adaptationSet.type,
copyRepresentations, adaptationSet.accessibilityDescriptors,
adaptationSet.supplementalProperties));
copyAdaptationSets.add(
new AdaptationSet(
adaptationSet.id,
adaptationSet.type,
copyRepresentations,
adaptationSet.accessibilityDescriptors,
adaptationSet.essentialProperties,
adaptationSet.supplementalProperties));
} while(key.periodIndex == periodIndex);
// Add back the last key which doesn't belong to the period being processed
keys.addFirst(key);
Expand Down
Expand Up @@ -289,6 +289,7 @@ protected AdaptationSet parseAdaptationSet(
ArrayList<Descriptor> inbandEventStreams = new ArrayList<>();
ArrayList<Descriptor> accessibilityDescriptors = new ArrayList<>();
ArrayList<Descriptor> roleDescriptors = new ArrayList<>();
ArrayList<Descriptor> essentialProperties = new ArrayList<>();
ArrayList<Descriptor> supplementalProperties = new ArrayList<>();
List<RepresentationInfo> representationInfos = new ArrayList<>();

Expand Down Expand Up @@ -317,6 +318,8 @@ protected AdaptationSet parseAdaptationSet(
audioChannels = parseAudioChannelConfiguration(xpp);
} else if (XmlPullParserUtil.isStartTag(xpp, "Accessibility")) {
accessibilityDescriptors.add(parseDescriptor(xpp, "Accessibility"));
} else if (XmlPullParserUtil.isStartTag(xpp, "EssentialProperty")) {
essentialProperties.add(parseDescriptor(xpp, "EssentialProperty"));
} else if (XmlPullParserUtil.isStartTag(xpp, "SupplementalProperty")) {
supplementalProperties.add(parseDescriptor(xpp, "SupplementalProperty"));
} else if (XmlPullParserUtil.isStartTag(xpp, "Representation")) {
Expand All @@ -334,6 +337,7 @@ protected AdaptationSet parseAdaptationSet(
language,
roleDescriptors,
accessibilityDescriptors,
essentialProperties,
supplementalProperties,
segmentBase,
periodDurationMs);
Expand Down Expand Up @@ -370,14 +374,28 @@ protected AdaptationSet parseAdaptationSet(
inbandEventStreams));
}

return buildAdaptationSet(id, contentType, representations, accessibilityDescriptors,
return buildAdaptationSet(
id,
contentType,
representations,
accessibilityDescriptors,
essentialProperties,
supplementalProperties);
}

protected AdaptationSet buildAdaptationSet(int id, int contentType,
List<Representation> representations, List<Descriptor> accessibilityDescriptors,
protected AdaptationSet buildAdaptationSet(
int id,
int contentType,
List<Representation> representations,
List<Descriptor> accessibilityDescriptors,
List<Descriptor> essentialProperties,
List<Descriptor> supplementalProperties) {
return new AdaptationSet(id, contentType, representations, accessibilityDescriptors,
return new AdaptationSet(
id,
contentType,
representations,
accessibilityDescriptors,
essentialProperties,
supplementalProperties);
}

Expand Down Expand Up @@ -492,6 +510,7 @@ protected RepresentationInfo parseRepresentation(
@Nullable String adaptationSetLanguage,
List<Descriptor> adaptationSetRoleDescriptors,
List<Descriptor> adaptationSetAccessibilityDescriptors,
List<Descriptor> adaptationSetEssentialProperties,
List<Descriptor> adaptationSetSupplementalProperties,
@Nullable SegmentBase segmentBase,
long periodDurationMs)
Expand All @@ -509,7 +528,9 @@ protected RepresentationInfo parseRepresentation(
String drmSchemeType = null;
ArrayList<SchemeData> drmSchemeDatas = new ArrayList<>();
ArrayList<Descriptor> inbandEventStreams = new ArrayList<>();
ArrayList<Descriptor> supplementalProperties = new ArrayList<>();
ArrayList<Descriptor> essentialProperties = new ArrayList<>(adaptationSetEssentialProperties);
ArrayList<Descriptor> supplementalProperties =
new ArrayList<>(adaptationSetSupplementalProperties);

boolean seenFirstBaseUrl = false;
do {
Expand Down Expand Up @@ -542,6 +563,8 @@ protected RepresentationInfo parseRepresentation(
}
} else if (XmlPullParserUtil.isStartTag(xpp, "InbandEventStream")) {
inbandEventStreams.add(parseDescriptor(xpp, "InbandEventStream"));
} else if (XmlPullParserUtil.isStartTag(xpp, "EssentialProperty")) {
essentialProperties.add(parseDescriptor(xpp, "EssentialProperty"));
} else if (XmlPullParserUtil.isStartTag(xpp, "SupplementalProperty")) {
supplementalProperties.add(parseDescriptor(xpp, "SupplementalProperty"));
} else {
Expand All @@ -563,6 +586,7 @@ protected RepresentationInfo parseRepresentation(
adaptationSetRoleDescriptors,
adaptationSetAccessibilityDescriptors,
codecs,
essentialProperties,
supplementalProperties);
segmentBase = segmentBase != null ? segmentBase : new SingleSegmentBase();

Expand All @@ -583,6 +607,7 @@ protected Format buildFormat(
List<Descriptor> roleDescriptors,
List<Descriptor> accessibilityDescriptors,
@Nullable String codecs,
List<Descriptor> essentialProperties,
List<Descriptor> supplementalProperties) {
@Nullable String sampleMimeType = getSampleMimeType(containerMimeType, codecs);
if (MimeTypes.AUDIO_E_AC3.equals(sampleMimeType)) {
Expand All @@ -591,6 +616,8 @@ protected Format buildFormat(
@C.SelectionFlags int selectionFlags = parseSelectionFlagsFromRoleDescriptors(roleDescriptors);
@C.RoleFlags int roleFlags = parseRoleFlagsFromRoleDescriptors(roleDescriptors);
roleFlags |= parseRoleFlagsFromAccessibilityDescriptors(accessibilityDescriptors);
roleFlags |= parseRoleFlagsFromProperties(essentialProperties);
roleFlags |= parseRoleFlagsFromProperties(supplementalProperties);

Format.Builder formatBuilder =
new Format.Builder()
Expand Down Expand Up @@ -1185,6 +1212,18 @@ protected int parseRoleFlagsFromAccessibilityDescriptors(
return result;
}

@C.RoleFlags
protected int parseRoleFlagsFromProperties(List<Descriptor> accessibilityDescriptors) {
@C.RoleFlags int result = 0;
for (int i = 0; i < accessibilityDescriptors.size(); i++) {
Descriptor descriptor = accessibilityDescriptors.get(i);
if ("http://dashif.org/guidelines/trickmode".equalsIgnoreCase(descriptor.schemeIdUri)) {
result |= C.ROLE_FLAG_TRICK_PLAY;
}
}
return result;
}

@C.RoleFlags
protected int parseDashRoleSchemeValue(@Nullable String value) {
if (value == null) {
Expand Down
Expand Up @@ -165,6 +165,7 @@ private static AdaptationSet createAdaptationSet(
trackType,
Arrays.asList(representations),
/* accessibilityDescriptors= */ Collections.emptyList(),
/* essentialProperties= */ Collections.emptyList(),
descriptor == null ? Collections.emptyList() : Collections.singletonList(descriptor));
}

Expand Down
Expand Up @@ -29,6 +29,7 @@
import com.google.android.exoplayer2.upstream.DummyDataSource;
import com.google.android.exoplayer2.util.MimeTypes;
import java.util.Arrays;
import java.util.Collections;
import org.junit.Test;
import org.junit.runner.RunWith;

Expand Down Expand Up @@ -69,7 +70,13 @@ private static Period newPeriod(AdaptationSet... adaptationSets) {
}

private static AdaptationSet newAdaptationSet(Representation... representations) {
return new AdaptationSet(0, C.TRACK_TYPE_VIDEO, Arrays.asList(representations), null, null);
return new AdaptationSet(
/* id= */ 0,
C.TRACK_TYPE_VIDEO,
Arrays.asList(representations),
/* accessibilityDescriptors= */ Collections.emptyList(),
/* essentialProperties= */ Collections.emptyList(),
/* supplementalProperties= */ Collections.emptyList());
}

private static Representation newRepresentation(DrmInitData drmInitData) {
Expand Down
Expand Up @@ -49,6 +49,7 @@ public class DashManifestParserTest {
private static final String SAMPLE_MPD_LABELS = "mpd/sample_mpd_labels";
private static final String SAMPLE_MPD_ASSET_IDENTIFIER = "mpd/sample_mpd_asset_identifier";
private static final String SAMPLE_MPD_TEXT = "mpd/sample_mpd_text";
private static final String SAMPLE_MPD_TRICK_PLAY = "mpd/sample_mpd_trick_play";

private static final String NEXT_TAG_NAME = "Next";
private static final String NEXT_TAG = "<" + NEXT_TAG_NAME + "/>";
Expand Down Expand Up @@ -173,7 +174,7 @@ public void parseMediaPresentationDescription_programInformation() throws IOExce
DashManifestParser parser = new DashManifestParser();
DashManifest manifest =
parser.parse(
Uri.parse("Https://example.com/test.mpd"),
Uri.parse("https://example.com/test.mpd"),
TestUtil.getInputStream(ApplicationProvider.getApplicationContext(), SAMPLE_MPD));
ProgramInformation expectedProgramInformation =
new ProgramInformation(
Expand Down Expand Up @@ -201,7 +202,7 @@ public void parseMediaPresentationDescription_text() throws IOException {
DashManifestParser parser = new DashManifestParser();
DashManifest manifest =
parser.parse(
Uri.parse("Https://example.com/test.mpd"),
Uri.parse("https://example.com/test.mpd"),
TestUtil.getInputStream(ApplicationProvider.getApplicationContext(), SAMPLE_MPD_TEXT));

List<AdaptationSet> adaptationSets = manifest.getPeriod(0).adaptationSets;
Expand All @@ -225,6 +226,46 @@ public void parseMediaPresentationDescription_text() throws IOException {
assertThat(adaptationSets.get(2).type).isEqualTo(C.TRACK_TYPE_TEXT);
}

@Test
public void parseMediaPresentationDescription_trickPlay() throws IOException {
DashManifestParser parser = new DashManifestParser();
DashManifest manifest =
parser.parse(
Uri.parse("https://example.com/test.mpd"),
TestUtil.getInputStream(
ApplicationProvider.getApplicationContext(), SAMPLE_MPD_TRICK_PLAY));

List<AdaptationSet> adaptationSets = manifest.getPeriod(0).adaptationSets;

AdaptationSet adaptationSet = adaptationSets.get(0);
assertThat(adaptationSet.essentialProperties).isEmpty();
assertThat(adaptationSet.supplementalProperties).isEmpty();
assertThat(adaptationSet.representations.get(0).format.roleFlags).isEqualTo(0);

adaptationSet = adaptationSets.get(1);
assertThat(adaptationSet.essentialProperties).isEmpty();
assertThat(adaptationSet.supplementalProperties).isEmpty();
assertThat(adaptationSet.representations.get(0).format.roleFlags).isEqualTo(0);

adaptationSet = adaptationSets.get(2);
assertThat(adaptationSet.essentialProperties).hasSize(1);
assertThat(adaptationSet.essentialProperties.get(0).schemeIdUri)
.isEqualTo("http://dashif.org/guidelines/trickmode");
assertThat(adaptationSet.essentialProperties.get(0).value).isEqualTo("0");
assertThat(adaptationSet.supplementalProperties).isEmpty();
assertThat(adaptationSet.representations.get(0).format.roleFlags)
.isEqualTo(C.ROLE_FLAG_TRICK_PLAY);

adaptationSet = adaptationSets.get(3);
assertThat(adaptationSet.essentialProperties).isEmpty();
assertThat(adaptationSet.supplementalProperties).hasSize(1);
assertThat(adaptationSet.supplementalProperties.get(0).schemeIdUri)
.isEqualTo("http://dashif.org/guidelines/trickmode");
assertThat(adaptationSet.supplementalProperties.get(0).value).isEqualTo("1");
assertThat(adaptationSet.representations.get(0).format.roleFlags)
.isEqualTo(C.ROLE_FLAG_TRICK_PLAY);
}

@Test
public void parseSegmentTimeline_repeatCount() throws Exception {
DashManifestParser parser = new DashManifestParser();
Expand Down
Expand Up @@ -239,6 +239,12 @@ private static Period newPeriod(String id, int startMs, AdaptationSet... adaptat
}

private static AdaptationSet newAdaptationSet(int seed, Representation... representations) {
return new AdaptationSet(++seed, ++seed, Arrays.asList(representations), null, null);
return new AdaptationSet(
++seed,
++seed,
Arrays.asList(representations),
/* accessibilityDescriptors= */ Collections.emptyList(),
/* essentialProperties= */ Collections.emptyList(),
/* supplementalProperties= */ Collections.emptyList());
}
}
32 changes: 32 additions & 0 deletions testdata/src/test/assets/mpd/sample_mpd_trick_play
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<MPD type="static" duration="1s" mediaPresentationDuration="PT1S">
<Period>
<SegmentTemplate startNumber="0" timescale="1000" media="sq/$Number$">
<SegmentTimeline>
<S d="1000"/>
</SegmentTimeline>
</SegmentTemplate>
<AdaptationSet id="0" mimeType="video/mp4" subsegmentAlignment="true">
<Representation id="0" codecs="avc1.42c01f" bandwidth="128000">
<BaseURL>https://test.com/0</BaseURL>
</Representation>
</AdaptationSet>
<AdaptationSet id="1" mimeType="video/mp4" subsegmentAlignment="true">
<Representation id="0" codecs="avc1.42c01f" bandwidth="128000">
<BaseURL>https://test.com/0</BaseURL>
</Representation>
</AdaptationSet>
<AdaptationSet id="2" mimeType="video/mp4" subsegmentAlignment="true">
<EssentialProperty schemeIdUri="http://dashif.org/guidelines/trickmode" value="0"/>
<Representation id="0" codecs="avc1.42c01f" bandwidth="128000">
<BaseURL>https://test.com/0</BaseURL>
</Representation>
</AdaptationSet>
<AdaptationSet id="3" mimeType="video/mp4" subsegmentAlignment="true">
<SupplementalProperty schemeIdUri="http://dashif.org/guidelines/trickmode" value="1"/>
<Representation id="0" codecs="avc1.42c01f" bandwidth="128000">
<BaseURL>https://test.com/0</BaseURL>
</Representation>
</AdaptationSet>
</Period>
</MPD>

0 comments on commit ba0028c

Please sign in to comment.