diff options
author | Jan Sebechlebsky <sebechlebskyjan@gmail.com> | 2016-04-21 00:48:38 +0300 |
---|---|---|
committer | Marton Balint <cus@passwd.hu> | 2016-04-22 20:04:27 +0200 |
commit | 2063f3ed9c2f7841b48d3d7635f42fafdace9de8 (patch) | |
tree | b5011107fa44a95ee686464b101d3c4ab998af07 /libavformat/tee.c | |
parent | f9d7e9feec2a0fd7f7930d01876a70a9b8a4a3b9 (diff) | |
download | ffmpeg-2063f3ed9c2f7841b48d3d7635f42fafdace9de8.tar.gz |
avformat/tee: Handling slave failure in tee muxer
Adds per slave option 'onfail' to the tee muxer allowing an output to
fail, so other slave outputs can continue.
Reviewed-by: Nicolas George <george@nsup.org>
Signed-off-by: Jan Sebechlebsky <sebechlebskyjan@gmail.com>
Signed-off-by: Marton Balint <cus@passwd.hu>
Diffstat (limited to 'libavformat/tee.c')
-rw-r--r-- | libavformat/tee.c | 91 |
1 files changed, 80 insertions, 11 deletions
diff --git a/libavformat/tee.c b/libavformat/tee.c index 753f7eae6a..499ef33ad1 100644 --- a/libavformat/tee.c +++ b/libavformat/tee.c @@ -29,10 +29,19 @@ #define MAX_SLAVES 16 +typedef enum { + ON_SLAVE_FAILURE_ABORT = 1, + ON_SLAVE_FAILURE_IGNORE = 2 +} SlaveFailurePolicy; + +#define DEFAULT_SLAVE_FAILURE_POLICY ON_SLAVE_FAILURE_ABORT + typedef struct { AVFormatContext *avf; AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream + SlaveFailurePolicy on_fail; + /** map from input to output streams indexes, * disabled output streams are set to -1 */ int *stream_map; @@ -42,6 +51,7 @@ typedef struct { typedef struct TeeContext { const AVClass *class; unsigned nb_slaves; + unsigned nb_alive; TeeSlave slaves[MAX_SLAVES]; } TeeContext; @@ -136,6 +146,23 @@ end: return ret; } +static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *tee_slave) +{ + if (!opt) { + tee_slave->on_fail = DEFAULT_SLAVE_FAILURE_POLICY; + return 0; + } else if (!av_strcasecmp("abort", opt)) { + tee_slave->on_fail = ON_SLAVE_FAILURE_ABORT; + return 0; + } else if (!av_strcasecmp("ignore", opt)) { + tee_slave->on_fail = ON_SLAVE_FAILURE_IGNORE; + return 0; + } + /* Set failure behaviour to abort, so invalid option error will not be ignored */ + tee_slave->on_fail = ON_SLAVE_FAILURE_ABORT; + return AVERROR(EINVAL); +} + static int close_slave(TeeSlave *tee_slave) { AVFormatContext *avf; @@ -184,7 +211,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) AVDictionary *options = NULL; AVDictionaryEntry *entry; char *filename; - char *format = NULL, *select = NULL; + char *format = NULL, *select = NULL, *on_fail = NULL; AVFormatContext *avf2 = NULL; AVStream *st, *st2; int stream_count; @@ -204,6 +231,14 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) STEAL_OPTION("f", format); STEAL_OPTION("select", select); + STEAL_OPTION("onfail", on_fail); + + ret = parse_slave_failure_policy_option(on_fail, tee_slave); + if (ret < 0) { + av_log(avf, AV_LOG_ERROR, + "Invalid onfail option value, valid options are 'abort' and 'ignore'\n"); + goto end; + } ret = avformat_alloc_output_context2(&avf2, NULL, format, filename); if (ret < 0) @@ -351,6 +386,7 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave) end: av_free(format); av_free(select); + av_free(on_fail); av_dict_free(&options); av_freep(&tmp_select); return ret; @@ -380,6 +416,28 @@ static void log_slave(TeeSlave *slave, void *log_ctx, int log_level) } } +static int tee_process_slave_failure(AVFormatContext *avf, unsigned slave_idx, int err_n) +{ + TeeContext *tee = avf->priv_data; + TeeSlave *tee_slave = &tee->slaves[slave_idx]; + + tee->nb_alive--; + + close_slave(tee_slave); + + if (!tee->nb_alive) { + av_log(avf, AV_LOG_ERROR, "All tee outputs failed.\n"); + return err_n; + } else if (tee_slave->on_fail == ON_SLAVE_FAILURE_ABORT) { + av_log(avf, AV_LOG_ERROR, "Slave muxer #%u failed, aborting.\n", slave_idx); + return err_n; + } else { + av_log(avf, AV_LOG_ERROR, "Slave muxer #%u failed: %s, continuing with %u/%u slaves.\n", + slave_idx, av_err2str(err_n), tee->nb_alive, tee->nb_slaves); + return 0; + } +} + static int tee_write_header(AVFormatContext *avf) { TeeContext *tee = avf->priv_data; @@ -403,19 +461,24 @@ static int tee_write_header(AVFormatContext *avf) filename++; } - tee->nb_slaves = nb_slaves; + tee->nb_slaves = tee->nb_alive = nb_slaves; for (i = 0; i < nb_slaves; i++) { - if ((ret = open_slave(avf, slaves[i], &tee->slaves[i])) < 0) - goto fail; - log_slave(&tee->slaves[i], avf, AV_LOG_VERBOSE); + if ((ret = open_slave(avf, slaves[i], &tee->slaves[i])) < 0) { + ret = tee_process_slave_failure(avf, i, ret); + if (ret < 0) + goto fail; + } else { + log_slave(&tee->slaves[i], avf, AV_LOG_VERBOSE); + } av_freep(&slaves[i]); } for (i = 0; i < avf->nb_streams; i++) { int j, mapped = 0; for (j = 0; j < tee->nb_slaves; j++) - mapped += tee->slaves[j].stream_map[i] >= 0; + if (tee->slaves[j].avf) + mapped += tee->slaves[j].stream_map[i] >= 0; if (!mapped) av_log(avf, AV_LOG_WARNING, "Input stream #%d is not mapped " "to any slave.\n", i); @@ -436,9 +499,11 @@ static int tee_write_trailer(AVFormatContext *avf) unsigned i; for (i = 0; i < tee->nb_slaves; i++) { - if ((ret = close_slave(&tee->slaves[i])) < 0) - if (!ret_all) + if ((ret = close_slave(&tee->slaves[i])) < 0) { + ret = tee_process_slave_failure(avf, i, ret); + if (!ret_all && ret < 0) ret_all = ret; + } } return ret_all; } @@ -454,7 +519,9 @@ static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt) AVRational tb, tb2; for (i = 0; i < tee->nb_slaves; i++) { - avf2 = tee->slaves[i].avf; + if (!(avf2 = tee->slaves[i].avf)) + continue; + s = pkt->stream_index; s2 = tee->slaves[i].stream_map[s]; if (s2 < 0) @@ -475,9 +542,11 @@ static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt) if ((ret = av_apply_bitstream_filters(avf2->streams[s2]->codec, &pkt2, tee->slaves[i].bsfs[s2])) < 0 || - (ret = av_interleaved_write_frame(avf2, &pkt2)) < 0) - if (!ret_all) + (ret = av_interleaved_write_frame(avf2, &pkt2)) < 0) { + ret = tee_process_slave_failure(avf, i, ret); + if (!ret_all && ret < 0) ret_all = ret; + } } return ret_all; } |