Subversion Repositories Kolibri OS

Rev

Rev 6147 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
6147 serge 1
/*
2
 * Vidvox Hap decoder
3
 * Copyright (C) 2015 Vittorio Giovara 
4
 * Copyright (C) 2015 Tom Butterworth 
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
/**
24
 * @file
25
 * Hap decoder
26
 *
27
 * Fourcc: Hap1, Hap5, HapY
28
 *
29
 * https://github.com/Vidvox/hap/blob/master/documentation/HapVideoDRAFT.md
30
 */
31
 
32
#include 
33
 
34
#include "libavutil/imgutils.h"
35
 
36
#include "avcodec.h"
37
#include "bytestream.h"
38
#include "hap.h"
39
#include "internal.h"
6303 serge 40
//#include "memory.h"
6147 serge 41
#include "snappy.h"
42
#include "texturedsp.h"
43
#include "thread.h"
44
 
45
/* The first three bytes are the size of the section past the header, or zero
46
 * if the length is stored in the next long word. The fourth byte in the first
47
 * long word indicates the type of the current section. */
48
static int parse_section_header(GetByteContext *gbc, int *section_size,
49
                                enum HapSectionType *section_type)
50
{
51
    if (bytestream2_get_bytes_left(gbc) < 4)
52
        return AVERROR_INVALIDDATA;
53
 
54
    *section_size = bytestream2_get_le24(gbc);
55
    *section_type = bytestream2_get_byte(gbc);
56
 
57
    if (*section_size == 0) {
58
        if (bytestream2_get_bytes_left(gbc) < 4)
59
            return AVERROR_INVALIDDATA;
60
 
61
        *section_size = bytestream2_get_le32(gbc);
62
    }
63
 
64
    if (*section_size > bytestream2_get_bytes_left(gbc) || *section_size < 0)
65
        return AVERROR_INVALIDDATA;
66
    else
67
        return 0;
68
}
69
 
70
static int hap_parse_decode_instructions(HapContext *ctx, int size)
71
{
72
    GetByteContext *gbc = &ctx->gbc;
73
    int section_size;
74
    enum HapSectionType section_type;
75
    int is_first_table = 1, had_offsets = 0, had_compressors = 0, had_sizes = 0;
76
    int i, ret;
77
 
78
    while (size > 0) {
79
        int stream_remaining = bytestream2_get_bytes_left(gbc);
80
        ret = parse_section_header(gbc, §ion_size, §ion_type);
81
        if (ret != 0)
82
            return ret;
83
 
84
        size -= stream_remaining - bytestream2_get_bytes_left(gbc);
85
 
86
        switch (section_type) {
87
            case HAP_ST_COMPRESSOR_TABLE:
88
                ret = ff_hap_set_chunk_count(ctx, section_size, is_first_table);
89
                if (ret != 0)
90
                    return ret;
91
                for (i = 0; i < section_size; i++) {
92
                    ctx->chunks[i].compressor = bytestream2_get_byte(gbc) << 4;
93
                }
94
                had_compressors = 1;
95
                is_first_table = 0;
96
                break;
97
            case HAP_ST_SIZE_TABLE:
98
                ret = ff_hap_set_chunk_count(ctx, section_size / 4, is_first_table);
99
                if (ret != 0)
100
                    return ret;
101
                for (i = 0; i < section_size / 4; i++) {
102
                    ctx->chunks[i].compressed_size = bytestream2_get_le32(gbc);
103
                }
104
                had_sizes = 1;
105
                is_first_table = 0;
106
                break;
107
            case HAP_ST_OFFSET_TABLE:
108
                ret = ff_hap_set_chunk_count(ctx, section_size / 4, is_first_table);
109
                if (ret != 0)
110
                    return ret;
111
                for (i = 0; i < section_size / 4; i++) {
112
                    ctx->chunks[i].compressed_offset = bytestream2_get_le32(gbc);
113
                }
114
                had_offsets = 1;
115
                is_first_table = 0;
116
                break;
117
            default:
118
                break;
119
        }
120
        size -= section_size;
121
    }
122
 
123
    if (!had_sizes || !had_compressors)
124
        return AVERROR_INVALIDDATA;
125
 
126
    /* The offsets table is optional. If not present than calculate offsets by
127
     * summing the sizes of preceding chunks. */
128
    if (!had_offsets) {
129
        size_t running_size = 0;
130
        for (i = 0; i < ctx->chunk_count; i++) {
131
            ctx->chunks[i].compressed_offset = running_size;
132
            running_size += ctx->chunks[i].compressed_size;
133
        }
134
    }
135
 
136
    return 0;
137
}
138
 
