Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
6147 serge 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 
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 {
39
    uint8_t version;
40
    int width;
41
    int height;
42
    uint8_t *current;
43
    uint8_t *previous;
44
    uint8_t *buffer1;
45
    uint8_t *buffer2;
46
    uint8_t *processed;
47
    uint32_t palette[256];
48
} Hnm4VideoContext;
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\n");
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\n");
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\n");
108
                break;
109
            }
110
            while (count--) {
111
                hnm->current[writeoffset++] = hnm->current[offset++];
112
            }
113
        }
114
    }
115
}
116
 
117
static void postprocess_current_frame(AVCodecContext *avctx)
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;
151
    int count, left, offset;
152
    uint8_t tag, previous, backline, backward, swap;
153
 
154
    bytestream2_init(&gb, src, size);
155
 
156
    while (bytestream2_tell(&gb) < size) {
157
        count = bytestream2_peek_byte(&gb) & 0x1F;
158
        if (count == 0) {
159
            tag = bytestream2_get_byte(&gb) & 0xE0;
160
            tag = tag >> 5;
161
 
162
            if (tag == 0) {
163
                if (writeoffset + 2 > hnm->width * hnm->height) {
164
                    av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
165
                    break;
166
                }
167
                hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
168
                hnm->current[writeoffset++] = bytestream2_get_byte(&gb);
169
            } else if (tag == 1) {
170
                writeoffset += bytestream2_get_byte(&gb) * 2;
171
            } else if (tag == 2) {
172
                count = bytestream2_get_le16(&gb);
173
                count *= 2;
174
                writeoffset += count;
175
            } else if (tag == 3) {
176
                count = bytestream2_get_byte(&gb) * 2;
177
                if (writeoffset + count > hnm->width * hnm->height) {
178
                    av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
179
                    break;
180
                }
181
                while (count > 0) {
182
                    hnm->current[writeoffset++] = bytestream2_peek_byte(&gb);
183
                    count--;
184
                }
185
                bytestream2_skip(&gb, 1);
186
            } else {
187
                break;
188
            }
189
            if (writeoffset > hnm->width * hnm->height) {
190
                av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
191
                break;
192
            }
193
        } else {
194
            previous = bytestream2_peek_byte(&gb) & 0x20;
195
            backline = bytestream2_peek_byte(&gb) & 0x40;
196
            backward = bytestream2_peek_byte(&gb) & 0x80;
197
            bytestream2_skip(&gb, 1);
198
            swap   = bytestream2_peek_byte(&gb) & 0x01;
199
            offset = bytestream2_get_le16(&gb);
200
            offset = (offset >> 1) & 0x7FFF;
201
            offset = writeoffset + (offset * 2) - 0x8000;
202
 
203
            left = count;
204
 
205
            if (!backward && offset + 2*count > hnm->width * hnm->height) {
206
                av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
207
                break;
208
            } else if (backward && offset + 1 >= hnm->width * hnm->height) {
209
                av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
210
                break;
211
            } else if (writeoffset + 2*count > hnm->width * hnm->height) {
212
                av_log(avctx, AV_LOG_ERROR,
213
                       "Attempting to write out of bounds\n");
214
                break;
215
            }
216
            if(backward) {
217
                if (offset < (!!backline)*(2 * hnm->width - 1) + 2*(left-1)) {
218
                    av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
219
                    break;
220
                }
221
            } else {
222
                if (offset < (!!backline)*(2 * hnm->width - 1)) {
223
                    av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
224
                    break;
225
                }
226
            }
227
 
228
            if (previous) {
229
                while (left > 0) {
230
                    if (backline) {
231
                        hnm->current[writeoffset++] = hnm->previous[offset - (2 * hnm->width) + 1];
232
                        hnm->current[writeoffset++] = hnm->previous[offset++];
233
                        offset++;
234
                    } else {
235
                        hnm->current[writeoffset++] = hnm->previous[offset++];
236
                        hnm->current[writeoffset++] = hnm->previous[offset++];
237
                    }
238
                    if (backward)
239
                        offset -= 4;
240
                    left--;
241
                }
242
            } else {
243
                while (left > 0) {
244
                    if (backline) {
245
                        hnm->current[writeoffset++] = hnm->current[offset - (2 * hnm->width) + 1];
246
                        hnm->current[writeoffset++] = hnm->current[offset++];
247
                        offset++;
248
                    } else {
249
                        hnm->current[writeoffset++] = hnm->current[offset++];
250
                        hnm->current[writeoffset++] = hnm->current[offset++];
251
                    }
252
                    if (backward)
253
                        offset -= 4;
254
                    left--;
255
                }
256
            }
257
 
258
            if (swap) {
259
                left         = count;
260
                writeoffset -= count * 2;
261
                while (left > 0) {
262
                    swap = hnm->current[writeoffset];
263
                    hnm->current[writeoffset] = hnm->current[writeoffset + 1];
264
                    hnm->current[writeoffset + 1] = swap;
265
                    left--;
266
                    writeoffset += 2;
267
                }
268
            }
269
        }
270
    }
