Commit 78149d66 authored by Alexander Kravchenko's avatar Alexander Kravchenko Committed by Luca Barbato

amfenc: Retain a reference to D3D frames used as input during the encoding process

This fixes frame corruption issue when decoder started reusing frames
while they are still in use of encoding process

Issue with frame corruption  was reproduced using:

    avconv.exe -y -hwaccel d3d11va -hwaccel_output_format d3d11 -i input.h264 -an -c:v h264_amf output.mkv

It is recommended to use -extra_hw_frames 16 option in case if hw frames
number in pool is not enough
Signed-off-by: 's avatarLuca Barbato <lu_zero@gentoo.org>
parent abf806f7
...@@ -162,6 +162,9 @@ static int amf_init_context(AVCodecContext *avctx) ...@@ -162,6 +162,9 @@ static int amf_init_context(AVCodecContext *avctx)
AmfContext *ctx = avctx->priv_data; AmfContext *ctx = avctx->priv_data;
AMF_RESULT res = AMF_OK; AMF_RESULT res = AMF_OK;
ctx->hwsurfaces_in_queue = 0;
ctx->hwsurfaces_in_queue_max = 16;
// configure AMF logger // configure AMF logger
// the return of these functions indicates old state and do not affect behaviour // 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 ); ctx->trace->pVtbl->EnableWriter(ctx->trace, AMF_TRACE_WRITER_DEBUG_OUTPUT, ctx->log_to_dbg != 0 );
...@@ -192,6 +195,8 @@ static int amf_init_context(AVCodecContext *avctx) ...@@ -192,6 +195,8 @@ static int amf_init_context(AVCodecContext *avctx)
if (!ctx->hw_frames_ctx) { if (!ctx->hw_frames_ctx) {
return AVERROR(ENOMEM); return AVERROR(ENOMEM);
} }
if (device_ctx->initial_pool_size > 0)
ctx->hwsurfaces_in_queue_max = device_ctx->initial_pool_size - 1;
} else { } 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"); av_log(avctx, AV_LOG_INFO, "avctx->hw_frames_ctx has D3D11 device which doesn't have D3D11VA interface, switching to default\n");
...@@ -447,6 +452,75 @@ int ff_amf_encode_init(AVCodecContext *avctx) ...@@ -447,6 +452,75 @@ int ff_amf_encode_init(AVCodecContext *avctx)
return ret; return ret;
} }
static AMF_RESULT amf_set_property_buffer(AMFSurface *object, const wchar_t *name, AMFBuffer *val)
{
AMF_RESULT res;
AMFVariantStruct var;
res = AMFVariantInit(&var);
if (res == AMF_OK) {
AMFGuid guid_AMFInterface = IID_AMFInterface();
AMFInterface *amf_interface;
res = val->pVtbl->QueryInterface(val, &guid_AMFInterface, (void**)&amf_interface);
if (res == AMF_OK) {
res = AMFVariantAssignInterface(&var, amf_interface);
amf_interface->pVtbl->Release(amf_interface);
}
if (res == AMF_OK) {
res = object->pVtbl->SetProperty(object, name, var);
}
AMFVariantClear(&var);
}
return res;
}
static AMF_RESULT amf_get_property_buffer(AMFData *object, const wchar_t *name, AMFBuffer **val)
{
AMF_RESULT res;
AMFVariantStruct var;
res = AMFVariantInit(&var);
if (res == AMF_OK) {
res = object->pVtbl->GetProperty(object, name, &var);
if (res == AMF_OK) {
if (var.type == AMF_VARIANT_INTERFACE) {
AMFGuid guid_AMFBuffer = IID_AMFBuffer();
AMFInterface *amf_interface = AMFVariantInterface(&var);
res = amf_interface->pVtbl->QueryInterface(amf_interface, &guid_AMFBuffer, (void**)val);
} else {
res = AMF_INVALID_DATA_TYPE;
}
}
AMFVariantClear(&var);
}
return res;
}
static AMFBuffer *amf_create_buffer_with_frame_ref(const AVFrame *frame, AMFContext *context)
{
AVFrame *frame_ref;
AMFBuffer *frame_ref_storage_buffer = NULL;
AMF_RESULT res;
res = context->pVtbl->AllocBuffer(context, AMF_MEMORY_HOST, sizeof(frame_ref), &frame_ref_storage_buffer);
if (res == AMF_OK) {
frame_ref = av_frame_clone(frame);
if (frame_ref) {
memcpy(frame_ref_storage_buffer->pVtbl->GetNative(frame_ref_storage_buffer), &frame_ref, sizeof(frame_ref));
} else {
frame_ref_storage_buffer->pVtbl->Release(frame_ref_storage_buffer);
frame_ref_storage_buffer = NULL;
}
}
return frame_ref_storage_buffer;
}
static void amf_release_buffer_with_frame_ref(AMFBuffer *frame_ref_storage_buffer)
{
AVFrame *av_frame_ref;
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);
}
int ff_amf_send_frame(AVCodecContext *avctx, const AVFrame *frame) int ff_amf_send_frame(AVCodecContext *avctx, const AVFrame *frame)
{ {
...@@ -488,6 +562,8 @@ int ff_amf_send_frame(AVCodecContext *avctx, const AVFrame *frame) ...@@ -488,6 +562,8 @@ int ff_amf_send_frame(AVCodecContext *avctx, const AVFrame *frame)
(ctx->hw_device_ctx && ((AVHWFramesContext*)frame->hw_frames_ctx->data)->device_ctx == (ctx->hw_device_ctx && ((AVHWFramesContext*)frame->hw_frames_ctx->data)->device_ctx ==
(AVHWDeviceContext*)ctx->hw_device_ctx->data) (AVHWDeviceContext*)ctx->hw_device_ctx->data)
)) { )) {
AMFBuffer *frame_ref_storage_buffer;
#if CONFIG_D3D11VA #if CONFIG_D3D11VA
static const GUID AMFTextureArrayIndexGUID = { 0x28115527, 0xe7c3, 0x4b66, { 0x99, 0xd3, 0x4f, 0x2a, 0xe6, 0xb4, 0x7f, 0xaf } }; static const GUID AMFTextureArrayIndexGUID = { 0x28115527, 0xe7c3, 0x4b66, { 0x99, 0xd3, 0x4f, 0x2a, 0xe6, 0xb4, 0x7f, 0xaf } };
ID3D11Texture2D *texture = (ID3D11Texture2D*)frame->data[0]; // actual texture ID3D11Texture2D *texture = (ID3D11Texture2D*)frame->data[0]; // actual texture
...@@ -500,6 +576,14 @@ int ff_amf_send_frame(AVCodecContext *avctx, const AVFrame *frame) ...@@ -500,6 +576,14 @@ int ff_amf_send_frame(AVCodecContext *avctx, const AVFrame *frame)
// input HW surfaces can be vertically aligned by 16; tell AMF the real size // input HW surfaces can be vertically aligned by 16; tell AMF the real size
surface->pVtbl->SetCrop(surface, 0, 0, frame->width, frame->height); surface->pVtbl->SetCrop(surface, 0, 0, frame->width, frame->height);
#endif #endif
frame_ref_storage_buffer = amf_create_buffer_with_frame_ref(frame, ctx->context);
AMF_RETURN_IF_FALSE(ctx, frame_ref_storage_buffer != NULL, AVERROR(ENOMEM), "create_buffer_with_frame_ref() returned NULL\n");
res = amf_set_property_buffer(surface, L"av_frame_ref", frame_ref_storage_buffer);
AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "SetProperty failed for \"av_frame_ref\" with error %d\n", res);
ctx->hwsurfaces_in_queue++;
frame_ref_storage_buffer->pVtbl->Release(frame_ref_storage_buffer);
} else { } else {
res = ctx->context->pVtbl->AllocSurface(ctx->context, AMF_MEMORY_HOST, ctx->format, avctx->width, avctx->height, &surface); 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); AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR(ENOMEM), "AllocSurface() failed with error %d\n", res);
...@@ -564,6 +648,15 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) ...@@ -564,6 +648,15 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
ret = amf_copy_buffer(avctx, avpkt, buffer); ret = amf_copy_buffer(avctx, avpkt, buffer);
buffer->pVtbl->Release(buffer); buffer->pVtbl->Release(buffer);
if (data->pVtbl->HasProperty(data, L"av_frame_ref")) {
AMFBuffer *frame_ref_storage_buffer;
res = amf_get_property_buffer(data, L"av_frame_ref", &frame_ref_storage_buffer);
AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "GetProperty failed for \"av_frame_ref\" with error %d\n", res);
amf_release_buffer_with_frame_ref(frame_ref_storage_buffer);
ctx->hwsurfaces_in_queue--;
}
data->pVtbl->Release(data); data->pVtbl->Release(data);
AMF_RETURN_IF_FALSE(ctx, ret >= 0, ret, "amf_copy_buffer() failed with error %d\n", ret); AMF_RETURN_IF_FALSE(ctx, ret >= 0, ret, "amf_copy_buffer() failed with error %d\n", ret);
...@@ -593,7 +686,7 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) ...@@ -593,7 +686,7 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
av_log(avctx, AV_LOG_WARNING, "Data acquired but delayed drain submission got AMF_INPUT_FULL- should not happen\n"); av_log(avctx, AV_LOG_WARNING, "Data acquired but delayed drain submission got AMF_INPUT_FULL- should not happen\n");
} }
} }
} else if (ctx->delayed_surface != NULL || ctx->delayed_drain || (ctx->eof && res_query != AMF_EOF)) { } else if (ctx->delayed_surface != NULL || ctx->delayed_drain || (ctx->eof && res_query != AMF_EOF) || (ctx->hwsurfaces_in_queue >= ctx->hwsurfaces_in_queue_max)) {
block_and_wait = 1; block_and_wait = 1;
av_usleep(1000); // wait and poll again av_usleep(1000); // wait and poll again
} }
......
...@@ -68,6 +68,9 @@ typedef struct AmfContext { ...@@ -68,6 +68,9 @@ typedef struct AmfContext {
AVBufferRef *hw_device_ctx; ///< pointer to HW accelerator (decoder) AVBufferRef *hw_device_ctx; ///< pointer to HW accelerator (decoder)
AVBufferRef *hw_frames_ctx; ///< pointer to HW accelerator (frame allocator) AVBufferRef *hw_frames_ctx; ///< pointer to HW accelerator (frame allocator)
int hwsurfaces_in_queue;
int hwsurfaces_in_queue_max;
// helpers to handle async calls // helpers to handle async calls
int delayed_drain; int delayed_drain;
AMFSurface *delayed_surface; AMFSurface *delayed_surface;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment