Add the TrackSelectionPolicy to decouple track "assignment" and selection

Allows the user to provide their own criteria for selecting one of the available tracks
without having to reimplement the track assignment logic, i.e. linking type x tracks with
type x renderer.
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=122422087
This commit is contained in:
aquilescanta 2016-05-16 08:24:58 -07:00 committed by Oliver Woodman
parent 23769fe82e
commit 2f4bfc3f5f
7 changed files with 157 additions and 32 deletions

View File

@ -17,6 +17,7 @@ package com.google.android.exoplayer.demo.player;
import com.google.android.exoplayer.C; import com.google.android.exoplayer.C;
import com.google.android.exoplayer.CodecCounters; import com.google.android.exoplayer.CodecCounters;
import com.google.android.exoplayer.DefaultTrackSelectionPolicy;
import com.google.android.exoplayer.DefaultTrackSelector; import com.google.android.exoplayer.DefaultTrackSelector;
import com.google.android.exoplayer.DefaultTrackSelector.TrackInfo; import com.google.android.exoplayer.DefaultTrackSelector.TrackInfo;
import com.google.android.exoplayer.ExoPlaybackException; import com.google.android.exoplayer.ExoPlaybackException;
@ -170,7 +171,7 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
renderers = renderersList.toArray(new TrackRenderer[renderersList.size()]); renderers = renderersList.toArray(new TrackRenderer[renderersList.size()]);
// Build the player and associated objects. // Build the player and associated objects.
trackSelector = new DefaultTrackSelector(mainHandler, this); trackSelector = new DefaultTrackSelector(mainHandler, this, new DefaultTrackSelectionPolicy());
player = ExoPlayer.Factory.newInstance(renderers, trackSelector, 1000, 5000); player = ExoPlayer.Factory.newInstance(renderers, trackSelector, 1000, 5000);
player.addListener(this); player.addListener(this);
playerControl = new PlayerControl(player); playerControl = new PlayerControl(player);

View File

