SkeyeRTMPClient扩展支持HEVC(H.265)解决方案之兼容H264和H265帧数据解析详解 (1)

发布于:2023-03-14 ⋅ 阅读:(397) ⋅ 点赞:(0)

在之前两篇关于SkeyeRTMPClient扩展支持HEVC(H.265)解决方案的文章中,我们已经完成了对H265的支持,本文主要阐述将H26和H265支持兼容起来,实现不同视频编码格式的自适应兼容适配。

1. 根据CodecId判断数据编码类型

根据视频编码ID判断视频编码类型,如果视频编码ID==FlvCodeId_Hevc(12),则判断视频编码格式为H265,反之则为H264(因为目前我们只支持这两种编码格式的视频推送),如下代码所示:

    parser_VideoTag *video_tag = (parser_VideoTag*)(buf+parser_offset);
    FlvCodeId video_code_id = (FlvCodeId)(video_tag->code_id&0x0f);
    if (video_code_id == FlvCodeId_Hevc)
    {
        av_frame.u32AVFrameFlag = SKEYE_SDK_VIDEO_CODEC_H265;// HEVC;
    } 
    else
    {
        av_frame.u32AVFrameFlag = SKEYE_SDK_VIDEO_CODEC_H264;// 默认h264, 其他类型是否需要判断?!;
    }
2. 数据帧头部判断

根据FLV/RTMP扩展支持H265标准,支持HEVC的VideoTagHeader定义如下图所示:
在这里插入图片描述
即 当CodecID == 12时,AVCPacketType为HEVCPacketType:

  • 如果HEVCPacketType为0,表示HEVCVIDEOPACKET中存放的是HEVC sequence header;
  • 如果HEVCPacketType为1,表示HEVCVIDEOPACKET中存放的是HEVC NALU;
  • 如果HEVCPacketType为2,表示HEVCVIDEPACKET中存放的是HEVC end of sequence,即HEVCDecoderConfigurationRecord;

而当CodecID == 7时,AVCPacketType为AVCPacketType:

  • 如果AVCPacketType为0,表示HEVCVIDEOPACKET中存放的是AVC sequence header;
  • 如果AVCPacketType为1,表示HEVCVIDEOPACKET中存放的是AVC NALU;
  • 如果AVCPacketType为2,表示HEVCVIDEPACKET中存放的是AVC end of sequence,即AVCDecoderConfigurationRecord;

SkeyeRTMPClient对sequence header的解析函数如下代码段所示:

int ParserVideoSequencePacket(FlvCodeId video_code_id, char *buf,int len)
{
    int parser_offset = 0;
    char *parser_config = buf;
    if (video_code_id == FlvCodeId_Hevc)
    {
        if(len <= sizeof(Parser_HEVCDecoderConfigurationRecord))
        {
            return -1001;
        }            
         ......’
        //Parser HEVCDecoderConfigurationRecord 
        ......
        rtmpclient_h265_decode_sps((unsigned char *)sps_buf_, sps_len_, width_, height_);    
    } 
    else
    {
        if(len <= sizeof(parser_AVCDecoderHeader))
        {
            return -1001;
        }
         ......’
        //Parser HEVCDecoderHeader
        ......
        rtmpclient_h264_decode_sps((unsigned char *)sps_buf_, sps_len_, width_, height_);
    }

    return 0;
}
3. 视频数据体帧数据nalu类型判断

根据FLV/RTMP扩展支持协议标准,支持H265的VideoTagBody定义如下, 扩展后的VideoTagBody如下图所示(红色字体为HEVC新增内容)::
在这里插入图片描述

当CodecID为12时,VideoTagBody中存放的就是HEVC视频帧内容。
SkeyeRTMPClient视频帧nalu解析如下代码所示:

