diff options
Diffstat (limited to 'libhb')
-rw-r--r-- | libhb/enc_qsv.c | 1543 | ||||
-rw-r--r-- | libhb/enc_qsv.h | 38 | ||||
-rw-r--r-- | libhb/h264_common.h | 17 | ||||
-rw-r--r-- | libhb/qsv_common.c | 780 | ||||
-rw-r--r-- | libhb/qsv_common.h | 119 | ||||
-rw-r--r-- | libhb/qsv_filter.c | 648 | ||||
-rw-r--r-- | libhb/qsv_filter.h | 35 | ||||
-rw-r--r-- | libhb/qsv_filter_pp.c | 916 | ||||
-rw-r--r-- | libhb/qsv_filter_pp.h | 114 | ||||
-rw-r--r-- | libhb/qsv_memory.c | 120 | ||||
-rw-r--r-- | libhb/qsv_memory.h | 55 |
11 files changed, 4385 insertions, 0 deletions
diff --git a/libhb/enc_qsv.c b/libhb/enc_qsv.c new file mode 100644 index 000000000..c1c832a81 --- /dev/null +++ b/libhb/enc_qsv.c @@ -0,0 +1,1543 @@ +/* ********************************************************************* *\ + +Copyright (C) 2013 Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +- Neither the name of Intel Corporation nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +\* ********************************************************************* */ + +#include "hb.h" +#include "enc_qsv.h" +#include "qsv_common.h" +#include "qsv_memory.h" +#include "h264_common.h" + +int encqsvInit( hb_work_object_t *, hb_job_t * ); +int encqsvWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); +void encqsvClose( hb_work_object_t * ); + +hb_work_object_t hb_encqsv = +{ + WORK_ENCQSV, + "H.264/AVC encoder (Intel QSV)", + encqsvInit, + encqsvWork, + encqsvClose +}; + +struct hb_work_private_s +{ + hb_job_t *job; + uint32_t frames_in; + uint32_t frames_out; + int64_t last_start; + + hb_qsv_param_t param; + av_qsv_space enc_space; + + mfxEncodeCtrl force_keyframe; + struct + { + int index; + int64_t start; + } next_chapter; + +#define BFRM_DELAY_MAX 16 + // for DTS generation (when MSDK API < 1.6 or VFR) + int bfrm_delay; + int bfrm_workaround; + int64_t init_pts[BFRM_DELAY_MAX + 1]; + hb_list_t *list_dts; + + int async_depth; + int max_async_depth; + + // if encode-only, system memory used + int is_sys_mem; + struct SwsContext *sws_context_to_nv12; + + // whether to expect input from VPP or from QSV decode + int is_vpp_present; + + // whether the encoder is initialized + int init_done; + + hb_list_t *delayed_processing; +}; + +// for DTS generation (when MSDK API < 1.6 or VFR) +static void hb_qsv_add_new_dts(hb_list_t *list, int64_t new_dts) +{ + if (list != NULL) + { + int64_t *item = malloc(sizeof(int64_t)); + if (item != NULL) + { + *item = new_dts; + hb_list_add(list, item); + } + } +} +static int64_t hb_qsv_pop_next_dts(hb_list_t *list) +{ + int64_t next_dts = INT64_MIN; + if (list != NULL && hb_list_count(list) > 0) + { + int64_t *item = hb_list_item(list, 0); + if (item != NULL) + { + next_dts = *item; + hb_list_rem(list, item); + free(item); + } + } + return next_dts; +} + +static const char* qsv_h264_profile_xlat(int profile) +{ + switch (profile) + { + case MFX_PROFILE_AVC_CONSTRAINED_BASELINE: + return "Constrained Baseline"; + case MFX_PROFILE_AVC_BASELINE: + return "Baseline"; + case MFX_PROFILE_AVC_EXTENDED: + return "Extended"; + case MFX_PROFILE_AVC_MAIN: + return "Main"; + case MFX_PROFILE_AVC_CONSTRAINED_HIGH: + return "Constrained High"; + case MFX_PROFILE_AVC_PROGRESSIVE_HIGH: + return "Progressive High"; + case MFX_PROFILE_AVC_HIGH: + return "High"; + case MFX_PROFILE_UNKNOWN: + default: + return NULL; + } +} + +static const char* qsv_h264_level_xlat(int level) +{ + int i; + for (i = 0; hb_h264_level_names[i] != NULL; i++) + { + if (hb_h264_level_values[i] == level) + { + return hb_h264_level_names[i]; + } + } + return NULL; +} + +int qsv_enc_init(av_qsv_context *qsv, hb_work_private_t *pv) +{ + int i = 0; + mfxStatus sts; + hb_job_t *job = pv->job; + + if (pv->init_done) + { + return 0; + } + + if (qsv == NULL) + { + if (!pv->is_sys_mem) + { + hb_error("qsv_enc_init: decode enabled but no context!"); + return 3; + } + job->qsv = qsv = av_mallocz(sizeof(av_qsv_context)); + } + + av_qsv_space *qsv_encode = qsv->enc_space; + if (qsv_encode == NULL) + { + // if only for encode + if (pv->is_sys_mem) + { + // no need to use additional sync as encode only -> single thread + // XXX: this zeroes the session handle, so call it before MFXInit + av_qsv_add_context_usage(qsv, 0); + + // initialize the session + qsv->impl = MFX_IMPL_AUTO_ANY; + qsv->ver.Major = AV_QSV_MSDK_VERSION_MAJOR; + qsv->ver.Minor = AV_QSV_MSDK_VERSION_MINOR; + sts = MFXInit(qsv->impl, &qsv->ver, &qsv->mfx_session); + if (sts != MFX_ERR_NONE) + { + hb_error("qsv_enc_init: MFXInit failed (%d)", sts); + *job->die = 1; + return -1; + } + } + qsv->enc_space = qsv_encode = &pv->enc_space; + } + + if (!pv->is_sys_mem) + { + if (!pv->is_vpp_present && job->list_filter != NULL) + { + for (i = 0; i < hb_list_count(job->list_filter); i++) + { + hb_filter_object_t *filter = hb_list_item(job->list_filter, i); + if (filter->id == HB_FILTER_QSV_PRE || + filter->id == HB_FILTER_QSV_POST || + filter->id == HB_FILTER_QSV) + { + pv->is_vpp_present = 1; + break; + } + } + } + + if (pv->is_vpp_present) + { + if (qsv->vpp_space == NULL) + { + return 2; + } + for (i = 0; i < av_qsv_list_count(qsv->vpp_space); i++) + { + av_qsv_space *vpp = av_qsv_list_item(qsv->vpp_space, i); + if (!vpp->is_init_done) + { + return 2; + } + } + } + + av_qsv_space *dec_space = qsv->dec_space; + if (dec_space == NULL || !dec_space->is_init_done) + { + return 2; + } + } + else + { + pv->sws_context_to_nv12 = hb_sws_get_context(job->width, job->height, + AV_PIX_FMT_YUV420P, + job->width, job->height, + AV_PIX_FMT_NV12, + SWS_LANCZOS|SWS_ACCURATE_RND); + } + + // allocate tasks + qsv_encode->p_buf_max_size = AV_QSV_BUF_SIZE_DEFAULT; + qsv_encode->tasks = av_qsv_list_init(HAVE_THREADS); + for (i = 0; i < pv->max_async_depth; i++) + { + av_qsv_task *task = av_mallocz(sizeof(av_qsv_task)); + task->bs = av_mallocz(sizeof(mfxBitstream)); + task->bs->Data = av_mallocz(sizeof(uint8_t) * qsv_encode->p_buf_max_size); + task->bs->MaxLength = qsv_encode->p_buf_max_size; + task->bs->DataLength = 0; + task->bs->DataOffset = 0; + av_qsv_list_add(qsv_encode->tasks, task); + } + + // setup surface allocation + qsv_encode->m_mfxVideoParam.IOPattern = (pv->is_sys_mem ? + MFX_IOPATTERN_IN_SYSTEM_MEMORY : + MFX_IOPATTERN_IN_OPAQUE_MEMORY); + memset(&qsv_encode->request, 0, sizeof(mfxFrameAllocRequest) * 2); + sts = MFXVideoENCODE_QueryIOSurf(qsv->mfx_session, + &qsv_encode->m_mfxVideoParam, + &qsv_encode->request); + if (sts < MFX_ERR_NONE) // ignore warnings + { + hb_error("qsv_enc_init: MFXVideoENCODE_QueryIOSurf failed (%d)", sts); + *job->die = 1; + return -1; + } + + // allocate surfaces + if (pv->is_sys_mem) + { + qsv_encode->surface_num = FFMIN(qsv_encode->request[0].NumFrameSuggested + + pv->max_async_depth, AV_QSV_SURFACE_NUM); + if (qsv_encode->surface_num <= 0) + { + qsv_encode->surface_num = AV_QSV_SURFACE_NUM; + } + for (i = 0; i < qsv_encode->surface_num; i++) + { + qsv_encode->p_surfaces[i] = av_mallocz(sizeof(mfxFrameSurface1)); + AV_QSV_CHECK_POINTER(qsv_encode->p_surfaces[i], MFX_ERR_MEMORY_ALLOC); + memcpy(&(qsv_encode->p_surfaces[i]->Info), + &(qsv_encode->request[0].Info), sizeof(mfxFrameInfo)); + } + } + else + { + av_qsv_space *in_space = qsv->dec_space; + if (pv->is_vpp_present) + { + // we get our input from VPP instead + in_space = av_qsv_list_item(qsv->vpp_space, + av_qsv_list_count(qsv->vpp_space) - 1); + } + // introduced in API 1.3 + memset(&qsv_encode->ext_opaque_alloc, 0, sizeof(mfxExtOpaqueSurfaceAlloc)); + qsv_encode->ext_opaque_alloc.Header.BufferId = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION; + qsv_encode->ext_opaque_alloc.Header.BufferSz = sizeof(mfxExtOpaqueSurfaceAlloc); + qsv_encode->ext_opaque_alloc.In.Surfaces = in_space->p_surfaces; + qsv_encode->ext_opaque_alloc.In.NumSurface = in_space->surface_num; + qsv_encode->ext_opaque_alloc.In.Type = qsv_encode->request[0].Type; + qsv_encode->m_mfxVideoParam.ExtParam[qsv_encode->m_mfxVideoParam.NumExtParam++] = (mfxExtBuffer*)&qsv_encode->ext_opaque_alloc; + } + + // allocate sync points + qsv_encode->sync_num = (qsv_encode->surface_num ? + FFMIN(qsv_encode->surface_num, AV_QSV_SYNC_NUM) : + AV_QSV_SYNC_NUM); + for (i = 0; i < qsv_encode->sync_num; i++) + { + qsv_encode->p_syncp[i] = av_mallocz(sizeof(av_qsv_sync)); + AV_QSV_CHECK_POINTER(qsv_encode->p_syncp[i], MFX_ERR_MEMORY_ALLOC); + qsv_encode->p_syncp[i]->p_sync = av_mallocz(sizeof(mfxSyncPoint)); + AV_QSV_CHECK_POINTER(qsv_encode->p_syncp[i]->p_sync, MFX_ERR_MEMORY_ALLOC); + } + + // initialize the encoder + sts = MFXVideoENCODE_Init(qsv->mfx_session, &qsv_encode->m_mfxVideoParam); + if (sts < MFX_ERR_NONE) // ignore warnings + { + hb_error("qsv_enc_init: MFXVideoENCODE_Init failed (%d)", sts); + *job->die = 1; + return -1; + } + qsv_encode->is_init_done = 1; + + pv->init_done = 1; + return 0; +} + +/*********************************************************************** + * encqsvInit + *********************************************************************** + * + **********************************************************************/ +int encqsvInit(hb_work_object_t *w, hb_job_t *job) +{ + hb_work_private_t *pv = calloc(1, sizeof(hb_work_private_t)); + w->private_data = pv; + + pv->job = job; + pv->is_sys_mem = !hb_qsv_decode_is_enabled(job); + pv->delayed_processing = hb_list_init(); + pv->last_start = INT64_MIN; + pv->frames_in = 0; + pv->frames_out = 0; + pv->init_done = 0; + pv->is_vpp_present = 0; + + // set up a re-usable mfxEncodeCtrl to force keyframes (e.g. for chapters) + pv->force_keyframe.QP = 0; + pv->force_keyframe.FrameType = MFX_FRAMETYPE_I|MFX_FRAMETYPE_IDR|MFX_FRAMETYPE_REF; + pv->force_keyframe.NumExtParam = 0; + pv->force_keyframe.NumPayload = 0; + pv->force_keyframe.ExtParam = NULL; + pv->force_keyframe.Payload = NULL; + + pv->next_chapter.index = 0; + pv->next_chapter.start = INT64_MIN; + + // default encoding parameters + if (hb_qsv_param_default(&pv->param, &pv->enc_space.m_mfxVideoParam)) + { + hb_error("encqsvInit: hb_qsv_param_default failed"); + return -1; + } + + // set AsyncDepth to match that of decode and VPP + pv->param.videoParam->AsyncDepth = job->qsv_async_depth; + + // enable and set colorimetry (video signal information) + pv->param.videoSignalInfo.ColourDescriptionPresent = 1; + switch (job->color_matrix_code) + { + case 4: + // custom + pv->param.videoSignalInfo.ColourPrimaries = job->color_prim; + pv->param.videoSignalInfo.TransferCharacteristics = job->color_transfer; + pv->param.videoSignalInfo.MatrixCoefficients = job->color_matrix; + break; + case 3: + // ITU BT.709 HD content + pv->param.videoSignalInfo.ColourPrimaries = HB_COLR_PRI_BT709; + pv->param.videoSignalInfo.TransferCharacteristics = HB_COLR_TRA_BT709; + pv->param.videoSignalInfo.MatrixCoefficients = HB_COLR_MAT_BT709; + break; + case 2: + // ITU BT.601 DVD or SD TV content (PAL) + pv->param.videoSignalInfo.ColourPrimaries = HB_COLR_PRI_EBUTECH; + pv->param.videoSignalInfo.TransferCharacteristics = HB_COLR_TRA_BT709; + pv->param.videoSignalInfo.MatrixCoefficients = HB_COLR_MAT_SMPTE170M; + break; + case 1: + // ITU BT.601 DVD or SD TV content (NTSC) + pv->param.videoSignalInfo.ColourPrimaries = HB_COLR_PRI_SMPTEC; + pv->param.videoSignalInfo.TransferCharacteristics = HB_COLR_TRA_BT709; + pv->param.videoSignalInfo.MatrixCoefficients = HB_COLR_MAT_SMPTE170M; + break; + default: + // detected during scan + pv->param.videoSignalInfo.ColourPrimaries = job->title->color_prim; + pv->param.videoSignalInfo.TransferCharacteristics = job->title->color_transfer; + pv->param.videoSignalInfo.MatrixCoefficients = job->title->color_matrix; + break; + } + + // parse user-specified advanced options, if present + if (job->advanced_opts != NULL && job->advanced_opts[0] != '\0') + { + hb_dict_t *options_list; + hb_dict_entry_t *option = NULL; + options_list = hb_encopts_to_dict(job->advanced_opts, job->vcodec); + while ((option = hb_dict_next(options_list, option)) != NULL) + { + switch (hb_qsv_param_parse(&pv->param, + option->key, option->value, job->vcodec)) + { + case HB_QSV_PARAM_OK: + break; + + case HB_QSV_PARAM_BAD_NAME: + hb_log("encqsvInit: hb_qsv_param_parse: bad key %s", + option->key); + break; + case HB_QSV_PARAM_BAD_VALUE: + hb_log("encqsvInit: hb_qsv_param_parse: bad value %s for key %s", + option->value, option->key); + break; + case HB_QSV_PARAM_UNSUPPORTED: + hb_log("encqsvInit: hb_qsv_param_parse: unsupported option %s", + option->key); + break; + + case HB_QSV_PARAM_ERROR: + default: + hb_log("encqsvInit: hb_qsv_param_parse: unknown error"); + break; + } + } + hb_dict_free(&options_list); + } + + // reload colorimetry in case values were set in advanced_opts + if (pv->param.videoSignalInfo.ColourDescriptionPresent) + { + job->color_matrix_code = 4; + job->color_prim = pv->param.videoSignalInfo.ColourPrimaries; + job->color_transfer = pv->param.videoSignalInfo.TransferCharacteristics; + job->color_matrix = pv->param.videoSignalInfo.MatrixCoefficients; + } + else + { + job->color_matrix_code = 0; + job->color_prim = HB_COLR_PRI_UNDEF; + job->color_transfer = HB_COLR_TRA_UNDEF; + job->color_matrix = HB_COLR_MAT_UNDEF; + } + + // sanitize values that may exceed the Media SDK variable size + int64_t vrate, vrate_base; + int64_t par_width, par_height; + hb_limit_rational64(&vrate, &vrate_base, + job->vrate, job->vrate_base, UINT32_MAX); + hb_limit_rational64(&par_width, &par_height, + job->anamorphic.par_width, + job->anamorphic.par_height, UINT16_MAX); + + // some encoding parameters are used by filters to configure their output + if (pv->param.videoParam->mfx.FrameInfo.PicStruct != MFX_PICSTRUCT_PROGRESSIVE) + { + job->qsv_enc_info.align_height = AV_QSV_ALIGN32(job->height); + } + else + { + job->qsv_enc_info.align_height = AV_QSV_ALIGN16(job->height); + } + 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; + pv->param.videoParam->mfx.CodecLevel = MFX_LEVEL_UNKNOWN; + pv->param.videoParam->mfx.CodecProfile = MFX_PROFILE_UNKNOWN; + pv->param.videoParam->mfx.FrameInfo.FourCC = MFX_FOURCC_NV12; + pv->param.videoParam->mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420; + pv->param.videoParam->mfx.FrameInfo.FrameRateExtN = vrate; + pv->param.videoParam->mfx.FrameInfo.FrameRateExtD = vrate_base; + pv->param.videoParam->mfx.FrameInfo.AspectRatioW = par_width; + pv->param.videoParam->mfx.FrameInfo.AspectRatioH = par_height; + pv->param.videoParam->mfx.FrameInfo.CropX = 0; + pv->param.videoParam->mfx.FrameInfo.CropY = 0; + pv->param.videoParam->mfx.FrameInfo.CropW = job->width; + pv->param.videoParam->mfx.FrameInfo.CropH = job->height; + pv->param.videoParam->mfx.FrameInfo.PicStruct = job->qsv_enc_info.pic_struct; + pv->param.videoParam->mfx.FrameInfo.Width = job->qsv_enc_info.align_width; + pv->param.videoParam->mfx.FrameInfo.Height = job->qsv_enc_info.align_height; + + // set H.264 profile and level + if (job->h264_profile != NULL && job->h264_profile[0] != '\0' && + strcasecmp(job->h264_profile, "auto")) + { + if (!strcasecmp(job->h264_profile, "baseline")) + { + pv->param.videoParam->mfx.CodecProfile = MFX_PROFILE_AVC_BASELINE; + } + else if (!strcasecmp(job->h264_profile, "main")) + { + pv->param.videoParam->mfx.CodecProfile = MFX_PROFILE_AVC_MAIN; + } + else if (!strcasecmp(job->h264_profile, "high")) + { + pv->param.videoParam->mfx.CodecProfile = MFX_PROFILE_AVC_HIGH; + } + else + { + hb_error("encqsvInit: bad profile %s", job->h264_profile); + return -1; + } + } + if (job->h264_level != NULL && job->h264_level[0] != '\0' && + strcasecmp(job->h264_level, "auto")) + { + int err; + int i = hb_qsv_atoindex(hb_h264_level_names, job->h264_level, &err); + if (err || i >= (sizeof(hb_h264_level_values) / + sizeof(hb_h264_level_values[0]))) + { + hb_error("encqsvInit: bad level %s", job->h264_level); + return -1; + } + else if (hb_qsv_info->capabilities & HB_QSV_CAP_MSDK_API_1_6) + { + pv->param.videoParam->mfx.CodecLevel = HB_QSV_CLIP3(MFX_LEVEL_AVC_1, + MFX_LEVEL_AVC_52, + hb_h264_level_values[i]); + } + else + { + // Media SDK API < 1.6, MFX_LEVEL_AVC_52 unsupported + pv->param.videoParam->mfx.CodecLevel = HB_QSV_CLIP3(MFX_LEVEL_AVC_1, + MFX_LEVEL_AVC_51, + hb_h264_level_values[i]); + } + } + + // interlaced encoding is not always possible + if (pv->param.videoParam->mfx.FrameInfo.PicStruct != MFX_PICSTRUCT_PROGRESSIVE) + { + if (pv->param.videoParam->mfx.CodecProfile == MFX_PROFILE_AVC_CONSTRAINED_BASELINE || + pv->param.videoParam->mfx.CodecProfile == MFX_PROFILE_AVC_BASELINE || + pv->param.videoParam->mfx.CodecProfile == MFX_PROFILE_AVC_PROGRESSIVE_HIGH) + { + hb_error("encqsvInit: profile %s doesn't support interlaced encoding", + qsv_h264_profile_xlat(pv->param.videoParam->mfx.CodecProfile)); + return -1; + } + if ((pv->param.videoParam->mfx.CodecLevel >= MFX_LEVEL_AVC_1b && + pv->param.videoParam->mfx.CodecLevel <= MFX_LEVEL_AVC_2) || + (pv->param.videoParam->mfx.CodecLevel >= MFX_LEVEL_AVC_42)) + { + hb_error("encqsvInit: level %s doesn't support interlaced encoding", + qsv_h264_level_xlat(pv->param.videoParam->mfx.CodecLevel)); + return -1; + } + } + + // set rate control paremeters + if (job->vquality >= 0) + { + // introduced in API 1.1 + pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_CQP; + pv->param.videoParam->mfx.QPI = HB_QSV_CLIP3(0, 51, job->vquality + pv->param.rc.cqp_offsets[0]); + pv->param.videoParam->mfx.QPP = HB_QSV_CLIP3(0, 51, job->vquality + pv->param.rc.cqp_offsets[1]); + pv->param.videoParam->mfx.QPB = HB_QSV_CLIP3(0, 51, job->vquality + pv->param.rc.cqp_offsets[2]); + } + else if (job->vbitrate > 0) + { + // sanitize lookahead + if (!(hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_LOOKAHEAD)) + { + // lookahead not supported + pv->param.rc.lookahead = 0; + } + else if (pv->param.rc.lookahead > 0 && + pv->param.videoParam->mfx.FrameInfo.PicStruct != MFX_PICSTRUCT_PROGRESSIVE) + { + // user force-enabled lookahead but we can't use it + hb_log("encqsvInit: MFX_RATECONTROL_LA not used (LookAhead is progressive-only)"); + pv->param.rc.lookahead = 0; + } + else if (pv->param.rc.lookahead < 0) + { + if (pv->param.rc.vbv_max_bitrate > 0 || + pv->param.videoParam->mfx.FrameInfo.PicStruct != MFX_PICSTRUCT_PROGRESSIVE) + { + // lookahead doesn't support VBV or interlaced encoding + pv->param.rc.lookahead = 0; + } + else + { + // set automatically based on target usage + pv->param.rc.lookahead = (pv->param.videoParam->mfx.TargetUsage <= MFX_TARGETUSAGE_2); + } + } + else + { + // user force-enabled or force-disabled lookahead + pv->param.rc.lookahead = !!pv->param.rc.lookahead; + } + 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) + { + hb_log("encqsvInit: MFX_RATECONTROL_LA, ignoring VBV"); + } + } + else if (job->vbitrate == pv->param.rc.vbv_max_bitrate) + { + // introduced in API 1.0 + pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_CBR; + pv->param.videoParam->mfx.MaxKbps = job->vbitrate; + pv->param.videoParam->mfx.TargetKbps = job->vbitrate; + pv->param.videoParam->mfx.BufferSizeInKB = (pv->param.rc.vbv_buffer_size / 8); + // only set BufferSizeInKB and InitialDelayInKB is bufsize is set + // else Media SDK will pick some good values for us automatically + if (pv->param.rc.vbv_buffer_size > 0) + { + if (pv->param.rc.vbv_buffer_init > 1.0) + { + pv->param.videoParam->mfx.InitialDelayInKB = (pv->param.rc.vbv_buffer_init / 8); + } + else + { + pv->param.videoParam->mfx.InitialDelayInKB = (pv->param.rc.vbv_buffer_size * + pv->param.rc.vbv_buffer_init / 8); + } + pv->param.videoParam->mfx.BufferSizeInKB = (pv->param.rc.vbv_buffer_size / 8); + } + } + else if (pv->param.rc.vbv_max_bitrate > 0) + { + // introduced in API 1.0 + pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_VBR; + pv->param.videoParam->mfx.MaxKbps = pv->param.rc.vbv_max_bitrate; + pv->param.videoParam->mfx.TargetKbps = job->vbitrate; + // only set BufferSizeInKB and InitialDelayInKB is bufsize is set + // else Media SDK will pick some good values for us automatically + if (pv->param.rc.vbv_buffer_size > 0) + { + if (pv->param.rc.vbv_buffer_init > 1.0) + { + pv->param.videoParam->mfx.InitialDelayInKB = (pv->param.rc.vbv_buffer_init / 8); + } + else + { + pv->param.videoParam->mfx.InitialDelayInKB = (pv->param.rc.vbv_buffer_size * + pv->param.rc.vbv_buffer_init / 8); + } + pv->param.videoParam->mfx.BufferSizeInKB = (pv->param.rc.vbv_buffer_size / 8); + } + } + else + { + // introduced in API 1.3 + // Media SDK will set Accuracy and Convergence for us automatically + pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_AVBR; + pv->param.videoParam->mfx.TargetKbps = job->vbitrate; + } + } + else + { + hb_error("encqsvInit: invalid rate control (%d, %d)", + job->vquality, job->vbitrate); + return -1; + } + + // set the keyframe interval + if (pv->param.gop.gop_pic_size < 0) + { + int rate = (int)((double)job->vrate / (double)job->vrate_base + 0.5); + if (pv->param.videoParam->mfx.RateControlMethod == MFX_RATECONTROL_CQP) + { + // ensure B-pyramid is enabled for CQP on Haswell + pv->param.gop.gop_pic_size = 32; + } + else + { + // set the keyframe interval based on the framerate + pv->param.gop.gop_pic_size = 5 * rate + 1; + } + } + pv->param.videoParam->mfx.GopPicSize = pv->param.gop.gop_pic_size; + + // sanitize some settings that affect memory consumption + if (!pv->is_sys_mem) + { + // limit these to avoid running out of resources (causes hang) + pv->param.videoParam->mfx.GopRefDist = FFMIN(pv->param.videoParam->mfx.GopRefDist, + pv->param.rc.lookahead ? 8 : 16); + pv->param.codingOption2.LookAheadDepth = FFMIN(pv->param.codingOption2.LookAheadDepth, + pv->param.rc.lookahead ? 48 - pv->param.videoParam->mfx.GopRefDist : 0); + } + else + { + // encode-only is a bit less sensitive to memory issues + pv->param.videoParam->mfx.GopRefDist = FFMIN(pv->param.videoParam->mfx.GopRefDist, 16); + pv->param.codingOption2.LookAheadDepth = FFMIN(pv->param.codingOption2.LookAheadDepth, + pv->param.rc.lookahead ? 60 : 0); + } + + /* + * 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 + */ + mfxIMPL impl; + mfxStatus err; + mfxVersion version; + mfxVideoParam videoParam; + mfxExtBuffer* ExtParamArray[3]; + mfxSession session = (mfxSession)0; + mfxExtCodingOption option1_buf, *option1 = &option1_buf; + mfxExtCodingOption2 option2_buf, *option2 = &option2_buf; + mfxExtCodingOptionSPSPPS sps_pps_buf, *sps_pps = &sps_pps_buf; + impl = MFX_IMPL_AUTO_ANY|MFX_IMPL_VIA_ANY; + version.Major = HB_QSV_MINVERSION_MAJOR; + version.Minor = HB_QSV_MINVERSION_MINOR; + err = MFXInit(impl, &version, &session); + if (err != MFX_ERR_NONE) + { + hb_error("encqsvInit: MFXInit failed (%d)", err); + return -1; + } + err = MFXVideoENCODE_Init(session, pv->param.videoParam); + if (err < MFX_ERR_NONE) // ignore warnings + { + hb_error("encqsvInit: MFXVideoENCODE_Init failed (%d)", err); + MFXClose(session); + return -1; + } + memset(&videoParam, 0, sizeof(mfxVideoParam)); + videoParam.ExtParam = ExtParamArray; + videoParam.NumExtParam = 0; + // introduced in API 1.3 + memset(sps_pps, 0, sizeof(mfxExtCodingOptionSPSPPS)); + sps_pps->Header.BufferId = MFX_EXTBUFF_CODING_OPTION_SPSPPS; + sps_pps->Header.BufferSz = sizeof(mfxExtCodingOptionSPSPPS); + sps_pps->SPSId = 0; + sps_pps->SPSBuffer = w->config->h264.sps; + sps_pps->SPSBufSize = sizeof(w->config->h264.sps); + sps_pps->PPSId = 0; + sps_pps->PPSBuffer = w->config->h264.pps; + sps_pps->PPSBufSize = sizeof(w->config->h264.pps); + videoParam.ExtParam[videoParam.NumExtParam++] = (mfxExtBuffer*)sps_pps; + // introduced in API 1.0 + memset(option1, 0, sizeof(mfxExtCodingOption)); + option1->Header.BufferId = MFX_EXTBUFF_CODING_OPTION; + option1->Header.BufferSz = sizeof(mfxExtCodingOption); + videoParam.ExtParam[videoParam.NumExtParam++] = (mfxExtBuffer*)option1; + // introduced in API 1.6 + memset(option2, 0, sizeof(mfxExtCodingOption2)); + option2->Header.BufferId = MFX_EXTBUFF_CODING_OPTION2; + option2->Header.BufferSz = sizeof(mfxExtCodingOption2); + if (hb_qsv_info->capabilities & HB_QSV_CAP_MSDK_API_1_6) + { + // attach to get the final output mfxExtCodingOption2 settings + videoParam.ExtParam[videoParam.NumExtParam++] = (mfxExtBuffer*)option2; + } + err = MFXVideoENCODE_GetVideoParam(session, &videoParam); + if (err == MFX_ERR_NONE) + { + // remove 32-bit NAL prefix (0x00 0x00 0x00 0x01) + w->config->h264.sps_length = sps_pps->SPSBufSize - 4; + memmove(w->config->h264.sps, w->config->h264.sps + 4, + w->config->h264.sps_length); + w->config->h264.pps_length = sps_pps->PPSBufSize - 4; + memmove(w->config->h264.pps, w->config->h264.pps + 4, + w->config->h264.pps_length); + } + else + { + hb_error("encqsvInit: MFXVideoENCODE_GetVideoParam failed (%d)", err); + MFXVideoENCODE_Close(session); + MFXClose (session); + return -1; + } + + // log implementation details before closing this session + if (pv->is_sys_mem) + { + hb_log("encqsvInit: using encode-only path"); + } + if ((MFXQueryIMPL (session, &impl) == MFX_ERR_NONE) && + (MFXQueryVersion(session, &version) == MFX_ERR_NONE)) + { + hb_log("encqsvInit: using %s implementation (%"PRIu16".%"PRIu16")", + impl == MFX_IMPL_SOFTWARE ? "software" : "hardware", + version.Major, version.Minor); + } + MFXVideoENCODE_Close(session); + MFXClose (session); + + // log main output settings + hb_log("encqsvInit: TargetUsage %"PRIu16" AsyncDepth %"PRIu16"", + videoParam.mfx.TargetUsage, videoParam.AsyncDepth); + hb_log("encqsvInit: GopRefDist %"PRIu16" GopPicSize %"PRIu16" NumRefFrame %"PRIu16"", + videoParam.mfx.GopRefDist, videoParam.mfx.GopPicSize, videoParam.mfx.NumRefFrame); + if (videoParam.mfx.RateControlMethod == MFX_RATECONTROL_CQP) + { + char qpi[7], qpp[9], qpb[9]; + snprintf(qpi, sizeof(qpi), "QPI %"PRIu16"", videoParam.mfx.QPI); + snprintf(qpp, sizeof(qpp), " QPP %"PRIu16"", videoParam.mfx.QPP); + snprintf(qpb, sizeof(qpb), " QPB %"PRIu16"", videoParam.mfx.QPB); + hb_log("encqsvInit: RateControlMethod CQP with %s%s%s", qpi, + videoParam.mfx.GopPicSize > 1 ? qpp : "", + videoParam.mfx.GopRefDist > 1 ? qpb : ""); + } + else + { + switch (videoParam.mfx.RateControlMethod) + { + case MFX_RATECONTROL_AVBR: + hb_log("encqsvInit: RateControlMethod AVBR TargetKbps %"PRIu16"", + videoParam.mfx.TargetKbps); + break; + case MFX_RATECONTROL_LA: + hb_log("encqsvInit: RateControlMethod LA TargetKbps %"PRIu16" LookAheadDepth %"PRIu16"", + videoParam.mfx.TargetKbps, option2->LookAheadDepth); + break; + case MFX_RATECONTROL_CBR: + case MFX_RATECONTROL_VBR: + hb_log("encqsvInit: RateControlMethod %s TargetKbps %"PRIu16" MaxKbps %"PRIu16" BufferSizeInKB %"PRIu16" InitialDelayInKB %"PRIu16"", + videoParam.mfx.RateControlMethod == MFX_RATECONTROL_CBR ? "CBR" : "VBR", + videoParam.mfx.TargetKbps, videoParam.mfx.MaxKbps, + videoParam.mfx.BufferSizeInKB, videoParam.mfx.InitialDelayInKB); + break; + default: + hb_log("encqsvInit: invalid rate control method %"PRIu16"", + videoParam.mfx.RateControlMethod); + return -1; + } + } + switch (videoParam.mfx.FrameInfo.PicStruct) + { + case MFX_PICSTRUCT_PROGRESSIVE: + hb_log("encqsvInit: PicStruct progressive"); + break; + case MFX_PICSTRUCT_FIELD_TFF: + hb_log("encqsvInit: PicStruct top field first"); + break; + case MFX_PICSTRUCT_FIELD_BFF: + hb_log("encqsvInit: PicStruct bottom field first"); + break; + default: + hb_error("encqsvInit: invalid PicStruct value 0x%"PRIx16"", + videoParam.mfx.FrameInfo.PicStruct); + return -1; + } + const char *cavlc, *rdopt; + switch (option1->CAVLC) + { + case MFX_CODINGOPTION_ON: + cavlc = "on"; + break; + case MFX_CODINGOPTION_OFF: + cavlc = "off"; + break; + default: + hb_error("encqsvInit: invalid CAVLC value %"PRIu16"", + option1->CAVLC); + return -1; + } + switch (option1->RateDistortionOpt) + { + case MFX_CODINGOPTION_ON: + rdopt = "on"; + break; + case MFX_CODINGOPTION_OFF: + rdopt = "off"; + break; + default: + hb_error("encqsvInit: invalid RateDistortionOpt value %"PRIu16"", + option1->RateDistortionOpt); + return -1; + } + hb_log("encqsvInit: CAVLC %s RateDistortionOpt %s", cavlc, rdopt); + if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_BRC) + { + const char *mbbrc, *extbrc; + switch (option2->MBBRC) + { + case MFX_CODINGOPTION_ON: + mbbrc = "on"; + break; + case MFX_CODINGOPTION_OFF: + mbbrc = "off"; + break; + case MFX_CODINGOPTION_ADAPTIVE: + mbbrc = "adaptive"; + break; + case MFX_CODINGOPTION_UNKNOWN: + mbbrc = "unknown (auto)"; + break; + default: + hb_error("encqsvInit: invalid MBBRC value %"PRIu16"", + option2->MBBRC); + return -1; + } + switch (option2->ExtBRC) + { + case MFX_CODINGOPTION_ON: + extbrc = "on"; + break; + case MFX_CODINGOPTION_OFF: + extbrc = "off"; + break; + case MFX_CODINGOPTION_ADAPTIVE: + extbrc = "adaptive"; + break; + case MFX_CODINGOPTION_UNKNOWN: + extbrc = "unknown (auto)"; + break; + default: + hb_error("encqsvInit: invalid ExtBRC value %"PRIu16"", + option2->ExtBRC); + return -1; + } + hb_log("encqsvInit: MBBRC %s ExtBRC %s", mbbrc, extbrc); + } + if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_TRELLIS) + { + switch (option2->Trellis) + { + case MFX_TRELLIS_OFF: + hb_log("encqsvInit: Trellis off"); + break; + case MFX_TRELLIS_UNKNOWN: + hb_log("encqsvInit: Trellis unknown (auto)"); + break; + default: + hb_log("encqsvInit: Trellis on (%s%s%s)", + option2->Trellis & MFX_TRELLIS_I ? "I" : "", + option2->Trellis & MFX_TRELLIS_P ? "P" : "", + option2->Trellis & MFX_TRELLIS_B ? "B" : ""); + break; + } + } + hb_log("encqsvInit: H.264 profile %s @ level %s", + qsv_h264_profile_xlat(videoParam.mfx.CodecProfile), + qsv_h264_level_xlat (videoParam.mfx.CodecLevel)); + + // AsyncDepth has now been set and/or modified by Media SDK + pv->max_async_depth = videoParam.AsyncDepth; + pv->async_depth = 0; + + // check whether B-frames are used + switch (videoParam.mfx.CodecProfile) + { + case MFX_PROFILE_AVC_BASELINE: + case MFX_PROFILE_AVC_CONSTRAINED_HIGH: + case MFX_PROFILE_AVC_CONSTRAINED_BASELINE: + pv->bfrm_delay = 0; + break; + default: + pv->bfrm_delay = 1; + break; + } + // sanitize + pv->bfrm_delay = FFMIN(pv->bfrm_delay, videoParam.mfx.GopRefDist - 1); + pv->bfrm_delay = FFMIN(pv->bfrm_delay, videoParam.mfx.GopPicSize - 2); + pv->bfrm_delay = FFMAX(pv->bfrm_delay, 0); + // let the muxer know whether to expect B-frames or not + job->areBframes = !!pv->bfrm_delay; + // check whether we need to generate DTS ourselves (MSDK API < 1.6 or VFR) + pv->bfrm_workaround = job->cfr != 1 || !(hb_qsv_info->capabilities & + HB_QSV_CAP_MSDK_API_1_6); + if (pv->bfrm_delay && pv->bfrm_workaround) + { + pv->bfrm_workaround = 1; + pv->list_dts = hb_list_init(); + } + else + { + pv->bfrm_workaround = 0; + pv->list_dts = NULL; + } + + return 0; +} + +void encqsvClose( hb_work_object_t * w ) +{ + int i = 0; + hb_work_private_t * pv = w->private_data; + + hb_log( "enc_qsv done: frames: %u in, %u out", pv->frames_in, pv->frames_out ); + + // if system memory ( encode only ) additional free(s) for surfaces + if( pv && pv->job && pv->job->qsv && + pv->job->qsv->is_context_active ){ + + av_qsv_context *qsv = pv->job->qsv; + + if(qsv && qsv->enc_space){ + av_qsv_space* qsv_encode = qsv->enc_space; + if(qsv_encode->is_init_done){ + if(pv->is_sys_mem){ + if( qsv_encode && qsv_encode->surface_num > 0) + for (i = 0; i < qsv_encode->surface_num; i++){ + if( qsv_encode->p_surfaces[i]->Data.Y){ + free(qsv_encode->p_surfaces[i]->Data.Y); + qsv_encode->p_surfaces[i]->Data.Y = 0; + } + if( qsv_encode->p_surfaces[i]->Data.VU){ + free(qsv_encode->p_surfaces[i]->Data.VU); + qsv_encode->p_surfaces[i]->Data.VU = 0; + } + if(qsv_encode->p_surfaces[i]) + av_freep(qsv_encode->p_surfaces[i]); + } + qsv_encode->surface_num = 0; + + sws_freeContext(pv->sws_context_to_nv12); + } + + for (i = av_qsv_list_count(qsv_encode->tasks); i > 1; i--){ + av_qsv_task* task = av_qsv_list_item(qsv_encode->tasks,i-1); + if(task && task->bs){ + av_freep(&task->bs->Data); + av_freep(&task->bs); + av_qsv_list_rem(qsv_encode->tasks,task); + } + } + av_qsv_list_close(&qsv_encode->tasks); + + for (i = 0; i < qsv_encode->surface_num; i++){ + av_freep(&qsv_encode->p_surfaces[i]); + } + qsv_encode->surface_num = 0; + + for (i = 0; i < qsv_encode->sync_num; i++){ + av_freep(&qsv_encode->p_syncp[i]->p_sync); + av_freep(&qsv_encode->p_syncp[i]); + } + qsv_encode->sync_num = 0; + + qsv_encode->is_init_done = 0; + } + } + + if(qsv){ + // closing the commong stuff + av_qsv_context_clean(qsv); + + if(pv->is_sys_mem){ + av_freep(&qsv); + } + } + } + + if (pv != NULL) + { + if (pv->list_dts != NULL) + { + while (hb_list_count(pv->list_dts) > 0) + { + int64_t *item = hb_list_item(pv->list_dts, 0); + hb_list_rem(pv->list_dts, item); + free(item); + } + hb_list_close(&pv->list_dts); + } + } + + free( pv ); + w->private_data = NULL; +} + +int encqsvWork( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_work_private_t * pv = w->private_data; + hb_job_t * job = pv->job; + hb_buffer_t * in = *buf_in, *buf; + av_qsv_context *qsv = job->qsv; + av_qsv_space* qsv_encode; + hb_buffer_t *last_buf = NULL; + mfxStatus sts = MFX_ERR_NONE; + int is_end = 0; + av_qsv_list* received_item = 0; + av_qsv_stage* stage = 0; + + while(1){ + int ret = qsv_enc_init(qsv, pv); + qsv = job->qsv; + qsv_encode = qsv->enc_space; + if(ret >= 2) + av_qsv_sleep(1); + else + break; + } + *buf_out = NULL; + + if( in->size <= 0 ) + { + // do delayed frames yet + *buf_in = NULL; + is_end = 1; + } + + // input from decode, as called - we always have some to proceed with + while (1) + { + { + mfxEncodeCtrl *work_control = NULL; + mfxFrameSurface1 *work_surface = NULL; + + if (!is_end) + { + if (pv->is_sys_mem) + { + int surface_idx = av_qsv_get_free_surface(qsv_encode, qsv, + &qsv_encode->request[0].Info, QSV_PART_ANY); + work_surface = qsv_encode->p_surfaces[surface_idx]; + + if (work_surface->Data.Y == NULL) + { + // if nv12 and 422 12bits per pixel + work_surface->Data.Pitch = pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.Width; + work_surface->Data.Y = calloc(1, + pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.Width * + pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.Height); + work_surface->Data.VU = calloc(1, + pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.Width * + pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.Height / 2); + } + qsv_yuv420_to_nv12(pv->sws_context_to_nv12, work_surface, in); + } + else + { + received_item = in->qsv_details.qsv_atom; + stage = av_qsv_get_last_stage(received_item); + work_surface = stage->out.p_surface; + + // don't let qsv->dts_seq grow needlessly + av_qsv_dts_pop(qsv); + } + + work_surface->Data.TimeStamp = in->s.start; + + /* + * Debugging code to check that the upstream modules have generated + * a continuous, self-consistent frame stream. + */ + int64_t start = work_surface->Data.TimeStamp; + if (pv->last_start > start) + { + hb_log("encqsvWork: input continuity error, last start %"PRId64" start %"PRId64"", + pv->last_start, start); + } + pv->last_start = start; + + // for DTS generation (when MSDK API < 1.6 or VFR) + if (pv->bfrm_delay && pv->bfrm_workaround) + { + if (pv->frames_in <= BFRM_DELAY_MAX) + { + pv->init_pts[pv->frames_in] = work_surface->Data.TimeStamp; + } + if (pv->frames_in) + { + hb_qsv_add_new_dts(pv->list_dts, + work_surface->Data.TimeStamp); + } + } + + /* + * Chapters have to start with a keyframe so request that this + * frame be coded as IDR. Since there may be several frames + * buffered in the encoder, remember the timestamp so when this + * frame finally pops out of the encoder we'll mark its buffer + * as the start of a chapter. + */ + if (in->s.new_chap > 0 && job->chapter_markers) + { + if (!pv->next_chapter.index) + { + pv->next_chapter.start = work_surface->Data.TimeStamp; + pv->next_chapter.index = in->s.new_chap; + work_control = &pv->force_keyframe; + } + else + { + // however unlikely, this can happen in theory + hb_log("encqsvWork: got chapter %d before we could write chapter %d, dropping marker", + in->s.new_chap, pv->next_chapter.index); + } + // don't let 'work_loop' put a chapter mark on the wrong buffer + in->s.new_chap = 0; + } + + /* + * If interlaced encoding is requested during encoder initialization, + * but the input mfxFrameSurface1 is flagged as progressive here, + * the output bitstream will be progressive (according to MediaInfo). + * + * Assume the user knows what he's doing (say he is e.g. encoding a + * progressive-flagged source using interlaced compression - he may + * well have a good reason to do so; mis-flagged sources do exist). + */ + work_surface->Info.PicStruct = pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.PicStruct; + } + else{ + work_surface = NULL; + received_item = NULL; + } + int sync_idx = av_qsv_get_free_sync( qsv_encode, qsv ); + if (sync_idx == -1) + { + hb_error("qsv: Not enough resources allocated for QSV encode"); + return 0; + } + av_qsv_task *task = av_qsv_list_item( qsv_encode->tasks, pv->async_depth ); + + for (;;) + { + // Encode a frame asychronously (returns immediately) + sts = MFXVideoENCODE_EncodeFrameAsync(qsv->mfx_session, + work_control, work_surface, task->bs, + qsv_encode->p_syncp[sync_idx]->p_sync); + + if (MFX_ERR_MORE_DATA == sts || (MFX_ERR_NONE <= sts && MFX_WRN_DEVICE_BUSY != sts)) + if (work_surface && !pv->is_sys_mem) + ff_qsv_atomic_dec(&work_surface->Data.Locked); + + if( MFX_ERR_MORE_DATA == sts ){ + ff_qsv_atomic_dec(&qsv_encode->p_syncp[sync_idx]->in_use); + if(work_surface && received_item) + hb_list_add(pv->delayed_processing, received_item); + break; + } + + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + if (MFX_ERR_NONE <= sts /*&& !syncpE*/) // repeat the call if warning and no output + { + if (MFX_WRN_DEVICE_BUSY == sts){ + av_qsv_sleep(10); // wait if device is busy + continue; + } + + av_qsv_stage* new_stage = av_qsv_stage_init(); + new_stage->type = AV_QSV_ENCODE; + new_stage->in.p_surface = work_surface; + new_stage->out.sync = qsv_encode->p_syncp[sync_idx]; + + new_stage->out.p_bs = task->bs;//qsv_encode->bs; + task->stage = new_stage; + + pv->async_depth++; + + if(received_item){ + av_qsv_add_stagee( &received_item, new_stage,HAVE_THREADS ); + } + else{ + // flushing the end + int pipe_idx = av_qsv_list_add( qsv->pipes, av_qsv_list_init(HAVE_THREADS) ); + av_qsv_list* list_item = av_qsv_list_item( qsv->pipes, pipe_idx ); + av_qsv_add_stagee( &list_item, new_stage,HAVE_THREADS ); + } + + int i = 0; + for(i=hb_list_count(pv->delayed_processing); i > 0;i--){ + hb_list_t *item = hb_list_item(pv->delayed_processing,i-1); + if(item){ + hb_list_rem(pv->delayed_processing,item); + av_qsv_flush_stages(qsv->pipes, &item); + } + } + + break; + } + + ff_qsv_atomic_dec(&qsv_encode->p_syncp[sync_idx]->in_use); + + if (MFX_ERR_NOT_ENOUGH_BUFFER == sts) + DEBUG_ASSERT( 1,"The bitstream buffer size is insufficient." ); + + break; + } + } + + buf = NULL; + + do{ + + if(pv->async_depth==0) break; + + // working properly with sync depth approach of MediaSDK OR flushing, if at the end + if( (pv->async_depth >= pv->max_async_depth) || is_end ){ + + pv->async_depth--; + + av_qsv_task *task = av_qsv_list_item( qsv_encode->tasks, 0 ); + av_qsv_stage* stage = task->stage; + av_qsv_list* this_pipe = av_qsv_pipe_by_stage(qsv->pipes,stage); + sts = MFX_ERR_NONE; + + // only here we need to wait on operation been completed, therefore SyncOperation is used, + // after this step - we continue to work with bitstream, muxing ... + av_qsv_wait_on_sync( qsv,stage ); + + if(task->bs->DataLength>0){ + av_qsv_flush_stages( qsv->pipes, &this_pipe ); + + // see nal_encode + buf = hb_video_buffer_init( job->width, job->height ); + buf->size = 0; + buf->s.frametype = 0; + + // maping of FrameType(s) + if(task->bs->FrameType & MFX_FRAMETYPE_IDR ) buf->s.frametype = HB_FRAME_IDR; + else + if(task->bs->FrameType & MFX_FRAMETYPE_I ) buf->s.frametype = HB_FRAME_I; + else + if(task->bs->FrameType & MFX_FRAMETYPE_P ) buf->s.frametype = HB_FRAME_P; + else + if(task->bs->FrameType & MFX_FRAMETYPE_B ) buf->s.frametype = HB_FRAME_B; + + if(task->bs->FrameType & MFX_FRAMETYPE_REF ) buf->s.flags = HB_FRAME_REF; + + parse_nalus(task->bs->Data + task->bs->DataOffset,task->bs->DataLength, buf, pv->frames_out); + + if ( last_buf == NULL ) + *buf_out = buf; + else + last_buf->next = buf; + last_buf = buf; + + // simple for now but check on TimeStampCalc from MSDK + int64_t duration = ((double)pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD / + (double)pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN) * 90000.; + + // start -> PTS + // renderOffset -> DTS + buf->s.start = buf->s.renderOffset = task->bs->TimeStamp; + buf->s.stop = buf->s.start + duration; + buf->s.duration = duration; + if (pv->bfrm_delay) + { + if (!pv->bfrm_workaround) + { + buf->s.renderOffset = task->bs->DecodeTimeStamp; + } + else + { + // MSDK API < 1.6 or VFR, so generate our own DTS + 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)) + { + // with B-pyramid, the delay may be more than 1 frame, + // so compute the actual delay based on the initial DTS + // provided by MSDK; also, account for rounding errors + // (e.g. 24000/1001 fps @ 90kHz -> 3753.75 ticks/frame) + pv->bfrm_delay = ((task->bs->TimeStamp - + task->bs->DecodeTimeStamp + + (duration / 2)) / duration); + pv->bfrm_delay = FFMAX(pv->bfrm_delay, 1); + pv->bfrm_delay = FFMIN(pv->bfrm_delay, BFRM_DELAY_MAX); + } + /* + * Generate VFR-compatible output DTS based on input PTS. + * + * Depends on the B-frame delay: + * + * 0: ipts0, ipts1, ipts2... + * 1: ipts0 - ipts1, ipts1 - ipts1, ipts1, ipts2... + * 2: ipts0 - ipts2, ipts1 - ipts2, ipts2 - ipts2, ipts1... + * ...and so on. + */ + if (pv->frames_out <= pv->bfrm_delay) + { + buf->s.renderOffset = (pv->init_pts[pv->frames_out] - + pv->init_pts[pv->bfrm_delay]); + } + else + { + buf->s.renderOffset = hb_qsv_pop_next_dts(pv->list_dts); + } + } + + /* + * In the MP4 container, DT(0) = STTS(0) = 0. + * + * Which gives us: + * CT(0) = CTTS(0) + STTS(0) = CTTS(0) = PTS(0) - DTS(0) + * When DTS(0) < PTS(0), we then have: + * CT(0) > 0 for video, but not audio (breaks A/V sync). + * + * This is typically solved by writing an edit list shifting + * video samples by the initial delay, PTS(0) - DTS(0). + * + * See: + * ISO/IEC 14496-12:2008(E), ISO base media file format + * - 8.6.1.2 Decoding Time to Sample Box + */ + if (w->config->h264.init_delay == 0 && buf->s.renderOffset < 0) + { + w->config->h264.init_delay = -buf->s.renderOffset; + } + } + + /* + * If we have a chapter marker pending and this frame's + * presentation time stamp is at or after the marker's time stamp, + * use this as the chapter start. + */ + if (pv->next_chapter.index && buf->s.frametype == HB_FRAME_IDR && + pv->next_chapter.start <= buf->s.start) + { + buf->s.new_chap = pv->next_chapter.index; + pv->next_chapter.index = 0; + } + + // shift for fifo + if(pv->async_depth){ + av_qsv_list_rem(qsv_encode->tasks,task); + av_qsv_list_add(qsv_encode->tasks,task); + } + + task->bs->DataLength = 0; + task->bs->DataOffset = 0; + task->bs->MaxLength = qsv_encode->p_buf_max_size; + task->stage = 0; + pv->frames_out++; + } + } + }while(is_end); + + + if(is_end){ + if( !buf && MFX_ERR_MORE_DATA == sts ) + break; + + } + else + break; + + } + + if(!is_end) + ++pv->frames_in; + + if(is_end){ + *buf_in = NULL; + if(last_buf){ + last_buf->next = in; + } + else + *buf_out = in; + return HB_WORK_DONE; + } + else{ + return HB_WORK_OK; + } +} + +int nal_find_start_code(uint8_t** pb, size_t* size){ + if ((int) *size < 4 ) + return 0; + + // find start code by MSDK , see ff_prefix_code[] + while ((4 <= *size) && + ((0 != (*pb)[0]) || + (0 != (*pb)[1]) || + (1 != (*pb)[2]) )) + { + *pb += 1; + *size -= 1; + } + + if (4 <= *size) + return (((*pb)[0] << 24) | ((*pb)[1] << 16) | ((*pb)[2] << 8) | ((*pb)[3])); + + return 0; +} + +void parse_nalus(uint8_t *nal_inits, size_t length, hb_buffer_t *buf, uint32_t frame_num){ + uint8_t *offset = nal_inits; + size_t size = length; + + if( nal_find_start_code(&offset,&size) == 0 ) + size = 0; + + while( size > 0 ){ + + uint8_t* current_nal = offset + sizeof(ff_prefix_code)-1; + uint8_t *next_offset = offset + sizeof(ff_prefix_code); + size_t next_size = size - sizeof(ff_prefix_code); + size_t current_size = next_size; + if( nal_find_start_code(&next_offset,&next_size) == 0 ){ + size = 0; + current_size += 1; + } + else{ + current_size -= next_size; + if( next_offset > 0 && *(next_offset-1) != 0 ) + current_size += 1; + } + { + char size_position[4] = {0,0,0,0}; + size_position[1] = (current_size >> 24) & 0xFF; + size_position[1] = (current_size >> 16) & 0xFF; + size_position[2] = (current_size >> 8) & 0xFF; + size_position[3] = current_size & 0xFF; + + memcpy(buf->data + buf->size,&size_position ,sizeof(size_position)); + buf->size += sizeof(size_position); + + memcpy(buf->data + buf->size,current_nal ,current_size); + buf->size += current_size; + } + + if(size){ + size = next_size; + offset = next_offset; + } + } +} diff --git a/libhb/enc_qsv.h b/libhb/enc_qsv.h new file mode 100644 index 000000000..9d27347cd --- /dev/null +++ b/libhb/enc_qsv.h @@ -0,0 +1,38 @@ +/* ********************************************************************* *\ + +Copyright (C) 2013 Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +- Neither the name of Intel Corporation nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +\* ********************************************************************* */ + +#ifndef ENC_QSV_H +#define ENC_QSV_H + +#include "hb.h" +#include "qsv_common.h" + +int nal_find_start_code(uint8_t** pb, size_t* size); +void parse_nalus( uint8_t *nal_inits, size_t length, hb_buffer_t *buf, uint32_t frame_num); + +#endif // ENC_QSV_H diff --git a/libhb/h264_common.h b/libhb/h264_common.h new file mode 100644 index 000000000..febe1965f --- /dev/null +++ b/libhb/h264_common.h @@ -0,0 +1,17 @@ +/* h264_common.h + + Copyright (c) 2003-2012 HandBrake Team + This file is part of the HandBrake source code + Homepage: <http://handbrake.fr/>. + It may be used under the terms of the GNU General Public License v2. + For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html + */ + +#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, }; + +#endif //HB_H264_COMMON_H diff --git a/libhb/qsv_common.c b/libhb/qsv_common.c new file mode 100644 index 000000000..7cc0bb70c --- /dev/null +++ b/libhb/qsv_common.c @@ -0,0 +1,780 @@ +/* qsv_common.c + * + * Copyright (c) 2003-2013 HandBrake Team + * This file is part of the HandBrake source code. + * Homepage: <http://handbrake.fr/>. + * It may be used under the terms of the GNU General Public License v2. + * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html + */ + +#include "hb.h" +#include "ports.h" +#include "common.h" +#include "hb_dict.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 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; + +// 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) + +int hb_qsv_available() +{ + return hb_qsv_info != NULL && (qsv_hardware_available || + qsv_software_available); +} + +int hb_qsv_info_init() +{ + static int init_done = 0; + if (init_done) + return (hb_qsv_info == NULL); + init_done = 1; + + hb_qsv_info = calloc(1, sizeof(*hb_qsv_info)); + if (hb_qsv_info == NULL) + { + hb_error("hb_qsv_info_init: alloc failure"); + return -1; + } + + mfxSession session; + qsv_minimum_version.Major = HB_QSV_MINVERSION_MAJOR; + qsv_minimum_version.Minor = HB_QSV_MINVERSION_MINOR; + + // check for software fallback + if (MFXInit(MFX_IMPL_SOFTWARE, + &qsv_minimum_version, &session) == MFX_ERR_NONE) + { + qsv_software_available = 1; + // our minimum is supported, but query the actual version + MFXQueryVersion(session, &qsv_software_version); + MFXClose(session); + } + + // check for actual hardware support + if (MFXInit(MFX_IMPL_HARDWARE_ANY|MFX_IMPL_VIA_ANY, + &qsv_minimum_version, &session) == MFX_ERR_NONE) + { + qsv_hardware_available = 1; + // our minimum is supported, but query the actual version + MFXQueryVersion(session, &qsv_hardware_version); + MFXClose(session); + } + + // check for version-specific or hardware-specific capabilities + // we only use software as a fallback, so check hardware first + if (qsv_hardware_available) + { + if (HB_CHECK_MFX_VERSION(qsv_hardware_version, 1, 6)) + { + hb_qsv_info->capabilities |= HB_QSV_CAP_OPTION2_BRC; + hb_qsv_info->capabilities |= HB_QSV_CAP_MSDK_API_1_6; + } + if (hb_get_cpu_platform() == HB_CPU_PLATFORM_INTEL_HSW) + { + if (HB_CHECK_MFX_VERSION(qsv_hardware_version, 1, 7)) + { + hb_qsv_info->capabilities |= HB_QSV_CAP_OPTION2_TRELLIS; + hb_qsv_info->capabilities |= HB_QSV_CAP_OPTION2_LOOKAHEAD; + } + hb_qsv_info->capabilities |= HB_QSV_CAP_H264_BPYRAMID; + } + } + else if (qsv_software_available) + { + if (HB_CHECK_MFX_VERSION(qsv_software_version, 1, 6)) + { + hb_qsv_info->capabilities |= HB_QSV_CAP_MSDK_API_1_6; + hb_qsv_info->capabilities |= HB_QSV_CAP_H264_BPYRAMID; + } + } + + // 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); + } + + // success + return 0; +} + +// we don't need it beyond this point +#undef HB_CHECK_MFX_VERSION + +void hb_qsv_info_print() +{ + if (hb_qsv_info == NULL) + return; + + // is QSV available? + hb_log("Intel Quick Sync Video support: %s", + hb_qsv_available() ? "yes": "no"); + + // if we have Quick Sync Video support, also print the details + if (hb_qsv_available()) + { + if (qsv_hardware_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); + } + if (qsv_software_available) + { + 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); + } + } +} + +const char* hb_qsv_decode_get_codec_name(enum AVCodecID codec_id) +{ + switch (codec_id) + { + case AV_CODEC_ID_H264: + return "h264_qsv"; + + default: + return NULL; + } +} + +int hb_qsv_decode_is_enabled(hb_job_t *job) +{ + return ((job != NULL && job->title->qsv_decode_support && job->qsv_decode) && + (job->vcodec & HB_VCODEC_QSV_MASK)); +} + +int hb_qsv_decode_is_supported(enum AVCodecID codec_id, + enum AVPixelFormat pix_fmt) +{ + switch (codec_id) + { + case AV_CODEC_ID_H264: + return (pix_fmt == AV_PIX_FMT_YUV420P || + pix_fmt == AV_PIX_FMT_YUVJ420P); + + default: + return 0; + } +} + +int hb_qsv_codingoption_xlat(int val) +{ + switch (HB_QSV_CLIP3(-1, 2, val)) + { + case 0: + return MFX_CODINGOPTION_OFF; + case 1: + case 2: // MFX_CODINGOPTION_ADAPTIVE, reserved + return MFX_CODINGOPTION_ON; + case -1: + default: + return MFX_CODINGOPTION_UNKNOWN; + } +} + +int hb_qsv_trellisvalue_xlat(int val) +{ + switch (HB_QSV_CLIP3(-1, 3, val)) + { + case 0: + return MFX_TRELLIS_OFF; + case 1: // I-frames only + return MFX_TRELLIS_I; + case 2: // I- and P-frames + return MFX_TRELLIS_I|MFX_TRELLIS_P; + case 3: // all frames + return MFX_TRELLIS_I|MFX_TRELLIS_P|MFX_TRELLIS_B; + case -1: + default: + return MFX_TRELLIS_UNKNOWN; + } +} + +int hb_qsv_atoindex(const char* const *arr, const char *str, int *err) +{ + int i; + for (i = 0; arr[i] != NULL; i++) + { + if (!strcasecmp(arr[i], str)) + { + break; + } + } + *err = (arr[i] == NULL); + return i; +} + +// adapted from libx264 +int hb_qsv_atobool(const char *str, int *err) +{ + if (!strcasecmp(str, "1") || + !strcasecmp(str, "yes") || + !strcasecmp(str, "true")) + { + return 1; + } + if (!strcasecmp(str, "0") || + !strcasecmp(str, "no") || + !strcasecmp(str, "false")) + { + return 0; + } + *err = 1; + return 0; +} + +// adapted from libx264 +int hb_qsv_atoi(const char *str, int *err) +{ + char *end; + int v = strtol(str, &end, 0); + if (end == str || end[0] != '\0') + { + *err = 1; + } + return v; +} + +// adapted from libx264 +float hb_qsv_atof(const char *str, int *err) +{ + char *end; + float v = strtod(str, &end); + if (end == str || end[0] != '\0') + { + *err = 1; + } + return v; +} + +int hb_qsv_param_parse(hb_qsv_param_t *param, + const char *key, const char *value, int vcodec) +{ + float fvalue; + int ivalue, error = 0; + if (param == NULL) + { + return HB_QSV_PARAM_ERROR; + } + if (value == NULL || value[0] == '\0') + { + value = "true"; + } + else if (value[0] == '=') + { + value++; + } + if (key == NULL || key[0] == '\0') + { + return HB_QSV_PARAM_BAD_NAME; + } + else if (!strncasecmp(key, "no-", 3)) + { + key += 3; + value = hb_qsv_atobool(value, &error) ? "false" : "true"; + if (error) + { + return HB_QSV_PARAM_BAD_VALUE; + } + } + if (!strcasecmp(key, "target-usage") || + !strcasecmp(key, "tu")) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->videoParam->mfx.TargetUsage = HB_QSV_CLIP3(MFX_TARGETUSAGE_1, + MFX_TARGETUSAGE_7, + ivalue); + } + } + else if (!strcasecmp(key, "num-ref-frame") || + !strcasecmp(key, "ref")) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->videoParam->mfx.NumRefFrame = HB_QSV_CLIP3(0, 16, ivalue); + } + } + else if (!strcasecmp(key, "gop-ref-dist")) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->videoParam->mfx.GopRefDist = HB_QSV_CLIP3(0, 32, ivalue); + } + } + else if (!strcasecmp(key, "gop-pic-size") || + !strcasecmp(key, "keyint")) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->gop.gop_pic_size = HB_QSV_CLIP3(-1, UINT16_MAX, ivalue); + } + } + else if (!strcasecmp(key, "scenecut")) + { + ivalue = hb_qsv_atobool(value, &error); + if (!error) + { + if (!ivalue) + { + param->videoParam->mfx.GopOptFlag |= MFX_GOP_STRICT; + } + else + { + param->videoParam->mfx.GopOptFlag &= ~MFX_GOP_STRICT; + } + } + } + else if (!strcasecmp(key, "cqp-offset-i")) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->rc.cqp_offsets[0] = HB_QSV_CLIP3(INT16_MIN, INT16_MAX, ivalue); + } + } + else if (!strcasecmp(key, "cqp-offset-p")) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->rc.cqp_offsets[1] = HB_QSV_CLIP3(INT16_MIN, INT16_MAX, ivalue); + } + } + else if (!strcasecmp(key, "cqp-offset-b")) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->rc.cqp_offsets[2] = HB_QSV_CLIP3(INT16_MIN, INT16_MAX, ivalue); + } + } + else if (!strcasecmp(key, "vbv-init")) + { + fvalue = hb_qsv_atof(value, &error); + if (!error) + { + param->rc.vbv_buffer_init = HB_QSV_CLIP3(0, UINT16_MAX, fvalue); + } + } + else if (!strcasecmp(key, "vbv-bufsize")) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->rc.vbv_buffer_size = HB_QSV_CLIP3(0, UINT16_MAX, ivalue); + } + } + else if (!strcasecmp(key, "vbv-maxrate")) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->rc.vbv_max_bitrate = HB_QSV_CLIP3(0, UINT16_MAX, ivalue); + } + } + else if (!strcasecmp(key, "cabac")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = !hb_qsv_atobool(value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (!error) + { + param->codingOption.CAVLC = hb_qsv_codingoption_xlat(ivalue); + } + } + else if (!strcasecmp(key, "rate-distorsion-opt") || + !strcasecmp(key, "rdo")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = hb_qsv_atobool(value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (!error) + { + param->codingOption.RateDistortionOpt = hb_qsv_codingoption_xlat(ivalue); + } + } + else if (!strcasecmp(key, "videoformat")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = hb_qsv_atoindex(x264_vidformat_names, value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (!error) + { + param->videoSignalInfo.VideoFormat = ivalue; + } + } + else if (!strcasecmp(key, "fullrange")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = hb_qsv_atoindex(x264_fullrange_names, value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (!error) + { + param->videoSignalInfo.VideoFullRange = ivalue; + } + } + else if (!strcasecmp(key, "colorprim")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = hb_qsv_atoindex(x264_colorprim_names, value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (!error) + { + param->videoSignalInfo.ColourDescriptionPresent = 1; + param->videoSignalInfo.ColourPrimaries = ivalue; + } + } + else if (!strcasecmp(key, "transfer")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = hb_qsv_atoindex(x264_transfer_names, value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (!error) + { + param->videoSignalInfo.ColourDescriptionPresent = 1; + param->videoSignalInfo.TransferCharacteristics = ivalue; + } + } + else if (!strcasecmp(key, "colormatrix")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = hb_qsv_atoindex(x264_colmatrix_names, value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (!error) + { + param->videoSignalInfo.ColourDescriptionPresent = 1; + param->videoSignalInfo.MatrixCoefficients = ivalue; + } + } + else if (!strcasecmp(key, "tff") || + !strcasecmp(key, "interlaced")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = hb_qsv_atobool(value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (!error) + { + param->videoParam->mfx.FrameInfo.PicStruct = (ivalue ? + MFX_PICSTRUCT_FIELD_TFF : + MFX_PICSTRUCT_PROGRESSIVE); + } + } + else if (!strcasecmp(key, "bff")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = hb_qsv_atobool(value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (!error) + { + param->videoParam->mfx.FrameInfo.PicStruct = (ivalue ? + MFX_PICSTRUCT_FIELD_BFF : + MFX_PICSTRUCT_PROGRESSIVE); + } + } + else if (!strcasecmp(key, "mbbrc")) + { + if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_BRC) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->codingOption2.MBBRC = hb_qsv_codingoption_xlat(ivalue); + } + } + else + { + return HB_QSV_PARAM_UNSUPPORTED; + } + } + else if (!strcasecmp(key, "extbrc")) + { + if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_BRC) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->codingOption2.ExtBRC = hb_qsv_codingoption_xlat(ivalue); + } + } + else + { + return HB_QSV_PARAM_UNSUPPORTED; + } + } + else if (!strcasecmp(key, "lookahead") || + !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 (!error) + { + param->rc.lookahead = ivalue; + } + } + else + { + return HB_QSV_PARAM_UNSUPPORTED; + } + } + 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 (!error) + { + param->codingOption2.LookAheadDepth = HB_QSV_CLIP3(10, 100, + ivalue); + } + } + else + { + return HB_QSV_PARAM_UNSUPPORTED; + } + } + else if (!strcasecmp(key, "trellis")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = hb_qsv_atoi(value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_TRELLIS) + { + if (!error) + { + param->codingOption2.Trellis = hb_qsv_trellisvalue_xlat(ivalue); + } + } + else + { + return HB_QSV_PARAM_UNSUPPORTED; + } + } + else + { + /* + * TODO: + * - slice count control + * - open-gop + * - fake-interlaced (mfxExtCodingOption.FramePicture???) + * - intra-refresh + */ + return HB_QSV_PARAM_BAD_NAME; + } + return error ? HB_QSV_PARAM_BAD_VALUE : HB_QSV_PARAM_OK; +} + +int hb_qsv_param_default(hb_qsv_param_t *param, mfxVideoParam *videoParam) +{ + if (param != NULL && videoParam != NULL) + { + // introduced in API 1.0 + memset(¶m->codingOption, 0, sizeof(mfxExtCodingOption)); + param->codingOption.Header.BufferId = MFX_EXTBUFF_CODING_OPTION; + param->codingOption.Header.BufferSz = sizeof(mfxExtCodingOption); + param->codingOption.MECostType = 0; // reserved, must be 0 + param->codingOption.MESearchType = 0; // reserved, must be 0 + param->codingOption.MVSearchWindow.x = 0; // reserved, must be 0 + param->codingOption.MVSearchWindow.y = 0; // reserved, must be 0 + param->codingOption.RefPicListReordering = 0; // reserved, must be 0 + param->codingOption.IntraPredBlockSize = 0; // reserved, must be 0 + param->codingOption.InterPredBlockSize = 0; // reserved, must be 0 + param->codingOption.MVPrecision = 0; // reserved, must be 0 + param->codingOption.EndOfSequence = MFX_CODINGOPTION_UNKNOWN; + param->codingOption.RateDistortionOpt = MFX_CODINGOPTION_UNKNOWN; + param->codingOption.CAVLC = MFX_CODINGOPTION_UNKNOWN; + param->codingOption.ResetRefList = MFX_CODINGOPTION_UNKNOWN; + param->codingOption.MaxDecFrameBuffering = 0; // unspecified + param->codingOption.AUDelimiter = MFX_CODINGOPTION_OFF; + param->codingOption.SingleSeiNalUnit = MFX_CODINGOPTION_UNKNOWN; + param->codingOption.PicTimingSEI = MFX_CODINGOPTION_OFF; + param->codingOption.VuiNalHrdParameters = MFX_CODINGOPTION_UNKNOWN; + param->codingOption.FramePicture = MFX_CODINGOPTION_UNKNOWN; + // introduced in API 1.3 + param->codingOption.RefPicMarkRep = MFX_CODINGOPTION_UNKNOWN; + param->codingOption.FieldOutput = MFX_CODINGOPTION_UNKNOWN; + param->codingOption.NalHrdConformance = MFX_CODINGOPTION_UNKNOWN; + param->codingOption.SingleSeiNalUnit = MFX_CODINGOPTION_UNKNOWN; + param->codingOption.VuiVclHrdParameters = MFX_CODINGOPTION_UNKNOWN; + // introduced in API 1.4 + param->codingOption.ViewOutput = MFX_CODINGOPTION_UNKNOWN; + // introduced in API 1.6 + param->codingOption.RecoveryPointSEI = MFX_CODINGOPTION_UNKNOWN; + + // introduced in API 1.3 + memset(¶m->videoSignalInfo, 0, sizeof(mfxExtVideoSignalInfo)); + param->videoSignalInfo.Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO; + param->videoSignalInfo.Header.BufferSz = sizeof(mfxExtVideoSignalInfo); + param->videoSignalInfo.VideoFormat = 5; // undefined + param->videoSignalInfo.VideoFullRange = 0; // TV range + param->videoSignalInfo.ColourDescriptionPresent = 0; // don't write to bitstream + param->videoSignalInfo.ColourPrimaries = 2; // undefined + param->videoSignalInfo.TransferCharacteristics = 2; // undefined + param->videoSignalInfo.MatrixCoefficients = 2; // undefined + + // introduced in API 1.6 + memset(¶m->codingOption2, 0, sizeof(mfxExtCodingOption2)); + param->codingOption2.Header.BufferId = MFX_EXTBUFF_CODING_OPTION2; + param->codingOption2.Header.BufferSz = sizeof(mfxExtCodingOption2); + param->codingOption2.IntRefType = 0; + param->codingOption2.IntRefCycleSize = 2; + param->codingOption2.IntRefQPDelta = 0; + param->codingOption2.MaxFrameSize = 0; + param->codingOption2.BitrateLimit = MFX_CODINGOPTION_ON; + param->codingOption2.ExtBRC = MFX_CODINGOPTION_OFF; + param->codingOption2.MBBRC = MFX_CODINGOPTION_UNKNOWN; + // introduced in API 1.7 + param->codingOption2.LookAheadDepth = 40; + param->codingOption2.Trellis = MFX_TRELLIS_UNKNOWN; + + // GOP & rate control + param->gop.gop_pic_size = -1; // set automatically + param->gop.int_ref_cycle_size = -1; // set automatically + param->rc.lookahead = -1; // set automatically + param->rc.cqp_offsets[0] = 0; + param->rc.cqp_offsets[1] = 2; + param->rc.cqp_offsets[2] = 4; + param->rc.vbv_max_bitrate = 0; + param->rc.vbv_buffer_size = 0; + param->rc.vbv_buffer_init = .5; + + // introduced in API 1.0 + memset(videoParam, 0, sizeof(mfxVideoParam)); + param->videoParam = videoParam; + param->videoParam->Protected = 0; // reserved, must be 0 + param->videoParam->NumExtParam = 0; + param->videoParam->IOPattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY; + param->videoParam->mfx.TargetUsage = MFX_TARGETUSAGE_2; + param->videoParam->mfx.GopOptFlag = MFX_GOP_CLOSED; + param->videoParam->mfx.NumThread = 0; // deprecated, must be 0 + param->videoParam->mfx.EncodedOrder = 0; // input is in display order + param->videoParam->mfx.IdrInterval = 0; // all I-frames are IDR + param->videoParam->mfx.NumSlice = 0; // use Media SDK default + param->videoParam->mfx.NumRefFrame = 0; // use Media SDK default + param->videoParam->mfx.GopPicSize = 0; // use Media SDK default + param->videoParam->mfx.GopRefDist = 4; // power of 2, >= 4: B-pyramid + // introduced in API 1.1 + param->videoParam->AsyncDepth = AV_QSV_ASYNC_DEPTH_DEFAULT; + // introduced in API 1.3 + param->videoParam->mfx.BRCParamMultiplier = 0; // no multiplier + + // FrameInfo: set by video encoder, except PicStruct + param->videoParam->mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; + + // attach supported mfxExtBuffer structures to the mfxVideoParam + param->videoParam->NumExtParam = 0; + param->videoParam->ExtParam = param->ExtParamArray; + param->videoParam->ExtParam[param->videoParam->NumExtParam++] = (mfxExtBuffer*)¶m->codingOption; + param->videoParam->ExtParam[param->videoParam->NumExtParam++] = (mfxExtBuffer*)¶m->videoSignalInfo; + if (hb_qsv_info->capabilities & HB_QSV_CAP_MSDK_API_1_6) + { + param->videoParam->ExtParam[param->videoParam->NumExtParam++] = (mfxExtBuffer*)¶m->codingOption2; + } + } + else + { + hb_error("hb_qsv_param_default: invalid pointer(s)"); + return -1; + } + return 0; +} diff --git a/libhb/qsv_common.h b/libhb/qsv_common.h new file mode 100644 index 000000000..cd85d33a2 --- /dev/null +++ b/libhb/qsv_common.h @@ -0,0 +1,119 @@ +/* qsv_common.h + * + * Copyright (c) 2003-2013 HandBrake Team + * This file is part of the HandBrake source code. + * Homepage: <http://handbrake.fr/>. + * It may be used under the terms of the GNU General Public License v2. + * For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html + */ + +#ifndef HB_QSV_COMMON_H +#define HB_QSV_COMMON_H + +#include "msdk/mfxvideo.h" +#include "libavcodec/avcodec.h" + +/* Minimum Intel Media SDK version (currently 1.3, for Sandy Bridge support) */ +#define HB_QSV_MINVERSION_MAJOR AV_QSV_MSDK_VERSION_MAJOR +#define HB_QSV_MINVERSION_MINOR AV_QSV_MSDK_VERSION_MINOR + +/* + * Get & store all available Intel Quick Sync information: + * + * - general availability + * - available implementations (hardware-accelerated, software fallback, etc.) + * - available codecs, filters, etc. for direct access (convenience) + * - supported API version + * - supported resolutions + */ +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_BRC (1 << 2) // mfxExtCodingOption2: MBBRC/ExtBRC +#define HB_QSV_CAP_OPTION2_LOOKAHEAD (1 << 3) // mfxExtCodingOption2: LookAhead +#define HB_QSV_CAP_OPTION2_TRELLIS (1 << 4) // mfxExtCodingOption2: Trellis + + // TODO: add available decoders, filters, encoders, + // maximum decode and 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(); + +/* 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); +int hb_qsv_decode_is_supported(enum AVCodecID codec_id, enum AVPixelFormat pix_fmt); + +/* Media SDK parameters handling */ +enum +{ + HB_QSV_PARAM_OK, + HB_QSV_PARAM_ERROR, + HB_QSV_PARAM_BAD_NAME, + HB_QSV_PARAM_BAD_VALUE, + HB_QSV_PARAM_UNSUPPORTED, +}; + +typedef struct +{ + /* + * Supported mfxExtBuffer.BufferId values: + * + * MFX_EXTBUFF_AVC_REFLIST_CTRL + * MFX_EXTBUFF_AVC_TEMPORAL_LAYERS + * MFX_EXTBUFF_CODING_OPTION + * MFX_EXTBUFF_CODING_OPTION_SPSPPS + * MFX_EXTBUFF_CODING_OPTION2 + * MFX_EXTBUFF_ENCODER_CAPABILITY + * MFX_EXTBUFF_ENCODER_RESET_OPTION + * MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION + * MFX_EXTBUFF_PICTURE_TIMING_SEI + * MFX_EXTBUFF_VIDEO_SIGNAL_INFO + * + * This should cover all encode-compatible extended + * buffers that can be attached to an mfxVideoParam. + */ +#define HB_QSV_ENC_NUM_EXT_PARAM_MAX 10 + mfxExtBuffer* ExtParamArray[HB_QSV_ENC_NUM_EXT_PARAM_MAX]; + mfxExtCodingOption codingOption; + mfxExtCodingOption2 codingOption2; + mfxExtVideoSignalInfo videoSignalInfo; + struct + { + int gop_pic_size; + int int_ref_cycle_size; + } gop; + struct + { + int lookahead; + int cqp_offsets[3]; + int vbv_max_bitrate; + int vbv_buffer_size; + float vbv_buffer_init; + } rc; + + // assigned via hb_qsv_param_default, may be shared with another structure + mfxVideoParam *videoParam; +} hb_qsv_param_t; + +#define HB_QSV_CLIP3(min, max, val) ((val < min) ? min : (val > max) ? max : val) +int hb_qsv_codingoption_xlat(int val); +int hb_qsv_trellisvalue_xlat(int val); +int hb_qsv_atoindex(const char* const *arr, const char *str, int *err); +int hb_qsv_atobool (const char *str, int *err); +int hb_qsv_atoi (const char *str, int *err); +float hb_qsv_atof (const char *str, int *err); + +int hb_qsv_param_default(hb_qsv_param_t *param, mfxVideoParam *videoParam); +int hb_qsv_param_parse (hb_qsv_param_t *param, const char *key, const char *value, int vcodec); + +#endif diff --git a/libhb/qsv_filter.c b/libhb/qsv_filter.c new file mode 100644 index 000000000..3de9d6254 --- /dev/null +++ b/libhb/qsv_filter.c @@ -0,0 +1,648 @@ +/* ********************************************************************* *\ + +Copyright (C) 2013 Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +- Neither the name of Intel Corporation nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +\* ********************************************************************* */ + +#include "hb.h" +#include "hbffmpeg.h" +#include "libavcodec/qsv.h" +#include "qsv_filter.h" +#include "enc_qsv.h" + +struct hb_filter_private_s +{ + hb_job_t *job; + hb_list_t *list; + + int width_in; + int height_in; + int pix_fmt; + int pix_fmt_out; + int width_out; + int height_out; + int crop[4]; + int deinterlace; + int is_frc_used; + + av_qsv_space *vpp_space; + + // FRC param(s) + mfxExtVPPFrameRateConversion frc_config; +}; + +static int hb_qsv_filter_init( hb_filter_object_t * filter, + hb_filter_init_t * init ); + +static int hb_qsv_filter_work( hb_filter_object_t * filter, + hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ); + +static int hb_qsv_filter_info( hb_filter_object_t * filter, + hb_filter_info_t * info ); + +static void hb_qsv_filter_close( hb_filter_object_t * filter ); + +hb_filter_object_t hb_filter_qsv = +{ + .id = HB_FILTER_QSV, + .enforce_order = 1, + .name = "Quick Sync Video VPP", + .settings = NULL, + .init = hb_qsv_filter_init, + .work = hb_qsv_filter_work, + .close = hb_qsv_filter_close, + .info = hb_qsv_filter_info, +}; + +static int filter_init( av_qsv_context* qsv, hb_filter_private_t * pv ){ + mfxStatus sts; + int i=0; + + if(!qsv) return 3; + + + if(!qsv->vpp_space){ + qsv->vpp_space = av_qsv_list_init(HAVE_THREADS); + } + if(!pv->vpp_space){ + for(i=0; i<av_qsv_list_count(qsv->vpp_space);i++){ + av_qsv_space *qsv_vpp = av_qsv_list_item( qsv->vpp_space, i ); + if(qsv_vpp->type == AV_QSV_VPP_DEFAULT){ + pv->vpp_space = qsv_vpp; + break; + } + } + } + + if(!pv->vpp_space){ + pv->vpp_space = calloc( 1, sizeof( av_qsv_space )); + pv->vpp_space->type = AV_QSV_VPP_DEFAULT; + av_qsv_list_add( qsv->vpp_space, pv->vpp_space ); + } + else + if(pv->vpp_space->is_init_done ) return 1; + + if(!qsv->dec_space || !qsv->dec_space->is_init_done) return 2; + + // we need to know final output settings before we can properly configure + if (!pv->job->qsv_enc_info.is_init_done) + { + return 2; + } + + av_qsv_add_context_usage(qsv,HAVE_THREADS); + + // see params needed like at mediasdk-man.pdf:"Appendix A: Configuration Parameter Constraints" + // for now - most will take from the decode + { + av_qsv_space *qsv_vpp = pv->vpp_space; + AV_QSV_ZERO_MEMORY(qsv_vpp->m_mfxVideoParam); + + if (pv->deinterlace) + { + /* + * Input may be progressive, interlaced or even mixed, so init with + * MFX_PICSTRUCT_UNKNOWN and use per-frame field order information + * (mfxFrameSurface1.Info.PicStruct) + */ + qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct = MFX_PICSTRUCT_UNKNOWN; + qsv_vpp->m_mfxVideoParam.vpp.Out.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; + } + else + { + /* Same PicStruct in/out: no filtering */ + qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct; + qsv_vpp->m_mfxVideoParam.vpp.Out.PicStruct = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct; + } + + // FrameRate is important for VPP to start with + if( qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN == 0 && + qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD == 0 ){ + qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN = pv->job->title->rate; + qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD = pv->job->title->rate_base; + } + + qsv_vpp->m_mfxVideoParam.vpp.In.FourCC = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FourCC; + qsv_vpp->m_mfxVideoParam.vpp.In.ChromaFormat = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.ChromaFormat; + qsv_vpp->m_mfxVideoParam.vpp.In.CropX = pv->crop[2]; + qsv_vpp->m_mfxVideoParam.vpp.In.CropY = pv->crop[0]; + qsv_vpp->m_mfxVideoParam.vpp.In.CropW = pv-> width_in - pv->crop[3] - pv->crop[2]; + qsv_vpp->m_mfxVideoParam.vpp.In.CropH = pv->height_in - pv->crop[1] - pv->crop[0]; + qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtN = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN; + qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtD = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD; + qsv_vpp->m_mfxVideoParam.vpp.In.AspectRatioW = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioW; + qsv_vpp->m_mfxVideoParam.vpp.In.AspectRatioH = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioH; + qsv_vpp->m_mfxVideoParam.vpp.In.Width = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.Width; + qsv_vpp->m_mfxVideoParam.vpp.In.Height = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.Height; + + qsv_vpp->m_mfxVideoParam.vpp.Out.FourCC = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FourCC; + qsv_vpp->m_mfxVideoParam.vpp.Out.ChromaFormat = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.ChromaFormat; + qsv_vpp->m_mfxVideoParam.vpp.Out.CropX = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropX; + qsv_vpp->m_mfxVideoParam.vpp.Out.CropY = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropY; + qsv_vpp->m_mfxVideoParam.vpp.Out.CropW = pv->width_out; + qsv_vpp->m_mfxVideoParam.vpp.Out.CropH = pv->height_out; + qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtN = pv->job->vrate; + qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtD = pv->job->vrate_base; + qsv_vpp->m_mfxVideoParam.vpp.Out.AspectRatioW = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioW; + qsv_vpp->m_mfxVideoParam.vpp.Out.AspectRatioH = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioH; + qsv_vpp->m_mfxVideoParam.vpp.Out.Width = pv->job->qsv_enc_info.align_width; + qsv_vpp->m_mfxVideoParam.vpp.Out.Height = pv->job->qsv_enc_info.align_height; + + qsv_vpp->m_mfxVideoParam.IOPattern = MFX_IOPATTERN_IN_OPAQUE_MEMORY | MFX_IOPATTERN_OUT_OPAQUE_MEMORY; + + qsv_vpp->m_mfxVideoParam.AsyncDepth = pv->job->qsv_async_depth; + + memset(&qsv_vpp->request, 0, sizeof(mfxFrameAllocRequest)*2); + + sts = MFXVideoVPP_QueryIOSurf(qsv->mfx_session, &qsv_vpp->m_mfxVideoParam, qsv_vpp->request ); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + int num_surfaces_in = qsv_vpp->request[0].NumFrameSuggested; + int num_surfaces_out = qsv_vpp->request[1].NumFrameSuggested; + + av_qsv_config *config = qsv->qsv_config; + + + qsv_vpp->surface_num = FFMIN( num_surfaces_in + num_surfaces_out + qsv_vpp->m_mfxVideoParam.AsyncDepth + config ? config->additional_buffers/2 :0 , AV_QSV_SURFACE_NUM ); + if(qsv_vpp->surface_num <= 0 ) + qsv_vpp->surface_num = AV_QSV_SURFACE_NUM; + + int i = 0; + for (i = 0; i < qsv_vpp->surface_num; i++){ + qsv_vpp->p_surfaces[i] = av_mallocz( sizeof(mfxFrameSurface1) ); + AV_QSV_CHECK_POINTER(qsv_vpp->p_surfaces[i], MFX_ERR_MEMORY_ALLOC); + memcpy(&(qsv_vpp->p_surfaces[i]->Info), &(qsv_vpp->m_mfxVideoParam.vpp.Out), sizeof(mfxFrameInfo)); + } + + qsv_vpp->sync_num = FFMIN( qsv_vpp->surface_num, AV_QSV_SYNC_NUM ); + + for (i = 0; i < qsv_vpp->sync_num; i++){ + qsv_vpp->p_syncp[i] = av_mallocz(sizeof(av_qsv_sync)); + AV_QSV_CHECK_POINTER(qsv_vpp->p_syncp[i], MFX_ERR_MEMORY_ALLOC); + qsv_vpp->p_syncp[i]->p_sync = av_mallocz(sizeof(mfxSyncPoint)); + AV_QSV_CHECK_POINTER(qsv_vpp->p_syncp[i]->p_sync, MFX_ERR_MEMORY_ALLOC); + } +/* + about available VPP filters, see "Table 4 Configurable VPP filters", mediasdk-man.pdf + Hints (optional feature) IDs: + MFX_EXTBUFF_VPP_DENOISE // Remove noise + // Value of 0-100 (inclusive) indicates + // the level of noise to remove. + MFX_EXTBUFF_VPP_DETAIL // Enhance picture details/edges: + // 0-100 value (inclusive) to indicate + // the level of details to be enhanced. + MFX_EXTBUFF_VPP_FRAME_RATE_CONVERSION // Convert input frame rate to match the output, based on frame interpolation: + // MFX_FRCALGM_PRESERVE_TIMESTAMP, + // MFX_FRCALGM_DISTRIBUTED_TIMESTAMP, + // MFX_FRCALGM_FRAME_INTERPOLATION + MFX_EXTBUFF_VPP_IMAGE_STABILIZATION // Perform image stabilization + // Stabilization modes: + // MFX_IMAGESTAB_MODE_UPSCALE + // MFX_IMAGESTAB_MODE_BOXING + MFX_EXTBUFF_VPP_PICSTRUCT_DETECTION // Perform detection of picture structure: + // Detected picture structure - top field first, bottom field first, progressive or unknown + // if video processor cannot detect picture structure. + MFX_EXTBUFF_VPP_PROCAMP // Adjust the brightness, contrast, saturation, and hue settings + + // Initialize extended buffer for frame processing + // - Process amplifier (ProcAmp) used to control brightness + // - mfxExtVPPDoUse: Define the processing algorithm to be used + // - mfxExtVPPProcAmp: ProcAmp configuration + // - mfxExtBuffer: Add extended buffers to VPP parameter configuration + mfxExtVPPDoUse extDoUse; + mfxU32 tabDoUseAlg[1]; + extDoUse.Header.BufferId = MFX_EXTBUFF_VPP_DOUSE; + extDoUse.Header.BufferSz = sizeof(mfxExtVPPDoUse); + extDoUse.NumAlg = 1; + extDoUse.AlgList = tabDoUseAlg; + tabDoUseAlg[0] = MFX_EXTBUFF_VPP_PROCAMP; + + mfxExtVPPProcAmp procampConfig; + procampConfig.Header.BufferId = MFX_EXTBUFF_VPP_PROCAMP; + procampConfig.Header.BufferSz = sizeof(mfxExtVPPProcAmp); + procampConfig.Hue = 0.0f; // Default + procampConfig.Saturation = 1.0f; // Default + procampConfig.Contrast = 1.0; // Default + procampConfig.Brightness = 40.0; // Adjust brightness + + mfxExtBuffer* ExtBuffer[2]; + ExtBuffer[0] = (mfxExtBuffer*)&extDoUse; + ExtBuffer[1] = (mfxExtBuffer*)&procampConfig; + VPPParams.NumExtParam = 2; + VPPParams.ExtParam = (mfxExtBuffer**)&ExtBuffer[0]; +*/ + memset(&qsv_vpp->ext_opaque_alloc, 0, sizeof(qsv_vpp->ext_opaque_alloc)); + + if( (qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtN / qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtD ) != + (qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtN / qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtD) ) + { + pv->is_frc_used = 1; + } + + qsv_vpp->m_mfxVideoParam.NumExtParam = qsv_vpp->p_ext_param_num = 1 + pv->is_frc_used; + + qsv_vpp->p_ext_params = av_mallocz(sizeof(mfxExtBuffer *)*qsv_vpp->p_ext_param_num); + AV_QSV_CHECK_POINTER(qsv_vpp->p_ext_params, MFX_ERR_MEMORY_ALLOC); + + qsv_vpp->m_mfxVideoParam.ExtParam = qsv_vpp->p_ext_params; + + qsv_vpp->ext_opaque_alloc.In.Surfaces = qsv->dec_space->p_surfaces; + qsv_vpp->ext_opaque_alloc.In.NumSurface = qsv->dec_space->surface_num; + qsv_vpp->ext_opaque_alloc.In.Type = qsv->dec_space->request[0].Type; + + qsv_vpp->ext_opaque_alloc.Out.Surfaces = qsv_vpp->p_surfaces; + qsv_vpp->ext_opaque_alloc.Out.NumSurface = qsv_vpp->surface_num; + qsv_vpp->ext_opaque_alloc.Out.Type = qsv->dec_space->request[0].Type; + + qsv_vpp->ext_opaque_alloc.Header.BufferId = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION; + qsv_vpp->ext_opaque_alloc.Header.BufferSz = sizeof(mfxExtOpaqueSurfaceAlloc); + qsv_vpp->p_ext_params[0] = (mfxExtBuffer*)&qsv_vpp->ext_opaque_alloc; + + if(pv->is_frc_used) + { + pv->frc_config.Header.BufferId = MFX_EXTBUFF_VPP_FRAME_RATE_CONVERSION; + pv->frc_config.Header.BufferSz = sizeof(mfxExtVPPFrameRateConversion); + pv->frc_config.Algorithm = MFX_FRCALGM_PRESERVE_TIMESTAMP; + + qsv_vpp->p_ext_params[1] = (mfxExtBuffer*)&pv->frc_config; + } + + sts = MFXVideoVPP_Init(qsv->mfx_session, &qsv_vpp->m_mfxVideoParam); + + AV_QSV_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + qsv_vpp->is_init_done = 1; + } + return 0; +} + +static int hb_qsv_filter_init( hb_filter_object_t * filter, + hb_filter_init_t * init ) +{ + + filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) ); + hb_filter_private_t * pv = filter->private_data; + + pv->list = hb_list_init(); + // list of init params provided at work.c:~700 + pv->width_in = init->width; + pv->height_in = init->height; + pv->width_out = init->width; + pv->height_out = init->height; + memcpy( pv->crop, init->crop, sizeof( int[4] ) ); + + if (filter->settings != NULL) + { + sscanf(filter->settings, "%d:%d:%d:%d:%d:%d_dei:%d", + &pv->width_out, &pv->height_out, + &pv->crop[0], &pv->crop[1], &pv->crop[2], &pv->crop[3], + &pv->deinterlace); + } + + pv->job = init->job; + + // will be later as more params will be known + // filter_init(pv->job->qsv, pv); + + // just passing + init->vrate = init->vrate; + init->vrate_base = init->vrate_base; + + // framerate shaping not yet supported + init->cfr = 0; + + init->pix_fmt = pv->pix_fmt; + init->width = pv->width_out; + init->height = pv->height_out; + memcpy( init->crop, pv->crop, sizeof( int[4] ) ); + + return 0; +} + +static int hb_qsv_filter_info( hb_filter_object_t * filter, + hb_filter_info_t * info ) +{ + + hb_filter_private_t *pv = filter->private_data; + if (pv == NULL) + return -1; + + sprintf(info->human_readable_desc, + "source: %d * %d, crop (%d/%d/%d/%d): %d * %d, scale: %d * %d", + pv->width_in, pv->height_in, + pv->crop[0], pv->crop[1], pv->crop[2], pv->crop[3], + pv->width_in - pv->crop[2] - pv->crop[3], + pv->height_in - pv->crop[0] - pv->crop[1], + pv->width_out, pv->height_out); + + if (pv->deinterlace) + { + sprintf(info->human_readable_desc + strlen(info->human_readable_desc), + ", deinterlace"); + } + + return 0; +} + +void qsv_filter_close( av_qsv_context* qsv, AV_QSV_STAGE_TYPE vpp_type ){ + int i = 0; + av_qsv_space* vpp_space = 0; + + if(qsv && qsv->is_context_active && qsv->vpp_space) + for(i=av_qsv_list_count( qsv->vpp_space);i>0;i--){ + + vpp_space = av_qsv_list_item( qsv->vpp_space, i-1 ); + if( vpp_space->type == vpp_type && vpp_space->is_init_done){ + + hb_log( "qsv_filter[%s] done: max_surfaces: %u/%u , max_syncs: %u/%u", ((vpp_type == AV_QSV_VPP_DEFAULT)?"Default": "User") ,vpp_space->surface_num_max_used, vpp_space->surface_num, vpp_space->sync_num_max_used, vpp_space->sync_num ); + + for (i = 0; i < vpp_space->surface_num; i++){ + av_freep(&vpp_space->p_surfaces[i]); + } + vpp_space->surface_num = 0; + + if( vpp_space->p_ext_param_num || vpp_space->p_ext_params ) + av_freep(&vpp_space->p_ext_params); + vpp_space->p_ext_param_num = 0; + + for (i = 0; i < vpp_space->sync_num; i++){ + av_freep(&vpp_space->p_syncp[i]->p_sync); + av_freep(&vpp_space->p_syncp[i]); + } + vpp_space->sync_num = 0; + + av_qsv_list_rem(qsv->vpp_space,vpp_space); + if( av_qsv_list_count(qsv->vpp_space) == 0 ) + av_qsv_list_close(&qsv->vpp_space); + + vpp_space->is_init_done = 0; + break; + } + } +} + +static void hb_qsv_filter_close( hb_filter_object_t * filter ) +{ + int i = 0; + hb_filter_private_t * pv = filter->private_data; + + if ( !pv ) + { + return; + } + + av_qsv_context* qsv = pv->job->qsv; + if(qsv && qsv->vpp_space && av_qsv_list_count(qsv->vpp_space) > 0){ + + // closing local stuff + qsv_filter_close(qsv,AV_QSV_VPP_DEFAULT); + + // closing the commong stuff + av_qsv_context_clean(qsv); + } + hb_list_close(&pv->list); + free( pv ); + filter->private_data = NULL; +} + +int process_frame(av_qsv_list* received_item, av_qsv_context* qsv, hb_filter_private_t * pv ){ + + // 1 if have results , 0 - otherwise + int ret = 1; + + mfxStatus sts = MFX_ERR_NONE; + mfxFrameSurface1 *work_surface = NULL; + av_qsv_stage* stage = 0; + + av_qsv_space *qsv_vpp = pv->vpp_space; + + if(received_item){ + stage = av_qsv_get_last_stage( received_item ); + work_surface = stage->out.p_surface; + } + + int sync_idx = av_qsv_get_free_sync(qsv_vpp, qsv); + int surface_idx = -1; + + for(;;) + { + if (sync_idx == -1) + { + hb_error("qsv: Not enough resources allocated for QSV filter"); + ret = 0; + break; + } + if( sts == MFX_ERR_MORE_SURFACE || sts == MFX_ERR_NONE ) + surface_idx = av_qsv_get_free_surface(qsv_vpp, qsv, &(qsv_vpp->m_mfxVideoParam.vpp.Out), QSV_PART_ANY); + if (surface_idx == -1) { + hb_error("qsv: Not enough resources allocated for QSV filter"); + ret = 0; + break; + } + if (work_surface) { + work_surface->Info.CropX = pv->crop[2]; + work_surface->Info.CropY = pv->crop[0]; + work_surface->Info.CropW = pv->width_in - pv->crop[3] - pv->crop[2]; + work_surface->Info.CropH = pv->height_in - pv->crop[1] - pv->crop[0]; + } + + sts = MFXVideoVPP_RunFrameVPPAsync(qsv->mfx_session, work_surface, qsv_vpp->p_surfaces[surface_idx] , NULL, qsv_vpp->p_syncp[sync_idx]->p_sync); + + if( MFX_ERR_MORE_DATA == sts ){ + if(!qsv_vpp->pending){ + qsv_vpp->pending = av_qsv_list_init(0); + } + + // if we have no results, we should not miss resource(s) + av_qsv_list_add( qsv_vpp->pending, received_item); + + ff_qsv_atomic_dec(&qsv_vpp->p_syncp[sync_idx]->in_use); + + ret = 0; + break; + } + + if( MFX_ERR_MORE_DATA == sts || (MFX_ERR_NONE <= sts && MFX_WRN_DEVICE_BUSY != sts)){ + if (work_surface){ + ff_qsv_atomic_dec(&work_surface->Data.Locked); + } + } + + if( MFX_ERR_MORE_SURFACE == sts || MFX_ERR_NONE <= sts){ + if( MFX_ERR_MORE_SURFACE == sts ) + continue; + + if (qsv_vpp->p_surfaces[surface_idx] && MFX_WRN_DEVICE_BUSY != sts ) + ff_qsv_atomic_inc(&qsv_vpp->p_surfaces[surface_idx]->Data.Locked); + } + + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + if (MFX_ERR_NONE <= sts ) // repeat the call if warning and no output + { + if (MFX_WRN_DEVICE_BUSY == sts){ + av_qsv_sleep(10); // wait if device is busy + continue; + } + + // shouldnt be a case but drain + if(stage){ + av_qsv_stage* new_stage = av_qsv_stage_init(); + + new_stage->type = AV_QSV_VPP_DEFAULT; + new_stage->in.p_surface = work_surface; + new_stage->out.p_surface = qsv_vpp->p_surfaces[surface_idx]; + new_stage->out.sync = qsv_vpp->p_syncp[sync_idx]; + av_qsv_add_stagee( &received_item, new_stage,HAVE_THREADS ); + + // add pending resources for the proper reclaim later + if( qsv_vpp->pending ){ + if( av_qsv_list_count(qsv_vpp->pending)>0 ){ + new_stage->pending = qsv_vpp->pending; + } + qsv_vpp->pending = 0; + + // making free via decrement for all pending + int i = 0; + for (i = av_qsv_list_count(new_stage->pending); i > 0; i--){ + av_qsv_list *atom_list = av_qsv_list_item(new_stage->pending, i-1); + av_qsv_stage *stage = av_qsv_get_last_stage( atom_list ); + mfxFrameSurface1 *work_surface = stage->out.p_surface; + if (work_surface) + ff_qsv_atomic_dec(&work_surface->Data.Locked); + } + } + } + break; + } + + ff_qsv_atomic_dec(&qsv_vpp->p_syncp[sync_idx]->in_use); + + if (MFX_ERR_NOT_ENOUGH_BUFFER == sts) + DEBUG_ASSERT( 1,"The bitstream buffer size is insufficient." ); + + break; + } + + return ret; +} + +static int hb_qsv_filter_work( hb_filter_object_t * filter, + hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + + hb_filter_private_t * pv = filter->private_data; + hb_buffer_t * in = *buf_in; + hb_buffer_t * out = *buf_out; + int sts = 0; + + av_qsv_context* qsv = pv->job->qsv; + + if ( !pv ) + { + *buf_out = in; + *buf_in = NULL; + return HB_FILTER_OK; + } + + while(1){ + int ret = filter_init(qsv,pv); + if(ret >= 2) + av_qsv_sleep(1); + else + break; + } + + *buf_in = NULL; + + if ( in->size <= 0 ) + { + while(1){ + sts = process_frame(in->qsv_details.qsv_atom, qsv, pv); + if(sts) + hb_list_add(pv->list,in); + else + break; + } + + hb_list_add( pv->list, in ); + *buf_out = link_buf_list( pv ); + return HB_FILTER_DONE; + } + + sts = process_frame(in->qsv_details.qsv_atom, qsv, pv); + + if(sts){ + hb_list_add(pv->list,in); + } + + if( hb_list_count(pv->list) ){ + *buf_out = hb_list_item(pv->list,0); + out = *buf_out; + if(pv->is_frc_used && out) + { + mfxStatus sts = MFX_ERR_NONE; + if(out->qsv_details.qsv_atom){ + av_qsv_stage* stage = av_qsv_get_last_stage( out->qsv_details.qsv_atom ); + mfxFrameSurface1 *work_surface = stage->out.p_surface; + + av_qsv_wait_on_sync( qsv,stage ); + + av_qsv_space *qsv_vpp = pv->vpp_space; + int64_t duration = ((double)qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtD/(double)qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtN ) * 90000.; + out->s.start = work_surface->Data.TimeStamp; + out->s.stop = work_surface->Data.TimeStamp + duration; + } + } + hb_list_rem(pv->list,*buf_out); + } + else + *buf_out = NULL; + + return HB_FILTER_OK; +} + +// see devavcode.c +hb_buffer_t *link_buf_list( hb_filter_private_t *pv ) +{ + hb_buffer_t *head = hb_list_item( pv->list, 0 ); + + if ( head ) + { + hb_list_rem( pv->list, head ); + hb_buffer_t *last = head, *buf; + while ( ( buf = hb_list_item( pv->list, 0 ) ) != NULL ) + { + hb_list_rem( pv->list, buf ); + last->next = buf; + last = buf; + } + } + return head; +} + diff --git a/libhb/qsv_filter.h b/libhb/qsv_filter.h new file mode 100644 index 000000000..e55a85cdf --- /dev/null +++ b/libhb/qsv_filter.h @@ -0,0 +1,35 @@ +/* ********************************************************************* *\ + +Copyright (C) 2013 Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +- Neither the name of Intel Corporation nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +\* ********************************************************************* */ + +#ifndef QSV_FILTER_H +#define QSV_FILTER_H + +hb_buffer_t *link_buf_list( hb_filter_private_t *pv ); +void qsv_filter_close( av_qsv_context* qsv, AV_QSV_STAGE_TYPE vpp_type ); + +#endif // QSV_FILTER_H diff --git a/libhb/qsv_filter_pp.c b/libhb/qsv_filter_pp.c new file mode 100644 index 000000000..1aef1eb80 --- /dev/null +++ b/libhb/qsv_filter_pp.c @@ -0,0 +1,916 @@ +/* ********************************************************************* *\ + +Copyright (C) 2013 Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +- Neither the name of Intel Corporation nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +\* ********************************************************************* */ + +#include "hb.h" +#include "hbffmpeg.h" +#include "libavcodec/qsv.h" +#include "qsv_filter_pp.h" +#include "qsv_filter.h" +#include "qsv_memory.h" + + +static int hb_qsv_filter_pre_init( hb_filter_object_t * filter, + hb_filter_init_t * init ); +static int hb_qsv_filter_pre_work( hb_filter_object_t * filter, + hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ); +static int hb_qsv_filter_pre_info( hb_filter_object_t * filter, + hb_filter_info_t * info ); +static void hb_qsv_filter_pre_close( hb_filter_object_t * filter ); + +static int hb_qsv_filter_post_init( hb_filter_object_t * filter, + hb_filter_init_t * init ); +static int hb_qsv_filter_post_work( hb_filter_object_t * filter, + hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ); +static int hb_qsv_filter_post_info( hb_filter_object_t * filter, + hb_filter_info_t * info ); +static void hb_qsv_filter_post_close( hb_filter_object_t * filter ); + + +hb_filter_object_t hb_filter_qsv_pre = +{ + .id = HB_FILTER_QSV_PRE, + .enforce_order = 1, + .name = "Quick Sync Video user filter (pre)", + .settings = NULL, + .init = hb_qsv_filter_pre_init, + .work = hb_qsv_filter_pre_work, + .close = hb_qsv_filter_pre_close, + .info = hb_qsv_filter_pre_info, +}; + +hb_filter_object_t hb_filter_qsv_post = +{ + .id = HB_FILTER_QSV_POST, + .enforce_order = 1, + .name = "Quick Sync Video user filter (post)", + .settings = NULL, + .init = hb_qsv_filter_post_init, + .work = hb_qsv_filter_post_work, + .close = hb_qsv_filter_post_close, + .info = hb_qsv_filter_post_info, +}; + + +static int filter_pre_init( av_qsv_context* qsv, hb_filter_private_t * pv ){ + mfxStatus sts = MFX_ERR_NONE; + int i=0; + + if(!qsv) return 3; + + av_qsv_space *prev_vpp = 0; + + if(!qsv->vpp_space){ + qsv->vpp_space = av_qsv_list_init(HAVE_THREADS); + // note some change as : when no size changes -> no VPP used + // impact on : prev_vpp + } + + if(!pv->vpp_space){ + for(i=0; i<av_qsv_list_count(qsv->vpp_space);i++){ + av_qsv_space *qsv_vpp = av_qsv_list_item( qsv->vpp_space, i ); + if(qsv_vpp->type == AV_QSV_VPP_USER){ + pv->vpp_space = qsv_vpp; + break; + } + else + if(qsv_vpp->type == AV_QSV_VPP_DEFAULT){ + prev_vpp = qsv_vpp; + } + + } + } + + if(!pv->vpp_space){ + pv->vpp_space = calloc( 1, sizeof( av_qsv_space )); + pv->vpp_space->type = AV_QSV_VPP_USER; + av_qsv_list_add( qsv->vpp_space, pv->vpp_space ); + av_qsv_add_context_usage(qsv,HAVE_THREADS); + } + else + if(pv->vpp_space->is_init_done ) return 1; + + if(!qsv->dec_space || !qsv->dec_space->is_init_done) return 2; + + av_qsv_space *qsv_vpp = pv->vpp_space; + + AV_QSV_ZERO_MEMORY(qsv_vpp->m_mfxVideoParam); + + + if (prev_vpp) + { + memcpy( &qsv_vpp->m_mfxVideoParam.vpp, &prev_vpp->m_mfxVideoParam.vpp, sizeof(prev_vpp->m_mfxVideoParam.vpp)); + } + else + { + AV_QSV_ZERO_MEMORY(qsv_vpp->m_mfxVideoParam); + + // FrameRate is important for VPP to start with + if( qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN == 0 && + qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD == 0 ){ + qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN = pv->job->title->rate; + qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD = pv->job->title->rate_base; + } + + qsv_vpp->m_mfxVideoParam.vpp.In.FourCC = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FourCC; + qsv_vpp->m_mfxVideoParam.vpp.In.ChromaFormat = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.ChromaFormat; + qsv_vpp->m_mfxVideoParam.vpp.In.CropX = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropX; + qsv_vpp->m_mfxVideoParam.vpp.In.CropY = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropY; + qsv_vpp->m_mfxVideoParam.vpp.In.CropW = pv->job->title->width; + qsv_vpp->m_mfxVideoParam.vpp.In.CropH = pv->job->title->height; + qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct; + qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtN = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN; + qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtD = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD; + qsv_vpp->m_mfxVideoParam.vpp.In.AspectRatioW = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioW; + qsv_vpp->m_mfxVideoParam.vpp.In.AspectRatioH = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioH; + qsv_vpp->m_mfxVideoParam.vpp.In.Width = AV_QSV_ALIGN16(pv->job->title->width); + qsv_vpp->m_mfxVideoParam.vpp.In.Height = (MFX_PICSTRUCT_PROGRESSIVE == qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct)? + AV_QSV_ALIGN16(pv->job->title->height) : AV_QSV_ALIGN32(pv->job->title->height); + + qsv_vpp->m_mfxVideoParam.vpp.Out.FourCC = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FourCC; + qsv_vpp->m_mfxVideoParam.vpp.Out.ChromaFormat = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.ChromaFormat; + qsv_vpp->m_mfxVideoParam.vpp.Out.CropX = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropX; + qsv_vpp->m_mfxVideoParam.vpp.Out.CropY = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropY; + qsv_vpp->m_mfxVideoParam.vpp.Out.CropW = pv->job->title->width; + qsv_vpp->m_mfxVideoParam.vpp.Out.CropH = pv->job->title->height; + qsv_vpp->m_mfxVideoParam.vpp.Out.PicStruct = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct; + qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtN = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN; + qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtD = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD; + qsv_vpp->m_mfxVideoParam.vpp.Out.AspectRatioW = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioW; + qsv_vpp->m_mfxVideoParam.vpp.Out.AspectRatioH = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioH; + qsv_vpp->m_mfxVideoParam.vpp.Out.Width = AV_QSV_ALIGN16(pv->job->title->width); + qsv_vpp->m_mfxVideoParam.vpp.Out.Height = (MFX_PICSTRUCT_PROGRESSIVE == qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct)? + AV_QSV_ALIGN16(pv->job->title->height) : AV_QSV_ALIGN32(pv->job->title->height); + + memset(&qsv_vpp->request, 0, sizeof(mfxFrameAllocRequest)*2); + } + + qsv_vpp->m_mfxVideoParam.IOPattern = MFX_IOPATTERN_IN_OPAQUE_MEMORY | MFX_IOPATTERN_OUT_OPAQUE_MEMORY; + + qsv_vpp->surface_num = FFMIN(prev_vpp ? prev_vpp->surface_num : qsv->dec_space->surface_num/2, AV_QSV_SURFACE_NUM); + + for(i = 0; i < qsv_vpp->surface_num; i++){ + qsv_vpp->p_surfaces[i] = av_mallocz( sizeof(mfxFrameSurface1) ); + AV_QSV_CHECK_POINTER(qsv_vpp->p_surfaces[i], MFX_ERR_MEMORY_ALLOC); + memcpy(&(qsv_vpp->p_surfaces[i]->Info), &(qsv_vpp->m_mfxVideoParam.vpp.Out), sizeof(mfxFrameInfo)); + } + + qsv_vpp->sync_num = FFMIN(prev_vpp ? prev_vpp->sync_num : qsv->dec_space->sync_num, AV_QSV_SYNC_NUM); + for (i = 0; i < qsv_vpp->sync_num; i++){ + qsv_vpp->p_syncp[i] = av_mallocz(sizeof(av_qsv_sync)); + AV_QSV_CHECK_POINTER(qsv_vpp->p_syncp[i], MFX_ERR_MEMORY_ALLOC); + qsv_vpp->p_syncp[i]->p_sync = av_mallocz(sizeof(mfxSyncPoint)); + AV_QSV_CHECK_POINTER(qsv_vpp->p_syncp[i]->p_sync, MFX_ERR_MEMORY_ALLOC); + } + + memset(&qsv_vpp->ext_opaque_alloc, 0, sizeof(mfxExtOpaqueSurfaceAlloc)); + qsv_vpp->m_mfxVideoParam.NumExtParam = qsv_vpp->p_ext_param_num = 1; + + qsv_vpp->p_ext_params = av_mallocz(sizeof(mfxExtBuffer *)*qsv_vpp->p_ext_param_num); + AV_QSV_CHECK_POINTER(qsv_vpp->p_ext_params, MFX_ERR_MEMORY_ALLOC); + + qsv_vpp->m_mfxVideoParam.ExtParam = qsv_vpp->p_ext_params; + + qsv_vpp->ext_opaque_alloc.Header.BufferId = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION; + qsv_vpp->ext_opaque_alloc.Header.BufferSz = sizeof(mfxExtOpaqueSurfaceAlloc); + qsv_vpp->p_ext_params[0] = (mfxExtBuffer*)&qsv_vpp->ext_opaque_alloc; + + if(prev_vpp){ + qsv_vpp->ext_opaque_alloc.In.Surfaces = prev_vpp->p_surfaces; + qsv_vpp->ext_opaque_alloc.In.NumSurface = prev_vpp->surface_num; + } + else{ + qsv_vpp->ext_opaque_alloc.In.Surfaces = qsv->dec_space->p_surfaces; + qsv_vpp->ext_opaque_alloc.In.NumSurface = qsv->dec_space->surface_num; + } + qsv_vpp->ext_opaque_alloc.In.Type = qsv->dec_space->request[0].Type; + + qsv_vpp->ext_opaque_alloc.Out.Surfaces = qsv_vpp->p_surfaces; + qsv_vpp->ext_opaque_alloc.Out.NumSurface = qsv_vpp->surface_num; + qsv_vpp->ext_opaque_alloc.Out.Type = qsv->dec_space->request[0].Type; + + pv->qsv_user = hb_list_init(); + + qsv_filter_t *plugin = av_mallocz( sizeof(qsv_filter_t) ); + + plugin->pv = pv; + plugin->plug.pthis = plugin; + plugin->plug.PluginInit = qsv_PluginInit; + plugin->plug.PluginClose = qsv_PluginClose; + plugin->plug.GetPluginParam = qsv_GetPluginParam; + plugin->plug.Submit = qsv_Submit; + plugin->plug.Execute = qsv_Execute; + plugin->plug.FreeResources = qsv_FreeResources; + + hb_list_add(pv->qsv_user,plugin); + + sts=MFXVideoUSER_Register(qsv->mfx_session,0,&plugin->plug); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + plugin_init(plugin,&qsv_vpp->m_mfxVideoParam); + + qsv_vpp->is_init_done = 1; + + return 0; +} + +static int hb_qsv_filter_pre_info( hb_filter_object_t * filter, + hb_filter_info_t * info ){ + hb_filter_private_t * pv = filter->private_data; + if( !pv ) + return 0; + + sprintf(info->human_readable_desc, "copy data to system memory"); + + return 0; +} +static int hb_qsv_filter_pre_init( hb_filter_object_t * filter, + hb_filter_init_t * init ){ + filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) ); + hb_filter_private_t * pv = filter->private_data; + pv->job = init->job; + + pv->pre.frame_go = 0; + pv->pre.frame_completed = hb_cond_init(); + pv->pre.frame_completed_lock = hb_lock_init(); + + pv->post.frame_go = 0; + pv->post.frame_completed = hb_cond_init(); + pv->post.frame_completed_lock = hb_lock_init(); + + pv->pre_busy.frame_go = 0; + pv->pre_busy.frame_completed = hb_cond_init(); + pv->pre_busy.frame_completed_lock = hb_lock_init(); + + pv->post_busy.frame_go = 0; + pv->post_busy.frame_completed = hb_cond_init(); + pv->post_busy.frame_completed_lock = hb_lock_init(); + + pv->list = hb_list_init(); + + // just to remind: + // PIX_FMT_YUV420P, ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) , 3 planes: Y, U, V + // PIX_FMT_NV12, ///< planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V) + pv->sws_context_from_nv12 = hb_sws_get_context( + pv->job->title->width, pv->job->title->height, AV_PIX_FMT_NV12, + pv->job->title->width, pv->job->title->height, AV_PIX_FMT_YUV420P, + SWS_LANCZOS|SWS_ACCURATE_RND); + pv->sws_context_to_nv12 = hb_sws_get_context( + pv->job->title->width, pv->job->title->height, AV_PIX_FMT_YUV420P, + pv->job->title->width, pv->job->title->height, AV_PIX_FMT_NV12, + SWS_LANCZOS|SWS_ACCURATE_RND); + return 0; +} +int pre_process_frame(hb_buffer_t *in, av_qsv_context* qsv, hb_filter_private_t * pv ){ + + // 1 if have results , 0 otherwise + int ret = 1; + + av_qsv_list* received_item = in->qsv_details.qsv_atom; + + mfxStatus sts = MFX_ERR_NONE; + mfxFrameSurface1 *work_surface = NULL; + av_qsv_stage* stage = 0; + + av_qsv_space *qsv_vpp = pv->vpp_space; + + if (received_item) + { + stage = av_qsv_get_last_stage( received_item ); + work_surface = stage->out.p_surface; + } + + int sync_idx = av_qsv_get_free_sync(qsv_vpp, qsv); + int surface_idx = -1; + + for (;;) + { + if (sync_idx == -1) + { + hb_error("qsv: Not enough resources allocated for the preprocessing filter"); + ret = 0; + break; + } + + if (sts == MFX_ERR_MORE_SURFACE || sts == MFX_ERR_NONE) + surface_idx = av_qsv_get_free_surface(qsv_vpp, qsv, &(qsv_vpp->m_mfxVideoParam.vpp.Out), QSV_PART_ANY); + if (surface_idx == -1) { + hb_error("qsv: Not enough resources allocated for the preprocessing filter"); + ret = 0; + break; + } + + sts = MFXVideoUSER_ProcessFrameAsync(qsv->mfx_session, &work_surface, 1, &qsv_vpp->p_surfaces[surface_idx] , 1, qsv_vpp->p_syncp[sync_idx]->p_sync); + + if (MFX_ERR_MORE_DATA == sts) + { + if (!qsv_vpp->pending) + { + qsv_vpp->pending = av_qsv_list_init(0); + } + + // if we have no results, we should not miss resource(s) + av_qsv_list_add( qsv_vpp->pending, received_item); + + ff_qsv_atomic_dec(&qsv_vpp->p_syncp[sync_idx]->in_use); + + ret = 0; + break; + } + + if( MFX_ERR_MORE_SURFACE == sts || MFX_ERR_NONE <= sts){ + if( MFX_ERR_MORE_SURFACE == sts ) + continue; + + if (qsv_vpp->p_surfaces[surface_idx] && MFX_WRN_DEVICE_BUSY != sts ) + ff_qsv_atomic_inc(&qsv_vpp->p_surfaces[surface_idx]->Data.Locked); + } + + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + if (MFX_ERR_NONE <= sts ) // repeat the call if warning and no output + { + if (MFX_WRN_DEVICE_BUSY == sts){ + hb_lock(pv->pre_busy.frame_completed_lock); + while(!pv->pre_busy.frame_go){ + hb_cond_timedwait(pv->pre_busy.frame_completed,pv->pre_busy.frame_completed_lock,1000); + if(*pv->job->die) + break; + } + pv->pre_busy.frame_go = 0; + hb_unlock(pv->pre_busy.frame_completed_lock); + + continue; + } + hb_lock(pv->pre.frame_completed_lock); + while(!pv->pre.frame_go){ + hb_cond_timedwait(pv->pre.frame_completed,pv->pre.frame_completed_lock,1000); + if(*pv->job->die) + break; + } + pv->pre.frame_go = 0; + hb_unlock(pv->pre.frame_completed_lock); + + in = pv->pre.out; + + if (work_surface){ + ff_qsv_atomic_dec(&work_surface->Data.Locked); + } + + // inserting for the future, will be locked until very ready + if(stage){ + av_qsv_stage* new_stage = av_qsv_stage_init(); + + new_stage->type = AV_QSV_VPP_USER; + new_stage->in.p_surface = work_surface; + new_stage->out.p_surface = qsv_vpp->p_surfaces[surface_idx]; + new_stage->out.sync = qsv_vpp->p_syncp[sync_idx]; + av_qsv_add_stagee( &received_item, new_stage,HAVE_THREADS ); + + // add pending resources for the proper reclaim later + if( qsv_vpp->pending ){ + if( av_qsv_list_count(qsv_vpp->pending)>0 ){ + new_stage->pending = qsv_vpp->pending; + } + qsv_vpp->pending = 0; + + // making free via decrement for all pending + int i = 0; + for (i = av_qsv_list_count(new_stage->pending); i > 0; i--){ + av_qsv_list *atom_list = av_qsv_list_item(new_stage->pending, i-1); + av_qsv_stage *stage = av_qsv_get_last_stage( atom_list ); + mfxFrameSurface1 *work_surface = stage->out.p_surface; + if (work_surface) + ff_qsv_atomic_dec(&work_surface->Data.Locked); + } + } + } + + break; + } + + ff_qsv_atomic_dec(&qsv_vpp->p_syncp[sync_idx]->in_use); + + if (MFX_ERR_NOT_ENOUGH_BUFFER == sts) + DEBUG_ASSERT( 1,"The bitstream buffer size is insufficient." ); + + break; + } + + return ret; +} + +static int hb_qsv_filter_pre_work( hb_filter_object_t * filter, + hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ){ + hb_filter_private_t * pv = filter->private_data; + hb_buffer_t * in = *buf_in; + hb_buffer_t * out = *buf_out; + int sts = 0; + + av_qsv_context* qsv = pv->job->qsv; + + if(!in->qsv_details.filter_details) + in->qsv_details.filter_details = pv; + + if ( in->size <= 0 ) + { + *buf_out = in; + *buf_in = NULL; + return HB_FILTER_DONE; + } + + while(1){ + int ret = filter_pre_init(qsv,pv); + if(ret >= 2) + av_qsv_sleep(1); + else + break; + } + + pv->pre.in = in; + pv->pre.out = in; + + sts = pre_process_frame(in, qsv, pv); + + if(sts){ + hb_list_add(pv->list,out); + } + + if( hb_list_count(pv->list) ){ + *buf_out = hb_list_item(pv->list,0); + hb_list_rem(pv->list,*buf_out); + *buf_in = NULL; + } + else{ + *buf_in = NULL; + *buf_out = in; + } + + return HB_FILTER_OK; +} +static void hb_qsv_filter_pre_close( hb_filter_object_t * filter ){ + int i = 0; + mfxStatus sts = MFX_ERR_NONE; + + hb_filter_private_t * pv = filter->private_data; + + if ( !pv ) + { + return; + } + + sws_freeContext(pv->sws_context_to_nv12); + sws_freeContext(pv->sws_context_from_nv12); + + av_qsv_context* qsv = pv->job->qsv; + if(qsv && qsv->vpp_space && av_qsv_list_count(qsv->vpp_space) > 0 ){ + if(pv->qsv_user && qsv->mfx_session){ + + sts=MFXVideoUSER_Unregister(qsv->mfx_session,0); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + for(i=hb_list_count(pv->qsv_user);i>0;i--){ + qsv_filter_t *plugin = hb_list_item(pv->qsv_user,i-1); + hb_list_rem(pv->qsv_user,plugin); + plugin_close(plugin); + } + hb_list_close(&pv->qsv_user); + } + + // closing local stuff + qsv_filter_close(qsv,AV_QSV_VPP_USER); + + // closing the commong stuff + av_qsv_context_clean(qsv); + } + hb_cond_close(&pv->pre.frame_completed); + hb_lock_close(&pv->pre.frame_completed_lock); + + hb_cond_close(&pv->post.frame_completed); + hb_lock_close(&pv->post.frame_completed_lock); + + hb_cond_close(&pv->pre_busy.frame_completed); + hb_lock_close(&pv->pre_busy.frame_completed_lock); + + hb_cond_close(&pv->post_busy.frame_completed); + hb_lock_close(&pv->post_busy.frame_completed_lock); + + hb_list_close( &pv->list ); + + free( pv ); + filter->private_data = NULL; +} + + +static int hb_qsv_filter_post_info( hb_filter_object_t * filter, + hb_filter_info_t * info ){ + hb_filter_private_t * pv = filter->private_data; + if( !pv ) + return 0; + + sprintf(info->human_readable_desc, "copy data to opaque memory"); + + return 0; +} +static int hb_qsv_filter_post_init( hb_filter_object_t * filter, + hb_filter_init_t * init ){ + filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) ); + hb_filter_private_t * pv = filter->private_data; + pv->job = init->job; + return 0; +} +static int hb_qsv_filter_post_work( hb_filter_object_t * filter, + hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ){ + hb_filter_private_t * pv = filter->private_data; + hb_buffer_t * in = *buf_in; + hb_buffer_t * out = *buf_out; + + if ( in->size <= 0 ) + { + *buf_out = in; + *buf_in = NULL; + return HB_FILTER_DONE; + } + + av_qsv_context* qsv = pv->job->qsv; + pv = in->qsv_details.filter_details; + + if (!pv) + { + *buf_out = NULL; + *buf_in = NULL; + return HB_FILTER_OK; + } + + while(1){ + int ret = filter_pre_init(qsv,pv); + if(ret >= 2) + av_qsv_sleep(1); + else + break; + } + + pv->post.in = in; + pv->post.out = out; + + // signal: input is prepared, can start inserting data back into pipeline + hb_lock(pv->post.frame_completed_lock); + pv->post.frame_go = 1; + hb_cond_broadcast(pv->post.frame_completed); + hb_unlock(pv->post.frame_completed_lock); + + // wait: on signal that data is ready + hb_lock(pv->post_busy.frame_completed_lock); + while(!pv->post_busy.frame_go){ + hb_cond_timedwait(pv->post_busy.frame_completed,pv->post_busy.frame_completed_lock,1000); + if(*pv->job->die) + break; + } + pv->post_busy.frame_go = 0; + hb_unlock(pv->post_busy.frame_completed_lock); + + if (pv->post.status == HB_FILTER_OK || pv->post.status == HB_FILTER_DONE) + { + *buf_out = in; + } + else + { + *buf_out = NULL; + pv->post.status = HB_FILTER_OK; + } + *buf_in = NULL; + + return HB_FILTER_OK; +} +static void hb_qsv_filter_post_close( hb_filter_object_t * filter ){ + hb_filter_private_t * pv = filter->private_data; + + if ( !pv ) + { + return; + } + + free( pv ); + filter->private_data = NULL; +} + + +mfxStatus MFX_CDECL qsv_PluginInit(mfxHDL pthis, mfxCoreInterface *core){ + mfxStatus sts = MFX_ERR_NONE; + + if(core && pthis){ + qsv_filter_t *plugin = pthis; + plugin->core = core; + + plugin->pluginparam.MaxThreadNum = 1; + plugin->pluginparam.ThreadPolicy = MFX_THREADPOLICY_SERIAL; + } + else + sts = MFX_ERR_NULL_PTR; + + return sts; +} +mfxStatus MFX_CDECL qsv_PluginClose (mfxHDL pthis){ + mfxStatus sts = MFX_ERR_NONE; + return sts; +} +mfxStatus MFX_CDECL qsv_GetPluginParam(mfxHDL pthis, mfxPluginParam *par){ + mfxStatus sts = MFX_ERR_NONE; + + if(pthis){ + qsv_filter_t *plugin = pthis; + *par = plugin->pluginparam; + } + else + sts = MFX_ERR_NULL_PTR; + return sts; +} +mfxStatus MFX_CDECL qsv_Submit(mfxHDL pthis, const mfxHDL *in, mfxU32 in_num, const mfxHDL *out, mfxU32 out_num, mfxThreadTask *task){ + mfxStatus sts = MFX_ERR_NONE; + + qsv_filter_t *plugin = pthis; + + mfxFrameSurface1 *surface_in = (mfxFrameSurface1 *)in[0]; + mfxFrameSurface1 *surface_out = (mfxFrameSurface1 *)out[0]; + mfxFrameSurface1 *real_surface_in = surface_in; + mfxFrameSurface1 *real_surface_out = surface_out; + + sts = plugin->core->GetRealSurface(plugin->core->pthis, surface_in, &real_surface_in); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, MFX_ERR_MEMORY_ALLOC); + + sts = plugin->core->GetRealSurface(plugin->core->pthis, surface_out, &real_surface_out); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, MFX_ERR_MEMORY_ALLOC); + + int task_idx = get_free_task(plugin->tasks); + + if (task_idx == -1) + { + return MFX_WRN_DEVICE_BUSY; + } + + plugin->core->IncreaseReference(plugin->core->pthis, &(real_surface_in->Data)); + plugin->core->IncreaseReference(plugin->core->pthis, &(real_surface_out->Data)); + + // to preserve timing if other filters are used in-between + surface_out->Data.TimeStamp = surface_in->Data.TimeStamp; + surface_out->Data.FrameOrder = surface_in->Data.FrameOrder; + + qsv_filter_task_t *current_task = hb_list_item(plugin->tasks,task_idx); + current_task->in = real_surface_in; + current_task->out = real_surface_out; + current_task->busy = 1; + current_task->pv = plugin->pv; + + *task = (mfxThreadTask)current_task; + + return sts; +} +mfxStatus MFX_CDECL qsv_Execute(mfxHDL pthis, mfxThreadTask task, mfxU32 uid_p, mfxU32 uid_a){ + mfxStatus sts = MFX_ERR_NONE; + + qsv_filter_task_t *current_task = (qsv_filter_task_t *)task; + qsv_filter_t *plugin = pthis; + + sts = (current_task->processor.process)(current_task,0); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + sts = MFX_TASK_DONE; + return sts; +} +mfxStatus MFX_CDECL qsv_FreeResources(mfxHDL pthis, mfxThreadTask task, mfxStatus sts){ + + qsv_filter_t *plugin = pthis; + qsv_filter_task_t *current_task = (qsv_filter_task_t *)task; + + plugin->core->DecreaseReference(plugin->core->pthis, &(current_task->in->Data)); + plugin->core->DecreaseReference(plugin->core->pthis, &(current_task->out->Data)); + + current_task->busy = 0; + + hb_lock(plugin->pv->pre_busy.frame_completed_lock); + plugin->pv->pre_busy.frame_go = 1; + hb_cond_broadcast(plugin->pv->pre_busy.frame_completed); + hb_unlock(plugin->pv->pre_busy.frame_completed_lock); + + return MFX_ERR_NONE; +} + +mfxStatus plugin_init(qsv_filter_t* plugin, mfxVideoParam *param){ + mfxStatus sts = MFX_ERR_NONE; + + if(plugin->is_init_done) return sts; + + plugin->videoparam = param; + + mfxExtOpaqueSurfaceAlloc* plugin_opaque_alloc = NULL; + + plugin_opaque_alloc = (mfxExtOpaqueSurfaceAlloc*) get_ext_buffer(plugin->videoparam->ExtParam, + plugin->videoparam->NumExtParam, MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION); + + if(!plugin_opaque_alloc || !plugin_opaque_alloc->In.Surfaces || !plugin_opaque_alloc->Out.Surfaces) + return MFX_ERR_INVALID_VIDEO_PARAM; + + sts = plugin->core->MapOpaqueSurface(plugin->core->pthis, plugin_opaque_alloc->In.NumSurface, + plugin_opaque_alloc->In.Type, plugin_opaque_alloc->In.Surfaces); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + + sts = plugin->core->MapOpaqueSurface(plugin->core->pthis, plugin_opaque_alloc->Out.NumSurface, + plugin_opaque_alloc->Out.Type, plugin_opaque_alloc->Out.Surfaces); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + + plugin->tasks = hb_list_init(); + qsv_filter_task_t *task = calloc( 1, sizeof( qsv_filter_task_t )); + + task->processor.process = process_filter; + task->processor.alloc = &plugin->core->FrameAllocator; + task->processor.core = plugin->core; + + hb_list_add(plugin->tasks,task); + + plugin->is_init_done = 1; + + return sts; +} + +mfxStatus plugin_close(qsv_filter_t* plugin){ + int i = 0; + mfxStatus sts = MFX_ERR_NONE; + + if(!plugin->is_init_done) return sts; + + mfxExtOpaqueSurfaceAlloc* plugin_opaque_alloc = NULL; + + plugin_opaque_alloc = (mfxExtOpaqueSurfaceAlloc*) get_ext_buffer(plugin->videoparam->ExtParam, + plugin->videoparam->NumExtParam, MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION); + + if(!plugin_opaque_alloc || !plugin_opaque_alloc->In.Surfaces || !plugin_opaque_alloc->Out.Surfaces) + return MFX_ERR_INVALID_VIDEO_PARAM; + + sts = plugin->core->UnmapOpaqueSurface(plugin->core->pthis, plugin_opaque_alloc->In.NumSurface, + plugin_opaque_alloc->In.Type, plugin_opaque_alloc->In.Surfaces); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + + sts = plugin->core->UnmapOpaqueSurface(plugin->core->pthis, plugin_opaque_alloc->Out.NumSurface, + plugin_opaque_alloc->Out.Type, plugin_opaque_alloc->Out.Surfaces); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + if(plugin->tasks){ + for(i=hb_list_count(plugin->tasks);i>0;i--){ + qsv_filter_task_t *task = hb_list_item(plugin->tasks,i-1); + hb_list_rem(plugin->tasks,task); + free(task); + } + hb_list_close(&plugin->tasks); + } + + plugin->is_init_done = 0; + + return sts; +} + +mfxExtBuffer* get_ext_buffer(mfxExtBuffer** buffers, mfxU32 buffers_num, mfxU32 buffer_id){ + int i = 0; + if(!buffers) return 0; + for(i=0;i<buffers_num;i++){ + if(!buffers[i]) continue; + if(buffers[i]->BufferId == buffer_id) + return buffers[i]; + } + return 0; +} + +int get_free_task(hb_list_t* tasks){ + int ret = -1; + int i = 0; + for(i=0;i<hb_list_count(tasks);i++){ + qsv_filter_task_t* task = hb_list_item(tasks,i); + if(!task->busy){ + ret = i; + break; + } + } + return ret; +} + +mfxStatus lock_frame(mfxFrameAllocator *alloc,mfxFrameSurface1 *surface){ + mfxStatus sts = MFX_ERR_NONE; + // prevent double lock + if (surface->Data.Y != 0 && surface->Data.MemId !=0){ + return MFX_ERR_UNSUPPORTED; + } + // not allocated, therefore no lock + if (surface->Data.Y != 0){ + return MFX_ERR_NONE; + } + sts = alloc->Lock(alloc->pthis,surface->Data.MemId,&surface->Data); + return sts; +} + +mfxStatus unlock_frame(mfxFrameAllocator *alloc,mfxFrameSurface1 *surface){ + mfxStatus sts = MFX_ERR_NONE; + // not allocated + if (surface->Data.Y != 0 && surface->Data.MemId == 0){ + return MFX_ERR_NONE; + } + // not locked + if (surface->Data.Y == 0){ + return MFX_ERR_NONE; + } + sts = alloc->Unlock(alloc->pthis,surface->Data.MemId,&surface->Data); + return sts; +} + + +int process_filter(qsv_filter_task_t* task, void* params){ + mfxStatus sts = MFX_ERR_NONE; + + if (MFX_ERR_NONE != (sts = lock_frame(task->processor.alloc,task->in)))return sts; + if (MFX_ERR_NONE != (sts = lock_frame(task->processor.alloc,task->out))) + { + unlock_frame(task->processor.alloc,task->in); + return sts; + } + + qsv_nv12_to_yuv420(task->pv->sws_context_from_nv12,task->pv->pre.out, task->in, task->processor.core); + + // signal: input is prepared, converted from pipeline into internal buffer + hb_lock(task->pv->pre.frame_completed_lock); + task->pv->pre.frame_go = 1; + hb_cond_broadcast(task->pv->pre.frame_completed); + hb_unlock(task->pv->pre.frame_completed_lock); + + // wait: input is prepared, converted from pipeline into internal buffer + hb_lock(task->pv->post.frame_completed_lock); + while(!task->pv->post.frame_go){ + hb_cond_timedwait(task->pv->post.frame_completed,task->pv->post.frame_completed_lock,1000); + if(*task->pv->job->die) + break; + } + task->pv->post.frame_go = 0; + hb_unlock(task->pv->post.frame_completed_lock); + +// this is just a simple fun/test case +#if 0 + { + int i = 0; + char *cur_line; + char* luma = task->pv->post.in->plane[0].data; + int pitch = task->pv->post.in->plane[0].stride; + int h = task->pv->post.in->plane[0].height; + int w = task->pv->post.in->plane[0].width; + for (i = 0; i < h; i++){ + + cur_line = luma + i * pitch; + if(i>h/4 && i < 3*h/4 && i % 5 == 0 ) + memset(cur_line, 0 , w ); + } + } +#endif + + if(task->pv->post.in) + { + qsv_yuv420_to_nv12(task->pv->sws_context_to_nv12, task->out, task->pv->post.in); + } + + // signal: output is prepared, converted from internal buffer into pipeline + hb_lock(task->pv->post_busy.frame_completed_lock); + task->pv->post_busy.frame_go = 1; + hb_cond_broadcast(task->pv->post_busy.frame_completed); + hb_unlock(task->pv->post_busy.frame_completed_lock); + + unlock_frame(task->processor.alloc,task->in); + unlock_frame(task->processor.alloc,task->out); + + return sts; +} diff --git a/libhb/qsv_filter_pp.h b/libhb/qsv_filter_pp.h new file mode 100644 index 000000000..e70370321 --- /dev/null +++ b/libhb/qsv_filter_pp.h @@ -0,0 +1,114 @@ +/* ********************************************************************* *\ + +Copyright (C) 2013 Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +- Neither the name of Intel Corporation nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +\* ********************************************************************* */ + +#ifndef QSV_FILTER_PP_H +#define QSV_FILTER_PP_H + +#include "msdk/mfxplugin.h" + +extern hb_buffer_t *link_buf_list( hb_filter_private_t *pv ); + +struct qsv_filter_task_s; + +typedef struct{ + mfxFrameAllocator *alloc; + mfxStatus (*process)(struct qsv_filter_task_s*,void*); + mfxCoreInterface *core; +}qsv_filter_processor_t; + +typedef struct qsv_filter_task_s{ + mfxFrameSurface1 *in; + mfxFrameSurface1 *out; + int busy; + hb_filter_private_t *pv; + qsv_filter_processor_t processor; +} qsv_filter_task_t; + +typedef struct qsv_filter_private_s{ + + int is_init_done; + + mfxCoreInterface *core; + mfxVideoParam *videoparam; + mfxPluginParam pluginparam; + + hb_filter_private_t *pv; + + mfxPlugin plug; + hb_list_t *tasks; +} qsv_filter_t; + +typedef struct hb_qsv_sync_s{ + int frame_go; + int status; + hb_cond_t *frame_completed; + hb_lock_t *frame_completed_lock; + + hb_buffer_t *in; + hb_buffer_t *out; +} hb_qsv_sync_t; + +typedef struct hb_filter_private_s +{ + hb_job_t *job; + hb_list_t *list; + + hb_qsv_sync_t pre; + hb_qsv_sync_t pre_busy; + + hb_qsv_sync_t post; + hb_qsv_sync_t post_busy; + + av_qsv_space *vpp_space; + hb_list_t *qsv_user; + + struct SwsContext* sws_context_to_nv12; + struct SwsContext* sws_context_from_nv12; +} hb_filter_private_t_qsv; + +// methods to be called by Media SDK +mfxStatus MFX_CDECL qsv_PluginInit(mfxHDL pthis, mfxCoreInterface *core); +mfxStatus MFX_CDECL qsv_PluginClose (mfxHDL pthis); +mfxStatus MFX_CDECL qsv_GetPluginParam(mfxHDL pthis, mfxPluginParam *par); +mfxStatus MFX_CDECL qsv_Submit(mfxHDL pthis, const mfxHDL *in, mfxU32 in_num, const mfxHDL *out, mfxU32 out_num, mfxThreadTask *task); +mfxStatus MFX_CDECL qsv_Execute(mfxHDL pthis, mfxThreadTask task, mfxU32 uid_p, mfxU32 uid_a); +mfxStatus MFX_CDECL qsv_FreeResources(mfxHDL pthis, mfxThreadTask task, mfxStatus sts); + +// methods to be called by us +mfxStatus plugin_init(qsv_filter_t*,mfxVideoParam*); +mfxStatus plugin_close(qsv_filter_t*); + +//internal functions +mfxExtBuffer* get_ext_buffer(mfxExtBuffer**, mfxU32, mfxU32); +int get_free_task(hb_list_t*); +mfxStatus process_filter(qsv_filter_task_t*,void*); +mfxStatus lock_frame(mfxFrameAllocator *,mfxFrameSurface1*); +mfxStatus unlock_frame(mfxFrameAllocator *,mfxFrameSurface1*); + + +#endif //QSV_FILTER_PP_H diff --git a/libhb/qsv_memory.c b/libhb/qsv_memory.c new file mode 100644 index 000000000..f04c77a76 --- /dev/null +++ b/libhb/qsv_memory.c @@ -0,0 +1,120 @@ +/* ********************************************************************* *\ + +Copyright (C) 2013 Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +- Neither the name of Intel Corporation nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +\* ********************************************************************* */ + +#include "hb.h" +#include "hbffmpeg.h" +#include "qsv_memory.h" + +int qsv_nv12_to_yuv420(struct SwsContext* sws_context,hb_buffer_t* dst, mfxFrameSurface1* src, mfxCoreInterface *core){ + int ret = 0; + int i,j; + + int in_pitch = src->Data.Pitch; + int w = AV_QSV_ALIGN16(src->Info.Width); + int h = (MFX_PICSTRUCT_PROGRESSIVE == src->Info.PicStruct) ? AV_QSV_ALIGN16(src->Info.Height) : AV_QSV_ALIGN32(src->Info.Height); + uint8_t *in_luma = 0; + uint8_t *in_chroma = 0; + static int copyframe_in_use = 1; + + + mfxStatus sts = MFX_ERR_NONE; + mfxFrameSurface1 accel_dst; + + if (copyframe_in_use) + { + accel_dst.Info.FourCC = src->Info.FourCC; + accel_dst.Info.CropH = src->Info.CropH; + accel_dst.Info.CropW = src->Info.CropW; + accel_dst.Info.CropY = src->Info.CropY; + accel_dst.Info.CropX = src->Info.CropX; + accel_dst.Info.Width = w; + accel_dst.Info.Height = h; + accel_dst.Data.Pitch = src->Data.Pitch; + accel_dst.Data.Y = calloc( 1, in_pitch*h ); + accel_dst.Data.VU = calloc( 1, in_pitch*h/2 ); + + sts = core->CopyFrame(core->pthis, &accel_dst, src); + + if (sts < MFX_ERR_NONE) + { + free(accel_dst.Data.Y); + free(accel_dst.Data.VU); + copyframe_in_use = 0; + } + else + { + in_luma = accel_dst.Data.Y + accel_dst.Info.CropY * in_pitch + accel_dst.Info.CropX; + in_chroma = accel_dst.Data.VU + accel_dst.Info.CropY / 2 * in_pitch + accel_dst.Info.CropX; + } + } + + if (!copyframe_in_use) + { + in_luma = src->Data.Y + src->Info.CropY * in_pitch + src->Info.CropX; + in_chroma = src->Data.VU + src->Info.CropY / 2 * in_pitch + src->Info.CropX; + } + + hb_video_buffer_realloc( dst, w, h ); + + uint8_t *srcs[] = { in_luma, in_chroma }; + int srcs_stride[] = { in_pitch, in_pitch }; + + uint8_t *dsts[] = { dst->plane[0].data, dst->plane[1].data, dst->plane[2].data }; + int dsts_stride[] = { dst->plane[0].stride, dst->plane[1].stride, dst->plane[2].stride }; + + ret = sws_scale(sws_context, srcs, srcs_stride, 0, h, dsts, dsts_stride ); + + if (copyframe_in_use) + { + free(accel_dst.Data.Y); + free(accel_dst.Data.VU); + } + + return ret; +} + +int qsv_yuv420_to_nv12(struct SwsContext* sws_context,mfxFrameSurface1* dst, hb_buffer_t* src){ + int ret = 0; + + int w = src->plane[0].width; + int h = src->plane[0].height; + + int out_pitch = dst->Data.Pitch; + uint8_t *out_luma = dst->Data.Y; + uint8_t *out_chroma = dst->Data.VU; + + uint8_t *srcs[] = { src->plane[0].data, src->plane[1].data, src->plane[2].data }; + int srcs_stride[] = { src->plane[0].stride, src->plane[1].stride, src->plane[2].stride }; + + uint8_t *dsts[] = { out_luma, out_chroma }; + int dsts_stride[] = { out_pitch, out_pitch }; + + ret = sws_scale(sws_context, srcs, srcs_stride, 0, h, dsts, dsts_stride ); + + return ret; +} diff --git a/libhb/qsv_memory.h b/libhb/qsv_memory.h new file mode 100644 index 000000000..2d0f51208 --- /dev/null +++ b/libhb/qsv_memory.h @@ -0,0 +1,55 @@ +/* ********************************************************************* *\ + +Copyright (C) 2013 Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +- Neither the name of Intel Corporation nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +\* ********************************************************************* */ + +#ifndef QSV_MEMORY_H +#define QSV_MEMORY_H + +#include "libavcodec/qsv.h" +#include "msdk/mfxplugin.h" + +typedef struct{ + + struct{ + // for "planes" , Y and VU + uint8_t *data[2]; + int strides[2]; + } qsv_data; + + struct{ + // for each plane, Y U V + uint8_t *data[3]; + int strides[3]; + } data; + int width; + int height; +} qsv_memory_copy_t; + +int qsv_nv12_to_yuv420(struct SwsContext* sws_context,hb_buffer_t* dst, mfxFrameSurface1* src,mfxCoreInterface *core); +int qsv_yuv420_to_nv12(struct SwsContext* sws_context,mfxFrameSurface1* dst, hb_buffer_t* src); + +#endif // QSV_MEMORY_H |