Merge "Audio focus enforcement: use VolumeShaper for ducking"
This commit is contained in:
committed by
Android (Google) Code Review
commit
8922f5dbcd
@@ -37,8 +37,9 @@ import java.util.Objects;
|
||||
public final class VolumeShaper {
|
||||
/* member variables */
|
||||
private int mId;
|
||||
private final WeakReference<PlayerBase> mPlayerBase;
|
||||
private final WeakReference<PlayerProxy> mPlayerProxy;
|
||||
private final WeakReference<PlayerBase> mWeakPlayerBase;
|
||||
private final WeakReference<PlayerProxy> mWeakPlayerProxy;
|
||||
private PlayerProxy mPlayerProxy;
|
||||
|
||||
/**
|
||||
* Constructs a {@code VolumeShaper} from a {@link VolumeShaper.Configuration} and an
|
||||
@@ -64,14 +65,14 @@ public final class VolumeShaper {
|
||||
|
||||
/* package */ VolumeShaper(
|
||||
@NonNull Configuration configuration, @NonNull PlayerBase playerBase) {
|
||||
mPlayerBase = new WeakReference<PlayerBase>(playerBase);
|
||||
mWeakPlayerBase = new WeakReference<PlayerBase>(playerBase);
|
||||
mPlayerProxy = null;
|
||||
mWeakPlayerProxy = null;
|
||||
mId = applyPlayer(configuration, new Operation.Builder().defer().build());
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* TODO SystemApi
|
||||
* Constructs a {@code VolumeShaper} from a {@link VolumeShaper.Configuration} and a
|
||||
* {@code PlayerProxy} object. The PlayerProxy object requires that the configuration
|
||||
* be set with a system VolumeShaper id (this is a reserved value).
|
||||
@@ -80,12 +81,20 @@ public final class VolumeShaper {
|
||||
* @param playerProxy
|
||||
*/
|
||||
public VolumeShaper(
|
||||
@NonNull Configuration configuration, @NonNull PlayerProxy playerProxy) {
|
||||
@NonNull Configuration configuration,
|
||||
@NonNull PlayerProxy playerProxy,
|
||||
boolean keepReference) {
|
||||
if (configuration.getId() < 0) {
|
||||
throw new IllegalArgumentException("playerProxy configuration id must be specified");
|
||||
}
|
||||
mPlayerProxy = new WeakReference<PlayerProxy>(playerProxy);
|
||||
mPlayerBase = null;
|
||||
if (keepReference) {
|
||||
mPlayerProxy = playerProxy;
|
||||
mWeakPlayerProxy = null;
|
||||
} else {
|
||||
mWeakPlayerProxy = new WeakReference<PlayerProxy>(playerProxy);
|
||||
mPlayerProxy = null;
|
||||
}
|
||||
mWeakPlayerBase = null;
|
||||
mId = applyPlayer(configuration, new Operation.Builder().defer().build());
|
||||
}
|
||||
|
||||
@@ -143,12 +152,13 @@ public final class VolumeShaper {
|
||||
} catch (IllegalStateException ise) {
|
||||
; // ok
|
||||
}
|
||||
if (mPlayerBase != null) {
|
||||
mPlayerBase.clear();
|
||||
if (mWeakPlayerBase != null) {
|
||||
mWeakPlayerBase.clear();
|
||||
}
|
||||
if (mPlayerProxy != null) {
|
||||
mPlayerProxy.clear();
|
||||
if (mWeakPlayerProxy != null) {
|
||||
mWeakPlayerProxy.clear();
|
||||
}
|
||||
mPlayerProxy = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -167,11 +177,11 @@ public final class VolumeShaper {
|
||||
@NonNull VolumeShaper.Configuration configuration,
|
||||
@NonNull VolumeShaper.Operation operation) {
|
||||
final int id;
|
||||
if (mPlayerProxy != null) {
|
||||
if (mPlayerProxy != null || mWeakPlayerProxy != null) {
|
||||
// The PlayerProxy accepts only one way transactions so
|
||||
// the Configuration must have an id set to one of the system
|
||||
// ids (a positive value less than 16).
|
||||
PlayerProxy player = mPlayerProxy.get();
|
||||
PlayerProxy player = mWeakPlayerProxy != null ? mWeakPlayerProxy.get() : mPlayerProxy;
|
||||
if (player == null) {
|
||||
throw new IllegalStateException("player deallocated");
|
||||
}
|
||||
@@ -180,8 +190,8 @@ public final class VolumeShaper {
|
||||
throw new IllegalArgumentException("proxy requires configuration with id");
|
||||
}
|
||||
player.applyVolumeShaper(configuration, operation);
|
||||
} else if (mPlayerBase != null) {
|
||||
PlayerBase player = mPlayerBase.get();
|
||||
} else if (mWeakPlayerBase != null) {
|
||||
PlayerBase player = mWeakPlayerBase.get();
|
||||
if (player == null) {
|
||||
throw new IllegalStateException("player deallocated");
|
||||
}
|
||||
@@ -210,14 +220,10 @@ public final class VolumeShaper {
|
||||
*/
|
||||
private @NonNull VolumeShaper.State getStatePlayer(int id) {
|
||||
final VolumeShaper.State state;
|
||||
if (mPlayerProxy != null) {
|
||||
PlayerProxy player = mPlayerProxy.get();
|
||||
if (player == null) {
|
||||
throw new IllegalStateException("player deallocated");
|
||||
}
|
||||
if (mPlayerProxy != null || mWeakPlayerProxy != null) {
|
||||
throw new IllegalStateException("getStatePlayer not permitted through proxy");
|
||||
} else if (mPlayerBase != null) {
|
||||
PlayerBase player = mPlayerBase.get();
|
||||
} else if (mWeakPlayerBase != null) {
|
||||
PlayerBase player = mWeakPlayerBase.get();
|
||||
if (player == null) {
|
||||
throw new IllegalStateException("player deallocated");
|
||||
}
|
||||
|
||||
@@ -437,7 +437,7 @@ public class MediaFocusControl implements PlayerFocusEnforcer {
|
||||
* @param attr attributes of the sound about to start playing
|
||||
* @return time in ms
|
||||
*/
|
||||
protected int getFocusRampTimeMs(int focusGain, AudioAttributes attr) {
|
||||
protected static int getFocusRampTimeMs(int focusGain, AudioAttributes attr) {
|
||||
switch (attr.getUsage()) {
|
||||
case AudioAttributes.USAGE_MEDIA:
|
||||
case AudioAttributes.USAGE_GAME:
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.media.AudioSystem;
|
||||
import android.media.IPlaybackConfigDispatcher;
|
||||
import android.media.MediaRecorder;
|
||||
import android.media.PlayerBase;
|
||||
import android.media.VolumeShaper;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
@@ -47,6 +48,7 @@ public final class PlaybackActivityMonitor
|
||||
|
||||
public final static String TAG = "AudioService.PlaybackActivityMonitor";
|
||||
private final static boolean DEBUG = false;
|
||||
private final static int VOLUME_SHAPER_SYSTEM_DUCK_ID = 1;
|
||||
|
||||
private ArrayList<PlayMonitorClient> mClients = new ArrayList<PlayMonitorClient>();
|
||||
// a public client is one that needs an anonymized version of the playback configurations, we
|
||||
@@ -126,6 +128,11 @@ public final class PlaybackActivityMonitor
|
||||
final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
|
||||
if (checkConfigurationCaller(piid, apc, binderUid)) {
|
||||
mPlayers.remove(new Integer(piid));
|
||||
final VolumeShaper vs = mDuckVolumeShapers.get(new Integer(piid));
|
||||
if (vs != null) {
|
||||
vs.release();
|
||||
mDuckVolumeShapers.remove(new Integer(piid));
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "Error releasing player " + piid);
|
||||
}
|
||||
@@ -242,6 +249,20 @@ public final class PlaybackActivityMonitor
|
||||
private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>();
|
||||
private final ArrayList<Integer> mMutedPlayers = new ArrayList<Integer>();
|
||||
|
||||
private final VolumeShaper.Configuration DUCK_VSHAPE =
|
||||
new VolumeShaper.Configuration.Builder()
|
||||
.setId(VOLUME_SHAPER_SYSTEM_DUCK_ID)
|
||||
.setCurve(new float[] { 0.f, 1.f } /* times */,
|
||||
new float[] { 1.f, 0.2f } /* volumes */)
|
||||
.setDurationMs(MediaFocusControl.getFocusRampTimeMs(
|
||||
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
|
||||
new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION)
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
private final HashMap<Integer, VolumeShaper> mDuckVolumeShapers =
|
||||
new HashMap<Integer, VolumeShaper>();
|
||||
|
||||
@Override
|
||||
public boolean duckPlayers(FocusRequester winner, FocusRequester loser) {
|
||||
if (DEBUG) {
|
||||
@@ -268,11 +289,24 @@ public final class PlaybackActivityMonitor
|
||||
// the player is speaking, ducking will make the speech unintelligible
|
||||
// so let the app handle it instead
|
||||
return false;
|
||||
} else if (apc.getPlayerType()
|
||||
== AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
|
||||
// TODO support ducking of SoundPool players
|
||||
return false;
|
||||
} else {
|
||||
try {
|
||||
if (DEBUG) { Log.v(TAG, "ducking player " + piid); }
|
||||
//FIXME just a test before we have VolumeShape
|
||||
apc.getPlayerProxy().setPan(-1.0f);
|
||||
final VolumeShaper ducker;
|
||||
if (mDuckVolumeShapers.containsKey(new Integer(piid))) {
|
||||
ducker = mDuckVolumeShapers.get(new Integer(piid));
|
||||
} else {
|
||||
ducker = new VolumeShaper(
|
||||
DUCK_VSHAPE,
|
||||
apc.getPlayerProxy(),
|
||||
true /* keepReference */);
|
||||
mDuckVolumeShapers.put(new Integer(piid), ducker);
|
||||
}
|
||||
ducker.apply(VolumeShaper.Operation.PLAY); // duck
|
||||
mDuckedPlayers.add(piid);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error ducking player " + piid, e);
|
||||
@@ -301,8 +335,10 @@ public final class PlaybackActivityMonitor
|
||||
try {
|
||||
if (DEBUG) { Log.v(TAG, "unducking player" + piid); }
|
||||
mDuckedPlayers.remove(new Integer(piid));
|
||||
//FIXME just a test before we have VolumeShape
|
||||
apc.getPlayerProxy().setPan(0.0f);
|
||||
if (mDuckVolumeShapers.containsKey(new Integer(piid))) {
|
||||
final VolumeShaper ducker = mDuckVolumeShapers.get(new Integer(piid));
|
||||
ducker.apply(VolumeShaper.Operation.REVERSE); // unduck
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error unducking player " + piid, e);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user