]> granicus.if.org Git - handbrake/commitdiff
QSV: interlaced encoding support.
authorRodeo <tdskywalker@gmail.com>
Sat, 17 Aug 2013 21:23:52 +0000 (21:23 +0000)
committerRodeo <tdskywalker@gmail.com>
Sat, 17 Aug 2013 21:23:52 +0000 (21:23 +0000)
Includes a facility which lets encode request specific aligned width and height (alignment may be higher or lower than that of the input).

git-svn-id: svn://svn.handbrake.fr/HandBrake/branches/qsv@5708 b64f7644-9d1e-0410-96f1-a4d463321fa5

libhb/common.c
libhb/common.h
libhb/enc_qsv.c
libhb/qsv_common.c
libhb/qsv_filter.c

index 77f7ef0d73d3f95450095e08b71dcad1d3b44484..9d44bc01b0d615ea86ea892bc185e00403a38269 100644 (file)
@@ -2831,8 +2831,9 @@ static void job_setup( hb_job_t * job, hb_title_t * title )
     job->metadata = hb_metadata_copy( title->metadata );
 
 #ifdef USE_QSV
-    job->qsv_decode      = title->qsv_decode_support;
-    job->qsv_async_depth = AV_QSV_ASYNC_DEPTH_DEFAULT;
+    job->qsv_enc_info.is_init_done = 0;
+    job->qsv_decode                = title->qsv_decode_support;
+    job->qsv_async_depth           = AV_QSV_ASYNC_DEPTH_DEFAULT;
 #endif
 }
 
index bf7495e4e002cd58541cbd268edd9c0d2b5afa36..ecb176b20a8c1cebd4c1d4ee4c387cae9d155443 100644 (file)
@@ -516,6 +516,16 @@ struct hb_job_s
     av_qsv_context   *qsv;
     int               qsv_decode;
     int               qsv_async_depth;
+    // shared encoding parameters
+    // initialized by the QSV encoder, then used upstream (e.g. by filters) to
+    // configure their output so that it corresponds to what the encoder expects
+    struct
+    {
+        int pic_struct;
+        int align_width;
+        int align_height;
+        int is_init_done;
+    } qsv_enc_info;
 #endif
 
 #ifdef __LIBHB__
index 989c38ca3579eb153bdbaa5d9f0127ec5489291d..284f8f3a88e42ff6b929672ae9070360643af478 100644 (file)
@@ -484,6 +484,19 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
                         job->anamorphic.par_width,
                         job->anamorphic.par_height, UINT16_MAX);
 
+    // some encoding parameters are used by filters to configure their output
+    if (pv->param.videoParam->mfx.FrameInfo.PicStruct != MFX_PICSTRUCT_PROGRESSIVE)
+    {
+        job->qsv_enc_info.align_height = AV_QSV_ALIGN32(job->height);
+    }
+    else
+    {
+        job->qsv_enc_info.align_height = AV_QSV_ALIGN16(job->height);
+    }
+    job->qsv_enc_info.align_width  = AV_QSV_ALIGN16(job->width);
+    job->qsv_enc_info.pic_struct   = pv->param.videoParam->mfx.FrameInfo.PicStruct;
+    job->qsv_enc_info.is_init_done = 1;
+
     // encode to H.264 and set FrameInfo
     pv->param.videoParam->mfx.CodecId                 = MFX_CODEC_AVC;
     pv->param.videoParam->mfx.CodecLevel              = MFX_LEVEL_UNKNOWN;
@@ -498,23 +511,9 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
     pv->param.videoParam->mfx.FrameInfo.CropY         = 0;
     pv->param.videoParam->mfx.FrameInfo.CropW         = job->width;
     pv->param.videoParam->mfx.FrameInfo.CropH         = job->height;
-    pv->param.videoParam->mfx.FrameInfo.Width         = AV_QSV_ALIGN16(job->width);
-    pv->param.videoParam->mfx.FrameInfo.Height        = AV_QSV_ALIGN16(job->height);
-    pv->param.videoParam->mfx.FrameInfo.PicStruct     = MFX_PICSTRUCT_PROGRESSIVE;
-    /*
-     * Note: interlaced encoding with the QSV H.264 encoder is explicitly
-     * unsupported for now.
-     *
-     * Unlike libx264, QSV requires that the input buffers be already padded to
-     * the coding width and height (the latter has to divide cleanly by 32 in
-     * the interlaced case). This would require:
-     *
-     * - detecting that interlaced encoding will be used early
-     * - padding VPP buffers so that the output height divides cleanly by 32
-     * - force-enabling VPP when there is no crop & scale but interlaced is on
-     *
-     * Also note that MFX_RATECONTROL_LA is progressive-only.
-     */
+    pv->param.videoParam->mfx.FrameInfo.PicStruct     = job->qsv_enc_info.pic_struct;
+    pv->param.videoParam->mfx.FrameInfo.Width         = job->qsv_enc_info.align_width;
+    pv->param.videoParam->mfx.FrameInfo.Height        = job->qsv_enc_info.align_height;
 
     // set H.264 profile and level
     if (job->h264_profile != NULL && job->h264_profile[0] != '\0' &&
@@ -564,6 +563,27 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
         }
     }
 