@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer.ext.flac; package com.google.android.exoplayer.ext.flac;
import com.google.android.exoplayer.DefaultTrackSelectionPolicy;
import com.google.android.exoplayer.DefaultTrackSelector; import com.google.android.exoplayer.DefaultTrackSelector;
import com.google.android.exoplayer.ExoPlaybackException; import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.ExoPlayer;
@ -76,7 +77,8 @@ public class FlacPlaybackTest extends InstrumentationTestCase {
public void run() { public void run() {
Looper.prepare(); Looper.prepare();
LibflacAudioTrackRenderer audioRenderer = new LibflacAudioTrackRenderer(); LibflacAudioTrackRenderer audioRenderer = new LibflacAudioTrackRenderer();
DefaultTrackSelector trackSelector = new DefaultTrackSelector(null, null); DefaultTrackSelector trackSelector = new DefaultTrackSelector(null, null,
new DefaultTrackSelectionPolicy());
player = ExoPlayer.Factory.newInstance(new TrackRenderer[] {audioRenderer}, trackSelector); player = ExoPlayer.Factory.newInstance(new TrackRenderer[] {audioRenderer}, trackSelector);
player.addListener(this); player.addListener(this);
ExtractorSampleSource sampleSource = new ExtractorSampleSource( ExtractorSampleSource sampleSource = new ExtractorSampleSource(

View File

@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer.ext.opus; package com.google.android.exoplayer.ext.opus;
import com.google.android.exoplayer.DefaultTrackSelectionPolicy;
import com.google.android.exoplayer.DefaultTrackSelector; import com.google.android.exoplayer.DefaultTrackSelector;
import com.google.android.exoplayer.ExoPlaybackException; import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.ExoPlayer;
@ -76,7 +77,8 @@ public class OpusPlaybackTest extends InstrumentationTestCase {
public void run() { public void run() {
Looper.prepare(); Looper.prepare();
LibopusAudioTrackRenderer audioRenderer = new LibopusAudioTrackRenderer(); LibopusAudioTrackRenderer audioRenderer = new LibopusAudioTrackRenderer();
DefaultTrackSelector trackSelector = new DefaultTrackSelector(null, null); DefaultTrackSelector trackSelector = new DefaultTrackSelector(null, null,
new DefaultTrackSelectionPolicy());
player = ExoPlayer.Factory.newInstance(new TrackRenderer[] {audioRenderer}, trackSelector); player = ExoPlayer.Factory.newInstance(new TrackRenderer[] {audioRenderer}, trackSelector);
player.addListener(this); player.addListener(this);
ExtractorSampleSource sampleSource = new ExtractorSampleSource( ExtractorSampleSource sampleSource = new ExtractorSampleSource(

View File

@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer.ext.vp9; package com.google.android.exoplayer.ext.vp9;
import com.google.android.exoplayer.DefaultTrackSelectionPolicy;
import com.google.android.exoplayer.DefaultTrackSelector; import com.google.android.exoplayer.DefaultTrackSelector;
import com.google.android.exoplayer.ExoPlaybackException; import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.ExoPlayer; import com.google.android.exoplayer.ExoPlayer;
@ -92,7 +93,8 @@ public class VpxPlaybackTest extends InstrumentationTestCase {
public void run() { public void run() {
Looper.prepare(); Looper.prepare();
LibvpxVideoTrackRenderer videoRenderer = new LibvpxVideoTrackRenderer(true); LibvpxVideoTrackRenderer videoRenderer = new LibvpxVideoTrackRenderer(true);
DefaultTrackSelector trackSelector = new DefaultTrackSelector(null, null); DefaultTrackSelector trackSelector = new DefaultTrackSelector(null, null,
new DefaultTrackSelectionPolicy());
player = ExoPlayer.Factory.newInstance(new TrackRenderer[] {videoRenderer}, trackSelector); player = ExoPlayer.Factory.newInstance(new TrackRenderer[] {videoRenderer}, trackSelector);
player.addListener(this); player.addListener(this);
ExtractorSampleSource sampleSource = new ExtractorSampleSource( ExtractorSampleSource sampleSource = new ExtractorSampleSource(

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2014 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.exoplayer;
/**
* A {@link TrackSelectionPolicy} that allows configuration of common parameters.
*/
public class DefaultTrackSelectionPolicy extends TrackSelectionPolicy {
@Override
public TrackSelection[] selectTracks(TrackRenderer[] renderers,
TrackGroupArray[] rendererTrackGroupArrays, int[][][] rendererFormatSupports) {
// Make a track selection for each renderer.
TrackSelection[] rendererTrackSelections = new TrackSelection[renderers.length];
for (int i = 0; i < renderers.length; i++) {
rendererTrackSelections[i] = selectTracksForRenderer(rendererTrackGroupArrays[i],
rendererFormatSupports[i]);
}
return rendererTrackSelections;
}
private static TrackSelection selectTracksForRenderer(TrackGroupArray trackGroups,
int[][] formatSupport) {
// TODO: Allow more specific track selection parameters.
for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
TrackGroup trackGroup = trackGroups.get(groupIndex);
int[] trackFormatSupport = formatSupport[groupIndex];
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
if ((trackFormatSupport[trackIndex] & TrackRenderer.FORMAT_SUPPORT_MASK)
== TrackRenderer.FORMAT_HANDLED) {
return new TrackSelection(groupIndex, trackIndex);
}
}
}
return null;
}
}

View File

@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer; package com.google.android.exoplayer;
import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
import android.os.Handler; import android.os.Handler;
@ -29,7 +30,8 @@ import java.util.Map;
/** /**
* A {@link TrackSelector} suitable for a wide range of use cases. * A {@link TrackSelector} suitable for a wide range of use cases.
*/ */
public class DefaultTrackSelector extends TrackSelector { public class DefaultTrackSelector extends TrackSelector implements
TrackSelectionPolicy.InvalidationListener{
/** /**
* Interface definition for a callback to be notified of {@link DefaultTrackSelector} events. * Interface definition for a callback to be notified of {@link DefaultTrackSelector} events.
@ -49,6 +51,7 @@ public class DefaultTrackSelector extends TrackSelector {
private final EventListener eventListener; private final EventListener eventListener;
private final SparseArray<Map<TrackGroupArray, TrackSelection>> trackSelectionOverrides; private final SparseArray<Map<TrackGroupArray, TrackSelection>> trackSelectionOverrides;
private final SparseBooleanArray rendererDisabledFlags; private final SparseBooleanArray rendererDisabledFlags;
private final TrackSelectionPolicy trackSelectionPolicy;
private TrackInfo activeTrackInfo; private TrackInfo activeTrackInfo;
@ -56,12 +59,16 @@ public class DefaultTrackSelector extends TrackSelector {
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be * @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
* null if delivery of events is not required. * null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required. * @param eventListener A listener of events. May be null if delivery of events is not required.
* @param trackSelectionPolicy Defines the policy for track selection.
*/ */
public DefaultTrackSelector(Handler eventHandler, EventListener eventListener) { public DefaultTrackSelector(Handler eventHandler, EventListener eventListener,
TrackSelectionPolicy trackSelectionPolicy) {
this.eventHandler = eventHandler; this.eventHandler = eventHandler;
this.eventListener = eventListener; this.eventListener = eventListener;
trackSelectionOverrides = new SparseArray<>(); trackSelectionOverrides = new SparseArray<>();
rendererDisabledFlags = new SparseBooleanArray(); rendererDisabledFlags = new SparseBooleanArray();
this.trackSelectionPolicy = Assertions.checkNotNull(trackSelectionPolicy);
this.trackSelectionPolicy.init(this);
} }
/** /**
@ -189,6 +196,14 @@ public class DefaultTrackSelector extends TrackSelector {
invalidate(); invalidate();
} }
/**
* Invoked when the {@link TrackSelectionPolicy} has changed.
*/
@Override
public void invalidatePolicySelections() {
invalidate();
}
// TrackSelector implementation. // TrackSelector implementation.
@Override @Override
@ -244,12 +259,21 @@ public class DefaultTrackSelector extends TrackSelector {
TrackGroupArray unassociatedTrackGroupArray = new TrackGroupArray( TrackGroupArray unassociatedTrackGroupArray = new TrackGroupArray(
Arrays.copyOf(rendererTrackGroups[renderers.length], unassociatedTrackGroupCount)); Arrays.copyOf(rendererTrackGroups[renderers.length], unassociatedTrackGroupCount));
// Make a track selection for each renderer. TrackSelection[] rendererTrackSelections = trackSelectionPolicy.selectTracks(renderers,
TrackSelection[] rendererTrackSelections = new TrackSelection[renderers.length]; rendererTrackGroupArrays, rendererFormatSupports);
// Apply track disabling and overriding.
for (int i = 0; i < renderers.length; i++) { for (int i = 0; i < renderers.length; i++) {
rendererTrackSelections[i] = rendererDisabledFlags.get(i) ? null if (rendererDisabledFlags.get(i)) {
: selectTracksForRenderer(rendererTrackGroupArrays[i], rendererFormatSupports[i], rendererTrackSelections[i] = null;
trackSelectionOverrides.get(i)); } else {
Map<TrackGroupArray, TrackSelection> override = trackSelectionOverrides.get(i);
TrackSelection overrideSelection = override == null ? null
: override.get(rendererTrackGroupArrays[i]);
if (overrideSelection != null) {
rendererTrackSelections[i] = overrideSelection;
}
}
} }
// The track selections above index into the track group arrays associated to each renderer, // The track selections above index into the track group arrays associated to each renderer,
@ -349,27 +373,6 @@ public class DefaultTrackSelector extends TrackSelector {
return mixedMimeTypeAdaptationSupport; return mixedMimeTypeAdaptationSupport;
} }
private static TrackSelection selectTracksForRenderer(TrackGroupArray trackGroups,
int[][] formatSupport, Map<TrackGroupArray, TrackSelection> overrides) {
if (overrides != null && overrides.containsKey(trackGroups)) {
return overrides.get(trackGroups);
}
// TODO[REFACTOR]: Implement real default selection logic here.
for (int groupIndex = 0; groupIndex < trackGroups.length; groupIndex++) {
TrackGroup trackGroup = trackGroups.get(groupIndex);
int[] trackFormatSupport = formatSupport[groupIndex];
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
if ((trackFormatSupport[trackIndex] & TrackRenderer.FORMAT_SUPPORT_MASK)
== TrackRenderer.FORMAT_HANDLED) {
return new TrackSelection(groupIndex, trackIndex);
}
}
}
return null;
}
/** /**
* Finds the specified group in a group array, using referential equality. * Finds the specified group in a group array, using referential equality.
* *

View File

@ -0,0 +1,63 @@
/*
* Copyright (C) 2016 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.exoplayer;
/**
* Defines a policy for selecting the track rendered by each {@link TrackRenderer}.
*/
public abstract class TrackSelectionPolicy {
/**
* Notified when selection parameters have changed.
*/
/* package */ interface InvalidationListener {
/**
* Invoked by a {@link TrackSelectionPolicy} when previous selections are invalidated.
*/
void invalidatePolicySelections();
}
private InvalidationListener listener;
/**
* Must be invoked by subclasses when a selection parameter has changed, invalidating previous
* selections.
*/
protected void invalidate() {
listener.invalidatePolicySelections();
}
/**
* Given an array of {@link TrackRenderer}s and a set of {@link TrackGroup}s assigned to each of
* them, provides a {@link TrackSelection} per renderer.
*
* @param renderers The available {@link TrackRenderer}s.
* @param rendererTrackGroupArrays An array of {@link TrackGroupArray}s where each entry
* corresponds to the {@link TrackRenderer} of equal index in {@code renderers}.
* @param rendererFormatSupports Maps every available track to a specific level of support as
* defined by the {@link TrackRenderer} FORMAT_* constants.
*/
/* package */ abstract TrackSelection[] selectTracks(TrackRenderer[] renderers,
TrackGroupArray[] rendererTrackGroupArrays, int[][][] rendererFormatSupports);
/* package */ void init(InvalidationListener listener) {
this.listener = listener;
}
}