15、软编码与硬编码对比:软编码方案(x264、fdk-aac)、软硬编码性能对比、混合编码策略

说到编码,很多同学第一反应就是「用硬编码啊,快」。嗯,这话对了一半。我在项目中踩过不少坑,今天就把软编码和硬编码的底裤扒干净,让你知道什么时候该用软的,什么时候该上硬的。

软编码方案:x264 与 fdk-aac

先说说软编码。说白了,就是用 CPU 去算编码。Android 上最主流的视频软编码器是 x264,音频则是 fdk-aac。这两个库都是开源界的扛把子。

x264:视频编码的瑞士军刀

x264 有多强?我做过一个对比测试:同样码率下,x264 的压缩效率比 MediaCodec 的硬编码高出 15%-20%。代价就是 CPU 占用高,发热大。

在 Android 上集成 x264,一般用这个配置:

// x264 参数配置示例
x264_param_t param;
x264_param_default_preset(&param, "medium", "zerolatency");
param.i_csp = X264_CSP_I420;
param.i_width = 1280;
param.i_height = 720;
param.i_fps_num = 30;
param.i_fps_den = 1;
param.rc.i_bitrate = 2000; // 2Mbps
param.i_keyint_max = 60;
x264_param_apply_profile(&param, "baseline");

这里有个坑:zerolatency 这个预设很关键。如果你做直播,必须用这个,否则编码器会缓存帧,导致延迟飙升。我曾经在项目里忘了加这个参数,结果延迟从 200ms 直接飙到 2 秒,被测试小姐姐追着骂了一下午。

fdk-aac:音频编码的良心之选

音频方面,fdk-aac 是 Android 上最靠谱的软编码方案。AAC 格式的压缩率比 MP3 高,音质还好。我一般这么配置:

// fdk-aac 编码器初始化
AACENC_InfoStruct info = {0};
AACENC_ERROR err;

// 设置参数
aacEncoder_SetParam(handle, AACENC_AOT, AOT_AAC_LC);
aacEncoder_SetParam(handle, AACENC_SAMPLERATE, 44100);
aacEncoder_SetParam(handle, AACENC_CHANNELMODE, MODE_2);
aacEncoder_SetParam(handle, AACENC_CHANNELORDER, CH_ORDER_MPEG);
aacEncoder_SetParam(handle, AACENC_BITRATE, 128000); // 128kbps
aacEncoder_SetParam(handle, AACENC_TRANSMUX, TT_MP4_LATM_MCP1);

注意这个 TT_MP4_LATM_MCP1,它决定了音频流的封装格式。做 RTMP 推流时,一般用 TT_MP4_RAW 或者 TT_MP4_ADTS,具体看你用的封装库支持哪种。

软硬编码性能对比

直接上数据,这是我用小米 11 实测的结果:

对比项 软编码(x264) 硬编码(MediaCodec)
编码延迟 30-50ms 5-15ms
CPU 占用 40%-60% 5%-15%
压缩效率 高(同码率画质更好) 中(画质略差)
发热量 严重 轻微
兼容性 好(所有设备一致) 差(不同芯片差异大)
码率控制 精准(CBR/VBR 随意调) 粗糙(部分设备不支持 VBR)

看到这个表,你可能会想:「那硬编码不是完胜吗?」别急,听我说完。

硬编码最大的问题是什么?是 不一致。高通骁龙的硬编码表现和联发科天玑完全不一样,甚至同一颗芯片不同厂商的调校也不同。我在做一款直播 App 时,在小米手机上跑得好好的,换到 OPPO 上画面就花屏了。查了两天才发现是 MediaCodec 的 COLOR_FormatYUV420SemiPlanarCOLOR_FormatYUV420Planar 的差异导致的。

而软编码呢?x264 在所有设备上的行为完全一致。你调好的参数,换台手机还是那个效果。这就是为什么很多专业直播软件宁可牺牲性能也要用软编码。

混合编码策略

既然软硬编码各有优劣,那能不能结合起来?当然可以。这就是我常说的「混合编码策略」。

核心思路很简单:根据场景动态切换

混合编码的核心原则:

  • 低负载场景(如普通聊天):用硬编码,省电省发热
  • 高画质场景(如游戏直播):用软编码,保证画质
  • 设备发热严重时:自动切回硬编码,防止烫手
  • 网络波动时:软编码可以更精细地控制码率

下面这张图是我在项目中实际使用的混合编码决策流程:

混合编码决策流程 开始编码 设备温度 > 45°C 或电量 < 20%? (发热/低电量保护) 硬编码模式 省电、低发热 当前场景类型? (普通聊天 / 游戏直播 / 网络波动) 普通聊天 硬编码模式 低延迟、省电 游戏直播 软编码模式 高画质、精准码率 网络波动 动态调整策略 软编码精细控制码率 + 降低分辨率 输出编码数据

这个流程看起来复杂,其实核心就一句话:能硬则硬,该软则软

我的实践经验:

在混合编码切换时,有个细节很容易被忽略——关键帧对齐。从软编码切到硬编码时,如果不在关键帧边界切换,接收端会出现花屏。我一般会在切换前强制编码器输出一个 IDR 帧,然后再切。

注意:不要频繁切换编码器。每次切换都会造成短暂的画面卡顿。我建议设置一个「冷却时间」,比如切换后至少 5 秒内不再切换。

最后说一个我踩过的坑。有一次做直播项目,产品经理要求「画质必须最好,延迟必须最低」。这俩要求本身就是矛盾的。我跟他解释了半天,最后定了个折中方案:启动时用硬编码保证低延迟,等用户进入直播间稳定后,再无缝切换到软编码提升画质。这个方案用户感知不到切换过程,体验反而更好。

嗯,这就是我对软硬编码的理解。说白了,没有银弹,只有最适合当前场景的方案。你想想看,是不是这个理?


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