Coverage Report

Created: 2024-09-06 07:53

/src/ffmpeg/libavcodec/hapdec.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Vidvox Hap decoder
3
 * Copyright (C) 2015 Vittorio Giovara <vittorio.giovara@gmail.com>
4
 * Copyright (C) 2015 Tom Butterworth <bangnoise@gmail.com>
5
 *
6
 * HapQA and HAPAlphaOnly added by Jokyo Images
7
 *
8
 * This file is part of FFmpeg.
9
 *
10
 * FFmpeg is free software; you can redistribute it and/or
11
 * modify it under the terms of the GNU Lesser General Public
12
 * License as published by the Free Software Foundation; either
13
 * version 2.1 of the License, or (at your option) any later version.
14
 *
15
 * FFmpeg is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18
 * Lesser General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Lesser General Public
21
 * License along with FFmpeg; if not, write to the Free Software
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23
 */
24
25
/**
26
 * @file
27
 * Hap decoder
28
 *
29
 * Fourcc: Hap1, Hap5, HapY, HapA, HapM
30
 *
31
 * https://github.com/Vidvox/hap/blob/master/documentation/HapVideoDRAFT.md
32
 */
33
34
#include <stdint.h>
35
36
#include "libavutil/imgutils.h"
37
#include "libavutil/mem.h"
38
39
#include "avcodec.h"
40
#include "bytestream.h"
41
#include "codec_internal.h"
42
#include "hap.h"
43
#include "snappy.h"
44
#include "texturedsp.h"
45
#include "thread.h"
46
47
static int hap_parse_decode_instructions(HapContext *ctx, int size)
48
3.51k
{
49
3.51k
    GetByteContext *gbc = &ctx->gbc;
50
3.51k
    int section_size;
51
3.51k
    enum HapSectionType section_type;
52
3.51k
    int is_first_table = 1, had_offsets = 0, had_compressors = 0, had_sizes = 0;
53
3.51k
    int i, ret;
54
55
16.0k
    while (size > 0) {
56
14.1k
        int stream_remaining = bytestream2_get_bytes_left(gbc);
57
14.1k
        ret = ff_hap_parse_section_header(gbc, &section_size, &section_type);
58
14.1k
        if (ret != 0)
59
799
            return ret;
60
61
13.3k
        size -= stream_remaining - bytestream2_get_bytes_left(gbc);
62
63
13.3k
        switch (section_type) {
64
2.06k
            case HAP_ST_COMPRESSOR_TABLE:
65
2.06k
                ret = ff_hap_set_chunk_count(ctx, section_size, is_first_table);
66
2.06k
                if (ret != 0)
67
260
                    return ret;
68
916k
                for (i = 0; i < section_size; i++) {
69
914k
                    ctx->chunks[i].compressor = bytestream2_get_byte(gbc) << 4;
70
914k
                }
71
1.80k
                had_compressors = 1;
72
1.80k
                is_first_table = 0;
73
1.80k
                break;
74
2.75k
            case HAP_ST_SIZE_TABLE:
75
2.75k
                ret = ff_hap_set_chunk_count(ctx, section_size / 4, is_first_table);
76
2.75k
                if (ret != 0)
77
324
                    return ret;
78
60.9k
                for (i = 0; i < section_size / 4; i++) {
79
58.5k
                    ctx->chunks[i].compressed_size = bytestream2_get_le32(gbc);
80
58.5k
                }
81
2.43k
                had_sizes = 1;
82
2.43k
                is_first_table = 0;
83
2.43k
                break;
84
1.90k
            case HAP_ST_OFFSET_TABLE:
85
1.90k
                ret = ff_hap_set_chunk_count(ctx, section_size / 4, is_first_table);
86
1.90k
                if (ret != 0)
87
203
                    return ret;
88
641k
                for (i = 0; i < section_size / 4; i++) {
89
640k
                    ctx->chunks[i].compressed_offset = bytestream2_get_le32(gbc);
90
640k
                }
91
1.70k
                had_offsets = 1;
92
1.70k
                is_first_table = 0;
93
1.70k
                break;
94
6.57k
            default:
95
6.57k
                break;
96
13.3k
        }
97
12.5k
        size -= section_size;
98
12.5k
    }
99
100
1.92k
    if (!had_sizes || !had_compressors)
101
667
        return AVERROR_INVALIDDATA;
102
103
    /* The offsets table is optional. If not present than calculate offsets by
104
     * summing the sizes of preceding chunks. */
105
1.25k
    if (!had_offsets) {
106
1.04k
        size_t running_size = 0;
107
6.37k
        for (i = 0; i < ctx->chunk_count; i++) {
108
5.56k
            ctx->chunks[i].compressed_offset = running_size;
109
5.56k
            if (ctx->chunks[i].compressed_size > UINT32_MAX - running_size)
110
238
                return AVERROR_INVALIDDATA;
111
5.32k
            running_size += ctx->chunks[i].compressed_size;
112
5.32k
        }
113
1.04k
    }
114
115
1.02k
    return 0;
116
1.25k
}
117
118
static int hap_can_use_tex_in_place(HapContext *ctx)
119
93.9k
{
120
93.9k
    int i;
121
93.9k
    size_t running_offset = 0;
122
95.3k
    for (i = 0; i < ctx->chunk_count; i++) {
123
93.9k
        if (ctx->chunks[i].compressed_offset != running_offset
124
93.9k
            || ctx->chunks[i].compressor != HAP_COMP_NONE)
125
92.5k
            return 0;
126
1.41k
        running_offset += ctx->chunks[i].compressed_size;
127
1.41k
    }
128
1.41k
    return 1;
129
93.9k
}
130
131
static int hap_parse_frame_header(AVCodecContext *avctx)
132
349k
{
133
349k
    HapContext *ctx = avctx->priv_data;
134
349k
    GetByteContext *gbc = &ctx->gbc;
135
349k
    int section_size;
136
349k
    enum HapSectionType section_type;
137
349k
    const char *compressorstr;
138
349k
    int i, ret;
139
140
349k
    ret = ff_hap_parse_section_header(gbc, &ctx->texture_section_size, &section_type);
141
349k
    if (ret != 0)
142
222k
        return ret;
143
144
126k
    if ((avctx->codec_tag == MKTAG('H','a','p','1') && (section_type & 0x0F) != HAP_FMT_RGBDXT1) ||
145
126k
        (avctx->codec_tag == MKTAG('H','a','p','5') && (section_type & 0x0F) != HAP_FMT_RGBADXT5) ||
146
126k
        (avctx->codec_tag == MKTAG('H','a','p','Y') && (section_type & 0x0F) != HAP_FMT_YCOCGDXT5) ||
147
126k
        (avctx->codec_tag == MKTAG('H','a','p','A') && (section_type & 0x0F) != HAP_FMT_RGTC1) ||
148
126k
        ((avctx->codec_tag == MKTAG('H','a','p','M') && (section_type & 0x0F) != HAP_FMT_RGTC1) &&
149
100k
                                                        (section_type & 0x0F) != HAP_FMT_YCOCGDXT5)) {
150
26.2k
        av_log(avctx, AV_LOG_ERROR,
151
26.2k
               "Invalid texture format %#04x.\n", section_type & 0x0F);
152
26.2k
        return AVERROR_INVALIDDATA;
153
26.2k
    }
154
155
100k
    switch (section_type & 0xF0) {
156
1.61k
        case HAP_COMP_NONE:
157
96.2k
        case HAP_COMP_SNAPPY:
158
96.2k
            ret = ff_hap_set_chunk_count(ctx, 1, 1);
159
96.2k
            if (ret == 0) {
160
96.2k
                ctx->chunks[0].compressor = section_type & 0xF0;
161
96.2k
                ctx->chunks[0].compressed_offset = 0;
162
96.2k
                ctx->chunks[0].compressed_size = ctx->texture_section_size;
163
96.2k
            }
164
96.2k
            if (ctx->chunks[0].compressor == HAP_COMP_NONE) {
165
1.61k
                compressorstr = "none";
166
94.6k
            } else {
167
94.6k
                compressorstr = "snappy";
168
94.6k
            }
169
96.2k
            break;
170
4.01k
        case HAP_COMP_COMPLEX:
171
4.01k
            ret = ff_hap_parse_section_header(gbc, &section_size, &section_type);
172
4.01k
            if (ret == 0 && section_type != HAP_ST_DECODE_INSTRUCTIONS)
173
219
                ret = AVERROR_INVALIDDATA;
174
4.01k
            if (ret == 0)
175
3.51k
                ret = hap_parse_decode_instructions(ctx, section_size);
176
4.01k
            compressorstr = "complex";
177
4.01k
            break;
178
462
        default:
179
462
            ret = AVERROR_INVALIDDATA;
180
462
            break;
181
100k
    }
182
183
100k
    if (ret != 0)
184
3.46k
        return ret;
185
186
    /* Check the frame is valid and read the uncompressed chunk sizes */
187
97.2k
    ctx->tex_size = 0;
188
193k
    for (i = 0; i < ctx->chunk_count; i++) {
189
97.2k
        HapChunk *chunk = &ctx->chunks[i];
190
191
        /* Check the compressed buffer is valid */
192
97.2k
        if (chunk->compressed_offset + (uint64_t)chunk->compressed_size > bytestream2_get_bytes_left(gbc))
193
343
            return AVERROR_INVALIDDATA;
194
195
        /* Chunks are unpacked sequentially, ctx->tex_size is the uncompressed
196
         * size thus far */
197
96.9k
        chunk->uncompressed_offset = ctx->tex_size;
198
199
        /* Fill out uncompressed size */
200
96.9k
        if (chunk->compressor == HAP_COMP_SNAPPY) {
201
95.0k
            GetByteContext gbc_tmp;
202
95.0k
            int64_t uncompressed_size;
203
95.0k
            bytestream2_init(&gbc_tmp, gbc->buffer + chunk->compressed_offset,
204
95.0k
                             chunk->compressed_size);
205
95.0k
            uncompressed_size = ff_snappy_peek_uncompressed_length(&gbc_tmp);
206
95.0k
            if (uncompressed_size < 0) {
207
467
                return uncompressed_size;
208
467
            }
209
94.5k
            chunk->uncompressed_size = uncompressed_size;
210
94.5k
        } else if (chunk->compressor == HAP_COMP_NONE) {
211
1.61k
            chunk->uncompressed_size = chunk->compressed_size;
212
1.61k
        } else {
213
255
            return AVERROR_INVALIDDATA;
214
255
        }
215
96.2k
        ctx->tex_size += chunk->uncompressed_size;
216
96.2k
    }
217
218
96.2k
    av_log(avctx, AV_LOG_DEBUG, "%s compressor\n", compressorstr);
219
220
96.2k
    return ret;
221
97.2k
}
222
223
static int decompress_chunks_thread(AVCodecContext *avctx, void *arg,
224
                                    int chunk_nb, int thread_nb)
