在之前两篇对于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进行拉流,均能获得残缺的视频帧数据进行解码和播放。