History

avconv: VAAPI hwcontext initialisation and hwaccel helper

Mark Thompson committed 10 years ago

Commit 5d273d31 parent ca8c7597 files · +694 −2Browse files

Unified · Split

7 files
  1. Makefile +1 −0
  2. avconv.c +2 −0
  3. avconv.h +9 −0
  4. avconv_filter.c +8 −1
  5. avconv_opt.c +41 −0
  6. avconv_vaapi.c +626 −0
  7. configure +7 −1
Makefile+1−0
@@ -86,6 +86,7 @@ OBJS-avconv-$(HAVE_VDPAU_X11) += avconv_vdpau.o · 85 unmodified lines
8686 OBJS-avconv-$(HAVE_DXVA2_LIB) += avconv_dxva2.o
8787 OBJS-avconv-$(CONFIG_VDA) += avconv_vda.o
8888 OBJS-avconv-$(CONFIG_LIBMFX) += avconv_qsv.o
89+OBJS-avconv-$(CONFIG_VAAPI) += avconv_vaapi.o
8990
9091 TESTTOOLS = audiogen videogen rotozoom tiny_psnr base64
9192 HOSTPROGS := $(TESTTOOLS:%=tests/%) doc/print_options
avconv.c+2−0
@@ -2736,6 +2736,8 @@ static int transcode(void) · 2735 unmodified lines
27362736 }
27372737 }
27382738
2739+ av_buffer_unref(&hw_device_ctx);
2740+
27392741 /* finished ! */
27402742 ret = 0;
27412743
avconv.h+9−0
@@ -55,6 +55,7 @@ enum HWAccelID { · 54 unmodified lines
5555 HWACCEL_DXVA2,
5656 HWACCEL_VDA,
5757 HWACCEL_QSV,
58+ HWACCEL_VAAPI,
5859 };
5960
6061 typedef struct HWAccel {
@@ -115,6 +116,8 @@ typedef struct OptionsContext { · 54 unmodified lines
115116 int nb_hwaccels;
116117 SpecifierOpt *hwaccel_devices;
117118 int nb_hwaccel_devices;
119+ SpecifierOpt *hwaccel_output_formats;
120+ int nb_hwaccel_output_formats;
118121 SpecifierOpt *autorotate;
119122 int nb_autorotate;
120123
@@ -261,6 +264,7 @@ typedef struct InputStream { · 140 unmodified lines
261264 /* hwaccel options */
262265 enum HWAccelID hwaccel_id;
263266 char *hwaccel_device;
267+ enum AVPixelFormat hwaccel_output_format;
264268
265269 /* hwaccel context */
266270 enum HWAccelID active_hwaccel_id;
@@ -270,6 +274,7 @@ typedef struct InputStream { · 3 unmodified lines
270274 int (*hwaccel_retrieve_data)(AVCodecContext *s, AVFrame *frame);
271275 enum AVPixelFormat hwaccel_pix_fmt;
272276 enum AVPixelFormat hwaccel_retrieved_pix_fmt;
277+ AVBufferRef *hw_frames_ctx;
273278
274279 /* stats */
275280 // combined size of all the packets read
@@ -428,6 +433,8 @@ extern const AVIOInterruptCB int_cb; · 152 unmodified lines
428433 extern const OptionDef options[];
429434
430435 extern const HWAccel hwaccels[];
436+extern int hwaccel_lax_profile_check;
437+extern AVBufferRef *hw_device_ctx;
431438
432439 void reset_options(OptionsContext *o);
433440 void show_usage(void);
@@ -451,5 +458,7 @@ int dxva2_init(AVCodecContext *s); · 17 unmodified lines
451458 int vda_init(AVCodecContext *s);
452459 int qsv_init(AVCodecContext *s);
453460 int qsv_transcode_init(OutputStream *ost);
461+int vaapi_decode_init(AVCodecContext *avctx);
462+int vaapi_device_init(const char *device);
454463
455464 #endif /* AVCONV_H */
avconv_filter.c+8−1
@@ -307,7 +307,7 @@ static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, · 306 unmodified lines
307307 if (ret < 0)
308308 return ret;
309309
310− if (codec->width || codec->height) {
310+ if (!hw_device_ctx && (codec->width || codec->height)) {
311311 char args[255];
312312 AVFilterContext *filter;
313313
@@ -513,6 +513,7 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, · 199 unmodified lines
513513 par->format = ist->hwaccel_retrieve_data ?
514514 ist->hwaccel_retrieved_pix_fmt : ist->dec_ctx->pix_fmt;
515515 par->time_base = tb;
516+ par->hw_frames_ctx = ist->hw_frames_ctx;
516517
517518 ret = av_buffersrc_parameters_set(ifilter->filter, par);
518519 av_freep(&par);
@@ -719,6 +720,12 @@ int configure_filtergraph(FilterGraph *fg) · 200 unmodified lines
719720 if ((ret = avfilter_graph_parse2(fg->graph, graph_desc, &inputs, &outputs)) < 0)
720721 return ret;
721722
723+ if (hw_device_ctx) {
724+ for (i = 0; i < fg->graph->nb_filters; i++) {
725+ fg->graph->filters[i]->hw_device_ctx = av_buffer_ref(hw_device_ctx);
726+ }
727+ }
728+
722729 if (simple && (!inputs || inputs->next || !outputs || outputs->next)) {
723730 av_log(NULL, AV_LOG_ERROR, "Simple filtergraph '%s' does not have "
724731 "exactly one input and output.\n", graph_desc);
avconv_opt.c+41−0
@@ -67,9 +67,14 @@ const HWAccel hwaccels[] = { · 66 unmodified lines
6767 #endif
6868 #if CONFIG_LIBMFX
6969 { "qsv", qsv_init, HWACCEL_QSV, AV_PIX_FMT_QSV },
70+#endif
71+#if CONFIG_VAAPI
72+ { "vaapi", vaapi_decode_init, HWACCEL_VAAPI, AV_PIX_FMT_VAAPI },
7073 #endif
7174 { 0 },
7275 };
76+int hwaccel_lax_profile_check = 0;
77+AVBufferRef *hw_device_ctx;
7378
7479 char *vstats_filename;
7580
@@ -321,6 +326,17 @@ static int opt_attach(void *optctx, const char *opt, const char *arg) · 245 unmodified lines
321326 return 0;
322327 }
323328
329+#if CONFIG_VAAPI
330+static int opt_vaapi_device(void *optctx, const char *opt, const char *arg)
331+{
332+ int err;
333+ err = vaapi_device_init(arg);
334+ if (err < 0)
335+ exit_program(1);
336+ return 0;
337+}
338+#endif
339+
324340 /**
325341 * Parse a metadata specifier passed as 'arg' parameter.
326342 * @param arg metadata string to parse
@@ -488,6 +504,7 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic) · 161 unmodified lines
488504 AVCodecParameters *par = st->codecpar;
489505 InputStream *ist = av_mallocz(sizeof(*ist));
490506 char *framerate = NULL, *hwaccel = NULL, *hwaccel_device = NULL;
507+ char *hwaccel_output_format = NULL;
491508 char *codec_tag = NULL;
492509 char *next;
493510
@@ -581,6 +598,19 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic) · 87 unmodified lines
581598 if (!ist->hwaccel_device)
582599 exit_program(1);
583600 }
601+
602+ MATCH_PER_STREAM_OPT(hwaccel_output_formats, str,
603+ hwaccel_output_format, ic, st);
604+ if (hwaccel_output_format) {
605+ ist->hwaccel_output_format = av_get_pix_fmt(hwaccel_output_format);
606+ if (ist->hwaccel_output_format == AV_PIX_FMT_NONE) {
607+ av_log(NULL, AV_LOG_FATAL, "Unrecognised hwaccel output "
608+ "format: %s", hwaccel_output_format);
609+ }
610+ } else {
611+ ist->hwaccel_output_format = AV_PIX_FMT_NONE;
612+ }
613+
584614 ist->hwaccel_pix_fmt = AV_PIX_FMT_NONE;
585615
586616 break;
@@ -2475,11 +2505,17 @@ const OptionDef options[] = { · 1888 unmodified lines
24752505 { "hwaccel_device", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT |
24762506 OPT_SPEC | OPT_INPUT, { .off = OFFSET(hwaccel_devices) },
24772507 "select a device for HW acceleration", "devicename" },
2508+ { "hwaccel_output_format", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT |
2509+ OPT_SPEC | OPT_INPUT, { .off = OFFSET(hwaccel_output_formats) },
2510+ "select output format used with HW accelerated decoding", "format" },
2511+
24782512 { "hwaccels", OPT_EXIT, { .func_arg = show_hwaccels },
24792513 "show available HW acceleration methods" },
24802514 { "autorotate", HAS_ARG | OPT_BOOL | OPT_SPEC |
24812515 OPT_EXPERT | OPT_INPUT, { .off = OFFSET(autorotate) },
24822516 "automatically insert correct rotate filters" },
2517+ { "hwaccel_lax_profile_check", OPT_BOOL | OPT_EXPERT, { &hwaccel_lax_profile_check},
2518+ "attempt to decode anyway if HW accelerated decoder's supported profiles do not exactly match the stream" },
24832519
24842520 /* audio options */
24852521 { "aframes", OPT_AUDIO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_audio_frames },
@@ -2535,5 +2571,10 @@ const OptionDef options[] = { · 49 unmodified lines
25352571 { "dcodec", HAS_ARG | OPT_DATA | OPT_PERFILE | OPT_EXPERT | OPT_INPUT | OPT_OUTPUT, { .func_arg = opt_data_codec },
25362572 "force data codec ('copy' to copy stream)", "codec" },
25372573
2574+#if CONFIG_VAAPI
2575+ { "vaapi_device", HAS_ARG | OPT_EXPERT, { .func_arg = opt_vaapi_device },
2576+ "set VAAPI hardware device (DRM path or X11 display name)", "device" },
2577+#endif
2578+
25382579 { NULL, },
25392580 };
avconv_vaapi.c+626−0
@@ -0,0 +1,626 @@
1+/*
2+ * This file is part of Libav.
3+ *
4+ * Libav is free software; you can redistribute it and/or
5+ * modify it under the terms of the GNU Lesser General Public
6+ * License as published by the Free Software Foundation; either
7+ * version 2.1 of the License, or (at your option) any later version.
8+ *
9+ * Libav is distributed in the hope that it will be useful,
10+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+ * Lesser General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU Lesser General Public
15+ * License along with Libav; if not, write to the Free Software
16+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17+ */
18+
19+#include "config.h"
20+
21+#include <fcntl.h>
22+#include <unistd.h>
23+
24+#include <va/va.h>
25+#if HAVE_VAAPI_X11
26+# include <va/va_x11.h>
27+#endif
28+#if HAVE_VAAPI_DRM
29+# include <va/va_drm.h>
30+#endif
31+
32+#include "libavutil/avassert.h"
33+#include "libavutil/avconfig.h"
34+#include "libavutil/buffer.h"
35+#include "libavutil/frame.h"
36+#include "libavutil/hwcontext.h"
37+#include "libavutil/hwcontext_vaapi.h"
38+#include "libavutil/imgutils.h"
39+#include "libavutil/opt.h"
40+#include "libavutil/pixfmt.h"
41+
42+#include "libavcodec/vaapi.h"
43+
44+#include "avconv.h"
45+
46+
47+static AVClass vaapi_class = {
48+ .class_name = "vaapi",
49+ .item_name = av_default_item_name,
50+ .version = LIBAVUTIL_VERSION_INT,
51+};
52+
53+#define DEFAULT_SURFACES 20
54+
55+typedef struct VAAPIDecoderContext {
56+ const AVClass *class;
57+
58+ AVBufferRef *device_ref;
59+ AVHWDeviceContext *device;
60+ AVBufferRef *frames_ref;
61+ AVHWFramesContext *frames;
62+
63+ VAProfile va_profile;
64+ VAEntrypoint va_entrypoint;
65+ VAConfigID va_config;
66+ VAContextID va_context;
67+
68+ enum AVPixelFormat decode_format;
69+ int decode_width;
70+ int decode_height;
71+ int decode_surfaces;
72+
73+ // The output need not have the same format, width and height as the
74+ // decoded frames - the copy for non-direct-mapped access is actually
75+ // a whole vpp instance which can do arbitrary scaling and format
76+ // conversion.
77+ enum AVPixelFormat output_format;
78+
79+ struct vaapi_context decoder_vaapi_context;
80+} VAAPIDecoderContext;
81+
82+
83+static int vaapi_get_buffer(AVCodecContext *avctx, AVFrame *frame, int flags)
84+{
85+ InputStream *ist = avctx->opaque;
86+ VAAPIDecoderContext *ctx = ist->hwaccel_ctx;
87+ int err;
88+
89+ err = av_hwframe_get_buffer(ctx->frames_ref, frame, 0);
90+ if (err < 0) {
91+ av_log(ctx, AV_LOG_ERROR, "Failed to allocate decoder surface.\n");
92+ } else {
93+ av_log(ctx, AV_LOG_DEBUG, "Decoder given surface %#x.\n",
94+ (unsigned int)(uintptr_t)frame->data[3]);
95+ }
96+ return err;
97+}
98+
99+static int vaapi_retrieve_data(AVCodecContext *avctx, AVFrame *input)
100+{
101+ InputStream *ist = avctx->opaque;
102+ VAAPIDecoderContext *ctx = ist->hwaccel_ctx;
103+ AVFrame *output = 0;
104+ int err;
105+
106+ av_assert0(input->format == AV_PIX_FMT_VAAPI);
107+
108+ if (ctx->output_format == AV_PIX_FMT_VAAPI) {
109+ // Nothing to do.
110+ return 0;
111+ }
112+
113+ av_log(ctx, AV_LOG_DEBUG, "Retrieve data from surface %#x.\n",
114+ (unsigned int)(uintptr_t)input->data[3]);
115+
116+ output = av_frame_alloc();
117+ if (!output)
118+ return AVERROR(ENOMEM);
119+
120+ output->format = ctx->output_format;
121+
122+ err = av_hwframe_transfer_data(output, input, 0);
123+ if (err < 0) {
124+ av_log(ctx, AV_LOG_ERROR, "Failed to transfer data to "
125+ "output frame: %d.\n", err);
126+ goto fail;
127+ }
128+
129+ err = av_frame_copy_props(output, input);
130+ if (err < 0) {
131+ av_frame_unref(output);
132+ goto fail;
133+ }
134+
135+ av_frame_unref(input);
136+ av_frame_move_ref(input, output);
137+ av_frame_free(&output);
138+
139+ return 0;
140+
141+fail:
142+ if (output)
143+ av_frame_free(&output);
144+ return err;
145+}
146+
147+
148+static const struct {
149+ enum AVCodecID codec_id;
150+ int codec_profile;
151+ VAProfile va_profile;
152+} vaapi_profile_map[] = {
153+#define MAP(c, p, v) { AV_CODEC_ID_ ## c, FF_PROFILE_ ## p, VAProfile ## v }
154+ MAP(MPEG2VIDEO, MPEG2_SIMPLE, MPEG2Simple ),
155+ MAP(MPEG2VIDEO, MPEG2_MAIN, MPEG2Main ),
156+ MAP(H263, UNKNOWN, H263Baseline),
157+ MAP(MPEG4, MPEG4_SIMPLE, MPEG4Simple ),
158+ MAP(MPEG4, MPEG4_ADVANCED_SIMPLE,
159+ MPEG4AdvancedSimple),
160+ MAP(MPEG4, MPEG4_MAIN, MPEG4Main ),
161+ MAP(H264, H264_CONSTRAINED_BASELINE,
162+ H264ConstrainedBaseline),
163+ MAP(H264, H264_BASELINE, H264Baseline),
164+ MAP(H264, H264_MAIN, H264Main ),
165+ MAP(H264, H264_HIGH, H264High ),
166+#if VA_CHECK_VERSION(0, 37, 0)
167+ MAP(HEVC, HEVC_MAIN, HEVCMain ),
168+#endif
169+ MAP(WMV3, VC1_SIMPLE, VC1Simple ),
170+ MAP(WMV3, VC1_MAIN, VC1Main ),
171+ MAP(WMV3, VC1_COMPLEX, VC1Advanced ),
172+ MAP(WMV3, VC1_ADVANCED, VC1Advanced ),
173+ MAP(VC1, VC1_SIMPLE, VC1Simple ),
174+ MAP(VC1, VC1_MAIN, VC1Main ),
175+ MAP(VC1, VC1_COMPLEX, VC1Advanced ),
176+ MAP(VC1, VC1_ADVANCED, VC1Advanced ),
177+#if VA_CHECK_VERSION(0, 35, 0)
178+ MAP(VP8, UNKNOWN, VP8Version0_3 ),
179+#endif
180+#if VA_CHECK_VERSION(0, 37, 1)
181+ MAP(VP9, VP9_0, VP9Profile0 ),
182+#endif
183+#undef MAP
184+};
185+
186+static int vaapi_build_decoder_config(VAAPIDecoderContext *ctx,
187+ AVCodecContext *avctx,
188+ int fallback_allowed)
189+{
190+ AVVAAPIDeviceContext *hwctx = ctx->device->hwctx;
191+ AVVAAPIHWConfig *hwconfig = NULL;
192+ AVHWFramesConstraints *constraints = NULL;
193+ VAStatus vas;
194+ int err, i, j;
195+ int loglevel = fallback_allowed ? AV_LOG_VERBOSE : AV_LOG_ERROR;
196+ const AVCodecDescriptor *codec_desc;
197+ const AVPixFmtDescriptor *pix_desc;
198+ enum AVPixelFormat pix_fmt;
199+ VAProfile profile, *profile_list = NULL;
200+ int profile_count, exact_match, alt_profile;
201+
202+ codec_desc = avcodec_descriptor_get(avctx->codec_id);
203+ if (!codec_desc) {
204+ err = AVERROR(EINVAL);
205+ goto fail;
206+ }
207+
208+ profile_count = vaMaxNumProfiles(hwctx->display);
209+ profile_list = av_malloc(profile_count * sizeof(VAProfile));
210+ if (!profile_list) {
211+ err = AVERROR(ENOMEM);
212+ goto fail;
213+ }
214+
215+ vas = vaQueryConfigProfiles(hwctx->display,
216+ profile_list, &profile_count);
217+ if (vas != VA_STATUS_SUCCESS) {
218+ av_log(ctx, loglevel, "Failed to query profiles: %d (%s).\n",
219+ vas, vaErrorStr(vas));
220+ err = AVERROR(EIO);
221+ goto fail;
222+ }
223+
224+ profile = VAProfileNone;
225+ exact_match = 0;
226+
227+ for (i = 0; i < FF_ARRAY_ELEMS(vaapi_profile_map); i++) {
228+ int profile_match = 0;
229+ if (avctx->codec_id != vaapi_profile_map[i].codec_id)
230+ continue;
231+ if (avctx->profile == vaapi_profile_map[i].codec_profile)
232+ profile_match = 1;
233+ profile = vaapi_profile_map[i].va_profile;
234+ for (j = 0; j < profile_count; j++) {
235+ if (profile == profile_list[j]) {
236+ exact_match = profile_match;
237+ break;
238+ }
239+ }
240+ if (j < profile_count) {
241+ if (exact_match)
242+ break;
243+ alt_profile = vaapi_profile_map[i].codec_profile;
244+ }
245+ }
246+ av_free(profile_list);
247+
248+ if (profile == VAProfileNone) {
249+ av_log(ctx, loglevel, "No VAAPI support for codec %s.\n",
250+ codec_desc->name);
251+ err = AVERROR(ENOSYS);
252+ goto fail;
253+ }
254+ if (!exact_match) {
255+ if (fallback_allowed || !hwaccel_lax_profile_check) {
256+ av_log(ctx, loglevel, "No VAAPI support for codec %s "
257+ "profile %d.\n", codec_desc->name, avctx->profile);
258+ if (!fallback_allowed) {
259+ av_log(ctx, AV_LOG_WARNING, "If you want attempt decoding "
260+ "anyway with a possibly-incompatible profile, add "
261+ "the option -hwaccel_lax_profile_check.\n");
262+ }
263+ err = AVERROR(EINVAL);
264+ goto fail;
265+ } else {
266+ av_log(ctx, AV_LOG_WARNING, "No VAAPI support for codec %s "
267+ "profile %d: trying instead with profile %d.\n",
268+ codec_desc->name, avctx->profile, alt_profile);
269+ av_log(ctx, AV_LOG_WARNING, "This may fail or give "
270+ "incorrect results, depending on your hardware.\n");
271+ }
272+ }
273+
274+ ctx->va_profile = profile;
275+ ctx->va_entrypoint = VAEntrypointVLD;
276+
277+ vas = vaCreateConfig(hwctx->display, ctx->va_profile,
278+ ctx->va_entrypoint, 0, 0, &ctx->va_config);
279+ if (vas != VA_STATUS_SUCCESS) {
280+ av_log(ctx, AV_LOG_ERROR, "Failed to create decode pipeline "
281+ "configuration: %d (%s).\n", vas, vaErrorStr(vas));
282+ err = AVERROR(EIO);
283+ goto fail;
284+ }
285+
286+ hwconfig = av_hwdevice_hwconfig_alloc(ctx->device_ref);
287+ if (!hwconfig) {
288+ err = AVERROR(ENOMEM);
289+ goto fail;
290+ }
291+ hwconfig->config_id = ctx->va_config;
292+
293+ constraints = av_hwdevice_get_hwframe_constraints(ctx->device_ref,
294+ hwconfig);
295+ if (!constraints)
296+ goto fail;
297+
298+ // Decide on the decoder target format.
299+ // If the user specified something with -hwaccel_output_format then
300+ // try to use that to minimise conversions later.
301+ ctx->decode_format = AV_PIX_FMT_NONE;
302+ if (ctx->output_format != AV_PIX_FMT_NONE &&
303+ ctx->output_format != AV_PIX_FMT_VAAPI) {
304+ for (i = 0; constraints->valid_sw_formats[i] != AV_PIX_FMT_NONE; i++) {
305+ if (constraints->valid_sw_formats[i] == ctx->decode_format) {
306+ ctx->decode_format = ctx->output_format;
307+ av_log(ctx, AV_LOG_DEBUG, "Using decode format %s (output "
308+ "format).\n", av_get_pix_fmt_name(ctx->decode_format));
309+ break;
310+ }
311+ }
312+ }
313+ // Otherwise, we would like to try to choose something which matches the
314+ // decoder output, but there isn't enough information available here to
315+ // do so. Assume for now that we are always dealing with YUV 4:2:0, so
316+ // pick a format which does that.
317+ if (ctx->decode_format == AV_PIX_FMT_NONE) {
318+ for (i = 0; constraints->valid_sw_formats[i] != AV_PIX_FMT_NONE; i++) {
319+ pix_fmt = constraints->valid_sw_formats[i];
320+ pix_desc = av_pix_fmt_desc_get(pix_fmt);
321+ if (pix_desc->nb_components == 3 &&
322+ pix_desc->log2_chroma_w == 1 &&
323+ pix_desc->log2_chroma_h == 1) {
324+ ctx->decode_format = pix_fmt;
325+ av_log(ctx, AV_LOG_DEBUG, "Using decode format %s (format "
326+ "matched).\n", av_get_pix_fmt_name(ctx->decode_format));
327+ break;
328+ }
329+ }
330+ }
331+ // Otherwise pick the first in the list and hope for the best.
332+ if (ctx->decode_format == AV_PIX_FMT_NONE) {
333+ ctx->decode_format = constraints->valid_sw_formats[0];
334+ av_log(ctx, AV_LOG_DEBUG, "Using decode format %s (first in list).\n",
335+ av_get_pix_fmt_name(ctx->decode_format));
336+ if (i > 1) {
337+ // There was a choice, and we picked randomly. Warn the user
338+ // that they might want to choose intelligently instead.
339+ av_log(ctx, AV_LOG_WARNING, "Using randomly chosen decode "
340+ "format %s.\n", av_get_pix_fmt_name(ctx->decode_format));
341+ }
342+ }
343+
344+ // Ensure the picture size is supported by the hardware.
345+ ctx->decode_width = avctx->coded_width;
346+ ctx->decode_height = avctx->coded_height;
347+ if (ctx->decode_width < constraints->min_width ||
348+ ctx->decode_height < constraints->min_height ||
349+ ctx->decode_width > constraints->max_width ||
350+ ctx->decode_height >constraints->max_height) {
351+ av_log(ctx, AV_LOG_ERROR, "VAAPI hardware does not support image "
352+ "size %dx%d (constraints: width %d-%d height %d-%d).\n",
353+ ctx->decode_width, ctx->decode_height,
354+ constraints->min_width, constraints->max_width,
355+ constraints->min_height, constraints->max_height);
356+ err = AVERROR(EINVAL);
357+ goto fail;
358+ }
359+
360+ av_hwframe_constraints_free(&constraints);
361+ av_freep(&hwconfig);
362+
363+ // Decide how many reference frames we need. This might be doable more
364+ // nicely based on the codec and input stream?
365+ ctx->decode_surfaces = DEFAULT_SURFACES;
366+ // For frame-threaded decoding, one additional surfaces is needed for
367+ // each thread.
368+ if (avctx->active_thread_type & FF_THREAD_FRAME)
369+ ctx->decode_surfaces += avctx->thread_count;
370+
371+ return 0;
372+
373+fail:
374+ av_hwframe_constraints_free(&constraints);
375+ av_freep(&hwconfig);
376+ vaDestroyConfig(hwctx->display, ctx->va_config);
377+ av_free(profile_list);
378+ return err;
379+}
380+
381+static void vaapi_decode_uninit(AVCodecContext *avctx)
382+{
383+ InputStream *ist = avctx->opaque;
384+ VAAPIDecoderContext *ctx = ist->hwaccel_ctx;
385+
386+ if (ctx) {
387+ AVVAAPIDeviceContext *hwctx = ctx->device->hwctx;
388+
389+ if (ctx->va_context != VA_INVALID_ID) {
390+ vaDestroyContext(hwctx->display, ctx->va_context);
391+ ctx->va_context = VA_INVALID_ID;
392+ }
393+ if (ctx->va_config != VA_INVALID_ID) {
394+ vaDestroyConfig(hwctx->display, ctx->va_config);
395+ ctx->va_config = VA_INVALID_ID;
396+ }
397+
398+ av_buffer_unref(&ctx->frames_ref);
399+ av_buffer_unref(&ctx->device_ref);
400+ av_free(ctx);
401+ }
402+
403+ av_buffer_unref(&ist->hw_frames_ctx);
404+
405+ ist->hwaccel_ctx = 0;
406+ ist->hwaccel_uninit = 0;
407+ ist->hwaccel_get_buffer = 0;
408+ ist->hwaccel_retrieve_data = 0;
409+}
410+
411+int vaapi_decode_init(AVCodecContext *avctx)
412+{
413+ InputStream *ist = avctx->opaque;
414+ AVVAAPIDeviceContext *hwctx;
415+ AVVAAPIFramesContext *avfc;
416+ VAAPIDecoderContext *ctx;
417+ VAStatus vas;
418+ int err;
419+ int loglevel = (ist->hwaccel_id != HWACCEL_VAAPI ? AV_LOG_VERBOSE
420+ : AV_LOG_ERROR);
421+
422+ if (ist->hwaccel_ctx)
423+ vaapi_decode_uninit(avctx);
424+
425+ // We have -hwaccel without -vaapi_device, so just initialise here with
426+ // the device passed as -hwaccel_device (if -vaapi_device was passed, it
427+ // will always have been called before now).
428+ if (!hw_device_ctx) {
429+ err = vaapi_device_init(ist->hwaccel_device);
430+ if (err < 0)
431+ return err;
432+ }
433+
434+ ctx = av_mallocz(sizeof(*ctx));
435+ if (!ctx)
436+ return AVERROR(ENOMEM);
437+ ctx->class = &vaapi_class;
438+
439+ ctx->device_ref = av_buffer_ref(hw_device_ctx);
440+ ctx->device = (AVHWDeviceContext*)ctx->device_ref->data;
441+
442+ ctx->va_config = VA_INVALID_ID;
443+ ctx->va_context = VA_INVALID_ID;
444+
445+ hwctx = ctx->device->hwctx;
446+
447+ ctx->output_format = ist->hwaccel_output_format;
448+
449+ err = vaapi_build_decoder_config(ctx, avctx,
450+ ist->hwaccel_id != HWACCEL_VAAPI);
451+ if (err < 0) {
452+ av_log(ctx, loglevel, "No supported configuration for this codec.");
453+ goto fail;
454+ }
455+
456+ avctx->pix_fmt = ctx->output_format;
457+
458+ ctx->frames_ref = av_hwframe_ctx_alloc(ctx->device_ref);
459+ if (!ctx->frames_ref) {
460+ av_log(ctx, loglevel, "Failed to create VAAPI frame context.\n");
461+ err = AVERROR(ENOMEM);
462+ goto fail;
463+ }
464+
465+ ctx->frames = (AVHWFramesContext*)ctx->frames_ref->data;
466+
467+ ctx->frames->format = AV_PIX_FMT_VAAPI;
468+ ctx->frames->sw_format = ctx->decode_format;
469+ ctx->frames->width = ctx->decode_width;
470+ ctx->frames->height = ctx->decode_height;
471+ ctx->frames->initial_pool_size = ctx->decode_surfaces;
472+
473+ err = av_hwframe_ctx_init(ctx->frames_ref);
474+ if (err < 0) {
475+ av_log(ctx, loglevel, "Failed to initialise VAAPI frame "
476+ "context: %d\n", err);
477+ goto fail;
478+ }
479+
480+ avfc = ctx->frames->hwctx;
481+
482+ vas = vaCreateContext(hwctx->display, ctx->va_config,
483+ ctx->decode_width, ctx->decode_height,
484+ VA_PROGRESSIVE,
485+ avfc->surface_ids, avfc->nb_surfaces,
486+ &ctx->va_context);
487+ if (vas != VA_STATUS_SUCCESS) {
488+ av_log(ctx, AV_LOG_ERROR, "Failed to create decode pipeline "
489+ "context: %d (%s).\n", vas, vaErrorStr(vas));
490+ err = AVERROR(EINVAL);
491+ goto fail;
492+ }
493+
494+ av_log(ctx, AV_LOG_DEBUG, "VAAPI decoder (re)init complete.\n");
495+
496+ // We would like to set this on the AVCodecContext for use by whoever gets
497+ // the frames from the decoder, but unfortunately the AVCodecContext we
498+ // have here need not be the "real" one (H.264 makes many copies for
499+ // threading purposes). To avoid the problem, we instead store it in the
500+ // InputStream and propagate it from there.
501+ ist->hw_frames_ctx = av_buffer_ref(ctx->frames_ref);
502+ if (!ist->hw_frames_ctx) {
503+ err = AVERROR(ENOMEM);
504+ goto fail;
505+ }
506+
507+ ist->hwaccel_ctx = ctx;
508+ ist->hwaccel_uninit = vaapi_decode_uninit;
509+ ist->hwaccel_get_buffer = vaapi_get_buffer;
510+ ist->hwaccel_retrieve_data = vaapi_retrieve_data;
511+
512+ ctx->decoder_vaapi_context.display = hwctx->display;
513+ ctx->decoder_vaapi_context.config_id = ctx->va_config;
514+ ctx->decoder_vaapi_context.context_id = ctx->va_context;
515+ avctx->hwaccel_context = &ctx->decoder_vaapi_context;
516+
517+ return 0;
518+
519+fail:
520+ vaapi_decode_uninit(avctx);
521+ return err;
522+}
523+
524+static AVClass *vaapi_log = &vaapi_class;
525+
526+static av_cold void vaapi_device_uninit(AVHWDeviceContext *hwdev)
527+{
528+ AVVAAPIDeviceContext *hwctx = hwdev->hwctx;
529+ av_log(&vaapi_log, AV_LOG_VERBOSE, "Terminating VAAPI connection.\n");
530+ vaTerminate(hwctx->display);
531+}
532+
533+av_cold int vaapi_device_init(const char *device)
534+{
535+ AVHWDeviceContext *hwdev;
536+ AVVAAPIDeviceContext *hwctx;
537+ VADisplay display;
538+ VAStatus vas;
539+ int major, minor, err;
540+
541+ display = 0;
542+
543+#if HAVE_VAAPI_X11
544+ if (!display) {
545+ Display *x11_display;
546+
547+ // Try to open the device as an X11 display.
548+ x11_display = XOpenDisplay(device);
549+ if (!x11_display) {
550+ av_log(&vaapi_log, AV_LOG_WARNING, "Cannot open X11 display "
551+ "%s.\n", XDisplayName(device));
552+ } else {
553+ display = vaGetDisplay(x11_display);
554+ if (!display) {
555+ av_log(&vaapi_log, AV_LOG_WARNING, "Cannot open a VA display "
556+ "from X11 display %s.\n", XDisplayName(device));
557+ XCloseDisplay(x11_display);
558+ } else {
559+ av_log(&vaapi_log, AV_LOG_VERBOSE, "Opened VA display via "
560+ "X11 display %s.\n", XDisplayName(device));
561+ }
562+ }
563+ }
564+#endif
565+
566+#if HAVE_VAAPI_DRM
567+ if (!display && device) {
568+ int drm_fd;
569+
570+ // Try to open the device as a DRM path.
571+ drm_fd = open(device, O_RDWR);
572+ if (drm_fd < 0) {
573+ av_log(&vaapi_log, AV_LOG_WARNING, "Cannot open DRM device %s.\n",
574+ device);
575+ } else {
576+ display = vaGetDisplayDRM(drm_fd);
577+ if (!display) {
578+ av_log(&vaapi_log, AV_LOG_WARNING, "Cannot open a VA display "
579+ "from DRM device %s.\n", device);
580+ close(drm_fd);
581+ } else {
582+ av_log(&vaapi_log, AV_LOG_VERBOSE, "Opened VA display via "
583+ "DRM device %s.\n", device);
584+ }
585+ }
586+ }
587+#endif
588+
589+ if (!display) {
590+ av_log(&vaapi_log, AV_LOG_ERROR, "No VA display found for "
591+ "device %s.\n", device);
592+ return AVERROR(EINVAL);
593+ }
594+
595+ vas = vaInitialize(display, &major, &minor);
596+ if (vas != VA_STATUS_SUCCESS) {
597+ av_log(&vaapi_log, AV_LOG_ERROR, "Failed to initialise VAAPI "
598+ "connection: %d (%s).\n", vas, vaErrorStr(vas));
599+ return AVERROR(EIO);
600+ }
601+ av_log(&vaapi_log, AV_LOG_VERBOSE, "Initialised VAAPI connection: "
602+ "version %d.%d\n", major, minor);
603+
604+ hw_device_ctx = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VAAPI);
605+ if (!hw_device_ctx) {
606+ av_log(&vaapi_log, AV_LOG_ERROR, "Failed to create VAAPI "
607+ "hardware context.\n");
608+ vaTerminate(display);
609+ return AVERROR(ENOMEM);
610+ }
611+
612+ hwdev = (AVHWDeviceContext*)hw_device_ctx->data;
613+ hwdev->free = &vaapi_device_uninit;
614+
615+ hwctx = hwdev->hwctx;
616+ hwctx->display = display;
617+
618+ err = av_hwdevice_ctx_init(hw_device_ctx);
619+ if (err < 0) {
620+ av_log(&vaapi_log, AV_LOG_ERROR, "Failed to initialise VAAPI "
621+ "hardware context: %d\n", err);
622+ return err;
623+ }
624+
625+ return 0;
626+}
configure+7−1
@@ -1636,6 +1636,7 @@ HAVE_LIST=" · 1635 unmodified lines
16361636 sdl
16371637 section_data_rel_ro
16381638 threads
1639+ vaapi_drm
16391640 vaapi_x11
16401641 vdpau_x11
16411642 xlib
@@ -4694,10 +4695,15 @@ enabled vaapi && · 3052 unmodified lines
46944695 check_code cc "va/va.h" "vaCreateSurfaces(0, 0, 0, 0, 0, 0, 0, 0)" ||
46954696 disable vaapi
46964697
4697−enabled vaapi && enabled xlib &&
4698+if enabled vaapi ; then
4699+ enabled xlib &&
46984700 check_lib2 "va/va.h va/va_x11.h" vaGetDisplay -lva -lva-x11 &&
46994701 enable vaapi_x11
47004702
4703+ check_lib2 "va/va.h va/va_drm.h" vaGetDisplayDRM -lva -lva-drm &&
4704+ enable vaapi_drm
4705+fi
4706+
47014707 enabled vdpau &&
47024708 check_cpp_condition vdpau/vdpau.h "defined VDP_DECODER_PROFILE_MPEG4_PART2_ASP" ||
47034709 disable vdpau

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t