Skip to content

Commit

Permalink
Use single TrackGroup for switchable adaptation sets
Browse files Browse the repository at this point in the history
Issue: #2431

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=157236031
  • Loading branch information
ojw28 committed May 31, 2017
1 parent 27fc82f commit 122b2a1
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 92 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ public interface DashChunkSource extends ChunkSource {
interface Factory {

DashChunkSource createDashChunkSource(LoaderErrorThrower manifestLoaderErrorThrower,
DashManifest manifest, int periodIndex, int adaptationSetIndex,
TrackSelection trackSelection, long elapsedRealtimeOffsetMs,
DashManifest manifest, int periodIndex, int[] adaptationSetIndices,
TrackSelection trackSelection, int type, long elapsedRealtimeOffsetMs,
boolean enableEventMessageTrack, boolean enableCea608Track);

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.google.android.exoplayer2.source.dash;

import android.util.Pair;
import android.util.SparseIntArray;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher;
Expand All @@ -37,6 +38,7 @@
import com.google.android.exoplayer2.upstream.LoaderErrorThrower;
import com.google.android.exoplayer2.util.MimeTypes;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
Expand All @@ -55,7 +57,7 @@
private final LoaderErrorThrower manifestLoaderErrorThrower;
private final Allocator allocator;
private final TrackGroupArray trackGroups;
private final EmbeddedTrackInfo[] embeddedTrackInfos;
private final TrackGroupInfo[] trackGroupInfos;

private Callback callback;
private ChunkSampleStream<DashChunkSource>[] sampleStreams;
Expand All @@ -80,9 +82,9 @@ public DashMediaPeriod(int id, DashManifest manifest, int periodIndex,
sampleStreams = newSampleStreamArray(0);
sequenceableLoader = new CompositeSequenceableLoader(sampleStreams);
adaptationSets = manifest.getPeriod(periodIndex).adaptationSets;
Pair<TrackGroupArray, EmbeddedTrackInfo[]> result = buildTrackGroups(adaptationSets);
Pair<TrackGroupArray, TrackGroupInfo[]> result = buildTrackGroups(adaptationSets);
trackGroups = result.first;
embeddedTrackInfos = result.second;
trackGroupInfos = result.second;
}

public void updateManifest(DashManifest manifest, int periodIndex) {
Expand Down Expand Up @@ -122,7 +124,6 @@ public TrackGroupArray getTrackGroups() {
@Override
public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamFlags,
SampleStream[] streams, boolean[] streamResetFlags, long positionUs) {
int adaptationSetCount = adaptationSets.size();
HashMap<Integer, ChunkSampleStream<DashChunkSource>> primarySampleStreams = new HashMap<>();
// First pass for primary tracks.
for (int i = 0; i < selections.length; i++) {
Expand All @@ -133,14 +134,15 @@ public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamF
stream.release();
streams[i] = null;
} else {
int adaptationSetIndex = trackGroups.indexOf(selections[i].getTrackGroup());
primarySampleStreams.put(adaptationSetIndex, stream);
int trackGroupIndex = trackGroups.indexOf(selections[i].getTrackGroup());
primarySampleStreams.put(trackGroupIndex, stream);
}
}
if (streams[i] == null && selections[i] != null) {
int trackGroupIndex = trackGroups.indexOf(selections[i].getTrackGroup());
if (trackGroupIndex < adaptationSetCount) {
ChunkSampleStream<DashChunkSource> stream = buildSampleStream(trackGroupIndex,
TrackGroupInfo trackGroupInfo = trackGroupInfos[trackGroupIndex];
if (trackGroupInfo.isPrimary) {
ChunkSampleStream<DashChunkSource> stream = buildSampleStream(trackGroupInfo,
selections[i], positionUs);
primarySampleStreams.put(trackGroupIndex, stream);
streams[i] = stream;
Expand All @@ -160,19 +162,18 @@ public long selectTracks(TrackSelection[] selections, boolean[] mayRetainStreamF
// may have been replaced, selected or deselected.
if (selections[i] != null) {
int trackGroupIndex = trackGroups.indexOf(selections[i].getTrackGroup());
if (trackGroupIndex >= adaptationSetCount) {
int embeddedTrackIndex = trackGroupIndex - adaptationSetCount;
EmbeddedTrackInfo embeddedTrackInfo = embeddedTrackInfos[embeddedTrackIndex];
int adaptationSetIndex = embeddedTrackInfo.adaptationSetIndex;
ChunkSampleStream<?> primaryStream = primarySampleStreams.get(adaptationSetIndex);
TrackGroupInfo trackGroupInfo = trackGroupInfos[trackGroupIndex];
if (!trackGroupInfo.isPrimary) {
ChunkSampleStream<?> primaryStream = primarySampleStreams.get(
trackGroupInfo.primaryTrackGroupIndex);
SampleStream stream = streams[i];
boolean mayRetainStream = primaryStream == null ? stream instanceof EmptySampleStream
: (stream instanceof EmbeddedSampleStream
&& ((EmbeddedSampleStream) stream).parent == primaryStream);
if (!mayRetainStream) {
releaseIfEmbeddedSampleStream(stream);
streams[i] = primaryStream == null ? new EmptySampleStream()
: primaryStream.selectEmbeddedTrack(positionUs, embeddedTrackInfo.trackType);
: primaryStream.selectEmbeddedTrack(positionUs, trackGroupInfo.trackType);
streamResetFlags[i] = true;
}
}
Expand Down Expand Up @@ -235,95 +236,163 @@ public void onContinueLoadingRequested(ChunkSampleStream<DashChunkSource> sample

// Internal methods.

private static Pair<TrackGroupArray, EmbeddedTrackInfo[]> buildTrackGroups(
private static Pair<TrackGroupArray, TrackGroupInfo[]> buildTrackGroups(
List<AdaptationSet> adaptationSets) {
int adaptationSetCount = adaptationSets.size();
int embeddedTrackCount = getEmbeddedTrackCount(adaptationSets);
TrackGroup[] trackGroupArray = new TrackGroup[adaptationSetCount + embeddedTrackCount];
EmbeddedTrackInfo[] embeddedTrackInfos = new EmbeddedTrackInfo[embeddedTrackCount];
int[][] groupedAdaptationSetIndices = getGroupedAdaptationSetIndices(adaptationSets);

int primaryGroupCount = groupedAdaptationSetIndices.length;
boolean[] primaryGroupHasEventMessageTrackFlags = new boolean[primaryGroupCount];
boolean[] primaryGroupHasCea608TrackFlags = new boolean[primaryGroupCount];
int totalGroupCount = primaryGroupCount;
for (int i = 0; i < primaryGroupCount; i++) {
if (hasEventMessageTrack(adaptationSets, groupedAdaptationSetIndices[i])) {
primaryGroupHasEventMessageTrackFlags[i] = true;
totalGroupCount++;
}
if (hasCea608Track(adaptationSets, groupedAdaptationSetIndices[i])) {
primaryGroupHasCea608TrackFlags[i] = true;
totalGroupCount++;
}
}

int embeddedTrackIndex = 0;
for (int i = 0; i < adaptationSetCount; i++) {
AdaptationSet adaptationSet = adaptationSets.get(i);
List<Representation> representations = adaptationSet.representations;
TrackGroup[] trackGroups = new TrackGroup[totalGroupCount];
TrackGroupInfo[] trackGroupInfos = new TrackGroupInfo[totalGroupCount];

int trackGroupCount = 0;
for (int i = 0; i < primaryGroupCount; i++) {
int[] adaptationSetIndices = groupedAdaptationSetIndices[i];
List<Representation> representations = new ArrayList<>();
for (int adaptationSetIndex : adaptationSetIndices) {
representations.addAll(adaptationSets.get(adaptationSetIndex).representations);
}
Format[] formats = new Format[representations.size()];
for (int j = 0; j < formats.length; j++) {
formats[j] = representations.get(j).format;
}
trackGroupArray[i] = new TrackGroup(formats);
if (hasEventMessageTrack(adaptationSet)) {
Format format = Format.createSampleFormat(adaptationSet.id + ":emsg",

AdaptationSet firstAdaptationSet = adaptationSets.get(adaptationSetIndices[0]);
int primaryTrackGroupIndex = trackGroupCount;
boolean hasEventMessageTrack = primaryGroupHasEventMessageTrackFlags[i];
boolean hasCea608Track = primaryGroupHasEventMessageTrackFlags[i];

trackGroups[trackGroupCount] = new TrackGroup(formats);
trackGroupInfos[trackGroupCount++] = new TrackGroupInfo(firstAdaptationSet.type,
adaptationSetIndices, primaryTrackGroupIndex, true, hasEventMessageTrack, hasCea608Track);
if (hasEventMessageTrack) {
Format format = Format.createSampleFormat(firstAdaptationSet.id + ":emsg",
MimeTypes.APPLICATION_EMSG, null, Format.NO_VALUE, null);
trackGroupArray[adaptationSetCount + embeddedTrackIndex] = new TrackGroup(format);
embeddedTrackInfos[embeddedTrackIndex++] = new EmbeddedTrackInfo(i, C.TRACK_TYPE_METADATA);
trackGroups[trackGroupCount] = new TrackGroup(format);
trackGroupInfos[trackGroupCount++] = new TrackGroupInfo(C.TRACK_TYPE_METADATA,
adaptationSetIndices, primaryTrackGroupIndex, false, false, false);
}
if (hasCea608Track(adaptationSet)) {
Format format = Format.createTextSampleFormat(adaptationSet.id + ":cea608",
if (hasCea608Track) {
Format format = Format.createTextSampleFormat(firstAdaptationSet.id + ":cea608",
MimeTypes.APPLICATION_CEA608, null, Format.NO_VALUE, 0, null, null);
trackGroupArray[adaptationSetCount + embeddedTrackIndex] = new TrackGroup(format);
embeddedTrackInfos[embeddedTrackIndex++] = new EmbeddedTrackInfo(i, C.TRACK_TYPE_TEXT);
trackGroups[trackGroupCount] = new TrackGroup(format);
trackGroupInfos[trackGroupCount++] = new TrackGroupInfo(C.TRACK_TYPE_TEXT,
adaptationSetIndices, primaryTrackGroupIndex, false, false, false);
}
}

return Pair.create(new TrackGroupArray(trackGroupArray), embeddedTrackInfos);
return Pair.create(new TrackGroupArray(trackGroups), trackGroupInfos);
}

private ChunkSampleStream<DashChunkSource> buildSampleStream(int adaptationSetIndex,
private static int[][] getGroupedAdaptationSetIndices(List<AdaptationSet> adaptationSets) {
int adaptationSetCount = adaptationSets.size();
SparseIntArray idToIndexMap = new SparseIntArray(adaptationSetCount);
for (int i = 0; i < adaptationSetCount; i++) {
idToIndexMap.put(adaptationSets.get(i).id, i);
}

int[][] groupedAdaptationSetIndices = new int[adaptationSetCount][];
boolean[] adaptationSetUsedFlags = new boolean[adaptationSetCount];

int groupCount = 0;
for (int i = 0; i < adaptationSetCount; i++) {
if (adaptationSetUsedFlags[i]) {
// This adaptation set has already been included in a group.
continue;
}
adaptationSetUsedFlags[i] = true;
Descriptor adaptationSetSwitchingProperty = findAdaptationSetSwitchingProperty(
adaptationSets.get(i).supplementalProperties);
if (adaptationSetSwitchingProperty == null) {
groupedAdaptationSetIndices[groupCount++] = new int[] {i};
} else {
String[] extraAdaptationSetIds = adaptationSetSwitchingProperty.value.split(",");
int[] adaptationSetIndices = new int[1 + extraAdaptationSetIds.length];
adaptationSetIndices[0] = i;
for (int j = 0; j < extraAdaptationSetIds.length; j++) {
int extraIndex = idToIndexMap.get(Integer.parseInt(extraAdaptationSetIds[j]));
adaptationSetUsedFlags[extraIndex] = true;
adaptationSetIndices[1 + j] = extraIndex;
}
groupedAdaptationSetIndices[groupCount++] = adaptationSetIndices;
}
}

return groupCount < adaptationSetCount
? Arrays.copyOf(groupedAdaptationSetIndices, groupCount) : groupedAdaptationSetIndices;
}

private ChunkSampleStream<DashChunkSource> buildSampleStream(TrackGroupInfo trackGroupInfo,
TrackSelection selection, long positionUs) {
AdaptationSet adaptationSet = adaptationSets.get(adaptationSetIndex);
int embeddedTrackCount = 0;
int[] embeddedTrackTypes = new int[2];
boolean enableEventMessageTrack = hasEventMessageTrack(adaptationSet);
boolean enableEventMessageTrack = trackGroupInfo.hasEmbeddedEventMessageTrack;
if (enableEventMessageTrack) {
embeddedTrackTypes[embeddedTrackCount++] = C.TRACK_TYPE_METADATA;
}
boolean enableCea608Track = hasCea608Track(adaptationSet);
boolean enableCea608Track = trackGroupInfo.hasEmbeddedCea608Track;
if (enableCea608Track) {
embeddedTrackTypes[embeddedTrackCount++] = C.TRACK_TYPE_TEXT;
}
if (embeddedTrackCount < embeddedTrackTypes.length) {
embeddedTrackTypes = Arrays.copyOf(embeddedTrackTypes, embeddedTrackCount);
}
DashChunkSource chunkSource = chunkSourceFactory.createDashChunkSource(
manifestLoaderErrorThrower, manifest, periodIndex, adaptationSetIndex, selection,
elapsedRealtimeOffset, enableEventMessageTrack, enableCea608Track);
ChunkSampleStream<DashChunkSource> stream = new ChunkSampleStream<>(adaptationSet.type,
manifestLoaderErrorThrower, manifest, periodIndex, trackGroupInfo.adaptationSetIndices,
selection, trackGroupInfo.trackType, elapsedRealtimeOffset, enableEventMessageTrack,
enableCea608Track);
ChunkSampleStream<DashChunkSource> stream = new ChunkSampleStream<>(trackGroupInfo.trackType,
embeddedTrackTypes, chunkSource, this, allocator, positionUs, minLoadableRetryCount,
eventDispatcher);
return stream;
}

private static int getEmbeddedTrackCount(List<AdaptationSet> adaptationSets) {
int embeddedTrackCount = 0;
for (int i = 0; i < adaptationSets.size(); i++) {
AdaptationSet adaptationSet = adaptationSets.get(i);
if (hasEventMessageTrack(adaptationSet)) {
embeddedTrackCount++;
}
if (hasCea608Track(adaptationSet)) {
embeddedTrackCount++;
private static Descriptor findAdaptationSetSwitchingProperty(List<Descriptor> descriptors) {
for (int i = 0; i < descriptors.size(); i++) {
Descriptor descriptor = descriptors.get(i);
if ("urn:mpeg:dash:adaptation-set-switching:2016".equals(descriptor.schemeIdUri)) {
return descriptor;
}
}
return embeddedTrackCount;
return null;
}

private static boolean hasEventMessageTrack(AdaptationSet adaptationSet) {
List<Representation> representations = adaptationSet.representations;
for (int i = 0; i < representations.size(); i++) {
Representation representation = representations.get(i);
if (!representation.inbandEventStreams.isEmpty()) {
return true;
private static boolean hasEventMessageTrack(List<AdaptationSet> adaptationSets,
int[] adaptationSetIndices) {
for (int i : adaptationSetIndices) {
List<Representation> representations = adaptationSets.get(i).representations;
for (int j = 0; j < representations.size(); j++) {
Representation representation = representations.get(j);
if (!representation.inbandEventStreams.isEmpty()) {
return true;
}
}
}
return false;
}

private static boolean hasCea608Track(AdaptationSet adaptationSet) {
List<Descriptor> descriptors = adaptationSet.accessibilityDescriptors;
for (int i = 0; i < descriptors.size(); i++) {
Descriptor descriptor = descriptors.get(i);
if ("urn:scte:dash:cc:cea-608:2015".equals(descriptor.schemeIdUri)) {
return true;
private static boolean hasCea608Track(List<AdaptationSet> adaptationSets,
int[] adaptationSetIndices) {
for (int i : adaptationSetIndices) {
List<Descriptor> descriptors = adaptationSets.get(i).accessibilityDescriptors;
for (int j = 0; j < descriptors.size(); j++) {
Descriptor descriptor = descriptors.get(j);
if ("urn:scte:dash:cc:cea-608:2015".equals(descriptor.schemeIdUri)) {
return true;
}
}
}
return false;
Expand All @@ -340,14 +409,24 @@ private static void releaseIfEmbeddedSampleStream(SampleStream sampleStream) {
}
}

private static final class EmbeddedTrackInfo {
private static final class TrackGroupInfo {

public final int adaptationSetIndex;
public final int[] adaptationSetIndices;
public final int trackType;
public final boolean isPrimary;

public final int primaryTrackGroupIndex;
public final boolean hasEmbeddedEventMessageTrack;
public final boolean hasEmbeddedCea608Track;

public EmbeddedTrackInfo(int adaptationSetIndex, int trackType) {
this.adaptationSetIndex = adaptationSetIndex;
public TrackGroupInfo(int trackType, int[] adaptationSetIndices, int primaryTrackGroupIndex,
boolean isPrimary, boolean hasEmbeddedEventMessageTrack, boolean hasEmbeddedCea608Track) {
this.trackType = trackType;
this.adaptationSetIndices = adaptationSetIndices;
this.primaryTrackGroupIndex = primaryTrackGroupIndex;
this.isPrimary = isPrimary;
this.hasEmbeddedEventMessageTrack = hasEmbeddedEventMessageTrack;
this.hasEmbeddedCea608Track = hasEmbeddedCea608Track;
}

}
Expand Down
Loading

0 comments on commit 122b2a1

Please sign in to comment.