diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 37d4eee858..477fd3727b 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -469,6 +469,7 @@ OBJS-$(CONFIG_SMPTEBARS_FILTER)              += vsrc_testsrc.o
 OBJS-$(CONFIG_SMPTEHDBARS_FILTER)            += vsrc_testsrc.o
 OBJS-$(CONFIG_TESTSRC_FILTER)                += vsrc_testsrc.o
 OBJS-$(CONFIG_TESTSRC2_FILTER)               += vsrc_testsrc.o
+OBJS-$(CONFIG_UHDTVBARS_FILTER)              += vsrc_testsrc.o
 OBJS-$(CONFIG_YUVTESTSRC_FILTER)             += vsrc_testsrc.o
 
 OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index c295f8e403..fce2cf41c5 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -447,6 +447,7 @@ extern AVFilter ff_vsrc_smptebars;
 extern AVFilter ff_vsrc_smptehdbars;
 extern AVFilter ff_vsrc_testsrc;
 extern AVFilter ff_vsrc_testsrc2;
+extern AVFilter ff_vsrc_uhdtvbars;
 extern AVFilter ff_vsrc_yuvtestsrc;
 
 extern AVFilter ff_vsink_nullsink;
diff --git a/libavfilter/vsrc_testsrc.c b/libavfilter/vsrc_testsrc.c
index 3c9ddd5b16..7d313ffa58 100644
--- a/libavfilter/vsrc_testsrc.c
+++ b/libavfilter/vsrc_testsrc.c
@@ -79,6 +79,10 @@ typedef struct TestSourceContext {
 
     /* only used by haldclut */
     int level;
+
+    /* only used by uhdtvbars */
+    int simple;
+    int mode;
 } TestSourceContext;
 
 #define OFFSET(x) offsetof(TestSourceContext, x)
@@ -1803,3 +1807,330 @@ AVFilter ff_vsrc_allrgb = {
 };
 
 #endif /* CONFIG_ALLRGB_FILTER */
+
+#ifdef CONFIG_UHDTVBARS_FILTER
+
+static const AVOption uhdtvbars_options[] = {
+    COMMON_OPTIONS_NOSIZE
+    { "simple", "use simple bars", OFFSET(simple), AV_OPT_TYPE_BOOL,  {.i64=0}, 0, 1, FLAGS },
+    { "mode",   "set bars mode",   OFFSET(mode),   AV_OPT_TYPE_INT,   {.i64=0}, 0, 1, FLAGS, "mode" },
+    { "4k",     NULL,              0,              AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "mode" },
+    { "8k",     NULL,              0,              AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "mode" },
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(uhdtvbars);
+
+static int uhdtvbars_query_formats(AVFilterContext *ctx)
+{
+    TestSourceContext *test = ctx->priv;
+    static const enum AVPixelFormat pix_fmts10[] = {
+        AV_PIX_FMT_GBRP10,
+        AV_PIX_FMT_NONE,
+    };
+    static const enum AVPixelFormat pix_fmts12[] = {
+        AV_PIX_FMT_GBRP12,
+        AV_PIX_FMT_NONE,
+    };
+
+    AVFilterFormats *fmts_list = ff_make_format_list(test->mode ? pix_fmts12 : pix_fmts10);
+    if (!fmts_list)
+        return AVERROR(ENOMEM);
+    return ff_set_common_formats(ctx, fmts_list);
+}
+
+static const uint8_t order[3] = { 1, 2, 0 };
+
+static void draw_bar16(int f, const uint16_t color[4],
+                       int x, int y, int w, int h,
+                       AVFrame *frame)
+{
+    uint16_t *p, *p0;
+
+    x *= f;
+    y *= f;
+    w *= f;
+    h *= f;
+
+    for (int plane = 0; plane < 3; plane++) {
+        const int comp = order[plane];
+        const int c = color[comp];
+        const int linesize = frame->linesize[plane];
+
+        p = p0 = (uint16_t *)(frame->data[plane] + y * linesize + x * 2);
+        for (int i = 0; i < w; i++)
+            p[i] = c;
+        p += linesize / 2;
+        for (int i = 1; i < h; i++, p += linesize / 2)
+            memcpy(p, p0, w * 2);
+    }
+}
+
+static const uint16_t uhdtv_gray40[2][3]    = { { 414, 414, 414 }, { 1656, 1656, 1656 } };
+static const uint16_t uhdtv_yellow40[2][3]  = { { 414, 426, 283 }, { 1656, 1704, 1132 } };
+static const uint16_t uhdtv_cyan40[2][3]    = { { 311, 454, 414 }, { 1244, 1816, 1656 } };
+static const uint16_t uhdtv_green40[2][3]   = { { 311, 466, 283 }, { 1244, 1864, 1132 } };
+static const uint16_t uhdtv_magenta40[2][3] = { { 518, 363, 546 }, { 2072, 1452, 2184 } };
+static const uint16_t uhdtv_red40[2][3]     = { { 518, 374, 414 }, { 2072, 1496, 1656 } };
+static const uint16_t uhdtv_blue40[2][3]    = { { 414, 403, 546 }, { 1656, 1612, 2184 } };
+
+static const uint16_t uhdtv_white75[2][3]   = { { 721, 721, 721 }, { 2884, 2884, 2884 } };
+static const uint16_t uhdtv_yellow75[2][3]  = { { 721, 721,  64 }, { 2884, 2884,  256 } };
+static const uint16_t uhdtv_cyan75[2][3]    = { {  64, 721, 721 }, {  256, 2884, 2884 } };
+static const uint16_t uhdtv_green75[2][3]   = { {  64, 721,  64 }, {  256, 2884,  256 } };
+static const uint16_t uhdtv_megenta75[2][3] = { { 721,  64, 721 }, { 2884,  256, 2884 } };
+static const uint16_t uhdtv_red75[2][3]     = { { 721,  64,  64 }, { 2884,  256,  256 } };
+static const uint16_t uhdtv_blue75[2][3]    = { {  64,  64, 721 }, {  256,  256, 2884 } };
+
+static const uint16_t uhdtv_cyan100[2][3]   = { {  64, 940, 940 }, {  256, 3760, 3760 } };
+static const uint16_t uhdtv_blue100[2][3]   = { {  64,  64, 940 }, {  256,  256, 3760 } };
+static const uint16_t uhdtv_red100[2][3]    = { { 940,  64,  64 }, { 3760,  256,  256 } };
+static const uint16_t uhdtv_yellow100[2][3] = { { 940, 940,  64 }, { 3760, 3760,  256 } };
+static const uint16_t uhdtv_magenta100[2][3]= { { 940,  64, 940 }, { 3760,  256, 3760 } };
+static const uint16_t uhdtv_white100[2][3]  = { { 940, 940, 940 }, { 3760, 3760, 3760 } };
+static const uint16_t uhdtv_black0[2][3]    = { {  64,  64,  64 }, {  256,  256,  256 } };
+
+static const uint16_t uhdtv_yellow[2][3]    = { { 707, 717, 276 }, { 2828, 2868, 1104 } };
+static const uint16_t uhdtv_cyan[2][3]      = { { 465, 698, 716 }, { 1860, 2792, 2864 } };
+static const uint16_t uhdtv_green[2][3]     = { { 441, 694, 259 }, { 1764, 2776, 1036 } };
+static const uint16_t uhdtv_magenta[2][3]   = { { 602, 250, 691 }, { 2408, 1000, 2764 } };
+static const uint16_t uhdtv_red[2][3]       = { { 584, 237, 148 }, { 2336,  948,  592 } };
+static const uint16_t uhdtv_blue[2][3]      = { { 201, 134, 686 }, {  804,  536, 2744 } };
+
+static const uint16_t uhdtv_black2p[2][3]   = { {  80,  80,  80 }, {  320,  320,  320 } };
+static const uint16_t uhdtv_black2n[2][3]   = { {  48,  48,  48 }, {  192,  192,  192 } };
+
+static const uint16_t uhdtv_magental[2][3]  = { { 940, 502, 940 }, { 3760, 2008, 3760 } };
+static const uint16_t uhdtv_magentad[2][3]  = { { 792, 502, 792 }, { 3168, 2008, 3168 } };
+
+static void draw_ramp16(int f, int x, int y, int w, int h,
+                        AVFrame *frame)
+{
+    const int max = f == 2 ? 4095 : 1023;
+    uint16_t *p, *p0;
+
+    x *= f;
+    y *= f;
+    w *= f;
+    h *= f;
+
+    for (int plane = 0; plane < 3; plane++) {
+        const int linesize = frame->linesize[plane];
+
+        p = p0 = (uint16_t *)(frame->data[plane] + y * linesize + x * 2);
+        for (int i = 0; i < w; i++) {
+            p[i] = i * max / w;
+        }
+    }
+
+    for (int plane = 0; plane < 3; plane++) {
+        const int linesize = frame->linesize[plane];
+
+        p = p0 = (uint16_t *)(frame->data[plane] + y * linesize + x * 2);
+        p += linesize / 2;
+        for (int i = 1; i < h; i++, p += linesize / 2)
+            memcpy(p, p0, w * 2);
+    }
+}
+
+static const uint8_t cyclone_pattern[9][10] =
+{
+    {1,0,0,0,0,0,0,0,0,0},
+    {1,0,1,1,1,1,1,1,1,0},
+    {1,0,1,0,0,0,0,0,1,0},
+    {1,0,1,0,1,1,1,0,1,0},
+    {1,0,1,0,1,0,1,0,1,0},
+    {1,0,1,0,0,0,1,0,1,0},
+    {1,0,1,1,1,1,1,0,1,0},
+    {1,0,0,0,0,0,0,0,1,0},
+    {1,1,1,1,1,1,1,1,1,0},
+};
+
+static void draw_Xk_pattern(int m, int x, int y, int w, int h, AVFrame *out)
+{
+    const int f = m ? 2 : 1;
+
+    x *= f;
+    y *= f;
+    w *= f;
+    h *= f;
+
+    for (int plane = 0; plane < 3; plane++) {
+        const int comp = order[plane];
+        const int linesize = out->linesize[plane];
+        uint16_t *p, *p0;
+
+        p = p0 = (uint16_t *)(out->data[plane] + y * linesize + x * 2);
+        for (int i = 0; i < 200 * f; i++) {
+            const int c = i & 1 ? uhdtv_white100[m][comp] : uhdtv_magenta100[m][comp];
+
+            p[i] = c;
+        }
+
+        p += linesize / 2;
+        for (int i = 1; i < h / 2; i++, p += linesize / 2)
+            memcpy(p, p0, 200 * 2 * f);
+    }
+
+    for (int plane = 0; plane < 3; plane++) {
+        const int comp = order[plane];
+        const int linesize = out->linesize[plane];
+        const int c = uhdtv_magental[m][comp];
+        uint16_t *p, *p0;
+
+        p = p0 = (uint16_t *)(out->data[plane] + y * linesize + (x + 200 * f) * 2);
+        for (int i = 0; i < 80 * f; i++) {
+            p[i] = c;
+        }
+
+        p += linesize / 2;
+        for (int i = 1; i < 135 * f; i++, p += linesize / 2)
+            memcpy(p, p0, 80 * 2 * f);
+    }
+
+    for (int plane = 0; plane < 3; plane++) {
+        const int comp = order[plane];
+        const int linesize = out->linesize[plane];
+        const int c = uhdtv_magentad[m][comp];
+        uint16_t *p, *p0;
+
+        p = p0 = (uint16_t *)(out->data[plane] + (y + 135 * f) * linesize + (x + 200 * f) * 2);
+        for (int i = 0; i < 80 * f; i++) {
+            p[i] = c;
+        }
+
+        p += linesize / 2;
+        for (int i = 1; i < 135 * f; i++, p += linesize / 2)
+            memcpy(p, p0, 80 * 2 * f);
+    }
+
+    for (int plane = 0; plane < 3; plane++) {
+        const int comp = order[plane];
+        const int linesize = out->linesize[plane];
+        uint16_t *p;
+
+        p = (uint16_t *)(out->data[plane] + y * linesize + (x + 280 * f) * 2);
+        for (int j = 0; j < 270 * f; j++) {
+            const int c = j & 1 ? uhdtv_white100[m][comp] : uhdtv_magenta100[m][comp];
+
+            for (int i = 0; i < 200 * f; i++) {
+                p[i] = c;
+            }
+            p += linesize / 2;
+        }
+    }
+
+    for (int plane = 0; plane < 3; plane++) {
+        const int comp = order[plane];
+        const int linesize = out->linesize[plane];
+        uint16_t *p;
+
+        for (int j = 0; j < 30 * f; j++) {
+            for (int i = 0; i < 48 * f; i++) {
+                p = (uint16_t *)(out->data[plane] + (y + 9 * j + 270 * f) * linesize + (x + i * 10) * 2);
+                for (int k = 0; k < 9; k++) {
+                    for (int l = 0; l < 10; l++) {
+                        p[l] = cyclone_pattern[k][l] ? uhdtv_white100[m][comp] : uhdtv_black0[m][comp];
+                    }
+                    p += linesize / 2;
+                }
+            }
+        }
+    }
+}
+
+static void uhdtvbars_fill_picture(AVFilterContext *ctx, AVFrame *out)
+{
+    TestSourceContext *test = ctx->priv;
+    const int m = test->mode;
+    const int f = m ? 2 : 1;
+
+    draw_bar16(f, uhdtv_gray40[m],       0,    0, 480, 1260, out);
+    draw_bar16(f, uhdtv_white75[m],    480,    0, 412, 1440, out);
+    draw_bar16(f, uhdtv_yellow75[m],   892,    0, 412, 1260, out);
+    draw_bar16(f, uhdtv_cyan75[m],    1304,    0, 412, 1260, out);
+    draw_bar16(f, uhdtv_green75[m],   1716,    0, 408, 1260, out);
+    draw_bar16(f, uhdtv_megenta75[m], 2124,    0, 412, 1260, out);
+    draw_bar16(f, uhdtv_red75[m],     2536,    0, 412, 1260, out);
+    draw_bar16(f, uhdtv_blue75[m],    2948,    0, 412, 1260, out);
+    draw_bar16(f, uhdtv_gray40[m],    3360,    0, 480, 1260, out);
+
+    draw_bar16(f, uhdtv_cyan100[m],      0, 1260, 480,  180, out);
+    draw_bar16(f, uhdtv_yellow100[m],    0, 1440, 480,  180, out);
+
+    draw_bar16(f, uhdtv_yellow[m],     892, 1260, 412,  180, out);
+    draw_bar16(f, uhdtv_cyan[m],      1304, 1260, 412,  180, out);
+    draw_bar16(f, uhdtv_green[m],     1716, 1260, 408,  180, out);
+    draw_bar16(f, uhdtv_magenta[m],   2124, 1260, 412,  180, out);
+    draw_bar16(f, uhdtv_red[m],       2536, 1260, 412,  180, out);
+    draw_bar16(f, uhdtv_blue[m],      2948, 1260, 412,  180, out);
+
+    draw_bar16(f, uhdtv_blue100[m],   3360, 1260, 480,  180, out);
+    draw_bar16(f, uhdtv_red100[m],    3360, 1440, 480,  180, out);
+
+    draw_bar16(f, uhdtv_gray40[m],       0, 1620, 480,  540, out);
+    draw_bar16(f, uhdtv_gray40[m],    3360, 1620, 480,  540, out);
+
+    draw_ramp16(f, 480, 1440, 2880, 180, out);
+
+    draw_bar16(f, uhdtv_black0[m],     480, 1620, 564,  540, out);
+    draw_bar16(f, uhdtv_white100[m],  1044, 1620, 876,  540, out);
+    draw_bar16(f, uhdtv_black0[m],    1920, 1620, 464,  540, out);
+    draw_bar16(f, uhdtv_black2n[m],   2384, 1620, 136,  540, out);
+    draw_bar16(f, uhdtv_black0[m],    2520, 1620, 140,  540, out);
+    draw_bar16(f, uhdtv_black2p[m],   2660, 1620, 136,  540, out);
+    draw_bar16(f, uhdtv_black0[m],    2796, 1620, 564,  540, out);
+
+    if (!test->simple) {
+        draw_Xk_pattern(m, m ? 0 : 3360, 1620, 480, 540, out);
+
+        draw_bar16(f, uhdtv_yellow40[m],   200,  90, 80,  180, out);
+        draw_bar16(f, uhdtv_cyan40[m],     200, 270, 80,  180, out);
+        draw_bar16(f, uhdtv_green40[m],    200, 450, 80,  180, out);
+        draw_bar16(f, uhdtv_magenta40[m],  200, 630, 80,  180, out);
+        draw_bar16(f, uhdtv_red40[m],      200, 810, 80,  180, out);
+        draw_bar16(f, uhdtv_blue40[m],     200, 990, 80,  180, out);
+
+        draw_bar16(f, uhdtv_yellow40[m],  3560,  90, 80,  180, out);
+        draw_bar16(f, uhdtv_cyan40[m],    3560, 270, 80,  180, out);
+        draw_bar16(f, uhdtv_green40[m],   3560, 450, 80,  180, out);
+        draw_bar16(f, uhdtv_magenta40[m], 3560, 630, 80,  180, out);
+        draw_bar16(f, uhdtv_red40[m],     3560, 810, 80,  180, out);
+        draw_bar16(f, uhdtv_blue40[m],    3560, 990, 80,  180, out);
+    }
+}
+
+static av_cold int uhdtvbars_init(AVFilterContext *ctx)
+{
+    TestSourceContext *test = ctx->priv;
+
+    test->fill_picture_fn = uhdtvbars_fill_picture;
+    test->draw_once = 1;
+    test->w = 3840 * (1 + test->mode);
+    test->h = 2160 * (1 + test->mode);
+    return init(ctx);
+}
+
+static const AVFilterPad uhdtvbars_outputs[] = {
+    {
+        .name          = "default",
+        .type          = AVMEDIA_TYPE_VIDEO,
+        .request_frame = request_frame,
+        .config_props  = config_props,
+    },
+    { NULL }
+};
+
+AVFilter ff_vsrc_uhdtvbars = {
+    .name          = "uhdtvbars",
+    .description   = NULL_IF_CONFIG_SMALL("Generate UHDTV color bars."),
+    .priv_size     = sizeof(TestSourceContext),
+    .priv_class    = &uhdtvbars_class,
+    .init          = uhdtvbars_init,
+    .uninit        = uninit,
+    .query_formats = uhdtvbars_query_formats,
+    .inputs        = NULL,
+    .outputs       = uhdtvbars_outputs,
+};
+
+#endif /* CONFIG_UHDTVBARS_FILTER */
