Android N的Audio系统(二)
来源:互联网 发布:ubuntu 安装terminal 编辑:程序博客网 时间:2024/06/03 17:31
Android 音频框架概述
转http://www.sohu.com/a/139005560_468731
Audio 是整个 Android 平台非常重要的一个组成部分,负责音频数据的采集和输出、音频流的控制、音频设备的管理、音量调节等,主要包括如下部分:
Audio Application Framework:音频应用框架 AudioTrack:负责回放数据的输出,属 Android 应用框架 API 类 AudioRecord:负责录音数据的采集,属 Android 应用框架 API 类 AudioSystem: 负责音频事务的综合管理,属 Android 应用框架 API 类Audio Native Framework:音频本地框架 AudioTrack:负责回放数据的输出,属 Android 本地框架 API 类 AudioRecord:负责录音数据的采集,属 Android 本地框架 API 类 AudioSystem: 负责音频事务的综合管理,属 Android 本地框架 API 类Audio Services:音频服务 AudioPolicyService:音频策略的制定者,负责音频设备切换的策略抉择、音量调节策略等 AudioFlinger:音频策略的执行者,负责输入输出流设备的管理及音频流数据的处理传输Audio HAL:音频硬件抽象层,负责与音频硬件设备的交互,由 AudioFlinger 直接调用
与 Audio 强相关的有 MultiMedia,MultiMedia 负责音视频的编解码,MultiMedia 将解码后的数据通过 AudioTrack 输出,而 AudioRecord 采集的录音数据交由 MultiMedia 进行编码。
本文分析基于 Android 7.0 - Nougat。
- AudioTrack API 概述
播放声音可以使用 MediaPlayer 和 AudioTrack,两者都提供 Java API 给应用开发者使用。两者的差别在于:MediaPlayer 可以播放多种格式的音源,如 mp3、flac、wma、ogg、wav 等,而 AudioTrack 只能播放解码后的 PCM 数据流。从上面 Android 音频系统架构图来看:MediaPlayer 在 Native 层会创建对应的音频解码器和一个 AudioTrack,解码后的数据交由 AudioTrack 输出。所以 MediaPlayer 的应用场景更广,一般情况下使用它也更方便;只有一些对声音时延要求非常苛刻的应用场景才需要用到 AudioTrack。
- AudioTrack Java API
AudioTrack Java API 音频流类型:
Android 为什么要定义这么多的流类型?这与 Android 的音频管理策略有关,例如:
音频流的音量管理,调节一个类型的音频流音量,不会影响到其他类型的音频流根据流类型选择合适的输出设备;比如插着有线耳机期间,音乐声(STREAM_MUSIC)只会输出到有线耳机,而铃声(STREAM_RING)会同时输出到有线耳机和外放
这些属于 AudioPolicyService 的内容,本文不展开分析了。应用开发者应该根据应用场景选择相应的流类型,以便系统为这道流选择合适的输出设备。
一个 AudioTrack Java API 的测试例子(MODE_STREAM 模式):
详细说明下 getMinBufferSize() 接口,字面意思是返回最小数据缓冲区的大小,它是声音能正常播放的最低保障,从函数参数来看,返回值取决于采样率、采样深度、声道数这三个属性。MODE_STREAM 模式下,应用程序重点参考其返回值然后确定分配多大的数据缓冲区。如果数据缓冲区分配得过小,那么播放声音会频繁遭遇 underrun,underrun 是指生产者(AudioTrack)提供数据的速度跟不上消费者(AudioFlinger::PlaybackThread)消耗数据的速度,反映到现实的后果就是声音断续卡顿,严重影响听觉体验。
//AudioTrack.java /** * Returns the estimated minimum buffer size required for an AudioTrack * object to be created in the {@link #MODE_STREAM} mode. * The size is an estimate because it does not consider either the route or the sink, * since neither is known yet. Note that this size doesn't * guarantee a smooth playback under load, and higher values should be chosen according to * the expected frequency at which the buffer will be refilled with additional data to play. * For example, if you intend to dynamically set the source sample rate of an AudioTrack * to a higher value than the initial source sample rate, be sure to configure the buffer size * based on the highest planned sample rate. * @param sampleRateInHz the source sample rate expressed in Hz. * {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} is not permitted. * @param channelConfig describes the configuration of the audio channels. * See {@link AudioFormat#CHANNEL_OUT_MONO} and * {@link AudioFormat#CHANNEL_OUT_STEREO} * @param audioFormat the format in which the audio data is represented. * See {@link AudioFormat#ENCODING_PCM_16BIT} and * {@link AudioFormat#ENCODING_PCM_8BIT}, * and {@link AudioFormat#ENCODING_PCM_FLOAT}. * @return {@link #ERROR_BAD_VALUE} if an invalid parameter was passed, * or {@link #ERROR} if unable to query for output properties, * or the minimum buffer size expressed in bytes. */ static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) { int channelCount = 0; switch(channelConfig) { case AudioFormat.CHANNEL_OUT_MONO: case AudioFormat.CHANNEL_CONFIGURATION_MONO: channelCount = 1; break; case AudioFormat.CHANNEL_OUT_STEREO: case AudioFormat.CHANNEL_CONFIGURATION_STEREO: channelCount = 2; break; default: if (!isMultichannelConfigSupported(channelConfig)) { loge("getMinBufferSize(): Invalid channel configuration."); return ERROR_BAD_VALUE; } else { channelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig); } } if (!AudioFormat.isPublicEncoding(audioFormat)) { loge("getMinBufferSize(): Invalid audio format."); return ERROR_BAD_VALUE; } // sample rate, note these values are subject to change // Note: AudioFormat.SAMPLE_RATE_UNSPECIFIED is not allowed if ( (sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN) || (sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) ) { loge("getMinBufferSize(): " + sampleRateInHz + " Hz is not a supported sample rate."); return ERROR_BAD_VALUE; } int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat); if (size <= 0) { loge("getMinBufferSize(): error querying hardware"); return ERROR; } else { return size; } }
//android_media_audiotrack.cpp// ----------------------------------------------------------------------------// returns the minimum required size for the successful creation of a streaming AudioTrack// returns -1 if there was an error querying the hardware.static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env, jobject thiz, jint sampleRateInHertz, jint channelCount, jint audioFormat) { size_t frameCount; const status_t status = AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT, sampleRateInHertz); if (status != NO_ERROR) { ALOGE("AudioTrack::getMinFrameCount() for sample rate %d failed with status %d", sampleRateInHertz, status); return -1; } const audio_format_t format = audioFormatToNative(audioFormat); if (audio_has_proportional_frames(format)) { const size_t bytesPerSample = audio_bytes_per_sample(format); return frameCount * channelCount * bytesPerSample; } else { return frameCount; }}
可见最小缓冲区的大小 = 最低帧数 声道数 采样深度,(采样深度以字节为单位),到这里大家应该有所明悟了吧,在视频中,如果帧数过低,那么画面会有卡顿感,对于音频,道理也是一样的。最低帧数如何求得,我们到 native 层再解释。
关于 MediaPlayer、AudioTrack,更多更详细的 API 接口说明请参考 Android Developer:
MediaPlayer:AudioTrack:
- AudioTrack Native API
AudioTrack Native API 四种数据传输模式:
- Android N的Audio系统(二)
- Android N的Audio系统(一)
- Android N的Audio系统(三)
- Android N的Audio系统(四)
- Android N的Audio系统(五)
- Android N的Audio系统(六)
- 【转】Android的Audio系统(二)
- Android的Audio系统之二
- Android的Audio系统
- Android的Audio系统
- Android的Audio系统
- Android的Audio 系统
- android的audio系统
- Android的Audio系统
- Android的Audio系统
- Android的Audio系统
- Android的Audio系统
- Android的Audio系统
- hdu 6141 I am your Father!(最小树形图+权值编码)
- Eclipse怎么搭建Spring环境
- 【数据结构和算法】Day 1
- 【JSON】页面解析详细介绍
- 洛谷p1092合唱队形
- Android N的Audio系统(二)
- RabbitMQ消息队列+spring监听mq服务器多个ip,接收消费mq消息(二)
- 51nod1347 旋转字符串
- (四)初探反应器(event_base)
- 记一次Http问题排查
- jsp与数据库
- [caffe笔记007]:在新版caffe中实现Holistically-Nested Edge Detection
- jQuery--动画篇
- Photographic Image Synthesis with Cascaded Refinement Networks(由语义分割图生成逼真街景图)