139
static int hap_can_use_tex_in_place(HapContext *ctx)
140
{
141
    int i;
142
    size_t running_offset = 0;
143
    for (i = 0; i < ctx->chunk_count; i++) {
144
        if (ctx->chunks[i].compressed_offset != running_offset
145
            || ctx->chunks[i].compressor != HAP_COMP_NONE)
146
            return 0;
147
        running_offset += ctx->chunks[i].compressed_size;
148
    }
149
    return 1;
150
}
151
 
152
static int hap_parse_frame_header(AVCodecContext *avctx)
153
{
154
    HapContext *ctx = avctx->priv_data;
155
    GetByteContext *gbc = &ctx->gbc;
156
    int section_size;
157
    enum HapSectionType section_type;
158
    const char *compressorstr;
159
    int i, ret;
160
 
161
    ret = parse_section_header(gbc, §ion_size, §ion_type);
162
    if (ret != 0)
163
        return ret;
164
 
165
    if ((avctx->codec_tag == MKTAG('H','a','p','1') && (section_type & 0x0F) != HAP_FMT_RGBDXT1) ||
166
        (avctx->codec_tag == MKTAG('H','a','p','5') && (section_type & 0x0F) != HAP_FMT_RGBADXT5) ||
167
        (avctx->codec_tag == MKTAG('H','a','p','Y') && (section_type & 0x0F) != HAP_FMT_YCOCGDXT5)) {
168
        av_log(avctx, AV_LOG_ERROR,
169
               "Invalid texture format %#04x.\n", section_type & 0x0F);
170
        return AVERROR_INVALIDDATA;
171
    }
172
 
173
    switch (section_type & 0xF0) {
174
        case HAP_COMP_NONE:
175
        case HAP_COMP_SNAPPY:
176
            ret = ff_hap_set_chunk_count(ctx, 1, 1);
177
            if (ret == 0) {
178
                ctx->chunks[0].compressor = section_type & 0xF0;
179
                ctx->chunks[0].compressed_offset = 0;
180
                ctx->chunks[0].compressed_size = section_size;
181
            }
182
            if (ctx->chunks[0].compressor == HAP_COMP_NONE) {
183
                compressorstr = "none";
184
            } else {
185
                compressorstr = "snappy";
186
            }
187
            break;
188
        case HAP_COMP_COMPLEX:
189
            ret = parse_section_header(gbc, §ion_size, §ion_type);
190
            if (ret == 0 && section_type != HAP_ST_DECODE_INSTRUCTIONS)
191
                ret = AVERROR_INVALIDDATA;
192
            if (ret == 0)
193
                ret = hap_parse_decode_instructions(ctx, section_size);
194
            compressorstr = "complex";
195
            break;
196
        default:
197
            ret = AVERROR_INVALIDDATA;
198
            break;
199
    }
200
 
201
    if (ret != 0)
202
        return ret;
203
 
204
    /* Check the frame is valid and read the uncompressed chunk sizes */
205
    ctx->tex_size = 0;
206
    for (i = 0; i < ctx->chunk_count; i++) {
207
        HapChunk *chunk = &ctx->chunks[i];
208
 
209
        /* Check the compressed buffer is valid */
210
        if (chunk->compressed_offset + chunk->compressed_size > bytestream2_get_bytes_left(gbc))
211
            return AVERROR_INVALIDDATA;
212
 
213
        /* Chunks are unpacked sequentially, ctx->tex_size is the uncompressed
214
         * size thus far */
215
        chunk->uncompressed_offset = ctx->tex_size;
216
 
217
        /* Fill out uncompressed size */
218
        if (chunk->compressor == HAP_COMP_SNAPPY) {
219
            GetByteContext gbc_tmp;
220
            int64_t uncompressed_size;
221
            bytestream2_init(&gbc_tmp, gbc->buffer + chunk->compressed_offset,
222
                             chunk->compressed_size);
223
            uncompressed_size = ff_snappy_peek_uncompressed_length(&gbc_tmp);
224
            if (uncompressed_size < 0) {
225
                return uncompressed_size;
226
            }
227
            chunk->uncompressed_size = uncompressed_size;
228
        } else if (chunk->compressor == HAP_COMP_NONE) {
229
            chunk->uncompressed_size = chunk->compressed_size;
230
        } else {
231
            return AVERROR_INVALIDDATA;
232
        }
233
        ctx->tex_size += chunk->uncompressed_size;
234
    }
235
 
236
    av_log(avctx, AV_LOG_DEBUG, "%s compressor\n", compressorstr);
237
 
238
    return ret;
239
}
240
 
