Improves compatibility with Resolume products by adding an additional hashtable for DXT color+LUT combinations, and padding the DXT texture dimensions to the next largest multiple of 16. Produces identical packets to Resolume Alley in manual tests.
Unified · Split
| @@ -34,6 +34,11 @@ · 33 unmodified lines | ||
| 34 | 34 | |
| 35 | 35 | #define DXV_HEADER_LENGTH 12 |
| 36 | 36 | |
| 37 | +/* | |
| 38 | + * Resolume will refuse to display frames that are not padded to 16x16 pixels. | |
| 39 | + */ | |
| 40 | +#define DXV_ALIGN(x) FFALIGN(x, 16) | |
| 41 | + | |
| 37 | 42 | /* |
| 38 | 43 | * DXV uses LZ-like back-references to avoid copying words that have already |
| 39 | 44 | * appeared in the decompressed stream. Using a simple hash table (HT) |
| @@ -60,6 +65,7 @@ typedef struct DXVEncContext { · 20 unmodified lines | ||
| 60 | 65 | |
| 61 | 66 | FFHashtableContext *color_ht; |
| 62 | 67 | FFHashtableContext *lut_ht; |
| 68 | + FFHashtableContext *combo_ht; | |
| 63 | 69 | } DXVEncContext; |
| 64 | 70 | |
| 65 | 71 | /* Converts an index offset value to a 2-bit opcode and pushes it to a stream. |
| @@ -94,10 +100,14 @@ static int dxv_compress_dxt1(AVCodecContext *avctx) · 28 unmodified lines | ||
| 94 | 100 | DXVEncContext *ctx = avctx->priv_data; |
| 95 | 101 | PutByteContext *pbc = &ctx->pbc; |
| 96 | 102 | void *value; |
| 97 | − uint32_t color, lut, idx, color_idx, lut_idx, prev_pos, state = 16, pos = 0, op = 0; | |
| 103 | + uint64_t combo; | |
| 104 | + uint32_t color, lut, idx, combo_idx, prev_pos, old_pos, state = 16, pos = 0, op = 0; | |
| 98 | 105 | |
| 99 | 106 | ff_hashtable_clear(ctx->color_ht); |
| 100 | 107 | ff_hashtable_clear(ctx->lut_ht); |
| 108 | + ff_hashtable_clear(ctx->combo_ht); | |
| 109 | + | |
| 110 | + ff_hashtable_set(ctx->combo_ht, ctx->tex_data, &pos); | |
| 101 | 111 | |
| 102 | 112 | bytestream2_put_le32(pbc, AV_RL32(ctx->tex_data)); |
| 103 | 113 | ff_hashtable_set(ctx->color_ht, ctx->tex_data, &pos); |
| @@ -107,51 +117,46 @@ static int dxv_compress_dxt1(AVCodecContext *avctx) · 3 unmodified lines | ||
| 107 | 117 | pos++; |
| 108 | 118 | |
| 109 | 119 | while (pos + 2 <= ctx->tex_size / 4) { |
| 110 | − idx = 0; | |
| 111 | − color_idx = 0; | |
| 112 | − lut_idx = 0; | |
| 120 | + combo = AV_RL64(ctx->tex_data + pos * 4); | |
| 121 | + combo_idx = ff_hashtable_get(ctx->combo_ht, &combo, &prev_pos) ? pos - prev_pos : 0; | |
| 122 | + idx = combo_idx; | |
| 123 | + PUSH_OP(2); | |
| 124 | + if (pos >= LOOKBACK_WORDS) { | |
| 125 | + old_pos = pos - LOOKBACK_WORDS; | |
| 126 | + if (ff_hashtable_get(ctx->combo_ht, ctx->tex_data + old_pos * 4, &prev_pos) && prev_pos <= old_pos) | |
| 127 | + ff_hashtable_delete(ctx->combo_ht, ctx->tex_data + old_pos * 4); | |
| 128 | + } | |
| 129 | + ff_hashtable_set(ctx->combo_ht, &combo, &pos); | |
| 113 | 130 | |
| 114 | 131 | color = AV_RL32(ctx->tex_data + pos * 4); |
| 115 | − if (ff_hashtable_get(ctx->color_ht, &color, &prev_pos)) | |
| 116 | − color_idx = pos - prev_pos; | |
| 117 | − ff_hashtable_set(ctx->color_ht, &color, &pos); | |
| 118 | − | |
| 132 | + if (!combo_idx) { | |
| 133 | + idx = ff_hashtable_get(ctx->color_ht, &color, &prev_pos) ? pos - prev_pos : 0; | |
| 134 | + PUSH_OP(2); | |
| 135 | + if (!idx) | |
| 136 | + bytestream2_put_le32(pbc, color); | |
| 137 | + } | |
| 119 | 138 | if (pos >= LOOKBACK_WORDS) { |
| 120 | − uint32_t old_pos = pos - LOOKBACK_WORDS; | |
| 139 | + old_pos = pos - LOOKBACK_WORDS; | |
| 121 | 140 | if (ff_hashtable_get(ctx->color_ht, ctx->tex_data + old_pos * 4, &prev_pos) && prev_pos <= old_pos) |
| 122 | 141 | ff_hashtable_delete(ctx->color_ht, ctx->tex_data + old_pos * 4); |
| 123 | 142 | } |
| 143 | + ff_hashtable_set(ctx->color_ht, &color, &pos); | |
| 124 | 144 | pos++; |
| 125 | 145 | |
| 126 | 146 | lut = AV_RL32(ctx->tex_data + pos * 4); |
| 127 | − if (color_idx && lut == AV_RL32(ctx->tex_data + (pos - color_idx) * 4)) { | |
| 128 | − idx = color_idx; | |
| 129 | − } else { | |
| 130 | − idx = 0; | |
| 131 | − if (ff_hashtable_get(ctx->lut_ht, &lut, &prev_pos)) | |
| 132 | − lut_idx = pos - prev_pos; | |
| 133 | − ff_hashtable_set(ctx->lut_ht, &lut, &pos); | |
| 147 | + if (!combo_idx) { | |
| 148 | + idx = ff_hashtable_get(ctx->lut_ht, &lut, &prev_pos) ? pos - prev_pos : 0; | |
| 149 | + PUSH_OP(2); | |
| 150 | + if (!idx) | |
| 151 | + bytestream2_put_le32(pbc, lut); | |
| 134 | 152 | } |
| 135 | 153 | if (pos >= LOOKBACK_WORDS) { |
| 136 | − uint32_t old_pos = pos - LOOKBACK_WORDS; | |
| 154 | + old_pos = pos - LOOKBACK_WORDS; | |
| 137 | 155 | if (ff_hashtable_get(ctx->lut_ht, ctx->tex_data + old_pos * 4, &prev_pos) && prev_pos <= old_pos) |
| 138 | 156 | ff_hashtable_delete(ctx->lut_ht, ctx->tex_data + old_pos * 4); |
| 139 | 157 | } |
| 158 | + ff_hashtable_set(ctx->lut_ht, &lut, &pos); | |
| 140 | 159 | pos++; |
| 141 | − | |
| 142 | − PUSH_OP(2); | |
| 143 | − | |
| 144 | − if (!idx) { | |
| 145 | − idx = color_idx; | |
| 146 | − PUSH_OP(2); | |
| 147 | − if (!idx) | |
| 148 | − bytestream2_put_le32(pbc, color); | |
| 149 | − | |
| 150 | − idx = lut_idx; | |
| 151 | − PUSH_OP(2); | |
| 152 | − if (!idx) | |
| 153 | − bytestream2_put_le32(pbc, lut); | |
| 154 | − } | |
| 155 | 160 | } |
| 156 | 161 | |
| 157 | 162 | return 0; |
| @@ -172,12 +177,50 @@ static int dxv_encode(AVCodecContext *avctx, AVPacket *pkt, · 14 unmodified lines | ||
| 172 | 177 | return ret; |
| 173 | 178 | |
| 174 | 179 | if (ctx->enc.tex_funct) { |
| 180 | + uint8_t *safe_data[4] = {frame->data[0], 0, 0, 0}; | |
| 181 | + int safe_linesize[4] = {frame->linesize[0], 0, 0, 0}; | |
| 182 | + | |
| 183 | + if (avctx->width != DXV_ALIGN(avctx->width) || avctx->height != DXV_ALIGN(avctx->height)) { | |
| 184 | + ret = av_image_alloc( | |
| 185 | + safe_data, | |
| 186 | + safe_linesize, | |
| 187 | + DXV_ALIGN(avctx->width), | |
| 188 | + DXV_ALIGN(avctx->height), | |
| 189 | + avctx->pix_fmt, | |
| 190 | + 1); | |
| 191 | + if (ret < 0) | |
| 192 | + return ret; | |
| 193 | + | |
| 194 | + av_image_copy2( | |
| 195 | + safe_data, | |
| 196 | + safe_linesize, | |
| 197 | + frame->data, | |
| 198 | + frame->linesize, | |
| 199 | + avctx->pix_fmt, | |
| 200 | + avctx->width, | |
| 201 | + avctx->height); | |
| 202 | + | |
| 203 | + if (avctx->width != DXV_ALIGN(avctx->width)) { | |
| 204 | + for (int y = 0; y < avctx->height; y++) { | |
| 205 | + memset(safe_data[0] + y * safe_linesize[0] + frame->linesize[0], 0, safe_linesize[0] - frame->linesize[0]); | |
| 206 | + } | |
| 207 | + } | |
| 208 | + if (avctx->height != DXV_ALIGN(avctx->height)) { | |
| 209 | + for (int y = avctx->height; y < DXV_ALIGN(avctx->height); y++) { | |
| 210 | + memset(safe_data[0] + y * safe_linesize[0], 0, safe_linesize[0]); | |
| 211 | + } | |
| 212 | + } | |
| 213 | + } | |
| 214 | + | |
| 175 | 215 | ctx->enc.tex_data.out = ctx->tex_data; |
| 176 | − ctx->enc.frame_data.in = frame->data[0]; | |
| 177 | − ctx->enc.stride = frame->linesize[0]; | |
| 178 | − ctx->enc.width = avctx->width; | |
| 179 | − ctx->enc.height = avctx->height; | |
| 216 | + ctx->enc.frame_data.in = safe_data[0]; | |
| 217 | + ctx->enc.stride = safe_linesize[0]; | |
| 218 | + ctx->enc.width = DXV_ALIGN(avctx->width); | |
| 219 | + ctx->enc.height = DXV_ALIGN(avctx->height); | |
| 180 | 220 | ff_texturedsp_exec_compress_threads(avctx, &ctx->enc); |
| 221 | + | |
| 222 | + if (safe_data[0] != frame->data[0]) | |
| 223 | + av_freep(&safe_data[0]); | |
| 181 | 224 | } else { |
| 182 | 225 | /* unimplemented: YCoCg formats */ |
| 183 | 226 | return AVERROR_INVALIDDATA; |
| @@ -216,14 +259,6 @@ static av_cold int dxv_init(AVCodecContext *avctx) · 32 unmodified lines | ||
| 216 | 259 | return ret; |
| 217 | 260 | } |
| 218 | 261 | |
| 219 | − if (avctx->width % TEXTURE_BLOCK_W || avctx->height % TEXTURE_BLOCK_H) { | |
| 220 | − av_log(avctx, | |
| 221 | − AV_LOG_ERROR, | |
| 222 | − "Video size %dx%d is not multiple of "AV_STRINGIFY(TEXTURE_BLOCK_W)"x"AV_STRINGIFY(TEXTURE_BLOCK_H)".\n", | |
| 223 | − avctx->width, avctx->height); | |
| 224 | − return AVERROR_INVALIDDATA; | |
| 225 | − } | |
| 226 | − | |
| 227 | 262 | ff_texturedspenc_init(&texdsp); |
| 228 | 263 | |
| 229 | 264 | switch (ctx->tex_fmt) { |
| @@ -237,10 +272,10 @@ static av_cold int dxv_init(AVCodecContext *avctx) · 7 unmodified lines | ||
| 237 | 272 | return AVERROR_INVALIDDATA; |
| 238 | 273 | } |
| 239 | 274 | ctx->enc.raw_ratio = 16; |
| 240 | − ctx->tex_size = avctx->width / TEXTURE_BLOCK_W * | |
| 241 | − avctx->height / TEXTURE_BLOCK_H * | |
| 275 | + ctx->tex_size = DXV_ALIGN(avctx->width) / TEXTURE_BLOCK_W * | |
| 276 | + DXV_ALIGN(avctx->height) / TEXTURE_BLOCK_H * | |
| 242 | 277 | ctx->enc.tex_ratio; |
| 243 | − ctx->enc.slice_count = av_clip(avctx->thread_count, 1, avctx->height / TEXTURE_BLOCK_H); | |
| 278 | + ctx->enc.slice_count = av_clip(avctx->thread_count, 1, DXV_ALIGN(avctx->height) / TEXTURE_BLOCK_H); | |
| 244 | 279 | |
| 245 | 280 | ctx->tex_data = av_malloc(ctx->tex_size); |
| 246 | 281 | if (!ctx->tex_data) { |
| @@ -251,6 +286,9 @@ static av_cold int dxv_init(AVCodecContext *avctx) · 4 unmodified lines | ||
| 251 | 286 | if (ret < 0) |
| 252 | 287 | return ret; |
| 253 | 288 | ret = ff_hashtable_alloc(&ctx->lut_ht, sizeof(uint32_t), sizeof(uint32_t), LOOKBACK_HT_ELEMS); |
| 289 | + if (ret < 0) | |
| 290 | + return ret; | |
| 291 | + ret = ff_hashtable_alloc(&ctx->combo_ht, sizeof(uint64_t), sizeof(uint32_t), LOOKBACK_HT_ELEMS); | |
| 254 | 292 | if (ret < 0) |
| 255 | 293 | return ret; |
| 256 | 294 | |
| @@ -265,6 +303,7 @@ static av_cold int dxv_close(AVCodecContext *avctx) · 8 unmodified lines | ||
| 265 | 303 | |
| 266 | 304 | ff_hashtable_freep(&ctx->color_ht); |
| 267 | 305 | ff_hashtable_freep(&ctx->lut_ht); |
| 306 | + ff_hashtable_freep(&ctx->combo_ht); | |
| 268 | 307 | |
| 269 | 308 | return 0; |
| 270 | 309 | } |
| @@ -3,4 +3,4 @@ · 2 unmodified lines | ||
| 3 | 3 | #codec_id 0: dxv |
| 4 | 4 | #dimensions 0: 1920x1080 |
| 5 | 5 | #sar 0: 1/1 |
| 6 | −0, 0, 0, 1, 76521, 0xed387a5e | |
| 6 | +0, 0, 0, 1, 76190, 0x0e6f0326 | |