diff options
author | Tim Walker <[email protected]> | 2015-09-20 12:54:01 +0200 |
---|---|---|
committer | Tim Walker <[email protected]> | 2015-09-20 12:54:01 +0200 |
commit | fa6604feffe1d61535ae0093055b5b647a1a4a18 (patch) | |
tree | d9d47f2d1d8aceca0495ac96130b908298608a5f | |
parent | 74234342b8ec3aa7fcbd5a3b2cdf3d5e56bd3d99 (diff) |
qsv: HEVC encoding support.
-rw-r--r-- | libhb/common.c | 1 | ||||
-rw-r--r-- | libhb/common.h | 2 | ||||
-rw-r--r-- | libhb/enc_qsv.c | 345 | ||||
-rw-r--r-- | libhb/muxavformat.c | 2 | ||||
-rw-r--r-- | libhb/qsv_common.c | 107 | ||||
-rw-r--r-- | libhb/work.c | 4 |
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: |