日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

FFmpeg代码导读——HEVC在RTMP中的扩展

發布時間:2024/4/11 c/c++ 65 豆豆
生活随笔 收集整理的這篇文章主要介紹了 FFmpeg代码导读——HEVC在RTMP中的扩展 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.


視頻流媒體中視頻數據的傳輸占據了絕大部分的帶寬,如何提升編碼效率、減小帶寬使用、提升畫面質量,成為音視頻開發者努力的重點。HEVC編碼格式的推出為此帶來了突破點。對于直播而言,大部分推拉流協議是基于RTMP的,因此本文將主要介紹如何在RTMP協議中增加對HEVC視頻編碼格式的支持。


本文來自金山云OBG事業部投稿,是《FFmpeg從入門到出家》系列的第一篇下半部分,由LiveVideoStack審校整理,希望能讓大家對FFmpeg有更深入了解。閱讀上半部分內容請點擊【閱讀原文】


文 / 施雪梅


4. HEVC在RTMP中的擴展


為推進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;

3.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中主要完成了以下工作:


  • 寫入FLV Header;

  • 寫入Metadata;

  • 如果音頻編碼格式為AAC,則寫入第一個Audio Tag,其AudioTagBody中存放的是AAC sequence header;

  • 如果視頻編碼格式為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);?


    5. 結束語


    本文簡單介紹了如何在FFmpeg中擴展rtmp協議對HEVC編碼格式的支持,而要將HEVC應用于直播整體方案,除推流端和播放端要提供相應能力外,源站、CDN、轉碼服務同樣都需要提供這種能力。金山云的所有視頻服務中,已完全支持HEVC視頻編碼格式,歡迎大家使用。


    總結

    以上是生活随笔為你收集整理的FFmpeg代码导读——HEVC在RTMP中的扩展的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。