+
+#define OFFSET(x) offsetof(QSVDecContext, x)
+#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
+
+#define DEFINE_QSV_DECODER_WITH_OPTION(x, X, bsf_name, opt) \
+static const AVClass x##_qsv_class = { \
+ .class_name = #x "_qsv", \
+ .item_name = av_default_item_name, \
+ .option = opt, \
+ .version = LIBAVUTIL_VERSION_INT, \
+}; \
+const AVCodec ff_##x##_qsv_decoder = { \
+ .name = #x "_qsv", \
+ .long_name = NULL_IF_CONFIG_SMALL(#X " video (Intel Quick Sync Video acceleration)"), \
+ .priv_data_size = sizeof(QSVDecContext), \
+ .type = AVMEDIA_TYPE_VIDEO, \
+ .id = AV_CODEC_ID_##X, \
+ .init = qsv_decode_init, \
+ .decode = qsv_decode_frame, \
+ .flush = qsv_decode_flush, \
+ .close = qsv_decode_close, \
+ .bsfs = bsf_name, \
+ .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HYBRID, \
+ .priv_class = &x##_qsv_class, \
+ .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_NV12, \
+ AV_PIX_FMT_P010, \
+ AV_PIX_FMT_QSV, \
+ AV_PIX_FMT_NONE }, \
+ .hw_configs = qsv_hw_configs, \
+ .wrapper_name = "qsv", \
+}; \
+
+#define DEFINE_QSV_DECODER(x, X, bsf_name) DEFINE_QSV_DECODER_WITH_OPTION(x, X, bsf_name, options)
+
+#if CONFIG_HEVC_QSV_DECODER
+static const AVOption hevc_options[] = {
+ { "async_depth", "Internal parallelization depth, the higher the value the higher the latency.", OFFSET(qsv.async_depth), AV_OPT_TYPE_INT, { .i64 = ASYNC_DEPTH_DEFAULT }, 1, INT_MAX, VD },
+
+ { "load_plugin", "A user plugin to load in an internal session", OFFSET(load_plugin), AV_OPT_TYPE_INT, { .i64 = LOAD_PLUGIN_HEVC_HW }, LOAD_PLUGIN_NONE, LOAD_PLUGIN_HEVC_HW, VD, "load_plugin" },
+ { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_NONE }, 0, 0, VD, "load_plugin" },
+ { "hevc_sw", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_HEVC_SW }, 0, 0, VD, "load_plugin" },
+ { "hevc_hw", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_HEVC_HW }, 0, 0, VD, "load_plugin" },
+
+ { "load_plugins", "A :-separate list of hexadecimal plugin UIDs to load in an internal session",
+ OFFSET(qsv.load_plugins), AV_OPT_TYPE_STRING, { .str = "" }, 0, 0, VD },
+
+ { "gpu_copy", "A GPU-accelerated copy between video and system memory", OFFSET(qsv.gpu_copy), AV_OPT_TYPE_INT, { .i64 = MFX_GPUCOPY_DEFAULT }, MFX_GPUCOPY_DEFAULT, MFX_GPUCOPY_OFF, VD, "gpu_copy"},
+ { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_DEFAULT }, 0, 0, VD, "gpu_copy"},
+ { "on", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_ON }, 0, 0, VD, "gpu_copy"},
+ { "off", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_OFF }, 0, 0, VD, "gpu_copy"},
+ { NULL },
+};
+DEFINE_QSV_DECODER_WITH_OPTION(hevc, HEVC, "hevc_mp4toannexb", hevc_options)
+#endif
+
+static const AVOption options[] = {
+ { "async_depth", "Internal parallelization depth, the higher the value the higher the latency.", OFFSET(qsv.async_depth), AV_OPT_TYPE_INT, { .i64 = ASYNC_DEPTH_DEFAULT }, 1, INT_MAX, VD },
+
+ { "gpu_copy", "A GPU-accelerated copy between video and system memory", OFFSET(qsv.gpu_copy), AV_OPT_TYPE_INT, { .i64 = MFX_GPUCOPY_DEFAULT }, MFX_GPUCOPY_DEFAULT, MFX_GPUCOPY_OFF, VD, "gpu_copy"},
+ { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_DEFAULT }, 0, 0, VD, "gpu_copy"},
+ { "on", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_ON }, 0, 0, VD, "gpu_copy"},
+ { "off", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_OFF }, 0, 0, VD, "gpu_copy"},
+ { NULL },
+};
+
+#if CONFIG_H264_QSV_DECODER
+DEFINE_QSV_DECODER(h264, H264, "h264_mp4toannexb")
+#endif
+
+#if CONFIG_MPEG2_QSV_DECODER
+DEFINE_QSV_DECODER(mpeg2, MPEG2VIDEO, NULL)
+#endif
+
+#if CONFIG_VC1_QSV_DECODER
+DEFINE_QSV_DECODER(vc1, VC1, NULL)
+#endif
+
+#if CONFIG_MJPEG_QSV_DECODER
+DEFINE_QSV_DECODER(mjpeg, MJPEG, NULL)
+#endif
+
+#if CONFIG_VP8_QSV_DECODER
+DEFINE_QSV_DECODER(vp8, VP8, NULL)
+#endif
+
+#if CONFIG_VP9_QSV_DECODER
+DEFINE_QSV_DECODER(vp9, VP9, NULL)
+#endif
+
+#if CONFIG_AV1_QSV_DECODER
+DEFINE_QSV_DECODER(av1, AV1, NULL)
+#endif