From: Fei Wang <fei.w.w...@intel.com> Once the '-mse' option enabled, MSE/PSNR of each frame will be shown in VERBOSE debug level log.
Signed-off-by: Fei Wang <fei.w.w...@intel.com> --- doc/encoders.texi | 4 + libavcodec/qsvenc.c | 162 +++++++++++++++++++++++++++++++++++---- libavcodec/qsvenc.h | 12 +++ libavcodec/qsvenc_av1.c | 3 + libavcodec/qsvenc_h264.c | 3 + libavcodec/qsvenc_hevc.c | 3 + 6 files changed, 173 insertions(+), 14 deletions(-) diff --git a/doc/encoders.texi b/doc/encoders.texi index 85be976a46..7d1373e0e0 100644 --- a/doc/encoders.texi +++ b/doc/encoders.texi @@ -3624,6 +3624,10 @@ ffmpeg -i input.mp4 -c:v h264_qsv -qsv_params "CodingOption1=1:CodingOption2=2" @end example This option allows fine-grained control over various encoder-specific settings provided by the QSV encoder. + +@item @var{mse} +Supported in h264_qsv, hevc_qsv, and av1_qsv on Windows. Output encoded +frame's quality(MSE/PSNR) information in VERBOSE log. @end table @subsection H264 options diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c index dfef15fa5a..cd89656f74 100644 --- a/libavcodec/qsvenc.c +++ b/libavcodec/qsvenc.c @@ -95,6 +95,9 @@ typedef struct QSVPacket { AVPacket pkt; mfxSyncPoint *sync; mfxBitstream *bs; + int bs_buf_num; + int frameinfo_buf_idx; + int mse_buf_idx; } QSVPacket; static const char *print_profile(enum AVCodecID codec_id, mfxU16 profile) @@ -206,6 +209,9 @@ static void dump_video_param(AVCodecContext *avctx, QSVEncContext *q, #if QSV_HAVE_AC mfxExtAlphaChannelEncCtrl *extalphachannel = NULL; #endif +#if QSV_HAVE_MSE + mfxExtQualityInfoMode *extmse = NULL; +#endif const char *tmp_str = NULL; @@ -228,6 +234,11 @@ static void dump_video_param(AVCodecContext *avctx, QSVEncContext *q, extalphachannel = (mfxExtAlphaChannelEncCtrl *)coding_opts[q->extaplhachannel_idx]; #endif +#if QSV_HAVE_MSE + if (q->extmse_idx > 0) + extmse = (mfxExtQualityInfoMode *)coding_opts[q->extmse_idx]; +#endif + av_log(avctx, AV_LOG_VERBOSE, "profile: %s; level: %"PRIu16"\n", print_profile(avctx->codec_id, info->CodecProfile), info->CodecLevel); @@ -425,6 +436,22 @@ static void dump_video_param(AVCodecContext *avctx, QSVEncContext *q, av_log(avctx, AV_LOG_VERBOSE, "\n"); } #endif + +#if QSV_HAVE_MSE + if (extmse) { + av_log(avctx, AV_LOG_VERBOSE, "MSE: "); + + if (extmse->QualityInfoMode == MFX_QUALITY_INFO_LEVEL_FRAME) + av_log(avctx, AV_LOG_VERBOSE, "ON"); + else if (extmse->QualityInfoMode == MFX_QUALITY_INFO_DISABLE) + av_log(avctx, AV_LOG_VERBOSE, "OFF"); + else + av_log(avctx, AV_LOG_VERBOSE, "unknown"); + + av_log(avctx, AV_LOG_VERBOSE, "\n"); + } +#endif + } static void dump_video_vp9_param(AVCodecContext *avctx, QSVEncContext *q, @@ -522,6 +549,9 @@ static void dump_video_av1_param(AVCodecContext *avctx, QSVEncContext *q, #if QSV_HAVE_EXT_AV1_SCC mfxExtAV1ScreenContentTools *scc = (mfxExtAV1ScreenContentTools*)coding_opts[4]; #endif +#if QSV_HAVE_MSE + mfxExtQualityInfoMode *mse = (mfxExtQualityInfoMode*)coding_opts[5]; +#endif av_log(avctx, AV_LOG_VERBOSE, "profile: %s; level: %"PRIu16"\n", print_profile(avctx->codec_id, info->CodecProfile), info->CodecLevel); @@ -602,6 +632,21 @@ static void dump_video_av1_param(AVCodecContext *avctx, QSVEncContext *q, print_threestate(scc->Palette), print_threestate(scc->IntraBlockCopy)); } #endif + +#if QSV_HAVE_MSE + if (mse) { + av_log(avctx, AV_LOG_VERBOSE, "MSE: "); + + if (mse->QualityInfoMode == MFX_QUALITY_INFO_LEVEL_FRAME) + av_log(avctx, AV_LOG_VERBOSE, "ON"); + else if (mse->QualityInfoMode == MFX_QUALITY_INFO_DISABLE) + av_log(avctx, AV_LOG_VERBOSE, "OFF"); + else + av_log(avctx, AV_LOG_VERBOSE, "unknown"); + + av_log(avctx, AV_LOG_VERBOSE, "\n"); + } +#endif } #endif @@ -1372,6 +1417,21 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) } #endif +#if QSV_HAVE_MSE + if (q->mse) { + if (QSV_RUNTIME_VERSION_ATLEAST(q->ver, 2, 13)) { + q->extmseparam.Header.BufferId = MFX_EXTBUFF_ENCODED_QUALITY_INFO_MODE; + q->extmseparam.Header.BufferSz = sizeof(q->extmseparam); + q->extmseparam.QualityInfoMode = MFX_QUALITY_INFO_LEVEL_FRAME; + q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->extmseparam; + } else { + av_log(avctx, AV_LOG_ERROR, + "This version of runtime doesn't support Mean Squared Error\n"); + return AVERROR_UNKNOWN; + } + } +#endif + if (!check_enc_param(avctx,q)) { av_log(avctx, AV_LOG_ERROR, "some encoding parameters are not supported by the QSV " @@ -1486,6 +1546,12 @@ static int qsv_retrieve_enc_av1_params(AVCodecContext *avctx, QSVEncContext *q) }; #endif +#if QSV_HAVE_MSE + mfxExtQualityInfoMode mse_buf = { + .Header.BufferId = MFX_EXTBUFF_ENCODED_QUALITY_INFO_MODE, + .Header.BufferSz = sizeof(mse_buf), + }; +#endif mfxExtBuffer *ext_buffers[] = { (mfxExtBuffer*)&av1_extend_tile_buf, (mfxExtBuffer*)&av1_bs_param, @@ -1493,6 +1559,9 @@ static int qsv_retrieve_enc_av1_params(AVCodecContext *avctx, QSVEncContext *q) (mfxExtBuffer*)&co3, #if QSV_HAVE_EXT_AV1_SCC (mfxExtBuffer*)&scc_buf, +#endif +#if QSV_HAVE_MSE + (mfxExtBuffer*)&mse_buf, #endif }; @@ -1570,7 +1639,14 @@ static int qsv_retrieve_enc_params(AVCodecContext *avctx, QSVEncContext *q) }; #endif - mfxExtBuffer *ext_buffers[6 + QSV_HAVE_HE + QSV_HAVE_AC]; +#if QSV_HAVE_MSE + mfxExtQualityInfoMode mse_buf = { + .Header.BufferId = MFX_EXTBUFF_ENCODED_QUALITY_INFO_MODE, + .Header.BufferSz = sizeof(mse_buf), + }; +#endif + + mfxExtBuffer *ext_buffers[6 + QSV_HAVE_HE + QSV_HAVE_AC + QSV_HAVE_MSE]; int need_pps = avctx->codec_id != AV_CODEC_ID_MPEG2VIDEO; int ret, ext_buf_num = 0, extradata_offset = 0; @@ -1613,6 +1689,13 @@ static int qsv_retrieve_enc_params(AVCodecContext *avctx, QSVEncContext *q) } #endif +#if QSV_HAVE_MSE + if (q->mse && QSV_RUNTIME_VERSION_ATLEAST(q->ver, 2, 13)) { + q->extmse_idx = ext_buf_num; + ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&mse_buf; + } +#endif + q->param.ExtParam = ext_buffers; q->param.NumExtParam = ext_buf_num; @@ -2228,7 +2311,8 @@ static int submit_frame(QSVEncContext *q, const AVFrame *frame, return ret; } } - qf->surface.Data.TimeStamp = av_rescale_q(frame->pts, q->avctx->time_base, (AVRational){1, 90000}); + qf->surface.Data.TimeStamp = av_rescale_q(frame->pts, q->avctx->time_base, (AVRational){1, 90000}); + qf->surface.Data.FrameOrder = MFX_FRAMEORDER_UNKNOWN; *new_frame = qf; @@ -2578,11 +2662,15 @@ static int encode_frame(AVCodecContext *avctx, QSVEncContext *q, { QSVPacket pkt = { { 0 } }; mfxExtAVCEncodedFrameInfo *enc_info = NULL; +#if QSV_HAVE_MSE + mfxExtQualityInfoOutput *mse = NULL; +#endif mfxExtBuffer **enc_buf = NULL; mfxFrameSurface1 *surf = NULL; QSVFrame *qsv_frame = NULL; mfxEncodeCtrl* enc_ctrl = NULL; + mfxExtBuffer *bs_buf[2]; int ret; if (frame) { @@ -2622,13 +2710,30 @@ static int encode_frame(AVCodecContext *avctx, QSVEncContext *q, enc_info->Header.BufferId = MFX_EXTBUFF_ENCODED_FRAME_INFO; enc_info->Header.BufferSz = sizeof (*enc_info); - pkt.bs->NumExtParam = 1; - enc_buf = av_mallocz(sizeof(mfxExtBuffer *)); - if (!enc_buf) + pkt.frameinfo_buf_idx = pkt.bs_buf_num; + bs_buf[pkt.bs_buf_num++] = (mfxExtBuffer *)enc_info; + } + +#if QSV_HAVE_MSE + if (q->mse) { + mse = av_mallocz(sizeof(*mse)); + if (!mse) goto nomem; - enc_buf[0] = (mfxExtBuffer *)enc_info; + mse->Header.BufferId = MFX_EXTBUFF_ENCODED_QUALITY_INFO_OUTPUT; + mse->Header.BufferSz = sizeof(*mse); + pkt.mse_buf_idx = pkt.bs_buf_num; + bs_buf[pkt.bs_buf_num++] = (mfxExtBuffer *)mse; + } +#endif + + if (pkt.bs_buf_num) { + enc_buf = av_mallocz(sizeof(mfxExtBuffer *) * pkt.bs_buf_num); + if (!enc_buf) + goto nomem; + memcpy(enc_buf, bs_buf, pkt.bs_buf_num * sizeof(mfxExtBuffer *)); pkt.bs->ExtParam = enc_buf; + pkt.bs->NumExtParam = pkt.bs_buf_num; } if (q->set_encode_ctrl_cb && enc_ctrl) { @@ -2683,8 +2788,13 @@ free: av_freep(&pkt.bs); if (avctx->codec_id == AV_CODEC_ID_H264) { av_freep(&enc_info); - av_freep(&enc_buf); } +#if QSV_HAVE_MSE + if (q->mse) + av_freep(&mse); +#endif + if (pkt.bs_buf_num) + av_freep(&enc_buf); } return ret; @@ -2770,7 +2880,7 @@ int ff_qsv_encode(AVCodecContext *avctx, QSVEncContext *q, (!frame && av_fifo_can_read(q->async_fifo))) { QSVPacket qpkt; mfxExtAVCEncodedFrameInfo *enc_info; - mfxExtBuffer **enc_buf; + mfxExtBuffer *enc_buf; enum AVPictureType pict_type; av_fifo_read(q->async_fifo, &qpkt, 1); @@ -2803,13 +2913,29 @@ int ff_qsv_encode(AVCodecContext *avctx, QSVEncContext *q, } if (avctx->codec_id == AV_CODEC_ID_H264) { - enc_buf = qpkt.bs->ExtParam; - enc_info = (mfxExtAVCEncodedFrameInfo *)(*enc_buf); + enc_buf = qpkt.bs->ExtParam[qpkt.frameinfo_buf_idx]; + enc_info = (mfxExtAVCEncodedFrameInfo *)enc_buf; ff_side_data_set_encoder_stats(&qpkt.pkt, enc_info->QP * FF_QP2LAMBDA, NULL, 0, pict_type); av_freep(&enc_info); - av_freep(&enc_buf); } +#if QSV_HAVE_MSE + if (q->mse) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->pix_fmt); + mfxExtQualityInfoOutput *mse; + enc_buf = qpkt.bs->ExtParam[qpkt.mse_buf_idx]; + mse = (mfxExtQualityInfoOutput *)enc_buf; + av_log(avctx, AV_LOG_VERBOSE, "Frame Display order:%d, MSE Y/U/V: %0.2f/%0.2f/%0.2f, " + "PSNR Y/U/V: %0.2f/%0.2f/%0.2f\n", + mse->FrameOrder, mse->MSE[0] / 256.0, mse->MSE[1] / 256.0, mse->MSE[2] / 256.0, + 10.0 * log10(pow(((1 << desc->comp[0].depth) -1), 2) / (mse->MSE[0] / 256.0)), + 10.0 * log10(pow(((1 << desc->comp[1].depth) -1), 2) / (mse->MSE[1] / 256.0)), + 10.0 * log10(pow(((1 << desc->comp[2].depth) -1), 2) / (mse->MSE[2] / 256.0))); + av_freep(&mse); + } +#endif + if (qpkt.bs_buf_num) + av_freep(&qpkt.bs->ExtParam); av_freep(&qpkt.bs); av_freep(&qpkt.sync); @@ -2847,11 +2973,19 @@ int ff_qsv_enc_close(AVCodecContext *avctx, QSVEncContext *q) QSVPacket pkt; while (av_fifo_read(q->async_fifo, &pkt, 1) >= 0) { if (avctx->codec_id == AV_CODEC_ID_H264) { - mfxExtBuffer **enc_buf = pkt.bs->ExtParam; - mfxExtAVCEncodedFrameInfo *enc_info = (mfxExtAVCEncodedFrameInfo *)(*enc_buf); + mfxExtBuffer *enc_buf = pkt.bs->ExtParam[pkt.frameinfo_buf_idx]; + mfxExtAVCEncodedFrameInfo *enc_info = (mfxExtAVCEncodedFrameInfo *)enc_buf; av_freep(&enc_info); - av_freep(&enc_buf); } +#if QSV_HAVE_MSE + if (q->mse) { + mfxExtBuffer *enc_buf = pkt.bs->ExtParam[pkt.mse_buf_idx]; + mfxExtQualityInfoOutput *mse = (mfxExtQualityInfoOutput *)enc_buf; + av_freep(&mse); + } +#endif + if (pkt.bs_buf_num) + av_freep(&pkt.bs->ExtParam); av_freep(&pkt.sync); av_freep(&pkt.bs); av_packet_unref(&pkt.pkt); diff --git a/libavcodec/qsvenc.h b/libavcodec/qsvenc.h index 1e1474d37c..5aa0aae790 100644 --- a/libavcodec/qsvenc.h +++ b/libavcodec/qsvenc.h @@ -46,12 +46,14 @@ #define QSV_HAVE_MF 0 #define QSV_HAVE_HE QSV_VERSION_ATLEAST(2, 4) #define QSV_HAVE_AC QSV_VERSION_ATLEAST(2, 13) +#define QSV_HAVE_MSE QSV_VERSION_ATLEAST(2, 13) #else #define QSV_HAVE_AVBR 0 #define QSV_HAVE_VCM 0 #define QSV_HAVE_MF !QSV_ONEVPL #define QSV_HAVE_HE 0 #define QSV_HAVE_AC 0 +#define QSV_HAVE_MSE 0 #endif #define QSV_COMMON_OPTS \ @@ -151,6 +153,11 @@ { "brc_only", "skip_frame metadata indicates the number of missed frames before the current frame", \ 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SKIPFRAME_BRC_ONLY }, .flags = VE, .unit = "skip_frame" }, +#if QSV_HAVE_MSE +#define QSV_MSE_OPTIONS \ +{ "mse", "Enable output MSE(Mean Squared Error) of each frame", OFFSET(qsv.mse), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, +#endif + extern const AVCodecHWConfigInternal *const ff_qsv_enc_hw_configs[]; typedef int SetEncodeCtrlCB (AVCodecContext *avctx, @@ -197,6 +204,9 @@ typedef struct QSVEncContext { #if QSV_HAVE_EXT_AV1_SCC mfxExtAV1ScreenContentTools extsccparam; #endif +#if QSV_HAVE_MSE + mfxExtQualityInfoMode extmseparam; +#endif mfxExtVideoSignalInfo extvsi; @@ -282,6 +292,7 @@ typedef struct QSVEncContext { int extaplhachannel_idx; int exthevctiles_idx; int exthypermodeparam_idx; + int extmse_idx; int vp9_idx; int max_qp_i; @@ -333,6 +344,7 @@ typedef struct QSVEncContext { int alpha_encode; int palette_mode; int intrabc; + int mse; } QSVEncContext; int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q); diff --git a/libavcodec/qsvenc_av1.c b/libavcodec/qsvenc_av1.c index 4f035f3d83..ee0617714a 100644 --- a/libavcodec/qsvenc_av1.c +++ b/libavcodec/qsvenc_av1.c @@ -183,6 +183,9 @@ static const AVOption options[] = { QSV_OPTION_EXTBRC QSV_OPTION_LOW_DELAY_BRC QSV_OPTION_MAX_FRAME_SIZE +#if QSV_HAVE_MSE + QSV_MSE_OPTIONS +#endif { "profile", NULL, OFFSET(qsv.profile), AV_OPT_TYPE_INT, { .i64 = MFX_PROFILE_UNKNOWN }, 0, INT_MAX, VE, .unit = "profile" }, { "unknown" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_UNKNOWN }, INT_MIN, INT_MAX, VE, .unit = "profile" }, { "main" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_AV1_MAIN }, INT_MIN, INT_MAX, VE, .unit = "profile" }, diff --git a/libavcodec/qsvenc_h264.c b/libavcodec/qsvenc_h264.c index 304d1e7dcb..0fc0c852ac 100644 --- a/libavcodec/qsvenc_h264.c +++ b/libavcodec/qsvenc_h264.c @@ -120,6 +120,9 @@ static const AVOption options[] = { #if QSV_HAVE_HE QSV_HE_OPTIONS #endif +#if QSV_HAVE_MSE + QSV_MSE_OPTIONS +#endif { "cavlc", "Enable CAVLC", OFFSET(qsv.cavlc), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, #if QSV_HAVE_VCM diff --git a/libavcodec/qsvenc_hevc.c b/libavcodec/qsvenc_hevc.c index 6ea9c4cdcb..8197e0958e 100644 --- a/libavcodec/qsvenc_hevc.c +++ b/libavcodec/qsvenc_hevc.c @@ -323,6 +323,9 @@ static const AVOption options[] = { #if QSV_HAVE_HE QSV_HE_OPTIONS #endif +#if QSV_HAVE_MSE + QSV_MSE_OPTIONS +#endif { "idr_interval", "Distance (in I-frames) between IDR frames", OFFSET(qsv.idr_interval), AV_OPT_TYPE_INT, { .i64 = 0 }, -1, INT_MAX, VE, .unit = "idr_interval" }, { "begin_only", "Output an IDR-frame only at the beginning of the stream", 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, 0, 0, VE, .unit = "idr_interval" }, -- 2.34.1 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".