Fixed problems with QuickSync (QSV) interlaced video encoding

Message ID 3f83d7ea-b7c2-581a-b2d9-2cde53f1c4ec@aracnet.com
State New
Headers show

Commit Message

Aaron Levinson April 14, 2017, 9:51 p.m.
I submitted the following patch to ffmpeg-devel, and I was asked by jkqxz on IRC to submit it to libav-devel, which has more active QuickSync (QSV) development than on ffmpeg-devel.  The ffmpeg patch, as-is, applied just fine to the libav source base, but I created a new patch for libav regardless.

Thanks,
Aaron Levinson

---------------------------------------------------------------------

From 5a0dc69eafdf5fac35c0e268deada6e6111b87fd Mon Sep 17 00:00:00 2001
From: Aaron Levinson <alevinsn@aracnet.com>
Date: Fri, 14 Apr 2017 14:31:44 -0700
Subject: [PATCH] Fixed problems with QuickSync (QSV) interlaced video encoding

Purpose: Fixed problems with QuickSync (QSV) interlaced video encoding
that were introduced in revision 1f26a23 on Oct. 31, 2016 (qsv: Merge
libav implementation, at
https://github.com/FFmpeg/FFmpeg/commit/1f26a231bb065276cd80ce02957c759f3197edfa#diff-7d84a34d58597bb7aa4b8239dca1f9f8).
As a result of the qsv libav merge, when attempting to encode
interlaced video, it doesn't work and instead results in a bunch of
incompatible video parameter errors.  In the case of the libav code,
it looks like interlaced video encoding has always been an issue based
on the history of qsvenc.c.

Comments:

-- qsvenc.c / .h:
a) Added code back in to set PicStruct appropriately based on whether
   or not interlaced or progressive video is being encoded.  Also
   reintroduced the related code to set the height alignment.  The
   height alignment code was also enhanced slightly (compared to the
   version in 3.2.4) to properly handle progressive video when the
   HEVC encoder is used.  The elimination of this code is the main
   reason why interlaced video encoding stopped working.
b) Reintroduced code to call MFXVideoENCODE_Query() after calling
   init_video_param().  This isn't strictly required to fix the
   interlaced video encoding issue, but it represents a generally good
   practice to make sure that one is working with the right parameter
   values.
---
 libavcodec/qsvenc.c | 33 +++++++++++++++++++++++++++++----
 libavcodec/qsvenc.h |  1 +
 2 files changed, 30 insertions(+), 4 deletions(-)

Patch

diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c
index bd8c243..7b7d8b9 100644
--- a/libavcodec/qsvenc.c
+++ b/libavcodec/qsvenc.c
@@ -366,6 +366,9 @@  static int init_video_param(AVCodecContext *avctx, QSVEncContext *q)
         return AVERROR_BUG;
     q->param.mfx.CodecId = ret;
 
+    // TODO:  detect version of MFX--if the minor version is greater than
+    // or equal to 19, then can use the same alignment settings as H.264
+    // for HEVC
     q->width_align = avctx->codec_id == AV_CODEC_ID_HEVC ? 32 : 16;
 
     if (avctx->level > 0)
@@ -389,20 +392,34 @@  static int init_video_param(AVCodecContext *avctx, QSVEncContext *q)
 
     ff_qsv_map_pixfmt(sw_format, &q->param.mfx.FrameInfo.FourCC);
 
-    q->param.mfx.FrameInfo.Width          = FFALIGN(avctx->width, q->width_align);
-    q->param.mfx.FrameInfo.Height         = FFALIGN(avctx->height, 32);
     q->param.mfx.FrameInfo.CropX          = 0;
     q->param.mfx.FrameInfo.CropY          = 0;
     q->param.mfx.FrameInfo.CropW          = avctx->width;
     q->param.mfx.FrameInfo.CropH          = avctx->height;
     q->param.mfx.FrameInfo.AspectRatioW   = avctx->sample_aspect_ratio.num;
     q->param.mfx.FrameInfo.AspectRatioH   = avctx->sample_aspect_ratio.den;
-    q->param.mfx.FrameInfo.PicStruct      = MFX_PICSTRUCT_PROGRESSIVE;
     q->param.mfx.FrameInfo.ChromaFormat   = MFX_CHROMAFORMAT_YUV420;
     q->param.mfx.FrameInfo.BitDepthLuma   = desc->comp[0].depth;
     q->param.mfx.FrameInfo.BitDepthChroma = desc->comp[0].depth;
     q->param.mfx.FrameInfo.Shift          = desc->comp[0].depth > 8;
 
+    q->param.mfx.FrameInfo.Width          = FFALIGN(avctx->width, q->width_align);
+    if (avctx->flags & AV_CODEC_FLAG_INTERLACED_DCT) {
+        // it is important that PicStruct be setup correctly from the
+        // start--otherwise, encoding doesn't work and results in a bunch
+        // of incompatible video parameter errors
+        q->param.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_FIELD_TFF;
+        // height alignment always must be 32 for interlaced video
+        q->height_align = 32;
+    } else {
+        q->param.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
+        // for progressive video, the height should be aligned to 16 for
+        // H.264.  For HEVC, depending on the version of MFX, it should be
+        // either 32 or 16.  The lower number is better if possible.
+        q->height_align = avctx->codec_id == AV_CODEC_ID_HEVC ? 32 : 16;
+    }
+    q->param.mfx.FrameInfo.Height = FFALIGN(avctx->height, q->height_align);
+
     if (avctx->hw_frames_ctx) {
         AVHWFramesContext *frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data;
         AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx;
@@ -757,10 +774,18 @@  int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q)
     if (ret < 0)
         return ret;
 
+    ret = MFXVideoENCODE_Query(q->session, &q->param, &q->param);
+    if (ret == MFX_WRN_PARTIAL_ACCELERATION) {
+        av_log(avctx, AV_LOG_WARNING, "Encoder will work with partial HW acceleration\n");
+    } else if (ret < 0) {
+        return ff_qsv_print_error(avctx, ret,
+                                  "Error querying encoder params");
+    }
+
     ret = MFXVideoENCODE_QueryIOSurf(q->session, &q->param, &q->req);
     if (ret < 0)
         return ff_qsv_print_error(avctx, ret,
-                                  "Error querying the encoding parameters");
+                                  "Error querying (IOSurf) the encoding parameters");
 
     if (opaque_alloc) {
         ret = qsv_init_opaque_alloc(avctx, q);
diff --git a/libavcodec/qsvenc.h b/libavcodec/qsvenc.h
index 13e4c47..a639904 100644
--- a/libavcodec/qsvenc.h
+++ b/libavcodec/qsvenc.h
@@ -79,6 +79,7 @@  typedef struct QSVEncContext {
 
     int packet_size;
     int width_align;
+    int height_align;
 
     mfxVideoParam param;
     mfxFrameAllocRequest req;