Browse code

lavf: add a concat demuxer.

Nicolas George authored on 2012/08/23 01:28:37
Showing 6 changed files
... ...
@@ -36,6 +36,7 @@ version <next>:
36 36
 - subtitles filter
37 37
 - IRCAM muxer/demuxer
38 38
 - Paris Audio File demuxer
39
+- Virtual concatenation demuxer
39 40
 
40 41
 
41 42
 version 1.0:
... ...
@@ -184,4 +184,35 @@ the script is directly played, the actual times will match the absolute
184 184
 timestamps up to the sound controller's clock accuracy, but if the user
185 185
 somehow pauses the playback or seeks, all times will be shifted accordingly.
186 186
 
187
+@section concat
188
+
189
+Virtual concatenation script demuxer.
190
+
191
+This demuxer reads a list of files and other directives from a text file and
192
+demuxes them one after the other, as if all their packet had been muxed
193
+together.
194
+
195
+The timestamps in the files are adjusted so that the first file starts at 0
196
+and each next file starts where the previous one finishes. Note that it is
197
+done globally and may cause gaps if all streams do not have exactly the same
198
+length.
199
+
200
+All files must have the same streams (same codecs, same time base, etc.).
201
+
202
+This script format can currently not be probed, it must be specified explicitly.
203
+
204
+@subsection Syntax
205
+
206
+The script is a text file in extended-ASCII, with one directive per line.
207
+Empty lines, leading spaces and lines starting with '#' are ignored. The
208
+following directive is recognized:
209
+
210
+@table @option
211
+
212
+@item @code{file @var{path}}
213
+Path to a file to read; special characters and spaces must be escaped with
214
+backslash or single quotes.
215
+
216
+@end table
217
+
187 218
 @c man end INPUT DEVICES
... ...
@@ -96,6 +96,7 @@ OBJS-$(CONFIG_CAVSVIDEO_DEMUXER)         += cavsvideodec.o rawdec.o
96 96
 OBJS-$(CONFIG_CAVSVIDEO_MUXER)           += rawenc.o
97 97
 OBJS-$(CONFIG_CDG_DEMUXER)               += cdg.o
98 98
 OBJS-$(CONFIG_CDXL_DEMUXER)              += cdxl.o
99
+OBJS-$(CONFIG_CONCAT_DEMUXER)            += concatdec.o
99 100
 OBJS-$(CONFIG_CRC_MUXER)                 += crcenc.o
100 101
 OBJS-$(CONFIG_DAUD_DEMUXER)              += daud.o
101 102
 OBJS-$(CONFIG_DAUD_MUXER)                += daud.o
... ...
@@ -85,6 +85,7 @@ void av_register_all(void)
85 85
     REGISTER_MUXDEMUX (CAVSVIDEO, cavsvideo);
86 86
     REGISTER_DEMUXER  (CDG, cdg);
87 87
     REGISTER_DEMUXER  (CDXL, cdxl);
88
+    REGISTER_DEMUXER  (CONCAT, concat);
88 89
     REGISTER_MUXER    (CRC, crc);
89 90
     REGISTER_MUXDEMUX (DAUD, daud);
90 91
     REGISTER_DEMUXER  (DFA, dfa);
