配置WebCodecs API解码器适配NVENC编码器时遇avcC解析错误
我用NVENC搭建了H264编码器编码视频流,想要在Google Chrome中通过WebCodecs API解码该流的帧,当前解码器配置代码如下:
let decoderConfig = {codec: "avc1.428020", width: 480, height: 270}; decoderConfig.description = new Uint8Array(dataFromEncoder); decoder.configure(decoderConfig);
其中dataFromEncoder是通过NVENC的NvEncGetSequenceParams函数获取的数组:[0, 0, 0, 1, 103, 100, 0, 50, 172, 43, 64, 240, 71, 245, 128, 136, 0, 0, 31, 64, 0, 21, 249, 4, 32, 0, 0, 0, 1, 104, 206, 60, 176]
运行时出现错误:Failed to execute 'configure' on 'VideoDecoder': Failed to parse avcC.
问题1:为什么avcC解析失败?用错NVENC函数了吗?
NvEncGetSequenceParams返回的是Annex B格式的SPS/PPS数据(以0x00000001起始码分隔NAL单元),但WebCodecs要求的是avcC格式的序列参数描述,两者格式不兼容,这是解析失败的核心原因。
你需要把Annex B格式的SPS/PPS转换成avcC格式,步骤如下:
- 提取原始SPS/PPS数据:从
dataFromEncoder中去掉起始码0x00000001,得到:- SPS数据:
[103, 100, 0, 50, 172, 43, 64, 240, 71, 245, 128, 136, 0, 0, 31, 64, 0, 21, 249, 4, 32](长度20字节) - PPS数据:
[104, 206, 60, 176](长度4字节)
- SPS数据:
- 构建avcC格式字节流:按照avcC标准结构拼接:
把这些字节组合成Uint8Array,替换原来的// 字节顺序: 0x01, // 版本号 0x67, // SPS的profile_idc(对应SPS第一个字节103) 0x64, // SPS的profile_compatibility(对应SPS第二个字节100) 0x32, // SPS的level_idc(对应SPS第三个字节50) 0xFF, // 保留位(6位) + lengthSizeMinusOne(2位,设为3表示用4字节长度前缀) 0xE1, // SPS数量(低6位为1,即1个SPS) 0x00, 0x14, // SPS的长度(2字节大端,对应20字节) ...(20字节SPS数据), // 上面提取的SPS数组 0x01, // PPS数量(1个) 0x00, 0x04, // PPS的长度(2字节大端,对应4字节) ...(4字节PPS数据) // 上面提取的PPS数组dataFromEncoder即可。
NvEncGetSequenceParams本身是正确的获取序列参数的函数,只是返回格式不符合WebCodecs要求,需要格式转换。
问题2:codec字符串avc1.428020是否正确?怎么获取匹配的字符串?
这个codec字符串是错误的,必须和编码器输出的H264参数匹配,否则会导致解码失败或兼容性问题。
avc1.XXXXXX格式中,XXXXXX是三位十六进制参数的拼接:
- 前两位:profile_idc(SPS第一个字节的十六进制值)
- 中间两位:profile_compatibility(SPS第二个字节的十六进制值)
- 最后两位:level_idc(SPS第三个字节的十六进制值)
从你的SPS数据来看:
- profile_idc = 103 → 十六进制
67 - profile_compatibility = 100 → 十六进制
64 - level_idc = 50 → 十六进制
32
所以正确的codec字符串应该是avc1.676432。
获取正确codec字符串的方法:直接从NvEncGetSequenceParams返回的SPS数据中解析前三个字节,转成两位十六进制后拼接即可,不需要依赖NVENC的额外API。
内容的提问来源于stack exchange,提问作者Pruza

