Android-audio playback/record Monitor

游戏 游戏 1853 人阅读 | 0 人回复

<
媒介:

安卓正在Q上撑持了多使用同时灌音,当两个使用试图捕捉音频时,它们皆能够领受输进旌旗灯号,大概此中一个大要会遭到寂静处置。当多个使用同时捕捉音频时,只要一个或两个使用处于“举动”形态(正正在领受音频),其他使用则处于静音形态(领受静音)。当举动使用发作变动时,音频框架大要会按照以下划定规矩从头设置音频途径:
每一个举动使用的音频输进装备大要会变动(比方,从内乱置麦克风变动为已毗连的蓝牙耳机)。启用取最下劣先级举动使用相干联的预处置。其他预处置皆将被疏忽。当劣先级较下的使用处于举动形态时,举动使用大要会遭到寂静处置,因而您能够正在 AudioRecord 或 MediaRecorder 工具上注册一个 AudioManager.AudioRecordingCallback,以便正在设置发作变动时支到告诉。

Playback/record Monitor常睹利用场景:



  • 多app音量调节
  • 使用通话灌音
谷歌民圆阐明

2017/Bootcamp 2017 - Core Audio.pdf
Recording notification:
214022xdofw6fo0yoywoff.jpg

 
214023g2pfw3fg6y3wxx81.jpg


Playback notification: 
214023gqhaqf3122s53qaj.jpg

 
214023cibzbbkkkfze8y8r.jpg

214023c1dxzkhdg9hygvy9.jpg
 
214024a4wc4diif7ufqdw4.jpg


APP挪用办法

https://developer.android.谷歌.cn/reference/android/media/AudioManager.AudioRecordingCallback.html
214024kjsvak7in7zjjs8j.jpg

1.1 APP监听record的变化

  1. public void onCreate() {
  2.    mHandler = new VoipRecorderHandler();
  3.    mAudioManager = (AudioManager)
  4.            getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
  5.    mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback,
  6.            mHandler);
  7. }
  8. public void onDestroy() {
  9.     mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback);
  10.     mHandler.removeCallbacksAndMessages(null);
  11. }
  12. private AudioManager.AudioRecordingCallback mAudioRecordingCallback =
  13.     new AudioManager.AudioRecordingCallback() {
  14.     @Override
  15.     public void onRecordingConfigChanged(List<AudioRecordingConfiguration> configs) {
  16.         ....
  17.         }
  18.     }
  19. };
  20. private class VoipRecorderHandler extends Handler {
  21.         @Override
  22.         public void handleMessage(Message msg) {
  23.             super.handleMessage(msg);
  24.             ......
  25.         }
  26.     }
复造代码
1.2 APP监听playback的变化

  1. public void onCreate() {
  2.    mHandler = new VoipRecorderHandler();
  3.    mAudioManager = (AudioManager)
  4.            getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
  5.    registerAudioPlaybackCallback(mAudioPlaybackCallback,
  6.            new Handler(mLooper));
  7. }
  8. public void onDestroy() {
  9.     mAudioManager.unregisterAudioPlaybackCallback(mAudioPlaybackCallback);
  10. }
  11. private final AudioManager.AudioPlaybackCallback mAudioPlaybackCallback =
  12.          new AudioManager.AudioPlaybackCallback() {
  13.     @Override
  14.     public void onPlaybackConfigChanged(List<AudioRecordingConfiguration> configs) {
  15.         .....
  16.     }
  17. };
复造代码

Recorder callback具体完成流程

