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