FFmpeg
Loading...
Searching...
No Matches
hapdec.c
Go to the documentation of this file.
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
48{
49 GetByteContext *gbc = &ctx->gbc;
50 int section_size;
51 enum HapSectionType section_type;
52 int is_first_table = 1, had_offsets = 0, had_compressors = 0, had_sizes = 0;
53 int i, ret;
54
55 while (size > 0) {
56 int stream_remaining = bytestream2_get_bytes_left(gbc);
57 ret = ff_hap_parse_section_header(gbc, &section_size, &section_type);
58 if (ret != 0)
59 return ret;
60
61 size -= stream_remaining - bytestream2_get_bytes_left(gbc);
62
63 switch (section_type) {
65 ret = ff_hap_set_chunk_count(ctx, section_size, is_first_table);
66 if (ret != 0)
67 return ret;
68 for (i = 0; i < section_size; i++) {
69 ctx->chunks[i].compressor = bytestream2_get_byte(gbc) << 4;
70 }
71 had_compressors = 1;
72 is_first_table = 0;
73 break;
75 ret = ff_hap_set_chunk_count(ctx, section_size / 4, is_first_table);
76 if (ret != 0)
77 return ret;
78 for (i = 0; i < section_size / 4; i++) {
79 ctx->chunks[i].compressed_size = bytestream2_get_le32(gbc);
80 }
81 had_sizes = 1;
82 is_first_table = 0;
83 break;
85 ret = ff_hap_set_chunk_count(ctx, section_size / 4, is_first_table);
86 if (ret != 0)
87 return ret;
88 for (i = 0; i < section_size / 4; i++) {
89 ctx->chunks[i].compressed_offset = bytestream2_get_le32(gbc);
90 }
91 had_offsets = 1;
92 is_first_table = 0;
93 break;
94 default:
95 break;
96 }
97 size -= section_size;
98 }
99
100 if (!had_sizes || !had_compressors)
101 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 if (!had_offsets) {
106 size_t running_size = 0;
107 for (i = 0; i < ctx->chunk_count; i++) {
108 ctx->chunks[i].compressed_offset = running_size;
109 if (ctx->chunks[i].compressed_size > UINT32_MAX - running_size)
110 return AVERROR_INVALIDDATA;
111 running_size += ctx->chunks[i].compressed_size;
112 }
113 }
114
115 return 0;
116}
117
119{
120 int i;
121 size_t running_offset = 0;
122 for (i = 0; i < ctx->chunk_count; i++) {
123 if (ctx->chunks[i].compressed_offset != running_offset
124 || ctx->chunks[i].compressor != HAP_COMP_NONE)
125 return 0;
126 running_offset += ctx->chunks[i].compressed_size;
127 }
128 return 1;
129}
130
132{
133 HapContext *ctx = avctx->priv_data;
134 GetByteContext *gbc = &ctx->gbc;
135 int section_size;
136 enum HapSectionType section_type;
137 const char *compressorstr;
138 int i, ret;
139
140 ret = ff_hap_parse_section_header(gbc, &ctx->texture_section_size, &section_type);
141 if (ret != 0)
142 return ret;
143
144 if ((avctx->codec_tag == MKTAG('H','a','p','1') && (section_type & 0x0F) != HAP_FMT_RGBDXT1) ||
145 (avctx->codec_tag == MKTAG('H','a','p','5') && (section_type & 0x0F) != HAP_FMT_RGBADXT5) ||
146 (avctx->codec_tag == MKTAG('H','a','p','Y') && (section_type & 0x0F) != HAP_FMT_YCOCGDXT5) ||
147 (avctx->codec_tag == MKTAG('H','a','p','A') && (section_type & 0x0F) != HAP_FMT_RGTC1) ||
148 ((avctx->codec_tag == MKTAG('H','a','p','M') && (section_type & 0x0F) != HAP_FMT_RGTC1) &&
149 (section_type & 0x0F) != HAP_FMT_YCOCGDXT5)) {
150 av_log(avctx, AV_LOG_ERROR,
151 "Invalid texture format %#04x.\n", section_type & 0x0F);
152 return AVERROR_INVALIDDATA;
153 }
154
155 switch (section_type & 0xF0) {
156 case HAP_COMP_NONE:
157 case HAP_COMP_SNAPPY:
158 ret = ff_hap_set_chunk_count(ctx, 1, 1);
159 if (ret == 0) {
160 ctx->chunks[0].compressor = section_type & 0xF0;
161 ctx->chunks[0].compressed_offset = 0;
162 ctx->chunks[0].compressed_size = ctx->texture_section_size;
163 }
164 if (ctx->chunks[0].compressor == HAP_COMP_NONE) {
165 compressorstr = "none";
166 } else {
167 compressorstr = "snappy";
168 }
169 break;
170 case HAP_COMP_COMPLEX:
171 ret = ff_hap_parse_section_header(gbc, &section_size, &section_type);
172 if (ret == 0 && section_type != HAP_ST_DECODE_INSTRUCTIONS)
174 if (ret == 0)
175 ret = hap_parse_decode_instructions(ctx, section_size);
176 compressorstr = "complex";
177 break;
178 default:
180 break;
181 }
182
183 if (ret != 0)
184 return ret;
185
186 /* Check the frame is valid and read the uncompressed chunk sizes */
187 ctx->tex_size = 0;
188 for (i = 0; i < ctx->chunk_count; i++) {
189 HapChunk *chunk = &ctx->chunks[i];
190
191 /* Check the compressed buffer is valid */
192 if (chunk->compressed_offset + (uint64_t)chunk->compressed_size > bytestream2_get_bytes_left(gbc))
193 return AVERROR_INVALIDDATA;
194
195 /* Chunks are unpacked sequentially, ctx->tex_size is the uncompressed
196 * size thus far */
197 chunk->uncompressed_offset = ctx->tex_size;
198
199 /* Fill out uncompressed size */
200 if (chunk->compressor == HAP_COMP_SNAPPY) {
201 GetByteContext gbc_tmp;
202 int64_t uncompressed_size;
203 bytestream2_init(&gbc_tmp, gbc->buffer + chunk->compressed_offset,
204 chunk->compressed_size);
205 uncompressed_size = ff_snappy_peek_uncompressed_length(&gbc_tmp);
206 if (uncompressed_size < 0) {
207 return uncompressed_size;
208 }
209 chunk->uncompressed_size = uncompressed_size;
210 } else if (chunk->compressor == HAP_COMP_NONE) {
211 chunk->uncompressed_size = chunk->compressed_size;
212 } else {
213 return AVERROR_INVALIDDATA;
214 }
215 ctx->tex_size += chunk->uncompressed_size;
216 }
217
218 av_log(avctx, AV_LOG_DEBUG, "%s compressor\n", compressorstr);
219
220 return ret;
221}
222
224 int chunk_nb, int thread_nb)
225{
226 HapContext *ctx = avctx->priv_data;
227
228 HapChunk *chunk = &ctx->chunks[chunk_nb];
229 GetByteContext gbc;
230 uint8_t *dst = ctx->tex_buf + chunk->uncompressed_offset;
231
232 bytestream2_init(&gbc, ctx->gbc.buffer + chunk->compressed_offset, chunk->compressed_size);
233
234 if (chunk->compressor == HAP_COMP_SNAPPY) {
235 int ret;
236 int64_t uncompressed_size = ctx->tex_size;
237
238 /* Uncompress the frame */
239 ret = ff_snappy_uncompress(&gbc, dst, &uncompressed_size);
240 if (ret < 0) {
241 av_log(avctx, AV_LOG_ERROR, "Snappy uncompress error\n");
242 return ret;
243 }
244 } else if (chunk->compressor == HAP_COMP_NONE) {
246 }
247
248 return 0;
249}
250
252 int *got_frame, AVPacket *avpkt)
253{
254 HapContext *ctx = avctx->priv_data;
255 int ret, i, t;
256 int section_size;
257 enum HapSectionType section_type;
258 int start_texture_section = 0;
259
260 bytestream2_init(&ctx->gbc, avpkt->data, avpkt->size);
261
262 /* check for multi texture header */
263 if (ctx->texture_count == 2) {
264 ret = ff_hap_parse_section_header(&ctx->gbc, &section_size, &section_type);
265 if (ret != 0)
266 return ret;
267 if ((section_type & 0x0F) != 0x0D) {
268 av_log(avctx, AV_LOG_ERROR, "Invalid section type in 2 textures mode %#04x.\n", section_type);
269 return AVERROR_INVALIDDATA;
270 }
271 start_texture_section = 4;
272 }
273
274 /* Get the output frame ready to receive data */
275 ret = ff_thread_get_buffer(avctx, frame, 0);
276 if (ret < 0)
277 return ret;
278
279 for (t = 0; t < ctx->texture_count; t++) {
280 bytestream2_seek(&ctx->gbc, start_texture_section, SEEK_SET);
281
282 /* Check for section header */
283 ret = hap_parse_frame_header(avctx);
284 if (ret < 0)
285 return ret;
286
287 if (ctx->tex_size != (avctx->coded_width / TEXTURE_BLOCK_W)
288 *(avctx->coded_height / TEXTURE_BLOCK_H)
289 *ctx->dec[t].tex_ratio) {
290 av_log(avctx, AV_LOG_ERROR, "uncompressed size mismatches\n");
291 return AVERROR_INVALIDDATA;
292 }
293
294 start_texture_section += ctx->texture_section_size + 4;
295
296 /* Unpack the DXT texture */
298 int tex_size;
299 /* Only DXTC texture compression in a contiguous block */
300 ctx->dec[t].tex_data.in = ctx->gbc.buffer;
301 tex_size = FFMIN(ctx->texture_section_size, bytestream2_get_bytes_left(&ctx->gbc));
302 if (tex_size < (avctx->coded_width / TEXTURE_BLOCK_W)
303 *(avctx->coded_height / TEXTURE_BLOCK_H)
304 *ctx->dec[t].tex_ratio) {
305 av_log(avctx, AV_LOG_ERROR, "Insufficient data\n");
306 return AVERROR_INVALIDDATA;
307 }
308 } else {
309 /* Perform the second-stage decompression */
310 ret = av_reallocp(&ctx->tex_buf, ctx->tex_size);
311 if (ret < 0)
312 return ret;
313 memset(ctx->tex_buf, 0, ctx->tex_size);
314
316 ctx->chunk_results, ctx->chunk_count);
317
318 for (i = 0; i < ctx->chunk_count; i++) {
319 if (ctx->chunk_results[i] < 0)
320 return ctx->chunk_results[i];
321 }
322
323 ctx->dec[t].tex_data.in = ctx->tex_buf;
324 }
325
326 ctx->dec[t].frame_data.out = frame->data[0];
327 ctx->dec[t].stride = frame->linesize[0];
328 ctx->dec[t].width = avctx->coded_width;
329 ctx->dec[t].height = avctx->coded_height;
331 }
332
333 /* Frame is ready to be output */
334 *got_frame = 1;
335
336 return avpkt->size;
337}
338
340{
341 HapContext *ctx = avctx->priv_data;
343 const char *texture_name;
344 int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx);
345
346 if (ret < 0) {
347 av_log(avctx, AV_LOG_ERROR, "Invalid video size %dx%d.\n",
348 avctx->width, avctx->height);
349 return ret;
350 }
351
352 /* Since codec is based on 4x4 blocks, size is aligned to 4 */
353 avctx->coded_width = FFALIGN(avctx->width, TEXTURE_BLOCK_W);
354 avctx->coded_height = FFALIGN(avctx->height, TEXTURE_BLOCK_H);
355
356 ff_texturedsp_init(&dxtc);
357
358 ctx->texture_count = 1;
359 ctx->dec[0].raw_ratio = 16;
360 ctx->dec[0].slice_count = av_clip(avctx->thread_count, 1,
362
363 switch (avctx->codec_tag) {
364 case MKTAG('H','a','p','1'):
365 texture_name = "DXT1";
366 ctx->dec[0].tex_ratio = 8;
367 ctx->dec[0].tex_funct = dxtc.dxt1_block;
368 avctx->pix_fmt = AV_PIX_FMT_RGB0;
369 break;
370 case MKTAG('H','a','p','5'):
371 texture_name = "DXT5";
372 ctx->dec[0].tex_ratio = 16;
373 ctx->dec[0].tex_funct = dxtc.dxt5_block;
374 avctx->pix_fmt = AV_PIX_FMT_RGBA;
375 break;
376 case MKTAG('H','a','p','Y'):
377 texture_name = "DXT5-YCoCg-scaled";
378 ctx->dec[0].tex_ratio = 16;
379 ctx->dec[0].tex_funct = dxtc.dxt5ys_block;
380 avctx->pix_fmt = AV_PIX_FMT_RGB0;
381 break;
382 case MKTAG('H','a','p','A'):
383 texture_name = "RGTC1";
384 ctx->dec[0].tex_ratio = 8;
385 ctx->dec[0].tex_funct = dxtc.rgtc1u_gray_block;
386 ctx->dec[0].raw_ratio = 4;
387 avctx->pix_fmt = AV_PIX_FMT_GRAY8;
388 break;
389 case MKTAG('H','a','p','M'):
390 texture_name = "DXT5-YCoCg-scaled / RGTC1";
391 ctx->dec[0].tex_ratio = 16;
392 ctx->dec[1].tex_ratio = 8;
393 ctx->dec[0].tex_funct = dxtc.dxt5ys_block;
394 ctx->dec[1].tex_funct = dxtc.rgtc1u_alpha_block;
395 ctx->dec[1].raw_ratio = 16;
396 ctx->dec[1].slice_count = ctx->dec[0].slice_count;
397 avctx->pix_fmt = AV_PIX_FMT_RGBA;
398 ctx->texture_count = 2;
399 break;
400 default:
402 }
403
404 av_log(avctx, AV_LOG_DEBUG, "%s texture\n", texture_name);
405
406 return 0;
407}
408
410{
411 HapContext *ctx = avctx->priv_data;
412
414
415 return 0;
416}
417
419 .p.name = "hap",
420 CODEC_LONG_NAME("Vidvox Hap"),
421 .p.type = AVMEDIA_TYPE_VIDEO,
422 .p.id = AV_CODEC_ID_HAP,
423 .init = hap_init,
425 .close = hap_close,
426 .priv_data_size = sizeof(HapContext),
429 .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
430 .codec_tags = (const uint32_t []){
431 MKTAG('H','a','p','1'),
432 MKTAG('H','a','p','5'),
433 MKTAG('H','a','p','Y'),
434 MKTAG('H','a','p','A'),
435 MKTAG('H','a','p','M'),
437 },
438};
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition dsp.h:87
const FFCodec ff_hap_decoder
Definition hapdec.c:418
static AVFormatContext * ctx
Libavcodec external API header.
static av_always_inline unsigned int bytestream2_get_buffer(GetByteContext *g, uint8_t *dst, unsigned int size)
Definition bytestream.h:267
static av_always_inline int bytestream2_get_bytes_left(const GetByteContext *g)
Definition bytestream.h:158
static av_always_inline void bytestream2_init(GetByteContext *g, const uint8_t *buf, int buf_size)
Definition bytestream.h:137
static av_always_inline int bytestream2_seek(GetByteContext *g, int offset, int whence)
Definition bytestream.h:212
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define FF_CODEC_TAGS_END
FFCodec.codec_tags termination value.
#define FF_CODEC_DECODE_CB(func)
#define CODEC_LONG_NAME(str)
#define FF_CODEC_CAP_INIT_CLEANUP
The codec allows calling the close function for deallocation even if the init function returned a fai...
#define av_clip
Definition common.h:100
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
static AVFrame * frame
#define AV_CODEC_CAP_DR1
Codec uses get_buffer() or get_encode_buffer() for allocating buffers and supports custom allocators.
Definition codec.h:49
#define AV_CODEC_CAP_SLICE_THREADS
Codec supports slice-based (or partition-based) multithreading.
Definition codec.h:102
#define AV_CODEC_CAP_FRAME_THREADS
Codec supports frame-level multithreading.
Definition codec.h:98
@ AV_CODEC_ID_HAP
Definition codec_id.h:238
#define AVERROR_DECODER_NOT_FOUND
Decoder not found.
Definition error.h:54
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition error.h:61
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition log.h:231
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
int av_reallocp(void *ptr, size_t size)
Allocate, reallocate, or free a block of memory through a pointer to a pointer.
Definition mem.c:188
@ AVMEDIA_TYPE_VIDEO
Definition avutil.h:200
int av_image_check_size(unsigned int w, unsigned int h, int log_offset, void *log_ctx)
Check if the given dimension of an image is valid, meaning that all bytes of the image can be address...
Definition imgutils.c:318
av_cold void ff_hap_free_context(HapContext *ctx)
Definition hap.c:51
int ff_hap_set_chunk_count(HapContext *ctx, int count, int first_in_frame)
Definition hap.c:29
int ff_hap_parse_section_header(GetByteContext *gbc, int *section_size, enum HapSectionType *section_type)
Definition hap.c:58
@ HAP_FMT_RGBDXT1
Definition hap.h:33
@ HAP_FMT_YCOCGDXT5
Definition hap.h:35
@ HAP_FMT_RGBADXT5
Definition hap.h:34
@ HAP_FMT_RGTC1
Definition hap.h:36
@ HAP_COMP_NONE
Definition hap.h:40
@ HAP_COMP_SNAPPY
Definition hap.h:41
@ HAP_COMP_COMPLEX
Definition hap.h:42
HapSectionType
Definition hap.h:45
@ HAP_ST_SIZE_TABLE
Definition hap.h:48
@ HAP_ST_DECODE_INSTRUCTIONS
Definition hap.h:46
@ HAP_ST_COMPRESSOR_TABLE
Definition hap.h:47
@ HAP_ST_OFFSET_TABLE
Definition hap.h:49
static av_cold int hap_init(AVCodecContext *avctx)
Definition hapdec.c:339
static int hap_parse_frame_header(AVCodecContext *avctx)
Definition hapdec.c:131
static av_cold int hap_close(AVCodecContext *avctx)
Definition hapdec.c:409
static int hap_parse_decode_instructions(HapContext *ctx, int size)
Definition hapdec.c:47
static int decompress_chunks_thread(AVCodecContext *avctx, void *arg, int chunk_nb, int thread_nb)
Definition hapdec.c:223
static int hap_can_use_tex_in_place(HapContext *ctx)
Definition hapdec.c:118
static int hap_decode(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *avpkt)
Definition hapdec.c:251
misc image utilities
const char * arg
Definition jacosubdec.c:65
Multithreading API for decoders.
#define av_cold
Definition attributes.h:117
#define FFMIN(a, b)
Definition macros.h:49
#define MKTAG(a, b, c, d)
Definition macros.h:55
#define FFALIGN(x, a)
Definition macros.h:78
Memory handling functions.
@ AV_PIX_FMT_GRAY8
Y , 8bpp.
Definition pixfmt.h:81
@ AV_PIX_FMT_RGBA
packed RGBA 8:8:8:8, 32bpp, RGBARGBA...
Definition pixfmt.h:100
@ AV_PIX_FMT_RGB0
packed RGB 8:8:8, 32bpp, RGBXRGBX... X=unused/undefined
Definition pixfmt.h:263
int ff_thread_get_buffer(AVCodecContext *avctx, AVFrame *f, int flags)
Wrapper around get_buffer() for frame-multithreaded codecs.
int64_t ff_snappy_peek_uncompressed_length(GetByteContext *gb)
Get the uncompressed length of an input buffer compressed using the Snappy algorithm.
Definition snappy.c:131
int ff_snappy_uncompress(GetByteContext *gb, uint8_t *buf, int64_t *size)
Decompress an input buffer using Snappy algorithm.
Definition snappy.c:141
Snappy decompression.
main external API structure.
Definition avcodec.h:443
enum AVPixelFormat pix_fmt
Pixel format, see AV_PIX_FMT_xxx.
Definition avcodec.h:643
int width
picture width / height.
Definition avcodec.h:604
unsigned int codec_tag
fourcc (LSB first, so "ABCD" -> ('D'<<24) + ('C'<<16) + ('B'<<8) + 'A').
Definition avcodec.h:468
int thread_count
thread count is used to decide how many independent tasks should be passed to execute()
Definition avcodec.h:1579
int(* execute2)(struct AVCodecContext *c, int(*func)(struct AVCodecContext *c2, void *arg, int jobnr, int threadnr), void *arg2, int *ret, int count)
The codec may call this to execute several independent things.
Definition avcodec.h:1628
int coded_width
Bitstream width / height, may be different from width/height e.g.
Definition avcodec.h:619
void * priv_data
Definition avcodec.h:470
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
This structure stores compressed data.
Definition packet.h:580
int size
Definition packet.h:604
uint8_t * data
Definition packet.h:603
const uint8_t * buffer
Definition bytestream.h:34
Definition hap.h:52
size_t compressed_size
Definition hap.h:55
uint32_t compressed_offset
Definition hap.h:54
int uncompressed_offset
Definition hap.h:56
size_t uncompressed_size
Definition hap.h:57
enum HapCompressor compressor
Definition hap.h:53
int(* dxt1_block)(uint8_t *dst, ptrdiff_t stride, const uint8_t *block)
Definition texturedsp.h:46
int(* dxt5ys_block)(uint8_t *dst, ptrdiff_t stride, const uint8_t *block)
Definition texturedsp.h:53
int(* rgtc1u_gray_block)(uint8_t *dst, ptrdiff_t stride, const uint8_t *block)
Definition texturedsp.h:56
int(* dxt5_block)(uint8_t *dst, ptrdiff_t stride, const uint8_t *block)
Definition texturedsp.h:51
int(* rgtc1u_alpha_block)(uint8_t *dst, ptrdiff_t stride, const uint8_t *block)
Definition texturedsp.h:57
#define av_log(a,...)
av_cold void ff_texturedsp_init(TextureDSPContext *c)
Definition texturedsp.c:640
Texture block (4x4) module.
int ff_texturedsp_exec_decompress_threads(struct AVCodecContext *avctx, TextureDSPThreadContext *ctx)
#define TEXTURE_BLOCK_W
Definition texturedsp.h:42
#define TEXTURE_BLOCK_H
Definition texturedsp.h:43
int size