news 2026/5/19 7:24:19

Android MediaCodec 编码实战:从 Camera 采集到 ByteBuffer 编码,生成 MP4 文件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android MediaCodec 编码实战:从 Camera 采集到 ByteBuffer 编码,生成 MP4 文件

1. Android Camera数据采集与YUV格式解析

在Android平台上使用Camera API采集视频数据是编码流程的第一步。我遇到过不少开发者在这一步就卡壳,主要问题集中在Camera2 API的复杂配置和YUV数据格式的理解上。这里分享几个实战经验:

Camera2 API的基本工作流程需要先创建CameraManager实例,然后通过openCamera方法打开指定摄像头。记得在AndroidManifest.xml中添加相机权限:

<uses-permission android:name="android.permission.CAMERA" />

采集到的原始数据通常是NV21或YUV_420_888格式。这里有个坑我踩过多次:不同设备支持的预览格式可能不同。建议在初始化时检查支持的格式:

StreamConfigurationMap map = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); int[] formats = map.getOutputFormats();

YUV420格式的数据排列方式很关键。以NV21为例,它的内存布局是:先存储所有Y分量(亮度),然后是交错的VU分量(色度)。一个分辨率为640x480的帧,其Y分量大小为640x480字节,UV分量各为320x240字节,总大小是width×height×1.5。

2. MediaCodec编码器配置实战

创建编码器时,我强烈建议使用异步模式。同步模式虽然直观,但在高帧率场景下容易阻塞主线程。以下是创建H.264编码器的标准流程:

MediaFormat format = MediaFormat.createVideoFormat( MediaFormat.MIMETYPE_VIDEO_AVC, width, height); format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate); format.setInteger(MediaFormat.KEY_FRAME_RATE, fps); format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval); format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible); MediaCodec encoder = MediaCodec.createEncoderByType( MediaFormat.MIMETYPE_VIDEO_AVC); encoder.setCallback(new MyCallback()); encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

这里有几个参数需要特别注意:

  • 比特率(BIT_RATE):建议根据分辨率设置,1080p视频通常设为4-8Mbps
  • 关键帧间隔(I_FRAME_INTERVAL):直播场景建议设为1-2秒,录播场景可以适当增大
  • 颜色格式(COLOR_FORMAT):COLOR_FormatYUV420Flexible是最通用的选择

3. YUV到ByteBuffer的转换技巧

Camera输出的YUV数据通常需要转换后才能送入编码器。这里分享一个高效的转换方法:

private void convertYUV420ToNV21(Image image, byte[] output) { ByteBuffer yBuffer = image.getPlanes()[0].getBuffer(); ByteBuffer uBuffer = image.getPlanes()[1].getBuffer(); ByteBuffer vBuffer = image.getPlanes()[2].getBuffer(); int ySize = yBuffer.remaining(); int uSize = uBuffer.remaining(); int vSize = vBuffer.remaining(); yBuffer.get(output, 0, ySize); vBuffer.get(output, ySize, vSize); uBuffer.get(output, ySize + vSize, uSize); }

转换时要注意:

  1. 不同设备的Image对象可能有不同的padding值
  2. UV分量的采样率通常是Y分量的1/2
  3. 建议使用ByteBuffer而不是byte[]处理大数据量,效率更高

4. 编码与MP4封装全流程

完整的编码流程需要MediaCodec和MediaMuxer配合工作。以下是核心步骤:

  1. 启动编码器
encoder.start();
  1. 在onInputBufferAvailable回调中喂数据
@Override public void onInputBufferAvailable(MediaCodec codec, int index) { ByteBuffer buffer = codec.getInputBuffer(index); // 填充YUV数据 codec.queueInputBuffer(index, 0, data.length, timestamp, 0); }
  1. 处理编码输出
@Override public void onOutputBufferAvailable(MediaCodec codec, int index, MediaCodec.BufferInfo info) { ByteBuffer encodedData = codec.getOutputBuffer(index); muxer.writeSampleData(trackIndex, encodedData, info); codec.releaseOutputBuffer(index, false); }
  1. 封装MP4文件
MediaMuxer muxer = new MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); int trackIndex = muxer.addTrack(encoder.getOutputFormat()); muxer.start();

常见问题排查:

  • 如果遇到"Failed to add track"错误,检查MediaFormat是否包含csd-0和csd-1参数
  • 视频不同步问题通常是由于时间戳计算错误导致的
  • 输出文件损坏可能是没有正确发送END_OF_STREAM标志

5. 性能优化与实战技巧

经过多个项目的实践,我总结出几个提升编码效率的方法:

