[FFmpeg-devel] [patch] glob matching for image series

Nicolas George nicolas.george at normalesup.org
Mon Jan 9 13:00:23 CET 2012


Le nonidi 19 nivôse, an CCXX, Brian Olson a écrit :
> To make a singular net diff, here's the output of `git diff master
> mybranch` (also attached if that's easier to parse):

Your mailer mangled the diff. Maybe you should find a more tech-friendly
one. The attachment was fine, but do not gzip it, it makes it harder to
quote it, and the compression is completely lost by the base64 encoding.

> On Jan 8, 2012, at 12:50 PM, Nicolas George wrote:

Please do not top-post.


> diff --git a/configure b/configure
> index 43b63bb..47146d8 100755
> --- a/configure
> +++ b/configure
> @@ -1179,6 +1179,7 @@ HAVE_LIST="
>      GetProcessMemoryInfo
>      GetProcessTimes
>      getrusage
> +    glob
>      gnu_as
>      ibm_asm
>      inet_aton
> @@ -3021,6 +3022,7 @@ check_func_headers windows.h GetProcessAffinityMask
>  check_func_headers windows.h GetProcessTimes
>  check_func_headers windows.h MapViewOfFile
>  check_func_headers windows.h VirtualAlloc
> +check_func_headers glob.h glob
>  
>  check_header dlfcn.h
>  check_header dxva2api.h -D_WIN32_WINNT=0x0600
> diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi
> index 83a45a8..ded3098 100644
> --- a/doc/ffmpeg.texi
> +++ b/doc/ffmpeg.texi
> @@ -1226,7 +1226,11 @@ ffmpeg -f image2 -i foo-%03d.jpeg -r 12 -s WxH foo.avi
>  The syntax @code{foo-%03d.jpeg} specifies to use a decimal number
>  composed of three digits padded with zeroes to express the sequence
>  number. It is the same syntax supported by the C printf function, but
> -only formats accepting a normal integer are suitable.
> +only formats accepting a normal integer are suitable. When importing
> +an image sequence, -i also accepts shell-like wildcard patterns such as
> + at code{foo-*.jpeg}, @code{foo-???.jpeg} or @code{foo-00[234]*.jpeg}.
> +It will probably be necessary to escape these patterns so they do not
> +get interpreted by your shell.
>  
>  @item
>  You can put many streams of the same type in the output:
> diff --git a/libavformat/img2.c b/libavformat/img2.c
> index bc35591..7f94a1f 100644
> --- a/libavformat/img2.c
> +++ b/libavformat/img2.c
> @@ -29,6 +29,9 @@
>  #include "avformat.h"
>  #include "avio_internal.h"
>  #include "internal.h"
> +#if HAVE_GLOB
> +#include <glob.h>
> +#endif
>  
>  typedef struct {
>      const AVClass *class;  /**< Class for private options. */
> @@ -44,6 +47,10 @@ typedef struct {
>      char *framerate;        /**< Set by a private option. */
>      int loop;
>      int updatefirst;
> +#if HAVE_GLOB
> +    int use_glob;
> +    glob_t globstate;
> +#endif
>  } VideoData;
>  
>  typedef struct {
> @@ -133,6 +140,38 @@ static enum CodecID av_str2id(const IdStrMap *tags, const char *str)
>      return CODEC_ID_NONE;
>  }
>  
> +#if HAVE_GLOB
> +static int glob_errfunc(const char* epath, int glob_error) {

Please use "char *name", not "char* name". And the brace on the next line.

> +	return 0;
> +}
> +static int is_glob(const char* path) {
> +    while (*path != '\0') {
> +        switch (*path) {
> +        case '*':
> +        case '?':
> +        case '[':
> +        case ']':
> +        case '{':
> +        case '}':
> +        case '\\':
> +            return 1;
> +        }
> +        path++;
> +    }

I believe strcspn could trivialize that.

> +    return 0;
> +}
> +static int use_glob(VideoData *s) {
> +    return s->use_glob;
> +}
> +#else
> +static int is_glob(const char* path) {
> +    return 0;
> +}
> +static int use_glob(VideoData *s) {
> +    return 0;
> +}
> +#endif

I believe it would be slightly simpler if the use_glob field were
unconditional and accessed directly.

> +
>  /* return -1 if no image found */
>  static int find_image_range(int *pfirst_index, int *plast_index,
>                              const char *path)
> @@ -192,6 +231,8 @@ static int read_probe(AVProbeData *p)
>      if (p->filename && av_str2id(img_tags, p->filename)) {
>          if (av_filename_number_test(p->filename))
>              return AVPROBE_SCORE_MAX;
> +        else if (is_glob(p->filename))
> +            return AVPROBE_SCORE_MAX;
>          else
>              return AVPROBE_SCORE_MAX/2;
>      }
> @@ -263,10 +304,42 @@ static int read_header(AVFormatContext *s1, AVFormatParameters *ap)
>      }
>  
>      if (!s->is_pipe) {
> -        if (find_image_range(&first_index, &last_index, s->path) < 0)
> -            return AVERROR(ENOENT);
> -        s->img_first = first_index;
> -        s->img_last = last_index;
> +        if (is_glob(s->path)) {
> +#if HAVE_GLOB
> +            int gerr;
> +            s->use_glob = 1;
> +/* Locally define as 0 (bitwise-OR no-op) any missing glob options that
> +   are non-posix glibc/bsd extensions. */
> +#ifndef GLOB_NOMAGIC
> +#define GLOB_NOMAGIC 0
> +#endif
> +#ifndef GLOB_TILDE
> +#define GLOB_TILDE 0
> +#endif
> +#ifndef GLOB_TILDE_CHECK
> +#define GLOB_TILDE_CHECK GLOB_TILDE
> +#endif
> +#ifndef GLOB_BRACE
> +#define GLOB_BRACE 0
> +#endif

This bunch of defines breaks the reading order: maybe put them somewhere
else, at the top with the rest of the ifdefry.

> +            gerr = glob(
> +			    s->path,
> +				GLOB_NOCHECK|GLOB_BRACE|GLOB_NOMAGIC|GLOB_TILDE_CHECK,
> +				glob_errfunc,
> +				&s->globstate);

The indentation is strange, and there are tabs.

> +            if (gerr != 0) {

if (gerr)

> +                return AVERROR(ENOENT);

Please match gerr against the GLOB_* error codes.

> +            }
> +            s->img_first = 0;
> +            s->img_last = s->globstate.gl_pathc - 1;
> +#endif
> +        } else {
> +            if (find_image_range(&first_index, &last_index, s->path) < 0) {
> +                return AVERROR(ENOENT);
> +            }
> +            s->img_first = first_index;
> +            s->img_last = last_index;

Do not reindent this part, it will make it easier to see what you actually
change.

> +        }
>          s->img_number = first_index;

Unless I am mistake, first_index can be unitialized.

>          /* compute duration */
>          st->start_time = 0;
> @@ -296,7 +369,8 @@ static int read_header(AVFormatContext *s1, AVFormatParameters *ap)
>  static int read_packet(AVFormatContext *s1, AVPacket *pkt)
>  {
>      VideoData *s = s1->priv_data;
> -    char filename[1024];
> +    char filename_bytes[1024];

filename_buf seems more natural.

> +    char* filename = filename_bytes;
>      int i;
>      int size[3]={0}, ret[3]={0};
>      AVIOContext *f[3];
> @@ -309,9 +383,17 @@ static int read_packet(AVFormatContext *s1, AVPacket *pkt)
>          }
>          if (s->img_number > s->img_last)
>              return AVERROR_EOF;
> -        if (av_get_frame_filename(filename, sizeof(filename),
> -                                  s->path, s->img_number)<0 && s->img_number > 1)
> -            return AVERROR(EIO);
> +        if (use_glob(s)) {
> +#if HAVE_GLOB
> +            filename = s->globstate.gl_pathv[s->img_number];
> +#endif
> +        } else {
> +            if (av_get_frame_filename(
> +                    filename_bytes, sizeof(filename_bytes),
> +                    s->path, s->img_number)<0 &&
> +                s->img_number > 1)
> +                return AVERROR(EIO);
> +        }

Again, do not reindent existing code: leave it for a later patch.

>          for(i=0; i<3; i++){
>              if (avio_open2(&f[i], filename, AVIO_FLAG_READ,
>                             &s1->interrupt_callback, NULL) < 0) {
> @@ -361,6 +443,16 @@ static int read_packet(AVFormatContext *s1, AVPacket *pkt)
>      }
>  }
>  
> +static int read_close(struct AVFormatContext* s1) {
> +    VideoData *s = s1->priv_data;
> +#if HAVE_GLOB
> +	if (s->use_glob) {
> +		globfree(&s->globstate);
> +	}

You can omit the braces in such simple cases.

> +#endif
> +    return 0;
> +}
> +
>  #if CONFIG_IMAGE2_MUXER || CONFIG_IMAGE2PIPE_MUXER
>  /******************************************************/
>  /* image output */
> @@ -494,6 +586,7 @@ AVInputFormat ff_image2_demuxer = {
>      .read_probe     = read_probe,
>      .read_header    = read_header,
>      .read_packet    = read_packet,
> +    .read_close     = read_close,
>      .flags          = AVFMT_NOFILE,
>      .priv_class     = &img2_class,
>  };

On the whole, it looks promising.

Regards,

-- 
  Nicolas George


More information about the ffmpeg-devel mailing list