[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
This commit is contained in:
ejurgensen 2016-03-22 22:59:50 +01:00
parent a07c24d625
commit 36755031cb
8 changed files with 80 additions and 65 deletions

View File

@ -110,7 +110,7 @@ forked_daapd_SOURCES = main.c \
outputs.h outputs.c \ outputs.h outputs.c \
laudio_dummy.c \ laudio_dummy.c \
laudio.c laudio.h \ 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 \ evrtsp/rtsp.c evrtp/evrtsp.h evrtsp/rtsp-internal.h evrtsp/log.h \
$(SPOTIFY_SRC) \ $(SPOTIFY_SRC) \
$(LASTFM_SRC) \ $(LASTFM_SRC) \

View File

@ -52,7 +52,7 @@ extern struct event_base *evbase_httpd;
// Should prevent that we keep transcoding to dead connections // Should prevent that we keep transcoding to dead connections
#define STREAMING_CONNECTION_TIMEOUT 60 #define STREAMING_CONNECTION_TIMEOUT 60
// Linked list of Icecast requests // Linked list of mp3 streaming requests
struct streaming_session { struct streaming_session {
struct evhttp_request *req; struct evhttp_request *req;
struct streaming_session *next; 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"); DPRINTF(E_INFO, L_STREAMING, "No more clients, will stop streaming\n");
event_del(streamingev); event_del(streamingev);
player_streaming_stop();
} }
} }
@ -122,16 +121,16 @@ streaming_send_cb(evutil_socket_t fd, short event, void *arg)
int len; int len;
int ret; int ret;
if (!streaming_sessions) // Player wrote data to the pipe (EV_READ)
return;
// Callback from player (EV_READ)
if (event & EV_READ) if (event & EV_READ)
{ {
ret = read(streaming_pipe[0], &streaming_rawbuf, STREAMING_RAWBUF_SIZE); ret = read(streaming_pipe[0], &streaming_rawbuf, STREAMING_RAWBUF_SIZE);
if (ret < 0) if (ret < 0)
return; return;
if (!streaming_sessions)
return;
decoded = transcode_raw2frame(streaming_rawbuf, STREAMING_RAWBUF_SIZE); decoded = transcode_raw2frame(streaming_rawbuf, STREAMING_RAWBUF_SIZE);
if (!decoded) if (!decoded)
{ {
@ -153,6 +152,9 @@ streaming_send_cb(evutil_socket_t fd, short event, void *arg)
player_get_status(&streaming_player_status); player_get_status(&streaming_player_status);
} }
if (!streaming_sessions)
return;
if (streaming_player_status.status != PLAY_PAUSED) if (streaming_player_status.status != PLAY_PAUSED)
return; return;
@ -177,22 +179,6 @@ streaming_send_cb(evutil_socket_t fd, short event, void *arg)
evbuffer_free(evbuf); 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...) // Thread: player (not fully thread safe, but hey...)
static void static void
player_change_cb(enum listener_event_type type) player_change_cb(enum listener_event_type type)
@ -200,6 +186,20 @@ player_change_cb(enum listener_event_type type)
streaming_player_changed = 1; 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 int
streaming_is_request(struct evhttp_request *req, char *uri) 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_timeout(evcon, STREAMING_CONNECTION_TIMEOUT);
evhttp_connection_set_closecb(evcon, streaming_fail_cb, session); evhttp_connection_set_closecb(evcon, streaming_fail_cb, session);
player_streaming_start(streaming_cb);
return 0; return 0;
} }
@ -403,18 +401,20 @@ streaming_deinit(void)
if (!streaming_initialized) if (!streaming_initialized)
return; return;
player_streaming_stop(); session = streaming_sessions;
streaming_sessions = NULL; // Stops writing and sending
event_free(streamingev);
next = NULL; next = NULL;
for (session = streaming_sessions; session; session = next) while (session)
{ {
evhttp_send_reply_end(session->req); evhttp_send_reply_end(session->req);
next = session->next; next = session->next;
free(session); free(session);
session = next;
} }
event_free(streamingev);
listener_remove(player_change_cb); listener_remove(player_change_cb);
close(streaming_pipe[0]); close(streaming_pipe[0]);

View File

@ -10,6 +10,9 @@
* if a suitable ffmpeg/libav encoder is not present at runtime. * if a suitable ffmpeg/libav encoder is not present at runtime.
*/ */
void
streaming_write(uint8_t *buf, uint64_t rtptime);
int int
streaming_is_request(struct evhttp_request *req, char *uri); streaming_is_request(struct evhttp_request *req, char *uri);

