FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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 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/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  int width;
40  int 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 
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;
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\n");
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\n");
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\n");
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 
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;
150  int 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 
161  if (tag == 0) {
162  if (writeoffset + 2 > hnm->width * hnm->height) {
163  av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
164  break;
165  }
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);
172  count *= 2;
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");
178  break;
179  }
180  while (count > 0) {
181  hnm->current[writeoffset++] = bytestream2_peek_byte(&gb);
182  count--;
183  }
184  bytestream2_skip(&gb, 1);
185  } else {
186  break;
187  }
188  if (writeoffset > hnm->width * hnm->height) {
189  av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
190  break;
191  }
192  } else {
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;
201 
202  left = count;
203 
204  if (!backward && offset + 2*count > hnm->width * hnm->height) {
205  av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
206  break;
207  } else if (backward && offset + 1 >= hnm->width * hnm->height) {
208  av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
209  break;
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");
213  break;
214  }
215  if(backward) {
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");
218  break;
219  }
220  } else {
221  if (offset < (!!backline)*(2 * hnm->width - 1)) {
222  av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
223  break;
224  }
225  }
226 
227  if (previous) {
228  while (left > 0) {
229  if (backline) {
230  hnm->current[writeoffset++] = hnm->previous[offset - (2 * hnm->width) + 1];
231  hnm->current[writeoffset++] = hnm->previous[offset++];
232  offset++;
233  } else {
234  hnm->current[writeoffset++] = hnm->previous[offset++];
235  hnm->current[writeoffset++] = hnm->previous[offset++];
236  }
237  if (backward)
238  offset -= 4;
239  left--;
240  }
241  } else {
242  while (left > 0) {
243  if (backline) {
244  hnm->current[writeoffset++] = hnm->current[offset - (2 * hnm->width) + 1];
245  hnm->current[writeoffset++] = hnm->current[offset++];
246  offset++;
247  } else {
248  hnm->current[writeoffset++] = hnm->current[offset++];
249  hnm->current[writeoffset++] = hnm->current[offset++];
250  }
251  if (backward)
252  offset -= 4;
253  left--;
254  }
255  }
256 
257  if (swap) {
258  left = count;
259  writeoffset -= count * 2;
260  while (left > 0) {
261  swap = hnm->current[writeoffset];
262  hnm->current[writeoffset] = hnm->current[writeoffset + 1];
263  hnm->current[writeoffset + 1] = swap;
264  left--;
265  writeoffset += 2;
266  }
267  }
268  }
269  }
270 }
271 
273  uint32_t size)
274 {
275  Hnm4VideoContext *hnm = avctx->priv_data;
276  GetByteContext gb;
277  uint32_t writeoffset = 0, offset;
278  uint8_t tag, count, previous, delta;
279 
280  bytestream2_init(&gb, src, size);
281 
282  while (bytestream2_tell(&gb) < size) {
283  count = bytestream2_peek_byte(&gb) & 0x3F;
284  if (count == 0) {
285  tag = bytestream2_get_byte(&gb) & 0xC0;
286  tag = tag >> 6;
287  if (tag == 0) {
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");
292  break;
293  }
294  hnm->current[writeoffset] = bytestream2_get_byte(&gb);
295  hnm->current[writeoffset + hnm->width] = bytestream2_get_byte(&gb);
296  writeoffset++;
297  } else if (tag == 2) {
298  writeoffset += hnm->width;
299  } else if (tag == 3) {
300  break;
301  }
302  if (writeoffset > hnm->width * hnm->height) {
303  av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
304  break;
305  }
306  } else {
307  delta = bytestream2_peek_byte(&gb) & 0x80;
308  previous = bytestream2_peek_byte(&gb) & 0x40;
309  bytestream2_skip(&gb, 1);
310 
311  offset = writeoffset;
312  offset += bytestream2_get_le16(&gb);
313 
314  if (delta) {
315  if (offset < 0x10000) {
316  av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
317  break;
318  }
319  offset -= 0x10000;
320  }
321 
322  if (offset + hnm->width + count >= hnm->width * hnm->height) {
323  av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
324  break;
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");
327  break;
328  }
329 
330  if (previous) {
331  while (count > 0) {
332  hnm->current[writeoffset] = hnm->previous[offset];
333  hnm->current[writeoffset + hnm->width] = hnm->previous[offset + hnm->width];
334  writeoffset++;
335  offset++;
336  count--;
337  }
338  } else {
339  while (count > 0) {
340  hnm->current[writeoffset] = hnm->current[offset];
341  hnm->current[writeoffset + hnm->width] = hnm->current[offset + hnm->width];
342  writeoffset++;
343  offset++;
344  count--;
345  }
346  }
347  }
348  }
349 }
350 
352  uint32_t size)
353 {
354  Hnm4VideoContext *hnm = avctx->priv_data;
355  GetByteContext gb;
356  uint8_t start, writeoffset;
357  uint16_t count;
358  int eight_bit_colors;
359 
360  eight_bit_colors = src[7] & 0x80 && hnm->version == 0x4a;
361 
362  // skip first 8 bytes
363  bytestream2_init(&gb, src + 8, size - 8);
364 
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)
369  break;
370  if (count == 0)
371  count = 256;
372  writeoffset = start;
373  while (count > 0) {
374  hnm->palette[writeoffset] = bytestream2_get_be24(&gb);
375  if (!eight_bit_colors)
376  hnm->palette[writeoffset] <<= 2;
377  count--;
378  writeoffset++;
379  }
380  }
381 }
382 
384 {
385  uint8_t *temp;
386 
387  temp = hnm->current;
388  hnm->current = hnm->previous;
389  hnm->previous = temp;
390 }
391 
392 static int hnm_decode_frame(AVCodecContext *avctx, void *data,
393  int *got_frame, AVPacket *avpkt)
394 {
395  AVFrame *frame = data;
396  Hnm4VideoContext *hnm = avctx->priv_data;
397  int ret;
398  uint16_t chunk_id;
399 
400  if (avpkt->size < 8) {
401  av_log(avctx, AV_LOG_ERROR, "packet too small\n");
402  return AVERROR_INVALIDDATA;
403  }
404 
405  chunk_id = AV_RL16(avpkt->data + 4);
406 
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;
413  }
414  if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
415  return ret;
416 
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);
421  else
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);
427  *got_frame = 1;
428  } else if (chunk_id == HNM4_CHUNK_ID_IU) {
429  if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
430  return ret;
431 
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);
435  } else {
436  decode_interframe_v4(avctx, avpkt->data + 8, avpkt->size - 8);
438  }
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);
443  *got_frame = 1;
444  hnm_flip_buffers(hnm);
445  } else {
446  av_log(avctx, AV_LOG_ERROR, "invalid chunk id: %d\n", chunk_id);
447  return AVERROR_INVALIDDATA;
448  }
449 
450  return avpkt->size;
451 }
452 
454 {
455  Hnm4VideoContext *hnm = avctx->priv_data;
456 
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;
461  }
462 
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);
470 
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);
479  }
480 
481  hnm->current = hnm->buffer1;
482  hnm->previous = hnm->buffer2;
483 
484  return 0;
485 }
486 
488 {
489  Hnm4VideoContext *hnm = avctx->priv_data;
490 
491  av_freep(&hnm->buffer1);
492  av_freep(&hnm->buffer2);
493  av_freep(&hnm->processed);
494 
495  return 0;
496 }
497 
499  .name = "hnm4video",
500  .long_name = NULL_IF_CONFIG_SMALL("HNM 4 video"),
501  .type = AVMEDIA_TYPE_VIDEO,
503  .priv_data_size = sizeof(Hnm4VideoContext),
507  .capabilities = CODEC_CAP_DR1,
508 };