8、视频录制:Camera+MediaRecorder录制视频、Camera2+MediaCodec录制视频
视频录制这块,说实话是播放器开发里最容易被低估的环节。很多人觉得「不就是调个API嘛」,结果真上手就发现坑一个接一个。我自己最早做录制功能时,也踩过不少雷——比如录出来的视频没声音、画面卡顿、甚至直接闪退。嗯,今天咱们就把这两条路都走一遍。
8.1 两条技术路线,怎么选?
Android上录制视频,主流就两条路:
- Camera + MediaRecorder:老牌组合,API简单,适合快速开发
- Camera2 + MediaCodec:新架构,灵活度高,适合定制化需求
我个人习惯是:如果只是做个简单的录制功能,比如拍个短视频上传,用MediaRecorder就够了。但如果你需要实时滤镜、自定义分辨率、或者要对接直播推流,那Camera2+MediaCodec才是正解。
核心区别一句话总结:
MediaRecorder是「黑盒」,你只管开始和停止,中间它自己搞定;MediaCodec是「白盒」,每一帧数据你都能拿到手,想怎么处理都行。
8.2 Camera + MediaRecorder:五分钟跑通录制
先说说最简单的方案。MediaRecorder这个类,说白了就是Google帮你封装好的一套录制管线。你只需要告诉它「从哪里取画面」「从哪里取声音」「输出到哪里」,剩下的它自己搞定。
核心步骤其实就这几步:
- 打开Camera,设置预览
- 创建MediaRecorder实例
- 配置音视频源、输出格式、编码器
- 调用start()开始录制
- 调用stop()结束录制
代码写起来大概这样:
// 配置 MediaRecorder
MediaRecorder recorder = new MediaRecorder();
// 必须先解锁Camera再关联
camera.unlock();
recorder.setCamera(camera);
// 设置音视频源
recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
// 设置输出格式和编码器
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
// 设置输出文件
recorder.setOutputFile(outputPath);
recorder.setVideoSize(1280, 720);
recorder.setVideoFrameRate(30);
// 准备并开始
recorder.prepare();
recorder.start();
注意:MediaRecorder的配置顺序是有讲究的。我曾经遇到过setCamera之后忘记先unlock,结果一直报错。另外,setVideoSize必须在setVideoEncoder之后调用,否则会默默忽略——这个坑我查了整整一下午。
8.3 Camera2 + MediaCodec:自己掌控每一帧
好,现在说进阶方案。Camera2配合MediaCodec,说白了就是你自己当导演——Camera2负责采集画面,MediaCodec负责编码压缩,中间的数据流你说了算。
为什么会需要这个方案?举个例子:你想在录视频时加个时间水印,或者做个美颜滤镜。MediaRecorder根本插不进手,但用MediaCodec,你可以在编码前对每一帧做处理。
核心流程是这样的:
- Camera2通过ImageReader输出原始帧(YUV格式)
- 你把原始帧传给MediaCodec的输入队列
- MediaCodec编码后输出H.264/H.265数据
- 最后用MediaMuxer封装成MP4文件
代码骨架大概长这样:
// 1. 配置 MediaCodec 编码器
MediaFormat format = MediaFormat.createVideoFormat(
"video/avc", 1280, 720);
format.setInteger(MediaFormat.KEY_BIT_RATE, 2_000_000);
format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
MediaCodec encoder = MediaCodec.createEncoderByType("video/avc");
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
encoder.start();
// 2. 从 ImageReader 获取帧数据
ImageReader reader = ImageReader.newInstance(1280, 720,
ImageFormat.YUV_420_888, 2);
reader.setOnImageAvailableListener(reader1 -> {
Image image = reader1.acquireLatestImage();
if (image != null) {
// 这里可以加滤镜、水印等处理
byte[] data = yuv420888ToNv21(image);
feedToEncoder(encoder, data, presentationTimeUs);
image.close();
}
}, backgroundHandler);
// 3. 将编码后的数据写入 MediaMuxer
MediaMuxer muxer = new MediaMuxer(outputPath,
MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
// ... 在编码器输出回调中不断写入 track
个人经验:MediaCodec的输入输出都是异步的,新手最容易犯的错就是「同步思维」。你往输入队列塞一帧,它不会立刻输出编码后的数据。正确的做法是:在编码器的输出回调里不断取数据,同时持续往输入队列塞帧。这个循环一旦断了,视频就会卡顿。
8.4 避坑指南:我踩过的那些雷
做录制功能,有几个坑是绕不开的。我一个个说:
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 录出来的视频没声音 | AudioSource配置不对,或权限没申请 | 检查RECORD_AUDIO权限,确认麦克风可用 |
| 视频画面是绿的 | YUV格式不匹配 | Camera输出格式和Codec输入格式必须一致 |
| 录制几秒后卡死 | MediaCodec输入队列满了 | 控制帧率,不要无限制塞帧 |
| 文件无法播放 | MediaMuxer没有正确stop | 确保所有track写入完毕后再stop |
我曾经在一个项目里,录出来的视频总是比实际时长短几秒。查了半天,发现是MediaMuxer的stop调用时机不对——必须在所有编码数据都写入之后才能stop,否则文件会被截断。
8.5 性能优化:别让手机变成暖手宝
视频录制是典型的高功耗场景。你想想看,摄像头一直在采集,CPU/GPU一直在编码,能不热吗?
几个优化方向:
- 控制分辨率:1080p够用就别上4K,功耗差好几倍
- 合理设置帧率:24fps和30fps肉眼差别不大,但编码压力差不少
- 使用Surface输入:Camera2可以直接输出到Surface,省去内存拷贝
- 及时释放资源:录制结束后记得release Camera和MediaCodec
我的建议:如果只是做普通录制,优先用MediaRecorder。它内部做了很多优化,比你自己手写MediaCodec要稳定得多。只有当你需要定制化处理时,才值得去碰Camera2+MediaCodec这条路线。
嗯,视频录制这块内容就这些。两条路线各有优劣,关键看你的业务场景。MediaRecorder省心,MediaCodec灵活——选哪个,你自己掂量。