[FFmpeg-devel] [PATCH 3/5] lavc/qsvenc: Support calculate encoded frame quality(MSE/PSNR) when encoding
fei.w.wang at intel.com
fei.w.wang at intel.com
Mon Sep 23 10:10:06 EEST 2024
From: Fei Wang <fei.w.wang at 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.wang at 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.
+
+ at 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
More information about the ffmpeg-devel
mailing list