[FFmpeg-devel] [PATCH v2] avformat/tee: Move to new BSF API

sebechlebskyjan at gmail.com sebechlebskyjan at gmail.com
Sat Jun 4 20:24:40 CEST 2016


From: Jan Sebechlebsky <sebechlebskyjan at gmail.com>

Signed-off-by: Jan Sebechlebsky <sebechlebskyjan at gmail.com>
---
 I've rewritten the patch rapidly. Instead of using recursion it
 accumulates bitstream filtered packets in fifo buffer and
 dynamic array is used instead of linked list to store chain of
 bitstream filters.

 libavformat/tee.c | 301 +++++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 255 insertions(+), 46 deletions(-)

diff --git a/libavformat/tee.c b/libavformat/tee.c
index 806beaa..0e394ea 100644
--- a/libavformat/tee.c
+++ b/libavformat/tee.c
@@ -23,6 +23,7 @@
 #include "libavutil/avutil.h"
 #include "libavutil/avstring.h"
 #include "libavutil/opt.h"
+#include "libavutil/fifo.h"
 #include "internal.h"
 #include "avformat.h"
 #include "avio_internal.h"
@@ -37,8 +38,15 @@ typedef enum {
 #define DEFAULT_SLAVE_FAILURE_POLICY ON_SLAVE_FAILURE_ABORT
 
 typedef struct {
+    AVBSFContext **bsfs_ctxs;
+    unsigned bsfs_ctxs_nr;
+} TeeBSFList;
+
+typedef struct {
     AVFormatContext *avf;
-    AVBitStreamFilterContext **bsfs; ///< bitstream filters per stream
+    TeeBSFList *bsfs; // bitstream filters per stream
+
+    AVFifoBuffer *fifo; // fifo buffer used for bsf processing
 
     SlaveFailurePolicy on_fail;
 
@@ -106,6 +114,46 @@ fail:
     return ret;
 }
 
+static int initialize_bsf(AVFormatContext *avf, const char * bsf_name,
+                          AVCodecParameters *par_in, AVRational tb_in,
+                          AVBSFContext ** bsf_ctx)
+{
+    int ret = 0;
+    const AVBitStreamFilter *filter = av_bsf_get_by_name(bsf_name);
+    AVBSFContext *bsf_ctx_tmp;
+
+    if (!filter) {
+        av_log(avf, AV_LOG_ERROR, "Unknown bitstream filter '%s'\n",
+                           bsf_name);
+                    ret = AVERROR(EINVAL);
+                    return ret;
+    }
+
+    if ((ret = av_bsf_alloc(filter, &bsf_ctx_tmp)) < 0) {
+        av_log(avf, AV_LOG_ERROR, "Cannot initialize bitstream filter '%s'",
+               bsf_name);
+        return ret;
+    }
+
+    ret = avcodec_parameters_copy(bsf_ctx_tmp->par_in, par_in);
+    if (ret < 0) {
+        goto fail;
+    }
+
+    bsf_ctx_tmp->time_base_in = tb_in;
+
+    if ((ret = av_bsf_init(bsf_ctx_tmp)) < 0) {
+        goto fail;
+    }
+
+    *bsf_ctx = bsf_ctx_tmp;
+
+    return ret;
+fail:
+    av_bsf_free(&bsf_ctx_tmp);
+    return ret;
+}
+
 /**
  * Parse list of bitstream filters and add them to the list of filters
  * pointed to by bsfs.
@@ -113,37 +161,49 @@ fail:
  * The list must be specified in the form:
  * BSFS ::= BSF[,BSFS]
  */
-static int parse_bsfs(void *log_ctx, const char *bsfs_spec,
-                      AVBitStreamFilterContext **bsfs)
+static int parse_bsfs(AVFormatContext *avf, const char *bsfs_spec,
+                      TeeBSFList * bsf_list, int stream_nr)
 {
     char *bsf_name, *buf, *dup, *saveptr;
-    int ret = 0;
+    int ret = 0, i;
+    AVBSFContext *bsf_ctx;
+    AVStream *stream = avf->streams[stream_nr];
+    AVCodecParameters *last_codecpar = stream->codecpar;
+    AVRational last_tb = stream->time_base;
 
-    if (!(dup = buf = av_strdup(bsfs_spec)))
-        return AVERROR(ENOMEM);
+    if (!(dup = buf = av_strdup(bsfs_spec))) {
+        ret = AVERROR(ENOMEM);
+        goto fail;
+    }
 
     while (bsf_name = av_strtok(buf, ",", &saveptr)) {
-        AVBitStreamFilterContext *bsf = av_bitstream_filter_init(bsf_name);
-
-        if (!bsf) {
-            av_log(log_ctx, AV_LOG_ERROR,
-                   "Cannot initialize bitstream filter with name '%s', "
-                   "unknown filter or internal error happened\n",
-                   bsf_name);
-            ret = AVERROR_UNKNOWN;
-            goto end;
+        ret = initialize_bsf(avf, bsf_name, last_codecpar, last_tb, &bsf_ctx);
+        if (ret < 0) {
+            goto fail;
         }
 
-        /* append bsf context to the list of bsf contexts */
-        *bsfs = bsf;
-        bsfs = &bsf->next;
+        last_tb = bsf_ctx->time_base_out;
+        last_codecpar = bsf_ctx->par_out;
+
+        ret = av_dynarray_add_nofree(&bsf_list->bsfs_ctxs, &bsf_list->bsfs_ctxs_nr, bsf_ctx);
+        if (ret < 0) {
+            goto fail;
+        }
 
         buf = NULL;
+        bsf_ctx = NULL;
     }
 
-end:
     av_free(dup);
     return ret;
+fail:
+    for (i = 0; i < bsf_list->bsfs_ctxs_nr; ++i) {
+        av_bsf_free(&bsf_list->bsfs_ctxs[i]);
+    }
+    bsf_list->bsfs_ctxs_nr = 0;
+    av_free(dup);
+    av_bsf_free(&bsf_ctx);
+    return ret;
 }
 
 static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *tee_slave)
@@ -163,6 +223,154 @@ static inline int parse_slave_failure_policy_option(const char *opt, TeeSlave *t
     return AVERROR(EINVAL);
 }
 
+/*
+ * Applies single bitstream filter to single packet, all resulting filtered packets
+ * are pushed to fifo buffer
+ */
+static int tee_apply_bsf(AVFifoBuffer *fifo, AVBSFContext *bsf_ctx, AVPacket *pkt)
+{
+    int ret = 0;
+    ret = av_bsf_send_packet(bsf_ctx, pkt);
+    if (ret < 0)
+        return ret;
+
+    do {
+        ret = av_bsf_receive_packet(bsf_ctx, pkt);
+        if (ret) {
+            ret = 0;
+            break;
+        }
+
+        if ( av_fifo_space(fifo) < sizeof(AVPacket)) {
+            ret = av_fifo_grow(fifo, sizeof(AVPacket));
+            if (ret < 0)
+                return ret;
+        }
+        av_fifo_generic_write(fifo, pkt, sizeof(AVPacket), NULL);
+    } while(1);
+
+    return ret;
+}
+
+/*
+ * Applies sequence of bitstream filters to all packets in fifo buffer.
+ * At the return point fifo will contain resulting filtered packets,
+ * time base pointed by tb will be set to ouput time base of last bitstream filter.
+ * If flushing != 0, each bitstream filter will be flushed
+ */
+static int tee_apply_bsfs(AVFifoBuffer *fifo, TeeBSFList *bsfs, AVRational *tb, int flushing)
+{
+    int ret = 0;
+    int i, j;
+    AVPacket pkt;
+
+    if (!bsfs || !bsfs->bsfs_ctxs_nr)
+        return 0;
+
+    for (i = 0; i < bsfs->bsfs_ctxs_nr; ++i ) {
+        int pkt_nr = av_fifo_size(fifo) / sizeof(AVPacket);
+
+        for (j = 0; j < pkt_nr; ++j) {
+            av_fifo_generic_read(fifo, &pkt, sizeof(AVPacket), NULL);
+            ret = tee_apply_bsf(fifo, bsfs->bsfs_ctxs[i], &pkt);
+            if (ret < 0) {
+                return ret;
+            }
+        }
+
+        if (flushing) {
+            ret = tee_apply_bsf(fifo, bsfs->bsfs_ctxs[i], NULL);
+            if (ret < 0)
+                return ret;
+        }
+    }
+
+    *tb = bsfs->bsfs_ctxs[bsfs->bsfs_ctxs_nr-1]->time_base_out;
+
+    return ret;
+}
+
+/*
+ * Apply bitstream filters and write frame. If pkt == NULL bitstream filters
+ * will be flushed.
+ */
+static int tee_process_packet(TeeSlave * tee_slave, AVPacket *pkt,
+                              int stream_nr, AVRational pkt_tb)
+{
+    int ret = 0, pkt_nr, i;
+    AVPacket proc_pkt;
+    AVFormatContext *avf = tee_slave->avf;
+    TeeBSFList * bsf = &tee_slave->bsfs[stream_nr];
+    AVFifoBuffer * fifo = tee_slave->fifo;
+    AVRational out_tb = avf->streams[stream_nr]->time_base;
+    AVRational in_tb = pkt_tb;
+
+    if (pkt) {
+        av_fifo_generic_write(fifo, pkt, sizeof(AVPacket), NULL);
+        ret = tee_apply_bsfs(fifo, bsf, &in_tb, 0);
+    } else {
+        ret = tee_apply_bsfs(fifo, bsf, &in_tb, 1);
+    }
+    if (ret < 0)
+        goto fail;
+
+    pkt_nr = av_fifo_size(fifo) / sizeof(AVPacket);
+    for (i = 0; i < pkt_nr; ++i) {
+        av_fifo_generic_read(fifo, &proc_pkt, sizeof(AVPacket), NULL);
+
+        proc_pkt.pts = av_rescale_q(proc_pkt.pts, in_tb, out_tb);
+        proc_pkt.dts = av_rescale_q(proc_pkt.dts, in_tb, out_tb);
+        proc_pkt.duration = av_rescale_q(proc_pkt.duration, in_tb, out_tb);
+        proc_pkt.stream_index = stream_nr;
+
+        ret = av_interleaved_write_frame(avf, &proc_pkt);
+        if (ret < 0)
+            goto fail;
+    }
+
+    return ret;
+fail:
+    /* Unreference unprocessed packets in fifo */
+    pkt_nr = av_fifo_size(fifo) / sizeof(AVPacket);
+    for (i = 0; i < pkt_nr; ++i) {
+        av_fifo_generic_read(fifo, &proc_pkt, sizeof(AVPacket), NULL);
+        av_packet_unref(&proc_pkt);
+    }
+    return ret;
+}
+
+static int flush_bsfs(TeeSlave *tee_slave)
+{
+    AVFormatContext *avf = tee_slave->avf;
+    int i;
+    int ret, ret_all = 0;
+
+    for (i = 0; i < avf->nb_streams; i++) {
+        if (tee_slave->bsfs) {
+            ret = tee_process_packet(tee_slave, NULL, i, av_make_q(1,0));
+            if (!ret_all && ret < 0) {
+                ret_all = ret;
+            }
+        }
+    }
+
+    return ret_all;
+}
+
+static void free_bsf_list(TeeBSFList * bsf_list) {
+    int i;
+
+    if (!bsf_list) {
+        return;
+    }
+
+    for (i = 0; i < bsf_list->bsfs_ctxs_nr; ++i ) {
+        av_bsf_free(&bsf_list->bsfs_ctxs[i]);
+    }
+    bsf_list->bsfs_ctxs_nr = 0;
+    av_freep(&bsf_list->bsfs_ctxs);
+}
+
 static int close_slave(TeeSlave *tee_slave)
 {
     AVFormatContext *avf;
@@ -173,21 +381,23 @@ static int close_slave(TeeSlave *tee_slave)
     if (!avf)
         return 0;
 
-    if (tee_slave->header_written)
+    if (tee_slave->header_written) {
+        ret = flush_bsfs(tee_slave);
+        if (ret < 0) {
+            av_log(avf, AV_LOG_ERROR, "Error flushing bitstream filters: %s\n",
+                   av_err2str(ret));
+        }
         ret = av_write_trailer(avf);
+    }
 
     if (tee_slave->bsfs) {
         for (i = 0; i < avf->nb_streams; ++i) {
-            AVBitStreamFilterContext *bsf_next, *bsf = tee_slave->bsfs[i];
-            while (bsf) {
-                bsf_next = bsf->next;
-                av_bitstream_filter_close(bsf);
-                bsf = bsf_next;
-            }
+            free_bsf_list(&tee_slave->bsfs[i]);
         }
     }
     av_freep(&tee_slave->stream_map);
     av_freep(&tee_slave->bsfs);
+    av_fifo_freep(&tee_slave->fifo);
 
     ff_format_io_close(avf, &avf->pb);
     avformat_free_context(avf);
@@ -330,6 +540,12 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave)
         goto end;
     }
 
