[player/outputs] Implement changed output interfaces in most backends

Still missing cast, alsa and pulseaudio, but these can so far just be
disabled with configure.

Otherwise still mostly untested.
This commit is contained in:
ejurgensen
2019-02-10 23:27:29 +01:00
parent 14a6d318f0
commit e99f20992e
12 changed files with 429 additions and 529 deletions

View File

@@ -105,9 +105,7 @@ struct alsa_session
struct event *deferredev;
output_status_cb defer_cb;
/* Do not dereference - only passed to the status cb */
struct output_device *device;
struct output_session *output_session;
output_status_cb status_cb;
struct alsa_session *next;
@@ -147,7 +145,6 @@ alsa_session_free(struct alsa_session *as)
prebuf_free(as);
free(as->output_session);
free(as);
}
@@ -169,6 +166,8 @@ alsa_session_cleanup(struct alsa_session *as)
s->next = as->next;
}
as->device->session = NULL;
alsa_session_free(as);
}
@@ -177,21 +176,7 @@ alsa_session_make(struct output_device *device, output_status_cb cb)
{
struct alsa_session *as;
as = calloc(1, sizeof(struct alsa_session));
if (!as)
{
DPRINTF(E_LOG, L_LAUDIO, "Out of memory for ALSA session (as)\n");
return NULL;
}
as->output_session = calloc(1, sizeof(struct output_session));
if (!as->output_session)
{
DPRINTF(E_LOG, L_LAUDIO, "Out of memory for ALSA session (output_session)\n");
goto failure_cleanup;
}
as->output_session->session = as;
as->output_session->type = device->type;
CHECK_NULL(L_LAUDIO, as = calloc(1, sizeof(struct alsa_session)));
as->deferredev = evtimer_new(evbase_player, defer_cb, as);
if (!as->deferredev)
@@ -210,6 +195,9 @@ alsa_session_make(struct output_device *device, output_status_cb cb)
as->next = sessions;
sessions = as;
device->session = as;
return as;
failure_cleanup:

View File

@@ -177,9 +177,7 @@ struct cast_session
char *session_id;
int media_session_id;
/* Do not dereference - only passed to the status cb */
struct output_device *device;
struct output_session *output_session;
output_status_cb status_cb;
struct cast_session *next;
@@ -1327,7 +1325,6 @@ cast_device_cb(const char *name, const char *type, const char *domain, const cha
static struct cast_session *
cast_session_make(struct output_device *device, int family, output_status_cb cb)
{
struct output_session *os;
struct cast_session *cs;
const char *proto;
const char *err;
@@ -1359,25 +1356,8 @@ cast_session_make(struct output_device *device, int family, output_status_cb cb)
return NULL;
}
os = calloc(1, sizeof(struct output_session));
if (!os)
{
DPRINTF(E_LOG, L_CAST, "Out of memory (os)\n");
return NULL;
}
CHECK_NULL(L_CAST, cs = calloc(1, sizeof(struct cast_session)));
cs = calloc(1, sizeof(struct cast_session));
if (!cs)
{
DPRINTF(E_LOG, L_CAST, "Out of memory (cs)\n");
free(os);
return NULL;
}
os->session = cs;
os->type = device->type;
cs->output_session = os;
cs->state = CAST_STATE_DISCONNECTED;
cs->device = device;
cs->status_cb = cb;
@@ -1440,6 +1420,8 @@ cast_session_make(struct output_device *device, int family, output_status_cb cb)
cs->next = sessions;
sessions = cs;
device->session = cs;
proto = gnutls_protocol_get_name(gnutls_protocol_get_version(cs->tls_session));
DPRINTF(E_INFO, L_CAST, "Connection to '%s' established using %s\n", cs->devname, proto);

View File

@@ -47,9 +47,7 @@ struct dummy_session
struct event *deferredev;
output_status_cb defer_cb;
/* Do not dereference - only passed to the status cb */
struct output_device *device;
struct output_session *output_session;
output_status_cb status_cb;
};
@@ -72,7 +70,6 @@ dummy_session_free(struct dummy_session *ds)
event_free(ds->deferredev);
free(ds->output_session);
free(ds);
}
@@ -82,27 +79,20 @@ dummy_session_cleanup(struct dummy_session *ds)
// Normally some here code to remove from linked list - here we just say:
sessions = NULL;
ds->device->session = NULL;
dummy_session_free(ds);
}
static struct dummy_session *
dummy_session_make(struct output_device *device, output_status_cb cb)
{
struct output_session *os;
struct dummy_session *ds;
os = calloc(1, sizeof(struct output_session));
if (!os)
{
DPRINTF(E_LOG, L_LAUDIO, "Out of memory for dummy session (os)\n");
return NULL;
}
ds = calloc(1, sizeof(struct dummy_session));
if (!ds)
{
DPRINTF(E_LOG, L_LAUDIO, "Out of memory for dummy session (as)\n");
free(os);
return NULL;
}
@@ -110,21 +100,18 @@ dummy_session_make(struct output_device *device, output_status_cb cb)
if (!ds->deferredev)
{
DPRINTF(E_LOG, L_LAUDIO, "Out of memory for dummy deferred event\n");
free(os);
free(ds);
return NULL;
}
os->session = ds;
os->type = device->type;
ds->output_session = os;
ds->state = OUTPUT_STATE_CONNECTED;
ds->device = device;
ds->status_cb = cb;
sessions = ds;
device->session = ds;
return ds;
}
@@ -139,7 +126,7 @@ defer_cb(int fd, short what, void *arg)
struct dummy_session *ds = arg;
if (ds->defer_cb)
ds->defer_cb(ds->device, ds->output_session, ds->state);
ds->defer_cb(ds->device, ds->state);
if (ds->state == OUTPUT_STATE_STOPPED)
dummy_session_cleanup(ds);
@@ -157,7 +144,7 @@ dummy_status(struct dummy_session *ds)
/* ------------------ INTERFACE FUNCTIONS CALLED BY OUTPUTS.C --------------- */
static int
dummy_device_start(struct output_device *device, output_status_cb cb, uint64_t rtptime)
dummy_device_start(struct output_device *device, output_status_cb cb)
{
struct dummy_session *ds;
@@ -170,13 +157,15 @@ dummy_device_start(struct output_device *device, output_status_cb cb, uint64_t r
return 0;
}
static void
dummy_device_stop(struct output_session *session)
static int
dummy_device_stop(struct output_device *device, output_status_cb cb)
{
struct dummy_session *ds = session->session;
struct dummy_session *ds = device->session;
ds->state = OUTPUT_STATE_STOPPED;
dummy_status(ds);
return 0;
}
static int
@@ -199,13 +188,11 @@ dummy_device_probe(struct output_device *device, output_status_cb cb)
static int
dummy_device_volume_set(struct output_device *device, output_status_cb cb)
{
struct dummy_session *ds;
struct dummy_session *ds = device->session;
if (!device->session || !device->session->session)
if (!ds)
return 0;
ds = device->session->session;
ds->status_cb = cb;
dummy_status(ds);
@@ -213,15 +200,11 @@ dummy_device_volume_set(struct output_device *device, output_status_cb cb)
}
static void
dummy_playback_start(uint64_t next_pkt, struct timespec *ts)
dummy_device_set_cb(struct output_device *device, output_status_cb cb)
{
struct dummy_session *ds = sessions;
struct dummy_session *ds = device->session;
if (!sessions)
return;
ds->state = OUTPUT_STATE_STREAMING;
dummy_status(ds);
ds->status_cb = cb;
}
static void
@@ -236,14 +219,6 @@ dummy_playback_stop(void)
dummy_status(ds);
}
static void
dummy_set_status_cb(struct output_session *session, output_status_cb cb)
{
struct dummy_session *ds = session->session;
ds->status_cb = cb;
}
static int
dummy_init(void)
{
@@ -298,7 +273,6 @@ struct output_definition output_dummy =
.device_stop = dummy_device_stop,
.device_probe = dummy_device_probe,
.device_volume_set = dummy_device_volume_set,
.playback_start = dummy_playback_start,
.device_set_cb = dummy_device_set_cb,
.playback_stop = dummy_playback_stop,
.status_cb = dummy_set_status_cb,
};

