]> granicus.if.org Git - handbrake/commitdiff
QSV: enc_qsv cleanup.
authorRodeo <tdskywalker@gmail.com>
Wed, 14 Aug 2013 14:03:09 +0000 (14:03 +0000)
committerRodeo <tdskywalker@gmail.com>
Wed, 14 Aug 2013 14:03:09 +0000 (14:03 +0000)
Lets us get rid of a muxer hack and brings us closer to merging back to trunk.

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

libhb/enc_qsv.c
libhb/enc_qsv.h
libhb/h264_common.h
libhb/muxmkv.c
libhb/muxmp4.c
libhb/qsv_common.c
libhb/qsv_common.h

index a032d2375aea105d3753de5eae7153c568183921..eb1883e3dd763cbaac57a4563a6983a94e4246dc 100644 (file)
@@ -26,58 +26,12 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 \* ********************************************************************* */
 
-#include <stdarg.h>
 #include "hb.h"
 #include "enc_qsv.h"
 #include "qsv_common.h"
 #include "qsv_memory.h"
 #include "h264_common.h"
 
-/**
- * @brief Convert profile string name into profile_e enum with mapping of supported profiles
- *
- * @param[in]   str     string name of profile
- * @retval      mapped  profile_e enum value
- */
-int profile_string_to_int( const char *str )
-{
-    if( !strcasecmp( str, "baseline" ) )
-        return PROFILE_BASELINE;
-    if( !strcasecmp( str, "main" ) )
-        return PROFILE_MAIN;
-    if( !strcasecmp( str, "high" ) )
-        return PROFILE_HIGH;
-    if( !strcasecmp( str, "high10" ) )
-        return PROFILE_HIGH;
-    if( !strcasecmp( str, "high422" ) )
-        return PROFILE_HIGH;
-    if( !strcasecmp( str, "high444" ) )
-        return PROFILE_HIGH;
-    return -1;
-}
-
-int qsv_h264_levels[sizeof(hb_h264_level_names)] = {
-                                                        0, // "auto"
-                                                        MFX_LEVEL_AVC_1,
-                                                        MFX_LEVEL_AVC_1b,
-                                                        MFX_LEVEL_AVC_11,
-                                                        MFX_LEVEL_AVC_12,
-                                                        MFX_LEVEL_AVC_13,
-                                                        MFX_LEVEL_AVC_2,
-                                                        MFX_LEVEL_AVC_21,
-                                                        MFX_LEVEL_AVC_22,
-                                                        MFX_LEVEL_AVC_3,
-                                                        MFX_LEVEL_AVC_31,
-                                                        MFX_LEVEL_AVC_32,
-                                                        MFX_LEVEL_AVC_4,
-                                                        MFX_LEVEL_AVC_41,
-                                                        MFX_LEVEL_AVC_42,
-                                                        MFX_LEVEL_AVC_5,
-                                                        MFX_LEVEL_AVC_51,
-                                                        0, // MFX_LEVEL_AVC_52 if MSDK API MFX_VERSION_MAJOR/MFX_VERSION_MINOR is 1/6
-                                                        0
-                                                    };
-
 int  encqsvInit( hb_work_object_t *, hb_job_t * );
 int  encqsvWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
 void encqsvClose( hb_work_object_t * );
@@ -91,17 +45,17 @@ hb_work_object_t hb_encqsv =
     encqsvClose
 };
 
-#define SPSPPS_SIZE     1024
-
 struct hb_work_private_s
 {
-    hb_job_t       *job;
-    hb_esconfig_t  *config;
-    uint32_t       frames_in;
-    uint32_t       frames_out;
-    int64_t        last_start;
+    hb_job_t *job;
+    uint32_t  frames_in;
+    uint32_t  frames_out;
+    int64_t   last_start;
 
-    mfxEncodeCtrl *force_keyframe;
+    hb_qsv_param_t param;
+    av_qsv_space enc_space;
+
+    mfxEncodeCtrl force_keyframe;
     struct
     {
         int     index;
@@ -115,37 +69,20 @@ struct hb_work_private_s
     int64_t        init_pts[BFRM_DELAY_MAX + 1];
     hb_list_t     *list_dts;
 
-    mfxExtCodingOptionSPSPPS    *sps_pps;
-
-    int codec_profile;
-    int codec_level;
-
-    hb_list_t*  delayed_processing;
-
-    // options
-    av_qsv_config qsv_config;
-
     int async_depth;
     int max_async_depth;
 
-    // is only encode/system memory used
+    // if encode-only, system memory used
     int is_sys_mem;
-    struct SwsContextsws_context_to_nv12;
+    struct SwsContext *sws_context_to_nv12;
 
-    // is to expect VPP before
+    // whether to expect input from VPP or from QSV decode
     int is_vpp_present;
 
-    // always set
-    mfxExtCodingOption qsv_coding_option_config;
-    mfxExtVideoSignalInfo qsv_signal_info_config;
-
-    // only set if supported
-    mfxU16 mbbrc;
-    mfxU16 extbrc;
-    mfxU16 trellis;
-    mfxU16 la_depth;
-    int mbbrx_param_idx;
-    mfxExtCodingOption2 qsv_coding_option2_config;
+    // whether the encoder is initialized
+    int init_done;
+
+    hb_list_t *delayed_processing;
 };
 
 // for DTS generation (when MSDK API < 1.6 or VFR)
@@ -177,611 +114,764 @@ static int64_t hb_qsv_pop_next_dts(hb_list_t *list)
     return next_dts;
 }
 
