From: "Ronald S. Bultje" <rsbul...@gmail.com>

From ffmpeg commit 2e6636aa87303d37b112e79f093ca39500f92364.
---
Exactly as in the other tine (except licence header).  It might be nicer to 
call it vp9_superframe_merge, but maintaining identical behaviour probably 
trumps that?


 libavcodec/Makefile             |   1 +
 libavcodec/bitstream_filters.c  |   1 +
 libavcodec/vp9_superframe_bsf.c | 205 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 207 insertions(+)
 create mode 100644 libavcodec/vp9_superframe_bsf.c

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index b3cee1d0c..7be9c2880 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -769,6 +769,7 @@ OBJS-$(CONFIG_NOISE_BSF)                  += noise_bsf.o
 OBJS-$(CONFIG_NULL_BSF)                   += null_bsf.o
 OBJS-$(CONFIG_REMOVE_EXTRADATA_BSF)       += remove_extradata_bsf.o
 OBJS-$(CONFIG_TEXT2MOVSUB_BSF)            += movsub_bsf.o
+OBJS-$(CONFIG_VP9_SUPERFRAME_BSF)         += vp9_superframe_bsf.o
 OBJS-$(CONFIG_VP9_SUPERFRAME_SPLIT_BSF)   += vp9_superframe_split_bsf.o
 
 # thread libraries
diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c
index 1cea6d77a..d46fdad81 100644
--- a/libavcodec/bitstream_filters.c
+++ b/libavcodec/bitstream_filters.c
@@ -38,6 +38,7 @@ extern const AVBitStreamFilter ff_null_bsf;
 extern const AVBitStreamFilter ff_text2movsub_bsf;
 extern const AVBitStreamFilter ff_noise_bsf;
 extern const AVBitStreamFilter ff_remove_extradata_bsf;
+extern const AVBitStreamFilter ff_vp9_superframe_bsf;
 extern const AVBitStreamFilter ff_vp9_superframe_split_bsf;
 
 #include "libavcodec/bsf_list.c"
