mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-27 23:55:57 -05:00
Merge pull request #150 from chme/mpdidle
Add support for mpd idle command
This commit is contained in:
commit
79cdfebf34
@ -119,7 +119,8 @@ forked_daapd_SOURCES = main.c \
|
||||
scan-wma.c \
|
||||
$(SPOTIFY_SRC) $(LASTFM_SRC) \
|
||||
$(MPD_SRC) \
|
||||
$(FLAC_SRC) $(MUSEPACK_SRC)
|
||||
$(FLAC_SRC) $(MUSEPACK_SRC) \
|
||||
listener.c listener.h
|
||||
|
||||
nodist_forked_daapd_SOURCES = \
|
||||
$(ANTLR_SOURCES)
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "dmap_common.h"
|
||||
#include "db.h"
|
||||
#include "player.h"
|
||||
#include "listener.h"
|
||||
|
||||
/* httpd event base, from httpd.c */
|
||||
extern struct event_base *evbase_httpd;
|
||||
@ -342,10 +343,14 @@ playstatusupdate_cb(int fd, short what, void *arg)
|
||||
|
||||
/* Thread: player */
|
||||
static void
|
||||
dacp_playstatus_update_handler(void)
|
||||
dacp_playstatus_update_handler(enum listener_event_type type)
|
||||
{
|
||||
int ret;
|
||||
|
||||
// Only send status update on player change events
|
||||
if (type != LISTENER_PLAYER)
|
||||
return;
|
||||
|
||||
#ifdef USE_EVENTFD
|
||||
ret = eventfd_write(update_efd, 1);
|
||||
if (ret < 0)
|
||||
@ -2423,7 +2428,7 @@ dacp_init(void)
|
||||
event_base_set(evbase_httpd, &updateev);
|
||||
event_add(&updateev, NULL);
|
||||
|
||||
player_set_update_handler(dacp_playstatus_update_handler);
|
||||
listener_add(dacp_playstatus_update_handler, LISTENER_PLAYER);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -2444,7 +2449,7 @@ dacp_deinit(void)
|
||||
struct evhttp_connection *evcon;
|
||||
int i;
|
||||
|
||||
player_set_update_handler(NULL);
|
||||
listener_remove(dacp_playstatus_update_handler);
|
||||
|
||||
for (i = 0; dacp_handlers[i].handler; i++)
|
||||
regfree(&dacp_handlers[i].preg);
|
||||
|
94
src/listener.c
Normal file
94
src/listener.c
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Christian Meffert <christian.meffert@googlemail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "listener.h"
|
||||
|
||||
struct listener
|
||||
{
|
||||
notify notify_cb;
|
||||
short events;
|
||||
struct listener *next;
|
||||
};
|
||||
|
||||
struct listener *listener_list = NULL;
|
||||
|
||||
int
|
||||
listener_add(notify notify_cb, short events)
|
||||
{
|
||||
struct listener *listener;
|
||||
|
||||
listener = (struct listener*)malloc(sizeof(struct listener));
|
||||
if (!listener)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
listener->notify_cb = notify_cb;
|
||||
listener->events = events;
|
||||
listener->next = listener_list;
|
||||
listener_list = listener;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
listener_remove(notify notify_cb)
|
||||
{
|
||||
struct listener *listener;
|
||||
struct listener *prev;
|
||||
|
||||
prev = NULL;
|
||||
for (listener = listener_list; listener; listener = listener->next)
|
||||
{
|
||||
if (listener->notify_cb == notify_cb)
|
||||
break;
|
||||
|
||||
prev = listener;
|
||||
}
|
||||
|
||||
if (!listener)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (prev)
|
||||
prev->next = listener->next;
|
||||
else
|
||||
listener_list = NULL;
|
||||
|
||||
free(listener);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
listener_notify(enum listener_event_type type)
|
||||
{
|
||||
struct listener *listener;
|
||||
|
||||
listener = listener_list;
|
||||
while (listener)
|
||||
{
|
||||
if (type & listener->events)
|
||||
listener->notify_cb(type);
|
||||
listener = listener->next;
|
||||
}
|
||||
}
|
54
src/listener.h
Normal file
54
src/listener.h
Normal file
@ -0,0 +1,54 @@
|
||||
|
||||
#ifndef __LISTENER_H__
|
||||
#define __LISTENER_H__
|
||||
|
||||
enum listener_event_type
|
||||
{
|
||||
/* The player has been started, stopped or seeked */
|
||||
LISTENER_PLAYER = (1 << 0),
|
||||
/* The current playlist has been modified */
|
||||
LISTENER_PLAYLIST = (1 << 1),
|
||||
/* The volume has been changed */
|
||||
LISTENER_VOLUME = (1 << 2),
|
||||
/* A speaker has been enabled or disabled */
|
||||
LISTENER_SPEAKER = (1 << 3),
|
||||
/* Options like repeat, random has been changed */
|
||||
LISTENER_OPTIONS = (1 << 4),
|
||||
/* The library has been modified */
|
||||
LISTENER_DATABASE = (1 << 5),
|
||||
};
|
||||
|
||||
typedef void (*notify)(enum listener_event_type type);
|
||||
|
||||
/*
|
||||
* Registers the given callback function to the given event types.
|
||||
* This function is not thread safe. Listeners must be added once at startup.
|
||||
*
|
||||
* @param notify_cb Callback function
|
||||
* @param events Event mask, one or more of LISTENER_*
|
||||
* @return 0 on success, -1 on failure
|
||||
*/
|
||||
int
|
||||
listener_add(notify notify_cb, short events);
|
||||
|
||||
/*
|
||||
* Removes the given callback function
|
||||
* This function is not thread safe. Listeners must be removed once at shutdown.
|
||||
*
|
||||
* @param notify_cb Callback function
|
||||
* @return 0 on success, -1 if the callback was not registered
|
||||
*/
|
||||
int
|
||||
listener_remove(notify notify_cb);
|
||||
|
||||
/*
|
||||
* Calls the callback function of the registered listeners listening for the
|
||||
* given type of event.
|
||||
*
|
||||
* @param type The event type, on of the LISTENER_* values
|
||||
*
|
||||
*/
|
||||
void
|
||||
listener_notify(enum listener_event_type type);
|
||||
|
||||
#endif /* !__LISTENER_H__ */
|
349
src/mpd.c
349
src/mpd.c
@ -34,6 +34,11 @@
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
# include <event2/event.h>
|
||||
# include <event2/buffer.h>
|
||||
# include <event2/bufferevent.h>
|
||||
# include <event2/listener.h>
|
||||
|
||||
#if defined(HAVE_SYS_EVENTFD_H) && defined(HAVE_EVENTFD)
|
||||
# define USE_EVENTFD
|
||||
# include <sys/eventfd.h>
|
||||
@ -47,7 +52,7 @@
|
||||
#include "db.h"
|
||||
#include "conffile.h"
|
||||
#include "misc.h"
|
||||
#include "mpd.h"
|
||||
#include "listener.h"
|
||||
|
||||
#include "player.h"
|
||||
#include "filescanner.h"
|
||||
@ -59,6 +64,26 @@ struct event_base *evbase_mpd;
|
||||
static int g_exit_pipe[2];
|
||||
static struct event *g_exitev;
|
||||
|
||||
static int g_cmd_pipe[2];
|
||||
static struct event *g_cmdev;
|
||||
|
||||
struct mpd_command;
|
||||
|
||||
typedef int (*cmd_func)(struct mpd_command *cmd);
|
||||
|
||||
struct mpd_command
|
||||
{
|
||||
pthread_mutex_t lck;
|
||||
pthread_cond_t cond;
|
||||
|
||||
cmd_func func;
|
||||
|
||||
enum listener_event_type arg_evtype;
|
||||
int nonblock;
|
||||
|
||||
int ret;
|
||||
};
|
||||
|
||||
#define COMMAND_ARGV_MAX 37
|
||||
|
||||
/* MPD error codes (taken from ack.h) */
|
||||
@ -121,6 +146,51 @@ free_outputs(struct output *outputs)
|
||||
}
|
||||
}
|
||||
|
||||
struct idle_client
|
||||
{
|
||||
struct evbuffer *evbuffer;
|
||||
short events;
|
||||
|
||||
struct idle_client *next;
|
||||
};
|
||||
|
||||
struct idle_client *idle_clients;
|
||||
|
||||
/* ---------------------------- COMMAND EXECUTION -------------------------- */
|
||||
|
||||
static int
|
||||
send_command(struct mpd_command *cmd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!cmd->func)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "BUG: cmd->func is NULL!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = write(g_cmd_pipe[1], &cmd, sizeof(cmd));
|
||||
if (ret != sizeof(cmd))
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Could not send command: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nonblock_command(struct mpd_command *cmd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = send_command(cmd);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
thread_exit(void)
|
||||
{
|
||||
@ -553,13 +623,95 @@ mpd_command_currentsong(struct evbuffer *evbuf, int argc, char **argv, char **er
|
||||
static int
|
||||
mpd_command_idle(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
||||
{
|
||||
DPRINTF(E_WARN, L_MPD, "Idle command is not supported by forked-daapd, there will be no notifications about changes\n");
|
||||
struct idle_client *client;
|
||||
int i;
|
||||
|
||||
client = (struct idle_client*)malloc(sizeof(struct idle_client));
|
||||
if (!client)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Out of memory for idle_client\n");
|
||||
return ACK_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
client->evbuffer = evbuf;
|
||||
client->events = 0;
|
||||
client->next = idle_clients;
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
if (0 == strcmp(argv[i], "database"))
|
||||
{
|
||||
client->events |= LISTENER_DATABASE;
|
||||
}
|
||||
else if (0 == strcmp(argv[i], "player"))
|
||||
{
|
||||
client->events |= LISTENER_PLAYER;
|
||||
}
|
||||
else if (0 == strcmp(argv[i], "playlist"))
|
||||
{
|
||||
client->events |= LISTENER_PLAYLIST;
|
||||
}
|
||||
else if (0 == strcmp(argv[i], "mixer"))
|
||||
{
|
||||
client->events |= LISTENER_VOLUME;
|
||||
}
|
||||
else if (0 == strcmp(argv[i], "output"))
|
||||
{
|
||||
client->events |= LISTENER_SPEAKER;
|
||||
}
|
||||
else if (0 == strcmp(argv[i], "options"))
|
||||
{
|
||||
client->events |= LISTENER_OPTIONS;
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINTF(E_DBG, L_MPD, "Idle command for '%s' not supported\n", argv[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
client->events = LISTENER_PLAYER | LISTENER_PLAYLIST | LISTENER_VOLUME | LISTENER_SPEAKER | LISTENER_OPTIONS;
|
||||
|
||||
idle_clients = client;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mpd_remove_idle_client(struct evbuffer *evbuf)
|
||||
{
|
||||
struct idle_client *client;
|
||||
struct idle_client *prev;
|
||||
|
||||
client = idle_clients;
|
||||
prev = NULL;
|
||||
|
||||
while (client)
|
||||
{
|
||||
if (client->evbuffer == evbuf)
|
||||
{
|
||||
DPRINTF(E_DBG, L_MPD, "Removing idle client for evbuffer\n");
|
||||
|
||||
if (prev)
|
||||
prev->next = client->next;
|
||||
else
|
||||
idle_clients = client->next;
|
||||
|
||||
free(client);
|
||||
break;
|
||||
}
|
||||
|
||||
prev = client;
|
||||
client = client->next;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
mpd_command_noidle(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
||||
{
|
||||
mpd_remove_idle_client(evbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -623,7 +775,7 @@ mpd_command_status(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
||||
status.shuffle,
|
||||
(status.repeat == REPEAT_SONG ? 1 : 0),
|
||||
0 /* consume: not supported by forked-daapd, always return 'off' */,
|
||||
status.plid,
|
||||
status.plversion,
|
||||
status.playlistlength,
|
||||
state);
|
||||
|
||||
@ -1648,6 +1800,7 @@ mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **e
|
||||
static int
|
||||
mpd_command_plchanges(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
||||
{
|
||||
DPRINTF(E_WARN, L_MPD, "Ignore command %s\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3697,11 +3850,20 @@ mpd_read_cb(struct bufferevent *bev, void *ctx)
|
||||
static void
|
||||
mpd_event_cb(struct bufferevent *bev, short events, void *ctx)
|
||||
{
|
||||
struct evbuffer *evbuf;
|
||||
|
||||
if (events & BEV_EVENT_ERROR)
|
||||
DPRINTF(E_LOG, L_MPD, "Error from buffer event\n");
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Error from bufferevent: %s\n",
|
||||
evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
|
||||
}
|
||||
|
||||
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR))
|
||||
{
|
||||
evbuf = bufferevent_get_output(bev);
|
||||
mpd_remove_idle_client(evbuf);
|
||||
bufferevent_free(bev);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3807,6 +3969,147 @@ mpd_accept_error_cb(struct evconnlistener *listener, void *ctx)
|
||||
DPRINTF(E_LOG, L_MPD, "Error occured %d (%s) on the listener.\n", err, evutil_socket_error_to_string(err));
|
||||
}
|
||||
|
||||
static int
|
||||
mpd_notify_idle_client(struct idle_client *client, enum listener_event_type type)
|
||||
{
|
||||
if (!(client->events & type))
|
||||
{
|
||||
DPRINTF(E_DBG, L_MPD, "Client not listening for event: %d\n", type);
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case LISTENER_PLAYER:
|
||||
evbuffer_add(client->evbuffer, "changed: player\n", 16);
|
||||
break;
|
||||
|
||||
case LISTENER_PLAYLIST:
|
||||
evbuffer_add(client->evbuffer, "changed: playlist\n", 18);
|
||||
break;
|
||||
|
||||
case LISTENER_VOLUME:
|
||||
evbuffer_add(client->evbuffer, "changed: mixer\n", 15);
|
||||
break;
|
||||
|
||||
case LISTENER_SPEAKER:
|
||||
evbuffer_add(client->evbuffer, "changed: output\n", 16);
|
||||
break;
|
||||
|
||||
case LISTENER_OPTIONS:
|
||||
evbuffer_add(client->evbuffer, "changed: options\n", 17);
|
||||
break;
|
||||
|
||||
default:
|
||||
DPRINTF(E_WARN, L_MPD, "Unsupported event type (%d) in notify idle clients.\n", type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
evbuffer_add(client->evbuffer, "OK\n", 3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mpd_notify_idle(struct mpd_command *cmd)
|
||||
{
|
||||
struct idle_client *client;
|
||||
struct idle_client *prev;
|
||||
struct idle_client *next;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
DPRINTF(E_DBG, L_MPD, "Notify clients waiting for idle results: %d\n", cmd->arg_evtype);
|
||||
|
||||
prev = NULL;
|
||||
next = NULL;
|
||||
i = 0;
|
||||
client = idle_clients;
|
||||
while (client)
|
||||
{
|
||||
DPRINTF(E_DBG, L_MPD, "Notify client #%d\n", i);
|
||||
|
||||
next = client->next;
|
||||
|
||||
ret = mpd_notify_idle_client(client, cmd->arg_evtype);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
if (prev)
|
||||
prev->next = next;
|
||||
else
|
||||
idle_clients = next;
|
||||
|
||||
free(client);
|
||||
}
|
||||
else
|
||||
{
|
||||
prev = client;
|
||||
}
|
||||
|
||||
client = next;
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
mpd_listener_cb(enum listener_event_type type)
|
||||
{
|
||||
DPRINTF(E_DBG, L_MPD, "Listener callback called with event type %d.\n", type);
|
||||
struct mpd_command *cmd;
|
||||
|
||||
cmd = (struct mpd_command *)malloc(sizeof(struct mpd_command));
|
||||
if (!cmd)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Could not allocate cache_command\n");
|
||||
return;
|
||||
}
|
||||
|
||||
memset(cmd, 0, sizeof(struct mpd_command));
|
||||
|
||||
cmd->nonblock = 1;
|
||||
|
||||
cmd->func = mpd_notify_idle;
|
||||
cmd->arg_evtype = type;
|
||||
|
||||
nonblock_command(cmd);
|
||||
}
|
||||
|
||||
static void
|
||||
command_cb(int fd, short what, void *arg)
|
||||
{
|
||||
struct mpd_command *cmd;
|
||||
int ret;
|
||||
|
||||
ret = read(g_cmd_pipe[0], &cmd, sizeof(cmd));
|
||||
if (ret != sizeof(cmd))
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Could not read command! (read %d): %s\n", ret, (ret < 0) ? strerror(errno) : "-no error-");
|
||||
goto readd;
|
||||
}
|
||||
|
||||
if (cmd->nonblock)
|
||||
{
|
||||
cmd->func(cmd);
|
||||
|
||||
free(cmd);
|
||||
goto readd;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&cmd->lck);
|
||||
|
||||
ret = cmd->func(cmd);
|
||||
cmd->ret = ret;
|
||||
|
||||
pthread_cond_signal(&cmd->cond);
|
||||
pthread_mutex_unlock(&cmd->lck);
|
||||
|
||||
readd:
|
||||
event_add(g_cmdev, NULL);
|
||||
}
|
||||
|
||||
|
||||
/* Thread: main */
|
||||
int mpd_init(void)
|
||||
@ -3841,6 +4144,17 @@ int mpd_init(void)
|
||||
goto exit_fail;
|
||||
}
|
||||
|
||||
# if defined(__linux__)
|
||||
ret = pipe2(g_cmd_pipe, O_CLOEXEC);
|
||||
# else
|
||||
ret = pipe(g_cmd_pipe);
|
||||
# endif
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Could not create command pipe: %s\n", strerror(errno));
|
||||
goto cmd_fail;
|
||||
}
|
||||
|
||||
evbase_mpd = event_base_new();
|
||||
if (!evbase_mpd)
|
||||
{
|
||||
@ -3857,6 +4171,16 @@ int mpd_init(void)
|
||||
|
||||
event_add(g_exitev, NULL);
|
||||
|
||||
|
||||
g_cmdev = event_new(evbase_mpd, g_cmd_pipe[0], EV_READ, command_cb, NULL);
|
||||
if (!g_cmdev)
|
||||
{
|
||||
DPRINTF(E_LOG, L_MPD, "Could not create cmd event\n");
|
||||
goto evnew_fail;
|
||||
}
|
||||
|
||||
event_add(g_cmdev, NULL);
|
||||
|
||||
if (v6enabled)
|
||||
{
|
||||
saddr_length = sizeof(sin6);
|
||||
@ -3902,6 +4226,9 @@ int mpd_init(void)
|
||||
goto thread_fail;
|
||||
}
|
||||
|
||||
idle_clients = NULL;
|
||||
listener_add(mpd_listener_cb, LISTENER_PLAYER | LISTENER_PLAYLIST | LISTENER_VOLUME | LISTENER_SPEAKER | LISTENER_OPTIONS);
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
@ -3912,6 +4239,10 @@ int mpd_init(void)
|
||||
evbase_mpd = NULL;
|
||||
|
||||
evbase_fail:
|
||||
close(g_cmd_pipe[0]);
|
||||
close(g_cmd_pipe[1]);
|
||||
|
||||
cmd_fail:
|
||||
close(g_exit_pipe[0]);
|
||||
close(g_exit_pipe[1]);
|
||||
|
||||
@ -3922,6 +4253,7 @@ int mpd_init(void)
|
||||
/* Thread: main */
|
||||
void mpd_deinit(void)
|
||||
{
|
||||
struct idle_client *temp;
|
||||
unsigned short port;
|
||||
int ret;
|
||||
|
||||
@ -3941,6 +4273,15 @@ void mpd_deinit(void)
|
||||
return;
|
||||
}
|
||||
|
||||
listener_remove(mpd_listener_cb);
|
||||
|
||||
while (idle_clients)
|
||||
{
|
||||
temp = idle_clients;
|
||||
idle_clients = idle_clients->next;
|
||||
free(temp);
|
||||
}
|
||||
|
||||
// Free event base (should free events too)
|
||||
event_base_free(evbase_mpd);
|
||||
|
||||
|
@ -2,14 +2,6 @@
|
||||
#ifndef __MPD_H__
|
||||
#define __MPD_H__
|
||||
|
||||
#ifdef HAVE_LIBEVENT2
|
||||
# include <event2/event.h>
|
||||
# include <event2/buffer.h>
|
||||
# include <event2/bufferevent.h>
|
||||
# include <event2/listener.h>
|
||||
#else
|
||||
# include <event.h>
|
||||
#endif
|
||||
|
||||
int
|
||||
mpd_init(void);
|
||||
|
82
src/player.c
82
src/player.c
@ -58,6 +58,7 @@
|
||||
#include "raop.h"
|
||||
#include "laudio.h"
|
||||
#include "worker.h"
|
||||
#include "listener.h"
|
||||
|
||||
#ifdef LASTFM
|
||||
# include "lastfm.h"
|
||||
@ -164,7 +165,6 @@ struct player_command
|
||||
struct player_status *status;
|
||||
struct player_source *ps;
|
||||
struct player_metadata *pmd;
|
||||
player_status_handler status_handler;
|
||||
uint32_t *id_ptr;
|
||||
uint64_t *raop_ids;
|
||||
enum repeat_mode mode;
|
||||
@ -207,9 +207,6 @@ static enum play_status player_state;
|
||||
static enum repeat_mode repeat;
|
||||
static char shuffle;
|
||||
|
||||
/* Status updates (for DACP) */
|
||||
static player_status_handler update_handler;
|
||||
|
||||
/* Playback timer */
|
||||
#if defined(__linux__)
|
||||
static int pb_timer_fd;
|
||||
@ -259,6 +256,7 @@ static struct player_source *shuffle_head;
|
||||
static struct player_source *cur_playing;
|
||||
static struct player_source *cur_streaming;
|
||||
static uint32_t cur_plid;
|
||||
static uint32_t cur_plversion;
|
||||
static struct evbuffer *audio_buf;
|
||||
|
||||
/* Play history */
|
||||
@ -299,8 +297,7 @@ status_update(enum play_status status)
|
||||
{
|
||||
player_state = status;
|
||||
|
||||
if (update_handler)
|
||||
update_handler();
|
||||
listener_notify(LISTENER_PLAYER);
|
||||
|
||||
if (status == PLAY_PLAYING)
|
||||
dev_autoselect = 0;
|
||||
@ -1642,6 +1639,10 @@ source_count()
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates cur_playing and notifies remotes and raop devices about
|
||||
* changes.
|
||||
*/
|
||||
static uint64_t
|
||||
source_check(void)
|
||||
{
|
||||
@ -1665,6 +1666,7 @@ source_check(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If cur_playing is NULL, we are still in the first two seconds after starting the stream */
|
||||
if (!cur_playing)
|
||||
{
|
||||
if (pos >= cur_streaming->output_start)
|
||||
@ -1678,9 +1680,13 @@ source_check(void)
|
||||
return pos;
|
||||
}
|
||||
|
||||
/* Check if we are still in the middle of the current playing song */
|
||||
if ((cur_playing->end == 0) || (pos < cur_playing->end))
|
||||
return pos;
|
||||
|
||||
/* We have reached the end of the current playing song, update cur_playing to the next song in the queue
|
||||
and initialize stream_start and output_start values. */
|
||||
|
||||
r_mode = repeat;
|
||||
/* Playlist has only one file, treat REPEAT_ALL as REPEAT_SONG */
|
||||
if ((r_mode == REPEAT_ALL) && (source_head == source_head->pl_next))
|
||||
@ -2509,6 +2515,7 @@ get_status(struct player_command *cmd)
|
||||
status->volume = master_volume;
|
||||
|
||||
status->plid = cur_plid;
|
||||
status->plversion = cur_plversion;
|
||||
|
||||
switch (player_state)
|
||||
{
|
||||
@ -3510,6 +3517,8 @@ speaker_set(struct player_command *cmd)
|
||||
}
|
||||
}
|
||||
|
||||
listener_notify(LISTENER_SPEAKER);
|
||||
|
||||
if (cmd->raop_pending > 0)
|
||||
return 1; /* async */
|
||||
|
||||
@ -3556,6 +3565,8 @@ volume_set(struct player_command *cmd)
|
||||
cmd->raop_pending += raop_set_volume_one(rd->session, rd->volume, device_command_cb);
|
||||
}
|
||||
|
||||
listener_notify(LISTENER_VOLUME);
|
||||
|
||||
if (cmd->raop_pending > 0)
|
||||
return 1; /* async */
|
||||
|
||||
@ -3606,6 +3617,8 @@ volume_setrel_speaker(struct player_command *cmd)
|
||||
}
|
||||
}
|
||||
|
||||
listener_notify(LISTENER_VOLUME);
|
||||
|
||||
if (cmd->raop_pending > 0)
|
||||
return 1; /* async */
|
||||
|
||||
@ -3665,6 +3678,8 @@ volume_setabs_speaker(struct player_command *cmd)
|
||||
}
|
||||
}
|
||||
|
||||
listener_notify(LISTENER_VOLUME);
|
||||
|
||||
if (cmd->raop_pending > 0)
|
||||
return 1; /* async */
|
||||
|
||||
@ -3674,6 +3689,9 @@ volume_setabs_speaker(struct player_command *cmd)
|
||||
static int
|
||||
repeat_set(struct player_command *cmd)
|
||||
{
|
||||
if (cmd->arg.mode == repeat)
|
||||
return 0;
|
||||
|
||||
switch (cmd->arg.mode)
|
||||
{
|
||||
case REPEAT_OFF:
|
||||
@ -3687,6 +3705,8 @@ repeat_set(struct player_command *cmd)
|
||||
return -1;
|
||||
}
|
||||
|
||||
listener_notify(LISTENER_OPTIONS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3708,6 +3728,8 @@ shuffle_set(struct player_command *cmd)
|
||||
return -1;
|
||||
}
|
||||
|
||||
listener_notify(LISTENER_OPTIONS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3851,6 +3873,9 @@ queue_add(struct player_command *cmd)
|
||||
|
||||
if (cur_plid != 0)
|
||||
cur_plid = 0;
|
||||
cur_plversion++;
|
||||
|
||||
listener_notify(LISTENER_PLAYLIST);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -3892,6 +3917,9 @@ queue_add_next(struct player_command *cmd)
|
||||
|
||||
if (cur_plid != 0)
|
||||
cur_plid = 0;
|
||||
cur_plversion++;
|
||||
|
||||
listener_notify(LISTENER_PLAYLIST);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -3960,6 +3988,10 @@ queue_move(struct player_command *cmd)
|
||||
ps_dst->pl_next = ps_src;
|
||||
}
|
||||
|
||||
cur_plversion++;
|
||||
|
||||
listener_notify(LISTENER_PLAYLIST);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -4025,6 +4057,10 @@ queue_remove(struct player_command *cmd)
|
||||
|
||||
source_free(ps);
|
||||
|
||||
cur_plversion++;
|
||||
|
||||
listener_notify(LISTENER_PLAYLIST);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -4050,6 +4086,9 @@ queue_clear(struct player_command *cmd)
|
||||
}
|
||||
|
||||
cur_plid = 0;
|
||||
cur_plversion++;
|
||||
|
||||
listener_notify(LISTENER_PLAYLIST);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -4100,6 +4139,10 @@ queue_empty(struct player_command *cmd)
|
||||
source_head->shuffle_prev = source_head;
|
||||
}
|
||||
|
||||
cur_plversion++;
|
||||
|
||||
listener_notify(LISTENER_PLAYLIST);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -4114,14 +4157,6 @@ queue_plid(struct player_command *cmd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
set_update_handler(struct player_command *cmd)
|
||||
{
|
||||
update_handler = cmd->arg.status_handler;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Command processing */
|
||||
/* Thread: player */
|
||||
static void
|
||||
@ -4810,22 +4845,6 @@ player_queue_plid(uint32_t plid)
|
||||
command_deinit(&cmd);
|
||||
}
|
||||
|
||||
void
|
||||
player_set_update_handler(player_status_handler handler)
|
||||
{
|
||||
struct player_command cmd;
|
||||
|
||||
command_init(&cmd);
|
||||
|
||||
cmd.func = set_update_handler;
|
||||
cmd.func_bh = NULL;
|
||||
cmd.arg.status_handler = handler;
|
||||
|
||||
sync_command(&cmd);
|
||||
|
||||
command_deinit(&cmd);
|
||||
}
|
||||
|
||||
/* Non-blocking commands used by mDNS */
|
||||
static void
|
||||
player_device_add(struct raop_device *rd)
|
||||
@ -5182,13 +5201,12 @@ player_init(void)
|
||||
cur_playing = NULL;
|
||||
cur_streaming = NULL;
|
||||
cur_plid = 0;
|
||||
cur_plversion = 0;
|
||||
|
||||
player_state = PLAY_STOPPED;
|
||||
repeat = REPEAT_OFF;
|
||||
shuffle = 0;
|
||||
|
||||
update_handler = NULL;
|
||||
|
||||
history = (struct player_history *)calloc(1, sizeof(struct player_history));
|
||||
|
||||
/*
|
||||
|
@ -56,6 +56,11 @@ struct player_status {
|
||||
|
||||
/* Playlist id */
|
||||
uint32_t plid;
|
||||
/* Playlist version
|
||||
After startup plversion is 0 and gets incremented after each change of the playlist
|
||||
(e. g. after adding/moving/removing items). It is used by mpd clients to recognize if
|
||||
they need to update the current playlist. */
|
||||
uint32_t plversion;
|
||||
/* Playlist length */
|
||||
uint32_t playlistlength;
|
||||
/* Playing song id*/
|
||||
@ -73,7 +78,6 @@ struct player_status {
|
||||
};
|
||||
|
||||
typedef void (*spk_enum_cb)(uint64_t id, const char *name, int relvol, struct spk_flags flags, void *arg);
|
||||
typedef void (*player_status_handler)(void);
|
||||
|
||||
struct player_source
|
||||
{
|
||||
@ -229,9 +233,6 @@ player_queue_plid(uint32_t plid);
|
||||
struct player_history *
|
||||
player_history_get(void);
|
||||
|
||||
void
|
||||
player_set_update_handler(player_status_handler handler);
|
||||
|
||||
int
|
||||
player_init(void);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user