在 Android 多媒体框架中,MediaMuxer 负责将编码后的音频、视频数据封装到容器文件(如 MP4、WebM 等)中。其调用流程涉及 轨道添加、数据同步、格式配置 等关键步骤。以下是其详细调用流程及核心实现机制:
MediaFormat
)。// 创建 MediaMuxer,指定输出格式(如 MP4)和输出文件
MediaMuxer muxer = new MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
底层动作:
MPEG4Writer
(或其他格式的 Writer)。ftyp
和 moov
原子)。在编码器(如 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()
前添加所有轨道。从编码器的输出缓冲区获取数据,并按时间戳写入:
// 示例:写入视频数据
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);
}
关键点:
MediaFormat
中传递,无需重复写入。writeSampleData
,避免多线程竞争。muxer.stop();
muxer.release();
底层动作:
moov
原子(包含音视频轨道的元数据和索引)。MPEG4Writer
实现,负责按 ISO BMFF 标准生成 ftyp
, moov
, mdat
等原子。WebmWriter
处理,遵循 WebM 规范。MediaMuxer
内部维护 List
,记录各轨道的格式和写入状态。addTrack()
时检查 MediaFormat
的必需参数。presentationTimeUs
转换为容器的时间基准(如 MP4 的 timescale)。MediaFormat
缺少关键参数(如视频的 csd-0
)。INFO_OUTPUT_FORMAT_CHANGED
后再获取 MediaFormat
。stop()
或异常退出导致 moov
原子未生成。try-finally
确保 stop()
被调用:try {
muxer.start();
// ... 写入数据
} finally {
muxer.stop();
muxer.release();
}
writeSampleData
,避免阻塞编码线程。writeSampleData
// 错误:未调用 start() 前写入数据
muxer.addTrack(videoFormat);
muxer.writeSampleData(...); // 抛出 IllegalStateException
// 错误:重复添加同一轨道
muxer.addTrack(videoFormat);
muxer.addTrack(videoFormat); // 抛出 IllegalStateException
// 错误:视频 PTS 非单调递增
bufferInfo.presentationTimeUs = 1000;
muxer.writeSampleData(...);
bufferInfo.presentationTimeUs = 500; // 可能导致文件无法播放
MediaMuxer 的调用流程为:初始化 → 添加轨道 → 启动 → 写入数据 → 停止释放。关键注意事项包括:
INFO_OUTPUT_FORMAT_CHANGED
)。stop()
以生成完整的容器文件。开发者应结合 MediaCodec
和 MediaMuxer
实现高效的音视频封装,适用于录制、转码等场景。
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();
MediaRecorder
初始化:
StagefrightRecorder
(实际处理录制的核心类)。AudioSource
,负责与 AudioFlinger
交互。AudioSource
的工作:
AudioRecord
与 AudioFlinger
通信,建立录音会话。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);
}
}
MediaCodec
)编码器初始化:
StagefrightRecorder
根据配置的编码格式(如 AAC)创建编码器实例。MediaCodec
创建软件或硬件编码器(如 OMX.google.aac.encoder
或厂商实现的编码器)。编码流程:
AudioSource
的缓冲区传递给编码器输入队列。MediaMuxer
。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);
}
StagefrightRecorder
frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp
AudioSource
、MediaCodec
、MediaMuxer
的协作。AudioSource
frameworks/av/media/libmediaplayerservice/AudioSource.cpp
AudioRecord
,管理 PCM 数据的采集。MediaCodec
)。MediaCodec
frameworks/av/media/libstagefright/MediaCodec.cpp
MPEG4Writer
/ WebmWriter
frameworks/av/media/libstagefright/MPEG4Writer.cpp
ftyp
, moov
, mdat
)。AudioRecord
直接控制:绕过 MediaRecorder
,手动管理 AudioRecord
和 MediaCodec
。getMinBufferSize()
计算最小缓冲区,减少延迟。MediaCodecInfo.isHardwareAccelerated()
为 true 的编码器。MediaCodec
状态机:处理 INFO_TRY_AGAIN_LATER
和 ERROR_END_OF_STREAM
。MediaMuxer.stop()
被调用,生成完整的容器文件。Android 框架层从麦克风录音到生成 AAC 文件的流程如下:
AudioFlinger
通过 RecordThread
从 HAL 读取 PCM 数据。MediaCodec
将 PCM 编码为 AAC。MediaMuxer
将 AAC 数据写入容器(如 MP4 或裸流)。关键类:
AudioFlinger
:管理音频硬件输入。StagefrightRecorder
:协调录制流程。MediaCodec
:实现音频编码。MediaMuxer
:处理文件封装。开发者可通过定制 AudioRecord
和 MediaCodec
实现更灵活的音频处理逻辑(如实时降噪),或通过 MediaRecorder
快速生成标准音频文件。