271
}
272
 
273
static void decode_interframe_v4a(AVCodecContext *avctx, uint8_t *src,
274
                                  uint32_t size)
275
{
276
    Hnm4VideoContext *hnm = avctx->priv_data;
277
    GetByteContext gb;
278
    uint32_t writeoffset = 0, offset;
279
    uint8_t tag, count, previous, delta;
280
 
281
    bytestream2_init(&gb, src, size);
282
 
283
    while (bytestream2_tell(&gb) < size) {
284
        count = bytestream2_peek_byte(&gb) & 0x3F;
285
        if (count == 0) {
286
            tag = bytestream2_get_byte(&gb) & 0xC0;
287
            tag = tag >> 6;
288
            if (tag == 0) {
289
                writeoffset += bytestream2_get_byte(&gb);
290
            } else if (tag == 1) {
291
                if (writeoffset + hnm->width >= hnm->width * hnm->height) {
292
                    av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
293
                    break;
294
                }
295
                hnm->current[writeoffset]              = bytestream2_get_byte(&gb);
296
                hnm->current[writeoffset + hnm->width] = bytestream2_get_byte(&gb);
297
                writeoffset++;
298
            } else if (tag == 2) {
299
                writeoffset += hnm->width;
300
            } else if (tag == 3) {
301
                break;
302
            }
303
            if (writeoffset > hnm->width * hnm->height) {
304
                av_log(avctx, AV_LOG_ERROR, "writeoffset out of bounds\n");
305
                break;
306
            }
307
        } else {
308
            delta    = bytestream2_peek_byte(&gb) & 0x80;
309
            previous = bytestream2_peek_byte(&gb) & 0x40;
310
            bytestream2_skip(&gb, 1);
311
 
312
            offset  = writeoffset;
313
            offset += bytestream2_get_le16(&gb);
314
 
315
            if (delta) {
316
                if (offset < 0x10000) {
317
                    av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
318
                    break;
319
                }
320
                offset -= 0x10000;
321
            }
322
 
323
            if (offset + hnm->width + count >= hnm->width * hnm->height) {
324
                av_log(avctx, AV_LOG_ERROR, "Attempting to read out of bounds\n");
325
                break;
326
            } else if (writeoffset + hnm->width + count >= hnm->width * hnm->height) {
327
                av_log(avctx, AV_LOG_ERROR, "Attempting to write out of bounds\n");
328
                break;
329
            }
330
 
331
            if (previous) {
332
                while (count > 0) {
333
                    hnm->current[writeoffset]              = hnm->previous[offset];
334
                    hnm->current[writeoffset + hnm->width] = hnm->previous[offset + hnm->width];
335
                    writeoffset++;
336
                    offset++;
337
                    count--;
338
                }
339
            } else {
340
                while (count > 0) {
341
                    hnm->current[writeoffset]              = hnm->current[offset];
342
                    hnm->current[writeoffset + hnm->width] = hnm->current[offset + hnm->width];
343
                    writeoffset++;
344
                    offset++;
345
                    count--;
346
                }
347
            }
348
        }
349
    }
350
}
351
 
352
static void hnm_update_palette(AVCodecContext *avctx, uint8_t *src,
353
                               uint32_t size)
354
{
355
    Hnm4VideoContext *hnm = avctx->priv_data;
356
    GetByteContext gb;
357
    uint8_t start, writeoffset;
358
    uint16_t count;
359
    int eight_bit_colors;
360
 
361
    eight_bit_colors = src[7] & 0x80 && hnm->version == 0x4a;
362
 
363
    // skip first 8 bytes
364
    bytestream2_init(&gb, src + 8, size - 8);
365
 
366
    while (bytestream2_tell(&gb) < size - 8) {
367
        start = bytestream2_get_byte(&gb);
368
        count = bytestream2_get_byte(&gb);
369
        if (start == 255 && count == 255)
370
            break;
371
        if (count == 0)
372
            count = 256;
373
        writeoffset = start;
374
        while (count > 0) {
375
            hnm->palette[writeoffset] = bytestream2_get_be24(&gb);
376
            if (!eight_bit_colors)
377
                hnm->palette[writeoffset] <<= 2;
378
            count--;
379
            writeoffset++;
380
        }
381
    }
382
}
383
 
384
static void hnm_flip_buffers(Hnm4VideoContext *hnm)
385
{
386
    uint8_t *temp;
387
 
388
    temp          = hnm->current;
389
    hnm->current  = hnm->previous;
390
    hnm->previous = temp;
391
}
392
 
393
static int hnm_decode_frame(AVCodecContext *avctx, void *data,
394
                            int *got_frame, AVPacket *avpkt)
