GCC Code Coverage Report
Directory: ../../../ffmpeg/ Exec Total Coverage
File: src/libavcodec/hnm4video.c Lines: 0 302 0.0 %
Date: 2019-11-18 18:00:01 Branches: 0 162 0.0 %

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