int ParserOneVideoNalu(SkeyeRTMPClient_AV_Frame& av_frame,char *buf,int len,char* processbuf)
{
    if(processbuf == NULL || buf == NULL || len == 0)
    {
        return -3001;
    }
    if(sps_len_ == 0 || pps_len_ == 0)
    {
        printf("do not get sequence head yet\n");
        return -3002;
    }

    int parse_offset = 0;
    int nalu_len = 0;
    int nalu_type = 0;
    int processlen = 0;
    while(parse_offset < len - 4)
    {
        nalu_len = ntohl(*(int*)(buf + parse_offset));  
        parse_offset += 4;
        //如果视频帧编码类型为H265
        if(av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H265)
        {
            nalu_type = (buf[parse_offset] >> 1) & 0x3F;
            if(nalu_type == e_H265_NAL_UNIT_VPS)
            {
                ASSERT_PARSER(nalu_len,MAX_VPS_LEN);
                memcpy(vps_buf_,buf + parse_offset,nalu_len);
                vps_len_ = nalu_len;
                parse_offset += nalu_len;
                continue;
            }
        }else{
            nalu_type = buf[parse_offset]&0x1F;
        }
       //H265 以及 H264的SPS头解析兼容
        if((av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H265&&nalu_type ==e_H265_NAL_UNIT_SPS) || 
            av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H264&&nalu_type == e_H264_Frame_Type_Sps)
        {
            ASSERT_PARSER(nalu_len,MAX_PPS_LEN);
            memcpy(sps_buf_,buf + parse_offset,nalu_len);
            sps_len_ = nalu_len;
            parse_offset += nalu_len;

            if(width_ == 0 && sps_len_ > 0)
            {
                if(av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H265)
                    rtmpclient_h265_decode_sps((unsigned char *)sps_buf_, sps_len_, width_, height_);
                else
                    rtmpclient_h264_decode_sps((unsigned char *)sps_buf_, sps_len_, width_, height_);
            }
            continue;
        }
               //H265 以及 H264的PPS头解析兼容
        else if((av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H265&&nalu_type ==e_H265_NAL_UNIT_PPS) || 
            av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H264&&nalu_type == e_H264_Frame_Type_Pps)
        {
            memcpy(pps_buf_,buf + parse_offset,nalu_len);
            pps_len_ = nalu_len;
            parse_offset += nalu_len;
            continue;
        }

       //H265 以及 H264的I帧解析兼容
        else if((av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H265&&nalu_type >=e_H265_NAL_UNIT_SLICE_BLA&&nalu_type <=e_H265_NAL_UNIT_SLICE_CRA ) || 
        av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H264&&nalu_type == e_H264_Frame_Type_Idr)
        {
            if(av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H265)
            {
                memcpy(processbuf + processlen,nalu_head_,4);
                processlen += 4;
                memcpy(processbuf + processlen,vps_buf_,vps_len_);
                processlen += vps_len_;
            }
            .......
            //拷贝SPS和PPS以及Idr nalu
            ......
            av_frame.u32VFrameType = SKEYE_SDK_VIDEO_FRAME_I;
            continue ;
        }
       //H265 以及 H264的P帧解析兼容
        else if((av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H265&&nalu_type >=e_H265_NAL_UNIT_SLICE_TRAIL_R&&nalu_type <=e_H265_NAL_UNIT_SLICE_TFD ) || 
            av_frame.u32AVFrameFlag == SKEYE_SDK_VIDEO_CODEC_H264&&nalu_type == e_H264_Frame_Type_Slice)
        {
            memcpy(processbuf + processlen,nalu_head_,4);
            processlen += 4;
            memcpy(processbuf + processlen,buf + parse_offset,nalu_len);
            processlen += nalu_len;
            parse_offset += nalu_len;

            av_frame.u32VFrameType = SKEYE_SDK_VIDEO_FRAME_P;
            continue ;
        }
        else
        {
            parse_offset += nalu_len;
            continue;
        }
    }

    av_frame.pBuffer = (uint8_t *)processbuf;
    av_frame.u32AVFrameLen = processlen;
    return 0;
}

至此,SkeyeRTMPClient对H264和H265的兼容适配就完成了,我们可以通过SkeyeRTMPClient拉取任意编码格式为H264或者H265的RTMP进行拉流,均能取得完整的视频帧数据进行解码和播放。


网站公告

今日签到

点亮在社区的每一天
去签到