10、视频编解码:H264与VP8/VP9编码器选择、硬件编码与软件编码、编解码参数配置

视频编解码这块,说实在的,是WebRTC视频会议App里最核心也最头疼的部分。我做了这么多年Android开发,每次遇到编解码问题,都得折腾好一阵子。今天咱们就把这块彻底聊透。

10.1 编码器三兄弟:H264、VP8、VP9

WebRTC默认支持三种视频编码器:H264、VP8和VP9。你可能会问,为什么要有这么多?其实每个都有它的脾气。

编码器 专利情况 压缩效率 硬件支持 适用场景
H264 需专利授权 中等 几乎所有设备 通用兼容
VP8 开源免费 略低于H264 部分设备 WebRTC默认
VP9 开源免费 比H264高30% 较新设备 高画质低码率

H264 是行业老大哥。几乎所有Android手机都有硬件编码器支持。我在项目中遇到过一个问题:某款华为手机用VP8编码时发热严重,换成H264立马正常。这就是硬件加速的威力。

VP8 是WebRTC的亲儿子。当年Google推WebRTC时,为了避开H264的专利坑,搞了VP8。说实话,VP8的压缩效率比H264差那么一丢丢,但胜在免费。如果你做的是纯WebRTC通信,VP8是最稳妥的选择。

VP9 是后起之秀。压缩效率比H264高30%左右,同样的画质,码率更低。但问题是,硬件支持不够广。我记得2019年做的一个项目,在小米8上跑VP9硬编,结果直接崩溃——因为那款芯片不支持VP9硬件编码。

我的建议:

  • 兼容性优先 → 选H264
  • 开源项目 → 选VP8
  • 追求画质且目标设备较新 → 选VP9

10.2 硬件编码 vs 软件编码

这个选择题,说白了就是「用专用芯片」还是「用CPU硬算」的区别。

硬件编码:手机里有专门的视频编码芯片。它干这活又快又省电。我测试过,硬件编码的功耗只有软件编码的1/5左右。但缺点也很明显——灵活性差,参数可调范围小。

软件编码:完全靠CPU计算。好处是灵活,你想怎么调参数都行。坏处是——发热、耗电、卡顿。我曾经在小米6上测试软件编码720P视频,5分钟后手机烫得能煎鸡蛋。

实战经验:

在Android平台上,我建议优先使用硬件编码。只有当硬件编码不支持你需要的参数时,才回退到软件编码。比如你要编码4K视频,但硬件不支持,那就只能软件编码了。

怎么判断设备是否支持硬件编码?用MediaCodec的CodecCapabilities来查:

// 检查是否支持H264硬件编码
MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
for (MediaCodecInfo codecInfo : codecList.getCodecInfos()) {
    if (codecInfo.isEncoder() && 
        codecInfo.getName().contains("h264") &&
        !codecInfo.getName().contains("sw")) {
        // 找到硬件编码器
        Log.d("Codec", "硬件编码器: " + codecInfo.getName());
    }
}

注意看那个 !codecInfo.getName().contains("sw") 的判断。软件编码器的名字里通常带「sw」或「software」字样,硬件编码器带「hw」或「omx」。

我曾经踩过的坑:

有些设备上,MediaCodecList会返回多个同名编码器。比如「OMX.google.h264.encoder」和「OMX.qcom.video.encoder.avc」。前者是软件编码,后者是高通硬件编码。一定要选对!

10.3 编解码参数配置

参数配置这块,我见过太多人乱调了。其实核心参数就那么几个,调好了视频质量蹭蹭往上涨。

10.3.1 关键参数一览

参数 说明 推荐值
比特率 每秒传输的数据量 500-2000 kbps
帧率 每秒显示的帧数 15-30 fps
分辨率 视频的宽高 640x480 或 1280x720
I帧间隔 关键帧出现的频率 2-5秒
码率控制模式 CBR/VBR/CRF CBR(视频会议)

比特率 是最重要的参数。设得太低,画面糊成马赛克;设得太高,网络扛不住。视频会议场景,720P视频我一般设1500kbps左右。

帧率 不是越高越好。30fps已经够流畅了,再高只会浪费带宽。而且帧率越高,编码压力越大,手机发热越严重。

I帧间隔 这个参数容易被忽略。I帧是关键帧,解码时必须从I帧开始。如果I帧间隔太长,加入会议的人要等好几秒才能看到画面。我一般设2秒一个I帧。

10.3.2 代码配置示例

在WebRTC中配置编码参数,通常通过PeerConnection的RtpParameters来实现:

// 创建视频编码配置
VideoEncoderConfig config = new VideoEncoderConfig();
config.codecName = "H264";
config.initialBitrate = 1500; // kbps
config.maxBitrate = 2000;
config.minBitrate = 500;
config.maxFramerate = 30;

// 设置到PeerConnection
RtpParameters parameters = new RtpParameters();
parameters.encoders = new ArrayList<>();
parameters.encoders.add(config);

// 应用配置
peerConnection.setVideoEncoderConfig(config);

如果是直接操作MediaCodec,配置会更细一些:

MediaFormat format = MediaFormat.createVideoFormat("video/avc", 1280, 720);
format.setInteger(MediaFormat.KEY_BIT_RATE, 1500000); // 1.5 Mbps
format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 2); // 2秒一个I帧
format.setInteger(MediaFormat.KEY_BITRATE_MODE, 
                 MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR);

// 配置编码器
MediaCodec encoder = MediaCodec.createEncoderByType("video/avc");
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

小技巧:

码率控制模式选CBR(恒定比特率)还是VBR(可变比特率)?视频会议场景我强烈推荐CBR。因为VBR在画面静止时码率很低,但画面一运动码率飙升,容易导致网络抖动。CBR虽然牺牲了一点画质,但网络更稳定。

10.4 编码器选择流程图

下面这张图是我自己总结的编码器选择逻辑,每次做项目都拿出来看看:

编码器选择决策流程 开始选择编码器 目标设备是否支持硬件编码? (通过MediaCodecList检测) 需要兼容性还是画质? (兼容性→H264,画质→VP9) H264 硬件编码 兼容性最好,功耗低 VP9 硬件编码 画质好,码率低 软件编码(VP8) 兼容性差时回退方案 开始/结束 决策点 推荐方案

这张图的核心逻辑是:先检测硬件编码支持情况,再根据需求选编码器。如果硬件不支持,就回退到软件编码。嗯,这里要注意,软件编码首选VP8,因为它是WebRTC原生支持的。

10.5 避坑指南

最后分享几个我踩过的坑,希望能帮你少走弯路:

  • 编码器名字别写错:H264的MIME类型是"video/avc",不是"video/h264"。我见过有人写错,结果MediaCodec创建失败。
  • I帧间隔别设太大:我曾经设了10秒,结果用户加入会议后要等10秒才能看到画面,直接被投诉。
  • 码率别设太低:720P视频码率低于800kbps,画面就会明显模糊。我一般不低于1000kbps。
  • 别忘了检测编码器能力:有些设备虽然支持H264,但不支持1080P编码。一定要用CodecCapabilities检测。

我曾经犯过的错:

有一次在三星S8上测试,硬件编码H264 1080P 30fps,结果画面每隔几秒就卡一下。查了半天,发现是码率设了3000kbps,但S8的硬件编码器最大只支持2000kbps。后来用CodecCapabilities查了一下,乖乖设回2000kbps,问题解决。

编解码这块,说白了就是「硬件优先,软件兜底,参数调优」。你只要记住这个原则,大部分问题都能搞定。


公众号:蓝海资料掘金营,微信deep3321