2.1 AudioManager流程

  1. // frameworks/base/media/java/android/media/AudioManager.java
  2. public void registerAudioRecordingCallback(cb, handler) {
  3.     // 此处保护了一个列表mRecordCallbackList
  4.     mRecordCallbackList.add(new AudioRecordingCallbackInfo(cb,
  5.           new ServiceEventHandlerDelegate(handler).getHandler()));
  6.     final IAudioService service = getService();
  7.     // getService的方法: IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
  8.     // sService = IAudioService.Stub.asInterface(b); return sService;
  9.     service.registerRecordingCallback(mRecCb);
  10. }
  11. private final IRecordingConfigDispatcher mRecCb = new IRecordingConfigDispatcher.Stub() { // 此处IRecordingConfigDispatcher 是aidl接心,会跨历程通讯
  12.     @Override
  13.     public void dispatchRecordingConfigChange(List<AudioRecordingConfiguration> configs) {
  14.         synchronized(mRecordCallbackLock) {
  15.             if (mRecordCallbackList != null) {
  16.                 for (int i=0 ; i < mRecordCallbackList.size() ; i++) {
  17.                     final AudioRecordingCallbackInfo arci = mRecordCallbackList.get(i);
  18.                     if (arci.mHandler != null) {
  19.                         final Message m = arci.mHandler.obtainMessage(
  20.                             MSSG_RECORDING_CONFIG_CHANGE/*what*/,
  21.                             new RecordConfigChangeCallbackData(arci.mCb, configs)/*obj*/);  // 那里会收收动静MSSG_RECORDING_CONFIG_CHANGE,handler内里支到那个动静便会处置: onRecordingConfigChanged,从而挪用到app内里的onRecordingConfigChanged函数
  22.                             arci.mHandler.sendMessage(m);
  23.                     }
  24.                 }
  25.             }
  26.         }
  27.     }
  28. };
  29. public void unregisterAudioRecordingCallback(@NonNull AudioRecordingCallback cb) {
  30.     removeRecordCallback_sync(cb);// 那里实践便是从mRecordCallbackList删除
  31. }
复造代码
总结: AudioManager内里的完成实在重面便是mRecordCallbackList,当有app注册callback的时分AudioManager便会把app的callback战handler疑息寄存到mRecordCallbackList内里。unregister的时分便从mRecordCallbackList内里把对应的疑息删撤除。当dispatchRecordingConfigChange的时分便暗示有record的变化,此时AudioManager便会for轮回从mRecordCallbackList掏出各个app的疑息,并挪用他们的callback把动静传已往。
audiomanager实践便是一个clinet端,实践的完成皆是正在audio server过程.
2.2 audio server流程

  1. // fw/base/services/core/java/com/android/server/audio/AudioService.java
  2. public void registerRecordingCallback(IRecordingConfigDispatcher rcdb) {
  3.     mRecordMonitor.registerRecordingCallback(rcdb, isPrivileged);
  4. }
复造代码
那里一切的完成皆是正在RecordingActivityMonitor.java
  1. // fw/bs/sv/core/java/com/android/server/audio/RecordingActivityMonitor.java
  2. void registerRecordingCallback(IRecordingConfigDispatcher rcdb, boolean isPrivileged) {
  3.     final RecMonitorClient rmc = new RecMonitorClient(rcdb, isPrivileged);
  4.     mClients.add(rmc);
  5.     // 那里实在便是保护了一个mClinets数组,寄存client数据
  6. }
  7. unregister实在便是从mClients删除那个记载
  8. private void dispatchCallbacks(List<AudioRecordingConfiguration> configs) {
  9.     final List<AudioRecordingConfiguration> configsPublic = mHasPublicClients
  10.             ? anonymizeForPublicConsumption(configs) :
  11.             new ArrayList<AudioRecordingConfiguration>();
  12.     for (RecMonitorClient rmc : mClients) {
  13.         rmc.mDispatcherCb.dispatchRecordingConfigChange(configs);
  14.     }
  15. }
