summaryrefslogtreecommitdiffstats
path: root/libhb
diff options
context:
space:
mode:
authorTim Walker <[email protected]>2015-09-20 12:54:01 +0200
committerTim Walker <[email protected]>2015-09-20 12:54:01 +0200
commitfa6604feffe1d61535ae0093055b5b647a1a4a18 (patch)
treed9d47f2d1d8aceca0495ac96130b908298608a5f /libhb
parent74234342b8ec3aa7fcbd5a3b2cdf3d5e56bd3d99 (diff)
qsv: HEVC encoding support.
Diffstat (limited to 'libhb')
-rw-r--r--libhb/common.c1
-rw-r--r--libhb/common.h2
-rw-r--r--libhb/enc_qsv.c345
-rw-r--r--libhb/muxavformat.c2
-rw-r--r--libhb/qsv_common.c107
-rw-r--r--libhb/work.c4
6 files changed, 390 insertions, 71 deletions
diff --git a/libhb/common.c b/libhb/common.c
index b6d36d203..6877e31a6 100644
--- a/libhb/common.c
+++ b/libhb/common.c
@@ -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, },
diff --git a/libhb/common.h b/libhb/common.h
index 9cf51b3e2..a928eb484 100644
--- a/libhb/common.h
+++ b/libhb/common.h
@@ -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;
diff --git a/libhb/enc_qsv.c b/libhb/enc_qsv.c
index 2f19b36f0..39ed16bef 100644
--- a/libhb/enc_qsv.c
+++ b/libhb/enc_qsv.c
@@ -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;
diff --git a/libhb/muxavformat.c b/libhb/muxavformat.c
index e6ad4ae23..bfa377089 100644
--- a/libhb/muxavformat.c
+++ b/libhb/muxavformat.c
@@ -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)
diff --git a/libhb/qsv_common.c b/libhb/qsv_common.c
index e383b806e..60d371f82 100644
--- a/libhb/qsv_common.c
+++ b/libhb/qsv_common.c
@@ -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;
}
diff --git a/libhb/work.c b/libhb/work.c
index 8a58519ee..11c8d2919 100644
--- a/libhb/work.c
+++ b/libhb/work.c
@@ -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: