Allow passing BandwidthMeter to TrackSelector and TrackSelection.Factory.

This enabled the player to specify the bandwidth meter after the track
selector and the track selection factory have been created.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=199286400
This commit is contained in:
tonihei 2018-06-05 06:20:30 -07:00 committed by Oliver Woodman
parent 23afdd6f66
commit 1773708fe4
11 changed files with 323 additions and 78 deletions

View File

@ -15,6 +15,8 @@
* Add support for lazy preparation of playlist media sources in * Add support for lazy preparation of playlist media sources in
`ConcatenatingMediaSource` `ConcatenatingMediaSource`
([#3972](https://github.com/google/ExoPlayer/issues/3972)). ([#3972](https://github.com/google/ExoPlayer/issues/3972)).
* Pass `BandwidthMeter` to `TrackSelection.Factory` which can be used to obtain
bandwidth estimates in the future. Always null at the moment.
* HLS: * HLS:
* Allow injection of custom playlist trackers. * Allow injection of custom playlist trackers.
* Add method to `BandwidthMeter` to return the `TransferListener` used to gather * Add method to `BandwidthMeter` to return the `TransferListener` used to gather

View File

@ -162,7 +162,7 @@ import java.util.Collections;
enabledRenderers = new Renderer[0]; enabledRenderers = new Renderer[0];
window = new Timeline.Window(); window = new Timeline.Window();
period = new Timeline.Period(); period = new Timeline.Period();
trackSelector.init(this); trackSelector.init(/* listener= */ this, /* bandwidthMeter= */ null);
// Note: The documentation for Process.THREAD_PRIORITY_AUDIO that states "Applications can // Note: The documentation for Process.THREAD_PRIORITY_AUDIO that states "Applications can
// not normally change to this priority" is incorrect. // not normally change to this priority" is incorrect.

View File

@ -21,6 +21,7 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.chunk.MediaChunk; import com.google.android.exoplayer2.source.chunk.MediaChunk;
import com.google.android.exoplayer2.upstream.BandwidthMeter; import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Clock; import com.google.android.exoplayer2.util.Clock;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.util.List; import java.util.List;
@ -137,11 +138,15 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
} }
@Override @Override
public AdaptiveTrackSelection createTrackSelection(TrackGroup group, int... tracks) { public AdaptiveTrackSelection createTrackSelection(
TrackGroup group, @Nullable BandwidthMeter bandwidthMeter, int... tracks) {
if (this.bandwidthMeter != null) {
bandwidthMeter = this.bandwidthMeter;
}
return new AdaptiveTrackSelection( return new AdaptiveTrackSelection(
group, group,
tracks, tracks,
bandwidthMeter, Assertions.checkNotNull(bandwidthMeter),
minDurationForQualityIncreaseMs, minDurationForQualityIncreaseMs,
maxDurationForQualityDecreaseMs, maxDurationForQualityDecreaseMs,
minDurationToRetainAfterDiscardMs, minDurationToRetainAfterDiscardMs,

View File

@ -1206,7 +1206,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
rendererTrackSelections[i] = rendererTrackSelections[i] =
Assertions.checkNotNull(adaptiveTrackSelectionFactory) Assertions.checkNotNull(adaptiveTrackSelectionFactory)
.createTrackSelection( .createTrackSelection(
rendererTrackGroups.get(override.groupIndex), override.tracks); rendererTrackGroups.get(override.groupIndex),
getBandwidthMeter(),
override.tracks);
} }
} }
} }
@ -1352,7 +1354,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
formatSupports, formatSupports,
mixedMimeTypeAdaptationSupports, mixedMimeTypeAdaptationSupports,
params, params,
adaptiveTrackSelectionFactory); adaptiveTrackSelectionFactory,
getBandwidthMeter());
} }
if (selection == null) { if (selection == null) {
selection = selectFixedVideoTrack(groups, formatSupports, params); selection = selectFixedVideoTrack(groups, formatSupports, params);
@ -1365,7 +1368,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
int[][] formatSupport, int[][] formatSupport,
int mixedMimeTypeAdaptationSupports, int mixedMimeTypeAdaptationSupports,
Parameters params, Parameters params,
TrackSelection.Factory adaptiveTrackSelectionFactory) TrackSelection.Factory adaptiveTrackSelectionFactory,
@Nullable BandwidthMeter bandwidthMeter)
throws ExoPlaybackException { throws ExoPlaybackException {
int requiredAdaptiveSupport = params.allowNonSeamlessAdaptiveness int requiredAdaptiveSupport = params.allowNonSeamlessAdaptiveness
? (RendererCapabilities.ADAPTIVE_NOT_SEAMLESS | RendererCapabilities.ADAPTIVE_SEAMLESS) ? (RendererCapabilities.ADAPTIVE_NOT_SEAMLESS | RendererCapabilities.ADAPTIVE_SEAMLESS)
@ -1381,7 +1385,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
params.viewportOrientationMayChange); params.viewportOrientationMayChange);
if (adaptiveTracks.length > 0) { if (adaptiveTracks.length > 0) {
return Assertions.checkNotNull(adaptiveTrackSelectionFactory) return Assertions.checkNotNull(adaptiveTrackSelectionFactory)
.createTrackSelection(group, adaptiveTracks); .createTrackSelection(group, bandwidthMeter, adaptiveTracks);
} }
} }
return null; return null;
@ -1600,8 +1604,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
getAdaptiveAudioTracks( getAdaptiveAudioTracks(
selectedGroup, formatSupports[selectedGroupIndex], params.allowMixedMimeAdaptiveness); selectedGroup, formatSupports[selectedGroupIndex], params.allowMixedMimeAdaptiveness);
if (adaptiveTracks.length > 0) { if (adaptiveTracks.length > 0) {
return adaptiveTrackSelectionFactory.createTrackSelection(selectedGroup, return adaptiveTrackSelectionFactory
adaptiveTracks); .createTrackSelection(selectedGroup, getBandwidthMeter(), adaptiveTracks);
} }
} }
return new FixedTrackSelection(selectedGroup, selectedTrackIndex); return new FixedTrackSelection(selectedGroup, selectedTrackIndex);

