乐趣区

关于c++:SkeyeRTMPPusher推送RTMP扩展支持HEVCH265

不久前刚实现 SkeyeRTMPPusher 扩大反对 h265 推送,过后在网上也查找了很多材料,发现都不尽具体,而官网也没有更新对 HEVC(H265,后文统称 HEVC)tag 的反对,反正是走了不少弯路,当然,在宽广网友以及 ffmpeg 代码的帮忙下我最终实现了通过 SkeyeRTMPPusher 推送 HEVC 视频帧数据到 SkeyeSMS,这里我将把实现过程具体的记录下来,供宽广网友参考。

 首先,RTMP 头部信息封装并没有定义 HEVC,咱们采纳 CDN 联盟的 HEVC 扩大规范,将 HEVC 的 VideoTagHeader 定义为 12,详见下图:![在这里插入图片形容](https://img-blog.csdnimg.cn/img_convert/3319b358ebe3b50efd59cb16cb18a292.png)
而后,咱们在 H264 封装的根底上进行改良,以反对 HEVC 头部的封装,而 HEVC 头有 

SPS PPS VPS,咱们参考 ffmpeg 的 HEVCDecoderConfigurationRecord 构造对 metadata 进行封装,该构造体代码如下:

typedef struct HVCCNALUnitArray {
    uint8_t  array_completeness;
    uint8_t  NAL_unit_type;
    uint16_t numNalus;
    uint16_t *nalUnitLength;
    uint8_t  **nalUnit;
} HVCCNALUnitArray;

typedef struct HEVCDecoderConfigurationRecord {
    uint8_t  configurationVersion;
    uint8_t  general_profile_space;
    uint8_t  general_tier_flag;
    uint8_t  general_profile_idc;
    uint32_t general_profile_compatibility_flags;
    uint64_t general_constraint_indicator_flags;
    uint8_t  general_level_idc;
    uint16_t min_spatial_segmentation_idc;
    uint8_t  parallelismType;
    uint8_t  chromaFormat;
    uint8_t  bitDepthLumaMinus8;
    uint8_t  bitDepthChromaMinus8;
    uint16_t avgFrameRate;
    uint8_t  constantFrameRate;
    uint8_t  numTemporalLayers;
    uint8_t  temporalIdNested;
    uint8_t  lengthSizeMinusOne;
    uint8_t  numOfArrays;
    HVCCNALUnitArray *array;
} HEVCDecoderConfigurationRecord;

参考 ffmeg 对该构造进行初始化如下:


static void hvcc_init(HEVCDecoderConfigurationRecord *hvcc)
{memset(hvcc, 0, sizeof(HEVCDecoderConfigurationRecord));
    hvcc->configurationVersion = 1;
    hvcc->lengthSizeMinusOne   = 3; // 4 bytes

    /*
     * The following fields have all their valid bits set by default,
     * the ProfileTierLevel parsing code will unset them when needed.
     */
    hvcc->general_profile_compatibility_flags = 0xffffffff;
    hvcc->general_constraint_indicator_flags  = 0xffffffffffff;

    /*
     * Initialize this field with an invalid value which can be used to detect
     * whether we didn't see any VUI (in which case it should be reset to zero).
     */
    hvcc->min_spatial_segmentation_idc = MAX_SPATIAL_SEGMENTATION + 1;
}

须要留神的是,该构造其余参数咱们其实能够不特地关怀,咱们只须要在 HVCCNALUnitArray 数组中把 HEVC 的 VPS,SPS 和 PPS 信息填入即可。

最初,填写好 Metadata 信息后,咱们还须要在发送帧数据的时候做一下批改,将 I 帧的 tag 头改成 12,P 帧 tag 不变,设置成 1 即可,如下代码所示:

    int i = 0;
    if(bIsKeyFrame)
    {//body[i++] = 0x17;// 2:Pframe  7:AVC
        body[i++] =      (m_metadata.nVideoCodec  == FLV_CODECID_HEVC) ? 0x1C:0x17;// 1:Iframe  7:AVC 12:HEVC

        if (m_bWaitingKeyFrame)
        {m_bWaitingKeyFrame = false;}
    }
    else
    {//body[i++] = 0x27;// 2:Pframe  7:AVC
        body[i++] =      (m_metadata.nVideoCodec  == FLV_CODECID_HEVC) ? 0x2C:0x27;// 1:Iframe  7:AVC 12:HEVC
    }
    body[i++] = 0x01;// AVC NALU
    body[i++] = 0x00;
    body[i++] = 0x00;
    body[i++] = 0x00;

    // NALU size
    body[i++] = size>>24;
    body[i++] = size>>16;
    body[i++] = size>>8;
    body[i++] = size&0xff;

有相干的技术问题,欢送大家和我进行技术交换:
295222688@qq.com
大家也能够退出 SkeyeRTMP 直播推流技术 QQ 群进行探讨:
102644504

退出移动版