/src/ffmpeg/libavcodec/hnm4video.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Cryo Interactive Entertainment HNM4 video decoder |
3 | | * |
4 | | * Copyright (c) 2012 David Kment |
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 <string.h> |
24 | | |
25 | | #include "libavutil/imgutils.h" |
26 | | #include "libavutil/internal.h" |
27 | | #include "libavutil/intreadwrite.h" |
28 | | #include "libavutil/mem.h" |
29 | | #include "avcodec.h" |
30 | | #include "bytestream.h" |
31 | | #include "codec_internal.h" |
32 | | #include "decode.h" |
33 | | |
34 | 149k | #define HNM4_CHUNK_ID_PL 19536 |
35 | 27.8k | #define HNM4_CHUNK_ID_IZ 23113 |
36 | 22.7k | #define HNM4_CHUNK_ID_IU 21833 |
37 | | #define HNM4_CHUNK_ID_SD 17491 |
38 | | |
39 | | typedef struct Hnm4VideoContext { |
40 | | uint8_t version; |
41 | | int width; |
42 | | int height; |
43 | | uint8_t *current; |
44 | | uint8_t *previous; |
45 | | uint8_t *buffer1; |
46 | | uint8_t *buffer2; |
47 | | uint8_t *processed; |
48 | | uint32_t palette[256]; |
49 | | } Hnm4VideoContext; |
50 | | |
51 | | static int getbit(GetByteContext *gb, uint32_t *bitbuf, int *bits) |
52 | 558k | { |
53 | 558k | int ret; |
54 | | |
55 | 558k | if (!*bits) { |
56 | 20.6k | *bitbuf = bytestream2_get_le32(gb); |
57 | 20.6k | *bits = 32; |
58 | 20.6k | } |
59 | | |
60 | 558k | ret = *bitbuf >> 31; |
61 | 558k | *bitbuf <<= 1; |
62 | 558k | (*bits)--; |
63 | | |
64 | 558k | return ret; |
65 | 558k | } |
66 | | |
67 | | static void unpack_intraframe(AVCodecContext *avctx, const uint8_t *src, |
68 | | uint32_t size) |
69 | 4.70k | { |
70 | 4.70k | Hnm4VideoContext *hnm = avctx->priv_data; |
71 | 4.70k | GetByteContext gb; |
72 | 4.70k | uint32_t bitbuf = 0, writeoffset = 0, count = 0; |
73 | 4.70k | uint16_t word; |
74 | 4.70k | int32_t offset; |
75 | 4.70k | int bits = 0; |
76 | | |
77 | 4.70k | bytestream2_init(&gb, src, size); |
78 | | |
79 | 326k | while (bytestream2_tell(&gb) < size) { |
80 | 325k | if (getbit(&gb, &bitbuf, &bits)) { |
81 | 217k | if (writeoffset >= hnm->width * hnm->height) { |
82 | 229 | av_log(avctx, AV_LOG_ERROR, |
83 | 229 | "Attempting to write out of bounds\n"); |
84 | 229 | break; |
85 | 229 | } |
86 | 217k | hnm->current[writeoffset++] = bytestream2_get_byte(&gb); |
87 | 217k | } else { |
88 | 108k | if (getbit(&gb, &bitbuf, &bits)) { |
89 | 45.5k | word = bytestream2_get_le16(&gb); |
90 | 45.5k | count = word & 0x07; |
91 | 45.5k | offset = (word >> 3) - 0x2000; |
92 | 45.5k | if (!count) |
93 | 8.34k | count = bytestream2_get_byte(&gb); |
94 | 45.5k | if (!count) |
95 | 256 | return; |
96 | 62.5k | } else { |
97 | 62.5k | count = getbit(&gb, &bitbuf, &bits) * 2; |
98 | 62.5k | count += getbit(&gb, &bitbuf, &bits); |
99 | 62.5k | offset = bytestream2_get_byte(&gb) - 0x0100; |
100 | 62.5k | } |
101 | 107k | count += 2; |
102 | 107k | offset += writeoffset; |
103 | 107k | if (offset < 0 || offset + count >= hnm->width * hnm->height) { |
104 | 2.46k | av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n"); |
105 | 2.46k | break; |
106 | 105k | } else if (writeoffset + count >= hnm->width * hnm->height) { |
107 | 206 | av_log(avctx, AV_LOG_ERROR, |
108 | 206 | "Attempting to write out of bounds\n"); |
109 | 206 | break; |
110 | 206 | } |
111 | 1.91M | while (count--) { |
112 | 1.81M | hnm->current[writeoffset++] = hnm->current[offset++]; |
113 | 1.81M | } |
114 | 105k | } |
115 | 325k | } |
116 | 4.70k | } |
117 | | |
118 | | static void postprocess_current_frame(AVCodecContext *avctx) |
119 | 9.25k | { |
120 | 9.25k | Hnm4VideoContext *hnm = avctx->priv_data; |
121 | 9.25k | uint32_t x, y, src_y; |
122 | 9.25k | int width = hnm->width; |
123 | | |
124 | 110M | for (y = 0; y < hnm->height; y++) { |
125 | 110M | uint8_t *dst = hnm->processed + y * width; |
126 | 110M | const uint8_t *src = hnm->current; |
127 | 110M | src_y = y - (y % 2); |
128 | 110M | src += src_y * width + (y % 2); |
129 | 10.6G | for (x = 0; x < width; x++) { |
130 | 10.5G | dst[x] = *src; |
131 | 10.5G | src += 2; |
132 | 10.5G | } |
133 | 110M | } |
134 | 9.25k | } |
135 | | |
136 | | static void copy_processed_frame(AVCodecContext *avctx, AVFrame *frame) |
137 | 12.6k | { |
138 | 12.6k | Hnm4VideoContext *hnm = avctx->priv_data; |
139 | 12.6k | uint8_t *src = hnm->processed; |
140 | 12.6k | uint8_t *dst = frame->data[0]; |
141 | 12.6k | int y; |
142 | | |
143 | 115M | for (y = 0; y < hnm->height; y++) { |
144 | 115M | memcpy(dst, src, hnm->width); |
145 | 115M | src += hnm->width; |
146 | 115M | dst += frame->linesize[0]; |
147 | 115M | } |
148 | 12.6k | } |
149 | | |
150 | | static int decode_interframe_v4(AVCodecContext *avctx, const uint8_t *src, uint32_t size) |
151 | 8.92k | { |
152 | 8.92k | Hnm4VideoContext *hnm = avctx->priv_data; |
153 | 8.92k | GetByteContext gb; |
154 | 8.92k | uint32_t writeoffset = 0; |
155 | 8.92k | int count, left, offset; |
156 | 8.92k | uint8_t tag, previous, backline, backward, swap; |
157 | | |
158 | 8.92k | bytestream2_init(&gb, src, size); |
159 | | |
160 | 217k | while (bytestream2_tell(&gb) < size) { |
161 | 213k | count = bytestream2_peek_byte(&gb) & 0x1F; |
162 | 213k | if (count == 0) { |
163 | 137k | tag = bytestream2_get_byte(&gb) & 0xE0; |
164 | 137k | tag = tag >> 5; |
165 | | |
166 | 137k | if (tag == 0) { |
167 | 110k | if (writeoffset + 2 > hnm->width * hnm->height) { |
168 | 198 | av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n"); |
169 | 198 | return AVERROR_INVALIDDATA; |
170 | 198 | } |
171 | 110k | hnm->current[writeoffset++] = bytestream2_get_byte(&gb); |
172 | 110k | hnm->current[writeoffset++] = bytestream2_get_byte(&gb); |
173 | 110k | } else if (tag == 1) { |
174 | 1.36k | writeoffset += bytestream2_get_byte(&gb) * 2; |
175 | 26.0k | } else if (tag == 2) { |
176 | 8.87k | count = bytestream2_get_le16(&gb); |
177 | 8.87k | count *= 2; |
178 | 8.87k | writeoffset += count; |
179 | 17.1k | } else if (tag == 3) { |
180 | 16.8k | count = bytestream2_get_byte(&gb) * 2; |
181 | 16.8k | if (writeoffset + count > hnm->width * hnm->height) { |
182 | 206 | av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n"); |
183 | 206 | return AVERROR_INVALIDDATA; |
184 | 206 | } |
185 | 3.25M | while (count > 0) { |
186 | 3.23M | hnm->current[writeoffset++] = bytestream2_peek_byte(&gb); |
187 | 3.23M | count--; |
188 | 3.23M | } |
189 | 16.6k | bytestream2_skip(&gb, 1); |
190 | 16.6k | } else { |
191 | 366 | break; |
192 | 366 | } |
193 | 137k | if (writeoffset > hnm->width * hnm->height) { |
194 | 457 | av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n"); |
195 | 457 | return AVERROR_INVALIDDATA; |
196 | 457 | } |
197 | 137k | } else { |
198 | 75.2k | previous = bytestream2_peek_byte(&gb) & 0x20; |
199 | 75.2k | backline = bytestream2_peek_byte(&gb) & 0x40; |
200 | 75.2k | backward = bytestream2_peek_byte(&gb) & 0x80; |
201 | 75.2k | bytestream2_skip(&gb, 1); |
202 | 75.2k | swap = bytestream2_peek_byte(&gb) & 0x01; |
203 | 75.2k | offset = bytestream2_get_le16(&gb); |
204 | 75.2k | offset = (offset >> 1) & 0x7FFF; |
205 | 75.2k | offset = writeoffset + (offset * 2) - 0x8000; |
206 | | |
207 | 75.2k | left = count; |
208 | | |
209 | 75.2k | if (!backward && offset + 2*count > hnm->width * hnm->height) { |
210 | 293 | av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n"); |
211 | 293 | return AVERROR_INVALIDDATA; |
212 | 74.9k | } else if (backward && offset + 1 >= hnm->width * hnm->height) { |
213 | 233 | av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n"); |
214 | 233 | return AVERROR_INVALIDDATA; |
215 | 74.7k | } else if (writeoffset + 2*count > hnm->width * hnm->height) { |
216 | 236 | av_log(avctx, AV_LOG_ERROR, |
217 | 236 | "Attempting to write out of bounds\n"); |
218 | 236 | return AVERROR_INVALIDDATA; |
219 | | |
220 | 236 | } |
221 | 74.5k | if(backward) { |
222 | 30.2k | if (offset < (!!backline)*(2 * hnm->width - 1) + 2*(left-1)) { |
223 | 327 | av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n"); |
224 | 327 | return AVERROR_INVALIDDATA; |
225 | 327 | } |
226 | 44.2k | } else { |
227 | 44.2k | if (offset < (!!backline)*(2 * hnm->width - 1)) { |
228 | 2.05k | av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n"); |
229 | 2.05k | return AVERROR_INVALIDDATA; |
230 | 2.05k | } |
231 | 44.2k | } |
232 | | |
233 | 72.1k | if (previous) { |
234 | 787k | while (left > 0) { |
235 | 750k | if (backline) { |
236 | 570k | hnm->current[writeoffset++] = hnm->previous[offset - (2 * hnm->width) + 1]; |
237 | 570k | hnm->current[writeoffset++] = hnm->previous[offset++]; |
238 | 570k | offset++; |
239 | 570k | } else { |
240 | 180k | hnm->current[writeoffset++] = hnm->previous[offset++]; |
241 | 180k | hnm->current[writeoffset++] = hnm->previous[offset++]; |
242 | 180k | } |
243 | 750k | if (backward) |
244 | 526k | offset -= 4; |
245 | 750k | left--; |
246 | 750k | } |
247 | 36.4k | } else { |
248 | 565k | while (left > 0) { |
249 | 530k | if (backline) { |
250 | 359k | hnm->current[writeoffset++] = hnm->current[offset - (2 * hnm->width) + 1]; |
251 | 359k | hnm->current[writeoffset++] = hnm->current[offset++]; |
252 | 359k | offset++; |
253 | 359k | } else { |
254 | 171k | hnm->current[writeoffset++] = hnm->current[offset++]; |
255 | 171k | hnm->current[writeoffset++] = hnm->current[offset++]; |
256 | 171k | } |
257 | 530k | if (backward) |
258 | 167k | offset -= 4; |
259 | 530k | left--; |
260 | 530k | } |
261 | 35.6k | } |
262 | | |
263 | 72.1k | if (swap) { |
264 | 28.3k | left = count; |
265 | 28.3k | writeoffset -= count * 2; |
266 | 620k | while (left > 0) { |
267 | 591k | swap = hnm->current[writeoffset]; |
268 | 591k | hnm->current[writeoffset] = hnm->current[writeoffset + 1]; |
269 | 591k | hnm->current[writeoffset + 1] = swap; |
270 | 591k | left--; |
271 | 591k | writeoffset += 2; |
272 | 591k | } |
273 | 28.3k | } |
274 | 72.1k | } |
275 | 213k | } |
276 | 4.92k | return 0; |
277 | 8.92k | } |
278 | | |
279 | | static void decode_interframe_v4a(AVCodecContext *avctx, const uint8_t *src, |
280 | | uint32_t size) |
281 | 3.01k | { |
282 | 3.01k | Hnm4VideoContext *hnm = avctx->priv_data; |
283 | 3.01k | GetByteContext gb; |
284 | 3.01k | uint32_t writeoffset = 0, offset; |
285 | 3.01k | uint8_t tag, count, previous, delta; |
286 | | |
287 | 3.01k | bytestream2_init(&gb, src, size); |
288 | | |
289 | 192k | while (bytestream2_tell(&gb) < size) { |
290 | 190k | count = bytestream2_peek_byte(&gb) & 0x3F; |
291 | 190k | if (count == 0) { |
292 | 65.2k | tag = bytestream2_get_byte(&gb) & 0xC0; |
293 | 65.2k | tag = tag >> 6; |
294 | 65.2k | if (tag == 0) { |
295 | 60.5k | writeoffset += bytestream2_get_byte(&gb); |
296 | 60.5k | } else if (tag == 1) { |
297 | 3.28k | if (writeoffset + hnm->width >= hnm->width * hnm->height) { |
298 | 229 | av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n"); |
299 | 229 | break; |
300 | 229 | } |
301 | 3.05k | hnm->current[writeoffset] = bytestream2_get_byte(&gb); |
302 | 3.05k | hnm->current[writeoffset + hnm->width] = bytestream2_get_byte(&gb); |
303 | 3.05k | writeoffset++; |
304 | 3.05k | } else if (tag == 2) { |
305 | 1.14k | writeoffset += hnm->width; |
306 | 1.14k | } else if (tag == 3) { |
307 | 231 | break; |
308 | 231 | } |
309 | 64.7k | if (writeoffset > hnm->width * hnm->height) { |
310 | 246 | av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n"); |
311 | 246 | break; |
312 | 246 | } |
313 | 125k | } else { |
314 | 125k | delta = bytestream2_peek_byte(&gb) & 0x80; |
315 | 125k | previous = bytestream2_peek_byte(&gb) & 0x40; |
316 | 125k | bytestream2_skip(&gb, 1); |
317 | | |
318 | 125k | offset = writeoffset; |
319 | 125k | offset += bytestream2_get_le16(&gb); |
320 | | |
321 | 125k | if (delta) { |
322 | 109k | if (offset < 0x10000) { |
323 | 337 | av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n"); |
324 | 337 | break; |
325 | 337 | } |
326 | 108k | offset -= 0x10000; |
327 | 108k | } |
328 | | |
329 | 125k | if (offset + hnm->width + count >= hnm->width * hnm->height) { |
330 | 292 | av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n"); |
331 | 292 | break; |
332 | 124k | } else if (writeoffset + hnm->width + count >= hnm->width * hnm->height) { |
333 | 216 | av_log(avctx, AV_LOG_ERROR, "Attempting to write out of bounds\n"); |
334 | 216 | break; |
335 | 216 | } |
336 | | |
337 | 124k | if (previous) { |
338 | 6.12M | while (count > 0) { |
339 | 6.01M | hnm->current[writeoffset] = hnm->previous[offset]; |
340 | 6.01M | hnm->current[writeoffset + hnm->width] = hnm->previous[offset + hnm->width]; |
341 | 6.01M | writeoffset++; |
342 | 6.01M | offset++; |
343 | 6.01M | count--; |
344 | 6.01M | } |
345 | 107k | } else { |
346 | 647k | while (count > 0) { |
347 | 630k | hnm->current[writeoffset] = hnm->current[offset]; |
348 | 630k | hnm->current[writeoffset + hnm->width] = hnm->current[offset + hnm->width]; |
349 | 630k | writeoffset++; |
350 | 630k | offset++; |
351 | 630k | count--; |
352 | 630k | } |
353 | 17.1k | } |
354 | 124k | } |
355 | 190k | } |
356 | 3.01k | } |
357 | | |
358 | | static void hnm_update_palette(AVCodecContext *avctx, const uint8_t *src, |
359 | | uint32_t size) |
360 | 121k | { |
361 | 121k | Hnm4VideoContext *hnm = avctx->priv_data; |
362 | 121k | GetByteContext gb; |
363 | 121k | uint8_t start, writeoffset; |
364 | 121k | uint16_t count; |
365 | 121k | int eight_bit_colors; |
366 | | |
367 | 121k | eight_bit_colors = src[7] & 0x80 && hnm->version == 0x4a; |
368 | | |
369 | | // skip first 8 bytes |
370 | 121k | bytestream2_init(&gb, src + 8, size - 8); |
371 | | |
372 | 190k | while (bytestream2_tell(&gb) < size - 8) { |
373 | 69.3k | start = bytestream2_get_byte(&gb); |
374 | 69.3k | count = bytestream2_get_byte(&gb); |
375 | 69.3k | if (start == 255 && count == 255) |
376 | 515 | break; |
377 | 68.8k | if (count == 0) |
378 | 21.1k | count = 256; |
379 | 68.8k | writeoffset = start; |
380 | 16.6M | while (count > 0) { |
381 | 16.5M | hnm->palette[writeoffset] = bytestream2_get_be24(&gb); |
382 | 16.5M | if (!eight_bit_colors) |
383 | 16.5M | hnm->palette[writeoffset] <<= 2; |
384 | 16.5M | hnm->palette[writeoffset] |= (0xFFU << 24); |
385 | 16.5M | count--; |
386 | 16.5M | writeoffset++; |
387 | 16.5M | } |
388 | 68.8k | } |
389 | 121k | } |
390 | | |
391 | | static int hnm_decode_frame(AVCodecContext *avctx, AVFrame *frame, |
392 | | int *got_frame, AVPacket *avpkt) |
393 | 198k | { |
394 | 198k | Hnm4VideoContext *hnm = avctx->priv_data; |
395 | 198k | int ret; |
396 | 198k | uint16_t chunk_id; |
397 | | |
398 | 198k | if (avpkt->size < 8) { |
399 | 48.4k | av_log(avctx, AV_LOG_ERROR, "packet too small\n"); |
400 | 48.4k | return AVERROR_INVALIDDATA; |
401 | 48.4k | } |
402 | | |
403 | 149k | chunk_id = AV_RL16(avpkt->data + 4); |
404 | | |
405 | 149k | if (chunk_id == HNM4_CHUNK_ID_PL) { |
406 | 121k | hnm_update_palette(avctx, avpkt->data, avpkt->size); |
407 | 121k | } else if (chunk_id == HNM4_CHUNK_ID_IZ) { |
408 | 5.06k | if (avpkt->size < 12) { |
409 | 264 | av_log(avctx, AV_LOG_ERROR, "packet too small\n"); |
410 | 264 | return AVERROR_INVALIDDATA; |
411 | 264 | } |
412 | 4.79k | if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) |
413 | 93 | return ret; |
414 | | |
415 | 4.70k | unpack_intraframe(avctx, avpkt->data + 12, avpkt->size - 12); |
416 | 4.70k | memcpy(hnm->previous, hnm->current, hnm->width * hnm->height); |
417 | 4.70k | if (hnm->version == 0x4a) |
418 | 371 | memcpy(hnm->processed, hnm->current, hnm->width * hnm->height); |
419 | 4.33k | else |
420 | 4.33k | postprocess_current_frame(avctx); |
421 | 4.70k | copy_processed_frame(avctx, frame); |
422 | 4.70k | frame->pict_type = AV_PICTURE_TYPE_I; |
423 | 4.70k | frame->flags |= AV_FRAME_FLAG_KEY; |
424 | 4.70k | memcpy(frame->data[1], hnm->palette, 256 * 4); |
425 | 4.70k | *got_frame = 1; |
426 | 22.7k | } else if (chunk_id == HNM4_CHUNK_ID_IU) { |
427 | 12.1k | if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) |
428 | 190 | return ret; |
429 | | |
430 | 11.9k | if (hnm->version == 0x4a) { |
431 | 3.01k | decode_interframe_v4a(avctx, avpkt->data + 8, avpkt->size - 8); |
432 | 3.01k | memcpy(hnm->processed, hnm->current, hnm->width * hnm->height); |
433 | 8.92k | } else { |
434 | 8.92k | int ret = decode_interframe_v4(avctx, avpkt->data + 8, avpkt->size - 8); |
435 | 8.92k | if (ret < 0) |
436 | 4.00k | return ret; |
437 | 4.92k | postprocess_current_frame(avctx); |
438 | 4.92k | } |
439 | 7.94k | copy_processed_frame(avctx, frame); |
440 | 7.94k | frame->pict_type = AV_PICTURE_TYPE_P; |
441 | 7.94k | frame->flags &= ~AV_FRAME_FLAG_KEY; |
442 | 7.94k | memcpy(frame->data[1], hnm->palette, 256 * 4); |
443 | 7.94k | *got_frame = 1; |
444 | 7.94k | FFSWAP(uint8_t *, hnm->current, hnm->previous); |
445 | 10.6k | } else { |
446 | 10.6k | av_log(avctx, AV_LOG_ERROR, "invalid chunk id: %d\n", chunk_id); |
447 | 10.6k | return AVERROR_INVALIDDATA; |
448 | 10.6k | } |
449 | | |
450 | 134k | return avpkt->size; |
451 | 149k | } |
452 | | |
453 | | static av_cold int hnm_decode_init(AVCodecContext *avctx) |
454 | 975 | { |
455 | 975 | Hnm4VideoContext *hnm = avctx->priv_data; |
456 | 975 | int ret; |
457 | | |
458 | 975 | if (avctx->extradata_size < 1) { |
459 | 173 | av_log(avctx, AV_LOG_ERROR, |
460 | 173 | "Extradata missing, decoder requires version number\n"); |
461 | 173 | return AVERROR_INVALIDDATA; |
462 | 173 | } |
463 | | |
464 | 802 | ret = av_image_check_size(avctx->width, avctx->height, 0, avctx); |
465 | 802 | if (ret < 0) |
466 | 19 | return ret; |
467 | 783 | if (avctx->height & 1) |
468 | 1 | return AVERROR(EINVAL); |
469 | | |
470 | 782 | hnm->version = avctx->extradata[0]; |
471 | 782 | avctx->pix_fmt = AV_PIX_FMT_PAL8; |
472 | 782 | hnm->width = avctx->width; |
473 | 782 | hnm->height = avctx->height; |
474 | 782 | hnm->buffer1 = av_mallocz(avctx->width * avctx->height); |
475 | 782 | hnm->buffer2 = av_mallocz(avctx->width * avctx->height); |
476 | 782 | hnm->processed = av_mallocz(avctx->width * avctx->height); |
477 | | |
478 | 782 | if (!hnm->buffer1 || !hnm->buffer2 || !hnm->processed) { |
479 | 0 | av_log(avctx, AV_LOG_ERROR, "av_mallocz() failed\n"); |
480 | 0 | return AVERROR(ENOMEM); |
481 | 0 | } |
482 | | |
483 | 782 | hnm->current = hnm->buffer1; |
484 | 782 | hnm->previous = hnm->buffer2; |
485 | | |
486 | 782 | return 0; |
487 | 782 | } |
488 | | |
489 | | static av_cold int hnm_decode_end(AVCodecContext *avctx) |
490 | 975 | { |
491 | 975 | Hnm4VideoContext *hnm = avctx->priv_data; |
492 | | |
493 | 975 | av_freep(&hnm->buffer1); |
494 | 975 | av_freep(&hnm->buffer2); |
495 | 975 | av_freep(&hnm->processed); |
496 | | |
497 | 975 | return 0; |
498 | 975 | } |
499 | | |
500 | | const FFCodec ff_hnm4_video_decoder = { |
501 | | .p.name = "hnm4video", |
502 | | CODEC_LONG_NAME("HNM 4 video"), |
503 | | .p.type = AVMEDIA_TYPE_VIDEO, |
504 | | .p.id = AV_CODEC_ID_HNM4_VIDEO, |
505 | | .priv_data_size = sizeof(Hnm4VideoContext), |
506 | | .init = hnm_decode_init, |
507 | | .close = hnm_decode_end, |
508 | | FF_CODEC_DECODE_CB(hnm_decode_frame), |
509 | | .p.capabilities = AV_CODEC_CAP_DR1, |
510 | | .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, |
511 | | }; |