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 快速生成标准音频文件。
