好的这是一个关于 Android 音频编解码的全面介绍涵盖基本概念、原理和例子。一、 基本概念音频编解码 (Audio Codec)◦ 编解码 是 编码 (Encode) 和 解码 (Decode) 的合称。◦ 编码将原始的、未压缩的音频数据如 PCM转换为压缩的、更小体积的数据格式如 MP3, AAC。目的是减小存储空间和传输带宽。◦ 解码将压缩的音频数据转换回或近似原始的、可以被扬声器播放的格式。目的是播放音频。关键数据格式◦ PCM (Pulse Code Modulation, 脉冲编码调制)▪ 定义是数字音频的“原始”或“无损”表示形式直接从模拟信号通过采样、量化得到。它没有经过压缩因此文件体积很大。 ▪ 特点它是所有数字音频处理如混音、特效的基础格式。音频在设备内部处理和硬件驱动之间传输时通常是 PCM 格式。◦ 压缩音频格式如 MP3, AAC, OGG, FLAC 等。它们是在 PCM 基础上通过特定算法压缩后的格式用于存储和网络传输。Android 中的核心类◦ MediaPlayer高级 API用于播放音频/视频。它内部处理了解码、同步等复杂任务开发者只需提供文件路径或 URI。主要用于播放压缩格式的音频文件或网络流。◦ MediaRecorder高级 API用于录制音频/视频。它内部处理了编码、文件封装等任务。◦ AudioTrack低级 API用于播放 PCM 音频流。开发者需要提供 PCM 数据缓冲区由 AudioTrack 写入音频硬件。它只处理播放不负责解码。◦ AudioRecord低级 API用于录制 PCM 音频流。它从音频硬件麦克风采集原始的 PCM 数据。它只负责采集不负责编码。◦ MediaCodec最核心的低级编解码 API。用于对原始数据如 PCMYUV进行编码或解码。它是连接压缩数据与原始数据的桥梁。二、 原理与流程在 Android 中完整的音频处理流程通常涉及以上类的组合。音频播放流程 (解码过程)对于一个压缩音频文件如 MP3的播放原理如下[压缩文件: e.g., MP3] --(输入)– [MediaCodec 解码器] --(输出)– [PCM 数据] --(写入)– [AudioTrack] --(播放)– [扬声器]• 步骤 1解封装与解码MediaPlayer 内部或开发者自己使用 MediaExtractor 和 MediaCodec 协作完成。◦ MediaExtractor 从 MP4/MP3 等容器中分离出音频轨道。 ◦ MediaCodec配置为解码模式接收压缩的音频帧如 AAC 帧将其解码为原始的 PCM 数据。• 步骤 2播放 PCM解码得到的 PCM 数据被送入 AudioTrack由 AudioTrack 管理音频缓冲区并将数据推送至底层音频硬件如 HAL进行数模转换和放大最终驱动扬声器发声。简单使用用 MediaPlayer 时步骤 1 和 2 被自动完成。高级/自定义使用开发者可以使用 MediaCodec AudioTrack 手动控制解码和播放过程以实现音频特效、可视化或极低延迟播放。音频录制流程 (编码过程)录制音频并保存为压缩文件如 AAC原理如下[麦克风] --(采集)– [AudioRecord] --(输出)– [PCM 数据] --(输入)– [MediaCodec 编码器] --(输出)– [压缩数据: e.g., AAC] --(写入)– [文件/网络]• 步骤 1采集 PCMAudioRecord 从麦克风硬件采集原始的 PCM 音频数据。• 步骤 2编码采集到的 PCM 数据被送入 MediaCodec配置为编码模式编码器将其压缩为指定的格式如 AAC。• 步骤 3封装与写入编码后的压缩数据如 AAC 帧被 MediaMuxer 封装到特定的容器格式如 MP4中并写入文件。简单使用用 MediaRecorder 时步骤 1、2、3 被自动完成。高级/自定义使用开发者可以使用 AudioRecord MediaCodec MediaMuxer 手动控制流程以实现音频预处理、自定义编码参数或流式传输。三、 例子这里给出使用低级 API (AudioTrack, MediaCodec) 进行解码播放的简化版代码框架以展示原理。场景解码一个 MP3 文件并用 AudioTrack 播放。// 注意此为示意代码省略了异常处理、资源释放、同步等细节。// 1. 创建并配置 MediaExtractor定位音频轨道MediaExtractor extractor new MediaExtractor();extractor.setDataSource(“/sdcard/music.mp3”);int audioTrackIndex selectAudioTrack(extractor); // 选择音频轨道的辅助函数extractor.selectTrack(audioTrackIndex);// 2. 从 Extractor 获取音频格式包含 MIME 类型如 “audio/mp4a-latm”MediaFormat format extractor.getTrackFormat(audioTrackIndex);String mime format.getString(MediaFormat.KEY_MIME);// 3. 创建解码器 (MediaCodec)MediaCodec decoder MediaCodec.createDecoderByType(mime);decoder.configure(format, null, null, 0); // 配置为解码模式decoder.start();// 4. 创建 AudioTrack 用于播放解码后的 PCM// 从 format 中获取音频参数int sampleRate format.getInteger(MediaFormat.KEY_SAMPLE_RATE);int channelCount format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);int channelConfig (channelCount 1) ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO;int audioFormat AudioFormat.ENCODING_PCM_16BIT; // 常见格式int bufferSize AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);AudioTrack audioTrack new AudioTrack(AudioManager.STREAM_MUSIC,sampleRate,channelConfig,audioFormat,bufferSize,AudioTrack.MODE_STREAM);audioTrack.play();// 5. 解码循环boolean sawInputEOS false;boolean sawOutputEOS false;ByteBuffer[] inputBuffers decoder.getInputBuffers(); // API 21 后使用 getInput/OutputBufferByteBuffer[] outputBuffers decoder.getOutputBuffers();while (!sawOutputEOS) {// 5.1 将压缩数据送入解码器输入缓冲区if (!sawInputEOS) {int inputBufferIndex decoder.dequeueInputBuffer(TIMEOUT_US);if (inputBufferIndex 0) {ByteBuffer inputBuffer inputBuffers[inputBufferIndex];int sampleSize extractor.readSampleData(inputBuffer, 0);if (sampleSize 0) { // 已读完sawInputEOS true;decoder.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);} else {long presentationTimeUs extractor.getSampleTime();decoder.queueInputBuffer(inputBufferIndex, 0, sampleSize, presentationTimeUs, 0);extractor.advance(); // 移动到下一帧}}}// 5.2 从解码器输出缓冲区获取解码后的 PCM 数据 MediaCodec.BufferInfo bufferInfo new MediaCodec.BufferInfo(); int outputBufferIndex decoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_US); if (outputBufferIndex 0) { if ((bufferInfo.flags MediaCodec.BUFFER_FLAG_END_OF_STREAM) ! 0) { sawOutputEOS true; } if (bufferInfo.size 0) { ByteBuffer outputBuffer outputBuffers[outputBufferIndex]; // 将 PCM 数据写入 AudioTrack byte[] pcmData new byte[bufferInfo.size]; outputBuffer.get(pcmData); outputBuffer.clear(); // 必须清空或重置位置 audioTrack.write(pcmData, 0, pcmData.length); } decoder.releaseOutputBuffer(outputBufferIndex, false); }}// 6. 清理资源audioTrack.stop();audioTrack.release();decoder.stop();decoder.release();extractor.release();总结• 高级API (MediaPlayer, MediaRecorder): 简单、快捷适合大多数播放/录制场景。• 低级API (AudioTrack, AudioRecord, MediaCodec): 灵活、强大允许开发者深入控制音频流水线的每一个环节适用于需要低延迟、实时处理、自定义编解码或特殊文件格式的场景。理解它们之间的数据流转PCM - 压缩数据是掌握 Android 音频处理的关键。