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