summaryrefslogtreecommitdiffstats
path: root/libhb
diff options
context:
space:
mode:
authormaximd33 <[email protected]>2019-05-18 20:49:10 +0200
committerScott <[email protected]>2019-05-26 19:28:57 +0100
commitec37ce222d4baaf13b0c5f5efedf350d5f28fc8f (patch)
treef168442ca71caa5d99929bca4e0d5c838df2f3ca /libhb
parentacbeba350a2067b716c5867296e4d9396be37657 (diff)
qsv: zero-copy re-implementation
Diffstat (limited to 'libhb')
-rw-r--r--libhb/decavcodec.c85
-rw-r--r--libhb/enc_qsv.c443
-rw-r--r--libhb/hb_json.c11
-rw-r--r--libhb/internal.h1
-rw-r--r--libhb/preset.c28
-rw-r--r--libhb/qsv_common.c558
-rw-r--r--libhb/qsv_common.h50
-rw-r--r--libhb/qsv_libav.c5
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;
}
}