1
mirror of https://github.com/mpv-player/mpv synced 2024-11-18 21:16:10 +01:00

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
This commit is contained in:
Stefano Pigozzi 2014-02-25 00:04:30 +01:00
parent 956b01ec4e
commit b0ee9334e3
8 changed files with 226 additions and 2 deletions

View File

@ -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

View File

@ -26,6 +26,7 @@
- (void)putCommand:(char*)cmd;
- (void)performAsyncResize:(NSSize)size;
- (void)handleFilesArray:(NSArray *)files;
- (void)didChangeWindowedScreenProfile:(NSScreen *)screen;
- (BOOL)isInFullScreenMode;
- (NSScreen *)fsScreen;

View File

@ -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) ==

View File

@ -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

View File

@ -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,

View File

@ -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);

View File

@ -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

View File

@ -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);