diff --git a/doc/filters.texi b/doc/filters.texi index f5740394e6..3b5e556eef 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -2801,6 +2801,13 @@ even frames, generating a frame with unchanged height at half framerate. @item 5 Interleave the lower field from odd frames with the upper field from even frames, generating a frame with unchanged height at half framerate. + +@item 6 +Double frame rate with unchanged height. Frames are inserted each +containing the second temporal field from the previous input frame and +the first temporal field from the next input frame. This mode relies on +the top_field_first flag. Useful for interlaced video displays with no +field synchronisation. @end table Default mode is 0. diff --git a/libavfilter/version.h b/libavfilter/version.h index 0f7ea501e6..c79f37de73 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -30,7 +30,7 @@ #define LIBAVFILTER_VERSION_MAJOR 2 #define LIBAVFILTER_VERSION_MINOR 72 -#define LIBAVFILTER_VERSION_MICRO 100 +#define LIBAVFILTER_VERSION_MICRO 101 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ LIBAVFILTER_VERSION_MINOR, \ diff --git a/libavfilter/vf_tinterlace.c b/libavfilter/vf_tinterlace.c index 62e8d46d25..683aa0c551 100644 --- a/libavfilter/vf_tinterlace.c +++ b/libavfilter/vf_tinterlace.c @@ -68,9 +68,9 @@ static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque) if (args) { n = sscanf(args, "%d", &tinterlace->mode); - if (n != 1 || tinterlace->mode < 0 || tinterlace->mode > 5) { + if (n != 1 || tinterlace->mode < 0 || tinterlace->mode > 6) { av_log(ctx, AV_LOG_ERROR, - "Invalid mode '%s', use an integer between 0 and 5\n", args); + "Invalid mode '%s', use an integer between 0 and 6\n", args); return AVERROR(EINVAL); } } @@ -253,6 +253,31 @@ static void end_frame(AVFilterLink *inlink) tff ? FIELD_LOWER : FIELD_UPPER, 1, tff ? FIELD_LOWER : FIELD_UPPER); avfilter_unref_bufferp(&tinterlace->next); break; + case 6: /* re-interlace preserving image height, double frame rate */ + /* output current frame first */ + out = avfilter_ref_buffer(cur, AV_PERM_READ); + + avfilter_start_frame(outlink, out); + avfilter_draw_slice(outlink, 0, outlink->h, 1); + avfilter_end_frame(outlink); + + /* output mix of current and next frame */ + tff = next->video->top_field_first; + out = avfilter_get_video_buffer(outlink, AV_PERM_WRITE, outlink->w, outlink->h); + avfilter_copy_buffer_ref_props(out, next); + out->video->interlaced = 1; + + /* write current frame second field lines into the second field of the new frame */ + copy_picture_field(out->data, out->linesize, + cur->data, cur->linesize, + inlink->format, inlink->w, inlink->h, + tff ? FIELD_LOWER : FIELD_UPPER, 1, tff ? FIELD_LOWER : FIELD_UPPER); + /* write next frame first field lines into the first field of the new frame */ + copy_picture_field(out->data, out->linesize, + next->data, next->linesize, + inlink->format, inlink->w, inlink->h, + tff ? FIELD_UPPER : FIELD_LOWER, 1, tff ? FIELD_UPPER : FIELD_LOWER); + break; } avfilter_start_frame(outlink, out);