]> granicus.if.org Git - handbrake/commitdiff
qsv: HEVC encoding support.
authorTim Walker <tdskywalker@gmail.com>
Sun, 20 Sep 2015 10:54:01 +0000 (12:54 +0200)
committerTim Walker <tdskywalker@gmail.com>
Sun, 20 Sep 2015 10:54:01 +0000 (12:54 +0200)
libhb/common.c
libhb/common.h
libhb/enc_qsv.c
libhb/muxavformat.c
libhb/qsv_common.c
libhb/work.c

index b6d36d2031cc1ea8284ab5997afad5f9e2b4764a..6877e31a6b82631bc31458746ce9407590d7d027 100644 (file)
@@ -226,6 +226,7 @@ hb_encoder_internal_t hb_video_encoders[]  =
     { { "H.264 (x264)",      "x264",      "H.264 (libx264)",         HB_VCODEC_X264,         HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H264,   },
     { { "H.264 (Intel QSV)", "qsv_h264",  "H.264 (Intel Media SDK)", HB_VCODEC_QSV_H264,     HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H264,   },
     { { "H.265 (x265)",      "x265",      "H.265 (libx265)",         HB_VCODEC_X265,           HB_MUX_AV_MP4|HB_MUX_AV_MKV,   }, NULL, 1, HB_GID_VCODEC_H265,   },
+    { { "H.265 (Intel QSV)", "qsv_h265",  "H.265 (Intel Media SDK)", HB_VCODEC_QSV_H265,     HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H265,   },
     { { "MPEG-4",            "mpeg4",     "MPEG-4 (libavcodec)",     HB_VCODEC_FFMPEG_MPEG4, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_MPEG4,  },
     { { "MPEG-2",            "mpeg2",     "MPEG-2 (libavcodec)",     HB_VCODEC_FFMPEG_MPEG2, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_MPEG2,  },
     { { "VP8",               "VP8",       "VP8 (libvpx)",            HB_VCODEC_FFMPEG_VP8,                   HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_VP8,    },
index 9cf51b3e270e832d3f2afac17308e1150e9a3c94..a928eb484062c0d60b82075abf73e1ca05650ec0 100644 (file)
@@ -498,8 +498,10 @@ struct hb_job_s
 #define HB_VCODEC_FFMPEG_VP8   0x0000040
 #define HB_VCODEC_FFMPEG_MASK  0x00000F0
 #define HB_VCODEC_QSV_H264     0x0000100
+#define HB_VCODEC_QSV_H265     0x0000200
 #define HB_VCODEC_QSV_MASK     0x0000F00
 #define HB_VCODEC_H264_MASK    (HB_VCODEC_X264|HB_VCODEC_QSV_H264)
+#define HB_VCODEC_H265_MASK    (HB_VCODEC_X265|HB_VCODEC_QSV_H265)
 
     int             vcodec;
     double          vquality;
index 2f19b36f0b91517222110d2f4b9ca522cd70e810..39ed16bef752c70125b505dd7cf635b62650757f 100644 (file)
@@ -33,6 +33,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "qsv_common.h"
 #include "qsv_memory.h"
 #include "h264_common.h"
+#include "h265_common.h"
 
 /*
  * The frame info struct remembers information about each frame across calls to
@@ -58,7 +59,7 @@ void encqsvClose(hb_work_object_t*);
 hb_work_object_t hb_encqsv =
 {
     WORK_ENCQSV,
-    "H.264/AVC encoder (Intel QSV)",
+    "Quick Sync Video encoder (Intel Media SDK)",
     encqsvInit,
     encqsvWork,
     encqsvClose
@@ -177,7 +178,7 @@ static void qsv_handle_breftype(hb_work_private_t *pv)
         /* We need 2 consecutive B-frames for B-pyramid (GopRefDist >= 3) */
         goto unsupported;
     }
-    else if (pv->qsv_info->codec_id == MFX_CODEC_AVC)
+    else if (pv->param.videoParam->mfx.CodecId == MFX_CODEC_AVC)
     {
         switch (pv->param.videoParam->mfx.CodecProfile)
         {
@@ -189,6 +190,16 @@ static void qsv_handle_breftype(hb_work_private_t *pv)
                 break;
         }
     }
+    else if (pv->param.videoParam->mfx.CodecId == MFX_CODEC_HEVC)
+    {
+        switch (pv->param.videoParam->mfx.CodecProfile)
+        {
+            case MFX_PROFILE_HEVC_MAINSP:
+                goto unsupported; // B-frames not allowed by profile
+            default:
+                break;
+        }
+    }
 
     /* Handle B-pyramid auto (on for CQP, off otherwise) */
     if (pv->param.gop.b_pyramid < 0)
@@ -275,6 +286,170 @@ unsupported:
     pv->param.codingOption2.BRefType = MFX_B_REF_OFF;
 }
 
+static int qsv_hevc_make_header(hb_work_object_t *w, mfxSession session)
+{
+    size_t len;
+    int ret = 0;
+    uint8_t *buf, *end;
+    mfxBitstream bitstream;
+    hb_buffer_t *bitstream_buf;
+    mfxStatus status;
+    mfxSyncPoint syncPoint;
+    mfxFrameSurface1 frameSurface1;
+    hb_work_private_t *pv = w->private_data;
+
+    memset(&bitstream,     0, sizeof(mfxBitstream));
+    memset(&syncPoint,     0, sizeof(mfxSyncPoint));
+    memset(&frameSurface1, 0, sizeof(mfxFrameSurface1));
+
+    /* The bitstream buffer should be able to hold any encoded frame */
+    bitstream_buf = hb_video_buffer_init(pv->job->width, pv->job->height);
+    if (bitstream_buf == NULL)
+    {
+        hb_log("qsv_hevc_make_header: hb_buffer_init failed");
+        ret = -1;
+        goto end;
+    }
+    bitstream.Data      = bitstream_buf->data;
+    bitstream.MaxLength = bitstream_buf->size;
+
+    /* We only need to encode one frame, so we only need one surface */
+    mfxU16 Height            = pv->param.videoParam->mfx.FrameInfo.Height;
+    mfxU16 Width             = pv->param.videoParam->mfx.FrameInfo.Width;
+    frameSurface1.Info       = pv->param.videoParam->mfx.FrameInfo;
+    frameSurface1.Data.VU    = av_mallocz(Width * Height / 2);
+    frameSurface1.Data.Y     = av_mallocz(Width * Height);
+    frameSurface1.Data.Pitch = Width;
+
+    /* Encode a single blank frame */
+    do
+    {
+        status = MFXVideoENCODE_EncodeFrameAsync(session, NULL,
+                                                 &frameSurface1,
+                                                 &bitstream,
+                                                 &syncPoint);
+
+        if (status == MFX_ERR_MORE_DATA)
+        {
+            break; // more input needed, but we don't have any
+        }
+        if (status < MFX_ERR_NONE)
+        {
+            hb_log("qsv_hevc_make_header: MFXVideoENCODE_EncodeFrameAsync failed (%d)", status);
+            ret = -1;
+            goto end;
+        }
+        if (syncPoint)
+        {
+            break; // we have output
+        }
+        if (status == MFX_WRN_DEVICE_BUSY)
+        {
+            av_qsv_sleep(1);
+        }
+    }
+    while (status >= MFX_ERR_NONE);
+
+    /* If we don't have any output yet, flush the encoder */
+    if (!syncPoint)
+    {
+        do
+        {
+            status = MFXVideoENCODE_EncodeFrameAsync(session, NULL, NULL,
+                                                     &bitstream,
+                                                     &syncPoint);
+
+            if (status == MFX_ERR_MORE_DATA)
+            {
+                break; // done flushing
+            }
+            if (status < MFX_ERR_NONE)
+            {
+                hb_log("qsv_hevc_make_header: MFXVideoENCODE_EncodeFrameAsync failed (%d)", status);
+                ret = -1;
+                goto end;
+            }
+            if (syncPoint)
+            {
+                break; // we have output
+            }
+            if (status == MFX_WRN_DEVICE_BUSY)
+            {
+                av_qsv_sleep(1);
+            }
+        }
+        while (status >= MFX_ERR_NONE);
+    }
+
+    /* Still no data at this point, we can't proceed */
+    if (!syncPoint)
+    {
+        hb_log("qsv_hevc_make_header: no sync point");
+        ret = -1;
+        goto end;
+    }
+
+    do
+    {
+        status = MFXVideoCORE_SyncOperation(session, syncPoint, 100);
+    }
+    while (status == MFX_WRN_IN_EXECUTION);
+
+    if (status != MFX_ERR_NONE)
+    {
+        hb_log("qsv_hevc_make_header: MFXVideoCORE_SyncOperation failed (%d)", status);
+        ret = -1;
+        goto end;
+    }
+
+    if (!bitstream.DataLength)
+    {
+        hb_log("qsv_hevc_make_header: no bitstream data");
+        ret = -1;
+        goto end;
+    }
+
+    /* Include any parameter sets and SEI NAL units in the headers. */
+    len = bitstream.DataLength;
+    buf = bitstream.Data + bitstream.DataOffset;
+    end = bitstream.Data + bitstream.DataOffset + bitstream.DataLength;
+    w->config->h265.headers_length = 0;
+
+    while ((buf = hb_annexb_find_next_nalu(buf, &len)) != NULL)
+    {
+        switch ((buf[0] >> 1) & 0x3f)
+        {
+            case 32: // VPS_NUT
+            case 33: // SPS_NUT
+            case 34: // PPS_NUT
+            case 39: // PREFIX_SEI_NUT
+            case 40: // SUFFIX_SEI_NUT
+                break;
+            default:
+                len = end - buf;
+                continue;
+        }
+
+        size_t size = hb_nal_unit_write_annexb(NULL, buf, len) + w->config->h265.headers_length;
+        if (sizeof(w->config->h265.headers) < size)
+        {
+            /* Will never happen in practice */
+            hb_log("qsv_hevc_make_header: header too large (size: %lu, max: %lu)",
+                   size, sizeof(w->config->h265.headers));
+        }
+
+        w->config->h265.headers_length += hb_nal_unit_write_annexb(w->config->h265.headers +
+                                                                   w->config->h265.headers_length, buf, len);
+        len = end - buf;
+    }
+
+end:
+    hb_buffer_close(&bitstream_buf);
+    av_free(frameSurface1.Data.VU);
+    av_free(frameSurface1.Data.Y);
+    return ret;
+}
+
 int qsv_enc_init(hb_work_private_t *pv)
 {
     av_qsv_context *qsv = pv->job->qsv.ctx;
@@ -377,6 +552,26 @@ int qsv_enc_init(hb_work_private_t *pv)
         av_qsv_list_add(qsv_encode->tasks, task);
     }
 
+    // plugins should be loaded before querying for surface allocation
+    if (pv->loaded_plugins == NULL)
+    {
+        if (MFXQueryVersion(qsv->mfx_session, &version) != MFX_ERR_NONE)
+        {
+            hb_error("qsv_enc_init: MFXQueryVersion failed");
+            *job->done_error = HB_ERROR_INIT;
+            *job->die = 1;
+            return -1;
+        }
+        pv->loaded_plugins = hb_qsv_load_plugins(pv->qsv_info, qsv->mfx_session, version);
+        if (pv->loaded_plugins == NULL)
+        {
+            hb_error("qsv_enc_init: hb_qsv_load_plugins failed");
+            *job->done_error = HB_ERROR_INIT;
+            *job->die = 1;
+            return -1;
+        }
+    }
+
     // setup surface allocation
     pv->param.videoParam->IOPattern = (pv->is_sys_mem                 ?
                                        MFX_IOPATTERN_IN_SYSTEM_MEMORY :
@@ -444,31 +639,6 @@ int qsv_enc_init(hb_work_private_t *pv)
         AV_QSV_CHECK_POINTER(qsv_encode->p_syncp[i]->p_sync, MFX_ERR_MEMORY_ALLOC);
     }
 
-    // log actual implementation details now that we know them
-    if ((MFXQueryIMPL   (qsv->mfx_session, &impl)    == MFX_ERR_NONE) &&
-        (MFXQueryVersion(qsv->mfx_session, &version) == MFX_ERR_NONE))
-    {
-        hb_log("qsv_enc_init: using '%s' implementation, API: %"PRIu16".%"PRIu16"",
-               hb_qsv_impl_get_name(impl), version.Major, version.Minor);
-    }
-    else
-    {
-        hb_log("qsv_enc_init: MFXQueryIMPL/MFXQueryVersion failure");
-    }
-
-    // if not re-using encqsvInit's MFX session, load required plug-ins here
-    if (pv->loaded_plugins == NULL)
-    {
-        pv->loaded_plugins = hb_qsv_load_plugins(pv->qsv_info, qsv->mfx_session, version);
-        if (pv->loaded_plugins == NULL)
-        {
-            hb_error("qsv_enc_init: hb_qsv_load_plugins failed");
-            *job->done_error = HB_ERROR_INIT;
-            *job->die = 1;
-            return -1;
-        }
-    }
-
     // initialize the encoder
     sts = MFXVideoENCODE_Init(qsv->mfx_session, pv->param.videoParam);
     if (sts < MFX_ERR_NONE) // ignore warnings
@@ -480,6 +650,18 @@ int qsv_enc_init(hb_work_private_t *pv)
     }
     qsv_encode->is_init_done = 1;
 
+    // query and log actual implementation details
+    if ((MFXQueryIMPL   (qsv->mfx_session, &impl)    == MFX_ERR_NONE) &&
+        (MFXQueryVersion(qsv->mfx_session, &version) == MFX_ERR_NONE))
+    {
+        hb_log("qsv_enc_init: using '%s' implementation, API: %"PRIu16".%"PRIu16"",
+               hb_qsv_impl_get_name(impl), version.Major, version.Minor);
+    }
+    else
+    {
+        hb_log("qsv_enc_init: MFXQueryIMPL/MFXQueryVersion failure");
+    }
+
     pv->init_done = 1;
     return 0;
 }
@@ -515,8 +697,7 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
     // 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;
+    // set and enable colorimetry (video signal information)
     switch (job->color_matrix_code)
     {
         case 4:
@@ -550,6 +731,7 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
             pv->param.videoSignalInfo.MatrixCoefficients      = job->title->color_matrix;
             break;
     }
+    pv->param.videoSignalInfo.ColourDescriptionPresent = 1;
 
     // parse user-specified encoder options, if present
     if (job->encoder_options != NULL && *job->encoder_options)
@@ -615,20 +797,37 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
                       job->par.num, job->par.den, UINT16_MAX);
 
     // some encoding parameters are used by filters to configure their output
-    if (pv->param.videoParam->mfx.FrameInfo.PicStruct != MFX_PICSTRUCT_PROGRESSIVE)
+    switch (pv->qsv_info->codec_id)
     {
-        job->qsv.enc_info.align_height = AV_QSV_ALIGN32(job->height);
+        case MFX_CODEC_HEVC:
+            job->qsv.enc_info.align_width  = AV_QSV_ALIGN32(job->width);
+            job->qsv.enc_info.align_height = AV_QSV_ALIGN32(job->height);
+            break;
+
+        case MFX_CODEC_AVC:
+        default:
+            job->qsv.enc_info.align_width  = AV_QSV_ALIGN16(job->width);
+            job->qsv.enc_info.align_height = AV_QSV_ALIGN16(job->height);
+            break;
     }
-    else
+    if (pv->param.videoParam->mfx.FrameInfo.PicStruct != MFX_PICSTRUCT_PROGRESSIVE)
     {
-        job->qsv.enc_info.align_height = AV_QSV_ALIGN16(job->height);
+        // additional alignment may be required
+        switch (pv->qsv_info->codec_id)
+        {
+            case MFX_CODEC_AVC:
+                job->qsv.enc_info.align_height = AV_QSV_ALIGN32(job->qsv.enc_info.align_height);
+                break;
+
+            default:
+                break;
+        }
     }
-    job->qsv.enc_info.align_width  = AV_QSV_ALIGN16(job->width);
     job->qsv.enc_info.pic_struct   = pv->param.videoParam->mfx.FrameInfo.PicStruct;
     job->qsv.enc_info.is_init_done = 1;
 
-    // encode to H.264 and set FrameInfo
-    pv->param.videoParam->mfx.CodecId                 = MFX_CODEC_AVC;
+    // set codec, profile/level and FrameInfo
+    pv->param.videoParam->mfx.CodecId                 = pv->qsv_info->codec_id;
     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;
@@ -871,7 +1070,7 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
      * 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
+     * values for all parameters relevant to the output bitstream
      */
     mfxStatus err;
     mfxVersion version;
@@ -941,7 +1140,10 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
     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;
+    if (pv->param.videoParam->mfx.CodecId == MFX_CODEC_AVC)
+    {
+        videoParam.ExtParam[videoParam.NumExtParam++] = (mfxExtBuffer*)sps_pps;
+    }
     // introduced in API 1.0
     memset(option1, 0, sizeof(mfxExtCodingOption));
     option1->Header.BufferId = MFX_EXTBUFF_CODING_OPTION;
@@ -959,10 +1161,18 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
         videoParam.ExtParam[videoParam.NumExtParam++] = (mfxExtBuffer*)option2;
     }
     err = MFXVideoENCODE_GetVideoParam(session, &videoParam);
