xref: /third_party/ffmpeg/libavcodec/amfenc.c (revision cabdff1a)
1/*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19#include "config.h"
20#include "config_components.h"
21
22#include "libavutil/avassert.h"
23#include "libavutil/imgutils.h"
24#include "libavutil/hwcontext.h"
25#if CONFIG_D3D11VA
26#include "libavutil/hwcontext_d3d11va.h"
27#endif
28#if CONFIG_DXVA2
29#define COBJMACROS
30#include "libavutil/hwcontext_dxva2.h"
31#endif
32#include "libavutil/mem.h"
33#include "libavutil/pixdesc.h"
34#include "libavutil/time.h"
35
36#include "amfenc.h"
37#include "encode.h"
38#include "internal.h"
39
40#if CONFIG_D3D11VA
41#include <d3d11.h>
42#endif
43
44#ifdef _WIN32
45#include "compat/w32dlfcn.h"
46#else
47#include <dlfcn.h>
48#endif
49
50#define FFMPEG_AMF_WRITER_ID L"ffmpeg_amf"
51
52#define PTS_PROP L"PtsProp"
53
54const enum AVPixelFormat ff_amf_pix_fmts[] = {
55    AV_PIX_FMT_NV12,
56    AV_PIX_FMT_YUV420P,
57#if CONFIG_D3D11VA
58    AV_PIX_FMT_D3D11,
59#endif
60#if CONFIG_DXVA2
61    AV_PIX_FMT_DXVA2_VLD,
62#endif
63    AV_PIX_FMT_NONE
64};
65
66typedef struct FormatMap {
67    enum AVPixelFormat       av_format;
68    enum AMF_SURFACE_FORMAT  amf_format;
69} FormatMap;
70
71static const FormatMap format_map[] =
72{
73    { AV_PIX_FMT_NONE,       AMF_SURFACE_UNKNOWN },
74    { AV_PIX_FMT_NV12,       AMF_SURFACE_NV12 },
75    { AV_PIX_FMT_BGR0,       AMF_SURFACE_BGRA },
76    { AV_PIX_FMT_RGB0,       AMF_SURFACE_RGBA },
77    { AV_PIX_FMT_GRAY8,      AMF_SURFACE_GRAY8 },
78    { AV_PIX_FMT_YUV420P,    AMF_SURFACE_YUV420P },
79    { AV_PIX_FMT_YUYV422,    AMF_SURFACE_YUY2 },
80};
81
82static enum AMF_SURFACE_FORMAT amf_av_to_amf_format(enum AVPixelFormat fmt)
83{
84    int i;
85    for (i = 0; i < amf_countof(format_map); i++) {
86        if (format_map[i].av_format == fmt) {
87            return format_map[i].amf_format;
88        }
89    }
90    return AMF_SURFACE_UNKNOWN;
91}
92
93static void AMF_CDECL_CALL AMFTraceWriter_Write(AMFTraceWriter *pThis,
94    const wchar_t *scope, const wchar_t *message)
95{
96    AmfTraceWriter *tracer = (AmfTraceWriter*)pThis;
97    av_log(tracer->avctx, AV_LOG_DEBUG, "%ls: %ls", scope, message); // \n is provided from AMF
98}
99
100static void AMF_CDECL_CALL AMFTraceWriter_Flush(AMFTraceWriter *pThis)
101{
102}
103
104static AMFTraceWriterVtbl tracer_vtbl =
105{
106    .Write = AMFTraceWriter_Write,
107    .Flush = AMFTraceWriter_Flush,
108};
109
110static int amf_load_library(AVCodecContext *avctx)
111{
112    AmfContext        *ctx = avctx->priv_data;
113    AMFInit_Fn         init_fun;
114    AMFQueryVersion_Fn version_fun;
115    AMF_RESULT         res;
116
117    ctx->delayed_frame = av_frame_alloc();
118    if (!ctx->delayed_frame) {
119        return AVERROR(ENOMEM);
120    }
121    // hardcoded to current HW queue size - will auto-realloc if too small
122    ctx->timestamp_list = av_fifo_alloc2(avctx->max_b_frames + 16, sizeof(int64_t),
123                                         AV_FIFO_FLAG_AUTO_GROW);
124    if (!ctx->timestamp_list) {
125        return AVERROR(ENOMEM);
126    }
127    ctx->dts_delay = 0;
128
129
130    ctx->library = dlopen(AMF_DLL_NAMEA, RTLD_NOW | RTLD_LOCAL);
131    AMF_RETURN_IF_FALSE(ctx, ctx->library != NULL,
132        AVERROR_UNKNOWN, "DLL %s failed to open\n", AMF_DLL_NAMEA);
133
134    init_fun = (AMFInit_Fn)dlsym(ctx->library, AMF_INIT_FUNCTION_NAME);
135    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);
136
137    version_fun = (AMFQueryVersion_Fn)dlsym(ctx->library, AMF_QUERY_VERSION_FUNCTION_NAME);
138    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);
139
140    res = version_fun(&ctx->version);
141    AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "%s failed with error %d\n", AMF_QUERY_VERSION_FUNCTION_NAME, res);
142    res = init_fun(AMF_FULL_VERSION, &ctx->factory);
143    AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "%s failed with error %d\n", AMF_INIT_FUNCTION_NAME, res);
144    res = ctx->factory->pVtbl->GetTrace(ctx->factory, &ctx->trace);
145    AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "GetTrace() failed with error %d\n", res);
146    res = ctx->factory->pVtbl->GetDebug(ctx->factory, &ctx->debug);
147    AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "GetDebug() failed with error %d\n", res);
148    return 0;
149}
150
151#if CONFIG_D3D11VA
152static int amf_init_from_d3d11_device(AVCodecContext *avctx, AVD3D11VADeviceContext *hwctx)
153{
154    AmfContext *ctx = avctx->priv_data;
155    AMF_RESULT res;
156
157    res = ctx->context->pVtbl->InitDX11(ctx->context, hwctx->device, AMF_DX11_1);
158    if (res != AMF_OK) {
159        if (res == AMF_NOT_SUPPORTED)
160            av_log(avctx, AV_LOG_ERROR, "AMF via D3D11 is not supported on the given device.\n");
161        else
162            av_log(avctx, AV_LOG_ERROR, "AMF failed to initialise on the given D3D11 device: %d.\n", res);
163        return AVERROR(ENODEV);
164    }
165
166    return 0;
167}
168#endif
169
170#if CONFIG_DXVA2
171static int amf_init_from_dxva2_device(AVCodecContext *avctx, AVDXVA2DeviceContext *hwctx)
172{
173    AmfContext *ctx = avctx->priv_data;
174    HANDLE device_handle;
175    IDirect3DDevice9 *device;
176    HRESULT hr;
177    AMF_RESULT res;
178    int ret;
179
180    hr = IDirect3DDeviceManager9_OpenDeviceHandle(hwctx->devmgr, &device_handle);
181    if (FAILED(hr)) {
182        av_log(avctx, AV_LOG_ERROR, "Failed to open device handle for Direct3D9 device: %lx.\n", (unsigned long)hr);
183        return AVERROR_EXTERNAL;
184    }
185
186    hr = IDirect3DDeviceManager9_LockDevice(hwctx->devmgr, device_handle, &device, FALSE);
187    if (SUCCEEDED(hr)) {
188        IDirect3DDeviceManager9_UnlockDevice(hwctx->devmgr, device_handle, FALSE);
189        ret = 0;
190    } else {
191        av_log(avctx, AV_LOG_ERROR, "Failed to lock device handle for Direct3D9 device: %lx.\n", (unsigned long)hr);
192        ret = AVERROR_EXTERNAL;
193    }
194
195    IDirect3DDeviceManager9_CloseDeviceHandle(hwctx->devmgr, device_handle);
196
197    if (ret < 0)
198        return ret;
199
200    res = ctx->context->pVtbl->InitDX9(ctx->context, device);
201
202    IDirect3DDevice9_Release(device);
203
204    if (res != AMF_OK) {
205        if (res == AMF_NOT_SUPPORTED)
206            av_log(avctx, AV_LOG_ERROR, "AMF via D3D9 is not supported on the given device.\n");
207        else
208            av_log(avctx, AV_LOG_ERROR, "AMF failed to initialise on given D3D9 device: %d.\n", res);
209        return AVERROR(ENODEV);
210    }
211
212    return 0;
213}
214#endif
215
216static int amf_init_context(AVCodecContext *avctx)
217{
218    AmfContext *ctx = avctx->priv_data;
219    AMFContext1 *context1 = NULL;
220    AMF_RESULT  res;
221    av_unused int ret;
222
223    ctx->hwsurfaces_in_queue = 0;
224    ctx->hwsurfaces_in_queue_max = 16;
225
226    // configure AMF logger
227    // the return of these functions indicates old state and do not affect behaviour
228    ctx->trace->pVtbl->EnableWriter(ctx->trace, AMF_TRACE_WRITER_DEBUG_OUTPUT, ctx->log_to_dbg != 0 );
229    if (ctx->log_to_dbg)
230        ctx->trace->pVtbl->SetWriterLevel(ctx->trace, AMF_TRACE_WRITER_DEBUG_OUTPUT, AMF_TRACE_TRACE);
231    ctx->trace->pVtbl->EnableWriter(ctx->trace, AMF_TRACE_WRITER_CONSOLE, 0);
232    ctx->trace->pVtbl->SetGlobalLevel(ctx->trace, AMF_TRACE_TRACE);
233
234    // connect AMF logger to av_log
235    ctx->tracer.vtbl = &tracer_vtbl;
236    ctx->tracer.avctx = avctx;
237    ctx->trace->pVtbl->RegisterWriter(ctx->trace, FFMPEG_AMF_WRITER_ID,(AMFTraceWriter*)&ctx->tracer, 1);
238    ctx->trace->pVtbl->SetWriterLevel(ctx->trace, FFMPEG_AMF_WRITER_ID, AMF_TRACE_TRACE);
239
240    res = ctx->factory->pVtbl->CreateContext(ctx->factory, &ctx->context);
241    AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "CreateContext() failed with error %d\n", res);
242
243    // If a device was passed to the encoder, try to initialise from that.
244    if (avctx->hw_frames_ctx) {
245        AVHWFramesContext *frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data;
246
247        if (amf_av_to_amf_format(frames_ctx->sw_format) == AMF_SURFACE_UNKNOWN) {
248            av_log(avctx, AV_LOG_ERROR, "Format of input frames context (%s) is not supported by AMF.\n",
249                   av_get_pix_fmt_name(frames_ctx->sw_format));
250            return AVERROR(EINVAL);
251        }
252
253        switch (frames_ctx->device_ctx->type) {
254#if CONFIG_D3D11VA
255        case AV_HWDEVICE_TYPE_D3D11VA:
256            ret = amf_init_from_d3d11_device(avctx, frames_ctx->device_ctx->hwctx);
257            if (ret < 0)
258                return ret;
259            break;
260#endif
261#if CONFIG_DXVA2
262        case AV_HWDEVICE_TYPE_DXVA2:
263            ret = amf_init_from_dxva2_device(avctx, frames_ctx->device_ctx->hwctx);
264            if (ret < 0)
265                return ret;
266            break;
267#endif
268        default:
269            av_log(avctx, AV_LOG_ERROR, "AMF initialisation from a %s frames context is not supported.\n",
270                   av_hwdevice_get_type_name(frames_ctx->device_ctx->type));
271            return AVERROR(ENOSYS);
272        }
273
274        ctx->hw_frames_ctx = av_buffer_ref(avctx->hw_frames_ctx);
275        if (!ctx->hw_frames_ctx)
276            return AVERROR(ENOMEM);
277
278        if (frames_ctx->initial_pool_size > 0)
279            ctx->hwsurfaces_in_queue_max = frames_ctx->initial_pool_size - 1;
280
281    } else if (avctx->hw_device_ctx) {
282        AVHWDeviceContext *device_ctx = (AVHWDeviceContext*)avctx->hw_device_ctx->data;
283
284        switch (device_ctx->type) {
285#if CONFIG_D3D11VA
286        case AV_HWDEVICE_TYPE_D3D11VA:
287            ret = amf_init_from_d3d11_device(avctx, device_ctx->hwctx);
288            if (ret < 0)
289                return ret;
290            break;
291#endif
292#if CONFIG_DXVA2
293        case AV_HWDEVICE_TYPE_DXVA2:
294            ret = amf_init_from_dxva2_device(avctx, device_ctx->hwctx);
295            if (ret < 0)
296                return ret;
297            break;
298#endif
299        default:
300            av_log(avctx, AV_LOG_ERROR, "AMF initialisation from a %s device is not supported.\n",
301                   av_hwdevice_get_type_name(device_ctx->type));
302            return AVERROR(ENOSYS);
303        }
304
305        ctx->hw_device_ctx = av_buffer_ref(avctx->hw_device_ctx);
306        if (!ctx->hw_device_ctx)
307            return AVERROR(ENOMEM);
308
309    } else {
310        res = ctx->context->pVtbl->InitDX11(ctx->context, NULL, AMF_DX11_1);
311        if (res == AMF_OK) {
312            av_log(avctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via D3D11.\n");
313        } else {
314            res = ctx->context->pVtbl->InitDX9(ctx->context, NULL);
315            if (res == AMF_OK) {
316                av_log(avctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via D3D9.\n");
317            } else {
318                AMFGuid guid = IID_AMFContext1();
319                res = ctx->context->pVtbl->QueryInterface(ctx->context, &guid, (void**)&context1);
320                AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "CreateContext1() failed with error %d\n", res);
321
322                res = context1->pVtbl->InitVulkan(context1, NULL);
323                context1->pVtbl->Release(context1);
324                if (res != AMF_OK) {
325                    if (res == AMF_NOT_SUPPORTED)
326                        av_log(avctx, AV_LOG_ERROR, "AMF via Vulkan is not supported on the given device.\n");
327                    else
328                        av_log(avctx, AV_LOG_ERROR, "AMF failed to initialise on the given Vulkan device: %d.\n", res);
329                    return AVERROR(ENOSYS);
330                }
331                av_log(avctx, AV_LOG_VERBOSE, "AMF initialisation succeeded via Vulkan.\n");
332            }
333        }
334    }
335    return 0;
336}
337
338static int amf_init_encoder(AVCodecContext *avctx)
339{
340    AmfContext        *ctx = avctx->priv_data;
341    const wchar_t     *codec_id = NULL;
342    AMF_RESULT         res;
343    enum AVPixelFormat pix_fmt;
344
345    switch (avctx->codec->id) {
346        case AV_CODEC_ID_H264:
347            codec_id = AMFVideoEncoderVCE_AVC;
348            break;
349        case AV_CODEC_ID_HEVC:
350            codec_id = AMFVideoEncoder_HEVC;
351            break;
352        default:
353            break;
354    }
355    AMF_RETURN_IF_FALSE(ctx, codec_id != NULL, AVERROR(EINVAL), "Codec %d is not supported\n", avctx->codec->id);
356
357    if (ctx->hw_frames_ctx)
358        pix_fmt = ((AVHWFramesContext*)ctx->hw_frames_ctx->data)->sw_format;
359    else
360        pix_fmt = avctx->pix_fmt;
361
362    ctx->format = amf_av_to_amf_format(pix_fmt);
363    AMF_RETURN_IF_FALSE(ctx, ctx->format != AMF_SURFACE_UNKNOWN, AVERROR(EINVAL),
364                        "Format %s is not supported\n", av_get_pix_fmt_name(pix_fmt));
365
366    res = ctx->factory->pVtbl->CreateComponent(ctx->factory, ctx->context, codec_id, &ctx->encoder);
367    AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_ENCODER_NOT_FOUND, "CreateComponent(%ls) failed with error %d\n", codec_id, res);
368
369    return 0;
370}
371
372int av_cold ff_amf_encode_close(AVCodecContext *avctx)
373{
374    AmfContext *ctx = avctx->priv_data;
375
376    if (ctx->delayed_surface) {
377        ctx->delayed_surface->pVtbl->Release(ctx->delayed_surface);
378        ctx->delayed_surface = NULL;
379    }
380
381    if (ctx->encoder) {
382        ctx->encoder->pVtbl->Terminate(ctx->encoder);
383        ctx->encoder->pVtbl->Release(ctx->encoder);
384        ctx->encoder = NULL;
385    }
386
387    if (ctx->context) {
388        ctx->context->pVtbl->Terminate(ctx->context);
389        ctx->context->pVtbl->Release(ctx->context);
390        ctx->context = NULL;
391    }
392    av_buffer_unref(&ctx->hw_device_ctx);
393    av_buffer_unref(&ctx->hw_frames_ctx);
394
395    if (ctx->trace) {
396        ctx->trace->pVtbl->UnregisterWriter(ctx->trace, FFMPEG_AMF_WRITER_ID);
397    }
398    if (ctx->library) {
399        dlclose(ctx->library);
400        ctx->library = NULL;
401    }
402    ctx->trace = NULL;
403    ctx->debug = NULL;
404    ctx->factory = NULL;
405    ctx->version = 0;
406    ctx->delayed_drain = 0;
407    av_frame_free(&ctx->delayed_frame);
408    av_fifo_freep2(&ctx->timestamp_list);
409
410    return 0;
411}
412
413static int amf_copy_surface(AVCodecContext *avctx, const AVFrame *frame,
414    AMFSurface* surface)
415{
416    AMFPlane *plane;
417    uint8_t  *dst_data[4];
418    int       dst_linesize[4];
419    int       planes;
420    int       i;
421
422    planes = surface->pVtbl->GetPlanesCount(surface);
423    av_assert0(planes < FF_ARRAY_ELEMS(dst_data));
424
425    for (i = 0; i < planes; i++) {
426        plane = surface->pVtbl->GetPlaneAt(surface, i);
427        dst_data[i] = plane->pVtbl->GetNative(plane);
428        dst_linesize[i] = plane->pVtbl->GetHPitch(plane);
429    }
430    av_image_copy(dst_data, dst_linesize,
431        (const uint8_t**)frame->data, frame->linesize, frame->format,
432        avctx->width, avctx->height);
433
434    return 0;
435}
436
437static int amf_copy_buffer(AVCodecContext *avctx, AVPacket *pkt, AMFBuffer *buffer)
438{
439    AmfContext      *ctx = avctx->priv_data;
440    int              ret;
441    AMFVariantStruct var = {0};
442    int64_t          timestamp = AV_NOPTS_VALUE;
443    int64_t          size = buffer->pVtbl->GetSize(buffer);
444
445    if ((ret = ff_get_encode_buffer(avctx, pkt, size, 0)) < 0) {
446        return ret;
447    }
448    memcpy(pkt->data, buffer->pVtbl->GetNative(buffer), size);
449
450    switch (avctx->codec->id) {
451        case AV_CODEC_ID_H264:
452            buffer->pVtbl->GetProperty(buffer, AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE, &var);
453            if(var.int64Value == AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE_IDR) {
454                pkt->flags = AV_PKT_FLAG_KEY;
455            }
456            break;
457        case AV_CODEC_ID_HEVC:
458            buffer->pVtbl->GetProperty(buffer, AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE, &var);
459            if (var.int64Value == AMF_VIDEO_ENCODER_HEVC_OUTPUT_DATA_TYPE_IDR) {
460                pkt->flags = AV_PKT_FLAG_KEY;
461            }
462            break;
463        default:
464            break;
465    }
466
467    buffer->pVtbl->GetProperty(buffer, PTS_PROP, &var);
468
469    pkt->pts = var.int64Value; // original pts
470
471
472    AMF_RETURN_IF_FALSE(ctx, av_fifo_read(ctx->timestamp_list, &timestamp, 1) >= 0,
473                        AVERROR_UNKNOWN, "timestamp_list is empty\n");
474
475    // calc dts shift if max_b_frames > 0
476    if (avctx->max_b_frames > 0 && ctx->dts_delay == 0) {
477        int64_t timestamp_last = AV_NOPTS_VALUE;
478        size_t can_read = av_fifo_can_read(ctx->timestamp_list);
479
480        AMF_RETURN_IF_FALSE(ctx, can_read > 0, AVERROR_UNKNOWN,
481            "timestamp_list is empty while max_b_frames = %d\n", avctx->max_b_frames);
482        av_fifo_peek(ctx->timestamp_list, &timestamp_last, 1, can_read - 1);
483        if (timestamp < 0 || timestamp_last < AV_NOPTS_VALUE) {
484            return AVERROR(ERANGE);
485        }
486        ctx->dts_delay = timestamp_last - timestamp;
487    }
488    pkt->dts = timestamp - ctx->dts_delay;
489    return 0;
490}
491
492// amfenc API implementation
493int ff_amf_encode_init(AVCodecContext *avctx)
494{
495    int ret;
496
497    if ((ret = amf_load_library(avctx)) == 0) {
498        if ((ret = amf_init_context(avctx)) == 0) {
499            if ((ret = amf_init_encoder(avctx)) == 0) {
500                return 0;
501            }
502        }
503    }
504    ff_amf_encode_close(avctx);
505    return ret;
506}
507
508static AMF_RESULT amf_set_property_buffer(AMFSurface *object, const wchar_t *name, AMFBuffer *val)
509{
510    AMF_RESULT res;
511    AMFVariantStruct var;
512    res = AMFVariantInit(&var);
513    if (res == AMF_OK) {
514        AMFGuid guid_AMFInterface = IID_AMFInterface();
515        AMFInterface *amf_interface;
516        res = val->pVtbl->QueryInterface(val, &guid_AMFInterface, (void**)&amf_interface);
517
518        if (res == AMF_OK) {
519            res = AMFVariantAssignInterface(&var, amf_interface);
520            amf_interface->pVtbl->Release(amf_interface);
521        }
522        if (res == AMF_OK) {
523            res = object->pVtbl->SetProperty(object, name, var);
524        }
525        AMFVariantClear(&var);
526    }
527    return res;
528}
529
530static AMF_RESULT amf_get_property_buffer(AMFData *object, const wchar_t *name, AMFBuffer **val)
531{
532    AMF_RESULT res;
533    AMFVariantStruct var;
534    res = AMFVariantInit(&var);
535    if (res == AMF_OK) {
536        res = object->pVtbl->GetProperty(object, name, &var);
537        if (res == AMF_OK) {
538            if (var.type == AMF_VARIANT_INTERFACE) {
539                AMFGuid guid_AMFBuffer = IID_AMFBuffer();
540                AMFInterface *amf_interface = AMFVariantInterface(&var);
541                res = amf_interface->pVtbl->QueryInterface(amf_interface, &guid_AMFBuffer, (void**)val);
542            } else {
543                res = AMF_INVALID_DATA_TYPE;
544            }
545        }
546        AMFVariantClear(&var);
547    }
548    return res;
549}
550
551static AMFBuffer *amf_create_buffer_with_frame_ref(const AVFrame *frame, AMFContext *context)
552{
553    AVFrame *frame_ref;
554    AMFBuffer *frame_ref_storage_buffer = NULL;
555    AMF_RESULT res;
556
557    res = context->pVtbl->AllocBuffer(context, AMF_MEMORY_HOST, sizeof(frame_ref), &frame_ref_storage_buffer);
558    if (res == AMF_OK) {
559        frame_ref = av_frame_clone(frame);
560        if (frame_ref) {
561            memcpy(frame_ref_storage_buffer->pVtbl->GetNative(frame_ref_storage_buffer), &frame_ref, sizeof(frame_ref));
562        } else {
563            frame_ref_storage_buffer->pVtbl->Release(frame_ref_storage_buffer);
564            frame_ref_storage_buffer = NULL;
565        }
566    }
567    return frame_ref_storage_buffer;
568}
569
570static void amf_release_buffer_with_frame_ref(AMFBuffer *frame_ref_storage_buffer)
571{
572    AVFrame *frame_ref;
573    memcpy(&frame_ref, frame_ref_storage_buffer->pVtbl->GetNative(frame_ref_storage_buffer), sizeof(frame_ref));
574    av_frame_free(&frame_ref);
575    frame_ref_storage_buffer->pVtbl->Release(frame_ref_storage_buffer);
576}
577
578int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
579{
580    AmfContext *ctx = avctx->priv_data;
581    AMFSurface *surface;
582    AMF_RESULT  res;
583    int         ret;
584    AMF_RESULT  res_query;
585    AMFData    *data = NULL;
586    AVFrame    *frame = ctx->delayed_frame;
587    int         block_and_wait;
588
589    if (!ctx->encoder)
590        return AVERROR(EINVAL);
591
592    if (!frame->buf[0]) {
593        ret = ff_encode_get_frame(avctx, frame);
594        if (ret < 0 && ret != AVERROR_EOF)
595            return ret;
596    }
597
598    if (!frame->buf[0]) { // submit drain
599        if (!ctx->eof) { // submit drain one time only
600            if (ctx->delayed_surface != NULL) {
601                ctx->delayed_drain = 1; // input queue is full: resubmit Drain() in ff_amf_receive_packet
602            } else if(!ctx->delayed_drain) {
603                res = ctx->encoder->pVtbl->Drain(ctx->encoder);
604                if (res == AMF_INPUT_FULL) {
605                    ctx->delayed_drain = 1; // input queue is full: resubmit Drain() in ff_amf_receive_packet
606                } else {
607                    if (res == AMF_OK) {
608                        ctx->eof = 1; // drain started
609                    }
610                    AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "Drain() failed with error %d\n", res);
611                }
612            }
613        }
614    } else if (!ctx->delayed_surface) { // submit frame
615        int hw_surface = 0;
616
617        // prepare surface from frame
618        switch (frame->format) {
619#if CONFIG_D3D11VA
620        case AV_PIX_FMT_D3D11:
621            {
622                static const GUID AMFTextureArrayIndexGUID = { 0x28115527, 0xe7c3, 0x4b66, { 0x99, 0xd3, 0x4f, 0x2a, 0xe6, 0xb4, 0x7f, 0xaf } };
623                ID3D11Texture2D *texture = (ID3D11Texture2D*)frame->data[0]; // actual texture
624                int index = (intptr_t)frame->data[1]; // index is a slice in texture array is - set to tell AMF which slice to use
625
626                av_assert0(frame->hw_frames_ctx       && ctx->hw_frames_ctx &&
627                           frame->hw_frames_ctx->data == ctx->hw_frames_ctx->data);
628
629                texture->lpVtbl->SetPrivateData(texture, &AMFTextureArrayIndexGUID, sizeof(index), &index);
630
631                res = ctx->context->pVtbl->CreateSurfaceFromDX11Native(ctx->context, texture, &surface, NULL); // wrap to AMF surface
632                AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR(ENOMEM), "CreateSurfaceFromDX11Native() failed  with error %d\n", res);
633
634                hw_surface = 1;
635            }
636            break;
637#endif
638#if CONFIG_DXVA2
639        case AV_PIX_FMT_DXVA2_VLD:
640            {
641                IDirect3DSurface9 *texture = (IDirect3DSurface9 *)frame->data[3]; // actual texture
642
643                res = ctx->context->pVtbl->CreateSurfaceFromDX9Native(ctx->context, texture, &surface, NULL); // wrap to AMF surface
644                AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR(ENOMEM), "CreateSurfaceFromDX9Native() failed  with error %d\n", res);
645
646                hw_surface = 1;
647            }
648            break;
649#endif
650        default:
651            {
652                res = ctx->context->pVtbl->AllocSurface(ctx->context, AMF_MEMORY_HOST, ctx->format, avctx->width, avctx->height, &surface);
653                AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR(ENOMEM), "AllocSurface() failed  with error %d\n", res);
654                amf_copy_surface(avctx, frame, surface);
655            }
656            break;
657        }
658
659        if (hw_surface) {
660            AMFBuffer *frame_ref_storage_buffer;
661
662            // input HW surfaces can be vertically aligned by 16; tell AMF the real size
663            surface->pVtbl->SetCrop(surface, 0, 0, frame->width, frame->height);
664
665            frame_ref_storage_buffer = amf_create_buffer_with_frame_ref(frame, ctx->context);
666            AMF_RETURN_IF_FALSE(ctx, frame_ref_storage_buffer != NULL, AVERROR(ENOMEM), "create_buffer_with_frame_ref() returned NULL\n");
667
668            res = amf_set_property_buffer(surface, L"av_frame_ref", frame_ref_storage_buffer);
669            AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "SetProperty failed for \"av_frame_ref\" with error %d\n", res);
670            ctx->hwsurfaces_in_queue++;
671            frame_ref_storage_buffer->pVtbl->Release(frame_ref_storage_buffer);
672        }
673
674        surface->pVtbl->SetPts(surface, frame->pts);
675        AMF_ASSIGN_PROPERTY_INT64(res, surface, PTS_PROP, frame->pts);
676
677        switch (avctx->codec->id) {
678        case AV_CODEC_ID_H264:
679            AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_ENCODER_INSERT_AUD, !!ctx->aud);
680            break;
681        case AV_CODEC_ID_HEVC:
682            AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_ENCODER_HEVC_INSERT_AUD, !!ctx->aud);
683            break;
684        default:
685            break;
686        }
687
688        // submit surface
689        res = ctx->encoder->pVtbl->SubmitInput(ctx->encoder, (AMFData*)surface);
690        if (res == AMF_INPUT_FULL) { // handle full queue
691            //store surface for later submission
692            ctx->delayed_surface = surface;
693        } else {
694            int64_t pts = frame->pts;
695            surface->pVtbl->Release(surface);
696            AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "SubmitInput() failed with error %d\n", res);
697
698            av_frame_unref(frame);
699            ret = av_fifo_write(ctx->timestamp_list, &pts, 1);
700            if (ret < 0)
701                return ret;
702        }
703    }
704
705
706    do {
707        block_and_wait = 0;
708        // poll data
709        res_query = ctx->encoder->pVtbl->QueryOutput(ctx->encoder, &data);
710        if (data) {
711            // copy data to packet
712            AMFBuffer* buffer;
713            AMFGuid guid = IID_AMFBuffer();
714            data->pVtbl->QueryInterface(data, &guid, (void**)&buffer); // query for buffer interface
715            ret = amf_copy_buffer(avctx, avpkt, buffer);
716
717            buffer->pVtbl->Release(buffer);
718
719            if (data->pVtbl->HasProperty(data, L"av_frame_ref")) {
720                AMFBuffer *frame_ref_storage_buffer;
721                res = amf_get_property_buffer(data, L"av_frame_ref", &frame_ref_storage_buffer);
722                AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "GetProperty failed for \"av_frame_ref\" with error %d\n", res);
723                amf_release_buffer_with_frame_ref(frame_ref_storage_buffer);
724                ctx->hwsurfaces_in_queue--;
725            }
726
727            data->pVtbl->Release(data);
728
729            AMF_RETURN_IF_FALSE(ctx, ret >= 0, ret, "amf_copy_buffer() failed with error %d\n", ret);
730
731            if (ctx->delayed_surface != NULL) { // try to resubmit frame
732                res = ctx->encoder->pVtbl->SubmitInput(ctx->encoder, (AMFData*)ctx->delayed_surface);
733                if (res != AMF_INPUT_FULL) {
734                    int64_t pts = ctx->delayed_surface->pVtbl->GetPts(ctx->delayed_surface);
735                    ctx->delayed_surface->pVtbl->Release(ctx->delayed_surface);
736                    ctx->delayed_surface = NULL;
737                    av_frame_unref(ctx->delayed_frame);
738                    AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "Repeated SubmitInput() failed with error %d\n", res);
739
740                    ret = av_fifo_write(ctx->timestamp_list, &pts, 1);
741                    if (ret < 0)
742                        return ret;
743                } else {
744                    av_log(avctx, AV_LOG_WARNING, "Data acquired but delayed frame submission got AMF_INPUT_FULL- should not happen\n");
745                }
746            } else if (ctx->delayed_drain) { // try to resubmit drain
747                res = ctx->encoder->pVtbl->Drain(ctx->encoder);
748                if (res != AMF_INPUT_FULL) {
749                    ctx->delayed_drain = 0;
750                    ctx->eof = 1; // drain started
751                    AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "Repeated Drain() failed with error %d\n", res);
752                } else {
753                    av_log(avctx, AV_LOG_WARNING, "Data acquired but delayed drain submission got AMF_INPUT_FULL- should not happen\n");
754                }
755            }
756        } else if (ctx->delayed_surface != NULL || ctx->delayed_drain || (ctx->eof && res_query != AMF_EOF) || (ctx->hwsurfaces_in_queue >= ctx->hwsurfaces_in_queue_max)) {
757            block_and_wait = 1;
758            av_usleep(1000); // wait and poll again
759        }
760    } while (block_and_wait);
761
762    if (res_query == AMF_EOF) {
763        ret = AVERROR_EOF;
764    } else if (data == NULL) {
765        ret = AVERROR(EAGAIN);
766    } else {
767        ret = 0;
768    }
769    return ret;
770}
771
772const AVCodecHWConfigInternal *const ff_amfenc_hw_configs[] = {
773#if CONFIG_D3D11VA
774    HW_CONFIG_ENCODER_FRAMES(D3D11, D3D11VA),
775    HW_CONFIG_ENCODER_DEVICE(NONE,  D3D11VA),
776#endif
777#if CONFIG_DXVA2
778    HW_CONFIG_ENCODER_FRAMES(DXVA2_VLD, DXVA2),
779    HW_CONFIG_ENCODER_DEVICE(NONE,      DXVA2),
780#endif
781    NULL,
782};
783