| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 2014 Nicolas George | ||
| 3 | * | ||
| 4 | * This file is part of FFmpeg. | ||
| 5 | * | ||
| 6 | * FFmpeg is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU Lesser General Public License | ||
| 8 | * as published by the Free Software Foundation; either | ||
| 9 | * version 2.1 of the License, or (at your option) any later version. | ||
| 10 | * | ||
| 11 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU Lesser General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU Lesser General Public License | ||
| 17 | * along with FFmpeg; if not, write to the Free Software Foundation, Inc., | ||
| 18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include "libavutil/avassert.h" | ||
| 22 | #include "libavutil/avstring.h" | ||
| 23 | #include "libavutil/opt.h" | ||
| 24 | #include "url.h" | ||
| 25 | |||
| 26 | typedef struct SubfileContext { | ||
| 27 | const AVClass *class; | ||
| 28 | URLContext *h; | ||
| 29 | int64_t start; | ||
| 30 | int64_t end; | ||
| 31 | int64_t pos; | ||
| 32 | } SubfileContext; | ||
| 33 | |||
| 34 | #define OFFSET(field) offsetof(SubfileContext, field) | ||
| 35 | #define D AV_OPT_FLAG_DECODING_PARAM | ||
| 36 | |||
| 37 | static const AVOption subfile_options[] = { | ||
| 38 | { "start", "start offset", OFFSET(start), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, D }, | ||
| 39 | { "end", "end offset", OFFSET(end), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, D }, | ||
| 40 | { NULL } | ||
| 41 | }; | ||
| 42 | |||
| 43 | #undef OFFSET | ||
| 44 | #undef D | ||
| 45 | |||
| 46 | static const AVClass subfile_class = { | ||
| 47 | .class_name = "subfile", | ||
| 48 | .item_name = av_default_item_name, | ||
| 49 | .option = subfile_options, | ||
| 50 | .version = LIBAVUTIL_VERSION_INT, | ||
| 51 | }; | ||
| 52 | |||
| 53 | ✗ | static int slave_seek(URLContext *h) | |
| 54 | { | ||
| 55 | ✗ | SubfileContext *c = h->priv_data; | |
| 56 | int64_t ret; | ||
| 57 | |||
| 58 | ✗ | if ((ret = ffurl_seek(c->h, c->pos, SEEK_SET)) != c->pos) { | |
| 59 | ✗ | if (ret >= 0) | |
| 60 | ✗ | ret = AVERROR_BUG; | |
| 61 | ✗ | av_log(h, AV_LOG_ERROR, "Impossible to seek in file: %s\n", | |
| 62 | ✗ | av_err2str(ret)); | |
| 63 | ✗ | return ret; | |
| 64 | } | ||
| 65 | ✗ | return 0; | |
| 66 | } | ||
| 67 | |||
| 68 | ✗ | static int subfile_open(URLContext *h, const char *filename, int flags, | |
| 69 | AVDictionary **options) | ||
| 70 | { | ||
| 71 | ✗ | SubfileContext *c = h->priv_data; | |
| 72 | int ret; | ||
| 73 | |||
| 74 | ✗ | if (!c->end) | |
| 75 | ✗ | c->end = INT64_MAX; | |
| 76 | |||
| 77 | ✗ | if (c->end <= c->start) { | |
| 78 | ✗ | av_log(h, AV_LOG_ERROR, "end before start\n"); | |
| 79 | ✗ | return AVERROR(EINVAL); | |
| 80 | } | ||
| 81 | ✗ | av_strstart(filename, "subfile:", &filename); | |
| 82 | ✗ | ret = ffurl_open_whitelist(&c->h, filename, flags, &h->interrupt_callback, | |
| 83 | options, h->protocol_whitelist, h->protocol_blacklist, h); | ||
| 84 | ✗ | if (ret < 0) | |
| 85 | ✗ | return ret; | |
| 86 | ✗ | c->pos = c->start; | |
| 87 | ✗ | if ((ret = slave_seek(h)) < 0) { | |
| 88 | ✗ | ffurl_closep(&c->h); | |
| 89 | ✗ | return ret; | |
| 90 | } | ||
| 91 | ✗ | return 0; | |
| 92 | } | ||
| 93 | |||
| 94 | ✗ | static int subfile_close(URLContext *h) | |
| 95 | { | ||
| 96 | ✗ | SubfileContext *c = h->priv_data; | |
| 97 | ✗ | return ffurl_closep(&c->h); | |
| 98 | } | ||
| 99 | |||
| 100 | ✗ | static int subfile_read(URLContext *h, unsigned char *buf, int size) | |
| 101 | { | ||
| 102 | ✗ | SubfileContext *c = h->priv_data; | |
| 103 | ✗ | int64_t rest = c->end - c->pos; | |
| 104 | int ret; | ||
| 105 | |||
| 106 | ✗ | if (rest <= 0) | |
| 107 | ✗ | return AVERROR_EOF; | |
| 108 | ✗ | size = FFMIN(size, rest); | |
| 109 | ✗ | ret = ffurl_read(c->h, buf, size); | |
| 110 | ✗ | if (ret >= 0) | |
| 111 | ✗ | c->pos += ret; | |
| 112 | ✗ | return ret; | |
| 113 | } | ||
| 114 | |||
| 115 | ✗ | static int64_t subfile_seek(URLContext *h, int64_t pos, int whence) | |
| 116 | { | ||
| 117 | ✗ | SubfileContext *c = h->priv_data; | |
| 118 | int64_t new_pos, end; | ||
| 119 | int ret; | ||
| 120 | |||
| 121 | ✗ | end = c->end; | |
| 122 | ✗ | if (end == INT64_MAX && (end = ffurl_seek(c->h, 0, AVSEEK_SIZE)) < 0) | |
| 123 | ✗ | return end; | |
| 124 | |||
| 125 | ✗ | switch (whence) { | |
| 126 | ✗ | case AVSEEK_SIZE: | |
| 127 | ✗ | return end - c->start; | |
| 128 | ✗ | case SEEK_SET: | |
| 129 | ✗ | new_pos = c->start + av_clip(pos, 0, end - c->start); | |
| 130 | ✗ | break; | |
| 131 | ✗ | case SEEK_CUR: | |
| 132 | ✗ | new_pos = c->pos + av_clip(pos, -(c->pos - c->start), end - c->pos); | |
| 133 | ✗ | break; | |
| 134 | ✗ | case SEEK_END: | |
| 135 | ✗ | new_pos = end + av_clip(pos, -(end - c->start), 0); | |
| 136 | ✗ | break; | |
| 137 | ✗ | default: | |
| 138 | ✗ | av_assert0(0); | |
| 139 | } | ||
| 140 | ✗ | if (new_pos < c->start) | |
| 141 | ✗ | return AVERROR(EINVAL); | |
| 142 | ✗ | c->pos = new_pos; | |
| 143 | ✗ | if ((ret = slave_seek(h)) < 0) | |
| 144 | ✗ | return ret; | |
| 145 | ✗ | return c->pos - c->start; | |
| 146 | } | ||
| 147 | |||
| 148 | const URLProtocol ff_subfile_protocol = { | ||
| 149 | .name = "subfile", | ||
| 150 | .url_open2 = subfile_open, | ||
| 151 | .url_read = subfile_read, | ||
| 152 | .url_seek = subfile_seek, | ||
| 153 | .url_close = subfile_close, | ||
| 154 | .priv_data_size = sizeof(SubfileContext), | ||
| 155 | .priv_data_class = &subfile_class, | ||
| 156 | .default_whitelist = "file", | ||
| 157 | }; | ||
| 158 |