FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/concat.c
Date: 2024-11-20 23:03:26
Exec Total Coverage
Lines: 61 158 38.6%
Functions: 4 5 80.0%
Branches: 28 88 31.8%

Line Branch Exec Source
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 <string.h>
25
26 #include "config_components.h"
27
28 #include "libavutil/avstring.h"
29 #include "libavutil/bprint.h"
30 #include "libavutil/error.h"
31 #include "libavutil/mem.h"
32
33 #include "avio_internal.h"
34 #include "url.h"
35
36 #define AV_CAT_SEPARATOR "|"
37
38 struct concat_nodes {
39 URLContext *uc; ///< node's URLContext
40 int64_t size; ///< url filesize
41 };
42
43 struct concat_data {
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
47 uint64_t total_size;
48 };
49
50 1 static av_cold int concat_close(URLContext *h)
51 {
52 1 int err = 0;
53 size_t i;
54 1 struct concat_data *data = h->priv_data;
55 1 struct concat_nodes *nodes = data->nodes;
56
57
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 for (i = 0; i != data->length; i++)
58 4 err |= ffurl_closep(&nodes[i].uc);
59
60 1 av_freep(&data->nodes);
61
62
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 return err < 0 ? -1 : 0;
63 }
64
65 #if CONFIG_CONCAT_PROTOCOL
66 1 static av_cold int concat_open(URLContext *h, const char *uri, int flags)
67 {
68 1 char *node_uri = NULL;
69 1 int err = 0;
70 1 int64_t size, total_size = 0;
71 size_t len, i;
72 URLContext *uc;
73 1 struct concat_data *data = h->priv_data;
74 struct concat_nodes *nodes;
75
76
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (!av_strstart(uri, "concat:", &uri)) {
77 av_log(h, AV_LOG_ERROR, "URL %s lacks prefix\n", uri);
78 return AVERROR(EINVAL);
79 }
80
81
2/2
✓ Branch 0 taken 223 times.
✓ Branch 1 taken 1 times.
224 for (i = 0, len = 1; uri[i]; i++) {
82
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 220 times.
223 if (uri[i] == *AV_CAT_SEPARATOR) {
83 3 len++;
84 }
85 }
86
87
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (!(nodes = av_realloc_array(NULL, len, sizeof(*nodes))))
88 return AVERROR(ENOMEM);
89 else
90 1 data->nodes = nodes;
91
92 /* handle input */
93
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!*uri)
94 err = AVERROR(ENOENT);
95
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 for (i = 0; *uri; i++) {
96 /* parsing uri */
97 4 len = strcspn(uri, AV_CAT_SEPARATOR);
98
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if ((err = av_reallocp(&node_uri, len + 1)) < 0)
99 break;
100 4 av_strlcpy(node_uri, uri, len + 1);
101 4 uri += len + strspn(uri + len, AV_CAT_SEPARATOR);
102
103 /* creating URLContext */
104 4 err = ffurl_open_whitelist(&uc, node_uri, flags,
105 4 &h->interrupt_callback, NULL, h->protocol_whitelist, h->protocol_blacklist, h);
106
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (err < 0)
107 break;
108
109 /* creating size */
110
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if ((size = ffurl_size(uc)) < 0) {
111 ffurl_close(uc);
112 err = AVERROR(ENOSYS);
113 break;
114 }
115
116 /* assembling */
117 4 nodes[i].uc = uc;
118 4 nodes[i].size = size;
119 4 total_size += size;
120 }
121 1 av_free(node_uri);
122 1 data->length = i;
123
124
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (err < 0)
125 concat_close(h);
126
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 else if (!(nodes = av_realloc(nodes, data->length * sizeof(*nodes)))) {
127 concat_close(h);
128 err = AVERROR(ENOMEM);
129 } else
130 1 data->nodes = nodes;
131 1 data->total_size = total_size;
132 1 return err;
133 }
134 #endif
135
136 33 static int concat_read(URLContext *h, unsigned char *buf, int size)
137 {
138 33 int result, total = 0;
139 33 struct concat_data *data = h->priv_data;
140 33 struct concat_nodes *nodes = data->nodes;
141 33 size_t i = data->current;
142
143
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 29 times.
69 while (size > 0) {
144 40 result = ffurl_read(nodes[i].uc, buf, size);
145
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 33 times.
40 if (result == AVERROR_EOF) {
146
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
10 if (i + 1 == data->length ||
147 3 ffurl_seek(nodes[++i].uc, 0, SEEK_SET) < 0)
148 break;
149 3 result = 0;
150 }
151
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
36 if (result < 0)
152 return total ? total : result;
153 36 total += result;
154 36 buf += result;
155 36 size -= result;
156 }
157 33 data->current = i;
158
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 3 times.
33 return total ? total : result;
159 }
160
161 2 static int64_t concat_seek(URLContext *h, int64_t pos, int whence)
162 {
163 int64_t result;
164 2 struct concat_data *data = h->priv_data;
165 2 struct concat_nodes *nodes = data->nodes;
166 size_t i;
167
168
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if ((whence & AVSEEK_SIZE))
169 2 return data->total_size;
170 switch (whence) {
171 case SEEK_END:
172 for (i = data->length - 1; i && pos < -nodes[i].size; i--)
173 pos += nodes[i].size;
174 break;
175 case SEEK_CUR:
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);
180 whence = SEEK_SET;
181 /* fall through with the absolute position */
182 case SEEK_SET:
183 for (i = 0; i != data->length - 1 && pos >= nodes[i].size; i++)
184 pos -= nodes[i].size;
185 break;
186 default:
187 return AVERROR(EINVAL);
188 }
189
190 result = ffurl_seek(nodes[i].uc, pos, whence);
191 if (result >= 0) {
192 data->current = i;
193 while (i)
194 result += nodes[--i].size;
195 }
196 return result;
197 }
198
199 #if CONFIG_CONCAT_PROTOCOL
200 const URLProtocol ff_concat_protocol = {
201 .name = "concat",
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",
208 };
209 #endif
210
211 #if CONFIG_CONCATF_PROTOCOL
212 static av_cold int concatf_open(URLContext *h, const char *uri, int flags)
213 {
214 AVBPrint bp;
215 struct concat_data *data = h->priv_data;
216 AVIOContext *in = NULL;
217 const char *cursor;
218 int64_t total_size = 0;
219 unsigned int nodes_size = 0;
220 size_t i = 0;
221 int err;
222
223 if (!av_strstart(uri, "concatf:", &uri)) {
224 av_log(h, AV_LOG_ERROR, "URL %s lacks prefix\n", uri);
225 return AVERROR(EINVAL);
226 }
227
228 /* handle input */
229 if (!*uri)
230 return AVERROR(ENOENT);
231
232 err = ffio_open_whitelist(&in, uri, AVIO_FLAG_READ, &h->interrupt_callback,
233 NULL, h->protocol_whitelist, h->protocol_blacklist);
234 if (err < 0)
235 return err;
236
237 av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
238 err = avio_read_to_bprint(in, &bp, SIZE_MAX);
239 avio_closep(&in);
240 if (err < 0) {
241 av_bprint_finalize(&bp, NULL);
242 return err;
243 }
244
245 cursor = bp.str;
246 while (*cursor) {
247 struct concat_nodes *nodes;
248 URLContext *uc;
249 char *node_uri;
250 int64_t size;
251 size_t len = i;
252 int leading_spaces = strspn(cursor, " \n\t\r");
253
254 if (!cursor[leading_spaces])
255 break;
256
257 node_uri = av_get_token(&cursor, "\r\n");
258 if (!node_uri) {
259 err = AVERROR(ENOMEM);
260 break;
261 }
262 if (*cursor)
263 cursor++;
264
265 if (++len == SIZE_MAX / sizeof(*nodes)) {
266 av_free(node_uri);
267 err = AVERROR(ENAMETOOLONG);
268 break;
269 }
270
271 /* creating URLContext */
272 err = ffurl_open_whitelist(&uc, node_uri, flags,
273 &h->interrupt_callback, NULL, h->protocol_whitelist, h->protocol_blacklist, h);
274 av_free(node_uri);
275 if (err < 0)
276 break;
277
278 /* creating size */
279 if ((size = ffurl_size(uc)) < 0) {
280 ffurl_close(uc);
281 err = AVERROR(ENOSYS);
282 break;
283 }
284
285 nodes = av_fast_realloc(data->nodes, &nodes_size, sizeof(*nodes) * len);
286 if (!nodes) {
287 ffurl_close(uc);
288 err = AVERROR(ENOMEM);
289 break;
290 }
291 data->nodes = nodes;
292
293 /* assembling */
294 data->nodes[i].uc = uc;
295 data->nodes[i++].size = size;
296 total_size += size;
297 }
298 av_bprint_finalize(&bp, NULL);
299 data->length = i;
300
301 if (!data->length)
302 err = AVERROR_INVALIDDATA;
303 if (err < 0)
304 concat_close(h);
305
306 data->total_size = total_size;
307 return err;
308 }
309
310 const URLProtocol ff_concatf_protocol = {
311 .name = "concatf",
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",
318 };
319 #endif
320