View File

@ -18,6 +18,7 @@ package com.google.android.exoplayer2.trackselection;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
/** /**
@ -48,7 +49,8 @@ public final class FixedTrackSelection extends BaseTrackSelection {
} }
@Override @Override
public FixedTrackSelection createTrackSelection(TrackGroup group, int... tracks) { public FixedTrackSelection createTrackSelection(
TrackGroup group, @Nullable BandwidthMeter bandwidthMeter, int... tracks) {
Assertions.checkArgument(tracks.length == 1); Assertions.checkArgument(tracks.length == 1);
return new FixedTrackSelection(group, tracks[0], reason, data); return new FixedTrackSelection(group, tracks[0], reason, data);
} }

View File

@ -19,6 +19,7 @@ import android.os.SystemClock;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import java.util.Random; import java.util.Random;
/** /**
@ -45,7 +46,8 @@ public final class RandomTrackSelection extends BaseTrackSelection {
} }
@Override @Override
public RandomTrackSelection createTrackSelection(TrackGroup group, int... tracks) { public RandomTrackSelection createTrackSelection(
TrackGroup group, @Nullable BandwidthMeter bandwidthMeter, int... tracks) {
return new RandomTrackSelection(group, tracks, random); return new RandomTrackSelection(group, tracks, random);
} }
} }

View File

@ -20,6 +20,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.source.TrackGroup; import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.chunk.MediaChunk; import com.google.android.exoplayer2.source.chunk.MediaChunk;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import java.util.List; import java.util.List;
/** /**
@ -36,16 +37,29 @@ public interface TrackSelection {
*/ */
interface Factory { interface Factory {
/**
* @deprecated Use and implement {@link
* #createTrackSelection(TrackGroup, BandwidthMeter, int...)} instead.
*/
@Deprecated
default TrackSelection createTrackSelection(TrackGroup group, int... tracks) {
return createTrackSelection(group, /* bandwidthMeter= */ null, tracks);
}
/** /**
* Creates a new selection. * Creates a new selection.
* *
* @param group The {@link TrackGroup}. Must not be null. * @param group The {@link TrackGroup}. Must not be null.
* @param bandwidthMeter A {@link BandwidthMeter} which can be used to select tracks, or null if
* no such bandwidth meter is available.
* @param tracks The indices of the selected tracks within the {@link TrackGroup}. Must not be * @param tracks The indices of the selected tracks within the {@link TrackGroup}. Must not be
* null or empty. May be in any order. * null or empty. May be in any order.
* @return The created selection. * @return The created selection.
*/ */
TrackSelection createTrackSelection(TrackGroup group, int... tracks); default TrackSelection createTrackSelection(
TrackGroup group, @Nullable BandwidthMeter bandwidthMeter, int... tracks) {
return createTrackSelection(group, tracks);
}
} }
/** /**

View File

@ -22,6 +22,7 @@ import com.google.android.exoplayer2.Renderer;
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.source.TrackGroupArray; import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
/** /**
* The component of an {@link ExoPlayer} responsible for selecting tracks to be consumed by each of * The component of an {@link ExoPlayer} responsible for selecting tracks to be consumed by each of
@ -29,48 +30,52 @@ import com.google.android.exoplayer2.source.TrackGroupArray;
* suitable for most use cases. * suitable for most use cases.
* *
* <h3>Interactions with the player</h3> * <h3>Interactions with the player</h3>
*
* The following interactions occur between the player and its track selector during playback. * The following interactions occur between the player and its track selector during playback.
*
* <p> * <p>
*
* <ul> * <ul>
* <li>When the player is created it will initialize the track selector by calling * <li>When the player is created it will initialize the track selector by calling {@link
* {@link #init(InvalidationListener)}.</li> * #init(InvalidationListener, BandwidthMeter)}.
* <li>When the player needs to make a track selection it will call * <li>When the player needs to make a track selection it will call {@link
* {@link #selectTracks(RendererCapabilities[], TrackGroupArray)}. This typically occurs at the * #selectTracks(RendererCapabilities[], TrackGroupArray)}. This typically occurs at the start
* start of playback, when the player starts to buffer a new period of the media being played, * of playback, when the player starts to buffer a new period of the media being played, and
* and when the track selector invalidates its previous selections.</li> * when the track selector invalidates its previous selections.
* <li>The player may perform a track selection well in advance of the selected tracks becoming * <li>The player may perform a track selection well in advance of the selected tracks becoming
* active, where active is defined to mean that the renderers are actually consuming media * active, where active is defined to mean that the renderers are actually consuming media
* corresponding to the selection that was made. For example when playing media containing * corresponding to the selection that was made. For example when playing media containing
* multiple periods, the track selection for a period is made when the player starts to buffer * multiple periods, the track selection for a period is made when the player starts to buffer
* that period. Hence if the player's buffering policy is to maintain a 30 second buffer, the * that period. Hence if the player's buffering policy is to maintain a 30 second buffer, the
* selection will occur approximately 30 seconds in advance of it becoming active. In fact the * selection will occur approximately 30 seconds in advance of it becoming active. In fact the
* selection may never become active, for example if the user seeks to some other period of the * selection may never become active, for example if the user seeks to some other period of
* media during the 30 second gap. The player indicates to the track selector when a selection * the media during the 30 second gap. The player indicates to the track selector when a
* it has previously made becomes active by calling {@link #onSelectionActivated(Object)}.</li> * selection it has previously made becomes active by calling {@link
* <li>If the track selector wishes to indicate to the player that selections it has previously * #onSelectionActivated(Object)}.
* made are invalid, it can do so by calling * <li>If the track selector wishes to indicate to the player that selections it has previously
* {@link InvalidationListener#onTrackSelectionsInvalidated()} on the * made are invalid, it can do so by calling {@link
* {@link InvalidationListener} that was passed to {@link #init(InvalidationListener)}. A * InvalidationListener#onTrackSelectionsInvalidated()} on the {@link InvalidationListener}
* track selector may wish to do this if its configuration has changed, for example if it now * that was passed to {@link #init(InvalidationListener, BandwidthMeter)}. A track selector
* wishes to prefer audio tracks in a particular language. This will trigger the player to make * may wish to do this if its configuration has changed, for example if it now wishes to
* new track selections. Note that the player will have to re-buffer in the case that the new * prefer audio tracks in a particular language. This will trigger the player to make new
* track selection for the currently playing period differs from the one that was invalidated. * track selections. Note that the player will have to re-buffer in the case that the new
* </li> * track selection for the currently playing period differs from the one that was invalidated.
* </ul> * </ul>
* *
* <h3>Renderer configuration</h3> * <h3>Renderer configuration</h3>
* The {@link TrackSelectorResult} returned by *
* {@link #selectTracks(RendererCapabilities[], TrackGroupArray)} contains not only * The {@link TrackSelectorResult} returned by {@link #selectTracks(RendererCapabilities[],
* {@link TrackSelection}s for each renderer, but also {@link RendererConfiguration}s defining * TrackGroupArray)} contains not only {@link TrackSelection}s for each renderer, but also {@link
* configuration parameters that the renderers should apply when consuming the corresponding media. * RendererConfiguration}s defining configuration parameters that the renderers should apply when
* Whilst it may seem counter-intuitive for a track selector to also specify renderer configuration * consuming the corresponding media. Whilst it may seem counter-intuitive for a track selector to
* information, in practice the two are tightly bound together. It may only be possible to play a * also specify renderer configuration information, in practice the two are tightly bound together.
* certain combination tracks if the renderers are configured in a particular way. Equally, it may * It may only be possible to play a certain combination tracks if the renderers are configured in a
* only be possible to configure renderers in a particular way if certain tracks are selected. Hence * particular way. Equally, it may only be possible to configure renderers in a particular way if
* it makes sense to determined the track selection and corresponding renderer configurations in a * certain tracks are selected. Hence it makes sense to determined the track selection and
* single step. * corresponding renderer configurations in a single step.
* *
* <h3>Threading model</h3> * <h3>Threading model</h3>
*
* All calls made by the player into the track selector are on the player's internal playback * All calls made by the player into the track selector are on the player's internal playback
* thread. The track selector may call {@link InvalidationListener#onTrackSelectionsInvalidated()} * thread. The track selector may call {@link InvalidationListener#onTrackSelectionsInvalidated()}
* from any thread. * from any thread.
@ -91,15 +96,19 @@ public abstract class TrackSelector {
} }
private @Nullable InvalidationListener listener; private @Nullable InvalidationListener listener;
private @Nullable BandwidthMeter bandwidthMeter;
/** /**
* Called by the player to initialize the selector. * Called by the player to initialize the selector.
* *
* @param listener An invalidation listener that the selector can call to indicate that selections * @param listener An invalidation listener that the selector can call to indicate that selections
* it has previously made are no longer valid. * it has previously made are no longer valid.
* @param bandwidthMeter A bandwidth meter which can be used by track selections to select tracks,
* or null if no such bandwidth meter is available.
*/ */
public final void init(InvalidationListener listener) { public final void init(InvalidationListener listener, @Nullable BandwidthMeter bandwidthMeter) {
this.listener = listener; this.listener = listener;
this.bandwidthMeter = bandwidthMeter;
} }
/** /**
@ -132,4 +141,11 @@ public abstract class TrackSelector {
} }
} }
/**
* Returns a bandwidth meter which can be used by track selections to select tracks, or null if no
* such bandwidth meter is available.
*/
protected final @Nullable BandwidthMeter getBandwidthMeter() {
return bandwidthMeter;
}
} }

