Hello,
I am collaborating with AMD to integrate and support AMF integration FFmpeg
This patch solves issue with frame corruption if encoder connected directly to
decoder
Solution: storing frame reference while it is used during encoding
From 0fae3679bae8e121ed7b997d7eabd533deb113fb Mon Sep 17 00:00:00 2001
From: Alexander Kravchenko
Date: Fri, 23 Mar 2018 12:50:21 +0300
Subject: [PATCH] AMF Encoder: fix issue with frame corruption if encoder
connected directly to decoder. Solution: storing frame reference while it is
used during encoding
---
libavcodec/amfenc.c | 64 ++---
1 file changed, 61 insertions(+), 3 deletions(-)
diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c
index 89a10ff253..9ffc52b0b0 100644
--- a/libavcodec/amfenc.c
+++ b/libavcodec/amfenc.c
@@ -443,6 +443,41 @@ int ff_amf_encode_init(AVCodecContext *avctx)
return ret;
}
+#define AV_AMF_QUERY_INTERFACE(res, from, InterfaceType, to ) \
+{ \
+AMFGuid guid_##InterfaceType = IID_##InterfaceType(); \
+res = from->pVtbl->QueryInterface(from, &guid_##InterfaceType,
(void**)&to); \
+}
+
+#define AV_AMF_ASSIGN_PROPERTY_INTERFACE(res, pThis, name, val ) \
+{ \
+AMFInterface *amf_interface; \
+AV_AMF_QUERY_INTERFACE(res, val, AMFInterface, amf_interface)\
+if(res == AMF_OK) { \
+AMFVariantStruct var; \
+AMFVariantInit(&var); \
+AMFVariantAssignInterface(&var, amf_interface); \
+amf_interface->pVtbl->Release(amf_interface); \
+res = pThis->pVtbl->SetProperty(pThis, name, var); \
+AMFVariantClear(&var); \
+} \
+}
+
+#define AV_AMF_GET_PROPERTY_INTERFACE(res, pThis, name, target_type, val) \
+{ \
+AMFVariantStruct var; \
+AMFVariantInit(&var); \
+res = pThis->pVtbl->GetProperty(pThis, name, &var); \
+if(res == AMF_OK) { \
+if(var.type == AMF_VARIANT_INTERFACE && var.pInterface) { \
+AMFGuid guid = IID_##target_type(); \
+res = var.pInterface->pVtbl->QueryInterface(var.pInterface,
&guid, (void**)&val); \
+} else { \
+res = AMF_INVALID_DATA_TYPE; \
+} \
+AMFVariantClear(&var); \
+} \
+}
int ff_amf_send_frame(AVCodecContext *avctx, const AVFrame *frame)
{
@@ -484,6 +519,8 @@ int ff_amf_send_frame(AVCodecContext *avctx, const AVFrame
*frame)
(ctx->hw_device_ctx &&
((AVHWFramesContext*)frame->hw_frames_ctx->data)->device_ctx ==
(AVHWDeviceContext*)ctx->hw_device_ctx->data)
)) {
+AVFrame* frame_ref = av_frame_clone(frame);
+AMFBuffer* frame_ref_storage_buffer;
#if CONFIG_D3D11VA
static const GUID AMFTextureArrayIndexGUID = { 0x28115527, 0xe7c3,
0x4b66, { 0x99, 0xd3, 0x4f, 0x2a, 0xe6, 0xb4, 0x7f, 0xaf } };
ID3D11Texture2D *texture = (ID3D11Texture2D*)frame->data[0]; //
actual texture
@@ -496,6 +533,12 @@ int ff_amf_send_frame(AVCodecContext *avctx, const AVFrame
*frame)
// input HW surfaces can be vertically aligned by 16; tell AMF the
real size
surface->pVtbl->SetCrop(surface, 0, 0, frame->width,
frame->height);
#endif
+res = ctx->context->pVtbl->AllocBuffer(ctx->context,
AMF_MEMORY_HOST, sizeof(frame_ref), &frame_ref_storage_buffer);
+AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR(ENOMEM),
"AllocBuffer() failed with error %d\n", res);
+
memcpy(frame_ref_storage_buffer->pVtbl->GetNative(frame_ref_storage_buffer),
&frame_ref, sizeof(frame_ref));
+
+AV_AMF_ASSIGN_PROPERTY_INTERFACE(res, surface, L"av_frame_ref",
frame_ref_storage_buffer);
+frame_ref_storage_buffer->pVtbl->Release(frame_ref_storage_buffer);
} else {
res = ctx->context->pVtbl->AllocSurface(ctx->context,
AMF_MEMORY_HOST, ctx->format, avctx->width, avctx->height, &surface);
AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR(ENOMEM),
"AllocSurface() failed with error %d\n", res);
@@ -555,11 +598,26 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket
*avpkt)
if (data) {
// copy data to packet
AMFBuffer* buffer;
-AMFGuid guid = IID_AMFBuffer();
-data->pVtbl->QueryInterface(data, &guid, (void**)&buffer); //
query for buffer interface
+AV_AMF_QUERY_INTERFACE(res, data, AMFBuffer, buffer);
+AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "Invalid
data type from encoder->QueryOutput, should be AMFBuffer, error %d\n", res);
ret = amf_copy_buffer(avctx, avpkt, buffer);
-
buffer->pVtbl->Release(buffer);
+
+//try to get attached av_frame_ref and unref
+if(data->pVtbl->HasProperty(data, L"av_frame_ref")) {
+AMFBuffer *frame_ref_storage_buffer = NULL;
+AVFrame *av_fr