summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVittorio Giovara <vittorio.giovara@gmail.com>2015-08-26 11:31:41 +0200
committerVittorio Giovara <vittorio.giovara@gmail.com>2015-09-02 22:22:02 +0200
commitc45fcf30cfab687004ed1cdc06ebaa21f4262a0b (patch)
treeb7ba394385830ffc6cb4fe9ad070dadb4f35699f
parent0c7707610f46a28b65eff935500c78c9781f8f4f (diff)
DXV decoder
Support all DXDI and DXD3 normal quality videos.
-rw-r--r--Changelog1
-rwxr-xr-xconfigure1
-rw-r--r--doc/general.texi1
-rw-r--r--libavcodec/Makefile1
-rw-r--r--libavcodec/allcodecs.c1
-rw-r--r--libavcodec/avcodec.h1
-rw-r--r--libavcodec/dxv.c461
-rw-r--r--libavcodec/version.h2
-rw-r--r--libavformat/isom.c3
-rw-r--r--tests/fate/video.mak15
-rw-r--r--tests/ref/fate/dxv-dxt12
-rw-r--r--tests/ref/fate/dxv-dxt52
-rw-r--r--tests/ref/fate/dxv3-dxt12
-rw-r--r--tests/ref/fate/dxv3-dxt52
14 files changed, 494 insertions, 1 deletions
diff --git a/Changelog b/Changelog
index 20944c26fe..461c5b1204 100644
--- a/Changelog
+++ b/Changelog
@@ -42,6 +42,7 @@ version <next>:
- bitstream filter for converting HEVC from MP4 to Annex B
- Intel QSV-accelerated MPEG-2 video and HEVC decoding
- Support DNx100 (1440x1080@8)
+- DXV decoding
version 11:
diff --git a/configure b/configure
index 9e83285f2c..852b931115 100755
--- a/configure
+++ b/configure
@@ -1841,6 +1841,7 @@ dnxhd_encoder_select="aandcttables blockdsp fdctdsp idctdsp mpegvideoenc pixbloc
dvvideo_decoder_select="dvprofile idctdsp"
dvvideo_encoder_select="dvprofile fdctdsp me_cmp pixblockdsp"
dxa_decoder_deps="zlib"
+dxv_decoder_select="lzf texturedsp"
eac3_decoder_select="ac3_decoder"
eac3_encoder_select="ac3_encoder"
eamad_decoder_select="aandcttables blockdsp bswapdsp idctdsp mpegvideo"
diff --git a/doc/general.texi b/doc/general.texi
index a6ee1f71d9..7cd76933a0 100644
--- a/doc/general.texi
+++ b/doc/general.texi
@@ -399,6 +399,7 @@ library:
@item RealMedia @tab X @tab X
@item Redirector @tab @tab X
@item Renderware TeXture Dictionary @tab @tab X
+@item Resolume DXV @tab @tab X
@item RL2 @tab @tab X
@tab Audio and video format used in some games by Entertainment Software Partners.
@item RPL/ARMovie @tab @tab X
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 534fe4dfb0..7b105ccdfa 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -196,6 +196,7 @@ OBJS-$(CONFIG_DVVIDEO_DECODER) += dvdec.o dv.o dvdata.o
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_EAC3_DECODER) += eac3dec.o 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 69790d6c77..49f5a70f4c 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -142,6 +142,7 @@ void avcodec_register_all(void)
REGISTER_ENCDEC (DVVIDEO, dvvideo);
REGISTER_DECODER(DXA, dxa);
REGISTER_DECODER(DXTORY, dxtory);
+ REGISTER_DECODER(DXV, dxv);
REGISTER_DECODER(EACMV, eacmv);
REGISTER_DECODER(EAMAD, eamad);
REGISTER_DECODER(EATGQ, eatgq);
diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 379311f29b..9b386551c9 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -298,6 +298,7 @@ enum AVCodecID {
AV_CODEC_ID_HQ_HQA,
AV_CODEC_ID_HAP,
AV_CODEC_ID_DDS,
+ AV_CODEC_ID_DXV,
/* various PCM "codecs" */
AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs
diff --git a/libavcodec/dxv.c b/libavcodec/dxv.c
new file mode 100644
index 0000000000..dc11f38def
--- /dev/null
+++ b/libavcodec/dxv.c
@@ -0,0 +1,461 @@
+/*
+ * Resolume DXV decoder
+ * Copyright (C) 2015 Vittorio Giovara <vittorio.giovara@gmail.com>
+ *
+ * This file is part of Libav.
+ *
+ * Libav is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * Libav is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdint.h>
+
+#include "libavutil/imgutils.h"
+
+#include "avcodec.h"
+#include "bytestream.h"
+#include "internal.h"
+#include "lzf.h"
+#include "texturedsp.h"
+#include "thread.h"
+
+typedef struct DXVContext {
+ TextureDSPContext texdsp;
+ GetByteContext gbc;
+
+ uint8_t *tex_data; // Compressed texture
+ int tex_rat; // Compression ratio
+ int tex_step; // Distance between blocks
+ int64_t tex_size; // Texture size
+
+ /* Optimal number of slices for parallel decoding */
+ int slice_count;
+
+ /* Pointer to the selected decompression function */
+ int (*tex_funct)(uint8_t *dst, ptrdiff_t stride, const uint8_t *block);
+} DXVContext;
+
+static int decompress_texture_thread(AVCodecContext *avctx, void *arg,
+ int slice, int thread_nb)
+{
+ DXVContext *ctx = avctx->priv_data;
+ AVFrame *frame = arg;
+ const uint8_t *d = ctx->tex_data;
+ int w_block = avctx->coded_width / TEXTURE_BLOCK_W;
+ int h_block = avctx->coded_height / TEXTURE_BLOCK_H;
+ int x, y;
+ int start_slice, end_slice;
+ int base_blocks_per_slice = h_block / ctx->slice_count;
+ int remainder_blocks = h_block % ctx->slice_count;
+
+ /* When the frame height (in blocks) doesn't divide evenly between the
+ * number of slices, spread the remaining blocks evenly between the first
+ * operations */
+ start_slice = slice * base_blocks_per_slice;
+ /* Add any extra blocks (one per slice) that have been added
+ * before this slice */
+ start_slice += FFMIN(slice, remainder_blocks);
+
+ end_slice = start_slice + base_blocks_per_slice;
+ /* Add an extra block if there are remainder blocks to be accounted for */
+ if (slice < remainder_blocks)
+ end_slice++;
+
+ for (y = start_slice; y < end_slice; y++) {
+ uint8_t *p = frame->data[0] + y * frame->linesize[0] * TEXTURE_BLOCK_H;
+ int off = y * w_block;
+ for (x = 0; x < w_block; x++) {
+ ctx->tex_funct(p + x * 16, frame->linesize[0],
+ d + (off + x) * ctx->tex_step);
+ }
+ }
+
+ return 0;
+}
+
+/* This scheme addresses already decoded elements depending on 2-bit status:
+ * 0 -> copy new element
+ * 1 -> copy one element from position -x
+ * 2 -> copy one element from position -(get_byte() + 2) * x
+ * 3 -> copy one element from position -(get_16le() + 0x102) * x
+ * x is always 2 for dxt1 and 4 for dxt5. */
+#define CHECKPOINT(x) \
+ do { \
+ if (state == 0) { \
+ value = bytestream2_get_le32(gbc); \
+ state = 16; \
+ } \
+ op = value & 0x3; \
+ value >>= 2; \
+ state--; \
+ switch (op) { \
+ case 1: \
+ idx = x; \
+ break; \
+ case 2: \
+ idx = (bytestream2_get_byte(gbc) + 2) * x; \
+ break; \
+ case 3: \
+ idx = (bytestream2_get_le16(gbc) + 0x102) * x; \
+ break; \
+ } \
+ } while(0)
+
+static int dxv_decompress_dxt1(AVCodecContext *avctx)
+{
+ DXVContext *ctx = avctx->priv_data;
+ GetByteContext *gbc = &ctx->gbc;
+ uint32_t value, prev, op;
+ int idx = 0, state = 0;
+ int pos = 2;
+
+ /* Copy the first two elements */
+ AV_WL32(ctx->tex_data, bytestream2_get_le32(gbc));
+ AV_WL32(ctx->tex_data + 4, bytestream2_get_le32(gbc));
+
+ /* Process input until the whole texture has been filled */
+ while (pos < ctx->tex_size / 4) {
+ CHECKPOINT(2);
+
+ /* Copy two elements from a previous offset or from the input buffer */
+ if (op) {
+ prev = AV_RL32(ctx->tex_data + 4 * (pos - idx));
+ AV_WL32(ctx->tex_data + 4 * pos, prev);
+ pos++;
+
+ prev = AV_RL32(ctx->tex_data + 4 * (pos - idx));
+ AV_WL32(ctx->tex_data + 4 * pos, prev);
+ pos++;
+ } else {
+ CHECKPOINT(2);
+
+ if (op)
+ prev = AV_RL32(ctx->tex_data + 4 * (pos - idx));
+ else
+ prev = bytestream2_get_le32(gbc);
+ AV_WL32(ctx->tex_data + 4 * pos, prev);
+ pos++;
+
+ CHECKPOINT(2);
+
+ if (op)
+ prev = AV_RL32(ctx->tex_data + 4 * (pos - idx));
+ else
+ prev = bytestream2_get_le32(gbc);
+ AV_WL32(ctx->tex_data + 4 * pos, prev);
+ pos++;
+ }
+ }
+
+ return 0;
+}
+
+static int dxv_decompress_dxt5(AVCodecContext *avctx)
+{
+ DXVContext *ctx = avctx->priv_data;
+ GetByteContext *gbc = &ctx->gbc;
+ uint32_t value, op;
+ int idx, prev, state = 0;
+ int pos = 4;
+ int run = 0;
+ int probe, check;
+
+ /* Copy the first four elements */
+ AV_WL32(ctx->tex_data + 0, bytestream2_get_le32(gbc));
+ AV_WL32(ctx->tex_data + 4, bytestream2_get_le32(gbc));
+ AV_WL32(ctx->tex_data + 8, bytestream2_get_le32(gbc));
+ AV_WL32(ctx->tex_data + 12, bytestream2_get_le32(gbc));
+
+ /* Process input until the whole texture has been filled */
+ while (pos < ctx->tex_size / 4) {
+ if (run) {
+ run--;
+
+ prev = AV_RL32(ctx->tex_data + 4 * (pos - 4));
+ AV_WL32(ctx->tex_data + 4 * pos, prev);
+ pos++;
+ prev = AV_RL32(ctx->tex_data + 4 * (pos - 4));
+ AV_WL32(ctx->tex_data + 4 * pos, prev);
+ pos++;
+ } else {
+ if (state == 0) {
+ value = bytestream2_get_le32(gbc);
+ state = 16;
+ }
+ op = value & 0x3;
+ value >>= 2;
+ state--;
+
+ switch (op) {
+ case 0:
+ /* Long copy */
+ check = bytestream2_get_byte(gbc) + 1;
+ if (check == 256) {
+ do {
+ probe = bytestream2_get_le16(gbc);
+ check += probe;
+ } while (probe == 0xFFFF);
+ }
+ while (check && pos < ctx->tex_size / 4) {
+ prev = AV_RL32(ctx->tex_data + 4 * (pos - 4));
+ AV_WL32(ctx->tex_data + 4 * pos, prev);
+ pos++;
+
+ prev = AV_RL32(ctx->tex_data + 4 * (pos - 4));
+ AV_WL32(ctx->tex_data + 4 * pos, prev);
+ pos++;
+
+ prev = AV_RL32(ctx->tex_data + 4 * (pos - 4));
+ AV_WL32(ctx->tex_data + 4 * pos, prev);
+ pos++;
+
+ prev = AV_RL32(ctx->tex_data + 4 * (pos - 4));
+ AV_WL32(ctx->tex_data + 4 * pos, prev);
+ pos++;
+
+ check--;
+ }
+
+ /* Restart (or exit) the loop */
+ continue;
+ break;
+ case 1:
+ /* Load new run value */
+ run = bytestream2_get_byte(gbc);
+ if (run == 255) {
+ do {
+ probe = bytestream2_get_le16(gbc);
+ run += probe;
+ } while (probe == 0xFFFF);
+ }
+
+ /* Copy two dwords from previous data */
+ prev = AV_RL32(ctx->tex_data + 4 * (pos - 4));
+ AV_WL32(ctx->tex_data + 4 * pos, prev);
+ pos++;
+
+ prev = AV_RL32(ctx->tex_data + 4 * (pos - 4));
+ AV_WL32(ctx->tex_data + 4 * pos, prev);
+ pos++;
+ break;
+ case 2:
+ /* Copy two dwords from a previous index */
+ idx = 8 + bytestream2_get_le16(gbc);
+ prev = AV_RL32(ctx->tex_data + 4 * (pos - idx));
+ AV_WL32(ctx->tex_data + 4 * pos, prev);
+ pos++;
+
+ prev = AV_RL32(ctx->tex_data + 4 * (pos - idx));
+ AV_WL32(ctx->tex_data + 4 * pos, prev);
+ pos++;
+ break;
+ case 3:
+ /* Copy two dwords from input */
+ prev = bytestream2_get_le32(gbc);
+ AV_WL32(ctx->tex_data + 4 * pos, prev);
+ pos++;
+
+ prev = bytestream2_get_le32(gbc);
+ AV_WL32(ctx->tex_data + 4 * pos, prev);
+ pos++;
+ break;
+ }
+ }
+
+ CHECKPOINT(4);
+
+ /* Copy two elements from a previous offset or from the input buffer */
+ if (op) {
+ prev = AV_RL32(ctx->tex_data + 4 * (pos - idx));
+ AV_WL32(ctx->tex_data + 4 * pos, prev);
+ pos++;
+
+ prev = AV_RL32(ctx->tex_data + 4 * (pos - idx));
+ AV_WL32(ctx->tex_data + 4 * pos, prev);
+ pos++;
+ } else {
+ CHECKPOINT(4);
+
+ if (op)
+ prev = AV_RL32(ctx->tex_data + 4 * (pos - idx));
+ else
+ prev = bytestream2_get_le32(gbc);
+ AV_WL32(ctx->tex_data + 4 * pos, prev);
+ pos++;
+
+ CHECKPOINT(4);
+
+ if (op)
+ prev = AV_RL32(ctx->tex_data + 4 * (pos - idx));
+ else
+ prev = bytestream2_get_le32(gbc);
+ AV_WL32(ctx->tex_data + 4 * pos, prev);
+ pos++;
+ }
+ }
+
+ return 0;
+}
+
+static int dxv_decompress_lzf(AVCodecContext *avctx)
+{
+ DXVContext *ctx = avctx->priv_data;
+ return ff_lzf_uncompress(&ctx->gbc, &ctx->tex_data, &ctx->tex_size);
+}
+
+static int dxv_decode(AVCodecContext *avctx, void *data,
+ int *got_frame, AVPacket *avpkt)
+{
+ DXVContext *ctx = avctx->priv_data;
+ ThreadFrame tframe;
+ GetByteContext *gbc = &ctx->gbc;
+ int (*decompress_tex)(AVCodecContext *avctx);
+ uint32_t tag;
+ int channels, size = 0, old_type = 0;
+ int ret;
+
+ bytestream2_init(gbc, avpkt->data, avpkt->size);
+
+ tag = bytestream2_get_le32(gbc);
+ switch (tag) {
+ case MKBETAG('D', 'X', 'T', '1'):
+ decompress_tex = dxv_decompress_dxt1;
+ ctx->tex_funct = ctx->texdsp.dxt1_block;
+ ctx->tex_rat = 8;
+ ctx->tex_step = 8;
+ av_log(avctx, AV_LOG_DEBUG, "DXTR1 compression and DXT1 texture ");
+ break;
+ case MKBETAG('D', 'X', 'T', '5'):
+ decompress_tex = dxv_decompress_dxt5;
+ ctx->tex_funct = ctx->texdsp.dxt5_block;
+ ctx->tex_rat = 4;
+ ctx->tex_step = 16;
+ av_log(avctx, AV_LOG_DEBUG, "DXTR5 compression and DXT5 texture ");
+ break;
+ case MKBETAG('Y', 'C', 'G', '6'):
+ case MKBETAG('Y', 'G', '1', '0'):
+ avpriv_report_missing_feature(avctx, "Tag 0x%08X", tag);
+ return AVERROR_PATCHWELCOME;
+ default:
+ /* Old version does not have a real header, just size and type. */
+ size = tag & 0x00FFFFFF;
+ old_type = tag >> 24;
+ channels = old_type & 0x0F;
+ if (old_type & 0x40) {
+ av_log(avctx, AV_LOG_DEBUG, "LZF compression and DXT5 texture ");
+ ctx->tex_funct = ctx->texdsp.dxt5_block;
+ ctx->tex_step = 16;
+ } else if (old_type & 0x20) {
+ av_log(avctx, AV_LOG_DEBUG, "LZF compression and DXT1 texture ");
+ ctx->tex_funct = ctx->texdsp.dxt1_block;
+ ctx->tex_step = 8;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Unsupported header (0x%08X)\n.", tag);
+ return AVERROR_INVALIDDATA;
+ }
+ decompress_tex = dxv_decompress_lzf;
+ ctx->tex_rat = 1;
+ break;
+ }
+
+ /* New header is 12 bytes long. */
+ if (!old_type) {
+ channels = bytestream2_get_byte(gbc);
+ bytestream2_skip(gbc, 3); // unknown
+ size = bytestream2_get_le32(gbc);
+ }
+ av_log(avctx, AV_LOG_DEBUG, "(%d channels)\n", channels);
+
+ if (size != bytestream2_get_bytes_left(gbc)) {
+ av_log(avctx, AV_LOG_ERROR, "Incomplete or invalid file (%u > %u)\n.",
+ size, bytestream2_get_bytes_left(gbc));
+ return AVERROR_INVALIDDATA;
+ }
+
+ ctx->tex_size = avctx->coded_width * avctx->coded_height * 4 / ctx->tex_rat;
+ ret = av_reallocp(&ctx->tex_data, ctx->tex_size);
+ if (ret < 0)
+ return ret;
+
+ /* Decompress texture out of the intermediate compression. */
+ ret = decompress_tex(avctx);
+ if (ret < 0)
+ return ret;
+
+ tframe.f = data;
+ ret = ff_thread_get_buffer(avctx, &tframe, 0);
+ if (ret < 0)
+ return ret;
+ ff_thread_finish_setup(avctx);
+
+ /* Now decompress the texture with the standard functions. */
+ avctx->execute2(avctx, decompress_texture_thread,
+ tframe.f, NULL, ctx->slice_count);
+
+ /* Frame is ready to be output. */
+ tframe.f->pict_type = AV_PICTURE_TYPE_I;
+ tframe.f->key_frame = 1;
+ *got_frame = 1;
+
+ return avpkt->size;
+}
+
+static int dxv_init(AVCodecContext *avctx)
+{
+ DXVContext *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;
+ }
+
+ /* Codec requires 16x16 alignment. */
+ avctx->coded_width = FFALIGN(avctx->width, 16);
+ avctx->coded_height = FFALIGN(avctx->height, 16);
+
+ ff_texturedsp_init(&ctx->texdsp);
+ avctx->pix_fmt = AV_PIX_FMT_RGBA;
+
+ ctx->slice_count = av_clip(avctx->thread_count, 1,
+ avctx->coded_height / TEXTURE_BLOCK_H);
+
+ return 0;
+}
+
+static int dxv_close(AVCodecContext *avctx)
+{
+ DXVContext *ctx = avctx->priv_data;
+
+ av_freep(&ctx->tex_data);
+
+ return 0;
+}
+
+AVCodec ff_dxv_decoder = {
+ .name = "dxv",
+ .long_name = NULL_IF_CONFIG_SMALL("Resolume DXV"),
+ .type = AVMEDIA_TYPE_VIDEO,
+ .id = AV_CODEC_ID_DXV,
+ .init = dxv_init,
+ .decode = dxv_decode,
+ .close = dxv_close,
+ .priv_data_size = sizeof(DXVContext),
+ .capabilities = AV_CODEC_CAP_DR1 |
+ AV_CODEC_CAP_SLICE_THREADS |
+ AV_CODEC_CAP_FRAME_THREADS,
+ .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE |
+ FF_CODEC_CAP_INIT_CLEANUP,
+};
diff --git a/libavcodec/version.h b/libavcodec/version.h
index 8dfa73558c..eefc3a93d1 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -29,7 +29,7 @@
#include "libavutil/version.h"
#define LIBAVCODEC_VERSION_MAJOR 57
-#define LIBAVCODEC_VERSION_MINOR 0
+#define LIBAVCODEC_VERSION_MINOR 1
#define LIBAVCODEC_VERSION_MICRO 0
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
diff --git a/libavformat/isom.c b/libavformat/isom.c
index 0582cfdac4..d421a1a4bd 100644
--- a/libavformat/isom.c
+++ b/libavformat/isom.c
@@ -254,6 +254,9 @@ const AVCodecTag ff_codec_movvideo_tags[] = {
{ AV_CODEC_ID_HAP, MKTAG('H', 'a', 'p', '5') },
{ AV_CODEC_ID_HAP, MKTAG('H', 'a', 'p', 'Y') },
+ { AV_CODEC_ID_DXV, MKTAG('D', 'X', 'D', '3') },
+ { AV_CODEC_ID_DXV, MKTAG('D', 'X', 'D', 'I') },
+
{ AV_CODEC_ID_NONE, 0 },
};
diff --git a/tests/fate/video.mak b/tests/fate/video.mak
index 77cc9e7c6a..ef1d41da2f 100644
--- a/tests/fate/video.mak
+++ b/tests/fate/video.mak
@@ -136,6 +136,21 @@ fate-dxa-scummvm: CMD = framecrc -i $(TARGET_SAMPLES)/dxa/scummvm.dxa -pix_fmt r
FATE_SAMPLES_AVCONV-$(call DEMDEC, DXA, DXA) += $(FATE_DXA)
fate-dxa: $(FATE_DXA)
+FATE_DXV += fate-dxv-dxt1
+fate-dxv-dxt1: CMD = framecrc -i $(TARGET_SAMPLES)/dxv/dxv-na.mov
+
+FATE_DXV += fate-dxv-dxt5
+fate-dxv-dxt5: CMD = framecrc -i $(TARGET_SAMPLES)/dxv/dxv-wa.mov
+
+FATE_DXV += fate-dxv3-dxt1
+fate-dxv3-dxt1: CMD = framecrc -i $(TARGET_SAMPLES)/dxv/dxv3-nqna.mov
+
+FATE_DXV += fate-dxv3-dxt5
+fate-dxv3-dxt5: CMD = framecrc -i $(TARGET_SAMPLES)/dxv/dxv3-nqwa.mov
+
+FATE_SAMPLES_AVCONV-$(call DEMDEC, MOV, DXV) += $(FATE_DXV)
+fate-dxv: $(FATE_DXV)
+
FATE_SAMPLES_AVCONV-$(call DEMDEC, SEGAFILM, CINEPAK) += fate-film-cvid
fate-film-cvid: CMD = framecrc -i $(TARGET_SAMPLES)/film/logo-capcom.cpk -an
diff --git a/tests/ref/fate/dxv-dxt1 b/tests/ref/fate/dxv-dxt1
new file mode 100644
index 0000000000..9b493807ca
--- /dev/null
+++ b/tests/ref/fate/dxv-dxt1
@@ -0,0 +1,2 @@
+#tb 0: 1/30000
+0, 0, 0, 0, 8294400, 0x0797cd53
diff --git a/tests/ref/fate/dxv-dxt5 b/tests/ref/fate/dxv-dxt5
new file mode 100644
index 0000000000..9b493807ca
--- /dev/null
+++ b/tests/ref/fate/dxv-dxt5
@@ -0,0 +1,2 @@
+#tb 0: 1/30000
+0, 0, 0, 0, 8294400, 0x0797cd53
diff --git a/tests/ref/fate/dxv3-dxt1 b/tests/ref/fate/dxv3-dxt1
new file mode 100644
index 0000000000..c65ead9124
--- /dev/null
+++ b/tests/ref/fate/dxv3-dxt1
@@ -0,0 +1,2 @@
+#tb 0: 1/30000
+0, 0, 0, 0, 8294400, 0x98bbcc85
diff --git a/tests/ref/fate/dxv3-dxt5 b/tests/ref/fate/dxv3-dxt5
new file mode 100644
index 0000000000..9b493807ca
--- /dev/null
+++ b/tests/ref/fate/dxv3-dxt5
@@ -0,0 +1,2 @@
+#tb 0: 1/30000
+0, 0, 0, 0, 8294400, 0x0797cd53