395
{
396
    AVFrame *frame = data;
397
    Hnm4VideoContext *hnm = avctx->priv_data;
398
    int ret;
399
    uint16_t chunk_id;
400
 
401
    if (avpkt->size < 8) {
402
        av_log(avctx, AV_LOG_ERROR, "packet too small\n");
403
        return AVERROR_INVALIDDATA;
404
    }
405
 
406
    chunk_id = AV_RL16(avpkt->data + 4);
407
 
408
    if (chunk_id == HNM4_CHUNK_ID_PL) {
409
        hnm_update_palette(avctx, avpkt->data, avpkt->size);
410
    } else if (chunk_id == HNM4_CHUNK_ID_IZ) {
411
        if (avpkt->size < 12) {
412
            av_log(avctx, AV_LOG_ERROR, "packet too small\n");
413
            return AVERROR_INVALIDDATA;
414
        }
415
        if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
416
            return ret;
417
 
418
        unpack_intraframe(avctx, avpkt->data + 12, avpkt->size - 12);
419
        memcpy(hnm->previous, hnm->current, hnm->width * hnm->height);
420
        if (hnm->version == 0x4a)
421
            memcpy(hnm->processed, hnm->current, hnm->width * hnm->height);
422
        else
423
            postprocess_current_frame(avctx);
424
        copy_processed_frame(avctx, frame);
425
        frame->pict_type = AV_PICTURE_TYPE_I;
426
        frame->key_frame = 1;
427
        memcpy(frame->data[1], hnm->palette, 256 * 4);
428
        *got_frame = 1;
429
    } else if (chunk_id == HNM4_CHUNK_ID_IU) {
430
        if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
431
            return ret;
432
 
433
        if (hnm->version == 0x4a) {
434
            decode_interframe_v4a(avctx, avpkt->data + 8, avpkt->size - 8);
435
            memcpy(hnm->processed, hnm->current, hnm->width * hnm->height);
436
        } else {
437
            decode_interframe_v4(avctx, avpkt->data + 8, avpkt->size - 8);
438
            postprocess_current_frame(avctx);
439
        }
440
        copy_processed_frame(avctx, frame);
441
        frame->pict_type = AV_PICTURE_TYPE_P;
442
        frame->key_frame = 0;
443
        memcpy(frame->data[1], hnm->palette, 256 * 4);
444
        *got_frame = 1;
445
        hnm_flip_buffers(hnm);
446
    } else {
447
        av_log(avctx, AV_LOG_ERROR, "invalid chunk id: %d\n", chunk_id);
448
        return AVERROR_INVALIDDATA;
449
    }
450
 
451
    return avpkt->size;
452
}
453
 
454
static av_cold int hnm_decode_init(AVCodecContext *avctx)
455
{
456
    Hnm4VideoContext *hnm = avctx->priv_data;
457
    int ret;
458
 
459
    if (avctx->extradata_size < 1) {
460
        av_log(avctx, AV_LOG_ERROR,
461
               "Extradata missing, decoder requires version number\n");
462
        return AVERROR_INVALIDDATA;
463
    }
464
 
465
    ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
466
    if (ret < 0)
467
        return ret;
468
 
469
    hnm->version   = avctx->extradata[0];
470
    avctx->pix_fmt = AV_PIX_FMT_PAL8;
471
    hnm->width     = avctx->width;
472
    hnm->height    = avctx->height;
473
    hnm->buffer1   = av_mallocz(avctx->width * avctx->height);
474
    hnm->buffer2   = av_mallocz(avctx->width * avctx->height);
475
    hnm->processed = av_mallocz(avctx->width * avctx->height);
476
 
477
    if (   !hnm->buffer1 || !hnm->buffer2 || !hnm->processed
478
        || avctx->width * avctx->height == 0
479
        || avctx->height % 2) {
480
        av_log(avctx, AV_LOG_ERROR, "av_mallocz() failed\n");
481
        av_freep(&hnm->buffer1);
482
        av_freep(&hnm->buffer2);
483
        av_freep(&hnm->processed);
484
        return AVERROR(ENOMEM);
485
    }
486
 
487
    hnm->current  = hnm->buffer1;
488
    hnm->previous = hnm->buffer2;
489
 
490
    return 0;
491
}
492
 
493
static av_cold int hnm_decode_end(AVCodecContext *avctx)
494
{
495
    Hnm4VideoContext *hnm = avctx->priv_data;
496
 
497
    av_freep(&hnm->buffer1);
498
    av_freep(&hnm->buffer2);
499
    av_freep(&hnm->processed);
500
 
501
    return 0;
502
}
503
 
504
AVCodec ff_hnm4_video_decoder = {
505
    .name           = "hnm4video",
506
    .long_name      = NULL_IF_CONFIG_SMALL("HNM 4 video"),
507
    .type           = AVMEDIA_TYPE_VIDEO,
508
    .id             = AV_CODEC_ID_HNM4_VIDEO,
509
    .priv_data_size = sizeof(Hnm4VideoContext),
510
    .init           = hnm_decode_init,
511
    .close          = hnm_decode_end,
512
    .decode         = hnm_decode_frame,
513
    .capabilities   = AV_CODEC_CAP_DR1,
514
};