Browse code

Merge commit '9af7a8523a6bb517834ebed36093bdab11a8b38e'

* commit '9af7a8523a6bb517834ebed36093bdab11a8b38e':
HNM4/HNM4A demuxer & video decoder

Conflicts:
Changelog
doc/general.texi
libavcodec/Makefile
libavcodec/allcodecs.c
libavcodec/avcodec.h
libavcodec/version.h
libavformat/Makefile
libavformat/allformats.c
libavformat/version.h

Merged-by: Michael Niedermayer <michaelni@gmx.at>

Michael Niedermayer authored on 2013/11/01 18:39:50
Showing 12 changed files
... ...
@@ -3,6 +3,8 @@ releases are sorted from youngest to oldest.
3 3
 
4 4
 version <next>
5 5
 
6
+- HNM version 4 demuxer and video decoder
7
+
6 8
 
7 9
 version 2.1:
8 10
 
... ...
@@ -249,6 +249,8 @@ library:
249 249
 @item GXF                       @tab X @tab X
250 250
     @tab General eXchange Format SMPTE 360M, used by Thomson Grass Valley
251 251
          playout servers.
252
+@item HNM @tab   @tab X
253
+    @tab Only version 4 supported, used in some games from Cryo Interactive
252 254
 @item iCEDraw File              @tab   @tab X
253 255
 @item ICO                       @tab X @tab X
254 256
     @tab Microsoft Windows ICO
... ...
@@ -607,6 +609,7 @@ following image formats are supported:
607 607
 @item H.263+ / H.263-1998 / H.263 version 2  @tab  X  @tab  X
608 608
 @item H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10  @tab  E  @tab  X
609 609
     @tab encoding supported through external library libx264
610
+@item HNM version 4          @tab     @tab  X
610 611
 @item HuffYUV                @tab  X  @tab  X
611 612
 @item HuffYUV FFmpeg variant @tab  X  @tab  X
612 613
 @item IBM Ultimotion         @tab     @tab  X
... ...
@@ -236,7 +236,7 @@ OBJS-$(CONFIG_H264_VDA_DECODER)        += vda_h264_dec.o
236 236
 OBJS-$(CONFIG_HEVC_DECODER)            += hevc.o hevc_mvs.o hevc_ps.o hevc_sei.o \
237 237
                                           hevc_cabac.o hevc_refs.o hevcpred.o    \
238 238
                                           hevcdsp.o hevc_filter.o cabac.o
239
-
239
+OBJS-$(CONFIG_HNM4_VIDEO_DECODER)      += hnm4video.o
240 240
 OBJS-$(CONFIG_HUFFYUV_DECODER)         += huffyuv.o huffyuvdec.o
241 241
 OBJS-$(CONFIG_HUFFYUV_ENCODER)         += huffyuv.o huffyuvenc.o
242 242
 OBJS-$(CONFIG_IAC_DECODER)             += imc.o
... ...
@@ -165,6 +165,7 @@ void avcodec_register_all(void)
165 165
     REGISTER_DECODER(H264_VDA,          h264_vda);
166 166
     REGISTER_DECODER(H264_VDPAU,        h264_vdpau);
167 167
     REGISTER_DECODER(HEVC,              hevc);
168
+    REGISTER_DECODER(HNM4_VIDEO,        hnm4_video);
168 169
     REGISTER_ENCDEC (HUFFYUV,           huffyuv);
169 170
     REGISTER_DECODER(IDCIN,             idcin);
170 171
     REGISTER_DECODER(IFF_BYTERUN1,      iff_byterun1);
