From b8e0280567ffc9a578f53a7ceab6858f4851b62e Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Tue, 5 Mar 2019 22:45:38 +0100 Subject: [PATCH] [outputs] Add a 10 sec stop timer + drop playback_stop() In the output implementations playback_stop() was somewhat redundant, since device_stop() does the same. The timer should make sure that we always close outputs (previously they were in some cases kept open). The commit also includes some renaming. --- src/outputs.c | 160 ++++++++++++++++++++++++++++++------------- src/outputs.h | 18 +++-- src/outputs/alsa.c | 17 ----- src/outputs/cast.c | 15 ----- src/outputs/dummy.c | 13 ---- src/outputs/fifo.c | 15 ----- src/outputs/pulse.c | 32 --------- src/outputs/raop.c | 18 ++--- src/player.c | 161 ++++++++++++++++++++++++++------------------ 9 files changed, 226 insertions(+), 223 deletions(-) diff --git a/src/outputs.c b/src/outputs.c index 22fbb7ed..c084f3ec 100644 --- a/src/outputs.c +++ b/src/outputs.c @@ -74,9 +74,15 @@ static struct output_definition *outputs[] = { NULL }; +// When we stop, we keep the outputs open for a while, just in case we are +// actually just restarting. This timeout determines how long we wait before +// full stop. +// (value is in seconds) +#define OUTPUTS_STOP_TIMEOUT 10 + #define OUTPUTS_MAX_CALLBACKS 64 -struct outputs_callback_queue +struct outputs_callback_register { output_status_cb cb; struct output_device *device; @@ -89,9 +95,6 @@ struct outputs_callback_queue enum output_device_state state; }; -struct outputs_callback_queue outputs_cb_queue[OUTPUTS_MAX_CALLBACKS]; -struct event *outputs_deferredev; - struct output_quality_subscription { int count; @@ -99,13 +102,31 @@ struct output_quality_subscription struct encode_ctx *encode_ctx; }; +static struct outputs_callback_register outputs_cb_register[OUTPUTS_MAX_CALLBACKS]; +static struct event *outputs_deferredev; +static struct timeval outputs_stop_timeout = { OUTPUTS_STOP_TIMEOUT, 0 }; + // Last element is a zero terminator static struct output_quality_subscription output_quality_subscriptions[OUTPUTS_MAX_QUALITY_SUBSCRIPTIONS + 1]; -static bool output_got_new_subscription; +static bool outputs_got_new_subscription; /* ------------------------------- MISC HELPERS ----------------------------- */ +static output_status_cb +callback_get(struct output_device *device) +{ + int callback_id; + + for (callback_id = 0; callback_id < ARRAY_SIZE(outputs_cb_register); callback_id++) + { + if (outputs_cb_register[callback_id].device == device) + return outputs_cb_register[callback_id].cb; + } + + return NULL; +} + static void callback_remove(struct output_device *device) { @@ -114,12 +135,12 @@ callback_remove(struct output_device *device) if (!device) return; - for (callback_id = 0; callback_id < ARRAY_SIZE(outputs_cb_queue); callback_id++) + for (callback_id = 0; callback_id < ARRAY_SIZE(outputs_cb_register); callback_id++) { - if (outputs_cb_queue[callback_id].device == device) + if (outputs_cb_register[callback_id].device == device) { - DPRINTF(E_DBG, L_PLAYER, "Removing callback to %s, id %d\n", player_pmap(outputs_cb_queue[callback_id].cb), callback_id); - memset(&outputs_cb_queue[callback_id], 0, sizeof(struct outputs_callback_queue)); + DPRINTF(E_DBG, L_PLAYER, "Removing callback to %s, id %d\n", player_pmap(outputs_cb_register[callback_id].cb), callback_id); + memset(&outputs_cb_register[callback_id], 0, sizeof(struct outputs_callback_register)); } } } @@ -137,26 +158,26 @@ callback_add(struct output_device *device, output_status_cb cb) callback_remove(device); // Find a free slot in the queue - for (callback_id = 0; callback_id < ARRAY_SIZE(outputs_cb_queue); callback_id++) + for (callback_id = 0; callback_id < ARRAY_SIZE(outputs_cb_register); callback_id++) { - if (outputs_cb_queue[callback_id].cb == NULL) + if (outputs_cb_register[callback_id].cb == NULL) break; } - if (callback_id == ARRAY_SIZE(outputs_cb_queue)) + if (callback_id == ARRAY_SIZE(outputs_cb_register)) { DPRINTF(E_LOG, L_PLAYER, "Output callback queue is full! (size is %d)\n", OUTPUTS_MAX_CALLBACKS); return -1; } - outputs_cb_queue[callback_id].cb = cb; - outputs_cb_queue[callback_id].device = device; // Don't dereference this later, it might become invalid! + outputs_cb_register[callback_id].cb = cb; + outputs_cb_register[callback_id].device = device; // Don't dereference this later, it might become invalid! DPRINTF(E_DBG, L_PLAYER, "Registered callback to %s with id %d (device %p, %s)\n", player_pmap(cb), callback_id, device, device->name); int active = 0; - for (int i = 0; i < ARRAY_SIZE(outputs_cb_queue); i++) - if (outputs_cb_queue[i].cb) + for (int i = 0; i < ARRAY_SIZE(outputs_cb_register); i++) + if (outputs_cb_register[i].cb) active++; DPRINTF(E_DBG, L_PLAYER, "Number of active callbacks: %d\n", active); @@ -172,19 +193,19 @@ deferred_cb(int fd, short what, void *arg) enum output_device_state state; int callback_id; - for (callback_id = 0; callback_id < ARRAY_SIZE(outputs_cb_queue); callback_id++) + for (callback_id = 0; callback_id < ARRAY_SIZE(outputs_cb_register); callback_id++) { - if (outputs_cb_queue[callback_id].ready) + if (outputs_cb_register[callback_id].ready) { // Must copy before making callback, since you never know what the // callback might result in (could call back in) - cb = outputs_cb_queue[callback_id].cb; - state = outputs_cb_queue[callback_id].state; + cb = outputs_cb_register[callback_id].cb; + state = outputs_cb_register[callback_id].state; // Will be NULL if the device has disappeared - device = outputs_device_get(outputs_cb_queue[callback_id].device_id); + device = outputs_device_get(outputs_cb_register[callback_id].device_id); - memset(&outputs_cb_queue[callback_id], 0, sizeof(struct outputs_callback_queue)); + memset(&outputs_cb_register[callback_id], 0, sizeof(struct outputs_callback_register)); // The device has left the building (stopped/failed), and the backend // is not using it any more @@ -200,13 +221,22 @@ deferred_cb(int fd, short what, void *arg) } } - for (int i = 0; i < ARRAY_SIZE(outputs_cb_queue); i++) + for (int i = 0; i < ARRAY_SIZE(outputs_cb_register); i++) { - if (outputs_cb_queue[i].cb) - DPRINTF(E_DBG, L_PLAYER, "%d. Active callback: %s\n", i, player_pmap(outputs_cb_queue[i].cb)); + if (outputs_cb_register[i].cb) + DPRINTF(E_DBG, L_PLAYER, "%d. Active callback: %s\n", i, player_pmap(outputs_cb_register[i].cb)); } } +static void +stop_timer_cb(int fd, short what, void *arg) +{ + struct output_device *device = arg; + output_status_cb cb = callback_get(device); + + outputs_device_stop(device, cb); +} + static void device_stop_cb(struct output_device *device, enum output_device_state status) { @@ -300,10 +330,10 @@ buffer_fill(struct output_buffer *obuf, void *buf, size_t bufsize, struct media_ // The resampling/encoding (transcode) contexts work for a given input quality, // so if the quality changes we need to reset the contexts. We also do that if // we have received a subscription for a new quality. - if (!quality_is_equal(quality, &obuf->data[0].quality) || output_got_new_subscription) + if (!quality_is_equal(quality, &obuf->data[0].quality) || outputs_got_new_subscription) { encoding_reset(quality); - output_got_new_subscription = false; + outputs_got_new_subscription = false; } // The first element of the output_buffer is always just the raw input data @@ -468,7 +498,7 @@ outputs_quality_subscribe(struct media_quality *quality) quality->sample_rate, quality->bits_per_sample, quality->channels, output_quality_subscriptions[i].count); // Better way of signaling this? - output_got_new_subscription = true; + outputs_got_new_subscription = true; return 0; } @@ -515,7 +545,7 @@ outputs_cb(int callback_id, uint64_t device_id, enum output_device_state state) if (callback_id < 0) return; - if (!(callback_id < ARRAY_SIZE(outputs_cb_queue)) || !outputs_cb_queue[callback_id].cb) + if (!(callback_id < ARRAY_SIZE(outputs_cb_register)) || !outputs_cb_register[callback_id].cb) { DPRINTF(E_LOG, L_PLAYER, "Bug! Output backend called us with an illegal callback id (%d)\n", callback_id); return; @@ -523,9 +553,9 @@ outputs_cb(int callback_id, uint64_t device_id, enum output_device_state state) DPRINTF(E_DBG, L_PLAYER, "Callback request received, id is %i\n", callback_id); - outputs_cb_queue[callback_id].ready = true; - outputs_cb_queue[callback_id].device_id = device_id; - outputs_cb_queue[callback_id].state = state; + outputs_cb_register[callback_id].ready = true; + outputs_cb_register[callback_id].device_id = device_id; + outputs_cb_register[callback_id].state = state; event_active(outputs_deferredev, 0, 0); } @@ -558,6 +588,8 @@ outputs_device_add(struct output_device *add, bool new_deselect, int default_vol { device = add; + device->stop_timer = evtimer_new(evbase_player, stop_timer_cb, device); + keep_name = strdup(device->name); ret = db_speaker_get(device, device->id); if (ret < 0) @@ -691,6 +723,19 @@ outputs_device_stop(struct output_device *device, output_status_cb cb) return outputs[device->type]->device_stop(device, callback_add(device, cb)); } +int +outputs_device_stop_delayed(struct output_device *device, output_status_cb cb) +{ + if (outputs[device->type]->disabled || !outputs[device->type]->device_stop) + return -1; + + outputs[device->type]->device_cb_set(device, callback_add(device, cb)); + + event_add(device->stop_timer, &outputs_stop_timeout); + + return 0; +} + int outputs_device_flush(struct output_device *device, output_status_cb cb) { @@ -772,6 +817,9 @@ outputs_device_free(struct output_device *device) if (outputs[device->type]->device_free_extra) outputs[device->type]->device_free_extra(device); + if (device->stop_timer) + event_free(device->stop_timer); + free(device->name); free(device->auth_key); free(device->v4_address); @@ -780,21 +828,6 @@ outputs_device_free(struct output_device *device) free(device); } -void -outputs_playback_stop(void) -{ - int i; - - for (i = 0; outputs[i]; i++) - { - if (outputs[i]->disabled) - continue; - - if (outputs[i]->playback_stop) - outputs[i]->playback_stop(); - } -} - int outputs_flush(output_status_cb cb) { @@ -814,6 +847,39 @@ outputs_flush(output_status_cb cb) return count; } +int +outputs_stop(output_status_cb cb) +{ + struct output_device *device; + int count = 0; + int ret; + + for (device = output_device_list; device; device = device->next) + { + if (!device->session) + continue; + + ret = outputs_device_stop(device, cb); + if (ret < 0) + continue; + + count++; + } + + return count; +} + +int +outputs_stop_delayed_cancel(void) +{ + struct output_device *device; + + for (device = output_device_list; device; device = device->next) + event_del(device->stop_timer); + + return 0; +} + void outputs_write(void *buf, size_t bufsize, int nsamples, struct media_quality *quality, struct timespec *pts) { diff --git a/src/outputs.h b/src/outputs.h index ccc338ad..e1b5d71e 100644 --- a/src/outputs.h +++ b/src/outputs.h @@ -3,6 +3,7 @@ #define __OUTPUTS_H__ #include +#include #include #include "misc.h" @@ -137,6 +138,8 @@ struct output_device short v4_port; short v6_port; + struct event *stop_timer; + // Opaque pointers to device and session data void *extra_device_info; void *session; @@ -220,9 +223,6 @@ struct output_definition // Free the private device data void (*device_free_extra)(struct output_device *device); - // Start/stop playback on devices that were started - void (*playback_stop)(void); - // Write stream data to the output devices void (*write)(struct output_buffer *buffer); @@ -281,6 +281,9 @@ outputs_device_start(struct output_device *device, output_status_cb cb); int outputs_device_stop(struct output_device *device, output_status_cb cb); +int +outputs_device_stop_delayed(struct output_device *device, output_status_cb cb); + int outputs_device_flush(struct output_device *device, output_status_cb cb); @@ -302,12 +305,15 @@ outputs_device_cb_set(struct output_device *device, output_status_cb cb); void outputs_device_free(struct output_device *device); -void -outputs_playback_stop(void); - int outputs_flush(output_status_cb cb); +int +outputs_stop(output_status_cb cb); + +int +outputs_stop_delayed_cancel(void); + void outputs_write(void *buf, size_t bufsize, int nsamples, struct media_quality *quality, struct timespec *pts); diff --git a/src/outputs/alsa.c b/src/outputs/alsa.c index 08e5d325..ede2e23d 100644 --- a/src/outputs/alsa.c +++ b/src/outputs/alsa.c @@ -967,22 +967,6 @@ alsa_device_cb_set(struct output_device *device, int callback_id) as->callback_id = callback_id; } -static void -alsa_playback_stop(void) -{ - struct alsa_session *as; - struct alsa_session *next; - - for (as = sessions; as; as = next) - { - next = as->next; - snd_pcm_drop(as->hdl); - - as->state = OUTPUT_STATE_STOPPED; - alsa_status(as); // Will stop the session - } -} - static void alsa_write(struct output_buffer *obuf) { @@ -1061,6 +1045,5 @@ struct output_definition output_alsa = .device_probe = alsa_device_probe, .device_volume_set = alsa_device_volume_set, .device_cb_set = alsa_device_cb_set, - .playback_stop = alsa_playback_stop, .write = alsa_write, }; diff --git a/src/outputs/cast.c b/src/outputs/cast.c index eabeadfc..030dca91 100644 --- a/src/outputs/cast.c +++ b/src/outputs/cast.c @@ -2062,20 +2062,6 @@ cast_device_volume_set(struct output_device *device, int callback_id) return 1; } -static void -cast_playback_stop(void) -{ - struct cast_session *cs; - struct cast_session *next; - - for (cs = cast_sessions; cs; cs = next) - { - next = cs->next; - if (cs->state & CAST_STATE_F_MEDIA_CONNECTED) - cast_session_shutdown(cs, CAST_STATE_NONE); - } -} - static void cast_write(struct output_buffer *obuf) { @@ -2220,7 +2206,6 @@ struct output_definition output_cast = .device_flush = cast_device_flush, .device_cb_set = cast_device_cb_set, .device_volume_set = cast_device_volume_set, - .playback_stop = cast_playback_stop, .write = cast_write, .init = cast_init, .deinit = cast_deinit, diff --git a/src/outputs/dummy.c b/src/outputs/dummy.c index 8783f01b..a1865652 100644 --- a/src/outputs/dummy.c +++ b/src/outputs/dummy.c @@ -183,18 +183,6 @@ dummy_device_cb_set(struct output_device *device, int callback_id) ds->callback_id = callback_id; } -static void -dummy_playback_stop(void) -{ - struct dummy_session *ds = sessions; - - if (!sessions) - return; - - ds->state = OUTPUT_STATE_CONNECTED; - dummy_status(ds); -} - static int dummy_init(void) { @@ -245,5 +233,4 @@ struct output_definition output_dummy = .device_probe = dummy_device_probe, .device_volume_set = dummy_device_volume_set, .device_cb_set = dummy_device_cb_set, - .playback_stop = dummy_playback_stop, }; diff --git a/src/outputs/fifo.c b/src/outputs/fifo.c index f1f54e80..0e246adb 100644 --- a/src/outputs/fifo.c +++ b/src/outputs/fifo.c @@ -387,20 +387,6 @@ fifo_device_cb_set(struct output_device *device, int callback_id) fifo_session->callback_id = callback_id; } -static void -fifo_playback_stop(void) -{ - struct fifo_session *fifo_session = sessions; - - if (!fifo_session) - return; - - free_buffer(); - - fifo_session->state = OUTPUT_STATE_CONNECTED; - fifo_status(fifo_session); -} - static void fifo_write(struct output_buffer *obuf) { @@ -532,6 +518,5 @@ struct output_definition output_fifo = .device_probe = fifo_device_probe, .device_volume_set = fifo_device_volume_set, .device_cb_set = fifo_device_cb_set, - .playback_stop = fifo_playback_stop, .write = fifo_write, }; diff --git a/src/outputs/pulse.c b/src/outputs/pulse.c index ed49e809..74d2f9ac 100644 --- a/src/outputs/pulse.c +++ b/src/outputs/pulse.c @@ -771,7 +771,6 @@ pulse_device_stop(struct output_device *device, int callback_id) return 0; } - static int pulse_device_flush(struct output_device *device, int callback_id) { @@ -907,36 +906,6 @@ pulse_write(struct output_buffer *obuf) } } -static void -pulse_playback_stop(void) -{ - struct pulse_session *ps; - pa_operation* o; - - pa_threaded_mainloop_lock(pulse.mainloop); - - for (ps = sessions; ps; ps = ps->next) - { - o = pa_stream_cork(ps->stream, 1, NULL, NULL); - if (!o) - { - DPRINTF(E_LOG, L_LAUDIO, "Pulseaudio could not pause '%s': %s\n", ps->devname, pa_strerror(pa_context_errno(pulse.context))); - continue; - } - pa_operation_unref(o); - - o = pa_stream_flush(ps->stream, NULL, NULL); - if (!o) - { - DPRINTF(E_LOG, L_LAUDIO, "Pulseaudio could not flush '%s': %s\n", ps->devname, pa_strerror(pa_context_errno(pulse.context))); - continue; - } - pa_operation_unref(o); - } - - pa_threaded_mainloop_unlock(pulse.mainloop); -} - static int pulse_init(void) { @@ -1032,7 +1001,6 @@ struct output_definition output_pulse = .device_free_extra = pulse_device_free_extra, .device_cb_set = pulse_device_cb_set, .device_volume_set = pulse_device_volume_set, - .playback_stop = pulse_playback_stop, .write = pulse_write, }; diff --git a/src/outputs/raop.c b/src/outputs/raop.c index 8fccb4c5..3ef3ad9e 100644 --- a/src/outputs/raop.c +++ b/src/outputs/raop.c @@ -2816,6 +2816,12 @@ raop_keep_alive_timer_cb(int fd, short what, void *arg) { struct raop_session *rs; + if (!raop_sessions) + { + event_del(keep_alive_timer); + return; + } + for (rs = raop_sessions; rs; rs = rs->next) { if (!(rs->state & RAOP_STATE_F_CONNECTED)) @@ -4850,17 +4856,6 @@ raop_device_free_extra(struct output_device *device) free(re); } -static void -raop_playback_stop(void) -{ - struct raop_session *rs; - - evtimer_del(keep_alive_timer); - - for (rs = raop_sessions; rs; rs = rs->next) - session_teardown(rs, "playback_stop"); -} - static void raop_write(struct output_buffer *obuf) { @@ -5069,7 +5064,6 @@ struct output_definition output_raop = .device_free_extra = raop_device_free_extra, .device_volume_set = raop_set_volume_one, .device_volume_to_pct = raop_volume_to_pct, - .playback_stop = raop_playback_stop, .write = raop_write, .metadata_prepare = raop_metadata_prepare, .metadata_send = raop_metadata_send, diff --git a/src/player.c b/src/player.c index 47232e44..82d25eb8 100644 --- a/src/player.c +++ b/src/player.c @@ -128,15 +128,9 @@ // When we pause, we keep the input open, but we can't do that forever. We must // think of the poor streaming servers, for instance. This timeout determines -// how long we stay paused, before we go to a full stop. +// how long we stay paused, before we close the inputs. // (value is in seconds) -#define PLAYER_PAUSE_TIME_MAX 600 - -// When we stop, we keep the outputs open for a while, just in case we are -// actually just restarting. This timeout determines how long we wait before -// full stop. -// (value is in seconds) -#define PLAYER_STOP_TIME_MAX 10 +#define PLAYER_PAUSE_TIMEOUT 600 //#define DEBUG_PLAYER 1 @@ -291,15 +285,14 @@ static int pb_timer_fd; timer_t pb_timer; #endif static struct event *pb_timer_ev; -static struct event *player_abort_timeout_ev; +static struct event *player_pause_timeout_ev; // Time between ticks, i.e. time between when playback_cb() is invoked static struct timespec player_tick_interval; // Timer resolution static struct timespec player_timer_res; -static struct timeval player_pause_timeout = { PLAYER_PAUSE_TIME_MAX, 0 }; -static struct timeval player_stop_timeout = { PLAYER_STOP_TIME_MAX, 0 }; +static struct timeval player_pause_timeout = { PLAYER_PAUSE_TIMEOUT, 0 }; // PLAYER_WRITE_BEHIND_MAX converted to clock ticks static int pb_write_deficit_max; @@ -324,10 +317,10 @@ static struct player_history *history; /* -------------------------------- Forwards -------------------------------- */ static void -playback_abort(void); +pb_abort(void); static void -playback_suspend(void); +pb_suspend(void); /* ----------------------------- Volume helpers ----------------------------- */ @@ -452,7 +445,7 @@ scrobble_cb(void *arg) static void pause_timer_cb(int fd, short what, void *arg) { - playback_abort(); + pb_abort(); } // Callback from the worker thread. Here the heavy lifting is done: updating the @@ -988,7 +981,7 @@ event_play_end() { DPRINTF(E_DBG, L_PLAYER, "event_play_end()\n"); - playback_abort(); + pb_abort(); } // Stuff to do when playback of current track ends @@ -1157,13 +1150,13 @@ playback_cb(int fd, short what, void *arg) if (pb_write_recovery) { DPRINTF(E_LOG, L_PLAYER, "Permanent output delay detected (behind=%" PRIu64 ", max=%d), aborting\n", overrun, pb_write_deficit_max); - playback_abort(); + pb_abort(); return; } DPRINTF(E_LOG, L_PLAYER, "Output delay detected (behind=%" PRIu64 ", max=%d), resetting all outputs\n", overrun, pb_write_deficit_max); pb_write_recovery = true; - playback_suspend(); + pb_suspend(); return; } else @@ -1229,7 +1222,7 @@ playback_cb(int fd, short what, void *arg) DPRINTF(E_LOG, L_PLAYER, "Source is not providing sufficient data, temporarily suspending playback (deficit=%zu/%zu bytes)\n", pb_session.read_deficit, pb_session.read_deficit_max); - playback_suspend(); + pb_suspend(); } } @@ -1372,7 +1365,7 @@ device_streaming_cb(struct output_device *device, enum output_device_state statu speaker_deselect_output(device); if (output_sessions == 0) - playback_abort(); + pb_abort(); } else if (status == OUTPUT_STATE_STOPPED) { @@ -1400,7 +1393,25 @@ device_command_cb(struct output_device *device, enum output_device_state status) if (status == OUTPUT_STATE_FAILED) device_streaming_cb(device, status); - // Used by playback_suspend - is basically the bottom half + out: + commands_exec_end(cmdbase, 0); +} + +static void +device_flush_cb(struct output_device *device, enum output_device_state status) +{ + if (!device) + { + DPRINTF(E_LOG, L_PLAYER, "Output device disappeared before flush completion!\n"); + goto out; + } + + DPRINTF(E_DBG, L_PLAYER, "Callback from %s to device_flush_cb (status %d)\n", outputs_name(device->type), status); + + if (status == OUTPUT_STATE_FAILED) + device_streaming_cb(device, status); + + // Used by pb_suspend - is basically the bottom half if (player_flush_pending > 0) { player_flush_pending--; @@ -1408,6 +1419,8 @@ device_command_cb(struct output_device *device, enum output_device_state status) input_buffer_full_cb(player_playback_start); } + outputs_device_stop_delayed(device, device_streaming_cb); + out: commands_exec_end(cmdbase, 0); } @@ -1578,6 +1591,8 @@ player_pmap(void *p) return "device_streaming_cb"; else if (p == device_command_cb) return "device_command_cb"; + else if (p == device_flush_cb) + return "device_flush_cb"; else if (p == device_shutdown_cb) return "device_shutdown_cb"; else @@ -1587,14 +1602,15 @@ player_pmap(void *p) /* ------------------------- Internal playback routines --------------------- */ static int -playback_timer_start(void) +pb_timer_start(void) { struct itimerspec tick; int ret; - // The pause timer will be active if we have recently paused, but now that - // the playback loop has been kicked off, we no longer want that - event_del(player_abort_timeout_ev); + // The stop timers will be active if we have recently paused, but now that the + // playback loop has been kicked off, we deactivate them + event_del(player_pause_timeout_ev); + outputs_stop_delayed_cancel(); ret = event_add(pb_timer_ev, NULL); if (ret < 0) @@ -1623,7 +1639,7 @@ playback_timer_start(void) } static int -playback_timer_stop(void) +pb_timer_stop(void) { struct itimerspec tick; int ret; @@ -1648,7 +1664,7 @@ playback_timer_stop(void) // Initiates the session and starts the input source static int -playback_session_start(struct db_queue_item *queue_item, uint32_t seek_ms) +pb_session_start(struct db_queue_item *queue_item, uint32_t seek_ms) { struct player_source *ps; int ret; @@ -1673,45 +1689,45 @@ playback_session_start(struct db_queue_item *queue_item, uint32_t seek_ms) // The input source is now open and ready, but we might actually be paused. So // we activate the below event in case the user never starts us again - event_add(player_abort_timeout_ev, &player_pause_timeout); + event_add(player_pause_timeout_ev, &player_pause_timeout); return ret; } // Stops input source and deallocates pb_session content static void -playback_session_stop(void) +pb_session_stop(void) { source_stop(); session_stop(); - playback_timer_stop(); + pb_timer_stop(); status_update(PLAY_STOPPED); } static void -playback_abort(void) +pb_abort(void) { - outputs_playback_stop(); + // Immediate stop of all outputs + outputs_stop(device_streaming_cb); + outputs_metadata_purge(); - playback_session_stop(); + pb_session_stop(); if (!clear_queue_on_stop_disabled) db_queue_clear(0); - - outputs_metadata_purge(); } // Temporarily suspends/resets playback, used when input buffer underruns or in // case of problems writing to the outputs static void -playback_suspend(void) +pb_suspend(void) { - player_flush_pending = outputs_flush(device_command_cb); + player_flush_pending = outputs_flush(device_flush_cb); - playback_timer_stop(); + pb_timer_stop(); status_update(PLAY_PAUSED); @@ -1818,17 +1834,13 @@ playback_stop(void *arg, int *retval) // We may be restarting very soon, so we don't bring the devices to a full // stop just yet; this saves time when restarting, which is nicer for the user - *retval = outputs_flush(device_command_cb); - - // Stops the input - playback_session_stop(); - - status_update(PLAY_STOPPED); - + *retval = outputs_flush(device_flush_cb); outputs_metadata_purge(); - // In case we aren't restarting soon we want to make a full stop - event_add(player_abort_timeout_ev, &player_stop_timeout); + // Stops the input + pb_session_stop(); + + status_update(PLAY_STOPPED); // We're async if we need to flush devices if (*retval > 0) @@ -1837,12 +1849,21 @@ playback_stop(void *arg, int *retval) return COMMAND_END; } +static enum command_state +playback_abort(void *arg, int *retval) +{ + pb_abort(); + + *retval = 0; + return COMMAND_END; +} + static enum command_state playback_start_bh(void *arg, int *retval) { int ret; - ret = playback_timer_start(); + ret = pb_timer_start(); if (ret < 0) goto error; @@ -1852,7 +1873,7 @@ playback_start_bh(void *arg, int *retval) return COMMAND_END; error: - playback_abort(); + pb_abort(); *retval = -1; return COMMAND_END; } @@ -1914,7 +1935,7 @@ playback_start_item(void *arg, int *retval) } } - ret = playback_session_start(queue_item, seek_ms); + ret = pb_session_start(queue_item, seek_ms); if (ret < 0) { *retval = -1; @@ -2033,7 +2054,7 @@ playback_prev_bh(void *arg, int *retval) int ret; // outputs_flush() in playback_pause() may have a caused a failure callback - // from the output, which in streaming_cb() can cause playback_abort() + // from the output, which in streaming_cb() can cause pb_abort() if (player_state == PLAY_STOPPED) { goto error; @@ -2055,7 +2076,7 @@ playback_prev_bh(void *arg, int *retval) goto error; } - ret = playback_session_start(queue_item, 0); + ret = pb_session_start(queue_item, 0); free_queue_item(queue_item, 0); if (ret < 0) { @@ -2070,7 +2091,7 @@ playback_prev_bh(void *arg, int *retval) return COMMAND_END; error: - playback_abort(); + pb_abort(); *retval = -1; return COMMAND_END; } @@ -2083,7 +2104,7 @@ playback_next_bh(void *arg, int *retval) int id; // outputs_flush() in playback_pause() may have a caused a failure callback - // from the output, which in streaming_cb() can cause playback_abort() + // from the output, which in streaming_cb() can cause pb_abort() if (player_state == PLAY_STOPPED) { goto error; @@ -2108,7 +2129,7 @@ playback_next_bh(void *arg, int *retval) goto error; } - ret = playback_session_start(queue_item, 0); + ret = pb_session_start(queue_item, 0); free_queue_item(queue_item, 0); if (ret < 0) { @@ -2123,7 +2144,7 @@ playback_next_bh(void *arg, int *retval) return COMMAND_END; error: - playback_abort(); + pb_abort(); *retval = -1; return COMMAND_END; } @@ -2136,7 +2157,7 @@ playback_seek_bh(void *arg, int *retval) int ret; // outputs_flush() in playback_pause() may have a caused a failure callback - // from the output, which in streaming_cb() can cause playback_abort() + // from the output, which in streaming_cb() can cause pb_abort() if (player_state == PLAY_STOPPED) { goto error; @@ -2149,7 +2170,7 @@ playback_seek_bh(void *arg, int *retval) goto error; } - ret = playback_session_start(queue_item, cmdarg->intval); + ret = pb_session_start(queue_item, cmdarg->intval); free_queue_item(queue_item, 0); if (ret < 0) { @@ -2164,7 +2185,7 @@ playback_seek_bh(void *arg, int *retval) return COMMAND_END; error: - playback_abort(); + pb_abort(); *retval = -1; return COMMAND_END; } @@ -2176,7 +2197,7 @@ playback_pause_bh(void *arg, int *retval) int ret; // outputs_flush() in playback_pause() may have a caused a failure callback - // from the output, which in streaming_cb() can cause playback_abort() + // from the output, which in streaming_cb() can cause pb_abort() if (player_state == PLAY_STOPPED) { goto error; @@ -2189,7 +2210,7 @@ playback_pause_bh(void *arg, int *retval) goto error; } - ret = playback_session_start(queue_item, pb_session.playing_now->pos_ms); + ret = pb_session_start(queue_item, pb_session.playing_now->pos_ms); free_queue_item(queue_item, 0); if (ret < 0) { @@ -2205,7 +2226,7 @@ playback_pause_bh(void *arg, int *retval) return COMMAND_END; error: - playback_abort(); + pb_abort(); *retval = -1; return COMMAND_END; } @@ -2225,10 +2246,9 @@ playback_pause(void *arg, int *retval) return COMMAND_END; } - playback_timer_stop(); - - *retval = outputs_flush(device_command_cb); + pb_timer_stop(); + *retval = outputs_flush(device_flush_cb); outputs_metadata_purge(); // We're async if we need to flush devices @@ -2859,6 +2879,15 @@ player_playback_stop(void) return ret; } +int +player_playback_abort(void) +{ + int ret; + + ret = commands_exec_sync(cmdbase, playback_abort, NULL, NULL); + return ret; +} + int player_playback_pause(void) { @@ -3258,7 +3287,7 @@ player_init(void) } CHECK_NULL(L_PLAYER, evbase_player = event_base_new()); - CHECK_NULL(L_PLAYER, player_abort_timeout_ev = evtimer_new(evbase_player, pause_timer_cb, NULL)); + CHECK_NULL(L_PLAYER, player_pause_timeout_ev = evtimer_new(evbase_player, pause_timer_cb, NULL)); #ifdef HAVE_TIMERFD CHECK_NULL(L_PLAYER, pb_timer_ev = event_new(evbase_player, pb_timer_fd, EV_READ | EV_PERSIST, playback_cb, NULL)); #else @@ -3317,7 +3346,7 @@ player_deinit(void) { int ret; - player_playback_stop(); + player_playback_abort(); #ifdef HAVE_TIMERFD close(pb_timer_fd);