lavf/tee: add special select option
authorStefano Sabatini <stefasab@gmail.com>
Thu, 8 Aug 2013 10:11:59 +0000 (12:11 +0200)
committerStefano Sabatini <stefasab@gmail.com>
Wed, 21 Aug 2013 15:44:35 +0000 (17:44 +0200)
doc/muxers.texi
libavformat/tee.c
libavformat/version.h

index 52da73e..c9c4416 100644 (file)
@@ -871,6 +871,11 @@ separated by @code{/}. If the stream specifier is not specified, the
 bistream filters will be applied to all streams in the output.
 
 Several bitstream filters can be specified, separated by ",".
+
+@item select
+Select the streams that should be mapped to the slave output,
+specified by a stream specifier. If not specified, this defaults to
+all the input streams.
 @end table
 
 Example: encode something and both archive it in a WebM file and stream it
index f7a8f41..111966c 100644 (file)
 typedef struct {
     AVFormatContext *avf;
     AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream
+
+    /** map from input to output streams indexes,
+     * disabled output streams are set to -1 */
+    int *stream_map;
 } TeeSlave;
 
 typedef struct TeeContext {
@@ -134,24 +138,54 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave)
     AVDictionary *options = NULL;
     AVDictionaryEntry *entry;
     char *filename;
-    char *format = NULL;
+    char *format = NULL, *select = NULL;
     AVFormatContext *avf2 = NULL;
     AVStream *st, *st2;
+    int stream_count;
 
     if ((ret = parse_slave_options(avf, slave, &options, &filename)) < 0)
         return ret;
-    if ((entry = av_dict_get(options, "f", NULL, 0))) {
-        format = entry->value;
-        entry->value = NULL; /* prevent it from being freed */
-        av_dict_set(&options, "f", NULL, 0);
-    }
+
+#define STEAL_OPTION(option, field) do {                                \
+        if ((entry = av_dict_get(options, option, NULL, 0))) {          \
+            field = entry->value;                                       \
+            entry->value = NULL; /* prevent it from being freed */      \
+            av_dict_set(&options, option, NULL, 0);                     \
+        }                                                               \
+    } while (0)
+
+    STEAL_OPTION("f", format);
+    STEAL_OPTION("select", select);
 
     ret = avformat_alloc_output_context2(&avf2, NULL, format, filename);
     if (ret < 0)
         goto end;
 
+    tee_slave->stream_map = av_calloc(avf->nb_streams, sizeof(*tee_slave->stream_map));
+    if (!tee_slave->stream_map) {
+        ret = AVERROR(ENOMEM);
+        goto end;
+    }
+
+    stream_count = 0;
     for (i = 0; i < avf->nb_streams; i++) {
         st = avf->streams[i];
+        if (select) {
+            ret = avformat_match_stream_specifier(avf, avf->streams[i], select);
+            if (ret < 0) {
+                av_log(avf, AV_LOG_ERROR,
+                       "Invalid stream specifier '%s' for output '%s'\n",
+                       select, slave);
+                goto end;
+            }
+
+            if (ret == 0) { /* no match */
+                tee_slave->stream_map[i] = -1;
+                continue;
+            }
+        }
+        tee_slave->stream_map[i] = stream_count++;
+
         if (!(st2 = avformat_new_stream(avf2, NULL))) {
             ret = AVERROR(ENOMEM);
             goto end;
@@ -266,6 +300,7 @@ static void close_slaves(AVFormatContext *avf)
                 bsf = bsf_next;
             }
         }
+        av_freep(&tee->slaves[i].stream_map);
 
         avio_close(avf2->pb);
         avf2->pb = NULL;
@@ -329,6 +364,15 @@ static int tee_write_header(AVFormatContext *avf)
     }
 
     tee->nb_slaves = nb_slaves;
+
+    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 (!mapped)
+            av_log(avf, AV_LOG_WARNING, "Input stream #%d is not mapped "
+                   "to any slave.\n", i);
+    }
     return 0;
 
 fail:
@@ -408,29 +452,30 @@ static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt)
     AVPacket pkt2;
     int ret_all = 0, ret;
     unsigned i, s;
+    int s2;
     AVRational tb, tb2;
 
     for (i = 0; i < tee->nb_slaves; i++) {
         avf2 = tee->slaves[i].avf;
         s = pkt->stream_index;
-        if (s >= avf2->nb_streams) {
-            if (!ret_all)
-                ret_all = AVERROR(EINVAL);
+        s2 = tee->slaves[i].stream_map[s];
+        if (s2 < 0)
             continue;
-        }
+
         if ((ret = av_copy_packet(&pkt2, pkt)) < 0 ||
             (ret = av_dup_packet(&pkt2))< 0)
             if (!ret_all) {
                 ret = ret_all;
                 continue;
             }
-        tb  = avf ->streams[s]->time_base;
-        tb2 = avf2->streams[s]->time_base;
+        tb  = avf ->streams[s ]->time_base;
+        tb2 = avf2->streams[s2]->time_base;
         pkt2.pts      = av_rescale_q(pkt->pts,      tb, tb2);
         pkt2.dts      = av_rescale_q(pkt->dts,      tb, tb2);
         pkt2.duration = av_rescale_q(pkt->duration, tb, tb2);
+        pkt2.stream_index = s2;
 
-        filter_packet(avf2, &pkt2, avf2, tee->slaves[i].bsfs[s]);
+        filter_packet(avf2, &pkt2, avf2, tee->slaves[i].bsfs[s2]);
         if ((ret = av_interleaved_write_frame(avf2, &pkt2)) < 0)
             if (!ret_all)
                 ret_all = ret;
index 112401a..956e6d6 100644 (file)
@@ -31,7 +31,7 @@
 
 #define LIBAVFORMAT_VERSION_MAJOR 55
 #define LIBAVFORMAT_VERSION_MINOR 14
-#define LIBAVFORMAT_VERSION_MICRO 100
+#define LIBAVFORMAT_VERSION_MICRO 101
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                LIBAVFORMAT_VERSION_MINOR, \