* [FFmpeg-devel] [PATCH] v3: lavu/pixdesc: favour formats where depth and subsampling exactly match
@ 2022-09-14 23:20 Philip Langdale
2022-09-15 21:25 ` Michael Niedermayer
0 siblings, 1 reply; 3+ messages in thread
From: Philip Langdale @ 2022-09-14 23:20 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Philip Langdale
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.
v2: Rework penalty system to scale penalty based on how different the
two formats are, and add new loss categories for them.
v3: Remove leftover bits of v1.
Remove bit depth penalty scaling to avoid the value being too large
in extreme cases (1 bit vs 16 bit).
Signed-off-by: Philip Langdale <philipl@overt.org>
---
libavutil/pixdesc.c | 31 +++++++++-
libavutil/pixdesc.h | 15 +++--
libavutil/tests/pixfmt_best.c | 111 ++++++++++++++++++++++++++++------
tests/ref/fate/pixfmt_best | 2 +-
4 files changed, 132 insertions(+), 27 deletions(-)
diff --git a/libavutil/pixdesc.c b/libavutil/pixdesc.c
index d7c6ebfdc4..6377224c64 100644
--- a/libavutil/pixdesc.c
+++ b/libavutil/pixdesc.c
@@ -3013,9 +3013,16 @@ static int get_pix_fmt_score(enum AVPixelFormat dst_pix_fmt,
for (i = 0; i < nb_components; i++) {
int depth_minus1 = (dst_pix_fmt == AV_PIX_FMT_PAL8) ? 7/nb_components : (dst_desc->comp[i].depth - 1);
- if (src_desc->comp[i].depth - 1 > depth_minus1 && (consider & FF_LOSS_DEPTH)) {
+ int depth_delta = src_desc->comp[i].depth - 1 - depth_minus1;
+ if (depth_delta > 0 && (consider & FF_LOSS_DEPTH)) {
loss |= FF_LOSS_DEPTH;
score -= 65536 >> depth_minus1;
+ } else if (depth_delta < 0 && (consider & FF_LOSS_EXCESS_DEPTH)) {
+ // Favour formats where bit depth exactly matches. If all other
+ // scoring is equal, we'd rather use the bit depth that most closely
+ // matches the source.
+ loss |= FF_LOSS_EXCESS_DEPTH;
+ score += depth_delta;
}
}
@@ -3035,6 +3042,28 @@ static int get_pix_fmt_score(enum AVPixelFormat dst_pix_fmt,
}
}
+ if (consider & FF_LOSS_EXCESS_RESOLUTION) {
+ // Favour formats where chroma subsampling exactly matches. If all other
+ // scoring is equal, we'd rather use the subsampling that most closely
+ // matches the source.
+ if (dst_desc->log2_chroma_w < src_desc->log2_chroma_w) {
+ loss |= FF_LOSS_EXCESS_RESOLUTION;
+ score -= 32 << (src_desc->log2_chroma_w - dst_desc->log2_chroma_w);
+ }
+
+ if (dst_desc->log2_chroma_h < src_desc->log2_chroma_h) {
+ loss |= FF_LOSS_EXCESS_RESOLUTION;
+ score -= 32 << (src_desc->log2_chroma_h - dst_desc->log2_chroma_h);
+ }
+
+ // don't favour 411 over 420, because 420 has much better support on the
+ // decoder side.
+ if (dst_desc->log2_chroma_w == 1 && src_desc->log2_chroma_w == 2 &&
+ dst_desc->log2_chroma_h == 1 && src_desc->log2_chroma_h == 2) {
+ score += 128;
+ }
+ }
+
if(consider & FF_LOSS_COLORSPACE)
switch(dst_color) {
case FF_COLOR_RGB:
diff --git a/libavutil/pixdesc.h b/libavutil/pixdesc.h
index f8a195ffcd..48d9300bfe 100644
--- a/libavutil/pixdesc.h
+++ b/libavutil/pixdesc.h
@@ -357,12 +357,15 @@ void av_write_image_line(const uint16_t *src, uint8_t *data[4],
*/
enum AVPixelFormat av_pix_fmt_swap_endianness(enum AVPixelFormat pix_fmt);
-#define FF_LOSS_RESOLUTION 0x0001 /**< loss due to resolution change */
-#define FF_LOSS_DEPTH 0x0002 /**< loss due to color depth change */
-#define FF_LOSS_COLORSPACE 0x0004 /**< loss due to color space conversion */
-#define FF_LOSS_ALPHA 0x0008 /**< loss of alpha bits */
-#define FF_LOSS_COLORQUANT 0x0010 /**< loss due to color quantization */
-#define FF_LOSS_CHROMA 0x0020 /**< loss of chroma (e.g. RGB to gray conversion) */
+#define FF_LOSS_RESOLUTION 0x0001 /**< loss due to resolution change */
+#define FF_LOSS_DEPTH 0x0002 /**< loss due to color depth change */
+#define FF_LOSS_COLORSPACE 0x0004 /**< loss due to color space conversion */
+#define FF_LOSS_ALPHA 0x0008 /**< loss of alpha bits */
+#define FF_LOSS_COLORQUANT 0x0010 /**< loss due to color quantization */
+#define FF_LOSS_CHROMA 0x0020 /**< loss of chroma (e.g. RGB to gray conversion) */
+#define FF_LOSS_EXCESS_RESOLUTION 0x0040 /**< loss due to unneeded extra resolution */
+#define FF_LOSS_EXCESS_DEPTH 0x0080 /**< loss due to unneeded extra color depth */
+
/**
* Compute what kind of losses will occur when converting from one specific
diff --git a/libavutil/tests/pixfmt_best.c b/libavutil/tests/pixfmt_best.c
index 0542af494f..bc0def8d83 100644
--- a/libavutil/tests/pixfmt_best.c
+++ b/libavutil/tests/pixfmt_best.c
@@ -39,32 +39,67 @@ static const enum AVPixelFormat pixfmt_list[] = {
AV_PIX_FMT_VAAPI,
};
-static enum AVPixelFormat find_best(enum AVPixelFormat pixfmt)
+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,
+};
+
+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)
+
+static void test(enum AVPixelFormat input, enum AVPixelFormat expected,
+ int *pass, int *fail, find_best_t find_best_fn)
{
- enum AVPixelFormat best = AV_PIX_FMT_NONE;
- int i;
- for (i = 0; i < FF_ARRAY_ELEMS(pixfmt_list); i++)
- best = av_find_best_pix_fmt_of_2(best, pixfmt_list[i],
- pixfmt, 0, NULL);
- return best;
+ 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)
{
- enum AVPixelFormat output;
int i, pass = 0, fail = 0;
-#define TEST(input, expected) do { \
- output = find_best(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; \
- } while (0)
+#define TEST(input, expected) \
+ test(input, expected, &pass, &fail, find_best_base)
// Same formats.
for (i = 0; i < FF_ARRAY_ELEMS(pixfmt_list); i++)
@@ -137,6 +172,44 @@ int main(void)
// Opaque formats are least unlike each other.
TEST(AV_PIX_FMT_DXVA2_VLD, AV_PIX_FMT_VDPAU);
+#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);
+
printf("%d tests passed, %d tests failed.\n", pass, fail);
return !!fail;
}
diff --git a/tests/ref/fate/pixfmt_best b/tests/ref/fate/pixfmt_best
index 783c5fe640..f9a0fd573b 100644
--- a/tests/ref/fate/pixfmt_best
+++ b/tests/ref/fate/pixfmt_best
@@ -1 +1 @@
-75 tests passed, 0 tests failed.
+101 tests passed, 0 tests failed.
--
2.34.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [FFmpeg-devel] [PATCH] v3: lavu/pixdesc: favour formats where depth and subsampling exactly match
2022-09-14 23:20 [FFmpeg-devel] [PATCH] v3: lavu/pixdesc: favour formats where depth and subsampling exactly match Philip Langdale
@ 2022-09-15 21:25 ` Michael Niedermayer
2022-09-17 1:45 ` Philip Langdale
0 siblings, 1 reply; 3+ messages in thread
From: Michael Niedermayer @ 2022-09-15 21:25 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1.1: Type: text/plain, Size: 4986 bytes --]
On Wed, Sep 14, 2022 at 04:20:02PM -0700, Philip Langdale wrote:
> 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.
>
> v2: Rework penalty system to scale penalty based on how different the
> two formats are, and add new loss categories for them.
>
> v3: Remove leftover bits of v1.
> Remove bit depth penalty scaling to avoid the value being too large
> in extreme cases (1 bit vs 16 bit).
>
> Signed-off-by: Philip Langdale <philipl@overt.org>
> ---
> libavutil/pixdesc.c | 31 +++++++++-
> libavutil/pixdesc.h | 15 +++--
> libavutil/tests/pixfmt_best.c | 111 ++++++++++++++++++++++++++++------
> tests/ref/fate/pixfmt_best | 2 +-
> 4 files changed, 132 insertions(+), 27 deletions(-)
>
> diff --git a/libavutil/pixdesc.c b/libavutil/pixdesc.c
> index d7c6ebfdc4..6377224c64 100644
> --- a/libavutil/pixdesc.c
> +++ b/libavutil/pixdesc.c
> @@ -3013,9 +3013,16 @@ static int get_pix_fmt_score(enum AVPixelFormat dst_pix_fmt,
>
> for (i = 0; i < nb_components; i++) {
> int depth_minus1 = (dst_pix_fmt == AV_PIX_FMT_PAL8) ? 7/nb_components : (dst_desc->comp[i].depth - 1);
> - if (src_desc->comp[i].depth - 1 > depth_minus1 && (consider & FF_LOSS_DEPTH)) {
> + int depth_delta = src_desc->comp[i].depth - 1 - depth_minus1;
> + if (depth_delta > 0 && (consider & FF_LOSS_DEPTH)) {
> loss |= FF_LOSS_DEPTH;
> score -= 65536 >> depth_minus1;
> + } else if (depth_delta < 0 && (consider & FF_LOSS_EXCESS_DEPTH)) {
> + // Favour formats where bit depth exactly matches. If all other
> + // scoring is equal, we'd rather use the bit depth that most closely
> + // matches the source.
> + loss |= FF_LOSS_EXCESS_DEPTH;
> + score += depth_delta;
> }
> }
>
> @@ -3035,6 +3042,28 @@ static int get_pix_fmt_score(enum AVPixelFormat dst_pix_fmt,
> }
> }
>
> + if (consider & FF_LOSS_EXCESS_RESOLUTION) {
> + // Favour formats where chroma subsampling exactly matches. If all other
> + // scoring is equal, we'd rather use the subsampling that most closely
> + // matches the source.
> + if (dst_desc->log2_chroma_w < src_desc->log2_chroma_w) {
> + loss |= FF_LOSS_EXCESS_RESOLUTION;
> + score -= 32 << (src_desc->log2_chroma_w - dst_desc->log2_chroma_w);
> + }
> +
> + if (dst_desc->log2_chroma_h < src_desc->log2_chroma_h) {
> + loss |= FF_LOSS_EXCESS_RESOLUTION;
> + score -= 32 << (src_desc->log2_chroma_h - dst_desc->log2_chroma_h);
> + }
with 16bit 4:2:0
to
14bit 4:2:0
vs.
16bit 4:4:4
more data is preserved in the later but the 420->444 would have a loss of 64
i think and 16->14 i think 8. I didnt try this just reading the code so i may
be missing something but i think the code would favor the lower bitdepth
That may be unexpected
thx
[...]
--
Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
Complexity theory is the science of finding the exact solution to an
approximation. Benchmarking OTOH is finding an approximation of the exact
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [FFmpeg-devel] [PATCH] v3: lavu/pixdesc: favour formats where depth and subsampling exactly match
2022-09-15 21:25 ` Michael Niedermayer
@ 2022-09-17 1:45 ` Philip Langdale
0 siblings, 0 replies; 3+ messages in thread
From: Philip Langdale @ 2022-09-17 1:45 UTC (permalink / raw)
To: ffmpeg-devel
On Thu, 15 Sep 2022 23:25:54 +0200
Michael Niedermayer <michael@niedermayer.cc> wrote:
> > +
> > + if (dst_desc->log2_chroma_h < src_desc->log2_chroma_h) {
> > + loss |= FF_LOSS_EXCESS_RESOLUTION;
> > + score -= 32 << (src_desc->log2_chroma_h -
> > dst_desc->log2_chroma_h);
> > + }
>
> with 16bit 4:2:0
> to
> 14bit 4:2:0
> vs.
> 16bit 4:4:4
>
> more data is preserved in the later but the 420->444 would have a
> loss of 64 i think and 16->14 i think 8. I didnt try this just
> reading the code so i may be missing something but i think the code
> would favor the lower bitdepth That may be unexpected
Yep. Another edge case :-)
I've posted a v4 that adjusts the excess resolution penalty to avoid
this.
Maybe I've got it right this time.
Thanks,
--phil
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2022-09-17 1:45 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-09-14 23:20 [FFmpeg-devel] [PATCH] v3: lavu/pixdesc: favour formats where depth and subsampling exactly match Philip Langdale
2022-09-15 21:25 ` Michael Niedermayer
2022-09-17 1:45 ` Philip Langdale
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git