FFmpeg
Loading...
Searching...
No Matches
ktxdec.c
Go to the documentation of this file.
1/*
2 * KTX 1.0 demuxer for ASTC
3 * Copyright (c) 2026 Jun Zhao
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22/**
23 * @file
24 * KTX 1.0 demuxer for ASTC textures.
25 *
26 * Parses the KTX 1.0 header, recovers the ASTC block size from the GL
27 * internal format enum, and synthesizes a 16-byte .astc-style extradata
28 * for the decoder. The raw ASTC bitstream is emitted as a single packet.
29 */
30
31#include "config_components.h"
32
33#include <inttypes.h>
34#include <limits.h>
35
36#include "libavcodec/defs.h"
37#include "avformat.h"
38#include "avio.h"
39#include "avio_internal.h"
40#include "demux.h"
41#include "internal.h"
42#include "libavutil/display.h"
44
45#define ASTC_HEADER_SIZE 16
46static const uint8_t astc_magic[4] = { 0x13, 0xAB, 0xA1, 0x5C };
47static const uint8_t ktx_magic[12] = {
48 0xAB, 'K', 'T', 'X', ' ', '1', '1', 0xBB, 0x0D, 0x0A, 0x1A, 0x0A
49};
50
51/* ASTC GL internal format enums (2D), linear base 0x93B0, sRGB = +0x20. */
52static const int astc_gl_linear[14] = {
53 0x93B0, 0x93B1, 0x93B2, 0x93B3, 0x93B4, 0x93B5, 0x93B6, 0x93B7,
54 0x93B8, 0x93B9, 0x93BA, 0x93BB, 0x93BC, 0x93BD
55};
56static const int astc_bx[14] = { 4, 5, 5, 6, 6, 8, 8, 8, 10, 10, 10, 10, 12, 12 };
57static const int astc_by[14] = { 4, 4, 5, 5, 6, 5, 6, 8, 5, 6, 8, 10, 10, 12 };
58
63
64/* Map a GL internal format enum to a 2D ASTC block size. Returns 1 on success. */
65static int gl_enum_to_block(uint32_t e, int *bx, int *by)
66{
67 int base = (e >= 0x93D0 && e <= 0x93DD) ? (e - 0x20) : e;
68 for (int i = 0; i < 14; i++) {
69 if (astc_gl_linear[i] == (int)base) {
70 *bx = astc_bx[i];
71 *by = astc_by[i];
72 return 1;
73 }
74 }
75 return 0;
76}
77
78static int ktx_probe(const AVProbeData *p)
79{
80 int bx, by;
81
82 if (p->buf_size < 32 || memcmp(p->buf, ktx_magic, sizeof(ktx_magic)))
83 return 0;
84
85 /* KTX 1.0 carries ETC, uncompressed and other payloads this demuxer cannot
86 * read, so only claim the file once glInternalFormat is an ASTC format we
87 * actually support. */
88 if (!gl_enum_to_block(AV_RL32(p->buf + 28), &bx, &by))
89 return 0;
90
91 /* Just below the maximum: this is a KTX file, but only the ASTC subset of
92 * the container is implemented here. */
93 return AVPROBE_SCORE_MAX - 1;
94}
95
96/* Map a KTXorientation value such as "S=r,T=d" onto the flips needed to
97 * bring the stored rows back to the top-down, left-to-right order the
98 * decoder produces. The R component is irrelevant for 2D textures. */
99static void ktx_orientation_flips(const char *value, int *hflip, int *vflip)
100{
101 const char *p = value;
102
103 while (p && *p) {
104 if (p[0] == 'S' && p[1] == '=' && p[2])
105 *hflip = p[2] == 'l';
106 else if (p[0] == 'T' && p[1] == '=' && p[2])
107 *vflip = p[2] == 'u';
108 p = strchr(p, ',');
109 if (p)
110 p++;
111 }
112}
113
114/* Walk the key/value block, picking up KTXorientation. Unknown or oversized
115 * entries are skipped rather than buffered wholesale. */
116static int ktx_read_orientation(AVFormatContext *s, uint32_t kvdata,
117 int *hflip, int *vflip)
118{
119 AVIOContext *pb = s->pb;
120 uint8_t buf[256];
121 uint32_t left = kvdata;
122
123 *hflip = *vflip = 0;
124
125 while (left >= 4) {
126 uint32_t entry = avio_rl32(pb);
127 uint32_t padded = (entry + 3) & ~3u;
128
129 left -= 4;
130 if (!entry || entry > left || padded > left) {
131 av_log(s, AV_LOG_ERROR, "Invalid KTX key/value data.\n");
132 return AVERROR_INVALIDDATA;
133 }
134
135 if (entry >= sizeof(buf)) {
136 /* Nothing interesting is this large. */
137 if (avio_skip(pb, padded) < 0)
138 return AVERROR_INVALIDDATA;
139 } else {
140 const char *key, *value;
141
142 if (ffio_read_size(pb, buf, entry) < 0)
143 return AVERROR_INVALIDDATA;
144 buf[entry] = 0;
145
146 key = (const char *)buf;
147 value = key + strlen(key) + 1;
148 if (value < (const char *)buf + entry) {
149 if (!strcmp(key, "KTXorientation"))
150 ktx_orientation_flips(value, hflip, vflip);
151 else
152 av_log(s, AV_LOG_VERBOSE, "Ignoring KTX key '%s'.\n", key);
153 }
154 if (avio_skip(pb, padded - entry) < 0)
155 return AVERROR_INVALIDDATA;
156 }
157 left -= padded;
158 }
159
160 /* Every entry occupies a padded, four byte aligned slot, so a leftover
161 * tail means the declared block size is not aligned and the image payload
162 * does not start where the header claims it does. */
163 if (left) {
164 av_log(s, AV_LOG_ERROR, "Invalid KTX key/value data.\n");
165 return AVERROR_INVALIDDATA;
166 }
167
168 return 0;
169}
170
172{
173 AVIOContext *pb = s->pb;
174 KTXDemuxerContext *ktx = s->priv_data;
175 AVStream *st;
176 uint8_t hdr[64];
177 uint32_t endian, gl_type, gl_type_size, gl_format, gl_internal;
178 uint32_t gl_base_internal, w, h, depth, array, faces, mips;
179 uint32_t kvdata, image_size;
180 uint8_t image_size_buf[4];
181 int bx = 0, by = 0;
182 int hflip = 0, vflip = 0;
183 uint8_t extra[16];
184
185 if (ffio_read_size(pb, hdr, sizeof(hdr)) < 0)
186 return AVERROR_INVALIDDATA;
187 if (memcmp(hdr, ktx_magic, sizeof(ktx_magic))) {
188 av_log(s, AV_LOG_ERROR, "Not a KTX file (bad magic).\n");
189 return AVERROR_INVALIDDATA;
190 }
191
192 endian = AV_RL32(hdr + 12);
193 gl_type = AV_RL32(hdr + 16);
194 gl_type_size = AV_RL32(hdr + 20);
195 gl_format = AV_RL32(hdr + 24);
196 gl_internal = AV_RL32(hdr + 28);
197 gl_base_internal = AV_RL32(hdr + 32);
198 w = AV_RL32(hdr + 36);
199 h = AV_RL32(hdr + 40);
200 depth = AV_RL32(hdr + 44);
201 array = AV_RL32(hdr + 48);
202 faces = AV_RL32(hdr + 52);
203 mips = AV_RL32(hdr + 56);
204 kvdata = AV_RL32(hdr + 60);
205
206 if (endian != 0x04030201) {
207 av_log(s, AV_LOG_ERROR, "KTX big-endian / unknown endianness not supported.\n");
208 return AVERROR_INVALIDDATA;
209 }
210 if (gl_type || gl_type_size != 1 || gl_format || gl_base_internal != 0x1908 ||
211 !w || !h || w > 0xFFFFFF || h > 0xFFFFFF ||
212 depth || array || faces != 1 || mips != 1) {
213 av_log(s, AV_LOG_ERROR, "Unsupported KTX texture layout.\n");
214 return AVERROR_INVALIDDATA;
215 }
216
217 if (!gl_enum_to_block(gl_internal, &bx, &by)) {
218 av_log(s, AV_LOG_ERROR, "Unsupported ASTC GL format 0x%X in KTX.\n", gl_internal);
219 return AVERROR_INVALIDDATA;
220 }
221
222 if (ktx_read_orientation(s, kvdata, &hflip, &vflip) < 0)
223 return AVERROR_INVALIDDATA;
224 if (ffio_read_size(pb, image_size_buf, sizeof(image_size_buf)) < 0)
225 return AVERROR_INVALIDDATA;
226 image_size = AV_RL32(image_size_buf);
227 if (!image_size || image_size > INT_MAX)
228 return AVERROR_INVALIDDATA;
229 /* The payload must hold exactly the blocks the texture geometry needs;
230 * anything else would either truncate the image or smuggle in trailing
231 * data that the decoder would ignore. */
232 {
233 uint64_t expected = (((uint64_t)w + bx - 1) / bx) *
234 (((uint64_t)h + by - 1) / by) * 16;
235 if (image_size != expected) {
237 "KTX image size %u does not match the %"PRIu64" bytes "
238 "required for a %ux%u texture with %dx%d blocks.\n",
239 image_size, expected, w, h, bx, by);
240 return AVERROR_INVALIDDATA;
241 }
242 }
243 ktx->image_size = image_size;
244
246 if (!st)
247 return AVERROR(ENOMEM);
248
251 st->codecpar->width = w;
252 st->codecpar->height = h;
253 /* The GL internal format fixes the colour space but not the endpoint
254 * format: an sRGB texture is LDR, while a linear one may hold LDR, HDR RGB
255 * with LDR alpha, or full HDR endpoints. Report that distinction so the
256 * decoder does not assume LDR for a linear texture; a linear texture is
257 * sampled with HDR precision by default (LINEAR_ANY), so the stream format
258 * is the matching half-float one rather than 8-bit RGBA. */
259 st->codecpar->profile = gl_internal >= 0x93D0 && gl_internal <= 0x93DD ?
263
264 /* Synthesize the .astc-style extradata for the decoder. Bytes [13-15] hold
265 * the image depth (dim_z), which is always 1 here: KTX 1.0 2D only. */
266 memset(extra, 0, sizeof(extra));
267 memcpy(extra, astc_magic, sizeof(astc_magic));
268 extra[4] = (uint8_t)bx;
269 extra[5] = (uint8_t)by;
270 extra[6] = 1;
271 AV_WL24(extra + 7, w);
272 AV_WL24(extra + 10, h);
273 extra[13] = 1;
275 return AVERROR(ENOMEM);
276 memcpy(st->codecpar->extradata, extra, ASTC_HEADER_SIZE);
277
278 /* Carry the stored row order to the caller instead of silently returning
279 * a mirrored image. */
280 if (hflip || vflip) {
283
287 9 * sizeof(*matrix), 0);
288 if (!sd)
289 return AVERROR(ENOMEM);
290 matrix = (int32_t *)sd->data;
292 av_display_matrix_flip(matrix, hflip, vflip);
294 "Applying KTXorientation (hflip %d, vflip %d).\n", hflip, vflip);
295 }
296
297 avpriv_set_pts_info(st, 64, 1, 1);
298 return 0;
299}
300
302{
303 KTXDemuxerContext *ktx = s->priv_data;
304 int ret;
305
306 /* Keep reporting a detected truncation instead of falling through to EOF:
307 * a caller that stops at the first error must not see the broken image as
308 * a clean end of stream. */
309 if (ktx->truncated)
310 return AVERROR_INVALIDDATA;
311
312 if (!ktx->image_size)
313 return AVERROR_EOF;
314
315 /* A payload that is missing entirely reads as EOF, which has to be
316 * reported as the truncation it is rather than as the end of a valid
317 * stream. */
318 ret = av_get_packet(s->pb, pkt, ktx->image_size);
319 if (ret == AVERROR_EOF)
320 ret = 0;
321 if (ret < 0)
322 return ret;
323 if (ret != ktx->image_size) {
325 "Truncated KTX image: got %d of %u bytes.\n",
326 ret, ktx->image_size);
328 ktx->truncated = 1;
329 return AVERROR_INVALIDDATA;
330 }
331
332 pkt->stream_index = 0;
333 pkt->flags |= AV_PKT_FLAG_KEY;
334 ktx->image_size = 0; /* single mip */
335 return 0;
336}
337
339 .p.name = "ktx",
340 .p.long_name = NULL_IF_CONFIG_SMALL("KTX 1.0 (Khronos Texture) for ASTC"),
341 .p.mime_type = "image/ktx",
342 .p.extensions = "ktx",
343 .p.flags = AVFMT_NOTIMESTAMPS,
344 .priv_data_size = sizeof(KTXDemuxerContext),
348};
const FFInputFormat ff_ktx_demuxer
Definition ktxdec.c:338
#define entry
static const uint8_t astc_magic[4]
Definition astcdec.c:44
int32_t
void avpriv_set_pts_info(AVStream *st, int pts_wrap_bits, unsigned int pts_num, unsigned int pts_den)
Set the time base and wrapping info for a given stream.
Definition avformat.c:834
Main libavformat public API header.
#define AVPROBE_SCORE_MAX
maximum score
Definition avformat.h:485
int av_get_packet(AVIOContext *s, AVPacket *pkt, int size)
Allocate and read the payload of a packet and initialize its fields with default values.
Definition utils.c:98
#define AVFMT_NOTIMESTAMPS
Format does not need / have any timestamps.
Definition avformat.h:500
Buffered I/O operations.
int64_t avio_skip(AVIOContext *s, int64_t offset)
Skip given number of bytes forward.
Definition aviobuf.c:321
unsigned int avio_rl32(AVIOContext *s)
Definition aviobuf.c:733
int ffio_read_size(AVIOContext *s, unsigned char *buf, int size)
Read size bytes from AVIOContext into buf.
Definition aviobuf.c:665
static int read_packet(void *opaque, uint8_t *buf, int buf_size)
static int BS_FUNC left(const BSCTX *bc)
Return the number of the bits left in a buffer.
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define s(width, name)
Definition cbs_vp9.c:198
static int read_probe(const AVProbeData *p)
Definition cdg.c:30
#define NULL
Definition coverity.c:32
Misc types and constants that do not belong anywhere else.
#define AV_PROFILE_ASTC_LDR_SRGB
sRGB LDR.
Definition defs.h:80
#define AV_PROFILE_ASTC_LINEAR_ANY
Definition defs.h:87
static AVPacket * pkt
Display matrix.
double value
Definition eval.c:102
const char * key
static int read_header(FFV1Context *f, RangeCoder *c)
Definition ffv1dec.c:578
@ AV_CODEC_ID_ASTC
Definition codec_id.h:327
AVPacketSideData * av_packet_side_data_new(AVPacketSideData **psd, int *pnb_sd, enum AVPacketSideDataType type, size_t size, int flags)
Allocate a new packet side data.
Definition packet.c:620
@ AV_PKT_DATA_DISPLAYMATRIX
This side data contains a 3x3 transformation matrix describing an affine transformation that needs to...
Definition packet.h:105
void av_packet_unref(AVPacket *pkt)
Wipe the packet.
Definition packet.c:434
#define AV_PKT_FLAG_KEY
The packet contains a keyframe.
Definition packet.h:650
AVStream * avformat_new_stream(AVFormatContext *s, const struct AVCodec *c)
Add a new stream to a media file.
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition error.h:61
#define AVERROR_EOF
End of file.
Definition error.h:57
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_VERBOSE
Detailed information.
Definition log.h:226
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
@ AVMEDIA_TYPE_VIDEO
Definition avutil.h:200
void av_display_rotation_set(int32_t matrix[9], double angle)
Initialize a transformation matrix describing a pure clockwise rotation by the specified angle (in de...
Definition display.c:50
void av_display_matrix_flip(int32_t matrix[9], int hflip, int vflip)
Flip the input matrix horizontally and/or vertically.
Definition display.c:65
#define AV_RL32(p)
#define AV_WL24(p, d)
static int ktx_read_orientation(AVFormatContext *s, uint32_t kvdata, int *hflip, int *vflip)
Definition ktxdec.c:116
static const uint8_t ktx_magic[12]
Definition ktxdec.c:47
static int ktx_read_packet(AVFormatContext *s, AVPacket *pkt)
Definition ktxdec.c:301
static const int astc_gl_linear[14]
Definition ktxdec.c:52
static int gl_enum_to_block(uint32_t e, int *bx, int *by)
Definition ktxdec.c:65
static int ktx_read_header(AVFormatContext *s)
Definition ktxdec.c:171
static const int astc_by[14]
Definition ktxdec.c:57
static void ktx_orientation_flips(const char *value, int *hflip, int *vflip)
Definition ktxdec.c:99
static int ktx_probe(const AVProbeData *p)
Definition ktxdec.c:78
static const int astc_bx[14]
Definition ktxdec.c:56
#define ASTC_HEADER_SIZE
Definition libastcdec.c:43
int ff_alloc_extradata(AVCodecParameters *par, int size)
Allocate extradata with additional AV_INPUT_BUFFER_PADDING_SIZE at end which is always set to 0.
Definition utils.c:237
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:97
uint8_t w
Definition llvidencdsp.c:39
#define AV_PIX_FMT_RGBAF16
Definition pixfmt.h:630
@ AV_PIX_FMT_RGBA
packed RGBA 8:8:8:8, 32bpp, RGBARGBA...
Definition pixfmt.h:100
int height
The height of the video frame in pixels.
Definition codec_par.h:150
int nb_coded_side_data
Amount of entries in coded_side_data.
Definition codec_par.h:88
int width
The width of the video frame in pixels.
Definition codec_par.h:143
enum AVMediaType codec_type
General type of the encoded data.
Definition codec_par.h:53
int profile
Codec-specific bitstream restrictions that the stream conforms to.
Definition codec_par.h:135
uint8_t * extradata
Extra binary data needed for initializing the decoder, codec-dependent.
Definition codec_par.h:71
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition codec_par.h:57
AVPacketSideData * coded_side_data
Additional data associated with the entire stream.
Definition codec_par.h:83
Format I/O context.
Definition avformat.h:1335
Bytestream IO Context.
Definition avio.h:160
This structure stores auxiliary information for decoding, presenting, or otherwise processing the cod...
Definition packet.h:424
uint8_t * data
Definition packet.h:425
This structure stores compressed data.
Definition packet.h:580
This structure contains the data a format has to probe a file.
Definition avformat.h:473
Stream structure.
Definition avformat.h:768
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition avformat.h:791
uint32_t image_size
Definition ktxdec.c:60
#define av_log(a,...)
static int array[MAX_W *MAX_W]
uint8_t base
Definition vp3data.h:128