diff mbox

[FFmpeg-devel] lavc/amfenc: moving amf common code (library and context) to lavu/hwcontext_amf from amfenc to be reused in other amf components

Message ID 20180508110004.43332-1-akravchenko188@gmail.com
State Superseded
Headers show

Commit Message

Alexander Kravchenko May 8, 2018, 11 a.m. UTC
This patch moves AMF common parts from amfenc to hwcontext_amf.
Now av_hwdevice_ctx API is used for AMF context creation/destroying.
This patch does not change component behaviour.
it contains only restructurization for further patches with new amf components

---
sending patch one more time in May, since April's one wasn't commented/pushed.
previous April's link:
http://ffmpeg.org/pipermail/ffmpeg-devel/2018-April/229051.html



 libavcodec/amfenc.c            | 250 ++++---------------------------
 libavcodec/amfenc.h            |  27 +---
 libavutil/Makefile             |   2 +
 libavutil/hwcontext.c          |   4 +
 libavutil/hwcontext.h          |   1 +
 libavutil/hwcontext_amf.c      | 329 +++++++++++++++++++++++++++++++++++++++++
 libavutil/hwcontext_amf.h      |  43 ++++++
 libavutil/hwcontext_internal.h |   1 +
 8 files changed, 418 insertions(+), 239 deletions(-)
 create mode 100644 libavutil/hwcontext_amf.c
 create mode 100644 libavutil/hwcontext_amf.h

Comments

