/src/mpv/video/img_format.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * This file is part of mpv. |
3 | | * |
4 | | * mpv is free software; you can redistribute it and/or |
5 | | * modify it under the terms of the GNU Lesser General Public |
6 | | * License as published by the Free Software Foundation; either |
7 | | * version 2.1 of the License, or (at your option) any later version. |
8 | | * |
9 | | * mpv is distributed in the hope that it will be useful, |
10 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | * GNU Lesser General Public License for more details. |
13 | | * |
14 | | * You should have received a copy of the GNU Lesser General Public |
15 | | * License along with mpv. If not, see <http://www.gnu.org/licenses/>. |
16 | | */ |
17 | | |
18 | | #include <assert.h> |
19 | | #include <string.h> |
20 | | |
21 | | #include <libavcodec/avcodec.h> |
22 | | #include <libavutil/imgutils.h> |
23 | | #include <libavutil/pixfmt.h> |
24 | | #include <libavutil/pixdesc.h> |
25 | | |
26 | | #include "video/img_format.h" |
27 | | #include "video/mp_image.h" |
28 | | #include "video/fmt-conversion.h" |
29 | | |
30 | | struct mp_imgfmt_entry { |
31 | | const char *name; |
32 | | // Valid if flags!=0. |
33 | | // This can be incomplete, and missing fields are filled in: |
34 | | // - sets num_planes and bpp[], derived from comps[] (rounds to bytes) |
35 | | // - sets MP_IMGFLAG_GRAY, derived from comps[] |
36 | | // - sets MP_IMGFLAG_ALPHA, derived from comps[] |
37 | | // - sets align_x/y if 0, derived from chroma shift |
38 | | // - sets xs[]/ys[] always, derived from num_planes/chroma_shift |
39 | | // - sets MP_IMGFLAG_HAS_COMPS|MP_IMGFLAG_NE if num_planes>0 |
40 | | // - sets MP_IMGFLAG_TYPE_UINT if no other type set |
41 | | // - sets id to mp_imgfmt_list[] implied format |
42 | | struct mp_imgfmt_desc desc; |
43 | | }; |
44 | | |
45 | | #define FRINGE_GBRP(def, dname, b) \ |
46 | | [def - IMGFMT_CUST_BASE] = { \ |
47 | | .name = dname, \ |
48 | | .desc = { .flags = MP_IMGFLAG_COLOR_RGB, \ |
49 | | .comps = { {2, 0, 8, (b) - 8}, {0, 0, 8, (b) - 8}, \ |
50 | | {1, 0, 8, (b) - 8}, }, }} |
51 | | |
52 | | #define FLOAT_YUV(def, dname, xs, ys, a) \ |
53 | | [def - IMGFMT_CUST_BASE] = { \ |
54 | | .name = dname, \ |
55 | | .desc = { .flags = MP_IMGFLAG_COLOR_YUV | MP_IMGFLAG_TYPE_FLOAT, \ |
56 | | .chroma_xs = xs, .chroma_ys = ys, \ |
57 | | .comps = { {0, 0, 32}, {1, 0, 32}, {2, 0, 32}, \ |
58 | | {3 * (a), 0, 32 * (a)} }, }} |
59 | | |
60 | | static const struct mp_imgfmt_entry mp_imgfmt_list[] = { |
61 | | // not in ffmpeg |
62 | | [IMGFMT_VDPAU_OUTPUT - IMGFMT_CUST_BASE] = { |
63 | | .name = "vdpau_output", |
64 | | .desc = { |
65 | | .flags = MP_IMGFLAG_NE | MP_IMGFLAG_RGB | MP_IMGFLAG_HWACCEL, |
66 | | }, |
67 | | }, |
68 | | [IMGFMT_RGB30 - IMGFMT_CUST_BASE] = { |
69 | | .name = "rgb30", |
70 | | .desc = { |
71 | | .flags = MP_IMGFLAG_RGB, |
72 | | .comps = { {0, 20, 10}, {0, 10, 10}, {0, 0, 10} }, |
73 | | }, |
74 | | }, |
75 | | [IMGFMT_YAP8 - IMGFMT_CUST_BASE] = { |
76 | | .name = "yap8", |
77 | | .desc = { |
78 | | .flags = MP_IMGFLAG_COLOR_YUV, |
79 | | .comps = { {0, 0, 8}, {0}, {0}, {1, 0, 8} }, |
80 | | }, |
81 | | }, |
82 | | [IMGFMT_YAP16 - IMGFMT_CUST_BASE] = { |
83 | | .name = "yap16", |
84 | | .desc = { |
85 | | .flags = MP_IMGFLAG_COLOR_YUV, |
86 | | .comps = { {0, 0, 16}, {0}, {0}, {1, 0, 16} }, |
87 | | }, |
88 | | }, |
89 | | [IMGFMT_Y1 - IMGFMT_CUST_BASE] = { |
90 | | .name = "y1", |
91 | | .desc = { |
92 | | .flags = MP_IMGFLAG_COLOR_RGB, |
93 | | .comps = { {0, 0, 8, -7} }, |
94 | | }, |
95 | | }, |
96 | | [IMGFMT_YAPF - IMGFMT_CUST_BASE] = { |
97 | | .name = "grayaf32", // try to mimic ffmpeg naming convention |
98 | | .desc = { |
99 | | .flags = MP_IMGFLAG_COLOR_YUV | MP_IMGFLAG_TYPE_FLOAT, |
100 | | .comps = { {0, 0, 32}, {0}, {0}, {1, 0, 32} }, |
101 | | }, |
102 | | }, |
103 | | FLOAT_YUV(IMGFMT_444PF, "yuv444pf", 0, 0, 0), |
104 | | FLOAT_YUV(IMGFMT_444APF, "yuva444pf", 0, 0, 1), |
105 | | FLOAT_YUV(IMGFMT_420PF, "yuv420pf", 1, 1, 0), |
106 | | FLOAT_YUV(IMGFMT_420APF, "yuva420pf", 1, 1, 1), |
107 | | FLOAT_YUV(IMGFMT_422PF, "yuv422pf", 1, 0, 0), |
108 | | FLOAT_YUV(IMGFMT_422APF, "yuva422pf", 1, 0, 1), |
109 | | FLOAT_YUV(IMGFMT_440PF, "yuv440pf", 0, 1, 0), |
110 | | FLOAT_YUV(IMGFMT_440APF, "yuva440pf", 0, 1, 1), |
111 | | FLOAT_YUV(IMGFMT_410PF, "yuv410pf", 2, 2, 0), |
112 | | FLOAT_YUV(IMGFMT_410APF, "yuva410pf", 2, 2, 1), |
113 | | FLOAT_YUV(IMGFMT_411PF, "yuv411pf", 2, 0, 0), |
114 | | FLOAT_YUV(IMGFMT_411APF, "yuva411pf", 2, 0, 1), |
115 | | FRINGE_GBRP(IMGFMT_GBRP1, "gbrp1", 1), |
116 | | FRINGE_GBRP(IMGFMT_GBRP2, "gbrp2", 2), |
117 | | FRINGE_GBRP(IMGFMT_GBRP3, "gbrp3", 3), |
118 | | FRINGE_GBRP(IMGFMT_GBRP4, "gbrp4", 4), |
119 | | FRINGE_GBRP(IMGFMT_GBRP5, "gbrp5", 5), |
120 | | FRINGE_GBRP(IMGFMT_GBRP6, "gbrp6", 6), |
121 | | // in FFmpeg, but FFmpeg names have an annoying "_vld" suffix |
122 | | [IMGFMT_VIDEOTOOLBOX - IMGFMT_CUST_BASE] = { |
123 | | .name = "videotoolbox", |
124 | | }, |
125 | | [IMGFMT_VAAPI - IMGFMT_CUST_BASE] = { |
126 | | .name = "vaapi", |
127 | | }, |
128 | | }; |
129 | | |
130 | | static const struct mp_imgfmt_entry *get_mp_desc(int imgfmt) |
131 | 6.00M | { |
132 | 6.00M | if (imgfmt < IMGFMT_CUST_BASE) |
133 | 5.77M | return NULL; |
134 | 230k | int index = imgfmt - IMGFMT_CUST_BASE; |
135 | 230k | if (index >= MP_ARRAY_SIZE(mp_imgfmt_list)) |
136 | 221k | return NULL; |
137 | 9.01k | const struct mp_imgfmt_entry *e = &mp_imgfmt_list[index]; |
138 | 9.01k | return e->name ? e : NULL; |
139 | 230k | } |
140 | | |
141 | | char **mp_imgfmt_name_list(void) |
142 | 143 | { |
143 | 143 | int count = IMGFMT_END - IMGFMT_START; |
144 | 143 | char **list = talloc_zero_array(NULL, char *, count + 1); |
145 | 143 | int num = 0; |
146 | 79.7k | for (int n = IMGFMT_START; n < IMGFMT_END; n++) { |
147 | 79.6k | const char *name = mp_imgfmt_to_name(n); |
148 | 79.6k | if (strcmp(name, "unknown") != 0) |
149 | 39.8k | list[num++] = talloc_strdup(list, name); |
150 | 79.6k | } |
151 | 143 | return list; |
152 | 143 | } |
153 | | |
154 | | int mp_imgfmt_from_name(bstr name) |
155 | 16.0k | { |
156 | 16.0k | if (bstr_equals0(name, "none")) |
157 | 39 | return 0; |
158 | 434k | for (int n = 0; n < MP_ARRAY_SIZE(mp_imgfmt_list); n++) { |
159 | 420k | const struct mp_imgfmt_entry *p = &mp_imgfmt_list[n]; |
160 | 420k | if (p->name && bstr_equals0(name, p->name)) |
161 | 1.22k | return IMGFMT_CUST_BASE + n; |
162 | 420k | } |
163 | 14.7k | return pixfmt2imgfmt(av_get_pix_fmt(mp_tprintf(80, "%.*s", BSTR_P(name)))); |
164 | 16.0k | } |
165 | | |
166 | | char *mp_imgfmt_to_name_buf(char *buf, size_t buf_size, int fmt) |
167 | 513k | { |
168 | 513k | const struct mp_imgfmt_entry *p = get_mp_desc(fmt); |
169 | 513k | const char *name = p ? p->name : NULL; |
170 | 513k | if (!name) { |
171 | 510k | const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(imgfmt2pixfmt(fmt)); |
172 | 510k | if (pixdesc) |
173 | 469k | name = pixdesc->name; |
174 | 510k | } |
175 | 513k | if (!name) |
176 | 40.9k | name = "unknown"; |
177 | 513k | snprintf(buf, buf_size, "%s", name); |
178 | 513k | int len = strlen(buf); |
179 | 513k | if (len > 2 && buf[len - 2] == MP_SELECT_LE_BE('l', 'b') && buf[len - 1] == 'e') |
180 | 40.8k | buf[len - 2] = '\0'; |
181 | 513k | return buf; |
182 | 513k | } |
183 | | |
184 | | static void fill_pixdesc_layout(struct mp_imgfmt_desc *desc, |
185 | | enum AVPixelFormat fmt, |
186 | | const AVPixFmtDescriptor *pd) |
187 | 5.48M | { |
188 | 5.48M | if (pd->flags & AV_PIX_FMT_FLAG_PAL || |
189 | 5.48M | pd->flags & AV_PIX_FMT_FLAG_HWACCEL) |
190 | 322k | goto fail; |
191 | | |
192 | 5.15M | bool has_alpha = pd->flags & AV_PIX_FMT_FLAG_ALPHA; |
193 | 5.15M | if (pd->nb_components != 1 + has_alpha && |
194 | 5.15M | pd->nb_components != 3 + has_alpha) |
195 | 0 | goto fail; |
196 | | |
197 | | // Very convenient: we assume we're always on little endian, and FFmpeg |
198 | | // explicitly marks big endian formats => don't need to guess whether a |
199 | | // format is little endian, or not affected by byte order. |
200 | 5.15M | bool is_be = pd->flags & AV_PIX_FMT_FLAG_BE; |
201 | 5.15M | bool is_ne = MP_SELECT_LE_BE(false, true) == is_be; |
202 | | |
203 | | // Packed sub-sampled YUV is very... special. |
204 | 5.15M | bool is_packed_ss_yuv = pd->log2_chroma_w && !pd->log2_chroma_h && |
205 | 5.15M | pd->comp[1].plane == 0 && pd->comp[2].plane == 0 && |
206 | 5.15M | pd->nb_components == 3; |
207 | | |
208 | 5.15M | if (is_packed_ss_yuv) |
209 | 1.38k | desc->bpp[0] = pd->comp[1].step * 8; |
210 | | |
211 | | // Determine if there are any byte overlaps => relevant for determining |
212 | | // access unit for endian, since pixdesc does not expose this, and assumes |
213 | | // a weird model where you do separate memory fetches for each component. |
214 | 5.15M | bool any_shared_bytes = !!(pd->flags & AV_PIX_FMT_FLAG_BITSTREAM); |
215 | 20.5M | for (int c = 0; c < pd->nb_components; c++) { |
216 | 30.7M | for (int i = 0; i < c; i++) { |
217 | 15.3M | const AVComponentDescriptor *d1 = &pd->comp[c]; |
218 | 15.3M | const AVComponentDescriptor *d2 = &pd->comp[i]; |
219 | 15.3M | if (d1->plane == d2->plane) { |
220 | 95.0k | if (d1->offset + (d1->depth + 7) / 8u > d2->offset && |
221 | 95.0k | d2->offset + (d2->depth + 7) / 8u > d1->offset) |
222 | 5.27k | any_shared_bytes = true; |
223 | 95.0k | } |
224 | 15.3M | } |
225 | 15.3M | } |
226 | | |
227 | 5.15M | int el_bits = (pd->flags & AV_PIX_FMT_FLAG_BITSTREAM) ? 1 : 8; |
228 | 20.5M | for (int c = 0; c < pd->nb_components; c++) { |
229 | 15.3M | const AVComponentDescriptor *d = &pd->comp[c]; |
230 | 15.3M | if (d->plane >= MP_MAX_PLANES) |
231 | 0 | goto fail; |
232 | | |
233 | 15.3M | desc->num_planes = MPMAX(desc->num_planes, d->plane + 1); |
234 | | |
235 | 15.3M | int plane_bits = desc->bpp[d->plane]; |
236 | 15.3M | int c_bits = d->step * el_bits; |
237 | | |
238 | | // The first component wins, because either all components result in |
239 | | // the same value, or luma wins (luma always comes before chroma). |
240 | 15.3M | if (plane_bits) { |
241 | 62.6k | if (c_bits > plane_bits) |
242 | 22 | goto fail; // inconsistent |
243 | 15.3M | } else { |
244 | 15.3M | desc->bpp[d->plane] = plane_bits = c_bits; |
245 | 15.3M | } |
246 | | |
247 | 15.3M | int shift = d->shift; |
248 | | // What the fuck: for some inexplicable reason, MONOB uses shift=7 |
249 | | // in pixdesc, which is basically out of bounds. Pixdesc bug? |
250 | | // Make it behave like MONOW. (No, the bit-order is not different.) |
251 | 15.3M | if (fmt == AV_PIX_FMT_MONOBLACK) |
252 | 243 | shift = 0; |
253 | | |
254 | 15.3M | int offset = d->offset * el_bits; |
255 | | // The pixdesc logic for reading and endian swapping is as follows |
256 | | // (reverse engineered from av_read_image_line2()): |
257 | | // - determine a word size that will include the component fully; |
258 | | // this includes the "active" bits and the amount "shifted" away |
259 | | // (for example shift=7/depth=18 => 32 bit word reading [31:0]) |
260 | | // - the same format can use different word sizes (e.g. bgr565: the R |
261 | | // component at offset 0 is read as 8 bit; BG is read as 16 bits) |
262 | | // - if BE flag is set, swap the word before proceeding |
263 | | // - extract via shift and mask derived by depth |
264 | 15.3M | int word = mp_round_next_power_of_2(MPMAX(d->depth + shift, 8)); |
265 | | // The purpose of this is unknown. It's an absurdity fished out of |
266 | | // av_read_image_line2()'s implementation. It seems technically |
267 | | // unnecessary, and provides no information. On the other hand, it |
268 | | // compensates for seemingly bogus packed integer pixdescs; this |
269 | | // is "why" some formats use d->offset = -1. |
270 | 15.3M | if (is_be && el_bits == 8 && word == 8) |
271 | 2.65k | offset += 8; |
272 | | // Pixdesc's model sometimes requires accesses with varying word-sizes, |
273 | | // as seen in bgr565 and other formats. Also, it makes you read some |
274 | | // formats with multiple endian-dependent accesses, where accessing a |
275 | | // larger unit would make more sense. (Consider X2RGB10BE, for which |
276 | | // pixdesc wants you to perform 3 * 2 byte accesses, and swap each of |
277 | | // the read 16 bit words. What you really want is to swap the entire 4 |
278 | | // byte thing, and then extract the components with bit shifts). |
279 | | // This is complete bullshit, so we transform it into word swaps before |
280 | | // further processing. Care needs to be taken to not change formats like |
281 | | // P010 or YA16 (prefer component accesses for them; P010 isn't even |
282 | | // representable, because endian_shift is for all planes). |
283 | | // As a heuristic, assume that if any components share a byte, the whole |
284 | | // pixel is read as a single memory access and endian swapped at once. |
285 | 15.3M | int access_size = 8; |
286 | 15.3M | if (plane_bits > 8) { |
287 | 136k | if (any_shared_bytes) { |
288 | 7.35k | access_size = plane_bits; |
289 | 7.35k | if (is_be && word != access_size) { |
290 | | // Before: offset = 8*byte_offset (with word bits of data) |
291 | | // After: offset = bit_offset into swapped endian_size word |
292 | 2.71k | offset = access_size - word - offset; |
293 | 2.71k | } |
294 | 129k | } else { |
295 | 129k | access_size = word; |
296 | 129k | } |
297 | 136k | } |
298 | 15.3M | int endian_size = (access_size && !is_ne) ? access_size : 8; |
299 | 15.3M | int endian_shift = mp_log2(endian_size) - 3; |
300 | 15.3M | if (!MP_IS_POWER_OF_2(endian_size) || endian_shift < 0 || endian_shift > 3) |
301 | 0 | goto fail; |
302 | 15.3M | if (desc->endian_shift && desc->endian_shift != endian_shift) |
303 | 0 | goto fail; |
304 | 15.3M | desc->endian_shift = endian_shift; |
305 | | |
306 | | // We always use bit offsets; this doesn't lose any information, |
307 | | // and pixdesc is merely more redundant. |
308 | 15.3M | offset += shift; |
309 | 15.3M | if (offset < 0 || offset >= (1 << 6)) |
310 | 30 | goto fail; |
311 | 15.3M | if (offset + d->depth > plane_bits) |
312 | 0 | goto fail; |
313 | 15.3M | if (d->depth < 0 || d->depth >= (1 << 6)) |
314 | 0 | goto fail; |
315 | 15.3M | desc->comps[c] = (struct mp_imgfmt_comp_desc){ |
316 | 15.3M | .plane = d->plane, |
317 | 15.3M | .offset = offset, |
318 | 15.3M | .size = d->depth, |
319 | 15.3M | }; |
320 | 15.3M | } |
321 | | |
322 | 20.4M | for (int p = 0; p < desc->num_planes; p++) { |
323 | 15.3M | if (!desc->bpp[p]) |
324 | 0 | goto fail; // plane doesn't exist |
325 | 15.3M | } |
326 | | |
327 | | // What the fuck: this is probably a pixdesc bug, so fix it. |
328 | 5.15M | if (fmt == AV_PIX_FMT_RGB8) { |
329 | 209 | desc->comps[2] = (struct mp_imgfmt_comp_desc){0, 0, 2}; |
330 | 209 | desc->comps[1] = (struct mp_imgfmt_comp_desc){0, 2, 3}; |
331 | 209 | desc->comps[0] = (struct mp_imgfmt_comp_desc){0, 5, 3}; |
332 | 209 | } |
333 | | |
334 | | // Overlap test. If any shared bits are happening, this is not a format we |
335 | | // can represent (or it's something like Bayer: components in the same bits, |
336 | | // but different alternating lines). |
337 | 5.15M | bool any_shared_bits = false; |
338 | 20.5M | for (int c = 0; c < pd->nb_components; c++) { |
339 | 30.6M | for (int i = 0; i < c; i++) { |
340 | 15.3M | struct mp_imgfmt_comp_desc *c1 = &desc->comps[c]; |
341 | 15.3M | struct mp_imgfmt_comp_desc *c2 = &desc->comps[i]; |
342 | 15.3M | if (c1->plane == c2->plane) { |
343 | 94.8k | if (c1->offset + c1->size > c2->offset && |
344 | 94.8k | c2->offset + c2->size > c1->offset) |
345 | 183 | any_shared_bits = true; |
346 | 94.8k | } |
347 | 15.3M | } |
348 | 15.3M | } |
349 | | |
350 | 5.15M | if (any_shared_bits) { |
351 | 244 | for (int c = 0; c < pd->nb_components; c++) |
352 | 183 | desc->comps[c] = (struct mp_imgfmt_comp_desc){0}; |
353 | 61 | } |
354 | | |
355 | | // Many important formats have padding within an access word. For example |
356 | | // yuv420p10 has the upper 6 bit cleared to 0; P010 has the lower 6 bits |
357 | | // cleared to 0. Pixdesc cannot represent that these bits are 0. There are |
358 | | // other formats where padding is not guaranteed to be 0, but they are |
359 | | // described in the same way. |
360 | | // Apply a heuristic that is supposed to identify formats which use |
361 | | // guaranteed 0 padding. This could fail, but nobody said this pixdesc crap |
362 | | // is robust. |
363 | 20.5M | for (int c = 0; c < pd->nb_components; c++) { |
364 | 15.3M | struct mp_imgfmt_comp_desc *cd = &desc->comps[c]; |
365 | | // Note: rgb444 would defeat our heuristic if we checked only per comp. |
366 | | // also, exclude "bitstream" formats due to monow/monob |
367 | 15.3M | int fsize = MP_ALIGN_UP(cd->size, 8); |
368 | 15.3M | if (!any_shared_bytes && el_bits == 8 && fsize != cd->size && |
369 | 15.3M | fsize - cd->size <= (1 << 3)) |
370 | 34.5k | { |
371 | 34.5k | if (!(cd->offset % 8u)) { |
372 | 30.9k | cd->pad = -(fsize - cd->size); |
373 | 30.9k | cd->size = fsize; |
374 | 30.9k | } else if (!((cd->offset + cd->size) % 8u)) { |
375 | 3.54k | cd->pad = fsize - cd->size; |
376 | 3.54k | cd->size = fsize; |
377 | 3.54k | cd->offset = MP_ALIGN_DOWN(cd->offset, 8); |
378 | 3.54k | } |
379 | 34.5k | } |
380 | 15.3M | } |
381 | | |
382 | | // The alpha component always has ID 4 (index 3) in our representation, so |
383 | | // move the alpha component to there. |
384 | 5.15M | if (has_alpha && pd->nb_components < 4) { |
385 | 397 | desc->comps[3] = desc->comps[pd->nb_components - 1]; |
386 | 397 | desc->comps[pd->nb_components - 1] = (struct mp_imgfmt_comp_desc){0}; |
387 | 397 | } |
388 | | |
389 | 5.15M | if (is_packed_ss_yuv) { |
390 | 1.38k | desc->flags |= MP_IMGFLAG_PACKED_SS_YUV; |
391 | 1.38k | desc->bpp[0] /= 1 << pd->log2_chroma_w; |
392 | 5.15M | } else if (!any_shared_bits) { |
393 | 5.15M | desc->flags |= MP_IMGFLAG_HAS_COMPS; |
394 | 5.15M | } |
395 | | |
396 | 5.15M | return; |
397 | | |
398 | 322k | fail: |
399 | 1.61M | for (int n = 0; n < 4; n++) |
400 | 1.29M | desc->comps[n] = (struct mp_imgfmt_comp_desc){0}; |
401 | | // Average bit size fallback. |
402 | 322k | int num_planes = av_pix_fmt_count_planes(fmt); |
403 | 322k | desc->num_planes = MPCLAMP(num_planes, 0, MP_MAX_PLANES); |
404 | 643k | for (int p = 0; p < desc->num_planes; p++) { |
405 | 321k | int ls = av_image_get_linesize(fmt, 256, p); |
406 | 321k | desc->bpp[p] = ls > 0 ? ls * 8 / 256 : 0; |
407 | 321k | } |
408 | 322k | } |
409 | | |
410 | | static bool mp_imgfmt_get_desc_from_pixdesc(int mpfmt, struct mp_imgfmt_desc *out) |
411 | 5.48M | { |
412 | 5.48M | enum AVPixelFormat fmt = imgfmt2pixfmt(mpfmt); |
413 | 5.48M | const AVPixFmtDescriptor *pd = av_pix_fmt_desc_get(fmt); |
414 | 5.48M | if (!pd || pd->nb_components > 4) |
415 | 4.93k | return false; |
416 | | |
417 | 5.48M | struct mp_imgfmt_desc desc = { |
418 | 5.48M | .id = mpfmt, |
419 | 5.48M | .chroma_xs = pd->log2_chroma_w, |
420 | 5.48M | .chroma_ys = pd->log2_chroma_h, |
421 | 5.48M | }; |
422 | | |
423 | 5.48M | if (pd->flags & AV_PIX_FMT_FLAG_ALPHA) |
424 | 329k | desc.flags |= MP_IMGFLAG_ALPHA; |
425 | | |
426 | 5.48M | if (pd->flags & AV_PIX_FMT_FLAG_HWACCEL) |
427 | 1.87k | desc.flags |= MP_IMGFLAG_TYPE_HW; |
428 | | |
429 | | // Pixdesc does not provide a flag for XYZ, so this is the best we can do. |
430 | 5.48M | if (strncmp(pd->name, "xyz", 3) == 0) { |
431 | 130 | desc.flags |= MP_IMGFLAG_COLOR_XYZ; |
432 | 5.48M | } else if (pd->flags & AV_PIX_FMT_FLAG_RGB) { |
433 | 32.9k | desc.flags |= MP_IMGFLAG_COLOR_RGB; |
434 | 5.44M | } else if (fmt == AV_PIX_FMT_MONOBLACK || fmt == AV_PIX_FMT_MONOWHITE) { |
435 | 13.9k | desc.flags |= MP_IMGFLAG_COLOR_RGB; |
436 | 5.43M | } else if (fmt == AV_PIX_FMT_PAL8) { |
437 | 320k | desc.flags |= MP_IMGFLAG_COLOR_RGB | MP_IMGFLAG_TYPE_PAL8; |
438 | 320k | } |
439 | | |
440 | 5.48M | if (pd->flags & AV_PIX_FMT_FLAG_FLOAT) |
441 | 2.39k | desc.flags |= MP_IMGFLAG_TYPE_FLOAT; |
442 | | |
443 | | // Educated guess. |
444 | 5.48M | if (!(desc.flags & MP_IMGFLAG_COLOR_MASK) && |
445 | 5.48M | !(desc.flags & MP_IMGFLAG_TYPE_HW)) |
446 | 5.11M | desc.flags |= MP_IMGFLAG_COLOR_YUV; |
447 | | |
448 | 5.48M | desc.align_x = 1 << desc.chroma_xs; |
449 | 5.48M | desc.align_y = 1 << desc.chroma_ys; |
450 | | |
451 | 5.48M | fill_pixdesc_layout(&desc, fmt, pd); |
452 | | |
453 | 5.48M | if (desc.flags & (MP_IMGFLAG_HAS_COMPS | MP_IMGFLAG_PACKED_SS_YUV)) { |
454 | 5.15M | if (!(desc.flags & MP_IMGFLAG_TYPE_MASK)) |
455 | 5.15M | desc.flags |= MP_IMGFLAG_TYPE_UINT; |
456 | 5.15M | } |
457 | | |
458 | 5.48M | if (desc.bpp[0] % 8u && (pd->flags & AV_PIX_FMT_FLAG_BITSTREAM)) |
459 | 14.3k | desc.align_x = 8 / desc.bpp[0]; // expect power of 2 |
460 | | |
461 | | // Very heuristical. |
462 | 5.48M | bool is_ne = !desc.endian_shift; |
463 | 5.48M | bool need_endian = (desc.comps[0].size % 8u && desc.bpp[0] > 8) || |
464 | 5.48M | desc.comps[0].size > 8; |
465 | | |
466 | 5.48M | if (need_endian) { |
467 | 30.5k | bool is_le = MP_SELECT_LE_BE(is_ne, !is_ne); |
468 | 30.5k | desc.flags |= is_le ? MP_IMGFLAG_LE : MP_IMGFLAG_BE; |
469 | 5.45M | } else { |
470 | 5.45M | desc.flags |= MP_IMGFLAG_LE | MP_IMGFLAG_BE; |
471 | 5.45M | } |
472 | | |
473 | 5.48M | *out = desc; |
474 | 5.48M | return true; |
475 | 5.48M | } |
476 | | |
477 | | bool mp_imgfmt_get_packed_yuv_locations(int imgfmt, uint8_t *luma_offsets) |
478 | 90 | { |
479 | 90 | struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(imgfmt); |
480 | 90 | if (!(desc.flags & MP_IMGFLAG_PACKED_SS_YUV)) |
481 | 0 | return false; |
482 | | |
483 | 90 | mp_assert(desc.num_planes == 1); |
484 | | |
485 | | // Guess at which positions the additional luma samples are. We iterate |
486 | | // starting with the first byte, and then put a luma sample at places |
487 | | // not covered by other luma/chroma. |
488 | | // Pixdesc does not and can not provide this information. This heuristic |
489 | | // may fail in certain cases. What a load of bullshit, right? |
490 | 90 | int lsize = desc.comps[0].size; |
491 | 90 | int cur_offset = 0; |
492 | 180 | for (int lsample = 1; lsample < (1 << desc.chroma_xs); lsample++) { |
493 | 270 | while (1) { |
494 | 270 | if (cur_offset + lsize > desc.bpp[0] * desc.align_x) |
495 | 0 | return false; |
496 | 270 | bool free = true; |
497 | 630 | for (int c = 0; c < 3; c++) { |
498 | 540 | struct mp_imgfmt_comp_desc *cd = &desc.comps[c]; |
499 | 540 | if (!cd->size) |
500 | 0 | continue; |
501 | 540 | if (cd->offset + cd->size > cur_offset && |
502 | 540 | cur_offset + lsize > cd->offset) |
503 | 180 | { |
504 | 180 | free = false; |
505 | 180 | break; |
506 | 180 | } |
507 | 540 | } |
508 | 270 | if (free) |
509 | 90 | break; |
510 | 180 | cur_offset += lsize; |
511 | 180 | } |
512 | 90 | luma_offsets[lsample] = cur_offset; |
513 | 90 | cur_offset += lsize; |
514 | 90 | } |
515 | | |
516 | 90 | luma_offsets[0] = desc.comps[0].offset; |
517 | 90 | return true; |
518 | 90 | } |
519 | | |
520 | | static bool get_native_desc(int mpfmt, struct mp_imgfmt_desc *desc) |
521 | 5.49M | { |
522 | 5.49M | const struct mp_imgfmt_entry *p = get_mp_desc(mpfmt); |
523 | 5.49M | if (!p || !p->desc.flags) |
524 | 5.48M | return false; |
525 | | |
526 | 4.59k | *desc = p->desc; |
527 | | |
528 | | // Fill in some fields mp_imgfmt_entry.desc is not required to set. |
529 | | |
530 | 4.59k | desc->id = mpfmt; |
531 | | |
532 | 22.9k | for (int n = 0; n < MP_NUM_COMPONENTS; n++) { |
533 | 18.3k | struct mp_imgfmt_comp_desc *cd = &desc->comps[n]; |
534 | 18.3k | if (cd->size) |
535 | 13.1k | desc->num_planes = MPMAX(desc->num_planes, cd->plane + 1); |
536 | 18.3k | desc->bpp[cd->plane] = |
537 | 18.3k | MPMAX(desc->bpp[cd->plane], MP_ALIGN_UP(cd->offset + cd->size, 8)); |
538 | 18.3k | } |
539 | | |
540 | 4.59k | if (!desc->align_x && !desc->align_y) { |
541 | 4.59k | desc->align_x = 1 << desc->chroma_xs; |
542 | 4.59k | desc->align_y = 1 << desc->chroma_ys; |
543 | 4.59k | } |
544 | | |
545 | 4.59k | if (desc->num_planes) |
546 | 4.40k | desc->flags |= MP_IMGFLAG_HAS_COMPS | MP_IMGFLAG_NE; |
547 | | |
548 | 4.59k | if (!(desc->flags & MP_IMGFLAG_TYPE_MASK)) |
549 | 1.99k | desc->flags |= MP_IMGFLAG_TYPE_UINT; |
550 | | |
551 | 4.59k | return true; |
552 | 5.49M | } |
553 | | |
554 | | int mp_imgfmt_desc_get_num_comps(struct mp_imgfmt_desc *desc) |
555 | 5.13M | { |
556 | 5.13M | int flags = desc->flags; |
557 | 5.13M | if (!(flags & MP_IMGFLAG_COLOR_MASK)) |
558 | 0 | return 0; |
559 | 5.13M | return 3 + (flags & MP_IMGFLAG_GRAY ? -2 : 0) + !!(flags & MP_IMGFLAG_ALPHA); |
560 | 5.13M | } |
561 | | |
562 | | struct mp_imgfmt_desc mp_imgfmt_get_desc(int mpfmt) |
563 | 5.49M | { |
564 | 5.49M | struct mp_imgfmt_desc desc; |
565 | | |
566 | 5.49M | if (!get_native_desc(mpfmt, &desc) && |
567 | 5.49M | !mp_imgfmt_get_desc_from_pixdesc(mpfmt, &desc)) |
568 | 4.93k | return (struct mp_imgfmt_desc){0}; |
569 | | |
570 | 21.1M | for (int p = 0; p < desc.num_planes; p++) { |
571 | 15.6M | desc.xs[p] = (p == 1 || p == 2) ? desc.chroma_xs : 0; |
572 | 15.6M | desc.ys[p] = (p == 1 || p == 2) ? desc.chroma_ys : 0; |
573 | 15.6M | } |
574 | | |
575 | 5.48M | bool is_ba = desc.num_planes > 0; |
576 | 21.1M | for (int p = 0; p < desc.num_planes; p++) |
577 | 15.6M | is_ba = !(desc.bpp[p] % 8u); |
578 | | |
579 | 5.48M | if (is_ba) |
580 | 5.47M | desc.flags |= MP_IMGFLAG_BYTE_ALIGNED; |
581 | | |
582 | 5.48M | if (desc.flags & MP_IMGFLAG_HAS_COMPS) { |
583 | 5.16M | if (desc.comps[3].size) |
584 | 10.2k | desc.flags |= MP_IMGFLAG_ALPHA; |
585 | | |
586 | | // Assuming all colors are (CCC+[A]) or (C+[A]), the latter being gray. |
587 | 5.16M | if (!desc.comps[1].size) |
588 | 59.1k | desc.flags |= MP_IMGFLAG_GRAY; |
589 | | |
590 | 5.16M | bool bb = true; |
591 | 25.8M | for (int n = 0; n < MP_NUM_COMPONENTS; n++) { |
592 | 20.6M | if (desc.comps[n].offset % 8u || desc.comps[n].size % 8u) |
593 | 25.7k | bb = false; |
594 | 20.6M | } |
595 | 5.16M | if (bb) |
596 | 5.14M | desc.flags |= MP_IMGFLAG_BYTES; |
597 | 5.16M | } |
598 | | |
599 | 5.48M | if ((desc.flags & (MP_IMGFLAG_YUV | MP_IMGFLAG_RGB)) |
600 | 5.48M | && (desc.flags & MP_IMGFLAG_HAS_COMPS) |
601 | 5.48M | && (desc.flags & MP_IMGFLAG_BYTES) |
602 | 5.48M | && ((desc.flags & MP_IMGFLAG_TYPE_MASK) == MP_IMGFLAG_TYPE_UINT)) |
603 | 5.13M | { |
604 | 5.13M | int cnt = mp_imgfmt_desc_get_num_comps(&desc); |
605 | 5.13M | bool same_depth = true; |
606 | 20.4M | for (int p = 0; p < desc.num_planes; p++) |
607 | 15.2M | same_depth &= desc.bpp[p] == desc.bpp[0]; |
608 | 5.13M | if (same_depth && cnt == desc.num_planes) { |
609 | 5.11M | if (desc.flags & MP_IMGFLAG_YUV) { |
610 | 5.10M | desc.flags |= MP_IMGFLAG_YUV_P; |
611 | 5.10M | } else { |
612 | 7.31k | desc.flags |= MP_IMGFLAG_RGB_P; |
613 | 7.31k | } |
614 | 5.11M | } |
615 | 5.13M | if (cnt == 3 && desc.num_planes == 2 && |
616 | 5.13M | desc.bpp[1] == desc.bpp[0] * 2 && |
617 | 5.13M | (desc.flags & MP_IMGFLAG_YUV)) |
618 | 1.04k | { |
619 | | |
620 | 1.04k | desc.flags |= MP_IMGFLAG_YUV_NV; |
621 | 1.04k | } |
622 | 5.13M | } |
623 | | |
624 | 5.48M | return desc; |
625 | 5.49M | } |
626 | | |
627 | | static bool validate_regular_imgfmt(const struct mp_regular_imgfmt *fmt) |
628 | 12.4k | { |
629 | 12.4k | bool present[MP_NUM_COMPONENTS] = {0}; |
630 | 12.4k | int n_comp = 0; |
631 | | |
632 | 43.9k | for (int n = 0; n < fmt->num_planes; n++) { |
633 | 31.5k | const struct mp_regular_imgfmt_plane *plane = &fmt->planes[n]; |
634 | 31.5k | n_comp += plane->num_components; |
635 | 31.5k | if (n_comp > MP_NUM_COMPONENTS) |
636 | 0 | return false; |
637 | 31.5k | if (!plane->num_components) |
638 | 0 | return false; // no empty planes in between allowed |
639 | | |
640 | 31.5k | bool pad_only = true; |
641 | 31.5k | int chroma_luma = 0; // luma: 1, chroma: 2, both: 3 |
642 | 70.9k | for (int i = 0; i < plane->num_components; i++) { |
643 | 39.4k | int comp = plane->components[i]; |
644 | 39.4k | if (comp > MP_NUM_COMPONENTS) |
645 | 0 | return false; |
646 | 39.4k | if (comp == 0) |
647 | 720 | continue; |
648 | 38.7k | pad_only = false; |
649 | 38.7k | if (present[comp - 1]) |
650 | 0 | return false; // no duplicates |
651 | 38.7k | present[comp - 1] = true; |
652 | 38.7k | chroma_luma |= (comp == 2 || comp == 3) ? 2 : 1; |
653 | 38.7k | } |
654 | 31.5k | if (pad_only) |
655 | 0 | return false; // no planes with only padding allowed |
656 | 31.5k | if ((fmt->chroma_xs > 0 || fmt->chroma_ys > 0) && chroma_luma == 3) |
657 | 0 | return false; // separate chroma/luma planes required |
658 | 31.5k | } |
659 | | |
660 | 12.4k | if (!(present[0] || present[3]) || // at least component 1 or alpha needed |
661 | 12.4k | (present[1] && !present[0]) || // component 2 requires component 1 |
662 | 12.4k | (present[2] && !present[1])) // component 3 requires component 2 |
663 | 0 | return false; |
664 | | |
665 | 12.4k | return true; |
666 | 12.4k | } |
667 | | |
668 | | static enum pl_color_system get_forced_csp_from_flags(int flags) |
669 | 83.4k | { |
670 | 83.4k | if (flags & MP_IMGFLAG_COLOR_XYZ) |
671 | 60 | return PL_COLOR_SYSTEM_XYZ; |
672 | | |
673 | 83.3k | if (flags & MP_IMGFLAG_COLOR_RGB) |
674 | 18.1k | return PL_COLOR_SYSTEM_RGB; |
675 | | |
676 | 65.2k | return PL_COLOR_SYSTEM_UNKNOWN; |
677 | 83.3k | } |
678 | | |
679 | | enum pl_color_system mp_imgfmt_get_forced_csp(int imgfmt) |
680 | 70.9k | { |
681 | 70.9k | return get_forced_csp_from_flags(mp_imgfmt_get_desc(imgfmt).flags); |
682 | 70.9k | } |
683 | | |
684 | | static enum mp_component_type get_component_type_from_flags(int flags) |
685 | 15.1k | { |
686 | 15.1k | if (flags & MP_IMGFLAG_TYPE_UINT) |
687 | 12.8k | return MP_COMPONENT_TYPE_UINT; |
688 | | |
689 | 2.34k | if (flags & MP_IMGFLAG_TYPE_FLOAT) |
690 | 2.34k | return MP_COMPONENT_TYPE_FLOAT; |
691 | | |
692 | 0 | return MP_COMPONENT_TYPE_UNKNOWN; |
693 | 2.34k | } |
694 | | |
695 | | enum mp_component_type mp_imgfmt_get_component_type(int imgfmt) |
696 | 0 | { |
697 | 0 | return get_component_type_from_flags(mp_imgfmt_get_desc(imgfmt).flags); |
698 | 0 | } |
699 | | |
700 | | int mp_find_other_endian(int imgfmt) |
701 | 0 | { |
702 | 0 | return pixfmt2imgfmt(av_pix_fmt_swap_endianness(imgfmt2pixfmt(imgfmt))); |
703 | 0 | } |
704 | | |
705 | | bool mp_get_regular_imgfmt(struct mp_regular_imgfmt *dst, int imgfmt) |
706 | 25.7k | { |
707 | 25.7k | struct mp_regular_imgfmt res = {0}; |
708 | | |
709 | 25.7k | struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(imgfmt); |
710 | 25.7k | if (!desc.num_planes) |
711 | 5.52k | return false; |
712 | 20.2k | res.num_planes = desc.num_planes; |
713 | | |
714 | 20.2k | if (desc.endian_shift || !(desc.flags & MP_IMGFLAG_HAS_COMPS)) |
715 | 5.04k | return false; |
716 | | |
717 | 15.1k | res.component_type = get_component_type_from_flags(desc.flags); |
718 | 15.1k | if (!res.component_type) |
719 | 0 | return false; |
720 | | |
721 | 15.1k | struct mp_imgfmt_comp_desc *comp0 = &desc.comps[0]; |
722 | 15.1k | if (comp0->size < 1 || comp0->size > 64 || (comp0->size % 8u)) |
723 | 2.70k | return false; |
724 | | |
725 | 12.4k | res.component_size = comp0->size / 8u; |
726 | 12.4k | res.component_pad = comp0->pad; |
727 | | |
728 | 43.9k | for (int n = 0; n < res.num_planes; n++) { |
729 | 31.5k | if (desc.bpp[n] % comp0->size) |
730 | 0 | return false; |
731 | 31.5k | res.planes[n].num_components = desc.bpp[n] / comp0->size; |
732 | 31.5k | } |
733 | | |
734 | 62.4k | for (int n = 0; n < MP_NUM_COMPONENTS; n++) { |
735 | 49.9k | struct mp_imgfmt_comp_desc *comp = &desc.comps[n]; |
736 | 49.9k | if (!comp->size) |
737 | 11.2k | continue; |
738 | | |
739 | 38.7k | struct mp_regular_imgfmt_plane *plane = &res.planes[comp->plane]; |
740 | | |
741 | 38.7k | res.num_planes = MPMAX(res.num_planes, comp->plane + 1); |
742 | | |
743 | | // We support uniform depth only. |
744 | 38.7k | if (comp->size != comp0->size || comp->pad != comp0->pad) |
745 | 0 | return false; |
746 | | |
747 | | // Size-aligned only. |
748 | 38.7k | int pos = comp->offset / comp->size; |
749 | 38.7k | if (comp->offset != pos * comp->size || pos >= MP_NUM_COMPONENTS) |
750 | 0 | return false; |
751 | | |
752 | 38.7k | if (plane->components[pos]) |
753 | 0 | return false; |
754 | 38.7k | plane->components[pos] = n + 1; |
755 | 38.7k | } |
756 | | |
757 | 12.4k | res.chroma_xs = desc.chroma_xs; |
758 | 12.4k | res.chroma_ys = desc.chroma_ys; |
759 | | |
760 | 12.4k | res.forced_csp = get_forced_csp_from_flags(desc.flags); |
761 | | |
762 | 12.4k | if (!validate_regular_imgfmt(&res)) |
763 | 0 | return false; |
764 | | |
765 | 12.4k | *dst = res; |
766 | 12.4k | return true; |
767 | 12.4k | } |
768 | | |
769 | | static bool regular_imgfmt_equals(struct mp_regular_imgfmt *a, |
770 | | struct mp_regular_imgfmt *b) |
771 | 12.0k | { |
772 | 12.0k | if (a->component_type != b->component_type || |
773 | 12.0k | a->component_size != b->component_size || |
774 | 12.0k | a->num_planes != b->num_planes || |
775 | 12.0k | a->component_pad != b->component_pad || |
776 | 12.0k | a->forced_csp != b->forced_csp || |
777 | 12.0k | a->chroma_xs != b->chroma_xs || |
778 | 12.0k | a->chroma_ys != b->chroma_ys) |
779 | 11.8k | return false; |
780 | | |
781 | 780 | for (int n = 0; n < a->num_planes; n++) { |
782 | 600 | int num_comps = a->planes[n].num_components; |
783 | 600 | if (num_comps != b->planes[n].num_components) |
784 | 0 | return false; |
785 | 1.20k | for (int i = 0; i < num_comps; i++) { |
786 | 600 | if (a->planes[n].components[i] != b->planes[n].components[i]) |
787 | 0 | return false; |
788 | 600 | } |
789 | 600 | } |
790 | | |
791 | 180 | return true; |
792 | 180 | } |
793 | | |
794 | | // Find a format that matches this one exactly. |
795 | | int mp_find_regular_imgfmt(struct mp_regular_imgfmt *src) |
796 | 180 | { |
797 | 25.2k | for (int n = IMGFMT_START + 1; n < IMGFMT_END; n++) { |
798 | 25.2k | struct mp_regular_imgfmt f; |
799 | 25.2k | if (mp_get_regular_imgfmt(&f, n) && regular_imgfmt_equals(src, &f)) |
800 | 180 | return n; |
801 | 25.2k | } |
802 | 0 | return 0; |
803 | 180 | } |
804 | | |
805 | | // Compare the dst image formats, and return the one which can carry more data |
806 | | // (e.g. higher depth, more color components, lower chroma subsampling, etc.), |
807 | | // with respect to what is required to keep most of the src format. |
808 | | // Returns the imgfmt, or 0 on error. |
809 | | int mp_imgfmt_select_best(int dst1, int dst2, int src) |
810 | 0 | { |
811 | 0 | enum AVPixelFormat dst1pxf = imgfmt2pixfmt(dst1); |
812 | 0 | enum AVPixelFormat dst2pxf = imgfmt2pixfmt(dst2); |
813 | 0 | enum AVPixelFormat srcpxf = imgfmt2pixfmt(src); |
814 | 0 | enum AVPixelFormat dstlist[] = {dst1pxf, dst2pxf, AV_PIX_FMT_NONE}; |
815 | 0 | return pixfmt2imgfmt(avcodec_find_best_pix_fmt_of_list(dstlist, srcpxf, 1, 0)); |
816 | 0 | } |
817 | | |
818 | | // Same as mp_imgfmt_select_best(), but with a list of dst formats. |
819 | | int mp_imgfmt_select_best_list(int *dst, int num_dst, int src) |
820 | 0 | { |
821 | 0 | int best = 0; |
822 | 0 | for (int n = 0; n < num_dst; n++) |
823 | 0 | best = best ? mp_imgfmt_select_best(best, dst[n], src) : dst[n]; |
824 | 0 | return best; |
825 | 0 | } |