1. 项目概述在Android音视频开发领域音频采集与播放是最基础也是最重要的功能之一。作为一名长期从事Android底层开发的工程师我经常需要处理各种音频相关的需求。今天要分享的是如何在Native层直接使用AudioTrack和AudioRecord这两个核心类来实现低延迟的音频采集与播放。与Java层的实现相比Native层的AudioTrack和AudioRecord能够提供更低的延迟和更高的性能。这对于需要实时音频处理的应用场景如语音通话、音频直播、游戏音效等尤为重要。在本文中我将详细介绍如何在C代码中初始化这两个类并实现边录音边播放的功能。2. 环境准备与依赖配置2.1 开发环境要求要开发Native层的音频功能需要准备以下环境Android NDK开发环境建议使用最新稳定版Android源码环境至少需要包含frameworks/av目录支持C17的编译器Android设备或模拟器建议使用真机测试模拟器可能存在音频延迟问题2.2 项目依赖配置在Android.bp文件中需要添加以下依赖shared_libs: [ framework-permission-aidl-cpp, libaudioclient, libutils, libcutils, liblog, libbinder, ],这些依赖项分别提供了libaudioclient包含AudioTrack和AudioRecord的核心实现libutils提供Android基础工具类libcutils提供底层C工具函数liblog用于日志输出libbinder用于进程间通信framework-permission-aidl-cpp提供权限相关的AIDL接口3. AudioRecord初始化与配置3.1 AudioRecord核心参数解析AudioRecord的初始化需要配置以下关键参数音频源Audio SourceAUDIO_SOURCE_DEFAULT默认音频源AUDIO_SOURCE_MIC麦克风AUDIO_SOURCE_VOICE_COMMUNICATION语音通话专用AUDIO_SOURCE_VOICE_RECOGNITION语音识别专用采样率Sample Rate常用值8000Hz、16000Hz、44100Hz、48000Hz选择依据根据应用场景和硬件支持情况决定音频格式Audio FormatAUDIO_FORMAT_PCM_16_BIT16位PCMAUDIO_FORMAT_PCM_8_BIT8位PCMAUDIO_FORMAT_PCM_FLOAT浮点PCM声道配置Channel ConfigAUDIO_CHANNEL_IN_MONO单声道AUDIO_CHANNEL_IN_STEREO立体声缓冲区大小Buffer Size必须大于等于getMinBufferSize()返回的最小值过小会导致音频数据丢失过大会增加延迟3.2 初始化代码实现以下是完整的AudioRecord初始化代码#include media/AudioRecord.h #include utils/Log.h spAudioRecord AudioTest::initRecord() { // 获取最小帧数 size_t minFrameCount; status_t status AudioRecord::getMinFrameCount(minFrameCount, 16000, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_MONO); if (status ! NO_ERROR) { ALOGE(Failed to get min frame count, using default); minFrameCount 480 * 4; // 默认值 } // 创建AttributionSourceState std::string packageName{AudioCapture}; AttributionSourceState attributionSource; attributionSource.packageName packageName; attributionSource.token spBBinder::make(); // 创建AudioRecord实例 spAudioRecord audioRecord new AudioRecord(attributionSource); // 配置AudioRecord参数 status audioRecord-set(AUDIO_SOURCE_DEFAULT, 16000, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_MONO, minFrameCount); // 检查初始化状态 if (audioRecord-initCheck() ! NO_ERROR) { ALOGE(AudioRecord initialization failed); return nullptr; } // 开始录音 status audioRecord-start(); if (status ! NO_ERROR) { ALOGE(Failed to start AudioRecord); return nullptr; } return audioRecord; }3.3 注意事项与常见问题权限问题必须确保应用有RECORD_AUDIO权限在Android 10及以上版本还需要在AndroidManifest.xml中声明FOREGROUND_SERVICE权限缓冲区大小计算音频帧与字节数的转换公式字节数 帧数 × 声道数 × 每样本字节数例如单声道16位PCM每帧1×22字节采样率选择语音通话8000Hz或16000Hz足够音乐录制建议44100Hz或48000Hz注意不是所有设备都支持所有采样率4. AudioTrack初始化与配置4.1 AudioTrack核心参数解析AudioTrack的初始化需要配置以下关键参数流类型Stream TypeAUDIO_STREAM_MUSIC音乐播放AUDIO_STREAM_VOICE_CALL语音通话AUDIO_STREAM_SYSTEM系统声音采样率Sample Rate必须与AudioRecord的采样率一致否则会导致播放速度异常音频格式Audio Format必须与AudioRecord的格式一致否则会导致播放声音失真声道配置Channel ConfigAUDIO_CHANNEL_OUT_MONO单声道输出AUDIO_CHANNEL_OUT_STEREO立体声输出输出标志Output FlagsAUDIO_OUTPUT_FLAG_PRIMARY主输出设备AUDIO_OUTPUT_FLAG_FAST低延迟模式AUDIO_OUTPUT_FLAG_DEEP_BUFFER大缓冲区模式4.2 初始化代码实现以下是完整的AudioTrack初始化代码#include media/AudioTrack.h #include utils/Log.h spAudioTrack AudioTest::initPlayback() { // 创建AudioTrack实例 spAudioTrack audioTrack new AudioTrack( AUDIO_STREAM_MUSIC, 16000, AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_MONO, AUDIO_OUTPUT_FLAG_PRIMARY); // 检查初始化状态 if (audioTrack-initCheck() ! NO_ERROR) { ALOGE(AudioTrack initialization failed); return nullptr; } // 设置缓冲区大小与AudioRecord一致 audioTrack-setBufferSizeInFrames(minFrameCount); // 开始播放 status_t status audioTrack-start(); if (status ! NO_ERROR) { ALOGE(Failed to start AudioTrack); return nullptr; } return audioTrack; }4.3 输出模式选择建议低延迟场景如游戏音效使用AUDIO_OUTPUT_FLAG_FAST配合AUDIO_STREAM_VOICE_CALL流类型缓冲区大小尽可能小高质量音乐播放使用AUDIO_OUTPUT_FLAG_DEEP_BUFFER配合AUDIO_STREAM_MUSIC流类型缓冲区大小可以适当增大语音通话场景使用AUDIO_OUTPUT_FLAG_PRIMARY配合AUDIO_STREAM_VOICE_CALL流类型缓冲区大小适中5. 音频采集与播放实现5.1 核心流程设计边录音边播放的流程如下初始化AudioRecord和AudioTrack创建音频数据缓冲区循环读取音频数据并写入AudioTrack处理停止信号释放资源5.2 完整实现代码void AudioTest::startRecordAndPlay() { // 初始化AudioRecord spAudioRecord audioRecord initRecord(); if (audioRecord nullptr) { ALOGE(Failed to initialize AudioRecord); return; } // 初始化AudioTrack spAudioTrack audioTrack initPlayback(); if (audioTrack nullptr) { ALOGE(Failed to initialize AudioTrack); audioRecord-stop(); return; } // 计算缓冲区大小字节 size_t bufferSize minFrameCount * 2; // 单声道16位PCM // 创建音频缓冲区 std::unique_ptruint8_t[] audioBuffer(new uint8_t[bufferSize]); // 设置运行标志 mProcessRunningFlag true; // 主循环 while (mProcessRunningFlag) { // 读取音频数据 ssize_t readSize audioRecord-read(audioBuffer.get(), bufferSize); if (readSize 0) { // 写入AudioTrack ssize_t written audioTrack-write(audioBuffer.get(), readSize); // 日志记录 ALOGV(Read %zd bytes, wrote %zd bytes, readSize, written); // 处理写入不完整的情况 if (written readSize) { ALOGW(Partial write: %zd/%zd bytes, written, readSize); } } else if (readSize 0) { ALOGE(Error reading from AudioRecord: %zd, readSize); break; } } // 停止并释放资源 audioRecord-stop(); audioTrack-stop(); } void AudioTest::stopRecordAndPlay() { // 设置停止标志 mProcessRunningFlag false; }5.3 性能优化技巧缓冲区管理使用智能指针管理内存避免内存泄漏缓冲区大小应根据实际需求调整线程优先级音频处理线程应设置为高优先级可以使用pthread_setschedparam设置实时性保障减少循环内的非音频处理逻辑避免在音频线程中进行IO操作错误处理检查每次read/write的返回值处理部分写入的情况6. 常见问题与解决方案6.1 音频延迟问题症状录音和播放之间存在明显延迟解决方案检查AudioRecord和AudioTrack的缓冲区大小是否一致尝试使用更小的缓冲区使用AUDIO_OUTPUT_FLAG_FAST标志确保音频处理线程有足够的CPU资源6.2 音频失真问题症状播放的声音有杂音或失真解决方案检查AudioRecord和AudioTrack的采样率、格式是否匹配确保缓冲区大小足够大检查音频源质量尝试不同的音频格式如改用PCM_FLOAT6.3 权限相关问题症状录音功能无法正常工作解决方案检查是否声明了RECORD_AUDIO权限在运行时检查权限对于Android 10确保有前台服务权限检查音频源是否可用6.4 内存泄漏问题症状长时间运行后内存占用持续增加解决方案使用智能指针管理资源确保所有资源都被正确释放使用Android Studio的内存分析工具检查泄漏点7. 高级应用与扩展7.1 音频数据处理可以在读取和写入之间添加音频处理逻辑如回声消除噪声抑制音频编码/解码音效处理7.2 多线程设计对于复杂的音频应用建议采用多线程设计单独的线程负责音频采集单独的线程负责音频处理单独的线程负责音频播放使用线程安全队列传递数据7.3 低延迟优化要实现极低延迟的音频处理使用AAudio APIAndroid O及以上设置合适的线程优先级使用高性能音频路径如FastMixer优化缓冲区大小在实际项目中我发现Native层的AudioTrack和AudioRecord虽然性能更好但也更加复杂。特别是在处理不同Android版本的兼容性问题时需要做大量的测试和适配工作。建议在关键音频应用中加入详细的日志和性能监控以便及时发现和解决问题。