Android-Audio-编码和解码

好的,这是一个关于 Android 音频编解码的全面介绍,涵盖基本概念、原理和例子。

一、 基本概念

  1. 音频编解码 (Audio Codec):

    ◦ 编解码 是 编码 (Encode) 和 解码 (Decode) 的合称。

    ◦ 编码:将原始的、未压缩的音频数据(如 PCM)转换为压缩的、更小体积的数据格式(如 MP3, AAC)。目的是减小存储空间和传输带宽。

    ◦ 解码:将压缩的音频数据转换回(或近似)原始的、可以被扬声器播放的格式。目的是播放音频。

  2. 关键数据格式:

    ◦ PCM (Pulse Code Modulation, 脉冲编码调制):

    复制代码
    ▪   定义:是数字音频的"原始"或"无损"表示形式,直接从模拟信号通过采样、量化得到。它没有经过压缩,因此文件体积很大。
    
    ▪   特点:它是所有数字音频处理(如混音、特效)的基础格式。音频在设备内部处理和硬件驱动之间传输时,通常是 PCM 格式。

    ◦ 压缩音频格式:如 MP3, AAC, OGG, FLAC 等。它们是在 PCM 基础上,通过特定算法压缩后的格式,用于存储和网络传输。

  3. Android 中的核心类:

    ◦ MediaPlayer:高级 API,用于播放音频/视频。它内部处理了解码、同步等复杂任务,开发者只需提供文件路径或 URI。主要用于播放压缩格式的音频文件或网络流。

    ◦ MediaRecorder:高级 API,用于录制音频/视频。它内部处理了编码、文件封装等任务。

    ◦ AudioTrack:低级 API,用于播放 PCM 音频流。开发者需要提供 PCM 数据缓冲区,由 AudioTrack 写入音频硬件。它只处理播放,不负责解码。

    ◦ AudioRecord:低级 API,用于录制 PCM 音频流。它从音频硬件(麦克风)采集原始的 PCM 数据。它只负责采集,不负责编码。

    ◦ MediaCodec:最核心的低级编解码 API。用于对原始数据(如 PCM,YUV)进行编码或解码。它是连接压缩数据与原始数据的桥梁。

二、 原理与流程

在 Android 中,完整的音频处理流程通常涉及以上类的组合。

  1. 音频播放流程 (解码过程)

对于一个压缩音频文件(如 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 手动控制解码和播放过程,以实现音频特效、可视化或极低延迟播放。

  1. 音频录制流程 (编码过程)

录制音频并保存为压缩文件(如 AAC),原理如下:

麦克风\] --(采集)--\> \[AudioRecord\] --(输出)--\> \[PCM 数据\] --(输入)--\> \[MediaCodec 编码器\] --(输出)--\> \[压缩数据: e.g., AAC\] --(写入)--\> \[文件/网络

• 步骤 1:采集 PCM:AudioRecord 从麦克风硬件采集原始的 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/OutputBuffer

ByteBuffer[] 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 音频处理的关键。

相关推荐
dawudayudaxue2 小时前
Eclipse安卓环境配置
android·java·eclipse
曾经我也有梦想2 小时前
Day5 Kotlin 协程
android
00后程序员张2 小时前
iOS上架工具,AppUploader(开心上架)用于证书生成、描述文件管理Xcode用于应用构建
android·macos·ios·小程序·uni-app·iphone·xcode
2501_915921432 小时前
只有 IPA 没有源码时,如何给 iOS 应用做安全处理
android·安全·ios·小程序·uni-app·iphone·webview
恋猫de小郭2 小时前
Flutter 3.41 iOS 键盘负优化:一个代码洁癖引发的负优化
android·前端·flutter
十六年开源服务商3 小时前
2026年WordPress网站安全检测服务避坑指南
android·安全
一直向钱3 小时前
android stutio 安装Ai插件写代码
android
linchare3 小时前
[原创]如何排查php-fpm的502报错(SIGSEGV)
android