Skip to content

Commit

Permalink
Add setPreferredAudioDevice method to ExoPlayer
Browse files Browse the repository at this point in the history
This allows to access the associated functionality of AudioTrack and
fills a feature gap to MediaPlayer, which has a similar method.

Issue: androidx/media#135
PiperOrigin-RevId: 476398964
(cherry picked from commit ccb820d)
  • Loading branch information
tonihei authored and microkatz committed Sep 23, 2022
1 parent 8b80743 commit 289d039
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 0 deletions.
Expand Up @@ -20,6 +20,7 @@
import static com.google.android.exoplayer2.util.Assertions.checkState;

import android.content.Context;
import android.media.AudioDeviceInfo;
import android.media.AudioTrack;
import android.media.MediaCodec;
import android.os.Looper;
Expand All @@ -29,6 +30,7 @@
import android.view.TextureView;
import androidx.annotation.IntRange;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.analytics.AnalyticsCollector;
import com.google.android.exoplayer2.analytics.AnalyticsListener;
Expand Down Expand Up @@ -1403,6 +1405,15 @@ void setMediaSources(
/** Detaches any previously attached auxiliary audio effect from the underlying audio track. */
void clearAuxEffectInfo();

/**
* Sets the preferred audio device.
*
* @param audioDeviceInfo The preferred {@linkplain AudioDeviceInfo audio device}, or null to
* restore the default.
*/
@RequiresApi(23)
void setPreferredAudioDevice(@Nullable AudioDeviceInfo audioDeviceInfo);

/**
* Sets whether skipping silences in the audio stream is enabled.
*
Expand Down
Expand Up @@ -23,6 +23,7 @@
import static com.google.android.exoplayer2.Renderer.MSG_SET_AUX_EFFECT_INFO;
import static com.google.android.exoplayer2.Renderer.MSG_SET_CAMERA_MOTION_LISTENER;
import static com.google.android.exoplayer2.Renderer.MSG_SET_CHANGE_FRAME_RATE_STRATEGY;
import static com.google.android.exoplayer2.Renderer.MSG_SET_PREFERRED_AUDIO_DEVICE;
import static com.google.android.exoplayer2.Renderer.MSG_SET_SCALING_MODE;
import static com.google.android.exoplayer2.Renderer.MSG_SET_SKIP_SILENCE_ENABLED;
import static com.google.android.exoplayer2.Renderer.MSG_SET_VIDEO_FRAME_METADATA_LISTENER;
Expand All @@ -38,6 +39,7 @@
import android.content.Context;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
import android.media.AudioTrack;
import android.media.MediaFormat;
Expand Down Expand Up @@ -1431,6 +1433,13 @@ public void clearAuxEffectInfo() {
setAuxEffectInfo(new AuxEffectInfo(AuxEffectInfo.NO_AUX_EFFECT_ID, /* sendLevel= */ 0f));
}

@RequiresApi(23)
@Override
public void setPreferredAudioDevice(@Nullable AudioDeviceInfo audioDeviceInfo) {
verifyApplicationThread();
sendRendererMessage(TRACK_TYPE_AUDIO, MSG_SET_PREFERRED_AUDIO_DEVICE, audioDeviceInfo);
}