+static const char* qsv_h264_profile_xlat(int profile)
+{
+    switch (profile)
+    {
+        case MFX_PROFILE_AVC_CONSTRAINED_BASELINE:
+            return "Constrained Baseline";
+        case MFX_PROFILE_AVC_BASELINE:
+            return "Baseline";
+        case MFX_PROFILE_AVC_EXTENDED:
+            return "Extended";
+        case MFX_PROFILE_AVC_MAIN:
+            return "Main";
+        case MFX_PROFILE_AVC_CONSTRAINED_HIGH:
+            return "Constrained High";
+        case MFX_PROFILE_AVC_PROGRESSIVE_HIGH:
+            return "Progressive High";
+        case MFX_PROFILE_AVC_HIGH:
+            return "High";
+        case MFX_PROFILE_UNKNOWN:
+        default:
+            return NULL;
+    }
+}
+
+static const char* qsv_h264_level_xlat(int level)
+{
+    int i;
+    for (i = 0; hb_h264_level_names[i] != NULL; i++)
+    {
+        if (hb_h264_level_values[i] == level)
+        {
+            return hb_h264_level_names[i];
+        }
+    }
+    return NULL;
+}
+
 int qsv_enc_init(av_qsv_context *qsv, hb_work_private_t *pv)
 {
     int i = 0;
     mfxStatus sts;
     hb_job_t *job = pv->job;
 
-    int is_encode_only = !hb_qsv_decode_is_enabled(job);
+    if (pv->init_done)
+    {
+        return 0;
+    }
+
+    pv->is_sys_mem = !hb_qsv_decode_is_enabled(job);
     if (qsv == NULL)
     {
-        if (!is_encode_only)
+        if (!pv->is_sys_mem)
         {
             hb_error("qsv_enc_init: decode enabled but no context!");
             return 3;
         }
-        qsv = av_mallocz(sizeof(av_qsv_context));
+        job->qsv = qsv = av_mallocz(sizeof(av_qsv_context));
     }
 
-    av_qsv_space* qsv_encode = qsv->enc_space;
-
-    if(qsv_encode == NULL){
-        qsv_encode = av_mallocz( sizeof( av_qsv_space ) );
+    av_qsv_space *qsv_encode = qsv->enc_space;
+    if (qsv_encode == NULL)
+    {
         // if only for encode
-        if(is_encode_only){
+        if (pv->is_sys_mem)
+        {
             // no need to use additional sync as encode only -> single thread
-            av_qsv_add_context_usage(qsv,0);
-
-            qsv->impl = MFX_IMPL_AUTO_ANY;
+            // XXX: this zeroes the session handle, so call it before MFXInit
+            av_qsv_add_context_usage(qsv, 0);
 
-            memset(&qsv->mfx_session, 0, sizeof(mfxSession));
+            // initialize the session
+            qsv->impl      = MFX_IMPL_AUTO_ANY;
             qsv->ver.Major = AV_QSV_MSDK_VERSION_MAJOR;
             qsv->ver.Minor = AV_QSV_MSDK_VERSION_MINOR;
-
             sts = MFXInit(qsv->impl, &qsv->ver, &qsv->mfx_session);
-            AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
-
-            pv->is_sys_mem = 1;
-            job->qsv = qsv;
-        }
-        else{
-            av_qsv_add_context_usage(qsv,HAVE_THREADS);
-            pv->is_sys_mem = 0;
+            if (sts != MFX_ERR_NONE)
+            {
+                hb_error("qsv_enc_init: MFXInit failed (%d)", sts);
+                *job->die = 1;
+                return -1;
+            }
         }
-
-        qsv->enc_space = qsv_encode;
+        qsv->enc_space = qsv_encode = &pv->enc_space;
     }
 
-    if(qsv_encode->is_init_done) return 1;
-
-    if(!pv->is_sys_mem){
-
-        if(!pv->is_vpp_present && job->list_filter){
-            int x = 0;
-            for(;x < hb_list_count( job->list_filter );x++){
-                hb_filter_object_t * filter = hb_list_item( job->list_filter, x );
-                if( filter->id == HB_FILTER_QSV_PRE  ||
+    if (!pv->is_sys_mem)
+    {
+        if (!pv->is_vpp_present && job->list_filter != NULL)
+        {
+            for (i = 0; i < hb_list_count(job->list_filter); i++)
+            {
+                hb_filter_object_t *filter = hb_list_item(job->list_filter, i);
+                if (filter->id == HB_FILTER_QSV_PRE  ||
                     filter->id == HB_FILTER_QSV_POST ||
-                    filter->id == HB_FILTER_QSV ){
-                        pv->is_vpp_present = 1;
-                        break;
+                    filter->id == HB_FILTER_QSV)
+                {
+                    pv->is_vpp_present = 1;
+                    break;
                 }
             }
         }
 
-        if(pv->is_vpp_present){
-            if(!qsv->vpp_space) return 2;
-
-            for(i = 0; i < av_qsv_list_count(qsv->vpp_space); i++){
-                av_qsv_space *vpp = av_qsv_list_item(qsv->vpp_space,i);
-                if(!vpp->is_init_done)
-                     return 2;
+        if (pv->is_vpp_present)
+        {
+            if (qsv->vpp_space == NULL)
+            {
+                return 2;
+            }
+            for (i = 0; i < av_qsv_list_count(qsv->vpp_space); i++)
+            {
+                av_qsv_space *vpp = av_qsv_list_item(qsv->vpp_space, i);
+                if (!vpp->is_init_done)
+                {
+                    return 2;
+                }
             }
         }
 
-        if(!qsv->dec_space) return 2;
-
         av_qsv_space *dec_space = qsv->dec_space;
-        if(!dec_space->is_init_done)
-             return 2;
+        if (dec_space == NULL || !dec_space->is_init_done)
+        {
+            return 2;
+        }
     }
-    else{
-        pv->sws_context_to_nv12 = hb_sws_get_context(
-                            job->width, job->height, AV_PIX_FMT_YUV420P,
-                            job->width, job->height, AV_PIX_FMT_NV12,
-                            SWS_LANCZOS|SWS_ACCURATE_RND);
+    else
+    {
+        pv->sws_context_to_nv12 = hb_sws_get_context(job->width, job->height,
+                                                     AV_PIX_FMT_YUV420P,
+                                                     job->width, job->height,
+                                                     AV_PIX_FMT_NV12,
+                                                     SWS_LANCZOS|SWS_ACCURATE_RND);
+    }
+
+    // allocate tasks
+    qsv_encode->p_buf_max_size = AV_QSV_BUF_SIZE_DEFAULT;
+    qsv_encode->tasks          = av_qsv_list_init(HAVE_THREADS);
+    for (i = 0; i < pv->max_async_depth; i++)
+    {
+        av_qsv_task *task    = av_mallocz(sizeof(av_qsv_task));
+        task->bs             = av_mallocz(sizeof(mfxBitstream));
+        task->bs->Data       = av_mallocz(sizeof(uint8_t) * qsv_encode->p_buf_max_size);
+        task->bs->MaxLength  = qsv_encode->p_buf_max_size;
+        task->bs->DataLength = 0;
+        task->bs->DataOffset = 0;
+        av_qsv_list_add(qsv_encode->tasks, task);
+    }
+
+    // setup surface allocation
+    qsv_encode->m_mfxVideoParam.IOPattern = (pv->is_sys_mem                 ?
+                                             MFX_IOPATTERN_IN_SYSTEM_MEMORY :
+                                             MFX_IOPATTERN_IN_OPAQUE_MEMORY);
+    memset(&qsv_encode->request, 0, sizeof(mfxFrameAllocRequest) * 2);
+    sts = MFXVideoENCODE_QueryIOSurf(qsv->mfx_session,
+                                     &qsv_encode->m_mfxVideoParam,
+                                     &qsv_encode->request);
+    if (sts < MFX_ERR_NONE) // ignore warnings
+    {
+        hb_error("qsv_enc_init: MFXVideoENCODE_QueryIOSurf failed (%d)", sts);
+        *job->die = 1;
+        return -1;
+    }
+
+    // allocate surfaces
+    if (pv->is_sys_mem)
+    {
+        qsv_encode->surface_num = FFMIN(qsv_encode->request[0].NumFrameSuggested +
+                                        pv->max_async_depth, AV_QSV_SURFACE_NUM);
+        if (qsv_encode->surface_num <= 0)
+        {
+            qsv_encode->surface_num = AV_QSV_SURFACE_NUM;
+        }
+        for (i = 0; i < qsv_encode->surface_num; i++)
+        {
+            qsv_encode->p_surfaces[i] = av_mallocz(sizeof(mfxFrameSurface1));
+            AV_QSV_CHECK_POINTER(qsv_encode->p_surfaces[i], MFX_ERR_MEMORY_ALLOC);
+            memcpy(&(qsv_encode->p_surfaces[i]->Info),
+                   &(qsv_encode->request[0].Info), sizeof(mfxFrameInfo));
+        }
+    }
+    else
+    {
+        av_qsv_space *in_space = qsv->dec_space;
+        if (pv->is_vpp_present)
+        {
+            // we get our input from VPP instead
+            in_space = av_qsv_list_item(qsv->vpp_space,
+                                        av_qsv_list_count(qsv->vpp_space) - 1);
+        }
+        // introduced in API 1.3
+        memset(&qsv_encode->ext_opaque_alloc, 0, sizeof(mfxExtOpaqueSurfaceAlloc));
+        qsv_encode->ext_opaque_alloc.Header.BufferId = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION;
+        qsv_encode->ext_opaque_alloc.Header.BufferSz = sizeof(mfxExtOpaqueSurfaceAlloc);
+        qsv_encode->ext_opaque_alloc.In.Surfaces     = in_space->p_surfaces;
+        qsv_encode->ext_opaque_alloc.In.NumSurface   = in_space->surface_num;
+        qsv_encode->ext_opaque_alloc.In.Type         = qsv_encode->request[0].Type;
+        qsv_encode->m_mfxVideoParam.ExtParam[qsv_encode->m_mfxVideoParam.NumExtParam++] = (mfxExtBuffer*)&qsv_encode->ext_opaque_alloc;
+    }
+
+    // allocate sync points
+    qsv_encode->sync_num = (qsv_encode->surface_num                         ?
+                            FFMIN(qsv_encode->surface_num, AV_QSV_SYNC_NUM) :
+                            AV_QSV_SYNC_NUM);
+    for (i = 0; i < qsv_encode->sync_num; i++)
+    {
+        qsv_encode->p_syncp[i] = av_mallocz(sizeof(av_qsv_sync));
+        AV_QSV_CHECK_POINTER(qsv_encode->p_syncp[i], MFX_ERR_MEMORY_ALLOC);
+        qsv_encode->p_syncp[i]->p_sync = av_mallocz(sizeof(mfxSyncPoint));
+        AV_QSV_CHECK_POINTER(qsv_encode->p_syncp[i]->p_sync, MFX_ERR_MEMORY_ALLOC);
     }
 
-    sts = MFXQueryIMPL(qsv->mfx_session,&qsv->impl);
-    AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
+    // initialize the encoder
+    sts = MFXVideoENCODE_Init(qsv->mfx_session, &qsv_encode->m_mfxVideoParam);
+    if (sts < MFX_ERR_NONE) // ignore warnings
+    {
+        hb_error("qsv_enc_init: MFXVideoENCODE_Init failed (%d)", sts);
+        *job->die = 1;
+        return -1;
+    }
+    qsv_encode->is_init_done = 1;
 
-    char* impl_name = (MFX_IMPL_SOFTWARE == qsv->impl) ? "software" : "hardware";
-    hb_log("qsv: Intel QSV/MediaSDK uses %s implementation", impl_name);
+    if (pv->is_sys_mem)
+    {
+        hb_log("qsv_enc_init: using encode-only path");
+    }
+    if (MFXQueryIMPL(qsv->mfx_session, &qsv->impl) == MFX_ERR_NONE)
+    {
+        hb_log("qsv_enc_init: using Intel Media SDK %s implementation",
+               qsv->impl == MFX_IMPL_SOFTWARE ? "software" : "hardware");
+    }
 
-    if(pv->is_sys_mem)
-        hb_log("qsv: Using for encode only.");
+    pv->init_done = 1;
+    return 0;
+}
 
+/***********************************************************************
+ * encqsvInit
+ ***********************************************************************
+ *
+ **********************************************************************/
+int encqsvInit(hb_work_object_t *w, hb_job_t *job)
+{
+    hb_work_private_t *pv = calloc(1, sizeof(hb_work_private_t));
+    w->private_data       = pv;
 
-    AV_QSV_ZERO_MEMORY(qsv_encode->m_mfxVideoParam);
-    AV_QSV_ZERO_MEMORY(qsv_encode->m_mfxVideoParam.mfx);
+    pv->job                = job;
+    pv->delayed_processing = hb_list_init();
+    pv->last_start         = INT64_MIN;
+    pv->frames_in          = 0;
+    pv->frames_out         = 0;
+    pv->init_done          = 0;
+    pv->is_sys_mem         = 0;
+    pv->is_vpp_present     = 0;
+
+    // set up a re-usable mfxEncodeCtrl to force keyframes (e.g. for chapters)
+    pv->force_keyframe.QP          = 0;
+    pv->force_keyframe.FrameType   = MFX_FRAMETYPE_I|MFX_FRAMETYPE_IDR|MFX_FRAMETYPE_REF;
+    pv->force_keyframe.NumExtParam = 0;
+    pv->force_keyframe.NumPayload  = 0;
+    pv->force_keyframe.ExtParam    = NULL;
+    pv->force_keyframe.Payload     = NULL;
 
-    qsv_param_set_defaults(&pv->qsv_config);
+    pv->next_chapter.index = 0;
+    pv->next_chapter.start = INT64_MIN;
 
-    /* set up the VUI color model to match what the COLR atom says. */
-    int color_prim, color_transfer, color_matrix;
+    // default encoding parameters
+    if (hb_qsv_param_default(&pv->param, &pv->enc_space.m_mfxVideoParam))
+    {
+        hb_error("encqsvInit: hb_qsv_param_default failed");
+        return -1;
+    }
+
+    // set AsyncDepth to match that of decode and VPP
+    pv->param.videoParam->AsyncDepth = job->qsv_async_depth;
+
+    // enable and set colorimetry (video signal information)
+    pv->param.videoSignalInfo.ColourDescriptionPresent = 1;
     switch (job->color_matrix_code)
     {
         case 4:
             // custom
-            color_prim     = job->color_prim;
-            color_transfer = job->color_transfer;
-            color_matrix   = job->color_matrix;
+            pv->param.videoSignalInfo.ColourPrimaries         = job->color_prim;
+            pv->param.videoSignalInfo.TransferCharacteristics = job->color_transfer;
+            pv->param.videoSignalInfo.MatrixCoefficients      = job->color_matrix;
             break;
         case 3:
             // ITU BT.709 HD content
-            color_prim     = HB_COLR_PRI_BT709;
-            color_transfer = HB_COLR_TRA_BT709;
-            color_matrix   = HB_COLR_MAT_BT709;
+            pv->param.videoSignalInfo.ColourPrimaries         = HB_COLR_PRI_BT709;
+            pv->param.videoSignalInfo.TransferCharacteristics = HB_COLR_TRA_BT709;
+            pv->param.videoSignalInfo.MatrixCoefficients      = HB_COLR_MAT_BT709;
             break;
         case 2:
             // ITU BT.601 DVD or SD TV content (PAL)
-            color_prim     = HB_COLR_PRI_EBUTECH;
-            color_transfer = HB_COLR_TRA_BT709;
-            color_matrix   = HB_COLR_MAT_SMPTE170M;
+            pv->param.videoSignalInfo.ColourPrimaries         = HB_COLR_PRI_EBUTECH;
+            pv->param.videoSignalInfo.TransferCharacteristics = HB_COLR_TRA_BT709;
+            pv->param.videoSignalInfo.MatrixCoefficients      = HB_COLR_MAT_SMPTE170M;
             break;
         case 1:
             // ITU BT.601 DVD or SD TV content (NTSC)
-            color_prim     = HB_COLR_PRI_SMPTEC;
-            color_transfer = HB_COLR_TRA_BT709;
-            color_matrix   = HB_COLR_MAT_SMPTE170M;
+            pv->param.videoSignalInfo.ColourPrimaries         = HB_COLR_PRI_SMPTEC;
+            pv->param.videoSignalInfo.TransferCharacteristics = HB_COLR_TRA_BT709;
+            pv->param.videoSignalInfo.MatrixCoefficients      = HB_COLR_MAT_SMPTE170M;
             break;
         default:
             // detected during scan
-            color_prim     = job->title->color_prim;
-            color_transfer = job->title->color_transfer;
-            color_matrix   = job->title->color_matrix;
+            pv->param.videoSignalInfo.ColourPrimaries         = job->title->color_prim;
+            pv->param.videoSignalInfo.TransferCharacteristics = job->title->color_transfer;
+            pv->param.videoSignalInfo.MatrixCoefficients      = job->title->color_matrix;
             break;
     }
 
-    hb_dict_t *qsv_opts_dict = NULL;
-    if( job->advanced_opts != NULL && *job->advanced_opts != '\0' )
-        qsv_opts_dict = hb_encopts_to_dict( job->advanced_opts, job->vcodec );
-
-    int ret;
-    hb_dict_entry_t *entry = NULL;
-    while( ( entry = hb_dict_next( qsv_opts_dict, entry ) ) )
+    // parse user-specified advanced options, if present
+    if (job->advanced_opts != NULL && job->advanced_opts[0] != '\0')
     {
-        ret = qsv_param_parse( &pv->qsv_config, entry->key, entry->value );
-        if( ret == QSV_PARAM_BAD_NAME )
-            hb_log( "QSV options: Unknown suboption %s", entry->key );
-        else
-        if( ret == QSV_PARAM_BAD_VALUE )
-            hb_log( "QSV options: Bad argument %s=%s", entry->key, entry->value ? entry->value : "(null)" );
+        hb_dict_t *options_list;
+        hb_dict_entry_t *option = NULL;
+        options_list = hb_encopts_to_dict(job->advanced_opts, job->vcodec);
+        while ((option = hb_dict_next(options_list, option)) != NULL)
+        {
+            switch (hb_qsv_param_parse(&pv->param,
+                                       option->key, option->value, job->vcodec))
+            {
+                case HB_QSV_PARAM_OK:
+                    break;
+
+                case HB_QSV_PARAM_BAD_NAME:
+                    hb_log("encqsvInit: hb_qsv_param_parse: bad key %s",
+                           option->key);
+                    break;
+                case HB_QSV_PARAM_BAD_VALUE:
+                    hb_log("encqsvInit: hb_qsv_param_parse: bad value %s for key %s",
+                           option->value, option->key);
+                    break;
+                case HB_QSV_PARAM_UNSUPPORTED:
+                    hb_log("encqsvInit: hb_qsv_param_parse: unsupported option %s",
+                           option->key);
+                    break;
+
+                case HB_QSV_PARAM_ERROR:
+                default:
+                    hb_log("encqsvInit: hb_qsv_param_parse: unknown error");
+                    break;
+            }
+        }
+        hb_dict_free(&options_list);
     }
 
-    qsv_encode->m_mfxVideoParam.mfx.TargetUsage     = pv->qsv_config.target_usage;
-    qsv_encode->m_mfxVideoParam.mfx.CodecId         = MFX_CODEC_AVC;
-    qsv_encode->m_mfxVideoParam.mfx.CodecProfile    = MFX_PROFILE_AVC_HIGH;
-    qsv_encode->m_mfxVideoParam.mfx.CodecLevel      = pv->codec_level;
-
-    if(pv->codec_profile>=0)
-        qsv_encode->m_mfxVideoParam.mfx.CodecProfile = pv->codec_profile;
-
-    qsv_encode->m_mfxVideoParam.mfx.TargetKbps              = 2000;
-    qsv_encode->m_mfxVideoParam.mfx.RateControlMethod       = MFX_RATECONTROL_VBR;
-
-    int old_TargetKbps = qsv_encode->m_mfxVideoParam.mfx.TargetKbps;
+    // reload colorimetry in case values were set in advanced_opts
+    if (pv->param.videoSignalInfo.ColourDescriptionPresent)
+    {
+        job->color_matrix_code = 4;
+        job->color_prim        = pv->param.videoSignalInfo.ColourPrimaries;
+        job->color_transfer    = pv->param.videoSignalInfo.TransferCharacteristics;
+        job->color_matrix      = pv->param.videoSignalInfo.MatrixCoefficients;
+    }
+    else
+    {
+        job->color_matrix_code = 0;
+        job->color_prim        = HB_COLR_PRI_UNDEF;
+        job->color_transfer    = HB_COLR_TRA_UNDEF;
+        job->color_matrix      = HB_COLR_MAT_UNDEF;
+    }
 
-    if (job->vquality >= 0)
+    // sanitize values that may exceed the Media SDK variable size
+    int64_t vrate, vrate_base;
+    int64_t par_width, par_height;
+    hb_limit_rational64(&vrate, &vrate_base,
+                        job->vrate, job->vrate_base, UINT32_MAX);
+    hb_limit_rational64(&par_width, &par_height,
+                        job->anamorphic.par_width,
+                        job->anamorphic.par_height, UINT16_MAX);
+
+    // encode to H.264 and set FrameInfo
+    pv->param.videoParam->mfx.CodecId                 = MFX_CODEC_AVC;
+    pv->param.videoParam->mfx.CodecLevel              = MFX_LEVEL_UNKNOWN;
+    pv->param.videoParam->mfx.CodecProfile            = MFX_PROFILE_UNKNOWN;
+    pv->param.videoParam->mfx.FrameInfo.FourCC        = MFX_FOURCC_NV12;
+    pv->param.videoParam->mfx.FrameInfo.ChromaFormat  = MFX_CHROMAFORMAT_YUV420;
+    pv->param.videoParam->mfx.FrameInfo.FrameRateExtN = vrate;
+    pv->param.videoParam->mfx.FrameInfo.FrameRateExtD = vrate_base;
+    pv->param.videoParam->mfx.FrameInfo.AspectRatioW  = par_width;
+    pv->param.videoParam->mfx.FrameInfo.AspectRatioH  = par_height;
+    pv->param.videoParam->mfx.FrameInfo.CropX         = 0;
+    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);
+    if (pv->param.videoParam->mfx.FrameInfo.PicStruct == MFX_PICSTRUCT_PROGRESSIVE)
+    {
+        pv->param.videoParam->mfx.FrameInfo.Height = AV_QSV_ALIGN16(job->height);
+    }
+    else
     {
-        int cqp_offset_i,cqp_offset_p,cqp_offset_b;
-        int is_enforcing = 0;
+        pv->param.videoParam->mfx.FrameInfo.Height = AV_QSV_ALIGN32(job->height);
+    }
 
-        if ((entry = hb_dict_get(qsv_opts_dict, QSV_NAME_cqp_offset_i)) != NULL && entry->value != NULL)
+    // set H.264 profile and level
+    if (job->h264_profile != NULL && job->h264_profile[0] != '\0' &&
+        strcasecmp(job->h264_profile, "auto"))
+    {
+        if (!strcasecmp(job->h264_profile, "baseline"))
         {
-            cqp_offset_i = atoi(entry->value);
+            pv->param.videoParam->mfx.CodecProfile = MFX_PROFILE_AVC_BASELINE;
         }
-        else
-            cqp_offset_i = 0;
-         if ((entry = hb_dict_get(qsv_opts_dict, QSV_NAME_cqp_offset_p)) != NULL && entry->value != NULL)
+        else if (!strcasecmp(job->h264_profile, "main"))
         {
-            cqp_offset_p = atoi(entry->value);
+            pv->param.videoParam->mfx.CodecProfile = MFX_PROFILE_AVC_MAIN;
         }
-        else
-            cqp_offset_p = 2;
-        if ((entry = hb_dict_get(qsv_opts_dict, QSV_NAME_cqp_offset_b)) != NULL && entry->value != NULL)
+        else if (!strcasecmp(job->h264_profile, "high"))
         {
-            cqp_offset_b = atoi(entry->value);
-            is_enforcing = 1;
+            pv->param.videoParam->mfx.CodecProfile = MFX_PROFILE_AVC_HIGH;
         }
         else
-            cqp_offset_b = 4;
-
-        if( pv->qsv_config.gop_ref_dist == 4 && !is_enforcing )
         {
-            cqp_offset_b = cqp_offset_p;
+            hb_error("encqsvInit: bad profile %s", job->h264_profile);
+            return -1;
         }
-        qsv_encode->m_mfxVideoParam.mfx.RateControlMethod   = MFX_RATECONTROL_CQP;
-        qsv_encode->m_mfxVideoParam.mfx.QPI                 = job->vquality + cqp_offset_i;
-        qsv_encode->m_mfxVideoParam.mfx.QPP                 = job->vquality + cqp_offset_p;
-        qsv_encode->m_mfxVideoParam.mfx.QPB                 = job->vquality + cqp_offset_b;
     }
-    else if (job->vbitrate > 0)
+    if (job->h264_level != NULL && job->h264_level[0] != '\0' &&
+        strcasecmp(job->h264_level, "auto"))
     {
-        // note: if there is a speed or quality difference,
-        //       we could set MFX_RATECONTROL_AVBR here and
-        //       set MFX_RATECONTROL_VBR in one of the clauses below
-        //
-        qsv_encode->m_mfxVideoParam.mfx.RateControlMethod = MFX_RATECONTROL_VBR;
-        qsv_encode->m_mfxVideoParam.mfx.TargetKbps        = job->vbitrate;
-        old_TargetKbps = qsv_encode->m_mfxVideoParam.mfx.TargetKbps;
-
-        // make it work like x264 for ease of use
-        // (more convenient if switching back and forth)
-        //
-        // see http://mewiki.project357.com/wiki/X264_Settings#vbv-maxrate
-        //
-        if (((entry = hb_dict_get(qsv_opts_dict, QSV_NAME_vbv_bufsize)) != NULL && entry->value != NULL) ||
-            (job->h264_level != NULL && !strcasecmp(job->h264_level, "auto")))
+        int err;
+        int i = hb_qsv_atoindex(hb_h264_level_names, job->h264_level, &err);
+        if (err || i >= (sizeof(hb_h264_level_values) /
+                         sizeof(hb_h264_level_values[0])))
+        {
+            hb_error("encqsvInit: bad level %s", job->h264_level);
+            return -1;
+        }
+        else if (hb_qsv_info->capabilities & HB_QSV_CAP_MSDK_API_1_6)
         {
-            qsv_encode->m_mfxVideoParam.mfx.BufferSizeInKB = atoi(entry->value) / 8;
+            pv->param.videoParam->mfx.CodecLevel = HB_QSV_CLIP3(MFX_LEVEL_AVC_1,
+                                                                MFX_LEVEL_AVC_52,
+                                                                hb_h264_level_values[i]);
         }
-        if (((entry = hb_dict_get(qsv_opts_dict, QSV_NAME_vbv_maxrate)) != NULL && entry->value != NULL) ||
-            (job->h264_level != NULL && !strcasecmp(job->h264_level, "auto")))
+        else
         {
-            if (job->vbitrate == atoi(entry->value))
+            // Media SDK API < 1.6, MFX_LEVEL_AVC_52 unsupported
+            pv->param.videoParam->mfx.CodecLevel = HB_QSV_CLIP3(MFX_LEVEL_AVC_1,
+                                                                MFX_LEVEL_AVC_51,
+                                                                hb_h264_level_values[i]);
+        }
+    }
+
+    // set rate control paremeters
+    if (job->vquality >= 0)
+    {
+        // introduced in API 1.1
+        pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_CQP;
+        pv->param.videoParam->mfx.QPI = HB_QSV_CLIP3(0, 51, job->vquality + pv->param.rc.cqp_offsets[0]);
+        pv->param.videoParam->mfx.QPP = HB_QSV_CLIP3(0, 51, job->vquality + pv->param.rc.cqp_offsets[1]);
+        pv->param.videoParam->mfx.QPB = HB_QSV_CLIP3(0, 51, job->vquality + pv->param.rc.cqp_offsets[2]);
+    }
+    else if (job->vbitrate > 0)
+    {
+        if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_LOOKAHEAD)
+        {
+            if (pv->param.rc.lookahead < 0)
             {
-                qsv_encode->m_mfxVideoParam.mfx.RateControlMethod = MFX_RATECONTROL_CBR;
+                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);
+                }
             }
             else
             {
-                qsv_encode->m_mfxVideoParam.mfx.MaxKbps = atoi(entry->value);
+                // user force-enabled or force-disabled lookahead RC
+                pv->param.rc.lookahead = !!pv->param.rc.lookahead;
             }
         }
-        if ((entry = hb_dict_get(qsv_opts_dict, QSV_NAME_vbv_init)) != NULL && entry->value != NULL)
+        else
+        {
+            // lookahead RC not supported
+            pv->param.rc.lookahead = 0;
+        }
+        if (pv->param.rc.lookahead)
         {
-            if (atof(entry->value) < 1)
+            // introduced in API 1.7
+            pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_LA;
+            pv->param.videoParam->mfx.TargetKbps        = job->vbitrate;
+            if (pv->param.rc.vbv_max_bitrate > 0)
             {
-                // FIXME: we may not know BufferSizeInKB here,
-                //        depending on what was set above?
-                // maybe just forget about this special case
-                qsv_encode->m_mfxVideoParam.mfx.InitialDelayInKB = atof(entry->value) * qsv_encode->m_mfxVideoParam.mfx.BufferSizeInKB;
+                hb_log("encqsvInit: MFX_RATECONTROL_LA, ignoring VBV");
             }
-            else
+        }
+        else if (job->vbitrate == pv->param.rc.vbv_max_bitrate)
+        {
+            // introduced in API 1.0
+            pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_CBR;
+            pv->param.videoParam->mfx.MaxKbps           = job->vbitrate;
+            pv->param.videoParam->mfx.TargetKbps        = job->vbitrate;
+            pv->param.videoParam->mfx.BufferSizeInKB    = (pv->param.rc.vbv_buffer_size / 8);
+            // only set BufferSizeInKB and InitialDelayInKB is bufsize is set
+            // else Media SDK will pick some good values for us automatically
+            if (pv->param.rc.vbv_buffer_size > 0)
             {
-                qsv_encode->m_mfxVideoParam.mfx.InitialDelayInKB = atoi(entry->value) / 8;
+                if (pv->param.rc.vbv_buffer_init > 1.0)
+                {
+                    pv->param.videoParam->mfx.InitialDelayInKB = (pv->param.rc.vbv_buffer_init / 8);
+                }
+                else
+                {
+                    pv->param.videoParam->mfx.InitialDelayInKB = (pv->param.rc.vbv_buffer_size *
+                                                                 pv->param.rc.vbv_buffer_init / 8);
+                }
+                pv->param.videoParam->mfx.BufferSizeInKB = (pv->param.rc.vbv_buffer_size / 8);
             }
         }
-    }
-    else
-    {
-        hb_error("wrong parameters setting");
-    }
-
-    if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_LOOKAHEAD)
-    {
-        int use_la = 0;
-
-        if (qsv_encode->m_mfxVideoParam.mfx.RateControlMethod == MFX_RATECONTROL_VBR ||
-            qsv_encode->m_mfxVideoParam.mfx.RateControlMethod == MFX_RATECONTROL_AVBR)
+        else if (pv->param.rc.vbv_max_bitrate > 0)
         {
-            if ((entry = hb_dict_get(qsv_opts_dict, QSV_NAME_lookahead)) != NULL && entry->value != NULL)
-                use_la |= atoi(entry->value);
-            else
-            if (qsv_encode->m_mfxVideoParam.mfx.TargetUsage < 3)
-                use_la = 1; // sort of default value
+            // introduced in API 1.0
+            pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_VBR;
+            pv->param.videoParam->mfx.MaxKbps           = pv->param.rc.vbv_max_bitrate;
+            pv->param.videoParam->mfx.TargetKbps        = job->vbitrate;
+            // only set BufferSizeInKB and InitialDelayInKB is bufsize is set
+            // else Media SDK will pick some good values for us automatically
+            if (pv->param.rc.vbv_buffer_size > 0)
+            {
+                if (pv->param.rc.vbv_buffer_init > 1.0)
+                {
+                    pv->param.videoParam->mfx.InitialDelayInKB = (pv->param.rc.vbv_buffer_init / 8);
+                }
+                else
+                {
+                    pv->param.videoParam->mfx.InitialDelayInKB = (pv->param.rc.vbv_buffer_size *
+                                                                 pv->param.rc.vbv_buffer_init / 8);
+                }
+                pv->param.videoParam->mfx.BufferSizeInKB = (pv->param.rc.vbv_buffer_size / 8);
+            }
         }
-        if (use_la)
+        else
         {
-            qsv_encode->m_mfxVideoParam.mfx.RateControlMethod = MFX_RATECONTROL_LA;
-            qsv_encode->m_mfxVideoParam.mfx.TargetKbps        = old_TargetKbps;
+            // introduced in API 1.3
+            // Media SDK will set Accuracy and Convergence for us automatically
+            pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_AVBR;
+            pv->param.videoParam->mfx.TargetKbps        = job->vbitrate;
         }
     }
+    else
+    {
+        hb_error("encqsvInit: invalid rate control (%d, %d)",
+                 job->vquality, job->vbitrate);
+        return -1;
+    }
 
-    if (pv->qsv_config.gop_pic_size < 0)
+    // set the keyframe interval
+    if (pv->param.gop.gop_pic_size < 0)
     {
         int rate = (int)((double)job->vrate / (double)job->vrate_base + 0.5);
-        if (qsv_encode->m_mfxVideoParam.mfx.RateControlMethod == MFX_RATECONTROL_CQP)
+        if (pv->param.videoParam->mfx.RateControlMethod == MFX_RATECONTROL_CQP)
         {
             // ensure B-pyramid is enabled for CQP on Haswell
-            pv->qsv_config.gop_pic_size = 32;
+            pv->param.gop.gop_pic_size = 32;
         }
         else
         {
             // set the keyframe interval based on the framerate
-            pv->qsv_config.gop_pic_size = 5 * rate + 1;
+            pv->param.gop.gop_pic_size = 5 * rate + 1;
         }
     }
+    pv->param.videoParam->mfx.GopPicSize = pv->param.gop.gop_pic_size;
 
-    // version-specific encoder options
-    if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_BRC)
+    /*
+     * init a dummy encode-only session to get the SPS/PPS
+     * and the final output settings sanitized by Media SDK
+     * this is fine since the actual encode will use the same
+     * values for all parameters relevant to the H.264 bitstream
+     */
+    mfxStatus err;
+    mfxVersion version;
+    mfxVideoParam videoParam;
+    mfxExtBuffer* ExtParamArray[2];
+    mfxSession session = (mfxSession)0;
+    mfxExtCodingOption2 option2_buf, *option2 = &option2_buf;
+    mfxExtCodingOptionSPSPPS sps_pps_buf, *sps_pps = &sps_pps_buf;
+    version.Major = HB_QSV_MINVERSION_MAJOR;
+    version.Minor = HB_QSV_MINVERSION_MINOR;
+    err = MFXInit(MFX_IMPL_AUTO_ANY, &version, &session);
+    if (err != MFX_ERR_NONE)
     {
-        if ((entry = hb_dict_get(qsv_opts_dict, QSV_NAME_mbbrc)) != NULL && entry->value != NULL)
-        {
-            pv->mbbrc =atoi(entry->value);
-        }
-        else
-            pv->mbbrc = 1; // default: MFX_CODINGOPTION_ON
-
-        if ((entry = hb_dict_get(qsv_opts_dict, QSV_NAME_extbrc)) != NULL && entry->value != NULL)
-        {
-            pv->extbrc =atoi(entry->value);
-        }
-        else
-            pv->extbrc = 2; // default: MFX_CODINGOPTION_OFF
+        hb_error("encqsvInit: MFXInit failed (%d)", err);
+        return -1;
     }
-
-    if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_TRELLIS)
+    err = MFXVideoENCODE_Init(session, pv->param.videoParam);
+    if (err < MFX_ERR_NONE) // ignore warnings
     {
-        if ((entry = hb_dict_get(qsv_opts_dict, QSV_NAME_trellis)) != NULL && entry->value != NULL)
-        {
-            pv->trellis = hb_qsv_trellisvalue_xlat(atoi(entry->value));
-        }
-        else
-            pv->trellis = MFX_TRELLIS_UNKNOWN;
+        hb_error("encqsvInit: MFXVideoENCODE_Init failed (%d)", err);
+        MFXClose(session);
+        return -1;
     }
-
-    if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_LOOKAHEAD)
-        if ((entry = hb_dict_get(qsv_opts_dict, QSV_NAME_lookaheaddepth)) != NULL && entry->value != NULL)
-            pv->la_depth = HB_QSV_CLIP3(11,50, atoi(entry->value)); // range of: >10 and <=50
-        else
-            pv->la_depth = 40; // default: value
-
-    hb_dict_free( &qsv_opts_dict );
-
-    /*
-     * reload colorimetry in case values were set in the advanced_opts string.
-     *
-     * TODO: let users set custom values in the advanced_opts string.
-     */
-    job->color_matrix_code = 4;
-    job->color_prim        = color_prim;
-    job->color_transfer    = color_transfer;
-    job->color_matrix      = color_matrix;
-
-    qsv_encode->m_mfxVideoParam.AsyncDepth = job->qsv_async_depth;
-    pv->max_async_depth                    = job->qsv_async_depth;
-    pv->async_depth                        = 0;
-
-    char *rc_method = NULL;
-    switch (qsv_encode->m_mfxVideoParam.mfx.RateControlMethod){
-        case MFX_RATECONTROL_CBR    : rc_method = "MFX_RATECONTROL_CBR";
-                                      break;
-        case MFX_RATECONTROL_AVBR   : rc_method = "MFX_RATECONTROL_AVBR";
-                                      break;
-        case MFX_RATECONTROL_VBR    : rc_method = "MFX_RATECONTROL_VBR";
-                                      break;
-        case MFX_RATECONTROL_CQP    : rc_method = "MFX_RATECONTROL_CQP";
-                                      break;
-        case MFX_RATECONTROL_LA     : rc_method = "MFX_RATECONTROL_LA";
-                                      break;
-        default                     : rc_method = "unknown";
-    };
-
-    if( qsv_encode->m_mfxVideoParam.mfx.RateControlMethod == MFX_RATECONTROL_CQP )
-        hb_log("qsv: RateControlMethod:%s(I:%d/P:%d/B:%d)",rc_method,
-                                                qsv_encode->m_mfxVideoParam.mfx.QPI, qsv_encode->m_mfxVideoParam.mfx.QPP, qsv_encode->m_mfxVideoParam.mfx.QPB );
-    else
-    if( qsv_encode->m_mfxVideoParam.mfx.RateControlMethod == MFX_RATECONTROL_LA )
-        hb_log("qsv: RateControlMethod:%s LookAheadDepth:%d TargetKbps:%d",rc_method , pv->la_depth, qsv_encode->m_mfxVideoParam.mfx.TargetKbps  );
-    else
-        hb_log("qsv: RateControlMethod:%s TargetKbps:%d",rc_method , qsv_encode->m_mfxVideoParam.mfx.TargetKbps );
-
-    hb_log("qsv: TargetUsage:%d AsyncDepth:%d", qsv_encode->m_mfxVideoParam.mfx.TargetUsage,qsv_encode->m_mfxVideoParam.AsyncDepth);
-
-    if (pv->trellis != MFX_TRELLIS_UNKNOWN)
+    memset(&videoParam, 0, sizeof(mfxVideoParam));
+    videoParam.ExtParam = ExtParamArray;
+    videoParam.NumExtParam = 0;
+    // introduced in API 1.3
+    memset(sps_pps, 0, sizeof(mfxExtCodingOptionSPSPPS));
+    sps_pps->Header.BufferId = MFX_EXTBUFF_CODING_OPTION_SPSPPS;
+    sps_pps->Header.BufferSz = sizeof(mfxExtCodingOptionSPSPPS);
+    sps_pps->SPSId           = 0;
+    sps_pps->SPSBuffer       = w->config->h264.sps;
+    sps_pps->SPSBufSize      = sizeof(w->config->h264.sps);
+    sps_pps->PPSId           = 0;
+    sps_pps->PPSBuffer       = w->config->h264.pps;
+    sps_pps->PPSBufSize      = sizeof(w->config->h264.pps);
+    videoParam.ExtParam[videoParam.NumExtParam++] = (mfxExtBuffer*)sps_pps;
+    // introduced in API 1.6
+    memset(option2, 0, sizeof(mfxExtCodingOption2));
+    option2->Header.BufferId = MFX_EXTBUFF_CODING_OPTION2;
+    option2->Header.BufferSz = sizeof(mfxExtCodingOption2);
+    if (hb_qsv_info->capabilities & HB_QSV_CAP_MSDK_API_1_6)
     {
-        char mask[] = "OFF";
-        if (!(pv->trellis & MFX_TRELLIS_OFF))
-        {
-            mask[0] = pv->trellis & MFX_TRELLIS_I ? 'I' : ' ';
-            mask[1] = pv->trellis & MFX_TRELLIS_P ? 'P' : ' ';
-            mask[2] = pv->trellis & MFX_TRELLIS_B ? 'B' : ' ';
-        }
-        hb_log("qsv: Trellis mask: \"%c%c%c\"", mask[0],mask[1],mask[2] );
+        // attach to get the final output mfxExtCodingOption2 settings
+        videoParam.ExtParam[videoParam.NumExtParam++] = (mfxExtBuffer*)option2;
     }
-
-    qsv_encode->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN    = job->vrate;
-    qsv_encode->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD    = job->vrate_base;
-    qsv_encode->m_mfxVideoParam.mfx.FrameInfo.AspectRatioW     = job->anamorphic.par_width;
-    qsv_encode->m_mfxVideoParam.mfx.FrameInfo.AspectRatioH     = job->anamorphic.par_height;
-    qsv_encode->m_mfxVideoParam.mfx.FrameInfo.FourCC           = MFX_FOURCC_NV12;
-    qsv_encode->m_mfxVideoParam.mfx.FrameInfo.ChromaFormat     = MFX_CHROMAFORMAT_YUV420;
-    qsv_encode->m_mfxVideoParam.mfx.FrameInfo.PicStruct        = MFX_PICSTRUCT_PROGRESSIVE;
-    qsv_encode->m_mfxVideoParam.mfx.FrameInfo.CropX            = 0;
-    qsv_encode->m_mfxVideoParam.mfx.FrameInfo.CropY            = 0;
-    qsv_encode->m_mfxVideoParam.mfx.FrameInfo.CropW            = job->width;
-    qsv_encode->m_mfxVideoParam.mfx.FrameInfo.CropH            = job->height;
-
-    qsv_encode->m_mfxVideoParam.mfx.GopRefDist                 = pv->qsv_config.gop_ref_dist;
-    qsv_encode->m_mfxVideoParam.mfx.GopPicSize                 = pv->qsv_config.gop_pic_size;
-    qsv_encode->m_mfxVideoParam.mfx.NumRefFrame                = pv->qsv_config.num_ref_frame;
-
-    hb_log("qsv: GopRefDist:%d GopPicSize:%d NumRefFrame:%d",
-       qsv_encode->m_mfxVideoParam.mfx.GopRefDist,
-       qsv_encode->m_mfxVideoParam.mfx.GopPicSize,
-       qsv_encode->m_mfxVideoParam.mfx.NumRefFrame);
-
-    // width must be a multiple of 16
-    // height must be a multiple of 32 in case of interlace picture and a multiple of 16 in case of progressive
-
-    if (pv->is_vpp_present)
+    err = MFXVideoENCODE_GetVideoParam(session, &videoParam);
+    MFXVideoENCODE_Close(session);
+    MFXClose(session);
+    if (err == MFX_ERR_NONE)
     {
-        av_qsv_space *vpp = av_qsv_list_item(qsv->vpp_space,av_qsv_list_count(qsv->vpp_space)-1);
-        qsv_encode->m_mfxVideoParam.mfx.FrameInfo.Width  = vpp->m_mfxVideoParam.vpp.Out.Width;
-        qsv_encode->m_mfxVideoParam.mfx.FrameInfo.Height = vpp->m_mfxVideoParam.vpp.Out.Height;
+        // remove 32-bit NAL prefix (0x00 0x00 0x00 0x01)
+        w->config->h264.sps_length = sps_pps->SPSBufSize - 4;
+        memmove(w->config->h264.sps, w->config->h264.sps + 4,
+                w->config->h264.sps_length);
+        w->config->h264.pps_length = sps_pps->PPSBufSize - 4;
+        memmove(w->config->h264.pps, w->config->h264.pps + 4,
+                w->config->h264.pps_length);
     }
     else
     {
-        qsv_encode->m_mfxVideoParam.mfx.FrameInfo.Width  =  AV_QSV_ALIGN16(qsv_encode->m_mfxVideoParam.mfx.FrameInfo.CropW);
-        qsv_encode->m_mfxVideoParam.mfx.FrameInfo.Height = (MFX_PICSTRUCT_PROGRESSIVE == qsv_encode->m_mfxVideoParam.mfx.FrameInfo.PicStruct)?
-                                                                AV_QSV_ALIGN16(qsv_encode->m_mfxVideoParam.mfx.FrameInfo.CropH) :
-                                                                AV_QSV_ALIGN32(qsv_encode->m_mfxVideoParam.mfx.FrameInfo.CropH);
-    }
-
-    if(pv->is_sys_mem)
-        qsv_encode->m_mfxVideoParam.IOPattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY;
-    else
-        qsv_encode->m_mfxVideoParam.IOPattern = MFX_IOPATTERN_IN_OPAQUE_MEMORY;
-
-    // if not explicit value given - making atleast one
-    int tasks_amount = pv->max_async_depth ? pv->max_async_depth : 1;
-    qsv_encode->tasks = av_qsv_list_init(HAVE_THREADS);
-    qsv_encode->p_buf_max_size = AV_QSV_BUF_SIZE_DEFAULT;
-
-    for (i = 0; i < tasks_amount; i++){
-            av_qsv_task* task = av_mallocz(sizeof(av_qsv_task));
-            task->bs = av_mallocz(sizeof(mfxBitstream));
-            task->bs->Data  = av_mallocz(qsv_encode->p_buf_max_size*sizeof(uint8_t));
-            task->bs->DataLength    = 0;
-            task->bs->DataOffset = 0;
-            task->bs->MaxLength = qsv_encode->p_buf_max_size;
-            av_qsv_list_add( qsv_encode->tasks, task );
-    }
-
-    // allocation of surfaces to work with
-    memset(&qsv_encode->request, 0, sizeof(mfxFrameAllocRequest)*2);
-    sts = MFXVideoENCODE_QueryIOSurf(qsv->mfx_session, &qsv_encode->m_mfxVideoParam, &qsv_encode->request);
-    AV_QSV_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION);
-    AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
-
-    if(pv->is_sys_mem){
-
-        qsv_encode->surface_num = FFMIN(qsv_encode->request[0].NumFrameSuggested +
-                                        pv->job->qsv_async_depth, AV_QSV_SURFACE_NUM);
-        if(qsv_encode->surface_num <= 0 )
-            qsv_encode->surface_num = AV_QSV_SURFACE_NUM;
-
-        for (i = 0; i < qsv_encode->surface_num; i++){
-            qsv_encode->p_surfaces[i] = av_mallocz( sizeof(mfxFrameSurface1) );
-            AV_QSV_CHECK_POINTER(qsv_encode->p_surfaces[i], MFX_ERR_MEMORY_ALLOC);
-            memcpy(&(qsv_encode->p_surfaces[i]->Info), &(qsv_encode->request[0].Info), sizeof(mfxFrameInfo));
-        }
+        hb_error("encqsvInit: MFXVideoENCODE_GetVideoParam failed (%d)", err);
+        return -1;
     }
 
-    qsv_encode->sync_num = qsv_encode->surface_num? FFMIN( qsv_encode->surface_num,AV_QSV_SYNC_NUM ) : AV_QSV_SYNC_NUM;
-    for (i = 0; i < qsv_encode->sync_num; i++){
-        qsv_encode->p_syncp[i] = av_mallocz(sizeof(av_qsv_sync));
-        AV_QSV_CHECK_POINTER(qsv_encode->p_syncp[i], MFX_ERR_MEMORY_ALLOC);
-        qsv_encode->p_syncp[i]->p_sync = av_mallocz(sizeof(mfxSyncPoint));
-        AV_QSV_CHECK_POINTER(qsv_encode->p_syncp[i]->p_sync, MFX_ERR_MEMORY_ALLOC);
+    // log main output settings
+    hb_log("encqsvInit: TargetUsage %"PRIu16" AsyncDepth %"PRIu16"",
+           videoParam.mfx.TargetUsage, videoParam.AsyncDepth);
+    hb_log("encqsvInit: GopRefDist %"PRIu16" GopPicSize %"PRIu16" NumRefFrame %"PRIu16"",
+           videoParam.mfx.GopRefDist, videoParam.mfx.GopPicSize, videoParam.mfx.NumRefFrame);
+    if (videoParam.mfx.RateControlMethod == MFX_RATECONTROL_CQP)
+    {
+        char qpi[7], qpp[9], qpb[9];
+        snprintf(qpi, sizeof(qpi),   "QPI %"PRIu16"", videoParam.mfx.QPI);
+        snprintf(qpp, sizeof(qpp), ", QPP %"PRIu16"", videoParam.mfx.QPP);
+        snprintf(qpb, sizeof(qpb), ", QPB %"PRIu16"", videoParam.mfx.QPB);
+        hb_log("encqsvInit: MFX_RATECONTROL_CQP with %s%s%s", qpi,
+               videoParam.mfx.GopPicSize > 1 ? qpp : "",
+               videoParam.mfx.GopRefDist > 1 ? qpb : "");
     }
-
-    qsv_encode->p_ext_param_num = 0;
-    if(!pv->is_sys_mem)
-        qsv_encode->p_ext_param_num = 1; // for MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION
-
-    if (pv->mbbrc || pv->extbrc ||
-        qsv_encode->m_mfxVideoParam.mfx.RateControlMethod == MFX_RATECONTROL_LA ||
-        pv->extbrc != MFX_TRELLIS_UNKNOWN)
+    else
     {
-        qsv_encode->p_ext_param_num++;
+        switch (videoParam.mfx.RateControlMethod)
+        {
+            case MFX_RATECONTROL_AVBR:
+                hb_log("encqsvInit: MFX_RATECONTROL_AVBR with TargetKbps %"PRIu16"",
+                       videoParam.mfx.TargetKbps);
+                break;
+            case MFX_RATECONTROL_LA:
+                hb_log("encqsvInit: MFX_RATECONTROL_LA with TargetKbps %"PRIu16", LookAheadDepth %"PRIu16"",
+                       videoParam.mfx.TargetKbps, option2->LookAheadDepth);
+                break;
+            case MFX_RATECONTROL_CBR:
+            case MFX_RATECONTROL_VBR:
+                hb_log("encqsvInit: MFX_RATECONTROL_%s with TargetKbps %"PRIu16", MaxKbps %"PRIu16"",
+                       videoParam.mfx.RateControlMethod == MFX_RATECONTROL_CBR ? "CBR" : "VBR",
+                       videoParam.mfx.TargetKbps, videoParam.mfx.MaxKbps);
+                hb_log("encqsvInit: VBV enabled with BufferSizeInKB %"PRIu16" and InitialDelayInKB %"PRIu16"",
+                       videoParam.mfx.BufferSizeInKB, videoParam.mfx.InitialDelayInKB);
+                break;
+            default:
+                hb_log("encqsvInit: invalid rate control method %"PRIu16"",
+                       videoParam.mfx.RateControlMethod);
+                return -1;
+        }
     }
-
-    qsv_encode->p_ext_param_num++; // for MFX_EXTBUFF_CODING_OPTION
-    qsv_encode->p_ext_param_num++; // for MFX_EXTBUFF_VIDEO_SIGNAL_INFO
-
-    qsv_encode->m_mfxVideoParam.NumExtParam = qsv_encode->p_ext_param_num;
-
-    if (qsv_encode->m_mfxVideoParam.NumExtParam)
+    if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_BRC)
     {
-        int cur_idx = 0;
-        qsv_encode->p_ext_params = av_mallocz(sizeof(mfxExtBuffer *)*qsv_encode->p_ext_param_num);
-        AV_QSV_CHECK_POINTER(qsv_encode->p_ext_params, MFX_ERR_MEMORY_ALLOC);
-
-        // MFX_EXTBUFF_CODING_OPTION, supported since MSDK API 1.0
-        pv->qsv_coding_option_config.Header.BufferId = MFX_EXTBUFF_CODING_OPTION;
-        pv->qsv_coding_option_config.Header.BufferSz = sizeof(mfxExtCodingOption);
-        pv->qsv_coding_option_config.AUDelimiter     = MFX_CODINGOPTION_OFF;
-        pv->qsv_coding_option_config.PicTimingSEI    = MFX_CODINGOPTION_OFF;
-        qsv_encode->p_ext_params[cur_idx++]          = (mfxExtBuffer*)&pv->qsv_coding_option_config;
-
-        // MFX_EXTBUFF_VIDEO_SIGNAL_INFO, supported since MSDK API 1.3
-        pv->qsv_signal_info_config.Header.BufferId          = MFX_EXTBUFF_VIDEO_SIGNAL_INFO;
-        pv->qsv_signal_info_config.Header.BufferSz          = sizeof(mfxExtVideoSignalInfo);
-        pv->qsv_signal_info_config.VideoFormat              = 5; // undefined
-        pv->qsv_signal_info_config.VideoFullRange           = 0; // TV range
-        pv->qsv_signal_info_config.ColourDescriptionPresent = 1; // write to bistream
-        pv->qsv_signal_info_config.ColourPrimaries          = color_prim;
-        pv->qsv_signal_info_config.TransferCharacteristics  = color_transfer;
-        pv->qsv_signal_info_config.MatrixCoefficients       = color_matrix;
-        qsv_encode->p_ext_params[cur_idx++]                 = (mfxExtBuffer*)&pv->qsv_signal_info_config;
-
-        if (!pv->is_sys_mem)
+        const char *mbbrc, *extbrc;
+        switch (option2->MBBRC)
         {
-            memset(&qsv_encode->ext_opaque_alloc, 0, sizeof(mfxExtOpaqueSurfaceAlloc));
-            qsv_encode->ext_opaque_alloc.Header.BufferId    = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION;
-            qsv_encode->ext_opaque_alloc.Header.BufferSz    = sizeof(mfxExtOpaqueSurfaceAlloc);
-                qsv_encode->p_ext_params[cur_idx++]             = (mfxExtBuffer*)&qsv_encode->ext_opaque_alloc;
-
-            av_qsv_space *in_space = 0;
-            // if we have any VPP , we will use its details , if not - failback to decode's settings
-            if(pv->is_vpp_present)
-                in_space = av_qsv_list_item(qsv->vpp_space, av_qsv_list_count(qsv->vpp_space)-1);
-            else
-                in_space = qsv->dec_space;
-
-            qsv_encode->ext_opaque_alloc.In.Surfaces    = in_space->p_surfaces;
-            qsv_encode->ext_opaque_alloc.In.NumSurface  = in_space->surface_num;
-            qsv_encode->ext_opaque_alloc.In.Type        = qsv_encode->request[0].Type;
+            case MFX_CODINGOPTION_ON:
+                mbbrc = "on";
+                break;
+            case MFX_CODINGOPTION_OFF:
+                mbbrc = "off";
+                break;
+            case MFX_CODINGOPTION_ADAPTIVE:
+                mbbrc = "adaptive";
+                break;
+            default:
+                hb_error("encqsvInit: invalid MBBRC value %"PRIu16"",
+                         option2->MBBRC);
+                return -1;
         }
-
-        if (pv->mbbrc || pv->extbrc ||
-            qsv_encode->m_mfxVideoParam.mfx.RateControlMethod == MFX_RATECONTROL_LA ||
-            pv->extbrc != MFX_TRELLIS_UNKNOWN)
+        switch (option2->ExtBRC)
         {
-            pv->qsv_coding_option2_config.Header.BufferId  = MFX_EXTBUFF_CODING_OPTION2;
-            pv->qsv_coding_option2_config.Header.BufferSz  = sizeof(mfxExtCodingOption2);
-            pv->qsv_coding_option2_config.MBBRC            = pv->mbbrc << 4;
-                                                            // default is off
-            pv->qsv_coding_option2_config.ExtBRC           = pv->extbrc == 0 ? MFX_CODINGOPTION_OFF : pv->extbrc << 4;
-            pv->qsv_coding_option2_config.LookAheadDepth   = pv->la_depth;
-            pv->qsv_coding_option2_config.Trellis          = pv->trellis;
-
-            // reset if out of the ranges
-            if(pv->qsv_coding_option2_config.MBBRC > MFX_CODINGOPTION_ADAPTIVE)
-                pv->qsv_coding_option2_config.MBBRC  = MFX_CODINGOPTION_UNKNOWN;
-            if(pv->qsv_coding_option2_config.ExtBRC > MFX_CODINGOPTION_ADAPTIVE)
-                pv->qsv_coding_option2_config.ExtBRC = MFX_CODINGOPTION_OFF;
-
-            qsv_encode->p_ext_params[cur_idx++]    = (mfxExtBuffer*)&pv->qsv_coding_option2_config;
-            pv->mbbrx_param_idx = cur_idx;
+            case MFX_CODINGOPTION_ON:
+                extbrc = "on";
+                break;
+            case MFX_CODINGOPTION_OFF:
+                extbrc = "off";
+                break;
+            case MFX_CODINGOPTION_ADAPTIVE:
+                extbrc = "adaptive";
+                break;
+            default:
+                hb_error("encqsvInit: invalid ExtBRC value %"PRIu16"",
+                         option2->ExtBRC);
+                return -1;
         }
+        hb_log("encqsvInit: MBBRC %s ExtBRC %s", mbbrc, extbrc);
     }
-    qsv_encode->m_mfxVideoParam.ExtParam           = qsv_encode->p_ext_params;
-
-    for(;;){
-    sts = MFXVideoENCODE_Init(qsv->mfx_session, &qsv_encode->m_mfxVideoParam);
-        // if not supported first time we will reset
-        if( sts < MFX_ERR_NONE ){
-            if( pv->mbbrx_param_idx ){
-                pv->qsv_coding_option2_config.MBBRC             = MFX_CODINGOPTION_UNKNOWN;
-                pv->qsv_coding_option2_config.ExtBRC            = MFX_CODINGOPTION_OFF;
-                pv->qsv_coding_option2_config.LookAheadDepth    = 0;
-                pv->qsv_coding_option2_config.Trellis           = MFX_TRELLIS_UNKNOWN;
-                    pv->mbbrx_param_idx = 0;
-            }
+    if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_TRELLIS)
+    {
+        switch (option2->Trellis)
+        {
+            case MFX_TRELLIS_OFF:
+                hb_log("encqsvInit: Trellis off");
+                break;
+            case MFX_TRELLIS_UNKNOWN:
+                hb_log("encqsvInit: Trellis unknown (auto)");
+                break;
+            default:
+                hb_log("encqsvInit: Trellis on (%s%s%s)",
+                       option2->Trellis & MFX_TRELLIS_I ? "I" : "",
+                       option2->Trellis & MFX_TRELLIS_P ? "P" : "",
+                       option2->Trellis & MFX_TRELLIS_B ? "B" : "");
+                break;
         }
-        else
-            break;
     }
-    AV_QSV_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION);
-    AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
-
-    // SPS/PPS retrieval with NAL prefix: 00 00 00 01 for each
-    mfxVideoParam               videoParam;
-    memset(&videoParam, 0, sizeof(mfxVideoParam));
-    pv->sps_pps                  = av_mallocz(sizeof(mfxExtCodingOptionSPSPPS));
-    pv->sps_pps->Header.BufferId = MFX_EXTBUFF_CODING_OPTION_SPSPPS;
-    pv->sps_pps->PPSId      = 0;
-    pv->sps_pps->SPSId      = 0;
-    pv->sps_pps->SPSBuffer  = av_mallocz(SPSPPS_SIZE);
-    pv->sps_pps->SPSBufSize = SPSPPS_SIZE;
-    pv->sps_pps->PPSBuffer  = av_mallocz(SPSPPS_SIZE);
-    pv->sps_pps->PPSBufSize = SPSPPS_SIZE;
-
-    videoParam.NumExtParam  = 1;
-    videoParam.ExtParam     = (mfxExtBuffer**)&pv->sps_pps;
-
-    sts = MFXVideoENCODE_GetVideoParam(qsv->mfx_session, &videoParam);
-    AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts);
-
-    // Sequence Parameter Set
-    memcpy(pv->config->h264.sps, pv->sps_pps->SPSBuffer + sizeof(ff_prefix_code), pv->sps_pps->SPSBufSize - sizeof(ff_prefix_code));
-    pv->config->h264.sps_length = pv->sps_pps->SPSBufSize - sizeof(ff_prefix_code);
-
-    // Picture Parameter Set
-    memcpy(pv->config->h264.pps, pv->sps_pps->PPSBuffer + sizeof(ff_prefix_code), pv->sps_pps->PPSBufSize - sizeof(ff_prefix_code));
-    pv->config->h264.pps_length = pv->sps_pps->PPSBufSize - sizeof(ff_prefix_code);
+    hb_log("encqsvInit: H.264 profile %s @ level %s",
+           qsv_h264_profile_xlat(videoParam.mfx.CodecProfile),
+           qsv_h264_level_xlat  (videoParam.mfx.CodecLevel));
 
