Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | Serge | 1 | /* |
2 | * Image format |
||
3 | * Copyright (c) 2000, 2001, 2002 Fabrice Bellard |
||
4 | * Copyright (c) 2004 Michael Niedermayer |
||
5 | * |
||
6 | * This file is part of FFmpeg. |
||
7 | * |
||
8 | * FFmpeg is free software; you can redistribute it and/or |
||
9 | * modify it under the terms of the GNU Lesser General Public |
||
10 | * License as published by the Free Software Foundation; either |
||
11 | * version 2.1 of the License, or (at your option) any later version. |
||
12 | * |
||
13 | * FFmpeg is distributed in the hope that it will be useful, |
||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
16 | * Lesser General Public License for more details. |
||
17 | * |
||
18 | * You should have received a copy of the GNU Lesser General Public |
||
19 | * License along with FFmpeg; if not, write to the Free Software |
||
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||
21 | */ |
||
22 | |||
23 | #include |
||
24 | #include "libavutil/avstring.h" |
||
25 | #include "libavutil/log.h" |
||
26 | #include "libavutil/opt.h" |
||
27 | #include "libavutil/pixdesc.h" |
||
28 | #include "libavutil/parseutils.h" |
||
29 | #include "avformat.h" |
||
30 | #include "internal.h" |
||
31 | #if HAVE_GLOB |
||
32 | #include |
||
33 | |||
34 | /* Locally define as 0 (bitwise-OR no-op) any missing glob options that |
||
35 | are non-posix glibc/bsd extensions. */ |
||
36 | #ifndef GLOB_NOMAGIC |
||
37 | #define GLOB_NOMAGIC 0 |
||
38 | #endif |
||
39 | #ifndef GLOB_BRACE |
||
40 | #define GLOB_BRACE 0 |
||
41 | #endif |
||
42 | |||
43 | #endif /* HAVE_GLOB */ |
||
44 | |||
45 | typedef struct { |
||
46 | const AVClass *class; /**< Class for private options. */ |
||
47 | int img_first; |
||
48 | int img_last; |
||
49 | int img_number; |
||
50 | int64_t pts; |
||
51 | int img_count; |
||
52 | int is_pipe; |
||
53 | int split_planes; /**< use independent file for each Y, U, V plane */ |
||
54 | char path[1024]; |
||
55 | char *pixel_format; /**< Set by a private option. */ |
||
56 | int width, height; /**< Set by a private option. */ |
||
57 | AVRational framerate; /**< Set by a private option. */ |
||
58 | int loop; |
||
59 | enum { PT_GLOB_SEQUENCE, PT_GLOB, PT_SEQUENCE } pattern_type; |
||
60 | int use_glob; |
||
61 | #if HAVE_GLOB |
||
62 | glob_t globstate; |
||
63 | #endif |
||
64 | int start_number; |
||
65 | int start_number_range; |
||
66 | int frame_size; |
||
67 | int ts_from_file; |
||
68 | } VideoDemuxData; |
||
69 | |||
70 | static const int sizes[][2] = { |
||
71 | { 640, 480 }, |
||
72 | { 720, 480 }, |
||
73 | { 720, 576 }, |
||
74 | { 352, 288 }, |
||
75 | { 352, 240 }, |
||
76 | { 160, 128 }, |
||
77 | { 512, 384 }, |
||
78 | { 640, 352 }, |
||
79 | { 640, 240 }, |
||
80 | }; |
||
81 | |||
82 | static int infer_size(int *width_ptr, int *height_ptr, int size) |
||
83 | { |
||
84 | int i; |
||
85 | |||
86 | for (i = 0; i < FF_ARRAY_ELEMS(sizes); i++) { |
||
87 | if ((sizes[i][0] * sizes[i][1]) == size) { |
||
88 | *width_ptr = sizes[i][0]; |
||
89 | *height_ptr = sizes[i][1]; |
||
90 | return 0; |
||
91 | } |
||
92 | } |
||
93 | |||
94 | return -1; |
||
95 | } |
||
96 | |||
97 | static int is_glob(const char *path) |
||
98 | { |
||
99 | #if HAVE_GLOB |
||
100 | size_t span = 0; |
||
101 | const char *p = path; |
||
102 | |||
103 | while (p = strchr(p, '%')) { |
||
104 | if (*(++p) == '%') { |
||
105 | ++p; |
||
106 | continue; |
||
107 | } |
||
108 | if (span = strspn(p, "*?[]{}")) |
||
109 | break; |
||
110 | } |
||
111 | /* Did we hit a glob char or get to the end? */ |
||
112 | return span != 0; |
||
113 | #else |
||
114 | return 0; |
||
115 | #endif |
||
116 | } |
||
117 | |||
118 | /** |
||
119 | * Get index range of image files matched by path. |
||
120 | * |
||
121 | * @param pfirst_index pointer to index updated with the first number in the range |
||
122 | * @param plast_index pointer to index updated with the last number in the range |
||
123 | * @param path path which has to be matched by the image files in the range |
||
124 | * @param start_index minimum accepted value for the first index in the range |
||
125 | * @return -1 if no image file could be found |
||
126 | */ |
||
127 | static int find_image_range(int *pfirst_index, int *plast_index, |
||
128 | const char *path, int start_index, int start_index_range) |
||
129 | { |
||
130 | char buf[1024]; |
||
131 | int range, last_index, range1, first_index; |
||
132 | |||
133 | /* find the first image */ |
||
134 | for (first_index = start_index; first_index < start_index + start_index_range; first_index++) { |
||
135 | if (av_get_frame_filename(buf, sizeof(buf), path, first_index) < 0) { |
||
136 | *pfirst_index = |
||
137 | *plast_index = 1; |
||
138 | if (avio_check(buf, AVIO_FLAG_READ) > 0) |
||
139 | return 0; |
||
140 | return -1; |
||
141 | } |
||
142 | if (avio_check(buf, AVIO_FLAG_READ) > 0) |
||
143 | break; |
||
144 | } |
||
145 | if (first_index == start_index + start_index_range) |
||
146 | goto fail; |
||
147 | |||
148 | /* find the last image */ |
||
149 | last_index = first_index; |
||
150 | for (;;) { |
||
151 | range = 0; |
||
152 | for (;;) { |
||
153 | if (!range) |
||
154 | range1 = 1; |
||
155 | else |
||
156 | range1 = 2 * range; |
||
157 | if (av_get_frame_filename(buf, sizeof(buf), path, |
||
158 | last_index + range1) < 0) |
||
159 | goto fail; |
||
160 | if (avio_check(buf, AVIO_FLAG_READ) <= 0) |
||
161 | break; |
||
162 | range = range1; |
||
163 | /* just in case... */ |
||
164 | if (range >= (1 << 30)) |
||
165 | goto fail; |
||
166 | } |
||
167 | /* we are sure than image last_index + range exists */ |
||
168 | if (!range) |
||
169 | break; |
||
170 | last_index += range; |
||
171 | } |
||
172 | *pfirst_index = first_index; |
||
173 | *plast_index = last_index; |
||
174 | return 0; |
||
175 | |||
176 | fail: |
||
177 | return -1; |
||
178 | } |
||
179 | |||
180 | static int img_read_probe(AVProbeData *p) |
||
181 | { |
||
182 | if (p->filename && ff_guess_image2_codec(p->filename)) { |
||
183 | if (av_filename_number_test(p->filename)) |
||
184 | return AVPROBE_SCORE_MAX; |
||
185 | else if (is_glob(p->filename)) |
||
186 | return AVPROBE_SCORE_MAX; |
||
187 | else if (av_match_ext(p->filename, "raw") || av_match_ext(p->filename, "gif")) |
||
188 | return 5; |
||
189 | else |
||
190 | return AVPROBE_SCORE_EXTENSION; |
||
191 | } |
||
192 | return 0; |
||
193 | } |
||
194 | |||
195 | static int img_read_header(AVFormatContext *s1) |
||
196 | { |
||
197 | VideoDemuxData *s = s1->priv_data; |
||
198 | int first_index, last_index; |
||
199 | AVStream *st; |
||
200 | enum AVPixelFormat pix_fmt = AV_PIX_FMT_NONE; |
||
201 | |||
202 | s1->ctx_flags |= AVFMTCTX_NOHEADER; |
||
203 | |||
204 | st = avformat_new_stream(s1, NULL); |
||
205 | if (!st) { |
||
206 | return AVERROR(ENOMEM); |
||
207 | } |
||
208 | |||
209 | if (s->pixel_format && |
||
210 | (pix_fmt = av_get_pix_fmt(s->pixel_format)) == AV_PIX_FMT_NONE) { |
||
211 | av_log(s1, AV_LOG_ERROR, "No such pixel format: %s.\n", |
||
212 | s->pixel_format); |
||
213 | return AVERROR(EINVAL); |
||
214 | } |
||
215 | |||
216 | av_strlcpy(s->path, s1->filename, sizeof(s->path)); |
||
217 | s->img_number = 0; |
||
218 | s->img_count = 0; |
||
219 | |||
220 | /* find format */ |
||
221 | if (s1->iformat->flags & AVFMT_NOFILE) |
||
222 | s->is_pipe = 0; |
||
223 | else { |
||
224 | s->is_pipe = 1; |
||
225 | st->need_parsing = AVSTREAM_PARSE_FULL; |
||
226 | } |
||
227 | |||
228 | if (s->ts_from_file) |
||
229 | avpriv_set_pts_info(st, 64, 1, 1); |
||
230 | else |
||
231 | avpriv_set_pts_info(st, 64, s->framerate.den, s->framerate.num); |
||
232 | |||
233 | if (s->width && s->height) { |
||
234 | st->codec->width = s->width; |
||
235 | st->codec->height = s->height; |
||
236 | } |
||
237 | |||
238 | if (!s->is_pipe) { |
||
239 | if (s->pattern_type == PT_GLOB_SEQUENCE) { |
||
240 | s->use_glob = is_glob(s->path); |
||
241 | if (s->use_glob) { |
||
242 | char *p = s->path, *q, *dup; |
||
243 | int gerr; |
||
244 | |||
245 | av_log(s1, AV_LOG_WARNING, "Pattern type 'glob_sequence' is deprecated: " |
||
246 | "use pattern_type 'glob' instead\n"); |
||
247 | #if HAVE_GLOB |
||
248 | dup = q = av_strdup(p); |
||
249 | while (*q) { |
||
250 | /* Do we have room for the next char and a \ insertion? */ |
||
251 | if ((p - s->path) >= (sizeof(s->path) - 2)) |
||
252 | break; |
||
253 | if (*q == '%' && strspn(q + 1, "%*?[]{}")) |
||
254 | ++q; |
||
255 | else if (strspn(q, "\\*?[]{}")) |
||
256 | *p++ = '\\'; |
||
257 | *p++ = *q++; |
||
258 | } |
||
259 | *p = 0; |
||
260 | av_free(dup); |
||
261 | |||
262 | gerr = glob(s->path, GLOB_NOCHECK|GLOB_BRACE|GLOB_NOMAGIC, NULL, &s->globstate); |
||
263 | if (gerr != 0) { |
||
264 | return AVERROR(ENOENT); |
||
265 | } |
||
266 | first_index = 0; |
||
267 | last_index = s->globstate.gl_pathc - 1; |
||
268 | #endif |
||
269 | } |
||
270 | } |
||
271 | if ((s->pattern_type == PT_GLOB_SEQUENCE && !s->use_glob) || s->pattern_type == PT_SEQUENCE) { |
||
272 | if (find_image_range(&first_index, &last_index, s->path, |
||
273 | s->start_number, s->start_number_range) < 0) { |
||
274 | av_log(s1, AV_LOG_ERROR, |
||
275 | "Could find no file with path '%s' and index in the range %d-%d\n", |
||
276 | s->path, s->start_number, s->start_number + s->start_number_range - 1); |
||
277 | return AVERROR(ENOENT); |
||
278 | } |
||
279 | } else if (s->pattern_type == PT_GLOB) { |
||
280 | #if HAVE_GLOB |
||
281 | int gerr; |
||
282 | gerr = glob(s->path, GLOB_NOCHECK|GLOB_BRACE|GLOB_NOMAGIC, NULL, &s->globstate); |
||
283 | if (gerr != 0) { |
||
284 | return AVERROR(ENOENT); |
||
285 | } |
||
286 | first_index = 0; |
||
287 | last_index = s->globstate.gl_pathc - 1; |
||
288 | s->use_glob = 1; |
||
289 | #else |
||
290 | av_log(s1, AV_LOG_ERROR, |
||
291 | "Pattern type 'glob' was selected but globbing " |
||
292 | "is not supported by this libavformat build\n"); |
||
293 | return AVERROR(ENOSYS); |
||
294 | #endif |
||
295 | } else if (s->pattern_type != PT_GLOB_SEQUENCE) { |
||
296 | av_log(s1, AV_LOG_ERROR, |
||
297 | "Unknown value '%d' for pattern_type option\n", s->pattern_type); |
||
298 | return AVERROR(EINVAL); |
||
299 | } |
||
300 | s->img_first = first_index; |
||
301 | s->img_last = last_index; |
||
302 | s->img_number = first_index; |
||
303 | /* compute duration */ |
||
304 | if (!s->ts_from_file) { |
||
305 | st->start_time = 0; |
||
306 | st->duration = last_index - first_index + 1; |
||
307 | } |
||
308 | } |
||
309 | |||
310 | if (s1->video_codec_id) { |
||
311 | st->codec->codec_type = AVMEDIA_TYPE_VIDEO; |
||
312 | st->codec->codec_id = s1->video_codec_id; |
||
313 | } else if (s1->audio_codec_id) { |
||
314 | st->codec->codec_type = AVMEDIA_TYPE_AUDIO; |
||
315 | st->codec->codec_id = s1->audio_codec_id; |
||
316 | } else { |
||
317 | const char *str = strrchr(s->path, '.'); |
||
318 | s->split_planes = str && !av_strcasecmp(str + 1, "y"); |
||
319 | st->codec->codec_type = AVMEDIA_TYPE_VIDEO; |
||
320 | st->codec->codec_id = ff_guess_image2_codec(s->path); |
||
321 | if (st->codec->codec_id == AV_CODEC_ID_LJPEG) |
||
322 | st->codec->codec_id = AV_CODEC_ID_MJPEG; |
||
323 | } |
||
324 | if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO && |
||
325 | pix_fmt != AV_PIX_FMT_NONE) |
||
326 | st->codec->pix_fmt = pix_fmt; |
||
327 | |||
328 | return 0; |
||
329 | } |
||
330 | |||
331 | static int img_read_packet(AVFormatContext *s1, AVPacket *pkt) |
||
332 | { |
||
333 | VideoDemuxData *s = s1->priv_data; |
||
334 | char filename_bytes[1024]; |
||
335 | char *filename = filename_bytes; |
||
336 | int i; |
||
337 | int size[3] = { 0 }, ret[3] = { 0 }; |
||
338 | AVIOContext *f[3] = { NULL }; |
||
339 | AVCodecContext *codec = s1->streams[0]->codec; |
||
340 | |||
341 | if (!s->is_pipe) { |
||
342 | /* loop over input */ |
||
343 | if (s->loop && s->img_number > s->img_last) { |
||
344 | s->img_number = s->img_first; |
||
345 | } |
||
346 | if (s->img_number > s->img_last) |
||
347 | return AVERROR_EOF; |
||
348 | if (s->use_glob) { |
||
349 | #if HAVE_GLOB |
||
350 | filename = s->globstate.gl_pathv[s->img_number]; |
||
351 | #endif |
||
352 | } else { |
||
353 | if (av_get_frame_filename(filename_bytes, sizeof(filename_bytes), |
||
354 | s->path, |
||
355 | s->img_number) < 0 && s->img_number > 1) |
||
356 | return AVERROR(EIO); |
||
357 | } |
||
358 | for (i = 0; i < 3; i++) { |
||
359 | if (avio_open2(&f[i], filename, AVIO_FLAG_READ, |
||
360 | &s1->interrupt_callback, NULL) < 0) { |
||
361 | if (i >= 1) |
||
362 | break; |
||
363 | av_log(s1, AV_LOG_ERROR, "Could not open file : %s\n", |
||
364 | filename); |
||
365 | return AVERROR(EIO); |
||
366 | } |
||
367 | size[i] = avio_size(f[i]); |
||
368 | |||
369 | if (!s->split_planes) |
||
370 | break; |
||
371 | filename[strlen(filename) - 1] = 'U' + i; |
||
372 | } |
||
373 | |||
374 | if (codec->codec_id == AV_CODEC_ID_RAWVIDEO && !codec->width) |
||
375 | infer_size(&codec->width, &codec->height, size[0]); |
||
376 | } else { |
||
377 | f[0] = s1->pb; |
||
378 | if (url_feof(f[0])) |
||
379 | return AVERROR(EIO); |
||
380 | if (s->frame_size > 0) { |
||
381 | size[0] = s->frame_size; |
||
382 | } else { |
||
383 | size[0] = 4096; |
||
384 | } |
||
385 | } |
||
386 | |||
387 | if (av_new_packet(pkt, size[0] + size[1] + size[2]) < 0) |
||
388 | return AVERROR(ENOMEM); |
||
389 | pkt->stream_index = 0; |
||
390 | pkt->flags |= AV_PKT_FLAG_KEY; |
||
391 | /* |
||
392 | if (s->ts_from_file) { |
||
393 | struct stat img_stat; |
||
394 | if (stat(filename, &img_stat)) |
||
395 | return AVERROR(EIO); |
||
396 | pkt->pts = (int64_t)img_stat.st_mtime; |
||
397 | av_add_index_entry(s1->streams[0], s->img_number, pkt->pts, 0, 0, AVINDEX_KEYFRAME); |
||
398 | } else if (!s->is_pipe) { |
||
399 | pkt->pts = s->pts; |
||
400 | } |
||
401 | */ |
||
402 | pkt->size = 0; |
||
403 | for (i = 0; i < 3; i++) { |
||
404 | if (f[i]) { |
||
405 | ret[i] = avio_read(f[i], pkt->data + pkt->size, size[i]); |
||
406 | if (!s->is_pipe) |
||
407 | avio_close(f[i]); |
||
408 | if (ret[i] > 0) |
||
409 | pkt->size += ret[i]; |
||
410 | } |
||
411 | } |
||
412 | |||
413 | if (ret[0] <= 0 || ret[1] < 0 || ret[2] < 0) { |
||
414 | av_free_packet(pkt); |
||
415 | return AVERROR(EIO); /* signal EOF */ |
||
416 | } else { |
||
417 | s->img_count++; |
||
418 | s->img_number++; |
||
419 | s->pts++; |
||
420 | return 0; |
||
421 | } |
||
422 | } |
||
423 | |||
424 | static int img_read_close(struct AVFormatContext* s1) |
||
425 | { |
||
426 | VideoDemuxData *s = s1->priv_data; |
||
427 | #if HAVE_GLOB |
||
428 | if (s->use_glob) { |
||
429 | globfree(&s->globstate); |
||
430 | } |
||
431 | #endif |
||
432 | return 0; |
||
433 | } |
||
434 | |||
435 | static int img_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) |
||
436 | { |
||
437 | VideoDemuxData *s1 = s->priv_data; |
||
438 | AVStream *st = s->streams[0]; |
||
439 | |||
440 | if (s1->ts_from_file) { |
||
441 | int index = av_index_search_timestamp(st, timestamp, flags); |
||
442 | if(index < 0) |
||
443 | return -1; |
||
444 | s1->img_number = st->index_entries[index].pos; |
||
445 | return 0; |
||
446 | } |
||
447 | |||
448 | if (timestamp < 0 || !s1->loop && timestamp > s1->img_last - s1->img_first) |
||
449 | return -1; |
||
450 | s1->img_number = timestamp%(s1->img_last - s1->img_first + 1) + s1->img_first; |
||
451 | s1->pts = timestamp; |
||
452 | return 0; |
||
453 | } |
||
454 | |||
455 | #define OFFSET(x) offsetof(VideoDemuxData, x) |
||
456 | #define DEC AV_OPT_FLAG_DECODING_PARAM |
||
457 | static const AVOption options[] = { |
||
458 | { "framerate", "set the video framerate", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, 0, DEC }, |
||
459 | { "loop", "force loop over input file sequence", OFFSET(loop), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, DEC }, |
||
460 | |||
461 | { "pattern_type", "set pattern type", OFFSET(pattern_type), AV_OPT_TYPE_INT, {.i64=PT_GLOB_SEQUENCE}, 0, INT_MAX, DEC, "pattern_type"}, |
||
462 | { "glob_sequence","select glob/sequence pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_GLOB_SEQUENCE}, INT_MIN, INT_MAX, DEC, "pattern_type" }, |
||
463 | { "glob", "select glob pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_GLOB }, INT_MIN, INT_MAX, DEC, "pattern_type" }, |
||
464 | { "sequence", "select sequence pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_SEQUENCE }, INT_MIN, INT_MAX, DEC, "pattern_type" }, |
||
465 | |||
466 | { "pixel_format", "set video pixel format", OFFSET(pixel_format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC }, |
||
467 | { "start_number", "set first number in the sequence", OFFSET(start_number), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, DEC }, |
||
468 | { "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 }, |
||
469 | { "video_size", "set video size", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, DEC }, |
||
470 | { "frame_size", "force frame size in bytes", OFFSET(frame_size), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, DEC }, |
||
471 | { "ts_from_file", "set frame timestamp from file's one", OFFSET(ts_from_file), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, DEC }, |
||
472 | { NULL }, |
||
473 | }; |
||
474 | |||
475 | #if CONFIG_IMAGE2_DEMUXER |
||
476 | static const AVClass img2_class = { |
||
477 | .class_name = "image2 demuxer", |
||
478 | .item_name = av_default_item_name, |
||
479 | .option = options, |
||
480 | .version = LIBAVUTIL_VERSION_INT, |
||
481 | }; |
||
482 | AVInputFormat ff_image2_demuxer = { |
||
483 | .name = "image2", |
||
484 | .long_name = NULL_IF_CONFIG_SMALL("image2 sequence"), |
||
485 | .priv_data_size = sizeof(VideoDemuxData), |
||
486 | .read_probe = img_read_probe, |
||
487 | .read_header = img_read_header, |
||
488 | .read_packet = img_read_packet, |
||
489 | .read_close = img_read_close, |
||
490 | .read_seek = img_read_seek, |
||
491 | .flags = AVFMT_NOFILE, |
||
492 | .priv_class = &img2_class, |
||
493 | }; |
||
494 | #endif |
||
495 | #if CONFIG_IMAGE2PIPE_DEMUXER |
||
496 | static const AVClass img2pipe_class = { |
||
497 | .class_name = "image2pipe demuxer", |
||
498 | .item_name = av_default_item_name, |
||
499 | .option = options, |
||
500 | .version = LIBAVUTIL_VERSION_INT, |
||
501 | }; |
||
502 | AVInputFormat ff_image2pipe_demuxer = { |
||
503 | .name = "image2pipe", |
||
504 | .long_name = NULL_IF_CONFIG_SMALL("piped image2 sequence"), |
||
505 | .priv_data_size = sizeof(VideoDemuxData), |
||
506 | .read_header = img_read_header, |
||
507 | .read_packet = img_read_packet, |
||
508 | .priv_class = &img2pipe_class, |
||
509 | }; |
||
510 | #endif>>>>=>>>>>>>><>=>>>>>>>>>> |