summaryrefslogtreecommitdiffstats
path: root/video/img_format.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/img_format.c')
-rw-r--r--video/img_format.c821
1 files changed, 556 insertions, 265 deletions
diff --git a/video/img_format.c b/video/img_format.c
index 57da094d39..c9e2a3c0e6 100644
--- a/video/img_format.c
+++ b/video/img_format.c
@@ -19,31 +19,125 @@
#include <string.h>
#include <libavcodec/avcodec.h>
+#include <libavutil/imgutils.h>
#include <libavutil/pixfmt.h>
#include <libavutil/pixdesc.h>
-#include "config.h"
-
#include "video/img_format.h"
#include "video/mp_image.h"
#include "video/fmt-conversion.h"
struct mp_imgfmt_entry {
const char *name;
- int fmt;
+ // Valid if flags!=0.
+ // This can be incomplete, and missing fields are filled in:
+ // - sets num_planes and bpp[], derived from comps[] (rounds to bytes)
+ // - sets MP_IMGFLAG_GRAY, derived from comps[]
+ // - sets MP_IMGFLAG_ALPHA, derived from comps[]
+ // - sets align_x/y if 0, derived from chroma shift
+ // - sets xs[]/ys[] always, derived from num_planes/chroma_shift
+ // - sets MP_IMGFLAG_HAS_COMPS|MP_IMGFLAG_NE if num_planes>0
+ // - sets MP_IMGFLAG_TYPE_UINT if no other type set
+ // - sets id to mp_imgfmt_list[] implied format
+ struct mp_imgfmt_desc desc;
};
+#define FRINGE_GBRP(def, dname, b) \
+ [def - IMGFMT_CUST_BASE] = { \
+ .name = dname, \
+ .desc = { .flags = MP_IMGFLAG_COLOR_RGB, \
+ .comps = { {2, 0, 8, (b) - 8}, {0, 0, 8, (b) - 8}, \
+ {1, 0, 8, (b) - 8}, }, }}
+
+#define FLOAT_YUV(def, dname, xs, ys, a) \
+ [def - IMGFMT_CUST_BASE] = { \
+ .name = dname, \
+ .desc = { .flags = MP_IMGFLAG_COLOR_YUV | MP_IMGFLAG_TYPE_FLOAT, \
+ .chroma_xs = xs, .chroma_ys = ys, \
+ .comps = { {0, 0, 32}, {1, 0, 32}, {2, 0, 32}, \
+ {3 * (a), 0, 32 * (a)} }, }}
+
static const struct mp_imgfmt_entry mp_imgfmt_list[] = {
// not in ffmpeg
- {"vdpau_output", IMGFMT_VDPAU_OUTPUT},
- {"rgb30", IMGFMT_RGB30},
- // FFmpeg names have an annoying "_vld" suffix
- {"videotoolbox", IMGFMT_VIDEOTOOLBOX},
- {"vaapi", IMGFMT_VAAPI},
- {"none", 0},
- {0}
+ [IMGFMT_VDPAU_OUTPUT - IMGFMT_CUST_BASE] = {
+ .name = "vdpau_output",
+ .desc = {
+ .flags = MP_IMGFLAG_NE | MP_IMGFLAG_RGB | MP_IMGFLAG_HWACCEL,
+ },
+ },
+ [IMGFMT_RGB30 - IMGFMT_CUST_BASE] = {
+ .name = "rgb30",
+ .desc = {
+ .flags = MP_IMGFLAG_RGB,
+ .comps = { {0, 20, 10}, {0, 10, 10}, {0, 0, 10} },
+ },
+ },
+ [IMGFMT_YAP8 - IMGFMT_CUST_BASE] = {
+ .name = "yap8",
+ .desc = {
+ .flags = MP_IMGFLAG_COLOR_YUV,
+ .comps = { {0, 0, 8}, {0}, {0}, {1, 0, 8} },
+ },
+ },
+ [IMGFMT_YAP16 - IMGFMT_CUST_BASE] = {
+ .name = "yap16",
+ .desc = {
+ .flags = MP_IMGFLAG_COLOR_YUV,
+ .comps = { {0, 0, 16}, {0}, {0}, {1, 0, 16} },
+ },
+ },
+ [IMGFMT_Y1 - IMGFMT_CUST_BASE] = {
+ .name = "y1",
+ .desc = {
+ .flags = MP_IMGFLAG_COLOR_RGB,
+ .comps = { {0, 0, 8, -7} },
+ },
+ },
+ [IMGFMT_YAPF - IMGFMT_CUST_BASE] = {
+ .name = "grayaf32", // try to mimic ffmpeg naming convention
+ .desc = {
+ .flags = MP_IMGFLAG_COLOR_YUV | MP_IMGFLAG_TYPE_FLOAT,
+ .comps = { {0, 0, 32}, {0}, {0}, {1, 0, 32} },
+ },
+ },
+ FLOAT_YUV(IMGFMT_444PF, "yuv444pf", 0, 0, 0),
+ FLOAT_YUV(IMGFMT_444APF, "yuva444pf", 0, 0, 1),
+ FLOAT_YUV(IMGFMT_420PF, "yuv420pf", 1, 1, 0),
+ FLOAT_YUV(IMGFMT_420APF, "yuva420pf", 1, 1, 1),
+ FLOAT_YUV(IMGFMT_422PF, "yuv422pf", 1, 0, 0),
+ FLOAT_YUV(IMGFMT_422APF, "yuva422pf", 1, 0, 1),
+ FLOAT_YUV(IMGFMT_440PF, "yuv440pf", 0, 1, 0),
+ FLOAT_YUV(IMGFMT_440APF, "yuva440pf", 0, 1, 1),
+ FLOAT_YUV(IMGFMT_410PF, "yuv410pf", 2, 2, 0),
+ FLOAT_YUV(IMGFMT_410APF, "yuva410pf", 2, 2, 1),
+ FLOAT_YUV(IMGFMT_411PF, "yuv411pf", 2, 0, 0),
+ FLOAT_YUV(IMGFMT_411APF, "yuva411pf", 2, 0, 1),
+ FRINGE_GBRP(IMGFMT_GBRP1, "gbrp1", 1),
+ FRINGE_GBRP(IMGFMT_GBRP2, "gbrp2", 2),
+ FRINGE_GBRP(IMGFMT_GBRP3, "gbrp3", 3),
+ FRINGE_GBRP(IMGFMT_GBRP4, "gbrp4", 4),
+ FRINGE_GBRP(IMGFMT_GBRP5, "gbrp5", 5),
+ FRINGE_GBRP(IMGFMT_GBRP6, "gbrp6", 6),
+ // in FFmpeg, but FFmpeg names have an annoying "_vld" suffix
+ [IMGFMT_VIDEOTOOLBOX - IMGFMT_CUST_BASE] = {
+ .name = "videotoolbox",
+ },
+ [IMGFMT_VAAPI - IMGFMT_CUST_BASE] = {
+ .name = "vaapi",
+ },
};
+static const struct mp_imgfmt_entry *get_mp_desc(int imgfmt)
+{
+ if (imgfmt < IMGFMT_CUST_BASE)
+ return NULL;
+ int index = imgfmt - IMGFMT_CUST_BASE;
+ if (index >= MP_ARRAY_SIZE(mp_imgfmt_list))
+ return NULL;
+ const struct mp_imgfmt_entry *e = &mp_imgfmt_list[index];
+ return e->name ? e : NULL;
+}
+
char **mp_imgfmt_name_list(void)
{
int count = IMGFMT_END - IMGFMT_START;
@@ -59,31 +153,20 @@ char **mp_imgfmt_name_list(void)
int mp_imgfmt_from_name(bstr name)
{
- int img_fmt = 0;
- for (const struct mp_imgfmt_entry *p = mp_imgfmt_list; p->name; ++p) {
- if (bstr_equals0(name, p->name)) {
- img_fmt = p->fmt;
- break;
- }
+ if (bstr_equals0(name, "none"))
+ return 0;
+ for (int n = 0; n < MP_ARRAY_SIZE(mp_imgfmt_list); n++) {
+ const struct mp_imgfmt_entry *p = &mp_imgfmt_list[n];
+ if (p->name && bstr_equals0(name, p->name))
+ return IMGFMT_CUST_BASE + n;
}
- if (!img_fmt) {
- char *t = bstrdup0(NULL, name);
- img_fmt = pixfmt2imgfmt(av_get_pix_fmt(t));
- talloc_free(t);
- }
- return img_fmt;
+ return pixfmt2imgfmt(av_get_pix_fmt(mp_tprintf(80, "%.*s", BSTR_P(name))));
}
char *mp_imgfmt_to_name_buf(char *buf, size_t buf_size, int fmt)
{
- const char *name = NULL;
- const struct mp_imgfmt_entry *p = mp_imgfmt_list;
- for (; p->fmt; p++) {
- if (p->name && p->fmt == fmt) {
- name = p->name;
- break;
- }
- }
+ const struct mp_imgfmt_entry *p = get_mp_desc(fmt);
+ const char *name = p ? p->name : NULL;
if (!name) {
const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(imgfmt2pixfmt(fmt));
if (pixdesc)
@@ -98,175 +181,443 @@ char *mp_imgfmt_to_name_buf(char *buf, size_t buf_size, int fmt)
return buf;
}
-static struct mp_imgfmt_desc mp_only_imgfmt_desc(int mpfmt)
+static void fill_pixdesc_layout(struct mp_imgfmt_desc *desc,
+ enum AVPixelFormat fmt,
+ const AVPixFmtDescriptor *pd)
{
- switch (mpfmt) {
- case IMGFMT_VDPAU_OUTPUT:
- return (struct mp_imgfmt_desc) {
- .id = mpfmt,
- .avformat = AV_PIX_FMT_NONE,
- .flags = MP_IMGFLAG_BE | MP_IMGFLAG_LE | MP_IMGFLAG_RGB |
- MP_IMGFLAG_HWACCEL,
- };
- case IMGFMT_RGB30:
- return (struct mp_imgfmt_desc) {
- .id = mpfmt,
- .avformat = AV_PIX_FMT_NONE,
- .flags = MP_IMGFLAG_BYTE_ALIGNED | MP_IMGFLAG_NE | MP_IMGFLAG_RGB,
- .num_planes = 1,
- .align_x = 1,
- .align_y = 1,
- .bytes = {4},
- .bpp = {32},
- .plane_bits = 30,
- .component_bits = 10,
+ if (pd->flags & AV_PIX_FMT_FLAG_PAL ||
+ pd->flags & AV_PIX_FMT_FLAG_HWACCEL)
+ goto fail;
+
+ bool has_alpha = pd->flags & AV_PIX_FMT_FLAG_ALPHA;
+ if (pd->nb_components != 1 + has_alpha &&
+ pd->nb_components != 3 + has_alpha)
+ goto fail;
+
+ // Very convenient: we assume we're always on little endian, and FFmpeg
+ // explicitly marks big endian formats => don't need to guess whether a
+ // format is little endian, or not affected by byte order.
+ bool is_be = pd->flags & AV_PIX_FMT_FLAG_BE;
+ bool is_ne = MP_SELECT_LE_BE(false, true) == is_be;
+
+ // Packed sub-sampled YUV is very... special.
+ bool is_packed_ss_yuv = pd->log2_chroma_w && !pd->log2_chroma_h &&
+ pd->comp[1].plane == 0 && pd->comp[2].plane == 0 &&
+ pd->nb_components == 3;
+
+ if (is_packed_ss_yuv)
+ desc->bpp[0] = pd->comp[1].step * 8;
+
+ // Determine if there are any byte overlaps => relevant for determining
+ // access unit for endian, since pixdesc does not expose this, and assumes
+ // a weird model where you do separate memory fetches for each component.
+ bool any_shared_bytes = !!(pd->flags & AV_PIX_FMT_FLAG_BITSTREAM);
+ for (int c = 0; c < pd->nb_components; c++) {
+ for (int i = 0; i < c; i++) {
+ const AVComponentDescriptor *d1 = &pd->comp[c];
+ const AVComponentDescriptor *d2 = &pd->comp[i];
+ if (d1->plane == d2->plane) {
+ if (d1->offset + (d1->depth + 7) / 8u > d2->offset &&
+ d2->offset + (d2->depth + 7) / 8u > d1->offset)
+ any_shared_bytes = true;
+ }
+ }
+ }
+
+ int el_bits = (pd->flags & AV_PIX_FMT_FLAG_BITSTREAM) ? 1 : 8;
+ for (int c = 0; c < pd->nb_components; c++) {
+ const AVComponentDescriptor *d = &pd->comp[c];
+ if (d->plane >= MP_MAX_PLANES)
+ goto fail;
+
+ desc->num_planes = MPMAX(desc->num_planes, d->plane + 1);
+
+ int plane_bits = desc->bpp[d->plane];
+ int c_bits = d->step * el_bits;
+
+ // The first component wins, because either all components result in
+ // the same value, or luma wins (luma always comes before chroma).
+ if (plane_bits) {
+ if (c_bits > plane_bits)
+ goto fail; // inconsistent
+ } else {
+ desc->bpp[d->plane] = plane_bits = c_bits;
+ }
+
+ int shift = d->shift;
+ // What the fuck: for some inexplicable reason, MONOB uses shift=7
+ // in pixdesc, which is basically out of bounds. Pixdesc bug?
+ // Make it behave like MONOW. (No, the bit-order is not different.)
+ if (fmt == AV_PIX_FMT_MONOBLACK)
+ shift = 0;
+
+ int offset = d->offset * el_bits;
+ // The pixdesc logic for reading and endian swapping is as follows
+ // (reverse engineered from av_read_image_line2()):
+ // - determine a word size that will include the component fully;
+ // this includes the "active" bits and the amount "shifted" away
+ // (for example shift=7/depth=18 => 32 bit word reading [31:0])
+ // - the same format can use different word sizes (e.g. bgr565: the R
+ // component at offset 0 is read as 8 bit; BG is read as 16 bits)
+ // - if BE flag is set, swap the word before proceeding
+ // - extract via shift and mask derived by depth
+ int word = mp_round_next_power_of_2(MPMAX(d->depth + shift, 8));
+ // The purpose of this is unknown. It's an absurdity fished out of
+ // av_read_image_line2()'s implementation. It seems technically
+ // unnecessary, and provides no information. On the other hand, it
+ // compensates for seemingly bogus packed integer pixdescs; this
+ // is "why" some formats use d->offset = -1.
+ if (is_be && el_bits == 8 && word == 8)
+ offset += 8;
+ // Pixdesc's model sometimes requires accesses with varying word-sizes,
+ // as seen in bgr565 and other formats. Also, it makes you read some
+ // formats with multiple endian-dependent accesses, where accessing a
+ // larger unit would make more sense. (Consider X2RGB10BE, for which
+ // pixdesc wants you to perform 3 * 2 byte accesses, and swap each of
+ // the read 16 bit words. What you really want is to swap the entire 4
+ // byte thing, and then extract the components with bit shifts).
+ // This is complete bullshit, so we transform it into word swaps before
+ // further processing. Care needs to be taken to not change formats like
+ // P010 or YA16 (prefer component accesses for them; P010 isn't even
+ // representable, because endian_shift is for all planes).
+ // As a heuristic, assume that if any components share a byte, the whole
+ // pixel is read as a single memory access and endian swapped at once.
+ int access_size = 8;
+ if (plane_bits > 8) {
+ if (any_shared_bytes) {
+ access_size = plane_bits;
+ if (is_be && word != access_size) {
+ // Before: offset = 8*byte_offset (with word bits of data)
+ // After: offset = bit_offset into swapped endian_size word
+ offset = access_size - word - offset;
+ }
+ } else {
+ access_size = word;
+ }
+ }
+ int endian_size = (access_size && !is_ne) ? access_size : 8;
+ int endian_shift = mp_log2(endian_size) - 3;
+ if (!MP_IS_POWER_OF_2(endian_size) || endian_shift < 0 || endian_shift > 3)
+ goto fail;
+ if (desc->endian_shift && desc->endian_shift != endian_shift)
+ goto fail;
+ desc->endian_shift = endian_shift;
+
+ // We always use bit offsets; this doesn't lose any information,
+ // and pixdesc is merely more redundant.
+ offset += shift;
+ if (offset < 0 || offset >= (1 << 6))
+ goto fail;
+ if (offset + d->depth > plane_bits)
+ goto fail;
+ if (d->depth < 0 || d->depth >= (1 << 6))
+ goto fail;
+ desc->comps[c] = (struct mp_imgfmt_comp_desc){
+ .plane = d->plane,
+ .offset = offset,
+ .size = d->depth,
};
}
- return (struct mp_imgfmt_desc) {0};
+
+ for (int p = 0; p < desc->num_planes; p++) {
+ if (!desc->bpp[p])
+ goto fail; // plane doesn't exist
+ }
+
+ // What the fuck: this is probably a pixdesc bug, so fix it.
+ if (fmt == AV_PIX_FMT_RGB8) {
+ desc->comps[2] = (struct mp_imgfmt_comp_desc){0, 0, 2};
+ desc->comps[1] = (struct mp_imgfmt_comp_desc){0, 2, 3};
+ desc->comps[0] = (struct mp_imgfmt_comp_desc){0, 5, 3};
+ }
+
+ // Overlap test. If any shared bits are happening, this is not a format we
+ // can represent (or it's something like Bayer: components in the same bits,
+ // but different alternating lines).
+ bool any_shared_bits = false;
+ for (int c = 0; c < pd->nb_components; c++) {
+ for (int i = 0; i < c; i++) {
+ struct mp_imgfmt_comp_desc *c1 = &desc->comps[c];
+ struct mp_imgfmt_comp_desc *c2 = &desc->comps[i];
+ if (c1->plane == c2->plane) {
+ if (c1->offset + c1->size > c2->offset &&
+ c2->offset + c2->size > c1->offset)
+ any_shared_bits = true;
+ }
+ }
+ }
+
+ if (any_shared_bits) {
+ for (int c = 0; c < pd->nb_components; c++)
+ desc->comps[c] = (struct mp_imgfmt_comp_desc){0};
+ }
+
+ // Many important formats have padding within an access word. For example
+ // yuv420p10 has the upper 6 bit cleared to 0; P010 has the lower 6 bits
+ // cleared to 0. Pixdesc cannot represent that these bits are 0. There are
+ // other formats where padding is not guaranteed to be 0, but they are
+ // described in the same way.
+ // Apply a heuristic that is supposed to identify formats which use
+ // guaranteed 0 padding. This could fail, but nobody said this pixdesc crap
+ // is robust.
+ for (int c = 0; c < pd->nb_components; c++) {
+ struct mp_imgfmt_comp_desc *cd = &desc->comps[c];
+ // Note: rgb444 would defeat our heuristic if we checked only per comp.
+ // also, exclude "bitstream" formats due to monow/monob
+ int fsize = MP_ALIGN_UP(cd->size, 8);
+ if (!any_shared_bytes && el_bits == 8 && fsize != cd->size &&
+ fsize - cd->size <= (1 << 3))
+ {
+ if (!(cd->offset % 8u)) {
+ cd->pad = -(fsize - cd->size);
+ cd->size = fsize;
+ } else if (!((cd->offset + cd->size) % 8u)) {
+ cd->pad = fsize - cd->size;
+ cd->size = fsize;
+ cd->offset = MP_ALIGN_DOWN(cd->offset, 8);
+ }
+ }
+ }
+
+ // The alpha component always has ID 4 (index 3) in our representation, so
+ // move the alpha component to there.
+ if (has_alpha && pd->nb_components < 4) {
+ desc->comps[3] = desc->comps[pd->nb_components - 1];
+ desc->comps[pd->nb_components - 1] = (struct mp_imgfmt_comp_desc){0};
+ }
+
+ if (is_packed_ss_yuv) {
+ desc->flags |= MP_IMGFLAG_PACKED_SS_YUV;
+ desc->bpp[0] /= 1 << pd->log2_chroma_w;
+ } else if (!any_shared_bits) {
+ desc->flags |= MP_IMGFLAG_HAS_COMPS;
+ }
+
+ return;
+
+fail:
+ for (int n = 0; n < 4; n++)
+ desc->comps[n] = (struct mp_imgfmt_comp_desc){0};
+ // Average bit size fallback.
+ desc->num_planes = av_pix_fmt_count_planes(fmt);
+ for (int p = 0; p < desc->num_planes; p++) {
+ int ls = av_image_get_linesize(fmt, 256, p);
+ desc->bpp[p] = ls > 0 ? ls * 8 / 256 : 0;
+ }
}
-struct mp_imgfmt_desc mp_imgfmt_get_desc(int mpfmt)
+static bool mp_imgfmt_get_desc_from_pixdesc(int mpfmt, struct mp_imgfmt_desc *out)
{
enum AVPixelFormat fmt = imgfmt2pixfmt(mpfmt);
const AVPixFmtDescriptor *pd = av_pix_fmt_desc_get(fmt);
- if (!pd || pd->nb_components > 4 || fmt == AV_PIX_FMT_NONE ||
- fmt == AV_PIX_FMT_UYYVYY411)
- return mp_only_imgfmt_desc(mpfmt);
- enum mp_component_type is_uint =
- mp_imgfmt_get_component_type(mpfmt) == MP_COMPONENT_TYPE_UINT;
+ if (!pd || pd->nb_components > 4)
+ return false;
struct mp_imgfmt_desc desc = {
.id = mpfmt,
- .avformat = fmt,
.chroma_xs = pd->log2_chroma_w,
.chroma_ys = pd->log2_chroma_h,
};
- int planedepth[4] = {0};
- int el_size = (pd->flags & AV_PIX_FMT_FLAG_BITSTREAM) ? 1 : 8;
- bool need_endian = false; // single component is spread over >1 bytes
- int shift = -1; // shift for all components, or -1 if not uniform
- for (int c = 0; c < pd->nb_components; c++) {
- AVComponentDescriptor d = pd->comp[c];
- // multiple components per plane -> Y is definitive, ignore chroma
- if (!desc.bpp[d.plane])
- desc.bpp[d.plane] = d.step * el_size;
- planedepth[d.plane] += d.depth;
- need_endian |= (d.depth + d.shift) > 8;
- if (c == 0)
- desc.component_bits = d.depth;
- if (d.depth != desc.component_bits)
- desc.component_bits = 0;
- if (c == 0)
- shift = d.shift;
- if (shift != d.shift)
- shift = -1;
- }
+ if (pd->flags & AV_PIX_FMT_FLAG_ALPHA)
+ desc.flags |= MP_IMGFLAG_ALPHA;
- for (int p = 0; p < 4; p++) {
- if (desc.bpp[p])
- desc.num_planes++;
+ if (pd->flags & AV_PIX_FMT_FLAG_HWACCEL)
+ desc.flags |= MP_IMGFLAG_TYPE_HW;
+
+ // Pixdesc does not provide a flag for XYZ, so this is the best we can do.
+ if (strncmp(pd->name, "xyz", 3) == 0) {
+ desc.flags |= MP_IMGFLAG_COLOR_XYZ;
+ } else if (pd->flags & AV_PIX_FMT_FLAG_RGB) {
+ desc.flags |= MP_IMGFLAG_COLOR_RGB;
+ } else if (fmt == AV_PIX_FMT_MONOBLACK || fmt == AV_PIX_FMT_MONOWHITE) {
+ desc.flags |= MP_IMGFLAG_COLOR_RGB;
+ } else if (fmt == AV_PIX_FMT_PAL8) {
+ desc.flags |= MP_IMGFLAG_COLOR_RGB | MP_IMGFLAG_TYPE_PAL8;
}
- desc.plane_bits = planedepth[0];
+ if (pd->flags & AV_PIX_FMT_FLAG_FLOAT)
+ desc.flags |= MP_IMGFLAG_TYPE_FLOAT;
- // Check whether any components overlap other components (per plane).
- // We're cheating/simplifying here: we assume that this happens if a shift
- // is set - which is wrong in general (could be needed for padding, instead
- // of overlapping bits of another component - use the "< 8" test to exclude
- // "normal" formats which use this for padding, like p010).
- // Needed for rgb444le/be.
- bool component_byte_overlap = false;
- for (int c = 0; c < pd->nb_components; c++) {
- AVComponentDescriptor d = pd->comp[c];
- component_byte_overlap |= d.shift > 0 && planedepth[d.plane] > 8 &&
- desc.component_bits < 8;
+ // Educated guess.
+ if (!(desc.flags & MP_IMGFLAG_COLOR_MASK) &&
+ !(desc.flags & MP_IMGFLAG_TYPE_HW))
+ desc.flags |= MP_IMGFLAG_COLOR_YUV;
+
+ desc.align_x = 1 << desc.chroma_xs;
+ desc.align_y = 1 << desc.chroma_ys;
+
+ fill_pixdesc_layout(&desc, fmt, pd);
+
+ if (desc.flags & (MP_IMGFLAG_HAS_COMPS | MP_IMGFLAG_PACKED_SS_YUV)) {
+ if (!(desc.flags & MP_IMGFLAG_TYPE_MASK))
+ desc.flags |= MP_IMGFLAG_TYPE_UINT;
}
- // If every component sits in its own byte, or all components are within
- // a single byte, no endian-dependent access is needed. If components
- // stride bytes (like with packed 2 byte RGB formats), endian-dependent
- // access is needed.
- need_endian |= component_byte_overlap;
+ if (desc.bpp[0] % 8u && (pd->flags & AV_PIX_FMT_FLAG_BITSTREAM))
+ desc.align_x = 8 / desc.bpp[0]; // expect power of 2
- if (!need_endian) {
- desc.flags |= MP_IMGFLAG_LE | MP_IMGFLAG_BE;
+ // Very heuristical.
+ bool is_ne = !desc.endian_shift;
+ bool need_endian = (desc.comps[0].size % 8u && desc.bpp[0] > 8) ||
+ desc.comps[0].size > 8;
+
+ if (need_endian) {
+ bool is_le = MP_SELECT_LE_BE(is_ne, !is_ne);
+ desc.flags |= is_le ? MP_IMGFLAG_LE : MP_IMGFLAG_BE;
} else {
- desc.flags |= (pd->flags & AV_PIX_FMT_FLAG_BE)
- ? MP_IMGFLAG_BE : MP_IMGFLAG_LE;
+ desc.flags |= MP_IMGFLAG_LE | MP_IMGFLAG_BE;
}
- if ((pd->flags & AV_PIX_FMT_FLAG_HWACCEL)) {
- desc.flags |= MP_IMGFLAG_HWACCEL;
- } else if (fmt == AV_PIX_FMT_XYZ12LE || fmt == AV_PIX_FMT_XYZ12BE) {
- /* nothing */
- } else if (!(pd->flags & AV_PIX_FMT_FLAG_RGB) &&
- fmt != AV_PIX_FMT_MONOBLACK &&
- fmt != AV_PIX_FMT_PAL8)
- {
- desc.flags |= MP_IMGFLAG_YUV;
- } else {
- desc.flags |= MP_IMGFLAG_RGB;
+ *out = desc;
+ return true;
+}
+
+bool mp_imgfmt_get_packed_yuv_locations(int imgfmt, uint8_t *luma_offsets)
+{
+ struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(imgfmt);
+ if (!(desc.flags & MP_IMGFLAG_PACKED_SS_YUV))
+ return false;
+
+ assert(desc.num_planes == 1);
+
+ // Guess at which positions the additional luma samples are. We iterate
+ // starting with the first byte, and then put a luma sample at places
+ // not covered by other luma/chroma.
+ // Pixdesc does not and can not provide this information. This heuristic
+ // may fail in certain cases. What a load of bullshit, right?
+ int lsize = desc.comps[0].size;
+ int cur_offset = 0;
+ for (int lsample = 1; lsample < (1 << desc.chroma_xs); lsample++) {
+ while (1) {
+ if (cur_offset + lsize > desc.bpp[0] * desc.align_x)
+ return false;
+ bool free = true;
+ for (int c = 0; c < 3; c++) {
+ struct mp_imgfmt_comp_desc *cd = &desc.comps[c];
+ if (!cd->size)
+ continue;
+ if (cd->offset + cd->size > cur_offset &&
+ cur_offset + lsize > cd->offset)
+ {
+ free = false;
+ break;
+ }
+ }
+ if (free)
+ break;
+ cur_offset += lsize;
+ }
+ luma_offsets[lsample] = cur_offset;
+ cur_offset += lsize;
}
- if (pd->flags & AV_PIX_FMT_FLAG_ALPHA)
- desc.flags |= MP_IMGFLAG_ALPHA;
+ luma_offsets[0] = desc.comps[0].offset;
+ return true;
+}
- if (!(pd->flags & AV_PIX_FMT_FLAG_HWACCEL) &&
- !(pd->flags & AV_PIX_FMT_FLAG_BITSTREAM))
- {
- desc.flags |= MP_IMGFLAG_BYTE_ALIGNED;
- for (int p = 0; p < desc.num_planes; p++)
- desc.bytes[p] = desc.bpp[p] / 8;
+static bool get_native_desc(int mpfmt, struct mp_imgfmt_desc *desc)
+{
+ const struct mp_imgfmt_entry *p = get_mp_desc(mpfmt);
+ if (!p || !p->desc.flags)
+ return false;
+
+ *desc = p->desc;
+
+ // Fill in some fields mp_imgfmt_entry.desc is not required to set.
+
+ desc->id = mpfmt;
+
+ for (int n = 0; n < MP_NUM_COMPONENTS; n++) {
+ struct mp_imgfmt_comp_desc *cd = &desc->comps[n];
+ if (cd->size)
+ desc->num_planes = MPMAX(desc->num_planes, cd->plane + 1);
+ desc->bpp[cd->plane] =
+ MPMAX(desc->bpp[cd->plane], MP_ALIGN_UP(cd->offset + cd->size, 8));
}
- if (pd->flags & AV_PIX_FMT_FLAG_PAL)
- desc.flags |= MP_IMGFLAG_PAL;
+ if (!desc->align_x && !desc->align_y) {
+ desc->align_x = 1 << desc->chroma_xs;
+ desc->align_y = 1 << desc->chroma_ys;
+ }
+
+ if (desc->num_planes)
+ desc->flags |= MP_IMGFLAG_HAS_COMPS | MP_IMGFLAG_NE;
+
+ if (!(desc->flags & MP_IMGFLAG_TYPE_MASK))
+ desc->flags |= MP_IMGFLAG_TYPE_UINT;
+
+ return true;
+}
+
+int mp_imgfmt_desc_get_num_comps(struct mp_imgfmt_desc *desc)
+{
+ int flags = desc->flags;
+ if (!(flags & MP_IMGFLAG_COLOR_MASK))
+ return 0;
+ return 3 + (flags & MP_IMGFLAG_GRAY ? -2 : 0) + !!(flags & MP_IMGFLAG_ALPHA);
+}
+
+struct mp_imgfmt_desc mp_imgfmt_get_desc(int mpfmt)
+{
+ struct mp_imgfmt_desc desc;
+
+ if (!get_native_desc(mpfmt, &desc) &&
+ !mp_imgfmt_get_desc_from_pixdesc(mpfmt, &desc))
+ return (struct mp_imgfmt_desc){0};
+
+ for (int p = 0; p < desc.num_planes; p++) {
+ desc.xs[p] = (p == 1 || p == 2) ? desc.chroma_xs : 0;
+ desc.ys[p] = (p == 1 || p == 2) ? desc.chroma_ys : 0;
+ }
+
+ bool is_ba = desc.num_planes > 0;
+ for (int p = 0; p < desc.num_planes; p++)
+ is_ba = !(desc.bpp[p] % 8u);
+
+ if (is_ba)
+ desc.flags |= MP_IMGFLAG_BYTE_ALIGNED;
+
+ if (desc.flags & MP_IMGFLAG_HAS_COMPS) {
+ if (desc.comps[3].size)
+ desc.flags |= MP_IMGFLAG_ALPHA;
+
+ // Assuming all colors are (CCC+[A]) or (C+[A]), the latter being gray.
+ if (!desc.comps[1].size)
+ desc.flags |= MP_IMGFLAG_GRAY;
+
+ bool bb = true;
+ for (int n = 0; n < MP_NUM_COMPONENTS; n++) {
+ if (desc.comps[n].offset % 8u || desc.comps[n].size % 8u)
+ bb = false;
+ }
+ if (bb)
+ desc.flags |= MP_IMGFLAG_BYTES;
+ }
if ((desc.flags & (MP_IMGFLAG_YUV | MP_IMGFLAG_RGB))
- && (desc.flags & MP_IMGFLAG_BYTE_ALIGNED)
- && !(pd->flags & AV_PIX_FMT_FLAG_PAL)
- && !component_byte_overlap
- && shift >= 0 && is_uint)
+ && (desc.flags & MP_IMGFLAG_HAS_COMPS)
+ && (desc.flags & MP_IMGFLAG_BYTES)
+ && ((desc.flags & MP_IMGFLAG_TYPE_MASK) == MP_IMGFLAG_TYPE_UINT))
{
+ int cnt = mp_imgfmt_desc_get_num_comps(&desc);
bool same_depth = true;
- for (int p = 0; p < desc.num_planes; p++) {
- same_depth &= planedepth[p] == planedepth[0] &&
- desc.bpp[p] == desc.bpp[0];
- }
- if (same_depth && pd->nb_components == desc.num_planes) {
+ for (int p = 0; p < desc.num_planes; p++)
+ same_depth &= desc.bpp[p] == desc.bpp[0];
+ if (same_depth && cnt == desc.num_planes) {
if (desc.flags & MP_IMGFLAG_YUV) {
desc.flags |= MP_IMGFLAG_YUV_P;
} else {
desc.flags |= MP_IMGFLAG_RGB_P;
}
}
- if (pd->nb_components == 3 && desc.num_planes == 2 &&
- planedepth[1] == planedepth[0] * 2 &&
+ if (cnt == 3 && desc.num_planes == 2 &&
desc.bpp[1] == desc.bpp[0] * 2 &&
(desc.flags & MP_IMGFLAG_YUV))
{
desc.flags |= MP_IMGFLAG_YUV_NV;
}
- if (desc.flags & (MP_IMGFLAG_YUV_P | MP_IMGFLAG_RGB_P | MP_IMGFLAG_YUV_NV))
- desc.component_bits += shift;
- }
-
- for (int p = 0; p < desc.num_planes; p++) {
- desc.xs[p] = (p == 1 || p == 2) ? desc.chroma_xs : 0;
- desc.ys[p] = (p == 1 || p == 2) ? desc.chroma_ys : 0;
- }
-
- desc.align_x = 1 << desc.chroma_xs;
- desc.align_y = 1 << desc.chroma_ys;
-
- if ((desc.bpp[0] % 8) != 0)
- desc.align_x = 8 / desc.bpp[0]; // expect power of 2
-
- if (desc.flags & MP_IMGFLAG_HWACCEL) {
- desc.component_bits = 0;
- desc.plane_bits = 0;
}
return desc;
@@ -301,7 +652,7 @@ static bool validate_regular_imgfmt(const struct mp_regular_imgfmt *fmt)
}
if (pad_only)
return false; // no planes with only padding allowed
- if ((fmt->chroma_w > 1 || fmt->chroma_h > 1) && chroma_luma == 3)
+ if ((fmt->chroma_xs > 0 || fmt->chroma_ys > 0) && chroma_luma == 3)
return false; // separate chroma/luma planes required
}
@@ -313,140 +664,99 @@ static bool validate_regular_imgfmt(const struct mp_regular_imgfmt *fmt)
return true;
}
-enum mp_csp mp_imgfmt_get_forced_csp(int imgfmt)
+static enum pl_color_system get_forced_csp_from_flags(int flags)
{
- if (imgfmt == IMGFMT_RGB30)
- return MP_CSP_RGB;
-
- enum AVPixelFormat pixfmt = imgfmt2pixfmt(imgfmt);
- const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(pixfmt);
+ if (flags & MP_IMGFLAG_COLOR_XYZ)
+ return PL_COLOR_SYSTEM_XYZ;
- // FFmpeg does not provide a flag for XYZ, so this is the best we can do.
- if (pixdesc && strncmp(pixdesc->name, "xyz", 3) == 0)
- return MP_CSP_XYZ;
+ if (flags & MP_IMGFLAG_COLOR_RGB)
+ return PL_COLOR_SYSTEM_RGB;
- if (pixdesc && (pixdesc->flags & AV_PIX_FMT_FLAG_RGB))
- return MP_CSP_RGB;
-
- if (pixfmt == AV_PIX_FMT_PAL8 || pixfmt == AV_PIX_FMT_MONOBLACK)
- return MP_CSP_RGB;
+ return PL_COLOR_SYSTEM_UNKNOWN;
+}
- return MP_CSP_AUTO;
+enum pl_color_system mp_imgfmt_get_forced_csp(int imgfmt)
+{
+ return get_forced_csp_from_flags(mp_imgfmt_get_desc(imgfmt).flags);
}
-enum mp_component_type mp_imgfmt_get_component_type(int imgfmt)
+static enum mp_component_type get_component_type_from_flags(int flags)
{
- if (imgfmt == IMGFMT_RGB30)
+ if (flags & MP_IMGFLAG_TYPE_UINT)
return MP_COMPONENT_TYPE_UINT;
- const AVPixFmtDescriptor *pixdesc =
- av_pix_fmt_desc_get(imgfmt2pixfmt(imgfmt));
-
- if (!pixdesc || (pixdesc->flags & AV_PIX_FMT_FLAG_HWACCEL))
- return MP_COMPONENT_TYPE_UNKNOWN;
-
-#if LIBAVUTIL_VERSION_MICRO >= 100
- if (pixdesc->flags & AV_PIX_FMT_FLAG_FLOAT)
+ if (flags & MP_IMGFLAG_TYPE_FLOAT)
return MP_COMPONENT_TYPE_FLOAT;
-#endif
- return MP_COMPONENT_TYPE_UINT;
+ return MP_COMPONENT_TYPE_UNKNOWN;
+}
+
+enum mp_component_type mp_imgfmt_get_component_type(int imgfmt)
+{
+ return get_component_type_from_flags(mp_imgfmt_get_desc(imgfmt).flags);
}
-static bool is_native_endian(const AVPixFmtDescriptor *pixdesc)
+int mp_find_other_endian(int imgfmt)
{
- enum AVPixelFormat pixfmt = av_pix_fmt_desc_get_id(pixdesc);
- enum AVPixelFormat other = av_pix_fmt_swap_endianness(pixfmt);
- if (other == AV_PIX_FMT_NONE || other == pixfmt)
- return true; // no endian nonsense
- bool is_le = *(char *)&(uint32_t){1};
- return pixdesc && (is_le != !!(pixdesc->flags & AV_PIX_FMT_FLAG_BE));
+ return pixfmt2imgfmt(av_pix_fmt_swap_endianness(imgfmt2pixfmt(imgfmt)));
}
bool mp_get_regular_imgfmt(struct mp_regular_imgfmt *dst, int imgfmt)
{
struct mp_regular_imgfmt res = {0};
- const AVPixFmtDescriptor *pixdesc =
- av_pix_fmt_desc_get(imgfmt2pixfmt(imgfmt));
+ struct mp_imgfmt_desc desc = mp_imgfmt_get_desc(imgfmt);
+ if (!desc.num_planes)
+ return false;
+ res.num_planes = desc.num_planes;
- if (!pixdesc || (pixdesc->flags & AV_PIX_FMT_FLAG_BITSTREAM) ||
- (pixdesc->flags & AV_PIX_FMT_FLAG_HWACCEL) ||
- (pixdesc->flags & AV_PIX_FMT_FLAG_PAL) ||
- pixdesc->nb_components < 1 ||
- pixdesc->nb_components > MP_NUM_COMPONENTS ||
- !is_native_endian(pixdesc))
+ if (desc.endian_shift || !(desc.flags & MP_IMGFLAG_HAS_COMPS))
return false;
- res.component_type = mp_imgfmt_get_component_type(imgfmt);
+ res.component_type = get_component_type_from_flags(desc.flags);
if (!res.component_type)
return false;
- const AVComponentDescriptor *comp0 = &pixdesc->comp[0];
-
- int depth = comp0->depth + comp0->shift;
- if (depth < 1 || depth > 64)
+ struct mp_imgfmt_comp_desc *comp0 = &desc.comps[0];
+ if (comp0->size < 1 || comp0->size > 64 || (comp0->size % 8u))
return false;
- res.component_size = (depth + 7) / 8;
- for (int n = 0; n < pixdesc->nb_components; n++) {
- const AVComponentDescriptor *comp = &pixdesc->comp[n];
+ res.component_size = comp0->size / 8u;
+ res.component_pad = comp0->pad;
- if (comp->plane < 0 || comp->plane >= MP_MAX_PLANES)
+ for (int n = 0; n < res.num_planes; n++) {
+ if (desc.bpp[n] % comp0->size)
return false;
+ res.planes[n].num_components = desc.bpp[n] / comp0->size;
+ }
+
+ for (int n = 0; n < MP_NUM_COMPONENTS; n++) {
+ struct mp_imgfmt_comp_desc *comp = &desc.comps[n];
+ if (!comp->size)
+ continue;
+
+ struct mp_regular_imgfmt_plane *plane = &res.planes[comp->plane];
res.num_planes = MPMAX(res.num_planes, comp->plane + 1);
// We support uniform depth only.
- if (comp->depth != comp0->depth || comp->shift != comp0->shift)
- return false;
-
- // Uniform component size; even the padding must have same size.
- int ncomp = comp->step / res.component_size;
- if (!ncomp || ncomp * res.component_size != comp->step)
+ if (comp->size != comp0->size || comp->pad != comp0->pad)
return false;
- struct mp_regular_imgfmt_plane *plane = &res.planes[comp->plane];
-
- if (plane->num_components && plane->num_components != ncomp)
+ // Size-aligned only.
+ int pos = comp->offset / comp->size;
+ if (comp->offset != pos * comp->size || pos >= MP_NUM_COMPONENTS)
return false;
- plane->num_components = ncomp;
- int pos = comp->offset / res.component_size;
- if (pos < 0 || pos >= ncomp || ncomp > MP_NUM_COMPONENTS)
- return false;
if (plane->components[pos])
return false;
plane->components[pos] = n + 1;
}
- // Make sure alpha is always component 4.
- if (pixdesc->nb_components == 2 && (pixdesc->flags & AV_PIX_FMT_FLAG_ALPHA)) {
- for (int n = 0; n < res.num_planes; n++) {
- for (int i = 0; i < res.planes[n].num_components; i++) {
- if (res.planes[n].components[i] == 2)
- res.planes[n].components[i] = 4;
- }
- }
- }
-
- res.component_pad = comp0->depth - res.component_size * 8;
- if (comp0->shift) {
- // We support padding only on 1 side.
- if (comp0->shift + comp0->depth != res.component_size * 8)
- return false;
- res.component_pad = -res.component_pad;
- }
-
- res.chroma_w = 1 << pixdesc->log2_chroma_w;
- res.chroma_h = 1 << pixdesc->log2_chroma_h;
-
-#if LIBAVUTIL_VERSION_MICRO >= 100
- if (pixdesc->flags & AV_PIX_FMT_FLAG_BAYER)
- return false; // it's satan himself
-#endif
+ res.chroma_xs = desc.chroma_x