20、录制与回放:本地录制MediaRecorder、云端录制方案、录制文件管理
录制功能,说白了就是给视频会议装上“记忆”能力。我做过不少会议APP,发现用户对录制的需求其实很朴素——能录、能看、别丢数据。但真做起来,坑还真不少。
这一章,咱们就聊聊Android端怎么把录制这件事做扎实。从本地MediaRecorder到云端方案,再到文件怎么管,一条线讲透。
20.1 本地录制:MediaRecorder的正确打开方式
Android自带的MediaRecorder,用起来不算复杂。但你要想录WebRTC的画面和声音,就得注意几个关键点。
核心思路:把WebRTC的VideoTrack和AudioTrack,喂给MediaRecorder。但MediaRecorder不吃Track,它吃Surface和AudioSource。所以得做个“转接头”。
关键步骤:
- 从VideoTrack获取Surface
- 把Surface塞给MediaRecorder的setInputSurface()
- 音频用AudioRecord采集PCM,再编码写入
嗯,这里要注意:MediaRecorder的音频输入不支持直接传PCM。我早期踩过这个坑,录出来的视频没声音。后来改用MediaCodec + AudioRecord自己拼,才搞定。
来个代码片段,看看核心流程:
// 初始化MediaRecorder
MediaRecorder recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
recorder.setVideoSize(1280, 720);
recorder.setVideoFrameRate(30);
// 关键:获取Surface
Surface surface = recorder.getSurface();
// 把这个surface传给VideoTrack的Sink
videoTrack.addSink(new VideoSink() {
@Override
public void onFrame(VideoFrame frame) {
// 内部会渲染到surface上
}
});
recorder.prepare();
recorder.start();
我曾经遇到过一个问题:录制过程中切换摄像头,画面会卡住。原因是MediaRecorder的Surface不能动态换。解决方案是停止录制,重新创建MediaRecorder。虽然有点粗暴,但稳定。
20.2 云端录制:把压力丢给服务器
本地录制有个硬伤——手机存储有限,而且耗电。用户开个两小时会议,手机能烫得煎鸡蛋。所以很多场景下,云端录制是更好的选择。
云端录制的本质:客户端把音视频流推到服务器,服务器负责混流、编码、存储。客户端只负责推流,不负责写文件。
我个人习惯用SFU(Selective Forwarding Unit)架构。客户端推一路流到SFU,SFU再转发给其他参会者。同时,SFU可以单独拉一路流去做录制。
这样好处很明显:
- 客户端零负担,不耗电不占存储
- 录制质量可控,服务器性能强
- 支持合流录制,所有参会者在一个画面里
但云端录制也有坑。我记得有个项目,用户反馈录制文件声音和画面不同步。查了半天,发现是服务器端音频采样率和视频帧率没对齐。后来加了时间戳校准才解决。
我建议:云端录制一定要做“录制状态回调”。客户端要知道什么时候开始录、什么时候结束、文件是否生成成功。不然用户点了录制按钮,结果文件没存上,体验就很糟糕。
20.3 录制文件管理:别让文件变成垃圾
录完的文件怎么管?这问题看着简单,实际挺头疼。用户录了100个会议,每个1GB,手机很快就满了。
文件管理三要素:
- 存储策略:本地还是云端?本地存多久?云端存多久?
- 文件命名:别用时间戳,用户看不懂。用“会议主题_日期_时长”这种格式。
- 清理机制:自动删除过期文件,或者提示用户手动清理。
我做过一个方案,供你参考:
| 存储位置 | 保留策略 | 清理方式 |
|---|---|---|
| 本地 | 保留7天 | 超过7天自动删除,或移入“已过期”目录 |
| 云端 | 保留30天 | 30天后转为冷存储,90天后删除 |
| 收藏 | 永久保留 | 用户手动取消收藏后按默认策略处理 |
嗯,这里要注意:文件删除前一定要给用户通知。我见过一个APP,悄无声息把用户的重要会议录删了,用户直接炸毛。所以,删除前弹个确认框,或者发个通知,都是必要的。
文件命名规范:
会议名称_日期_开始时间_时长.mp4
示例:项目评审_2025-03-20_14-30_01h23m.mp4
20.4 整体架构图
下面这张图,把本地录制、云端录制、文件管理的关系画清楚了。你一看就明白。
从图里能看出来,本地录制和云端录制是两条并行的路。文件管理是它们的“汇合点”。回放则是最终的用户体验。
20.5 避坑指南
做录制功能,我踩过的坑不少。挑几个典型的说说:
- 录制文件损坏:录制过程中APP被杀死,文件没正常关闭。解决方案是加个“录制中”状态标记,恢复时检查并修复。
- 音频回声:本地录制时,扬声器声音又被麦克风录进去。我建议用耳机,或者开启回声消除。
- 存储空间不足:录制前检查剩余空间,低于500MB就提示用户。别等到写文件失败才报错。
- 权限问题:Android 11以后,存储权限变严了。记得用MediaStore API,别直接写文件路径。
我个人习惯:录制开始前,先做一次“环境检查”。检查存储空间、权限、网络状态(如果是云端录制)。全部通过才允许用户点击录制按钮。这样能避免90%的录制失败问题。
好了,录制与回放这块,核心就是这些。本地录制适合轻量场景,云端录制适合正式会议。文件管理则是用户体验的最后一公里。把这三点做好,录制功能就稳了。
公众号:蓝海资料掘金营,微信deep3321