blob: 4b1c2d25ccfa0ae823602b12595159514d1dfc03
1 | /* |
2 | * Resolume DXV decoder |
3 | * Copyright (C) 2015 Vittorio Giovara <vittorio.giovara@gmail.com> |
4 | * |
5 | * This file is part of FFmpeg. |
6 | * |
7 | * FFmpeg is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either |
10 | * version 2.1 of the License, or (at your option) any later version. |
11 | * |
12 | * FFmpeg is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with FFmpeg; if not, write to the Free Software |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | */ |
21 | |
22 | #include <stdint.h> |
23 | |
24 | #include "libavutil/imgutils.h" |
25 | |
26 | #include "avcodec.h" |
27 | #include "bytestream.h" |
28 | #include "internal.h" |
29 | #include "lzf.h" |
30 | #include "texturedsp.h" |
31 | #include "thread.h" |
32 | |
33 | typedef struct DXVContext { |
34 | TextureDSPContext texdsp; |
35 | GetByteContext gbc; |
36 | |
37 | uint8_t *tex_data; // Compressed texture |
38 | int tex_rat; // Compression ratio |
39 | int tex_step; // Distance between blocks |
40 | int64_t tex_size; // Texture size |
41 | |
42 | /* Optimal number of slices for parallel decoding */ |
43 | int slice_count; |
44 | |
45 | /* Pointer to the selected decompression function */ |
46 | int (*tex_funct)(uint8_t *dst, ptrdiff_t stride, const uint8_t *block); |
47 | } DXVContext; |
48 | |
49 | static int decompress_texture_thread(AVCodecContext *avctx, void *arg, |
50 | int slice, int thread_nb) |
51 | { |
52 | DXVContext *ctx = avctx->priv_data; |
53 | AVFrame *frame = arg; |
54 | const uint8_t *d = ctx->tex_data; |
55 | int w_block = avctx->coded_width / TEXTURE_BLOCK_W; |
56 | int h_block = avctx->coded_height / TEXTURE_BLOCK_H; |
57 | int x, y; |
58 | int start_slice, end_slice; |
59 | int base_blocks_per_slice = h_block / ctx->slice_count; |
60 | int remainder_blocks = h_block % ctx->slice_count; |
61 | |
62 | /* When the frame height (in blocks) doesn't divide evenly between the |
63 | * number of slices, spread the remaining blocks evenly between the first |
64 | * operations */ |
65 | start_slice = slice * base_blocks_per_slice; |
66 | /* Add any extra blocks (one per slice) that have been added |
67 | * before this slice */ |
68 | start_slice += FFMIN(slice, remainder_blocks); |
69 | |
70 | end_slice = start_slice + base_blocks_per_slice; |
71 | /* Add an extra block if there are remainder blocks to be accounted for */ |
72 | if (slice < remainder_blocks) |
73 | end_slice++; |
74 | |
75 | for (y = start_slice; y < end_slice; y++) { |
76 | uint8_t *p = frame->data[0] + y * frame->linesize[0] * TEXTURE_BLOCK_H; |
77 | int off = y * w_block; |
78 | for (x = 0; x < w_block; x++) { |
79 | ctx->tex_funct(p + x * 16, frame->linesize[0], |
80 | d + (off + x) * ctx->tex_step); |
81 | } |
82 | } |
83 | |
84 | return 0; |
85 | } |
86 | |
87 | /* This scheme addresses already decoded elements depending on 2-bit status: |
88 | * 0 -> copy new element |
89 | * 1 -> copy one element from position -x |
90 | * 2 -> copy one element from position -(get_byte() + 2) * x |
91 | * 3 -> copy one element from position -(get_16le() + 0x102) * x |
92 | * x is always 2 for dxt1 and 4 for dxt5. */ |
93 | #define CHECKPOINT(x) \ |
94 | do { \ |
95 | if (state == 0) { \ |
96 | value = bytestream2_get_le32(gbc); \ |
97 | state = 16; \ |
98 | } \ |
99 | op = value & 0x3; \ |
100 | value >>= 2; \ |
101 | state--; \ |
102 | switch (op) { \ |
103 | case 1: \ |
104 | idx = x; \ |
105 | break; \ |
106 | case 2: \ |
107 | idx = (bytestream2_get_byte(gbc) + 2) * x; \ |
108 | if (idx > pos) { \ |
109 | av_log(avctx, AV_LOG_ERROR, "idx %d > %d\n", idx, pos); \ |
110 | return AVERROR_INVALIDDATA; \ |
111 | } \ |
112 | break; \ |
113 | case 3: \ |
114 | idx = (bytestream2_get_le16(gbc) + 0x102) * x; \ |
115 | if (idx > pos) { \ |
116 | av_log(avctx, AV_LOG_ERROR, "idx %d > %d\n", idx, pos); \ |
117 | return AVERROR_INVALIDDATA; \ |
118 | } \ |
119 | break; \ |
120 | } \ |
121 | } while(0) |
122 | |
123 | static int dxv_decompress_dxt1(AVCodecContext *avctx) |
124 | { |
125 | DXVContext *ctx = avctx->priv_data; |
126 | GetByteContext *gbc = &ctx->gbc; |
127 | uint32_t value, prev, op; |
128 | int idx = 0, state = 0; |
129 | int pos = 2; |
130 | |
131 | /* Copy the first two elements */ |
132 | AV_WL32(ctx->tex_data, bytestream2_get_le32(gbc)); |
133 | AV_WL32(ctx->tex_data + 4, bytestream2_get_le32(gbc)); |
134 | |
135 | /* Process input until the whole texture has been filled */ |
136 | while (pos + 2 <= ctx->tex_size / 4) { |
137 | CHECKPOINT(2); |
138 | |
139 | /* Copy two elements from a previous offset or from the input buffer */ |
140 | if (op) { |
141 | prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); |
142 | AV_WL32(ctx->tex_data + 4 * pos, prev); |
143 | pos++; |
144 | |
145 | prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); |
146 | AV_WL32(ctx->tex_data + 4 * pos, prev); |
147 | pos++; |
148 | } else { |
149 | CHECKPOINT(2); |
150 | |
151 | if (op) |
152 | prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); |
153 | else |
154 | prev = bytestream2_get_le32(gbc); |
155 | AV_WL32(ctx->tex_data + 4 * pos, prev); |
156 | pos++; |
157 | |
158 | CHECKPOINT(2); |
159 | |
160 | if (op) |
161 | prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); |
162 | else |
163 | prev = bytestream2_get_le32(gbc); |
164 | AV_WL32(ctx->tex_data + 4 * pos, prev); |
165 | pos++; |
166 | } |
167 | } |
168 | |
169 | return 0; |
170 | } |
171 | |
172 | static int dxv_decompress_dxt5(AVCodecContext *avctx) |
173 | { |
174 | DXVContext *ctx = avctx->priv_data; |
175 | GetByteContext *gbc = &ctx->gbc; |
176 | uint32_t value, op; |
177 | int idx, prev, state = 0; |
178 | int pos = 4; |
179 | int run = 0; |
180 | int probe, check; |
181 | |
182 | /* Copy the first four elements */ |
183 | AV_WL32(ctx->tex_data + 0, bytestream2_get_le32(gbc)); |
184 | AV_WL32(ctx->tex_data + 4, bytestream2_get_le32(gbc)); |
185 | AV_WL32(ctx->tex_data + 8, bytestream2_get_le32(gbc)); |
186 | AV_WL32(ctx->tex_data + 12, bytestream2_get_le32(gbc)); |
187 | |
188 | /* Process input until the whole texture has been filled */ |
189 | while (pos + 2 <= ctx->tex_size / 4) { |
190 | if (run) { |
191 | run--; |
192 | |
193 | prev = AV_RL32(ctx->tex_data + 4 * (pos - 4)); |
194 | AV_WL32(ctx->tex_data + 4 * pos, prev); |
195 | pos++; |
196 | prev = AV_RL32(ctx->tex_data + 4 * (pos - 4)); |
197 | AV_WL32(ctx->tex_data + 4 * pos, prev); |
198 | pos++; |
199 | } else { |
200 | if (state == 0) { |
201 | value = bytestream2_get_le32(gbc); |
202 | state = 16; |
203 | } |
204 | op = value & 0x3; |
205 | value >>= 2; |
206 | state--; |
207 | |
208 | switch (op) { |
209 | case 0: |
210 | /* Long copy */ |
211 | check = bytestream2_get_byte(gbc) + 1; |
212 | if (check == 256) { |
213 | do { |
214 | probe = bytestream2_get_le16(gbc); |
215 | check += probe; |
216 | } while (probe == 0xFFFF); |
217 | } |
218 | while (check && pos + 4 <= ctx->tex_size / 4) { |
219 | prev = AV_RL32(ctx->tex_data + 4 * (pos - 4)); |
220 | AV_WL32(ctx->tex_data + 4 * pos, prev); |
221 | pos++; |
222 | |
223 | prev = AV_RL32(ctx->tex_data + 4 * (pos - 4)); |
224 | AV_WL32(ctx->tex_data + 4 * pos, prev); |
225 | pos++; |
226 | |
227 | prev = AV_RL32(ctx->tex_data + 4 * (pos - 4)); |
228 | AV_WL32(ctx->tex_data + 4 * pos, prev); |
229 | pos++; |
230 | |
231 | prev = AV_RL32(ctx->tex_data + 4 * (pos - 4)); |
232 | AV_WL32(ctx->tex_data + 4 * pos, prev); |
233 | pos++; |
234 | |
235 | check--; |
236 | } |
237 | |
238 | /* Restart (or exit) the loop */ |
239 | continue; |
240 | break; |
241 | case 1: |
242 | /* Load new run value */ |
243 | run = bytestream2_get_byte(gbc); |
244 | if (run == 255) { |
245 | do { |
246 | probe = bytestream2_get_le16(gbc); |
247 | run += probe; |
248 | } while (probe == 0xFFFF); |
249 | } |
250 | |
251 | /* Copy two dwords from previous data */ |
252 | prev = AV_RL32(ctx->tex_data + 4 * (pos - 4)); |
253 | AV_WL32(ctx->tex_data + 4 * pos, prev); |
254 | pos++; |
255 | |
256 | prev = AV_RL32(ctx->tex_data + 4 * (pos - 4)); |
257 | AV_WL32(ctx->tex_data + 4 * pos, prev); |
258 | pos++; |
259 | break; |
260 | case 2: |
261 | /* Copy two dwords from a previous index */ |
262 | idx = 8 + bytestream2_get_le16(gbc); |
263 | if (idx > pos || (unsigned int)(pos - idx) + 2 > ctx->tex_size / 4) |
264 | return AVERROR_INVALIDDATA; |
265 | prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); |
266 | AV_WL32(ctx->tex_data + 4 * pos, prev); |
267 | pos++; |
268 | |
269 | prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); |
270 | AV_WL32(ctx->tex_data + 4 * pos, prev); |
271 | pos++; |
272 | break; |
273 | case 3: |
274 | /* Copy two dwords from input */ |
275 | prev = bytestream2_get_le32(gbc); |
276 | AV_WL32(ctx->tex_data + 4 * pos, prev); |
277 | pos++; |
278 | |
279 | prev = bytestream2_get_le32(gbc); |
280 | AV_WL32(ctx->tex_data + 4 * pos, prev); |
281 | pos++; |
282 | break; |
283 | } |
284 | } |
285 | |
286 | CHECKPOINT(4); |
287 | if (pos + 2 > ctx->tex_size / 4) |
288 | return AVERROR_INVALIDDATA; |
289 | |
290 | /* Copy two elements from a previous offset or from the input buffer */ |
291 | if (op) { |
292 | if (idx > pos || (unsigned int)(pos - idx) + 2 > ctx->tex_size / 4) |
293 | return AVERROR_INVALIDDATA; |
294 | prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); |
295 | AV_WL32(ctx->tex_data + 4 * pos, prev); |
296 | pos++; |
297 | |
298 | prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); |
299 | AV_WL32(ctx->tex_data + 4 * pos, prev); |
300 | pos++; |
301 | } else { |
302 | CHECKPOINT(4); |
303 | |
304 | if (op && (idx > pos || (unsigned int)(pos - idx) + 2 > ctx->tex_size / 4)) |
305 | return AVERROR_INVALIDDATA; |
306 | if (op) |
307 | prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); |
308 | else |
309 | prev = bytestream2_get_le32(gbc); |
310 | AV_WL32(ctx->tex_data + 4 * pos, prev); |
311 | pos++; |
312 | |
313 | CHECKPOINT(4); |
314 | |
315 | if (op) |
316 | prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); |
317 | else |
318 | prev = bytestream2_get_le32(gbc); |
319 | AV_WL32(ctx->tex_data + 4 * pos, prev); |
320 | pos++; |
321 | } |
322 | } |
323 | |
324 | return 0; |
325 | } |
326 | |
327 | static int dxv_decompress_lzf(AVCodecContext *avctx) |
328 | { |
329 | DXVContext *ctx = avctx->priv_data; |
330 | return ff_lzf_uncompress(&ctx->gbc, &ctx->tex_data, &ctx->tex_size); |
331 | } |
332 | |
333 | static int dxv_decompress_raw(AVCodecContext *avctx) |
334 | { |
335 | DXVContext *ctx = avctx->priv_data; |
336 | GetByteContext *gbc = &ctx->gbc; |
337 | |
338 | bytestream2_get_buffer(gbc, ctx->tex_data, ctx->tex_size); |
339 | return 0; |
340 | } |
341 | |
342 | static int dxv_decode(AVCodecContext *avctx, void *data, |
343 | int *got_frame, AVPacket *avpkt) |
344 | { |
345 | DXVContext *ctx = avctx->priv_data; |
346 | ThreadFrame tframe; |
347 | GetByteContext *gbc = &ctx->gbc; |
348 | int (*decompress_tex)(AVCodecContext *avctx); |
349 | const char *msgcomp, *msgtext; |
350 | uint32_t tag; |
351 | int version_major, version_minor = 0; |
352 | int size = 0, old_type = 0; |
353 | int ret; |
354 | |
355 | bytestream2_init(gbc, avpkt->data, avpkt->size); |
356 | |
357 | tag = bytestream2_get_le32(gbc); |
358 | switch (tag) { |
359 | case MKBETAG('D', 'X', 'T', '1'): |
360 | decompress_tex = dxv_decompress_dxt1; |
361 | ctx->tex_funct = ctx->texdsp.dxt1_block; |
362 | ctx->tex_rat = 8; |
363 | ctx->tex_step = 8; |
364 | msgcomp = "DXTR1"; |
365 | msgtext = "DXT1"; |
366 | break; |
367 | case MKBETAG('D', 'X', 'T', '5'): |
368 | decompress_tex = dxv_decompress_dxt5; |
369 | ctx->tex_funct = ctx->texdsp.dxt5_block; |
370 | ctx->tex_rat = 4; |
371 | ctx->tex_step = 16; |
372 | msgcomp = "DXTR5"; |
373 | msgtext = "DXT5"; |
374 | break; |
375 | case MKBETAG('Y', 'C', 'G', '6'): |
376 | case MKBETAG('Y', 'G', '1', '0'): |
377 | avpriv_report_missing_feature(avctx, "Tag 0x%08"PRIX32, tag); |
378 | return AVERROR_PATCHWELCOME; |
379 | default: |
380 | /* Old version does not have a real header, just size and type. */ |
381 | size = tag & 0x00FFFFFF; |
382 | old_type = tag >> 24; |
383 | version_major = (old_type & 0x0F) - 1; |
384 | |
385 | if (old_type & 0x80) { |
386 | msgcomp = "RAW"; |
387 | decompress_tex = dxv_decompress_raw; |
388 | } else { |
389 | msgcomp = "LZF"; |
390 | decompress_tex = dxv_decompress_lzf; |
391 | } |
392 | |
393 | if (old_type & 0x40) { |
394 | msgtext = "DXT5"; |
395 | |
396 | ctx->tex_funct = ctx->texdsp.dxt5_block; |
397 | ctx->tex_step = 16; |
398 | } else if (old_type & 0x20 || version_major == 1) { |
399 | msgtext = "DXT1"; |
400 | |
401 | ctx->tex_funct = ctx->texdsp.dxt1_block; |
402 | ctx->tex_step = 8; |
403 | } else { |
404 | av_log(avctx, AV_LOG_ERROR, "Unsupported header (0x%08"PRIX32")\n.", tag); |
405 | return AVERROR_INVALIDDATA; |
406 | } |
407 | ctx->tex_rat = 1; |
408 | break; |
409 | } |
410 | |
411 | /* New header is 12 bytes long. */ |
412 | if (!old_type) { |
413 | version_major = bytestream2_get_byte(gbc) - 1; |
414 | version_minor = bytestream2_get_byte(gbc); |
415 | |
416 | /* Encoder copies texture data when compression is not advantageous. */ |
417 | if (bytestream2_get_byte(gbc)) { |
418 | msgcomp = "RAW"; |
419 | ctx->tex_rat = 1; |
420 | decompress_tex = dxv_decompress_raw; |
421 | } |
422 | |
423 | bytestream2_skip(gbc, 1); // unknown |
424 | size = bytestream2_get_le32(gbc); |
425 | } |
426 | av_log(avctx, AV_LOG_DEBUG, |
427 | "%s compression with %s texture (version %d.%d)\n", |
428 | msgcomp, msgtext, version_major, version_minor); |
429 | |
430 | if (size != bytestream2_get_bytes_left(gbc)) { |
431 | av_log(avctx, AV_LOG_ERROR, |
432 | "Incomplete or invalid file (header %d, left %u).\n", |
433 | size, bytestream2_get_bytes_left(gbc)); |
434 | return AVERROR_INVALIDDATA; |
435 | } |
436 | |
437 | ctx->tex_size = avctx->coded_width * avctx->coded_height * 4 / ctx->tex_rat; |
438 | ret = av_reallocp(&ctx->tex_data, ctx->tex_size); |
439 | if (ret < 0) |
440 | return ret; |
441 | |
442 | /* Decompress texture out of the intermediate compression. */ |
443 | ret = decompress_tex(avctx); |
444 | if (ret < 0) |
445 | return ret; |
446 | |
447 | tframe.f = data; |
448 | ret = ff_thread_get_buffer(avctx, &tframe, 0); |
449 | if (ret < 0) |
450 | return ret; |
451 | |
452 | /* Now decompress the texture with the standard functions. */ |
453 | avctx->execute2(avctx, decompress_texture_thread, |
454 | tframe.f, NULL, ctx->slice_count); |
455 | |
456 | /* Frame is ready to be output. */ |
457 | tframe.f->pict_type = AV_PICTURE_TYPE_I; |
458 | tframe.f->key_frame = 1; |
459 | *got_frame = 1; |
460 | |
461 | return avpkt->size; |
462 | } |
463 | |
464 | static int dxv_init(AVCodecContext *avctx) |
465 | { |
466 | DXVContext *ctx = avctx->priv_data; |
467 | int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx); |
468 | |
469 | if (ret < 0) { |
470 | av_log(avctx, AV_LOG_ERROR, "Invalid image size %dx%d.\n", |
471 | avctx->width, avctx->height); |
472 | return ret; |
473 | } |
474 | |
475 | /* Codec requires 16x16 alignment. */ |
476 | avctx->coded_width = FFALIGN(avctx->width, 16); |
477 | avctx->coded_height = FFALIGN(avctx->height, 16); |
478 | |
479 | ff_texturedsp_init(&ctx->texdsp); |
480 | avctx->pix_fmt = AV_PIX_FMT_RGBA; |
481 | |
482 | ctx->slice_count = av_clip(avctx->thread_count, 1, |
483 | avctx->coded_height / TEXTURE_BLOCK_H); |
484 | |
485 | return 0; |
486 | } |
487 | |
488 | static int dxv_close(AVCodecContext *avctx) |
489 | { |
490 | DXVContext *ctx = avctx->priv_data; |
491 | |
492 | av_freep(&ctx->tex_data); |
493 | |
494 | return 0; |
495 | } |
496 | |
497 | AVCodec ff_dxv_decoder = { |
498 | .name = "dxv", |
499 | .long_name = NULL_IF_CONFIG_SMALL("Resolume DXV"), |
500 | .type = AVMEDIA_TYPE_VIDEO, |
501 | .id = AV_CODEC_ID_DXV, |
502 | .init = dxv_init, |
503 | .decode = dxv_decode, |
504 | .close = dxv_close, |
505 | .priv_data_size = sizeof(DXVContext), |
506 | .capabilities = AV_CODEC_CAP_DR1 | |
507 | AV_CODEC_CAP_SLICE_THREADS | |
508 | AV_CODEC_CAP_FRAME_THREADS, |
509 | .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | |
510 | FF_CODEC_CAP_INIT_CLEANUP, |
511 | }; |
512 |