复造代码
总结: audioserver内里的完成实在战audiomanger是一样的,也是经由过程保护mClients列表完成register战unregister,并正在dispatchCallbacks函数内里轮回为每一个RecMonitorClient挪用callback。
成绩: dispatchCallbacks那个是正在甚么机会挪用的?
当native层发明record有变化时,会挪用recordingCallbackFromNative,那个函数经由过程jni挪用到AudioSystem.java的onRecordingConfigurationChanged,然后挪用audio server dispatchCallbacks。
2.3 native 流程

  1. // frameworks/av/media/libaudioclient/AudioSystem.cpp
  2. /*static*/ void AudioSystem::setRecordConfigCallback(record_config_callback cb)
  3. {
  4.     Mutex::Autolock _l(gLock);
  5.     gRecordConfigCallback = cb;
  6. }
  7. void AudioSystem::AudioPolicyServiceClient::onRecordingConfigurationUpdate(
  8.           int event,
  9.           const record_client_info_t *clientInfo,
  10.           const audio_config_base_t *clientConfig,
  11.           std::vector<effect_descriptor_t> clientEffects,
  12.           const audio_config_base_t *deviceConfig,
  13.           std::vector<effect_descriptor_t> effects,
  14.           audio_patch_handle_t patchHandle,
  15.           audio_source_t source) {
  16.     record_config_callback cb = NULL;
  17.     {
  18.         Mutex::Autolock _l(AudioSystem::gLock);
  19.         cb = gRecordConfigCallback;
  20.     }
  21.     if (cb != NULL) {
  22.         cb(event, clientInfo, clientConfig, clientEffects,
  23.            deviceConfig, effects, patchHandle, source);
  24.         // 那里挪用了cb函数也便是android_media_AudioSystem_recording_callback函数
  25.     }
  26. }
复造代码
成绩: AudioSystem.cpp的setRecordConfigCallback是甚么时分挪用的?
AudioService机关函数挪用RecordingActivityMonitor.java的initMonitor。
initMonitor挪用AudioSystem.setRecordingCallback(this)
setRecordingCallback挪用AudioSystem.java setRecordingCallback
setRecordingCallback终极挪用的便是AudioSystem.cpp的setRecordConfigCallback。// 此处传进的参数是jni的android_media_AudioSystem_recording_callback函数,也便是那个函数挪用的上里的recordingCallbackFromNative。
成绩: 是谁挪用的AudioSystem::AudioPolicyServiceClient::onRecordingConfigurationUpdate
  1. av/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
  2. void AudioInputDescriptor::updateClientRecordingConfiguration (){}
  3. //  mClientInterface->onRecordingConfigurationUpdate .....
  4. // frameworks/av/services/audiopolicy/service/AudioPolicyClientImpl.cpp
  5. AudioPolicyService::AudioPolicyClient::onRecordingConfigurationUpdate
  6. // 那个函数内里挪用了AudioPolicyService的onRecordingConfigurationUpdate
  7. AudioPolicyService::onRecordingConfigurationUpdate挪用commandthread并收收动静RECORDING_CONFIGURATION_UPDATE
  8. // recordingConfigurationUpdateCommand收收动静RECORDING_CONFIGURATION_UPDATE
  9. 然后挪用AudioPolicyService::NotificationClient::onRecordingConfigurationUpdate
  10. 正在那个函数内里挪用mAudioPolicyServiceClient->onRecordingConfigurationUpdate
  11. // /frameworks/av/services/audiopolicy/service/AudioPolicyService.cpp
  12. void AudioPolicyService::onRecordingConfigurationUpdate(......) {
  13.     mAudioPolicyServiceClient->recordingConfigurationUpdateCommand(event,
  14.         clientInfo, clientConfig, clientEffects,
  15.         deviceConfig, effects,patchHandle, source);
  16. }
  17. 然后挪用的便AudioSystem::AudioPolicyServiceClient::onRecordingConfigurationUpdate
复造代码
成绩: AudioInputDescriptor::updateClientRecordingConfiguration的挪用:
void AudioInputDescriptor::updateClientRecordingConfiguration(int event, const sp& client)
那个函数有两个参数,一个是event,一个是client.
event一共有三品种型: RECORD_CONFIG_EVENT_START / RECORD_CONFIG_EVENT_STOP / RECORD_CONFIG_EVENT_UPDATE
上传的数据范例: session_id / source / deviceformat / clientformat / packagename / uid / handle / port_id / silenced / devicesource / effect等

