Merge commit '124134e42455763b28cc346fed1d07017a76e84e'
[ffmpeg.git] / libavformat / img2dec.c
index 18fa5d5..23e2a1e 100644 (file)
@@ -3,20 +3,20 @@
  * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
  * Copyright (c) 2004 Michael Niedermayer
  *
- * This file is part of Libav.
+ * This file is part of FFmpeg.
  *
- * Libav is free software; you can redistribute it and/or
+ * 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.
  *
- * Libav is distributed in the hope that it will be useful,
+ * 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 Libav; if not, write to the Free Software
+ * 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/parseutils.h"
 #include "avformat.h"
 #include "internal.h"
+#if HAVE_GLOB
+#include <glob.h>
+
+/* 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_BRACE
+#define GLOB_BRACE 0
+#endif
+
+#endif /* HAVE_GLOB */
 
 typedef struct {
     const AVClass *class;  /**< Class for private options. */
@@ -35,12 +48,19 @@ typedef struct {
     int img_number;
     int img_count;
     int is_pipe;
+    int split_planes;       /**< use independent file for each Y, U, V plane */
     char path[1024];
     char *pixel_format;     /**< Set by a private option. */
     char *video_size;       /**< Set by a private option. */
     char *framerate;        /**< Set by a private option. */
     int loop;
+    enum { PT_GLOB_SEQUENCE, PT_GLOB, PT_SEQUENCE } pattern_type;
+    int use_glob;
+#if HAVE_GLOB
+    glob_t globstate;
+#endif
     int start_number;
+    int start_number_range;
 } VideoDemuxData;
 
 static const int sizes[][2] = {
@@ -69,15 +89,44 @@ static int infer_size(int *width_ptr, int *height_ptr, int size)
     return -1;
 }
 
-/* return -1 if no image found */
+static int is_glob(const char *path)
+{
+#if HAVE_GLOB
+    size_t span = 0;
+    const char *p = path;
+
+    while (p = strchr(p, '%')) {
+        if (*(++p) == '%') {
+            ++p;
+            continue;
+        }
+        if (span = strspn(p, "*?[]{}"))
+            break;
+    }
+    /* Did we hit a glob char or get to the end? */
+    return span != 0;
+#else
+    return 0;
+#endif
+}
+
+/**
+ * Get index range of image files matched by path.
+ *
+ * @param pfirst_index pointer to index updated with the first number in the range
+ * @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
+ * @return -1 if no image file could be found
+ */
 static int find_image_range(int *pfirst_index, int *plast_index,
-                            const char *path, int max_start)
+                            const char *path, int start_index, int start_index_range)
 {
     char buf[1024];
     int range, last_index, range1, first_index;
 
     /* find the first image */
-    for (first_index = 0; first_index < max_start; first_index++) {
+    for (first_index = start_index; first_index < start_index + start_index_range; first_index++) {
         if (av_get_frame_filename(buf, sizeof(buf), path, first_index) < 0){
             *pfirst_index =
             *plast_index = 1;
@@ -88,7 +137,7 @@ static int find_image_range(int *pfirst_index, int *plast_index,
         if (avio_check(buf, AVIO_FLAG_READ) > 0)
             break;
     }
-    if (first_index == 5)
+    if (first_index == start_index + start_index_range)
         goto fail;
 
     /* find the last image */
@@ -128,6 +177,8 @@ static int read_probe(AVProbeData *p)
     if (p->filename && ff_guess_image2_codec(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;
     }
@@ -183,9 +234,67 @@ static int read_header(AVFormatContext *s1)
     }
 
     if (!s->is_pipe) {
-        if (find_image_range(&first_index, &last_index, s->path,
-                             FFMAX(s->start_number, 5)) < 0)
-            return AVERROR(ENOENT);
+        if (s->pattern_type == PT_GLOB_SEQUENCE) {
+        s->use_glob = is_glob(s->path);
+        if (s->use_glob) {
+            char *p = s->path, *q, *dup;
+            int gerr;
+
+            av_log(s1, AV_LOG_WARNING, "Pattern type 'glob_sequence' is deprecated: "
+                   "use pattern_type 'glob' instead\n");
+#if HAVE_GLOB
+            dup = q = av_strdup(p);
+            while (*q) {
+                /* Do we have room for the next char and a \ insertion? */
+                if ((p - s->path) >= (sizeof(s->path) - 2))
+                  break;
+                if (*q == '%' && strspn(q + 1, "%*?[]{}"))
+                    ++q;
+                else if (strspn(q, "\\*?[]{}"))
+                    *p++ = '\\';
+                *p++ = *q++;
+            }
+            *p = 0;
+            av_free(dup);
+
+            gerr = glob(s->path, GLOB_NOCHECK|GLOB_BRACE|GLOB_NOMAGIC, NULL, &s->globstate);
+            if (gerr != 0) {
+                return AVERROR(ENOENT);
+            }
+            first_index = 0;
+            last_index = s->globstate.gl_pathc - 1;
+#endif
+        }
+        }
+        if ((s->pattern_type == PT_GLOB_SEQUENCE && !s->use_glob) || s->pattern_type == PT_SEQUENCE) {
+            if (find_image_range(&first_index, &last_index, s->path,
+                                 s->start_number, s->start_number_range) < 0) {
+                av_log(s1, AV_LOG_ERROR,
+                       "Could find no file with with path '%s' and index in the range %d-%d\n",
+                       s->path, s->start_number, s->start_number + s->start_number_range - 1);
+                return AVERROR(ENOENT);
+            }
+        } else if (s->pattern_type == PT_GLOB) {
+#if HAVE_GLOB
+            int gerr;
+            gerr = glob(s->path, GLOB_NOCHECK|GLOB_BRACE|GLOB_NOMAGIC, NULL, &s->globstate);
+            if (gerr != 0) {
+                return AVERROR(ENOENT);
+            }
+            first_index = 0;
+            last_index = s->globstate.gl_pathc - 1;
+            s->use_glob = 1;
+#else
+            av_log(s1, AV_LOG_ERROR,
+                   "Pattern type 'glob' was selected but globbing "
+                   "is not supported by this libavformat build\n");
+            return AVERROR(ENOSYS);
+#endif
+        } else if (s->pattern_type != PT_GLOB_SEQUENCE) {
+            av_log(s1, AV_LOG_ERROR,
+                   "Unknown value '%d' for pattern_type option\n", s->pattern_type);
+            return AVERROR(EINVAL);
+        }
         s->img_first = first_index;
         s->img_last = last_index;
         s->img_number = first_index;
@@ -201,8 +310,12 @@ static int read_header(AVFormatContext *s1)
         st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
         st->codec->codec_id = s1->audio_codec_id;
     }else{
+        const char *str= strrchr(s->path, '.');
+        s->split_planes = str && !av_strcasecmp(str + 1, "y");
         st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
         st->codec->codec_id = ff_guess_image2_codec(s->path);
+        if (st->codec->codec_id == AV_CODEC_ID_LJPEG)
+            st->codec->codec_id = AV_CODEC_ID_MJPEG;
     }
     if(st->codec->codec_type == AVMEDIA_TYPE_VIDEO && pix_fmt != PIX_FMT_NONE)
         st->codec->pix_fmt = pix_fmt;
@@ -213,7 +326,8 @@ static int read_header(AVFormatContext *s1)
 static int read_packet(AVFormatContext *s1, AVPacket *pkt)
 {
     VideoDemuxData *s = s1->priv_data;
-    char filename[1024];
+    char filename_bytes[1024];
+    char *filename = filename_bytes;
     int i;
     int size[3]={0}, ret[3]={0};
     AVIOContext *f[3];
@@ -226,9 +340,15 @@ 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),
+        if (s->use_glob) {
+#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);
+        }
         for(i=0; i<3; i++){
             if (avio_open2(&f[i], filename, AVIO_FLAG_READ,
                            &s1->interrupt_callback, NULL) < 0) {
@@ -239,7 +359,7 @@ static int read_packet(AVFormatContext *s1, AVPacket *pkt)
             }
             size[i]= avio_size(f[i]);
 
-            if(codec->codec_id != AV_CODEC_ID_RAWVIDEO)
+            if(!s->split_planes)
                 break;
             filename[ strlen(filename) - 1 ]= 'U' + i;
         }
@@ -248,7 +368,7 @@ static int read_packet(AVFormatContext *s1, AVPacket *pkt)
             infer_size(&codec->width, &codec->height, size[0]);
     } else {
         f[0] = s1->pb;
-        if (f[0]->eof_reached)
+        if (url_feof(f[0]))
             return AVERROR(EIO);
         size[0]= 4096;
     }
@@ -278,14 +398,32 @@ static int read_packet(AVFormatContext *s1, AVPacket *pkt)
     }
 }
 
+static int read_close(struct AVFormatContext* s1)
+{
+    VideoDemuxData *s = s1->priv_data;
+#if HAVE_GLOB
+    if (s->use_glob) {
+        globfree(&s->globstate);
+    }
+#endif
+    return 0;
+}
+
 #define OFFSET(x) offsetof(VideoDemuxData, x)
 #define DEC AV_OPT_FLAG_DECODING_PARAM
 static const AVOption options[] = {
-    { "pixel_format", "", OFFSET(pixel_format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC },
-    { "video_size",   "", OFFSET(video_size),   AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC },
-    { "framerate",    "", OFFSET(framerate),    AV_OPT_TYPE_STRING, {.str = "25"}, 0, 0, DEC },
-    { "loop",         "", OFFSET(loop),         AV_OPT_TYPE_INT,    {.dbl = 0},    0, 1, DEC },
-    { "start_number", "first number in the sequence", OFFSET(start_number), AV_OPT_TYPE_INT, {.dbl = 1}, 1, INT_MAX, DEC },
+    { "framerate",    "set the video framerate",             OFFSET(framerate),    AV_OPT_TYPE_STRING, {.str = "25"}, 0, 0, DEC },
+    { "loop",         "force loop over input file sequence", OFFSET(loop),         AV_OPT_TYPE_INT,    {.dbl = 0},    0, 1, DEC },
+
+    { "pattern_type", "set pattern type",                    OFFSET(pattern_type), AV_OPT_TYPE_INT, {.dbl=PT_GLOB_SEQUENCE}, 0, INT_MAX, DEC, "pattern_type"},
+    { "glob_sequence","glob/sequence pattern type",          0, AV_OPT_TYPE_CONST, {.i64=PT_GLOB_SEQUENCE}, INT_MIN, INT_MAX, DEC, "pattern_type" },
+    { "glob",         "glob pattern type",                   0, AV_OPT_TYPE_CONST, {.i64=PT_GLOB},          INT_MIN, INT_MAX, DEC, "pattern_type" },
+    { "sequence",     "glob pattern type",                   0, AV_OPT_TYPE_CONST, {.i64=PT_SEQUENCE},      INT_MIN, INT_MAX, DEC, "pattern_type" },
+
+    { "pixel_format", "set video pixel format",              OFFSET(pixel_format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC },
+    { "start_number", "set first number in the sequence",    OFFSET(start_number), AV_OPT_TYPE_INT,    {.dbl = 0},    0, INT_MAX, DEC },
+    { "start_number_range", "set range for looking at the first sequence number", OFFSET(start_number_range), AV_OPT_TYPE_INT, {.dbl = 5}, 1, INT_MAX, DEC },
+    { "video_size",   "set video size",                      OFFSET(video_size),   AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC },
     { NULL },
 };
 
@@ -303,6 +441,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,
 };