mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-27 23:55:57 -05:00
Better thread safety by making sure that the raop globals are only accessed through the player thread
This commit is contained in:
parent
50ef710549
commit
ad81e05ab4
82
src/player.c
82
src/player.c
@ -154,6 +154,7 @@ struct player_command
|
|||||||
void *noarg;
|
void *noarg;
|
||||||
struct spk_enum *spk_enum;
|
struct spk_enum *spk_enum;
|
||||||
struct raop_device *rd;
|
struct raop_device *rd;
|
||||||
|
struct raop_metadata *rmd;
|
||||||
struct player_status *status;
|
struct player_status *status;
|
||||||
struct player_source *ps;
|
struct player_source *ps;
|
||||||
player_status_handler status_handler;
|
player_status_handler status_handler;
|
||||||
@ -578,6 +579,9 @@ playback_abort(void);
|
|||||||
static int
|
static int
|
||||||
queue_clear(struct player_command *cmd);
|
queue_clear(struct player_command *cmd);
|
||||||
|
|
||||||
|
static void
|
||||||
|
player_metadata_send(struct raop_metadata *rmd);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
player_laudio_status_cb(enum laudio_state status)
|
player_laudio_status_cb(enum laudio_state status)
|
||||||
{
|
{
|
||||||
@ -626,7 +630,7 @@ player_laudio_status_cb(enum laudio_state status)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Callbacks from the worker thread */
|
/* Callback from the worker thread (async operation as it may block) */
|
||||||
static void
|
static void
|
||||||
playcount_inc_cb(void *arg)
|
playcount_inc_cb(void *arg)
|
||||||
{
|
{
|
||||||
@ -635,14 +639,23 @@ playcount_inc_cb(void *arg)
|
|||||||
db_file_inc_playcount(*id);
|
db_file_inc_playcount(*id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Callback from the worker thread
|
||||||
|
* This prepares metadata in the worker thread, since especially the artwork
|
||||||
|
* retrieval may take some time. raop_metadata_prepare() is thread safe. The
|
||||||
|
* sending, however, must be done in the player thread.
|
||||||
|
*/
|
||||||
static void
|
static void
|
||||||
metadata_send_cb(void *arg)
|
metadata_prepare_cb(void *arg)
|
||||||
{
|
{
|
||||||
struct raop_metadata_arg *rma = arg;
|
struct raop_metadata *rmd;
|
||||||
|
|
||||||
raop_metadata_send(rma);
|
rmd = raop_metadata_prepare(arg);
|
||||||
|
|
||||||
|
if (rmd)
|
||||||
|
player_metadata_send(rmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Callback from the worker thread (async operation as it may block) */
|
||||||
static void
|
static void
|
||||||
update_icy_cb(void *arg)
|
update_icy_cb(void *arg)
|
||||||
{
|
{
|
||||||
@ -667,7 +680,7 @@ metadata_purge(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
metadata_send(struct player_source *ps, int startup)
|
metadata_trigger(struct player_source *ps, int startup)
|
||||||
{
|
{
|
||||||
struct raop_metadata_arg rma;
|
struct raop_metadata_arg rma;
|
||||||
|
|
||||||
@ -695,11 +708,11 @@ metadata_send(struct player_source *ps, int startup)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
rma.rtptime = 0;
|
rma.rtptime = 0;
|
||||||
DPRINTF(E_LOG, L_PLAYER, "PTOH! Unhandled song boundary case in metadata_send()\n");
|
DPRINTF(E_LOG, L_PLAYER, "PTOH! Unhandled song boundary case in metadata_trigger()\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Defer the actual work of sending the metadata to the worker thread */
|
/* Defer the actual work of preparing the metadata to the worker thread */
|
||||||
worker_execute(metadata_send_cb, &rma, sizeof(struct raop_metadata_arg), 0);
|
worker_execute(metadata_prepare_cb, &rma, sizeof(struct raop_metadata_arg), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -732,7 +745,7 @@ metadata_icy_poll_cb(int fd, short what, void *arg)
|
|||||||
worker_execute(update_icy_cb, metadata, sizeof(struct http_icy_metadata), 0);
|
worker_execute(update_icy_cb, metadata, sizeof(struct http_icy_metadata), 0);
|
||||||
|
|
||||||
status_update(player_state);
|
status_update(player_state);
|
||||||
metadata_send(cur_streaming, 0);
|
metadata_trigger(cur_streaming, 0);
|
||||||
|
|
||||||
/* Only free the struct, the content must be preserved for update_icy_cb */
|
/* Only free the struct, the content must be preserved for update_icy_cb */
|
||||||
free(metadata);
|
free(metadata);
|
||||||
@ -1427,7 +1440,7 @@ source_open(struct player_source *ps, int no_md)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!no_md)
|
if (!no_md)
|
||||||
metadata_send(ps, (player_state == PLAY_PLAYING) ? 0 : 1);
|
metadata_trigger(ps, (player_state == PLAY_PLAYING) ? 0 : 1);
|
||||||
|
|
||||||
ps->setup_done = 1;
|
ps->setup_done = 1;
|
||||||
|
|
||||||
@ -1480,7 +1493,7 @@ source_next(int force)
|
|||||||
* so we have to handle metadata ourselves here
|
* so we have to handle metadata ourselves here
|
||||||
*/
|
*/
|
||||||
if (ret >= 0)
|
if (ret >= 0)
|
||||||
metadata_send(cur_streaming, 0);
|
metadata_trigger(cur_streaming, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
ret = source_open(cur_streaming, force);
|
ret = source_open(cur_streaming, force);
|
||||||
@ -2159,6 +2172,18 @@ device_remove_family(struct player_command *cmd)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
metadata_send(struct player_command *cmd)
|
||||||
|
{
|
||||||
|
struct raop_metadata *rmd;
|
||||||
|
|
||||||
|
rmd = cmd->arg.rmd;
|
||||||
|
|
||||||
|
raop_metadata_send(rmd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* RAOP callbacks executed in the player thread */
|
/* RAOP callbacks executed in the player thread */
|
||||||
static void
|
static void
|
||||||
device_streaming_cb(struct raop_device *dev, struct raop_session *rs, enum raop_session_state status)
|
device_streaming_cb(struct raop_device *dev, struct raop_session *rs, enum raop_session_state status)
|
||||||
@ -2862,7 +2887,7 @@ playback_start(struct player_command *cmd)
|
|||||||
* After a pause, the source is still open so source_open() doesn't get
|
* After a pause, the source is still open so source_open() doesn't get
|
||||||
* called and we have to handle metadata ourselves.
|
* called and we have to handle metadata ourselves.
|
||||||
*/
|
*/
|
||||||
metadata_send(cur_streaming, 1);
|
metadata_trigger(cur_streaming, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Start local audio if needed */
|
/* Start local audio if needed */
|
||||||
@ -4869,6 +4894,39 @@ player_device_remove(struct raop_device *rd)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Thread: worker */
|
||||||
|
static void
|
||||||
|
player_metadata_send(struct raop_metadata *rmd)
|
||||||
|
{
|
||||||
|
struct player_command *cmd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
cmd = (struct player_command *)malloc(sizeof(struct player_command));
|
||||||
|
if (!cmd)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_PLAYER, "Could not allocate player_command\n");
|
||||||
|
|
||||||
|
raop_metadata_free(rmd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(cmd, 0, sizeof(struct player_command));
|
||||||
|
|
||||||
|
cmd->nonblock = 1;
|
||||||
|
|
||||||
|
cmd->func = metadata_send;
|
||||||
|
cmd->arg.rmd = rmd;
|
||||||
|
|
||||||
|
ret = nonblock_command(cmd);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
free(cmd);
|
||||||
|
raop_metadata_free(rmd);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* RAOP devices discovery - mDNS callback */
|
/* RAOP devices discovery - mDNS callback */
|
||||||
/* Thread: main (mdns) */
|
/* Thread: main (mdns) */
|
||||||
|
52
src/raop.c
52
src/raop.c
@ -154,6 +154,9 @@ struct raop_metadata
|
|||||||
uint64_t start;
|
uint64_t start;
|
||||||
uint64_t end;
|
uint64_t end;
|
||||||
|
|
||||||
|
uint64_t offset;
|
||||||
|
int startup;
|
||||||
|
|
||||||
struct raop_metadata *next;
|
struct raop_metadata *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -708,7 +711,7 @@ raop_crypt_encrypt_aes_key_base64(void)
|
|||||||
|
|
||||||
|
|
||||||
/* RAOP metadata */
|
/* RAOP metadata */
|
||||||
static void
|
void
|
||||||
raop_metadata_free(struct raop_metadata *rmd)
|
raop_metadata_free(struct raop_metadata *rmd)
|
||||||
{
|
{
|
||||||
evbuffer_free(rmd->metadata);
|
evbuffer_free(rmd->metadata);
|
||||||
@ -751,8 +754,9 @@ raop_metadata_prune(uint64_t rtptime)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct raop_metadata *
|
/* Thread: worker */
|
||||||
raop_metadata_prepare(int id, uint64_t rtptime)
|
struct raop_metadata *
|
||||||
|
raop_metadata_prepare(struct raop_metadata_arg *rma)
|
||||||
{
|
{
|
||||||
struct query_params qp;
|
struct query_params qp;
|
||||||
struct db_media_file_info dbmfi;
|
struct db_media_file_info dbmfi;
|
||||||
@ -781,10 +785,10 @@ raop_metadata_prepare(int id, uint64_t rtptime)
|
|||||||
goto skip_artwork;
|
goto skip_artwork;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = artwork_get_item(id, 600, 600, rmd->artwork);
|
ret = artwork_get_item(rma->id, 600, 600, rmd->artwork);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_INFO, L_RAOP, "Failed to retrieve artwork for file id %d; no artwork will be sent\n", id);
|
DPRINTF(E_INFO, L_RAOP, "Failed to retrieve artwork for file id %d; no artwork will be sent\n", rma->id);
|
||||||
|
|
||||||
evbuffer_free(rmd->artwork);
|
evbuffer_free(rmd->artwork);
|
||||||
rmd->artwork = NULL;
|
rmd->artwork = NULL;
|
||||||
@ -801,10 +805,10 @@ raop_metadata_prepare(int id, uint64_t rtptime)
|
|||||||
qp.sort = S_NONE;
|
qp.sort = S_NONE;
|
||||||
qp.filter = filter;
|
qp.filter = filter;
|
||||||
|
|
||||||
ret = snprintf(filter, sizeof(filter), "id = %d", id);
|
ret = snprintf(filter, sizeof(filter), "id = %d", rma->id);
|
||||||
if ((ret < 0) || (ret >= sizeof(filter)))
|
if ((ret < 0) || (ret >= sizeof(filter)))
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_RAOP, "Could not build filter for file id %d; metadata will not be sent\n", id);
|
DPRINTF(E_LOG, L_RAOP, "Could not build filter for file id %d; metadata will not be sent\n", rma->id);
|
||||||
|
|
||||||
goto out_rmd;
|
goto out_rmd;
|
||||||
}
|
}
|
||||||
@ -820,7 +824,7 @@ raop_metadata_prepare(int id, uint64_t rtptime)
|
|||||||
ret = db_query_fetch_file(&qp, &dbmfi);
|
ret = db_query_fetch_file(&qp, &dbmfi);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_RAOP, "Couldn't fetch file id %d; metadata will not be sent\n", id);
|
DPRINTF(E_LOG, L_RAOP, "Couldn't fetch file id %d; metadata will not be sent\n", rma->id);
|
||||||
|
|
||||||
goto out_query;
|
goto out_query;
|
||||||
}
|
}
|
||||||
@ -863,17 +867,11 @@ raop_metadata_prepare(int id, uint64_t rtptime)
|
|||||||
|
|
||||||
db_query_end(&qp);
|
db_query_end(&qp);
|
||||||
|
|
||||||
rmd->start = rtptime;
|
rmd->start = rma->rtptime;
|
||||||
rmd->end = rtptime + (duration * 44100UL) / 1000UL;
|
rmd->end = rma->rtptime + (duration * 44100UL) / 1000UL;
|
||||||
|
|
||||||
/* Add rmd to metadata list */
|
rmd->startup = rma->startup;
|
||||||
if (metadata_tail)
|
rmd->offset = rma->offset;
|
||||||
metadata_tail->next = rmd;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
metadata_head = rmd;
|
|
||||||
metadata_tail = rmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rmd;
|
return rmd;
|
||||||
|
|
||||||
@ -2164,16 +2162,20 @@ raop_metadata_startup_send(struct raop_session *rs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
raop_metadata_send(struct raop_metadata_arg *rma)
|
raop_metadata_send(struct raop_metadata *rmd)
|
||||||
{
|
{
|
||||||
struct raop_session *rs;
|
struct raop_session *rs;
|
||||||
struct raop_metadata *rmd;
|
|
||||||
uint32_t delay;
|
uint32_t delay;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
rmd = raop_metadata_prepare(rma->id, rma->rtptime);
|
/* Add the rmd to the metadata list */
|
||||||
if (!rmd)
|
if (metadata_tail)
|
||||||
return;
|
metadata_tail->next = rmd;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
metadata_head = rmd;
|
||||||
|
metadata_tail = rmd;
|
||||||
|
}
|
||||||
|
|
||||||
for (rs = sessions; rs; rs = rs->next)
|
for (rs = sessions; rs; rs = rs->next)
|
||||||
{
|
{
|
||||||
@ -2183,9 +2185,9 @@ raop_metadata_send(struct raop_metadata_arg *rma)
|
|||||||
if (!rs->wants_metadata)
|
if (!rs->wants_metadata)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
delay = (rma->startup) ? RAOP_MD_DELAY_STARTUP : RAOP_MD_DELAY_SWITCH;
|
delay = (rmd->startup) ? RAOP_MD_DELAY_STARTUP : RAOP_MD_DELAY_SWITCH;
|
||||||
|
|
||||||
ret = raop_metadata_send_internal(rs, rmd, rma->offset, delay);
|
ret = raop_metadata_send_internal(rs, rmd, rmd->offset, delay);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
raop_session_failure(rs);
|
raop_session_failure(rs);
|
||||||
|
12
src/raop.h
12
src/raop.h
@ -26,6 +26,7 @@ enum raop_devtype {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct raop_session;
|
struct raop_session;
|
||||||
|
struct raop_metadata;
|
||||||
|
|
||||||
struct raop_device
|
struct raop_device
|
||||||
{
|
{
|
||||||
@ -98,6 +99,8 @@ enum raop_session_state
|
|||||||
|
|
||||||
typedef void (*raop_status_cb)(struct raop_device *dev, struct raop_session *rs, enum raop_session_state status);
|
typedef void (*raop_status_cb)(struct raop_device *dev, struct raop_session *rs, enum raop_session_state status);
|
||||||
|
|
||||||
|
void
|
||||||
|
raop_metadata_free(struct raop_metadata *rmd);
|
||||||
|
|
||||||
void
|
void
|
||||||
raop_metadata_purge(void);
|
raop_metadata_purge(void);
|
||||||
@ -105,6 +108,11 @@ raop_metadata_purge(void);
|
|||||||
void
|
void
|
||||||
raop_metadata_prune(uint64_t rtptime);
|
raop_metadata_prune(uint64_t rtptime);
|
||||||
|
|
||||||
|
struct raop_metadata *
|
||||||
|
raop_metadata_prepare(struct raop_metadata_arg *rma);
|
||||||
|
|
||||||
|
void
|
||||||
|
raop_metadata_send(struct raop_metadata *rmd);
|
||||||
|
|
||||||
int
|
int
|
||||||
raop_device_probe(struct raop_device *rd, raop_status_cb cb);
|
raop_device_probe(struct raop_device *rd, raop_status_cb cb);
|
||||||
@ -121,10 +129,6 @@ raop_playback_start(uint64_t next_pkt, struct timespec *ts);
|
|||||||
void
|
void
|
||||||
raop_playback_stop(void);
|
raop_playback_stop(void);
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
raop_metadata_send(struct raop_metadata_arg *rma);
|
|
||||||
|
|
||||||
int
|
int
|
||||||
raop_set_volume_one(struct raop_session *rs, int volume, raop_status_cb cb);
|
raop_set_volume_one(struct raop_session *rs, int volume, raop_status_cb cb);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user