+    tee_slave->fifo = av_fifo_alloc(sizeof(AVPacket));
+    if (!tee_slave->fifo) {
+        ret = AVERROR(ENOMEM);
+        goto end;
+    }
+
     entry = NULL;
     while (entry = av_dict_get(options, "bsfs", NULL, AV_DICT_IGNORE_SUFFIX)) {
         const char *spec = entry->key + strlen("bsfs");
@@ -356,13 +572,14 @@ static int open_slave(AVFormatContext *avf, char *slave, TeeSlave *tee_slave)
             if (ret > 0) {
                 av_log(avf, AV_LOG_DEBUG, "spec:%s bsfs:%s matches stream %d of slave "
                        "output '%s'\n", spec, entry->value, i, filename);
-                if (tee_slave->bsfs[i]) {
+                if (tee_slave->bsfs[i].bsfs_ctxs_nr) {
                     av_log(avf, AV_LOG_WARNING,
                            "Duplicate bsfs specification associated to stream %d of slave "
                            "output '%s', filters will be ignored\n", i, filename);
                     continue;
                 }
-                ret = parse_bsfs(avf, entry->value, &tee_slave->bsfs[i]);
+
+                ret = parse_bsfs(avf, entry->value, &tee_slave->bsfs[i], i);
                 if (ret < 0) {
                     av_log(avf, AV_LOG_ERROR,
                            "Error parsing bitstream filter sequence '%s' associated to "
@@ -394,22 +611,21 @@ end:
 
 static void log_slave(TeeSlave *slave, void *log_ctx, int log_level)
 {
-    int i;
+    int i, j;
     av_log(log_ctx, log_level, "filename:'%s' format:%s\n",
            slave->avf->filename, slave->avf->oformat->name);
     for (i = 0; i < slave->avf->nb_streams; i++) {
         AVStream *st = slave->avf->streams[i];
-        AVBitStreamFilterContext *bsf = slave->bsfs[i];
+        TeeBSFList *bsf = &slave->bsfs[i];
 
         av_log(log_ctx, log_level, "    stream:%d codec:%s type:%s",
                i, avcodec_get_name(st->codecpar->codec_id),
                av_get_media_type_string(st->codecpar->codec_type));
-        if (bsf) {
+        if (bsf->bsfs_ctxs_nr) {
             av_log(log_ctx, log_level, " bsfs:");
-            while (bsf) {
+            for(j = 0; j < bsf->bsfs_ctxs_nr; ++j) {
                 av_log(log_ctx, log_level, "%s%s",
-                       bsf->filter->name, bsf->next ? "," : "");
-                bsf = bsf->next;
+                       bsf->bsfs_ctxs[j]->filter->name, j < bsf->bsfs_ctxs_nr ? "," : "");
             }
         }
         av_log(log_ctx, log_level, "\n");
@@ -511,15 +727,14 @@ static int tee_write_trailer(AVFormatContext *avf)
 static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt)
 {
     TeeContext *tee = avf->priv_data;
-    AVFormatContext *avf2;
     AVPacket pkt2;
     int ret_all = 0, ret;
     unsigned i, s;
     int s2;
-    AVRational tb, tb2;
+    AVRational tb;
 
     for (i = 0; i < tee->nb_slaves; i++) {
-        if (!(avf2 = tee->slaves[i].avf))
+        if (!tee->slaves[i].avf)
             continue;
 
         s = pkt->stream_index;
@@ -533,16 +748,10 @@ static int tee_write_packet(AVFormatContext *avf, AVPacket *pkt)
                 ret_all = ret;
                 continue;
             }
+
         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;
-
-        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 = tee_process_packet(&tee->slaves[i], &pkt2, s2, tb)) < 0) {
             ret = tee_process_slave_failure(avf, i, ret);
             if (!ret_all && ret < 0)
                 ret_all = ret;
-- 
1.9.1



More information about the ffmpeg-devel mailing list