Requiring offload disables any tracks that are non-compatible

With `AudioOffloadModePreference` `AUDIO_OFFLOAD_MODE_PREFERENCE_REQUIRED`, the `DefaultTrackSelector` will select a single audio track that it finds is offload compatible. If not any audio track is supported in offload, then no track will be selected.

PiperOrigin-RevId: 537877183
This commit is contained in:
michaelkatz 2023-06-05 15:04:15 +00:00 committed by Tofunmi Adigun-Hameed
parent 133943a635
commit 6b7b97ccfe
4 changed files with 251 additions and 32 deletions

View File

@ -28,6 +28,11 @@
`TrackSelectionParameters`. If the set preference is to enable, the
device supports offload for the format, and the track selection is a
single audio track, then audio offload will be enabled.
* If `audioOffloadModePreference` is set to
`AUDIO_OFFLOAD_MODE_PREFERENCE_REQUIRED`, then the
`DefaultTrackSelector` will only select an audio track and only if that
track's format is supported in offload. If no audio track is supported
in offload, then no track will be selected.
* Remove parameter `enableOffload` from
`DefaultRenderersFactory.buildAudioSink` method signature.
* Remove method `DefaultAudioSink.Builder.setOffloadMode`.

View File

@ -72,19 +72,27 @@ import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
public class TrackSelectionParameters implements Bundleable {
/**
* The preference level for enabling audio offload on the audio sink. Either {@link
* #AUDIO_OFFLOAD_MODE_PREFERENCE_ENABLED} or {@link #AUDIO_OFFLOAD_MODE_PREFERENCE_DISABLED}.
* The preference level for enabling audio offload on the audio sink. One of {@link
* #AUDIO_OFFLOAD_MODE_PREFERENCE_REQUIRED}, {@link #AUDIO_OFFLOAD_MODE_PREFERENCE_ENABLED}, or
* {@link #AUDIO_OFFLOAD_MODE_PREFERENCE_DISABLED}.
*/
@UnstableApi
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef({
AUDIO_OFFLOAD_MODE_PREFERENCE_REQUIRED,
AUDIO_OFFLOAD_MODE_PREFERENCE_ENABLED,
AUDIO_OFFLOAD_MODE_PREFERENCE_DISABLED,
})
public @interface AudioOffloadModePreference {}
/**
* The track selector will only select tracks that with the renderer capabilities provide an audio
* offload compatible playback scenario. If it is impossible to create an offload-compatible track
* selection, then no tracks will be selected.
*/
@UnstableApi public static final int AUDIO_OFFLOAD_MODE_PREFERENCE_REQUIRED = 2;
/**
* The track selector will enable audio offload if the selected tracks and renderer capabilities
* are compatible.
@ -987,9 +995,8 @@ public class TrackSelectionParameters implements Bundleable {
public final ImmutableList<String> preferredAudioMimeTypes;
/**
* The preferred offload mode setting for audio playback. Either {@link
* #AUDIO_OFFLOAD_MODE_PREFERENCE_ENABLED} or {@link #AUDIO_OFFLOAD_MODE_PREFERENCE_DISABLED}. The
* default is {@code AUDIO_OFFLOAD_MODE_PREFERENCE_DISABLED}.
* The preferred offload mode setting for audio playback. The default is {@link
* #AUDIO_OFFLOAD_MODE_PREFERENCE_DISABLED}.
*/
public final @AudioOffloadModePreference int audioOffloadModePreference;
/**

View File

@ -16,6 +16,7 @@
package androidx.media3.exoplayer.trackselection;
import static androidx.media3.common.TrackSelectionParameters.AUDIO_OFFLOAD_MODE_PREFERENCE_DISABLED;
import static androidx.media3.common.TrackSelectionParameters.AUDIO_OFFLOAD_MODE_PREFERENCE_REQUIRED;
import static androidx.media3.common.util.Assertions.checkStateNotNull;
import static androidx.media3.common.util.Util.castNonNull;
import static androidx.media3.exoplayer.RendererCapabilities.AUDIO_OFFLOAD_GAPLESS_SUPPORTED;
@ -2514,6 +2515,10 @@ public class DefaultTrackSelector extends MappingTrackSelector
* <p>The implementation should not account for overrides and disabled flags. Track selections
* generated by this method will be overridden to account for these properties.
*
* <p>If selection parameters include {@link Parameters#audioOffloadModePreference} with {@link
* TrackSelectionParameters#AUDIO_OFFLOAD_MODE_PREFERENCE_REQUIRED}, then only audio tracks will
* be selected. If no audio track is supported in offload, then no track will be selected.
*
* @param mappedTrackInfo Mapped track information.
* @param rendererFormatSupports The {@link Capabilities} for each mapped track, indexed by
* renderer, track group and track (in that order).
@ -2578,7 +2583,6 @@ public class DefaultTrackSelector extends MappingTrackSelector
trackType, mappedTrackInfo.getTrackGroups(i), rendererFormatSupports[i], params);
}
}
return definitions;
}
@ -2605,6 +2609,9 @@ public class DefaultTrackSelector extends MappingTrackSelector
@AdaptiveSupport int[] mixedMimeTypeSupports,
Parameters params)
throws ExoPlaybackException {
if (params.audioOffloadModePreference == AUDIO_OFFLOAD_MODE_PREFERENCE_REQUIRED) {
return null;
}
return selectTracksForType(
C.TRACK_TYPE_VIDEO,
mappedTrackInfo,
@ -2720,6 +2727,9 @@ public class DefaultTrackSelector extends MappingTrackSelector
Parameters params,
@Nullable String selectedAudioLanguage)
throws ExoPlaybackException {
if (params.audioOffloadModePreference == AUDIO_OFFLOAD_MODE_PREFERENCE_REQUIRED) {
return null;
}
return selectTracksForType(
C.TRACK_TYPE_TEXT,
mappedTrackInfo,
@ -2748,6 +2758,9 @@ public class DefaultTrackSelector extends MappingTrackSelector
protected ExoTrackSelection.Definition selectOtherTrack(
int trackType, TrackGroupArray groups, @Capabilities int[][] formatSupport, Parameters params)
throws ExoPlaybackException {
if (params.audioOffloadModePreference == AUDIO_OFFLOAD_MODE_PREFERENCE_REQUIRED) {
return null;
}
@Nullable TrackGroup selectedGroup = null;
int selectedTrackIndex = 0;
@Nullable OtherTrackScore selectedTrackScore = null;
@ -3069,7 +3082,8 @@ public class DefaultTrackSelector extends MappingTrackSelector
@Capabilities
int trackFormatSupport =
rendererFormatSupports[i][trackGroupIndex][trackSelection.getIndexInTrackGroup(0)];
if (rendererSupportsOffload(parameters, trackFormatSupport, trackSelection)) {
if (rendererSupportsOffload(
parameters, trackFormatSupport, trackSelection.getSelectedFormat())) {
audioRendererIndex = i;
audioRenderersSupportingOffload++;
}
@ -3092,11 +3106,11 @@ public class DefaultTrackSelector extends MappingTrackSelector
*
* @param parameters The selection parameters with audio offload mode preferences.
* @param formatSupport The {@link Capabilities} for the selected track.
* @param selection The track selection.
* @param format The format of the track selection.
* @return Whether the renderer supports tunneling for the {@link ExoTrackSelection}.
*/
private static boolean rendererSupportsOffload(
Parameters parameters, @Capabilities int formatSupport, ExoTrackSelection selection) {
Parameters parameters, @Capabilities int formatSupport, Format format) {
if (RendererCapabilities.getAudioOffloadSupport(formatSupport) == AUDIO_OFFLOAD_NOT_SUPPORTED) {
return false;
}
@ -3108,9 +3122,7 @@ public class DefaultTrackSelector extends MappingTrackSelector
}
// TODO(b/235883373): Add check for OPUS where gapless info is in initialization data
if (parameters.isGaplessSupportRequired) {
boolean isGapless =
selection.getSelectedFormat().encoderDelay != 0
|| selection.getSelectedFormat().encoderPadding != 0;
boolean isGapless = format.encoderDelay != 0 || format.encoderPadding != 0;
boolean isGaplessSupported =
(RendererCapabilities.getAudioOffloadSupport(formatSupport)
& AUDIO_OFFLOAD_GAPLESS_SUPPORTED)
@ -3643,7 +3655,8 @@ public class DefaultTrackSelector extends MappingTrackSelector
usesHardwareAcceleration =
RendererCapabilities.getHardwareAccelerationSupport(formatSupport)
== RendererCapabilities.HARDWARE_ACCELERATION_SUPPORTED;
selectionEligibility = evaluateSelectionEligibility(formatSupport, hasMappedVideoTracks);
selectionEligibility =
evaluateSelectionEligibility(parameters, formatSupport, hasMappedVideoTracks);
}
@Override
@ -3717,19 +3730,24 @@ public class DefaultTrackSelector extends MappingTrackSelector
}
private @SelectionEligibility int evaluateSelectionEligibility(
@Capabilities int rendererSupport, boolean hasMappedVideoTracks) {
Parameters params, @Capabilities int rendererSupport, boolean hasMappedVideoTracks) {
if (!isSupported(rendererSupport, parameters.exceedRendererCapabilitiesIfNecessary)) {
return SELECTION_ELIGIBILITY_NO;
}
if (!isWithinConstraints && !parameters.exceedAudioConstraintsIfNecessary) {
return SELECTION_ELIGIBILITY_NO;
}
if (params.audioOffloadModePreference == AUDIO_OFFLOAD_MODE_PREFERENCE_REQUIRED
&& !rendererSupportsOffload(params, rendererSupport, format)) {
return SELECTION_ELIGIBILITY_NO;
}
return isSupported(rendererSupport, /* allowExceedsCapabilities= */ false)
&& isWithinConstraints
&& format.bitrate != Format.NO_VALUE
&& !parameters.forceHighestSupportedBitrate
&& !parameters.forceLowestBitrate
&& (parameters.allowMultipleAdaptiveSelections || !hasMappedVideoTracks)
&& params.audioOffloadModePreference != AUDIO_OFFLOAD_MODE_PREFERENCE_REQUIRED
? SELECTION_ELIGIBILITY_ADAPTIVE
: SELECTION_ELIGIBILITY_FIXED;
}

