summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefano Pigozzi <stefano.pigozzi@gmail.com>2014-02-25 00:04:30 +0100
committerStefano Pigozzi <stefano.pigozzi@gmail.com>2014-03-31 22:07:33 +0200
commitb0ee9334e33a8603dd07f991ee5cd5f2345030ed (patch)
treea3c5e4d92b028be15d084e664343aee81f458bb9
parent956b01ec4eaecef0d48409e7aa5c1d133132fb82 (diff)
downloadmpv-b0ee9334e33a8603dd07f991ee5cd5f2345030ed.tar.bz2
mpv-b0ee9334e33a8603dd07f991ee5cd5f2345030ed.tar.xz
vo_opengl, cocoa: allow to autoselect a color profile
This commit adds support for automatic selection of color profiles based on the display where mpv is initialized, and automatically changes the color profile when display is changed or the profile itself is changed from System Preferences. @UliZappe was responsible with the testing and implementation of a lot of this commit, including the original implementation of `cocoa_get_icc_profile_path` (See #594). Fixes #594
-rw-r--r--DOCS/man/en/vo.rst7
-rw-r--r--video/out/cocoa/mpvadapter.h1
-rw-r--r--video/out/cocoa/window.m8
-rw-r--r--video/out/cocoa_common.m168
-rw-r--r--video/out/gl_lcms.c12
-rw-r--r--video/out/gl_lcms.h4
-rw-r--r--video/out/vo.h3
-rw-r--r--video/out/vo_opengl.c25
8 files changed, 226 insertions, 2 deletions
diff --git a/DOCS/man/en/vo.rst b/DOCS/man/en/vo.rst
index 4e4e08ee80..9a342acb68 100644
--- a/DOCS/man/en/vo.rst
+++ b/DOCS/man/en/vo.rst
@@ -449,6 +449,13 @@ Available video output drivers are:
property, as using both is somewhat redundant. It also enables linear
light scaling.
+
+ ``icc-profile-auto``
+ Automatically select the ICC display profile currently specified by
+ the display settings of the operating system.
+
+ NOTE: Only implemented on OS X with Cocoa.
+
``icc-cache=<file>``
Store and load the 3D LUT created from the ICC profile in this file.
This can be used to speed up loading, since LittleCMS2 can take a while
diff --git a/video/out/cocoa/mpvadapter.h b/video/out/cocoa/mpvadapter.h
index 9833993988..af536d2f56 100644
--- a/video/out/cocoa/mpvadapter.h
+++ b/video/out/cocoa/mpvadapter.h
@@ -26,6 +26,7 @@
- (void)putCommand:(char*)cmd;
- (void)performAsyncResize:(NSSize)size;
- (void)handleFilesArray:(NSArray *)files;
+- (void)didChangeWindowedScreenProfile:(NSScreen *)screen;
- (BOOL)isInFullScreenMode;
- (NSScreen *)fsScreen;
diff --git a/video/out/cocoa/window.m b/video/out/cocoa/window.m
index e760fd184d..009315d7ac 100644
--- a/video/out/cocoa/window.m
+++ b/video/out/cocoa/window.m
@@ -54,10 +54,16 @@
[self.adapter setNeedsResize];
}
-- (void)windowDidChangeBackingProperties:(NSNotification *)notification {
+- (void)windowDidChangeBackingProperties:(NSNotification *)notification
+{
[self.adapter setNeedsResize];
}
+- (void)windowDidChangeScreenProfile:(NSNotification *)notification
+{
+ [self.adapter didChangeWindowedScreenProfile:[self screen]];
+}
+
- (BOOL)isInFullScreenMode
{
return (([self styleMask] & NSFullScreenWindowMask) ==
diff --git a/video/out/cocoa_common.m b/video/out/cocoa_common.m
index 5780af3bdc..7e660e9fae 100644
--- a/video/out/cocoa_common.m
+++ b/video/out/cocoa_common.m
@@ -43,8 +43,12 @@
#include "common/msg.h"
+#define CF_RELEASE(a) if ((a) != NULL) CFRelease(a)
+
static void vo_cocoa_fullscreen(struct vo *vo);
static void vo_cocoa_ontop(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 {
MpvVideoWindow *window;
@@ -73,6 +77,11 @@ struct vo_cocoa_state {
uint32_t old_dwidth;
uint32_t old_dheight;
+
+ bool icc_profile_path_changed;
+ char *icc_wnd_profile_path;
+ char *icc_fs_profile_path;
+ id fs_icc_changed_ns_observer;
};
void *vo_cocoa_glgetaddr(const char *s)
@@ -118,6 +127,7 @@ int vo_cocoa_init(struct vo *vo)
.lock = [[NSLock alloc] init],
.enable_resize_redraw = NO,
.log = mp_log_new(s, vo->log, "cocoa"),
+ .icc_profile_path_changed = false,
};
vo->cocoa = s;
return 1;
@@ -141,6 +151,7 @@ void vo_cocoa_uninit(struct vo *vo)
dispatch_sync(dispatch_get_main_queue(), ^{
struct vo_cocoa_state *s = vo->cocoa;
enable_power_management(vo);
+ cocoa_rm_fs_screen_profile_observer(vo);
[NSApp setPresentationOptions:NSApplicationPresentationDefault];
// XXX: It looks like there are some circular retain cycles for the
@@ -377,6 +388,35 @@ static void vo_cocoa_resize_redraw(struct vo *vo, int width, int height)
vo_cocoa_set_current_context(vo, false);
}
+static void cocoa_rm_fs_screen_profile_observer(struct vo *vo)
+{
+ struct vo_cocoa_state *s = vo->cocoa;
+ [[NSNotificationCenter defaultCenter]
+ removeObserver:s->fs_icc_changed_ns_observer];
+}
+
+static void cocoa_add_fs_screen_profile_observer(struct vo *vo)
+{
+ struct vo_cocoa_state *s = vo->cocoa;
+
+ if (s->fs_icc_changed_ns_observer)
+ cocoa_rm_fs_screen_profile_observer(vo);
+
+ if (vo->opts->fsscreen_id < 0)
+ return;
+
+ void (^nblock)(NSNotification *n) = ^(NSNotification *n) {
+ cocoa_change_profile(vo, &s->icc_fs_profile_path, s->fs_screen);
+ s->icc_profile_path_changed = true;
+ };
+
+ s->fs_icc_changed_ns_observer = [[NSNotificationCenter defaultCenter]
+ addObserverForName:NSScreenColorSpaceDidChangeNotification
+ object:s->fs_screen
+ queue:nil
+ usingBlock:nblock];
+}
+
int vo_cocoa_config_window(struct vo *vo, uint32_t width, uint32_t height,
uint32_t flags, int gl3profile)
{
@@ -419,6 +459,7 @@ int vo_cocoa_config_window(struct vo *vo, uint32_t width, uint32_t height,
[s->window queueNewVideoSize:NSMakeSize(width, height)];
cocoa_set_window_title(vo, vo_get_window_title(vo));
vo_cocoa_fullscreen(vo);
+ cocoa_add_fs_screen_profile_observer(vo);
}
s->inside_sync_section = false;
@@ -474,6 +515,11 @@ int vo_cocoa_check_events(struct vo *vo)
return VO_EVENT_RESIZE;
}
+ if (s->icc_profile_path_changed) {
+ s->icc_profile_path_changed = false;
+ return VO_EVENT_ICC_PROFILE_PATH_CHANGED;
+ }
+
return 0;
}
@@ -503,12 +549,124 @@ static void vo_cocoa_fullscreen(struct vo *vo)
[s->view setFullScreen:opts->fullscreen];
}
+ if (s->icc_fs_profile_path != s->icc_wnd_profile_path)
+ s->icc_profile_path_changed = true;
+
[s->window didChangeFullScreenState];
// Make the core aware of the view size change.
resize_window(vo);
}
+static char *cocoa_get_icc_profile_path(struct vo *vo, NSScreen *screen)
+{
+ 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;
+
+ vo_cocoa_update_screen_info(vo);
+
+ 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;
+ }
+
+ cocoa_change_profile(vo, path, screen);
+ *p = *path;
+}
+
int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg)
{
switch (request) {
@@ -557,6 +715,9 @@ 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);
+ return VO_TRUE;
}
return VO_NOTIMPL;
}
@@ -652,4 +813,11 @@ int vo_cocoa_cgl_color_size(struct vo *vo)
{
[mpv_shared_app() handleFilesArray:files];
}
+
+- (void)didChangeWindowedScreenProfile:(NSScreen *)screen
+{
+ struct vo_cocoa_state *s = self.vout->cocoa;
+ cocoa_change_profile(self.vout, &s->icc_wnd_profile_path, screen);
+ s->icc_profile_path_changed = true;
+}
@end
diff --git a/video/out/gl_lcms.c b/video/out/gl_lcms.c
index 08f89b81d9..d2f59306f6 100644
--- a/video/out/gl_lcms.c
+++ b/video/out/gl_lcms.c
@@ -71,6 +71,7 @@ static int validate_3dlut_size_opt(struct mp_log *log, const m_option_t *opt,
const struct m_sub_options mp_icc_conf = {
.opts = (m_option_t[]) {
OPT_STRING("icc-profile", profile, 0),
+ OPT_FLAG("icc-profile-auto", profile_auto, 0),
OPT_STRING("icc-cache", cache, 0),
OPT_INT("icc-intent", intent, 0),
OPT_STRING_VALIDATE("3dlut-size", size_str, 0, validate_3dlut_size_opt),
@@ -106,6 +107,17 @@ 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)
+{
+ if (!opts->profile || strcmp(opts->profile, profile) != 0) {
+ if (opts->profile)
+ talloc_free(opts->profile);
+ opts->profile = talloc_strdup(opts, profile);
+ return true;
+ }
+ return false;
+}
+
#define LUT3D_CACHE_HEADER "mpv 3dlut cache 1.0\n"
struct lut3d *mp_load_icc(struct mp_icc_opts *opts, struct mp_log *log,
diff --git a/video/out/gl_lcms.h b/video/out/gl_lcms.h
index a579b78f43..f1ab069574 100644
--- a/video/out/gl_lcms.h
+++ b/video/out/gl_lcms.h
@@ -1,10 +1,13 @@
#ifndef MP_GL_LCMS_H
#define MP_GL_LCMS_H
+#include <stdbool.h>
+
extern const struct m_sub_options mp_icc_conf;
struct mp_icc_opts {
char *profile;
+ int profile_auto;
char *cache;
char *size_str;
int intent;
@@ -13,6 +16,7 @@ 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);
diff --git a/video/out/vo.h b/video/out/vo.h
index c53e788a69..dd98636189 100644
--- a/video/out/vo.h
+++ b/video/out/vo.h
@@ -32,6 +32,7 @@
#define VO_EVENT_EXPOSE 1
#define VO_EVENT_RESIZE 2
+#define VO_EVENT_ICC_PROFILE_PATH_CHANGED 4
enum mp_voctrl {
/* signal a device reset seek */
@@ -82,6 +83,8 @@ enum mp_voctrl {
VOCTRL_SCREENSHOT, // struct voctrl_screenshot_args*
VOCTRL_SET_COMMAND_LINE, // char**
+
+ VOCTRL_GET_ICC_PROFILE_PATH, // char**
};
// VOCTRL_SET_EQUALIZER
diff --git a/video/out/vo_opengl.c b/video/out/vo_opengl.c
index 485fbdf8fa..c243cdeb85 100644
--- a/video/out/vo_opengl.c
+++ b/video/out/vo_opengl.c
@@ -275,6 +275,25 @@ static bool update_icc_profile(struct gl_priv *p, struct mp_icc_opts *opts)
return true;
}
+static bool get_and_update_icc_profile(struct vo *vo,
+ struct mp_icc_opts *opts)
+{
+ struct gl_priv *p = vo->priv;
+
+ if (!opts->profile_auto)
+ return update_icc_profile(p, opts);
+
+ char *icc = NULL;
+ int r = p->glctx->vo_control(vo, NULL, VOCTRL_GET_ICC_PROFILE_PATH, &icc);
+ if (r != VO_TRUE)
+ return false;
+
+ if (mp_icc_set_profile(opts, icc))
+ return update_icc_profile(p, opts);
+
+ return true;
+}
+
static bool reparse_cmdline(struct gl_priv *p, char *args)
{
struct m_config *cfg = NULL;
@@ -377,6 +396,10 @@ static int control(struct vo *vo, uint32_t request, void *data)
resize(p);
if (events & VO_EVENT_EXPOSE)
vo->want_redraw = true;
+ if (events & VO_EVENT_ICC_PROFILE_PATH_CHANGED) {
+ get_and_update_icc_profile(vo, p->icc_opts);
+ vo->want_redraw = true;
+ }
mpgl_unlock(p->glctx);
return r;
@@ -416,7 +439,7 @@ 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);
- if (!update_icc_profile(p, p->icc_opts))
+ if (!get_and_update_icc_profile(vo, p->icc_opts))
goto err_out;
mpgl_unset_context(p->glctx);