Sélectionner une révision Git
mpv.c 7,53 Kio
#define _POSIX_C_SOURCE 200809L
#include "mpv.h"
#include <lektor/common.h>
#include <lektor/database.h>
#include <strings.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sqlite3.h>
enum {
STATE_STOP = 0,
STATE_PLAY = 1,
STATE_PAUSE = 2,
};
void
lmpv_free(mpv_handle **ctx)
{
RETURN_UNLESS(ctx && *ctx, "Missing mpv ctx", NOTHING);
static const char *cmd[] = {"quit", NULL};
mpv_command(*ctx, cmd);
mpv_destroy(*ctx);
*ctx = NULL;
LOG_INFO("WINDOW", "The mpv context was destroyed");
}
mpv_handle *
lmpv_prepare(volatile sqlite3 *db)
{
mpv_handle *ctx = mpv_create();
int status;
RETURN_UNLESS(ctx, "Failed to create context", NULL);
char _opt[LKT_LINE_MAX];
#define MPV_SET_OPTION(opt, value) \
if ((status = mpv_set_option_string(ctx, opt, value)) < 0) { \
LOG_ERROR("WINDOW", "Failed to set %s to %s: %s", \
opt, value, mpv_error_string(status)); \
return NULL; \
}
#define MPV_SET_FROM_INI(opt, section, key) \
if (!database_config_get_text(db, section, key, _opt, LKT_LINE_MAX)) { \
LOG_WARN("WINDOW", "Failed to get option " \
key " in section " section); \
return ctx; \
} \
MPV_SET_OPTION(opt, _opt);
MPV_SET_OPTION("input-default-bindings", "yes");
MPV_SET_OPTION("input-vo-keyboard", "yes");
MPV_SET_OPTION("replaygain", "track");
MPV_SET_OPTION("gpu-context", "x11"); /* Wayland you sucks */
MPV_SET_OPTION("demuxer-readahead-secs", "5.0");
MPV_SET_OPTION("demuxer-max-bytes", "100M");
MPV_SET_OPTION("hwdec", "yes");
MPV_SET_FROM_INI("osd-font-size", "player", "font_size");
MPV_SET_FROM_INI("osd-font", "player", "font_name");
MPV_SET_FROM_INI("osd-duration", "player", "msg_duration");
return ctx;
#undef MPV_SET_OPTION
#undef MPV_SET_FROM_INI
}
int
lmpv_observe_properties(mpv_handle *ctx)
{
return (mpv_observe_property(ctx, 0, "ao-volume", MPV_FORMAT_INT64) >= 0) &&
(mpv_observe_property(ctx, 0, "duration", MPV_FORMAT_INT64) >= 0) &&
(mpv_observe_property(ctx, 0, "time-pos", MPV_FORMAT_INT64) >= 0) &&
(mpv_observe_property(ctx, 0, "pause", MPV_FORMAT_FLAG) >= 0);
}
mpv_handle *
lmpv_new(unsigned long int wid, volatile sqlite3 *db)
{
mpv_handle *ctx = lmpv_prepare(db);
int status;
if ((status = mpv_set_property(ctx, "wid", MPV_FORMAT_INT64, &wid)) < 0) {
LOG_ERROR("WINDOW", "Failed to set wid: %s", mpv_error_string(status));
goto error;
}
if ((status = mpv_initialize(ctx)) < 0) {
LOG_ERROR("WINDOW", "Failed to init mpv: %s", mpv_error_string(status));
goto error;
}
if (!lmpv_observe_properties(ctx)) {
LOG_ERROR("WINDOW", "Failed to observe properties");
goto error;
}
LOG_INFO("WINDOW", "Create mpv context");
return ctx;
error:
lmpv_free(&ctx);
return NULL;
}
int
lmpv_set_volume(mpv_handle *ctx, int vol)
{
RETURN_UNLESS(ctx, "Missing mpv ctx", 1);
int status;
char str[5];
memset(str, 0, 5);
safe_snprintf(str, 4, "%d", vol);
const char *cmd[] = {"set", "ao-volume", str, NULL};
if ((status = mpv_command_async(ctx, 0, cmd)) < 0) {
LOG_ERROR("WINDOW", "Failed to execute command: %s", mpv_error_string(status));
return 1;
}
return 0;
}
int
lmpv_load_file(mpv_handle *ctx, const char *file)
{
RETURN_UNLESS(ctx, "Missing mpv ctx", 1);
RETURN_IF(access(file, R_OK), "Failed to read file", 1);
const char *cmd1[] = { "loadfile", file, "replace", NULL };
const char *cmd2[] = { "set", "pause", "0", NULL };
int status;
if ((status = mpv_command_async(ctx, 0, cmd1)) < 0) {
LOG_ERROR("WINDOW", "Failed to add '%s': %s", file, mpv_error_string(status));
return 1;
}
if ((status = mpv_command_async(ctx, 0, cmd2)) < 0) {
LOG_ERROR("WINDOW", "Failed to set state to play: %s", mpv_error_string(status));
return 1;
}
return 0;
}
int
lmpv_toggle_pause(mpv_handle *ctx)
{
RETURN_UNLESS(ctx, "Missing mpv ctx", 1);
const char *cmd[] = {"cycle", "pause", "up", NULL};
int status;
if ((status = mpv_command_async(ctx, 0, cmd)) < 0) {
LOG_ERROR("WINDOW", "Failed issus command: %s", mpv_error_string(status));
return 1;
}
return 0;
}
int
lmpv_handle(struct lkt_module *mod, mpv_handle *ctx, struct queue *queue,
volatile int *time_pos, volatile int *time_duration,
volatile int *state)
{
size_t ao_volume;
mpv_event *event = NULL;
mpv_event_property *prop;
RETURN_UNLESS(ctx, "Invalid argument", 1);
loop:
event = mpv_wait_event(ctx, 0);
switch (event->event_id) {
case MPV_EVENT_PAUSE:
*state = STATE_PAUSE;
lkt_queue_send(queue, lkt_event_play_toggle, LKT_PLAY_PAUSE);
break;
case MPV_EVENT_UNPAUSE:
*state = STATE_PLAY;
lkt_queue_send(queue, lkt_event_play_toggle, LKT_PLAY_PLAY);
break;
case MPV_EVENT_SHUTDOWN:
*time_pos = (*time_duration = 0);
*state = STATE_STOP;
lkt_queue_send(queue, lkt_event_play_toggle, LKT_PLAY_STOP);
reg_call(mod->reg, "close", 1, mod->data);
return 1;
case MPV_EVENT_NONE:
return 1;
case MPV_EVENT_IDLE:
if (*state != STATE_STOP && *time_pos > 0)
lkt_queue_send(queue, lkt_event_play_next, NULL);
break;
case MPV_EVENT_PROPERTY_CHANGE:
prop = (mpv_event_property *) event->data;
if (prop->format == MPV_FORMAT_NONE)
break;
/* MPV volume (BUG: The flag is not MPV_FORMAT_NONE only at the end of the song...) */
if (STR_MATCH(prop->name, "ao-volume")
&& prop->format == MPV_FORMAT_INT64) {
ao_volume = *(int *) prop->data;
lkt_queue_send(queue, lkt_event_prop_vol, (void *) ao_volume);
}
/* File duration */
if (STR_MATCH(prop->name, "duration")
&& prop->format == MPV_FORMAT_INT64) {
*time_duration = *(int *) prop->data;
lkt_queue_send(queue, lkt_event_prop_dur, (void *) (size_t) *time_duration);
}
if (STR_MATCH(prop->name, "time-pos")
&& prop->format == MPV_FORMAT_INT64) {
*time_pos = *(int *) prop->data;
lkt_queue_send(queue, lkt_event_prop_time, (void *) (size_t) *time_pos);
}
/* Pause state */
if (STR_MATCH(prop->name, "pause")
&& prop->format == MPV_FORMAT_FLAG) {
if (* (bool *) prop->data) {
lkt_queue_send(queue, lkt_event_play_toggle, LKT_PLAY_PAUSE);
*state = STATE_PAUSE;
} else {
lkt_queue_send(queue, lkt_event_play_toggle, LKT_PLAY_PLAY);
*state = STATE_PLAY;
}
}
break;
/* Ignored */
case MPV_EVENT_VIDEO_RECONFIG:
case MPV_EVENT_AUDIO_RECONFIG:
break;
case MPV_EVENT_COMMAND_REPLY:
free((void *) event->reply_userdata);
break;
default:
LOG_WARN("WINDOW", "Unhandled mpv event '%s'", mpv_event_name(event->event_id));
break;
}
goto loop; /* A loop without indentation. Ugly but better for not-really-wide screens */
}