[FFmpeg-devel] [WIP][RFC][PATCH] avcodec: add BRAW video decoder

Paul B Mahol onemda at gmail.com
Sat Dec 14 13:57:05 EET 2019


Signed-off-by: Paul B Mahol <onemda at gmail.com>
---

Does not use optimized 8x8 idct as its buggy and give errors around high contrast borders.
Sending to receive general feedback and useful comments.
Still probabbly missing additional post processing.

---
 libavcodec/Makefile      |   1 +
 libavcodec/allcodecs.c   |   1 +
 libavcodec/avcodec.h     |   1 +
 libavcodec/braw.c        | 576 +++++++++++++++++++++++++++++++++++++++
 libavcodec/codec_desc.c  |   7 +
 libavcodec/simple_idct.c |  41 +++
 libavcodec/simple_idct.h |   2 +
 libavformat/isom.c       |   7 +
 8 files changed, 636 insertions(+)
 create mode 100644 libavcodec/braw.c

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index c1f35b40d8..81ba7e7380 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -238,6 +238,7 @@ OBJS-$(CONFIG_BMP_DECODER)             += bmp.o msrledec.o
 OBJS-$(CONFIG_BMP_ENCODER)             += bmpenc.o
 OBJS-$(CONFIG_BMV_AUDIO_DECODER)       += bmvaudio.o
 OBJS-$(CONFIG_BMV_VIDEO_DECODER)       += bmvvideo.o
+OBJS-$(CONFIG_BRAW_DECODER)            += braw.o
 OBJS-$(CONFIG_BRENDER_PIX_DECODER)     += brenderpix.o
 OBJS-$(CONFIG_C93_DECODER)             += c93.o
 OBJS-$(CONFIG_CAVS_DECODER)            += cavs.o cavsdec.o cavsdsp.o \
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index c33edf23c9..a3e9d0b4ad 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -64,6 +64,7 @@ extern AVCodec ff_bitpacked_decoder;
 extern AVCodec ff_bmp_encoder;
 extern AVCodec ff_bmp_decoder;
 extern AVCodec ff_bmv_video_decoder;
