yading@11: /* yading@11: * Image format yading@11: * Copyright (c) 2000, 2001, 2002 Fabrice Bellard yading@11: * Copyright (c) 2004 Michael Niedermayer yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: #include "libavutil/avstring.h" yading@11: #include "libavutil/log.h" yading@11: #include "libavutil/opt.h" yading@11: #include "libavutil/pixdesc.h" yading@11: #include "libavutil/parseutils.h" yading@11: #include "avformat.h" yading@11: #include "internal.h" yading@11: #if HAVE_GLOB yading@11: #include yading@11: yading@11: /* Locally define as 0 (bitwise-OR no-op) any missing glob options that yading@11: are non-posix glibc/bsd extensions. */ yading@11: #ifndef GLOB_NOMAGIC yading@11: #define GLOB_NOMAGIC 0 yading@11: #endif yading@11: #ifndef GLOB_BRACE yading@11: #define GLOB_BRACE 0 yading@11: #endif yading@11: yading@11: #endif /* HAVE_GLOB */ yading@11: yading@11: typedef struct { yading@11: const AVClass *class; /**< Class for private options. */ yading@11: int img_first; yading@11: int img_last; yading@11: int img_number; yading@11: int64_t pts; yading@11: int img_count; yading@11: int is_pipe; yading@11: int split_planes; /**< use independent file for each Y, U, V plane */ yading@11: char path[1024]; yading@11: char *pixel_format; /**< Set by a private option. */ yading@11: int width, height; /**< Set by a private option. */ yading@11: AVRational framerate; /**< Set by a private option. */ yading@11: int loop; yading@11: enum { PT_GLOB_SEQUENCE, PT_GLOB, PT_SEQUENCE } pattern_type; yading@11: int use_glob; yading@11: #if HAVE_GLOB yading@11: glob_t globstate; yading@11: #endif yading@11: int start_number; yading@11: int start_number_range; yading@11: int frame_size; yading@11: } VideoDemuxData; yading@11: yading@11: static const int sizes[][2] = { yading@11: { 640, 480 }, yading@11: { 720, 480 }, yading@11: { 720, 576 }, yading@11: { 352, 288 }, yading@11: { 352, 240 }, yading@11: { 160, 128 }, yading@11: { 512, 384 }, yading@11: { 640, 352 }, yading@11: { 640, 240 }, yading@11: }; yading@11: yading@11: static int infer_size(int *width_ptr, int *height_ptr, int size) yading@11: { yading@11: int i; yading@11: yading@11: for (i = 0; i < FF_ARRAY_ELEMS(sizes); i++) { yading@11: if ((sizes[i][0] * sizes[i][1]) == size) { yading@11: *width_ptr = sizes[i][0]; yading@11: *height_ptr = sizes[i][1]; yading@11: return 0; yading@11: } yading@11: } yading@11: yading@11: return -1; yading@11: } yading@11: yading@11: static int is_glob(const char *path) yading@11: { yading@11: #if HAVE_GLOB yading@11: size_t span = 0; yading@11: const char *p = path; yading@11: yading@11: while (p = strchr(p, '%')) { yading@11: if (*(++p) == '%') { yading@11: ++p; yading@11: continue; yading@11: } yading@11: if (span = strspn(p, "*?[]{}")) yading@11: break; yading@11: } yading@11: /* Did we hit a glob char or get to the end? */ yading@11: return span != 0; yading@11: #else yading@11: return 0; yading@11: #endif yading@11: } yading@11: yading@11: /** yading@11: * Get index range of image files matched by path. yading@11: * yading@11: * @param pfirst_index pointer to index updated with the first number in the range yading@11: * @param plast_index pointer to index updated with the last number in the range yading@11: * @param path path which has to be matched by the image files in the range yading@11: * @param start_index minimum accepted value for the first index in the range yading@11: * @return -1 if no image file could be found yading@11: */ yading@11: static int find_image_range(int *pfirst_index, int *plast_index, yading@11: const char *path, int start_index, int start_index_range) yading@11: { yading@11: char buf[1024]; yading@11: int range, last_index, range1, first_index; yading@11: yading@11: /* find the first image */ yading@11: for (first_index = start_index; first_index < start_index + start_index_range; first_index++) { yading@11: if (av_get_frame_filename(buf, sizeof(buf), path, first_index) < 0) { yading@11: *pfirst_index = yading@11: *plast_index = 1; yading@11: if (avio_check(buf, AVIO_FLAG_READ) > 0) yading@11: return 0; yading@11: return -1; yading@11: } yading@11: if (avio_check(buf, AVIO_FLAG_READ) > 0) yading@11: break; yading@11: } yading@11: if (first_index == start_index + start_index_range) yading@11: goto fail; yading@11: yading@11: /* find the last image */ yading@11: last_index = first_index; yading@11: for (;;) { yading@11: range = 0; yading@11: for (;;) { yading@11: if (!range) yading@11: range1 = 1; yading@11: else yading@11: range1 = 2 * range; yading@11: if (av_get_frame_filename(buf, sizeof(buf), path, yading@11: last_index + range1) < 0) yading@11: goto fail; yading@11: if (avio_check(buf, AVIO_FLAG_READ) <= 0) yading@11: break; yading@11: range = range1; yading@11: /* just in case... */ yading@11: if (range >= (1 << 30)) yading@11: goto fail; yading@11: } yading@11: /* we are sure than image last_index + range exists */ yading@11: if (!range) yading@11: break; yading@11: last_index += range; yading@11: } yading@11: *pfirst_index = first_index; yading@11: *plast_index = last_index; yading@11: return 0; yading@11: yading@11: fail: yading@11: return -1; yading@11: } yading@11: yading@11: static int img_read_probe(AVProbeData *p) yading@11: { yading@11: if (p->filename && ff_guess_image2_codec(p->filename)) { yading@11: if (av_filename_number_test(p->filename)) yading@11: return AVPROBE_SCORE_MAX; yading@11: else if (is_glob(p->filename)) yading@11: return AVPROBE_SCORE_MAX; yading@11: else if (av_match_ext(p->filename, "raw") || av_match_ext(p->filename, "gif")) yading@11: return 5; yading@11: else yading@11: return AVPROBE_SCORE_MAX / 2; yading@11: } yading@11: return 0; yading@11: } yading@11: yading@11: static int img_read_header(AVFormatContext *s1) yading@11: { yading@11: VideoDemuxData *s = s1->priv_data; yading@11: int first_index, last_index; yading@11: AVStream *st; yading@11: enum AVPixelFormat pix_fmt = AV_PIX_FMT_NONE; yading@11: yading@11: s1->ctx_flags |= AVFMTCTX_NOHEADER; yading@11: yading@11: st = avformat_new_stream(s1, NULL); yading@11: if (!st) { yading@11: return AVERROR(ENOMEM); yading@11: } yading@11: yading@11: if (s->pixel_format && yading@11: (pix_fmt = av_get_pix_fmt(s->pixel_format)) == AV_PIX_FMT_NONE) { yading@11: av_log(s1, AV_LOG_ERROR, "No such pixel format: %s.\n", yading@11: s->pixel_format); yading@11: return AVERROR(EINVAL); yading@11: } yading@11: yading@11: av_strlcpy(s->path, s1->filename, sizeof(s->path)); yading@11: s->img_number = 0; yading@11: s->img_count = 0; yading@11: yading@11: /* find format */ yading@11: if (s1->iformat->flags & AVFMT_NOFILE) yading@11: s->is_pipe = 0; yading@11: else { yading@11: s->is_pipe = 1; yading@11: st->need_parsing = AVSTREAM_PARSE_FULL; yading@11: } yading@11: yading@11: avpriv_set_pts_info(st, 60, s->framerate.den, s->framerate.num); yading@11: yading@11: if (s->width && s->height) { yading@11: st->codec->width = s->width; yading@11: st->codec->height = s->height; yading@11: } yading@11: yading@11: if (!s->is_pipe) { yading@11: if (s->pattern_type == PT_GLOB_SEQUENCE) { yading@11: s->use_glob = is_glob(s->path); yading@11: if (s->use_glob) { yading@11: char *p = s->path, *q, *dup; yading@11: int gerr; yading@11: yading@11: av_log(s1, AV_LOG_WARNING, "Pattern type 'glob_sequence' is deprecated: " yading@11: "use pattern_type 'glob' instead\n"); yading@11: #if HAVE_GLOB yading@11: dup = q = av_strdup(p); yading@11: while (*q) { yading@11: /* Do we have room for the next char and a \ insertion? */ yading@11: if ((p - s->path) >= (sizeof(s->path) - 2)) yading@11: break; yading@11: if (*q == '%' && strspn(q + 1, "%*?[]{}")) yading@11: ++q; yading@11: else if (strspn(q, "\\*?[]{}")) yading@11: *p++ = '\\'; yading@11: *p++ = *q++; yading@11: } yading@11: *p = 0; yading@11: av_free(dup); yading@11: yading@11: gerr = glob(s->path, GLOB_NOCHECK|GLOB_BRACE|GLOB_NOMAGIC, NULL, &s->globstate); yading@11: if (gerr != 0) { yading@11: return AVERROR(ENOENT); yading@11: } yading@11: first_index = 0; yading@11: last_index = s->globstate.gl_pathc - 1; yading@11: #endif yading@11: } yading@11: } yading@11: if ((s->pattern_type == PT_GLOB_SEQUENCE && !s->use_glob) || s->pattern_type == PT_SEQUENCE) { yading@11: if (find_image_range(&first_index, &last_index, s->path, yading@11: s->start_number, s->start_number_range) < 0) { yading@11: av_log(s1, AV_LOG_ERROR, yading@11: "Could find no file with with path '%s' and index in the range %d-%d\n", yading@11: s->path, s->start_number, s->start_number + s->start_number_range - 1); yading@11: return AVERROR(ENOENT); yading@11: } yading@11: } else if (s->pattern_type == PT_GLOB) { yading@11: #if HAVE_GLOB yading@11: int gerr; yading@11: gerr = glob(s->path, GLOB_NOCHECK|GLOB_BRACE|GLOB_NOMAGIC, NULL, &s->globstate); yading@11: if (gerr != 0) { yading@11: return AVERROR(ENOENT); yading@11: } yading@11: first_index = 0; yading@11: last_index = s->globstate.gl_pathc - 1; yading@11: s->use_glob = 1; yading@11: #else yading@11: av_log(s1, AV_LOG_ERROR, yading@11: "Pattern type 'glob' was selected but globbing " yading@11: "is not supported by this libavformat build\n"); yading@11: return AVERROR(ENOSYS); yading@11: #endif yading@11: } else if (s->pattern_type != PT_GLOB_SEQUENCE) { yading@11: av_log(s1, AV_LOG_ERROR, yading@11: "Unknown value '%d' for pattern_type option\n", s->pattern_type); yading@11: return AVERROR(EINVAL); yading@11: } yading@11: s->img_first = first_index; yading@11: s->img_last = last_index; yading@11: s->img_number = first_index; yading@11: /* compute duration */ yading@11: st->start_time = 0; yading@11: st->duration = last_index - first_index + 1; yading@11: } yading@11: yading@11: if (s1->video_codec_id) { yading@11: st->codec->codec_type = AVMEDIA_TYPE_VIDEO; yading@11: st->codec->codec_id = s1->video_codec_id; yading@11: } else if (s1->audio_codec_id) { yading@11: st->codec->codec_type = AVMEDIA_TYPE_AUDIO; yading@11: st->codec->codec_id = s1->audio_codec_id; yading@11: } else { yading@11: const char *str = strrchr(s->path, '.'); yading@11: s->split_planes = str && !av_strcasecmp(str + 1, "y"); yading@11: st->codec->codec_type = AVMEDIA_TYPE_VIDEO; yading@11: st->codec->codec_id = ff_guess_image2_codec(s->path); yading@11: if (st->codec->codec_id == AV_CODEC_ID_LJPEG) yading@11: st->codec->codec_id = AV_CODEC_ID_MJPEG; yading@11: } yading@11: if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && yading@11: pix_fmt != AV_PIX_FMT_NONE) yading@11: st->codec->pix_fmt = pix_fmt; yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int img_read_packet(AVFormatContext *s1, AVPacket *pkt) yading@11: { yading@11: VideoDemuxData *s = s1->priv_data; yading@11: char filename_bytes[1024]; yading@11: char *filename = filename_bytes; yading@11: int i; yading@11: int size[3] = { 0 }, ret[3] = { 0 }; yading@11: AVIOContext *f[3] = { NULL }; yading@11: AVCodecContext *codec = s1->streams[0]->codec; yading@11: yading@11: if (!s->is_pipe) { yading@11: /* loop over input */ yading@11: if (s->loop && s->img_number > s->img_last) { yading@11: s->img_number = s->img_first; yading@11: } yading@11: if (s->img_number > s->img_last) yading@11: return AVERROR_EOF; yading@11: if (s->use_glob) { yading@11: #if HAVE_GLOB yading@11: filename = s->globstate.gl_pathv[s->img_number]; yading@11: #endif yading@11: } else { yading@11: if (av_get_frame_filename(filename_bytes, sizeof(filename_bytes), yading@11: s->path, yading@11: s->img_number) < 0 && s->img_number > 1) yading@11: return AVERROR(EIO); yading@11: } yading@11: for (i = 0; i < 3; i++) { yading@11: if (avio_open2(&f[i], filename, AVIO_FLAG_READ, yading@11: &s1->interrupt_callback, NULL) < 0) { yading@11: if (i >= 1) yading@11: break; yading@11: av_log(s1, AV_LOG_ERROR, "Could not open file : %s\n", yading@11: filename); yading@11: return AVERROR(EIO); yading@11: } yading@11: size[i] = avio_size(f[i]); yading@11: yading@11: if (!s->split_planes) yading@11: break; yading@11: filename[strlen(filename) - 1] = 'U' + i; yading@11: } yading@11: yading@11: if (codec->codec_id == AV_CODEC_ID_RAWVIDEO && !codec->width) yading@11: infer_size(&codec->width, &codec->height, size[0]); yading@11: } else { yading@11: f[0] = s1->pb; yading@11: if (url_feof(f[0])) yading@11: return AVERROR(EIO); yading@11: if (s->frame_size > 0) { yading@11: size[0] = s->frame_size; yading@11: } else { yading@11: size[0] = 4096; yading@11: } yading@11: } yading@11: yading@11: if (av_new_packet(pkt, size[0] + size[1] + size[2]) < 0) yading@11: return AVERROR(ENOMEM); yading@11: pkt->stream_index = 0; yading@11: pkt->flags |= AV_PKT_FLAG_KEY; yading@11: if (!s->is_pipe) yading@11: pkt->pts = s->pts; yading@11: yading@11: pkt->size = 0; yading@11: for (i = 0; i < 3; i++) { yading@11: if (f[i]) { yading@11: ret[i] = avio_read(f[i], pkt->data + pkt->size, size[i]); yading@11: if (!s->is_pipe) yading@11: avio_close(f[i]); yading@11: if (ret[i] > 0) yading@11: pkt->size += ret[i]; yading@11: } yading@11: } yading@11: yading@11: if (ret[0] <= 0 || ret[1] < 0 || ret[2] < 0) { yading@11: av_free_packet(pkt); yading@11: return AVERROR(EIO); /* signal EOF */ yading@11: } else { yading@11: s->img_count++; yading@11: s->img_number++; yading@11: s->pts++; yading@11: return 0; yading@11: } yading@11: } yading@11: yading@11: static int img_read_close(struct AVFormatContext* s1) yading@11: { yading@11: VideoDemuxData *s = s1->priv_data; yading@11: #if HAVE_GLOB yading@11: if (s->use_glob) { yading@11: globfree(&s->globstate); yading@11: } yading@11: #endif yading@11: return 0; yading@11: } yading@11: yading@11: static int img_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) yading@11: { yading@11: VideoDemuxData *s1 = s->priv_data; yading@11: yading@11: if (timestamp < 0 || !s1->loop && timestamp > s1->img_last - s1->img_first) yading@11: return -1; yading@11: s1->img_number = timestamp%(s1->img_last - s1->img_first + 1) + s1->img_first; yading@11: s1->pts = timestamp; yading@11: return 0; yading@11: } yading@11: yading@11: #define OFFSET(x) offsetof(VideoDemuxData, x) yading@11: #define DEC AV_OPT_FLAG_DECODING_PARAM yading@11: static const AVOption options[] = { yading@11: { "framerate", "set the video framerate", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, DEC }, yading@11: { "loop", "force loop over input file sequence", OFFSET(loop), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, DEC }, yading@11: yading@11: { "pattern_type", "set pattern type", OFFSET(pattern_type), AV_OPT_TYPE_INT, {.i64=PT_GLOB_SEQUENCE}, 0, INT_MAX, DEC, "pattern_type"}, yading@11: { "glob_sequence","select glob/sequence pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_GLOB_SEQUENCE}, INT_MIN, INT_MAX, DEC, "pattern_type" }, yading@11: { "glob", "select glob pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_GLOB }, INT_MIN, INT_MAX, DEC, "pattern_type" }, yading@11: { "sequence", "select sequence pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_SEQUENCE }, INT_MIN, INT_MAX, DEC, "pattern_type" }, yading@11: yading@11: { "pixel_format", "set video pixel format", OFFSET(pixel_format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC }, yading@11: { "start_number", "set first number in the sequence", OFFSET(start_number), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, DEC }, yading@11: { "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 }, yading@11: { "video_size", "set video size", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, DEC }, yading@11: { "frame_size", "force frame size in bytes", OFFSET(frame_size), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, DEC }, yading@11: { NULL }, yading@11: }; yading@11: yading@11: #if CONFIG_IMAGE2_DEMUXER yading@11: static const AVClass img2_class = { yading@11: .class_name = "image2 demuxer", yading@11: .item_name = av_default_item_name, yading@11: .option = options, yading@11: .version = LIBAVUTIL_VERSION_INT, yading@11: }; yading@11: AVInputFormat ff_image2_demuxer = { yading@11: .name = "image2", yading@11: .long_name = NULL_IF_CONFIG_SMALL("image2 sequence"), yading@11: .priv_data_size = sizeof(VideoDemuxData), yading@11: .read_probe = img_read_probe, yading@11: .read_header = img_read_header, yading@11: .read_packet = img_read_packet, yading@11: .read_close = img_read_close, yading@11: .read_seek = img_read_seek, yading@11: .flags = AVFMT_NOFILE, yading@11: .priv_class = &img2_class, yading@11: }; yading@11: #endif yading@11: #if CONFIG_IMAGE2PIPE_DEMUXER yading@11: static const AVClass img2pipe_class = { yading@11: .class_name = "image2pipe demuxer", yading@11: .item_name = av_default_item_name, yading@11: .option = options, yading@11: .version = LIBAVUTIL_VERSION_INT, yading@11: }; yading@11: AVInputFormat ff_image2pipe_demuxer = { yading@11: .name = "image2pipe", yading@11: .long_name = NULL_IF_CONFIG_SMALL("piped image2 sequence"), yading@11: .priv_data_size = sizeof(VideoDemuxData), yading@11: .read_header = img_read_header, yading@11: .read_packet = img_read_packet, yading@11: .priv_class = &img2pipe_class, yading@11: }; yading@11: #endif