-    pv->qsv_config.gop_ref_dist = videoParam.mfx.GopRefDist;
+    // AsyncDepth has now been set and/or modified by Media SDK
+    pv->max_async_depth = videoParam.AsyncDepth;
+    pv->async_depth     = 0;
 
     // check whether B-frames are used
-    pv->bfrm_delay = !((videoParam.mfx.CodecProfile == MFX_PROFILE_AVC_BASELINE)             ||
-                       (videoParam.mfx.CodecProfile == MFX_PROFILE_AVC_CONSTRAINED_BASELINE) ||
-                       (videoParam.mfx.CodecProfile == MFX_PROFILE_AVC_CONSTRAINED_HIGH)     ||
-                       (videoParam.mfx.CodecProfile &  MFX_PROFILE_AVC_CONSTRAINT_SET0));
-    if (videoParam.mfx.GopRefDist > 0)
+    switch (videoParam.mfx.CodecProfile)
     {
-        pv->bfrm_delay = FFMIN(pv->bfrm_delay, videoParam.mfx.GopRefDist - 1);
-    }
-    if (videoParam.mfx.GopPicSize > 0)
-    {
-        pv->bfrm_delay = FFMIN(pv->bfrm_delay, videoParam.mfx.GopPicSize - 2);
+        case MFX_PROFILE_AVC_BASELINE:
+        case MFX_PROFILE_AVC_CONSTRAINED_HIGH:
+        case MFX_PROFILE_AVC_CONSTRAINED_BASELINE:
+            pv->bfrm_delay = 0;
+            break;
+        default:
+            pv->bfrm_delay = 1;
+            break;
     }
     // sanitize
+    pv->bfrm_delay = FFMIN(pv->bfrm_delay, videoParam.mfx.GopRefDist - 1);
+    pv->bfrm_delay = FFMIN(pv->bfrm_delay, videoParam.mfx.GopPicSize - 2);
     pv->bfrm_delay = FFMAX(pv->bfrm_delay, 0);
+    // let the muxer know whether to expect B-frames or not
+    job->areBframes = !!pv->bfrm_delay;
     // check whether we need to generate DTS ourselves (MSDK API < 1.6 or VFR)
     pv->bfrm_workaround = job->cfr != 1 || !(hb_qsv_info->capabilities &
                                              HB_QSV_CAP_MSDK_API_1_6);
@@ -796,89 +886,6 @@ int qsv_enc_init(av_qsv_context *qsv, hb_work_private_t *pv)
         pv->list_dts        = NULL;
     }
 
-    qsv_encode->is_init_done = 1;
-
-    return 0;
-}
-
-/***********************************************************************
- * encqsvInit
- ***********************************************************************
- *
- **********************************************************************/
-int encqsvInit( hb_work_object_t * w, hb_job_t * job )
-{
-    hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
-    w->private_data = pv;
-
-    pv->delayed_processing = hb_list_init();
-    pv->frames_in = 0;
-    pv->frames_out = 0;
-    pv->last_start = INT64_MIN;
-
-    // set up a re-usable mfxEncodeCtrl to force keyframes (e.g. for chapters)
-    pv->force_keyframe = calloc(1, sizeof(mfxEncodeCtrl));
-    if (pv->force_keyframe == NULL)
-    {
-        hb_error("encqsvInit: mfxEncodeCtrl allocation failure");
-        return -1;
-    }
-    pv->force_keyframe->QP          = 0;
-    pv->force_keyframe->FrameType   = MFX_FRAMETYPE_I|MFX_FRAMETYPE_IDR|MFX_FRAMETYPE_REF;
-    pv->force_keyframe->NumExtParam = 0;
-    pv->force_keyframe->NumPayload  = 0;
-    pv->force_keyframe->ExtParam    = NULL;
-    pv->force_keyframe->Payload     = NULL;
-
-    pv->next_chapter.index = 0;
-    pv->next_chapter.start = INT64_MIN;
-
-    pv->job = job;
-    pv->config = w->config;
-
-    pv->codec_level = MFX_LEVEL_AVC_3;
-    if(job->h264_level){
-        int i = 0;
-        for (i = 0; hb_h264_level_names[i]; i++)
-        {
-            if (job->h264_level && !strcmp(hb_h264_level_names[i], job->h264_level))
-            {
-                // skipping "auto" for default one
-                if(i)
-                    pv->codec_level = qsv_h264_levels[i];
-                break;
-            }
-        }
-    }
-
-    int profile = PROFILE_HIGH;
-    pv->codec_profile = -1;
-    if(job->h264_profile){
-        profile = profile_string_to_int(job->h264_profile);
-        switch(profile){
-            case PROFILE_BASELINE:  pv->codec_profile = MFX_PROFILE_AVC_BASELINE;
-                                    break;
-            case PROFILE_MAIN:      pv->codec_profile = MFX_PROFILE_AVC_MAIN;
-                                    break;
-            case PROFILE_HIGH:      pv->codec_profile = MFX_PROFILE_AVC_HIGH;
-                                    break;
-            default:                pv->codec_profile = -1;
-        }
-    }
-
-    // let the muxer know whether to expect B-frames or not
-    // FIXME: we must do this here even though we don't have all the info yet
-    job->areBframes = pv->codec_profile != MFX_PROFILE_AVC_BASELINE;
-
-    // tbd make it very properly
-    w->config->h264.sps[1] = profile;
-    w->config->h264.sps[2] = 0;
-    w->config->h264.sps[3] = pv->codec_level;
-    w->config->h264.sps_length = 0;
-    w->config->h264.pps_length = 0;
-
-    pv->is_vpp_present = 0;
-
     return 0;
 }
 
