1 /*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg 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 * FFmpeg 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 FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 /**
20 ** @file
21 ** Hardware accelerated common filters based on Intel Quick Sync Video VPP
22 **/
23
25
26 #include "config_components.h"
27
35
39
42
43 #define OFFSET(x) offsetof(VPPContext, x)
44 #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
45
46 /* number of video enhancement filters */
47 #define ENH_FILTERS_COUNT (8)
48
51
52 /* Video Enhancement Algorithms */
61 #if QSV_ONEVPL
62 /** Video signal info attached on the input frame */
63 mfxExtVideoSignalInfo invsi_conf;
64 /** Video signal info attached on the output frame */
65 mfxExtVideoSignalInfo outvsi_conf;
66 /** HDR parameters attached on the input frame */
67 mfxExtMasteringDisplayColourVolume mdcv_conf;
68 mfxExtContentLightLevelInfo clli_conf;
69 #endif
70
71 /**
72 * New dimensions. Special values are:
73 * 0 = original width/height
74 * -1 = keep original aspect
75 */
78 /**
79 * Output sw format. AV_PIX_FMT_NONE for no conversion.
80 */
82
84 int use_frc;
/* use framerate conversion */
85 int deinterlace;
/* deinterlace mode : 0=off, 1=bob, 2=advanced */
86 int denoise;
/* Enable Denoise algorithm. Value [0, 100] */
87 int detail;
/* Enable Detail Enhancement algorithm. */
88 /* Level is the optional, value [0, 100] */
94
96 int rotate;
/* rotate angle : [0, 90, 180, 270] */
97 int hflip;
/* flip mode : 0 = off, 1 = HORIZONTAL flip */
98
99 int scale_mode;
/* scale mode : 0 = auto, 1 = low power, 2 = high quality */
100
101 /* param for the procamp */
107
111
112 /** The color properties for output */
116
121
123 int field_rate;
/* Generate output at frame rate or field rate for deinterlace mode, 0: frame, 1: field */
124 int tonemap;
/* 1: perform tonemapping if the input has HDR metadata, 0: always disable tonemapping */
126
128 "iw", "in_w",
129 "ih", "in_h",
130 "ow", "out_w", "w",
131 "oh", "out_h", "h",
132 "cw",
133 "ch",
134 "cx",
135 "cy",
136 "a", "dar",
137 "sar",
139 };
140
153 };
154
156 {
157 #define PASS_EXPR(e, s) {\
158 if (s) {\
159 ret = av_expr_parse(&e, s, var_names, NULL, NULL, NULL, NULL, 0, ctx); \
160 if (ret < 0) { \
161 av_log(ctx, AV_LOG_ERROR, "Error when passing '%s'.\n", s); \
162 goto release; \
163 } \
164 }\
165 }
166 #define CALC_EXPR(e, v, i, d) {\
167 if (e)\
168 i = v = av_expr_eval(e, var_values, NULL); \
169 else\
170 i = v = d;\
171 }
178
181
184
187
190
193
195 var_values[
VAR_SAR] =
ctx->inputs[0]->sample_aspect_ratio.num ?
196 (
double)
ctx->inputs[0]->sample_aspect_ratio.num /
ctx->inputs[0]->sample_aspect_ratio.den : 1;
198
199 /* crop params */
202
203 /* calc again in case cw is relative to ch */
205
212
213 /* calc again in case ow is relative to oh */
217
220
221 /* calc again in case cx is relative to cy */
223
226
227 release:
234 #undef PASS_EXPR
235 #undef CALC_EXPR
236
238 }
239
241 {
243 /* For AV_OPT_TYPE_STRING options, NULL is handled in other way so
244 * we needn't set default value here
245 */
249
254
256
257 return 0;
258 }
259
261 {
263
266 } else {
271 }
272 }
273
274 #define STRING_OPTION(var_name, func_name, default_value) do { \
275 if (vpp->var_name ## _str) { \
276 int var = av_ ## func_name ## _from_name(vpp->var_name ## _str); \
277 if (var < 0) { \
278 av_log(ctx, AV_LOG_ERROR, "Invalid %s.\n", #var_name); \
279 return AVERROR(EINVAL); \
280 } \
281 vpp->var_name = var; \
282 } else { \
283 vpp->var_name = default_value; \
284 } \
285 } while (0)
286
290
291 #undef STRING_OPTION
292 return 0;
293 }
294
296 {
302
305
309 }
310
313
318 }
319
322
323 /* sanity check params */
324 if (ow < -1 || oh < -1) {
327 }
328
329 if (ow == -1 && oh == -1)
331
334
337
338 if (ow == -1)
340
341 if (oh == -1)
343
344 if (ow > INT_MAX || oh > INT_MAX ||
345 (oh *
inlink->w) > INT_MAX ||
346 (ow *
inlink->h) > INT_MAX)
348
351
355
360 }
361
362 return 0;
363 }
364
366 {
371
375 }
else if (
ctx->hw_device_ctx) {
376 device_ref =
ctx->hw_device_ctx;
377 } else {
378 // Unavailable hw context doesn't matter in pass-through mode,
379 // so don't error here but let runtime version checks fail by setting to 0.0
380 mfx_version->Major = 0;
381 mfx_version->Minor = 0;
382 return MFX_ERR_NONE;
383 }
384
386 device_hwctx = device_ctx->
hwctx;
387
388 return MFXQueryVersion(device_hwctx->
session, mfx_version);
389 }
390
392 {
393 #if QSV_ONEVPL
396 mfxExtVideoSignalInfo invsi_conf, outvsi_conf;
397 mfxExtMasteringDisplayColourVolume mdcv_conf;
398 mfxExtContentLightLevelInfo clli_conf;
400 int tm = 0;
401
403
406 return 0;
407
408 memset(&invsi_conf, 0, sizeof(mfxExtVideoSignalInfo));
409 invsi_conf.Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO_IN;
410 invsi_conf.Header.BufferSz = sizeof(mfxExtVideoSignalInfo);
415 invsi_conf.ColourDescriptionPresent = 1;
416
417 memset(&mdcv_conf, 0, sizeof(mfxExtMasteringDisplayColourVolume));
421
423 const int mapping[3] = {1, 2, 0};
424 const int chroma_den = 50000;
425 const int luma_den = 10000;
427
428 mdcv_conf.Header.BufferId = MFX_EXTBUFF_MASTERING_DISPLAY_COLOUR_VOLUME_IN;
429 mdcv_conf.Header.BufferSz = sizeof(mfxExtMasteringDisplayColourVolume);
430
431 for (
i = 0;
i < 3;
i++) {
432 const int j = mapping[
i];
433
434 mdcv_conf.DisplayPrimariesX[
i] =
437 chroma_den);
438 mdcv_conf.DisplayPrimariesY[
i] =
441 chroma_den);
442 }
443
444 mdcv_conf.WhitePointX =
446 chroma_den);
447 mdcv_conf.WhitePointY =
449 chroma_den);
450
451 /* MaxDisplayMasteringLuminance is in the unit of 1 nits however
452 * MinDisplayMasteringLuminance is in the unit of 0.0001 nits
453 */
454 mdcv_conf.MaxDisplayMasteringLuminance =
456 mdcv_conf.MinDisplayMasteringLuminance =
458 tm = 1;
459 }
460 }
461
462 memset(&clli_conf, 0, sizeof(mfxExtContentLightLevelInfo));
466
467 clli_conf.Header.BufferId = MFX_EXTBUFF_CONTENT_LIGHT_LEVEL_INFO;
468 clli_conf.Header.BufferSz = sizeof(mfxExtContentLightLevelInfo);
469 clli_conf.MaxContentLightLevel =
FFMIN(clm->
MaxCLL, 65535);
470 clli_conf.MaxPicAverageLightLevel =
FFMIN(clm->
MaxFALL, 65535);
471 tm = 1;
472 }
473
474 if (tm) {
477
482 }
483
492
493 memset(&outvsi_conf, 0, sizeof(mfxExtVideoSignalInfo));
494 outvsi_conf.Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO_OUT;
495 outvsi_conf.Header.BufferSz = sizeof(mfxExtVideoSignalInfo);
500 outvsi_conf.ColourDescriptionPresent = 1;
501
502 if (memcmp(&vpp->invsi_conf, &invsi_conf,
sizeof(mfxExtVideoSignalInfo)) ||
503 memcmp(&vpp->mdcv_conf, &mdcv_conf, sizeof(mfxExtMasteringDisplayColourVolume)) ||
504 memcmp(&vpp->clli_conf, &clli_conf, sizeof(mfxExtContentLightLevelInfo)) ||
505 memcmp(&vpp->outvsi_conf, &outvsi_conf, sizeof(mfxExtVideoSignalInfo))) {
506 vpp->invsi_conf = invsi_conf;
508
509 vpp->outvsi_conf = outvsi_conf;
511
512 vpp->mdcv_conf = mdcv_conf;
513 if (mdcv_conf.Header.BufferId)
515
516 vpp->clli_conf = clli_conf;
517 if (clli_conf.Header.BufferId)
519 }
520 #endif
521
522 return 0;
523 }
524
526 {
533 mfxVersion mfx_version;
538
544 else
546
551
555 }
556
560 else
562 } else
563 in_format =
inlink->format;
564
568
575
578 }
579
580 #define INIT_MFX_EXTBUF(extbuf, id) do { \
581 memset(&vpp->extbuf, 0, sizeof(vpp->extbuf)); \
582 vpp->extbuf.Header.BufferId = id; \
583 vpp->extbuf.Header.BufferSz = sizeof(vpp->extbuf); \
584 param.ext_buf[param.num_ext_buf++] = (mfxExtBuffer*)&vpp->extbuf; \
585 } while (0)
586
587 #define SET_MFX_PARAM_FIELD(extbuf, field, value) do { \
588 vpp->extbuf.field = value; \
589 } while (0)
590
594 MFX_DEINTERLACING_BOB : MFX_DEINTERLACING_ADVANCED));
595 }
596
600 }
601
605 }
606
610 }
611
618 }
619
624 vpp->
rotate = MFX_ANGLE_270;
625 vpp->
hflip = MFX_MIRRORING_HORIZONTAL;
626 break;
628 vpp->
rotate = MFX_ANGLE_90;
629 vpp->
hflip = MFX_MIRRORING_DISABLED;
630 break;
632 vpp->
rotate = MFX_ANGLE_270;
633 vpp->
hflip = MFX_MIRRORING_DISABLED;
634 break;
636 vpp->
rotate = MFX_ANGLE_90;
637 vpp->
hflip = MFX_MIRRORING_HORIZONTAL;
638 break;
640 vpp->
rotate = MFX_ANGLE_180;
641 vpp->
hflip = MFX_MIRRORING_DISABLED;
642 break;
644 vpp->
rotate = MFX_ANGLE_0;
645 vpp->
hflip = MFX_MIRRORING_HORIZONTAL;
646 break;
648 vpp->
rotate = MFX_ANGLE_180;
649 vpp->
hflip = MFX_MIRRORING_HORIZONTAL;
650 break;
651 default:
654 }
655 } else {
657 "not supported with this MSDK version.\n");
659 }
660 }
661
666
667 if (MFX_ANGLE_90 == vpp->
rotate || MFX_ANGLE_270 == vpp->
rotate) {
669 FFSWAP(
int, outlink->
w, outlink->
h);
671 }
672 } else {
674 "not supported with this MSDK version.\n");
676 }
677 }
678
683 } else {
685 "not supported with this MSDK version.\n");
687 }
688 }
689
693
694 #if QSV_ONEVPL
696 mode = MFX_SCALING_MODE_VENDOR +
mode - 2;
697 #endif
698
701 } else
703 "option is not supported with this MSDK version.\n");
704 }
705
706 #undef INIT_MFX_EXTBUF
707 #undef SET_MFX_PARAM_FIELD
708
719 else {
720 /* No MFX session is created in this case */
724 }
725
726 return 0;
727 }
728
730 {
737
739
744
748 }
749 }
750 }
751
753 if (in || qsv->
eof) {
757 goto not_ready;
760
762 goto eof;
763
766 return 0;
767 }
768 }
769 } else {
770 /* No MFX session is created in pass-through mode */
771 if (in) {
775
778 else
780
784
786 goto eof;
787
788 return 0;
789 }
790 }
791
792 not_ready:
794 goto eof;
795
797
799
800 eof:
803 return 0;
804 }
805
807 {
809 }
810
812 {
817 },
818 };
819
821 {
825 },
826 };
827
828 #define DEFINE_QSV_FILTER(x, sn, ln, fmts) \
829 static const AVClass x##_class = { \
830 .class_name = #sn "_qsv", \
831 .item_name = av_default_item_name, \
832 .option = x##_options, \
833 .version = LIBAVUTIL_VERSION_INT, \
834 }; \
835 const AVFilter ff_vf_##sn##_qsv = { \
836 .name = #sn "_qsv", \
837 .description = NULL_IF_CONFIG_SMALL("Quick Sync Video " #ln), \
838 .preinit = x##_preinit, \
839 .init = vpp_init, \
840 .uninit = vpp_uninit, \
841 .priv_size = sizeof(VPPContext), \
842 .priv_class = &x##_class, \
843 FILTER_INPUTS(vpp_inputs), \
844 FILTER_OUTPUTS(vpp_outputs), \
845 fmts, \
846 .activate = activate, \
847 .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, \
848 .flags = AVFILTER_FLAG_HWDEVICE, \
849 };
850
851 #if CONFIG_VPP_QSV_FILTER
852
853 static const AVOption vpp_options[] = {
854 {
"deinterlace",
"deinterlace mode: 0=off, 1=bob, 2=advanced",
OFFSET(deinterlace),
AV_OPT_TYPE_INT, { .i64 = 0 }, 0, MFX_DEINTERLACING_ADVANCED, .flags =
FLAGS, .unit =
"deinterlace" },
855 {
"bob",
"Bob deinterlace mode.", 0,
AV_OPT_TYPE_CONST, { .i64 = MFX_DEINTERLACING_BOB }, .flags =
FLAGS, .unit =
"deinterlace" },
856 {
"advanced",
"Advanced deinterlace mode. ", 0,
AV_OPT_TYPE_CONST, { .i64 = MFX_DEINTERLACING_ADVANCED }, .flags =
FLAGS, .unit =
"deinterlace" },
857
866
875
880
881 {
"w",
"Output video width(0=input video width, -1=keep input video aspect)",
OFFSET(ow),
AV_OPT_TYPE_STRING, { .str=
"cw" }, 0, 255, .flags =
FLAGS },
882 {
"width",
"Output video width(0=input video width, -1=keep input video aspect)",
OFFSET(ow),
AV_OPT_TYPE_STRING, { .str=
"cw" }, 0, 255, .flags =
FLAGS },
883 {
"h",
"Output video height(0=input video height, -1=keep input video aspect)",
OFFSET(oh),
AV_OPT_TYPE_STRING, { .str=
"w*ch/cw" }, 0, 255, .flags =
FLAGS },
884 {
"height",
"Output video height(0=input video height, -1=keep input video aspect)",
OFFSET(oh),
AV_OPT_TYPE_STRING, { .str=
"w*ch/cw" }, 0, 255, .flags =
FLAGS },
886 {
"async_depth",
"Internal parallelization depth, the higher the value the higher the latency.",
OFFSET(qsv.async_depth),
AV_OPT_TYPE_INT, { .i64 = 4 }, 0, INT_MAX, .flags =
FLAGS },
887 #if QSV_ONEVPL
888 {
"scale_mode",
"scaling & format conversion mode (mode compute(3), vd(4) and ve(5) are only available on some platforms)",
OFFSET(scale_mode),
AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 5, .flags =
FLAGS, .unit =
"scale mode" },
889 #else
890 {
"scale_mode",
"scaling & format conversion mode",
OFFSET(scale_mode),
AV_OPT_TYPE_INT, { .i64 = MFX_SCALING_MODE_DEFAULT }, MFX_SCALING_MODE_DEFAULT, MFX_SCALING_MODE_QUALITY, .flags =
FLAGS, .unit =
"scale mode" },
891 #endif
892 {
"auto",
"auto mode", 0,
AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_DEFAULT}, INT_MIN, INT_MAX,
FLAGS, .unit =
"scale mode"},
893 {
"low_power",
"low power mode", 0,
AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_LOWPOWER}, INT_MIN, INT_MAX,
FLAGS, .unit =
"scale mode"},
894 {
"hq",
"high quality mode", 0,
AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_QUALITY}, INT_MIN, INT_MAX,
FLAGS, .unit =
"scale mode"},
895 #if QSV_ONEVPL
896 {
"compute",
"compute", 0,
AV_OPT_TYPE_CONST, { .i64 = 3}, INT_MIN, INT_MAX,
FLAGS, .unit =
"scale mode"},
899 #endif
900
901 { "rate", "Generate output at frame rate or field rate, available only for deinterlace mode",
903 { "frame", "Output at frame rate (one frame of output for each field-pair)",
905 { "field", "Output at field rate (one frame of output for each field)",
907
908 { "out_range", "Output color range",
911 { "full", "Full range",
913 { "limited", "Limited range",
915 { "jpeg", "Full range",
917 { "mpeg", "Limited range",
919 { "tv", "Limited range",
921 { "pc", "Full range",
923 { "out_color_matrix", "Output color matrix coefficient set",
925 { "out_color_primaries", "Output color primaries",
927 { "out_color_transfer", "Output color transfer characteristics",
929
930 {
"tonemap",
"Perform tonemapping (0=disable tonemapping, 1=perform tonemapping if the input has HDR metadata)",
OFFSET(
tonemap),
AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, .flags =
FLAGS},
931
933 };
934
936 {
945 #if CONFIG_VAAPI
947 #endif
950 };
952
954 &
ctx->inputs[0]->outcfg.formats);
957
958 /* User specifies the output format */
962 else {
965 }
966
969
971 &
ctx->outputs[0]->incfg.formats);
972 }
973
975
976 #endif
977
978 #if CONFIG_SCALE_QSV_FILTER
979
980 static const AVOption qsvscale_options[] = {
981 {
"w",
"Output video width(0=input video width, -1=keep input video aspect)",
OFFSET(ow),
AV_OPT_TYPE_STRING, { .str =
"iw" }, .flags =
FLAGS },
982 {
"h",
"Output video height(0=input video height, -1=keep input video aspect)",
OFFSET(oh),
AV_OPT_TYPE_STRING, { .str =
"ih" }, .flags =
FLAGS },
984
985 #if QSV_ONEVPL
986 {
"mode",
"scaling & format conversion mode (mode compute(3), vd(4) and ve(5) are only available on some platforms)",
OFFSET(scale_mode),
AV_OPT_TYPE_INT, { .i64 = 0}, 0, 5,
FLAGS, .unit =
"mode"},
987 #else
988 {
"mode",
"scaling & format conversion mode",
OFFSET(scale_mode),
AV_OPT_TYPE_INT, { .i64 = MFX_SCALING_MODE_DEFAULT}, MFX_SCALING_MODE_DEFAULT, MFX_SCALING_MODE_QUALITY,
FLAGS, .unit =
"mode"},
989 #endif
990 {
"low_power",
"low power mode", 0,
AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_LOWPOWER}, INT_MIN, INT_MAX,
FLAGS, .unit =
"mode"},
991 {
"hq",
"high quality mode", 0,
AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_QUALITY}, INT_MIN, INT_MAX,
FLAGS, .unit =
"mode"},
992 #if QSV_ONEVPL
996 #endif
997
999 };
1000
1002 {
1004
1007
1008 return 0;
1009 }
1010
1012
1013 #endif
1014
1015 #if CONFIG_DEINTERLACE_QSV_FILTER
1016
1017 static const AVOption qsvdeint_options[] = {
1018 {
"mode",
"set deinterlace mode",
OFFSET(deinterlace),
AV_OPT_TYPE_INT, {.i64 = MFX_DEINTERLACING_ADVANCED}, MFX_DEINTERLACING_BOB, MFX_DEINTERLACING_ADVANCED,
FLAGS, .unit =
"mode"},
1019 {
"bob",
"bob algorithm", 0,
AV_OPT_TYPE_CONST, {.i64 = MFX_DEINTERLACING_BOB}, MFX_DEINTERLACING_BOB, MFX_DEINTERLACING_ADVANCED,
FLAGS, .unit =
"mode"},
1020 {
"advanced",
"Motion adaptive algorithm", 0,
AV_OPT_TYPE_CONST, {.i64 = MFX_DEINTERLACING_ADVANCED}, MFX_DEINTERLACING_BOB, MFX_DEINTERLACING_ADVANCED,
FLAGS, .unit =
"mode"},
1021
1023 };
1024
1026 {
1028
1032
1033 return 0;
1034 }
1035
1037
1038 #endif