LCOV - code coverage report
Current view: top level - libavformat - tee.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 334 0.0 %
Date: 2017-12-16 21:16:39 Functions: 0 10 0.0 %

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

Generated by: LCOV version 1.13