241
static int decompress_chunks_thread(AVCodecContext *avctx, void *arg,
242
                                    int chunk_nb, int thread_nb)
243
{
244
    HapContext *ctx = avctx->priv_data;
245
 
246
    HapChunk *chunk = &ctx->chunks[chunk_nb];
247
    GetByteContext gbc;
248
    uint8_t *dst = ctx->tex_buf + chunk->uncompressed_offset;
249
 
250
    bytestream2_init(&gbc, ctx->gbc.buffer + chunk->compressed_offset, chunk->compressed_size);
251
 
252
    if (chunk->compressor == HAP_COMP_SNAPPY) {
253
        int ret;
254
        int64_t uncompressed_size = ctx->tex_size;
255
 
256
        /* Uncompress the frame */
257
        ret = ff_snappy_uncompress(&gbc, dst, &uncompressed_size);
258
        if (ret < 0) {
259
             av_log(avctx, AV_LOG_ERROR, "Snappy uncompress error\n");
260
             return ret;
261
        }
262
    } else if (chunk->compressor == HAP_COMP_NONE) {
263
        bytestream2_get_buffer(&gbc, dst, chunk->compressed_size);
264
    }
265
 
266
    return 0;
267
}
268
 
269
static int decompress_texture_thread(AVCodecContext *avctx, void *arg,
270
                                     int slice, int thread_nb)
271
{
272
    HapContext *ctx = avctx->priv_data;
273
    AVFrame *frame = arg;
274
    const uint8_t *d = ctx->tex_data;
275
    int w_block = avctx->coded_width / TEXTURE_BLOCK_W;
276
    int h_block = avctx->coded_height / TEXTURE_BLOCK_H;
277
    int x, y;
278
    int start_slice, end_slice;
279
    int base_blocks_per_slice = h_block / ctx->slice_count;
280
    int remainder_blocks = h_block % ctx->slice_count;
281
 
282
    /* When the frame height (in blocks) doesn't divide evenly between the
283
     * number of slices, spread the remaining blocks evenly between the first
284
     * operations */
285
    start_slice = slice * base_blocks_per_slice;
286
    /* Add any extra blocks (one per slice) that have been added before this slice */
287
    start_slice += FFMIN(slice, remainder_blocks);
288
 
289
    end_slice = start_slice + base_blocks_per_slice;
290
    /* Add an extra block if there are still remainder blocks to be accounted for */
291
    if (slice < remainder_blocks)
292
        end_slice++;
293
 
294
    for (y = start_slice; y < end_slice; y++) {
295
        uint8_t *p = frame->data[0] + y * frame->linesize[0] * TEXTURE_BLOCK_H;
296
        int off  = y * w_block;
297
        for (x = 0; x < w_block; x++) {
298
            ctx->tex_fun(p + x * 16, frame->linesize[0],
299
                         d + (off + x) * ctx->tex_rat);
300
        }
301
    }
302
 
303
    return 0;
304
}
305
 
306
static int hap_decode(AVCodecContext *avctx, void *data,
307
                      int *got_frame, AVPacket *avpkt)
