Skip to content

Commit

Permalink
vo_opengl, cocoa: allow to autoselect a color profile
Browse files Browse the repository at this point in the history
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
  • Loading branch information
pigoz committed Mar 31, 2014
1 parent 956b01e commit b0ee933
Show file tree
Hide file tree
Showing 8 changed files with 226 additions and 2 deletions.
7 changes: 7 additions & 0 deletions DOCS/man/en/vo.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions video/out/cocoa/mpvadapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
- (void)putCommand:(char*)cmd;
- (void)performAsyncResize:(NSSize)size;
- (void)handleFilesArray:(NSArray *)files;
- (void)didChangeWindowedScreenProfile:(NSScreen *)screen;

- (BOOL)isInFullScreenMode;
- (NSScreen *)fsScreen;
Expand Down
8 changes: 7 additions & 1 deletion video/out/cocoa/window.m
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,16 @@ - (void)windowDidResize:(NSNotification *) notification
[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) ==
Expand Down
168 changes: 168 additions & 0 deletions video/out/cocoa_common.m
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -73,6 +77,11 @@

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)
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -652,4 +813,11 @@ - (void)handleFilesArray:(NSArray *)files
{
[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
12 changes: 12 additions & 0 deletions video/out/gl_lcms.c
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions video/out/gl_lcms.h
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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);

Expand Down
3 changes: 3 additions & 0 deletions video/out/vo.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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
Expand Down
25 changes: 24 additions & 1 deletion video/out/vo_opengl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit b0ee933

Please sign in to comment.