View File

@ -35,8 +35,8 @@ extern struct output_definition output_raop;
#ifdef CHROMECAST #ifdef CHROMECAST
extern struct output_definition output_cast; extern struct output_definition output_cast;
#endif #endif
/* TODO
extern struct output_definition output_streaming; extern struct output_definition output_streaming;
/* TODO
#ifdef ALSA #ifdef ALSA
extern struct output_definition output_alsa; extern struct output_definition output_alsa;
#endif #endif
@ -52,8 +52,8 @@ static struct output_definition *outputs[] = {
#ifdef CHROMECAST #ifdef CHROMECAST
&output_cast, &output_cast,
#endif #endif
/* TODO
&output_streaming, &output_streaming,
/* TODO
#ifdef ALSA #ifdef ALSA
&output_alsa, &output_alsa,
#endif #endif
@ -338,6 +338,9 @@ outputs_init(void)
return -1; return -1;
} }
if (!outputs[i]->init)
continue;
ret = outputs[i]->init(); ret = outputs[i]->init();
if (ret < 0) if (ret < 0)
outputs[i]->disabled = 1; outputs[i]->disabled = 1;
@ -358,7 +361,10 @@ outputs_deinit(void)
for (i = 0; outputs[i]; i++) for (i = 0; outputs[i]; i++)
{ {
if (!outputs[i]->disabled) if (outputs[i]->disabled)
continue;
if (outputs[i]->deinit)
outputs[i]->deinit(); outputs[i]->deinit();
} }
} }

View File

@ -23,8 +23,8 @@ enum output_types
#ifdef CHROMECAST #ifdef CHROMECAST
OUTPUT_TYPE_CAST, OUTPUT_TYPE_CAST,
#endif #endif
/* TODO
OUTPUT_TYPE_STREAMING, OUTPUT_TYPE_STREAMING,
/* TODO
OUTPUT_TYPE_ALSA, OUTPUT_TYPE_ALSA,
OUTPUT_TYPE_OSS, OUTPUT_TYPE_OSS,
OUTPUT_TYPE_DUMMY, OUTPUT_TYPE_DUMMY,

35
src/outputs/streaming.c Normal file
View File

@ -0,0 +1,35 @@
/*
* Copyright (C) 2016 Espen Jürgensen <espenjurgensen@gmail.com>
*
* 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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#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,
};

View File

@ -291,9 +291,6 @@ static int laudio_selected;
static int laudio_volume; static int laudio_volume;
static int laudio_relvol; static int laudio_relvol;
static int output_sessions; static int output_sessions;
static int streaming_selected;
static player_streaming_cb streaming_write;
/* Commands */ /* Commands */
static struct player_command *cur_cmd; static struct player_command *cur_cmd;
@ -1606,14 +1603,10 @@ playback_write(void)
return; return;
} }
if (streaming_selected)
streaming_write(rawbuf, sizeof(rawbuf));
if (laudio_status & LAUDIO_F_STARTED) if (laudio_status & LAUDIO_F_STARTED)
laudio_write(rawbuf, last_rtptime); laudio_write(rawbuf, last_rtptime);
if (output_sessions > 0) outputs_write(rawbuf, last_rtptime);
outputs_write(rawbuf, last_rtptime);
} }
static void static void
@ -2170,8 +2163,7 @@ playback_abort(void)
if (laudio_status != LAUDIO_CLOSED) if (laudio_status != LAUDIO_CLOSED)
laudio_close(); laudio_close();
if (output_sessions > 0) outputs_playback_stop();
outputs_playback_stop();
pb_timer_stop(); pb_timer_stop();
@ -2423,8 +2415,7 @@ playback_start_bh(struct player_command *cmd)
goto out_fail; goto out_fail;
/* Everything OK, start outputs */ /* 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); status_update(PLAY_PLAYING);
@ -4014,19 +4005,6 @@ player_playback_prev(void)
return ret; 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 void
player_speaker_enumerate(spk_enum_cb cb, void *arg) player_speaker_enumerate(spk_enum_cb cb, void *arg)

View File

@ -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 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 struct player_history
{ {
@ -131,12 +130,6 @@ player_playback_next(void);
int int
player_playback_prev(void); player_playback_prev(void);
void
player_streaming_start(player_streaming_cb cb);
void
player_streaming_stop(void);
int int
player_volume_set(int vol); player_volume_set(int vol);