FFmpeg5.0源碼閱讀——VideoToobox硬件解碼

??摘要:本文描述了FFmpeg中videotoobox解碼器如何進行解碼工作,如何將一個編碼的碼流解碼為最終的裸流。
??關鍵字:videotoobox,decoder,ffmpeg
??VideoToolbox 是一個低級框架,提供對硬件編碼器和解碼器的直接訪問。 它提供視頻壓縮和解壓縮服務,以及存儲在 CoreVideo 像素緩沖區中的光柵圖像格式之間的轉換服務。 這些服務以會話對象(壓縮、解壓縮和像素傳輸)的形式提供,并作為 Core Foundation (CF) 類型輸出。 VideoToolbox支持H.263, H.264, HEVC, MPEG-1, MPEG-2, MPEG-4 Part 2, ProRes解碼,H.264, HEVC, ProRes編碼,最新的版本似乎也支持了VP9解碼。

1 主流程

1.1 涉及的Context

??FFmpeg中每個解碼器都有自己的Context描述,該描述按照約定的格式描述對應的解碼器參數和解碼器的處理函數指針。FFmpeg中的VideoToolbox解碼器主要實現代碼在libavcodec/videotoobox.{h,c}中,其中針對每一種支持的解碼格式定義了一個獨立的Context,比如ff_h263_videotoolbox_hwaccel,ff_h263_videotoolbox_hwaccel,ff_h264_videotoolbox_hwaccel,...等,只是實現上有差異,我們主要關注其中一個即可,這里主要關注ff_h264_videotoolbox_hwaccel

const AVHWAccel ff_h264_videotoolbox_hwaccel = {.name           = "h264_videotoolbox",.type           = AVMEDIA_TYPE_VIDEO,.id             = AV_CODEC_ID_H264,.pix_fmt        = AV_PIX_FMT_VIDEOTOOLBOX,.alloc_frame    = ff_videotoolbox_alloc_frame,.start_frame    = ff_videotoolbox_h264_start_frame,.decode_slice   = ff_videotoolbox_h264_decode_slice,.decode_params  = videotoolbox_h264_decode_params,.end_frame      = videotoolbox_h264_end_frame,.frame_params   = ff_videotoolbox_frame_params,.init           = ff_videotoolbox_common_init,.uninit         = ff_videotoolbox_uninit,.priv_data_size = sizeof(VTContext),
};

??該結構中定義了:

  • 解碼器的名稱;
  • 解碼數據的類型;
  • 解碼器ID;
  • 硬件解碼的格式;
  • 申請一個硬件相關的幀結構的函數指針;
  • 解碼開始前針對幀進行內存拷貝之類的操作;
  • 解碼數據;
  • 解析解碼器需要的參數比如sps等;
  • 送幀結束后的后處理;
  • 初始化硬件解碼器;
  • 銷毀硬件解碼器;
  • 當前硬件解碼器的描述結構。

??ff_h264_videotoolbox_hwaccel是存儲在hw_configs中的,運行時遍歷該列表尋找期望的硬件解碼器。所以解碼工作是先經過FFmpeg內的ff_h264_decoder解碼器再進入硬件解碼器的。

