ffmpeg/libavutil/tests/pixfmt_best.c

234 lines
8.7 KiB
C
Raw Normal View History

/*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "libavutil/pixdesc.c"
static const enum AVPixelFormat pixfmt_list[] = {
AV_PIX_FMT_MONOWHITE,
AV_PIX_FMT_GRAY8,
AV_PIX_FMT_GRAY10,
AV_PIX_FMT_GRAY16,
AV_PIX_FMT_YUV420P,
AV_PIX_FMT_YUV420P10,
AV_PIX_FMT_YUV420P16,
AV_PIX_FMT_YUV422P,
AV_PIX_FMT_YUV422P10,
AV_PIX_FMT_YUV422P16,
AV_PIX_FMT_YUV444P,
AV_PIX_FMT_YUV444P10,
AV_PIX_FMT_YUV444P16,
AV_PIX_FMT_RGB565,
AV_PIX_FMT_RGB24,
AV_PIX_FMT_RGB48,
AV_PIX_FMT_VDPAU,
AV_PIX_FMT_VAAPI,
};
lavu/pixdesc: favour formats where depth and subsampling exactly match Since introducing the various packed formats used by VAAPI (and p012), we've noticed that there's actually a gap in how av_find_best_pix_fmt_of_2 works. It doesn't actually assign any value to having the same bit depth as the source format, when comparing against formats with a higher bit depth. This usually doesn't matter, because av_get_padded_bits_per_pixel() will account for it. However, as many of these formats use padding internally, we find that av_get_padded_bits_per_pixel() actually returns the same value for the 10 bit, 12 bit, 16 bit flavours, etc. In these tied situations, we end up just picking the first of the two provided formats, even if the second one should be preferred because it matches the actual bit depth. This bug already existed if you tried to compare yuv420p10 against p016 and p010, for example, but it simply hadn't come up before so we never noticed. But now, we actually got a situation in the VAAPI VP9 decoder where it offers both p010 and p012 because Profile 3 could be either depth and ends up picking p012 for 10 bit content due to the ordering of the testing. In addition, in the process of testing the fix, I realised we have the same gap when it comes to chroma subsampling - we do not favour a format that has exactly the same subsampling vs one with less subsampling when all else is equal. To fix this, I'm introducing a small score penalty if the bit depth or subsampling doesn't exactly match the source format. This will break the tie in favour of the format with the exact match, but not offset any of the other scoring penalties we already have. I have added a set of tests around these formats which will fail without this fix.
2022-09-08 04:03:15 +00:00
static const enum AVPixelFormat semiplanar_list[] = {
AV_PIX_FMT_P016,
AV_PIX_FMT_P012,
AV_PIX_FMT_P010,
AV_PIX_FMT_NV12,
};
static const enum AVPixelFormat packed_list[] = {
AV_PIX_FMT_XV36,
AV_PIX_FMT_XV30,
AV_PIX_FMT_VUYX,
AV_PIX_FMT_Y212,
AV_PIX_FMT_Y210,
AV_PIX_FMT_YUYV422,
};
static const enum AVPixelFormat subsampled_list[] = {
AV_PIX_FMT_YUV411P,
AV_PIX_FMT_YUV420P,
AV_PIX_FMT_YUV422P,
AV_PIX_FMT_YUV444P,
};
static const enum AVPixelFormat depthchroma_list[] = {
AV_PIX_FMT_YUV420P14,
AV_PIX_FMT_YUV422P14,
AV_PIX_FMT_YUV444P16,
};
typedef enum AVPixelFormat (*find_best_t)(enum AVPixelFormat pixfmt);
#define find_best_wrapper(name, list) \
static enum AVPixelFormat find_best_ ## name (enum AVPixelFormat pixfmt) \
{ \
enum AVPixelFormat best = AV_PIX_FMT_NONE; \
int i; \
for (i = 0; i < FF_ARRAY_ELEMS(list); i++) \
best = av_find_best_pix_fmt_of_2(best, list[i], \
pixfmt, 0, NULL); \
return best; \
}
find_best_wrapper(base, pixfmt_list)
find_best_wrapper(seminplanar, semiplanar_list)
find_best_wrapper(packed, packed_list)
find_best_wrapper(subsampled, subsampled_list)
find_best_wrapper(depthchroma, depthchroma_list)
static void test(enum AVPixelFormat input, enum AVPixelFormat expected,
int *pass, int *fail, find_best_t find_best_fn)
{
lavu/pixdesc: favour formats where depth and subsampling exactly match Since introducing the various packed formats used by VAAPI (and p012), we've noticed that there's actually a gap in how av_find_best_pix_fmt_of_2 works. It doesn't actually assign any value to having the same bit depth as the source format, when comparing against formats with a higher bit depth. This usually doesn't matter, because av_get_padded_bits_per_pixel() will account for it. However, as many of these formats use padding internally, we find that av_get_padded_bits_per_pixel() actually returns the same value for the 10 bit, 12 bit, 16 bit flavours, etc. In these tied situations, we end up just picking the first of the two provided formats, even if the second one should be preferred because it matches the actual bit depth. This bug already existed if you tried to compare yuv420p10 against p016 and p010, for example, but it simply hadn't come up before so we never noticed. But now, we actually got a situation in the VAAPI VP9 decoder where it offers both p010 and p012 because Profile 3 could be either depth and ends up picking p012 for 10 bit content due to the ordering of the testing. In addition, in the process of testing the fix, I realised we have the same gap when it comes to chroma subsampling - we do not favour a format that has exactly the same subsampling vs one with less subsampling when all else is equal. To fix this, I'm introducing a small score penalty if the bit depth or subsampling doesn't exactly match the source format. This will break the tie in favour of the format with the exact match, but not offset any of the other scoring penalties we already have. I have added a set of tests around these formats which will fail without this fix.
2022-09-08 04:03:15 +00:00
enum AVPixelFormat output = find_best_fn(input);
if (output != expected) {
printf("Matching %s: got %s, expected %s\n",
av_get_pix_fmt_name(input),
av_get_pix_fmt_name(output),
av_get_pix_fmt_name(expected));
++(*fail);
} else
++(*pass);
}
int main(void)
{
int i, pass = 0, fail = 0;
lavu/pixdesc: favour formats where depth and subsampling exactly match Since introducing the various packed formats used by VAAPI (and p012), we've noticed that there's actually a gap in how av_find_best_pix_fmt_of_2 works. It doesn't actually assign any value to having the same bit depth as the source format, when comparing against formats with a higher bit depth. This usually doesn't matter, because av_get_padded_bits_per_pixel() will account for it. However, as many of these formats use padding internally, we find that av_get_padded_bits_per_pixel() actually returns the same value for the 10 bit, 12 bit, 16 bit flavours, etc. In these tied situations, we end up just picking the first of the two provided formats, even if the second one should be preferred because it matches the actual bit depth. This bug already existed if you tried to compare yuv420p10 against p016 and p010, for example, but it simply hadn't come up before so we never noticed. But now, we actually got a situation in the VAAPI VP9 decoder where it offers both p010 and p012 because Profile 3 could be either depth and ends up picking p012 for 10 bit content due to the ordering of the testing. In addition, in the process of testing the fix, I realised we have the same gap when it comes to chroma subsampling - we do not favour a format that has exactly the same subsampling vs one with less subsampling when all else is equal. To fix this, I'm introducing a small score penalty if the bit depth or subsampling doesn't exactly match the source format. This will break the tie in favour of the format with the exact match, but not offset any of the other scoring penalties we already have. I have added a set of tests around these formats which will fail without this fix.
2022-09-08 04:03:15 +00:00
#define TEST(input, expected) \
test(input, expected, &pass, &fail, find_best_base)
// Same formats.
for (i = 0; i < FF_ARRAY_ELEMS(pixfmt_list); i++)
TEST(pixfmt_list[i], pixfmt_list[i]);
// Formats containing the same data in different layouts.
TEST(AV_PIX_FMT_MONOBLACK, AV_PIX_FMT_MONOWHITE);
TEST(AV_PIX_FMT_NV12, AV_PIX_FMT_YUV420P);
TEST(AV_PIX_FMT_P010, AV_PIX_FMT_YUV420P10);
TEST(AV_PIX_FMT_P016, AV_PIX_FMT_YUV420P16);
TEST(AV_PIX_FMT_NV16, AV_PIX_FMT_YUV422P);
TEST(AV_PIX_FMT_NV24, AV_PIX_FMT_YUV444P);
TEST(AV_PIX_FMT_YUYV422, AV_PIX_FMT_YUV422P);
TEST(AV_PIX_FMT_UYVY422, AV_PIX_FMT_YUV422P);
TEST(AV_PIX_FMT_BGR565, AV_PIX_FMT_RGB565);
TEST(AV_PIX_FMT_BGR24, AV_PIX_FMT_RGB24);
TEST(AV_PIX_FMT_GBRP, AV_PIX_FMT_RGB24);
TEST(AV_PIX_FMT_0RGB, AV_PIX_FMT_RGB24);
TEST(AV_PIX_FMT_GBRP16, AV_PIX_FMT_RGB48);
TEST(AV_PIX_FMT_VUYX, AV_PIX_FMT_YUV444P);
// Formats additionally containing alpha (here ignored).
TEST(AV_PIX_FMT_YA8, AV_PIX_FMT_GRAY8);
TEST(AV_PIX_FMT_YA16, AV_PIX_FMT_GRAY16);
TEST(AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV420P);
TEST(AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUV422P);
TEST(AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUV444P);
TEST(AV_PIX_FMT_VUYA, AV_PIX_FMT_YUV444P);
TEST(AV_PIX_FMT_AYUV64, AV_PIX_FMT_YUV444P16);
TEST(AV_PIX_FMT_RGBA, AV_PIX_FMT_RGB24);
TEST(AV_PIX_FMT_ABGR, AV_PIX_FMT_RGB24);
TEST(AV_PIX_FMT_GBRAP, AV_PIX_FMT_RGB24);
TEST(AV_PIX_FMT_RGBA64, AV_PIX_FMT_RGB48);
TEST(AV_PIX_FMT_BGRA64, AV_PIX_FMT_RGB48);
TEST(AV_PIX_FMT_GBRAP16, AV_PIX_FMT_RGB48);
// Formats requiring upsampling to represent exactly.
TEST(AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY16);
TEST(AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV420P);
TEST(AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV422P);
TEST(AV_PIX_FMT_UYYVYY411, AV_PIX_FMT_YUV422P);
TEST(AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P);
TEST(AV_PIX_FMT_YUV440P10, AV_PIX_FMT_YUV444P10);
TEST(AV_PIX_FMT_YUV440P12, AV_PIX_FMT_YUV444P16);
TEST(AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV420P10);
TEST(AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV420P16);
TEST(AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV444P10);
TEST(AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV444P16);
TEST(AV_PIX_FMT_BGR4, AV_PIX_FMT_RGB565);
TEST(AV_PIX_FMT_RGB444, AV_PIX_FMT_RGB565);
TEST(AV_PIX_FMT_RGB555, AV_PIX_FMT_RGB565);
TEST(AV_PIX_FMT_GBRP10, AV_PIX_FMT_RGB48);
TEST(AV_PIX_FMT_GBRAP10, AV_PIX_FMT_RGB48);
TEST(AV_PIX_FMT_GBRAP12, AV_PIX_FMT_RGB48);
// Formats containing the same data in different endianness.
TEST(AV_PIX_FMT_GRAY10BE, AV_PIX_FMT_GRAY10);
TEST(AV_PIX_FMT_GRAY10LE, AV_PIX_FMT_GRAY10);
TEST(AV_PIX_FMT_GRAY16BE, AV_PIX_FMT_GRAY16);
TEST(AV_PIX_FMT_GRAY16LE, AV_PIX_FMT_GRAY16);
TEST(AV_PIX_FMT_YUV422P10BE, AV_PIX_FMT_YUV422P10);
TEST(AV_PIX_FMT_YUV422P10LE, AV_PIX_FMT_YUV422P10);
TEST(AV_PIX_FMT_YUV444P16BE, AV_PIX_FMT_YUV444P16);
TEST(AV_PIX_FMT_YUV444P16LE, AV_PIX_FMT_YUV444P16);
TEST(AV_PIX_FMT_RGB565BE, AV_PIX_FMT_RGB565);
TEST(AV_PIX_FMT_RGB565LE, AV_PIX_FMT_RGB565);
TEST(AV_PIX_FMT_RGB48BE, AV_PIX_FMT_RGB48);
TEST(AV_PIX_FMT_RGB48LE, AV_PIX_FMT_RGB48);
// Opaque formats are least unlike each other.
TEST(AV_PIX_FMT_DXVA2_VLD, AV_PIX_FMT_VDPAU);
lavu/pixdesc: favour formats where depth and subsampling exactly match Since introducing the various packed formats used by VAAPI (and p012), we've noticed that there's actually a gap in how av_find_best_pix_fmt_of_2 works. It doesn't actually assign any value to having the same bit depth as the source format, when comparing against formats with a higher bit depth. This usually doesn't matter, because av_get_padded_bits_per_pixel() will account for it. However, as many of these formats use padding internally, we find that av_get_padded_bits_per_pixel() actually returns the same value for the 10 bit, 12 bit, 16 bit flavours, etc. In these tied situations, we end up just picking the first of the two provided formats, even if the second one should be preferred because it matches the actual bit depth. This bug already existed if you tried to compare yuv420p10 against p016 and p010, for example, but it simply hadn't come up before so we never noticed. But now, we actually got a situation in the VAAPI VP9 decoder where it offers both p010 and p012 because Profile 3 could be either depth and ends up picking p012 for 10 bit content due to the ordering of the testing. In addition, in the process of testing the fix, I realised we have the same gap when it comes to chroma subsampling - we do not favour a format that has exactly the same subsampling vs one with less subsampling when all else is equal. To fix this, I'm introducing a small score penalty if the bit depth or subsampling doesn't exactly match the source format. This will break the tie in favour of the format with the exact match, but not offset any of the other scoring penalties we already have. I have added a set of tests around these formats which will fail without this fix.
2022-09-08 04:03:15 +00:00
#define TEST_SEMIPLANAR(input, expected) \
test(input, expected, &pass, &fail, find_best_seminplanar)
// Same formats.
for (i = 0; i < FF_ARRAY_ELEMS(semiplanar_list); i++)
TEST_SEMIPLANAR(semiplanar_list[i], semiplanar_list[i]);
// Formats containing the same data in different layouts.
TEST_SEMIPLANAR(AV_PIX_FMT_YUV420P, AV_PIX_FMT_NV12);
TEST_SEMIPLANAR(AV_PIX_FMT_YUV420P10, AV_PIX_FMT_P010);
TEST_SEMIPLANAR(AV_PIX_FMT_YUV420P12, AV_PIX_FMT_P012);
TEST_SEMIPLANAR(AV_PIX_FMT_YUV420P16, AV_PIX_FMT_P016);
TEST_SEMIPLANAR(AV_PIX_FMT_YUV420P9, AV_PIX_FMT_P010);
#define TEST_PACKED(input, expected) \
test(input, expected, &pass, &fail, find_best_packed)
// Same formats.
for (i = 0; i < FF_ARRAY_ELEMS(packed_list); i++)
TEST_PACKED(packed_list[i], packed_list[i]);
// Formats containing the same data in different layouts.
TEST_PACKED(AV_PIX_FMT_YUV444P, AV_PIX_FMT_VUYX);
TEST_PACKED(AV_PIX_FMT_YUV444P10, AV_PIX_FMT_XV30);
TEST_PACKED(AV_PIX_FMT_YUV444P12, AV_PIX_FMT_XV36);
TEST_PACKED(AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUYV422);
TEST_PACKED(AV_PIX_FMT_YUV422P10, AV_PIX_FMT_Y210);
TEST_PACKED(AV_PIX_FMT_YUV422P12, AV_PIX_FMT_Y212);
#define TEST_SUBSAMPLED(input, expected) \
test(input, expected, &pass, &fail, find_best_subsampled)
// Same formats.
for (i = 0; i < FF_ARRAY_ELEMS(subsampled_list); i++)
TEST_SUBSAMPLED(subsampled_list[i], subsampled_list[i]);
TEST_SUBSAMPLED(AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV420P);
#define TEST_DEPTH_CHROMA(input, expected) \
test(input, expected, &pass, &fail, find_best_depthchroma)
// Same formats.
for (i = 0; i < FF_ARRAY_ELEMS(depthchroma_list); i++)
TEST_DEPTH_CHROMA(depthchroma_list[i], depthchroma_list[i]);
TEST_DEPTH_CHROMA(AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV444P16);
TEST_DEPTH_CHROMA(AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16);
printf("%d tests passed, %d tests failed.\n", pass, fail);
return !!fail;
}