FFmpeg 从入门到出家(HEVC 在 RTMP 中的扩展) - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
KSClive
V2EX    问与答

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

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

    由金山云视频云技术团队提供: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     Solana     2781 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 02:09 PVG 10:09 LAX 18:09 JFK 21:09
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86