顯卡是個人計算機基礎的組成部分之一,將計算機系統需要的顯示信息進行轉換驅動顯示器,并向顯示器提供逐行或隔行掃描信號,控制顯示器的正確顯示,是連接顯示器和個人計算機主板的重要組件,是“人機”的重要設備之一,其內置的并行計算能力現階段也用于深度學習等運算。 寫在前面 本文將介紹如何使用FFMPEG API進行硬件解碼。如果您不熟悉解碼過程,請先閱讀: 我是Xiaobei Dig Haha:視頻和視頻幀:FFMPEG CPU解碼API簡介 
作者之前也看到過類似的問題:視頻硬解碼和軟解碼有什么區別? 本質上沒有什么區別,該芯片用于執行編計算。 “軟”和“硬”一詞很容易引起歧義。從本質上講,使用CPU通用計算單元(無論是Intel還是AMD)都是一種軟解決方案。使用專用芯片模塊(GPU,QSV等)是一個很難的解決方案。 因此產生了差異:基礎接口不同,指令集不同,硬件驅動程序也不同。由此產生的問題很明顯: 首先,因為CPU是通用計算單元,所以接口是通用的,并且可移植性好;專用芯片模塊不能移植和互操作;其次,由于CPU接口是通用的,因此編中的許多細節都便于開發人員進行修改。專用的芯片模塊,接口和驅動程序都是由不同的制造商提供的,并且其中許多都是非開源的,因此控制內部細節更加困難。最后,在實際測試中,當前使用CPU進行編碼和解碼的效果將優于專用芯片模塊。但是,可以通過優化算法和芯片來解決此問題。這是制造商的業務,我們無法控制。 在現實生活中,是選擇硬解碼還是軟解碼? 這取決于不同的情況。例如: CPU過剩,需要對解碼過程進行精確控制,對解碼算法進行優化,對通用性要求較高,直接使用軟解決方案(即CPU解碼);如果還有其他編芯片/模塊,而CPU不足,則必須切換到硬解碼(即專用芯片解碼)。 本文將介紹如何使用FFMPEG API進行常規的硬解碼。 “常規硬解碼”是指主流FFMEG支持的硬解碼類型。作者將結合自己在QSV和CUDA解碼方面的經驗,最后分享我所涉足的一些陷阱以及一些擴展方面的小問題。 I。 FFMPEG支持的硬解碼 首先,讓我們看一下FFMPEG原生支持哪些硬解碼類型。列出AVHWDeviceType(libavutil / hwcontext.h)本機支持的所有硬解碼類型: enum AVHWDeviceType {
AV_HWDEVICE_TYPE_NONE,
AV_HWDEVICE_TYPE_VDPAU,
AV_HWDEVICE_TYPE_CUDA,
AV_HWDEVICE_TYPE_VAAPI,
AV_HWDEVICE_TYPE_DXVA2,
AV_HWDEVICE_TYPE_QSV,
AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
AV_HWDEVICE_TYPE_D3D11VA,
AV_HWDEVICE_TYPE_DRM,
AV_HWDEVICE_TYPE_OPENCL,
AV_HWDEVICE_TYPE_MEDIACODEC,
};
上面的AV_HWDEVICE_TYPE_CUDA是作者當前正在使用的CUDA,是NVIDIA的硬件加速庫,而AV_HWDEVICE_TYPE_QSV是以前的QSV,是英特爾提供的一組硬件加速解決方案。 
那么,您如何知道當前FFMPEG支持哪些硬件庫? 您可以通過命令行查看它:ffmpeg -hwaccel。在硬件加速方法中:您可以在下面看到當前的FFMPEG集成硬解碼庫。 然后,如果發現所需的硬件庫不在當前的FFMPEG中,該怎么辦? 答案是:您可能需要自己重新編譯源代碼。每個硬件解碼庫的集成方法都不同。如果是QSV,請參見 我是小貝挖哈哈:視頻和視頻幀:FFMPEG + Intel QSV硬件解決方案環境安裝文章 
如果它是另一個硬解碼庫,請自己在Internet上搜索。 (作者將在稍后添加如何將CUDA庫集成到FFMPEG中,敬請期待) II。 FFMPEG硬解碼API 硬解碼步驟類似于軟解碼步驟。作者繪制了FFMPEG硬件解碼流程圖:圖中的橙色部分是硬解碼中包含的部分,軟解碼中沒有。該圖的靈感來自博客“ FFmpeg示例硬件解碼hw_decode”,這篇文章是推薦的,簡潔的和詳細的。 

