mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-11-21 10:55:51 +02:00
963d4b76de
This code was written when the allocation functions used parameters of type unsigned. This is no longer true today and therefore we only need to check whether the multiplication of the array's size stays within a size_t -- and this can be offloaded to av_realloc_array. Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@gmail.com>
200 lines
5.6 KiB
C
200 lines
5.6 KiB
C
/*
|
|
* Concat URL protocol
|
|
* Copyright (c) 2006 Steve Lhomme
|
|
* Copyright (c) 2007 Wolfram Gloger
|
|
* Copyright (c) 2010 Michele Orrù
|
|
*
|
|
* This file is part of FFmpeg.
|
|
*
|
|
* FFmpeg is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* FFmpeg is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with FFmpeg; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "libavutil/avstring.h"
|
|
#include "libavutil/mem.h"
|
|
|
|
#include "avformat.h"
|
|
#include "url.h"
|
|
|
|
#define AV_CAT_SEPARATOR "|"
|
|
|
|
struct concat_nodes {
|
|
URLContext *uc; ///< node's URLContext
|
|
int64_t size; ///< url filesize
|
|
};
|
|
|
|
struct concat_data {
|
|
struct concat_nodes *nodes; ///< list of nodes to concat
|
|
size_t length; ///< number of cat'ed nodes
|
|
size_t current; ///< index of currently read node
|
|
uint64_t total_size;
|
|
};
|
|
|
|
static av_cold int concat_close(URLContext *h)
|
|
{
|
|
int err = 0;
|
|
size_t i;
|
|
struct concat_data *data = h->priv_data;
|
|
struct concat_nodes *nodes = data->nodes;
|
|
|
|
for (i = 0; i != data->length; i++)
|
|
err |= ffurl_closep(&nodes[i].uc);
|
|
|
|
av_freep(&data->nodes);
|
|
|
|
return err < 0 ? -1 : 0;
|
|
}
|
|
|
|
static av_cold int concat_open(URLContext *h, const char *uri, int flags)
|
|
{
|
|
char *node_uri = NULL;
|
|
int err = 0;
|
|
int64_t size, total_size = 0;
|
|
size_t len, i;
|
|
URLContext *uc;
|
|
struct concat_data *data = h->priv_data;
|
|
struct concat_nodes *nodes;
|
|
|
|
if (!av_strstart(uri, "concat:", &uri)) {
|
|
av_log(h, AV_LOG_ERROR, "URL %s lacks prefix\n", uri);
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
for (i = 0, len = 1; uri[i]; i++) {
|
|
if (uri[i] == *AV_CAT_SEPARATOR) {
|
|
len++;
|
|
}
|
|
}
|
|
|
|
if (!(nodes = av_realloc_array(NULL, len, sizeof(*nodes))))
|
|
return AVERROR(ENOMEM);
|
|
else
|
|
data->nodes = nodes;
|
|
|
|
/* handle input */
|
|
if (!*uri)
|
|
err = AVERROR(ENOENT);
|
|
for (i = 0; *uri; i++) {
|
|
/* parsing uri */
|
|
len = strcspn(uri, AV_CAT_SEPARATOR);
|
|
if ((err = av_reallocp(&node_uri, len + 1)) < 0)
|
|
break;
|
|
av_strlcpy(node_uri, uri, len + 1);
|
|
uri += len + strspn(uri + len, AV_CAT_SEPARATOR);
|
|
|
|
/* creating URLContext */
|
|
err = ffurl_open_whitelist(&uc, node_uri, flags,
|
|
&h->interrupt_callback, NULL, h->protocol_whitelist, h->protocol_blacklist, h);
|
|
if (err < 0)
|
|
break;
|
|
|
|
/* creating size */
|
|
if ((size = ffurl_size(uc)) < 0) {
|
|
ffurl_close(uc);
|
|
err = AVERROR(ENOSYS);
|
|
break;
|
|
}
|
|
|
|
/* assembling */
|
|
nodes[i].uc = uc;
|
|
nodes[i].size = size;
|
|
total_size += size;
|
|
}
|
|
av_free(node_uri);
|
|
data->length = i;
|
|
|
|
if (err < 0)
|
|
concat_close(h);
|
|
else if (!(nodes = av_realloc(nodes, data->length * sizeof(*nodes)))) {
|
|
concat_close(h);
|
|
err = AVERROR(ENOMEM);
|
|
} else
|
|
data->nodes = nodes;
|
|
data->total_size = total_size;
|
|
return err;
|
|
}
|
|
|
|
static int concat_read(URLContext *h, unsigned char *buf, int size)
|
|
{
|
|
int result, total = 0;
|
|
struct concat_data *data = h->priv_data;
|
|
struct concat_nodes *nodes = data->nodes;
|
|
size_t i = data->current;
|
|
|
|
while (size > 0) {
|
|
result = ffurl_read(nodes[i].uc, buf, size);
|
|
if (result == AVERROR_EOF) {
|
|
if (i + 1 == data->length ||
|
|
ffurl_seek(nodes[++i].uc, 0, SEEK_SET) < 0)
|
|
break;
|
|
result = 0;
|
|
}
|
|
if (result < 0)
|
|
return total ? total : result;
|
|
total += result;
|
|
buf += result;
|
|
size -= result;
|
|
}
|
|
data->current = i;
|
|
return total ? total : result;
|
|
}
|
|
|
|
static int64_t concat_seek(URLContext *h, int64_t pos, int whence)
|
|
{
|
|
int64_t result;
|
|
struct concat_data *data = h->priv_data;
|
|
struct concat_nodes *nodes = data->nodes;
|
|
size_t i;
|
|
|
|
if ((whence & AVSEEK_SIZE))
|
|
return data->total_size;
|
|
switch (whence) {
|
|
case SEEK_END:
|
|
for (i = data->length - 1; i && pos < -nodes[i].size; i--)
|
|
pos += nodes[i].size;
|
|
break;
|
|
case SEEK_CUR:
|
|
/* get the absolute position */
|
|
for (i = 0; i != data->current; i++)
|
|
pos += nodes[i].size;
|
|
pos += ffurl_seek(nodes[i].uc, 0, SEEK_CUR);
|
|
whence = SEEK_SET;
|
|
/* fall through with the absolute position */
|
|
case SEEK_SET:
|
|
for (i = 0; i != data->length - 1 && pos >= nodes[i].size; i++)
|
|
pos -= nodes[i].size;
|
|
break;
|
|
default:
|
|
return AVERROR(EINVAL);
|
|
}
|
|
|
|
result = ffurl_seek(nodes[i].uc, pos, whence);
|
|
if (result >= 0) {
|
|
data->current = i;
|
|
while (i)
|
|
result += nodes[--i].size;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
const URLProtocol ff_concat_protocol = {
|
|
.name = "concat",
|
|
.url_open = concat_open,
|
|
.url_read = concat_read,
|
|
.url_seek = concat_seek,
|
|
.url_close = concat_close,
|
|
.priv_data_size = sizeof(struct concat_data),
|
|
.default_whitelist = "concat,file,subfile",
|
|
};
|