diff mbox series

[FFmpeg-devel] avformat/img2dec: Option to play sequence backwards

Message ID 20220212115709.45936-1-sergio.acereda@gmail.com
State New
Headers show
Series [FFmpeg-devel] avformat/img2dec: Option to play sequence backwards | expand

Checks

Context Check Description
yinshiyou/make_loongarch64 success Make finished
yinshiyou/make_fate_loongarch64 success Make fate finished
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished
andriy/make_aarch64_jetson success Make finished
andriy/make_fate_aarch64_jetson success Make fate finished
andriy/make_armv7_RPi4 success Make finished
andriy/make_fate_armv7_RPi4 success Make fate finished
andriy/make_ppc success Make finished
andriy/make_fate_ppc success Make fate finished

Commit Message

Sergio Acereda Feb. 12, 2022, 11:57 a.m. UTC
This patch should allow playing an image sequence in backwards direction, without needing to apply a reverse filter.

ffmpeg -i sequence%05d.png forward.mkv
ffmpeg -reverse 1 -i sequence%05d.png backward.mkv

Signed-off-by: Sergio Acereda <sergio.acereda@gmail.com>
---
 libavformat/img2.h    |  1 +
 libavformat/img2dec.c | 40 +++++++++++++++++++++++++++-------------
 2 files changed, 28 insertions(+), 13 deletions(-)

Comments

Michael Niedermayer Feb. 12, 2022, 7:56 p.m. UTC | #1
On Sat, Feb 12, 2022 at 12:57:11PM +0100, Sergio Acereda wrote:
> This patch should allow playing an image sequence in backwards direction, without needing to apply a reverse filter.
> 
> ffmpeg -i sequence%05d.png forward.mkv
> ffmpeg -reverse 1 -i sequence%05d.png backward.mkv
> 
> Signed-off-by: Sergio Acereda <sergio.acereda@gmail.com>
> ---
>  libavformat/img2.h    |  1 +
>  libavformat/img2dec.c | 40 +++++++++++++++++++++++++++-------------
>  2 files changed, 28 insertions(+), 13 deletions(-)

seems to break/change  -loop

ffmpeg -f image2 -loop 1 -framerate 2 -i tickets/3329/arrow_%1d.png -frames 1000 -vf fps=fps=25 -omit_video_pes_length 0 file-3329.ts

the output filesize changes by more than 10x

thx

[...]
Sergio Acereda Feb. 13, 2022, 11:36 a.m. UTC | #2
> seems to break/change  -loop
> 
> ffmpeg -f image2 -loop 1 -framerate 2 -i tickets/3329/arrow_%1d.png -frames 1000 -vf fps=fps=25 -omit_video_pes_length 0 file-3329.ts
> 
> the output filesize changes by more than 10x
> 
> thx

Ok, I'll look into that later.

Once I have a new patch, how should I proceed? Should I amend my previous commit and send a new patch with [FFmpeg-devel,v2] prefix?

Thanks,

> On 12 Feb 2022, at 12:57, Sergio Acereda <sergio.acereda@gmail.com> wrote:
Michael Niedermayer Feb. 13, 2022, 11:53 a.m. UTC | #3
On Sun, Feb 13, 2022 at 12:36:14PM +0100, Sergio Acereda wrote:
> > seems to break/change  -loop
> > 
> > ffmpeg -f image2 -loop 1 -framerate 2 -i tickets/3329/arrow_%1d.png -frames 1000 -vf fps=fps=25 -omit_video_pes_length 0 file-3329.ts
> > 
> > the output filesize changes by more than 10x
> > 
> > thx
> 
> Ok, I'll look into that later.
> 
> Once I have a new patch, how should I proceed? Should I amend my previous commit and send a new patch with [FFmpeg-devel,v2] prefix?

whatever was the last version +1

thx

[...]
diff mbox series

Patch