+    // interlaced encoding is not always possible
+    if (pv->param.videoParam->mfx.FrameInfo.PicStruct != MFX_PICSTRUCT_PROGRESSIVE)
+    {
+        if (pv->param.videoParam->mfx.CodecProfile == MFX_PROFILE_AVC_CONSTRAINED_BASELINE ||
+            pv->param.videoParam->mfx.CodecProfile == MFX_PROFILE_AVC_BASELINE             ||
+            pv->param.videoParam->mfx.CodecProfile == MFX_PROFILE_AVC_PROGRESSIVE_HIGH)
+        {
+            hb_error("encqsvInit: profile %s doesn't support interlaced encoding",
+                     qsv_h264_profile_xlat(pv->param.videoParam->mfx.CodecProfile));
+            return -1;
+        }
+        if ((pv->param.videoParam->mfx.CodecLevel >= MFX_LEVEL_AVC_1b &&
+             pv->param.videoParam->mfx.CodecLevel <= MFX_LEVEL_AVC_2) ||
+            (pv->param.videoParam->mfx.CodecLevel >= MFX_LEVEL_AVC_42))
+        {
+            hb_error("encqsvInit: level %s doesn't support interlaced encoding",
+                     qsv_h264_level_xlat(pv->param.videoParam->mfx.CodecLevel));
+            return -1;
+        }
+    }
+
     // set rate control paremeters
     if (job->vquality >= 0)
     {
@@ -575,31 +595,37 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
     }
     else if (job->vbitrate > 0)
     {
-        if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_LOOKAHEAD)
+        // sanitize lookahead
+        if (!(hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_LOOKAHEAD))
+        {
+            // lookahead not supported
+            pv->param.rc.lookahead = 0;
+        }
+        else if (pv->param.rc.lookahead > 0 &&
+                 pv->param.videoParam->mfx.FrameInfo.PicStruct != MFX_PICSTRUCT_PROGRESSIVE)
         {
-            if (pv->param.rc.lookahead < 0)
+            // user force-enabled lookahead but we can't use it
+            hb_log("encqsvInit: MFX_RATECONTROL_LA not used (LookAhead is progressive-only)");
+            pv->param.rc.lookahead = 0;
+        }
+        else if (pv->param.rc.lookahead < 0)
+        {
+            if (pv->param.rc.vbv_max_bitrate > 0 ||
+                pv->param.videoParam->mfx.FrameInfo.PicStruct != MFX_PICSTRUCT_PROGRESSIVE)
             {
-                if (pv->param.rc.vbv_max_bitrate > 0)
-                {
-                    // lookahead RC doesn't support VBV
-                    pv->param.rc.lookahead = 0;
-                }
-                else
-                {
-                    // set automatically based on target usage
-                    pv->param.rc.lookahead = (pv->param.videoParam->mfx.TargetUsage <= MFX_TARGETUSAGE_2);
-                }
+                // lookahead doesn't support VBV or interlaced encoding
+                pv->param.rc.lookahead = 0;
             }
             else
             {
-                // user force-enabled or force-disabled lookahead RC
-                pv->param.rc.lookahead = !!pv->param.rc.lookahead;
+                // set automatically based on target usage
+                pv->param.rc.lookahead = (pv->param.videoParam->mfx.TargetUsage <= MFX_TARGETUSAGE_2);
             }
         }
         else
         {
-            // lookahead RC not supported
-            pv->param.rc.lookahead = 0;
+            // user force-enabled or force-disabled lookahead
+            pv->param.rc.lookahead = !!pv->param.rc.lookahead;
         }
         if (pv->param.rc.lookahead)
         {
@@ -629,7 +655,7 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
                 else
                 {
                     pv->param.videoParam->mfx.InitialDelayInKB = (pv->param.rc.vbv_buffer_size *
-                                                                 pv->param.rc.vbv_buffer_init / 8);
+                                                                  pv->param.rc.vbv_buffer_init / 8);
                 }
                 pv->param.videoParam->mfx.BufferSizeInKB = (pv->param.rc.vbv_buffer_size / 8);
             }