-    MFXVideoENCODE_Close(session);
-    if (err == MFX_ERR_NONE)
+    if (err != MFX_ERR_NONE)
     {
-        // remove 32-bit NAL prefix (0x00 0x00 0x00 0x01)
+        hb_error("encqsvInit: MFXVideoENCODE_GetVideoParam failed (%d)", err);
+        hb_qsv_unload_plugins(&pv->loaded_plugins, session, version);
+        MFXClose(session);
+        return -1;
+    }
+
+    /* We have the final encoding parameters, now get the headers for muxing */
+    if (videoParam.mfx.CodecId == MFX_CODEC_AVC)
+    {
+        // remove 4-byte Annex B NAL unit 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);
@@ -970,14 +1180,21 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
         memmove(w->config->h264.pps, w->config->h264.pps + 4,
                 w->config->h264.pps_length);
     }
-    else
+    else if (videoParam.mfx.CodecId == MFX_CODEC_HEVC)
     {
-        hb_error("encqsvInit: MFXVideoENCODE_GetVideoParam failed (%d)", err);
-        hb_qsv_unload_plugins(&pv->loaded_plugins, session, version);
-        MFXClose(session);
-        return -1;
+        if (qsv_hevc_make_header(w, session) < 0)
+        {
+            hb_error("encqsvInit: qsv_hevc_make_header failed");
+            hb_qsv_unload_plugins(&pv->loaded_plugins, session, version);
+            MFXVideoENCODE_Close(session);
+            MFXClose(session);
+            return -1;
+        }
     }
 
