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.
Showing the changes to libavcodec (1 of 2 files). Show all
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 | } |