diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am
index 0b7c8dcf5b5ffc092e979b1cafb651ed39268df4..8abc72b614612b9ce894830a109a28220aa31e2d 100644
--- a/modules/video_output/Makefile.am
+++ b/modules/video_output/Makefile.am
@@ -4,6 +4,7 @@ vout_LTLIBRARIES =
 EXTRA_DIST += video_output/README
 
 OPENGL_COMMONSOURCES = video_output/opengl/vout_helper.c \
+	video_output/placebo_utils.c video_output/placebo_utils.h \
 	video_output/opengl/vout_helper.h video_output/opengl/converter.h \
 	video_output/opengl/internal.h video_output/opengl/fragment_shaders.c \
 	video_output/opengl/converter_sw.c
diff --git a/modules/video_output/opengl/fragment_shaders.c b/modules/video_output/opengl/fragment_shaders.c
index e117122b93901efc72826e5de86ca21e22eb2824..52af4939a0c21765dc1673156b34f5c7333ac7b1 100644
--- a/modules/video_output/opengl/fragment_shaders.c
+++ b/modules/video_output/opengl/fragment_shaders.c
@@ -28,6 +28,7 @@
 #ifdef HAVE_LIBPLACEBO
 #include <libplacebo/shaders.h>
 #include <libplacebo/shaders/colorspace.h>
+#include "../placebo_utils.h"
 #endif
 
 #include <vlc_common.h>
@@ -503,57 +504,6 @@ xyz12_shader_init(opengl_tex_converter_t *tc)
     return fragment_shader;
 }
 
-#ifdef HAVE_LIBPLACEBO
-static struct pl_color_space pl_color_space_from_video_format(const video_format_t *fmt)
-{
-    static enum pl_color_primaries primaries[COLOR_PRIMARIES_MAX+1] = {
-        [COLOR_PRIMARIES_UNDEF]     = PL_COLOR_PRIM_UNKNOWN,
-        [COLOR_PRIMARIES_BT601_525] = PL_COLOR_PRIM_BT_601_525,
-        [COLOR_PRIMARIES_BT601_625] = PL_COLOR_PRIM_BT_601_625,
-        [COLOR_PRIMARIES_BT709]     = PL_COLOR_PRIM_BT_709,
-        [COLOR_PRIMARIES_BT2020]    = PL_COLOR_PRIM_BT_2020,
-        [COLOR_PRIMARIES_DCI_P3]    = PL_COLOR_PRIM_DCI_P3,
-        [COLOR_PRIMARIES_BT470_M]   = PL_COLOR_PRIM_BT_470M,
-    };
-
-    static enum pl_color_transfer transfers[TRANSFER_FUNC_MAX+1] = {
-        [TRANSFER_FUNC_UNDEF]        = PL_COLOR_TRC_UNKNOWN,
-        [TRANSFER_FUNC_LINEAR]       = PL_COLOR_TRC_LINEAR,
-        [TRANSFER_FUNC_SRGB]         = PL_COLOR_TRC_SRGB,
-        [TRANSFER_FUNC_SMPTE_ST2084] = PL_COLOR_TRC_PQ,
-        [TRANSFER_FUNC_HLG]          = PL_COLOR_TRC_HLG,
-        // these are all designed to be displayed on BT.1886 displays, so this
-        // is the correct way to handle them in libplacebo
-        [TRANSFER_FUNC_BT470_BG]    = PL_COLOR_TRC_BT_1886,
-        [TRANSFER_FUNC_BT470_M]     = PL_COLOR_TRC_BT_1886,
-        [TRANSFER_FUNC_BT709]       = PL_COLOR_TRC_BT_1886,
-        [TRANSFER_FUNC_SMPTE_240]   = PL_COLOR_TRC_BT_1886,
-    };
-
-    // Derive the signal peak/avg from the color light level metadata
-    float sig_peak = fmt->lighting.MaxCLL / PL_COLOR_REF_WHITE;
-    float sig_avg = fmt->lighting.MaxFALL / PL_COLOR_REF_WHITE;
-
-    // As a fallback value for the signal peak, we can also use the mastering
-    // metadata's luminance information
-    if (!sig_peak)
-        sig_peak = fmt->mastering.max_luminance / (10000.0 * PL_COLOR_REF_WHITE);
-
-    // Sanitize the sig_peak/sig_avg, because of buggy or low quality tagging
-    // that's sadly common in lots of typical sources
-    sig_peak = (sig_peak > 1.0 && sig_peak <= 100.0) ? sig_peak : 0.0;
-    sig_avg  = (sig_avg >= 0.0 && sig_avg <= 1.0) ? sig_avg : 0.0;
-
-    return (struct pl_color_space) {
-        .primaries = primaries[fmt->primaries],
-        .transfer  = transfers[fmt->transfer],
-        .light     = PL_COLOR_LIGHT_UNKNOWN,
-        .sig_peak  = sig_peak,
-        .sig_avg   = sig_avg,
-    };
-}
-#endif
-
 GLuint
 opengl_fragment_shader_init_impl(opengl_tex_converter_t *tc, GLenum tex_target,
                                  vlc_fourcc_t chroma, video_color_space_t yuv_space)
