From: alingherghescu Date: Sat, 7 Jan 2023 02:09:28 +0000 (-0800) Subject: update vaapi X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6a6c9b7240ae4d19a8d57dd7e4a9428c326a68de;p=thirdparty%2Ftvheadend.git update vaapi - added new settings: platform, bitrate scale factor, low power, loop filter level, loop filter sharpness, async depth - implemented new (dynamic) settings adjustment (in js) - added new parameters: b, low_power, loop_filter_level, loop_filter_sharpness, async_depth - tvhva_context_check_profile() will change TVHVAContext->entrypoint from VAEntrypointEncSlice into VAEntrypointEncSliceLP if VAEntrypointEncSlice is not available for that CODEC (according to VAAPI info) - moved low_power to tvh_codeo_profile in order to initialize properly the entrypoint - many error reporting improvements - separated some bundled conditions --- diff --git a/src/transcoding/codec.h b/src/transcoding/codec.h index 88e85b53b..3938e8b0e 100644 --- a/src/transcoding/codec.h +++ b/src/transcoding/codec.h @@ -92,6 +92,7 @@ struct tvh_codec_profile { double bit_rate; double qscale; int profile; + int low_power; char *device; // for hardware acceleration LIST_ENTRY(tvh_codec_profile) link; }; diff --git a/src/transcoding/codec/codecs/libs/vaapi.c b/src/transcoding/codec/codecs/libs/vaapi.c index cbcfe9b47..38e573398 100644 --- a/src/transcoding/codec/codecs/libs/vaapi.c +++ b/src/transcoding/codec/codecs/libs/vaapi.c @@ -22,10 +22,37 @@ #include #include +#include "idnode.h" +#include "htsmsg.h" -#define AV_DICT_SET_QP(d, v, a) \ - AV_DICT_SET_INT((d), "qp", (v) ? (v) : (a), AV_DICT_DONT_OVERWRITE) +/* hts ==================================================================== */ +static htsmsg_t * +platform_get_list( void *o, const char *lang ) +{ + static const struct strtab tab[] = { + { N_("Unconstrained"), 0 }, + { N_("Intel"), 1 }, + { N_("AMD"), 2 }, + }; + return strtab2htsmsg(tab, 1, lang); +} + +static htsmsg_t * +rc_mode_get_list( void *o, const char *lang ) +{ + static const struct strtab tab[] = { + { N_("skip"), -1 }, + { N_("auto"), 0 }, + { N_("CQP"), 1 }, + { N_("CBR"), 2 }, + { N_("VBR"), 3 }, + { N_("ICQ"), 4 }, + { N_("QVBR"), 5 }, + { N_("AVBR"), 6 }, + }; + return strtab2htsmsg(tab, 1, lang); +} /* vaapi ==================================================================== */ @@ -33,10 +60,15 @@ typedef struct { TVHVideoCodecProfile; int qp; int quality; + int async_depth; + double max_bit_rate; + double bit_rate_scale_factor; + int platform; + int loop_filter_level; + int loop_filter_sharpness; double buff_factor; int rc_mode; int tier; - int ignore_bframe; } tvh_codec_profile_vaapi_t; #if defined(__linux__) @@ -121,73 +153,117 @@ tvh_codec_profile_vaapi_open(tvh_codec_profile_vaapi_t *self, return 0; } +// NOTE: +// the names below are used in codec.js (/src/webui/static/app/codec.js) static const codec_profile_class_t codec_profile_vaapi_class = { { .ic_super = (idclass_t *)&codec_profile_video_class, .ic_class = "codec_profile_vaapi", .ic_caption = N_("vaapi"), .ic_properties = (const property_t[]){ + { + .type = PT_INT, + .id = "platform", // Don't change + .name = N_("Platform"), + .desc = N_("Select platform"), + .group = 3, + .get_opts = codec_profile_class_get_opts, + .off = offsetof(tvh_codec_profile_vaapi_t, platform), + .list = platform_get_list, + .def.i = 0, + }, { .type = PT_STR, .id = "device", .name = N_("Device name"), - .desc = N_("Device name (e.g. /dev/dri/renderD129)."), + .desc = N_("Device name (e.g. /dev/dri/renderD128)."), .group = 3, .off = offsetof(tvh_codec_profile_vaapi_t, device), .list = tvh_codec_profile_vaapi_device_list, }, { - .type = PT_DBL, - .id = "bit_rate", - .name = N_("Bitrate (kb/s) (0=auto)"), - .desc = N_("Target bitrate."), + .type = PT_BOOL, + .id = "low_power", // Don't change + .name = N_("Low Power"), + .desc = N_("Set low power mode.[if disabled will not send paramter to libav]"), .group = 3, + .opts = PO_EXPERT, .get_opts = codec_profile_class_get_opts, - .off = offsetof(TVHCodecProfile, bit_rate), - .def.d = 0, + .off = offsetof(tvh_codec_profile_vaapi_t, low_power), + .def.i = 0, }, { - .type = PT_DBL, - .id = "buff_factor", - .name = N_("Buffer factor"), - .desc = N_("Size of transcoding buffer (buffer=bitrate*1000*factor). Good factor is 3."), + .type = PT_INT, + .id = "async_depth", + .name = N_("Maximum processing parallelism"), + .desc = N_("Set maximum process in parallel (0=skip, 2=default).[driver must implement vaSyncBuffer function]"), .group = 3, .get_opts = codec_profile_class_get_opts, - .off = offsetof(tvh_codec_profile_vaapi_t, buff_factor), - .def.d = 3, + .off = offsetof(tvh_codec_profile_vaapi_t, async_depth), + .intextra = INTEXTRA_RANGE(0, 64, 1), + .def.i = 2, }, { .type = PT_INT, - .id = "rc_mode", + .id = "rc_mode", // Don't change .name = N_("Rate control mode"), - .desc = N_("Set rate control mode (from 0 to 6).[0=auto 1=CQP 2=CBR 3=VBR 4=ICQ 5=QVBR 6=AVBR]"), + .desc = N_("Set rate control mode"), .group = 3, .get_opts = codec_profile_class_get_opts, .off = offsetof(tvh_codec_profile_vaapi_t, rc_mode), - .intextra = INTEXTRA_RANGE(0, 6, 0), - .def.d = 0, + .list = rc_mode_get_list, + .def.i = 0, }, { .type = PT_INT, - .id = "qp", - .name = N_("Constant QP (0=auto)"), + .id = "qp", // Don't change + .name = N_("Constant QP"), .group = 3, - .desc = N_("Fixed QP of P frames [0-52]."), + .desc = N_("Fixed QP of P frames (from 0 to 52, 0=skip).[if disabled will not send paramter to libav]"), .get_opts = codec_profile_class_get_opts, .off = offsetof(tvh_codec_profile_vaapi_t, qp), .intextra = INTEXTRA_RANGE(0, 52, 1), .def.i = 0, }, { - .type = PT_INT, - .id = "ignore_bframe", - .name = N_("Ignore B-Frames"), + .type = PT_DBL, + .id = "bit_rate", // Don't change + .name = N_("Bitrate (kb/s)"), + .desc = N_("Target bitrate (0=skip).[if disabled will not send paramter to libav]"), .group = 3, - .desc = N_("Some VAAPI drivers cannot handle b-frames (like AMD). [0=use b-frames (default) 1=ignore b-frames]"), .get_opts = codec_profile_class_get_opts, - .off = offsetof(tvh_codec_profile_vaapi_t, ignore_bframe), - .intextra = INTEXTRA_RANGE(0, 1, 0), - .def.i = 0, + .off = offsetof(tvh_codec_profile_vaapi_t, bit_rate), + .def.d = 0, + }, + { + .type = PT_DBL, + .id = "max_bit_rate", // Don't change + .name = N_("Max bitrate (kb/s)"), + .desc = N_("Maximum bitrate (0=skip).[if disabled will not send paramter to libav]"), + .group = 3, + .get_opts = codec_profile_class_get_opts, + .off = offsetof(tvh_codec_profile_vaapi_t, max_bit_rate), + .def.d = 0, + }, + { + .type = PT_DBL, + .id = "buff_factor", // Don't change + .name = N_("Buffer size factor"), + .desc = N_("Size of transcoding buffer (buffer=bitrate*2048*factor). Good factor is 3."), + .group = 3, + .get_opts = codec_profile_class_get_opts, + .off = offsetof(tvh_codec_profile_vaapi_t, buff_factor), + .def.d = 3, + }, + { + .type = PT_DBL, + .id = "bit_rate_scale_factor", // Don't change + .name = N_("Bitrate scale factor"), + .desc = N_("Bitrate & Max bitrate scaler with resolution (0=no scale; 1=proportional_change). Relative to 480."), + .group = 3, + .get_opts = codec_profile_class_get_opts, + .off = offsetof(tvh_codec_profile_vaapi_t, bit_rate_scale_factor), + .def.d = 0, }, {} } @@ -209,28 +285,154 @@ static int tvh_codec_profile_vaapi_h264_open(tvh_codec_profile_vaapi_t *self, AVDictionary **opts) { - // bit_rate or qp - if (self->bit_rate) { - if (self->buff_factor <= 0) { - self->buff_factor = 3; - } - AV_DICT_SET_BIT_RATE(opts, self->bit_rate); - AV_DICT_SET_INT(opts, "maxrate", (self->bit_rate) * 1000, AV_DICT_DONT_OVERWRITE); - AV_DICT_SET_INT(opts, "bufsize", ((self->bit_rate) * 1000) * self->buff_factor, AV_DICT_DONT_OVERWRITE); - AV_DICT_SET(opts, "force_key_frames", "expr:gte(t,n_forced*3)", AV_DICT_DONT_OVERWRITE); + // to avoid issues we have this check: + if (self->buff_factor <= 0) { + self->buff_factor = 3; + } + int int_bitrate = (int)((double)(self->bit_rate) * 1024.0 * (1.0 + (self->bit_rate_scale_factor * ((double)(self->size.den) - 480.0) / 480.0))); + int int_buffer_size = (int)((double)(self->bit_rate) * 2048.0 * self->buff_factor * (1.0 + self->bit_rate_scale_factor * ((double)(self->size.den) - 480.0) / 480)); + int int_max_bitrate = (int)((double)(self->max_bit_rate) * 1024.0 * (1.0 + (self->bit_rate_scale_factor * ((double)(self->size.den) - 480.0) / 480.0))); + // https://wiki.libav.org/Hardware/vaapi + // https://blog.wmspanel.com/2017/03/vaapi-libva-support-nimble-streamer.html + // to find available parameters use: + // ffmpeg -hide_banner -h encoder=h264_vaapi + //-rc_mode E..V....... Set rate control mode (from 0 to 6) (default auto) + // auto 0 E..V....... Choose mode automatically based on other parameters + // CQP 1 E..V....... Constant-quality + // CBR 2 E..V....... Constant-bitrate + // VBR 3 E..V....... Variable-bitrate + // ICQ 4 E..V....... Intelligent constant-quality + // QVBR 5 E..V....... Quality-defined variable-bitrate + // AVBR 6 E..V....... Average variable-bitrate + if (self->rc_mode >= 0) { AV_DICT_SET_INT(opts, "rc_mode", self->rc_mode, AV_DICT_DONT_OVERWRITE); - if (self->ignore_bframe) { - AV_DICT_SET_INT(opts, "bf", 0, 0); - } } - else { - AV_DICT_SET_QP(opts, self->qp, 20); + switch (self->platform) { + case 0: + // Uncontrained --> will allow any combination of parameters (valid or invalid) + // this mode is usefull fur future platform and for debugging. + if (self->bit_rate) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + if (self->max_bit_rate) { + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + } + if (self->qp) { + AV_DICT_SET_INT(opts, "qp", self->qp, AV_DICT_DONT_OVERWRITE); + } + if (self->quality >= 0) { + AV_DICT_SET_INT(opts, "quality", self->quality, AV_DICT_DONT_OVERWRITE); + } + if (self->low_power) { + AV_DICT_SET_INT(opts, "low_power", self->low_power, AV_DICT_DONT_OVERWRITE); + } + if (self->async_depth) { + AV_DICT_SET_INT(opts, "async_depth", self->async_depth, AV_DICT_DONT_OVERWRITE); + } + break; + case 1: + // Intel + switch (self->rc_mode) { + case -1: + // same like 0 but is not sending 'rc_mode' + case 0: + // for auto --> let the driver decide as requested by documentation + if (self->bit_rate) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + if (self->max_bit_rate) { + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + } + if (self->qp) { + AV_DICT_SET_INT(opts, "qp", self->qp, AV_DICT_DONT_OVERWRITE); + } + break; + case 1: + case 4: + // for constant quality: CQP and ICQ we use qp + if (self->qp) { + AV_DICT_SET_INT(opts, "qp", self->qp, AV_DICT_DONT_OVERWRITE); + } + break; + case 2: + // for constant bitrate: CBR we use bitrate + if (self->bit_rate && self->buff_factor) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + break; + case 3: + // for variable bitrate: VBR we use bitrate + if (self->bit_rate && self->buff_factor) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + break; + case 5: + // for variable bitrate: QVBR we use bitrate + qp + if (self->bit_rate && self->buff_factor) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + if (self->qp) { + AV_DICT_SET_INT(opts, "qp", self->qp, AV_DICT_DONT_OVERWRITE); + } + break; + case 6: + // for variable bitrate: AVBR we use bitrate + if (self->bit_rate && self->buff_factor) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + } + break; + } + if (self->quality >= 0) { + AV_DICT_SET_INT(opts, "quality", self->quality, AV_DICT_DONT_OVERWRITE); + } + if (self->low_power) { + AV_DICT_SET_INT(opts, "low_power", self->low_power, AV_DICT_DONT_OVERWRITE); + } + if (self->async_depth) { + AV_DICT_SET_INT(opts, "async_depth", self->async_depth, AV_DICT_DONT_OVERWRITE); + } + break; + case 2: + // AMD --> will allow any combination of parameters + // I am unable to confirm this platform because I don't have the HW + // Is only going to override bf to 0 (as highlited by the previous implementation) + if (self->bit_rate) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + if (self->max_bit_rate) { + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + } + if (self->qp) { + AV_DICT_SET_INT(opts, "qp", self->qp, AV_DICT_DONT_OVERWRITE); + } + AV_DICT_SET_INT(opts, "bf", 0, 0); + if (self->quality >= 0) { + AV_DICT_SET_INT(opts, "quality", self->quality, AV_DICT_DONT_OVERWRITE); + } + if (self->low_power) { + AV_DICT_SET_INT(opts, "low_power", self->low_power, AV_DICT_DONT_OVERWRITE); + } + if (self->async_depth) { + AV_DICT_SET_INT(opts, "async_depth", self->async_depth, AV_DICT_DONT_OVERWRITE); + } + break; } - AV_DICT_SET_INT(opts, "quality", self->quality, 0); + // force keyframe every 3 sec. + AV_DICT_SET(opts, "force_key_frames", "expr:gte(t,n_forced*3)", AV_DICT_DONT_OVERWRITE); return 0; } - +// NOTE: +// the names below are used in codec.js (/src/webui/static/app/codec.js) static const codec_profile_class_t codec_profile_vaapi_h264_class = { { .ic_super = (idclass_t *)&codec_profile_vaapi_class, @@ -239,15 +441,15 @@ static const codec_profile_class_t codec_profile_vaapi_h264_class = { .ic_properties = (const property_t[]){ { .type = PT_INT, - .id = "quality", - .name = N_("Quality (0=auto)"), + .id = "quality", // Don't change + .name = N_("Quality"), .desc = N_("Set encode quality (trades off against speed, " - "higher is faster) [0-8]."), + "higher is faster) [-1=skip 0-7]."), .group = 5, .opts = PO_EXPERT, .get_opts = codec_profile_class_get_opts, .off = offsetof(tvh_codec_profile_vaapi_t, quality), - .intextra = INTEXTRA_RANGE(0, 8, 1), + .intextra = INTEXTRA_RANGE(-1, 7, 1), .def.i = 0, }, {} @@ -280,24 +482,148 @@ static int tvh_codec_profile_vaapi_hevc_open(tvh_codec_profile_vaapi_t *self, AVDictionary **opts) { - // bit_rate or qp - if (self->bit_rate) { - AV_DICT_SET_BIT_RATE(opts, self->bit_rate); - if (self->buff_factor <= 0) { - self->buff_factor = 3; - } - AV_DICT_SET_INT(opts, "maxrate", (self->bit_rate) * 1000, AV_DICT_DONT_OVERWRITE); - AV_DICT_SET_INT(opts, "bufsize", ((self->bit_rate) * 1000) * self->buff_factor, AV_DICT_DONT_OVERWRITE); - AV_DICT_SET(opts, "force_key_frames", "expr:gte(t,n_forced*3)", AV_DICT_DONT_OVERWRITE); + // to avoid issues we have this check: + if (self->buff_factor <= 0) { + self->buff_factor = 3; + } + int int_bitrate = (int)((double)(self->bit_rate) * 1024.0 * (1.0 + (self->bit_rate_scale_factor * ((double)(self->size.den) - 480.0) / 480.0))); + int int_buffer_size = (int)((double)(self->bit_rate) * 2048.0 * self->buff_factor * (1.0 + self->bit_rate_scale_factor * ((double)(self->size.den) - 480.0) / 480)); + int int_max_bitrate = (int)((double)(self->max_bit_rate) * 1024.0 * (1.0 + (self->bit_rate_scale_factor * ((double)(self->size.den) - 480.0) / 480.0))); + // https://wiki.libav.org/Hardware/vaapi + // to find available parameters use: + // ffmpeg -hide_banner -h encoder=hevc_vaapi + //-rc_mode E..V....... Set rate control mode (from 0 to 6) (default auto) + // auto 0 E..V....... Choose mode automatically based on other parameters + // CQP 1 E..V....... Constant-quality + // CBR 2 E..V....... Constant-bitrate + // VBR 3 E..V....... Variable-bitrate + // ICQ 4 E..V....... Intelligent constant-quality + // QVBR 5 E..V....... Quality-defined variable-bitrate + // AVBR 6 E..V....... Average variable-bitrate + if (self->rc_mode >= 0) { AV_DICT_SET_INT(opts, "rc_mode", self->rc_mode, AV_DICT_DONT_OVERWRITE); - AV_DICT_SET_INT(opts, "tier", self->tier, AV_DICT_DONT_OVERWRITE); - if (self->ignore_bframe) { - AV_DICT_SET_INT(opts, "bf", 0, 0); - } } - else { - AV_DICT_SET_QP(opts, self->qp, 25); + switch (self->platform) { + case 0: + // Unconstrained --> will allow any combination of parameters (valid or invalid) + // this mode is usefull fur future platform and for debugging. + if (self->bit_rate) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + if (self->max_bit_rate) { + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + } + if (self->qp) { + AV_DICT_SET_INT(opts, "qp", self->qp, AV_DICT_DONT_OVERWRITE); + } + if (self->tier >= 0) { + AV_DICT_SET_INT(opts, "tier", self->tier, AV_DICT_DONT_OVERWRITE); + } + if (self->low_power) { + AV_DICT_SET_INT(opts, "low_power", self->low_power, AV_DICT_DONT_OVERWRITE); + } + if (self->async_depth) { + AV_DICT_SET_INT(opts, "async_depth", self->async_depth, AV_DICT_DONT_OVERWRITE); + } + break; + case 1: + // Intel + switch (self->rc_mode) { + case -1: + // same like 0 but is not sending 'rc_mode' + case 0: + // for auto --> let the driver decide as requested by documentation + if (self->bit_rate) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + if (self->max_bit_rate) { + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + } + if (self->qp) { + AV_DICT_SET_INT(opts, "qp", self->qp, AV_DICT_DONT_OVERWRITE); + } + break; + case 1: + case 4: + // for constant quality: CQP and ICQ we use qp + if (self->qp) { + AV_DICT_SET_INT(opts, "qp", self->qp, AV_DICT_DONT_OVERWRITE); + } + break; + case 2: + // for constant bitrate: CBR we use bitrate + if (self->bit_rate && self->buff_factor) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + break; + case 3: + // for variable bitrate: VBR we use bitrate + if (self->bit_rate && self->buff_factor) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + break; + case 5: + // for variable bitrate: QVBR we use bitrate + qp + if (self->bit_rate && self->buff_factor) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + if (self->qp) { + AV_DICT_SET_INT(opts, "qp", self->qp, AV_DICT_DONT_OVERWRITE); + } + break; + case 6: + // for variable bitrate: AVBR we use bitrate + if (self->bit_rate && self->buff_factor) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + } + break; + } + if (self->tier >= 0) { + AV_DICT_SET_INT(opts, "tier", self->tier, AV_DICT_DONT_OVERWRITE); + } + if (self->low_power) { + AV_DICT_SET_INT(opts, "low_power", self->low_power, AV_DICT_DONT_OVERWRITE); + } + if (self->async_depth) { + AV_DICT_SET_INT(opts, "async_depth", self->async_depth, AV_DICT_DONT_OVERWRITE); + } + break; + case 2: + // AMD --> will allow any combination of parameters + // I am unable to confirm this platform because I don't have the HW + // Is only going to override bf to 0 (as highlited by the previous implementation) + if (self->bit_rate) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + if (self->max_bit_rate) { + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + } + if (self->qp) { + AV_DICT_SET_INT(opts, "qp", self->qp, AV_DICT_DONT_OVERWRITE); + } + AV_DICT_SET_INT(opts, "bf", 0, 0); + if (self->tier >= 0) { + AV_DICT_SET_INT(opts, "tier", self->tier, AV_DICT_DONT_OVERWRITE); + } + if (self->low_power) { + AV_DICT_SET_INT(opts, "low_power", self->low_power, AV_DICT_DONT_OVERWRITE); + } + if (self->async_depth) { + AV_DICT_SET_INT(opts, "async_depth", self->async_depth, AV_DICT_DONT_OVERWRITE); + } + break; } + // force keyframe every 3 sec. + AV_DICT_SET(opts, "force_key_frames", "expr:gte(t,n_forced*3)", AV_DICT_DONT_OVERWRITE); return 0; } @@ -310,14 +636,14 @@ static const codec_profile_class_t codec_profile_vaapi_hevc_class = { .ic_properties = (const property_t[]){ { .type = PT_INT, - .id = "tier", + .id = "tier", // Don't change .name = N_("Tier"), - .desc = N_("Set tier (general_tier_flag) [0=main 1=high]"), + .desc = N_("Set tier (-1, 0 or 1) [-1=skip 0=main 1=high]"), .group = 5, .opts = PO_EXPERT, .get_opts = codec_profile_class_get_opts, .off = offsetof(tvh_codec_profile_vaapi_t, tier), - .intextra = INTEXTRA_RANGE(0, 1, 0), + .intextra = INTEXTRA_RANGE(-1, 1, 1), .def.i = 0, }, {} @@ -347,22 +673,166 @@ static int tvh_codec_profile_vaapi_vp8_open(tvh_codec_profile_vaapi_t *self, AVDictionary **opts) { - // bit_rate or qp - if (self->bit_rate) { - AV_DICT_SET_BIT_RATE(opts, self->bit_rate); - if (self->buff_factor <= 0) { - self->buff_factor = 3; - } - AV_DICT_SET_INT(opts, "maxrate", (self->bit_rate) * 1000, AV_DICT_DONT_OVERWRITE); - AV_DICT_SET_INT(opts, "bufsize", ((self->bit_rate) * 1000) * self->buff_factor, AV_DICT_DONT_OVERWRITE); - AV_DICT_SET(opts, "force_key_frames", "expr:gte(t,n_forced*3)", AV_DICT_DONT_OVERWRITE); + // to avoid issues we have this check: + if (self->buff_factor <= 0) { + self->buff_factor = 3; } - else { - AV_DICT_SET_QP(opts, self->qp, 25); + int int_bitrate = (int)((double)(self->bit_rate) * 1024.0 * (1.0 + (self->bit_rate_scale_factor * ((double)(self->size.den) - 480.0) / 480.0))); + int int_buffer_size = (int)((double)(self->bit_rate) * 2048.0 * self->buff_factor * (1.0 + self->bit_rate_scale_factor * ((double)(self->size.den) - 480.0) / 480)); + int int_max_bitrate = (int)((double)(self->max_bit_rate) * 1024.0 * (1.0 + (self->bit_rate_scale_factor * ((double)(self->size.den) - 480.0) / 480.0))); + // https://wiki.libav.org/Hardware/vaapi + // to find available parameters use: + // ffmpeg -hide_banner -h encoder=vp8_vaapi + //-rc_mode E..V....... Set rate control mode (from 0 to 6) (default auto) + // auto 0 E..V....... Choose mode automatically based on other parameters + // CQP 1 E..V....... Constant-quality + // CBR 2 E..V....... Constant-bitrate + // VBR 3 E..V....... Variable-bitrate + // ICQ 4 E..V....... Intelligent constant-quality + // QVBR 5 E..V....... Quality-defined variable-bitrate + // AVBR 6 E..V....... Average variable-bitrate + if (self->rc_mode >= 0) { + AV_DICT_SET_INT(opts, "rc_mode", self->rc_mode, AV_DICT_DONT_OVERWRITE); } - if (self->ignore_bframe) { - AV_DICT_SET_INT(opts, "bf", 0, 0); + switch (self->platform) { + case 0: + // Unconstrained --> will allow any combination of parameters (valid or invalid) + // this mode is usefull fur future platform and for debugging. + if (self->bit_rate) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + if (self->max_bit_rate) { + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + } + if (self->qp) { + AV_DICT_SET_INT(opts, "qp", self->qp, AV_DICT_DONT_OVERWRITE); + } + if (self->quality >= 0) { + AV_DICT_SET_INT(opts, "global_quality", self->quality, AV_DICT_DONT_OVERWRITE); + } + if (self->low_power) { + AV_DICT_SET_INT(opts, "low_power", self->low_power, AV_DICT_DONT_OVERWRITE); + } + if (self->loop_filter_level >= 0) { + AV_DICT_SET_INT(opts, "loop_filter_level", self->loop_filter_level, AV_DICT_DONT_OVERWRITE); + } + if (self->loop_filter_sharpness >= 0) { + AV_DICT_SET_INT(opts, "loop_filter_sharpness", self->loop_filter_sharpness, AV_DICT_DONT_OVERWRITE); + } + if (self->async_depth) { + AV_DICT_SET_INT(opts, "async_depth", self->async_depth, AV_DICT_DONT_OVERWRITE); + } + break; + case 1: + // Intel + if (self->quality >= 0) { + AV_DICT_SET_INT(opts, "global_quality", self->quality, AV_DICT_DONT_OVERWRITE); + } + switch (self->rc_mode) { + case -1: + // same like 0 but is not sending 'rc_mode' + case 0: + // for auto --> let the driver decide as requested by documentation + if (self->bit_rate) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + if (self->max_bit_rate) { + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + } + if (self->qp) { + AV_DICT_SET_INT(opts, "qp", self->qp, AV_DICT_DONT_OVERWRITE); + } + break; + case 1: + case 4: + // for constant quality: CQP we use qp + if (self->qp) { + AV_DICT_SET_INT(opts, "qp", self->qp, AV_DICT_DONT_OVERWRITE); + } + break; + case 2: + // for constant bitrate: CBR we use bitrate + if (self->bit_rate && self->buff_factor) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + break; + case 3: + // for variable bitrate: VBR we use bitrate + if (self->bit_rate && self->buff_factor) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + break; + case 5: + // for variable bitrate: QVBR we use bitrate + qp + if (self->bit_rate && self->buff_factor) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + if (self->qp) { + AV_DICT_SET_INT(opts, "qp", self->qp, AV_DICT_DONT_OVERWRITE); + } + break; + case 6: + // for variable bitrate: AVBR we use bitrate + if (self->bit_rate && self->buff_factor) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + } + break; + } + if (self->low_power) { + AV_DICT_SET_INT(opts, "low_power", self->low_power, AV_DICT_DONT_OVERWRITE); + } + if (self->loop_filter_level >= 0) { + AV_DICT_SET_INT(opts, "loop_filter_level", self->loop_filter_level, AV_DICT_DONT_OVERWRITE); + } + if (self->loop_filter_sharpness >= 0) { + AV_DICT_SET_INT(opts, "loop_filter_sharpness", self->loop_filter_sharpness, AV_DICT_DONT_OVERWRITE); + } + if (self->async_depth) { + AV_DICT_SET_INT(opts, "async_depth", self->async_depth, AV_DICT_DONT_OVERWRITE); + } + break; + case 2: + // AMD --> will allow any combination of parameters + // I am unable to confirm this platform because I don't have the HW + // Is only going to override bf to 0 (as highlited by the previous implementation) + if (self->bit_rate) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + if (self->max_bit_rate) { + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + } + if (self->qp) { + AV_DICT_SET_INT(opts, "qp", self->qp, AV_DICT_DONT_OVERWRITE); + } + AV_DICT_SET_INT(opts, "bf", 0, 0); + if (self->quality >= 0) { + AV_DICT_SET_INT(opts, "global_quality", self->quality, AV_DICT_DONT_OVERWRITE); + } + if (self->low_power) { + AV_DICT_SET_INT(opts, "low_power", self->low_power, AV_DICT_DONT_OVERWRITE); + } + if (self->loop_filter_level >= 0) { + AV_DICT_SET_INT(opts, "loop_filter_level", self->loop_filter_level, AV_DICT_DONT_OVERWRITE); + } + if (self->loop_filter_sharpness >= 0) { + AV_DICT_SET_INT(opts, "loop_filter_sharpness", self->loop_filter_sharpness, AV_DICT_DONT_OVERWRITE); + } + if (self->async_depth) { + AV_DICT_SET_INT(opts, "async_depth", self->async_depth, AV_DICT_DONT_OVERWRITE); + } + break; } + // force keyframe every 3 sec. + AV_DICT_SET(opts, "force_key_frames", "expr:gte(t,n_forced*3)", AV_DICT_DONT_OVERWRITE); return 0; } @@ -371,7 +841,46 @@ static const codec_profile_class_t codec_profile_vaapi_vp8_class = { { .ic_super = (idclass_t *)&codec_profile_vaapi_class, .ic_class = "codec_profile_vaapi_vp8", - .ic_caption = N_("vaapi_vp8") + .ic_caption = N_("vaapi_vp8"), + .ic_properties = (const property_t[]){ + { + .type = PT_INT, + .id = "quality", // Don't change + .name = N_("Global Quality"), + .desc = N_("Set encode quality [-1=skip 0-127]."), + .group = 5, + .opts = PO_EXPERT, + .get_opts = codec_profile_class_get_opts, + .off = offsetof(tvh_codec_profile_vaapi_t, quality), + .intextra = INTEXTRA_RANGE(-1, 127, 1), + .def.i = 40, + }, + { + .type = PT_INT, + .id = "loop_filter_level", // Don't change + .name = N_("Loop filter level"), + .desc = N_("Set Loop filter level (-1=skip from 0 to 63) [default 16]"), + .group = 5, + .opts = PO_EXPERT, + .get_opts = codec_profile_class_get_opts, + .off = offsetof(tvh_codec_profile_vaapi_t, loop_filter_level), + .intextra = INTEXTRA_RANGE(-1, 63, 1), + .def.i = 16, + }, + { + .type = PT_INT, + .id = "loop_filter_sharpness", // Don't change + .name = N_("Loop filter sharpness"), + .desc = N_("Set Loop filter sharpness (-1=skip from 0 to 15) [default 4]"), + .group = 5, + .opts = PO_EXPERT, + .get_opts = codec_profile_class_get_opts, + .off = offsetof(tvh_codec_profile_vaapi_t, loop_filter_sharpness), + .intextra = INTEXTRA_RANGE(-1, 15, 1), + .def.i = 4, + }, + {} + } }, .open = (codec_profile_open_meth)tvh_codec_profile_vaapi_vp8_open, }; @@ -389,6 +898,10 @@ TVHVideoCodec tvh_codec_vaapi_vp8 = { /* vp9_vaapi =============================================================== */ static const AVProfile vaapi_vp9_profiles[] = { + { FF_PROFILE_VP9_0, "Profile0" }, + { FF_PROFILE_VP9_1, "Profile1" }, + { FF_PROFILE_VP9_2, "Profile2" }, + { FF_PROFILE_VP9_3, "Profile3" }, { FF_PROFILE_UNKNOWN }, }; @@ -396,22 +909,166 @@ static int tvh_codec_profile_vaapi_vp9_open(tvh_codec_profile_vaapi_t *self, AVDictionary **opts) { - // bit_rate or qp - if (self->bit_rate) { - AV_DICT_SET_BIT_RATE(opts, self->bit_rate); - if (self->buff_factor <= 0) { - self->buff_factor = 3; - } - AV_DICT_SET_INT(opts, "maxrate", (self->bit_rate) * 1000, AV_DICT_DONT_OVERWRITE); - AV_DICT_SET_INT(opts, "bufsize", ((self->bit_rate) * 1000) * self->buff_factor, AV_DICT_DONT_OVERWRITE); - AV_DICT_SET(opts, "force_key_frames", "expr:gte(t,n_forced*3)", AV_DICT_DONT_OVERWRITE); + // to avoid issues we have this check: + if (self->buff_factor <= 0) { + self->buff_factor = 3; } - else { - AV_DICT_SET_QP(opts, self->qp, 25); + int int_bitrate = (int)((double)(self->bit_rate) * 1024.0 * (1.0 + (self->bit_rate_scale_factor * ((double)(self->size.den) - 480.0) / 480.0))); + int int_buffer_size = (int)((double)(self->bit_rate) * 2048.0 * self->buff_factor * (1.0 + self->bit_rate_scale_factor * ((double)(self->size.den) - 480.0) / 480)); + int int_max_bitrate = (int)((double)(self->max_bit_rate) * 1024.0 * (1.0 + (self->bit_rate_scale_factor * ((double)(self->size.den) - 480.0) / 480.0))); + // https://wiki.libav.org/Hardware/vaapi + // to find available parameters use: + // ffmpeg -hide_banner -h encoder=vp9_vaapi + //-rc_mode E..V....... Set rate control mode (from 0 to 6) (default auto) + // auto 0 E..V....... Choose mode automatically based on other parameters + // CQP 1 E..V....... Constant-quality + // CBR 2 E..V....... Constant-bitrate + // VBR 3 E..V....... Variable-bitrate + // ICQ 4 E..V....... Intelligent constant-quality + // QVBR 5 E..V....... Quality-defined variable-bitrate + // AVBR 6 E..V....... Average variable-bitrate + if (self->rc_mode >= 0) { + AV_DICT_SET_INT(opts, "rc_mode", self->rc_mode, AV_DICT_DONT_OVERWRITE); } - if (self->ignore_bframe) { - AV_DICT_SET_INT(opts, "bf", 0, 0); + switch (self->platform) { + case 0: + // Unconstrained --> will allow any combination of parameters (valid or invalid) + // this mode is usefull fur future platform and for debugging. + if (self->bit_rate) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + if (self->max_bit_rate) { + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + } + if (self->qp) { + AV_DICT_SET_INT(opts, "qp", self->qp, AV_DICT_DONT_OVERWRITE); + } + if (self->quality >= 0) { + AV_DICT_SET_INT(opts, "global_quality", self->quality, AV_DICT_DONT_OVERWRITE); + } + if (self->low_power) { + AV_DICT_SET_INT(opts, "low_power", self->low_power, AV_DICT_DONT_OVERWRITE); + } + if (self->loop_filter_level >= 0) { + AV_DICT_SET_INT(opts, "loop_filter_level", self->loop_filter_level, AV_DICT_DONT_OVERWRITE); + } + if (self->loop_filter_sharpness >= 0) { + AV_DICT_SET_INT(opts, "loop_filter_sharpness", self->loop_filter_sharpness, AV_DICT_DONT_OVERWRITE); + } + if (self->async_depth) { + AV_DICT_SET_INT(opts, "async_depth", self->async_depth, AV_DICT_DONT_OVERWRITE); + } + break; + case 1: + // Intel + if (self->quality >= 0) { + AV_DICT_SET_INT(opts, "global_quality", self->quality, AV_DICT_DONT_OVERWRITE); + } + switch (self->rc_mode) { + case -1: + // same like 0 but is not sending 'rc_mode' + case 0: + // for auto --> let the driver decide as requested by documentation + if (self->bit_rate) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + if (self->max_bit_rate) { + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + } + if (self->qp) { + AV_DICT_SET_INT(opts, "qp", self->qp, AV_DICT_DONT_OVERWRITE); + } + break; + case 1: + case 4: + // for constant quality: CQP we use qp + if (self->qp) { + AV_DICT_SET_INT(opts, "qp", self->qp, AV_DICT_DONT_OVERWRITE); + } + break; + case 2: + // for constant bitrate: CBR we use bitrate + if (self->bit_rate && self->buff_factor) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + break; + case 3: + // for variable bitrate: VBR we use bitrate + if (self->bit_rate && self->buff_factor) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + break; + case 5: + // for variable bitrate: QVBR we use bitrate + qp + if (self->bit_rate && self->buff_factor) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + if (self->qp) { + AV_DICT_SET_INT(opts, "qp", self->qp, AV_DICT_DONT_OVERWRITE); + } + break; + case 6: + // for variable bitrate: AVBR we use bitrate + if (self->bit_rate && self->buff_factor) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + } + break; + } + if (self->low_power) { + AV_DICT_SET_INT(opts, "low_power", self->low_power, AV_DICT_DONT_OVERWRITE); + } + if (self->loop_filter_level >= 0) { + AV_DICT_SET_INT(opts, "loop_filter_level", self->loop_filter_level, AV_DICT_DONT_OVERWRITE); + } + if (self->loop_filter_sharpness >= 0) { + AV_DICT_SET_INT(opts, "loop_filter_sharpness", self->loop_filter_sharpness, AV_DICT_DONT_OVERWRITE); + } + if (self->async_depth) { + AV_DICT_SET_INT(opts, "async_depth", self->async_depth, AV_DICT_DONT_OVERWRITE); + } + break; + case 2: + // AMD --> will allow any combination of parameters + // I am unable to confirm this platform because I don't have the HW + // Is only going to override bf to 0 (as highlited by the previous implementation) + if (self->bit_rate) { + AV_DICT_SET_INT(opts, "b", int_bitrate, AV_DICT_DONT_OVERWRITE); + AV_DICT_SET_INT(opts, "bufsize", int_buffer_size, AV_DICT_DONT_OVERWRITE); + } + if (self->max_bit_rate) { + AV_DICT_SET_INT(opts, "maxrate", int_max_bitrate, AV_DICT_DONT_OVERWRITE); + } + if (self->qp) { + AV_DICT_SET_INT(opts, "qp", self->qp, AV_DICT_DONT_OVERWRITE); + } + AV_DICT_SET_INT(opts, "bf", 0, 0); + if (self->quality >= 0) { + AV_DICT_SET_INT(opts, "global_quality", self->quality, AV_DICT_DONT_OVERWRITE); + } + if (self->low_power) { + AV_DICT_SET_INT(opts, "low_power", self->low_power, AV_DICT_DONT_OVERWRITE); + } + if (self->loop_filter_level >= 0) { + AV_DICT_SET_INT(opts, "loop_filter_level", self->loop_filter_level, AV_DICT_DONT_OVERWRITE); + } + if (self->loop_filter_sharpness >= 0) { + AV_DICT_SET_INT(opts, "loop_filter_sharpness", self->loop_filter_sharpness, AV_DICT_DONT_OVERWRITE); + } + if (self->async_depth) { + AV_DICT_SET_INT(opts, "async_depth", self->async_depth, AV_DICT_DONT_OVERWRITE); + } + break; } + // force keyframe every 3 sec. + AV_DICT_SET(opts, "force_key_frames", "expr:gte(t,n_forced*3)", AV_DICT_DONT_OVERWRITE); return 0; } @@ -420,7 +1077,46 @@ static const codec_profile_class_t codec_profile_vaapi_vp9_class = { { .ic_super = (idclass_t *)&codec_profile_vaapi_class, .ic_class = "codec_profile_vaapi_vp9", - .ic_caption = N_("vaapi_vp9") + .ic_caption = N_("vaapi_vp9"), + .ic_properties = (const property_t[]){ + { + .type = PT_INT, + .id = "quality", // Don't change + .name = N_("Global Quality"), + .desc = N_("Set encode quality [-1=skip 0-255]."), + .group = 5, + .opts = PO_EXPERT, + .get_opts = codec_profile_class_get_opts, + .off = offsetof(tvh_codec_profile_vaapi_t, quality), + .intextra = INTEXTRA_RANGE(-1, 255, 1), + .def.i = 40, + }, + { + .type = PT_INT, + .id = "loop_filter_level", // Don't change + .name = N_("Loop filter level"), + .desc = N_("Set Loop filter level (-1=skip from 0 to 63) [default 16]"), + .group = 5, + .opts = PO_EXPERT, + .get_opts = codec_profile_class_get_opts, + .off = offsetof(tvh_codec_profile_vaapi_t, loop_filter_level), + .intextra = INTEXTRA_RANGE(-1, 63, 1), + .def.i = 16, + }, + { + .type = PT_INT, + .id = "loop_filter_sharpness", // Don't change + .name = N_("Loop filter sharpness"), + .desc = N_("Set Loop filter sharpness (-1=skip from 0 to 15) [default 4]"), + .group = 5, + .opts = PO_EXPERT, + .get_opts = codec_profile_class_get_opts, + .off = offsetof(tvh_codec_profile_vaapi_t, loop_filter_sharpness), + .intextra = INTEXTRA_RANGE(-1, 15, 1), + .def.i = 4, + }, + {} + } }, .open = (codec_profile_open_meth)tvh_codec_profile_vaapi_vp9_open, }; diff --git a/src/transcoding/transcode/hwaccels/hwaccels.c b/src/transcoding/transcode/hwaccels/hwaccels.c index 7a79cda18..0130f2af6 100644 --- a/src/transcoding/transcode/hwaccels/hwaccels.c +++ b/src/transcoding/transcode/hwaccels/hwaccels.c @@ -177,12 +177,12 @@ hwaccels_get_deint_filter(AVCodecContext *avctx, char *filter, size_t filter_len /* encoding ================================================================= */ int -hwaccels_encode_setup_context(AVCodecContext *avctx) +hwaccels_encode_setup_context(AVCodecContext *avctx, int low_power) { switch (avctx->pix_fmt) { #if ENABLE_VAAPI case AV_PIX_FMT_VAAPI: - return vaapi_encode_setup_context(avctx); + return vaapi_encode_setup_context(avctx, low_power); #endif default: break; diff --git a/src/transcoding/transcode/hwaccels/hwaccels.h b/src/transcoding/transcode/hwaccels/hwaccels.h index 55caad8e6..a14647672 100644 --- a/src/transcoding/transcode/hwaccels/hwaccels.h +++ b/src/transcoding/transcode/hwaccels/hwaccels.h @@ -47,7 +47,7 @@ hwaccels_get_deint_filter(AVCodecContext *avctx, char *filter, size_t filter_len /* encoding ================================================================= */ int -hwaccels_encode_setup_context(AVCodecContext *avctx); +hwaccels_encode_setup_context(AVCodecContext *avctx, int low_power); void hwaccels_encode_close_context(AVCodecContext *avctx); diff --git a/src/transcoding/transcode/hwaccels/vaapi.c b/src/transcoding/transcode/hwaccels/vaapi.c index 3cf784e1c..c449060dc 100644 --- a/src/transcoding/transcode/hwaccels/vaapi.c +++ b/src/transcoding/transcode/hwaccels/vaapi.c @@ -232,6 +232,9 @@ tvhva_context_profile(TVHVAContext *self, AVCodecContext *avctx) for (j = 0; j < profiles_max; j++) { profiles[j] = VAProfileNone; } + if (profiles_max == 0) { + tvherror(LS_VAAPI, "%s: vaMaxNumProfiles() returned %d; vaapi doesn't have any profiles available, run: $ vainfo", self->logpref, profiles_max); + } va_res = vaQueryConfigProfiles(self->display, profiles, &profiles_len); if (va_res == VA_STATUS_SUCCESS) { @@ -242,6 +245,9 @@ tvhva_context_profile(TVHVAContext *self, AVCodecContext *avctx) } } } + else { + tvherror(LS_VAAPI, "%s: va_res != VA_STATUS_SUCCESS; Failed to query profiles: %d (%s), run: $ vainfo", self->logpref, va_res, vaErrorStr(va_res)); + } free(profiles); } @@ -256,10 +262,11 @@ tvhva_context_check_profile(TVHVAContext *self, VAProfile profile) VAEntrypoint *entrypoints = NULL; int res = -1, i, entrypoints_max, entrypoints_len; - if ((entrypoints_max = vaMaxNumEntrypoints(self->display)) > 0 && - (entrypoints = calloc(entrypoints_max, sizeof(VAEntrypoint)))) { - va_res = vaQueryConfigEntrypoints(self->display, profile, - entrypoints, &entrypoints_len); + entrypoints_max = vaMaxNumEntrypoints(self->display); + + if (entrypoints_max > 0) { + entrypoints = calloc(entrypoints_max, sizeof(VAEntrypoint)); + va_res = vaQueryConfigEntrypoints(self->display, profile, entrypoints, &entrypoints_len); if (va_res == VA_STATUS_SUCCESS) { for (i = 0; i < entrypoints_len; i++) { if (entrypoints[i] == self->entrypoint) { @@ -268,8 +275,43 @@ tvhva_context_check_profile(TVHVAContext *self, VAProfile profile) } } } + else { + tvherror(LS_VAAPI, "%s: va_res != VA_STATUS_SUCCESS; Failed to query entrypoints: %d (%s), run: $ vainfo", self->logpref, va_res, vaErrorStr(va_res)); + } + if (res != 0) { + // before giving up we swap VAEntrypointEncSliceLP with VAEntrypointEncSlice or viceversa + if (self->entrypoint == VAEntrypointEncSlice) { + // we try VAEntrypointEncSliceLP + self->entrypoint = VAEntrypointEncSliceLP; + va_res = vaQueryConfigEntrypoints(self->display, profile, entrypoints, &entrypoints_len); + if (va_res == VA_STATUS_SUCCESS) { + for (i = 0; i < entrypoints_len; i++) { + if (entrypoints[i] == self->entrypoint) { + res = 0; + break; + } + } + } + } + else { + // we try VAEntrypointEncSlice + self->entrypoint = VAEntrypointEncSlice; + va_res = vaQueryConfigEntrypoints(self->display, profile, entrypoints, &entrypoints_len); + if (va_res == VA_STATUS_SUCCESS) { + for (i = 0; i < entrypoints_len; i++) { + if (entrypoints[i] == self->entrypoint) { + res = 0; + break; + } + } + } + } + } free(entrypoints); } + else { + tvherror(LS_VAAPI, "%s: vaMaxNumEntrypoints() returned %d; vaapi doesn't have any entrypoints available, run: $ vainfo", self->logpref, entrypoints_max); + } return res; } @@ -409,13 +451,20 @@ tvhva_context_setup(TVHVAContext *self, AVCodecContext *avctx) } libav_vaapi_init_context(self->display); + // + profile = tvhva_context_profile(self, avctx); + + if (profile == VAProfileNone) { + tvherror(LS_VAAPI, "%s: tvhva_context_profile() returned VAProfileNone for %s", + self->logpref, + avctx->codec->name); + return -1; + } - if ((profile = tvhva_context_profile(self, avctx)) == VAProfileNone || - tvhva_context_check_profile(self, profile)) { - tvherror(LS_VAAPI, "%s: unsupported codec: %s and/or profile: %s", + if (tvhva_context_check_profile(self, profile)) { + tvherror(LS_VAAPI, "%s: tvhva_context_check_profile() check failed for codec: %s --> codec not available", self->logpref, - avctx->codec->name, - av_get_profile_name(avctx->codec, avctx->profile)); + avctx->codec->name); return -1; } if (!(format = tvhva_get_format(self->io_format))) { @@ -578,13 +627,21 @@ vaapi_get_deint_filter(AVCodecContext *avctx, char *filter, size_t filter_len) /* encoding ================================================================= */ int -vaapi_encode_setup_context(AVCodecContext *avctx) +vaapi_encode_setup_context(AVCodecContext *avctx, int low_power) { TVHContext *ctx = avctx->opaque; TVHVAContext *hwaccel_context = NULL; + VAEntrypoint entrypoint; + + if (low_power) { + entrypoint = VAEntrypointEncSliceLP; + } + else { + entrypoint = VAEntrypointEncSlice; + } if (!(hwaccel_context = - tvhva_context_create("encode", avctx, VAEntrypointEncSlice))) { + tvhva_context_create("encode", avctx, entrypoint))) { return -1; } if (!(ctx->hw_device_octx = av_buffer_ref(hwaccel_context->hw_device_ref))) { diff --git a/src/transcoding/transcode/hwaccels/vaapi.h b/src/transcoding/transcode/hwaccels/vaapi.h index 9a61a5e24..3ce9817b9 100644 --- a/src/transcoding/transcode/hwaccels/vaapi.h +++ b/src/transcoding/transcode/hwaccels/vaapi.h @@ -45,7 +45,7 @@ vaapi_get_deint_filter(AVCodecContext *avctx, char *filter, size_t filter_len); /* encoding ================================================================= */ int -vaapi_encode_setup_context(AVCodecContext *avctx); +vaapi_encode_setup_context(AVCodecContext *avctx, int low_power); void vaapi_encode_close_context(AVCodecContext *avctx); diff --git a/src/transcoding/transcode/video.c b/src/transcoding/transcode/video.c index 4482b5fba..ac002e136 100644 --- a/src/transcoding/transcode/video.c +++ b/src/transcoding/transcode/video.c @@ -162,7 +162,7 @@ tvh_video_context_open_encoder(TVHContext *self, AVDictionary **opts) #if ENABLE_HWACCELS self->oavctx->coded_width = self->oavctx->width; self->oavctx->coded_height = self->oavctx->height; - if (hwaccels_encode_setup_context(self->oavctx)) { + if (hwaccels_encode_setup_context(self->oavctx, self->profile->low_power)) { return -1; } #endif diff --git a/src/webui/static/app/codec.js b/src/webui/static/app/codec.js index dff82db6c..6ca5beba0 100644 --- a/src/webui/static/app/codec.js +++ b/src/webui/static/app/codec.js @@ -229,9 +229,439 @@ var codec_profile_forms = { }); }, - 'codec_profile_vaapi_h264': genericCBRvsVBR, + 'codec_profile_vaapi_h264': function(form) { + function updateFilters(form) { + var platform_field = form.findField('platform'); + var rc_mode_field = form.findField('rc_mode'); + var bit_rate_field = form.findField('bit_rate'); + var max_bit_rate_field = form.findField('max_bit_rate'); + var buff_factor_field = form.findField('buff_factor'); + var bit_rate_scale_factor_field = form.findField('bit_rate_scale_factor'); + var qp_field = form.findField('qp'); + var low_power_field = form.findField('low_power'); + + var platform = platform_field.getValue(); + switch (platform) { + case 0: + // Unconstrained --> will allow any combination of parameters (valid or invalid) + // this mode is usefull fur future platform and for debugging. + // no filter applied + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(false); + low_power_field.setDisabled(false); + break; + case 1: + // Intel + var rc_mode = rc_mode_field.getValue(); + switch (rc_mode) { + case -1: + case 0: + // for auto --> let the driver decide as requested by documentation + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(false); + break; + case 1: + case 4: + // for constant quality: CQP and ICQ we use qp + bit_rate_field.setDisabled(true); + max_bit_rate_field.setDisabled(true); + buff_factor_field.setDisabled(true); + bit_rate_scale_factor_field.setDisabled(true); + qp_field.setDisabled(false); + break; + case 2: + // for constant bitrate: CBR we use bitrate + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(true); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(true); + break; + case 3: + // for variable bitrate: VBR we use bitrate + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(true); + break; + case 5: + // for variable bitrate: QVBR we use bitrate + qp + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(false); + break; + case 6: + // for variable bitrate: AVBR we use bitrate + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(true); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(true); + break; + } + low_power_field.setDisabled(false); + break; + case 2: + // AMD --> will allow any combination of parameters + // I am unable to confirm this platform because I don't have the HW + // Is only going to override bf to 0 (as highlited by the previous implementation) + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(false); + break; + default: + } + } + + var platform_field = form.findField('platform'); + var rc_mode_field = form.findField('rc_mode'); + // first time we have to call this manually + updateFilters(form); + + // on platform change + platform_field.on('select', function(spinner) { + updateFilters(form); + }); + // on rc_mode change + rc_mode_field.on('select', function(spinner) { + updateFilters(form); + }); + }, + + 'codec_profile_vaapi_hevc': function(form) { + function updateFilters(form) { + var platform_field = form.findField('platform'); + var rc_mode_field = form.findField('rc_mode'); + var bit_rate_field = form.findField('bit_rate'); + var max_bit_rate_field = form.findField('max_bit_rate'); + var buff_factor_field = form.findField('buff_factor'); + var bit_rate_scale_factor_field = form.findField('bit_rate_scale_factor'); + var qp_field = form.findField('qp'); + var low_power_field = form.findField('low_power'); + + var platform = platform_field.getValue(); + switch (platform) { + case 0: + // Unconstrained --> will allow any combination of parameters (valid or invalid) + // this mode is usefull fur future platform and for debugging. + // no filter applied + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(false); + low_power_field.setDisabled(false); + break; + case 1: + // Intel + var rc_mode = rc_mode_field.getValue(); + switch (rc_mode) { + case -1: + case 0: + // for auto --> let the driver decide as requested by documentation + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(false); + break; + case 1: + case 4: + // for constant quality: CQP and ICQ we use qp + bit_rate_field.setDisabled(true); + max_bit_rate_field.setDisabled(true); + buff_factor_field.setDisabled(true); + bit_rate_scale_factor_field.setDisabled(true); + qp_field.setDisabled(false); + break; + case 2: + // for constant bitrate: CBR we use bitrate + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(true); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(true); + break; + case 3: + // for variable bitrate: VBR we use bitrate + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(true); + break; + case 5: + // for variable bitrate: QVBR we use bitrate + qp + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(false); + break; + case 6: + // for variable bitrate: AVBR we use bitrate + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(true); + bit_rate_scale_factor_field.setDisabled(true); + qp_field.setDisabled(true); + break; + } + low_power_field.setDisabled(false); + break; + case 2: + // AMD --> will allow any combination of parameters + // I am unable to confirm this platform because I don't have the HW + // Is only going to override bf to 0 (as highlited by the previous implementation) + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(false); + break; + default: + } + } + + var platform_field = form.findField('platform'); + var rc_mode_field = form.findField('rc_mode'); + // first time we have to call this manually + updateFilters(form); + + // on platform change + platform_field.on('select', function(spinner) { + updateFilters(form); + }); + // on rc_mode change + rc_mode_field.on('select', function(spinner) { + updateFilters(form); + }); + }, + + 'codec_profile_vaapi_vp8': function(form) { + function updateFilters(form) { + var platform_field = form.findField('platform'); + var rc_mode_field = form.findField('rc_mode'); + var bit_rate_field = form.findField('bit_rate'); + var max_bit_rate_field = form.findField('max_bit_rate'); + var buff_factor_field = form.findField('buff_factor'); + var bit_rate_scale_factor_field = form.findField('bit_rate_scale_factor'); + var qp_field = form.findField('qp'); + + var platform = platform_field.getValue(); + switch (platform) { + case 0: + // Unconstrained --> will allow any combination of parameters (valid or invalid) + // this mode is usefull fur future platform and for debugging. + // no filter applied + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(false); + break; + case 1: + // Intel + var rc_mode = rc_mode_field.getValue(); + switch (rc_mode) { + case -1: + case 0: + // for auto --> let the driver decide as requested by documentation + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(false); + break; + case 1: + case 4: + // for constant quality: CQP and ICQ we use qp + bit_rate_field.setDisabled(true); + max_bit_rate_field.setDisabled(true); + buff_factor_field.setDisabled(true); + bit_rate_scale_factor_field.setDisabled(true); + qp_field.setDisabled(false); + break; + case 2: + // for constant bitrate: CBR we use bitrate + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(true); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(true); + break; + case 3: + // for variable bitrate: VBR we use bitrate + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(true); + break; + case 5: + // for variable bitrate: QVBR we use bitrate + qp + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(false); + break; + case 6: + // for variable bitrate: AVBR we use bitrate + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(true); + bit_rate_scale_factor_field.setDisabled(true); + qp_field.setDisabled(true); + break; + } + break; + case 2: + // AMD --> will allow any combination of parameters + // I am unable to confirm this platform because I don't have the HW + // Is only going to override bf to 0 (as highlited by the previous implementation) + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(false); + break; + default: + } + } + + var platform_field = form.findField('platform'); + var rc_mode_field = form.findField('rc_mode'); + // first time we have to call this manually + updateFilters(form); + + // on platform change + platform_field.on('select', function(spinner) { + updateFilters(form); + }); + // on rc_mode change + rc_mode_field.on('select', function(spinner) { + updateFilters(form); + }); + }, - 'codec_profile_vaapi_hevc': genericCBRvsVBR + 'codec_profile_vaapi_vp9': function(form) { + function updateFilters(form) { + var platform_field = form.findField('platform'); + var rc_mode_field = form.findField('rc_mode'); + var bit_rate_field = form.findField('bit_rate'); + var max_bit_rate_field = form.findField('max_bit_rate'); + var buff_factor_field = form.findField('buff_factor'); + var bit_rate_scale_factor_field = form.findField('bit_rate_scale_factor'); + var qp_field = form.findField('qp'); + + var platform = platform_field.getValue(); + switch (platform) { + case 0: + // Unconstrained --> will allow any combination of parameters (valid or invalid) + // this mode is usefull fur future platform and for debugging. + // no filter applied + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(false); + break; + case 1: + // Intel + var rc_mode = rc_mode_field.getValue(); + switch (rc_mode) { + case -1: + case 0: + // for auto --> let the driver decide as requested by documentation + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(false); + break; + case 1: + case 4: + // for constant quality: CQP and ICQ we use qp + bit_rate_field.setDisabled(true); + max_bit_rate_field.setDisabled(true); + buff_factor_field.setDisabled(true); + bit_rate_scale_factor_field.setDisabled(true); + qp_field.setDisabled(false); + break; + case 2: + // for constant bitrate: CBR we use bitrate + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(true); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(true); + break; + case 3: + // for variable bitrate: VBR we use bitrate + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(true); + break; + case 5: + // for variable bitrate: QVBR we use bitrate + qp + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(false); + break; + case 6: + // for variable bitrate: AVBR we use bitrate + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(true); + bit_rate_scale_factor_field.setDisabled(true); + qp_field.setDisabled(true); + break; + } + break; + case 2: + // AMD --> will allow any combination of parameters + // I am unable to confirm this platform because I don't have the HW + // Is only going to override bf to 0 (as highlited by the previous implementation) + bit_rate_field.setDisabled(false); + max_bit_rate_field.setDisabled(false); + buff_factor_field.setDisabled(false); + bit_rate_scale_factor_field.setDisabled(false); + qp_field.setDisabled(false); + break; + default: + } + } + + var platform_field = form.findField('platform'); + var rc_mode_field = form.findField('rc_mode'); + // first time we have to call this manually + updateFilters(form); + + // on platform change + platform_field.on('select', function(spinner) { + updateFilters(form); + }); + // on rc_mode change + rc_mode_field.on('select', function(spinner) { + updateFilters(form); + }); + } };