Mark Thompson May 10, 2018, 8:42 p.m. UTC | #1
On 08/05/18 12:00, Alexander Kravchenko wrote:
> This patch moves AMF common parts from amfenc to hwcontext_amf.
> Now av_hwdevice_ctx API is used for AMF context creation/destroying.
> This patch does not change component behaviour.
> it contains only restructurization for further patches with new amf components
> 
> ---
>  libavcodec/amfenc.c            | 250 ++++---------------------------
>  libavcodec/amfenc.h            |  27 +---
>  libavutil/Makefile             |   2 +
>  libavutil/hwcontext.c          |   4 +
>  libavutil/hwcontext.h          |   1 +
>  libavutil/hwcontext_amf.c      | 329 +++++++++++++++++++++++++++++++++++++++++
>  libavutil/hwcontext_amf.h      |  43 ++++++
>  libavutil/hwcontext_internal.h |   1 +
>  8 files changed, 418 insertions(+), 239 deletions(-)
>  create mode 100644 libavutil/hwcontext_amf.c
>  create mode 100644 libavutil/hwcontext_amf.h
> 
> diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c
> index 384d8efc92..a8186c7b86 100644
> --- a/libavcodec/amfenc.c
> +++ b/libavcodec/amfenc.c
> @@ -21,13 +21,7 @@
>  #include "libavutil/avassert.h"
>  #include "libavutil/imgutils.h"
>  #include "libavutil/hwcontext.h"
> -#if CONFIG_D3D11VA
> -#include "libavutil/hwcontext_d3d11va.h"
> -#endif
> -#if CONFIG_DXVA2
> -#define COBJMACROS
> -#include "libavutil/hwcontext_dxva2.h"
> -#endif
> +
>  #include "libavutil/mem.h"
>  #include "libavutil/pixdesc.h"
>  #include "libavutil/time.h"
> @@ -35,14 +29,12 @@
>  #include "amfenc.h"
>  #include "internal.h"
>  
> -#if CONFIG_D3D11VA
> -#include <d3d11.h>
> +#if CONFIG_DXVA2
> +#include <d3d9.h>
>  #endif
>  
> -#ifdef _WIN32
> -#include "compat/w32dlfcn.h"
> -#else
> -#include <dlfcn.h>
> +#if CONFIG_D3D11VA
> +#include <d3d11.h>
>  #endif
>  
>  #define FFMPEG_AMF_WRITER_ID L"ffmpeg_amf"
> @@ -88,34 +80,17 @@ static enum AMF_SURFACE_FORMAT amf_av_to_amf_format(enum AVPixelFormat fmt)
>      return AMF_SURFACE_UNKNOWN;
>  }
>  
> -static void AMF_CDECL_CALL AMFTraceWriter_Write(AMFTraceWriter *pThis,
> -    const wchar_t *scope, const wchar_t *message)
> -{
> -    AmfTraceWriter *tracer = (AmfTraceWriter*)pThis;
> -    av_log(tracer->avctx, AV_LOG_DEBUG, "%ls: %ls", scope, message); // \n is provided from AMF
> -}
>  
> -static void AMF_CDECL_CALL AMFTraceWriter_Flush(AMFTraceWriter *pThis)
> -{
> -}
> -
> -static AMFTraceWriterVtbl tracer_vtbl =
> -{
> -    .Write = AMFTraceWriter_Write,
> -    .Flush = AMFTraceWriter_Flush,
> -};
> -
> -static int amf_load_library(AVCodecContext *avctx)
> +static int amf_init_context(AVCodecContext *avctx)
>  {
> -    AmfContext        *ctx = avctx->priv_data;
> -    AMFInit_Fn         init_fun;
> -    AMFQueryVersion_Fn version_fun;
> -    AMF_RESULT         res;
> +    AmfContext *ctx = avctx->priv_data;
> +    av_unused int ret;

This is now used in every configuration, so it doesn't need the annotation.

>  
>      ctx->delayed_frame = av_frame_alloc();
>      if (!ctx->delayed_frame) {
>          return AVERROR(ENOMEM);
>      }
> +    

Cosmetic change, and it's also trailing whitespace.

>      // hardcoded to current HW queue size - will realloc in timestamp_queue_enqueue() if too small
>      ctx->timestamp_list = av_fifo_alloc((avctx->max_b_frames + 16) * sizeof(int64_t));
>      if (!ctx->timestamp_list) {
> @@ -123,119 +98,9 @@ static int amf_load_library(AVCodecContext *avctx)
>      }
>      ctx->dts_delay = 0;
>  
> -
> -    ctx->library = dlopen(AMF_DLL_NAMEA, RTLD_NOW | RTLD_LOCAL);
> -    AMF_RETURN_IF_FALSE(ctx, ctx->library != NULL,
> -        AVERROR_UNKNOWN, "DLL %s failed to open\n", AMF_DLL_NAMEA);
> -
> -    init_fun = (AMFInit_Fn)dlsym(ctx->library, AMF_INIT_FUNCTION_NAME);
> -    AMF_RETURN_IF_FALSE(ctx, init_fun != NULL, AVERROR_UNKNOWN, "DLL %s failed to find function %s\n", AMF_DLL_NAMEA, AMF_INIT_FUNCTION_NAME);
> -
> -    version_fun = (AMFQueryVersion_Fn)dlsym(ctx->library, AMF_QUERY_VERSION_FUNCTION_NAME);
> -    AMF_RETURN_IF_FALSE(ctx, version_fun != NULL, AVERROR_UNKNOWN, "DLL %s failed to find function %s\n", AMF_DLL_NAMEA, AMF_QUERY_VERSION_FUNCTION_NAME);
> -
> -    res = version_fun(&ctx->version);
> -    AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "%s failed with error %d\n", AMF_QUERY_VERSION_FUNCTION_NAME, res);
> -    res = init_fun(AMF_FULL_VERSION, &ctx->factory);
> -    AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "%s failed with error %d\n", AMF_INIT_FUNCTION_NAME, res);
> -    res = ctx->factory->pVtbl->GetTrace(ctx->factory, &ctx->trace);
> -    AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "GetTrace() failed with error %d\n", res);
> -    res = ctx->factory->pVtbl->GetDebug(ctx->factory, &ctx->debug);
> -    AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "GetDebug() failed with error %d\n", res);
> -    return 0;
> -}
> -
> -#if CONFIG_D3D11VA
> -static int amf_init_from_d3d11_device(AVCodecContext *avctx, AVD3D11VADeviceContext *hwctx)
> -{
> -    AmfContext *ctx = avctx->priv_data;
> -    AMF_RESULT res;
> -
> -    res = ctx->context->pVtbl->InitDX11(ctx->context, hwctx->device, AMF_DX11_1);
> -    if (res != AMF_OK) {
> -        if (res == AMF_NOT_SUPPORTED)
> -            av_log(avctx, AV_LOG_ERROR, "AMF via D3D11 is not supported on the given device.\n");
> -        else
> -            av_log(avctx, AV_LOG_ERROR, "AMF failed to initialise on the given D3D11 device: %d.\n", res);
> -        return AVERROR(ENODEV);
> -    }
> -
> -    return 0;
> -}
> -#endif
> -
> -#if CONFIG_DXVA2
> -static int amf_init_from_dxva2_device(AVCodecContext *avctx, AVDXVA2DeviceContext *hwctx)
> -{
> -    AmfContext *ctx = avctx->priv_data;
> -    HANDLE device_handle;
> -    IDirect3DDevice9 *device;
> -    HRESULT hr;
> -    AMF_RESULT res;
> -    int ret;
> -
> -    hr = IDirect3DDeviceManager9_OpenDeviceHandle(hwctx->devmgr, &device_handle);
> -    if (FAILED(hr)) {
> -        av_log(avctx, AV_LOG_ERROR, "Failed to open device handle for Direct3D9 device: %lx.\n", (unsigned long)hr);
> -        return AVERROR_EXTERNAL;
> -    }
> -
> -    hr = IDirect3DDeviceManager9_LockDevice(hwctx->devmgr, device_handle, &device, FALSE);
> -    if (SUCCEEDED(hr)) {
> -        IDirect3DDeviceManager9_UnlockDevice(hwctx->devmgr, device_handle, FALSE);
> -        ret = 0;
> -    } else {
> -        av_log(avctx, AV_LOG_ERROR, "Failed to lock device handle for Direct3D9 device: %lx.\n", (unsigned long)hr);
> -        ret = AVERROR_EXTERNAL;
> -    }
> -
> -    IDirect3DDeviceManager9_CloseDeviceHandle(hwctx->devmgr, device_handle);
> -
> -    if (ret < 0)
> -        return ret;
> -
> -    res = ctx->context->pVtbl->InitDX9(ctx->context, device);
> -
> -    IDirect3DDevice9_Release(device);
> -
> -    if (res != AMF_OK) {
> -        if (res == AMF_NOT_SUPPORTED)
> -            av_log(avctx, AV_LOG_ERROR, "AMF via D3D9 is not supported on the given device.\n");
> -        else
> -            av_log(avctx, AV_LOG_ERROR, "AMF failed to initialise on given D3D9 device: %d.\n", res);
> -        return AVERROR(ENODEV);
> -    }
> -
> -    return 0;
> -}
> -#endif
> -
> -static int amf_init_context(AVCodecContext *avctx)
> -{
> -    AmfContext *ctx = avctx->priv_data;
> -    AMF_RESULT  res;
> -    av_unused int ret;
> -
>      ctx->hwsurfaces_in_queue = 0;
>      ctx->hwsurfaces_in_queue_max = 16;
>  
> -    // configure AMF logger
> -    // the return of these functions indicates old state and do not affect behaviour
> -    ctx->trace->pVtbl->EnableWriter(ctx->trace, AMF_TRACE_WRITER_DEBUG_OUTPUT, ctx->log_to_dbg != 0 );
> -    if (ctx->log_to_dbg)
> -        ctx->trace->pVtbl->SetWriterLevel(ctx->trace, AMF_TRACE_WRITER_DEBUG_OUTPUT, AMF_TRACE_TRACE);
> -    ctx->trace->pVtbl->EnableWriter(ctx->trace, AMF_TRACE_WRITER_CONSOLE, 0);
> -    ctx->trace->pVtbl->SetGlobalLevel(ctx->trace, AMF_TRACE_TRACE);
> -
> -    // connect AMF logger to av_log
> -    ctx->tracer.vtbl = &tracer_vtbl;
> -    ctx->tracer.avctx = avctx;
> -    ctx->trace->pVtbl->RegisterWriter(ctx->trace, FFMPEG_AMF_WRITER_ID,(AMFTraceWriter*)&ctx->tracer, 1);
> -    ctx->trace->pVtbl->SetWriterLevel(ctx->trace, FFMPEG_AMF_WRITER_ID, AMF_TRACE_TRACE);
> -
> -    res = ctx->factory->pVtbl->CreateContext(ctx->factory, &ctx->context);
> -    AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "CreateContext() failed with error %d\n", res);
> -
>      // If a device was passed to the encoder, try to initialise from that.
>      if (avctx->hw_frames_ctx) {
>          AVHWFramesContext *frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data;
> @@ -246,26 +111,9 @@ static int amf_init_context(AVCodecContext *avctx)
>              return AVERROR(EINVAL);
>          }
>  
> -        switch (frames_ctx->device_ctx->type) {
> -#if CONFIG_D3D11VA
> -        case AV_HWDEVICE_TYPE_D3D11VA:
> -            ret = amf_init_from_d3d11_device(avctx, frames_ctx->device_ctx->hwctx);
> -            if (ret < 0)
> -                return ret;
> -            break;
> -#endif
> -#if CONFIG_DXVA2
> -        case AV_HWDEVICE_TYPE_DXVA2:
> -            ret = amf_init_from_dxva2_device(avctx, frames_ctx->device_ctx->hwctx);
> -            if (ret < 0)
> -                return ret;
> -            break;
> -#endif
> -        default:
> -            av_log(avctx, AV_LOG_ERROR, "AMF initialisation from a %s frames context is not supported.\n",
> -                   av_hwdevice_get_type_name(frames_ctx->device_ctx->type));
> -            return AVERROR(ENOSYS);
> -        }
> +        ret = av_hwdevice_ctx_create_derived(&ctx->amf_device_ctx, AV_HWDEVICE_TYPE_AMF, frames_ctx->device_ref, 0);
> +        if (ret < 0)
> +            return ret;
>  
>          ctx->hw_frames_ctx = av_buffer_ref(avctx->hw_frames_ctx);
>          if (!ctx->hw_frames_ctx)
> @@ -275,46 +123,26 @@ static int amf_init_context(AVCodecContext *avctx)
>              ctx->hwsurfaces_in_queue_max = frames_ctx->initial_pool_size - 1;
>  
>      } else if (avctx->hw_device_ctx) {
> -        AVHWDeviceContext *device_ctx = (AVHWDeviceContext*)avctx->hw_device_ctx->data;
> -
> -        switch (device_ctx->type) {
> -#if CONFIG_D3D11VA
> -        case AV_HWDEVICE_TYPE_D3D11VA:
> -            ret = amf_init_from_d3d11_device(avctx, device_ctx->hwctx);
> -            if (ret < 0)
> -                return ret;
> -            break;
> -#endif
> -#if CONFIG_DXVA2
> -        case AV_HWDEVICE_TYPE_DXVA2:
> -            ret = amf_init_from_dxva2_device(avctx, device_ctx->hwctx);
> -            if (ret < 0)
> -                return ret;
> -            break;
> -#endif
> -        default:
> -            av_log(avctx, AV_LOG_ERROR, "AMF initialisation from a %s device is not supported.\n",
> -                   av_hwdevice_get_type_name(device_ctx->type));
> -            return AVERROR(ENOSYS);
> -        }
> +        ret = av_hwdevice_ctx_create_derived(&ctx->amf_device_ctx, AV_HWDEVICE_TYPE_AMF, avctx->hw_device_ctx, 0);
> +        if (ret < 0)
> +            return ret;
>  
>          ctx->hw_device_ctx = av_buffer_ref(avctx->hw_device_ctx);
>          if (!ctx->hw_device_ctx)
>              return AVERROR(ENOMEM);
>  
>      } else {
> -        res = ctx->context->pVtbl->InitDX11(ctx->context, NULL, AMF_DX11_1);
> -        if (res == AMF_OK) {
> -            av_log(avctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via D3D11.\n");
> -        } else {
> -            res = ctx->context->pVtbl->InitDX9(ctx->context, NULL);
> -            if (res == AMF_OK) {
> -                av_log(avctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via D3D9.\n");
> -            } else {
> -                av_log(avctx, AV_LOG_ERROR, "AMF initialisation failed via D3D9: error %d.\n", res);
> -                return AVERROR(ENOSYS);
> -            }
> -        }
> +        ret = av_hwdevice_ctx_create(&ctx->amf_device_ctx, AV_HWDEVICE_TYPE_AMF, NULL, NULL, 0);
> +        if (ret < 0)
> +            return ret;
> +    }
> +
> +    if(ctx->amf_device_ctx)

When can this not be set?

> +    {
> +        AVHWDeviceContext *avhwctx =  (AVHWDeviceContext*)ctx->amf_device_ctx->data;
> +        AVAMFDeviceContext *amf_ctx = avhwctx->hwctx;
> +        ctx->context = amf_ctx->context;
> +        ctx->factory = amf_ctx->factory;
>      }
>      return 0;
>  }
> @@ -368,29 +196,17 @@ int av_cold ff_amf_encode_close(AVCodecContext *avctx)
>          ctx->encoder = NULL;
>      }
>  
> -    if (ctx->context) {
> -        ctx->context->pVtbl->Terminate(ctx->context);
> -        ctx->context->pVtbl->Release(ctx->context);
> -        ctx->context = NULL;
> -    }
>      av_buffer_unref(&ctx->hw_device_ctx);
>      av_buffer_unref(&ctx->hw_frames_ctx);
>  
> -    if (ctx->trace) {
> -        ctx->trace->pVtbl->UnregisterWriter(ctx->trace, FFMPEG_AMF_WRITER_ID);
> -    }
> -    if (ctx->library) {
> -        dlclose(ctx->library);
> -        ctx->library = NULL;
> -    }
> -    ctx->trace = NULL;
> -    ctx->debug = NULL;
>      ctx->factory = NULL;
> -    ctx->version = 0;
> +    ctx->context = NULL;
>      ctx->delayed_drain = 0;
>      av_frame_free(&ctx->delayed_frame);
>      av_fifo_freep(&ctx->timestamp_list);
>  
> +    av_buffer_unref(&ctx->amf_device_ctx);
> +
>      return 0;
>  }
>  
> @@ -494,11 +310,9 @@ int ff_amf_encode_init(AVCodecContext *avctx)
>  {
>      int ret;
>  
> -    if ((ret = amf_load_library(avctx)) == 0) {
> -        if ((ret = amf_init_context(avctx)) == 0) {
> -            if ((ret = amf_init_encoder(avctx)) == 0) {
> -                return 0;
> -            }
> +    if ((ret = amf_init_context(avctx)) == 0) {
> +        if ((ret = amf_init_encoder(avctx)) == 0) {
> +            return 0;
>          }
>      }
>      ff_amf_encode_close(avctx);
> diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h
> index b1361842bd..9ce577fa8f 100644
> --- a/libavcodec/amfenc.h
> +++ b/libavcodec/amfenc.h
> @@ -19,41 +19,26 @@
>  #ifndef AVCODEC_AMFENC_H
>  #define AVCODEC_AMFENC_H
>  
> -#include <AMF/core/Factory.h>
> -
>  #include <AMF/components/VideoEncoderVCE.h>
>  #include <AMF/components/VideoEncoderHEVC.h>
>  
>  #include "libavutil/fifo.h"
> +#include "libavutil/hwcontext_amf.h"
>  
>  #include "avcodec.h"
>  
>  
> -/**
> -* AMF trace writer callback class
> -* Used to capture all AMF logging
> -*/
> -
> -typedef struct AmfTraceWriter {
> -    AMFTraceWriterVtbl *vtbl;
> -    AVCodecContext     *avctx;
> -} AmfTraceWriter;
> -
>  /**
>  * AMF encoder context
>  */
>  
>  typedef struct AmfContext {
>      AVClass            *avclass;
> -    // access to AMF runtime
> -    amf_handle          library; ///< handle to DLL library
> -    AMFFactory         *factory; ///< pointer to AMF factory
> -    AMFDebug           *debug;   ///< pointer to AMF debug interface
> -    AMFTrace           *trace;   ///< pointer to AMF trace interface
> -
> -    amf_uint64          version; ///< version of AMF runtime
> -    AmfTraceWriter      tracer;  ///< AMF writer registered with AMF
> -    AMFContext         *context; ///< AMF context
> +
> +    AMFContext         *context;
> +    AMFFactory         *factory;
> +    AVBufferRef        *amf_device_ctx;
> +
>      //encoder
>      AMFComponent       *encoder; ///< AMF encoder object
>      amf_bool            eof;     ///< flag indicating EOF happened
> diff --git a/libavutil/Makefile b/libavutil/Makefile
> index a63ba523c9..34b858c606 100644
> --- a/libavutil/Makefile
> +++ b/libavutil/Makefile
> @@ -168,6 +168,7 @@ OBJS-$(CONFIG_VAAPI)                    += hwcontext_vaapi.o
>  OBJS-$(CONFIG_VIDEOTOOLBOX)             += hwcontext_videotoolbox.o
>  OBJS-$(CONFIG_VDPAU)                    += hwcontext_vdpau.o
>  OBJS-$(CONFIG_MEDIACODEC)               += hwcontext_mediacodec.o
> +OBJS-$(CONFIG_AMF)                      += hwcontext_amf.o
>  
>  OBJS += $(COMPAT_OBJS:%=../compat/%)
>  
> @@ -183,6 +184,7 @@ SKIPHEADERS-$(CONFIG_OPENCL)           += hwcontext_opencl.h
>  SKIPHEADERS-$(CONFIG_VAAPI)            += hwcontext_vaapi.h
>  SKIPHEADERS-$(CONFIG_VIDEOTOOLBOX)     += hwcontext_videotoolbox.h
>  SKIPHEADERS-$(CONFIG_VDPAU)            += hwcontext_vdpau.h
> +SKIPHEADERS-$(CONFIG_AMF)              += hwcontext_amf.h
>  
>  TESTPROGS = adler32                                                     \
>              aes                                                         \
> diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c
> index 70c556ecac..decf2d3566 100644
> --- a/libavutil/hwcontext.c
> +++ b/libavutil/hwcontext.c
> @@ -58,6 +58,9 @@ static const HWContextType * const hw_table[] = {
>  #endif
>  #if CONFIG_MEDIACODEC
>      &ff_hwcontext_type_mediacodec,
> +#endif
> +#if CONFIG_AMF
> +    &ff_hwcontext_type_amf,
>  #endif
>      NULL,
>  };
> @@ -73,6 +76,7 @@ static const char *const hw_type_names[] = {
>      [AV_HWDEVICE_TYPE_VDPAU]  = "vdpau",
>      [AV_HWDEVICE_TYPE_VIDEOTOOLBOX] = "videotoolbox",
>      [AV_HWDEVICE_TYPE_MEDIACODEC] = "mediacodec",
> +    [AV_HWDEVICE_TYPE_AMF] = "amf",
>  };
>  
>  enum AVHWDeviceType av_hwdevice_find_type_by_name(const char *name)
> diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h
> index f5a4b62387..b18591205a 100644
> --- a/libavutil/hwcontext.h
> +++ b/libavutil/hwcontext.h
> @@ -36,6 +36,7 @@ enum AVHWDeviceType {
>      AV_HWDEVICE_TYPE_DRM,
>      AV_HWDEVICE_TYPE_OPENCL,
>      AV_HWDEVICE_TYPE_MEDIACODEC,
> +    AV_HWDEVICE_TYPE_AMF,
>  };
>  
>  typedef struct AVHWDeviceInternal AVHWDeviceInternal;
> diff --git a/libavutil/hwcontext_amf.c b/libavutil/hwcontext_amf.c
> new file mode 100644
> index 0000000000..2c32f0e224
> --- /dev/null
> +++ b/libavutil/hwcontext_amf.c
> @@ -0,0 +1,329 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include <string.h>
> +
> +#include "config.h"
> +
> +#include "avassert.h"
> +#include "avstring.h"
> +#include "common.h"
> +#include "hwcontext.h"
> +#include "hwcontext_internal.h"
> +#include "hwcontext_amf.h"
> +
> +#if CONFIG_D3D11VA
> +#include "libavutil/hwcontext_d3d11va.h"
> +#endif
> +
> +#if CONFIG_DXVA2
> +#define COBJMACROS
> +#include "libavutil/hwcontext_dxva2.h"
> +#endif
> +
> +#ifdef _WIN32
> +#include "compat/w32dlfcn.h"
> +#else
> +#include <dlfcn.h>
> +#endif
> +
> +/**
> +* Error handling helper
> +*/
> +#define AMFAV_RETURN_IF_FALSE(avctx, exp, ret_value, /*message,*/ ...) \
> +    if (!(exp)) { \
> +        av_log(avctx, AV_LOG_ERROR, __VA_ARGS__); \
> +        return ret_value; \
> +    }
> +
> +typedef struct AmfTraceWriter {
> +    AMFTraceWriterVtbl  *vtbl;
> +    void                *avcl;
> +} AmfTraceWriter;
> +
> +static void AMF_CDECL_CALL AMFTraceWriter_Write(AMFTraceWriter *pThis,
> +    const wchar_t *scope, const wchar_t *message)
> +{
> +    AmfTraceWriter *tracer = (AmfTraceWriter*)pThis;
> +    av_log(tracer->avcl, AV_LOG_DEBUG, "%ls: %ls", scope, message);
> +}
> +
> +static void AMF_CDECL_CALL AMFTraceWriter_Flush(AMFTraceWriter *pThis)
> +{
> +}
> +
> +static AMFTraceWriterVtbl tracer_vtbl =

const

> +{
> +    .Write = AMFTraceWriter_Write,
> +    .Flush = AMFTraceWriter_Flush,
> +};
> +
> +#define AMFAV_WRITER_ID L"avlog"
> +
> +static const AVClass amflib_class = {
> +    .class_name = "amf",
> +    .item_name = av_default_item_name,
> +    .version = LIBAVUTIL_VERSION_INT,
> +};
> +
> +typedef struct AMFLibrary {
> +    const AVClass      *avclass;

I don't think you want this AVClass.  It has no options, and you should be passing the AVHWDeviceContext to the log functions.

> +    amf_handle          library;
> +    AMFFactory         *factory;
> +    AMFDebug           *debug;
> +    AMFTrace           *trace;
> +
> +    amf_uint64          version;
> +    AmfTraceWriter      tracer;
> +
> +} AMFLibrary;
> +
> +static void amf_library_ctx_free(void *opaque, uint8_t *data)
> +{
> +    AMFLibrary *ctx = (AMFLibrary*)data;
> +
> +    if (ctx->trace) {
> +        ctx->trace->pVtbl->UnregisterWriter(ctx->trace, AMFAV_WRITER_ID);
> +    }
> +    if (ctx->library) {
> +        dlclose(ctx->library);
> +    }
> +
> +    av_freep(&ctx);
> +}
> +
> +static AVBufferRef *amf_library_ctx_alloc(void)
> +{
> +    AVBufferRef *buf;
> +    AMFLibrary  *ctx;
> +
> +    ctx = av_mallocz(sizeof(*ctx));
> +    if (!ctx)
> +        return NULL;
> +
> +    buf = av_buffer_create((uint8_t*)ctx, sizeof(*ctx), amf_library_ctx_free, NULL, AV_BUFFER_FLAG_READONLY);

Check failure (returns the right thing, but leaks ctx).

> +    return buf;
> +}
> +
> +static int amf_init_library(AMFLibrary *ctx)
> +{
> +    AMFInit_Fn              init_fun = NULL;
> +    AMFQueryVersion_Fn      version_fun = NULL;
> +    AMF_RESULT              res = AMF_OK;
> +
> +    ctx->avclass = &amflib_class;
> +    ctx->library = dlopen(AMF_DLL_NAMEA, RTLD_NOW | RTLD_LOCAL);
> +    AMFAV_RETURN_IF_FALSE(ctx, ctx->library != NULL, AVERROR_UNKNOWN, "DLL %s failed to open\n", AMF_DLL_NAMEA);
> +
> +    init_fun = (AMFInit_Fn)dlsym(ctx->library, AMF_INIT_FUNCTION_NAME);
> +    AMFAV_RETURN_IF_FALSE(ctx, init_fun != NULL, AVERROR_UNKNOWN, "DLL %s failed to find function %s\n", AMF_DLL_NAMEA, AMF_INIT_FUNCTION_NAME);
> +
> +    version_fun = (AMFQueryVersion_Fn)dlsym(ctx->library, AMF_QUERY_VERSION_FUNCTION_NAME);
> +    AMFAV_RETURN_IF_FALSE(ctx, version_fun != NULL, AVERROR_UNKNOWN, "DLL %s failed to find function %s\n", AMF_DLL_NAMEA, AMF_QUERY_VERSION_FUNCTION_NAME);
> +
> +    res = version_fun(&ctx->version);
> +    AMFAV_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "%s failed with error %d\n", AMF_QUERY_VERSION_FUNCTION_NAME, res);

Nothing happens to this version.  I realise it's been copied from the amfenc code, but why does it exist at all?

> +    res = init_fun(AMF_FULL_VERSION, &ctx->factory);
> +    AMFAV_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "%s failed with error %d\n", AMF_INIT_FUNCTION_NAME, res);
> +    res = ctx->factory->pVtbl->GetTrace(ctx->factory, &ctx->trace);
> +    AMFAV_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "GetTrace() failed with error %d\n", res);
> +    res = ctx->factory->pVtbl->GetDebug(ctx->factory, &ctx->debug);
> +    AMFAV_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "GetDebug() failed with error %d\n", res);
> +
> +    ctx->trace->pVtbl->EnableWriter(ctx->trace, AMF_TRACE_WRITER_CONSOLE, 0);
> +    ctx->trace->pVtbl->SetGlobalLevel(ctx->trace, AMF_TRACE_TRACE);
> +
> +    // connect AMF logger to av_log
> +    ctx->tracer.vtbl = &tracer_vtbl;
> +    ctx->tracer.avcl = ctx;
> +    ctx->trace->pVtbl->RegisterWriter(ctx->trace, AMFAV_WRITER_ID, (AMFTraceWriter*)&ctx->tracer, 1);
> +    ctx->trace->pVtbl->SetWriterLevel(ctx->trace, AMFAV_WRITER_ID, AMF_TRACE_TRACE);
> +    return 0;
> +}
> +
> +static AVBufferRef *amf_library_ctx = NULL;