const AVCodec ff_h264_decoder = {.name                  = "h264",.long_name             = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),.type                  = AVMEDIA_TYPE_VIDEO,.id                    = AV_CODEC_ID_H264,.priv_data_size        = sizeof(H264Context),.init                  = h264_decode_init,.close                 = h264_decode_end,.decode                = h264_decode_frame,.capabilities          = /*AV_CODEC_CAP_DRAW_HORIZ_BAND |*/ AV_CODEC_CAP_DR1 |AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS |AV_CODEC_CAP_FRAME_THREADS,.hw_configs            = (const AVCodecHWConfigInternal *const []) {
#if CONFIG_H264_DXVA2_HWACCELHWACCEL_DXVA2(h264),
#endif
#if CONFIG_H264_D3D11VA_HWACCELHWACCEL_D3D11VA(h264),
#endif
#if CONFIG_H264_D3D11VA2_HWACCELHWACCEL_D3D11VA2(h264),
#endif
#if CONFIG_H264_NVDEC_HWACCELHWACCEL_NVDEC(h264),
#endif
#if CONFIG_H264_VAAPI_HWACCELHWACCEL_VAAPI(h264),
#endif
#if CONFIG_H264_VDPAU_HWACCELHWACCEL_VDPAU(h264),
#endif
#if CONFIG_H264_VIDEOTOOLBOX_HWACCELHWACCEL_VIDEOTOOLBOX(h264),
#endifNULL},.caps_internal         = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_EXPORTS_CROPPING |FF_CODEC_CAP_ALLOCATE_PROGRESS | FF_CODEC_CAP_INIT_CLEANUP,.flush                 = h264_decode_flush,.update_thread_context = ONLY_IF_THREADS_ENABLED(ff_h264_update_thread_context),.update_thread_context_for_user = ONLY_IF_THREADS_ENABLED(ff_h264_update_thread_context_for_user),.profiles              = NULL_IF_CONFIG_SMALL(ff_h264_profiles),.priv_class            = &h264_class,
};

??VTContextVT解碼過程中描述VT的Context。

typedef struct VTContext {// The current bitstream buffer.uint8_t                     *bitstream;// The current size of the bitstream.int                         bitstream_size;// The reference size used for fast reallocation.int                         allocated_size;// The core video bufferCVImageBufferRef            frame;// Current dummy frames context (depends on exact CVImageBufferRef params).struct AVBufferRef         *cached_hw_frames_ctx;// Non-NULL if the new hwaccel API is used. This is only a separate struct// to ease compatibility with the old API.struct AVVideotoolboxContext *vt_ctx;// Current H264 parameters (used to trigger decoder restart on SPS changes).uint8_t                     sps[3];bool                        reconfig_needed;void *logctx;
} VTContext;

1.2 主要流程

在這里插入圖片描述

2 每個步驟的具體實現

2.1ff_videotoolbox_common_init

