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 FFmpeg. 8 * 9 * FFmpeg 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 * FFmpeg 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 FFmpeg; 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; 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_realloc(NULL, 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 ((err = av_reallocp(&node_uri, len + 1)) < 0) 89 break; 90 av_strlcpy(node_uri, uri, len+1); 91 uri += len + strspn(uri+len, AV_CAT_SEPARATOR); 92 93 /* creating URLContext */ 94 if ((err = ffurl_open(&uc, node_uri, flags, 95 &h->interrupt_callback, NULL)) < 0) 96 break; 97 98 /* creating size */ 99 if ((size = ffurl_size(uc)) < 0) { 100 ffurl_close(uc); 101 err = AVERROR(ENOSYS); 102 break; 103 } 104 105 /* assembling */ 106 nodes[i].uc = uc; 107 nodes[i].size = size; 108 } 109 av_free(node_uri); 110 data->length = i; 111 112 if (err < 0) 113 concat_close(h); 114 else if (!(nodes = av_realloc(nodes, data->length * sizeof(*nodes)))) { 115 concat_close(h); 116 err = AVERROR(ENOMEM); 117 } else 118 data->nodes = nodes; 119 return err; 120} 121 122static int concat_read(URLContext *h, unsigned char *buf, int size) 123{ 124 int result, total = 0; 125 struct concat_data *data = h->priv_data; 126 struct concat_nodes *nodes = data->nodes; 127 size_t i = data->current; 128 129 while (size > 0) { 130 result = ffurl_read(nodes[i].uc, buf, size); 131 if (result < 0) 132 return total ? total : result; 133 if (!result) 134 if (i + 1 == data->length || 135 ffurl_seek(nodes[++i].uc, 0, SEEK_SET) < 0) 136 break; 137 total += result; 138 buf += result; 139 size -= result; 140 } 141 data->current = i; 142 return total; 143} 144 145static int64_t concat_seek(URLContext *h, int64_t pos, int whence) 146{ 147 int64_t result; 148 struct concat_data *data = h->priv_data; 149 struct concat_nodes *nodes = data->nodes; 150 size_t i; 151 152 switch (whence) { 153 case SEEK_END: 154 for (i = data->length - 1; 155 i && pos < -nodes[i].size; 156 i--) 157 pos += nodes[i].size; 158 break; 159 case SEEK_CUR: 160 /* get the absolute position */ 161 for (i = 0; i != data->current; i++) 162 pos += nodes[i].size; 163 pos += ffurl_seek(nodes[i].uc, 0, SEEK_CUR); 164 whence = SEEK_SET; 165 /* fall through with the absolute position */ 166 case SEEK_SET: 167 for (i = 0; i != data->length - 1 && pos >= nodes[i].size; i++) 168 pos -= nodes[i].size; 169 break; 170 default: 171 return AVERROR(EINVAL); 172 } 173 174 result = ffurl_seek(nodes[i].uc, pos, whence); 175 if (result >= 0) { 176 data->current = i; 177 while (i) 178 result += nodes[--i].size; 179 } 180 return result; 181} 182 183URLProtocol ff_concat_protocol = { 184 .name = "concat", 185 .url_open = concat_open, 186 .url_read = concat_read, 187 .url_seek = concat_seek, 188 .url_close = concat_close, 189 .priv_data_size = sizeof(struct concat_data), 190}; 191