2 * Cryo Interactive Entertainment HNM4 video decoder
4 * Copyright (c) 2012 David Kment
6 * This file is part of FFmpeg.
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.
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.
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
25 #include "libavutil/imgutils.h"
26 #include "libavutil/internal.h"
27 #include "libavutil/intreadwrite.h"
28 #include "libavutil/mem.h"
30 #include "bytestream.h"
33 #define HNM4_CHUNK_ID_PL 19536
34 #define HNM4_CHUNK_ID_IZ 23113
35 #define HNM4_CHUNK_ID_IU 21833
36 #define HNM4_CHUNK_ID_SD 17491
38 typedef struct Hnm4VideoContext
{
47 uint32_t palette
[256];
50 static int getbit(GetByteContext
*gb
, uint32_t *bitbuf
, int *bits
)
55 *bitbuf
= bytestream2_get_le32(gb
);
66 static void unpack_intraframe(AVCodecContext
*avctx
, uint8_t *src
,
69 Hnm4VideoContext
*hnm
= avctx
->priv_data
;
71 uint32_t bitbuf
= 0, writeoffset
= 0, count
= 0;
76 bytestream2_init(&gb
, src
, size
);
78 while (bytestream2_tell(&gb
) < size
) {
79 if (getbit(&gb
, &bitbuf
, &bits
)) {
80 if (writeoffset
>= hnm
->width
* hnm
->height
) {
81 av_log(avctx
, AV_LOG_ERROR
,
82 "Attempting to write out of bounds\n");
85 hnm
->current
[writeoffset
++] = bytestream2_get_byte(&gb
);
87 if (getbit(&gb
, &bitbuf
, &bits
)) {
88 word
= bytestream2_get_le16(&gb
);
90 offset
= (word
>> 3) - 0x2000;
92 count
= bytestream2_get_byte(&gb
);
96 count
= getbit(&gb
, &bitbuf
, &bits
) * 2;
97 count
+= getbit(&gb
, &bitbuf
, &bits
);
98 offset
= bytestream2_get_byte(&gb
) - 0x0100;
101 offset
+= writeoffset
;
102 if (offset
< 0 || offset
+ count
>= hnm
->width
* hnm
->height
) {
103 av_log(avctx
, AV_LOG_ERROR
, "Attempting to read out of bounds\n");
105 } else if (writeoffset
+ count
>= hnm
->width
* hnm
->height
) {
106 av_log(avctx
, AV_LOG_ERROR
,
107 "Attempting to write out of bounds\n");
111 hnm
->current
[writeoffset
++] = hnm
->current
[offset
++];
117 static void postprocess_current_frame(AVCodecContext
*avctx
)
119 Hnm4VideoContext
*hnm
= avctx
->priv_data
;
120 uint32_t x
, y
, src_x
, src_y
;
122 for (y
= 0; y
< hnm
->height
; y
++) {
124 src_x
= src_y
* hnm
->width
+ (y
% 2);
125 for (x
= 0; x
< hnm
->width
; x
++) {
126 hnm
->processed
[(y
* hnm
->width
) + x
] = hnm
->current
[src_x
];
132 static void copy_processed_frame(AVCodecContext
*avctx
, AVFrame
*frame
)
134 Hnm4VideoContext
*hnm
= avctx
->priv_data
;
135 uint8_t *src
= hnm
->processed
;
136 uint8_t *dst
= frame
->data
[0];
139 for (y
= 0; y
< hnm
->height
; y
++) {
140 memcpy(dst
, src
, hnm
->width
);
142 dst
+= frame
->linesize
[0];
146 static void decode_interframe_v4(AVCodecContext
*avctx
, uint8_t *src
, uint32_t size
)
148 Hnm4VideoContext
*hnm
= avctx
->priv_data
;
150 uint32_t writeoffset
= 0;
151 int count
, left
, offset
;
152 uint8_t tag
, previous
, backline
, backward
, swap
;
154 bytestream2_init(&gb
, src
, size
);
156 while (bytestream2_tell(&gb
) < size
) {
157 count
= bytestream2_peek_byte(&gb
) & 0x1F;
159 tag
= bytestream2_get_byte(&gb
) & 0xE0;
163 if (writeoffset
+ 2 > hnm
->width
* hnm
->height
) {
164 av_log(avctx
, AV_LOG_ERROR
, "writeoffset out of bounds\n");
167 hnm
->current
[writeoffset
++] = bytestream2_get_byte(&gb
);
168 hnm
->current
[writeoffset
++] = bytestream2_get_byte(&gb
);
169 } else if (tag
== 1) {
170 writeoffset
+= bytestream2_get_byte(&gb
) * 2;
171 } else if (tag
== 2) {
172 count
= bytestream2_get_le16(&gb
);
174 writeoffset
+= count
;
175 } else if (tag
== 3) {
176 count
= bytestream2_get_byte(&gb
) * 2;
177 if (writeoffset
+ count
> hnm
->width
* hnm
->height
) {
178 av_log(avctx
, AV_LOG_ERROR
, "writeoffset out of bounds\n");
182 hnm
->current
[writeoffset
++] = bytestream2_peek_byte(&gb
);
185 bytestream2_skip(&gb
, 1);
189 if (writeoffset
> hnm
->width
* hnm
->height
) {
190 av_log(avctx
, AV_LOG_ERROR
, "writeoffset out of bounds\n");
194 previous
= bytestream2_peek_byte(&gb
) & 0x20;
195 backline
= bytestream2_peek_byte(&gb
) & 0x40;
196 backward
= bytestream2_peek_byte(&gb
) & 0x80;
197 bytestream2_skip(&gb
, 1);
198 swap
= bytestream2_peek_byte(&gb
) & 0x01;
199 offset
= bytestream2_get_le16(&gb
);
200 offset
= (offset
>> 1) & 0x7FFF;
201 offset
= writeoffset
+ (offset
* 2) - 0x8000;
205 if (!backward
&& offset
+ 2*count
> hnm
->width
* hnm
->height
) {
206 av_log(avctx
, AV_LOG_ERROR
, "Attempting to read out of bounds\n");
208 } else if (backward
&& offset
+ 1 >= hnm
->width
* hnm
->height
) {
209 av_log(avctx
, AV_LOG_ERROR
, "Attempting to read out of bounds\n");
211 } else if (writeoffset
+ 2*count
> hnm
->width
* hnm
->height
) {
212 av_log(avctx
, AV_LOG_ERROR
,
213 "Attempting to write out of bounds\n");
217 if (offset
< (!!backline
)*(2 * hnm
->width
- 1) + 2*(left
-1)) {
218 av_log(avctx
, AV_LOG_ERROR
, "Attempting to read out of bounds\n");
222 if (offset
< (!!backline
)*(2 * hnm
->width
- 1)) {
223 av_log(avctx
, AV_LOG_ERROR
, "Attempting to read out of bounds\n");
231 hnm
->current
[writeoffset
++] = hnm
->previous
[offset
- (2 * hnm
->width
) + 1];
232 hnm
->current
[writeoffset
++] = hnm
->previous
[offset
++];
235 hnm
->current
[writeoffset
++] = hnm
->previous
[offset
++];
236 hnm
->current
[writeoffset
++] = hnm
->previous
[offset
++];
245 hnm
->current
[writeoffset
++] = hnm
->current
[offset
- (2 * hnm
->width
) + 1];
246 hnm
->current
[writeoffset
++] = hnm
->current
[offset
++];
249 hnm
->current
[writeoffset
++] = hnm
->current
[offset
++];
250 hnm
->current
[writeoffset
++] = hnm
->current
[offset
++];
260 writeoffset
-= count
* 2;
262 swap
= hnm
->current
[writeoffset
];
263 hnm
->current
[writeoffset
] = hnm
->current
[writeoffset
+ 1];
264 hnm
->current
[writeoffset
+ 1] = swap
;
273 static void decode_interframe_v4a(AVCodecContext
*avctx
, uint8_t *src
,
276 Hnm4VideoContext
*hnm
= avctx
->priv_data
;
278 uint32_t writeoffset
= 0, offset
;
279 uint8_t tag
, count
, previous
, delta
;
281 bytestream2_init(&gb
, src
, size
);
283 while (bytestream2_tell(&gb
) < size
) {
284 count
= bytestream2_peek_byte(&gb
) & 0x3F;
286 tag
= bytestream2_get_byte(&gb
) & 0xC0;
289 writeoffset
+= bytestream2_get_byte(&gb
);
290 } else if (tag
== 1) {
291 if (writeoffset
+ hnm
->width
>= hnm
->width
* hnm
->height
) {
292 av_log(avctx
, AV_LOG_ERROR
, "writeoffset out of bounds\n");
295 hnm
->current
[writeoffset
] = bytestream2_get_byte(&gb
);
296 hnm
->current
[writeoffset
+ hnm
->width
] = bytestream2_get_byte(&gb
);
298 } else if (tag
== 2) {
299 writeoffset
+= hnm
->width
;
300 } else if (tag
== 3) {
303 if (writeoffset
> hnm
->width
* hnm
->height
) {
304 av_log(avctx
, AV_LOG_ERROR
, "writeoffset out of bounds\n");
308 delta
= bytestream2_peek_byte(&gb
) & 0x80;
309 previous
= bytestream2_peek_byte(&gb
) & 0x40;
310 bytestream2_skip(&gb
, 1);
312 offset
= writeoffset
;
313 offset
+= bytestream2_get_le16(&gb
);
316 if (offset
< 0x10000) {
317 av_log(avctx
, AV_LOG_ERROR
, "Attempting to read out of bounds\n");
323 if (offset
+ hnm
->width
+ count
>= hnm
->width
* hnm
->height
) {
324 av_log(avctx
, AV_LOG_ERROR
, "Attempting to read out of bounds\n");
326 } else if (writeoffset
+ hnm
->width
+ count
>= hnm
->width
* hnm
->height
) {
327 av_log(avctx
, AV_LOG_ERROR
, "Attempting to write out of bounds\n");
333 hnm
->current
[writeoffset
] = hnm
->previous
[offset
];
334 hnm
->current
[writeoffset
+ hnm
->width
] = hnm
->previous
[offset
+ hnm
->width
];
341 hnm
->current
[writeoffset
] = hnm
->current
[offset
];
342 hnm
->current
[writeoffset
+ hnm
->width
] = hnm
->current
[offset
+ hnm
->width
];
352 static void hnm_update_palette(AVCodecContext
*avctx
, uint8_t *src
,
355 Hnm4VideoContext
*hnm
= avctx
->priv_data
;
357 uint8_t start
, writeoffset
;
359 int eight_bit_colors
;
361 eight_bit_colors
= src
[7] & 0x80 && hnm
->version
== 0x4a;
363 // skip first 8 bytes
364 bytestream2_init(&gb
, src
+ 8, size
- 8);
366 while (bytestream2_tell(&gb
) < size
- 8) {
367 start
= bytestream2_get_byte(&gb
);
368 count
= bytestream2_get_byte(&gb
);
369 if (start
== 255 && count
== 255)
375 hnm
->palette
[writeoffset
] = bytestream2_get_be24(&gb
);
376 if (!eight_bit_colors
)
377 hnm
->palette
[writeoffset
] <<= 2;
384 static void hnm_flip_buffers(Hnm4VideoContext
*hnm
)
389 hnm
->current
= hnm
->previous
;
390 hnm
->previous
= temp
;
393 static int hnm_decode_frame(AVCodecContext
*avctx
, void *data
,
394 int *got_frame
, AVPacket
*avpkt
)
396 AVFrame
*frame
= data
;
397 Hnm4VideoContext
*hnm
= avctx
->priv_data
;
401 if (avpkt
->size
< 8) {
402 av_log(avctx
, AV_LOG_ERROR
, "packet too small\n");
403 return AVERROR_INVALIDDATA
;
406 chunk_id
= AV_RL16(avpkt
->data
+ 4);
408 if (chunk_id
== HNM4_CHUNK_ID_PL
) {
409 hnm_update_palette(avctx
, avpkt
->data
, avpkt
->size
);
410 } else if (chunk_id
== HNM4_CHUNK_ID_IZ
) {
411 if (avpkt
->size
< 12) {
412 av_log(avctx
, AV_LOG_ERROR
, "packet too small\n");
413 return AVERROR_INVALIDDATA
;
415 if ((ret
= ff_get_buffer(avctx
, frame
, 0)) < 0)
418 unpack_intraframe(avctx
, avpkt
->data
+ 12, avpkt
->size
- 12);
419 memcpy(hnm
->previous
, hnm
->current
, hnm
->width
* hnm
->height
);
420 if (hnm
->version
== 0x4a)
421 memcpy(hnm
->processed
, hnm
->current
, hnm
->width
* hnm
->height
);
423 postprocess_current_frame(avctx
);
424 copy_processed_frame(avctx
, frame
);
425 frame
->pict_type
= AV_PICTURE_TYPE_I
;
426 frame
->key_frame
= 1;
427 memcpy(frame
->data
[1], hnm
->palette
, 256 * 4);
429 } else if (chunk_id
== HNM4_CHUNK_ID_IU
) {
430 if ((ret
= ff_get_buffer(avctx
, frame
, 0)) < 0)
433 if (hnm
->version
== 0x4a) {
434 decode_interframe_v4a(avctx
, avpkt
->data
+ 8, avpkt
->size
- 8);
435 memcpy(hnm
->processed
, hnm
->current
, hnm
->width
* hnm
->height
);
437 decode_interframe_v4(avctx
, avpkt
->data
+ 8, avpkt
->size
- 8);
438 postprocess_current_frame(avctx
);
440 copy_processed_frame(avctx
, frame
);
441 frame
->pict_type
= AV_PICTURE_TYPE_P
;
442 frame
->key_frame
= 0;
443 memcpy(frame
->data
[1], hnm
->palette
, 256 * 4);
445 hnm_flip_buffers(hnm
);
447 av_log(avctx
, AV_LOG_ERROR
, "invalid chunk id: %d\n", chunk_id
);
448 return AVERROR_INVALIDDATA
;
454 static av_cold
int hnm_decode_init(AVCodecContext
*avctx
)
456 Hnm4VideoContext
*hnm
= avctx
->priv_data
;
459 if (avctx
->extradata_size
< 1) {
460 av_log(avctx
, AV_LOG_ERROR
,
461 "Extradata missing, decoder requires version number\n");
462 return AVERROR_INVALIDDATA
;
465 ret
= av_image_check_size(avctx
->width
, avctx
->height
, 0, avctx
);
469 hnm
->version
= avctx
->extradata
[0];
470 avctx
->pix_fmt
= AV_PIX_FMT_PAL8
;
471 hnm
->width
= avctx
->width
;
472 hnm
->height
= avctx
->height
;
473 hnm
->buffer1
= av_mallocz(avctx
->width
* avctx
->height
);
474 hnm
->buffer2
= av_mallocz(avctx
->width
* avctx
->height
);
475 hnm
->processed
= av_mallocz(avctx
->width
* avctx
->height
);
477 if ( !hnm
->buffer1
|| !hnm
->buffer2
|| !hnm
->processed
478 || avctx
->width
* avctx
->height
== 0
479 || avctx
->height
% 2) {
480 av_log(avctx
, AV_LOG_ERROR
, "av_mallocz() failed\n");
481 av_freep(&hnm
->buffer1
);
482 av_freep(&hnm
->buffer2
);
483 av_freep(&hnm
->processed
);
484 return AVERROR(ENOMEM
);
487 hnm
->current
= hnm
->buffer1
;
488 hnm
->previous
= hnm
->buffer2
;
493 static av_cold
int hnm_decode_end(AVCodecContext
*avctx
)
495 Hnm4VideoContext
*hnm
= avctx
->priv_data
;
497 av_freep(&hnm
->buffer1
);
498 av_freep(&hnm
->buffer2
);
499 av_freep(&hnm
->processed
);
504 AVCodec ff_hnm4_video_decoder
= {
506 .long_name
= NULL_IF_CONFIG_SMALL("HNM 4 video"),
507 .type
= AVMEDIA_TYPE_VIDEO
,
508 .id
= AV_CODEC_ID_HNM4_VIDEO
,
509 .priv_data_size
= sizeof(Hnm4VideoContext
),
510 .init
= hnm_decode_init
,
511 .close
= hnm_decode_end
,
512 .decode
= hnm_decode_frame
,
513 .capabilities
= CODEC_CAP_DR1
,