Commit 7d6b0e1f by olly Committed by Oliver Woodman

Move renderer flags, overrides and tunneling ID into Parameters

- This is needed to make DefaultTrackSelector properly thread
  safe, and enable atomic changes to the selector that, for
  example, disable a renderer and enable another one at the same
  time.

- This change also saves/restores parameters and the start
  position in PlayerActivity, resolving the ref'd issue.

Issue: #3915

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=193913350
parent 5926e201
...@@ -116,6 +116,12 @@ public class PlayerActivity extends Activity ...@@ -116,6 +116,12 @@ public class PlayerActivity extends Activity
// For backwards compatibility only. // For backwards compatibility only.
private static final String DRM_SCHEME_UUID_EXTRA = "drm_scheme_uuid"; private static final String DRM_SCHEME_UUID_EXTRA = "drm_scheme_uuid";
// Saved instance state keys.
private static final String KEY_TRACK_SELECTOR_PARAMETERS = "track_selector_parameters";
private static final String KEY_WINDOW = "window";
private static final String KEY_POSITION = "position";
private static final String KEY_AUTO_PLAY = "auto_play";
private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter(); private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter();
private static final CookieManager DEFAULT_COOKIE_MANAGER; private static final CookieManager DEFAULT_COOKIE_MANAGER;
static { static {
...@@ -132,14 +138,15 @@ public class PlayerActivity extends Activity ...@@ -132,14 +138,15 @@ public class PlayerActivity extends Activity
private SimpleExoPlayer player; private SimpleExoPlayer player;
private MediaSource mediaSource; private MediaSource mediaSource;
private DefaultTrackSelector trackSelector; private DefaultTrackSelector trackSelector;
private DefaultTrackSelector.Parameters trackSelectorParameters;
private TrackSelectionHelper trackSelectionHelper; private TrackSelectionHelper trackSelectionHelper;
private DebugTextViewHelper debugViewHelper; private DebugTextViewHelper debugViewHelper;
private boolean inErrorState; private boolean inErrorState;
private TrackGroupArray lastSeenTrackGroupArray; private TrackGroupArray lastSeenTrackGroupArray;
private boolean shouldAutoPlay; private boolean startAutoPlay;
private int resumeWindow; private int startWindow;
private long resumePosition; private long startPosition;
// Fields used only for ad playback. The ads loader is loaded via reflection. // Fields used only for ad playback. The ads loader is loaded via reflection.
...@@ -152,8 +159,6 @@ public class PlayerActivity extends Activity ...@@ -152,8 +159,6 @@ public class PlayerActivity extends Activity
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
shouldAutoPlay = true;
clearResumePosition();
mediaDataSourceFactory = buildDataSourceFactory(true); mediaDataSourceFactory = buildDataSourceFactory(true);
mainHandler = new Handler(); mainHandler = new Handler();
if (CookieHandler.getDefault() != DEFAULT_COOKIE_MANAGER) { if (CookieHandler.getDefault() != DEFAULT_COOKIE_MANAGER) {
...@@ -169,13 +174,22 @@ public class PlayerActivity extends Activity ...@@ -169,13 +174,22 @@ public class PlayerActivity extends Activity
playerView = findViewById(R.id.player_view); playerView = findViewById(R.id.player_view);
playerView.setControllerVisibilityListener(this); playerView.setControllerVisibilityListener(this);
playerView.requestFocus(); playerView.requestFocus();
if (savedInstanceState != null) {
trackSelectorParameters = savedInstanceState.getParcelable(KEY_TRACK_SELECTOR_PARAMETERS);
startAutoPlay = savedInstanceState.getBoolean(KEY_AUTO_PLAY);
startWindow = savedInstanceState.getInt(KEY_WINDOW);
startPosition = savedInstanceState.getLong(KEY_POSITION);
} else {
trackSelectorParameters = new DefaultTrackSelector.ParametersBuilder().build();
clearStartPosition();
}
} }
@Override @Override
public void onNewIntent(Intent intent) { public void onNewIntent(Intent intent) {
releasePlayer(); releasePlayer();
shouldAutoPlay = true; clearStartPosition();
clearResumePosition();
setIntent(intent); setIntent(intent);
} }
...@@ -233,6 +247,16 @@ public class PlayerActivity extends Activity ...@@ -233,6 +247,16 @@ public class PlayerActivity extends Activity
} }
} }
@Override
public void onSaveInstanceState(Bundle outState) {
updateTrackSelectorParameters();
updateStartPosition();
outState.putParcelable(KEY_TRACK_SELECTOR_PARAMETERS, trackSelectorParameters);
outState.putBoolean(KEY_AUTO_PLAY, startAutoPlay);
outState.putInt(KEY_WINDOW, startWindow);
outState.putLong(KEY_POSITION, startPosition);
}
// Activity input // Activity input
@Override @Override
...@@ -365,6 +389,7 @@ public class PlayerActivity extends Activity ...@@ -365,6 +389,7 @@ public class PlayerActivity extends Activity
new DefaultRenderersFactory(this, extensionRendererMode); new DefaultRenderersFactory(this, extensionRendererMode);
trackSelector = new DefaultTrackSelector(trackSelectionFactory); trackSelector = new DefaultTrackSelector(trackSelectionFactory);
trackSelector.setParameters(trackSelectorParameters);
trackSelectionHelper = new TrackSelectionHelper(trackSelector); trackSelectionHelper = new TrackSelectionHelper(trackSelector);
lastSeenTrackGroupArray = null; lastSeenTrackGroupArray = null;
...@@ -381,7 +406,7 @@ public class PlayerActivity extends Activity ...@@ -381,7 +406,7 @@ public class PlayerActivity extends Activity
drmSessionManager.addListener(mainHandler, eventLogger); drmSessionManager.addListener(mainHandler, eventLogger);
} }
player.setPlayWhenReady(shouldAutoPlay); player.setPlayWhenReady(startAutoPlay);
playerView.setPlayer(player); playerView.setPlayer(player);
playerView.setPlaybackPreparer(this); playerView.setPlaybackPreparer(this);
debugViewHelper = new DebugTextViewHelper(player, debugTextView); debugViewHelper = new DebugTextViewHelper(player, debugTextView);
...@@ -421,11 +446,11 @@ public class PlayerActivity extends Activity ...@@ -421,11 +446,11 @@ public class PlayerActivity extends Activity
} }
mediaSource.addEventListener(mainHandler, eventLogger); mediaSource.addEventListener(mainHandler, eventLogger);
} }
boolean haveResumePosition = resumeWindow != C.INDEX_UNSET; boolean haveStartPosition = startWindow != C.INDEX_UNSET;
if (haveResumePosition) { if (haveStartPosition) {
player.seekTo(resumeWindow, resumePosition); player.seekTo(startWindow, startPosition);
} }
player.prepare(mediaSource, !haveResumePosition, false); player.prepare(mediaSource, !haveStartPosition, false);
inErrorState = false; inErrorState = false;
updateButtonVisibilities(); updateButtonVisibilities();
} }
...@@ -483,10 +508,10 @@ public class PlayerActivity extends Activity ...@@ -483,10 +508,10 @@ public class PlayerActivity extends Activity
private void releasePlayer() { private void releasePlayer() {
if (player != null) { if (player != null) {
updateTrackSelectorParameters();
updateStartPosition();
debugViewHelper.stop(); debugViewHelper.stop();
debugViewHelper = null; debugViewHelper = null;
shouldAutoPlay = player.getPlayWhenReady();
updateResumePosition();
player.release(); player.release();
player = null; player = null;
mediaSource = null; mediaSource = null;
...@@ -495,14 +520,24 @@ public class PlayerActivity extends Activity ...@@ -495,14 +520,24 @@ public class PlayerActivity extends Activity
} }
} }
private void updateResumePosition() { private void updateTrackSelectorParameters() {
resumeWindow = player.getCurrentWindowIndex(); if (trackSelector != null) {
resumePosition = Math.max(0, player.getContentPosition()); trackSelectorParameters = trackSelector.getParameters();
}
}
private void updateStartPosition() {
if (player != null) {
startAutoPlay = player.getPlayWhenReady();
startWindow = player.getCurrentWindowIndex();
startPosition = Math.max(0, player.getContentPosition());
}
} }
private void clearResumePosition() { private void clearStartPosition() {
resumeWindow = C.INDEX_UNSET; startAutoPlay = true;
resumePosition = C.TIME_UNSET; startWindow = C.INDEX_UNSET;
startPosition = C.TIME_UNSET;
} }
/** /**
...@@ -661,7 +696,7 @@ public class PlayerActivity extends Activity ...@@ -661,7 +696,7 @@ public class PlayerActivity extends Activity
// This will only occur if the user has performed a seek whilst in the error state. Update // This will only occur if the user has performed a seek whilst in the error state. Update
// the resume position so that if the user then retries, playback will resume from the // the resume position so that if the user then retries, playback will resume from the
// position to which they seeked. // position to which they seeked.
updateResumePosition(); updateStartPosition();
} }
} }
...@@ -695,10 +730,10 @@ public class PlayerActivity extends Activity ...@@ -695,10 +730,10 @@ public class PlayerActivity extends Activity
} }
inErrorState = true; inErrorState = true;
if (isBehindLiveWindow(e)) { if (isBehindLiveWindow(e)) {
clearResumePosition(); clearStartPosition();
initializePlayer(); initializePlayer();
} else { } else {
updateResumePosition(); updateStartPosition();
updateButtonVisibilities(); updateButtonVisibilities();
showControls(); showControls();
} }
......
...@@ -30,6 +30,8 @@ import com.google.android.exoplayer2.RendererCapabilities; ...@@ -30,6 +30,8 @@ import com.google.android.exoplayer2.RendererCapabilities;
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.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.Parameters;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.ParametersBuilder;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.SelectionOverride;
import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo; import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo;
import java.util.Arrays; import java.util.Arrays;
...@@ -79,8 +81,9 @@ import java.util.Arrays; ...@@ -79,8 +81,9 @@ import java.util.Arrays;
!= RendererCapabilities.ADAPTIVE_NOT_SUPPORTED != RendererCapabilities.ADAPTIVE_NOT_SUPPORTED
&& trackGroups.get(i).length > 1; && trackGroups.get(i).length > 1;
} }
isDisabled = selector.getRendererDisabled(rendererIndex); Parameters parameters = selector.getParameters();
override = selector.getSelectionOverride(rendererIndex, trackGroups); isDisabled = parameters.getRendererDisabled(rendererIndex);
override = parameters.getSelectionOverride(rendererIndex, trackGroups);
AlertDialog.Builder builder = new AlertDialog.Builder(activity); AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(title) builder.setTitle(title)
...@@ -170,12 +173,14 @@ import java.util.Arrays; ...@@ -170,12 +173,14 @@ import java.util.Arrays;
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
selector.setRendererDisabled(rendererIndex, isDisabled); ParametersBuilder parametersBuilder = selector.buildUponParameters();
parametersBuilder.setRendererDisabled(rendererIndex, isDisabled);
if (override != null) { if (override != null) {
selector.setSelectionOverride(rendererIndex, trackGroups, override); parametersBuilder.setSelectionOverride(rendererIndex, trackGroups, override);
} else { } else {
selector.clearSelectionOverrides(rendererIndex); parametersBuilder.clearSelectionOverrides(rendererIndex);
} }
selector.setParameters(parametersBuilder);
} }
// View.OnClickListener // View.OnClickListener
......
...@@ -45,42 +45,47 @@ import java.util.Map; ...@@ -45,42 +45,47 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
/** /**
* A default {@link TrackSelector} suitable for most use cases. * A default {@link TrackSelector} suitable for most use cases. Track selections are made according
* to configurable {@link Parameters}, which can be set by calling {@link
* #setParameters(Parameters)}.
* *
* <h3>Constraint based track selection</h3> * <h3>Modifying parameters</h3>
* *
* Whilst this selector supports setting specific track overrides, the recommended way of changing * To modify only some aspects of the parameters currently used by a selector, it's possible to
* which tracks are selected is by setting {@link Parameters} that constrain the track selection * obtain a {@link ParametersBuilder} initialized with the current {@link Parameters}. The desired
* process. For example an instance can specify a preferred language for the audio track, and impose * modifications can be made on the builder, and the resulting {@link Parameters} can then be built
* constraints on the maximum video resolution that should be selected for adaptive playbacks. * and set on the selector. For example the following code modifies the parameters to restrict video
* Modifying the parameters is simple: * track selections to SD, and to prefer German audio tracks:
* *
* <pre>{@code * <pre>{@code
* // Build on the current parameters.
* Parameters currentParameters = trackSelector.getParameters(); * Parameters currentParameters = trackSelector.getParameters();
* // Generate new parameters to prefer German audio and impose a maximum video size constraint. * // Build the resulting parameters.
* Parameters newParameters = currentParameters * Parameters newParameters = currentParameters
* .withPreferredAudioLanguage("deu") * .buildUpon()
* .withMaxVideoSize(1024, 768); * .setMaxVideoSizeSd()
* // Set the new parameters on the selector. * .setPreferredAudioLanguage("deu")
* .build();
* // Set the new parameters.
* trackSelector.setParameters(newParameters); * trackSelector.setParameters(newParameters);
* }</pre> * }</pre>
* *
* There are several benefits to using constraint based track selection instead of specific track * Convenience methods and chaining allow this to be written more concisely as:
* overrides:
* *
* <ul> * <pre>{@code
* <li>You can specify constraints before knowing what tracks the media provides. This can * trackSelector.setParameters(
* simplify track selection code (e.g. you don't have to listen for changes in the available * trackSelector
* tracks before configuring the selector). * .buildUponParameters()
* <li>Constraints can be applied consistently across all periods in a complex piece of media, * .setMaxVideoSizeSd()
* even if those periods contain different tracks. In contrast, a specific track override is * .setPreferredAudioLanguage("deu"));
* only applied to periods whose tracks match those for which the override was set. * }</pre>
* </ul> *
* Selection {@link Parameters} support many different options, some of which are described below.
* *
* <h3>Track overrides</h3> * <h3>Track selection overrides</h3>
* *
* This selector supports overriding of track selections for each renderer. To specify an override * Track selection overrides can be used to select specific tracks. To specify an override for a
* for a renderer it's first necessary to obtain the tracks that have been mapped to it: * renderer, it's first necessary to obtain the tracks that have been mapped to it:
* *
* <pre>{@code * <pre>{@code
* MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo(); * MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo();
...@@ -92,31 +97,58 @@ import java.util.concurrent.atomic.AtomicReference; ...@@ -92,31 +97,58 @@ import java.util.concurrent.atomic.AtomicReference;
* setting an override isn't possible. Note that a {@link Player.EventListener} registered on the * setting an override isn't possible. Note that a {@link Player.EventListener} registered on the
* player can be used to determine when the current tracks (and therefore the mapping) changes. If * player can be used to determine when the current tracks (and therefore the mapping) changes. If
* {@code rendererTrackGroups} is non-null then an override can be set. The next step is to query * {@code rendererTrackGroups} is non-null then an override can be set. The next step is to query
* the properties of the available tracks to determine the {@code groupIndex} of the track group you * the properties of the available tracks to determine the {@code groupIndex} and the {@code
* want to select and the {@code trackIndices} within it. You can then create and set the override: * trackIndices} within the group it that should be selected. The override can then be specified
* using {@link ParametersBuilder#setSelectionOverride}:
* *
* <pre>{@code * <pre>{@code
* trackSelector.setSelectionOverride(rendererIndex, rendererTrackGroups, * SelectionOverride selectionOverride = new SelectionOverride(groupIndex, trackIndices);
* new SelectionOverride(groupIndex, trackIndices)); * trackSelector.setParameters(
* trackSelector
* .buildUponParameters()
* .setSelectionOverride(rendererIndex, rendererTrackGroups, selectionOverride));
* }</pre> * }</pre>
* *
* If the override is {@code null} then no tracks will be selected. * <h3>Disabling renderers</h3>
*
* Renderers can be disabled using {@link ParametersBuilder#setRendererDisabled}. Disabling a
* renderer differs from setting a {@code null} override because the renderer is disabled
* unconditionally, whereas a {@code null} override is applied only when the track groups available
* to the renderer match the {@link TrackGroupArray} for which it was specified.
* *
* <p>Note that an override applies only when the track groups available to the renderer match the * <h3>Constraint based track selection</h3>
* {@link TrackGroupArray} for which the override was specified. Overrides can be cleared using the
* {@code clearSelectionOverride} methods.
* *
* <h3>Disabling renderers</h3> * Whilst track selection overrides make it possible to select specific tracks, the recommended way
* of controlling which tracks are selected is by specifying constraints. For example consider the
* case of wanting to restrict video track selections to SD, and preferring German audio tracks.
* Track selection overrides could be used to select specific tracks meeting these criteria, however
* a simpler and more flexible approach is to specify these constraints directly:
* *
* Renderers can be disabled using {@link #setRendererDisabled(int, boolean)}. Disabling a renderer * <pre>{@code
* differs from setting a {@code null} override because the renderer is disabled unconditionally, * trackSelector.setParameters(
* whereas a {@code null} override is applied only when the track groups available to the renderer * trackSelector
* match the {@link TrackGroupArray} for which it was specified. * .buildUponParameters()
* .setMaxVideoSizeSd()
* .setPreferredAudioLanguage("deu"));
* }</pre>
*
* There are several benefits to using constraint based track selection instead of specific track
* overrides:
*
* <ul>
* <li>You can specify constraints before knowing what tracks the media provides. This can
* simplify track selection code (e.g. you don't have to listen for changes in the available
* tracks before configuring the selector).
* <li>Constraints can be applied consistently across all periods in a complex piece of media,
* even if those periods contain different tracks. In contrast, a specific track override is
* only applied to periods whose tracks match those for which the override was set.
* </ul>
* *
* <h3>Tunneling</h3> * <h3>Tunneling</h3>
* *
* Tunneled playback can be enabled in cases where the combination of renderers and selected tracks * Tunneled playback can be enabled in cases where the combination of renderers and selected tracks
* support it. See {@link #setTunnelingAudioSessionId(int)} for more details. * support it. Tunneled playback is enabled by passing an audio session ID to {@link
* ParametersBuilder#setTunnelingAudioSessionId(int)}.
*/ */
public class DefaultTrackSelector extends MappingTrackSelector { public class DefaultTrackSelector extends MappingTrackSelector {
...@@ -125,6 +157,9 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -125,6 +157,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
*/ */
public static final class ParametersBuilder { public static final class ParametersBuilder {
private final SparseArray<Map<TrackGroupArray, SelectionOverride>> selectionOverrides;
private final SparseBooleanArray rendererDisabledFlags;
private String preferredAudioLanguage; private String preferredAudioLanguage;
private String preferredTextLanguage; private String preferredTextLanguage;
private boolean selectUndeterminedTextLanguage; private boolean selectUndeterminedTextLanguage;
...@@ -140,6 +175,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -140,6 +175,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
private int viewportWidth; private int viewportWidth;
private int viewportHeight; private int viewportHeight;
private boolean viewportOrientationMayChange; private boolean viewportOrientationMayChange;
private int tunnelingAudioSessionId;
/** /**
* Creates a builder obtaining the initial values from {@link Parameters#DEFAULT}. * Creates a builder obtaining the initial values from {@link Parameters#DEFAULT}.
...@@ -153,6 +189,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -153,6 +189,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* obtained. * obtained.
*/ */
private ParametersBuilder(Parameters initialValues) { private ParametersBuilder(Parameters initialValues) {
selectionOverrides = initialValues.selectionOverrides.clone();
rendererDisabledFlags = initialValues.rendererDisabledFlags.clone();
preferredAudioLanguage = initialValues.preferredAudioLanguage; preferredAudioLanguage = initialValues.preferredAudioLanguage;
preferredTextLanguage = initialValues.preferredTextLanguage; preferredTextLanguage = initialValues.preferredTextLanguage;
selectUndeterminedTextLanguage = initialValues.selectUndeterminedTextLanguage; selectUndeterminedTextLanguage = initialValues.selectUndeterminedTextLanguage;
...@@ -168,6 +206,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -168,6 +206,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
viewportWidth = initialValues.viewportWidth; viewportWidth = initialValues.viewportWidth;
viewportHeight = initialValues.viewportHeight; viewportHeight = initialValues.viewportHeight;
viewportOrientationMayChange = initialValues.viewportOrientationMayChange; viewportOrientationMayChange = initialValues.viewportOrientationMayChange;
tunnelingAudioSessionId = initialValues.tunnelingAudioSessionId;
} }
/** /**
...@@ -343,10 +382,133 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -343,10 +382,133 @@ public class DefaultTrackSelector extends MappingTrackSelector {
} }
/** /**
* Sets whether the renderer at the specified index is disabled. Disabling a renderer prevents
* the selector from selecting any tracks for it.
*
* @param rendererIndex The renderer index.
* @param disabled Whether the renderer is disabled.
*/
public final ParametersBuilder setRendererDisabled(int rendererIndex, boolean disabled) {
if (rendererDisabledFlags.get(rendererIndex) == disabled) {
// The disabled flag is unchanged.
return this;
}
// Only true values are placed in the array to make it easier to check for equality.
if (disabled) {
rendererDisabledFlags.put(rendererIndex, true);
} else {
rendererDisabledFlags.delete(rendererIndex);
}
return this;
}
/**
* Overrides the track selection for the renderer at the specified index.
*
* <p>When the {@link TrackGroupArray} mapped to the renderer matches the one provided, the
* override is applied. When the {@link TrackGroupArray} does not match, the override has no
* effect. The override replaces any previous override for the specified {@link TrackGroupArray}
* for the specified {@link Renderer}.
*
* <p>Passing a {@code null} override will cause the renderer to be disabled when the {@link
* TrackGroupArray} mapped to it matches the one provided. When the {@link TrackGroupArray} does
* not match a {@code null} override has no effect. Hence a {@code null} override differs from
* disabling the renderer using {@link #setRendererDisabled(int, boolean)} because the renderer
* is disabled conditionally on the {@link TrackGroupArray} mapped to it, where-as {@link
* #setRendererDisabled(int, boolean)} disables the renderer unconditionally.
*
* <p>To remove overrides use {@link #clearSelectionOverride(int, TrackGroupArray)}, {@link
* #clearSelectionOverrides(int)} or {@link #clearSelectionOverrides()}.
*
* @param rendererIndex The renderer index.
* @param groups The {@link TrackGroupArray} for which the override should be applied.
* @param override The override.
*/
public final ParametersBuilder setSelectionOverride(
int rendererIndex, TrackGroupArray groups, SelectionOverride override) {
Map<TrackGroupArray, SelectionOverride> overrides = selectionOverrides.get(rendererIndex);
if (overrides == null) {
overrides = new HashMap<>();
selectionOverrides.put(rendererIndex, overrides);
}
if (overrides.containsKey(groups) && Util.areEqual(overrides.get(groups), override)) {
// The override is unchanged.
return this;
}
overrides.put(groups, override);
return this;
}
/**
* Clears a track selection override for the specified renderer and {@link TrackGroupArray}.
*
* @param rendererIndex The renderer index.
* @param groups The {@link TrackGroupArray} for which the override should be cleared.
*/
public final ParametersBuilder clearSelectionOverride(
int rendererIndex, TrackGroupArray groups) {
Map<TrackGroupArray, SelectionOverride> overrides = selectionOverrides.get(rendererIndex);
if (overrides == null || !overrides.containsKey(groups)) {
// Nothing to clear.
return this;
}
overrides.remove(groups);
if (overrides.isEmpty()) {
selectionOverrides.remove(rendererIndex);
}
return this;
}
/**
* Clears all track selection overrides for the specified renderer.
*
* @param rendererIndex The renderer index.
*/
public final ParametersBuilder clearSelectionOverrides(int rendererIndex) {
Map<TrackGroupArray, SelectionOverride> overrides = selectionOverrides.get(rendererIndex);
if (overrides == null || overrides.isEmpty()) {
// Nothing to clear.
return this;
}
selectionOverrides.remove(rendererIndex);
return this;
}
/** Clears all track selection overrides for all renderers. */
public final ParametersBuilder clearSelectionOverrides() {
if (selectionOverrides.size() == 0) {
// Nothing to clear.
return this;
}
selectionOverrides.clear();
return this;
}
/**
* Enables or disables tunneling. To enable tunneling, pass an audio session id to use when in
* tunneling mode. Session ids can be generated using {@link
* C#generateAudioSessionIdV21(Context)}. To disable tunneling pass {@link
* C#AUDIO_SESSION_ID_UNSET}. Tunneling will only be activated if it's both enabled and
* supported by the audio and video renderers for the selected tracks.
*
* @param tunnelingAudioSessionId The audio session id to use when tunneling, or {@link
* C#AUDIO_SESSION_ID_UNSET} to disable tunneling.
*/
public ParametersBuilder setTunnelingAudioSessionId(int tunnelingAudioSessionId) {
if (this.tunnelingAudioSessionId != tunnelingAudioSessionId) {
this.tunnelingAudioSessionId = tunnelingAudioSessionId;
return this;
}
return this;
}
/**
* Builds a {@link Parameters} instance with the selected values. * Builds a {@link Parameters} instance with the selected values.
*/ */
public Parameters build() { public Parameters build() {
return new Parameters( return new Parameters(
selectionOverrides,
rendererDisabledFlags,
preferredAudioLanguage, preferredAudioLanguage,
preferredTextLanguage, preferredTextLanguage,
selectUndeterminedTextLanguage, selectUndeterminedTextLanguage,
...@@ -361,7 +523,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -361,7 +523,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
exceedRendererCapabilitiesIfNecessary, exceedRendererCapabilitiesIfNecessary,
viewportWidth, viewportWidth,
viewportHeight, viewportHeight,
viewportOrientationMayChange); viewportOrientationMayChange,
tunnelingAudioSessionId);
} }
} }
...@@ -390,6 +553,11 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -390,6 +553,11 @@ public class DefaultTrackSelector extends MappingTrackSelector {
*/ */
public static final Parameters DEFAULT = new Parameters(); public static final Parameters DEFAULT = new Parameters();
// Per renderer overrides.
private final SparseArray<Map<TrackGroupArray, SelectionOverride>> selectionOverrides;
private final SparseBooleanArray rendererDisabledFlags;
// Audio // Audio
/** /**
* The preferred language for audio, as well as for forced text tracks, as an ISO 639-2/T tag. * The preferred language for audio, as well as for forced text tracks, as an ISO 639-2/T tag.
...@@ -465,9 +633,16 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -465,9 +633,16 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* Whether to exceed renderer capabilities when no selection can be made otherwise. * Whether to exceed renderer capabilities when no selection can be made otherwise.
*/ */
public final boolean exceedRendererCapabilitiesIfNecessary; public final boolean exceedRendererCapabilitiesIfNecessary;
/**
* The audio session id to use when tunneling, or {@link C#AUDIO_SESSION_ID_UNSET} if tunneling
* is not to be enabled.
*/
public final int tunnelingAudioSessionId;
private Parameters() { private Parameters() {
this( this(
new SparseArray<Map<TrackGroupArray, SelectionOverride>>(),
new SparseBooleanArray(),
null, null,
null, null,
false, false,
...@@ -482,10 +657,13 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -482,10 +657,13 @@ public class DefaultTrackSelector extends MappingTrackSelector {
true, true,
Integer.MAX_VALUE, Integer.MAX_VALUE,
Integer.MAX_VALUE, Integer.MAX_VALUE,
true); true,
C.AUDIO_SESSION_ID_UNSET);
} }
/* package */ Parameters( /* package */ Parameters(
SparseArray<Map<TrackGroupArray, SelectionOverride>> selectionOverrides,
SparseBooleanArray rendererDisabledFlags,
String preferredAudioLanguage, String preferredAudioLanguage,
String preferredTextLanguage, String preferredTextLanguage,
boolean selectUndeterminedTextLanguage, boolean selectUndeterminedTextLanguage,
...@@ -500,7 +678,10 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -500,7 +678,10 @@ public class DefaultTrackSelector extends MappingTrackSelector {
boolean exceedRendererCapabilitiesIfNecessary, boolean exceedRendererCapabilitiesIfNecessary,
int viewportWidth, int viewportWidth,
int viewportHeight, int viewportHeight,
boolean viewportOrientationMayChange) { boolean viewportOrientationMayChange,
int tunnelingAudioSessionId) {
this.selectionOverrides = selectionOverrides;
this.rendererDisabledFlags = rendererDisabledFlags;
this.preferredAudioLanguage = Util.normalizeLanguageCode(preferredAudioLanguage); this.preferredAudioLanguage = Util.normalizeLanguageCode(preferredAudioLanguage);
this.preferredTextLanguage = Util.normalizeLanguageCode(preferredTextLanguage); this.preferredTextLanguage = Util.normalizeLanguageCode(preferredTextLanguage);
this.selectUndeterminedTextLanguage = selectUndeterminedTextLanguage; this.selectUndeterminedTextLanguage = selectUndeterminedTextLanguage;
...@@ -516,9 +697,12 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -516,9 +697,12 @@ public class DefaultTrackSelector extends MappingTrackSelector {
this.viewportWidth = viewportWidth; this.viewportWidth = viewportWidth;
this.viewportHeight = viewportHeight; this.viewportHeight = viewportHeight;
this.viewportOrientationMayChange = viewportOrientationMayChange; this.viewportOrientationMayChange = viewportOrientationMayChange;
this.tunnelingAudioSessionId = tunnelingAudioSessionId;
} }
/* package */ Parameters(Parcel in) { /* package */ Parameters(Parcel in) {
this.selectionOverrides = readSelectionOverrides(in);
this.rendererDisabledFlags = in.readSparseBooleanArray();
this.preferredAudioLanguage = in.readString(); this.preferredAudioLanguage = in.readString();
this.preferredTextLanguage = in.readString(); this.preferredTextLanguage = in.readString();
this.selectUndeterminedTextLanguage = Util.readBoolean(in); this.selectUndeterminedTextLanguage = Util.readBoolean(in);
...@@ -534,6 +718,41 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -534,6 +718,41 @@ public class DefaultTrackSelector extends MappingTrackSelector {
this.viewportWidth = in.readInt(); this.viewportWidth = in.readInt();
this.viewportHeight = in.readInt(); this.viewportHeight = in.readInt();
this.viewportOrientationMayChange = Util.readBoolean(in); this.viewportOrientationMayChange = Util.readBoolean(in);
this.tunnelingAudioSessionId = in.readInt();
}
/**
* Returns whether the renderer is disabled.
*
* @param rendererIndex The renderer index.
* @return Whether the renderer is disabled.
*/
public final boolean getRendererDisabled(int rendererIndex) {
return rendererDisabledFlags.get(rendererIndex);
}
/**
* Returns whether there is an override for the specified renderer and {@link TrackGroupArray}.
*
* @param rendererIndex The renderer index.
* @param groups The {@link TrackGroupArray}.
* @return Whether there is an override.
*/
public final boolean hasSelectionOverride(int rendererIndex, TrackGroupArray groups) {
Map<TrackGroupArray, SelectionOverride> overrides = selectionOverrides.get(rendererIndex);
return overrides != null && overrides.containsKey(groups);
}
/**
* Returns the override for the specified renderer and {@link TrackGroupArray}.
*
* @param rendererIndex The renderer index.
* @param groups The {@link TrackGroupArray}.
* @return The override, or null if no override exists.
*/
public final SelectionOverride getSelectionOverride(int rendererIndex, TrackGroupArray groups) {
Map<TrackGroupArray, SelectionOverride> overrides = selectionOverrides.get(rendererIndex);
return overrides != null ? overrides.get(groups) : null;
} }
/** /**
...@@ -565,8 +784,11 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -565,8 +784,11 @@ public class DefaultTrackSelector extends MappingTrackSelector {
&& viewportWidth == other.viewportWidth && viewportWidth == other.viewportWidth
&& viewportHeight == other.viewportHeight && viewportHeight == other.viewportHeight
&& maxVideoBitrate == other.maxVideoBitrate && maxVideoBitrate == other.maxVideoBitrate
&& tunnelingAudioSessionId == other.tunnelingAudioSessionId
&& TextUtils.equals(preferredAudioLanguage, other.preferredAudioLanguage) && TextUtils.equals(preferredAudioLanguage, other.preferredAudioLanguage)
&& TextUtils.equals(preferredTextLanguage, other.preferredTextLanguage); && TextUtils.equals(preferredTextLanguage, other.preferredTextLanguage)
&& areRendererDisabledFlagsEqual(rendererDisabledFlags, other.rendererDisabledFlags)
&& areSelectionOverridesEqual(selectionOverrides, other.selectionOverrides);
} }
@Override @Override
...@@ -584,6 +806,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -584,6 +806,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
result = 31 * result + viewportWidth; result = 31 * result + viewportWidth;
result = 31 * result + viewportHeight; result = 31 * result + viewportHeight;
result = 31 * result + maxVideoBitrate; result = 31 * result + maxVideoBitrate;
result = 31 * result + tunnelingAudioSessionId;
result = 31 * result + preferredAudioLanguage.hashCode(); result = 31 * result + preferredAudioLanguage.hashCode();
result = 31 * result + preferredTextLanguage.hashCode(); result = 31 * result + preferredTextLanguage.hashCode();
return result; return result;
...@@ -598,6 +821,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -598,6 +821,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
@Override @Override
public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) {
writeSelectionOverridesToParcel(dest, selectionOverrides);
dest.writeSparseBooleanArray(rendererDisabledFlags);
dest.writeString(preferredAudioLanguage); dest.writeString(preferredAudioLanguage);
dest.writeString(preferredTextLanguage); dest.writeString(preferredTextLanguage);
Util.writeBoolean(dest, selectUndeterminedTextLanguage); Util.writeBoolean(dest, selectUndeterminedTextLanguage);
...@@ -613,6 +838,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -613,6 +838,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
dest.writeInt(viewportWidth); dest.writeInt(viewportWidth);
dest.writeInt(viewportHeight); dest.writeInt(viewportHeight);
Util.writeBoolean(dest, viewportOrientationMayChange); Util.writeBoolean(dest, viewportOrientationMayChange);
dest.writeInt(tunnelingAudioSessionId);
} }
public static final Parcelable.Creator<Parameters> CREATOR = public static final Parcelable.Creator<Parameters> CREATOR =
...@@ -628,6 +854,93 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -628,6 +854,93 @@ public class DefaultTrackSelector extends MappingTrackSelector {
return new Parameters[size]; return new Parameters[size];
} }
}; };
// Static utility methods.
private static SparseArray<Map<TrackGroupArray, SelectionOverride>> readSelectionOverrides(
Parcel in) {
int renderersWithOverridesCount = in.readInt();
SparseArray<Map<TrackGroupArray, SelectionOverride>> selectionOverrides =
new SparseArray<>(renderersWithOverridesCount);
for (int i = 0; i < renderersWithOverridesCount; i++) {
int rendererIndex = in.readInt();
int overrideCount = in.readInt();
Map<TrackGroupArray, SelectionOverride> overrides = new HashMap<>(overrideCount);
for (int j = 0; j < overrideCount; j++) {
TrackGroupArray trackGroups = in.readParcelable(TrackGroupArray.class.getClassLoader());
SelectionOverride override = in.readParcelable(SelectionOverride.class.getClassLoader());
overrides.put(trackGroups, override);
}
selectionOverrides.put(rendererIndex, overrides);
}
return selectionOverrides;
}
private static void writeSelectionOverridesToParcel(
Parcel dest, SparseArray<Map<TrackGroupArray, SelectionOverride>> selectionOverrides) {
int renderersWithOverridesCount = selectionOverrides.size();
dest.writeInt(renderersWithOverridesCount);
for (int i = 0; i < renderersWithOverridesCount; i++) {
int rendererIndex = selectionOverrides.keyAt(i);
Map<TrackGroupArray, SelectionOverride> overrides = selectionOverrides.valueAt(i);
int overrideCount = overrides.size();
dest.writeInt(rendererIndex);
dest.writeInt(overrideCount);
for (Map.Entry<TrackGroupArray, SelectionOverride> override : overrides.entrySet()) {
dest.writeParcelable(override.getKey(), /* parcelableFlags= */ 0);
dest.writeParcelable(override.getValue(), /* parcelableFlags= */ 0);
}
}
}
private static boolean areRendererDisabledFlagsEqual(
SparseBooleanArray first, SparseBooleanArray second) {
int firstSize = first.size();
if (second.size() != firstSize) {
return false;
}
// Only true values are put into rendererDisabledFlags, so we don't need to compare values.
for (int indexInFirst = 0; indexInFirst < firstSize; indexInFirst++) {
if (second.indexOfKey(first.keyAt(indexInFirst)) < 0) {
return false;
}
}
return true;
}
private static boolean areSelectionOverridesEqual(
SparseArray<Map<TrackGroupArray, SelectionOverride>> first,
SparseArray<Map<TrackGroupArray, SelectionOverride>> second) {
int firstSize = first.size();
if (second.size() != firstSize) {
return false;
}
for (int indexInFirst = 0; indexInFirst < firstSize; indexInFirst++) {
int indexInSecond = second.indexOfKey(first.keyAt(indexInFirst));
if (indexInSecond < 0
|| !areSelectionOverridesEqual(
first.valueAt(indexInFirst), second.valueAt(indexInSecond))) {
return false;
}
}
return true;
}
private static boolean areSelectionOverridesEqual(
Map<TrackGroupArray, SelectionOverride> first,
Map<TrackGroupArray, SelectionOverride> second) {
int firstSize = first.size();
if (second.size() != firstSize) {
return false;
}
for (Map.Entry<TrackGroupArray, SelectionOverride> firstEntry : first.entrySet()) {
TrackGroupArray key = firstEntry.getKey();
if (!second.containsKey(key) || !Util.areEqual(firstEntry.getValue(), second.get(key))) {
return false;
}
}
return true;
}
} }
/** A track selection override. */ /** A track selection override. */
...@@ -720,11 +1033,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -720,11 +1033,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
private static final int WITHIN_RENDERER_CAPABILITIES_BONUS = 1000; private static final int WITHIN_RENDERER_CAPABILITIES_BONUS = 1000;
private final TrackSelection.Factory adaptiveTrackSelectionFactory; private final TrackSelection.Factory adaptiveTrackSelectionFactory;
private final AtomicReference<Parameters> paramsReference; private final AtomicReference<Parameters> parametersReference;
private final SparseArray<Map<TrackGroupArray, SelectionOverride>> selectionOverrides;
private final SparseBooleanArray rendererDisabledFlags;
private int tunnelingAudioSessionId;
/** /**
* Constructs an instance that does not support adaptive track selection. * Constructs an instance that does not support adaptive track selection.
...@@ -752,179 +1061,100 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -752,179 +1061,100 @@ public class DefaultTrackSelector extends MappingTrackSelector {
*/ */
public DefaultTrackSelector(TrackSelection.Factory adaptiveTrackSelectionFactory) { public DefaultTrackSelector(TrackSelection.Factory adaptiveTrackSelectionFactory) {
this.adaptiveTrackSelectionFactory = adaptiveTrackSelectionFactory; this.adaptiveTrackSelectionFactory = adaptiveTrackSelectionFactory;
paramsReference = new AtomicReference<>(Parameters.DEFAULT); parametersReference = new AtomicReference<>(Parameters.DEFAULT);
selectionOverrides = new SparseArray<>();
rendererDisabledFlags = new SparseBooleanArray();
tunnelingAudioSessionId = C.AUDIO_SESSION_ID_UNSET;
} }
/** /**
* Atomically sets the provided parameters for track selection. * Atomically sets the provided parameters for track selection.
* *
* @param params The parameters for track selection. * @param parameters The parameters for track selection.
*/ */
public void setParameters(Parameters params) { public void setParameters(Parameters parameters) {
Assertions.checkNotNull(params); Assertions.checkNotNull(parameters);
if (!paramsReference.getAndSet(params).equals(params)) { if (!parametersReference.getAndSet(parameters).equals(parameters)) {
invalidate(); invalidate();
} }
} }
/** /**
* Gets the current selection parameters. * Atomically sets the provided parameters for track selection.
* *
* @return The current selection parameters. * @param parametersBuilder A builder from which to obtain the parameters for track selection.
*/ */
public Parameters getParameters() { public void setParameters(ParametersBuilder parametersBuilder) {
return paramsReference.get(); setParameters(parametersBuilder.build());
} }
/** /**
* Sets whether the renderer at the specified index is disabled. Disabling a renderer prevents the * Gets the current selection parameters.
* selector from selecting any tracks for it.
* *
* @param rendererIndex The renderer index. * @return The current selection parameters.
* @param disabled Whether the renderer is disabled.
*/ */
public Parameters getParameters() {
return parametersReference.get();
}
/** Returns a new {@link ParametersBuilder} initialized with the current selection parameters. */
public ParametersBuilder buildUponParameters() {
return getParameters().buildUpon();
}
/** @deprecated Use {@link ParametersBuilder#setRendererDisabled(int, boolean)}. */
@Deprecated
public final void setRendererDisabled(int rendererIndex, boolean disabled) { public final void setRendererDisabled(int rendererIndex, boolean disabled) {
if (rendererDisabledFlags.get(rendererIndex) == disabled) { setParameters(buildUponParameters().setRendererDisabled(rendererIndex, disabled));
// The disabled flag is unchanged.
return;
}
rendererDisabledFlags.put(rendererIndex, disabled);
invalidate();
} }
/** /** @deprecated Use {@link Parameters#getRendererDisabled(int)}. * */
* Returns whether the renderer is disabled. @Deprecated
*
* @param rendererIndex The renderer index.
* @return Whether the renderer is disabled.
*/
public final boolean getRendererDisabled(int rendererIndex) { public final boolean getRendererDisabled(int rendererIndex) {
return rendererDisabledFlags.get(rendererIndex); return getParameters().getRendererDisabled(rendererIndex);
} }
/** /**
* Overrides the track selection for the renderer at the specified index. * @deprecated Use {@link ParametersBuilder#setSelectionOverride(int, TrackGroupArray,
* * SelectionOverride)}.
* <p>When the {@link TrackGroupArray} mapped to the renderer matches the one provided, the
* override is applied. When the {@link TrackGroupArray} does not match, the override has no
* effect. The override replaces any previous override for the specified {@link TrackGroupArray}
* for the specified {@link Renderer}.
*
* <p>Passing a {@code null} override will cause the renderer to be disabled when the {@link
* TrackGroupArray} mapped to it matches the one provided. When the {@link TrackGroupArray} does
* not match a {@code null} override has no effect. Hence a {@code null} override differs from
* disabling the renderer using {@link #setRendererDisabled(int, boolean)} because the renderer is
* disabled conditionally on the {@link TrackGroupArray} mapped to it, where-as {@link
* #setRendererDisabled(int, boolean)} disables the renderer unconditionally.
*
* <p>To remove overrides use {@link #clearSelectionOverride(int, TrackGroupArray)}, {@link
* #clearSelectionOverrides(int)} or {@link #clearSelectionOverrides()}.
*
* @param rendererIndex The renderer index.
* @param groups The {@link TrackGroupArray} for which the override should be applied.
* @param override The override.
*/ */
@Deprecated
public final void setSelectionOverride( public final void setSelectionOverride(
int rendererIndex, TrackGroupArray groups, SelectionOverride override) { int rendererIndex, TrackGroupArray groups, SelectionOverride override) {
Map<TrackGroupArray, SelectionOverride> overrides = selectionOverrides.get(rendererIndex); setParameters(buildUponParameters().setSelectionOverride(rendererIndex, groups, override));
if (overrides == null) {
overrides = new HashMap<>();
selectionOverrides.put(rendererIndex, overrides);
}
if (overrides.containsKey(groups) && Util.areEqual(overrides.get(groups), override)) {
// The override is unchanged.
return;
}
overrides.put(groups, override);
invalidate();
} }
/** /** @deprecated Use {@link Parameters#hasSelectionOverride(int, TrackGroupArray)}. * */
* Returns whether there is an override for the specified renderer and {@link TrackGroupArray}. @Deprecated
*
* @param rendererIndex The renderer index.
* @param groups The {@link TrackGroupArray}.
* @return Whether there is an override.
*/
public final boolean hasSelectionOverride(int rendererIndex, TrackGroupArray groups) { public final boolean hasSelectionOverride(int rendererIndex, TrackGroupArray groups) {
Map<TrackGroupArray, SelectionOverride> overrides = selectionOverrides.get(rendererIndex); return getParameters().hasSelectionOverride(rendererIndex, groups);
return overrides != null && overrides.containsKey(groups);
} }
/** /** @deprecated Use {@link Parameters#getSelectionOverride(int, TrackGroupArray)}. */
* Returns the override for the specified renderer and {@link TrackGroupArray}. @Deprecated
*
* @param rendererIndex The renderer index.
* @param groups The {@link TrackGroupArray}.
* @return The override, or null if no override exists.
*/
public final SelectionOverride getSelectionOverride(int rendererIndex, TrackGroupArray groups) { public final SelectionOverride getSelectionOverride(int rendererIndex, TrackGroupArray groups) {
Map<TrackGroupArray, SelectionOverride> overrides = selectionOverrides.get(rendererIndex); return getParameters().getSelectionOverride(rendererIndex, groups);
return overrides != null ? overrides.get(groups) : null;
} }
/** /** @deprecated Use {@link ParametersBuilder#clearSelectionOverride(int, TrackGroupArray)}. */
* Clears a track selection override for the specified renderer and {@link TrackGroupArray}. @Deprecated
*
* @param rendererIndex The renderer index.
* @param groups The {@link TrackGroupArray} for which the override should be cleared.
*/
public final void clearSelectionOverride(int rendererIndex, TrackGroupArray groups) { public final void clearSelectionOverride(int rendererIndex, TrackGroupArray groups) {
Map<TrackGroupArray, SelectionOverride> overrides = selectionOverrides.get(rendererIndex); setParameters(buildUponParameters().clearSelectionOverride(rendererIndex, groups));
if (overrides == null || !overrides.containsKey(groups)) {
// Nothing to clear.
return;
}
overrides.remove(groups);
if (overrides.isEmpty()) {
selectionOverrides.remove(rendererIndex);
}
invalidate();
} }
/** /** @deprecated Use {@link ParametersBuilder#clearSelectionOverrides(int)}. */
* Clears all track selection overrides for the specified renderer. @Deprecated
*
* @param rendererIndex The renderer index.
*/
public final void clearSelectionOverrides(int rendererIndex) { public final void clearSelectionOverrides(int rendererIndex) {
Map<TrackGroupArray, SelectionOverride> overrides = selectionOverrides.get(rendererIndex); setParameters(buildUponParameters().clearSelectionOverrides(rendererIndex));
if (overrides == null || overrides.isEmpty()) {
// Nothing to clear.
return;
}
selectionOverrides.remove(rendererIndex);
invalidate();
} }
/** Clears all track selection overrides for all renderers. */ /** @deprecated Use {@link ParametersBuilder#clearSelectionOverrides()}. */
@Deprecated
public final void clearSelectionOverrides() { public final void clearSelectionOverrides() {
if (selectionOverrides.size() == 0) { setParameters(buildUponParameters().clearSelectionOverrides());
// Nothing to clear.
return;
}
selectionOverrides.clear();
invalidate();
} }
/** /** @deprecated Use {@link ParametersBuilder#setTunnelingAudioSessionId(int)}. */
* Enables or disables tunneling. To enable tunneling, pass an audio session id to use when in @Deprecated
* tunneling mode. Session ids can be generated using {@link
* C#generateAudioSessionIdV21(Context)}. To disable tunneling pass {@link
* C#AUDIO_SESSION_ID_UNSET}. Tunneling will only be activated if it's both enabled and supported
* by the audio and video renderers for the selected tracks.
*
* @param tunnelingAudioSessionId The audio session id to use when tunneling, or {@link
* C#AUDIO_SESSION_ID_UNSET} to disable tunneling.
*/
public void setTunnelingAudioSessionId(int tunnelingAudioSessionId) { public void setTunnelingAudioSessionId(int tunnelingAudioSessionId) {
if (this.tunnelingAudioSessionId != tunnelingAudioSessionId) { setParameters(buildUponParameters().setTunnelingAudioSessionId(tunnelingAudioSessionId));
this.tunnelingAudioSessionId = tunnelingAudioSessionId;
invalidate();
}
} }
// MappingTrackSelector implementation. // MappingTrackSelector implementation.
...@@ -935,29 +1165,33 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -935,29 +1165,33 @@ public class DefaultTrackSelector extends MappingTrackSelector {
int[][][] rendererFormatSupports, int[][][] rendererFormatSupports,
int[] rendererMixedMimeTypeAdaptationSupports) int[] rendererMixedMimeTypeAdaptationSupports)
throws ExoPlaybackException { throws ExoPlaybackException {
Parameters params = parametersReference.get();
int rendererCount = mappedTrackInfo.getRendererCount(); int rendererCount = mappedTrackInfo.getRendererCount();
TrackSelection[] rendererTrackSelections = TrackSelection[] rendererTrackSelections =
selectAllTracks( selectAllTracks(
mappedTrackInfo, rendererFormatSupports, rendererMixedMimeTypeAdaptationSupports); mappedTrackInfo,
rendererFormatSupports,
rendererMixedMimeTypeAdaptationSupports,
params);
// Apply track disabling and overriding. // Apply track disabling and overriding.
for (int i = 0; i < rendererCount; i++) { for (int i = 0; i < rendererCount; i++) {
if (rendererDisabledFlags.get(i)) { if (params.getRendererDisabled(i)) {
rendererTrackSelections[i] = null; rendererTrackSelections[i] = null;
} else { } else {
TrackGroupArray rendererTrackGroup = mappedTrackInfo.getTrackGroups(i); TrackGroupArray rendererTrackGroups = mappedTrackInfo.getTrackGroups(i);
if (hasSelectionOverride(i, rendererTrackGroup)) { if (params.hasSelectionOverride(i, rendererTrackGroups)) {
SelectionOverride override = selectionOverrides.get(i).get(rendererTrackGroup); SelectionOverride override = params.getSelectionOverride(i, rendererTrackGroups);
if (override == null) { if (override == null) {
rendererTrackSelections[i] = null; rendererTrackSelections[i] = null;
} else if (override.length == 1) { } else if (override.length == 1) {
rendererTrackSelections[i] = rendererTrackSelections[i] =
new FixedTrackSelection( new FixedTrackSelection(
rendererTrackGroup.get(override.groupIndex), override.tracks[0]); rendererTrackGroups.get(override.groupIndex), override.tracks[0]);
} else { } else {
rendererTrackSelections[i] = rendererTrackSelections[i] =
adaptiveTrackSelectionFactory.createTrackSelection( adaptiveTrackSelectionFactory.createTrackSelection(
rendererTrackGroup.get(override.groupIndex), override.tracks); rendererTrackGroups.get(override.groupIndex), override.tracks);
} }
} }
} }
...@@ -967,7 +1201,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -967,7 +1201,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// selections, and null otherwise. // selections, and null otherwise.
RendererConfiguration[] rendererConfigurations = new RendererConfiguration[rendererCount]; RendererConfiguration[] rendererConfigurations = new RendererConfiguration[rendererCount];
for (int i = 0; i < rendererCount; i++) { for (int i = 0; i < rendererCount; i++) {
boolean forceRendererDisabled = rendererDisabledFlags.get(i); boolean forceRendererDisabled = params.getRendererDisabled(i);
boolean rendererEnabled = boolean rendererEnabled =
!forceRendererDisabled !forceRendererDisabled
&& (mappedTrackInfo.getRendererType(i) == C.TRACK_TYPE_NONE && (mappedTrackInfo.getRendererType(i) == C.TRACK_TYPE_NONE
...@@ -981,7 +1215,7 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -981,7 +1215,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
rendererFormatSupports, rendererFormatSupports,
rendererConfigurations, rendererConfigurations,
rendererTrackSelections, rendererTrackSelections,
tunnelingAudioSessionId); params.tunnelingAudioSessionId);
return Pair.create(rendererConfigurations, rendererTrackSelections); return Pair.create(rendererConfigurations, rendererTrackSelections);
} }
...@@ -1007,11 +1241,11 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -1007,11 +1241,11 @@ public class DefaultTrackSelector extends MappingTrackSelector {
protected TrackSelection[] selectAllTracks( protected TrackSelection[] selectAllTracks(
MappedTrackInfo mappedTrackInfo, MappedTrackInfo mappedTrackInfo,
int[][][] rendererFormatSupports, int[][][] rendererFormatSupports,
int[] rendererMixedMimeTypeAdaptationSupports) int[] rendererMixedMimeTypeAdaptationSupports,
Parameters params)
throws ExoPlaybackException { throws ExoPlaybackException {
int rendererCount = mappedTrackInfo.getRendererCount(); int rendererCount = mappedTrackInfo.getRendererCount();
TrackSelection[] rendererTrackSelections = new TrackSelection[rendererCount]; TrackSelection[] rendererTrackSelections = new TrackSelection[rendererCount];
Parameters params = paramsReference.get();
boolean seenVideoRendererWithMappedTracks = false; boolean seenVideoRendererWithMappedTracks = false;
boolean selectedVideoTracks = false; boolean selectedVideoTracks = false;
...@@ -1073,8 +1307,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -1073,8 +1307,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// Video track selection implementation. // Video track selection implementation.
/** /**
* Called by {@link #selectAllTracks(MappedTrackInfo, int[][][], int[])} to create a {@link * Called by {@link #selectAllTracks(MappedTrackInfo, int[][][], int[], Parameters)} to create a
* TrackSelection} for a video renderer. * {@link TrackSelection} for a video renderer.
* *
* @param groups The {@link TrackGroupArray} mapped to the renderer. * @param groups The {@link TrackGroupArray} mapped to the renderer.
* @param formatSupports The result of {@link RendererCapabilities#supportsFormat} for each mapped * @param formatSupports The result of {@link RendererCapabilities#supportsFormat} for each mapped
...@@ -1279,8 +1513,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -1279,8 +1513,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// Audio track selection implementation. // Audio track selection implementation.
/** /**
* Called by {@link #selectAllTracks(MappedTrackInfo, int[][][], int[])} to create a {@link * Called by {@link #selectAllTracks(MappedTrackInfo, int[][][], int[], Parameters)} to create a
* TrackSelection} for an audio renderer. * {@link TrackSelection} for an audio renderer.
* *
* @param groups The {@link TrackGroupArray} mapped to the renderer. * @param groups The {@link TrackGroupArray} mapped to the renderer.
* @param formatSupports The result of {@link RendererCapabilities#supportsFormat} for each mapped * @param formatSupports The result of {@link RendererCapabilities#supportsFormat} for each mapped
...@@ -1394,8 +1628,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -1394,8 +1628,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// Text track selection implementation. // Text track selection implementation.
/** /**
* Called by {@link #selectAllTracks(MappedTrackInfo, int[][][], int[])} to create a {@link * Called by {@link #selectAllTracks(MappedTrackInfo, int[][][], int[], Parameters)} to create a
* TrackSelection} for a text renderer. * {@link TrackSelection} for a text renderer.
* *
* @param groups The {@link TrackGroupArray} mapped to the renderer. * @param groups The {@link TrackGroupArray} mapped to the renderer.
* @param formatSupport The result of {@link RendererCapabilities#supportsFormat} for each mapped * @param formatSupport The result of {@link RendererCapabilities#supportsFormat} for each mapped
...@@ -1466,8 +1700,8 @@ public class DefaultTrackSelector extends MappingTrackSelector { ...@@ -1466,8 +1700,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// General track selection methods. // General track selection methods.
/** /**
* Called by {@link #selectAllTracks(MappedTrackInfo, int[][][], int[])} to create a {@link * Called by {@link #selectAllTracks(MappedTrackInfo, int[][][], int[], Parameters)} to create a
* TrackSelection} for a renderer whose type is neither video, audio or text. * {@link TrackSelection} for a renderer whose type is neither video, audio or text.
* *
* @param trackType The type of the renderer. * @param trackType The type of the renderer.
* @param groups The {@link TrackGroupArray} mapped to the renderer. * @param groups The {@link TrackGroupArray} mapped to the renderer.
......
...@@ -25,6 +25,8 @@ import static org.mockito.Mockito.verify; ...@@ -25,6 +25,8 @@ import static org.mockito.Mockito.verify;
import static org.mockito.MockitoAnnotations.initMocks; import static org.mockito.MockitoAnnotations.initMocks;
import android.os.Parcel; import android.os.Parcel;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
...@@ -120,8 +122,18 @@ public final class DefaultTrackSelectorTest { ...@@ -120,8 +122,18 @@ public final class DefaultTrackSelectorTest {
/** Tests {@link Parameters} {@link android.os.Parcelable} implementation. */ /** Tests {@link Parameters} {@link android.os.Parcelable} implementation. */
@Test @Test
public void testParametersParcelable() { public void testParametersParcelable() {
SparseArray<Map<TrackGroupArray, SelectionOverride>> selectionOverrides = new SparseArray<>();
Map<TrackGroupArray, SelectionOverride> videoOverrides = new HashMap<>();
videoOverrides.put(new TrackGroupArray(VIDEO_TRACK_GROUP), new SelectionOverride(0, 1));
selectionOverrides.put(2, videoOverrides);
SparseBooleanArray rendererDisabledFlags = new SparseBooleanArray();
rendererDisabledFlags.put(3, true);
Parameters parametersToParcel = Parameters parametersToParcel =
new Parameters( new Parameters(
selectionOverrides,
rendererDisabledFlags,
/* preferredAudioLanguage= */ "en", /* preferredAudioLanguage= */ "en",
/* preferredTextLanguage= */ "de", /* preferredTextLanguage= */ "de",
/* selectUndeterminedTextLanguage= */ false, /* selectUndeterminedTextLanguage= */ false,
...@@ -136,7 +148,8 @@ public final class DefaultTrackSelectorTest { ...@@ -136,7 +148,8 @@ public final class DefaultTrackSelectorTest {
/* exceedRendererCapabilitiesIfNecessary= */ true, /* exceedRendererCapabilitiesIfNecessary= */ true,
/* viewportWidth= */ 4, /* viewportWidth= */ 4,
/* viewportHeight= */ 5, /* viewportHeight= */ 5,
/* viewportOrientationMayChange= */ false); /* viewportOrientationMayChange= */ false,
/* tunnelingAudioSessionId= */ C.AUDIO_SESSION_ID_UNSET);
Parcel parcel = Parcel.obtain(); Parcel parcel = Parcel.obtain();
parametersToParcel.writeToParcel(parcel, 0); parametersToParcel.writeToParcel(parcel, 0);
...@@ -170,7 +183,10 @@ public final class DefaultTrackSelectorTest { ...@@ -170,7 +183,10 @@ public final class DefaultTrackSelectorTest {
@Test @Test
public void testSelectTracksWithNullOverride() throws ExoPlaybackException { public void testSelectTracksWithNullOverride() throws ExoPlaybackException {
DefaultTrackSelector trackSelector = new DefaultTrackSelector(); DefaultTrackSelector trackSelector = new DefaultTrackSelector();
trackSelector.setSelectionOverride(0, new TrackGroupArray(VIDEO_TRACK_GROUP), null); trackSelector.setParameters(
trackSelector
.buildUponParameters()
.setSelectionOverride(0, new TrackGroupArray(VIDEO_TRACK_GROUP), null));
TrackSelectorResult result = trackSelector.selectTracks(RENDERER_CAPABILITIES, TRACK_GROUPS); TrackSelectorResult result = trackSelector.selectTracks(RENDERER_CAPABILITIES, TRACK_GROUPS);
assertTrackSelections(result, new TrackSelection[] {null, TRACK_SELECTIONS[1]}); assertTrackSelections(result, new TrackSelection[] {null, TRACK_SELECTIONS[1]});
assertThat(result.rendererConfigurations) assertThat(result.rendererConfigurations)
...@@ -181,8 +197,11 @@ public final class DefaultTrackSelectorTest { ...@@ -181,8 +197,11 @@ public final class DefaultTrackSelectorTest {
@Test @Test
public void testSelectTracksWithClearedNullOverride() throws ExoPlaybackException { public void testSelectTracksWithClearedNullOverride() throws ExoPlaybackException {
DefaultTrackSelector trackSelector = new DefaultTrackSelector(); DefaultTrackSelector trackSelector = new DefaultTrackSelector();
trackSelector.setSelectionOverride(0, new TrackGroupArray(VIDEO_TRACK_GROUP), null); trackSelector.setParameters(
trackSelector.clearSelectionOverride(0, new TrackGroupArray(VIDEO_TRACK_GROUP)); trackSelector
.buildUponParameters()
.setSelectionOverride(0, new TrackGroupArray(VIDEO_TRACK_GROUP), null)
.clearSelectionOverride(0, new TrackGroupArray(VIDEO_TRACK_GROUP)));
TrackSelectorResult result = trackSelector.selectTracks(RENDERER_CAPABILITIES, TRACK_GROUPS); TrackSelectorResult result = trackSelector.selectTracks(RENDERER_CAPABILITIES, TRACK_GROUPS);
assertTrackSelections(result, TRACK_SELECTIONS); assertTrackSelections(result, TRACK_SELECTIONS);
assertThat(result.rendererConfigurations) assertThat(result.rendererConfigurations)
...@@ -193,7 +212,10 @@ public final class DefaultTrackSelectorTest { ...@@ -193,7 +212,10 @@ public final class DefaultTrackSelectorTest {
@Test @Test
public void testSelectTracksWithNullOverrideForDifferentTracks() throws ExoPlaybackException { public void testSelectTracksWithNullOverrideForDifferentTracks() throws ExoPlaybackException {
DefaultTrackSelector trackSelector = new DefaultTrackSelector(); DefaultTrackSelector trackSelector = new DefaultTrackSelector();
trackSelector.setSelectionOverride(0, new TrackGroupArray(VIDEO_TRACK_GROUP), null); trackSelector.setParameters(
trackSelector
.buildUponParameters()
.setSelectionOverride(0, new TrackGroupArray(VIDEO_TRACK_GROUP), null));
TrackSelectorResult result = TrackSelectorResult result =
trackSelector.selectTracks( trackSelector.selectTracks(
RENDERER_CAPABILITIES, RENDERER_CAPABILITIES,
...@@ -207,7 +229,7 @@ public final class DefaultTrackSelectorTest { ...@@ -207,7 +229,7 @@ public final class DefaultTrackSelectorTest {
@Test @Test
public void testSelectTracksWithDisabledRenderer() throws ExoPlaybackException { public void testSelectTracksWithDisabledRenderer() throws ExoPlaybackException {
DefaultTrackSelector trackSelector = new DefaultTrackSelector(); DefaultTrackSelector trackSelector = new DefaultTrackSelector();
trackSelector.setRendererDisabled(1, true); trackSelector.setParameters(trackSelector.buildUponParameters().setRendererDisabled(1, true));
TrackSelectorResult result = trackSelector.selectTracks(RENDERER_CAPABILITIES, TRACK_GROUPS); TrackSelectorResult result = trackSelector.selectTracks(RENDERER_CAPABILITIES, TRACK_GROUPS);
assertTrackSelections(result, new TrackSelection[] {TRACK_SELECTIONS[0], null}); assertTrackSelections(result, new TrackSelection[] {TRACK_SELECTIONS[0], null});
assertThat(new RendererConfiguration[] {DEFAULT, null}) assertThat(new RendererConfiguration[] {DEFAULT, null})
...@@ -218,8 +240,11 @@ public final class DefaultTrackSelectorTest { ...@@ -218,8 +240,11 @@ public final class DefaultTrackSelectorTest {
@Test @Test
public void testSelectTracksWithClearedDisabledRenderer() throws ExoPlaybackException { public void testSelectTracksWithClearedDisabledRenderer() throws ExoPlaybackException {
DefaultTrackSelector trackSelector = new DefaultTrackSelector(); DefaultTrackSelector trackSelector = new DefaultTrackSelector();
trackSelector.setRendererDisabled(1, true); trackSelector.setParameters(
trackSelector.setRendererDisabled(1, false); trackSelector
.buildUponParameters()
.setRendererDisabled(1, true)
.setRendererDisabled(1, false));
TrackSelectorResult result = trackSelector.selectTracks(RENDERER_CAPABILITIES, TRACK_GROUPS); TrackSelectorResult result = trackSelector.selectTracks(RENDERER_CAPABILITIES, TRACK_GROUPS);
assertTrackSelections(result, TRACK_SELECTIONS); assertTrackSelections(result, TRACK_SELECTIONS);
assertThat(new RendererConfiguration[] {DEFAULT, DEFAULT}) assertThat(new RendererConfiguration[] {DEFAULT, DEFAULT})
...@@ -241,7 +266,7 @@ public final class DefaultTrackSelectorTest { ...@@ -241,7 +266,7 @@ public final class DefaultTrackSelectorTest {
@Test @Test
public void testSelectTracksWithDisabledNoSampleRenderer() throws ExoPlaybackException { public void testSelectTracksWithDisabledNoSampleRenderer() throws ExoPlaybackException {
DefaultTrackSelector trackSelector = new DefaultTrackSelector(); DefaultTrackSelector trackSelector = new DefaultTrackSelector();
trackSelector.setRendererDisabled(1, true); trackSelector.setParameters(trackSelector.buildUponParameters().setRendererDisabled(1, true));
TrackSelectorResult result = TrackSelectorResult result =
trackSelector.selectTracks(RENDERER_CAPABILITIES_WITH_NO_SAMPLE_RENDERER, TRACK_GROUPS); trackSelector.selectTracks(RENDERER_CAPABILITIES_WITH_NO_SAMPLE_RENDERER, TRACK_GROUPS);
assertTrackSelections(result, TRACK_SELECTIONS_WITH_NO_SAMPLE_RENDERER); assertTrackSelections(result, TRACK_SELECTIONS_WITH_NO_SAMPLE_RENDERER);
......
...@@ -397,7 +397,8 @@ public final class DashTestRunner { ...@@ -397,7 +397,8 @@ public final class DashTestRunner {
protected TrackSelection[] selectAllTracks( protected TrackSelection[] selectAllTracks(
MappedTrackInfo mappedTrackInfo, MappedTrackInfo mappedTrackInfo,
int[][][] rendererFormatSupports, int[][][] rendererFormatSupports,
int[] rendererMixedMimeTypeAdaptationSupports) int[] rendererMixedMimeTypeAdaptationSupports,
Parameters parameters)
throws ExoPlaybackException { throws ExoPlaybackException {
Assertions.checkState( Assertions.checkState(
mappedTrackInfo.getRendererType(VIDEO_RENDERER_INDEX) == C.TRACK_TYPE_VIDEO); mappedTrackInfo.getRendererType(VIDEO_RENDERER_INDEX) == C.TRACK_TYPE_VIDEO);
......
...@@ -33,6 +33,7 @@ import com.google.android.exoplayer2.testutil.ActionSchedule.ActionNode; ...@@ -33,6 +33,7 @@ import com.google.android.exoplayer2.testutil.ActionSchedule.ActionNode;
import com.google.android.exoplayer2.testutil.ActionSchedule.PlayerRunnable; import com.google.android.exoplayer2.testutil.ActionSchedule.PlayerRunnable;
import com.google.android.exoplayer2.testutil.ActionSchedule.PlayerTarget; import com.google.android.exoplayer2.testutil.ActionSchedule.PlayerTarget;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.Parameters;
import com.google.android.exoplayer2.util.HandlerWrapper; import com.google.android.exoplayer2.util.HandlerWrapper;
/** /**
...@@ -219,7 +220,10 @@ public abstract class Action { ...@@ -219,7 +220,10 @@ public abstract class Action {
} }
/** Calls {@link DefaultTrackSelector#setRendererDisabled(int, boolean)}. */ /**
* Updates the {@link Parameters} of a {@link DefaultTrackSelector} to specify whether the
* renderer at a given index should be disabled.
*/
public static final class SetRendererDisabled extends Action { public static final class SetRendererDisabled extends Action {
private final int rendererIndex; private final int rendererIndex;
...@@ -239,7 +243,8 @@ public abstract class Action { ...@@ -239,7 +243,8 @@ public abstract class Action {
@Override @Override
protected void doActionImpl( protected void doActionImpl(
SimpleExoPlayer player, DefaultTrackSelector trackSelector, Surface surface) { SimpleExoPlayer player, DefaultTrackSelector trackSelector, Surface surface) {
trackSelector.setRendererDisabled(rendererIndex, disabled); trackSelector.setParameters(
trackSelector.buildUponParameters().setRendererDisabled(rendererIndex, disabled));
} }
} }
......
...@@ -48,7 +48,8 @@ public class FakeTrackSelector extends DefaultTrackSelector { ...@@ -48,7 +48,8 @@ public class FakeTrackSelector extends DefaultTrackSelector {
protected TrackSelection[] selectAllTracks( protected TrackSelection[] selectAllTracks(
MappedTrackInfo mappedTrackInfo, MappedTrackInfo mappedTrackInfo,
int[][][] rendererFormatSupports, int[][][] rendererFormatSupports,
int[] rendererMixedMimeTypeAdaptationSupports) int[] rendererMixedMimeTypeAdaptationSupports,
Parameters params)
throws ExoPlaybackException { throws ExoPlaybackException {
TrackSelection[] selections = new TrackSelection[mappedTrackInfo.length]; TrackSelection[] selections = new TrackSelection[mappedTrackInfo.length];
for (int i = 0; i < mappedTrackInfo.length; i++) { for (int i = 0; i < mappedTrackInfo.length; i++) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment