History

lavc/dxvenc: improve compatibility with Resolume products

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.

Emma Worley committed 1 year ago

Commit d4556c91 parent 2de0d092 files · +86 −47Browse files

Showing the changes to libavcodec (1 of 2 files). Show all

Unified · Split

libavcodec/dxvenc.c+85−46
@@ -34,6 +34,11 @@ · 33 unmodified lines
34 34
35#define DXV_HEADER_LENGTH 1235#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 already43 * 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}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t