1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2025-08-10 06:10:52 +02:00

avfilter/libplacebo: add option for outputting separate fields

cf. the other deinterlacing-aware filters.
This commit is contained in:
Niklas Haas
2025-02-16 16:56:18 +01:00
parent 83c1b34964
commit 27814dc3b6
2 changed files with 25 additions and 3 deletions

View File

@@ -16632,6 +16632,11 @@ Yet another deinterlacing filter. See the @ref{yadif} filter for more details.
@item skip_spatial_check @item skip_spatial_check
Skip the spatial deinterlacing check when using @code{yadif} deinterlacing. Skip the spatial deinterlacing check when using @code{yadif} deinterlacing.
@item send_fields
Output a frame for each field, rather than for each frame. Note that this will
always double the tagged output frame rate, even if the input does not contain
any interlaced frames. Disabled by default.
@end table @end table
@subsubsection Debanding @subsubsection Debanding

View File

@@ -215,6 +215,7 @@ typedef struct LibplaceboContext {
/* pl_deinterlace_params */ /* pl_deinterlace_params */
int deinterlace; int deinterlace;
int skip_spatial_check; int skip_spatial_check;
int send_fields;
/* pl_deband_params */ /* pl_deband_params */
int deband; int deband;
@@ -967,8 +968,7 @@ static int handle_input(AVFilterContext *ctx, LibplaceboInput *input)
int64_t pts; int64_t pts;
while ((ret = ff_inlink_consume_frame(inlink, &in)) > 0) { while ((ret = ff_inlink_consume_frame(inlink, &in)) > 0) {
in->opaque = s; struct pl_source_frame src = {
pl_queue_push(input->queue, &(struct pl_source_frame) {
.pts = in->pts * av_q2d(inlink->time_base), .pts = in->pts * av_q2d(inlink->time_base),
.duration = in->duration * av_q2d(inlink->time_base), .duration = in->duration * av_q2d(inlink->time_base),
.first_field = s->deinterlace ? pl_field_from_avframe(in) : PL_FIELD_NONE, .first_field = s->deinterlace ? pl_field_from_avframe(in) : PL_FIELD_NONE,
@@ -976,12 +976,21 @@ static int handle_input(AVFilterContext *ctx, LibplaceboInput *input)
.map = map_frame, .map = map_frame,
.unmap = unmap_frame, .unmap = unmap_frame,
.discard = discard_frame, .discard = discard_frame,
}); };
in->opaque = s;
pl_queue_push(input->queue, &src);
if (!s->fps.num) { if (!s->fps.num) {
/* Internally queue an output frame for the same PTS */ /* Internally queue an output frame for the same PTS */
pts = av_rescale_q(in->pts, inlink->time_base, outlink->time_base); pts = av_rescale_q(in->pts, inlink->time_base, outlink->time_base);
av_fifo_write(input->out_pts, &pts, 1); av_fifo_write(input->out_pts, &pts, 1);
if (s->send_fields && src.first_field != PL_FIELD_NONE) {
/* Queue the second field for interlaced content */
pts += av_rescale_q(in->duration, inlink->time_base, outlink->time_base) / 2;
av_fifo_write(input->out_pts, &pts, 1);
}
} }
} }
@@ -1239,6 +1248,13 @@ static int libplacebo_config_output(AVFilterLink *outlink)
avctx->inputs[i]->time_base, avctx->inputs[i]->time_base,
AV_TIME_BASE / 2, AV_TIME_BASE_Q); AV_TIME_BASE / 2, AV_TIME_BASE_Q);
} }
if (s->deinterlace && s->send_fields) {
const AVRational q2 = { 2, 1 };
ol->frame_rate = av_mul_q(ol->frame_rate, q2);
/* Ensure output frame timestamps are divisible by two */
outlink->time_base = av_div_q(outlink->time_base, q2);
}
} }
/* Static variables */ /* Static variables */
@@ -1373,6 +1389,7 @@ static const AVOption libplacebo_options[] = {
{ "bob", "Naive bob deinterlacing", 0, AV_OPT_TYPE_CONST, {.i64 = PL_DEINTERLACE_BOB}, 0, 0, STATIC, .unit = "deinterlace" }, { "bob", "Naive bob deinterlacing", 0, AV_OPT_TYPE_CONST, {.i64 = PL_DEINTERLACE_BOB}, 0, 0, STATIC, .unit = "deinterlace" },
{ "yadif", "Yet another deinterlacing filter", 0, AV_OPT_TYPE_CONST, {.i64 = PL_DEINTERLACE_YADIF}, 0, 0, STATIC, .unit = "deinterlace" }, { "yadif", "Yet another deinterlacing filter", 0, AV_OPT_TYPE_CONST, {.i64 = PL_DEINTERLACE_YADIF}, 0, 0, STATIC, .unit = "deinterlace" },
{ "skip_spatial_check", "Skip yadif spatial check", OFFSET(skip_spatial_check), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, { "skip_spatial_check", "Skip yadif spatial check", OFFSET(skip_spatial_check), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC },
{ "send_fields", "Output a frame for each field", OFFSET(send_fields), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC },
{ "deband", "Enable debanding", OFFSET(deband), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, { "deband", "Enable debanding", OFFSET(deband), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC },
{ "deband_iterations", "Deband iterations", OFFSET(deband_iterations), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 16, DYNAMIC }, { "deband_iterations", "Deband iterations", OFFSET(deband_iterations), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 16, DYNAMIC },