内存优化

  • 复用ByteBuffer对象,避免频繁内存分配
  • 使用Surface输入模式代替ByteBuffer模式(需要API 18+)
  • 设置合适的缓冲区数量:
format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, width * height * 3 / 2);

参数调优

  • 动态比特率调整:
Bundle params = new Bundle(); params.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, newBitrate); encoder.setParameters(params);
  • 低延迟配置:
format.setInteger(MediaFormat.KEY_LATENCY, 1);

异常处理: 编码过程中常见的异常包括:

  • CodecException:通常由配置错误引起
  • IllegalStateException:多线程访问导致
  • MediaCodec.CodecError:硬件资源不足

建议在回调中实现健壮的错误处理:

@Override public void onError(MediaCodec codec, MediaCodec.CodecException e) { Log.e(TAG, "Encoder error: " + e.getDiagnosticInfo()); // 重启编码器或通知上层 }

6. 音视频同步实现方案

如果需要加入音频,同步是关键。我常用的时间戳同步方案是:

  1. 使用系统时钟作为基准:
long startTime = System.nanoTime() / 1000;
  1. 计算视频时间戳:
long videoPts = (System.nanoTime() / 1000 - startTime);
  1. 音频时间戳对齐:
audioBufferInfo.presentationTimeUs = (System.nanoTime() / 1000 - startTime);

同步时要注意:

  • 音频数据通常采用AAC格式,采样率设为44100Hz
  • 视频关键帧间隔不宜设置过大
  • 使用MediaSync类可以实现更精确的同步

在实际项目中,我发现使用单独的同步线程来管理时间戳比依赖编码器内部计时更可靠,特别是在处理高帧率视频时。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/19 7:21:04

为AI智能体项目选择稳定且多模型的后端API供应商

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 为AI智能体项目选择稳定且多模型的后端API供应商 在开发AI智能体或自动化工作流时&#xff0c;工程师们面临的核心挑战之一是如何为…

作者头像 李华
网站建设 2026/5/19 7:18:01

2026 AI 短剧自动生成的工具,有哪些值得说一说

3 人团队 5 天完成、上线 29 小时播放量破 2 亿——这不是电影里才有的故事&#xff0c;而是 2026 年 AI 短剧赛道的真实产能。据 DataEye 数据&#xff0c;2026 年 3 月单月新增漫剧约 4.7 万部&#xff0c;总播放增量近 477.38 亿&#xff0c;环比激增 94.52%。当下&#xff…

作者头像 李华
网站建设 2026/5/19 7:17:14

AI办公革命:国产工具提升工作效率

AI办公革命&#xff1a;国产工具提升工作效率 告别加班&#xff0c;拥抱AI。从文档到会议&#xff0c;从数据分析到邮件处理&#xff0c;国产AI工具让办公效率倍增。 一、办公革命的到来 1.1 为什么办公需要AI 职场人的一天往往被各种琐碎事务所占据&#xff1a;回复邮件、整…

作者头像 李华
网站建设 2026/5/19 7:17:06

CVE-2026-31431 Copy Fail 漏洞分析

文章目录简介pocsendmsgsplicesplice状态下的sendmsgrecv修复补丁总结AI boom!CVE漏洞库信息参考简介 漏洞自linux 4.13-rc1引入 # git show 72548b093ee3:Makefile VERSION 4 PATCHLEVEL 13 SUBLEVEL 0 EXTRAVERSION -rc1 NAME Fearless Coyote借助这个最广为流传的poc…

作者头像 李华
网站建设 2026/5/19 7:16:04

MSP430L092 0.9V超低功耗MCU:物联网设备微型化与长续航的终极方案

1. 项目概述&#xff1a;为什么0.9V工作电压是微型化与长续航的关键在物联网设备、可穿戴传感器、便携式医疗仪器以及智能家居控制器这些领域里&#xff0c;工程师们每天都在和两个“魔鬼”做斗争&#xff1a;一个是设备的物理尺寸&#xff0c;另一个是电池的续航能力。用户希望…

作者头像 李华
网站建设 2026/5/19 7:08:26

第12期:综合优化与结业项目(工程落地与量产调优)

一、本期课程简介本期为整套TinyML嵌入式实战课程的收官总结阶段&#xff0c;旨在帮助学员打通技术壁垒&#xff0c;完成从零散知识点积累到系统化工程落地能力的蜕变。课程将全面梳理前序所有实战项目技术栈&#xff0c;涵盖传感器数据采集、数据集预处理、神经网络模型轻量化…

作者头像 李华