Commit | Line | Data |
---|---|---|
2ba45a60 DM |
1 | /* |
2 | * Chronomaster DFA Video Decoder | |
3 | * Copyright (c) 2011 Konstantin Shishkov | |
4 | * based on work by Vladimir "VAG" Gneushev | |
5 | * | |
6 | * This file is part of FFmpeg. | |
7 | * | |
8 | * FFmpeg is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License as published by the Free Software Foundation; either | |
11 | * version 2.1 of the License, or (at your option) any later version. | |
12 | * | |
13 | * FFmpeg is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | * Lesser General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU Lesser General Public | |
19 | * License along with FFmpeg; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
21 | */ | |
22 | ||
23 | #include <inttypes.h> | |
24 | ||
25 | #include "avcodec.h" | |
26 | #include "bytestream.h" | |
27 | #include "internal.h" | |
28 | ||
29 | #include "libavutil/avassert.h" | |
30 | #include "libavutil/imgutils.h" | |
31 | #include "libavutil/mem.h" | |
32 | ||
33 | typedef struct DfaContext { | |
34 | uint32_t pal[256]; | |
35 | uint8_t *frame_buf; | |
36 | } DfaContext; | |
37 | ||
38 | static av_cold int dfa_decode_init(AVCodecContext *avctx) | |
39 | { | |
40 | DfaContext *s = avctx->priv_data; | |
41 | ||
42 | avctx->pix_fmt = AV_PIX_FMT_PAL8; | |
43 | ||
44 | if (!avctx->width || !avctx->height) | |
45 | return AVERROR_INVALIDDATA; | |
46 | ||
47 | av_assert0(av_image_check_size(avctx->width, avctx->height, 0, avctx) >= 0); | |
48 | ||
49 | s->frame_buf = av_mallocz(avctx->width * avctx->height); | |
50 | if (!s->frame_buf) | |
51 | return AVERROR(ENOMEM); | |
52 | ||
53 | return 0; | |
54 | } | |
55 | ||
56 | static int decode_copy(GetByteContext *gb, uint8_t *frame, int width, int height) | |
57 | { | |
58 | const int size = width * height; | |
59 | ||
60 | if (bytestream2_get_buffer(gb, frame, size) != size) | |
61 | return AVERROR_INVALIDDATA; | |
62 | return 0; | |
63 | } | |
64 | ||
65 | static int decode_tsw1(GetByteContext *gb, uint8_t *frame, int width, int height) | |
66 | { | |
67 | const uint8_t *frame_start = frame; | |
68 | const uint8_t *frame_end = frame + width * height; | |
69 | int mask = 0x10000, bitbuf = 0; | |
70 | int v, count, segments; | |
71 | unsigned offset; | |
72 | ||
73 | segments = bytestream2_get_le32(gb); | |
74 | offset = bytestream2_get_le32(gb); | |
75 | if (segments == 0 && offset == frame_end - frame) | |
76 | return 0; // skip frame | |
77 | if (frame_end - frame <= offset) | |
78 | return AVERROR_INVALIDDATA; | |
79 | frame += offset; | |
80 | while (segments--) { | |
81 | if (bytestream2_get_bytes_left(gb) < 2) | |
82 | return AVERROR_INVALIDDATA; | |
83 | if (mask == 0x10000) { | |
84 | bitbuf = bytestream2_get_le16u(gb); | |
85 | mask = 1; | |
86 | } | |
87 | if (frame_end - frame < 2) | |
88 | return AVERROR_INVALIDDATA; | |
89 | if (bitbuf & mask) { | |
90 | v = bytestream2_get_le16(gb); | |
91 | offset = (v & 0x1FFF) << 1; | |
92 | count = ((v >> 13) + 2) << 1; | |
93 | if (frame - frame_start < offset || frame_end - frame < count) | |
94 | return AVERROR_INVALIDDATA; | |
95 | av_memcpy_backptr(frame, offset, count); | |
96 | frame += count; | |
97 | } else { | |
98 | *frame++ = bytestream2_get_byte(gb); | |
99 | *frame++ = bytestream2_get_byte(gb); | |
100 | } | |
101 | mask <<= 1; | |
102 | } | |
103 | ||
104 | return 0; | |
105 | } | |
106 | ||
107 | static int decode_dsw1(GetByteContext *gb, uint8_t *frame, int width, int height) | |
108 | { | |
109 | const uint8_t *frame_start = frame; | |
110 | const uint8_t *frame_end = frame + width * height; | |
111 | int mask = 0x10000, bitbuf = 0; | |
112 | int v, offset, count, segments; | |
113 | ||
114 | segments = bytestream2_get_le16(gb); | |
115 | while (segments--) { | |
116 | if (bytestream2_get_bytes_left(gb) < 2) | |
117 | return AVERROR_INVALIDDATA; | |
118 | if (mask == 0x10000) { | |
119 | bitbuf = bytestream2_get_le16u(gb); | |
120 | mask = 1; | |
121 | } | |
122 | if (frame_end - frame < 2) | |
123 | return AVERROR_INVALIDDATA; | |
124 | if (bitbuf & mask) { | |
125 | v = bytestream2_get_le16(gb); | |
126 | offset = (v & 0x1FFF) << 1; | |
127 | count = ((v >> 13) + 2) << 1; | |
128 | if (frame - frame_start < offset || frame_end - frame < count) | |
129 | return AVERROR_INVALIDDATA; | |
130 | av_memcpy_backptr(frame, offset, count); | |
131 | frame += count; | |
132 | } else if (bitbuf & (mask << 1)) { | |
133 | frame += bytestream2_get_le16(gb); | |
134 | } else { | |
135 | *frame++ = bytestream2_get_byte(gb); | |
136 | *frame++ = bytestream2_get_byte(gb); | |
137 | } | |
138 | mask <<= 2; | |
139 | } | |
140 | ||
141 | return 0; | |
142 | } | |
143 | ||
144 | static int decode_dds1(GetByteContext *gb, uint8_t *frame, int width, int height) | |
145 | { | |
146 | const uint8_t *frame_start = frame; | |
147 | const uint8_t *frame_end = frame + width * height; | |
148 | int mask = 0x10000, bitbuf = 0; | |
149 | int i, v, offset, count, segments; | |
150 | ||
151 | segments = bytestream2_get_le16(gb); | |
152 | while (segments--) { | |
153 | if (bytestream2_get_bytes_left(gb) < 2) | |
154 | return AVERROR_INVALIDDATA; | |
155 | if (mask == 0x10000) { | |
156 | bitbuf = bytestream2_get_le16u(gb); | |
157 | mask = 1; | |
158 | } | |
159 | ||
160 | if (bitbuf & mask) { | |
161 | v = bytestream2_get_le16(gb); | |
162 | offset = (v & 0x1FFF) << 2; | |
163 | count = ((v >> 13) + 2) << 1; | |
164 | if (frame - frame_start < offset || frame_end - frame < count*2 + width) | |
165 | return AVERROR_INVALIDDATA; | |
166 | for (i = 0; i < count; i++) { | |
167 | frame[0] = frame[1] = | |
168 | frame[width] = frame[width + 1] = frame[-offset]; | |
169 | ||
170 | frame += 2; | |
171 | } | |
172 | } else if (bitbuf & (mask << 1)) { | |
173 | v = bytestream2_get_le16(gb)*2; | |
174 | if (frame - frame_end < v) | |
175 | return AVERROR_INVALIDDATA; | |
176 | frame += v; | |
177 | } else { | |
178 | if (frame_end - frame < width + 3) | |
179 | return AVERROR_INVALIDDATA; | |
180 | frame[0] = frame[1] = | |
181 | frame[width] = frame[width + 1] = bytestream2_get_byte(gb); | |
182 | frame += 2; | |
183 | frame[0] = frame[1] = | |
184 | frame[width] = frame[width + 1] = bytestream2_get_byte(gb); | |
185 | frame += 2; | |
186 | } | |
187 | mask <<= 2; | |
188 | } | |
189 | ||
190 | return 0; | |
191 | } | |
192 | ||
193 | static int decode_bdlt(GetByteContext *gb, uint8_t *frame, int width, int height) | |
194 | { | |
195 | uint8_t *line_ptr; | |
196 | int count, lines, segments; | |
197 | ||
198 | count = bytestream2_get_le16(gb); | |
199 | if (count >= height) | |
200 | return AVERROR_INVALIDDATA; | |
201 | frame += width * count; | |
202 | lines = bytestream2_get_le16(gb); | |
203 | if (count + lines > height) | |
204 | return AVERROR_INVALIDDATA; | |
205 | ||
206 | while (lines--) { | |
207 | if (bytestream2_get_bytes_left(gb) < 1) | |
208 | return AVERROR_INVALIDDATA; | |
209 | line_ptr = frame; | |
210 | frame += width; | |
211 | segments = bytestream2_get_byteu(gb); | |
212 | while (segments--) { | |
213 | if (frame - line_ptr <= bytestream2_peek_byte(gb)) | |
214 | return AVERROR_INVALIDDATA; | |
215 | line_ptr += bytestream2_get_byte(gb); | |
216 | count = (int8_t)bytestream2_get_byte(gb); | |
217 | if (count >= 0) { | |
218 | if (frame - line_ptr < count) | |
219 | return AVERROR_INVALIDDATA; | |
220 | if (bytestream2_get_buffer(gb, line_ptr, count) != count) | |
221 | return AVERROR_INVALIDDATA; | |
222 | } else { | |
223 | count = -count; | |
224 | if (frame - line_ptr < count) | |
225 | return AVERROR_INVALIDDATA; | |
226 | memset(line_ptr, bytestream2_get_byte(gb), count); | |
227 | } | |
228 | line_ptr += count; | |
229 | } | |
230 | } | |
231 | ||
232 | return 0; | |
233 | } | |
234 | ||
235 | static int decode_wdlt(GetByteContext *gb, uint8_t *frame, int width, int height) | |
236 | { | |
237 | const uint8_t *frame_end = frame + width * height; | |
238 | uint8_t *line_ptr; | |
239 | int count, i, v, lines, segments; | |
240 | int y = 0; | |
241 | ||
242 | lines = bytestream2_get_le16(gb); | |
243 | if (lines > height) | |
244 | return AVERROR_INVALIDDATA; | |
245 | ||
246 | while (lines--) { | |
247 | if (bytestream2_get_bytes_left(gb) < 2) | |
248 | return AVERROR_INVALIDDATA; | |
249 | segments = bytestream2_get_le16u(gb); | |
250 | while ((segments & 0xC000) == 0xC000) { | |
251 | unsigned skip_lines = -(int16_t)segments; | |
252 | unsigned delta = -((int16_t)segments * width); | |
253 | if (frame_end - frame <= delta || y + lines + skip_lines > height) | |
254 | return AVERROR_INVALIDDATA; | |
255 | frame += delta; | |
256 | y += skip_lines; | |
257 | segments = bytestream2_get_le16(gb); | |
258 | } | |
259 | ||
260 | if (frame_end <= frame) | |
261 | return AVERROR_INVALIDDATA; | |
262 | if (segments & 0x8000) { | |
263 | frame[width - 1] = segments & 0xFF; | |
264 | segments = bytestream2_get_le16(gb); | |
265 | } | |
266 | line_ptr = frame; | |
267 | if (frame_end - frame < width) | |
268 | return AVERROR_INVALIDDATA; | |
269 | frame += width; | |
270 | y++; | |
271 | while (segments--) { | |
272 | if (frame - line_ptr <= bytestream2_peek_byte(gb)) | |
273 | return AVERROR_INVALIDDATA; | |
274 | line_ptr += bytestream2_get_byte(gb); | |
275 | count = (int8_t)bytestream2_get_byte(gb); | |
276 | if (count >= 0) { | |
277 | if (frame - line_ptr < count * 2) | |
278 | return AVERROR_INVALIDDATA; | |
279 | if (bytestream2_get_buffer(gb, line_ptr, count * 2) != count * 2) | |
280 | return AVERROR_INVALIDDATA; | |
281 | line_ptr += count * 2; | |
282 | } else { | |
283 | count = -count; | |
284 | if (frame - line_ptr < count * 2) | |
285 | return AVERROR_INVALIDDATA; | |
286 | v = bytestream2_get_le16(gb); | |
287 | for (i = 0; i < count; i++) | |
288 | bytestream_put_le16(&line_ptr, v); | |
289 | } | |
290 | } | |
291 | } | |
292 | ||
293 | return 0; | |
294 | } | |
295 | ||
296 | static int decode_tdlt(GetByteContext *gb, uint8_t *frame, int width, int height) | |
297 | { | |
298 | const uint8_t *frame_end = frame + width * height; | |
299 | uint32_t segments = bytestream2_get_le32(gb); | |
300 | int skip, copy; | |
301 | ||
302 | while (segments--) { | |
303 | if (bytestream2_get_bytes_left(gb) < 2) | |
304 | return AVERROR_INVALIDDATA; | |
305 | copy = bytestream2_get_byteu(gb) * 2; | |
306 | skip = bytestream2_get_byteu(gb) * 2; | |
307 | if (frame_end - frame < copy + skip || | |
308 | bytestream2_get_bytes_left(gb) < copy) | |
309 | return AVERROR_INVALIDDATA; | |
310 | frame += skip; | |
311 | bytestream2_get_buffer(gb, frame, copy); | |
312 | frame += copy; | |
313 | } | |
314 | ||
315 | return 0; | |
316 | } | |
317 | ||
318 | static int decode_blck(GetByteContext *gb, uint8_t *frame, int width, int height) | |
319 | { | |
320 | memset(frame, 0, width * height); | |
321 | return 0; | |
322 | } | |
323 | ||
324 | ||
325 | typedef int (*chunk_decoder)(GetByteContext *gb, uint8_t *frame, int width, int height); | |
326 | ||
327 | static const chunk_decoder decoder[8] = { | |
328 | decode_copy, decode_tsw1, decode_bdlt, decode_wdlt, | |
329 | decode_tdlt, decode_dsw1, decode_blck, decode_dds1, | |
330 | }; | |
331 | ||
332 | static const char* chunk_name[8] = { | |
333 | "COPY", "TSW1", "BDLT", "WDLT", "TDLT", "DSW1", "BLCK", "DDS1" | |
334 | }; | |
335 | ||
336 | static int dfa_decode_frame(AVCodecContext *avctx, | |
337 | void *data, int *got_frame, | |
338 | AVPacket *avpkt) | |
339 | { | |
340 | AVFrame *frame = data; | |
341 | DfaContext *s = avctx->priv_data; | |
342 | GetByteContext gb; | |
343 | const uint8_t *buf = avpkt->data; | |
344 | uint32_t chunk_type, chunk_size; | |
345 | uint8_t *dst; | |
346 | int ret; | |
347 | int i, pal_elems; | |
348 | int version = avctx->extradata_size==2 ? AV_RL16(avctx->extradata) : 0; | |
349 | ||
350 | if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) | |
351 | return ret; | |
352 | ||
353 | bytestream2_init(&gb, avpkt->data, avpkt->size); | |
354 | while (bytestream2_get_bytes_left(&gb) > 0) { | |
355 | bytestream2_skip(&gb, 4); | |
356 | chunk_size = bytestream2_get_le32(&gb); | |
357 | chunk_type = bytestream2_get_le32(&gb); | |
358 | if (!chunk_type) | |
359 | break; | |
360 | if (chunk_type == 1) { | |
361 | pal_elems = FFMIN(chunk_size / 3, 256); | |
362 | for (i = 0; i < pal_elems; i++) { | |
363 | s->pal[i] = bytestream2_get_be24(&gb) << 2; | |
364 | s->pal[i] |= 0xFFU << 24 | (s->pal[i] >> 6) & 0x30303; | |
365 | } | |
366 | frame->palette_has_changed = 1; | |
367 | } else if (chunk_type <= 9) { | |
368 | if (decoder[chunk_type - 2](&gb, s->frame_buf, avctx->width, avctx->height)) { | |
369 | av_log(avctx, AV_LOG_ERROR, "Error decoding %s chunk\n", | |
370 | chunk_name[chunk_type - 2]); | |
371 | return AVERROR_INVALIDDATA; | |
372 | } | |
373 | } else { | |
374 | av_log(avctx, AV_LOG_WARNING, | |
375 | "Ignoring unknown chunk type %"PRIu32"\n", | |
376 | chunk_type); | |
377 | } | |
378 | buf += chunk_size; | |
379 | } | |
380 | ||
381 | buf = s->frame_buf; | |
382 | dst = frame->data[0]; | |
383 | for (i = 0; i < avctx->height; i++) { | |
384 | if(version == 0x100) { | |
385 | int j; | |
386 | for(j = 0; j < avctx->width; j++) { | |
387 | dst[j] = buf[ (i&3)*(avctx->width /4) + (j/4) + | |
388 | ((j&3)*(avctx->height/4) + (i/4))*avctx->width]; | |
389 | } | |
390 | } else { | |
391 | memcpy(dst, buf, avctx->width); | |
392 | buf += avctx->width; | |
393 | } | |
394 | dst += frame->linesize[0]; | |
395 | } | |
396 | memcpy(frame->data[1], s->pal, sizeof(s->pal)); | |
397 | ||
398 | *got_frame = 1; | |
399 | ||
400 | return avpkt->size; | |
401 | } | |
402 | ||
403 | static av_cold int dfa_decode_end(AVCodecContext *avctx) | |
404 | { | |
405 | DfaContext *s = avctx->priv_data; | |
406 | ||
407 | av_freep(&s->frame_buf); | |
408 | ||
409 | return 0; | |
410 | } | |
411 | ||
412 | AVCodec ff_dfa_decoder = { | |
413 | .name = "dfa", | |
414 | .long_name = NULL_IF_CONFIG_SMALL("Chronomaster DFA"), | |
415 | .type = AVMEDIA_TYPE_VIDEO, | |
416 | .id = AV_CODEC_ID_DFA, | |
417 | .priv_data_size = sizeof(DfaContext), | |
418 | .init = dfa_decode_init, | |
419 | .close = dfa_decode_end, | |
420 | .decode = dfa_decode_frame, | |
421 | .capabilities = CODEC_CAP_DR1, | |
422 | }; |