【GPU編解碼】GPU硬解碼---DXVA
【GPU編解碼】GPU硬解碼---DXVA
前面介紹利用NVIDIA公司提供的CUVID庫進行視頻硬解碼,下面將介紹利用DXVA進行硬解碼。
一、DXVA介紹
DXVA是微軟公司專門定制的視頻加速規範,是一種接口規範。DXVA規範制定硬件加速解碼可分四級:VLD,控制BitStream;IDCT,反余弦變換;Mocomp,運動補償,Pixel Prediction;PostProc,顯示後處理。其中,VLD加速等級最高,所以其包含IDCT、MoCoopm和PostProc;IDCT加速次之,包含MoCoopm和PostProc;最後MoComp加速僅包含PostProc。一款顯卡芯片在硬件支持DXVA規範,並不代表它就實現了DXVA所有功能。DXVA_Checker
二、使用FFmpeg中DXVA技術硬解碼
基本思路:
1.根據FFmpeg對編碼器的描述,實現自定義的硬解碼器。
2.通過REGISTER_ENCODEC(X,x)將自定義的視頻編碼器添加到視頻編解碼器。
3.在視頻解碼,根據編碼器ID或編碼器名稱找到視頻編解碼器中自定義的視頻解碼器。
4.利用自定義的視頻解碼器,解碼視頻。
其關鍵步驟是:自定義解碼器的實現,需要參考FFmpeg源碼中,解碼器的定義和接口設計。
基於DXVA的自定義解碼器實現
1.熟悉FFmpeg中編解碼的組織方式
下圖是ffmpeg編解碼組織的簡單示意圖。
由示意圖可知,編解碼器由全局鏈表組織,可根據編碼器的名稱或ID,獲取編解碼器。
編解碼器的具體編解碼的具體工作,由編解碼器定義的函數指針完成。
自定義解碼器時,需要按照AVCodec結構體,定義解碼器的屬性,然後註冊到全局編解碼器鏈表中。
2.基於DXVA解碼器的定義實現
ff_h264_dxva2_decoder的定義如下:
1 AVCodec ff_h264_dxva2_decoder = { 2 .name = "h264_dxva2", 3 .type = AVMEDIA_TYPE_VIDEO, 4 .id = AV_CODEC_ID_H264, 5 .priv_data_size = sizeof(DXVA2_DecoderContext), 6 .init = h264_dxva2dec_init, 7 .close = h264_dxva2dec_close, 8 .decode = h264_dxva2dec_decode, 9 .capabilities = CODEC_CAP_DELAY, 10 .flush = h264_dxva2dec_flush, 11 .long_name = NULL_IF_CONFIG_SMALL("H.264 (DXVA2 acceleration)"), 12 };
ff_h264_dxva2_decoder的函數指針對應的函數定義如下:
1 static int h264_dxva2dec_decode(AVCodecContext *avctx, void *data, int *got_frame, 2 AVPacket *avpkt) 3 { 4 return ff_dxva2dec_decode(avctx,data,got_frame,avpkt,&ff_h264_decoder); 5 } 6 7 static av_cold int h264_dxva2dec_close(AVCodecContext *avctx) 8 { 9 return ff_dxva2dec_close(avctx,&ff_h264_decoder); 10 } 11 12 static av_cold int h264_dxva2dec_init(AVCodecContext *avctx) 13 { 14 return ff_dxva2dec_init(avctx,&ff_h264_dxva2_decoder,&ff_h264_decoder); 15 } 16 17 static void h264_dxva2dec_flush(AVCodecContext *avctx) 18 { 19 ff_dxva2dec_flush(avctx,&ff_h264_decoder); 20 }
上述代碼,只是ff_dxva2dec_init(),ff_dxva2dec_flush(),ff_dxva2dec_decode(),ff_dxva2dec_close()的封裝,具體解碼的實現,由ff_dxva2dec_xxx相關函數完成,其代碼實現如下:
1 static int get_buffer(struct AVCodecContext *avctx, AVFrame *pic)
2 {
3 int ret;
4 DXVA2_DecoderContext *ctx = (DXVA2_DecoderContext *)avctx->priv_data;
5 dxva2_context *dxva2_ctx = &ctx->dxva2_ctx;
6 avctx->pix_fmt = ctx->pix_fmt;
7 ff_init_buffer_info(avctx, pic);
8 if ((ret = ctx->get_buffer(avctx,pic)) < 0) {
9 av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
10 return ret;
11 }
12 if (dxva2_ctx) {
13 if (av_get_dxva2_surface(dxva2_ctx, pic)) {
14 av_log(NULL, AV_LOG_ERROR, "VaGrabSurface failed");
15 return -1;
16 }
17 return 0;
18 } else {
19 av_log(NULL, AV_LOG_ERROR, "No dxva2 context, get buffer failed");
20 return -1;
21 }
22 }
23
24 static void release_buffer(struct AVCodecContext *avctx, AVFrame *pic)
25 {
26 DXVA2_DecoderContext *ctx = (DXVA2_DecoderContext *)avctx->priv_data;
27 dxva2_context *dxva2_ctx = &ctx->dxva2_ctx;
28 if (dxva2_ctx) {
29 av_release_dxva2_surface(dxva2_ctx, pic);
30 }
31 ctx->release_buffer(avctx,pic);
32 for (int i = 0; i < 4; i++)
33 pic->data[i] = NULL;
34 }
35
36 static enum PixelFormat get_format(AVCodecContext *p_context,
37 const enum PixelFormat *pi_fmt)
38 {
39 return AV_PIX_FMT_DXVA2_VLD;
40 }
41 static int check_format(AVCodecContext *avctx)
42 {
43 uint8_t *pout;
44 int psize;
45 int index;
46 H264Context *h;
47 int ret = -1;
48 AVCodecParserContext *parser = NULL;
49 /* check if support */
50 switch (avctx->codec_id) {
51 case AV_CODEC_ID_H264:
52 /* init parser & parse file */
53 parser = av_parser_init(avctx->codec->id);
54 if (!parser) {
55 av_log(avctx, AV_LOG_ERROR, "Failed to open parser.\n");
56 break;
57 }
58 parser->flags = PARSER_FLAG_COMPLETE_FRAMES;
59 index = av_parser_parse2(parser, avctx, &pout, &psize, NULL, 0, 0, 0, 0);
60 if (index < 0) {
61 av_log(avctx, AV_LOG_ERROR, "Failed to parse this file.\n");
62 av_parser_close(parser);
63 }
64 h = parser->priv_data;
65 if (8 == h->sps.bit_depth_luma) {
66 if (!CHROMA444 && !CHROMA422) {
67 // only this will decoder switch to hwaccel
68 av_parser_close(parser);
69 ret = 0;
70 break;
71 }
72 } else {
73 av_log(avctx, AV_LOG_ERROR, "Unsupported file.\n");
74 av_parser_close(parser);
75 break;
76 }
77 break;
78 case AV_CODEC_ID_MPEG2VIDEO:
79 if (CHROMA_420 == get_mpeg2_video_format(avctx)) {
80 ret = 0;
81 break;
82 } else {
83 av_log(avctx, AV_LOG_ERROR, "Unsupported file.\n