updateClientRecordingConfiguration的挪用机会:
AudioInputDescriptor::setPatchHandle(audio_patch_handle_t handle) // 收收start or update
// audio patch暗示音频中端到真个毗连干系。那里setPatchHandle该当是切换了装备,大概开端毗连装备的时分会挪用的。
AudioInputDescriptor::setClientActive(const sp& client, bool active) // 收收start or stop
// setClientActive是标识表记标帜当前Client能否活泼,当startinput startsource等函数中标识表记标帜为true,stop的时分标识表记标帜为false
AudioInputDescriptor::trackEffectEnabled(const sp &effect, bool enabled) // 收收 update
// trackEffectEnabled是切换音效的
AudioInputDescriptor::setAppState(audio_port_handle_t portId, app_state_t state) // 收收 update
// setAppState那个是设置app state,有三种state: APP_STATE_IDLE & APP_STATE_FOREGROUND & APP_STATE_TOP, 第一个暗示client是闲暇形态,不克不及灌音。第两个暗示client有一个前台service,能够灌音。第三个暗示client有一个可睹的ui,能够灌音也能够挑选use case。
214024dmoz96w94yz6k2yp.jpg


PlaybackCallBack具体完成流程:

214025pbzmbvjxxba3ba3n.jpg

 updateState有6种state,前面挪用中的event便是那里的state。
  1. typedef enum {
  2.     PLAYER_STATE_UNKNOWN  = -1,
  3.     PLAYER_STATE_RELEASED = 0,  // 挪用release以后state是released
  4.     PLAYER_STATE_IDLE     = 1,  // 创立playerbase的时分state是idle
  5.     PLAYER_STATE_STARTED  = 2,
  6.     PLAYER_STATE_PAUSED   = 3,
  7.     PLAYER_STATE_STOPPED  = 4,
  8. } player_state_t;
