FFmpeg源代碼簡單分析-編碼-av_write_trailer()

參考鏈接:

  • FFmpeg源代碼簡單分析:av_write_trailer()_雷霄驊的博客-CSDN博客_av_malloc

av_write_trailer()

  • av_write_trailer()用于輸出文件尾,它的聲明位于libavformat\avformat.h,如下所示
/*** Write the stream trailer to an output media file and free the* file private data.** May only be called after a successful call to avformat_write_header.** @param s media file handle* @return 0 if OK, AVERROR_xxx on error*/
int av_write_trailer(AVFormatContext *s);
  • 它只需要指定一個參數,即用于輸出的AVFormatContext。
  • 函數正常執行后返回值等于0。
  • av_write_trailer()的定義位于libavformat\mux.c,如下所示。
int av_write_trailer(AVFormatContext *s)
{FFFormatContext *const si = ffformatcontext(s);AVPacket *const pkt = si->parse_pkt;int ret1, ret = 0;for (unsigned i = 0; i < s->nb_streams; i++) {AVStream *const st  = s->streams[i];FFStream *const sti = ffstream(st);if (sti->bsfc) {ret1 = write_packets_from_bsfs(s, st, pkt, 1/*interleaved*/);if (ret1 < 0)av_packet_unref(pkt);if (ret >= 0)ret = ret1;}}ret1 = interleaved_write_packet(s, pkt, 1, 0);if (ret >= 0)ret = ret1;if (s->oformat->write_trailer) {if (!(s->oformat->flags & AVFMT_NOFILE) && s->pb)avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_TRAILER);if (ret >= 0) {ret = s->oformat->write_trailer(s);} else {s->oformat->write_trailer(s);}}deinit_muxer(s);if (s->pb)avio_flush(s->pb);if (ret == 0)ret = s->pb ? s->pb->error : 0;for (unsigned i = 0; i < s->nb_streams; i++) {av_freep(&s->streams[i]->priv_data);av_freep(&ffstream(s->streams[i])->index_entries);}if (s->oformat->priv_class)av_opt_free(s->priv_data);av_freep(&s->priv_data);av_packet_unref(si->pkt);return ret;
}
  • 從源代碼可以看出av_write_trailer()主要完成了以下兩步工作:
    • (1)循環調用輸出將還未輸出的AVPacket。
    • (2)調用AVOutputFormat的write_trailer(),輸出文件尾。

AVOutputFormat->write_trailer()

  • AVOutputFormat的write_trailer()是一個函數指針,指向特定的AVOutputFormat中的實現函數。
  • 我們以FLV對應的AVOutputFormat為例,看一下它的定義,如下所示。
const AVOutputFormat ff_flv_muxer = {.name           = "flv",.long_name      = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),.mime_type      = "video/x-flv",.extensions     = "flv",.priv_data_size = sizeof(FLVContext),.audio_codec    = CONFIG_LIBMP3LAME ? AV_CODEC_ID_MP3 : AV_CODEC_ID_ADPCM_SWF,.video_codec    = AV_CODEC_ID_FLV1,.init           = flv_init,.write_header   = flv_write_header,.write_packet   = flv_write_packet,.write_trailer  = flv_write_trailer,.check_bitstream= flv_check_bitstream,.codec_tag      = (const AVCodecTag* const []) {flv_video_codec_ids, flv_audio_codec_ids, 0},.flags          = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS |AVFMT_TS_NONSTRICT,.priv_class     = &flv_muxer_class,
};
  • 從FLV對應的AVOutputFormat結構體的定義我們可以看出,write_trailer()指向了flv_write_trailer()函數

flv_write_trailer()

  • flv_write_trailer()函數的定義位于libavformat\flvenc.c,如下所示。
