1
0
mirror of https://github.com/mpv-player/mpv synced 2024-12-24 15:52:25 +00:00
mpv/test/img_format.c
wm4 1edb3d061b test: add dumping of img_format metadata
This is fragile enough that it warrants getting "monitored".

This takes the commented test program code from img_format.c, makes it
output to a text file, and then compares it to a "ref" file stored in
git.

Originally, I wanted to do the comparison etc. in a shell or Python
script. But why not do it in C. So mpv calls /usr/bin/diff as a
sub-process now.

This test will start producing different output if FFmpeg adds new pixel
formats or pixel format flags, or if mpv adds new IMGFMT (either aliases
to FFmpeg formats or own formats). That is unavoidable, and requires
manual inspection of the results, and then updating the ref file.

The changes in the non-test code are to guarantee that the format ID
conversion functions only translate between valid IDs.
2019-11-08 21:22:49 +01:00

212 lines
7.9 KiB
C

#include <libavutil/frame.h>
#include <libavutil/pixdesc.h>
#include "tests.h"
#include "video/fmt-conversion.h"
#include "video/img_format.h"
#include "video/mp_image.h"
#include "video/sws_utils.h"
static int imgfmts[IMGFMT_AVPIXFMT_END - IMGFMT_AVPIXFMT_START + 100];
static int num_imgfmts;
static enum AVPixelFormat pixfmt_unsup[100];
static int num_pixfmt_unsup;
static bool imgfmts_initialized;
static int cmp_imgfmt_name(const void *a, const void *b)
{
char *name_a = mp_imgfmt_to_name(*(int *)a);
char *name_b = mp_imgfmt_to_name(*(int *)b);
return strcmp(name_a, name_b);
}
static void find_all_imgfmts(void)
{
if (imgfmts_initialized)
return;
const AVPixFmtDescriptor *avd = av_pix_fmt_desc_next(NULL);
for (; avd; avd = av_pix_fmt_desc_next(avd)) {
enum AVPixelFormat fmt = av_pix_fmt_desc_get_id(avd);
int mpfmt = pixfmt2imgfmt(fmt);
if (!mpfmt) {
assert(num_pixfmt_unsup < MP_ARRAY_SIZE(pixfmt_unsup));
pixfmt_unsup[num_pixfmt_unsup++] = fmt;
}
}
for (int fmt = IMGFMT_START; fmt <= IMGFMT_END; fmt++) {
struct mp_imgfmt_desc d = mp_imgfmt_get_desc(fmt);
enum AVPixelFormat pixfmt = imgfmt2pixfmt(fmt);
if (d.id || pixfmt != AV_PIX_FMT_NONE) {
assert(num_imgfmts < MP_ARRAY_SIZE(imgfmts)); // enlarge that array
imgfmts[num_imgfmts++] = fmt;
}
}
qsort(imgfmts, num_imgfmts, sizeof(imgfmts[0]), cmp_imgfmt_name);
imgfmts_initialized = true;
}
static const char *comp_type(enum mp_component_type type)
{
switch (type) {
case MP_COMPONENT_TYPE_UINT: return "uint";
case MP_COMPONENT_TYPE_FLOAT: return "float";
default: return "unknown";
}
}
static void run(struct test_ctx *ctx)
{
find_all_imgfmts();
FILE *f = test_open_out(ctx, "img_formats.txt");
for (int z = 0; z < num_imgfmts; z++) {
int mpfmt = imgfmts[z];
enum AVPixelFormat pixfmt = imgfmt2pixfmt(mpfmt);
const AVPixFmtDescriptor *avd = av_pix_fmt_desc_get(pixfmt);
fprintf(f, "%s: ", mp_imgfmt_to_name(mpfmt));
if (mpfmt >= IMGFMT_AVPIXFMT_START && mpfmt < IMGFMT_AVPIXFMT_END)
fprintf(f, "[GENERIC] ");
int fcsp = mp_imgfmt_get_forced_csp(mpfmt);
if (fcsp)
fprintf(f, "fcsp=%s ", m_opt_choice_str(mp_csp_names, fcsp));
fprintf(f, "ctype=%s\n", comp_type(mp_imgfmt_get_component_type(mpfmt)));
struct mp_imgfmt_desc d = mp_imgfmt_get_desc(mpfmt);
if (d.id) {
fprintf(f, " Legacy desc: ");
#define FLAG(t, c) if (d.flags & (t)) fprintf(f, "[%s]", c);
FLAG(MP_IMGFLAG_BYTE_ALIGNED, "ba")
FLAG(MP_IMGFLAG_ALPHA, "a")
FLAG(MP_IMGFLAG_YUV_P, "yuvp")
FLAG(MP_IMGFLAG_YUV_NV, "nv")
FLAG(MP_IMGFLAG_YUV, "yuv")
FLAG(MP_IMGFLAG_RGB, "rgb")
FLAG(MP_IMGFLAG_LE, "le")
FLAG(MP_IMGFLAG_BE, "be")
FLAG(MP_IMGFLAG_PAL, "pal")
FLAG(MP_IMGFLAG_HWACCEL, "hw")
fprintf(f, "\n");
fprintf(f, " planes=%d, chroma=%d:%d align=%d:%d bits=%d cbits=%d\n",
d.num_planes, d.chroma_xs, d.chroma_ys, d.align_x, d.align_y,
d.plane_bits, d.component_bits);
fprintf(f, " {");
for (int n = 0; n < MP_MAX_PLANES; n++) {
fprintf(f, "%d/%d/[%d:%d] ", d.bytes[n], d.bpp[n],
d.xs[n], d.ys[n]);
}
fprintf(f, "}\n");
} else {
fprintf(f, " [NODESC]\n");
}
if (!(d.flags & MP_IMGFLAG_HWACCEL) && pixfmt != AV_PIX_FMT_NONE) {
AVFrame *fr = av_frame_alloc();
fr->format = pixfmt;
fr->width = 128;
fr->height = 128;
int err = av_frame_get_buffer(fr, MP_IMAGE_BYTE_ALIGN);
assert(err >= 0);
struct mp_image *mpi = mp_image_alloc(mpfmt, fr->width, fr->height);
if (mpi) {
// A rather fuzzy test, which might fail even if there's no bug.
for (int n = 0; n < 4; n++) {
if (!!mpi->planes[n] != !!fr->data[n]) {
#ifdef AV_PIX_FMT_FLAG_PSEUDOPAL
if (n == 1 && (avd->flags & AV_PIX_FMT_FLAG_PSEUDOPAL))
continue;
#endif
fprintf(f, " Warning: p%d: %p %p\n", n,
mpi->planes[n], fr->data[n]);
}
if (mpi->stride[n] != fr->linesize[n]) {
fprintf(f, " Warning: p%d: %d %d\n", n,
mpi->stride[n], fr->linesize[n]);
}
}
} else {
fprintf(f, " [NOALLOC]\n");
}
talloc_free(mpi);
av_frame_free(&fr);
}
struct mp_regular_imgfmt reg;
if (mp_get_regular_imgfmt(&reg, mpfmt)) {
fprintf(f, " Regular: planes=%d compbytes=%d bitpad=%d "
"chroma=%dx%d ctype=%s\n",
reg.num_planes, reg.component_size, reg.component_pad,
reg.chroma_w, reg.chroma_h, comp_type(reg.component_type));
for (int n = 0; n < reg.num_planes; n++) {
struct mp_regular_imgfmt_plane *plane = &reg.planes[n];
fprintf(f, " %d: {", n);
for (int i = 0; i < plane->num_components; i++) {
if (i > 0)
fprintf(f, ", ");
fprintf(f, "%d", plane->components[i]);
}
fprintf(f, "}\n");
}
}
// This isn't ours, but changes likely affect us.
if (avd) {
fprintf(f, " AVD: name=%s chroma=%d:%d flags=0x%"PRIx64, avd->name,
avd->log2_chroma_w, avd->log2_chroma_h, avd->flags);
#define FLAGAV(t, c) if (avd->flags & (t)) \
{fprintf(f, "%s[%s]", pre, c); pre = ""; }
char *pre = " ";
FLAGAV(AV_PIX_FMT_FLAG_BE, "be")
FLAGAV(AV_PIX_FMT_FLAG_PAL, "pal")
FLAGAV(AV_PIX_FMT_FLAG_BITSTREAM, "bs")
FLAGAV(AV_PIX_FMT_FLAG_HWACCEL, "hw")
FLAGAV(AV_PIX_FMT_FLAG_PLANAR, "planar")
FLAGAV(AV_PIX_FMT_FLAG_RGB, "rgb")
FLAGAV(AV_PIX_FMT_FLAG_ALPHA, "alpha")
FLAGAV(AV_PIX_FMT_FLAG_BAYER, "bayer")
FLAGAV(AV_PIX_FMT_FLAG_FLOAT, "float")
fprintf(f, "\n");
for (int n = 0; n < avd->nb_components; n++) {
const AVComponentDescriptor *cd = &avd->comp[n];
fprintf(f, " %d: p=%-2d st=%-2d o=%-2d sh=%-2d d=%d\n",
n, cd->plane, cd->step, cd->offset, cd->shift, cd->depth);
}
for (int n = avd->nb_components; n < 4; n++) {
const AVComponentDescriptor *cd = &avd->comp[n];
assert(!cd->plane && !cd->step && !cd->offset && !cd->shift &&
!cd->depth);
}
}
const AVPixFmtDescriptor *avd2 = av_pix_fmt_desc_next(NULL);
for (; avd2; avd2 = av_pix_fmt_desc_next(avd2)) {
enum AVPixelFormat pixfmt2 = av_pix_fmt_desc_get_id(avd2);
int mpfmt2 = pixfmt2imgfmt(pixfmt2);
if (mpfmt2 == mpfmt && pixfmt2 != pixfmt)
fprintf(f, " Ambiguous alias: %s\n", avd2->name);
}
}
for (int z = 0; z < num_pixfmt_unsup; z++) {
const AVPixFmtDescriptor *avd = av_pix_fmt_desc_get(pixfmt_unsup[z]);
fprintf(f, "Unsupported: %s\n", avd->name);
}
fclose(f);
assert_text_files_equal(ctx, "img_formats.txt", "img_formats.txt",
"This can fail if FFmpeg adds new formats or flags.");
}
const struct unittest test_img_format = {
.name = "img_format",
.run = run,
};