Global mutable state in libraries is strongly discouraged.  Just loading it again should be fine?

> +static AVBufferRef *aquire_amf_library_ctx(void)
> +{
> +    AVBufferRef *ret = NULL;
> +    if (amf_library_ctx == NULL)
> +    {
> +        ret = amf_library_ctx_alloc();
> +        if(amf_init_library((AMFLibrary *)ret->data) < 0)
> +        {
> +            av_buffer_unref(&amf_library_ctx);
> +            ret = NULL;

av_buffer_unref() sets the reference to NULL already.

> +        } else {
> +            amf_library_ctx = ret;
> +        }
> +    } else {
> +        ret = av_buffer_ref(amf_library_ctx);
> +    }
> +    return ret;
> +}
> +
> +typedef struct AVAMFDeviceContextPrivate {
> +    AMFLibrary *lib;
> +    AVBufferRef *lib_ref;
> +} AVAMFDeviceContextPrivate;

Shouldn't have an "AV" prefix.

> +
> +static int amf_init_device_ctx_object(AVHWDeviceContext *ctx)
> +{
> +    AVAMFDeviceContext *hwctx = ctx->hwctx;
> +    AVAMFDeviceContextPrivate *priv = ctx->internal->priv;
> +    AMF_RESULT res;
> +
> +    priv->lib_ref = aquire_amf_library_ctx();
> +    priv->lib = (AMFLibrary*)priv->lib_ref->data;
> +
> +    res = priv->lib->factory->pVtbl->CreateContext(priv->lib->factory, &hwctx->context);
> +    hwctx->factory = priv->lib->factory;
> +    AMFAV_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "CreateContext() failed with error %d\n", res);
> +    return 0;
> +}
> +
> +static int amf_device_create(AVHWDeviceContext *ctx, const char *device,
> +                                AVDictionary *opts, int flags)
> +{
> +    AVAMFDeviceContext *amf_ctx = ctx->hwctx;
> +    AMF_RESULT res;
> +    int err;
> +
> +    err = amf_init_device_ctx_object(ctx);
> +    if(err < 0)
> +        return err;
> +
> +    res = amf_ctx->context->pVtbl->InitDX11(amf_ctx->context, NULL, AMF_DX11_1);
> +    if (res == AMF_OK) {
> +        av_log(ctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via D3D11.\n");
> +    } else {
> +        res = amf_ctx->context->pVtbl->InitDX9(amf_ctx->context, NULL);
> +        if (res == AMF_OK) {
> +            av_log(ctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via D3D9.\n");
> +        } else {
> +            av_log(ctx, AV_LOG_ERROR, "AMF initialisation failed via D3D9: error %d.\n", res);
> +            return AVERROR(ENOSYS);
> +        }
> +    }
> +    return 0;
> +}
> +
> +static int amf_device_derive(AVHWDeviceContext *dst_ctx,
> +                                AVHWDeviceContext *src_ctx,
> +                                int flags)
> +{
> +    AVAMFDeviceContext *amf_ctx = dst_ctx->hwctx;
> +    AMF_RESULT res;
> +    int err;
> +
> +    err = amf_init_device_ctx_object(dst_ctx);
> +    if(err < 0)
> +        return err;
> +
> +    switch (src_ctx->type) {
> +
> +#if CONFIG_D3D11VA
> +    case AV_HWDEVICE_TYPE_DXVA2:
> +        {
> +            AVDXVA2DeviceContext *dxva2_ctx = src_ctx->hwctx;
> +            HANDLE device_handle;
> +            IDirect3DDevice9 *device;
> +            HRESULT hr;
> +            AMF_RESULT res;
> +            int ret;
> +
> +            hr = IDirect3DDeviceManager9_OpenDeviceHandle(dxva2_ctx->devmgr, &device_handle);
> +            if (FAILED(hr)) {
> +                av_log(dst_ctx, AV_LOG_ERROR, "Failed to open device handle for Direct3D9 device: %lx.\n", (unsigned long)hr);
> +                return AVERROR_EXTERNAL;
> +            }
> +
> +            hr = IDirect3DDeviceManager9_LockDevice(dxva2_ctx->devmgr, device_handle, &device, FALSE);
> +            if (SUCCEEDED(hr)) {
> +                IDirect3DDeviceManager9_UnlockDevice(dxva2_ctx->devmgr, device_handle, FALSE);
> +                ret = 0;
> +            } else {
> +                av_log(dst_ctx, AV_LOG_ERROR, "Failed to lock device handle for Direct3D9 device: %lx.\n", (unsigned long)hr);
> +                ret = AVERROR_EXTERNAL;
> +            }
> +
> +            IDirect3DDeviceManager9_CloseDeviceHandle(dxva2_ctx->devmgr, device_handle);
> +
> +            if (ret < 0)
> +                return ret;
> +
> +            res = amf_ctx->context->pVtbl->InitDX9(amf_ctx->context, device);
> +
> +            IDirect3DDevice9_Release(device);
> +
> +            if (res != AMF_OK) {
> +                if (res == AMF_NOT_SUPPORTED)
> +                    av_log(dst_ctx, AV_LOG_ERROR, "AMF via D3D9 is not supported on the given device.\n");
> +                else
> +                    av_log(dst_ctx, AV_LOG_ERROR, "AMF failed to initialise on given D3D9 device: %d.\n", res);
> +                return AVERROR(ENODEV);
> +            }
> +        }
> +        break;
> +#endif
> +
> +#if CONFIG_D3D11VA
> +    case AV_HWDEVICE_TYPE_D3D11VA:
> +        {
> +            AVD3D11VADeviceContext *d3d11_ctx = src_ctx->hwctx;
> +            res = amf_ctx->context->pVtbl->InitDX11(amf_ctx->context, d3d11_ctx->device, AMF_DX11_1);
> +            if (res != AMF_OK) {
> +                if (res == AMF_NOT_SUPPORTED)
> +                    av_log(dst_ctx, AV_LOG_ERROR, "AMF via D3D11 is not supported on the given device.\n");
> +                else
> +                    av_log(dst_ctx, AV_LOG_ERROR, "AMF failed to initialise on the given D3D11 device: %d.\n", res);
> +                return AVERROR(ENODEV);
> +            }
> +        }
> +        break;
> +#endif
> +    default:
> +        av_log(dst_ctx, AV_LOG_ERROR, "AMF initialisation from a %s device is not supported.\n",
> +                av_hwdevice_get_type_name(src_ctx->type));
> +        return AVERROR(ENOSYS);
> +    }
> +    return 0;
> +}
> +
> +static void amf_device_uninit(AVHWDeviceContext *ctx)
> +{
> +    AVAMFDeviceContext *amf_ctx = ctx->hwctx;
> +    AVAMFDeviceContextPrivate *priv = ctx->internal->priv;
> +    if (amf_ctx->context) {
> +        amf_ctx->context->pVtbl->Terminate(amf_ctx->context);
> +        amf_ctx->context->pVtbl->Release(amf_ctx->context);
> +        amf_ctx->context = NULL;
> +    }
> +    av_buffer_unref(&priv->lib_ref);
> +}
> +
> +const HWContextType ff_hwcontext_type_amf = {
> +    .type                   = AV_HWDEVICE_TYPE_AMF,
> +    .name                   = "AMF",
> +
> +    .device_hwctx_size      = sizeof(AVAMFDeviceContext),
> +    .device_priv_size       = sizeof(AVAMFDeviceContextPrivate),
> +
> +    .device_create          = &amf_device_create,
> +    .device_derive          = &amf_device_derive,
> +    .device_uninit          = &amf_device_uninit,
> +};
> diff --git a/libavutil/hwcontext_amf.h b/libavutil/hwcontext_amf.h
> new file mode 100644
> index 0000000000..1b19fd2ce8
> --- /dev/null
> +++ b/libavutil/hwcontext_amf.h
> @@ -0,0 +1,43 @@
> +/*
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +
> +#ifndef AVUTIL_HWCONTEXT_AMF_H
> +#define AVUTIL_HWCONTEXT_AMF_H
> +
> +/**
> + * @file
> + * API-specific header for AV_HWDEVICE_TYPE_AMF.
> + *
> + */
> +
> +#include "frame.h"
> +#include "AMF/core/Context.h"
> +#include "AMF/core/Factory.h"
> +
> +
> +/**
> + * This struct is allocated as AVHWDeviceContext.hwctx
> + */
> +typedef struct AVAMFDeviceContext {
> +    AMFContext *context;
> +    AMFFactory *factory;

Do you actually need both of these?  It feels like you should be able to derive the creating factory (/ library instance) from the context.

> +} AVAMFDeviceContext;
> +
> +
> +#endif /* AVUTIL_HWCONTEXT_AMF_H */
> diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h
> index 332062ddaa..179797a936 100644
> --- a/libavutil/hwcontext_internal.h
> +++ b/libavutil/hwcontext_internal.h
> @@ -167,5 +167,6 @@ extern const HWContextType ff_hwcontext_type_vaapi;
>  extern const HWContextType ff_hwcontext_type_vdpau;
>  extern const HWContextType ff_hwcontext_type_videotoolbox;
>  extern const HWContextType ff_hwcontext_type_mediacodec;
> +extern const HWContextType ff_hwcontext_type_amf;
>  
>  #endif /* AVUTIL_HWCONTEXT_INTERNAL_H */
> 

I need to reconstitute an AMD+Windows machine to test this, I'm going to try to do that later this week.

Thanks,

- Mark
Alexander Kravchenko May 11, 2018, 6:37 p.m. UTC | #2
Hi Mark, 
Thank you for your comments.
Could you see my comments and questions bellow

> -----Original Message-----
> From: ffmpeg-devel [mailto:ffmpeg-devel-bounces@ffmpeg.org] On Behalf Of Mark Thompson
> Sent: Thursday, May 10, 2018 11:43 PM
> To: ffmpeg-devel@ffmpeg.org
> Subject: Re: [FFmpeg-devel] [PATCH] lavc/amfenc: moving amf common code (library and context) to lavu/hwcontext_amf from
> amfenc to be reused in other amf components
> 

> > +static AVBufferRef *amf_library_ctx = NULL;
> 
> Global mutable state in libraries is strongly discouraged.  Just loading it again should be fine?
> 

Yes, loading it again could be fine.
I wanted to prevent additional call of library loading and trace initializations every time amf_device context is created.
User can create many instances of encoder in one process. 
Storing amf_library_ctx allows to detect when cleanup functions can be called (UnregisterWriter...). Of course we can never call dclose and keep library loaded all time.
What is your opinion?

> > +
> > +#include "frame.h"
> > +#include "AMF/core/Context.h"
> > +#include "AMF/core/Factory.h"
> > +
> > +
> > +/**
> > + * This struct is allocated as AVHWDeviceContext.hwctx  */ typedef
> > +struct AVAMFDeviceContext {
> > +    AMFContext *context;
> > +    AMFFactory *factory;
> 
> Do you actually need both of these?  It feels like you should be able to derive the creating factory (/ library instance) from the context.
> 

AMFContext does not have pointer to AMFFactory. Now AMFFactory -> CreateComponent is used in encoder initialization.
May be AMFFactory is too wide interface on this level. Pointer to function like CreateComponent can be published in AVAMFDeviceContext instead of factory pointer.
or did you mean something different?

--
I have another question about publish amf library level options (Trace level, trace writers...). This could be in another patch if we decide this option is possible.
What is the best way to add such option? Can command line options be used to configure library? Or somehow publish this API?

Thanks,
Alexander
Mark Thompson May 12, 2018, 10:20 p.m. UTC | #3
On 11/05/18 19:37, Alexander Kravchenko wrote:
> Hi Mark, 
> Thank you for your comments.
> Could you see my comments and questions bellow
> 
>> -----Original Message-----
>> From: ffmpeg-devel [mailto:ffmpeg-devel-bounces@ffmpeg.org] On Behalf Of Mark Thompson
>> Sent: Thursday, May 10, 2018 11:43 PM
>> To: ffmpeg-devel@ffmpeg.org
>> Subject: Re: [FFmpeg-devel] [PATCH] lavc/amfenc: moving amf common code (library and context) to lavu/hwcontext_amf from
>> amfenc to be reused in other amf components
>>
> 
>>> +static AVBufferRef *amf_library_ctx = NULL;
>>
>> Global mutable state in libraries is strongly discouraged.  Just loading it again should be fine?
>>
> 
> Yes, loading it again could be fine.
> I wanted to prevent additional call of library loading and trace initializations every time amf_device context is created.
> User can create many instances of encoder in one process. 
> Storing amf_library_ctx allows to detect when cleanup functions can be called (UnregisterWriter...). Of course we can never call dclose and keep library loaded all time.
> What is your opinion?

Is there any later benefit to reusing the pointer, or is it just saving on the load/initialisation code?  If there isn't a strong reason to do so I would avoid storing it.  (Note that doing so would require more machinery to guard against data races as well.)

>>> +
>>> +#include "frame.h"
>>> +#include "AMF/core/Context.h"
>>> +#include "AMF/core/Factory.h"
>>> +
>>> +
>>> +/**
>>> + * This struct is allocated as AVHWDeviceContext.hwctx  */ typedef
>>> +struct AVAMFDeviceContext {
>>> +    AMFContext *context;
>>> +    AMFFactory *factory;
>>
>> Do you actually need both of these?  It feels like you should be able to derive the creating factory (/ library instance) from the context.
>>
> 
> AMFContext does not have pointer to AMFFactory. Now AMFFactory -> CreateComponent is used in encoder initialization.
> May be AMFFactory is too wide interface on this level. Pointer to function like CreateComponent can be published in AVAMFDeviceContext instead of factory pointer.
> or did you mean something different?

Since this is ending up as immutable public API, we should be avoiding adding anything which isn't absolutely needed.  If they are both needed then that's fine - I don't think including the CreateComponent pointer instead would be any simpler.

> I have another question about publish amf library level options (Trace level, trace writers...). This could be in another patch if we decide this option is possible.
> What is the best way to add such option? Can command line options be used to configure library? Or somehow publish this API?

Maybe use the opts dict passed to av_hwdevice_ctx_create()?  (They're accessible in the ffmpeg utility via -init_hw_device.)

- Mark
diff mbox

Patch

diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c
index 384d8efc92..a8186c7b86 100644
--- a/libavcodec/amfenc.c
+++ b/libavcodec/amfenc.c
@@ -21,13 +21,7 @@ 
 #include "libavutil/avassert.h"
 #include "libavutil/imgutils.h"
 #include "libavutil/hwcontext.h"
-#if CONFIG_D3D11VA
-#include "libavutil/hwcontext_d3d11va.h"
-#endif
-#if CONFIG_DXVA2
-#define COBJMACROS
-#include "libavutil/hwcontext_dxva2.h"
-#endif
+
 #include "libavutil/mem.h"
 #include "libavutil/pixdesc.h"
 #include "libavutil/time.h"
@@ -35,14 +29,12 @@ 
 #include "amfenc.h"
 #include "internal.h"
 
-#if CONFIG_D3D11VA
-#include <d3d11.h>
+#if CONFIG_DXVA2
+#include <d3d9.h>
 #endif
 
-#ifdef _WIN32
-#include "compat/w32dlfcn.h"
-#else
-#include <dlfcn.h>
+#if CONFIG_D3D11VA
+#include <d3d11.h>
 #endif
 
 #define FFMPEG_AMF_WRITER_ID L"ffmpeg_amf"
@@ -88,34 +80,17 @@  static enum AMF_SURFACE_FORMAT amf_av_to_amf_format(enum AVPixelFormat fmt)
     return AMF_SURFACE_UNKNOWN;
 }
 
-static void AMF_CDECL_CALL AMFTraceWriter_Write(AMFTraceWriter *pThis,
-    const wchar_t *scope, const wchar_t *message)
-{
-    AmfTraceWriter *tracer = (AmfTraceWriter*)pThis;
-    av_log(tracer->avctx, AV_LOG_DEBUG, "%ls: %ls", scope, message); // \n is provided from AMF
-}
 
-static void AMF_CDECL_CALL AMFTraceWriter_Flush(AMFTraceWriter *pThis)
-{
-}
-
-static AMFTraceWriterVtbl tracer_vtbl =
-{
-    .Write = AMFTraceWriter_Write,
-    .Flush = AMFTraceWriter_Flush,
-};
-
-static int amf_load_library(AVCodecContext *avctx)
+static int amf_init_context(AVCodecContext *avctx)
 {
-    AmfContext        *ctx = avctx->priv_data;
-    AMFInit_Fn         init_fun;
-    AMFQueryVersion_Fn version_fun;
-    AMF_RESULT         res;
+    AmfContext *ctx = avctx->priv_data;
+    av_unused int ret;
 
     ctx->delayed_frame = av_frame_alloc();
     if (!ctx->delayed_frame) {
         return AVERROR(ENOMEM);
     }
+    
     // hardcoded to current HW queue size - will realloc in timestamp_queue_enqueue() if too small
     ctx->timestamp_list = av_fifo_alloc((avctx->max_b_frames + 16) * sizeof(int64_t));
     if (!ctx->timestamp_list) {
@@ -123,119 +98,9 @@  static int amf_load_library(AVCodecContext *avctx)
     }
     ctx->dts_delay = 0;
 
-
-    ctx->library = dlopen(AMF_DLL_NAMEA, RTLD_NOW | RTLD_LOCAL);
-    AMF_RETURN_IF_FALSE(ctx, ctx->library != NULL,
-        AVERROR_UNKNOWN, "DLL %s failed to open\n", AMF_DLL_NAMEA);
-
-    init_fun = (AMFInit_Fn)dlsym(ctx->library, AMF_INIT_FUNCTION_NAME);
-    AMF_RETURN_IF_FALSE(ctx, init_fun != NULL, AVERROR_UNKNOWN, "DLL %s failed to find function %s\n", AMF_DLL_NAMEA, AMF_INIT_FUNCTION_NAME);
-
-    version_fun = (AMFQueryVersion_Fn)dlsym(ctx->library, AMF_QUERY_VERSION_FUNCTION_NAME);
-    AMF_RETURN_IF_FALSE(ctx, version_fun != NULL, AVERROR_UNKNOWN, "DLL %s failed to find function %s\n", AMF_DLL_NAMEA, AMF_QUERY_VERSION_FUNCTION_NAME);
-
-    res = version_fun(&ctx->version);
-    AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "%s failed with error %d\n", AMF_QUERY_VERSION_FUNCTION_NAME, res);
-    res = init_fun(AMF_FULL_VERSION, &ctx->factory);
-    AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "%s failed with error %d\n", AMF_INIT_FUNCTION_NAME, res);
-    res = ctx->factory->pVtbl->GetTrace(ctx->factory, &ctx->trace);
-    AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "GetTrace() failed with error %d\n", res);
-    res = ctx->factory->pVtbl->GetDebug(ctx->factory, &ctx->debug);
-    AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "GetDebug() failed with error %d\n", res);
-    return 0;
-}
-
-#if CONFIG_D3D11VA
-static int amf_init_from_d3d11_device(AVCodecContext *avctx, AVD3D11VADeviceContext *hwctx)
-{
-    AmfContext *ctx = avctx->priv_data;
-    AMF_RESULT res;
-
-    res = ctx->context->pVtbl->InitDX11(ctx->context, hwctx->device, AMF_DX11_1);
-    if (res != AMF_OK) {
-        if (res == AMF_NOT_SUPPORTED)
-            av_log(avctx, AV_LOG_ERROR, "AMF via D3D11 is not supported on the given device.\n");
-        else
-            av_log(avctx, AV_LOG_ERROR, "AMF failed to initialise on the given D3D11 device: %d.\n", res);
-        return AVERROR(ENODEV);
-    }
-
-    return 0;
-}
-#endif
-
-#if CONFIG_DXVA2
-static int amf_init_from_dxva2_device(AVCodecContext *avctx, AVDXVA2DeviceContext *hwctx)
-{
-    AmfContext *ctx = avctx->priv_data;
-    HANDLE device_handle;
-    IDirect3DDevice9 *device;
-    HRESULT hr;
-    AMF_RESULT res;
-    int ret;
-
-    hr = IDirect3DDeviceManager9_OpenDeviceHandle(hwctx->devmgr, &device_handle);
-    if (FAILED(hr)) {
-        av_log(avctx, AV_LOG_ERROR, "Failed to open device handle for Direct3D9 device: %lx.\n", (unsigned long)hr);
-        return AVERROR_EXTERNAL;
-    }
-
-    hr = IDirect3DDeviceManager9_LockDevice(hwctx->devmgr, device_handle, &device, FALSE);
-    if (SUCCEEDED(hr)) {
-        IDirect3DDeviceManager9_UnlockDevice(hwctx->devmgr, device_handle, FALSE);
-        ret = 0;
-    } else {
-        av_log(avctx, AV_LOG_ERROR, "Failed to lock device handle for Direct3D9 device: %lx.\n", (unsigned long)hr);
-        ret = AVERROR_EXTERNAL;
-    }
-
-    IDirect3DDeviceManager9_CloseDeviceHandle(hwctx->devmgr, device_handle);
-
-    if (ret < 0)
-        return ret;
-
-    res = ctx->context->pVtbl->InitDX9(ctx->context, device);
-
-    IDirect3DDevice9_Release(device);
-
-    if (res != AMF_OK) {
-        if (res == AMF_NOT_SUPPORTED)
-            av_log(avctx, AV_LOG_ERROR, "AMF via D3D9 is not supported on the given device.\n");
-        else
-            av_log(avctx, AV_LOG_ERROR, "AMF failed to initialise on given D3D9 device: %d.\n", res);
-        return AVERROR(ENODEV);
-    }
-
-    return 0;
-}
-#endif
-
-static int amf_init_context(AVCodecContext *avctx)
-{
-    AmfContext *ctx = avctx->priv_data;
-    AMF_RESULT  res;
-    av_unused int ret;
-
     ctx->hwsurfaces_in_queue = 0;
     ctx->hwsurfaces_in_queue_max = 16;
 
-    // configure AMF logger
-    // the return of these functions indicates old state and do not affect behaviour
-    ctx->trace->pVtbl->EnableWriter(ctx->trace, AMF_TRACE_WRITER_DEBUG_OUTPUT, ctx->log_to_dbg != 0 );
-    if (ctx->log_to_dbg)
-        ctx->trace->pVtbl->SetWriterLevel(ctx->trace, AMF_TRACE_WRITER_DEBUG_OUTPUT, AMF_TRACE_TRACE);
-    ctx->trace->pVtbl->EnableWriter(ctx->trace, AMF_TRACE_WRITER_CONSOLE, 0);
-    ctx->trace->pVtbl->SetGlobalLevel(ctx->trace, AMF_TRACE_TRACE);
-
-    // connect AMF logger to av_log
-    ctx->tracer.vtbl = &tracer_vtbl;
-    ctx->tracer.avctx = avctx;
-    ctx->trace->pVtbl->RegisterWriter(ctx->trace, FFMPEG_AMF_WRITER_ID,(AMFTraceWriter*)&ctx->tracer, 1);
-    ctx->trace->pVtbl->SetWriterLevel(ctx->trace, FFMPEG_AMF_WRITER_ID, AMF_TRACE_TRACE);
-
-    res = ctx->factory->pVtbl->CreateContext(ctx->factory, &ctx->context);
-    AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "CreateContext() failed with error %d\n", res);
-
     // If a device was passed to the encoder, try to initialise from that.
     if (avctx->hw_frames_ctx) {
         AVHWFramesContext *frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data;
@@ -246,26 +111,9 @@  static int amf_init_context(AVCodecContext *avctx)
             return AVERROR(EINVAL);
         }
 