View File

@ -20,7 +20,9 @@ import static androidx.media3.common.C.FORMAT_HANDLED;
import static androidx.media3.common.C.FORMAT_UNSUPPORTED_SUBTYPE;
import static androidx.media3.common.C.FORMAT_UNSUPPORTED_TYPE;
import static androidx.media3.common.TrackSelectionParameters.AUDIO_OFFLOAD_MODE_PREFERENCE_ENABLED;
import static androidx.media3.common.TrackSelectionParameters.AUDIO_OFFLOAD_MODE_PREFERENCE_REQUIRED;
import static androidx.media3.exoplayer.RendererCapabilities.ADAPTIVE_NOT_SEAMLESS;
import static androidx.media3.exoplayer.RendererCapabilities.AUDIO_OFFLOAD_GAPLESS_SUPPORTED;
import static androidx.media3.exoplayer.RendererCapabilities.AUDIO_OFFLOAD_SUPPORTED;
import static androidx.media3.exoplayer.RendererCapabilities.DECODER_SUPPORT_FALLBACK;
import static androidx.media3.exoplayer.RendererCapabilities.DECODER_SUPPORT_FALLBACK_MIMETYPE;
@ -30,6 +32,7 @@ import static androidx.media3.exoplayer.RendererCapabilities.HARDWARE_ACCELERATI
import static androidx.media3.exoplayer.RendererCapabilities.TUNNELING_NOT_SUPPORTED;
import static androidx.media3.exoplayer.RendererConfiguration.DEFAULT;
import static androidx.media3.exoplayer.audio.AudioSink.OFFLOAD_MODE_DISABLED;
import static androidx.media3.exoplayer.audio.AudioSink.OFFLOAD_MODE_ENABLED_GAPLESS_NOT_REQUIRED;
import static androidx.media3.exoplayer.audio.AudioSink.OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
@ -2318,7 +2321,7 @@ public final class DefaultTrackSelectorTest {
@Test
public void
selectTracks_withSingleTrackAndOffloadPreferenceEnabled_returnsRendererConfigOffloadEnabled()
selectTracks_withSingleTrackAndOffloadPreferenceEnabled_returnsRendererConfigOffloadModeEnabledGaplessRequired()
throws Exception {
Format formatAAC =
new Format.Builder().setId("0").setSampleMimeType(MimeTypes.AUDIO_AAC).build();
@ -2347,8 +2350,6 @@ public final class DefaultTrackSelectorTest {
periodId,
TIMELINE);
assertThat(trackSelector.getParameters().audioOffloadModePreference)
.isEqualTo(AUDIO_OFFLOAD_MODE_PREFERENCE_ENABLED);
assertFixedSelection(result.selections[0], trackGroups, formatAAC);
assertThat(result.rendererConfigurations[0].offloadModePreferred)
.isEqualTo(OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED);
@ -2356,7 +2357,7 @@ public final class DefaultTrackSelectorTest {
@Test
public void
selectTracks_withMultipleAudioTracksAndOffloadPreferenceEnabled_returnsRendererConfigOffloadDisabled()
selectTracks_withMultipleAudioTracksAndOffloadPreferenceEnabled_returnsRendererConfigOffloadModeDisabled()
throws Exception {
Format.Builder formatBuilder = AUDIO_FORMAT.buildUpon();
TrackGroupArray trackGroups =
@ -2385,8 +2386,6 @@ public final class DefaultTrackSelectorTest {
periodId,
TIMELINE);
assertThat(trackSelector.getParameters().audioOffloadModePreference)
.isEqualTo(AUDIO_OFFLOAD_MODE_PREFERENCE_ENABLED);
assertThat(result.length).isEqualTo(1);
assertAdaptiveSelection(result.selections[0], trackGroups.get(0), 0, 1);
assertThat(result.rendererConfigurations[0].offloadModePreferred)
@ -2395,20 +2394,16 @@ public final class DefaultTrackSelectorTest {
@Test
public void
selectTracks_gaplessTrackWithOffloadPreferenceGaplessRequired_returnsConfigOffloadDisabled()
selectTracks_withMultipleAudioTracksAndOffloadPreferenceRequired_returnsRendererConfigOffloadModeEnabledGaplessRequired()
throws Exception {
Format formatAAC =
new Format.Builder()
.setId("0")
.setSampleMimeType(MimeTypes.AUDIO_AAC)
.setEncoderDelay(100)
.build();
TrackGroupArray trackGroups = new TrackGroupArray(new TrackGroup(formatAAC));
Format format1 = AUDIO_FORMAT.buildUpon().setId("0").build();
Format format2 = AUDIO_FORMAT.buildUpon().setId("0").build();
TrackGroupArray trackGroups = singleTrackGroup(format1, format2);
trackSelector.setParameters(
trackSelector
.buildUponParameters()
.setAudioOffloadPreference(
AUDIO_OFFLOAD_MODE_PREFERENCE_ENABLED,
AUDIO_OFFLOAD_MODE_PREFERENCE_REQUIRED,
/* isGaplessSupportRequired= */ true,
/* isSpeedChangeSupportRequired= */ false)
.build());
@ -2428,13 +2423,207 @@ public final class DefaultTrackSelectorTest {
periodId,
TIMELINE);
assertThat(trackSelector.getParameters().audioOffloadModePreference)
.isEqualTo(AUDIO_OFFLOAD_MODE_PREFERENCE_ENABLED);
assertFixedSelection(result.selections[0], trackGroups, formatAAC);
assertThat(result.length).isEqualTo(1);
assertFixedSelection(result.selections[0], trackGroups, format1);
assertThat(result.rendererConfigurations[0].offloadModePreferred)
.isEqualTo(OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED);
}
@Test
public void
selectTracks_withAudioAndVideoAndOffloadPreferenceEnabled_returnsRendererConfigOffloadModeDisabled()
throws Exception {
TrackGroupArray trackGroups =
new TrackGroupArray(new TrackGroup(VIDEO_FORMAT), new TrackGroup(AUDIO_FORMAT));
trackSelector.setParameters(
trackSelector
.buildUponParameters()
.setAudioOffloadPreference(
AUDIO_OFFLOAD_MODE_PREFERENCE_ENABLED,
/* isGaplessSupportRequired= */ false,
/* isSpeedChangeSupportRequired= */ false)
.build());
RendererCapabilities audioCapabilities =
new FakeRendererCapabilities(
C.TRACK_TYPE_AUDIO,
RendererCapabilities.create(
FORMAT_HANDLED,
ADAPTIVE_NOT_SEAMLESS,
TUNNELING_NOT_SUPPORTED,
AUDIO_OFFLOAD_SUPPORTED));
TrackSelectorResult result =
trackSelector.selectTracks(
new RendererCapabilities[] {VIDEO_CAPABILITIES, audioCapabilities},
trackGroups,
periodId,
TIMELINE);
assertThat(result.length).isEqualTo(2);
assertFixedSelection(result.selections[0], trackGroups, VIDEO_FORMAT);
assertFixedSelection(result.selections[1], trackGroups, AUDIO_FORMAT);
assertThat(result.rendererConfigurations[1].offloadModePreferred)
.isEqualTo(OFFLOAD_MODE_DISABLED);
}
@Test
public void
selectTracks_withAudioAndVideoAndOffloadPreferenceRequired_returnsRendererConfigOffloadModeEnabledGaplessNotRequired()
throws Exception {
TrackGroupArray trackGroups =
new TrackGroupArray(new TrackGroup(VIDEO_FORMAT), new TrackGroup(AUDIO_FORMAT));
trackSelector.setParameters(
trackSelector
.buildUponParameters()
.setAudioOffloadPreference(
AUDIO_OFFLOAD_MODE_PREFERENCE_REQUIRED,
/* isGaplessSupportRequired= */ false,
/* isSpeedChangeSupportRequired= */ false)
.build());
RendererCapabilities audioCapabilities =
new FakeRendererCapabilities(
C.TRACK_TYPE_AUDIO,
RendererCapabilities.create(
FORMAT_HANDLED,
ADAPTIVE_NOT_SEAMLESS,
TUNNELING_NOT_SUPPORTED,
AUDIO_OFFLOAD_SUPPORTED));
TrackSelectorResult result =
trackSelector.selectTracks(
new RendererCapabilities[] {VIDEO_CAPABILITIES, audioCapabilities},
trackGroups,
periodId,
TIMELINE);
assertThat(result.length).isEqualTo(2);
assertThat(result.selections[0]).isNull();
assertFixedSelection(result.selections[1], trackGroups, AUDIO_FORMAT);
assertThat(result.rendererConfigurations[1].offloadModePreferred)
.isEqualTo(OFFLOAD_MODE_ENABLED_GAPLESS_NOT_REQUIRED);
}
@Test
public void
selectTracks_gaplessTrackWithOffloadPreferenceGaplessRequired_returnsConfigOffloadDisabled()
throws Exception {
Format formatAac =
new Format.Builder()
.setId("0")
.setSampleMimeType(MimeTypes.AUDIO_AAC)
.setEncoderDelay(100)
.build();
TrackGroupArray trackGroups = new TrackGroupArray(new TrackGroup(formatAac));
trackSelector.setParameters(
trackSelector
.buildUponParameters()
.setAudioOffloadPreference(
AUDIO_OFFLOAD_MODE_PREFERENCE_ENABLED,
/* isGaplessSupportRequired= */ true,
/* isSpeedChangeSupportRequired= */ false)
.build());
// Offload playback without gapless transitions is supported
RendererCapabilities capabilitiesOffloadSupport =
new FakeRendererCapabilities(
C.TRACK_TYPE_AUDIO,
RendererCapabilities.create(
FORMAT_HANDLED,
ADAPTIVE_NOT_SEAMLESS,
TUNNELING_NOT_SUPPORTED,
AUDIO_OFFLOAD_SUPPORTED));
TrackSelectorResult result =
trackSelector.selectTracks(
new RendererCapabilities[] {capabilitiesOffloadSupport},
trackGroups,
periodId,
TIMELINE);
assertThat(result.length).isEqualTo(1);
assertFixedSelection(result.selections[0], trackGroups, formatAac);
assertThat(result.rendererConfigurations[0].offloadModePreferred)
.isEqualTo(OFFLOAD_MODE_DISABLED);
}
@Test
public void
selectTracks_gaplessTrackWithOffloadPreferenceGaplessRequiredWithGaplessSupport_returnsConfigOffloadModeEnabledGaplessRequired()
throws Exception {
Format formatAac =
new Format.Builder()
.setId("0")
.setSampleMimeType(MimeTypes.AUDIO_AAC)
.setEncoderDelay(100)
.build();
TrackGroupArray trackGroups = new TrackGroupArray(new TrackGroup(formatAac));
trackSelector.setParameters(
trackSelector
.buildUponParameters()
.setAudioOffloadPreference(
AUDIO_OFFLOAD_MODE_PREFERENCE_ENABLED,
/* isGaplessSupportRequired= */ true,
/* isSpeedChangeSupportRequired= */ false)
.build());
// Offload playback without gapless transitions is supported
RendererCapabilities capabilitiesOffloadSupport =
new FakeRendererCapabilities(
C.TRACK_TYPE_AUDIO,
RendererCapabilities.create(
FORMAT_HANDLED,
ADAPTIVE_NOT_SEAMLESS,
TUNNELING_NOT_SUPPORTED,
/* audioOffloadSupport= */ AUDIO_OFFLOAD_SUPPORTED
| AUDIO_OFFLOAD_GAPLESS_SUPPORTED));
TrackSelectorResult result =
trackSelector.selectTracks(
new RendererCapabilities[] {capabilitiesOffloadSupport},
trackGroups,
periodId,
TIMELINE);
assertThat(result.length).isEqualTo(1);
assertFixedSelection(result.selections[0], trackGroups, formatAac);
assertThat(result.rendererConfigurations[0].offloadModePreferred)
.isEqualTo(OFFLOAD_MODE_ENABLED_GAPLESS_REQUIRED);
}
@Test
public void
selectTracks_gaplessTrackWithOffloadPreferenceRequiredGaplessRequired_returnsEmptySelection()
throws Exception {
Format formatAac =
new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).setEncoderDelay(100).build();
TrackGroupArray trackGroups = new TrackGroupArray(new TrackGroup(formatAac));
trackSelector.setParameters(
trackSelector
.buildUponParameters()
.setAudioOffloadPreference(
AUDIO_OFFLOAD_MODE_PREFERENCE_REQUIRED,
/* isGaplessSupportRequired= */ true,
/* isSpeedChangeSupportRequired= */ false)
.build());
// Offload playback without gapless transitions is supported
RendererCapabilities capabilitiesOffloadSupport =
new FakeRendererCapabilities(
C.TRACK_TYPE_AUDIO,
RendererCapabilities.create(
FORMAT_HANDLED,
ADAPTIVE_NOT_SEAMLESS,
TUNNELING_NOT_SUPPORTED,
AUDIO_OFFLOAD_SUPPORTED));
TrackSelectorResult result =
trackSelector.selectTracks(
new RendererCapabilities[] {capabilitiesOffloadSupport},
trackGroups,
periodId,
TIMELINE);
assertThat(result.selections).hasLength(1);
assertThat(result.selections[0]).isNull();
}
/**
* Tests that track selector will select the video track with the highest number of matching role
* flags given by {@link Parameters}.