xref: /third_party/ffmpeg/libavformat/teeproto.c (revision cabdff1a)
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