1 /*
2 * Concat URL protocol
3 * Copyright (c) 2006 Steve Lhomme
4 * Copyright (c) 2007 Wolfram Gloger
5 * Copyright (c) 2010 Michele Orrù
6 *
7 * This file is part of FFmpeg.
8 *
9 * FFmpeg is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * FFmpeg is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with FFmpeg; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #include "config_components.h"
25
26 #include "libavutil/avstring.h"
27 #include "libavutil/bprint.h"
28 #include "libavutil/mem.h"
29
30 #include "avformat.h"
31 #include "avio_internal.h"
32 #include "url.h"
33
34 #define AV_CAT_SEPARATOR "|"
35
36 struct concat_nodes {
37 URLContext *uc; ///< node's URLContext
38 int64_t size; ///< url filesize
39 };
40
41 struct concat_data {
42 struct concat_nodes *nodes; ///< list of nodes to concat
43 size_t length; ///< number of cat'ed nodes
44 size_t current; ///< index of currently read node
45 uint64_t total_size;
46 };
47
concat_close(URLContext * h)48 static av_cold int concat_close(URLContext *h)
49 {
50 int err = 0;
51 size_t i;
52 struct concat_data *data = h->priv_data;
53 struct concat_nodes *nodes = data->nodes;
54
55 for (i = 0; i != data->length; i++)
56 err |= ffurl_closep(&nodes[i].uc);
57
58 av_freep(&data->nodes);
59
60 return err < 0 ? -1 : 0;
61 }
62
63 #if CONFIG_CONCAT_PROTOCOL
concat_open(URLContext * h,const char * uri,int flags)64 static av_cold int concat_open(URLContext *h, const char *uri, int flags)
65 {
66 char *node_uri = NULL;
67 int err = 0;
68 int64_t size, total_size = 0;
69 size_t len, i;
70 URLContext *uc;
71 struct concat_data *data = h->priv_data;
72 struct concat_nodes *nodes;
73
74 if (!av_strstart(uri, "concat:", &uri)) {
75 av_log(h, AV_LOG_ERROR, "URL %s lacks prefix\n", uri);
76 return AVERROR(EINVAL);
77 }
78
79 for (i = 0, len = 1; uri[i]; i++) {
80 if (uri[i] == *AV_CAT_SEPARATOR) {
81 len++;
82 }
83 }
84
85 if (!(nodes = av_realloc_array(NULL, len, sizeof(*nodes))))
86 return AVERROR(ENOMEM);
87 else
88 data->nodes = nodes;
89
90 /* handle input */
91 if (!*uri)
92 err = AVERROR(ENOENT);
93 for (i = 0; *uri; i++) {
94 /* parsing uri */
95 len = strcspn(uri, AV_CAT_SEPARATOR);
96 if ((err = av_reallocp(&node_uri, len + 1)) < 0)
97 break;
98 av_strlcpy(node_uri, uri, len + 1);
99 uri += len + strspn(uri + len, AV_CAT_SEPARATOR);
100
101 /* creating URLContext */
102 err = ffurl_open_whitelist(&uc, node_uri, flags,
103 &h->interrupt_callback, NULL, h->protocol_whitelist, h->protocol_blacklist, h);
104 if (err < 0)
105 break;
106
107 /* creating size */
108 if ((size = ffurl_size(uc)) < 0) {
109 ffurl_close(uc);
110 err = AVERROR(ENOSYS);
111 break;
112 }
113
114 /* assembling */
115 nodes[i].uc = uc;
116 nodes[i].size = size;
117 total_size += size;
118 }
119 av_free(node_uri);
120 data->length = i;
121
122 if (err < 0)
123 concat_close(h);
124 else if (!(nodes = av_realloc(nodes, data->length * sizeof(*nodes)))) {
125 concat_close(h);
126 err = AVERROR(ENOMEM);
127 } else
128 data->nodes = nodes;
129 data->total_size = total_size;
130 return err;
131 }
132 #endif
133
concat_read(URLContext * h,unsigned char * buf,int size)134 static int concat_read(URLContext *h, unsigned char *buf, int size)
135 {
136 int result, total = 0;
137 struct concat_data *data = h->priv_data;
138 struct concat_nodes *nodes = data->nodes;
139 size_t i = data->current;
140
141 while (size > 0) {
142 result = ffurl_read(nodes[i].uc, buf, size);
143 if (result == AVERROR_EOF) {
144 if (i + 1 == data->length ||
145 ffurl_seek(nodes[++i].uc, 0, SEEK_SET) < 0)
146 break;
147 result = 0;
148 }
149 if (result < 0)
150 return total ? total : result;
151 total += result;
152 buf += result;
153 size -= result;
154 }
155 data->current = i;
156 return total ? total : result;
157 }
158
concat_seek(URLContext * h,int64_t pos,int whence)159 static int64_t concat_seek(URLContext *h, int64_t pos, int whence)
160 {
161 int64_t result;
162 struct concat_data *data = h->priv_data;
163 struct concat_nodes *nodes = data->nodes;
164 size_t i;
165
166 if ((whence & AVSEEK_SIZE))
167 return data->total_size;
168 switch (whence) {
169 case SEEK_END:
170 for (i = data->length - 1; i && pos < -nodes[i].size; i--)
171 pos += nodes[i].size;
172 break;
173 case SEEK_CUR:
174 /* get the absolute position */
175 for (i = 0; i != data->current; i++)
176 pos += nodes[i].size;
177 pos += ffurl_seek(nodes[i].uc, 0, SEEK_CUR);
178 whence = SEEK_SET;
179 /* fall through with the absolute position */
180 case SEEK_SET:
181 for (i = 0; i != data->length - 1 && pos >= nodes[i].size; i++)
182 pos -= nodes[i].size;
183 break;
184 default:
185 return AVERROR(EINVAL);
186 }
187
188 result = ffurl_seek(nodes[i].uc, pos, whence);
189 if (result >= 0) {
190 data->current = i;
191 while (i)
192 result += nodes[--i].size;
193 }
194 return result;
195 }
196
197 #if CONFIG_CONCAT_PROTOCOL
198 const URLProtocol ff_concat_protocol = {
199 .name = "concat",
200 .url_open = concat_open,
201 .url_read = concat_read,
202 .url_seek = concat_seek,
203 .url_close = concat_close,
204 .priv_data_size = sizeof(struct concat_data),
205 .default_whitelist = "concat,file,subfile",
206 };
207 #endif
208
209 #if CONFIG_CONCATF_PROTOCOL
concatf_open(URLContext * h,const char * uri,int flags)210 static av_cold int concatf_open(URLContext *h, const char *uri, int flags)
211 {
212 AVBPrint bp;
213 struct concat_data *data = h->priv_data;
214 AVIOContext *in = NULL;
215 const char *cursor;
216 int64_t total_size = 0;
217 unsigned int nodes_size = 0;
218 size_t i = 0;
219 int err;
220
221 if (!av_strstart(uri, "concatf:", &uri)) {
222 av_log(h, AV_LOG_ERROR, "URL %s lacks prefix\n", uri);
223 return AVERROR(EINVAL);
224 }
225
226 /* handle input */
227 if (!*uri)
228 return AVERROR(ENOENT);
229
230 err = ffio_open_whitelist(&in, uri, AVIO_FLAG_READ, &h->interrupt_callback,
231 NULL, h->protocol_whitelist, h->protocol_blacklist);
232 if (err < 0)
233 return err;
234
235 av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
236 err = avio_read_to_bprint(in, &bp, SIZE_MAX);
237 avio_closep(&in);
238 if (err < 0) {
239 av_bprint_finalize(&bp, NULL);
240 return err;
241 }
242
243 cursor = bp.str;
244 while (*cursor) {
245 struct concat_nodes *nodes;
246 URLContext *uc;
247 char *node_uri;
248 int64_t size;
249 size_t len = i;
250 int leading_spaces = strspn(cursor, " \n\t\r");
251
252 if (!cursor[leading_spaces])
253 break;
254
255 node_uri = av_get_token(&cursor, "\r\n");
256 if (!node_uri) {
257 err = AVERROR(ENOMEM);
258 break;
259 }
260 if (*cursor)
261 cursor++;
262
263 if (++len == SIZE_MAX / sizeof(*nodes)) {
264 av_free(node_uri);
265 err = AVERROR(ENAMETOOLONG);
266 break;
267 }
268
269 /* creating URLContext */
270 err = ffurl_open_whitelist(&uc, node_uri, flags,
271 &h->interrupt_callback, NULL, h->protocol_whitelist, h->protocol_blacklist, h);
272 av_free(node_uri);
273 if (err < 0)
274 break;
275
276 /* creating size */
277 if ((size = ffurl_size(uc)) < 0) {
278 ffurl_close(uc);
279 err = AVERROR(ENOSYS);
280 break;
281 }
282
283 nodes = av_fast_realloc(data->nodes, &nodes_size, sizeof(*nodes) * len);
284 if (!nodes) {
285 ffurl_close(uc);
286 err = AVERROR(ENOMEM);
287 break;
288 }
289 data->nodes = nodes;
290
291 /* assembling */
292 data->nodes[i].uc = uc;
293 data->nodes[i++].size = size;
294 total_size += size;
295 }
296 av_bprint_finalize(&bp, NULL);
297 data->length = i;
298
299 if (!data->length)
300 err = AVERROR_INVALIDDATA;
301 if (err < 0)
302 concat_close(h);
303
304 data->total_size = total_size;
305 return err;
306 }
307
308 const URLProtocol ff_concatf_protocol = {
309 .name = "concatf",
310 .url_open = concatf_open,
311 .url_read = concat_read,
312 .url_seek = concat_seek,
313 .url_close = concat_close,
314 .priv_data_size = sizeof(struct concat_data),
315 .default_whitelist = "concatf,concat,file,subfile",
316 };
317 #endif
318