Add TracksInfo to the Player API

TracksInfo is very similar to
`MappingTrackSelector.MappedTracksInfo` with some
fields removed to simplify the Player API,
notably it doesn't expose the renderer concept.

A significant difference is the addition of a `selected` boolean
field which avoids having a separate `getCurrentTrackSelection`
API.

This cl is a part of the bigger track selection change,
splitted for ease of review.

In particular, the MediaSession implementation and UI usage
have been slitted in child cls.

Find all cls with the tag:
#player-track-selection

PiperOrigin-RevId: 400937124
This commit is contained in:
krocard 2021-10-05 11:16:12 +01:00 committed by bachinger
parent 47573d4575
commit ac881be2fc
22 changed files with 727 additions and 59 deletions

View File

@ -37,6 +37,7 @@ import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
@ -66,6 +67,7 @@ import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.ResultCallback; import com.google.android.gms.common.api.ResultCallback;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.util.List; import java.util.List;
import org.checkerframework.checker.nullness.compatqual.NullableType;
import org.checkerframework.checker.nullness.qual.RequiresNonNull; import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/** /**
@ -101,7 +103,8 @@ public final class CastPlayer extends BasePlayer {
COMMAND_GET_TIMELINE, COMMAND_GET_TIMELINE,
COMMAND_GET_MEDIA_ITEMS_METADATA, COMMAND_GET_MEDIA_ITEMS_METADATA,
COMMAND_SET_MEDIA_ITEMS_METADATA, COMMAND_SET_MEDIA_ITEMS_METADATA,
COMMAND_CHANGE_MEDIA_ITEMS) COMMAND_CHANGE_MEDIA_ITEMS,
COMMAND_GET_TRACK_INFOS)
.build(); .build();
public static final float MIN_SPEED_SUPPORTED = 0.5f; public static final float MIN_SPEED_SUPPORTED = 0.5f;
@ -142,6 +145,7 @@ public final class CastPlayer extends BasePlayer {
private CastTimeline currentTimeline; private CastTimeline currentTimeline;
private TrackGroupArray currentTrackGroups; private TrackGroupArray currentTrackGroups;
private TrackSelectionArray currentTrackSelection; private TrackSelectionArray currentTrackSelection;
private TracksInfo currentTracksInfo;
private Commands availableCommands; private Commands availableCommands;
@Player.State private int playbackState; @Player.State private int playbackState;
private int currentWindowIndex; private int currentWindowIndex;
@ -219,6 +223,7 @@ public final class CastPlayer extends BasePlayer {
currentTimeline = CastTimeline.EMPTY_CAST_TIMELINE; currentTimeline = CastTimeline.EMPTY_CAST_TIMELINE;
currentTrackGroups = TrackGroupArray.EMPTY; currentTrackGroups = TrackGroupArray.EMPTY;
currentTrackSelection = EMPTY_TRACK_SELECTION_ARRAY; currentTrackSelection = EMPTY_TRACK_SELECTION_ARRAY;
currentTracksInfo = TracksInfo.EMPTY;
availableCommands = new Commands.Builder().addAll(PERMANENT_AVAILABLE_COMMANDS).build(); availableCommands = new Commands.Builder().addAll(PERMANENT_AVAILABLE_COMMANDS).build();
pendingSeekWindowIndex = C.INDEX_UNSET; pendingSeekWindowIndex = C.INDEX_UNSET;
pendingSeekPositionMs = C.TIME_UNSET; pendingSeekPositionMs = C.TIME_UNSET;
@ -583,14 +588,19 @@ public final class CastPlayer extends BasePlayer {
return false; return false;
} }
@Override
public TrackGroupArray getCurrentTrackGroups() {
return currentTrackGroups;
}
@Override @Override
public TrackSelectionArray getCurrentTrackSelections() { public TrackSelectionArray getCurrentTrackSelections() {
return currentTrackSelection; return currentTrackSelection;
} }
@Override @Override
public TrackGroupArray getCurrentTrackGroups() { public TracksInfo getCurrentTracksInfo() {
return currentTrackGroups; return currentTracksInfo;
} }
@Override @Override
@ -871,6 +881,8 @@ public final class CastPlayer extends BasePlayer {
listeners.queueEvent( listeners.queueEvent(
Player.EVENT_TRACKS_CHANGED, Player.EVENT_TRACKS_CHANGED,
listener -> listener.onTracksChanged(currentTrackGroups, currentTrackSelection)); listener -> listener.onTracksChanged(currentTrackGroups, currentTrackSelection));
listeners.queueEvent(
Player.EVENT_TRACKS_CHANGED, listener -> listener.onTracksInfoChanged(currentTracksInfo));
} }
updateAvailableCommandsAndNotifyIfChanged(); updateAvailableCommandsAndNotifyIfChanged();
listeners.flushEvents(); listeners.flushEvents();
@ -1032,6 +1044,7 @@ public final class CastPlayer extends BasePlayer {
boolean hasChanged = !currentTrackGroups.isEmpty(); boolean hasChanged = !currentTrackGroups.isEmpty();
currentTrackGroups = TrackGroupArray.EMPTY; currentTrackGroups = TrackGroupArray.EMPTY;
currentTrackSelection = EMPTY_TRACK_SELECTION_ARRAY; currentTrackSelection = EMPTY_TRACK_SELECTION_ARRAY;
currentTracksInfo = TracksInfo.EMPTY;
return hasChanged; return hasChanged;
} }
long[] activeTrackIds = mediaStatus.getActiveTrackIds(); long[] activeTrackIds = mediaStatus.getActiveTrackIds();
@ -1040,7 +1053,9 @@ public final class CastPlayer extends BasePlayer {
} }
TrackGroup[] trackGroups = new TrackGroup[castMediaTracks.size()]; TrackGroup[] trackGroups = new TrackGroup[castMediaTracks.size()];
TrackSelection[] trackSelections = new TrackSelection[RENDERER_COUNT]; @NullableType TrackSelection[] trackSelections = new TrackSelection[RENDERER_COUNT];
TracksInfo.TrackGroupInfo[] trackGroupInfos =
new TracksInfo.TrackGroupInfo[castMediaTracks.size()];
for (int i = 0; i < castMediaTracks.size(); i++) { for (int i = 0; i < castMediaTracks.size(); i++) {
MediaTrack mediaTrack = castMediaTracks.get(i); MediaTrack mediaTrack = castMediaTracks.get(i);
trackGroups[i] = new TrackGroup(CastUtils.mediaTrackToFormat(mediaTrack)); trackGroups[i] = new TrackGroup(CastUtils.mediaTrackToFormat(mediaTrack));
@ -1048,19 +1063,28 @@ public final class CastPlayer extends BasePlayer {
long id = mediaTrack.getId(); long id = mediaTrack.getId();
@C.TrackType int trackType = MimeTypes.getTrackType(mediaTrack.getContentType()); @C.TrackType int trackType = MimeTypes.getTrackType(mediaTrack.getContentType());
int rendererIndex = getRendererIndexForTrackType(trackType); int rendererIndex = getRendererIndexForTrackType(trackType);
if (isTrackActive(id, activeTrackIds) boolean supported = rendererIndex != C.INDEX_UNSET;
&& rendererIndex != C.INDEX_UNSET boolean selected =
&& trackSelections[rendererIndex] == null) { isTrackActive(id, activeTrackIds) && supported && trackSelections[rendererIndex] == null;
if (selected) {
trackSelections[rendererIndex] = new CastTrackSelection(trackGroups[i]); trackSelections[rendererIndex] = new CastTrackSelection(trackGroups[i]);
} }
@C.FormatSupport
int[] trackSupport = new int[] {supported ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_TYPE};
final boolean[] trackSelected = new boolean[] {selected};
trackGroupInfos[i] =
new TracksInfo.TrackGroupInfo(trackGroups[i], trackSupport, trackType, trackSelected);
} }
TrackGroupArray newTrackGroups = new TrackGroupArray(trackGroups); TrackGroupArray newTrackGroups = new TrackGroupArray(trackGroups);
TrackSelectionArray newTrackSelections = new TrackSelectionArray(trackSelections); TrackSelectionArray newTrackSelections = new TrackSelectionArray(trackSelections);
TracksInfo newTracksInfo = new TracksInfo(ImmutableList.copyOf(trackGroupInfos));
if (!newTrackGroups.equals(currentTrackGroups) if (!newTrackGroups.equals(currentTrackGroups)
|| !newTrackSelections.equals(currentTrackSelection)) { || !newTrackSelections.equals(currentTrackSelection)
currentTrackSelection = new TrackSelectionArray(trackSelections); || !newTracksInfo.equals(currentTracksInfo)) {
currentTrackGroups = new TrackGroupArray(trackGroups); currentTrackSelection = newTrackSelections;
currentTrackGroups = newTrackGroups;
currentTracksInfo = newTracksInfo;
return true; return true;
} }
return false; return false;

View File

@ -21,6 +21,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.testutil.StubExoPlayer; import com.google.android.exoplayer2.testutil.StubExoPlayer;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.trackselection.TrackSelectionParameters; import com.google.android.exoplayer2.trackselection.TrackSelectionParameters;
@ -240,6 +241,11 @@ import com.google.android.exoplayer2.util.ListenerSet;
return new TrackSelectionArray(); return new TrackSelectionArray();
} }
@Override
public TracksInfo getCurrentTracksInfo() {
return TracksInfo.EMPTY;
}
@Override @Override
public TrackSelectionParameters getTrackSelectionParameters() { public TrackSelectionParameters getTrackSelectionParameters() {
return TrackSelectionParameters.DEFAULT_WITHOUT_CONTEXT; return TrackSelectionParameters.DEFAULT_WITHOUT_CONTEXT;

View File

@ -360,6 +360,11 @@ public class ForwardingPlayer implements Player {
return player.getCurrentTrackSelections(); return player.getCurrentTrackSelections();
} }
@Override
public TracksInfo getCurrentTracksInfo() {
return player.getCurrentTracksInfo();
}
@Override @Override
public TrackSelectionParameters getTrackSelectionParameters() { public TrackSelectionParameters getTrackSelectionParameters() {
return player.getTrackSelectionParameters(); return player.getTrackSelectionParameters();
@ -645,6 +650,11 @@ public class ForwardingPlayer implements Player {
eventListener.onTracksChanged(trackGroups, trackSelections); eventListener.onTracksChanged(trackGroups, trackSelections);
} }
@Override
public void onTracksInfoChanged(TracksInfo tracksInfo) {
eventListener.onTracksInfoChanged(tracksInfo);
}
@Override @Override
public void onMediaMetadataChanged(MediaMetadata mediaMetadata) { public void onMediaMetadataChanged(MediaMetadata mediaMetadata) {
eventListener.onMediaMetadataChanged(mediaMetadata); eventListener.onMediaMetadataChanged(mediaMetadata);

View File

@ -57,11 +57,8 @@ import java.util.List;
* <ul> * <ul>
* <li>They can provide a {@link Timeline} representing the structure of the media being played, * <li>They can provide a {@link Timeline} representing the structure of the media being played,
* which can be obtained by calling {@link #getCurrentTimeline()}. * which can be obtained by calling {@link #getCurrentTimeline()}.
* <li>They can provide a {@link TrackGroupArray} defining the currently available tracks, which * <li>They can provide a {@link TracksInfo} defining the currently available tracks and which are
* can be obtained by calling {@link #getCurrentTrackGroups()}. * selected to be rendered, which can be obtained by calling {@link #getCurrentTracksInfo()}.
* <li>They can provide a {@link TrackSelectionArray} defining which of the currently available
* tracks are selected to be rendered. This can be obtained by calling {@link
* #getCurrentTrackSelections()}}.
* </ul> * </ul>
*/ */
public interface Player { public interface Player {
@ -122,10 +119,22 @@ public interface Player {
* concrete implementation may include null elements if it has a fixed number of renderer * concrete implementation may include null elements if it has a fixed number of renderer
* components, wishes to report a TrackSelection for each of them, and has one or more * components, wishes to report a TrackSelection for each of them, and has one or more
* renderer components that is not assigned any selected tracks. * renderer components that is not assigned any selected tracks.
* @deprecated Use {@link #onTracksInfoChanged(TracksInfo)} instead.
*/ */
@Deprecated
default void onTracksChanged( default void onTracksChanged(
TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {} TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {}
/**
* Called when the available or selected tracks change.
*
* <p>{@link #onEvents(Player, Events)} will also be called to report this event along with
* other events that happen in the same {@link Looper} message queue iteration.
*
* @param tracksInfo The available tracks information. Never null, but may be of length zero.
*/
default void onTracksInfoChanged(TracksInfo tracksInfo) {}
/** /**
* Called when the combined {@link MediaMetadata} changes. * Called when the combined {@link MediaMetadata} changes.
* *
@ -693,6 +702,7 @@ public interface Player {
COMMAND_SET_VIDEO_SURFACE, COMMAND_SET_VIDEO_SURFACE,
COMMAND_GET_TEXT, COMMAND_GET_TEXT,
COMMAND_SET_TRACK_SELECTION_PARAMETERS, COMMAND_SET_TRACK_SELECTION_PARAMETERS,
COMMAND_GET_TRACK_INFOS,
}; };
private final FlagSet.Builder flagsBuilder; private final FlagSet.Builder flagsBuilder;
@ -923,8 +933,7 @@ public interface Player {
@Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {} @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {}
@Override @Override
default void onTracksChanged( default void onTracksInfoChanged(TracksInfo tracksInfo) {}
TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {}
@Override @Override
default void onIsLoadingChanged(boolean isLoading) {} default void onIsLoadingChanged(boolean isLoading) {}
@ -1278,7 +1287,7 @@ public interface Player {
int EVENT_TIMELINE_CHANGED = 0; int EVENT_TIMELINE_CHANGED = 0;
/** {@link #getCurrentMediaItem()} changed or the player started repeating the current item. */ /** {@link #getCurrentMediaItem()} changed or the player started repeating the current item. */
int EVENT_MEDIA_ITEM_TRANSITION = 1; int EVENT_MEDIA_ITEM_TRANSITION = 1;
/** {@link #getCurrentTrackGroups()} or {@link #getCurrentTrackSelections()} changed. */ /** {@link #getCurrentTracksInfo()} changed. */
int EVENT_TRACKS_CHANGED = 2; int EVENT_TRACKS_CHANGED = 2;
/** {@link #isLoading()} ()} changed. */ /** {@link #isLoading()} ()} changed. */
int EVENT_IS_LOADING_CHANGED = 3; int EVENT_IS_LOADING_CHANGED = 3;
@ -1331,8 +1340,8 @@ public interface Player {
* #COMMAND_CHANGE_MEDIA_ITEMS}, {@link #COMMAND_GET_AUDIO_ATTRIBUTES}, {@link * #COMMAND_CHANGE_MEDIA_ITEMS}, {@link #COMMAND_GET_AUDIO_ATTRIBUTES}, {@link
* #COMMAND_GET_VOLUME}, {@link #COMMAND_GET_DEVICE_VOLUME}, {@link #COMMAND_SET_VOLUME}, {@link * #COMMAND_GET_VOLUME}, {@link #COMMAND_GET_DEVICE_VOLUME}, {@link #COMMAND_SET_VOLUME}, {@link
* #COMMAND_SET_DEVICE_VOLUME}, {@link #COMMAND_ADJUST_DEVICE_VOLUME}, {@link * #COMMAND_SET_DEVICE_VOLUME}, {@link #COMMAND_ADJUST_DEVICE_VOLUME}, {@link
* #COMMAND_SET_VIDEO_SURFACE}, {@link #COMMAND_GET_TEXT} or {@link * #COMMAND_SET_VIDEO_SURFACE}, {@link #COMMAND_GET_TEXT}, {@link
* #COMMAND_SET_TRACK_SELECTION_PARAMETERS}. * #COMMAND_SET_TRACK_SELECTION_PARAMETERS} or {@link #COMMAND_GET_TRACK_INFOS}.
*/ */
@Documented @Documented
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@ -1366,6 +1375,7 @@ public interface Player {
COMMAND_SET_VIDEO_SURFACE, COMMAND_SET_VIDEO_SURFACE,
COMMAND_GET_TEXT, COMMAND_GET_TEXT,
COMMAND_SET_TRACK_SELECTION_PARAMETERS, COMMAND_SET_TRACK_SELECTION_PARAMETERS,
COMMAND_GET_TRACK_INFOS,
}) })
@interface Command {} @interface Command {}
/** Command to start, pause or resume playback. */ /** Command to start, pause or resume playback. */
@ -1424,6 +1434,8 @@ public interface Player {
int COMMAND_GET_TEXT = 27; int COMMAND_GET_TEXT = 27;
/** Command to set the player's track selection parameters. */ /** Command to set the player's track selection parameters. */
int COMMAND_SET_TRACK_SELECTION_PARAMETERS = 28; int COMMAND_SET_TRACK_SELECTION_PARAMETERS = 28;
/** Command to get track infos. */
int COMMAND_GET_TRACK_INFOS = 29;
/** Represents an invalid {@link Command}. */ /** Represents an invalid {@link Command}. */
int COMMAND_INVALID = -1; int COMMAND_INVALID = -1;
@ -1962,7 +1974,9 @@ public interface Player {
* Returns the available track groups. * Returns the available track groups.
* *
* @see Listener#onTracksChanged(TrackGroupArray, TrackSelectionArray) * @see Listener#onTracksChanged(TrackGroupArray, TrackSelectionArray)
* @deprecated Use {@link #getCurrentTracksInfo()}.
*/ */
@Deprecated
TrackGroupArray getCurrentTrackGroups(); TrackGroupArray getCurrentTrackGroups();
/** /**
@ -1973,10 +1987,23 @@ public interface Player {
* components that is not assigned any selected tracks. * components that is not assigned any selected tracks.
* *
* @see Listener#onTracksChanged(TrackGroupArray, TrackSelectionArray) * @see Listener#onTracksChanged(TrackGroupArray, TrackSelectionArray)
* @deprecated Use {@link #getCurrentTracksInfo()}.
*/ */
@Deprecated
TrackSelectionArray getCurrentTrackSelections(); TrackSelectionArray getCurrentTrackSelections();
/** Returns the parameters constraining the track selection. */ /**
* Returns the available tracks, as well as the tracks' support, type, and selection status.
*
* @see Listener#onTracksChanged(TrackGroupArray, TrackSelectionArray)
*/
TracksInfo getCurrentTracksInfo();
/**
* Returns the parameters constraining the track selection.
*
* @see Listener#onTrackSelectionParametersChanged}
*/
TrackSelectionParameters getTrackSelectionParameters(); TrackSelectionParameters getTrackSelectionParameters();
/** /**

View File

@ -0,0 +1,300 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2;
import static com.google.android.exoplayer2.util.Assertions.checkArgument;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.BundleableUtil.fromBundleNullableList;
import static com.google.android.exoplayer2.util.BundleableUtil.fromNullableBundle;
import static com.google.android.exoplayer2.util.BundleableUtil.toBundleArrayList;
import android.os.Bundle;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.trackselection.TrackSelectionParameters;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.Booleans;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.List;
/** Immutable information ({@link TrackGroupInfo}) about tracks. */
public final class TracksInfo implements Bundleable {
/**
* Information about tracks in a {@link TrackGroup}: their {@link C.TrackType}, if their format is
* supported by the player and if they are selected for playback.
*/
public static final class TrackGroupInfo implements Bundleable {
private final TrackGroup trackGroup;
@C.FormatSupport private final int[] trackSupport;
private final @C.TrackType int trackType;
private final boolean[] trackSelected;
/**
* Constructs a TrackGroupInfo.
*
* @param trackGroup The {@link TrackGroup} described.
* @param trackSupport The {@link C.FormatSupport} of each track in the {@code trackGroup}.
* @param trackType The {@link C.TrackType} of the tracks in the {@code trackGroup}.
* @param tracksSelected Whether a track is selected for each track in {@code trackGroup}.
*/
public TrackGroupInfo(
TrackGroup trackGroup,
@C.FormatSupport int[] trackSupport,
@C.TrackType int trackType,
boolean[] tracksSelected) {
int length = trackGroup.length;
checkArgument(length == trackSupport.length && length == tracksSelected.length);
this.trackGroup = trackGroup;
this.trackSupport = trackSupport.clone();
this.trackType = trackType;
this.trackSelected = tracksSelected.clone();
}
/** Returns the {@link TrackGroup} described by this {@code TrackGroupInfo}. */
public TrackGroup getTrackGroup() {
return trackGroup;
}
/**
* Returns the level of support for a track in a {@link TrackGroup}.
*
* @param trackIndex The index of the track in the {@link TrackGroup}.
* @return The {@link C.FormatSupport} of the track.
*/
@C.FormatSupport
public int getTrackSupport(int trackIndex) {
return trackSupport[trackIndex];
}
/**
* Returns if a track in a {@link TrackGroup} is supported for playback.
*
* @param trackIndex The index of the track in the {@link TrackGroup}.
* @return True if the track's format can be played, false otherwise.
*/
public boolean isTrackSupported(int trackIndex) {
return trackSupport[trackIndex] == C.FORMAT_HANDLED;
}
/** Returns if at least one track in a {@link TrackGroup} is selected for playback. */
public boolean isSelected() {
return Booleans.contains(trackSelected, true);
}
/** Returns if at least one track in a {@link TrackGroup} is supported. */
public boolean isSupported() {
for (int i = 0; i < trackSupport.length; i++) {
if (isTrackSupported(i)) {
return true;
}
}
return false;
}
/**
* Returns if a track in a {@link TrackGroup} is selected for playback.
*
* <p>Multiple tracks of a track group may be selected, in which case the the active one
* (currently playing) is undefined. This is common in adaptive streaming, where multiple tracks
* of different quality are selected and the active one changes depending on the network and the
* {@link TrackSelectionParameters}.
*
* @param trackIndex The index of the track in the {@link TrackGroup}.
* @return true if the track is selected, false otherwise.
*/
public boolean isTrackSelected(int trackIndex) {
return trackSelected[trackIndex];
}
/**
* Returns the {@link C.TrackType} of the tracks in the {@link TrackGroup}. Tracks in a group
* are all of the same type.
*/
public @C.TrackType int getTrackType() {
return trackType;
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (other == null || getClass() != other.getClass()) {
return false;
}
TrackGroupInfo that = (TrackGroupInfo) other;
return trackType == that.trackType
&& trackGroup.equals(that.trackGroup)
&& Arrays.equals(trackSupport, that.trackSupport)
&& Arrays.equals(trackSelected, that.trackSelected);
}
@Override
public int hashCode() {
int result = trackGroup.hashCode();
result = 31 * result + Arrays.hashCode(trackSupport);
result = 31 * result + trackType;
result = 31 * result + Arrays.hashCode(trackSelected);
return result;
}
// Bundleable implementation.
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
FIELD_TRACK_GROUP,
FIELD_TRACK_SUPPORT,
FIELD_TRACK_TYPE,
FIELD_TRACK_SELECTED,
})
private @interface FieldNumber {}
private static final int FIELD_TRACK_GROUP = 0;
private static final int FIELD_TRACK_SUPPORT = 1;
private static final int FIELD_TRACK_TYPE = 2;
private static final int FIELD_TRACK_SELECTED = 3;
@Override
public Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putBundle(keyForField(FIELD_TRACK_GROUP), trackGroup.toBundle());
bundle.putIntArray(keyForField(FIELD_TRACK_SUPPORT), trackSupport);
bundle.putInt(keyForField(FIELD_TRACK_TYPE), trackType);
bundle.putBooleanArray(keyForField(FIELD_TRACK_SELECTED), trackSelected);
return bundle;
}
/** Object that can restores a {@code TracksInfo} from a {@link Bundle}. */
public static final Creator<TrackGroupInfo> CREATOR =
bundle -> {
TrackGroup trackGroup =
fromNullableBundle(
TrackGroup.CREATOR, bundle.getBundle(keyForField(FIELD_TRACK_GROUP)));
checkNotNull(trackGroup); // Can't create a trackGroup info without a trackGroup
@C.FormatSupport
final int[] trackSupport =
MoreObjects.firstNonNull(
bundle.getIntArray(keyForField(FIELD_TRACK_SUPPORT)), new int[trackGroup.length]);
@C.TrackType
int trackType = bundle.getInt(keyForField(FIELD_TRACK_TYPE), C.TRACK_TYPE_UNKNOWN);
boolean[] selected =
MoreObjects.firstNonNull(
bundle.getBooleanArray(keyForField(FIELD_TRACK_SELECTED)),
new boolean[trackGroup.length]);
return new TrackGroupInfo(trackGroup, trackSupport, trackType, selected);
};
private static String keyForField(@FieldNumber int field) {
return Integer.toString(field, Character.MAX_RADIX);
}
}
private final ImmutableList<TrackGroupInfo> trackGroupInfos;
/** An empty {@code TrackInfo} containing no {@link TrackGroupInfo}. */
public static final TracksInfo EMPTY = new TracksInfo(ImmutableList.of());
/** Constructs {@code TracksInfo} from the provided {@link TrackGroupInfo}. */
public TracksInfo(List<TrackGroupInfo> trackGroupInfos) {
this.trackGroupInfos = ImmutableList.copyOf(trackGroupInfos);
}
/** Returns the {@link TrackGroupInfo TrackGroupInfos}, describing each {@link TrackGroup}. */
public ImmutableList<TrackGroupInfo> getTrackGroupInfos() {
return trackGroupInfos;
}
/** Returns if there is at least one track of type {@code trackType} but none are supported. */
public boolean isTypeSupportedOrEmpty(@C.TrackType int trackType) {
boolean supported = true;
for (int i = 0; i < trackGroupInfos.size(); i++) {
if (trackGroupInfos.get(i).trackType == trackType) {
if (trackGroupInfos.get(i).isSupported()) {
return true;
} else {
supported = false;
}
}
}
return supported;
}
/** Returns if at least one track of the type {@code trackType} is selected for playback. */
public boolean isTypeSelected(@C.TrackType int trackType) {
for (int i = 0; i < trackGroupInfos.size(); i++) {
TrackGroupInfo trackGroupInfo = trackGroupInfos.get(i);
if (trackGroupInfo.isSelected() && trackGroupInfo.getTrackType() == trackType) {
return true;
}
}
return false;
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (other == null || getClass() != other.getClass()) {
return false;
}
TracksInfo that = (TracksInfo) other;
return trackGroupInfos.equals(that.trackGroupInfos);
}
@Override
public int hashCode() {
return trackGroupInfos.hashCode();
}
// Bundleable implementation.
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
FIELD_TRACK_GROUP_INFOS,
})
private @interface FieldNumber {}
private static final int FIELD_TRACK_GROUP_INFOS = 0;
@Override
public Bundle toBundle() {
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(
keyForField(FIELD_TRACK_GROUP_INFOS), toBundleArrayList(trackGroupInfos));
return bundle;
}
/** Object that can restore a {@code TracksInfo} from a {@link Bundle}. */
public static final Creator<TracksInfo> CREATOR =
bundle -> {
List<TrackGroupInfo> trackGroupInfos =
fromBundleNullableList(
TrackGroupInfo.CREATOR,
bundle.getParcelableArrayList(keyForField(FIELD_TRACK_GROUP_INFOS)),
/* defaultValue= */ ImmutableList.of());
return new TracksInfo(trackGroupInfos);
};
private static String keyForField(@FieldNumber int field) {
return Integer.toString(field, Character.MAX_RADIX);
}
}

View File

@ -0,0 +1,111 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Unit test for {@link TracksInfo}. */
@RunWith(AndroidJUnit4.class)
public class TracksInfoTest {
@Test
public void roundTripViaBundle_ofEmptyTracksInfo_yieldsEqualInstance() {
TracksInfo before = TracksInfo.EMPTY;
TracksInfo after = TracksInfo.CREATOR.fromBundle(before.toBundle());
assertThat(after).isEqualTo(before);
}
@Test
public void roundTripViaBundle_ofTracksInfo_yieldsEqualInstance() {
TracksInfo before =
new TracksInfo(
ImmutableList.of(
new TracksInfo.TrackGroupInfo(
new TrackGroup(new Format.Builder().build()),
new int[] {C.FORMAT_EXCEEDS_CAPABILITIES},
C.TRACK_TYPE_AUDIO,
new boolean[] {true}),
new TracksInfo.TrackGroupInfo(
new TrackGroup(new Format.Builder().build(), new Format.Builder().build()),
new int[] {C.FORMAT_UNSUPPORTED_DRM, C.FORMAT_UNSUPPORTED_TYPE},
C.TRACK_TYPE_VIDEO,
new boolean[] {false, true})));
TracksInfo after = TracksInfo.CREATOR.fromBundle(before.toBundle());
assertThat(after).isEqualTo(before);
}
@Test
public void tracksInfoGetters_withoutTrack_returnExpectedValues() {
TracksInfo tracksInfo = new TracksInfo(ImmutableList.of());
assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_AUDIO)).isTrue();
assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_AUDIO)).isFalse();
ImmutableList<TracksInfo.TrackGroupInfo> trackGroupInfos = tracksInfo.getTrackGroupInfos();
assertThat(trackGroupInfos).isEmpty();
}
@Test
public void tracksInfo_emptyStaticInstance_isEmpty() {
TracksInfo tracksInfo = TracksInfo.EMPTY;
assertThat(tracksInfo.getTrackGroupInfos()).isEmpty();
assertThat(tracksInfo).isEqualTo(new TracksInfo(ImmutableList.of()));
}
@Test
public void tracksInfoGetters_ofComplexTracksInfo_returnExpectedValues() {
TracksInfo.TrackGroupInfo trackGroupInfo0 =
new TracksInfo.TrackGroupInfo(
new TrackGroup(new Format.Builder().build()),
new int[] {C.FORMAT_EXCEEDS_CAPABILITIES},
C.TRACK_TYPE_AUDIO,
/* tracksSelected= */ new boolean[] {false});
TracksInfo.TrackGroupInfo trackGroupInfo1 =
new TracksInfo.TrackGroupInfo(
new TrackGroup(new Format.Builder().build(), new Format.Builder().build()),
new int[] {C.FORMAT_UNSUPPORTED_DRM, C.FORMAT_HANDLED},
C.TRACK_TYPE_VIDEO,
/* tracksSelected= */ new boolean[] {false, true});
TracksInfo tracksInfo = new TracksInfo(ImmutableList.of(trackGroupInfo0, trackGroupInfo1));
assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_AUDIO)).isFalse();
assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_VIDEO)).isTrue();
assertThat(tracksInfo.isTypeSupportedOrEmpty(C.TRACK_TYPE_TEXT)).isTrue();
assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_AUDIO)).isFalse();
assertThat(tracksInfo.isTypeSelected(C.TRACK_TYPE_VIDEO)).isTrue();
ImmutableList<TracksInfo.TrackGroupInfo> trackGroupInfos = tracksInfo.getTrackGroupInfos();
assertThat(trackGroupInfos).hasSize(2);
assertThat(trackGroupInfos.get(0)).isSameInstanceAs(trackGroupInfo0);
assertThat(trackGroupInfos.get(1)).isSameInstanceAs(trackGroupInfo1);
assertThat(trackGroupInfos.get(0).isTrackSupported(0)).isFalse();
assertThat(trackGroupInfos.get(1).isTrackSupported(0)).isFalse();
assertThat(trackGroupInfos.get(1).isTrackSupported(1)).isTrue();
assertThat(trackGroupInfos.get(0).getTrackSupport(0)).isEqualTo(C.FORMAT_EXCEEDS_CAPABILITIES);
assertThat(trackGroupInfos.get(1).getTrackSupport(0)).isEqualTo(C.FORMAT_UNSUPPORTED_DRM);
assertThat(trackGroupInfos.get(1).getTrackSupport(1)).isEqualTo(C.FORMAT_HANDLED);
assertThat(trackGroupInfos.get(0).isTrackSelected(0)).isFalse();
assertThat(trackGroupInfos.get(1).isTrackSelected(0)).isFalse();
assertThat(trackGroupInfos.get(1).isTrackSelected(1)).isTrue();
assertThat(trackGroupInfos.get(0).getTrackType()).isEqualTo(C.TRACK_TYPE_AUDIO);
assertThat(trackGroupInfos.get(1).getTrackType()).isEqualTo(C.TRACK_TYPE_VIDEO);
}
}

