Android 音频源码分析——音量调节流程
Posted zpy_公众号_码农修仙儿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 音频源码分析——音量调节流程相关的知识,希望对你有一定的参考价值。
源码分析基于android9.0
一、声音类型
对于大多数手机用户来说,操作手机音量按键可以看到,声音类型分为四种:媒体、铃声、闹钟、通话,但是其系统内部则分为十几种类型。
声⾳类型用来区分不同播放用途及播放设备,包含11中类型
定义在 frameworks/base/media/java/android/media/Audiosystem.java中
/** Used to identify the default audio stream volume */
public static final int STREAM_DEFAULT = -1;
/** Used to identify the volume of audio streams for phone calls */
public static final int STREAM_VOICE_CALL = 0;
/** Used to identify the volume of audio streams for system sounds */
public static final int STREAM_SYSTEM = 1;
/** Used to identify the volume of audio streams for the phone ring and message alerts */
public static final int STREAM_RING = 2;
/** Used to identify the volume of audio streams for music playback */
public static final int STREAM_MUSIC = 3;
/** Used to identify the volume of audio streams for alarms */
public static final int STREAM_ALARM = 4;
/** Used to identify the volume of audio streams for notifications */
public static final int STREAM_NOTIFICATION = 5;
/** Used to identify the volume of audio streams for phone calls when connected on bluetooth */
public static final int STREAM_BLUETOOTH_SCO = 6;
/** Used to identify the volume of audio streams for enforced system sounds in certain
* countries (e.g camera in Japan) */
public static final int STREAM_SYSTEM_ENFORCED = 7;
/** Used to identify the volume of audio streams for DTMF tones */
public static final int STREAM_DTMF = 8;
/** Used to identify the volume of audio streams exclusively transmitted through the
* speaker (TTS) of the device */
public static final int STREAM_TTS = 9;
/** Used to identify the volume of audio streams for accessibility prompts */
public static final int STREAM_ACCESSIBILITY = 10;
虽然是11种类型,但许多是使用相同的音量配置。
在 frameworks/base/services/core/java/com/android/server/audio/AudioService.java
定义声音类型的具体使用类型,通过调节几个类型,从而控制所有的声音的音量。
private final int[] STREAM_VOLUME_ALIAS_DEFAULT = new int[] {
AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
AudioSystem.STREAM_RING, // STREAM_SYSTEM
AudioSystem.STREAM_RING, // STREAM_RING
AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
AudioSystem.STREAM_ALARM, // STREAM_ALARM
AudioSystem.STREAM_RING, // STREAM_NOTIFICATION
AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
AudioSystem.STREAM_RING, // STREAM_SYSTEM_ENFORCED
AudioSystem.STREAM_RING, // STREAM_DTMF
AudioSystem.STREAM_MUSIC, // STREAM_TTS
AudioSystem.STREAM_MUSIC // STREAM_ACCESSIBILITY
};
二、音量调节
Android调节音量主要有两种方式:
- 应用层通过AudioManager接口调节;
adjustStreamVolume 和setStreamVolume等 - 系统音量按键调节,framework调用AudioService接口
这里分析下系统音量按键调节流程
framework层调节
PhoneWindowManager可以管理系统的一些按键事件:home键、返回键、Menu键、音量键等。
它接收到音量加减事件,如下函数:
private void dispatchDirectAudioEvent(KeyEvent event) {
if (event.getAction() != KeyEvent.ACTION_DOWN) {
return;
}
int keyCode = event.getKeyCode();
int flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND
| AudioManager.FLAG_FROM_KEY;
String pkgName = mContext.getOpPackageName();
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:
try {
getAudioService().adjustSuggestedStreamVolume(AudioManager.ADJUST_RAISE,
AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
} catch (Exception e) {
Log.e(TAG, "Error dispatching volume up in dispatchTvAudioEvent.", e);
}
break;
case KeyEvent.KEYCODE_VOLUME_DOWN:
try {
getAudioService().adjustSuggestedStreamVolume(AudioManager.ADJUST_LOWER,
AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
} catch (Exception e) {
Log.e(TAG, "Error dispatching volume down in dispatchTvAudioEvent.", e);
}
break;
case KeyEvent.KEYCODE_VOLUME_MUTE:
try {
if (event.getRepeatCount() == 0) {
getAudioService().adjustSuggestedStreamVolume(
AudioManager.ADJUST_TOGGLE_MUTE,
AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
}
} catch (Exception e) {
Log.e(TAG, "Error dispatching mute in dispatchTvAudioEvent.", e);
}
break;
}
}
对于音量加减都会调用getAudioService().adjustSuggestedStreamVolume,只是参数不同区分是加还是减。getAudioService获取AudioService,调用其接口调节。
AudioService
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage, String caller) {
final IAudioPolicyCallback extVolCtlr;
synchronized (mExtVolumeControllerLock) {
extVolCtlr = mExtVolumeController;
}
if (extVolCtlr != null) {
sendMsg(mAudioHandler, MSG_NOTIFY_VOL_EVENT, SENDMSG_QUEUE,
direction, 0 /*ignored*/,
extVolCtlr, 0 /*delay*/);
} else {
adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
caller, Binder.getCallingUid());
}
}
adjustSuggestedStreamVolume调用重载函数,参数不一致,新加了调用uid参数
private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage, String caller, int uid) {
final int streamType;
synchronized (mForceControlStreamLock) {
// Request lock in case mVolumeControlStream is changed by other thread.
if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1
streamType = mVolumeControlStream;
} else {
final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType);
final boolean activeForReal;
if (maybeActiveStreamType == AudioSystem.STREAM_RING
|| maybeActiveStreamType == AudioSystem.STREAM_NOTIFICATION) {
activeForReal = wasStreamActiveRecently(maybeActiveStreamType, 0);
} else {
activeForReal = AudioSystem.isStreamActive(maybeActiveStreamType, 0);
}
if (activeForReal || mVolumeControlStream == -1) {
streamType = maybeActiveStreamType;
} else {
streamType = mVolumeControlStream;
}
}
}
final boolean isMute = isMuteAdjust(direction);
ensureValidStreamType(streamType);
final int resolvedStream = mStreamVolumeAlias[streamType];
// Play sounds on STREAM_RING only.
if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
resolvedStream != AudioSystem.STREAM_RING) {
flags &= ~AudioManager.FLAG_PLAY_SOUND;
}
// For notifications/ring, show the ui before making any adjustments
// Don't suppress mute/unmute requests
if (mVolumeController.suppressAdjustment(resolvedStream, flags, isMute)) {
direction = 0;
flags &= ~AudioManager.FLAG_PLAY_SOUND;
flags &= ~AudioManager.FLAG_VIBRATE;
if (DEBUG_VOL) Log.d(TAG, "Volume controller suppressed adjustment");
}
adjustStreamVolume(streamType, direction, flags, callingPackage, caller, uid);
}
- streamType 获取当前要控制的流类型;
- resolvedStream 通过streamType转换为其对于的音频流类型别名;
- 对于通知和铃声类型,显示调节音量ui;
- 调用adjustStreamVolume;
接着看adjustStreamVolume
protected void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage, String caller, int uid) {
//...
int streamTypeAlias = mStreamVolumeAlias[streamType];
VolumeStreamState streamState = mStreamStates[streamTypeAlias];
final int device = getDeviceForStream(streamTypeAlias);
int aliasIndex = streamState.getIndex(device);
boolean adjustVolume = true;
int step;
// skip a2dp absolute volume control request when the device
// is not an a2dp device
if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) == 0 &&
(flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) != 0) {
return;
}
// If we are being called by the system (e.g. hardware keys) check for current user
// so we handle user restrictions correctly.
if (uid == android.os.Process.SYSTEM_UID) {
uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));
}
if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)
!= AppOpsManager.MODE_ALLOWED) {
return;
}
// reset any pending volume command
synchronized (mSafeMediaVolumeState) {
mPendingVolumeCommand = null;
}
flags &= ~AudioManager.FLAG_FIXED_VOLUME;
if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) &&
((device & mFixedVolumeDevices) != 0)) {
flags |= AudioManager.FLAG_FIXED_VOLUME;
// Always toggle between max safe volume and 0 for fixed volume devices where safe
// volume is enforced, and max and 0 for the others.
// This is simulated by stepping by the full allowed volume range
if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&
(device & mSafeMediaVolumeDevices) != 0) {
step = safeMediaVolumeIndex(device);
} else {
step = streamState.getMaxIndex();
}
if (aliasIndex != 0) {
aliasIndex = step;
}
} else {
// convert one UI step (+/-1) into a number of internal units on the stream alias
step = rescaleIndex(10, streamType, streamTypeAlias);
}
if (streamState.adjustIndex(direction * step, device, caller)
|| streamState.mIsMuted) {
// Post message to set system volume (it in turn will post a
// message to persist).
if (streamState.mIsMuted) {
// Unmute the stream if it was previously muted
if (direction == AudioManager.ADJUST_RAISE) {
// unmute immediately for volume up
streamState.mute(false);
} else if (direction == AudioManager.ADJUST_LOWER) {
if (mIsSingleVolume) {
sendMsg(mAudioHandler, MSG_UNMUTE_STREAM, SENDMSG_QUEUE,
streamTypeAlias, flags, null, UNMUTE_STREAM_DELAY);
}
}
}
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME,
SENDMSG_QUEUE,
device,
0,
streamState,
0);
}
//.......
int index = mStreamStates[streamType].getIndex(device);
sendVolumeUpdate(streamType, oldIndex, index, flags);
}
- streamTypeAlias 获取音频流类型别名;
- streamState获取该音频流 状态;device 获取对应音频设备;
- step 转换为音频流调节的单位;
- streamState.adjustIndex 调节音量值保存在VolumeStreamState中;
- 发送MSG_SET_DEVICE_VOLUME 消息,调节音量
MSG_SET_DEVICE_VOLUME 调用流程
AudioService.setDeviceVolume()
>>streamState.applyDeviceVolume_syncVSS(device);
>>AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
>>android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env,
jobject thiz,
jint stream,
jint index,
jint device);
>>AudioSystem::setStreamVolumeIndex(static_cast <audio_stream_type_t>(stream),
index,
(audio_devices_t)device);
>>aps->setStreamVolumeIndex(stream, index, device);
>>AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream,
int index,
audio_devices_t device);
>>AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream,
int index,
audio_devices_t device)
audioserver层调节
这里接着看 AudioPolicyManager::setStreamVolumeIndex
status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream,
int index,
audio_devices_t device)
{
// VOICE_CALL stream has minVolumeIndex > 0 but can be muted directly by an
// app that has MODIFY_PHONE_STATE permission.
if (((index < mVolumeCurves->getVolumeIndexMin(stream)) &&
!(stream == AUDIO_STREAM_VOICE_CALL && index == 0)) ||
(index > mVolumeCurves->getVolumeIndexMax(stream))) {
return BAD_VALUE;
}
if (!audio_is_output_device(device)) {
return BAD_VALUE;
}
// Force max volume if stream cannot be muted
if (!mVolumeCurves->canBeMuted(stream)) index = mVolumeCurves->getVolumeIndexMax(stream);
// update other private stream volumes which follow this one
for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) {
if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) {
continue;
}
mVolumeCurves->addCurrentVolumeIndex((audio_stream_type_t)curStream, device, index);
}
// 更新符合以下条件的所有输出和流的音量:
// -请求的流(或与音量控制匹配的流)在输出中处于活动状态
// -与该流相对应的策略选择的设备(一个或多个),包括请求的设备
// -对于非默认请求的设备,输出中当前选择的设备是请求的设备或策略选择的设备之一
// - 对于默认的请求设备(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME),
// 仅当当前所选设备不存在特定设备音量值时,才应用音量。
status_t status = NO_ERROR;
for (size_t i = 0; i < mOutputs.size(); i++) {
sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
audio_devices_t curDevice = Volume::getDeviceForVolume(desc->device());
for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) {
if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) {
continue;
}
if (!(desc->isStreamActive((audio_stream_type_t)curStream) ||
(isInCall() && (curStream == AUDIO_STREAM_VOICE_CALL)))) {
continue;
}
routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream);
audio_devices_t curStreamDevice = Volume::getDeviceForVolume(getDeviceForStrategy(
curStrategy, false /*fromCache*/));
if ((device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) &&
((curStreamDevice & device) == 0)) {
continue;
}
bool applyVolume;
if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) {
curStreamDevice |= device;
applyVolume = (curDevice & curStreamDevice) != 0;
} else {
applyVolume = !mVolumeCurves->hasVolumeIndexForDevice(
stream, curStreamDevice);
}
if (applyVolume) {
status_t volStatus =
checkAndSetVolume((audio_stream_type_t)curStream, index, desc, curDevice,
(stream == AUDIO_STREAM_SYSTEM) ? TOUCH_SOUND_FIXED_DELAY_MS : 0);
if (volStatus != NO_ERROR) {
status = volStatus;
}
}
}
}
return status;
}
对以上符合调节的输出和流
以上是关于Android 音频源码分析——音量调节流程的主要内容,如果未能解决你的问题,请参考以下文章
Android App开发音量调节中实现拖动条和滑动条和音频管理器AudioManager讲解及实战(超详细 附源码和演示视频)