+    /* We don't need this encode session once we have the header */
+    MFXVideoENCODE_Close(session);
+
 #ifdef HB_DRIVER_FIX_33
     if (la_workaround)
     {
@@ -1003,9 +1220,10 @@ int encqsvInit(hb_work_object_t *w, hb_job_t *job)
     if (videoParam.mfx.GopRefDist > 1)
     {
         /* the muxer needs to know to the init_delay */
-        switch (pv->qsv_info->codec_id)
+        switch (videoParam.mfx.CodecId)
         {
             case MFX_CODEC_AVC:
+            case MFX_CODEC_HEVC:
                 pv->init_delay = &w->config->h264.init_delay;
                 break;
             default: // unreachable
@@ -1473,16 +1691,31 @@ static int qsv_frame_is_key(mfxU16 FrameType)
 
 static void qsv_bitstream_slurp(hb_work_private_t *pv, mfxBitstream *bs)
 {
-    /*
-     * we need to convert the encoder's Annex B output
-     * to an MP4-compatible format (ISO/IEC 14496-15).
-     */
-    hb_buffer_t *buf = hb_nal_bitstream_annexb_to_mp4(bs->Data + bs->DataOffset,
-                                                      bs->DataLength);
-    if (buf == NULL)
+    hb_buffer_t *buf;
+
+    if (pv->param.videoParam->mfx.CodecId == MFX_CODEC_AVC)
     {
-        hb_error("encqsv: hb_nal_bitstream_annexb_to_mp4 failed");
-        goto fail;
+        /*
+         * We provided the muxer with the parameter sets in an MP4-compatible
+         * format (ISO/IEC 14496-15). We need to convert the bitstream to the
+         * same format to match the extradata.
+         */
+        if ((buf = hb_nal_bitstream_annexb_to_mp4(bs->Data + bs->DataOffset,
+                                                  bs->DataLength)) == NULL)
+        {
+            hb_error("encqsv: hb_nal_bitstream_annexb_to_mp4 failed");
+            goto fail;
+        }
+    }
+    else
+    {
+        /* Both extradata and bitstream are in Annex B format. */
+        if ((buf = hb_buffer_init(bs->DataLength)) == NULL)
+        {
+            hb_error("encqsv: hb_buffer_init failed");
+            goto fail;
+        }
+        memcpy(buf->data, bs->Data + bs->DataOffset, bs->DataLength);
     }
     bs->DataLength = bs->DataOffset = 0;
     bs->MaxLength  = pv->job->qsv.ctx->enc_space->p_buf_max_size;
index e6ad4ae23bdc9f029c077bbd5fc4c7f9686f0d14..bfa377089d29de1d4a3f6345d2487717178cdfe0 100644 (file)
@@ -324,6 +324,7 @@ static int avformatInit( hb_mux_object_t * m )
         } break;
 
         case HB_VCODEC_X265:
+        case HB_VCODEC_QSV_H265:
             track->st->codec->codec_id = AV_CODEC_ID_HEVC;
 
             if (job->config.h265.headers_length > 0)
@@ -1069,6 +1070,7 @@ static int avformatMux(hb_mux_object_t *m, hb_mux_data_t *track, hb_buffer_t *bu
     pkt.duration = duration;
 
     if (track->type == MUX_TYPE_VIDEO && ((job->vcodec & HB_VCODEC_H264_MASK) ||
+                                          (job->vcodec & HB_VCODEC_H265_MASK) ||
                                           (job->vcodec & HB_VCODEC_FFMPEG_MASK)))
     {
         if (buf->s.frametype == HB_FRAME_IDR)
index e383b806e8998cd40ec2a07bf66c13556298474c..60d371f825dcdec31ef15e3ff73998497f274a75 100644 (file)
@@ -18,6 +18,7 @@
 #include "hb_dict.h"
 #include "qsv_common.h"
 #include "h264_common.h"
+#include "h265_common.h"
 
 // QSV info for each codec
 static hb_qsv_info_t *hb_qsv_info_avc       = NULL;
@@ -45,6 +46,13 @@ static hb_triplet_t hb_qsv_h264_profiles[] =
     { "Progressive High",     "high|set4",      MFX_PROFILE_AVC_PROGRESSIVE_HIGH,     },
     { NULL,                                                                           },
 };
+static hb_triplet_t hb_qsv_h265_profiles[] =
+{
+    { "Main",               "main",             MFX_PROFILE_HEVC_MAIN,   },
+    { "Main 10",            "main10",           MFX_PROFILE_HEVC_MAIN10, },
+    { "Main Still Picture", "mainstillpicture", MFX_PROFILE_HEVC_MAINSP, },
+    { NULL,                                                              },
+};
 static hb_triplet_t hb_qsv_h264_levels[] =
 {
     { "1.0", "1.0", MFX_LEVEL_AVC_1,  },
@@ -66,6 +74,23 @@ static hb_triplet_t hb_qsv_h264_levels[] =
     { "5.2", "5.2", MFX_LEVEL_AVC_52, },
     { NULL,                           },
 };
+static hb_triplet_t hb_qsv_h265_levels[] =
+{
+    { "1.0", "1.0", MFX_LEVEL_HEVC_1,  },
+    { "2.0", "2.0", MFX_LEVEL_HEVC_2,  },
+    { "2.1", "2.1", MFX_LEVEL_HEVC_21, },
+    { "3.0", "3.0", MFX_LEVEL_HEVC_3,  },
+    { "3.1", "3.1", MFX_LEVEL_HEVC_31, },
+    { "4.0", "4.0", MFX_LEVEL_HEVC_4,  },
+    { "4.1", "4.1", MFX_LEVEL_HEVC_41, },
+    { "5.0", "5.0", MFX_LEVEL_HEVC_5,  },
+    { "5.1", "5.1", MFX_LEVEL_HEVC_51, },
+    { "5.2", "5.2", MFX_LEVEL_HEVC_52, },
+    { "6.0", "6.0", MFX_LEVEL_HEVC_6,  },
+    { "6.1", "6.1", MFX_LEVEL_HEVC_61, },
+    { "6.2", "6.2", MFX_LEVEL_HEVC_62, },
+    { NULL,                            },
+};
 
 // check available Intel Media SDK version against a minimum
 #define HB_CHECK_MFX_VERSION(MFX_VERSION, MAJOR, MINOR) \
@@ -114,7 +139,8 @@ static int qsv_implementation_is_hardware(mfxIMPL implementation)
 
 int hb_qsv_available()
 {
-    return hb_qsv_video_encoder_is_enabled(HB_VCODEC_QSV_H264);
+    return (hb_qsv_video_encoder_is_enabled(HB_VCODEC_QSV_H264) ||
+            hb_qsv_video_encoder_is_enabled(HB_VCODEC_QSV_H265));
 }
 
 int hb_qsv_video_encoder_is_enabled(int encoder)
@@ -123,6 +149,8 @@ int hb_qsv_video_encoder_is_enabled(int encoder)
     {
         case HB_VCODEC_QSV_H264:
             return hb_qsv_info_avc != NULL && hb_qsv_info_avc->available;
+        case HB_VCODEC_QSV_H265:
+            return hb_qsv_info_hevc != NULL && hb_qsv_info_hevc->available;
         default:
             return 0;
     }
@@ -773,23 +801,23 @@ void hb_qsv_info_print()
         }
         if (hb_qsv_info_hevc != NULL && hb_qsv_info_hevc->available)
         {
-            hb_deep_log(2, " - H.265 encoder: yes (unsupported)");
-            hb_deep_log(2, "    - preferred implementation: %s",
-                        hb_qsv_impl_get_name(hb_qsv_info_hevc->implementation));
+            hb_log(" - H.265 encoder: yes");
+            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,
+                log_capabilities(1, qsv_hardware_info_hevc.capabilities,
                                  "    - capabilities (hardware): ");
             }
             if (qsv_software_info_hevc.available)
             {
-                log_capabilities(2, qsv_software_info_hevc.capabilities,
+                log_capabilities(1, qsv_software_info_hevc.capabilities,
                                  "    - capabilities (software): ");
             }
         }
         else
         {
-            hb_deep_log(2, " - H.265 encoder: no");
+            hb_log(" - H.265 encoder: no");
         }
     }
 }
@@ -800,6 +828,8 @@ hb_qsv_info_t* hb_qsv_info_get(int encoder)
     {
         case HB_VCODEC_QSV_H264:
             return hb_qsv_info_avc;
+        case HB_VCODEC_QSV_H265:
+            return hb_qsv_info_hevc;
         default:
             return NULL;
     }
@@ -1220,6 +1250,9 @@ int hb_qsv_param_parse(hb_qsv_param_t *param, hb_qsv_info_t *info,
                 case MFX_CODEC_AVC:
                     ivalue = hb_qsv_atoindex(hb_h264_vidformat_names, value, &error);
                     break;
+                case MFX_CODEC_HEVC:
+                    ivalue = hb_qsv_atoindex(hb_h265_vidformat_names, value, &error);
+                    break;
                 default:
                     return HB_QSV_PARAM_UNSUPPORTED;
             }
@@ -1242,6 +1275,9 @@ int hb_qsv_param_parse(hb_qsv_param_t *param, hb_qsv_info_t *info,
                 case MFX_CODEC_AVC:
                     ivalue = hb_qsv_atoindex(hb_h264_fullrange_names, value, &error);
                     break;
+                case MFX_CODEC_HEVC:
+                    ivalue = hb_qsv_atoindex(hb_h265_fullrange_names, value, &error);
+                    break;
                 default:
                     return HB_QSV_PARAM_UNSUPPORTED;
             }
@@ -1264,6 +1300,9 @@ int hb_qsv_param_parse(hb_qsv_param_t *param, hb_qsv_info_t *info,
                 case MFX_CODEC_AVC:
                     ivalue = hb_qsv_atoindex(hb_h264_colorprim_names, value, &error);
                     break;
+                case MFX_CODEC_HEVC:
+                    ivalue = hb_qsv_atoindex(hb_h265_colorprim_names, value, &error);
+                    break;
                 default:
                     return HB_QSV_PARAM_UNSUPPORTED;
             }
@@ -1287,6 +1326,9 @@ int hb_qsv_param_parse(hb_qsv_param_t *param, hb_qsv_info_t *info,
                 case MFX_CODEC_AVC:
                     ivalue = hb_qsv_atoindex(hb_h264_transfer_names, value, &error);
                     break;
+                case MFX_CODEC_HEVC:
+                    ivalue = hb_qsv_atoindex(hb_h265_transfer_names, value, &error);
+                    break;
                 default:
                     return HB_QSV_PARAM_UNSUPPORTED;
             }
@@ -1310,6 +1352,9 @@ int hb_qsv_param_parse(hb_qsv_param_t *param, hb_qsv_info_t *info,
                 case MFX_CODEC_AVC:
                     ivalue = hb_qsv_atoindex(hb_h264_colmatrix_names, value, &error);
                     break;
+                case MFX_CODEC_HEVC:
+                    ivalue = hb_qsv_atoindex(hb_h265_colmatrix_names, value, &error);
+                    break;
                 default:
                     return HB_QSV_PARAM_UNSUPPORTED;
             }
@@ -1479,6 +1524,11 @@ int hb_qsv_profile_parse(hb_qsv_param_t *param, hb_qsv_info_t *info, const char
             case MFX_CODEC_AVC:
                 profile = hb_triplet4key(hb_qsv_h264_profiles, profile_key);
                 break;
+
+            case MFX_CODEC_HEVC:
+                profile = hb_triplet4key(hb_qsv_h265_profiles, profile_key);
+                break;
+
             default:
                 break;
         }
@@ -1501,6 +1551,11 @@ int hb_qsv_level_parse(hb_qsv_param_t *param, hb_qsv_info_t *info, const char *l
             case MFX_CODEC_AVC:
                 level = hb_triplet4key(hb_qsv_h264_levels, level_key);
                 break;
+
+            case MFX_CODEC_HEVC:
+                level = hb_triplet4key(hb_qsv_h265_levels, level_key);
+                break;
+
             default:
                 break;
         }
@@ -1546,6 +1601,8 @@ const char* const* hb_qsv_profile_get_names(int encoder)
     {
         case HB_VCODEC_QSV_H264:
             return hb_h264_profile_names;
+        case HB_VCODEC_QSV_H265:
+            return hb_h265_profile_names;
         default:
             return NULL;
     }
@@ -1557,6 +1614,8 @@ const char* const* hb_qsv_level_get_names(int encoder)
     {
         case HB_VCODEC_QSV_H264:
             return hb_h264_level_names;
+        case HB_VCODEC_QSV_H265:
+            return hb_h265_level_names;
         default:
             return NULL;
     }
@@ -1564,36 +1623,43 @@ const char* const* hb_qsv_level_get_names(int encoder)
 
 const char* hb_qsv_video_quality_get_name(uint32_t codec)
 {
-    uint64_t caps;
+    uint64_t caps = 0;
     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";
+            if (hb_qsv_info_avc != NULL) caps = hb_qsv_info_avc->capabilities;
+            break;
+
+        case HB_VCODEC_QSV_H265:
+            if (hb_qsv_info_hevc != NULL) caps = hb_qsv_info_hevc->capabilities;
+            break;
 
         default:
-            return "QP";
+            break;
     }
+    return (caps & HB_QSV_CAP_RATECONTROL_ICQ) ? "ICQ" : "QP";
 }
 
 void hb_qsv_video_quality_get_limits(uint32_t codec, float *low, float *high,
                                      float *granularity, int *direction)
 {
-    uint64_t caps;
+    uint64_t caps = 0;
     switch (codec)
     {
-        case HB_VCODEC_QSV_H264:
-            caps = hb_qsv_info_avc != NULL ? hb_qsv_info_avc->capabilities : 0;
+        case HB_VCODEC_QSV_H265:
+            if (hb_qsv_info_hevc != NULL) caps = hb_qsv_info_hevc->capabilities;
             *direction   = 1;
             *granularity = 1.;
             *low         = (caps & HB_QSV_CAP_RATECONTROL_ICQ) ? 1. : 0.;
             *high        = 51.;
             break;
 
+        case HB_VCODEC_QSV_H264:
         default:
+            if (hb_qsv_info_avc != NULL) caps = hb_qsv_info_avc->capabilities;
             *direction   = 1;
             *granularity = 1.;
-            *low         = 0.;
+            *low         = (caps & HB_QSV_CAP_RATECONTROL_ICQ) ? 1. : 0.;
             *high        = 51.;
             break;
     }
@@ -1893,6 +1959,9 @@ const char* hb_qsv_codec_name(uint32_t codec_id)
         case MFX_CODEC_AVC:
             return "H.264/AVC";
 
+        case MFX_CODEC_HEVC:
+            return "H.265/HEVC";
+
         default:
             return NULL;
     }
