]> granicus.if.org Git - handbrake/commitdiff
QSV: API 1.8 support and related improvements
authorRodeo <tdskywalker@gmail.com>
Tue, 18 Feb 2014 17:14:14 +0000 (17:14 +0000)
committerRodeo <tdskywalker@gmail.com>
Tue, 18 Feb 2014 17:14:14 +0000 (17:14 +0000)
Optional feature detection now done via MFXVideoENCODE_Query. This should work better with future hardware and encoder implementations.

Optional API 1.8 features are not tested (not supported by the software implementation, and no drivers with API 1.8 support available yet).

git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@6041 b64f7644-9d1e-0410-96f1-a4d463321fa5

libhb/common.c
libhb/decavcodec.c
libhb/enc_qsv.c
libhb/h264_common.h
libhb/qsv_common.c
libhb/qsv_common.h
libhb/work.c
test/test.c

index 6dbdde75c70162fd1d5600b163dbb2b07e49404e..3ca5c58b525fc770d435ac082df9f10d0a3cd7f0 100644 (file)
@@ -215,13 +215,14 @@ hb_encoder_internal_t hb_video_encoders[]  =
 int hb_video_encoders_count = sizeof(hb_video_encoders) / sizeof(hb_video_encoders[0]);
 static int hb_video_encoder_is_enabled(int encoder)
 {
-    switch (encoder)
-    {
 #ifdef USE_QSV
-        case HB_VCODEC_QSV_H264:
-            return hb_qsv_available();
+    if (encoder & HB_VCODEC_QSV_MASK)
+    {
+        return hb_qsv_video_encoder_is_enabled(encoder);
+    }
 #endif
-
+    switch (encoder)
+    {
         // the following encoders are always enabled
         case HB_VCODEC_X264:
         case HB_VCODEC_THEORA:
@@ -1119,6 +1120,14 @@ const hb_rate_t* hb_audio_bitrate_get_next(const hb_rate_t *last)
 void hb_video_quality_get_limits(uint32_t codec, float *low, float *high,
                                  float *granularity, int *direction)
 {
+#ifdef USE_QSV
+    if (codec & HB_VCODEC_QSV_MASK)
+    {
+        return hb_qsv_video_quality_get_limits(codec, low, high, granularity,
+                                               direction);
+    }
+#endif
+
     switch (codec)
     {
         case HB_VCODEC_X264:
@@ -1151,6 +1160,13 @@ void hb_video_quality_get_limits(uint32_t codec, float *low, float *high,
 
 const char* hb_video_quality_get_name(uint32_t codec)
 {
+#ifdef USE_QSV
+    if (codec & HB_VCODEC_QSV_MASK)
+    {
+        return hb_qsv_video_quality_get_name(codec);
+    }
+#endif
+
     switch (codec)
     {
         case HB_VCODEC_X264:
@@ -1166,16 +1182,18 @@ const char* hb_video_quality_get_name(uint32_t codec)
 
 const char* const* hb_video_encoder_get_presets(int encoder)
 {
+#ifdef USE_QSV
+    if (encoder & HB_VCODEC_QSV_MASK)
+    {
+        return hb_qsv_preset_get_names();
+    }
+#endif
+
     switch (encoder)
     {
         case HB_VCODEC_X264:
             return x264_preset_names;
 
-#ifdef USE_QSV
-        case HB_VCODEC_QSV_H264:
-            return hb_qsv_preset_get_names();
-#endif
-
 #ifdef USE_X265
         case HB_VCODEC_X265:
             return x265_preset_names;
@@ -1203,10 +1221,16 @@ const char* const* hb_video_encoder_get_tunes(int encoder)
 
 const char* const* hb_video_encoder_get_profiles(int encoder)
 {
+#ifdef USE_QSV
+    if (encoder & HB_VCODEC_QSV_MASK)
+    {
+        return hb_qsv_profile_get_names(encoder);
+    }
+#endif
+
     switch (encoder)
     {
         case HB_VCODEC_X264:
-        case HB_VCODEC_QSV_H264:
             return hb_h264_profile_names;
 
 #ifdef USE_X265
@@ -1220,10 +1244,16 @@ const char* const* hb_video_encoder_get_profiles(int encoder)
 
 const char* const* hb_video_encoder_get_levels(int encoder)
 {
+#ifdef USE_QSV
+    if (encoder & HB_VCODEC_QSV_MASK)
+    {
+        return hb_qsv_level_get_names(encoder);
+    }
+#endif
+
     switch (encoder)
     {
         case HB_VCODEC_X264:
-        case HB_VCODEC_QSV_H264:
             return hb_h264_level_names;
 
         default:
@@ -3050,7 +3080,6 @@ static void job_setup( hb_job_t * job, hb_title_t * title )
 
 #ifdef USE_QSV
     job->qsv.enc_info.is_init_done = 0;
-    job->qsv.preset                = NULL;
     job->qsv.async_depth           = AV_QSV_ASYNC_DEPTH_DEFAULT;
     job->qsv.decode                = !!(title->video_decode_support &
                                         HB_DECODE_SUPPORT_QSV);
index c158bcf1b3dbf6d5ac275de5e62dcf3b7a49d4f1..410283e4fce7103852cb4268e297b8642b2a51a3 100644 (file)
@@ -1518,20 +1518,25 @@ static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job )
 #ifdef USE_QSV
     if (hb_qsv_decode_is_enabled(job))
     {
-        // setup the QSV configuration
-        pv->qsv.config.io_pattern         = MFX_IOPATTERN_OUT_OPAQUE_MEMORY;
-        pv->qsv.config.impl_requested     = hb_qsv_impl_get_preferred();
-        pv->qsv.config.async_depth        = job->qsv.async_depth;
-        pv->qsv.config.sync_need          =  0;
-        pv->qsv.config.usage_threaded     =  1;
-        pv->qsv.config.additional_buffers = 64; // FIFO_LARGE
-        if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_LOOKAHEAD)
-        {
-            // more surfaces may be needed for the lookahead
-            pv->qsv.config.additional_buffers = 160;
-        }
-        pv->qsv.codec_name = hb_qsv_decode_get_codec_name(w->codec_param);
-        pv->qsv.decode     = 1;
+        // determine which encoder we're using
+        hb_qsv_info_t *info = hb_qsv_info_get(job->vcodec);
+        pv->qsv.decode = info != NULL;
+        if (pv->qsv.decode)
+        {
+            // setup the QSV configuration
+            pv->qsv.config.io_pattern         = MFX_IOPATTERN_OUT_OPAQUE_MEMORY;
+            pv->qsv.config.impl_requested     = info->implementation;
+            pv->qsv.config.async_depth        = job->qsv.async_depth;
+            pv->qsv.config.sync_need          =  0;
+            pv->qsv.config.usage_threaded     =  1;
+            pv->qsv.config.additional_buffers = 64; // FIFO_LARGE
+            if (info->capabilities & HB_QSV_CAP_RATECONTROL_LA)
+            {
+                // more surfaces may be needed for the lookahead
+                pv->qsv.config.additional_buffers = 160;
+            }
+            pv->qsv.codec_name = hb_qsv_decode_get_codec_name(w->codec_param);
+        }
     }
     else
     {
index 3b745744a82d0522611f00dc0ff26d07a524fb94..c3129b74977aff8d969c3450c982868f0b42222e 100644 (file)
@@ -56,6 +56,7 @@ struct hb_work_private_s
 
     hb_qsv_param_t param;
     av_qsv_space enc_space;
+    hb_qsv_info_t *qsv_info;
 
     mfxEncodeCtrl force_keyframe;
     hb_list_t *delayed_chapters;
@@ -361,6 +362,7 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
 
     pv->job                = job;
     pv->is_sys_mem         = !hb_qsv_decode_is_enabled(job);
+    pv->qsv_info           = hb_qsv_info_get(job->vcodec);
     pv->delayed_processing = hb_list_init();
     pv->last_start         = INT64_MIN;
     pv->frames_in          = 0;
@@ -381,7 +383,7 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
 
     // default encoding parameters
     if (hb_qsv_param_default_preset(&pv->param, &pv->enc_space.m_mfxVideoParam,
-                                    job->encoder_preset))
+                                     pv->qsv_info, job->encoder_preset))
     {
         hb_error("encqsvInit: hb_qsv_param_default_preset failed");
         return -1;
@@ -434,8 +436,8 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
         options_list = hb_encopts_to_dict(job->encoder_options, job->vcodec);
         while ((option = hb_dict_next(options_list, option)) != NULL)
         {
-            switch (hb_qsv_param_parse(&pv->param,
-                                       option->key, option->value, job->vcodec))
+            switch (hb_qsv_param_parse(&pv->param,  pv->qsv_info,
+                                       option->key, option->value))
             {
                 case HB_QSV_PARAM_OK:
                     break;
@@ -551,7 +553,7 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
             hb_error("encqsvInit: bad level %s", job->encoder_level);
             return -1;
         }
-        else if (hb_qsv_info->capabilities & HB_QSV_CAP_MSDK_API_1_6)
+        else if (pv->qsv_info->capabilities & HB_QSV_CAP_MSDK_API_1_6)
         {
             pv->param.videoParam->mfx.CodecLevel = HB_QSV_CLIP3(MFX_LEVEL_AVC_1,
                                                                 MFX_LEVEL_AVC_52,
@@ -587,46 +589,91 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
         }
     }
 
-    // set rate control paremeters
-    if (job->vquality >= 0)
+    // sanitize ICQ
+    if (!(pv->qsv_info->capabilities & HB_QSV_CAP_RATECONTROL_ICQ))
     {
-        // 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]);
-        // CQP + ExtBRC can cause bad output
-        pv->param.codingOption2.ExtBRC = MFX_CODINGOPTION_OFF;
+        // ICQ not supported
+        pv->param.rc.icq = 0;
     }
-    else if (job->vbitrate > 0)
+    else
+    {
+        pv->param.rc.icq = !!pv->param.rc.icq;
+    }
+
+    // sanitize lookahead
+    if (!(pv->qsv_info->capabilities & HB_QSV_CAP_RATECONTROL_LA))
+    {
+        // lookahead not supported
+        pv->param.rc.lookahead = 0;
+    }
+    else if ((pv->param.rc.lookahead)                                       &&
+             (pv->qsv_info->capabilities & HB_QSV_CAP_RATECONTROL_LAi) == 0 &&
+             (pv->param.videoParam->mfx.FrameInfo.PicStruct != MFX_PICSTRUCT_PROGRESSIVE))
     {
-        // sanitize lookahead
-        if (!(hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_LOOKAHEAD))
+        // lookahead enabled but we can't use it
+        hb_log("encqsvInit: LookAhead not used (LookAhead is progressive-only)");
+        pv->param.rc.lookahead = 0;
+    }
+    else
+    {
+        pv->param.rc.lookahead = !!pv->param.rc.lookahead;
+    }
+
+    // set VBV here (this will be overridden for CQP and ignored for LA)
+    // only set BufferSizeInKB, InitialDelayInKB and MaxKbps if we have
+    // them - otheriwse Media SDK will pick 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 if (pv->param.rc.vbv_buffer_init > 0.0)
         {
-            // lookahead not supported
-            pv->param.rc.lookahead = 0;
+            pv->param.videoParam->mfx.InitialDelayInKB = (pv->param.rc.vbv_buffer_size *
+                                                          pv->param.rc.vbv_buffer_init / 8);
         }
-        else if (pv->param.rc.lookahead &&
-                 pv->param.videoParam->mfx.FrameInfo.PicStruct != MFX_PICSTRUCT_PROGRESSIVE)
+        pv->param.videoParam->mfx.BufferSizeInKB = (pv->param.rc.vbv_buffer_size / 8);
+    }
+    if (pv->param.rc.vbv_max_bitrate > 0)
+    {
+        pv->param.videoParam->mfx.MaxKbps = pv->param.rc.vbv_max_bitrate;
+    }
+
+    // set rate control paremeters
+    if (job->vquality >= 0)
+    {
+        if (pv->param.rc.icq)
         {
-            // lookahead enabled but we can't use it
-            hb_log("encqsvInit: MFX_RATECONTROL_LA not used (LookAhead is progressive-only)");
-            pv->param.rc.lookahead = 0;
+            // introduced in API 1.8
+            if (pv->param.rc.lookahead)
+            {
+                pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_LA_ICQ;
+            }
+            else
+            {
+                pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_ICQ;
+            }
+            pv->param.videoParam->mfx.ICQQuality = HB_QSV_CLIP3(1, 51, job->vquality);
         }
         else
         {
-            pv->param.rc.lookahead = !!pv->param.rc.lookahead;
+            // 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]);
+            // CQP + ExtBRC can cause bad output
+            pv->param.codingOption2.ExtBRC = MFX_CODINGOPTION_OFF;
         }
+    }
+    else if (job->vbitrate > 0)
+    {
         if (pv->param.rc.lookahead)
         {
             // 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 ||
-                pv->param.rc.vbv_buffer_size > 0)
-            {
-                hb_log("encqsvInit: MFX_RATECONTROL_LA, ignoring VBV");
-            }
             // ignored, but some drivers will change AsyncDepth because of it
             pv->param.codingOption2.ExtBRC = MFX_CODINGOPTION_OFF;
         }
@@ -641,25 +688,6 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
             {
                 pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_VBR;
             }
-            // only set BufferSizeInKB, InitialDelayInKB and MaxKbps if we have
-            // them - otheriwse Media SDK will pick 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 if (pv->param.rc.vbv_buffer_init > 0.0)
-                {
-                    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 (pv->param.rc.vbv_max_bitrate > 0)
-            {
-                pv->param.videoParam->mfx.MaxKbps = pv->param.rc.vbv_max_bitrate;
-            }
             pv->param.videoParam->mfx.TargetKbps = job->vbitrate;
         }
     }
@@ -670,6 +698,23 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
         return -1;
     }
 
+    // if VBV is enabled but ignored, log it
+    if (pv->param.rc.vbv_max_bitrate > 0 || pv->param.rc.vbv_buffer_size > 0)
+    {
+        switch (pv->param.videoParam->mfx.RateControlMethod)
+        {
+            case MFX_RATECONTROL_LA:
+            case MFX_RATECONTROL_LA_ICQ:
+                hb_log("encqsvInit: LookAhead enabled, ignoring VBV");
+                break;
+            case MFX_RATECONTROL_ICQ:
+                hb_log("encqsvInit: ICQ rate control, ignoring VBV");
+                break;
+            default:
+                break;
+        }
+    }
+
     // set B-pyramid
     if (pv->param.gop.b_pyramid < 0)
     {
@@ -733,7 +778,7 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
                                                        pv->param.rc.lookahead ? 60 : 0);
     }
 
-    if ((hb_qsv_info->capabilities & HB_QSV_CAP_H264_BPYRAMID) &&
+    if ((pv->qsv_info->capabilities & HB_QSV_CAP_B_REF_PYRAMID) &&
         (pv->param.videoParam->mfx.CodecProfile != MFX_PROFILE_AVC_BASELINE         &&
          pv->param.videoParam->mfx.CodecProfile != MFX_PROFILE_AVC_CONSTRAINED_HIGH &&
          pv->param.videoParam->mfx.CodecProfile != MFX_PROFILE_AVC_CONSTRAINED_BASELINE))
@@ -838,7 +883,7 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
     mfxExtCodingOptionSPSPPS sps_pps_buf, *sps_pps = &sps_pps_buf;
     version.Major = HB_QSV_MINVERSION_MAJOR;
     version.Minor = HB_QSV_MINVERSION_MINOR;
-    err = MFXInit(hb_qsv_impl_get_preferred(), &version, &session);
+    err = MFXInit(pv->qsv_info->implementation, &version, &session);
     if (err != MFX_ERR_NONE)
     {
         hb_error("encqsvInit: MFXInit failed (%d)", err);
@@ -886,7 +931,7 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
     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)
+    if (pv->qsv_info->capabilities & HB_QSV_CAP_MSDK_API_1_6)
     {
         // attach to get the final output mfxExtCodingOption2 settings
         videoParam.ExtParam[videoParam.NumExtParam++] = (mfxExtBuffer*)option2;
@@ -949,7 +994,7 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
     // 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 &
+    pv->bfrm_workaround = job->cfr != 1 || !(pv->qsv_info->capabilities &
                                              HB_QSV_CAP_MSDK_API_1_6);
     if (pv->bfrm_delay && pv->bfrm_workaround)
     {
@@ -969,7 +1014,7 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
            videoParam.mfx.TargetUsage, videoParam.AsyncDepth);
     hb_log("encqsvInit: GopRefDist %"PRIu16" GopPicSize %"PRIu16" NumRefFrame %"PRIu16"",
            videoParam.mfx.GopRefDist, videoParam.mfx.GopPicSize, videoParam.mfx.NumRefFrame);
-    if (hb_qsv_info->capabilities & HB_QSV_CAP_H264_BPYRAMID)
+    if (pv->qsv_info->capabilities & HB_QSV_CAP_B_REF_PYRAMID)
     {
         hb_log("encqsvInit: BFrames %s BPyramid %s",
                pv->bfrm_delay                            ? "on" : "off",
@@ -979,6 +1024,20 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
     {
         hb_log("encqsvInit: BFrames %s", pv->bfrm_delay ? "on" : "off");
     }
+    if (pv->qsv_info->capabilities & HB_QSV_CAP_OPTION2_IB_ADAPT)
+    {
+        if (pv->bfrm_delay > 0)
+        {
+            hb_log("encqsvInit: AdaptiveI %s AdaptiveB %s",
+                   hb_qsv_codingoption_get_name(option2->AdaptiveI),
+                   hb_qsv_codingoption_get_name(option2->AdaptiveB));
+        }
+        else
+        {
+            hb_log("encqsvInit: AdaptiveI %s",
+                   hb_qsv_codingoption_get_name(option2->AdaptiveI));
+        }
+    }
     if (videoParam.mfx.RateControlMethod == MFX_RATECONTROL_CQP)
     {
         char qpi[7], qpp[9], qpb[9];
@@ -997,6 +1056,14 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
                 hb_log("encqsvInit: RateControlMethod LA TargetKbps %"PRIu16" LookAheadDepth %"PRIu16"",
                        videoParam.mfx.TargetKbps, option2->LookAheadDepth);
                 break;
+            case MFX_RATECONTROL_LA_ICQ:
+                hb_log("encqsvInit: RateControlMethod LA_ICQ ICQQuality %"PRIu16" LookAheadDepth %"PRIu16"",
+                       videoParam.mfx.ICQQuality, option2->LookAheadDepth);
+                break;
+            case MFX_RATECONTROL_ICQ:
+                hb_log("encqsvInit: RateControlMethod ICQ ICQQuality %"PRIu16"",
+                       videoParam.mfx.ICQQuality);
+                break;
             case MFX_RATECONTROL_CBR:
             case MFX_RATECONTROL_VBR:
                 hb_log("encqsvInit: RateControlMethod %s TargetKbps %"PRIu16" MaxKbps %"PRIu16" BufferSizeInKB %"PRIu16" InitialDelayInKB %"PRIu16"",
@@ -1010,6 +1077,30 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
                 return -1;
         }
     }
+    if ((pv->qsv_info->capabilities & HB_QSV_CAP_OPTION2_LA_DOWNS) &&
+        (videoParam.mfx.RateControlMethod == MFX_RATECONTROL_LA ||
+         videoParam.mfx.RateControlMethod == MFX_RATECONTROL_LA_ICQ))
+    {
+        switch (option2->LookAheadDS)
+        {
+            case MFX_LOOKAHEAD_DS_UNKNOWN:
+                hb_log("encqsvInit: LookAheadDS unknown (auto)");
+                break;
+            case MFX_LOOKAHEAD_DS_OFF:
+                hb_log("encqsvInit: LookAheadDS off");
+                break;
+            case MFX_LOOKAHEAD_DS_2x:
+                hb_log("encqsvInit: LookAheadDS 2x");
+                break;
+            case MFX_LOOKAHEAD_DS_4x:
+                hb_log("encqsvInit: LookAheadDS 4x");
+                break;
+            default:
+                hb_log("encqsvInit: invalid LookAheadDS value 0x%"PRIx16"",
+                       option2->LookAheadDS);
+                break;
+        }
+    }
     switch (videoParam.mfx.FrameInfo.PicStruct)
     {
         // quiet, most people don't care
@@ -1029,22 +1120,22 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
     }
     hb_log("encqsvInit: CAVLC %s",
            hb_qsv_codingoption_get_name(option1->CAVLC));
-    if (videoParam.mfx.RateControlMethod != MFX_RATECONTROL_LA &&
+    if (pv->param.rc.lookahead           == 0 &&
         videoParam.mfx.RateControlMethod != MFX_RATECONTROL_CQP)
     {
         // LA/CQP and ExtBRC/MBBRC are mutually exclusive
-        if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_EXTBRC)
+        if (pv->qsv_info->capabilities & HB_QSV_CAP_OPTION2_EXTBRC)
         {
             hb_log("encqsvInit: ExtBRC %s",
                    hb_qsv_codingoption_get_name(option2->ExtBRC));
         }
-        if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_MBBRC)
+        if (pv->qsv_info->capabilities & HB_QSV_CAP_OPTION2_MBBRC)
         {
             hb_log("encqsvInit: MBBRC %s",
                    hb_qsv_codingoption_get_name(option2->MBBRC));
         }
     }
-    if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_TRELLIS)
+    if (pv->qsv_info->capabilities & HB_QSV_CAP_OPTION2_TRELLIS)
     {
         switch (option2->Trellis)
         {
@@ -1453,9 +1544,9 @@ int encqsvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
             buf->s.duration = duration;
             if (pv->bfrm_delay)
             {
-                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))
+                if ((pv->frames_out == 0)                                  &&
+                    (pv->qsv_info->capabilities & HB_QSV_CAP_MSDK_API_1_6) &&
+                    (pv->qsv_info->capabilities & HB_QSV_CAP_B_REF_PYRAMID))
                 {
                     // with B-pyramid, the delay may be more than 1 frame,
                     // so compute the actual delay based on the initial DTS
index febe1965ff17bb5fb7bd1a5ebaa41008e34c247d..bc64cf9287672fb0b2fbc4edaf5a53d9418035d1 100644 (file)
 #ifndef HB_H264_COMMON_H
 #define HB_H264_COMMON_H
 
-static const char * const hb_h264_profile_names[] = { "auto", "high", "main", "baseline", NULL, };
-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, };
+static const char * const   hb_h264_profile_names[] = { "auto", "high", "main", "baseline", NULL, };
+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, };
+
+// stolen from libx264's x264.h
+static const char * const hb_h264_fullrange_names[] = { "off", "on", NULL, };
+static const char * const hb_h264_vidformat_names[] = { "component", "pal", "ntsc", "secam", "mac", "undef", NULL, };
+static const char * const hb_h264_colorprim_names[] = { "", "bt709", "undef", "", "bt470m", "bt470bg", "smpte170m", "smpte240m", "film", "bt2020", NULL, };
+static const char * const  hb_h264_transfer_names[] = { "", "bt709", "undef", "", "bt470m", "bt470bg", "smpte170m", "smpte240m", "linear", "log100", "log316", "iec61966-2-4", "bt1361e", "iec61966-2-1", "bt2020-10", "bt2020-12", NULL, };
+static const char * const hb_h264_colmatrix_names[] = { "GBR", "bt709", "undef", "", "fcc", "bt470bg", "smpte170m", "smpte240m", "YCgCo", "bt2020nc", "bt2020c", NULL, };
 
 #endif  //HB_H264_COMMON_H
index 195845200052dafee27ec840b3b4dca3c021182e..9a21af41169e7f7c90b5abc64b0c2d7a9fa06358 100644 (file)
@@ -9,6 +9,8 @@
 
 #ifdef USE_QSV
 
+#include <stdio.h>
+
 #include "hb.h"
 #include "ports.h"
 #include "common.h"
 #include "qsv_common.h"
 #include "h264_common.h"
 
-// for x264_vidformat_names etc.
-#include "x264.h"
-
-// avoids a warning
-#include "libavutil/cpu.h"
-extern void ff_cpu_cpuid(int index, int *eax, int *ebx, int *ecx, int *edx);
-
-// make the Intel QSV information available to the UIs
-hb_qsv_info_t *hb_qsv_info = NULL;
-
-// availability and versions
-static mfxIMPL preferred_implementation;
-static mfxVersion qsv_hardware_version;
-static mfxVersion qsv_software_version;
-static mfxVersion qsv_minimum_version;
-static int qsv_hardware_available = 0;
-static int qsv_software_available = 0;
+// QSV info for each codec
+static hb_qsv_info_t *hb_qsv_info_avc       = NULL;
+static hb_qsv_info_t *hb_qsv_info_hevc      = NULL;
+// API versions
+static mfxVersion qsv_software_version      = { .Version   = 0, };
+static mfxVersion qsv_hardware_version      = { .Version   = 0, };
+// AVC implementations
+static hb_qsv_info_t qsv_software_info_avc  = { .available = 0, .codec_id = MFX_CODEC_AVC,  .implementation = MFX_IMPL_SOFTWARE, };
+static hb_qsv_info_t qsv_hardware_info_avc  = { .available = 0, .codec_id = MFX_CODEC_AVC,  .implementation = MFX_IMPL_HARDWARE_ANY|MFX_IMPL_VIA_ANY, };
+// HEVC implementations
+static mfxPluginUID  qsv_encode_plugin_hevc = { .Data      = { 0x2F, 0xCA, 0x99, 0x74, 0x9F, 0xDB, 0x49, 0xAE, 0xB1, 0x21, 0xA5, 0xB6, 0x3E, 0xF5, 0x68, 0xF7 } };
+static hb_qsv_info_t qsv_software_info_hevc = { .available = 0, .codec_id = MFX_CODEC_HEVC, .implementation = MFX_IMPL_SOFTWARE, };
+static hb_qsv_info_t qsv_hardware_info_hevc = { .available = 0, .codec_id = MFX_CODEC_HEVC, .implementation = MFX_IMPL_HARDWARE_ANY|MFX_IMPL_VIA_ANY, };
 
 // check available Intel Media SDK version against a minimum
 #define HB_CHECK_MFX_VERSION(MFX_VERSION, MAJOR, MINOR) \
     (MFX_VERSION.Major == MAJOR  && MFX_VERSION.Minor >= MINOR)
 
+/*
+ * Determine the "generation" of QSV hardware based on the CPU microarchitecture.
+ * Anything unknown is assumed to be more recent than the latest known generation.
+ * This avoids having to order the hb_cpu_platform enum depending on QSV hardware.
+ */
+enum
+{
+    QSV_G0, // third party hardware
+    QSV_G1, // Sandy Bridge or equivalent
+    QSV_G2, // Ivy Bridge or equivalent
+    QSV_G3, // Haswell or equivalent
+};
+static int qsv_hardware_generation(int cpu_platform)
+{
+    switch (cpu_platform)
+    {
+        case HB_CPU_PLATFORM_INTEL_BNL:
+            return QSV_G0;
+        case HB_CPU_PLATFORM_INTEL_SNB:
+            return QSV_G1;
+        case HB_CPU_PLATFORM_INTEL_IVB:
+        case HB_CPU_PLATFORM_INTEL_SLM:
+            return QSV_G2;
+        case HB_CPU_PLATFORM_INTEL_HSW:
+        default:
+            return QSV_G3;
+    }
+}
+
+/*
+ * Determine whether a given mfxIMPL is hardware-accelerated.
+ */
+static int qsv_implementation_is_hardware(mfxIMPL implementation)
+{
+    return MFX_IMPL_BASETYPE(implementation) != MFX_IMPL_SOFTWARE;
+}
+
 int hb_qsv_available()
 {
-    return hb_qsv_info != NULL && (qsv_hardware_available ||
-                                   qsv_software_available);
+    return hb_qsv_video_encoder_is_enabled(HB_VCODEC_QSV_H264);
 }
 
-int hb_qsv_info_init()
+int hb_qsv_video_encoder_is_enabled(int encoder)
 {
-    static int init_done = 0;
-    if (init_done)
-        return (hb_qsv_info == NULL);
-    init_done = 1;
+    switch (encoder)
+    {
+        case HB_VCODEC_QSV_H264:
+            return hb_qsv_info_avc != NULL && hb_qsv_info_avc->available;
+        default:
+            return 0;
+    }
+}
 
-    hb_qsv_info = calloc(1, sizeof(*hb_qsv_info));
-    if (hb_qsv_info == NULL)
+int hb_qsv_audio_encoder_is_enabled(int encoder)
+{
+    switch (encoder)
     {
-        hb_error("hb_qsv_info_init: alloc failure");
-        return -1;
+        default:
+            return 0;
     }
+}
 
-    mfxSession session;
-    qsv_minimum_version.Major = HB_QSV_MINVERSION_MAJOR;
-    qsv_minimum_version.Minor = HB_QSV_MINVERSION_MINOR;
+static void init_video_param(mfxVideoParam *videoParam)
+{
+    if (videoParam == NULL)
+    {
+        return;
+    }
 
-    // check for software fallback
-    if (MFXInit(MFX_IMPL_SOFTWARE,
-                &qsv_minimum_version, &session) == MFX_ERR_NONE)
+    memset(videoParam, 0, sizeof(mfxVideoParam));
+    videoParam->mfx.CodecId                 = MFX_CODEC_AVC;
+    videoParam->mfx.CodecLevel              = MFX_LEVEL_UNKNOWN;
+    videoParam->mfx.CodecProfile            = MFX_PROFILE_UNKNOWN;
+    videoParam->mfx.RateControlMethod       = MFX_RATECONTROL_VBR;
+    videoParam->mfx.TargetUsage             = MFX_TARGETUSAGE_BALANCED;
+    videoParam->mfx.TargetKbps              = 5000;
+    videoParam->mfx.GopOptFlag              = MFX_GOP_CLOSED;
+    videoParam->mfx.FrameInfo.FourCC        = MFX_FOURCC_NV12;
+    videoParam->mfx.FrameInfo.ChromaFormat  = MFX_CHROMAFORMAT_YUV420;
+    videoParam->mfx.FrameInfo.PicStruct     = MFX_PICSTRUCT_PROGRESSIVE;
+    videoParam->mfx.FrameInfo.FrameRateExtN = 25;
+    videoParam->mfx.FrameInfo.FrameRateExtD = 1;
+    videoParam->mfx.FrameInfo.Width         = 1920;
+    videoParam->mfx.FrameInfo.CropW         = 1920;
+    videoParam->mfx.FrameInfo.AspectRatioW  = 1;
+    videoParam->mfx.FrameInfo.Height        = 1088;
+    videoParam->mfx.FrameInfo.CropH         = 1080;
+    videoParam->mfx.FrameInfo.AspectRatioH  = 1;
+    videoParam->AsyncDepth                  = AV_QSV_ASYNC_DEPTH_DEFAULT;
+    videoParam->IOPattern                   = MFX_IOPATTERN_IN_SYSTEM_MEMORY;
+}
+
+static void init_ext_coding_option2(mfxExtCodingOption2 *extCodingOption2)
+{
+    if (extCodingOption2 == NULL)
     {
-        qsv_software_available   = 1;
-        preferred_implementation = MFX_IMPL_SOFTWARE;
-        // our minimum is supported, but query the actual version
-        MFXQueryVersion(session, &qsv_software_version);
-        MFXClose(session);
+        return;
     }
 
-    // check for actual hardware support
-    if (MFXInit(MFX_IMPL_HARDWARE_ANY|MFX_IMPL_VIA_ANY,
-                &qsv_minimum_version, &session) == MFX_ERR_NONE)
+    memset(extCodingOption2, 0, sizeof(mfxExtCodingOption2));
+    extCodingOption2->Header.BufferId = MFX_EXTBUFF_CODING_OPTION2;
+    extCodingOption2->Header.BufferSz = sizeof(mfxExtCodingOption2);
+    extCodingOption2->MBBRC           = MFX_CODINGOPTION_ON;
+    extCodingOption2->ExtBRC          = MFX_CODINGOPTION_ON;
+    extCodingOption2->Trellis         = MFX_TRELLIS_I|MFX_TRELLIS_P|MFX_TRELLIS_B;
+    extCodingOption2->RepeatPPS       = MFX_CODINGOPTION_ON;
+    extCodingOption2->BRefType        = MFX_B_REF_PYRAMID;
+    extCodingOption2->AdaptiveI       = MFX_CODINGOPTION_ON;
+    extCodingOption2->AdaptiveB       = MFX_CODINGOPTION_ON;
+    extCodingOption2->LookAheadDS     = MFX_LOOKAHEAD_DS_4x;
+    extCodingOption2->NumMbPerSlice   = 2040; // 1920x1088/4
+}
+
+static int query_capabilities(mfxSession session, mfxVersion version, hb_qsv_info_t *info)
+{
+    /*
+     * MFXVideoENCODE_Query(mfxSession, mfxVideoParam *in, mfxVideoParam *out);
+     *
+     * Mode 1:
+     * - in is NULL
+     * - out has the parameters we want to query set to 1
+     * - out->mfx.CodecId field has to be set (mandatory)
+     * - MFXVideoENCODE_Query should zero out all unsupported parameters
+     *
+     * Mode 2:
+     * - the paramaters we want to query are set for in
+     * - in ->mfx.CodecId field has to be set (mandatory)
+     * - out->mfx.CodecId field has to be set (mandatory)
+     * - MFXVideoENCODE_Query should sanitize all unsupported parameters
+     */
+    mfxStatus status;
+    mfxPluginUID *pluginUID;
+    mfxExtBuffer *videoExtParam[1];
+    mfxVideoParam videoParam, inputParam;
+    mfxExtCodingOption2 extCodingOption2;
+
+    /* Reset capabilities before querying */
+    info->capabilities = 0;
+
+    /* Load optional codec plug-ins */
+    switch (info->codec_id)
+    {
+        case MFX_CODEC_HEVC:
+            pluginUID = &qsv_encode_plugin_hevc;
+            break;
+        default:
+            pluginUID = NULL;
+            break;
+    }
+    if (pluginUID != NULL && HB_CHECK_MFX_VERSION(version, 1, 8) &&
+        MFXVideoUSER_Load(session, pluginUID, 0) < MFX_ERR_NONE)
     {
-        // Cloverview (Bonnell microarchitecture) supports MSDK via third-party
-        // hardware - we don't support this configuration for the time being
-        if (hb_get_cpu_platform() != HB_CPU_PLATFORM_INTEL_BNL)
+        // couldn't load plugin successfully
+        return 0;
+    }
+
+    /*
+     * First of all, check availability of an encoder for
+     * this combination of a codec ID and implementation.
+     *
+     * Note: can error out rather than sanitizing
+     * unsupported codec IDs, so don't log errors.
+     */
+    if (HB_CHECK_MFX_VERSION(version, HB_QSV_MINVERSION_MAJOR, HB_QSV_MINVERSION_MINOR))
+    {
+        if (info->implementation & MFX_IMPL_AUDIO)
         {
-            qsv_hardware_available   = 1;
-            preferred_implementation = MFX_IMPL_HARDWARE_ANY|MFX_IMPL_VIA_ANY;
-            // our minimum is supported, but query the actual version
-            MFXQueryVersion(session, &qsv_hardware_version);
+            /* Not yet supported */
+            return 0;
+        }
+        else
+        {
+            init_video_param(&inputParam);
+            inputParam.mfx.CodecId = info->codec_id;
+
+            memset(&videoParam, 0, sizeof(mfxVideoParam));
+            videoParam.mfx.CodecId = inputParam.mfx.CodecId;
+
+            if (MFXVideoENCODE_Query(session, &inputParam, &videoParam) >= MFX_ERR_NONE &&
+                videoParam.mfx.CodecId == info->codec_id)
+            {
+                info->available = 1;
+            }
         }
-        MFXClose(session);
+    }
+    if (!info->available)
+    {
+        /* Don't check capabilities for unavailable encoders */
+        return 0;
     }
 
-    // check for version-specific or hardware-specific capabilities
-    // we only use software as a fallback, so check hardware first
-    if (qsv_hardware_available)
+    if (info->implementation & MFX_IMPL_AUDIO)
+    {
+        /* We don't have any audio capability checks yet */
+        return 0;
+    }
+    else
     {
-        if (HB_CHECK_MFX_VERSION(qsv_hardware_version, 1, 6))
+        /* Implementation-specific features that can't be queried */
+        if (qsv_implementation_is_hardware(info->implementation))
         {
-            hb_qsv_info->capabilities |= HB_QSV_CAP_MSDK_API_1_6;
-            hb_qsv_info->capabilities |= HB_QSV_CAP_OPTION2_EXTBRC;
+            if (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G3)
+            {
+                info->capabilities |= HB_QSV_CAP_B_REF_PYRAMID;
+            }
         }
-        if (HB_CHECK_MFX_VERSION(qsv_hardware_version, 1, 7))
+        else
         {
-            // we should really check the driver version, but since that's not
-            // available here, checking the API version is the best we can do :-(
-            hb_qsv_info->capabilities |= HB_QSV_CAP_CORE_COPYFRAME;
+            if (HB_CHECK_MFX_VERSION(version, 1, 6))
+            {
+                info->capabilities |= HB_QSV_CAP_B_REF_PYRAMID;
+            }
         }
-        if (hb_get_cpu_platform() == HB_CPU_PLATFORM_INTEL_HSW)
+
+        /* API-specific features that can't be queried */
+        if (HB_CHECK_MFX_VERSION(version, 1, 6))
         {
-            if (HB_CHECK_MFX_VERSION(qsv_hardware_version, 1, 6))
+            // API >= 1.6 (mfxBitstream::DecodeTimeStamp, mfxExtCodingOption2)
+            info->capabilities |= HB_QSV_CAP_MSDK_API_1_6;
+        }
+
+        /*
+         * Check availability of optional rate control methods.
+         *
+         * Mode 2 tends to error out, but mode 1 gives false negatives, which
+         * is worse. So use mode 2 and assume an error means it's unsupported.
+         *
+         * Also assume that LA and ICQ combined imply LA_ICQ is
+         * supported, so we don't need to check the latter too.
+         */
+        if (HB_CHECK_MFX_VERSION(version, 1, 7))
+        {
+            init_video_param(&inputParam);
+            inputParam.mfx.CodecId           = info->codec_id;
+            inputParam.mfx.RateControlMethod = MFX_RATECONTROL_LA;
+
+            memset(&videoParam, 0, sizeof(mfxVideoParam));
+            videoParam.mfx.CodecId = inputParam.mfx.CodecId;
+
+            if (MFXVideoENCODE_Query(session, &inputParam, &videoParam) >= MFX_ERR_NONE &&
+                videoParam.mfx.RateControlMethod == MFX_RATECONTROL_LA)
+            {
+                info->capabilities |= HB_QSV_CAP_RATECONTROL_LA;
+
+                // also check for LA + interlaced support
+                init_video_param(&inputParam);
+                inputParam.mfx.CodecId             = info->codec_id;
+                inputParam.mfx.RateControlMethod   = MFX_RATECONTROL_LA;
+                inputParam.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_FIELD_TFF;
+
+                memset(&videoParam, 0, sizeof(mfxVideoParam));
+                videoParam.mfx.CodecId = inputParam.mfx.CodecId;
+
+                if (MFXVideoENCODE_Query(session, &inputParam, &videoParam) >= MFX_ERR_NONE &&
+                    videoParam.mfx.FrameInfo.PicStruct == MFX_PICSTRUCT_FIELD_TFF           &&
+                    videoParam.mfx.RateControlMethod   == MFX_RATECONTROL_LA)
+                {
+                    info->capabilities |= HB_QSV_CAP_RATECONTROL_LAi;
+                }
+            }
+        }
+        if (HB_CHECK_MFX_VERSION(version, 1, 8))
+        {
+            init_video_param(&inputParam);
+            inputParam.mfx.CodecId           = info->codec_id;
+            inputParam.mfx.RateControlMethod = MFX_RATECONTROL_ICQ;
+
+            memset(&videoParam, 0, sizeof(mfxVideoParam));
+            videoParam.mfx.CodecId = inputParam.mfx.CodecId;
+
+            if (MFXVideoENCODE_Query(session, &inputParam, &videoParam) >= MFX_ERR_NONE &&
+                videoParam.mfx.RateControlMethod == MFX_RATECONTROL_ICQ)
+            {
+                info->capabilities |= HB_QSV_CAP_RATECONTROL_ICQ;
+            }
+        }
+
+        /*
+         * Check mfxExtCodingOption2 fields.
+         *
+         * Mode 2 suffers from false negatives with some drivers, whereas mode 1
+         * suffers from false positives instead. The latter is probably easier
+         * and/or safer to sanitize for us, so use mode 1.
+         */
+        if (HB_CHECK_MFX_VERSION(version, 1, 6))
+        {
+            init_video_param(&videoParam);
+            videoParam.mfx.CodecId = info->codec_id;
+
+            init_ext_coding_option2(&extCodingOption2);
+            videoParam.ExtParam    = videoExtParam;
+            videoParam.ExtParam[0] = (mfxExtBuffer*)&extCodingOption2;
+            videoParam.NumExtParam = 1;
+
+            status = MFXVideoENCODE_Query(session, NULL, &videoParam);
+            if (status >= MFX_ERR_NONE)
             {
-                hb_qsv_info->capabilities |= HB_QSV_CAP_OPTION2_MBBRC;
+#if 0
+                // testing code that could come in handy
+                fprintf(stderr, "-------------------\n");
+                fprintf(stderr, "MBBRC:         0x%02X\n",     extCodingOption2.MBBRC);
+                fprintf(stderr, "ExtBRC:        0x%02X\n",     extCodingOption2.ExtBRC);
+                fprintf(stderr, "Trellis:       0x%02X\n",     extCodingOption2.Trellis);
+                fprintf(stderr, "RepeatPPS:     0x%02X\n",     extCodingOption2.RepeatPPS);
+                fprintf(stderr, "BRefType:      %4"PRIu16"\n", extCodingOption2.BRefType);
+                fprintf(stderr, "AdaptiveI:     0x%02X\n",     extCodingOption2.AdaptiveI);
+                fprintf(stderr, "AdaptiveB:     0x%02X\n",     extCodingOption2.AdaptiveB);
+                fprintf(stderr, "LookAheadDS:   %4"PRIu16"\n", extCodingOption2.LookAheadDS);
+                fprintf(stderr, "-------------------\n");
+#endif
+
+                /*
+                 * Sanitize API 1.6 fields:
+                 *
+                 * - MBBRC  requires G3 hardware (Haswell or equivalent)
+                 * - ExtBRC requires G2 hardware (Ivy Bridge or equivalent)
+                 */
+                if (qsv_implementation_is_hardware(info->implementation) &&
+                    qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G3)
+                {
+                    if (extCodingOption2.MBBRC)
+                    {
+                        info->capabilities |= HB_QSV_CAP_OPTION2_MBBRC;
+                    }
+                }
+                if (qsv_implementation_is_hardware(info->implementation) &&
+                    qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G2)
+                {
+                    if (extCodingOption2.ExtBRC)
+                    {
+                        info->capabilities |= HB_QSV_CAP_OPTION2_EXTBRC;
+                    }
+                }
+
+                /*
+                 * Sanitize API 1.7 fields:
+                 *
+                 * - Trellis requires G3 hardware (Haswell or equivalent)
+                 */
+                if (HB_CHECK_MFX_VERSION(version, 1, 7))
+                {
+                    if (qsv_implementation_is_hardware(info->implementation) &&
+                        qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G3)
+                    {
+                        if (extCodingOption2.Trellis)
+                        {
+                            info->capabilities |= HB_QSV_CAP_OPTION2_TRELLIS;
+                        }
+                    }
+                }
+
+                /*
+                 * Sanitize API 1.8 fields:
+                 *
+                 * - BRefType    requires B-pyramid support
+                 * - LookAheadDS requires lookahead support
+                 * - AdaptiveI, AdaptiveB, NumMbPerSlice unknown (trust Query)
+                 */
+                if (HB_CHECK_MFX_VERSION(version, 1, 8))
+                {
+                    if (info->capabilities & HB_QSV_CAP_B_REF_PYRAMID)
+                    {
+                        if (extCodingOption2.BRefType)
+                        {
+                            info->capabilities |= HB_QSV_CAP_OPTION2_BREFTYPE;
+                        }
+                    }
+                    if (info->capabilities & HB_QSV_CAP_RATECONTROL_LA)
+                    {
+                        if (extCodingOption2.LookAheadDS)
+                        {
+                            info->capabilities |= HB_QSV_CAP_OPTION2_LA_DOWNS;
+                        }
+                    }
+                    if (extCodingOption2.AdaptiveI && extCodingOption2.AdaptiveB)
+                    {
+                        info->capabilities |= HB_QSV_CAP_OPTION2_IB_ADAPT;
+                    }
+                    if (extCodingOption2.NumMbPerSlice)
+                    {
+                        info->capabilities |= HB_QSV_CAP_OPTION2_NMBSLICE;
+                    }
+                }
             }
-            if (HB_CHECK_MFX_VERSION(qsv_hardware_version, 1, 7))
+            else
             {
-                hb_qsv_info->capabilities |= HB_QSV_CAP_OPTION2_TRELLIS;
-                hb_qsv_info->capabilities |= HB_QSV_CAP_OPTION2_LOOKAHEAD;
+                fprintf(stderr,
+                        "hb_qsv_info_init: mfxExtCodingOption2 check failed (0x%"PRIX32", 0x%"PRIX32", %d)\n",
+                        info->codec_id, info->implementation, status);
             }
-            hb_qsv_info->capabilities |= HB_QSV_CAP_H264_BPYRAMID;
         }
     }
-    else if (qsv_software_available)
+
+    /* Unload optional codec plug-ins */
+    if (pluginUID != NULL && HB_CHECK_MFX_VERSION(version, 1, 8))
+    {
+        MFXVideoUSER_UnLoad(session, pluginUID);
+    }
+
+    return 0;
+}
+
+int hb_qsv_info_init()
+{
+    static int init_done = 0;
+    if (init_done)
+        return 0;
+    init_done = 1;
+
+    /*
+     * First, check for any MSDK version to determine whether one or
+     * more implementations are present; then check if we can use them.
+     *
+     * I've had issues using a NULL version with some combinations of
+     * hardware and driver, so use a low version number (1.0) instead.
+     */
+    mfxSession session;
+    mfxVersion version = { .Major = 1, .Minor = 0, };
+
+    // check for software fallback
+    if (MFXInit(MFX_IMPL_SOFTWARE, &version, &session) == MFX_ERR_NONE)
     {
-        if (HB_CHECK_MFX_VERSION(qsv_software_version, 1, 6))
+        // Media SDK software found, but check that our minimum is supported
+        MFXQueryVersion(session, &qsv_software_version);
+        if (HB_CHECK_MFX_VERSION(qsv_software_version,
+                                 HB_QSV_MINVERSION_MAJOR,
+                                 HB_QSV_MINVERSION_MINOR))
         {
-            hb_qsv_info->capabilities |= HB_QSV_CAP_MSDK_API_1_6;
-            hb_qsv_info->capabilities |= HB_QSV_CAP_H264_BPYRAMID;
+            query_capabilities(session, qsv_software_version, &qsv_software_info_avc);
+            query_capabilities(session, qsv_software_version, &qsv_software_info_hevc);
+            // now that we know which hardware encoders are
+            // available, we can set the preferred implementation
+            hb_qsv_impl_set_preferred("software");
         }
-        hb_qsv_info->capabilities |= HB_QSV_CAP_CORE_COPYFRAME;
+        MFXClose(session);
     }
 
-    // note: we pass a pointer to MFXInit but it never gets modified
-    //       let's make sure of it just to be safe though
-    if (qsv_minimum_version.Major != HB_QSV_MINVERSION_MAJOR ||
-        qsv_minimum_version.Minor != HB_QSV_MINVERSION_MINOR)
-    {
-        hb_error("hb_qsv_info_init: minimum version (%d.%d) was modified",
-                 qsv_minimum_version.Major,
-                 qsv_minimum_version.Minor);
+    // check for actual hardware support
+    if (MFXInit(MFX_IMPL_HARDWARE_ANY, &version, &session) == MFX_ERR_NONE)
+    {
+        // Media SDK hardware found, but check that our minimum is supported
+        //
+        // Note: this-party hardware (QSV_G0) is unsupported for the time being
+        MFXQueryVersion(session, &qsv_hardware_version);
+        if (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G1 &&
+            HB_CHECK_MFX_VERSION(qsv_hardware_version,
+                                 HB_QSV_MINVERSION_MAJOR,
+                                 HB_QSV_MINVERSION_MINOR))
+        {
+            query_capabilities(session, qsv_hardware_version, &qsv_hardware_info_avc);
+            query_capabilities(session, qsv_hardware_version, &qsv_hardware_info_hevc);
+            // now that we know which hardware encoders are
+            // available, we can set the preferred implementation
+            hb_qsv_impl_set_preferred("hardware");
+        }
+        MFXClose(session);
     }
 
     // success
     return 0;
 }
 
-// we don't need it beyond this point
-#undef HB_CHECK_MFX_VERSION
-
-void hb_qsv_info_print()
+static void log_capabilities(int log_level, uint64_t caps, const char *prefix)
 {
-    if (hb_qsv_info == NULL)
+    if (!caps)
     {
-        hb_error("hb_qsv_info_print: QSV info not initialized!");
+        hb_deep_log(log_level, "%s none (standard feature set)", prefix);
     }
+    else
+    {
+        hb_deep_log(log_level, "%s%s%s%s%s%s%s%s%s%s%s%s%s", prefix,
+                    !(caps & HB_QSV_CAP_MSDK_API_1_6)     ? "" : " api1.6",
+                    !(caps & HB_QSV_CAP_B_REF_PYRAMID)    ? "" : " bpyramid",
+                    !(caps & HB_QSV_CAP_OPTION2_BREFTYPE) ? "" : " breftype",
+                    !(caps & HB_QSV_CAP_RATECONTROL_LA)   ? "" : " lookahead",
+                    !(caps & HB_QSV_CAP_RATECONTROL_LAi)  ? "" : " lookaheadi",
+                    !(caps & HB_QSV_CAP_OPTION2_LA_DOWNS) ? "" : " lookaheadds",
+                    !(caps & HB_QSV_CAP_RATECONTROL_ICQ)  ? "" : " icq",
+                    !(caps & HB_QSV_CAP_OPTION2_MBBRC)    ? "" : " mbbrc",
+                    !(caps & HB_QSV_CAP_OPTION2_EXTBRC)   ? "" : " extbrc",
+                    !(caps & HB_QSV_CAP_OPTION2_TRELLIS)  ? "" : " trellis",
+                    !(caps & HB_QSV_CAP_OPTION2_IB_ADAPT) ? "" : " adaptivei adaptiveb",
+                    !(caps & HB_QSV_CAP_OPTION2_NMBSLICE) ? "" : " nummbperslice");
+    }
+}
 
-    // is QSV available?
+void hb_qsv_info_print()
+{
+    // is QSV available and usable?
     hb_log("Intel Quick Sync Video support: %s",
            hb_qsv_available() ? "yes": "no");
 
-    // if we have Quick Sync Video support, also print the details
+    // also print the details
+    if (qsv_hardware_version.Version)
+    {
+        hb_log(" - Intel Media SDK hardware: API %"PRIu16".%"PRIu16" (minimum: %"PRIu16".%"PRIu16")",
+               qsv_hardware_version.Major, qsv_hardware_version.Minor,
+               HB_QSV_MINVERSION_MAJOR,    HB_QSV_MINVERSION_MINOR);
+    }
+    if (qsv_software_version.Version)
+    {
+        hb_log(" - Intel Media SDK software: API %"PRIu16".%"PRIu16" (minimum: %"PRIu16".%"PRIu16")",
+               qsv_software_version.Major, qsv_software_version.Minor,
+               HB_QSV_MINVERSION_MAJOR,    HB_QSV_MINVERSION_MINOR);
+    }
     if (hb_qsv_available())
     {
-        if (qsv_hardware_available)
+        if (hb_qsv_info_avc != NULL && hb_qsv_info_avc->available)
         {
-            hb_log(" - Intel Media SDK hardware: API %d.%d (minimum: %d.%d)",
-                   qsv_hardware_version.Major,
-                   qsv_hardware_version.Minor,
-                   qsv_minimum_version.Major,
-                   qsv_minimum_version.Minor);
+            hb_log(" - H.264 encoder: yes");
+            hb_log("    - preferred implementation: %s",
+                   hb_qsv_impl_get_name(hb_qsv_info_avc->implementation));
+            if (qsv_hardware_info_avc.available)
+            {
+                log_capabilities(2, qsv_hardware_info_avc.capabilities,
+                                 "    - capabilities (hardware): ");
+            }
+            if (qsv_software_info_avc.available)
+            {
+                log_capabilities(2, qsv_software_info_avc.capabilities,
+                                 "    - capabilities (software): ");
+            }
         }
-        if (qsv_software_available)
+        else
         {
-            hb_log(" - Intel Media SDK software: API %d.%d (minimum: %d.%d)",
-                   qsv_software_version.Major,
-                   qsv_software_version.Minor,
-                   qsv_minimum_version.Major,
-                   qsv_minimum_version.Minor);
+            hb_log(" - H.264 encoder: no");
+        }
+        if (hb_qsv_info_hevc != NULL && hb_qsv_info_hevc->available)
+        {
+            hb_log(" - H.265 encoder: yes (unsupported)");
+            hb_log("    - preferred implementation: %s",
+                   hb_qsv_impl_get_name(hb_qsv_info_hevc->implementation));
+            if (qsv_hardware_info_hevc.available)
+            {
+                log_capabilities(2, qsv_hardware_info_hevc.capabilities,
+                                 "    - capabilities (hardware): ");
+            }
+            if (qsv_software_info_hevc.available)
+            {
+                log_capabilities(2, qsv_software_info_hevc.capabilities,
+                                 "    - capabilities (software): ");
+            }
+        }
+        else
+        {
+            hb_log(" - H.265 encoder: no");
         }
-        hb_log(" - Preferred implementation: %s",
-               hb_qsv_impl_get_name(preferred_implementation));
+    }
+}
+
+hb_qsv_info_t* hb_qsv_info_get(int encoder)
+{
+    switch (encoder)
+    {
+        case HB_VCODEC_QSV_H264:
+            return hb_qsv_info_avc;
+        default:
+            return NULL;
     }
 }
 
@@ -199,6 +616,18 @@ int hb_qsv_decode_is_enabled(hb_job_t *job)
             (job->title->video_decode_support & HB_DECODE_SUPPORT_QSV));
 }
 
+int hb_qsv_copyframe_is_slow(int encoder)
+{
+    hb_qsv_info_t *info = hb_qsv_info_get(encoder);
+    if (info != NULL && qsv_implementation_is_hardware(info->implementation))
+    {
+        // we should really check the driver version, but since it's not
+        // available, checking the API version is the best we can do :-(
+        return !HB_CHECK_MFX_VERSION(qsv_hardware_version, 1, 7);
+    }
+    return 0;
+}
+
 int hb_qsv_codingoption_xlat(int val)
 {
     switch (HB_QSV_CLIP3(-1, 2, val))
@@ -305,12 +734,12 @@ 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)
+int hb_qsv_param_parse(hb_qsv_param_t *param, hb_qsv_info_t *info,
+                       const char *key, const char *value)
 {
     float fvalue;
     int ivalue, error = 0;
-    if (param == NULL)
+    if (param == NULL || info == NULL)
     {
         return HB_QSV_PARAM_ERROR;
     }
@@ -374,16 +803,9 @@ int hb_qsv_param_parse(hb_qsv_param_t *param,
     }
     else if (!strcasecmp(key, "b-pyramid"))
     {
-        if (hb_qsv_info->capabilities & HB_QSV_CAP_H264_BPYRAMID)
+        if (info->capabilities & HB_QSV_CAP_B_REF_PYRAMID)
         {
-            switch (vcodec)
-            {
-                case HB_VCODEC_QSV_H264:
-                    ivalue = hb_qsv_atoi(value, &error);
-                    break;
-                default:
-                    return HB_QSV_PARAM_UNSUPPORTED;
-            }
+            ivalue = hb_qsv_atoi(value, &error);
             if (!error)
             {
                 param->gop.b_pyramid = HB_QSV_CLIP3(-1, 1, ivalue);
@@ -409,6 +831,46 @@ int hb_qsv_param_parse(hb_qsv_param_t *param,
             }
         }
     }
+    else if (!strcasecmp(key, "adaptive-i") ||
+             !strcasecmp(key, "i-adapt"))
+    {
+        if (info->capabilities & HB_QSV_CAP_OPTION2_IB_ADAPT)
+        {
+            ivalue = hb_qsv_atobool(value, &error);
+            if (!error)
+            {
+                param->codingOption2.AdaptiveI = hb_qsv_codingoption_xlat(ivalue);
+            }
+        }
+        else
+        {
+            return HB_QSV_PARAM_UNSUPPORTED;
+        }
+    }
+    else if (!strcasecmp(key, "adaptive-b") ||
+             !strcasecmp(key, "b-adapt"))
+    {
+        if (info->capabilities & HB_QSV_CAP_OPTION2_IB_ADAPT)
+        {
+            ivalue = hb_qsv_atobool(value, &error);
+            if (!error)
+            {
+                param->codingOption2.AdaptiveB = hb_qsv_codingoption_xlat(ivalue);
+            }
+        }
+        else
+        {
+            return HB_QSV_PARAM_UNSUPPORTED;
+        }
+    }
+    else if (!strcasecmp(key, "force-cqp"))
+    {
+        ivalue = hb_qsv_atobool(value, &error);
+        if (!error)
+        {
+            param->rc.icq = !ivalue;
+        }
+    }
     else if (!strcasecmp(key, "cqp-offset-i"))
     {
         ivalue = hb_qsv_atoi(value, &error);
@@ -459,9 +921,9 @@ int hb_qsv_param_parse(hb_qsv_param_t *param,
     }
     else if (!strcasecmp(key, "cavlc") || !strcasecmp(key, "cabac"))
     {
-        switch (vcodec)
+        switch (info->codec_id)
         {
-            case HB_VCODEC_QSV_H264:
+            case MFX_CODEC_AVC:
                 ivalue = hb_qsv_atobool(value, &error);
                 break;
             default:
@@ -478,10 +940,10 @@ int hb_qsv_param_parse(hb_qsv_param_t *param,
     }
     else if (!strcasecmp(key, "videoformat"))
     {
-        switch (vcodec)
+        switch (info->codec_id)
         {
-            case HB_VCODEC_QSV_H264:
-                ivalue = hb_qsv_atoindex(x264_vidformat_names, value, &error);
+            case MFX_CODEC_AVC:
+                ivalue = hb_qsv_atoindex(hb_h264_vidformat_names, value, &error);
                 break;
             default:
                 return HB_QSV_PARAM_UNSUPPORTED;
@@ -493,10 +955,10 @@ int hb_qsv_param_parse(hb_qsv_param_t *param,
     }
     else if (!strcasecmp(key, "fullrange"))
     {
-        switch (vcodec)
+        switch (info->codec_id)
         {
-            case HB_VCODEC_QSV_H264:
-                ivalue = hb_qsv_atoindex(x264_fullrange_names, value, &error);
+            case MFX_CODEC_AVC:
+                ivalue = hb_qsv_atoindex(hb_h264_fullrange_names, value, &error);
                 break;
             default:
                 return HB_QSV_PARAM_UNSUPPORTED;
@@ -508,10 +970,10 @@ int hb_qsv_param_parse(hb_qsv_param_t *param,
     }
     else if (!strcasecmp(key, "colorprim"))
     {
-        switch (vcodec)
+        switch (info->codec_id)
         {
-            case HB_VCODEC_QSV_H264:
-                ivalue = hb_qsv_atoindex(x264_colorprim_names, value, &error);
+            case MFX_CODEC_AVC:
+                ivalue = hb_qsv_atoindex(hb_h264_colorprim_names, value, &error);
                 break;
             default:
                 return HB_QSV_PARAM_UNSUPPORTED;
@@ -524,10 +986,10 @@ int hb_qsv_param_parse(hb_qsv_param_t *param,
     }
     else if (!strcasecmp(key, "transfer"))
     {
-        switch (vcodec)
+        switch (info->codec_id)
         {
-            case HB_VCODEC_QSV_H264:
-                ivalue = hb_qsv_atoindex(x264_transfer_names, value, &error);
+            case MFX_CODEC_AVC:
+                ivalue = hb_qsv_atoindex(hb_h264_transfer_names, value, &error);
                 break;
             default:
                 return HB_QSV_PARAM_UNSUPPORTED;
@@ -540,10 +1002,10 @@ int hb_qsv_param_parse(hb_qsv_param_t *param,
     }
     else if (!strcasecmp(key, "colormatrix"))
     {
-        switch (vcodec)
+        switch (info->codec_id)
         {
-            case HB_VCODEC_QSV_H264:
-                ivalue = hb_qsv_atoindex(x264_colmatrix_names, value, &error);
+            case MFX_CODEC_AVC:
+                ivalue = hb_qsv_atoindex(hb_h264_colmatrix_names, value, &error);
                 break;
             default:
                 return HB_QSV_PARAM_UNSUPPORTED;
@@ -557,9 +1019,9 @@ int hb_qsv_param_parse(hb_qsv_param_t *param,
     else if (!strcasecmp(key, "tff") ||
              !strcasecmp(key, "interlaced"))
     {
-        switch (vcodec)
+        switch (info->codec_id)
         {
-            case HB_VCODEC_QSV_H264:
+            case MFX_CODEC_AVC:
                 ivalue = hb_qsv_atobool(value, &error);
                 break;
             default:
@@ -574,9 +1036,9 @@ int hb_qsv_param_parse(hb_qsv_param_t *param,
     }
     else if (!strcasecmp(key, "bff"))
     {
-        switch (vcodec)
+        switch (info->codec_id)
         {
-            case HB_VCODEC_QSV_H264:
+            case MFX_CODEC_AVC:
                 ivalue = hb_qsv_atobool(value, &error);
                 break;
             default:
@@ -591,7 +1053,7 @@ int hb_qsv_param_parse(hb_qsv_param_t *param,
     }
     else if (!strcasecmp(key, "mbbrc"))
     {
-        if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_MBBRC)
+        if (info->capabilities & HB_QSV_CAP_OPTION2_MBBRC)
         {
             ivalue = hb_qsv_atobool(value, &error);
             if (!error)
@@ -606,7 +1068,7 @@ int hb_qsv_param_parse(hb_qsv_param_t *param,
     }
     else if (!strcasecmp(key, "extbrc"))
     {
-        if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_EXTBRC)
+        if (info->capabilities & HB_QSV_CAP_OPTION2_EXTBRC)
         {
             ivalue = hb_qsv_atobool(value, &error);
             if (!error)
@@ -622,16 +1084,9 @@ int hb_qsv_param_parse(hb_qsv_param_t *param,
     else if (!strcasecmp(key, "lookahead") ||
              !strcasecmp(key, "la"))
     {
-        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 (info->capabilities & HB_QSV_CAP_RATECONTROL_LA)
         {
+            ivalue = hb_qsv_atobool(value, &error);
             if (!error)
             {
                 param->rc.lookahead = ivalue;
@@ -645,16 +1100,9 @@ int hb_qsv_param_parse(hb_qsv_param_t *param,
     else if (!strcasecmp(key, "lookahead-depth") ||
              !strcasecmp(key, "la-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 (info->capabilities & HB_QSV_CAP_RATECONTROL_LA)
         {
+            ivalue = hb_qsv_atoi(value, &error);
             if (!error)
             {
                 // LookAheadDepth 10 will cause a hang with some driver versions
@@ -667,18 +1115,29 @@ int hb_qsv_param_parse(hb_qsv_param_t *param,
             return HB_QSV_PARAM_UNSUPPORTED;
         }
     }
-    else if (!strcasecmp(key, "trellis"))
+    else if (!strcasecmp(key, "lookahead-ds") ||
+             !strcasecmp(key, "la-ds"))
     {
-        switch (vcodec)
+        if (info->capabilities & HB_QSV_CAP_OPTION2_LA_DOWNS)
         {
-            case HB_VCODEC_QSV_H264:
-                ivalue = hb_qsv_atoi(value, &error);
-                break;
-            default:
-                return HB_QSV_PARAM_UNSUPPORTED;
+            ivalue = hb_qsv_atoi(value, &error);
+            if (!error)
+            {
+                param->codingOption2.LookAheadDS = HB_QSV_CLIP3(MFX_LOOKAHEAD_DS_UNKNOWN,
+                                                                MFX_LOOKAHEAD_DS_4x,
+                                                                ivalue);
+            }
+        }
+        else
+        {
+            return HB_QSV_PARAM_UNSUPPORTED;
         }
-        if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_TRELLIS)
+    }
+    else if (!strcasecmp(key, "trellis"))
+    {
+        if (info->capabilities & HB_QSV_CAP_OPTION2_TRELLIS)
         {
+            ivalue = hb_qsv_atoi(value, &error);
             if (!error)
             {
                 param->codingOption2.Trellis = hb_qsv_trellisvalue_xlat(ivalue);
@@ -693,7 +1152,7 @@ int hb_qsv_param_parse(hb_qsv_param_t *param,
     {
         /*
          * TODO:
-         * - slice count control
+         * - slice count (num-slice/slices, num-mb-per-slice/slice-max-mbs)
          * - open-gop
          * - fake-interlaced (mfxExtCodingOption.FramePicture???)
          * - intra-refresh
@@ -703,31 +1162,91 @@ int hb_qsv_param_parse(hb_qsv_param_t *param,
     return error ? HB_QSV_PARAM_BAD_VALUE : HB_QSV_PARAM_OK;
 }
 
+#ifdef HB_API_OLD_PRESET_GETTERS
+const char* const* hb_qsv_presets()
+{
+    return hb_qsv_preset_get_names();
+}
+#endif
+
 const char* const* hb_qsv_preset_get_names()
 {
-    if (hb_get_cpu_platform() >= HB_CPU_PLATFORM_INTEL_HSW)
+    if (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G3)
     {
         return hb_qsv_preset_names2;
     }
     else
     {
-        return hb_qsv_preset_names1;
+        return hb_qsv_preset_names2;
     }
 }
 
-#ifdef HB_API_OLD_PRESET_GETTERS
-const char* const* hb_qsv_presets()
+const char* const* hb_qsv_profile_get_names(int encoder)
 {
-    return hb_qsv_preset_get_names();
+    switch (encoder)
+    {
+        case HB_VCODEC_QSV_H264:
+            return hb_h264_profile_names;
+        default:
+            return NULL;
+    }
+}
+
+const char* const* hb_qsv_level_get_names(int encoder)
+{
+    switch (encoder)
+    {
+        case HB_VCODEC_QSV_H264:
+            return hb_h264_level_names;
+        default:
+            return NULL;
+    }
+}
+
+const char* hb_qsv_video_quality_get_name(uint32_t codec)
+{
+    uint64_t caps;
+    switch (codec)
+    {
+        case HB_VCODEC_QSV_H264:
+            caps = hb_qsv_info_avc != NULL ? hb_qsv_info_avc->capabilities : 0;
+            return (caps & HB_QSV_CAP_RATECONTROL_ICQ) ? "ICQ" : "QP";
+
+        default:
+            return "QP";
+    }
+}
+
+void hb_qsv_video_quality_get_limits(uint32_t codec, float *low, float *high,
+                                     float *granularity, int *direction)
+{
+    uint64_t caps;
+    switch (codec)
+    {
+        case HB_VCODEC_QSV_H264:
+            caps = hb_qsv_info_avc != NULL ? hb_qsv_info_avc->capabilities : 0;
+            *direction   = 1;
+            *granularity = 1.;
+            *low         = (caps & HB_QSV_CAP_RATECONTROL_ICQ) ? 1. : 0.;
+            *high        = 51.;
+            break;
+
+        default:
+            *direction   = 1;
+            *granularity = 1.;
+            *low         = 0.;
+            *high        = 51.;
+            break;
+    }
 }
-#endif
 
 int hb_qsv_param_default_preset(hb_qsv_param_t *param,
-                                mfxVideoParam *videoParam, const char *preset)
+                                mfxVideoParam *videoParam,
+                                hb_qsv_info_t *info, const char *preset)
 {
-    if (param != NULL && videoParam != NULL)
+    if (param != NULL && videoParam != NULL && info != NULL)
     {
-        int ret = hb_qsv_param_default(param, videoParam);
+        int ret = hb_qsv_param_default(param, videoParam, info);
         if (ret)
         {
             return ret;
@@ -769,7 +1288,7 @@ int hb_qsv_param_default_preset(hb_qsv_param_t *param,
              *     LookAhead:       0 (off)
              *     LookAheadDepth: Not Applicable
              */
-            if (hb_get_cpu_platform() >= HB_CPU_PLATFORM_INTEL_HSW)
+            if (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G3)
             {
                 param->rc.lookahead                = 0;
                 param->videoParam->mfx.NumRefFrame = 1;
@@ -794,7 +1313,7 @@ int hb_qsv_param_default_preset(hb_qsv_param_t *param,
         }
         else if (!strcasecmp(preset, "speed"))
         {
-            if (hb_get_cpu_platform() >= HB_CPU_PLATFORM_INTEL_HSW)
+            if (qsv_hardware_generation(hb_get_cpu_platform()) >= QSV_G3)
             {
                 /*
                  * HSW TargetUsage:     6
@@ -840,9 +1359,10 @@ int hb_qsv_param_default_preset(hb_qsv_param_t *param,
     return 0;
 }
 
-int hb_qsv_param_default(hb_qsv_param_t *param, mfxVideoParam *videoParam)
+int hb_qsv_param_default(hb_qsv_param_t *param, mfxVideoParam *videoParam,
+                         hb_qsv_info_t  *info)
 {
-    if (param != NULL && videoParam != NULL)
+    if (param != NULL && videoParam != NULL && info != NULL)
     {
         // introduced in API 1.0
         memset(&param->codingOption, 0, sizeof(mfxExtCodingOption));
@@ -902,13 +1422,21 @@ int hb_qsv_param_default(hb_qsv_param_t *param, mfxVideoParam *videoParam)
         // introduced in API 1.7
         param->codingOption2.LookAheadDepth  = 40;
         param->codingOption2.Trellis         = MFX_TRELLIS_OFF;
+        // introduced in API 1.8
+        param->codingOption2.RepeatPPS       = MFX_CODINGOPTION_ON;
+        param->codingOption2.BRefType        = MFX_B_REF_UNKNOWN; // controlled via gop.b_pyramid
+        param->codingOption2.AdaptiveI       = MFX_CODINGOPTION_ON;
+        param->codingOption2.AdaptiveB       = MFX_CODINGOPTION_ON;
+        param->codingOption2.LookAheadDS     = MFX_LOOKAHEAD_DS_OFF;
+        param->codingOption2.NumMbPerSlice   = 0;
 
         // GOP & rate control
         param->gop.b_pyramid          = -1; // set automatically
         param->gop.gop_pic_size       = -1; // set automatically
         param->gop.gop_ref_dist       = -1; // set automatically
         param->gop.int_ref_cycle_size = -1; // set automatically
-        param->rc.lookahead           =  1;
+        param->rc.icq                 =  1; // enabled by default (if supported)
+        param->rc.lookahead           =  1; // enabled by default (if supported)
         param->rc.cqp_offsets[0]      =  0;
         param->rc.cqp_offsets[1]      =  2;
         param->rc.cqp_offsets[2]      =  4;
@@ -944,7 +1472,7 @@ int hb_qsv_param_default(hb_qsv_param_t *param, mfxVideoParam *videoParam)
         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)
+        if (info->capabilities & HB_QSV_CAP_MSDK_API_1_6)
         {
             param->videoParam->ExtParam[param->videoParam->NumExtParam++] = (mfxExtBuffer*)&param->codingOption2;
         }
@@ -1015,9 +1543,37 @@ uint8_t hb_qsv_frametype_xlat(uint16_t qsv_frametype, uint16_t *out_flags)
     return frametype;
 }
 
-mfxIMPL hb_qsv_impl_get_preferred()
+int hb_qsv_impl_set_preferred(const char *name)
 {
-    return preferred_implementation;
+    if (name == NULL)
+    {
+        return -1;
+    }
+    if (!strcasecmp(name, "software"))
+    {
+        if (qsv_software_info_avc.available)
+        {
+            hb_qsv_info_avc = &qsv_software_info_avc;
+        }
+        if (qsv_software_info_hevc.available)
+        {
+            hb_qsv_info_hevc = &qsv_software_info_hevc;
+        }
+        return 0;
+    }
+    if (!strcasecmp(name, "hardware"))
+    {
+        if (qsv_hardware_info_avc.available)
+        {
+            hb_qsv_info_avc = &qsv_hardware_info_avc;
+        }
+        if (qsv_hardware_info_hevc.available)
+        {
+            hb_qsv_info_hevc = &qsv_hardware_info_hevc;
+        }
+        return 0;
+    }
+    return -1;
 }
 
 const char* hb_qsv_impl_get_name(int impl)
@@ -1048,4 +1604,12 @@ const char* hb_qsv_impl_get_name(int impl)
     }
 }
 
+void hb_qsv_force_workarounds()
+{
+    qsv_software_info_avc.capabilities  &= ~HB_QSV_CAP_MSDK_API_1_6;
+    qsv_hardware_info_avc.capabilities  &= ~HB_QSV_CAP_MSDK_API_1_6;
+    qsv_software_info_hevc.capabilities &= ~HB_QSV_CAP_MSDK_API_1_6;
+    qsv_hardware_info_hevc.capabilities &= ~HB_QSV_CAP_MSDK_API_1_6;
+}
+
 #endif // USE_QSV
index c79cd2aa1dd7ffd0a1f707475d4d2c28f25c4250..da879e548472be4c67c3a097d9cf7bd126b0f7d2 100644 (file)
@@ -11,6 +11,7 @@
 #define HB_QSV_COMMON_H
 
 #include "msdk/mfxvideo.h"
+#include "msdk/mfxplugin.h"
 #include "libavcodec/avcodec.h"
 
 /* Minimum Intel Media SDK version (currently 1.3, for Sandy Bridge support) */
  */
 typedef struct hb_qsv_info_s
 {
-    // supported version-specific or hardware-specific capabilities
-    int capabilities;
-#define HB_QSV_CAP_H264_BPYRAMID     (1 << 0) // H.264: reference B-frames
-#define HB_QSV_CAP_MSDK_API_1_6      (1 << 1) // Support for API 1.6 or later
-#define HB_QSV_CAP_OPTION2_EXTBRC    (1 << 2) // mfxExtCodingOption2: ExtBRC
-#define HB_QSV_CAP_OPTION2_MBBRC     (1 << 3) // mfxExtCodingOption2: MBBRC
-#define HB_QSV_CAP_OPTION2_LOOKAHEAD (1 << 4) // mfxExtCodingOption2: LookAhead
-#define HB_QSV_CAP_OPTION2_TRELLIS   (1 << 5) // mfxExtCodingOption2: Trellis
-// mfxCoreInterface: CopyFrame has a bug which prevents us from using it, but
-// the bug is fixed in newer drivers, we can use this cap to determine usability
-#define HB_QSV_CAP_CORE_COPYFRAME    (1 << 6)
-
-    // TODO: add available decoders, filters, encoders,
-    //       maximum decode and encode resolution, etc.
+    // each info struct only corresponds to one CodecId and implementation combo
+    const mfxU32  codec_id;
+    const mfxIMPL implementation;
+
+    // whether the encoder is available for this implementation
+    int available;
+
+    // version-specific or hardware-specific capabilities
+    uint64_t capabilities;
+    // support for API 1.6 or later
+#define HB_QSV_CAP_MSDK_API_1_6      (1LL <<  0)
+    // H.264, H.265: B-frames can be used as references
+#define HB_QSV_CAP_B_REF_PYRAMID     (1LL <<  1)
+    // optional rate control methods
+#define HB_QSV_CAP_RATECONTROL_LA    (1LL << 10)
+#define HB_QSV_CAP_RATECONTROL_LAi   (1LL << 11)
+#define HB_QSV_CAP_RATECONTROL_ICQ   (1LL << 12)
+    // mfxExtCodingOption2 fields
+#define HB_QSV_CAP_OPTION2_MBBRC     (1LL << 20)
+#define HB_QSV_CAP_OPTION2_EXTBRC    (1LL << 21)
+#define HB_QSV_CAP_OPTION2_TRELLIS   (1LL << 22)
+#define HB_QSV_CAP_OPTION2_BREFTYPE  (1LL << 23)
+#define HB_QSV_CAP_OPTION2_IB_ADAPT  (1LL << 24)
+#define HB_QSV_CAP_OPTION2_LA_DOWNS  (1LL << 25)
+#define HB_QSV_CAP_OPTION2_NMBSLICE  (1LL << 26)
+
+    // TODO: add maximum encode resolution, etc.
 } hb_qsv_info_t;
 
-/* Global Intel QSV information for use by the UIs */
-extern hb_qsv_info_t *hb_qsv_info;
-
 /* Intel Quick Sync Video utilities */
-int  hb_qsv_available();
-int  hb_qsv_info_init();
-void hb_qsv_info_print();
+int            hb_qsv_available();
+int            hb_qsv_video_encoder_is_enabled(int encoder);
+int            hb_qsv_audio_encoder_is_enabled(int encoder);
+int            hb_qsv_info_init();
+void           hb_qsv_info_print();
+hb_qsv_info_t* hb_qsv_info_get(int encoder);
 
 /* Intel Quick Sync Video DECODE utilities */
 const char* hb_qsv_decode_get_codec_name(enum AVCodecID codec_id);
 int hb_qsv_decode_is_enabled(hb_job_t *job);
 
+/*
+ * mfxCoreInterface::CopyFrame had a bug preventing us from using it, but
+ * it was fixed in newer drivers - we can use this to determine usability
+ */
+int hb_qsv_copyframe_is_slow(int encoder);
+
 /* Media SDK parameters handling */
 enum
 {
@@ -99,6 +119,7 @@ typedef struct
     } gop;
     struct
     {
+        int   icq;
         int   lookahead;
         int   cqp_offsets[3];
         int   vbv_max_bitrate;
@@ -110,9 +131,14 @@ typedef struct
     mfxVideoParam *videoParam;
 } hb_qsv_param_t;
 
-const char* const* hb_qsv_preset_get_names();
 static const char* const hb_qsv_preset_names1[] = { "speed", "balanced",            NULL, };
 static const char* const hb_qsv_preset_names2[] = { "speed", "balanced", "quality", NULL, };
+const char* const* hb_qsv_preset_get_names();
+const char* const* hb_qsv_profile_get_names(int encoder);
+const char* const* hb_qsv_level_get_names(int encoder);
+
+const char* hb_qsv_video_quality_get_name(uint32_t codec);
+void hb_qsv_video_quality_get_limits(uint32_t codec, float *low, float *high, float *granularity, int *direction);
 
 #define HB_QSV_CLIP3(min, max, val) ((val < min) ? min : (val > max) ? max : val)
 int         hb_qsv_codingoption_xlat    (int val);
@@ -124,14 +150,16 @@ 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_preset(hb_qsv_param_t *param, mfxVideoParam *videoParam, const char *preset);
-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);
+int hb_qsv_param_default_preset(hb_qsv_param_t *param, mfxVideoParam *videoParam, hb_qsv_info_t *info, const char *preset);
+int hb_qsv_param_default       (hb_qsv_param_t *param, mfxVideoParam *videoParam, hb_qsv_info_t *info);
+int hb_qsv_param_parse         (hb_qsv_param_t *param,                            hb_qsv_info_t *info, const char *key, const char *value);
 
 const char* hb_qsv_frametype_name(uint16_t qsv_frametype);
 uint8_t     hb_qsv_frametype_xlat(uint16_t qsv_frametype, uint16_t *out_flags);
 
-mfxIMPL     hb_qsv_impl_get_preferred();
+int         hb_qsv_impl_set_preferred(const char *name);
 const char* hb_qsv_impl_get_name(int impl);
 
+void hb_qsv_force_workarounds(); // for developers only
+
 #endif
index 96f5016f07c11aef76476f55a1806d4239702a2e..dd047a20af9cedb8b92a265713e0eec5faecae2b 100644 (file)
@@ -734,7 +734,7 @@ static void do_job(hb_job_t *job)
      *      the list and we can't use CopyFrame, disable QSV decoding until a
      *      better solution is implemented.
      */
-    if (!(hb_qsv_info->capabilities & HB_QSV_CAP_CORE_COPYFRAME))
+    if (hb_qsv_copyframe_is_slow(job->vcodec))
     {
         if (job->list_filter != NULL)
         {
index cd414e22130d9be226ff7f8389281a4ee2dd4f95..a595a9d58e5b59eb20e4c803b953e959605286f7 100644 (file)
@@ -3687,6 +3687,7 @@ static int ParseOptions( int argc, char ** argv )
     #define AUDIO_DITHER        289
     #define QSV_BASELINE        290
     #define QSV_ASYNC_DEPTH     291
+    #define QSV_IMPLEMENTATION  292
 
     for( ;; )
     {
@@ -4339,19 +4340,14 @@ static int ParseOptions( int argc, char ** argv )
                 break;
 #ifdef USE_QSV
             case QSV_BASELINE:
-                if (hb_qsv_available())
-                {
-                    /* XXX: for testing workarounds */
-                    hb_qsv_info->capabilities &= ~HB_QSV_CAP_MSDK_API_1_6;
-                    hb_qsv_info->capabilities &= ~HB_QSV_CAP_OPTION2_MBBRC;
-                    hb_qsv_info->capabilities &= ~HB_QSV_CAP_OPTION2_EXTBRC;
-                    hb_qsv_info->capabilities &= ~HB_QSV_CAP_OPTION2_TRELLIS;
-                    hb_qsv_info->capabilities &= ~HB_QSV_CAP_OPTION2_LOOKAHEAD;
-                }
+                hb_qsv_force_workarounds();
                 break;
             case QSV_ASYNC_DEPTH:
                 qsv_async_depth = atoi(optarg);
                 break;
+            case QSV_IMPLEMENTATION:
+                hb_qsv_impl_set_preferred(optarg);
+                break;
 #endif
             default:
                 fprintf( stderr, "unknown option (%s)\n", argv[cur_optind] );