??ff_videotoolbox_common_init在初始化解碼器時調用,一般是在avcodec_open2時初始化硬件解碼器。一般FFmpeg為了更加準確的探測當前視頻的媒體信息,在avformat_find_stream_info時就會初始化解碼器解碼少部分的幀來進行流媒體信息探測。
??初始化時首先就時申請VT的Context內存,并設置一些參數,實際上只設置了VT的callback函數和PixFormat。之后及時根據需要初始化AVHWFramesContext,主要就是申請內存并設置幀格式比如寬高,格式等等。
??最后就是調用videotoolbox_start創建VT的Session,創建的過程比較簡單就是直接調用Apple的API創建Session,需要重點關注的是如何設置的。具體的實現函數為videotoolbox_decoder_config_create,其中設置硬件加速的配置時寫死的,無法進行配置。另外就是從當前的CodecCteonxt中取出sps等信息送給解碼器,如果沒有這些信息,解碼器是無法準確識別出時間戳信息的。sps和pps的解析是由FFmpeg完成的。

    switch (codec_type) {case kCMVideoCodecType_MPEG4Video :if (avctx->extradata_size)data = videotoolbox_esds_extradata_create(avctx);if (data)CFDictionarySetValue(avc_info, CFSTR("esds"), data);break;case kCMVideoCodecType_H264 :data = ff_videotoolbox_avcc_extradata_create(avctx);if (data)CFDictionarySetValue(avc_info, CFSTR("avcC"), data);break;case kCMVideoCodecType_HEVC :data = ff_videotoolbox_hvcc_extradata_create(avctx);if (data)CFDictionarySetValue(avc_info, CFSTR("hvcC"), data);break;
#if CONFIG_VP9_VIDEOTOOLBOX_HWACCELcase kCMVideoCodecType_VP9 :data = ff_videotoolbox_vpcc_extradata_create(avctx);if (data)CFDictionarySetValue(avc_info, CFSTR("vpcC"), data);break;
#endifdefault:break;}

??解碼callback的實現比較簡單就是Retain一下CVPixelBuffer。

static void videotoolbox_decoder_callback(void *opaque,void *sourceFrameRefCon,OSStatus status,VTDecodeInfoFlags flags,CVImageBufferRef image_buffer,CMTime pts,CMTime duration)
{VTContext *vtctx = opaque;if (vtctx->frame) {CVPixelBufferRelease(vtctx->frame);vtctx->frame = NULL;}if (!image_buffer) {av_log(vtctx->logctx,  AV_LOG_DEBUG,"vt decoder cb: output image buffer is null: %i\n", status);return;}vtctx->frame = CVPixelBufferRetain(image_buffer);
}

2.2 videotoolbox_h264_decode_paramsff_videotoolbox_frame_params

?&esmp;videotoolbox_h264_decode_params主要的工作就是將上層解碼出來額sps和pps信息拷貝到VTContext中。

case H264_NAL_SPS: {GetBitContext tmp_gb = nal->gb;if (avctx->hwaccel && avctx->hwaccel->decode_params) {ret = avctx->hwaccel->decode_params(avctx,nal->type,nal->raw_data,nal->raw_size);if (ret < 0)goto end;}if (ff_h264_decode_seq_parameter_set(&tmp_gb, avctx, &h->ps, 0) >= 0)break;av_log(h->avctx, AV_LOG_DEBUG,"SPS decoding failure, trying again with the complete NAL\n");init_get_bits8(&tmp_gb, nal->raw_data + 1, nal->raw_size - 1);if (ff_h264_decode_seq_parameter_set(&tmp_gb, avctx, &h->ps, 0) >= 0)break;ff_h264_decode_seq_parameter_set(&nal->gb, avctx, &h->ps, 1);break;

??ff_videotoolbox_frame_params比較簡單就是將CodecContext中的參數傳遞給HWFramesContext。

ff_videotoolbox_alloc_frame,ff_videotoolbox_h264_start_frame,ff_videotoolbox_h264_decode_slice,videotoolbox_h264_end_frame

??這幾個函數每一幀都會調用,順序是alloc_frame->start_frame->decode_frame->end_frame
??ff_videotoolbox_alloc_frame用來申請一塊內存,此時的內存只是一塊兒裸內存只是將release函數指針設置成了VT的release指針,還未與CVPixelBuffer綁定,綁定是在解碼器的Callback中進行的。
??ff_videotoolbox_h264_start_frame主要就是將上層傳下來的stream數據流拷貝到VTContext中。
??videotoolbox_common_decode_slice也是拷貝數據流。
??videotoolbox_h264_end_frame才是具體將數據送給解碼器的地方,核心的地方就是videotoolbox_session_decode_frame,這里送給解碼器的數據流就上上面拷貝的數據流,需要注意的是在初始化時的callback中只是做了拷貝內存其他什么也沒有做。這是因為在這里調用了VTDecompressionSessionWaitForAsynchronousFrames等待異步解碼完成,能夠保證上一幀解碼完成后才送下一幀數據。

2.3 ff_videotoolbox_uninit

??ff_videotoolbox_uninit比較簡單就是釋放解碼器的Context和緩存中的內存。

  • Apple Documentation——VideoToolbox

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

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

相關文章

WebRTC音視頻通話-RTC直播本地視頻及相冊視頻文件

WebRTC音視頻通話-RTC直播本地視頻及相冊視頻文件 WebRTC音視頻通話-RTC直播本地視頻文件效果圖如下 WebRTC音視頻通話-RTC直播本地視頻文件時候&#xff0c;用到了AVPlayer、CADisplayLink。 一、通過AVPlayer播放本地視頻 AVPlayer是什么&#xff1f; AVPlayer是基于AV…

35_windows環境debug Nginx 源碼-CLion配置CMake和啟動

文章目錄 生成 CMakeLists.txt 組態檔35_windows環境debug Nginx 源碼-CLion配置CMake和啟動生成 CMakeLists.txt 組態檔 修改auto目錄configure文件,在 . auto/make 上邊增加 . auto/cmake, 大概在 106 行。在 auto 目錄下創建cmake 文件其內容如下: #!/usr/bin/env bash NG…

從外部訪問K8s中Pod的五種方式

hostNetwork、 hostPort、 NodePort、 LoadBalancer、 Ingress 暴露Pod與Service一樣&#xff0c;因為Pod就是Service的backend 1、hostNetwork&#xff1a;true 這是一種直接定義 Pod 網絡的方式。 如果在 Pod 中使用 hostNetwork:true 配置&#xff0c; pod 中運行的應用程序…

C++頭文件

C頭文件 一般頭文件特殊頭文件windows.hbits/stdc.h 一般頭文件 C頭文件是一種包含預定義函數、類和變量聲明的文件。它們通常用于在源代碼文件中引入外部庫或模塊的功能。 頭文件的作用是提供程序所需的聲明信息&#xff0c;以便在源代碼文件中使用這些聲明。當你在源代碼文…

前端面試題-CSS

1. 盒模型 ??渲染時&#xff0c; dom 元素所采?的 布局模型。可通過 box-sizing 進?設置。根據計算寬?的區域可分為 content-box ( W3C 標準盒模型)border-box ( IE 盒模型)padding-boxmargin-box (瀏覽器未實現) 2. BFC 塊級格式化上下?&#xff0c;是?個獨?的渲染…

題解:ABC277E - Crystal Switches

題解&#xff1a;ABC277E - Crystal Switches 題目 鏈接&#xff1a;Atcoder。 鏈接&#xff1a;洛谷。 難度 算法難度&#xff1a;B。 思維難度&#xff1a;A。 調碼難度&#xff1a;C。 綜合評價&#xff1a;普及/提高。 算法 寬度優先搜索拆點思路 思路 把每個點…

Android WakefulBroadcastReceiver的使用

WakefulBroadcastReceiver 是一種特殊類型的廣播接收器&#xff0c;為應用創建和管理 PARTIAL_WAKE_LOCK 。 簡單來說&#xff0c; WakefulBroadcastReceiver 是持有系統喚醒鎖的 BroadcastReceiver &#xff0c;用于執行需要保持CPU運轉的場景。 注冊 注冊 Receiver &#…

將vue項目通過electron打包成windows可執行程序

將vue項目打包成windows可執行程序 1、準備好dist將整個項目打包 npm run build2、安裝electron依賴 npm install electron --save-dev npm install electron-packager --save-dev"electron": "^13.1.4", "electron-packager": "^15.2.0…

九耶丨閣瑞鈦倫特-在項目中找到的經典BUG是什么?

在項目中找到的經典BUG有很多種&#xff0c;以下是其中一些常見的例子&#xff1a; 空指針異常&#xff08;NullPointerException&#xff09;&#xff1a;當程序試圖訪問一個空對象或未初始化的變量時&#xff0c;會拋出空指針異常。這通常是由于缺少對變量的正確初始化或檢查…

Neo4j之FOREACH基礎

在 Neo4j 中&#xff0c;FOREACH 語句用于在查詢中對一組元素執行某些操作&#xff0c;通常是在創建或更新節點關系時。它常常與 CREATE 或 SET 等操作結合使用。 創建多個關系&#xff1a; MATCH (p:Person), (m:Movie) WHERE p.name Alice AND m.title The Matrix FOREAC…

MySQL常用練手題目

數據庫表名和字段設計 1.學生表 Student(s_id,s_name,s_birth,s_sex) 學生編號,學生姓名, 出生年月,學生性別 2.課程表 Course(c_id,c_name,t_id) 課程編號, 課程名稱, 教師編號 3.教師表 Teacher(t_id,t_name) 教師編號,教師姓名 4.成績表 Score (s_id,c_id,s_score) 學生編號…

C# window forms 進度條實現

在 C# Windows Forms 應用程序中&#xff0c;如果在后臺執行長時間運行的任務&#xff0c;并希望同時更新進度條&#xff0c;可以使用多線程來實現。這將確保進度條的更新不會阻塞主線程&#xff0c;從而保持界面的響應性。以下是一個示例&#xff0c;演示了如何在后臺執行任務…

【Datawhale 科大訊飛-基于論文摘要的文本分類與關鍵詞抽取挑戰賽】機器學習方法baseline

內容 科大訊飛AI開發者大賽NLP賽道題目&#xff1a; 基于論文摘要的文本分類與關鍵詞抽取挑戰賽 任務&#xff1a; 1.機器通過對論文摘要等信息的理解&#xff0c;判斷該論文是否屬于醫學領域的文獻。 2.提取出該論文關鍵詞。 數據集的獲取 訓練集&#xff1a; 這里讀取tit…

【基礎】Android Handler

一、博客參考 Handler機制詳解【重點】&#xff1a;https://www.jianshu.com/p/b4d745c7ff7a Handler Thread工作線程操作UI范例【重點】&#xff1a;https://www.cnblogs.com/net168/p/4075126.html 二、內存泄漏的解決&#xff1a;靜態內部類弱引用 關于 Handler&#xf…

vue+flask基于知識圖譜的抑郁癥問答系統

vueflask基于知識圖譜的抑郁癥問答系統 抑郁癥已經成為當今社會刻不容緩需要解決的問題&#xff0c;抑郁癥的危害主要有以下幾種&#xff1a;1.可導致病人情緒低落&#xff1a;抑郁癥的病人長期處于悲觀的狀態中&#xff0c;感覺不到快樂&#xff0c;總是高興不起來。2.可導致工…

智慧工地平臺工地人員管理系統 可視化大數據智能云平臺源碼

智慧工地概述&#xff1a; 智慧工地管理平臺是以物聯網、移動互聯網技術為基礎&#xff0c;充分應用大數據、人工智能、移動通訊、云計算等信息技術&#xff0c;利用前端信息采通過人機交互、感知、決策、執行和反饋等&#xff0c;實現對工程項目內人員、車輛、安全、設備、材…

elaticsearch(3)

整合springboot 1.整合依賴 注意依賴版本和安裝的版本一致 <properties> <java.version>1.8</java.version> <!-- 統一版本 --> <elasticsearch.version>7.6.1</elasticsearch.version> </properties> 導入elastics…

數據結構算法--1 順序查找二分查找

順序查找時間復雜度為O(n) 我們可以借助Python中的函數enumerate,通過enumerate遍歷列表返回其索引和值 def linnear_search(li, val):for ind, v in enumerate(li):if v val:return indelse:return None 也可以通過列表長度依次遍歷: def linear_search(li, val): # …

瀏覽器渲染原理 - 輸入url 回車后發生了什么

目錄 渲染時間點渲染流水線1&#xff0c;解析&#xff08;parse&#xff09;HTML1.1&#xff0c;DOM樹1.2&#xff0c;CSSOM樹1.3&#xff0c;解析時遇到 css 是怎么做的1.4&#xff0c;解析時遇到 js 是怎么做的 2&#xff0c;樣式計算 Recalculate style3&#xff0c;布局 la…

創建react native項目的筆記

創建react native項目的筆記 重新下載 ruby安裝 watchman安裝 cocoapods安裝 react native 項目創建好項目后安裝 ios 依賴清除設備緩存安裝 android 依賴鏈接 網易 mumu 模擬器安裝路由 Navigation頁面之間的跳轉邏輯自定義頭部樣式判斷不同設備平臺代碼示例安裝 Axios安裝本地…