@@ -1907,6 +1976,10 @@ const char* hb_qsv_profile_name(uint32_t codec_id, uint16_t profile_id)
             profile = hb_triplet4value(hb_qsv_h264_profiles, profile_id);
             break;
 
+        case MFX_CODEC_HEVC:
+            profile = hb_triplet4value(hb_qsv_h265_profiles, profile_id);
+            break;
+
         default:
             break;
     }
@@ -1922,6 +1995,10 @@ const char* hb_qsv_level_name(uint32_t codec_id, uint16_t level_id)
             level = hb_triplet4value(hb_qsv_h264_levels, level_id);
             break;
 
+        case MFX_CODEC_HEVC:
+            level = hb_triplet4value(hb_qsv_h265_levels, level_id);
+            break;
+
         default:
             break;
     }
index 8a58519eed2617df74ffd2a27e85a135c5035564..11c8d291928a36a2f3280e392726f492fda949d4 100644 (file)
@@ -363,6 +363,7 @@ void hb_display_job_info(hb_job_t *job)
                 case HB_VCODEC_X264:
                 case HB_VCODEC_X265:
                 case HB_VCODEC_QSV_H264:
+                case HB_VCODEC_QSV_H265:
                     hb_log("     + preset:  %s", job->encoder_preset);
                 default:
                     break;
@@ -391,6 +392,7 @@ void hb_display_job_info(hb_job_t *job)
                 case HB_VCODEC_X264:
                 case HB_VCODEC_X265:
                 case HB_VCODEC_QSV_H264:
+                case HB_VCODEC_QSV_H265:
                     hb_log("     + profile: %s", job->encoder_profile);
                 default:
                     break;
@@ -402,6 +404,7 @@ void hb_display_job_info(hb_job_t *job)
             {
                 case HB_VCODEC_X264:
                 case HB_VCODEC_QSV_H264:
+                case HB_VCODEC_QSV_H265:
                     hb_log("     + level:   %s", job->encoder_level);
                 default:
                     break;
@@ -1359,6 +1362,7 @@ static void do_job(hb_job_t *job)
             w = hb_get_work( job->h, WORK_ENCX264 );
             break;
         case HB_VCODEC_QSV_H264:
+        case HB_VCODEC_QSV_H265:
             w = hb_get_work( job->h, WORK_ENCQSV );
             break;
         case HB_VCODEC_THEORA: