diff --git a/libavutil/pixdesc.c b/libavutil/pixdesc.c
index d7c6ebfdc4..b472a94f60 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 -= 1 << (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 -= 1 << (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 += 4;
+            }
+    }
+
     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..d71b3cc2e8 100644
--- a/libavutil/tests/pixfmt_best.c
+++ b/libavutil/tests/pixfmt_best.c
@@ -39,32 +39,74 @@ 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,
+};
+
+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)
 {
-    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 +179,55 @@ 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);
+
+#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;
 }
diff --git a/tests/ref/fate/pixfmt_best b/tests/ref/fate/pixfmt_best
index 783c5fe640..1bdc0e83f0 100644
--- a/tests/ref/fate/pixfmt_best
+++ b/tests/ref/fate/pixfmt_best
@@ -1 +1 @@
-75 tests passed, 0 tests failed.
+106 tests passed, 0 tests failed.
