Skip to content

Commit

Permalink
Add optional id to TrackGroup.
Browse files Browse the repository at this point in the history
This allows to give TrackGroups an identifier. The underlying goal is
to provide a way to make otherwise identical TrackGroups
distinguishable.

Also set this id in all internal sources that may produce identical
TrackGroups in certain edge cases.

Issue: #9718
PiperOrigin-RevId: 413430719
  • Loading branch information
tonihei committed Dec 2, 2021
1 parent 0578b2e commit d53d51d
Show file tree
Hide file tree
Showing 12 changed files with 252 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1058,7 +1058,8 @@ private boolean updateTracksAndSelectionsAndNotifyIfChanged() {
new TracksInfo.TrackGroupInfo[castMediaTracks.size()];
for (int i = 0; i < castMediaTracks.size(); i++) {
MediaTrack mediaTrack = castMediaTracks.get(i);
trackGroups[i] = new TrackGroup(CastUtils.mediaTrackToFormat(mediaTrack));
trackGroups[i] =
new TrackGroup(/* id= */ Integer.toString(i), CastUtils.mediaTrackToFormat(mediaTrack));

long id = mediaTrack.getId();
@C.TrackType int trackType = MimeTypes.getTrackType(mediaTrack.getContentType());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import static com.google.android.exoplayer2.util.Assertions.checkArgument;

import android.os.Bundle;
import androidx.annotation.CheckResult;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Bundleable;
Expand All @@ -40,24 +41,48 @@ public final class TrackGroup implements Bundleable {

/** The number of tracks in the group. */
public final int length;
/** An identifier for the track group. */
public final String id;

private final Format[] formats;

// Lazily initialized hashcode.
private int hashCode;

/**
* Constructs an instance {@code TrackGroup} containing the provided {@code formats}.
* Constructs a track group containing the provided {@code formats}.
*
* @param formats Non empty array of format.
* @param formats The list of {@link Format Formats}. Must not be empty.
*/
public TrackGroup(Format... formats) {
this(/* id= */ "", formats);
}

/**
* Constructs a track group with the provided {@code id} and {@code formats}.
*
* @param id The identifier of the track group. May be an empty string.
* @param formats The list of {@link Format Formats}. Must not be empty.
*/
public TrackGroup(String id, Format... formats) {
checkArgument(formats.length > 0);
this.id = id;
this.formats = formats;
this.length = formats.length;
verifyCorrectness();
}

/**
* Returns a copy of this track group with the specified {@code id}.
*
* @param id The identifier for the copy of the track group.
* @return The copied track group.
*/
@CheckResult
public TrackGroup copyWithId(String id) {
return new TrackGroup(id, formats);
}

/**
* Returns the format of the track at a given index.
*
Expand Down Expand Up @@ -90,6 +115,7 @@ public int indexOf(Format format) {
public int hashCode() {
if (hashCode == 0) {
int result = 17;
result = 31 * result + id.hashCode();
result = 31 * result + Arrays.hashCode(formats);
hashCode = result;
}
Expand All @@ -105,25 +131,25 @@ public boolean equals(@Nullable Object obj) {
return false;
}
TrackGroup other = (TrackGroup) obj;
return length == other.length && Arrays.equals(formats, other.formats);
return length == other.length && id.equals(other.id) && Arrays.equals(formats, other.formats);
}

// Bundleable implementation.

@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
FIELD_FORMATS,
})
@IntDef({FIELD_FORMATS, FIELD_ID})
private @interface FieldNumber {}

private static final int FIELD_FORMATS = 0;
private static final int FIELD_ID = 1;

@Override
public Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(
keyForField(FIELD_FORMATS), BundleableUtil.toBundleArrayList(Lists.newArrayList(formats)));
bundle.putString(keyForField(FIELD_ID), id);
return bundle;
}

Expand All @@ -135,7 +161,8 @@ public Bundle toBundle() {
Format.CREATOR,
bundle.getParcelableArrayList(keyForField(FIELD_FORMATS)),
ImmutableList.of());
return new TrackGroup(formats.toArray(new Format[0]));
String id = bundle.getString(keyForField(FIELD_ID), /* defaultValue= */ "");
return new TrackGroup(id, formats.toArray(new Format[0]));
};

private static String keyForField(@FieldNumber int field) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ public void roundTripViaBundle_ofTrackGroup_yieldsEqualInstance() {
Format.Builder formatBuilder = new Format.Builder();
Format format1 = formatBuilder.setSampleMimeType(MimeTypes.VIDEO_H264).build();
Format format2 = formatBuilder.setSampleMimeType(MimeTypes.AUDIO_AAC).build();
String id = "abc";

TrackGroup trackGroupToBundle = new TrackGroup(format1, format2);
TrackGroup trackGroupToBundle = new TrackGroup(id, format1, format2);

TrackGroup trackGroupFromBundle = TrackGroup.CREATOR.fromBundle(trackGroupToBundle.toBundle());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,25 @@
*/
package com.google.android.exoplayer2.source;

import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static java.lang.Math.max;

import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.SeekParameters;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.source.chunk.Chunk;
import com.google.android.exoplayer2.source.chunk.MediaChunk;
import com.google.android.exoplayer2.source.chunk.MediaChunkIterator;
import com.google.android.exoplayer2.trackselection.ExoTrackSelection;
import com.google.android.exoplayer2.util.Assertions;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import org.checkerframework.checker.nullness.compatqual.NullableType;
Expand All @@ -40,6 +46,7 @@
private final IdentityHashMap<SampleStream, Integer> streamPeriodIndices;
private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
private final ArrayList<MediaPeriod> childrenPendingPreparation;
private final HashMap<TrackGroup, TrackGroup> childTrackGroupByMergedTrackGroup;

@Nullable private Callback callback;
@Nullable private TrackGroupArray trackGroups;
Expand All @@ -53,6 +60,7 @@ public MergingMediaPeriod(
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
this.periods = periods;
childrenPendingPreparation = new ArrayList<>();
childTrackGroupByMergedTrackGroup = new HashMap<>();
compositeSequenceableLoader =
compositeSequenceableLoaderFactory.createCompositeSequenceableLoader();
streamPeriodIndices = new IdentityHashMap<>();
Expand Down Expand Up @@ -111,9 +119,11 @@ public long selectTracks(
streamChildIndices[i] = streamChildIndex == null ? C.INDEX_UNSET : streamChildIndex;
selectionChildIndices[i] = C.INDEX_UNSET;
if (selections[i] != null) {
TrackGroup trackGroup = selections[i].getTrackGroup();
TrackGroup mergedTrackGroup = selections[i].getTrackGroup();
TrackGroup childTrackGroup =
checkNotNull(childTrackGroupByMergedTrackGroup.get(mergedTrackGroup));
for (int j = 0; j < periods.length; j++) {
if (periods[j].getTrackGroups().indexOf(trackGroup) != C.INDEX_UNSET) {
if (periods[j].getTrackGroups().indexOf(childTrackGroup) != C.INDEX_UNSET) {
selectionChildIndices[i] = j;
break;
}
Expand All @@ -129,7 +139,15 @@ public long selectTracks(
for (int i = 0; i < periods.length; i++) {
for (int j = 0; j < selections.length; j++) {
childStreams[j] = streamChildIndices[j] == i ? streams[j] : null;
childSelections[j] = selectionChildIndices[j] == i ? selections[j] : null;
if (selectionChildIndices[j] == i) {
ExoTrackSelection mergedTrackSelection = checkNotNull(selections[j]);
TrackGroup mergedTrackGroup = mergedTrackSelection.getTrackGroup();
TrackGroup childTrackGroup =
checkNotNull(childTrackGroupByMergedTrackGroup.get(mergedTrackGroup));
childSelections[j] = new ForwardingTrackSelection(mergedTrackSelection, childTrackGroup);
} else {
childSelections[j] = null;
}
}
long selectPositionUs =
periods[i].selectTracks(
Expand Down Expand Up @@ -268,11 +286,14 @@ public void onPrepared(MediaPeriod preparedPeriod) {
}
TrackGroup[] trackGroupArray = new TrackGroup[totalTrackGroupCount];
int trackGroupIndex = 0;
for (MediaPeriod period : periods) {
TrackGroupArray periodTrackGroups = period.getTrackGroups();
for (int i = 0; i < periods.length; i++) {
TrackGroupArray periodTrackGroups = periods[i].getTrackGroups();
int periodTrackGroupCount = periodTrackGroups.length;
for (int j = 0; j < periodTrackGroupCount; j++) {
trackGroupArray[trackGroupIndex++] = periodTrackGroups.get(j);
TrackGroup childTrackGroup = periodTrackGroups.get(j);
TrackGroup mergedTrackGroup = childTrackGroup.copyWithId(i + ":" + childTrackGroup.id);
childTrackGroupByMergedTrackGroup.put(mergedTrackGroup, childTrackGroup);
trackGroupArray[trackGroupIndex++] = mergedTrackGroup;
}
}
trackGroups = new TrackGroupArray(trackGroupArray);
Expand Down Expand Up @@ -453,4 +474,138 @@ public int skipData(long positionUs) {
return sampleStream.skipData(positionUs - timeOffsetUs);
}
}

private static final class ForwardingTrackSelection implements ExoTrackSelection {

private final ExoTrackSelection trackSelection;
private final TrackGroup trackGroup;

public ForwardingTrackSelection(ExoTrackSelection trackSelection, TrackGroup trackGroup) {
this.trackSelection = trackSelection;
this.trackGroup = trackGroup;
}

@Override
public @Type int getType() {
return trackSelection.getType();
}

@Override
public TrackGroup getTrackGroup() {
return trackGroup;
}

@Override
public int length() {
return trackSelection.length();
}

@Override
public Format getFormat(int index) {
return trackSelection.getFormat(index);
}

@Override
public int getIndexInTrackGroup(int index) {
return trackSelection.getIndexInTrackGroup(index);
}

@Override
public int indexOf(Format format) {
return trackSelection.indexOf(format);
}

@Override
public int indexOf(int indexInTrackGroup) {
return trackSelection.indexOf(indexInTrackGroup);
}

@Override
public void enable() {
trackSelection.enable();
}

@Override
public void disable() {
trackSelection.disable();
}

@Override
public Format getSelectedFormat() {
return trackSelection.getSelectedFormat();
}

@Override
public int getSelectedIndexInTrackGroup() {
return trackSelection.getSelectedIndexInTrackGroup();
}

@Override
public int getSelectedIndex() {
return trackSelection.getSelectedIndex();
}

@Override
public int getSelectionReason() {
return trackSelection.getSelectionReason();
}

@Nullable
@Override
public Object getSelectionData() {
return trackSelection.getSelectionData();
}

@Override
public void onPlaybackSpeed(float playbackSpeed) {
trackSelection.onPlaybackSpeed(playbackSpeed);
}

@Override
public void onDiscontinuity() {
trackSelection.onDiscontinuity();
}

@Override
public void onRebuffer() {
trackSelection.onRebuffer();
}

@Override
public void onPlayWhenReadyChanged(boolean playWhenReady) {
trackSelection.onPlayWhenReadyChanged(playWhenReady);
}

@Override
public void updateSelectedTrack(
long playbackPositionUs,
long bufferedDurationUs,
long availableDurationUs,
List<? extends MediaChunk> queue,
MediaChunkIterator[] mediaChunkIterators) {
trackSelection.updateSelectedTrack(
playbackPositionUs, bufferedDurationUs, availableDurationUs, queue, mediaChunkIterators);
}

@Override
public int evaluateQueueSize(long playbackPositionUs, List<? extends MediaChunk> queue) {
return trackSelection.evaluateQueueSize(playbackPositionUs, queue);
}

@Override
public boolean shouldCancelChunkLoad(
long playbackPositionUs, Chunk loadingChunk, List<? extends MediaChunk> queue) {
return trackSelection.shouldCancelChunkLoad(playbackPositionUs, loadingChunk, queue);
}

@Override
public boolean blacklist(int index, long exclusionDurationMs) {
return trackSelection.blacklist(index, exclusionDurationMs);
}

@Override
public boolean isBlacklisted(int index, long nowMs) {
return trackSelection.isBlacklisted(index, nowMs);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@ private void maybeFinishPrepare() {
}
}
trackFormat = trackFormat.copyWithCryptoType(drmSessionManager.getCryptoType(trackFormat));
trackArray[i] = new TrackGroup(trackFormat);
trackArray[i] = new TrackGroup(/* id= */ Integer.toString(i), trackFormat);
}
trackState = new TrackState(new TrackGroupArray(trackArray), trackIsAudioVideoFlags);
prepared = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ public void onTracksChanged(
trackGroup.length,
mappedTrackInfo.getAdaptiveSupport(
rendererIndex, groupIndex, /* includeCapabilitiesExceededTracks= */ false));
logd(" Group:" + groupIndex + ", adaptive_supported=" + adaptiveSupport + " [");
logd(" Group:" + trackGroup.id + ", adaptive_supported=" + adaptiveSupport + " [");
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
String status = getTrackStatusString(trackSelection, trackGroup, trackIndex);
String formatSupport =
Expand Down
Loading

0 comments on commit d53d51d

Please sign in to comment.