3 * Copyright (C) 2015 Vittorio Giovara <vittorio.giovara@gmail.com>
4 * Copyright (C) 2015 Tom Butterworth <bangnoise@gmail.com>
6 * HapQA and HAPAlphaOnly added by Jokyo Images
8 * This file is part of FFmpeg.
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.
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.
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
29 * Fourcc: Hap1, Hap5, HapY, HapA, HapM
31 * https://github.com/Vidvox/hap/blob/master/documentation/HapVideoDRAFT.md
36 #include "libavutil/imgutils.h"
37 #include "libavutil/mem.h"
40 #include "bytestream.h"
41 #include "codec_internal.h"
44 #include "texturedsp.h"
47 static int hap_parse_decode_instructions(HapContext
*ctx
, int size
)
49 GetByteContext
*gbc
= &ctx
->gbc
;
51 enum HapSectionType section_type
;
52 int is_first_table
= 1, had_offsets
= 0, had_compressors
= 0, had_sizes
= 0;
56 int stream_remaining
= bytestream2_get_bytes_left(gbc
);
57 ret
= ff_hap_parse_section_header(gbc
, §ion_size
, §ion_type
);
61 size
-= stream_remaining
- bytestream2_get_bytes_left(gbc
);
63 switch (section_type
) {
64 case HAP_ST_COMPRESSOR_TABLE
:
65 ret
= ff_hap_set_chunk_count(ctx
, section_size
, is_first_table
);
68 for (i
= 0; i
< section_size
; i
++) {
69 ctx
->chunks
[i
].compressor
= bytestream2_get_byte(gbc
) << 4;
74 case HAP_ST_SIZE_TABLE
:
75 ret
= ff_hap_set_chunk_count(ctx
, section_size
/ 4, is_first_table
);
78 for (i
= 0; i
< section_size
/ 4; i
++) {
79 ctx
->chunks
[i
].compressed_size
= bytestream2_get_le32(gbc
);
84 case HAP_ST_OFFSET_TABLE
:
85 ret
= ff_hap_set_chunk_count(ctx
, section_size
/ 4, is_first_table
);
88 for (i
= 0; i
< section_size
/ 4; i
++) {
89 ctx
->chunks
[i
].compressed_offset
= bytestream2_get_le32(gbc
);
100 if (!had_sizes
|| !had_compressors
)
101 return AVERROR_INVALIDDATA
;
103 /* The offsets table is optional. If not present than calculate offsets by
104 * summing the sizes of preceding chunks. */
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
;
118 static int hap_can_use_tex_in_place(HapContext
*ctx
)
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
)
126 running_offset
+= ctx
->chunks
[i
].compressed_size
;
131 static int hap_parse_frame_header(AVCodecContext
*avctx
)
133 HapContext
*ctx
= avctx
->priv_data
;
134 GetByteContext
*gbc
= &ctx
->gbc
;
136 enum HapSectionType section_type
;
137 const char *compressorstr
;
140 ret
= ff_hap_parse_section_header(gbc
, &ctx
->texture_section_size
, §ion_type
);
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
;
155 switch (section_type
& 0xF0) {
157 case HAP_COMP_SNAPPY
:
158 ret
= ff_hap_set_chunk_count(ctx
, 1, 1);
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
;
164 if (ctx
->chunks
[0].compressor
== HAP_COMP_NONE
) {
165 compressorstr
= "none";
167 compressorstr
= "snappy";
170 case HAP_COMP_COMPLEX
:
171 ret
= ff_hap_parse_section_header(gbc
, §ion_size
, §ion_type
);
172 if (ret
== 0 && section_type
!= HAP_ST_DECODE_INSTRUCTIONS
)
173 ret
= AVERROR_INVALIDDATA
;
175 ret
= hap_parse_decode_instructions(ctx
, section_size
);
176 compressorstr
= "complex";
179 ret
= AVERROR_INVALIDDATA
;
186 /* Check the frame is valid and read the uncompressed chunk sizes */
188 for (i
= 0; i
< ctx
->chunk_count
; i
++) {
189 HapChunk
*chunk
= &ctx
->chunks
[i
];
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
;
195 /* Chunks are unpacked sequentially, ctx->tex_size is the uncompressed
197 chunk
->uncompressed_offset
= ctx
->tex_size
;
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
;
209 chunk
->uncompressed_size
= uncompressed_size
;
210 } else if (chunk
->compressor
== HAP_COMP_NONE
) {
211 chunk
->uncompressed_size
= chunk
->compressed_size
;
213 return AVERROR_INVALIDDATA
;
215 ctx
->tex_size
+= chunk
->uncompressed_size
;
218 av_log(avctx
, AV_LOG_DEBUG
, "%s compressor\n", compressorstr
);
223 static int decompress_chunks_thread(AVCodecContext
*avctx
, void *arg
,
224 int chunk_nb
, int thread_nb
)
226 HapContext
*ctx
= avctx
->priv_data
;
228 HapChunk
*chunk
= &ctx
->chunks
[chunk_nb
];
230 uint8_t *dst
= ctx
->tex_buf
+ chunk
->uncompressed_offset
;
232 bytestream2_init(&gbc
, ctx
->gbc
.buffer
+ chunk
->compressed_offset
, chunk
->compressed_size
);
234 if (chunk
->compressor
== HAP_COMP_SNAPPY
) {
236 int64_t uncompressed_size
= ctx
->tex_size
;
238 /* Uncompress the frame */
239 ret
= ff_snappy_uncompress(&gbc
, dst
, &uncompressed_size
);
241 av_log(avctx
, AV_LOG_ERROR
, "Snappy uncompress error\n");
244 } else if (chunk
->compressor
== HAP_COMP_NONE
) {
245 bytestream2_get_buffer(&gbc
, dst
, chunk
->compressed_size
);
251 static int hap_decode(AVCodecContext
*avctx
, AVFrame
*frame
,
252 int *got_frame
, AVPacket
*avpkt
)
254 HapContext
*ctx
= avctx
->priv_data
;
257 enum HapSectionType section_type
;
258 int start_texture_section
= 0;
260 bytestream2_init(&ctx
->gbc
, avpkt
->data
, avpkt
->size
);
262 /* check for multi texture header */
263 if (ctx
->texture_count
== 2) {
264 ret
= ff_hap_parse_section_header(&ctx
->gbc
, §ion_size
, §ion_type
);
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
;
271 start_texture_section
= 4;
274 /* Get the output frame ready to receive data */
275 ret
= ff_thread_get_buffer(avctx
, frame
, 0);
279 for (t
= 0; t
< ctx
->texture_count
; t
++) {
280 bytestream2_seek(&ctx
->gbc
, start_texture_section
, SEEK_SET
);
282 /* Check for section header */
283 ret
= hap_parse_frame_header(avctx
);
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
;
294 start_texture_section
+= ctx
->texture_section_size
+ 4;
296 /* Unpack the DXT texture */
297 if (hap_can_use_tex_in_place(ctx
)) {
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
;
309 /* Perform the second-stage decompression */
310 ret
= av_reallocp(&ctx
->tex_buf
, ctx
->tex_size
);
313 memset(ctx
->tex_buf
, 0, ctx
->tex_size
);
315 avctx
->execute2(avctx
, decompress_chunks_thread
, NULL
,
316 ctx
->chunk_results
, ctx
->chunk_count
);
318 for (i
= 0; i
< ctx
->chunk_count
; i
++) {
319 if (ctx
->chunk_results
[i
] < 0)
320 return ctx
->chunk_results
[i
];
323 ctx
->dec
[t
].tex_data
.in
= ctx
->tex_buf
;
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
;
330 ff_texturedsp_exec_decompress_threads(avctx
, &ctx
->dec
[t
]);
333 /* Frame is ready to be output */
339 static av_cold
int hap_init(AVCodecContext
*avctx
)
341 HapContext
*ctx
= avctx
->priv_data
;
342 TextureDSPContext dxtc
;
343 const char *texture_name
;
344 int ret
= av_image_check_size(avctx
->width
, avctx
->height
, 0, avctx
);
347 av_log(avctx
, AV_LOG_ERROR
, "Invalid video size %dx%d.\n",
348 avctx
->width
, avctx
->height
);
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
);
356 ff_texturedsp_init(&dxtc
);
358 ctx
->texture_count
= 1;
359 ctx
->dec
[0].raw_ratio
= 16;
360 ctx
->dec
[0].slice_count
= av_clip(avctx
->thread_count
, 1,
361 avctx
->coded_height
/ TEXTURE_BLOCK_H
);
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
;
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
;
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
;
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
;
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;
401 return AVERROR_DECODER_NOT_FOUND
;
404 av_log(avctx
, AV_LOG_DEBUG
, "%s texture\n", texture_name
);
409 static av_cold
int hap_close(AVCodecContext
*avctx
)
411 HapContext
*ctx
= avctx
->priv_data
;
413 ff_hap_free_context(ctx
);
418 const FFCodec ff_hap_decoder
= {
420 CODEC_LONG_NAME("Vidvox Hap"),
421 .p
.type
= AVMEDIA_TYPE_VIDEO
,
422 .p
.id
= AV_CODEC_ID_HAP
,
424 FF_CODEC_DECODE_CB(hap_decode
),
426 .priv_data_size
= sizeof(HapContext
),
427 .p
.capabilities
= AV_CODEC_CAP_FRAME_THREADS
| AV_CODEC_CAP_SLICE_THREADS
|
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'),