diff options
author | maximd33 <[email protected]> | 2019-05-18 20:49:10 +0200 |
---|---|---|
committer | Scott <[email protected]> | 2019-05-26 19:28:57 +0100 |
commit | ec37ce222d4baaf13b0c5f5efedf350d5f28fc8f (patch) | |
tree | f168442ca71caa5d99929bca4e0d5c838df2f3ca /libhb | |
parent | acbeba350a2067b716c5867296e4d9396be37657 (diff) |
qsv: zero-copy re-implementation
Diffstat (limited to 'libhb')
-rw-r--r-- | libhb/decavcodec.c | 85 | ||||
-rw-r--r-- | libhb/enc_qsv.c | 443 | ||||
-rw-r--r-- | libhb/hb_json.c | 11 | ||||
-rw-r--r-- | libhb/internal.h | 1 | ||||
-rw-r--r-- | libhb/preset.c | 28 | ||||
-rw-r--r-- | libhb/qsv_common.c | 558 | ||||
-rw-r--r-- | libhb/qsv_common.h | 50 | ||||
-rw-r--r-- | libhb/qsv_libav.c | 5 |
8 files changed, 1111 insertions, 70 deletions
diff --git a/libhb/decavcodec.c b/libhb/decavcodec.c index 610f9ecfa..452847098 100644 --- a/libhb/decavcodec.c +++ b/libhb/decavcodec.c @@ -44,11 +44,14 @@ #include "libavfilter/avfilter.h" #include "libavfilter/buffersrc.h" #include "libavfilter/buffersink.h" +#include "libavutil/hwcontext_qsv.h" +#include "libavutil/hwcontext.h" #include "lang.h" #include "audio_resample.h" #if HB_PROJECT_FEATURE_QSV #include "qsv_common.h" +#include "qsv_libav.h" #endif static void compute_frame_duration( hb_work_private_t *pv ); @@ -377,6 +380,7 @@ static void closePrivData( hb_work_private_t ** ppv ) * form of communication between the two libmfx sessions). */ //if (!(pv->qsv.decode && pv->job != NULL && (pv->job->vcodec & HB_VCODEC_QSV_MASK))) + hb_qsv_uninit_dec(pv->context); #endif { hb_avcodec_free_context(&pv->context); @@ -973,13 +977,9 @@ static hb_buffer_t *copy_frame( hb_work_private_t *pv ) #if HB_PROJECT_FEATURE_QSV // no need to copy the frame data when decoding with QSV to opaque memory if (pv->qsv.decode && - pv->qsv.config.io_pattern == MFX_IOPATTERN_OUT_OPAQUE_MEMORY) + pv->qsv.config.io_pattern == MFX_IOPATTERN_OUT_VIDEO_MEMORY) { - out = hb_frame_buffer_init(pv->frame->format, pv->frame->width, pv->frame->height); - hb_avframe_set_video_buffer_flags(out, pv->frame, (AVRational){1,1}); - - out->qsv_details.qsv_atom = pv->frame->data[2]; - out->qsv_details.ctx = pv->job->qsv.ctx; + out = hb_qsv_copy_frame(pv->frame, pv->job->qsv.ctx); } else #endif @@ -1125,7 +1125,7 @@ int reinit_video_filters(hb_work_private_t * pv) #if HB_PROJECT_FEATURE_QSV if (pv->qsv.decode && - pv->qsv.config.io_pattern == MFX_IOPATTERN_OUT_OPAQUE_MEMORY) + pv->qsv.config.io_pattern == MFX_IOPATTERN_OUT_VIDEO_MEMORY) { // Can't use software filters when decoding with QSV opaque memory return 0; @@ -1349,17 +1349,6 @@ static int decodeFrame( hb_work_private_t * pv, packet_info_t * packet_info ) return 0; } -#if HB_PROJECT_FEATURE_QSV - if (pv->qsv.decode && - pv->qsv.config.io_pattern == MFX_IOPATTERN_OUT_OPAQUE_MEMORY && - pv->job->qsv.ctx == NULL && pv->video_codec_opened > 0) - { - // this is quite late, but we can't be certain that the QSV context is - // available until after we call avcodec_send_packet() at least once - pv->job->qsv.ctx = pv->context->priv_data; - } -#endif - do { ret = avcodec_receive_frame(pv->context, pv->frame); @@ -1407,24 +1396,42 @@ static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job ) { pv->qsv.codec_name = hb_qsv_decode_get_codec_name(w->codec_param); pv->qsv.config.io_pattern = MFX_IOPATTERN_OUT_SYSTEM_MEMORY; -#if 0 // TODO: re-implement QSV zerocopy path - hb_qsv_info_t *info = hb_qsv_info_get(job->vcodec); - if (info != NULL) - { - // setup the QSV configuration - pv->qsv.config.io_pattern = MFX_IOPATTERN_OUT_OPAQUE_MEMORY; - pv->qsv.config.impl_requested = info->implementation; - pv->qsv.config.async_depth = job->qsv.async_depth; - pv->qsv.config.sync_need = 0; - pv->qsv.config.usage_threaded = 1; - pv->qsv.config.additional_buffers = 64; // FIFO_LARGE - if (info->capabilities & HB_QSV_CAP_RATECONTROL_LA) + if(hb_qsv_full_path_is_enabled(job)) + { + hb_qsv_info_t *info = hb_qsv_info_get(job->vcodec); + if (info != NULL) { - // more surfaces may be needed for the lookahead - pv->qsv.config.additional_buffers = 160; + // setup the QSV configuration + pv->qsv.config.io_pattern = MFX_IOPATTERN_OUT_VIDEO_MEMORY; + pv->qsv.config.impl_requested = info->implementation; + pv->qsv.config.async_depth = job->qsv.async_depth; + pv->qsv.config.sync_need = 0; + pv->qsv.config.usage_threaded = 1; + pv->qsv.config.additional_buffers = 64; // FIFO_LARGE + if (info->capabilities & HB_QSV_CAP_RATECONTROL_LA) + { + // more surfaces may be needed for the lookahead + pv->qsv.config.additional_buffers = 160; + } + if(!pv->job->qsv.ctx) + { + pv->job->qsv.ctx = av_mallocz(sizeof(hb_qsv_context)); + if(!pv->job->qsv.ctx) + { + hb_error( "decavcodecvInit: qsv ctx alloc failed" ); + return 1; + } + hb_qsv_add_context_usage(pv->job->qsv.ctx, 0); + pv->job->qsv.ctx->dec_space = av_mallocz(sizeof(hb_qsv_space)); + if(!pv->job->qsv.ctx->dec_space) + { + hb_error( "decavcodecvInit: dec_space alloc failed" ); + return 1; + } + pv->job->qsv.ctx->dec_space->is_init_done = 1; + } } } -#endif // QSV zerocopy path } #endif @@ -1463,14 +1470,16 @@ static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job ) #if HB_PROJECT_FEATURE_QSV if (pv->qsv.decode && - pv->qsv.config.io_pattern == MFX_IOPATTERN_OUT_OPAQUE_MEMORY) + pv->qsv.config.io_pattern == MFX_IOPATTERN_OUT_VIDEO_MEMORY) { - // set the QSV configuration before opening the decoder - pv->context->hwaccel_context = &pv->qsv.config; + // assign callbacks + pv->context->get_format = hb_qsv_get_format; + pv->context->get_buffer2 = hb_qsv_get_buffer; + pv->context->hwaccel_context = 0; } #endif - // Set encoder opts... + // Set encoder opts AVDictionary * av_opts = NULL; av_dict_set( &av_opts, "refcounted_frames", "1", 0 ); if (pv->title->flags & HBTF_NO_IDR) @@ -1596,7 +1605,7 @@ static int decodePacket( hb_work_object_t * w ) #if HB_PROJECT_FEATURE_QSV if (pv->qsv.decode && - pv->qsv.config.io_pattern == MFX_IOPATTERN_OUT_OPAQUE_MEMORY) + pv->qsv.config.io_pattern == MFX_IOPATTERN_OUT_VIDEO_MEMORY) { // set the QSV configuration before opening the decoder pv->context->hwaccel_context = &pv->qsv.config; diff --git a/libhb/enc_qsv.c b/libhb/enc_qsv.c index 55478cf7d..f7f84cb38 100644 --- a/libhb/enc_qsv.c +++ b/libhb/enc_qsv.c @@ -37,6 +37,13 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "h264_common.h" #include "h265_common.h" +#include "libavutil/hwcontext_qsv.h" +#include "libavutil/hwcontext.h" +#include <mfx/mfxvideo.h> + +extern AVBufferRef *hb_hw_device_ctx; +EncQSVFramesContext hb_enc_qsv_frames_ctx; + /* * The frame info struct remembers information about each frame across calls to * the encoder. Since frames are uniquely identified by their timestamp, we use @@ -109,6 +116,20 @@ struct hb_work_private_s hb_list_t * loaded_plugins; }; +int hb_qsv_find_surface_idx(EncQSVFramesContext *ctx, mfxMemId MemId) +{ + if(ctx != NULL) + { + int i; + for (i = 0; i < ctx->nb_mids; i++) { + QSVMid *mid = &ctx->mids[i]; + if (mid->handle == MemId) + return i; + } + } + return -1; +} + static void hb_qsv_add_new_dts(hb_list_t *list, int64_t new_dts) { if (list != NULL) @@ -452,6 +473,269 @@ end: return ret; } +static void mids_buf_free(void *opaque, uint8_t *data) +{ + AVBufferRef *hw_frames_ref = opaque; + av_buffer_unref(&hw_frames_ref); + av_freep(&data); +} + +static enum AVPixelFormat qsv_map_fourcc(uint32_t fourcc) +{ + switch (fourcc) { + case MFX_FOURCC_NV12: return AV_PIX_FMT_NV12; + case MFX_FOURCC_P010: return AV_PIX_FMT_P010; + case MFX_FOURCC_P8: return AV_PIX_FMT_PAL8; + } + return AV_PIX_FMT_NONE; +} + +AVBufferRef *hb_qsv_create_mids(AVBufferRef *hw_frames_ref) +{ + AVHWFramesContext *frames_ctx = (AVHWFramesContext*)hw_frames_ref->data; + AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx; + int nb_surfaces = frames_hwctx->nb_surfaces; + + AVBufferRef *mids_buf, *hw_frames_ref1; + QSVMid *mids; + int i; + + hw_frames_ref1 = av_buffer_ref(hw_frames_ref); + if (!hw_frames_ref1) + return NULL; + + mids = av_mallocz_array(nb_surfaces, sizeof(*mids)); + if (!mids) { + av_buffer_unref(&hw_frames_ref1); + return NULL; + } + + mids_buf = av_buffer_create((uint8_t*)mids, nb_surfaces * sizeof(*mids), + mids_buf_free, hw_frames_ref1, 0); + if (!mids_buf) { + av_buffer_unref(&hw_frames_ref1); + av_freep(&mids); + return NULL; + } + + for (i = 0; i < nb_surfaces; i++) { + QSVMid *mid = &mids[i]; + mid->handle = frames_hwctx->surfaces[i].Data.MemId; + mid->hw_frames_ref = hw_frames_ref1; + } + + return mids_buf; +} + +static int qsv_setup_mids(mfxFrameAllocResponse *resp, AVBufferRef *hw_frames_ref, + AVBufferRef *mids_buf) +{ + AVHWFramesContext *frames_ctx = (AVHWFramesContext*)hw_frames_ref->data; + AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx; + QSVMid *mids = (QSVMid*)mids_buf->data; + int nb_surfaces = frames_hwctx->nb_surfaces; + int i; + + // the allocated size of the array is two larger than the number of + // surfaces, we store the references to the frames context and the + // QSVMid array there + resp->mids = av_mallocz_array(nb_surfaces + 2, sizeof(*resp->mids)); + if (!resp->mids) + return AVERROR(ENOMEM); + + for (i = 0; i < nb_surfaces; i++) + resp->mids[i] = &mids[i]; + resp->NumFrameActual = nb_surfaces; + + resp->mids[resp->NumFrameActual] = (mfxMemId)av_buffer_ref(hw_frames_ref); + if (!resp->mids[resp->NumFrameActual]) { + av_freep(&resp->mids); + return AVERROR(ENOMEM); + } + + resp->mids[resp->NumFrameActual + 1] = av_buffer_ref(mids_buf); + if (!resp->mids[resp->NumFrameActual + 1]) { + av_buffer_unref((AVBufferRef**)&resp->mids[resp->NumFrameActual]); + av_freep(&resp->mids); + return AVERROR(ENOMEM); + } + + return 0; +} + +static mfxStatus hb_qsv_frame_alloc(mfxHDL pthis, mfxFrameAllocRequest *req, + mfxFrameAllocResponse *resp) +{ + EncQSVFramesContext *ctx = pthis; + int ret; + + /* this should only be called from an encoder or decoder and + * only allocates video memory frames */ + if (!(req->Type & (MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET | + MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET)) || + !(req->Type & (MFX_MEMTYPE_FROM_DECODE | MFX_MEMTYPE_FROM_ENCODE))) + return MFX_ERR_UNSUPPORTED; + + if (req->Type & MFX_MEMTYPE_EXTERNAL_FRAME) { + /* external frames -- fill from the caller-supplied frames context */ + AVHWFramesContext *frames_ctx = (AVHWFramesContext*)ctx->hw_frames_ctx->data; + AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx; + mfxFrameInfo *i = &req->Info; + mfxFrameInfo *i1 = &frames_hwctx->surfaces[0].Info; + + if (i->Width > i1->Width || i->Height > i1->Height || + i->FourCC != i1->FourCC || i->ChromaFormat != i1->ChromaFormat) { + hb_error("Mismatching surface properties in an " + "allocation request: %dx%d %d %d vs %dx%d %d %d\n", + i->Width, i->Height, i->FourCC, i->ChromaFormat, + i1->Width, i1->Height, i1->FourCC, i1->ChromaFormat); + return MFX_ERR_UNSUPPORTED; + } + + ret = qsv_setup_mids(resp, ctx->hw_frames_ctx, ctx->mids_buf); + if (ret < 0) { + hb_error("Error filling an external frame allocation request\n"); + return MFX_ERR_MEMORY_ALLOC; + } + } else if (req->Type & MFX_MEMTYPE_INTERNAL_FRAME) { + /* internal frames -- allocate a new hw frames context */ + AVHWFramesContext *ext_frames_ctx = (AVHWFramesContext*)ctx->hw_frames_ctx->data; + mfxFrameInfo *i = &req->Info; + + AVBufferRef *frames_ref, *mids_buf; + AVHWFramesContext *frames_ctx; + AVQSVFramesContext *frames_hwctx; + + frames_ref = av_hwframe_ctx_alloc(ext_frames_ctx->device_ref); + if (!frames_ref) + return MFX_ERR_MEMORY_ALLOC; + + frames_ctx = (AVHWFramesContext*)frames_ref->data; + frames_hwctx = frames_ctx->hwctx; + + frames_ctx->format = AV_PIX_FMT_QSV; + frames_ctx->sw_format = qsv_map_fourcc(i->FourCC); + frames_ctx->width = i->Width; + frames_ctx->height = i->Height; + frames_ctx->initial_pool_size = req->NumFrameSuggested; + + frames_hwctx->frame_type = req->Type; + + ret = av_hwframe_ctx_init(frames_ref); + if (ret < 0) { + hb_error("Error initializing a frames context for an internal frame " + "allocation request\n"); + av_buffer_unref(&frames_ref); + return MFX_ERR_MEMORY_ALLOC; + } + + mids_buf = hb_qsv_create_mids(frames_ref); + if (!mids_buf) { + av_buffer_unref(&frames_ref); + return MFX_ERR_MEMORY_ALLOC; + } + + ret = qsv_setup_mids(resp, frames_ref, mids_buf); + av_buffer_unref(&mids_buf); + av_buffer_unref(&frames_ref); + if (ret < 0) { + hb_error("Error filling an internal frame allocation request\n"); + return MFX_ERR_MEMORY_ALLOC; + } + } else { + return MFX_ERR_UNSUPPORTED; + } + + return MFX_ERR_NONE; +} + +static mfxStatus hb_qsv_frame_free(mfxHDL pthis, mfxFrameAllocResponse *resp) +{ + av_buffer_unref((AVBufferRef**)&resp->mids[resp->NumFrameActual]); + av_buffer_unref((AVBufferRef**)&resp->mids[resp->NumFrameActual + 1]); + av_freep(&resp->mids); + return MFX_ERR_NONE; +} + +static mfxStatus hb_qsv_frame_lock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) +{ + QSVMid *qsv_mid = mid; + AVHWFramesContext *hw_frames_ctx = (AVHWFramesContext*)qsv_mid->hw_frames_ref->data; + AVQSVFramesContext *hw_frames_hwctx = hw_frames_ctx->hwctx; + int ret; + + if (qsv_mid->locked_frame) + return MFX_ERR_UNDEFINED_BEHAVIOR; + + /* Allocate a system memory frame that will hold the mapped data. */ + qsv_mid->locked_frame = av_frame_alloc(); + if (!qsv_mid->locked_frame) + return MFX_ERR_MEMORY_ALLOC; + qsv_mid->locked_frame->format = hw_frames_ctx->sw_format; + + /* wrap the provided handle in a hwaccel AVFrame */ + qsv_mid->hw_frame = av_frame_alloc(); + if (!qsv_mid->hw_frame) + goto fail; + + qsv_mid->hw_frame->data[3] = (uint8_t*)&qsv_mid->surf; + qsv_mid->hw_frame->format = AV_PIX_FMT_QSV; + + // doesn't really matter what buffer is used here + qsv_mid->hw_frame->buf[0] = av_buffer_alloc(1); + if (!qsv_mid->hw_frame->buf[0]) + goto fail; + + qsv_mid->hw_frame->width = hw_frames_ctx->width; + qsv_mid->hw_frame->height = hw_frames_ctx->height; + + qsv_mid->hw_frame->hw_frames_ctx = av_buffer_ref(qsv_mid->hw_frames_ref); + if (!qsv_mid->hw_frame->hw_frames_ctx) + goto fail; + + qsv_mid->surf.Info = hw_frames_hwctx->surfaces[0].Info; + qsv_mid->surf.Data.MemId = qsv_mid->handle; + + /* map the data to the system memory */ + ret = av_hwframe_map(qsv_mid->locked_frame, qsv_mid->hw_frame, + AV_HWFRAME_MAP_DIRECT); + if (ret < 0) + goto fail; + + ptr->Pitch = qsv_mid->locked_frame->linesize[0]; + ptr->Y = qsv_mid->locked_frame->data[0]; + ptr->U = qsv_mid->locked_frame->data[1]; + ptr->V = qsv_mid->locked_frame->data[1] + 1; + + return MFX_ERR_NONE; +fail: + av_frame_free(&qsv_mid->hw_frame); + av_frame_free(&qsv_mid->locked_frame); + return MFX_ERR_MEMORY_ALLOC; +} + +static mfxStatus hb_qsv_frame_unlock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) +{ + QSVMid *qsv_mid = mid; + + av_frame_free(&qsv_mid->locked_frame); + av_frame_free(&qsv_mid->hw_frame); + + return MFX_ERR_NONE; +} + +static mfxStatus hb_qsv_frame_get_hdl(mfxHDL pthis, mfxMemId mid, mfxHDL *hdl) +{ + QSVMid *qsv_mid = (QSVMid*)mid; + *hdl = qsv_mid->handle; + + return MFX_ERR_NONE; +} + +#define QSV_RUNTIME_VERSION_ATLEAST(MFX_VERSION, MAJOR, MINOR) \ + (MFX_VERSION.Major > (MAJOR)) || \ + (MFX_VERSION.Major == (MAJOR) && MFX_VERSION.Minor >= (MINOR)) + int qsv_enc_init(hb_work_private_t *pv) { hb_qsv_context *qsv = pv->job->qsv.ctx; @@ -488,6 +772,62 @@ int qsv_enc_init(hb_work_private_t *pv) // re-use the session from encqsvInit qsv->mfx_session = pv->mfx_session; } + else + { + mfxStatus err; + //qsv->mfx_session = pv->mfx_session; + AVHWFramesContext *frames_ctx = (AVHWFramesContext*)hb_enc_qsv_frames_ctx.hw_frames_ctx->data; + AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx; + + mfxVersion ver; + mfxIMPL impl; + mfxHDL handle = NULL; + mfxHandleType handle_type; + + static const mfxHandleType handle_types[] = { + MFX_HANDLE_VA_DISPLAY, + MFX_HANDLE_D3D9_DEVICE_MANAGER, + MFX_HANDLE_D3D11_DEVICE, + }; + + int i; + + AVHWDeviceContext *device_ctx = (AVHWDeviceContext*)hb_hw_device_ctx->data; + AVQSVDeviceContext *device_hwctx = device_ctx->hwctx; + mfxSession parent_session = device_hwctx->session; + + err = MFXQueryIMPL(parent_session, &impl); + if (err != MFX_ERR_NONE) + { + hb_error("Error querying the session attributes"); + return -1; + } + + err = MFXQueryVersion(parent_session, &ver); + if (err != MFX_ERR_NONE) + { + hb_error("Error querying the session attributes"); + return -1; + } + + // reuse parent session + qsv->mfx_session = parent_session; + mfxFrameAllocator frame_allocator = { + .pthis = &hb_enc_qsv_frames_ctx, + .Alloc = hb_qsv_frame_alloc, + .Lock = hb_qsv_frame_lock, + .Unlock = hb_qsv_frame_unlock, + .GetHDL = hb_qsv_frame_get_hdl, + .Free = hb_qsv_frame_free, + }; + + err = MFXVideoCORE_SetFrameAllocator(qsv->mfx_session, &frame_allocator); + if (err != MFX_ERR_NONE) + { + hb_log("encqsvInit: MFXVideoCORE_SetFrameAllocator error %d", err); + return -1; + } + } qsv->enc_space = qsv_encode = &pv->enc_space; } @@ -591,7 +931,7 @@ int qsv_enc_init(hb_work_private_t *pv) // setup surface allocation pv->param.videoParam->IOPattern = (pv->is_sys_mem ? MFX_IOPATTERN_IN_SYSTEM_MEMORY : - MFX_IOPATTERN_IN_OPAQUE_MEMORY); + MFX_IOPATTERN_IN_VIDEO_MEMORY); memset(&qsv_encode->request, 0, sizeof(mfxFrameAllocRequest) * 2); sts = MFXVideoENCODE_QueryIOSurf(qsv->mfx_session, pv->param.videoParam, @@ -629,21 +969,20 @@ int qsv_enc_init(hb_work_private_t *pv) } else { - hb_qsv_space *in_space = qsv->dec_space; - if (pv->is_vpp_present) + qsv_encode->surface_num = FFMIN(qsv_encode->request[0].NumFrameSuggested + + pv->max_async_depth, HB_QSV_SURFACE_NUM); + if (qsv_encode->surface_num <= 0) { - // we get our input from VPP instead - in_space = hb_qsv_list_item(qsv->vpp_space, - hb_qsv_list_count(qsv->vpp_space) - 1); + qsv_encode->surface_num = HB_QSV_SURFACE_NUM; } - // 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; - pv->param.videoParam->ExtParam[pv->param.videoParam->NumExtParam++] = (mfxExtBuffer*)&qsv_encode->ext_opaque_alloc; + // Use only when VPP will be fixed + // hb_qsv_space *in_space = qsv->dec_space; + // if (pv->is_vpp_present) + // { + // // we get our input from VPP instead + // in_space = hb_qsv_list_item(qsv->vpp_space, + // hb_qsv_list_count(qsv->vpp_space) - 1); + // } } // allocate sync points @@ -667,7 +1006,6 @@ int qsv_enc_init(hb_work_private_t *pv) *job->die = 1; return -1; } - qsv_encode->is_init_done = 1; // query and log actual implementation details if ((MFXQueryIMPL (qsv->mfx_session, &impl) == MFX_ERR_NONE) && @@ -681,6 +1019,7 @@ int qsv_enc_init(hb_work_private_t *pv) hb_log("qsv_enc_init: MFXQueryIMPL/MFXQueryVersion failure"); } + qsv_encode->is_init_done = 1; pv->init_done = 1; return 0; } @@ -695,7 +1034,7 @@ 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->is_sys_mem = 1; // TODO: re-implement QSV VPP filtering support + pv->is_sys_mem = hb_qsv_full_path_is_enabled(job) ? 0 : 1; // TODO: re-implement QSV VPP filtering support pv->job = job; pv->qsv_info = hb_qsv_info_get(job->vcodec); pv->delayed_processing = hb_list_init(); @@ -1494,6 +1833,8 @@ void encqsvClose(hb_work_object_t *w) av_freep(&qsv_ctx); } } + + hb_qsv_uninit_enc(); } if (pv != NULL) @@ -1756,17 +2097,26 @@ static int qsv_enc_work(hb_work_private_t *pv, NULL, surface, task->bs, qsv_enc_space->p_syncp[sync_idx]->p_sync); - if (sts == MFX_ERR_MORE_DATA || (sts >= MFX_ERR_NONE && - sts != MFX_WRN_DEVICE_BUSY)) + if (sts == MFX_ERR_MORE_DATA) { - if (surface != NULL && !pv->is_sys_mem) + if(surface) { - ff_qsv_atomic_dec(&surface->Data.Locked); + // release surface + if(!pv->is_sys_mem) + { + QSVMid *mid = surface->Data.MemId; + if (hb_enc_qsv_frames_ctx.mids) { + int ret = hb_qsv_find_surface_idx(&hb_enc_qsv_frames_ctx, mid->handle); + if (ret < 0) + { + hb_error("encqsv: Invalid surface to release %d", ret); + return -1; + } + ff_qsv_atomic_dec(&hb_enc_qsv_frames_ctx.pool[ret]); + } + } } - } - if (sts == MFX_ERR_MORE_DATA) - { if (qsv_atom != NULL) { hb_list_add(pv->delayed_processing, qsv_atom); @@ -1838,6 +2188,25 @@ static int qsv_enc_work(hb_work_private_t *pv, /* perform a sync operation to get the output bitstream */ hb_qsv_wait_on_sync(qsv_ctx, task->stage); + // release surface + if(!pv->is_sys_mem) + { + mfxFrameSurface1 *surface_to_release = task->stage->in.p_surface; + if(surface_to_release) + { + QSVMid *mid = surface_to_release->Data.MemId; + if (hb_enc_qsv_frames_ctx.mids) { + int ret = hb_qsv_find_surface_idx(&hb_enc_qsv_frames_ctx, mid->handle); + if (ret < 0) + { + hb_error("encqsv: Invalid surface to release %d", ret); + return -1; + } + ff_qsv_atomic_dec(&hb_enc_qsv_frames_ctx.pool[ret]); + } + } + } + if (task->bs->DataLength > 0) { hb_qsv_list *pipe = hb_qsv_pipe_by_stage(qsv_ctx->pipes, @@ -1914,8 +2283,32 @@ int encqsvWork(hb_work_object_t *w, hb_buffer_t **buf_in, hb_buffer_t **buf_out) } else { - qsv_atom = in->qsv_details.qsv_atom; - surface = hb_qsv_get_last_stage(qsv_atom)->out.p_surface; +#if HB_PROJECT_FEATURE_QSV + if(in->qsv_details.frame) + { + surface = ((mfxFrameSurface1*)in->qsv_details.frame->data[3]); + } + else + { + // Create black buffer in the begining of the encoding, usually first 2 frames + QSVMid *mid = NULL; + hb_qsv_get_free_surface_from_pool(&mid, &surface, HB_POOL_SURFACE_SIZE); + } + + if(surface) + { + if (hb_enc_qsv_frames_ctx.mids) { + int ret = hb_qsv_find_surface_idx(&hb_enc_qsv_frames_ctx, surface->Data.MemId); + if (ret < 0) + return ret; + surface->Data.MemId = &hb_enc_qsv_frames_ctx.mids[ret]; + } + } + else + { + goto fail; + } +#endif // At this point, enc_qsv takes ownership of the QSV resources // in the 'in' buffer. in->qsv_details.qsv_atom = NULL; diff --git a/libhb/hb_json.c b/libhb/hb_json.c index 7433089f0..d3d190df4 100644 --- a/libhb/hb_json.c +++ b/libhb/hb_json.c @@ -1366,7 +1366,16 @@ hb_job_t* hb_dict_to_job( hb_handle_t * h, hb_dict_t *dict ) { hb_filter_object_t *filter; filter = hb_filter_init(filter_id); - hb_add_filter_dict(job, filter, filter_settings); +#if HB_PROJECT_FEATURE_QSV + if(hb_qsv_full_path_is_enabled(job)) + { + hb_log("Filter with ID=%d is disabled", filter_id); + } + else +#endif + { + hb_add_filter_dict(job, filter, filter_settings); + } } } } diff --git a/libhb/internal.h b/libhb/internal.h index 49e081169..26a15d7e4 100644 --- a/libhb/internal.h +++ b/libhb/internal.h @@ -151,6 +151,7 @@ struct hb_buffer_s struct qsv { void * qsv_atom; + AVFrame * frame; void * filter_details; hb_qsv_context * ctx; } qsv_details; diff --git a/libhb/preset.c b/libhb/preset.c index 4eb45254c..b60a0ff7e 100644 --- a/libhb/preset.c +++ b/libhb/preset.c @@ -12,6 +12,10 @@ #include "hb_dict.h" #include "plist.h" +#if HB_PROJECT_FEATURE_QSV +#include "qsv_common.h" +#endif + #if defined(SYS_LINUX) #define HB_PRESET_PLIST_FILE "ghb/presets" #define HB_PRESET_JSON_FILE "ghb/presets.json" @@ -1601,8 +1605,16 @@ int hb_preset_apply_filters(const hb_dict_t *preset, hb_dict_t *job_dict) filter_dict = hb_dict_init(); hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_VFR)); hb_dict_set(filter_dict, "Settings", filter_settings); - hb_add_filter2(filter_list, filter_dict); - +#if HB_PROJECT_FEATURE_QSV + if(hb_qsv_preset_is_zero_copy_enabled(job_dict)) + { + hb_log("HB_FILTER_VFR filter is disabled"); + } + else +#endif + { + hb_add_filter2(filter_list, filter_dict); + } return 0; } @@ -1977,8 +1989,16 @@ int hb_preset_apply_title(hb_handle_t *h, int title_index, filter_dict = hb_dict_init(); hb_dict_set(filter_dict, "ID", hb_value_int(HB_FILTER_CROP_SCALE)); hb_dict_set(filter_dict, "Settings", filter_settings); - hb_add_filter2(filter_list, filter_dict); - +#if HB_PROJECT_FEATURE_QSV + if(hb_qsv_preset_is_zero_copy_enabled(job_dict)) + { + hb_log("HB_FILTER_CROP_SCALE filter is disabled"); + } + else +#endif + { + hb_add_filter2(filter_list, filter_dict); + } // Audio settings if (hb_preset_job_add_audio(h, title_index, preset, job_dict) != 0) { diff --git a/libhb/qsv_common.c b/libhb/qsv_common.c index a4322bcd6..b13fcbd96 100644 --- a/libhb/qsv_common.c +++ b/libhb/qsv_common.c @@ -991,6 +991,20 @@ int hb_qsv_decode_is_enabled(hb_job_t *job) (job->title->video_decode_support & HB_DECODE_SUPPORT_QSV)); } +static int hb_dxva2_device_check(); + +int hb_qsv_full_path_is_enabled(hb_job_t *job) +{ + static int device_check_completed = 0; + static int device_check_succeded = 0; + if(!device_check_completed) + { + device_check_succeded = (hb_dxva2_device_check() == 0) ? 1 : 0; + device_check_completed = 1; + } + return (hb_qsv_decode_is_enabled(job) && hb_qsv_info_get(job->vcodec) && device_check_succeded); +} + int hb_qsv_copyframe_is_slow(int encoder) { hb_qsv_info_t *info = hb_qsv_info_get(encoder); @@ -2265,6 +2279,550 @@ void hb_qsv_force_workarounds() #undef FORCE_WORKAROUNDS } +#include "hb.h" +#include "hbffmpeg.h" +#include "libavfilter/avfilter.h" +#include "libavfilter/buffersrc.h" +#include "libavfilter/buffersink.h" +#include "libavutil/hwcontext_qsv.h" +#include "libavutil/hwcontext.h" +#include "lang.h" +#include "audio_resample.h" + +AVBufferRef *enc_hw_frames_ctx = NULL; +extern EncQSVFramesContext hb_enc_qsv_frames_ctx; +AVBufferRef *hb_hw_device_ctx = NULL; +char *qsv_device = NULL; +mfxHDL device_manager_handle = NULL; + +#if defined(_WIN32) || defined(__MINGW32__) +// Direct X +#include <d3d9.h> +#include <dxva2api.h> + +typedef IDirect3D9* WINAPI pDirect3DCreate9(UINT); +typedef HRESULT WINAPI pDirect3DCreate9Ex(UINT, IDirect3D9Ex **); + +static int hb_dxva2_device_create9(HMODULE d3dlib, UINT adapter, IDirect3D9 **d3d9_out) +{ + pDirect3DCreate9 *createD3D = (pDirect3DCreate9 *)hb_dlsym(d3dlib, "Direct3DCreate9"); + if (!createD3D) { + hb_error("Failed to locate Direct3DCreate9"); + return -1; + } + + IDirect3D9 *d3d9 = createD3D(D3D_SDK_VERSION); + if (!d3d9) { + hb_error("Failed to create IDirect3D object"); + return -1; + } + *d3d9_out = d3d9; + return 0; +} + +static int hb_dxva2_device_create9ex(HMODULE d3dlib, UINT adapter, IDirect3D9 **d3d9_out) +{ + IDirect3D9Ex *d3d9ex = NULL; + HRESULT hr; + pDirect3DCreate9Ex *createD3DEx = (pDirect3DCreate9Ex *)hb_dlsym(d3dlib, "Direct3DCreate9Ex"); + if (!createD3DEx) + { + hb_error("Failed to locate Direct3DCreate9Ex"); + return -1; + } + + hr = createD3DEx(D3D_SDK_VERSION, &d3d9ex); + if (FAILED(hr)) + { + hb_error("Failed to create IDirect3DEx object"); + return -1; + } + *d3d9_out = (IDirect3D9 *)d3d9ex; + return 0; +} + +static int hb_dxva2_device_check() +{ + HMODULE d3dlib = NULL; + IDirect3D9 *d3d9 = NULL; + D3DADAPTER_IDENTIFIER9 identifier; + D3DADAPTER_IDENTIFIER9 *d3dai = &identifier; + UINT adapter = D3DADAPTER_DEFAULT; + int err = 0; + + d3dlib = hb_dlopen("d3d9.dll"); + if (!d3dlib) + { + hb_error("Failed to load D3D9 library"); + return -1; + } + + if (hb_dxva2_device_create9ex(d3dlib, adapter, &d3d9) < 0) + { + // Retry with "classic" d3d9 + err = hb_dxva2_device_create9(d3dlib, adapter, &d3d9); + if (err < 0) + { + err = -1; + goto clean_up; + } + } + + UINT adapter_count = IDirect3D9_GetAdapterCount(d3d9); + hb_log("D3D9: %d adapters available", adapter_count); + if (FAILED(IDirect3D9_GetAdapterIdentifier(d3d9, D3DADAPTER_DEFAULT, 0, d3dai))) + { + hb_error("Failed to get Direct3D adapter identifier"); + err = -1; + goto clean_up; + } + + unsigned intel_id = 0x8086; + if(d3dai) + { + if(d3dai->VendorId != intel_id) + { + hb_log("D3D9: Intel adapter is required for zero-copy QSV path"); + err = -1; + goto clean_up; + } + } + err = 0; + +clean_up: + if (d3d9) + IDirect3D9_Release(d3d9); + + if (d3dlib) + hb_dlclose(d3dlib); + + return err; +} + +static HRESULT lock_device( + IDirect3DDeviceManager9 *pDeviceManager, + BOOL fBlock, + IDirect3DDevice9 **ppDevice, // Receives a pointer to the device. + HANDLE *pHandle // Receives a device handle. + ) +{ + *pHandle = NULL; + *ppDevice = NULL; + + HANDLE hDevice = 0; + + HRESULT hr = pDeviceManager->lpVtbl->OpenDeviceHandle(pDeviceManager, &hDevice); + + if (SUCCEEDED(hr)) + { + hr = pDeviceManager->lpVtbl->LockDevice(pDeviceManager, hDevice, ppDevice, fBlock); + } + + if (hr == DXVA2_E_NEW_VIDEO_DEVICE) + { + // Invalid device handle. Try to open a new device handle. + hr = pDeviceManager->lpVtbl->CloseDeviceHandle(pDeviceManager, hDevice); + + if (SUCCEEDED(hr)) + { + hr = pDeviceManager->lpVtbl->OpenDeviceHandle(pDeviceManager, &hDevice); + } + + // Try to lock the device again. + if (SUCCEEDED(hr)) + { + hr = pDeviceManager->lpVtbl->LockDevice(pDeviceManager, hDevice, ppDevice, TRUE); + } + } + + if (SUCCEEDED(hr)) + { + *pHandle = hDevice; + } + return hr; +} + +static HRESULT unlock_device( + IDirect3DDeviceManager9 *pDeviceManager, + HANDLE handle // Receives a device handle. + ) +{ + HRESULT hr = pDeviceManager->lpVtbl->UnlockDevice(pDeviceManager, handle, 0); + if (SUCCEEDED(hr)) + { + hr = pDeviceManager->lpVtbl->CloseDeviceHandle(pDeviceManager, handle); + } + return hr; +} + +void hb_qsv_get_free_surface_from_pool(QSVMid **out_mid, mfxFrameSurface1 **out_surface, int pool_size) +{ + QSVMid *mid = NULL; + mfxFrameSurface1 *output_surface = NULL; + + AVHWFramesContext *frames_ctx = (AVHWFramesContext*)hb_enc_qsv_frames_ctx.hw_frames_ctx->data; + AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx; + + // find the first available surface in the pool + int count = 0; + while( (mid == 0) && (output_surface == 0) ) + { + if(count > 30) + { + hb_qsv_sleep(10); // prevent hang when all surfaces all used + } + + for(int i = 0; i < pool_size; i++) + { + if(hb_enc_qsv_frames_ctx.pool[i] == 0) + { + mid = &hb_enc_qsv_frames_ctx.mids[i]; + output_surface = &frames_hwctx->surfaces[i]; + if(output_surface->Data.Locked == 0) + { + ff_qsv_atomic_inc(&hb_enc_qsv_frames_ctx.pool[i]); + break; + } + else + { + mid = 0; + output_surface = 0; + } + } + } + + count++; + } + + *out_mid = mid; + *out_surface = output_surface; +} + +hb_buffer_t* hb_qsv_copy_frame(AVFrame *frame, hb_qsv_context *qsv_ctx) +{ + hb_buffer_t *out; + out = hb_frame_buffer_init(frame->format, frame->width, frame->height); + hb_avframe_set_video_buffer_flags(out, frame, (AVRational){1,1}); + + // alloc new frame + out->qsv_details.frame = av_frame_alloc(); + if (!out->qsv_details.frame) { + return out; + } + + // copy content of input frame + av_frame_copy(out->qsv_details.frame, frame); + // but no copy the sufrace pointer, it will be added later from the pool + out->qsv_details.frame->data[3] = 0; + + QSVMid *mid = NULL; + mfxFrameSurface1* output_surface = NULL; + AVHWFramesContext *frames_ctx = (AVHWFramesContext*)hb_enc_qsv_frames_ctx.hw_frames_ctx->data; + AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx; + + hb_qsv_get_free_surface_from_pool(&mid, &output_surface, HB_POOL_SURFACE_SIZE - 2); // leave 2 empty surfaces in the pool for black buffers + + // Get D3DDeviceManger handle from Media SDK + mfxHandleType handle_type; + IDirect3DDevice9 *pDevice = NULL; + HANDLE handle; + + static const mfxHandleType handle_types[] = { + MFX_HANDLE_VA_DISPLAY, + MFX_HANDLE_D3D9_DEVICE_MANAGER, + MFX_HANDLE_D3D11_DEVICE, + }; + + int i; + + AVHWDeviceContext *device_ctx = (AVHWDeviceContext*)hb_hw_device_ctx->data; + AVQSVDeviceContext *device_hwctx = device_ctx->hwctx; + mfxSession parent_session = device_hwctx->session; + + if(device_manager_handle == NULL) + { + for (i = 0; i < 3; i++) { + int err = MFXVideoCORE_GetHandle(parent_session, handle_types[i], &device_manager_handle); + if (err == MFX_ERR_NONE) { + handle_type = handle_types[i]; + break; + } + device_manager_handle = NULL; + } + + if (!device_manager_handle) { + hb_error("No supported hw handle could be retrieved " + "from the session\n"); + return out; + } + } + + HRESULT result = lock_device((IDirect3DDeviceManager9 *)device_manager_handle, 0, &pDevice, &handle); + if(FAILED(result)) { + hb_error("copy_frame qsv: LockDevice failded=%d", result); + return out; + } + + mfxFrameSurface1* input_surface = (mfxFrameSurface1*)frame->data[3]; + + // copy all surface fields + *output_surface = *input_surface; + // replace the mem id to mem id from the pool + output_surface->Data.MemId = mid->handle; + // copy input sufrace to sufrace from the pool + result = IDirect3DDevice9_StretchRect(pDevice, input_surface->Data.MemId, 0, output_surface->Data.MemId, 0, D3DTEXF_LINEAR); // used in media sdk samples + if(FAILED(result)) { + hb_error("copy_frame qsv: IDirect3DDevice9_StretchRect failded=%d", result); + return out; + } + result = unlock_device((IDirect3DDeviceManager9 *)device_manager_handle, handle); + if(FAILED(result)) { + hb_error("copy_frame qsv: UnlockDevice failded=%d", result); + return out; + } + out->qsv_details.frame->data[3] = output_surface; + out->qsv_details.qsv_atom = 0; + out->qsv_details.ctx = qsv_ctx; + return out; +} + +static int qsv_get_buffer(AVCodecContext *s, AVFrame *frame, int flags) +{ + int ret = -1; + if(s->hw_frames_ctx) + { + ret = av_hwframe_get_buffer(s->hw_frames_ctx, frame, 0); + } + return ret; +} + +void hb_qsv_uninit_dec(AVCodecContext *s) +{ + if(s && s->hw_frames_ctx) + av_buffer_unref(&s->hw_frames_ctx); +} + +void hb_qsv_uninit_enc() +{ + if(enc_hw_frames_ctx) + av_buffer_unref(&enc_hw_frames_ctx); + + enc_hw_frames_ctx = NULL; + hb_hw_device_ctx = NULL; + qsv_device = NULL; + device_manager_handle = NULL; +} + +static int qsv_device_init(AVCodecContext *s) +{ + int err; + AVDictionary *dict = NULL; + + if (qsv_device) { + err = av_dict_set(&dict, "child_device", qsv_device, 0); + if (err < 0) + return err; + } + + err = av_hwdevice_ctx_create(&hb_hw_device_ctx, AV_HWDEVICE_TYPE_QSV, + 0, dict, 0); + if (err < 0) { + av_log(NULL, AV_LOG_ERROR, "Error creating a QSV device\n"); + goto err_out; + } + +err_out: + if (dict) + av_dict_free(&dict); + + return err; +} + +static int qsv_init(AVCodecContext *s) +{ + AVHWFramesContext *frames_ctx; + AVQSVFramesContext *frames_hwctx; + + int ret; + + if (!hb_hw_device_ctx) { + ret = qsv_device_init(s); + if (ret < 0) + return ret; + } + + av_buffer_unref(&s->hw_frames_ctx); + s->hw_frames_ctx = av_hwframe_ctx_alloc(hb_hw_device_ctx); + if (!s->hw_frames_ctx) + return AVERROR(ENOMEM); + + frames_ctx = (AVHWFramesContext*)s->hw_frames_ctx->data; + frames_hwctx = frames_ctx->hwctx; + + frames_ctx->width = FFALIGN(s->coded_width, 32); + frames_ctx->height = FFALIGN(s->coded_height, 32); + frames_ctx->format = AV_PIX_FMT_QSV; + frames_ctx->sw_format = s->sw_pix_fmt; + frames_ctx->initial_pool_size = 32 + s->extra_hw_frames; + frames_hwctx->frame_type = MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET; + + ret = av_hwframe_ctx_init(s->hw_frames_ctx); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "Error initializing a QSV frame pool\n"); + return ret; + } + + av_buffer_unref(&enc_hw_frames_ctx); + enc_hw_frames_ctx = av_hwframe_ctx_alloc(hb_hw_device_ctx); + if (!enc_hw_frames_ctx) + return AVERROR(ENOMEM); + + hb_enc_qsv_frames_ctx.hw_frames_ctx = enc_hw_frames_ctx; + frames_ctx = (AVHWFramesContext*)enc_hw_frames_ctx->data; + frames_hwctx = frames_ctx->hwctx; + + frames_ctx->width = FFALIGN(s->coded_width, 32); + frames_ctx->height = FFALIGN(s->coded_height, 32); + frames_ctx->format = AV_PIX_FMT_QSV; + frames_ctx->sw_format = s->sw_pix_fmt; + frames_ctx->initial_pool_size = HB_POOL_SURFACE_SIZE; + frames_hwctx->frame_type = MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET; + + ret = av_hwframe_ctx_init(enc_hw_frames_ctx); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "Error initializing a QSV frame pool\n"); + return ret; + } + + /* allocate the memory ids for the external frames */ + av_buffer_unref(&hb_enc_qsv_frames_ctx.mids_buf); + hb_enc_qsv_frames_ctx.mids_buf = hb_qsv_create_mids(hb_enc_qsv_frames_ctx.hw_frames_ctx); + if (!hb_enc_qsv_frames_ctx.mids_buf) + return AVERROR(ENOMEM); + hb_enc_qsv_frames_ctx.mids = (QSVMid*)hb_enc_qsv_frames_ctx.mids_buf->data; + hb_enc_qsv_frames_ctx.nb_mids = frames_hwctx->nb_surfaces; + + memset(hb_enc_qsv_frames_ctx.pool, 0, hb_enc_qsv_frames_ctx.nb_mids * sizeof(hb_enc_qsv_frames_ctx.pool[0])); + return 0; +} + +int hb_qsv_get_buffer(AVCodecContext *s, AVFrame *frame, int flags) +{ + if (frame->format == AV_PIX_FMT_QSV) + return qsv_get_buffer(s, frame, flags); + + return avcodec_default_get_buffer2(s, frame, flags); +} + +enum AVPixelFormat hb_qsv_get_format(AVCodecContext *s, const enum AVPixelFormat *pix_fmts) +{ + const enum AVPixelFormat *p; + + for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(*p); + + if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) + break; + + if(*p == AV_PIX_FMT_QSV) + { + qsv_init(s); + + if (s->hw_frames_ctx) { + s->hw_frames_ctx = av_buffer_ref(s->hw_frames_ctx); + if (!s->hw_frames_ctx) + return AV_PIX_FMT_NONE; + } + break; + } + else + { + hb_error("get_format: p != AV_PIX_FMT_QSV"); + } + } + + return *p; +} + +int hb_qsv_preset_is_zero_copy_enabled(const hb_dict_t *job_dict) +{ + hb_dict_t *video_dict, *qsv, *encoder; + int qsv_encoder_enabled = 0; + int qsv_decoder_enabled = 0; + video_dict = hb_dict_get(job_dict, "Video"); + if(video_dict) + { + encoder = hb_dict_get(video_dict, "Encoder"); + if(encoder) + { + if (hb_value_type(encoder) == HB_VALUE_TYPE_STRING) + { + if(!strcasecmp(hb_value_get_string(encoder), "qsv_h264") || + !strcasecmp(hb_value_get_string(encoder), "qsv_h265")) + { + qsv_encoder_enabled = 1; + } + } + } + qsv = hb_dict_get(video_dict, "QSV"); + if (qsv != NULL) + { + hb_dict_t *decode; + decode = hb_dict_get(qsv, "Decode"); + if(decode) + { + if (hb_value_type(decode) == HB_VALUE_TYPE_BOOL) + { + qsv_decoder_enabled = hb_value_get_bool(decode); + } + } + } + } + return (qsv_encoder_enabled && qsv_decoder_enabled); +} + +#else // other OS + +hb_buffer_t* hb_qsv_copy_frame(AVFrame *frame, hb_qsv_context *qsv_ctx) +{ + return NULL; +} + +void hb_qsv_get_free_surface_from_pool(QSVMid **out_mid, mfxFrameSurface1 **out_surface, int pool_size) +{ + return; +} + +enum AVPixelFormat hb_qsv_get_format(AVCodecContext *s, const enum AVPixelFormat *pix_fmts) +{ + return AV_PIX_FMT_NONE; +} + +int hb_qsv_get_buffer(AVCodecContext *s, AVFrame *frame, int flags) +{ + return -1; +} + +void hb_qsv_uninit_dec(AVCodecContext *s) +{ +} + +void hb_qsv_uninit_enc() +{ +} + +int hb_qsv_preset_is_zero_copy_enabled(const hb_dict_t *job_dict) +{ + return 0; +} + +static int hb_dxva2_device_check() +{ + return -1; +} + +#endif + #else int hb_qsv_available() diff --git a/libhb/qsv_common.h b/libhb/qsv_common.h index dedc8a3fe..9522f7488 100644 --- a/libhb/qsv_common.h +++ b/libhb/qsv_common.h @@ -19,6 +19,7 @@ int hb_qsv_available(); #include "mfx/mfxvideo.h" #include "mfx/mfxplugin.h" #include "libavcodec/avcodec.h" +#include "hb_dict.h" /* Minimum Intel Media SDK version (currently 1.3, for Sandy Bridge support) */ #define HB_QSV_MINVERSION_MAJOR HB_QSV_MSDK_VERSION_MAJOR @@ -196,5 +197,54 @@ const char* hb_qsv_impl_get_via_name(int impl); void hb_qsv_force_workarounds(); // for developers only +typedef struct QSVMid { + AVBufferRef *hw_frames_ref; + mfxHDL handle; + + AVFrame *locked_frame; + AVFrame *hw_frame; + mfxFrameSurface1 surf; +} QSVMid; + +typedef struct QSVFrame { + AVFrame *frame; + mfxFrameSurface1 surface; + mfxEncodeCtrl enc_ctrl; + mfxExtDecodedFrameInfo dec_info; + mfxExtBuffer *ext_param; + + int queued; + int used; + + struct QSVFrame *next; +} QSVFrame; + +#define HB_POOL_SURFACE_SIZE (200) + +typedef struct EncQSVFramesContext { + AVBufferRef *hw_frames_ctx; + //void *logctx; + + /* The memory ids for the external frames. + * Refcounted, since we need one reference owned by the QSVFramesContext + * (i.e. by the encoder/decoder) and another one given to the MFX session + * from the frame allocator. */ + AVBufferRef *mids_buf; + QSVMid *mids; + int nb_mids; + int pool[HB_POOL_SURFACE_SIZE]; +} EncQSVFramesContext; + +/* Full QSV pipeline helpers */ +int hb_qsv_full_path_is_enabled(hb_job_t *job); +AVBufferRef *hb_qsv_create_mids(AVBufferRef *hw_frames_ref); +hb_buffer_t* hb_qsv_copy_frame(AVFrame *frame, hb_qsv_context *qsv_ctx); +void hb_qsv_get_free_surface_from_pool(QSVMid **out_mid, mfxFrameSurface1 **out_surface, int pool_size); +int hb_qsv_get_buffer(AVCodecContext *s, AVFrame *frame, int flags); +enum AVPixelFormat hb_qsv_get_format(AVCodecContext *s, const enum AVPixelFormat *pix_fmts); +int hb_qsv_preset_is_zero_copy_enabled(const hb_dict_t *job_dict); +void hb_qsv_uninit_dec(AVCodecContext *s); +void hb_qsv_uninit_enc(); + #endif // HB_PROJECT_FEATURE_QSV #endif // HB_QSV_COMMON_H diff --git a/libhb/qsv_libav.c b/libhb/qsv_libav.c index c2e768767..797aee184 100644 --- a/libhb/qsv_libav.c +++ b/libhb/qsv_libav.c @@ -236,8 +236,9 @@ int hb_qsv_context_clean(hb_qsv_context * qsv) hb_qsv_pipe_list_clean(&qsv->pipes); if (qsv->mfx_session) { - sts = MFXClose(qsv->mfx_session); - HB_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + // Intentionally leave it because the decode session closed first and encoder session hangs + //sts = MFXClose(qsv->mfx_session); + //HB_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); qsv->mfx_session = 0; } } |