Line | Branch | Exec | Source |
---|---|---|---|
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 | 6 | static int hap_parse_decode_instructions(HapContext *ctx, int size) | |
48 | { | ||
49 | 6 | GetByteContext *gbc = &ctx->gbc; | |
50 | int section_size; | ||
51 | enum HapSectionType section_type; | ||
52 | 6 | int is_first_table = 1, had_offsets = 0, had_compressors = 0, had_sizes = 0; | |
53 | int i, ret; | ||
54 | |||
55 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
|
18 | while (size > 0) { |
56 | 12 | int stream_remaining = bytestream2_get_bytes_left(gbc); | |
57 | 12 | ret = ff_hap_parse_section_header(gbc, §ion_size, §ion_type); | |
58 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (ret != 0) |
59 | ✗ | return ret; | |
60 | |||
61 | 12 | size -= stream_remaining - bytestream2_get_bytes_left(gbc); | |
62 | |||
63 |
2/4✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
12 | switch (section_type) { |
64 | 6 | case HAP_ST_COMPRESSOR_TABLE: | |
65 | 6 | ret = ff_hap_set_chunk_count(ctx, section_size, is_first_table); | |
66 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (ret != 0) |
67 | ✗ | return ret; | |
68 |
2/2✓ Branch 0 taken 47 times.
✓ Branch 1 taken 6 times.
|
53 | for (i = 0; i < section_size; i++) { |
69 | 47 | ctx->chunks[i].compressor = bytestream2_get_byte(gbc) << 4; | |
70 | } | ||
71 | 6 | had_compressors = 1; | |
72 | 6 | is_first_table = 0; | |
73 | 6 | break; | |
74 | 6 | case HAP_ST_SIZE_TABLE: | |
75 | 6 | ret = ff_hap_set_chunk_count(ctx, section_size / 4, is_first_table); | |
76 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | if (ret != 0) |
77 | ✗ | return ret; | |
78 |
2/2✓ Branch 0 taken 47 times.
✓ Branch 1 taken 6 times.
|
53 | for (i = 0; i < section_size / 4; i++) { |
79 | 47 | ctx->chunks[i].compressed_size = bytestream2_get_le32(gbc); | |
80 | } | ||
81 | 6 | had_sizes = 1; | |
82 | 6 | is_first_table = 0; | |
83 | 6 | break; | |
84 | ✗ | case HAP_ST_OFFSET_TABLE: | |
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 | 12 | size -= section_size; | |
98 | } | ||
99 | |||
100 |
2/4✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
|
6 | 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 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if (!had_offsets) { |
106 | 6 | size_t running_size = 0; | |
107 |
2/2✓ Branch 0 taken 47 times.
✓ Branch 1 taken 6 times.
|
53 | for (i = 0; i < ctx->chunk_count; i++) { |
108 | 47 | ctx->chunks[i].compressed_offset = running_size; | |
109 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 47 times.
|
47 | if (ctx->chunks[i].compressed_size > UINT32_MAX - running_size) |
110 | ✗ | return AVERROR_INVALIDDATA; | |
111 | 47 | running_size += ctx->chunks[i].compressed_size; | |
112 | } | ||
113 | } | ||
114 | |||
115 | 6 | return 0; | |
116 | } | ||
117 | |||
118 | 12 | static int hap_can_use_tex_in_place(HapContext *ctx) | |
119 | { | ||
120 | int i; | ||
121 | 12 | size_t running_offset = 0; | |
122 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 3 times.
|
15 | for (i = 0; i < ctx->chunk_count; i++) { |
123 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (ctx->chunks[i].compressed_offset != running_offset |
124 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
|
12 | || ctx->chunks[i].compressor != HAP_COMP_NONE) |
125 | 9 | return 0; | |
126 | 3 | running_offset += ctx->chunks[i].compressed_size; | |
127 | } | ||
128 | 3 | return 1; | |
129 | } | ||
130 | |||
131 | 12 | static int hap_parse_frame_header(AVCodecContext *avctx) | |
132 | { | ||
133 | 12 | HapContext *ctx = avctx->priv_data; | |
134 | 12 | GetByteContext *gbc = &ctx->gbc; | |
135 | int section_size; | ||
136 | enum HapSectionType section_type; | ||
137 | const char *compressorstr; | ||
138 | int i, ret; | ||
139 | |||
140 | 12 | ret = ff_hap_parse_section_header(gbc, &ctx->texture_section_size, §ion_type); | |
141 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (ret != 0) |
142 | ✗ | return ret; | |
143 | |||
144 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
12 | if ((avctx->codec_tag == MKTAG('H','a','p','1') && (section_type & 0x0F) != HAP_FMT_RGBDXT1) || |
145 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
12 | (avctx->codec_tag == MKTAG('H','a','p','5') && (section_type & 0x0F) != HAP_FMT_RGBADXT5) || |
146 |
3/4✓ Branch 0 taken 2 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
12 | (avctx->codec_tag == MKTAG('H','a','p','Y') && (section_type & 0x0F) != HAP_FMT_YCOCGDXT5) || |
147 |
3/4✓ Branch 0 taken 2 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
|
12 | (avctx->codec_tag == MKTAG('H','a','p','A') && (section_type & 0x0F) != HAP_FMT_RGTC1) || |
148 |
4/4✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 3 times.
|
12 | ((avctx->codec_tag == MKTAG('H','a','p','M') && (section_type & 0x0F) != HAP_FMT_RGTC1) && |
149 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | (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 |
2/3✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
12 | switch (section_type & 0xF0) { |
156 | 6 | case HAP_COMP_NONE: | |
157 | case HAP_COMP_SNAPPY: | ||
158 | 6 | ret = ff_hap_set_chunk_count(ctx, 1, 1); | |
159 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if (ret == 0) { |
160 | 6 | ctx->chunks[0].compressor = section_type & 0xF0; | |
161 | 6 | ctx->chunks[0].compressed_offset = 0; | |
162 | 6 | ctx->chunks[0].compressed_size = ctx->texture_section_size; | |
163 | } | ||
164 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
|
6 | if (ctx->chunks[0].compressor == HAP_COMP_NONE) { |
165 | 3 | compressorstr = "none"; | |
166 | } else { | ||
167 | 3 | compressorstr = "snappy"; | |
168 | } | ||
169 | 6 | break; | |
170 | 6 | case HAP_COMP_COMPLEX: | |
171 | 6 | ret = ff_hap_parse_section_header(gbc, §ion_size, §ion_type); | |
172 |
2/4✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
|
6 | if (ret == 0 && section_type != HAP_ST_DECODE_INSTRUCTIONS) |
173 | ✗ | ret = AVERROR_INVALIDDATA; | |
174 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if (ret == 0) |
175 | 6 | ret = hap_parse_decode_instructions(ctx, section_size); | |
176 | 6 | compressorstr = "complex"; | |
177 | 6 | break; | |
178 | ✗ | default: | |
179 | ✗ | ret = AVERROR_INVALIDDATA; | |
180 | ✗ | break; | |
181 | } | ||
182 | |||
183 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (ret != 0) |
184 | ✗ | return ret; | |
185 | |||
186 | /* Check the frame is valid and read the uncompressed chunk sizes */ | ||
187 | 12 | ctx->tex_size = 0; | |
188 |
2/2✓ Branch 0 taken 53 times.
✓ Branch 1 taken 12 times.
|
65 | for (i = 0; i < ctx->chunk_count; i++) { |
189 | 53 | HapChunk *chunk = &ctx->chunks[i]; | |
190 | |||
191 | /* Check the compressed buffer is valid */ | ||
192 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 53 times.
|
53 | 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 | 53 | chunk->uncompressed_offset = ctx->tex_size; | |
198 | |||
199 | /* Fill out uncompressed size */ | ||
200 |
2/2✓ Branch 0 taken 50 times.
✓ Branch 1 taken 3 times.
|
53 | if (chunk->compressor == HAP_COMP_SNAPPY) { |
201 | GetByteContext gbc_tmp; | ||
202 | int64_t uncompressed_size; | ||
203 | 50 | bytestream2_init(&gbc_tmp, gbc->buffer + chunk->compressed_offset, | |
204 | 50 | chunk->compressed_size); | |
205 | 50 | uncompressed_size = ff_snappy_peek_uncompressed_length(&gbc_tmp); | |
206 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
|
50 | if (uncompressed_size < 0) { |
207 | ✗ | return uncompressed_size; | |
208 | } | ||
209 | 50 | chunk->uncompressed_size = uncompressed_size; | |
210 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | } else if (chunk->compressor == HAP_COMP_NONE) { |
211 | 3 | chunk->uncompressed_size = chunk->compressed_size; | |
212 | } else { | ||
213 | ✗ | return AVERROR_INVALIDDATA; | |
214 | } | ||
215 | 53 | ctx->tex_size += chunk->uncompressed_size; | |
216 | } | ||
217 | |||
218 | 12 | av_log(avctx, AV_LOG_DEBUG, "%s compressor\n", compressorstr); | |
219 | |||
220 | 12 | return ret; | |
221 | } | ||
222 | |||
223 | 50 | static int decompress_chunks_thread(AVCodecContext *avctx, void *arg, | |
224 | int chunk_nb, int thread_nb) | ||
225 | { | ||
226 | 50 | HapContext *ctx = avctx->priv_data; | |
227 | |||
228 | 50 | HapChunk *chunk = &ctx->chunks[chunk_nb]; | |
229 | GetByteContext gbc; | ||
230 | 50 | uint8_t *dst = ctx->tex_buf + chunk->uncompressed_offset; | |
231 | |||
232 | 50 | bytestream2_init(&gbc, ctx->gbc.buffer + chunk->compressed_offset, chunk->compressed_size); | |
233 | |||
234 |
1/2✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
|
50 | if (chunk->compressor == HAP_COMP_SNAPPY) { |
235 | int ret; | ||
236 | 50 | int64_t uncompressed_size = ctx->tex_size; | |
237 | |||
238 | /* Uncompress the frame */ | ||
239 | 50 | ret = ff_snappy_uncompress(&gbc, dst, &uncompressed_size); | |
240 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
|
50 | 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) { | |
245 | ✗ | bytestream2_get_buffer(&gbc, dst, chunk->compressed_size); | |
246 | } | ||
247 | |||
248 | 50 | return 0; | |
249 | } | ||
250 | |||
251 | 9 | static int hap_decode(AVCodecContext *avctx, AVFrame *frame, | |
252 | int *got_frame, AVPacket *avpkt) | ||
253 | { | ||
254 | 9 | HapContext *ctx = avctx->priv_data; | |
255 | int ret, i, t; | ||
256 | int section_size; | ||
257 | enum HapSectionType section_type; | ||
258 | 9 | int start_texture_section = 0; | |
259 | |||
260 | 9 | bytestream2_init(&ctx->gbc, avpkt->data, avpkt->size); | |
261 | |||
262 | /* check for multi texture header */ | ||
263 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 6 times.
|
9 | if (ctx->texture_count == 2) { |
264 | 3 | ret = ff_hap_parse_section_header(&ctx->gbc, §ion_size, §ion_type); | |
265 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (ret != 0) |
266 | ✗ | return ret; | |
267 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | 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 | 3 | start_texture_section = 4; | |
272 | } | ||
273 | |||
274 | /* Get the output frame ready to receive data */ | ||
275 | 9 | ret = ff_thread_get_buffer(avctx, frame, 0); | |
276 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if (ret < 0) |
277 | ✗ | return ret; | |
278 | |||
279 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 9 times.
|
21 | for (t = 0; t < ctx->texture_count; t++) { |
280 | 12 | bytestream2_seek(&ctx->gbc, start_texture_section, SEEK_SET); | |
281 | |||
282 | /* Check for section header */ | ||
283 | 12 | ret = hap_parse_frame_header(avctx); | |
284 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (ret < 0) |
285 | ✗ | return ret; | |
286 | |||
287 | 12 | if (ctx->tex_size != (avctx->coded_width / TEXTURE_BLOCK_W) | |
288 | 12 | *(avctx->coded_height / TEXTURE_BLOCK_H) | |
289 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | *ctx->dec[t].tex_ratio) { |
290 | ✗ | av_log(avctx, AV_LOG_ERROR, "uncompressed size mismatches\n"); | |
291 | ✗ | return AVERROR_INVALIDDATA; | |
292 | } | ||
293 | |||
294 | 12 | start_texture_section += ctx->texture_section_size + 4; | |
295 | |||
296 | /* Unpack the DXT texture */ | ||
297 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 9 times.
|
12 | if (hap_can_use_tex_in_place(ctx)) { |
298 | int tex_size; | ||
299 | /* Only DXTC texture compression in a contiguous block */ | ||
300 | 3 | ctx->dec[t].tex_data.in = ctx->gbc.buffer; | |
301 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
|
3 | tex_size = FFMIN(ctx->texture_section_size, bytestream2_get_bytes_left(&ctx->gbc)); |
302 | 3 | if (tex_size < (avctx->coded_width / TEXTURE_BLOCK_W) | |
303 | 3 | *(avctx->coded_height / TEXTURE_BLOCK_H) | |
304 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | *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 | 9 | ret = av_reallocp(&ctx->tex_buf, ctx->tex_size); | |
311 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | if (ret < 0) |
312 | ✗ | return ret; | |
313 | |||
314 | 9 | avctx->execute2(avctx, decompress_chunks_thread, NULL, | |
315 | ctx->chunk_results, ctx->chunk_count); | ||
316 | |||
317 |
2/2✓ Branch 0 taken 50 times.
✓ Branch 1 taken 9 times.
|
59 | for (i = 0; i < ctx->chunk_count; i++) { |
318 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
|
50 | if (ctx->chunk_results[i] < 0) |
319 | ✗ | return ctx->chunk_results[i]; | |
320 | } | ||
321 | |||
322 | 9 | ctx->dec[t].tex_data.in = ctx->tex_buf; | |
323 | } | ||
324 | |||
325 | 12 | ctx->dec[t].frame_data.out = frame->data[0]; | |
326 | 12 | ctx->dec[t].stride = frame->linesize[0]; | |
327 | 12 | ctx->dec[t].width = avctx->coded_width; | |
328 | 12 | ctx->dec[t].height = avctx->coded_height; | |
329 | 12 | ff_texturedsp_exec_decompress_threads(avctx, &ctx->dec[t]); | |
330 | } | ||
331 | |||
332 | /* Frame is ready to be output */ | ||
333 | 9 | *got_frame = 1; | |
334 | |||
335 | 9 | return avpkt->size; | |
336 | } | ||
337 | |||
338 | 28 | static av_cold int hap_init(AVCodecContext *avctx) | |
339 | { | ||
340 | 28 | HapContext *ctx = avctx->priv_data; | |
341 | TextureDSPContext dxtc; | ||
342 | const char *texture_name; | ||
343 | 28 | int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx); | |
344 | |||
345 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
|
28 | if (ret < 0) { |
346 | ✗ | av_log(avctx, AV_LOG_ERROR, "Invalid video size %dx%d.\n", | |
347 | avctx->width, avctx->height); | ||
348 | ✗ | return ret; | |
349 | } | ||
350 | |||
351 | /* Since codec is based on 4x4 blocks, size is aligned to 4 */ | ||
352 | 28 | avctx->coded_width = FFALIGN(avctx->width, TEXTURE_BLOCK_W); | |
353 | 28 | avctx->coded_height = FFALIGN(avctx->height, TEXTURE_BLOCK_H); | |
354 | |||
355 | 28 | ff_texturedsp_init(&dxtc); | |
356 | |||
357 | 28 | ctx->texture_count = 1; | |
358 | 28 | ctx->dec[0].raw_ratio = 16; | |
359 | 28 | ctx->dec[0].slice_count = av_clip(avctx->thread_count, 1, | |
360 | 28 | avctx->coded_height / TEXTURE_BLOCK_H); | |
361 | |||
362 |
5/6✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
|
28 | switch (avctx->codec_tag) { |
363 | 2 | case MKTAG('H','a','p','1'): | |
364 | 2 | texture_name = "DXT1"; | |
365 | 2 | ctx->dec[0].tex_ratio = 8; | |
366 | 2 | ctx->dec[0].tex_funct = dxtc.dxt1_block; | |
367 | 2 | avctx->pix_fmt = AV_PIX_FMT_RGB0; | |
368 | 2 | break; | |
369 | 2 | case MKTAG('H','a','p','5'): | |
370 | 2 | texture_name = "DXT5"; | |
371 | 2 | ctx->dec[0].tex_ratio = 16; | |
372 | 2 | ctx->dec[0].tex_funct = dxtc.dxt5_block; | |
373 | 2 | avctx->pix_fmt = AV_PIX_FMT_RGBA; | |
374 | 2 | break; | |
375 | 6 | case MKTAG('H','a','p','Y'): | |
376 | 6 | texture_name = "DXT5-YCoCg-scaled"; | |
377 | 6 | ctx->dec[0].tex_ratio = 16; | |
378 | 6 | ctx->dec[0].tex_funct = dxtc.dxt5ys_block; | |
379 | 6 | avctx->pix_fmt = AV_PIX_FMT_RGB0; | |
380 | 6 | break; | |
381 | 6 | case MKTAG('H','a','p','A'): | |
382 | 6 | texture_name = "RGTC1"; | |
383 | 6 | ctx->dec[0].tex_ratio = 8; | |
384 | 6 | ctx->dec[0].tex_funct = dxtc.rgtc1u_gray_block; | |
385 | 6 | ctx->dec[0].raw_ratio = 4; | |
386 | 6 | avctx->pix_fmt = AV_PIX_FMT_GRAY8; | |
387 | 6 | break; | |
388 | 12 | case MKTAG('H','a','p','M'): | |
389 | 12 | texture_name = "DXT5-YCoCg-scaled / RGTC1"; | |
390 | 12 | ctx->dec[0].tex_ratio = 16; | |
391 | 12 | ctx->dec[1].tex_ratio = 8; | |
392 | 12 | ctx->dec[0].tex_funct = dxtc.dxt5ys_block; | |
393 | 12 | ctx->dec[1].tex_funct = dxtc.rgtc1u_alpha_block; | |
394 | 12 | ctx->dec[1].raw_ratio = 16; | |
395 | 12 | ctx->dec[1].slice_count = ctx->dec[0].slice_count; | |
396 | 12 | avctx->pix_fmt = AV_PIX_FMT_RGBA; | |
397 | 12 | ctx->texture_count = 2; | |
398 | 12 | break; | |
399 | ✗ | default: | |
400 | ✗ | return AVERROR_DECODER_NOT_FOUND; | |
401 | } | ||
402 | |||
403 | 28 | av_log(avctx, AV_LOG_DEBUG, "%s texture\n", texture_name); | |
404 | |||
405 | 28 | return 0; | |
406 | } | ||
407 | |||
408 | 28 | static av_cold int hap_close(AVCodecContext *avctx) | |
409 | { | ||
410 | 28 | HapContext *ctx = avctx->priv_data; | |
411 | |||
412 | 28 | ff_hap_free_context(ctx); | |
413 | |||
414 | 28 | return 0; | |
415 | } | ||
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 | }; | ||
438 |