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 9aa251c98c)
This commit is contained in:
Mark Thompson 2017-02-27 21:29:46 +00:00
parent 527a1e2131
commit bff7bec1d7
1 changed files with 175 additions and 132 deletions

View File

@ -22,6 +22,7 @@
#include <va/va_vpp.h>
#include "libavutil/avassert.h"
#include "libavutil/common.h"
#include "libavutil/hwcontext.h"
#include "libavutil/hwcontext_vaapi.h"
#include "libavutil/mem.h"
@ -42,6 +43,8 @@ typedef struct DeintVAAPIContext {
AVBufferRef *device_ref;
int mode;
int field_rate;
int auto_enable;
int valid_ids;
VAConfigID va_config;
@ -63,6 +66,7 @@ typedef struct DeintVAAPIContext {
int queue_depth;
int queue_count;
AVFrame *frame_queue[MAX_REFERENCES];
int extra_delay_for_timestamps;
VABufferID filter_buffer;
} DeintVAAPIContext;
@ -211,8 +215,12 @@ static int deint_vaapi_build_filter_params(AVFilterContext *avctx)
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->pipeline_caps.num_forward_references + 1;
ctx->pipeline_caps.num_forward_references +
ctx->extra_delay_for_timestamps + 1;
if (ctx->queue_depth > MAX_REFERENCES) {
av_log(avctx, AV_LOG_ERROR, "Pipeline requires too many "
"references (%u forward, %u back).\n",
@ -227,6 +235,7 @@ static int deint_vaapi_build_filter_params(AVFilterContext *avctx)
static int deint_vaapi_config_output(AVFilterLink *outlink)
{
AVFilterContext *avctx = outlink->src;
AVFilterLink *inlink = avctx->inputs[0];
DeintVAAPIContext *ctx = avctx->priv;
AVVAAPIHWConfig *hwconfig = NULL;
AVHWFramesConstraints *constraints = NULL;
@ -326,8 +335,13 @@ static int deint_vaapi_config_output(AVFilterLink *outlink)
if (err < 0)
goto fail;
outlink->w = ctx->output_width;
outlink->h = ctx->output_height;
outlink->w = inlink->w;
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);
if (!outlink->hw_frames_ctx) {
@ -375,7 +389,7 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
VABufferID params_id;
VAStatus vas;
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_get_pix_fmt_name(input_frame->format),
@ -394,17 +408,16 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
ctx->frame_queue[i] = input_frame;
}
input_frame =
ctx->frame_queue[ctx->pipeline_caps.num_backward_references];
current_frame_index = ctx->pipeline_caps.num_forward_references;
input_frame = ctx->frame_queue[current_frame_index];
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++)
forward_references[i] = (VASurfaceID)(uintptr_t)
ctx->frame_queue[ctx->pipeline_caps.num_backward_references +
i + 1]->data[3];
ctx->frame_queue[current_frame_index - 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 "
"deinterlace input.\n", input_surface);
@ -417,19 +430,14 @@ 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, "\n");
output_frame = av_frame_alloc();
for (field = 0; field < ctx->field_rate; field++) {
output_frame = ff_get_video_buffer(outlink, ctx->output_width,
ctx->output_height);
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);
@ -445,8 +453,8 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
params.surface = input_surface;
params.surface_region = &input_region;
params.surface_color_standard = vaapi_proc_colour_standard(
input_frame->colorspace);
params.surface_color_standard =
vaapi_proc_colour_standard(input_frame->colorspace);
params.output_region = NULL;
params.output_background_color = 0xff000000;
@ -455,6 +463,7 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
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) {
@ -465,8 +474,12 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
}
filter_params = filter_params_addr;
filter_params->flags = 0;
if (input_frame->interlaced_frame && !input_frame->top_field_first)
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)
@ -483,6 +496,11 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
params.num_backward_references =
ctx->pipeline_caps.num_backward_references;
} else {
params.filters = NULL;
params.num_filters = 0;
}
vas = vaBeginPicture(ctx->hwctx->display,
ctx->va_context, output_surface);
if (vas != VA_STATUS_SUCCESS) {
@ -535,11 +553,25 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
if (err < 0)
goto fail;
if (ctx->field_rate == 2) {
if (field == 0)
output_frame->pts = 2 * input_frame->pts;
else
output_frame->pts = input_frame->pts +
ctx->frame_queue[current_frame_index + 1]->pts;
}
output_frame->interlaced_frame = 0;
av_log(avctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n",
av_get_pix_fmt_name(output_frame->format),
output_frame->width, output_frame->height, output_frame->pts);
return ff_filter_frame(outlink, output_frame);
err = ff_filter_frame(outlink, output_frame);
if (err < 0)
break;
}
return err;
fail_after_begin:
vaRenderPicture(ctx->hwctx->display, ctx->va_context, &params_id, 1);
@ -592,6 +624,17 @@ static const AVOption deint_vaapi_options[] = {
0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingMotionAdaptive }, .unit = "mode" },
{ "motion_compensated", "Use the motion compensated deinterlacing algorithm",
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 },
};