vp9: Add bsf to merge superframes
authorRonald S. Bultje <rsbultje@gmail.com>
Mon, 29 Feb 2016 14:43:07 +0000 (09:43 -0500)
committerMark Thompson <sw@jkqxz.net>
Sun, 2 Apr 2017 16:54:05 +0000 (17:54 +0100)
From ffmpeg commit 2e6636aa87303d37b112e79f093ca39500f92364.

Changelog
doc/bitstream_filters.texi
libavcodec/Makefile
libavcodec/bitstream_filters.c
libavcodec/vp9_superframe_bsf.c [new file with mode: 0644]

index 0497239..b3aed3c 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -12,6 +12,7 @@ version <next>:
 - The x86 assembler default switched from yasm to nasm, pass
   --x86asmexe=yasm to configure to restore the old behavior.
 - Cineform HD decoder
+- VP9 superframe split/merge bitstream filters
 
 
 version 12:
index 49b8a64..ddadd76 100644 (file)
@@ -95,6 +95,10 @@ This bitstream filter passes the packets through unchanged.
 
 @section remove_extradata
 
+@section vp9_superframe
+
+Combine VP9 frames into superframes.
+
 @section vp9_superframe_split
 
 Split VP9 superframes into single frames.
index 3a55a28..80c572c 100644 (file)
@@ -772,6 +772,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
index 1cea6d7..d46fdad 100644 (file)
@@ -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 (file)
index 0000000..be83ba3
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * VP9 invisible (alt-ref) frame to superframe merge bitstream filter
+ * Copyright (c) 2016 Ronald S. Bultje <rsbultje@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) do { \
+        for (n = 0; n < n_in; n++) { \
+            wr; \
+            ptr += mag + 1; \
+        } \
+    } while (0)
+
+    // 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(ENOSYS);
+        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;
+    }
+
+    if (invisible) {
+        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);
+        s->cache[s->n_cache++].size = in->size;
+        res = AVERROR(EAGAIN);
+        goto done;
+    }
+    av_assert0(s->n_cache > 0);
+
+    s->cache[s->n_cache].data = in->data;
+    s->cache[s->n_cache].size = in->size;
+
+    // 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,
+};