@Override
public void setVolume(float volume) {
verifyApplicationThread();
Expand Down
Expand Up @@ -194,6 +194,13 @@ interface WakeupListener {
* <p>The message payload must be a {@link WakeupListener} instance.
*/
int MSG_SET_WAKEUP_LISTENER = 11;
/**
* The type of a message that can be passed to audio renderers via {@link
* ExoPlayer#createMessage(PlayerMessage.Target)}. The message payload should be an {@link
* android.media.AudioDeviceInfo} instance representing the preferred audio device, or null to
* restore the default.
*/
int MSG_SET_PREFERRED_AUDIO_DEVICE = 12;
/**
* Applications or extensions may define custom {@code MSG_*} constants that can be passed to
* renderers. These custom constants must be greater than or equal to this value.
Expand Down
Expand Up @@ -16,13 +16,15 @@
package com.google.android.exoplayer2;

import android.content.Context;
import android.media.AudioDeviceInfo;
import android.os.Looper;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import androidx.annotation.IntRange;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.analytics.AnalyticsCollector;
import com.google.android.exoplayer2.analytics.AnalyticsListener;
Expand Down Expand Up @@ -617,6 +619,13 @@ public void clearAuxEffectInfo() {
player.clearAuxEffectInfo();
}

@RequiresApi(23)
@Override
public void setPreferredAudioDevice(@Nullable AudioDeviceInfo audioDeviceInfo) {
blockUntilConstructorFinished();
player.setPreferredAudioDevice(audioDeviceInfo);
}

@Override
public void setVolume(float volume) {
blockUntilConstructorFinished();
Expand Down
Expand Up @@ -17,9 +17,11 @@

import static java.lang.annotation.ElementType.TYPE_USE;

import android.media.AudioDeviceInfo;
import android.media.AudioTrack;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
Expand Down Expand Up @@ -416,6 +418,15 @@ boolean handleBuffer(ByteBuffer buffer, long presentationTimeUs, int encodedAcce
/** Sets the auxiliary effect. */
void setAuxEffectInfo(AuxEffectInfo auxEffectInfo);

/**
* Sets the preferred audio device.
*
* @param audioDeviceInfo The preferred {@linkplain AudioDeviceInfo audio device}, or null to
* restore the default.
*/
@RequiresApi(23)
default void setPreferredDevice(@Nullable AudioDeviceInfo audioDeviceInfo) {}

/**
* Enables tunneling, if possible. The sink is reset if tunneling was previously disabled.
* Enabling tunneling is only possible if the sink is based on a platform {@link AudioTrack}, and
Expand Down
Expand Up @@ -23,11 +23,14 @@
import static java.lang.Math.max;
import static java.lang.annotation.ElementType.TYPE_USE;

import android.media.AudioDeviceInfo;
import android.os.Handler;
import android.os.SystemClock;
import androidx.annotation.CallSuper;
import androidx.annotation.DoNotInline;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.BaseRenderer;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
Expand Down Expand Up @@ -619,6 +622,11 @@ public void handleMessage(@MessageType int messageType, @Nullable Object message
case MSG_SET_AUDIO_SESSION_ID:
audioSink.setAudioSessionId((Integer) message);
break;
case MSG_SET_PREFERRED_AUDIO_DEVICE:
if (Util.SDK_INT >= 23) {
Api23.setAudioSinkPreferredDevice(audioSink, message);
}
break;
case MSG_SET_CAMERA_MOTION_LISTENER:
case MSG_SET_CHANGE_FRAME_RATE_STRATEGY:
case MSG_SET_SCALING_MODE:
Expand Down Expand Up @@ -791,4 +799,16 @@ public void onAudioSinkError(Exception audioSinkError) {
eventDispatcher.audioSinkError(audioSinkError);
}
}

@RequiresApi(23)
private static final class Api23 {
private Api23() {}

@DoNotInline
public static void setAudioSinkPreferredDevice(
AudioSink audioSink, @Nullable Object messagePayload) {
@Nullable AudioDeviceInfo audioDeviceInfo = (AudioDeviceInfo) messagePayload;
audioSink.setPreferredDevice(audioDeviceInfo);
}
}
}
Expand Up @@ -24,6 +24,7 @@
import static java.lang.annotation.ElementType.TYPE_USE;

import android.annotation.SuppressLint;
import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
Expand Down Expand Up @@ -556,6 +557,7 @@ public DefaultAudioSink build() {
private boolean externalAudioSessionIdProvided;
private int audioSessionId;
private AuxEffectInfo auxEffectInfo;
@Nullable private AudioDeviceInfoApi23 preferredDevice;
private boolean tunneling;
private long lastFeedElapsedRealtimeMs;
private boolean offloadDisabledUntilNextConfiguration;
Expand Down Expand Up @@ -904,6 +906,9 @@ private boolean initializeAudioTrack() throws InitializationException {
audioTrack.attachAuxEffect(auxEffectInfo.effectId);
audioTrack.setAuxEffectSendLevel(auxEffectInfo.sendLevel);
}
if (preferredDevice != null && Util.SDK_INT >= 23) {
Api23.setPreferredDeviceOnAudioTrack(audioTrack, preferredDevice);
}

startMediaTimeUsNeedsInit = true;
return true;
Expand Down Expand Up @@ -1404,6 +1409,16 @@ public void setAuxEffectInfo(AuxEffectInfo auxEffectInfo) {
this.auxEffectInfo = auxEffectInfo;
}

@RequiresApi(23)
@Override
public void setPreferredDevice(@Nullable AudioDeviceInfo audioDeviceInfo) {
this.preferredDevice =
audioDeviceInfo == null ? null : new AudioDeviceInfoApi23(audioDeviceInfo);
if (audioTrack != null) {
Api23.setPreferredDeviceOnAudioTrack(audioTrack, this.preferredDevice);
}
}

@Override
public void enableTunnelingV21() {
Assertions.checkState(Util.SDK_INT >= 21);
Expand Down Expand Up @@ -2300,6 +2315,28 @@ public void clear() {
}
}

@RequiresApi(23)
private static final class AudioDeviceInfoApi23 {

public final AudioDeviceInfo audioDeviceInfo;

public AudioDeviceInfoApi23(AudioDeviceInfo audioDeviceInfo) {
this.audioDeviceInfo = audioDeviceInfo;
}
}

@RequiresApi(23)
private static final class Api23 {
private Api23() {}

@DoNotInline
public static void setPreferredDeviceOnAudioTrack(
AudioTrack audioTrack, @Nullable AudioDeviceInfoApi23 audioDeviceInfo) {
audioTrack.setPreferredDevice(
audioDeviceInfo == null ? null : audioDeviceInfo.audioDeviceInfo);
}
}

@RequiresApi(31)
private static final class Api31 {
private Api31() {}
Expand Down
Expand Up @@ -15,7 +15,9 @@
*/
package com.google.android.exoplayer2.audio;

import android.media.AudioDeviceInfo;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.analytics.PlayerId;
Expand Down Expand Up @@ -134,6 +136,12 @@ public void setAuxEffectInfo(AuxEffectInfo auxEffectInfo) {
sink.setAuxEffectInfo(auxEffectInfo);
}

@RequiresApi(23)
@Override
public void setPreferredDevice(@Nullable AudioDeviceInfo audioDeviceInfo) {
sink.setPreferredDevice(audioDeviceInfo);
}

@Override
public void enableTunnelingV21() {
sink.enableTunnelingV21();
Expand Down
Expand Up @@ -23,13 +23,16 @@

import android.annotation.SuppressLint;
import android.content.Context;
import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
import android.media.MediaCodec;
import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.os.Handler;
import androidx.annotation.CallSuper;
import androidx.annotation.DoNotInline;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
Expand Down Expand Up @@ -745,6 +748,11 @@ public void handleMessage(@MessageType int messageType, @Nullable Object message
AuxEffectInfo auxEffectInfo = (AuxEffectInfo) message;
audioSink.setAuxEffectInfo(auxEffectInfo);
break;
case MSG_SET_PREFERRED_AUDIO_DEVICE:
if (Util.SDK_INT >= 23) {
Api23.setAudioSinkPreferredDevice(audioSink, message);
}
break;
case MSG_SET_SKIP_SILENCE_ENABLED:
audioSink.setSkipSilenceEnabled((Boolean) message);
break;
Expand Down Expand Up @@ -938,4 +946,16 @@ public void onAudioSinkError(Exception audioSinkError) {
eventDispatcher.audioSinkError(audioSinkError);
}
}

@RequiresApi(23)
private static final class Api23 {
private Api23() {}

@DoNotInline
public static void setAudioSinkPreferredDevice(
AudioSink audioSink, @Nullable Object messagePayload) {
@Nullable AudioDeviceInfo audioDeviceInfo = (AudioDeviceInfo) messagePayload;
audioSink.setPreferredDevice(audioDeviceInfo);
}
}
}
Expand Up @@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2.testutil;

import android.media.AudioDeviceInfo;
import android.os.Looper;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.ExoPlaybackException;
Expand Down Expand Up @@ -234,6 +235,11 @@ public void clearAuxEffectInfo() {
throw new UnsupportedOperationException();
}

@Override
public void setPreferredAudioDevice(@Nullable AudioDeviceInfo audioDeviceInfo) {
throw new UnsupportedOperationException();
}

@Override
public void setSkipSilenceEnabled(boolean skipSilenceEnabled) {
throw new UnsupportedOperationException();
Expand Down

0 comments on commit 289d039

Please sign in to comment.