Am 07.07.19 um 15:45 schrieb Paul B Mahol:
> Could you just send whole patch?
Here it is
Please excuse the "under construction" state. The command line t use:

./ffmpeg -y -v warning -i in.mp4 -vf
lineshiftrecover=10:-564:60:-60:report=shifts -q 5 out.mp4

I can send you the in.mp4 I use.

-Ulf

From a54db59a5303a12f87c37cc963b03bc52726d258 Mon Sep 17 00:00:00 2001
From: Ulf Zibis <[email protected]>
Date: 07.07.2019, 16:21:49

avfilter/lineshiftrecover: new filter (has memory access error)

diff --git a/Changelog b/Changelog
index 57476c3..41b7af9 100644
--- a/Changelog
+++ b/Changelog
@@ -34,6 +34,7 @@
 - VP4 video decoder
 - IFV demuxer
 - derain filter
+- lineshiftrecover video filter


 version 4.1:
diff --git a/doc/filters.texi b/doc/filters.texi
index 2d9af46..479fb19 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -16770,6 +16770,64 @@
 @section swapuv
 Swap U & V plane.

+@section lineshiftrecover (tapeglitchrecover)
+
+Detect irregularly horizontally shifted lines and recover them to their proper position.
+Videos, grabbed from analog tape, e.g. VHS cassette, often have such artefacts,
+typically at the bottom of the screen, mostly from
+@url{https://bavc.github.io/avaa/artifacts/head_switching_noise.html head switching}.
+Currently only the luminance of the signal is taken into account for the matching.
+After using this filter, using a denoise filter on these lines likely makes sense.
+
+To find the most matching shift, each line is compared against it's before line (reference line).
+
+This filter accepts the following options:
+
+@table @option
+@item lines
+Number of lines to investigate. If negative, investigate upwards. Default is 2 % of @var{h}.
+
+@item start
+Start the detection here. Default is @var{h - lines}, or @var{-lines} if negative.
+If negative, crosscheck the found shift of the last line against the following line and approximate the shifts accordingly.
+
+@item span
+Horizontal span where to search for the best matching shift. Default is 10 % of @var{w}.
+
+@item spanl
+Selectively set the left span where to search. Because a shift to the left is rare, default is @var{-span / 2}.
+
+@item ignore
+Pixels at the borders of the relative reference line to ignore for comparison. Default is @var{span}.
+
+@item ignorel
+Pixels at the left border of the reference line to ignore. Default is @var{ignore * -spanl / span}
+if span is both-sided, otherwise @var{ignore} if set or @var{-spanl}.
+
+To evaluate the impact of the options use @var{report=debug}.
+
+@item report
+Set report mode. Default is @var{fails}.
+
+It accepts the following values:
+@table @samp
+@item fails
+If no optimal shift value is found for a line, report the nearest and actually used shift.
+
+@item shifts
+Report found and actually used shift values except 0, includes @var{fails}.
+
+@item verbose
+Verbosely report all line differences (quadratic) per possible shift values, includes @var{shifts}.
+It is recommended to only use this on one or very few single frames.
+
+@item debug
+Report all single pixel differences (quadratic) per possible shift values, includes @var{verbose}.
+As these report lines become very long, it is recommended to redirect the output into a file.
+@end table
+
+@end table
+
 @section telecine

 Apply telecine process to the video.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 07ea8d7..1da97b6 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -277,6 +277,7 @@
 OBJS-$(CONFIG_LENSFUN_FILTER)                += vf_lensfun.o
 OBJS-$(CONFIG_LIBVMAF_FILTER)                += vf_libvmaf.o framesync.o
 OBJS-$(CONFIG_LIMITER_FILTER)                += vf_limiter.o
+OBJS-$(CONFIG_LINESHIFTRECOVER_FILTER)       += vf_lineshiftrecover.o
 OBJS-$(CONFIG_LOOP_FILTER)                   += f_loop.o
 OBJS-$(CONFIG_LUMAKEY_FILTER)                += vf_lumakey.o
 OBJS-$(CONFIG_LUT1D_FILTER)                  += vf_lut3d.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index 9c846b1..93b0a73 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -262,6 +262,7 @@
 extern AVFilter ff_vf_lensfun;
 extern AVFilter ff_vf_libvmaf;
 extern AVFilter ff_vf_limiter;
+extern AVFilter ff_vf_lineshiftrecover;
 extern AVFilter ff_vf_loop;
 extern AVFilter ff_vf_lumakey;
 extern AVFilter ff_vf_lut;
diff --git a/libavfilter/vf_lineshiftrecover.c b/libavfilter/vf_lineshiftrecover.c
new file mode 100644
index 0000000..cc5acd6
--- /dev/null
+++ b/libavfilter/vf_lineshiftrecover.c
@@ -0,0 +1,759 @@
+/*
+ * Copyright (c) 2019 Ulf Zibis
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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/colorspace.h"
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/timestamp.h"
+#include "drawutils.h"
+#include "internal.h"
+
+typedef struct Borders {
+    int left, right, top, bottom;
+} Borders;
+
+typedef struct LineShiftContext {
+    const AVClass *class;
+    /* Command line options: */
+    int lines, start, span, spanl, ignore, ignorel;
+    int report;
+    uint8_t rgba_color[4];
+    /* Internal: */
+    int crosscheck;
+    int span_l, span_r, ign_l, ign_r;
+    int nb_planes;
+    int planewidth[4];
+    int planeheight[4];
+    Borders borders[4];
+    void *filler[4];
+    int16_t *shifts;
+    int16_t **shifts_sums;
+
+    void (*recoverlineshifts)(struct AVFilterLink *inlink, AVFrame *frame);
+} LineShiftContext;
+
+enum Reports { R_FAILS, R_SHIFTS, R_VERBOSE, R_DEBUG, R_MAX };
+
+#define OFFSET(x) offsetof(LineShiftContext, x)
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
+static const AVOption lineshiftrecover_options[] = {
+    { "lines",    "set lines to investigate",   OFFSET(lines),   AV_OPT_TYPE_INT, {.i64=INT_MAX},  INT_MIN, INT_MAX,    FLAGS },
+    { "start",    "set start of investigation", OFFSET(start),   AV_OPT_TYPE_INT, {.i64=INT_MAX},  INT_MIN, INT_MAX,    FLAGS },
+    { "span",     "set span to investigate",    OFFSET(span),    AV_OPT_TYPE_INT, {.i64=INT_MAX},  INT_MIN, INT_MAX,    FLAGS },
+    { "spanl",    "set left span",              OFFSET(spanl),   AV_OPT_TYPE_INT, {.i64=INT_MAX},  INT_MIN, INT_MAX,    FLAGS },
+    { "ignore",   "set border to ignore",       OFFSET(ignore),  AV_OPT_TYPE_INT, {.i64=INT_MAX},  INT_MIN, INT_MAX,    FLAGS },
+    { "ignorel",  "set left border to ignore",  OFFSET(ignorel), AV_OPT_TYPE_INT, {.i64=INT_MAX},  INT_MIN, INT_MAX,    FLAGS },
+    { "report",   "set the report mode",        OFFSET(report),  AV_OPT_TYPE_INT, {.i64=R_FAILS}, 0, R_MAX-1, FLAGS, "report" },
+        { "fails",   NULL, 0, AV_OPT_TYPE_CONST, {.i64=R_FAILS},  0, 0, FLAGS, "report" },
+        { "shifts",  NULL, 0, AV_OPT_TYPE_CONST, {.i64=R_SHIFTS}, 0, 0, FLAGS, "report" },
+        { "verbose", NULL, 0, AV_OPT_TYPE_CONST, {.i64=R_VERBOSE}, 0, 0, FLAGS, "report" },
+        { "debug",   NULL, 0, AV_OPT_TYPE_CONST, {.i64=R_DEBUG}, 0, 0, FLAGS, "report" },
+/*
+    { "color",  "set the color for the fixed mode", OFFSET(rgba_color), AV_OPT_TYPE_COLOR, {.str = "black"}, .flags = FLAGS },
+*/
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(lineshiftrecover);
+
+/***********************/
+/*  Private functions  */
+/***********************/
+
+static float diff8(LineShiftContext *s, uint8_t *lineref, int shift_ref, int width, uint8_t *line, int shift)
+{
+    int startref = FFMAX(-shift, s->ign_l);
+    int tailref =  FFMAX(shift, s->ign_r);
+/*
+    int widthdiff = width - FFMAX(-shift, s->ign_r) - FFMAX(shift, s->ign_l);
+*/
+    int widthdiff = width - startref - tailref;
+    int start = FFMAX(shift + s->ign_l, 0);
+    float sum = 0;
+    char printwidth[7];
+
+    int debug = s->report == R_DEBUG;
+    if (debug) {
+        int tab = 6;
+        char format1[16] = "x:      ";
+        char format2[16] = "\nlineref:";
+        char format3[16] = "\nline:   ";
+        char format4[16] = "\ndiff:   ";
+        const char space[] = "";
+        snprintf(printwidth, sizeof(printwidth), "%%%ds", startref * tab);
+        strcat(format3, printwidth);
+        strcat(format4, printwidth);
+        snprintf(printwidth, sizeof(printwidth), "%%%dd", tab);
+/*
+        av_log(NULL, AV_LOG_ERROR, "shift: %4d, lineref: %x, startref: %d\n", shift, lineref, startref);
+*/
+        av_log(NULL, AV_LOG_ERROR, format1, space);
+        for (int x = 0; x < width; x++)
+            av_log(NULL, AV_LOG_ERROR, printwidth, x);
+        av_log(NULL, AV_LOG_ERROR, format2, space);
+        for (int x = 0; x < width; x++)
+            av_log(NULL, AV_LOG_ERROR, printwidth, lineref[x]);
+        av_log(NULL, AV_LOG_ERROR, format3, space);
+        for (int x = start; x < start + widthdiff; x++)
+            av_log(NULL, AV_LOG_ERROR, printwidth, line[x]);
+        av_log(NULL, AV_LOG_ERROR, format4, space);
+    }
+
+    for (int x = 0; x < widthdiff; x++) {
+        float diff = pow(line[start + x] - lineref[startref + x], 2);
+        sum += diff;
+        if (debug)
+            av_log(NULL, AV_LOG_ERROR, printwidth, (int)diff);
+    }
+    if (debug)
+        av_log(NULL, AV_LOG_ERROR, "\n");
+    return sum / widthdiff;
+}
+
+static float diff8_3(LineShiftContext *s, uint8_t *lineref, int shift_ref, int width, uint8_t *line, int shift)
+{
+    int startref = FFMAX3(-shift_ref, -shift, s->ign_l);
+    int tailref =  FFMAX3(shift_ref, shift, s->ign_r);
+    int widthdiff = width - startref - tailref;
+    int start = FFMAX(shift + s->ign_l, 0);
+    float sum = 0;
+
+    lineref += shift_ref;
+    if (s->report < R_DEBUG)
+        for (int x = widthdiff; x-- > 0; ) // loop against 0 is faster
+            sum += pow(line[start + x] - lineref[startref + x], 2);
+    else {
+        av_log(NULL, AV_LOG_ERROR, "lineref: %p, shift_ref: %4d, startref: %d, tailref: %d, line: %p, shift: %4d, start: %d\n",
+                lineref, shift_ref, startref, tailref, line, shift, start);
+        int tab = 6;
+        char fieldformat[16];
+        char formatx[16] =    "x:      ";
+        char formatlr[16] = "\nlineref:";
+        char formatln[16] = "\nline:   ";
+        char formatdf[16] = "\ndiff:   ";
+        const char space[] = "";
+        snprintf(fieldformat, sizeof(fieldformat), "%%%ds", startref * tab);
+        strcat(formatln, fieldformat);
+        strcat(formatdf, fieldformat);
+        snprintf(fieldformat, sizeof(fieldformat), "%%%dd", tab);
+        av_log(NULL, AV_LOG_ERROR, formatx, space);
+        for (int x = 0; x < width; x++)
+            av_log(NULL, AV_LOG_ERROR, fieldformat, x);
+        av_log(NULL, AV_LOG_ERROR, formatlr, space);
+        for (int x = 0; x < width; x++)
+            av_log(NULL, AV_LOG_ERROR, fieldformat, lineref[x]);
+        av_log(NULL, AV_LOG_ERROR, formatln, space);
+        for (int x = start; x < start + widthdiff; x++)
+            av_log(NULL, AV_LOG_ERROR, fieldformat, line[x]);
+        av_log(NULL, AV_LOG_ERROR, formatdf, space);
+        for (int x = 0; x < widthdiff; x++) {
+            float diff = pow(line[start + x] - lineref[startref + x], 2);
+            av_log(NULL, AV_LOG_ERROR, fieldformat, (int)diff);
+            sum += diff;
+        }
+        av_log(NULL, AV_LOG_ERROR, "\n");
+    }
+    return sum / widthdiff;
+}
+
+static float diff8_debug(LineShiftContext *s, uint8_t *lineref, int shift_ref, int width, uint8_t *line, int shift)
+{
+    av_log(NULL, AV_LOG_ERROR, "lineref: %p, shift_ref: %4d, width: %d, line: %p, shift: %4d\n",
+            lineref, shift_ref, width, line, shift);
+    int ref_start  =         FFMAX(-shift_ref, 0);
+    int ref_end    = width - FFMAX(shift_ref, 0);
+    int line_start =         FFMAX(shift, 0);
+    int line_end   = width - FFMAX(-shift, 0);
+    int diff_start =         FFMAX3(-shift_ref, -shift, s->ign_l);
+    int diff_width = width - diff_start - FFMAX3(shift_ref, shift, s->ign_r);
+    int line_diff_start =    FFMAX(shift + s->ign_l, 0);
+    float sum = 0;
+    lineref += shift_ref;
+    av_log(NULL, AV_LOG_ERROR, "lineref: %p, ref_start: %d, ref_end: %d, line_start: %d, line_end: %d, diff_start: %d, diff_width: %d, line_diff_start: %d\n",
+            lineref, ref_start, ref_end, line_start, line_end, diff_start, diff_width, line_diff_start);
+
+    int tab = 6;
+    char fieldformat[16];
+    char formatx[16] =    "x:      ";
+    char formatlr[16] = "\nlineref:";
+    char formatln[16] = "\nline:   ";
+    char formatdf[16] = "\ndiff:   ";
+    const char indent[] = "";
+    snprintf(fieldformat, sizeof(fieldformat), "%%%ds", ref_start * tab);
+    strcat(formatlr, fieldformat);
+    snprintf(fieldformat, sizeof(fieldformat), "%%%ds", (width - line_end) * tab);
+    strcat(formatln, fieldformat);
+    snprintf(fieldformat, sizeof(fieldformat), "%%%ds", diff_start * tab);
+    strcat(formatdf, fieldformat);
+    snprintf(fieldformat, sizeof(fieldformat), "%%%dd", tab);
+    av_log(NULL, AV_LOG_ERROR, formatx, indent);
+    for (int x = 0; x < width; x++)
+        av_log(NULL, AV_LOG_ERROR, fieldformat, x);
+    av_log(NULL, AV_LOG_ERROR, formatlr, indent);
+    for (int x = ref_start; x < ref_end; x++)
+        av_log(NULL, AV_LOG_ERROR, fieldformat, lineref[x]);
+    av_log(NULL, AV_LOG_ERROR, formatln, indent);
+    for (int x = line_start; x < line_end; x++)
+        av_log(NULL, AV_LOG_ERROR, fieldformat, line[x]);
+    av_log(NULL, AV_LOG_ERROR, formatdf, indent);
+
+    lineref += diff_start;
+    line += line_diff_start;
+    for (int x = 0; x < diff_width; x++) {
+        float diff = pow(lineref[x] - line[x], 2);
+        av_log(NULL, AV_LOG_ERROR, fieldformat, (int)diff);
+        sum += diff;
+    }
+    av_log(NULL, AV_LOG_ERROR, "\n");
+    return sum / diff_width;
+}
+
+inline static float diff8_5(uint8_t *lineref, uint8_t *line, int width)
+{
+    float sum = 0;
+    for (int x = width; x-- > 0; ) // loop against 0 is faster
+        sum += pow(lineref[x] - line[x], 2);
+    return sum / width;
+
+}
+
+inline static float diff8_5_debug(uint8_t *lineref, int shift_ref, uint8_t *line, int shift, int width, int diff_start, int diff_width)
+{
+    av_log(NULL, AV_LOG_ERROR, "lineref: %p, shift_ref: %4d, width: %d, line: %p, shift: %4d\n",
+            lineref, shift_ref, width, line, shift);
+    int ref_start  =         FFMAX(-shift_ref, 0);
+    int ref_end    = width - FFMAX(shift_ref, 0);
+    int line_start =         FFMAX(shift, 0);
+    int line_end   = width - FFMAX(-shift, 0);
+    float sum = 0;
+    av_log(NULL, AV_LOG_ERROR, "ref_start: %d, ref_end: %d, line_start: %d, line_end: %d, diff_start: %d, diff_width: %d\n",
+            ref_start, ref_end, line_start, line_end, diff_start, diff_width);
+
+    int tab = 6;
+    char fieldformat[16];
+    char formatx[16] =    "x:      ";
+    char formatlr[16] = "\nlineref:";
+    char formatln[16] = "\nline:   ";
+    char formatdf[16] = "\ndiff:   ";
+    const char indent[] = "";
+    snprintf(fieldformat, sizeof(fieldformat), "%%%ds", ref_start * tab);
+    strcat(formatlr, fieldformat);
+    snprintf(fieldformat, sizeof(fieldformat), "%%%ds", (width - line_end) * tab);
+    strcat(formatln, fieldformat);
+    snprintf(fieldformat, sizeof(fieldformat), "%%%ds", diff_start * tab);
+    strcat(formatdf, fieldformat);
+    snprintf(fieldformat, sizeof(fieldformat), "%%%dd", tab);
+    av_log(NULL, AV_LOG_ERROR, formatx, indent);
+    for (int x = 0; x < width; x++)
+        av_log(NULL, AV_LOG_ERROR, fieldformat, x);
+    av_log(NULL, AV_LOG_ERROR, formatlr, indent);
+    for (int x = ref_start; x < ref_end; x++)
+        av_log(NULL, AV_LOG_ERROR, fieldformat, lineref[x]);
+    av_log(NULL, AV_LOG_ERROR, formatln, indent);
+    for (int x = line_start; x < line_end; x++)
+        av_log(NULL, AV_LOG_ERROR, fieldformat, line[x]);
+    av_log(NULL, AV_LOG_ERROR, formatdf, indent);
+
+    lineref += diff_start;
+    line += diff_start + shift;
+    for (int x = 0; x < diff_width; x++) {
+        float diff = pow(lineref[x] - line[x], 2);
+        av_log(NULL, AV_LOG_ERROR, fieldformat, (int)diff);
+        sum += diff;
+    }
+    av_log(NULL, AV_LOG_ERROR, "\n");
+    return sum / diff_width;
+}
+
+static int calculate_shift8(LineShiftContext *s, uint8_t *lineref, int shift_ref, uint8_t *line, int width)
+{
+/*
+    av_log(NULL, AV_LOG_ERROR, "line: %16X, lineref: %16X, width: %4d\n", line, lineref, width);
+*/
+    int shift_best = 0;
+    float diff, diff_min = MAXFLOAT;
+    lineref += shift_ref;
+
+    for (int shift = s->span_l; shift <= s->span_r; shift++) {
+        int diff_start = FFMAX3(s->ign_l, -shift_ref, -shift);
+        int diff_width = width - diff_start - FFMAX3(s->ign_r, shift_ref, shift);
+
+        if (s->report < R_DEBUG)
+            diff = diff8_5(lineref + diff_start, line + diff_start + shift, diff_width);
+        else
+            diff = diff8_5_debug(lineref, shift_ref, line, shift, width, diff_start, diff_width);
+/*
+        if (diff <= diff_min) {
+            if (diff < diff_min || abs(x) < abs(shift)) // prefer smaller shift if ambiguous
+                shift_best = shift;
+            if (diff < diff_min)
+                diff_min = diff;
+        }
+*/
+        if (diff < diff_min)
+            goto all;
+        if (diff == diff_min && abs(shift) < abs(shift_best)) // prefer smaller shift if ambiguous
+            goto shift;
+        else
+            goto none;
+all:    diff_min = diff;
+shift:  shift_best = shift;
+none:
+
+        if (s->report >= R_VERBOSE)
+            av_log(NULL, AV_LOG_ERROR, "shift: %3d, diff: %8.2f, diff_min: %8.2f, shift_best: %3d\n", shift, diff, diff_min, shift_best);
+    }
+    return shift_best;
+}
+
+static void recoverlineshifts8(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    LineShiftContext *s = ctx->priv;
+    char *ptsstr = av_ts2str(frame->pts);
+    char *timestr = av_ts2timestr(frame->pts, &inlink->time_base);
+    char *minutestr = av_ts2minutestr(frame->pts, &inlink->time_base);
+    uint8_t *data = frame->data[0];
+    int16_t lz = frame->linesize[0] / sizeof(*data);
+    int width = s->planewidth[0];
+    int height = s->planeheight[0];
+    int increment = s->lines >= 0 ? 1 : -1; // if negative, run upwards
+/*
+    uint8_t *temp = (uint8_t *)s->filler[0];
+*/
+    int16_t *shifts = s->shifts;
+
+/*
+    av_log(NULL, AV_LOG_ERROR, "start: %d, lines: %d, increment: %d, width: %d, span_l: %d, span_r: %d, ign_l: %d, ign_r: %d\n",
+            s->start, s->lines, increment, width, s->span_l, s->span_r, s->ign_l, s->ign_r);
+*/
+    if (s->report >= R_SHIFTS)
+/*
+        av_log(NULL, AV_LOG_ERROR, "calculated shifts of frame %6d ...\n", frame->display_picture_number);
+        av_log(NULL, AV_LOG_ERROR, "calculated shifts of frame %6d ...\n", frame->coded_picture_number);
+*/
+        av_log(NULL, AV_LOG_ERROR, "calculated shifts of frame %6ld, pts: %-10s, time: %7s ...\n",
+                inlink->frame_count_out, ptsstr, minutestr);
+    int y = s->start;
+    for (; y != s->start + s->lines; y += increment) {
+/*
+        av_log(NULL, AV_LOG_ERROR, "y: %d, yref: %d\n", y, y - increment);
+        av_log(NULL, AV_LOG_ERROR, "y: %4d, yref: %4d, data: %X, y*lz: %4d, yref*lz: %4d\nlineref:", y, y - increment, data, y*lz, (y - increment)*lz);
+*/
+        if (s->report >= R_VERBOSE)
+            av_log(NULL, AV_LOG_ERROR, "differences of line %4d against %4d in span %4d to %4d ...\n",
+                    y, y - increment, s->span_l, s->span_r);
+        shifts[y] = calculate_shift8(s, data + (y - increment) * lz, shifts[y - increment], data + y * lz, width);
+
+        switch (s->report) {
+            case R_DEBUG:
+            case R_VERBOSE:
+            case R_SHIFTS: if (s->report > R_SHIFTS || shifts[y] != 0)
+                av_log(NULL, AV_LOG_ERROR, "shift of line %d against %d was: %d\n",
+                        y, y - increment, shifts[y]);
+            case R_FAILS: if (shifts[y] == s->span_l || shifts[y] == s->span_r)
+                av_log(ctx, AV_LOG_WARNING,
+                        "frame: %6ld, pts: %7s, time: %-10s - optimal shift in line %d against %d seems outside of span (%d - %d), was: %d\n",
+/*
+                        "frame: %6d, pts: %10ld - optimal shift in line %d against %d seems outside of span (%d - %d), was: %d\n",
+                        frame->display_picture_number, frame->pts, y, y - increment, s->span_l, s->span_r, shifts[y]);
+                        frame->coded_picture_number, frame->pts, y, y - increment, s->span_l, s->span_r, shifts[y]);
+                        frame_count, frame->pts, y, y - increment, s->span_l, s->span_r, shifts[y]);
+*/
+                        inlink->frame_count_out, ptsstr, minutestr, y, y - increment, s->span_l, s->span_r, shifts[y]);
+        }
+    }
+    if (s->crosscheck) {
+        if (s->report >= R_VERBOSE)
+            av_log(NULL, AV_LOG_ERROR, "crosscheck of line %d against %d ...\n", y - increment, y);
+        int shift_cc = calculate_shift8(s, data + y * lz, 0, data + (y - increment) * lz, width);
+        if (shift_cc != shifts[y - increment]) {
+            if (s->report >= R_SHIFTS)
+                av_log(NULL, AV_LOG_ERROR, "shift crosscheck of line %d against %d was: %d\nshifts of:       ",
+                        y - increment, y, shift_cc);
+            for (int y = 0; y != s->lines; y += increment) {
+                if (s->report >= R_SHIFTS)
+                    av_log(NULL, AV_LOG_ERROR, "%4d", shifts[s->start + y]);
+                shifts[s->start + y] -= (shifts[s->start + s->lines - increment] - shift_cc) * (y + increment) / s->lines;
+            }
+            if (s->report >= R_SHIFTS) {
+                av_log(NULL, AV_LOG_ERROR, "\nappproximated to:");
+                for (int y = 0; y != s->lines; y += increment)
+                    av_log(NULL, AV_LOG_ERROR, "%4d", shifts[s->start + y]);
+                av_log(NULL, AV_LOG_ERROR, "\n");
+            }
+        }
+        if (shift_cc == s->span_l || shift_cc == s->span_r)
+            av_log(ctx, AV_LOG_WARNING,
+                    "frame: %6ld, pts: %7s, time: %-10s - optimal shift crosscheck in line %d against %d seems outside of span (%d - %d), was: %d\n",
+/*
+                    "frame: %6d, pts: %10ld - optimal shift crosscheck in line %d against %d seems outside of span (%d - %d), was: %d\n",
+                    frame->display_picture_number, frame->pts, y, y - increment, s->span_l, s->span_r, shift_cc);
+                    frame->coded_picture_number, frame->pts, y, y - increment, s->span_l, s->span_r, shift_cc);
+                    frame_count, frame->pts, y, y - increment, s->span_l, s->span_r, shift_cc);
+*/
+                    inlink->frame_count_out, ptsstr, minutestr, y, y - increment, s->span_l, s->span_r, shift_cc);
+        if (s->report >= R_SHIFTS)
+            s->shifts_sums[s->lines][shift_cc - s->span_l] += 1;
+    }
+    if (s->report >= R_SHIFTS)
+/*
+        for (int y = s->start; y != s->start + s->lines; y += increment) {
+            for (int l = s->lines; --l >= 0; ) {
+                s->shifts_sums[l][shifts[y] - s->span_l] += 1;
+            }
+        }
+*/
+            for (int l = s->lines; --l >= 0; ) {
+                av_log(NULL, AV_LOG_ERROR, "line: %4d, shifts[%4d] %3d:, shifts_sums[%4d[%3d]: %6d\n",
+                        l, s->start + l * increment, shifts[s->start + l * increment], l, shifts[s->start + l * increment] - s->span_l, s->shifts_sums[l][shifts[s->start + l * increment] - s->span_l]);
+                s->shifts_sums[l][shifts[s->start + l * increment] - s->span_l] += 1;
+            }
+    /* move the lines to their correct position */
+    for (int y = s->start; y != s->start + s->lines; y += increment) {
+        if (shifts[y] != 0) { // shift current line
+            memmove(data + y * lz + FFMAX(0, -shifts[y]), data + y * lz + FFMAX(0, shifts[y]),
+                    (width - abs(shifts[y])) * sizeof(*data));
+            memcpy(data + y * lz, data + (y - increment) * lz,
+                    FFMAX(0, -shifts[y]) * sizeof(*data)); // fill left gap from reference line
+            memcpy(data + y * lz + width - FFMAX(0, shifts[y]), data + (y - increment) * lz + width - FFMAX(0, shifts[y]),
+                    FFMAX(0, shifts[y]) * sizeof(*data)); // fill right gap from reference line
+        }
+        if (shifts[y - increment] != 0) { // average the gaps of before line with content of current line
+/*
+            memcpy(temp, data + (y - increment) * lz, width);
+            if (s->report >= R_VERBOSE)
+                av_log(NULL, AV_LOG_ERROR, "diff old line %4d against it's now averaged with new %4d ...\n", y - increment, y);
+*/
+/*
+            for (int x = -shift_old; x-- > 0; )
+                data[(y - increment) * lz + x] = (data[(y - increment) * lz + x] + data[y * lz + x]) / 2;
+            for (int x = width - shift_old; x < width; x++)
+                data[(y - increment) * lz + x] = (data[(y - increment) * lz + x] + data[y * lz + x]) / 2;
+*/
+/*
+            int ign_l = s->ign_l, ign_r = s->ign_r;
+            s->ign_l = 0; s->ign_r = 0;
+            float diff = diff8(s, temp, width, data + (y - increment) * lz, 0);
+            s->ign_l = ign_l; s->ign_r = ign_r;
+            if (s->report >= R_VERBOSE)
+                av_log(NULL, AV_LOG_ERROR, "diff shifted against averaged: %8.2f\n", diff);
+*/
+        }
+    }
+}
+
+static void recoverlineshifts16(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    LineShiftContext *s = ctx->priv;
+    for (int p = 0; p < s->nb_planes; p++) {
+        uint16_t *data = (uint16_t *)frame->data[p];
+        int lz = frame->linesize[p] / sizeof(*data);
+        int width = s->planewidth[p];
+        int left = s->borders[p].left;
+        int right = width - s->borders[p].right;
+        int height = s->planeheight[p] * lz;
+        int top = s->borders[p].top * lz;
+        int bottom = height - s->borders[p].bottom * lz;
+
+        /* fill left and right borders from top to bottom border */
+        if (left + width > right) // in case skip for performance
+            for (int y = top; y < bottom; y += lz) {
+                for (int x = left; x > 0; )
+                    data[y + --x] = data[y + left];
+                for (int x = right; x < width; x++)
+                    data[y + x] = data[y + right - 1];
+            }
+
+        /* fill top and bottom borders */
+        for (int y = top; y > 0; )
+            memcpy(data +  (y -= lz), data + top, width * sizeof(*data));
+        for (int y = bottom; y < height; y += lz)
+            memcpy(data + y, data + bottom - lz, width * sizeof(*data));
+    }
+}
+
+/*****************************/
+/*  Global access functions  */
+/*****************************/
+
+static av_cold int init(AVFilterContext *ctx)
+{
+    LineShiftContext *s = ctx->priv;
+
+    return 0;
+}
+
+static int config_props(AVFilterLink *inlink)
+{
+    AVFilterContext *ctx = inlink->dst;
+    LineShiftContext *s = ctx->priv;
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
+    int depth = desc->comp[0].depth;
+
+    s->nb_planes = desc->nb_components;
+    s->planewidth [0] = s->planewidth [3] = inlink->w;
+    s->planeheight[0] = s->planeheight[3] = inlink->h;
+    s->planewidth [1] = s->planewidth [2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
+    s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
+
+    s->crosscheck = s->start < 0 ? 1 : 0;
+    s->lines = (s->lines != INT_MAX)   ? s->lines      : inlink->h * 0.02;
+    s->start = (s->start != INT_MAX)   ? abs(s->start) : (s->lines >= 0 ? inlink->h : 0) - s->lines;
+    s->span_r = (s->span != INT_MAX)   ? s->span       : inlink->w * 0.1;
+    s->span_l = (s->spanl != INT_MAX)  ? s->spanl      : -s->span_r / 2;
+    s->ign_r = (s->ignore != INT_MAX)  ? s->ignore     : FFMAX(s->span_r, 0);
+    s->ign_l = (s->ignorel != INT_MAX) ? s->ignorel    :
+        (s->span_l < 0) && (s->span_r > 0)
+                ? s->ign_r * -s->span_l / s->span_r // for both-sided span
+                : (s->ignore != INT_MAX) ? s->ignore : FFMAX(-s->span_l, 0); // one-sided span
+/*
+    s->ign_r = (s->ignore != INT_MIN)  ?
+        (s->span_r >= 0 ? s->ignore : FFMAX(s->ignore + s->span_l, 0) : FFMAX(s->span_r, 0);
+    s->ign_l = (s->ignorel != INT_MIN) ? s->ignorel    :
+        (s->span_l < 0)
+            ? (s->span_r > 0)
+                ? s->ign_r * -s->span_l / s->span_r // normal case
+                : (s->ignore != INT_MIN) ? s->ignore - s->span_l : -s->span_l
+            : FFMAX(s->ign_r - s->span_r, 0); // span leftsided
+*/
+    av_log(ctx, s->report >= R_SHIFTS ? AV_LOG_WARNING : AV_LOG_INFO,
+            "calculated from options -> crosscheck: %d, start: %d, lines: %d, span_l: %d, span_r: %d, ign_l: %d, ign_r: %d\n",
+            s->crosscheck, s->start, s->lines, s->span_l, s->span_r, s->ign_l, s->ign_r);
+    for (int p = 0; p < s->nb_planes; p++)
+        s->filler[p] = av_malloc(s->planewidth[p] * (depth <= 8 ? sizeof(uint8_t) : sizeof(uint16_t)));
+    s->shifts = av_malloc(s->planeheight[0] * sizeof(*s->shifts));
+    if (s->report >= R_SHIFTS) {
+/*
+        s->shifts_sums = av_malloc((s->lines + 1) * sizeof(*s->shifts_sums));
+        av_log(NULL, AV_LOG_WARNING, "sizeof ptr: %ld, width: %ld\n", sizeof(*s->shifts_sums), (s->lines + 1) * sizeof(*s->shifts_sums));
+        for (int l = s->lines; l >= 0; l--) {
+            s->shifts_sums[l] = av_malloc((s->span_r - s->span_l + 1) * sizeof(**s->shifts_sums));
+            av_log(NULL, AV_LOG_WARNING, "sizeof elem: %ld, line width: %ld\n", sizeof(**s->shifts_sums), (s->span_r - s->span_l + 1) * sizeof(**s->shifts_sums));
+            av_log(NULL, AV_LOG_WARNING, "Line: %2d ->", l);
+            for (int y = s->span_l; y <= s->span_r; y++)
+                av_log(NULL, AV_LOG_WARNING, "%4d>%6d", y, s->shifts_sums[l][-s->span_l + y]);
+            av_log(NULL, AV_LOG_WARNING, "\n");
+            memset(s->shifts_sums[l], 0, (s->span_r - s->span_l + 1) * sizeof(**s->shifts_sums));
+            av_log(NULL, AV_LOG_WARNING, "Line: %2d ->", l);
+            for (int y = s->span_l; y <= s->span_r; y++)
+                av_log(NULL, AV_LOG_WARNING, "%4d>%6d", y, s->shifts_sums[l][-s->span_l + y]);
+            av_log(NULL, AV_LOG_WARNING, "\n");
+            bzero(s->shifts_sums[l], (s->span_r - s->span_l + 1) * sizeof(**s->shifts_sums));
+            av_log(NULL, AV_LOG_WARNING, "Line: %2d ->", l);
+            for (int y = s->span_l; y <= s->span_r; y++)
+                av_log(NULL, AV_LOG_WARNING, "%4d>%6d", y, s->shifts_sums[l][-s->span_l + y]);
+            av_log(NULL, AV_LOG_WARNING, "\n");
+        }
+*/
+        s->shifts_sums = av_malloc(s->lines + 1 * sizeof(**s->shifts_sums));
+/*
+        s->shifts_sums = av_calloc(s->lines + 1, sizeof(**s->shifts_sums));
+*/
+        for (int l = s->lines; l >= 0; l--) {
+            s->shifts_sums[l] = av_calloc(s->span_r - s->span_l + 1, sizeof(**s->shifts_sums));
+/*
+            memset(s->shifts_sums[l], 0, (s->span_r - s->span_l + 1) * sizeof(**s->shifts_sums));
+*/
+        }
+    }
+
+    s->recoverlineshifts = depth <= 8 ? recoverlineshifts8  : recoverlineshifts16;
+
+    /*
+    if (inlink->w < s->left + s->right ||
+            inlink->w <= s->left ||
+            inlink->w <= s->right ||
+            inlink->h < s->top + s->bottom ||
+            inlink->h <= s->top ||
+            inlink->h <= s->bottom ||
+            inlink->w < s->left * 2 ||
+            inlink->w < s->right * 2 ||
+            inlink->h < s->top * 2 ||
+            inlink->h < s->bottom * 2) {
+        av_log(ctx, AV_LOG_ERROR, "Borders are bigger than input frame size.\n");
+        return AVERROR(EINVAL);
+    }
+
+    s->nb_planes = desc->nb_components;
+
+    s->planewidth [0] = s->planewidth [3] = inlink->w;
+    s->planeheight[0] = s->planeheight[3] = inlink->h;
+    s->borders[0].left   = s->borders[3].left   = s->left;
+    s->borders[0].right  = s->borders[3].right  = s->right;
+    s->borders[0].top    = s->borders[3].top    = s->top;
+    s->borders[0].bottom = s->borders[3].bottom = s->bottom;
+
+    s->planewidth [1] = s->planewidth [2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w);
+    s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
+    s->borders[1].left   = s->borders[2].left   = s->left >> desc->log2_chroma_w;
+    s->borders[1].right  = s->borders[2].right  = s->right >> desc->log2_chroma_w;
+    s->borders[1].top    = s->borders[2].top    = s->top >> desc->log2_chroma_h;
+    s->borders[1].bottom = s->borders[2].bottom = s->bottom >> desc->log2_chroma_h;
+
+    switch (s->report) {
+        case FM_FAILS: s->recoverlineshiftes = depth <= 8 ? report_shifts8  : report_shifts16;  break;
+        case FM_SHIFTS:  s->recoverlineshiftes = depth <= 8 ? smear_borders8  : smear_borders16;  break;
+        case FM_VERBOSE: s->recoverlineshiftes = depth <= 8 ? mirror_borders8 : mirror_borders16; break;
+        case FM_FIXED:  s->recoverlineshiftes = depth <= 8 ? fixed_borders8  : fixed_borders16;  {
+            uint8_t filler[4];
+            if (desc->flags & AV_PIX_FMT_FLAG_RGB) {
+                uint8_t rgba_map[4];
+                ff_fill_rgba_map(rgba_map, inlink->format);
+                for (int i = 0; i < sizeof(rgba_map); i++)
+                    filler[rgba_map[i]] = s->rgba_color[i];
+            } else {
+                enum { Y, U, V, A };
+                enum { R, G, B };
+                filler[Y] = RGB_TO_Y_CCIR(s->rgba_color[R], s->rgba_color[G], s->rgba_color[B]);
+                filler[U] = RGB_TO_U_CCIR(s->rgba_color[R], s->rgba_color[G], s->rgba_color[B], 0);
+                filler[V] = RGB_TO_V_CCIR(s->rgba_color[R], s->rgba_color[G], s->rgba_color[B], 0);
+                filler[A] = s->rgba_color[A];
+            }
+            for (int p = 0; p < s->nb_planes; p++) {
+                int fill_sz = FFMAX3(s->borders[p].left, s->borders[p].right,
+                        s->top + s->bottom > 0 ? s->planewidth [p] : 0);
+                s->filler[p] = av_malloc(fill_sz * (depth <= 8 ? sizeof(uint8_t) : sizeof(uint16_t)));
+                if (depth <= 8)
+                    memset((uint8_t *)s->filler[p], filler[p], fill_sz);
+                else
+                    for (int i = fill_sz; i-- > 0; )
+                        ((uint16_t *)s->filler[p])[i] = filler[p] << (depth - 8);
+            }
+        } break;
+    }
+*/
+
+    return 0;
+}
+
+static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
+{
+    AVFilterContext *ctx = inlink->dst;
+    LineShiftContext *s = ctx->priv;
+
+    s->recoverlineshifts(inlink, frame);
+
+    return ff_filter_frame(inlink->dst->outputs[0], frame);
+}
+
+static int query_formats(AVFilterContext *ctx)
+{
+    static const enum AVPixelFormat pix_fmts[] = {
+        AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV440P,
+        AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P,
+        AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P,
+        AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ420P,
+        AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV410P,
+        AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9,
+        AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10,
+        AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV440P12,
+        AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14,
+        AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16,
+        AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9,
+        AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
+        AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16,
+        AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10,
+        AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16,
+        AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
+        AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16,
+        AV_PIX_FMT_NONE
+    };
+    AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
+    if (!fmts_list)
+        return AVERROR(ENOMEM);
+    return ff_set_common_formats(ctx, fmts_list);
+}
+
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    LineShiftContext *s = ctx->priv;
+    av_log(ctx, s->report >= R_SHIFTS ? AV_LOG_WARNING : AV_LOG_INFO, "start freeing memory ...\n");
+    for (int p = 0; p < s->nb_planes; p++)
+        av_freep(&s->filler[p]);
+    av_freep(&s->shifts);
+/*
+    printf("printf(): filler and shifts freed!\n");
+*/
+    av_log(ctx, s->report >= R_SHIFTS ? AV_LOG_WARNING : AV_LOG_INFO, "filler and shifts freed!\n");
+    if (s->report >= R_SHIFTS) {
+/*
+        printf("do printf() before statistic of found shifts!\n");
+        av_log(ctx, AV_LOG_WARNING, "statistic of found shifts ...\n");
+        av_log(NULL, AV_LOG_WARNING, "shift:    "); // * increment);
+        for (int shift = 0; shift <= s->span_r -  s->span_l; shift++)
+            av_log(NULL, AV_LOG_WARNING, "%6d", s->span_l + shift);
+        for (int l = 0; l <= s->lines; l++) {
+            av_log(NULL, AV_LOG_WARNING, "\nline %4d:", s->start + l);
+            for (int shift = 0; shift <= s->span_r -  s->span_l; shift++)
+                av_log(NULL, AV_LOG_WARNING, "%6d", s->shifts_sums[l][shift]);
+            av_log(NULL, AV_LOG_WARNING, "\n");
+        }
+*/
+/*
+        printf("printf(): now start freeing shifts_sums ...\n");
+*/
+        av_log(ctx, AV_LOG_WARNING, "now start freeing shifts_sums ...\n");
+        for (int l = 0; l <= s->lines; l++) {
+            av_log(ctx, AV_LOG_WARNING, "freeing line %d ...\n", l);
+            if (s->shifts_sums[l])
+                av_freep(&s->shifts_sums[l]);
+        }
+        av_log(ctx, AV_LOG_WARNING, "lines of shifts_sums freed!\n");
+        av_freep(&s->shifts_sums);
+/*
+        printf("printf(): shifts_sums freed!\n");
+*/
+        av_log(ctx, AV_LOG_WARNING, "shifts_sums freed!\n");
+    }
+}
+
+static const AVFilterPad lineshiftrecover_inputs[] = {
+    {
+        .name           = "default",
+        .type           = AVMEDIA_TYPE_VIDEO,
+        .config_props   = config_props,
+        .filter_frame   = filter_frame,
+        .needs_writable = 1,
+    },
+    { NULL }
+};
+
+static const AVFilterPad lineshiftrecover_outputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_VIDEO,
+    },
+    { NULL }
+};
+
+AVFilter ff_vf_lineshiftrecover = {
+    .name          = "lineshiftrecover",
+    .description   = NULL_IF_CONFIG_SMALL("Recover video tape glitches."),
+    .priv_size     = sizeof(LineShiftContext),
+    .priv_class    = &lineshiftrecover_class,
+    .init          = init,
+    .uninit        = uninit,
+    .query_formats = query_formats,
+    .inputs        = lineshiftrecover_inputs,
+    .outputs       = lineshiftrecover_outputs,
+    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC,
+};
diff --git a/libavutil/timestamp.h b/libavutil/timestamp.h
index e082f01..97dc0a1 100644
--- a/libavutil/timestamp.h
+++ b/libavutil/timestamp.h
@@ -33,6 +33,17 @@
 #define AV_TS_MAX_STRING_SIZE 32

 /**
+ * Convert a time base scaled timestamp to micro seconds.
+ *
+ * @param ts the timestamp to convert, must be less than 2**63/1,000,000/tb->num
+ * @param tb the timebase of the timestamp
+ * @return the  timestamp in micro seconds
+ */
+static inline int64_t av_ts2us(int64_t ts, AVRational *tb) {
+    return ts * 1000000 * tb->num / tb->den;
+}
+
+/**
  * Fill the provided buffer with a string containing a timestamp
  * representation.
  *
@@ -55,7 +66,7 @@

 /**
  * Fill the provided buffer with a string containing a timestamp time
- * representation.
+ * representation in seconds.
  *
  * @param buf a buffer with size in bytes of at least AV_TS_MAX_STRING_SIZE
  * @param ts the timestamp to represent
@@ -75,4 +86,80 @@
  */
 #define av_ts2timestr(ts, tb) av_ts_make_time_string((char[AV_TS_MAX_STRING_SIZE]){0}, ts, tb)

