From 6e5d6791ff0462304fafe897a510032b4bd06069 Mon Sep 17 00:00:00 2001 From: chme Date: Sun, 22 Sep 2019 09:18:53 +0200 Subject: [PATCH] [player/dacp/mpd/jsonapi] Combine seek commands into one with mode param Also changes relative seeking behavior: - seeking behind the the current track only switches to the previous track, if we are not more than 3 seconds into the current track, otherwise starts current track from the beginning - seeking beyond the current track will start the next track from the beginning --- src/httpd_dacp.c | 2 +- src/httpd_jsonapi.c | 4 +- src/mpd.c | 6 +- src/player.c | 212 +++++++++++++++++++++++++------------------- src/player.h | 10 ++- 5 files changed, 134 insertions(+), 100 deletions(-) diff --git a/src/httpd_dacp.c b/src/httpd_dacp.c index 83d2db8f..251a2e5b 100644 --- a/src/httpd_dacp.c +++ b/src/httpd_dacp.c @@ -549,7 +549,7 @@ seek_timer_cb(int fd, short what, void *arg) DPRINTF(E_DBG, L_DACP, "Seek timer expired, target %d ms\n", seek_target); - ret = player_playback_seek(seek_target); + ret = player_playback_seek(seek_target, PLAYER_SEEK_POSITION); if (ret < 0) { DPRINTF(E_LOG, L_DACP, "Player failed to seek to %d ms\n", seek_target); diff --git a/src/httpd_jsonapi.c b/src/httpd_jsonapi.c index 6c36f101..ddc1fdb7 100644 --- a/src/httpd_jsonapi.c +++ b/src/httpd_jsonapi.c @@ -1865,7 +1865,7 @@ jsonapi_reply_player_seek(struct httpd_request *hreq) if (ret < 0) return HTTP_BADREQUEST; - ret = player_playback_seek(position_ms); + ret = player_playback_seek(position_ms, PLAYER_SEEK_POSITION); } else { @@ -1873,7 +1873,7 @@ jsonapi_reply_player_seek(struct httpd_request *hreq) if (ret < 0) return HTTP_BADREQUEST; - ret = player_playback_seek_rel(seek_ms); + ret = player_playback_seek(seek_ms, PLAYER_SEEK_RELATIVE); } if (ret < 0) diff --git a/src/mpd.c b/src/mpd.c index 87ef8a8f..29e9a85c 100644 --- a/src/mpd.c +++ b/src/mpd.c @@ -1521,7 +1521,7 @@ mpd_command_seek(struct evbuffer *evbuf, int argc, char **argv, char **errmsg, s seek_target_sec = strtof(argv[2], NULL); seek_target_msec = seek_target_sec * 1000; - ret = player_playback_seek(seek_target_msec); + ret = player_playback_seek(seek_target_msec, PLAYER_SEEK_POSITION); if (ret < 0) { @@ -1571,7 +1571,7 @@ mpd_command_seekid(struct evbuffer *evbuf, int argc, char **argv, char **errmsg, seek_target_sec = strtof(argv[2], NULL); seek_target_msec = seek_target_sec * 1000; - ret = player_playback_seek(seek_target_msec); + ret = player_playback_seek(seek_target_msec, PLAYER_SEEK_POSITION); if (ret < 0) { @@ -1604,7 +1604,7 @@ mpd_command_seekcur(struct evbuffer *evbuf, int argc, char **argv, char **errmsg seek_target_msec = seek_target_sec * 1000; // TODO If prefixed by '+' or '-', then the time is relative to the current playing position. - ret = player_playback_seek(seek_target_msec); + ret = player_playback_seek(seek_target_msec, PLAYER_SEEK_POSITION); if (ret < 0) { diff --git a/src/player.c b/src/player.c index 9b30c76c..874a3457 100644 --- a/src/player.c +++ b/src/player.c @@ -146,6 +146,12 @@ struct speaker_auth_param char pin[5]; }; +struct player_seek_param +{ + int ms; + enum player_seek_mode mode; +}; + union player_arg { struct output_device *device; @@ -2167,103 +2173,110 @@ playback_next_bh(void *arg, int *retval) return COMMAND_END; } -static enum command_state -playback_seek_bh(void *arg, int *retval) +static int +seek_calc_position_ms(struct player_seek_param *seek_param, struct db_queue_item **queue_item, int *position_ms) { - struct db_queue_item *queue_item; - union player_arg *cmdarg = arg; - int ret; + struct db_queue_item *seek_queue_item = NULL; + int seek_ms = 0; - // outputs_flush() in playback_pause() may have a caused a failure callback - // from the output, which in streaming_cb() can cause pb_abort() - if (player_state == PLAY_STOPPED) - { - goto error; - } + // Initialize out parameters + *queue_item = NULL; + *position_ms = 0; - queue_item = db_queue_fetch_byitemid(pb_session.playing_now->item_id); - if (!queue_item) - { - DPRINTF(E_DBG, L_PLAYER, "Error seeking in source, queue item has disappeared\n"); - goto error; - } - - ret = pb_session_start(queue_item, cmdarg->intval); - free_queue_item(queue_item, 0); - if (ret < 0) - { - DPRINTF(E_DBG, L_PLAYER, "Error seeking to %d, aborting playback\n", cmdarg->intval); - goto error; - } - - // Silent status change - playback_start() sends the real status update - player_state = PLAY_PAUSED; - - *retval = 0; - return COMMAND_END; - - error: - pb_abort(); - *retval = -1; - return COMMAND_END; -} - -static enum command_state -playback_seek_rel_bh(void *arg, int *retval) -{ - struct db_queue_item *queue_item; - union player_arg *cmdarg = arg; - int seek_ms; - int ret; - - - // outputs_flush() in playback_pause() may have a caused a failure callback - // from the output, which in streaming_cb() can cause pb_abort() - if (player_state == PLAY_STOPPED) - { - goto error; - } - - seek_ms = pb_session.playing_now->pos_ms + cmdarg->intval; + // Calculate seek position + if (seek_param->mode == PLAYER_SEEK_POSITION) + seek_ms = seek_param->ms; + else + seek_ms = pb_session.playing_now->pos_ms + seek_param->ms; + // Check if we need to switch to a previous track, this will be done if we are in the first 3 seconds + // of a track and we have a seek request for more than 3 seconds if (seek_ms < 0) { - // Seeking behind the start of the current queue item - queue_item = queue_item_prev(pb_session.playing_now->item_id); - if (queue_item) + if (pb_session.playing_now->pos_ms < 3000) { - seek_ms = queue_item->song_length + seek_ms; + // We are in the first 3 seconds of the track, switch to the previous track and recalculate the absolute seek position + seek_queue_item = queue_item_prev(pb_session.playing_now->item_id); + + if (seek_queue_item) + { + seek_ms = seek_queue_item->song_length + seek_ms; + // Make sure to not try to seek behind the previous track (this is also the case if song_length is zero) + seek_ms = (seek_ms < 0) ? 0 : seek_ms; + } + else + { + // There is no previous queue item, seek to the start of the current item + seek_ms = 0; + } } else { - // There is no previous queue item, seek to the start of the current item - queue_item = db_queue_fetch_byitemid(pb_session.playing_now->item_id); + // We are more than 3 seconds into the playing track, seek to beginning of current track seek_ms = 0; } } - else if (seek_ms > pb_session.playing_now->len_ms) + else if (seek_ms > 0 && seek_ms > pb_session.playing_now->len_ms) { - // Seeking beyond the end of the current queue item - queue_item = queue_item_next(pb_session.playing_now->item_id); - seek_ms = seek_ms - pb_session.playing_now->len_ms; - } - else - { - // Seeking in the current queue item - queue_item = db_queue_fetch_byitemid(pb_session.playing_now->item_id); + // We are seeking beyond the current track, play the next track from the beginning + seek_queue_item = queue_item_next(pb_session.playing_now->item_id); + if (seek_queue_item) + { + seek_ms = 0; + } } - if (!queue_item) + if (!seek_queue_item) + { + // Seeking in the current queue item + seek_queue_item = db_queue_fetch_byitemid(pb_session.playing_now->item_id); + } + + if (!seek_queue_item) + { + DPRINTF(E_LOG, L_PLAYER, "Error fetching queue item for seek command (seek_ms=%d, seek_mode=%d)\n", seek_param->ms, seek_param->mode); + return -1; + } + + if (seek_ms < 0) + { + DPRINTF(E_LOG, L_PLAYER, "Error calculating new seek position for seek command (seek_ms=%d, seek_mode=%d)\n", seek_param->ms, seek_param->mode); + free(seek_queue_item); + return -1; + } + + *queue_item = seek_queue_item; + *position_ms = seek_ms; + return 0; +} + +static enum command_state +playback_seek_bh(void *arg, int *retval) +{ + struct player_seek_param *seek_param = arg; + struct db_queue_item *queue_item; + int position_ms; + int ret; + + // outputs_flush() in playback_pause() may have a caused a failure callback + // from the output, which in streaming_cb() can cause pb_abort() + if (player_state == PLAY_STOPPED) { - DPRINTF(E_DBG, L_PLAYER, "Error seeking in source, queue item has disappeared\n"); goto error; } - ret = pb_session_start(queue_item, seek_ms); + ret = seek_calc_position_ms(seek_param, &queue_item, &position_ms); + if (ret < 0) + { + DPRINTF(E_LOG, L_PLAYER, "Error calculating new seek position\n"); + goto error; + } + + ret = pb_session_start(queue_item, position_ms); free_queue_item(queue_item, 0); if (ret < 0) { - DPRINTF(E_DBG, L_PLAYER, "Error seeking to %d, aborting playback\n", cmdarg->intval); + DPRINTF(E_LOG, L_PLAYER, "Error seeking to %d, aborting playback\n", position_ms); goto error; } @@ -2348,6 +2361,21 @@ playback_pause(void *arg, int *retval) return COMMAND_END; } +static enum command_state +playback_seek(void *arg, int *retval) +{ + // Only check if the current playing track is seekable, other checks will be done in playback_pause() + if (pb_session.playing_now && pb_session.playing_now->len_ms <= 0) + { + DPRINTF(E_WARN, L_PLAYER, "Failed to seek, track is not seekable\n"); + + *retval = -1; + return COMMAND_END; + } + + return playback_pause(arg, retval); +} + static void device_to_speaker_info(struct player_speaker_info *spk, struct output_device *device) { @@ -2986,27 +3014,31 @@ player_playback_pause(void) return ret; } +/** + * Seeks to the position "seek_ms", depending on the given "seek_mode" seek_ms is + * either the new position in the current track (seek_mode == PLAYER_SEEK_POSITION) + * or a relative amount of milliseconds from the current playing position + * (seek_mode == PLAYER_SEEK_RELATIVE). + * + * Relative seeking switches tracks, if: + * - seeking behind the the current track and current playing position is not more than 3 seconds + * - seeking beyond the current track + * + * @param seek_ms Position or relative amount of milliseconds to seek to + * @param seek_mode If PLAYER_SEEK_POSITION seek_ms is a position in milliseconds, + * if PLAYER_SEEK_RELATIVE seek_ms is the relative amount of milliseconds + * @return Returns 0 on success and a negative value on error + */ int -player_playback_seek(int ms) +player_playback_seek(int seek_ms, enum player_seek_mode seek_mode) { - union player_arg cmdarg; + struct player_seek_param seek_param; int ret; - cmdarg.intval = ms; + seek_param.ms = seek_ms; + seek_param.mode = seek_mode; - ret = commands_exec_sync(cmdbase, playback_pause, playback_seek_bh, &cmdarg); - return ret; -} - -int -player_playback_seek_rel(int ms) -{ - union player_arg cmdarg; - int ret; - - cmdarg.intval = ms; - - ret = commands_exec_sync(cmdbase, playback_pause, playback_seek_rel_bh, &cmdarg); + ret = commands_exec_sync(cmdbase, playback_seek, playback_seek_bh, &seek_param); return ret; } diff --git a/src/player.h b/src/player.h index 746b5294..b7d83fb5 100644 --- a/src/player.h +++ b/src/player.h @@ -22,6 +22,11 @@ enum repeat_mode { REPEAT_ALL = 2, }; +enum player_seek_mode { + PLAYER_SEEK_POSITION = 1, + PLAYER_SEEK_RELATIVE = 2, +}; + struct player_speaker_info { uint64_t id; char name[255]; @@ -109,10 +114,7 @@ int player_playback_pause(void); int -player_playback_seek(int ms); - -int -player_playback_seek_rel(int ms); +player_playback_seek(int seek_ms, enum player_seek_mode seek_mode); int player_playback_next(void);