... ...
@@ -276,6 +276,7 @@ enum AVCodecID {
276 276
     AV_CODEC_ID_ESCAPE130_DEPRECATED,
277 277
     AV_CODEC_ID_G2M_DEPRECATED,
278 278
     AV_CODEC_ID_WEBP_DEPRECATED,
279
+    AV_CODEC_ID_HNM4_VIDEO,
279 280
 
280 281
     AV_CODEC_ID_BRENDER_PIX= MKBETAG('B','P','I','X'),
281 282
     AV_CODEC_ID_Y41P       = MKBETAG('Y','4','1','P'),
... ...
@@ -1394,6 +1394,13 @@ static const AVCodecDescriptor codec_descriptors[] = {
1394 1394
         .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY |
1395 1395
                      AV_CODEC_PROP_LOSSLESS,
1396 1396
     },
1397
+    {
1398
+        .id        = AV_CODEC_ID_HNM4_VIDEO,
1399
+        .type      = AVMEDIA_TYPE_VIDEO,
1400
+        .name      = "hnm4video",
1401
+        .long_name = NULL_IF_CONFIG_SMALL("HNM 4 video"),
1402
+        .props     = AV_CODEC_PROP_LOSSY,
1403
+    },
1397 1404
 
1398 1405
     /* various PCM "codecs" */
1399 1406
     {
1400 1407
new file mode 100644
... ...
@@ -0,0 +1,459 @@
0
+/*
1
+ * Cryo Interactive Entertainment HNM4 video decoder
2
+ *
3
+ * Copyright (c) 2012 David Kment
4
+ *
5
+ * This file is part of FFmpeg.
6
+ *
7
+ * FFmpeg is free software; you can redistribute it and/or
8
+ * modify it under the terms of the GNU Lesser General Public
9
+ * License as published by the Free Software Foundation; either
10
+ * version 2.1 of the License, or (at your option) any later version.
11
+ *
12
+ * FFmpeg is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
+ * Lesser General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU Lesser General Public
18
+ * License along with FFmpeg; if not, write to the Free Software
19
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
+ */
21
+
22
+#include <string.h>
23
+
24
+#include "libavutil/internal.h"
25
+#include "libavutil/intreadwrite.h"
26
+#include "libavutil/mem.h"
27
+#include "avcodec.h"
28
+#include "bytestream.h"
29
+#include "internal.h"
30
+
31
+#define HNM4_CHUNK_ID_PL 19536
32
+#define HNM4_CHUNK_ID_IZ 23113
33
+#define HNM4_CHUNK_ID_IU 21833
34
+#define HNM4_CHUNK_ID_SD 17491
35
+
36
+typedef struct Hnm4VideoContext {
37
+    uint8_t version;
38
+    uint16_t width;
39
+    uint16_t height;
40
+    uint8_t *current;
41
+    uint8_t *previous;
42
+    uint8_t *buffer1;
43
+    uint8_t *buffer2;
44
+    uint8_t *processed;
45
+    uint32_t palette[256];
46
+} Hnm4VideoContext;
47
+
48
+static int getbit(GetByteContext *gb, uint32_t *bitbuf, int *bits)
49
+{
50
+    int ret;
51
+
52
+    if (!*bits) {
53
+        *bitbuf = bytestream2_get_le32(gb);
54
+        *bits = 32;
55
+    }
56
+
57
+    ret = *bitbuf >> 31;
58
+    *bitbuf <<= 1;
59
+    (*bits)--;
60
+
61
+    return ret;
62
+}
63
+
64
+static void unpack_intraframe(AVCodecContext *avctx, uint8_t *src,
65
+                              uint32_t size)
66
+{
67
+    Hnm4VideoContext *hnm = avctx->priv_data;
68
+    GetByteContext gb;
69
+    uint32_t bitbuf = 0, writeoffset = 0, count = 0;
70
+    uint16_t word;
71
+    int32_t offset;
72
+    int bits = 0;
73
+
74
+    bytestream2_init(&gb, src, size);
75
+
76
+    while (bytestream2_tell(&gb) < size) {
77
+        if (getbit(&gb, &bitbuf, &bits)) {
78
+            if (writeoffset >= hnm->width * hnm->height) {
79
+                av_log(avctx, AV_LOG_ERROR,
80
+                       "Attempting to write out of bounds");
81
+                break;
82
+            }
83
+            hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
84
+        } else {
85
+            if (getbit(&gb, &bitbuf, &bits)) {
86
+                word   = bytestream2_get_le16(&gb);
87
+                count  = word & 0x07;
88
+                offset = (word >> 3) - 0x2000;
89
+                if (!count)
90
+                    count = bytestream2_get_byte(&gb);
91
+                if (!count)
92
+                    return;
93
+            } else {
94
+                count  = getbit(&gb, &bitbuf, &bits) * 2;
95
+                count += getbit(&gb, &bitbuf, &bits);
96
+                offset = bytestream2_get_byte(&gb) - 0x0100;
97
+            }
98
+            count  += 2;
99
+            offset += writeoffset;
100
+            if (offset < 0 || offset + count >= hnm->width * hnm->height) {
101
+                av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds");
102
+                break;
103
+            } else if (writeoffset + count >= hnm->width * hnm->height) {
104
+                av_log(avctx, AV_LOG_ERROR,
105
+                       "Attempting to write out of bounds");
106
+                break;
107
+            }
108
+            while (count--) {
109
+                hnm->current[writeoffset++] = hnm->current[offset++];
110
+            }
111
+        }
112
+    }
113
+}
114
+
115
+static void postprocess_current_frame(AVCodecContext *avctx)
116
+{
117
+    Hnm4VideoContext *hnm = avctx->priv_data;
118
+    uint32_t x, y, src_x, src_y;
119
+
120
+    for (y = 0; y < hnm->height; y++) {
121
+        src_y = y - (y % 2);
122
+        src_x = src_y * hnm->width + (y % 2);
123
+        for (x = 0; x < hnm->width; x++) {
124
+            hnm->processed[(y * hnm->width) + x] = hnm->current[src_x];
125
+            src_x += 2;
126
+        }
127
+    }
128
+}
129
+
130
+static void copy_processed_frame(AVCodecContext *avctx, AVFrame *frame)
131
+{
132
+    Hnm4VideoContext *hnm = avctx->priv_data;
133
+    uint8_t *src = hnm->processed;
134
+    uint8_t *dst = frame->data[0];
135
+    int y;
136
+
137
+    for (y = 0; y < hnm->height; y++) {
138
+        memcpy(dst, src, hnm->width);
139
+        src += hnm->width;
140
+        dst += frame->linesize[0];
141
+    }
142
+}
143
+
144
+static void decode_interframe_v4(AVCodecContext *avctx, uint8_t *src, uint32_t size)
145
+{
146
+    Hnm4VideoContext *hnm = avctx->priv_data;
147
+    GetByteContext gb;
148
+    uint32_t writeoffset = 0, count, left, offset;
149
+    uint8_t tag, previous, backline, backward, swap;
150
+
151
+    bytestream2_init(&gb, src, size);
152
+
153
+    while (bytestream2_tell(&gb) < size) {
154
+        count = bytestream2_peek_byte(&gb) & 0x1F;
155
+        if (count == 0) {
156
+            tag = bytestream2_get_byte(&gb) & 0xE0;
157
+            tag = tag >> 5;
158
+            if (tag == 0) {
159
+                hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
160
+                hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
161
+            } else if (tag == 1) {
162
+                writeoffset += bytestream2_get_byte(&gb) * 2;
163
+            } else if (tag == 2) {
164
+                count = bytestream2_get_le16(&gb);
165
+                count *= 2;
166
+                writeoffset += count;
167
+            } else if (tag == 3) {
168
+                count = bytestream2_get_byte(&gb) * 2;
169
+                while (count > 0) {
170
+                    hnm->current[writeoffset++] = bytestream2_peek_byte(&gb);
171
+                    count--;
172
+                }
173
+                bytestream2_skip(&gb, 1);
174
+            } else {
175
+                break;
176
+            }
177
+        } else {
178
+            previous = bytestream2_peek_byte(&gb) & 0x20;
179
+            backline = bytestream2_peek_byte(&gb) & 0x40;
180
+            backward = bytestream2_peek_byte(&gb) & 0x80;
181
+            bytestream2_skip(&gb, 1);
182
+            swap   = bytestream2_peek_byte(&gb) & 0x01;
183
+            offset = bytestream2_get_le16(&gb);
184
+            offset = (offset >> 1) & 0x7FFF;
185
+            offset = writeoffset + (offset * 2) - 0x8000;
186
+
187
+            left = count;
188
+
189
+            if (!backward && offset + count >= hnm->width * hnm->height) {
190
+                av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds");
191
+                break;
192
+            } else if (backward && offset >= hnm->width * hnm->height) {
193
+                av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds");
194
+                break;
195
+            } else if (writeoffset + count >= hnm->width * hnm->height) {
196
+                av_log(avctx, AV_LOG_ERROR,
197
+                       "Attempting to write out of bounds");
198
+                break;
199
+            }
200
+
201
+            if (previous) {
202
+                while (left > 0) {
203
+                    if (backline) {
204
+                        hnm->current[writeoffset++] = hnm->previous[offset - (2 * hnm->width) + 1];
205
+                        hnm->current[writeoffset++] = hnm->previous[offset++];
206
+                        offset++;
207
+                    } else {
208
+                        hnm->current[writeoffset++] = hnm->previous[offset++];
209
+                        hnm->current[writeoffset++] = hnm->previous[offset++];
210
+                    }
211
+                    if (backward)
212
+                        offset -= 4;
213
+                    left--;
214
+                }
215
+            } else {
216
+                while (left > 0) {
217
+                    if (backline) {
218
+                        hnm->current[writeoffset++] = hnm->current[offset - (2 * hnm->width) + 1];
219
+                        hnm->current[writeoffset++] = hnm->current[offset++];
220
+                        offset++;
221
+                    } else {
222
+                        hnm->current[writeoffset++] = hnm->current[offset++];
223
+                        hnm->current[writeoffset++] = hnm->current[offset++];
224
+                    }
225
+                    if (backward)
226
+                        offset -= 4;
227
+                    left--;
228
+                }
229
+            }
230
+
231
+            if (swap) {
232
+                left         = count;
233
+                writeoffset -= count * 2;
234
+                while (left > 0) {
235
+                    swap = hnm->current[writeoffset];
236
+                    hnm->current[writeoffset] = hnm->current[writeoffset + 1];
237
+                    hnm->current[writeoffset + 1] = swap;
238
+                    left--;
239
+                    writeoffset += 2;
240
+                }
241
+            }
242
+        }
243
+    }
244
+}
245
+
246
+static void decode_interframe_v4a(AVCodecContext *avctx, uint8_t *src,
247
+                                  uint32_t size)
248
+{
249
+    Hnm4VideoContext *hnm = avctx->priv_data;
250
+    GetByteContext gb;
251
+    uint32_t writeoffset = 0, offset;
252
+    uint8_t tag, count, previous, delta;
253
+
254
+    bytestream2_init(&gb, src, size);
255
+
256
+    while (bytestream2_tell(&gb) < size) {
257
+        count = bytestream2_peek_byte(&gb) & 0x3F;
258
+        if (count == 0) {
259
+            tag = bytestream2_get_byte(&gb) & 0xC0;
260
+            tag = tag >> 6;
261
+            if (tag == 0) {
262
+                writeoffset += bytestream2_get_byte(&gb);
263
+            } else if (tag == 1) {
264
+                hnm->current[writeoffset]              = bytestream2_get_byte(&gb);
265
+                hnm->current[writeoffset + hnm->width] = bytestream2_get_byte(&gb);
266
+                writeoffset++;
267
+            } else if (tag == 2) {
268
+                writeoffset += hnm->width;
269
+            } else if (tag == 3) {
270
+                break;
271
+            }
272
+        } else {
273
+            delta    = bytestream2_peek_byte(&gb) & 0x80;
274
+            previous = bytestream2_peek_byte(&gb) & 0x40;
275
+            bytestream2_skip(&gb, 1);
276
+
277
+            offset  = writeoffset;
278
+            offset += bytestream2_get_le16(&gb);
279
+
280
+            if (delta)
281
+                offset -= 0x10000;
282
+
283
+            if (offset + hnm->width + count >= hnm->width * hnm->height) {
284
+                av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds");
285
+                break;
286
+            } else if (writeoffset + hnm->width + count >= hnm->width * hnm->height) {
287
+                av_log(avctx, AV_LOG_ERROR, "Attempting to write out of bounds");
288
+                break;
289
+            }
290
+
291
+            if (previous) {
292
+                while (count > 0) {
293
+                    hnm->current[writeoffset]              = hnm->previous[offset];
294
+                    hnm->current[writeoffset + hnm->width] = hnm->previous[offset + hnm->width];
295
+                    writeoffset++;
296
+                    offset++;
297
+                    count--;
298
+                }
299
+            } else {
300
+                while (count > 0) {
301
+                    hnm->current[writeoffset]              = hnm->current[offset];
302
+                    hnm->current[writeoffset + hnm->width] = hnm->current[offset + hnm->width];
303
+                    writeoffset++;
304
+                    offset++;
305
+                    count--;
306
+                }
307
+            }
308
+        }
309
+    }
310
+}
311
+
312
+static void hnm_update_palette(AVCodecContext *avctx, uint8_t *src,
313
+                               uint32_t size)
314
+{
315
+    Hnm4VideoContext *hnm = avctx->priv_data;
316
+    GetByteContext gb;
317
+    uint8_t start, writeoffset;
318
+    uint16_t count;
319
+    int eight_bit_colors;
320
+
321
+    eight_bit_colors = src[7] & 0x80 && hnm->version == 0x4a;
322
+
323
+    // skip first 8 bytes
324
+    bytestream2_init(&gb, src + 8, size - 8);
325
+
326
+    while (bytestream2_tell(&gb) < size - 8) {
327
+        start = bytestream2_get_byte(&gb);
328
+        count = bytestream2_get_byte(&gb);
329
+        if (start == 255 && count == 255)
330
+            break;
331
+        if (count == 0)
332
+            count = 256;
333
+        writeoffset = start;
334
+        while (count > 0) {
335
+            hnm->palette[writeoffset] = bytestream2_get_be24(&gb);
336
+            if (!eight_bit_colors)
337
+                hnm->palette[writeoffset] <<= 2;
338
+            count--;
339
+            writeoffset++;
340
+        }
341
+    }
342
+}
343
+
344
+static void hnm_flip_buffers(Hnm4VideoContext *hnm)
345
+{
346
+    uint8_t *temp;
347
+
348
+    temp          = hnm->current;
349
+    hnm->current  = hnm->previous;
350
+    hnm->previous = temp;
351
+}
352
+
353
+static int hnm_decode_frame(AVCodecContext *avctx, void *data,
354
+                            int *got_frame, AVPacket *avpkt)
355
+{
356
+    AVFrame *frame = data;
357
+    Hnm4VideoContext *hnm = avctx->priv_data;
358
+    int ret;
359
+    uint16_t chunk_id;
360
+
361
+    if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) {
362
+        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
363
+        return ret;
364
+    }
365
+
366
+    chunk_id = AV_RL16(avpkt->data + 4);
367
+
368
+    if (chunk_id == HNM4_CHUNK_ID_PL) {
369
+        hnm_update_palette(avctx, avpkt->data, avpkt->size);
370
+        frame->palette_has_changed = 1;
371
+    } else if (chunk_id == HNM4_CHUNK_ID_IZ) {
372
+        unpack_intraframe(avctx, avpkt->data + 12, avpkt->size - 12);
373
+        memcpy(hnm->previous, hnm->current, hnm->width * hnm->height);
374
+        if (hnm->version == 0x4a)
375
+            memcpy(hnm->processed, hnm->current, hnm->width * hnm->height);
376
+        else
377
+            postprocess_current_frame(avctx);
378
+        copy_processed_frame(avctx, frame);
379
+        frame->pict_type = AV_PICTURE_TYPE_I;
380
+        frame->key_frame = 1;
381
+        memcpy(frame->data[1], hnm->palette, 256 * 4);
382
+        *got_frame = 1;
383
+    } else if (chunk_id == HNM4_CHUNK_ID_IU) {
384
+        if (hnm->version == 0x4a) {
385
+            decode_interframe_v4a(avctx, avpkt->data + 8, avpkt->size - 8);
386
+            memcpy(hnm->processed, hnm->current, hnm->width * hnm->height);
387
+        } else {
388
+            decode_interframe_v4(avctx, avpkt->data + 8, avpkt->size - 8);
389
+            postprocess_current_frame(avctx);
390
+        }
391
+        copy_processed_frame(avctx, frame);
392
+        frame->pict_type = AV_PICTURE_TYPE_P;
393
+        frame->key_frame = 0;
394
+        memcpy(frame->data[1], hnm->palette, 256 * 4);
395
+        *got_frame = 1;
396
+        hnm_flip_buffers(hnm);
397
+    } else {
398
+        av_log(avctx, AV_LOG_ERROR, "invalid chunk id: %d\n", chunk_id);
399
+        return AVERROR_INVALIDDATA;
400
+    }
401
+
402
+    return avpkt->size;
403
+}
404
+
405
+static av_cold int hnm_decode_init(AVCodecContext *avctx)
406
+{
407
+    Hnm4VideoContext *hnm = avctx->priv_data;
408
+
409
+    if (avctx->extradata_size < 1) {
410
+        av_log(avctx, AV_LOG_ERROR,
411
+               "Extradata missing, decoder requires version number\n");
412
+        return AVERROR_INVALIDDATA;
413
+    }
414
+
415
+    hnm->version   = avctx->extradata[0];
416
+    avctx->pix_fmt = AV_PIX_FMT_PAL8;
417
+    hnm->width     = avctx->width;
418
+    hnm->height    = avctx->height;
419
+    hnm->buffer1   = av_mallocz(avctx->width * avctx->height);
420
+    hnm->buffer2   = av_mallocz(avctx->width * avctx->height);
421
+    hnm->processed = av_mallocz(avctx->width * avctx->height);
422
+
423
+    if (!hnm->buffer1 || !hnm->buffer2 || !hnm->processed) {
424
+        av_log(avctx, AV_LOG_ERROR, "av_mallocz() failed\n");
425
+        av_freep(&hnm->buffer1);
426
+        av_freep(&hnm->buffer2);
427
+        av_freep(&hnm->processed);
428
+        return AVERROR(ENOMEM);
429
+    }
430
+
431
+    hnm->current  = hnm->buffer1;
432
+    hnm->previous = hnm->buffer2;
433
+
434
+    return 0;
435
+}
436
+
437
+static av_cold int hnm_decode_end(AVCodecContext *avctx)
438
+{
439
+    Hnm4VideoContext *hnm = avctx->priv_data;
440
+
441
+    av_freep(&hnm->buffer1);
442
+    av_freep(&hnm->buffer2);
443
+    av_freep(&hnm->processed);
444
+
445
+    return 0;
446
+}
447
+
448
+AVCodec ff_hnm4_video_decoder = {
449
+    .name           = "hnm4video",
450
+    .long_name      = NULL_IF_CONFIG_SMALL("HNM 4 video"),
451
+    .type           = AVMEDIA_TYPE_VIDEO,
452
+    .id             = AV_CODEC_ID_HNM4_VIDEO,
453
+    .priv_data_size = sizeof(Hnm4VideoContext),
454
+    .init           = hnm_decode_init,
455
+    .close          = hnm_decode_end,
456
+    .decode         = hnm_decode_frame,
457
+    .capabilities   = CODEC_CAP_DR1,
458
+};
... ...
@@ -29,7 +29,7 @@
29 29
 #include "libavutil/avutil.h"
30 30
 
31 31
 #define LIBAVCODEC_VERSION_MAJOR 55
32
-#define LIBAVCODEC_VERSION_MINOR  39
32
+#define LIBAVCODEC_VERSION_MINOR  40
33 33
 #define LIBAVCODEC_VERSION_MICRO 100
34 34
 
35 35
 #define LIBAVCODEC_VERSION_INT  AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
... ...
@@ -169,6 +169,7 @@ OBJS-$(CONFIG_H264_MUXER)                += rawenc.o
169 169
 OBJS-$(CONFIG_HEVC_DEMUXER)              += hevcdec.o rawdec.o
170 170
 OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o
171 171
 OBJS-$(CONFIG_HLS_MUXER)                 += hlsenc.o
172
+OBJS-$(CONFIG_HNM_DEMUXER)               += hnm.o
172 173
 OBJS-$(CONFIG_ICO_DEMUXER)               += icodec.o
173 174
 OBJS-$(CONFIG_ICO_MUXER)                 += icoenc.o
174 175
 OBJS-$(CONFIG_IDCIN_DEMUXER)             += idcin.o
... ...
@@ -136,6 +136,7 @@ void av_register_all(void)
136 136
     REGISTER_MUXDEMUX(H264,             h264);
137 137
     REGISTER_DEMUXER (HEVC,             hevc);
138 138
     REGISTER_MUXDEMUX(HLS,              hls);
139
+    REGISTER_DEMUXER (HNM,              hnm);
139 140
     REGISTER_MUXDEMUX(ICO,              ico);
140 141
     REGISTER_DEMUXER (IDCIN,            idcin);
141 142
     REGISTER_DEMUXER (IDF,              idf);
142 143
new file mode 100644
... ...
@@ -0,0 +1,204 @@
0
+/*
1
+ * Cryo Interactive Entertainment HNM4 demuxer
2
+ *
3
+ * Copyright (c) 2012 David Kment
4
+ *
5
+ * This file is part of FFmpeg .
6
+ *
7
+ * FFmpeg  is free software; you can redistribute it and/or
8
+ * modify it under the terms of the GNU Lesser General Public
9
+ * License as published by the Free Software Foundation; either
10
+ * version 2.1 of the License, or (at your option) any later version.
11
+ *
12
+ * FFmpeg  is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
+ * Lesser General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU Lesser General Public
18
+ * License along with FFmpeg ; if not, write to the Free Software
19
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
+ */
21
+
22
+#include "libavutil/intreadwrite.h"
23
+#include "avformat.h"
24
+#include "internal.h"
25
+
26
+#define HNM4_TAG MKTAG('H', 'N', 'M', '4')
27
+
28
+#define HNM4_SAMPLE_RATE 22050
29
+#define HNM4_FRAME_FPS 24
30
+
31
+#define HNM4_CHUNK_ID_PL 19536
32
+#define HNM4_CHUNK_ID_IZ 23113
33
+#define HNM4_CHUNK_ID_IU 21833
34
+#define HNM4_CHUNK_ID_SD 17491
35
+
36
+typedef struct Hnm4DemuxContext {
37
+    uint8_t version;
38
+    uint16_t width;
39
+    uint16_t height;
40
+    uint32_t filesize;
41
+    uint32_t frames;
42
+    uint32_t taboffset;
43
+    uint16_t bits;
44
+    uint16_t channels;
45
+    uint32_t framesize;
46
+    uint32_t currentframe;
47
+    int64_t pts;
48
+    uint32_t superchunk_remaining;
49
+    AVPacket vpkt;
50
+} Hnm4DemuxContext;
51
+
52
+static int hnm_probe(AVProbeData *p)
53
+{
54
+    if (p->buf_size < 4)
55
+        return 0;
56
+
57
+    // check for HNM4 header.
58
+    // currently only HNM v4/v4A is supported
59
+    if (AV_RL32(&p->buf[0]) == HNM4_TAG)
60
+        return AVPROBE_SCORE_MAX;
61
+
62
+    return 0;
63
+}
64
+
65
+static int hnm_read_header(AVFormatContext *s)
66
+{
67
+    Hnm4DemuxContext *hnm = s->priv_data;
68
+    AVIOContext *pb = s->pb;
69
+    AVStream *vst;
70
+
71
+    /* default context members */
72
+    hnm->pts = 0;
73
+    av_init_packet(&hnm->vpkt);
74
+    hnm->vpkt.data = NULL;
75
+    hnm->vpkt.size = 0;
76
+
77
+    hnm->superchunk_remaining = 0;
78
+
79
+    avio_skip(pb, 8);
80
+    hnm->width     = avio_rl16(pb);
81
+    hnm->height    = avio_rl16(pb);
82
+    hnm->filesize  = avio_rl32(pb);
83
+    hnm->frames    = avio_rl32(pb);
84
+    hnm->taboffset = avio_rl32(pb);
85
+    hnm->bits      = avio_rl16(pb);
86
+    hnm->channels  = avio_rl16(pb);
87
+    hnm->framesize = avio_rl32(pb);
88
+    avio_skip(pb, 32);
89
+
90
+    hnm->currentframe = 0;
91
+
92
+    if (hnm->width  < 320 || hnm->width  > 640 ||
93
+        hnm->height < 150 || hnm->height > 480) {
94
+        av_log(s, AV_LOG_ERROR,
95
+               "invalid resolution: %ux%u\n", hnm->width, hnm->height);
96
+        return AVERROR_INVALIDDATA;
97
+    }
98
+
99
+    // TODO: find a better way to detect HNM4A
100
+    if (hnm->width == 640)
101
+        hnm->version = 0x4a;
102
+    else
103
+        hnm->version = 0x40;
104
+
105
+    if (!(vst = avformat_new_stream(s, NULL)))
106
+        return AVERROR(ENOMEM);
107
+
108
+    vst->codec->codec_type = AVMEDIA_TYPE_VIDEO;
109
+    vst->codec->codec_id   = AV_CODEC_ID_HNM4_VIDEO;
110
+    vst->codec->codec_tag  = 0;
111
+    vst->codec->width      = hnm->width;
112
+    vst->codec->height     = hnm->height;
113
+    vst->codec->extradata  = av_mallocz(1);
114
+
115
+    vst->codec->extradata_size = 1;
116
+    memcpy(vst->codec->extradata, &hnm->version, 1);
117
+
118
+    vst->start_time = 0;
119
+
120
+    avpriv_set_pts_info(vst, 33, 1, HNM4_FRAME_FPS);
121
+
122
+    return 0;
123
+}
124
+
125
+static int hnm_read_packet(AVFormatContext *s, AVPacket *pkt)
126
+{
127
+    Hnm4DemuxContext *hnm = s->priv_data;
128
+    AVIOContext *pb = s->pb;
129
+    int ret = 0;
130
+
131
+    uint32_t superchunk_size, chunk_size;
132
+    uint16_t chunk_id;
133
+
134
+    if (hnm->currentframe == hnm->frames || pb->eof_reached)
135
+        return AVERROR_EOF;
136
+
137
+    if (hnm->superchunk_remaining == 0) {
138
+        /* parse next superchunk */
139
+        superchunk_size = avio_rl24(pb);
140
+        avio_skip(pb, 1);
141
+
142
+        hnm->superchunk_remaining = superchunk_size - 4;
143
+    }
144
+
145
+    chunk_size = avio_rl24(pb);
146
+    avio_skip(pb, 1);
147
+    chunk_id = avio_rl16(pb);
148
+    avio_skip(pb, 2);
149
+
150
+    if (chunk_size > hnm->superchunk_remaining) {
151
+        av_log(s, AV_LOG_ERROR, "invalid chunk size: %u, offset: %u\n",
152
+               chunk_size, (int) avio_tell(pb));
153
+        avio_skip(pb, hnm->superchunk_remaining - 8);
154
+        hnm->superchunk_remaining = 0;
155
+    }
156
+
157
+    switch (chunk_id) {
158
+    case HNM4_CHUNK_ID_PL:
159
+    case HNM4_CHUNK_ID_IZ:
160
+    case HNM4_CHUNK_ID_IU:
161
+        avio_seek(pb, -8, SEEK_CUR);
162
+        ret += av_get_packet(pb, pkt, chunk_size);
163
+        hnm->superchunk_remaining -= chunk_size;
164
+        if (chunk_id == HNM4_CHUNK_ID_IZ || chunk_id == HNM4_CHUNK_ID_IU)
165
+            hnm->currentframe++;
166
+        break;
167
+
168
+    case HNM4_CHUNK_ID_SD:
169
+        avio_skip(pb, chunk_size - 8);
170
+        hnm->superchunk_remaining -= chunk_size;
171
+        break;
172
+
173
+    default:
174
+        av_log(s, AV_LOG_WARNING, "unknown chunk found: %d, offset: %d\n",
175
+               chunk_id, (int) avio_tell(pb));
176
+        avio_skip(pb, chunk_size - 8);
177
+        hnm->superchunk_remaining -= chunk_size;
178
+        break;
179
+    }
180
+
181
+    return ret;
182
+}
183
+
184
+static int hnm_read_close(AVFormatContext *s)
185
+{
186
+    Hnm4DemuxContext *hnm = s->priv_data;
187
+
188
+    if (hnm->vpkt.size > 0)
189
+        av_free_packet(&hnm->vpkt);
190
+
191
+    return 0;
192
+}
193
+
194
+AVInputFormat ff_hnm_demuxer = {
195
+    .name           = "hnm",
196
+    .long_name      = NULL_IF_CONFIG_SMALL("Cryo HNM v4"),
197
+    .priv_data_size = sizeof(Hnm4DemuxContext),
198
+    .read_probe     = hnm_probe,
199
+    .read_header    = hnm_read_header,
200
+    .read_packet    = hnm_read_packet,
201
+    .read_close     = hnm_read_close,
202
+    .flags          = AVFMT_NO_BYTE_SEEK | AVFMT_NOGENSEARCH | AVFMT_NOBINSEARCH
203
+};
... ...
@@ -30,8 +30,8 @@
30 30
 #include "libavutil/avutil.h"
31 31
 
32 32
 #define LIBAVFORMAT_VERSION_MAJOR 55
33
-#define LIBAVFORMAT_VERSION_MINOR 19
34
-#define LIBAVFORMAT_VERSION_MICRO 104
33
+#define LIBAVFORMAT_VERSION_MINOR 20
34
+#define LIBAVFORMAT_VERSION_MICRO 100
35 35
 
36 36
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
37 37
                                                LIBAVFORMAT_VERSION_MINOR, \