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