流媒体服务器SMS-ANNEXB转AVCC/HVCC的简易方法

1.简介

        众所周知,h264/h265的码流封装的时候有两种模式:ANNEXB和AVCC/HVCC。h264裸流、ps、ts等格式下是annexb;flv、MP4等格式下是avcc/hvcc。

        如果完全按照标准进行封装,是一件比较麻烦的事情,本文介绍一种懒人办法,亲测有效。

        介绍一下本人的开源流媒体,点个star,有兴趣一起开发的朋友也可以联系本人:SimpleMediaServer(流媒体): 旨在开发一个可商用的,功能丰富、高性能、高可扩展的流媒体服务。支持RTSP、RTMP、WEBRTC、GB28181、HTTP-FLV、HLS、JT1078等协议。各个协议可以单独剥离出来,相互独立;也可以互相转换协议。有兴趣一起开发开源项目的朋友可以联系本人(qq 1084855601),包括且不限于音视频后端、信令开发、控制台,前端等。icon-default.png?t=N7T8https://gitee.com/inyeme/simple-media-server

2.AVCC

        h264的annexb转为avcc还是比较简单的,按照标准走就行

    *data++ = 0x01;   //configurationversion
    *data++ = spsBuffer[spsSize + 1]; //avcprofileindication
    *data++ = spsBuffer[spsSize + 2]; //profilecompatibilty
    *data++ = spsBuffer[spsSize + 3]; //avclevelindication
    *data++ = 0xff;   //reserved + lengthsizeminusone
    *data++ = 0xe1;   //num of sps
    *data++ = (uint8_t)(spsLen >> 8); //sequence parameter set length high 8 bits
    *data++ = (uint8_t)(spsLen); //sequence parameter set  length low 8 bits
    // data length 13
    memcpy(data, spsBuffer.data() + spsSize, spsLen); //H264 sequence parameter set
    data += spsLen; // 13 + spsLen

    *data++ = 0x01; //num of pps
    *data++ = (uint8_t)(ppsLen >> 8); //picture parameter set length high 8 bits
    *data++ = (uint8_t)(ppsLen); //picture parameter set length low 8 bits
    // 16 + spsLen + ppsLen
    memcpy(data, ppsBuffer.data() + ppsSize, ppsLen); //H264 picture parameter set

        avcc的第2、3、4个字节就是sps的第2、3、4个字节

3.HVCC

        h265的annexb转hvcc比较复杂,想要完全按照标准去封装,需要对sps做比较全的解析,太麻烦了,在开发项目的时候,突发灵感,既然hvcc里的字段都是vps或sps里的字段,解码器应该不会真的去解析hvcc的字段,而是直接会去解析vps,sps,pps。那hvcc那些难搞的字段是不是可以随便填呢,抱着试一试的态度,写下代码:

string H265Track::getConfig()
{
    if (!_vps || !_sps || !_pps) {
        return "";
    }

    string config;

    auto spsSize = _sps->startSize();
    auto vpsSize = _vps->startSize();
    auto ppsSize = _pps->startSize();

    auto vpsBuffer = _vps->_buffer;
    int vpsLen = _vps->size() - vpsSize;
    auto spsBuffer = _sps->_buffer;
    int spsLen = _sps->size() - spsSize;
    auto ppsBuffer = _pps->_buffer;
    int ppsLen = _pps->size() - ppsSize;

    config.resize(38  + vpsLen + spsLen + ppsLen);
    auto data = (char*)config.data();

    // *data++ = 0x1c; //key frame, AVC
    // *data++ = 0x00; //avc sequence header
    // *data++ = 0x00; //composit time 
    // *data++ = 0x00; //composit time
    // *data++ = 0x00; //composit time
    *data++ = 0x01;   //configurationversion
    // 6 byte

    *data++ = spsBuffer[spsSize + 5];  //general_profile_space(2), general_tier_flag(1), general_profile_idc(5)

    *data++ = spsBuffer[spsSize + 6]; //general_profile_compatibility_flags
    *data++ = spsBuffer[spsSize + 7]; 
    *data++ = spsBuffer[spsSize + 8]; 
    *data++ = spsBuffer[spsSize + 9]; 

    *data++ = spsBuffer[spsSize + 10]; //general_constraint_indicator_flags
    *data++ = spsBuffer[spsSize + 11]; 
    *data++ = spsBuffer[spsSize + 12]; 
    *data++ = spsBuffer[spsSize + 13]; 
    *data++ = spsBuffer[spsSize + 14]; 
    *data++ = spsBuffer[spsSize + 15]; 

    *data++ = spsBuffer[spsSize + 16]; // general_level_idc

    *data++ = spsBuffer[spsSize + 17]; // reserved(4),min_spatial_segmentation_idc(12)
    *data++ = spsBuffer[spsSize + 18]; 

    *data++ = spsBuffer[spsSize + 19]; // reserved(6),parallelismType(2)

    *data++ = spsBuffer[spsSize + 20]; // reserved(6),chromaFormat(2)
    // 22 byte 

    // 下面几个0xff乱填的,貌似不影响播放
    *data++ = 0xf << 4 | 0xf;  reserved(5),bitDepthLumaMinus8(3)

    *data++ = 0xff; // reserved(5),bitDepthChromaMinus8(3)

    *data++ = 0xff; //avgFrameRate
    *data++ = 0xff;

    *data++ = 0xff; //constantFrameRate(2),numTemporalLayers(3),temporalIdNested(1),lengthSizeMinusOne(2)

    // *data++ = 0xff;

    // *data++ = 0xff;
    // *data++ = 0xff;
    // *data++ = 0xff;
    // 默认vps,sps,pps 各一个
    *data++ = 3; //numOfArrays
    //28 byte

    *data++ = 1 << 7 | H265NalType::H265_VPS & 0x3f; //array_completeness(1),reserved(1),NAL_unit_type(6)

    // 一个vps
    *data++ = 0; //numNalus
    *data++ = 1;

    *data++ = (uint8_t)(vpsLen >> 8); //sequence parameter set length high 8 bits
    *data++ = (uint8_t)(vpsLen); //sequence parameter set  length low 8 bits

    memcpy(data, vpsBuffer.data() + vpsSize, vpsLen); //H264 sequence parameter set
    data += vpsLen; 
    // 37 + vpsLen

    *data++ = 1 << 7 | H265NalType::H265_SPS & 0x3f;

    // 一个sps
    *data++ = 0;
    *data++ = 1;

    *data++ = (uint8_t)(spsLen >> 8); //sequence parameter set length high 8 bits
    *data++ = (uint8_t)(spsLen); //sequence parameter set  length low 8 bits

    memcpy(data, spsBuffer.data() + spsSize, spsLen); //H264 sequence parameter set
    data += spsLen; 
    // 38 + vpsLen + spsLen

    *data++ = 1 << 7 | H265NalType::H265_PPS & 0x3f;

    // 一个pps
    *data++ = 0;
    *data++ = 1;

    *data++ = (uint8_t)(ppsLen >> 8); //sequence parameter set length high 8 bits
    *data++ = (uint8_t)(ppsLen); //sequence parameter set  length low 8 bits

    memcpy(data, ppsBuffer.data() + ppsSize, ppsLen); //H264 sequence parameter set
    data += ppsLen; 
    // 43  + vpsLen + spsLen + ppsLen

    return config;
}

        经实测,rtmp协议可以正常播放。

结束语

        本文介绍的方法,在开发过程中可以省下点时间,但是想学习hvcc和sps结构的,还是仔细地按照标准进行封装。

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值