225
92.5k
{
226
92.5k
    HapContext *ctx = avctx->priv_data;
227
228
92.5k
    HapChunk *chunk = &ctx->chunks[chunk_nb];
229
92.5k
    GetByteContext gbc;
230
92.5k
    uint8_t *dst = ctx->tex_buf + chunk->uncompressed_offset;
231
232
92.5k
    bytestream2_init(&gbc, ctx->gbc.buffer + chunk->compressed_offset, chunk->compressed_size);
233
234
92.5k
    if (chunk->compressor == HAP_COMP_SNAPPY) {
235
92.5k
        int ret;
236
92.5k
        int64_t uncompressed_size = ctx->tex_size;
237
238
        /* Uncompress the frame */
239
92.5k
        ret = ff_snappy_uncompress(&gbc, dst, &uncompressed_size);
240
92.5k
        if (ret < 0) {
241
2.82k
             av_log(avctx, AV_LOG_ERROR, "Snappy uncompress error\n");
242
2.82k
             return ret;
243
2.82k
        }
244
92.5k
    } else if (chunk->compressor == HAP_COMP_NONE) {
245
0
        bytestream2_get_buffer(&gbc, dst, chunk->compressed_size);
246
0
    }
247
248
89.6k
    return 0;
249
92.5k
}
250
251
static int hap_decode(AVCodecContext *avctx, AVFrame *frame,
252
                      int *got_frame, AVPacket *avpkt)
