V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
KSClive
V2EX  ›  问与答

FFmpeg 从入门到出家(HEVC 在 RTMP 中的扩展)

  •  
  •   KSClive · 2018-01-15 18:00:43 +08:00 · 1509 次点击
    这是一个创建于 2502 天前的主题,其中的信息可能已经有所发展或是发生改变。

    由金山云视频云技术团队提供:FFmpeg 从入门到出家第三季;

    为推进 HEVC 视频编码格式在直播方案中的落地,经过 CDN 联盟讨论,并和主流云服务厂商达成一致,规范了 HEVC 在 RTMP/FLV 中的扩展,具体修改内容见下。

    4.1 FLV 规范扩展

    HEVC 为视频编码格式,因此对 FLV 规范的扩展,只集中在 Video Tag,其它部分,无任何改动。

    4.1.1 支持 HEVC 的 VideoTagHeader

    扩展后的 VideoTagHeader 如下图所示(红色字体为新增内容):

    、 图 9. 支持 HEVC 的 FLVTagHeader 修改点如下:

    1. CodecID - 定义 HEVC 格式的值为 12 ;

    2、HEVCPacketType - 当 CodecID == 12 时,AVCPacketType 为 HEVCPacketType:

    ◦如果 HEVCPacketType 为 0,表示 HEVCVIDEOPACKET 中存放的是 HEVC sequence header ;

    ◦如果 HEVCPacketType 为 1,表示 HEVCVIDEOPACKET 中存放的是 HEVC NALU ;

    ◦如果 HEVCPacketType 为 2,表示 HEVCVIDEPACKET 中存放的是 HEVC end of sequence,即 HEVCDecoderConfigurationRecord ;

    1. CompositionTime - 当 CodecID == 12 时,同样需要 CompositionTime。

    4.1.2 支持 HEVC 的 VideoTagBody

    当 CodecID 为 12 时,VideoTagBody 中存放的就是 HEVC 视频帧内容。

    扩展后的 VideoTagBody 如下图所示(红色字体为 HEVC 新增内容):

    图 10. 支持 HEVC 的 VideoTagBody 4.2 FFmpeg 中的修改

    我们已在 FFmpeg 的各个版本上提供相关的完整修改,具体参见: https://github.com/ksvc/FFmpeg,完整 patch 获取及相关说明见: https://github.com/ksvc/FFmpeg/wiki。

    由第二章节的阐述可知,FLV 的解复用和复用功能代码分别在 libavformt/flvdec.c 和 libavformat/flvenc.c 中,扩展后的修改也都集中在这两个文件。本节将在 FFmpeg3.3 的基础上,说明修改的关键点。

    4.2.1 编码类型定义

    libavformat/flv.h 中按照 VideoTagHeader 中的 CodecID 定义了一组视频编码格式的枚举值,扩展后的枚举定义如下:

    enum {

    FLV_CODECID_H263    = 2,
    
    FLV_CODECID_SCREEN  = 3,
    
    FLV_CODECID_VP6     = 4,
    
    FLV_CODECID_VP6A    = 5,
    
    FLV_CODECID_SCREEN2 = 6,
    
    FLV_CODECID_H264    = 7,
    
    FLV_CODECID_REALH263= 8,
    
    FLV_CODECID_MPEG4   = 9,
    
    FLV_CODECID_HEVC   = 12,
    

    };

    4.2.2 FLV demux

    在解复用过程中,flv_read_packet 方法是整个过程的核心,它里面完成了对每个 Tag 的读取和解析。

    4.1.1 中提到,如果 HEVCPacketType 为 0 时,表示 HEVCVIDEOPACKET 中存放的是 HEVC sequence header,也就是 HEVCDecoderConfigurationRecord,解码时需设置 HEVCDecoderConfigurationRecord 方能正确解码。

    HEVC 与 AVC 视频帧在 FLV 中的存放格式相同,所以只需在读取 Video Tag 的地方增加 AV_CODEC_ID_HEVC 的判断条件即可,调整后的代码如下:

    if (st->codecpar->codec_id == AV_CODEC_ID_AAC ||

    st->codecpar->codec_id == AV_CODEC_ID_H264 ||

    st->codecpar->codec_id == AV_CODEC_ID_HEVC ||

    st->codecpar->codec_id == AV_CODEC_ID_MPEG4) {

    int type = avio_r8(s->pb);

    size--;
    
    if (st->codecpar->codec_id == AV_CODEC_ID_H264 ||
    

    st->codecpar->codec_id == AV_CODEC_ID_HEVC ||

    st->codecpar->codec_id == AV_CODEC_ID_MPEG4) {

         // sign extension
    
         int32_t cts = (avio_rb24(s->pb) + 0xff800000) ^ 0xff800000;
    
         pts = dts + cts;
    
         if (cts< 0) { // dts might be wrong
    
             if (!flv->wrong_dts)
    

    av_log(s, AV_LOG_WARNING,

    "Negative cts, previous timestamps might be wrong.\n");

    flv->wrong_dts = 1;

        } else if (FFABS(dts - pts) > 1000*60*15) {
    

    av_log(s, AV_LOG_WARNING,

    "invalid timestamps %"PRId64" %"PRId64"\n", dts, pts);

    dts = pts = AV_NOPTS_VALUE;

        }
    

    }

    if (type == 0 &&(!st->codecpar->extradata ||

    st->codecpar->codec_id == AV_CODEC_ID_AAC ||

    st->codecpar->codec_id == AV_CODEC_ID_HEVC ||

    st->codecpar->codec_id == AV_CODEC_ID_H264)) {

    AVDictionaryEntry *t;

       if (st->codecpar->extradata) {
    
           if ((ret = flv_queue_extradata(flv, s->pb, stream_type, size)) < 0)
    
               return ret;
    
           ret = FFERROR_REDO;
    

    goto leave;

       }
    
       if ((ret = flv_get_extradata(s, st, size)) < 0)
    
           return ret;
    
       ……
    

    }

    }

    AVCDecoderConfigurationRecord 和 HEVCDecoderConfigurationRecord 都是存放在 AVStream->AVCodecParameter->extradata 中。

    4.2.3 FLV mux

    FLV mux 的修改相对较多、header、packet、trailer 中均有涉及。

    4.2.3.1 write header

    flv_write_header 中主要完成了以下工作:

    1. 写入 FLV Header ;

    2. 写入 Metadata ;

    3. 如果音频编码格式为 AAC,则写入第一个 Audio Tag,其 AudioTagBody 中存放的是 AAC sequence header ;

    4. 如果视频编码格式为 AVC,则写入第一个 Video Tag,其中 VideoTagBody 中存放的是 AVC sequence header。

    同样,当视频编码格式 HEVC 时,也要写入第一个 VideoTag,其中 VideoTagBody 中存放的是 HEVCDecoderConfigurationRecord,修改点如下:

    avio_w8(pb, par->codec_tag | FLV_FRAME_KEY); // flags

    avio_w8(pb, 0); // AVC sequence header

    avio_wb24(pb, 0); // composition time

    if (par->codec_id == AV_CODEC_ID_HEVC)

    ff_isom_write_hvcc(pb, par->extradata, par->extradata_size, 0);

    else

    ff_isom_write_avcc(pb, par->extradata, par->extradata_size);

    ff_isom_write_hvcc 的作用是将 extradata 转为 HEVCDecoderConfigurationRecord 结构并写入。

    4.2.3.2 write packet

    flv_write_packet 的作用是写入音视频帧,其中有关写入 video 数据的地方,都需要加上 AV_CODEC_ID_HEVC 的判断条件,修改内容如下:

    else if (par->codec_id == AV_CODEC_ID_HEVC ){
    
        if (par->extradata_size> 0 && *(uint8_t*)par->extradata != 1)
    
            if ((ret = ff_hevc_annexb2mp4_buf(pkt->data, &data, &size, 0, NULL)) < 0)
    
                return ret;
    
    }
    

    ff_hevc_annexb2mp4_buf 方法的作用是将 Annex-B 格式的 HEVC 视频帧转为 HVCC 格式。

    AnnexB 与 AVCC/HVCC(ISO/IEC14496-15 中所定义,通常也称为 MPEG-4 格式)的区别在于参数集与帧格式,AnnexB 的参数集 sps、pps 以 NAL 的形式存在码流中(带内传输),以 startcode 分割 NAL。而 HVCC 的参数集存储在 extradata 中(带外传输),使用 NALU 长度(固定字节,通常为 4 字节,从 extradata 中解析)分隔 NAL。

    4.2.3.3 write trailer

    结束时需要写入 HEVC end of sequence,其格式与 AVC end of sequence 相同,直接复用即可,flv_write_trailer 的修改内容如下:

    if (par->codec_type == AVMEDIA_TYPE_VIDEO &&

           (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_HEVC || par->codec_id == AV_CODEC_ID_MPEG4))
    

    put_avc_eos_tag(pb, sc->last_ts);

    1. 结束语

    本文只是简单介绍了如何在 FFmpeg 中扩展 rtmp 协议对 HEVC 编码格式的支持,而要将 HEVC 应用于直播整体方案,除推流端和播放端要提供相应能力外,源站、CDN、转码服务同样都需要提供这种能力。金山云的所有视频服务中,已完全支持 HEVC 视频编码格式,欢迎大家使用。

    客户端的直播 /短视频 SDK,在适配了系统的 HEVC 硬编 /解码能力外,还提供了高效的 HEVC 软编 /解码方案,如果您感兴趣,可联系我们咨询或讨论。

    1 条回复    2019-10-25 17:43:27 +08:00
    jiqiren007
        1
    jiqiren007  
       2019-10-25 17:43:27 +08:00
    不更新了嘛
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3042 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 13:57 · PVG 21:57 · LAX 05:57 · JFK 08:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.