/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 | 128k | #define HNM4_CHUNK_ID_PL 19536 |
35 | 26.8k | #define HNM4_CHUNK_ID_IZ 23113 |
36 | 21.1k | #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 | 319k | { |
53 | 319k | int ret; |
54 | | |
55 | 319k | if (!*bits) { |
56 | 13.4k | *bitbuf = bytestream2_get_le32(gb); |
57 | 13.4k | *bits = 32; |
58 | 13.4k | } |
59 | | |
60 | 319k | ret = *bitbuf >> 31; |
61 | 319k | *bitbuf <<= 1; |
62 | 319k | (*bits)--; |
63 | | |
64 | 319k | return ret; |
65 | 319k | } |
66 | | |
67 | | static void unpack_intraframe(AVCodecContext *avctx, const uint8_t *src, |
68 | | uint32_t size) |
69 | 5.19k | { |
70 | 5.19k | Hnm4VideoContext *hnm = avctx->priv_data; |
71 | 5.19k | GetByteContext gb; |
72 | 5.19k | uint32_t bitbuf = 0, writeoffset = 0, count = 0; |
73 | 5.19k | uint16_t word; |
74 | 5.19k | int32_t offset; |
75 | 5.19k | int bits = 0; |
76 | | |
77 | 5.19k | bytestream2_init(&gb, src, size); |
78 | | |
79 | 194k | while (bytestream2_tell(&gb) < size) { |
80 | 192k | if (getbit(&gb, &bitbuf, &bits)) { |
81 | 137k | if (writeoffset >= hnm->width * hnm->height) { |
82 | 227 | av_log(avctx, AV_LOG_ERROR, |
83 | 227 | "Attempting to write out of bounds\n"); |
84 | 227 | break; |
85 | 227 | } |
86 | 136k | hnm->current[writeoffset++] = bytestream2_get_byte(&gb); |
87 | 136k | } else { |
88 | 55.3k | if (getbit(&gb, &bitbuf, &bits)) { |
89 | 19.3k | word = bytestream2_get_le16(&gb); |
90 | 19.3k | count = word & 0x07; |
91 | 19.3k | offset = (word >> 3) - 0x2000; |
92 | 19.3k | if (!count) |
93 | 3.88k | count = bytestream2_get_byte(&gb); |
94 | 19.3k | if (!count) |
95 | 261 | return; |
96 | 35.9k | } else { |
97 | 35.9k | count = getbit(&gb, &bitbuf, &bits) * 2; |
98 | 35.9k | count += getbit(&gb, &bitbuf, &bits); |
99 | 35.9k | offset = bytestream2_get_byte(&gb) - 0x0100; |
100 | 35.9k | } |
101 | 55.0k | count += 2; |
102 | 55.0k | offset += writeoffset; |
103 | 55.0k | if (offset < 0 || offset + count >= hnm->width * hnm->height) { |
104 | 2.77k | av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n"); |
105 | 2.77k | break; |
106 | 52.2k | } else if (writeoffset + count >= hnm->width * hnm->height) { |
107 | 213 | av_log(avctx, AV_LOG_ERROR, |
108 | 213 | "Attempting to write out of bounds\n"); |
109 | 213 | break; |
110 | 213 | } |
111 | 921k | while (count--) { |
112 | 869k | hnm->current[writeoffset++] = hnm->current[offset++]; |
113 | 869k | } |
114 | 52.0k | } |
115 | 192k | } |
116 | 5.19k | } |
117 | | |
118 | | static void postprocess_current_frame(AVCodecContext *avctx) |
119 | 10.9k | { |
120 | 10.9k | Hnm4VideoContext *hnm = avctx->priv_data; |
121 | 10.9k | uint32_t x, y, src_y; |
122 | 10.9k | int width = hnm->width; |
123 | | |
124 | 149M | for (y = 0; y < hnm->height; y++) { |
125 | 149M | uint8_t *dst = hnm->processed + y * width; |
126 | 149M | const uint8_t *src = hnm->current; |
127 | 149M | src_y = y - (y % 2); |
128 | 149M | src += src_y * width + (y % 2); |
129 | 12.3G | for (x = 0; x < width; x++) { |
130 | 12.2G | dst[x] = *src; |
131 | 12.2G | src += 2; |
132 | 12.2G | } |
133 | 149M | } |
134 | 10.9k | } |
135 | | |
136 | | static void copy_processed_frame(AVCodecContext *avctx, AVFrame *frame) |
137 | 15.3k | { |
138 | 15.3k | Hnm4VideoContext *hnm = avctx->priv_data; |
139 | 15.3k | uint8_t *src = hnm->processed; |
140 | 15.3k | uint8_t *dst = frame->data[0]; |
141 | 15.3k | int y; |
142 | | |
143 | 159M | for (y = 0; y < hnm->height; y++) { |
144 | 159M | memcpy(dst, src, hnm->width); |
145 | 159M | src += hnm->width; |
146 | 159M | dst += frame->linesize[0]; |
147 | 159M | } |
148 | 15.3k | } |
149 | | |
150 | | static int decode_interframe_v4(AVCodecContext *avctx, const uint8_t *src, uint32_t size) |
151 | 10.8k | { |
152 | 10.8k | Hnm4VideoContext *hnm = avctx->priv_data; |
153 | 10.8k | GetByteContext gb; |
154 | 10.8k | uint32_t writeoffset = 0; |
155 | 10.8k | int count, left, offset; |
156 | 10.8k | uint8_t tag, previous, backline, backward, swap; |
157 | | |
158 | 10.8k | bytestream2_init(&gb, src, size); |
159 | | |
160 | 266k | while (bytestream2_tell(&gb) < size) { |
161 | 260k | count = bytestream2_peek_byte(&gb) & 0x1F; |
162 | 260k | if (count == 0) { |
163 | 170k | tag = bytestream2_get_byte(&gb) & 0xE0; |
164 | 170k | tag = tag >> 5; |
165 | | |
166 | 170k | if (tag == 0) { |
167 | 136k | if (writeoffset + 2 > hnm->width * hnm->height) { |
168 | 200 | av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n"); |
169 | 200 | return AVERROR_INVALIDDATA; |
170 | 200 | } |
171 | 136k | hnm->current[writeoffset++] = bytestream2_get_byte(&gb); |
172 | 136k | hnm->current[writeoffset++] = bytestream2_get_byte(&gb); |
173 | 136k | } else if (tag == 1) { |
174 | 1.66k | writeoffset += bytestream2_get_byte(&gb) * 2; |
175 | 32.9k | } else if (tag == 2) { |
176 | 10.6k | count = bytestream2_get_le16(&gb); |
177 | 10.6k | count *= 2; |
178 | 10.6k | writeoffset += count; |
179 | 22.2k | } else if (tag == 3) { |
180 | 21.8k | count = bytestream2_get_byte(&gb) * 2; |
181 | 21.8k | if (writeoffset + count > hnm->width * hnm->height) { |
182 | 225 | av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n"); |
183 | 225 | return AVERROR_INVALIDDATA; |
184 | 225 | } |
185 | 4.25M | while (count > 0) { |
186 | 4.23M | hnm->current[writeoffset++] = bytestream2_peek_byte(&gb); |
187 | 4.23M | count--; |
188 | 4.23M | } |
189 | 21.6k | bytestream2_skip(&gb, 1); |
190 | 21.6k | } else { |
191 | 412 | break; |
192 | 412 | } |
193 | 170k | if (writeoffset > hnm->width * hnm->height) { |
194 | 444 | av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n"); |
195 | 444 | return AVERROR_INVALIDDATA; |
196 | 444 | } |
197 | 170k | } else { |
198 | 89.6k | previous = bytestream2_peek_byte(&gb) & 0x20; |
199 | 89.6k | backline = bytestream2_peek_byte(&gb) & 0x40; |
200 | 89.6k | backward = bytestream2_peek_byte(&gb) & 0x80; |
201 | 89.6k | bytestream2_skip(&gb, 1); |
202 | 89.6k | swap = bytestream2_peek_byte(&gb) & 0x01; |
203 | 89.6k | offset = bytestream2_get_le16(&gb); |
204 | 89.6k | offset = (offset >> 1) & 0x7FFF; |
205 | 89.6k | offset = writeoffset + (offset * 2) - 0x8000; |
206 | | |
207 | 89.6k | left = count; |
208 | | |
209 | 89.6k | if (!backward && offset + 2*count > hnm->width * hnm->height) { |
210 | 284 | av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n"); |
211 | 284 | return AVERROR_INVALIDDATA; |
212 | 89.3k | } else if (backward && offset + 1 >= hnm->width * hnm->height) { |
213 | 249 | av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n"); |
214 | 249 | return AVERROR_INVALIDDATA; |
215 | 89.0k | } else if (writeoffset + 2*count > hnm->width * hnm->height) { |
216 | 249 | av_log(avctx, AV_LOG_ERROR, |
217 | 249 | "Attempting to write out of bounds\n"); |
218 | 249 | return AVERROR_INVALIDDATA; |
219 | | |
220 | 249 | } |
221 | 88.8k | if(backward) { |
222 | 37.4k | if (offset < (!!backline)*(2 * hnm->width - 1) + 2*(left-1)) { |
223 | 354 | av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n"); |
224 | 354 | return AVERROR_INVALIDDATA; |
225 | 354 | } |
226 | 51.3k | } else { |
227 | 51.3k | if (offset < (!!backline)*(2 * hnm->width - 1)) { |
228 | 2.60k | av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n"); |
229 | 2.60k | return AVERROR_INVALIDDATA; |
230 | 2.60k | } |
231 | 51.3k | } |
232 | | |
233 | 85.8k | if (previous) { |
234 | 984k | while (left > 0) { |
235 | 940k | if (backline) { |
236 | 720k | hnm->current[writeoffset++] = hnm->previous[offset - (2 * hnm->width) + 1]; |
237 | 720k | hnm->current[writeoffset++] = hnm->previous[offset++]; |
238 | 720k | offset++; |
239 | 720k | } else { |
240 | 220k | hnm->current[writeoffset++] = hnm->previous[offset++]; |
241 | 220k | hnm->current[writeoffset++] = hnm->previous[offset++]; |
242 | 220k | } |
243 | 940k | if (backward) |
244 | 672k | offset -= 4; |
245 | 940k | left--; |
246 | 940k | } |
247 | 44.1k | } else { |
248 | 664k | while (left > 0) { |
249 | 622k | if (backline) { |
250 | 401k | hnm->current[writeoffset++] = hnm->current[offset - (2 * hnm->width) + 1]; |
251 | 401k | hnm->current[writeoffset++] = hnm->current[offset++]; |
252 | 401k | offset++; |
253 | 401k | } else { |
254 | 220k | hnm->current[writeoffset++] = hnm->current[offset++]; |
255 | 220k | hnm->current[writeoffset++] = hnm->current[offset++]; |
256 | 220k | } |
257 | 622k | if (backward) |
258 | 212k | offset -= 4; |
259 | 622k | left--; |
260 | 622k | } |
261 | 41.7k | } |
262 | | |
263 | 85.8k | if (swap) { |
264 | 35.6k | left = count; |
265 | 35.6k | writeoffset -= count * 2; |
266 | 803k | while (left > 0) { |
267 | 768k | swap = hnm->current[writeoffset]; |
268 | 768k | hnm->current[writeoffset] = hnm->current[writeoffset + 1]; |
269 | 768k | hnm->current[writeoffset + 1] = swap; |
270 | 768k | left--; |
271 | 768k | writeoffset += 2; |
272 | 768k | } |
273 | 35.6k | } |
274 | 85.8k | } |
275 | 260k | } |
276 | 6.20k | return 0; |
277 | 10.8k | } |
278 | | |
279 | | static void decode_interframe_v4a(AVCodecContext *avctx, const uint8_t *src, |
280 | | uint32_t size) |
281 | 3.97k | { |
282 | 3.97k | Hnm4VideoContext *hnm = avctx->priv_data; |
283 | 3.97k | GetByteContext gb; |
284 | 3.97k | uint32_t writeoffset = 0, offset; |
285 | 3.97k | uint8_t tag, count, previous, delta; |
286 | | |
287 | 3.97k | bytestream2_init(&gb, src, size); |
288 | | |
289 | 173k | while (bytestream2_tell(&gb) < size) { |
290 | 171k | count = bytestream2_peek_byte(&gb) & 0x3F; |
291 | 171k | if (count == 0) { |
292 | 94.3k | tag = bytestream2_get_byte(&gb) & 0xC0; |
293 | 94.3k | tag = tag >> 6; |
294 | 94.3k | if (tag == 0) { |
295 | 89.9k | writeoffset += bytestream2_get_byte(&gb); |
296 | 89.9k | } else if (tag == 1) { |
297 | 2.74k | if (writeoffset + hnm->width >= hnm->width * hnm->height) { |
298 | 210 | av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n"); |
299 | 210 | break; |
300 | 210 | } |
301 | 2.53k | hnm->current[writeoffset] = bytestream2_get_byte(&gb); |
302 | 2.53k | hnm->current[writeoffset + hnm->width] = bytestream2_get_byte(&gb); |
303 | 2.53k | writeoffset++; |
304 | 2.53k | } else if (tag == 2) { |
305 | 983 | writeoffset += hnm->width; |
306 | 983 | } else if (tag == 3) { |
307 | 768 | break; |
308 | 768 | } |
309 | 93.4k | if (writeoffset > hnm->width * hnm->height) { |
310 | 262 | av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n"); |
311 | 262 | break; |
312 | 262 | } |
313 | 93.4k | } else { |
314 | 77.5k | delta = bytestream2_peek_byte(&gb) & 0x80; |
315 | 77.5k | previous = bytestream2_peek_byte(&gb) & 0x40; |
316 | 77.5k | bytestream2_skip(&gb, 1); |
317 | | |
318 | 77.5k | offset = writeoffset; |
319 | 77.5k | offset += bytestream2_get_le16(&gb); |
320 | | |
321 | 77.5k | if (delta) { |
322 | 62.5k | if (offset < 0x10000) { |
323 | 397 | av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n"); |
324 | 397 | break; |
325 | 397 | } |
326 | 62.1k | offset -= 0x10000; |
327 | 62.1k | } |
328 | | |
329 | 77.1k | if (offset + hnm->width + count >= hnm->width * hnm->height) { |
330 | 363 | av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n"); |
331 | 363 | break; |
332 | 76.8k | } else if (writeoffset + hnm->width + count >= hnm->width * hnm->height) { |
333 | 243 | av_log(avctx, AV_LOG_ERROR, "Attempting to write out of bounds\n"); |
334 | 243 | break; |
335 | 243 | } |
336 | | |
337 | 76.5k | if (previous) { |
338 | 3.79M | while (count > 0) { |
339 | 3.72M | hnm->current[writeoffset] = hnm->previous[offset]; |
340 | 3.72M | hnm->current[writeoffset + hnm->width] = hnm->previous[offset + hnm->width]; |
341 | 3.72M | writeoffset++; |
342 | 3.72M | offset++; |
343 | 3.72M | count--; |
344 | 3.72M | } |
345 | 67.4k | } else { |
346 | 312k | while (count > 0) { |
347 | 303k | hnm->current[writeoffset] = hnm->current[offset]; |
348 | 303k | hnm->current[writeoffset + hnm->width] = hnm->current[offset + hnm->width]; |
349 | 303k | writeoffset++; |
350 | 303k | offset++; |
351 | 303k | count--; |
352 | 303k | } |
353 | 9.13k | } |
354 | 76.5k | } |
355 | 171k | } |
356 | 3.97k | } |
357 | | |
358 | | static void hnm_update_palette(AVCodecContext *avctx, const uint8_t *src, |
359 | | uint32_t size) |
360 | 101k | { |
361 | 101k | Hnm4VideoContext *hnm = avctx->priv_data; |
362 | 101k | GetByteContext gb; |
363 | 101k | uint8_t start, writeoffset; |
364 | 101k | uint16_t count; |
365 | 101k | int eight_bit_colors; |
366 | | |
367 | 101k | eight_bit_colors = src[7] & 0x80 && hnm->version == 0x4a; |
368 | | |
369 | | // skip first 8 bytes |
370 | 101k | bytestream2_init(&gb, src + 8, size - 8); |
371 | | |
372 | 156k | while (bytestream2_tell(&gb) < size - 8) { |
373 | 56.1k | start = bytestream2_get_byte(&gb); |
374 | 56.1k | count = bytestream2_get_byte(&gb); |
375 | 56.1k | if (start == 255 && count == 255) |
376 | 422 | break; |
377 | 55.6k | if (count == 0) |
378 | 12.8k | count = 256; |
379 | 55.6k | writeoffset = start; |
380 | 13.4M | while (count > 0) { |
381 | 13.3M | hnm->palette[writeoffset] = bytestream2_get_be24(&gb); |
382 | 13.3M | if (!eight_bit_colors) |
383 | 13.3M | hnm->palette[writeoffset] <<= 2; |
384 | 13.3M | hnm->palette[writeoffset] |= (0xFFU << 24); |
385 | 13.3M | count--; |
386 | 13.3M | writeoffset++; |
387 | 13.3M | } |
388 | 55.6k | } |
389 | 101k | } |
390 | | |
391 | | static int hnm_decode_frame(AVCodecContext *avctx, AVFrame *frame, |
392 | | int *got_frame, AVPacket *avpkt) |
393 | 136k | { |
394 | 136k | Hnm4VideoContext *hnm = avctx->priv_data; |
395 | 136k | int ret; |
396 | 136k | uint16_t chunk_id; |
397 | | |
398 | 136k | if (avpkt->size < 8) { |
399 | 7.97k | av_log(avctx, AV_LOG_ERROR, "packet too small\n"); |
400 | 7.97k | return AVERROR_INVALIDDATA; |
401 | 7.97k | } |
402 | | |
403 | 128k | chunk_id = AV_RL16(avpkt->data + 4); |
404 | | |
405 | 128k | if (chunk_id == HNM4_CHUNK_ID_PL) { |
406 | 101k | hnm_update_palette(avctx, avpkt->data, avpkt->size); |
407 | 101k | } else if (chunk_id == HNM4_CHUNK_ID_IZ) { |
408 | 5.63k | if (avpkt->size < 12) { |
409 | 308 | av_log(avctx, AV_LOG_ERROR, "packet too small\n"); |
410 | 308 | return AVERROR_INVALIDDATA; |
411 | 308 | } |
412 | 5.32k | if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) |
413 | 131 | return ret; |
414 | | |
415 | 5.19k | unpack_intraframe(avctx, avpkt->data + 12, avpkt->size - 12); |
416 | 5.19k | memcpy(hnm->previous, hnm->current, hnm->width * hnm->height); |
417 | 5.19k | if (hnm->version == 0x4a) |
418 | 474 | memcpy(hnm->processed, hnm->current, hnm->width * hnm->height); |
419 | 4.72k | else |
420 | 4.72k | postprocess_current_frame(avctx); |
421 | 5.19k | copy_processed_frame(avctx, frame); |
422 | 5.19k | frame->pict_type = AV_PICTURE_TYPE_I; |
423 | 5.19k | frame->flags |= AV_FRAME_FLAG_KEY; |
424 | 5.19k | memcpy(frame->data[1], hnm->palette, 256 * 4); |
425 | 5.19k | *got_frame = 1; |
426 | 21.1k | } else if (chunk_id == HNM4_CHUNK_ID_IU) { |
427 | 14.9k | if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) |
428 | 169 | return ret; |
429 | | |
430 | 14.7k | if (hnm->version == 0x4a) { |
431 | 3.97k | decode_interframe_v4a(avctx, avpkt->data + 8, avpkt->size - 8); |
432 | 3.97k | memcpy(hnm->processed, hnm->current, hnm->width * hnm->height); |
433 | 10.8k | } else { |
434 | 10.8k | int ret = decode_interframe_v4(avctx, avpkt->data + 8, avpkt->size - 8); |
435 | 10.8k | if (ret < 0) |
436 | 4.61k | return ret; |
437 | 6.20k | postprocess_current_frame(avctx); |
438 | 6.20k | } |
439 | 10.1k | copy_processed_frame(avctx, frame); |
440 | 10.1k | frame->pict_type = AV_PICTURE_TYPE_P; |
441 | 10.1k | frame->flags &= ~AV_FRAME_FLAG_KEY; |
442 | 10.1k | memcpy(frame->data[1], hnm->palette, 256 * 4); |
443 | 10.1k | *got_frame = 1; |
444 | 10.1k | FFSWAP(uint8_t *, hnm->current, hnm->previous); |
445 | 10.1k | } else { |
446 | 6.21k | av_log(avctx, AV_LOG_ERROR, "invalid chunk id: %d\n", chunk_id); |
447 | 6.21k | return AVERROR_INVALIDDATA; |
448 | 6.21k | } |
449 | | |
450 | 116k | return avpkt->size; |
451 | 128k | } |
452 | | |
453 | | static av_cold int hnm_decode_init(AVCodecContext *avctx) |
454 | 1.05k | { |
455 | 1.05k | Hnm4VideoContext *hnm = avctx->priv_data; |
456 | 1.05k | int ret; |
457 | | |
458 | 1.05k | if (avctx->extradata_size < 1) { |
459 | 178 | av_log(avctx, AV_LOG_ERROR, |
460 | 178 | "Extradata missing, decoder requires version number\n"); |
461 | 178 | return AVERROR_INVALIDDATA; |
462 | 178 | } |
463 | | |
464 | 877 | ret = av_image_check_size(avctx->width, avctx->height, 0, avctx); |
465 | 877 | if (ret < 0) |
466 | 15 | return ret; |
467 | 862 | if (avctx->height & 1) |
468 | 2 | return AVERROR(EINVAL); |
469 | | |
470 | 860 | hnm->version = avctx->extradata[0]; |
471 | 860 | avctx->pix_fmt = AV_PIX_FMT_PAL8; |
472 | 860 | hnm->width = avctx->width; |
473 | 860 | hnm->height = avctx->height; |
474 | 860 | hnm->buffer1 = av_mallocz(avctx->width * avctx->height); |
475 | 860 | hnm->buffer2 = av_mallocz(avctx->width * avctx->height); |
476 | 860 | hnm->processed = av_mallocz(avctx->width * avctx->height); |
477 | | |
478 | 860 | 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 | 860 | hnm->current = hnm->buffer1; |
484 | 860 | hnm->previous = hnm->buffer2; |
485 | | |
486 | 860 | return 0; |
487 | 860 | } |
488 | | |
489 | | static av_cold int hnm_decode_end(AVCodecContext *avctx) |
490 | 1.05k | { |
491 | 1.05k | Hnm4VideoContext *hnm = avctx->priv_data; |
492 | | |
493 | 1.05k | av_freep(&hnm->buffer1); |
494 | 1.05k | av_freep(&hnm->buffer2); |
495 | 1.05k | av_freep(&hnm->processed); |
496 | | |
497 | 1.05k | return 0; |
498 | 1.05k | } |
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 | | }; |