253
361k
{
254
361k
    HapContext *ctx = avctx->priv_data;
255
361k
    int ret, i, t;
256
361k
    int section_size;
257
361k
    enum HapSectionType section_type;
258
361k
    int start_texture_section = 0;
259
260
361k
    bytestream2_init(&ctx->gbc, avpkt->data, avpkt->size);
261
262
    /* check for multi texture header */
263
361k
    if (ctx->texture_count == 2) {
264
14.2k
        ret = ff_hap_parse_section_header(&ctx->gbc, &section_size, &section_type);
265
14.2k
        if (ret != 0)
266
12.1k
            return ret;
267
2.16k
        if ((section_type & 0x0F) != 0x0D) {
268
352
            av_log(avctx, AV_LOG_ERROR, "Invalid section type in 2 textures mode %#04x.\n", section_type);
269
352
            return AVERROR_INVALIDDATA;
270
352
        }
271
1.81k
        start_texture_section = 4;
272
1.81k
    }
273
274
    /* Get the output frame ready to receive data */
275
348k
    ret = ff_thread_get_buffer(avctx, frame, 0);
276
348k
    if (ret < 0)
277
380
        return ret;
278
279
439k
    for (t = 0; t < ctx->texture_count; t++) {
280
349k
        bytestream2_seek(&ctx->gbc, start_texture_section, SEEK_SET);
281
282
        /* Check for section header */
283
349k
        ret = hap_parse_frame_header(avctx);
284
349k
        if (ret < 0)
285
253k
            return ret;
286
287
96.2k
        if (ctx->tex_size != (avctx->coded_width  / TEXTURE_BLOCK_W)
288
96.2k
            *(avctx->coded_height / TEXTURE_BLOCK_H)
289
96.2k
            *ctx->dec[t].tex_ratio) {
290
2.27k
            av_log(avctx, AV_LOG_ERROR, "uncompressed size mismatches\n");
291
2.27k
            return AVERROR_INVALIDDATA;
292
2.27k
        }
293
294
93.9k
        start_texture_section += ctx->texture_section_size + 4;
295
296
        /* Unpack the DXT texture */
297
93.9k
        if (hap_can_use_tex_in_place(ctx)) {
298
1.41k
            int tex_size;
299
            /* Only DXTC texture compression in a contiguous block */
300
1.41k
            ctx->dec[t].tex_data.in = ctx->gbc.buffer;
301
1.41k
            tex_size = FFMIN(ctx->texture_section_size, bytestream2_get_bytes_left(&ctx->gbc));
302
1.41k
            if (tex_size < (avctx->coded_width  / TEXTURE_BLOCK_W)
303
1.41k
                *(avctx->coded_height / TEXTURE_BLOCK_H)
304
1.41k
                *ctx->dec[t].tex_ratio) {
305
0
                av_log(avctx, AV_LOG_ERROR, "Insufficient data\n");
306
0
                return AVERROR_INVALIDDATA;
307
0
            }
308
92.5k
        } else {
309
            /* Perform the second-stage decompression */
310
92.5k
            ret = av_reallocp(&ctx->tex_buf, ctx->tex_size);
311
92.5k
            if (ret < 0)
312
0
                return ret;
313
314
92.5k
            avctx->execute2(avctx, decompress_chunks_thread, NULL,
315
92.5k
                            ctx->chunk_results, ctx->chunk_count);
316
317
182k
            for (i = 0; i < ctx->chunk_count; i++) {
318
92.5k
                if (ctx->chunk_results[i] < 0)
319
2.82k
                    return ctx->chunk_results[i];
320
92.5k
            }
321
322
89.6k
            ctx->dec[t].tex_data.in = ctx->tex_buf;
323
89.6k
        }
324
325
91.1k
        ctx->dec[t].frame_data.out = frame->data[0];
326
91.1k
        ctx->dec[t].stride = frame->linesize[0];
327
91.1k
        ctx->dec[t].width  = avctx->coded_width;
328
91.1k
        ctx->dec[t].height = avctx->coded_height;
329
91.1k
        ff_texturedsp_exec_decompress_threads(avctx, &ctx->dec[t]);
330
91.1k
    }
331
332
    /* Frame is ready to be output */
333
89.9k
    *got_frame = 1;
334
335
89.9k
    return avpkt->size;
336
348k
}
337
338
static av_cold int hap_init(AVCodecContext *avctx)
339
950
{
340
950
    HapContext *ctx = avctx->priv_data;
341
950
    TextureDSPContext dxtc;
342
950
    const char *texture_name;
343
950
    int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
344
345
950
    if (ret < 0) {
346
147
        av_log(avctx, AV_LOG_ERROR, "Invalid video size %dx%d.\n",
347
147
               avctx->width, avctx->height);
348
147
        return ret;
349
147
    }
350
351
    /* Since codec is based on 4x4 blocks, size is aligned to 4 */
352
803
    avctx->coded_width  = FFALIGN(avctx->width,  TEXTURE_BLOCK_W);
353
803
    avctx->coded_height = FFALIGN(avctx->height, TEXTURE_BLOCK_H);
354
355
803
    ff_texturedsp_init(&dxtc);
356
357
803
    ctx->texture_count  = 1;
358
803
    ctx->dec[0].raw_ratio = 16;
359
803
    ctx->dec[0].slice_count = av_clip(avctx->thread_count, 1,
360
803
                                      avctx->coded_height / TEXTURE_BLOCK_H);
361
362
803
    switch (avctx->codec_tag) {
363
110
    case MKTAG('H','a','p','1'):
364
110
        texture_name = "DXT1";
365
110
        ctx->dec[0].tex_ratio = 8;
366
110
        ctx->dec[0].tex_funct = dxtc.dxt1_block;
367
110
        avctx->pix_fmt = AV_PIX_FMT_RGB0;
368
110
        break;
369
78
    case MKTAG('H','a','p','5'):
370
78
        texture_name = "DXT5";
371
78
        ctx->dec[0].tex_ratio = 16;
372
78
        ctx->dec[0].tex_funct = dxtc.dxt5_block;
373
78
        avctx->pix_fmt = AV_PIX_FMT_RGBA;
374
78
        break;
375
480
    case MKTAG('H','a','p','Y'):
376
480
        texture_name = "DXT5-YCoCg-scaled";
377
480
        ctx->dec[0].tex_ratio = 16;
378
480
        ctx->dec[0].tex_funct = dxtc.dxt5ys_block;
379
480
        avctx->pix_fmt = AV_PIX_FMT_RGB0;
380
480
        break;
381
66
    case MKTAG('H','a','p','A'):
382
66
        texture_name = "RGTC1";
383
66
        ctx->dec[0].tex_ratio = 8;
384
66
        ctx->dec[0].tex_funct = dxtc.rgtc1u_gray_block;
385
66
        ctx->dec[0].raw_ratio = 4;
386
66
        avctx->pix_fmt = AV_PIX_FMT_GRAY8;
387
66
        break;
388
69
    case MKTAG('H','a','p','M'):
389
69
        texture_name  = "DXT5-YCoCg-scaled / RGTC1";
390
69
        ctx->dec[0].tex_ratio = 16;
391
69
        ctx->dec[1].tex_ratio = 8;
392
69
        ctx->dec[0].tex_funct = dxtc.dxt5ys_block;
393
69
        ctx->dec[1].tex_funct = dxtc.rgtc1u_alpha_block;
394
69
        ctx->dec[1].raw_ratio = 16;
395
69
        ctx->dec[1].slice_count = ctx->dec[0].slice_count;
396
69
        avctx->pix_fmt = AV_PIX_FMT_RGBA;
397
69
        ctx->texture_count = 2;
398
69
        break;
399
0
    default:
400
0
        return AVERROR_DECODER_NOT_FOUND;
401
803
    }
402
403
803
    av_log(avctx, AV_LOG_DEBUG, "%s texture\n", texture_name);
404
405
803
    return 0;
406
803
}
407
408
static av_cold int hap_close(AVCodecContext *avctx)
409
950
{
410
950
    HapContext *ctx = avctx->priv_data;
411
412
950
    ff_hap_free_context(ctx);
413
414
950
    return 0;
415
950
}
416
417
const FFCodec ff_hap_decoder = {
418
    .p.name         = "hap",
419
    CODEC_LONG_NAME("Vidvox Hap"),
420
    .p.type         = AVMEDIA_TYPE_VIDEO,
421
    .p.id           = AV_CODEC_ID_HAP,
422
    .init           = hap_init,
423
    FF_CODEC_DECODE_CB(hap_decode),
424
    .close          = hap_close,
425
    .priv_data_size = sizeof(HapContext),
426
    .p.capabilities = AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_SLICE_THREADS |
427
                      AV_CODEC_CAP_DR1,
428
    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,
429
    .codec_tags     = (const uint32_t []){
430
        MKTAG('H','a','p','1'),
431
        MKTAG('H','a','p','5'),
432
        MKTAG('H','a','p','Y'),
433
        MKTAG('H','a','p','A'),
434
        MKTAG('H','a','p','M'),
435
        FF_CODEC_TAGS_END,
436
    },
437
};