h264切割出nalu的两种方式

目录

 

前言:

一、Annex B格式

二、AVCC格式

三、总结


前言:

        目前h264中nalu的组织格式主要有两种:Annex B格式和AVCC格式。下面介绍一下这两种格式,并结合开源流媒体服务srs的代码来分析这两种格式。

一、Annex B格式

         Annex B格式是用开始码来分割前后的nalu,即在每个nalu前面加上2个或者3个0x00,再加一个0x01(0x000001或者0x00000001)。注意:nalu的内容中,所有三字节的序列0x00 00 **在nalu中都是非法的,都会被转义成0x00 00 03 **

int SrsAvcAacCodec::avc_demux_annexb_format(SrsStream* stream, SrsCodecSample* sample)
{
    int ret = ERROR_SUCCESS;
    
    // 检测是否是0x00000001或0x000001开头
    if (!srs_avc_startswith_annexb(stream, NULL)) {
        return ERROR_HLS_AVC_TRY_OTHERS;
    }
    
    // AnnexB
    // B.1.1 Byte stream NAL unit syntax,
    // H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
    while (!stream->empty()) {
        // find start code
        int nb_start_code = 0;
        // 找到除去0x00000001或0x000001后的开头位置,即真正的nalu的开始位置
        if (!srs_avc_startswith_annexb(stream, &nb_start_code)) {
            return ret;
        }

        // 跳过0x00000001或0x000001
        if (nb_start_code > 0) {
            stream->skip(nb_start_code);
        }
        
        // p指向nalu的开始位置
        char* p = stream->data() + stream->pos();
        
        // get the last matched NALU
        while (!stream->empty()) {
            // 跳过连续的0x00000001或0x000001
            if (srs_avc_startswith_annexb(stream, NULL)) {
                break;
            }
            
            stream->skip(1);
        }
        
        char* pp = stream->data() + stream->pos();
        
        // skip the empty.
        if (pp - p <= 0) {
            continue;
        }
        
        // got the NALU.
        // 将解析出来的nalu放到sample的nalu数组里面
        if ((ret = sample->add_sample_unit(p, pp - p)) != ERROR_SUCCESS) {
            srs_error("annexb add video sample failed. ret=%d", ret);
            return ret;
        }
    }
    
    return ret;
}

        下面是分隔符的查找算法,兼容3字节和4字节。

bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code)
{
    char* bytes = stream->data() + stream->pos();
    char* p = bytes;
    
    for (;;) {
        if (!stream->require(p - bytes + 3)) {
            return false;
        }
        
        // not match
        if (p[0] != (char)0x00 || p[1] != (char)0x00) {
            return false;
        }
        
        // match N[00] 00 00 01, where N>=0
        if (p[2] == (char)0x01) {
            if (pnb_start_code) {
                *pnb_start_code = (int)(p - bytes) + 3;
            }
            return true;
        }
        
        p++;
    }
    
    return false;
}

二、AVCC格式

       AVCC格式是根据每个nalu前面的长度字段来切分出nalu,nalu前面的长度字段本身的长度是由视频元信息帧(即包含sps和pps的帧)中NALULengthSizeMinusOne字段来决定的,该字段取值为0、1、3,对应表示nalu长度字段本身的长度为1B、2B、4B

int SrsAvcAacCodec::avc_demux_ibmf_format(SrsStream* stream, SrsCodecSample* sample)
{
    int ret = ERROR_SUCCESS;
    
    // 整个帧的长度
    int PictureLength = stream->size() - stream->pos();
    
    // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
    // 5.2.4.1 AVC decoder configuration record
    // 5.2.4.1.2 Semantics
    // The value of this field shall be one of 0, 1, or 3 corresponding to a
    // length encoded with 1, 2, or 4 bytes, respectively.
    srs_assert(NAL_unit_length != 2);// 因为NAL_unit_length 取值只有0,1,3
    
    // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 20
    for (int i = 0; i < PictureLength;) {
        // unsigned int((NAL_unit_length+1)*8) NALUnitLength;
        if (!stream->require(NAL_unit_length + 1)) {
            ret = ERROR_HLS_DECODE_ERROR;
            srs_error("avc decode NALU size failed. ret=%d", ret);
            return ret;
        }
        int32_t NALUnitLength = 0;// 用于获取nalu真正的长度
        if (NAL_unit_length == 3) {
            NALUnitLength = stream->read_4bytes();
        } else if (NAL_unit_length == 1) {
            NALUnitLength = stream->read_2bytes();
        } else {
            NALUnitLength = stream->read_1bytes();
        }
        
        // maybe stream is invalid format.
        // see: https://github.com/ossrs/srs/issues/183
        if (NALUnitLength < 0) {
            ret = ERROR_HLS_DECODE_ERROR;
            srs_error("maybe stream is AnnexB format. ret=%d", ret);
            return ret;
        }
        
        // NALUnit
        if (!stream->require(NALUnitLength)) {
            ret = ERROR_HLS_DECODE_ERROR;
            srs_error("avc decode NALU data failed. ret=%d", ret);
            return ret;
        }
        // 7.3.1 NAL unit syntax, H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
        // 将当前解析出的nalu依次放到sample数组中
        if ((ret = sample->add_sample_unit(stream->data() + stream->pos(), NALUnitLength)) != ERROR_SUCCESS) {
            srs_error("avc add video sample failed. ret=%d", ret);
            return ret;
        }
        stream->skip(NALUnitLength);
        
        i += NAL_unit_length + 1 + NALUnitLength;
    }
    
    return ret;
}

 

三、总结

        实际上,上述两种方式就是tcp解决沾包问题的主要方法:1、分隔符  2、附加长度字段。当然也可以通过固定包长度来解决沾包问题,比如mpeg-ts协议中的ts包就是固定188字节,但是nalu是按内容来组包的,所以不可能长度一致,因此主流使用上面介绍的两种方法来分包。

 

你可能感兴趣的:(直播后台)