View File

@ -16,6 +16,10 @@
package com.google.android.exoplayer2.trackselection; package com.google.android.exoplayer2.trackselection;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks; import static org.mockito.MockitoAnnotations.initMocks;
@ -54,6 +58,23 @@ public final class AdaptiveTrackSelectionTest {
fakeClock = new FakeClock(0); fakeClock = new FakeClock(0);
} }
@Test
public void testFactoryUsesInitiallyProvidedBandwidthMeter() {
BandwidthMeter initialBandwidthMeter = mock(BandwidthMeter.class);
BandwidthMeter injectedBandwidthMeter = mock(BandwidthMeter.class);
Format format = videoFormat(/* bitrate= */ 500, /* width= */ 320, /* height= */ 240);
AdaptiveTrackSelection adaptiveTrackSelection =
new AdaptiveTrackSelection.Factory(initialBandwidthMeter)
.createTrackSelection(new TrackGroup(format), injectedBandwidthMeter, /* tracks= */ 0);
adaptiveTrackSelection.updateSelectedTrack(
/* playbackPositionUs= */ 0,
/* bufferedDurationUs= */ 0,
/* availableDurationUs= */ C.TIME_UNSET);
verify(initialBandwidthMeter, atLeastOnce()).getBitrateEstimate();
verifyZeroInteractions(injectedBandwidthMeter);
}
@Test @Test
public void testSelectInitialIndexUseMaxInitialBitrateIfNoBandwidthEstimate() { public void testSelectInitialIndexUseMaxInitialBitrateIfNoBandwidthEstimate() {
Format format1 = videoFormat(/* bitrate= */ 500, /* width= */ 320, /* height= */ 240); Format format1 = videoFormat(/* bitrate= */ 500, /* width= */ 320, /* height= */ 240);

View File

@ -19,9 +19,13 @@ import static com.google.android.exoplayer2.RendererCapabilities.FORMAT_EXCEEDS_
import static com.google.android.exoplayer2.RendererCapabilities.FORMAT_HANDLED; import static com.google.android.exoplayer2.RendererCapabilities.FORMAT_HANDLED;
import static com.google.android.exoplayer2.RendererConfiguration.DEFAULT; import static com.google.android.exoplayer2.RendererConfiguration.DEFAULT;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyVararg;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks; import static org.mockito.MockitoAnnotations.initMocks;
import android.os.Parcel; import android.os.Parcel;
@ -38,6 +42,7 @@ import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.Paramet
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector.ParametersBuilder; 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.TrackSelector.InvalidationListener; import com.google.android.exoplayer2.trackselection.TrackSelector.InvalidationListener;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -71,33 +76,33 @@ public final class DefaultTrackSelectorTest {
private static final RendererCapabilities[] RENDERER_CAPABILITIES_WITH_NO_SAMPLE_RENDERER = private static final RendererCapabilities[] RENDERER_CAPABILITIES_WITH_NO_SAMPLE_RENDERER =
new RendererCapabilities[] {VIDEO_CAPABILITIES, NO_SAMPLE_CAPABILITIES}; new RendererCapabilities[] {VIDEO_CAPABILITIES, NO_SAMPLE_CAPABILITIES};
private static final TrackGroup VIDEO_TRACK_GROUP = private static final Format VIDEO_FORMAT =
new TrackGroup( Format.createVideoSampleFormat(
Format.createVideoSampleFormat( "video",
"video", MimeTypes.VIDEO_H264,
MimeTypes.VIDEO_H264, null,
null, Format.NO_VALUE,
Format.NO_VALUE, Format.NO_VALUE,
Format.NO_VALUE, 1024,
1024, 768,
768, Format.NO_VALUE,
Format.NO_VALUE, null,
null, null);
null)); private static final Format AUDIO_FORMAT =
private static final TrackGroup AUDIO_TRACK_GROUP = Format.createAudioSampleFormat(
new TrackGroup( "audio",
Format.createAudioSampleFormat( MimeTypes.AUDIO_AAC,
"audio", null,
MimeTypes.AUDIO_AAC, Format.NO_VALUE,
null, Format.NO_VALUE,
Format.NO_VALUE, 2,
Format.NO_VALUE, 44100,
2, null,
44100, null,
null, 0,
null, null);
0, private static final TrackGroup VIDEO_TRACK_GROUP = new TrackGroup(VIDEO_FORMAT);
null)); private static final TrackGroup AUDIO_TRACK_GROUP = new TrackGroup(AUDIO_FORMAT);
private static final TrackGroupArray TRACK_GROUPS = private static final TrackGroupArray TRACK_GROUPS =
new TrackGroupArray(VIDEO_TRACK_GROUP, AUDIO_TRACK_GROUP); new TrackGroupArray(VIDEO_TRACK_GROUP, AUDIO_TRACK_GROUP);
@ -282,7 +287,7 @@ public final class DefaultTrackSelectorTest {
@Test @Test
public void testSetParameterWithDefaultParametersDoesNotNotifyInvalidationListener() public void testSetParameterWithDefaultParametersDoesNotNotifyInvalidationListener()
throws Exception { throws Exception {
trackSelector.init(invalidationListener); trackSelector.init(invalidationListener, /* bandwidthMeter= */ null);
verify(invalidationListener, never()).onTrackSelectionsInvalidated(); verify(invalidationListener, never()).onTrackSelectionsInvalidated();
} }
@ -295,7 +300,7 @@ public final class DefaultTrackSelectorTest {
public void testSetParameterWithNonDefaultParameterNotifyInvalidationListener() public void testSetParameterWithNonDefaultParameterNotifyInvalidationListener()
throws Exception { throws Exception {
Parameters parameters = new ParametersBuilder().setPreferredAudioLanguage("eng").build(); Parameters parameters = new ParametersBuilder().setPreferredAudioLanguage("eng").build();
trackSelector.init(invalidationListener); trackSelector.init(invalidationListener, /* bandwidthMeter= */ null);
trackSelector.setParameters(parameters); trackSelector.setParameters(parameters);
verify(invalidationListener).onTrackSelectionsInvalidated(); verify(invalidationListener).onTrackSelectionsInvalidated();
@ -310,7 +315,7 @@ public final class DefaultTrackSelectorTest {
public void testSetParameterWithSameParametersDoesNotNotifyInvalidationListenerAgain() public void testSetParameterWithSameParametersDoesNotNotifyInvalidationListenerAgain()
throws Exception { throws Exception {
ParametersBuilder builder = new ParametersBuilder().setPreferredAudioLanguage("eng"); ParametersBuilder builder = new ParametersBuilder().setPreferredAudioLanguage("eng");
trackSelector.init(invalidationListener); trackSelector.init(invalidationListener, /* bandwidthMeter= */ null);
trackSelector.setParameters(builder.build()); trackSelector.setParameters(builder.build());
trackSelector.setParameters(builder.build()); trackSelector.setParameters(builder.build());
@ -956,6 +961,116 @@ public final class DefaultTrackSelectorTest {
assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(lowerBitrateFormat); assertThat(result.selections.get(0).getSelectedFormat()).isEqualTo(lowerBitrateFormat);
} }
@Test
public void testSelectTracksWithMultipleAudioTracksReturnsAdaptiveTrackSelection()
throws Exception {
TrackSelection adaptiveTrackSelection = mock(TrackSelection.class);
TrackSelection.Factory adaptiveTrackSelectionFactory = mock(TrackSelection.Factory.class);
when(adaptiveTrackSelectionFactory.createTrackSelection(any(), any(), anyVararg()))
.thenReturn(adaptiveTrackSelection);
trackSelector = new DefaultTrackSelector(adaptiveTrackSelectionFactory);
BandwidthMeter bandwidthMeter = mock(BandwidthMeter.class);
trackSelector.init(invalidationListener, bandwidthMeter);
TrackGroupArray trackGroupArray = singleTrackGroup(AUDIO_FORMAT, AUDIO_FORMAT);
TrackSelectorResult result =
trackSelector.selectTracks(
new RendererCapabilities[] {AUDIO_CAPABILITIES}, trackGroupArray);
assertThat(result.length).isEqualTo(1);
assertThat(result.selections.get(0)).isEqualTo(adaptiveTrackSelection);
verify(adaptiveTrackSelectionFactory)
.createTrackSelection(trackGroupArray.get(0), bandwidthMeter, 0, 1);
}
@Test
public void testSelectTracksWithMultipleAudioTracksOverrideReturnsAdaptiveTrackSelection()
throws Exception {
TrackSelection adaptiveTrackSelection = mock(TrackSelection.class);
TrackSelection.Factory adaptiveTrackSelectionFactory = mock(TrackSelection.Factory.class);
when(adaptiveTrackSelectionFactory.createTrackSelection(any(), any(), anyVararg()))
.thenReturn(adaptiveTrackSelection);
trackSelector = new DefaultTrackSelector(adaptiveTrackSelectionFactory);
BandwidthMeter bandwidthMeter = mock(BandwidthMeter.class);
trackSelector.init(invalidationListener, bandwidthMeter);
TrackGroupArray trackGroupArray = singleTrackGroup(AUDIO_FORMAT, AUDIO_FORMAT, AUDIO_FORMAT);
trackSelector.setParameters(
trackSelector
.buildUponParameters()
.setSelectionOverride(
/* rendererIndex= */ 0,
trackGroupArray,
new SelectionOverride(/* groupIndex= */ 0, /* tracks= */ 1, 2)));
TrackSelectorResult result =
trackSelector.selectTracks(
new RendererCapabilities[] {AUDIO_CAPABILITIES}, trackGroupArray);
assertThat(result.length).isEqualTo(1);
assertThat(result.selections.get(0)).isEqualTo(adaptiveTrackSelection);
verify(adaptiveTrackSelectionFactory)
.createTrackSelection(trackGroupArray.get(0), bandwidthMeter, 1, 2);
}
@Test
public void testSelectTracksWithMultipleVideoTracksReturnsAdaptiveTrackSelection()
throws Exception {
TrackSelection adaptiveTrackSelection = mock(TrackSelection.class);
TrackSelection.Factory adaptiveTrackSelectionFactory = mock(TrackSelection.Factory.class);
when(adaptiveTrackSelectionFactory.createTrackSelection(any(), any(), anyVararg()))
.thenReturn(adaptiveTrackSelection);
trackSelector = new DefaultTrackSelector(adaptiveTrackSelectionFactory);
BandwidthMeter bandwidthMeter = mock(BandwidthMeter.class);
trackSelector.init(invalidationListener, bandwidthMeter);
TrackGroupArray trackGroupArray = singleTrackGroup(VIDEO_FORMAT, VIDEO_FORMAT);
TrackSelectorResult result =
trackSelector.selectTracks(
new RendererCapabilities[] {VIDEO_CAPABILITIES}, trackGroupArray);
assertThat(result.length).isEqualTo(1);
assertThat(result.selections.get(0)).isEqualTo(adaptiveTrackSelection);
verify(adaptiveTrackSelectionFactory)
.createTrackSelection(trackGroupArray.get(0), bandwidthMeter, 0, 1);
}
@Test
public void testSelectTracksWithMultipleVideoTracksOverrideReturnsAdaptiveTrackSelection()
throws Exception {
TrackSelection adaptiveTrackSelection = mock(TrackSelection.class);
TrackSelection.Factory adaptiveTrackSelectionFactory = mock(TrackSelection.Factory.class);
when(adaptiveTrackSelectionFactory.createTrackSelection(any(), any(), anyVararg()))
.thenReturn(adaptiveTrackSelection);
trackSelector = new DefaultTrackSelector(adaptiveTrackSelectionFactory);
BandwidthMeter bandwidthMeter = mock(BandwidthMeter.class);
trackSelector.init(invalidationListener, bandwidthMeter);
TrackGroupArray trackGroupArray = singleTrackGroup(VIDEO_FORMAT, VIDEO_FORMAT, VIDEO_FORMAT);
trackSelector.setParameters(
trackSelector
.buildUponParameters()
.setSelectionOverride(
/* rendererIndex= */ 0,
trackGroupArray,
new SelectionOverride(/* groupIndex= */ 0, /* tracks= */ 1, 2)));
TrackSelectorResult result =
trackSelector.selectTracks(
new RendererCapabilities[] {VIDEO_CAPABILITIES}, trackGroupArray);
assertThat(result.length).isEqualTo(1);
assertThat(result.selections.get(0)).isEqualTo(adaptiveTrackSelection);
verify(adaptiveTrackSelectionFactory)
.createTrackSelection(trackGroupArray.get(0), bandwidthMeter, 1, 2);
}
private static void assertTrackSelections(TrackSelectorResult result, TrackSelection[] expected) { private static void assertTrackSelections(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

@ -0,0 +1,64 @@
/*
* Copyright (C) 2018 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.trackselection;
import static com.google.common.truth.Truth.assertThat;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.trackselection.TrackSelector.InvalidationListener;
import com.google.android.exoplayer2.upstream.BandwidthMeter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.robolectric.RobolectricTestRunner;
/** Unit test for {@link TrackSelector}. */
@RunWith(RobolectricTestRunner.class)
public class TrackSelectorTest {
private TrackSelector trackSelector;
@Before
public void setUp() {
trackSelector = new TrackSelector() {
@Override
public TrackSelectorResult selectTracks(RendererCapabilities[] rendererCapabilities,
TrackGroupArray trackGroups) throws ExoPlaybackException {
throw new UnsupportedOperationException();
}
@Override
public void onSelectionActivated(Object info) {}
};
}
@Test
public void getBandwidthMeter_beforeInitialization_returnsNull() {
assertThat(trackSelector.getBandwidthMeter()).isNull();
}
@Test
public void getBandwidthMeter_afterInitialization_returnsProvidedBandwidthMeter() {
InvalidationListener invalidationListener = Mockito.mock(InvalidationListener.class);
BandwidthMeter bandwidthMeter = Mockito.mock(BandwidthMeter.class);
trackSelector.init(invalidationListener, bandwidthMeter);
assertThat(trackSelector.getBandwidthMeter()).isEqualTo(bandwidthMeter);
}
}