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 | #define DXV_HEADER_LENGTH 12 | 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 | * DXV uses LZ-like back-references to avoid copying words that have already | 43 | * DXV uses LZ-like back-references to avoid copying words that have already |
| 39 | * appeared in the decompressed stream. Using a simple hash table (HT) | 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 | FFHashtableContext *color_ht; | 66 | FFHashtableContext *color_ht; |
| 62 | FFHashtableContext *lut_ht; | 67 | FFHashtableContext *lut_ht; |
| 68 | FFHashtableContext *combo_ht; | ||
| 63 | } DXVEncContext; | 69 | } DXVEncContext; |
| 64 | 70 | ||
| 65 | /* Converts an index offset value to a 2-bit opcode and pushes it to a stream. | 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 | DXVEncContext *ctx = avctx->priv_data; | 100 | DXVEncContext *ctx = avctx->priv_data; |
| 95 | PutByteContext *pbc = &ctx->pbc; | 101 | PutByteContext *pbc = &ctx->pbc; |
| 96 | void *value; | 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 | ff_hashtable_clear(ctx->color_ht); | 106 | ff_hashtable_clear(ctx->color_ht); |
| 100 | ff_hashtable_clear(ctx->lut_ht); | 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 | bytestream2_put_le32(pbc, AV_RL32(ctx->tex_data)); | 112 | bytestream2_put_le32(pbc, AV_RL32(ctx->tex_data)); |
| 103 | ff_hashtable_set(ctx->color_ht, ctx->tex_data, &pos); | 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 | pos++; | 117 | pos++; |
| 108 | 118 | ||
| 109 | while (pos + 2 <= ctx->tex_size / 4) { | 119 | while (pos + 2 <= ctx->tex_size / 4) { |
| 110 | idx = 0; | 120 | combo = AV_RL64(ctx->tex_data + pos * 4); |
| 111 | color_idx = 0; | 121 | combo_idx = ff_hashtable_get(ctx->combo_ht, &combo, &prev_pos) ? pos - prev_pos : 0; |
| 112 | lut_idx = 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 | color = AV_RL32(ctx->tex_data + pos * 4); | 131 | color = AV_RL32(ctx->tex_data + pos * 4); |
| 115 | if (ff_hashtable_get(ctx->color_ht, &color, &prev_pos)) | 132 | if (!combo_idx) { |
| 116 | color_idx = pos - prev_pos; | 133 | idx = ff_hashtable_get(ctx->color_ht, &color, &prev_pos) ? pos - prev_pos : 0; |
| 117 | ff_hashtable_set(ctx->color_ht, &color, &pos); | 134 | PUSH_OP(2); |
| 118 | 135 | if (!idx) | |
| 136 | bytestream2_put_le32(pbc, color); | ||
| 137 | } | ||
| 119 | if (pos >= LOOKBACK_WORDS) { | 138 | if (pos >= LOOKBACK_WORDS) { |
| 120 | uint32_t old_pos = pos - LOOKBACK_WORDS; | 139 | old_pos = pos - LOOKBACK_WORDS; |
| 121 | if (ff_hashtable_get(ctx->color_ht, ctx->tex_data + old_pos * 4, &prev_pos) && prev_pos <= old_pos) | 140 | if (ff_hashtable_get(ctx->color_ht, ctx->tex_data + old_pos * 4, &prev_pos) && prev_pos <= old_pos) |
| 122 | ff_hashtable_delete(ctx->color_ht, ctx->tex_data + old_pos * 4); | 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 | pos++; | 144 | pos++; |
| 125 | 145 | ||
| 126 | lut = AV_RL32(ctx->tex_data + pos * 4); | 146 | lut = AV_RL32(ctx->tex_data + pos * 4); |
| 127 | if (color_idx && lut == AV_RL32(ctx->tex_data + (pos - color_idx) * 4)) { | 147 | if (!combo_idx) { |
| 128 | idx = color_idx; | 148 | idx = ff_hashtable_get(ctx->lut_ht, &lut, &prev_pos) ? pos - prev_pos : 0; |
| 129 | } else { | 149 | PUSH_OP(2); |
| 130 | idx = 0; | 150 | if (!idx) |
| 131 | if (ff_hashtable_get(ctx->lut_ht, &lut, &prev_pos)) | 151 | bytestream2_put_le32(pbc, lut); |
| 132 | lut_idx = pos - prev_pos; | ||
| 133 | ff_hashtable_set(ctx->lut_ht, &lut, &pos); | ||
| 134 | } | 152 | } |
| 135 | if (pos >= LOOKBACK_WORDS) { | 153 | if (pos >= LOOKBACK_WORDS) { |
| 136 | uint32_t old_pos = pos - LOOKBACK_WORDS; | 154 | old_pos = pos - LOOKBACK_WORDS; |
| 137 | if (ff_hashtable_get(ctx->lut_ht, ctx->tex_data + old_pos * 4, &prev_pos) && prev_pos <= old_pos) | 155 | if (ff_hashtable_get(ctx->lut_ht, ctx->tex_data + old_pos * 4, &prev_pos) && prev_pos <= old_pos) |
| 138 | ff_hashtable_delete(ctx->lut_ht, ctx->tex_data + old_pos * 4); | 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 | pos++; | 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 | return 0; | 162 | return 0; |
| @@ -172,12 +177,50 @@ static int dxv_encode(AVCodecContext *avctx, AVPacket *pkt, · 14 unmodified lines | |||
| 172 | return ret; | 177 | return ret; |
| 173 | 178 | ||
| 174 | if (ctx->enc.tex_funct) { | 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 | ctx->enc.tex_data.out = ctx->tex_data; | 215 | ctx->enc.tex_data.out = ctx->tex_data; |
| 176 | ctx->enc.frame_data.in = frame->data[0]; | 216 | ctx->enc.frame_data.in = safe_data[0]; |
| 177 | ctx->enc.stride = frame->linesize[0]; | 217 | ctx->enc.stride = safe_linesize[0]; |
| 178 | ctx->enc.width = avctx->width; | 218 | ctx->enc.width = DXV_ALIGN(avctx->width); |
| 179 | ctx->enc.height = avctx->height; | 219 | ctx->enc.height = DXV_ALIGN(avctx->height); |
| 180 | ff_texturedsp_exec_compress_threads(avctx, &ctx->enc); | 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 | } else { | 224 | } else { |
| 182 | /* unimplemented: YCoCg formats */ | 225 | /* unimplemented: YCoCg formats */ |
| 183 | return AVERROR_INVALIDDATA; | 226 | return AVERROR_INVALIDDATA; |
| @@ -216,14 +259,6 @@ static av_cold int dxv_init(AVCodecContext *avctx) · 32 unmodified lines | |||
| 216 | return ret; | 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 | ff_texturedspenc_init(&texdsp); | 262 | ff_texturedspenc_init(&texdsp); |
| 228 | 263 | ||
| 229 | switch (ctx->tex_fmt) { | 264 | switch (ctx->tex_fmt) { |
| @@ -237,10 +272,10 @@ static av_cold int dxv_init(AVCodecContext *avctx) · 7 unmodified lines | |||
| 237 | return AVERROR_INVALIDDATA; | 272 | return AVERROR_INVALIDDATA; |
| 238 | } | 273 | } |
| 239 | ctx->enc.raw_ratio = 16; | 274 | ctx->enc.raw_ratio = 16; |
| 240 | ctx->tex_size = avctx->width / TEXTURE_BLOCK_W * | 275 | ctx->tex_size = DXV_ALIGN(avctx->width) / TEXTURE_BLOCK_W * |
| 241 | avctx->height / TEXTURE_BLOCK_H * | 276 | DXV_ALIGN(avctx->height) / TEXTURE_BLOCK_H * |
| 242 | ctx->enc.tex_ratio; | 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 | ctx->tex_data = av_malloc(ctx->tex_size); | 280 | ctx->tex_data = av_malloc(ctx->tex_size); |
| 246 | if (!ctx->tex_data) { | 281 | if (!ctx->tex_data) { |
| @@ -251,6 +286,9 @@ static av_cold int dxv_init(AVCodecContext *avctx) · 4 unmodified lines | |||
| 251 | if (ret < 0) | 286 | if (ret < 0) |
| 252 | return ret; | 287 | return ret; |
| 253 | ret = ff_hashtable_alloc(&ctx->lut_ht, sizeof(uint32_t), sizeof(uint32_t), LOOKBACK_HT_ELEMS); | 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 | if (ret < 0) | 292 | if (ret < 0) |
| 255 | return ret; | 293 | return ret; |
| 256 | 294 | ||
| @@ -265,6 +303,7 @@ static av_cold int dxv_close(AVCodecContext *avctx) · 8 unmodified lines | |||
| 265 | 303 | ||
| 266 | ff_hashtable_freep(&ctx->color_ht); | 304 | ff_hashtable_freep(&ctx->color_ht); |
| 267 | ff_hashtable_freep(&ctx->lut_ht); | 305 | ff_hashtable_freep(&ctx->lut_ht); |
| 306 | ff_hashtable_freep(&ctx->combo_ht); | ||
| 268 | 307 | ||
| 269 | return 0; | 308 | return 0; |
| 270 | } | 309 | } |
| @@ -3,4 +3,4 @@ · 2 unmodified lines | |||
| 3 | #codec_id 0: dxv | 3 | #codec_id 0: dxv |
| 4 | #dimensions 0: 1920x1080 | 4 | #dimensions 0: 1920x1080 |
| 5 | #sar 0: 1/1 | 5 | #sar 0: 1/1 |
| 6 | 0, 0, 0, 1, 76521, 0xed387a5e | 6 | 0, 0, 0, 1, 76190, 0x0e6f0326 |