-        switch (frames_ctx->device_ctx->type) {
-#if CONFIG_D3D11VA
-        case AV_HWDEVICE_TYPE_D3D11VA:
-            ret = amf_init_from_d3d11_device(avctx, frames_ctx->device_ctx->hwctx);
-            if (ret < 0)
-                return ret;
-            break;
-#endif
-#if CONFIG_DXVA2
-        case AV_HWDEVICE_TYPE_DXVA2:
-            ret = amf_init_from_dxva2_device(avctx, frames_ctx->device_ctx->hwctx);
-            if (ret < 0)
-                return ret;
-            break;
-#endif
-        default:
-            av_log(avctx, AV_LOG_ERROR, "AMF initialisation from a %s frames context is not supported.\n",
-                   av_hwdevice_get_type_name(frames_ctx->device_ctx->type));
-            return AVERROR(ENOSYS);
-        }
+        ret = av_hwdevice_ctx_create_derived(&ctx->amf_device_ctx, AV_HWDEVICE_TYPE_AMF, frames_ctx->device_ref, 0);
+        if (ret < 0)
+            return ret;
 
         ctx->hw_frames_ctx = av_buffer_ref(avctx->hw_frames_ctx);
         if (!ctx->hw_frames_ctx)
@@ -275,46 +123,26 @@  static int amf_init_context(AVCodecContext *avctx)
             ctx->hwsurfaces_in_queue_max = frames_ctx->initial_pool_size - 1;
 
     } else if (avctx->hw_device_ctx) {
-        AVHWDeviceContext *device_ctx = (AVHWDeviceContext*)avctx->hw_device_ctx->data;
-
-        switch (device_ctx->type) {
-#if CONFIG_D3D11VA
-        case AV_HWDEVICE_TYPE_D3D11VA:
-            ret = amf_init_from_d3d11_device(avctx, device_ctx->hwctx);
-            if (ret < 0)
-                return ret;
-            break;
-#endif
-#if CONFIG_DXVA2
-        case AV_HWDEVICE_TYPE_DXVA2:
-            ret = amf_init_from_dxva2_device(avctx, device_ctx->hwctx);
-            if (ret < 0)
-                return ret;
-            break;
-#endif
-        default:
-            av_log(avctx, AV_LOG_ERROR, "AMF initialisation from a %s device is not supported.\n",
-                   av_hwdevice_get_type_name(device_ctx->type));
-            return AVERROR(ENOSYS);
-        }
+        ret = av_hwdevice_ctx_create_derived(&ctx->amf_device_ctx, AV_HWDEVICE_TYPE_AMF, avctx->hw_device_ctx, 0);
+        if (ret < 0)
+            return ret;
 
         ctx->hw_device_ctx = av_buffer_ref(avctx->hw_device_ctx);
         if (!ctx->hw_device_ctx)
             return AVERROR(ENOMEM);
 
     } else {
-        res = ctx->context->pVtbl->InitDX11(ctx->context, NULL, AMF_DX11_1);
-        if (res == AMF_OK) {
-            av_log(avctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via D3D11.\n");
-        } else {
-            res = ctx->context->pVtbl->InitDX9(ctx->context, NULL);
-            if (res == AMF_OK) {
-                av_log(avctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via D3D9.\n");
-            } else {
-                av_log(avctx, AV_LOG_ERROR, "AMF initialisation failed via D3D9: error %d.\n", res);
-                return AVERROR(ENOSYS);
-            }
-        }
+        ret = av_hwdevice_ctx_create(&ctx->amf_device_ctx, AV_HWDEVICE_TYPE_AMF, NULL, NULL, 0);
+        if (ret < 0)
+            return ret;
+    }
+
+    if(ctx->amf_device_ctx)
+    {
+        AVHWDeviceContext *avhwctx =  (AVHWDeviceContext*)ctx->amf_device_ctx->data;
+        AVAMFDeviceContext *amf_ctx = avhwctx->hwctx;
+        ctx->context = amf_ctx->context;
+        ctx->factory = amf_ctx->factory;
     }
     return 0;
 }
@@ -368,29 +196,17 @@  int av_cold ff_amf_encode_close(AVCodecContext *avctx)
         ctx->encoder = NULL;
     }
 
