Libav
hnm4video.c
Go to the documentation of this file.
1 /*
2  * Cryo Interactive Entertainment HNM4 video decoder
3  *
4  * Copyright (c) 2012 David Kment
5  *
6  * This file is part of Libav.
7  *
8  * Libav 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  * Libav 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 Libav; 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 "internal.h"
32 
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
37 
38 typedef struct Hnm4VideoContext {
40  uint16_t width;
41  uint16_t height;
47  uint32_t palette[256];
49 
50 static int getbit(GetByteContext *gb, uint32_t *bitbuf, int *bits)
51 {
52  int ret;
53 
54  if (!*bits) {
55  *bitbuf = bytestream2_get_le32(gb);
56  *bits = 32;
57  }
58 
59  ret = *bitbuf >> 31;
60  *bitbuf <<= 1;
61  (*bits)--;
62 
63  return ret;
64 }
65 
66 static void unpack_intraframe(AVCodecContext *avctx, uint8_t *src,
67  uint32_t size)
68 {
69  Hnm4VideoContext *hnm = avctx->priv_data;
70  GetByteContext gb;
71  uint32_t bitbuf = 0, writeoffset = 0, count = 0;
72  uint16_t word;
73  int32_t offset;
74  int bits = 0;
75 
76  bytestream2_init(&gb, src, size);
77 
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");
83  break;
84  }
85  hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
86  } else {
87  if (getbit(&gb, &bitbuf, &bits)) {
88  word = bytestream2_get_le16(&gb);
89  count = word & 0x07;
90  offset = (word >> 3) - 0x2000;
91  if (!count)
92  count = bytestream2_get_byte(&gb);
93  if (!count)
94  return;
95  } else {
96  count = getbit(&gb, &bitbuf, &bits) * 2;
97  count += getbit(&gb, &bitbuf, &bits);
98  offset = bytestream2_get_byte(&gb) - 0x0100;
99  }
100  count += 2;
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");
104  break;
105  } else if (writeoffset + count >= hnm->width * hnm->height) {
106  av_log(avctx, AV_LOG_ERROR,
107  "Attempting to write out of bounds");
108  break;
109  }
110  while (count--) {
111  hnm->current[writeoffset++] = hnm->current[offset++];
112  }
113  }
114  }
115 }
116 
118 {
119  Hnm4VideoContext *hnm = avctx->priv_data;
120  uint32_t x, y, src_x, src_y;
121 
122  for (y = 0; y < hnm->height; y++) {
123  src_y = y - (y % 2);
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];
127  src_x += 2;
128  }
129  }
130 }
131 
132 static void copy_processed_frame(AVCodecContext *avctx, AVFrame *frame)
133 {
134  Hnm4VideoContext *hnm = avctx->priv_data;
135  uint8_t *src = hnm->processed;
136  uint8_t *dst = frame->data[0];
137  int y;
138 
139  for (y = 0; y < hnm->height; y++) {
140  memcpy(dst, src, hnm->width);
141  src += hnm->width;
142  dst += frame->linesize[0];
143  }
144 }
145 
146 static void decode_interframe_v4(AVCodecContext *avctx, uint8_t *src, uint32_t size)
147 {
148  Hnm4VideoContext *hnm = avctx->priv_data;
149  GetByteContext gb;
150  uint32_t writeoffset = 0, count, left, offset;
151  uint8_t tag, previous, backline, backward, swap;
152 
153  bytestream2_init(&gb, src, size);
154 
155  while (bytestream2_tell(&gb) < size) {
156  count = bytestream2_peek_byte(&gb) & 0x1F;
157  if (count == 0) {
158  tag = bytestream2_get_byte(&gb) & 0xE0;
159  tag = tag >> 5;
160  if (tag == 0) {
161  hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
162  hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
163  } else if (tag == 1) {
164  writeoffset += bytestream2_get_byte(&gb) * 2;
165  } else if (tag == 2) {
166  count = bytestream2_get_le16(&gb);
167  count *= 2;
168  writeoffset += count;
169  } else if (tag == 3) {
170  count = bytestream2_get_byte(&gb) * 2;
171  while (count > 0) {
172  hnm->current[writeoffset++] = bytestream2_peek_byte(&gb);
173  count--;
174  }
175  bytestream2_skip(&gb, 1);
176  } else {
177  break;
178  }
179  } else {
180  previous = bytestream2_peek_byte(&gb) & 0x20;
181  backline = bytestream2_peek_byte(&gb) & 0x40;
182  backward = bytestream2_peek_byte(&gb) & 0x80;
183  bytestream2_skip(&gb, 1);
184  swap = bytestream2_peek_byte(&gb) & 0x01;
185  offset = bytestream2_get_le16(&gb);
186  offset = (offset >> 1) & 0x7FFF;
187  offset = writeoffset + (offset * 2) - 0x8000;
188 
189  left = count;
190 
191  if (!backward && offset + count >= hnm->width * hnm->height) {
192  av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds");
193  break;
194  } else if (backward && offset >= hnm->width * hnm->height) {
195  av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds");
196  break;
197  } else if (writeoffset + count >= hnm->width * hnm->height) {
198  av_log(avctx, AV_LOG_ERROR,
199  "Attempting to write out of bounds");
200  break;
201  }
202 
203  if (previous) {
204  while (left > 0) {
205  if (backline) {
206  hnm->current[writeoffset++] = hnm->previous[offset - (2 * hnm->width) + 1];
207  hnm->current[writeoffset++] = hnm->previous[offset++];
208  offset++;
209  } else {
210  hnm->current[writeoffset++] = hnm->previous[offset++];
211  hnm->current[writeoffset++] = hnm->previous[offset++];
212  }
213  if (backward)
214  offset -= 4;
215  left--;
216  }
217  } else {
218  while (left > 0) {
219  if (backline) {
220  hnm->current[writeoffset++] = hnm->current[offset - (2 * hnm->width) + 1];
221  hnm->current[writeoffset++] = hnm->current[offset++];
222  offset++;
223  } else {
224  hnm->current[writeoffset++] = hnm->current[offset++];
225  hnm->current[writeoffset++] = hnm->current[offset++];
226  }
227  if (backward)
228  offset -= 4;
229  left--;
230  }
231  }
232 
233  if (swap) {
234  left = count;
235  writeoffset -= count * 2;
236  while (left > 0) {
237  swap = hnm->current[writeoffset];
238  hnm->current[writeoffset] = hnm->current[writeoffset + 1];
239  hnm->current[writeoffset + 1] = swap;
240  left--;
241  writeoffset += 2;
242  }
243  }
244  }
245  }
246 }
247 
249  uint32_t size)
250 {
251  Hnm4VideoContext *hnm = avctx->priv_data;
252  GetByteContext gb;
253  uint32_t writeoffset = 0, offset;
254  uint8_t tag, count, previous, delta;
255 
256  bytestream2_init(&gb, src, size);
257 
258  while (bytestream2_tell(&gb) < size) {
259  count = bytestream2_peek_byte(&gb) & 0x3F;
260  if (count == 0) {
261  tag = bytestream2_get_byte(&gb) & 0xC0;
262  tag = tag >> 6;
263  if (tag == 0) {
264  writeoffset += bytestream2_get_byte(&gb);
265  } else if (tag == 1) {
266  hnm->current[writeoffset] = bytestream2_get_byte(&gb);
267  hnm->current[writeoffset + hnm->width] = bytestream2_get_byte(&gb);
268  writeoffset++;
269  } else if (tag == 2) {
270  writeoffset += hnm->width;
271  } else if (tag == 3) {
272  break;
273  }
274  } else {
275  delta = bytestream2_peek_byte(&gb) & 0x80;
276  previous = bytestream2_peek_byte(&gb) & 0x40;
277  bytestream2_skip(&gb, 1);
278 
279  offset = writeoffset;
280  offset += bytestream2_get_le16(&gb);
281 
282  if (delta)
283  offset -= 0x10000;
284 
285  if (offset + hnm->width + count >= hnm->width * hnm->height) {
286  av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds");
287  break;
288  } else if (writeoffset + hnm->width + count >= hnm->width * hnm->height) {
289  av_log(avctx, AV_LOG_ERROR, "Attempting to write out of bounds");
290  break;
291  }
292 
293  if (previous) {
294  while (count > 0) {
295  hnm->current[writeoffset] = hnm->previous[offset];
296  hnm->current[writeoffset + hnm->width] = hnm->previous[offset + hnm->width];
297  writeoffset++;
298  offset++;
299  count--;
300  }
301  } else {
302  while (count > 0) {
303  hnm->current[writeoffset] = hnm->current[offset];
304  hnm->current[writeoffset + hnm->width] = hnm->current[offset + hnm->width];
305  writeoffset++;
306  offset++;
307  count--;
308  }
309  }
310  }
311  }
312 }
313 
314 static void hnm_update_palette(AVCodecContext *avctx, uint8_t *src,
315  uint32_t size)
316 {
317  Hnm4VideoContext *hnm = avctx->priv_data;
318  GetByteContext gb;
319  uint8_t start, writeoffset;
320  uint16_t count;
321  int eight_bit_colors;
322 
323  eight_bit_colors = src[7] & 0x80 && hnm->version == 0x4a;
324 
325  // skip first 8 bytes
326  bytestream2_init(&gb, src + 8, size - 8);
327 
328  while (bytestream2_tell(&gb) < size - 8) {
329  start = bytestream2_get_byte(&gb);
330  count = bytestream2_get_byte(&gb);
331  if (start == 255 && count == 255)
332  break;
333  if (count == 0)
334  count = 256;
335  writeoffset = start;
336  while (count > 0) {
337  hnm->palette[writeoffset] = bytestream2_get_be24(&gb);
338  if (!eight_bit_colors)
339  hnm->palette[writeoffset] <<= 2;
340  count--;
341  writeoffset++;
342  }
343  }
344 }
345 
347 {
348  uint8_t *temp;
349 
350  temp = hnm->current;
351  hnm->current = hnm->previous;
352  hnm->previous = temp;
353 }
354 
355 static int hnm_decode_frame(AVCodecContext *avctx, void *data,
356  int *got_frame, AVPacket *avpkt)
357 {
358  AVFrame *frame = data;
359  Hnm4VideoContext *hnm = avctx->priv_data;
360  int ret;
361  uint16_t chunk_id;
362 
363  if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) {
364  av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
365  return ret;
366  }
367 
368  chunk_id = AV_RL16(avpkt->data + 4);
369 
370  if (chunk_id == HNM4_CHUNK_ID_PL) {
371  hnm_update_palette(avctx, avpkt->data, avpkt->size);
372  frame->palette_has_changed = 1;
373  } else if (chunk_id == HNM4_CHUNK_ID_IZ) {
374  unpack_intraframe(avctx, avpkt->data + 12, avpkt->size - 12);
375  memcpy(hnm->previous, hnm->current, hnm->width * hnm->height);
376  if (hnm->version == 0x4a)
377  memcpy(hnm->processed, hnm->current, hnm->width * hnm->height);
378  else
380  copy_processed_frame(avctx, frame);
381  frame->pict_type = AV_PICTURE_TYPE_I;
382  frame->key_frame = 1;
383  memcpy(frame->data[1], hnm->palette, 256 * 4);
384  *got_frame = 1;
385  } else if (chunk_id == HNM4_CHUNK_ID_IU) {
386  if (hnm->version == 0x4a) {
387  decode_interframe_v4a(avctx, avpkt->data + 8, avpkt->size - 8);
388  memcpy(hnm->processed, hnm->current, hnm->width * hnm->height);
389  } else {
390  decode_interframe_v4(avctx, avpkt->data + 8, avpkt->size - 8);
392  }
393  copy_processed_frame(avctx, frame);
394  frame->pict_type = AV_PICTURE_TYPE_P;
395  frame->key_frame = 0;
396  memcpy(frame->data[1], hnm->palette, 256 * 4);
397  *got_frame = 1;
398  hnm_flip_buffers(hnm);
399  } else {
400  av_log(avctx, AV_LOG_ERROR, "invalid chunk id: %d\n", chunk_id);
401  return AVERROR_INVALIDDATA;
402  }
403 
404  return avpkt->size;
405 }
406 
408 {
409  Hnm4VideoContext *hnm = avctx->priv_data;
410  int ret;
411 
412  if (avctx->extradata_size < 1) {
413  av_log(avctx, AV_LOG_ERROR,
414  "Extradata missing, decoder requires version number\n");
415  return AVERROR_INVALIDDATA;
416  }
417 
418  ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
419  if (ret < 0)
420  return ret;
421 
422  hnm->version = avctx->extradata[0];
423  avctx->pix_fmt = AV_PIX_FMT_PAL8;
424  hnm->width = avctx->width;
425  hnm->height = avctx->height;
426  hnm->buffer1 = av_mallocz(avctx->width * avctx->height);
427  hnm->buffer2 = av_mallocz(avctx->width * avctx->height);
428  hnm->processed = av_mallocz(avctx->width * avctx->height);
429 
430  if (!hnm->buffer1 || !hnm->buffer2 || !hnm->processed) {
431  av_log(avctx, AV_LOG_ERROR, "av_mallocz() failed\n");
432  av_freep(&hnm->buffer1);
433  av_freep(&hnm->buffer2);
434  av_freep(&hnm->processed);
435  return AVERROR(ENOMEM);
436  }
437 
438  hnm->current = hnm->buffer1;
439  hnm->previous = hnm->buffer2;
440 
441  return 0;
442 }
443 
445 {
446  Hnm4VideoContext *hnm = avctx->priv_data;
447 
448  av_freep(&hnm->buffer1);
449  av_freep(&hnm->buffer2);
450  av_freep(&hnm->processed);
451 
452  return 0;
453 }
454 
456  .name = "hnm4video",
457  .long_name = NULL_IF_CONFIG_SMALL("HNM 4 video"),
458  .type = AVMEDIA_TYPE_VIDEO,
460  .priv_data_size = sizeof(Hnm4VideoContext),
464  .capabilities = CODEC_CAP_DR1,
465 };
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:54
int size
This structure describes decoded (raw) audio or video data.
Definition: frame.h:135
misc image utilities
memory handling functions
uint8_t * processed
Definition: hnm4video.c:46
int size
Definition: avcodec.h:974
enum AVPixelFormat pix_fmt
Pixel format, see AV_PIX_FMT_xxx.
Definition: avcodec.h:1270
static av_always_inline void bytestream2_init(GetByteContext *g, const uint8_t *buf, int buf_size)
Definition: bytestream.h:132
#define AV_RL16
Definition: intreadwrite.h:42
AVCodec.
Definition: avcodec.h:2812
uint16_t height
Definition: hnm4video.c:41
void av_freep(void *arg)
Free a memory block which has been allocated with av_malloc(z)() or av_realloc() and set the pointer ...
Definition: mem.c:198
uint8_t * buffer2
Definition: hnm4video.c:45
#define HNM4_CHUNK_ID_IZ
Definition: hnm4video.c:34
static int decode(MimicContext *ctx, int quality, int num_coeffs, int is_iframe)
Definition: mimic.c:275
uint8_t bits
Definition: crc.c:251
uint8_t version
Definition: hnm4video.c:39
uint8_t
#define av_cold
Definition: attributes.h:66
float delta
8 bit with PIX_FMT_RGB32 palette
Definition: pixfmt.h:76
uint8_t * extradata
some codecs need / can use extradata like Huffman tables.
Definition: avcodec.h:1164
static int getbit(GetByteContext *gb, uint32_t *bitbuf, int *bits)
Definition: hnm4video.c:50
#define CODEC_CAP_DR1
Codec uses get_buffer() for allocating buffers and supports custom allocators.
Definition: avcodec.h:684
const char data[16]
Definition: mxf.c:70
uint8_t * data
Definition: avcodec.h:973
uint32_t tag
Definition: movenc.c:844
static void unpack_intraframe(AVCodecContext *avctx, uint8_t *src, uint32_t size)
Definition: hnm4video.c:66
static void copy_processed_frame(AVCodecContext *avctx, AVFrame *frame)
Definition: hnm4video.c:132
static void decode_interframe_v4a(AVCodecContext *avctx, uint8_t *src, uint32_t size)
Definition: hnm4video.c:248
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:123
#define AVERROR(e)
Definition: error.h:43
static av_always_inline void bytestream2_skip(GetByteContext *g, unsigned int size)
Definition: bytestream.h:161
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
Definition: internal.h:145
void av_log(void *avcl, int level, const char *fmt,...)
Definition: log.c:169
const char * name
Name of the codec implementation.
Definition: avcodec.h:2819
uint8_t * buffer1
Definition: hnm4video.c:44
common internal API header
uint16_t width
Definition: hnm4video.c:40
int av_image_check_size(unsigned int w, unsigned int h, int log_offset, void *log_ctx)
Check if the given dimension of an image is valid, meaning that all bytes of the image can be address...
Definition: imgutils.c:222
enum AVPictureType pict_type
Picture type of the frame.
Definition: frame.h:196
uint8_t * current
Definition: hnm4video.c:42
int width
picture width / height.
Definition: avcodec.h:1229
int32_t
uint8_t * previous
Definition: hnm4video.c:43
static av_cold int hnm_decode_end(AVCodecContext *avctx)
Definition: hnm4video.c:444
static av_always_inline int bytestream2_tell(GetByteContext *g)
Definition: bytestream.h:185
Libavcodec external API header.
int linesize[AV_NUM_DATA_POINTERS]
For video, size in bytes of each picture line.
Definition: frame.h:153
#define HNM4_CHUNK_ID_PL
Definition: hnm4video.c:33
main external API structure.
Definition: avcodec.h:1050
static void close(AVCodecParserContext *s)
Definition: h264_parser.c:490
int ff_get_buffer(AVCodecContext *avctx, AVFrame *frame, int flags)
Get a buffer for a frame.
Definition: utils.c:621
int extradata_size
Definition: avcodec.h:1165
int palette_has_changed
Tell user application that palette has changed from previous frame.
Definition: frame.h:330
#define HNM4_CHUNK_ID_IU
Definition: hnm4video.c:35
static void hnm_flip_buffers(Hnm4VideoContext *hnm)
Definition: hnm4video.c:346
static av_cold int hnm_decode_init(AVCodecContext *avctx)
Definition: hnm4video.c:407
static void decode_interframe_v4(AVCodecContext *avctx, uint8_t *src, uint32_t size)
Definition: hnm4video.c:146
uint8_t * data[AV_NUM_DATA_POINTERS]
pointer to the picture/channel planes.
Definition: frame.h:141
common internal api header.
static int hnm_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPacket *avpkt)
Definition: hnm4video.c:355
static av_cold int init(AVCodecParserContext *s)
Definition: h264_parser.c:499
static void hnm_update_palette(AVCodecContext *avctx, uint8_t *src, uint32_t size)
Definition: hnm4video.c:314
void * priv_data
Definition: avcodec.h:1092
int key_frame
1 -> keyframe, 0-> not
Definition: frame.h:191
uint32_t palette[256]
Definition: hnm4video.c:47
static void postprocess_current_frame(AVCodecContext *avctx)
Definition: hnm4video.c:117
AVCodec ff_hnm4_video_decoder
Definition: hnm4video.c:455
This structure stores compressed data.
Definition: avcodec.h:950
void * av_mallocz(size_t size)
Allocate a block of size bytes with alignment suitable for all memory accesses (including vectors if ...
Definition: mem.c:205
Predicted.
Definition: avutil.h:254