diff --git a/libavformat/img2.h b/libavformat/img2.h
index 5fd8ff77fc..3d01c00537 100644
--- a/libavformat/img2.h
+++ b/libavformat/img2.h
@@ -59,6 +59,7 @@  typedef struct VideoDemuxData {
 #endif
     int start_number;
     int start_number_range;
+    int reverse;
     int frame_size;
     int ts_from_file;
     int export_path_metadata; /**< enabled when set to 1. */
diff --git a/libavformat/img2dec.c b/libavformat/img2dec.c
index 8608252d83..f7ad85be08 100644
--- a/libavformat/img2dec.c
+++ b/libavformat/img2dec.c
@@ -105,10 +105,12 @@  static int is_glob(const char *path)
  * @param plast_index  pointer to index updated with the last number in the range
  * @param path         path which has to be matched by the image files in the range
  * @param start_index  minimum accepted value for the first index in the range
+ * @param start_index_range range for looking at the first sequence number
+ * @param reverse      play backwards
  * @return -1 if no image file could be found
  */
 static int find_image_range(AVIOContext *pb, int *pfirst_index, int *plast_index,
-                            const char *path, int start_index, int start_index_range)
+                            const char *path, int start_index, int start_index_range, int reverse)
 {
     char buf[1024];
     int range, last_index, range1, first_index;
@@ -152,8 +154,13 @@  static int find_image_range(AVIOContext *pb, int *pfirst_index, int *plast_index
             break;
         last_index += range;
     }
-    *pfirst_index = first_index;
-    *plast_index  = last_index;
+    if (reverse) {
+        *pfirst_index = last_index;
+        *plast_index  = first_index;
+    } else {
+        *pfirst_index = first_index;
+        *plast_index  = last_index;
+    }
     return 0;
 
 fail:
@@ -274,7 +281,7 @@  int ff_img_read_header(AVFormatContext *s1)
         }
         if ((s->pattern_type == PT_GLOB_SEQUENCE && !s->use_glob) || s->pattern_type == PT_SEQUENCE) {
             if (find_image_range(s1->pb, &first_index, &last_index, s->path,
-                                 s->start_number, s->start_number_range) < 0) {
+                                 s->start_number, s->start_number_range, s->reverse) < 0) {
                 av_log(s1, AV_LOG_ERROR,
                        "Could find no file with path '%s' and index in the range %d-%d\n",
                        s->path, s->start_number, s->start_number + s->start_number_range - 1);
@@ -307,7 +314,7 @@  int ff_img_read_header(AVFormatContext *s1)
         /* compute duration */
         if (!s->ts_from_file) {
             st->start_time = 0;
-            st->duration   = last_index - first_index + 1;
+            st->duration   = abs(last_index - first_index) + 1;
         }
     }
 
@@ -413,11 +420,12 @@  int ff_img_read_packet(AVFormatContext *s1, AVPacket *pkt)
     AVCodecParameters *par = s1->streams[0]->codecpar;
 
     if (!s->is_pipe) {
+        int bad = s->reverse? (s->img_number < s->img_last) : (s->img_number > s->img_last);
         /* loop over input */
-        if (s->loop && s->img_number > s->img_last) {
+        if (s->loop && bad) {
             s->img_number = s->img_first;
         }
-        if (s->img_number > s->img_last)
+        if (bad)
             return AVERROR_EOF;
         if (s->pattern_type == PT_NONE) {
             av_strlcpy(filename_bytes, s->path, sizeof(filename_bytes));
@@ -554,8 +562,9 @@  int ff_img_read_packet(AVFormatContext *s1, AVPacket *pkt)
         }
         goto fail;
     } else {
+        int step = s->reverse? -1 : 1;
         s->img_count++;
-        s->img_number++;
+        s->img_number += step;
         s->pts++;
         return 0;
     }
@@ -585,6 +594,7 @@  static int img_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp
 {
     VideoDemuxData *s1 = s->priv_data;
     AVStream *st = s->streams[0];
+    int bad = s1->reverse? (timestamp  < s1->img_last) : (timestamp > s1->img_last);
 
     if (s1->ts_from_file) {
         int index = av_index_search_timestamp(st, timestamp, flags);
@@ -592,12 +602,15 @@  static int img_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp
             return -1;
         s1->img_number = ffstream(st)->index_entries[index].pos;
         return 0;
-    }
-
-    if (timestamp < 0 || !s1->loop && timestamp > s1->img_last - s1->img_first)
+    } else if (timestamp < 0 || !s1->loop && bad)
         return -1;
-    s1->img_number = timestamp%(s1->img_last - s1->img_first + 1) + s1->img_first;
-    s1->pts = timestamp;
+    else {
+        int dir = s1->reverse? -1 : 1;
+        int span = 1 + dir * (s1->img_last - s1->img_first);
+        int rel = timestamp % span;
+        s1->img_number = s1->img_first + dir * rel;
+        s1->pts = timestamp;
+    }
     return 0;
 }
 
@@ -619,6 +632,7 @@  const AVOption ff_img_options[] = {
     { "none",         "disable pattern matching",            0, AV_OPT_TYPE_CONST,  {.i64=PT_NONE         }, INT_MIN, INT_MAX, DEC, "pattern_type" },
     { "start_number", "set first number in the sequence",    OFFSET(start_number), AV_OPT_TYPE_INT,    {.i64 = 0   }, INT_MIN, INT_MAX, DEC },
     { "start_number_range", "set range for looking at the first sequence number", OFFSET(start_number_range), AV_OPT_TYPE_INT, {.i64 = 5}, 1, INT_MAX, DEC },
+    { "reverse",      "reverse direction",                   OFFSET(reverse),      AV_OPT_TYPE_INT,    {.i64 = 0   }, 0, 1, DEC },
     { "ts_from_file", "set frame timestamp from file's one", OFFSET(ts_from_file), AV_OPT_TYPE_INT,    {.i64 = 0   }, 0, 2,       DEC, "ts_type" },
     { "none", "none",                   0, AV_OPT_TYPE_CONST,    {.i64 = 0   }, 0, 2,       DEC, "ts_type" },
     { "sec",  "second precision",       0, AV_OPT_TYPE_CONST,    {.i64 = 1   }, 0, 2,       DEC, "ts_type" },