* [FFmpeg-devel] [PATCH] Add DXV encoder with support for DXT1 texture format
@ 2024-01-17 8:27 Connor Worley
2024-01-17 13:12 ` Michael Niedermayer
0 siblings, 1 reply; 11+ messages in thread
From: Connor Worley @ 2024-01-17 8:27 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: Connor Worley <connorbworley@gmail.com>
---
Changelog | 1 +
configure | 1 +
doc/general_contents.texi | 3 +-
libavcodec/Makefile | 1 +
libavcodec/allcodecs.c | 1 +
libavcodec/dxvenc.c | 362 ++++++++++++++++++++++++++++++++++++++
libavcodec/version.h | 2 +-
7 files changed, 369 insertions(+), 2 deletions(-)
create mode 100644 libavcodec/dxvenc.c
diff --git a/Changelog b/Changelog
index 5b2899d05b..224d84664a 100644
--- a/Changelog
+++ b/Changelog
@@ -2,6 +2,7 @@ Entries are sorted chronologically from oldest to
youngest within each release,
releases are sorted from youngest to oldest.
version <next>:
+- DXV DXT1 encoder
- LEAD MCMP decoder
- EVC decoding using external library libxevd
- EVC encoding using external library libxeve
diff --git a/configure b/configure
index c8ae0a061d..21663000f8 100755
--- a/configure
+++ b/configure
@@ -2851,6 +2851,7 @@ dvvideo_decoder_select="dvprofile idctdsp"
dvvideo_encoder_select="dvprofile fdctdsp me_cmp pixblockdsp"
dxa_decoder_deps="zlib"
dxv_decoder_select="lzf texturedsp"
+dxv_encoder_select="texturedspenc"
eac3_decoder_select="ac3_decoder"
eac3_encoder_select="ac3_encoder"
eamad_decoder_select="aandcttables blockdsp bswapdsp"
diff --git a/doc/general_contents.texi b/doc/general_contents.texi
index 8b48fed060..f269cbd1a9 100644
--- a/doc/general_contents.texi
+++ b/doc/general_contents.texi
@@ -670,7 +670,8 @@ library:
@item Redirector @tab @tab X
@item RedSpark @tab @tab X
@item Renderware TeXture Dictionary @tab @tab X
-@item Resolume DXV @tab @tab X
+@item Resolume DXV @tab X @tab X
+ @tab Encoding is only supported for the DXT1 (Normal Quality, No
Alpha) texture format.
@item RF64 @tab @tab X
@item RL2 @tab @tab X
@tab Audio and video format used in some games by Entertainment
Software Partners.
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index bb42095165..96361ac794 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -341,6 +341,7 @@ OBJS-$(CONFIG_DVVIDEO_ENCODER) += dvenc.o
dv.o dvdata.o
OBJS-$(CONFIG_DXA_DECODER) += dxa.o
OBJS-$(CONFIG_DXTORY_DECODER) += dxtory.o
OBJS-$(CONFIG_DXV_DECODER) += dxv.o
+OBJS-$(CONFIG_DXV_ENCODER) += dxvenc.o
OBJS-$(CONFIG_EAC3_DECODER) += eac3_data.o
OBJS-$(CONFIG_EAC3_ENCODER) += eac3enc.o eac3_data.o
OBJS-$(CONFIG_EACMV_DECODER) += eacmv.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 93ce8e3224..ef8c3a6d7d 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -106,6 +106,7 @@ extern const FFCodec ff_dvvideo_encoder;
extern const FFCodec ff_dvvideo_decoder;
extern const FFCodec ff_dxa_decoder;
extern const FFCodec ff_dxtory_decoder;
+extern const FFCodec ff_dxv_encoder;
extern const FFCodec ff_dxv_decoder;
extern const FFCodec ff_eacmv_decoder;
extern const FFCodec ff_eamad_decoder;
diff --git a/libavcodec/dxvenc.c b/libavcodec/dxvenc.c
new file mode 100644
index 0000000000..36ee06699d
--- /dev/null
+++ b/libavcodec/dxvenc.c
@@ -0,0 +1,362 @@
+/*
+ * Resolume DXV encoder
+ * Copyright (C) 2015 Vittorio Giovara <vittorio.giovara@gmail.com>
+ * Copyright (C) 2015 Tom Butterworth <bangnoise@gmail.com>
+ * Copyright (C) 2018 Paul B Mahol
+ * Copyright (C) 2024 Connor Worley <connorbworley@gmail.com>
+ *
+ * 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 <stdint.h>
+
+#include "libavutil/crc.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+
+#include "mathops.h"
+#include "avcodec.h"
+#include "bytestream.h"
+#include "codec_internal.h"
+#include "encode.h"
+#include "lzf.h"
+#include "texturedsp.h"
+#include "thread.h"
+
+#define LOOKBACK_HT_ELEMS 0x40000
+#define LOOKBACK_WORDS 0x20202
+
+enum DXVTextureFormat {
+ DXV_FMT_DXT1 = MKBETAG('D', 'X', 'T', '1'),
+};
+
+typedef struct HTEntry {
+ uint32_t key;
+ uint32_t pos;
+} HTEntry;
+
+static void ht_init(HTEntry *ht)
+{
+ for (size_t i = 0; i < LOOKBACK_HT_ELEMS; i++) {
+ ht[i].pos = -1;
+ }
+}
+
+static uint32_t ht_lookup_and_upsert(HTEntry *ht, AVCRC *hash_ctx,
+ uint32_t key, uint32_t pos)
+{
+ uint32_t ret = -1;
+ size_t hash = av_crc(hash_ctx, 0, (uint8_t*)&key, 4) %
LOOKBACK_HT_ELEMS;
+ for (size_t i = hash; i < hash + LOOKBACK_HT_ELEMS; i++) {
+ size_t wrapped_index = i % LOOKBACK_HT_ELEMS;
+ HTEntry *entry = &ht[wrapped_index];
+ if (entry->key == key || entry->pos == -1) {
+ ret = entry->pos;
+ entry->key = key;
+ entry->pos = pos;
+ break;
+ }
+ }
+ return ret;
+}
+
+static void ht_delete(HTEntry *ht, AVCRC *hash_ctx,
+ uint32_t key, uint32_t pos)
+{
+ HTEntry *removed_entry = NULL;
+ size_t removed_hash;
+ size_t hash = av_crc(hash_ctx, 0, (uint8_t*)&key, 4) %
LOOKBACK_HT_ELEMS;
+
+ for (size_t i = hash; i < hash + LOOKBACK_HT_ELEMS; i++) {
+ size_t wrapped_index = i % LOOKBACK_HT_ELEMS;
+ HTEntry *entry = &ht[wrapped_index];
+ if (entry->pos == -1)
+ return;
+ if (removed_entry) {
+ size_t candidate_hash = av_crc(hash_ctx, 0,
(uint8_t*)&entry->key, 4) % LOOKBACK_HT_ELEMS;
+ if ((wrapped_index > removed_hash && (candidate_hash <=
removed_hash || candidate_hash > wrapped_index)) ||
+ (wrapped_index < removed_hash && (candidate_hash <=
removed_hash && candidate_hash > wrapped_index))) {
+ *removed_entry = *entry;
+ entry->pos = -1;
+ removed_entry = entry;
+ removed_hash = wrapped_index;
+ }
+ } else if (entry->key == key) {
+ if (entry->pos <= pos) {
+ entry->pos = -1;
+ removed_entry = entry;
+ removed_hash = wrapped_index;
+ } else {
+ return;
+ }
+ }
+ }
+}
+
+typedef struct DXVEncContext {
+ AVClass *class;
+
+ TextureDSPContext texdsp;
+ PutByteContext pbc;
+
+ uint8_t *tex_data; // Compressed texture
+ int64_t tex_size; // Texture size
+
+ /* Optimal number of slices for parallel decoding */
+ int slice_count;
+
+ TextureDSPThreadContext enc;
+
+ enum DXVTextureFormat tex_fmt;
+ int (*compress_tex)(AVCodecContext *avctx);
+
+ AVCRC *crc_ctx;
+
+ HTEntry color_lookback_ht[LOOKBACK_HT_ELEMS];
+ HTEntry lut_lookback_ht[LOOKBACK_HT_ELEMS];
+} DXVEncContext;
+
+static int compress_texture_thread(AVCodecContext *avctx, void *arg,
+ int slice, int thread_nb)
+{
+ DXVEncContext *ctx = avctx->priv_data;
+ AVFrame *frame = arg;
+
+ if (ctx->enc.tex_funct) {
+ ctx->enc.tex_data.out = ctx->tex_data;
+ ctx->enc.frame_data.in = frame->data[0];
+ ctx->enc.stride = frame->linesize[0];
+ return ff_texturedsp_compress_thread(avctx, &ctx->enc, slice,
thread_nb);
+ } else {
+ /* unimplemented: YCoCg formats */
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Converts an index offset value to a 2-bit opcode and pushes it to a
stream.
+ * Inverse of CHECKPOINT in dxv.c. */
+#define PUSH_OP(x)
\
+ do {
\
+ if (state == 16) {
\
+ if (bytestream2_get_bytes_left_p(pbc) < 4) {
\
+ return AVERROR_INVALIDDATA;
\
+ }
\
+ value = (uint32_t*)pbc->buffer;
\
+ bytestream2_put_le32(pbc, 0);
\
+ state = 0;
\
+ }
\
+ if (idx >= 0x102 * x) {
\
+ op = 3;
\
+ bytestream2_put_le16(pbc, (idx / x) - 0x102);
\
+ } else if (idx >= 2 * x) {
\
+ op = 2;
\
+ bytestream2_put_byte(pbc, (idx / x) - 2);
\
+ } else if (idx == x) {
\
+ op = 1;
\
+ } else {
\
+ op = 0;
\
+ }
\
+ *value |= (op << (state * 2));
\
+ state++;
\
+ } while (0)
+
+static int dxv_compress_dxt1(AVCodecContext *avctx)
+{
+ DXVEncContext *ctx = avctx->priv_data;
+ PutByteContext *pbc = &ctx->pbc;
+ uint32_t *value;
+ uint32_t color, lut, idx, color_idx, lut_idx, prev_pos, state = 16,
pos = 2, op = 0;
+
+ ht_init(ctx->color_lookback_ht);
+ ht_init(ctx->lut_lookback_ht);
+
+ bytestream2_put_le32(pbc, AV_RL32(ctx->tex_data));
+ bytestream2_put_le32(pbc, AV_RL32(ctx->tex_data + 4));
+
+ ht_lookup_and_upsert(ctx->color_lookback_ht, ctx->crc_ctx,
AV_RL32(ctx->tex_data), 0);
+ ht_lookup_and_upsert(ctx->lut_lookback_ht, ctx->crc_ctx,
AV_RL32(ctx->tex_data + 4), 1);
+
+ while (pos + 2 <= ctx->tex_size / 4) {
+ idx = 0;
+
+ color = AV_RL32(ctx->tex_data + pos * 4);
+ prev_pos = ht_lookup_and_upsert(ctx->color_lookback_ht,
ctx->crc_ctx, color, pos);
+ color_idx = prev_pos != -1 ? pos - prev_pos : 0;
+ if (pos >= LOOKBACK_WORDS) {
+ uint32_t old_pos = pos - LOOKBACK_WORDS;
+ uint32_t old_color = AV_RL32(ctx->tex_data + old_pos * 4);
+ ht_delete(ctx->color_lookback_ht, ctx->crc_ctx, old_color,
old_pos);
+ }
+ pos++;
+
+ lut = AV_RL32(ctx->tex_data + pos * 4);
+ if (color_idx && lut == AV_RL32(ctx->tex_data + (pos -
color_idx) * 4)) {
+ idx = color_idx;
+ } else {
+ idx = 0;
+ prev_pos = ht_lookup_and_upsert(ctx->lut_lookback_ht,
ctx->crc_ctx, lut, pos);
+ lut_idx = prev_pos != -1 ? pos - prev_pos : 0;
+ }
+ if (pos >= LOOKBACK_WORDS) {
+ uint32_t old_pos = pos - LOOKBACK_WORDS;
+ uint32_t old_lut = AV_RL32(ctx->tex_data + old_pos * 4);
+ ht_delete(ctx->lut_lookback_ht, ctx->crc_ctx, old_lut,
old_pos);
+ }
+ pos++;
+
+ PUSH_OP(2);
+
+ if (!idx) {
+ idx = color_idx;
+ PUSH_OP(2);
+ if (!idx)
+ bytestream2_put_le32(pbc, color);
+
+ idx = lut_idx;
+ PUSH_OP(2);
+ if (!idx)
+ bytestream2_put_le32(pbc, lut);
+ }
+ }
+
+ return 0;
+}
+
+static int dxv_encode(AVCodecContext *avctx, AVPacket *pkt,
+ const AVFrame *frame, int *got_packet)
+{
+ DXVEncContext *ctx = avctx->priv_data;
+ PutByteContext *pbc = &ctx->pbc;
+ int ret;
+
+ /* unimplemented: needs to depend on compression ratio of tex format */
+ ret = ff_alloc_packet(avctx, pkt, 12 + 8 + (ctx->tex_size - 8) +
(ctx->tex_size - 8)*12/128 + 1);
+ if (ret < 0)
+ return ret;
+
+ avctx->execute2(avctx, compress_texture_thread, (void*)frame, NULL,
ctx->enc.slice_count);
+
+ bytestream2_init_writer(pbc, pkt->data, pkt->size);
+
+ bytestream2_put_le32(pbc, ctx->tex_fmt);
+ bytestream2_put_byte(pbc, 4);
+ bytestream2_put_byte(pbc, 0);
+ bytestream2_put_byte(pbc, 0);
+ bytestream2_put_byte(pbc, 0);
+ /* Fill in compressed size later */
+ bytestream2_skip_p(pbc, 4);
+
+ ret = ctx->compress_tex(avctx);
+ if (ret < 0)
+ return ret;
+
+ AV_WL32(pkt->data + 8, bytestream2_tell_p(pbc) - 12);
+ av_shrink_packet(pkt, bytestream2_tell_p(pbc));
+
+ *got_packet = 1;
+ return 0;
+}
+
+static av_cold int dxv_init(AVCodecContext *avctx)
+{
+ DXVEncContext *ctx = avctx->priv_data;
+ int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
+
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid image size %dx%d.\n",
+ avctx->width, avctx->height);
+ return ret;
+ }
+
+ if (avctx->width % 4 || avctx->height % 4) {
+ av_log(avctx, AV_LOG_ERROR, "Video size %dx%d is not multiple
of 4.\n",
+ avctx->width, avctx->height);
+ return AVERROR_INVALIDDATA;
+ }
+
+ ff_texturedspenc_init(&ctx->texdsp);
+
+ switch (ctx->tex_fmt) {
+ case DXV_FMT_DXT1:
+ ctx->compress_tex = dxv_compress_dxt1;
+ ctx->enc.tex_funct = ctx->texdsp.dxt1_block;
+ ctx->enc.tex_ratio = 8;
+ ctx->tex_size = avctx->width * avctx->height / 2;
+ break;
+ default:
+ av_log(avctx, AV_LOG_ERROR, "Invalid format %08X\n", ctx->tex_fmt);
+ return AVERROR_INVALIDDATA;
+ }
+ ctx->enc.raw_ratio = 16;
+ ctx->enc.slice_count = av_clip(avctx->thread_count, 1,
avctx->height / TEXTURE_BLOCK_H);
+
+ ctx->tex_data = av_malloc(ctx->tex_size);
+ if (!ctx->tex_data) {
+ return AVERROR(ENOMEM);
+ }
+
+ ctx->crc_ctx = (AVCRC*)av_crc_get_table(AV_CRC_32_IEEE);
+ if (!ctx->crc_ctx) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static av_cold int dxv_close(AVCodecContext *avctx)
+{
+ DXVEncContext *ctx = avctx->priv_data;
+
+ av_freep(&ctx->tex_data);
+
+ return 0;
+}
+
+#define OFFSET(x) offsetof(DXVEncContext, x)
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+ { "format", NULL, OFFSET(tex_fmt), AV_OPT_TYPE_INT, { .i64 =
DXV_FMT_DXT1 }, DXV_FMT_DXT1, DXV_FMT_DXT1, FLAGS, "format" },
+ { "dxt1", "DXT1 (Normal Quality, No Alpha)", 0,
AV_OPT_TYPE_CONST, { .i64 = DXV_FMT_DXT1 }, 0, 0, FLAGS, "format" },
+ { NULL },
+};
+
+static const AVClass dxvenc_class = {
+ .class_name = "DXV encoder",
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+const FFCodec ff_dxv_encoder = {
+ .p.name = "dxv",
+ CODEC_LONG_NAME("Resolume DXV"),
+ .p.type = AVMEDIA_TYPE_VIDEO,
+ .p.id = AV_CODEC_ID_DXV,
+ .init = dxv_init,
+ FF_CODEC_ENCODE_CB(dxv_encode),
+ .close = dxv_close,
+ .priv_data_size = sizeof(DXVEncContext),
+ .p.capabilities = AV_CODEC_CAP_DR1 |
+ AV_CODEC_CAP_SLICE_THREADS |
+ AV_CODEC_CAP_FRAME_THREADS,
+ .p.priv_class = &dxvenc_class,
+ .p.pix_fmts = (const enum AVPixelFormat[]) {
+ AV_PIX_FMT_RGBA, AV_PIX_FMT_NONE,
+ },
+ .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
+};
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 376388c5bb..0fae3d06d3 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -29,7 +29,7 @@
#include "version_major.h"
-#define LIBAVCODEC_VERSION_MINOR 37
+#define LIBAVCODEC_VERSION_MINOR 38
#define LIBAVCODEC_VERSION_MICRO 100
#define LIBAVCODEC_VERSION_INT
AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
--
2.39.2 (Apple Git-143)
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [FFmpeg-devel] [PATCH] Add DXV encoder with support for DXT1 texture format
2024-01-17 8:27 [FFmpeg-devel] [PATCH] Add DXV encoder with support for DXT1 texture format Connor Worley
@ 2024-01-17 13:12 ` Michael Niedermayer
2024-01-17 16:55 ` Connor Worley
0 siblings, 1 reply; 11+ messages in thread
From: Michael Niedermayer @ 2024-01-17 13:12 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1.1: Type: text/plain, Size: 1452 bytes --]
On Wed, Jan 17, 2024 at 12:27:40AM -0800, Connor Worley wrote:
> Signed-off-by: Connor Worley <connorbworley@gmail.com>
> ---
> Changelog | 1 +
> configure | 1 +
> doc/general_contents.texi | 3 +-
> libavcodec/Makefile | 1 +
> libavcodec/allcodecs.c | 1 +
> libavcodec/dxvenc.c | 362 ++++++++++++++++++++++++++++++++++++++
> libavcodec/version.h | 2 +-
> 7 files changed, 369 insertions(+), 2 deletions(-)
> create mode 100644 libavcodec/dxvenc.c
>
> diff --git a/Changelog b/Changelog
> index 5b2899d05b..224d84664a 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -2,6 +2,7 @@ Entries are sorted chronologically from oldest to youngest
> within each release,
> releases are sorted from youngest to oldest.
> version <next>:
> +- DXV DXT1 encoder
> - LEAD MCMP decoder
> - EVC decoding using external library libxevd
> - EVC encoding using external library libxeve
please resend without extra linebreaks
Applying: Add DXV encoder with support for DXT1 texture format
error: corrupt patch at line 23
thx
[...]
--
Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
"You are 36 times more likely to die in a bathtub than at the hands of a
terrorist. Also, you are 2.5 times more likely to become a president and
2 times more likely to become an astronaut, than to die in a terrorist
attack." -- Thoughty2
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 11+ messages in thread
* [FFmpeg-devel] [PATCH] Add DXV encoder with support for DXT1 texture format
2024-01-17 13:12 ` Michael Niedermayer
@ 2024-01-17 16:55 ` Connor Worley
2024-01-17 20:45 ` Connor Worley
0 siblings, 1 reply; 11+ messages in thread
From: Connor Worley @ 2024-01-17 16:55 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: Connor Worley <connorbworley@gmail.com>
---
Changelog | 1 +
configure | 1 +
doc/general_contents.texi | 3 +-
libavcodec/Makefile | 1 +
libavcodec/allcodecs.c | 1 +
libavcodec/dxvenc.c | 362 ++++++++++++++++++++++++++++++++++++++
libavcodec/version.h | 2 +-
7 files changed, 369 insertions(+), 2 deletions(-)
create mode 100644 libavcodec/dxvenc.c
diff --git a/Changelog b/Changelog
index 5b2899d05b..224d84664a 100644
--- a/Changelog
+++ b/Changelog
@@ -2,6 +2,7 @@ Entries are sorted chronologically from oldest to youngest within each release,
releases are sorted from youngest to oldest.
version <next>:
+- DXV DXT1 encoder
- LEAD MCMP decoder
- EVC decoding using external library libxevd
- EVC encoding using external library libxeve
diff --git a/configure b/configure
index c8ae0a061d..21663000f8 100755
--- a/configure
+++ b/configure
@@ -2851,6 +2851,7 @@ dvvideo_decoder_select="dvprofile idctdsp"
dvvideo_encoder_select="dvprofile fdctdsp me_cmp pixblockdsp"
dxa_decoder_deps="zlib"
dxv_decoder_select="lzf texturedsp"
+dxv_encoder_select="texturedspenc"
eac3_decoder_select="ac3_decoder"
eac3_encoder_select="ac3_encoder"
eamad_decoder_select="aandcttables blockdsp bswapdsp"
diff --git a/doc/general_contents.texi b/doc/general_contents.texi
index 8b48fed060..f269cbd1a9 100644
--- a/doc/general_contents.texi
+++ b/doc/general_contents.texi
@@ -670,7 +670,8 @@ library:
@item Redirector @tab @tab X
@item RedSpark @tab @tab X
@item Renderware TeXture Dictionary @tab @tab X
-@item Resolume DXV @tab @tab X
+@item Resolume DXV @tab X @tab X
+ @tab Encoding is only supported for the DXT1 (Normal Quality, No Alpha) texture format.
@item RF64 @tab @tab X
@item RL2 @tab @tab X
@tab Audio and video format used in some games by Entertainment Software Partners.
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index bb42095165..96361ac794 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -341,6 +341,7 @@ OBJS-$(CONFIG_DVVIDEO_ENCODER) += dvenc.o dv.o dvdata.o
OBJS-$(CONFIG_DXA_DECODER) += dxa.o
OBJS-$(CONFIG_DXTORY_DECODER) += dxtory.o
OBJS-$(CONFIG_DXV_DECODER) += dxv.o
+OBJS-$(CONFIG_DXV_ENCODER) += dxvenc.o
OBJS-$(CONFIG_EAC3_DECODER) += eac3_data.o
OBJS-$(CONFIG_EAC3_ENCODER) += eac3enc.o eac3_data.o
OBJS-$(CONFIG_EACMV_DECODER) += eacmv.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 93ce8e3224..ef8c3a6d7d 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -106,6 +106,7 @@ extern const FFCodec ff_dvvideo_encoder;
extern const FFCodec ff_dvvideo_decoder;
extern const FFCodec ff_dxa_decoder;
extern const FFCodec ff_dxtory_decoder;
+extern const FFCodec ff_dxv_encoder;
extern const FFCodec ff_dxv_decoder;
extern const FFCodec ff_eacmv_decoder;
extern const FFCodec ff_eamad_decoder;
diff --git a/libavcodec/dxvenc.c b/libavcodec/dxvenc.c
new file mode 100644
index 0000000000..36ee06699d
--- /dev/null
+++ b/libavcodec/dxvenc.c
@@ -0,0 +1,362 @@
+/*
+ * Resolume DXV encoder
+ * Copyright (C) 2015 Vittorio Giovara <vittorio.giovara@gmail.com>
+ * Copyright (C) 2015 Tom Butterworth <bangnoise@gmail.com>
+ * Copyright (C) 2018 Paul B Mahol
+ * Copyright (C) 2024 Connor Worley <connorbworley@gmail.com>
+ *
+ * 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 <stdint.h>
+
+#include "libavutil/crc.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+
+#include "mathops.h"
+#include "avcodec.h"
+#include "bytestream.h"
+#include "codec_internal.h"
+#include "encode.h"
+#include "lzf.h"
+#include "texturedsp.h"
+#include "thread.h"
+
+#define LOOKBACK_HT_ELEMS 0x40000
+#define LOOKBACK_WORDS 0x20202
+
+enum DXVTextureFormat {
+ DXV_FMT_DXT1 = MKBETAG('D', 'X', 'T', '1'),
+};
+
+typedef struct HTEntry {
+ uint32_t key;
+ uint32_t pos;
+} HTEntry;
+
+static void ht_init(HTEntry *ht)
+{
+ for (size_t i = 0; i < LOOKBACK_HT_ELEMS; i++) {
+ ht[i].pos = -1;
+ }
+}
+
+static uint32_t ht_lookup_and_upsert(HTEntry *ht, AVCRC *hash_ctx,
+ uint32_t key, uint32_t pos)
+{
+ uint32_t ret = -1;
+ size_t hash = av_crc(hash_ctx, 0, (uint8_t*)&key, 4) % LOOKBACK_HT_ELEMS;
+ for (size_t i = hash; i < hash + LOOKBACK_HT_ELEMS; i++) {
+ size_t wrapped_index = i % LOOKBACK_HT_ELEMS;
+ HTEntry *entry = &ht[wrapped_index];
+ if (entry->key == key || entry->pos == -1) {
+ ret = entry->pos;
+ entry->key = key;
+ entry->pos = pos;
+ break;
+ }
+ }
+ return ret;
+}
+
+static void ht_delete(HTEntry *ht, AVCRC *hash_ctx,
+ uint32_t key, uint32_t pos)
+{
+ HTEntry *removed_entry = NULL;
+ size_t removed_hash;
+ size_t hash = av_crc(hash_ctx, 0, (uint8_t*)&key, 4) % LOOKBACK_HT_ELEMS;
+
+ for (size_t i = hash; i < hash + LOOKBACK_HT_ELEMS; i++) {
+ size_t wrapped_index = i % LOOKBACK_HT_ELEMS;
+ HTEntry *entry = &ht[wrapped_index];
+ if (entry->pos == -1)
+ return;
+ if (removed_entry) {
+ size_t candidate_hash = av_crc(hash_ctx, 0, (uint8_t*)&entry->key, 4) % LOOKBACK_HT_ELEMS;
+ if ((wrapped_index > removed_hash && (candidate_hash <= removed_hash || candidate_hash > wrapped_index)) ||
+ (wrapped_index < removed_hash && (candidate_hash <= removed_hash && candidate_hash > wrapped_index))) {
+ *removed_entry = *entry;
+ entry->pos = -1;
+ removed_entry = entry;
+ removed_hash = wrapped_index;
+ }
+ } else if (entry->key == key) {
+ if (entry->pos <= pos) {
+ entry->pos = -1;
+ removed_entry = entry;
+ removed_hash = wrapped_index;
+ } else {
+ return;
+ }
+ }
+ }
+}
+
+typedef struct DXVEncContext {
+ AVClass *class;
+
+ TextureDSPContext texdsp;
+ PutByteContext pbc;
+
+ uint8_t *tex_data; // Compressed texture
+ int64_t tex_size; // Texture size
+
+ /* Optimal number of slices for parallel decoding */
+ int slice_count;
+
+ TextureDSPThreadContext enc;
+
+ enum DXVTextureFormat tex_fmt;
+ int (*compress_tex)(AVCodecContext *avctx);
+
+ AVCRC *crc_ctx;
+
+ HTEntry color_lookback_ht[LOOKBACK_HT_ELEMS];
+ HTEntry lut_lookback_ht[LOOKBACK_HT_ELEMS];
+} DXVEncContext;
+
+static int compress_texture_thread(AVCodecContext *avctx, void *arg,
+ int slice, int thread_nb)
+{
+ DXVEncContext *ctx = avctx->priv_data;
+ AVFrame *frame = arg;
+
+ if (ctx->enc.tex_funct) {
+ ctx->enc.tex_data.out = ctx->tex_data;
+ ctx->enc.frame_data.in = frame->data[0];
+ ctx->enc.stride = frame->linesize[0];
+ return ff_texturedsp_compress_thread(avctx, &ctx->enc, slice, thread_nb);
+ } else {
+ /* unimplemented: YCoCg formats */
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Converts an index offset value to a 2-bit opcode and pushes it to a stream.
+ * Inverse of CHECKPOINT in dxv.c. */
+#define PUSH_OP(x) \
+ do { \
+ if (state == 16) { \
+ if (bytestream2_get_bytes_left_p(pbc) < 4) { \
+ return AVERROR_INVALIDDATA; \
+ } \
+ value = (uint32_t*)pbc->buffer; \
+ bytestream2_put_le32(pbc, 0); \
+ state = 0; \
+ } \
+ if (idx >= 0x102 * x) { \
+ op = 3; \
+ bytestream2_put_le16(pbc, (idx / x) - 0x102); \
+ } else if (idx >= 2 * x) { \
+ op = 2; \
+ bytestream2_put_byte(pbc, (idx / x) - 2); \
+ } else if (idx == x) { \
+ op = 1; \
+ } else { \
+ op = 0; \
+ } \
+ *value |= (op << (state * 2)); \
+ state++; \
+ } while (0)
+
+static int dxv_compress_dxt1(AVCodecContext *avctx)
+{
+ DXVEncContext *ctx = avctx->priv_data;
+ PutByteContext *pbc = &ctx->pbc;
+ uint32_t *value;
+ uint32_t color, lut, idx, color_idx, lut_idx, prev_pos, state = 16, pos = 2, op = 0;
+
+ ht_init(ctx->color_lookback_ht);
+ ht_init(ctx->lut_lookback_ht);
+
+ bytestream2_put_le32(pbc, AV_RL32(ctx->tex_data));
+ bytestream2_put_le32(pbc, AV_RL32(ctx->tex_data + 4));
+
+ ht_lookup_and_upsert(ctx->color_lookback_ht, ctx->crc_ctx, AV_RL32(ctx->tex_data), 0);
+ ht_lookup_and_upsert(ctx->lut_lookback_ht, ctx->crc_ctx, AV_RL32(ctx->tex_data + 4), 1);
+
+ while (pos + 2 <= ctx->tex_size / 4) {
+ idx = 0;
+
+ color = AV_RL32(ctx->tex_data + pos * 4);
+ prev_pos = ht_lookup_and_upsert(ctx->color_lookback_ht, ctx->crc_ctx, color, pos);
+ color_idx = prev_pos != -1 ? pos - prev_pos : 0;
+ if (pos >= LOOKBACK_WORDS) {
+ uint32_t old_pos = pos - LOOKBACK_WORDS;
+ uint32_t old_color = AV_RL32(ctx->tex_data + old_pos * 4);
+ ht_delete(ctx->color_lookback_ht, ctx->crc_ctx, old_color, old_pos);
+ }
+ pos++;
+
+ lut = AV_RL32(ctx->tex_data + pos * 4);
+ if (color_idx && lut == AV_RL32(ctx->tex_data + (pos - color_idx) * 4)) {
+ idx = color_idx;
+ } else {
+ idx = 0;
+ prev_pos = ht_lookup_and_upsert(ctx->lut_lookback_ht, ctx->crc_ctx, lut, pos);
+ lut_idx = prev_pos != -1 ? pos - prev_pos : 0;
+ }
+ if (pos >= LOOKBACK_WORDS) {
+ uint32_t old_pos = pos - LOOKBACK_WORDS;
+ uint32_t old_lut = AV_RL32(ctx->tex_data + old_pos * 4);
+ ht_delete(ctx->lut_lookback_ht, ctx->crc_ctx, old_lut, old_pos);
+ }
+ pos++;
+
+ PUSH_OP(2);
+
+ if (!idx) {
+ idx = color_idx;
+ PUSH_OP(2);
+ if (!idx)
+ bytestream2_put_le32(pbc, color);
+
+ idx = lut_idx;
+ PUSH_OP(2);
+ if (!idx)
+ bytestream2_put_le32(pbc, lut);
+ }
+ }
+
+ return 0;
+}
+
+static int dxv_encode(AVCodecContext *avctx, AVPacket *pkt,
+ const AVFrame *frame, int *got_packet)
+{
+ DXVEncContext *ctx = avctx->priv_data;
+ PutByteContext *pbc = &ctx->pbc;
+ int ret;
+
+ /* unimplemented: needs to depend on compression ratio of tex format */
+ ret = ff_alloc_packet(avctx, pkt, 12 + 8 + (ctx->tex_size - 8) + (ctx->tex_size - 8)*12/128 + 1);
+ if (ret < 0)
+ return ret;
+
+ avctx->execute2(avctx, compress_texture_thread, (void*)frame, NULL, ctx->enc.slice_count);
+
+ bytestream2_init_writer(pbc, pkt->data, pkt->size);
+
+ bytestream2_put_le32(pbc, ctx->tex_fmt);
+ bytestream2_put_byte(pbc, 4);
+ bytestream2_put_byte(pbc, 0);
+ bytestream2_put_byte(pbc, 0);
+ bytestream2_put_byte(pbc, 0);
+ /* Fill in compressed size later */
+ bytestream2_skip_p(pbc, 4);
+
+ ret = ctx->compress_tex(avctx);
+ if (ret < 0)
+ return ret;
+
+ AV_WL32(pkt->data + 8, bytestream2_tell_p(pbc) - 12);
+ av_shrink_packet(pkt, bytestream2_tell_p(pbc));
+
+ *got_packet = 1;
+ return 0;
+}
+
+static av_cold int dxv_init(AVCodecContext *avctx)
+{
+ DXVEncContext *ctx = avctx->priv_data;
+ int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
+
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid image size %dx%d.\n",
+ avctx->width, avctx->height);
+ return ret;
+ }
+
+ if (avctx->width % 4 || avctx->height % 4) {
+ av_log(avctx, AV_LOG_ERROR, "Video size %dx%d is not multiple of 4.\n",
+ avctx->width, avctx->height);
+ return AVERROR_INVALIDDATA;
+ }
+
+ ff_texturedspenc_init(&ctx->texdsp);
+
+ switch (ctx->tex_fmt) {
+ case DXV_FMT_DXT1:
+ ctx->compress_tex = dxv_compress_dxt1;
+ ctx->enc.tex_funct = ctx->texdsp.dxt1_block;
+ ctx->enc.tex_ratio = 8;
+ ctx->tex_size = avctx->width * avctx->height / 2;
+ break;
+ default:
+ av_log(avctx, AV_LOG_ERROR, "Invalid format %08X\n", ctx->tex_fmt);
+ return AVERROR_INVALIDDATA;
+ }
+ ctx->enc.raw_ratio = 16;
+ ctx->enc.slice_count = av_clip(avctx->thread_count, 1, avctx->height / TEXTURE_BLOCK_H);
+
+ ctx->tex_data = av_malloc(ctx->tex_size);
+ if (!ctx->tex_data) {
+ return AVERROR(ENOMEM);
+ }
+
+ ctx->crc_ctx = (AVCRC*)av_crc_get_table(AV_CRC_32_IEEE);
+ if (!ctx->crc_ctx) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static av_cold int dxv_close(AVCodecContext *avctx)
+{
+ DXVEncContext *ctx = avctx->priv_data;
+
+ av_freep(&ctx->tex_data);
+
+ return 0;
+}
+
+#define OFFSET(x) offsetof(DXVEncContext, x)
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+ { "format", NULL, OFFSET(tex_fmt), AV_OPT_TYPE_INT, { .i64 = DXV_FMT_DXT1 }, DXV_FMT_DXT1, DXV_FMT_DXT1, FLAGS, "format" },
+ { "dxt1", "DXT1 (Normal Quality, No Alpha)", 0, AV_OPT_TYPE_CONST, { .i64 = DXV_FMT_DXT1 }, 0, 0, FLAGS, "format" },
+ { NULL },
+};
+
+static const AVClass dxvenc_class = {
+ .class_name = "DXV encoder",
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+const FFCodec ff_dxv_encoder = {
+ .p.name = "dxv",
+ CODEC_LONG_NAME("Resolume DXV"),
+ .p.type = AVMEDIA_TYPE_VIDEO,
+ .p.id = AV_CODEC_ID_DXV,
+ .init = dxv_init,
+ FF_CODEC_ENCODE_CB(dxv_encode),
+ .close = dxv_close,
+ .priv_data_size = sizeof(DXVEncContext),
+ .p.capabilities = AV_CODEC_CAP_DR1 |
+ AV_CODEC_CAP_SLICE_THREADS |
+ AV_CODEC_CAP_FRAME_THREADS,
+ .p.priv_class = &dxvenc_class,
+ .p.pix_fmts = (const enum AVPixelFormat[]) {
+ AV_PIX_FMT_RGBA, AV_PIX_FMT_NONE,
+ },
+ .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
+};
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 376388c5bb..0fae3d06d3 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -29,7 +29,7 @@
#include "version_major.h"
-#define LIBAVCODEC_VERSION_MINOR 37
+#define LIBAVCODEC_VERSION_MINOR 38
#define LIBAVCODEC_VERSION_MICRO 100
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
--
2.43.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [FFmpeg-devel] [PATCH] Add DXV encoder with support for DXT1 texture format
2024-01-17 16:55 ` Connor Worley
@ 2024-01-17 20:45 ` Connor Worley
2024-01-17 20:57 ` Connor Worley
0 siblings, 1 reply; 11+ messages in thread
From: Connor Worley @ 2024-01-17 20:45 UTC (permalink / raw)
To: ffmpeg-devel
On 1/17/24 08:55, Connor Worley wrote:
> Signed-off-by: Connor Worley <connorbworley@gmail.com>
> ---
> Changelog | 1 +
> configure | 1 +
> doc/general_contents.texi | 3 +-
> libavcodec/Makefile | 1 +
> libavcodec/allcodecs.c | 1 +
> libavcodec/dxvenc.c | 362 ++++++++++++++++++++++++++++++++++++++
> libavcodec/version.h | 2 +-
> 7 files changed, 369 insertions(+), 2 deletions(-)
> create mode 100644 libavcodec/dxvenc.c
>
> diff --git a/Changelog b/Changelog
> index 5b2899d05b..224d84664a 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -2,6 +2,7 @@ Entries are sorted chronologically from oldest to youngest within each release,
> releases are sorted from youngest to oldest.
>
> version <next>:
> +- DXV DXT1 encoder
> - LEAD MCMP decoder
> - EVC decoding using external library libxevd
> - EVC encoding using external library libxeve
> diff --git a/configure b/configure
> index c8ae0a061d..21663000f8 100755
> --- a/configure
> +++ b/configure
> @@ -2851,6 +2851,7 @@ dvvideo_decoder_select="dvprofile idctdsp"
> dvvideo_encoder_select="dvprofile fdctdsp me_cmp pixblockdsp"
> dxa_decoder_deps="zlib"
> dxv_decoder_select="lzf texturedsp"
> +dxv_encoder_select="texturedspenc"
> eac3_decoder_select="ac3_decoder"
> eac3_encoder_select="ac3_encoder"
> eamad_decoder_select="aandcttables blockdsp bswapdsp"
> diff --git a/doc/general_contents.texi b/doc/general_contents.texi
> index 8b48fed060..f269cbd1a9 100644
> --- a/doc/general_contents.texi
> +++ b/doc/general_contents.texi
> @@ -670,7 +670,8 @@ library:
> @item Redirector @tab @tab X
> @item RedSpark @tab @tab X
> @item Renderware TeXture Dictionary @tab @tab X
> -@item Resolume DXV @tab @tab X
> +@item Resolume DXV @tab X @tab X
> + @tab Encoding is only supported for the DXT1 (Normal Quality, No Alpha) texture format.
> @item RF64 @tab @tab X
> @item RL2 @tab @tab X
> @tab Audio and video format used in some games by Entertainment Software Partners.
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index bb42095165..96361ac794 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -341,6 +341,7 @@ OBJS-$(CONFIG_DVVIDEO_ENCODER) += dvenc.o dv.o dvdata.o
> OBJS-$(CONFIG_DXA_DECODER) += dxa.o
> OBJS-$(CONFIG_DXTORY_DECODER) += dxtory.o
> OBJS-$(CONFIG_DXV_DECODER) += dxv.o
> +OBJS-$(CONFIG_DXV_ENCODER) += dxvenc.o
> OBJS-$(CONFIG_EAC3_DECODER) += eac3_data.o
> OBJS-$(CONFIG_EAC3_ENCODER) += eac3enc.o eac3_data.o
> OBJS-$(CONFIG_EACMV_DECODER) += eacmv.o
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index 93ce8e3224..ef8c3a6d7d 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -106,6 +106,7 @@ extern const FFCodec ff_dvvideo_encoder;
> extern const FFCodec ff_dvvideo_decoder;
> extern const FFCodec ff_dxa_decoder;
> extern const FFCodec ff_dxtory_decoder;
> +extern const FFCodec ff_dxv_encoder;
> extern const FFCodec ff_dxv_decoder;
> extern const FFCodec ff_eacmv_decoder;
> extern const FFCodec ff_eamad_decoder;
> diff --git a/libavcodec/dxvenc.c b/libavcodec/dxvenc.c
> new file mode 100644
> index 0000000000..36ee06699d
> --- /dev/null
> +++ b/libavcodec/dxvenc.c
> @@ -0,0 +1,362 @@
> +/*
> + * Resolume DXV encoder
> + * Copyright (C) 2015 Vittorio Giovara <vittorio.giovara@gmail.com>
> + * Copyright (C) 2015 Tom Butterworth <bangnoise@gmail.com>
> + * Copyright (C) 2018 Paul B Mahol
> + * Copyright (C) 2024 Connor Worley <connorbworley@gmail.com>
> + *
> + * 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 <stdint.h>
> +
> +#include "libavutil/crc.h"
> +#include "libavutil/imgutils.h"
> +#include "libavutil/opt.h"
> +
> +#include "mathops.h"
> +#include "avcodec.h"
> +#include "bytestream.h"
> +#include "codec_internal.h"
> +#include "encode.h"
> +#include "lzf.h"
> +#include "texturedsp.h"
> +#include "thread.h"
> +
> +#define LOOKBACK_HT_ELEMS 0x40000
> +#define LOOKBACK_WORDS 0x20202
> +
> +enum DXVTextureFormat {
> + DXV_FMT_DXT1 = MKBETAG('D', 'X', 'T', '1'),
> +};
> +
> +typedef struct HTEntry {
> + uint32_t key;
> + uint32_t pos;
> +} HTEntry;
> +
> +static void ht_init(HTEntry *ht)
> +{
> + for (size_t i = 0; i < LOOKBACK_HT_ELEMS; i++) {
> + ht[i].pos = -1;
> + }
> +}
> +
> +static uint32_t ht_lookup_and_upsert(HTEntry *ht, AVCRC *hash_ctx,
> + uint32_t key, uint32_t pos)
> +{
> + uint32_t ret = -1;
> + size_t hash = av_crc(hash_ctx, 0, (uint8_t*)&key, 4) % LOOKBACK_HT_ELEMS;
> + for (size_t i = hash; i < hash + LOOKBACK_HT_ELEMS; i++) {
> + size_t wrapped_index = i % LOOKBACK_HT_ELEMS;
> + HTEntry *entry = &ht[wrapped_index];
> + if (entry->key == key || entry->pos == -1) {
> + ret = entry->pos;
> + entry->key = key;
> + entry->pos = pos;
> + break;
> + }
> + }
> + return ret;
> +}
> +
> +static void ht_delete(HTEntry *ht, AVCRC *hash_ctx,
> + uint32_t key, uint32_t pos)
> +{
> + HTEntry *removed_entry = NULL;
> + size_t removed_hash;
> + size_t hash = av_crc(hash_ctx, 0, (uint8_t*)&key, 4) % LOOKBACK_HT_ELEMS;
> +
> + for (size_t i = hash; i < hash + LOOKBACK_HT_ELEMS; i++) {
> + size_t wrapped_index = i % LOOKBACK_HT_ELEMS;
> + HTEntry *entry = &ht[wrapped_index];
> + if (entry->pos == -1)
> + return;
> + if (removed_entry) {
> + size_t candidate_hash = av_crc(hash_ctx, 0, (uint8_t*)&entry->key, 4) % LOOKBACK_HT_ELEMS;
> + if ((wrapped_index > removed_hash && (candidate_hash <= removed_hash || candidate_hash > wrapped_index)) ||
> + (wrapped_index < removed_hash && (candidate_hash <= removed_hash && candidate_hash > wrapped_index))) {
> + *removed_entry = *entry;
> + entry->pos = -1;
> + removed_entry = entry;
> + removed_hash = wrapped_index;
> + }
> + } else if (entry->key == key) {
> + if (entry->pos <= pos) {
> + entry->pos = -1;
> + removed_entry = entry;
> + removed_hash = wrapped_index;
> + } else {
> + return;
> + }
> + }
> + }
> +}
> +
> +typedef struct DXVEncContext {
> + AVClass *class;
> +
> + TextureDSPContext texdsp;
> + PutByteContext pbc;
> +
> + uint8_t *tex_data; // Compressed texture
> + int64_t tex_size; // Texture size
> +
> + /* Optimal number of slices for parallel decoding */
> + int slice_count;
> +
> + TextureDSPThreadContext enc;
> +
> + enum DXVTextureFormat tex_fmt;
> + int (*compress_tex)(AVCodecContext *avctx);
> +
> + AVCRC *crc_ctx;
> +
> + HTEntry color_lookback_ht[LOOKBACK_HT_ELEMS];
> + HTEntry lut_lookback_ht[LOOKBACK_HT_ELEMS];
> +} DXVEncContext;
> +
> +static int compress_texture_thread(AVCodecContext *avctx, void *arg,
> + int slice, int thread_nb)
> +{
> + DXVEncContext *ctx = avctx->priv_data;
> + AVFrame *frame = arg;
> +
> + if (ctx->enc.tex_funct) {
> + ctx->enc.tex_data.out = ctx->tex_data;
> + ctx->enc.frame_data.in = frame->data[0];
> + ctx->enc.stride = frame->linesize[0];
> + return ff_texturedsp_compress_thread(avctx, &ctx->enc, slice, thread_nb);
> + } else {
> + /* unimplemented: YCoCg formats */
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +/* Converts an index offset value to a 2-bit opcode and pushes it to a stream.
> + * Inverse of CHECKPOINT in dxv.c. */
> +#define PUSH_OP(x) \
> + do { \
> + if (state == 16) { \
> + if (bytestream2_get_bytes_left_p(pbc) < 4) { \
> + return AVERROR_INVALIDDATA; \
> + } \
> + value = (uint32_t*)pbc->buffer; \
> + bytestream2_put_le32(pbc, 0); \
> + state = 0; \
> + } \
> + if (idx >= 0x102 * x) { \
> + op = 3; \
> + bytestream2_put_le16(pbc, (idx / x) - 0x102); \
> + } else if (idx >= 2 * x) { \
> + op = 2; \
> + bytestream2_put_byte(pbc, (idx / x) - 2); \
> + } else if (idx == x) { \
> + op = 1; \
> + } else { \
> + op = 0; \
> + } \
> + *value |= (op << (state * 2)); \
> + state++; \
> + } while (0)
> +
> +static int dxv_compress_dxt1(AVCodecContext *avctx)
> +{
> + DXVEncContext *ctx = avctx->priv_data;
> + PutByteContext *pbc = &ctx->pbc;
> + uint32_t *value;
> + uint32_t color, lut, idx, color_idx, lut_idx, prev_pos, state = 16, pos = 2, op = 0;
> +
> + ht_init(ctx->color_lookback_ht);
> + ht_init(ctx->lut_lookback_ht);
> +
> + bytestream2_put_le32(pbc, AV_RL32(ctx->tex_data));
> + bytestream2_put_le32(pbc, AV_RL32(ctx->tex_data + 4));
> +
> + ht_lookup_and_upsert(ctx->color_lookback_ht, ctx->crc_ctx, AV_RL32(ctx->tex_data), 0);
> + ht_lookup_and_upsert(ctx->lut_lookback_ht, ctx->crc_ctx, AV_RL32(ctx->tex_data + 4), 1);
> +
> + while (pos + 2 <= ctx->tex_size / 4) {
> + idx = 0;
> +
> + color = AV_RL32(ctx->tex_data + pos * 4);
> + prev_pos = ht_lookup_and_upsert(ctx->color_lookback_ht, ctx->crc_ctx, color, pos);
> + color_idx = prev_pos != -1 ? pos - prev_pos : 0;
> + if (pos >= LOOKBACK_WORDS) {
> + uint32_t old_pos = pos - LOOKBACK_WORDS;
> + uint32_t old_color = AV_RL32(ctx->tex_data + old_pos * 4);
> + ht_delete(ctx->color_lookback_ht, ctx->crc_ctx, old_color, old_pos);
> + }
> + pos++;
> +
> + lut = AV_RL32(ctx->tex_data + pos * 4);
> + if (color_idx && lut == AV_RL32(ctx->tex_data + (pos - color_idx) * 4)) {
> + idx = color_idx;
> + } else {
> + idx = 0;
> + prev_pos = ht_lookup_and_upsert(ctx->lut_lookback_ht, ctx->crc_ctx, lut, pos);
> + lut_idx = prev_pos != -1 ? pos - prev_pos : 0;
> + }
> + if (pos >= LOOKBACK_WORDS) {
> + uint32_t old_pos = pos - LOOKBACK_WORDS;
> + uint32_t old_lut = AV_RL32(ctx->tex_data + old_pos * 4);
> + ht_delete(ctx->lut_lookback_ht, ctx->crc_ctx, old_lut, old_pos);
> + }
> + pos++;
> +
> + PUSH_OP(2);
> +
> + if (!idx) {
> + idx = color_idx;
> + PUSH_OP(2);
> + if (!idx)
> + bytestream2_put_le32(pbc, color);
> +
> + idx = lut_idx;
> + PUSH_OP(2);
> + if (!idx)
> + bytestream2_put_le32(pbc, lut);
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int dxv_encode(AVCodecContext *avctx, AVPacket *pkt,
> + const AVFrame *frame, int *got_packet)
> +{
> + DXVEncContext *ctx = avctx->priv_data;
> + PutByteContext *pbc = &ctx->pbc;
> + int ret;
> +
> + /* unimplemented: needs to depend on compression ratio of tex format */
> + ret = ff_alloc_packet(avctx, pkt, 12 + 8 + (ctx->tex_size - 8) + (ctx->tex_size - 8)*12/128 + 1);
> + if (ret < 0)
> + return ret;
> +
> + avctx->execute2(avctx, compress_texture_thread, (void*)frame, NULL, ctx->enc.slice_count);
> +
> + bytestream2_init_writer(pbc, pkt->data, pkt->size);
> +
> + bytestream2_put_le32(pbc, ctx->tex_fmt);
> + bytestream2_put_byte(pbc, 4);
> + bytestream2_put_byte(pbc, 0);
> + bytestream2_put_byte(pbc, 0);
> + bytestream2_put_byte(pbc, 0);
> + /* Fill in compressed size later */
> + bytestream2_skip_p(pbc, 4);
> +
> + ret = ctx->compress_tex(avctx);
> + if (ret < 0)
> + return ret;
> +
> + AV_WL32(pkt->data + 8, bytestream2_tell_p(pbc) - 12);
> + av_shrink_packet(pkt, bytestream2_tell_p(pbc));
> +
> + *got_packet = 1;
> + return 0;
> +}
> +
> +static av_cold int dxv_init(AVCodecContext *avctx)
> +{
> + DXVEncContext *ctx = avctx->priv_data;
> + int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
> +
> + if (ret < 0) {
> + av_log(avctx, AV_LOG_ERROR, "Invalid image size %dx%d.\n",
> + avctx->width, avctx->height);
> + return ret;
> + }
> +
> + if (avctx->width % 4 || avctx->height % 4) {
> + av_log(avctx, AV_LOG_ERROR, "Video size %dx%d is not multiple of 4.\n",
> + avctx->width, avctx->height);
> + return AVERROR_INVALIDDATA;
> + }
> +
> + ff_texturedspenc_init(&ctx->texdsp);
> +
> + switch (ctx->tex_fmt) {
> + case DXV_FMT_DXT1:
> + ctx->compress_tex = dxv_compress_dxt1;
> + ctx->enc.tex_funct = ctx->texdsp.dxt1_block;
> + ctx->enc.tex_ratio = 8;
> + ctx->tex_size = avctx->width * avctx->height / 2;
> + break;
> + default:
> + av_log(avctx, AV_LOG_ERROR, "Invalid format %08X\n", ctx->tex_fmt);
> + return AVERROR_INVALIDDATA;
> + }
> + ctx->enc.raw_ratio = 16;
> + ctx->enc.slice_count = av_clip(avctx->thread_count, 1, avctx->height / TEXTURE_BLOCK_H);
> +
> + ctx->tex_data = av_malloc(ctx->tex_size);
> + if (!ctx->tex_data) {
> + return AVERROR(ENOMEM);
> + }
> +
> + ctx->crc_ctx = (AVCRC*)av_crc_get_table(AV_CRC_32_IEEE);
> + if (!ctx->crc_ctx) {
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +static av_cold int dxv_close(AVCodecContext *avctx)
> +{
> + DXVEncContext *ctx = avctx->priv_data;
> +
> + av_freep(&ctx->tex_data);
> +
> + return 0;
> +}
> +
> +#define OFFSET(x) offsetof(DXVEncContext, x)
> +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
> +static const AVOption options[] = {
> + { "format", NULL, OFFSET(tex_fmt), AV_OPT_TYPE_INT, { .i64 = DXV_FMT_DXT1 }, DXV_FMT_DXT1, DXV_FMT_DXT1, FLAGS, "format" },
> + { "dxt1", "DXT1 (Normal Quality, No Alpha)", 0, AV_OPT_TYPE_CONST, { .i64 = DXV_FMT_DXT1 }, 0, 0, FLAGS, "format" },
> + { NULL },
> +};
> +
> +static const AVClass dxvenc_class = {
> + .class_name = "DXV encoder",
> + .option = options,
> + .version = LIBAVUTIL_VERSION_INT,
> +};
> +
> +const FFCodec ff_dxv_encoder = {
> + .p.name = "dxv",
> + CODEC_LONG_NAME("Resolume DXV"),
> + .p.type = AVMEDIA_TYPE_VIDEO,
> + .p.id = AV_CODEC_ID_DXV,
> + .init = dxv_init,
> + FF_CODEC_ENCODE_CB(dxv_encode),
> + .close = dxv_close,
> + .priv_data_size = sizeof(DXVEncContext),
> + .p.capabilities = AV_CODEC_CAP_DR1 |
> + AV_CODEC_CAP_SLICE_THREADS |
> + AV_CODEC_CAP_FRAME_THREADS,
> + .p.priv_class = &dxvenc_class,
> + .p.pix_fmts = (const enum AVPixelFormat[]) {
> + AV_PIX_FMT_RGBA, AV_PIX_FMT_NONE,
> + },
> + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
> +};
> diff --git a/libavcodec/version.h b/libavcodec/version.h
> index 376388c5bb..0fae3d06d3 100644
> --- a/libavcodec/version.h
> +++ b/libavcodec/version.h
> @@ -29,7 +29,7 @@
>
> #include "version_major.h"
>
> -#define LIBAVCODEC_VERSION_MINOR 37
> +#define LIBAVCODEC_VERSION_MINOR 38
> #define LIBAVCODEC_VERSION_MICRO 100
>
> #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
Sorry, still figuring out my email client settings to get patches to send correctly. Bear with me.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 11+ messages in thread
* [FFmpeg-devel] [PATCH] Add DXV encoder with support for DXT1 texture format
2024-01-17 20:45 ` Connor Worley
@ 2024-01-17 20:57 ` Connor Worley
2024-01-19 16:23 ` Vittorio Giovara
0 siblings, 1 reply; 11+ messages in thread
From: Connor Worley @ 2024-01-17 20:57 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: Connor Worley <connorbworley@gmail.com>
---
Changelog | 1 +
configure | 1 +
doc/general_contents.texi | 3 +-
libavcodec/Makefile | 1 +
libavcodec/allcodecs.c | 1 +
libavcodec/dxvenc.c | 358 ++++++++++++++++++++++++++++++++++++++
libavcodec/version.h | 2 +-
7 files changed, 365 insertions(+), 2 deletions(-)
create mode 100644 libavcodec/dxvenc.c
diff --git a/Changelog b/Changelog
index 5b2899d05b..224d84664a 100644
--- a/Changelog
+++ b/Changelog
@@ -2,6 +2,7 @@ Entries are sorted chronologically from oldest to youngest within each release,
releases are sorted from youngest to oldest.
version <next>:
+- DXV DXT1 encoder
- LEAD MCMP decoder
- EVC decoding using external library libxevd
- EVC encoding using external library libxeve
diff --git a/configure b/configure
index c8ae0a061d..21663000f8 100755
--- a/configure
+++ b/configure
@@ -2851,6 +2851,7 @@ dvvideo_decoder_select="dvprofile idctdsp"
dvvideo_encoder_select="dvprofile fdctdsp me_cmp pixblockdsp"
dxa_decoder_deps="zlib"
dxv_decoder_select="lzf texturedsp"
+dxv_encoder_select="texturedspenc"
eac3_decoder_select="ac3_decoder"
eac3_encoder_select="ac3_encoder"
eamad_decoder_select="aandcttables blockdsp bswapdsp"
diff --git a/doc/general_contents.texi b/doc/general_contents.texi
index 8b48fed060..f269cbd1a9 100644
--- a/doc/general_contents.texi
+++ b/doc/general_contents.texi
@@ -670,7 +670,8 @@ library:
@item Redirector @tab @tab X
@item RedSpark @tab @tab X
@item Renderware TeXture Dictionary @tab @tab X
-@item Resolume DXV @tab @tab X
+@item Resolume DXV @tab X @tab X
+ @tab Encoding is only supported for the DXT1 (Normal Quality, No Alpha) texture format.
@item RF64 @tab @tab X
@item RL2 @tab @tab X
@tab Audio and video format used in some games by Entertainment Software Partners.
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index bb42095165..96361ac794 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -341,6 +341,7 @@ OBJS-$(CONFIG_DVVIDEO_ENCODER) += dvenc.o dv.o dvdata.o
OBJS-$(CONFIG_DXA_DECODER) += dxa.o
OBJS-$(CONFIG_DXTORY_DECODER) += dxtory.o
OBJS-$(CONFIG_DXV_DECODER) += dxv.o
+OBJS-$(CONFIG_DXV_ENCODER) += dxvenc.o
OBJS-$(CONFIG_EAC3_DECODER) += eac3_data.o
OBJS-$(CONFIG_EAC3_ENCODER) += eac3enc.o eac3_data.o
OBJS-$(CONFIG_EACMV_DECODER) += eacmv.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 93ce8e3224..ef8c3a6d7d 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -106,6 +106,7 @@ extern const FFCodec ff_dvvideo_encoder;
extern const FFCodec ff_dvvideo_decoder;
extern const FFCodec ff_dxa_decoder;
extern const FFCodec ff_dxtory_decoder;
+extern const FFCodec ff_dxv_encoder;
extern const FFCodec ff_dxv_decoder;
extern const FFCodec ff_eacmv_decoder;
extern const FFCodec ff_eamad_decoder;
diff --git a/libavcodec/dxvenc.c b/libavcodec/dxvenc.c
new file mode 100644
index 0000000000..33080fa1c9
--- /dev/null
+++ b/libavcodec/dxvenc.c
@@ -0,0 +1,358 @@
+/*
+ * Resolume DXV encoder
+ * Copyright (C) 2015 Vittorio Giovara <vittorio.giovara@gmail.com>
+ * Copyright (C) 2015 Tom Butterworth <bangnoise@gmail.com>
+ * Copyright (C) 2018 Paul B Mahol
+ * Copyright (C) 2024 Connor Worley <connorbworley@gmail.com>
+ *
+ * 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 <stdint.h>
+
+#include "libavutil/crc.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+
+#include "bytestream.h"
+#include "codec_internal.h"
+#include "encode.h"
+#include "texturedsp.h"
+
+#define LOOKBACK_HT_ELEMS 0x40000
+#define LOOKBACK_WORDS 0x20202
+
+enum DXVTextureFormat {
+ DXV_FMT_DXT1 = MKBETAG('D', 'X', 'T', '1'),
+};
+
+typedef struct HTEntry {
+ uint32_t key;
+ uint32_t pos;
+} HTEntry;
+
+static void ht_init(HTEntry *ht)
+{
+ for (size_t i = 0; i < LOOKBACK_HT_ELEMS; i++) {
+ ht[i].pos = -1;
+ }
+}
+
+static uint32_t ht_lookup_and_upsert(HTEntry *ht, AVCRC *hash_ctx,
+ uint32_t key, uint32_t pos)
+{
+ uint32_t ret = -1;
+ size_t hash = av_crc(hash_ctx, 0, (uint8_t*)&key, 4) % LOOKBACK_HT_ELEMS;
+ for (size_t i = hash; i < hash + LOOKBACK_HT_ELEMS; i++) {
+ size_t wrapped_index = i % LOOKBACK_HT_ELEMS;
+ HTEntry *entry = &ht[wrapped_index];
+ if (entry->key == key || entry->pos == -1) {
+ ret = entry->pos;
+ entry->key = key;
+ entry->pos = pos;
+ break;
+ }
+ }
+ return ret;
+}
+
+static void ht_delete(HTEntry *ht, AVCRC *hash_ctx,
+ uint32_t key, uint32_t pos)
+{
+ HTEntry *removed_entry = NULL;
+ size_t removed_hash;
+ size_t hash = av_crc(hash_ctx, 0, (uint8_t*)&key, 4) % LOOKBACK_HT_ELEMS;
+
+ for (size_t i = hash; i < hash + LOOKBACK_HT_ELEMS; i++) {
+ size_t wrapped_index = i % LOOKBACK_HT_ELEMS;
+ HTEntry *entry = &ht[wrapped_index];
+ if (entry->pos == -1)
+ return;
+ if (removed_entry) {
+ size_t candidate_hash = av_crc(hash_ctx, 0, (uint8_t*)&entry->key, 4) % LOOKBACK_HT_ELEMS;
+ if ((wrapped_index > removed_hash && (candidate_hash <= removed_hash || candidate_hash > wrapped_index)) ||
+ (wrapped_index < removed_hash && (candidate_hash <= removed_hash && candidate_hash > wrapped_index))) {
+ *removed_entry = *entry;
+ entry->pos = -1;
+ removed_entry = entry;
+ removed_hash = wrapped_index;
+ }
+ } else if (entry->key == key) {
+ if (entry->pos <= pos) {
+ entry->pos = -1;
+ removed_entry = entry;
+ removed_hash = wrapped_index;
+ } else {
+ return;
+ }
+ }
+ }
+}
+
+typedef struct DXVEncContext {
+ AVClass *class;
+
+ TextureDSPContext texdsp;
+ PutByteContext pbc;
+
+ uint8_t *tex_data; // Compressed texture
+ int64_t tex_size; // Texture size
+
+ /* Optimal number of slices for parallel decoding */
+ int slice_count;
+
+ TextureDSPThreadContext enc;
+
+ enum DXVTextureFormat tex_fmt;
+ int (*compress_tex)(AVCodecContext *avctx);
+
+ AVCRC *crc_ctx;
+
+ HTEntry color_lookback_ht[LOOKBACK_HT_ELEMS];
+ HTEntry lut_lookback_ht[LOOKBACK_HT_ELEMS];
+} DXVEncContext;
+
+static int compress_texture_thread(AVCodecContext *avctx, void *arg,
+ int slice, int thread_nb)
+{
+ DXVEncContext *ctx = avctx->priv_data;
+ AVFrame *frame = arg;
+
+ if (ctx->enc.tex_funct) {
+ ctx->enc.tex_data.out = ctx->tex_data;
+ ctx->enc.frame_data.in = frame->data[0];
+ ctx->enc.stride = frame->linesize[0];
+ return ff_texturedsp_compress_thread(avctx, &ctx->enc, slice, thread_nb);
+ } else {
+ /* unimplemented: YCoCg formats */
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Converts an index offset value to a 2-bit opcode and pushes it to a stream.
+ * Inverse of CHECKPOINT in dxv.c. */
+#define PUSH_OP(x) \
+ do { \
+ if (state == 16) { \
+ if (bytestream2_get_bytes_left_p(pbc) < 4) { \
+ return AVERROR_INVALIDDATA; \
+ } \
+ value = (uint32_t*)pbc->buffer; \
+ bytestream2_put_le32(pbc, 0); \
+ state = 0; \
+ } \
+ if (idx >= 0x102 * x) { \
+ op = 3; \
+ bytestream2_put_le16(pbc, (idx / x) - 0x102); \
+ } else if (idx >= 2 * x) { \
+ op = 2; \
+ bytestream2_put_byte(pbc, (idx / x) - 2); \
+ } else if (idx == x) { \
+ op = 1; \
+ } else { \
+ op = 0; \
+ } \
+ *value |= (op << (state * 2)); \
+ state++; \
+ } while (0)
+
+static int dxv_compress_dxt1(AVCodecContext *avctx)
+{
+ DXVEncContext *ctx = avctx->priv_data;
+ PutByteContext *pbc = &ctx->pbc;
+ uint32_t *value;
+ uint32_t color, lut, idx, color_idx, lut_idx, prev_pos, state = 16, pos = 2, op = 0;
+
+ ht_init(ctx->color_lookback_ht);
+ ht_init(ctx->lut_lookback_ht);
+
+ bytestream2_put_le32(pbc, AV_RL32(ctx->tex_data));
+ bytestream2_put_le32(pbc, AV_RL32(ctx->tex_data + 4));
+
+ ht_lookup_and_upsert(ctx->color_lookback_ht, ctx->crc_ctx, AV_RL32(ctx->tex_data), 0);
+ ht_lookup_and_upsert(ctx->lut_lookback_ht, ctx->crc_ctx, AV_RL32(ctx->tex_data + 4), 1);
+
+ while (pos + 2 <= ctx->tex_size / 4) {
+ idx = 0;
+
+ color = AV_RL32(ctx->tex_data + pos * 4);
+ prev_pos = ht_lookup_and_upsert(ctx->color_lookback_ht, ctx->crc_ctx, color, pos);
+ color_idx = prev_pos != -1 ? pos - prev_pos : 0;
+ if (pos >= LOOKBACK_WORDS) {
+ uint32_t old_pos = pos - LOOKBACK_WORDS;
+ uint32_t old_color = AV_RL32(ctx->tex_data + old_pos * 4);
+ ht_delete(ctx->color_lookback_ht, ctx->crc_ctx, old_color, old_pos);
+ }
+ pos++;
+
+ lut = AV_RL32(ctx->tex_data + pos * 4);
+ if (color_idx && lut == AV_RL32(ctx->tex_data + (pos - color_idx) * 4)) {
+ idx = color_idx;
+ } else {
+ idx = 0;
+ prev_pos = ht_lookup_and_upsert(ctx->lut_lookback_ht, ctx->crc_ctx, lut, pos);
+ lut_idx = prev_pos != -1 ? pos - prev_pos : 0;
+ }
+ if (pos >= LOOKBACK_WORDS) {
+ uint32_t old_pos = pos - LOOKBACK_WORDS;
+ uint32_t old_lut = AV_RL32(ctx->tex_data + old_pos * 4);
+ ht_delete(ctx->lut_lookback_ht, ctx->crc_ctx, old_lut, old_pos);
+ }
+ pos++;
+
+ PUSH_OP(2);
+
+ if (!idx) {
+ idx = color_idx;
+ PUSH_OP(2);
+ if (!idx)
+ bytestream2_put_le32(pbc, color);
+
+ idx = lut_idx;
+ PUSH_OP(2);
+ if (!idx)
+ bytestream2_put_le32(pbc, lut);
+ }
+ }
+
+ return 0;
+}
+
+static int dxv_encode(AVCodecContext *avctx, AVPacket *pkt,
+ const AVFrame *frame, int *got_packet)
+{
+ DXVEncContext *ctx = avctx->priv_data;
+ PutByteContext *pbc = &ctx->pbc;
+ int ret;
+
+ /* unimplemented: needs to depend on compression ratio of tex format */
+ ret = ff_alloc_packet(avctx, pkt, 12 + 8 + (ctx->tex_size - 8) + (ctx->tex_size - 8)*12/128 + 1);
+ if (ret < 0)
+ return ret;
+
+ avctx->execute2(avctx, compress_texture_thread, (void*)frame, NULL, ctx->enc.slice_count);
+
+ bytestream2_init_writer(pbc, pkt->data, pkt->size);
+
+ bytestream2_put_le32(pbc, ctx->tex_fmt);
+ bytestream2_put_byte(pbc, 4);
+ bytestream2_put_byte(pbc, 0);
+ bytestream2_put_byte(pbc, 0);
+ bytestream2_put_byte(pbc, 0);
+ /* Fill in compressed size later */
+ bytestream2_skip_p(pbc, 4);
+
+ ret = ctx->compress_tex(avctx);
+ if (ret < 0)
+ return ret;
+
+ AV_WL32(pkt->data + 8, bytestream2_tell_p(pbc) - 12);
+ av_shrink_packet(pkt, bytestream2_tell_p(pbc));
+
+ *got_packet = 1;
+ return 0;
+}
+
+static av_cold int dxv_init(AVCodecContext *avctx)
+{
+ DXVEncContext *ctx = avctx->priv_data;
+ int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
+
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid image size %dx%d.\n",
+ avctx->width, avctx->height);
+ return ret;
+ }
+
+ if (avctx->width % 4 || avctx->height % 4) {
+ av_log(avctx, AV_LOG_ERROR, "Video size %dx%d is not multiple of 4.\n",
+ avctx->width, avctx->height);
+ return AVERROR_INVALIDDATA;
+ }
+
+ ff_texturedspenc_init(&ctx->texdsp);
+
+ switch (ctx->tex_fmt) {
+ case DXV_FMT_DXT1:
+ ctx->compress_tex = dxv_compress_dxt1;
+ ctx->enc.tex_funct = ctx->texdsp.dxt1_block;
+ ctx->enc.tex_ratio = 8;
+ ctx->tex_size = avctx->width * avctx->height / 2;
+ break;
+ default:
+ av_log(avctx, AV_LOG_ERROR, "Invalid format %08X\n", ctx->tex_fmt);
+ return AVERROR_INVALIDDATA;
+ }
+ ctx->enc.raw_ratio = 16;
+ ctx->enc.slice_count = av_clip(avctx->thread_count, 1, avctx->height / TEXTURE_BLOCK_H);
+
+ ctx->tex_data = av_malloc(ctx->tex_size);
+ if (!ctx->tex_data) {
+ return AVERROR(ENOMEM);
+ }
+
+ ctx->crc_ctx = (AVCRC*)av_crc_get_table(AV_CRC_32_IEEE);
+ if (!ctx->crc_ctx) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static av_cold int dxv_close(AVCodecContext *avctx)
+{
+ DXVEncContext *ctx = avctx->priv_data;
+
+ av_freep(&ctx->tex_data);
+
+ return 0;
+}
+
+#define OFFSET(x) offsetof(DXVEncContext, x)
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+ { "format", NULL, OFFSET(tex_fmt), AV_OPT_TYPE_INT, { .i64 = DXV_FMT_DXT1 }, DXV_FMT_DXT1, DXV_FMT_DXT1, FLAGS, "format" },
+ { "dxt1", "DXT1 (Normal Quality, No Alpha)", 0, AV_OPT_TYPE_CONST, { .i64 = DXV_FMT_DXT1 }, 0, 0, FLAGS, "format" },
+ { NULL },
+};
+
+static const AVClass dxvenc_class = {
+ .class_name = "DXV encoder",
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+const FFCodec ff_dxv_encoder = {
+ .p.name = "dxv",
+ CODEC_LONG_NAME("Resolume DXV"),
+ .p.type = AVMEDIA_TYPE_VIDEO,
+ .p.id = AV_CODEC_ID_DXV,
+ .init = dxv_init,
+ FF_CODEC_ENCODE_CB(dxv_encode),
+ .close = dxv_close,
+ .priv_data_size = sizeof(DXVEncContext),
+ .p.capabilities = AV_CODEC_CAP_DR1 |
+ AV_CODEC_CAP_SLICE_THREADS |
+ AV_CODEC_CAP_FRAME_THREADS,
+ .p.priv_class = &dxvenc_class,
+ .p.pix_fmts = (const enum AVPixelFormat[]) {
+ AV_PIX_FMT_RGBA, AV_PIX_FMT_NONE,
+ },
+ .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
+};
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 376388c5bb..0fae3d06d3 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -29,7 +29,7 @@
#include "version_major.h"
-#define LIBAVCODEC_VERSION_MINOR 37
+#define LIBAVCODEC_VERSION_MINOR 38
#define LIBAVCODEC_VERSION_MICRO 100
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
--
2.43.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [FFmpeg-devel] [PATCH] Add DXV encoder with support for DXT1 texture format
2024-01-17 20:57 ` Connor Worley
@ 2024-01-19 16:23 ` Vittorio Giovara
2024-01-19 16:55 ` Connor Worley
0 siblings, 1 reply; 11+ messages in thread
From: Vittorio Giovara @ 2024-01-19 16:23 UTC (permalink / raw)
To: FFmpeg development discussions and patches
Hi,
thanks for the patch, below a few minor nits
On Wed, Jan 17, 2024 at 9:57 PM Connor Worley <connorbworley@gmail.com>
wrote:
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index 93ce8e3224..ef8c3a6d7d 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -106,6 +106,7 @@ extern const FFCodec ff_dvvideo_encoder;
> extern const FFCodec ff_dvvideo_decoder;
> extern const FFCodec ff_dxa_decoder;
> extern const FFCodec ff_dxtory_decoder;
> +extern const FFCodec ff_dxv_encoder;
> extern const FFCodec ff_dxv_decoder;
>
nit: keep list in order
> extern const FFCodec ff_eacmv_decoder;
> extern const FFCodec ff_eamad_decoder;
> diff --git a/libavcodec/dxvenc.c b/libavcodec/dxvenc.c
> new file mode 100644
> index 0000000000..33080fa1c9
> --- /dev/null
> +++ b/libavcodec/dxvenc.c
> @@ -0,0 +1,358 @@
> +/*
> + * Resolume DXV encoder
> + * Copyright (C) 2015 Vittorio Giovara <vittorio.giovara@gmail.com>
> + * Copyright (C) 2015 Tom Butterworth <bangnoise@gmail.com>
> + * Copyright (C) 2018 Paul B Mahol
> + * Copyright (C) 2024 Connor Worley <connorbworley@gmail.com>
>
Idk about tom or paul, but I haven't done anything for this encoder :)
I think you can prune the list of copyright quite a bit here
> +#define LOOKBACK_HT_ELEMS 0x40000
>
What does the HT stand for?
> +#define LOOKBACK_WORDS 0x20202
> +
> +enum DXVTextureFormat {
> + DXV_FMT_DXT1 = MKBETAG('D', 'X', 'T', '1'),
> +};
>
Why would you go for an enum here? Just for future expansion and the switch
case below?
lgtm overall
--
Vittorio
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [FFmpeg-devel] [PATCH] Add DXV encoder with support for DXT1 texture format
2024-01-19 16:23 ` Vittorio Giovara
@ 2024-01-19 16:55 ` Connor Worley
2024-01-19 16:58 ` Vittorio Giovara
0 siblings, 1 reply; 11+ messages in thread
From: Connor Worley @ 2024-01-19 16:55 UTC (permalink / raw)
To: ffmpeg-devel
Thanks for the feedback! For the next revision, is it preferred to reply to this thread or create a new one?
On 1/19/24 08:23, Vittorio Giovara wrote:
>> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
>> index 93ce8e3224..ef8c3a6d7d 100644
>> --- a/libavcodec/allcodecs.c
>> +++ b/libavcodec/allcodecs.c
>> @@ -106,6 +106,7 @@ extern const FFCodec ff_dvvideo_encoder;
>> extern const FFCodec ff_dvvideo_decoder;
>> extern const FFCodec ff_dxa_decoder;
>> extern const FFCodec ff_dxtory_decoder;
>> +extern const FFCodec ff_dxv_encoder;
>> extern const FFCodec ff_dxv_decoder;
>>
> nit: keep list in order
Not sure what you mean, the present order seems to be encoder followed by decoder for codecs that have both.
>> extern const FFCodec ff_eacmv_decoder;
>> extern const FFCodec ff_eamad_decoder;
>> diff --git a/libavcodec/dxvenc.c b/libavcodec/dxvenc.c
>> new file mode 100644
>> index 0000000000..33080fa1c9
>> --- /dev/null
>> +++ b/libavcodec/dxvenc.c
>> @@ -0,0 +1,358 @@
>> +/*
>> + * Resolume DXV encoder
>> + * Copyright (C) 2015 Vittorio Giovara <vittorio.giovara@gmail.com>
>> + * Copyright (C) 2015 Tom Butterworth <bangnoise@gmail.com>
>> + * Copyright (C) 2018 Paul B Mahol
>> + * Copyright (C) 2024 Connor Worley <connorbworley@gmail.com>
>>
> Idk about tom or paul, but I haven't done anything for this encoder :)
> I think you can prune the list of copyright quite a bit here
Got it. I copied some code verbatim from dxv.c and hapenc.c and erred on the side of overcrediting.
>> +#define LOOKBACK_HT_ELEMS 0x40000
>>
> What does the HT stand for?
Hash table -- this change implements a simple linear probing approach.
>> +#define LOOKBACK_WORDS 0x20202
>> +
>> +enum DXVTextureFormat {
>> + DXV_FMT_DXT1 = MKBETAG('D', 'X', 'T', '1'),
>> +};
>>
> Why would you go for an enum here? Just for future expansion and the switch
> case below?
Exactly, that's the plan.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [FFmpeg-devel] [PATCH] Add DXV encoder with support for DXT1 texture format
2024-01-19 16:55 ` Connor Worley
@ 2024-01-19 16:58 ` Vittorio Giovara
2024-01-19 17:07 ` [FFmpeg-devel] [PATCH v2] lavc/dxvenc: add " Connor Worley
0 siblings, 1 reply; 11+ messages in thread
From: Vittorio Giovara @ 2024-01-19 16:58 UTC (permalink / raw)
To: FFmpeg development discussions and patches
On Fri, Jan 19, 2024 at 5:56 PM Connor Worley <connorbworley@gmail.com>
wrote:
> Thanks for the feedback! For the next revision, is it preferred to reply
> to this thread or create a new one?
>
here is fine
> On 1/19/24 08:23, Vittorio Giovara wrote:
> >> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> >> index 93ce8e3224..ef8c3a6d7d 100644
> >> --- a/libavcodec/allcodecs.c
> >> +++ b/libavcodec/allcodecs.c
> >> @@ -106,6 +106,7 @@ extern const FFCodec ff_dvvideo_encoder;
> >> extern const FFCodec ff_dvvideo_decoder;
> >> extern const FFCodec ff_dxa_decoder;
> >> extern const FFCodec ff_dxtory_decoder;
> >> +extern const FFCodec ff_dxv_encoder;
> >> extern const FFCodec ff_dxv_decoder;
> >>
> > nit: keep list in order
>
>
> Not sure what you mean, the present order seems to be encoder followed by
> decoder for codecs that have both.
>
disregard, I assumed it was in alphabetical order
> > What does the HT stand for?
>
>
> Hash table -- this change implements a simple linear probing approach.
>
got it, would be nice to have a small comment on why it's needed, as
documentation
> >> +#define LOOKBACK_WORDS 0x20202
> >> +
> >> +enum DXVTextureFormat {
> >> + DXV_FMT_DXT1 = MKBETAG('D', 'X', 'T', '1'),
> >> +};
> >>
> > Why would you go for an enum here? Just for future expansion and the
> switch
> > case below?
>
>
> Exactly, that's the plan.
>
very cool
--
Vittorio
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 11+ messages in thread
* [FFmpeg-devel] [PATCH v2] lavc/dxvenc: add DXV encoder with support for DXT1 texture format
2024-01-19 16:58 ` Vittorio Giovara
@ 2024-01-19 17:07 ` Connor Worley
2024-01-19 19:16 ` Connor Worley
0 siblings, 1 reply; 11+ messages in thread
From: Connor Worley @ 2024-01-19 17:07 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: Connor Worley <connorbworley@gmail.com>
---
Changelog | 1 +
configure | 1 +
doc/general_contents.texi | 3 +-
libavcodec/Makefile | 1 +
libavcodec/allcodecs.c | 1 +
libavcodec/dxvenc.c | 361 ++++++++++++++++++++++++++++++++++++++
libavcodec/version.h | 2 +-
7 files changed, 368 insertions(+), 2 deletions(-)
create mode 100644 libavcodec/dxvenc.c
diff --git a/Changelog b/Changelog
index 5b2899d05b..224d84664a 100644
--- a/Changelog
+++ b/Changelog
@@ -2,6 +2,7 @@ Entries are sorted chronologically from oldest to youngest within each release,
releases are sorted from youngest to oldest.
version <next>:
+- DXV DXT1 encoder
- LEAD MCMP decoder
- EVC decoding using external library libxevd
- EVC encoding using external library libxeve
diff --git a/configure b/configure
index c8ae0a061d..21663000f8 100755
--- a/configure
+++ b/configure
@@ -2851,6 +2851,7 @@ dvvideo_decoder_select="dvprofile idctdsp"
dvvideo_encoder_select="dvprofile fdctdsp me_cmp pixblockdsp"
dxa_decoder_deps="zlib"
dxv_decoder_select="lzf texturedsp"
+dxv_encoder_select="texturedspenc"
eac3_decoder_select="ac3_decoder"
eac3_encoder_select="ac3_encoder"
eamad_decoder_select="aandcttables blockdsp bswapdsp"
diff --git a/doc/general_contents.texi b/doc/general_contents.texi
index 8b48fed060..f269cbd1a9 100644
--- a/doc/general_contents.texi
+++ b/doc/general_contents.texi
@@ -670,7 +670,8 @@ library:
@item Redirector @tab @tab X
@item RedSpark @tab @tab X
@item Renderware TeXture Dictionary @tab @tab X
-@item Resolume DXV @tab @tab X
+@item Resolume DXV @tab X @tab X
+ @tab Encoding is only supported for the DXT1 (Normal Quality, No Alpha) texture format.
@item RF64 @tab @tab X
@item RL2 @tab @tab X
@tab Audio and video format used in some games by Entertainment Software Partners.
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index bb42095165..96361ac794 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -341,6 +341,7 @@ OBJS-$(CONFIG_DVVIDEO_ENCODER) += dvenc.o dv.o dvdata.o
OBJS-$(CONFIG_DXA_DECODER) += dxa.o
OBJS-$(CONFIG_DXTORY_DECODER) += dxtory.o
OBJS-$(CONFIG_DXV_DECODER) += dxv.o
+OBJS-$(CONFIG_DXV_ENCODER) += dxvenc.o
OBJS-$(CONFIG_EAC3_DECODER) += eac3_data.o
OBJS-$(CONFIG_EAC3_ENCODER) += eac3enc.o eac3_data.o
OBJS-$(CONFIG_EACMV_DECODER) += eacmv.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 93ce8e3224..ef8c3a6d7d 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -106,6 +106,7 @@ extern const FFCodec ff_dvvideo_encoder;
extern const FFCodec ff_dvvideo_decoder;
extern const FFCodec ff_dxa_decoder;
extern const FFCodec ff_dxtory_decoder;
+extern const FFCodec ff_dxv_encoder;
extern const FFCodec ff_dxv_decoder;
extern const FFCodec ff_eacmv_decoder;
extern const FFCodec ff_eamad_decoder;
diff --git a/libavcodec/dxvenc.c b/libavcodec/dxvenc.c
new file mode 100644
index 0000000000..3a5b310c9b
--- /dev/null
+++ b/libavcodec/dxvenc.c
@@ -0,0 +1,361 @@
+/*
+ * Resolume DXV encoder
+ * Copyright (C) 2024 Connor Worley <connorbworley@gmail.com>
+ *
+ * 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 <stdint.h>
+
+#include "libavutil/crc.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+
+#include "bytestream.h"
+#include "codec_internal.h"
+#include "encode.h"
+#include "texturedsp.h"
+
+#define DXV_HEADER_LENGTH 12
+
+/*
+ * DXV uses LZ-like back-references to avoid copying words that have already
+ * appeared in the decompressed stream. Using a simple hash table (HT)
+ * significantly speeds up the lookback process while encoding.
+ */
+#define LOOKBACK_HT_ELEMS 0x40000
+#define LOOKBACK_WORDS 0x20202
+
+enum DXVTextureFormat {
+ DXV_FMT_DXT1 = MKBETAG('D', 'X', 'T', '1'),
+};
+
+typedef struct HTEntry {
+ uint32_t key;
+ uint32_t pos;
+} HTEntry;
+
+static void ht_init(HTEntry *ht)
+{
+ for (size_t i = 0; i < LOOKBACK_HT_ELEMS; i++) {
+ ht[i].pos = -1;
+ }
+}
+
+static uint32_t ht_lookup_and_upsert(HTEntry *ht, AVCRC *hash_ctx,
+ uint32_t key, uint32_t pos)
+{
+ uint32_t ret = -1;
+ size_t hash = av_crc(hash_ctx, 0, (uint8_t*)&key, 4) % LOOKBACK_HT_ELEMS;
+ for (size_t i = hash; i < hash + LOOKBACK_HT_ELEMS; i++) {
+ size_t wrapped_index = i % LOOKBACK_HT_ELEMS;
+ HTEntry *entry = &ht[wrapped_index];
+ if (entry->key == key || entry->pos == -1) {
+ ret = entry->pos;
+ entry->key = key;
+ entry->pos = pos;
+ break;
+ }
+ }
+ return ret;
+}
+
+static void ht_delete(HTEntry *ht, AVCRC *hash_ctx,
+ uint32_t key, uint32_t pos)
+{
+ HTEntry *removed_entry = NULL;
+ size_t removed_hash;
+ size_t hash = av_crc(hash_ctx, 0, (uint8_t*)&key, 4) % LOOKBACK_HT_ELEMS;
+
+ for (size_t i = hash; i < hash + LOOKBACK_HT_ELEMS; i++) {
+ size_t wrapped_index = i % LOOKBACK_HT_ELEMS;
+ HTEntry *entry = &ht[wrapped_index];
+ if (entry->pos == -1)
+ return;
+ if (removed_entry) {
+ size_t candidate_hash = av_crc(hash_ctx, 0, (uint8_t*)&entry->key, 4) % LOOKBACK_HT_ELEMS;
+ if ((wrapped_index > removed_hash && (candidate_hash <= removed_hash || candidate_hash > wrapped_index)) ||
+ (wrapped_index < removed_hash && (candidate_hash <= removed_hash && candidate_hash > wrapped_index))) {
+ *removed_entry = *entry;
+ entry->pos = -1;
+ removed_entry = entry;
+ removed_hash = wrapped_index;
+ }
+ } else if (entry->key == key) {
+ if (entry->pos <= pos) {
+ entry->pos = -1;
+ removed_entry = entry;
+ removed_hash = wrapped_index;
+ } else {
+ return;
+ }
+ }
+ }
+}
+
+typedef struct DXVEncContext {
+ AVClass *class;
+
+ TextureDSPContext texdsp;
+ PutByteContext pbc;
+
+ uint8_t *tex_data; // Compressed texture
+ int64_t tex_size; // Texture size
+
+ /* Optimal number of slices for parallel decoding */
+ int slice_count;
+
+ TextureDSPThreadContext enc;
+
+ enum DXVTextureFormat tex_fmt;
+ int (*compress_tex)(AVCodecContext *avctx);
+
+ AVCRC *crc_ctx;
+
+ HTEntry color_lookback_ht[LOOKBACK_HT_ELEMS];
+ HTEntry lut_lookback_ht[LOOKBACK_HT_ELEMS];
+} DXVEncContext;
+
+static int compress_texture_thread(AVCodecContext *avctx, void *arg,
+ int slice, int thread_nb)
+{
+ DXVEncContext *ctx = avctx->priv_data;
+ AVFrame *frame = arg;
+
+ if (ctx->enc.tex_funct) {
+ ctx->enc.tex_data.out = ctx->tex_data;
+ ctx->enc.frame_data.in = frame->data[0];
+ ctx->enc.stride = frame->linesize[0];
+ return ff_texturedsp_compress_thread(avctx, &ctx->enc, slice, thread_nb);
+ } else {
+ /* unimplemented: YCoCg formats */
+ return AVERROR_INVALIDDATA;
+ }
+
+ return 0;
+}
+
+/* Converts an index offset value to a 2-bit opcode and pushes it to a stream.
+ * Inverse of CHECKPOINT in dxv.c. */
+#define PUSH_OP(x) \
+ do { \
+ if (state == 16) { \
+ if (bytestream2_get_bytes_left_p(pbc) < 4) { \
+ return AVERROR_INVALIDDATA; \
+ } \
+ value = (uint32_t*)pbc->buffer; \
+ bytestream2_put_le32(pbc, 0); \
+ state = 0; \
+ } \
+ if (idx >= 0x102 * x) { \
+ op = 3; \
+ bytestream2_put_le16(pbc, (idx / x) - 0x102); \
+ } else if (idx >= 2 * x) { \
+ op = 2; \
+ bytestream2_put_byte(pbc, (idx / x) - 2); \
+ } else if (idx == x) { \
+ op = 1; \
+ } else { \
+ op = 0; \
+ } \
+ *value |= (op << (state * 2)); \
+ state++; \
+ } while (0)
+
+static int dxv_compress_dxt1(AVCodecContext *avctx)
+{
+ DXVEncContext *ctx = avctx->priv_data;
+ PutByteContext *pbc = &ctx->pbc;
+ uint32_t *value;
+ uint32_t color, lut, idx, color_idx, lut_idx, prev_pos, state = 16, pos = 2, op = 0;
+
+ ht_init(ctx->color_lookback_ht);
+ ht_init(ctx->lut_lookback_ht);
+
+ bytestream2_put_le32(pbc, AV_RL32(ctx->tex_data));
+ bytestream2_put_le32(pbc, AV_RL32(ctx->tex_data + 4));
+
+ ht_lookup_and_upsert(ctx->color_lookback_ht, ctx->crc_ctx, AV_RL32(ctx->tex_data), 0);
+ ht_lookup_and_upsert(ctx->lut_lookback_ht, ctx->crc_ctx, AV_RL32(ctx->tex_data + 4), 1);
+
+ while (pos + 2 <= ctx->tex_size / 4) {
+ idx = 0;
+
+ color = AV_RL32(ctx->tex_data + pos * 4);
+ prev_pos = ht_lookup_and_upsert(ctx->color_lookback_ht, ctx->crc_ctx, color, pos);
+ color_idx = prev_pos != -1 ? pos - prev_pos : 0;
+ if (pos >= LOOKBACK_WORDS) {
+ uint32_t old_pos = pos - LOOKBACK_WORDS;
+ uint32_t old_color = AV_RL32(ctx->tex_data + old_pos * 4);
+ ht_delete(ctx->color_lookback_ht, ctx->crc_ctx, old_color, old_pos);
+ }
+ pos++;
+
+ lut = AV_RL32(ctx->tex_data + pos * 4);
+ if (color_idx && lut == AV_RL32(ctx->tex_data + (pos - color_idx) * 4)) {
+ idx = color_idx;
+ } else {
+ idx = 0;
+ prev_pos = ht_lookup_and_upsert(ctx->lut_lookback_ht, ctx->crc_ctx, lut, pos);
+ lut_idx = prev_pos != -1 ? pos - prev_pos : 0;
+ }
+ if (pos >= LOOKBACK_WORDS) {
+ uint32_t old_pos = pos - LOOKBACK_WORDS;
+ uint32_t old_lut = AV_RL32(ctx->tex_data + old_pos * 4);
+ ht_delete(ctx->lut_lookback_ht, ctx->crc_ctx, old_lut, old_pos);
+ }
+ pos++;
+
+ PUSH_OP(2);
+
+ if (!idx) {
+ idx = color_idx;
+ PUSH_OP(2);
+ if (!idx)
+ bytestream2_put_le32(pbc, color);
+
+ idx = lut_idx;
+ PUSH_OP(2);
+ if (!idx)
+ bytestream2_put_le32(pbc, lut);
+ }
+ }
+
+ return 0;
+}
+
+static int dxv_encode(AVCodecContext *avctx, AVPacket *pkt,
+ const AVFrame *frame, int *got_packet)
+{
+ DXVEncContext *ctx = avctx->priv_data;
+ PutByteContext *pbc = &ctx->pbc;
+ int ret;
+
+ /* unimplemented: needs to depend on compression ratio of tex format */
+ /* under DXT1, we need 3 words to encode load ops for 32 words.
+ * the first 2 words of the texture do not need load ops. */
+ ret = ff_alloc_packet(avctx, pkt, DXV_HEADER_LENGTH + ctx->tex_size + AV_CEIL_RSHIFT(ctx->tex_size - 8, 7) * 12);
+ if (ret < 0)
+ return ret;
+
+ avctx->execute2(avctx, compress_texture_thread, (void*)frame, NULL, ctx->enc.slice_count);
+
+ bytestream2_init_writer(pbc, pkt->data, pkt->size);
+
+ bytestream2_put_le32(pbc, ctx->tex_fmt);
+ bytestream2_put_byte(pbc, 4);
+ bytestream2_put_byte(pbc, 0);
+ bytestream2_put_byte(pbc, 0);
+ bytestream2_put_byte(pbc, 0);
+ /* Fill in compressed size later */
+ bytestream2_skip_p(pbc, 4);
+
+ ret = ctx->compress_tex(avctx);
+ if (ret < 0)
+ return ret;
+
+ AV_WL32(pkt->data + 8, bytestream2_tell_p(pbc) - DXV_HEADER_LENGTH);
+ av_shrink_packet(pkt, bytestream2_tell_p(pbc));
+
+ *got_packet = 1;
+ return 0;
+}
+
+static av_cold int dxv_init(AVCodecContext *avctx)
+{
+ DXVEncContext *ctx = avctx->priv_data;
+ int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
+
+ if (ret < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid image size %dx%d.\n",
+ avctx->width, avctx->height);
+ return ret;
+ }
+
+ ff_texturedspenc_init(&ctx->texdsp);
+
+ switch (ctx->tex_fmt) {
+ case DXV_FMT_DXT1:
+ ctx->compress_tex = dxv_compress_dxt1;
+ ctx->enc.tex_funct = ctx->texdsp.dxt1_block;
+ ctx->enc.tex_ratio = 8;
+ break;
+ default:
+ av_log(avctx, AV_LOG_ERROR, "Invalid format %08X\n", ctx->tex_fmt);
+ return AVERROR_INVALIDDATA;
+ }
+ ctx->enc.raw_ratio = 16;
+ ctx->tex_size = FFALIGN(avctx->width, 16) / TEXTURE_BLOCK_W *
+ FFALIGN(avctx->height, 16) / TEXTURE_BLOCK_H *
+ ctx->enc.tex_ratio;
+ ctx->enc.slice_count = av_clip(avctx->thread_count, 1, FFALIGN(avctx->height, 16) / TEXTURE_BLOCK_H);
+
+ ctx->tex_data = av_malloc(ctx->tex_size);
+ if (!ctx->tex_data) {
+ return AVERROR(ENOMEM);
+ }
+
+ ctx->crc_ctx = (AVCRC*)av_crc_get_table(AV_CRC_32_IEEE);
+ if (!ctx->crc_ctx) {
+ av_log(avctx, AV_LOG_ERROR, "Could not initialize CRC table.\n");
+ return AVERROR_BUG;
+ }
+
+ return 0;
+}
+
+static av_cold int dxv_close(AVCodecContext *avctx)
+{
+ DXVEncContext *ctx = avctx->priv_data;
+
+ av_freep(&ctx->tex_data);
+
+ return 0;
+}
+
+#define OFFSET(x) offsetof(DXVEncContext, x)
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+ { "format", NULL, OFFSET(tex_fmt), AV_OPT_TYPE_INT, { .i64 = DXV_FMT_DXT1 }, DXV_FMT_DXT1, DXV_FMT_DXT1, FLAGS, "format" },
+ { "dxt1", "DXT1 (Normal Quality, No Alpha)", 0, AV_OPT_TYPE_CONST, { .i64 = DXV_FMT_DXT1 }, 0, 0, FLAGS, "format" },
+ { NULL },
+};
+
+static const AVClass dxvenc_class = {
+ .class_name = "DXV encoder",
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+const FFCodec ff_dxv_encoder = {
+ .p.name = "dxv",
+ CODEC_LONG_NAME("Resolume DXV"),
+ .p.type = AVMEDIA_TYPE_VIDEO,
+ .p.id = AV_CODEC_ID_DXV,
+ .init = dxv_init,
+ FF_CODEC_ENCODE_CB(dxv_encode),
+ .close = dxv_close,
+ .priv_data_size = sizeof(DXVEncContext),
+ .p.capabilities = AV_CODEC_CAP_DR1 |
+ AV_CODEC_CAP_SLICE_THREADS |
+ AV_CODEC_CAP_FRAME_THREADS,
+ .p.priv_class = &dxvenc_class,
+ .p.pix_fmts = (const enum AVPixelFormat[]) {
+ AV_PIX_FMT_RGBA, AV_PIX_FMT_NONE,
+ },
+ .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
+};
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 376388c5bb..0fae3d06d3 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -29,7 +29,7 @@
#include "version_major.h"
-#define LIBAVCODEC_VERSION_MINOR 37
+#define LIBAVCODEC_VERSION_MINOR 38
#define LIBAVCODEC_VERSION_MICRO 100
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
--
2.43.0
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [FFmpeg-devel] [PATCH v2] lavc/dxvenc: add DXV encoder with support for DXT1 texture format
2024-01-19 17:07 ` [FFmpeg-devel] [PATCH v2] lavc/dxvenc: add " Connor Worley
@ 2024-01-19 19:16 ` Connor Worley
2024-01-23 20:32 ` Vittorio Giovara
0 siblings, 1 reply; 11+ messages in thread
From: Connor Worley @ 2024-01-19 19:16 UTC (permalink / raw)
To: ffmpeg-devel
I've tested the latest patch with both the lavc decoder and Resolume's proprietary software, and the encoded outputs are working for me.
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [FFmpeg-devel] [PATCH v2] lavc/dxvenc: add DXV encoder with support for DXT1 texture format
2024-01-19 19:16 ` Connor Worley
@ 2024-01-23 20:32 ` Vittorio Giovara
0 siblings, 0 replies; 11+ messages in thread
From: Vittorio Giovara @ 2024-01-23 20:32 UTC (permalink / raw)
To: FFmpeg development discussions and patches
On Fri, Jan 19, 2024 at 8:17 PM Connor Worley <connorbworley@gmail.com>
wrote:
> I've tested the latest patch with both the lavc decoder and Resolume's
> proprietary software, and the encoded outputs are working for me.
>
Pushed, thank you
--
Vittorio
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2024-01-23 20:32 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-01-17 8:27 [FFmpeg-devel] [PATCH] Add DXV encoder with support for DXT1 texture format Connor Worley
2024-01-17 13:12 ` Michael Niedermayer
2024-01-17 16:55 ` Connor Worley
2024-01-17 20:45 ` Connor Worley
2024-01-17 20:57 ` Connor Worley
2024-01-19 16:23 ` Vittorio Giovara
2024-01-19 16:55 ` Connor Worley
2024-01-19 16:58 ` Vittorio Giovara
2024-01-19 17:07 ` [FFmpeg-devel] [PATCH v2] lavc/dxvenc: add " Connor Worley
2024-01-19 19:16 ` Connor Worley
2024-01-23 20:32 ` Vittorio Giovara
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git