diff --git a/LICENSE.md b/LICENSE.md index 915575e07f..4fbe1370ac 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -46,6 +46,7 @@ Specifically, the GPL parts of FFmpeg are: - vf_pullup.c - vf_sab.c - vf_smartblur.c + - vf_repeatfields.c - vf_spp.c - vf_stereo3d.c - vf_super2xsai.c diff --git a/configure b/configure index f66ab42cd6..1d14d0abd4 100755 --- a/configure +++ b/configure @@ -2613,6 +2613,7 @@ phase_filter_deps="gpl" pp_filter_deps="gpl postproc" pullup_filter_deps="gpl" removelogo_filter_deps="avcodec avformat swscale" +repeatfields_filter_deps="gpl" resample_filter_deps="avresample" sab_filter_deps="gpl swscale" scale_filter_deps="swscale" diff --git a/doc/filters.texi b/doc/filters.texi index a90825aa98..2f29c46f9c 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -7418,6 +7418,11 @@ much, but it will increase the amount of blurring needed to cover over the image and will destroy more information than necessary, and extra pixels will slow things down on a large logo. +@section repeatfields + +This filter uses the repeat_field flag from the Video ES headers and hard repeats +fields based on its value. + @section rotate Rotate video by an arbitrary angle expressed in radians. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index 3434d5bf19..21a3fbe7ce 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -169,6 +169,7 @@ OBJS-$(CONFIG_PSNR_FILTER) += vf_psnr.o dualinput.o framesync. OBJS-$(CONFIG_PULLUP_FILTER) += vf_pullup.o OBJS-$(CONFIG_QP_FILTER) += vf_qp.o OBJS-$(CONFIG_REMOVELOGO_FILTER) += bbox.o lswsutils.o lavfutils.o vf_removelogo.o +OBJS-$(CONFIG_REPEATFIELDS_FILTER) += vf_repeatfields.o OBJS-$(CONFIG_ROTATE_FILTER) += vf_rotate.o OBJS-$(CONFIG_SEPARATEFIELDS_FILTER) += vf_separatefields.o OBJS-$(CONFIG_SAB_FILTER) += vf_sab.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 3b13ed77ff..9c6f2aeb0c 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -184,6 +184,7 @@ void avfilter_register_all(void) REGISTER_FILTER(PULLUP, pullup, vf); REGISTER_FILTER(QP, qp, vf); REGISTER_FILTER(REMOVELOGO, removelogo, vf); + REGISTER_FILTER(REPEATFIELDS, repeatfields, vf); REGISTER_FILTER(ROTATE, rotate, vf); REGISTER_FILTER(SAB, sab, vf); REGISTER_FILTER(SCALE, scale, vf); diff --git a/libavfilter/vf_repeatfields.c b/libavfilter/vf_repeatfields.c new file mode 100644 index 0000000000..5dbfd52d68 --- /dev/null +++ b/libavfilter/vf_repeatfields.c @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2003 Tobias Diedrich + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with FFmpeg; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "libavutil/imgutils.h" +#include "avfilter.h" +#include "internal.h" + +typedef struct RepeatFieldsContext { + const AVClass *class; + int state; + int nb_planes; + int linesize[4]; + int planeheight[4]; + AVFrame *frame; +} RepeatFieldsContext; + +static av_cold void uninit(AVFilterContext *ctx) +{ + RepeatFieldsContext *s = ctx->priv; + + av_frame_free(&s->frame); +} + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pixel_fmts_eq[] = { + AV_PIX_FMT_GRAY8, + AV_PIX_FMT_YUV410P, + AV_PIX_FMT_YUV411P, + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUV422P, + AV_PIX_FMT_YUV444P, + AV_PIX_FMT_NONE + }; + + ff_set_common_formats(ctx, ff_make_format_list(pixel_fmts_eq)); + + return 0; +} + +static int config_input(AVFilterLink *inlink) +{ + RepeatFieldsContext *s = inlink->dst->priv; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + int ret; + + if ((ret = av_image_fill_linesizes(s->linesize, inlink->format, inlink->w)) < 0) + return ret; + + s->planeheight[1] = s->planeheight[2] = FF_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); + s->planeheight[0] = s->planeheight[3] = inlink->h; + + s->nb_planes = av_pix_fmt_count_planes(inlink->format); + + return 0; +} + +static void update_pts(AVFilterLink *link, AVFrame *f, int64_t pts, int fields) +{ + if (av_cmp_q(link->frame_rate, (AVRational){30000, 1001}) == 0 && + av_cmp_q(link->time_base, (AVRational){1001, 60000}) <= 0 + ) { + f->pts = pts + av_rescale_q(fields, (AVRational){1001, 60000}, link->time_base); + } else + f->pts = AV_NOPTS_VALUE; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *in) { + AVFilterContext *ctx = inlink->dst; + AVFilterLink *outlink = inlink->dst->outputs[0]; + RepeatFieldsContext *s = ctx->priv; + AVFrame *out; + int ret, i; + int state = s->state; + + if (!s->frame) { + s->frame = av_frame_clone(in); + if (!s->frame) + return AVERROR(ENOMEM); + s->frame->pts = AV_NOPTS_VALUE; + } + + out = s->frame; + + if ((state == 0 && !in->top_field_first) || + (state == 1 && in->top_field_first)) { + av_log(ctx, AV_LOG_WARNING, "Unexpected field flags: " + "state=%d top_field_first=%d repeat_first_field=%d\n", + state, in->top_field_first, in->repeat_pict); + state ^= 1; + } + + if (state == 0) { + AVFrame *new; + + new = av_frame_clone(in); + if (!new) + return AVERROR(ENOMEM); + + ret = ff_filter_frame(outlink, new); + + if (in->repeat_pict) { + av_frame_make_writable(out); + update_pts(outlink, out, in->pts, 2); + for (i = 0; i < s->nb_planes; i++) { + av_image_copy_plane(out->data[i], out->linesize[i] * 2, + in->data[i], in->linesize[i] * 2, + s->linesize[i], s->planeheight[i] / 2); + } + state = 1; + } + } else { + for (i = 0; i < s->nb_planes; i++) { + av_frame_make_writable(out); + av_image_copy_plane(out->data[i] + out->linesize[i], out->linesize[i] * 2, + in->data[i] + in->linesize[i], in->linesize[i] * 2, + s->linesize[i], s->planeheight[i] / 2); + } + + ret = ff_filter_frame(outlink, av_frame_clone(out)); + + if (in->repeat_pict) { + AVFrame *new; + + new = av_frame_clone(in); + if (!new) + return AVERROR(ENOMEM); + + ret = ff_filter_frame(outlink, new); + state = 0; + } else { + av_frame_make_writable(out); + update_pts(outlink, out, in->pts, 1); + for (i = 0; i < s->nb_planes; i++) { + av_image_copy_plane(out->data[i], out->linesize[i] * 2, + in->data[i], in->linesize[i] * 2, + s->linesize[i], s->planeheight[i] / 2); + } + } + } + + s->state = state; + + av_frame_free(&in); + return ret; +} + +static const AVFilterPad repeatfields_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + .config_props = config_input, + }, + { NULL } +}; + +static const AVFilterPad repeatfields_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, + { NULL } +}; + +AVFilter ff_vf_repeatfields = { + .name = "repeatfields", + .description = NULL_IF_CONFIG_SMALL("Hard repeat fields based on MPEG repeat field flag."), + .priv_size = sizeof(RepeatFieldsContext), + .uninit = uninit, + .inputs = repeatfields_inputs, + .outputs = repeatfields_outputs, + .query_formats = query_formats, +};