+extern AVCodec ff_braw_decoder;
 extern AVCodec ff_brender_pix_decoder;
 extern AVCodec ff_c93_decoder;
 extern AVCodec ff_cavs_decoder;
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 119b32dc1f..b6d1cc4a3a 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -460,6 +460,7 @@ enum AVCodecID {
     AV_CODEC_ID_IMM5,
     AV_CODEC_ID_MVDV,
     AV_CODEC_ID_MVHA,
+    AV_CODEC_ID_BRAW,
 
     /* various PCM "codecs" */
     AV_CODEC_ID_FIRST_AUDIO = 0x10000,     ///< A dummy id pointing at the start of audio codecs
diff --git a/libavcodec/braw.c b/libavcodec/braw.c
new file mode 100644
index 0000000000..4ed4a4ef15
--- /dev/null
+++ b/libavcodec/braw.c
@@ -0,0 +1,576 @@
+/*
+ * Blackmagic RAW codec
+ *
+ * Copyright (c) 2019 Paul B Mahol
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libavutil/avassert.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/thread.h"
+
+#include "avcodec.h"
+#include "bytestream.h"
+#include "get_bits.h"
+#include "idctdsp.h"
+#include "internal.h"
+#include "simple_idct.h"
+#include "thread.h"
+
+static const uint8_t half_scan[] =
+{
+     0, 1, 2, 3, 8, 9, 16, 17, 10, 11, 4, 5, 6, 7, 12, 13, 18,
+     19, 24, 25, 26, 27, 20, 21, 14,15, 22, 23, 28, 29, 30, 31,
+};
+
+static const int16_t dcval_tab[2][16] = {
+  {    -1,     -3,     -7,    -15,
+      -31,    -63,   -127,   -255,
+     -511,  -1023,  -2047,  -4095,
+    -8191, -16383, -32767,      0 },
+  {     1,      2,      4,      8,
+       16,     32,     64,    128,
+      256,    512,   1024,   2048,
+     4096,   8192,  16384,      0 },
+};
+
+static const uint8_t dc_bits[16] =
+{
+    2, 3, 3, 3, 3, 3, 5, 5, 6, 12, 12, 12, 12, 5, 12, 13,
+};
+
+static const uint16_t dc_codes[16] =
+{
+    0x0, 0x2, 0x3, 0x4, 0x5, 0x6, 0x1C, 0x1D, 0x3E, 0xFF4, 0xFF5, 0xFF7, 0xFED, 0x1E, 0xFFE, 0x1FFE,
+};
+
+static const uint8_t dc_table[16][3] =
+{
+    {  0, 0, 15 }, {  0, 1,  0 }, {  1, 1,  1 }, {  2, 1,  2 },
+    {  3, 1,  3 }, {  4, 1,  4 }, {  5, 1,  5 }, {  6, 1,  6 },
+    {  7, 1,  7 }, {  8, 1,  8 }, {  9, 1,  9 }, { 10, 1, 10 },
+    { 11, 1, 11 }, { 12, 1, 12 }, { 13, 1, 13 }, { 14, 1, 14 },
+};
+
+static const uint8_t ac_bits[194] =
+{
+    2, 2, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10,
+    10, 10, 10, 11, 11, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 15, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 18, 18, 18, 18,
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+};
+
+static const uint32_t ac_codes[194] =
+{
+    0x000000, 0x000001, 0x000004, 0x00000B, 0x00000C, 0x00000A,
+    0x00001A, 0x00001B, 0x00001C, 0x00003A, 0x00003B, 0x000078,
+    0x000079, 0x00007A, 0x00007B, 0x0000F8, 0x0000F9, 0x0000FA,
+    0x0001F6, 0x0001F7, 0x0001F8, 0x0001F9, 0x0001FA, 0x0003F6,
+    0x0003F7, 0x0003F8, 0x0003F9, 0x0003FA, 0x0007F8, 0x0007F9,
+    0x000FED, 0x000FF4, 0x000FF5, 0x000FF7, 0x000FEC, 0x000FF6,
+    0x001FDC, 0x001FDD, 0x001FDE, 0x001FDF, 0x007FC0, 0x00FF84,
+    0x00FF85, 0x00FF86, 0x00FF87, 0x00FF88, 0x00FF89, 0x00FF8A,
+    0x00FF8B, 0x00FF8C, 0x00FF8D, 0x00FF8E, 0x00FF8F, 0x00FF90,
+    0x00FF91, 0x00FF92, 0x00FF93, 0x00FF94, 0x00FF95, 0x00FF96,
+    0x00FF97, 0x00FF98, 0x00FF99, 0x00FF9A, 0x00FF9B, 0x00FF9C,
+    0x00FF9D, 0x00FF9E, 0x00FF9F, 0x00FFA0, 0x00FFA1, 0x00FFA2,
+    0x00FFA3, 0x00FFA4, 0x00FFA5, 0x00FFA6, 0x00FFA7, 0x00FFA8,
+    0x00FFA9, 0x00FFAA, 0x00FFAB, 0x00FFAC, 0x00FFAE, 0x00FFAF,
+    0x00FFB0, 0x00FFB1, 0x00FFB2, 0x00FFB3, 0x00FFB4, 0x00FFB6,
+    0x00FFB7, 0x00FFB8, 0x00FFB9, 0x00FFBA, 0x00FFBB, 0x00FFBC,
+    0x00FFBE, 0x00FFBF, 0x00FFC0, 0x00FFC1, 0x00FFC2, 0x00FFC3,
+    0x00FFC4, 0x00FFC5, 0x00FFC7, 0x00FFC8, 0x00FFC9, 0x00FFCA,
+    0x00FFCB, 0x00FFCC, 0x00FFCD, 0x00FFCE, 0x00FFD0, 0x00FFD1,
+    0x00FFD2, 0x00FFD3, 0x00FFD4, 0x00FFD5, 0x00FFD6, 0x00FFD7,
+    0x00FFD9, 0x00FFDA, 0x00FFDB, 0x00FFDC, 0x00FFDD, 0x00FFDE,
+    0x00FFDF, 0x00FFE0, 0x00FFE2, 0x00FFE3, 0x00FFE4, 0x00FFE5,
+    0x00FFE6, 0x00FFE7, 0x00FFE8, 0x00FFE9, 0x00FFEB, 0x00FFEC,
+    0x00FFED, 0x00FFEE, 0x00FFEF, 0x00FFF0, 0x00FFF1, 0x00FFF2,
+    0x00FFF3, 0x00FFF5, 0x00FFF6, 0x00FFF7, 0x00FFF8, 0x00FFF9,
+    0x00FFFA, 0x00FFFB, 0x00FFFC, 0x00FFFD, 0x03FEB5, 0x03FEB6,
+    0x03FEB7, 0x03FED5, 0x03FED6, 0x03FED7, 0x03FEF5, 0x03FEF6,
+    0x03FEF7, 0x03FF19, 0x03FEB4, 0x03FF1A, 0x03FF1B, 0x03FED4,
+    0x03FF3D, 0x03FF3E, 0x03FEF4, 0x03FF3F, 0x03FF61, 0x03FF18,
+    0x03FF62, 0x03FF63, 0x03FF3C, 0x03FF85, 0x03FF86, 0x03FF60,
+    0x03FF87, 0x03FFA9, 0x03FF84, 0x03FFAA, 0x03FFAB, 0x03FFA8,
+    0x03FFD1, 0x03FFD2, 0x03FFD0, 0x03FFD3, 0x03FFF9, 0x03FFF8,
+    0x03FFFA, 0x03FFFB,
+};
+
+static const uint8_t ac_table[194][2] =
+{
+    { 0, 1 }, { 0, 2 }, { 0, 3 }, { 0, 4 }, { 1, 1 }, { 255, 0 },
+    { 0, 5 }, { 1, 2 }, { 2, 1 }, { 3, 1 }, { 4, 1 }, { 0, 6 },
+    { 1, 3 }, { 5, 1 }, { 6, 1 }, { 0, 7 }, { 2, 2 }, { 7, 1 },
+    { 1, 4 }, { 3, 2 }, { 8, 1 }, { 9, 1 }, { 10, 1 }, { 0, 8 },
+    { 2, 3 }, { 4, 2 }, { 11, 1 }, { 12, 1 }, { 13, 1 }, { 15, 0 },
+    { 0, 12 },{ 0, 9 }, { 0, 10 }, { 0, 11 }, { 1, 5 }, { 6, 2 },
+    { 2, 4 }, { 3, 3 }, { 5, 2 }, { 7, 2 }, { 8, 2 }, { 1, 6 },
+    { 1, 7 }, { 1, 8 }, { 1, 9 }, { 1, 10 }, { 2, 5 }, { 2, 6 },
+    { 2, 7 }, { 2, 8 }, { 2, 9 }, { 2, 10 }, { 3, 4 }, { 3, 5 },
+    { 3, 6 }, { 3, 7 }, { 3, 8 }, { 3, 9 }, { 3, 10 }, { 4, 3 },
+    { 4, 4 }, { 4, 5 }, { 4, 6 }, { 4, 7 }, { 4, 8 }, { 4, 9 },
+    { 4, 10 }, { 5, 3 }, { 5, 4 }, { 5, 5 }, { 5, 6 }, { 5, 7 },
+    { 5, 8 }, { 5, 9 }, { 5, 10 }, { 6, 3 }, { 6, 4 }, { 6, 5 },
+    { 6, 6 }, { 6, 7 }, { 6, 8 }, { 6, 9 }, { 7, 3 }, { 7, 4 },
+    { 7, 5 }, { 7, 6 }, { 7, 7 }, { 7, 8 }, { 7, 9 }, { 8, 3 },
+    { 8, 4 }, { 8, 5 }, { 8, 6 }, { 8, 7 }, { 8, 8 }, { 8, 9 },
+    { 9, 2 }, { 9, 3 }, { 9, 4 }, { 9, 5 }, { 9, 6 }, { 9, 7 },
+    { 9, 8 }, { 9, 9 }, { 10, 2 }, { 10, 3 }, { 10, 4 }, { 10, 5 },
+    { 10, 6 }, { 10, 7 }, { 10, 8 }, { 10, 9 }, { 11, 2 }, { 11, 3 },
+    { 11, 4 }, { 11, 5 }, { 11, 6 }, { 11, 7 }, { 11, 8 }, { 11, 9 },
+    { 12, 2 }, { 12, 3 }, { 12, 4 }, { 12, 5 }, { 12, 6 }, { 12, 7 },
+    { 12, 8 }, { 12, 9 }, { 13, 2 }, { 13, 3 }, { 13, 4 }, { 13, 5 },
+    { 13, 6 }, { 13, 7 }, { 13, 8 }, { 13, 9 }, { 14, 1 }, { 14, 2 },
+    { 14, 3 }, { 14, 4 }, { 14, 5 }, { 14, 6 }, { 14, 7 }, { 14, 8 },
+    { 14, 9 }, { 15, 1 }, { 15, 2 }, { 15, 3 }, { 15, 4 }, { 15, 5 },
+    { 15, 6 }, { 15, 7 }, { 15, 8 }, { 15, 9 }, { 1, 11 }, { 1, 12 },
+    { 2, 11 }, { 2, 12 }, { 3, 11 }, { 3, 12 }, { 4, 11 }, { 4, 12 },
+    { 5, 11 }, { 5, 12 }, { 6, 10 }, { 6, 11 }, { 6, 12 }, { 7, 10 },
+    { 7, 11 }, { 7, 12 }, { 8, 10 }, { 8, 11 }, { 8, 12 }, { 9, 10 },
+    { 9, 11 }, { 9, 12 }, { 10, 10 }, { 10, 11 }, { 10, 12 }, { 11, 10 },
+    { 11, 11 }, { 11, 12 }, { 12, 10 }, { 12, 11 }, { 12, 12 }, { 13, 10 },
+    { 13, 11 }, { 13, 12 }, { 14, 10 }, { 14, 11 }, { 14, 12 }, { 15, 10 },
+    { 15, 11 }, { 15, 12 },
+};
+
+static VLC dc_vlc;
+static VLC ac_vlc;
+
+typedef struct BRAWContext {
+    const AVClass    *class;
+
+    GetByteContext    gb;
+    GetBitContext     gbit;
+
+    uint32_t          header_size;
+
+    int               offsets[4][2];
+
+    int               nb_tiles_w;
+    int               nb_tiles_h;
+    int               tile_size_w[256];
+    int               tile_offset_w[257];
+    int               tile_size_h;
+    int               blocks_w_in_tile;
+    int               blocks_h_in_tile;
+
+    int               qscale[3];
+    int               quant[2][64];
+
+    ScanTable         scan;
+    IDCTDSPContext    idsp;
+
+    DECLARE_ALIGNED(32, int16_t, block)[4][64];
+    DECLARE_ALIGNED(32, uint8_t, out)[2][64];
+} BRAWContext;
+
+static int parse_frame_metadata(AVCodecContext *avctx)
+{
+    BRAWContext *s = avctx->priv_data;
+    GetByteContext *gb = &s->gb;
+
+    s->header_size = bytestream2_get_be32(gb);
+    if (s->header_size <= 8 ||
+        s->header_size - 8 >= bytestream2_get_bytes_left(gb))
+        return AVERROR_INVALIDDATA;
+    if (bytestream2_get_le32(gb) != MKTAG('b','m','d','f'))
+        return AVERROR_INVALIDDATA;
+    bytestream2_skip(gb, s->header_size - 8);
+
+    return 0;
+}
+
+static int decode_block(AVCodecContext *avctx,
+                        int16_t *dst, const int *quant,
+                        int *prev_dc, int max,
+                        int mul, int add,
+                        const uint8_t *scan)
+{
+    BRAWContext *s = avctx->priv_data;
+    GetBitContext *gbit = &s->gbit;
+    int dc_idx, sgnbit = 0, sign, len, val, code;
+
+    memset(dst, 0, 64 * 2);
+
+    dc_idx = get_vlc2(gbit, dc_vlc.table, dc_vlc.bits, 2);
+    if (dc_idx < 0)
+        return AVERROR_INVALIDDATA;
+
+    sign = dc_table[dc_idx][1];
+    sgnbit = get_bitsz(gbit, sign);
+    len = dc_table[dc_idx][0];
+    code = get_bitsz(gbit, len);
+    val = code + *prev_dc + dcval_tab[sgnbit][dc_table[dc_idx][2]];
+    *prev_dc = val;
+    dst[0] = FFMIN(((val * quant[0] + 0x8000) >> 16) + add, 32767);
+
+    for (int i = 0;;) {
+        int skip, len, ac_idx = get_vlc2(gbit, ac_vlc.table, ac_vlc.bits, 3);
+
+        if (ac_idx < 0)
+            return AVERROR_INVALIDDATA;
+        skip = ac_table[ac_idx][0];
+        if (skip == 255)
+            break;
+
+        len = ac_table[ac_idx][1];
+        val = get_bits_long(gbit, len);
+        i = i + 1 + skip;
+        if (i >= max)
+            return AVERROR_INVALIDDATA;
+        if (len && val < 1 << (len - 1))
+            val -= (1 << len) - 1;
+        dst[scan[i]] = (val * quant[scan[i]] + 0x8000) >> 16;
+    }
+
+    return 0;
+}
+
+static void decorrelate(AVCodecContext *avctx, AVFrame *frame, int pos_x, int pos_y)
+{
+    BRAWContext *s = avctx->priv_data;
+    uint16_t *dst = (uint16_t *)(frame->data[0] + pos_y * frame->linesize[0] + pos_x * 2);
+    uint16_t *dst0 = (uint16_t *)(frame->data[0] + pos_y * frame->linesize[0] + pos_x * 2);
+    uint16_t *dst1 = (uint16_t *)(frame->data[0] + (pos_y + 1) * frame->linesize[0] + (pos_x + 1) * 2);
+    uint16_t *src0 = (uint16_t *)(s->out[0]);
+    uint16_t *src1 = (uint16_t *)(s->out[1]);
+
+    for (int y = 0; y < 4; y++) {
+        for (int x = 0; x < 8; x++) {
+            dst0[x*2] = av_clip_uintp2(dst0[x*2] + src1[x] - 2048, 12);
+            dst1[x*2] = av_clip_uintp2(dst1[x*2] + src0[x] - 2048, 12);
+        }
+
+        src0 += 8;
+        src1 += 8;
+        dst0 += frame->linesize[0];
+        dst1 += frame->linesize[0];
+    }
+
+    for (int y = 0; y < 8; y++) {
+        for (int x = 0; x < 16; x++) {
+            dst[x] = (dst[x] << 4) + (dst[x] & 15);
+        }
+
+        dst += frame->linesize[0] / 2;
+    }
+}
+
+static int decode_tile(AVCodecContext *avctx, AVFrame *frame, int tile_x, int tile_y,
+                       int blocks_w_in_tile, int blocks_h_in_tile)
+{
+    BRAWContext *s = avctx->priv_data;
+    GetBitContext *gbit = &s->gbit;
+    int prev_dc[3];
+    int ret = 0;
+
+    prev_dc[0] = prev_dc[1] = prev_dc[2] = 0;
+
+    for (int y = 0; y < blocks_h_in_tile; y++) {
+        int pos_y = y * 8 + tile_y * s->tile_size_h;
+
+        for (int x = 0; x < blocks_w_in_tile; x++) {
+            int pos_x = s->tile_offset_w[tile_x] + x * 16;
+
+            ret = decode_block(avctx, s->block[0], s->quant[0], &prev_dc[0], 64, 1, 16384, ff_zigzag_direct);
+            if (ret < 0)
+                goto end;
+            ret = decode_block(avctx, s->block[1], s->quant[0], &prev_dc[0], 64, 1, 16384, ff_zigzag_direct);
+            if (ret < 0)
+                goto end;
+            ret = decode_block(avctx, s->block[2], s->quant[1], &prev_dc[1], 32, 1,  8192, half_scan);
+            if (ret < 0)
+                goto end;
+            ret = decode_block(avctx, s->block[3], s->quant[1], &prev_dc[2], 32, 1,  8192, half_scan);
+            if (ret < 0)
+                goto end;
+
+            ff_simple_idct_put_int16_12bit(frame->data[0] + pos_y * frame->linesize[0] + pos_x * 2,
+                             frame->linesize[0], s->block[0]);
+            ff_simple_idct_put_int16_12bit(frame->data[0] + pos_y * frame->linesize[0] + pos_x * 2 + 16,
+                             frame->linesize[0], s->block[1]);
+            ff_simple_idct84_put_int16_12bit(s->out[0], 16, s->block[2]);
+            ff_simple_idct84_put_int16_12bit(s->out[1], 16, s->block[3]);
+
+            decorrelate(avctx, frame, pos_x, pos_y);
+
+            skip_bits(gbit, 8);
+            if (get_bits_left(gbit) < 0)
+                return AVERROR_INVALIDDATA;
+        }
+    }
+
+    av_log(avctx, AV_LOG_DEBUG, "bits left: %d\n", get_bits_left(gbit));
+end:
+    return ret;
+}
+
+static int get_offset(AVCodecContext *avctx, int x)
+{
+    BRAWContext *s = avctx->priv_data;
+    GetByteContext *gb = &s->gb;
+    int a, b, nb, idx = 4;
+
+    a = bytestream2_get_byte(gb);
+    b = bytestream2_get_byte(gb);
+
+    nb = a * 2 + b;
+    if (nb != 0) {
+        int i = 0, j = 0;
+
+        while (i = j, (nb >> j & 1) == 0) {
+            j++;
+        }
+
+        idx = 4 - i;
+    }
+
+    s->offsets[x][0] = a << (idx & 0x1f);
+    s->offsets[x][1] = b << (idx & 0x1f);
+
+    av_log(avctx, AV_LOG_DEBUG, "offset:%d %dx%d\n", x, s->offsets[x][0], s->offsets[x][1]);
+
+    return 0;
+}
+
+static int get_offsets(AVCodecContext *avctx)
+{
+    for (int i = 0; i < 4; i++)
+        get_offset(avctx, i);
+
+    return 0;
+}
+
+static int decode_quants(AVCodecContext *avctx)
+{
+    BRAWContext *s = avctx->priv_data;
+    GetByteContext *gb = &s->gb;
+    int scale, dcq;
+
+    scale = (s->qscale[0] + 4) * 2048;
+    for (int n = 0; n < 64; n++) {
+        s->quant[0][n] = bytestream2_get_be16(gb) * scale;
+    }
+
+    dcq = s->qscale[1];
+    if (s->quant[0][0] <= dcq)
+        s->quant[0][0] = dcq;
+
+    scale = lrint(fmax(4.0, round((s->qscale[0] + 4) * 0.5 + 0.5)) * 2048.);
+
+    for (int n = 0; n < 32; n++) {
+        s->quant[1][n] = bytestream2_get_be16(gb) * scale;
+    }
+
+    dcq = s->qscale[2];
+    if (s->quant[1][0] <= dcq)
+        s->quant[1][0] = dcq;
+
+    return 0;
+}
+
+static int decode_tiles(AVCodecContext *avctx, AVFrame *frame, ThreadFrame *tframe)
+{
+    BRAWContext *s = avctx->priv_data;
+    GetBitContext *gbit = &s->gbit;
+    GetByteContext *gb = &s->gb;
+    uint32_t braw_size;
+    int version, ret, w, h;
+
+    if (bytestream2_get_le32(gb) != MKTAG('b','r','a','w'))
+        return AVERROR_INVALIDDATA;
+    braw_size = bytestream2_get_be32(gb);
+    if (braw_size < 4352)
+        return AVERROR_INVALIDDATA;
+    if (braw_size - 8 > bytestream2_get_bytes_left(gb))
+        return AVERROR_INVALIDDATA;
+
+    version = bytestream2_get_byte(gb);
+    av_log(avctx, AV_LOG_DEBUG, "version: %d\n", version);
+    if (version != 1 && version != 2)
+        return AVERROR_INVALIDDATA;
+    s->qscale[0] = bytestream2_get_byte(gb);
+    av_log(avctx, AV_LOG_DEBUG, "qscale[0]: %d\n", s->qscale[0]);
+
+    s->nb_tiles_w = bytestream2_get_byte(gb);
+    s->nb_tiles_h = bytestream2_get_byte(gb);
+    av_log(avctx, AV_LOG_DEBUG, "nb_tiles: %dx%d\n", s->nb_tiles_w, s->nb_tiles_h);
+    if (!s->nb_tiles_w ||
+        !s->nb_tiles_h ||
+        s->nb_tiles_w * s->nb_tiles_h > 1024)
+        return AVERROR_INVALIDDATA;
+
+    w = bytestream2_get_be16(gb);
+    h = bytestream2_get_be16(gb);
+    av_log(avctx, AV_LOG_DEBUG, "WxH: %dx%d\n", w, h);
+
+    s->tile_size_h = bytestream2_get_be16(gb);
+    av_log(avctx, AV_LOG_DEBUG, "tile_size_h = %d\n", s->tile_size_h);
+    if (s->tile_size_h & 7)
+        return AVERROR_INVALIDDATA;
+
+    bytestream2_skip(gb, 2);
+
+    ret = get_offsets(avctx);
+    if (ret < 0)
+        return ret;
+    bytestream2_skip(gb, 4);
+
+    s->qscale[1] = bytestream2_get_be16(gb);
+    av_log(avctx, AV_LOG_DEBUG, "qscale[1]: %d\n", s->qscale[1]);
+    s->qscale[2] = bytestream2_get_be16(gb);
+    av_log(avctx, AV_LOG_DEBUG, "qscale[2]: %d\n", s->qscale[2]);
+
+    bytestream2_skip(gb, 28);
+
+    s->tile_offset_w[0] = 0;
+
+    for (int x = 0; x < s->nb_tiles_w; x++) {
+        s->tile_size_w[x] = bytestream2_get_be16(gb);
+        s->tile_offset_w[x+1] = s->tile_offset_w[x] + s->tile_size_w[x];
+        av_log(avctx, AV_LOG_DEBUG, "tile_size_w[%d] = %d\n", x, s->tile_size_w[x]);
+        av_log(avctx, AV_LOG_DEBUG, "tile_offset_w[%d] = %d\n", x+1, s->tile_offset_w[x+1]);
+        if (s->tile_offset_w[x+1] > w)
+            return AVERROR_INVALIDDATA;
+    }
+
+    ret = ff_set_dimensions(avctx, s->tile_offset_w[s->nb_tiles_w], s->tile_size_h * s->nb_tiles_h);
+    if (ret < 0)
+        return ret;
+    avctx->width  = w;
+    avctx->height = h;
+
+    ret = ff_thread_get_buffer(avctx, tframe, 0);
+    if (ret < 0)
+        return ret;
+
+    bytestream2_seek(gb, 0x1180, SEEK_SET);
+
+    ret = decode_quants(avctx);
+    if (ret < 0)
+        return ret;
+
+    bytestream2_seek(gb, 0x180, SEEK_SET);
+
+    for (int x = 0; x < s->nb_tiles_w; x++) {
+        s->blocks_w_in_tile = s->tile_size_w[x] / 16;
+
+        for (int y = 0; y < s->nb_tiles_h; y++) {
+            uint32_t tile_offset = bytestream2_get_be32(gb);
+            int last_tile = (x == (s->nb_tiles_w - 1)) && (y == (s->nb_tiles_h - 1));
+            int tile_size = last_tile ? bytestream2_size(gb) - tile_offset - s->header_size : bytestream2_peek_be32(gb) - tile_offset;
+
+            if (tile_offset > bytestream2_size(gb) || tile_size <= 0)
+                return AVERROR_INVALIDDATA;
+
+            av_log(avctx, AV_LOG_DEBUG, "%dx%d: tile_bitstream_size: 0x%X, tile_bitstream_offset: 0x%X\n", x, y, tile_size, tile_offset + s->header_size);
+            ret = init_get_bits8(gbit, gb->buffer_start + tile_offset + s->header_size, tile_size);
+            if (ret < 0)
+                return ret;
+
+            if (y == s->nb_tiles_h - 1)
+                s->blocks_h_in_tile = (h - y * s->tile_size_h) / 8;
+            else
+                s->blocks_h_in_tile = s->tile_size_h / 8;
+
+            ret = decode_tile(avctx, frame, x, y, s->blocks_w_in_tile, s->blocks_h_in_tile);
+            if (ret < 0)
+                return ret;
+        }
+    }
+
+    return 0;
+}
+
+static int braw_decode_frame(AVCodecContext *avctx,
+                             void *data, int *got_frame,
+                             AVPacket *avpkt)
+{
+    BRAWContext *s = avctx->priv_data;
+    AVFrame *p = data;
+    ThreadFrame frame = { .f = data };
+    int ret;
+
+    if (avpkt->size <= 4608)
+        return AVERROR_INVALIDDATA;
+
+    bytestream2_init(&s->gb, avpkt->data, avpkt->size);
+    ret = parse_frame_metadata(avctx);
+    if (ret < 0)
+        return ret;
+
+    ret = decode_tiles(avctx, p, &frame);
+    if (ret < 0)
+        return ret;
+
+    p->pict_type = AV_PICTURE_TYPE_I;
+    p->key_frame = 1;
+    *got_frame = 1;
+
+    return avpkt->size;
+}
+
+static av_cold void braw_static_init(void)
+{
+    INIT_VLC_STATIC(&dc_vlc, 13, 16, dc_bits, 1, 1, dc_codes, 2, 2, 8192);
+    INIT_VLC_STATIC(&ac_vlc, 18, 194, ac_bits, 1, 1, ac_codes, 4, 4, 262144);
+}
+
+static av_cold int braw_decode_init(AVCodecContext *avctx)
+{
+    BRAWContext *s = avctx->priv_data;
+    static AVOnce init_once = AV_ONCE_INIT;
+    int ret;
+
+    avctx->pix_fmt = AV_PIX_FMT_BAYER_RGGB16;
+    avctx->bits_per_raw_sample = 12;
+
+    ff_idctdsp_init(&s->idsp, avctx);
+    ff_init_scantable(s->idsp.idct_permutation, &s->scan, ff_zigzag_direct);
+
+    ret = ff_thread_once(&init_once, braw_static_init);
+    if (ret)
+        return AVERROR_UNKNOWN;
+
+    return 0;
+}
+
+AVCodec ff_braw_decoder = {
+    .name             = "braw",
+    .long_name        = NULL_IF_CONFIG_SMALL("Blackmagic RAW"),
+    .type             = AVMEDIA_TYPE_VIDEO,
+    .id               = AV_CODEC_ID_BRAW,
+    .priv_data_size   = sizeof(BRAWContext),
+    .init             = braw_decode_init,
+    .decode           = braw_decode_frame,
+    .capabilities     = AV_CODEC_CAP_DR1 |
+                        AV_CODEC_CAP_FRAME_THREADS,
+    .caps_internal    = FF_CODEC_CAP_INIT_THREADSAFE |
+                        FF_CODEC_CAP_INIT_CLEANUP,
+};
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 529b838e5b..29cb4578e5 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -1747,6 +1747,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
         .long_name = NULL_IF_CONFIG_SMALL("MidiVid Archive Codec"),
         .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
     },
+    {
+        .id        = AV_CODEC_ID_BRAW,
+        .type      = AVMEDIA_TYPE_VIDEO,
+        .name      = "braw",
+        .long_name = NULL_IF_CONFIG_SMALL("Blackmagic RAW"),
+        .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
+    },
 
     /* various PCM "codecs" */
     {
diff --git a/libavcodec/simple_idct.c b/libavcodec/simple_idct.c
index 3b2e736538..a29f51164f 100644
--- a/libavcodec/simple_idct.c
+++ b/libavcodec/simple_idct.c
@@ -267,3 +267,44 @@ void ff_prores_idct_12(int16_t *block, const int16_t *qmat)
         idctSparseCol_int16_12bit(block + i);
     }
 }
+
+static inline void idct4ColPut_int16_12bit(uint16_t *dest, ptrdiff_t line_size, int16_t *in)
+{
+    const int X0 = 17734;
+    const int X1 = 42813;
+    const int X2 = 32768;
+    int a0 = (in[8*0] * X2);
+    int a2 = (in[8*2] * X2);
+    int a1 = in[8*1];
+    int a3 = in[8*3];
+    int c0 = a0 + a2;
+    int c1 = a0 - a2;
+    int c2 = a1 * X0 - a3 * X1;
+    int c3 = a3 * X0 + a1 * X1;
+    int d0 = av_clip_uintp2((c0 + c3) >> 16, 12);
+    int d1 = av_clip_uintp2((c1 + c2) >> 16, 12);
+    int d2 = av_clip_uintp2((c1 - c2) >> 16, 12);
+    int d3 = av_clip_uintp2((c0 - c3) >> 16, 12);
+
+    dest[0] = d0;
+    dest += line_size;
+    dest[0] = d1;
+    dest += line_size;
+    dest[0] = d2;
+    dest += line_size;
+    dest[0] = d3;
+}
+
+void ff_simple_idct84_put_int16_12bit(uint8_t *dest_, ptrdiff_t line_size, int16_t *block)
+{
+    uint16_t *dest = (uint16_t *)dest_;
+    int i;
+
+    line_size /= 2;
+
+    for (i = 0; i < 4; i++)
+        idctRowCondDC_int16_12bit(block + i * 8, 0);
+
+    for (i = 0; i < 8; i++)
+        idct4ColPut_int16_12bit(dest + i, line_size, block + i);
+}
diff --git a/libavcodec/simple_idct.h b/libavcodec/simple_idct.h
index 20578b3347..98d3074ae3 100644
--- a/libavcodec/simple_idct.h
+++ b/libavcodec/simple_idct.h
@@ -61,4 +61,6 @@ void ff_simple_idct84_add(uint8_t *dest, ptrdiff_t line_size, int16_t *block);
 void ff_simple_idct48_add(uint8_t *dest, ptrdiff_t line_size, int16_t *block);
 void ff_simple_idct44_add(uint8_t *dest, ptrdiff_t line_size, int16_t *block);
 
+void ff_simple_idct84_put_int16_12bit(uint8_t *dest, ptrdiff_t line_size, int16_t *block);
+
 #endif /* AVCODEC_SIMPLE_IDCT_H */
diff --git a/libavformat/isom.c b/libavformat/isom.c
index 824e811177..e9f8590812 100644
--- a/libavformat/isom.c
+++ b/libavformat/isom.c
@@ -312,6 +312,13 @@ const AVCodecTag ff_codec_movvideo_tags[] = {
 
     { AV_CODEC_ID_PIXLET, MKTAG('p', 'x', 'l', 't') },
 
+    { AV_CODEC_ID_BRAW, MKTAG('b', 'r', 'x', 'q') },
+    { AV_CODEC_ID_BRAW, MKTAG('b', 'r', 'h', 'q') },
+    { AV_CODEC_ID_BRAW, MKTAG('b', 'r', 's', 't') },
+    { AV_CODEC_ID_BRAW, MKTAG('b', 'r', 'l', 't') },
+    { AV_CODEC_ID_BRAW, MKTAG('b', 'r', 'v', 'l') },
+    { AV_CODEC_ID_BRAW, MKTAG('b', 'r', 'v', 'm') },
+
     { AV_CODEC_ID_NONE, 0 },
 };
 
-- 
2.17.1



More information about the ffmpeg-devel mailing list