[FFmpeg-devel] [PATCH v2 2/2] Newtek SpeedHQ decoder.
Steinar H. Gunderson
steinar+ffmpeg at gunderson.no
Sun Jan 8 13:07:09 EET 2017
This decoder can decode all existing SpeedHQ formats (SHQ0–5, 7, and 9),
including correct decoding of the alpha channel.
1080p is decoded in 142 fps on one core of my i7-4600U (2.1 GHz Haswell),
about evenly split between bitstream reader and IDCT. There is currently
no attempt at slice or frame threading, even though the format trivially
supports both.
NewTek very helpfully provided a full set of SHQ samples, as well as
source code for an SHQ2 encoder (not included) and assistance with
understanding some details of the format.
---
libavcodec/Makefile | 1 +
libavcodec/allcodecs.c | 1 +
libavcodec/avcodec.h | 1 +
libavcodec/codec_desc.c | 7 +
libavcodec/get_bits.h | 14 +
libavcodec/mpeg12.c | 15 +-
libavcodec/mpeg12.h | 9 +
libavcodec/speedhq.c | 678 ++++++++++++++++++++++++++++++++++++++++++++++++
libavcodec/vlc.h | 9 +
libavformat/riff.c | 8 +
10 files changed, 732 insertions(+), 11 deletions(-)
create mode 100644 libavcodec/speedhq.c
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 44e416e6cd..7864079271 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -521,6 +521,7 @@ OBJS-$(CONFIG_SOL_DPCM_DECODER) += dpcm.o
OBJS-$(CONFIG_SONIC_DECODER) += sonic.o
OBJS-$(CONFIG_SONIC_ENCODER) += sonic.o
OBJS-$(CONFIG_SONIC_LS_ENCODER) += sonic.o
+OBJS-$(CONFIG_SPEEDHQ_DECODER) += speedhq.o simple_idct.o
OBJS-$(CONFIG_SP5X_DECODER) += sp5xdec.o
OBJS-$(CONFIG_SRT_DECODER) += srtdec.o ass.o htmlsubtitles.o
OBJS-$(CONFIG_SRT_ENCODER) += srtenc.o ass_split.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 4540ef7962..22a93f305c 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -316,6 +316,7 @@ void avcodec_register_all(void)
REGISTER_DECODER(SMVJPEG, smvjpeg);
REGISTER_ENCDEC (SNOW, snow);
REGISTER_DECODER(SP5X, sp5x);
+ REGISTER_DECODER(SPEEDHQ, speedhq);
REGISTER_ENCDEC (SUNRAST, sunrast);
REGISTER_ENCDEC (SVQ1, svq1);
REGISTER_DECODER(SVQ3, svq3);
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index ca8b786077..4d26d14ccc 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -413,6 +413,7 @@ enum AVCodecID {
AV_CODEC_ID_YLC,
AV_CODEC_ID_PSD,
AV_CODEC_ID_PIXLET,
+ AV_CODEC_ID_SPEEDHQ,
/* various PCM "codecs" */
AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index e7ef368f49..09d3c4809e 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -1291,6 +1291,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
.props = AV_CODEC_PROP_LOSSLESS,
},
{
+ .id = AV_CODEC_ID_SPEEDHQ,
+ .type = AVMEDIA_TYPE_VIDEO,
+ .name = "speedhq",
+ .long_name = NULL_IF_CONFIG_SMALL("NewTek SpeedHQ"),
+ .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
+ },
+ {
.id = AV_CODEC_ID_WRAPPED_AVFRAME,
.type = AVMEDIA_TYPE_VIDEO,
.name = "wrapped_avframe",
diff --git a/libavcodec/get_bits.h b/libavcodec/get_bits.h
index c094a14caa..c530015169 100644
--- a/libavcodec/get_bits.h
+++ b/libavcodec/get_bits.h
@@ -229,6 +229,20 @@ static inline int get_xbits(GetBitContext *s, int n)
return (NEG_USR32(sign ^ cache, n) ^ sign) - sign;
}
+static inline int get_xbits_le(GetBitContext *s, int n)
+{
+ register int sign;
+ register int32_t cache;
+ OPEN_READER(re, s);
+ av_assert2(n>0 && n<=25);
+ UPDATE_CACHE_LE(re, s);
+ cache = GET_CACHE(re, s);
+ sign = sign_extend(~cache, n) >> 31;
+ LAST_SKIP_BITS(re, s, n);
+ CLOSE_READER(re, s);
+ return (zero_extend(sign ^ cache, n) ^ sign) - sign;
+}
+
static inline int get_sbits(GetBitContext *s, int n)
{
register int tmp;
diff --git a/libavcodec/mpeg12.c b/libavcodec/mpeg12.c
index a61d45b39a..ab6c19c615 100644
--- a/libavcodec/mpeg12.c
+++ b/libavcodec/mpeg12.c
@@ -67,20 +67,13 @@ static const uint8_t table_mb_btype[11][2] = {
{ 2, 5 }, // 0x1E MB_QUANT|MB_FOR|MB_BACK|MB_PAT
};
-#define INIT_2D_VLC_RL(rl, static_size)\
-{\
- static RL_VLC_ELEM rl_vlc_table[static_size];\
- rl.rl_vlc[0] = rl_vlc_table;\
- init_2d_vlc_rl(&rl, static_size);\
-}
-
-static av_cold void init_2d_vlc_rl(RLTable *rl, unsigned static_size)
+av_cold void ff_init_2d_vlc_rl(RLTable *rl, unsigned static_size, int flags)
{
int i;
VLC_TYPE table[680][2] = {{0}};
VLC vlc = { .table = table, .table_allocated = static_size };
av_assert0(static_size <= FF_ARRAY_ELEMS(table));
- init_vlc(&vlc, TEX_VLC_BITS, rl->n + 2, &rl->table_vlc[0][1], 4, 2, &rl->table_vlc[0][0], 4, 2, INIT_VLC_USE_NEW_STATIC);
+ init_vlc(&vlc, TEX_VLC_BITS, rl->n + 2, &rl->table_vlc[0][1], 4, 2, &rl->table_vlc[0][0], 4, 2, INIT_VLC_USE_NEW_STATIC | flags);
for (i = 0; i < vlc.table_size; i++) {
int code = vlc.table[i][0];
@@ -173,8 +166,8 @@ av_cold void ff_mpeg12_init_vlcs(void)
ff_rl_init(&ff_rl_mpeg1, ff_mpeg12_static_rl_table_store[0]);
ff_rl_init(&ff_rl_mpeg2, ff_mpeg12_static_rl_table_store[1]);
- INIT_2D_VLC_RL(ff_rl_mpeg1, 680);
- INIT_2D_VLC_RL(ff_rl_mpeg2, 674);
+ INIT_2D_VLC_RL(ff_rl_mpeg1, 680, 0);
+ INIT_2D_VLC_RL(ff_rl_mpeg2, 674, 0);
}
}
diff --git a/libavcodec/mpeg12.h b/libavcodec/mpeg12.h
index ad9b00c08b..f551504b8c 100644
--- a/libavcodec/mpeg12.h
+++ b/libavcodec/mpeg12.h
@@ -29,6 +29,15 @@ extern uint8_t ff_mpeg12_static_rl_table_store[2][2][2*MAX_RUN + MAX_LEVEL + 3];
void ff_mpeg12_common_init(MpegEncContext *s);
+#define INIT_2D_VLC_RL(rl, static_size, flags)\
+{\
+ static RL_VLC_ELEM rl_vlc_table[static_size];\
+ rl.rl_vlc[0] = rl_vlc_table;\
+ ff_init_2d_vlc_rl(&rl, static_size, flags);\
+}
+
+void ff_init_2d_vlc_rl(RLTable *rl, unsigned static_size, int flags);
+
static inline int decode_dc(GetBitContext *gb, int component)
{
int code, diff;
diff --git a/libavcodec/speedhq.c b/libavcodec/speedhq.c
new file mode 100644
index 0000000000..c718ac2960
--- /dev/null
+++ b/libavcodec/speedhq.c
@@ -0,0 +1,678 @@
+/*
+ * NewTek SpeedHQ codec
+ * Copyright 2017 Steinar H. Gunderson
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * NewTek SpeedHQ decoder.
+ */
+
+#define BITSTREAM_READER_LE
+
+#include "libavutil/attributes.h"
+
+#include "avcodec.h"
+#include "get_bits.h"
+#include "internal.h"
+#include "mathops.h"
+#include "mpeg12.h"
+#include "mpeg12data.h"
+#include "mpeg12vlc.h"
+
+#define MAX_INDEX (64 - 1)
+
+/*
+ * 5 bits makes for very small tables, with no more than two lookups needed
+ * for the longest (10-bit) codes.
+ */
+#define ALPHA_VLC_BITS 5
+
+typedef struct SHQContext {
+ AVCodecContext *avctx;
+ BlockDSPContext bdsp;
+ IDCTDSPContext idsp;
+ ScanTable intra_scantable;
+ int quant_matrix[64];
+ enum { SHQ_SUBSAMPLING_420, SHQ_SUBSAMPLING_422, SHQ_SUBSAMPLING_444 }
+ subsampling;
+ enum { SHQ_NO_ALPHA, SHQ_RLE_ALPHA, SHQ_DCT_ALPHA } alpha_type;
+} SHQContext;
+
+
+/* AC codes: Very similar but not identical to MPEG-2. */
+static uint16_t speedhq_vlc[123][2] = {
+ {0x02, 2}, {0x06, 3}, {0x07, 4}, {0x1c, 5},
+ {0x1d, 5}, {0x05, 6}, {0x04, 6}, {0x7b, 7},
+ {0x7c, 7}, {0x23, 8}, {0x22, 8}, {0xfa, 8},
+ {0xfb, 8}, {0xfe, 8}, {0xff, 8}, {0x1f,14},
+ {0x1e,14}, {0x1d,14}, {0x1c,14}, {0x1b,14},
+ {0x1a,14}, {0x19,14}, {0x18,14}, {0x17,14},
+ {0x16,14}, {0x15,14}, {0x14,14}, {0x13,14},
+ {0x12,14}, {0x11,14}, {0x10,14}, {0x18,15},
+ {0x17,15}, {0x16,15}, {0x15,15}, {0x14,15},
+ {0x13,15}, {0x12,15}, {0x11,15}, {0x10,15},
+ {0x02, 3}, {0x06, 5}, {0x79, 7}, {0x27, 8},
+ {0x20, 8}, {0x16,13}, {0x15,13}, {0x1f,15},
+ {0x1e,15}, {0x1d,15}, {0x1c,15}, {0x1b,15},
+ {0x1a,15}, {0x19,15}, {0x13,16}, {0x12,16},
+ {0x11,16}, {0x10,16}, {0x18,13}, {0x17,13},
+ {0x05, 5}, {0x07, 7}, {0xfc, 8}, {0x0c,10},
+ {0x14,13}, {0x18,12}, {0x14,12}, {0x13,12},
+ {0x10,12}, {0x1a,13}, {0x19,13}, {0x07, 5},
+ {0x26, 8}, {0x1c,12}, {0x13,13}, {0x1b,12},
+ {0x06, 6}, {0xfd, 8}, {0x12,12}, {0x1d,12},
+ {0x07, 6}, {0x04, 9}, {0x12,13}, {0x06, 7},
+ {0x1e,12}, {0x14,16}, {0x04, 7}, {0x15,12},
+ {0x05, 7}, {0x11,12}, {0x78, 7}, {0x11,13},
+ {0x7a, 7}, {0x10,13}, {0x21, 8}, {0x1a,16},
+ {0x25, 8}, {0x19,16}, {0x24, 8}, {0x18,16},
+ {0x05, 9}, {0x17,16}, {0x07, 9}, {0x16,16},
+ {0x0d,10}, {0x15,16}, {0x1f,12}, {0x1a,12},
+ {0x19,12}, {0x17,12}, {0x16,12}, {0x1f,13},
+ {0x1e,13}, {0x1d,13}, {0x1c,13}, {0x1b,13},
+ {0x1f,16}, {0x1e,16}, {0x1d,16}, {0x1c,16},
+ {0x1b,16},
+ {0x01,6}, /* escape */
+ {0x06,4}, /* EOB */
+};
+
+static const uint8_t speedhq_level[121] = {
+ 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40,
+ 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 1,
+ 2, 3, 4, 5, 1, 2, 3, 4,
+ 1, 2, 3, 1, 2, 3, 1, 2,
+ 1, 2, 1, 2, 1, 2, 1, 2,
+ 1, 2, 1, 2, 1, 2, 1, 2,
+ 1, 2, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1,
+};
+
+static const uint8_t speedhq_run[121] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 3,
+ 3, 3, 3, 3, 4, 4, 4, 4,
+ 5, 5, 5, 6, 6, 6, 7, 7,
+ 8, 8, 9, 9, 10, 10, 11, 11,
+ 12, 12, 13, 13, 14, 14, 15, 15,
+ 16, 16, 17, 18, 19, 20, 21, 22,
+ 23, 24, 25, 26, 27, 28, 29, 30,
+ 31,
+};
+
+static RLTable ff_rl_speedhq = {
+ 121,
+ 121,
+ speedhq_vlc,
+ speedhq_run,
+ speedhq_level,
+};
+
+/* NOTE: The first element is always 16, unscaled. */
+static const uint16_t unscaled_quant_matrix[64] = {
+ 16, 16, 19, 22, 26, 27, 29, 34,
+ 16, 16, 22, 24, 27, 29, 34, 37,
+ 19, 22, 26, 27, 29, 34, 34, 38,
+ 22, 22, 26, 27, 29, 34, 37, 40,
+ 22, 26, 27, 29, 32, 35, 40, 48,
+ 26, 27, 29, 32, 35, 40, 48, 58,
+ 26, 27, 29, 34, 38, 46, 56, 69,
+ 27, 29, 35, 38, 46, 56, 69, 83
+};
+
+static uint8_t ff_speedhq_static_rl_table_store[2][2*MAX_RUN + MAX_LEVEL + 3];
+
+static VLC ff_dc_lum_vlc_le;
+static VLC ff_dc_chroma_vlc_le;
+static VLC ff_dc_alpha_run_vlc_le;
+static VLC ff_dc_alpha_level_vlc_le;
+
+static inline int decode_dc_le(GetBitContext *gb, int component)
+{
+ int code, diff;
+
+ if (component == 0 || component == 3) {
+ code = get_vlc2(gb, ff_dc_lum_vlc_le.table, DC_VLC_BITS, 2);
+ } else {
+ code = get_vlc2(gb, ff_dc_chroma_vlc_le.table, DC_VLC_BITS, 2);
+ }
+ if (code < 0) {
+ av_log(NULL, AV_LOG_ERROR, "invalid dc code at\n");
+ return 0xffff;
+ }
+ if (!code) {
+ diff = 0;
+ } else {
+ diff = get_xbits_le(gb, code);
+ }
+ return diff;
+}
+
+static inline int decode_alpha_block(const SHQContext *s, GetBitContext *gb, uint8_t last_alpha[16], uint8_t *dest, int linesize)
+{
+ uint8_t block[128];
+ int i = 0, x, y;
+
+ memset(block, 0, sizeof(block));
+
+ {
+ OPEN_READER(re, gb);
+
+ for ( ;; ) {
+ int run, level;
+
+ UPDATE_CACHE_LE(re, gb);
+ GET_VLC(run, re, gb, ff_dc_alpha_run_vlc_le.table, ALPHA_VLC_BITS, 2);
+
+ if (run == 128) break;
+ i += run;
+ if (i >= 128)
+ return AVERROR_INVALIDDATA;
+
+ UPDATE_CACHE_LE(re, gb);
+ GET_VLC(level, re, gb, ff_dc_alpha_level_vlc_le.table, ALPHA_VLC_BITS, 2);
+ block[i++] = level;
+ }
+
+ CLOSE_READER(re, gb);
+ }
+
+ for (y = 0; y < 8; y++) {
+ for (x = 0; x < 16; x++) {
+ last_alpha[x] -= block[y * 16 + x];
+ }
+ memcpy(dest, last_alpha, 16);
+ dest += linesize;
+ }
+
+ return 0;
+}
+
+static inline int decode_dct_block(const SHQContext *s, GetBitContext *gb, int last_dc[4], int component, uint8_t *dest, int linesize)
+{
+ const int *quant_matrix = s->quant_matrix;
+ const uint8_t *scantable = s->intra_scantable.permutated;
+ int16_t block[64];
+ int dc_offset;
+
+ s->bdsp.clear_block(block);
+
+ dc_offset = decode_dc_le(gb, component);
+ last_dc[component] -= dc_offset; /* Note: Opposite of most codecs. */
+ block[scantable[0]] = last_dc[component]; /* quant_matrix[0] is always 16. */
+
+ /* Read AC coefficients. */
+ {
+ int i = 0;
+ OPEN_READER(re, gb);
+ for ( ;; ) {
+ int level, run;
+ UPDATE_CACHE_LE(re, gb);
+ GET_RL_VLC(level, run, re, gb, ff_rl_speedhq.rl_vlc[0],
+ TEX_VLC_BITS, 2, 0);
+ if (level == 127) {
+ break;
+ } else if (level) {
+ i += run;
+ if (i > MAX_INDEX)
+ return AVERROR_INVALIDDATA;
+ /* If next bit is 1, level = -level */
+ level = (level ^ SHOW_SBITS(re, gb, 1)) -
+ SHOW_SBITS(re, gb, 1);
+ LAST_SKIP_BITS(re, gb, 1);
+ } else {
+ /* Escape. */
+#if MIN_CACHE_BITS < 6 + 6 + 12
+#error MIN_CACHE_BITS is too small for the escape code, add UPDATE_CACHE
+#endif
+ run = SHOW_UBITS(re, gb, 6) + 1;
+ SKIP_BITS(re, gb, 6);
+ level = SHOW_UBITS(re, gb, 12) - 2048;
+ LAST_SKIP_BITS(re, gb, 12);
+
+ i += run;
+ if (i > MAX_INDEX)
+ return AVERROR_INVALIDDATA;
+ }
+
+ block[scantable[i]] = (level * quant_matrix[i]) >> 4;
+ }
+ CLOSE_READER(re, gb);
+ }
+
+ s->idsp.idct_put(dest, linesize, block);
+
+ return 0;
+}
+
+static int decode_speedhq_field(const SHQContext *s, const uint8_t *buf, int buf_size, AVFrame *frame, int field_number, int start, int end, int line_stride)
+{
+ int ret, slice_number, slice_offsets[5];
+ int linesize_y = frame->linesize[0] * line_stride;
+ int linesize_cb = frame->linesize[1] * line_stride;
+ int linesize_cr = frame->linesize[2] * line_stride;
+ int linesize_a;
+
+ if (s->alpha_type != SHQ_NO_ALPHA)
+ linesize_a = frame->linesize[3] * line_stride;
+
+ if (end < start || end - start < 3 || end > buf_size)
+ return AVERROR_INVALIDDATA;
+
+ slice_offsets[0] = start;
+ slice_offsets[4] = end;
+ for (slice_number = 1; slice_number < 4; slice_number++) {
+ uint32_t last_offset, slice_len;
+
+ last_offset = slice_offsets[slice_number - 1];
+ slice_len = AV_RL24(buf + last_offset);
+ slice_offsets[slice_number] = last_offset + slice_len;
+
+ if (slice_len < 3 || slice_offsets[slice_number] >= end - 3)
+ return AVERROR_INVALIDDATA;
+ }
+
+ for (slice_number = 0; slice_number < 4; slice_number++) {
+ GetBitContext gb;
+ uint32_t slice_begin, slice_end;
+ int x, y;
+
+ slice_begin = slice_offsets[slice_number];
+ slice_end = slice_offsets[slice_number + 1];
+
+ if ((ret = init_get_bits8(&gb, buf + slice_begin + 3, slice_end - slice_begin - 3)) < 0)
+ return ret;
+
+ for (y = slice_number * 16 * line_stride; y < frame->height; y += line_stride * 64) {
+ uint8_t *dest_y, *dest_cb, *dest_cr, *dest_a;
+ int last_dc[4] = { 1024, 1024, 1024, 1024 };
+ uint8_t last_alpha[16];
+
+ memset(last_alpha, 255, sizeof(last_alpha));
+
+ dest_y = frame->data[0] + frame->linesize[0] * (y + field_number);
+ if (s->subsampling == SHQ_SUBSAMPLING_420) {
+ dest_cb = frame->data[1] + frame->linesize[1] * (y/2 + field_number);
+ dest_cr = frame->data[2] + frame->linesize[2] * (y/2 + field_number);
+ } else {
+ dest_cb = frame->data[1] + frame->linesize[1] * (y + field_number);
+ dest_cr = frame->data[2] + frame->linesize[2] * (y + field_number);
+ }
+ if (s->alpha_type != SHQ_NO_ALPHA) {
+ dest_a = frame->data[3] + frame->linesize[3] * (y + field_number);
+ }
+
+ for (x = 0; x < frame->width; x += 16) {
+ /* Decode the four luma blocks. */
+ if ((ret = decode_dct_block(s, &gb, last_dc, 0, dest_y, linesize_y)) < 0)
+ return ret;
+ if ((ret = decode_dct_block(s, &gb, last_dc, 0, dest_y + 8, linesize_y)) < 0)
+ return ret;
+ if ((ret = decode_dct_block(s, &gb, last_dc, 0, dest_y + 8 * linesize_y, linesize_y)) < 0)
+ return ret;
+ if ((ret = decode_dct_block(s, &gb, last_dc, 0, dest_y + 8 * linesize_y + 8, linesize_y)) < 0)
+ return ret;
+
+ /*
+ * Decode the first chroma block. For 4:2:0, this is the only one;
+ * for 4:2:2, it's the top block; for 4:4:4, it's the top-left block.
+ */
+ if ((ret = decode_dct_block(s, &gb, last_dc, 1, dest_cb, linesize_cb)) < 0)
+ return ret;
+ if ((ret = decode_dct_block(s, &gb, last_dc, 2, dest_cr, linesize_cr)) < 0)
+ return ret;
+
+ if (s->subsampling != SHQ_SUBSAMPLING_420) {
+ /* For 4:2:2, this is the bottom block; for 4:4:4, it's the bottom-left block. */
+ if ((ret = decode_dct_block(s, &gb, last_dc, 1, dest_cb + 8 * linesize_cb, linesize_cb)) < 0)
+ return ret;
+ if ((ret = decode_dct_block(s, &gb, last_dc, 2, dest_cr + 8 * linesize_cr, linesize_cr)) < 0)
+ return ret;
+
+ if (s->subsampling == SHQ_SUBSAMPLING_444) {
+ /* Top-right and bottom-right blocks. */
+ if ((ret = decode_dct_block(s, &gb, last_dc, 1, dest_cb + 8, linesize_cb)) < 0)
+ return ret;
+ if ((ret = decode_dct_block(s, &gb, last_dc, 2, dest_cr + 8, linesize_cr)) < 0)
+ return ret;
+ if ((ret = decode_dct_block(s, &gb, last_dc, 1, dest_cb + 8 * linesize_cb + 8, linesize_cb)) < 0)
+ return ret;
+ if ((ret = decode_dct_block(s, &gb, last_dc, 2, dest_cr + 8 * linesize_cr + 8, linesize_cr)) < 0)
+ return ret;
+
+ dest_cb += 8;
+ dest_cr += 8;
+ }
+ }
+ dest_y += 16;
+ dest_cb += 8;
+ dest_cr += 8;
+
+ if (s->alpha_type == SHQ_RLE_ALPHA) {
+ /* Alpha coded using 16x8 RLE blocks. */
+ if ((ret = decode_alpha_block(s, &gb, last_alpha, dest_a, linesize_a)) < 0)
+ return ret;
+ if ((ret = decode_alpha_block(s, &gb, last_alpha, dest_a + 8 * linesize_a, linesize_a)) < 0)
+ return ret;
+ dest_a += 16;
+ } else if (s->alpha_type == SHQ_DCT_ALPHA) {
+ /* Alpha encoded exactly like luma. */
+ if ((ret = decode_dct_block(s, &gb, last_dc, 3, dest_a, linesize_a)) < 0)
+ return ret;
+ if ((ret = decode_dct_block(s, &gb, last_dc, 3, dest_a + 8, linesize_a)) < 0)
+ return ret;
+ if ((ret = decode_dct_block(s, &gb, last_dc, 3, dest_a + 8 * linesize_a, linesize_a)) < 0)
+ return ret;
+ if ((ret = decode_dct_block(s, &gb, last_dc, 3, dest_a + 8 * linesize_a + 8, linesize_a)) < 0)
+ return ret;
+ dest_a += 16;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void compute_quant_matrix(int *output, int qscale)
+{
+ int i;
+ for (i = 0; i < 64; i++) output[i] = unscaled_quant_matrix[i] * qscale;
+}
+
+static int speedhq_decode_frame(AVCodecContext *avctx,
+ void *data, int *got_frame,
+ AVPacket *avpkt)
+{
+ SHQContext * const s = avctx->priv_data;
+ const uint8_t *buf = avpkt->data;
+ int buf_size = avpkt->size;
+ AVFrame *frame = data;
+ uint8_t *rbuf;
+ uint8_t quality;
+ uint32_t second_field_offset;
+ int ret;
+
+ if (buf_size < 4)
+ return AVERROR_INVALIDDATA;
+
+ rbuf = av_malloc(buf_size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!rbuf) {
+ av_log(avctx, AV_LOG_ERROR, "Cannot allocate temporary buffer\n");
+ av_free(rbuf);
+ return AVERROR(ENOMEM);
+ }
+ memcpy(rbuf, buf, buf_size);
+ memset(rbuf + buf_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
+
+ quality = rbuf[0];
+ if (quality >= 100) {
+ av_free(rbuf);
+ return AVERROR_INVALIDDATA;
+ }
+
+ compute_quant_matrix(s->quant_matrix, 100 - quality);
+
+ second_field_offset = AV_RL24(rbuf + 1);
+ if (second_field_offset >= buf_size - 3) {
+ av_free(rbuf);
+ return AVERROR_INVALIDDATA;
+ }
+
+ avctx->coded_width = FFALIGN(avctx->width, 16);
+ avctx->coded_height = FFALIGN(avctx->height, 16);
+
+ if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) {
+ av_free(rbuf);
+ return ret;
+ }
+ frame->key_frame = 1;
+
+ if (second_field_offset == 4) {
+ /*
+ * Overlapping first and second fields is used to signal
+ * encoding only a single field (the second field then comes
+ * as a separate, later frame).
+ */
+ frame->height >>= 1;
+ if ((ret = decode_speedhq_field(s, rbuf, buf_size, frame, 0, 4, buf_size, 1)) < 0) {
+ av_free(rbuf);
+ return ret;
+ }
+ } else {
+ if ((ret = decode_speedhq_field(s, rbuf, buf_size, frame, 0, 4, second_field_offset, 2)) < 0) {
+ av_free(rbuf);
+ return ret;
+ }
+ if ((ret = decode_speedhq_field(s, rbuf, buf_size, frame, 1, second_field_offset, buf_size, 2)) < 0) {
+ av_free(rbuf);
+ return ret;
+ }
+ }
+
+ *got_frame = 1;
+ av_free(rbuf);
+ return buf_size;
+}
+
+/*
+ * Alpha VLC. Run and level are independently coded, and would be
+ * outside the default limits for MAX_RUN/MAX_LEVEL, so we don't
+ * bother with combining them into one table.
+ */
+static av_cold void compute_alpha_vlcs(void)
+{
+ uint16_t run_code[129], level_code[256];
+ uint8_t run_bits[129], level_bits[256];
+ int run, level;
+
+ for (run = 0; run < 128; run++) {
+ if (!run) {
+ /* 0 -> 0. */
+ run_code[run] = 0;
+ run_bits[run] = 1;
+ } else if (run <= 4) {
+ /* 10xx -> xx plus 1. */
+ run_code[run] = ((run - 1) << 2) | 1;
+ run_bits[run] = 4;
+ } else {
+ /* 111xxxxxxx -> xxxxxxxx. */
+ run_code[run] = (run << 3) | 7;
+ run_bits[run] = 10;
+ }
+ }
+
+ /* 110 -> EOB. */
+ run_code[128] = 3;
+ run_bits[128] = 3;
+
+ INIT_LE_VLC_STATIC(&ff_dc_alpha_run_vlc_le, ALPHA_VLC_BITS, 129,
+ run_bits, 1, 1,
+ run_code, 2, 2, 160);
+
+ for (level = 0; level < 256; level++) {
+ int8_t signed_level = (int8_t)level;
+ int abs_signed_level = abs(signed_level);
+ int sign = (signed_level < 0) ? 1 : 0;
+
+ if (abs_signed_level == 1) {
+ /* 1s -> -1 or +1 (depending on sign bit). */
+ level_code[level] = (sign << 1) | 1;
+ level_bits[level] = 2;
+ } else if (abs_signed_level >= 2 && abs_signed_level <= 5) {
+ /* 01sxx -> xx plus 2 (2..5 or -2..-5, depending on sign bit). */
+ level_code[level] = ((abs_signed_level - 2) << 3) | (sign << 2) | 2;
+ level_bits[level] = 5;
+ } else {
+ /*
+ * 00xxxxxxxx -> xxxxxxxx, in two's complement. 0 is technically an
+ * illegal code (that would be encoded by increasing run), but it
+ * doesn't hurt and simplifies indexing.
+ */
+ level_code[level] = level << 2;
+ level_bits[level] = 10;
+ }
+ }
+
+ INIT_LE_VLC_STATIC(&ff_dc_alpha_level_vlc_le, ALPHA_VLC_BITS, 256,
+ level_bits, 1, 1,
+ level_code, 2, 2, 288);
+}
+
+static uint32_t reverse(uint32_t num, int bits)
+{
+ return av_bitswap_32(num) >> (32 - bits);
+}
+
+static void reverse_code(const uint16_t *code, const uint8_t *bits,
+ uint16_t *reversed_code, int num_entries)
+{
+ int i;
+ for (i = 0; i < num_entries; i++) {
+ reversed_code[i] = reverse(code[i], bits[i]);
+ }
+}
+
+static av_cold int speedhq_decode_init(AVCodecContext *avctx)
+{
+ static int done = 0;
+ uint16_t ff_mpeg12_vlc_dc_lum_code_reversed[12];
+ uint16_t ff_mpeg12_vlc_dc_chroma_code_reversed[12];
+ SHQContext * const s = avctx->priv_data;
+
+ s->avctx = avctx;
+
+ if (!done) {
+ int i;
+
+ /* Exactly the same as MPEG-2, except little-endian. */
+ reverse_code(ff_mpeg12_vlc_dc_lum_code,
+ ff_mpeg12_vlc_dc_lum_bits,
+ ff_mpeg12_vlc_dc_lum_code_reversed,
+ 12);
+ INIT_LE_VLC_STATIC(&ff_dc_lum_vlc_le, DC_VLC_BITS, 12,
+ ff_mpeg12_vlc_dc_lum_bits, 1, 1,
+ ff_mpeg12_vlc_dc_lum_code_reversed, 2, 2, 512);
+ reverse_code(ff_mpeg12_vlc_dc_chroma_code,
+ ff_mpeg12_vlc_dc_chroma_bits,
+ ff_mpeg12_vlc_dc_chroma_code_reversed,
+ 12);
+ INIT_LE_VLC_STATIC(&ff_dc_chroma_vlc_le, DC_VLC_BITS, 12,
+ ff_mpeg12_vlc_dc_chroma_bits, 1, 1,
+ ff_mpeg12_vlc_dc_chroma_code_reversed, 2, 2, 514);
+
+ /* Reverse the AC VLC, because INIT_VLC_LE wants it in that order. */
+ for (i = 0; i < FF_ARRAY_ELEMS(speedhq_vlc); ++i) {
+ speedhq_vlc[i][0] = reverse(speedhq_vlc[i][0], speedhq_vlc[i][1]);
+ }
+ ff_rl_init(&ff_rl_speedhq, ff_speedhq_static_rl_table_store);
+ INIT_2D_VLC_RL(ff_rl_speedhq, 674, INIT_VLC_LE);
+
+ compute_alpha_vlcs();
+
+ done = 1;
+ }
+
+ ff_blockdsp_init(&s->bdsp, avctx);
+ ff_idctdsp_init(&s->idsp, avctx);
+ ff_init_scantable(s->idsp.idct_permutation, &s->intra_scantable, ff_zigzag_direct);
+
+ switch (avctx->codec_tag) {
+ case MKTAG('S', 'H', 'Q', '0'):
+ s->subsampling = SHQ_SUBSAMPLING_420;
+ s->alpha_type = SHQ_NO_ALPHA;
+ avctx->pix_fmt = AV_PIX_FMT_YUV420P;
+ break;
+ case MKTAG('S', 'H', 'Q', '1'):
+ s->subsampling = SHQ_SUBSAMPLING_420;
+ s->alpha_type = SHQ_RLE_ALPHA;
+ avctx->pix_fmt = AV_PIX_FMT_YUVA420P;
+ break;
+ case MKTAG('S', 'H', 'Q', '2'):
+ s->subsampling = SHQ_SUBSAMPLING_422;
+ s->alpha_type = SHQ_NO_ALPHA;
+ avctx->pix_fmt = AV_PIX_FMT_YUV422P;
+ break;
+ case MKTAG('S', 'H', 'Q', '3'):
+ s->subsampling = SHQ_SUBSAMPLING_422;
+ s->alpha_type = SHQ_RLE_ALPHA;
+ avctx->pix_fmt = AV_PIX_FMT_YUVA422P;
+ break;
+ case MKTAG('S', 'H', 'Q', '4'):
+ s->subsampling = SHQ_SUBSAMPLING_444;
+ s->alpha_type = SHQ_NO_ALPHA;
+ avctx->pix_fmt = AV_PIX_FMT_YUV444P;
+ break;
+ case MKTAG('S', 'H', 'Q', '5'):
+ s->subsampling = SHQ_SUBSAMPLING_444;
+ s->alpha_type = SHQ_RLE_ALPHA;
+ avctx->pix_fmt = AV_PIX_FMT_YUVA444P;
+ break;
+ case MKTAG('S', 'H', 'Q', '7'):
+ s->subsampling = SHQ_SUBSAMPLING_422;
+ s->alpha_type = SHQ_DCT_ALPHA;
+ avctx->pix_fmt = AV_PIX_FMT_YUVA422P;
+ break;
+ case MKTAG('S', 'H', 'Q', '9'):
+ s->subsampling = SHQ_SUBSAMPLING_444;
+ s->alpha_type = SHQ_DCT_ALPHA;
+ avctx->pix_fmt = AV_PIX_FMT_YUVA444P;
+ break;
+ default:
+ av_assert0(0);
+ }
+
+ /* This matches what NDI's RGB -> Y'CbCr 4:2:2 converter uses. */
+ avctx->colorspace = AVCOL_SPC_BT470BG;
+ avctx->chroma_sample_location = AVCHROMA_LOC_CENTER;
+
+ return 0;
+}
+
+static av_cold int speedhq_decode_end(AVCodecContext *avctx)
+{
+ return 0;
+}
+
+AVCodec ff_speedhq_decoder = {
+ .name = "speedhq",
+ .long_name = NULL_IF_CONFIG_SMALL("NewTek SpeedHQ"),
+ .type = AVMEDIA_TYPE_VIDEO,
+ .id = AV_CODEC_ID_SPEEDHQ,
+ .priv_data_size = sizeof(SHQContext),
+ .init = speedhq_decode_init,
+ .close = speedhq_decode_end,
+ .decode = speedhq_decode_frame,
+ .capabilities = AV_CODEC_CAP_DR1,
+};
diff --git a/libavcodec/vlc.h b/libavcodec/vlc.h
index 9e38f8c6b2..40096d8944 100644
--- a/libavcodec/vlc.h
+++ b/libavcodec/vlc.h
@@ -62,4 +62,13 @@ void ff_free_vlc(VLC *vlc);
init_vlc(vlc, bits, a, b, c, d, e, f, g, INIT_VLC_USE_NEW_STATIC); \
} while (0)
+#define INIT_LE_VLC_STATIC(vlc, bits, a, b, c, d, e, f, g, static_size) \
+ do { \
+ static VLC_TYPE table[static_size][2]; \
+ (vlc)->table = table; \
+ (vlc)->table_allocated = static_size; \
+ init_vlc(vlc, bits, a, b, c, d, e, f, g, \
+ INIT_VLC_USE_NEW_STATIC | INIT_VLC_LE); \
+ } while (0)
+
#endif /* AVCODEC_VLC_H */
diff --git a/libavformat/riff.c b/libavformat/riff.c
index cd2143c876..d44b908f8b 100644
--- a/libavformat/riff.c
+++ b/libavformat/riff.c
@@ -440,6 +440,14 @@ const AVCodecTag ff_codec_bmp_tags[] = {
{ AV_CODEC_ID_MAGICYUV, MKTAG('M', '0', 'G', '0') },
{ AV_CODEC_ID_MAGICYUV, MKTAG('M', '0', 'Y', '2') },
{ AV_CODEC_ID_YLC, MKTAG('Y', 'L', 'C', '0') },
+ { AV_CODEC_ID_SPEEDHQ, MKTAG('S', 'H', 'Q', '0') },
+ { AV_CODEC_ID_SPEEDHQ, MKTAG('S', 'H', 'Q', '1') },
+ { AV_CODEC_ID_SPEEDHQ, MKTAG('S', 'H', 'Q', '2') },
+ { AV_CODEC_ID_SPEEDHQ, MKTAG('S', 'H', 'Q', '3') },
+ { AV_CODEC_ID_SPEEDHQ, MKTAG('S', 'H', 'Q', '4') },
+ { AV_CODEC_ID_SPEEDHQ, MKTAG('S', 'H', 'Q', '5') },
+ { AV_CODEC_ID_SPEEDHQ, MKTAG('S', 'H', 'Q', '7') },
+ { AV_CODEC_ID_SPEEDHQ, MKTAG('S', 'H', 'Q', '9') },
{ AV_CODEC_ID_NONE, 0 }
};
--
2.11.0
More information about the ffmpeg-devel
mailing list