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/internal.h"
26 #include "libavutil/intreadwrite.h"
27 #include "libavutil/mem.h"
28 #include "avcodec.h"
29 #include "bytestream.h"
30 #include "internal.h"
31 
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
36 
37 typedef struct Hnm4VideoContext {
39  uint16_t width;
40  uint16_t height;
46  uint32_t palette[256];
48 
49 static int getbit(GetByteContext *gb, uint32_t *bitbuf, int *bits)
50 {
51  int ret;
52 
53  if (!*bits) {
54  *bitbuf = bytestream2_get_le32(gb);
55  *bits = 32;
56  }
57 
58  ret = *bitbuf >> 31;
59  *bitbuf <<= 1;
60  (*bits)--;
61 
62  return ret;
63 }
64 
65 static void unpack_intraframe(AVCodecContext *avctx, uint8_t *src,
66  uint32_t size)
67 {
68  Hnm4VideoContext *hnm = avctx->priv_data;
69  GetByteContext gb;
70  uint32_t bitbuf = 0, writeoffset = 0, count = 0;
71  uint16_t word;
72  int32_t offset;
73  int bits = 0;
74 
75  bytestream2_init(&gb, src, size);
76 
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");
82  break;
83  }
84  hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
85  } else {
86  if (getbit(&gb, &bitbuf, &bits)) {
87  word = bytestream2_get_le16(&gb);
88  count = word & 0x07;
89  offset = (word >> 3) - 0x2000;
90  if (!count)
91  count = bytestream2_get_byte(&gb);
92  if (!count)
93  return;
94  } else {
95  count = getbit(&gb, &bitbuf, &bits) * 2;
96  count += getbit(&gb, &bitbuf, &bits);
97  offset = bytestream2_get_byte(&gb) - 0x0100;
98  }
99  count += 2;
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");
103  break;
104  } else if (writeoffset + count >= hnm->width * hnm->height) {
105  av_log(avctx, AV_LOG_ERROR,
106  "Attempting to write out of bounds");
107  break;
108  }
109  while (count--) {
110  hnm->current[writeoffset++] = hnm->current[offset++];
111  }
112  }
113  }
114 }
115 
117 {
118  Hnm4VideoContext *hnm = avctx->priv_data;
119  uint32_t x, y, src_x, src_y;
120 
121  for (y = 0; y < hnm->height; y++) {
122  src_y = y - (y % 2);
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];
126  src_x += 2;
127  }
128  }
129 }
130 
131 static void copy_processed_frame(AVCodecContext *avctx, AVFrame *frame)
132 {
133  Hnm4VideoContext *hnm = avctx->priv_data;
134  uint8_t *src = hnm->processed;
135  uint8_t *dst = frame->data[0];
136  int y;
137 
138  for (y = 0; y < hnm->height; y++) {
139  memcpy(dst, src, hnm->width);
140  src += hnm->width;
141  dst += frame->linesize[0];
142  }
143 }
144 
145 static void decode_interframe_v4(AVCodecContext *avctx, uint8_t *src, uint32_t size)
146 {
147  Hnm4VideoContext *hnm = avctx->priv_data;
148  GetByteContext gb;
149  uint32_t writeoffset = 0, count, left, offset;
150  uint8_t tag, previous, backline, backward, swap;
151 
152  bytestream2_init(&gb, src, size);
153 
154  while (bytestream2_tell(&gb) < size) {
155  count = bytestream2_peek_byte(&gb) & 0x1F;
156  if (count == 0) {
157  tag = bytestream2_get_byte(&gb) & 0xE0;
158  tag = tag >> 5;
159  if (tag == 0) {
160  hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
161  hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
162  } else if (tag == 1) {
163  writeoffset += bytestream2_get_byte(&gb) * 2;
164  } else if (tag == 2) {
165  count = bytestream2_get_le16(&gb);
166  count *= 2;
167  writeoffset += count;
168  } else if (tag == 3) {
169  count = bytestream2_get_byte(&gb) * 2;
170  while (count > 0) {
171  hnm->current[writeoffset++] = bytestream2_peek_byte(&gb);
172  count--;
173  }
174  bytestream2_skip(&gb, 1);
175  } else {
176  break;
177  }
178  } else {
179  previous = bytestream2_peek_byte(&gb) & 0x20;
180  backline = bytestream2_peek_byte(&gb) & 0x40;
181  backward = bytestream2_peek_byte(&gb) & 0x80;
182  bytestream2_skip(&gb, 1);
183  swap = bytestream2_peek_byte(&gb) & 0x01;
184  offset = bytestream2_get_le16(&gb);
185  offset = (offset >> 1) & 0x7FFF;
186  offset = writeoffset + (offset * 2) - 0x8000;
187 
188  left = count;
189 
190  if (!backward && offset + count >= hnm->width * hnm->height) {
191  av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds");
192  break;
193  } else if (backward && offset >= hnm->width * hnm->height) {
194  av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds");
195  break;
196  } else if (writeoffset + count >= hnm->width * hnm->height) {
197  av_log(avctx, AV_LOG_ERROR,
198  "Attempting to write out of bounds");
199  break;
200  }
201 
202  if (previous) {
203  while (left > 0) {
204  if (backline) {
205  hnm->current[writeoffset++] = hnm->previous[offset - (2 * hnm->width) + 1];
206  hnm->current[writeoffset++] = hnm->previous[offset++];
207  offset++;
208  } else {
209  hnm->current[writeoffset++] = hnm->previous[offset++];
210  hnm->current[writeoffset++] = hnm->previous[offset++];
211  }
212  if (backward)
213  offset -= 4;
214  left--;
215  }
216  } else {
217  while (left > 0) {
218  if (backline) {
219  hnm->current[writeoffset++] = hnm->current[offset - (2 * hnm->width) + 1];
220  hnm->current[writeoffset++] = hnm->current[offset++];
221  offset++;
222  } else {
223  hnm->current[writeoffset++] = hnm->current[offset++];
224  hnm->current[writeoffset++] = hnm->current[offset++];
225  }
226  if (backward)
227  offset -= 4;
228  left--;
229  }
230  }
231 
232  if (swap) {
233  left = count;
234  writeoffset -= count * 2;
235  while (left > 0) {
236  swap = hnm->current[writeoffset];
237  hnm->current[writeoffset] = hnm->current[writeoffset + 1];
238  hnm->current[writeoffset + 1] = swap;
239  left--;
240  writeoffset += 2;
241  }
242  }
243  }
244  }
245 }
246 
248  uint32_t size)
249 {
250  Hnm4VideoContext *hnm = avctx->priv_data;
251  GetByteContext gb;
252  uint32_t writeoffset = 0, offset;
253  uint8_t tag, count, previous, delta;
254 
255  bytestream2_init(&gb, src, size);
256 
257  while (bytestream2_tell(&gb) < size) {
258  count = bytestream2_peek_byte(&gb) & 0x3F;
259  if (count == 0) {
260  tag = bytestream2_get_byte(&gb) & 0xC0;
261  tag = tag >> 6;
262  if (tag == 0) {
263  writeoffset += bytestream2_get_byte(&gb);
264  } else if (tag == 1) {
265  hnm->current[writeoffset] = bytestream2_get_byte(&gb);
266  hnm->current[writeoffset + hnm->width] = bytestream2_get_byte(&gb);
267  writeoffset++;
268  } else if (tag == 2) {
269  writeoffset += hnm->width;
270  } else if (tag == 3) {
271  break;
272  }
273  } else {
274  delta = bytestream2_peek_byte(&gb) & 0x80;
275  previous = bytestream2_peek_byte(&gb) & 0x40;
276  bytestream2_skip(&gb, 1);
277 
278  offset = writeoffset;
279  offset += bytestream2_get_le16(&gb);
280 
281  if (delta)
282  offset -= 0x10000;
283 
284  if (offset + hnm->width + count >= hnm->width * hnm->height) {
285  av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds");
286  break;
287  } else if (writeoffset + hnm->width + count >= hnm->width * hnm->height) {
288  av_log(avctx, AV_LOG_ERROR, "Attempting to write out of bounds");
289  break;
290  }
291 
292  if (previous) {
293  while (count > 0) {
294  hnm->current[writeoffset] = hnm->previous[offset];
295  hnm->current[writeoffset + hnm->width] = hnm->previous[offset + hnm->width];
296  writeoffset++;
297  offset++;
298  count--;
299  }
300  } else {
301  while (count > 0) {
302  hnm->current[writeoffset] = hnm->current[offset];
303  hnm->current[writeoffset + hnm->width] = hnm->current[offset + hnm->width];
304  writeoffset++;
305  offset++;
306  count--;
307  }
308  }
309  }
310  }
311 }
312 
313 static void hnm_update_palette(AVCodecContext *avctx, uint8_t *src,
314  uint32_t size)
315 {
316  Hnm4VideoContext *hnm = avctx->priv_data;
317  GetByteContext gb;
318  uint8_t start, writeoffset;
319  uint16_t count;
320  int eight_bit_colors;
321 
322  eight_bit_colors = src[7] & 0x80 && hnm->version == 0x4a;
323 
324  // skip first 8 bytes
325  bytestream2_init(&gb, src + 8, size - 8);
326 
327  while (bytestream2_tell(&gb) < size - 8) {
328  start = bytestream2_get_byte(&gb);
329  count = bytestream2_get_byte(&gb);
330  if (start == 255 && count == 255)
331  break;
332  if (count == 0)
333  count = 256;
334  writeoffset = start;
335  while (count > 0) {
336  hnm->palette[writeoffset] = bytestream2_get_be24(&gb);
337  if (!eight_bit_colors)
338  hnm->palette[writeoffset] <<= 2;
339  count--;
340  writeoffset++;
341  }
342  }
343 }
344 
346 {
347  uint8_t *temp;
348 
349  temp = hnm->current;
350  hnm->current = hnm->previous;
351  hnm->previous = temp;
352 }
353 
354 static int hnm_decode_frame(AVCodecContext *avctx, void *data,
355  int *got_frame, AVPacket *avpkt)
356 {
357  AVFrame *frame = data;
358  Hnm4VideoContext *hnm = avctx->priv_data;
359  int ret;
360  uint16_t chunk_id;
361 
362  if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) {
363  av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
364  return ret;
365  }
366 
367  chunk_id = AV_RL16(avpkt->data + 4);
368 
369  if (chunk_id == HNM4_CHUNK_ID_PL) {
370  hnm_update_palette(avctx, avpkt->data, avpkt->size);
371  frame->palette_has_changed = 1;
372  } else if (chunk_id == HNM4_CHUNK_ID_IZ) {
373  unpack_intraframe(avctx, avpkt->data + 12, avpkt->size - 12);
374  memcpy(hnm->previous, hnm->current, hnm->width * hnm->height);
375  if (hnm->version == 0x4a)
376  memcpy(hnm->processed, hnm->current, hnm->width * hnm->height);
377  else
379  copy_processed_frame(avctx, frame);
380  frame->pict_type = AV_PICTURE_TYPE_I;
381  frame->key_frame = 1;
382  memcpy(frame->data[1], hnm->palette, 256 * 4);
383  *got_frame = 1;
384  } else if (chunk_id == HNM4_CHUNK_ID_IU) {
385  if (hnm->version == 0x4a) {
386  decode_interframe_v4a(avctx, avpkt->data + 8, avpkt->size - 8);
387  memcpy(hnm->processed, hnm->current, hnm->width * hnm->height);
388  } else {
389  decode_interframe_v4(avctx, avpkt->data + 8, avpkt->size - 8);
391  }
392  copy_processed_frame(avctx, frame);
393  frame->pict_type = AV_PICTURE_TYPE_P;
394  frame->key_frame = 0;
395  memcpy(frame->data[1], hnm->palette, 256 * 4);
396  *got_frame = 1;
397  hnm_flip_buffers(hnm);
398  } else {
399  av_log(avctx, AV_LOG_ERROR, "invalid chunk id: %d\n", chunk_id);
400  return AVERROR_INVALIDDATA;
401  }
402 
403  return avpkt->size;
404 }
405 
407 {
408  Hnm4VideoContext *hnm = avctx->priv_data;
409 
410  if (avctx->extradata_size < 1) {
411  av_log(avctx, AV_LOG_ERROR,
412  "Extradata missing, decoder requires version number\n");
413  return AVERROR_INVALIDDATA;
414  }
415 
416  hnm->version = avctx->extradata[0];
417  avctx->pix_fmt = AV_PIX_FMT_PAL8;
418  hnm->width = avctx->width;
419  hnm->height = avctx->height;
420  hnm->buffer1 = av_mallocz(avctx->width * avctx->height);
421  hnm->buffer2 = av_mallocz(avctx->width * avctx->height);
422  hnm->processed = av_mallocz(avctx->width * avctx->height);
423 
424  if (!hnm->buffer1 || !hnm->buffer2 || !hnm->processed) {
425  av_log(avctx, AV_LOG_ERROR, "av_mallocz() failed\n");
426  av_freep(&hnm->buffer1);
427  av_freep(&hnm->buffer2);
428  av_freep(&hnm->processed);
429  return AVERROR(ENOMEM);
430  }
431 
432  hnm->current = hnm->buffer1;
433  hnm->previous = hnm->buffer2;
434 
435  return 0;
436 }
437 
439 {
440  Hnm4VideoContext *hnm = avctx->priv_data;
441 
442  av_freep(&hnm->buffer1);
443  av_freep(&hnm->buffer2);
444  av_freep(&hnm->processed);
445 
446  return 0;
447 }
448 
450  .name = "hnm4video",
451  .long_name = NULL_IF_CONFIG_SMALL("HNM 4 video"),
452  .type = AVMEDIA_TYPE_VIDEO,
454  .priv_data_size = sizeof(Hnm4VideoContext),
458  .capabilities = CODEC_CAP_DR1,
459 };
#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
memory handling functions
uint8_t * processed
Definition: hnm4video.c:45
int size
Definition: avcodec.h:974
enum AVPixelFormat pix_fmt
Pixel format, see AV_PIX_FMT_xxx.
Definition: avcodec.h:1254
static av_always_inline void bytestream2_init(GetByteContext *g, const uint8_t *buf, int buf_size)
Definition: bytestream.h:130
#define AV_RL16
Definition: intreadwrite.h:42
AVCodec.
Definition: avcodec.h:2796
uint16_t height
Definition: hnm4video.c:40
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:44
#define HNM4_CHUNK_ID_IZ
Definition: hnm4video.c:33
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:38
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:49
#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:65
static void copy_processed_frame(AVCodecContext *avctx, AVFrame *frame)
Definition: hnm4video.c:131
static void decode_interframe_v4a(AVCodecContext *avctx, uint8_t *src, uint32_t size)
Definition: hnm4video.c:247
#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:159
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
Definition: internal.h:150
void av_log(void *avcl, int level, const char *fmt,...)
Definition: log.c:168
const char * name
Name of the codec implementation.
Definition: avcodec.h:2803
uint8_t * buffer1
Definition: hnm4video.c:43
common internal API header
uint16_t width
Definition: hnm4video.c:39
enum AVPictureType pict_type
Picture type of the frame.
Definition: frame.h:196
uint8_t * current
Definition: hnm4video.c:41
int width
picture width / height.
Definition: avcodec.h:1224
int32_t
uint8_t * previous
Definition: hnm4video.c:42
static av_cold int hnm_decode_end(AVCodecContext *avctx)
Definition: hnm4video.c:438
static av_always_inline int bytestream2_tell(GetByteContext *g)
Definition: bytestream.h:183
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:32
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:612
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:34
static void hnm_flip_buffers(Hnm4VideoContext *hnm)
Definition: hnm4video.c:345
static av_cold int hnm_decode_init(AVCodecContext *avctx)
Definition: hnm4video.c:406
static void decode_interframe_v4(AVCodecContext *avctx, uint8_t *src, uint32_t size)
Definition: hnm4video.c:145
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:354
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:313
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:46
static void postprocess_current_frame(AVCodecContext *avctx)
Definition: hnm4video.c:116
AVCodec ff_hnm4_video_decoder
Definition: hnm4video.c:449
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