@@ -932,9 +939,6 @@ void encqsvClose( hb_work_object_t * w )
             }
             qsv_encode->surface_num = 0;
 
-                if( qsv_encode->p_ext_param_num || qsv_encode->p_ext_params )
-                    av_freep(&qsv_encode->p_ext_params);
-
             for (i = 0; i < qsv_encode->sync_num; i++){
                     av_freep(&qsv_encode->p_syncp[i]->p_sync);
                     av_freep(&qsv_encode->p_syncp[i]);
@@ -944,8 +948,6 @@ void encqsvClose( hb_work_object_t * w )
                 qsv_encode->is_init_done = 0;
         }
         }
-        if(qsv->enc_space)
-        av_freep(&qsv->enc_space);
 
         if(qsv){
         // closing the commong stuff
@@ -955,13 +957,6 @@ void encqsvClose( hb_work_object_t * w )
             av_freep(&qsv);
         }
         }
-
-        if (pv->sps_pps)
-        {
-            av_freep(&pv->sps_pps->SPSBuffer);
-            av_freep(&pv->sps_pps->PPSBuffer);
-            av_freep(&pv->sps_pps);
-        }
     }
 
     if (pv != NULL)
@@ -976,11 +971,6 @@ void encqsvClose( hb_work_object_t * w )
             }
             hb_list_close(&pv->list_dts);
         }
