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