/* * Resolume DXV decoder * Copyright (C) 2015 Vittorio Giovara * Copyright (C) 2018 Paul B Mahol * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "libavutil/imgutils.h" #include "libavutil/mem.h" #include "avcodec.h" #include "bytestream.h" #include "codec_internal.h" #include "dxv.h" #include "lzf.h" #include "texturedsp.h" #include "thread.h" typedef struct DXVContext { TextureDSPContext texdsp; GetByteContext gbc; uint8_t *tex_data; // Compressed texture uint8_t *ctex_data; // Compressed chroma texture int64_t tex_size; // Texture size int64_t ctex_size; // Chroma texture size uint8_t *op_data[4]; // Opcodes int64_t op_size[4]; // Opcodes size } DXVContext; /* 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) { \ if (bytestream2_get_bytes_left(gbc) < 4) \ return AVERROR_INVALIDDATA; \ 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; \ if (idx > pos) { \ av_log(avctx, AV_LOG_ERROR, "idx %d > %d\n", idx, pos); \ return AVERROR_INVALIDDATA; \ } \ break; \ case 3: \ idx = (bytestream2_get_le16(gbc) + 0x102) * x; \ if (idx > pos) { \ av_log(avctx, AV_LOG_ERROR, "idx %d > %d\n", idx, pos); \ return AVERROR_INVALIDDATA; \ } \ 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 + 2 <= 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; } typedef struct OpcodeTable { int16_t next; uint8_t val1; uint8_t val2; } OpcodeTable; static int fill_ltable(GetByteContext *gb, uint32_t *table, int *nb_elements) { unsigned half = 512, bits = 1023, left = 1024, input, mask; int value, counter = 0, rshift = 10, lshift = 30; mask = bytestream2_get_le32(gb) >> 2; while (left) { if (counter >= 256) return AVERROR_INVALIDDATA; value = bits & mask; left -= bits & mask; mask >>= rshift; lshift -= rshift; table[counter++] = value; if (lshift < 16) { if (bytestream2_get_bytes_left(gb) <= 0) return AVERROR_INVALIDDATA; input = bytestream2_get_le16(gb); mask += input << lshift; lshift += 16; } if (left < half) { half >>= 1; bits >>= 1; rshift--; } } for (; !table[counter - 1]; counter--) if (counter <= 0) return AVERROR_INVALIDDATA; *nb_elements = counter; if (counter < 256) memset(&table[counter], 0, 4 * (256 - counter)); if (lshift >= 16) bytestream2_seek(gb, -2, SEEK_CUR); return 0; } static int fill_optable(unsigned *table0, OpcodeTable *table1, int nb_elements) { unsigned table2[256] = { 0 }; unsigned x = 0; int val0, val1, i, j = 2, k = 0; table2[0] = table0[0]; for (i = 0; i < nb_elements - 1; i++, table2[i] = val0) { val0 = table0[i + 1] + table2[i]; } if (!table2[0]) { do { k++; } while (!table2[k]); } j = 2; for (i = 1024; i > 0; i--) { for (table1[x].val1 = k; k < 256 && j > table2[k]; k++); x = (x - 383) & 0x3FF; j++; } if (nb_elements > 0) memcpy(&table2[0], table0, 4 * nb_elements); for (i = 0; i < 1024; i++) { val0 = table1[i].val1; val1 = table2[val0]; table2[val0]++; x = 31 - ff_clz(val1); if (x > 10) return AVERROR_INVALIDDATA; table1[i].val2 = 10 - x; table1[i].next = (val1 << table1[i].val2) - 1024; } return 0; } static int get_opcodes(GetByteContext *gb, uint32_t *table, uint8_t *dst, int op_size, int nb_elements) { OpcodeTable optable[1024]; int sum, x, val, lshift, rshift, ret, i, idx; int64_t size_in_bits; unsigned endoffset, newoffset, offset; unsigned next; const uint8_t *src = gb->buffer; ret = fill_optable(table, optable, nb_elements); if (ret < 0) return ret; size_in_bits = bytestream2_get_le32(gb); endoffset = ((size_in_bits + 7) >> 3) - 4; if (endoffset <= 0 || bytestream2_get_bytes_left(gb) < endoffset) return AVERROR_INVALIDDATA; offset = endoffset; next = AV_RL32(src + endoffset); rshift = (((size_in_bits & 0xFF) - 1) & 7) + 15; lshift = 32 - rshift; idx = (next >> rshift) & 0x3FF; for (i = 0; i < op_size; i++) { dst[i] = optable[idx].val1; val = optable[idx].val2; sum = val + lshift; x = (next << lshift) >> 1 >> (31 - val); newoffset = offset - (sum >> 3); lshift = sum & 7; idx = x + optable[idx].next; offset = newoffset; if (offset > endoffset) return AVERROR_INVALIDDATA; next = AV_RL32(src + offset); } bytestream2_skip(gb, (size_in_bits + 7 >> 3) - 4); return 0; } static int dxv_decompress_opcodes(GetByteContext *gb, void *dstp, size_t op_size) { int pos = bytestream2_tell(gb); int flag = bytestream2_peek_byte(gb); if ((flag & 3) == 0) { bytestream2_skip(gb, 1); bytestream2_get_buffer(gb, dstp, op_size); } else if ((flag & 3) == 1) { bytestream2_skip(gb, 1); memset(dstp, bytestream2_get_byte(gb), op_size); } else { uint32_t table[256]; int ret, elements = 0; ret = fill_ltable(gb, table, &elements); if (ret < 0) return ret; ret = get_opcodes(gb, table, dstp, op_size, elements); if (ret < 0) return ret; } return bytestream2_tell(gb) - pos; } static int dxv_decompress_cgo(DXVContext *ctx, GetByteContext *gb, uint8_t *tex_data, int tex_size, uint8_t *op_data, int *oindex, int op_size, uint8_t **dstp, int *statep, uint8_t **tab0, uint8_t **tab1, int offset) { uint8_t *dst = *dstp; uint8_t *tptr0, *tptr1, *tptr3; int oi = *oindex; int state = *statep; int opcode, v, vv; if (state <= 0) { if (oi >= op_size) return AVERROR_INVALIDDATA; opcode = op_data[oi++]; if (!opcode) { v = bytestream2_get_byte(gb); if (v == 255) { do { if (bytestream2_get_bytes_left(gb) <= 0) return AVERROR_INVALIDDATA; opcode = bytestream2_get_le16(gb); v += opcode; } while (opcode == 0xFFFF); } AV_WL32(dst, AV_RL32(dst - (8 + offset))); AV_WL32(dst + 4, AV_RL32(dst - (4 + offset))); state = v + 4; goto done; } switch (opcode) { case 1: AV_WL32(dst, AV_RL32(dst - (8 + offset))); AV_WL32(dst + 4, AV_RL32(dst - (4 + offset))); break; case 2: vv = (8 + offset) * (bytestream2_get_le16(gb) + 1); if (vv < 0 || vv > dst - tex_data) return AVERROR_INVALIDDATA; tptr0 = dst - vv; v = AV_RL32(tptr0); AV_WL32(dst, AV_RL32(tptr0)); AV_WL32(dst + 4, AV_RL32(tptr0 + 4)); tab0[0x9E3779B1 * (uint16_t)v >> 24] = dst; tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 3: AV_WL32(dst, bytestream2_get_le32(gb)); AV_WL32(dst + 4, bytestream2_get_le32(gb)); tab0[0x9E3779B1 * AV_RL16(dst) >> 24] = dst; tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 4: tptr3 = tab1[bytestream2_get_byte(gb)]; if (!tptr3) return AVERROR_INVALIDDATA; AV_WL16(dst, bytestream2_get_le16(gb)); AV_WL16(dst + 2, AV_RL16(tptr3)); dst[4] = tptr3[2]; AV_WL16(dst + 5, bytestream2_get_le16(gb)); dst[7] = bytestream2_get_byte(gb); tab0[0x9E3779B1 * AV_RL16(dst) >> 24] = dst; break; case 5: tptr3 = tab1[bytestream2_get_byte(gb)]; if (!tptr3) return AVERROR_INVALIDDATA; AV_WL16(dst, bytestream2_get_le16(gb)); AV_WL16(dst + 2, bytestream2_get_le16(gb)); dst[4] = bytestream2_get_byte(gb); AV_WL16(dst + 5, AV_RL16(tptr3)); dst[7] = tptr3[2]; tab0[0x9E3779B1 * AV_RL16(dst) >> 24] = dst; tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 6: tptr0 = tab1[bytestream2_get_byte(gb)]; if (!tptr0) return AVERROR_INVALIDDATA; tptr1 = tab1[bytestream2_get_byte(gb)]; if (!tptr1) return AVERROR_INVALIDDATA; AV_WL16(dst, bytestream2_get_le16(gb)); AV_WL16(dst + 2, AV_RL16(tptr0)); dst[4] = tptr0[2]; AV_WL16(dst + 5, AV_RL16(tptr1)); dst[7] = tptr1[2]; tab0[0x9E3779B1 * AV_RL16(dst) >> 24] = dst; break; case 7: v = (8 + offset) * (bytestream2_get_le16(gb) + 1); if (v < 0 || v > dst - tex_data) return AVERROR_INVALIDDATA; tptr0 = dst - v; AV_WL16(dst, bytestream2_get_le16(gb)); AV_WL16(dst + 2, AV_RL16(tptr0 + 2)); AV_WL32(dst + 4, AV_RL32(tptr0 + 4)); tab0[0x9E3779B1 * AV_RL16(dst) >> 24] = dst; tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 8: tptr1 = tab0[bytestream2_get_byte(gb)]; if (!tptr1) return AVERROR_INVALIDDATA; AV_WL16(dst, AV_RL16(tptr1)); AV_WL16(dst + 2, bytestream2_get_le16(gb)); AV_WL32(dst + 4, bytestream2_get_le32(gb)); tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 9: tptr1 = tab0[bytestream2_get_byte(gb)]; if (!tptr1) return AVERROR_INVALIDDATA; tptr3 = tab1[bytestream2_get_byte(gb)]; if (!tptr3) return AVERROR_INVALIDDATA; AV_WL16(dst, AV_RL16(tptr1)); AV_WL16(dst + 2, AV_RL16(tptr3)); dst[4] = tptr3[2]; AV_WL16(dst + 5, bytestream2_get_le16(gb)); dst[7] = bytestream2_get_byte(gb); tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 10: tptr1 = tab0[bytestream2_get_byte(gb)]; if (!tptr1) return AVERROR_INVALIDDATA; tptr3 = tab1[bytestream2_get_byte(gb)]; if (!tptr3) return AVERROR_INVALIDDATA; AV_WL16(dst, AV_RL16(tptr1)); AV_WL16(dst + 2, bytestream2_get_le16(gb)); dst[4] = bytestream2_get_byte(gb); AV_WL16(dst + 5, AV_RL16(tptr3)); dst[7] = tptr3[2]; tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 11: tptr0 = tab0[bytestream2_get_byte(gb)]; if (!tptr0) return AVERROR_INVALIDDATA; tptr3 = tab1[bytestream2_get_byte(gb)]; if (!tptr3) return AVERROR_INVALIDDATA; tptr1 = tab1[bytestream2_get_byte(gb)]; if (!tptr1) return AVERROR_INVALIDDATA; AV_WL16(dst, AV_RL16(tptr0)); AV_WL16(dst + 2, AV_RL16(tptr3)); dst[4] = tptr3[2]; AV_WL16(dst + 5, AV_RL16(tptr1)); dst[7] = tptr1[2]; break; case 12: tptr1 = tab0[bytestream2_get_byte(gb)]; if (!tptr1) return AVERROR_INVALIDDATA; v = (8 + offset) * (bytestream2_get_le16(gb) + 1); if (v < 0 || v > dst - tex_data) return AVERROR_INVALIDDATA; tptr0 = dst - v; AV_WL16(dst, AV_RL16(tptr1)); AV_WL16(dst + 2, AV_RL16(tptr0 + 2)); AV_WL32(dst + 4, AV_RL32(tptr0 + 4)); tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 13: AV_WL16(dst, AV_RL16(dst - (8 + offset))); AV_WL16(dst + 2, bytestream2_get_le16(gb)); AV_WL32(dst + 4, bytestream2_get_le32(gb)); tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 14: tptr3 = tab1[bytestream2_get_byte(gb)]; if (!tptr3) return AVERROR_INVALIDDATA; AV_WL16(dst, AV_RL16(dst - (8 + offset))); AV_WL16(dst + 2, AV_RL16(tptr3)); dst[4] = tptr3[2]; AV_WL16(dst + 5, bytestream2_get_le16(gb)); dst[7] = bytestream2_get_byte(gb); tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 15: tptr3 = tab1[bytestream2_get_byte(gb)]; if (!tptr3) return AVERROR_INVALIDDATA; AV_WL16(dst, AV_RL16(dst - (8 + offset))); AV_WL16(dst + 2, bytestream2_get_le16(gb)); dst[4] = bytestream2_get_byte(gb); AV_WL16(dst + 5, AV_RL16(tptr3)); dst[7] = tptr3[2]; tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; case 16: tptr3 = tab1[bytestream2_get_byte(gb)]; if (!tptr3) return AVERROR_INVALIDDATA; tptr1 = tab1[bytestream2_get_byte(gb)]; if (!tptr1) return AVERROR_INVALIDDATA; AV_WL16(dst, AV_RL16(dst - (8 + offset))); AV_WL16(dst + 2, AV_RL16(tptr3)); dst[4] = tptr3[2]; AV_WL16(dst + 5, AV_RL16(tptr1)); dst[7] = tptr1[2]; break; case 17: v = (8 + offset) * (bytestream2_get_le16(gb) + 1); if (v < 0 || v > dst - tex_data) return AVERROR_INVALIDDATA; AV_WL16(dst, AV_RL16(dst - (8 + offset))); AV_WL16(dst + 2, AV_RL16(&dst[-v + 2])); AV_WL32(dst + 4, AV_RL32(&dst[-v + 4])); tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFFu) >> 24] = dst + 2; break; default: break; } } else { done: AV_WL32(dst, AV_RL32(dst - (8 + offset))); AV_WL32(dst + 4, AV_RL32(dst - (4 + offset))); state--; } if (dst - tex_data + 8 > tex_size) return AVERROR_INVALIDDATA; dst += 8; *oindex = oi; *dstp = dst; *statep = state; return 0; } static int dxv_decompress_cocg(DXVContext *ctx, GetByteContext *gb, uint8_t *tex_data, int tex_size, uint8_t *op_data0, uint8_t *op_data1, int max_op_size0, int max_op_size1) { uint8_t *dst, *tab2[256] = { 0 }, *tab0[256] = { 0 }, *tab3[256] = { 0 }, *tab1[256] = { 0 }; int op_offset = bytestream2_get_le32(gb); unsigned op_size0 = bytestream2_get_le32(gb); unsigned op_size1 = bytestream2_get_le32(gb); int data_start = bytestream2_tell(gb); int skip0, skip1, oi0 = 0, oi1 = 0; int ret, state0 = 0, state1 = 0; if (op_offset < 12 || op_offset - 12 > bytestream2_get_bytes_left(gb)) return AVERROR_INVALIDDATA; dst = tex_data; bytestream2_skip(gb, op_offset - 12); if (op_size0 > max_op_size0) return AVERROR_INVALIDDATA; skip0 = dxv_decompress_opcodes(gb, op_data0, op_size0); if (skip0 < 0) return skip0; if (op_size1 > max_op_size1) return AVERROR_INVALIDDATA; skip1 = dxv_decompress_opcodes(gb, op_data1, op_size1); if (skip1 < 0) return skip1; bytestream2_seek(gb, data_start, SEEK_SET); AV_WL32(dst, bytestream2_get_le32(gb)); AV_WL32(dst + 4, bytestream2_get_le32(gb)); AV_WL32(dst + 8, bytestream2_get_le32(gb)); AV_WL32(dst + 12, bytestream2_get_le32(gb)); tab0[0x9E3779B1 * AV_RL16(dst) >> 24] = dst; tab1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFF) >> 24] = dst + 2; tab2[0x9E3779B1 * AV_RL16(dst + 8) >> 24] = dst + 8; tab3[0x9E3779B1 * (AV_RL32(dst + 10) & 0xFFFFFF) >> 24] = dst + 10; dst += 16; while (dst + 10 < tex_data + tex_size) { ret = dxv_decompress_cgo(ctx, gb, tex_data, tex_size, op_data0, &oi0, op_size0, &dst, &state0, tab0, tab1, 8); if (ret < 0) return ret; ret = dxv_decompress_cgo(ctx, gb, tex_data, tex_size, op_data1, &oi1, op_size1, &dst, &state1, tab2, tab3, 8); if (ret < 0) return ret; } bytestream2_seek(gb, data_start - 12 + op_offset + skip0 + skip1, SEEK_SET); return 0; } static int dxv_decompress_yo(DXVContext *ctx, GetByteContext *gb, uint8_t *tex_data, int tex_size, uint8_t *op_data, int max_op_size) { int op_offset = bytestream2_get_le32(gb); unsigned op_size = bytestream2_get_le32(gb); int data_start = bytestream2_tell(gb); uint8_t *dst, *table0[256] = { 0 }, *table1[256] = { 0 }; int ret, state = 0, skip, oi = 0, v, vv; if (op_offset < 8 || op_offset - 8 > bytestream2_get_bytes_left(gb)) return AVERROR_INVALIDDATA; dst = tex_data; bytestream2_skip(gb, op_offset - 8); if (op_size > max_op_size) return AVERROR_INVALIDDATA; skip = dxv_decompress_opcodes(gb, op_data, op_size); if (skip < 0) return skip; bytestream2_seek(gb, data_start, SEEK_SET); v = bytestream2_get_le32(gb); AV_WL32(dst, v); vv = bytestream2_get_le32(gb); table0[0x9E3779B1 * (uint16_t)v >> 24] = dst; AV_WL32(dst + 4, vv); table1[0x9E3779B1 * (AV_RL32(dst + 2) & 0xFFFFFF) >> 24] = dst + 2; dst += 8; while (dst < tex_data + tex_size) { ret = dxv_decompress_cgo(ctx, gb, tex_data, tex_size, op_data, &oi, op_size, &dst, &state, table0, table1, 0); if (ret < 0) return ret; } bytestream2_seek(gb, data_start + op_offset + skip - 8, SEEK_SET); return 0; } static int dxv_decompress_ycg6(AVCodecContext *avctx) { DXVContext *ctx = avctx->priv_data; GetByteContext *gb = &ctx->gbc; int ret; ret = dxv_decompress_yo(ctx, gb, ctx->tex_data, ctx->tex_size, ctx->op_data[0], ctx->op_size[0]); if (ret < 0) return ret; return dxv_decompress_cocg(ctx, gb, ctx->ctex_data, ctx->ctex_size, ctx->op_data[1], ctx->op_data[2], ctx->op_size[1], ctx->op_size[2]); } static int dxv_decompress_yg10(AVCodecContext *avctx) { DXVContext *ctx = avctx->priv_data; GetByteContext *gb = &ctx->gbc; int ret; ret = dxv_decompress_cocg(ctx, gb, ctx->tex_data, ctx->tex_size, ctx->op_data[0], ctx->op_data[3], ctx->op_size[0], ctx->op_size[3]); if (ret < 0) return ret; return dxv_decompress_cocg(ctx, gb, ctx->ctex_data, ctx->ctex_size, ctx->op_data[1], ctx->op_data[2], ctx->op_size[1], ctx->op_size[2]); } static int dxv_decompress_dxt5(AVCodecContext *avctx) { DXVContext *ctx = avctx->priv_data; GetByteContext *gbc = &ctx->gbc; uint32_t value, op, prev; int idx, 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 + 2 <= 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 (bytestream2_get_bytes_left(gbc) < 1) return AVERROR_INVALIDDATA; 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 + 4 <= 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 + 4 * bytestream2_get_le16(gbc); if (idx > pos || (unsigned int)(pos - idx) + 2 > ctx->tex_size / 4) return AVERROR_INVALIDDATA; 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); if (pos + 2 > ctx->tex_size / 4) return AVERROR_INVALIDDATA; /* Copy two elements from a previous offset or from the input buffer */ if (op) { if (idx > pos || (unsigned int)(pos - idx) + 2 > ctx->tex_size / 4) return AVERROR_INVALIDDATA; 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 && (idx > pos || (unsigned int)(pos - idx) + 2 > ctx->tex_size / 4)) return AVERROR_INVALIDDATA; 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_decompress_raw(AVCodecContext *avctx) { DXVContext *ctx = avctx->priv_data; GetByteContext *gbc = &ctx->gbc; if (bytestream2_get_bytes_left(gbc) < ctx->tex_size) return AVERROR_INVALIDDATA; bytestream2_get_buffer(gbc, ctx->tex_data, ctx->tex_size); return 0; } static int dxv_decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *avpkt) { DXVContext *ctx = avctx->priv_data; GetByteContext *gbc = &ctx->gbc; TextureDSPThreadContext texdsp_ctx, ctexdsp_ctx; int (*decompress_tex)(AVCodecContext *avctx); const char *msgcomp, *msgtext; uint32_t tag; int version_major, version_minor = 0; int size = 0, old_type = 0; int ret; bytestream2_init(gbc, avpkt->data, avpkt->size); avctx->pix_fmt = AV_PIX_FMT_RGBA; avctx->colorspace = AVCOL_SPC_RGB; tag = bytestream2_get_le32(gbc); switch (tag) { case DXV_FMT_DXT1: decompress_tex = dxv_decompress_dxt1; texdsp_ctx.tex_funct = ctx->texdsp.dxt1_block; texdsp_ctx.tex_ratio = 8; texdsp_ctx.raw_ratio = 16; msgcomp = "DXTR1"; msgtext = "DXT1"; break; case DXV_FMT_DXT5: decompress_tex = dxv_decompress_dxt5; /* DXV misnomers DXT5, alpha is premultiplied so use DXT4 instead */ texdsp_ctx.tex_funct = ctx->texdsp.dxt4_block; texdsp_ctx.tex_ratio = 16; texdsp_ctx.raw_ratio = 16; msgcomp = "DXTR5"; msgtext = "DXT5"; break; case DXV_FMT_YCG6: decompress_tex = dxv_decompress_ycg6; texdsp_ctx.tex_funct = ctx->texdsp.rgtc1u_gray_block; texdsp_ctx.tex_ratio = 8; texdsp_ctx.raw_ratio = 4; ctexdsp_ctx.tex_funct = ctx->texdsp.rgtc1u_gray_block; ctexdsp_ctx.tex_ratio = 16; ctexdsp_ctx.raw_ratio = 4; msgcomp = "YOCOCG6"; msgtext = "YCG6"; avctx->pix_fmt = AV_PIX_FMT_YUV420P; avctx->colorspace = AVCOL_SPC_YCOCG; break; case DXV_FMT_YG10: decompress_tex = dxv_decompress_yg10; texdsp_ctx.tex_funct = ctx->texdsp.rgtc1u_gray_block; texdsp_ctx.tex_ratio = 16; texdsp_ctx.raw_ratio = 4; ctexdsp_ctx.tex_funct = ctx->texdsp.rgtc1u_gray_block; ctexdsp_ctx.tex_ratio = 16; ctexdsp_ctx.raw_ratio = 4; msgcomp = "YAOCOCG10"; msgtext = "YG10"; avctx->pix_fmt = AV_PIX_FMT_YUVA420P; avctx->colorspace = AVCOL_SPC_YCOCG; break; default: /* Old version does not have a real header, just size and type. */ size = tag & 0x00FFFFFF; old_type = tag >> 24; version_major = (old_type & 0x0F) - 1; if (old_type & 0x80) { msgcomp = "RAW"; decompress_tex = dxv_decompress_raw; } else { msgcomp = "LZF"; decompress_tex = dxv_decompress_lzf; } if (old_type & 0x40) { tag = DXV_FMT_DXT5; msgtext = "DXT5"; texdsp_ctx.tex_funct = ctx->texdsp.dxt4_block; texdsp_ctx.tex_ratio = 16; texdsp_ctx.raw_ratio = 16; } else if (old_type & 0x20 || version_major == 1) { tag = DXV_FMT_DXT1; msgtext = "DXT1"; texdsp_ctx.tex_funct = ctx->texdsp.dxt1_block; texdsp_ctx.tex_ratio = 8; texdsp_ctx.raw_ratio = 16; } else { av_log(avctx, AV_LOG_ERROR, "Unsupported header (0x%08"PRIX32")\n.", tag); return AVERROR_INVALIDDATA; } break; } texdsp_ctx.slice_count = av_clip(avctx->thread_count, 1, avctx->coded_height / TEXTURE_BLOCK_H); ctexdsp_ctx.slice_count = av_clip(avctx->thread_count, 1, avctx->coded_height / 2 / TEXTURE_BLOCK_H); /* New header is 12 bytes long. */ if (!old_type) { version_major = bytestream2_get_byte(gbc) - 1; version_minor = bytestream2_get_byte(gbc); /* Encoder copies texture data when compression is not advantageous. */ if (bytestream2_get_byte(gbc)) { msgcomp = "RAW"; decompress_tex = dxv_decompress_raw; } bytestream2_skip(gbc, 1); // unknown size = bytestream2_get_le32(gbc); } av_log(avctx, AV_LOG_DEBUG, "%s compression with %s texture (version %d.%d)\n", msgcomp, msgtext, version_major, version_minor); if (size != bytestream2_get_bytes_left(gbc)) { av_log(avctx, AV_LOG_ERROR, "Incomplete or invalid file (header %d, left %u).\n", size, bytestream2_get_bytes_left(gbc)); return AVERROR_INVALIDDATA; } ctx->tex_size = avctx->coded_width / (texdsp_ctx.raw_ratio / (avctx->pix_fmt == AV_PIX_FMT_RGBA ? 4 : 1)) * avctx->coded_height / TEXTURE_BLOCK_H * texdsp_ctx.tex_ratio; ret = av_reallocp(&ctx->tex_data, ctx->tex_size + AV_INPUT_BUFFER_PADDING_SIZE); if (ret < 0) return ret; if (avctx->pix_fmt != AV_PIX_FMT_RGBA) { int i; ctx->ctex_size = avctx->coded_width / 2 / ctexdsp_ctx.raw_ratio * avctx->coded_height / 2 / TEXTURE_BLOCK_H * ctexdsp_ctx.tex_ratio; ctx->op_size[0] = avctx->coded_width * avctx->coded_height / 16; ctx->op_size[1] = avctx->coded_width * avctx->coded_height / 32; ctx->op_size[2] = avctx->coded_width * avctx->coded_height / 32; ctx->op_size[3] = avctx->coded_width * avctx->coded_height / 16; ret = av_reallocp(&ctx->ctex_data, ctx->ctex_size + AV_INPUT_BUFFER_PADDING_SIZE); if (ret < 0) return ret; for (i = 0; i < 4; i++) { ret = av_reallocp(&ctx->op_data[i], ctx->op_size[i]); if (ret < 0) return ret; } } /* Decompress texture out of the intermediate compression. */ ret = decompress_tex(avctx); if (ret < 0) return ret; ret = ff_thread_get_buffer(avctx, frame, 0); if (ret < 0) return ret; texdsp_ctx.width = avctx->coded_width; texdsp_ctx.height = avctx->coded_height; ctexdsp_ctx.width = avctx->coded_width / 2; ctexdsp_ctx.height = avctx->coded_height / 2; switch (tag) { case DXV_FMT_YG10: /* BC5 texture with alpha in the second half of each block */ texdsp_ctx.tex_data.in = ctx->tex_data + texdsp_ctx.tex_ratio / 2; texdsp_ctx.frame_data.out = frame->data[3]; texdsp_ctx.stride = frame->linesize[3]; ret = ff_texturedsp_exec_decompress_threads(avctx, &texdsp_ctx); if (ret < 0) return ret; /* fallthrough */ case DXV_FMT_YCG6: /* BC5 texture with Co in the first half of each block and Cg in the second */ ctexdsp_ctx.tex_data.in = ctx->ctex_data; ctexdsp_ctx.frame_data.out = frame->data[2]; ctexdsp_ctx.stride = frame->linesize[2]; ret = ff_texturedsp_exec_decompress_threads(avctx, &ctexdsp_ctx); if (ret < 0) return ret; ctexdsp_ctx.tex_data.in = ctx->ctex_data + ctexdsp_ctx.tex_ratio / 2; ctexdsp_ctx.frame_data.out = frame->data[1]; ctexdsp_ctx.stride = frame->linesize[1]; ret = ff_texturedsp_exec_decompress_threads(avctx, &ctexdsp_ctx); if (ret < 0) return ret; /* fallthrough */ case DXV_FMT_DXT1: case DXV_FMT_DXT5: /* For DXT1 and DXT5, self explanatory * For YCG6, BC4 texture for Y * For YG10, BC5 texture with Y in the first half of each block */ texdsp_ctx.tex_data.in = ctx->tex_data; texdsp_ctx.frame_data.out = frame->data[0]; texdsp_ctx.stride = frame->linesize[0]; ret = ff_texturedsp_exec_decompress_threads(avctx, &texdsp_ctx); if (ret < 0) return ret; break; } /* Frame is ready to be output. */ frame->pict_type = AV_PICTURE_TYPE_I; frame->flags |= AV_FRAME_FLAG_KEY; *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; } /* Since codec is based on 4x4 blocks, size is aligned to 4 */ avctx->coded_width = FFALIGN(avctx->width, TEXTURE_BLOCK_W); avctx->coded_height = FFALIGN(avctx->height, TEXTURE_BLOCK_H); ff_texturedsp_init(&ctx->texdsp); return 0; } static int dxv_close(AVCodecContext *avctx) { DXVContext *ctx = avctx->priv_data; av_freep(&ctx->tex_data); av_freep(&ctx->ctex_data); av_freep(&ctx->op_data[0]); av_freep(&ctx->op_data[1]); av_freep(&ctx->op_data[2]); av_freep(&ctx->op_data[3]); return 0; } const FFCodec ff_dxv_decoder = { .p.name = "dxv", CODEC_LONG_NAME("Resolume DXV"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_DXV, .init = dxv_init, FF_CODEC_DECODE_CB(dxv_decode), .close = dxv_close, .priv_data_size = sizeof(DXVContext), .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, };