• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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