static int flv_write_trailer(AVFormatContext *s)
{int64_t file_size;AVIOContext *pb = s->pb;FLVContext *flv = s->priv_data;int build_keyframes_idx = flv->flags & FLV_ADD_KEYFRAME_INDEX;int i, res;int64_t cur_pos = avio_tell(s->pb);if (build_keyframes_idx) {FLVFileposition *newflv_posinfo, *p;avio_seek(pb, flv->videosize_offset, SEEK_SET);put_amf_double(pb, flv->videosize);avio_seek(pb, flv->audiosize_offset, SEEK_SET);put_amf_double(pb, flv->audiosize);avio_seek(pb, flv->lasttimestamp_offset, SEEK_SET);put_amf_double(pb, flv->lasttimestamp);avio_seek(pb, flv->lastkeyframetimestamp_offset, SEEK_SET);put_amf_double(pb, flv->lastkeyframetimestamp);avio_seek(pb, flv->lastkeyframelocation_offset, SEEK_SET);put_amf_double(pb, flv->lastkeyframelocation + flv->keyframe_index_size);avio_seek(pb, cur_pos, SEEK_SET);res = shift_data(s);if (res < 0) {goto end;}avio_seek(pb, flv->keyframes_info_offset, SEEK_SET);put_amf_string(pb, "filepositions");put_amf_dword_array(pb, flv->filepositions_count);for (newflv_posinfo = flv->head_filepositions; newflv_posinfo; newflv_posinfo = newflv_posinfo->next) {put_amf_double(pb, newflv_posinfo->keyframe_position + flv->keyframe_index_size);}put_amf_string(pb, "times");put_amf_dword_array(pb, flv->filepositions_count);for (newflv_posinfo = flv->head_filepositions; newflv_posinfo; newflv_posinfo = newflv_posinfo->next) {put_amf_double(pb, newflv_posinfo->keyframe_timestamp);}newflv_posinfo = flv->head_filepositions;while (newflv_posinfo) {p = newflv_posinfo->next;if (p) {newflv_posinfo->next = p->next;av_free(p);p = NULL;} else {av_free(newflv_posinfo);newflv_posinfo = NULL;}}put_amf_string(pb, "");avio_w8(pb, AMF_END_OF_OBJECT);avio_seek(pb, cur_pos + flv->keyframe_index_size, SEEK_SET);}end:if (flv->flags & FLV_NO_SEQUENCE_END) {av_log(s, AV_LOG_DEBUG, "FLV no sequence end mode open\n");} else {/* Add EOS tag */for (i = 0; i < s->nb_streams; i++) {AVCodecParameters *par = s->streams[i]->codecpar;FLVStreamContext *sc = s->streams[i]->priv_data;if (par->codec_type == AVMEDIA_TYPE_VIDEO &&(par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4))put_avc_eos_tag(pb, sc->last_ts);}}file_size = avio_tell(pb);if (build_keyframes_idx) {flv->datasize = file_size - flv->datastart_offset;avio_seek(pb, flv->datasize_offset, SEEK_SET);put_amf_double(pb, flv->datasize);}if (!(flv->flags & FLV_NO_METADATA)) {if (!(flv->flags & FLV_NO_DURATION_FILESIZE)) {/* update information */if (avio_seek(pb, flv->duration_offset, SEEK_SET) < 0) {av_log(s, AV_LOG_WARNING, "Failed to update header with correct duration.\n");} else {put_amf_double(pb, flv->duration / (double)1000);}if (avio_seek(pb, flv->filesize_offset, SEEK_SET) < 0) {av_log(s, AV_LOG_WARNING, "Failed to update header with correct filesize.\n");} else {put_amf_double(pb, file_size);}}}return 0;
}
  • 從flv_write_trailer()的源代碼可以看出該函數做了以下兩步工作:? 位于end:之后的兩段代碼
    • (1)如果視頻流是H.264,則添加包含EOS(End Of Stream) NALU的Tag。
    • (2)更新FLV的時長信息,以及文件大小信息。
  • 其中,put_avc_eos_tag()函數用于添加包含EOS NALU的Tag(包含結尾的一個PreviousTagSize),如下所示。
static void put_avc_eos_tag(AVIOContext *pb, unsigned ts)
{avio_w8(pb, FLV_TAG_TYPE_VIDEO);avio_wb24(pb, 5);               /* Tag Data Size */put_timestamp(pb, ts);avio_wb24(pb, 0);               /* StreamId = 0 */avio_w8(pb, 23);                /* ub[4] FrameType = 1, ub[4] CodecId = 7 */avio_w8(pb, 2);                 /* AVC end of sequence */avio_wb24(pb, 0);               /* Always 0 for AVC EOS. */avio_wb32(pb, 16);              /* Size of FLV tag */
}
  • 可以參考FLV封裝格式理解上述函數。AVCVIDEOPACKET的格式,如下所示

  • 可以看出包含EOS NALU的AVCVIDEOPACKET的AVCPacketType為2。
  • 在這種情況下,AVCVIDEOPACKET的CompositionTime字段取0,并且無需包含Data字段。?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/445936.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/445936.shtml
英文地址,請注明出處:http://en.pswp.cn/news/445936.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

科沃斯掃地機器人風扇模塊_掃地機器人不能開機,不能關機,風扇不轉

家庭的重要性自不必再細說&#xff0c;而小編今天要說的則是家庭環境的重要性。一般家庭最少居住三口人&#xff0c;兩個大人加一個孩子&#xff0c;每天回到家&#xff0c;看到家里整潔舒適的環境&#xff0c;心情該是多么地愜意。要是我們每天下班回到家中&#xff0c;看到滿…

MySQL關鍵字EXPLAIN的用法及其案例

文章目錄概述EXPLAIN輸出的列的解釋實例說明select_type的說明UNIONDEPENDENT UNION與DEPENDENT SUBQUERYSUBQUERYDERIVEDtype的說明system&#xff0c;consteq_refrefref_or_nullindex_mergeunique_subqueryindex_subqueryrangeindexALLextra的說明DistinctNot existsRange ch…

FFmpeg源代碼簡單分析-其他-日志輸出系統(av_log()等)

參考鏈接 FFmpeg源代碼簡單分析&#xff1a;日志輸出系統&#xff08;av_log()等&#xff09;_雷霄驊的博客-CSDN博客_ffmpeg源碼分析 日志輸出系統&#xff08;av_log()等&#xff09; 本文分析一下FFmpeg的日志&#xff08;Log&#xff09;輸出系統的源代碼。日志輸出部分的…

FFmpeg源代碼簡單分析-其他-AVClass和AVoption

參考鏈接 FFmpeg源代碼簡單分析&#xff1a;結構體成員管理系統-AVClass_雷霄驊的博客-CSDN博客FFmpeg源代碼簡單分析&#xff1a;結構體成員管理系統-AVOption_雷霄驊的博客-CSDN博客 概述 AVOption用于在FFmpeg中描述結構體中的成員變量。它最主要的作用可以概括為兩個字&a…

oracle手工收集awr報告_oracle手工生成AWR報告方法記錄-阿里云開發者社區

AWR(Automatic Workload Repository)報告是我們進行日常數據庫性能評定、問題SQL發現的重要手段。熟練掌握AWR報告&#xff0c;是做好開發、運維DBA工作的重要基本功。AWR報告的原理是基于Oracle數據庫的定時鏡像功能。默認情況下&#xff0c;Oracle數據庫后臺進程會以一定間隔…

IntelliJ IDEA 默認快捷鍵大全

文章目錄Remember these ShortcutsGeneralDebuggingSearch / ReplaceEditingRefactoringNavigationCompile and RunUsage SearchVCS / Local HistoryLive Templates參考資料Remember these Shortcuts 常用功能快捷鍵備注●Smart code completionCtrl Shift Space-●Search e…

python爬蟲的數據如何解決亂碼_寫爬蟲時如何解決網頁亂碼問題

實戰講解&#xff0c;文章較長&#xff0c;對爬蟲比較熟悉的瀏覽翻看章節 2.3 獲取新聞文本內容。寫爬蟲時經常對網址發起請求&#xff0c;結果返回的html數據除了標簽能看懂&#xff0c;其他的全部是亂碼。大家如果對爬蟲感興趣&#xff0c;請耐心閱讀本文&#xff0c;我們就以…

FFmpeg源代碼簡單分析-其他-libswscale的sws_getContext()

參考鏈接 FFmpeg源代碼簡單分析&#xff1a;libswscale的sws_getContext()_雷霄驊的博客-CSDN博客 libswscale的sws_getContext() FFmpeg中類庫libswsscale用于圖像處理&#xff08;縮放&#xff0c;YUV/RGB格式轉換&#xff09;libswscale是一個主要用于處理圖片像素數據的類…

IntelliJ IDEA 學習筆記

IDEA教學視頻 文章目錄1.IntelliJ IDEA的介紹和優勢IDEA 的主要優勢2.版本介紹與安裝前的準備3.IDEA的卸載4.IDEA的安裝5.安裝目錄和設置目錄結構的說明安裝目錄設置目錄6.啟動IDEA并執行HelloWorld7.Module的使用8.IDEA的常用設置9.快捷鍵的設置10.常用的快捷鍵的使用111.常用…

機器學習頂刊文獻_人工智能頂刊TPAMI2019最新《多模態機器學習綜述》

原標題&#xff1a;人工智能頂刊TPAMI2019最新《多模態機器學習綜述》來源&#xff1a;專知摘要&#xff1a;”當研究問題或數據集包括多個這樣的模態時&#xff0c;其特征在于多模態。【導讀】人工智能領域最頂級國際期刊IEEE Transactions on Pattern Analysis and Machine I…

Windows上同時運行兩個Tomcat

