From f811348d0caabccdc72100c7d7d2f9df8332518a Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Wed, 7 Jan 2015 18:47:27 +0100 Subject: vo_opengl: add support for in memory icc profiles Previously we just forced loading a profile from file, but that has poor integration for querying the OS / display server for an ICC profile, and generating profiles on the fly (which we might use in the future for creating preset 3dluts). Also changed the previous icc-profile-auto code to use this mechanism, and moved gl_lcms to be an opaque type with state instead of just providing pure functions. --- video/out/cocoa_common.m | 122 ++++------------------------------------- video/out/gl_lcms.c | 137 ++++++++++++++++++++++++++++++++--------------- video/out/gl_lcms.h | 12 +++-- video/out/vo.h | 2 +- video/out/vo_opengl.c | 31 +++++------ 5 files changed, 132 insertions(+), 172 deletions(-) diff --git a/video/out/cocoa_common.m b/video/out/cocoa_common.m index 6dcdc16bae..a893d0448a 100644 --- a/video/out/cocoa_common.m +++ b/video/out/cocoa_common.m @@ -52,7 +52,6 @@ #define cocoa_unlock(s) pthread_mutex_unlock(&s->mutex) static void vo_cocoa_fullscreen(struct vo *vo); -static void cocoa_change_profile(struct vo *vo, char **store, NSScreen *screen); static void cocoa_rm_fs_screen_profile_observer(struct vo *vo); struct vo_cocoa_state { @@ -80,8 +79,8 @@ struct vo_cocoa_state { uint32_t old_dwidth; uint32_t old_dheight; - char *icc_wnd_profile_path; - char *icc_fs_profile_path; + NSData *icc_wnd_profile; + NSData *icc_fs_profile; id fs_icc_changed_ns_observer; void (*resize_redraw)(struct vo *vo, int w, int h); @@ -420,7 +419,6 @@ static void cocoa_add_fs_screen_profile_observer(struct vo *vo) return; void (^nblock)(NSNotification *n) = ^(NSNotification *n) { - cocoa_change_profile(vo, &s->icc_fs_profile_path, s->fs_screen); s->pending_events |= VO_EVENT_ICC_PROFILE_PATH_CHANGED; }; @@ -586,119 +584,22 @@ static void vo_cocoa_fullscreen(struct vo *vo) draw_changes_after_next_frame(vo); [(MpvEventsView *)s->view setFullScreen:opts->fullscreen]; - if (s->icc_fs_profile_path != s->icc_wnd_profile_path) - s->pending_events = VO_EVENT_ICC_PROFILE_PATH_CHANGED; - + s->pending_events |= VO_EVENT_ICC_PROFILE_PATH_CHANGED; s->pending_events |= VO_EVENT_RESIZE; } -static char *cocoa_get_icc_profile_path(struct vo *vo, NSScreen *screen) +static void vo_cocoa_control_get_icc_profile(struct vo *vo, void *arg) { - assert(screen); - struct vo_cocoa_state *s = vo->cocoa; - char *result = NULL; - CFDictionaryRef device_info = NULL; - - CGDirectDisplayID displayID = (CGDirectDisplayID) - [[screen deviceDescription][@"NSScreenNumber"] unsignedLongValue]; - - CFUUIDRef uuid = CGDisplayCreateUUIDFromDisplayID(displayID); - if (CFGetTypeID(uuid) == CFNullGetTypeID()) { - MP_ERR(s, "cannot get display UUID.\n"); - goto get_icc_profile_path_err_out; - } - - device_info = - ColorSyncDeviceCopyDeviceInfo(kColorSyncDisplayDeviceClass, uuid); - - CFRelease(uuid); - - if (!device_info) { - MP_ERR(s, "cannot get display info.\n"); - goto get_icc_profile_path_err_out; - } - - CFDictionaryRef factory_info = - CFDictionaryGetValue(device_info, kColorSyncFactoryProfiles); - if (!factory_info) { - MP_ERR(s, "cannot get display factory settings.\n"); - goto get_icc_profile_path_err_out; - } - - CFStringRef default_profile_id = - CFDictionaryGetValue(factory_info, kColorSyncDeviceDefaultProfileID); - if (!default_profile_id) { - MP_ERR(s, "cannot get display default profile ID.\n"); - goto get_icc_profile_path_err_out; - } - - CFURLRef icc_url; - CFDictionaryRef custom_profile_info = - CFDictionaryGetValue(device_info, kColorSyncCustomProfiles); - if (custom_profile_info) { - icc_url = CFDictionaryGetValue(custom_profile_info, default_profile_id); - // If icc_url is NULL, the ICC profile URL could not be retrieved - // although a custom profile was specified. This points to a - // configuration error, so we should not fall back to the factory - // profile, but return an error instead. - if (!icc_url) { - MP_ERR(s, "cannot get display profile URL\n"); - goto get_icc_profile_path_err_out; - } - } else { - // No custom profile specified; try factory profile for the device - CFDictionaryRef factory_profile_info = - CFDictionaryGetValue(factory_info, default_profile_id); - if (!factory_profile_info) { - MP_ERR(s, "cannot get display profile info\n"); - goto get_icc_profile_path_err_out; - } - - icc_url = CFDictionaryGetValue(factory_profile_info, - kColorSyncDeviceProfileURL); - if (!icc_url) { - MP_ERR(s, "cannot get display factory profile URL.\n"); - goto get_icc_profile_path_err_out; - } - } - - result = talloc_strdup(vo, (char *)[[(NSURL *)icc_url path] UTF8String]); - if (!result) - MP_ERR(s, "cannot get display profile path.\n"); - -get_icc_profile_path_err_out: - CF_RELEASE(device_info); - return result; -} - -static void cocoa_change_profile(struct vo *vo, char **store, NSScreen *screen) -{ - if (*store) - talloc_free(*store); - *store = cocoa_get_icc_profile_path(vo, screen); -} - -static void vo_cocoa_control_get_icc_profile_path(struct vo *vo, void *arg) -{ - struct vo_cocoa_state *s = vo->cocoa; - char **p = arg; + bstr *p = arg; vo_cocoa_update_screen_info(vo, NULL); - NSScreen *screen; - char **path; - - if (vo->opts->fullscreen) { - screen = s->fs_screen; - path = &s->icc_fs_profile_path; - } else { - screen = s->current_screen; - path = &s->icc_wnd_profile_path; - } + NSScreen *screen = vo->opts->fullscreen ? s->fs_screen : s->current_screen; + NSData *profile = [[screen colorSpace] ICCProfileData]; - cocoa_change_profile(vo, path, screen); - *p = *path; + p->start = talloc_memdup(NULL, (void *)[profile bytes], [profile length]); + p->len = [profile length]; } int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg) @@ -740,8 +641,8 @@ int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg) case VOCTRL_KILL_SCREENSAVER: disable_power_management(vo); return VO_TRUE; - case VOCTRL_GET_ICC_PROFILE_PATH: - vo_cocoa_control_get_icc_profile_path(vo, arg); + case VOCTRL_GET_ICC_PROFILE: + vo_cocoa_control_get_icc_profile(vo, arg); return VO_TRUE; } return VO_NOTIMPL; @@ -830,7 +731,6 @@ int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg) - (void)didChangeWindowedScreenProfile:(NSScreen *)screen { struct vo_cocoa_state *s = self.vout->cocoa; - cocoa_change_profile(self.vout, &s->icc_wnd_profile_path, screen); s->pending_events |= VO_EVENT_ICC_PROFILE_PATH_CHANGED; } @end diff --git a/video/out/gl_lcms.c b/video/out/gl_lcms.c index 71fe66eb0a..254826bb15 100644 --- a/video/out/gl_lcms.c +++ b/video/out/gl_lcms.c @@ -40,6 +40,16 @@ #include +struct gl_lcms { + void *icc_data; + size_t icc_size; + char *icc_path; + + struct mp_log *log; + struct mpv_global *global; + struct mp_icc_opts opts; +}; + static bool parse_3dlut_size(const char *arg, int *p1, int *p2, int *p3) { if (sscanf(arg, "%dx%dx%d", p1, p2, p3) != 3) @@ -81,8 +91,8 @@ const struct m_sub_options mp_icc_conf = { static void lcms2_error_handler(cmsContext ctx, cmsUInt32Number code, const char *msg) { - struct mp_log *log = cmsGetContextUserData(ctx); - mp_msg(log, MSGL_ERR, "lcms2: %s\n", msg); + struct gl_lcms *p = cmsGetContextUserData(ctx); + MP_ERR(p, "lcms2: %s\n", msg); } static struct bstr load_file(void *talloc_ctx, const char *filename, @@ -99,51 +109,91 @@ static struct bstr load_file(void *talloc_ctx, const char *filename, return res; } -bool mp_icc_set_profile(struct mp_icc_opts *opts, char *profile) +static bool load_profile(struct gl_lcms *p) { - if (!opts->profile || strcmp(opts->profile, profile) != 0) { - if (opts->profile) - talloc_free(opts->profile); - opts->profile = talloc_strdup(opts, profile); + if (p->icc_data && p->icc_size) return true; - } - return false; + + if (!p->icc_path) + return false; + + MP_INFO(p, "Opening ICC profile '%s'\n", p->icc_path); + struct bstr iccdata = load_file(p, p->icc_path, p->global); + if (!iccdata.len) + return false; + + p->icc_data = iccdata.start; + p->icc_size = iccdata.len; + return true; +} + +struct gl_lcms *gl_lcms_init(void *talloc_ctx, struct mp_log *log, + struct mpv_global *global) +{ + struct gl_lcms *p = talloc_ptrtype(talloc_ctx, p); + *p = (struct gl_lcms) { + .global = global, + .log = log, + }; + return p; +} + +void gl_lcms_set_options(struct gl_lcms *p, struct mp_icc_opts *opts) +{ + p->opts = *opts; + p->icc_path = talloc_strdup(p, p->opts.profile); + load_profile(p); +} + +void gl_lcms_set_memory_profile(struct gl_lcms *p, bstr *profile) +{ + if (!p->opts.profile_auto) + return; + + if (p->icc_path) + talloc_free(p->icc_path); + + if (p->icc_data) + talloc_free(p->icc_data); + + p->icc_data = talloc_steal(p, profile->start); + p->icc_size = profile->len; } #define LUT3D_CACHE_HEADER "mpv 3dlut cache 1.0\n" -struct lut3d *mp_load_icc(struct mp_icc_opts *opts, struct mp_log *log, - struct mpv_global *global) +bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d) { int s_r, s_g, s_b; - if (!parse_3dlut_size(opts->size_str, &s_r, &s_g, &s_b)) - return NULL; + bool result = false; - if (!opts->profile) - return NULL; + if (!parse_3dlut_size(p->opts.size_str, &s_r, &s_g, &s_b)) + return false; + + if (!p->icc_data && !p->icc_path) + return false; void *tmp = talloc_new(NULL); uint16_t *output = talloc_array(tmp, uint16_t, s_r * s_g * s_b * 3); struct lut3d *lut = NULL; cmsContext cms = NULL; - mp_msg(log, MSGL_INFO, "Opening ICC profile '%s'\n", opts->profile); - struct bstr iccdata = load_file(tmp, opts->profile, global); - if (!iccdata.len) - goto error_exit; - char *cache_info = // Gamma is included in the header to help uniquely identify it, // because we may change the parameter in the future or make it // customizable, same for the primaries. talloc_asprintf(tmp, "intent=%d, size=%dx%dx%d, gamma=2.4, prim=bt2020\n", - opts->intent, s_r, s_g, s_b); + p->opts.intent, s_r, s_g, s_b); + + bstr iccdata = (bstr) { + .start = p->icc_data, + .len = p->icc_size, + }; // check cache - if (opts->cache) { - mp_msg(log, MSGL_INFO, "Opening 3D LUT cache in file '%s'.\n", - opts->cache); - struct bstr cachedata = load_file(tmp, opts->cache, global); + if (p->opts.cache) { + MP_INFO(p, "Opening 3D LUT cache in file '%s'.\n", p->opts.cache); + struct bstr cachedata = load_file(tmp, p->opts.cache, p->global); if (bstr_eatstart(&cachedata, bstr0(LUT3D_CACHE_HEADER)) && bstr_eatstart(&cachedata, bstr0(cache_info)) && bstr_eatstart(&cachedata, iccdata) @@ -152,16 +202,17 @@ struct lut3d *mp_load_icc(struct mp_icc_opts *opts, struct mp_log *log, memcpy(output, cachedata.start, cachedata.len); goto done; } else { - mp_msg(log, MSGL_WARN, "3D LUT cache invalid!\n"); + MP_WARN(p, "3D LUT cache invalid!\n"); } } - cms = cmsCreateContext(NULL, log); + cms = cmsCreateContext(NULL, p); if (!cms) goto error_exit; cmsSetLogErrorHandlerTHR(cms, lcms2_error_handler); - cmsHPROFILE profile = cmsOpenProfileFromMemTHR(cms, iccdata.start, iccdata.len); + cmsHPROFILE profile = + cmsOpenProfileFromMemTHR(cms, p->icc_data, p->icc_size); if (!profile) goto error_exit; @@ -184,7 +235,7 @@ struct lut3d *mp_load_icc(struct mp_icc_opts *opts, struct mp_log *log, cmsFreeToneCurve(tonecurve); cmsHTRANSFORM trafo = cmsCreateTransformTHR(cms, vid_profile, TYPE_RGB_16, profile, TYPE_RGB_16, - opts->intent, + p->opts.intent, cmsFLAGS_HIGHRESPRECALC); cmsCloseProfile(profile); cmsCloseProfile(vid_profile); @@ -208,12 +259,12 @@ struct lut3d *mp_load_icc(struct mp_icc_opts *opts, struct mp_log *log, cmsDeleteTransform(trafo); - if (opts->cache) { - char *fname = mp_get_user_path(NULL, global, opts->cache); + if (p->opts.cache) { + char *fname = mp_get_user_path(NULL, p->global, p->opts.cache); FILE *out = fopen(fname, "wb"); if (out) { fprintf(out, "%s%s", LUT3D_CACHE_HEADER, cache_info); - fwrite(iccdata.start, iccdata.len, 1, out); + fwrite(p->icc_data, p->icc_size, 1, out); fwrite(output, talloc_get_size(output), 1, out); fclose(out); } @@ -228,16 +279,19 @@ done: ; .size = {s_r, s_g, s_b}, }; + *result_lut3d = lut; + result = true; + error_exit: if (cms) cmsDeleteContext(cms); if (!lut) - mp_msg(log, MSGL_FATAL, "Error loading ICC profile.\n"); + MP_FATAL(p, "Error loading ICC profile.\n"); talloc_free(tmp); - return lut; + return result; } #else /* HAVE_LCMS2 */ @@ -248,16 +302,15 @@ const struct m_sub_options mp_icc_conf = { .defaults = &(const struct mp_icc_opts) {0}, }; -bool mp_icc_set_profile(struct mp_icc_opts *opts, char *profile) -{ - return false; -} -struct lut3d *mp_load_icc(struct mp_icc_opts *opts, struct mp_log *log, - struct mpv_global *global) +struct gl_lcms *gl_lcms_init(void *talloc_ctx, struct mp_log *log, + struct mpv_global *global) { - mp_msg(log, MSGL_FATAL, "LCMS2 support not compiled.\n"); - return NULL; + return (struct gl_lcms *) talloc_new(talloc_ctx); } +void gl_lcms_set_options(struct gl_lcms *p, struct mp_icc_opts *opts) { } +void gl_lcms_set_memory_profile(struct gl_lcms *p, bstr *profile) { } +bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **x) { return false; } + #endif diff --git a/video/out/gl_lcms.h b/video/out/gl_lcms.h index f1ab069574..2b34d62cca 100644 --- a/video/out/gl_lcms.h +++ b/video/out/gl_lcms.h @@ -1,7 +1,9 @@ #ifndef MP_GL_LCMS_H #define MP_GL_LCMS_H +#include #include +#include "misc/bstr.h" extern const struct m_sub_options mp_icc_conf; @@ -16,8 +18,12 @@ struct mp_icc_opts { struct lut3d; struct mp_log; struct mpv_global; -bool mp_icc_set_profile(struct mp_icc_opts *opts, char *profile); -struct lut3d *mp_load_icc(struct mp_icc_opts *opts, struct mp_log *log, - struct mpv_global *global); +struct gl_lcms; + +struct gl_lcms *gl_lcms_init(void *talloc_ctx, struct mp_log *log, + struct mpv_global *global); +void gl_lcms_set_options(struct gl_lcms *p, struct mp_icc_opts *opts); +void gl_lcms_set_memory_profile(struct gl_lcms *p, bstr *profile); +bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **); #endif diff --git a/video/out/vo.h b/video/out/vo.h index 383f47c7d7..ab7d97ae49 100644 --- a/video/out/vo.h +++ b/video/out/vo.h @@ -100,7 +100,7 @@ enum mp_voctrl { VOCTRL_SET_COMMAND_LINE, // char** - VOCTRL_GET_ICC_PROFILE_PATH, // char** + VOCTRL_GET_ICC_PROFILE, // bstr* VOCTRL_GET_DISPLAY_FPS, // double* VOCTRL_GET_RECENT_FLIP_TIME, // int64_t* (using mp_time_us()) diff --git a/video/out/vo_opengl.c b/video/out/vo_opengl.c index b4e5ecd660..2ed3785154 100644 --- a/video/out/vo_opengl.c +++ b/video/out/vo_opengl.c @@ -59,6 +59,7 @@ struct gl_priv { GL *gl; struct gl_video *renderer; + struct gl_lcms *cms; struct gl_hwdec *hwdec; struct mp_hwdec_info hwdec_info; @@ -242,13 +243,9 @@ static void call_request_hwdec_api(struct mp_hwdec_info *info, static bool update_icc_profile(struct gl_priv *p) { - struct mp_icc_opts *opts = p->icc_opts; struct lut3d *lut3d = NULL; - if (opts->profile) { - lut3d = mp_load_icc(opts, p->vo->log, p->vo->global); - if (!lut3d) - return false; - } + if (gl_lcms_get_lut3d(p->cms, &lut3d) && !lut3d) + return false; gl_video_set_lut3d(p->renderer, lut3d); talloc_free(lut3d); return true; @@ -256,20 +253,19 @@ static bool update_icc_profile(struct gl_priv *p) static bool get_and_update_icc_profile(struct gl_priv *p) { - if (!p->icc_opts->profile_auto) - return update_icc_profile(p); + bstr icc; + int r = p->glctx->vo_control(p->vo, NULL, VOCTRL_GET_ICC_PROFILE, &icc); - char *icc = NULL; - int r = p->glctx->vo_control(p->vo, NULL, VOCTRL_GET_ICC_PROFILE_PATH, &icc); - if (r != VO_TRUE) { + if (r == VO_FALSE) { MP_WARN(p->vo, "Could not retrieve an ICC profile.\n"); - return true; // no error if the system doesn't have any + return false; } - if (mp_icc_set_profile(p->icc_opts, icc)) - return update_icc_profile(p); + if (r == VO_TRUE) { + gl_lcms_set_memory_profile(p->cms, &icc); + } - return true; + return update_icc_profile(p); } static bool reparse_cmdline(struct gl_priv *p, char *args) @@ -430,6 +426,11 @@ static int preinit(struct vo *vo) gl_video_set_output_depth(p->renderer, p->glctx->depth_r, p->glctx->depth_g, p->glctx->depth_b); gl_video_set_options(p->renderer, p->renderer_opts); + + p->cms = gl_lcms_init(p, vo->log, vo->global); + if (!p->cms) + goto err_out; + gl_lcms_set_options(p->cms, p->icc_opts); if (!get_and_update_icc_profile(p)) goto err_out; -- cgit v1.2.3