FFMPEG硬解碼流程圖 接下來,我將詳細介紹上圖的橙色部分以及與硬解碼有關的API函數。 硬解碼步驟1.查找硬解碼codec編實現的名稱。該名稱在編碼器和之間是全局唯一的(但編碼器和可以共享相同的名稱)。這是從用戶角度查找編的主要方法。 實際上,FFMPEG中的每個編都是一種結構,它維護自己的信息,特定功能和其他信息。例如,英特爾的QSV(在libavcodec / qsvdec_h264 5. c中)為: AVCodec ff_h264_qsv_decoder = {
.name = "h264_qsv",
.long_name = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (Intel Quick Sync Video acceleration)"),
.priv_data_size = sizeof(QSVH2645Context),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_H264,
.init = qsv_decode_init,
.decode = qsv_decode_frame,
.flush = qsv_decode_flush,
.close = qsv_decode_close,
.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HYBRID,
.priv_class = &class,
.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_NV12,
AV_PIX_FMT_P010,
AV_PIX_FMT_QSV,
AV_PIX_FMT_NONE },
.hw_configs = ff_qsv_hw_configs,
.bsfs = "h264_mp4toannexb",
.wrapper_name = "qsv",
};

您可以看到此編支持的編ID為AV_CODEC_ID_H264,支持的目標像素格式為{AV_PIX_FMT_NV12,AV_PIX_FMT_P010,AV_PIX_FMT_QSV,AV_PIX_FMT_NONE}。 是的,硬件與通用不同,只能支持有限的目標像素格式。 讓我們看一下CUDA(在libavcodec / cuviddec.c中)。同樣,它只能支持有限的目標像素格式: AVCodec ff_##x##_cuvid_decoder = { \
.name = #x "_cuvid", \
.long_name = NULL_IF_CONFIG_SMALL("Nvidia CUVID " #X " decoder"), \
.type = AVMEDIA_TYPE_VIDEO, \
.id = AV_CODEC_ID_##X, \
.priv_data_size = sizeof(CuvidContext), \
.priv_class = &x##_cuvid_class, \
.init = cuvid_decode_init, \
.close = cuvid_decode_end, \
.decode = cuvid_decode_frame, \
.receive_frame = cuvid_output_frame, \
.flush = cuvid_flush, \
.bsfs = bsf_name, \
.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE, \
.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_CUDA, \
AV_PIX_FMT_NV12, \
AV_PIX_FMT_P010, \
AV_PIX_FMT_P016, \
AV_PIX_FMT_NONE }, \
.hw_configs = cuvid_hw_configs, \
.wrapper_name = "cuvid", \
};
硬解碼步驟2.找到硬解目標像素 在上一節中,我了解到一個事實:硬解碼編支持的目標像素是有限的,并且它們可能不相同。因此,在找到經過硬解碼的編之后,您必須設置其目標像素(像素格式)。 enum AVHWDeviceType {
AV_HWDEVICE_TYPE_NONE,
AV_HWDEVICE_TYPE_VDPAU,
AV_HWDEVICE_TYPE_CUDA,
AV_HWDEVICE_TYPE_VAAPI,
AV_HWDEVICE_TYPE_DXVA2,
AV_HWDEVICE_TYPE_QSV,
AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
AV_HWDEVICE_TYPE_D3D11VA,
AV_HWDEVICE_TYPE_DRM,
AV_HWDEVICE_TYPE_OPENCL,
AV_HWDEVICE_TYPE_MEDIACODEC,
AV_HWDEVICE_TYPE_VULKAN,
};
此類型和名稱之間的關系表要簡單得多。它由FFMPEG代碼中的hw_type_names關系表維護(在libavutil / hwcontext.c文件中定義): static const char *const hw_type_names[] = {
[AV_HWDEVICE_TYPE_CUDA] = "cuda",
[AV_HWDEVICE_TYPE_DRM] = "drm",
[AV_HWDEVICE_TYPE_DXVA2] = "dxva2",
[AV_HWDEVICE_TYPE_D3D11VA] = "d3d11va",
[AV_HWDEVICE_TYPE_OPENCL] = "opencl",
[AV_HWDEVICE_TYPE_QSV] = "qsv",
[AV_HWDEVICE_TYPE_VAAPI] = "vaapi",
[AV_HWDEVICE_TYPE_VDPAU] = "vdpau",
[AV_HWDEVICE_TYPE_VIDEOTOOLBOX] = "videotoolbox",
[AV_HWDEVICE_TYPE_MEDIACODEC] = "mediacodec",
[AV_HWDEVICE_TYPE_VULKAN] = "vulkan",
};
typedef struct AVCodecHWConfig {
/**
* A hardware pixel format which the codec can use. !!!硬解碼codec支持的像素格式!!!
*/
enum AVPixelFormat pix_fmt;
/**
* Bit set of AV_CODEC_HW_CONFIG_METHOD_* flags, describing the possible
* setup methods which can be used with this configuration.
*/
int methods;
/**
* The device type associated with the configuration.
*
* Must be set for AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX and
* AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX, otherwise unused.
*/
enum AVHWDeviceType device_type;
} AVCodecHWConfig;
本文來自本站,轉載請注明本文網址: http://www.pc-fly.com/a/shenmilingyu/article-372911-1.html
|