1/* 2 * Tee output protocol 3 * Copyright (c) 2016 Michael Niedermayer 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * FFmpeg is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with FFmpeg; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22#include "libavutil/avstring.h" 23#include "libavutil/opt.h" 24#include "avformat.h" 25#include "avio_internal.h" 26#include "tee_common.h" 27 28typedef struct ChildContext { 29 URLContext *url_context; 30} ChildContext; 31 32typedef struct TeeContext { 33 const AVClass *class; 34 int child_count; 35 ChildContext *child; 36} TeeContext; 37 38static const AVOption tee_options[] = { 39 { NULL } 40}; 41 42static const AVClass tee_class = { 43 .class_name = "tee", 44 .item_name = av_default_item_name, 45 .option = tee_options, 46 .version = LIBAVUTIL_VERSION_INT, 47}; 48 49static const char *const child_delim = "|"; 50 51static int tee_write(URLContext *h, const unsigned char *buf, int size) 52{ 53 TeeContext *c = h->priv_data; 54 int i; 55 int main_ret = size; 56 57 for (i=0; i<c->child_count; i++) { 58 int ret = ffurl_write(c->child[i].url_context, buf, size); 59 if (ret < 0) 60 main_ret = ret; 61 } 62 return main_ret; 63} 64 65static int tee_close(URLContext *h) 66{ 67 TeeContext *c = h->priv_data; 68 int i; 69 int main_ret = 0; 70 71 for (i=0; i<c->child_count; i++) { 72 int ret = ffurl_closep(&c->child[i].url_context); 73 if (ret < 0) 74 main_ret = ret; 75 } 76 77 av_freep(&c->child); 78 c->child_count = 0; 79 return main_ret; 80} 81 82static int tee_open(URLContext *h, const char *filename, int flags) 83{ 84 TeeContext *c = h->priv_data; 85 int ret, i; 86 87 av_strstart(filename, "tee:", &filename); 88 89 if (flags & AVIO_FLAG_READ) 90 return AVERROR(ENOSYS); 91 92 while (*filename) { 93 char *child_string = av_get_token(&filename, child_delim); 94 char *child_name = NULL; 95 void *tmp; 96 AVDictionary *options = NULL; 97 if (!child_string) { 98 ret = AVERROR(ENOMEM); 99 goto fail; 100 } 101 102 tmp = av_realloc_array(c->child, c->child_count + 1, sizeof(*c->child)); 103 if (!tmp) { 104 ret = AVERROR(ENOMEM); 105 goto loop_fail; 106 } 107 c->child = tmp; 108 memset(&c->child[c->child_count], 0, sizeof(c->child[c->child_count])); 109 110 ret = ff_tee_parse_slave_options(h, child_string, &options, &child_name); 111 if (ret < 0) 112 goto loop_fail; 113 114 ret = ffurl_open_whitelist(&c->child[c->child_count].url_context, child_name, flags, 115 &h->interrupt_callback, &options, 116 h->protocol_whitelist, h->protocol_blacklist, 117 h); 118loop_fail: 119 av_freep(&child_string); 120 av_dict_free(&options); 121 if (ret < 0) 122 goto fail; 123 c->child_count++; 124 125 if (strspn(filename, child_delim)) 126 filename++; 127 } 128 129 h->is_streamed = 0; 130 for (i=0; i<c->child_count; i++) { 131 h->is_streamed |= c->child[i].url_context->is_streamed; 132 } 133 134 return 0; 135fail: 136 tee_close(h); 137 return ret; 138} 139const URLProtocol ff_tee_protocol = { 140 .name = "tee", 141 .url_open = tee_open, 142 .url_write = tee_write, 143 .url_close = tee_close, 144 .priv_data_size = sizeof(TeeContext), 145 .priv_data_class = &tee_class, 146 .default_whitelist = "crypto,file,http,https,httpproxy,rtmp,tcp,tls" 147}; 148