From 1812848ee1412b00221e5f92a38ecf323dbb92f7 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 11 Jul 2015 10:58:38 +0200 Subject: [PATCH 01/23] Move client protocol specific make_queue functions out of player.c/h --- src/httpd_dacp.c | 209 +++++++++++++++++++++++++++++++++++++++++ src/mpd.c | 31 ++++++ src/player.c | 240 ----------------------------------------------- src/player.h | 8 +- 4 files changed, 244 insertions(+), 244 deletions(-) diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index b0f3a93a..a7307c9e 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -48,6 +48,7 @@ #include "httpd_dacp.h" #include "dmap_common.h" #include "db.h" +#include "daap_query.h" #include "player.h" #include "listener.h" @@ -753,6 +754,214 @@ dacp_reply_ctrlint(struct evhttp_request *req, struct evbuffer *evbuf, char **ur httpd_send_reply(req, HTTP_OK, "OK", evbuf); } +static int +find_first_song_id(const char *query) +{ + struct db_media_file_info dbmfi; + struct query_params qp; + int id; + int ret; + + memset(&qp, 0, sizeof(struct query_params)); + + /* We only want the id of the first song */ + qp.type = Q_ITEMS; + qp.idx_type = I_FIRST; + qp.sort = S_NONE; + qp.offset = 0; + qp.limit = 1; + qp.filter = daap_query_parse_sql(query); + if (!qp.filter) + { + DPRINTF(E_LOG, L_PLAYER, "Improper DAAP query!\n"); + + return -1; + } + + ret = db_query_start(&qp); + if (ret < 0) + { + DPRINTF(E_LOG, L_PLAYER, "Could not start query\n"); + + goto no_query_start; + } + + if (((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id)) + { + ret = safe_atoi32(dbmfi.id, &id); + if (ret < 0) + { + DPRINTF(E_LOG, L_PLAYER, "Invalid song id in query result!\n"); + + goto no_result; + } + + DPRINTF(E_DBG, L_PLAYER, "Found index song (id %d)\n", id); + ret = 1; + } + else + { + DPRINTF(E_LOG, L_PLAYER, "No song matches query (results %d): %s\n", qp.results, qp.filter); + + goto no_result; + } + + no_result: + db_query_end(&qp); + + no_query_start: + if (qp.filter) + free(qp.filter); + + if (ret == 1) + return id; + else + return -1; +} + + +/* Thread: httpd (DACP) */ +int +player_queue_make_daap(struct player_source **head, const char *query, const char *queuefilter, const char *sort, int quirk) +{ + struct media_file_info *mfi; + struct query_params qp; + struct player_source *ps; + int64_t albumid; + int64_t artistid; + int plid; + int id; + int idx; + int ret; + int len; + char *s; + char buf[1024]; + + if (query) + { + id = find_first_song_id(query); + if (id < 0) + return -1; + } + else + id = 0; + + memset(&qp, 0, sizeof(struct query_params)); + + qp.offset = 0; + qp.limit = 0; + qp.sort = S_NONE; + qp.idx_type = I_NONE; + + if (quirk) + { + qp.sort = S_ALBUM; + qp.type = Q_ITEMS; + mfi = db_file_fetch_byid(id); + if (!mfi) + return -1; + snprintf(buf, sizeof(buf), "f.songalbumid = %" PRIi64, mfi->songalbumid); + free_mfi(mfi, 0); + qp.filter = strdup(buf); + } + else if (queuefilter) + { + len = strlen(queuefilter); + if ((len > 6) && (strncmp(queuefilter, "album:", 6) == 0)) + { + qp.type = Q_ITEMS; + ret = safe_atoi64(strchr(queuefilter, ':') + 1, &albumid); + if (ret < 0) + { + DPRINTF(E_LOG, L_PLAYER, "Invalid album id in queuefilter: %s\n", queuefilter); + + return -1; + } + snprintf(buf, sizeof(buf), "f.songalbumid = %" PRIi64, albumid); + qp.filter = strdup(buf); + } + else if ((len > 7) && (strncmp(queuefilter, "artist:", 7) == 0)) + { + qp.type = Q_ITEMS; + ret = safe_atoi64(strchr(queuefilter, ':') + 1, &artistid); + if (ret < 0) + { + DPRINTF(E_LOG, L_PLAYER, "Invalid artist id in queuefilter: %s\n", queuefilter); + + return -1; + } + snprintf(buf, sizeof(buf), "f.songartistid = %" PRIi64, artistid); + qp.filter = strdup(buf); + } + else if ((len > 9) && (strncmp(queuefilter, "playlist:", 9) == 0)) + { + qp.type = Q_PLITEMS; + ret = safe_atoi32(strchr(queuefilter, ':') + 1, &plid); + if (ret < 0) + { + DPRINTF(E_LOG, L_PLAYER, "Invalid playlist id in queuefilter: %s\n", queuefilter); + + return -1; + } + qp.id = plid; + qp.filter = strdup("1 = 1"); + } + else if ((len > 6) && (strncmp(queuefilter, "genre:", 6) == 0)) + { + qp.type = Q_ITEMS; + s = db_escape_string(queuefilter + 6); + if (!s) + return -1; + snprintf(buf, sizeof(buf), "f.genre = '%s'", s); + qp.filter = strdup(buf); + } + else + { + DPRINTF(E_LOG, L_PLAYER, "Unknown queuefilter %s\n", queuefilter); + + // If the queuefilter is unkown, ignore it and use the query parameter instead to build the sql query + id = 0; + qp.type = Q_ITEMS; + qp.filter = daap_query_parse_sql(query); + } + } + else + { + id = 0; + qp.type = Q_ITEMS; + qp.filter = daap_query_parse_sql(query); + } + + if (sort) + { + if (strcmp(sort, "name") == 0) + qp.sort = S_NAME; + else if (strcmp(sort, "album") == 0) + qp.sort = S_ALBUM; + else if (strcmp(sort, "artist") == 0) + qp.sort = S_ARTIST; + } + + ps = player_queue_make(&qp); + + if (qp.filter) + free(qp.filter); + + if (ps) + *head = ps; + else + return -1; + + idx = 0; + while (id && ps && ps->pl_next && (ps->id != id) && (ps->pl_next != *head)) + { + idx++; + ps = ps->pl_next; + } + + return idx; +} + static void dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query) { diff --git a/src/mpd.c b/src/mpd.c index 0e72c922..49949b39 100644 --- a/src/mpd.c +++ b/src/mpd.c @@ -1506,6 +1506,37 @@ mpd_command_stop(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) return 0; } +struct player_source * +player_queue_make_mpd(char *path, int recursive) +{ + struct query_params qp; + struct player_source *ps; + + memset(&qp, 0, sizeof(struct query_params)); + + qp.type = Q_ITEMS; + qp.idx_type = I_NONE; + qp.sort = S_ALBUM; + + if (recursive) + { + qp.filter = sqlite3_mprintf("f.virtual_path LIKE '/%q%%'", path); + if (!qp.filter) + DPRINTF(E_DBG, L_PLAYER, "Out of memory\n"); + } + else + { + qp.filter = sqlite3_mprintf("f.virtual_path LIKE '/%q'", path); + if (!qp.filter) + DPRINTF(E_DBG, L_PLAYER, "Out of memory\n"); + } + + ps = player_queue_make(&qp); + + sqlite3_free(qp.filter); + return ps; +} + /* * Command handler function for 'add' * Adds the all songs under the given path to the end of the playqueue (directories add recursively). diff --git a/src/player.c b/src/player.c index 67ca548e..f878ff8f 100644 --- a/src/player.c +++ b/src/player.c @@ -48,7 +48,6 @@ #include #include "db.h" -#include "daap_query.h" #include "logger.h" #include "mdns.h" #include "conffile.h" @@ -873,214 +872,6 @@ player_queue_make(struct query_params *qp) return q_head; } -static int -find_first_song_id(const char *query) -{ - struct db_media_file_info dbmfi; - struct query_params qp; - int id; - int ret; - - memset(&qp, 0, sizeof(struct query_params)); - - /* We only want the id of the first song */ - qp.type = Q_ITEMS; - qp.idx_type = I_FIRST; - qp.sort = S_NONE; - qp.offset = 0; - qp.limit = 1; - qp.filter = daap_query_parse_sql(query); - if (!qp.filter) - { - DPRINTF(E_LOG, L_PLAYER, "Improper DAAP query!\n"); - - return -1; - } - - ret = db_query_start(&qp); - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Could not start query\n"); - - goto no_query_start; - } - - if (((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id)) - { - ret = safe_atoi32(dbmfi.id, &id); - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Invalid song id in query result!\n"); - - goto no_result; - } - - DPRINTF(E_DBG, L_PLAYER, "Found index song (id %d)\n", id); - ret = 1; - } - else - { - DPRINTF(E_LOG, L_PLAYER, "No song matches query (results %d): %s\n", qp.results, qp.filter); - - goto no_result; - } - - no_result: - db_query_end(&qp); - - no_query_start: - if (qp.filter) - free(qp.filter); - - if (ret == 1) - return id; - else - return -1; -} - - -/* Thread: httpd (DACP) */ -int -player_queue_make_daap(struct player_source **head, const char *query, const char *queuefilter, const char *sort, int quirk) -{ - struct media_file_info *mfi; - struct query_params qp; - struct player_source *ps; - int64_t albumid; - int64_t artistid; - int plid; - int id; - int idx; - int ret; - int len; - char *s; - char buf[1024]; - - if (query) - { - id = find_first_song_id(query); - if (id < 0) - return -1; - } - else - id = 0; - - memset(&qp, 0, sizeof(struct query_params)); - - qp.offset = 0; - qp.limit = 0; - qp.sort = S_NONE; - qp.idx_type = I_NONE; - - if (quirk) - { - qp.sort = S_ALBUM; - qp.type = Q_ITEMS; - mfi = db_file_fetch_byid(id); - if (!mfi) - return -1; - snprintf(buf, sizeof(buf), "f.songalbumid = %" PRIi64, mfi->songalbumid); - free_mfi(mfi, 0); - qp.filter = strdup(buf); - } - else if (queuefilter) - { - len = strlen(queuefilter); - if ((len > 6) && (strncmp(queuefilter, "album:", 6) == 0)) - { - qp.type = Q_ITEMS; - ret = safe_atoi64(strchr(queuefilter, ':') + 1, &albumid); - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Invalid album id in queuefilter: %s\n", queuefilter); - - return -1; - } - snprintf(buf, sizeof(buf), "f.songalbumid = %" PRIi64, albumid); - qp.filter = strdup(buf); - } - else if ((len > 7) && (strncmp(queuefilter, "artist:", 7) == 0)) - { - qp.type = Q_ITEMS; - ret = safe_atoi64(strchr(queuefilter, ':') + 1, &artistid); - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Invalid artist id in queuefilter: %s\n", queuefilter); - - return -1; - } - snprintf(buf, sizeof(buf), "f.songartistid = %" PRIi64, artistid); - qp.filter = strdup(buf); - } - else if ((len > 9) && (strncmp(queuefilter, "playlist:", 9) == 0)) - { - qp.type = Q_PLITEMS; - ret = safe_atoi32(strchr(queuefilter, ':') + 1, &plid); - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Invalid playlist id in queuefilter: %s\n", queuefilter); - - return -1; - } - qp.id = plid; - qp.filter = strdup("1 = 1"); - } - else if ((len > 6) && (strncmp(queuefilter, "genre:", 6) == 0)) - { - qp.type = Q_ITEMS; - s = db_escape_string(queuefilter + 6); - if (!s) - return -1; - snprintf(buf, sizeof(buf), "f.genre = '%s'", s); - qp.filter = strdup(buf); - } - else - { - DPRINTF(E_LOG, L_PLAYER, "Unknown queuefilter %s\n", queuefilter); - - // If the queuefilter is unkown, ignore it and use the query parameter instead to build the sql query - id = 0; - qp.type = Q_ITEMS; - qp.filter = daap_query_parse_sql(query); - } - } - else - { - id = 0; - qp.type = Q_ITEMS; - qp.filter = daap_query_parse_sql(query); - } - - if (sort) - { - if (strcmp(sort, "name") == 0) - qp.sort = S_NAME; - else if (strcmp(sort, "album") == 0) - qp.sort = S_ALBUM; - else if (strcmp(sort, "artist") == 0) - qp.sort = S_ARTIST; - } - - ps = player_queue_make(&qp); - - if (qp.filter) - free(qp.filter); - - if (ps) - *head = ps; - else - return -1; - - idx = 0; - while (id && ps && ps->pl_next && (ps->id != id) && (ps->pl_next != *head)) - { - idx++; - ps = ps->pl_next; - } - - return idx; -} - struct player_source * player_queue_make_pl(int plid, uint32_t *id) { @@ -1142,37 +933,6 @@ player_queue_make_pl(int plid, uint32_t *id) return ps; } -struct player_source * -player_queue_make_mpd(char *path, int recursive) -{ - struct query_params qp; - struct player_source *ps; - - memset(&qp, 0, sizeof(struct query_params)); - - qp.type = Q_ITEMS; - qp.idx_type = I_NONE; - qp.sort = S_ALBUM; - - if (recursive) - { - qp.filter = sqlite3_mprintf("f.virtual_path LIKE '/%q%%'", path); - if (!qp.filter) - DPRINTF(E_DBG, L_PLAYER, "Out of memory\n"); - } - else - { - qp.filter = sqlite3_mprintf("f.virtual_path LIKE '/%q'", path); - if (!qp.filter) - DPRINTF(E_DBG, L_PLAYER, "Out of memory\n"); - } - - ps = player_queue_make(&qp); - - sqlite3_free(qp.filter); - return ps; -} - static void source_free(struct player_source *ps) { diff --git a/src/player.h b/src/player.h index ac9842e4..74a7c9dc 100644 --- a/src/player.h +++ b/src/player.h @@ -185,14 +185,14 @@ player_shuffle_set(int enable); struct player_source * player_queue_make(struct query_params *qp); -int -player_queue_make_daap(struct player_source **head, const char *query, const char *queuefilter, const char *sort, int quirk); +//int +//player_queue_make_daap(struct player_source **head, const char *query, const char *queuefilter, const char *sort, int quirk); struct player_source * player_queue_make_pl(int plid, uint32_t *id); -struct player_source * -player_queue_make_mpd(char *path, int recursive); +//struct player_source * +//player_queue_make_mpd(char *path, int recursive); struct player_queue * player_queue_get(int start_pos, int end_pos, char shuffle); From 8e1834cdeabce9055d92b389dc110901e5adaf76 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 11 Jul 2015 11:02:10 +0200 Subject: [PATCH 02/23] Rename make_queue function and set as static --- src/httpd_dacp.c | 11 +++++------ src/mpd.c | 8 ++++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index a7307c9e..a51b026f 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -820,9 +820,8 @@ find_first_song_id(const char *query) } -/* Thread: httpd (DACP) */ -int -player_queue_make_daap(struct player_source **head, const char *query, const char *queuefilter, const char *sort, int quirk) +static int +make_queue_for_query(struct player_source **head, const char *query, const char *queuefilter, const char *sort, int quirk) { struct media_file_info *mfi; struct query_params qp; @@ -998,7 +997,7 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u { sort = evhttp_find_header(query, "sort"); - ret = player_queue_make_daap(&ps, cuequery, NULL, sort, 0); + ret = make_queue_for_query(&ps, cuequery, NULL, sort, 0); if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Could not build song queue\n"); @@ -1731,7 +1730,7 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf, if (!querymodifier || (strcmp(querymodifier, "containers") != 0)) { quirkyquery = (mode == 1) && strstr(editquery, "dmap.itemid:") && ((!queuefilter) || strstr(queuefilter, "(null)")); - ret = player_queue_make_daap(&ps, editquery, queuefilter, sort, quirkyquery); + ret = make_queue_for_query(&ps, editquery, queuefilter, sort, quirkyquery); } else { @@ -1746,7 +1745,7 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf, } snprintf(modifiedquery, sizeof(modifiedquery), "playlist:%d", plid); - ret = player_queue_make_daap(&ps, NULL, modifiedquery, sort, 0); + ret = make_queue_for_query(&ps, NULL, modifiedquery, sort, 0); } if (ret < 0) diff --git a/src/mpd.c b/src/mpd.c index 49949b39..52dee196 100644 --- a/src/mpd.c +++ b/src/mpd.c @@ -1506,8 +1506,8 @@ mpd_command_stop(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) return 0; } -struct player_source * -player_queue_make_mpd(char *path, int recursive) +static struct player_source * +make_queue_for_path(char *path, int recursive) { struct query_params qp; struct player_source *ps; @@ -1556,7 +1556,7 @@ mpd_command_add(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) return ACK_ERROR_ARG; } - ps = player_queue_make_mpd(argv[1], 1); + ps = make_queue_for_path(argv[1], 1); if (!ps) { @@ -1603,7 +1603,7 @@ mpd_command_addid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) DPRINTF(E_LOG, L_MPD, "Adding at a specified position not supported for 'addid', adding songs at end of queue.\n"); } - ps = player_queue_make_mpd(argv[1], 0); + ps = make_queue_for_path(argv[1], 0); if (!ps) { From f0a6a4859940541064e24c35d09df44945891773 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 1 Aug 2015 10:16:40 +0200 Subject: [PATCH 03/23] [player] Refactor queue_remove functions --- src/httpd_dacp.c | 2 +- src/mpd.c | 4 +- src/player.c | 189 ++++++++++++++++++++++++++++++----------------- src/player.h | 4 +- 4 files changed, 128 insertions(+), 71 deletions(-) diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index a51b026f..bfc661d1 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -1865,7 +1865,7 @@ dacp_reply_playqueueedit_remove(struct evhttp_request *req, struct evbuffer *evb return; } - player_queue_remove(item_index); + player_queue_remove_pos_relative(item_index); } /* 204 No Content is the canonical reply */ diff --git a/src/mpd.c b/src/mpd.c index 52dee196..99d9b949 100644 --- a/src/mpd.c +++ b/src/mpd.c @@ -1695,7 +1695,7 @@ mpd_command_delete(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) return ACK_ERROR_ARG; } - ret = player_queue_remove(pos); + ret = player_queue_remove_pos_relative(pos); if (ret < 0) { ret = asprintf(errmsg, "Failed to remove song at position '%d'", pos); @@ -1734,7 +1734,7 @@ mpd_command_deleteid(struct evbuffer *evbuf, int argc, char **argv, char **errms return ACK_ERROR_ARG; } - ret = player_queue_removeid(songid); + ret = player_queue_remove_queueitemid(songid); if (ret < 0) { ret = asprintf(errmsg, "Failed to remove song with id '%s'", argv[1]); diff --git a/src/player.c b/src/player.c index f878ff8f..6f246bc3 100644 --- a/src/player.c +++ b/src/player.c @@ -3797,65 +3797,8 @@ queue_move(struct player_command *cmd) } static int -queue_remove(struct player_command *cmd) +queue_remove(struct player_source *ps) { - struct player_source *ps; - struct player_source *ps_current; - uint32_t pos; - uint32_t id; - int i; - - if (cmd->arg.item_range.type == RANGEARG_ID) - { - id = cmd->arg.item_range.id; - pos = 0; - if (id < 1) - { - DPRINTF(E_LOG, L_PLAYER, "Can't remove item, invalid id %d\n", id); - return -1; - } - - DPRINTF(E_DBG, L_PLAYER, "Removing item with id %d\n", id); - } - else - { - id = 0; - pos = cmd->arg.item_range.start_pos; - if (pos < 1) - { - DPRINTF(E_LOG, L_PLAYER, "Can't remove item, invalid position %d\n", pos); - return -1; - } - - DPRINTF(E_DBG, L_PLAYER, "Removing item from position %d\n", pos); - } - - ps_current = cur_playing ? cur_playing : cur_streaming; - if (!ps_current) - { - DPRINTF(E_LOG, L_PLAYER, "Current playing/streaming item not found\n"); - return -1; - } - - i = 0; - ps = ps_current; - while (ps) - { - i++; - ps = next_ps(ps, shuffle); - - if (ps == ps_current) - ps = NULL; - else if ((i == pos) || (ps->id == id)) - break; - } - - if (!ps) - { - DPRINTF(E_LOG, L_PLAYER, "Can't remove requested item from queue (id %d, pos %d)\n", id, pos); - return -1; - } - if (ps == source_head) source_head = ps->pl_next; if (ps == shuffle_head) @@ -3876,6 +3819,106 @@ queue_remove(struct player_command *cmd) return 0; } +static int +queue_remove_pos_relative(struct player_command *cmd) +{ + struct player_source *ps; + struct player_source *ps_current; + int pos; + int i; + int ret; + + pos = cmd->arg.intval; + if (pos < 1) + { + DPRINTF(E_LOG, L_PLAYER, "Can't remove item, invalid position %d\n", pos); + return -1; + } + + DPRINTF(E_DBG, L_PLAYER, "Removing item from position %d\n", pos); + + ps_current = cur_playing ? cur_playing : cur_streaming; + if (!ps_current) + { + DPRINTF(E_LOG, L_PLAYER, "Current playing/streaming item not found\n"); + return -1; + } + + i = 0; + ps = ps_current; + while (ps) + { + i++; + ps = next_ps(ps, shuffle); + + if (ps == ps_current) + ps = NULL; + else if (i == pos) + break; + } + + if (!ps) + { + DPRINTF(E_LOG, L_PLAYER, "Can't remove requested item from queue (pos %d)\n", pos); + return -1; + } + + ret = queue_remove(ps); + + return ret; +} + +static int +queue_remove_queueitemid(struct player_command *cmd) +{ + struct player_source *ps; + struct player_source *ps_current; + uint32_t pos; + uint32_t id; + int i; + int ret; + + id = cmd->arg.id; + pos = 0; + if (id < 1) + { + DPRINTF(E_LOG, L_PLAYER, "Can't remove item, invalid id %d\n", id); + return -1; + } + + DPRINTF(E_DBG, L_PLAYER, "Removing item with id %d\n", id); + + ps_current = cur_playing ? cur_playing : cur_streaming; + if (!ps_current) + { + DPRINTF(E_LOG, L_PLAYER, "Current playing/streaming item not found\n"); + return -1; + } + + i = 0; + ps = ps_current; + while (ps) + { + i++; + ps = next_ps(ps, shuffle); + + if (ps == ps_current) + ps = NULL; + else if (ps->id == id) + break; + } + + if (!ps) + { + DPRINTF(E_LOG, L_PLAYER, "Can't remove requested item from queue (id %d, pos %d)\n", id, pos); + return -1; + } + + ret = queue_remove(ps); + + return ret; +} + /* * queue_clear removes all items from the playqueue, playback must be stopped before calling queue_clear */ @@ -4569,18 +4612,27 @@ player_queue_move(int ps_pos_from, int ps_pos_to) return ret; } +/* + * Removes the item at the given position from the queue, where the + * position is relative to the now playing item and dependent on the + * shuffle state of the player. + * If shuffle is on, the position determines the position in the shuffle + * queue. + * + * @param pos Position relative to the now playing item and the shuffle state + * @return 0 on success, -1 on failure + */ int -player_queue_remove(int ps_pos_remove) +player_queue_remove_pos_relative(int pos) { struct player_command cmd; int ret; command_init(&cmd); - cmd.func = queue_remove; + cmd.func = queue_remove_pos_relative; cmd.func_bh = NULL; - cmd.arg.item_range.type = RANGEARG_POS; - cmd.arg.item_range.start_pos = ps_pos_remove; + cmd.arg.intval = pos; ret = sync_command(&cmd); @@ -4589,18 +4641,23 @@ player_queue_remove(int ps_pos_remove) return ret; } +/* + * Removes the item with the given item id from the queue + * + * @param id Id of the queue item to remove + * @return 0 on success, -1 on failure + */ int -player_queue_removeid(uint32_t id) +player_queue_remove_queueitemid(uint32_t id) { struct player_command cmd; int ret; command_init(&cmd); - cmd.func = queue_remove; + cmd.func = queue_remove_queueitemid; cmd.func_bh = NULL; - cmd.arg.item_range.type = RANGEARG_ID; - cmd.arg.item_range.id = id; + cmd.arg.id = id; ret = sync_command(&cmd); diff --git a/src/player.h b/src/player.h index 74a7c9dc..ef39d8bb 100644 --- a/src/player.h +++ b/src/player.h @@ -210,10 +210,10 @@ int player_queue_move(int ps_pos_from, int ps_pos_to); int -player_queue_remove(int ps_pos_remove); +player_queue_remove_pos_relative(int pos); int -player_queue_removeid(uint32_t id); +player_queue_remove_queueitemid(uint32_t id); void player_queue_clear(void); From 1fbedaa54fa510eee1ac116afb1d7a9bfd41f874 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 1 Aug 2015 11:43:51 +0200 Subject: [PATCH 04/23] [player] Refactor queue_get --- src/httpd_dacp.c | 2 +- src/mpd.c | 14 +++-- src/player.c | 142 +++++++++++++++++++++++++++++++---------------- src/player.h | 5 +- 4 files changed, 108 insertions(+), 55 deletions(-) diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index bfc661d1..b51ba4fc 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -1544,7 +1544,7 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf, /* Get queue and make songlist only if playing or paused */ if (status.status != PLAY_STOPPED) { - queue = player_queue_get(-1, abs(span), status.shuffle); + queue = player_queue_get_relative(abs(span)); if (queue) { i = queue->start_pos; diff --git a/src/mpd.c b/src/mpd.c index 99d9b949..cff31642 100644 --- a/src/mpd.c +++ b/src/mpd.c @@ -1777,7 +1777,7 @@ mpd_command_playlistid(struct evbuffer *evbuf, int argc, char **argv, char **err } // Get the whole queue (start_pos = 0, end_pos = -1) - queue = player_queue_get(0, -1, 0); + queue = player_queue_get(0, 0); if (!queue) { @@ -1825,12 +1825,13 @@ mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **e struct player_queue *queue; int start_pos; int end_pos; + int count; int pos_pl; int i; int ret; start_pos = 0; - end_pos = -1; + end_pos = 0; if (argc > 1) { @@ -1844,13 +1845,16 @@ mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **e } } + count = end_pos - start_pos; + if (start_pos < 0) { DPRINTF(E_DBG, L_MPD, "Command 'playlistinfo' called with pos < 0 (arg = '%s'), ignore arguments and return whole queue\n", argv[1]); start_pos = 0; - end_pos = -1; + count = 0; } - queue = player_queue_get(start_pos, end_pos, 0); + + queue = player_queue_get(start_pos, count); if (!queue) { @@ -1897,7 +1901,7 @@ mpd_command_plchanges(struct evbuffer *evbuf, int argc, char **argv, char **errm * forked-daapd does not keep track of changes in the queue based on the playlist version, * therefor plchanges returns all songs in the queue as changed ignoring the given version. */ - queue = player_queue_get(0, -1, 0); + queue = player_queue_get(0, 0); if (!queue) { diff --git a/src/player.c b/src/player.c index 6f246bc3..b8edbb9f 100644 --- a/src/player.c +++ b/src/player.c @@ -133,6 +133,14 @@ struct item_range uint32_t *id_ptr; }; +struct playerqueue_get_param +{ + int pos; + int count; + + struct player_queue *queue; +}; + struct icy_artwork { uint32_t id; @@ -175,13 +183,12 @@ struct player_command int ps_pos[2]; struct item_range item_range; struct icy_artwork icy; + struct playerqueue_get_param queue_get_param; } arg; int ret; int raop_pending; - - struct player_queue *queue; }; /* Keep in sync with enum raop_devtype */ @@ -3554,61 +3561,44 @@ queue_count() return count; } -static int -queue_get(struct player_command *cmd) +static struct player_queue * +queue_get(int pos, int count, char shuffle) { - int start_pos; - int end_pos; struct player_queue *queue; uint32_t *ids; unsigned int qlength; - unsigned int count; struct player_source *ps; + int nitems; int i; - int pos; - char qshuffle; queue = malloc(sizeof(struct player_queue)); qlength = queue_count(); - qshuffle = cmd->arg.item_range.shuffle; - start_pos = cmd->arg.item_range.start_pos; - if (start_pos < 0) - { - // Set start_pos to the position of the current item + 1 - ps = cur_playing ? cur_playing : cur_streaming; - start_pos = ps ? source_position(ps, qshuffle) + 1 : 0; - } - - end_pos = cmd->arg.item_range.end_pos; - if (cmd->arg.item_range.start_pos < 0) - end_pos += start_pos; - if (end_pos <= 0 || end_pos > qlength) - end_pos = qlength; - - if (end_pos > start_pos) - count = end_pos - start_pos; + if (count > 0) + nitems = count; else - count = 0; + nitems = qlength - pos; - ids = malloc(count * sizeof(uint32_t)); + ids = malloc(nitems * sizeof(uint32_t)); pos = 0; - ps = qshuffle ? shuffle_head : source_head; - for (i = 0; i < end_pos; i++) + ps = shuffle ? shuffle_head : source_head; + for (i = 0; i < pos && ps; i++) { - if (i >= start_pos) - { - ids[pos] = ps->id; - pos++; - } - - ps = qshuffle ? ps->shuffle_next : ps->pl_next; + ps = shuffle ? ps->shuffle_next : ps->pl_next; } - queue->start_pos = start_pos; - queue->count = count; + for (i = 0; i < nitems && ps; i++) + { + ids[pos] = ps->id; + pos++; + + ps = shuffle ? ps->shuffle_next : ps->pl_next; + } + + queue->start_pos = pos; + queue->count = nitems; queue->queue = ids; queue->length = qlength; @@ -3618,7 +3608,41 @@ queue_get(struct player_command *cmd) else if (cur_streaming) queue->playingid = cur_streaming->id; - cmd->queue = queue; + return queue; +} + +static int +queue_get_relative(struct player_command *cmd) +{ + int pos; + int count; + struct player_queue *queue; + struct player_source *ps; + + count = cmd->arg.queue_get_param.count; + + // Set pos to the position of the current item + 1 + ps = cur_playing ? cur_playing : cur_streaming; + pos = ps ? source_position(ps, shuffle) + 1 : 0; + + queue = queue_get(pos, count, shuffle); + cmd->arg.queue_get_param.queue = queue; + + return 0; +} + +static int +queue_get_absolute(struct player_command *cmd) +{ + int pos; + int count; + struct player_queue *queue; + + pos = cmd->arg.queue_get_param.pos; + count = cmd->arg.queue_get_param.count; + + queue = queue_get(pos, count, shuffle); + cmd->arg.queue_get_param.queue = queue; return 0; } @@ -4529,20 +4553,18 @@ player_shuffle_set(int enable) * @return List of items (ids) in the queue */ struct player_queue * -player_queue_get(int start_pos, int end_pos, char shuffle) +player_queue_get_relative(int count) { struct player_command cmd; int ret; command_init(&cmd); - cmd.func = queue_get; + cmd.func = queue_get_relative; cmd.func_bh = NULL; - cmd.arg.item_range.type = RANGEARG_POS; - cmd.arg.item_range.start_pos = start_pos; - cmd.arg.item_range.end_pos = end_pos; - cmd.arg.item_range.shuffle = shuffle; - cmd.queue = NULL; + cmd.arg.queue_get_param.pos = -1; + cmd.arg.queue_get_param.count = count; + cmd.arg.queue_get_param.queue = NULL; ret = sync_command(&cmd); @@ -4551,7 +4573,31 @@ player_queue_get(int start_pos, int end_pos, char shuffle) if (ret != 0) return NULL; - return cmd.queue; + return cmd.arg.queue_get_param.queue; +} + +struct player_queue * +player_queue_get(int pos, int count) +{ + struct player_command cmd; + int ret; + + command_init(&cmd); + + cmd.func = queue_get_absolute; + cmd.func_bh = NULL; + cmd.arg.queue_get_param.pos = pos; + cmd.arg.queue_get_param.count = count; + cmd.arg.queue_get_param.queue = NULL; + + ret = sync_command(&cmd); + + command_deinit(&cmd); + + if (ret != 0) + return NULL; + + return cmd.arg.queue_get_param.queue; } int diff --git a/src/player.h b/src/player.h index ef39d8bb..2513c3b8 100644 --- a/src/player.h +++ b/src/player.h @@ -195,7 +195,10 @@ player_queue_make_pl(int plid, uint32_t *id); //player_queue_make_mpd(char *path, int recursive); struct player_queue * -player_queue_get(int start_pos, int end_pos, char shuffle); +player_queue_get_relative(int count); + +struct player_queue * +player_queue_get(int pos, int count); void queue_free(struct player_queue *queue); From 10d7097f9856feecdf4acba58cb52ade0076aa05 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 8 Aug 2015 07:01:45 +0200 Subject: [PATCH 05/23] [player] rename "queue_*"-functions to "playerqueue_*" (avoid naming conflicts with later introduced separate queue file --- src/player.c | 80 ++++++++++++++++++++++++++-------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/player.c b/src/player.c index b8edbb9f..bed0974d 100644 --- a/src/player.c +++ b/src/player.c @@ -589,7 +589,7 @@ static void playback_abort(void); static int -queue_clear(struct player_command *cmd); +playerqueue_clear(struct player_command *cmd); static void player_metadata_send(struct player_metadata *pmd); @@ -2288,7 +2288,7 @@ playback_abort(void) else source_stop(cur_streaming); - queue_clear(NULL); + playerqueue_clear(NULL); cur_playing = NULL; cur_streaming = NULL; @@ -2540,7 +2540,7 @@ playback_start_bh(struct player_command *cmd) } static struct player_source * -queue_get_source_byid(uint32_t id) +playerqueue_get_source_byid(uint32_t id) { struct player_source *ps; @@ -2557,7 +2557,7 @@ queue_get_source_byid(uint32_t id) } static struct player_source * -queue_get_source_bypos(int pos) +playerqueue_get_source_bypos(int pos) { struct player_source *ps; int i; @@ -2617,9 +2617,9 @@ playback_start(struct player_command *cmd) * player_source from the queue. */ if (cmd->arg.item_range.type == RANGEARG_ID) - ps = queue_get_source_byid(cmd->arg.item_range.id); + ps = playerqueue_get_source_byid(cmd->arg.item_range.id); else if (cmd->arg.item_range.type == RANGEARG_POS) - ps = queue_get_source_bypos(cmd->arg.item_range.start_pos); + ps = playerqueue_get_source_bypos(cmd->arg.item_range.start_pos); else ps = NULL; @@ -3542,7 +3542,7 @@ shuffle_set(struct player_command *cmd) } static unsigned int -queue_count() +playerqueue_count() { struct player_source *ps; int count; @@ -3562,7 +3562,7 @@ queue_count() } static struct player_queue * -queue_get(int pos, int count, char shuffle) +playerqueue_get(int pos, int count, char shuffle) { struct player_queue *queue; uint32_t *ids; @@ -3573,7 +3573,7 @@ queue_get(int pos, int count, char shuffle) queue = malloc(sizeof(struct player_queue)); - qlength = queue_count(); + qlength = playerqueue_count(); if (count > 0) nitems = count; @@ -3612,7 +3612,7 @@ queue_get(int pos, int count, char shuffle) } static int -queue_get_relative(struct player_command *cmd) +playerqueue_get_relative(struct player_command *cmd) { int pos; int count; @@ -3625,14 +3625,14 @@ queue_get_relative(struct player_command *cmd) ps = cur_playing ? cur_playing : cur_streaming; pos = ps ? source_position(ps, shuffle) + 1 : 0; - queue = queue_get(pos, count, shuffle); + queue = playerqueue_get(pos, count, shuffle); cmd->arg.queue_get_param.queue = queue; return 0; } static int -queue_get_absolute(struct player_command *cmd) +playerqueue_get_absolute(struct player_command *cmd) { int pos; int count; @@ -3641,21 +3641,21 @@ queue_get_absolute(struct player_command *cmd) pos = cmd->arg.queue_get_param.pos; count = cmd->arg.queue_get_param.count; - queue = queue_get(pos, count, shuffle); + queue = playerqueue_get(pos, count, shuffle); cmd->arg.queue_get_param.queue = queue; return 0; } void -queue_free(struct player_queue *queue) +playerqueue_free(struct player_queue *queue) { free(queue->queue); free(queue); } static int -queue_add(struct player_command *cmd) +playerqueue_add(struct player_command *cmd) { struct player_source *ps; struct player_source *ps_shuffle; @@ -3706,7 +3706,7 @@ queue_add(struct player_command *cmd) } static int -queue_add_next(struct player_command *cmd) +playerqueue_add_next(struct player_command *cmd) { struct player_source *ps; struct player_source *ps_shuffle; @@ -3750,7 +3750,7 @@ queue_add_next(struct player_command *cmd) } static int -queue_move(struct player_command *cmd) +playerqueue_move(struct player_command *cmd) { struct player_source *ps; struct player_source *ps_src; @@ -3821,7 +3821,7 @@ queue_move(struct player_command *cmd) } static int -queue_remove(struct player_source *ps) +playerqueue_remove(struct player_source *ps) { if (ps == source_head) source_head = ps->pl_next; @@ -3844,7 +3844,7 @@ queue_remove(struct player_source *ps) } static int -queue_remove_pos_relative(struct player_command *cmd) +playerqueue_remove_pos_relative(struct player_command *cmd) { struct player_source *ps; struct player_source *ps_current; @@ -3887,13 +3887,13 @@ queue_remove_pos_relative(struct player_command *cmd) return -1; } - ret = queue_remove(ps); + ret = playerqueue_remove(ps); return ret; } static int -queue_remove_queueitemid(struct player_command *cmd) +playerqueue_remove_queueitemid(struct player_command *cmd) { struct player_source *ps; struct player_source *ps_current; @@ -3938,16 +3938,16 @@ queue_remove_queueitemid(struct player_command *cmd) return -1; } - ret = queue_remove(ps); + ret = playerqueue_remove(ps); return ret; } /* - * queue_clear removes all items from the playqueue, playback must be stopped before calling queue_clear + * playerqueue_clear removes all items from the playqueue, playback must be stopped before calling playerqueue_clear */ static int -queue_clear(struct player_command *cmd) +playerqueue_clear(struct player_command *cmd) { struct player_source *ps; @@ -3973,12 +3973,12 @@ queue_clear(struct player_command *cmd) } /* - * Depending on cmd->arg.intval queue_empty removes all items from the history (arg.intval = 1), - * or removes all upcoming songs from the playqueue (arg.intval != 1). After calling queue_empty + * Depending on cmd->arg.intval playerqueue_empty removes all items from the history (arg.intval = 1), + * or removes all upcoming songs from the playqueue (arg.intval != 1). After calling playerqueue_empty * to remove the upcoming songs, the playqueue will only contain the current playing song. */ static int -queue_empty(struct player_command *cmd) +playerqueue_empty(struct player_command *cmd) { int clear_hist; struct player_source *ps; @@ -3997,7 +3997,7 @@ queue_empty(struct player_command *cmd) if (!cur_playing || cur_playing != cur_streaming) { playback_stop(cmd); - queue_clear(cmd); + playerqueue_clear(cmd); return 0; } @@ -4026,7 +4026,7 @@ queue_empty(struct player_command *cmd) } static int -queue_plid(struct player_command *cmd) +playerqueue_plid(struct player_command *cmd) { if (!source_head) return 0; @@ -4560,7 +4560,7 @@ player_queue_get_relative(int count) command_init(&cmd); - cmd.func = queue_get_relative; + cmd.func = playerqueue_get_relative; cmd.func_bh = NULL; cmd.arg.queue_get_param.pos = -1; cmd.arg.queue_get_param.count = count; @@ -4584,7 +4584,7 @@ player_queue_get(int pos, int count) command_init(&cmd); - cmd.func = queue_get_absolute; + cmd.func = playerqueue_get_absolute; cmd.func_bh = NULL; cmd.arg.queue_get_param.pos = pos; cmd.arg.queue_get_param.count = count; @@ -4608,7 +4608,7 @@ player_queue_add(struct player_source *ps) command_init(&cmd); - cmd.func = queue_add; + cmd.func = playerqueue_add; cmd.func_bh = NULL; cmd.arg.ps = ps; @@ -4627,7 +4627,7 @@ player_queue_add_next(struct player_source *ps) command_init(&cmd); - cmd.func = queue_add_next; + cmd.func = playerqueue_add_next; cmd.func_bh = NULL; cmd.arg.ps = ps; @@ -4646,7 +4646,7 @@ player_queue_move(int ps_pos_from, int ps_pos_to) command_init(&cmd); - cmd.func = queue_move; + cmd.func = playerqueue_move; cmd.func_bh = NULL; cmd.arg.ps_pos[0] = ps_pos_from; cmd.arg.ps_pos[1] = ps_pos_to; @@ -4676,7 +4676,7 @@ player_queue_remove_pos_relative(int pos) command_init(&cmd); - cmd.func = queue_remove_pos_relative; + cmd.func = playerqueue_remove_pos_relative; cmd.func_bh = NULL; cmd.arg.intval = pos; @@ -4701,7 +4701,7 @@ player_queue_remove_queueitemid(uint32_t id) command_init(&cmd); - cmd.func = queue_remove_queueitemid; + cmd.func = playerqueue_remove_queueitemid; cmd.func_bh = NULL; cmd.arg.id = id; @@ -4719,7 +4719,7 @@ player_queue_clear(void) command_init(&cmd); - cmd.func = queue_clear; + cmd.func = playerqueue_clear; cmd.func_bh = NULL; cmd.arg.noarg = NULL; @@ -4735,7 +4735,7 @@ player_queue_empty(int clear_hist) command_init(&cmd); - cmd.func = queue_empty; + cmd.func = playerqueue_empty; cmd.func_bh = NULL; cmd.arg.intval = clear_hist; @@ -4751,7 +4751,7 @@ player_queue_plid(uint32_t plid) command_init(&cmd); - cmd.func = queue_plid; + cmd.func = playerqueue_plid; cmd.func_bh = NULL; cmd.arg.id = plid; @@ -5368,7 +5368,7 @@ player_deinit(void) } if (source_head) - queue_clear(NULL); + playerqueue_clear(NULL); free(history); From 1f53d7ab1ad14911178f30f3815b3345e3d7c3c7 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 8 Aug 2015 18:02:49 +0200 Subject: [PATCH 06/23] [queue] Refactor queue handling Decouple the playing/streaming item from the queue. Move all queue related functions to seperate file queue.h/c. Introduce internal item "head" to make iterating over the play-queue and shuffle-queue easier. --- src/Makefile.am | 1 + src/httpd_dacp.c | 72 ++- src/mpd.c | 101 +-- src/player.c | 1526 +++++++++++++++------------------------------- src/player.h | 73 +-- src/queue.c | 1091 +++++++++++++++++++++++++++++++++ src/queue.h | 129 ++++ 7 files changed, 1825 insertions(+), 1168 deletions(-) create mode 100644 src/queue.c create mode 100644 src/queue.h diff --git a/src/Makefile.am b/src/Makefile.am index fac6e0b7..8a908089 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -102,6 +102,7 @@ forked_daapd_SOURCES = main.c \ rsp_query.c rsp_query.h \ daap_query.c daap_query.h \ player.c player.h \ + queue.c queue.h \ worker.c worker.h \ $(ALSA_SRC) $(OSS4_SRC) \ laudio_dummy.c \ diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index b51ba4fc..19a2b4da 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -50,6 +50,7 @@ #include "db.h" #include "daap_query.h" #include "player.h" +#include "queue.h" #include "listener.h" /* httpd event base, from httpd.c */ @@ -757,6 +758,7 @@ dacp_reply_ctrlint(struct evhttp_request *req, struct evbuffer *evbuf, char **ur static int find_first_song_id(const char *query) { + //TODO [refactor][performance] Unnecessary query, it is enough to extract the item id from the query string. Accessing the db to verify the item exists is not needed. struct db_media_file_info dbmfi; struct query_params qp; int id; @@ -821,11 +823,11 @@ find_first_song_id(const char *query) static int -make_queue_for_query(struct player_source **head, const char *query, const char *queuefilter, const char *sort, int quirk) +make_queue_for_query(struct queue_item **head, const char *query, const char *queuefilter, const char *sort, int quirk) { struct media_file_info *mfi; struct query_params qp; - struct player_source *ps; + struct queue_item *items; int64_t albumid; int64_t artistid; int plid; @@ -941,22 +943,18 @@ make_queue_for_query(struct player_source **head, const char *query, const char qp.sort = S_ARTIST; } - ps = player_queue_make(&qp); + items = queue_make(&qp); if (qp.filter) free(qp.filter); - if (ps) - *head = ps; + if (items) + *head = items; else return -1; - idx = 0; - while (id && ps && ps->pl_next && (ps->id != id) && (ps->pl_next != *head)) - { - idx++; - ps = ps->pl_next; - } + // Get the position (0-based) of the first item + idx = queueitem_pos(items, id); return idx; } @@ -965,7 +963,7 @@ static void dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query) { struct player_status status; - struct player_source *ps; + struct queue_item *items; const char *sort; const char *cuequery; const char *param; @@ -997,7 +995,7 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u { sort = evhttp_find_header(query, "sort"); - ret = make_queue_for_query(&ps, cuequery, NULL, sort, 0); + ret = make_queue_for_query(&items, cuequery, NULL, sort, 0); if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Could not build song queue\n"); @@ -1006,7 +1004,7 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u return; } - player_queue_add(ps); + player_queue_add(items); } else { @@ -1063,9 +1061,9 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u /* If playing from history queue, the pos holds the id of the item to play */ if (hist) - ret = player_playback_startid(id, &id); + ret = player_playback_start_byitemid(id, &id); else - ret = player_playback_startpos(pos, &id); + ret = player_playback_start_bypos(pos, &id); if (ret < 0) { @@ -1134,7 +1132,7 @@ static void dacp_reply_playspec(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query) { struct player_status status; - struct player_source *ps; + struct queue_item *items; struct daap_session *s; const char *param; const char *shuffle; @@ -1216,8 +1214,8 @@ dacp_reply_playspec(struct evhttp_request *req, struct evbuffer *evbuf, char **u DPRINTF(E_DBG, L_DACP, "Playspec request for playlist %d, start song id %d%s\n", plid, pos, (shuffle) ? ", shuffle" : ""); - ps = player_queue_make_pl(plid, &pos); - if (!ps) + items = queue_make_pl(plid); //TODO [queue] get queue-item-id or pos for first song to play (dacp) --- , &pos); + if (!items) { DPRINTF(E_LOG, L_DACP, "Could not build song queue from playlist %d\n", plid); @@ -1232,13 +1230,13 @@ dacp_reply_playspec(struct evhttp_request *req, struct evbuffer *evbuf, char **u player_playback_stop(); player_queue_clear(); - player_queue_add(ps); + player_queue_add(items); player_queue_plid(plid); if (shuffle) dacp_propset_shufflestate(shuffle, NULL); - ret = player_playback_startpos(pos, &id); + ret = player_playback_start_bypos(pos, &id); if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Could not start playback\n"); @@ -1472,7 +1470,7 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf, struct evbuffer *playlists; struct player_status status; struct player_history *history; - struct player_queue *queue; + struct queue_info *queue; const char *param; size_t songlist_length; size_t playlist_length; @@ -1544,13 +1542,13 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf, /* Get queue and make songlist only if playing or paused */ if (status.status != PLAY_STOPPED) { - queue = player_queue_get_relative(abs(span)); + queue = player_queue_get_bypos(abs(span)); if (queue) { i = queue->start_pos; for (n = 0; (n < queue->count) && (n < abs(span)); n++) { - ret = playqueuecontents_add_source(songlist, queue->queue[n], (n + i + 1), status.plid); + ret = playqueuecontents_add_source(songlist, queue->queue[n].dbmfi_id, (n + i + 1), status.plid); if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Could not add song to songlist for playqueue-contents\n"); @@ -1559,8 +1557,8 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf, return; } } + queue_info_free(queue); } - queue_free(queue); } } @@ -1644,9 +1642,7 @@ static void dacp_reply_playqueueedit_clear(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query) { const char *param; - int clear_hist; - clear_hist = 0; param = evhttp_find_header(query, "mode"); /* @@ -1655,9 +1651,9 @@ dacp_reply_playqueueedit_clear(struct evhttp_request *req, struct evbuffer *evbu * otherwise the current playlist. */ if (strcmp(param,"0x68697374") == 0) - clear_hist = 1; - - player_queue_empty(clear_hist); + player_queue_clear_history(); + else + player_queue_clear(); dmap_add_container(evbuf, "cacr", 24); /* 8 + len */ dmap_add_int(evbuf, "mstt", 200); /* 12 */ @@ -1678,7 +1674,7 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf, //?command=add&query='dmap.itemid:2'&query-modifier=containers&sort=name&mode=2&session-id=100 // -> mode 2: stop playblack, clear playqueue, add shuffled songs from playlist=itemid to playqueue - struct player_source *ps; + struct queue_item *items; const char *editquery; const char *queuefilter; const char *querymodifier; @@ -1730,7 +1726,7 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf, if (!querymodifier || (strcmp(querymodifier, "containers") != 0)) { quirkyquery = (mode == 1) && strstr(editquery, "dmap.itemid:") && ((!queuefilter) || strstr(queuefilter, "(null)")); - ret = make_queue_for_query(&ps, editquery, queuefilter, sort, quirkyquery); + ret = make_queue_for_query(&items, editquery, queuefilter, sort, quirkyquery); } else { @@ -1745,7 +1741,7 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf, } snprintf(modifiedquery, sizeof(modifiedquery), "playlist:%d", plid); - ret = make_queue_for_query(&ps, NULL, modifiedquery, sort, 0); + ret = make_queue_for_query(&items, NULL, modifiedquery, sort, 0); } if (ret < 0) @@ -1760,11 +1756,11 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf, if (mode == 3) { - player_queue_add_next(ps); + player_queue_add_next(items); } else { - player_queue_add(ps); + player_queue_add(items); } } else @@ -1782,7 +1778,7 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf, } DPRINTF(E_DBG, L_DACP, "Song queue built, playback starting at index %" PRIu32 "\n", idx); - ret = player_playback_startpos(idx, NULL); + ret = player_playback_start_bypos(idx, NULL); if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Could not start playback\n"); @@ -1833,7 +1829,7 @@ dacp_reply_playqueueedit_move(struct evhttp_request *req, struct evbuffer *evbuf return; } - player_queue_move(src, dst); + player_queue_move_bypos(src, dst); } /* 204 No Content is the canonical reply */ @@ -1865,7 +1861,7 @@ dacp_reply_playqueueedit_remove(struct evhttp_request *req, struct evbuffer *evb return; } - player_queue_remove_pos_relative(item_index); + player_queue_remove_bypos(item_index); } /* 204 No Content is the canonical reply */ diff --git a/src/mpd.c b/src/mpd.c index cff31642..39b23191 100644 --- a/src/mpd.c +++ b/src/mpd.c @@ -57,6 +57,7 @@ #include "artwork.h" #include "player.h" +#include "queue.h" #include "filescanner.h" @@ -1210,7 +1211,7 @@ mpd_command_play(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) } if (songpos > 0) - ret = player_playback_startpos(songpos, NULL); + ret = player_playback_start_byindex(songpos, NULL); else ret = player_playback_start(NULL); @@ -1263,7 +1264,7 @@ mpd_command_playid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) } if (id > 0) - ret = player_playback_startid(id, NULL); + ret = player_playback_start_byitemid(id, NULL); else ret = player_playback_start(NULL); @@ -1506,11 +1507,11 @@ mpd_command_stop(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) return 0; } -static struct player_source * +static struct queue_item * make_queue_for_path(char *path, int recursive) { struct query_params qp; - struct player_source *ps; + struct queue_item *items; memset(&qp, 0, sizeof(struct query_params)); @@ -1531,10 +1532,10 @@ make_queue_for_path(char *path, int recursive) DPRINTF(E_DBG, L_PLAYER, "Out of memory\n"); } - ps = player_queue_make(&qp); + items = queue_make(&qp); sqlite3_free(qp.filter); - return ps; + return items; } /* @@ -1545,7 +1546,7 @@ make_queue_for_path(char *path, int recursive) static int mpd_command_add(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) { - struct player_source *ps; + struct queue_item *items; int ret; if (argc < 2) @@ -1556,9 +1557,9 @@ mpd_command_add(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) return ACK_ERROR_ARG; } - ps = make_queue_for_path(argv[1], 1); + items = make_queue_for_path(argv[1], 1); - if (!ps) + if (!items) { ret = asprintf(errmsg, "Failed to add song '%s' to playlist", argv[1]); if (ret < 0) @@ -1566,7 +1567,7 @@ mpd_command_add(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) return ACK_ERROR_UNKNOWN; } - player_queue_add(ps); + player_queue_add(items); ret = player_playback_start(NULL); if (ret < 0) @@ -1586,7 +1587,7 @@ mpd_command_add(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) static int mpd_command_addid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) { - struct player_source *ps; + struct queue_item *items; int ret; if (argc < 2) @@ -1603,9 +1604,9 @@ mpd_command_addid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) DPRINTF(E_LOG, L_MPD, "Adding at a specified position not supported for 'addid', adding songs at end of queue.\n"); } - ps = make_queue_for_path(argv[1], 0); + items = make_queue_for_path(argv[1], 0); - if (!ps) + if (!items) { ret = asprintf(errmsg, "Failed to add song '%s' to playlist", argv[1]); if (ret < 0) @@ -1614,13 +1615,14 @@ mpd_command_addid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) } - player_queue_add(ps); + player_queue_add(items); + //TODO [queue] Get queue-item-id for mpd-command addid evbuffer_add_printf(evbuf, "addid: %s\n" "Id: %d\n", argv[1], - ps->id); + 0); //ps->id); ret = player_playback_start(NULL); if (ret < 0) @@ -1668,7 +1670,7 @@ mpd_command_delete(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) // If argv[1] is ommited clear the whole queue except the current playing one if (argc < 2) { - player_queue_empty(0); + player_queue_clear(); return 0; } @@ -1695,7 +1697,7 @@ mpd_command_delete(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) return ACK_ERROR_ARG; } - ret = player_queue_remove_pos_relative(pos); + ret = player_queue_remove_bypos(pos); if (ret < 0) { ret = asprintf(errmsg, "Failed to remove song at position '%d'", pos); @@ -1734,7 +1736,7 @@ mpd_command_deleteid(struct evbuffer *evbuf, int argc, char **argv, char **errms return ACK_ERROR_ARG; } - ret = player_queue_remove_queueitemid(songid); + ret = player_queue_remove_byitemid(songid); if (ret < 0) { ret = asprintf(errmsg, "Failed to remove song with id '%s'", argv[1]); @@ -1756,7 +1758,7 @@ mpd_command_deleteid(struct evbuffer *evbuf, int argc, char **argv, char **errms static int mpd_command_playlistid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) { - struct player_queue *queue; + struct queue_info *queue; uint32_t songid; int pos_pl; int i; @@ -1777,7 +1779,7 @@ mpd_command_playlistid(struct evbuffer *evbuf, int argc, char **argv, char **err } // Get the whole queue (start_pos = 0, end_pos = -1) - queue = player_queue_get(0, 0); + queue = player_queue_get_byindex(0, 0); if (!queue) { @@ -1788,14 +1790,14 @@ mpd_command_playlistid(struct evbuffer *evbuf, int argc, char **argv, char **err pos_pl = queue->start_pos; for (i = 0; i < queue->count; i++) { - if (songid == 0 || songid == queue->queue[i]) + if (songid == 0 || songid == queue->queue[i].item_id) { - ret = mpd_add_mediainfo_byid(evbuf, queue->queue[i], pos_pl); + ret = mpd_add_mediainfo_byid(evbuf, queue->queue[i].dbmfi_id, pos_pl); if (ret < 0) { - ret = asprintf(errmsg, "Error adding media info for file with id: %d", queue->queue[i]); + ret = asprintf(errmsg, "Error adding media info for file with id: %d", queue->queue[i].dbmfi_id); - queue_free(queue); + queue_info_free(queue); if (ret < 0) DPRINTF(E_LOG, L_MPD, "Out of memory\n"); @@ -1806,7 +1808,7 @@ mpd_command_playlistid(struct evbuffer *evbuf, int argc, char **argv, char **err pos_pl++; } - queue_free(queue); + queue_info_free(queue); return 0; } @@ -1822,7 +1824,7 @@ mpd_command_playlistid(struct evbuffer *evbuf, int argc, char **argv, char **err static int mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) { - struct player_queue *queue; + struct queue_info *queue; int start_pos; int end_pos; int count; @@ -1854,7 +1856,7 @@ mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **e count = 0; } - queue = player_queue_get(start_pos, count); + queue = player_queue_get_byindex(start_pos, count); if (!queue) { @@ -1865,12 +1867,12 @@ mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **e pos_pl = queue->start_pos; for (i = 0; i < queue->count; i++) { - ret = mpd_add_mediainfo_byid(evbuf, queue->queue[i], pos_pl); + ret = mpd_add_mediainfo_byid(evbuf, queue->queue[i].dbmfi_id, pos_pl); if (ret < 0) { - ret = asprintf(errmsg, "Error adding media info for file with id: %d", queue->queue[i]); + ret = asprintf(errmsg, "Error adding media info for file with id: %d", queue->queue[i].dbmfi_id); - queue_free(queue); + queue_info_free(queue); if (ret < 0) DPRINTF(E_LOG, L_MPD, "Out of memory\n"); @@ -1880,7 +1882,7 @@ mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **e pos_pl++; } - queue_free(queue); + queue_info_free(queue); return 0; } @@ -1892,7 +1894,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) { - struct player_queue *queue; + struct queue_info *queue; int pos_pl; int i; int ret; @@ -1901,7 +1903,7 @@ mpd_command_plchanges(struct evbuffer *evbuf, int argc, char **argv, char **errm * forked-daapd does not keep track of changes in the queue based on the playlist version, * therefor plchanges returns all songs in the queue as changed ignoring the given version. */ - queue = player_queue_get(0, 0); + queue = player_queue_get_byindex(0, 0); if (!queue) { @@ -1912,12 +1914,12 @@ mpd_command_plchanges(struct evbuffer *evbuf, int argc, char **argv, char **errm pos_pl = queue->start_pos; for (i = 0; i < queue->count; i++) { - ret = mpd_add_mediainfo_byid(evbuf, queue->queue[i], pos_pl); + ret = mpd_add_mediainfo_byid(evbuf, queue->queue[i].dbmfi_id, pos_pl); if (ret < 0) { - ret = asprintf(errmsg, "Error adding media info for file with id: %d", queue->queue[i]); + ret = asprintf(errmsg, "Error adding media info for file with id: %d", queue->queue[i].dbmfi_id); - queue_free(queue); + queue_info_free(queue); if (ret < 0) DPRINTF(E_LOG, L_MPD, "Out of memory\n"); @@ -1927,7 +1929,7 @@ mpd_command_plchanges(struct evbuffer *evbuf, int argc, char **argv, char **errm pos_pl++; } - queue_free(queue); + queue_info_free(queue); return 0; } @@ -2141,8 +2143,7 @@ mpd_command_load(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) { char path[PATH_MAX]; struct playlist_info *pli; - struct player_source *ps; - uint32_t pos; + struct queue_item *items; int ret; if (argc < 2) @@ -2173,9 +2174,9 @@ mpd_command_load(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) //TODO If a second parameter is given only add the specified range of songs to the playqueue - ps = player_queue_make_pl(pli->id, &pos); + items = queue_make_pl(pli->id); - if (!ps) + if (!items) { free_pli(pli, 0); @@ -2185,7 +2186,7 @@ mpd_command_load(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) return ACK_ERROR_UNKNOWN; } - player_queue_add(ps); + player_queue_add(items); ret = player_playback_start(NULL); if (ret < 0) @@ -2422,7 +2423,7 @@ static int mpd_command_findadd(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) { struct query_params qp; - struct player_source *ps; + struct queue_item *items; int ret; if (argc < 3 || ((argc - 1) % 2) != 0) @@ -2441,9 +2442,9 @@ mpd_command_findadd(struct evbuffer *evbuf, int argc, char **argv, char **errmsg mpd_get_query_params_find(argc - 1, argv + 1, &qp); - ps = player_queue_make(&qp); + items = queue_make(&qp); - if (!ps) + if (!items) { ret = asprintf(errmsg, "Failed to add songs to playlist"); if (ret < 0) @@ -2451,7 +2452,7 @@ mpd_command_findadd(struct evbuffer *evbuf, int argc, char **argv, char **errmsg return ACK_ERROR_UNKNOWN; } - player_queue_add(ps); + player_queue_add(items); ret = player_playback_start(NULL); if (ret < 0) @@ -2896,7 +2897,7 @@ static int mpd_command_searchadd(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) { struct query_params qp; - struct player_source *ps; + struct queue_item *items; int ret; if (argc < 3 || ((argc - 1) % 2) != 0) @@ -2915,9 +2916,9 @@ mpd_command_searchadd(struct evbuffer *evbuf, int argc, char **argv, char **errm mpd_get_query_params_search(argc - 1, argv + 1, &qp); - ps = player_queue_make(&qp); + items = queue_make(&qp); - if (!ps) + if (!items) { ret = asprintf(errmsg, "Failed to add songs to playlist"); if (ret < 0) @@ -2925,7 +2926,7 @@ mpd_command_searchadd(struct evbuffer *evbuf, int argc, char **argv, char **errm return ACK_ERROR_UNKNOWN; } - player_queue_add(ps); + player_queue_add(items); ret = player_playback_start(NULL); if (ret < 0) diff --git a/src/player.c b/src/player.c index bed0974d..431fabb8 100644 --- a/src/player.c +++ b/src/player.c @@ -82,6 +82,25 @@ // Default volume (must be from 0 - 100) #define PLAYER_DEFAULT_VOLUME 50 +struct player_source +{ + uint32_t id; + uint32_t queueitem_id; + uint32_t len_ms; + + enum data_kind data_kind; + enum media_kind media_kind; + int setup_done; + + uint64_t stream_start; + uint64_t output_start; + uint64_t end; + + struct transcode_ctx *ctx; + + struct player_source *play_next; +}; + enum player_sync_source { PLAYER_SYNC_CLOCK, @@ -110,25 +129,10 @@ enum range_type RANGEARG_RANGE }; -/* - * Identifies an item or a range of items - * - * Depending on item_range.type the item(s) are identified by: - * - item id (type = RANGEARG_ID) given in item_range.id - * - item position (type = RANGEARG_POS) given in item_range.start_pos - * - start and end position (type = RANGEARG_RANGE) given in item_range.start_pos to item_range.end_pos - * - * The pointer id_ptr may be set to an item id by the called function. - */ -struct item_range +struct playback_start_param { - enum range_type type; - uint32_t id; - int start_pos; - int end_pos; - - char shuffle; + int pos; uint32_t *id_ptr; }; @@ -138,7 +142,13 @@ struct playerqueue_get_param int pos; int count; - struct player_queue *queue; + struct queue_info *queue; +}; + +struct playerqueue_add_param +{ + struct queue_item *items; + int pos; }; struct icy_artwork @@ -181,9 +191,10 @@ struct player_command uint32_t id; int intval; int ps_pos[2]; - struct item_range item_range; struct icy_artwork icy; + struct playback_start_param playback_start_param; struct playerqueue_get_param queue_get_param; + struct playerqueue_add_param queue_add_param; } arg; int ret; @@ -260,14 +271,15 @@ static int master_volume; struct rng_ctx shuffle_rng; /* Audio source */ -static struct player_source *source_head; -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 queue */ +static struct queue *queue; + /* Play history */ static struct player_history *history; @@ -776,166 +788,18 @@ metadata_check_icy(void) /* Audio sources */ -/* Helper */ static struct player_source * -next_ps(struct player_source *ps, char shuffle) +source_new(struct queue_item_info *item) { - if (shuffle) - return ps->shuffle_next; - else - return ps->pl_next; -} - -/* Thread: httpd (DACP) */ -struct player_source * -player_queue_make(struct query_params *qp) -{ - struct db_media_file_info dbmfi; - struct player_source *q_head; - struct player_source *q_tail; struct player_source *ps; - uint32_t id; - uint32_t song_length; - int ret; - ret = db_query_start(qp); - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Could not start query\n"); + ps = (struct player_source *)calloc(1, sizeof(struct player_source)); - return NULL; - } - - DPRINTF(E_DBG, L_PLAYER, "Player queue query returned %d items\n", qp->results); - - q_head = NULL; - q_tail = NULL; - while (((ret = db_query_fetch_file(qp, &dbmfi)) == 0) && (dbmfi.id)) - { - ret = safe_atou32(dbmfi.id, &id); - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Invalid song id in query result!\n"); - - continue; - } - - ret = safe_atou32(dbmfi.song_length, &song_length); - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Invalid song id in query result!\n"); - - continue; - } - - ps = (struct player_source *)malloc(sizeof(struct player_source)); - if (!ps) - { - DPRINTF(E_LOG, L_PLAYER, "Out of memory for struct player_source\n"); - - ret = -1; - break; - } - - memset(ps, 0, sizeof(struct player_source)); - - ps->id = id; - ps->len_ms = song_length; - - if (!q_head) - q_head = ps; - - if (q_tail) - { - q_tail->pl_next = ps; - ps->pl_prev = q_tail; - - q_tail->shuffle_next = ps; - ps->shuffle_prev = q_tail; - } - - q_tail = ps; - - DPRINTF(E_DBG, L_PLAYER, "Added song id %d (%s)\n", id, dbmfi.title); - } - - db_query_end(qp); - - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Error fetching results\n"); - - return NULL; - } - - if (!q_head) - return NULL; - - q_head->pl_prev = q_tail; - q_tail->pl_next = q_head; - q_head->shuffle_prev = q_tail; - q_tail->shuffle_next = q_head; - - return q_head; -} - -struct player_source * -player_queue_make_pl(int plid, uint32_t *id) -{ - struct query_params qp; - struct player_source *ps; - struct player_source *p; - uint32_t i; - char buf[124]; - - memset(&qp, 0, sizeof(struct query_params)); - - if (plid) - { - qp.id = plid; - qp.type = Q_PLITEMS; - qp.offset = 0; - qp.limit = 0; - qp.sort = S_NONE; - } - else if (*id) - { - qp.id = 0; - qp.type = Q_ITEMS; - qp.offset = 0; - qp.limit = 0; - qp.sort = S_NONE; - snprintf(buf, sizeof(buf), "f.id = %" PRIu32, *id); - qp.filter = strdup(buf); - } - else - return NULL; - - qp.idx_type = I_NONE; - - ps = player_queue_make(&qp); - - if (qp.filter) - free(qp.filter); - - /* Shortcut for shuffled playlist */ - if (*id == 0) - return ps; - - p = ps; - i = 0; - do - { - if (p->id == *id) - { - *id = i; - break; - } - - p = p->pl_next; - i++; - } - while (p != ps); + ps->id = item->dbmfi_id; + ps->queueitem_id = item->item_id; + ps->data_kind = item->data_kind; + ps->media_kind = item->media_kind; + ps->len_ms = item->len_ms; return ps; } @@ -1001,133 +865,6 @@ source_stop(struct player_source *ps) } } -/* - * Shuffles the items between head and tail (excluding head and tail) - */ -static void -source_shuffle(struct player_source *head, struct player_source *tail) -{ - struct player_source *ps; - struct player_source **ps_array; - int nitems; - int i; - - if (!head) - return; - - if (!tail) - return; - - if (!shuffle) - { - ps = head; - do - { - ps->shuffle_next = ps->pl_next; - ps->shuffle_prev = ps->pl_prev; - ps = ps->pl_next; - } - while (ps != head); - } - - // Count items in queue (excluding head and tail) - ps = head->shuffle_next; - if (!cur_streaming) - nitems = 1; - else - nitems = 0; - while (ps != tail) - { - nitems++; - ps = ps->shuffle_next; - } - - // Do not reshuffle queue with one item - if (nitems < 1) - return; - - // Construct array for number of items in queue - ps_array = (struct player_source **)malloc(nitems * sizeof(struct player_source *)); - if (!ps_array) - { - DPRINTF(E_LOG, L_PLAYER, "Could not allocate memory for shuffle array\n"); - return; - } - - // Fill array with items in queue (excluding head and tail) - if (cur_streaming) - ps = head->shuffle_next; - else - ps = head; - i = 0; - do - { - ps_array[i] = ps; - - ps = ps->shuffle_next; - i++; - } - while (ps != tail); - - shuffle_ptr(&shuffle_rng, (void **)ps_array, nitems); - - for (i = 0; i < nitems; i++) - { - ps = ps_array[i]; - - if (i > 0) - ps->shuffle_prev = ps_array[i - 1]; - - if (i < (nitems - 1)) - ps->shuffle_next = ps_array[i + 1]; - } - - // Insert shuffled items between head and tail - if (cur_streaming) - { - ps_array[0]->shuffle_prev = head; - ps_array[nitems - 1]->shuffle_next = tail; - head->shuffle_next = ps_array[0]; - tail->shuffle_prev = ps_array[nitems - 1]; - } - else - { - ps_array[0]->shuffle_prev = ps_array[nitems - 1]; - ps_array[nitems - 1]->shuffle_next = ps_array[0]; - shuffle_head = ps_array[0]; - } - - free(ps_array); - - return; -} - -static void -source_reshuffle(void) -{ - struct player_source *head; - struct player_source *tail; - - if (cur_streaming) - head = cur_streaming; - else if (shuffle) - head = shuffle_head; - else - head = source_head; - - if (repeat == REPEAT_ALL) - tail = head; - else if (shuffle) - tail = shuffle_head; - else - tail = source_head; - - source_shuffle(head, tail); - - if (repeat == REPEAT_ALL) - shuffle_head = head; -} - /* Helper */ static int source_open(struct player_source *ps, int no_md, int seek) @@ -1223,110 +960,80 @@ source_open(struct player_source *ps, int no_md, int seek) static int source_next(int force) { - struct player_source *ps; - struct player_source *head; - struct player_source *limit; + struct player_source *ps_next; + struct queue_item_info *item; enum repeat_mode r_mode; + uint32_t cur_queueitem_id; int ret; - head = (shuffle) ? shuffle_head : source_head; - limit = head; r_mode = repeat; /* Force repeat mode at user request */ if (force && (r_mode == REPEAT_SONG)) r_mode = REPEAT_ALL; - /* Playlist has only one file, treat REPEAT_ALL as REPEAT_SONG */ - if ((r_mode == REPEAT_ALL) && (source_head == source_head->pl_next)) - r_mode = REPEAT_SONG; - /* Playlist has only one file, not a user action, treat as REPEAT_ALL - * and source_check() will stop playback - */ - else if (!force && (r_mode == REPEAT_OFF) && (source_head == source_head->pl_next)) - r_mode = REPEAT_SONG; + cur_queueitem_id = 0; + if (cur_streaming) + cur_queueitem_id = cur_streaming->queueitem_id; - if (!cur_streaming) - ps = head; - else - ps = next_ps(cur_streaming, shuffle); + item = queue_next(queue, cur_queueitem_id, shuffle, r_mode); - switch (r_mode) + if (!item) { - case REPEAT_SONG: - if (!cur_streaming) - break; + DPRINTF(E_DBG, L_PLAYER, "End of playlist reached and repeat is OFF\n"); - if ((cur_streaming->data_kind == DATA_KIND_FILE) && cur_streaming->ctx) - { - ret = transcode_seek(cur_streaming->ctx, 0); + //playback_abort(); + cur_streaming = NULL; + return 0; + } - /* source_open() takes care of sending metadata, but we don't - * call it when repeating a song as we just seek back to 0 - * so we have to handle metadata ourselves here - */ - if (ret >= 0) - metadata_trigger(cur_streaming, 0); - } - else - ret = source_open(cur_streaming, force, 0); + /* + * Check if the next item is the same as the current streaming (REPEAT SINGLE or REPEAT ALL + * with only one item in the playlist) + */ + if (item->item_id == cur_streaming->queueitem_id) + { + if ((cur_streaming->data_kind == DATA_KIND_FILE) && cur_streaming->ctx) + { + ret = transcode_seek(cur_streaming->ctx, 0); - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Failed to restart song for song repeat\n"); + /* source_open() takes care of sending metadata, but we don't + * call it when repeating a song as we just seek back to 0 + * so we have to handle metadata ourselves here + */ + if (ret >= 0) + metadata_trigger(cur_streaming, 0); + } + else + ret = source_open(cur_streaming, force, 0); - return -1; - } + if (ret < 0) + { + DPRINTF(E_LOG, L_PLAYER, "Failed to restart song for song repeat\n"); - return 0; + return -1; + } - case REPEAT_ALL: - if (!shuffle) - { - limit = ps; - break; - } - - /* Reshuffle before repeating playlist */ - if (cur_streaming && (ps == shuffle_head)) - { - source_reshuffle(); - ps = shuffle_head; - } - - limit = shuffle_head; - - break; - - case REPEAT_OFF: - limit = head; - - if (force && (ps == limit)) - { - DPRINTF(E_DBG, L_PLAYER, "End of playlist reached and repeat is OFF\n"); - - playback_abort(); - return 0; - } - break; + return 0; } do { - ret = source_open(ps, force, 0); + ps_next = source_new(item); + //TODO [queue] reshuffle if repeat all and end of playlist reached + ret = source_open(ps_next, 1, 0); if (ret < 0) { - if (shuffle) - ps = ps->shuffle_next; - else - ps = ps->pl_next; - + // Failed to open source, try next + source_free(ps_next); + item = queue_next(queue, item->item_id, shuffle, repeat); //TODO performance optimization, call to queue_next always iterates from the beginning of the queue continue; } + // Successfully opened the next source, break out of the loop break; } - while (ps != limit); + while (item); /* Couldn't open any of the files in our queue */ if (ret < 0) @@ -1337,9 +1044,9 @@ source_next(int force) } if (!force && cur_streaming) - cur_streaming->play_next = ps; + cur_streaming->play_next = ps_next; - cur_streaming = ps; + cur_streaming = ps_next; return 0; } @@ -1347,19 +1054,16 @@ source_next(int force) static int source_prev(void) { - struct player_source *ps; - struct player_source *head; - struct player_source *limit; + struct queue_item_info *item; + struct player_source *ps_prev; int ret; if (!cur_streaming) return -1; - head = (shuffle) ? shuffle_head : source_head; - ps = (shuffle) ? cur_streaming->shuffle_prev : cur_streaming->pl_prev; - limit = ps; + item = queue_prev(queue, cur_streaming->queueitem_id, shuffle, repeat); - if ((repeat == REPEAT_OFF) && (cur_streaming == head)) + if (!item) { DPRINTF(E_DBG, L_PLAYER, "Start of playlist reached and repeat is OFF\n"); @@ -1367,24 +1071,21 @@ source_prev(void) return 0; } - /* We are not reshuffling on prev calls in the shuffle case - should we? */ - do { - ret = source_open(ps, 1, 0); + ps_prev = source_new(item); + ret = source_open(ps_prev, 1, 0); if (ret < 0) - { - if (shuffle) - ps = ps->shuffle_prev; - else - ps = ps->pl_prev; - - continue; - } + { + // Failed to open source, try next + source_free(ps_prev); + item = queue_prev(queue, item->item_id, shuffle, repeat); //TODO performance optimization, call to queue_prev always iterates from the beginning of the queue + continue; + } break; } - while (ps != limit); + while (item); /* Couldn't open any of the files in our queue */ if (ret < 0) @@ -1394,65 +1095,11 @@ source_prev(void) return -1; } - cur_streaming = ps; + cur_streaming = ps_prev; return 0; } -/* - * Returns the position of the given player_source (ps) in the playqueue or shufflequeue. - * First song in the queue has position 0. Depending on the 'shuffle' argument, - * the position is either determined in the playqueue or shufflequeue. - * - * @param ps the source to search in the queue - * @param shuffle 0 search in the playqueue, 1 search in the shufflequeue - * @return position 0-based in the queue (-1 if not found) - */ -static int -source_position(struct player_source *source, char shuffle) -{ - struct player_source *ps; - struct player_source *head; - int ret; - - head = shuffle ? shuffle_head : source_head; - if (!head) - return -1; - - if (source == head) - return 0; - - ret = 0; - for (ps = next_ps(head, shuffle); ps != head; ps = next_ps(ps, shuffle)) - { - ret++; - if (ps == source) - return ret; - } - - DPRINTF(E_LOG, L_PLAYER, "Bug! source_position was given non-existent source\n"); - - return -1; -} - -static uint32_t -source_count() -{ - struct player_source *ps; - uint32_t ret; - - ret = 0; - - if (source_head) - { - ret++; - for (ps = source_head->pl_next; ps != source_head; ps = ps->pl_next) - ret++; - } - - return ret; -} - /* * Updates cur_playing and notifies remotes and raop devices about * changes. @@ -1462,15 +1109,14 @@ source_check(void) { struct timespec ts; struct player_source *ps; - struct player_source *head; uint64_t pos; enum repeat_mode r_mode; int i; int id; int ret; - if (!cur_streaming) - return 0; +// if (!cur_streaming) +// return 0; ret = player_get_current_pos(&pos, &ts, 0); if (ret < 0) @@ -1502,11 +1148,10 @@ source_check(void) 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)) - r_mode = REPEAT_SONG; - if (r_mode == REPEAT_SONG) + /* If repeat mode is REPEAT_SONG or playlist has only one file and repeat mode is REPEAT_ALL, + source_next() does not open a new source but instead seeks to position 0. */ + if (r_mode == REPEAT_SONG || (r_mode == REPEAT_ALL && !cur_playing->play_next)) { ps = cur_playing; @@ -1541,8 +1186,6 @@ source_check(void) return pos; } - head = (shuffle) ? shuffle_head : source_head; - i = 0; while (cur_playing && (cur_playing->end != 0) && (pos > cur_playing->end)) { @@ -1558,8 +1201,7 @@ source_check(void) * - at end of playlist (NULL) * - repeat OFF and at end of playlist (wraparound) */ - if (!cur_playing->play_next - || ((r_mode == REPEAT_OFF) && (cur_playing->play_next == head))) + if (!cur_playing->play_next) { playback_abort(); @@ -1595,6 +1237,15 @@ source_check(void) return pos; } +static struct player_source * +source_now_playing(void) +{ + if (cur_playing) + return cur_playing; + + return cur_streaming; +} + struct player_history * player_history_get(void) { @@ -1636,6 +1287,7 @@ source_read(uint8_t *buf, int len, uint64_t rtptime) int ret; int nbytes; int icy_timer; + char *silence_buf; if (!cur_streaming) return 0; @@ -1660,31 +1312,43 @@ source_read(uint8_t *buf, int len, uint64_t rtptime) if (evbuffer_get_length(audio_buf) == 0) { - switch (cur_streaming->data_kind) + if (cur_streaming) { - case DATA_KIND_HTTP: - ret = transcode(cur_streaming->ctx, audio_buf, len - nbytes, &icy_timer); + switch (cur_streaming->data_kind) + { + case DATA_KIND_HTTP: + ret = transcode(cur_streaming->ctx, audio_buf, len - nbytes, &icy_timer); - if (icy_timer) - metadata_check_icy(); - break; + if (icy_timer) + metadata_check_icy(); + break; - case DATA_KIND_FILE: - ret = transcode(cur_streaming->ctx, audio_buf, len - nbytes, &icy_timer); - break; + case DATA_KIND_FILE: + ret = transcode(cur_streaming->ctx, audio_buf, len - nbytes, &icy_timer); + break; #ifdef HAVE_SPOTIFY_H - case DATA_KIND_SPOTIFY: - ret = spotify_audio_get(audio_buf, len - nbytes); - break; + case DATA_KIND_SPOTIFY: + ret = spotify_audio_get(audio_buf, len - nbytes); + break; #endif - case DATA_KIND_PIPE: - ret = pipe_audio_get(audio_buf, len - nbytes); - break; + case DATA_KIND_PIPE: + ret = pipe_audio_get(audio_buf, len - nbytes); + break; - default: - ret = -1; + default: + ret = -1; + } + } + else + { + // Reached end of playlist (cur_playing is NULL) send silence and source_check will abort playback if the last item was played + DPRINTF(E_DBG, L_PLAYER, "End of playlist reached, stream silence until playback of last item ends\n"); + silence_buf = (char *)calloc((len - nbytes), sizeof(char)); + evbuffer_add(audio_buf, silence_buf, (len - nbytes)); + free(silence_buf); + ret = len - nbytes; } if (ret <= 0) @@ -2275,6 +1939,9 @@ device_restart_cb(struct raop_device *dev, struct raop_session *rs, enum raop_se static void playback_abort(void) { + struct player_source *ps_playing; + struct player_source *ps_temp; + if (laudio_status != LAUDIO_CLOSED) laudio_close(); @@ -2283,16 +1950,22 @@ playback_abort(void) pb_timer_stop(); - if (cur_playing) - source_stop(cur_playing); - else - source_stop(cur_streaming); + ps_playing = source_now_playing(); + source_stop(ps_playing); - playerqueue_clear(NULL); + // Free all player_source items + while (ps_playing) + { + ps_temp = ps_playing; + ps_playing = ps_playing->play_next; + source_free(ps_temp); + } cur_playing = NULL; cur_streaming = NULL; + playerqueue_clear(NULL); + evbuffer_drain(audio_buf, evbuffer_get_length(audio_buf)); status_update(PLAY_STOPPED); @@ -2307,6 +1980,7 @@ get_status(struct player_command *cmd) struct timespec ts; struct player_source *ps; struct player_status *status; + struct queue_item_info *item_next; uint64_t pos; int ret; @@ -2340,7 +2014,7 @@ get_status(struct player_command *cmd) status->pos_ms = (pos * 1000) / 44100; status->len_ms = cur_streaming->len_ms; - status->pos_pl = source_position(cur_streaming, 0); + status->pos_pl = queue_index_byitemid(queue, cur_streaming->queueitem_id, 0); break; @@ -2380,13 +2054,22 @@ get_status(struct player_command *cmd) status->len_ms = ps->len_ms; status->id = ps->id; - status->pos_pl = source_position(ps, 0); + status->pos_pl = queue_index_byitemid(queue, ps->queueitem_id, 0); - ps = next_ps(ps, shuffle); - status->next_id = ps->id; - status->next_pos_pl = source_position(ps, 0); + item_next = queue_next(queue, ps->queueitem_id, shuffle, repeat); + if (item_next) + { + status->next_id = item_next->dbmfi_id; + status->next_pos_pl = queue_index_byitemid(queue, item_next->item_id, 0); + } + else + { + //TODO [queue/mpd] Check how mpd sets the next-id/-pos if the last song is playing + status->next_id = 0; + status->next_pos_pl = 0; + } - status->playlistlength = source_count(); + status->playlistlength = queue_count(queue); break; } @@ -2397,13 +2080,14 @@ static int now_playing(struct player_command *cmd) { uint32_t *id; + struct player_source *ps_playing; id = cmd->arg.id_ptr; - if (cur_playing) - *id = cur_playing->id; - else if (cur_streaming) - *id = cur_streaming->id; + ps_playing = source_now_playing(); + + if (ps_playing) + *id = ps_playing->id; else return -1; @@ -2436,6 +2120,9 @@ artwork_url_get(struct player_command *cmd) static int playback_stop(struct player_command *cmd) { + struct player_source *ps_playing; + struct player_source *ps_temp; + if (laudio_status != LAUDIO_CLOSED) laudio_close(); @@ -2447,15 +2134,19 @@ playback_stop(struct player_command *cmd) pb_timer_stop(); - if (cur_playing) + ps_playing = source_now_playing(); + if (ps_playing) { - history_add(cur_playing->id); - source_stop(cur_playing); + history_add(ps_playing->id); + source_stop(ps_playing); } - else if (cur_streaming) + + // Free all player_source items + while (ps_playing) { - history_add(cur_streaming->id); - source_stop(cur_streaming); + ps_temp = ps_playing; + ps_playing = ps_playing->play_next; + source_free(ps_temp); } cur_playing = NULL; @@ -2539,55 +2230,19 @@ playback_start_bh(struct player_command *cmd) return -1; } -static struct player_source * -playerqueue_get_source_byid(uint32_t id) -{ - struct player_source *ps; - - if (!source_head) - return NULL; - - ps = source_head->pl_next; - while (ps->id != id && ps != source_head) - { - ps = ps->pl_next; - } - - return ps; -} - -static struct player_source * -playerqueue_get_source_bypos(int pos) -{ - struct player_source *ps; - int i; - - if (!source_head) - return NULL; - - ps = source_head; - for (i = pos; i > 0; i--) - ps = ps->pl_next; - - return ps; -} - static int -playback_start(struct player_command *cmd) +playback_start_item(struct player_command *cmd, struct queue_item_info *qii) { + uint32_t *dbmfi_id; struct raop_device *rd; - uint32_t *idx_id; + struct player_source *ps_playing; + struct player_source *ps_temp; struct player_source *ps; int ret; - if (!source_head) - { - DPRINTF(E_LOG, L_PLAYER, "Nothing to play!\n"); + dbmfi_id = cmd->arg.playback_start_param.id_ptr; - return -1; - } - - idx_id = cmd->arg.item_range.id_ptr; + ps_playing = source_now_playing(); if (player_state == PLAY_PLAYING) { @@ -2596,12 +2251,9 @@ playback_start(struct player_command *cmd) * and do not change player state (ignores given arguments for playing a * specified song by pos or id). */ - if (idx_id) + if (dbmfi_id && ps_playing) { - if (cur_playing) - *idx_id = cur_playing->id; - else - *idx_id = cur_streaming->id; + *dbmfi_id = ps_playing->id; } status_update(player_state); @@ -2616,10 +2268,8 @@ playback_start(struct player_command *cmd) * If either an item id or an item position is given, get the corresponding * player_source from the queue. */ - if (cmd->arg.item_range.type == RANGEARG_ID) - ps = playerqueue_get_source_byid(cmd->arg.item_range.id); - else if (cmd->arg.item_range.type == RANGEARG_POS) - ps = playerqueue_get_source_bypos(cmd->arg.item_range.start_pos); + if (qii) + ps = source_new(qii); else ps = NULL; @@ -2636,21 +2286,20 @@ playback_start(struct player_command *cmd) * * Stop playback (if it was paused) and prepare to start playback on ps. */ - if (cur_playing) - source_stop(cur_playing); - else if (cur_streaming) - source_stop(cur_streaming); + source_stop(ps_playing); + + // Free all player_source items + while (ps_playing) + { + ps_temp = ps_playing; + ps_playing = ps_playing->play_next; + source_free(ps_temp); + } cur_playing = NULL; cur_streaming = NULL; - if (shuffle) - { - source_reshuffle(); - cur_streaming = shuffle_head; - } - else - cur_streaming = ps; + cur_streaming = ps; ret = source_open(cur_streaming, 0, 1); if (ret < 0) @@ -2661,8 +2310,8 @@ playback_start(struct player_command *cmd) return -1; } - if (idx_id) - *idx_id = cur_streaming->id; + if (dbmfi_id) + *dbmfi_id = cur_streaming->id; cur_streaming->stream_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES - ((uint64_t)ret * 44100) / 1000; cur_streaming->output_start = cur_streaming->stream_start; @@ -2673,7 +2322,7 @@ playback_start(struct player_command *cmd) * Player was stopped, start playing the queue */ if (shuffle) - source_reshuffle(); + queue_shuffle(queue, 0); ret = source_next(0); if (ret < 0) @@ -2762,6 +2411,61 @@ playback_start(struct player_command *cmd) return playback_start_bh(cmd); } +static int +playback_start(struct player_command *cmd) +{ + return playback_start_item(cmd, NULL); +} + +static int +playback_start_byitemid(struct player_command *cmd) +{ + int item_id; + struct queue_item_info *qii; + + item_id = cmd->arg.playback_start_param.id; + + qii = queue_get_byitemid(queue, item_id); + + return playback_start_item(cmd, qii); +} + +static int +playback_start_byindex(struct player_command *cmd) +{ + int pos; + struct queue_item_info *qii; + + pos = cmd->arg.playback_start_param.pos; + + qii = queue_get_byindex(queue, pos, 0); + + return playback_start_item(cmd, qii); +} + +static int +playback_start_bypos(struct player_command *cmd) +{ + int offset; + struct player_source *ps_playing; + struct queue_item_info *qii; + + offset = cmd->arg.playback_start_param.pos; + + ps_playing = source_now_playing(); + + if (ps_playing) + { + qii = queue_get_bypos(queue, ps_playing->queueitem_id, offset, shuffle); + } + else + { + qii = queue_get_byindex(queue, offset, shuffle); + } + + return playback_start_item(cmd, qii); +} + static int playback_prev_bh(struct player_command *cmd) { @@ -2979,7 +2683,9 @@ playback_pause_bh(struct player_command *cmd) static int playback_pause(struct player_command *cmd) { + struct player_source *ps_playing; struct player_source *ps; + struct player_source *ps_temp; uint64_t pos; pos = source_check(); @@ -2995,13 +2701,10 @@ playback_pause(struct player_command *cmd) if (player_state == PLAY_STOPPED) return -1; - if (cur_playing) - ps = cur_playing; - else - ps = cur_streaming; + ps_playing = source_now_playing(); /* Store pause position */ - ps->end = pos; + ps_playing->end = pos; cmd->raop_pending = raop_flush(device_command_cb, last_rtptime + AIRTUNES_V2_PACKET_SAMPLES); @@ -3010,11 +2713,20 @@ playback_pause(struct player_command *cmd) pb_timer_stop(); - if (ps->play_next) - source_stop(ps->play_next); + ps = ps_playing->play_next; + if (ps) + source_stop(ps); + + // Free all player_source items in the play_next list except the now playing source (becomes the new cur_streaming source) + while (ps) + { + ps_temp = ps; + ps = ps->play_next; + source_free(ps_temp); + } cur_playing = NULL; - cur_streaming = ps; + cur_streaming = ps_playing; cur_streaming->play_next = NULL; evbuffer_drain(audio_buf, evbuffer_get_length(audio_buf)); @@ -3521,11 +3233,16 @@ repeat_set(struct player_command *cmd) static int shuffle_set(struct player_command *cmd) { + uint32_t cur_id; + switch (cmd->arg.intval) { case 1: if (!shuffle) - source_reshuffle(); + { + cur_id = cur_streaming ? cur_streaming->queueitem_id : 0; + queue_shuffle(queue, cur_id); + } /* FALLTHROUGH*/ case 0: shuffle = cmd->arg.intval; @@ -3541,108 +3258,44 @@ shuffle_set(struct player_command *cmd) return 0; } -static unsigned int -playerqueue_count() -{ - struct player_source *ps; - int count; - - if (!source_head) - return 0; - - count = 1; - ps = source_head->pl_next; - while (ps != source_head) - { - count++; - ps = ps->pl_next; - } - - return count; -} - -static struct player_queue * -playerqueue_get(int pos, int count, char shuffle) -{ - struct player_queue *queue; - uint32_t *ids; - unsigned int qlength; - struct player_source *ps; - int nitems; - int i; - - queue = malloc(sizeof(struct player_queue)); - - qlength = playerqueue_count(); - - if (count > 0) - nitems = count; - else - nitems = qlength - pos; - - ids = malloc(nitems * sizeof(uint32_t)); - - pos = 0; - ps = shuffle ? shuffle_head : source_head; - for (i = 0; i < pos && ps; i++) - { - ps = shuffle ? ps->shuffle_next : ps->pl_next; - } - - for (i = 0; i < nitems && ps; i++) - { - ids[pos] = ps->id; - pos++; - - ps = shuffle ? ps->shuffle_next : ps->pl_next; - } - - queue->start_pos = pos; - queue->count = nitems; - queue->queue = ids; - - queue->length = qlength; - queue->playingid = 0; - if (cur_playing) - queue->playingid = cur_playing->id; - else if (cur_streaming) - queue->playingid = cur_streaming->id; - - return queue; -} - static int -playerqueue_get_relative(struct player_command *cmd) +playerqueue_get_bypos(struct player_command *cmd) { - int pos; int count; - struct player_queue *queue; + struct queue_info *qi; struct player_source *ps; + int item_id; count = cmd->arg.queue_get_param.count; // Set pos to the position of the current item + 1 - ps = cur_playing ? cur_playing : cur_streaming; - pos = ps ? source_position(ps, shuffle) + 1 : 0; + ps = source_now_playing(); - queue = playerqueue_get(pos, count, shuffle); - cmd->arg.queue_get_param.queue = queue; + item_id = 0; + if (ps) + { + item_id = ps->queueitem_id; + } + + qi = queue_info_new_bypos(queue, item_id, count, shuffle); + + cmd->arg.queue_get_param.queue = qi; return 0; } static int -playerqueue_get_absolute(struct player_command *cmd) +playerqueue_get_byindex(struct player_command *cmd) { int pos; int count; - struct player_queue *queue; + struct queue_info *qi; pos = cmd->arg.queue_get_param.pos; count = cmd->arg.queue_get_param.count; - queue = playerqueue_get(pos, count, shuffle); - cmd->arg.queue_get_param.queue = queue; + qi = queue_info_new_byindex(queue, pos, count, 0); + cmd->arg.queue_get_param.queue = qi; return 0; } @@ -3657,45 +3310,20 @@ playerqueue_free(struct player_queue *queue) static int playerqueue_add(struct player_command *cmd) { - struct player_source *ps; - struct player_source *ps_shuffle; - struct player_source *source_tail; - struct player_source *ps_tail; + struct queue_item *items; + uint32_t cur_id; - ps = cmd->arg.ps; - ps_shuffle = ps; + items = cmd->arg.queue_add_param.items; - if (source_head) - { - /* Playlist order */ - source_tail = source_head->pl_prev; - ps_tail = ps->pl_prev; - - source_tail->pl_next = ps; - ps_tail->pl_next = source_head; - - source_head->pl_prev = ps_tail; - ps->pl_prev = source_tail; - - /* Shuffle */ - source_tail = shuffle_head->shuffle_prev; - ps_tail = ps_shuffle->shuffle_prev; - - source_tail->shuffle_next = ps_shuffle; - ps_tail->shuffle_next = shuffle_head; - - shuffle_head->shuffle_prev = ps_tail; - ps_shuffle->shuffle_prev = source_tail; - } - else - { - source_head = ps; - shuffle_head = ps_shuffle; - } + queue_add(queue, items); if (shuffle) - source_reshuffle(); + { + cur_id = cur_streaming ? cur_streaming->queueitem_id : 0; + queue_shuffle(queue, cur_id); + } + //TODO [refactor] Unnecessary if, always set plid to 0 after adding items if (cur_plid != 0) cur_plid = 0; cur_plversion++; @@ -3708,38 +3336,19 @@ playerqueue_add(struct player_command *cmd) static int playerqueue_add_next(struct player_command *cmd) { - struct player_source *ps; - struct player_source *ps_shuffle; - struct player_source *ps_playing; + struct queue_item *items; + uint32_t cur_id; - ps = cmd->arg.ps; - ps_shuffle = ps; + items = cmd->arg.queue_add_param.items; - if (source_head && cur_streaming) - { - ps_playing = cur_streaming; + cur_id = cur_streaming ? cur_streaming->queueitem_id : 0; - // Insert ps after ps_playing - ps->pl_prev->pl_next = ps_playing->pl_next; - ps_playing->pl_next->pl_prev = ps->pl_prev; - ps->pl_prev = ps_playing; - ps_playing->pl_next = ps; - - // Insert ps_shuffle after ps_playing - ps_shuffle->shuffle_prev->shuffle_next = ps_playing->shuffle_next; - ps_playing->shuffle_next->shuffle_prev = ps_shuffle->shuffle_prev; - ps_shuffle->shuffle_prev = ps_playing; - ps_playing->shuffle_next = ps_shuffle; - } - else - { - source_head = ps; - shuffle_head = ps_shuffle; - } + queue_add_after(queue, items, cur_id); if (shuffle) - source_reshuffle(); + queue_shuffle(queue, cur_id); + //TODO [refactor] Unnecessary if, always set plid to 0 after adding items if (cur_plid != 0) cur_plid = 0; cur_plversion++; @@ -3750,68 +3359,22 @@ playerqueue_add_next(struct player_command *cmd) } static int -playerqueue_move(struct player_command *cmd) +playerqueue_move_bypos(struct player_command *cmd) { - struct player_source *ps; - struct player_source *ps_src; - struct player_source *ps_dst; - int pos_max; - int i; + struct player_source *ps_playing; DPRINTF(E_DBG, L_PLAYER, "Moving song from position %d to be the next song after %d\n", cmd->arg.ps_pos[0], cmd->arg.ps_pos[1]); - ps = cur_playing ? cur_playing : cur_streaming; - if (!ps) - { - DPRINTF(E_LOG, L_PLAYER, "Current playing/streaming song not found\n"); - return -1; - } + ps_playing = source_now_playing(); - pos_max = MAX(cmd->arg.ps_pos[0], cmd->arg.ps_pos[1]); - ps_src = NULL; - ps_dst = NULL; + if (!ps_playing) + { + DPRINTF(E_LOG, L_PLAYER, "Can't move item, no playing item found\n"); + return -1; + } - for (i = 0; i <= pos_max; i++) - { - if (i == cmd->arg.ps_pos[0]) - ps_src = ps; - if (i == cmd->arg.ps_pos[1]) - ps_dst = ps; - - ps = next_ps(ps, shuffle); - } - - if (!ps_src || !ps_dst || (ps_src == ps_dst)) - { - DPRINTF(E_LOG, L_PLAYER, "Invalid source and/or destination for queue_move\n"); - return -1; - } - - if (shuffle) - { - // Remove ps_src from shuffle queue - ps_src->shuffle_prev->shuffle_next = ps_src->shuffle_next; - ps_src->shuffle_next->shuffle_prev = ps_src->shuffle_prev; - - // Insert after ps_dst - ps_src->shuffle_prev = ps_dst; - ps_src->shuffle_next = ps_dst->shuffle_next; - ps_dst->shuffle_next->shuffle_prev = ps_src; - ps_dst->shuffle_next = ps_src; - } - else - { - // Remove ps_src from queue - ps_src->pl_prev->pl_next = ps_src->pl_next; - ps_src->pl_next->pl_prev = ps_src->pl_prev; - - // Insert after ps_dst - ps_src->pl_prev = ps_dst; - ps_src->pl_next = ps_dst->pl_next; - ps_dst->pl_next->pl_prev = ps_src; - ps_dst->pl_next = ps_src; - } + queue_move_bypos(queue, ps_playing->queueitem_id, cmd->arg.ps_pos[0], cmd->arg.ps_pos[1], shuffle); cur_plversion++; @@ -3821,36 +3384,10 @@ playerqueue_move(struct player_command *cmd) } static int -playerqueue_remove(struct player_source *ps) +playerqueue_remove_bypos(struct player_command *cmd) { - if (ps == source_head) - source_head = ps->pl_next; - if (ps == shuffle_head) - shuffle_head = ps->shuffle_next; - - ps->shuffle_prev->shuffle_next = ps->shuffle_next; - ps->shuffle_next->shuffle_prev = ps->shuffle_prev; - - ps->pl_prev->pl_next = ps->pl_next; - ps->pl_next->pl_prev = ps->pl_prev; - - source_free(ps); - - cur_plversion++; - - listener_notify(LISTENER_PLAYLIST); - - return 0; -} - -static int -playerqueue_remove_pos_relative(struct player_command *cmd) -{ - struct player_source *ps; - struct player_source *ps_current; int pos; - int i; - int ret; + struct player_source *ps_playing; pos = cmd->arg.intval; if (pos < 1) @@ -3859,51 +3396,26 @@ playerqueue_remove_pos_relative(struct player_command *cmd) return -1; } + ps_playing = source_now_playing(); + + if (!ps_playing) + { + DPRINTF(E_LOG, L_PLAYER, "Can't remove item at pos %d, no playing item found\n", pos); + return -1; + } + DPRINTF(E_DBG, L_PLAYER, "Removing item from position %d\n", pos); + queue_remove_bypos(queue, ps_playing->queueitem_id, pos, shuffle); - ps_current = cur_playing ? cur_playing : cur_streaming; - if (!ps_current) - { - DPRINTF(E_LOG, L_PLAYER, "Current playing/streaming item not found\n"); - return -1; - } - - i = 0; - ps = ps_current; - while (ps) - { - i++; - ps = next_ps(ps, shuffle); - - if (ps == ps_current) - ps = NULL; - else if (i == pos) - break; - } - - if (!ps) - { - DPRINTF(E_LOG, L_PLAYER, "Can't remove requested item from queue (pos %d)\n", pos); - return -1; - } - - ret = playerqueue_remove(ps); - - return ret; + return 0; } static int -playerqueue_remove_queueitemid(struct player_command *cmd) +playerqueue_remove_byitemid(struct player_command *cmd) { - struct player_source *ps; - struct player_source *ps_current; - uint32_t pos; uint32_t id; - int i; - int ret; id = cmd->arg.id; - pos = 0; if (id < 1) { DPRINTF(E_LOG, L_PLAYER, "Can't remove item, invalid id %d\n", id); @@ -3911,58 +3423,18 @@ playerqueue_remove_queueitemid(struct player_command *cmd) } DPRINTF(E_DBG, L_PLAYER, "Removing item with id %d\n", id); + queue_remove_byitemid(queue, id); - ps_current = cur_playing ? cur_playing : cur_streaming; - if (!ps_current) - { - DPRINTF(E_LOG, L_PLAYER, "Current playing/streaming item not found\n"); - return -1; - } - - i = 0; - ps = ps_current; - while (ps) - { - i++; - ps = next_ps(ps, shuffle); - - if (ps == ps_current) - ps = NULL; - else if (ps->id == id) - break; - } - - if (!ps) - { - DPRINTF(E_LOG, L_PLAYER, "Can't remove requested item from queue (id %d, pos %d)\n", id, pos); - return -1; - } - - ret = playerqueue_remove(ps); - - return ret; + return 0; } /* - * playerqueue_clear removes all items from the playqueue, playback must be stopped before calling playerqueue_clear + * Removes all media items from the queue */ static int playerqueue_clear(struct player_command *cmd) { - struct player_source *ps; - - if (!source_head) - return 0; - - shuffle_head = NULL; - source_head->pl_prev->pl_next = NULL; - - for (ps = source_head; ps; ps = source_head) - { - source_head = ps->pl_next; - - source_free(ps); - } + queue_clear(queue); cur_plid = 0; cur_plversion++; @@ -3973,50 +3445,12 @@ playerqueue_clear(struct player_command *cmd) } /* - * Depending on cmd->arg.intval playerqueue_empty removes all items from the history (arg.intval = 1), - * or removes all upcoming songs from the playqueue (arg.intval != 1). After calling playerqueue_empty - * to remove the upcoming songs, the playqueue will only contain the current playing song. + * Removes all items from the history */ static int -playerqueue_empty(struct player_command *cmd) +playerqueue_clear_history(struct player_command *cmd) { - int clear_hist; - struct player_source *ps; - - clear_hist = cmd->arg.intval; - if (clear_hist) - { - memset(history, 0, sizeof(struct player_history)); - } - else - { - if (!source_head || !cur_streaming) - return 0; - - // Stop playback if playing and streaming song are not the same - if (!cur_playing || cur_playing != cur_streaming) - { - playback_stop(cmd); - playerqueue_clear(cmd); - return 0; - } - - // Set head to the current playing song - shuffle_head = cur_playing; - source_head = cur_playing; - - // Free all items in the queue except the current playing song - for (ps = source_head->pl_next; ps != source_head; ps = ps->pl_next) - { - source_free(ps); - } - - // Make the queue circular again - source_head->pl_next = source_head; - source_head->pl_prev = source_head; - source_head->shuffle_next = source_head; - source_head->shuffle_prev = source_head; - } + memset(history, 0, sizeof(struct player_history)); cur_plversion++; @@ -4028,9 +3462,6 @@ playerqueue_empty(struct player_command *cmd) static int playerqueue_plid(struct player_command *cmd) { - if (!source_head) - return 0; - cur_plid = cmd->arg.id; return 0; @@ -4171,6 +3602,12 @@ player_get_status(struct player_status *status) return ret; } +/* + * Stores the now playing media item dbmfi-id in the given id pointer. + * + * @param id Pointer will hold the playing item (dbmfi) id if the function returns 0 + * @return 0 on success, -1 on failure (e. g. no playing item found) + */ int player_now_playing(uint32_t *id) { @@ -4218,14 +3655,14 @@ player_get_icy_artwork_url(uint32_t id) /* * Starts/resumes playback * - * Depending on the player state, this will either resumes playing the current item (player is paused) - * or begins playing the queue from the beginning. + * Depending on the player state, this will either resume playing the current item (player is paused) + * or begin playing the queue from the beginning. * * If shuffle is set, the queue is reshuffled prior to starting playback. * - * If a pointer is given as argument "itemid", its value will be set to the playing item id. + * If a pointer is given as argument "itemid", its value will be set to the playing item dbmfi-id. * - * @param *itemid if not NULL, will be set to the playing item id + * @param *itemid if not NULL, will be set to the playing item dbmfi-id * @return 0 if successful, -1 if an error occurred */ int @@ -4238,8 +3675,7 @@ player_playback_start(uint32_t *itemid) cmd.func = playback_start; cmd.func_bh = playback_start_bh; - cmd.arg.item_range.type = RANGEARG_NONE; - cmd.arg.item_range.id_ptr = itemid; + cmd.arg.playback_start_param.id_ptr = itemid; ret = sync_command(&cmd); @@ -4249,28 +3685,28 @@ player_playback_start(uint32_t *itemid) } /* - * Starts playback at item number "pos" of the current queue + * Starts playback with the media item at the given index of the play-queue. * * If shuffle is set, the queue is reshuffled prior to starting playback. * * If a pointer is given as argument "itemid", its value will be set to the playing item id. * + * @param index the index of the item in the play-queue * @param *itemid if not NULL, will be set to the playing item id * @return 0 if successful, -1 if an error occurred */ int -player_playback_startpos(int pos, uint32_t *itemid) +player_playback_start_byindex(int index, uint32_t *itemid) { struct player_command cmd; int ret; command_init(&cmd); - cmd.func = playback_start; + cmd.func = playback_start_byindex; cmd.func_bh = playback_start_bh; - cmd.arg.item_range.type = RANGEARG_POS; - cmd.arg.item_range.start_pos = pos; - cmd.arg.item_range.id_ptr = itemid; + cmd.arg.playback_start_param.pos = index; + cmd.arg.playback_start_param.id_ptr = itemid; ret = sync_command(&cmd); command_deinit(&cmd); @@ -4279,28 +3715,60 @@ player_playback_startpos(int pos, uint32_t *itemid) } /* - * Starts playback at item with "id" of the current queue + * Starts playback with the media item at the given position in the UpNext-queue. + * The UpNext-queue consists of all items of the play-queue (shuffle off) or shuffle-queue + * (shuffle on) after the current playing item (starting with position 0). * * If shuffle is set, the queue is reshuffled prior to starting playback. * - * If a pointer is given as argument "itemid", its value will be set to the playing item id. + * If a pointer is given as argument "itemid", its value will be set to the playing item dbmfi-id. * - * @param *itemid if not NULL, will be set to the playing item id + * @param pos the position in the UpNext-queue (zero-based) + * @param *itemid if not NULL, will be set to the playing item dbmfi-id * @return 0 if successful, -1 if an error occurred */ int -player_playback_startid(uint32_t id, uint32_t *itemid) +player_playback_start_bypos(int pos, uint32_t *itemid) { struct player_command cmd; int ret; command_init(&cmd); - cmd.func = playback_start; + cmd.func = playback_start_bypos; cmd.func_bh = playback_start_bh; - cmd.arg.item_range.type = RANGEARG_ID; - cmd.arg.item_range.id = id; - cmd.arg.item_range.id_ptr = itemid; + cmd.arg.playback_start_param.pos = pos; + cmd.arg.playback_start_param.id_ptr = itemid; + ret = sync_command(&cmd); + + command_deinit(&cmd); + + return ret; +} + +/* + * Starts playback with the media item with the given (queueitem) item-id in queue + * + * If shuffle is set, the queue is reshuffled prior to starting playback. + * + * If a pointer is given as argument "itemid", its value will be set to the playing item dbmfi-id. + * + * @param id The queue-item-id + * @param *itemid if not NULL, will be set to the playing item dbmfi-id + * @return 0 if successful, -1 if an error occurred + */ +int +player_playback_start_byitemid(uint32_t id, uint32_t *itemid) +{ + struct player_command cmd; + int ret; + + command_init(&cmd); + + cmd.func = playback_start_byitemid; + cmd.func_bh = playback_start_bh; + cmd.arg.playback_start_param.id = id; + cmd.arg.playback_start_param.id_ptr = itemid; ret = sync_command(&cmd); command_deinit(&cmd); @@ -4539,28 +4007,23 @@ player_shuffle_set(int enable) } /* - * Retrieves a list of item ids in the queue from postion 'start_pos' to 'end_pos' + * Returns the queue info for max "count" media items in the UpNext-queue * - * If start_pos is -1, the list starts with the item next from the current playing item. - * If end_pos is -1, this list contains all songs starting from 'start_pos' + * The UpNext-queue consists of all items of the play-queue (shuffle off) or shuffle-queue + * (shuffle on) after the current playing item (starting with position 0). * - * The 'shuffle' argument determines if the items are taken from the playqueue (shuffle = 0) - * or the shufflequeue (shuffle = 1). - * - * @param start_pos Start the listing from 'start_pos' - * @param end_pos End the listing at 'end_pos' - * @param shuffle If set to 1 use the shuffle queue, otherwise the playqueue - * @return List of items (ids) in the queue + * @param count max number of media items to return + * @return queue info */ -struct player_queue * -player_queue_get_relative(int count) +struct queue_info * +player_queue_get_bypos(int count) { struct player_command cmd; int ret; command_init(&cmd); - cmd.func = playerqueue_get_relative; + cmd.func = playerqueue_get_bypos; cmd.func_bh = NULL; cmd.arg.queue_get_param.pos = -1; cmd.arg.queue_get_param.count = count; @@ -4576,17 +4039,25 @@ player_queue_get_relative(int count) return cmd.arg.queue_get_param.queue; } -struct player_queue * -player_queue_get(int pos, int count) +/* + * Returns the queue info for max "count" media items starting with the item at the given + * index in the play-queue + * + * @param index Index of the play-queue for the first item + * @param count max number of media items to return + * @return queue info + */ +struct queue_info * +player_queue_get_byindex(int index, int count) { struct player_command cmd; int ret; command_init(&cmd); - cmd.func = playerqueue_get_absolute; + cmd.func = playerqueue_get_byindex; cmd.func_bh = NULL; - cmd.arg.queue_get_param.pos = pos; + cmd.arg.queue_get_param.pos = index; cmd.arg.queue_get_param.count = count; cmd.arg.queue_get_param.queue = NULL; @@ -4600,8 +4071,11 @@ player_queue_get(int pos, int count) return cmd.arg.queue_get_param.queue; } +/* + * Appends the given media items to the queue + */ int -player_queue_add(struct player_source *ps) +player_queue_add(struct queue_item *items) { struct player_command cmd; int ret; @@ -4610,46 +4084,7 @@ player_queue_add(struct player_source *ps) cmd.func = playerqueue_add; cmd.func_bh = NULL; - cmd.arg.ps = ps; - - ret = sync_command(&cmd); - - command_deinit(&cmd); - - return ret; -} - -int -player_queue_add_next(struct player_source *ps) -{ - struct player_command cmd; - int ret; - - command_init(&cmd); - - cmd.func = playerqueue_add_next; - cmd.func_bh = NULL; - cmd.arg.ps = ps; - - ret = sync_command(&cmd); - - command_deinit(&cmd); - - return ret; -} - -int -player_queue_move(int ps_pos_from, int ps_pos_to) -{ - struct player_command cmd; - int ret; - - command_init(&cmd); - - cmd.func = playerqueue_move; - cmd.func_bh = NULL; - cmd.arg.ps_pos[0] = ps_pos_from; - cmd.arg.ps_pos[1] = ps_pos_to; + cmd.arg.queue_add_param.items = items; ret = sync_command(&cmd); @@ -4659,24 +4094,71 @@ player_queue_move(int ps_pos_from, int ps_pos_to) } /* - * Removes the item at the given position from the queue, where the - * position is relative to the now playing item and dependent on the - * shuffle state of the player. - * If shuffle is on, the position determines the position in the shuffle - * queue. - * - * @param pos Position relative to the now playing item and the shuffle state - * @return 0 on success, -1 on failure + * Adds the given media items directly after the current playing/streaming media item */ int -player_queue_remove_pos_relative(int pos) +player_queue_add_next(struct queue_item *items) { struct player_command cmd; int ret; command_init(&cmd); - cmd.func = playerqueue_remove_pos_relative; + cmd.func = playerqueue_add_next; + cmd.func_bh = NULL; + cmd.arg.queue_add_param.items = items; + + ret = sync_command(&cmd); + + command_deinit(&cmd); + + return ret; +} + +/* + * Moves the media item at 'pos_from' to 'pos_to' in the UpNext-queue. + * + * The UpNext-queue consists of all items of the play-queue (shuffle off) or shuffle-queue + * (shuffle on) after the current playing item (starting with position 0). + */ +int +player_queue_move_bypos(int pos_from, int pos_to) +{ + struct player_command cmd; + int ret; + + command_init(&cmd); + + cmd.func = playerqueue_move_bypos; + cmd.func_bh = NULL; + cmd.arg.ps_pos[0] = pos_from; + cmd.arg.ps_pos[1] = pos_to; + + ret = sync_command(&cmd); + + command_deinit(&cmd); + + return ret; +} + +/* + * Removes the media item at the given position from the UpNext-queue + * + * The UpNext-queue consists of all items of the play-queue (shuffle off) or shuffle-queue + * (shuffle on) after the current playing item (starting with position 0). + * + * @param pos Position in the UpNext-queue (0-based) + * @return 0 on success, -1 on failure + */ +int +player_queue_remove_bypos(int pos) +{ + struct player_command cmd; + int ret; + + command_init(&cmd); + + cmd.func = playerqueue_remove_bypos; cmd.func_bh = NULL; cmd.arg.intval = pos; @@ -4688,20 +4170,20 @@ player_queue_remove_pos_relative(int pos) } /* - * Removes the item with the given item id from the queue + * Removes the item with the given (queueitem) item id from the queue * * @param id Id of the queue item to remove * @return 0 on success, -1 on failure */ int -player_queue_remove_queueitemid(uint32_t id) +player_queue_remove_byitemid(uint32_t id) { struct player_command cmd; int ret; command_init(&cmd); - cmd.func = playerqueue_remove_queueitemid; + cmd.func = playerqueue_remove_byitemid; cmd.func_bh = NULL; cmd.arg.id = id; @@ -4729,15 +4211,14 @@ player_queue_clear(void) } void -player_queue_empty(int clear_hist) +player_queue_clear_history() { struct player_command cmd; command_init(&cmd); - cmd.func = playerqueue_empty; + cmd.func = playerqueue_clear_history; cmd.func_bh = NULL; - cmd.arg.intval = clear_hist; sync_command(&cmd); @@ -5111,8 +4592,6 @@ player_init(void) cur_cmd = NULL; - source_head = NULL; - shuffle_head = NULL; cur_playing = NULL; cur_streaming = NULL; cur_plid = 0; @@ -5122,6 +4601,7 @@ player_init(void) repeat = REPEAT_OFF; shuffle = 0; + queue = queue_new(); history = (struct player_history *)calloc(1, sizeof(struct player_history)); /* @@ -5367,9 +4847,7 @@ player_deinit(void) return; } - if (source_head) - playerqueue_clear(NULL); - + queue_free(queue); free(history); pb_timer_stop(); diff --git a/src/player.h b/src/player.h index 2513c3b8..da4776ef 100644 --- a/src/player.h +++ b/src/player.h @@ -5,6 +5,7 @@ #include #include "db.h" +#include "queue.h" /* AirTunes v2 packet interval in ns */ /* (352 samples/packet * 1e9 ns/s) / 44100 samples/s = 7981859 ns/packet */ @@ -27,12 +28,6 @@ enum play_status { PLAY_PLAYING = 4, }; -enum repeat_mode { - REPEAT_OFF = 0, - REPEAT_SONG = 1, - REPEAT_ALL = 2, -}; - struct spk_flags { unsigned selected:1; unsigned has_password:1; @@ -72,30 +67,6 @@ struct player_status { typedef void (*spk_enum_cb)(uint64_t id, const char *name, int relvol, struct spk_flags flags, void *arg); -struct player_source -{ - uint32_t id; - uint32_t len_ms; - - enum data_kind data_kind; - enum media_kind media_kind; - int setup_done; - - uint64_t stream_start; - uint64_t output_start; - uint64_t end; - - struct transcode_ctx *ctx; - - struct player_source *pl_next; - struct player_source *pl_prev; - - struct player_source *shuffle_next; - struct player_source *shuffle_prev; - - struct player_source *play_next; -}; - struct player_queue { // The item id of the current playing item @@ -146,10 +117,13 @@ int player_playback_start(uint32_t *idx_id); int -player_playback_startpos(int pos, uint32_t *itemid); +player_playback_start_byindex(int pos, uint32_t *itemid); int -player_playback_startid(uint32_t id, uint32_t *itemid); +player_playback_start_bypos(int pos, uint32_t *itemid); + +int +player_playback_start_byitemid(uint32_t id, uint32_t *itemid); int player_playback_stop(void); @@ -182,51 +156,38 @@ player_repeat_set(enum repeat_mode mode); int player_shuffle_set(int enable); -struct player_source * -player_queue_make(struct query_params *qp); -//int -//player_queue_make_daap(struct player_source **head, const char *query, const char *queuefilter, const char *sort, int quirk); +struct queue_info * +player_queue_get_bypos(int count); -struct player_source * -player_queue_make_pl(int plid, uint32_t *id); - -//struct player_source * -//player_queue_make_mpd(char *path, int recursive); - -struct player_queue * -player_queue_get_relative(int count); - -struct player_queue * -player_queue_get(int pos, int count); - -void -queue_free(struct player_queue *queue); +struct queue_info * +player_queue_get_byindex(int pos, int count); int -player_queue_add(struct player_source *ps); +player_queue_add(struct queue_item *items); int -player_queue_add_next(struct player_source *ps); +player_queue_add_next(struct queue_item *items); int -player_queue_move(int ps_pos_from, int ps_pos_to); +player_queue_move_bypos(int ps_pos_from, int ps_pos_to); int -player_queue_remove_pos_relative(int pos); +player_queue_remove_bypos(int pos); int -player_queue_remove_queueitemid(uint32_t id); +player_queue_remove_byitemid(uint32_t id); void player_queue_clear(void); void -player_queue_empty(int clear_hist); +player_queue_clear_history(void); void player_queue_plid(uint32_t plid); + struct player_history * player_history_get(void); diff --git a/src/queue.c b/src/queue.c new file mode 100644 index 00000000..64c14b53 --- /dev/null +++ b/src/queue.c @@ -0,0 +1,1091 @@ +/* + * Copyright (C) 2015 Christian Meffert + * + * 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 +#include +#include + +#include "db.h" +#include "logger.h" +#include "misc.h" +#include "queue.h" +#include "rng.h" + + +/* + * Internal representation of an item in a queue. It links to the previous and the next item + * in the queue for shuffle on/off. Only the queue_item_info can be exposed. + */ +struct queue_item +{ + /* Identifies the item in the db and the queue */ + struct queue_item_info qii; + + /* Link to the previous/next item in the queue */ + struct queue_item *next; + struct queue_item *prev; + + /* Link to the previous/next item in the shuffle queue */ + struct queue_item *shuffle_next; + struct queue_item *shuffle_prev; +}; + +/* + * The queue struct references two (double) linked lists of queue_item. One for the play-queue + * and one for the shuffle-queue. + * + * Both linked lists start with the "head" item. The head item is not a media item, instead it is + * an internal item created during initialization of a new queue struct (see queue_new() function). + * The head item is always the first item in the queue and will only be removed when queue is + * destructed. + * + * The linked lists are circular, therefor the last item in a list has the first item (the head item) + * as "next" and the first item in the queue (the head item) has the last item as "prev" linked. + * + * An empty queue (with no media items) will only consist of the head item pointing to itself. + */ +struct queue +{ + /* The queue item id of the last inserted item */ + unsigned int last_inserted_item_id; + + /* The version number of the queue */ + unsigned int version; + + /* Shuffle RNG state */ + struct rng_ctx shuffle_rng; + + /* + * The head item in the queue is not an actual media item, instead it is the + * starting point for the play-queue and the shuffle-queue. It always has the + * item-id 0. The queue is circular, the last item of the queue has the head + * item as "next" and the head item has the last item as "prev". + */ + struct queue_item *head; +}; + + +/* + * Creates and initializes a new queue + */ +struct queue * +queue_new() +{ + struct queue *queue; + + queue = (struct queue *)calloc(1, sizeof(struct queue)); + queue->head = (struct queue_item *)calloc(1, sizeof(struct queue_item)); + + // Create the head item and make the queue circular (head points to itself) + queue->head->next = queue->head; + queue->head->prev = queue->head; + queue->head->shuffle_next = queue->head; + queue->head->shuffle_prev = queue->head; + + rng_init(&queue->shuffle_rng); + + return queue; +} + +/* + * Frees the given item and all linked items + */ +static void +queue_items_free(struct queue_item *item) +{ + struct queue_item *temp; + struct queue_item *next; + + item->prev->next = NULL; + + next = item; + while (next) + { + temp = next->next; + free(next); + next = temp; + } +} + +/* + * Frees the given queue and all the items in it + */ +void +queue_free(struct queue *queue) +{ + queue_items_free(queue->head); + free(queue); +} + +/* + * Returns the number of media items in the queue + * + * @param queue The queue + * @return The number of items in the queue + */ +unsigned int +queue_count(struct queue *queue) +{ + struct queue_item *item; + int count; + + count = 0; + + for (item = queue->head->next; item != queue->head; item = item->next) + { + count++; + } + + return count; +} + +/* + * Returns the next item in the play-queue (shuffle = 0) or shuffle-queue (shuffle = 1) + */ +static struct queue_item * +item_next(struct queue_item *item, char shuffle) +{ + if (shuffle) + return item->shuffle_next; + return item->next; +} + +/* + * Returns the previous item in the play-queue (shuffle = 0) or shuffle-queue (shuffle = 1) + */ +static struct queue_item * +item_prev(struct queue_item *item, char shuffle) +{ + if (shuffle) + return item->shuffle_prev; + return item->prev; +} + +/* + * Returns the (0-based) position of the first item with the given dbmfi-id. + * If no item is found for the given id, it returns -1. + */ +int +queueitem_pos(struct queue_item *item, uint32_t dbmfi_id) +{ + struct queue_item *temp; + int pos; + + if (dbmfi_id == 0 || item->qii.dbmfi_id == dbmfi_id) + return 0; + + pos = 1; + for (temp = item->next; (temp != item) && temp->qii.dbmfi_id != dbmfi_id; temp = temp->next) + { + pos++; + } + + if (temp == item) + { + // Item with given dbmfi_id does not exists + return -1; + } + + return pos; +} + +/* + * Returns the item with the given item_id in the queue + * + * @param queue The queue + * @param item_id The unique id of the item in the queue + * @return Item with the given item_id or NULL if not found + */ +static struct queue_item * +queueitem_get_byitemid(struct queue *queue, int item_id) +{ + struct queue_item *item; + + for (item = queue->head->next; item != queue->head && item->qii.item_id != item_id; item = item->next) + { + // Iterate through the queue until the item with item_id is found + } + + if (item == queue->head && item_id != 0) + return NULL; + + return item; +} + +/* + * Returns the item at the given index (0-based) in the play-queue (shuffle = 0) or shuffle-queue (shuffle = 1) + * + * @param queue The queue + * @param index Index of item in the queue (0-based) + * @param shuffle Play-queue (shuffle = 0) or shuffle-queue (shuffle = 1) + * @return Item at position in the queue or NULL if not found + */ +static struct queue_item * +queueitem_get_byindex(struct queue *queue, unsigned int index, char shuffle) +{ + struct queue_item *item; + int i; + + i = 0; + for (item = item_next(queue->head, shuffle); item != queue->head && i < index; item = item_next(item, shuffle)) + { + i++; + } + + if (item == queue->head) + return NULL; + + return item; +} + +/* + * Returns the item at the given position relative to the item with the given item_id in the + * play queue (shuffle = 0) or shuffle queue (shuffle = 1). + * + * The item with item_id is at pos == 0. + * + * @param queue The queue + * @param pos The position relative to the item with given queue-item-id + * @param shuffle If 0 the position in the play-queue, 1 the position in the shuffle-queue + * @return Item at position in the queue or NULL if not found + */ +static struct queue_item * +queueitem_get_bypos(struct queue *queue, unsigned int item_id, unsigned int pos, char shuffle) +{ + struct queue_item *item_base; + struct queue_item *item; + int i; + + item_base = queueitem_get_byitemid(queue, item_id); + + if (!item_base) + return NULL; + + i = 0; + for (item = item_base; item != queue->head && i < pos; item = item_next(item, shuffle)) + { + i++; + } + + if (item == queue->head) + return NULL; + + return item; +} + +/* + * Returns the item with the given item_id in the queue + * + * @param queue The queue + * @param item_id The unique id of the item in the queue + * @return Item with the given item_id or NULL if not found + */ +struct queue_item_info * +queue_get_byitemid(struct queue *queue, unsigned int item_id) +{ + struct queue_item *item; + + item = queueitem_get_byitemid(queue, item_id); + + if (!item) + return NULL; + + return &item->qii; +} + +/* + * Returns the item at the given index (0-based) in the play queue (shuffle = 0) or shuffle queue (shuffle = 1) + * + * @param queue The queue + * @param index Position of item in the queue (zero-based) + * @param shuffle Play queue (shuffle = 0) or shuffle queue (shuffle = 1) + * @return Item at index in the queue or NULL if not found + */ +struct queue_item_info * +queue_get_byindex(struct queue *queue, unsigned int index, char shuffle) +{ + struct queue_item *item; + + item = queueitem_get_byindex(queue, index, shuffle); + + if (!item) + return NULL; + + return &item->qii; +} + +/* + * Returns the item at the given position relative to the item with the given item_id in the + * play queue (shuffle = 0) or shuffle queue (shuffle = 1). + * + * The item with item_id is at pos == 0. + * + * @param queue The queue + * @param item_id The unique id of the item in the queue + * @param pos The position relative to the item with given queue-item-id + * @param shuffle If 0 the position in the play-queue, 1 the position in the shuffle-queue + * @return Item at position in the queue or NULL if not found + */ +struct queue_item_info * +queue_get_bypos(struct queue *queue, unsigned int item_id, unsigned int pos, char shuffle) +{ + struct queue_item *item; + + item = queueitem_get_bypos(queue, item_id, pos, shuffle); + + if (!item) + return NULL; + + return &item->qii; +} + +/* + * Returns the index of the item with the given item-id (unique id in the queue) + * or -1 if the item does not exist. Depending on the given shuffle value, the position + * is either the on in the play-queue (shuffle = 0) or the shuffle-queue (shuffle = 1). + * + * @param queue The queue to search the item + * @param item_id The id of the item in the queue + * @param shuffle If 0 the position in the play-queue, 1 the position in the shuffle-queue + * @return Index (0-based) of the item in the given queue or -1 if it does not exist + */ +int +queue_index_byitemid(struct queue *queue, unsigned int item_id, char shuffle) +{ + struct queue_item *item; + int pos; + + pos = 0; + for (item = item_next(queue->head, shuffle); item != queue->head && item->qii.item_id != item_id; item = item_next(item, shuffle)) + { + pos++; + } + + if (item == queue->head) + // Item not found + return -1; + + return pos; +} + +/* + * Return the next item in the queue for the item with the given item-id. + * + * @param queue The queue + * @param item_id The id of the item in the queue + * @param shuffle If 0 return the next item in the play-queue, if 1 the next item in the shuffle-queue + * @param r_mode Repeat mode + * @return The next item + */ +struct queue_item_info * +queue_next(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_mode r_mode) +{ + struct queue_item *item; + + item = queueitem_get_byitemid(queue, item_id); + + if (!item) + // Item not found + return NULL; + + if (r_mode == REPEAT_SONG && item != queue->head) + return &item->qii; + + item = item_next(item, shuffle); + + if (item == queue->head && r_mode == REPEAT_ALL) + { + // Repeat all and end of queue reached, return first item in the queue + item = item_next(queue->head, shuffle); + } + + if (item == queue->head) + return NULL; + + return &item->qii; +} + +/* + * Return the previous item in the queue for the item with the given item-id. + * + * @param queue The queue + * @param item_id The id of the item in the queue + * @param shuffle If 0 return the next item in the play-queue, if 1 the next item in the shuffle-queue + * @param r_mode Repeat mode + * @return The previous item + */ +struct queue_item_info * +queue_prev(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_mode r_mode) +{ + struct queue_item *item; + + item = queueitem_get_byitemid(queue, item_id); + + if (!item) + // Item not found + return NULL; + + if (r_mode == REPEAT_SONG && item != queue->head) + return &item->qii; + + item = item_prev(item, shuffle); + + if (item == queue->head && r_mode == REPEAT_ALL) + { + // Repeat all and start of queue reached, return last item in the queue + item = item_prev(queue->head, shuffle); + } + + if (item == queue->head) + return NULL; + + return &item->qii; +} + +/* + * Creates a new queue-info for the given queue. + * + * The given number of items (count) are copied from the play-queue (shuffle = 0) or shuffle-queue (shuffle = 1) + * starting with the item at the given index (0-based). + * + * If count == 0, all items from the given index up to the end of the queue will be returned. + * + * @param queue The queue + * @param index Index of the first item in the queue + * @param count Maximum number of items to copy (if 0 all remaining items after index) + * @param shuffle If 0 the play-queue, if 1 the shuffle queue + * @return A new queue-info with the specified items + */ +struct queue_info * +queue_info_new_byindex(struct queue *queue, unsigned int index, unsigned int count, char shuffle) +{ + struct queue_info *qi; + struct queue_item_info *qii; + struct queue_item *item_base; + struct queue_item *item; + unsigned int i; + unsigned int qlength; + int qii_size; + + qlength = queue_count(queue); + + qii_size = qlength - index; + if (count > 0 && count < qii_size) + qii_size = count; + + if (qii_size <= 0) + { + return NULL; + } + + item_base = queueitem_get_byindex(queue, index, shuffle); + + if (!item_base) + return NULL; + + qi = malloc(sizeof(struct queue_info)); + qii = malloc(qii_size * sizeof(struct queue_item_info)); + + i = 0; + for (item = item_base; item != queue->head && i < qii_size; item = item_next(item, shuffle)) + { + qii[i].dbmfi_id = item->qii.dbmfi_id; + qii[i].item_id = item->qii.item_id; + qii[i].len_ms = item->qii.len_ms; + qii[i].data_kind = item->qii.data_kind; + qii[i].media_kind = item->qii.media_kind; + + i++; + } + + qi->count = i; + qi->length = qlength; + qi->start_pos = index; + qi->queue = qii; + + return qi; +} + +/* + * Creates a new queue-info for the given queue. + * + * The given number of items (count) are copied from the play-queue (shuffle = 0) or shuffle-queue (shuffle = 1) + * starting after the item with the given item_id. The item with item_id is excluded, therefor the first item + * is the one after the item with item_id. + * + * If count == 0, all items from the given index up to the end of the queue will be returned. + * + * @param queue The queue + * @param item_id The unique id of the item in the queue + * @param count Maximum number of items to copy (if 0 all remaining items after index) + * @param shuffle If 0 the play-queue, if 1 the shuffle queue + * @return A new queue-info with the specified items + */ +struct queue_info * +queue_info_new_bypos(struct queue *queue, unsigned int item_id, unsigned int count, char shuffle) +{ + int pos; + struct queue_info *qi; + + pos = queue_index_byitemid(queue, item_id, shuffle); + + if (pos < 0) + pos = 0; + else + pos = pos + 1; // exclude the item with the given item-id + + qi = queue_info_new_byindex(queue, pos, count, shuffle); + + return qi; +} + +/* + * Frees the queue info + */ +void +queue_info_free(struct queue_info *qi) +{ + free(qi->queue); + free(qi); +} + +/* + * Adds items to the queue after the given item + * + * @param queue The queue to add the new items + * @param item_new The item(s) to add + * @param item_prev The item to append the new items + */ +static void +queue_add_afteritem(struct queue *queue, struct queue_item *item_new, struct queue_item *item_prev) +{ + struct queue_item *item; + struct queue_item *item_tail; + + if (!item_new) + { + DPRINTF(E_LOG, L_PLAYER, "Invalid new item given to add items\n"); + return; + } + + // Check the item after which the new items will be added + if (!item_prev) + { + DPRINTF(E_LOG, L_PLAYER, "Invalid previous item given to add items\n"); + queue_items_free(item_new); + return; + } + + // Set item-id for all new items + queue->last_inserted_item_id++; + item_new->qii.item_id = queue->last_inserted_item_id; + for (item = item_new->next; item != item_new; item = item->next) + { + queue->last_inserted_item_id++; + item->qii.item_id = queue->last_inserted_item_id; + } + + // Add items into the queue + item_tail = item_new->prev; + + item_tail->next = item_prev->next; + item_tail->shuffle_next = item_prev->shuffle_next; + item_prev->next->prev = item_tail; + item_prev->shuffle_next->shuffle_prev = item_tail; + + item_prev->next = item_new; + item_prev->shuffle_next = item_new; + item_new->prev = item_prev; + item_new->shuffle_prev = item_prev; +} + +/* + * Adds items to the end of the queue + * + * @param queue The queue to add the new items + * @param item The item(s) to add + */ +void +queue_add(struct queue *queue, struct queue_item *item) +{ + queue_add_afteritem(queue, item, queue->head->prev); +} + +/* + * Adds items to the queue after the item with the given item id (id of the item in the queue) + * + * @param queue The queue to add the new items + * @param item The item(s) to add + * @param item_id The item id after which the new items will be inserted + */ +void +queue_add_after(struct queue *queue, struct queue_item *item, unsigned int item_id) +{ + struct queue_item *item_prev; + + // Get the item after which the new items will be added + item_prev = queueitem_get_byitemid(queue, item_id); + queue_add_afteritem(queue, item, item_prev); +} + +/* + * Moves the item at from_pos to to_pos in the play-queue (shuffle = 0) or shuffle-queue (shuffle = 1) + * + * The position arguments are relativ to the item with the given id. At position = 1 is the first item + * after the item with the given id (either in the play-queue or shuffle-queue, depending on the shuffle + * argument). + * + * @param queue The queue to move items + * @param from_pos The position of the first item to be moved + * @param to_pos The position to move the items + * @param shuffle If 0 the position in the play-queue, 1 the position in the shuffle-queue + */ +void +queue_move_bypos(struct queue *queue, unsigned int item_id, unsigned int from_pos, unsigned int to_offset, char shuffle) +{ + struct queue_item *item; + struct queue_item *item_next; + + // Get the item to be moved + item = queueitem_get_bypos(queue, item_id, from_pos, shuffle); + if (!item) + { + DPRINTF(E_LOG, L_PLAYER, "Invalid position given to move items\n"); + return; + } + + // Get the item at the target position + item_next = queueitem_get_bypos(queue, item_id, to_offset, shuffle); + if (!item_next) + { + DPRINTF(E_LOG, L_PLAYER, "Invalid position given to move items\n"); + return; + } + + // Remove item from the queue + if (shuffle) + { + item->shuffle_prev->shuffle_next = item->shuffle_next; + item->shuffle_next->shuffle_prev = item->shuffle_prev; + } + else + { + item->prev->next = item->next; + item->next->prev = item->prev; + } + + // Insert item into the queue befor the item at the target postion + if (shuffle) + { + item_next->shuffle_prev->shuffle_next = item; + item->shuffle_prev = item_next->shuffle_prev; + + item_next->shuffle_prev = item; + item->shuffle_next = item_next; + } + else + { + item_next->next->prev = item; + item->next = item_next->next; + + item_next->next = item; + item->prev = item_next; + } +} + +/* + * Removes the item from the queue and frees it + */ +static void +queue_remove_item(struct queue_item *item) +{ + struct queue_item *item_next; + struct queue_item *item_prev; + + item_next = item->next; + item_prev = item->prev; + + item_prev->next = item_next; + item_next->prev = item_prev; + + item_next = item->shuffle_next; + item_prev = item->shuffle_prev; + + item_prev->shuffle_next = item_next; + item_next->shuffle_prev = item_prev; + + item->next = NULL; + item->prev = NULL; + item->shuffle_next = NULL; + item->shuffle_prev = NULL; + + free(item); +} + +/* + * Removes the item with the given item-id from the queue + */ +void +queue_remove_byitemid(struct queue *queue, unsigned int item_id) +{ + struct queue_item *item; + + // Do not remove the head item + if (item_id <= 0) + return; + + // Get the item after which the items will be removed from the queue + item = queueitem_get_byitemid(queue, item_id); + if (!item) + { + DPRINTF(E_LOG, L_PLAYER, "Invalid item-id given to remove items\n"); + return; + } + + queue_remove_item(item); +} + +/* + * Remove item at index from the play-queue (shuffle = 0) or shuffle-queue (shuffle = 1) + * + * @param queue The queue + * @param index The index of the item to be removed (0-based) + * @param shuffle If 0 the position in the play-queue, 1 the position in the shuffle-queue + */ +void +queue_remove_byindex(struct queue *queue, unsigned int index, char shuffle) +{ + struct queue_item *item; + + // Get the item after which the items will be removed from the queue + item = queueitem_get_byindex(queue, index, shuffle); + if (!item) + { + DPRINTF(E_LOG, L_PLAYER, "Invalid position given to remove items\n"); + return; + } + + queue_remove_item(item); +} + +/* + * Removes the item at pos from the play-queue (shuffle = 0) or shuffle-queue (shuffle = 1) + * + * The position argument is relativ to the item with the given id. At position = 1 is the first item + * after the item with the given id (either in the play-queue or shuffle-queue, depending on the shuffle + * argument). + * + * @param queue The queue to add the new items + * @param item_id The unique id of the item in the queue + * @param pos The position of the first item to be removed + * @param shuffle If 0 the position in the play-queue, 1 the position in the shuffle-queue + */ +void +queue_remove_bypos(struct queue *queue, unsigned int item_id, unsigned int pos, char shuffle) +{ + struct queue_item *item; + + // Get the item after which the items will be removed from the queue + item = queueitem_get_bypos(queue, item_id, pos, shuffle); + if (!item) + { + DPRINTF(E_LOG, L_PLAYER, "Invalid position given to remove items\n"); + return; + } + + queue_remove_item(item); +} + +/* + * Removes all items from the queue + * + * @param queue The queue to clear + */ +void +queue_clear(struct queue *queue) +{ + struct queue_item *item; + + // Check if the queue is already empty + if (queue->head->next == queue->head) + return; + + // Remove the head item from the shuffle-queue + item = queue->head->shuffle_next; + item->shuffle_prev = queue->head->shuffle_prev; + queue->head->shuffle_prev->shuffle_next = item; + + // Remove the head item from the play-queue + item = queue->head->next; + item->prev = queue->head->prev; + queue->head->prev->next = item; + + // item now points to the first item in the play-queue (excluding the head item) + queue_items_free(item); + + // Make the queue circular again + queue->head->next = queue->head; + queue->head->prev = queue->head; + queue->head->shuffle_next = queue->head; + queue->head->shuffle_prev = queue->head; +} + +/* + * Resets the shuffle-queue to be identical to the play-queue and returns the item + * with the given item_id. + * + * If no item was found with the given item_id, it returns the head item. + */ +static struct queue_item * +queue_reset_and_find(struct queue *queue, unsigned int item_id) +{ + struct queue_item *item; + struct queue_item *temp; + + item = queue->head; + + item->shuffle_next = item->next; + item->shuffle_prev = item->prev; + + for (temp = item->next; temp != queue->head; temp = temp->next) + { + temp->shuffle_next = temp->next; + temp->shuffle_prev = temp->prev; + + if (temp->qii.item_id == item_id) + item = temp; + } + + return item; +} + +/* + * Shuffles the queue + * + * If the item_id > 0, only the items in the queue after the item (excluding it) + * with the given id are shuffled. + * + * @param queue The queue to shuffle + * @param item_id 0 to shuffle the whole queue or the item-id after which the queue gets shuffled + */ +void +queue_shuffle(struct queue *queue, unsigned int item_id) +{ + struct queue_item *temp; + struct queue_item *item; + struct queue_item **item_array; + int nitems; + int i; + + item = queue_reset_and_find(queue, item_id); + + // Count items to reshuffle + nitems = 0; + for (temp = item->next; temp != queue->head; temp = temp->next) + { + nitems++; + } + + // Do not reshuffle queue with one item + if (nitems < 2) + return; + + // Construct array for number of items in queue + item_array = (struct queue_item **)malloc(nitems * sizeof(struct queue_item *)); + if (!item_array) + { + DPRINTF(E_LOG, L_PLAYER, "Could not allocate memory for shuffle array\n"); + return; + } + + // Fill array with items in queue + i = 0; + for (temp = item->next; temp != queue->head; temp = temp->next) + { + item_array[i] = temp; + i++; + } + + // Shuffle item array + shuffle_ptr(&queue->shuffle_rng, (void **)item_array, nitems); + + // Update shuffle-next/-prev for shuffled items + for (i = 0; i < nitems; i++) + { + temp = item_array[i]; + + if (i > 0) + temp->shuffle_prev = item_array[i - 1]; + else + temp->shuffle_prev = NULL; + + if (i < (nitems - 1)) + temp->shuffle_next = item_array[i + 1]; + else + temp->shuffle_next = NULL; + } + + // Insert shuffled items after item with given item_id + item->shuffle_next = item_array[0]; + item_array[0]->shuffle_prev = item; + + queue->head->shuffle_prev = item_array[nitems - 1]; + item_array[nitems - 1]->shuffle_next = queue->head; + + free(item_array); +} + +/* + * Creates a new queue item for the given media file + * + * @param dbmfi media file info + * @return The new queue item or NULL if an error occured + */ +static struct queue_item * +queue_item_new(struct db_media_file_info *dbmfi) +{ + struct queue_item *item; + uint32_t id; + uint32_t len_ms; + uint32_t data_kind; + uint32_t media_kind; + int ret; + + ret = safe_atou32(dbmfi->id, &id); + if (ret < 0) + { + DPRINTF(E_LOG, L_PLAYER, "Invalid song id in query result!\n"); + return NULL; + } + + ret = safe_atou32(dbmfi->song_length, &len_ms); + if (ret < 0) + { + DPRINTF(E_LOG, L_PLAYER, "Invalid song length in query result!\n"); + return NULL; + } + + ret = safe_atou32(dbmfi->data_kind, &data_kind); + if (ret < 0) + { + DPRINTF(E_LOG, L_PLAYER, "Invalid data kind in query result!\n"); + return NULL; + } + + ret = safe_atou32(dbmfi->media_kind, &media_kind); + if (ret < 0) + { + DPRINTF(E_LOG, L_PLAYER, "Invalid media kind in query result!\n"); + return NULL; + } + + item = (struct queue_item *) calloc(1, sizeof(struct queue_item)); + if (!item) + { + DPRINTF(E_LOG, L_PLAYER, "Out of memory for struct queue_item\n"); + return NULL; + } + + item->qii.dbmfi_id = id; + item->qii.len_ms = len_ms; + item->qii.data_kind = data_kind; + item->qii.media_kind = media_kind; + + return item; +} + +struct queue_item * +queue_make(struct query_params *qp) +{ + struct db_media_file_info dbmfi; + struct queue_item *item_head; + struct queue_item *item_tail; + struct queue_item *item_temp; + int ret; + + ret = db_query_start(qp); + if (ret < 0) + { + DPRINTF(E_LOG, L_PLAYER, "Could not start query\n"); + return NULL; + } + + DPRINTF(E_DBG, L_PLAYER, "Player queue query returned %d items\n", qp->results); + + item_head = NULL; + item_tail = NULL; + while (((ret = db_query_fetch_file(qp, &dbmfi)) == 0) && (dbmfi.id)) + { + item_temp = queue_item_new(&dbmfi); + if (!item_temp) + { + DPRINTF(E_LOG, L_PLAYER, "Error creating new queue_item for id '%s'\n", dbmfi.id); + continue; + } + + if (!item_head) + item_head = item_temp; + + if (item_tail) + { + item_tail->next = item_temp; + item_temp->prev = item_tail; + item_tail->shuffle_next = item_temp; + item_temp->shuffle_prev = item_tail; + } + + item_tail = item_temp; + + DPRINTF(E_DBG, L_PLAYER, "Added song id %s (%s)\n", dbmfi.id, dbmfi.title); + } + + db_query_end(qp); + + if (ret < 0) + { + DPRINTF(E_LOG, L_PLAYER, "Error fetching results\n"); + return NULL; + } + + item_head->prev = item_tail; + item_tail->next = item_head; + item_head->shuffle_prev = item_tail; + item_tail->shuffle_next = item_head; + + return item_head; +} + +struct queue_item * +queue_make_pl(int plid) +{ + struct query_params qp; + struct queue_item *item;; + + memset(&qp, 0, sizeof(struct query_params)); + + qp.id = plid; + qp.type = Q_PLITEMS; + qp.offset = 0; + qp.limit = 0; + qp.sort = S_NONE; + qp.idx_type = I_NONE; + + item = queue_make(&qp); + + return item; +} diff --git a/src/queue.h b/src/queue.h new file mode 100644 index 00000000..27a6c0e9 --- /dev/null +++ b/src/queue.h @@ -0,0 +1,129 @@ + +#ifndef SRC_QUEUE_H_ +#define SRC_QUEUE_H_ + + +enum repeat_mode { + REPEAT_OFF = 0, + REPEAT_SONG = 1, + REPEAT_ALL = 2, +}; + + +/* + * Internal representation of a queue + */ +struct queue; + +/* + * Internal representation of a list of queue items + */ +struct queue_item; + +/* + * External representation of an item in a queue + */ +struct queue_item_info +{ + /* Item-Id is a unique id for this queue item. If the same item appears multiple + times in the queue each corresponding queue item has its own id. */ + unsigned int item_id; + + /* Id of the file/item in the files database */ + unsigned int dbmfi_id; + + /* Length of the item in ms */ + unsigned int len_ms; + + /* Data type of the item */ + enum data_kind data_kind; + /* Media type of the item */ + enum media_kind media_kind; +}; + +/* + * External representation of a queue + */ +struct queue_info +{ + // The number of items in the queue + unsigned int length; + + // The position (0-based) in the queue for the first item in the queue array + unsigned int start_pos; + // The number of items in the queue array + unsigned int count; + // The queue array (array of items infos) + struct queue_item_info *queue; +}; + +struct queue * +queue_new(); + +void +queue_free(struct queue *queue); + +unsigned int +queue_count(struct queue *queue); + +int +queueitem_pos(struct queue_item *item, uint32_t dbmfi_id); + +struct queue_item_info * +queue_get_byitemid(struct queue *queue, unsigned int item_id); + +struct queue_item_info * +queue_get_byindex(struct queue *queue, unsigned int index, char shuffle); + +struct queue_item_info * +queue_get_bypos(struct queue *queue, unsigned int item_id, unsigned int pos, char shuffle); + +int +queue_index_byitemid(struct queue *queue, unsigned int item_id, char shuffle); + +struct queue_item_info * +queue_next(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_mode r_mode); + +struct queue_item_info * +queue_prev(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_mode r_mode); + +struct queue_info * +queue_info_new_byindex(struct queue *queue, unsigned int index, unsigned int count, char shuffle); + +struct queue_info * +queue_info_new_bypos(struct queue *queue, unsigned int item_id, unsigned int count, char shuffle); + +void +queue_info_free(struct queue_info *qi); + +void +queue_add(struct queue *queue, struct queue_item *item); + +void +queue_add_after(struct queue *queue, struct queue_item *item, unsigned int item_id); + +void +queue_move_bypos(struct queue *queue, unsigned int item_id, unsigned int from_pos, unsigned int to_offset, char shuffle); + +void +queue_remove_byitemid(struct queue *queue, unsigned int item_id); + +void +queue_remove_byindex(struct queue *queue, unsigned int index, char shuffle); + +void +queue_remove_bypos(struct queue *queue, unsigned int item_id, unsigned int pos, char shuffle); + +void +queue_clear(struct queue *queue); + +void +queue_shuffle(struct queue *queue, unsigned int item_id); + +struct queue_item * +queue_make(struct query_params *qp); + +struct queue_item * +queue_make_pl(int plid); + +#endif /* SRC_QUEUE_H_ */ From e1147c00a1d6d1325b842514f76ef2e06ccd451d Mon Sep 17 00:00:00 2001 From: chme Date: Wed, 2 Sep 2015 18:31:43 +0200 Subject: [PATCH 07/23] [player] Refactor player to better fit the queue refactor --- src/db.h | 2 +- src/player.c | 1260 ++++++++++++++++++++++++----------------------- src/spotify.h | 3 + src/transcode.c | 2 +- 4 files changed, 651 insertions(+), 616 deletions(-) diff --git a/src/db.h b/src/db.h index d5ac0fa7..1d20569e 100644 --- a/src/db.h +++ b/src/db.h @@ -157,7 +157,7 @@ struct media_file_info { uint32_t item_kind; /* song or movie */ uint32_t data_kind; /* dmap.datakind (asdk) */ - uint64_t sample_count; + uint64_t sample_count; //TODO [unused] sample count is never set and therefor always 0 uint32_t compilation; char artwork; diff --git a/src/player.c b/src/player.c index 431fabb8..1a374e64 100644 --- a/src/player.c +++ b/src/player.c @@ -52,7 +52,6 @@ #include "mdns.h" #include "conffile.h" #include "misc.h" -#include "rng.h" #include "player.h" #include "raop.h" #include "laudio.h" @@ -84,19 +83,41 @@ struct player_source { + /* Id of the file/item in the files database */ uint32_t id; + + /* Item-Id of the file/item in the queue */ uint32_t queueitem_id; + + /* Length of the file/item in milliseconds */ uint32_t len_ms; enum data_kind data_kind; enum media_kind media_kind; - int setup_done; + /* Start time of the media item as rtp-time + The stream-start is the rtp-time the media item did or would have + started playing (after seek or pause), therefor the elapsed time of the + media item is always: + elapsed time = current rtptime - stream-start */ uint64_t stream_start; + + /* Output start time of the media item as rtp-time + The output start time is the rtp-time of the first audio packet send + to the audio outputs. + It differs from stream-start especially after a seek, where the first audio + packet has the next rtp-time as output start and stream start becomes the + rtp-time the media item would have been started playing if the seek did + not happen. */ uint64_t output_start; + + /* End time of media item as rtp-time + The end time is set if the reading (source_read) of the media item reached + end of file, until then it is 0. */ uint64_t end; struct transcode_ctx *ctx; + int setup_done; struct player_source *play_next; }; @@ -121,14 +142,6 @@ struct spk_enum void *arg; }; -enum range_type - { - RANGEARG_NONE, - RANGEARG_ID, - RANGEARG_POS, - RANGEARG_RANGE - }; - struct playback_start_param { uint32_t id; @@ -267,9 +280,6 @@ static struct player_command *cur_cmd; /* Last commanded volume */ static int master_volume; -/* Shuffle RNG state */ -struct rng_ctx shuffle_rng; - /* Audio source */ static struct player_source *cur_playing; static struct player_source *cur_streaming; @@ -786,123 +796,71 @@ metadata_check_icy(void) http_icy_metadata_free(metadata, 0); } +struct player_history * +player_history_get(void) +{ + return history; +} + +/* + * Add the song with the given id to the list of previously played songs + */ +static void +history_add(uint32_t id) +{ + unsigned int cur_index; + unsigned int next_index; + + /* Check if the current song is already the last in the history to avoid duplicates */ + cur_index = (history->start_index + history->count - 1) % MAX_HISTORY_COUNT; + if (id == history->id[cur_index]) + { + DPRINTF(E_LOG, L_PLAYER, "Current playing/streaming song already in history\n"); + return; + } + + /* Calculate the next index and update the start-index and count for the id-buffer */ + next_index = (history->start_index + history->count) % MAX_HISTORY_COUNT; + if (next_index == history->start_index && history->count > 0) + history->start_index = (history->start_index + 1) % MAX_HISTORY_COUNT; + + history->id[next_index] = id; + + if (history->count < MAX_HISTORY_COUNT) + history->count++; +} + + /* Audio sources */ -static struct player_source * -source_new(struct queue_item_info *item) -{ - struct player_source *ps; - - ps = (struct player_source *)calloc(1, sizeof(struct player_source)); - - ps->id = item->dbmfi_id; - ps->queueitem_id = item->item_id; - ps->data_kind = item->data_kind; - ps->media_kind = item->media_kind; - ps->len_ms = item->len_ms; - - return ps; -} - -static void -source_free(struct player_source *ps) -{ - switch (ps->data_kind) - { - case DATA_KIND_FILE: - case DATA_KIND_HTTP: - if (ps->ctx) - transcode_cleanup(ps->ctx); - break; - - case DATA_KIND_SPOTIFY: -#ifdef HAVE_SPOTIFY_H - spotify_playback_stop(); -#endif - break; - - case DATA_KIND_PIPE: - pipe_cleanup(); - break; - } - - free(ps); -} - -static void -source_stop(struct player_source *ps) -{ - struct player_source *tmp; - - while (ps) - { - switch (ps->data_kind) - { - case DATA_KIND_FILE: - case DATA_KIND_HTTP: - if (ps->ctx) - { - transcode_cleanup(ps->ctx); - ps->ctx = NULL; - } - break; - - case DATA_KIND_SPOTIFY: -#ifdef HAVE_SPOTIFY_H - spotify_playback_stop(); -#endif - break; - - case DATA_KIND_PIPE: - pipe_cleanup(); - break; - } - - tmp = ps; - ps = ps->play_next; - - tmp->play_next = NULL; - } -} - -/* Helper */ +/* + * Initializes the given player source for playback + */ static int -source_open(struct player_source *ps, int no_md, int seek) +stream_setup(struct player_source *ps, struct media_file_info *mfi) { - struct media_file_info *mfi; char *url; int ret; - ps->setup_done = 0; - ps->stream_start = 0; - ps->output_start = 0; - ps->end = 0; - ps->play_next = NULL; - - mfi = db_file_fetch_byid(ps->id); - if (!mfi) + if (!ps || !mfi) { - DPRINTF(E_LOG, L_PLAYER, "Couldn't fetch file id %d\n", ps->id); - + DPRINTF(E_LOG, L_PLAYER, "No player source and/or media info given to stream_setup\n"); return -1; } - if (mfi->disabled) + if (ps->setup_done) { - DPRINTF(E_DBG, L_PLAYER, "File id %d is disabled, skipping\n", ps->id); - - free_mfi(mfi, 0); + DPRINTF(E_LOG, L_PLAYER, "Given player source already setup\n"); return -1; } - DPRINTF(E_INFO, L_PLAYER, "Opening '%s' (%s)\n", mfi->title, mfi->path); - - ps->data_kind = mfi->data_kind; - ps->media_kind = mfi->media_kind; - - // Setup the source type responsible for getting the audio - switch (mfi->data_kind) + // Setup depending on data kind + switch (ps->data_kind) { + case DATA_KIND_FILE: + ret = transcode_setup(&ps->ctx, mfi, NULL, 0); + break; + case DATA_KIND_HTTP: ret = http_stream_setup(&url, mfi->path); if (ret < 0) @@ -916,12 +874,7 @@ source_open(struct player_source *ps, int no_md, int seek) case DATA_KIND_SPOTIFY: #ifdef HAVE_SPOTIFY_H - ret = spotify_playback_play(mfi); - if (seek && mfi->seek) - { - DPRINTF(E_DBG, L_PLAYER, "Source id %d started with seek %d\n", ps->id, mfi->seek); - ret = spotify_playback_seek(mfi->seek); - } + ret = spotify_playback_play(mfi); //TODO [player] split spotify setup/play into separate functions #else ret = -1; #endif @@ -932,170 +885,537 @@ source_open(struct player_source *ps, int no_md, int seek) break; default: - ret = transcode_setup(&ps->ctx, mfi, NULL, 0); - if (seek && mfi->seek) - { - DPRINTF(E_DBG, L_PLAYER, "Source id %d started with seek %d\n", ps->id, mfi->seek); - ret = transcode_seek(ps->ctx, mfi->seek); - } + ret = -1; } - free_mfi(mfi, 0); - - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Could not open file id %d\n", ps->id); - - return -1; - } - - if (!no_md) - metadata_trigger(ps, (player_state == PLAY_PLAYING) ? 0 : 1); - - ps->setup_done = 1; + if (ret == 0) + ps->setup_done = 1; return ret; } +/* + * Starts or resumes plaback for the given player source + */ static int -source_next(int force) +stream_play(struct player_source *ps) { - struct player_source *ps_next; - struct queue_item_info *item; - enum repeat_mode r_mode; - uint32_t cur_queueitem_id; int ret; - r_mode = repeat; - - /* Force repeat mode at user request */ - if (force && (r_mode == REPEAT_SONG)) - r_mode = REPEAT_ALL; - - cur_queueitem_id = 0; - if (cur_streaming) - cur_queueitem_id = cur_streaming->queueitem_id; - - item = queue_next(queue, cur_queueitem_id, shuffle, r_mode); - - if (!item) + if (!ps) { - DPRINTF(E_DBG, L_PLAYER, "End of playlist reached and repeat is OFF\n"); - - //playback_abort(); - cur_streaming = NULL; - return 0; - } - - /* - * Check if the next item is the same as the current streaming (REPEAT SINGLE or REPEAT ALL - * with only one item in the playlist) - */ - if (item->item_id == cur_streaming->queueitem_id) - { - if ((cur_streaming->data_kind == DATA_KIND_FILE) && cur_streaming->ctx) - { - ret = transcode_seek(cur_streaming->ctx, 0); - - /* source_open() takes care of sending metadata, but we don't - * call it when repeating a song as we just seek back to 0 - * so we have to handle metadata ourselves here - */ - if (ret >= 0) - metadata_trigger(cur_streaming, 0); - } - else - ret = source_open(cur_streaming, force, 0); - - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Failed to restart song for song repeat\n"); - - return -1; - } - - return 0; - } - - do - { - ps_next = source_new(item); - //TODO [queue] reshuffle if repeat all and end of playlist reached - ret = source_open(ps_next, 1, 0); - if (ret < 0) - { - // Failed to open source, try next - source_free(ps_next); - item = queue_next(queue, item->item_id, shuffle, repeat); //TODO performance optimization, call to queue_next always iterates from the beginning of the queue - continue; - } - - // Successfully opened the next source, break out of the loop - break; - } - while (item); - - /* Couldn't open any of the files in our queue */ - if (ret < 0) - { - DPRINTF(E_WARN, L_PLAYER, "Could not open any file in the queue (next)\n"); - + DPRINTF(E_LOG, L_PLAYER, "Stream pause called with no active streaming player source\n"); return -1; } - if (!force && cur_streaming) - cur_streaming->play_next = ps_next; + if (!ps->setup_done) + { + DPRINTF(E_LOG, L_PLAYER, "Given player source not setup, pause not possible\n"); + return -1; + } - cur_streaming = ps_next; + // Start/resume playback depending on data kind + switch (ps->data_kind) + { + case DATA_KIND_HTTP: + case DATA_KIND_FILE: + ret = 0; + break; + +#ifdef HAVE_SPOTIFY_H + case DATA_KIND_SPOTIFY: + ret = spotify_playback_resume(); + break; +#endif + + case DATA_KIND_PIPE: + ret = 0; + break; + + default: + ret = -1; + } + + return ret; +} + +/* + * Read up to "len" data from the given player source and returns + * the actual amount of data read. + */ +static int +stream_read(struct player_source *ps, int len) +{ + int icy_timer; + int ret; + + if (!ps) + { + DPRINTF(E_LOG, L_PLAYER, "Stream read called with no active streaming player source\n"); + return -1; + } + + if (!ps->setup_done) + { + DPRINTF(E_LOG, L_PLAYER, "Given player source not setup for reading data\n"); + return -1; + } + + // Read up to len data depending on data kind + switch (ps->data_kind) + { + case DATA_KIND_HTTP: + ret = transcode(ps->ctx, audio_buf, len, &icy_timer); + + if (icy_timer) + metadata_check_icy(); + break; + + case DATA_KIND_FILE: + ret = transcode(ps->ctx, audio_buf, len, &icy_timer); + break; + +#ifdef HAVE_SPOTIFY_H + case DATA_KIND_SPOTIFY: + ret = spotify_audio_get(audio_buf, len); + break; +#endif + + case DATA_KIND_PIPE: + ret = pipe_audio_get(audio_buf, len); + break; + + default: + ret = -1; + } + + return ret; +} + +/* + * Pauses playback of the given player source + */ +static int +stream_pause(struct player_source *ps) +{ + int ret; + + if (!ps) + { + DPRINTF(E_LOG, L_PLAYER, "Stream pause called with no active streaming player source\n"); + return -1; + } + + if (!ps->setup_done) + { + DPRINTF(E_LOG, L_PLAYER, "Given player source not setup, pause not possible\n"); + return -1; + } + + // Pause playback depending on data kind + switch (ps->data_kind) + { + case DATA_KIND_HTTP: + case DATA_KIND_FILE: + ret = 0; + break; + +#ifdef HAVE_SPOTIFY_H + case DATA_KIND_SPOTIFY: + spotify_playback_pause_nonblock(); //TODO [player] spotify blocking pause command missing + ret = 0; + break; +#endif + + case DATA_KIND_PIPE: + ret = 0; + break; + + default: + ret = -1; + } + + return ret; +} + +/* + * Seeks to the given position in miliseconds of the given player source + */ +static int +stream_seek(struct player_source *ps, int seek_ms) +{ + int ret; + + if (!ps) + { + DPRINTF(E_LOG, L_PLAYER, "Stream seek called with no active streaming player source\n"); + return -1; + } + + if (!ps->setup_done) + { + DPRINTF(E_LOG, L_PLAYER, "Given player source not setup, seek not possible\n"); + return -1; + } + + // Seek depending on data kind + switch (ps->data_kind) + { + case DATA_KIND_HTTP: + ret = 0; + break; + + case DATA_KIND_FILE: + ret = transcode_seek(ps->ctx, seek_ms); + break; + +#ifdef HAVE_SPOTIFY_H + case DATA_KIND_SPOTIFY: + ret = spotify_playback_seek(seek_ms); + break; +#endif + + case DATA_KIND_PIPE: + ret = 0; + break; + + default: + ret = -1; + } + + return ret; +} + +/* + * Stops playback for the given player source + */ +static int +stream_stop(struct player_source *ps) +{ + int ret; + + if (!ps) + { + DPRINTF(E_LOG, L_PLAYER, "Stream pause called with no active streaming player source\n"); + return -1; + } + + if (!ps->setup_done) + { + DPRINTF(E_LOG, L_PLAYER, "Given player source not setup, pause not possible\n"); + return -1; + } + + // Pause playback depending on data kind + switch (ps->data_kind) + { + case DATA_KIND_HTTP: + case DATA_KIND_FILE: + ret = 0; + break; + +#ifdef HAVE_SPOTIFY_H + case DATA_KIND_SPOTIFY: + ret = spotify_playback_stop(); + break; +#endif + + case DATA_KIND_PIPE: + ret = 0; + break; + + default: + ret = -1; + } + + return ret; +} + +/* + * Cleanup given player source + */ +static int +stream_cleanup(struct player_source *ps) +{ + if (!ps) + { + DPRINTF(E_LOG, L_PLAYER, "Stream cleanup called with no active streaming player source\n"); + return -1; + } + + if (!ps->setup_done) + { + DPRINTF(E_LOG, L_PLAYER, "Given player source not setup, cleanup not possible\n"); + return -1; + } + + switch (ps->data_kind) + { + case DATA_KIND_FILE: + case DATA_KIND_HTTP: + if (ps->ctx) + { + transcode_cleanup(ps->ctx); + ps->ctx = NULL; + } + break; + + case DATA_KIND_SPOTIFY: +#ifdef HAVE_SPOTIFY_H + spotify_playback_stop(); //TODO [player] spotify cleanup functions +#endif + break; + + case DATA_KIND_PIPE: + pipe_cleanup(); + break; + } + + ps->setup_done = 0; return 0; } -static int -source_prev(void) + +static struct player_source * +source_now_playing() { - struct queue_item_info *item; - struct player_source *ps_prev; + if (cur_playing) + return cur_playing; + + return cur_streaming; +} + +/* + * Creates a new player source for the given queue item + */ +static struct player_source * +source_new(struct queue_item_info *item) +{ + struct player_source *ps; + + ps = (struct player_source *)calloc(1, sizeof(struct player_source)); + + ps->id = item->dbmfi_id; + ps->queueitem_id = item->item_id; + ps->data_kind = item->data_kind; + ps->media_kind = item->media_kind; + ps->len_ms = item->len_ms; + ps->play_next = NULL; + + return ps; +} + +/* + * Stops playback for the current streaming source and frees all + * player sources (starting from the playing source). Sets current streaming + * and playing sources to NULL. + */ +static int +source_stop() +{ + struct player_source *ps_playing; + struct player_source *ps_temp; + + stream_stop(cur_streaming); + stream_cleanup(cur_streaming); + + ps_playing = source_now_playing(); + + while (ps_playing) + { + ps_temp = ps_playing; + ps_playing = ps_playing->play_next; + + ps_temp->play_next = NULL; + free(ps_temp); + } + + cur_playing = NULL; + cur_streaming = NULL; + return 0; +} + +/* + * Pauses playback + * + * Resets the streaming source to the playing source and adjusts stream-start + * and output-start values to the playing time. Sets the current streaming + * source to NULL. + */ +static int +source_pause(uint64_t pos) +{ + struct player_source *ps_playing; + struct player_source *ps_playnext; + struct player_source *ps_temp; + struct media_file_info *mfi; + uint64_t seek_frames; + int seek_ms; int ret; - if (!cur_streaming) - return -1; + ps_playing = source_now_playing(); - item = queue_prev(queue, cur_streaming->queueitem_id, shuffle, repeat); - - if (!item) + if (ps_playing != cur_streaming) { - DPRINTF(E_DBG, L_PLAYER, "Start of playlist reached and repeat is OFF\n"); + stream_stop(cur_streaming); + stream_cleanup(cur_streaming); + } + else + stream_pause(cur_streaming); - playback_abort(); - return 0; + ps_playnext = ps_playing->play_next; + while (ps_playnext) + { + ps_temp = ps_playnext; + ps_playnext = ps_playnext->play_next; + + ps_temp->play_next = NULL; + free(ps_temp); + } + ps_playing->play_next = NULL; + + cur_playing = NULL; + cur_streaming = ps_playing; + + if (!cur_streaming->setup_done) + { + mfi = db_file_fetch_byid(cur_streaming->id); + if (!mfi) + { + DPRINTF(E_LOG, L_PLAYER, "Couldn't fetch file id %d\n", cur_streaming->id); + + return -1; + } + + if (mfi->disabled) + { + DPRINTF(E_DBG, L_PLAYER, "File id %d is disabled, skipping\n", cur_streaming->id); + + free_mfi(mfi, 0); + return -1; + } + + DPRINTF(E_INFO, L_PLAYER, "Opening '%s' (%s)\n", mfi->title, mfi->path); + + stream_setup(cur_streaming, mfi); } - do - { - ps_prev = source_new(item); - ret = source_open(ps_prev, 1, 0); - if (ret < 0) - { - // Failed to open source, try next - source_free(ps_prev); - item = queue_prev(queue, item->item_id, shuffle, repeat); //TODO performance optimization, call to queue_prev always iterates from the beginning of the queue - continue; - } + seek_frames = (pos - cur_streaming->stream_start); + seek_ms = (int)((seek_frames * 1000) / 44100); + ret = stream_seek(cur_streaming, seek_ms); - break; + /* Adjust start_pos to take into account the pause and seek back */ + cur_streaming->stream_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES - ((uint64_t)ret * 44100) / 1000; + cur_streaming->output_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES; + cur_streaming->end = 0; + + return 0; +} + +/* + * Seeks the current streaming source to the given postion in milliseconds + * and adjusts stream-start and output-start values. + */ +static int +source_seek(int seek_ms) +{ + int ret; + + ret = stream_seek(cur_streaming, seek_ms); + + /* Adjust start_pos to take into account the pause and seek back */ + cur_streaming->stream_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES - ((uint64_t)ret * 44100) / 1000; + cur_streaming->output_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES; + + return ret; +} + +/* + * Starts or resumes playback + */ +static int +source_play() +{ + int ret; + + ret = stream_play(cur_streaming); + + return ret; +} + +/* + * Initializes playback of the given queue item (but does not start playback) + * + * A new source is created for the given queue item and is set as the current + * streaming source. If a streaming source already existed (and reached eof) + * the new source is appended as the play-next item to it. + * + * Stream-start and output-start values are set to the given start position. + */ +static int +source_open(struct queue_item_info *qii, uint64_t start_pos, int seek) +{ + struct player_source *ps; + struct media_file_info *mfi; + int ret; + + if (cur_streaming && cur_streaming->end == 0) + { + DPRINTF(E_LOG, L_PLAYER, "Current streaming source not at eof %d\n", cur_streaming->id); + return -1; } - while (item); - /* Couldn't open any of the files in our queue */ - if (ret < 0) + mfi = db_file_fetch_byid(qii->dbmfi_id); + if (!mfi) { - DPRINTF(E_WARN, L_PLAYER, "Could not open any file in the queue (prev)\n"); + DPRINTF(E_LOG, L_PLAYER, "Couldn't fetch file id %d\n", qii->dbmfi_id); return -1; } - cur_streaming = ps_prev; + if (mfi->disabled) + { + DPRINTF(E_DBG, L_PLAYER, "File id %d is disabled, skipping\n", qii->dbmfi_id); + + free_mfi(mfi, 0); + return -1; + } + + DPRINTF(E_INFO, L_PLAYER, "Opening '%s' (%s)\n", mfi->title, mfi->path); + + ps = source_new(qii); + + ret = stream_setup(ps, mfi); + + if (cur_streaming) + { + cur_streaming->play_next = ps; + } + + cur_streaming = ps; + + cur_streaming->stream_start = start_pos; + cur_streaming->output_start = cur_streaming->stream_start; + cur_streaming->end = 0; + + if (seek && mfi->seek) + source_seek(mfi->seek); + + free_mfi(mfi, 0); + return ret; +} + +/* + * Closes the current streaming source and sets its end-time to the given + * position + */ +static int +source_close(uint64_t end_pos) +{ + stream_cleanup(cur_streaming); + + cur_streaming->end = end_pos; return 0; } @@ -1110,14 +1430,10 @@ source_check(void) struct timespec ts; struct player_source *ps; uint64_t pos; - enum repeat_mode r_mode; int i; int id; int ret; -// if (!cur_streaming) -// return 0; - ret = player_get_current_pos(&pos, &ts, 0); if (ret < 0) { @@ -1147,45 +1463,6 @@ source_check(void) /* 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; - - /* If repeat mode is REPEAT_SONG or playlist has only one file and repeat mode is REPEAT_ALL, - source_next() does not open a new source but instead seeks to position 0. */ - if (r_mode == REPEAT_SONG || (r_mode == REPEAT_ALL && !cur_playing->play_next)) - { - ps = cur_playing; - - /* Check that we haven't gone to the next file already - * (repeat song toggled in the last 2 seconds of a song) - */ - if (cur_playing->play_next) - { - cur_playing = cur_playing->play_next; - - if (ps->setup_done) - { - if ((ps->data_kind == DATA_KIND_FILE) && ps->ctx) - { - transcode_cleanup(ps->ctx); - ps->ctx = NULL; - } - ps->play_next = NULL; - } - } - - cur_playing->stream_start = ps->end + 1; - cur_playing->output_start = cur_playing->stream_start; - - /* Do not use cur_playing to reset the end position, it may have changed */ - ps->end = 0; - - status_update(PLAY_PLAYING); - - metadata_prune(pos); - - return pos; - } - i = 0; while (cur_playing && (cur_playing->end != 0) && (pos > cur_playing->end)) { @@ -1196,11 +1473,9 @@ source_check(void) #ifdef LASTFM worker_execute(scrobble_cb, &id, sizeof(int), 8); #endif + history_add(cur_playing->id); - /* Stop playback if: - * - at end of playlist (NULL) - * - repeat OFF and at end of playlist (wraparound) - */ + /* Stop playback */ if (!cur_playing->play_next) { playback_abort(); @@ -1211,18 +1486,7 @@ source_check(void) ps = cur_playing; cur_playing = cur_playing->play_next; - cur_playing->stream_start = ps->end + 1; - cur_playing->output_start = cur_playing->stream_start; - - if (ps->setup_done) - { - if ((ps->data_kind == DATA_KIND_FILE) && ps->ctx) - { - transcode_cleanup(ps->ctx); - ps->ctx = NULL; - } - ps->play_next = NULL; - } + free(ps); } if (i > 0) @@ -1237,57 +1501,14 @@ source_check(void) return pos; } -static struct player_source * -source_now_playing(void) -{ - if (cur_playing) - return cur_playing; - - return cur_streaming; -} - -struct player_history * -player_history_get(void) -{ - return history; -} - -/* - * Add the song with the given id to the list of previously played songs - */ -static void -history_add(uint32_t id) -{ - unsigned int cur_index; - unsigned int next_index; - - /* Check if the current song is already the last in the history to avoid duplicates */ - cur_index = (history->start_index + history->count - 1) % MAX_HISTORY_COUNT; - if (id == history->id[cur_index]) - { - DPRINTF(E_LOG, L_PLAYER, "Current playing/streaming song already in history\n"); - return; - } - - /* Calculate the next index and update the start-index and count for the id-buffer */ - next_index = (history->start_index + history->count) % MAX_HISTORY_COUNT; - if (next_index == history->start_index && history->count > 0) - history->start_index = (history->start_index + 1) % MAX_HISTORY_COUNT; - - history->id[next_index] = id; - - if (history->count < MAX_HISTORY_COUNT) - history->count++; -} - static int source_read(uint8_t *buf, int len, uint64_t rtptime) { int new; int ret; int nbytes; - int icy_timer; char *silence_buf; + struct queue_item_info *item; if (!cur_streaming) return 0; @@ -1302,49 +1523,30 @@ source_read(uint8_t *buf, int len, uint64_t rtptime) new = 0; - // add song to the played history - history_add(cur_streaming->id); + item = queue_next(queue, cur_streaming->queueitem_id, shuffle, repeat); + if (item) + { + ret = source_open(item, cur_streaming->end + 1, 0); - ret = source_next(0); - if (ret < 0) - return -1; + if (ret < 0) + return -1; + } + else + { + cur_streaming = NULL; + } } if (evbuffer_get_length(audio_buf) == 0) { if (cur_streaming) { - switch (cur_streaming->data_kind) - { - case DATA_KIND_HTTP: - ret = transcode(cur_streaming->ctx, audio_buf, len - nbytes, &icy_timer); - - if (icy_timer) - metadata_check_icy(); - break; - - case DATA_KIND_FILE: - ret = transcode(cur_streaming->ctx, audio_buf, len - nbytes, &icy_timer); - break; - -#ifdef HAVE_SPOTIFY_H - case DATA_KIND_SPOTIFY: - ret = spotify_audio_get(audio_buf, len - nbytes); - break; -#endif - - case DATA_KIND_PIPE: - ret = pipe_audio_get(audio_buf, len - nbytes); - break; - - default: - ret = -1; - } + ret = stream_read(cur_streaming, len - nbytes); } else { // Reached end of playlist (cur_playing is NULL) send silence and source_check will abort playback if the last item was played - DPRINTF(E_DBG, L_PLAYER, "End of playlist reached, stream silence until playback of last item ends\n"); + DPRINTF(E_SPAM, L_PLAYER, "End of playlist reached, stream silence until playback of last item ends\n"); silence_buf = (char *)calloc((len - nbytes), sizeof(char)); evbuffer_add(audio_buf, silence_buf, (len - nbytes)); free(silence_buf); @@ -1354,7 +1556,7 @@ source_read(uint8_t *buf, int len, uint64_t rtptime) if (ret <= 0) { /* EOF or error */ - cur_streaming->end = rtptime + BTOS(nbytes) - 1; + source_close(rtptime + BTOS(nbytes) - 1); new = 1; continue; @@ -1375,6 +1577,7 @@ playback_write(void) int ret; source_check(); + /* Make sure playback is still running after source_check() */ if (player_state == PLAY_STOPPED) return; @@ -1939,9 +2142,6 @@ device_restart_cb(struct raop_device *dev, struct raop_session *rs, enum raop_se static void playback_abort(void) { - struct player_source *ps_playing; - struct player_source *ps_temp; - if (laudio_status != LAUDIO_CLOSED) laudio_close(); @@ -1950,19 +2150,7 @@ playback_abort(void) pb_timer_stop(); - ps_playing = source_now_playing(); - source_stop(ps_playing); - - // Free all player_source items - while (ps_playing) - { - ps_temp = ps_playing; - ps_playing = ps_playing->play_next; - source_free(ps_temp); - } - - cur_playing = NULL; - cur_streaming = NULL; + source_stop(); playerqueue_clear(NULL); @@ -2121,7 +2309,6 @@ static int playback_stop(struct player_command *cmd) { struct player_source *ps_playing; - struct player_source *ps_temp; if (laudio_status != LAUDIO_CLOSED) laudio_close(); @@ -2138,19 +2325,9 @@ playback_stop(struct player_command *cmd) if (ps_playing) { history_add(ps_playing->id); - source_stop(ps_playing); } - // Free all player_source items - while (ps_playing) - { - ps_temp = ps_playing; - ps_playing = ps_playing->play_next; - source_free(ps_temp); - } - - cur_playing = NULL; - cur_streaming = NULL; + source_stop(); evbuffer_drain(audio_buf, evbuffer_get_length(audio_buf)); @@ -2236,8 +2413,7 @@ playback_start_item(struct player_command *cmd, struct queue_item_info *qii) uint32_t *dbmfi_id; struct raop_device *rd; struct player_source *ps_playing; - struct player_source *ps_temp; - struct player_source *ps; + struct queue_item_info *item; int ret; dbmfi_id = cmd->arg.playback_start_param.id_ptr; @@ -2264,89 +2440,38 @@ playback_start_item(struct player_command *cmd, struct queue_item_info *qii) // Update global playback position pb_pos = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES - 88200; - /* - * If either an item id or an item position is given, get the corresponding - * player_source from the queue. - */ + item = NULL; if (qii) - ps = source_new(qii); - else - ps = NULL; - - /* - * Update queue and cur_streaming depending on - * - given player_source to start playing - * - player state - */ - if (ps) { - /* - * A song is specified in the arguments (by id or pos) and the corresponding - * player_source (ps) from the queue was found. - * - * Stop playback (if it was paused) and prepare to start playback on ps. - */ - source_stop(ps_playing); - - // Free all player_source items - while (ps_playing) - { - ps_temp = ps_playing; - ps_playing = ps_playing->play_next; - source_free(ps_temp); - } - - cur_playing = NULL; - cur_streaming = NULL; - - cur_streaming = ps; - - ret = source_open(cur_streaming, 0, 1); - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Couldn't jump to source %d in queue\n", cur_streaming->id); - - playback_abort(); - return -1; - } - - if (dbmfi_id) - *dbmfi_id = cur_streaming->id; - - cur_streaming->stream_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES - ((uint64_t)ret * 44100) / 1000; - cur_streaming->output_start = cur_streaming->stream_start; + item = qii; } else if (!cur_streaming) { - /* - * Player was stopped, start playing the queue - */ if (shuffle) - queue_shuffle(queue, 0); - - ret = source_next(0); - if (ret < 0) - { - DPRINTF(E_LOG, L_PLAYER, "Couldn't find anything to play!\n"); - - playback_abort(); - return -1; - } - - cur_streaming->stream_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES; - cur_streaming->output_start = cur_streaming->stream_start; + queue_shuffle(queue, 0); + item = queue_next(queue, 0, shuffle, repeat); } - else + + if (item) { - /* - * Player was paused, resume playing cur_streaming - * - * After a pause, the source is still open so source_open() doesn't get - * called and we have to handle metadata ourselves. - */ - metadata_trigger(cur_streaming, 1); + source_stop(); + source_open(item, last_rtptime + AIRTUNES_V2_PACKET_SAMPLES, 1); } + ret = source_play(); + if (ret < 0) + { + playback_abort(); + return -1; + } + + + if (dbmfi_id) + *dbmfi_id = cur_streaming->id; + + metadata_trigger(cur_streaming, 1); + + /* Start local audio if needed */ if (laudio_selected && (laudio_status == LAUDIO_CLOSED)) { @@ -2471,6 +2596,7 @@ playback_prev_bh(struct player_command *cmd) { int ret; int pos_sec; + struct queue_item_info *item; if (!cur_streaming) { @@ -2479,14 +2605,12 @@ playback_prev_bh(struct player_command *cmd) } /* Only add to history if playback started. */ - if (cur_streaming->end > cur_streaming->stream_start) + if (cur_streaming->output_start > cur_streaming->stream_start) history_add(cur_streaming->id); - source_stop(cur_streaming); - /* Compute the playing time in seconds for the current song. */ - if (cur_streaming->end > cur_streaming->stream_start) - pos_sec = (cur_streaming->end - cur_streaming->stream_start) / 44100; + if (cur_streaming->output_start > cur_streaming->stream_start) + pos_sec = (cur_streaming->output_start - cur_streaming->stream_start) / 44100; else pos_sec = 0; @@ -2495,7 +2619,16 @@ playback_prev_bh(struct player_command *cmd) DPRINTF(E_DBG, L_PLAYER, "Skipping song played %d sec\n", pos_sec); if (pos_sec < 3) { - ret = source_prev(); + item = queue_prev(queue, cur_streaming->queueitem_id, shuffle, repeat); + if (!item) + { + playback_abort(); + return -1; + } + + source_stop(); + + ret = source_open(item, last_rtptime + AIRTUNES_V2_PACKET_SAMPLES, 0); if (ret < 0) { playback_abort(); @@ -2505,7 +2638,7 @@ playback_prev_bh(struct player_command *cmd) } else { - ret = source_open(cur_streaming, 1, 0); + ret = source_seek(0); if (ret < 0) { playback_abort(); @@ -2517,11 +2650,6 @@ playback_prev_bh(struct player_command *cmd) if (player_state == PLAY_STOPPED) return -1; - cur_streaming->stream_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES; - cur_streaming->output_start = cur_streaming->stream_start; - - cur_playing = NULL; - /* Silent status change - playback_start() sends the real status update */ player_state = PLAY_PAUSED; @@ -2533,6 +2661,7 @@ static int playback_next_bh(struct player_command *cmd) { int ret; + struct queue_item_info *item; if (!cur_streaming) { @@ -2541,27 +2670,28 @@ playback_next_bh(struct player_command *cmd) } /* Only add to history if playback started. */ - if (cur_streaming->end > cur_streaming->stream_start) + if (cur_streaming->output_start > cur_streaming->stream_start) history_add(cur_streaming->id); - source_stop(cur_streaming); + item = queue_next(queue, cur_streaming->queueitem_id, shuffle, repeat); + if (!item) + { + playback_abort(); + return -1; + } - ret = source_next(1); + source_stop(); + + ret = source_open(item, last_rtptime + AIRTUNES_V2_PACKET_SAMPLES, 0); if (ret < 0) { playback_abort(); - return -1; } if (player_state == PLAY_STOPPED) return -1; - cur_streaming->stream_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES; - cur_streaming->output_start = cur_streaming->stream_start; - - cur_playing = NULL; - /* Silent status change - playback_start() sends the real status update */ player_state = PLAY_PAUSED; @@ -2571,38 +2701,12 @@ playback_next_bh(struct player_command *cmd) static int playback_seek_bh(struct player_command *cmd) { - struct player_source *ps; int ms; int ret; ms = cmd->arg.intval; - if (cur_playing) - ps = cur_playing; - else - ps = cur_streaming; - - ps->end = 0; - - /* Seek to commanded position */ - switch (ps->data_kind) - { - case DATA_KIND_FILE: - ret = transcode_seek(ps->ctx, ms); - break; -#ifdef HAVE_SPOTIFY_H - case DATA_KIND_SPOTIFY: - ret = spotify_playback_seek(ms); - break; -#endif - case DATA_KIND_PIPE: - case DATA_KIND_HTTP: - ret = 1; - break; - - default: - ret = -1; - } + ret = source_seek(ms); if (ret < 0) { @@ -2611,13 +2715,6 @@ playback_seek_bh(struct player_command *cmd) return -1; } - /* Adjust start_pos for the new position */ - ps->stream_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES - ((uint64_t)ret * 44100) / 1000; - ps->output_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES; - - cur_streaming = ps; - cur_playing = NULL; - /* Silent status change - playback_start() sends the real status update */ player_state = PLAY_PAUSED; @@ -2627,55 +2724,15 @@ playback_seek_bh(struct player_command *cmd) static int playback_pause_bh(struct player_command *cmd) { - struct player_source *ps; - uint64_t pos; - int ms; int ret; - if (cur_playing) - ps = cur_playing; - else - ps = cur_streaming; - - pos = ps->end; - ps->end = 0; - - /* Seek back to current playback position */ - pos -= ps->stream_start; - ms = (int)((pos * 1000) / 44100); - - switch (ps->data_kind) - { - case DATA_KIND_FILE: - ret = transcode_seek(ps->ctx, ms); - break; -#ifdef HAVE_SPOTIFY_H - case DATA_KIND_SPOTIFY: - ret = spotify_playback_seek(ms); - break; -#endif - default: - ret = -1; - } - - if (ret < 0) - { - playback_abort(); - - return -1; - } - - /* Adjust start_pos to take into account the pause and seek back */ - ps->stream_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES - ((uint64_t)ret * 44100) / 1000; - ps->output_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES; - - cur_streaming = ps; - cur_playing = NULL; - status_update(PLAY_PAUSED); - if (ps->media_kind & (MEDIA_KIND_MOVIE | MEDIA_KIND_PODCAST | MEDIA_KIND_AUDIOBOOK | MEDIA_KIND_TVSHOW)) - db_file_save_seek(ps->id, ret); + if (cur_streaming->media_kind & (MEDIA_KIND_MOVIE | MEDIA_KIND_PODCAST | MEDIA_KIND_AUDIOBOOK | MEDIA_KIND_TVSHOW)) + { + ret = (cur_streaming->output_start - cur_streaming->stream_start) / 44100 * 1000; + db_file_save_seek(cur_streaming->id, ret); + } return 0; } @@ -2683,9 +2740,6 @@ playback_pause_bh(struct player_command *cmd) static int playback_pause(struct player_command *cmd) { - struct player_source *ps_playing; - struct player_source *ps; - struct player_source *ps_temp; uint64_t pos; pos = source_check(); @@ -2701,11 +2755,6 @@ playback_pause(struct player_command *cmd) if (player_state == PLAY_STOPPED) return -1; - ps_playing = source_now_playing(); - - /* Store pause position */ - ps_playing->end = pos; - cmd->raop_pending = raop_flush(device_command_cb, last_rtptime + AIRTUNES_V2_PACKET_SAMPLES); if (laudio_status != LAUDIO_CLOSED) @@ -2713,21 +2762,7 @@ playback_pause(struct player_command *cmd) pb_timer_stop(); - ps = ps_playing->play_next; - if (ps) - source_stop(ps); - - // Free all player_source items in the play_next list except the now playing source (becomes the new cur_streaming source) - while (ps) - { - ps_temp = ps; - ps = ps->play_next; - source_free(ps_temp); - } - - cur_playing = NULL; - cur_streaming = ps_playing; - cur_streaming->play_next = NULL; + source_pause(pos); evbuffer_drain(audio_buf, evbuffer_get_length(audio_buf)); @@ -3268,7 +3303,6 @@ playerqueue_get_bypos(struct player_command *cmd) count = cmd->arg.queue_get_param.count; - // Set pos to the position of the current item + 1 ps = source_now_playing(); item_id = 0; @@ -4644,8 +4678,6 @@ player_init(void) gcry_randomize(&rnd, sizeof(rnd), GCRY_STRONG_RANDOM); last_rtptime = ((uint64_t)1 << 32) | rnd; - rng_init(&shuffle_rng); - ret = db_speaker_get(0, &laudio_selected, &laudio_volume); if (ret < 0) laudio_volume = PLAYER_DEFAULT_VOLUME; diff --git a/src/spotify.h b/src/spotify.h index e4fce854..b2147983 100644 --- a/src/spotify.h +++ b/src/spotify.h @@ -16,6 +16,9 @@ spotify_playback_play(struct media_file_info *mfi); void spotify_playback_pause_nonblock(void); +int +spotify_playback_resume(void); + int spotify_playback_stop(void); diff --git a/src/transcode.c b/src/transcode.c index 3b2e076d..a67040e2 100644 --- a/src/transcode.c +++ b/src/transcode.c @@ -731,7 +731,7 @@ transcode_setup(struct transcode_ctx **nctx, struct media_file_info *mfi, off_t } ctx->duration = mfi->song_length; - ctx->samples = mfi->sample_count; + ctx->samples = mfi->sample_count; //TODO [unused] sample count is never set and therefor always 0 ctx->wavhdr = wavhdr; if (wavhdr) From 58069e67f886992568a222070b42426594b5bf03 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 12 Sep 2015 07:45:31 +0200 Subject: [PATCH 08/23] [player/spotify] adjust spotify integration to refactoring of player --- src/player.c | 16 ++++++++------ src/spotify.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++----- src/spotify.h | 8 ++++++- 3 files changed, 72 insertions(+), 12 deletions(-) diff --git a/src/player.c b/src/player.c index 1a374e64..669bfa01 100644 --- a/src/player.c +++ b/src/player.c @@ -874,7 +874,7 @@ stream_setup(struct player_source *ps, struct media_file_info *mfi) case DATA_KIND_SPOTIFY: #ifdef HAVE_SPOTIFY_H - ret = spotify_playback_play(mfi); //TODO [player] split spotify setup/play into separate functions + ret = spotify_playback_setup(mfi); #else ret = -1; #endif @@ -904,13 +904,13 @@ stream_play(struct player_source *ps) if (!ps) { - DPRINTF(E_LOG, L_PLAYER, "Stream pause called with no active streaming player source\n"); + DPRINTF(E_LOG, L_PLAYER, "Stream play called with no active streaming player source\n"); return -1; } if (!ps->setup_done) { - DPRINTF(E_LOG, L_PLAYER, "Given player source not setup, pause not possible\n"); + DPRINTF(E_LOG, L_PLAYER, "Given player source not setup, play not possible\n"); return -1; } @@ -924,7 +924,7 @@ stream_play(struct player_source *ps) #ifdef HAVE_SPOTIFY_H case DATA_KIND_SPOTIFY: - ret = spotify_playback_resume(); + ret = spotify_playback_play(); break; #endif @@ -1022,7 +1022,7 @@ stream_pause(struct player_source *ps) #ifdef HAVE_SPOTIFY_H case DATA_KIND_SPOTIFY: - spotify_playback_pause_nonblock(); //TODO [player] spotify blocking pause command missing + spotify_playback_pause(); ret = 0; break; #endif @@ -1162,7 +1162,7 @@ stream_cleanup(struct player_source *ps) case DATA_KIND_SPOTIFY: #ifdef HAVE_SPOTIFY_H - spotify_playback_stop(); //TODO [player] spotify cleanup functions + spotify_playback_stop(); //TODO [player] spotify cleanup functions? #endif break; @@ -1530,6 +1530,10 @@ source_read(uint8_t *buf, int len, uint64_t rtptime) if (ret < 0) return -1; + + ret = source_play(); + if (ret < 0) + return -1; } else { diff --git a/src/spotify.c b/src/spotify.c index d4658ac0..e1603b66 100644 --- a/src/spotify.c +++ b/src/spotify.c @@ -912,12 +912,12 @@ audio_fifo_flush(void) } static int -playback_play(struct spotify_command *cmd) +playback_setup(struct spotify_command *cmd) { sp_track *track; sp_error err; - DPRINTF(E_DBG, L_SPOTIFY, "Starting playback\n"); + DPRINTF(E_DBG, L_SPOTIFY, "Setting up for playback\n"); if (SP_CONNECTION_STATE_LOGGED_IN != fptr_sp_session_connectionstate(g_sess)) { @@ -947,6 +947,16 @@ playback_play(struct spotify_command *cmd) audio_fifo_flush(); + return 0; +} + +static int +playback_play(struct spotify_command *cmd) +{ + sp_error err; + + DPRINTF(E_DBG, L_SPOTIFY, "Starting playback\n"); + err = fptr_sp_session_player_play(g_sess, 1); if (SP_ERROR_OK != err) { @@ -1626,13 +1636,13 @@ notify_cb(int fd, short what, void *arg) /* Thread: player */ int -spotify_playback_play(struct media_file_info *mfi) +spotify_playback_setup(struct media_file_info *mfi) { struct spotify_command cmd; sp_link *link; int ret; - DPRINTF(E_DBG, L_SPOTIFY, "Playback request\n"); + DPRINTF(E_DBG, L_SPOTIFY, "Playback setup request\n"); link = fptr_sp_link_create_from_string(mfi->path); if (!link) @@ -1643,7 +1653,7 @@ spotify_playback_play(struct media_file_info *mfi) command_init(&cmd); - cmd.func = playback_play; + cmd.func = playback_setup; cmd.arg.link = link; ret = sync_command(&cmd); @@ -1653,6 +1663,46 @@ spotify_playback_play(struct media_file_info *mfi) return ret; } +int +spotify_playback_play() +{ + struct spotify_command cmd; + int ret; + + DPRINTF(E_DBG, L_SPOTIFY, "Playback request\n"); + + command_init(&cmd); + + cmd.func = playback_play; + cmd.arg.noarg = NULL; + + ret = sync_command(&cmd); + + command_deinit(&cmd); + + return ret; +} + +int +spotify_playback_pause() +{ + struct spotify_command cmd; + int ret; + + DPRINTF(E_DBG, L_SPOTIFY, "Pause request\n"); + + command_init(&cmd); + + cmd.func = playback_pause; + cmd.arg.noarg = NULL; + + ret = sync_command(&cmd); + + command_deinit(&cmd); + + return ret; +} + /* Thread: libspotify */ void spotify_playback_pause_nonblock(void) diff --git a/src/spotify.h b/src/spotify.h index b2147983..3e95325c 100644 --- a/src/spotify.h +++ b/src/spotify.h @@ -11,7 +11,13 @@ #endif int -spotify_playback_play(struct media_file_info *mfi); +spotify_playback_setup(struct media_file_info *mfi); + +int +spotify_playback_play(); + +int +spotify_playback_pause(); void spotify_playback_pause_nonblock(void); From f4faac3cc8b9a3534a64a6308036af55cce57bca Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 12 Sep 2015 07:47:49 +0200 Subject: [PATCH 09/23] [spotify] Remove duplicate method "resume" in favor of "play" --- src/spotify.c | 42 +----------------------------------------- src/spotify.h | 3 --- 2 files changed, 1 insertion(+), 44 deletions(-) diff --git a/src/spotify.c b/src/spotify.c index e1603b66..acee4ffc 100644 --- a/src/spotify.c +++ b/src/spotify.c @@ -990,25 +990,6 @@ playback_pause(struct spotify_command *cmd) return 0; } -static int -playback_resume(struct spotify_command *cmd) -{ - sp_error err; - - DPRINTF(E_DBG, L_SPOTIFY, "Resuming playback\n"); - - err = fptr_sp_session_player_play(g_sess, 1); - if (SP_ERROR_OK != err) - { - DPRINTF(E_LOG, L_SPOTIFY, "Playback resume failed: %s\n", fptr_sp_error_message(err)); - return -1; - } - - g_state = SPOTIFY_STATE_PLAYING; - - return 0; -} - static int playback_stop(struct spotify_command *cmd) { @@ -1081,7 +1062,7 @@ audio_get(struct spotify_command *cmd) // If spotify was paused begin by resuming playback if (g_state == SPOTIFY_STATE_PAUSED) - playback_resume(NULL); + playback_play(NULL); pthread_mutex_lock(&g_audio_fifo->mutex); @@ -1728,27 +1709,6 @@ spotify_playback_pause_nonblock(void) nonblock_command(cmd); } -/* Not used */ -int -spotify_playback_resume(void) -{ - struct spotify_command cmd; - int ret; - - DPRINTF(E_DBG, L_SPOTIFY, "Resume request\n"); - - command_init(&cmd); - - cmd.func = playback_resume; - cmd.arg.noarg = NULL; - - ret = sync_command(&cmd); - - command_deinit(&cmd); - - return ret; -} - /* Thread: player and libspotify */ int spotify_playback_stop(void) diff --git a/src/spotify.h b/src/spotify.h index 3e95325c..1eccd221 100644 --- a/src/spotify.h +++ b/src/spotify.h @@ -22,9 +22,6 @@ spotify_playback_pause(); void spotify_playback_pause_nonblock(void); -int -spotify_playback_resume(void); - int spotify_playback_stop(void); From c8a61015fcb3dc6641544de0fbe2c85982771cf0 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 12 Sep 2015 08:22:52 +0200 Subject: [PATCH 10/23] [dacp] Fix playing of radio streams after queue refactoring --- src/httpd_dacp.c | 8 ++++++-- src/player.c | 1 - src/queue.c | 42 +++++++++++++++++++++++++++++++++++++++--- src/queue.h | 5 +++++ 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index 19a2b4da..f834dff7 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -758,7 +758,6 @@ dacp_reply_ctrlint(struct evhttp_request *req, struct evbuffer *evbuf, char **ur static int find_first_song_id(const char *query) { - //TODO [refactor][performance] Unnecessary query, it is enough to extract the item id from the query string. Accessing the db to verify the item exists is not needed. struct db_media_file_info dbmfi; struct query_params qp; int id; @@ -1214,7 +1213,12 @@ dacp_reply_playspec(struct evhttp_request *req, struct evbuffer *evbuf, char **u DPRINTF(E_DBG, L_DACP, "Playspec request for playlist %d, start song id %d%s\n", plid, pos, (shuffle) ? ", shuffle" : ""); - items = queue_make_pl(plid); //TODO [queue] get queue-item-id or pos for first song to play (dacp) --- , &pos); + items = NULL; + if (plid > 0) + items = queue_make_pl(plid); + else if (pos > 0) + items = queue_make_item(pos); + if (!items) { DPRINTF(E_LOG, L_DACP, "Could not build song queue from playlist %d\n", plid); diff --git a/src/player.c b/src/player.c index 669bfa01..faff5815 100644 --- a/src/player.c +++ b/src/player.c @@ -1527,7 +1527,6 @@ source_read(uint8_t *buf, int len, uint64_t rtptime) if (item) { ret = source_open(item, cur_streaming->end + 1, 0); - if (ret < 0) return -1; diff --git a/src/queue.c b/src/queue.c index 64c14b53..1c6c35b6 100644 --- a/src/queue.c +++ b/src/queue.c @@ -16,14 +16,16 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "queue.h" + +#include #include #include #include -#include "db.h" #include "logger.h" #include "misc.h" -#include "queue.h" #include "rng.h" @@ -1070,11 +1072,17 @@ queue_make(struct query_params *qp) return item_head; } +/* + * Makes a list of queue-items for the given playlist id (plid) + * + * @param plid Id of the playlist + * @return List of items for all playlist items + */ struct queue_item * queue_make_pl(int plid) { struct query_params qp; - struct queue_item *item;; + struct queue_item *item; memset(&qp, 0, sizeof(struct query_params)); @@ -1089,3 +1097,31 @@ queue_make_pl(int plid) return item; } + +/* + * Makes a queue-item for the item/file with the given id (dbmfi_id) + * + * @param dbmfi_id Id of the item/file + * @return List of items containing only the item with the given id + */ +struct queue_item * +queue_make_item(uint32_t dbmfi_id) +{ + struct query_params qp; + struct queue_item *item; + char buf[124]; + + memset(&qp, 0, sizeof(struct query_params)); + + qp.id = 0; + qp.type = Q_ITEMS; + qp.offset = 0; + qp.limit = 0; + qp.sort = S_NONE; + snprintf(buf, sizeof(buf), "f.id = %" PRIu32, dbmfi_id); + qp.filter = strdup(buf); + + item = queue_make(&qp); + + return item; +} diff --git a/src/queue.h b/src/queue.h index 27a6c0e9..5c99a232 100644 --- a/src/queue.h +++ b/src/queue.h @@ -3,6 +3,8 @@ #define SRC_QUEUE_H_ +#include "db.h" + enum repeat_mode { REPEAT_OFF = 0, REPEAT_SONG = 1, @@ -126,4 +128,7 @@ queue_make(struct query_params *qp); struct queue_item * queue_make_pl(int plid); +struct queue_item * +queue_make_item(uint32_t dbmfi_id); + #endif /* SRC_QUEUE_H_ */ From 4595a2f4a669d57fcd8719f98ab170e267e4be27 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 12 Sep 2015 08:44:35 +0200 Subject: [PATCH 11/23] [player] Add missing metadata trigger on item change (after eof) --- src/player.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/player.c b/src/player.c index faff5815..135afe99 100644 --- a/src/player.c +++ b/src/player.c @@ -1533,6 +1533,8 @@ source_read(uint8_t *buf, int len, uint64_t rtptime) ret = source_play(); if (ret < 0) return -1; + + metadata_trigger(cur_streaming, 0); } else { From e44a160b9a781d6911ab3b73b697d213932be1c7 Mon Sep 17 00:00:00 2001 From: chme Date: Sun, 13 Sep 2015 07:58:27 +0200 Subject: [PATCH 12/23] [player] Improve error handling --- src/player.c | 58 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/src/player.c b/src/player.c index 135afe99..c47a8271 100644 --- a/src/player.c +++ b/src/player.c @@ -850,7 +850,7 @@ stream_setup(struct player_source *ps, struct media_file_info *mfi) if (ps->setup_done) { - DPRINTF(E_LOG, L_PLAYER, "Given player source already setup\n"); + DPRINTF(E_LOG, L_PLAYER, "Given player source already setup (id = %d)\n", ps->id); return -1; } @@ -876,6 +876,8 @@ stream_setup(struct player_source *ps, struct media_file_info *mfi) #ifdef HAVE_SPOTIFY_H ret = spotify_playback_setup(mfi); #else + DPRINTF(E_LOG, L_PLAYER, "Player source has data kind 'spotify' (%d), but forked-daapd is compiled without spotify support - cannot setup source '%s' (%s)\n", + ps->data_kind, mfi->title, mfi->path); ret = -1; #endif break; @@ -885,11 +887,15 @@ stream_setup(struct player_source *ps, struct media_file_info *mfi) break; default: + DPRINTF(E_LOG, L_PLAYER, "Unknown data kind (%d) for player source - cannot setup source '%s' (%s)\n", + ps->data_kind, mfi->title, mfi->path); ret = -1; } if (ret == 0) - ps->setup_done = 1; + ps->setup_done = 1; + else + DPRINTF(E_LOG, L_PLAYER, "Failed to setup player source (id = %d)\n", ps->id); return ret; } @@ -1022,8 +1028,7 @@ stream_pause(struct player_source *ps) #ifdef HAVE_SPOTIFY_H case DATA_KIND_SPOTIFY: - spotify_playback_pause(); - ret = 0; + ret = spotify_playback_pause(); break; #endif @@ -1096,13 +1101,13 @@ stream_stop(struct player_source *ps) if (!ps) { - DPRINTF(E_LOG, L_PLAYER, "Stream pause called with no active streaming player source\n"); + DPRINTF(E_LOG, L_PLAYER, "Stream stop called with no active streaming player source\n"); return -1; } if (!ps->setup_done) { - DPRINTF(E_LOG, L_PLAYER, "Given player source not setup, pause not possible\n"); + DPRINTF(E_LOG, L_PLAYER, "Given player source not setup, stop not possible\n"); return -1; } @@ -1258,11 +1263,23 @@ source_pause(uint64_t pos) if (ps_playing != cur_streaming) { - stream_stop(cur_streaming); - stream_cleanup(cur_streaming); + DPRINTF(E_DBG, L_PLAYER, + "Pause called on playing source (id=%d) and streaming source already " + "switched to the next item (id=%d)\n", ps_playing->id, cur_streaming->id); + ret = stream_stop(cur_streaming); + if (ret < 0) + return -1; + + ret = stream_cleanup(cur_streaming); + if (ret < 0) + return -1; } else - stream_pause(cur_streaming); + { + ret = stream_pause(cur_streaming); + if (ret < 0) + return -1; + } ps_playnext = ps_playing->play_next; while (ps_playnext) @@ -1298,9 +1315,18 @@ source_pause(uint64_t pos) DPRINTF(E_INFO, L_PLAYER, "Opening '%s' (%s)\n", mfi->title, mfi->path); - stream_setup(cur_streaming, mfi); + ret = stream_setup(cur_streaming, mfi); + if (ret < 0) + { + DPRINTF(E_LOG, L_PLAYER, "Failed to open '%s' (%s)\n", mfi->title, mfi->path); + free_mfi(mfi, 0); + return -1; + } + + free_mfi(mfi, 0); } + /* Seek back to the pause position */ seek_frames = (pos - cur_streaming->stream_start); seek_ms = (int)((seek_frames * 1000) / 44100); ret = stream_seek(cur_streaming, seek_ms); @@ -1323,6 +1349,8 @@ source_seek(int seek_ms) int ret; ret = stream_seek(cur_streaming, seek_ms); + if (ret < 0) + return -1; /* Adjust start_pos to take into account the pause and seek back */ cur_streaming->stream_start = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES - ((uint64_t)ret * 44100) / 1000; @@ -1387,7 +1415,15 @@ source_open(struct queue_item_info *qii, uint64_t start_pos, int seek) ps = source_new(qii); ret = stream_setup(ps, mfi); + if (ret < 0) + { + DPRINTF(E_LOG, L_PLAYER, "Failed to open '%s' (%s)\n", mfi->title, mfi->path); + free_mfi(mfi, 0); + return -1; + } + /* If a streaming source exists, append the new source as play-next and set it + as the new streaming source */ if (cur_streaming) { cur_streaming->play_next = ps; @@ -1399,6 +1435,7 @@ source_open(struct queue_item_info *qii, uint64_t start_pos, int seek) cur_streaming->output_start = cur_streaming->stream_start; cur_streaming->end = 0; + /* Seek to the saved seek position */ if (seek && mfi->seek) source_seek(mfi->seek); @@ -1561,6 +1598,7 @@ source_read(uint8_t *buf, int len, uint64_t rtptime) if (ret <= 0) { /* EOF or error */ + //TODO [player] distinguish between eof and error - on error remove item from queue two avoid endless loops if repeat is set source_close(rtptime + BTOS(nbytes) - 1); new = 1; From 2b037b67d94f8ebec47a14a1b9beeae9fc2cdf0b Mon Sep 17 00:00:00 2001 From: chme Date: Fri, 18 Sep 2015 19:19:02 +0200 Subject: [PATCH 13/23] [player] Combine stream_stop and stream_cleanup into one function --- src/player.c | 56 +++------------------------------------------------- 1 file changed, 3 insertions(+), 53 deletions(-) diff --git a/src/player.c b/src/player.c index c47a8271..5787123b 100644 --- a/src/player.c +++ b/src/player.c @@ -264,7 +264,7 @@ static uint64_t pb_pos; static uint64_t last_rtptime; /* AirPlay devices */ -static int dev_autoselect; +static int dev_autoselect; //TODO [player] Is this still necessary? static struct raop_device *dev_list; /* Device status */ @@ -1092,55 +1092,10 @@ stream_seek(struct player_source *ps, int seek_ms) } /* - * Stops playback for the given player source + * Stops playback and cleanup for the given player source */ static int stream_stop(struct player_source *ps) -{ - int ret; - - if (!ps) - { - DPRINTF(E_LOG, L_PLAYER, "Stream stop called with no active streaming player source\n"); - return -1; - } - - if (!ps->setup_done) - { - DPRINTF(E_LOG, L_PLAYER, "Given player source not setup, stop not possible\n"); - return -1; - } - - // Pause playback depending on data kind - switch (ps->data_kind) - { - case DATA_KIND_HTTP: - case DATA_KIND_FILE: - ret = 0; - break; - -#ifdef HAVE_SPOTIFY_H - case DATA_KIND_SPOTIFY: - ret = spotify_playback_stop(); - break; -#endif - - case DATA_KIND_PIPE: - ret = 0; - break; - - default: - ret = -1; - } - - return ret; -} - -/* - * Cleanup given player source - */ -static int -stream_cleanup(struct player_source *ps) { if (!ps) { @@ -1223,7 +1178,6 @@ source_stop() struct player_source *ps_temp; stream_stop(cur_streaming); - stream_cleanup(cur_streaming); ps_playing = source_now_playing(); @@ -1269,10 +1223,6 @@ source_pause(uint64_t pos) ret = stream_stop(cur_streaming); if (ret < 0) return -1; - - ret = stream_cleanup(cur_streaming); - if (ret < 0) - return -1; } else { @@ -1450,7 +1400,7 @@ source_open(struct queue_item_info *qii, uint64_t start_pos, int seek) static int source_close(uint64_t end_pos) { - stream_cleanup(cur_streaming); + stream_stop(cur_streaming); cur_streaming->end = end_pos; From cb2805800ba75f23ba99ac7b1e5218a6c112297a Mon Sep 17 00:00:00 2001 From: chme Date: Fri, 18 Sep 2015 20:01:20 +0200 Subject: [PATCH 14/23] [player] Fix segfault if pause requested in the last two seconds of the last item in the playqueue (cur_streaming is already NULL) --- src/player.c | 86 +++++++++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/src/player.c b/src/player.c index 5787123b..d8d36537 100644 --- a/src/player.c +++ b/src/player.c @@ -1122,7 +1122,7 @@ stream_stop(struct player_source *ps) case DATA_KIND_SPOTIFY: #ifdef HAVE_SPOTIFY_H - spotify_playback_stop(); //TODO [player] spotify cleanup functions? + spotify_playback_stop(); #endif break; @@ -1215,20 +1215,23 @@ source_pause(uint64_t pos) ps_playing = source_now_playing(); - if (ps_playing != cur_streaming) + if (cur_streaming) { - DPRINTF(E_DBG, L_PLAYER, - "Pause called on playing source (id=%d) and streaming source already " - "switched to the next item (id=%d)\n", ps_playing->id, cur_streaming->id); - ret = stream_stop(cur_streaming); - if (ret < 0) - return -1; - } - else - { - ret = stream_pause(cur_streaming); - if (ret < 0) - return -1; + if (ps_playing != cur_streaming) + { + DPRINTF(E_DBG, L_PLAYER, + "Pause called on playing source (id=%d) and streaming source already " + "switched to the next item (id=%d)\n", ps_playing->id, cur_streaming->id); + ret = stream_stop(cur_streaming); + if (ret < 0) + return -1; + } + else + { + ret = stream_pause(cur_streaming); + if (ret < 0) + return -1; + } } ps_playnext = ps_playing->play_next; @@ -1504,31 +1507,6 @@ source_read(uint8_t *buf, int len, uint64_t rtptime) new = 0; while (nbytes < len) { - if (new) - { - DPRINTF(E_DBG, L_PLAYER, "New file\n"); - - new = 0; - - item = queue_next(queue, cur_streaming->queueitem_id, shuffle, repeat); - if (item) - { - ret = source_open(item, cur_streaming->end + 1, 0); - if (ret < 0) - return -1; - - ret = source_play(); - if (ret < 0) - return -1; - - metadata_trigger(cur_streaming, 0); - } - else - { - cur_streaming = NULL; - } - } - if (evbuffer_get_length(audio_buf) == 0) { if (cur_streaming) @@ -1544,14 +1522,38 @@ source_read(uint8_t *buf, int len, uint64_t rtptime) free(silence_buf); ret = len - nbytes; } - + if (ret <= 0) { /* EOF or error */ - //TODO [player] distinguish between eof and error - on error remove item from queue two avoid endless loops if repeat is set source_close(rtptime + BTOS(nbytes) - 1); - new = 1; + DPRINTF(E_DBG, L_PLAYER, "New file\n"); + + item = queue_next(queue, cur_streaming->queueitem_id, shuffle, repeat); + + if (ret < 0) + { + DPRINTF(E_LOG, L_PLAYER, "Error reading source %d\n", cur_streaming->id); + queue_remove_byitemid(queue, cur_streaming->queueitem_id); + } + + if (item) + { + ret = source_open(item, cur_streaming->end + 1, 0); + if (ret < 0) + return -1; + + ret = source_play(); + if (ret < 0) + return -1; + + metadata_trigger(cur_streaming, 0); + } + else + { + cur_streaming = NULL; + } continue; } } From a06424a70123bfc6c8ec6c121fee3372fb8b213f Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 19 Sep 2015 08:23:41 +0200 Subject: [PATCH 15/23] [mpd] Expose queue-item-id instead of dbmfi-id in mpd commands [player] Reshuffle on end of playlist and repeat mode is "repeat all" --- src/mpd.c | 24 ++++++++++++------------ src/player.c | 13 +++++++------ src/player.h | 6 +++++- src/queue.c | 5 ++++- src/queue.h | 2 +- 5 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/mpd.c b/src/mpd.c index 39b23191..de336ab5 100644 --- a/src/mpd.c +++ b/src/mpd.c @@ -471,11 +471,12 @@ mpd_parse_args(char *args, int *argc, char **argv) * * @param evbuf the response event buffer * @param mfi media information + * @param item_id queue-item id * @param pos_pl position in the playqueue, if -1 the position is ignored * @return the number of bytes added if successful, or -1 if an error occurred. */ static int -mpd_add_mediainfo(struct evbuffer *evbuf, struct media_file_info *mfi, int pos_pl) +mpd_add_mediainfo(struct evbuffer *evbuf, struct media_file_info *mfi, unsigned int item_id, int pos_pl) { char modified[32]; int ret; @@ -516,10 +517,9 @@ mpd_add_mediainfo(struct evbuffer *evbuf, struct media_file_info *mfi, int pos_p "Pos: %d\n", pos_pl); - //TODO mpd does not return the persistent id of a file but instead a unique id in the current playlist ret = evbuffer_add_printf(evbuf, "Id: %d\n", - mfi->id); + item_id); } @@ -527,7 +527,7 @@ mpd_add_mediainfo(struct evbuffer *evbuf, struct media_file_info *mfi, int pos_p } static int -mpd_add_mediainfo_byid(struct evbuffer *evbuf, int id, int pos_pl) +mpd_add_mediainfo_byid(struct evbuffer *evbuf, int id, unsigned int item_id, int pos_pl) { struct media_file_info *mfi; int ret; @@ -539,7 +539,7 @@ mpd_add_mediainfo_byid(struct evbuffer *evbuf, int id, int pos_pl) return -1; } - ret = mpd_add_mediainfo(evbuf, mfi, pos_pl); + ret = mpd_add_mediainfo(evbuf, mfi, item_id, pos_pl); if (ret < 0) { DPRINTF(E_LOG, L_MPD, "Error adding media info for file with id: %d\n", id); @@ -650,7 +650,7 @@ mpd_command_currentsong(struct evbuffer *evbuf, int argc, char **argv, char **er return 0; } - ret = mpd_add_mediainfo_byid(evbuf, status.id, status.pos_pl); + ret = mpd_add_mediainfo_byid(evbuf, status.id, status.queueitem_id, status.pos_pl); if (ret < 0) { ret = asprintf(errmsg, "Error adding media info for file with id: %d", status.id); @@ -839,11 +839,11 @@ mpd_command_status(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) "nextsong: %d\n" "nextsongid: %d\n", status.pos_pl, - status.id, + status.queueitem_id, (status.pos_ms / 1000), (status.len_ms / 1000), (status.pos_ms / 1000.0), status.next_pos_pl, - status.next_id); + status.next_queueitem_id); } if (filescanner_scanning()) @@ -1792,7 +1792,7 @@ mpd_command_playlistid(struct evbuffer *evbuf, int argc, char **argv, char **err { if (songid == 0 || songid == queue->queue[i].item_id) { - ret = mpd_add_mediainfo_byid(evbuf, queue->queue[i].dbmfi_id, pos_pl); + ret = mpd_add_mediainfo_byid(evbuf, queue->queue[i].dbmfi_id, queue->queue[i].item_id, pos_pl); if (ret < 0) { ret = asprintf(errmsg, "Error adding media info for file with id: %d", queue->queue[i].dbmfi_id); @@ -1867,7 +1867,7 @@ mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **e pos_pl = queue->start_pos; for (i = 0; i < queue->count; i++) { - ret = mpd_add_mediainfo_byid(evbuf, queue->queue[i].dbmfi_id, pos_pl); + ret = mpd_add_mediainfo_byid(evbuf, queue->queue[i].dbmfi_id, queue->queue[i].item_id, pos_pl); if (ret < 0) { ret = asprintf(errmsg, "Error adding media info for file with id: %d", queue->queue[i].dbmfi_id); @@ -1914,7 +1914,7 @@ mpd_command_plchanges(struct evbuffer *evbuf, int argc, char **argv, char **errm pos_pl = queue->start_pos; for (i = 0; i < queue->count; i++) { - ret = mpd_add_mediainfo_byid(evbuf, queue->queue[i].dbmfi_id, pos_pl); + ret = mpd_add_mediainfo_byid(evbuf, queue->queue[i].dbmfi_id, queue->queue[i].item_id, pos_pl); if (ret < 0) { ret = asprintf(errmsg, "Error adding media info for file with id: %d", queue->queue[i].dbmfi_id); @@ -2691,7 +2691,7 @@ mpd_command_lsinfo(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) mfi = db_file_fetch_byvirtualpath(fi->virtual_path); if (mfi) { - ret = mpd_add_mediainfo(evbuf, mfi, -1); + ret = mpd_add_mediainfo(evbuf, mfi, 0, -1); if (ret < 0) { DPRINTF(E_LOG, L_MPD, "Could not add mediainfo for path '%s'\n", fi->virtual_path); diff --git a/src/player.c b/src/player.c index d8d36537..a56fcba4 100644 --- a/src/player.c +++ b/src/player.c @@ -1494,7 +1494,6 @@ source_check(void) static int source_read(uint8_t *buf, int len, uint64_t rtptime) { - int new; int ret; int nbytes; char *silence_buf; @@ -1504,7 +1503,6 @@ source_read(uint8_t *buf, int len, uint64_t rtptime) return 0; nbytes = 0; - new = 0; while (nbytes < len) { if (evbuffer_get_length(audio_buf) == 0) @@ -1530,7 +1528,7 @@ source_read(uint8_t *buf, int len, uint64_t rtptime) DPRINTF(E_DBG, L_PLAYER, "New file\n"); - item = queue_next(queue, cur_streaming->queueitem_id, shuffle, repeat); + item = queue_next(queue, cur_streaming->queueitem_id, shuffle, repeat, 1); if (ret < 0) { @@ -2192,6 +2190,7 @@ get_status(struct player_command *cmd) status->status = PLAY_PAUSED; status->id = cur_streaming->id; + status->queueitem_id = cur_streaming->queueitem_id; pos = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES - cur_streaming->stream_start; status->pos_ms = (pos * 1000) / 44100; @@ -2237,12 +2236,14 @@ get_status(struct player_command *cmd) status->len_ms = ps->len_ms; status->id = ps->id; + status->queueitem_id = ps->queueitem_id; status->pos_pl = queue_index_byitemid(queue, ps->queueitem_id, 0); - item_next = queue_next(queue, ps->queueitem_id, shuffle, repeat); + item_next = queue_next(queue, ps->queueitem_id, shuffle, repeat, 0); if (item_next) { status->next_id = item_next->dbmfi_id; + status->next_queueitem_id = item_next->item_id; status->next_pos_pl = queue_index_byitemid(queue, item_next->item_id, 0); } else @@ -2444,7 +2445,7 @@ playback_start_item(struct player_command *cmd, struct queue_item_info *qii) { if (shuffle) queue_shuffle(queue, 0); - item = queue_next(queue, 0, shuffle, repeat); + item = queue_next(queue, 0, shuffle, repeat, 0); } if (item) @@ -2668,7 +2669,7 @@ playback_next_bh(struct player_command *cmd) if (cur_streaming->output_start > cur_streaming->stream_start) history_add(cur_streaming->id); - item = queue_next(queue, cur_streaming->queueitem_id, shuffle, repeat); + item = queue_next(queue, cur_streaming->queueitem_id, shuffle, repeat, 0); if (!item) { playback_abort(); diff --git a/src/player.h b/src/player.h index da4776ef..2e80df7b 100644 --- a/src/player.h +++ b/src/player.h @@ -51,8 +51,10 @@ struct player_status { uint32_t plversion; /* Playlist length */ uint32_t playlistlength; - /* Playing song id*/ + /* Id of the playing file/item in the files database */ uint32_t id; + /* Item-Id of the playing file/item in the queue */ + uint32_t queueitem_id; /* Elapsed time in ms of playing item */ uint32_t pos_ms; /* Length in ms of playing item */ @@ -61,6 +63,8 @@ struct player_status { int pos_pl; /* Item id of next item in playlist */ uint32_t next_id; + /* Item-Id of the next file/item in the queue */ + uint32_t next_queueitem_id; /* Playlist position of next item */ int next_pos_pl; }; diff --git a/src/queue.c b/src/queue.c index 1c6c35b6..13053bce 100644 --- a/src/queue.c +++ b/src/queue.c @@ -392,10 +392,11 @@ queue_index_byitemid(struct queue *queue, unsigned int item_id, char shuffle) * @param item_id The id of the item in the queue * @param shuffle If 0 return the next item in the play-queue, if 1 the next item in the shuffle-queue * @param r_mode Repeat mode + * @param reshuffle If 1 and repeat mode is "repeat all" reshuffles the queue on wrap around * @return The next item */ struct queue_item_info * -queue_next(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_mode r_mode) +queue_next(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_mode r_mode, int reshuffle) { struct queue_item *item; @@ -413,6 +414,8 @@ queue_next(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_ if (item == queue->head && r_mode == REPEAT_ALL) { // Repeat all and end of queue reached, return first item in the queue + if (reshuffle) + queue_shuffle(queue, 0); item = item_next(queue->head, shuffle); } diff --git a/src/queue.h b/src/queue.h index 5c99a232..99b75500 100644 --- a/src/queue.h +++ b/src/queue.h @@ -84,7 +84,7 @@ int queue_index_byitemid(struct queue *queue, unsigned int item_id, char shuffle); struct queue_item_info * -queue_next(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_mode r_mode); +queue_next(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_mode r_mode, int reshuffle); struct queue_item_info * queue_prev(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_mode r_mode); From 64698eb3a4bfbd0fd714992b6b7fd97b271a92cc Mon Sep 17 00:00:00 2001 From: chme Date: Sun, 20 Sep 2015 07:11:39 +0200 Subject: [PATCH 16/23] [player] Some minor cleanup (error handling, comments) --- src/player.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/player.c b/src/player.c index a56fcba4..147b8639 100644 --- a/src/player.c +++ b/src/player.c @@ -1044,7 +1044,7 @@ stream_pause(struct player_source *ps) } /* - * Seeks to the given position in miliseconds of the given player source + * Seeks to the given position in milliseconds of the given player source */ static int stream_seek(struct player_source *ps, int seek_ms) @@ -1171,13 +1171,14 @@ source_new(struct queue_item_info *item) * player sources (starting from the playing source). Sets current streaming * and playing sources to NULL. */ -static int +static void source_stop() { struct player_source *ps_playing; struct player_source *ps_temp; - stream_stop(cur_streaming); + if (cur_streaming) + stream_stop(cur_streaming); ps_playing = source_now_playing(); @@ -1192,7 +1193,6 @@ source_stop() cur_playing = NULL; cur_streaming = NULL; - return 0; } /* @@ -1295,6 +1295,9 @@ source_pause(uint64_t pos) /* * Seeks the current streaming source to the given postion in milliseconds * and adjusts stream-start and output-start values. + * + * @param seek_ms Position in milliseconds to seek + * @return The new position in milliseconds or -1 on error */ static int source_seek(int seek_ms) @@ -1411,8 +1414,10 @@ source_close(uint64_t end_pos) } /* - * Updates cur_playing and notifies remotes and raop devices about - * changes. + * Updates the now playing item (cur_playing) and notifies remotes and raop devices + * about changes. Also takes care of stopping playback after the last item. + * + * @return Returns the current playback position as rtp-time */ static uint64_t source_check(void) @@ -2451,7 +2456,12 @@ playback_start_item(struct player_command *cmd, struct queue_item_info *qii) if (item) { source_stop(); - source_open(item, last_rtptime + AIRTUNES_V2_PACKET_SAMPLES, 1); + ret = source_open(item, last_rtptime + AIRTUNES_V2_PACKET_SAMPLES, 1); + if (ret < 0) + { + playback_abort(); + return -1; + } } ret = source_play(); @@ -2594,6 +2604,10 @@ playback_prev_bh(struct player_command *cmd) int pos_sec; struct queue_item_info *item; + /* + * The upper half is playback_pause, therefor the current playing item is + * already set as the cur_streaming (cur_playing is NULL). + */ if (!cur_streaming) { DPRINTF(E_LOG, L_PLAYER, "Could not get current stream source\n"); @@ -2652,13 +2666,19 @@ playback_prev_bh(struct player_command *cmd) return 0; } - +/* + * The bottom half of the next command + */ static int playback_next_bh(struct player_command *cmd) { int ret; struct queue_item_info *item; + /* + * The upper half is playback_pause, therefor the current playing item is + * already set as the cur_streaming (cur_playing is NULL). + */ if (!cur_streaming) { DPRINTF(E_LOG, L_PLAYER, "Could not get current stream source\n"); From 064dfef2de0f9d5b9880ee71e00df4ccd7ed1e0a Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 26 Sep 2015 08:45:52 +0200 Subject: [PATCH 17/23] [player] cleanup metadata_trigger metadata_trigger is always called for the current streaming item and source_open makes sure that the stream_start and output_start values are set correctly --- src/player.c | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/src/player.c b/src/player.c index 147b8639..65af77c9 100644 --- a/src/player.c +++ b/src/player.c @@ -725,31 +725,19 @@ metadata_purge(void) } static void -metadata_trigger(struct player_source *ps, int startup) +metadata_trigger(int startup) { struct player_metadata pmd; memset(&pmd, 0, sizeof(struct player_metadata)); - pmd.id = ps->id; + pmd.id = cur_streaming->id; pmd.startup = startup; - /* Determine song boundaries, dependent on context */ - - /* Restart after pause/seek */ - if (ps->stream_start) + if (cur_streaming->stream_start && cur_streaming->output_start) { - pmd.offset = ps->output_start - ps->stream_start; - pmd.rtptime = ps->stream_start; - } - else if (startup) - { - /* Will be set later, right before sending */ - } - /* Generic case */ - else if (cur_streaming && (cur_streaming->end)) - { - pmd.rtptime = cur_streaming->end + 1; + pmd.offset = cur_streaming->output_start - cur_streaming->stream_start; + pmd.rtptime = cur_streaming->stream_start; } else { @@ -783,7 +771,7 @@ metadata_check_icy(void) worker_execute(update_icy_cb, metadata, sizeof(struct http_icy_metadata), 0); /* Triggers preparing and sending RAOP metadata */ - metadata_trigger(cur_streaming, 0); + metadata_trigger(0); /* Only free the struct, the content must be preserved for update_icy_cb */ free(metadata); @@ -815,7 +803,7 @@ history_add(uint32_t id) cur_index = (history->start_index + history->count - 1) % MAX_HISTORY_COUNT; if (id == history->id[cur_index]) { - DPRINTF(E_LOG, L_PLAYER, "Current playing/streaming song already in history\n"); + DPRINTF(E_DBG, L_PLAYER, "Current playing/streaming song already in history\n"); return; } @@ -1551,7 +1539,7 @@ source_read(uint8_t *buf, int len, uint64_t rtptime) if (ret < 0) return -1; - metadata_trigger(cur_streaming, 0); + metadata_trigger(0); } else { @@ -2475,7 +2463,7 @@ playback_start_item(struct player_command *cmd, struct queue_item_info *qii) if (dbmfi_id) *dbmfi_id = cur_streaming->id; - metadata_trigger(cur_streaming, 1); + metadata_trigger(1); /* Start local audio if needed */ From 100e08da9d915826689104b41ed006c48ae5415a Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 26 Sep 2015 09:03:13 +0200 Subject: [PATCH 18/23] Rename make_queue functions --- src/httpd_dacp.c | 10 +++++----- src/mpd.c | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index f834dff7..3f6e7df2 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -822,7 +822,7 @@ find_first_song_id(const char *query) static int -make_queue_for_query(struct queue_item **head, const char *query, const char *queuefilter, const char *sort, int quirk) +make_queue_byquery(struct queue_item **head, const char *query, const char *queuefilter, const char *sort, int quirk) { struct media_file_info *mfi; struct query_params qp; @@ -994,7 +994,7 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u { sort = evhttp_find_header(query, "sort"); - ret = make_queue_for_query(&items, cuequery, NULL, sort, 0); + ret = make_queue_byquery(&items, cuequery, NULL, sort, 0); if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Could not build song queue\n"); @@ -1060,7 +1060,7 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u /* If playing from history queue, the pos holds the id of the item to play */ if (hist) - ret = player_playback_start_byitemid(id, &id); + ret = player_playback_start_byitemid(id, &id); //TODO [queue/history] id does not hold the queueitemid but the dbmfiid else ret = player_playback_start_bypos(pos, &id); @@ -1730,7 +1730,7 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf, if (!querymodifier || (strcmp(querymodifier, "containers") != 0)) { quirkyquery = (mode == 1) && strstr(editquery, "dmap.itemid:") && ((!queuefilter) || strstr(queuefilter, "(null)")); - ret = make_queue_for_query(&items, editquery, queuefilter, sort, quirkyquery); + ret = make_queue_byquery(&items, editquery, queuefilter, sort, quirkyquery); } else { @@ -1745,7 +1745,7 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf, } snprintf(modifiedquery, sizeof(modifiedquery), "playlist:%d", plid); - ret = make_queue_for_query(&items, NULL, modifiedquery, sort, 0); + ret = make_queue_byquery(&items, NULL, modifiedquery, sort, 0); } if (ret < 0) diff --git a/src/mpd.c b/src/mpd.c index de336ab5..9456b28b 100644 --- a/src/mpd.c +++ b/src/mpd.c @@ -1508,7 +1508,7 @@ mpd_command_stop(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) } static struct queue_item * -make_queue_for_path(char *path, int recursive) +make_queue_bypath(char *path, int recursive) { struct query_params qp; struct queue_item *items; @@ -1557,7 +1557,7 @@ mpd_command_add(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) return ACK_ERROR_ARG; } - items = make_queue_for_path(argv[1], 1); + items = make_queue_bypath(argv[1], 1); if (!items) { @@ -1604,7 +1604,7 @@ mpd_command_addid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) DPRINTF(E_LOG, L_MPD, "Adding at a specified position not supported for 'addid', adding songs at end of queue.\n"); } - items = make_queue_for_path(argv[1], 0); + items = make_queue_bypath(argv[1], 0); if (!items) { From 35df9455b9f6652d1cc043e32b695fcf114626eb Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 26 Sep 2015 09:33:48 +0200 Subject: [PATCH 19/23] [queue] Fix memory leak in queue_make_item --- src/queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/queue.c b/src/queue.c index 13053bce..453393d9 100644 --- a/src/queue.c +++ b/src/queue.c @@ -1122,7 +1122,7 @@ queue_make_item(uint32_t dbmfi_id) qp.limit = 0; qp.sort = S_NONE; snprintf(buf, sizeof(buf), "f.id = %" PRIu32, dbmfi_id); - qp.filter = strdup(buf); + qp.filter = buf; item = queue_make(&qp); From ba3385b0ec82ac3968126102e85dd8c4674c41ce Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 3 Oct 2015 08:24:13 +0200 Subject: [PATCH 20/23] [queue] Remove queue_info and queue_item_info structs --- src/httpd_dacp.c | 28 ++++--- src/mpd.c | 69 +++++++++------- src/player.c | 60 +++++++------- src/player.h | 4 +- src/queue.c | 200 +++++++++++++++++++++++++++++------------------ src/queue.h | 80 +++++++------------ 6 files changed, 240 insertions(+), 201 deletions(-) diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index 3f6e7df2..c7410db7 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -822,7 +822,7 @@ find_first_song_id(const char *query) static int -make_queue_byquery(struct queue_item **head, const char *query, const char *queuefilter, const char *sort, int quirk) +dacp_queueitem_make(struct queue_item **head, const char *query, const char *queuefilter, const char *sort, int quirk) { struct media_file_info *mfi; struct query_params qp; @@ -942,7 +942,7 @@ make_queue_byquery(struct queue_item **head, const char *query, const char *queu qp.sort = S_ARTIST; } - items = queue_make(&qp); + items = queueitem_make_byquery(&qp); if (qp.filter) free(qp.filter); @@ -994,7 +994,7 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u { sort = evhttp_find_header(query, "sort"); - ret = make_queue_byquery(&items, cuequery, NULL, sort, 0); + ret = dacp_queueitem_make(&items, cuequery, NULL, sort, 0); if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Could not build song queue\n"); @@ -1215,9 +1215,9 @@ dacp_reply_playspec(struct evhttp_request *req, struct evbuffer *evbuf, char **u items = NULL; if (plid > 0) - items = queue_make_pl(plid); + items = queueitem_make_byplid(plid); else if (pos > 0) - items = queue_make_item(pos); + items = queueitem_make_byid(pos); if (!items) { @@ -1474,11 +1474,13 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf, struct evbuffer *playlists; struct player_status status; struct player_history *history; - struct queue_info *queue; + struct queue *queue; + struct queue_item *item; const char *param; size_t songlist_length; size_t playlist_length; int span; + int count; int i; int n; int ret; @@ -1549,10 +1551,12 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf, queue = player_queue_get_bypos(abs(span)); if (queue) { - i = queue->start_pos; - for (n = 0; (n < queue->count) && (n < abs(span)); n++) + i = 0; + count = queue_count(queue); + for (n = 0; (n < count) && (n < abs(span)); n++) { - ret = playqueuecontents_add_source(songlist, queue->queue[n].dbmfi_id, (n + i + 1), status.plid); + item = queue_get_byindex(queue, n, 0); + ret = playqueuecontents_add_source(songlist, queueitem_id(item), (n + i + 1), status.plid); if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Could not add song to songlist for playqueue-contents\n"); @@ -1561,7 +1565,7 @@ dacp_reply_playqueuecontents(struct evhttp_request *req, struct evbuffer *evbuf, return; } } - queue_info_free(queue); + queue_free(queue); } } } @@ -1730,7 +1734,7 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf, if (!querymodifier || (strcmp(querymodifier, "containers") != 0)) { quirkyquery = (mode == 1) && strstr(editquery, "dmap.itemid:") && ((!queuefilter) || strstr(queuefilter, "(null)")); - ret = make_queue_byquery(&items, editquery, queuefilter, sort, quirkyquery); + ret = dacp_queueitem_make(&items, editquery, queuefilter, sort, quirkyquery); } else { @@ -1745,7 +1749,7 @@ dacp_reply_playqueueedit_add(struct evhttp_request *req, struct evbuffer *evbuf, } snprintf(modifiedquery, sizeof(modifiedquery), "playlist:%d", plid); - ret = make_queue_byquery(&items, NULL, modifiedquery, sort, 0); + ret = dacp_queueitem_make(&items, NULL, modifiedquery, sort, 0); } if (ret < 0) diff --git a/src/mpd.c b/src/mpd.c index 9456b28b..9ce8d86a 100644 --- a/src/mpd.c +++ b/src/mpd.c @@ -1508,7 +1508,7 @@ mpd_command_stop(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) } static struct queue_item * -make_queue_bypath(char *path, int recursive) +mpd_queueitem_make(char *path, int recursive) { struct query_params qp; struct queue_item *items; @@ -1532,7 +1532,7 @@ make_queue_bypath(char *path, int recursive) DPRINTF(E_DBG, L_PLAYER, "Out of memory\n"); } - items = queue_make(&qp); + items = queueitem_make_byquery(&qp); sqlite3_free(qp.filter); return items; @@ -1557,7 +1557,7 @@ mpd_command_add(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) return ACK_ERROR_ARG; } - items = make_queue_bypath(argv[1], 1); + items = mpd_queueitem_make(argv[1], 1); if (!items) { @@ -1604,7 +1604,7 @@ mpd_command_addid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) DPRINTF(E_LOG, L_MPD, "Adding at a specified position not supported for 'addid', adding songs at end of queue.\n"); } - items = make_queue_bypath(argv[1], 0); + items = mpd_queueitem_make(argv[1], 0); if (!items) { @@ -1758,9 +1758,11 @@ mpd_command_deleteid(struct evbuffer *evbuf, int argc, char **argv, char **errms static int mpd_command_playlistid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) { - struct queue_info *queue; + struct queue *queue; + struct queue_item *item; uint32_t songid; int pos_pl; + int count; int i; int ret; @@ -1787,17 +1789,19 @@ mpd_command_playlistid(struct evbuffer *evbuf, int argc, char **argv, char **err return 0; } - pos_pl = queue->start_pos; - for (i = 0; i < queue->count; i++) + pos_pl = 0; + count = queue_count(queue); + for (i = 0; i < count; i++) { - if (songid == 0 || songid == queue->queue[i].item_id) + item = queue_get_byindex(queue, i, 0); + if (songid == 0 || songid == queueitem_item_id(item)) { - ret = mpd_add_mediainfo_byid(evbuf, queue->queue[i].dbmfi_id, queue->queue[i].item_id, pos_pl); + ret = mpd_add_mediainfo_byid(evbuf, queueitem_id(item), queueitem_item_id(item), pos_pl); if (ret < 0) { - ret = asprintf(errmsg, "Error adding media info for file with id: %d", queue->queue[i].dbmfi_id); + ret = asprintf(errmsg, "Error adding media info for file with id: %d", queueitem_id(item)); - queue_info_free(queue); + queue_free(queue); if (ret < 0) DPRINTF(E_LOG, L_MPD, "Out of memory\n"); @@ -1808,7 +1812,7 @@ mpd_command_playlistid(struct evbuffer *evbuf, int argc, char **argv, char **err pos_pl++; } - queue_info_free(queue); + queue_free(queue); return 0; } @@ -1824,7 +1828,8 @@ mpd_command_playlistid(struct evbuffer *evbuf, int argc, char **argv, char **err static int mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) { - struct queue_info *queue; + struct queue *queue; + struct queue_item *item; int start_pos; int end_pos; int count; @@ -1864,15 +1869,17 @@ mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **e return 0; } - pos_pl = queue->start_pos; - for (i = 0; i < queue->count; i++) + pos_pl = start_pos; + count = queue_count(queue); + for (i = 0; i < count; i++) { - ret = mpd_add_mediainfo_byid(evbuf, queue->queue[i].dbmfi_id, queue->queue[i].item_id, pos_pl); + item = queue_get_byindex(queue, i, 0); + ret = mpd_add_mediainfo_byid(evbuf, queueitem_id(item), queueitem_item_id(item), pos_pl); if (ret < 0) { - ret = asprintf(errmsg, "Error adding media info for file with id: %d", queue->queue[i].dbmfi_id); + ret = asprintf(errmsg, "Error adding media info for file with id: %d", queueitem_id(item)); - queue_info_free(queue); + queue_free(queue); if (ret < 0) DPRINTF(E_LOG, L_MPD, "Out of memory\n"); @@ -1882,7 +1889,7 @@ mpd_command_playlistinfo(struct evbuffer *evbuf, int argc, char **argv, char **e pos_pl++; } - queue_info_free(queue); + queue_free(queue); return 0; } @@ -1894,8 +1901,10 @@ 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) { - struct queue_info *queue; + struct queue *queue; + struct queue_item *item; int pos_pl; + int count; int i; int ret; @@ -1911,15 +1920,17 @@ mpd_command_plchanges(struct evbuffer *evbuf, int argc, char **argv, char **errm return 0; } - pos_pl = queue->start_pos; - for (i = 0; i < queue->count; i++) + pos_pl = 0; + count = queue_count(queue); + for (i = 0; i < count; i++) { - ret = mpd_add_mediainfo_byid(evbuf, queue->queue[i].dbmfi_id, queue->queue[i].item_id, pos_pl); + item = queue_get_byindex(queue, i, 0); + ret = mpd_add_mediainfo_byid(evbuf, queueitem_id(item), queueitem_item_id(item), pos_pl); if (ret < 0) { - ret = asprintf(errmsg, "Error adding media info for file with id: %d", queue->queue[i].dbmfi_id); + ret = asprintf(errmsg, "Error adding media info for file with id: %d", queueitem_id(item)); - queue_info_free(queue); + queue_free(queue); if (ret < 0) DPRINTF(E_LOG, L_MPD, "Out of memory\n"); @@ -1929,7 +1940,7 @@ mpd_command_plchanges(struct evbuffer *evbuf, int argc, char **argv, char **errm pos_pl++; } - queue_info_free(queue); + queue_free(queue); return 0; } @@ -2174,7 +2185,7 @@ mpd_command_load(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) //TODO If a second parameter is given only add the specified range of songs to the playqueue - items = queue_make_pl(pli->id); + items = queueitem_make_byplid(pli->id); if (!items) { @@ -2442,7 +2453,7 @@ mpd_command_findadd(struct evbuffer *evbuf, int argc, char **argv, char **errmsg mpd_get_query_params_find(argc - 1, argv + 1, &qp); - items = queue_make(&qp); + items = queueitem_make_byquery(&qp); if (!items) { @@ -2916,7 +2927,7 @@ mpd_command_searchadd(struct evbuffer *evbuf, int argc, char **argv, char **errm mpd_get_query_params_search(argc - 1, argv + 1, &qp); - items = queue_make(&qp); + items = queueitem_make_byquery(&qp); if (!items) { diff --git a/src/player.c b/src/player.c index 65af77c9..f3fd563f 100644 --- a/src/player.c +++ b/src/player.c @@ -155,7 +155,7 @@ struct playerqueue_get_param int pos; int count; - struct queue_info *queue; + struct queue *queue; }; struct playerqueue_add_param @@ -1138,17 +1138,17 @@ source_now_playing() * Creates a new player source for the given queue item */ static struct player_source * -source_new(struct queue_item_info *item) +source_new(struct queue_item *item) { struct player_source *ps; ps = (struct player_source *)calloc(1, sizeof(struct player_source)); - ps->id = item->dbmfi_id; - ps->queueitem_id = item->item_id; - ps->data_kind = item->data_kind; - ps->media_kind = item->media_kind; - ps->len_ms = item->len_ms; + ps->id = queueitem_id(item); + ps->queueitem_id = queueitem_item_id(item); + ps->data_kind = queueitem_data_kind(item); + ps->media_kind = queueitem_media_kind(item); + ps->len_ms = queueitem_len(item); ps->play_next = NULL; return ps; @@ -1326,10 +1326,11 @@ source_play() * Stream-start and output-start values are set to the given start position. */ static int -source_open(struct queue_item_info *qii, uint64_t start_pos, int seek) +source_open(struct queue_item *qii, uint64_t start_pos, int seek) { struct player_source *ps; struct media_file_info *mfi; + uint32_t id; int ret; if (cur_streaming && cur_streaming->end == 0) @@ -1338,17 +1339,18 @@ source_open(struct queue_item_info *qii, uint64_t start_pos, int seek) return -1; } - mfi = db_file_fetch_byid(qii->dbmfi_id); + id = queueitem_id(qii); + mfi = db_file_fetch_byid(id); if (!mfi) { - DPRINTF(E_LOG, L_PLAYER, "Couldn't fetch file id %d\n", qii->dbmfi_id); + DPRINTF(E_LOG, L_PLAYER, "Couldn't fetch file id %d\n", id); return -1; } if (mfi->disabled) { - DPRINTF(E_DBG, L_PLAYER, "File id %d is disabled, skipping\n", qii->dbmfi_id); + DPRINTF(E_DBG, L_PLAYER, "File id %d is disabled, skipping\n", id); free_mfi(mfi, 0); return -1; @@ -1490,7 +1492,7 @@ source_read(uint8_t *buf, int len, uint64_t rtptime) int ret; int nbytes; char *silence_buf; - struct queue_item_info *item; + struct queue_item *item; if (!cur_streaming) return 0; @@ -2154,7 +2156,7 @@ get_status(struct player_command *cmd) struct timespec ts; struct player_source *ps; struct player_status *status; - struct queue_item_info *item_next; + struct queue_item *item_next; uint64_t pos; int ret; @@ -2235,9 +2237,9 @@ get_status(struct player_command *cmd) item_next = queue_next(queue, ps->queueitem_id, shuffle, repeat, 0); if (item_next) { - status->next_id = item_next->dbmfi_id; - status->next_queueitem_id = item_next->item_id; - status->next_pos_pl = queue_index_byitemid(queue, item_next->item_id, 0); + status->next_id = queueitem_id(item_next); + status->next_queueitem_id = queueitem_item_id(item_next); + status->next_pos_pl = queue_index_byitemid(queue, status->next_queueitem_id, 0); } else { @@ -2397,12 +2399,12 @@ playback_start_bh(struct player_command *cmd) } static int -playback_start_item(struct player_command *cmd, struct queue_item_info *qii) +playback_start_item(struct player_command *cmd, struct queue_item *qii) { uint32_t *dbmfi_id; struct raop_device *rd; struct player_source *ps_playing; - struct queue_item_info *item; + struct queue_item *item; int ret; dbmfi_id = cmd->arg.playback_start_param.id_ptr; @@ -2540,7 +2542,7 @@ static int playback_start_byitemid(struct player_command *cmd) { int item_id; - struct queue_item_info *qii; + struct queue_item *qii; item_id = cmd->arg.playback_start_param.id; @@ -2553,7 +2555,7 @@ static int playback_start_byindex(struct player_command *cmd) { int pos; - struct queue_item_info *qii; + struct queue_item *qii; pos = cmd->arg.playback_start_param.pos; @@ -2567,7 +2569,7 @@ playback_start_bypos(struct player_command *cmd) { int offset; struct player_source *ps_playing; - struct queue_item_info *qii; + struct queue_item *qii; offset = cmd->arg.playback_start_param.pos; @@ -2590,7 +2592,7 @@ playback_prev_bh(struct player_command *cmd) { int ret; int pos_sec; - struct queue_item_info *item; + struct queue_item *item; /* * The upper half is playback_pause, therefor the current playing item is @@ -2661,7 +2663,7 @@ static int playback_next_bh(struct player_command *cmd) { int ret; - struct queue_item_info *item; + struct queue_item *item; /* * The upper half is playback_pause, therefor the current playing item is @@ -3301,7 +3303,7 @@ static int playerqueue_get_bypos(struct player_command *cmd) { int count; - struct queue_info *qi; + struct queue *qi; struct player_source *ps; int item_id; @@ -3315,7 +3317,7 @@ playerqueue_get_bypos(struct player_command *cmd) item_id = ps->queueitem_id; } - qi = queue_info_new_bypos(queue, item_id, count, shuffle); + qi = queue_new_bypos(queue, item_id, count, shuffle); cmd->arg.queue_get_param.queue = qi; @@ -3327,12 +3329,12 @@ playerqueue_get_byindex(struct player_command *cmd) { int pos; int count; - struct queue_info *qi; + struct queue *qi; pos = cmd->arg.queue_get_param.pos; count = cmd->arg.queue_get_param.count; - qi = queue_info_new_byindex(queue, pos, count, 0); + qi = queue_new_byindex(queue, pos, count, 0); cmd->arg.queue_get_param.queue = qi; return 0; @@ -4053,7 +4055,7 @@ player_shuffle_set(int enable) * @param count max number of media items to return * @return queue info */ -struct queue_info * +struct queue * player_queue_get_bypos(int count) { struct player_command cmd; @@ -4085,7 +4087,7 @@ player_queue_get_bypos(int count) * @param count max number of media items to return * @return queue info */ -struct queue_info * +struct queue * player_queue_get_byindex(int index, int count) { struct player_command cmd; diff --git a/src/player.h b/src/player.h index 2e80df7b..76cc6696 100644 --- a/src/player.h +++ b/src/player.h @@ -161,10 +161,10 @@ int player_shuffle_set(int enable); -struct queue_info * +struct queue * player_queue_get_bypos(int count); -struct queue_info * +struct queue * player_queue_get_byindex(int pos, int count); int diff --git a/src/queue.c b/src/queue.c index 453393d9..44a5941e 100644 --- a/src/queue.c +++ b/src/queue.c @@ -31,12 +31,24 @@ /* * Internal representation of an item in a queue. It links to the previous and the next item - * in the queue for shuffle on/off. Only the queue_item_info can be exposed. + * in the queue for shuffle on/off. To access the properties use the queueitem_* functions. */ struct queue_item { - /* Identifies the item in the db and the queue */ - struct queue_item_info qii; + /* Item-Id is a unique id for this queue item. If the same item appears multiple + times in the queue each corresponding queue item has its own id. */ + unsigned int item_id; + + /* Id of the file/item in the files database */ + uint32_t id; + + /* Length of the item in ms */ + unsigned int len_ms; + + /* Data type of the item */ + enum data_kind data_kind; + /* Media type of the item */ + enum media_kind media_kind; /* Link to the previous/next item in the queue */ struct queue_item *next; @@ -183,29 +195,74 @@ item_prev(struct queue_item *item, char shuffle) * If no item is found for the given id, it returns -1. */ int -queueitem_pos(struct queue_item *item, uint32_t dbmfi_id) +queueitem_pos(struct queue_item *item, uint32_t id) { struct queue_item *temp; int pos; - if (dbmfi_id == 0 || item->qii.dbmfi_id == dbmfi_id) + if (id == 0 || item->id == id) return 0; pos = 1; - for (temp = item->next; (temp != item) && temp->qii.dbmfi_id != dbmfi_id; temp = temp->next) + for (temp = item->next; (temp != item) && temp->id != id; temp = temp->next) { pos++; } if (temp == item) { - // Item with given dbmfi_id does not exists + // Item with given (database) id does not exists return -1; } return pos; } +/* + * Returns the id of the item/file in the files database table + */ +uint32_t +queueitem_id(struct queue_item *item) +{ + return item->id; +} + +/* + * Returns the queue-item-id + */ +unsigned int +queueitem_item_id(struct queue_item *item) +{ + return item->item_id; +} + +/* + * Returns the length of the item in milliseconds + */ +unsigned int +queueitem_len(struct queue_item *item) +{ + return item->len_ms; +} + +/* + * Returns the data-kind + */ +enum data_kind +queueitem_data_kind(struct queue_item *item) +{ + return item->data_kind; +} + +/* + * Returns the media-kind + */ +enum media_kind +queueitem_media_kind(struct queue_item *item) +{ + return item->media_kind; +} + /* * Returns the item with the given item_id in the queue * @@ -218,7 +275,7 @@ queueitem_get_byitemid(struct queue *queue, int item_id) { struct queue_item *item; - for (item = queue->head->next; item != queue->head && item->qii.item_id != item_id; item = item->next) + for (item = queue->head->next; item != queue->head && item->item_id != item_id; item = item->next) { // Iterate through the queue until the item with item_id is found } @@ -297,7 +354,7 @@ queueitem_get_bypos(struct queue *queue, unsigned int item_id, unsigned int pos, * @param item_id The unique id of the item in the queue * @return Item with the given item_id or NULL if not found */ -struct queue_item_info * +struct queue_item * queue_get_byitemid(struct queue *queue, unsigned int item_id) { struct queue_item *item; @@ -307,7 +364,7 @@ queue_get_byitemid(struct queue *queue, unsigned int item_id) if (!item) return NULL; - return &item->qii; + return item; } /* @@ -318,7 +375,7 @@ queue_get_byitemid(struct queue *queue, unsigned int item_id) * @param shuffle Play queue (shuffle = 0) or shuffle queue (shuffle = 1) * @return Item at index in the queue or NULL if not found */ -struct queue_item_info * +struct queue_item * queue_get_byindex(struct queue *queue, unsigned int index, char shuffle) { struct queue_item *item; @@ -328,7 +385,7 @@ queue_get_byindex(struct queue *queue, unsigned int index, char shuffle) if (!item) return NULL; - return &item->qii; + return item; } /* @@ -343,7 +400,7 @@ queue_get_byindex(struct queue *queue, unsigned int index, char shuffle) * @param shuffle If 0 the position in the play-queue, 1 the position in the shuffle-queue * @return Item at position in the queue or NULL if not found */ -struct queue_item_info * +struct queue_item * queue_get_bypos(struct queue *queue, unsigned int item_id, unsigned int pos, char shuffle) { struct queue_item *item; @@ -353,7 +410,7 @@ queue_get_bypos(struct queue *queue, unsigned int item_id, unsigned int pos, cha if (!item) return NULL; - return &item->qii; + return item; } /* @@ -373,7 +430,7 @@ queue_index_byitemid(struct queue *queue, unsigned int item_id, char shuffle) int pos; pos = 0; - for (item = item_next(queue->head, shuffle); item != queue->head && item->qii.item_id != item_id; item = item_next(item, shuffle)) + for (item = item_next(queue->head, shuffle); item != queue->head && item->item_id != item_id; item = item_next(item, shuffle)) { pos++; } @@ -395,7 +452,7 @@ queue_index_byitemid(struct queue *queue, unsigned int item_id, char shuffle) * @param reshuffle If 1 and repeat mode is "repeat all" reshuffles the queue on wrap around * @return The next item */ -struct queue_item_info * +struct queue_item * queue_next(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_mode r_mode, int reshuffle) { struct queue_item *item; @@ -407,7 +464,7 @@ queue_next(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_ return NULL; if (r_mode == REPEAT_SONG && item != queue->head) - return &item->qii; + return item; item = item_next(item, shuffle); @@ -422,7 +479,7 @@ queue_next(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_ if (item == queue->head) return NULL; - return &item->qii; + return item; } /* @@ -434,7 +491,7 @@ queue_next(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_ * @param r_mode Repeat mode * @return The previous item */ -struct queue_item_info * +struct queue_item * queue_prev(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_mode r_mode) { struct queue_item *item; @@ -446,7 +503,7 @@ queue_prev(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_ return NULL; if (r_mode == REPEAT_SONG && item != queue->head) - return &item->qii; + return item; item = item_prev(item, shuffle); @@ -459,11 +516,11 @@ queue_prev(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_ if (item == queue->head) return NULL; - return &item->qii; + return item; } /* - * Creates a new queue-info for the given queue. + * Creates a new queue with a copy of the items of the given queue. * * The given number of items (count) are copied from the play-queue (shuffle = 0) or shuffle-queue (shuffle = 1) * starting with the item at the given index (0-based). @@ -474,19 +531,20 @@ queue_prev(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_ * @param index Index of the first item in the queue * @param count Maximum number of items to copy (if 0 all remaining items after index) * @param shuffle If 0 the play-queue, if 1 the shuffle queue - * @return A new queue-info with the specified items + * @return A new queue with the specified items */ -struct queue_info * -queue_info_new_byindex(struct queue *queue, unsigned int index, unsigned int count, char shuffle) +struct queue * +queue_new_byindex(struct queue *queue, unsigned int index, unsigned int count, char shuffle) { - struct queue_info *qi; - struct queue_item_info *qii; - struct queue_item *item_base; + struct queue *qi; + struct queue_item *qii; struct queue_item *item; - unsigned int i; + int i; unsigned int qlength; int qii_size; + qi = queue_new(); + qlength = queue_count(queue); qii_size = qlength - index; @@ -495,39 +553,37 @@ queue_info_new_byindex(struct queue *queue, unsigned int index, unsigned int cou if (qii_size <= 0) { - return NULL; + return qi; } - item_base = queueitem_get_byindex(queue, index, shuffle); + item = queueitem_get_byindex(queue, index, shuffle); - if (!item_base) + if (!item) return NULL; - qi = malloc(sizeof(struct queue_info)); - qii = malloc(qii_size * sizeof(struct queue_item_info)); - i = 0; - for (item = item_base; item != queue->head && i < qii_size; item = item_next(item, shuffle)) + for (; item != queue->head && i < qii_size; item = item_next(item, shuffle)) { - qii[i].dbmfi_id = item->qii.dbmfi_id; - qii[i].item_id = item->qii.item_id; - qii[i].len_ms = item->qii.len_ms; - qii[i].data_kind = item->qii.data_kind; - qii[i].media_kind = item->qii.media_kind; + qii = malloc(sizeof(struct queue_item)); + qii->id = item->id; + qii->item_id = item->item_id; + qii->len_ms = item->len_ms; + qii->data_kind = item->data_kind; + qii->media_kind = item->media_kind; + qii->next = qii; + qii->prev = qii; + qii->shuffle_next = qii; + qii->shuffle_prev = qii; + queue_add(qi, qii); i++; } - qi->count = i; - qi->length = qlength; - qi->start_pos = index; - qi->queue = qii; - return qi; } /* - * Creates a new queue-info for the given queue. + * Creates a new queue with a copy of the items of the given queue. * * The given number of items (count) are copied from the play-queue (shuffle = 0) or shuffle-queue (shuffle = 1) * starting after the item with the given item_id. The item with item_id is excluded, therefor the first item @@ -539,13 +595,13 @@ queue_info_new_byindex(struct queue *queue, unsigned int index, unsigned int cou * @param item_id The unique id of the item in the queue * @param count Maximum number of items to copy (if 0 all remaining items after index) * @param shuffle If 0 the play-queue, if 1 the shuffle queue - * @return A new queue-info with the specified items + * @return A new queue with the specified items */ -struct queue_info * -queue_info_new_bypos(struct queue *queue, unsigned int item_id, unsigned int count, char shuffle) +struct queue * +queue_new_bypos(struct queue *queue, unsigned int item_id, unsigned int count, char shuffle) { int pos; - struct queue_info *qi; + struct queue *qi; pos = queue_index_byitemid(queue, item_id, shuffle); @@ -554,21 +610,11 @@ queue_info_new_bypos(struct queue *queue, unsigned int item_id, unsigned int cou else pos = pos + 1; // exclude the item with the given item-id - qi = queue_info_new_byindex(queue, pos, count, shuffle); + qi = queue_new_byindex(queue, pos, count, shuffle); return qi; } -/* - * Frees the queue info - */ -void -queue_info_free(struct queue_info *qi) -{ - free(qi->queue); - free(qi); -} - /* * Adds items to the queue after the given item * @@ -598,11 +644,11 @@ queue_add_afteritem(struct queue *queue, struct queue_item *item_new, struct que // Set item-id for all new items queue->last_inserted_item_id++; - item_new->qii.item_id = queue->last_inserted_item_id; + item_new->item_id = queue->last_inserted_item_id; for (item = item_new->next; item != item_new; item = item->next) { queue->last_inserted_item_id++; - item->qii.item_id = queue->last_inserted_item_id; + item->item_id = queue->last_inserted_item_id; } // Add items into the queue @@ -872,7 +918,7 @@ queue_reset_and_find(struct queue *queue, unsigned int item_id) temp->shuffle_next = temp->next; temp->shuffle_prev = temp->prev; - if (temp->qii.item_id == item_id) + if (temp->item_id == item_id) item = temp; } @@ -1006,16 +1052,16 @@ queue_item_new(struct db_media_file_info *dbmfi) return NULL; } - item->qii.dbmfi_id = id; - item->qii.len_ms = len_ms; - item->qii.data_kind = data_kind; - item->qii.media_kind = media_kind; + item->id = id; + item->len_ms = len_ms; + item->data_kind = data_kind; + item->media_kind = media_kind; return item; } struct queue_item * -queue_make(struct query_params *qp) +queueitem_make_byquery(struct query_params *qp) { struct db_media_file_info dbmfi; struct queue_item *item_head; @@ -1082,7 +1128,7 @@ queue_make(struct query_params *qp) * @return List of items for all playlist items */ struct queue_item * -queue_make_pl(int plid) +queueitem_make_byplid(int plid) { struct query_params qp; struct queue_item *item; @@ -1096,19 +1142,19 @@ queue_make_pl(int plid) qp.sort = S_NONE; qp.idx_type = I_NONE; - item = queue_make(&qp); + item = queueitem_make_byquery(&qp); return item; } /* - * Makes a queue-item for the item/file with the given id (dbmfi_id) + * Makes a queue-item for the item/file with the given id * - * @param dbmfi_id Id of the item/file + * @param id Id of the item/file in the db * @return List of items containing only the item with the given id */ struct queue_item * -queue_make_item(uint32_t dbmfi_id) +queueitem_make_byid(uint32_t id) { struct query_params qp; struct queue_item *item; @@ -1121,10 +1167,10 @@ queue_make_item(uint32_t dbmfi_id) qp.offset = 0; qp.limit = 0; qp.sort = S_NONE; - snprintf(buf, sizeof(buf), "f.id = %" PRIu32, dbmfi_id); + snprintf(buf, sizeof(buf), "f.id = %" PRIu32, id); qp.filter = buf; - item = queue_make(&qp); + item = queueitem_make_byquery(&qp); return item; } diff --git a/src/queue.h b/src/queue.h index 99b75500..d5b0aacc 100644 --- a/src/queue.h +++ b/src/queue.h @@ -22,42 +22,6 @@ struct queue; */ struct queue_item; -/* - * External representation of an item in a queue - */ -struct queue_item_info -{ - /* Item-Id is a unique id for this queue item. If the same item appears multiple - times in the queue each corresponding queue item has its own id. */ - unsigned int item_id; - - /* Id of the file/item in the files database */ - unsigned int dbmfi_id; - - /* Length of the item in ms */ - unsigned int len_ms; - - /* Data type of the item */ - enum data_kind data_kind; - /* Media type of the item */ - enum media_kind media_kind; -}; - -/* - * External representation of a queue - */ -struct queue_info -{ - // The number of items in the queue - unsigned int length; - - // The position (0-based) in the queue for the first item in the queue array - unsigned int start_pos; - // The number of items in the queue array - unsigned int count; - // The queue array (array of items infos) - struct queue_item_info *queue; -}; struct queue * queue_new(); @@ -69,34 +33,46 @@ unsigned int queue_count(struct queue *queue); int -queueitem_pos(struct queue_item *item, uint32_t dbmfi_id); +queueitem_pos(struct queue_item *item, uint32_t id); -struct queue_item_info * +uint32_t +queueitem_id(struct queue_item *item); + +unsigned int +queueitem_item_id(struct queue_item *item); + +unsigned int +queueitem_len(struct queue_item *item); + +enum data_kind +queueitem_data_kind(struct queue_item *item); + +enum media_kind +queueitem_media_kind(struct queue_item *item); + +struct queue_item * queue_get_byitemid(struct queue *queue, unsigned int item_id); -struct queue_item_info * +struct queue_item * queue_get_byindex(struct queue *queue, unsigned int index, char shuffle); -struct queue_item_info * +struct queue_item * queue_get_bypos(struct queue *queue, unsigned int item_id, unsigned int pos, char shuffle); int queue_index_byitemid(struct queue *queue, unsigned int item_id, char shuffle); -struct queue_item_info * +struct queue_item * queue_next(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_mode r_mode, int reshuffle); -struct queue_item_info * +struct queue_item * queue_prev(struct queue *queue, unsigned int item_id, char shuffle, enum repeat_mode r_mode); -struct queue_info * -queue_info_new_byindex(struct queue *queue, unsigned int index, unsigned int count, char shuffle); +struct queue * +queue_new_byindex(struct queue *queue, unsigned int index, unsigned int count, char shuffle); -struct queue_info * -queue_info_new_bypos(struct queue *queue, unsigned int item_id, unsigned int count, char shuffle); - -void -queue_info_free(struct queue_info *qi); +struct queue * +queue_new_bypos(struct queue *queue, unsigned int item_id, unsigned int count, char shuffle); void queue_add(struct queue *queue, struct queue_item *item); @@ -123,12 +99,12 @@ void queue_shuffle(struct queue *queue, unsigned int item_id); struct queue_item * -queue_make(struct query_params *qp); +queueitem_make_byquery(struct query_params *qp); struct queue_item * -queue_make_pl(int plid); +queueitem_make_byplid(int plid); struct queue_item * -queue_make_item(uint32_t dbmfi_id); +queueitem_make_byid(uint32_t id); #endif /* SRC_QUEUE_H_ */ From fbce3a7d577c437c2eac0f9fb633882dd9bb085b Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 3 Oct 2015 08:49:04 +0200 Subject: [PATCH 21/23] [player] Confusing naming of ids cleaned up --- src/player.c | 28 ++++++++++++++-------------- src/player.h | 8 ++++---- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/player.c b/src/player.c index f3fd563f..207d4f26 100644 --- a/src/player.c +++ b/src/player.c @@ -3702,11 +3702,11 @@ player_get_icy_artwork_url(uint32_t id) * * If a pointer is given as argument "itemid", its value will be set to the playing item dbmfi-id. * - * @param *itemid if not NULL, will be set to the playing item dbmfi-id + * @param *id if not NULL, will be set to the playing item dbmfi-id * @return 0 if successful, -1 if an error occurred */ int -player_playback_start(uint32_t *itemid) +player_playback_start(uint32_t *id) { struct player_command cmd; int ret; @@ -3715,7 +3715,7 @@ player_playback_start(uint32_t *itemid) cmd.func = playback_start; cmd.func_bh = playback_start_bh; - cmd.arg.playback_start_param.id_ptr = itemid; + cmd.arg.playback_start_param.id_ptr = id; ret = sync_command(&cmd); @@ -3732,11 +3732,11 @@ player_playback_start(uint32_t *itemid) * If a pointer is given as argument "itemid", its value will be set to the playing item id. * * @param index the index of the item in the play-queue - * @param *itemid if not NULL, will be set to the playing item id + * @param *id if not NULL, will be set to the playing item id * @return 0 if successful, -1 if an error occurred */ int -player_playback_start_byindex(int index, uint32_t *itemid) +player_playback_start_byindex(int index, uint32_t *id) { struct player_command cmd; int ret; @@ -3746,7 +3746,7 @@ player_playback_start_byindex(int index, uint32_t *itemid) cmd.func = playback_start_byindex; cmd.func_bh = playback_start_bh; cmd.arg.playback_start_param.pos = index; - cmd.arg.playback_start_param.id_ptr = itemid; + cmd.arg.playback_start_param.id_ptr = id; ret = sync_command(&cmd); command_deinit(&cmd); @@ -3764,11 +3764,11 @@ player_playback_start_byindex(int index, uint32_t *itemid) * If a pointer is given as argument "itemid", its value will be set to the playing item dbmfi-id. * * @param pos the position in the UpNext-queue (zero-based) - * @param *itemid if not NULL, will be set to the playing item dbmfi-id + * @param *id if not NULL, will be set to the playing item dbmfi-id * @return 0 if successful, -1 if an error occurred */ int -player_playback_start_bypos(int pos, uint32_t *itemid) +player_playback_start_bypos(int pos, uint32_t *id) { struct player_command cmd; int ret; @@ -3778,7 +3778,7 @@ player_playback_start_bypos(int pos, uint32_t *itemid) cmd.func = playback_start_bypos; cmd.func_bh = playback_start_bh; cmd.arg.playback_start_param.pos = pos; - cmd.arg.playback_start_param.id_ptr = itemid; + cmd.arg.playback_start_param.id_ptr = id; ret = sync_command(&cmd); command_deinit(&cmd); @@ -3793,12 +3793,12 @@ player_playback_start_bypos(int pos, uint32_t *itemid) * * If a pointer is given as argument "itemid", its value will be set to the playing item dbmfi-id. * - * @param id The queue-item-id - * @param *itemid if not NULL, will be set to the playing item dbmfi-id + * @param item_id The queue-item-id + * @param *id if not NULL, will be set to the playing item dbmfi-id * @return 0 if successful, -1 if an error occurred */ int -player_playback_start_byitemid(uint32_t id, uint32_t *itemid) +player_playback_start_byitemid(uint32_t item_id, uint32_t *id) { struct player_command cmd; int ret; @@ -3807,8 +3807,8 @@ player_playback_start_byitemid(uint32_t id, uint32_t *itemid) cmd.func = playback_start_byitemid; cmd.func_bh = playback_start_bh; - cmd.arg.playback_start_param.id = id; - cmd.arg.playback_start_param.id_ptr = itemid; + cmd.arg.playback_start_param.id = item_id; + cmd.arg.playback_start_param.id_ptr = id; ret = sync_command(&cmd); command_deinit(&cmd); diff --git a/src/player.h b/src/player.h index 76cc6696..7147f2e3 100644 --- a/src/player.h +++ b/src/player.h @@ -118,16 +118,16 @@ int player_speaker_set(uint64_t *ids); int -player_playback_start(uint32_t *idx_id); +player_playback_start(uint32_t *id); int -player_playback_start_byindex(int pos, uint32_t *itemid); +player_playback_start_byindex(int pos, uint32_t *id); int -player_playback_start_bypos(int pos, uint32_t *itemid); +player_playback_start_bypos(int pos, uint32_t *id); int -player_playback_start_byitemid(uint32_t id, uint32_t *itemid); +player_playback_start_byitemid(uint32_t item_id, uint32_t *id); int player_playback_stop(void); From b19a7280b910210272a6c9ffc64d74ce1fea66ba Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 3 Oct 2015 08:51:58 +0200 Subject: [PATCH 22/23] [player] remove unused player_queue struct --- src/player.c | 7 ------- src/player.h | 15 --------------- 2 files changed, 22 deletions(-) diff --git a/src/player.c b/src/player.c index 207d4f26..7daa31d5 100644 --- a/src/player.c +++ b/src/player.c @@ -3340,13 +3340,6 @@ playerqueue_get_byindex(struct player_command *cmd) return 0; } -void -playerqueue_free(struct player_queue *queue) -{ - free(queue->queue); - free(queue); -} - static int playerqueue_add(struct player_command *cmd) { diff --git a/src/player.h b/src/player.h index 7147f2e3..cb1e7f07 100644 --- a/src/player.h +++ b/src/player.h @@ -71,21 +71,6 @@ struct player_status { typedef void (*spk_enum_cb)(uint64_t id, const char *name, int relvol, struct spk_flags flags, void *arg); -struct player_queue -{ - // The item id of the current playing item - uint32_t playingid; - // The number of items in the queue - unsigned int length; - - // The position in the queue for the first item in the queue array - unsigned int start_pos; - // The number of items in the queue array - unsigned int count; - // The queue array (array of item ids) - uint32_t *queue; -}; - struct player_history { /* Buffer index of the oldest remembered song */ From 199b765a7d15e6d522d8aaefb2a8677f9d5c3e17 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 3 Oct 2015 09:01:26 +0200 Subject: [PATCH 23/23] [player] Store item-ids in history to allow start of playback from the previously played items in dacp --- src/httpd_dacp.c | 6 ++++-- src/mpd.c | 6 +++--- src/player.c | 51 ++++++++++++++++++++++++------------------------ src/player.h | 5 +++-- 4 files changed, 36 insertions(+), 32 deletions(-) diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index c7410db7..ed56fb8e 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -967,6 +967,7 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u const char *cuequery; const char *param; uint32_t id; + uint32_t item_id; uint32_t pos; int clear; struct player_history *history; @@ -1018,6 +1019,7 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u dacp_propset_shufflestate(param, NULL); id = 0; + item_id = 0; pos = 0; param = evhttp_find_header(query, "index"); if (param) @@ -1041,7 +1043,7 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u if (history->count > pos) { pos = (history->start_index + history->count - pos - 1) % MAX_HISTORY_COUNT; - id = history->id[pos]; + item_id = history->item_id[pos]; } else { @@ -1060,7 +1062,7 @@ dacp_reply_cue_play(struct evhttp_request *req, struct evbuffer *evbuf, char **u /* If playing from history queue, the pos holds the id of the item to play */ if (hist) - ret = player_playback_start_byitemid(id, &id); //TODO [queue/history] id does not hold the queueitemid but the dbmfiid + ret = player_playback_start_byitemid(item_id, &id); else ret = player_playback_start_bypos(pos, &id); diff --git a/src/mpd.c b/src/mpd.c index 9ce8d86a..dc019a2e 100644 --- a/src/mpd.c +++ b/src/mpd.c @@ -650,7 +650,7 @@ mpd_command_currentsong(struct evbuffer *evbuf, int argc, char **argv, char **er return 0; } - ret = mpd_add_mediainfo_byid(evbuf, status.id, status.queueitem_id, status.pos_pl); + ret = mpd_add_mediainfo_byid(evbuf, status.id, status.item_id, status.pos_pl); if (ret < 0) { ret = asprintf(errmsg, "Error adding media info for file with id: %d", status.id); @@ -839,11 +839,11 @@ mpd_command_status(struct evbuffer *evbuf, int argc, char **argv, char **errmsg) "nextsong: %d\n" "nextsongid: %d\n", status.pos_pl, - status.queueitem_id, + status.item_id, (status.pos_ms / 1000), (status.len_ms / 1000), (status.pos_ms / 1000.0), status.next_pos_pl, - status.next_queueitem_id); + status.next_item_id); } if (filescanner_scanning()) diff --git a/src/player.c b/src/player.c index 7daa31d5..6b5c6d7c 100644 --- a/src/player.c +++ b/src/player.c @@ -87,7 +87,7 @@ struct player_source uint32_t id; /* Item-Id of the file/item in the queue */ - uint32_t queueitem_id; + uint32_t item_id; /* Length of the file/item in milliseconds */ uint32_t len_ms; @@ -794,7 +794,7 @@ player_history_get(void) * Add the song with the given id to the list of previously played songs */ static void -history_add(uint32_t id) +history_add(uint32_t id, uint32_t item_id) { unsigned int cur_index; unsigned int next_index; @@ -813,6 +813,7 @@ history_add(uint32_t id) history->start_index = (history->start_index + 1) % MAX_HISTORY_COUNT; history->id[next_index] = id; + history->item_id[next_index] = item_id; if (history->count < MAX_HISTORY_COUNT) history->count++; @@ -1145,7 +1146,7 @@ source_new(struct queue_item *item) ps = (struct player_source *)calloc(1, sizeof(struct player_source)); ps->id = queueitem_id(item); - ps->queueitem_id = queueitem_item_id(item); + ps->item_id = queueitem_item_id(item); ps->data_kind = queueitem_data_kind(item); ps->media_kind = queueitem_media_kind(item); ps->len_ms = queueitem_len(item); @@ -1458,7 +1459,7 @@ source_check(void) #ifdef LASTFM worker_execute(scrobble_cb, &id, sizeof(int), 8); #endif - history_add(cur_playing->id); + history_add(cur_playing->id, cur_playing->item_id); /* Stop playback */ if (!cur_playing->play_next) @@ -1523,12 +1524,12 @@ source_read(uint8_t *buf, int len, uint64_t rtptime) DPRINTF(E_DBG, L_PLAYER, "New file\n"); - item = queue_next(queue, cur_streaming->queueitem_id, shuffle, repeat, 1); + item = queue_next(queue, cur_streaming->item_id, shuffle, repeat, 1); if (ret < 0) { DPRINTF(E_LOG, L_PLAYER, "Error reading source %d\n", cur_streaming->id); - queue_remove_byitemid(queue, cur_streaming->queueitem_id); + queue_remove_byitemid(queue, cur_streaming->item_id); } if (item) @@ -2185,13 +2186,13 @@ get_status(struct player_command *cmd) status->status = PLAY_PAUSED; status->id = cur_streaming->id; - status->queueitem_id = cur_streaming->queueitem_id; + status->item_id = cur_streaming->item_id; pos = last_rtptime + AIRTUNES_V2_PACKET_SAMPLES - cur_streaming->stream_start; status->pos_ms = (pos * 1000) / 44100; status->len_ms = cur_streaming->len_ms; - status->pos_pl = queue_index_byitemid(queue, cur_streaming->queueitem_id, 0); + status->pos_pl = queue_index_byitemid(queue, cur_streaming->item_id, 0); break; @@ -2231,15 +2232,15 @@ get_status(struct player_command *cmd) status->len_ms = ps->len_ms; status->id = ps->id; - status->queueitem_id = ps->queueitem_id; - status->pos_pl = queue_index_byitemid(queue, ps->queueitem_id, 0); + status->item_id = ps->item_id; + status->pos_pl = queue_index_byitemid(queue, ps->item_id, 0); - item_next = queue_next(queue, ps->queueitem_id, shuffle, repeat, 0); + item_next = queue_next(queue, ps->item_id, shuffle, repeat, 0); if (item_next) { status->next_id = queueitem_id(item_next); - status->next_queueitem_id = queueitem_item_id(item_next); - status->next_pos_pl = queue_index_byitemid(queue, status->next_queueitem_id, 0); + status->next_item_id = queueitem_item_id(item_next); + status->next_pos_pl = queue_index_byitemid(queue, status->next_item_id, 0); } else { @@ -2315,7 +2316,7 @@ playback_stop(struct player_command *cmd) ps_playing = source_now_playing(); if (ps_playing) { - history_add(ps_playing->id); + history_add(ps_playing->id, ps_playing->item_id); } source_stop(); @@ -2577,7 +2578,7 @@ playback_start_bypos(struct player_command *cmd) if (ps_playing) { - qii = queue_get_bypos(queue, ps_playing->queueitem_id, offset, shuffle); + qii = queue_get_bypos(queue, ps_playing->item_id, offset, shuffle); } else { @@ -2606,7 +2607,7 @@ playback_prev_bh(struct player_command *cmd) /* Only add to history if playback started. */ if (cur_streaming->output_start > cur_streaming->stream_start) - history_add(cur_streaming->id); + history_add(cur_streaming->id, cur_streaming->item_id); /* Compute the playing time in seconds for the current song. */ if (cur_streaming->output_start > cur_streaming->stream_start) @@ -2619,7 +2620,7 @@ playback_prev_bh(struct player_command *cmd) DPRINTF(E_DBG, L_PLAYER, "Skipping song played %d sec\n", pos_sec); if (pos_sec < 3) { - item = queue_prev(queue, cur_streaming->queueitem_id, shuffle, repeat); + item = queue_prev(queue, cur_streaming->item_id, shuffle, repeat); if (!item) { playback_abort(); @@ -2677,9 +2678,9 @@ playback_next_bh(struct player_command *cmd) /* Only add to history if playback started. */ if (cur_streaming->output_start > cur_streaming->stream_start) - history_add(cur_streaming->id); + history_add(cur_streaming->id, cur_streaming->item_id); - item = queue_next(queue, cur_streaming->queueitem_id, shuffle, repeat, 0); + item = queue_next(queue, cur_streaming->item_id, shuffle, repeat, 0); if (!item) { playback_abort(); @@ -3281,7 +3282,7 @@ shuffle_set(struct player_command *cmd) case 1: if (!shuffle) { - cur_id = cur_streaming ? cur_streaming->queueitem_id : 0; + cur_id = cur_streaming ? cur_streaming->item_id : 0; queue_shuffle(queue, cur_id); } /* FALLTHROUGH*/ @@ -3314,7 +3315,7 @@ playerqueue_get_bypos(struct player_command *cmd) item_id = 0; if (ps) { - item_id = ps->queueitem_id; + item_id = ps->item_id; } qi = queue_new_bypos(queue, item_id, count, shuffle); @@ -3352,7 +3353,7 @@ playerqueue_add(struct player_command *cmd) if (shuffle) { - cur_id = cur_streaming ? cur_streaming->queueitem_id : 0; + cur_id = cur_streaming ? cur_streaming->item_id : 0; queue_shuffle(queue, cur_id); } @@ -3374,7 +3375,7 @@ playerqueue_add_next(struct player_command *cmd) items = cmd->arg.queue_add_param.items; - cur_id = cur_streaming ? cur_streaming->queueitem_id : 0; + cur_id = cur_streaming ? cur_streaming->item_id : 0; queue_add_after(queue, items, cur_id); @@ -3407,7 +3408,7 @@ playerqueue_move_bypos(struct player_command *cmd) return -1; } - queue_move_bypos(queue, ps_playing->queueitem_id, cmd->arg.ps_pos[0], cmd->arg.ps_pos[1], shuffle); + queue_move_bypos(queue, ps_playing->item_id, cmd->arg.ps_pos[0], cmd->arg.ps_pos[1], shuffle); cur_plversion++; @@ -3438,7 +3439,7 @@ playerqueue_remove_bypos(struct player_command *cmd) } DPRINTF(E_DBG, L_PLAYER, "Removing item from position %d\n", pos); - queue_remove_bypos(queue, ps_playing->queueitem_id, pos, shuffle); + queue_remove_bypos(queue, ps_playing->item_id, pos, shuffle); return 0; } diff --git a/src/player.h b/src/player.h index cb1e7f07..b1f8e1ec 100644 --- a/src/player.h +++ b/src/player.h @@ -54,7 +54,7 @@ struct player_status { /* Id of the playing file/item in the files database */ uint32_t id; /* Item-Id of the playing file/item in the queue */ - uint32_t queueitem_id; + uint32_t item_id; /* Elapsed time in ms of playing item */ uint32_t pos_ms; /* Length in ms of playing item */ @@ -64,7 +64,7 @@ struct player_status { /* Item id of next item in playlist */ uint32_t next_id; /* Item-Id of the next file/item in the queue */ - uint32_t next_queueitem_id; + uint32_t next_item_id; /* Playlist position of next item */ int next_pos_pl; }; @@ -81,6 +81,7 @@ struct player_history /* Circular buffer of song ids previously played by forked-daapd */ uint32_t id[MAX_HISTORY_COUNT]; + uint32_t item_id[MAX_HISTORY_COUNT]; };