View File

@@ -45,10 +45,11 @@
struct fifo_packet
{
/* pcm data */
uint8_t samples[FIFO_PACKET_SIZE];
uint8_t *samples;
size_t samples_size;
/* RTP-time of the first sample*/
uint64_t rtptime;
/* Presentation timestamp of the first sample */
struct timespec pts;
struct fifo_packet *next;
struct fifo_packet *prev;
@@ -59,8 +60,11 @@ struct fifo_buffer
struct fifo_packet *head;
struct fifo_packet *tail;
};
static struct fifo_buffer buffer;
static struct media_quality fifo_quality = { 44100, 16, 2 };
static void
free_buffer()
@@ -94,9 +98,7 @@ struct fifo_session
struct event *deferredev;
output_status_cb defer_cb;
/* Do not dereference - only passed to the status cb */
struct output_device *device;
struct output_session *output_session;
output_status_cb status_cb;
};
@@ -242,7 +244,6 @@ fifo_session_free(struct fifo_session *fifo_session)
event_free(fifo_session->deferredev);
free(fifo_session->output_session);
free(fifo_session);
free_buffer();
}
@@ -253,43 +254,26 @@ fifo_session_cleanup(struct fifo_session *fifo_session)
// Normally some here code to remove from linked list - here we just say:
sessions = NULL;
fifo_session->device->session = NULL;
fifo_session_free(fifo_session);
}
static struct fifo_session *
fifo_session_make(struct output_device *device, output_status_cb cb)
{
struct output_session *output_session;
struct fifo_session *fifo_session;
output_session = calloc(1, sizeof(struct output_session));
if (!output_session)
{
DPRINTF(E_LOG, L_FIFO, "Out of memory (os)\n");
return NULL;
}
fifo_session = calloc(1, sizeof(struct fifo_session));
if (!fifo_session)
{
DPRINTF(E_LOG, L_FIFO, "Out of memory (fs)\n");
free(output_session);
return NULL;
}
CHECK_NULL(L_FIFO, fifo_session = calloc(1, sizeof(struct fifo_session)));
fifo_session->deferredev = evtimer_new(evbase_player, defer_cb, fifo_session);
if (!fifo_session->deferredev)
{
DPRINTF(E_LOG, L_FIFO, "Out of memory for fifo deferred event\n");
free(output_session);
free(fifo_session);
return NULL;
}
output_session->session = fifo_session;
output_session->type = device->type;
fifo_session->output_session = output_session;
fifo_session->state = OUTPUT_STATE_CONNECTED;
fifo_session->device = device;
fifo_session->status_cb = cb;
@@ -301,6 +285,8 @@ fifo_session_make(struct output_device *device, output_status_cb cb)
sessions = fifo_session;
device->session = fifo_session;
return fifo_session;
}
@@ -315,7 +301,7 @@ defer_cb(int fd, short what, void *arg)
struct fifo_session *ds = arg;
if (ds->defer_cb)
ds->defer_cb(ds->device, ds->output_session, ds->state);
ds->defer_cb(ds->device, ds->state);
if (ds->state == OUTPUT_STATE_STOPPED)
fifo_session_cleanup(ds);
@@ -333,11 +319,15 @@ fifo_status(struct fifo_session *fifo_session)
/* ------------------ INTERFACE FUNCTIONS CALLED BY OUTPUTS.C --------------- */
static int
fifo_device_start(struct output_device *device, output_status_cb cb, uint64_t rtptime)
fifo_device_start(struct output_device *device, output_status_cb cb)
{
struct fifo_session *fifo_session;
int ret;
ret = outputs_quality_subscribe(&fifo_quality);
if (ret < 0)
return -1;
fifo_session = fifo_session_make(device, cb);
if (!fifo_session)
return -1;
@@ -351,16 +341,22 @@ fifo_device_start(struct output_device *device, output_status_cb cb, uint64_t rt
return 0;
}
static void
fifo_device_stop(struct output_session *output_session)
static int
fifo_device_stop(struct output_device *device, output_status_cb cb)
{
struct fifo_session *fifo_session = output_session->session;
struct fifo_session *fifo_session = device->session;
outputs_quality_unsubscribe(&fifo_quality);
fifo_session->status_cb = cb;
fifo_close(fifo_session);
free_buffer();
fifo_session->state = OUTPUT_STATE_STOPPED;
fifo_status(fifo_session);
return 0;
}
static int
@@ -393,13 +389,11 @@ fifo_device_probe(struct output_device *device, output_status_cb cb)
static int
fifo_device_volume_set(struct output_device *device, output_status_cb cb)
{
struct fifo_session *fifo_session;
struct fifo_session *fifo_session = device->session;
if (!device->session || !device->session->session)
if (!fifo_session)
return 0;
fifo_session = device->session->session;
fifo_session->status_cb = cb;
fifo_status(fifo_session);
@@ -407,15 +401,11 @@ fifo_device_volume_set(struct output_device *device, output_status_cb cb)
}
static void
fifo_playback_start(uint64_t next_pkt, struct timespec *ts)
fifo_device_set_cb(struct output_device *device, output_status_cb cb)
{
struct fifo_session *fifo_session = sessions;
struct fifo_session *fifo_session = device->session;
if (!fifo_session)
return;
fifo_session->state = OUTPUT_STATE_STREAMING;
fifo_status(fifo_session);
fifo_session->status_cb = cb;
}
static void
@@ -433,7 +423,7 @@ fifo_playback_stop(void)
}
static int
fifo_flush(output_status_cb cb, uint64_t rtptime)
fifo_flush(output_status_cb cb)
{
struct fifo_session *fifo_session = sessions;
@@ -450,45 +440,59 @@ fifo_flush(output_status_cb cb, uint64_t rtptime)
}
static void
fifo_write(uint8_t *buf, uint64_t rtptime)
fifo_write(struct output_buffer *obuf)
{
struct fifo_session *fifo_session = sessions;
size_t length = FIFO_PACKET_SIZE;
ssize_t bytes;
struct fifo_packet *packet;
uint64_t cur_pos;
struct timespec now;
int ret;
ssize_t bytes;
int i;
if (!fifo_session || !fifo_session->device->selected)
return;
packet = (struct fifo_packet *) calloc(1, sizeof(struct fifo_packet));
memcpy(packet->samples, buf, sizeof(packet->samples));
packet->rtptime = rtptime;
for (i = 0; obuf->frames[i].buffer; i++)
{
if (quality_is_equal(&fifo_quality, &obuf->frames[i].quality))
break;
}
if (!obuf->frames[i].buffer)
{
DPRINTF(E_LOG, L_FIFO, "Bug! Did not get audio in quality required\n");
return;
}
fifo_session->state = OUTPUT_STATE_STREAMING;
CHECK_NULL(L_FIFO, packet = calloc(1, sizeof(struct fifo_packet)));
CHECK_NULL(L_FIFO, packet->samples = malloc(obuf->frames[i].bufsize));
memcpy(packet->samples, obuf->frames[i].buffer, obuf->frames[i].bufsize);
packet->samples_size = obuf->frames[i].bufsize;
packet->pts = obuf->pts;
if (buffer.head)
{
buffer.head->next = packet;
packet->prev = buffer.head;
}
buffer.head = packet;
if (!buffer.tail)
buffer.tail = packet;
ret = player_get_current_pos(&cur_pos, &now, 0);
if (ret < 0)
{
DPRINTF(E_LOG, L_FIFO, "Could not get playback position\n");
return;
}
now.tv_sec = obuf->pts.tv_sec - OUTPUTS_BUFFER_DURATION;
now.tv_nsec = obuf->pts.tv_sec;
while (buffer.tail && buffer.tail->rtptime <= cur_pos)
while (buffer.tail && (timespec_cmp(buffer.tail->pts, now) == -1))
{
bytes = write(fifo_session->output_fd, buffer.tail->samples, length);
bytes = write(fifo_session->output_fd, buffer.tail->samples, buffer.tail->samples_size);
if (bytes > 0)
{
packet = buffer.tail;
buffer.tail = buffer.tail->next;
free(packet->samples);
free(packet);
return;
}
@@ -511,14 +515,6 @@ fifo_write(uint8_t *buf, uint64_t rtptime)
}
}
static void
fifo_set_status_cb(struct output_session *session, output_status_cb cb)
{
struct fifo_session *fifo_session = session->session;
fifo_session->status_cb = cb;
}
static int
fifo_init(void)
{
@@ -578,9 +574,8 @@ struct output_definition output_fifo =
.device_stop = fifo_device_stop,
.device_probe = fifo_device_probe,
.device_volume_set = fifo_device_volume_set,
.playback_start = fifo_playback_start,
.device_set_cb = fifo_device_set_cb,
.playback_stop = fifo_playback_stop,
.write = fifo_write,
.flush = fifo_flush,
.status_cb = fifo_set_status_cb,
};

View File

@@ -70,9 +70,7 @@ struct pulse_session
char *devname;
/* Do not dereference - only passed to the status cb */
struct output_device *device;
struct output_session *output_session;
output_status_cb status_cb;
struct pulse_session *next;
@@ -141,34 +139,18 @@ pulse_session_cleanup(struct pulse_session *ps)
p->next = ps->next;
}
ps->device->session = NULL;
pulse_session_free(ps);
}
static struct pulse_session *
pulse_session_make(struct output_device *device, output_status_cb cb)
{
struct output_session *os;
struct pulse_session *ps;
os = calloc(1, sizeof(struct output_session));
if (!os)
{
DPRINTF(E_LOG, L_LAUDIO, "Out of memory (os)\n");
return NULL;
}
CHECK_NULL(L_LAUDIO, ps = calloc(1, sizeof(struct pulse_session)));
ps = calloc(1, sizeof(struct pulse_session));
if (!ps)
{
DPRINTF(E_LOG, L_LAUDIO, "Out of memory (ps)\n");
free(os);
return NULL;
}
os->session = ps;
os->type = device->type;
ps->output_session = os;
ps->state = PA_STREAM_UNCONNECTED;
ps->device = device;
ps->status_cb = cb;
@@ -178,6 +160,8 @@ pulse_session_make(struct output_device *device, output_status_cb cb)
ps->next = sessions;
sessions = ps;
device->session = ps;
return ps;
}