308
{
309
    HapContext *ctx = avctx->priv_data;
310
    ThreadFrame tframe;
311
    int ret, i;
312
    int tex_size;
313
 
314
    bytestream2_init(&ctx->gbc, avpkt->data, avpkt->size);
315
 
316
    /* Check for section header */
317
    ret = hap_parse_frame_header(avctx);
318
    if (ret < 0)
319
        return ret;
320
 
321
    /* Get the output frame ready to receive data */
322
    tframe.f = data;
323
    ret = ff_thread_get_buffer(avctx, &tframe, 0);
324
    if (ret < 0)
325
        return ret;
326
    if (avctx->codec->update_thread_context)
327
        ff_thread_finish_setup(avctx);
328
 
329
    /* Unpack the DXT texture */
330
    if (hap_can_use_tex_in_place(ctx)) {
331
        /* Only DXTC texture compression in a contiguous block */
332
        ctx->tex_data = ctx->gbc.buffer;
333
        tex_size = bytestream2_get_bytes_left(&ctx->gbc);
334
    } else {
335
        /* Perform the second-stage decompression */
336
        ret = av_reallocp(&ctx->tex_buf, ctx->tex_size);
337
        if (ret < 0)
338
            return ret;
339
 
340
        avctx->execute2(avctx, decompress_chunks_thread, NULL,
341
                        ctx->chunk_results, ctx->chunk_count);
342
 
343
        for (i = 0; i < ctx->chunk_count; i++) {
344
            if (ctx->chunk_results[i] < 0)
345
                return ctx->chunk_results[i];
346
        }
347
 
348
        ctx->tex_data = ctx->tex_buf;
349
        tex_size = ctx->tex_size;
350
    }
351
 
352
    if (tex_size < (avctx->coded_width  / TEXTURE_BLOCK_W)
353
                  *(avctx->coded_height / TEXTURE_BLOCK_H)
354
                  *ctx->tex_rat) {
355
        av_log(avctx, AV_LOG_ERROR, "Insufficient data\n");
356
        return AVERROR_INVALIDDATA;
357
    }
358
 
359
    /* Use the decompress function on the texture, one block per thread */
360
    avctx->execute2(avctx, decompress_texture_thread, tframe.f, NULL, ctx->slice_count);
361
 
362
    /* Frame is ready to be output */
363
    tframe.f->pict_type = AV_PICTURE_TYPE_I;
364
    tframe.f->key_frame = 1;
365
    *got_frame = 1;
366
 
367
    return avpkt->size;
368
}
369
 
370
static av_cold int hap_init(AVCodecContext *avctx)
371
{
372
    HapContext *ctx = avctx->priv_data;
373
    const char *texture_name;
374
    int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
375
 
376
    if (ret < 0) {
377
        av_log(avctx, AV_LOG_ERROR, "Invalid video size %dx%d.\n",
378
               avctx->width, avctx->height);
379
        return ret;
380
    }
381
 
382
    /* Since codec is based on 4x4 blocks, size is aligned to 4 */
383
    avctx->coded_width  = FFALIGN(avctx->width,  TEXTURE_BLOCK_W);
384
    avctx->coded_height = FFALIGN(avctx->height, TEXTURE_BLOCK_H);
385
 
386
    /* Technically only one mode has alpha, but 32 bits are easier to handle */
387
    avctx->pix_fmt = AV_PIX_FMT_RGBA;
388
 
389
    ff_texturedsp_init(&ctx->dxtc);
390
 
391
    switch (avctx->codec_tag) {
392
    case MKTAG('H','a','p','1'):
393
        texture_name = "DXT1";
394
        ctx->tex_rat = 8;
395
        ctx->tex_fun = ctx->dxtc.dxt1_block;
396
        break;
397
    case MKTAG('H','a','p','5'):
398
        texture_name = "DXT5";
399
        ctx->tex_rat = 16;
400
        ctx->tex_fun = ctx->dxtc.dxt5_block;
401
        break;
402
    case MKTAG('H','a','p','Y'):
403
        texture_name = "DXT5-YCoCg-scaled";
404
        ctx->tex_rat = 16;
405
        ctx->tex_fun = ctx->dxtc.dxt5ys_block;
406
        break;
407
    default:
408
        return AVERROR_DECODER_NOT_FOUND;
409
    }
410
 
411
    av_log(avctx, AV_LOG_DEBUG, "%s texture\n", texture_name);
412
 
413
    ctx->slice_count = av_clip(avctx->thread_count, 1,
414
                               avctx->coded_height / TEXTURE_BLOCK_H);
415
 
416
    return 0;
417
}
418
 
419
static av_cold int hap_close(AVCodecContext *avctx)
420
{
421
    HapContext *ctx = avctx->priv_data;
422
 
423
    ff_hap_free_context(ctx);
424
 
425
    return 0;
426
}
427
 
428
AVCodec ff_hap_decoder = {
429
    .name           = "hap",
430
    .long_name      = NULL_IF_CONFIG_SMALL("Vidvox Hap decoder"),
431
    .type           = AVMEDIA_TYPE_VIDEO,
432
    .id             = AV_CODEC_ID_HAP,
433
    .init           = hap_init,
434
    .decode         = hap_decode,
435
    .close          = hap_close,
436
    .priv_data_size = sizeof(HapContext),
437
    .capabilities   = AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_SLICE_THREADS |
438
                      AV_CODEC_CAP_DR1,
439
    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE |
440
                      FF_CODEC_CAP_INIT_CLEANUP,
441
};