当前位置: 首页 > news >正文

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 的 ftypmoov 原子)。
(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-ratechannel-count,视频的 widthheightcsd-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. 完整调用流程图

App MediaMuxer MediaCodec new MediaMuxer(path, format) getOutputFormat() addTrack(audioFormat) addTrack(videoFormat) start() 输出缓冲区就绪 writeSampleData(trackIndex, buffer, info) loop [编码循环] stop() release() App MediaMuxer MediaCodec

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 的调用流程为:初始化 → 添加轨道 → 启动 → 写入数据 → 停止释放。关键注意事项包括:

  1. 轨道添加时机:必须在编码器输出格式确定后(收到 INFO_OUTPUT_FORMAT_CHANGED)。
  2. 时间戳管理:确保 PTS 单调递增且音视频同步。
  3. 资源释放:必须调用 stop() 以生成完整的容器文件。

开发者应结合 MediaCodecMediaMuxer 实现高效的音视频封装,适用于录制、转码等场景。

2.录音生成aac文件

2.1. 整体流程

App MediaRecorder AudioSource AudioFlinger HAL MediaCodec OMX Codec MediaMuxer 初始化配置(音频源、编码格式等) 创建音频输入源 请求音频流 从麦克风读取PCM数据 返回原始PCM数据 填充数据到缓冲区 传递PCM数据 编码为AAC 返回AAC数据包 写入封装文件 生成AAC/MP4文件 App MediaRecorder AudioSource AudioFlinger HAL MediaCodec OMX Codec MediaMuxer

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 交互。
  • AudioSource 的工作

    • 通过 AudioRecordAudioFlinger 通信,建立录音会话。
    • 分配共享内存缓冲区,用于接收来自 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
(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
  • 职责
    • 协调 AudioSourceMediaCodecMediaMuxer 的协作。
    • 处理状态机(录制、暂停、停止)。
(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,手动管理 AudioRecordMediaCodec
  • 缓冲区大小调整:通过 getMinBufferSize() 计算最小缓冲区,减少延迟。
(2) 编码格式兼容性
  • 硬件编码支持:优先选择 MediaCodecInfo.isHardwareAccelerated() 为 true 的编码器。
  • ADTS 头添加:若输出裸 AAC 流,需手动添加 ADTS 头(7 或 9 字节)。
(3) 错误处理
  • MediaCodec 状态机:处理 INFO_TRY_AGAIN_LATERERROR_END_OF_STREAM
  • 文件写入完整性:确保 MediaMuxer.stop() 被调用,生成完整的容器文件。

2.5. 总结

Android 框架层从麦克风录音到生成 AAC 文件的流程如下:

  1. 音频采集AudioFlinger 通过 RecordThread 从 HAL 读取 PCM 数据。
  2. 编码处理MediaCodec 将 PCM 编码为 AAC。
  3. 文件封装MediaMuxer 将 AAC 数据写入容器(如 MP4 或裸流)。

关键类

  • AudioFlinger:管理音频硬件输入。
  • StagefrightRecorder:协调录制流程。
  • MediaCodec:实现音频编码。
  • MediaMuxer:处理文件封装。

开发者可通过定制 AudioRecordMediaCodec 实现更灵活的音频处理逻辑(如实时降噪),或通过 MediaRecorder 快速生成标准音频文件。

http://www.xdnf.cn/news/357589.html

相关文章:

  • Android Handler 机制面试总结
  • 力扣刷题 每日四道
  • pandas中的数据聚合函数:`pivot_table` 和 `groupby`有啥不同?
  • 【项目中的流程管理(十一)】
  • MongoDB 创建索引原则
  • 设计模式-策略模式(Strategy Pattern)
  • 前端指南——项目代码结构解析(React为例)
  • 系统文件夹迁移与恢复
  • 系分论文《论多云架构治理的分析和应用》
  • 为人类文明建一座“永不遗忘”的数字博物馆:Funes 技术解析
  • 【计算机视觉】OpenCV项目实战:get_inverse_perspective:基于OpenCV的透视图转化为不同平面
  • 【LangChain全栈开发指南】从LLM集成到智能体系统构建
  • 【MYSQL错误连接太多】
  • 【智体OS】AI社交产品头榜赋能电商新零售:某品牌吹风机的智能营销实战案例
  • 解决 MySQL 数据库无法远程连接的问题
  • 一场陟遐自迩的 SwiftUI + CoreData 性能优化之旅(下)
  • 介质访问控制(MAC)
  • sqli-labs靶场18-22关(http头)
  • 数据分析与逻辑思维:六步解决业务难题;参考书籍《数据分析原理:6步解决业务分析难题 (周文全, 黄怡媛, 马炯雄)》
  • C# WinForm 如何高效地将大量数据从 CSV 文件导入 DataGridView
  • JavaScript 基础
  • 成龙电影中的三菱汽车
  • 退货处理费归零:亚马逊卖家年度成本节省路径解析
  • 硅基计划 学习总结 拾贰
  • WebSocket与Socket.IO实现简易客服聊天系统全解析
  • Spring Web MVC快速入门
  • [css]纯css绘制三角形
  • MindSpore框架学习项目-ResNet药物分类-数据增强
  • HTML应用指南:利用POST请求获取全国德邦快递服务网点位置信息
  • C++中extern关键字详解:不同情况下的使用方式