1/* 2 * Concat URL protocol 3 * Copyright (c) 2006 Steve Lhomme 4 * Copyright (c) 2007 Wolfram Gloger 5 * Copyright (c) 2010 Michele Orr�� 6 * 7 * This file is part of Libav. 8 * 9 * Libav is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; either 12 * version 2.1 of the License, or (at your option) any later version. 13 * 14 * Libav is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with Libav; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 */ 23 24#include "avformat.h" 25#include "libavutil/avstring.h" 26#include "libavutil/mem.h" 27#include "url.h" 28 29#define AV_CAT_SEPARATOR "|" 30 31struct concat_nodes { 32 URLContext *uc; ///< node's URLContext 33 int64_t size; ///< url filesize 34}; 35 36struct concat_data { 37 struct concat_nodes *nodes; ///< list of nodes to concat 38 size_t length; ///< number of cat'ed nodes 39 size_t current; ///< index of currently read node 40}; 41 42static av_cold int concat_close(URLContext *h) 43{ 44 int err = 0; 45 size_t i; 46 struct concat_data *data = h->priv_data; 47 struct concat_nodes *nodes = data->nodes; 48 49 for (i = 0; i != data->length; i++) 50 err |= ffurl_close(nodes[i].uc); 51 52 av_freep(&data->nodes); 53 54 return err < 0 ? -1 : 0; 55} 56 57static av_cold int concat_open(URLContext *h, const char *uri, int flags) 58{ 59 char *node_uri = NULL, *tmp_uri; 60 int err = 0; 61 int64_t size; 62 size_t len, i; 63 URLContext *uc; 64 struct concat_data *data = h->priv_data; 65 struct concat_nodes *nodes; 66 67 av_strstart(uri, "concat:", &uri); 68 69 for (i = 0, len = 1; uri[i]; i++) 70 if (uri[i] == *AV_CAT_SEPARATOR) 71 /* integer overflow */ 72 if (++len == UINT_MAX / sizeof(*nodes)) { 73 av_freep(&h->priv_data); 74 return AVERROR(ENAMETOOLONG); 75 } 76 77 if (!(nodes = av_malloc(sizeof(*nodes) * len))) { 78 return AVERROR(ENOMEM); 79 } else 80 data->nodes = nodes; 81 82 /* handle input */ 83 if (!*uri) 84 err = AVERROR(ENOENT); 85 for (i = 0; *uri; i++) { 86 /* parsing uri */ 87 len = strcspn(uri, AV_CAT_SEPARATOR); 88 if (!(tmp_uri = av_realloc(node_uri, len+1))) { 89 err = AVERROR(ENOMEM); 90 break; 91 } else 92 node_uri = tmp_uri; 93 av_strlcpy(node_uri, uri, len+1); 94 uri += len + strspn(uri+len, AV_CAT_SEPARATOR); 95 96 /* creating URLContext */ 97 if ((err = ffurl_open(&uc, node_uri, flags, 98 &h->interrupt_callback, NULL)) < 0) 99 break; 100 101 /* creating size */ 102 if ((size = ffurl_size(uc)) < 0) { 103 ffurl_close(uc); 104 err = AVERROR(ENOSYS); 105 break; 106 } 107 108 /* assembling */ 109 nodes[i].uc = uc; 110 nodes[i].size = size; 111 } 112 av_free(node_uri); 113 data->length = i; 114 115 if (err < 0) 116 concat_close(h); 117 else if (!(nodes = av_realloc(nodes, data->length * sizeof(*nodes)))) { 118 concat_close(h); 119 err = AVERROR(ENOMEM); 120 } else 121 data->nodes = nodes; 122 return err; 123} 124 125static int concat_read(URLContext *h, unsigned char *buf, int size) 126{ 127 int result, total = 0; 128 struct concat_data *data = h->priv_data; 129 struct concat_nodes *nodes = data->nodes; 130 size_t i = data->current; 131 132 while (size > 0) { 133 result = ffurl_read(nodes[i].uc, buf, size); 134 if (result < 0) 135 return total ? total : result; 136 if (!result) 137 if (i + 1 == data->length || 138 ffurl_seek(nodes[++i].uc, 0, SEEK_SET) < 0) 139 break; 140 total += result; 141 buf += result; 142 size -= result; 143 } 144 data->current = i; 145 return total; 146} 147 148static int64_t concat_seek(URLContext *h, int64_t pos, int whence) 149{ 150 int64_t result; 151 struct concat_data *data = h->priv_data; 152 struct concat_nodes *nodes = data->nodes; 153 size_t i; 154 155 switch (whence) { 156 case SEEK_END: 157 for (i = data->length - 1; 158 i && pos < -nodes[i].size; 159 i--) 160 pos += nodes[i].size; 161 break; 162 case SEEK_CUR: 163 /* get the absolute position */ 164 for (i = 0; i != data->current; i++) 165 pos += nodes[i].size; 166 pos += ffurl_seek(nodes[i].uc, 0, SEEK_CUR); 167 whence = SEEK_SET; 168 /* fall through with the absolute position */ 169 case SEEK_SET: 170 for (i = 0; i != data->length - 1 && pos >= nodes[i].size; i++) 171 pos -= nodes[i].size; 172 break; 173 default: 174 return AVERROR(EINVAL); 175 } 176 177 result = ffurl_seek(nodes[i].uc, pos, whence); 178 if (result >= 0) { 179 data->current = i; 180 while (i) 181 result += nodes[--i].size; 182 } 183 return result; 184} 185 186URLProtocol ff_concat_protocol = { 187 .name = "concat", 188 .url_open = concat_open, 189 .url_read = concat_read, 190 .url_seek = concat_seek, 191 .url_close = concat_close, 192 .priv_data_size = sizeof(struct concat_data), 193}; 194