am dce7a427: Merge "Full volume on remote submix for apps that need it" into lmp-dev
* commit 'dce7a427593c6e11277d9e3075ab027512923276': Full volume on remote submix for apps that need it
This commit is contained in:
@@ -18,10 +18,15 @@ package android.media;
|
|||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import android.os.Binder;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.os.ServiceManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,6 +104,8 @@ public class AudioRecord
|
|||||||
|
|
||||||
private final static String TAG = "android.media.AudioRecord";
|
private final static String TAG = "android.media.AudioRecord";
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public final static String SUBMIX_FIXED_VOLUME = "fixedVolume";
|
||||||
|
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
// Used exclusively by native code
|
// Used exclusively by native code
|
||||||
@@ -184,6 +191,7 @@ public class AudioRecord
|
|||||||
* AudioAttributes
|
* AudioAttributes
|
||||||
*/
|
*/
|
||||||
private AudioAttributes mAudioAttributes;
|
private AudioAttributes mAudioAttributes;
|
||||||
|
private boolean mIsSubmixFullVolume = false;
|
||||||
|
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
// Constructor, Finalize
|
// Constructor, Finalize
|
||||||
@@ -267,6 +275,18 @@ public class AudioRecord
|
|||||||
|
|
||||||
mAudioAttributes = attributes;
|
mAudioAttributes = attributes;
|
||||||
|
|
||||||
|
// is this AudioRecord using REMOTE_SUBMIX at full volume?
|
||||||
|
if (mAudioAttributes.getCapturePreset() == MediaRecorder.AudioSource.REMOTE_SUBMIX) {
|
||||||
|
final Iterator<String> tagsIter = mAudioAttributes.getTags().iterator();
|
||||||
|
while (tagsIter.hasNext()) {
|
||||||
|
if (tagsIter.next().equalsIgnoreCase(SUBMIX_FIXED_VOLUME)) {
|
||||||
|
mIsSubmixFullVolume = true;
|
||||||
|
Log.v(TAG, "Will record from REMOTE_SUBMIX at full fixed volume");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int rate = 0;
|
int rate = 0;
|
||||||
if ((format.getPropertySetMask()
|
if ((format.getPropertySetMask()
|
||||||
& AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) != 0)
|
& AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) != 0)
|
||||||
@@ -420,7 +440,8 @@ public class AudioRecord
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void finalize() {
|
protected void finalize() {
|
||||||
native_finalize();
|
// will cause stop() to be called, and if appropriate, will handle fixed volume recording
|
||||||
|
release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -587,6 +608,7 @@ public class AudioRecord
|
|||||||
// start recording
|
// start recording
|
||||||
synchronized(mRecordingStateLock) {
|
synchronized(mRecordingStateLock) {
|
||||||
if (native_start(MediaSyncEvent.SYNC_EVENT_NONE, 0) == SUCCESS) {
|
if (native_start(MediaSyncEvent.SYNC_EVENT_NONE, 0) == SUCCESS) {
|
||||||
|
handleFullVolumeRec(true);
|
||||||
mRecordingState = RECORDSTATE_RECORDING;
|
mRecordingState = RECORDSTATE_RECORDING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -609,6 +631,7 @@ public class AudioRecord
|
|||||||
// start recording
|
// start recording
|
||||||
synchronized(mRecordingStateLock) {
|
synchronized(mRecordingStateLock) {
|
||||||
if (native_start(syncEvent.getType(), syncEvent.getAudioSessionId()) == SUCCESS) {
|
if (native_start(syncEvent.getType(), syncEvent.getAudioSessionId()) == SUCCESS) {
|
||||||
|
handleFullVolumeRec(true);
|
||||||
mRecordingState = RECORDSTATE_RECORDING;
|
mRecordingState = RECORDSTATE_RECORDING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -626,11 +649,25 @@ public class AudioRecord
|
|||||||
|
|
||||||
// stop recording
|
// stop recording
|
||||||
synchronized(mRecordingStateLock) {
|
synchronized(mRecordingStateLock) {
|
||||||
|
handleFullVolumeRec(false);
|
||||||
native_stop();
|
native_stop();
|
||||||
mRecordingState = RECORDSTATE_STOPPED;
|
mRecordingState = RECORDSTATE_STOPPED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final IBinder mICallBack = new Binder();
|
||||||
|
private void handleFullVolumeRec(boolean starting) {
|
||||||
|
if (!mIsSubmixFullVolume) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final IBinder b = ServiceManager.getService(android.content.Context.AUDIO_SERVICE);
|
||||||
|
final IAudioService ias = IAudioService.Stub.asInterface(b);
|
||||||
|
try {
|
||||||
|
ias.forceRemoteSubmixFullVolume(starting, mICallBack);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, "Error talking to AudioService when handling full submix volume", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
// Audio data supply
|
// Audio data supply
|
||||||
@@ -881,6 +918,7 @@ public class AudioRecord
|
|||||||
int sampleRate, int channelMask, int audioFormat,
|
int sampleRate, int channelMask, int audioFormat,
|
||||||
int buffSizeInBytes, int[] sessionId);
|
int buffSizeInBytes, int[] sessionId);
|
||||||
|
|
||||||
|
// TODO remove: implementation calls directly into implementation of native_release()
|
||||||
private native final void native_finalize();
|
private native final void native_finalize();
|
||||||
|
|
||||||
private native final void native_release();
|
private native final void native_release();
|
||||||
|
|||||||
@@ -485,6 +485,7 @@ public class AudioService extends IAudioService.Stub {
|
|||||||
AudioSystem.DEVICE_OUT_HDMI_ARC |
|
AudioSystem.DEVICE_OUT_HDMI_ARC |
|
||||||
AudioSystem.DEVICE_OUT_SPDIF |
|
AudioSystem.DEVICE_OUT_SPDIF |
|
||||||
AudioSystem.DEVICE_OUT_AUX_LINE;
|
AudioSystem.DEVICE_OUT_AUX_LINE;
|
||||||
|
int mFullVolumeDevices = 0;
|
||||||
|
|
||||||
// TODO merge orientation and rotation
|
// TODO merge orientation and rotation
|
||||||
private final boolean mMonitorOrientation;
|
private final boolean mMonitorOrientation;
|
||||||
@@ -727,6 +728,10 @@ public class AudioService extends IAudioService.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkAllFixedVolumeDevices(int streamType) {
|
||||||
|
mStreamStates[streamType].checkFixedVolumeDevices();
|
||||||
|
}
|
||||||
|
|
||||||
private void createStreamStates() {
|
private void createStreamStates() {
|
||||||
int numStreamTypes = AudioSystem.getNumStreamTypes();
|
int numStreamTypes = AudioSystem.getNumStreamTypes();
|
||||||
VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
|
VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
|
||||||
@@ -1466,6 +1471,106 @@ public class AudioService extends IAudioService.Stub {
|
|||||||
return mStreamStates[streamType].isMuted();
|
return mStreamStates[streamType].isMuted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class RmtSbmxFullVolDeathHandler implements IBinder.DeathRecipient {
|
||||||
|
private IBinder mICallback; // To be notified of client's death
|
||||||
|
|
||||||
|
RmtSbmxFullVolDeathHandler(IBinder cb) {
|
||||||
|
mICallback = cb;
|
||||||
|
try {
|
||||||
|
cb.linkToDeath(this, 0/*flags*/);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, "can't link to death", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isHandlerFor(IBinder cb) {
|
||||||
|
return mICallback.equals(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
void forget() {
|
||||||
|
try {
|
||||||
|
mICallback.unlinkToDeath(this, 0/*flags*/);
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
Log.e(TAG, "error unlinking to death", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void binderDied() {
|
||||||
|
Log.w(TAG, "Recorder with remote submix at full volume died " + mICallback);
|
||||||
|
forceRemoteSubmixFullVolume(false, mICallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* call must be synchronized on mRmtSbmxFullVolDeathHandlers
|
||||||
|
* @return true if there is a registered death handler, false otherwise */
|
||||||
|
private boolean discardRmtSbmxFullVolDeathHandlerFor(IBinder cb) {
|
||||||
|
Iterator<RmtSbmxFullVolDeathHandler> it = mRmtSbmxFullVolDeathHandlers.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
final RmtSbmxFullVolDeathHandler handler = it.next();
|
||||||
|
if (handler.isHandlerFor(cb)) {
|
||||||
|
handler.forget();
|
||||||
|
mRmtSbmxFullVolDeathHandlers.remove(handler);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** call synchronized on mRmtSbmxFullVolDeathHandlers */
|
||||||
|
private boolean hasRmtSbmxFullVolDeathHandlerFor(IBinder cb) {
|
||||||
|
Iterator<RmtSbmxFullVolDeathHandler> it = mRmtSbmxFullVolDeathHandlers.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
if (it.next().isHandlerFor(cb)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int mRmtSbmxFullVolRefCount = 0;
|
||||||
|
private ArrayList<RmtSbmxFullVolDeathHandler> mRmtSbmxFullVolDeathHandlers =
|
||||||
|
new ArrayList<RmtSbmxFullVolDeathHandler>();
|
||||||
|
|
||||||
|
public void forceRemoteSubmixFullVolume(boolean startForcing, IBinder cb) {
|
||||||
|
if (cb == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
|
||||||
|
android.Manifest.permission.CAPTURE_AUDIO_OUTPUT))) {
|
||||||
|
Log.w(TAG, "Trying to call forceRemoteSubmixFullVolume() without CAPTURE_AUDIO_OUTPUT");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
synchronized(mRmtSbmxFullVolDeathHandlers) {
|
||||||
|
boolean applyRequired = false;
|
||||||
|
if (startForcing) {
|
||||||
|
if (!hasRmtSbmxFullVolDeathHandlerFor(cb)) {
|
||||||
|
mRmtSbmxFullVolDeathHandlers.add(new RmtSbmxFullVolDeathHandler(cb));
|
||||||
|
if (mRmtSbmxFullVolRefCount == 0) {
|
||||||
|
mFullVolumeDevices |= AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
|
||||||
|
mFixedVolumeDevices |= AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
|
||||||
|
applyRequired = true;
|
||||||
|
}
|
||||||
|
mRmtSbmxFullVolRefCount++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (discardRmtSbmxFullVolDeathHandlerFor(cb) && (mRmtSbmxFullVolRefCount > 0)) {
|
||||||
|
mRmtSbmxFullVolRefCount--;
|
||||||
|
if (mRmtSbmxFullVolRefCount == 0) {
|
||||||
|
mFullVolumeDevices &= ~AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
|
||||||
|
mFixedVolumeDevices &= ~AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
|
||||||
|
applyRequired = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (applyRequired) {
|
||||||
|
// Assumes only STREAM_MUSIC going through DEVICE_OUT_REMOTE_SUBMIX
|
||||||
|
checkAllFixedVolumeDevices(AudioSystem.STREAM_MUSIC);
|
||||||
|
mStreamStates[AudioSystem.STREAM_MUSIC].applyAllVolumes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @see AudioManager#setMasterMute(boolean, int) */
|
/** @see AudioManager#setMasterMute(boolean, int) */
|
||||||
public void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb) {
|
public void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb) {
|
||||||
if (mUseFixedVolume) {
|
if (mUseFixedVolume) {
|
||||||
@@ -3243,8 +3348,8 @@ public class AudioService extends IAudioService.Stub {
|
|||||||
int index;
|
int index;
|
||||||
if (isMuted()) {
|
if (isMuted()) {
|
||||||
index = 0;
|
index = 0;
|
||||||
} else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
|
} else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && mAvrcpAbsVolSupported)
|
||||||
mAvrcpAbsVolSupported) {
|
|| ((device & mFullVolumeDevices) != 0)) {
|
||||||
index = (mIndexMax + 5)/10;
|
index = (mIndexMax + 5)/10;
|
||||||
} else {
|
} else {
|
||||||
index = (getIndex(device) + 5)/10;
|
index = (getIndex(device) + 5)/10;
|
||||||
@@ -3272,8 +3377,10 @@ public class AudioService extends IAudioService.Stub {
|
|||||||
if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
|
if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
|
||||||
if (isMuted()) {
|
if (isMuted()) {
|
||||||
index = 0;
|
index = 0;
|
||||||
} else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
|
} else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
|
||||||
mAvrcpAbsVolSupported) {
|
mAvrcpAbsVolSupported)
|
||||||
|
|| ((device & mFullVolumeDevices) != 0))
|
||||||
|
{
|
||||||
index = (mIndexMax + 5)/10;
|
index = (mIndexMax + 5)/10;
|
||||||
} else {
|
} else {
|
||||||
index = ((Integer)entry.getValue() + 5)/10;
|
index = ((Integer)entry.getValue() + 5)/10;
|
||||||
@@ -3403,7 +3510,8 @@ public class AudioService extends IAudioService.Stub {
|
|||||||
Map.Entry entry = (Map.Entry)i.next();
|
Map.Entry entry = (Map.Entry)i.next();
|
||||||
int device = ((Integer)entry.getKey()).intValue();
|
int device = ((Integer)entry.getKey()).intValue();
|
||||||
int index = ((Integer)entry.getValue()).intValue();
|
int index = ((Integer)entry.getValue()).intValue();
|
||||||
if (((device & mFixedVolumeDevices) != 0) && index != 0) {
|
if (((device & mFullVolumeDevices) != 0)
|
||||||
|
|| (((device & mFixedVolumeDevices) != 0) && index != 0)) {
|
||||||
entry.setValue(mIndexMax);
|
entry.setValue(mIndexMax);
|
||||||
}
|
}
|
||||||
applyDeviceVolume(device);
|
applyDeviceVolume(device);
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ interface IAudioService {
|
|||||||
|
|
||||||
boolean isStreamMute(int streamType);
|
boolean isStreamMute(int streamType);
|
||||||
|
|
||||||
|
void forceRemoteSubmixFullVolume(boolean startForcing, IBinder cb);
|
||||||
|
|
||||||
void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb);
|
void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb);
|
||||||
|
|
||||||
boolean isMasterMute();
|
boolean isMasterMute();
|
||||||
|
|||||||
Reference in New Issue
Block a user