diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c index cf92ad453323a71e65f8e3625682e9766128e51e..13b97c7d67365ef5215a273db13dc4b44885bb8e 100644 --- a/libavcodec/qsvenc.c +++ b/libavcodec/qsvenc.c @@ -266,10 +266,93 @@ static void dump_video_param(AVCodecContext *avctx, QSVEncContext *q, } } -static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) +static int select_rc_mode(AVCodecContext *avctx, QSVEncContext *q) { - const char *ratecontrol_desc; + const char *rc_desc; + mfxU16 rc_mode; + + int want_la = q->look_ahead; + int want_qscale = !!(avctx->flags & AV_CODEC_FLAG_QSCALE); + int want_vcm = q->vcm; + + if (want_la && !QSV_HAVE_LA) { + av_log(avctx, AV_LOG_ERROR, + "Lookahead ratecontrol mode requested, but is not supported by this SDK version\n"); + return AVERROR(ENOSYS); + } + if (want_vcm && !QSV_HAVE_VCM) { + av_log(avctx, AV_LOG_ERROR, + "VCM ratecontrol mode requested, but is not supported by this SDK version\n"); + return AVERROR(ENOSYS); + } + + if (want_la + want_qscale + want_vcm > 1) { + av_log(avctx, AV_LOG_ERROR, + "More than one of: { constant qscale, lookahead, VCM } requested, " + "only one of them can be used at a time.\n"); + return AVERROR(EINVAL); + } + + if (want_qscale) { + rc_mode = MFX_RATECONTROL_CQP; + rc_desc = "constant quantization parameter (CQP)"; + } +#if QSV_HAVE_VCM + else if (want_vcm) { + rc_mode = MFX_RATECONTROL_VCM; + rc_desc = "video conferencing mode (VCM)"; + } +#endif +#if QSV_HAVE_LA + else if (want_la) { + rc_mode = MFX_RATECONTROL_LA; + rc_desc = "VBR with lookahead (LA)"; + +#if QSV_HAVE_ICQ + if (avctx->global_quality > 0) { + rc_mode = MFX_RATECONTROL_LA_ICQ; + rc_desc = "intelligent constant quality with lookahead (LA_ICQ)"; + } +#endif + } +#endif +#if QSV_HAVE_ICQ + else if (avctx->global_quality > 0) { + rc_mode = MFX_RATECONTROL_ICQ; + rc_desc = "intelligent constant quality (ICQ)"; + } +#endif + else if (avctx->rc_max_rate == avctx->bit_rate) { + rc_mode = MFX_RATECONTROL_CBR; + rc_desc = "constant bitrate (CBR)"; + } else if (!avctx->rc_max_rate) { + rc_mode = MFX_RATECONTROL_AVBR; + rc_desc = "average variable bitrate (AVBR)"; + } else { + rc_mode = MFX_RATECONTROL_VBR; + rc_desc = "variable bitrate (VBR)"; + } + + q->param.mfx.RateControlMethod = rc_mode; + av_log(avctx, AV_LOG_VERBOSE, "Using the %s ratecontrol method\n", rc_desc); + + return 0; +} +static int rc_supported(QSVEncContext *q) +{ + mfxVideoParam param_out = { .mfx.CodecId = q->param.mfx.CodecId }; + mfxStatus ret; + + ret = MFXVideoENCODE_Query(q->session, &q->param, ¶m_out); + if (ret < 0 || + param_out.mfx.RateControlMethod != q->param.mfx.RateControlMethod) + return 0; + return 1; +} + +static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) +{ float quant; int ret; @@ -329,33 +412,16 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) q->param.mfx.FrameInfo.FrameRateExtD = avctx->time_base.num; } - if (avctx->flags & AV_CODEC_FLAG_QSCALE) { - q->param.mfx.RateControlMethod = MFX_RATECONTROL_CQP; - ratecontrol_desc = "constant quantization parameter (CQP)"; - } else if (avctx->rc_max_rate == avctx->bit_rate) { - q->param.mfx.RateControlMethod = MFX_RATECONTROL_CBR; - ratecontrol_desc = "constant bitrate (CBR)"; - } else if (!avctx->rc_max_rate) { -#if QSV_VERSION_ATLEAST(1,7) - if (q->look_ahead) { - q->param.mfx.RateControlMethod = MFX_RATECONTROL_LA; - ratecontrol_desc = "lookahead (LA)"; - } else -#endif - { - q->param.mfx.RateControlMethod = MFX_RATECONTROL_AVBR; - ratecontrol_desc = "average variable bitrate (AVBR)"; - } - } else { - q->param.mfx.RateControlMethod = MFX_RATECONTROL_VBR; - ratecontrol_desc = "variable bitrate (VBR)"; - } - - av_log(avctx, AV_LOG_VERBOSE, "Using the %s ratecontrol method\n", ratecontrol_desc); + ret = select_rc_mode(avctx, q); + if (ret < 0) + return ret; switch (q->param.mfx.RateControlMethod) { case MFX_RATECONTROL_CBR: case MFX_RATECONTROL_VBR: +#if QSV_HAVE_VCM + case MFX_RATECONTROL_VCM: +#endif q->param.mfx.InitialDelayInKB = avctx->rc_initial_buffer_occupancy / 1000; q->param.mfx.TargetKbps = avctx->bit_rate / 1000; q->param.mfx.MaxKbps = avctx->rc_max_rate / 1000; @@ -369,13 +435,23 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) break; case MFX_RATECONTROL_AVBR: -#if QSV_VERSION_ATLEAST(1,7) - case MFX_RATECONTROL_LA: -#endif q->param.mfx.TargetKbps = avctx->bit_rate / 1000; q->param.mfx.Convergence = q->avbr_convergence; q->param.mfx.Accuracy = q->avbr_accuracy; break; +#if QSV_HAVE_LA + case MFX_RATECONTROL_LA: + q->param.mfx.TargetKbps = avctx->bit_rate / 1000; + q->extco2.LookAheadDepth = q->look_ahead_depth; + break; +#if QSV_HAVE_ICQ + case MFX_RATECONTROL_LA_ICQ: + q->extco2.LookAheadDepth = q->look_ahead_depth; + case MFX_RATECONTROL_ICQ: + q->param.mfx.ICQQuality = avctx->global_quality; + break; +#endif +#endif } // the HEVC encoder plugin currently fails if coding options @@ -391,24 +467,26 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->extco; -#if QSV_VERSION_ATLEAST(1,6) - q->extco2.Header.BufferId = MFX_EXTBUFF_CODING_OPTION2; - q->extco2.Header.BufferSz = sizeof(q->extco2); +#if QSV_HAVE_CO2 + if (avctx->codec_id == AV_CODEC_ID_H264) { + q->extco2.Header.BufferId = MFX_EXTBUFF_CODING_OPTION2; + q->extco2.Header.BufferSz = sizeof(q->extco2); + q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->extco2; -#if QSV_VERSION_ATLEAST(1,7) - // valid value range is from 10 to 100 inclusive - // to instruct the encoder to use the default value this should be set to zero - q->extco2.LookAheadDepth = q->look_ahead_depth != 0 ? FFMAX(10, q->look_ahead_depth) : 0; -#endif #if QSV_VERSION_ATLEAST(1,8) - q->extco2.LookAheadDS = q->look_ahead_downsampling; + q->extco2.LookAheadDS = q->look_ahead_downsampling; #endif - - q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->extco2; - + } #endif } + if (!rc_supported(q)) { + av_log(avctx, AV_LOG_ERROR, + "Selected ratecontrol mode is not supported by the QSV " + "runtime. Choose a different mode.\n"); + return AVERROR(ENOSYS); + } + return 0; } diff --git a/libavcodec/qsvenc.h b/libavcodec/qsvenc.h index 238be6a1aeba0c88cfbbd24c0b2b425f2c91458b..4c0f5a5efa60073dba46306148cdc35ea006a2e3 100644 --- a/libavcodec/qsvenc.h +++ b/libavcodec/qsvenc.h @@ -57,6 +57,7 @@ { "slow", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TARGETUSAGE_3 }, INT_MIN, INT_MAX, VE, "preset" }, \ { "slower", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TARGETUSAGE_2 }, INT_MIN, INT_MAX, VE, "preset" }, \ { "veryslow", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TARGETUSAGE_BEST_QUALITY }, INT_MIN, INT_MAX, VE, "preset" }, \ +{ "vcm", "Use the video conferencing mode ratecontrol", OFFSET(qsv.vcm), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE }, \ typedef struct QSVEncContext { AVCodecContext *avctx; @@ -74,7 +75,7 @@ typedef struct QSVEncContext { mfxFrameAllocRequest req; mfxExtCodingOption extco; -#if QSV_VERSION_ATLEAST(1,6) +#if QSV_HAVE_CO2 mfxExtCodingOption2 extco2; #endif @@ -82,7 +83,7 @@ typedef struct QSVEncContext { mfxFrameSurface1 **opaque_surfaces; AVBufferRef *opaque_alloc_buf; - mfxExtBuffer *extparam_internal[3]; + mfxExtBuffer *extparam_internal[2 + QSV_HAVE_CO2]; int nb_extparam_internal; mfxExtBuffer **extparam; @@ -100,6 +101,7 @@ typedef struct QSVEncContext { int look_ahead; int look_ahead_depth; int look_ahead_downsampling; + int vcm; char *load_plugins; } QSVEncContext; diff --git a/libavcodec/qsvenc_h264.c b/libavcodec/qsvenc_h264.c index 147194b96ce3cb3a8fdb9ce22480d25141111db8..542711bd4a89b4b8077144d6fdb002b6657c8bb6 100644 --- a/libavcodec/qsvenc_h264.c +++ b/libavcodec/qsvenc_h264.c @@ -70,7 +70,7 @@ static const AVOption options[] = { { "idr_interval", "Distance (in I-frames) between IDR frames", OFFSET(qsv.idr_interval), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, { "pic_timing_sei", "Insert picture timing SEI with pic_struct_syntax element", OFFSET(qsv.pic_timing_sei), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, VE }, -#if QSV_VERSION_ATLEAST(1,7) +#if QSV_HAVE_LA { "look_ahead", "Use VBR algorithm with look ahead", OFFSET(qsv.look_ahead), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, VE }, { "look_ahead_depth", "Depth of look ahead in number frames", OFFSET(qsv.look_ahead_depth), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 100, VE }, #endif