Skip to content
Snippets Groups Projects
Commit bff7bec1 authored by Mark Thompson's avatar Mark Thompson
Browse files

vf_deinterlace_vaapi: Add support for field rate output

In order to work correctly with the i965 driver, this also fixes the
direction of forward/backward references - forward references are
intended to be those from the past to the current frame, not from the
current frame to the future.

(cherry picked from commit 9aa251c9)
parent 527a1e21
No related branches found
No related tags found
No related merge requests found
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <va/va_vpp.h> #include <va/va_vpp.h>
#include "libavutil/avassert.h" #include "libavutil/avassert.h"
#include "libavutil/common.h"
#include "libavutil/hwcontext.h" #include "libavutil/hwcontext.h"
#include "libavutil/hwcontext_vaapi.h" #include "libavutil/hwcontext_vaapi.h"
#include "libavutil/mem.h" #include "libavutil/mem.h"
...@@ -42,6 +43,8 @@ typedef struct DeintVAAPIContext { ...@@ -42,6 +43,8 @@ typedef struct DeintVAAPIContext {
AVBufferRef *device_ref; AVBufferRef *device_ref;
int mode; int mode;
int field_rate;
int auto_enable;
int valid_ids; int valid_ids;
VAConfigID va_config; VAConfigID va_config;
...@@ -63,6 +66,7 @@ typedef struct DeintVAAPIContext { ...@@ -63,6 +66,7 @@ typedef struct DeintVAAPIContext {
int queue_depth; int queue_depth;
int queue_count; int queue_count;
AVFrame *frame_queue[MAX_REFERENCES]; AVFrame *frame_queue[MAX_REFERENCES];
int extra_delay_for_timestamps;
VABufferID filter_buffer; VABufferID filter_buffer;
} DeintVAAPIContext; } DeintVAAPIContext;
...@@ -211,8 +215,12 @@ static int deint_vaapi_build_filter_params(AVFilterContext *avctx) ...@@ -211,8 +215,12 @@ static int deint_vaapi_build_filter_params(AVFilterContext *avctx)
return AVERROR(EIO); return AVERROR(EIO);
} }
ctx->extra_delay_for_timestamps = ctx->field_rate == 2 &&
ctx->pipeline_caps.num_backward_references == 0;
ctx->queue_depth = ctx->pipeline_caps.num_backward_references + ctx->queue_depth = ctx->pipeline_caps.num_backward_references +
ctx->pipeline_caps.num_forward_references + 1; ctx->pipeline_caps.num_forward_references +
ctx->extra_delay_for_timestamps + 1;
if (ctx->queue_depth > MAX_REFERENCES) { if (ctx->queue_depth > MAX_REFERENCES) {
av_log(avctx, AV_LOG_ERROR, "Pipeline requires too many " av_log(avctx, AV_LOG_ERROR, "Pipeline requires too many "
"references (%u forward, %u back).\n", "references (%u forward, %u back).\n",
...@@ -227,6 +235,7 @@ static int deint_vaapi_build_filter_params(AVFilterContext *avctx) ...@@ -227,6 +235,7 @@ static int deint_vaapi_build_filter_params(AVFilterContext *avctx)
static int deint_vaapi_config_output(AVFilterLink *outlink) static int deint_vaapi_config_output(AVFilterLink *outlink)
{ {
AVFilterContext *avctx = outlink->src; AVFilterContext *avctx = outlink->src;
AVFilterLink *inlink = avctx->inputs[0];
DeintVAAPIContext *ctx = avctx->priv; DeintVAAPIContext *ctx = avctx->priv;
AVVAAPIHWConfig *hwconfig = NULL; AVVAAPIHWConfig *hwconfig = NULL;
AVHWFramesConstraints *constraints = NULL; AVHWFramesConstraints *constraints = NULL;
...@@ -326,8 +335,13 @@ static int deint_vaapi_config_output(AVFilterLink *outlink) ...@@ -326,8 +335,13 @@ static int deint_vaapi_config_output(AVFilterLink *outlink)
if (err < 0) if (err < 0)
goto fail; goto fail;
outlink->w = ctx->output_width; outlink->w = inlink->w;
outlink->h = ctx->output_height; outlink->h = inlink->h;
outlink->time_base = av_mul_q(inlink->time_base,
(AVRational) { 1, ctx->field_rate });
outlink->frame_rate = av_mul_q(inlink->frame_rate,
(AVRational) { ctx->field_rate, 1 });
outlink->hw_frames_ctx = av_buffer_ref(ctx->output_frames_ref); outlink->hw_frames_ctx = av_buffer_ref(ctx->output_frames_ref);
if (!outlink->hw_frames_ctx) { if (!outlink->hw_frames_ctx) {
...@@ -375,7 +389,7 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame) ...@@ -375,7 +389,7 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
VABufferID params_id; VABufferID params_id;
VAStatus vas; VAStatus vas;
void *filter_params_addr = NULL; void *filter_params_addr = NULL;
int err, i; int err, i, field, current_frame_index;
av_log(avctx, AV_LOG_DEBUG, "Filter input: %s, %ux%u (%"PRId64").\n", av_log(avctx, AV_LOG_DEBUG, "Filter input: %s, %ux%u (%"PRId64").\n",
av_get_pix_fmt_name(input_frame->format), av_get_pix_fmt_name(input_frame->format),
...@@ -394,17 +408,16 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame) ...@@ -394,17 +408,16 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
ctx->frame_queue[i] = input_frame; ctx->frame_queue[i] = input_frame;
} }
input_frame = current_frame_index = ctx->pipeline_caps.num_forward_references;
ctx->frame_queue[ctx->pipeline_caps.num_backward_references];
input_frame = ctx->frame_queue[current_frame_index];
input_surface = (VASurfaceID)(uintptr_t)input_frame->data[3]; input_surface = (VASurfaceID)(uintptr_t)input_frame->data[3];
for (i = 0; i < ctx->pipeline_caps.num_backward_references; i++)
backward_references[i] = (VASurfaceID)(uintptr_t)
ctx->frame_queue[ctx->pipeline_caps.num_backward_references -
i - 1]->data[3];
for (i = 0; i < ctx->pipeline_caps.num_forward_references; i++) for (i = 0; i < ctx->pipeline_caps.num_forward_references; i++)
forward_references[i] = (VASurfaceID)(uintptr_t) forward_references[i] = (VASurfaceID)(uintptr_t)
ctx->frame_queue[ctx->pipeline_caps.num_backward_references + ctx->frame_queue[current_frame_index - i - 1]->data[3];
i + 1]->data[3]; for (i = 0; i < ctx->pipeline_caps.num_backward_references; i++)
backward_references[i] = (VASurfaceID)(uintptr_t)
ctx->frame_queue[current_frame_index + i + 1]->data[3];
av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for " av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for "
"deinterlace input.\n", input_surface); "deinterlace input.\n", input_surface);
...@@ -417,129 +430,148 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame) ...@@ -417,129 +430,148 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
av_log(avctx, AV_LOG_DEBUG, " %#x", forward_references[i]); av_log(avctx, AV_LOG_DEBUG, " %#x", forward_references[i]);
av_log(avctx, AV_LOG_DEBUG, "\n"); av_log(avctx, AV_LOG_DEBUG, "\n");
output_frame = av_frame_alloc(); for (field = 0; field < ctx->field_rate; field++) {
if (!output_frame) { output_frame = ff_get_video_buffer(outlink, ctx->output_width,
err = AVERROR(ENOMEM); ctx->output_height);
goto fail; if (!output_frame) {
} err = AVERROR(ENOMEM);
goto fail;
err = av_hwframe_get_buffer(ctx->output_frames_ref, }
output_frame, 0);
if (err < 0) {
err = AVERROR(ENOMEM);
goto fail;
}
output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3];
av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for "
"deinterlace output.\n", output_surface);
memset(&params, 0, sizeof(params));
input_region = (VARectangle) {
.x = 0,
.y = 0,
.width = input_frame->width,
.height = input_frame->height,
};
params.surface = input_surface; output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3];
params.surface_region = &input_region; av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for "
params.surface_color_standard = vaapi_proc_colour_standard( "deinterlace output.\n", output_surface);
input_frame->colorspace);
memset(&params, 0, sizeof(params));
input_region = (VARectangle) {
.x = 0,
.y = 0,
.width = input_frame->width,
.height = input_frame->height,
};
params.surface = input_surface;
params.surface_region = &input_region;
params.surface_color_standard =
vaapi_proc_colour_standard(input_frame->colorspace);
params.output_region = NULL;
params.output_background_color = 0xff000000;
params.output_color_standard = params.surface_color_standard;
params.pipeline_flags = 0;
params.filter_flags = VA_FRAME_PICTURE;
if (!ctx->auto_enable || input_frame->interlaced_frame) {
vas = vaMapBuffer(ctx->hwctx->display, ctx->filter_buffer,
&filter_params_addr);
if (vas != VA_STATUS_SUCCESS) {
av_log(avctx, AV_LOG_ERROR, "Failed to map filter parameter "
"buffer: %d (%s).\n", vas, vaErrorStr(vas));
err = AVERROR(EIO);
goto fail;
}
filter_params = filter_params_addr;
filter_params->flags = 0;
if (input_frame->top_field_first) {
filter_params->flags |= field ? VA_DEINTERLACING_BOTTOM_FIELD : 0;
} else {
filter_params->flags |= VA_DEINTERLACING_BOTTOM_FIELD_FIRST;
filter_params->flags |= field ? 0 : VA_DEINTERLACING_BOTTOM_FIELD;
}
filter_params_addr = NULL;
vas = vaUnmapBuffer(ctx->hwctx->display, ctx->filter_buffer);
if (vas != VA_STATUS_SUCCESS)
av_log(avctx, AV_LOG_ERROR, "Failed to unmap filter parameter "
"buffer: %d (%s).\n", vas, vaErrorStr(vas));
params.filters = &ctx->filter_buffer;
params.num_filters = 1;
params.forward_references = forward_references;
params.num_forward_references =
ctx->pipeline_caps.num_forward_references;
params.backward_references = backward_references;
params.num_backward_references =
ctx->pipeline_caps.num_backward_references;
} else {
params.filters = NULL;
params.num_filters = 0;
}
params.output_region = NULL; vas = vaBeginPicture(ctx->hwctx->display,
params.output_background_color = 0xff000000; ctx->va_context, output_surface);
params.output_color_standard = params.surface_color_standard; if (vas != VA_STATUS_SUCCESS) {
av_log(avctx, AV_LOG_ERROR, "Failed to attach new picture: "
"%d (%s).\n", vas, vaErrorStr(vas));
err = AVERROR(EIO);
goto fail;
}
params.pipeline_flags = 0; vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context,
params.filter_flags = VA_FRAME_PICTURE; VAProcPipelineParameterBufferType,
sizeof(params), 1, &params, &params_id);
if (vas != VA_STATUS_SUCCESS) {
av_log(avctx, AV_LOG_ERROR, "Failed to create parameter buffer: "
"%d (%s).\n", vas, vaErrorStr(vas));
err = AVERROR(EIO);
goto fail_after_begin;
}
av_log(avctx, AV_LOG_DEBUG, "Pipeline parameter buffer is %#x.\n",
params_id);
vas = vaMapBuffer(ctx->hwctx->display, ctx->filter_buffer, vas = vaRenderPicture(ctx->hwctx->display, ctx->va_context,
&filter_params_addr); &params_id, 1);
if (vas != VA_STATUS_SUCCESS) { if (vas != VA_STATUS_SUCCESS) {
av_log(avctx, AV_LOG_ERROR, "Failed to map filter parameter " av_log(avctx, AV_LOG_ERROR, "Failed to render parameter buffer: "
"buffer: %d (%s).\n", vas, vaErrorStr(vas)); "%d (%s).\n", vas, vaErrorStr(vas));
err = AVERROR(EIO); err = AVERROR(EIO);
goto fail; goto fail_after_begin;
} }
filter_params = filter_params_addr;
filter_params->flags = 0;
if (input_frame->interlaced_frame && !input_frame->top_field_first)
filter_params->flags |= VA_DEINTERLACING_BOTTOM_FIELD_FIRST;
filter_params_addr = NULL;
vas = vaUnmapBuffer(ctx->hwctx->display, ctx->filter_buffer);
if (vas != VA_STATUS_SUCCESS)
av_log(avctx, AV_LOG_ERROR, "Failed to unmap filter parameter "
"buffer: %d (%s).\n", vas, vaErrorStr(vas));
params.filters = &ctx->filter_buffer;
params.num_filters = 1;
params.forward_references = forward_references;
params.num_forward_references =
ctx->pipeline_caps.num_forward_references;
params.backward_references = backward_references;
params.num_backward_references =
ctx->pipeline_caps.num_backward_references;
vas = vaBeginPicture(ctx->hwctx->display,
ctx->va_context, output_surface);
if (vas != VA_STATUS_SUCCESS) {
av_log(avctx, AV_LOG_ERROR, "Failed to attach new picture: "
"%d (%s).\n", vas, vaErrorStr(vas));
err = AVERROR(EIO);
goto fail;
}
vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context, vas = vaEndPicture(ctx->hwctx->display, ctx->va_context);
VAProcPipelineParameterBufferType, if (vas != VA_STATUS_SUCCESS) {
sizeof(params), 1, &params, &params_id); av_log(avctx, AV_LOG_ERROR, "Failed to start picture processing: "
if (vas != VA_STATUS_SUCCESS) { "%d (%s).\n", vas, vaErrorStr(vas));
av_log(avctx, AV_LOG_ERROR, "Failed to create parameter buffer: " err = AVERROR(EIO);
"%d (%s).\n", vas, vaErrorStr(vas)); goto fail_after_render;
err = AVERROR(EIO); }
goto fail_after_begin;
}
av_log(avctx, AV_LOG_DEBUG, "Pipeline parameter buffer is %#x.\n",
params_id);
vas = vaRenderPicture(ctx->hwctx->display, ctx->va_context, if (ctx->hwctx->driver_quirks &
&params_id, 1); AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS) {
if (vas != VA_STATUS_SUCCESS) { vas = vaDestroyBuffer(ctx->hwctx->display, params_id);
av_log(avctx, AV_LOG_ERROR, "Failed to render parameter buffer: " if (vas != VA_STATUS_SUCCESS) {
"%d (%s).\n", vas, vaErrorStr(vas)); av_log(avctx, AV_LOG_ERROR, "Failed to free parameter buffer: "
err = AVERROR(EIO); "%d (%s).\n", vas, vaErrorStr(vas));
goto fail_after_begin; // And ignore.
} }
}
vas = vaEndPicture(ctx->hwctx->display, ctx->va_context); err = av_frame_copy_props(output_frame, input_frame);
if (vas != VA_STATUS_SUCCESS) { if (err < 0)
av_log(avctx, AV_LOG_ERROR, "Failed to start picture processing: " goto fail;
"%d (%s).\n", vas, vaErrorStr(vas));
err = AVERROR(EIO);
goto fail_after_render;
}
if (ctx->hwctx->driver_quirks & if (ctx->field_rate == 2) {
AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS) { if (field == 0)
vas = vaDestroyBuffer(ctx->hwctx->display, params_id); output_frame->pts = 2 * input_frame->pts;
if (vas != VA_STATUS_SUCCESS) { else
av_log(avctx, AV_LOG_ERROR, "Failed to free parameter buffer: " output_frame->pts = input_frame->pts +
"%d (%s).\n", vas, vaErrorStr(vas)); ctx->frame_queue[current_frame_index + 1]->pts;
// And ignore.
} }
} output_frame->interlaced_frame = 0;
err = av_frame_copy_props(output_frame, input_frame); av_log(avctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n",
if (err < 0) av_get_pix_fmt_name(output_frame->format),
goto fail; output_frame->width, output_frame->height, output_frame->pts);
av_log(avctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n", err = ff_filter_frame(outlink, output_frame);
av_get_pix_fmt_name(output_frame->format), if (err < 0)
output_frame->width, output_frame->height, output_frame->pts); break;
}
return ff_filter_frame(outlink, output_frame); return err;
fail_after_begin: fail_after_begin:
vaRenderPicture(ctx->hwctx->display, ctx->va_context, &params_id, 1); vaRenderPicture(ctx->hwctx->display, ctx->va_context, &params_id, 1);
...@@ -592,6 +624,17 @@ static const AVOption deint_vaapi_options[] = { ...@@ -592,6 +624,17 @@ static const AVOption deint_vaapi_options[] = {
0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingMotionAdaptive }, .unit = "mode" }, 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingMotionAdaptive }, .unit = "mode" },
{ "motion_compensated", "Use the motion compensated deinterlacing algorithm", { "motion_compensated", "Use the motion compensated deinterlacing algorithm",
0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingMotionCompensated }, .unit = "mode" }, 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingMotionCompensated }, .unit = "mode" },
{ "rate", "Generate output at frame rate or field rate",
OFFSET(field_rate), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, 2, FLAGS, "rate" },
{ "frame", "Output at frame rate (one frame of output for each field-pair)",
0, AV_OPT_TYPE_CONST, { .i64 = 1 }, .unit = "rate" },
{ "field", "Output at field rate (one frame of output for each field)",
0, AV_OPT_TYPE_CONST, { .i64 = 2 }, .unit = "rate" },
{ "auto", "Only deinterlace fields, passing frames through unchanged",
OFFSET(auto_enable), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS },
{ NULL }, { NULL },
}; };
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment