summaryrefslogtreecommitdiffstats
path: root/libhb/qsv_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'libhb/qsv_common.c')
-rw-r--r--libhb/qsv_common.c558
1 files changed, 558 insertions, 0 deletions
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()