]> git.sesse.net Git - ffmpeg/blob - libavformat/concatdec.c
ca193f92ec6259367812e12882c7d87c94d57d35
[ffmpeg] / libavformat / concatdec.c
1 /*
2  * Copyright (c) 2012 Nicolas George
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License
8  * as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with FFmpeg; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20
21 #include "libavutil/avstring.h"
22 #include "libavutil/opt.h"
23 #include "libavutil/parseutils.h"
24 #include "avformat.h"
25 #include "internal.h"
26 #include "url.h"
27
28 typedef struct ConcatStream {
29     int out_stream_index;
30 } ConcatStream;
31
32 typedef struct {
33     char *url;
34     int64_t start_time;
35     int64_t duration;
36     ConcatStream *streams;
37     int nb_streams;
38 } ConcatFile;
39
40 typedef struct {
41     AVClass *class;
42     ConcatFile *files;
43     ConcatFile *cur_file;
44     unsigned nb_files;
45     AVFormatContext *avf;
46     int safe;
47     int seekable;
48     int match_streams;
49 } ConcatContext;
50
51 static int concat_probe(AVProbeData *probe)
52 {
53     return memcmp(probe->buf, "ffconcat version 1.0", 20) ?
54            0 : AVPROBE_SCORE_MAX;
55 }
56
57 static char *get_keyword(uint8_t **cursor)
58 {
59     char *ret = *cursor += strspn(*cursor, SPACE_CHARS);
60     *cursor += strcspn(*cursor, SPACE_CHARS);
61     if (**cursor) {
62         *((*cursor)++) = 0;
63         *cursor += strspn(*cursor, SPACE_CHARS);
64     }
65     return ret;
66 }
67
68 static int safe_filename(const char *f)
69 {
70     const char *start = f;
71
72     for (; *f; f++) {
73         /* A-Za-z0-9_- */
74         if (!((unsigned)((*f | 32) - 'a') < 26 ||
75               (unsigned)(*f - '0') < 10 || *f == '_' || *f == '-')) {
76             if (f == start)
77                 return 0;
78             else if (*f == '/')
79                 start = f + 1;
80             else if (*f != '.')
81                 return 0;
82         }
83     }
84     return 1;
85 }
86
87 #define FAIL(retcode) do { ret = (retcode); goto fail; } while(0)
88
89 static int add_file(AVFormatContext *avf, char *filename, ConcatFile **rfile,
90                     unsigned *nb_files_alloc)
91 {
92     ConcatContext *cat = avf->priv_data;
93     ConcatFile *file;
94     char *url = NULL;
95     const char *proto;
96     size_t url_len, proto_len;
97     int ret;
98
99     if (cat->safe > 0 && !safe_filename(filename)) {
100         av_log(avf, AV_LOG_ERROR, "Unsafe file name '%s'\n", filename);
101         FAIL(AVERROR(EPERM));
102     }
103
104     proto = avio_find_protocol_name(filename);
105     proto_len = proto ? strlen(proto) : 0;
106     if (!memcmp(filename, proto, proto_len) &&
107         (filename[proto_len] == ':' || filename[proto_len] == ',')) {
108         url = filename;
109         filename = NULL;
110     } else {
111         url_len = strlen(avf->filename) + strlen(filename) + 16;
112         if (!(url = av_malloc(url_len)))
113             FAIL(AVERROR(ENOMEM));
114         ff_make_absolute_url(url, url_len, avf->filename, filename);
115         av_freep(&filename);
116     }
117
118     if (cat->nb_files >= *nb_files_alloc) {
119         size_t n = FFMAX(*nb_files_alloc * 2, 16);
120         ConcatFile *new_files;
121         if (n <= cat->nb_files || n > SIZE_MAX / sizeof(*cat->files) ||
122             !(new_files = av_realloc(cat->files, n * sizeof(*cat->files))))
123             FAIL(AVERROR(ENOMEM));
124         cat->files = new_files;
125         *nb_files_alloc = n;
126     }
127
128     file = &cat->files[cat->nb_files++];
129     memset(file, 0, sizeof(*file));
130     *rfile = file;
131
132     file->url        = url;
133     file->start_time = AV_NOPTS_VALUE;
134     file->duration   = AV_NOPTS_VALUE;
135
136     return 0;
137
138 fail:
139     av_free(url);
140     av_free(filename);
141     return ret;
142 }
143
144 static int copy_stream_props(AVStream *st, AVStream *source_st)
145 {
146     int ret;
147
148     if ((ret = avcodec_copy_context(st->codec, source_st->codec)) < 0)
149         return ret;
150     st->r_frame_rate        = source_st->r_frame_rate;
151     st->avg_frame_rate      = source_st->avg_frame_rate;
152     st->time_base           = source_st->time_base;
153     st->sample_aspect_ratio = source_st->sample_aspect_ratio;
154     return 0;
155 }
156
157 static int match_streams(AVFormatContext *avf)
158 {
159     ConcatContext *cat = avf->priv_data;
160     AVStream *st;
161     ConcatStream *map;
162     int i, j, ret;
163
164     if (!cat->match_streams ||
165         cat->cur_file->nb_streams >= cat->avf->nb_streams)
166         return 0;
167     map = av_realloc(cat->cur_file->streams,
168                      cat->avf->nb_streams * sizeof(*map));
169     if (!map)
170         return AVERROR(ENOMEM);
171     cat->cur_file->streams = map;
172
173     for (i = cat->cur_file->nb_streams; i < cat->avf->nb_streams; i++) {
174         st = cat->avf->streams[i];
175         map[i].out_stream_index = -1;
176         for (j = 0; j < avf->nb_streams; j++) {
177             if (avf->streams[j]->id == st->id) {
178                 av_log(avf, AV_LOG_VERBOSE,
179                        "Match slave stream #%d with stream #%d id 0x%x\n",
180                        i, j, st->id);
181                 map[i].out_stream_index = j;
182                 if (!avf->streams[j]->codec->codec_id && st->codec->codec_id)
183                     if ((ret = copy_stream_props(avf->streams[j], st)) < 0)
184                         return ret;
185             }
186         }
187     }
188
189     cat->cur_file->nb_streams = cat->avf->nb_streams;
190     return 0;
191 }
192
193 static int open_file(AVFormatContext *avf, unsigned fileno)
194 {
195     ConcatContext *cat = avf->priv_data;
196     ConcatFile *file = &cat->files[fileno];
197     int ret;
198
199     if (cat->avf)
200         avformat_close_input(&cat->avf);
201
202     cat->avf = avformat_alloc_context();
203     if (!cat->avf)
204         return AVERROR(ENOMEM);
205
206     cat->avf->interrupt_callback = avf->interrupt_callback;
207     if ((ret = avformat_open_input(&cat->avf, file->url, NULL, NULL)) < 0 ||
208         (ret = avformat_find_stream_info(cat->avf, NULL)) < 0) {
209         av_log(avf, AV_LOG_ERROR, "Impossible to open '%s'\n", file->url);
210         avformat_close_input(&cat->avf);
211         return ret;
212     }
213     cat->cur_file = file;
214     if (file->start_time == AV_NOPTS_VALUE)
215         file->start_time = !fileno ? 0 :
216                            cat->files[fileno - 1].start_time +
217                            cat->files[fileno - 1].duration;
218     if ((ret = match_streams(avf)) < 0)
219         return ret;
220     return 0;
221 }
222
223 static int concat_read_close(AVFormatContext *avf)
224 {
225     ConcatContext *cat = avf->priv_data;
226     unsigned i;
227
228     if (cat->avf)
229         avformat_close_input(&cat->avf);
230     for (i = 0; i < cat->nb_files; i++)
231         av_freep(&cat->files[i].url);
232     av_freep(&cat->files);
233     return 0;
234 }
235
236 static int concat_read_header(AVFormatContext *avf)
237 {
238     ConcatContext *cat = avf->priv_data;
239     uint8_t buf[4096];
240     uint8_t *cursor, *keyword;
241     int ret, line = 0, i;
242     unsigned nb_files_alloc = 0;
243     ConcatFile *file = NULL;
244     AVStream *st;
245     int64_t time = 0;
246
247     while (1) {
248         if ((ret = ff_get_line(avf->pb, buf, sizeof(buf))) <= 0)
249             break;
250         line++;
251         cursor = buf;
252         keyword = get_keyword(&cursor);
253         if (!*keyword || *keyword == '#')
254             continue;
255
256         if (!strcmp(keyword, "file")) {
257             char *filename = av_get_token((const char **)&cursor, SPACE_CHARS);
258             if (!filename) {
259                 av_log(avf, AV_LOG_ERROR, "Line %d: filename required\n", line);
260                 FAIL(AVERROR_INVALIDDATA);
261             }
262             if ((ret = add_file(avf, filename, &file, &nb_files_alloc)) < 0)
263                 FAIL(ret);
264         } else if (!strcmp(keyword, "duration")) {
265             char *dur_str = get_keyword(&cursor);
266             int64_t dur;
267             if (!file) {
268                 av_log(avf, AV_LOG_ERROR, "Line %d: duration without file\n",
269                        line);
270                 FAIL(AVERROR_INVALIDDATA);
271             }
272             if ((ret = av_parse_time(&dur, dur_str, 1)) < 0) {
273                 av_log(avf, AV_LOG_ERROR, "Line %d: invalid duration '%s'\n",
274                        line, dur_str);
275                 FAIL(ret);
276             }
277             file->duration = dur;
278         } else if (!strcmp(keyword, "stream")) {
279             if (!avformat_new_stream(avf, NULL))
280                 FAIL(AVERROR(ENOMEM));
281         } else if (!strcmp(keyword, "exact_stream_id")) {
282             if (!avf->nb_streams) {
283                 av_log(avf, AV_LOG_ERROR, "Line %d: exact_stream_id without stream\n",
284                        line);
285                 FAIL(AVERROR_INVALIDDATA);
286             }
287             avf->streams[avf->nb_streams - 1]->id =
288                 strtol(get_keyword(&cursor), NULL, 0);
289         } else if (!strcmp(keyword, "ffconcat")) {
290             char *ver_kw  = get_keyword(&cursor);
291             char *ver_val = get_keyword(&cursor);
292             if (strcmp(ver_kw, "version") || strcmp(ver_val, "1.0")) {
293                 av_log(avf, AV_LOG_ERROR, "Line %d: invalid version\n", line);
294                 FAIL(AVERROR_INVALIDDATA);
295             }
296             if (cat->safe < 0)
297                 cat->safe = 1;
298         } else {
299             av_log(avf, AV_LOG_ERROR, "Line %d: unknown keyword '%s'\n",
300                    line, keyword);
301             FAIL(AVERROR_INVALIDDATA);
302         }
303     }
304     if (ret < 0)
305         FAIL(ret);
306     if (!cat->nb_files)
307         FAIL(AVERROR_INVALIDDATA);
308
309     for (i = 0; i < cat->nb_files; i++) {
310         if (cat->files[i].start_time == AV_NOPTS_VALUE)
311             cat->files[i].start_time = time;
312         else
313             time = cat->files[i].start_time;
314         if (cat->files[i].duration == AV_NOPTS_VALUE)
315             break;
316         time += cat->files[i].duration;
317     }
318     if (i == cat->nb_files) {
319         avf->duration = time;
320         cat->seekable = 1;
321     }
322
323     cat->match_streams = !!avf->nb_streams;
324     if ((ret = open_file(avf, 0)) < 0)
325         FAIL(ret);
326     if (!cat->match_streams) {
327         for (i = 0; i < cat->avf->nb_streams; i++) {
328             if (!(st = avformat_new_stream(avf, NULL)))
329                 FAIL(AVERROR(ENOMEM));
330             if ((ret = copy_stream_props(st, cat->avf->streams[i])) < 0)
331                 FAIL(ret);
332         }
333     }
334
335     return 0;
336
337 fail:
338     concat_read_close(avf);
339     return ret;
340 }
341
342 static int open_next_file(AVFormatContext *avf)
343 {
344     ConcatContext *cat = avf->priv_data;
345     unsigned fileno = cat->cur_file - cat->files;
346
347     if (cat->cur_file->duration == AV_NOPTS_VALUE)
348         cat->cur_file->duration = cat->avf->duration;
349
350     if (++fileno >= cat->nb_files)
351         return AVERROR_EOF;
352     return open_file(avf, fileno);
353 }
354
355 static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt)
356 {
357     ConcatContext *cat = avf->priv_data;
358     int ret;
359     int64_t delta;
360     ConcatStream *cs;
361
362     while (1) {
363         ret = av_read_frame(cat->avf, pkt);
364         if (ret == AVERROR_EOF) {
365             if ((ret = open_next_file(avf)) < 0)
366                 return ret;
367             continue;
368         }
369         if (ret < 0)
370             return ret;
371         if (cat->match_streams) {
372             if ((ret = match_streams(avf)) < 0) {
373                 av_packet_unref(pkt);
374                 return ret;
375             }
376             cs = &cat->cur_file->streams[pkt->stream_index];
377             if (cs->out_stream_index < 0) {
378                 av_packet_unref(pkt);
379                 continue;
380             }
381             pkt->stream_index = cs->out_stream_index;
382         }
383         break;
384     }
385
386     delta = av_rescale_q(cat->cur_file->start_time - cat->avf->start_time,
387                          AV_TIME_BASE_Q,
388                          cat->avf->streams[pkt->stream_index]->time_base);
389     if (pkt->pts != AV_NOPTS_VALUE)
390         pkt->pts += delta;
391     if (pkt->dts != AV_NOPTS_VALUE)
392         pkt->dts += delta;
393     return ret;
394 }
395
396 static void rescale_interval(AVRational tb_in, AVRational tb_out,
397                              int64_t *min_ts, int64_t *ts, int64_t *max_ts)
398 {
399     *ts     = av_rescale_q    (*    ts, tb_in, tb_out);
400     *min_ts = av_rescale_q_rnd(*min_ts, tb_in, tb_out,
401                                AV_ROUND_UP   | AV_ROUND_PASS_MINMAX);
402     *max_ts = av_rescale_q_rnd(*max_ts, tb_in, tb_out,
403                                AV_ROUND_DOWN | AV_ROUND_PASS_MINMAX);
404 }
405
406 static int try_seek(AVFormatContext *avf, int stream,
407                     int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
408 {
409     ConcatContext *cat = avf->priv_data;
410     int64_t t0 = cat->cur_file->start_time - cat->avf->start_time;
411
412     ts -= t0;
413     min_ts = min_ts == INT64_MIN ? INT64_MIN : min_ts - t0;
414     max_ts = max_ts == INT64_MAX ? INT64_MAX : max_ts - t0;
415     if (stream >= 0) {
416         if (stream >= cat->avf->nb_streams)
417             return AVERROR(EIO);
418         rescale_interval(AV_TIME_BASE_Q, cat->avf->streams[stream]->time_base,
419                          &min_ts, &ts, &max_ts);
420     }
421     return avformat_seek_file(cat->avf, stream, min_ts, ts, max_ts, flags);
422 }
423
424 static int real_seek(AVFormatContext *avf, int stream,
425                      int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
426 {
427     ConcatContext *cat = avf->priv_data;
428     int ret, left, right;
429
430     if (stream >= 0) {
431         if (stream >= avf->nb_streams)
432             return AVERROR(EINVAL);
433         rescale_interval(avf->streams[stream]->time_base, AV_TIME_BASE_Q,
434                          &min_ts, &ts, &max_ts);
435     }
436
437     left  = 0;
438     right = cat->nb_files;
439     while (right - left > 1) {
440         int mid = (left + right) / 2;
441         if (ts < cat->files[mid].start_time)
442             right = mid;
443         else
444             left  = mid;
445     }
446
447     if ((ret = open_file(avf, left)) < 0)
448         return ret;
449
450     ret = try_seek(avf, stream, min_ts, ts, max_ts, flags);
451     if (ret < 0 &&
452         left < cat->nb_files - 1 &&
453         cat->files[left + 1].start_time < max_ts) {
454         if ((ret = open_file(avf, left + 1)) < 0)
455             return ret;
456         ret = try_seek(avf, stream, min_ts, ts, max_ts, flags);
457     }
458     return ret;
459 }
460
461 static int concat_seek(AVFormatContext *avf, int stream,
462                        int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
463 {
464     ConcatContext *cat = avf->priv_data;
465     ConcatFile *cur_file_saved = cat->cur_file;
466     AVFormatContext *cur_avf_saved = cat->avf;
467     int ret;
468
469     if (!cat->seekable)
470         return AVERROR(ESPIPE); /* XXX: can we use it? */
471     if (flags & (AVSEEK_FLAG_BYTE | AVSEEK_FLAG_FRAME))
472         return AVERROR(ENOSYS);
473     cat->avf = NULL;
474     if ((ret = real_seek(avf, stream, min_ts, ts, max_ts, flags)) < 0) {
475         if (cat->avf)
476             avformat_close_input(&cat->avf);
477         cat->avf      = cur_avf_saved;
478         cat->cur_file = cur_file_saved;
479     } else {
480         avformat_close_input(&cur_avf_saved);
481     }
482     return ret;
483 }
484
485 #define OFFSET(x) offsetof(ConcatContext, x)
486 #define DEC AV_OPT_FLAG_DECODING_PARAM
487
488 static const AVOption options[] = {
489     { "safe", "enable safe mode",
490       OFFSET(safe), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, DEC },
491     { NULL }
492 };
493
494 static const AVClass concat_class = {
495     .class_name = "concat demuxer",
496     .item_name  = av_default_item_name,
497     .option     = options,
498     .version    = LIBAVUTIL_VERSION_INT,
499 };
500
501
502 AVInputFormat ff_concat_demuxer = {
503     .name           = "concat",
504     .long_name      = NULL_IF_CONFIG_SMALL("Virtual concatenation script"),
505     .priv_data_size = sizeof(ConcatContext),
506     .read_probe     = concat_probe,
507     .read_header    = concat_read_header,
508     .read_packet    = concat_read_packet,
509     .read_close     = concat_read_close,
510     .read_seek2     = concat_seek,
511     .priv_class     = &concat_class,
512 };