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