-        if (pv->force_keyframe != NULL)
-        {
-            free(pv->force_keyframe);
-            pv->force_keyframe = NULL;
-        }
     }
 
     free( pv );
@@ -1020,33 +1010,44 @@ int encqsvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
     }
 
     // input from decode, as called - we always have some to proceed with
-    while( 1 ){
-
+    while (1)
+    {
     {
         mfxEncodeCtrl    *work_control = NULL;
         mfxFrameSurface1 *work_surface = NULL;
 
-        if(!is_end) {
-            if(pv->is_sys_mem){
-                int surface_idx = av_qsv_get_free_surface(qsv_encode, qsv, &qsv_encode->request[0].Info, QSV_PART_ANY);
+        if (!is_end)
+        {
+            if (pv->is_sys_mem)
+            {
+                int surface_idx = av_qsv_get_free_surface(qsv_encode, qsv,
+                                                          &qsv_encode->request[0].Info, QSV_PART_ANY);
                 work_surface = qsv_encode->p_surfaces[surface_idx];
 
-                if(!work_surface->Data.Y){
+                if (work_surface->Data.Y == NULL)
+                {
                     // if nv12 and 422 12bits per pixel
-                    work_surface->Data.Y = calloc( 1, qsv_encode->m_mfxVideoParam.mfx.FrameInfo.Width *  qsv_encode->m_mfxVideoParam.mfx.FrameInfo.Height );
-                    work_surface->Data.VU = calloc( 1, qsv_encode->m_mfxVideoParam.mfx.FrameInfo.Width *  qsv_encode->m_mfxVideoParam.mfx.FrameInfo.Height /2);
-                    work_surface->Data.Pitch = qsv_encode->m_mfxVideoParam.mfx.FrameInfo.Width;
+                    work_surface->Data.Pitch = pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.Width;
+                    work_surface->Data.Y     = calloc(1,
+                                                      pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.Width *
+                                                      pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.Height);
+                    work_surface->Data.VU    = calloc(1,
+                                                      pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.Width *
+                                                      pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.Height / 2);
                 }
-                qsv_yuv420_to_nv12(pv->sws_context_to_nv12,work_surface,in);
+                qsv_yuv420_to_nv12(pv->sws_context_to_nv12, work_surface, in);
             }
-            else{
+            else
+            {
                 received_item = in->qsv_details.qsv_atom;
-                stage = av_qsv_get_last_stage( received_item );
+                stage = av_qsv_get_last_stage(received_item);
                 work_surface = stage->out.p_surface;
+
+                // don't let qsv->dts_seq grow needlessly
+                av_qsv_dts_pop(qsv);
             }
 
             work_surface->Data.TimeStamp = in->s.start;
-            av_qsv_dts_ordered_insert(qsv, 0, 0, work_surface->Data.TimeStamp, 0);
 
             /*
              * Debugging code to check that the upstream modules have generated
@@ -1087,7 +1088,7 @@ int encqsvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
                 {
                     pv->next_chapter.start = work_surface->Data.TimeStamp;
                     pv->next_chapter.index = in->s.new_chap;
-                    work_control           = pv->force_keyframe;
+                    work_control           = &pv->force_keyframe;
                 }
                 else
                 {
@@ -1227,8 +1228,8 @@ int encqsvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
                 last_buf = buf;
 
             // simple for now but check on TimeStampCalc from MSDK
-            int64_t duration  = ((double)qsv_encode->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD /
-                                 (double)qsv_encode->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN) * 90000.;
+            int64_t duration  = ((double)pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD /
+                                 (double)pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN) * 90000.;
 
             // start        -> PTS
             // renderOffset -> DTS
@@ -1244,7 +1245,7 @@ int encqsvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
                 else
                 {
                     // MSDK API < 1.6 or VFR, so generate our own DTS
-                    if ((pv->frames_out == 0)                             &&
+                    if ((pv->frames_out == 0)                                 &&
                         (hb_qsv_info->capabilities & HB_QSV_CAP_MSDK_API_1_6) &&
                         (hb_qsv_info->capabilities & HB_QSV_CAP_H264_BPYRAMID))
                     {
@@ -1312,12 +1313,6 @@ int encqsvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
                 pv->next_chapter.index = 0;
             }
 
-                if(pv->qsv_config.gop_ref_dist > 1)
-                    pv->qsv_config.gop_ref_dist--;
-                else{
-                    av_qsv_dts_pop(qsv);
-                }
-
                 // shift for fifo
                 if(pv->async_depth){
                     av_qsv_list_rem(qsv_encode->tasks,task);
@@ -1423,52 +1418,3 @@ void parse_nalus(uint8_t *nal_inits, size_t length, hb_buffer_t *buf, uint32_t f
             }
         }
 }
-
-int qsv_param_parse( av_qsv_config* config, const char *name, const char *value){
-    int ret = QSV_PARAM_OK;
-
-    if(!config)
-        return QSV_PARAM_BAD_CONFIG;
-
-    if(!strcmp(name,QSV_NAME_target_usage))
-        config->target_usage = FFMAX( atoi(value),0 );
-    else
-    if(!strcmp(name,QSV_NAME_num_ref_frame))
-        config->num_ref_frame = FFMAX( atoi(value),0 );
-    else
-    if(!strcmp(name,QSV_NAME_gop_ref_dist))
-        config->gop_ref_dist = FFMAX( FFMIN(atoi(value),32),0 );
-    else
-    if(!strcmp(name,QSV_NAME_gop_pic_size))
-        config->gop_pic_size = FFMAX(atoi(value),0);
-    else
-    if(!strcmp(name,QSV_NAME_vbv_bufsize) ||
-       !strcmp(name,QSV_NAME_vbv_maxrate) ||
-       !strcmp(name,QSV_NAME_vbv_init)    ||
-       !strcmp(name,QSV_NAME_mbbrc)       ||
-       !strcmp(name,QSV_NAME_extbrc)      ||
-       !strcmp(name,QSV_NAME_cqp_offset_i)      ||
-       !strcmp(name,QSV_NAME_cqp_offset_p)      ||
-       !strcmp(name,QSV_NAME_cqp_offset_b)      ||
-       !strcmp(name,QSV_NAME_lookaheaddepth)    ||
-       !strcmp(name,QSV_NAME_lookahead)         ||
-       !strcmp(name,QSV_NAME_trellis)
-       )
-        ret = QSV_PARAM_OK;
-    else
-        ret = QSV_PARAM_BAD_NAME;
-
-    return ret;
-}
-
-void qsv_param_set_defaults(av_qsv_config *config)
-{
-    if (config != NULL)
-    {
-        config->async_depth   = AV_QSV_ASYNC_DEPTH_DEFAULT;
-        config->target_usage  = MFX_TARGETUSAGE_BEST_QUALITY + 1;
-        config->num_ref_frame = 0;  // set automatically by MSDK
-        config->gop_ref_dist  = 4;  // power of 2, >= 4: B-pyramid
-        config->gop_pic_size  = -1; // set automatically based on framerate
-    }
-}
index f42ffbd7660d8a7dd60153c378baa5c76f6f211b..9d27347cd14758ae3149f5ec5424ce33c60616b5 100644 (file)
@@ -30,37 +30,9 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #define ENC_QSV_H
 
 #include "hb.h"
-#include "hb_dict.h"
-#include "libavcodec/qsv.h"
+#include "qsv_common.h"
 
 int nal_find_start_code(uint8_t** pb, size_t* size);
 void parse_nalus( uint8_t *nal_inits, size_t length, hb_buffer_t *buf, uint32_t frame_num);
 
-
-#define QSV_NAME_target_usage   "target-usage"
-#define QSV_NAME_num_ref_frame  "num-ref-frame"
-#define QSV_NAME_gop_ref_dist   "gop-ref-dist"
-#define QSV_NAME_gop_pic_size   "gop-pic-size"
-#define QSV_NAME_vbv_bufsize    "vbv-bufsize"
-#define QSV_NAME_vbv_maxrate    "vbv-maxrate"
-#define QSV_NAME_vbv_init       "vbv-init"
-#define QSV_NAME_mbbrc          "mbbrc"
-#define QSV_NAME_extbrc         "extbrc"
-#define QSV_NAME_cqp_offset_i   "cqp-offset-i"
-#define QSV_NAME_cqp_offset_p   "cqp-offset-p"
-#define QSV_NAME_cqp_offset_b   "cqp-offset-b"
-#define QSV_NAME_lookaheaddepth "lookahead-depth"
-#define QSV_NAME_lookahead      "lookahead"
-#define QSV_NAME_trellis        "trellis"
-
-typedef enum {
-    QSV_PARAM_OK            = 0,
-    QSV_PARAM_BAD_NAME      = -1,
-    QSV_PARAM_BAD_VALUE     = -2,
-    QSV_PARAM_BAD_CONFIG    = -3,
-} qsv_param_errors;
-
-int  qsv_param_parse(av_qsv_config *config, const char *name, const char *value);
-void qsv_param_set_defaults(av_qsv_config *config);
-
-#endif //ENC_QSV_H
+#endif // ENC_QSV_H
index fe92d1c1096b70a2f1c514981232a5ff860360b0..febe1965ff17bb5fb7bd1a5ebaa41008e34c247d 100755 (executable)
@@ -14,16 +14,4 @@ static const char * const hb_h264_profile_names[] = { "auto", "high", "main", "b
 static const char * const   hb_h264_level_names[] = { "auto", "1.0", "1b", "1.1", "1.2", "1.3", "2.0", "2.1", "2.2", "3.0", "3.1", "3.2", "4.0", "4.1", "4.2", "5.0", "5.1", "5.2",  NULL, };
 static const int    const  hb_h264_level_values[] = {     -1,    10,    9,    11,    12,    13,    20,    21,    22,    30,    31,    32,    40,    41,    42,    50,    51,    52,     0, };
 
-// as per
-// #include "set.h"
-enum profile_e
-{
-    PROFILE_BASELINE = 66,
-    PROFILE_MAIN     = 77,
-    PROFILE_HIGH    = 100,
-    PROFILE_HIGH10  = 110,
-    PROFILE_HIGH422 = 122,
-    PROFILE_HIGH444_PREDICTIVE = 244,
-};
-
 #endif  //HB_H264_COMMON_H
index ccaacbfe1992ef536e34dc3baa989eccfee374a1..cef42f9964f913ed92ec49186195ebf38836d355 100644 (file)
@@ -26,8 +26,6 @@ struct hb_mux_object_s
     hb_job_t * job;
 
     mk_Writer * file;
-
-    int wait_for_sps_pps;
 };
 
 struct hb_mux_data_s
@@ -138,23 +136,8 @@ static int MKVInit( hb_mux_object_t * m )
     switch (job->vcodec)
     {
         case HB_VCODEC_X264:
-            // we already have the SPS/PPS, write it now
-            avcC = create_h264_header(job, &avcC_len);
-            if (avcC == NULL)
-            {
-                free(track);
-                return -1;
-            }
-            track->codecID          = MK_VCODEC_MP4AVC;
-            track->codecPrivate     = avcC;
-            track->codecPrivateSize = avcC_len;
-            if (job->areBframes)
-                track->minCache = 1;
-            break;
         case HB_VCODEC_QSV_H264:
-            // we don't have the SPS/PPS yet
-            avcC     = calloc(1, MAX_AVCC_LEN); // allocate here, write later
-            avcC_len = MAX_AVCC_LEN;
+            avcC = create_h264_header(job, &avcC_len);
             if (avcC == NULL)
             {
                 free(track);
@@ -163,7 +146,6 @@ static int MKVInit( hb_mux_object_t * m )
             track->codecID          = MK_VCODEC_MP4AVC;
             track->codecPrivate     = avcC;
             track->codecPrivateSize = avcC_len;
-            m->wait_for_sps_pps     = 1;
             if (job->areBframes)
                 track->minCache = 1;
             break;
@@ -486,33 +468,6 @@ static int MKVMux(hb_mux_object_t *m, hb_mux_data_t *mux_data, hb_buffer_t *buf)
     ogg_packet *op    = NULL;
     hb_job_t *job     = m->job;
 
-    if (m->wait_for_sps_pps && (job->vcodec & HB_VCODEC_H264_MASK))
-    {
-#ifdef USE_QSV
-        if (job->vcodec != HB_VCODEC_QSV_H264 || (job->qsv            != NULL &&
-                                                  job->qsv->enc_space != NULL &&
-                                                  job->qsv->enc_space->is_init_done))
-#endif
-        {
-            // add the SPS/PPS now that we know it
-            int avcC_len;
-            uint8_t *avcC = create_h264_header(job, &avcC_len);
-            if (avcC != NULL)
-            {
-                if (mk_updateTrackPrivateData(m->file, job->mux_data->track,
-                                              avcC, avcC_len) < 0)
-                {
-                    hb_log("MKVMux: failed to update SPS/PPS (H.264)");
-                }
-            }
-            else
-            {
-                hb_log("MKVMux: create_h264_header() failed");
-            }
-            m->wait_for_sps_pps = 0;
-        }
-    }
-
     if (mux_data == job->mux_data)
     {
         /* Video */
@@ -746,11 +701,6 @@ static int MKVEnd(hb_mux_object_t *m)
         *job->die = 1;
     }
 
-    if (m->wait_for_sps_pps && (job->vcodec & HB_VCODEC_H264_MASK))
-    {
-        hb_log("MKVEnd: warning: we didn't get any SPS/PPS (H.264)");
-    }
-
     // TODO: Free what we alloc'd
 
     return 0;
@@ -763,6 +713,5 @@ hb_mux_object_t * hb_mux_mkv_init( hb_job_t * job )
     m->mux       = MKVMux;
     m->end       = MKVEnd;
     m->job       = job;
-    m->wait_for_sps_pps = 0;
     return m;
 }
index 08a1a892766e9a70cf9dc89706d4b8925aab0dd6..cbe094e3ebb145d0976e5ed04f97497883104cd2 100644 (file)
@@ -29,8 +29,6 @@ struct hb_mux_object_s
     MP4TrackId chapter_track;
     int current_chapter;
     uint64_t chapter_duration;
-
-    int wait_for_sps_pps;
 };
 
 struct hb_mux_data_s
@@ -152,21 +150,13 @@ static int MP4Init( hb_mux_object_t * m )
             return 0;
         }
 
-        if (job->vcodec == HB_VCODEC_QSV_H264)
-        {
-            // we don't have the SPS/PPS yet
-            m->wait_for_sps_pps = 1;
-        }
-        else
-        {
-            // we already have the SPS/PPS, write it now
-            MP4AddH264SequenceParameterSet(m->file, mux_data->track,
-                                           job->config.h264.sps,
-                                           job->config.h264.sps_length);
-            MP4AddH264PictureParameterSet(m->file, mux_data->track,
-                                          job->config.h264.pps,
-                                          job->config.h264.pps_length);
-               }
+        // write SPS/PPS
+        MP4AddH264SequenceParameterSet(m->file, mux_data->track,
+                                       job->config.h264.sps,
+                                       job->config.h264.sps_length);
+        MP4AddH264PictureParameterSet(m->file, mux_data->track,
+                                      job->config.h264.pps,
+                                      job->config.h264.pps_length);
 
                if( job->ipod_atom )
                {
@@ -922,26 +912,6 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
     int64_t offset = 0;
     hb_buffer_t *tmp;
 
-    if (m->wait_for_sps_pps && (job->vcodec & HB_VCODEC_H264_MASK))
-    {
-#ifdef USE_QSV
-        if (job->vcodec != HB_VCODEC_QSV_H264 || (job->qsv            != NULL &&
-                                                  job->qsv->enc_space != NULL &&
-                                                  job->qsv->enc_space->is_init_done))
-#endif
-        {
-            // add the SPS/PPS now that we know it
-            MP4AddH264SequenceParameterSet(m->file, job->mux_data->track,
-                                           job->config.h264.sps,
-                                           job->config.h264.sps_length);
-            
-            MP4AddH264PictureParameterSet(m->file, job->mux_data->track,
-                                          job->config.h264.pps,
-                                          job->config.h264.pps_length);
-            m->wait_for_sps_pps = 0;
-        }
-       }
-
     if( mux_data == job->mux_data )
     {
         /* Video */
@@ -1421,11 +1391,6 @@ static int MP4End( hb_mux_object_t * m )
 
         MP4Close( m->file );
 
-        if (m->wait_for_sps_pps && (job->vcodec & HB_VCODEC_H264_MASK))
-        {
-            hb_log("MP4End: warning: we didn't get any SPS/PPS (H.264)");
-        }
-
         if ( job->mp4_optimize )
         {
             hb_log( "muxmp4: optimizing file" );
@@ -1447,7 +1412,6 @@ hb_mux_object_t * hb_mux_mp4_init( hb_job_t * job )
     m->mux       = MP4Mux;
     m->end       = MP4End;
     m->job       = job;
-    m->wait_for_sps_pps = 0;
     return m;
 }
 
index 9460f53eb8e46a61fe542d26a8be881d7d9c6f53..fdda9abf09aaf089350420ef90352ecd066d41b4 100644 (file)
@@ -153,8 +153,8 @@ int hb_qsv_info_init()
         {
             if (hb_qsv_info->cpu_platform == HB_CPU_PLATFORM_INTEL_HSW)
             {
-                hb_qsv_info->capabilities |= HB_QSV_CAP_OPTION2_LOOKAHEAD;
                 hb_qsv_info->capabilities |= HB_QSV_CAP_OPTION2_TRELLIS;
+                hb_qsv_info->capabilities |= HB_QSV_CAP_OPTION2_LOOKAHEAD;
             }
         }
         if (hb_qsv_info->cpu_platform == HB_CPU_PLATFORM_INTEL_HSW)
@@ -317,6 +317,7 @@ int hb_qsv_atoindex(const char* const *arr, const char *str, int *err)
     *err = (arr[i] == NULL);
     return i;
 }
+
 // adapted from libx264
 int hb_qsv_atobool(const char *str, int *err)
 {
@@ -335,6 +336,8 @@ int hb_qsv_atobool(const char *str, int *err)
     *err = 1;
     return 0;
 }
+
+// adapted from libx264
 int hb_qsv_atoi(const char *str, int *err)
 {
     char *end;
@@ -345,6 +348,8 @@ int hb_qsv_atoi(const char *str, int *err)
     }
     return v;
 }
+
+// adapted from libx264
 float hb_qsv_atof(const char *str, int *err)
 {
     char *end;
@@ -355,3 +360,506 @@ float hb_qsv_atof(const char *str, int *err)
     }
     return v;
 }
+
+int hb_qsv_param_parse(hb_qsv_param_t *param,
+                       const char *key, const char *value, int vcodec)
+{
+    float fvalue;
+    int ivalue, error = 0;
+    if (param == NULL)
+    {
+        return HB_QSV_PARAM_ERROR;
+    }
+    if (value == NULL || value[0] == '\0')
+    {
+        value = "true";
+    }
+    else if (value[0] == '=')
+    {
+        value++;
+    }
+    if (key == NULL || key[0] == '\0')
+    {
+        return HB_QSV_PARAM_BAD_NAME;
+    }
+    else if (!strncasecmp(key, "no-", 3))
+    {
+        key  += 3;
+        value = hb_qsv_atobool(value, &error) ? "false" : "true";
+        if (error)
+        {
+            return HB_QSV_PARAM_BAD_VALUE;
+        }
+    }
+    if (!strcasecmp(key, "target-usage"))
+    {
+        ivalue = hb_qsv_atoi(value, &error);
+        if (!error)
+        {
+            param->videoParam->mfx.TargetUsage = HB_QSV_CLIP3(MFX_TARGETUSAGE_1,
+                                                              MFX_TARGETUSAGE_7,
+                                                              ivalue);
+        }
+    }
+    else if (!strcasecmp(key, "num-ref-frame"))
+    {
+        ivalue = hb_qsv_atoi(value, &error);
+        if (!error)
+        {
+            param->videoParam->mfx.NumRefFrame = HB_QSV_CLIP3(0, 16, ivalue);
+        }
+    }
+    else if (!strcasecmp(key, "gop-ref-dist"))
+    {
+        ivalue = hb_qsv_atoi(value, &error);
+        if (!error)
+        {
+            param->videoParam->mfx.GopRefDist = HB_QSV_CLIP3(0, 32, ivalue);
+        }
+    }
+    else if (!strcasecmp(key, "gop-pic-size"))
+    {
+        ivalue = hb_qsv_atoi(value, &error);
+        if (!error)
+        {
+            param->gop.gop_pic_size = HB_QSV_CLIP3(-1, UINT16_MAX, ivalue);
+        }
+    }
+    /* not yet validated
+    else if (!strcasecmp(key, "scenecut"))
+    {
+        ivalue = hb_qsv_atobool(value, &error);
+        if (!error)
+        {
+            if (!ivalue)
+            {
+                param->videoParam->mfx.GopOptFlag |= MFX_GOP_STRICT;
+            }
+            else
+            {
+                param->videoParam->mfx.GopOptFlag &= ~MFX_GOP_STRICT;
+            }
+        }
+    }*/
+    else if (!strcasecmp(key, "cqp-offset-i"))
+    {
+        ivalue = hb_qsv_atoi(value, &error);
+        if (!error)
+        {
+            param->rc.cqp_offsets[0] = HB_QSV_CLIP3(INT16_MIN, INT16_MAX, ivalue);
+        }
+    }
+    else if (!strcasecmp(key, "cqp-offset-p"))
+    {
+        ivalue = hb_qsv_atoi(value, &error);
+        if (!error)
+        {
+            param->rc.cqp_offsets[1] = HB_QSV_CLIP3(INT16_MIN, INT16_MAX, ivalue);
+        }
+    }
+    else if (!strcasecmp(key, "cqp-offset-b"))
+    {
+        ivalue = hb_qsv_atoi(value, &error);
+        if (!error)
+        {
+            param->rc.cqp_offsets[2] = HB_QSV_CLIP3(INT16_MIN, INT16_MAX, ivalue);
+        }
+    }
+    else if (!strcasecmp(key, "vbv-init"))
+    {
+        fvalue = hb_qsv_atof(value, &error);
+        if (!error)
+        {
+            param->rc.vbv_buffer_init = HB_QSV_CLIP3(0, UINT16_MAX, fvalue);
+        }
+    }
+    else if (!strcasecmp(key, "vbv-bufsize"))
+    {
+        ivalue = hb_qsv_atoi(value, &error);
+        if (!error)
+        {
+            param->rc.vbv_buffer_size = HB_QSV_CLIP3(0, UINT16_MAX, ivalue);
+        }
+    }
+    else if (!strcasecmp(key, "vbv-maxrate"))
+    {
+        ivalue = hb_qsv_atoi(value, &error);
+        if (!error)
+        {
+            param->rc.vbv_max_bitrate = HB_QSV_CLIP3(0, UINT16_MAX, ivalue);
+        }
+    }
+    /* not yet validated
+    else if (!strcasecmp(key, "cabac"))
+    {
+        switch (vcodec)
+        {
+            case HB_VCODEC_QSV_H264:
+                ivalue = !hb_qsv_atobool(value, &error);
+                break;
+            default:
+                return HB_QSV_PARAM_UNSUPPORTED;
+        }
+        if (!error)
+        {
+            param->codingOption.CAVLC = hb_qsv_codingoption_xlat(ivalue);
+        }
+    }*/
+    /* not yet validated
+    else if (!strcasecmp(key, "rdo"))
+    {
+        switch (vcodec)
+        {
+            case HB_VCODEC_QSV_H264:
+                ivalue = hb_qsv_atoi(value, &error);
+                break;
+            default:
+                return HB_QSV_PARAM_UNSUPPORTED;
+        }
+        if (!error)
+        {
+            param->codingOption.RateDistortionOpt = hb_qsv_codingoption_xlat(ivalue);
+        }
+    }*/
+    /* not yet validated
+    else if (!strcasecmp(key, "videoformat"))
+    {
+        switch (vcodec)
+        {
+            case HB_VCODEC_QSV_H264:
+                ivalue = hb_qsv_atoindex(x264_vidformat_names, value, &error);
+                break;
+            default:
+                return HB_QSV_PARAM_UNSUPPORTED;
+        }
+        if (!error)
+        {
+            param->videoSignalInfo.VideoFormat = ivalue;
+        }
+    }
+    else if (!strcasecmp(key, "fullrange"))
+    {
+        switch (vcodec)
+        {
+            case HB_VCODEC_QSV_H264:
+                ivalue = hb_qsv_atoindex(x264_fullrange_names, value, &error);
+                break;
+            default:
+                return HB_QSV_PARAM_UNSUPPORTED;
+        }
+        if (!error)
+        {
+            param->videoSignalInfo.VideoFullRange = ivalue;
+        }
+    }
+    else if (!strcasecmp(key, "colorprim"))
+    {
+        switch (vcodec)
+        {
+            case HB_VCODEC_QSV_H264:
+                ivalue = hb_qsv_atoindex(x264_colorprim_names, value, &error);
+                break;
+            default:
+                return HB_QSV_PARAM_UNSUPPORTED;
+        }
+        if (!error)
+        {
+            param->videoSignalInfo.ColourDescriptionPresent = 1;
+            param->videoSignalInfo.ColourPrimaries = ivalue;
+        }
+    }
+    else if (!strcasecmp(key, "transfer"))
+    {
+        switch (vcodec)
+        {
+            case HB_VCODEC_QSV_H264:
+                ivalue = hb_qsv_atoindex(x264_transfer_names, value, &error);
+                break;
+            default:
+                return HB_QSV_PARAM_UNSUPPORTED;
+        }
+        if (!error)
+        {
+            param->videoSignalInfo.ColourDescriptionPresent = 1;
+            param->videoSignalInfo.TransferCharacteristics = ivalue;
+        }
+    }
+    else if (!strcasecmp(key, "colormatrix"))
+    {
+        switch (vcodec)
+        {
+            case HB_VCODEC_QSV_H264:
+                ivalue = hb_qsv_atoindex(x264_colmatrix_names, value, &error);
+                break;
+            default:
+                return HB_QSV_PARAM_UNSUPPORTED;
+        }
+        if (!error)
+        {
+            param->videoSignalInfo.ColourDescriptionPresent = 1;
+            param->videoSignalInfo.MatrixCoefficients = ivalue;
+        }
+    }*/
+    /* not yet validated
+    else if (!strcasecmp(key, "tff") ||
+             !strcasecmp(key, "interlaced"))
+    {
+        switch (vcodec)
+        {
+            case HB_VCODEC_QSV_H264:
+                ivalue = hb_qsv_atobool(value, &error);
+                break;
+            default:
+                return HB_QSV_PARAM_UNSUPPORTED;
+        }
+        if (!error)
+        {
+            param->videoParam->mfx.FrameInfo.PicStruct = (ivalue                  ?
+                                                          MFX_PICSTRUCT_FIELD_TFF :
+                                                          MFX_PICSTRUCT_PROGRESSIVE);
+        }
+    }
+    else if (!strcasecmp(key, "bff"))
+    {
+        switch (vcodec)
+        {
+            case HB_VCODEC_QSV_H264:
+                ivalue = hb_qsv_atobool(value, &error);
+                break;
+            default:
+                return HB_QSV_PARAM_UNSUPPORTED;
+        }
+        if (!error)
+        {
+            param->videoParam->mfx.FrameInfo.PicStruct = (ivalue                  ?
+                                                          MFX_PICSTRUCT_FIELD_BFF :
+                                                          MFX_PICSTRUCT_PROGRESSIVE);
+        }
+    }*/
+    else if (!strcasecmp(key, "mbbrc"))
+    {
+        if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_BRC)
+        {
+            ivalue = hb_qsv_atoi(value, &error);
+            if (!error)
+            {
+                param->codingOption2.MBBRC = hb_qsv_codingoption_xlat(ivalue);
+            }
+        }
+        else
+        {
+            return HB_QSV_PARAM_UNSUPPORTED;
+        }
+    }
+    else if (!strcasecmp(key, "extbrc"))
+    {
+        if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_BRC)
+        {
+            ivalue = hb_qsv_atoi(value, &error);
+            if (!error)
+            {
+                param->codingOption2.ExtBRC = hb_qsv_codingoption_xlat(ivalue);
+            }
+        }
+        else
+        {
+            return HB_QSV_PARAM_UNSUPPORTED;
+        }
+    }
+    else if (!strcasecmp(key, "lookahead"))
+    {
+        switch (vcodec)
+        {
+            case HB_VCODEC_QSV_H264:
+                ivalue = hb_qsv_atobool(value, &error);
+                break;
+            default:
+                return HB_QSV_PARAM_UNSUPPORTED;
+        }
+        if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_LOOKAHEAD)
+        {
+            if (!error)
+            {
+                param->rc.lookahead = ivalue;
+            }
+        }
+        else
+        {
+            return HB_QSV_PARAM_UNSUPPORTED;
+        }
+    }
+    else if (!strcasecmp(key, "lookahead-depth"))
+    {
+        switch (vcodec)
+        {
+            case HB_VCODEC_QSV_H264:
+                ivalue = hb_qsv_atoi(value, &error);
+                break;
+            default:
+                return HB_QSV_PARAM_UNSUPPORTED;
+        }
+        if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_LOOKAHEAD)
+        {
+            if (!error)
+            {
+                // this requires a LOT of video memory - documentation says
+                // 10-100 but limit it to 50 to avoid running out of memory
+                param->codingOption2.LookAheadDepth = HB_QSV_CLIP3(10, 50,
+                                                                   ivalue);
+            }
+        }
+        else
+        {
+            return HB_QSV_PARAM_UNSUPPORTED;
+        }
+    }
+    else if (!strcasecmp(key, "trellis"))
+    {
+        switch (vcodec)
+        {
+            case HB_VCODEC_QSV_H264:
+                ivalue = hb_qsv_atoi(value, &error);
+                break;
+            default:
+                return HB_QSV_PARAM_UNSUPPORTED;
+        }
+        if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_TRELLIS)
+        {
+            if (!error)
+            {
+                param->codingOption2.Trellis = hb_qsv_trellisvalue_xlat(ivalue);
+            }
+        }
+        else
+        {
+            return HB_QSV_PARAM_UNSUPPORTED;
+        }
+    }
+    else
+    {
+        /*
+         * TODO:
+         * - slice count control
+         * - open-gop
+         * - fake-interlaced (mfxExtCodingOption.FramePicture???)
+         * - intra-refresh
+         */
+        return HB_QSV_PARAM_BAD_NAME;
+    }
+    return error ? HB_QSV_PARAM_BAD_VALUE : HB_QSV_PARAM_OK;
+}
+
+int hb_qsv_param_default(hb_qsv_param_t *param, mfxVideoParam *videoParam)
+{
+    if (param != NULL && videoParam != NULL)
+    {
+        // introduced in API 1.0
+        memset(&param->codingOption, 0, sizeof(mfxExtCodingOption));
+        param->codingOption.Header.BufferId      = MFX_EXTBUFF_CODING_OPTION;
+        param->codingOption.Header.BufferSz      = sizeof(mfxExtCodingOption);
+        param->codingOption.MECostType           = 0; // reserved, must be 0
+        param->codingOption.MESearchType         = 0; // reserved, must be 0
+        param->codingOption.MVSearchWindow.x     = 0; // reserved, must be 0
+        param->codingOption.MVSearchWindow.y     = 0; // reserved, must be 0
+        param->codingOption.RefPicListReordering = 0; // reserved, must be 0
+        param->codingOption.IntraPredBlockSize   = 0; // reserved, must be 0
+        param->codingOption.InterPredBlockSize   = 0; // reserved, must be 0
+        param->codingOption.MVPrecision          = 0; // reserved, must be 0
+        param->codingOption.EndOfSequence        = MFX_CODINGOPTION_UNKNOWN;
+        param->codingOption.RateDistortionOpt    = MFX_CODINGOPTION_UNKNOWN;
+        param->codingOption.CAVLC                = MFX_CODINGOPTION_UNKNOWN;
+        param->codingOption.ResetRefList         = MFX_CODINGOPTION_UNKNOWN;
+        param->codingOption.MaxDecFrameBuffering = 0; // unspecified
+        param->codingOption.AUDelimiter          = MFX_CODINGOPTION_OFF;
+        param->codingOption.SingleSeiNalUnit     = MFX_CODINGOPTION_UNKNOWN;
+        param->codingOption.PicTimingSEI         = MFX_CODINGOPTION_OFF;
+        param->codingOption.VuiNalHrdParameters  = MFX_CODINGOPTION_UNKNOWN;
+        param->codingOption.FramePicture         = MFX_CODINGOPTION_UNKNOWN;
+        // introduced in API 1.3
+        param->codingOption.RefPicMarkRep        = MFX_CODINGOPTION_UNKNOWN;
+        param->codingOption.FieldOutput          = MFX_CODINGOPTION_UNKNOWN;
+        param->codingOption.NalHrdConformance    = MFX_CODINGOPTION_UNKNOWN;
+        param->codingOption.SingleSeiNalUnit     = MFX_CODINGOPTION_UNKNOWN;
+        param->codingOption.VuiVclHrdParameters  = MFX_CODINGOPTION_UNKNOWN;
+        // introduced in API 1.4
+        param->codingOption.ViewOutput           = MFX_CODINGOPTION_UNKNOWN;
+        // introduced in API 1.6
+        param->codingOption.RecoveryPointSEI     = MFX_CODINGOPTION_UNKNOWN;
+
+        // introduced in API 1.3
+        memset(&param->videoSignalInfo, 0, sizeof(mfxExtVideoSignalInfo));
+        param->videoSignalInfo.Header.BufferId          = MFX_EXTBUFF_VIDEO_SIGNAL_INFO;
+        param->videoSignalInfo.Header.BufferSz          = sizeof(mfxExtVideoSignalInfo);
+        param->videoSignalInfo.VideoFormat              = 5; // undefined
+        param->videoSignalInfo.VideoFullRange           = 0; // TV range
+        param->videoSignalInfo.ColourDescriptionPresent = 0; // don't write to bitstream
+        param->videoSignalInfo.ColourPrimaries          = 2; // undefined
+        param->videoSignalInfo.TransferCharacteristics  = 2; // undefined
+        param->videoSignalInfo.MatrixCoefficients       = 2; // undefined
+
+        // introduced in API 1.6
+        memset(&param->codingOption2, 0, sizeof(mfxExtCodingOption2));
+        param->codingOption2.Header.BufferId = MFX_EXTBUFF_CODING_OPTION2;
+        param->codingOption2.Header.BufferSz = sizeof(mfxExtCodingOption2);
+        param->codingOption2.IntRefType      = 0;
+        param->codingOption2.IntRefCycleSize = 2;
+        param->codingOption2.IntRefQPDelta   = 0;
+        param->codingOption2.MaxFrameSize    = 0;
+        param->codingOption2.BitrateLimit    = MFX_CODINGOPTION_ON;
+        param->codingOption2.ExtBRC          = MFX_CODINGOPTION_OFF;
+        param->codingOption2.MBBRC           = MFX_CODINGOPTION_UNKNOWN;
+        // introduced in API 1.7
+        param->codingOption2.LookAheadDepth  = 40;
+        param->codingOption2.Trellis         = MFX_TRELLIS_UNKNOWN;
+
+        // GOP & rate control
+        param->gop.gop_pic_size       = -1; // set automatically
+        param->gop.int_ref_cycle_size = -1; // set automatically
+        param->rc.lookahead           = -1; // set automatically
+        param->rc.cqp_offsets[0]      =  0;
+        param->rc.cqp_offsets[1]      =  2;
+        param->rc.cqp_offsets[2]      =  4;
+        param->rc.vbv_max_bitrate     =  0;
+        param->rc.vbv_buffer_size     =  0;
+        param->rc.vbv_buffer_init     = .5;
+
+        // introduced in API 1.0
+        memset(videoParam, 0, sizeof(mfxVideoParam));
+        param->videoParam                   = videoParam;
+        param->videoParam->Protected        = 0; // reserved, must be 0
+        param->videoParam->NumExtParam      = 0;
+        param->videoParam->IOPattern        = MFX_IOPATTERN_IN_SYSTEM_MEMORY;
+        param->videoParam->mfx.TargetUsage  = MFX_TARGETUSAGE_2;
+        param->videoParam->mfx.GopOptFlag   = MFX_GOP_CLOSED;
+        param->videoParam->mfx.NumThread    = 0; // deprecated, must be 0
+        param->videoParam->mfx.EncodedOrder = 0; // input is in display order
+        param->videoParam->mfx.IdrInterval  = 0; // all I-frames are IDR
+        param->videoParam->mfx.NumSlice     = 0; // use Media SDK default
+        param->videoParam->mfx.NumRefFrame  = 0; // use Media SDK default
+        param->videoParam->mfx.GopPicSize   = 0; // use Media SDK default
+        param->videoParam->mfx.GopRefDist   = 4; // power of 2, >= 4: B-pyramid
+        // introduced in API 1.1
+        param->videoParam->AsyncDepth = AV_QSV_ASYNC_DEPTH_DEFAULT;
+        // introduced in API 1.3
+        param->videoParam->mfx.BRCParamMultiplier = 0; // no multiplier
+
+        // FrameInfo: set by video encoder, except PicStruct
+        param->videoParam->mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
+
+        // attach supported mfxExtBuffer structures to the mfxVideoParam
+        param->videoParam->NumExtParam                                = 0;
+        param->videoParam->ExtParam                                   = param->ExtParamArray;
+        param->videoParam->ExtParam[param->videoParam->NumExtParam++] = (mfxExtBuffer*)&param->codingOption;
+        param->videoParam->ExtParam[param->videoParam->NumExtParam++] = (mfxExtBuffer*)&param->videoSignalInfo;
+        if (hb_qsv_info->capabilities & HB_QSV_CAP_MSDK_API_1_6)
+        {
+            param->videoParam->ExtParam[param->videoParam->NumExtParam++] = (mfxExtBuffer*)&param->codingOption2;
+        }
+    }
+    else
+    {
+        hb_error("hb_qsv_param_default: invalid pointer(s)");
+        return -1;
+    }
+    return 0;
+}
index ecc7f6ff1fd7501f1e502c7eb68f313cca270403..a0c582909792bec827d0fad8de9dc7253658f1e6 100644 (file)
@@ -65,6 +65,58 @@ const char* hb_qsv_decode_get_codec_name(enum AVCodecID codec_id);
 int hb_qsv_decode_is_enabled(hb_job_t *job);
 int hb_qsv_decode_is_supported(enum AVCodecID codec_id, enum AVPixelFormat pix_fmt);
 
