diff options
Diffstat (limited to 'libavformat/img2dec.c')
-rw-r--r-- | libavformat/img2dec.c | 208 |
1 files changed, 186 insertions, 22 deletions
diff --git a/libavformat/img2dec.c b/libavformat/img2dec.c index 1d437f5b4d..23cdc4b85b 100644 --- a/libavformat/img2dec.c +++ b/libavformat/img2dec.c @@ -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 */ @@ -27,20 +27,42 @@ #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. */ int img_first; int img_last; int img_number; + int64_t pts; 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; + int frame_size; } VideoDemuxData; static const int sizes[][2] = { @@ -70,15 +92,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; @@ -89,7 +140,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 */ @@ -129,6 +180,10 @@ static int img_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 if (av_match_ext(p->filename, "raw") || av_match_ext(p->filename, "gif")) + return 5; else return AVPROBE_SCORE_MAX / 2; } @@ -189,9 +244,67 @@ static int img_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; @@ -207,8 +320,12 @@ static int img_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 != AV_PIX_FMT_NONE) @@ -220,7 +337,8 @@ static int img_read_header(AVFormatContext *s1) static int img_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] = { NULL }; @@ -233,10 +351,16 @@ static int img_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) { @@ -248,7 +372,7 @@ static int img_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; } @@ -257,14 +381,21 @@ static int img_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; + if (s->frame_size > 0) { + size[0] = s->frame_size; + } else { + size[0] = 4096; + } } - av_new_packet(pkt, size[0] + size[1] + size[2]); + if (av_new_packet(pkt, size[0] + size[1] + size[2]) < 0) + return AVERROR(ENOMEM); pkt->stream_index = 0; pkt->flags |= AV_PKT_FLAG_KEY; + if (!s->is_pipe) + pkt->pts = s->pts; pkt->size = 0; for (i = 0; i < 3; i++) { @@ -283,18 +414,49 @@ static int img_read_packet(AVFormatContext *s1, AVPacket *pkt) } else { s->img_count++; s->img_number++; + s->pts++; return 0; } } +static int img_read_close(struct AVFormatContext* s1) +{ + VideoDemuxData *s = s1->priv_data; +#if HAVE_GLOB + if (s->use_glob) { + globfree(&s->globstate); + } +#endif + return 0; +} + +static int img_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) +{ + VideoDemuxData *s1 = s->priv_data; + + if (timestamp < 0 || !s1->loop && timestamp > s1->img_last - s1->img_first) + return -1; + s1->img_number = timestamp%(s1->img_last - s1->img_first + 1) + s1->img_first; + s1->pts = timestamp; + 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, { .i64 = 0 }, 0, 1, DEC }, - { "start_number", "first number in the sequence", OFFSET(start_number), AV_OPT_TYPE_INT, { .i64 = 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, {.i64 = 0 }, 0, 1, DEC }, + + { "pattern_type", "set pattern type", OFFSET(pattern_type), AV_OPT_TYPE_INT, {.i64=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, {.i64 = 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, {.i64 = 5}, 1, INT_MAX, DEC }, + { "video_size", "set video size", OFFSET(video_size), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC }, + { "frame_size", "force frame size in bytes", OFFSET(frame_size), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, DEC }, { NULL }, }; @@ -312,6 +474,8 @@ AVInputFormat ff_image2_demuxer = { .read_probe = img_read_probe, .read_header = img_read_header, .read_packet = img_read_packet, + .read_close = img_read_close, + .read_seek = img_read_seek, .flags = AVFMT_NOFILE, .priv_class = &img2_class, }; |