diff --git a/libavcodec/vp9_superframe_bsf.c b/libavcodec/vp9_superframe_bsf.c
new file mode 100644
index 000000000..27445176b
--- /dev/null
+++ b/libavcodec/vp9_superframe_bsf.c
@@ -0,0 +1,205 @@
+/*
+ * Vp9 invisible (alt-ref) frame to superframe merge bitstream filter
+ * Copyright (c) 2016 Ronald S. Bultje <rsbul...@gmail.com>
+ *
+ * This file is part of Libav.
+ *
+ * Libav is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * Libav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/avassert.h"
+#include "avcodec.h"
+#include "bsf.h"
+#include "get_bits.h"
+
+#define MAX_CACHE 8
+typedef struct VP9BSFContext {
+    int n_cache;
+    struct CachedBuf {
+        uint8_t *data;
+        int size;
+    } cache[MAX_CACHE];
+} VP9BSFContext;
+
+static void stats(const struct CachedBuf *in, int n_in,
+                  unsigned *_max, unsigned *_sum)
+{
+    int n;
+    unsigned max = 0, sum = 0;
+
+    for (n = 0; n < n_in; n++) {
+        unsigned sz = in[n].size;
+
+        if (sz > max)
+            max = sz;
+        sum += sz;
+    }
+
+    *_max = max;
+    *_sum = sum;
+}
+
+static int merge_superframe(const struct CachedBuf *in, int n_in, AVPacket 
*out)
+{
+    unsigned max, sum, mag, marker, n, sz;
+    uint8_t *ptr;
+    int res;
+
+    stats(in, n_in, &max, &sum);
+    mag = av_log2(max) >> 3;
+    marker = 0xC0 + (mag << 3) + (n_in - 1);
+    sz = sum + 2 + (mag + 1) * n_in;
+    res = av_new_packet(out, sz);
+    if (res < 0)
+        return res;
+    ptr = out->data;
+    for (n = 0; n < n_in; n++) {
+        memcpy(ptr, in[n].data, in[n].size);
+        ptr += in[n].size;
+    }
+
+#define wloop(mag, wr) \
+    for (n = 0; n < n_in; n++) { \
+        wr; \
+        ptr += mag + 1; \
+    }
+
+    // write superframe with marker 110[mag:2][nframes:3]
+    *ptr++ = marker;
+    switch (mag) {
+    case 0:
+        wloop(mag, *ptr = in[n].size);
+        break;
+    case 1:
+        wloop(mag, AV_WL16(ptr, in[n].size));
+        break;
+    case 2:
+        wloop(mag, AV_WL24(ptr, in[n].size));
+        break;
+    case 3:
+        wloop(mag, AV_WL32(ptr, in[n].size));
+        break;
+    }
+    *ptr++ = marker;
+    av_assert0(ptr == &out->data[out->size]);
+
+    return 0;
+}
+
+static int vp9_superframe_filter(AVBSFContext *ctx, AVPacket *out)
+{
+    GetBitContext gb;
+    VP9BSFContext *s = ctx->priv_data;
+    AVPacket *in;
+    int res, invisible, profile, marker, uses_superframe_syntax = 0, n;
+
+    res = ff_bsf_get_packet(ctx, &in);
+    if (res < 0)
+        return res;
+
+    marker = in->data[in->size - 1];
+    if ((marker & 0xe0) == 0xc0) {
+        int nbytes = 1 + ((marker >> 3) & 0x3);
+        int n_frames = 1 + (marker & 0x7), idx_sz = 2 + n_frames * nbytes;
+
+        uses_superframe_syntax = in->size >= idx_sz && in->data[in->size - 
idx_sz] == marker;
+    }
+
+    if ((res = init_get_bits8(&gb, in->data, in->size)) < 0)
+        goto done;
+
+    get_bits(&gb, 2); // frame marker
+    profile  = get_bits1(&gb);
+    profile |= get_bits1(&gb) << 1;
+    if (profile == 3) profile += get_bits1(&gb);
+
+    if (get_bits1(&gb)) {
+        invisible = 0;
+    } else {
+        get_bits1(&gb); // keyframe
+        invisible = !get_bits1(&gb);
+    }
+
+    if (uses_superframe_syntax && s->n_cache > 0) {
+        av_log(ctx, AV_LOG_ERROR,
+               "Mixing of superframe syntax and naked VP9 frames not 
supported");
+        res = AVERROR_INVALIDDATA;
+        goto done;
+    } else if ((!invisible || uses_superframe_syntax) && !s->n_cache) {
+        // passthrough
+        av_packet_move_ref(out, in);
+        goto done;
+    } else if (s->n_cache + 1 >= MAX_CACHE) {
+        av_log(ctx, AV_LOG_ERROR,
+               "Too many invisible frames");
+        res = AVERROR_INVALIDDATA;
+        goto done;
+    }
+
+    s->cache[s->n_cache].size = in->size;
+    if (invisible && !uses_superframe_syntax) {
+        s->cache[s->n_cache].data = av_malloc(in->size);
+        if (!s->cache[s->n_cache].data) {
+            res = AVERROR(ENOMEM);
+            goto done;
+        }
+        memcpy(s->cache[s->n_cache++].data, in->data, in->size);
+        res = AVERROR(EAGAIN);
+        goto done;
+    }
+    av_assert0(s->n_cache > 0);
+
+    s->cache[s->n_cache].data = in->data;
+
+    // build superframe
+    if ((res = merge_superframe(s->cache, s->n_cache + 1, out)) < 0)
+        goto done;
+
+    for (n = 0; n < s->n_cache; n++)
+        av_freep(&s->cache[n].data);
+    s->n_cache = 0;
+
+    res = av_packet_copy_props(out, in);
+    if (res < 0)
+        goto done;
+
+done:
+    if (res < 0)
+        av_packet_unref(out);
+    av_packet_free(&in);
+    return res;
+}
+
+static void vp9_superframe_close(AVBSFContext *ctx)
+{
+    VP9BSFContext *s = ctx->priv_data;
+    int n;
+
+    // free cached data
+    for (n = 0; n < s->n_cache; n++)
+        av_freep(&s->cache[n].data);
+}
+
+static const enum AVCodecID codec_ids[] = {
+    AV_CODEC_ID_VP9, AV_CODEC_ID_NONE,
+};
+
+const AVBitStreamFilter ff_vp9_superframe_bsf = {
+    .name           = "vp9_superframe",
+    .priv_data_size = sizeof(VP9BSFContext),
+    .filter         = vp9_superframe_filter,
+    .close          = vp9_superframe_close,
+    .codec_ids      = codec_ids,
+};
-- 
2.11.0
_______________________________________________
libav-devel mailing list
libav-devel@libav.org
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to