Fix audio ducking issue by using Android's automatic audio focus management
Previously, manual audio focus handling caused volume to not be restored after transient ducking on some devices. This change removes manual AudioFocusRequest management and relies on ExoPlayer's built-in audio focus handling (handleAudioFocus=true), which properly manages automatic ducking as recommended in: https://developer.android.com/media/optimize/audio-focus#automatic-ducking Fixes #9710
This commit is contained in:
parent
35b70c5e9e
commit
3c58b34ac4
@ -641,6 +641,15 @@ public final class Player implements PlaybackListener, Listener {
|
||||
simpleExoPlayer.setWakeMode(C.WAKE_MODE_NETWORK);
|
||||
simpleExoPlayer.setHandleAudioBecomingNoisy(true);
|
||||
|
||||
// Enable automatic audio focus management - let Android handle ducking automatically
|
||||
simpleExoPlayer.setAudioAttributes(
|
||||
new com.google.android.exoplayer2.audio.AudioAttributes.Builder()
|
||||
.setUsage(C.USAGE_MEDIA)
|
||||
.setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
|
||||
.build(),
|
||||
true // handleAudioFocus = true for automatic management
|
||||
);
|
||||
|
||||
audioReactor = new AudioReactor(context, simpleExoPlayer);
|
||||
|
||||
registerBroadcastReceiver();
|
||||
@ -1190,10 +1199,6 @@ public final class Player implements PlaybackListener, Listener {
|
||||
}
|
||||
|
||||
UIs.call(PlayerUi::onPrepared);
|
||||
|
||||
if (playWhenReady && !isMuted()) {
|
||||
audioReactor.requestAudioFocus();
|
||||
}
|
||||
}
|
||||
|
||||
private void onBlocked() {
|
||||
@ -1341,11 +1346,6 @@ public final class Player implements PlaybackListener, Listener {
|
||||
public void toggleMute() {
|
||||
final boolean wasMuted = isMuted();
|
||||
simpleExoPlayer.setVolume(wasMuted ? 1 : 0);
|
||||
if (wasMuted) {
|
||||
audioReactor.requestAudioFocus();
|
||||
} else {
|
||||
audioReactor.abandonAudioFocus();
|
||||
}
|
||||
UIs.call(playerUi -> playerUi.onMuteUnmuteChanged(!wasMuted));
|
||||
notifyPlaybackUpdateToListeners();
|
||||
}
|
||||
@ -1757,10 +1757,6 @@ public final class Player implements PlaybackListener, Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isMuted()) {
|
||||
audioReactor.requestAudioFocus();
|
||||
}
|
||||
|
||||
if (currentState == STATE_COMPLETED) {
|
||||
if (playQueue.getIndex() == 0) {
|
||||
seekToDefault();
|
||||
@ -1781,7 +1777,6 @@ public final class Player implements PlaybackListener, Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
audioReactor.abandonAudioFocus();
|
||||
simpleExoPlayer.pause();
|
||||
saveStreamProgressState();
|
||||
}
|
||||
|
||||
@ -1,54 +1,36 @@
|
||||
package org.schabi.newpipe.player.helper;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioManager;
|
||||
import android.media.audiofx.AudioEffect;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.media.AudioFocusRequestCompat;
|
||||
import androidx.media.AudioManagerCompat;
|
||||
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.analytics.AnalyticsListener;
|
||||
|
||||
public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, AnalyticsListener {
|
||||
public class AudioReactor implements AnalyticsListener {
|
||||
|
||||
private static final String TAG = "AudioFocusReactor";
|
||||
|
||||
private static final int DUCK_DURATION = 1500;
|
||||
private static final float DUCK_AUDIO_TO = .2f;
|
||||
|
||||
private static final int FOCUS_GAIN_TYPE = AudioManagerCompat.AUDIOFOCUS_GAIN;
|
||||
private static final int STREAM_TYPE = AudioManager.STREAM_MUSIC;
|
||||
|
||||
private final ExoPlayer player;
|
||||
private final Context context;
|
||||
private final AudioManager audioManager;
|
||||
|
||||
private final AudioFocusRequestCompat request;
|
||||
|
||||
public AudioReactor(@NonNull final Context context,
|
||||
@NonNull final ExoPlayer player) {
|
||||
this.player = player;
|
||||
this.context = context;
|
||||
this.audioManager = ContextCompat.getSystemService(context, AudioManager.class);
|
||||
player.addAnalyticsListener(this);
|
||||
|
||||
request = new AudioFocusRequestCompat.Builder(FOCUS_GAIN_TYPE)
|
||||
//.setAcceptsDelayedFocusGain(true)
|
||||
.setWillPauseWhenDucked(true)
|
||||
.setOnAudioFocusChangeListener(this)
|
||||
.build();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
abandonAudioFocus();
|
||||
player.removeAnalyticsListener(this);
|
||||
notifyAudioSessionUpdate(false, player.getAudioSessionId());
|
||||
}
|
||||
@ -57,14 +39,6 @@ public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, An
|
||||
// Audio Manager
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public void requestAudioFocus() {
|
||||
AudioManagerCompat.requestAudioFocus(audioManager, request);
|
||||
}
|
||||
|
||||
public void abandonAudioFocus() {
|
||||
AudioManagerCompat.abandonAudioFocusRequest(audioManager, request);
|
||||
}
|
||||
|
||||
public int getVolume() {
|
||||
return audioManager.getStreamVolume(STREAM_TYPE);
|
||||
}
|
||||
@ -77,73 +51,6 @@ public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, An
|
||||
return AudioManagerCompat.getStreamMaxVolume(audioManager, STREAM_TYPE);
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// AudioFocus
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
public void onAudioFocusChange(final int focusChange) {
|
||||
Log.d(TAG, "onAudioFocusChange() called with: focusChange = [" + focusChange + "]");
|
||||
switch (focusChange) {
|
||||
case AudioManager.AUDIOFOCUS_GAIN:
|
||||
onAudioFocusGain();
|
||||
break;
|
||||
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
|
||||
onAudioFocusLossCanDuck();
|
||||
break;
|
||||
case AudioManager.AUDIOFOCUS_LOSS:
|
||||
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
|
||||
onAudioFocusLoss();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void onAudioFocusGain() {
|
||||
Log.d(TAG, "onAudioFocusGain() called");
|
||||
player.setVolume(DUCK_AUDIO_TO);
|
||||
animateAudio(DUCK_AUDIO_TO, 1.0f);
|
||||
|
||||
if (PlayerHelper.isResumeAfterAudioFocusGain(context)) {
|
||||
player.play();
|
||||
}
|
||||
}
|
||||
|
||||
private void onAudioFocusLoss() {
|
||||
Log.d(TAG, "onAudioFocusLoss() called");
|
||||
player.pause();
|
||||
}
|
||||
|
||||
private void onAudioFocusLossCanDuck() {
|
||||
Log.d(TAG, "onAudioFocusLossCanDuck() called");
|
||||
// Set the volume to 1/10 on ducking
|
||||
player.setVolume(DUCK_AUDIO_TO);
|
||||
}
|
||||
|
||||
private void animateAudio(final float from, final float to) {
|
||||
final ValueAnimator valueAnimator = new ValueAnimator();
|
||||
valueAnimator.setFloatValues(from, to);
|
||||
valueAnimator.setDuration(AudioReactor.DUCK_DURATION);
|
||||
valueAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationStart(final Animator animation) {
|
||||
player.setVolume(from);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(final Animator animation) {
|
||||
player.setVolume(to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(final Animator animation) {
|
||||
player.setVolume(to);
|
||||
}
|
||||
});
|
||||
valueAnimator.addUpdateListener(animation ->
|
||||
player.setVolume(((float) animation.getAnimatedValue())));
|
||||
valueAnimator.start();
|
||||
}
|
||||
|
||||
/*//////////////////////////////////////////////////////////////////////////
|
||||
// Audio Processing
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user