View File

@ -196,6 +196,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
new TrackSelectorResult( new TrackSelectorResult(
new RendererConfiguration[renderers.length], new RendererConfiguration[renderers.length],
new ExoTrackSelection[renderers.length], new ExoTrackSelection[renderers.length],
TracksInfo.EMPTY,
/* info= */ null); /* info= */ null);
period = new Timeline.Period(); period = new Timeline.Period();
permanentAvailableCommands = permanentAvailableCommands =
@ -210,7 +211,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
COMMAND_GET_TIMELINE, COMMAND_GET_TIMELINE,
COMMAND_GET_MEDIA_ITEMS_METADATA, COMMAND_GET_MEDIA_ITEMS_METADATA,
COMMAND_SET_MEDIA_ITEMS_METADATA, COMMAND_SET_MEDIA_ITEMS_METADATA,
COMMAND_CHANGE_MEDIA_ITEMS) COMMAND_CHANGE_MEDIA_ITEMS,
COMMAND_GET_TRACK_INFOS)
.addIf(COMMAND_SET_TRACK_SELECTION_PARAMETERS, trackSelector.isSetParametersSupported()) .addIf(COMMAND_SET_TRACK_SELECTION_PARAMETERS, trackSelector.isSetParametersSupported())
.addAll(additionalPermanentAvailableCommands) .addAll(additionalPermanentAvailableCommands)
.build(); .build();
@ -951,6 +953,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
return new TrackSelectionArray(playbackInfo.trackSelectorResult.selections); return new TrackSelectionArray(playbackInfo.trackSelectorResult.selections);
} }
@Override
public TracksInfo getCurrentTracksInfo() {
return playbackInfo.trackSelectorResult.tracksInfo;
}
@Override @Override
public TrackSelectionParameters getTrackSelectionParameters() { public TrackSelectionParameters getTrackSelectionParameters() {
return trackSelector.getParameters(); return trackSelector.getParameters();
@ -1274,6 +1281,9 @@ import java.util.concurrent.CopyOnWriteArraySet;
listeners.queueEvent( listeners.queueEvent(
Player.EVENT_TRACKS_CHANGED, Player.EVENT_TRACKS_CHANGED,
listener -> listener.onTracksChanged(newPlaybackInfo.trackGroups, newSelection)); listener -> listener.onTracksChanged(newPlaybackInfo.trackGroups, newSelection));
listeners.queueEvent(
Player.EVENT_TRACKS_CHANGED,
listener -> listener.onTracksInfoChanged(newPlaybackInfo.trackSelectorResult.tracksInfo));
} }
if (metadataChanged) { if (metadataChanged) {
final MediaMetadata finalMediaMetadata = mediaMetadata; final MediaMetadata finalMediaMetadata = mediaMetadata;

View File

@ -1456,6 +1456,12 @@ public class SimpleExoPlayer extends BasePlayer
return player.getCurrentTrackSelections(); return player.getCurrentTrackSelections();
} }
@Override
public TracksInfo getCurrentTracksInfo() {
verifyApplicationThread();
return player.getCurrentTracksInfo();
}
@Override @Override
public TrackSelectionParameters getTrackSelectionParameters() { public TrackSelectionParameters getTrackSelectionParameters() {
verifyApplicationThread(); verifyApplicationThread();

View File

@ -35,6 +35,7 @@ import com.google.android.exoplayer2.Player.PlaybackSuppressionReason;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Period; import com.google.android.exoplayer2.Timeline.Period;
import com.google.android.exoplayer2.Timeline.Window; import com.google.android.exoplayer2.Timeline.Window;
import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.analytics.AnalyticsListener.EventTime; import com.google.android.exoplayer2.analytics.AnalyticsListener.EventTime;
import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.audio.AudioRendererEventListener; import com.google.android.exoplayer2.audio.AudioRendererEventListener;
@ -586,6 +587,7 @@ public class AnalyticsCollector
} }
@Override @Override
@SuppressWarnings("deprecation") // Implementing and calling deprecate listener method
public final void onTracksChanged( public final void onTracksChanged(
TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
@ -595,6 +597,15 @@ public class AnalyticsCollector
listener -> listener.onTracksChanged(eventTime, trackGroups, trackSelections)); listener -> listener.onTracksChanged(eventTime, trackGroups, trackSelections));
} }
@Override
public void onTracksInfoChanged(TracksInfo tracksInfo) {
EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
sendEvent(
eventTime,
AnalyticsListener.EVENT_TRACKS_CHANGED,
listener -> listener.onTracksInfoChanged(eventTime, tracksInfo));
}
@SuppressWarnings("deprecation") // Calling deprecated listener method. @SuppressWarnings("deprecation") // Calling deprecated listener method.
@Override @Override
public final void onIsLoadingChanged(boolean isLoading) { public final void onIsLoadingChanged(boolean isLoading) {

View File

@ -36,6 +36,7 @@ import com.google.android.exoplayer2.Player.DiscontinuityReason;
import com.google.android.exoplayer2.Player.PlaybackSuppressionReason; import com.google.android.exoplayer2.Player.PlaybackSuppressionReason;
import com.google.android.exoplayer2.Player.TimelineChangeReason; import com.google.android.exoplayer2.Player.TimelineChangeReason;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.audio.AudioSink; import com.google.android.exoplayer2.audio.AudioSink;
import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.decoder.DecoderCounters;
@ -221,7 +222,8 @@ public interface AnalyticsListener {
*/ */
int EVENT_MEDIA_ITEM_TRANSITION = Player.EVENT_MEDIA_ITEM_TRANSITION; int EVENT_MEDIA_ITEM_TRANSITION = Player.EVENT_MEDIA_ITEM_TRANSITION;
/** /**
* {@link Player#getCurrentTrackGroups()} or {@link Player#getCurrentTrackSelections()} changed. * {@link Player#getCurrentTracksInfo()}, {@link Player#getCurrentTrackGroups()} or {@link
* Player#getCurrentTrackSelections()} changed.
*/ */
int EVENT_TRACKS_CHANGED = Player.EVENT_TRACKS_CHANGED; int EVENT_TRACKS_CHANGED = Player.EVENT_TRACKS_CHANGED;
/** {@link Player#isLoading()} ()} changed. */ /** {@link Player#isLoading()} ()} changed. */
@ -674,10 +676,20 @@ public interface AnalyticsListener {
* @param eventTime The event time. * @param eventTime The event time.
* @param trackGroups The available tracks. May be empty. * @param trackGroups The available tracks. May be empty.
* @param trackSelections The track selections for each renderer. May contain null elements. * @param trackSelections The track selections for each renderer. May contain null elements.
* @deprecated Use {@link #onTracksInfoChanged}.
*/ */
@Deprecated
default void onTracksChanged( default void onTracksChanged(
EventTime eventTime, TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {} EventTime eventTime, TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {}
/**
* Called when the available or selected tracks change.
*
* @param eventTime The event time.
* @param tracksInfo The available tracks information. Never null, but may be of length zero.
*/
default void onTracksInfoChanged(EventTime eventTime, TracksInfo tracksInfo) {}
/** /**
* Called when the combined {@link MediaMetadata} changes. * Called when the combined {@link MediaMetadata} changes.
* *

View File

@ -21,6 +21,7 @@ import static java.lang.Math.min;
import android.util.Pair; import android.util.Pair;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.C.FormatSupport; import com.google.android.exoplayer2.C.FormatSupport;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
@ -30,11 +31,13 @@ import com.google.android.exoplayer2.RendererCapabilities.AdaptiveSupport;
import com.google.android.exoplayer2.RendererCapabilities.Capabilities; import com.google.android.exoplayer2.RendererCapabilities.Capabilities;
import com.google.android.exoplayer2.RendererConfiguration; import com.google.android.exoplayer2.RendererConfiguration;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.common.collect.ImmutableList;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
@ -106,7 +109,7 @@ public abstract class MappingTrackSelector extends TrackSelector {
* renderer, track group and track (in that order). * renderer, track group and track (in that order).
* @param unmappedTrackGroups {@link TrackGroup}s not mapped to any renderer. * @param unmappedTrackGroups {@link TrackGroup}s not mapped to any renderer.
*/ */
@SuppressWarnings("deprecation") @VisibleForTesting
/* package */ MappedTrackInfo( /* package */ MappedTrackInfo(
String[] rendererNames, String[] rendererNames,
@C.TrackType int[] rendererTrackTypes, @C.TrackType int[] rendererTrackTypes,
@ -144,7 +147,7 @@ public abstract class MappingTrackSelector extends TrackSelector {
* *
* @see Renderer#getTrackType() * @see Renderer#getTrackType()
* @param rendererIndex The renderer index. * @param rendererIndex The renderer index.
* @return One of the {@code TRACK_TYPE_*} constants defined in {@link C}. * @return The {@link C.TrackType} of the renderer.
*/ */
public @C.TrackType int getRendererType(int rendererIndex) { public @C.TrackType int getRendererType(int rendererIndex) {
return rendererTrackTypes[rendererIndex]; return rendererTrackTypes[rendererIndex];
@ -279,6 +282,7 @@ public abstract class MappingTrackSelector extends TrackSelector {
String firstSampleMimeType = null; String firstSampleMimeType = null;
for (int i = 0; i < trackIndices.length; i++) { for (int i = 0; i < trackIndices.length; i++) {
int trackIndex = trackIndices[i]; int trackIndex = trackIndices[i];
@Nullable
String sampleMimeType = String sampleMimeType =
rendererTrackGroups[rendererIndex].get(groupIndex).getFormat(trackIndex).sampleMimeType; rendererTrackGroups[rendererIndex].get(groupIndex).getFormat(trackIndex).sampleMimeType;
if (handledTrackCount++ == 0) { if (handledTrackCount++ == 0) {
@ -406,7 +410,10 @@ public abstract class MappingTrackSelector extends TrackSelector {
rendererMixedMimeTypeAdaptationSupports, rendererMixedMimeTypeAdaptationSupports,
periodId, periodId,
timeline); timeline);
return new TrackSelectorResult(result.first, result.second, mappedTrackInfo);
TracksInfo tracksInfo = buildTracksInfo(result.second, mappedTrackInfo);
return new TrackSelectorResult(result.first, result.second, tracksInfo, mappedTrackInfo);
} }
/** /**
@ -536,4 +543,49 @@ public abstract class MappingTrackSelector extends TrackSelector {
} }
return mixedMimeTypeAdaptationSupport; return mixedMimeTypeAdaptationSupport;
} }
@VisibleForTesting
/* package */ static TracksInfo buildTracksInfo(
@NullableType TrackSelection[] selections, MappedTrackInfo mappedTrackInfo) {
ImmutableList.Builder<TracksInfo.TrackGroupInfo> builder = new ImmutableList.Builder<>();
for (int rendererIndex = 0;
rendererIndex < mappedTrackInfo.getRendererCount();
rendererIndex++) {
TrackGroupArray trackGroupArray = mappedTrackInfo.getTrackGroups(rendererIndex);
@Nullable TrackSelection trackSelection = selections[rendererIndex];
for (int groupIndex = 0; groupIndex < trackGroupArray.length; groupIndex++) {
TrackGroup trackGroup = trackGroupArray.get(groupIndex);
@C.FormatSupport int[] trackSupport = new int[trackGroup.length];
boolean[] selected = new boolean[trackGroup.length];
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
trackSupport[trackIndex] =
mappedTrackInfo.getTrackSupport(rendererIndex, groupIndex, trackIndex);
// Suppressing reference equality warning because the track group stored in the track
// selection must point to the exact track group object to be considered part of it.
@SuppressWarnings("ReferenceEquality")
boolean isTrackSelected =
(trackSelection != null)
&& (trackSelection.getTrackGroup() == trackGroup)
&& (trackSelection.indexOf(trackIndex) != C.INDEX_UNSET);
selected[trackIndex] = isTrackSelected;
}
@C.TrackType int trackGroupType = mappedTrackInfo.getRendererType(rendererIndex);
builder.add(
new TracksInfo.TrackGroupInfo(trackGroup, trackSupport, trackGroupType, selected));
}
}
TrackGroupArray unmappedTrackGroups = mappedTrackInfo.getUnmappedTrackGroups();
for (int groupIndex = 0; groupIndex < unmappedTrackGroups.length; groupIndex++) {
TrackGroup trackGroup = unmappedTrackGroups.get(groupIndex);
@C.FormatSupport int[] trackSupport = new int[trackGroup.length];
Arrays.fill(trackSupport, C.FORMAT_UNSUPPORTED_TYPE);
// A track group only contains tracks of the same type, thus only consider the first track.
@C.TrackType
int trackGroupType = MimeTypes.getTrackType(trackGroup.getFormat(0).sampleMimeType);
boolean[] selected = new boolean[trackGroup.length]; // Initialized to false.
builder.add(
new TracksInfo.TrackGroupInfo(trackGroup, trackSupport, trackGroupType, selected));
}
return new TracksInfo(builder.build());
}
} }

View File

@ -17,6 +17,7 @@ package com.google.android.exoplayer2.trackselection;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.RendererConfiguration; import com.google.android.exoplayer2.RendererConfiguration;
import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import org.checkerframework.checker.nullness.compatqual.NullableType; import org.checkerframework.checker.nullness.compatqual.NullableType;
@ -32,6 +33,8 @@ public final class TrackSelectorResult {
public final @NullableType RendererConfiguration[] rendererConfigurations; public final @NullableType RendererConfiguration[] rendererConfigurations;
/** A {@link ExoTrackSelection} array containing the track selection for each renderer. */ /** A {@link ExoTrackSelection} array containing the track selection for each renderer. */
public final @NullableType ExoTrackSelection[] selections; public final @NullableType ExoTrackSelection[] selections;
/** Describe the tracks and which one were selected. */
public final TracksInfo tracksInfo;
/** /**
* An opaque object that will be returned to {@link TrackSelector#onSelectionActivated(Object)} * An opaque object that will be returned to {@link TrackSelector#onSelectionActivated(Object)}
* should the selections be activated. * should the selections be activated.
@ -45,13 +48,34 @@ public final class TrackSelectorResult {
* @param info An opaque object that will be returned to {@link * @param info An opaque object that will be returned to {@link
* TrackSelector#onSelectionActivated(Object)} should the selection be activated. May be * TrackSelector#onSelectionActivated(Object)} should the selection be activated. May be
* {@code null}. * {@code null}.
* @deprecated Use {@link #TrackSelectorResult(RendererConfiguration[], ExoTrackSelection[],
* TracksInfo, Object)}.
*/ */
@Deprecated
public TrackSelectorResult( public TrackSelectorResult(
@NullableType RendererConfiguration[] rendererConfigurations, @NullableType RendererConfiguration[] rendererConfigurations,
@NullableType ExoTrackSelection[] selections, @NullableType ExoTrackSelection[] selections,
@Nullable Object info) { @Nullable Object info) {
this(rendererConfigurations, selections, TracksInfo.EMPTY, info);
}
/**
* @param rendererConfigurations A {@link RendererConfiguration} for each renderer. A null entry
* indicates the corresponding renderer should be disabled.
* @param selections A {@link ExoTrackSelection} array containing the selection for each renderer.
* @param tracksInfo Description of the available tracks and which one were selected.
* @param info An opaque object that will be returned to {@link
* TrackSelector#onSelectionActivated(Object)} should the selection be activated. May be
* {@code null}.
*/
public TrackSelectorResult(
@NullableType RendererConfiguration[] rendererConfigurations,
@NullableType ExoTrackSelection[] selections,
TracksInfo tracksInfo,
@Nullable Object info) {
this.rendererConfigurations = rendererConfigurations; this.rendererConfigurations = rendererConfigurations;
this.selections = selections.clone(); this.selections = selections.clone();
this.tracksInfo = tracksInfo;
this.info = info; this.info = info;
length = rendererConfigurations.length; length = rendererConfigurations.length;
} }

View File

@ -23,6 +23,7 @@ import static com.google.android.exoplayer2.Player.COMMAND_GET_DEVICE_VOLUME;
import static com.google.android.exoplayer2.Player.COMMAND_GET_MEDIA_ITEMS_METADATA; import static com.google.android.exoplayer2.Player.COMMAND_GET_MEDIA_ITEMS_METADATA;
import static com.google.android.exoplayer2.Player.COMMAND_GET_TEXT; import static com.google.android.exoplayer2.Player.COMMAND_GET_TEXT;
import static com.google.android.exoplayer2.Player.COMMAND_GET_TIMELINE; import static com.google.android.exoplayer2.Player.COMMAND_GET_TIMELINE;
import static com.google.android.exoplayer2.Player.COMMAND_GET_TRACK_INFOS;
import static com.google.android.exoplayer2.Player.COMMAND_GET_VOLUME; import static com.google.android.exoplayer2.Player.COMMAND_GET_VOLUME;
import static com.google.android.exoplayer2.Player.COMMAND_PLAY_PAUSE; import static com.google.android.exoplayer2.Player.COMMAND_PLAY_PAUSE;
import static com.google.android.exoplayer2.Player.COMMAND_PREPARE_STOP; import static com.google.android.exoplayer2.Player.COMMAND_PREPARE_STOP;
@ -8344,6 +8345,7 @@ public final class ExoPlayerTest {
assertThat(player.isCommandAvailable(COMMAND_SET_VIDEO_SURFACE)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_SET_VIDEO_SURFACE)).isTrue();
assertThat(player.isCommandAvailable(COMMAND_GET_TEXT)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_GET_TEXT)).isTrue();
assertThat(player.isCommandAvailable(COMMAND_SET_TRACK_SELECTION_PARAMETERS)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_SET_TRACK_SELECTION_PARAMETERS)).isTrue();
assertThat(player.isCommandAvailable(COMMAND_GET_TRACK_INFOS)).isTrue();
} }
@Test @Test
@ -11238,7 +11240,8 @@ public final class ExoPlayerTest {
COMMAND_ADJUST_DEVICE_VOLUME, COMMAND_ADJUST_DEVICE_VOLUME,
COMMAND_SET_VIDEO_SURFACE, COMMAND_SET_VIDEO_SURFACE,
COMMAND_GET_TEXT, COMMAND_GET_TEXT,
COMMAND_SET_TRACK_SELECTION_PARAMETERS); COMMAND_SET_TRACK_SELECTION_PARAMETERS,
COMMAND_GET_TRACK_INFOS);
if (!isTimelineEmpty) { if (!isTimelineEmpty) {
builder.add(COMMAND_SEEK_TO_PREVIOUS); builder.add(COMMAND_SEEK_TO_PREVIOUS);
} }

View File

@ -771,7 +771,10 @@ public final class MediaPeriodQueueTest {
mediaSourceList, mediaSourceList,
getNextMediaPeriodInfo(), getNextMediaPeriodInfo(),
new TrackSelectorResult( new TrackSelectorResult(
new RendererConfiguration[0], new ExoTrackSelection[0], /* info= */ null)); new RendererConfiguration[0],
new ExoTrackSelection[0],
TracksInfo.EMPTY,
/* info= */ null));
} }
private void clear() { private void clear() {

View File

@ -84,6 +84,7 @@ import com.google.android.exoplayer2.RenderersFactory;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Window; import com.google.android.exoplayer2.Timeline.Window;
import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.drm.DefaultDrmSessionManager; import com.google.android.exoplayer2.drm.DefaultDrmSessionManager;
import com.google.android.exoplayer2.drm.DrmInitData; import com.google.android.exoplayer2.drm.DrmInitData;
@ -100,7 +101,6 @@ import com.google.android.exoplayer2.source.LoadEventInfo;
import com.google.android.exoplayer2.source.MediaLoadData; import com.google.android.exoplayer2.source.MediaLoadData;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.ads.AdPlaybackState; import com.google.android.exoplayer2.source.ads.AdPlaybackState;
import com.google.android.exoplayer2.testutil.ActionSchedule; import com.google.android.exoplayer2.testutil.ActionSchedule;
import com.google.android.exoplayer2.testutil.ActionSchedule.PlayerRunnable; import com.google.android.exoplayer2.testutil.ActionSchedule.PlayerRunnable;
@ -115,7 +115,6 @@ import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinit
import com.google.android.exoplayer2.testutil.FakeVideoRenderer; import com.google.android.exoplayer2.testutil.FakeVideoRenderer;
import com.google.android.exoplayer2.testutil.TestExoPlayerBuilder; import com.google.android.exoplayer2.testutil.TestExoPlayerBuilder;
import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.util.Clock; import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.ConditionVariable; import com.google.android.exoplayer2.util.ConditionVariable;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
@ -2224,8 +2223,7 @@ public final class AnalyticsCollectorTest {
} }
@Override @Override
public void onTracksChanged( public void onTracksInfoChanged(EventTime eventTime, TracksInfo tracksInfo) {
EventTime eventTime, TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
reportedEvents.add(new ReportedEvent(EVENT_TRACKS_CHANGED, eventTime)); reportedEvents.add(new ReportedEvent(EVENT_TRACKS_CHANGED, eventTime));
} }

View File

@ -38,6 +38,7 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.RendererConfiguration; import com.google.android.exoplayer2.RendererConfiguration;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
@ -50,6 +51,7 @@ import com.google.android.exoplayer2.trackselection.TrackSelector.InvalidationLi
import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import java.util.HashMap; import java.util.HashMap;
@ -1717,6 +1719,32 @@ public final class DefaultTrackSelectorTest {
assertFixedSelection(result.selections[0], trackGroups, formatAac); assertFixedSelection(result.selections[0], trackGroups, formatAac);
} }
/** Tests audio track selection when there are multiple audio renderers. */
@Test
public void selectTracks_multipleRenderer_allSelected() throws Exception {
RendererCapabilities[] rendererCapabilities =
new RendererCapabilities[] {VIDEO_CAPABILITIES, AUDIO_CAPABILITIES, AUDIO_CAPABILITIES};
TrackGroupArray trackGroups = new TrackGroupArray(AUDIO_TRACK_GROUP);
TrackSelectorResult result =
trackSelector.selectTracks(rendererCapabilities, trackGroups, periodId, TIMELINE);
assertThat(result.length).isEqualTo(3);
assertThat(result.rendererConfigurations)
.asList()
.containsExactly(null, DEFAULT, null)
.inOrder();
assertThat(result.selections[0]).isNull();
assertFixedSelection(result.selections[1], trackGroups, trackGroups.get(0).getFormat(0));
assertThat(result.selections[2]).isNull();
ImmutableList<TracksInfo.TrackGroupInfo> trackGroupInfos =
result.tracksInfo.getTrackGroupInfos();
assertThat(trackGroupInfos).hasSize(1);
assertThat(trackGroupInfos.get(0).getTrackGroup()).isEqualTo(AUDIO_TRACK_GROUP);
assertThat(trackGroupInfos.get(0).isTrackSelected(0)).isTrue();
assertThat(trackGroupInfos.get(0).getTrackSupport(0)).isEqualTo(FORMAT_HANDLED);
}
private static void assertSelections(TrackSelectorResult result, TrackSelection[] expected) { private static void assertSelections(TrackSelectorResult result, TrackSelection[] expected) {
assertThat(result.length).isEqualTo(expected.length); assertThat(result.length).isEqualTo(expected.length);
for (int i = 0; i < expected.length; i++) { for (int i = 0; i < expected.length; i++) {

View File

@ -27,12 +27,15 @@ import com.google.android.exoplayer2.RendererCapabilities.AdaptiveSupport;
import com.google.android.exoplayer2.RendererCapabilities.Capabilities; import com.google.android.exoplayer2.RendererCapabilities.Capabilities;
import com.google.android.exoplayer2.RendererConfiguration; import com.google.android.exoplayer2.RendererConfiguration;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.TracksInfo.TrackGroupInfo;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.testutil.FakeTimeline; import com.google.android.exoplayer2.testutil.FakeTimeline;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.common.collect.ImmutableList;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -129,6 +132,61 @@ public final class MappingTrackSelectorTest {
return new TrackGroup(new Format.Builder().setSampleMimeType(sampleMimeType).build()); return new TrackGroup(new Format.Builder().setSampleMimeType(sampleMimeType).build());
} }
@Test
public void buildTrackInfos_withTestValues_isAsExpected() {
MappingTrackSelector.MappedTrackInfo mappedTrackInfo =
new MappingTrackSelector.MappedTrackInfo(
new String[] {"1", "2"},
new int[] {C.TRACK_TYPE_AUDIO, C.TRACK_TYPE_VIDEO},
new TrackGroupArray[] {
new TrackGroupArray(
new TrackGroup(new Format.Builder().build()),
new TrackGroup(new Format.Builder().build())),
new TrackGroupArray(
new TrackGroup(new Format.Builder().build(), new Format.Builder().build()))
},
new int[] {
RendererCapabilities.ADAPTIVE_SEAMLESS, RendererCapabilities.ADAPTIVE_NOT_SUPPORTED
},
new int[][][] {
new int[][] {new int[] {C.FORMAT_HANDLED}, new int[] {C.FORMAT_UNSUPPORTED_SUBTYPE}},
new int[][] {new int[] {C.FORMAT_UNSUPPORTED_DRM, C.FORMAT_EXCEEDS_CAPABILITIES}}
},
new TrackGroupArray(new TrackGroup(new Format.Builder().build())));
TrackSelection[] selections =
new TrackSelection[] {
new FixedTrackSelection(mappedTrackInfo.getTrackGroups(0).get(1), 0),
new FixedTrackSelection(mappedTrackInfo.getTrackGroups(1).get(0), 1)
};
TracksInfo tracksInfo = MappingTrackSelector.buildTracksInfo(selections, mappedTrackInfo);
ImmutableList<TrackGroupInfo> trackGroupInfos = tracksInfo.getTrackGroupInfos();
assertThat(trackGroupInfos).hasSize(4);
assertThat(trackGroupInfos.get(0).getTrackGroup())
.isEqualTo(mappedTrackInfo.getTrackGroups(0).get(0));
assertThat(trackGroupInfos.get(1).getTrackGroup())
.isEqualTo(mappedTrackInfo.getTrackGroups(0).get(1));
assertThat(trackGroupInfos.get(2).getTrackGroup())
.isEqualTo(mappedTrackInfo.getTrackGroups(1).get(0));
assertThat(trackGroupInfos.get(3).getTrackGroup())
.isEqualTo(mappedTrackInfo.getUnmappedTrackGroups().get(0));
assertThat(trackGroupInfos.get(0).getTrackSupport(0)).isEqualTo(C.FORMAT_HANDLED);
assertThat(trackGroupInfos.get(1).getTrackSupport(0)).isEqualTo(C.FORMAT_UNSUPPORTED_SUBTYPE);
assertThat(trackGroupInfos.get(2).getTrackSupport(0)).isEqualTo(C.FORMAT_UNSUPPORTED_DRM);
assertThat(trackGroupInfos.get(2).getTrackSupport(1)).isEqualTo(C.FORMAT_EXCEEDS_CAPABILITIES);
assertThat(trackGroupInfos.get(3).getTrackSupport(0)).isEqualTo(C.FORMAT_UNSUPPORTED_TYPE);
assertThat(trackGroupInfos.get(0).isTrackSelected(0)).isFalse();
assertThat(trackGroupInfos.get(1).isTrackSelected(0)).isTrue();
assertThat(trackGroupInfos.get(2).isTrackSelected(0)).isFalse();
assertThat(trackGroupInfos.get(2).isTrackSelected(1)).isTrue();
assertThat(trackGroupInfos.get(3).isTrackSelected(0)).isFalse();
assertThat(trackGroupInfos.get(0).getTrackType()).isEqualTo(C.TRACK_TYPE_AUDIO);
assertThat(trackGroupInfos.get(1).getTrackType()).isEqualTo(C.TRACK_TYPE_AUDIO);
assertThat(trackGroupInfos.get(2).getTrackType()).isEqualTo(C.TRACK_TYPE_VIDEO);
assertThat(trackGroupInfos.get(3).getTrackType()).isEqualTo(C.TRACK_TYPE_UNKNOWN);
}
/** /**
* A {@link MappingTrackSelector} that stashes the {@link MappedTrackInfo} passed to {@link * A {@link MappingTrackSelector} that stashes the {@link MappedTrackInfo} passed to {@link
* #selectTracks(MappedTrackInfo, int[][][], int[], MediaPeriodId, Timeline)}. * #selectTracks(MappedTrackInfo, int[][][], int[], MediaPeriodId, Timeline)}.

View File

@ -45,6 +45,7 @@ import com.google.android.exoplayer2.Renderer;
import com.google.android.exoplayer2.RenderersFactory; import com.google.android.exoplayer2.RenderersFactory;
import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.analytics.AnalyticsListener; import com.google.android.exoplayer2.analytics.AnalyticsListener;
import com.google.android.exoplayer2.audio.AudioRendererEventListener; import com.google.android.exoplayer2.audio.AudioRendererEventListener;
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
@ -53,10 +54,8 @@ import com.google.android.exoplayer2.metadata.MetadataOutput;
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory; import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSourceFactory; import com.google.android.exoplayer2.source.MediaSourceFactory;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.text.TextOutput; import com.google.android.exoplayer2.text.TextOutput;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.util.Clock; import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
@ -636,8 +635,7 @@ public final class Transformer {
} }
@Override @Override
public void onTracksChanged( public void onTracksInfoChanged(EventTime eventTime, TracksInfo tracksInfo) {
EventTime eventTime, TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
if (muxerWrapper.getTrackCount() == 0) { if (muxerWrapper.getTrackCount() == 0) {
handleTransformationEnded( handleTransformationEnded(
new IllegalStateException( new IllegalStateException(

View File

@ -56,7 +56,7 @@ import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Player.DiscontinuityReason; import com.google.android.exoplayer2.Player.DiscontinuityReason;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Period; import com.google.android.exoplayer2.Timeline.Period;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
@ -1511,7 +1511,7 @@ public class PlayerView extends FrameLayout implements AdViewProvider {
} }
@Override @Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray selections) { public void onTracksInfoChanged(TracksInfo tracksInfo) {
// Suppress the update if transitioning to an unprepared period within the same window. This // Suppress the update if transitioning to an unprepared period within the same window. This
// is necessary to avoid closing the shutter when such a transition occurs. See: // is necessary to avoid closing the shutter when such a transition occurs. See:
// https://github.com/google/ExoPlayer/issues/5507. // https://github.com/google/ExoPlayer/issues/5507.

View File

@ -57,7 +57,7 @@ import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.Player.DiscontinuityReason; import com.google.android.exoplayer2.Player.DiscontinuityReason;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.Timeline.Period; import com.google.android.exoplayer2.Timeline.Period;
import com.google.android.exoplayer2.source.TrackGroupArray; import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
@ -1551,7 +1551,7 @@ public class StyledPlayerView extends FrameLayout implements AdViewProvider {
} }
@Override @Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray selections) { public void onTracksInfoChanged(TracksInfo tracksInfo) {
// Suppress the update if transitioning to an unprepared period within the same window. This // Suppress the update if transitioning to an unprepared period within the same window. This
// is necessary to avoid closing the shutter when such a transition occurs. See: // is necessary to avoid closing the shutter when such a transition occurs. See:
// https://github.com/google/ExoPlayer/issues/5507. // https://github.com/google/ExoPlayer/issues/5507.

View File

@ -38,9 +38,7 @@ import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.analytics.AnalyticsListener; import com.google.android.exoplayer2.analytics.AnalyticsListener;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.util.Clock; import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.HandlerWrapper; import com.google.android.exoplayer2.util.HandlerWrapper;
@ -383,7 +381,6 @@ public final class ExoPlayerTestRunner implements Player.Listener, ActionSchedul
private SimpleExoPlayer player; private SimpleExoPlayer player;
private Exception exception; private Exception exception;
private TrackGroupArray trackGroups;
private boolean playerWasPrepared; private boolean playerWasPrepared;
private ExoPlayerTestRunner( private ExoPlayerTestRunner(
@ -562,17 +559,6 @@ public final class ExoPlayerTestRunner implements Player.Listener, ActionSchedul
assertThat(playbackStates).containsExactlyElementsIn(states).inOrder(); assertThat(playbackStates).containsExactlyElementsIn(states).inOrder();
} }
/**
* Asserts that the last track group array reported by {@link
* Player.Listener#onTracksChanged(TrackGroupArray, TrackSelectionArray)} is equal to the provided
* track group array.
*
* @param trackGroupArray The expected {@link TrackGroupArray}.
*/
public void assertTrackGroupsEqual(TrackGroupArray trackGroupArray) {
assertThat(this.trackGroups).isEqualTo(trackGroupArray);
}
/** /**
* Asserts that {@link Player.Listener#onPositionDiscontinuity(Player.PositionInfo, * Asserts that {@link Player.Listener#onPositionDiscontinuity(Player.PositionInfo,
* Player.PositionInfo, int)} was not called. * Player.PositionInfo, int)} was not called.
@ -656,11 +642,6 @@ public final class ExoPlayerTestRunner implements Player.Listener, ActionSchedul
mediaItemTransitionReasons.add(reason); mediaItemTransitionReasons.add(reason);
} }
@Override
public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
this.trackGroups = trackGroups;
}
@Override @Override
public void onPlaybackStateChanged(@Player.State int playbackState) { public void onPlaybackStateChanged(@Player.State int playbackState) {
playbackStates.add(playbackState); playbackStates.add(playbackState);

View File

@ -33,6 +33,7 @@ import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.PlayerMessage; import com.google.android.exoplayer2.PlayerMessage;
import com.google.android.exoplayer2.SeekParameters; import com.google.android.exoplayer2.SeekParameters;
import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.TracksInfo;
import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.audio.AuxEffectInfo; import com.google.android.exoplayer2.audio.AuxEffectInfo;
import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource;
@ -463,6 +464,11 @@ public class StubExoPlayer extends BasePlayer implements ExoPlayer {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public TracksInfo getCurrentTracksInfo() {
throw new UnsupportedOperationException();
}
@Override @Override
public TrackSelectionParameters getTrackSelectionParameters() { public TrackSelectionParameters getTrackSelectionParameters() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();