View File

@@ -80,14 +80,6 @@
#include "raop_verification.h"
#endif
// AirTunes v2 packet interval in ns */
// (352 samples/packet * 1e9 ns/s) / 44100 samples/s = 7981859 ns/packet
// #define AIRTUNES_V2_STREAM_PERIOD 7981859
#ifndef MIN
# define MIN(a, b) ((a < b) ? a : b)
#endif
#define ALAC_HEADER_LEN 3
#define RAOP_QUALITY_SAMPLE_RATE_DEFAULT 44100
@@ -220,9 +212,7 @@ struct raop_session
int volume;
uint64_t start_rtptime;
/* Do not dereference - only passed to the status cb */
struct output_device *device;
struct output_session *output_session;
output_status_cb status_cb;
/* AirTunes v2 */
@@ -320,6 +310,14 @@ static const char *raop_devtype[] =
"Other",
};
/* Struct with default quality levels */
static struct media_quality raop_quality_default =
{
RAOP_QUALITY_SAMPLE_RATE_DEFAULT,
RAOP_QUALITY_BITS_PER_SAMPLE_DEFAULT,
RAOP_QUALITY_CHANNELS_DEFAULT
};
/* From player.c */
extern struct event_base *evbase_player;
@@ -355,16 +353,10 @@ static struct timeval keep_alive_tv = { 30, 0 };
static struct raop_master_session *raop_master_sessions;
static struct raop_session *raop_sessions;
/* Struct with default quality levels */
static struct media_quality raop_quality_default = { RAOP_QUALITY_SAMPLE_RATE_DEFAULT, RAOP_QUALITY_BITS_PER_SAMPLE_DEFAULT, RAOP_QUALITY_CHANNELS_DEFAULT };
// Forwards
static int
raop_device_start(struct output_device *rd, output_status_cb cb);
static void
raop_device_stop(struct output_session *session);
/* ------------------------------- MISC HELPERS ----------------------------- */
@@ -1763,7 +1755,7 @@ raop_status(struct raop_session *rs)
rs->status_cb = NULL;
if (status_cb)
status_cb(rs->device, rs->output_session, state);
status_cb(rs->device, state);
if (rs->state == RAOP_STATE_UNVERIFIED)
player_speaker_status_trigger();
@@ -1882,8 +1874,6 @@ session_free(struct raop_session *rs)
if (rs->devname)
free(rs->devname);
free(rs->output_session);
free(rs);
}
@@ -1905,6 +1895,8 @@ session_cleanup(struct raop_session *rs)
s->next = rs->next;
}
rs->device->session = NULL;
session_free(rs);
}
@@ -1920,14 +1912,6 @@ session_failure(struct raop_session *rs)
session_cleanup(rs);
}
static void
session_failure_cb(struct evrtsp_request *req, void *arg)
{
struct raop_session *rs = arg;
session_failure(rs);
}
static void
deferredev_cb(int fd, short what, void *arg)
{
@@ -1939,23 +1923,67 @@ deferredev_cb(int fd, short what, void *arg)
}
static void
raop_rtsp_close_cb(struct evrtsp_connection *evcon, void *arg)
deferred_session_failure(struct raop_session *rs)
{
struct raop_session *rs = arg;
struct timeval tv;
DPRINTF(E_LOG, L_RAOP, "Device '%s' closed RTSP connection\n", rs->devname);
rs->state = RAOP_STATE_FAILED;
evutil_timerclear(&tv);
evtimer_add(rs->deferredev, &tv);
}
static void
raop_rtsp_close_cb(struct evrtsp_connection *evcon, void *arg)
{
struct raop_session *rs = arg;
DPRINTF(E_LOG, L_RAOP, "Device '%s' closed RTSP connection\n", rs->devname);
deferred_session_failure(rs);
}
static void
session_teardown_cb(struct evrtsp_request *req, void *arg)
{
struct raop_session *rs = arg;
struct output_device *rd = rs->device;
output_status_cb status_cb = rs->status_cb;
rs->reqs_in_flight--;
if (!req || req->response_code != RTSP_OK)
DPRINTF(E_LOG, L_RAOP, "TEARDOWN request failed in session shutdown: %d %s\n", req->response_code, req->response_code_line);
// Clean up session before giving status to the user (good to get rid of it if he
// calls back into us - e.g. tries to make a new session in the callback)
session_cleanup(rs);
// Can't use raop_status() here, sadly
if (status_cb)
status_cb(rd, OUTPUT_STATE_STOPPED);
return;
}
static int
session_teardown(struct raop_session *rs, const char *log_caller)
{
int ret;
ret = raop_send_req_teardown(rs, session_teardown_cb, log_caller);
if (ret < 0)
{
DPRINTF(E_LOG, L_RAOP, "%s: TEARDOWN request failed!\n", log_caller);
deferred_session_failure(rs);
}
return ret;
}
static struct raop_session *
session_make(struct output_device *rd, int family, output_status_cb cb, bool only_probe)
{
struct output_session *os;
struct raop_session *rs;
struct raop_extra *re;
char *address;
@@ -1988,25 +2016,8 @@ session_make(struct output_device *rd, int family, output_status_cb cb, bool onl
return NULL;
}
os = calloc(1, sizeof(struct output_session));
if (!os)
{
DPRINTF(E_LOG, L_RAOP, "Out of memory (os)\n");
return NULL;
}
CHECK_NULL(L_PLAYER, rs = calloc(1, sizeof(struct raop_session)));
rs = calloc(1, sizeof(struct raop_session));
if (!rs)
{
DPRINTF(E_LOG, L_RAOP, "Out of memory (rs)\n");
free(os);
return NULL;
}
os->session = rs;
os->type = rd->type;
rs->output_session = os;
rs->state = RAOP_STATE_STOPPED;
rs->only_probe = only_probe;
rs->reqs_in_flight = 0;
@@ -2135,9 +2146,13 @@ session_make(struct output_device *rd, int family, output_status_cb cb, bool onl
goto out_free_evcon;
}
// Attach to list of sessions
rs->next = raop_sessions;
raop_sessions = rs;
// rs is now the official device session
rd->session = rs;
return rs;
out_free_evcon:
@@ -2717,15 +2732,10 @@ raop_cb_set_volume(struct evrtsp_request *req, void *arg)
static int
raop_set_volume_one(struct output_device *rd, output_status_cb cb)
{
struct raop_session *rs;
struct raop_session *rs = rd->session;
int ret;
if (!rd->session || !rd->session->session)
return 0;
rs = rd->session->session;
if (!(rs->state & RAOP_STATE_F_CONNECTED))
if (!rs || !(rs->state & RAOP_STATE_F_CONNECTED))
return 0;
ret = raop_set_volume_internal(rs, rd->volume, raop_cb_set_volume);
@@ -2813,12 +2823,7 @@ raop_flush_timer_cb(int fd, short what, void *arg)
DPRINTF(E_DBG, L_RAOP, "Flush timer expired; tearing down RAOP sessions\n");
for (rs = raop_sessions; rs; rs = rs->next)
{
if (!(rs->state & RAOP_STATE_F_CONNECTED))
continue;
raop_device_stop(rs->output_session);
}
session_teardown(rs, "raop_flush_timer_cb");
}
static void
@@ -2882,7 +2887,6 @@ packet_prepare(struct rtp_packet *pkt, uint8_t *rawbuf, size_t rawbuf_size, bool
static int
packet_send(struct raop_session *rs, struct rtp_packet *pkt)
{
struct timeval tv;
int ret;
if (!rs)
@@ -2893,12 +2897,9 @@ packet_send(struct raop_session *rs, struct rtp_packet *pkt)
{
DPRINTF(E_LOG, L_RAOP, "Send error for '%s': %s\n", rs->devname, strerror(errno));
rs->state = RAOP_STATE_FAILED;
// Can't free it right away, it would make the ->next in the calling
// master_session and session loops invalid
evutil_timerclear(&tv);
evtimer_add(rs->deferredev, &tv);
deferred_session_failure(rs);
return -1;
}
else if (ret != pkt->data_len)
@@ -2943,8 +2944,6 @@ control_packet_send(struct raop_session *rs, struct rtp_packet *pkt)
ret = sendto(rs->control_svc->fd, pkt->data, pkt->data_len, 0, &rs->sa.sa, len);
if (ret < 0)
DPRINTF(E_LOG, L_RAOP, "Could not send playback sync to device '%s': %s\n", rs->devname, strerror(errno));
DPRINTF(E_DBG, L_PLAYER, "SYNC PACKET SENT\n");
}
static void
@@ -2990,7 +2989,6 @@ packets_send(struct raop_master_session *rms)
{
pkt->header[1] = 0xe0;
packet_send(rs, pkt);
rs->state = RAOP_STATE_STREAMING;
}
else if (rs->state == RAOP_STATE_STREAMING)
{
@@ -3006,7 +3004,7 @@ packets_send(struct raop_master_session *rms)
}
static void
packets_sync_send(struct raop_master_session *rms, struct timespec *pts)
packets_sync_send(struct raop_master_session *rms, struct timespec pts)
{
struct rtp_packet *sync_pkt;
struct raop_session *rs;
@@ -3022,9 +3020,9 @@ packets_sync_send(struct raop_master_session *rms, struct timespec *pts)
// OUTPUTS_BUFFER_DURATION secs into the future. However, in the sync packet
// we want to tell the device what it should be playing right now. So we give
// it a cur_time where we subtract this duration.
// TODO do we need this? could we just send the future timestamp?
cur_stamp.ts.tv_sec = pts->tv_sec - OUTPUTS_BUFFER_DURATION;
cur_stamp.ts.tv_nsec = pts->tv_nsec;
cur_stamp.ts.tv_sec = pts.tv_sec - OUTPUTS_BUFFER_DURATION;
cur_stamp.ts.tv_nsec = pts.tv_nsec;
// The cur_pos will be the rtptime of the coming packet, minus
// OUTPUTS_BUFFER_DURATION in samples (output_buffer_samples). Because we
// might also have some data lined up in rms->evbuf, we also need to account
@@ -3041,6 +3039,8 @@ packets_sync_send(struct raop_master_session *rms, struct timespec *pts)
{
sync_pkt = rtp_sync_packet_next(rms->rtp_session, &cur_stamp, 0x90);
control_packet_send(rs, sync_pkt);
DPRINTF(E_DBG, L_PLAYER, "Start sync packet sent to '%s': cur_pos=%" PRIu32 ", rtptime=%" PRIu32 "\n", rs->devname, cur_stamp.pos, rms->rtp_session->pos);
}
else if (is_sync_time && rs->state == RAOP_STATE_STREAMING)
{
@@ -3553,9 +3553,32 @@ raop_v2_control_start(int v6enabled)
/* ------------------------------ Session startup --------------------------- */
static void
raop_cb_startup_retry(struct evrtsp_request *req, void *arg)
{
struct raop_session *rs = arg;
struct output_device *rd = rs->device;
output_status_cb cb = rs->status_cb;
session_cleanup(rs);
raop_device_start(rd, cb);
}
static void
raop_cb_startup_cancel(struct evrtsp_request *req, void *arg)
{
struct raop_session *rs = arg;
session_failure(rs);
}
static void
raop_startup_cancel(struct raop_session *rs)
{
struct output_device *rd = rs->device;
output_status_cb cb;
int ret;
if (!rs->session)
{
session_failure(rs);
@@ -3567,20 +3590,24 @@ raop_startup_cancel(struct raop_session *rs)
if (rs->family == AF_INET6 && !(rs->state & RAOP_STATE_F_FAILED))
{
// This flag is permanent and will not be overwritten by mdns advertisements
rs->device->v6_disabled = 1;
rd->v6_disabled = 1;
// Be nice to our peer + session_failure_cb() cleans up old session
raop_send_req_teardown(rs, session_failure_cb, "startup_cancel");
// Stop current session and wait for call back
ret = raop_send_req_teardown(rs, raop_cb_startup_retry, "startup_cancel");
if (ret < 0)
{
// No connection at all, lets clean up and try again
cb = rs->status_cb;
session_cleanup(rs);
raop_device_start(rd, cb);
}
// Try to start a new session
raop_device_start(rs->device, rs->status_cb);
// Don't let the failed session make a negative status callback
rs->status_cb = NULL;
return;
}
raop_send_req_teardown(rs, session_failure_cb, "startup_cancel");
ret = raop_send_req_teardown(rs, raop_cb_startup_cancel, "startup_cancel");
if (ret < 0)
session_failure(rs);
}
static void
@@ -4069,41 +4096,6 @@ raop_cb_startup_options(struct evrtsp_request *req, void *arg)
raop_startup_cancel(rs);
}
static void
raop_cb_shutdown_teardown(struct evrtsp_request *req, void *arg)
{
struct raop_session *rs = arg;
int ret;
rs->reqs_in_flight--;
if (!req)
goto error;
if (req->response_code != RTSP_OK)
{
DPRINTF(E_LOG, L_RAOP, "TEARDOWN request failed in session shutdown: %d %s\n", req->response_code, req->response_code_line);
goto error;
}
ret = raop_check_cseq(rs, req);
if (ret < 0)
goto error;
rs->state = RAOP_STATE_STOPPED;
/* Session shut down, tell our user */
raop_status(rs);
session_cleanup(rs);
return;
error:
session_failure(rs);
}
/* ------------------------- tvOS device verification ----------------------- */
/* e.g. for the ATV4 (read it from the bottom and up) */
@@ -4801,51 +4793,51 @@ raop_device_probe(struct output_device *rd, output_status_cb cb)
static int
raop_device_start(struct output_device *rd, output_status_cb cb)
{
event_del(flush_timer);
evtimer_add(keep_alive_timer, &keep_alive_tv);
return raop_device_start_generic(rd, cb, 0);
}
static void
raop_device_stop(struct output_session *session)
static int
raop_device_stop(struct output_device *rd, output_status_cb cb)
{
struct raop_session *rs = session->session;
struct raop_session *rs = rd->session;
if (rs->state & RAOP_STATE_F_CONNECTED)
raop_send_req_teardown(rs, raop_cb_shutdown_teardown, "device_stop");
else
session_cleanup(rs);
rs->status_cb = cb;
return session_teardown(rs, "device_stop");
}
static void
raop_device_free_extra(struct output_device *device)
raop_device_free_extra(struct output_device *rd)
{
struct raop_extra *re = device->extra_device_info;
struct raop_extra *re = rd->extra_device_info;
free(re);
}
static void
raop_device_set_cb(struct output_device *rd, output_status_cb cb)
{
struct raop_session *rs = rd->session;
rs->status_cb = cb;
}
static void
raop_playback_stop(void)
{
struct raop_session *rs;
int ret;
evtimer_del(keep_alive_timer);
for (rs = raop_sessions; rs; rs = rs->next)
{
ret = raop_send_req_teardown(rs, raop_cb_shutdown_teardown, "playback_stop");
if (ret < 0)
DPRINTF(E_LOG, L_RAOP, "playback_stop: TEARDOWN request failed!\n");
}
session_teardown(rs, "playback_stop");
}
static void
raop_write(struct output_buffer *obuf)
{
struct raop_master_session *rms;
struct raop_session *rs;
int i;
for (rms = raop_master_sessions; rms; rms = rms->next)
@@ -4871,6 +4863,19 @@ raop_write(struct output_buffer *obuf)
}
}
}
// Check for devices that have joined since last write (we have already sent them
// initialization sync and rtp packets via packets_sync_send and packets_send)
for (rs = raop_sessions; rs; rs = rs->next)
{
if (rs->state != RAOP_STATE_CONNECTED)
continue;
event_del(flush_timer); // In case playback was stopped but then restarted again
rs->state = RAOP_STATE_STREAMING;
// Make a cb?
}
}
static int
@@ -4894,7 +4899,6 @@ raop_flush(output_status_cb cb)
if (ret < 0)
{
session_failure(rs);
continue;
}
@@ -4912,14 +4916,6 @@ raop_flush(output_status_cb cb)
return pending;
}
static void
raop_set_status_cb(struct output_session *session, output_status_cb cb)
{
struct raop_session *rs = session->session;
rs->status_cb = cb;
}
static int
raop_init(void)
{
@@ -5035,7 +5031,6 @@ raop_init(void)
goto out_stop_control;
}
return 0;
out_stop_control:
@@ -5087,16 +5082,16 @@ struct output_definition output_raop =
.disabled = 0,
.init = raop_init,
.deinit = raop_deinit,
.device_start2 = raop_device_start,
.device_start = raop_device_start,
.device_stop = raop_device_stop,
.device_probe = raop_device_probe,
.device_free_extra = raop_device_free_extra,
.device_volume_set = raop_set_volume_one,
.device_volume_to_pct = raop_volume_to_pct,
.device_set_cb = raop_device_set_cb,
.playback_stop = raop_playback_stop,
.write2 = raop_write,
.flush2 = raop_flush,
.status_cb = raop_set_status_cb,
.write = raop_write,
.flush = raop_flush,
.metadata_prepare = raop_metadata_prepare,
.metadata_send = raop_metadata_send,
.metadata_purge = raop_metadata_purge,

View File

@@ -252,14 +252,14 @@ rtp_sync_packet_next(struct rtp_session *session, struct rtcp_timestamp *cur_sta
rtptime = htobe32(session->pos);
memcpy(session->sync_packet_next.data + 16, &rtptime, 4);
DPRINTF(E_DBG, L_PLAYER, "SYNC PACKET cur_ts:%ld.%ld, next_pkt:%u, cur_pos:%u, type:0x%x, sync_counter:%d\n",
/* DPRINTF(E_DBG, L_PLAYER, "SYNC PACKET cur_ts:%ld.%ld, next_pkt:%u, cur_pos:%u, type:0x%x, sync_counter:%d\n",
cur_stamp->ts.tv_sec, cur_stamp->ts.tv_nsec,
session->pos,
cur_stamp->pos,
session->sync_packet_next.data[0],
session->sync_counter
);
*/
return &session->sync_packet_next;
}

View File

@@ -24,7 +24,6 @@
#include "outputs.h"
#include "httpd_streaming.h"
struct output_definition output_streaming =
{
.name = "mp3 streaming",