步驟 1.獲得免安裝包 從Tomcat官網下載免安裝包。 2.解壓復制 解壓并創建兩個副本tomcat1和tomcat2&#xff0c;它們的路徑分別為&#xff1a; tomcat1&#xff1a;C:\tomcat\double\apache-tomcat-7.0.90-8081tomcat2&#xff1a;C:\tomcat\double\apache-tomcat-7.0.90-…

FFmpeg源代碼簡單分析-其他-libswscale的sws_scale()

參考鏈接 FFmpeg源代碼簡單分析&#xff1a;libswscale的sws_scale()_雷霄驊的博客-CSDN博客_bad dst image pointers libswscale的sws_scale() FFmpeg的圖像處理&#xff08;縮放&#xff0c;YUV/RGB格式轉換&#xff09;類庫libswsscale中的sws_scale()函數。libswscale是一…

布朗橋python_MATLAB 里面有哪些加快程序運行速度的方法呢,求分享?

挖墳了…睡不著覺當個備忘錄記一下用過的方法吧1. 循環向量化2. 利用函數的矩陣輸入功能批量處理3. 必須用for且費時的地方改成單層parfor&#xff0c;要是循環次數比cpu核數還少反而會拖慢程序4. 非常大的矩陣的運算可以用gpuArray(這個在matlab 深度學習工具箱中深有體會)5. …

FFmpeg源代碼簡單分析-其他-libavdevice的avdevice_register_all()

參考鏈接 FFmpeg源代碼簡單分析&#xff1a;libavdevice的avdevice_register_all()_雷霄驊的博客-CSDN博客 libavdevice的avdevice_register_all() FFmpeg中libavdevice注冊設備的函數avdevice_register_all()。avdevice_register_all()在編程中的使用示例可以參考文章&#…

Tomcat無需輸入項目名,直接用域名訪問項目

問題 在Tomcat上開發Web應用&#xff0c;通常是將應用放置Tomcat主目錄下webapps&#xff0c;然后在瀏覽器地址欄輸入域名應用名&#xff08;如http://localhost:8080/app&#xff09;對應用進行訪問。 為了方便開發&#xff0c;打算直接用域名訪問項目。例如&#xff0c;在瀏…

藍牙該串口設備不存在或已被占用_電腦識別不了串口設備如何解決_電腦檢測不到串口怎么辦...

2015-09-07 10:46:45win8.1系統USB轉串口不能識別設備出現錯誤代碼10的解決方法分享給大家&#xff0c;win8.1系統插入USB設備提示“指定不存在的設備”&#xff0c;左下角有小黃色感嘆號&#xff0c;導致設備無法識別不能識別...2016-12-02 10:52:57一般情況下&#xff0c;win…

FFmpeg源代碼簡單分析-其他-libavdevice的gdigrab

參考鏈接 FFmpeg源代碼簡單分析&#xff1a;libavdevice的gdigrab_雷霄驊的博客-CSDN博客_gdigrab libavdevice的gdigrab GDIGrab用于在Windows下屏幕錄像&#xff08;抓屏&#xff09;gdigrab的源代碼位于libavdevice\gdigrab.c。關鍵函數的調用關系圖如下圖所示。圖中綠色背…

分區和分片的區別_PHP: 分區和分片 - Manual

分區和分片數據庫群組是由于各種各樣的原因建立的&#xff0c;他可以提升處理能力、容忍錯誤&#xff0c;并且提升大量服務器同時工作的的性能。群組有時會組合分區和共享功能&#xff0c;來將大量復雜的任務分拆成更加簡單的任務&#xff0c;更加可控的單元。插件可以支持各種…

Ubuntu安裝GmSSL庫適用于ubuntu18和ubuntu20版本

參考鏈接 編譯與安裝【GmSSL】GmSSL 與 OpenSSL 共存的安裝方法_阿卡基YUAN的博客-CSDN博客_openssl和gmssl在Linux下安裝GmSSL_百里楊的博客-CSDN博客_安裝gmssl ubuntu18操作 需要超級管理員權限本人將下載的安裝包master.zip和安裝的位置都設定在/usr/local下創建文件夾/u…

Windows7右鍵菜單欄添加打開cmd項

背景簡介 眾所周知&#xff0c;在Linux桌面操作系統中的工作目錄窗口中&#xff0c;單擊鼠標右鍵&#xff0c;彈出的菜單欄通常有一項“打開終端”&#xff0c;然后移動鼠標點擊該項&#xff0c;就可以打開Shell窗口&#xff0c;在當前工作目錄進行命令行操作。 但是&#xf…