+/**
+ * Fill the provided buffer with a string containing a timestamp time
+ * representation in minutes and seconds.
+ *
+ * @param buf a buffer with size in bytes of at least AV_TS_MAX_STRING_SIZE
+ * @param ts the timestamp to represent, must be less than 2**63/1,000,000/tb->num
+ * @param tb the timebase of the timestamp
+ * @return the buffer in input
+ */
+static char *av_ts_make_minute_string(char *buf, int64_t ts, AVRational *tb)
+{
+    if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
+    else {
+        int64_t us = av_ts2us(ts, tb);
+        int len = snprintf(buf, AV_TS_MAX_STRING_SIZE, "%3ld:%02d.%06d",
+                us / 60000000, (int)(us / 1000000 % 60), (int)(us % 1000000));
+//        int len;
+//        double time = av_q2d(*tb) * ts;
+//        const char *format = (time >= 0 ? "%3d:%09.6f" : "-%3d:%09.6f");
+//        time = (fabs(time) > INT_MAX * 60.0 ? INT_MAX * 60.0 : fabs(time));
+//        len = snprintf(buf, AV_TS_MAX_STRING_SIZE, format, (int)(time / 60), fmod(time, 60));
+//        if (len - 9 >= 0 && buf[len - 9] > '5') // correct rare rounding issue
+//            len = snprintf(buf, AV_TS_MAX_STRING_SIZE, format, (int)(time / 60) + 1, .0);
+        while (buf[--len] == '0'); // search trailing zeros or ...
+        buf[len + (buf[len] != '.')] = '\0'; // dot and strip them
+    }
+    return buf;
+}
+
+/**
+ * Convenience macro. The return value should be used only directly in
+ * function arguments but never stand-alone.
+ */
+#define av_ts2minutestr(ts, tb) av_ts_make_minute_string((char[AV_TS_MAX_STRING_SIZE]){'\0'}, ts, tb)
+
+/**
+ * Fill the provided buffer with a string containing a timestamp time
+ * representation in hours, minutes and seconds.
+ *
+ * @param buf a buffer with size in bytes of at least AV_TS_MAX_STRING_SIZE
+ * @param ts the timestamp to represent, must be less than 2**63/1,000,000/tb->num
+ * @param tb the timebase of the timestamp
+ * @return the buffer in input
+ */
+static char *av_ts_make_hour_string(char *buf, int64_t ts, AVRational *tb)
+{
+    if (ts == AV_NOPTS_VALUE) snprintf(buf, AV_TS_MAX_STRING_SIZE, "NOPTS");
+    else {
+        int64_t us = av_ts2us(ts, tb);
+        int len = snprintf(buf, AV_TS_MAX_STRING_SIZE, "%ld:%02d:%02d.%06d",
+                us / 3600000000L, (int)(us / 60000000 % 60), (int)(us / 1000000 % 60), (int)(us % 1000000));
+//        int len, hours, minutes;
+//        double time = av_q2d(*tb) * ts;
+//        const char *format = (time >= 0 ? "%d:%02d:%09.6f" : "-%d:%02d:%09.6f");
+//        time = (fabs(time) > INT_MAX * 60.0 * 60.0 ? INT_MAX * 60.0 * 60.0 : fabs(time));
+//        hours = time / 60 / 60; minutes = fmod(time / 60, 60);
+//        len = snprintf(buf, AV_TS_MAX_STRING_SIZE, format, hours, minutes, fmod(time, 60));
+//        if (len - 9 >= 0 && buf[len - 9] > '5') { // correct rare rounding issue
+//            if (++minutes > 59) {
+//                minutes = 0;
+//                hours++;
+//            }
+//            len = snprintf(buf, AV_TS_MAX_STRING_SIZE, format, hours, minutes, .0);
+//        }
+        while (buf[--len] == '0'); // search trailing zeros or ...
+        buf[len + (buf[len] != '.')] = '\0'; // dot and strip them
+    }
+    return buf;
+}
+
+/**
+ * Convenience macro. The return value should be used only directly in
+ * function arguments but never stand-alone.
+ */
+#define av_ts2hourstr(ts, tb) av_ts_make_hour_string((char[AV_TS_MAX_STRING_SIZE]){'\0'}, ts, tb)
+
 #endif /* AVUTIL_TIMESTAMP_H */
_______________________________________________
ffmpeg-user mailing list
[email protected]
https://ffmpeg.org/mailman/listinfo/ffmpeg-user

To unsubscribe, visit link above, or email
[email protected] with subject "unsubscribe".

Reply via email to