91 92
new file mode 100644
... ...
@@ -0,0 +1,221 @@
0
+/*
1
+ * Copyright (c) 2012 Nicolas George
2
+ *
3
+ * This file is part of FFmpeg.
4
+ *
5
+ * FFmpeg is free software; you can redistribute it and/or
6
+ * modify it under the terms of the GNU Lesser General Public License
7
+ * as published by the Free Software Foundation; either
8
+ * version 2.1 of the License, or (at your option) any later version.
9
+ *
10
+ * FFmpeg is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+ * GNU Lesser General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Lesser General Public License
16
+ * along with FFmpeg; if not, write to the Free Software Foundation, Inc.,
17
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+ */
19
+
20
+#include "libavutil/avstring.h"
21
+#include "avformat.h"
22
+#include "internal.h"
23
+
24
+typedef struct {
25
+    char *url;
26
+    int64_t start_time;
27
+    int64_t duration;
28
+} ConcatFile;
29
+
30
+typedef struct {
31
+    ConcatFile *files;
32
+    ConcatFile *cur_file;
33
+    unsigned nb_files;
34
+    AVFormatContext *avf;
35
+} ConcatContext;
36
+
37
+static int concat_probe(AVProbeData *probe)
38
+{
39
+    return 0;
40
+}
41
+
42
+static char *get_keyword(uint8_t **cursor)
43
+{
44
+    char *ret = *cursor += strspn(*cursor, SPACE_CHARS);
45
+    *cursor += strcspn(*cursor, SPACE_CHARS);
46
+    if (**cursor) {
47
+        *((*cursor)++) = 0;
48
+        *cursor += strspn(*cursor, SPACE_CHARS);
49
+    }
50
+    return ret;
51
+}
52
+
53
+#define FAIL(retcode) do { ret = (retcode); goto fail; } while(0)
54
+
55
+static int add_file(AVFormatContext *avf, char *filename, ConcatFile **rfile,
56
+                    unsigned *nb_files_alloc)
57
+{
58
+    ConcatContext *cat = avf->priv_data;
59
+    ConcatFile *file;
60
+    char *url;
61
+    size_t url_len;
62
+
63
+    url_len = strlen(avf->filename) + strlen(filename) + 16;
64
+    if (!(url = av_malloc(url_len)))
65
+        return AVERROR(ENOMEM);
66
+    ff_make_absolute_url(url, url_len, avf->filename, filename);
67
+    av_free(filename);
68
+
69
+    if (cat->nb_files >= *nb_files_alloc) {
70
+        unsigned n = FFMAX(*nb_files_alloc * 2, 16);
71
+        if (n <= cat->nb_files ||
72
+            !(cat->files = av_realloc_f(cat->files, n, sizeof(*cat->files))))
73
+            return AVERROR(ENOMEM);
74
+        *nb_files_alloc = n;
75
+    }
76
+
77
+    file = &cat->files[cat->nb_files++];
78
+    memset(file, 0, sizeof(*file));
79
+    *rfile = file;
80
+
81
+    file->url        = url;
82
+    file->start_time = AV_NOPTS_VALUE;
83
+    file->duration   = AV_NOPTS_VALUE;
84
+
85
+    return 0;
86
+}
87
+
88
+static int open_file(AVFormatContext *avf, unsigned fileno)
89
+{
90
+    ConcatContext *cat = avf->priv_data;
91
+    ConcatFile *file = &cat->files[fileno];
92
+    int ret;
93
+
94
+    if ((ret = avformat_open_input(&cat->avf, file->url, NULL, NULL)) < 0 ||
95
+        (ret = avformat_find_stream_info(cat->avf, NULL)) < 0) {
96
+        av_log(avf, AV_LOG_ERROR, "Impossible to open '%s'\n", file->url);
97
+        return ret;
98
+    }
99
+    cat->cur_file = file;
100
+    if (file->start_time == AV_NOPTS_VALUE)
101
+        file->start_time = !fileno ? 0 :
102
+                           cat->files[fileno - 1].start_time +
103
+                           cat->files[fileno - 1].duration;
104
+    return 0;
105
+}
106
+
107
+static int concat_read_close(AVFormatContext *avf)
108
+{
109
+    ConcatContext *cat = avf->priv_data;
110
+    unsigned i;
111
+
112
+    if (cat->avf)
113
+        avformat_close_input(&cat->avf);
114
+    for (i = 0; i < cat->nb_files; i++)
115
+        av_freep(&cat->files[i].url);
116
+    av_freep(&cat->files);
117
+    return 0;
118
+}
119
+
120
+static int concat_read_header(AVFormatContext *avf)
121
+{
122
+    ConcatContext *cat = avf->priv_data;
123
+    uint8_t buf[4096];
124
+    uint8_t *cursor, *keyword;
125
+    int ret, line = 0, i;
126
+    unsigned nb_files_alloc = 0;
127
+    ConcatFile *file = NULL;
128
+    AVStream *st, *source_st;
129
+
130
+    while (1) {
131
+        if ((ret = ff_get_line(avf->pb, buf, sizeof(buf))) <= 0)
132
+            break;
133
+        line++;
134
+        cursor = buf;
135
+        keyword = get_keyword(&cursor);
136
+        if (!*keyword || *keyword == '#')
137
+            continue;
138
+
139
+        if (!strcmp(keyword, "file")) {
140
+            char *filename = av_get_token((const char **)&cursor, SPACE_CHARS);
141
+            if (!filename) {
142
+                av_log(avf, AV_LOG_ERROR, "Line %d: filename required\n", line);
143
+                FAIL(AVERROR_INVALIDDATA);
144
+            }
145
+            if ((ret = add_file(avf, filename, &file, &nb_files_alloc)) < 0)
146
+                FAIL(ret);
147
+        } else {
148
+            av_log(avf, AV_LOG_ERROR, "Line %d: unknown keyword '%s'\n",
149
+                   line, keyword);
150
+            FAIL(AVERROR_INVALIDDATA);
151
+        }
152
+    }
153
+    if (ret < 0)
154
+        FAIL(ret);
155
+
156
+    if ((ret = open_file(avf, 0)) < 0)
157
+        FAIL(ret);
158
+    for (i = 0; i < cat->avf->nb_streams; i++) {
159
+        if (!(st = avformat_new_stream(avf, NULL)))
160
+            FAIL(AVERROR(ENOMEM));
161
+        source_st = cat->avf->streams[i];
162
+        if ((ret = avcodec_copy_context(st->codec, source_st->codec)) < 0)
163
+            FAIL(ret);
164
+        st->r_frame_rate        = source_st->r_frame_rate;
165
+        st->avg_frame_rate      = source_st->avg_frame_rate;
166
+        st->time_base           = source_st->time_base;
167
+        st->sample_aspect_ratio = source_st->sample_aspect_ratio;
168
+    }
169
+
170
+    return 0;
171
+
172
+fail:
173
+    concat_read_close(avf);
174
+    return ret;
175
+}
176
+
177
+static int open_next_file(AVFormatContext *avf)
178
+{
179
+    ConcatContext *cat = avf->priv_data;
180
+    unsigned fileno = cat->cur_file - cat->files;
181
+
182
+    if (cat->cur_file->duration == AV_NOPTS_VALUE)
183
+        cat->cur_file->duration = cat->avf->duration;
184
+
185
+    if (++fileno >= cat->nb_files)
186
+        return AVERROR_EOF;
187
+    avformat_close_input(&cat->avf);
188
+    return open_file(avf, fileno);
189
+}
190
+
191
+static int concat_read_packet(AVFormatContext *avf, AVPacket *pkt)
192
+{
193
+    ConcatContext *cat = avf->priv_data;
194
+    int ret;
195
+    int64_t delta;
196
+
197
+    while (1) {
198
+        if ((ret = av_read_frame(cat->avf, pkt)) != AVERROR_EOF ||
199
+            (ret = open_next_file(avf)) < 0)
200
+            break;
201
+    }
202
+    delta = av_rescale_q(cat->cur_file->start_time - cat->avf->start_time,
203
+                         AV_TIME_BASE_Q,
204
+                         cat->avf->streams[pkt->stream_index]->time_base);
205
+    if (pkt->pts != AV_NOPTS_VALUE)
206
+        pkt->pts += delta;
207
+    if (pkt->dts != AV_NOPTS_VALUE)
208
+        pkt->dts += delta;
209
+    return ret;
210
+}
211
+
212
+AVInputFormat ff_concat_demuxer = {
213
+    .name           = "concat",
214
+    .long_name      = NULL_IF_CONFIG_SMALL("Virtual concatenation script"),
215
+    .priv_data_size = sizeof(ConcatContext),
216
+    .read_probe     = concat_probe,
217
+    .read_header    = concat_read_header,
218
+    .read_packet    = concat_read_packet,
219
+    .read_close     = concat_read_close,
220
+};
... ...
@@ -30,7 +30,7 @@
30 30
 #include "libavutil/avutil.h"
31 31
 
32 32
 #define LIBAVFORMAT_VERSION_MAJOR 54
33
-#define LIBAVFORMAT_VERSION_MINOR 46
33
+#define LIBAVFORMAT_VERSION_MINOR 47
34 34
 #define LIBAVFORMAT_VERSION_MICRO 100
35 35
 
36 36
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \