Most of the work was done by Paul B Mahol. I cleaned the patch up, improved the code a bit, and added slice threading. --- configure | 1 + libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/braw.c | 666 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 669 insertions(+) create mode 100755 libavcodec/braw.c
diff --git a/configure b/configure index bd4f872376..1171260d57 100755 --- a/configure +++ b/configure @@ -2943,6 +2943,7 @@ av1_decoder_select="atsc_a53 cbs_av1 dovi_rpudec" bink_decoder_select="blockdsp hpeldsp" binkaudio_dct_decoder_select="wma_freqs" binkaudio_rdft_decoder_select="wma_freqs" +braw_decoder_select="idctdsp" cavs_decoder_select="blockdsp golomb h264chroma idctdsp qpeldsp videodsp" clearvideo_decoder_select="idctdsp" cllc_decoder_select="bswapdsp" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 7bd1dbec9a..50733ab048 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -289,6 +289,7 @@ OBJS-$(CONFIG_BMP_ENCODER) += bmpenc.o OBJS-$(CONFIG_BMV_AUDIO_DECODER) += bmvaudio.o OBJS-$(CONFIG_BMV_VIDEO_DECODER) += bmvvideo.o OBJS-$(CONFIG_BONK_DECODER) += bonk.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 f10519617e..277e17acc3 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -69,6 +69,7 @@ extern const FFCodec ff_bitpacked_encoder; extern const FFCodec ff_bmp_encoder; extern const FFCodec ff_bmp_decoder; extern const FFCodec ff_bmv_video_decoder; +extern const FFCodec ff_braw_decoder; extern const FFCodec ff_brender_pix_decoder; extern const FFCodec ff_c93_decoder; extern const FFCodec ff_cavs_decoder; diff --git a/libavcodec/braw.c b/libavcodec/braw.c new file mode 100755 index 0000000000..2246b2d645 --- /dev/null +++ b/libavcodec/braw.c @@ -0,0 +1,666 @@ +/* + * Blackmagic RAW codec + * + * Copyright (c) 2019 Paul B Mahol + * Copyright (c) 2025 Lynne + * + * 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/mem_internal.h" + +#include "decode.h" +#include "thread.h" +#include "codec_internal.h" +#include "mpegvideo.h" +#include "bytestream.h" +#include "get_bits.h" +#include "vlc.h" +#include "simple_idct.h" + +#define IN_IDCT_DEPTH 16 +#define PRORES_ONLY +#define BIT_DEPTH 12 +#include "simple_idct_template.c" + +#define BRAW_MAX_TILES 1024 + +typedef struct BRAWTile { + const uint8_t *data; + uint32_t size; + int x; + int y; + int blocks_w_in_tile; + int blocks_h_in_tile; + int ret; +} BRAWTile; + +typedef struct BRAWContext { + const AVClass *class; + + enum AVPixelFormat pix_fmt; + + GetByteContext gb; + + 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 qscale[3]; + int quant[2][64]; + + ScanTable scan; + IDCTDSPContext idsp; + + BRAWTile tiles[BRAW_MAX_TILES]; + AVFrame *frame; +} BRAWContext; + +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_bits[16] = { + 2, 3, 3, 3, 3, 3, 5, 5, 6, 12, 12, 12, 12, 5, 12, 13, +}; + +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_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, +}; + +#define DC_VLC_BITS 13 +#define AC_VLC_BITS 18 +static VLCElem dc_vlc[1 << DC_VLC_BITS]; +static VLCElem ac_vlc[1 << AC_VLC_BITS]; + +static av_cold void braw_static_init(void) +{ + VLC_INIT_STATIC_TABLE(dc_vlc, DC_VLC_BITS, FF_ARRAY_ELEMS(dc_bits), dc_bits, 1, 1, dc_codes, 2, 2, 0); + VLC_INIT_STATIC_TABLE(ac_vlc, AC_VLC_BITS, FF_ARRAY_ELEMS(ac_bits), ac_bits, 1, 1, ac_codes, 4, 4, 0); +} + +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; +} + +static void idct84_put_int16_12bit(uint16_t *dest, ptrdiff_t line_size, int16_t *block) +{ + line_size >>= 1; + + for (int i = 0; i < 4; i++) + idctRowCondDC_int16_12bit(block + i * 8, 0); + + for (int i = 0; i < 8; i++) + idct4ColPut_int16_12bit(dest + i, line_size, block + i); +} + +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_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_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 int decode_block(AVCodecContext *avctx, GetBitContext *gbit, + int16_t *dst, const int *quant, + int *prev_dc, int max, + int mul, int add, + const uint8_t *scan) +{ + int dc_idx, sgnbit = 0, sign, len, val, code; + + memset(dst, 0, 64 * 2); + + dc_idx = get_vlc2(gbit, dc_vlc, 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; + + ac_idx = get_vlc2(gbit, ac_vlc, 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 (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, uint8_t *data, ptrdiff_t linesize, + uint8_t *out0, uint8_t *out1, int pos_x, int pos_y) +{ + uint16_t *dst = (uint16_t *)(data + pos_y * linesize + pos_x * 2); + uint16_t *dst0 = (uint16_t *)(data + pos_y * linesize + pos_x * 2); + uint16_t *dst1 = (uint16_t *)(data + (pos_y + 1) * linesize + (pos_x + 1) * 2); + uint16_t *src0 = (uint16_t *)(out0); + uint16_t *src1 = (uint16_t *)(out1); + + 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 += linesize; + dst1 += linesize; + } + + for (int y = 0; y < 8; y++) { + for (int x = 0; x < 16; x++) + dst[x] = (dst[x] << 4) + (dst[x] & 15); + + dst += linesize >> 1; + } +} + +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 int decode_tile_thread(AVCodecContext *avctx, void *arg, int jobnr, int threadnr) +{ + BRAWContext *s = avctx->priv_data; + BRAWTile *t = &s->tiles[jobnr]; + + GetBitContext gb; + int prev_dc[3]; + int ret = 0; + + LOCAL_ALIGNED_32(int16_t, block, [4], [64]); + LOCAL_ALIGNED_32(uint16_t, out, [2], [64]); + + t->ret = -1; + + ret = init_get_bits8(&gb, t->data, t->size); + if (ret < 0) + return ret; + + prev_dc[0] = prev_dc[1] = prev_dc[2] = 0; + + for (int y = 0; y < t->blocks_h_in_tile; y++) { + int pos_y = y * 8 + t->y * s->tile_size_h; + + for (int x = 0; x < t->blocks_w_in_tile; x++) { + int pos_x = s->tile_offset_w[t->x] + x * 16; + + ret = decode_block(avctx, &gb, block[0], s->quant[0], &prev_dc[0], 64, 1, 16384, ff_zigzag_direct); + if (ret < 0) + return ret; + ret = decode_block(avctx, &gb, block[1], s->quant[0], &prev_dc[0], 64, 1, 16384, ff_zigzag_direct); + if (ret < 0) + return ret; + ret = decode_block(avctx, &gb, block[2], s->quant[1], &prev_dc[1], 32, 1, 8192, half_scan); + if (ret < 0) + return ret; + ret = decode_block(avctx, &gb, block[3], s->quant[1], &prev_dc[2], 32, 1, 8192, half_scan); + if (ret < 0) + return ret; + + ff_simple_idct_put_int16_12bit(s->frame->data[0] + pos_y * s->frame->linesize[0] + pos_x * 2, + s->frame->linesize[0], block[0]); + ff_simple_idct_put_int16_12bit(s->frame->data[0] + pos_y * s->frame->linesize[0] + pos_x * 2 + 16, + s->frame->linesize[0], block[1]); + idct84_put_int16_12bit(out[0], 16, block[2]); + idct84_put_int16_12bit(out[1], 16, block[3]); + + decorrelate(avctx, s->frame->data[0], s->frame->linesize[0], (uint8_t *)out[0], (uint8_t *)out[1], pos_x, pos_y); + + skip_bits(&gb, 8); + if (get_bits_left(&gb) < 0) + return AVERROR_INVALIDDATA; + } + } + + t->ret = 0; + av_log(avctx, AV_LOG_DEBUG, "bits left: %d\n", get_bits_left(&gb)); + return 0; +} + +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 parse_frame_header(AVCodecContext *avctx) +{ + BRAWContext *s = avctx->priv_data; + GetByteContext *gb = &s->gb; + + uint32_t braw_size; + int version, ret, w, h; + enum AVPixelFormat pix_fmt; + + 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); + + 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 > BRAW_MAX_TILES) + 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; + + pix_fmt = AV_PIX_FMT_BAYER_RGGB16; + avctx->bits_per_raw_sample = 12; + if (pix_fmt != s->pix_fmt) { + avctx->pix_fmt = s->pix_fmt = pix_fmt; + } + + return 0; +} + +static int parse_tiles(AVCodecContext *avctx) +{ + BRAWContext *s = avctx->priv_data; + GetByteContext *gb = &s->gb; + + for (int x = 0; x < s->nb_tiles_w; x++) { + for (int y = 0; y < s->nb_tiles_h; y++) { + uint32_t offset; + BRAWTile *t = &s->tiles[y*s->nb_tiles_w + x]; + + int last_tile = (x == (s->nb_tiles_w - 1)) && (y == (s->nb_tiles_h - 1)); + + t->x = x; + t->y = y; + t->blocks_w_in_tile = s->tile_size_w[x] / 16; + if (y == s->nb_tiles_h - 1) + t->blocks_h_in_tile = (avctx->height - y * s->tile_size_h) / 8; + else + t->blocks_h_in_tile = s->tile_size_h / 8; + offset = bytestream2_get_be32(gb); + t->size = last_tile ? bytestream2_size(gb) - offset - s->header_size : bytestream2_peek_be32(gb) - offset; + + if (offset > bytestream2_size(gb) || t->size <= 0) + return AVERROR_INVALIDDATA; + + t->data = gb->buffer_start + offset + s->header_size; + + av_log(avctx, AV_LOG_DEBUG, "%dx%d: tile_bitstream_size: 0x%X, tile_bitstream_offset: 0x%X\n", x, y, t->size, offset + s->header_size); + } + } + + return 0; +} + +static int decode_frame(AVCodecContext *avctx) +{ + BRAWContext *s = avctx->priv_data; + int error = 0, tile_count = s->nb_tiles_w * s->nb_tiles_h; + + avctx->execute2(avctx, decode_tile_thread, NULL, NULL, tile_count); + + for (int i = 0; i < tile_count; i++) + error += s->tiles[i].ret < 0; + + if (error) + s->frame->decode_error_flags = FF_DECODE_ERROR_INVALID_BITSTREAM; + if (error < tile_count) + return 0; + + return s->tiles[0].ret; +} + +static int braw_decode_frame(AVCodecContext *avctx, AVFrame *f, int *got_frame, + AVPacket *avpkt) +{ + int ret; + BRAWContext *s = avctx->priv_data; + + if (avpkt->size <= 4608) + return AVERROR_INVALIDDATA; + + bytestream2_init(&s->gb, avpkt->data, avpkt->size); + ret = parse_frame_header(avctx); + if (ret < 0) + return ret; + + ret = ff_thread_get_buffer(avctx, f, 0); + if (ret < 0) + return ret; + ff_thread_finish_setup(avctx); + + bytestream2_seek(&s->gb, 0x1180, SEEK_SET); + ret = decode_quants(avctx); + if (ret < 0) + return ret; + + bytestream2_seek(&s->gb, 0x180, SEEK_SET); + ret = parse_tiles(avctx); + if (ret < 0) + return ret; + + s->frame = f; + ret = decode_frame(avctx); + if (ret < 0) + return ret; + + f->pict_type = AV_PICTURE_TYPE_I; + f->flags = AV_FRAME_FLAG_KEY; + *got_frame = 1; + + return avpkt->size; +} + +static av_cold int braw_decode_init(AVCodecContext *avctx) +{ + BRAWContext *s = avctx->priv_data; + static AVOnce init_once = AV_ONCE_INIT; + int ret; + + 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; + + s->pix_fmt = AV_PIX_FMT_NONE; + + return 0; +} + +#if HAVE_THREADS +static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src) +{ + BRAWContext *csrc = src->priv_data; + BRAWContext *cdst = dst->priv_data; + + cdst->pix_fmt = csrc->pix_fmt; + + return 0; +} +#endif + +const FFCodec ff_braw_decoder = { + .p.name = "braw", + CODEC_LONG_NAME("Blackmagic RAW"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_BRAW, + .priv_data_size = sizeof(BRAWContext), + .init = braw_decode_init, + FF_CODEC_DECODE_CB(braw_decode_frame), + UPDATE_THREAD_CONTEXT(update_thread_context), + .p.capabilities = AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_SLICE_THREADS | + AV_CODEC_CAP_FRAME_THREADS, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, +}; + -- 2.49.0.395.g12beb8f557c _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".