mirror of
https://github.com/mpv-player/mpv
synced 2024-12-28 10:02:17 +00:00
94d853d3a3
This tests the RGB repacker code in zimg, which deserves to be tested because it's tricky and there will be more formats. scale_test.c contains some code that can be used to test any scaler. Or at least that would be great; currently it can only test repacking of some byte-aligned-component RGB formats. It should be called repack_test.c, but I'm too lazy to change the filename now. The idea is that libswscale is used to cross-check the conversions performed by the zimg wrapper. This is why it's "OK" that scale_test.c does libswscale calls. scale_sws.c is the equivalent to scale_zimg.c, and is of course worthless (because it tests libswscale by comparing the results with libswscale), but still might help with finding bugs in scale_test.c. This borrows a sorted list of image formats from test/img_format.c, for the same reason that file sorts them. There's a slight possibility that this can be used to test vo_gpu.c too some times in the future.
213 lines
7.8 KiB
C
213 lines
7.8 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"
|
|
|
|
int imgfmts[IMGFMT_AVPIXFMT_END - IMGFMT_AVPIXFMT_START + 100];
|
|
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);
|
|
}
|
|
|
|
void init_imgfmts_list(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)
|
|
{
|
|
init_imgfmts_list();
|
|
|
|
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(®, 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 = ®.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,
|
|
};
|