FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/file.c
Date: 2026-10-24 19:27:14
Exec Total Coverage
Lines: 84 179 46.9%
Functions: 9 16 56.2%
Branches: 47 130 36.2%

Line Branch Exec Source
1 /*
2 * buffered file I/O
3 * Copyright (c) 2001 Fabrice Bellard
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include <string.h>
23
24 #include "config_components.h"
25
26 #include "libavutil/avstring.h"
27 #include "libavutil/error.h"
28 #include "libavutil/file_open.h"
29 #include "libavutil/internal.h"
30 #include "libavutil/mem.h"
31 #include "libavutil/opt.h"
32 #include "avio.h"
33 #if HAVE_DIRENT_H
34 #include <dirent.h>
35 #endif
36 #include <fcntl.h>
37 #if HAVE_IO_H
38 #include <io.h>
39 #endif
40 #if HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43 #include <sys/stat.h>
44 #include <stdlib.h>
45 #include "os_support.h"
46 #include "url.h"
47
48 /* Some systems may not have S_ISFIFO */
49 #ifndef S_ISFIFO
50 # ifdef S_IFIFO
51 # define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
52 # else
53 # define S_ISFIFO(m) 0
54 # endif
55 #endif
56
57 /* Not available in POSIX.1-1996 */
58 #ifndef S_ISLNK
59 # ifdef S_IFLNK
60 # define S_ISLNK(m) (((m) & S_IFLNK) == S_IFLNK)
61 # else
62 # define S_ISLNK(m) 0
63 # endif
64 #endif
65
66 /* Not available in POSIX.1-1996 */
67 #ifndef S_ISSOCK
68 # ifdef S_IFSOCK
69 # define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
70 # else
71 # define S_ISSOCK(m) 0
72 # endif
73 #endif
74
75 /* S_ISREG not available on Windows */
76 #ifndef S_ISREG
77 # ifdef S_IFREG
78 # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
79 # else
80 # define S_ISREG(m) 0
81 # endif
82 #endif
83
84 /* S_ISBLK not available on Windows */
85 #ifndef S_ISBLK
86 # ifdef S_IFBLK
87 # define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
88 # else
89 # define S_ISBLK(m) 0
90 # endif
91 #endif
92
93 /* standard file protocol */
94
95 typedef struct FileContext {
96 const AVClass *class;
97 int fd;
98 int trunc;
99 int blocksize;
100 int pkt_size;
101 int follow;
102 int seekable;
103 #if HAVE_DIRENT_H
104 DIR *dir;
105 #endif
106 #if CONFIG_WINDOWS_HANDLE_PROTOCOL
107 int64_t handle;
108 #endif
109 } FileContext;
110
111 static const AVOption file_options[] = {
112 { "truncate", "truncate existing files on write", offsetof(FileContext, trunc), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
113 { "blocksize", "set I/O operation maximum block size", offsetof(FileContext, blocksize), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
114 { "follow", "Follow a file as it is being written", offsetof(FileContext, follow), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_DECODING_PARAM },
115 { "seekable", "Sets if the file is seekable", offsetof(FileContext, seekable), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 0, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_ENCODING_PARAM },
116 { "pkt_size", "Maximum packet size", offsetof(FileContext, pkt_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_ENCODING_PARAM },
117 { NULL }
118 };
119
120 static const AVOption pipe_options[] = {
121 { "blocksize", "set I/O operation maximum block size", offsetof(FileContext, blocksize), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
122 { "fd", "set file descriptor", offsetof(FileContext, fd), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
123 { NULL }
124 };
125
126 static const AVClass file_class = {
127 .class_name = "file",
128 .item_name = av_default_item_name,
129 .option = file_options,
130 .version = LIBAVUTIL_VERSION_INT,
131 };
132
133 static const AVClass pipe_class = {
134 .class_name = "pipe",
135 .item_name = av_default_item_name,
136 .option = pipe_options,
137 .version = LIBAVUTIL_VERSION_INT,
138 };
139
140 static const AVClass fd_class = {
141 .class_name = "fd",
142 .item_name = av_default_item_name,
143 .option = pipe_options,
144 .version = LIBAVUTIL_VERSION_INT,
145 };
146
147 227886 static int file_read(URLContext *h, unsigned char *buf, int size)
148 {
149 227886 FileContext *c = h->priv_data;
150 int ret;
151 227886 size = FFMIN(size, c->blocksize);
152 227886 ret = read(c->fd, buf, size);
153
3/4
✓ Branch 0 taken 13745 times.
✓ Branch 1 taken 214141 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 13745 times.
227886 if (ret == 0 && c->follow)
154 ✗ return AVERROR(EAGAIN);
155
2/2
✓ Branch 0 taken 13745 times.
✓ Branch 1 taken 214141 times.
227886 if (ret == 0)
156 13745 return AVERROR_EOF;
157
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 214141 times.
214141 return (ret == -1) ? AVERROR(errno) : ret;
158 }
159
160 308141 static int file_write(URLContext *h, const unsigned char *buf, int size)
161 {
162 308141 FileContext *c = h->priv_data;
163 int ret;
164 308141 size = FFMIN(size, c->blocksize);
165 308141 ret = write(c->fd, buf, size);
166
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 308141 times.
308141 return (ret == -1) ? AVERROR(errno) : ret;
167 }
168
169 ✗ static int file_get_handle(URLContext *h)
170 {
171 ✗ FileContext *c = h->priv_data;
172 ✗ return c->fd;
173 }
174
175 52602 static int file_check(URLContext *h, int mask)
176 {
177 52602 int ret = 0;
178 52602 const char *filename = h->filename;
179 52602 av_strstart(filename, "file:", &filename);
180
181 {
182 #if HAVE_ACCESS && defined(R_OK)
183
2/2
✓ Branch 1 taken 12455 times.
✓ Branch 2 taken 40147 times.
52602 if (access(filename, F_OK) < 0)
184 12455 return AVERROR(errno);
185
2/2
✓ Branch 0 taken 40146 times.
✓ Branch 1 taken 1 times.
40147 if (mask&AVIO_FLAG_READ)
186
1/2
✓ Branch 1 taken 40146 times.
✗ Branch 2 not taken.
40146 if (access(filename, R_OK) >= 0)
187 40146 ret |= AVIO_FLAG_READ;
188
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40147 times.
40147 if (mask&AVIO_FLAG_WRITE)
189 ✗ if (access(filename, W_OK) >= 0)
190 ✗ ret |= AVIO_FLAG_WRITE;
191 #else
192 struct stat st;
193 ret = stat(filename, &st);
194 if (ret < 0)
195 return AVERROR(errno);
196
197 ret |= st.st_mode&S_IRUSR ? mask&AVIO_FLAG_READ : 0;
198 ret |= st.st_mode&S_IWUSR ? mask&AVIO_FLAG_WRITE : 0;
199 #endif
200 }
201 40147 return ret;
202 }
203
204 #if CONFIG_FD_PROTOCOL || CONFIG_PIPE_PROTOCOL
205 2997 static int fd_dup(URLContext *h, int oldfd)
206 {
207 int newfd;
208
209 #ifdef F_DUPFD_CLOEXEC
210 newfd = fcntl(oldfd, F_DUPFD_CLOEXEC, 0);
211 #else
212 2997 newfd = dup(oldfd);
213 #endif
214
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2997 times.
2997 if (newfd == -1)
215 ✗ return newfd;
216
217 #if HAVE_FCNTL
218
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2997 times.
2997 if (fcntl(newfd, F_SETFD, FD_CLOEXEC) == -1)
219 ✗ av_log(h, AV_LOG_DEBUG, "Failed to set close on exec\n");
220 #endif
221
222 #if HAVE_SETMODE
223 setmode(newfd, O_BINARY);
224 #endif
225 2997 return newfd;
226 }
227 #endif
228
229 #if CONFIG_FD_PROTOCOL || CONFIG_PIPE_PROTOCOL || CONFIG_WINDOWS_HANDLE_PROTOCOL
230 /* Without a check, avio_check() would open the descriptor, rewinding seekable
231 * output. It cannot pass the option selecting the descriptor, so just fail. */
232 ✗ static int fd_check(URLContext *h, int mask)
233 {
234 ✗ return AVERROR(ENOSYS);
235 }
236 #endif
237
238 114808 static int file_close(URLContext *h)
239 {
240 114808 FileContext *c = h->priv_data;
241 114808 int ret = close(c->fd);
242
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 114808 times.
114808 return (ret == -1) ? AVERROR(errno) : 0;
243 }
244
245 /* XXX: use llseek */
246 385427 static int64_t file_seek(URLContext *h, int64_t pos, int whence)
247 {
248 385427 FileContext *c = h->priv_data;
249 int64_t ret;
250
251
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 385427 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
385427 if (c->follow && (whence == SEEK_END || whence == AVSEEK_SIZE))
252 ✗ return AVERROR(ENOSYS); /* true size is not known */
253
254
2/2
✓ Branch 0 taken 261411 times.
✓ Branch 1 taken 124016 times.
385427 if (whence == AVSEEK_SIZE) {
255 struct stat st;
256 261411 ret = fstat(c->fd, &st);
257
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 261411 times.
✓ Branch 2 taken 261411 times.
✗ Branch 3 not taken.
261411 return ret < 0 ? AVERROR(errno) : (S_ISFIFO(st.st_mode) ? 0 : st.st_size);
258 }
259
260 124016 ret = lseek(c->fd, pos, whence);
261
262
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 124016 times.
124016 return ret < 0 ? AVERROR(errno) : ret;
263 }
264
265 #if CONFIG_FILE_PROTOCOL
266
267 ✗ static int file_delete(URLContext *h)
268 {
269 #if HAVE_UNISTD_H
270 int ret;
271 ✗ const char *filename = h->filename;
272 ✗ av_strstart(filename, "file:", &filename);
273
274 ✗ ret = rmdir(filename);
275 ✗ if (ret < 0 && (errno == ENOTDIR
276 # ifdef _WIN32
277 || errno == EINVAL
278 # endif
279 ))
280 ✗ ret = unlink(filename);
281 ✗ if (ret < 0)
282 ✗ return AVERROR(errno);
283
284 ✗ return ret;
285 #else
286 return AVERROR(ENOSYS);
287 #endif /* HAVE_UNISTD_H */
288 }
289
290 122 static int file_move(URLContext *h_src, URLContext *h_dst)
291 {
292 122 const char *filename_src = h_src->filename;
293 122 const char *filename_dst = h_dst->filename;
294 122 av_strstart(filename_src, "file:", &filename_src);
295 122 av_strstart(filename_dst, "file:", &filename_dst);
296
297
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 122 times.
122 if (rename(filename_src, filename_dst) < 0)
298 ✗ return AVERROR(errno);
299
300 122 return 0;
301 }
302
303 111813 static int file_open(URLContext *h, const char *filename, int flags)
304 {
305 111813 FileContext *c = h->priv_data;
306 int access;
307 int fd;
308 struct stat st;
309
310 111813 av_strstart(filename, "file:", &filename);
311
312
3/4
✓ Branch 0 taken 4373 times.
✓ Branch 1 taken 107440 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4373 times.
111813 if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) {
313 ✗ access = O_CREAT | O_RDWR;
314 ✗ if (c->trunc)
315 ✗ access |= O_TRUNC;
316
2/2
✓ Branch 0 taken 4373 times.
✓ Branch 1 taken 107440 times.
111813 } else if (flags & AVIO_FLAG_WRITE) {
317 4373 access = O_CREAT | O_WRONLY;
318
1/2
✓ Branch 0 taken 4373 times.
✗ Branch 1 not taken.
4373 if (c->trunc)
319 4373 access |= O_TRUNC;
320 } else {
321 107440 access = O_RDONLY;
322 }
323 #ifdef O_BINARY
324 111813 access |= O_BINARY;
325 #endif
326 111813 fd = avpriv_open(filename, access, 0666);
327
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 111811 times.
111813 if (fd == -1)
328 2 return AVERROR(errno);
329 111811 c->fd = fd;
330
331
2/4
✓ Branch 1 taken 111811 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 111811 times.
111811 h->is_streamed = !fstat(fd, &st) && S_ISFIFO(st.st_mode);
332
333
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 111811 times.
111811 if (c->pkt_size) {
334 ✗ h->max_packet_size = c->pkt_size;
335 } else {
336 /* Buffer writes more than the default 32k to improve throughput especially
337 * with networked file systems */
338
3/4
✓ Branch 0 taken 111811 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4373 times.
✓ Branch 3 taken 107438 times.
111811 if (!h->is_streamed && flags & AVIO_FLAG_WRITE)
339 4373 h->max_packet_size = 262144;
340 }
341 /* Disable per-packet flushing by default to improve throughput especially
342 * with networked file systems */
343
3/4
✓ Branch 0 taken 111811 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4373 times.
✓ Branch 3 taken 107438 times.
111811 if (!h->is_streamed && flags & AVIO_FLAG_WRITE)
344 4373 h->min_packet_size = h->max_packet_size;
345
346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 111811 times.
111811 if (c->seekable >= 0)
347 ✗ h->is_streamed = !c->seekable;
348
349 111811 return 0;
350 }
351
352 ✗ static int file_open_dir(URLContext *h)
353 {
354 #if HAVE_LSTAT
355 ✗ FileContext *c = h->priv_data;
356
357 ✗ c->dir = opendir(h->filename);
358 ✗ if (!c->dir)
359 ✗ return AVERROR(errno);
360
361 ✗ return 0;
362 #else
363 return AVERROR(ENOSYS);
364 #endif /* HAVE_LSTAT */
365 }
366
367 ✗ static int file_read_dir(URLContext *h, AVIODirEntry **next)
368 {
369 #if HAVE_LSTAT
370 ✗ FileContext *c = h->priv_data;
371 struct dirent *dir;
372 ✗ char *fullpath = NULL;
373
374 ✗ *next = ff_alloc_dir_entry();
375 ✗ if (!*next)
376 ✗ return AVERROR(ENOMEM);
377 do {
378 ✗ errno = 0;
379 ✗ dir = readdir(c->dir);
380 ✗ if (!dir) {
381 ✗ av_freep(next);
382 ✗ return AVERROR(errno);
383 }
384 ✗ } while (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, ".."));
385
386 ✗ fullpath = av_append_path_component(h->filename, dir->d_name);
387 ✗ if (fullpath) {
388 struct stat st;
389 ✗ if (!lstat(fullpath, &st)) {
390 ✗ if (S_ISDIR(st.st_mode))
391 ✗ (*next)->type = AVIO_ENTRY_DIRECTORY;
392 ✗ else if (S_ISFIFO(st.st_mode))
393 ✗ (*next)->type = AVIO_ENTRY_NAMED_PIPE;
394 ✗ else if (S_ISCHR(st.st_mode))
395 ✗ (*next)->type = AVIO_ENTRY_CHARACTER_DEVICE;
396 ✗ else if (S_ISBLK(st.st_mode))
397 ✗ (*next)->type = AVIO_ENTRY_BLOCK_DEVICE;
398 ✗ else if (S_ISLNK(st.st_mode))
399 ✗ (*next)->type = AVIO_ENTRY_SYMBOLIC_LINK;
400 ✗ else if (S_ISSOCK(st.st_mode))
401 ✗ (*next)->type = AVIO_ENTRY_SOCKET;
402 ✗ else if (S_ISREG(st.st_mode))
403 ✗ (*next)->type = AVIO_ENTRY_FILE;
404 else
405 ✗ (*next)->type = AVIO_ENTRY_UNKNOWN;
406
407 ✗ (*next)->group_id = st.st_gid;
408 ✗ (*next)->user_id = st.st_uid;
409 ✗ (*next)->size = st.st_size;
410 ✗ (*next)->filemode = st.st_mode & 0777;
411 ✗ (*next)->modification_timestamp = INT64_C(1000000) * st.st_mtime;
412 ✗ (*next)->access_timestamp = INT64_C(1000000) * st.st_atime;
413 ✗ (*next)->status_change_timestamp = INT64_C(1000000) * st.st_ctime;
414 }
415 ✗ av_free(fullpath);
416 }
417
418 ✗ (*next)->name = av_strdup(dir->d_name);
419 ✗ return (*next)->name ? 0 : AVERROR(ENOMEM);
420 #else
421 return AVERROR(ENOSYS);
422 #endif /* HAVE_LSTAT */
423 }
424
425 ✗ static int file_close_dir(URLContext *h)
426 {
427 #if HAVE_LSTAT
428 ✗ FileContext *c = h->priv_data;
429 ✗ closedir(c->dir);
430 ✗ return 0;
431 #else
432 return AVERROR(ENOSYS);
433 #endif /* HAVE_LSTAT */
434 }
435
436 const URLProtocol ff_file_protocol = {
437 .name = "file",
438 .url_open = file_open,
439 .url_read = file_read,
440 .url_write = file_write,
441 .url_seek = file_seek,
442 .url_close = file_close,
443 .url_get_file_handle = file_get_handle,
444 .url_check = file_check,
445 .url_delete = file_delete,
446 .url_move = file_move,
447 .priv_data_size = sizeof(FileContext),
448 .priv_data_class = &file_class,
449 .url_open_dir = file_open_dir,
450 .url_read_dir = file_read_dir,
451 .url_close_dir = file_close_dir,
452 .default_whitelist = "file,crypto,data"
453 };
454
455 #endif /* CONFIG_FILE_PROTOCOL */
456
457 #if CONFIG_PIPE_PROTOCOL
458
459 2997 static int pipe_open(URLContext *h, const char *filename, int flags)
460 {
461 2997 FileContext *c = h->priv_data;
462 int fd;
463 char *final;
464
465
1/2
✓ Branch 0 taken 2997 times.
✗ Branch 1 not taken.
2997 if (c->fd < 0) {
466 2997 av_strstart(filename, "pipe:", &filename);
467
468
2/2
✓ Branch 0 taken 2970 times.
✓ Branch 1 taken 27 times.
2997 if (!*filename) {
469
1/2
✓ Branch 0 taken 2970 times.
✗ Branch 1 not taken.
2970 if (flags & AVIO_FLAG_WRITE) {
470 2970 fd = 1;
471 } else {
472 ✗ fd = 0;
473 }
474 } else {
475 27 fd = strtol(filename, &final, 10);
476
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
27 if (*final) /* No digits found, or something like 10ab */
477 ✗ return AVERROR(EINVAL);
478 }
479 2997 c->fd = fd;
480 }
481
482 2997 c->fd = fd_dup(h, c->fd);
483
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2997 times.
2997 if (c->fd == -1)
484 ✗ return AVERROR(errno);
485 2997 h->is_streamed = 1;
486 2997 return 0;
487 }
488
489 const URLProtocol ff_pipe_protocol = {
490 .name = "pipe",
491 .url_open = pipe_open,
492 .url_read = file_read,
493 .url_write = file_write,
494 .url_close = file_close,
495 .url_get_file_handle = file_get_handle,
496 .url_check = fd_check,
497 .priv_data_size = sizeof(FileContext),
498 .priv_data_class = &pipe_class,
499 .default_whitelist = "crypto,data"
500 };
501
502 #endif /* CONFIG_PIPE_PROTOCOL */
503
504 #if CONFIG_FD_PROTOCOL
505
506 ✗ static int fd_open(URLContext *h, const char *filename, int flags)
507 {
508 ✗ FileContext *c = h->priv_data;
509 struct stat st;
510
511 ✗ if (strcmp(filename, "fd:") != 0) {
512 ✗ av_log(h, AV_LOG_ERROR, "Doesn't support pass file descriptor via URL,"
513 " please set it via -fd {num}\n");
514 ✗ return AVERROR(EINVAL);
515 }
516
517 ✗ if (c->fd < 0) {
518 ✗ if (flags & AVIO_FLAG_WRITE) {
519 ✗ c->fd = 1;
520 } else {
521 ✗ c->fd = 0;
522 }
523 }
524 ✗ if (fstat(c->fd, &st) < 0)
525 ✗ return AVERROR(errno);
526 ✗ h->is_streamed = !(S_ISREG(st.st_mode) || S_ISBLK(st.st_mode));
527 ✗ c->fd = fd_dup(h, c->fd);
528 ✗ if (c->fd == -1)
529 ✗ return AVERROR(errno);
530
531 ✗ return 0;
532 }
533
534 const URLProtocol ff_fd_protocol = {
535 .name = "fd",
536 .url_open = fd_open,
537 .url_read = file_read,
538 .url_write = file_write,
539 .url_seek = file_seek,
540 .url_close = file_close,
541 .url_get_file_handle = file_get_handle,
542 .url_check = fd_check,
543 .priv_data_size = sizeof(FileContext),
544 .priv_data_class = &fd_class,
545 .default_whitelist = "crypto,data"
546 };
547
548 #endif /* CONFIG_FD_PROTOCOL */
549
550 #if CONFIG_WINDOWS_HANDLE_PROTOCOL
551
552 #include <windows.h>
553
554 static const AVOption windows_handle_options[] = {
555 { "blocksize", "set I/O operation maximum block size", offsetof(FileContext, blocksize), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
556 { "handle", "set Windows handle", offsetof(FileContext, handle), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, AV_OPT_FLAG_ENCODING_PARAM },
557 { NULL }
558 };
559
560 static const AVClass windows_handle_class = {
561 .class_name = "windows_handle",
562 .item_name = av_default_item_name,
563 .option = windows_handle_options,
564 .version = LIBAVUTIL_VERSION_INT,
565 };
566
567 static int windows_handle_open(URLContext *h, const char *filename, int flags)
568 {
569 FileContext *c = h->priv_data;
570 HANDLE handle = (HANDLE)(intptr_t)c->handle;
571 HANDLE dup_handle;
572 int fd;
573 struct stat st;
574
575 if (strcmp(filename, "handle:") != 0) {
576 av_log(h, AV_LOG_ERROR, "Doesn't support passing Windows handles via URL,"
577 " please set it via -handle {num}\n");
578 return AVERROR(EINVAL);
579 }
580
581 if (handle == INVALID_HANDLE_VALUE || !handle) {
582 if (flags & AVIO_FLAG_WRITE)
583 handle = GetStdHandle(STD_OUTPUT_HANDLE);
584 else
585 handle = GetStdHandle(STD_INPUT_HANDLE);
586 if (handle == INVALID_HANDLE_VALUE || !handle)
587 return AVERROR(EBADF);
588 }
589
590 if (!DuplicateHandle(GetCurrentProcess(), handle,
591 GetCurrentProcess(), &dup_handle,
592 0, FALSE, DUPLICATE_SAME_ACCESS))
593 return GetLastError() == ERROR_INVALID_HANDLE ? AVERROR(EBADF)
594 : AVERROR(EIO);
595
596 fd = _open_osfhandle((intptr_t)dup_handle, O_BINARY);
597 if (fd == -1) {
598 CloseHandle(dup_handle);
599 return AVERROR(errno);
600 }
601
602 if (fstat(fd, &st) < 0) {
603 close(fd);
604 return AVERROR(errno);
605 }
606
607 c->fd = fd;
608 h->is_streamed = !(S_ISREG(st.st_mode) || S_ISBLK(st.st_mode));
609
610 return 0;
611 }
612
613 const URLProtocol ff_windows_handle_protocol = {
614 .name = "handle",
615 .url_open = windows_handle_open,
616 .url_read = file_read,
617 .url_write = file_write,
618 .url_seek = file_seek,
619 .url_close = file_close,
620 .url_get_file_handle = file_get_handle,
621 .url_check = fd_check,
622 .priv_data_size = sizeof(FileContext),
623 .priv_data_class = &windows_handle_class,
624 .default_whitelist = "crypto,data"
625 };
626
627 #endif /* CONFIG_WINDOWS_HANDLE_PROTOCOL */
628
629 #if CONFIG_ANDROID_CONTENT_PROTOCOL
630 #include <jni.h>
631 #include "libavcodec/ffjni.h"
632 #include "libavcodec/jni.h"
633
634 typedef struct JFields {
635 jclass uri_class;
636 jmethodID parse_id;
637
638 jclass context_class;
639 jmethodID get_content_resolver_id;
640
641 jclass content_resolver_class;
642 jmethodID open_file_descriptor_id;
643
644 jclass parcel_file_descriptor_class;
645 jmethodID detach_fd_id;
646 } JFields;
647
648 #define OFFSET(x) offsetof(JFields, x)
649 static const struct FFJniField jfields_mapping[] = {
650 { "android/net/Uri", NULL, NULL, FF_JNI_CLASS, OFFSET(uri_class), 1 },
651 { "android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", FF_JNI_STATIC_METHOD, OFFSET(parse_id), 1 },
652
653 { "android/content/Context", NULL, NULL, FF_JNI_CLASS, OFFSET(context_class), 1 },
654 { "android/content/Context", "getContentResolver", "()Landroid/content/ContentResolver;", FF_JNI_METHOD, OFFSET(get_content_resolver_id), 1 },
655
656 { "android/content/ContentResolver", NULL, NULL, FF_JNI_CLASS, OFFSET(content_resolver_class), 1 },
657 { "android/content/ContentResolver", "openFileDescriptor", "(Landroid/net/Uri;Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;", FF_JNI_METHOD, OFFSET(open_file_descriptor_id), 1 },
658
659 { "android/os/ParcelFileDescriptor", NULL, NULL, FF_JNI_CLASS, OFFSET(parcel_file_descriptor_class), 1 },
660 { "android/os/ParcelFileDescriptor", "detachFd", "()I", FF_JNI_METHOD, OFFSET(detach_fd_id), 1 },
661
662 { NULL }
663 };
664 #undef OFFSET
665
666 static int android_content_open(URLContext *h, const char *filename, int flags)
667 {
668 FileContext *c = h->priv_data;
669 int fd, ret;
670 struct stat st;
671 const char *mode_str = "r";
672
673 JNIEnv *env;
674 JFields jfields = { 0 };
675 jobject application_context = NULL;
676 jobject url = NULL;
677 jobject mode = NULL;
678 jobject uri = NULL;
679 jobject content_resolver = NULL;
680 jobject parcel_file_descriptor = NULL;
681
682 env = ff_jni_get_env(c);
683 if (!env) {
684 return AVERROR(EINVAL);
685 }
686
687 ret = ff_jni_init_jfields(env, &jfields, jfields_mapping, 0, c);
688 if (ret < 0) {
689 av_log(c, AV_LOG_ERROR, "failed to initialize jni fields\n");
690 return ret;
691 }
692
693 application_context = av_jni_get_android_app_ctx();
694 if (!application_context) {
695 av_log(c, AV_LOG_ERROR, "application context is not set\n");
696 ret = AVERROR_EXTERNAL;
697 goto done;
698 }
699
700 url = ff_jni_utf_chars_to_jstring(env, filename, c);
701 if (!url) {
702 ret = AVERROR_EXTERNAL;
703 goto done;
704 }
705
706 if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ)
707 mode_str = "rw";
708 else if (flags & AVIO_FLAG_WRITE)
709 mode_str = "w";
710
711 mode = ff_jni_utf_chars_to_jstring(env, mode_str, c);
712 if (!mode) {
713 ret = AVERROR_EXTERNAL;
714 goto done;
715 }
716
717 uri = (*env)->CallStaticObjectMethod(env, jfields.uri_class, jfields.parse_id, url);
718 ret = ff_jni_exception_check(env, 1, c);
719 if (ret < 0)
720 goto done;
721
722 content_resolver = (*env)->CallObjectMethod(env, application_context, jfields.get_content_resolver_id);
723 ret = ff_jni_exception_check(env, 1, c);
724 if (ret < 0)
725 goto done;
726
727 parcel_file_descriptor = (*env)->CallObjectMethod(env, content_resolver, jfields.open_file_descriptor_id, uri, mode);
728 ret = ff_jni_exception_check(env, 1, c);
729 if (ret < 0)
730 goto done;
731 if (!parcel_file_descriptor) {
732 av_log(c, AV_LOG_ERROR, "file descriptor is null\n");
733 ret = AVERROR_EXTERNAL;
734 goto done;
735 }
736
737 fd = (*env)->CallIntMethod(env, parcel_file_descriptor, jfields.detach_fd_id);
738 ret = ff_jni_exception_check(env, 1, c);
739 if (ret < 0)
740 goto done;
741
742 if (fstat(fd, &st) < 0) {
743 ret = AVERROR(errno);
744 close(fd);
745 goto done;
746 }
747
748 c->fd = fd;
749 h->is_streamed = !(S_ISREG(st.st_mode) || S_ISBLK(st.st_mode));
750
751 done:
752 (*env)->DeleteLocalRef(env, url);
753 (*env)->DeleteLocalRef(env, mode);
754 (*env)->DeleteLocalRef(env, uri);
755 (*env)->DeleteLocalRef(env, content_resolver);
756 (*env)->DeleteLocalRef(env, parcel_file_descriptor);
757 ff_jni_reset_jfields(env, &jfields, jfields_mapping, 0, c);
758
759 return ret;
760 }
761
762 static const AVOption android_content_options[] = {
763 { "blocksize", "set I/O operation maximum block size", offsetof(FileContext, blocksize), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
764 { NULL }
765 };
766
767 static const AVClass android_content_class = {
768 .class_name = "android_content",
769 .item_name = av_default_item_name,
770 .option = android_content_options,
771 .version = LIBAVUTIL_VERSION_INT,
772 };
773
774 const URLProtocol ff_android_content_protocol = {
775 .name = "content",
776 .url_open = android_content_open,
777 .url_read = file_read,
778 .url_write = file_write,
779 .url_seek = file_seek,
780 .url_close = file_close,
781 .url_get_file_handle = file_get_handle,
782 .priv_data_size = sizeof(FileContext),
783 .priv_data_class = &android_content_class,
784 };
785
786 #endif /* CONFIG_ANDROID_CONTENT_PROTOCOL */
787