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 "config_components.h" | ||
23 | |||
24 | #include "libavutil/avstring.h" | ||
25 | #include "libavutil/file_open.h" | ||
26 | #include "libavutil/internal.h" | ||
27 | #include "libavutil/mem.h" | ||
28 | #include "libavutil/opt.h" | ||
29 | #include "avio.h" | ||
30 | #if HAVE_DIRENT_H | ||
31 | #include <dirent.h> | ||
32 | #endif | ||
33 | #include <fcntl.h> | ||
34 | #if HAVE_IO_H | ||
35 | #include <io.h> | ||
36 | #endif | ||
37 | #if HAVE_UNISTD_H | ||
38 | #include <unistd.h> | ||
39 | #endif | ||
40 | #include <sys/stat.h> | ||
41 | #include <stdlib.h> | ||
42 | #include "os_support.h" | ||
43 | #include "url.h" | ||
44 | |||
45 | /* Some systems may not have S_ISFIFO */ | ||
46 | #ifndef S_ISFIFO | ||
47 | # ifdef S_IFIFO | ||
48 | # define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) | ||
49 | # else | ||
50 | # define S_ISFIFO(m) 0 | ||
51 | # endif | ||
52 | #endif | ||
53 | |||
54 | /* Not available in POSIX.1-1996 */ | ||
55 | #ifndef S_ISLNK | ||
56 | # ifdef S_IFLNK | ||
57 | # define S_ISLNK(m) (((m) & S_IFLNK) == S_IFLNK) | ||
58 | # else | ||
59 | # define S_ISLNK(m) 0 | ||
60 | # endif | ||
61 | #endif | ||
62 | |||
63 | /* Not available in POSIX.1-1996 */ | ||
64 | #ifndef S_ISSOCK | ||
65 | # ifdef S_IFSOCK | ||
66 | # define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) | ||
67 | # else | ||
68 | # define S_ISSOCK(m) 0 | ||
69 | # endif | ||
70 | #endif | ||
71 | |||
72 | /* S_ISREG not available on Windows */ | ||
73 | #ifndef S_ISREG | ||
74 | # ifdef S_IFREG | ||
75 | # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) | ||
76 | # else | ||
77 | # define S_ISREG(m) 0 | ||
78 | # endif | ||
79 | #endif | ||
80 | |||
81 | /* S_ISBLK not available on Windows */ | ||
82 | #ifndef S_ISBLK | ||
83 | # ifdef S_IFBLK | ||
84 | # define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) | ||
85 | # else | ||
86 | # define S_ISBLK(m) 0 | ||
87 | # endif | ||
88 | #endif | ||
89 | |||
90 | /* standard file protocol */ | ||
91 | |||
92 | typedef struct FileContext { | ||
93 | const AVClass *class; | ||
94 | int fd; | ||
95 | int trunc; | ||
96 | int blocksize; | ||
97 | int follow; | ||
98 | int seekable; | ||
99 | #if HAVE_DIRENT_H | ||
100 | DIR *dir; | ||
101 | #endif | ||
102 | int64_t initial_pos; | ||
103 | } FileContext; | ||
104 | |||
105 | static const AVOption file_options[] = { | ||
106 | { "truncate", "truncate existing files on write", offsetof(FileContext, trunc), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, | ||
107 | { "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 }, | ||
108 | { "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 }, | ||
109 | { "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 }, | ||
110 | { NULL } | ||
111 | }; | ||
112 | |||
113 | static const AVOption pipe_options[] = { | ||
114 | { "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 }, | ||
115 | { "fd", "set file descriptor", offsetof(FileContext, fd), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, | ||
116 | { NULL } | ||
117 | }; | ||
118 | |||
119 | static const AVClass file_class = { | ||
120 | .class_name = "file", | ||
121 | .item_name = av_default_item_name, | ||
122 | .option = file_options, | ||
123 | .version = LIBAVUTIL_VERSION_INT, | ||
124 | }; | ||
125 | |||
126 | static const AVClass pipe_class = { | ||
127 | .class_name = "pipe", | ||
128 | .item_name = av_default_item_name, | ||
129 | .option = pipe_options, | ||
130 | .version = LIBAVUTIL_VERSION_INT, | ||
131 | }; | ||
132 | |||
133 | static const AVClass fd_class = { | ||
134 | .class_name = "fd", | ||
135 | .item_name = av_default_item_name, | ||
136 | .option = pipe_options, | ||
137 | .version = LIBAVUTIL_VERSION_INT, | ||
138 | }; | ||
139 | |||
140 | 206420 | static int file_read(URLContext *h, unsigned char *buf, int size) | |
141 | { | ||
142 | 206420 | FileContext *c = h->priv_data; | |
143 | int ret; | ||
144 | 206420 | size = FFMIN(size, c->blocksize); | |
145 | 206420 | ret = read(c->fd, buf, size); | |
146 |
3/4✓ Branch 0 taken 11952 times.
✓ Branch 1 taken 194468 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11952 times.
|
206420 | if (ret == 0 && c->follow) |
147 | ✗ | return AVERROR(EAGAIN); | |
148 |
2/2✓ Branch 0 taken 11952 times.
✓ Branch 1 taken 194468 times.
|
206420 | if (ret == 0) |
149 | 11952 | return AVERROR_EOF; | |
150 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 194468 times.
|
194468 | return (ret == -1) ? AVERROR(errno) : ret; |
151 | } | ||
152 | |||
153 | 232507 | static int file_write(URLContext *h, const unsigned char *buf, int size) | |
154 | { | ||
155 | 232507 | FileContext *c = h->priv_data; | |
156 | int ret; | ||
157 | 232507 | size = FFMIN(size, c->blocksize); | |
158 | 232507 | ret = write(c->fd, buf, size); | |
159 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 232507 times.
|
232507 | return (ret == -1) ? AVERROR(errno) : ret; |
160 | } | ||
161 | |||
162 | ✗ | static int file_get_handle(URLContext *h) | |
163 | { | ||
164 | ✗ | FileContext *c = h->priv_data; | |
165 | ✗ | return c->fd; | |
166 | } | ||
167 | |||
168 | 45284 | static int file_check(URLContext *h, int mask) | |
169 | { | ||
170 | 45284 | int ret = 0; | |
171 | 45284 | const char *filename = h->filename; | |
172 | 45284 | av_strstart(filename, "file:", &filename); | |
173 | |||
174 | { | ||
175 | #if HAVE_ACCESS && defined(R_OK) | ||
176 |
2/2✓ Branch 1 taken 10732 times.
✓ Branch 2 taken 34552 times.
|
45284 | if (access(filename, F_OK) < 0) |
177 | 10732 | return AVERROR(errno); | |
178 |
1/2✓ Branch 0 taken 34552 times.
✗ Branch 1 not taken.
|
34552 | if (mask&AVIO_FLAG_READ) |
179 |
1/2✓ Branch 1 taken 34552 times.
✗ Branch 2 not taken.
|
34552 | if (access(filename, R_OK) >= 0) |
180 | 34552 | ret |= AVIO_FLAG_READ; | |
181 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 34552 times.
|
34552 | if (mask&AVIO_FLAG_WRITE) |
182 | ✗ | if (access(filename, W_OK) >= 0) | |
183 | ✗ | ret |= AVIO_FLAG_WRITE; | |
184 | #else | ||
185 | struct stat st; | ||
186 | ret = stat(filename, &st); | ||
187 | if (ret < 0) | ||
188 | return AVERROR(errno); | ||
189 | |||
190 | ret |= st.st_mode&S_IRUSR ? mask&AVIO_FLAG_READ : 0; | ||
191 | ret |= st.st_mode&S_IWUSR ? mask&AVIO_FLAG_WRITE : 0; | ||
192 | #endif | ||
193 | } | ||
194 | 34552 | return ret; | |
195 | } | ||
196 | |||
197 | 2608 | static int fd_dup(URLContext *h, int oldfd) | |
198 | { | ||
199 | int newfd; | ||
200 | |||
201 | #ifdef F_DUPFD_CLOEXEC | ||
202 | newfd = fcntl(oldfd, F_DUPFD_CLOEXEC, 0); | ||
203 | #else | ||
204 | 2608 | newfd = dup(oldfd); | |
205 | #endif | ||
206 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2608 times.
|
2608 | if (newfd == -1) |
207 | ✗ | return newfd; | |
208 | |||
209 | #if HAVE_FCNTL | ||
210 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2608 times.
|
2608 | if (fcntl(newfd, F_SETFD, FD_CLOEXEC) == -1) |
211 | ✗ | av_log(h, AV_LOG_DEBUG, "Failed to set close on exec\n"); | |
212 | #endif | ||
213 | |||
214 | #if HAVE_SETMODE | ||
215 | setmode(newfd, O_BINARY); | ||
216 | #endif | ||
217 | 2608 | return newfd; | |
218 | } | ||
219 | |||
220 | 98553 | static int file_close(URLContext *h) | |
221 | { | ||
222 | 98553 | FileContext *c = h->priv_data; | |
223 | int ret; | ||
224 | |||
225 |
3/4✓ Branch 0 taken 2608 times.
✓ Branch 1 taken 95945 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2608 times.
|
98553 | if (c->initial_pos >= 0 && !h->is_streamed) |
226 | ✗ | lseek(c->fd, c->initial_pos, SEEK_SET); | |
227 | |||
228 | 98553 | ret = close(c->fd); | |
229 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 98553 times.
|
98553 | return (ret == -1) ? AVERROR(errno) : 0; |
230 | } | ||
231 | |||
232 | /* XXX: use llseek */ | ||
233 | 303484 | static int64_t file_seek(URLContext *h, int64_t pos, int whence) | |
234 | { | ||
235 | 303484 | FileContext *c = h->priv_data; | |
236 | int64_t ret; | ||
237 | |||
238 |
2/2✓ Branch 0 taken 196210 times.
✓ Branch 1 taken 107274 times.
|
303484 | if (whence == AVSEEK_SIZE) { |
239 | struct stat st; | ||
240 | 196210 | ret = fstat(c->fd, &st); | |
241 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 196210 times.
✓ Branch 2 taken 196210 times.
✗ Branch 3 not taken.
|
196210 | return ret < 0 ? AVERROR(errno) : (S_ISFIFO(st.st_mode) ? 0 : st.st_size); |
242 | } | ||
243 | |||
244 | 107274 | ret = lseek(c->fd, pos, whence); | |
245 | |||
246 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 107274 times.
|
107274 | return ret < 0 ? AVERROR(errno) : ret; |
247 | } | ||
248 | |||
249 | #if CONFIG_FILE_PROTOCOL | ||
250 | |||
251 | ✗ | static int file_delete(URLContext *h) | |
252 | { | ||
253 | #if HAVE_UNISTD_H | ||
254 | int ret; | ||
255 | ✗ | const char *filename = h->filename; | |
256 | ✗ | av_strstart(filename, "file:", &filename); | |
257 | |||
258 | ✗ | ret = rmdir(filename); | |
259 | ✗ | if (ret < 0 && (errno == ENOTDIR | |
260 | # ifdef _WIN32 | ||
261 | || errno == EINVAL | ||
262 | # endif | ||
263 | )) | ||
264 | ✗ | ret = unlink(filename); | |
265 | ✗ | if (ret < 0) | |
266 | ✗ | return AVERROR(errno); | |
267 | |||
268 | ✗ | return ret; | |
269 | #else | ||
270 | return AVERROR(ENOSYS); | ||
271 | #endif /* HAVE_UNISTD_H */ | ||
272 | } | ||
273 | |||
274 | 78 | static int file_move(URLContext *h_src, URLContext *h_dst) | |
275 | { | ||
276 | 78 | const char *filename_src = h_src->filename; | |
277 | 78 | const char *filename_dst = h_dst->filename; | |
278 | 78 | av_strstart(filename_src, "file:", &filename_src); | |
279 | 78 | av_strstart(filename_dst, "file:", &filename_dst); | |
280 | |||
281 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 78 times.
|
78 | if (rename(filename_src, filename_dst) < 0) |
282 | ✗ | return AVERROR(errno); | |
283 | |||
284 | 78 | return 0; | |
285 | } | ||
286 | |||
287 | 95946 | static int file_open(URLContext *h, const char *filename, int flags) | |
288 | { | ||
289 | 95946 | FileContext *c = h->priv_data; | |
290 | int access; | ||
291 | int fd; | ||
292 | struct stat st; | ||
293 | |||
294 | 95946 | av_strstart(filename, "file:", &filename); | |
295 | |||
296 | 95946 | c->initial_pos = -1; | |
297 |
3/4✓ Branch 0 taken 2509 times.
✓ Branch 1 taken 93437 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2509 times.
|
95946 | if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) { |
298 | ✗ | access = O_CREAT | O_RDWR; | |
299 | ✗ | if (c->trunc) | |
300 | ✗ | access |= O_TRUNC; | |
301 |
2/2✓ Branch 0 taken 2509 times.
✓ Branch 1 taken 93437 times.
|
95946 | } else if (flags & AVIO_FLAG_WRITE) { |
302 | 2509 | access = O_CREAT | O_WRONLY; | |
303 |
1/2✓ Branch 0 taken 2509 times.
✗ Branch 1 not taken.
|
2509 | if (c->trunc) |
304 | 2509 | access |= O_TRUNC; | |
305 | } else { | ||
306 | 93437 | access = O_RDONLY; | |
307 | } | ||
308 | #ifdef O_BINARY | ||
309 | access |= O_BINARY; | ||
310 | #endif | ||
311 | 95946 | fd = avpriv_open(filename, access, 0666); | |
312 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 95945 times.
|
95946 | if (fd == -1) |
313 | 1 | return AVERROR(errno); | |
314 | 95945 | c->fd = fd; | |
315 | |||
316 |
2/4✓ Branch 1 taken 95945 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 95945 times.
|
95945 | h->is_streamed = !fstat(fd, &st) && S_ISFIFO(st.st_mode); |
317 | |||
318 | /* Buffer writes more than the default 32k to improve throughput especially | ||
319 | * with networked file systems */ | ||
320 |
3/4✓ Branch 0 taken 95945 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2509 times.
✓ Branch 3 taken 93436 times.
|
95945 | if (!h->is_streamed && flags & AVIO_FLAG_WRITE) |
321 | 2509 | h->min_packet_size = h->max_packet_size = 262144; | |
322 | |||
323 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 95945 times.
|
95945 | if (c->seekable >= 0) |
324 | ✗ | h->is_streamed = !c->seekable; | |
325 | |||
326 | 95945 | return 0; | |
327 | } | ||
328 | |||
329 | ✗ | static int file_open_dir(URLContext *h) | |
330 | { | ||
331 | #if HAVE_LSTAT | ||
332 | ✗ | FileContext *c = h->priv_data; | |
333 | |||
334 | ✗ | c->dir = opendir(h->filename); | |
335 | ✗ | if (!c->dir) | |
336 | ✗ | return AVERROR(errno); | |
337 | |||
338 | ✗ | return 0; | |
339 | #else | ||
340 | return AVERROR(ENOSYS); | ||
341 | #endif /* HAVE_LSTAT */ | ||
342 | } | ||
343 | |||
344 | ✗ | static int file_read_dir(URLContext *h, AVIODirEntry **next) | |
345 | { | ||
346 | #if HAVE_LSTAT | ||
347 | ✗ | FileContext *c = h->priv_data; | |
348 | struct dirent *dir; | ||
349 | ✗ | char *fullpath = NULL; | |
350 | |||
351 | ✗ | *next = ff_alloc_dir_entry(); | |
352 | ✗ | if (!*next) | |
353 | ✗ | return AVERROR(ENOMEM); | |
354 | do { | ||
355 | ✗ | errno = 0; | |
356 | ✗ | dir = readdir(c->dir); | |
357 | ✗ | if (!dir) { | |
358 | ✗ | av_freep(next); | |
359 | ✗ | return AVERROR(errno); | |
360 | } | ||
361 | ✗ | } while (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")); | |
362 | |||
363 | ✗ | fullpath = av_append_path_component(h->filename, dir->d_name); | |
364 | ✗ | if (fullpath) { | |
365 | struct stat st; | ||
366 | ✗ | if (!lstat(fullpath, &st)) { | |
367 | ✗ | if (S_ISDIR(st.st_mode)) | |
368 | ✗ | (*next)->type = AVIO_ENTRY_DIRECTORY; | |
369 | ✗ | else if (S_ISFIFO(st.st_mode)) | |
370 | ✗ | (*next)->type = AVIO_ENTRY_NAMED_PIPE; | |
371 | ✗ | else if (S_ISCHR(st.st_mode)) | |
372 | ✗ | (*next)->type = AVIO_ENTRY_CHARACTER_DEVICE; | |
373 | ✗ | else if (S_ISBLK(st.st_mode)) | |
374 | ✗ | (*next)->type = AVIO_ENTRY_BLOCK_DEVICE; | |
375 | ✗ | else if (S_ISLNK(st.st_mode)) | |
376 | ✗ | (*next)->type = AVIO_ENTRY_SYMBOLIC_LINK; | |
377 | ✗ | else if (S_ISSOCK(st.st_mode)) | |
378 | ✗ | (*next)->type = AVIO_ENTRY_SOCKET; | |
379 | ✗ | else if (S_ISREG(st.st_mode)) | |
380 | ✗ | (*next)->type = AVIO_ENTRY_FILE; | |
381 | else | ||
382 | ✗ | (*next)->type = AVIO_ENTRY_UNKNOWN; | |
383 | |||
384 | ✗ | (*next)->group_id = st.st_gid; | |
385 | ✗ | (*next)->user_id = st.st_uid; | |
386 | ✗ | (*next)->size = st.st_size; | |
387 | ✗ | (*next)->filemode = st.st_mode & 0777; | |
388 | ✗ | (*next)->modification_timestamp = INT64_C(1000000) * st.st_mtime; | |
389 | ✗ | (*next)->access_timestamp = INT64_C(1000000) * st.st_atime; | |
390 | ✗ | (*next)->status_change_timestamp = INT64_C(1000000) * st.st_ctime; | |
391 | } | ||
392 | ✗ | av_free(fullpath); | |
393 | } | ||
394 | |||
395 | ✗ | (*next)->name = av_strdup(dir->d_name); | |
396 | ✗ | return 0; | |
397 | #else | ||
398 | return AVERROR(ENOSYS); | ||
399 | #endif /* HAVE_LSTAT */ | ||
400 | } | ||
401 | |||
402 | ✗ | static int file_close_dir(URLContext *h) | |
403 | { | ||
404 | #if HAVE_LSTAT | ||
405 | ✗ | FileContext *c = h->priv_data; | |
406 | ✗ | closedir(c->dir); | |
407 | ✗ | return 0; | |
408 | #else | ||
409 | return AVERROR(ENOSYS); | ||
410 | #endif /* HAVE_LSTAT */ | ||
411 | } | ||
412 | |||
413 | const URLProtocol ff_file_protocol = { | ||
414 | .name = "file", | ||
415 | .url_open = file_open, | ||
416 | .url_read = file_read, | ||
417 | .url_write = file_write, | ||
418 | .url_seek = file_seek, | ||
419 | .url_close = file_close, | ||
420 | .url_get_file_handle = file_get_handle, | ||
421 | .url_check = file_check, | ||
422 | .url_delete = file_delete, | ||
423 | .url_move = file_move, | ||
424 | .priv_data_size = sizeof(FileContext), | ||
425 | .priv_data_class = &file_class, | ||
426 | .url_open_dir = file_open_dir, | ||
427 | .url_read_dir = file_read_dir, | ||
428 | .url_close_dir = file_close_dir, | ||
429 | .default_whitelist = "file,crypto,data" | ||
430 | }; | ||
431 | |||
432 | #endif /* CONFIG_FILE_PROTOCOL */ | ||
433 | |||
434 | #if CONFIG_PIPE_PROTOCOL | ||
435 | |||
436 | 2608 | static int pipe_open(URLContext *h, const char *filename, int flags) | |
437 | { | ||
438 | 2608 | FileContext *c = h->priv_data; | |
439 | int fd; | ||
440 | char *final; | ||
441 | |||
442 |
1/2✓ Branch 0 taken 2608 times.
✗ Branch 1 not taken.
|
2608 | if (c->fd < 0) { |
443 | 2608 | av_strstart(filename, "pipe:", &filename); | |
444 | |||
445 | 2608 | fd = strtol(filename, &final, 10); | |
446 |
3/4✓ Branch 0 taken 23 times.
✓ Branch 1 taken 2585 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 23 times.
|
2608 | if((filename == final) || *final ) {/* No digits found, or something like 10ab */ |
447 |
1/2✓ Branch 0 taken 2585 times.
✗ Branch 1 not taken.
|
2585 | if (flags & AVIO_FLAG_WRITE) { |
448 | 2585 | fd = 1; | |
449 | } else { | ||
450 | ✗ | fd = 0; | |
451 | } | ||
452 | } | ||
453 | 2608 | c->fd = fd; | |
454 | } | ||
455 | |||
456 | 2608 | c->fd = fd_dup(h, c->fd); | |
457 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2608 times.
|
2608 | if (c->fd == -1) |
458 | ✗ | return AVERROR(errno); | |
459 | 2608 | h->is_streamed = 1; | |
460 | 2608 | return 0; | |
461 | } | ||
462 | |||
463 | const URLProtocol ff_pipe_protocol = { | ||
464 | .name = "pipe", | ||
465 | .url_open = pipe_open, | ||
466 | .url_read = file_read, | ||
467 | .url_write = file_write, | ||
468 | .url_close = file_close, | ||
469 | .url_get_file_handle = file_get_handle, | ||
470 | .url_check = file_check, | ||
471 | .priv_data_size = sizeof(FileContext), | ||
472 | .priv_data_class = &pipe_class, | ||
473 | .default_whitelist = "crypto,data" | ||
474 | }; | ||
475 | |||
476 | #endif /* CONFIG_PIPE_PROTOCOL */ | ||
477 | |||
478 | #if CONFIG_FD_PROTOCOL | ||
479 | |||
480 | ✗ | static int fd_open(URLContext *h, const char *filename, int flags) | |
481 | { | ||
482 | ✗ | FileContext *c = h->priv_data; | |
483 | struct stat st; | ||
484 | |||
485 | ✗ | if (strcmp(filename, "fd:") != 0) { | |
486 | ✗ | av_log(h, AV_LOG_ERROR, "Doesn't support pass file descriptor via URL," | |
487 | " please set it via -fd {num}\n"); | ||
488 | ✗ | return AVERROR(EINVAL); | |
489 | } | ||
490 | |||
491 | ✗ | if (c->fd < 0) { | |
492 | ✗ | if (flags & AVIO_FLAG_WRITE) { | |
493 | ✗ | c->fd = 1; | |
494 | } else { | ||
495 | ✗ | c->fd = 0; | |
496 | } | ||
497 | } | ||
498 | ✗ | if (fstat(c->fd, &st) < 0) | |
499 | ✗ | return AVERROR(errno); | |
500 | ✗ | h->is_streamed = !(S_ISREG(st.st_mode) || S_ISBLK(st.st_mode)); | |
501 | ✗ | c->fd = fd_dup(h, c->fd); | |
502 | ✗ | if (c->fd == -1) | |
503 | ✗ | return AVERROR(errno); | |
504 | |||
505 | ✗ | if (h->is_streamed) | |
506 | ✗ | c->initial_pos = -1; | |
507 | else | ||
508 | ✗ | c->initial_pos = lseek(c->fd, 0, SEEK_CUR); | |
509 | |||
510 | ✗ | return 0; | |
511 | } | ||
512 | |||
513 | const URLProtocol ff_fd_protocol = { | ||
514 | .name = "fd", | ||
515 | .url_open = fd_open, | ||
516 | .url_read = file_read, | ||
517 | .url_write = file_write, | ||
518 | .url_seek = file_seek, | ||
519 | .url_close = file_close, | ||
520 | .url_get_file_handle = file_get_handle, | ||
521 | .url_check = file_check, | ||
522 | .priv_data_size = sizeof(FileContext), | ||
523 | .priv_data_class = &fd_class, | ||
524 | .default_whitelist = "crypto,data" | ||
525 | }; | ||
526 | |||
527 | #endif /* CONFIG_FD_PROTOCOL */ | ||
528 | |||
529 | #if CONFIG_ANDROID_CONTENT_PROTOCOL | ||
530 | #include <jni.h> | ||
531 | #include "libavcodec/ffjni.h" | ||
532 | #include "libavcodec/jni.h" | ||
533 | |||
534 | typedef struct JFields { | ||
535 | jclass uri_class; | ||
536 | jmethodID parse_id; | ||
537 | |||
538 | jclass context_class; | ||
539 | jmethodID get_content_resolver_id; | ||
540 | |||
541 | jclass content_resolver_class; | ||
542 | jmethodID open_file_descriptor_id; | ||
543 | |||
544 | jclass parcel_file_descriptor_class; | ||
545 | jmethodID detach_fd_id; | ||
546 | } JFields; | ||
547 | |||
548 | #define OFFSET(x) offsetof(JFields, x) | ||
549 | static const struct FFJniField jfields_mapping[] = { | ||
550 | { "android/net/Uri", NULL, NULL, FF_JNI_CLASS, OFFSET(uri_class), 1 }, | ||
551 | { "android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", FF_JNI_STATIC_METHOD, OFFSET(parse_id), 1 }, | ||
552 | |||
553 | { "android/content/Context", NULL, NULL, FF_JNI_CLASS, OFFSET(context_class), 1 }, | ||
554 | { "android/content/Context", "getContentResolver", "()Landroid/content/ContentResolver;", FF_JNI_METHOD, OFFSET(get_content_resolver_id), 1 }, | ||
555 | |||
556 | { "android/content/ContentResolver", NULL, NULL, FF_JNI_CLASS, OFFSET(content_resolver_class), 1 }, | ||
557 | { "android/content/ContentResolver", "openFileDescriptor", "(Landroid/net/Uri;Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;", FF_JNI_METHOD, OFFSET(open_file_descriptor_id), 1 }, | ||
558 | |||
559 | { "android/os/ParcelFileDescriptor", NULL, NULL, FF_JNI_CLASS, OFFSET(parcel_file_descriptor_class), 1 }, | ||
560 | { "android/os/ParcelFileDescriptor", "detachFd", "()I", FF_JNI_METHOD, OFFSET(detach_fd_id), 1 }, | ||
561 | |||
562 | { NULL } | ||
563 | }; | ||
564 | #undef OFFSET | ||
565 | |||
566 | static int android_content_open(URLContext *h, const char *filename, int flags) | ||
567 | { | ||
568 | FileContext *c = h->priv_data; | ||
569 | int fd, ret; | ||
570 | struct stat st; | ||
571 | const char *mode_str = "r"; | ||
572 | |||
573 | JNIEnv *env; | ||
574 | JFields jfields = { 0 }; | ||
575 | jobject application_context = NULL; | ||
576 | jobject url = NULL; | ||
577 | jobject mode = NULL; | ||
578 | jobject uri = NULL; | ||
579 | jobject content_resolver = NULL; | ||
580 | jobject parcel_file_descriptor = NULL; | ||
581 | |||
582 | env = ff_jni_get_env(c); | ||
583 | if (!env) { | ||
584 | return AVERROR(EINVAL); | ||
585 | } | ||
586 | |||
587 | ret = ff_jni_init_jfields(env, &jfields, jfields_mapping, 0, c); | ||
588 | if (ret < 0) { | ||
589 | av_log(c, AV_LOG_ERROR, "failed to initialize jni fields\n"); | ||
590 | return ret; | ||
591 | } | ||
592 | |||
593 | application_context = av_jni_get_android_app_ctx(); | ||
594 | if (!application_context) { | ||
595 | av_log(c, AV_LOG_ERROR, "application context is not set\n"); | ||
596 | ret = AVERROR_EXTERNAL; | ||
597 | goto done; | ||
598 | } | ||
599 | |||
600 | url = ff_jni_utf_chars_to_jstring(env, filename, c); | ||
601 | if (!url) { | ||
602 | ret = AVERROR_EXTERNAL; | ||
603 | goto done; | ||
604 | } | ||
605 | |||
606 | if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) | ||
607 | mode_str = "rw"; | ||
608 | else if (flags & AVIO_FLAG_WRITE) | ||
609 | mode_str = "w"; | ||
610 | |||
611 | mode = ff_jni_utf_chars_to_jstring(env, mode_str, c); | ||
612 | if (!mode) { | ||
613 | ret = AVERROR_EXTERNAL; | ||
614 | goto done; | ||
615 | } | ||
616 | |||
617 | uri = (*env)->CallStaticObjectMethod(env, jfields.uri_class, jfields.parse_id, url); | ||
618 | ret = ff_jni_exception_check(env, 1, c); | ||
619 | if (ret < 0) | ||
620 | goto done; | ||
621 | |||
622 | content_resolver = (*env)->CallObjectMethod(env, application_context, jfields.get_content_resolver_id); | ||
623 | ret = ff_jni_exception_check(env, 1, c); | ||
624 | if (ret < 0) | ||
625 | goto done; | ||
626 | |||
627 | parcel_file_descriptor = (*env)->CallObjectMethod(env, content_resolver, jfields.open_file_descriptor_id, uri, mode); | ||
628 | ret = ff_jni_exception_check(env, 1, c); | ||
629 | if (ret < 0) | ||
630 | goto done; | ||
631 | |||
632 | fd = (*env)->CallIntMethod(env, parcel_file_descriptor, jfields.detach_fd_id); | ||
633 | ret = ff_jni_exception_check(env, 1, c); | ||
634 | if (ret < 0) | ||
635 | goto done; | ||
636 | |||
637 | if (fstat(fd, &st) < 0) { | ||
638 | close(fd); | ||
639 | return AVERROR(errno); | ||
640 | } | ||
641 | |||
642 | c->fd = fd; | ||
643 | h->is_streamed = !(S_ISREG(st.st_mode) || S_ISBLK(st.st_mode)); | ||
644 | |||
645 | done: | ||
646 | (*env)->DeleteLocalRef(env, url); | ||
647 | (*env)->DeleteLocalRef(env, mode); | ||
648 | (*env)->DeleteLocalRef(env, uri); | ||
649 | (*env)->DeleteLocalRef(env, content_resolver); | ||
650 | (*env)->DeleteLocalRef(env, parcel_file_descriptor); | ||
651 | ff_jni_reset_jfields(env, &jfields, jfields_mapping, 0, c); | ||
652 | |||
653 | return ret; | ||
654 | } | ||
655 | |||
656 | static const AVOption android_content_options[] = { | ||
657 | { "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 }, | ||
658 | { NULL } | ||
659 | }; | ||
660 | |||
661 | static const AVClass android_content_class = { | ||
662 | .class_name = "android_content", | ||
663 | .item_name = av_default_item_name, | ||
664 | .option = android_content_options, | ||
665 | .version = LIBAVUTIL_VERSION_INT, | ||
666 | }; | ||
667 | |||
668 | const URLProtocol ff_android_content_protocol = { | ||
669 | .name = "content", | ||
670 | .url_open = android_content_open, | ||
671 | .url_read = file_read, | ||
672 | .url_write = file_write, | ||
673 | .url_seek = file_seek, | ||
674 | .url_close = file_close, | ||
675 | .url_get_file_handle = file_get_handle, | ||
676 | .url_check = NULL, | ||
677 | .priv_data_size = sizeof(FileContext), | ||
678 | .priv_data_class = &android_content_class, | ||
679 | }; | ||
680 | |||
681 | #endif /* CONFIG_ANDROID_CONTENT_PROTOCOL */ | ||
682 |