1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * Tee pseudo-muxer
3cabdff1aSopenharmony_ci * Copyright (c) 2012 Nicolas George
4cabdff1aSopenharmony_ci *
5cabdff1aSopenharmony_ci * This file is part of FFmpeg.
6cabdff1aSopenharmony_ci *
7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public License
9cabdff1aSopenharmony_ci * as published by the Free Software Foundation; either
10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
11cabdff1aSopenharmony_ci *
12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15cabdff1aSopenharmony_ci * GNU Lesser General Public License for more details.
16cabdff1aSopenharmony_ci *
17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public License
18cabdff1aSopenharmony_ci * along with FFmpeg; if not, write to the Free Software * Foundation, Inc.,
19cabdff1aSopenharmony_ci * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20cabdff1aSopenharmony_ci */
21cabdff1aSopenharmony_ci
22cabdff1aSopenharmony_ci
23cabdff1aSopenharmony_ci#include "libavutil/avutil.h"
24cabdff1aSopenharmony_ci#include "libavutil/avstring.h"
25cabdff1aSopenharmony_ci#include "libavutil/opt.h"
26cabdff1aSopenharmony_ci#include "libavcodec/bsf.h"
27cabdff1aSopenharmony_ci#include "internal.h"
28cabdff1aSopenharmony_ci#include "avformat.h"
29cabdff1aSopenharmony_ci#include "mux.h"
30cabdff1aSopenharmony_ci#include "tee_common.h"
31cabdff1aSopenharmony_ci
32cabdff1aSopenharmony_citypedef enum {
33cabdff1aSopenharmony_ci    ON_SLAVE_FAILURE_ABORT  = 1,
34cabdff1aSopenharmony_ci    ON_SLAVE_FAILURE_IGNORE = 2
35cabdff1aSopenharmony_ci} SlaveFailurePolicy;
36cabdff1aSopenharmony_ci
37cabdff1aSopenharmony_ci#define DEFAULT_SLAVE_FAILURE_POLICY ON_SLAVE_FAILURE_ABORT
38cabdff1aSopenharmony_ci
39cabdff1aSopenharmony_citypedef struct {
40cabdff1aSopenharmony_ci    AVFormatContext *avf;
41cabdff1aSopenharmony_ci    AVBSFContext **bsfs; ///< bitstream filters per stream
42cabdff1aSopenharmony_ci
43cabdff1aSopenharmony_ci    SlaveFailurePolicy on_fail;
44cabdff1aSopenharmony_ci    int use_fifo;
45cabdff1aSopenharmony_ci    AVDictionary *fifo_options;
46cabdff1aSopenharmony_ci
47cabdff1aSopenharmony_ci    /** map from input to output streams indexes,
48cabdff1aSopenharmony_ci     * disabled output streams are set to -1 */
49cabdff1aSopenharmony_ci    int *stream_map;
50cabdff1aSopenharmony_ci    int header_written;
51cabdff1aSopenharmony_ci} TeeSlave;
52cabdff1aSopenharmony_ci
53cabdff1aSopenharmony_citypedef struct TeeContext {
54cabdff1aSopenharmony_ci    const AVClass *class;
55cabdff1aSopenharmony_ci    unsigned nb_slaves;
56cabdff1aSopenharmony_ci    unsigned nb_alive;
57cabdff1aSopenharmony_ci    TeeSlave *slaves;
58cabdff1aSopenharmony_ci    int use_fifo;
59cabdff1aSopenharmony_ci    AVDictionary *fifo_options;
60cabdff1aSopenharmony_ci} TeeContext;
61cabdff1aSopenharmony_ci
62cabdff1aSopenharmony_cistatic const char *const slave_delim     = "|";
63cabdff1aSopenharmony_cistatic const char *const slave_bsfs_spec_sep = "/";
64cabdff1aSopenharmony_cistatic const char *const slave_select_sep = ",";
65cabdff1aSopenharmony_ci
66cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(TeeContext, x)
67cabdff1aSopenharmony_cistatic const AVOption options[] = {
68cabdff1aSopenharmony_ci        {"use_fifo", "Use fifo pseudo-muxer to separate actual muxers from encoder",
69cabdff1aSopenharmony_ci         OFFSET(use_fifo), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM},
70cabdff1aSopenharmony_ci        {"fifo_options", "fifo pseudo-muxer options", OFFSET(fifo_options),
71cabdff1aSopenharmony_ci         AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM},
72cabdff1aSopenharmony_ci        {NULL}
73cabdff1aSopenharmony_ci};
74cabdff1aSopenharmony_ci
75cabdff1aSopenharmony_cistatic const AVClass tee_muxer_class = {
76cabdff1aSopenharmony_ci    .class_name = "Tee muxer",
77cabdff1aSopenharmony_ci    .item_name  = av_default_item_name,
78cabdff1aSopenharmony_ci    .option = options,
79cabdff1aSopenharmony_ci    .version    = LIBAVUTIL_VERSION_INT,
80cabdff1aSopenharmony_ci};
81cabdff1aSopenharmony_ci
82cabdff1aSopenharmony_cistatic inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *tee_slave)
83cabdff1aSopenharmony_ci{
84cabdff1aSopenharmony_ci    if (!opt) {
85cabdff1aSopenharmony_ci        tee_slave->on_fail = DEFAULT_SLAVE_FAILURE_POLICY;
86cabdff1aSopenharmony_ci        return 0;
87cabdff1aSopenharmony_ci    } else if (!av_strcasecmp("abort", opt)) {
88cabdff1aSopenharmony_ci        tee_slave->on_fail = ON_SLAVE_FAILURE_ABORT;
89cabdff1aSopenharmony_ci        return 0;
90cabdff1aSopenharmony_ci    } else if (!av_strcasecmp("ignore", opt)) {
91cabdff1aSopenharmony_ci        tee_slave->on_fail = ON_SLAVE_FAILURE_IGNORE;
92cabdff1aSopenharmony_ci        return 0;
93cabdff1aSopenharmony_ci    }
94cabdff1aSopenharmony_ci    /* Set failure behaviour to abort, so invalid option error will not be ignored */
95cabdff1aSopenharmony_ci    tee_slave->on_fail = ON_SLAVE_FAILURE_ABORT;
96cabdff1aSopenharmony_ci    return AVERROR(EINVAL);
97cabdff1aSopenharmony_ci}
98cabdff1aSopenharmony_ci
99cabdff1aSopenharmony_cistatic int parse_slave_fifo_policy(const char *use_fifo, TeeSlave *tee_slave)
100cabdff1aSopenharmony_ci{
101cabdff1aSopenharmony_ci    /*TODO - change this to use proper function for parsing boolean
102cabdff1aSopenharmony_ci     *       options when there is one */
103cabdff1aSopenharmony_ci    if (av_match_name(use_fifo, "true,y,yes,enable,enabled,on,1")) {
104cabdff1aSopenharmony_ci        tee_slave->use_fifo = 1;
105cabdff1aSopenharmony_ci    } else if (av_match_name(use_fifo, "false,n,no,disable,disabled,off,0")) {
106cabdff1aSopenharmony_ci        tee_slave->use_fifo = 0;
107cabdff1aSopenharmony_ci    } else {
108cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
109cabdff1aSopenharmony_ci    }
110cabdff1aSopenharmony_ci    return 0;
111cabdff1aSopenharmony_ci}
112cabdff1aSopenharmony_ci
113cabdff1aSopenharmony_cistatic int parse_slave_fifo_options(const char *fifo_options, TeeSlave *tee_slave)
114cabdff1aSopenharmony_ci{
115cabdff1aSopenharmony_ci    return av_dict_parse_string(&tee_slave->fifo_options, fifo_options, "=", ":", 0);
116cabdff1aSopenharmony_ci}
117cabdff1aSopenharmony_ci
118cabdff1aSopenharmony_cistatic int close_slave(TeeSlave *tee_slave)
119cabdff1aSopenharmony_ci{
120cabdff1aSopenharmony_ci    AVFormatContext *avf;
121cabdff1aSopenharmony_ci    unsigned i;
122cabdff1aSopenharmony_ci    int ret = 0;
123cabdff1aSopenharmony_ci
124cabdff1aSopenharmony_ci    av_dict_free(&tee_slave->fifo_options);
125cabdff1aSopenharmony_ci    avf = tee_slave->avf;
126cabdff1aSopenharmony_ci    if (!avf)
127cabdff1aSopenharmony_ci        return 0;
128cabdff1aSopenharmony_ci
129cabdff1aSopenharmony_ci    if (tee_slave->header_written)
130cabdff1aSopenharmony_ci        ret = av_write_trailer(avf);
131cabdff1aSopenharmony_ci
132cabdff1aSopenharmony_ci    if (tee_slave->bsfs) {
133cabdff1aSopenharmony_ci        for (i = 0; i < avf->nb_streams; ++i)
134cabdff1aSopenharmony_ci            av_bsf_free(&tee_slave->bsfs[i]);
135cabdff1aSopenharmony_ci    }
136cabdff1aSopenharmony_ci    av_freep(&tee_slave->stream_map);
137cabdff1aSopenharmony_ci    av_freep(&tee_slave->bsfs);
138cabdff1aSopenharmony_ci
139cabdff1aSopenharmony_ci    ff_format_io_close(avf, &avf->pb);
140cabdff1aSopenharmony_ci    avformat_free_context(avf);
141cabdff1aSopenharmony_ci    tee_slave->avf = NULL;
142cabdff1aSopenharmony_ci    return ret;
143cabdff1aSopenharmony_ci}
144cabdff1aSopenharmony_ci
145cabdff1aSopenharmony_cistatic void close_slaves(AVFormatContext *avf)
146cabdff1aSopenharmony_ci{
147cabdff1aSopenharmony_ci    TeeContext *tee = avf->priv_data;
148cabdff1aSopenharmony_ci    unsigned i;
149cabdff1aSopenharmony_ci
150cabdff1aSopenharmony_ci    for (i = 0; i < tee->nb_slaves; i++) {
151cabdff1aSopenharmony_ci        close_slave(&tee->slaves[i]);
152cabdff1aSopenharmony_ci    }
153cabdff1aSopenharmony_ci    av_freep(&tee->slaves);
154cabdff1aSopenharmony_ci}
155cabdff1aSopenharmony_ci
156cabdff1aSopenharmony_cistatic int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave)
157cabdff1aSopenharmony_ci{
158cabdff1aSopenharmony_ci    int i, ret;
159cabdff1aSopenharmony_ci    AVDictionary *options = NULL, *bsf_options = NULL;
160cabdff1aSopenharmony_ci    AVDictionaryEntry *entry;
161cabdff1aSopenharmony_ci    char *filename;
162cabdff1aSopenharmony_ci    char *format = NULL, *select = NULL, *on_fail = NULL;
163cabdff1aSopenharmony_ci    char *use_fifo = NULL, *fifo_options_str = NULL;
164cabdff1aSopenharmony_ci    AVFormatContext *avf2 = NULL;
165cabdff1aSopenharmony_ci    AVStream *st, *st2;
166cabdff1aSopenharmony_ci    int stream_count;
167cabdff1aSopenharmony_ci    int fullret;
168cabdff1aSopenharmony_ci    char *subselect = NULL, *next_subselect = NULL, *first_subselect = NULL, *tmp_select = NULL;
169cabdff1aSopenharmony_ci
170cabdff1aSopenharmony_ci    if ((ret = ff_tee_parse_slave_options(avf, slave, &options, &filename)) < 0)
171cabdff1aSopenharmony_ci        return ret;
172cabdff1aSopenharmony_ci
173cabdff1aSopenharmony_ci#define CONSUME_OPTION(option, field, action) do {                      \
174cabdff1aSopenharmony_ci        if ((entry = av_dict_get(options, option, NULL, 0))) {          \
175cabdff1aSopenharmony_ci            field = entry->value;                                       \
176cabdff1aSopenharmony_ci            { action }                                                  \
177cabdff1aSopenharmony_ci            av_dict_set(&options, option, NULL, 0);                     \
178cabdff1aSopenharmony_ci        }                                                               \
179cabdff1aSopenharmony_ci    } while (0)
180cabdff1aSopenharmony_ci#define STEAL_OPTION(option, field)                                     \
181cabdff1aSopenharmony_ci    CONSUME_OPTION(option, field,                                       \
182cabdff1aSopenharmony_ci                   entry->value = NULL; /* prevent it from being freed */)
183cabdff1aSopenharmony_ci#define PROCESS_OPTION(option, field, function, on_error)               \
184cabdff1aSopenharmony_ci    CONSUME_OPTION(option, field, if ((ret = function) < 0) { { on_error } goto end; })
185cabdff1aSopenharmony_ci
186cabdff1aSopenharmony_ci    STEAL_OPTION("f", format);
187cabdff1aSopenharmony_ci    STEAL_OPTION("select", select);
188cabdff1aSopenharmony_ci    PROCESS_OPTION("onfail", on_fail,
189cabdff1aSopenharmony_ci                   parse_slave_failure_policy_option(on_fail, tee_slave),
190cabdff1aSopenharmony_ci                   av_log(avf, AV_LOG_ERROR, "Invalid onfail option value, "
191cabdff1aSopenharmony_ci                          "valid options are 'abort' and 'ignore'\n"););
192cabdff1aSopenharmony_ci    PROCESS_OPTION("use_fifo", use_fifo,
193cabdff1aSopenharmony_ci                   parse_slave_fifo_policy(use_fifo, tee_slave),
194cabdff1aSopenharmony_ci                   av_log(avf, AV_LOG_ERROR, "Error parsing fifo options: %s\n",
195cabdff1aSopenharmony_ci                          av_err2str(ret)););
196cabdff1aSopenharmony_ci    PROCESS_OPTION("fifo_options", fifo_options_str,
197cabdff1aSopenharmony_ci                   parse_slave_fifo_options(fifo_options_str, tee_slave), ;);
198cabdff1aSopenharmony_ci    entry = NULL;
199cabdff1aSopenharmony_ci    while ((entry = av_dict_get(options, "bsfs", entry, AV_DICT_IGNORE_SUFFIX))) {
200cabdff1aSopenharmony_ci        /* trim out strlen("bsfs") characters from key */
201cabdff1aSopenharmony_ci        av_dict_set(&bsf_options, entry->key + 4, entry->value, 0);
202cabdff1aSopenharmony_ci        av_dict_set(&options, entry->key, NULL, 0);
203cabdff1aSopenharmony_ci    }
204cabdff1aSopenharmony_ci
205cabdff1aSopenharmony_ci    if (tee_slave->use_fifo) {
206cabdff1aSopenharmony_ci
207cabdff1aSopenharmony_ci        if (options) {
208cabdff1aSopenharmony_ci            char *format_options_str = NULL;
209cabdff1aSopenharmony_ci            ret = av_dict_get_string(options, &format_options_str, '=', ':');
210cabdff1aSopenharmony_ci            if (ret < 0)
211cabdff1aSopenharmony_ci                goto end;
212cabdff1aSopenharmony_ci
213cabdff1aSopenharmony_ci            ret = av_dict_set(&tee_slave->fifo_options, "format_opts", format_options_str,
214cabdff1aSopenharmony_ci                              AV_DICT_DONT_STRDUP_VAL);
215cabdff1aSopenharmony_ci            if (ret < 0)
216cabdff1aSopenharmony_ci                goto end;
217cabdff1aSopenharmony_ci        }
218cabdff1aSopenharmony_ci
219cabdff1aSopenharmony_ci        if (format) {
220cabdff1aSopenharmony_ci            ret = av_dict_set(&tee_slave->fifo_options, "fifo_format", format,
221cabdff1aSopenharmony_ci                              AV_DICT_DONT_STRDUP_VAL);
222cabdff1aSopenharmony_ci            format = NULL;
223cabdff1aSopenharmony_ci            if (ret < 0)
224cabdff1aSopenharmony_ci                goto end;
225cabdff1aSopenharmony_ci        }
226cabdff1aSopenharmony_ci
227cabdff1aSopenharmony_ci        av_dict_free(&options);
228cabdff1aSopenharmony_ci        options = tee_slave->fifo_options;
229cabdff1aSopenharmony_ci        tee_slave->fifo_options = NULL;
230cabdff1aSopenharmony_ci    }
231cabdff1aSopenharmony_ci    ret = avformat_alloc_output_context2(&avf2, NULL,
232cabdff1aSopenharmony_ci                                         tee_slave->use_fifo ? "fifo" :format, filename);
233cabdff1aSopenharmony_ci    if (ret < 0)
234cabdff1aSopenharmony_ci        goto end;
235cabdff1aSopenharmony_ci    tee_slave->avf = avf2;
236cabdff1aSopenharmony_ci    av_dict_copy(&avf2->metadata, avf->metadata, 0);
237cabdff1aSopenharmony_ci    avf2->opaque   = avf->opaque;
238cabdff1aSopenharmony_ci    avf2->io_open  = avf->io_open;
239cabdff1aSopenharmony_ci    avf2->io_close = avf->io_close;
240cabdff1aSopenharmony_ci    avf2->io_close2 = avf->io_close2;
241cabdff1aSopenharmony_ci    avf2->interrupt_callback = avf->interrupt_callback;
242cabdff1aSopenharmony_ci    avf2->flags = avf->flags;
243cabdff1aSopenharmony_ci    avf2->strict_std_compliance = avf->strict_std_compliance;
244cabdff1aSopenharmony_ci
245cabdff1aSopenharmony_ci    tee_slave->stream_map = av_calloc(avf->nb_streams, sizeof(*tee_slave->stream_map));
246cabdff1aSopenharmony_ci    if (!tee_slave->stream_map) {
247cabdff1aSopenharmony_ci        ret = AVERROR(ENOMEM);
248cabdff1aSopenharmony_ci        goto end;
249cabdff1aSopenharmony_ci    }
250cabdff1aSopenharmony_ci
251cabdff1aSopenharmony_ci    stream_count = 0;
252cabdff1aSopenharmony_ci    for (i = 0; i < avf->nb_streams; i++) {
253cabdff1aSopenharmony_ci        st = avf->streams[i];
254cabdff1aSopenharmony_ci        if (select) {
255cabdff1aSopenharmony_ci            tmp_select = av_strdup(select);  // av_strtok is destructive so we regenerate it in each loop
256cabdff1aSopenharmony_ci            if (!tmp_select) {
257cabdff1aSopenharmony_ci                ret = AVERROR(ENOMEM);
258cabdff1aSopenharmony_ci                goto end;
259cabdff1aSopenharmony_ci            }
260cabdff1aSopenharmony_ci            fullret = 0;
261cabdff1aSopenharmony_ci            first_subselect = tmp_select;
262cabdff1aSopenharmony_ci            next_subselect = NULL;
263cabdff1aSopenharmony_ci            while (subselect = av_strtok(first_subselect, slave_select_sep, &next_subselect)) {
264cabdff1aSopenharmony_ci                first_subselect = NULL;
265cabdff1aSopenharmony_ci
266cabdff1aSopenharmony_ci                ret = avformat_match_stream_specifier(avf, avf->streams[i], subselect);
267cabdff1aSopenharmony_ci                if (ret < 0) {
268cabdff1aSopenharmony_ci                    av_log(avf, AV_LOG_ERROR,
269cabdff1aSopenharmony_ci                           "Invalid stream specifier '%s' for output '%s'\n",
270cabdff1aSopenharmony_ci                           subselect, slave);
271cabdff1aSopenharmony_ci                    goto end;
272cabdff1aSopenharmony_ci                }
273cabdff1aSopenharmony_ci                if (ret != 0) {
274cabdff1aSopenharmony_ci                    fullret = 1; // match
275cabdff1aSopenharmony_ci                    break;
276cabdff1aSopenharmony_ci                }
277cabdff1aSopenharmony_ci            }
278cabdff1aSopenharmony_ci            av_freep(&tmp_select);
279cabdff1aSopenharmony_ci
280cabdff1aSopenharmony_ci            if (fullret == 0) { /* no match */
281cabdff1aSopenharmony_ci                tee_slave->stream_map[i] = -1;
282cabdff1aSopenharmony_ci                continue;
283cabdff1aSopenharmony_ci            }
284cabdff1aSopenharmony_ci        }
285cabdff1aSopenharmony_ci        tee_slave->stream_map[i] = stream_count++;
286cabdff1aSopenharmony_ci
287cabdff1aSopenharmony_ci        if (!(st2 = avformat_new_stream(avf2, NULL))) {
288cabdff1aSopenharmony_ci            ret = AVERROR(ENOMEM);
289cabdff1aSopenharmony_ci            goto end;
290cabdff1aSopenharmony_ci        }
291cabdff1aSopenharmony_ci
292cabdff1aSopenharmony_ci        ret = ff_stream_encode_params_copy(st2, st);
293cabdff1aSopenharmony_ci        if (ret < 0)
294cabdff1aSopenharmony_ci            goto end;
295cabdff1aSopenharmony_ci    }
296cabdff1aSopenharmony_ci
297cabdff1aSopenharmony_ci    ret = ff_format_output_open(avf2, filename, &options);
298cabdff1aSopenharmony_ci    if (ret < 0) {
299cabdff1aSopenharmony_ci        av_log(avf, AV_LOG_ERROR, "Slave '%s': error opening: %s\n", slave,
300cabdff1aSopenharmony_ci               av_err2str(ret));
301cabdff1aSopenharmony_ci        goto end;
302cabdff1aSopenharmony_ci    }
303cabdff1aSopenharmony_ci
304cabdff1aSopenharmony_ci    if ((ret = avformat_write_header(avf2, &options)) < 0) {
305cabdff1aSopenharmony_ci        av_log(avf, AV_LOG_ERROR, "Slave '%s': error writing header: %s\n",
306cabdff1aSopenharmony_ci               slave, av_err2str(ret));
307cabdff1aSopenharmony_ci        goto end;
308cabdff1aSopenharmony_ci    }
309cabdff1aSopenharmony_ci    tee_slave->header_written = 1;
310cabdff1aSopenharmony_ci
311cabdff1aSopenharmony_ci    tee_slave->bsfs = av_calloc(avf2->nb_streams, sizeof(*tee_slave->bsfs));
312cabdff1aSopenharmony_ci    if (!tee_slave->bsfs) {
313cabdff1aSopenharmony_ci        ret = AVERROR(ENOMEM);
314cabdff1aSopenharmony_ci        goto end;
315cabdff1aSopenharmony_ci    }
316cabdff1aSopenharmony_ci
317cabdff1aSopenharmony_ci    entry = NULL;
318cabdff1aSopenharmony_ci    while (entry = av_dict_get(bsf_options, "", NULL, AV_DICT_IGNORE_SUFFIX)) {
319cabdff1aSopenharmony_ci        const char *spec = entry->key;
320cabdff1aSopenharmony_ci        if (*spec) {
321cabdff1aSopenharmony_ci            if (strspn(spec, slave_bsfs_spec_sep) != 1) {
322cabdff1aSopenharmony_ci                av_log(avf, AV_LOG_ERROR,
323cabdff1aSopenharmony_ci                       "Specifier separator in '%s' is '%c', but only characters '%s' "
324cabdff1aSopenharmony_ci                       "are allowed\n", entry->key, *spec, slave_bsfs_spec_sep);
325cabdff1aSopenharmony_ci                ret = AVERROR(EINVAL);
326cabdff1aSopenharmony_ci                goto end;
327cabdff1aSopenharmony_ci            }
328cabdff1aSopenharmony_ci            spec++; /* consume separator */
329cabdff1aSopenharmony_ci        }
330cabdff1aSopenharmony_ci
331cabdff1aSopenharmony_ci        for (i = 0; i < avf2->nb_streams; i++) {
332cabdff1aSopenharmony_ci            ret = avformat_match_stream_specifier(avf2, avf2->streams[i], spec);
333cabdff1aSopenharmony_ci            if (ret < 0) {
334cabdff1aSopenharmony_ci                av_log(avf, AV_LOG_ERROR,
335cabdff1aSopenharmony_ci                       "Invalid stream specifier '%s' in bsfs option '%s' for slave "
336cabdff1aSopenharmony_ci                       "output '%s'\n", spec, entry->key, filename);
337cabdff1aSopenharmony_ci                goto end;
338cabdff1aSopenharmony_ci            }
339cabdff1aSopenharmony_ci
340cabdff1aSopenharmony_ci            if (ret > 0) {
341cabdff1aSopenharmony_ci                av_log(avf, AV_LOG_DEBUG, "spec:%s bsfs:%s matches stream %d of slave "
342cabdff1aSopenharmony_ci                       "output '%s'\n", spec, entry->value, i, filename);
343cabdff1aSopenharmony_ci                if (tee_slave->bsfs[i]) {
344cabdff1aSopenharmony_ci                    av_log(avf, AV_LOG_WARNING,
345cabdff1aSopenharmony_ci                           "Duplicate bsfs specification associated to stream %d of slave "
346cabdff1aSopenharmony_ci                           "output '%s', filters will be ignored\n", i, filename);
347cabdff1aSopenharmony_ci                    continue;
348cabdff1aSopenharmony_ci                }
349cabdff1aSopenharmony_ci                ret = av_bsf_list_parse_str(entry->value, &tee_slave->bsfs[i]);
350cabdff1aSopenharmony_ci                if (ret < 0) {
351cabdff1aSopenharmony_ci                    av_log(avf, AV_LOG_ERROR,
352cabdff1aSopenharmony_ci                           "Error parsing bitstream filter sequence '%s' associated to "
353cabdff1aSopenharmony_ci                           "stream %d of slave output '%s'\n", entry->value, i, filename);
354cabdff1aSopenharmony_ci                    goto end;
355cabdff1aSopenharmony_ci                }
356cabdff1aSopenharmony_ci            }
357cabdff1aSopenharmony_ci        }
358cabdff1aSopenharmony_ci
359cabdff1aSopenharmony_ci        av_dict_set(&bsf_options, entry->key, NULL, 0);
360cabdff1aSopenharmony_ci    }
361cabdff1aSopenharmony_ci
362cabdff1aSopenharmony_ci    for (i = 0; i < avf->nb_streams; i++){
363cabdff1aSopenharmony_ci        int target_stream = tee_slave->stream_map[i];
364cabdff1aSopenharmony_ci        if (target_stream < 0)
365cabdff1aSopenharmony_ci            continue;
366cabdff1aSopenharmony_ci
367cabdff1aSopenharmony_ci        if (!tee_slave->bsfs[target_stream]) {
368cabdff1aSopenharmony_ci            /* Add pass-through bitstream filter */
369cabdff1aSopenharmony_ci            ret = av_bsf_get_null_filter(&tee_slave->bsfs[target_stream]);
370cabdff1aSopenharmony_ci            if (ret < 0) {
371cabdff1aSopenharmony_ci                av_log(avf, AV_LOG_ERROR,
372cabdff1aSopenharmony_ci                       "Failed to create pass-through bitstream filter: %s\n",
373cabdff1aSopenharmony_ci                       av_err2str(ret));
374cabdff1aSopenharmony_ci                goto end;
375cabdff1aSopenharmony_ci            }
376cabdff1aSopenharmony_ci        }
377cabdff1aSopenharmony_ci
378cabdff1aSopenharmony_ci        tee_slave->bsfs[target_stream]->time_base_in = avf->streams[i]->time_base;
379cabdff1aSopenharmony_ci        ret = avcodec_parameters_copy(tee_slave->bsfs[target_stream]->par_in,
380cabdff1aSopenharmony_ci                                      avf->streams[i]->codecpar);
381cabdff1aSopenharmony_ci        if (ret < 0)
382cabdff1aSopenharmony_ci            goto end;
383cabdff1aSopenharmony_ci
384cabdff1aSopenharmony_ci        ret = av_bsf_init(tee_slave->bsfs[target_stream]);
385cabdff1aSopenharmony_ci        if (ret < 0) {
386cabdff1aSopenharmony_ci            av_log(avf, AV_LOG_ERROR,
387cabdff1aSopenharmony_ci            "Failed to initialize bitstream filter(s): %s\n",
388cabdff1aSopenharmony_ci            av_err2str(ret));
389cabdff1aSopenharmony_ci            goto end;
390cabdff1aSopenharmony_ci        }
391cabdff1aSopenharmony_ci    }
392cabdff1aSopenharmony_ci
393cabdff1aSopenharmony_ci    if (options) {
394cabdff1aSopenharmony_ci        entry = NULL;
395cabdff1aSopenharmony_ci        while ((entry = av_dict_get(options, "", entry, AV_DICT_IGNORE_SUFFIX)))
396cabdff1aSopenharmony_ci            av_log(avf2, AV_LOG_ERROR, "Unknown option '%s'\n", entry->key);
397cabdff1aSopenharmony_ci        ret = AVERROR_OPTION_NOT_FOUND;
398cabdff1aSopenharmony_ci        goto end;
399cabdff1aSopenharmony_ci    }
400cabdff1aSopenharmony_ci
401cabdff1aSopenharmony_ciend:
402cabdff1aSopenharmony_ci    av_free(format);
403cabdff1aSopenharmony_ci    av_free(select);
404cabdff1aSopenharmony_ci    av_dict_free(&options);
405cabdff1aSopenharmony_ci    av_dict_free(&bsf_options);
406cabdff1aSopenharmony_ci    av_freep(&tmp_select);
407cabdff1aSopenharmony_ci    return ret;
408cabdff1aSopenharmony_ci}
409cabdff1aSopenharmony_ci
410cabdff1aSopenharmony_cistatic void log_slave(TeeSlave *slave, void *log_ctx, int log_level)
411cabdff1aSopenharmony_ci{
412cabdff1aSopenharmony_ci    int i;
413cabdff1aSopenharmony_ci    av_log(log_ctx, log_level, "filename:'%s' format:%s\n",
414cabdff1aSopenharmony_ci           slave->avf->url, slave->avf->oformat->name);
415cabdff1aSopenharmony_ci    for (i = 0; i < slave->avf->nb_streams; i++) {
416cabdff1aSopenharmony_ci        AVStream *st = slave->avf->streams[i];
417cabdff1aSopenharmony_ci        AVBSFContext *bsf = slave->bsfs[i];
418cabdff1aSopenharmony_ci        const char *bsf_name;
419cabdff1aSopenharmony_ci
420cabdff1aSopenharmony_ci        av_log(log_ctx, log_level, "    stream:%d codec:%s type:%s",
421cabdff1aSopenharmony_ci               i, avcodec_get_name(st->codecpar->codec_id),
422cabdff1aSopenharmony_ci               av_get_media_type_string(st->codecpar->codec_type));
423cabdff1aSopenharmony_ci
424cabdff1aSopenharmony_ci        bsf_name = bsf->filter->priv_class ?
425cabdff1aSopenharmony_ci                   bsf->filter->priv_class->item_name(bsf) : bsf->filter->name;
426cabdff1aSopenharmony_ci        av_log(log_ctx, log_level, " bsfs: %s\n", bsf_name);
427cabdff1aSopenharmony_ci    }
428cabdff1aSopenharmony_ci}
429cabdff1aSopenharmony_ci
430cabdff1aSopenharmony_cistatic int tee_process_slave_failure(AVFormatContext *avf, unsigned slave_idx, int err_n)
431cabdff1aSopenharmony_ci{
432cabdff1aSopenharmony_ci    TeeContext *tee = avf->priv_data;
433cabdff1aSopenharmony_ci    TeeSlave *tee_slave = &tee->slaves[slave_idx];
434cabdff1aSopenharmony_ci
435cabdff1aSopenharmony_ci    tee->nb_alive--;
436cabdff1aSopenharmony_ci
437cabdff1aSopenharmony_ci    close_slave(tee_slave);
438cabdff1aSopenharmony_ci
439cabdff1aSopenharmony_ci    if (!tee->nb_alive) {
440cabdff1aSopenharmony_ci        av_log(avf, AV_LOG_ERROR, "All tee outputs failed.\n");
441cabdff1aSopenharmony_ci        return err_n;
442cabdff1aSopenharmony_ci    } else if (tee_slave->on_fail == ON_SLAVE_FAILURE_ABORT) {
443cabdff1aSopenharmony_ci        av_log(avf, AV_LOG_ERROR, "Slave muxer #%u failed, aborting.\n", slave_idx);
444cabdff1aSopenharmony_ci        return err_n;
445cabdff1aSopenharmony_ci    } else {
446cabdff1aSopenharmony_ci        av_log(avf, AV_LOG_ERROR, "Slave muxer #%u failed: %s, continuing with %u/%u slaves.\n",
447cabdff1aSopenharmony_ci               slave_idx, av_err2str(err_n), tee->nb_alive, tee->nb_slaves);
448cabdff1aSopenharmony_ci        return 0;
449cabdff1aSopenharmony_ci    }
450cabdff1aSopenharmony_ci}
451cabdff1aSopenharmony_ci
452cabdff1aSopenharmony_cistatic int tee_write_header(AVFormatContext *avf)
453cabdff1aSopenharmony_ci{
454cabdff1aSopenharmony_ci    TeeContext *tee = avf->priv_data;
455cabdff1aSopenharmony_ci    unsigned nb_slaves = 0, i;
456cabdff1aSopenharmony_ci    const char *filename = avf->url;
457cabdff1aSopenharmony_ci    char **slaves = NULL;
458cabdff1aSopenharmony_ci    int ret;
459cabdff1aSopenharmony_ci
460cabdff1aSopenharmony_ci    while (*filename) {
461cabdff1aSopenharmony_ci        char *slave = av_get_token(&filename, slave_delim);
462cabdff1aSopenharmony_ci        if (!slave) {
463cabdff1aSopenharmony_ci            ret = AVERROR(ENOMEM);
464cabdff1aSopenharmony_ci            goto fail;
465cabdff1aSopenharmony_ci        }
466cabdff1aSopenharmony_ci        ret = av_dynarray_add_nofree(&slaves, &nb_slaves, slave);
467cabdff1aSopenharmony_ci        if (ret < 0) {
468cabdff1aSopenharmony_ci            av_free(slave);
469cabdff1aSopenharmony_ci            goto fail;
470cabdff1aSopenharmony_ci        }
471cabdff1aSopenharmony_ci        if (strspn(filename, slave_delim))
472cabdff1aSopenharmony_ci            filename++;
473cabdff1aSopenharmony_ci    }
474cabdff1aSopenharmony_ci
475cabdff1aSopenharmony_ci    if (!FF_ALLOCZ_TYPED_ARRAY(tee->slaves, nb_slaves)) {
476cabdff1aSopenharmony_ci        ret = AVERROR(ENOMEM);
477cabdff1aSopenharmony_ci        goto fail;
478cabdff1aSopenharmony_ci    }
479cabdff1aSopenharmony_ci    tee->nb_slaves = tee->nb_alive = nb_slaves;
480cabdff1aSopenharmony_ci
481cabdff1aSopenharmony_ci    for (i = 0; i < nb_slaves; i++) {
482cabdff1aSopenharmony_ci
483cabdff1aSopenharmony_ci        tee->slaves[i].use_fifo = tee->use_fifo;
484cabdff1aSopenharmony_ci        ret = av_dict_copy(&tee->slaves[i].fifo_options, tee->fifo_options, 0);
485cabdff1aSopenharmony_ci        if (ret < 0)
486cabdff1aSopenharmony_ci            goto fail;
487cabdff1aSopenharmony_ci
488cabdff1aSopenharmony_ci        if ((ret = open_slave(avf, slaves[i], &tee->slaves[i])) < 0) {
489cabdff1aSopenharmony_ci            ret = tee_process_slave_failure(avf, i, ret);
490cabdff1aSopenharmony_ci            if (ret < 0)
491cabdff1aSopenharmony_ci                goto fail;
492cabdff1aSopenharmony_ci        } else {
493cabdff1aSopenharmony_ci            log_slave(&tee->slaves[i], avf, AV_LOG_VERBOSE);
494cabdff1aSopenharmony_ci        }
495cabdff1aSopenharmony_ci        av_freep(&slaves[i]);
496cabdff1aSopenharmony_ci    }
497cabdff1aSopenharmony_ci
498cabdff1aSopenharmony_ci    for (i = 0; i < avf->nb_streams; i++) {
499cabdff1aSopenharmony_ci        int j, mapped = 0;
500cabdff1aSopenharmony_ci        for (j = 0; j < tee->nb_slaves; j++)
501cabdff1aSopenharmony_ci            if (tee->slaves[j].avf)
502cabdff1aSopenharmony_ci                mapped += tee->slaves[j].stream_map[i] >= 0;
503cabdff1aSopenharmony_ci        if (!mapped)
504cabdff1aSopenharmony_ci            av_log(avf, AV_LOG_WARNING, "Input stream #%d is not mapped "
505cabdff1aSopenharmony_ci                   "to any slave.\n", i);
506cabdff1aSopenharmony_ci    }
507cabdff1aSopenharmony_ci    av_free(slaves);
508cabdff1aSopenharmony_ci    return 0;
509cabdff1aSopenharmony_ci
510cabdff1aSopenharmony_cifail:
511cabdff1aSopenharmony_ci    for (i = 0; i < nb_slaves; i++)
512cabdff1aSopenharmony_ci        av_freep(&slaves[i]);
513cabdff1aSopenharmony_ci    close_slaves(avf);
514cabdff1aSopenharmony_ci    av_free(slaves);
515cabdff1aSopenharmony_ci    return ret;
516cabdff1aSopenharmony_ci}
517cabdff1aSopenharmony_ci
518cabdff1aSopenharmony_cistatic int tee_write_trailer(AVFormatContext *avf)
519cabdff1aSopenharmony_ci{
520cabdff1aSopenharmony_ci    TeeContext *tee = avf->priv_data;
521cabdff1aSopenharmony_ci    int ret_all = 0, ret;
522cabdff1aSopenharmony_ci    unsigned i;
523cabdff1aSopenharmony_ci
524cabdff1aSopenharmony_ci    for (i = 0; i < tee->nb_slaves; i++) {
525cabdff1aSopenharmony_ci        if ((ret = close_slave(&tee->slaves[i])) < 0) {
526cabdff1aSopenharmony_ci            ret = tee_process_slave_failure(avf, i, ret);
527cabdff1aSopenharmony_ci            if (!ret_all && ret < 0)
528cabdff1aSopenharmony_ci                ret_all = ret;
529cabdff1aSopenharmony_ci        }
530cabdff1aSopenharmony_ci    }
531cabdff1aSopenharmony_ci    av_freep(&tee->slaves);
532cabdff1aSopenharmony_ci    return ret_all;
533cabdff1aSopenharmony_ci}
534cabdff1aSopenharmony_ci
535cabdff1aSopenharmony_cistatic int tee_write_packet(AVFormatContext *avf, AVPacket *pkt)
536cabdff1aSopenharmony_ci{
537cabdff1aSopenharmony_ci    TeeContext *tee = avf->priv_data;
538cabdff1aSopenharmony_ci    AVFormatContext *avf2;
539cabdff1aSopenharmony_ci    AVBSFContext *bsfs;
540cabdff1aSopenharmony_ci    AVPacket *const pkt2 = ffformatcontext(avf)->pkt;
541cabdff1aSopenharmony_ci    int ret_all = 0, ret;
542cabdff1aSopenharmony_ci    unsigned i, s;
543cabdff1aSopenharmony_ci    int s2;
544cabdff1aSopenharmony_ci
545cabdff1aSopenharmony_ci    for (i = 0; i < tee->nb_slaves; i++) {
546cabdff1aSopenharmony_ci        if (!(avf2 = tee->slaves[i].avf))
547cabdff1aSopenharmony_ci            continue;
548cabdff1aSopenharmony_ci
549cabdff1aSopenharmony_ci        /* Flush slave if pkt is NULL*/
550cabdff1aSopenharmony_ci        if (!pkt) {
551cabdff1aSopenharmony_ci            ret = av_interleaved_write_frame(avf2, NULL);
552cabdff1aSopenharmony_ci            if (ret < 0) {
553cabdff1aSopenharmony_ci                ret = tee_process_slave_failure(avf, i, ret);
554cabdff1aSopenharmony_ci                if (!ret_all && ret < 0)
555cabdff1aSopenharmony_ci                    ret_all = ret;
556cabdff1aSopenharmony_ci            }
557cabdff1aSopenharmony_ci            continue;
558cabdff1aSopenharmony_ci        }
559cabdff1aSopenharmony_ci
560cabdff1aSopenharmony_ci        s = pkt->stream_index;
561cabdff1aSopenharmony_ci        s2 = tee->slaves[i].stream_map[s];
562cabdff1aSopenharmony_ci        if (s2 < 0)
563cabdff1aSopenharmony_ci            continue;
564cabdff1aSopenharmony_ci
565cabdff1aSopenharmony_ci        if ((ret = av_packet_ref(pkt2, pkt)) < 0) {
566cabdff1aSopenharmony_ci            if (!ret_all)
567cabdff1aSopenharmony_ci                ret_all = ret;
568cabdff1aSopenharmony_ci            continue;
569cabdff1aSopenharmony_ci        }
570cabdff1aSopenharmony_ci        bsfs = tee->slaves[i].bsfs[s2];
571cabdff1aSopenharmony_ci        pkt2->stream_index = s2;
572cabdff1aSopenharmony_ci
573cabdff1aSopenharmony_ci        ret = av_bsf_send_packet(bsfs, pkt2);
574cabdff1aSopenharmony_ci        if (ret < 0) {
575cabdff1aSopenharmony_ci            av_packet_unref(pkt2);
576cabdff1aSopenharmony_ci            av_log(avf, AV_LOG_ERROR, "Error while sending packet to bitstream filter: %s\n",
577cabdff1aSopenharmony_ci                   av_err2str(ret));
578cabdff1aSopenharmony_ci            ret = tee_process_slave_failure(avf, i, ret);
579cabdff1aSopenharmony_ci            if (!ret_all && ret < 0)
580cabdff1aSopenharmony_ci                ret_all = ret;
581cabdff1aSopenharmony_ci        }
582cabdff1aSopenharmony_ci
583cabdff1aSopenharmony_ci        while(1) {
584cabdff1aSopenharmony_ci            ret = av_bsf_receive_packet(bsfs, pkt2);
585cabdff1aSopenharmony_ci            if (ret == AVERROR(EAGAIN)) {
586cabdff1aSopenharmony_ci                ret = 0;
587cabdff1aSopenharmony_ci                break;
588cabdff1aSopenharmony_ci            } else if (ret < 0) {
589cabdff1aSopenharmony_ci                break;
590cabdff1aSopenharmony_ci            }
591cabdff1aSopenharmony_ci
592cabdff1aSopenharmony_ci            av_packet_rescale_ts(pkt2, bsfs->time_base_out,
593cabdff1aSopenharmony_ci                                 avf2->streams[s2]->time_base);
594cabdff1aSopenharmony_ci            ret = av_interleaved_write_frame(avf2, pkt2);
595cabdff1aSopenharmony_ci            if (ret < 0)
596cabdff1aSopenharmony_ci                break;
597cabdff1aSopenharmony_ci        };
598cabdff1aSopenharmony_ci
599cabdff1aSopenharmony_ci        if (ret < 0) {
600cabdff1aSopenharmony_ci            ret = tee_process_slave_failure(avf, i, ret);
601cabdff1aSopenharmony_ci            if (!ret_all && ret < 0)
602cabdff1aSopenharmony_ci                ret_all = ret;
603cabdff1aSopenharmony_ci        }
604cabdff1aSopenharmony_ci    }
605cabdff1aSopenharmony_ci    return ret_all;
606cabdff1aSopenharmony_ci}
607cabdff1aSopenharmony_ci
608cabdff1aSopenharmony_ciconst AVOutputFormat ff_tee_muxer = {
609cabdff1aSopenharmony_ci    .name              = "tee",
610cabdff1aSopenharmony_ci    .long_name         = NULL_IF_CONFIG_SMALL("Multiple muxer tee"),
611cabdff1aSopenharmony_ci    .priv_data_size    = sizeof(TeeContext),
612cabdff1aSopenharmony_ci    .write_header      = tee_write_header,
613cabdff1aSopenharmony_ci    .write_trailer     = tee_write_trailer,
614cabdff1aSopenharmony_ci    .write_packet      = tee_write_packet,
615cabdff1aSopenharmony_ci    .priv_class        = &tee_muxer_class,
616cabdff1aSopenharmony_ci    .flags             = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE,
617cabdff1aSopenharmony_ci};
618