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