1
mirror of https://github.com/mpv-player/mpv synced 2024-11-03 03:19:24 +01:00

cocoa: add support for querying ambient lighting

This will be pretty useful to let mpv automatically change VO parameters based
on ambient lighting conditions.

The conversion code and polinomial equation from Apple LMU values to Lux is
taken from Firefox: their license, MPL is GPL compatible and allows
relicensing to GPL (MPL is more liberal).
This commit is contained in:
Stefano Pigozzi 2015-02-03 18:16:02 +01:00
parent 9746e71efc
commit 89306818bb
2 changed files with 97 additions and 0 deletions

View File

@ -20,6 +20,8 @@
#import <Cocoa/Cocoa.h>
#import <CoreServices/CoreServices.h> // for CGDisplayHideCursor
#import <IOKit/pwr_mgt/IOPMLib.h>
#import <IOKit/IOKitLib.h>
#include <mach/mach.h>
#import "cocoa_common.h"
#import "video/out/cocoa/window.h"
@ -73,6 +75,10 @@ struct vo_cocoa_state {
bool embedded; // wether we are embedding in another GUI
IOPMAssertionID power_mgmt_assertion;
io_connect_t light_sensor;
uint64_t last_lmuvalue;
int last_lux;
IONotificationPortRef light_sensor_io_port;
pthread_mutex_t mutex;
struct mp_log *log;
@ -146,6 +152,87 @@ static void set_application_icon(NSApplication *app)
[pool release];
}
static int lmuvalue_to_lux(uint64_t v)
{
// the polinomial approximation for apple lmu value -> lux was empirically
// derived by firefox developers (Apple provides no documentation).
// https://bugzilla.mozilla.org/show_bug.cgi?id=793728
double power_c4 = 1/pow((double)10,27);
double power_c3 = 1/pow((double)10,19);
double power_c2 = 1/pow((double)10,12);
double power_c1 = 1/pow((double)10,5);
double term4 = -3.0 * power_c4 * pow(v,4);
double term3 = 2.6 * power_c3 * pow(v,3);
double term2 = -3.4 * power_c2 * pow(v,2);
double term1 = 3.9 * power_c1 * v;
int lux = ceil(term4 + term3 + term2 + term1 - 0.19);
return lux > 0 ? lux : 0;
}
static void light_sensor_cb(void *ctx, io_service_t srv, natural_t mtype, void *msg)
{
struct vo *vo = ctx;
struct vo_cocoa_state *s = vo->cocoa;
uint32_t outputs = 2;
uint64_t values[outputs];
kern_return_t kr = IOConnectCallMethod(
s->light_sensor, 0, NULL, 0, NULL, 0, values, &outputs, nil, 0);
if (kr == KERN_SUCCESS) {
uint64_t mean = (values[0] + values[1]) / 2;
if (s->last_lmuvalue != mean) {
s->last_lmuvalue = mean;
s->last_lux = lmuvalue_to_lux(s->last_lmuvalue);
s->pending_events |= VO_EVENT_AMBIENT_LIGHTING_CHANGED;
vo_wakeup(vo);
return;
}
}
}
static void cocoa_init_light_sensor(struct vo *vo)
{
with_cocoa_lock_on_main_thread(vo, ^{
struct vo_cocoa_state *s = vo->cocoa;
io_service_t srv = IOServiceGetMatchingService(
kIOMasterPortDefault, IOServiceMatching("AppleLMUController"));
if (srv == IO_OBJECT_NULL) {
MP_VERBOSE(vo, "can't find an ambient light sensor\n");
return;
}
// subscribe to notifications from the light sensor driver
s->light_sensor_io_port = IONotificationPortCreate(kIOMasterPortDefault);
IONotificationPortSetDispatchQueue(
s->light_sensor_io_port, dispatch_get_main_queue());
io_object_t n;
IOServiceAddInterestNotification(
s->light_sensor_io_port, srv, kIOGeneralInterest, light_sensor_cb,
vo, &n);
kern_return_t kr = IOServiceOpen(srv, mach_task_self(), 0,
&s->light_sensor);
IOObjectRelease(srv);
if (kr != KERN_SUCCESS) {
MP_WARN(vo, "can't start ambient light sensor connection\n");
return;
}
light_sensor_cb(vo, 0, 0, NULL);
});
}
static void cocoa_uninit_light_sensor(struct vo *vo)
{
struct vo_cocoa_state *s = vo->cocoa;
IONotificationPortDestroy(s->light_sensor_io_port);
IOObjectRelease(s->light_sensor);
}
int vo_cocoa_init(struct vo *vo)
{
struct vo_cocoa_state *s = talloc_zero(vo, struct vo_cocoa_state);
@ -156,6 +243,7 @@ int vo_cocoa_init(struct vo *vo)
.embedded = vo->opts->WinID >= 0,
};
mpthread_mutex_init_recursive(&s->mutex);
cocoa_init_light_sensor(vo);
vo->cocoa = s;
return 1;
}
@ -199,6 +287,7 @@ void vo_cocoa_uninit(struct vo *vo)
with_cocoa_lock_on_main_thread(vo, ^{
enable_power_management(vo);
cocoa_uninit_light_sensor(vo);
cocoa_rm_fs_screen_profile_observer(vo);
[s->gl_ctx release];
@ -676,6 +765,11 @@ int vo_cocoa_control(struct vo *vo, int *events, int request, void *arg)
*(double *)arg = vo->cocoa->screen_fps;
return VO_TRUE;
}
case VOCTRL_GET_AMBIENT_LUX:
if (vo->cocoa->light_sensor != IO_OBJECT_NULL) {
*(int *)arg = vo->cocoa->last_lux;
return VO_TRUE;
}
}
return VO_NOTIMPL;
}

View File

@ -38,6 +38,8 @@
#define VO_EVENT_ICC_PROFILE_CHANGED 4
// Some other window state changed
#define VO_EVENT_WIN_STATE 8
// The ambient light conditions changed and need to be reloaded
#define VO_EVENT_AMBIENT_LIGHTING_CHANGED 16
// Set of events the player core may be interested in.
#define VO_EVENTS_USER (VO_EVENT_RESIZE | VO_EVENT_WIN_STATE)
@ -103,6 +105,7 @@ enum mp_voctrl {
VOCTRL_SET_COMMAND_LINE, // char**
VOCTRL_GET_ICC_PROFILE, // bstr*
VOCTRL_GET_AMBIENT_LUX, // int*
VOCTRL_GET_DISPLAY_FPS, // double*
VOCTRL_GET_RECENT_FLIP_TIME, // int64_t* (using mp_time_us())