@@ -651,7 +677,7 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
                 else
                 {
                     pv->param.videoParam->mfx.InitialDelayInKB = (pv->param.rc.vbv_buffer_size *
-                                                                 pv->param.rc.vbv_buffer_init / 8);
+                                                                  pv->param.rc.vbv_buffer_init / 8);
                 }
                 pv->param.videoParam->mfx.BufferSizeInKB = (pv->param.rc.vbv_buffer_size / 8);
             }
@@ -805,6 +831,22 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
                 return -1;
         }
     }
+    switch (videoParam.mfx.FrameInfo.PicStruct)
+    {
+        case MFX_PICSTRUCT_PROGRESSIVE:
+            hb_log("encqsvInit: PicStruct progressive");
+            break;
+        case MFX_PICSTRUCT_FIELD_TFF:
+            hb_log("encqsvInit: PicStruct top field first");
+            break;
+        case MFX_PICSTRUCT_FIELD_BFF:
+            hb_log("encqsvInit: PicStruct bottom field first");
+            break;
+        default:
+            hb_error("encqsvInit: invalid PicStruct value 0x%"PRIx16"",
+                     videoParam.mfx.FrameInfo.PicStruct);
+            return -1;
+    }
     const char *cavlc, *rdopt;
     switch (option1->CAVLC)
     {
@@ -1147,6 +1189,17 @@ int encqsvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
                 // don't let 'work_loop' put a chapter mark on the wrong buffer
                 in->s.new_chap = 0;
             }
