Android MediaCodec 编码实战:从 Camera 采集到 ByteBuffer 编码,生成 MP4 文件
1. Android Camera数据采集与YUV格式解析在Android平台上使用Camera API采集视频数据是编码流程的第一步。我遇到过不少开发者在这一步就卡壳主要问题集中在Camera2 API的复杂配置和YUV数据格式的理解上。这里分享几个实战经验Camera2 API的基本工作流程需要先创建CameraManager实例然后通过openCamera方法打开指定摄像头。记得在AndroidManifest.xml中添加相机权限uses-permission android:nameandroid.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_FORMATCOLOR_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); }转换时要注意不同设备的Image对象可能有不同的padding值UV分量的采样率通常是Y分量的1/2建议使用ByteBuffer而不是byte[]处理大数据量效率更高4. 编码与MP4封装全流程完整的编码流程需要MediaCodec和MediaMuxer配合工作。以下是核心步骤启动编码器encoder.start();在onInputBufferAvailable回调中喂数据Override public void onInputBufferAvailable(MediaCodec codec, int index) { ByteBuffer buffer codec.getInputBuffer(index); // 填充YUV数据 codec.queueInputBuffer(index, 0, data.length, timestamp, 0); }处理编码输出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); }封装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. 音视频同步实现方案如果需要加入音频同步是关键。我常用的时间戳同步方案是使用系统时钟作为基准long startTime System.nanoTime() / 1000;计算视频时间戳long videoPts (System.nanoTime() / 1000 - startTime);音频时间戳对齐audioBufferInfo.presentationTimeUs (System.nanoTime() / 1000 - startTime);同步时要注意音频数据通常采用AAC格式采样率设为44100Hz视频关键帧间隔不宜设置过大使用MediaSync类可以实现更精确的同步在实际项目中我发现使用单独的同步线程来管理时间戳比依赖编码器内部计时更可靠特别是在处理高帧率视频时。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2624266.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!