3 * Copyright (c) 2006 Steve Lhomme
4 * Copyright (c) 2007 Wolfram Gloger
5 * Copyright (c) 2010 Michele OrrĂ¹
7 * This file is part of FFmpeg.
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.
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.
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
26 #include "config_components.h"
28 #include "libavutil/avstring.h"
29 #include "libavutil/bprint.h"
30 #include "libavutil/error.h"
31 #include "libavutil/mem.h"
33 #include "avio_internal.h"
36 #define AV_CAT_SEPARATOR "|"
39 URLContext
*uc
; ///< node's URLContext
40 int64_t size
; ///< url filesize
44 struct concat_nodes
*nodes
; ///< list of nodes to concat
45 size_t length
; ///< number of cat'ed nodes
46 size_t current
; ///< index of currently read node
50 static av_cold
int concat_close(URLContext
*h
)
54 struct concat_data
*data
= h
->priv_data
;
55 struct concat_nodes
*nodes
= data
->nodes
;
57 for (i
= 0; i
!= data
->length
; i
++)
58 err
|= ffurl_closep(&nodes
[i
].uc
);
60 av_freep(&data
->nodes
);
62 return err
< 0 ? -1 : 0;
65 #if CONFIG_CONCAT_PROTOCOL
66 static av_cold
int concat_open(URLContext
*h
, const char *uri
, int flags
)
68 char *node_uri
= NULL
;
70 int64_t size
, total_size
= 0;
73 struct concat_data
*data
= h
->priv_data
;
74 struct concat_nodes
*nodes
;
76 if (!av_strstart(uri
, "concat:", &uri
)) {
77 av_log(h
, AV_LOG_ERROR
, "URL %s lacks prefix\n", uri
);
78 return AVERROR(EINVAL
);
81 for (i
= 0, len
= 1; uri
[i
]; i
++) {
82 if (uri
[i
] == *AV_CAT_SEPARATOR
) {
87 if (!(nodes
= av_realloc_array(NULL
, len
, sizeof(*nodes
))))
88 return AVERROR(ENOMEM
);
94 err
= AVERROR(ENOENT
);
95 for (i
= 0; *uri
; i
++) {
97 len
= strcspn(uri
, AV_CAT_SEPARATOR
);
98 if ((err
= av_reallocp(&node_uri
, len
+ 1)) < 0)
100 av_strlcpy(node_uri
, uri
, len
+ 1);
101 uri
+= len
+ strspn(uri
+ len
, AV_CAT_SEPARATOR
);
103 /* creating URLContext */
104 err
= ffurl_open_whitelist(&uc
, node_uri
, flags
,
105 &h
->interrupt_callback
, NULL
, h
->protocol_whitelist
, h
->protocol_blacklist
, h
);
110 if ((size
= ffurl_size(uc
)) < 0) {
112 err
= AVERROR(ENOSYS
);
118 nodes
[i
].size
= size
;
126 else if (!(nodes
= av_realloc(nodes
, data
->length
* sizeof(*nodes
)))) {
128 err
= AVERROR(ENOMEM
);
131 data
->total_size
= total_size
;
136 static int concat_read(URLContext
*h
, unsigned char *buf
, int size
)
138 int result
, total
= 0;
139 struct concat_data
*data
= h
->priv_data
;
140 struct concat_nodes
*nodes
= data
->nodes
;
141 size_t i
= data
->current
;
144 result
= ffurl_read(nodes
[i
].uc
, buf
, size
);
145 if (result
== AVERROR_EOF
) {
146 if (i
+ 1 == data
->length
||
147 ffurl_seek(nodes
[++i
].uc
, 0, SEEK_SET
) < 0)
152 return total
? total
: result
;
158 return total
? total
: result
;
161 static int64_t concat_seek(URLContext
*h
, int64_t pos
, int whence
)
164 struct concat_data
*data
= h
->priv_data
;
165 struct concat_nodes
*nodes
= data
->nodes
;
168 if ((whence
& AVSEEK_SIZE
))
169 return data
->total_size
;
172 for (i
= data
->length
- 1; i
&& pos
< -nodes
[i
].size
; i
--)
173 pos
+= nodes
[i
].size
;
176 /* get the absolute position */
177 for (i
= 0; i
!= data
->current
; i
++)
178 pos
+= nodes
[i
].size
;
179 pos
+= ffurl_seek(nodes
[i
].uc
, 0, SEEK_CUR
);
181 /* fall through with the absolute position */
183 for (i
= 0; i
!= data
->length
- 1 && pos
>= nodes
[i
].size
; i
++)
184 pos
-= nodes
[i
].size
;
187 return AVERROR(EINVAL
);
190 result
= ffurl_seek(nodes
[i
].uc
, pos
, whence
);
194 result
+= nodes
[--i
].size
;
199 #if CONFIG_CONCAT_PROTOCOL
200 const URLProtocol ff_concat_protocol
= {
202 .url_open
= concat_open
,
203 .url_read
= concat_read
,
204 .url_seek
= concat_seek
,
205 .url_close
= concat_close
,
206 .priv_data_size
= sizeof(struct concat_data
),
207 .default_whitelist
= "concat,file,subfile",
211 #if CONFIG_CONCATF_PROTOCOL
212 static av_cold
int concatf_open(URLContext
*h
, const char *uri
, int flags
)
215 struct concat_data
*data
= h
->priv_data
;
216 AVIOContext
*in
= NULL
;
218 int64_t total_size
= 0;
219 unsigned int nodes_size
= 0;
223 if (!av_strstart(uri
, "concatf:", &uri
)) {
224 av_log(h
, AV_LOG_ERROR
, "URL %s lacks prefix\n", uri
);
225 return AVERROR(EINVAL
);
230 return AVERROR(ENOENT
);
232 err
= ffio_open_whitelist(&in
, uri
, AVIO_FLAG_READ
, &h
->interrupt_callback
,
233 NULL
, h
->protocol_whitelist
, h
->protocol_blacklist
);
237 av_bprint_init(&bp
, 0, AV_BPRINT_SIZE_UNLIMITED
);
238 err
= avio_read_to_bprint(in
, &bp
, SIZE_MAX
);
241 av_bprint_finalize(&bp
, NULL
);
247 struct concat_nodes
*nodes
;
252 int leading_spaces
= strspn(cursor
, " \n\t\r");
254 if (!cursor
[leading_spaces
])
257 node_uri
= av_get_token(&cursor
, "\r\n");
259 err
= AVERROR(ENOMEM
);
265 if (++len
== SIZE_MAX
/ sizeof(*nodes
)) {
267 err
= AVERROR(ENAMETOOLONG
);
271 /* creating URLContext */
272 err
= ffurl_open_whitelist(&uc
, node_uri
, flags
,
273 &h
->interrupt_callback
, NULL
, h
->protocol_whitelist
, h
->protocol_blacklist
, h
);
279 if ((size
= ffurl_size(uc
)) < 0) {
281 err
= AVERROR(ENOSYS
);
285 nodes
= av_fast_realloc(data
->nodes
, &nodes_size
, sizeof(*nodes
) * len
);
288 err
= AVERROR(ENOMEM
);
294 data
->nodes
[i
].uc
= uc
;
295 data
->nodes
[i
++].size
= size
;
298 av_bprint_finalize(&bp
, NULL
);
302 err
= AVERROR_INVALIDDATA
;
306 data
->total_size
= total_size
;
310 const URLProtocol ff_concatf_protocol
= {
312 .url_open
= concatf_open
,
313 .url_read
= concat_read
,
314 .url_seek
= concat_seek
,
315 .url_close
= concat_close
,
316 .priv_data_size
= sizeof(struct concat_data
),
317 .default_whitelist
= "concatf,concat,file,subfile",