android 媒体框架
1 MediaMuxer
在 Android 多媒体框架中,MediaMuxer 负责将编码后的音频、视频数据封装到容器文件(如 MP4、WebM 等)中。其调用流程涉及 轨道添加、数据同步、格式配置 等关键步骤。以下是其详细调用流程及核心实现机制:
1.1 MediaMuxer 的核心职责
- 容器封装:将编码后的音频(如 AAC)和视频(如 H.264)数据按容器格式(如 MP4)写入文件。
- 轨道管理:支持添加多个音视频轨道,并维护各轨道的格式信息(
MediaFormat
)。 - 时间戳同步:确保音视频数据的 PTS(Presentation Time Stamp) 按容器要求对齐。
1.2. 核心调用流程
(1) 初始化 MediaMuxer
// 创建 MediaMuxer,指定输出格式(如 MP4)和输出文件
MediaMuxer muxer = new MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
底层动作:
- 初始化底层
MPEG4Writer
(或其他格式的 Writer)。 - 创建文件句柄,准备写入容器头部(如 MP4 的
ftyp
和moov
原子)。
(2) 添加音视频轨道
在编码器(如 MediaCodec
)输出格式确定后,添加轨道:
// 从编码器获取 MediaFormat(需包含 CSD-0/CSD-1 等格式参数)
MediaFormat audioFormat = audioEncoder.getOutputFormat();
MediaFormat videoFormat = videoEncoder.getOutputFormat();// 添加轨道并记录轨道索引
int audioTrackIndex = muxer.addTrack(audioFormat);
int videoTrackIndex = muxer.addTrack(videoFormat);// 必须调用 start() 后才能写入数据
muxer.start();
关键约束:
- 格式参数必须完整:如音频的
sample-rate
、channel-count
,视频的width
、height
、csd-0
(如 H.264 的 SPS/PPS)。 - 轨道顺序无关:但需在
start()
前添加所有轨道。
(3) 写入编码数据
从编码器的输出缓冲区获取数据,并按时间戳写入:
// 示例:写入视频数据
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = videoEncoder.dequeueOutputBuffer(bufferInfo, TIMEOUT_US);
if (outputBufferIndex >= 0) {ByteBuffer encodedData = videoEncoder.getOutputBuffer(outputBufferIndex);if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {// 编码配置信息(如 SPS/PPS)已通过 addTrack 传递,无需重复写入bufferInfo.size = 0;}if (bufferInfo.size > 0) {// 调整时间戳基址(如从 0 开始)bufferInfo.presentationTimeUs = computePresentationTimeUs();// 写入数据到对应轨道muxer.writeSampleData(videoTrackIndex, encodedData, bufferInfo);}videoEncoder.releaseOutputBuffer(outputBufferIndex, false);
}
关键点:
- 时间戳对齐:音频和视频的 PTS 必须基于同一时间基准。
- BUFFER_FLAG_CODEC_CONFIG:编码器初始化的配置数据(如 H.264 的 SPS/PPS)通常已在
MediaFormat
中传递,无需重复写入。 - 线程安全:建议在单线程中调用
writeSampleData
,避免多线程竞争。
(4) 停止并释放资源
muxer.stop();
muxer.release();
底层动作:
- 生成容器尾部:如 MP4 的
moov
原子(包含音视频轨道的元数据和索引)。 - 关闭文件句柄:确保数据完全写入磁盘。
1.3. 实现原理与核心源码
(1) 容器格式处理
- MP4 封装:由
MPEG4Writer
实现,负责按 ISO BMFF 标准生成ftyp
,moov
,mdat
等原子。 - WebM 封装:由
WebmWriter
处理,遵循 WebM 规范。
(2) 轨道管理
- 轨道索引映射:
MediaMuxer
内部维护List<Track>
,记录各轨道的格式和写入状态。 - 格式校验:在
addTrack()
时检查MediaFormat
的必需参数。
(3) 时间戳同步
- PTS 转换:将输入的
presentationTimeUs
转换为容器的时间基准(如 MP4 的 timescale)。 - 乱序处理:某些容器(如 MP4)要求样本按时间戳递增顺序写入,需内部缓存排序。
1.4. 关键问题与优化
(1) 轨道添加失败
- 原因:
MediaFormat
缺少关键参数(如视频的csd-0
)。 - 解决:确保在编码器输出
INFO_OUTPUT_FORMAT_CHANGED
后再获取MediaFormat
。
(2) 文件损坏或无数据
- 原因:未调用
stop()
或异常退出导致moov
原子未生成。 - 解决:使用
try-finally
确保stop()
被调用:try {muxer.start();// ... 写入数据 } finally {muxer.stop();muxer.release(); }
(3) 性能优化
- 批量写入:合并多个样本后一次性写入,减少 IO 次数。
- 异步写入:在独立线程中处理
writeSampleData
,避免阻塞编码线程。
1.5. 完整调用流程图
1.6. 典型错误案例
(1) 提前调用 writeSampleData
// 错误:未调用 start() 前写入数据
muxer.addTrack(videoFormat);
muxer.writeSampleData(...); // 抛出 IllegalStateException
(2) 重复添加轨道
// 错误:重复添加同一轨道
muxer.addTrack(videoFormat);
muxer.addTrack(videoFormat); // 抛出 IllegalStateException
(3) 时间戳跳跃
// 错误:视频 PTS 非单调递增
bufferInfo.presentationTimeUs = 1000;
muxer.writeSampleData(...);
bufferInfo.presentationTimeUs = 500; // 可能导致文件无法播放
总结
MediaMuxer 的调用流程为:初始化 → 添加轨道 → 启动 → 写入数据 → 停止释放。关键注意事项包括:
- 轨道添加时机:必须在编码器输出格式确定后(收到
INFO_OUTPUT_FORMAT_CHANGED
)。 - 时间戳管理:确保 PTS 单调递增且音视频同步。
- 资源释放:必须调用
stop()
以生成完整的容器文件。
开发者应结合 MediaCodec
和 MediaMuxer
实现高效的音视频封装,适用于录制、转码等场景。
2.录音生成aac文件
2.1. 整体流程
2.2. 框架层核心组件与流程
(1) 应用层调用 MediaRecorder
应用通过 MediaRecorder
配置录音参数并启动录制:
MediaRecorder recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.AAC_ADTS); // 裸AAC流
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
recorder.setOutputFile("output.aac");
recorder.prepare();
recorder.start();
- 关键配置:音频源(MIC)、输出格式(AAC_ADTS 或 MPEG_4)、编码格式(AAC)。
(2) 框架层初始化音频输入
-
MediaRecorder
初始化:- 在 Native 层创建
StagefrightRecorder
(实际处理录制的核心类)。 - 初始化
AudioSource
,负责与AudioFlinger
交互。
- 在 Native 层创建
-
AudioSource
的工作:- 通过
AudioRecord
与AudioFlinger
通信,建立录音会话。 - 分配共享内存缓冲区,用于接收来自 HAL 的 PCM 数据。
- 通过
(3) 音频数据采集(AudioFlinger
)
-
AudioFlinger
的角色:- 系统级音频服务,管理所有音频输入/输出设备。
- 创建
RecordThread
,从 HAL 层读取麦克风的原始 PCM 数据。
-
数据流路径:
// frameworks/av/services/audioflinger/Threads.cpp void AudioFlinger::RecordThread::threadLoop() {while (true) {// 从 HAL 读取 PCM 数据inputStream->read(buffer, bufferSize);// 将数据传递给客户端(如 AudioSource)mClient->copyBuffer(buffer, bufferSize);} }
(4) 音频编码(MediaCodec
)
-
编码器初始化:
StagefrightRecorder
根据配置的编码格式(如 AAC)创建编码器实例。- 通过
MediaCodec
创建软件或硬件编码器(如OMX.google.aac.encoder
或厂商实现的编码器)。
-
编码流程:
- PCM 数据从
AudioSource
的缓冲区传递给编码器输入队列。 - 编码器将 PCM 转换为 AAC 格式的压缩数据包。
- 输出队列中的 AAC 数据被提取并传递给
MediaMuxer
。
- PCM 数据从
(5) 文件封装(MediaMuxer
)
-
封装逻辑:
- 若输出格式为
AAC_ADTS
,直接写入 AAC 原始流(需添加 ADTS 头)。 - 若输出格式为
MPEG_4
,使用MPEG4Writer
封装为 MP4 文件,包含moov
(元数据)和mdat
(音频数据)原子。
- 若输出格式为
-
关键代码路径:
// frameworks/av/media/libstagefright/MPEG4Writer.cpp status_t MPEG4Writer::writeSampleData(...) {// 将 AAC 数据包写入 mdat 原子writeMdatData(buffer, size); }
2.3. 框架层关键类与交互
(1) StagefrightRecorder
- 位置:
frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp
- 职责:
- 协调
AudioSource
、MediaCodec
、MediaMuxer
的协作。 - 处理状态机(录制、暂停、停止)。
- 协调
(2) AudioSource
- 位置:
frameworks/av/media/libmediaplayerservice/AudioSource.cpp
- 职责:
- 封装
AudioRecord
,管理 PCM 数据的采集。 - 提供数据给编码器(
MediaCodec
)。
- 封装
(3) MediaCodec
- 位置:
frameworks/av/media/libstagefright/MediaCodec.cpp
- 职责:
- 封装编码器(如 AAC 编码器),处理 PCM 到 AAC 的转换。
- 支持同步/异步模式,管理输入/输出缓冲区。
(4) MPEG4Writer
/ WebmWriter
- 位置:
frameworks/av/media/libstagefright/MPEG4Writer.cpp
- 职责:
- 将编码后的 AAC 数据按容器格式(如 MP4)写入文件。
- 生成完整的文件结构(如
ftyp
,moov
,mdat
)。
2.4. 关键问题与优化
(1) 低延迟优化
- 使用
AudioRecord
直接控制:绕过MediaRecorder
,手动管理AudioRecord
和MediaCodec
。 - 缓冲区大小调整:通过
getMinBufferSize()
计算最小缓冲区,减少延迟。
(2) 编码格式兼容性
- 硬件编码支持:优先选择
MediaCodecInfo.isHardwareAccelerated()
为 true 的编码器。 - ADTS 头添加:若输出裸 AAC 流,需手动添加 ADTS 头(7 或 9 字节)。
(3) 错误处理
MediaCodec
状态机:处理INFO_TRY_AGAIN_LATER
和ERROR_END_OF_STREAM
。- 文件写入完整性:确保
MediaMuxer.stop()
被调用,生成完整的容器文件。
2.5. 总结
Android 框架层从麦克风录音到生成 AAC 文件的流程如下:
- 音频采集:
AudioFlinger
通过RecordThread
从 HAL 读取 PCM 数据。 - 编码处理:
MediaCodec
将 PCM 编码为 AAC。 - 文件封装:
MediaMuxer
将 AAC 数据写入容器(如 MP4 或裸流)。
关键类:
AudioFlinger
:管理音频硬件输入。StagefrightRecorder
:协调录制流程。MediaCodec
:实现音频编码。MediaMuxer
:处理文件封装。
开发者可通过定制 AudioRecord
和 MediaCodec
实现更灵活的音频处理逻辑(如实时降噪),或通过 MediaRecorder
快速生成标准音频文件。