]> git.sesse.net Git - ffmpeg/blob - libavformat/concatdec.c
Merge commit '252c0bfdc014c1fb6ad4fe06242c7beca58a6b41'
[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
27 typedef struct {
28     char *url;
29     int64_t start_time;
30     int64_t duration;
31 } ConcatFile;
32
33 typedef struct {
34     AVClass *class;
35     ConcatFile *files;
36     ConcatFile *cur_file;
37     unsigned nb_files;
38     AVFormatContext *avf;
39     int safe;
40     int seekable;
41 } ConcatContext;
42
43 static int concat_probe(AVProbeData *probe)
44 {
45     return memcmp(probe->buf, "ffconcat version 1.0", 20) ?
46            0 : AVPROBE_SCORE_MAX;
47 }
48
49 static char *get_keyword(uint8_t **cursor)
50 {
51     char *ret = *cursor += strspn(*cursor, SPACE_CHARS);
52     *cursor += strcspn(*cursor, SPACE_CHARS);
53     if (**cursor) {
54         *((*cursor)++) = 0;
55         *cursor += strspn(*cursor, SPACE_CHARS);
56     }
57     return ret;
58 }
59
60 static int safe_filename(const char *f)
61 {
62     const char *start = f;
63
64     for (; *f; f++) {
65         /* A-Za-z0-9_- */
66         if (!((unsigned)((*f | 32) - 'a') < 26 ||
67               (unsigned)(*f - '0') < 10 || *f == '_' || *f == '-')) {
68             if (f == start)
69                 return 0;
70             else if (*f == '/')
71                 start = f + 1;
72             else if (*f != '.')
73                 return 0;
74         }
75     }
76     return 1;
77 }
78
79 #define FAIL(retcode) do { ret = (retcode); goto fail; } while(0)
80
81 static int add_file(AVFormatContext *avf, char *filename, ConcatFile **rfile,
82                     unsigned *nb_files_alloc)
83 {
84     ConcatContext *cat = avf->priv_data;
85     ConcatFile *file;
86     char *url = NULL;
87     size_t url_len;
88     int ret;
89
90     if (cat->safe > 0 && !safe_filename(filename)) {
91         av_log(avf, AV_LOG_ERROR, "Unsafe file name '%s'\n", filename);
92         FAIL(AVERROR(EPERM));
93     }
94     url_len = strlen(avf->filename) + strlen(filename) + 16;
95     if (!(url = av_malloc(url_len)))
96         FAIL(AVERROR(ENOMEM));
97     ff_make_absolute_url(url, url_len, avf->filename, filename);
98     av_freep(&filename);
99
100     if (cat->nb_files >= *nb_files_alloc) {
101         size_t n = FFMAX(*nb_files_alloc * 2, 16);
102         ConcatFile *new_files;
103         if (n <= cat->nb_files || n > SIZE_MAX / sizeof(*cat->files) ||
104             !(new_files = av_realloc(cat->files, n * sizeof(*cat->files))))
105             FAIL(AVERROR(ENOMEM));
106         cat->files = new_files;
107         *nb_files_alloc = n;
108     }
109
110     file = &cat->files[cat->nb_files++];
111     memset(file, 0, sizeof(*file));
112     *rfile = file;
113
114     file->url        = url;
115     file->start_time = AV_NOPTS_VALUE;
116     file->duration   = AV_NOPTS_VALUE;
117
118     return 0;
119
120 fail:
121     av_free(url);
122     av_free(filename);
123     return ret;
124 }
125
126 static int open_file(AVFormatContext *avf, unsigned fileno)
127 {
128     ConcatContext *cat = avf->priv_data;
129     ConcatFile *file = &cat->files[fileno];
130     int ret;
131
132     if (cat->avf)
133         avformat_close_input(&cat->avf);
134     if ((ret = avformat_open_input(&cat->avf, file->url, NULL, NULL)) < 0 ||
135         (ret = avformat_find_stream_info(cat->avf, NULL)) < 0) {
136         av_log(avf, AV_LOG_ERROR, "Impossible to open '%s'\n", file->url);
137         return ret;
138     }
139     cat->cur_file = file;
140     if (file->start_time == AV_NOPTS_VALUE)
141         file->start_time = !fileno ? 0 :
142                            cat->files[fileno - 1].start_time +
143                            cat->files[fileno - 1].duration;
144     return 0;
145 }
146
147 static int concat_read_close(AVFormatContext *avf)
148 {
149     ConcatContext *cat = avf->priv_data;
150     unsigned i;
151
152     if (cat->avf)
153         avformat_close_input(&cat->avf);
154     for (i = 0; i < cat->nb_files; i++)
155         av_freep(&cat->files[i].url);
156     av_freep(&cat->files);
157     return 0;
158 }
159
160 static int concat_read_header(AVFormatContext *avf)
161 {
162     ConcatContext *cat = avf->priv_data;
163     uint8_t buf[4096];
164     uint8_t *cursor, *keyword;
165     int ret, line = 0, i;
166     unsigned nb_files_alloc = 0;
167     ConcatFile *file = NULL;
168     AVStream *st, *source_st;
169     int64_t time = 0;
170
171     while (1) {
172         if ((ret = ff_get_line(avf->pb, buf, sizeof(buf))) <= 0)
173             break;
174         line++;
175         cursor = buf;
176         keyword = get_keyword(&cursor);
177         if (!*keyword || *keyword == '#')
178             continue;
179
180         if (!strcmp(keyword, "file")) {
181             char *filename = av_get_token((const char **)&cursor, SPACE_CHARS);
182             if (!filename) {
183                 av_log(avf, AV_LOG_ERROR, "Line %d: filename required\n", line);
184                 FAIL(AVERROR_INVALIDDATA);
185             }
186             if ((ret = add_file(avf, filename, &file, &nb_files_alloc)) < 0)
187                 FAIL(ret);
188         } else if (!strcmp(keyword, "duration")) {
189             char *dur_str = get_keyword(&cursor);
190             int64_t dur;
191             if (!file) {
192                 av_log(avf, AV_LOG_ERROR, "Line %d: duration without file\n",
193                        line);
194                 FAIL(AVERROR_INVALIDDATA);
195             }
196             if ((ret = av_parse_time(&dur, dur_str, 1)) < 0) {
197                 av_log(avf, AV_LOG_ERROR, "Line %d: invalid duration '%s'\n",
198                        line, dur_str);
199                 FAIL(ret);
200             }
201             file->duration = dur;
202         } else if (!strcmp(keyword, "ffconcat")) {
203             char *ver_kw  = get_keyword(&cursor);
204             char *ver_val = get_keyword(&cursor);
205             if (strcmp(ver_kw, "version") || strcmp(ver_val, "1.0")) {
206                 av_log(avf, AV_LOG_ERROR, "Line %d: invalid version\n", line);
207                 FAIL(AVERROR_INVALIDDATA);
208             }
209             if (cat->safe < 0)
210                 cat->safe = 1;
211         } else {
212             av_log(avf, AV_LOG_ERROR, "Line %d: unknown keyword '%s'\n",
213                    line, keyword);
214             FAIL(AVERROR_INVALIDDATA);
215         }
216     }
217     if (ret < 0)
218         FAIL(ret);
219
220     for (i = 0; i < cat->nb_files; i++) {
221         if (cat->files[i].start_time == AV_NOPTS_VALUE)
222             cat->files[i].start_time = time;
223         else
224             time = cat->files[i].start_time;
225         if (cat->files[i].duration == AV_NOPTS_VALUE)
226             break;
227         time += cat->files[i].duration;
228     }
229     if (i == cat->nb_files) {
230         avf->duration = time;
231         cat->seekable = 1;
232     }
233
234     if ((ret = open_file(avf, 0)) < 0)
235         FAIL(ret);
236     for (i = 0; i < cat->avf->nb_streams; i++) {
237         if (!(st = avformat_new_stream(avf, NULL)))
238             FAIL(AVERROR(ENOMEM));
239         source_st = cat->avf->streams[i];
240         if ((ret = avcodec_copy_context(st->codec, source_st->codec)) < 0)
241             FAIL(ret);
242         st->r_frame_rate        = source_st->r_frame_rate;
243         st->avg_frame_rate      = source_st->avg_frame_rate;
244         st->time_base           = source_st->time_base;
245         st->sample_aspect_ratio = source_st->sample_aspect_ratio;
246     }
247
248     return 0;
249
250 fail:
251     concat_read_close(avf);
252     return ret;
253 }
254
255 static int open_next_file(AVFormatContext *avf)
256 {
257     ConcatContext *cat = avf->priv_data;
258     unsigned fileno = cat->cur_file - cat->files;
259
260     if (cat->cur_file->duration == AV_NOPTS_VALUE)
261         cat->cur_file->duration = cat->avf->duration;
262
263     if (++fileno >= cat->nb_files)
264         return AVERROR_EOF;
265     return open_file(avf, fileno);
266 }
267
268 static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt)
269 {
270     ConcatContext *cat = avf->priv_data;
271     int ret;
272     int64_t delta;
273
274     while (1) {
275         if ((ret = av_read_frame(cat->avf, pkt)) != AVERROR_EOF ||
276             (ret = open_next_file(avf)) < 0)
277             break;
278     }
279     delta = av_rescale_q(cat->cur_file->start_time - cat->avf->start_time,
280                          AV_TIME_BASE_Q,
281                          cat->avf->streams[pkt->stream_index]->time_base);
282     if (pkt->pts != AV_NOPTS_VALUE)
283         pkt->pts += delta;
284     if (pkt->dts != AV_NOPTS_VALUE)
285         pkt->dts += delta;
286     return ret;
287 }
288
289 static void rescale_interval(AVRational tb_in, AVRational tb_out,
290                              int64_t *min_ts, int64_t *ts, int64_t *max_ts)
291 {
292     *ts     = av_rescale_q    (*    ts, tb_in, tb_out);
293     *min_ts = av_rescale_q_rnd(*min_ts, tb_in, tb_out,
294                                AV_ROUND_UP   | AV_ROUND_PASS_MINMAX);
295     *max_ts = av_rescale_q_rnd(*max_ts, tb_in, tb_out,
296                                AV_ROUND_DOWN | AV_ROUND_PASS_MINMAX);
297 }
298
299 static int try_seek(AVFormatContext *avf, int stream,
300                     int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
301 {
302     ConcatContext *cat = avf->priv_data;
303     int64_t t0 = cat->cur_file->start_time - cat->avf->start_time;
304
305     ts -= t0;
306     min_ts = min_ts == INT64_MIN ? INT64_MIN : min_ts - t0;
307     max_ts = max_ts == INT64_MAX ? INT64_MAX : max_ts - t0;
308     if (stream >= 0) {
309         if (stream >= cat->avf->nb_streams)
310             return AVERROR(EIO);
311         rescale_interval(AV_TIME_BASE_Q, cat->avf->streams[stream]->time_base,
312                          &min_ts, &ts, &max_ts);
313     }
314     return avformat_seek_file(cat->avf, stream, min_ts, ts, max_ts, flags);
315 }
316
317 static int real_seek(AVFormatContext *avf, int stream,
318                      int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
319 {
320     ConcatContext *cat = avf->priv_data;
321     int ret, left, right;
322
323     if (stream >= 0) {
324         if (stream >= avf->nb_streams)
325             return AVERROR(EINVAL);
326         rescale_interval(avf->streams[stream]->time_base, AV_TIME_BASE_Q,
327                          &min_ts, &ts, &max_ts);
328     }
329
330     left  = 0;
331     right = cat->nb_files;
332     while (right - left > 1) {
333         int mid = (left + right) / 2;
334         if (ts < cat->files[mid].start_time)
335             right = mid;
336         else
337             left  = mid;
338     }
339
340     if ((ret = open_file(avf, left)) < 0)
341         return ret;
342
343     ret = try_seek(avf, stream, min_ts, ts, max_ts, flags);
344     if (ret < 0 &&
345         left < cat->nb_files - 1 &&
346         cat->files[left + 1].start_time < max_ts) {
347         if ((ret = open_file(avf, left + 1)) < 0)
348             return ret;
349         ret = try_seek(avf, stream, min_ts, ts, max_ts, flags);
350     }
351     return ret;
352 }
353
354 static int concat_seek(AVFormatContext *avf, int stream,
355                        int64_t min_ts, int64_t ts, int64_t max_ts, int flags)
356 {
357     ConcatContext *cat = avf->priv_data;
358     ConcatFile *cur_file_saved = cat->cur_file;
359     AVFormatContext *cur_avf_saved = cat->avf;
360     int ret;
361
362     if (!cat->seekable)
363         return AVERROR(ESPIPE); /* XXX: can we use it? */
364     if (flags & (AVSEEK_FLAG_BYTE | AVSEEK_FLAG_FRAME))
365         return AVERROR(ENOSYS);
366     cat->avf = NULL;
367     if ((ret = real_seek(avf, stream, min_ts, ts, max_ts, flags)) < 0) {
368         if (cat->avf)
369             avformat_close_input(&cat->avf);
370         cat->avf      = cur_avf_saved;
371         cat->cur_file = cur_file_saved;
372     } else {
373         avformat_close_input(&cur_avf_saved);
374     }
375     return ret;
376 }
377
378 #define OFFSET(x) offsetof(ConcatContext, x)
379 #define DEC AV_OPT_FLAG_DECODING_PARAM
380
381 static const AVOption options[] = {
382     { "safe", "enable safe mode",
383       OFFSET(safe), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, DEC },
384     { NULL }
385 };
386
387 static const AVClass concat_class = {
388     .class_name = "concat demuxer",
389     .item_name  = av_default_item_name,
390     .option     = options,
391     .version    = LIBAVUTIL_VERSION_INT,
392 };
393
394
395 AVInputFormat ff_concat_demuxer = {
396     .name           = "concat",
397     .long_name      = NULL_IF_CONFIG_SMALL("Virtual concatenation script"),
398     .priv_data_size = sizeof(ConcatContext),
399     .read_probe     = concat_probe,
400     .read_header    = concat_read_header,
401     .read_packet    = concat_read_packet,
402     .read_close     = concat_read_close,
403     .read_seek2     = concat_seek,
404     .priv_class     = &concat_class,
405 };