diff --git a/Changelog b/Changelog index f2a1dcde28..f545d91a20 100644 --- a/Changelog +++ b/Changelog @@ -3,6 +3,7 @@ releases are sorted from youngest to oldest. version : - libopenmpt demuxer +- tee protocol version 3.1: diff --git a/doc/protocols.texi b/doc/protocols.texi index 9bbd1315fc..4bc554badd 100644 --- a/doc/protocols.texi +++ b/doc/protocols.texi @@ -1159,6 +1159,15 @@ Play an AVI file directly from a TAR archive: subfile,,start,183241728,end,366490624,,:archive.tar @end example +@section tee + +Writes the output to multiple protocols. The individual outputs are seperated +by | + +@example +tee:file://path/to/local/this.avi|file://path/to/local/that.avi +@end example + @section tcp Transmission Control Protocol. diff --git a/libavformat/Makefile b/libavformat/Makefile index 6451c1cdc2..52f35f0eb6 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -564,6 +564,7 @@ OBJS-$(CONFIG_RTP_PROTOCOL) += rtpproto.o OBJS-$(CONFIG_SCTP_PROTOCOL) += sctp.o OBJS-$(CONFIG_SRTP_PROTOCOL) += srtpproto.o srtp.o OBJS-$(CONFIG_SUBFILE_PROTOCOL) += subfile.o +OBJS-$(CONFIG_TEE_PROTOCOL) += teeproto.o OBJS-$(CONFIG_TCP_PROTOCOL) += tcp.o OBJS-$(CONFIG_TLS_GNUTLS_PROTOCOL) += tls_gnutls.o tls.o OBJS-$(CONFIG_TLS_OPENSSL_PROTOCOL) += tls_openssl.o tls.o diff --git a/libavformat/protocols.c b/libavformat/protocols.c index 124010cced..b250f4934c 100644 --- a/libavformat/protocols.c +++ b/libavformat/protocols.c @@ -53,6 +53,7 @@ extern const URLProtocol ff_rtp_protocol; extern const URLProtocol ff_sctp_protocol; extern const URLProtocol ff_srtp_protocol; extern const URLProtocol ff_subfile_protocol; +extern const URLProtocol ff_tee_protocol; extern const URLProtocol ff_tcp_protocol; extern const URLProtocol ff_tls_gnutls_protocol; extern const URLProtocol ff_tls_schannel_protocol; diff --git a/libavformat/teeproto.c b/libavformat/teeproto.c new file mode 100644 index 0000000000..62055de990 --- /dev/null +++ b/libavformat/teeproto.c @@ -0,0 +1,137 @@ +/* + * Tee output protocol + * Copyright (c) 2016 Michael Niedermayer + * + * 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/opt.h" +#include "avformat.h" +#include "avio_internal.h" + +typedef struct ChildContext { + URLContext *url_context; +} ChildContext; + +typedef struct TeeContext { + const AVClass *class; + int child_count; + ChildContext *child; +} TeeContext; + +static const AVOption tee_options[] = { + { NULL } +}; + +static const AVClass tee_class = { + .class_name = "tee", + .item_name = av_default_item_name, + .option = tee_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static const char *const child_delim = "|"; + +static int tee_write(URLContext *h, const unsigned char *buf, int size) +{ + TeeContext *c = h->priv_data; + int i; + int main_ret = size; + + for (i=0; ichild_count; i++) { + int ret = ffurl_write(c->child[i].url_context, buf, size); + if (ret < 0) + main_ret = ret; + } + return main_ret; +} + +static int tee_close(URLContext *h) +{ + TeeContext *c = h->priv_data; + int i; + int main_ret = 0; + + for (i=0; ichild_count; i++) { + int ret = ffurl_closep(&c->child[i].url_context); + if (ret < 0) + main_ret = ret; + } + + av_freep(&c->child); + c->child_count = 0; + return main_ret; +} + +static int tee_open(URLContext *h, const char *filename, int flags) +{ + TeeContext *c = h->priv_data; + int ret, i; + + av_strstart(filename, "tee:", &filename); + + if (flags & AVIO_FLAG_READ) + return AVERROR(ENOSYS); + + while (*filename) { + char *child_name = av_get_token(&filename, child_delim); + void *tmp; + if (!child_name) { + ret = AVERROR(ENOMEM); + goto fail; + } + + tmp = av_realloc_array(c->child, c->child_count + 1, sizeof(*c->child)); + if (!tmp) { + ret = AVERROR(ENOMEM); + goto fail; + } + c->child = tmp; + memset(&c->child[c->child_count], 0, sizeof(&c->child[c->child_count])); + + ret = ffurl_open_whitelist(&c->child[c->child_count].url_context, child_name, flags, + &h->interrupt_callback, /*AVDictionary **options*/NULL, + h->protocol_whitelist, h->protocol_blacklist, + h); + av_free(child_name); + if (ret < 0) + goto fail; + c->child_count++; + + if (strspn(filename, child_delim)) + filename++; + } + + h->is_streamed = 0; + for (i=0; ichild_count; i++) { + h->is_streamed |= c->child[i].url_context->is_streamed; + } + + return 0; +fail: + tee_close(h); + return ret; +} +const URLProtocol ff_tee_protocol = { + .name = "tee", + .url_open = tee_open, + .url_write = tee_write, + .priv_data_size = sizeof(TeeContext), + .priv_data_class = &tee_class, + .default_whitelist = "crypto,file,http,https,httpproxy,rtmp,tcp,tls" +}; diff --git a/libavformat/version.h b/libavformat/version.h index 3921ee5eab..307542e4ae 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -32,7 +32,7 @@ // Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium) // Also please add any ticket numbers that you belive might be affected here #define LIBAVFORMAT_VERSION_MAJOR 57 -#define LIBAVFORMAT_VERSION_MINOR 42 +#define LIBAVFORMAT_VERSION_MINOR 43 #define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \