mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-26 23:25:56 -05:00
[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
This commit is contained in:
parent
d69f328973
commit
6e5d6791ff
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
212
src/player.c
212
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;
|
||||
}
|
||||
|
||||
|
10
src/player.h
10
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);
|
||||
|
Loading…
Reference in New Issue
Block a user