复造代码
  1. // frameworks/base/media/java/android/media/AudioManager.java
  2. 支到动静MSSG_PLAYBACK_CONFIG_CHANGE便会挪用cbData.mCb.onPlaybackConfigChanged(cbData.mConfigs)
  3. public void dispatchPlaybackConfigChange // 收回动静MSSG_PLAYBACK_CONFIG_CHANGE
  4. // frameworks/base/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
  5. private void dispatchPlaybackChange(boolean iplayerReleased)
  6. playerAttributes && playerEvent 城市挪用dispatchPlaybackChange
  7. // 先看看playerAttributes的挪用
  8. // frameworks/base/services/core/java/com/android/server/audio/AudioService.java
  9. public void playerAttributes(int piid, AudioAttributes attr)
  10. // frameworks/base/media/java/android/media/PlayerBase.java
  11. void baseUpdateAudioAttributes(@NonNull AudioAttributes attr)
  12. setAudioStreamType && setAudioAttributes 城市挪用baseUpdateAudioAttributes
  13. // /frameworks/base/media/java/android/media/MediaPlayer.java
  14. public void setAudioAttributes(AudioAttributes attributes) // 皆是正在mediaplayer creat的时分挪用的
  15. // 再看看playerEvent的挪用
  16. // frameworks/base/services/core/java/com/android/server/audio/AudioService.java
  17. public void playerEvent(int piid, int event)
  18. // frameworks/base/media/java/android/media/PlayerBase.java
  19. private void updateState(int state)
  20. baseStart() & basePause() & baseStop() 别离收收start / stop / pause动静
  21. // frameworks/base/media/java/android/media/AudioTrack.java
  22. private void startImpl()
  23. // frameworks/base/media/java/android/media/MediaPlayer.java
  24. private void startImpl() {
  25. // playerbase便是一个接心类,完成了APP经由过程mediaplayer播放仍是经由过程audiotrack播放,皆能把动静传给PlaybackActivityMonitor
复造代码

多个APP复用通路,怎样支到告诉:

output复用逻辑:

  1. // /frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
  2. status_t AudioPolicyManager::checkOutputsForDevice(device, state,outputs) {
  3.     status_t status = dupOutputDesc->openDuplicating(mPrimaryOutput, desc,
  4.                    &duplicatedOutput);
  5. }
  6. //frameworks/av/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
  7. status_t SwAudioOutputDescriptor::openDuplicating(output1,output2,*ioHandle) {
  8.     *ioHandle = mClientInterface->openDuplicateOutput(output2->mIoHandle, output1->mIoHandle);
  9. }
  10. // frameworks/av/services/audiopolicy/service/AudioPolicyClientImpl.cpp
  11. audio_io_handle_t AudioPolicyService::AudioPolicyClient::openDuplicateOutput(
  12.        audio_io_handle_t output1, audio_io_handle_t output2) {
  13.     sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
  14.     return af->openDuplicateOutput(output1, output2);
  15.     // audioflinger中会把那两个output参加统一个复用thread内里(DuplicatingThread)
  16. }
复造代码
output复用皆是正在native层做处置,可是playbackcallback的检测皆是正在java层,以是复用没有会影响动静领受。只需有一个audiotrack/mediaplayer播放,playbackmonitor便会收收动静。

input复用逻辑:

  1. // frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
  2. audio_io_handle_t AudioPolicyManager::getInputForDevice(......) {
  3.     sp<IOProfile> profile;
  4.     profile = getInputProfile(device, profileSamplingRate, profileFormat,
  5.               profileChannelMask,profileFlags);
  6.     if (!profile->canOpenNewIo()|| (curInputCount >= 2)) { // 需求复用
  7.         for (size_t i = 0; i < mInputs.size(); ) {
  8.             sp<AudioInputDescriptor> desc = mInputs.valueAt(i);
  9.             RecordClientVector clients = desc->clientsList();
  10.             for (const auto& client : clients) {
  11.                 if (client->active() && client->appState() != APP_STATE_IDLE) {
  12.                     ALOGD("%s() resue input: %d", __func__, desc->mIoHandle);
  13.                     return desc->mIoHandle; // 复用胜利
  14.                 }
  15.             }
  16.         }
  17.     }
  18.     // 前面是假如没有复用,翻开新input的逻辑
  19. }
复造代码
record的复用是正在AudioManager内里判定的,假如复用则两个app的灌音会复用统一个AudioInputDescriptor。record callback的动静也是从AudioInputDescriptor收回的,那末call back能够辨别是哪一个app的动静吗?
  1. void AudioInputDescriptor::setAppState(audio_port_handle_t portId, app_state_t state) {
  2.     RecordClientVector clients = clientsList(false /*activeOnly*/);
  3.     RecordClientVector updatedClients;
  4.     for (const auto& client : clients) {
  5.         if (portId == client->portId()) {
  6.             bool wasSilenced = client->isSilenced();
  7.             client->setAppState(state);
  8.             if (client->active() && wasSilenced != client->isSilenced()) {
  9.                 updatedClients.push_back(client);
  10.             }
  11.         }
  12.     }
  13.     checkSuspendEffects();
  14.     for (const auto& client : updatedClients) {
  15.         updateClientRecordingConfiguration(RECORD_CONFIG_EVENT_UPDATE, client);
  16.     }
  17. }
复造代码
audiopolicyservice内里寄存了每一个client的疑息: mAudioRecordClients。当有变化时,会挪用对每一个client挪用setAppState,此处会带上client的portId,以是AudioInputDescriptor也能够辨别具体是哪一个client,因而即便多个APP复用了input通路,record callback也能够一般的领受到每一个app的动静。

免责声明:假如进犯了您的权益,请联络站少,我们会实时删除侵权内乱容,感谢协作!
1、本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,按照目前互联网开放的原则,我们将在不通知作者的情况下,转载文章;如果原文明确注明“禁止转载”,我们一定不会转载。如果我们转载的文章不符合作者的版权声明或者作者不想让我们转载您的文章的话,请您发送邮箱:Cdnjson@163.com提供相关证明,我们将积极配合您!
2、本网站转载文章仅为传播更多信息之目的,凡在本网站出现的信息,均仅供参考。本网站将尽力确保所提供信息的准确性及可靠性,但不保证信息的正确性和完整性,且不对因信息的不正确或遗漏导致的任何损失或损害承担责任。
3、任何透过本网站网页而链接及得到的资讯、产品及服务,本网站概不负责,亦不负任何法律责任。
4、本网站所刊发、转载的文章,其版权均归原作者所有,如其他媒体、网站或个人从本网下载使用,请在转载有关文章时务必尊重该文章的著作权,保留本网注明的“稿件来源”,并自负版权等法律责任。
回复 关闭延时

使用道具 举报

 
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则