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/internal.h"
26 #include "libavutil/intreadwrite.h"
27 #include "libavutil/mem.h"
29 #include "bytestream.h"
32 #define HNM4_CHUNK_ID_PL 19536
33 #define HNM4_CHUNK_ID_IZ 23113
34 #define HNM4_CHUNK_ID_IU 21833
35 #define HNM4_CHUNK_ID_SD 17491
37 typedef struct Hnm4VideoContext
{
46 uint32_t palette
[256];
49 static int getbit(GetByteContext
*gb
, uint32_t *bitbuf
, int *bits
)
54 *bitbuf
= bytestream2_get_le32(gb
);
65 static void unpack_intraframe(AVCodecContext
*avctx
, uint8_t *src
,
68 Hnm4VideoContext
*hnm
= avctx
->priv_data
;
70 uint32_t bitbuf
= 0, writeoffset
= 0, count
= 0;
75 bytestream2_init(&gb
, src
, size
);
77 while (bytestream2_tell(&gb
) < size
) {
78 if (getbit(&gb
, &bitbuf
, &bits
)) {
79 if (writeoffset
>= hnm
->width
* hnm
->height
) {
80 av_log(avctx
, AV_LOG_ERROR
,
81 "Attempting to write out of bounds\n");
84 hnm
->current
[writeoffset
++] = bytestream2_get_byte(&gb
);
86 if (getbit(&gb
, &bitbuf
, &bits
)) {
87 word
= bytestream2_get_le16(&gb
);
89 offset
= (word
>> 3) - 0x2000;
91 count
= bytestream2_get_byte(&gb
);
95 count
= getbit(&gb
, &bitbuf
, &bits
) * 2;
96 count
+= getbit(&gb
, &bitbuf
, &bits
);
97 offset
= bytestream2_get_byte(&gb
) - 0x0100;
100 offset
+= writeoffset
;
101 if (offset
< 0 || offset
+ count
>= hnm
->width
* hnm
->height
) {
102 av_log(avctx
, AV_LOG_ERROR
, "Attempting to read out of bounds\n");
104 } else if (writeoffset
+ count
>= hnm
->width
* hnm
->height
) {
105 av_log(avctx
, AV_LOG_ERROR
,
106 "Attempting to write out of bounds\n");
110 hnm
->current
[writeoffset
++] = hnm
->current
[offset
++];
116 static void postprocess_current_frame(AVCodecContext
*avctx
)
118 Hnm4VideoContext
*hnm
= avctx
->priv_data
;
119 uint32_t x
, y
, src_x
, src_y
;
121 for (y
= 0; y
< hnm
->height
; y
++) {
123 src_x
= src_y
* hnm
->width
+ (y
% 2);
124 for (x
= 0; x
< hnm
->width
; x
++) {
125 hnm
->processed
[(y
* hnm
->width
) + x
] = hnm
->current
[src_x
];
131 static void copy_processed_frame(AVCodecContext
*avctx
, AVFrame
*frame
)
133 Hnm4VideoContext
*hnm
= avctx
->priv_data
;
134 uint8_t *src
= hnm
->processed
;
135 uint8_t *dst
= frame
->data
[0];
138 for (y
= 0; y
< hnm
->height
; y
++) {
139 memcpy(dst
, src
, hnm
->width
);
141 dst
+= frame
->linesize
[0];
145 static void decode_interframe_v4(AVCodecContext
*avctx
, uint8_t *src
, uint32_t size
)
147 Hnm4VideoContext
*hnm
= avctx
->priv_data
;
149 uint32_t writeoffset
= 0;
150 int count
, left
, offset
;
151 uint8_t tag
, previous
, backline
, backward
, swap
;
153 bytestream2_init(&gb
, src
, size
);
155 while (bytestream2_tell(&gb
) < size
) {
156 count
= bytestream2_peek_byte(&gb
) & 0x1F;
158 tag
= bytestream2_get_byte(&gb
) & 0xE0;
162 if (writeoffset
+ 2 > hnm
->width
* hnm
->height
) {
163 av_log(avctx
, AV_LOG_ERROR
, "writeoffset out of bounds\n");
166 hnm
->current
[writeoffset
++] = bytestream2_get_byte(&gb
);
167 hnm
->current
[writeoffset
++] = bytestream2_get_byte(&gb
);
168 } else if (tag
== 1) {
169 writeoffset
+= bytestream2_get_byte(&gb
) * 2;
170 } else if (tag
== 2) {
171 count
= bytestream2_get_le16(&gb
);
173 writeoffset
+= count
;
174 } else if (tag
== 3) {
175 count
= bytestream2_get_byte(&gb
) * 2;
176 if (writeoffset
+ count
> hnm
->width
* hnm
->height
) {
177 av_log(avctx
, AV_LOG_ERROR
, "writeoffset out of bounds\n");
181 hnm
->current
[writeoffset
++] = bytestream2_peek_byte(&gb
);
184 bytestream2_skip(&gb
, 1);
188 if (writeoffset
> hnm
->width
* hnm
->height
) {
189 av_log(avctx
, AV_LOG_ERROR
, "writeoffset out of bounds\n");
193 previous
= bytestream2_peek_byte(&gb
) & 0x20;
194 backline
= bytestream2_peek_byte(&gb
) & 0x40;
195 backward
= bytestream2_peek_byte(&gb
) & 0x80;
196 bytestream2_skip(&gb
, 1);
197 swap
= bytestream2_peek_byte(&gb
) & 0x01;
198 offset
= bytestream2_get_le16(&gb
);
199 offset
= (offset
>> 1) & 0x7FFF;
200 offset
= writeoffset
+ (offset
* 2) - 0x8000;
204 if (!backward
&& offset
+ 2*count
> hnm
->width
* hnm
->height
) {
205 av_log(avctx
, AV_LOG_ERROR
, "Attempting to read out of bounds\n");
207 } else if (backward
&& offset
+ 1 >= hnm
->width
* hnm
->height
) {
208 av_log(avctx
, AV_LOG_ERROR
, "Attempting to read out of bounds\n");
210 } else if (writeoffset
+ 2*count
> hnm
->width
* hnm
->height
) {
211 av_log(avctx
, AV_LOG_ERROR
,
212 "Attempting to write out of bounds\n");
216 if (offset
< (!!backline
)*(2 * hnm
->width
- 1) + 2*(left
-1)) {
217 av_log(avctx
, AV_LOG_ERROR
, "Attempting to read out of bounds\n");
221 if (offset
< (!!backline
)*(2 * hnm
->width
- 1)) {
222 av_log(avctx
, AV_LOG_ERROR
, "Attempting to read out of bounds\n");
230 hnm
->current
[writeoffset
++] = hnm
->previous
[offset
- (2 * hnm
->width
) + 1];
231 hnm
->current
[writeoffset
++] = hnm
->previous
[offset
++];
234 hnm
->current
[writeoffset
++] = hnm
->previous
[offset
++];
235 hnm
->current
[writeoffset
++] = hnm
->previous
[offset
++];
244 hnm
->current
[writeoffset
++] = hnm
->current
[offset
- (2 * hnm
->width
) + 1];
245 hnm
->current
[writeoffset
++] = hnm
->current
[offset
++];
248 hnm
->current
[writeoffset
++] = hnm
->current
[offset
++];
249 hnm
->current
[writeoffset
++] = hnm
->current
[offset
++];
259 writeoffset
-= count
* 2;
261 swap
= hnm
->current
[writeoffset
];
262 hnm
->current
[writeoffset
] = hnm
->current
[writeoffset
+ 1];
263 hnm
->current
[writeoffset
+ 1] = swap
;
272 static void decode_interframe_v4a(AVCodecContext
*avctx
, uint8_t *src
,
275 Hnm4VideoContext
*hnm
= avctx
->priv_data
;
277 uint32_t writeoffset
= 0, offset
;
278 uint8_t tag
, count
, previous
, delta
;
280 bytestream2_init(&gb
, src
, size
);
282 while (bytestream2_tell(&gb
) < size
) {
283 count
= bytestream2_peek_byte(&gb
) & 0x3F;
285 tag
= bytestream2_get_byte(&gb
) & 0xC0;
288 writeoffset
+= bytestream2_get_byte(&gb
);
289 } else if (tag
== 1) {
290 if (writeoffset
+ hnm
->width
>= hnm
->width
* hnm
->height
) {
291 av_log(avctx
, AV_LOG_ERROR
, "writeoffset out of bounds\n");
294 hnm
->current
[writeoffset
] = bytestream2_get_byte(&gb
);
295 hnm
->current
[writeoffset
+ hnm
->width
] = bytestream2_get_byte(&gb
);
297 } else if (tag
== 2) {
298 writeoffset
+= hnm
->width
;
299 } else if (tag
== 3) {
302 if (writeoffset
> hnm
->width
* hnm
->height
) {
303 av_log(avctx
, AV_LOG_ERROR
, "writeoffset out of bounds\n");
307 delta
= bytestream2_peek_byte(&gb
) & 0x80;
308 previous
= bytestream2_peek_byte(&gb
) & 0x40;
309 bytestream2_skip(&gb
, 1);
311 offset
= writeoffset
;
312 offset
+= bytestream2_get_le16(&gb
);
315 if (offset
< 0x10000) {
316 av_log(avctx
, AV_LOG_ERROR
, "Attempting to read out of bounds\n");
322 if (offset
+ hnm
->width
+ count
>= hnm
->width
* hnm
->height
) {
323 av_log(avctx
, AV_LOG_ERROR
, "Attempting to read out of bounds\n");
325 } else if (writeoffset
+ hnm
->width
+ count
>= hnm
->width
* hnm
->height
) {
326 av_log(avctx
, AV_LOG_ERROR
, "Attempting to write out of bounds\n");
332 hnm
->current
[writeoffset
] = hnm
->previous
[offset
];
333 hnm
->current
[writeoffset
+ hnm
->width
] = hnm
->previous
[offset
+ hnm
->width
];
340 hnm
->current
[writeoffset
] = hnm
->current
[offset
];
341 hnm
->current
[writeoffset
+ hnm
->width
] = hnm
->current
[offset
+ hnm
->width
];
351 static void hnm_update_palette(AVCodecContext
*avctx
, uint8_t *src
,
354 Hnm4VideoContext
*hnm
= avctx
->priv_data
;
356 uint8_t start
, writeoffset
;
358 int eight_bit_colors
;
360 eight_bit_colors
= src
[7] & 0x80 && hnm
->version
== 0x4a;
362 // skip first 8 bytes
363 bytestream2_init(&gb
, src
+ 8, size
- 8);
365 while (bytestream2_tell(&gb
) < size
- 8) {
366 start
= bytestream2_get_byte(&gb
);
367 count
= bytestream2_get_byte(&gb
);
368 if (start
== 255 && count
== 255)
374 hnm
->palette
[writeoffset
] = bytestream2_get_be24(&gb
);
375 if (!eight_bit_colors
)
376 hnm
->palette
[writeoffset
] <<= 2;
383 static void hnm_flip_buffers(Hnm4VideoContext
*hnm
)
388 hnm
->current
= hnm
->previous
;
389 hnm
->previous
= temp
;
392 static int hnm_decode_frame(AVCodecContext
*avctx
, void *data
,
393 int *got_frame
, AVPacket
*avpkt
)
395 AVFrame
*frame
= data
;
396 Hnm4VideoContext
*hnm
= avctx
->priv_data
;
400 if (avpkt
->size
< 8) {
401 av_log(avctx
, AV_LOG_ERROR
, "packet too small\n");
402 return AVERROR_INVALIDDATA
;
405 chunk_id
= AV_RL16(avpkt
->data
+ 4);
407 if (chunk_id
== HNM4_CHUNK_ID_PL
) {
408 hnm_update_palette(avctx
, avpkt
->data
, avpkt
->size
);
409 } else if (chunk_id
== HNM4_CHUNK_ID_IZ
) {
410 if (avpkt
->size
< 12) {
411 av_log(avctx
, AV_LOG_ERROR
, "packet too small\n");
412 return AVERROR_INVALIDDATA
;
414 if ((ret
= ff_get_buffer(avctx
, frame
, 0)) < 0)
417 unpack_intraframe(avctx
, avpkt
->data
+ 12, avpkt
->size
- 12);
418 memcpy(hnm
->previous
, hnm
->current
, hnm
->width
* hnm
->height
);
419 if (hnm
->version
== 0x4a)
420 memcpy(hnm
->processed
, hnm
->current
, hnm
->width
* hnm
->height
);
422 postprocess_current_frame(avctx
);
423 copy_processed_frame(avctx
, frame
);
424 frame
->pict_type
= AV_PICTURE_TYPE_I
;
425 frame
->key_frame
= 1;
426 memcpy(frame
->data
[1], hnm
->palette
, 256 * 4);
428 } else if (chunk_id
== HNM4_CHUNK_ID_IU
) {
429 if ((ret
= ff_get_buffer(avctx
, frame
, 0)) < 0)
432 if (hnm
->version
== 0x4a) {
433 decode_interframe_v4a(avctx
, avpkt
->data
+ 8, avpkt
->size
- 8);
434 memcpy(hnm
->processed
, hnm
->current
, hnm
->width
* hnm
->height
);
436 decode_interframe_v4(avctx
, avpkt
->data
+ 8, avpkt
->size
- 8);
437 postprocess_current_frame(avctx
);
439 copy_processed_frame(avctx
, frame
);
440 frame
->pict_type
= AV_PICTURE_TYPE_P
;
441 frame
->key_frame
= 0;
442 memcpy(frame
->data
[1], hnm
->palette
, 256 * 4);
444 hnm_flip_buffers(hnm
);
446 av_log(avctx
, AV_LOG_ERROR
, "invalid chunk id: %d\n", chunk_id
);
447 return AVERROR_INVALIDDATA
;
453 static av_cold
int hnm_decode_init(AVCodecContext
*avctx
)
455 Hnm4VideoContext
*hnm
= avctx
->priv_data
;
457 if (avctx
->extradata_size
< 1) {
458 av_log(avctx
, AV_LOG_ERROR
,
459 "Extradata missing, decoder requires version number\n");
460 return AVERROR_INVALIDDATA
;
463 hnm
->version
= avctx
->extradata
[0];
464 avctx
->pix_fmt
= AV_PIX_FMT_PAL8
;
465 hnm
->width
= avctx
->width
;
466 hnm
->height
= avctx
->height
;
467 hnm
->buffer1
= av_mallocz(avctx
->width
* avctx
->height
);
468 hnm
->buffer2
= av_mallocz(avctx
->width
* avctx
->height
);
469 hnm
->processed
= av_mallocz(avctx
->width
* avctx
->height
);
471 if ( !hnm
->buffer1
|| !hnm
->buffer2
|| !hnm
->processed
472 || avctx
->width
* avctx
->height
== 0
473 || avctx
->height
% 2) {
474 av_log(avctx
, AV_LOG_ERROR
, "av_mallocz() failed\n");
475 av_freep(&hnm
->buffer1
);
476 av_freep(&hnm
->buffer2
);
477 av_freep(&hnm
->processed
);
478 return AVERROR(ENOMEM
);
481 hnm
->current
= hnm
->buffer1
;
482 hnm
->previous
= hnm
->buffer2
;
487 static av_cold
int hnm_decode_end(AVCodecContext
*avctx
)
489 Hnm4VideoContext
*hnm
= avctx
->priv_data
;
491 av_freep(&hnm
->buffer1
);
492 av_freep(&hnm
->buffer2
);
493 av_freep(&hnm
->processed
);
498 AVCodec ff_hnm4_video_decoder
= {
500 .long_name
= NULL_IF_CONFIG_SMALL("HNM 4 video"),
501 .type
= AVMEDIA_TYPE_VIDEO
,
502 .id
= AV_CODEC_ID_HNM4_VIDEO
,
503 .priv_data_size
= sizeof(Hnm4VideoContext
),
504 .init
= hnm_decode_init
,
505 .close
= hnm_decode_end
,
506 .decode
= hnm_decode_frame
,
507 .capabilities
= CODEC_CAP_DR1
,