[player] Try to consolidate metadata handling + use input interface

This commit is contained in:
ejurgensen 2017-01-22 23:21:48 +01:00
parent 7b6a7b65b3
commit 0b9b008a1a
2 changed files with 80 additions and 176 deletions

View File

@ -123,28 +123,18 @@ struct spk_enum
void *arg;
};
struct icy_artwork
{
uint32_t id;
char *artwork_url;
};
struct player_metadata
{
int id;
uint64_t rtptime;
uint64_t offset;
int startup;
struct output_metadata *omd;
};
struct speaker_set_param
{
uint64_t *device_ids;
int intval;
};
struct metadata_param
{
struct input_metadata *input;
struct output_metadata *output;
};
union player_arg
{
struct volume_param vol_param;
@ -153,13 +143,12 @@ union player_arg
struct output_device *device;
struct player_status *status;
struct player_source *ps;
struct player_metadata *pmd;
struct metadata_param metadata_param;
uint32_t *id_ptr;
struct speaker_set_param speaker_set_param;
enum repeat_mode mode;
uint32_t id;
int intval;
struct icy_artwork icy;
};
struct event_base *evbase_player;
@ -447,9 +436,10 @@ static void
playback_suspend(void);
static void
player_metadata_send(struct player_metadata *pmd);
player_metadata_send(struct input_metadata *imd, struct output_metadata *omd);
/* Callback from the worker thread (async operation as it may block) */
// Callback from the worker thread (async operation as it may block)
static void
playcount_inc_cb(void *arg)
{
@ -459,7 +449,7 @@ playcount_inc_cb(void *arg)
}
#ifdef LASTFM
/* Callback from the worker thread (async operation as it may block) */
// Callback from the worker thread (async operation as it may block)
static void
scrobble_cb(void *arg)
{
@ -469,108 +459,72 @@ scrobble_cb(void *arg)
}
#endif
/* Callback from the worker thread
* This prepares metadata in the worker thread, since especially the artwork
* retrieval may take some time. outputs_metadata_prepare() must be thread safe.
* The sending must be done in the player thread.
*/
// Callback from the worker thread. Here the heavy lifting is done: updating the
// db_queue_item, retrieving artwork (through outputs_metadata_prepare) and
// when done, telling the player to send the metadata to the clients
static void
metadata_prepare_cb(void *arg)
metadata_update_cb(void *arg)
{
struct player_metadata *pmd = arg;
struct input_metadata *metadata = arg;
struct output_metadata *o_metadata;
struct db_queue_item *queue_item;
int ret;
pmd->omd = outputs_metadata_prepare(pmd->id);
if (pmd->omd)
player_metadata_send(pmd);
outputs_metadata_free(pmd->omd);
queue_item = db_queue_fetch_byitemid(metadata->item_id);
if (!queue_item)
{
DPRINTF(E_LOG, L_PLAYER, "Bug! Input metadata item_id does not match anything in queue\n");
goto out_free_metadata;
}
/* Callback from the worker thread (async operation as it may block) */
static void
update_icy_cb(void *arg)
// Since we won't be using the metadata struct values for anything else than
// this we just swap pointers
if (metadata->artist)
swap_pointers(&queue_item->artist, &metadata->artist);
if (metadata->title)
swap_pointers(&queue_item->title, &metadata->title);
if (metadata->album)
swap_pointers(&queue_item->album, &metadata->album);
if (metadata->artwork_url)
swap_pointers(&queue_item->artwork_url, &metadata->artwork_url);
ret = db_queue_update_item(queue_item);
if (ret < 0)
{
struct http_icy_metadata *metadata = arg;
db_queue_update_icymetadata(metadata->id, metadata->artist, metadata->title);
http_icy_metadata_free(metadata, 1);
DPRINTF(E_LOG, L_PLAYER, "Database error while updating queue with new metadata\n");
goto out_free_queueitem;
}
/* Metadata */
static void
metadata_prune(uint64_t pos)
{
outputs_metadata_prune(pos);
o_metadata = outputs_metadata_prepare(metadata->item_id);
// Actual sending must be done by player, since the worker does not own the outputs
player_metadata_send(metadata, o_metadata);
outputs_metadata_free(o_metadata);
out_free_queueitem:
free_queue_item(queue_item, 0);
out_free_metadata:
input_metadata_free(metadata, 1);
}
static void
metadata_purge(void)
{
outputs_metadata_purge();
}
static void
// Gets the metadata, but since the actual update requires db writes and
// possibly retrieving artwork we let the worker do the next step
void
metadata_trigger(int startup)
{
struct player_metadata pmd;
struct input_metadata metadata;
int ret;
memset(&pmd, 0, sizeof(struct player_metadata));
pmd.id = cur_streaming->item_id;
pmd.startup = startup;
if (cur_streaming->stream_start && cur_streaming->output_start)
{
pmd.offset = cur_streaming->output_start - cur_streaming->stream_start;
pmd.rtptime = cur_streaming->stream_start;
}
else
{
DPRINTF(E_LOG, L_PLAYER, "PTOH! Unhandled song boundary case in metadata_trigger()\n");
}
/* Defer the actual work of preparing the metadata to the worker thread */
worker_execute(metadata_prepare_cb, &pmd, sizeof(struct player_metadata), 0);
}
/* Checks if there is new HTTP ICY metadata, and if so sends updates to clients */
void
metadata_check_icy(void)
{
struct http_icy_metadata *metadata;
int changed;
metadata = transcode_metadata(cur_streaming->xcode, &changed);
if (!metadata)
ret = input_metadata_get(&metadata, cur_streaming, startup);
if (ret < 0)
return;
if (!changed || !metadata->title)
goto no_update;
if (metadata->title[0] == '\0')
goto no_update;
metadata->id = cur_streaming->item_id;
/* Defer the database update to the worker thread */
worker_execute(update_icy_cb, metadata, sizeof(struct http_icy_metadata), 0);
/* Triggers preparing and sending output metadata */
metadata_trigger(0);
/* Only free the struct, the content must be preserved for update_icy_cb */
free(metadata);
status_update(player_state);
return;
no_update:
http_icy_metadata_free(metadata, 0);
worker_execute(metadata_update_cb, &metadata, sizeof(metadata), 0);
}
struct player_history *
player_history_get(void)
{
@ -950,7 +904,7 @@ source_check(void)
status_update(PLAY_PLAYING);
metadata_prune(pos);
outputs_metadata_prune(pos);
}
return pos;
@ -1101,6 +1055,10 @@ source_read(uint8_t *buf, int len)
if (ret < 0)
return -1;
}
else if (flags & INPUT_FLAG_METADATA)
{
metadata_trigger(0);
}
// We pad the output buffer with silence if we don't have enough data for a
// full packet and there is no more data coming up (no more tracks in queue)
@ -1429,18 +1387,16 @@ static enum command_state
metadata_send(void *arg, int *retval)
{
union player_arg *cmdarg;
struct player_metadata *pmd;
struct input_metadata *imd;
struct output_metadata *omd;
cmdarg = arg;
pmd = cmdarg->pmd;
imd = cmdarg->metadata_param.input;
omd = cmdarg->metadata_param.output;
/* Do the setting of rtptime which was deferred in metadata_trigger because we
* wanted to wait until we had the actual last_rtptime
*/
if ((pmd->rtptime == 0) && (pmd->startup))
pmd->rtptime = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES;
outputs_metadata_send(omd, imd->rtptime, imd->offset, imd->startup);
outputs_metadata_send(pmd->omd, pmd->rtptime, pmd->offset, pmd->startup);
status_update(player_state);
*retval = 0;
return COMMAND_END;
@ -1724,7 +1680,7 @@ playback_abort(void)
status_update(PLAY_STOPPED);
metadata_purge();
outputs_metadata_purge();
}
/* Internal routine for waiting when input buffer underruns */
@ -1854,37 +1810,6 @@ now_playing(void *arg, int *retval)
return COMMAND_END;
}
static enum command_state
artwork_url_get(void *arg, int *retval)
{
union player_arg *cmdarg = arg;
struct player_source *ps;
cmdarg->icy.artwork_url = NULL;
if (cur_playing)
ps = cur_playing;
else if (cur_streaming)
ps = cur_streaming;
else
{
*retval = -1;
return COMMAND_END;
}
/* Check that we are playing a viable stream, and that it has the requested id */
if (!ps->xcode || ps->data_kind != DATA_KIND_HTTP || ps->id != cmdarg->icy.id)
{
*retval = -1;
return COMMAND_END;
}
cmdarg->icy.artwork_url = transcode_metadata_artwork_url(ps->xcode);
*retval = 0;
return COMMAND_END;
}
static enum command_state
playback_stop(void *arg, int *retval)
{
@ -1908,7 +1833,7 @@ playback_stop(void *arg, int *retval)
status_update(PLAY_STOPPED);
metadata_purge();
outputs_metadata_purge();
/* We're async if we need to flush devices */
if (*retval > 0)
@ -2343,7 +2268,7 @@ playback_pause(void *arg, int *retval)
source_pause(pos);
metadata_purge();
outputs_metadata_purge();
/* We're async if we need to flush devices */
if (*retval > 0)
@ -2794,25 +2719,6 @@ player_now_playing(uint32_t *id)
return ret;
}
char *
player_get_icy_artwork_url(uint32_t id)
{
union player_arg cmdarg;
int ret;
cmdarg.icy.id = id;
if (pthread_self() != tid_player)
ret = commands_exec_sync(cmdbase, artwork_url_get, NULL, &cmdarg);
else
artwork_url_get(&cmdarg, &ret);
if (ret < 0)
return NULL;
else
return cmdarg.icy.artwork_url;
}
/*
* Starts/resumes playback
*
@ -3057,11 +2963,12 @@ player_device_remove(void *device)
/* Thread: worker */
static void
player_metadata_send(struct player_metadata *pmd)
player_metadata_send(struct input_metadata *imd, struct output_metadata *omd)
{
union player_arg cmdarg;
cmdarg.pmd = pmd;
cmdarg.metadata_param.input = imd;
cmdarg.metadata_param.output = omd;
commands_exec_sync(cmdbase, metadata_send, NULL, &cmdarg);
}

View File

@ -80,9 +80,6 @@ player_get_status(struct player_status *status);
int
player_now_playing(uint32_t *id);
char *
player_get_icy_artwork_url(uint32_t id);
void
player_speaker_enumerate(spk_enum_cb cb, void *arg);