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 "hap.h" |
41 |
|
|
#include "internal.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 |
✓✓ |
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 |
✗✓ |
12 |
if (ret != 0) |
58 |
|
|
return ret; |
59 |
|
|
|
60 |
|
12 |
size -= stream_remaining - bytestream2_get_bytes_left(gbc); |
61 |
|
|
|
62 |
✓✓✗✗
|
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 |
✗✓ |
6 |
if (ret != 0) |
66 |
|
|
return ret; |
67 |
✓✓ |
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 |
✗✓ |
6 |
if (ret != 0) |
76 |
|
|
return ret; |
77 |
✓✓ |
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 |
✓✗✗✓
|
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 |
✓✗ |
6 |
if (!had_offsets) { |
105 |
|
6 |
size_t running_size = 0; |
106 |
✓✓ |
53 |
for (i = 0; i < ctx->chunk_count; i++) { |
107 |
|
47 |
ctx->chunks[i].compressed_offset = running_size; |
108 |
✗✓ |
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 |
✓✓ |
15 |
for (i = 0; i < ctx->chunk_count; i++) { |
122 |
✓✗ |
12 |
if (ctx->chunks[i].compressed_offset != running_offset |
123 |
✓✓ |
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 |
✗✓ |
12 |
if (ret != 0) |
141 |
|
|
return ret; |
142 |
|
|
|
143 |
✓✓✓✗
|
12 |
if ((avctx->codec_tag == MKTAG('H','a','p','1') && (section_type & 0x0F) != HAP_FMT_RGBDXT1) || |
144 |
✓✓✓✗
|
12 |
(avctx->codec_tag == MKTAG('H','a','p','5') && (section_type & 0x0F) != HAP_FMT_RGBADXT5) || |
145 |
✓✓✓✗
|
12 |
(avctx->codec_tag == MKTAG('H','a','p','Y') && (section_type & 0x0F) != HAP_FMT_YCOCGDXT5) || |
146 |
✓✓✓✗
|
12 |
(avctx->codec_tag == MKTAG('H','a','p','A') && (section_type & 0x0F) != HAP_FMT_RGTC1) || |
147 |
✓✓✓✓
|
12 |
((avctx->codec_tag == MKTAG('H','a','p','M') && (section_type & 0x0F) != HAP_FMT_RGTC1) && |
148 |
✗✓ |
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 |
✓✓✗ |
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 |
✓✗ |
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 |
✓✓ |
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 |
✓✗✗✓
|
6 |
if (ret == 0 && section_type != HAP_ST_DECODE_INSTRUCTIONS) |
172 |
|
|
ret = AVERROR_INVALIDDATA; |
173 |
✓✗ |
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 |
✗✓ |
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 |
✓✓ |
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 |
✗✓ |
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 |
✓✓ |
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 |
✗✓ |
50 |
if (uncompressed_size < 0) { |
206 |
|
|
return uncompressed_size; |
207 |
|
|
} |
208 |
|
50 |
chunk->uncompressed_size = uncompressed_size; |
209 |
✓✗ |
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 |
✓✗ |
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 |
✗✓ |
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 |
|
12 |
static int decompress_texture_thread_internal(AVCodecContext *avctx, void *arg, |
251 |
|
|
int slice, int thread_nb, int texture_num) |
252 |
|
|
{ |
253 |
|
12 |
HapContext *ctx = avctx->priv_data; |
254 |
|
12 |
AVFrame *frame = arg; |
255 |
|
12 |
const uint8_t *d = ctx->tex_data; |
256 |
|
12 |
int w_block = avctx->coded_width / TEXTURE_BLOCK_W; |
257 |
|
12 |
int h_block = avctx->coded_height / TEXTURE_BLOCK_H; |
258 |
|
|
int x, y; |
259 |
|
|
int start_slice, end_slice; |
260 |
|
12 |
int base_blocks_per_slice = h_block / ctx->slice_count; |
261 |
|
12 |
int remainder_blocks = h_block % ctx->slice_count; |
262 |
|
|
|
263 |
|
|
/* When the frame height (in blocks) doesn't divide evenly between the |
264 |
|
|
* number of slices, spread the remaining blocks evenly between the first |
265 |
|
|
* operations */ |
266 |
|
12 |
start_slice = slice * base_blocks_per_slice; |
267 |
|
|
/* Add any extra blocks (one per slice) that have been added before this slice */ |
268 |
|
12 |
start_slice += FFMIN(slice, remainder_blocks); |
269 |
|
|
|
270 |
|
12 |
end_slice = start_slice + base_blocks_per_slice; |
271 |
|
|
/* Add an extra block if there are still remainder blocks to be accounted for */ |
272 |
✗✓ |
12 |
if (slice < remainder_blocks) |
273 |
|
|
end_slice++; |
274 |
|
|
|
275 |
✓✓ |
532 |
for (y = start_slice; y < end_slice; y++) { |
276 |
|
520 |
uint8_t *p = frame->data[0] + y * frame->linesize[0] * TEXTURE_BLOCK_H; |
277 |
|
520 |
int off = y * w_block; |
278 |
✓✓ |
62984 |
for (x = 0; x < w_block; x++) { |
279 |
✓✓ |
62464 |
if (texture_num == 0) { |
280 |
|
60736 |
ctx->tex_fun(p + x * 4 * ctx->uncompress_pix_size, frame->linesize[0], |
281 |
|
60736 |
d + (off + x) * ctx->tex_rat); |
282 |
|
|
} else { |
283 |
|
1728 |
ctx->tex_fun2(p + x * 4 * ctx->uncompress_pix_size, frame->linesize[0], |
284 |
|
1728 |
d + (off + x) * ctx->tex_rat2); |
285 |
|
|
} |
286 |
|
|
} |
287 |
|
|
} |
288 |
|
|
|
289 |
|
12 |
return 0; |
290 |
|
|
} |
291 |
|
|
|
292 |
|
9 |
static int decompress_texture_thread(AVCodecContext *avctx, void *arg, |
293 |
|
|
int slice, int thread_nb) |
294 |
|
|
{ |
295 |
|
9 |
return decompress_texture_thread_internal(avctx, arg, slice, thread_nb, 0); |
296 |
|
|
} |
297 |
|
|
|
298 |
|
3 |
static int decompress_texture2_thread(AVCodecContext *avctx, void *arg, |
299 |
|
|
int slice, int thread_nb) |
300 |
|
|
{ |
301 |
|
3 |
return decompress_texture_thread_internal(avctx, arg, slice, thread_nb, 1); |
302 |
|
|
} |
303 |
|
|
|
304 |
|
9 |
static int hap_decode(AVCodecContext *avctx, void *data, |
305 |
|
|
int *got_frame, AVPacket *avpkt) |
306 |
|
|
{ |
307 |
|
9 |
HapContext *ctx = avctx->priv_data; |
308 |
|
|
ThreadFrame tframe; |
309 |
|
|
int ret, i, t; |
310 |
|
|
int section_size; |
311 |
|
|
enum HapSectionType section_type; |
312 |
|
9 |
int start_texture_section = 0; |
313 |
|
9 |
int tex_rat[2] = {0, 0}; |
314 |
|
|
|
315 |
|
9 |
bytestream2_init(&ctx->gbc, avpkt->data, avpkt->size); |
316 |
|
|
|
317 |
|
9 |
tex_rat[0] = ctx->tex_rat; |
318 |
|
|
|
319 |
|
|
/* check for multi texture header */ |
320 |
✓✓ |
9 |
if (ctx->texture_count == 2) { |
321 |
|
3 |
ret = ff_hap_parse_section_header(&ctx->gbc, §ion_size, §ion_type); |
322 |
✗✓ |
3 |
if (ret != 0) |
323 |
|
|
return ret; |
324 |
✗✓ |
3 |
if ((section_type & 0x0F) != 0x0D) { |
325 |
|
|
av_log(avctx, AV_LOG_ERROR, "Invalid section type in 2 textures mode %#04x.\n", section_type); |
326 |
|
|
return AVERROR_INVALIDDATA; |
327 |
|
|
} |
328 |
|
3 |
start_texture_section = 4; |
329 |
|
3 |
tex_rat[1] = ctx->tex_rat2; |
330 |
|
|
} |
331 |
|
|
|
332 |
|
|
/* Get the output frame ready to receive data */ |
333 |
|
9 |
tframe.f = data; |
334 |
|
9 |
ret = ff_thread_get_buffer(avctx, &tframe, 0); |
335 |
✗✓ |
9 |
if (ret < 0) |
336 |
|
|
return ret; |
337 |
|
|
|
338 |
✓✓ |
21 |
for (t = 0; t < ctx->texture_count; t++) { |
339 |
|
12 |
bytestream2_seek(&ctx->gbc, start_texture_section, SEEK_SET); |
340 |
|
|
|
341 |
|
|
/* Check for section header */ |
342 |
|
12 |
ret = hap_parse_frame_header(avctx); |
343 |
✗✓ |
12 |
if (ret < 0) |
344 |
|
|
return ret; |
345 |
|
|
|
346 |
|
12 |
if (ctx->tex_size != (avctx->coded_width / TEXTURE_BLOCK_W) |
347 |
|
12 |
*(avctx->coded_height / TEXTURE_BLOCK_H) |
348 |
✗✓ |
12 |
*tex_rat[t]) { |
349 |
|
|
av_log(avctx, AV_LOG_ERROR, "uncompressed size mismatches\n"); |
350 |
|
|
return AVERROR_INVALIDDATA; |
351 |
|
|
} |
352 |
|
|
|
353 |
|
12 |
start_texture_section += ctx->texture_section_size + 4; |
354 |
|
|
|
355 |
✗✓ |
12 |
if (avctx->codec->update_thread_context) |
356 |
|
|
ff_thread_finish_setup(avctx); |
357 |
|
|
|
358 |
|
|
/* Unpack the DXT texture */ |
359 |
✓✓ |
12 |
if (hap_can_use_tex_in_place(ctx)) { |
360 |
|
|
int tex_size; |
361 |
|
|
/* Only DXTC texture compression in a contiguous block */ |
362 |
|
3 |
ctx->tex_data = ctx->gbc.buffer; |
363 |
✗✓ |
3 |
tex_size = FFMIN(ctx->texture_section_size, bytestream2_get_bytes_left(&ctx->gbc)); |
364 |
|
3 |
if (tex_size < (avctx->coded_width / TEXTURE_BLOCK_W) |
365 |
|
3 |
*(avctx->coded_height / TEXTURE_BLOCK_H) |
366 |
✗✓ |
3 |
*tex_rat[t]) { |
367 |
|
|
av_log(avctx, AV_LOG_ERROR, "Insufficient data\n"); |
368 |
|
|
return AVERROR_INVALIDDATA; |
369 |
|
|
} |
370 |
|
|
} else { |
371 |
|
|
/* Perform the second-stage decompression */ |
372 |
|
9 |
ret = av_reallocp(&ctx->tex_buf, ctx->tex_size); |
373 |
✗✓ |
9 |
if (ret < 0) |
374 |
|
|
return ret; |
375 |
|
|
|
376 |
|
9 |
avctx->execute2(avctx, decompress_chunks_thread, NULL, |
377 |
|
|
ctx->chunk_results, ctx->chunk_count); |
378 |
|
|
|
379 |
✓✓ |
59 |
for (i = 0; i < ctx->chunk_count; i++) { |
380 |
✗✓ |
50 |
if (ctx->chunk_results[i] < 0) |
381 |
|
|
return ctx->chunk_results[i]; |
382 |
|
|
} |
383 |
|
|
|
384 |
|
9 |
ctx->tex_data = ctx->tex_buf; |
385 |
|
|
} |
386 |
|
|
|
387 |
|
|
/* Use the decompress function on the texture, one block per thread */ |
388 |
✓✓ |
12 |
if (t == 0){ |
389 |
|
9 |
avctx->execute2(avctx, decompress_texture_thread, tframe.f, NULL, ctx->slice_count); |
390 |
|
|
} else{ |
391 |
|
3 |
tframe.f = data; |
392 |
|
3 |
avctx->execute2(avctx, decompress_texture2_thread, tframe.f, NULL, ctx->slice_count); |
393 |
|
|
} |
394 |
|
|
} |
395 |
|
|
|
396 |
|
|
/* Frame is ready to be output */ |
397 |
|
9 |
tframe.f->pict_type = AV_PICTURE_TYPE_I; |
398 |
|
9 |
tframe.f->key_frame = 1; |
399 |
|
9 |
*got_frame = 1; |
400 |
|
|
|
401 |
|
9 |
return avpkt->size; |
402 |
|
|
} |
403 |
|
|
|
404 |
|
28 |
static av_cold int hap_init(AVCodecContext *avctx) |
405 |
|
|
{ |
406 |
|
28 |
HapContext *ctx = avctx->priv_data; |
407 |
|
|
const char *texture_name; |
408 |
|
28 |
int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx); |
409 |
|
|
|
410 |
✗✓ |
28 |
if (ret < 0) { |
411 |
|
|
av_log(avctx, AV_LOG_ERROR, "Invalid video size %dx%d.\n", |
412 |
|
|
avctx->width, avctx->height); |
413 |
|
|
return ret; |
414 |
|
|
} |
415 |
|
|
|
416 |
|
|
/* Since codec is based on 4x4 blocks, size is aligned to 4 */ |
417 |
|
28 |
avctx->coded_width = FFALIGN(avctx->width, TEXTURE_BLOCK_W); |
418 |
|
28 |
avctx->coded_height = FFALIGN(avctx->height, TEXTURE_BLOCK_H); |
419 |
|
|
|
420 |
|
28 |
ff_texturedsp_init(&ctx->dxtc); |
421 |
|
|
|
422 |
|
28 |
ctx->texture_count = 1; |
423 |
|
28 |
ctx->uncompress_pix_size = 4; |
424 |
|
|
|
425 |
✓✓✓✓ ✓✗ |
28 |
switch (avctx->codec_tag) { |
426 |
|
2 |
case MKTAG('H','a','p','1'): |
427 |
|
2 |
texture_name = "DXT1"; |
428 |
|
2 |
ctx->tex_rat = 8; |
429 |
|
2 |
ctx->tex_fun = ctx->dxtc.dxt1_block; |
430 |
|
2 |
avctx->pix_fmt = AV_PIX_FMT_RGB0; |
431 |
|
2 |
break; |
432 |
|
2 |
case MKTAG('H','a','p','5'): |
433 |
|
2 |
texture_name = "DXT5"; |
434 |
|
2 |
ctx->tex_rat = 16; |
435 |
|
2 |
ctx->tex_fun = ctx->dxtc.dxt5_block; |
436 |
|
2 |
avctx->pix_fmt = AV_PIX_FMT_RGBA; |
437 |
|
2 |
break; |
438 |
|
6 |
case MKTAG('H','a','p','Y'): |
439 |
|
6 |
texture_name = "DXT5-YCoCg-scaled"; |
440 |
|
6 |
ctx->tex_rat = 16; |
441 |
|
6 |
ctx->tex_fun = ctx->dxtc.dxt5ys_block; |
442 |
|
6 |
avctx->pix_fmt = AV_PIX_FMT_RGB0; |
443 |
|
6 |
break; |
444 |
|
6 |
case MKTAG('H','a','p','A'): |
445 |
|
6 |
texture_name = "RGTC1"; |
446 |
|
6 |
ctx->tex_rat = 8; |
447 |
|
6 |
ctx->tex_fun = ctx->dxtc.rgtc1u_gray_block; |
448 |
|
6 |
avctx->pix_fmt = AV_PIX_FMT_GRAY8; |
449 |
|
6 |
ctx->uncompress_pix_size = 1; |
450 |
|
6 |
break; |
451 |
|
12 |
case MKTAG('H','a','p','M'): |
452 |
|
12 |
texture_name = "DXT5-YCoCg-scaled / RGTC1"; |
453 |
|
12 |
ctx->tex_rat = 16; |
454 |
|
12 |
ctx->tex_rat2 = 8; |
455 |
|
12 |
ctx->tex_fun = ctx->dxtc.dxt5ys_block; |
456 |
|
12 |
ctx->tex_fun2 = ctx->dxtc.rgtc1u_alpha_block; |
457 |
|
12 |
avctx->pix_fmt = AV_PIX_FMT_RGBA; |
458 |
|
12 |
ctx->texture_count = 2; |
459 |
|
12 |
break; |
460 |
|
|
default: |
461 |
|
|
return AVERROR_DECODER_NOT_FOUND; |
462 |
|
|
} |
463 |
|
|
|
464 |
|
28 |
av_log(avctx, AV_LOG_DEBUG, "%s texture\n", texture_name); |
465 |
|
|
|
466 |
|
28 |
ctx->slice_count = av_clip(avctx->thread_count, 1, |
467 |
|
28 |
avctx->coded_height / TEXTURE_BLOCK_H); |
468 |
|
|
|
469 |
|
28 |
return 0; |
470 |
|
|
} |
471 |
|
|
|
472 |
|
28 |
static av_cold int hap_close(AVCodecContext *avctx) |
473 |
|
|
{ |
474 |
|
28 |
HapContext *ctx = avctx->priv_data; |
475 |
|
|
|
476 |
|
28 |
ff_hap_free_context(ctx); |
477 |
|
|
|
478 |
|
28 |
return 0; |
479 |
|
|
} |
480 |
|
|
|
481 |
|
|
AVCodec ff_hap_decoder = { |
482 |
|
|
.name = "hap", |
483 |
|
|
.long_name = NULL_IF_CONFIG_SMALL("Vidvox Hap"), |
484 |
|
|
.type = AVMEDIA_TYPE_VIDEO, |
485 |
|
|
.id = AV_CODEC_ID_HAP, |
486 |
|
|
.init = hap_init, |
487 |
|
|
.decode = hap_decode, |
488 |
|
|
.close = hap_close, |
489 |
|
|
.priv_data_size = sizeof(HapContext), |
490 |
|
|
.capabilities = AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_SLICE_THREADS | |
491 |
|
|
AV_CODEC_CAP_DR1, |
492 |
|
|
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | |
493 |
|
|
FF_CODEC_CAP_INIT_CLEANUP, |
494 |
|
|
.codec_tags = (const uint32_t []){ |
495 |
|
|
MKTAG('H','a','p','1'), |
496 |
|
|
MKTAG('H','a','p','5'), |
497 |
|
|
MKTAG('H','a','p','Y'), |
498 |
|
|
MKTAG('H','a','p','A'), |
499 |
|
|
MKTAG('H','a','p','M'), |
500 |
|
|
FF_CODEC_TAGS_END, |
501 |
|
|
}, |
502 |
|
|
}; |