-    if (ctx->context) {
-        ctx->context->pVtbl->Terminate(ctx->context);
-        ctx->context->pVtbl->Release(ctx->context);
-        ctx->context = NULL;
-    }
     av_buffer_unref(&ctx->hw_device_ctx);
     av_buffer_unref(&ctx->hw_frames_ctx);
 
-    if (ctx->trace) {
-        ctx->trace->pVtbl->UnregisterWriter(ctx->trace, FFMPEG_AMF_WRITER_ID);
-    }
-    if (ctx->library) {
-        dlclose(ctx->library);
-        ctx->library = NULL;
-    }
-    ctx->trace = NULL;
-    ctx->debug = NULL;
     ctx->factory = NULL;
-    ctx->version = 0;
+    ctx->context = NULL;
     ctx->delayed_drain = 0;
     av_frame_free(&ctx->delayed_frame);
     av_fifo_freep(&ctx->timestamp_list);
 
+    av_buffer_unref(&ctx->amf_device_ctx);
+
     return 0;
 }
 
@@ -494,11 +310,9 @@  int ff_amf_encode_init(AVCodecContext *avctx)
 {
     int ret;
 
-    if ((ret = amf_load_library(avctx)) == 0) {
-        if ((ret = amf_init_context(avctx)) == 0) {
-            if ((ret = amf_init_encoder(avctx)) == 0) {
-                return 0;
-            }
+    if ((ret = amf_init_context(avctx)) == 0) {
+        if ((ret = amf_init_encoder(avctx)) == 0) {
+            return 0;
         }
     }
     ff_amf_encode_close(avctx);
diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h
index b1361842bd..9ce577fa8f 100644
--- a/libavcodec/amfenc.h
+++ b/libavcodec/amfenc.h
@@ -19,41 +19,26 @@ 
 #ifndef AVCODEC_AMFENC_H
 #define AVCODEC_AMFENC_H
 
-#include <AMF/core/Factory.h>
-
 #include <AMF/components/VideoEncoderVCE.h>
 #include <AMF/components/VideoEncoderHEVC.h>
 
 #include "libavutil/fifo.h"
+#include "libavutil/hwcontext_amf.h"
 
 #include "avcodec.h"
 
 
-/**
-* AMF trace writer callback class
-* Used to capture all AMF logging
-*/
-
-typedef struct AmfTraceWriter {
-    AMFTraceWriterVtbl *vtbl;
-    AVCodecContext     *avctx;
-} AmfTraceWriter;
-
 /**
 * AMF encoder context
 */
 
 typedef struct AmfContext {
     AVClass            *avclass;
-    // access to AMF runtime
-    amf_handle          library; ///< handle to DLL library
-    AMFFactory         *factory; ///< pointer to AMF factory
-    AMFDebug           *debug;   ///< pointer to AMF debug interface
-    AMFTrace           *trace;   ///< pointer to AMF trace interface
-
-    amf_uint64          version; ///< version of AMF runtime
-    AmfTraceWriter      tracer;  ///< AMF writer registered with AMF
-    AMFContext         *context; ///< AMF context
+
+    AMFContext         *context;
+    AMFFactory         *factory;
+    AVBufferRef        *amf_device_ctx;
+
     //encoder
     AMFComponent       *encoder; ///< AMF encoder object
     amf_bool            eof;     ///< flag indicating EOF happened
diff --git a/libavutil/Makefile b/libavutil/Makefile
index a63ba523c9..34b858c606 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -168,6 +168,7 @@  OBJS-$(CONFIG_VAAPI)                    += hwcontext_vaapi.o
 OBJS-$(CONFIG_VIDEOTOOLBOX)             += hwcontext_videotoolbox.o
 OBJS-$(CONFIG_VDPAU)                    += hwcontext_vdpau.o
 OBJS-$(CONFIG_MEDIACODEC)               += hwcontext_mediacodec.o
+OBJS-$(CONFIG_AMF)                      += hwcontext_amf.o
 
 OBJS += $(COMPAT_OBJS:%=../compat/%)
 
@@ -183,6 +184,7 @@  SKIPHEADERS-$(CONFIG_OPENCL)           += hwcontext_opencl.h
 SKIPHEADERS-$(CONFIG_VAAPI)            += hwcontext_vaapi.h
 SKIPHEADERS-$(CONFIG_VIDEOTOOLBOX)     += hwcontext_videotoolbox.h
 SKIPHEADERS-$(CONFIG_VDPAU)            += hwcontext_vdpau.h
+SKIPHEADERS-$(CONFIG_AMF)              += hwcontext_amf.h
 
 TESTPROGS = adler32                                                     \
             aes                                                         \
diff --git a/libavutil/hwcontext.c b/libavutil/hwcontext.c
index 70c556ecac..decf2d3566 100644
--- a/libavutil/hwcontext.c
+++ b/libavutil/hwcontext.c
@@ -58,6 +58,9 @@  static const HWContextType * const hw_table[] = {
 #endif
 #if CONFIG_MEDIACODEC
     &ff_hwcontext_type_mediacodec,
+#endif
+#if CONFIG_AMF
+    &ff_hwcontext_type_amf,
 #endif
     NULL,
 };
@@ -73,6 +76,7 @@  static const char *const hw_type_names[] = {
     [AV_HWDEVICE_TYPE_VDPAU]  = "vdpau",
     [AV_HWDEVICE_TYPE_VIDEOTOOLBOX] = "videotoolbox",
     [AV_HWDEVICE_TYPE_MEDIACODEC] = "mediacodec",
+    [AV_HWDEVICE_TYPE_AMF] = "amf",
 };
 
 enum AVHWDeviceType av_hwdevice_find_type_by_name(const char *name)
diff --git a/libavutil/hwcontext.h b/libavutil/hwcontext.h
index f5a4b62387..b18591205a 100644
--- a/libavutil/hwcontext.h
+++ b/libavutil/hwcontext.h
@@ -36,6 +36,7 @@  enum AVHWDeviceType {
     AV_HWDEVICE_TYPE_DRM,
     AV_HWDEVICE_TYPE_OPENCL,
     AV_HWDEVICE_TYPE_MEDIACODEC,
+    AV_HWDEVICE_TYPE_AMF,
 };
 
 typedef struct AVHWDeviceInternal AVHWDeviceInternal;
diff --git a/libavutil/hwcontext_amf.c b/libavutil/hwcontext_amf.c
new file mode 100644
index 0000000000..2c32f0e224
--- /dev/null
+++ b/libavutil/hwcontext_amf.c
@@ -0,0 +1,329 @@ 
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <string.h>
+
+#include "config.h"
+
+#include "avassert.h"
+#include "avstring.h"
+#include "common.h"
+#include "hwcontext.h"
+#include "hwcontext_internal.h"
+#include "hwcontext_amf.h"
+
+#if CONFIG_D3D11VA
+#include "libavutil/hwcontext_d3d11va.h"
+#endif
+
+#if CONFIG_DXVA2
+#define COBJMACROS
+#include "libavutil/hwcontext_dxva2.h"
+#endif
+
+#ifdef _WIN32
+#include "compat/w32dlfcn.h"
+#else
+#include <dlfcn.h>
+#endif
+
+/**
+* Error handling helper
+*/
+#define AMFAV_RETURN_IF_FALSE(avctx, exp, ret_value, /*message,*/ ...) \
+    if (!(exp)) { \
+        av_log(avctx, AV_LOG_ERROR, __VA_ARGS__); \
+        return ret_value; \
+    }
+
+typedef struct AmfTraceWriter {
+    AMFTraceWriterVtbl  *vtbl;
+    void                *avcl;
+} AmfTraceWriter;
+
+static void AMF_CDECL_CALL AMFTraceWriter_Write(AMFTraceWriter *pThis,
+    const wchar_t *scope, const wchar_t *message)
+{
+    AmfTraceWriter *tracer = (AmfTraceWriter*)pThis;
+    av_log(tracer->avcl, AV_LOG_DEBUG, "%ls: %ls", scope, message);
+}
+
+static void AMF_CDECL_CALL AMFTraceWriter_Flush(AMFTraceWriter *pThis)
+{
+}
+
+static AMFTraceWriterVtbl tracer_vtbl =
+{
+    .Write = AMFTraceWriter_Write,
+    .Flush = AMFTraceWriter_Flush,
+};
+
+#define AMFAV_WRITER_ID L"avlog"
+
+static const AVClass amflib_class = {
+    .class_name = "amf",
+    .item_name = av_default_item_name,
+    .version = LIBAVUTIL_VERSION_INT,
+};
+
+typedef struct AMFLibrary {
+    const AVClass      *avclass;
+    amf_handle          library;
+    AMFFactory         *factory;
+    AMFDebug           *debug;
+    AMFTrace           *trace;
+
+    amf_uint64          version;
+    AmfTraceWriter      tracer;
+
+} AMFLibrary;
+
+static void amf_library_ctx_free(void *opaque, uint8_t *data)
+{
+    AMFLibrary *ctx = (AMFLibrary*)data;
+
+    if (ctx->trace) {
+        ctx->trace->pVtbl->UnregisterWriter(ctx->trace, AMFAV_WRITER_ID);
+    }
+    if (ctx->library) {
+        dlclose(ctx->library);
+    }
+
+    av_freep(&ctx);
+}
+
+static AVBufferRef *amf_library_ctx_alloc(void)
+{
+    AVBufferRef *buf;
+    AMFLibrary  *ctx;
+
+    ctx = av_mallocz(sizeof(*ctx));
+    if (!ctx)
+        return NULL;
+
+    buf = av_buffer_create((uint8_t*)ctx, sizeof(*ctx), amf_library_ctx_free, NULL, AV_BUFFER_FLAG_READONLY);
+    return buf;
+}
+
+static int amf_init_library(AMFLibrary *ctx)
+{
+    AMFInit_Fn              init_fun = NULL;
+    AMFQueryVersion_Fn      version_fun = NULL;
+    AMF_RESULT              res = AMF_OK;
+
+    ctx->avclass = &amflib_class;
+    ctx->library = dlopen(AMF_DLL_NAMEA, RTLD_NOW | RTLD_LOCAL);
+    AMFAV_RETURN_IF_FALSE(ctx, ctx->library != NULL, AVERROR_UNKNOWN, "DLL %s failed to open\n", AMF_DLL_NAMEA);
+
+    init_fun = (AMFInit_Fn)dlsym(ctx->library, AMF_INIT_FUNCTION_NAME);
+    AMFAV_RETURN_IF_FALSE(ctx, init_fun != NULL, AVERROR_UNKNOWN, "DLL %s failed to find function %s\n", AMF_DLL_NAMEA, AMF_INIT_FUNCTION_NAME);
+
+    version_fun = (AMFQueryVersion_Fn)dlsym(ctx->library, AMF_QUERY_VERSION_FUNCTION_NAME);
+    AMFAV_RETURN_IF_FALSE(ctx, version_fun != NULL, AVERROR_UNKNOWN, "DLL %s failed to find function %s\n", AMF_DLL_NAMEA, AMF_QUERY_VERSION_FUNCTION_NAME);
+
+    res = version_fun(&ctx->version);
+    AMFAV_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "%s failed with error %d\n", AMF_QUERY_VERSION_FUNCTION_NAME, res);
+    res = init_fun(AMF_FULL_VERSION, &ctx->factory);
+    AMFAV_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "%s failed with error %d\n", AMF_INIT_FUNCTION_NAME, res);
+    res = ctx->factory->pVtbl->GetTrace(ctx->factory, &ctx->trace);
+    AMFAV_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "GetTrace() failed with error %d\n", res);
+    res = ctx->factory->pVtbl->GetDebug(ctx->factory, &ctx->debug);
+    AMFAV_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "GetDebug() failed with error %d\n", res);
+
+    ctx->trace->pVtbl->EnableWriter(ctx->trace, AMF_TRACE_WRITER_CONSOLE, 0);
+    ctx->trace->pVtbl->SetGlobalLevel(ctx->trace, AMF_TRACE_TRACE);
+
+    // connect AMF logger to av_log
+    ctx->tracer.vtbl = &tracer_vtbl;
+    ctx->tracer.avcl = ctx;
+    ctx->trace->pVtbl->RegisterWriter(ctx->trace, AMFAV_WRITER_ID, (AMFTraceWriter*)&ctx->tracer, 1);
+    ctx->trace->pVtbl->SetWriterLevel(ctx->trace, AMFAV_WRITER_ID, AMF_TRACE_TRACE);
+    return 0;
+}
+
+static AVBufferRef *amf_library_ctx = NULL;
+static AVBufferRef *aquire_amf_library_ctx(void)
+{
+    AVBufferRef *ret = NULL;
+    if (amf_library_ctx == NULL)
+    {
+        ret = amf_library_ctx_alloc();
+        if(amf_init_library((AMFLibrary *)ret->data) < 0)
+        {
+            av_buffer_unref(&amf_library_ctx);
+            ret = NULL;
+        } else {
+            amf_library_ctx = ret;
+        }
+    } else {
+        ret = av_buffer_ref(amf_library_ctx);
+    }
+    return ret;
+}
+
+typedef struct AVAMFDeviceContextPrivate {
+    AMFLibrary *lib;
+    AVBufferRef *lib_ref;
+} AVAMFDeviceContextPrivate;
+
+static int amf_init_device_ctx_object(AVHWDeviceContext *ctx)
+{
+    AVAMFDeviceContext *hwctx = ctx->hwctx;
+    AVAMFDeviceContextPrivate *priv = ctx->internal->priv;
+    AMF_RESULT res;
+
+    priv->lib_ref = aquire_amf_library_ctx();
+    priv->lib = (AMFLibrary*)priv->lib_ref->data;
+
+    res = priv->lib->factory->pVtbl->CreateContext(priv->lib->factory, &hwctx->context);
+    hwctx->factory = priv->lib->factory;
+    AMFAV_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "CreateContext() failed with error %d\n", res);
+    return 0;
+}
+
+static int amf_device_create(AVHWDeviceContext *ctx, const char *device,
+                                AVDictionary *opts, int flags)
+{
+    AVAMFDeviceContext *amf_ctx = ctx->hwctx;
+    AMF_RESULT res;
+    int err;
+
+    err = amf_init_device_ctx_object(ctx);
+    if(err < 0)
+        return err;
+
+    res = amf_ctx->context->pVtbl->InitDX11(amf_ctx->context, NULL, AMF_DX11_1);
+    if (res == AMF_OK) {
+        av_log(ctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via D3D11.\n");
+    } else {
+        res = amf_ctx->context->pVtbl->InitDX9(amf_ctx->context, NULL);
+        if (res == AMF_OK) {
+            av_log(ctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via D3D9.\n");
+        } else {
+            av_log(ctx, AV_LOG_ERROR, "AMF initialisation failed via D3D9: error %d.\n", res);
+            return AVERROR(ENOSYS);
+        }
+    }
+    return 0;
+}
+
+static int amf_device_derive(AVHWDeviceContext *dst_ctx,
+                                AVHWDeviceContext *src_ctx,
+                                int flags)
+{
+    AVAMFDeviceContext *amf_ctx = dst_ctx->hwctx;
+    AMF_RESULT res;
+    int err;
+
+    err = amf_init_device_ctx_object(dst_ctx);
+    if(err < 0)
+        return err;
+
+    switch (src_ctx->type) {
+
+#if CONFIG_D3D11VA
+    case AV_HWDEVICE_TYPE_DXVA2:
+        {
+            AVDXVA2DeviceContext *dxva2_ctx = src_ctx->hwctx;
+            HANDLE device_handle;
+            IDirect3DDevice9 *device;
+            HRESULT hr;
+            AMF_RESULT res;
+            int ret;
+
+            hr = IDirect3DDeviceManager9_OpenDeviceHandle(dxva2_ctx->devmgr, &device_handle);
+            if (FAILED(hr)) {
+                av_log(dst_ctx, AV_LOG_ERROR, "Failed to open device handle for Direct3D9 device: %lx.\n", (unsigned long)hr);
+                return AVERROR_EXTERNAL;
+            }
+
+            hr = IDirect3DDeviceManager9_LockDevice(dxva2_ctx->devmgr, device_handle, &device, FALSE);
+            if (SUCCEEDED(hr)) {
+                IDirect3DDeviceManager9_UnlockDevice(dxva2_ctx->devmgr, device_handle, FALSE);
+                ret = 0;
+            } else {
+                av_log(dst_ctx, AV_LOG_ERROR, "Failed to lock device handle for Direct3D9 device: %lx.\n", (unsigned long)hr);
+                ret = AVERROR_EXTERNAL;
+            }
+
+            IDirect3DDeviceManager9_CloseDeviceHandle(dxva2_ctx->devmgr, device_handle);
+
+            if (ret < 0)
+                return ret;
+
+            res = amf_ctx->context->pVtbl->InitDX9(amf_ctx->context, device);
+
+            IDirect3DDevice9_Release(device);
+
+            if (res != AMF_OK) {
+                if (res == AMF_NOT_SUPPORTED)
+                    av_log(dst_ctx, AV_LOG_ERROR, "AMF via D3D9 is not supported on the given device.\n");
+                else
+                    av_log(dst_ctx, AV_LOG_ERROR, "AMF failed to initialise on given D3D9 device: %d.\n", res);
+                return AVERROR(ENODEV);
+            }
+        }
+        break;
+#endif
+
+#if CONFIG_D3D11VA
+    case AV_HWDEVICE_TYPE_D3D11VA:
+        {
+            AVD3D11VADeviceContext *d3d11_ctx = src_ctx->hwctx;
+            res = amf_ctx->context->pVtbl->InitDX11(amf_ctx->context, d3d11_ctx->device, AMF_DX11_1);
+            if (res != AMF_OK) {
+                if (res == AMF_NOT_SUPPORTED)
+                    av_log(dst_ctx, AV_LOG_ERROR, "AMF via D3D11 is not supported on the given device.\n");
+                else
+                    av_log(dst_ctx, AV_LOG_ERROR, "AMF failed to initialise on the given D3D11 device: %d.\n", res);
+                return AVERROR(ENODEV);
+            }
+        }
+        break;
+#endif
+    default:
+        av_log(dst_ctx, AV_LOG_ERROR, "AMF initialisation from a %s device is not supported.\n",
+                av_hwdevice_get_type_name(src_ctx->type));
+        return AVERROR(ENOSYS);
+    }
+    return 0;
+}
+
+static void amf_device_uninit(AVHWDeviceContext *ctx)
+{
+    AVAMFDeviceContext *amf_ctx = ctx->hwctx;
+    AVAMFDeviceContextPrivate *priv = ctx->internal->priv;
+    if (amf_ctx->context) {
+        amf_ctx->context->pVtbl->Terminate(amf_ctx->context);
+        amf_ctx->context->pVtbl->Release(amf_ctx->context);
+        amf_ctx->context = NULL;
+    }
+    av_buffer_unref(&priv->lib_ref);
+}
+
+const HWContextType ff_hwcontext_type_amf = {
+    .type                   = AV_HWDEVICE_TYPE_AMF,
+    .name                   = "AMF",
+
+    .device_hwctx_size      = sizeof(AVAMFDeviceContext),
+    .device_priv_size       = sizeof(AVAMFDeviceContextPrivate),
+
+    .device_create          = &amf_device_create,
+    .device_derive          = &amf_device_derive,
+    .device_uninit          = &amf_device_uninit,
+};
diff --git a/libavutil/hwcontext_amf.h b/libavutil/hwcontext_amf.h
new file mode 100644
index 0000000000..1b19fd2ce8
--- /dev/null
+++ b/libavutil/hwcontext_amf.h
@@ -0,0 +1,43 @@ 
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#ifndef AVUTIL_HWCONTEXT_AMF_H
+#define AVUTIL_HWCONTEXT_AMF_H
+
+/**
+ * @file
+ * API-specific header for AV_HWDEVICE_TYPE_AMF.
+ *
+ */
+
+#include "frame.h"
+#include "AMF/core/Context.h"
+#include "AMF/core/Factory.h"
+
+
+/**
+ * This struct is allocated as AVHWDeviceContext.hwctx
+ */
+typedef struct AVAMFDeviceContext {
+    AMFContext *context;
+    AMFFactory *factory;
+} AVAMFDeviceContext;
+
+
+#endif /* AVUTIL_HWCONTEXT_AMF_H */
diff --git a/libavutil/hwcontext_internal.h b/libavutil/hwcontext_internal.h
index 332062ddaa..179797a936 100644
--- a/libavutil/hwcontext_internal.h
+++ b/libavutil/hwcontext_internal.h
@@ -167,5 +167,6 @@  extern const HWContextType ff_hwcontext_type_vaapi;
 extern const HWContextType ff_hwcontext_type_vdpau;
 extern const HWContextType ff_hwcontext_type_videotoolbox;
 extern const HWContextType ff_hwcontext_type_mediacodec;
+extern const HWContextType ff_hwcontext_type_amf;
 
 #endif /* AVUTIL_HWCONTEXT_INTERNAL_H */