@@ -624,7 +574,7 @@ opengl_fragment_shader_init_impl(opengl_tex_converter_t *tc, GLenum tex_target,
         dst_space.transfer = var_InheritInteger(tc->gl, "target-trc");
 
         pl_shader_color_map(sh, &color_params,
-                pl_color_space_from_video_format(&tc->fmt),
+                vlc_placebo_ColorSpace(&tc->fmt),
                 dst_space, NULL, false);
 
         struct pl_shader_obj *dither_state = NULL;
diff --git a/modules/video_output/opengl/vout_helper.c b/modules/video_output/opengl/vout_helper.c
index 89fc1ae9c7664a19bc76b0815f6263c2d2554629..adcc019d4543667f86e667de315e4c0f95d39a64 100644
--- a/modules/video_output/opengl/vout_helper.c
+++ b/modules/video_output/opengl/vout_helper.c
@@ -579,21 +579,6 @@ opengl_deinit_program(vout_display_opengl_t *vgl, struct prgm *prgm)
     vlc_object_release(tc);
 }
 
-#ifdef HAVE_LIBPLACEBO
-static void
-log_cb(void *priv, enum pl_log_level level, const char *msg)
-{
-    opengl_tex_converter_t *tc = priv;
-    switch (level) {
-    case PL_LOG_FATAL: // fall through
-    case PL_LOG_ERR:  msg_Err(tc->gl, "%s", msg); break;
-    case PL_LOG_WARN: msg_Warn(tc->gl,"%s", msg); break;
-    case PL_LOG_INFO: msg_Info(tc->gl,"%s", msg); break;
-    default: break;
-    }
-}
-#endif
-
 static int
 opengl_init_program(vout_display_opengl_t *vgl, struct prgm *prgm,
                     const char *glexts, const video_format_t *fmt, bool subpics,
@@ -621,14 +606,10 @@ opengl_init_program(vout_display_opengl_t *vgl, struct prgm *prgm,
     tc->fmt = *fmt;
 
 #ifdef HAVE_LIBPLACEBO
-    // create the main libplacebo context
+    // Create the main libplacebo context
     if (!subpics)
     {
-        tc->pl_ctx = pl_context_create(PL_API_VER, &(struct pl_context_params) {
-            .log_cb    = log_cb,
-            .log_priv  = tc,
-            .log_level = PL_LOG_INFO,
-        });
+        tc->pl_ctx = vlc_placebo_Create(VLC_OBJECT(tc));
         if (tc->pl_ctx) {
 #   if PL_API_VER >= 6
             tc->pl_sh = pl_shader_alloc(tc->pl_ctx, NULL, 0);
diff --git a/modules/video_output/opengl/vout_helper.h b/modules/video_output/opengl/vout_helper.h
index 02b17fabbb6a00b52e74b10f43302744bbcf9f84..8b413de7e0c27a0da6928419fb9e585e389a57ef 100644
--- a/modules/video_output/opengl/vout_helper.h
+++ b/modules/video_output/opengl/vout_helper.h
@@ -32,144 +32,7 @@
 #include "converter.h"
 
 #ifdef HAVE_LIBPLACEBO
-#include <libplacebo/shaders/colorspace.h>
-
-#define RENDER_INTENT_TEXT N_("Rendering intent for color conversion")
-#define RENDER_INTENT_LONGTEXT N_("The algorithm used to convert between color spaces")
-
-static const int intent_values[] = {
-    PL_INTENT_PERCEPTUAL,
-    PL_INTENT_RELATIVE_COLORIMETRIC,
-    PL_INTENT_SATURATION,
-    PL_INTENT_ABSOLUTE_COLORIMETRIC,
-};
-
-static const char * const intent_text[] = {
-    "Perceptual",
-    "Relative colorimetric",
-    "Absolute colorimetric",
-    "Saturation",
-};
-
-#define PRIM_TEXT N_("Display primaries")
-#define PRIM_LONGTEXT N_("The primaries associated with the output display")
-
-static const int prim_values[] = {
-    PL_COLOR_PRIM_UNKNOWN,
-    PL_COLOR_PRIM_BT_601_525,
-    PL_COLOR_PRIM_BT_601_625,
-    PL_COLOR_PRIM_BT_709,
-    PL_COLOR_PRIM_BT_470M,
-    PL_COLOR_PRIM_BT_2020,
-    PL_COLOR_PRIM_APPLE,
-    PL_COLOR_PRIM_ADOBE,
-    PL_COLOR_PRIM_PRO_PHOTO,
-    PL_COLOR_PRIM_CIE_1931,
-    PL_COLOR_PRIM_DCI_P3,
-    PL_COLOR_PRIM_V_GAMUT,
-    PL_COLOR_PRIM_S_GAMUT,
-};
-
-static const char * const prim_text[] = {
-    "Unknown primaries",
-    "ITU-R Rec. BT.601 (525-line = NTSC, SMPTE-C)",
-    "ITU-R Rec. BT.601 (625-line = PAL, SECAM)",
-    "ITU-R Rec. BT.709 (HD), also sRGB",
-    "ITU-R Rec. BT.470 M",
-    "ITU-R Rec. BT.2020 (UltraHD)",
-    "Apple RGB",
-    "Adobe RGB (1998)",
-    "ProPhoto RGB (ROMM)",
-    "CIE 1931 RGB primaries",
-    "DCI-P3 (Digital Cinema)",
-    "Panasonic V-Gamut (VARICAM)",
-    "Sony S-Gamut",
-};
-
-#define TRC_TEXT N_("Display gamma / transfer function")
-#define TRC_LONGTEXT N_("The gamma/transfer function associated with the output display")
-
-static const int trc_values[] = {
-    PL_COLOR_TRC_UNKNOWN,
-    PL_COLOR_TRC_BT_1886,
-    PL_COLOR_TRC_SRGB,
-    PL_COLOR_TRC_LINEAR,
-    PL_COLOR_TRC_GAMMA18,
-    PL_COLOR_TRC_GAMMA22,
-    PL_COLOR_TRC_GAMMA28,
-    PL_COLOR_TRC_PRO_PHOTO,
-    PL_COLOR_TRC_PQ,
-    PL_COLOR_TRC_HLG,
-    PL_COLOR_TRC_V_LOG,
-    PL_COLOR_TRC_S_LOG1,
-    PL_COLOR_TRC_S_LOG2,
-};
-
-static const char * const trc_text[] = {
-    "Unknown gamma",
-    "ITU-R Rec. BT.1886 (CRT emulation + OOTF)",
-    "IEC 61966-2-4 sRGB (CRT emulation)",
-    "Linear light content",
-    "Pure power gamma 1.8",
-    "Pure power gamma 2.2",
-    "Pure power gamma 2.8",
-    "ProPhoto RGB (ROMM)",
-    "ITU-R BT.2100 PQ (perceptual quantizer), aka SMPTE ST2084",
-    "ITU-R BT.2100 HLG (hybrid log-gamma), aka ARIB STD-B67",
-    "Panasonic V-Log (VARICAM)",
-    "Sony S-Log1",
-    "Sony S-Log2",
-};
-
-#define TONEMAPPING_TEXT N_("Tone-mapping algorithm")
-#define TONEMAPPING_LONGTEXT N_("Algorithm to use when converting from wide gamut to standard gamut, or from HDR to SDR")
-
-static const int tone_values[] = {
-    PL_TONE_MAPPING_HABLE,
-    PL_TONE_MAPPING_MOBIUS,
-    PL_TONE_MAPPING_REINHARD,
-    PL_TONE_MAPPING_GAMMA,
-    PL_TONE_MAPPING_LINEAR,
-    PL_TONE_MAPPING_CLIP,
-};
-
-static const char * const tone_text[] = {
-    "Hable (filmic mapping, recommended)",
-    "Mobius (linear + knee)",
-    "Reinhard (simple non-linear)",
-    "Gamma-Power law",
-    "Linear stretch (peak to peak)",
-    "Hard clip out-of-gamut",
-};
-
-#define TONEMAP_PARAM_TEXT N_("Tone-mapping parameter")
-#define TONEMAP_PARAM_LONGTEXT N_("This parameter can be used to tune the tone-mapping curve. Specifics depend on the curve used.")
-
-#define TONEMAP_DESAT_TEXT N_("Tone-mapping desaturation coefficient")
-#define TONEMAP_DESAT_LONGTEXT N_("How strongly to desaturate overbright colors towards white. 0.0 disables this behavior.")
-
-#define TONEMAP_WARN_TEXT N_("Highlight clipped pixels")
-#define TONEMAP_WARN_LONGTEXT N_("Debugging tool to indicate which pixels were clipped as part of the tone mapping process.")
-
-#define DITHER_TEXT N_("Dithering algorithm")
-#define DITHER_LONGTEXT N_("The algorithm to use when dithering to a lower bit depth (degrades performance on some platforms).")
-
-static const int dither_values[] = {
-    -1, // no dithering
-    PL_DITHER_BLUE_NOISE,
-    PL_DITHER_WHITE_NOISE,
-    PL_DITHER_ORDERED_LUT,
-};
-
-static const char * const dither_text[] = {
-    "Disabled",
-    "Blue noise",
-    "White noise",
-    "Bayer matrix (ordered dither)",
-};
-
-#define DEPTH_TEXT N_("Dither depth override (0 = framebuffer depth)")
-#define DEPTH_LONGTEXT N_("Overrides the detected framebuffer depth. Useful to dither to lower bit depths than otherwise required.")
+#include "../placebo_utils.h"
 
 #define add_glopts_placebo() \
     set_section(N_("Colorspace conversion"), NULL) \
@@ -188,11 +51,12 @@ static const char * const dither_text[] = {
               TONEMAP_PARAM_TEXT, TONEMAP_PARAM_LONGTEXT, true) \
     add_float("tone-mapping-desat", pl_color_map_default_params.tone_mapping_desaturate, \
               TONEMAP_DESAT_TEXT, TONEMAP_DESAT_LONGTEXT, false) \
-    add_bool("tone-mapping-warn", false, TONEMAP_WARN_TEXT, TONEMAP_WARN_LONGTEXT, false) \
+    add_bool("tone-mapping-warn", false, GAMUT_WARN_TEXT, GAMUT_WARN_LONGTEXT, false) \
     set_section(N_("Dithering"), NULL) \
     add_integer("dither-algo", -1, DITHER_TEXT, DITHER_LONGTEXT, false) \
             change_integer_list(dither_values, dither_text) \
-    add_integer_with_range("dither-depth", 0, 0, 16, DEPTH_TEXT, DEPTH_LONGTEXT, false)
+    add_integer_with_range("dither-depth", 0, 0, 16, \
+            DITHER_DEPTH_TEXT, DITHER_DEPTH_LONGTEXT, false)
 #else
 #define add_glopts_placebo()
 #endif
diff --git a/modules/video_output/placebo_utils.c b/modules/video_output/placebo_utils.c
new file mode 100644
index 0000000000000000000000000000000000000000..2515dc0befa82c0b671de083f0d120f0369f11a3
--- /dev/null
+++ b/modules/video_output/placebo_utils.c
@@ -0,0 +1,102 @@
+/*****************************************************************************
+ * placebo.c: Definition of various libplacebo helpers
+ *****************************************************************************
+ * Copyright (C) 2018 Niklas Haas
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdatomic.h>
+#include <stdlib.h>
+
+#include <vlc_common.h>
+#include "placebo_utils.h"
+
+static void Log(void *priv, enum pl_log_level level, const char *msg)
+{
+    struct vlc_object_t *obj = priv;
+
+    switch (level) {
+    case PL_LOG_FATAL: // fall through
+    case PL_LOG_ERR:   msg_Err(obj,  "%s", msg); break;
+    case PL_LOG_WARN:  msg_Warn(obj, "%s", msg); break;
+    case PL_LOG_INFO:  msg_Info(obj, "%s", msg); break;
+    case PL_LOG_DEBUG: msg_Dbg(obj,  "%s", msg); break;
+    default: break;
+    }
+}
+
+struct pl_context *vlc_placebo_Create(vlc_object_t *obj)
+{
+    return pl_context_create(PL_API_VER, &(struct pl_context_params) {
+        .log_level = PL_LOG_DEBUG,
+        .log_cb    = Log,
+        .log_priv  = obj,
+    });
+}
+
+struct pl_color_space vlc_placebo_ColorSpace(const video_format_t *fmt)
+{
+    static const enum pl_color_primaries primaries[COLOR_PRIMARIES_MAX+1] = {
+        [COLOR_PRIMARIES_UNDEF]     = PL_COLOR_PRIM_UNKNOWN,
+        [COLOR_PRIMARIES_BT601_525] = PL_COLOR_PRIM_BT_601_525,
+        [COLOR_PRIMARIES_BT601_625] = PL_COLOR_PRIM_BT_601_625,
+        [COLOR_PRIMARIES_BT709]     = PL_COLOR_PRIM_BT_709,
+        [COLOR_PRIMARIES_BT2020]    = PL_COLOR_PRIM_BT_2020,
+        [COLOR_PRIMARIES_DCI_P3]    = PL_COLOR_PRIM_DCI_P3,
+        [COLOR_PRIMARIES_BT470_M]   = PL_COLOR_PRIM_BT_470M,
+    };
+
+    static const enum pl_color_transfer transfers[TRANSFER_FUNC_MAX+1] = {
+        [TRANSFER_FUNC_UNDEF]        = PL_COLOR_TRC_UNKNOWN,
+        [TRANSFER_FUNC_LINEAR]       = PL_COLOR_TRC_LINEAR,
+        [TRANSFER_FUNC_SRGB]         = PL_COLOR_TRC_SRGB,
+        [TRANSFER_FUNC_SMPTE_ST2084] = PL_COLOR_TRC_PQ,
+        [TRANSFER_FUNC_HLG]          = PL_COLOR_TRC_HLG,
+        // these are all designed to be displayed on BT.1886 displays, so this
+        // is the correct way to handle them in libplacebo
+        [TRANSFER_FUNC_BT470_BG]    = PL_COLOR_TRC_BT_1886,
+        [TRANSFER_FUNC_BT470_M]     = PL_COLOR_TRC_BT_1886,
+        [TRANSFER_FUNC_BT709]       = PL_COLOR_TRC_BT_1886,
+        [TRANSFER_FUNC_SMPTE_240]   = PL_COLOR_TRC_BT_1886,
+    };
+
+    // Derive the signal peak/avg from the color light level metadata
+    float sig_peak = fmt->lighting.MaxCLL / PL_COLOR_REF_WHITE;
+    float sig_avg = fmt->lighting.MaxFALL / PL_COLOR_REF_WHITE;
+
+    // As a fallback value for the signal peak, we can also use the mastering
+    // metadata's luminance information
+    if (!sig_peak)
+        sig_peak = fmt->mastering.max_luminance / (10000.0 * PL_COLOR_REF_WHITE);
+
+    // Sanitize the sig_peak/sig_avg, because of buggy or low quality tagging
+    // that's sadly common in lots of typical sources
+    sig_peak = (sig_peak > 1.0 && sig_peak <= 100.0) ? sig_peak : 0.0;
+    sig_avg  = (sig_avg >= 0.0 && sig_avg <= 1.0) ? sig_avg : 0.0;
+
+    return (struct pl_color_space) {
+        .primaries = primaries[fmt->primaries],
+        .transfer  = transfers[fmt->transfer],
+        .light     = PL_COLOR_LIGHT_UNKNOWN,
+        .sig_peak  = sig_peak,
+        .sig_avg   = sig_avg,
+    };
+}
diff --git a/modules/video_output/placebo_utils.h b/modules/video_output/placebo_utils.h
new file mode 100644
index 0000000000000000000000000000000000000000..2933996255a01285d684f11b0b2b13b853c67393
--- /dev/null
+++ b/modules/video_output/placebo_utils.h
@@ -0,0 +1,178 @@
+/*****************************************************************************
+ * placebo_utils.h: Definition of various libplacebo helpers
+ *****************************************************************************
+ * Copyright (C) 2018 Niklas Haas
+ *
+ * This program 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.
+ *
+ * This program 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 this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef VLC_PLACEBO_UTILS_H
+#define VLC_PLACEBO_UTILS_H 1
+
+#include <vlc_common.h>
+#include <vlc_picture.h>
+
+#include <libplacebo/colorspace.h>
+#include <libplacebo/shaders/colorspace.h>
+
+// Create a libplacebo context, hooked up to the log system; or NULL on OOM
+VLC_API struct pl_context *vlc_placebo_Create(vlc_object_t *);
+
+// Turn a video_format_t into the equivalent libplacebo values
+VLC_API struct pl_color_space vlc_placebo_ColorSpace(const video_format_t *);
+
+// Shared options strings/structs for libplacebo options
+
+#define RENDER_INTENT_TEXT "Rendering intent for color conversion"
+#define RENDER_INTENT_LONGTEXT "The mapping type used to convert between color spaces."
+
+static const int intent_values[] = {
+    PL_INTENT_PERCEPTUAL,
+    PL_INTENT_RELATIVE_COLORIMETRIC,
+    PL_INTENT_SATURATION,
+    PL_INTENT_ABSOLUTE_COLORIMETRIC,
+};
+
+static const char * const intent_text[] = {
+    "Perceptual",
+    "Relative colorimetric",
+    "Absolute colorimetric",
+    "Saturation",
+};
+
+#define PRIM_TEXT "Display primaries"
+#define PRIM_LONGTEXT "The primaries associated with the output display"
+
+static const int prim_values[] = {
+    PL_COLOR_PRIM_UNKNOWN,
+    PL_COLOR_PRIM_BT_601_525,
+    PL_COLOR_PRIM_BT_601_625,
+    PL_COLOR_PRIM_BT_709,
+    PL_COLOR_PRIM_BT_470M,
+    PL_COLOR_PRIM_BT_2020,
+    PL_COLOR_PRIM_APPLE,
+    PL_COLOR_PRIM_ADOBE,
+    PL_COLOR_PRIM_PRO_PHOTO,
+    PL_COLOR_PRIM_CIE_1931,
+    PL_COLOR_PRIM_DCI_P3,
+    PL_COLOR_PRIM_V_GAMUT,
+    PL_COLOR_PRIM_S_GAMUT,
+};
+
+static const char * const prim_text[] = {
+    "Automatic / Unknown primaries",
+    "ITU-R Rec. BT.601 (525-line = NTSC, SMPTE-C)",
+    "ITU-R Rec. BT.601 (625-line = PAL, SECAM)",
+    "ITU-R Rec. BT.709 (HD), also sRGB",
+    "ITU-R Rec. BT.470 M",
+    "ITU-R Rec. BT.2020 (UltraHD)",
+    "Apple RGB",
+    "Adobe RGB (1998)",
+    "ProPhoto RGB (ROMM)",
+    "CIE 1931 RGB primaries",
+    "DCI-P3 (Digital Cinema)",
+    "Panasonic V-Gamut (VARICAM)",
+    "Sony S-Gamut",
+};
+
+#define TRC_TEXT "Display gamma / transfer function"
+#define TRC_LONGTEXT "The gamma/transfer function associated with the output display"
+
+static const int trc_values[] = {
+    PL_COLOR_TRC_UNKNOWN,
+    PL_COLOR_TRC_BT_1886,
+    PL_COLOR_TRC_SRGB,
+    PL_COLOR_TRC_LINEAR,
+    PL_COLOR_TRC_GAMMA18,
+    PL_COLOR_TRC_GAMMA22,
+    PL_COLOR_TRC_GAMMA28,
+    PL_COLOR_TRC_PRO_PHOTO,
+    PL_COLOR_TRC_PQ,
+    PL_COLOR_TRC_HLG,
+    PL_COLOR_TRC_V_LOG,
+    PL_COLOR_TRC_S_LOG1,
+    PL_COLOR_TRC_S_LOG2,
+};
+
+static const char * const trc_text[] = {
+    "Automatic / Unknown gamma",
+    "ITU-R Rec. BT.1886 (CRT emulation + OOTF)",
+    "IEC 61966-2-4 sRGB (CRT emulation)",
+    "Linear light content",
+    "Pure power gamma 1.8",
+    "Pure power gamma 2.2",
+    "Pure power gamma 2.8",
+    "ProPhoto RGB (ROMM)",
+    "ITU-R BT.2100 PQ (perceptual quantizer), aka SMPTE ST2084",
+    "ITU-R BT.2100 HLG (hybrid log-gamma), aka ARIB STD-B67",
+    "Panasonic V-Log (VARICAM)",
+    "Sony S-Log1",
+    "Sony S-Log2",
+};
+
+
+#define TONEMAPPING_TEXT "Tone-mapping algorithm"
+#define TONEMAPPING_LONGTEXT "Algorithm to use when converting from wide gamut to standard gamut, or from HDR to SDR."
+
+static const int tone_values[] = {
+    PL_TONE_MAPPING_HABLE,
+    PL_TONE_MAPPING_MOBIUS,
+    PL_TONE_MAPPING_REINHARD,
+    PL_TONE_MAPPING_GAMMA,
+    PL_TONE_MAPPING_LINEAR,
+    PL_TONE_MAPPING_CLIP,
+};
+
+static const char * const tone_text[] = {
+    "Hable (filmic mapping, recommended)",
+    "Mobius (linear + knee)",
+    "Reinhard (simple non-linear)",
+    "Gamma-Power law",
+    "Linear stretch (peak to peak)",
+    "Hard clip out-of-gamut",
+};
+
+#define TONEMAP_PARAM_TEXT "Tone-mapping parameter"
+#define TONEMAP_PARAM_LONGTEXT "This parameter can be used to tune the tone-mapping curve. Specifics depend on the curve used. If left as 0, the curve's preferred default is used."
+
+#define TONEMAP_DESAT_TEXT "Tone-mapping desaturation coefficient"
+#define TONEMAP_DESAT_LONGTEXT "How strongly to desaturate bright spectral colors towards white. 0.0 disables this behavior."
+
+#define GAMUT_WARN_TEXT "Highlight clipped pixels"
+#define GAMUT_WARN_LONGTEXT "Debugging tool to indicate which pixels were clipped as part of the tone mapping process."
+
+#define DITHER_TEXT "Dithering algorithm"
+#define DITHER_LONGTEXT "The algorithm to use when dithering to a lower bit depth."
+
+static const int dither_values[] = {
+    -1, // no dithering
+    PL_DITHER_BLUE_NOISE,
+    PL_DITHER_ORDERED_FIXED,
+    PL_DITHER_ORDERED_LUT,
+    PL_DITHER_WHITE_NOISE,
+};
+
+static const char * const dither_text[] = {
+    "Disabled",
+    "Blue noise (high quality)",
+    "Bayer matrix (ordered dither), 16x16 fixed size (fast)",
+    "Bayer matrix (ordered dither), any size",
+    "White noise (fast but low quality)",
+};
+
+#define DITHER_DEPTH_TEXT "Dither depth override (0 = auto)"
+#define DITHER_DEPTH_LONGTEXT "Overrides the detected framebuffer depth. Useful to dither to lower bit depths than otherwise required."
+
+#endif // VLC_PLACEBO_UTILS_H