今天我们要讨论的问题是一位社群的朋友遇到的一个工作中的技术问题,原文如下:
请问如何根据推送过来的 NALU 裸流数据来判断其是 H.264 编码还是 H.265 编码,然后根据判断得到的编码方式进行对应的解码处理?
以下是回答,欢迎大家留言讨论补充:
这是个很好的问题,谢谢你的提问。在这里我分两部分说明一下这个问题。
1)通常在处理音视频数据时,我们如何选择解码器?
通常我们不是根据 NALU 裸流数据中的信息来选择解码器,而是根据媒体封装层的信息来确定解码器。
媒体封装层是表示媒体数据是什么封装格式的,比如 MP4、FLV。在这层信息里,通常会携带码流编码格式的信息。
拿 MP4 来说,我们可以根据 Sample Description Box(moov/trak/mdia/minf/stbl/stsd)
中的信息来确定其封装的码流的编码格式。参考:《MP4 格式》。
对于 FLV,我们可以根据 VideoTagHeader 中的 CodecID 等信息来确定其封装的码流的编码格式。参考:《FLV 格式》。
这样的好处是效率比较高,解封装的时候就可以确定选择何种解码器了。
2)怎么识别 NALU 裸流数据的编码格式是 H.264 还是 H.265?
但是,如果出现题目中的情况,没有对码流进行封装,而是直接传输码流时,这时候 NALU 中有什么字段能标识自己的编码格式吗?答案是,没有这样明确的字段能标识码流的编码格式。
但是这个问题也不是不能解决,因为 H.264、H.265 码流本身也是遵循一定格式规范的,我们可以按照它的格式规范进行探测,如果能解析出来正确的信息,那也可以确定它的编码格式。
比如,拿 H.265 来说,FFmpeg 中 hevcdec.c
就有对其码流数据进行探测的函数:
static int hevc_probe(AVProbeData *p)
{
uint32_t code = -1;
int vps = 0, sps = 0, pps = 0, irap = 0;
int i;
for (i = 0; i < p->buf_size - 1; i ) {
code = (code << 8) p->buf[i];
if ((code & 0xffffff00) == 0x100) {
uint8_t nal2 = p->buf[i 1];
int type = (code & 0x7E) >> 1;
if (code & 0x81) // forbidden and reserved zero bits
return 0;
if (nal2 & 0xf8) // reserved zero
return 0;
switch (type) {
case HEVC_NAL_VPS: vps ; break;
case HEVC_NAL_SPS: sps ; break;
case HEVC_NAL_PPS: pps ; break;
case HEVC_NAL_BLA_N_LP:
case HEVC_NAL_BLA_W_LP:
case HEVC_NAL_BLA_W_RADL:
case HEVC_NAL_CRA_NUT:
case HEVC_NAL_IDR_N_LP:
case HEVC_NAL_IDR_W_RADL: irap ; break;
}
}
}
if (vps && sps && pps && irap)
return AVPROBE_SCORE_EXTENSION 1; // 1 more than .mpg
return 0;
}
所以,我们可以按照编码格式规范探测,比如 H.265 如果解析出了 pps、sps、vps 的各字段信息符合规范,就认为它是 H.265 的编码;如果不是,在你们的码流格式范围中就只剩 H.264 了;接下来将码流数据交给对应的解码器解码即可。