+/* Media SDK parameters handling */
+enum
+{
+    HB_QSV_PARAM_OK,
+    HB_QSV_PARAM_ERROR,
+    HB_QSV_PARAM_BAD_NAME,
+    HB_QSV_PARAM_BAD_VALUE,
+    HB_QSV_PARAM_UNSUPPORTED,
+};
+
+typedef struct
+{
+    /*
+     * Supported mfxExtBuffer.BufferId values:
+     *
+     * MFX_EXTBUFF_AVC_REFLIST_CTRL
+     * MFX_EXTBUFF_AVC_TEMPORAL_LAYERS
+     * MFX_EXTBUFF_CODING_OPTION
+     * MFX_EXTBUFF_CODING_OPTION_SPSPPS
+     * MFX_EXTBUFF_CODING_OPTION2
+     * MFX_EXTBUFF_ENCODER_CAPABILITY
+     * MFX_EXTBUFF_ENCODER_RESET_OPTION
+     * MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION
+     * MFX_EXTBUFF_PICTURE_TIMING_SEI
+     * MFX_EXTBUFF_VIDEO_SIGNAL_INFO
+     *
+     * This should cover all encode-compatible extended
+     * buffers that can be attached to an mfxVideoParam.
+     */
+#define HB_QSV_ENC_NUM_EXT_PARAM_MAX 10
+    mfxExtBuffer*         ExtParamArray[HB_QSV_ENC_NUM_EXT_PARAM_MAX];
+    mfxExtCodingOption    codingOption;
+    mfxExtCodingOption2   codingOption2;
+    mfxExtVideoSignalInfo videoSignalInfo;
+    struct
+    {
+        int gop_pic_size;
+        int int_ref_cycle_size;
+    } gop;
+    struct
+    {
+        int   lookahead;
+        int   cqp_offsets[3];
+        int   vbv_max_bitrate;
+        int   vbv_buffer_size;
+        float vbv_buffer_init;
+    } rc;
+
+    // assigned via hb_qsv_param_default, may be shared with another structure
+    mfxVideoParam *videoParam;
+} hb_qsv_param_t;
+
 #define HB_QSV_CLIP3(min, max, val) ((val < min) ? min : (val > max) ? max : val)
 int   hb_qsv_codingoption_xlat(int val);
 int   hb_qsv_trellisvalue_xlat(int val);
@@ -73,4 +125,7 @@ int   hb_qsv_atobool (const char *str, int *err);
 int   hb_qsv_atoi    (const char *str, int *err);
 float hb_qsv_atof    (const char *str, int *err);
 
+int hb_qsv_param_default(hb_qsv_param_t *param, mfxVideoParam *videoParam);
+int hb_qsv_param_parse  (hb_qsv_param_t *param, const char *key, const char *value, int vcodec);
+
 #endif