+
+            /*
+             * If interlaced encoding is requested during encoder initialization,
+             * but the input mfxFrameSurface1 is flagged as progressive here,
+             * the output bitstream will be progressive (according to MediaInfo).
+             *
+             * Assume the user knows what he's doing (say he is e.g. encoding a
+             * progressive-flagged source using interlaced compression - he may
+             * well have a good reason to do so; mis-flagged sources do exist).
+             */
+            work_surface->Info.PicStruct = pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.PicStruct;
         }
         else{
             work_surface = NULL;
index 26b6be132264a1f02c49696f2b0b8c603f96170e..581809ba5a047d942c37e692f70c4b279506e402 100644 (file)
@@ -601,6 +601,9 @@ int hb_qsv_param_parse(hb_qsv_param_t *param,
     {
         switch (vcodec)
         {
+            case HB_VCODEC_QSV_H264:
+                ivalue = hb_qsv_atobool(value, &error);
+                break;
             default:
                 return HB_QSV_PARAM_UNSUPPORTED;
         }
@@ -615,6 +618,9 @@ int hb_qsv_param_parse(hb_qsv_param_t *param,
     {
         switch (vcodec)
         {
+            case HB_VCODEC_QSV_H264:
+                ivalue = hb_qsv_atobool(value, &error);
+                break;
             default:
                 return HB_QSV_PARAM_UNSUPPORTED;
         }
index d3533fd0ea1f0cce1468482b59db5a61ce8d23c6..3de9d6254324eba2e01c41d0fd0d1da55c2c9250 100644 (file)
@@ -107,6 +107,12 @@ static int filter_init( av_qsv_context* qsv, hb_filter_private_t * pv ){
 
     if(!qsv->dec_space || !qsv->dec_space->is_init_done) return 2;
 
+    // we need to know final output settings before we can properly configure
+    if (!pv->job->qsv_enc_info.is_init_done)
+    {
+        return 2;
+    }
+
     av_qsv_add_context_usage(qsv,HAVE_THREADS);
 
     // see params needed like at mediasdk-man.pdf:"Appendix A: Configuration Parameter Constraints"
@@ -115,6 +121,23 @@ static int filter_init( av_qsv_context* qsv, hb_filter_private_t * pv ){
         av_qsv_space *qsv_vpp = pv->vpp_space;
         AV_QSV_ZERO_MEMORY(qsv_vpp->m_mfxVideoParam);
 
+        if (pv->deinterlace)
+        {
+            /*
+             * Input may be progressive, interlaced or even mixed, so init with
+             * MFX_PICSTRUCT_UNKNOWN and use per-frame field order information
+             * (mfxFrameSurface1.Info.PicStruct)
+             */
+            qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct  = MFX_PICSTRUCT_UNKNOWN;
+            qsv_vpp->m_mfxVideoParam.vpp.Out.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
+        }
+        else
+        {
+            /* Same PicStruct in/out: no filtering */
+            qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct  = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct;
+            qsv_vpp->m_mfxVideoParam.vpp.Out.PicStruct = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct;
+        }
+
         // FrameRate is important for VPP to start with
         if( qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN == 0 &&
             qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD == 0 ){
@@ -126,16 +149,14 @@ static int filter_init( av_qsv_context* qsv, hb_filter_private_t * pv ){
         qsv_vpp->m_mfxVideoParam.vpp.In.ChromaFormat    = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.ChromaFormat;
         qsv_vpp->m_mfxVideoParam.vpp.In.CropX           = pv->crop[2];
         qsv_vpp->m_mfxVideoParam.vpp.In.CropY           = pv->crop[0];
-        qsv_vpp->m_mfxVideoParam.vpp.In.CropW           = pv->width_in - pv->crop[3] - pv->crop[2];
+        qsv_vpp->m_mfxVideoParam.vpp.In.CropW           = pv-> width_in - pv->crop[3] - pv->crop[2];
         qsv_vpp->m_mfxVideoParam.vpp.In.CropH           = pv->height_in - pv->crop[1] - pv->crop[0];
-        qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct       = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct;
         qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtN   = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN;
         qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtD   = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD;
         qsv_vpp->m_mfxVideoParam.vpp.In.AspectRatioW    = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioW;
         qsv_vpp->m_mfxVideoParam.vpp.In.AspectRatioH    = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioH;
-        qsv_vpp->m_mfxVideoParam.vpp.In.Width           = AV_QSV_ALIGN16(pv->width_in);
-        qsv_vpp->m_mfxVideoParam.vpp.In.Height          = (MFX_PICSTRUCT_PROGRESSIVE == qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct)?
-                                                            AV_QSV_ALIGN16(pv->height_in) : AV_QSV_ALIGN32(pv->height_in);
+        qsv_vpp->m_mfxVideoParam.vpp.In.Width           = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.Width;
+        qsv_vpp->m_mfxVideoParam.vpp.In.Height          = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.Height;
 
         qsv_vpp->m_mfxVideoParam.vpp.Out.FourCC          = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FourCC;
         qsv_vpp->m_mfxVideoParam.vpp.Out.ChromaFormat    = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.ChromaFormat;
@@ -143,14 +164,12 @@ static int filter_init( av_qsv_context* qsv, hb_filter_private_t * pv ){
         qsv_vpp->m_mfxVideoParam.vpp.Out.CropY           = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropY;
         qsv_vpp->m_mfxVideoParam.vpp.Out.CropW           = pv->width_out;
         qsv_vpp->m_mfxVideoParam.vpp.Out.CropH           = pv->height_out;
-        qsv_vpp->m_mfxVideoParam.vpp.Out.PicStruct       = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct;
         qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtN   = pv->job->vrate;
         qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtD   = pv->job->vrate_base;
         qsv_vpp->m_mfxVideoParam.vpp.Out.AspectRatioW    = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioW;
         qsv_vpp->m_mfxVideoParam.vpp.Out.AspectRatioH    = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioH;
-        qsv_vpp->m_mfxVideoParam.vpp.Out.Width           = AV_QSV_ALIGN16(pv->width_out);
-        qsv_vpp->m_mfxVideoParam.vpp.Out.Height          = (MFX_PICSTRUCT_PROGRESSIVE == qsv_vpp->m_mfxVideoParam.vpp.Out.PicStruct)?
-                                                            AV_QSV_ALIGN16(pv->height_out) : AV_QSV_ALIGN32(pv->height_out);
+        qsv_vpp->m_mfxVideoParam.vpp.Out.Width           = pv->job->qsv_enc_info.align_width;
+        qsv_vpp->m_mfxVideoParam.vpp.Out.Height          = pv->job->qsv_enc_info.align_height;
 
         qsv_vpp->m_mfxVideoParam.IOPattern = MFX_IOPATTERN_IN_OPAQUE_MEMORY | MFX_IOPATTERN_OUT_OPAQUE_MEMORY;
 
@@ -271,11 +290,6 @@ static int filter_init( av_qsv_context* qsv, hb_filter_private_t * pv ){
             qsv_vpp->p_ext_params[1] = (mfxExtBuffer*)&pv->frc_config;
         }
 
-        if (pv->deinterlace)
-        {
-            qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct  = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct;
-            qsv_vpp->m_mfxVideoParam.vpp.Out.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
-        }
         sts = MFXVideoVPP_Init(qsv->mfx_session, &qsv_vpp->m_mfxVideoParam);
 
         AV_QSV_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION);