diff mbox

[FFmpeg-devel] lavc/amfenc: Reference to input AVFrame (hwaccel) is retained during the encoding process

Message ID 002401d3c4ee$fada2fe0$f08e8fa0$@gmail.com
State Superseded
Headers show

Commit Message

Alexander Kravchenko March 26, 2018, 10:41 a.m. UTC
Hello,
I have fixed issues listed in previous patch.


> Say what the change is in the title.  Something like "amfenc: Retain a reference to D3D11 frames used as input during the encoding
> process", maybe?
Sure, but I am preparing next patch adding DX9 support, so probably better to write D3D instead D3D11

> 
> How many frames can end up queued inside the encoder here?
16

> 
> Is there always an exact 1->1 correspondence between input frames and output packets?  That is, is it guaranteed that no frames are
> ever dropped, even in the low-latency modes?
yes

> Put the * in the right place - it's part of the declarator, not the declaration-specifiers.
> "if (", and in all places below too.
I have fixed these issues in whole file (Hopefully you don’t mind if it put to same commit. There aren't many pf them)


From: Alexander Kravchenko <akravchenko188@gmail.com>
---
 libavcodec/amfenc.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 81 insertions(+), 8 deletions(-)

Comments

Michael Niedermayer March 26, 2018, 9:50 p.m. UTC | #1
On Mon, Mar 26, 2018 at 01:41:20PM +0300, Alexander Kravchenko wrote:
> Hello,
> I have fixed issues listed in previous patch.
> 
> 
> > Say what the change is in the title.  Something like "amfenc: Retain a reference to D3D11 frames used as input during the encoding
> > process", maybe?
> Sure, but I am preparing next patch adding DX9 support, so probably better to write D3D instead D3D11
> 
> > 
> > How many frames can end up queued inside the encoder here?
> 16
> 
> > 
> > Is there always an exact 1->1 correspondence between input frames and output packets?  That is, is it guaranteed that no frames are
> > ever dropped, even in the low-latency modes?
> yes
> 
> > Put the * in the right place - it's part of the declarator, not the declaration-specifiers.
> > "if (", and in all places below too.
> I have fixed these issues in whole file (Hopefully you don’t mind if it put to same commit. There aren't many pf them)
> 
> 

> From: Alexander Kravchenko <akravchenko188@gmail.com>
> ---
>  libavcodec/amfenc.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 81 insertions(+), 8 deletions(-)
> 
> diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c
> index 89a10ff253..f532a32b7b 100644
> --- a/libavcodec/amfenc.c
> +++ b/libavcodec/amfenc.c
> @@ -188,7 +188,7 @@ static int amf_init_context(AVCodecContext *avctx)
>                              return AVERROR(ENOMEM);
>                          }
>                      } else {
> -                        if(res == AMF_NOT_SUPPORTED)
> +                        if (res == AMF_NOT_SUPPORTED)
>                              av_log(avctx, AV_LOG_INFO, "avctx->hw_frames_ctx has D3D11 device which doesn't have D3D11VA interface, switching to default\n");
>                          else
>                              av_log(avctx, AV_LOG_INFO, "avctx->hw_frames_ctx has non-AMD device, switching to default\n");
> @@ -298,7 +298,7 @@ int av_cold ff_amf_encode_close(AVCodecContext *avctx)
>  }
>  
>  static int amf_copy_surface(AVCodecContext *avctx, const AVFrame *frame,
> -    AMFSurface* surface)
> +    AMFSurface *surface)
>  {
>      AVFrame        *sw_frame = NULL;
>      AMFPlane       *plane = NULL;

These changes are unrelated and should not be in this patch


[...]
James Almer March 26, 2018, 9:55 p.m. UTC | #2
On 3/26/2018 7:41 AM, Alexander Kravchenko wrote:
> Hello,
> I have fixed issues listed in previous patch.
> 
> 
>> Say what the change is in the title.  Something like "amfenc: Retain a reference to D3D11 frames used as input during the encoding
>> process", maybe?
> Sure, but I am preparing next patch adding DX9 support, so probably better to write D3D instead D3D11
> 
>>
>> How many frames can end up queued inside the encoder here?
> 16
> 
>>
>> Is there always an exact 1->1 correspondence between input frames and output packets?  That is, is it guaranteed that no frames are
>> ever dropped, even in the low-latency modes?
> yes
> 
>> Put the * in the right place - it's part of the declarator, not the declaration-specifiers.
>> "if (", and in all places below too.
> I have fixed these issues in whole file (Hopefully you don’t mind if it put to same commit. There aren't many pf them)

Usually no, cosmetic changes on existing code go in their own separate
commits.

Also, if this is fixing the issue described in
https://trac.ffmpeg.org/ticket/6990 then please add a "Fixes ticket
#6990" to the commit message.
Mark Thompson March 26, 2018, 10:12 p.m. UTC | #3
On 26/03/18 11:41, Alexander Kravchenko wrote:

Put email comments on a patch below the "---" line (otherwise they end up in the commit message when applied).

> Hello,
> I have fixed issues listed in previous patch.
> 
> 
>> Say what the change is in the title.  Something like "amfenc: Retain a reference to D3D11 frames used as input during the encoding
>> process", maybe?
> Sure, but I am preparing next patch adding DX9 support, so probably better to write D3D instead D3D11

Well, it currently means D3D11 only, unless you are posting a D3D9 patch ahead of it?  Doesn't really matter, though.

>>
>> How many frames can end up queued inside the encoder here?
> 16

So a transcode from a file with a lot of references will fall over without extra_hw_frames being set on the decoder?  That probably wants more documentation somewhere, but it shouldn't block this patch.

>>
>> Is there always an exact 1->1 correspondence between input frames and output packets?  That is, is it guaranteed that no frames are
>> ever dropped, even in the low-latency modes?
> yes

Ok, good.

>> Put the * in the right place - it's part of the declarator, not the declaration-specifiers.
>> "if (", and in all places below too.
> I have fixed these issues in whole file (Hopefully you don’t mind if it put to same commit. There aren't many pf them)

I was meaning only in the code you are adding.  The others should be fixed, but it should be separate patch with no functional content (saying something like "fix cosmetic issues").

> 
> From: Alexander Kravchenko <akravchenko188@gmail.com>
> ---
>  libavcodec/amfenc.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 81 insertions(+), 8 deletions(-)
> 
> diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c
> index 89a10ff253..f532a32b7b 100644
> --- a/libavcodec/amfenc.c
> +++ b/libavcodec/amfenc.c
> @@ -188,7 +188,7 @@ static int amf_init_context(AVCodecContext *avctx)
>                              return AVERROR(ENOMEM);
>                          }
>                      } else {
> -                        if(res == AMF_NOT_SUPPORTED)
> +                        if (res == AMF_NOT_SUPPORTED)
>                              av_log(avctx, AV_LOG_INFO, "avctx->hw_frames_ctx has D3D11 device which doesn't have D3D11VA interface, switching to default\n");
>                          else
>                              av_log(avctx, AV_LOG_INFO, "avctx->hw_frames_ctx has non-AMD device, switching to default\n");
> @@ -298,7 +298,7 @@ int av_cold ff_amf_encode_close(AVCodecContext *avctx)
>  }
>  
>  static int amf_copy_surface(AVCodecContext *avctx, const AVFrame *frame,
> -    AMFSurface* surface)
> +    AMFSurface *surface)
>  {
>      AVFrame        *sw_frame = NULL;
>      AMFPlane       *plane = NULL;
> @@ -371,7 +371,7 @@ static int amf_copy_buffer(AVCodecContext *avctx, AVPacket *pkt, AMFBuffer *buff
>      switch (avctx->codec->id) {
>          case AV_CODEC_ID_H264:
>              buffer->pVtbl->GetProperty(buffer, AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE, &var);
> -            if(var.int64Value == AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE_IDR) {
> +            if (var.int64Value == AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE_IDR) {
>                  pkt->flags = AV_PKT_FLAG_KEY;
>              }
>              break;
> @@ -443,6 +443,48 @@ int ff_amf_encode_init(AVCodecContext *avctx)
>      return ret;
>  }
>  
> +#define AMF_AV_QUERY_INTERFACE(res, from, InterfaceTypeTo, to) \
> +    { \
> +        AMFGuid guid_##InterfaceTypeTo = IID_##InterfaceTypeTo(); \
> +        res = from->pVtbl->QueryInterface(from, &guid_##InterfaceTypeTo, (void**)&to); \
> +    }
> +
> +#define AMF_AV_ASSIGN_PROPERTY_INTERFACE(res, pThis, name, val) \
> +    { \
> +        AMFInterface *amf_interface; \
> +        AMFVariantStruct var; \
> +        res = AMFVariantInit(&var); \
> +        if (res != AMF_OK) \
> +            return res; \
> +        if (res == AMF_OK) { \
> +            AMF_AV_QUERY_INTERFACE(res, val, AMFInterface, amf_interface)\
> +        } \
> +        if (res == AMF_OK) { \
> +            res = AMFVariantAssignInterface(&var, amf_interface); \
> +            amf_interface->pVtbl->Release(amf_interface); \
> +        } \
> +        if (res == AMF_OK) { \
> +            res = pThis->pVtbl->SetProperty(pThis, name, var); \
> +        } \
> +        res = AMFVariantClear(&var); \
> +    }
> +
> +#define AMF_AV_GET_PROPERTY_INTERFACE(res, pThis, name, TargetType, val) \
> +    { \
> +        AMFVariantStruct var; \
> +        res = AMFVariantInit(&var); \
> +        if (res != AMF_OK) \
> +            return res; \
> +        res = pThis->pVtbl->GetProperty(pThis, name, &var); \
> +        if (res == AMF_OK) { \
> +            if (var.type == AMF_VARIANT_INTERFACE && AMFVariantInterface(&var)) { \
> +                AMF_AV_QUERY_INTERFACE(res, AMFVariantInterface(&var), TargetType, val); \
> +            } else { \
> +                res = AMF_INVALID_DATA_TYPE; \
> +            } \
> +        } \
> +        AMFVariantClear(&var); \
> +    }

These look even more like they should be functions rather than macros now?  In particular, the returns in them interact badly with the code below.

>  
>  int ff_amf_send_frame(AVCodecContext *avctx, const AVFrame *frame)
>  {
> @@ -458,7 +500,7 @@ int ff_amf_send_frame(AVCodecContext *avctx, const AVFrame *frame)
>          if (!ctx->eof) { // submit drain one time only
>              if (ctx->delayed_surface != NULL) {
>                  ctx->delayed_drain = 1; // input queue is full: resubmit Drain() in ff_amf_receive_packet
> -            } else if(!ctx->delayed_drain) {
> +            } else if (!ctx->delayed_drain) {
>                  res = ctx->encoder->pVtbl->Drain(ctx->encoder);
>                  if (res == AMF_INPUT_FULL) {
>                      ctx->delayed_drain = 1; // input queue is full: resubmit Drain() in ff_amf_receive_packet
> @@ -484,6 +526,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;
> +            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 +540,20 @@ 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);
> +            frame_ref = av_frame_clone(frame);
> +            AMF_RETURN_IF_FALSE(ctx, frame_ref != NULL, AVERROR(ENOMEM), "av_frame_clone() returned NULL\n");
> +            memcpy(frame_ref_storage_buffer->pVtbl->GetNative(frame_ref_storage_buffer), &frame_ref, sizeof(frame_ref));
> +            AMF_AV_ASSIGN_PROPERTY_INTERFACE(res, surface, L"av_frame_ref", frame_ref_storage_buffer);
> +            if (res != AMF_OK)
> +            {

{ on the previous line.

> +                av_log(avctx, AV_LOG_WARNING, "failed to attach av_frame_ref to surface\n");

And keep going anyway, corrupting the output?

> +                av_frame_free(&frame_ref);
> +                surface->pVtbl->Release(surface);
> +            }
> +            frame_ref_storage_buffer->pVtbl->Release(frame_ref_storage_buffer);
> +            AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR(ENOMEM), "SetProperty failed for \"frame_ref\" with error %d\n", res);
>          } 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);
> @@ -554,12 +612,27 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
>          res_query = ctx->encoder->pVtbl->QueryOutput(ctx->encoder, &data);
>          if (data) {
>              // copy data to packet
> -            AMFBuffer* buffer;
> -            AMFGuid guid = IID_AMFBuffer();
> -            data->pVtbl->QueryInterface(data, &guid, (void**)&buffer); // query for buffer interface
> +            AMFBuffer *buffer;
> +            AMF_AV_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_frame_ref;
> +
> +                AMF_AV_GET_PROPERTY_INTERFACE(res, data, L"av_frame_ref", AMFBuffer, frame_ref_storage_buffer);
> +                if (res == AMF_OK) {
> +                    memcpy(&av_frame_ref, frame_ref_storage_buffer->pVtbl->GetNative(frame_ref_storage_buffer), sizeof(av_frame_ref));
> +                    av_frame_free(&av_frame_ref);
> +                    frame_ref_storage_buffer->pVtbl->Release(frame_ref_storage_buffer);
> +                } else {
> +                    av_log(avctx, AV_LOG_WARNING, "av_frame_ref data attached to frame is corrupted\n");
> +                }
> +            }
> +
>              data->pVtbl->Release(data);
>  
>              AMF_RETURN_IF_FALSE(ctx, ret >= 0, ret, "amf_copy_buffer() failed with error %d\n", ret);
> 

Thanks,

- Mark
Kravchenko, Alexander March 26, 2018, 10:34 p.m. UTC | #4
>

> Put email comments on a patch below the "---" line (otherwise they end up in the commit message when applied).

>

Sorry, I am not sure I understand you correctly here


> > +#define AMF_AV_GET_PROPERTY_INTERFACE(res, pThis, name, TargetType, val) \

> > +    { \

> > +        AMFVariantStruct var; \

> > +        res = AMFVariantInit(&var); \

> > +        if (res != AMF_OK) \

> > +            return res; \

> > +        res = pThis->pVtbl->GetProperty(pThis, name, &var); \

> > +        if (res == AMF_OK) { \

> > +            if (var.type == AMF_VARIANT_INTERFACE && AMFVariantInterface(&var)) { \

> > +                AMF_AV_QUERY_INTERFACE(res, AMFVariantInterface(&var), TargetType, val); \

> > +            } else { \

> > +                res = AMF_INVALID_DATA_TYPE; \

> > +            } \

> > +        } \

> > +        AMFVariantClear(&var); \

> > +    }

>

> These look even more like they should be functions rather than macros now?  In particular, the returns in them interact badly with the

> code below.


I'd also prefer function here, but it works with different types (TargetType parameter is type name)
I am going to discuss propose such macros to AMF headers near future.


> > +            if (res != AMF_OK)

> > +            {

>

> { on the previous line.

>

> > +                av_log(avctx, AV_LOG_WARNING, "failed to attach av_frame_ref to surface\n");

>

> And keep going anyway, corrupting the output?

>

Ok, agree, I will return error here



Thanks,
Alexander

________________________________

This e-mail and any attachment(s) are intended only for the recipient(s) named above and others who have been specifically authorized to receive them. They may contain confidential information. If you are not the intended recipient, please do not read this email or its attachment(s). Furthermore, you are hereby notified that any dissemination, distribution or copying of this e-mail and any attachment(s) is strictly prohibited. If you have received this e-mail in error, please immediately notify the sender by replying to this e-mail and then delete this e-mail and any attachment(s) or copies thereof from your system. Thank you.
Alexander Kravchenko April 2, 2018, 9:36 p.m. UTC | #5
Hello, Mark,
Did you have a chance to review the latest version patch?

Thanks,
Alexander
diff mbox

Patch

diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c
index 89a10ff253..f532a32b7b 100644
--- a/libavcodec/amfenc.c
+++ b/libavcodec/amfenc.c
@@ -188,7 +188,7 @@  static int amf_init_context(AVCodecContext *avctx)
                             return AVERROR(ENOMEM);
                         }
                     } else {
-                        if(res == AMF_NOT_SUPPORTED)
+                        if (res == AMF_NOT_SUPPORTED)
                             av_log(avctx, AV_LOG_INFO, "avctx->hw_frames_ctx has D3D11 device which doesn't have D3D11VA interface, switching to default\n");
                         else
                             av_log(avctx, AV_LOG_INFO, "avctx->hw_frames_ctx has non-AMD device, switching to default\n");
@@ -298,7 +298,7 @@  int av_cold ff_amf_encode_close(AVCodecContext *avctx)
 }
 
 static int amf_copy_surface(AVCodecContext *avctx, const AVFrame *frame,
-    AMFSurface* surface)
+    AMFSurface *surface)
 {
     AVFrame        *sw_frame = NULL;
     AMFPlane       *plane = NULL;
@@ -371,7 +371,7 @@  static int amf_copy_buffer(AVCodecContext *avctx, AVPacket *pkt, AMFBuffer *buff
     switch (avctx->codec->id) {
         case AV_CODEC_ID_H264:
             buffer->pVtbl->GetProperty(buffer, AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE, &var);
-            if(var.int64Value == AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE_IDR) {
+            if (var.int64Value == AMF_VIDEO_ENCODER_OUTPUT_DATA_TYPE_IDR) {
                 pkt->flags = AV_PKT_FLAG_KEY;
             }
             break;
@@ -443,6 +443,48 @@  int ff_amf_encode_init(AVCodecContext *avctx)
     return ret;
 }
 
+#define AMF_AV_QUERY_INTERFACE(res, from, InterfaceTypeTo, to) \
+    { \
+        AMFGuid guid_##InterfaceTypeTo = IID_##InterfaceTypeTo(); \
+        res = from->pVtbl->QueryInterface(from, &guid_##InterfaceTypeTo, (void**)&to); \
+    }
+
+#define AMF_AV_ASSIGN_PROPERTY_INTERFACE(res, pThis, name, val) \
+    { \
+        AMFInterface *amf_interface; \
+        AMFVariantStruct var; \
+        res = AMFVariantInit(&var); \
+        if (res != AMF_OK) \
+            return res; \
+        if (res == AMF_OK) { \
+            AMF_AV_QUERY_INTERFACE(res, val, AMFInterface, amf_interface)\
+        } \
+        if (res == AMF_OK) { \
+            res = AMFVariantAssignInterface(&var, amf_interface); \
+            amf_interface->pVtbl->Release(amf_interface); \
+        } \
+        if (res == AMF_OK) { \
+            res = pThis->pVtbl->SetProperty(pThis, name, var); \
+        } \
+        res = AMFVariantClear(&var); \
+    }
+
+#define AMF_AV_GET_PROPERTY_INTERFACE(res, pThis, name, TargetType, val) \
+    { \
+        AMFVariantStruct var; \
+        res = AMFVariantInit(&var); \
+        if (res != AMF_OK) \
+            return res; \
+        res = pThis->pVtbl->GetProperty(pThis, name, &var); \
+        if (res == AMF_OK) { \
+            if (var.type == AMF_VARIANT_INTERFACE && AMFVariantInterface(&var)) { \
+                AMF_AV_QUERY_INTERFACE(res, AMFVariantInterface(&var), TargetType, val); \
+            } else { \
+                res = AMF_INVALID_DATA_TYPE; \
+            } \
+        } \
+        AMFVariantClear(&var); \
+    }
 
 int ff_amf_send_frame(AVCodecContext *avctx, const AVFrame *frame)
 {
@@ -458,7 +500,7 @@  int ff_amf_send_frame(AVCodecContext *avctx, const AVFrame *frame)
         if (!ctx->eof) { // submit drain one time only
             if (ctx->delayed_surface != NULL) {
                 ctx->delayed_drain = 1; // input queue is full: resubmit Drain() in ff_amf_receive_packet
-            } else if(!ctx->delayed_drain) {
+            } else if (!ctx->delayed_drain) {
                 res = ctx->encoder->pVtbl->Drain(ctx->encoder);
                 if (res == AMF_INPUT_FULL) {
                     ctx->delayed_drain = 1; // input queue is full: resubmit Drain() in ff_amf_receive_packet
@@ -484,6 +526,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;
+            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 +540,20 @@  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);
+            frame_ref = av_frame_clone(frame);
+            AMF_RETURN_IF_FALSE(ctx, frame_ref != NULL, AVERROR(ENOMEM), "av_frame_clone() returned NULL\n");
+            memcpy(frame_ref_storage_buffer->pVtbl->GetNative(frame_ref_storage_buffer), &frame_ref, sizeof(frame_ref));
+            AMF_AV_ASSIGN_PROPERTY_INTERFACE(res, surface, L"av_frame_ref", frame_ref_storage_buffer);
+            if (res != AMF_OK)
+            {
+                av_log(avctx, AV_LOG_WARNING, "failed to attach av_frame_ref to surface\n");
+                av_frame_free(&frame_ref);
+                surface->pVtbl->Release(surface);
+            }
+            frame_ref_storage_buffer->pVtbl->Release(frame_ref_storage_buffer);
+            AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR(ENOMEM), "SetProperty failed for \"frame_ref\" with error %d\n", res);
         } 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);
@@ -554,12 +612,27 @@  int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
         res_query = ctx->encoder->pVtbl->QueryOutput(ctx->encoder, &data);
         if (data) {
             // copy data to packet
-            AMFBuffer* buffer;
-            AMFGuid guid = IID_AMFBuffer();
-            data->pVtbl->QueryInterface(data, &guid, (void**)&buffer); // query for buffer interface
+            AMFBuffer *buffer;
+            AMF_AV_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_frame_ref;
+
+                AMF_AV_GET_PROPERTY_INTERFACE(res, data, L"av_frame_ref", AMFBuffer, frame_ref_storage_buffer);
+                if (res == AMF_OK) {
+                    memcpy(&av_frame_ref, frame_ref_storage_buffer->pVtbl->GetNative(frame_ref_storage_buffer), sizeof(av_frame_ref));
+                    av_frame_free(&av_frame_ref);
+                    frame_ref_storage_buffer->pVtbl->Release(frame_ref_storage_buffer);
+                } else {
+                    av_log(avctx, AV_LOG_WARNING, "av_frame_ref data attached to frame is corrupted\n");
+                }
+            }
+
             data->pVtbl->Release(data);
 
             AMF_RETURN_IF_FALSE(ctx, ret >= 0, ret, "amf_copy_buffer() failed with error %d\n", ret);