diff --git a/DOCS/tech/slave.txt b/DOCS/tech/slave.txt index 0e906cf857..f68d5e9c1b 100644 --- a/DOCS/tech/slave.txt +++ b/DOCS/tech/slave.txt @@ -565,6 +565,9 @@ saturation int -100 100 X X X hue int -100 100 X X X panscan float 0 1 X X X vsync flag 0 1 X X X +colormatrix choice X X X as --colormatrix +colormatrix_input_range choice X X X as --colormatrix-input-range +colormatrix_output_range choice X X X as --colormatrix-output-range video_format int X video_codec string X video_bitrate int X diff --git a/cfg-mplayer.h b/cfg-mplayer.h index b197854df5..12d4b582c4 100644 --- a/cfg-mplayer.h +++ b/cfg-mplayer.h @@ -33,6 +33,7 @@ #include "libmpdemux/demux_ts.h" #include "stream/tv.h" #include "stream/stream_radio.h" +#include "libvo/csputils.h" extern char *fb_mode_cfgfile; extern char *fb_mode_name; @@ -797,6 +798,19 @@ const m_option_t mplayer_opts[]={ {"novsync", &vo_vsync, CONF_TYPE_FLAG, 0, 1, 0, NULL}, {"panscan", &vo_panscan, CONF_TYPE_FLOAT, CONF_RANGE, -1.0, 1.0, NULL}, OPT_FLOATRANGE("panscanrange", vo_panscanrange, 0, -19.0, 99.0), + OPT_CHOICE("colormatrix", requested_colorspace, 0, + ({"auto", MP_CSP_AUTO}, {"0", MP_CSP_AUTO}, + {"BT.601", MP_CSP_BT_601}, {"sd", MP_CSP_BT_601}, {"1", MP_CSP_BT_601}, + {"BT.709", MP_CSP_BT_709}, {"hd", MP_CSP_BT_709}, {"2", MP_CSP_BT_709}, + {"SMPTE-240M", MP_CSP_SMPTE_240M}, {"3", MP_CSP_SMPTE_240M})), + OPT_CHOICE("colormatrix-input-range", requested_input_range, 0, + ({"auto", MP_CSP_LEVELS_AUTO}, + {"limited", MP_CSP_LEVELS_TV}, + {"full", MP_CSP_LEVELS_PC})), + OPT_CHOICE("colormatrix-output-range", requested_output_range, 0, + ({"auto", MP_CSP_LEVELS_AUTO}, + {"limited", MP_CSP_LEVELS_TV}, + {"full", MP_CSP_LEVELS_PC})), {"grabpointer", &vo_grabpointer, CONF_TYPE_FLAG, 0, 0, 1, NULL}, {"nograbpointer", &vo_grabpointer, CONF_TYPE_FLAG, 0, 1, 0, NULL}, diff --git a/command.c b/command.c index e9fc49fd17..789b60186c 100644 --- a/command.c +++ b/command.c @@ -41,6 +41,7 @@ #include "libmpcodecs/vd.h" #include "mp_osd.h" #include "libvo/video_out.h" +#include "libvo/csputils.h" #include "sub/font_load.h" #include "playtree.h" #include "libao2/audio_out.h" @@ -1152,48 +1153,114 @@ static int mp_property_deinterlace(m_option_t *prop, int action, return m_property_flag_ro(prop, action, arg, value); } -static int mp_property_yuv_colorspace(m_option_t *prop, int action, +static int colormatrix_property_helper(m_option_t *prop, int action, void *arg, MPContext *mpctx) { - if (!mpctx->sh_video || !mpctx->sh_video->vfilter) - return M_PROPERTY_UNAVAILABLE; - - struct vf_instance *vf = mpctx->sh_video->vfilter; - int colorspace; + int r = mp_property_generic_option(prop, action, arg, mpctx); + // testing for an actual change is too much effort + switch (action) { + case M_PROPERTY_SET: + case M_PROPERTY_STEP_UP: + case M_PROPERTY_STEP_DOWN: + if (mpctx->sh_video) + set_video_colorspace(mpctx->sh_video); + break; + } + return r; +} + +static int mp_property_colormatrix(m_option_t *prop, int action, void *arg, + MPContext *mpctx) +{ + struct MPOpts *opts = &mpctx->opts; switch (action) { - case M_PROPERTY_GET: - if (!arg) - return M_PROPERTY_ERROR; - if (vf->control(vf, VFCTRL_GET_YUV_COLORSPACE, arg) != true) - return M_PROPERTY_UNAVAILABLE; - return M_PROPERTY_OK; case M_PROPERTY_PRINT: if (!arg) return M_PROPERTY_ERROR; - if (vf->control(vf, VFCTRL_GET_YUV_COLORSPACE, &colorspace) != true) - return M_PROPERTY_UNAVAILABLE; - char *const names[] = { - "BT.601 (SD)", "BT.709 (HD)", "SMPTE-240M" - }; - if (colorspace < 0 || colorspace >= sizeof(names) / sizeof(names[0])) - *(char **)arg = talloc_strdup(NULL, mp_gtext("unknown")); - else - *(char **)arg = talloc_strdup(NULL, names[colorspace]); + struct mp_csp_details actual = { .format = -1 }; + char *req_csp = mp_csp_names[opts->requested_colorspace]; + char *real_csp = NULL; + if (mpctx->sh_video) { + struct vf_instance *vf = mpctx->sh_video->vfilter; + if (vf->control(vf, VFCTRL_GET_YUV_COLORSPACE, &actual) == true) { + real_csp = mp_csp_names[actual.format]; + } else { + real_csp = "Unknown"; + } + } + char *res; + if (opts->requested_colorspace == MP_CSP_AUTO && real_csp) { + // Caveat: doesn't handle the case when the autodetected colorspace + // is different from the actual colorspace as used by the + // VO - the OSD will display the VO colorspace without + // indication that it doesn't match the requested colorspace. + res = talloc_asprintf(NULL, "Auto (%s)", real_csp); + } else if (opts->requested_colorspace == actual.format || !real_csp) { + res = talloc_strdup(NULL, req_csp); + } else + res = talloc_asprintf(NULL, mp_gtext("%s, but %s used"), + req_csp, real_csp); + *(char **)arg = res; return M_PROPERTY_OK; - case M_PROPERTY_SET: + default:; + return colormatrix_property_helper(prop, action, arg, mpctx); + } +} + +static int levels_property_helper(int offset, m_option_t *prop, int action, + void *arg, MPContext *mpctx) +{ + char *optname = prop->priv; + const struct m_option *opt = m_config_get_option(mpctx->mconfig, + bstr(optname)); + int *valptr = (int *)m_option_get_ptr(opt, &mpctx->opts); + + switch (action) { + case M_PROPERTY_PRINT: if (!arg) return M_PROPERTY_ERROR; - M_PROPERTY_CLAMP(prop, *(int *) arg); - vf->control(vf, VFCTRL_SET_YUV_COLORSPACE, arg); - return M_PROPERTY_OK; - case M_PROPERTY_STEP_UP:; - if (vf->control(vf, VFCTRL_GET_YUV_COLORSPACE, &colorspace) != true) - return M_PROPERTY_UNAVAILABLE; - colorspace += 1; - vf->control(vf, VFCTRL_SET_YUV_COLORSPACE, &colorspace); + struct mp_csp_details actual = {0}; + int actual_level = -1; + char *req_level = m_option_print(opt, valptr); + char *real_level = NULL; + if (mpctx->sh_video) { + struct vf_instance *vf = mpctx->sh_video->vfilter; + if (vf->control(vf, VFCTRL_GET_YUV_COLORSPACE, &actual) == true) { + actual_level = *(enum mp_csp_levels *)(((char *)&actual) + offset); + real_level = m_option_print(opt, &actual_level); + } else { + real_level = talloc_strdup(NULL, "Unknown"); + } + } + char *res; + if (*valptr == MP_CSP_LEVELS_AUTO && real_level) { + res = talloc_asprintf(NULL, "Auto (%s)", real_level); + } else if (*valptr == actual_level || !real_level) { + res = talloc_strdup(NULL, real_level); + } else + res = talloc_asprintf(NULL, mp_gtext("%s, but %s used"), + req_level, real_level); + talloc_free(req_level); + talloc_free(real_level); + *(char **)arg = res; return M_PROPERTY_OK; + default:; + return colormatrix_property_helper(prop, action, arg, mpctx); } - return M_PROPERTY_NOT_IMPLEMENTED; +} + +static int mp_property_colormatrix_input_range(m_option_t *prop, int action, + void *arg, MPContext *mpctx) +{ + return levels_property_helper(offsetof(struct mp_csp_details, levels_in), + prop, action, arg, mpctx); +} + +static int mp_property_colormatrix_output_range(m_option_t *prop, int action, + void *arg, MPContext *mpctx) +{ + return levels_property_helper(offsetof(struct mp_csp_details, levels_out), + prop, action, arg, mpctx); } static int mp_property_capture(m_option_t *prop, int action, @@ -2267,8 +2334,12 @@ static const m_option_t mp_properties[] = { M_OPT_RANGE, 0, 1, NULL }, { "deinterlace", mp_property_deinterlace, CONF_TYPE_FLAG, M_OPT_RANGE, 0, 1, NULL }, - { "yuv_colorspace", mp_property_yuv_colorspace, CONF_TYPE_INT, - M_OPT_RANGE, 0, 2, NULL }, + { "colormatrix", mp_property_colormatrix, &m_option_type_choice, + 0, 0, 0, "colormatrix" }, + { "colormatrix_input_range", mp_property_colormatrix_input_range, &m_option_type_choice, + 0, 0, 0, "colormatrix-input-range" }, + { "colormatrix_output_range", mp_property_colormatrix_output_range, &m_option_type_choice, + 0, 0, 0, "colormatrix-output-range" }, { "ontop", mp_property_ontop, CONF_TYPE_FLAG, M_OPT_RANGE, 0, 1, NULL }, { "rootwin", mp_property_rootwin, CONF_TYPE_FLAG, @@ -2430,7 +2501,9 @@ static struct property_osd_display { { "border", 0, -1, _("Border: %s") }, { "framedropping", 0, -1, _("Framedropping: %s") }, { "deinterlace", 0, -1, _("Deinterlace: %s") }, - { "yuv_colorspace", 0, -1, _("YUV colorspace: %s") }, + { "colormatrix", 0, -1, _("YUV colormatrix: %s") }, + { "colormatrix_input_range", 0, -1, _("YUV input range: %s") }, + { "colormatrix_output_range", 0, -1, _("RGB output range: %s") }, { "gamma", OSD_BRIGHTNESS, -1, _("Gamma") }, { "brightness", OSD_BRIGHTNESS, -1, _("Brightness") }, { "contrast", OSD_CONTRAST, -1, _("Contrast") }, @@ -2473,6 +2546,7 @@ static int show_property_osd(MPContext *mpctx, const char *pname) for (p = property_osd_display; p->name; p++) if (!strcmp(p->name, pname)) break; + if (!p->name) return -1; diff --git a/input/input.c b/input/input.c index 35714820e3..29b462a49f 100644 --- a/input/input.c +++ b/input/input.c @@ -467,7 +467,7 @@ static const struct cmd_bind def_cmd_binds[] = { { { '8', 0 }, "saturation 1" }, { { 'd', 0 }, "frame_drop" }, { { 'D', 0 }, "step_property_osd deinterlace" }, - { { 'c', 0 }, "step_property_osd yuv_colorspace" }, + { { 'c', 0 }, "step_property_osd colormatrix" }, { { 'r', 0 }, "sub_pos -1" }, { { 't', 0 }, "sub_pos +1" }, { { 'a', 0 }, "sub_alignment" }, diff --git a/libmpcodecs/dec_video.c b/libmpcodecs/dec_video.c index 8dbd733fd7..b41e4fb768 100644 --- a/libmpcodecs/dec_video.c +++ b/libmpcodecs/dec_video.c @@ -36,6 +36,7 @@ #include "codec-cfg.h" #include "libvo/video_out.h" +#include "libvo/csputils.h" #include "libmpdemux/stheader.h" #include "vd.h" @@ -139,6 +140,51 @@ int get_video_colors(sh_video_t *sh_video, const char *item, int *value) return 0; } +void get_detected_video_colorspace(struct sh_video *sh, struct mp_csp_details *csp) +{ + struct MPOpts *opts = sh->opts; + struct vf_instance *vf = sh->vfilter; + + csp->format = opts->requested_colorspace; + csp->levels_in = opts->requested_input_range; + csp->levels_out = opts->requested_output_range; + + if (csp->format == MP_CSP_AUTO) + csp->format = mp_csp_guess_colorspace(vf->w, vf->h); + if (csp->levels_in == MP_CSP_LEVELS_AUTO) + csp->levels_in = MP_CSP_LEVELS_TV; + if (csp->levels_out == MP_CSP_LEVELS_AUTO) + csp->levels_out = MP_CSP_LEVELS_PC; +} + +void set_video_colorspace(struct sh_video *sh) +{ + struct vf_instance *vf = sh->vfilter; + + struct mp_csp_details requested; + get_detected_video_colorspace(sh, &requested); + vf->control(vf, VFCTRL_SET_YUV_COLORSPACE, &requested); + + struct mp_csp_details actual = MP_CSP_DETAILS_DEFAULTS; + vf->control(vf, VFCTRL_GET_YUV_COLORSPACE, &actual); + + int success = actual.format == requested.format + && actual.levels_in == requested.levels_in + && actual.levels_out == requested.levels_out; + + if (!success) + mp_tmsg(MSGT_DECVIDEO, MSGL_WARN, + "Colorspace details not fully supported by selected vo.\n"); + + if (actual.format != requested.format + && requested.format == MP_CSP_SMPTE_240M) { + // BT.709 is pretty close, much better than BT.601 + requested.format = MP_CSP_BT_709; + vf->control(vf, VFCTRL_SET_YUV_COLORSPACE, &requested); + } + +} + int set_rectangle(sh_video_t *sh_video, int param, int value) { vf_instance_t *vf = sh_video->vfilter; diff --git a/libmpcodecs/dec_video.h b/libmpcodecs/dec_video.h index be087d09b9..60a9e5f2d2 100644 --- a/libmpcodecs/dec_video.h +++ b/libmpcodecs/dec_video.h @@ -40,6 +40,9 @@ void set_video_quality(sh_video_t *sh_video, int quality); int get_video_colors(sh_video_t *sh_video, const char *item, int *value); int set_video_colors(sh_video_t *sh_video, const char *item, int value); +struct mp_csp_details; +void get_detected_video_colorspace(struct sh_video *sh, struct mp_csp_details *csp); +void set_video_colorspace(struct sh_video *sh); int set_rectangle(sh_video_t *sh_video, int param, int value); int redraw_osd(struct sh_video *sh_video, struct osd_state *osd); void resync_video_stream(sh_video_t *sh_video); diff --git a/libmpcodecs/vd.c b/libmpcodecs/vd.c index 3ba72790a5..21c940b9be 100644 --- a/libmpcodecs/vd.c +++ b/libmpcodecs/vd.c @@ -327,6 +327,7 @@ int mpcodecs_config_vo2(sh_video_t *sh, int w, int h, vf->w = sh->disp_w; vf->h = sh->disp_h; + if (vf_config_wrapper (vf, sh->disp_w, sh->disp_h, screen_size_x, screen_size_y, vocfg_flags, out_fmt) == 0) { @@ -337,6 +338,8 @@ int mpcodecs_config_vo2(sh_video_t *sh, int w, int h, sh->vf_initialized = 1; + set_video_colorspace(sh); + if (opts->vo_gamma_gamma != 1000) set_video_colors(sh, "gamma", opts->vo_gamma_gamma); if (opts->vo_gamma_brightness != 1000) diff --git a/libmpcodecs/vf.h b/libmpcodecs/vf.h index ec1a9c23ff..11ee5eaf8e 100644 --- a/libmpcodecs/vf.h +++ b/libmpcodecs/vf.h @@ -113,8 +113,8 @@ typedef struct vf_seteq_s * access OSD/subtitle state outside of normal OSD draw time. */ #define VFCTRL_SET_OSD_OBJ 20 #define VFCTRL_REDRAW_OSD 21 /* Change user-visible OSD immediately */ -#define VFCTRL_SET_YUV_COLORSPACE 22 -#define VFCTRL_GET_YUV_COLORSPACE 23 +#define VFCTRL_SET_YUV_COLORSPACE 22 /* arg is struct mp_csp_details* */ +#define VFCTRL_GET_YUV_COLORSPACE 23 /* arg is struct mp_csp_details* */ #include "vfcap.h" diff --git a/libmpcodecs/vf_scale.c b/libmpcodecs/vf_scale.c index 1a2cb28e94..8cd0a37c33 100644 --- a/libmpcodecs/vf_scale.c +++ b/libmpcodecs/vf_scale.c @@ -35,6 +35,8 @@ #include "libswscale/swscale.h" #include "vf_scale.h" +#include "libvo/csputils.h" + #include "m_option.h" #include "m_struct.h" @@ -49,6 +51,7 @@ static struct vf_priv_s { int interlaced; int noup; int accurate_rnd; + struct mp_csp_details colorspace; } const vf_priv_dflt = { -1,-1, 0, @@ -187,6 +190,8 @@ static int config(struct vf_instance *vf, SwsFilter *srcFilter, *dstFilter; enum PixelFormat dfmt, sfmt; + vf->priv->colorspace = (struct mp_csp_details) {0}; + if(!best){ mp_msg(MSGT_VFILTER,MSGL_WARN,"SwScale: no supported outfmt found :(\n"); return 0; @@ -505,6 +510,32 @@ static int control(struct vf_instance *vf, int request, void* data){ } return CONTROL_TRUE; + case VFCTRL_SET_YUV_COLORSPACE: { + struct mp_csp_details colorspace = *(struct mp_csp_details *)data; + if (mp_sws_set_colorspace(vf->priv->ctx, &colorspace) >= 0) { + if (vf->priv->ctx2) + mp_sws_set_colorspace(vf->priv->ctx2, &colorspace); + vf->priv->colorspace = colorspace; + return 1; + } + break; + } + case VFCTRL_GET_YUV_COLORSPACE: { + /* This scale filter should never react to colorspace commands if it + * doesn't do YUV->RGB conversion. But because finding out whether this + * is really YUV->RGB (and not YUV->YUV or anything else) is hard, + * react only if the colorspace has been set explicitly before. The + * trick is that mp_sws_set_colorspace does not succeed for YUV->YUV + * and RGB->YUV conversions, which makes this code correct in "most" + * cases. (This would be trivial to do correctly if libswscale exposed + * functionality like isYUV()). + */ + if (vf->priv->colorspace.format) { + *(struct mp_csp_details *)data = vf->priv->colorspace; + return CONTROL_TRUE; + } + break; + } default: break; } @@ -512,6 +543,54 @@ static int control(struct vf_instance *vf, int request, void* data){ return vf_next_control(vf,request,data); } +static const int mp_csp_to_swscale[MP_CSP_COUNT] = { + [MP_CSP_BT_601] = SWS_CS_ITU601, + [MP_CSP_BT_709] = SWS_CS_ITU709, + [MP_CSP_SMPTE_240M] = SWS_CS_SMPTE240M, +}; + +// Adjust the colorspace used for YUV->RGB conversion. On other conversions, +// do nothing or return an error. +// The csp argument is set to the supported values. +// Return 0 on success and -1 on error. +int mp_sws_set_colorspace(struct SwsContext *sws, struct mp_csp_details *csp) +{ + int *table, *inv_table; + int brightness, contrast, saturation, srcRange, dstRange; + + csp->levels_out = MP_CSP_LEVELS_PC; + + // NOTE: returns an error if the destination format is YUV + if (sws_getColorspaceDetails(sws, &inv_table, &srcRange, &table, &dstRange, + &brightness, &contrast, &saturation) == -1) + goto error_out; + + int sws_csp = mp_csp_to_swscale[csp->format]; + if (sws_csp == 0) { + // colorspace not supported, go with a reasonable default + csp->format = SWS_CS_ITU601; + sws_csp = MP_CSP_BT_601; + } + + /* The swscale API for these is hardly documented. + * Apparently table/range only apply to YUV. Thus dstRange has no effect + * for YUV->RGB conversions, and conversions to limited-range RGB are + * not supported. + */ + srcRange = csp->levels_in == MP_CSP_LEVELS_PC; + const int *new_inv_table = sws_getCoefficients(sws_csp); + + if (sws_setColorspaceDetails(sws, new_inv_table, srcRange, table, dstRange, + brightness, contrast, saturation) == -1) + goto error_out; + + return 0; + +error_out: + *csp = (struct mp_csp_details){0}; + return -1; +} + //===========================================================================// // supported Input formats: YV12, I420, IYUV, YUY2, UYVY, BGR32, BGR24, BGR16, BGR15, RGB32, RGB24, Y8, Y800 diff --git a/libmpcodecs/vf_scale.h b/libmpcodecs/vf_scale.h index 359ed2c4f8..a9b3b506d5 100644 --- a/libmpcodecs/vf_scale.h +++ b/libmpcodecs/vf_scale.h @@ -22,4 +22,7 @@ int get_sws_cpuflags(void); struct SwsContext *sws_getContextFromCmdLine(int srcW, int srcH, int srcFormat, int dstW, int dstH, int dstFormat); +struct mp_csp_details; +int mp_sws_set_colorspace(struct SwsContext *sws, struct mp_csp_details *csp); + #endif /* MPLAYER_VF_SCALE_H */ diff --git a/libvo/csputils.c b/libvo/csputils.c index 831e5c774a..6656e5e196 100644 --- a/libvo/csputils.c +++ b/libvo/csputils.c @@ -32,6 +32,27 @@ #include "csputils.h" +char * const mp_csp_names[MP_CSP_COUNT] = { + "Autoselect", + "BT.601 (SD)", + "BT.709 (HD)", + "SMPTE-240M", +}; + +char * const mp_csp_equalizer_names[MP_CSP_EQ_COUNT] = { + "brightness", + "contrast", + "hue", + "saturation", + "gamma", +}; + + +enum mp_csp mp_csp_guess_colorspace(int width, int height) +{ + return width >= 1280 || height > 576 ? MP_CSP_BT_709 : MP_CSP_BT_601; +} + /** * \brief little helper function to create a lookup table for gamma * \param map buffer to create map into @@ -98,8 +119,8 @@ static void luma_coeffs(float m[3][4], float lr, float lg, float lb) */ void mp_get_yuv2rgb_coeffs(struct mp_csp_params *params, float m[3][4]) { - int format = params->format; - if (format <= MP_CSP_DEFAULT || format >= MP_CSP_COUNT) + int format = params->colorspace.format; + if (format <= MP_CSP_AUTO || format >= MP_CSP_COUNT) format = MP_CSP_BT_601; switch (format) { case MP_CSP_BT_601: luma_coeffs(m, 0.299, 0.587, 0.114 ); break; @@ -119,25 +140,35 @@ void mp_get_yuv2rgb_coeffs(struct mp_csp_params *params, float m[3][4]) m[i][COL_V] = huesin * u + huecos * m[i][COL_V]; } - int levelconv = params->levelconv; - if (levelconv < 0 || levelconv >= MP_CSP_LEVELCONV_COUNT) - levelconv = MP_CSP_LEVELCONV_TV_TO_PC; + int levels_in = params->colorspace.levels_in; + if (levels_in <= MP_CSP_LEVELS_AUTO || levels_in >= MP_CSP_LEVELS_COUNT) + levels_in = MP_CSP_LEVELS_TV; // The values below are written in 0-255 scale struct yuvlevels { double ymin, ymax, cmin, cmid; } yuvlim = { 16, 235, 16, 128 }, - yuvfull = { 16, 235, 1, 128 }, // '1' to make it symmetric around 128 + yuvfull = { 0, 255, 1, 128 }, // '1' to make it symmetric around 128 yuvlev; + switch (levels_in) { + case MP_CSP_LEVELS_TV: yuvlev = yuvlim; break; + case MP_CSP_LEVELS_PC: yuvlev = yuvfull; break; + default: + abort(); + } + + int levels_out = params->colorspace.levels_out; + if (levels_out <= MP_CSP_LEVELS_AUTO || levels_out >= MP_CSP_LEVELS_COUNT) + levels_out = MP_CSP_LEVELS_PC; struct rgblevels { double min, max; } rgblim = { 16, 235 }, rgbfull = { 0, 255 }, rgblev; - switch (levelconv) { - case MP_CSP_LEVELCONV_TV_TO_PC: yuvlev = yuvlim; rgblev = rgbfull; break; - case MP_CSP_LEVELCONV_PC_TO_TV: yuvlev = yuvfull; rgblev = rgblim; break; - case MP_CSP_LEVELCONV_TV_TO_TV: yuvlev = yuvlim; rgblev = rgblim; break; + switch (levels_out) { + case MP_CSP_LEVELS_TV: rgblev = rgblim; break; + case MP_CSP_LEVELS_PC: rgblev = rgbfull; break; default: abort(); } + double ymul = (rgblev.max - rgblev.min) / (yuvlev.ymax - yuvlev.ymin); double cmul = (rgblev.max - rgblev.min) / (yuvlev.cmid - yuvlev.cmin) / 2; for (int i = 0; i < 3; i++) { @@ -205,3 +236,50 @@ void mp_gen_yuv2rgb_map(struct mp_csp_params *params, unsigned char *map, int si v += (i == -1 || i == size - 1) ? step / 2 : step; } } + +// Copy settings from eq into params. +void mp_csp_copy_equalizer_values(struct mp_csp_params *params, + const struct mp_csp_equalizer *eq) +{ + params->brightness = eq->values[MP_CSP_EQ_BRIGHTNESS] / 100.0; + params->contrast = (eq->values[MP_CSP_EQ_CONTRAST] + 100) / 100.0; + params->hue = eq->values[MP_CSP_EQ_HUE] / 100.0 * 3.1415927; + params->saturation = (eq->values[MP_CSP_EQ_SATURATION] + 100) / 100.0; + float gamma = exp(log(8.0) * eq->values[MP_CSP_EQ_GAMMA] / 100.0); + params->rgamma = gamma; + params->ggamma = gamma; + params->bgamma = gamma; +} + +static int find_eq(int capabilities, const char *name) +{ + for (int i = 0; i < MP_CSP_EQ_COUNT; i++) { + if (strcmp(name, mp_csp_equalizer_names[i]) == 0) + return ((1 << i) & capabilities) ? i : -1; + } + return -1; +} + +int mp_csp_equalizer_get(struct mp_csp_equalizer *eq, const char *property, + int *out_value) +{ + int index = find_eq(eq->capabilities, property); + if (index < 0) + return -1; + + *out_value = eq->values[index]; + + return 0; +} + +int mp_csp_equalizer_set(struct mp_csp_equalizer *eq, const char *property, + int value) +{ + int index = find_eq(eq->capabilities, property); + if (index < 0) + return 0; + + eq->values[index] = value; + + return 1; +} diff --git a/libvo/csputils.h b/libvo/csputils.h index 93f9ca0316..3a754a8273 100644 --- a/libvo/csputils.h +++ b/libvo/csputils.h @@ -26,24 +26,41 @@ #include -enum mp_csp_standard { - MP_CSP_DEFAULT, + +/* NOTE: the csp and levels AUTO values are converted to specific ones + * above vf/vo level. At least vf_scale relies on all valid settings being + * nonzero at vf/vo level. + */ + +enum mp_csp { + MP_CSP_AUTO, MP_CSP_BT_601, MP_CSP_BT_709, MP_CSP_SMPTE_240M, MP_CSP_COUNT }; -enum mp_csp_levelconv { - MP_CSP_LEVELCONV_TV_TO_PC, - MP_CSP_LEVELCONV_PC_TO_TV, - MP_CSP_LEVELCONV_TV_TO_TV, - MP_CSP_LEVELCONV_COUNT +// Any enum mp_csp value is a valid index (except MP_CSP_COUNT) +extern char * const mp_csp_names[MP_CSP_COUNT]; + +enum mp_csp_levels { + MP_CSP_LEVELS_AUTO, + MP_CSP_LEVELS_TV, + MP_CSP_LEVELS_PC, + MP_CSP_LEVELS_COUNT, }; +struct mp_csp_details { + enum mp_csp format; + enum mp_csp_levels levels_in; // encoded video + enum mp_csp_levels levels_out; // output device +}; + +// initializer for struct mp_csp_details that contains reasonable defaults +#define MP_CSP_DETAILS_DEFAULTS {MP_CSP_BT_601, MP_CSP_LEVELS_TV, MP_CSP_LEVELS_PC} + struct mp_csp_params { - enum mp_csp_standard format; - enum mp_csp_levelconv levelconv; + struct mp_csp_details colorspace; float brightness; float contrast; float hue; @@ -54,6 +71,47 @@ struct mp_csp_params { int input_shift; }; +enum mp_csp_equalizer_param { + MP_CSP_EQ_BRIGHTNESS, + MP_CSP_EQ_CONTRAST, + MP_CSP_EQ_HUE, + MP_CSP_EQ_SATURATION, + MP_CSP_EQ_GAMMA, + MP_CSP_EQ_COUNT, +}; + +#define MP_CSP_EQ_CAPS_COLORMATRIX \ + ( (1 << MP_CSP_EQ_BRIGHTNESS) \ + | (1 << MP_CSP_EQ_CONTRAST) \ + | (1 << MP_CSP_EQ_HUE) \ + | (1 << MP_CSP_EQ_SATURATION) ) + +#define MP_CSP_EQ_CAPS_GAMMA (1 << MP_CSP_EQ_GAMMA) + +extern char * const mp_csp_equalizer_names[MP_CSP_EQ_COUNT]; + +// Default initialization with 0 is enough, except for the capabilities field +struct mp_csp_equalizer { + // Bit field of capabilities. For example (1 << MP_CSP_EQ_HUE) means hue + // support is available. + int capabilities; + // Value for each property is in the range [-100, 100]. + // 0 is default, meaning neutral or no change. + int values[MP_CSP_EQ_COUNT]; +}; + + +void mp_csp_copy_equalizer_values(struct mp_csp_params *params, + const struct mp_csp_equalizer *eq); + +int mp_csp_equalizer_set(struct mp_csp_equalizer *eq, const char *property, + int value); + +int mp_csp_equalizer_get(struct mp_csp_equalizer *eq, const char *property, + int *out_value); + +enum mp_csp mp_csp_guess_colorspace(int width, int height); + void mp_gen_gamma_map(unsigned char *map, int size, float gamma); #define ROW_R 0 #define ROW_G 1 diff --git a/libvo/video_out.h b/libvo/video_out.h index ca249819ff..d1a2a7f65a 100644 --- a/libvo/video_out.h +++ b/libvo/video_out.h @@ -76,8 +76,8 @@ enum mp_voctrl { VOCTRL_UPDATE_SCREENINFO, - VOCTRL_SET_YUV_COLORSPACE, - VOCTRL_GET_YUV_COLORSPACE, + VOCTRL_SET_YUV_COLORSPACE, // struct mp_csp_details + VOCTRL_GET_YUV_COLORSPACE, // struct mp_csp_details }; // VOCTRL_SET_EQUALIZER @@ -98,7 +98,7 @@ typedef struct { uint16_t r,g,b; } mp_colorkey_t; -//VOCTRL_GET_EOSD_RES +// VOCTRL_GET_EOSD_RES typedef struct mp_eosd_res { int w, h; // screen dimensions, including black borders int mt, mb, ml, mr; // borders (top, bottom, left, right) diff --git a/libvo/vo_gl.c b/libvo/vo_gl.c index cefa30cba3..cb1ccc593b 100644 --- a/libvo/vo_gl.c +++ b/libvo/vo_gl.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "config.h" #include "mp_msg.h" @@ -129,8 +130,9 @@ static int use_ycbcr; #define MASK_NOT_COMBINERS (~((1 << YUV_CONVERSION_NONE) | (1 << YUV_CONVERSION_COMBINERS))) #define MASK_GAMMA_SUPPORT (MASK_NOT_COMBINERS & ~(1 << YUV_CONVERSION_FRAGMENT)) static int use_yuv; -static int colorspace; -static int levelconv; +static struct mp_csp_details colorspace = MP_CSP_DETAILS_DEFAULTS; +static int user_colorspace; //essentially unused; legacy warning +static int levelconv; //essentially unused; legacy warning static int is_yuv; static int lscale; static int cscale; @@ -169,13 +171,8 @@ static int mipmap_gen; static int stereo_mode; static int int_pause; -static int eq_bri = 0; -static int eq_cont = 0; -static int eq_sat = 0; -static int eq_hue = 0; -static int eq_rgamma = 0; -static int eq_ggamma = 0; -static int eq_bgamma = 0; + +static struct mp_csp_equalizer video_eq; static int texture_width; static int texture_height; @@ -248,15 +245,9 @@ static void texSize(int w, int h, int *texw, int *texh) { #define MAX_CUSTOM_PROG_SIZE (1024 * 1024) static void update_yuvconv(void) { int xs, ys, depth; - float bri = eq_bri / 100.0; - float cont = (eq_cont + 100) / 100.0; - float hue = eq_hue / 100.0 * 3.1415927; - float sat = (eq_sat + 100) / 100.0; - float rgamma = exp(log(8.0) * eq_rgamma / 100.0); - float ggamma = exp(log(8.0) * eq_ggamma / 100.0); - float bgamma = exp(log(8.0) * eq_bgamma / 100.0); - gl_conversion_params_t params = {gl_target, yuvconvtype, - {colorspace, levelconv, bri, cont, hue, sat, rgamma, ggamma, bgamma, 0}, + struct mp_csp_params cparams = { .colorspace = colorspace }; + mp_csp_copy_equalizer_values(&cparams, &video_eq); + gl_conversion_params_t params = {gl_target, yuvconvtype, cparams, texture_width, texture_height, 0, 0, filter_strength}; mp_get_chroma_shift(image_format, &xs, &ys, &depth); params.chrom_texw = params.texw >> xs; @@ -521,6 +512,19 @@ static void autodetectGlExtensions(void) { use_osd = mpglBindTexture != NULL; if (use_yuv == -1) use_yuv = glAutodetectYUVConversion(); + + int eq_caps = 0; + int yuv_mask = (1 << use_yuv); + if (!(yuv_mask & MASK_NOT_COMBINERS)) { + // combiners + eq_caps = (1 << MP_CSP_EQ_HUE) | (1 << MP_CSP_EQ_SATURATION); + } else if (yuv_mask & MASK_ALL_YUV) { + eq_caps = MP_CSP_EQ_CAPS_COLORMATRIX; + if (yuv_mask & MASK_GAMMA_SUPPORT) + eq_caps |= MP_CSP_EQ_CAPS_GAMMA; + } + video_eq.capabilities = eq_caps; + if (is_ati && (lscale == 1 || lscale == 2 || cscale == 1 || cscale == 2)) mp_msg(MSGT_VO, MSGL_WARN, "[gl] Selected scaling mode may be broken on ATI cards.\n" "Tell _them_ to fix GL_REPEAT if you have issues.\n"); @@ -1133,18 +1137,6 @@ uninit(void) uninit_mpglcontext(&glctx); } -static int valid_csp(void *p) -{ - int *csp = p; - return *csp >= -1 && *csp < MP_CSP_COUNT; -} - -static int valid_csp_lvl(void *p) -{ - int *lvl = p; - return *lvl >= -1 && *lvl < MP_CSP_LEVELCONV_COUNT; -} - static const opt_t subopts[] = { {"manyfmts", OPT_ARG_BOOL, &many_fmts, NULL}, {"osd", OPT_ARG_BOOL, &use_osd, NULL}, @@ -1154,8 +1146,8 @@ static const opt_t subopts[] = { {"slice-height", OPT_ARG_INT, &slice_height, int_non_neg}, {"rectangle", OPT_ARG_INT, &use_rectangle,int_non_neg}, {"yuv", OPT_ARG_INT, &use_yuv, int_non_neg}, - {"colorspace", OPT_ARG_INT, &colorspace, valid_csp}, - {"levelconv", OPT_ARG_INT, &levelconv, valid_csp_lvl}, + {"colorspace", OPT_ARG_INT, &user_colorspace, NULL}, + {"levelconv", OPT_ARG_INT, &levelconv, NULL}, {"lscale", OPT_ARG_INT, &lscale, int_non_neg}, {"cscale", OPT_ARG_INT, &cscale, int_non_neg}, {"filter-strength", OPT_ARG_FLOAT, &filter_strength, NULL}, @@ -1184,7 +1176,7 @@ static int preinit_internal(const char *arg, int allow_sw) use_aspect = 1; use_ycbcr = 0; use_yuv = -1; - colorspace = -1; + user_colorspace = 0; levelconv = -1; lscale = 0; cscale = 0; @@ -1242,17 +1234,6 @@ static int preinit_internal(const char *arg, int allow_sw) " 4: use fragment program with gamma correction via lookup.\n" " 5: use ATI-specific method (for older cards).\n" " 6: use lookup via 3D texture.\n" - " colorspace=\n" - " 0: MPlayer's default YUV to RGB conversion\n" - " 1: YUV to RGB according to BT.601\n" - " 2: YUV to RGB according to BT.709\n" - " 3: YUV to RGB according to SMPT-240M\n" - " 4: YUV to RGB according to EBU\n" - " 5: XYZ to RGB\n" - " levelconv=\n" - " 0: YUV to RGB converting TV to PC levels\n" - " 1: YUV to RGB converting PC to TV levels\n" - " 2: YUV to RGB without converting levels\n" " lscale=\n" " 0: use standard bilinear scaling for luma.\n" " 1: use improved bicubic scaling for luma.\n" @@ -1284,6 +1265,12 @@ static int preinit_internal(const char *arg, int allow_sw) "\n" ); return -1; } + if (user_colorspace != 0 || levelconv != -1) { + mp_msg(MSGT_VO, MSGL_ERR, "[gl] \"colorspace\" and \"levelconv\" " + "suboptions have been removed. Use options --colormatrix and" + " --colormatrix-input-range/--colormatrix-output-range instead.\n"); + return -1; + } if (!init_mpglcontext(&glctx, gltype)) goto err_out; if (use_yuv == -1 || !allow_sw) { @@ -1318,22 +1305,6 @@ static int preinit_nosw(const char *arg) return preinit_internal(arg, 0); } -static const struct { - const char *name; - int *value; - int supportmask; -} eq_map[] = { - {"brightness", &eq_bri, MASK_NOT_COMBINERS}, - {"contrast", &eq_cont, MASK_NOT_COMBINERS}, - {"saturation", &eq_sat, MASK_ALL_YUV }, - {"hue", &eq_hue, MASK_ALL_YUV }, - {"gamma", &eq_rgamma, MASK_GAMMA_SUPPORT}, - {"red_gamma", &eq_rgamma, MASK_GAMMA_SUPPORT}, - {"green_gamma", &eq_ggamma, MASK_GAMMA_SUPPORT}, - {"blue_gamma", &eq_bgamma, MASK_GAMMA_SUPPORT}, - {NULL, NULL, 0 } -}; - static int control(uint32_t request, void *data) { switch (request) { @@ -1386,30 +1357,30 @@ static int control(uint32_t request, void *data) case VOCTRL_GET_EQUALIZER: if (is_yuv) { struct voctrl_get_equalizer_args *args = data; - int i; - for (i = 0; eq_map[i].name; i++) - if (strcmp(args->name, eq_map[i].name) == 0) break; - if (!(eq_map[i].supportmask & (1 << use_yuv))) - break; - *args->valueptr = *eq_map[i].value; - return VO_TRUE; + return mp_csp_equalizer_get(&video_eq, args->name, args->valueptr) >= 0 ? + VO_TRUE : VO_NOTIMPL; } break; case VOCTRL_SET_EQUALIZER: if (is_yuv) { struct voctrl_set_equalizer_args *args = data; - int i; - for (i = 0; eq_map[i].name; i++) - if (strcmp(args->name, eq_map[i].name) == 0) break; - if (!(eq_map[i].supportmask & (1 << use_yuv))) - break; - *eq_map[i].value = args->value; - if (strcmp(args->name, "gamma") == 0) - eq_rgamma = eq_ggamma = eq_bgamma = args->value; + if (mp_csp_equalizer_set(&video_eq, args->name, args->value) < 0) + return VO_NOTIMPL; update_yuvconv(); return VO_TRUE; } break; + case VOCTRL_SET_YUV_COLORSPACE: { + bool supports_csp = (1 << use_yuv) & MASK_NOT_COMBINERS; + if (vo_config_count && supports_csp) { + colorspace = *(struct mp_csp_details *)data; + update_yuvconv(); + } + return VO_TRUE; + } + case VOCTRL_GET_YUV_COLORSPACE: + *(struct mp_csp_details *)data = colorspace; + return VO_TRUE; case VOCTRL_UPDATE_SCREENINFO: glctx.update_xinerama_info(); return VO_TRUE; diff --git a/libvo/vo_gl2.c b/libvo/vo_gl2.c index ac1d957136..6dd9597412 100644 --- a/libvo/vo_gl2.c +++ b/libvo/vo_gl2.c @@ -565,7 +565,7 @@ static int initGl(uint32_t d_width, uint32_t d_height) if (is_yuv) { int xs, ys, depth; gl_conversion_params_t params = {GL_TEXTURE_2D, use_yuv, - {-1, -1, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0}, + { { -1 }, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0}, texture_width, texture_height, 0, 0, 0}; switch (use_yuv) { case YUV_CONVERSION_FRAGMENT_LOOKUP: diff --git a/libvo/vo_vdpau.c b/libvo/vo_vdpau.c index f487b3b2a3..322645ebd3 100644 --- a/libvo/vo_vdpau.c +++ b/libvo/vo_vdpau.c @@ -41,6 +41,7 @@ #include "video_out.h" #include "x11_common.h" #include "aspect.h" +#include "csputils.h" #include "sub/sub.h" #include "subopt-helper.h" #include "libmpcodecs/vfcap.h" @@ -124,9 +125,7 @@ struct vdpctx { int output_surface_width, output_surface_height; VdpVideoMixer video_mixer; - int user_colorspace; - int colorspace; - int studio_levels; + struct mp_csp_details colorspace; int deint; int deint_type; int deint_counter; @@ -190,7 +189,7 @@ struct vdpctx { int eosd_render_count; // Video equalizer - VdpProcamp procamp; + struct mp_csp_equalizer video_eq; int num_shown_frames; bool paused; @@ -574,33 +573,16 @@ static int set_video_attribute(struct vdpctx *vc, VdpVideoMixerAttribute attr, static void update_csc_matrix(struct vo *vo) { struct vdpctx *vc = vo->priv; - struct vdp_functions *vdp = vc->vdp; - VdpStatus vdp_st; - const VdpColorStandard vdp_colors[] = {VDP_COLOR_STANDARD_ITUR_BT_601, - VDP_COLOR_STANDARD_ITUR_BT_709, - VDP_COLOR_STANDARD_SMPTE_240M}; - char * const vdp_names[] = {"BT.601", "BT.709", "SMPTE-240M"}; - int csp = vc->colorspace; - mp_msg(MSGT_VO, MSGL_V, "[vdpau] Updating CSC matrix for %s\n", - vdp_names[csp]); + mp_msg(MSGT_VO, MSGL_V, "[vdpau] Updating CSC matrix\n"); + // VdpCSCMatrix happens to be compatible with mplayer's CSC matrix type + // both are float[3][4] VdpCSCMatrix matrix; - vdp_st = vdp->generate_csc_matrix(&vc->procamp, vdp_colors[csp], &matrix); - CHECK_ST_WARNING("Error when generating CSC matrix"); - if (vc->studio_levels) { - /* Modify matrix to change output range from 0..255 to 16..235. - * Clipping limits can't be changed, so out-of-range results that - * would have been clipped to 0 or 255 before can still go below - * 16 or above 235. - */ - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 4; j++) - matrix[i][j] *= 220. / 256; - matrix[i][3] += 16. / 256; - } - } + struct mp_csp_params cparams = { .colorspace = vc->colorspace }; + mp_csp_copy_equalizer_values(&cparams, &vc->video_eq); + mp_get_yuv2rgb_coeffs(&cparams, matrix); set_video_attribute(vc, VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX, &matrix, "CSC matrix"); @@ -886,10 +868,6 @@ static int config(struct vo *vo, uint32_t width, uint32_t height, vc->image_format = format; vc->vid_width = width; vc->vid_height = height; - if (vc->user_colorspace == 0) - vc->colorspace = width >= 1280 || height > 576 ? 1 : 0; - else - vc->colorspace = vc->user_colorspace - 1; free_video_specific(vo); if (IMGFMT_IS_VDPAU(vc->image_format) && !create_vdp_decoder(vo, 2)) return -1; @@ -1607,6 +1585,8 @@ static void uninit(struct vo *vo) static int preinit(struct vo *vo, const char *arg) { int i; + int user_colorspace = 0; + int studio_levels = 0; struct vdpctx *vc = talloc_zero(vo, struct vdpctx); vo->priv = vc; @@ -1615,20 +1595,21 @@ static int preinit(struct vo *vo, const char *arg) // allocated mark_vdpau_objects_uninitialized(vo); + vc->colorspace = (struct mp_csp_details) MP_CSP_DETAILS_DEFAULTS; vc->deint_type = 3; vc->chroma_deint = 1; - vc->user_colorspace = 1; vc->flip_offset_window = 50; vc->flip_offset_fs = 50; vc->num_output_surfaces = 3; + vc->video_eq.capabilities = MP_CSP_EQ_CAPS_COLORMATRIX; const opt_t subopts[] = { {"deint", OPT_ARG_INT, &vc->deint, NULL}, {"chroma-deint", OPT_ARG_BOOL, &vc->chroma_deint, NULL}, {"pullup", OPT_ARG_BOOL, &vc->pullup, NULL}, {"denoise", OPT_ARG_FLOAT, &vc->denoise, NULL}, {"sharpen", OPT_ARG_FLOAT, &vc->sharpen, NULL}, - {"colorspace", OPT_ARG_INT, &vc->user_colorspace, NULL}, - {"studio", OPT_ARG_BOOL, &vc->studio_levels, NULL}, + {"colorspace", OPT_ARG_INT, &user_colorspace, NULL}, + {"studio", OPT_ARG_BOOL, &studio_levels, NULL}, {"hqscaling", OPT_ARG_INT, &vc->hqscaling, NULL}, {"fps", OPT_ARG_FLOAT, &vc->user_fps, NULL}, {"queuetime_windowed", OPT_ARG_INT, &vc->flip_offset_window, NULL}, @@ -1650,6 +1631,12 @@ static int preinit(struct vo *vo, const char *arg) "output_surfaces: can't use less than 2 surfaces\n"); return -1; } + if (user_colorspace != 0 || studio_levels != 0) { + mp_msg(MSGT_VO, MSGL_ERR, "[vdpau] \"colorspace\" and \"studio\"" + " suboptions have been removed. Use options --colormatrix and" + " --colormatrix-output-range=limited instead.\n"); + return -1; + } if (vc->num_output_surfaces > MAX_OUTPUT_SURFACES) { mp_msg(MSGT_VO, MSGL_WARN, "[vdpau] Number of output surfaces " "is limited to %d.\n", MAX_OUTPUT_SURFACES); @@ -1676,30 +1663,14 @@ static int preinit(struct vo *vo, const char *arg) for (i = 0; i < PALETTE_SIZE; ++i) vc->palette[i] = (i << 16) | (i << 8) | i; - vc->procamp.struct_version = VDP_PROCAMP_VERSION; - vc->procamp.brightness = 0.0; - vc->procamp.contrast = 1.0; - vc->procamp.saturation = 1.0; - vc->procamp.hue = 0.0; - return 0; } static int get_equalizer(struct vo *vo, const char *name, int *value) { struct vdpctx *vc = vo->priv; - - if (!strcasecmp(name, "brightness")) - *value = vc->procamp.brightness * 100; - else if (!strcasecmp(name, "contrast")) - *value = (vc->procamp.contrast - 1.0) * 100; - else if (!strcasecmp(name, "saturation")) - *value = (vc->procamp.saturation - 1.0) * 100; - else if (!strcasecmp(name, "hue")) - *value = vc->procamp.hue * 100 / M_PI; - else - return VO_NOTIMPL; - return VO_TRUE; + return mp_csp_equalizer_get(&vc->video_eq, name, value) >= 0 ? + VO_TRUE : VO_NOTIMPL; } static bool status_ok(struct vo *vo) @@ -1713,15 +1684,7 @@ static int set_equalizer(struct vo *vo, const char *name, int value) { struct vdpctx *vc = vo->priv; - if (!strcasecmp(name, "brightness")) - vc->procamp.brightness = value / 100.0; - else if (!strcasecmp(name, "contrast")) - vc->procamp.contrast = value / 100.0 + 1.0; - else if (!strcasecmp(name, "saturation")) - vc->procamp.saturation = value / 100.0 + 1.0; - else if (!strcasecmp(name, "hue")) - vc->procamp.hue = value / 100.0 * M_PI; - else + if (mp_csp_equalizer_set(&vc->video_eq, name, value) < 0) return VO_NOTIMPL; if (status_ok(vo)) @@ -1798,12 +1761,12 @@ static int control(struct vo *vo, uint32_t request, void *data) return get_equalizer(vo, args->name, args->valueptr); } case VOCTRL_SET_YUV_COLORSPACE: - vc->colorspace = *(int *)data % 3; + vc->colorspace = *(struct mp_csp_details *)data; if (status_ok(vo)) update_csc_matrix(vo); return true; case VOCTRL_GET_YUV_COLORSPACE: - *(int *)data = vc->colorspace; + *(struct mp_csp_details *)data = vc->colorspace; return true; case VOCTRL_ONTOP: vo_x11_ontop(vo); diff --git a/libvo/vo_xv.c b/libvo/vo_xv.c index 64324c9785..3c8b040195 100644 --- a/libvo/vo_xv.c +++ b/libvo/vo_xv.c @@ -58,6 +58,7 @@ Buffer allocation: #include "fastmemcpy.h" #include "sub/sub.h" #include "aspect.h" +#include "csputils.h" #include "subopt-helper.h" @@ -809,13 +810,16 @@ static int control(struct vo *vo, uint32_t request, void *data) return vo_xv_get_eq(vo, x11->xv_port, args->name, args->valueptr); } case VOCTRL_SET_YUV_COLORSPACE:; - int given_cspc = *(int *)data % 2; - return vo_xv_set_eq(vo, x11->xv_port, "bt_709", given_cspc * 200 - 100); + struct mp_csp_details* given_cspc = data; + int is_709 = given_cspc->format == MP_CSP_BT_709; + vo_xv_set_eq(vo, x11->xv_port, "bt_709", is_709 * 200 - 100); + return true; case VOCTRL_GET_YUV_COLORSPACE:; + struct mp_csp_details* cspc = data; + *cspc = (struct mp_csp_details) MP_CSP_DETAILS_DEFAULTS; int bt709_enabled; - if (!vo_xv_get_eq(vo, x11->xv_port, "bt_709", &bt709_enabled)) - return false; - *(int *)data = bt709_enabled == 100; + if (vo_xv_get_eq(vo, x11->xv_port, "bt_709", &bt709_enabled)) + cspc->format = bt709_enabled == 100 ? MP_CSP_BT_709 : MP_CSP_BT_601; return true; case VOCTRL_ONTOP: vo_x11_ontop(vo); diff --git a/options.h b/options.h index 6ee25351c8..3aee5dedc5 100644 --- a/options.h +++ b/options.h @@ -21,6 +21,9 @@ typedef struct MPOpts { int fullscreen; int vo_dbpp; float vo_panscanrange; + int requested_colorspace; + int requested_input_range; + int requested_output_range; // ranges -100 - 100, 1000 if the vo default should be used int vo_gamma_gamma;