From 36755031cb4c9f58fcfdb37497f4a7f3452a808e Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Tue, 22 Mar 2016 22:59:50 +0100 Subject: [PATCH] [outputs] Make a wrapper for mp3 streaming so it can be included in the generic outputs interface and so special handling in player.c can be removed --- src/Makefile.am | 2 +- src/httpd_streaming.c | 56 ++++++++++++++++++++--------------------- src/httpd_streaming.h | 3 +++ src/outputs.c | 12 ++++++--- src/outputs.h | 2 +- src/outputs/streaming.c | 35 ++++++++++++++++++++++++++ src/player.c | 28 +++------------------ src/player.h | 7 ------ 8 files changed, 80 insertions(+), 65 deletions(-) create mode 100644 src/outputs/streaming.c diff --git a/src/Makefile.am b/src/Makefile.am index cce264bf..168c619f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -110,7 +110,7 @@ forked_daapd_SOURCES = main.c \ outputs.h outputs.c \ laudio_dummy.c \ laudio.c laudio.h \ - outputs/raop.c \ + outputs/raop.c outputs/streaming.c \ evrtsp/rtsp.c evrtp/evrtsp.h evrtsp/rtsp-internal.h evrtsp/log.h \ $(SPOTIFY_SRC) \ $(LASTFM_SRC) \ diff --git a/src/httpd_streaming.c b/src/httpd_streaming.c index 5cb83277..f00ae1f8 100644 --- a/src/httpd_streaming.c +++ b/src/httpd_streaming.c @@ -52,7 +52,7 @@ extern struct event_base *evbase_httpd; // Should prevent that we keep transcoding to dead connections #define STREAMING_CONNECTION_TIMEOUT 60 -// Linked list of Icecast requests +// Linked list of mp3 streaming requests struct streaming_session { struct evhttp_request *req; struct streaming_session *next; @@ -108,7 +108,6 @@ streaming_fail_cb(struct evhttp_connection *evcon, void *arg) { DPRINTF(E_INFO, L_STREAMING, "No more clients, will stop streaming\n"); event_del(streamingev); - player_streaming_stop(); } } @@ -122,16 +121,16 @@ streaming_send_cb(evutil_socket_t fd, short event, void *arg) int len; int ret; - if (!streaming_sessions) - return; - - // Callback from player (EV_READ) + // Player wrote data to the pipe (EV_READ) if (event & EV_READ) { ret = read(streaming_pipe[0], &streaming_rawbuf, STREAMING_RAWBUF_SIZE); if (ret < 0) return; + if (!streaming_sessions) + return; + decoded = transcode_raw2frame(streaming_rawbuf, STREAMING_RAWBUF_SIZE); if (!decoded) { @@ -153,6 +152,9 @@ streaming_send_cb(evutil_socket_t fd, short event, void *arg) player_get_status(&streaming_player_status); } + if (!streaming_sessions) + return; + if (streaming_player_status.status != PLAY_PAUSED) return; @@ -177,22 +179,6 @@ streaming_send_cb(evutil_socket_t fd, short event, void *arg) evbuffer_free(evbuf); } -// Thread: player -static int -streaming_cb(uint8_t *rawbuf, size_t size) -{ - if (size != STREAMING_RAWBUF_SIZE) - { - DPRINTF(E_LOG, L_STREAMING, "Bug! Buffer size in streaming_cb does not equal input from player\n"); - return -1; - } - - if (write(streaming_pipe[1], rawbuf, size) < 0) - return -1; - - return 0; -} - // Thread: player (not fully thread safe, but hey...) static void player_change_cb(enum listener_event_type type) @@ -200,6 +186,20 @@ player_change_cb(enum listener_event_type type) streaming_player_changed = 1; } +// Thread: player (also prone to race conditions, mostly during deinit) +void +streaming_write(uint8_t *buf, uint64_t rtptime) +{ + int ret; + + if (!streaming_sessions) + return; + + ret = write(streaming_pipe[1], buf, STREAMING_RAWBUF_SIZE); + if (ret < 0) + DPRINTF(E_LOG, L_STREAMING, "Error writing to streaming pipe\n"); +} + int streaming_is_request(struct evhttp_request *req, char *uri) { @@ -269,8 +269,6 @@ streaming_request(struct evhttp_request *req) evhttp_connection_set_timeout(evcon, STREAMING_CONNECTION_TIMEOUT); evhttp_connection_set_closecb(evcon, streaming_fail_cb, session); - player_streaming_start(streaming_cb); - return 0; } @@ -403,18 +401,20 @@ streaming_deinit(void) if (!streaming_initialized) return; - player_streaming_stop(); - - event_free(streamingev); + session = streaming_sessions; + streaming_sessions = NULL; // Stops writing and sending next = NULL; - for (session = streaming_sessions; session; session = next) + while (session) { evhttp_send_reply_end(session->req); next = session->next; free(session); + session = next; } + event_free(streamingev); + listener_remove(player_change_cb); close(streaming_pipe[0]); diff --git a/src/httpd_streaming.h b/src/httpd_streaming.h index 5e1486b6..2fcbc805 100644 --- a/src/httpd_streaming.h +++ b/src/httpd_streaming.h @@ -10,6 +10,9 @@ * if a suitable ffmpeg/libav encoder is not present at runtime. */ +void +streaming_write(uint8_t *buf, uint64_t rtptime); + int streaming_is_request(struct evhttp_request *req, char *uri); diff --git a/src/outputs.c b/src/outputs.c index c78e94de..01356357 100644 --- a/src/outputs.c +++ b/src/outputs.c @@ -35,8 +35,8 @@ extern struct output_definition output_raop; #ifdef CHROMECAST extern struct output_definition output_cast; #endif -/* TODO extern struct output_definition output_streaming; +/* TODO #ifdef ALSA extern struct output_definition output_alsa; #endif @@ -52,8 +52,8 @@ static struct output_definition *outputs[] = { #ifdef CHROMECAST &output_cast, #endif -/* TODO &output_streaming, +/* TODO #ifdef ALSA &output_alsa, #endif @@ -338,6 +338,9 @@ outputs_init(void) return -1; } + if (!outputs[i]->init) + continue; + ret = outputs[i]->init(); if (ret < 0) outputs[i]->disabled = 1; @@ -358,7 +361,10 @@ outputs_deinit(void) for (i = 0; outputs[i]; i++) { - if (!outputs[i]->disabled) + if (outputs[i]->disabled) + continue; + + if (outputs[i]->deinit) outputs[i]->deinit(); } } diff --git a/src/outputs.h b/src/outputs.h index 63df7158..ca21e457 100644 --- a/src/outputs.h +++ b/src/outputs.h @@ -23,8 +23,8 @@ enum output_types #ifdef CHROMECAST OUTPUT_TYPE_CAST, #endif -/* TODO OUTPUT_TYPE_STREAMING, +/* TODO OUTPUT_TYPE_ALSA, OUTPUT_TYPE_OSS, OUTPUT_TYPE_DUMMY, diff --git a/src/outputs/streaming.c b/src/outputs/streaming.c new file mode 100644 index 00000000..00a963bc --- /dev/null +++ b/src/outputs/streaming.c @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 Espen Jürgensen + * + * 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 + +#include "outputs.h" +#include "httpd_streaming.h" + + +struct output_definition output_streaming = +{ + .name = "mp3 streaming", + .type = OUTPUT_TYPE_STREAMING, + .priority = 0, + .disabled = 0, + .write = streaming_write, +}; diff --git a/src/player.c b/src/player.c index f0ad0b35..059381b0 100644 --- a/src/player.c +++ b/src/player.c @@ -291,9 +291,6 @@ static int laudio_selected; static int laudio_volume; static int laudio_relvol; static int output_sessions; -static int streaming_selected; - -static player_streaming_cb streaming_write; /* Commands */ static struct player_command *cur_cmd; @@ -1606,14 +1603,10 @@ playback_write(void) return; } - if (streaming_selected) - streaming_write(rawbuf, sizeof(rawbuf)); - if (laudio_status & LAUDIO_F_STARTED) laudio_write(rawbuf, last_rtptime); - if (output_sessions > 0) - outputs_write(rawbuf, last_rtptime); + outputs_write(rawbuf, last_rtptime); } static void @@ -2170,8 +2163,7 @@ playback_abort(void) if (laudio_status != LAUDIO_CLOSED) laudio_close(); - if (output_sessions > 0) - outputs_playback_stop(); + outputs_playback_stop(); pb_timer_stop(); @@ -2423,8 +2415,7 @@ playback_start_bh(struct player_command *cmd) goto out_fail; /* Everything OK, start outputs */ - if (output_sessions > 0) - outputs_playback_start(last_rtptime + AIRTUNES_V2_PACKET_SAMPLES, &pb_pos_stamp); + outputs_playback_start(last_rtptime + AIRTUNES_V2_PACKET_SAMPLES, &pb_pos_stamp); status_update(PLAY_PLAYING); @@ -4014,19 +4005,6 @@ player_playback_prev(void) return ret; } -void -player_streaming_start(player_streaming_cb cb) -{ - streaming_write = cb; - streaming_selected = 1; -} - -void -player_streaming_stop(void) -{ - streaming_selected = 0; -} - void player_speaker_enumerate(spk_enum_cb cb, void *arg) diff --git a/src/player.h b/src/player.h index eef8d5b9..c69a69fa 100644 --- a/src/player.h +++ b/src/player.h @@ -70,7 +70,6 @@ struct player_status { }; typedef void (*spk_enum_cb)(uint64_t id, const char *name, int relvol, struct spk_flags flags, void *arg); -typedef int (*player_streaming_cb)(uint8_t *rawbuf, size_t size); struct player_history { @@ -131,12 +130,6 @@ player_playback_next(void); int player_playback_prev(void); -void -player_streaming_start(player_streaming_cb cb); - -void -player_streaming_stop(void); - int player_volume_set(int vol);