[outputs] Changes to interface of outputs module

* Drop output_sessions, was just a pointer to the actual session anyway
* Drop the old write, flush and stop prototypes
* Some minor changes/renaming

Purpose of this is also to fix a race condition in player.c where it
could try to start two sessions on the same speaker. This could happen
because outputs_device_start() in line 2093 is conditional on device->session
which however is false while a device is starting up.
This commit is contained in:
ejurgensen 2019-02-10 23:20:12 +01:00
parent cee740ae51
commit 14a6d318f0
2 changed files with 59 additions and 113 deletions

View File

@ -148,7 +148,7 @@ buffer_fill(struct output_buffer *obuf, void *buf, size_t bufsize, struct media_
int n;
obuf->write_counter++;
obuf->pts = pts;
obuf->pts = *pts;
// 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
@ -207,49 +207,48 @@ buffer_drain(struct output_buffer *obuf)
/* ----------------------------------- API ---------------------------------- */
int
outputs_device_start(struct output_device *device, output_status_cb cb, uint64_t rtptime)
outputs_device_start(struct output_device *device, output_status_cb cb)
{
if (outputs[device->type]->disabled)
if (outputs[device->type]->disabled || !outputs[device->type]->device_start)
return -1;
if (outputs[device->type]->device_start)
return outputs[device->type]->device_start(device, cb, rtptime);
else
return -1;
if (device->session)
{
DPRINTF(E_LOG, L_PLAYER, "Bug! outputs_device_start() called for a device that already has a session\n");
return -1;
}
return outputs[device->type]->device_start(device, cb);
}
int
outputs_device_start2(struct output_device *device, output_status_cb cb)
outputs_device_stop(struct output_device *device, output_status_cb cb)
{
if (outputs[device->type]->disabled)
if (outputs[device->type]->disabled || !outputs[device->type]->device_stop)
return -1;
if (outputs[device->type]->device_start2)
return outputs[device->type]->device_start2(device, cb);
else
return -1;
}
if (!device->session)
{
DPRINTF(E_LOG, L_PLAYER, "Bug! outputs_device_stop() called for a device that has no session\n");
return -1;
}
void
outputs_device_stop(struct output_session *session)
{
if (outputs[session->type]->disabled)
return;
if (outputs[session->type]->device_stop)
outputs[session->type]->device_stop(session);
return outputs[device->type]->device_stop(device, cb);
}
int
outputs_device_probe(struct output_device *device, output_status_cb cb)
{
if (outputs[device->type]->disabled)
if (outputs[device->type]->disabled || !outputs[device->type]->device_probe)
return -1;
if (outputs[device->type]->device_probe)
return outputs[device->type]->device_probe(device, cb);
else
return -1;
if (device->session)
{
DPRINTF(E_LOG, L_PLAYER, "Bug! outputs_device_probe() called for a device that already has a session\n");
return -1;
}
return outputs[device->type]->device_probe(device, cb);
}
void
@ -311,6 +310,16 @@ outputs_device_quality_set(struct output_device *device, struct media_quality *q
return -1;
}
void
outputs_device_set_cb(struct output_device *device, output_status_cb cb)
{
if (outputs[device->type]->disabled)
return;
if (outputs[device->type]->device_set_cb)
outputs[device->type]->device_set_cb(device, cb);
}
void
outputs_playback_stop(void)
{
@ -327,22 +336,7 @@ outputs_playback_stop(void)
}
void
outputs_write(uint8_t *buf, uint64_t rtptime)
{
int i;
for (i = 0; outputs[i]; i++)
{
if (outputs[i]->disabled)
continue;
if (outputs[i]->write)
outputs[i]->write(buf, rtptime);
}
}
void
outputs_write2(void *buf, size_t bufsize, struct media_quality *quality, int nsamples, struct timespec *pts)
outputs_write(void *buf, size_t bufsize, struct media_quality *quality, int nsamples, struct timespec *pts)
{
int i;
@ -353,15 +347,15 @@ outputs_write2(void *buf, size_t bufsize, struct media_quality *quality, int nsa
if (outputs[i]->disabled)
continue;
if (outputs[i]->write2)
outputs[i]->write2(&output_buffer);
if (outputs[i]->write)
outputs[i]->write(&output_buffer);
}
buffer_drain(&output_buffer);
}
int
outputs_flush(output_status_cb cb, uint64_t rtptime)
outputs_flush(output_status_cb cb)
{
int ret;
int i;
@ -373,41 +367,12 @@ outputs_flush(output_status_cb cb, uint64_t rtptime)
continue;
if (outputs[i]->flush)
ret += outputs[i]->flush(cb, rtptime);
ret += outputs[i]->flush(cb);
}
return ret;
}
int
outputs_flush2(output_status_cb cb)
{
int ret;
int i;
ret = 0;
for (i = 0; outputs[i]; i++)
{
if (outputs[i]->disabled)
continue;
if (outputs[i]->flush2)
ret += outputs[i]->flush2(cb);
}
return ret;
}
void
outputs_status_cb(struct output_session *session, output_status_cb cb)
{
if (outputs[session->type]->disabled)
return;
if (outputs[session->type]->status_cb)
outputs[session->type]->status_cb(session, cb);
}
struct output_metadata *
outputs_metadata_prepare(int id)
{

View File

@ -140,18 +140,11 @@ struct output_device
// Opaque pointers to device and session data
void *extra_device_info;
struct output_session *session;
void *session;
struct output_device *next;
};
// Except for the type, sessions are opaque outside of the output backend
struct output_session
{
enum output_types type;
void *session;
};
// Linked list of metadata prepared by each output backend
struct output_metadata
{
@ -172,12 +165,12 @@ struct output_frame
struct output_buffer
{
uint32_t write_counter; // REMOVE ME? not used for anything
struct timespec *pts;
struct timespec pts;
struct output_frame frames[OUTPUTS_MAX_QUALITY_SUBSCRIPTIONS + 1];
} output_buffer;
typedef void (*output_status_cb)(struct output_device *device, struct output_session *session, enum output_device_state status);
typedef void (*output_status_cb)(struct output_device *device, enum output_device_state status);
struct output_definition
{
@ -202,11 +195,10 @@ struct output_definition
void (*deinit)(void);
// Prepare a playback session on device and call back
int (*device_start)(struct output_device *device, output_status_cb cb, uint64_t rtptime);
int (*device_start2)(struct output_device *device, output_status_cb cb);
int (*device_start)(struct output_device *device, output_status_cb cb);
// Close a session prepared by device_start
void (*device_stop)(struct output_session *session);
// Close a session prepared by device_start and call back
int (*device_stop)(struct output_device *device, output_status_cb cb);
// Test the connection to a device and call back
int (*device_probe)(struct output_device *device, output_status_cb cb);
@ -223,24 +215,22 @@ struct output_definition
// Request a change of quality from the device
int (*quality_set)(struct output_device *device, struct media_quality *quality);
// Change the call back associated with a device
void (*device_set_cb)(struct output_device *device, output_status_cb cb);
// Start/stop playback on devices that were started
void (*playback_start)(uint64_t next_pkt, struct timespec *ts);
void (*playback_stop)(void);
// Write stream data to the output devices
void (*write)(uint8_t *buf, uint64_t rtptime);
void (*write2)(struct output_buffer *buffer);
void (*write)(struct output_buffer *buffer);
// Flush all sessions, the return must be number of sessions pending the flush
int (*flush)(output_status_cb cb, uint64_t rtptime);
int (*flush2)(output_status_cb cb);
int (*flush)(output_status_cb cb);
// Authorize an output with a pin-code (probably coming from the filescanner)
void (*authorize)(const char *pin);
// Change the call back associated with a session
void (*status_cb)(struct output_session *session, output_status_cb cb);
// Metadata
void *(*metadata_prepare)(int id);
void (*metadata_send)(void *metadata, uint64_t rtptime, uint64_t offset, int startup);
@ -249,13 +239,10 @@ struct output_definition
};
int
outputs_device_start(struct output_device *device, output_status_cb cb, uint64_t rtptime);
outputs_device_start(struct output_device *device, output_status_cb cb);
int
outputs_device_start2(struct output_device *device, output_status_cb cb);
void
outputs_device_stop(struct output_session *session);
outputs_device_stop(struct output_device *device, output_status_cb cb);
int
outputs_device_probe(struct output_device *device, output_status_cb cb);
@ -273,23 +260,17 @@ outputs_device_volume_to_pct(struct output_device *device, const char *value);
int
outputs_device_quality_set(struct output_device *device, struct media_quality *quality);
void
outputs_device_set_cb(struct output_device *device, output_status_cb cb);
void
outputs_playback_stop(void);
void
outputs_write(uint8_t *buf, uint64_t rtptime);
void
outputs_write2(void *buf, size_t bufsize, struct media_quality *quality, int nsamples, struct timespec *pts);
outputs_write(void *buf, size_t bufsize, struct media_quality *quality, int nsamples, struct timespec *pts);
int
outputs_flush(output_status_cb cb, uint64_t rtptime);
int
outputs_flush2(output_status_cb cb);
void
outputs_status_cb(struct output_session *session, output_status_cb cb);
outputs_flush(output_status_cb cb);
struct output_metadata *
outputs_metadata_prepare(int id);