2016-01-23 19:14:07 -05:00
|
|
|
|
|
|
|
#ifndef __OUTPUTS_H__
|
|
|
|
#define __OUTPUTS_H__
|
|
|
|
|
2019-03-18 18:04:34 -04:00
|
|
|
#include <stdbool.h>
|
2016-05-05 12:49:27 -04:00
|
|
|
#include <time.h>
|
2019-03-05 16:45:38 -05:00
|
|
|
#include <event2/event.h>
|
2019-02-08 14:36:21 -05:00
|
|
|
#include <event2/buffer.h>
|
|
|
|
#include "misc.h"
|
2016-05-05 12:49:27 -04:00
|
|
|
|
2016-01-23 19:14:07 -05:00
|
|
|
/* Outputs is a generic interface between the player and a media output method,
|
|
|
|
* like for instance AirPlay (raop) or ALSA. The purpose of the interface is to
|
|
|
|
* make it easier to add new outputs without messing too much with the player or
|
|
|
|
* existing output methods.
|
|
|
|
*
|
|
|
|
* An output method will have a general type, and it will be able to detect
|
|
|
|
* supported devices that are available for output. A device will be typically
|
|
|
|
* be something like an AirPlay speaker.
|
|
|
|
*
|
|
|
|
* When a device is started the output backend will typically create a session.
|
|
|
|
* This session is only passed around as an opaque object in this interface.
|
|
|
|
*
|
2020-05-05 10:04:39 -04:00
|
|
|
* Many of the functions here use callbacks to the player to support async setup
|
|
|
|
* etc. The general concept is that the player initiates an action, e.g. volume
|
|
|
|
* change, and then the return value from the output function is the number of
|
|
|
|
* callbacks the player should wait for. The output backend *must* make all the
|
|
|
|
* callbacks, otherwise the player may hang.
|
2016-01-23 19:14:07 -05:00
|
|
|
*/
|
|
|
|
|
2019-02-08 14:36:21 -05:00
|
|
|
// If an output requires a specific quality (like Airplay 1 devices often
|
|
|
|
// require 44100/16) then it should make a subscription request to the output
|
|
|
|
// module, which will then make sure to include this quality when it writes the
|
|
|
|
// audio. The below sets the maximum number of *different* subscriptions
|
|
|
|
// allowed. Note that multiple outputs requesting the *same* quality only counts
|
|
|
|
// as one.
|
|
|
|
#define OUTPUTS_MAX_QUALITY_SUBSCRIPTIONS 5
|
|
|
|
|
|
|
|
// Number of seconds the outputs should buffer before starting playback. Note
|
|
|
|
// this value cannot freely be changed because 1) some Airplay devices ignore
|
|
|
|
// the values we give and stick to 2 seconds, 2) those devices that can handle
|
|
|
|
// different values can only do so within a limited range (maybe max 3 secs)
|
|
|
|
#define OUTPUTS_BUFFER_DURATION 2
|
|
|
|
|
2020-05-27 16:53:11 -04:00
|
|
|
// Whether the device should be *displayed* as selected is not given by
|
|
|
|
// device->selected, since that means "has the user selected the device",
|
|
|
|
// without taking into account whether it is working or available. This macro
|
|
|
|
// is a compound of the factors that determine how to display speaker selection.
|
|
|
|
#define OUTPUTS_DEVICE_DISPLAY_SELECTED(device) ((device)->selected && (device)->state >= OUTPUT_STATE_STOPPED && !(device)->busy && !(device)->prevent_playback)
|
|
|
|
|
2019-03-18 18:04:34 -04:00
|
|
|
// Forward declarations
|
|
|
|
struct output_device;
|
|
|
|
struct output_metadata;
|
|
|
|
enum output_device_state;
|
|
|
|
|
|
|
|
typedef void (*output_status_cb)(struct output_device *device, enum output_device_state status);
|
|
|
|
typedef int (*output_metadata_finalize_cb)(struct output_metadata *metadata);
|
|
|
|
|
2016-01-24 16:19:15 -05:00
|
|
|
// Must be in sync with outputs[] in outputs.c
|
2016-01-23 19:14:07 -05:00
|
|
|
enum output_types
|
|
|
|
{
|
|
|
|
OUTPUT_TYPE_RAOP,
|
2020-11-27 16:46:38 -05:00
|
|
|
OUTPUT_TYPE_AIRPLAY,
|
2016-04-04 10:58:07 -04:00
|
|
|
OUTPUT_TYPE_STREAMING,
|
2016-04-04 17:43:34 -04:00
|
|
|
OUTPUT_TYPE_DUMMY,
|
2016-10-25 15:23:09 -04:00
|
|
|
OUTPUT_TYPE_FIFO,
|
2022-01-15 07:24:49 -05:00
|
|
|
OUTPUT_TYPE_RCP,
|
2017-01-06 03:44:18 -05:00
|
|
|
#ifdef HAVE_ALSA
|
2016-01-24 16:19:15 -05:00
|
|
|
OUTPUT_TYPE_ALSA,
|
2016-04-04 10:58:07 -04:00
|
|
|
#endif
|
2017-01-06 03:44:18 -05:00
|
|
|
#ifdef HAVE_LIBPULSE
|
2016-07-21 16:13:32 -04:00
|
|
|
OUTPUT_TYPE_PULSE,
|
|
|
|
#endif
|
2016-04-04 17:43:34 -04:00
|
|
|
#ifdef CHROMECAST
|
|
|
|
OUTPUT_TYPE_CAST,
|
|
|
|
#endif
|
2016-01-23 19:14:07 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Output session state */
|
|
|
|
enum output_device_state
|
|
|
|
{
|
2016-02-12 15:59:26 -05:00
|
|
|
// Device is stopped (no session)
|
2016-01-23 19:14:07 -05:00
|
|
|
OUTPUT_STATE_STOPPED = 0,
|
2016-02-12 15:59:26 -05:00
|
|
|
// Device is starting up
|
|
|
|
OUTPUT_STATE_STARTUP = 1,
|
|
|
|
// Session established (streaming ready and commands are possible)
|
|
|
|
OUTPUT_STATE_CONNECTED = 2,
|
|
|
|
// Media data is being sent
|
|
|
|
OUTPUT_STATE_STREAMING = 3,
|
2016-01-24 16:19:15 -05:00
|
|
|
// Session is failed, couldn't startup or error occurred
|
2016-01-23 19:14:07 -05:00
|
|
|
OUTPUT_STATE_FAILED = -1,
|
2016-01-24 16:19:15 -05:00
|
|
|
// Password issue: unknown password or bad password
|
2016-01-23 19:14:07 -05:00
|
|
|
OUTPUT_STATE_PASSWORD = -2,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Linked list of device info used by the player for each device
|
|
|
|
*/
|
|
|
|
struct output_device
|
|
|
|
{
|
|
|
|
// Device id
|
|
|
|
uint64_t id;
|
|
|
|
|
|
|
|
// Name of the device, e.g. "Living Room"
|
|
|
|
char *name;
|
|
|
|
|
|
|
|
// Type of the device, will be used to determine which output backend to call
|
|
|
|
enum output_types type;
|
|
|
|
|
|
|
|
// Type of output (string)
|
|
|
|
const char *type_name;
|
|
|
|
|
2020-05-01 14:11:45 -04:00
|
|
|
// Last state that the backend returned to the handlers in outputs.c. This
|
|
|
|
// field must only be set in outputs.c (not in the backends/player).
|
|
|
|
enum output_device_state state;
|
|
|
|
|
2016-01-23 19:14:07 -05:00
|
|
|
// Misc device flags
|
|
|
|
unsigned selected:1;
|
2019-02-13 10:56:17 -05:00
|
|
|
unsigned advertised:1;
|
2016-01-23 19:14:07 -05:00
|
|
|
unsigned has_password:1;
|
|
|
|
unsigned has_video:1;
|
2017-06-19 15:52:01 -04:00
|
|
|
unsigned requires_auth:1;
|
2018-09-19 17:03:33 -04:00
|
|
|
unsigned v6_disabled:1;
|
2020-04-10 14:40:23 -04:00
|
|
|
unsigned prevent_playback:1;
|
|
|
|
unsigned busy:1;
|
2020-07-22 18:39:29 -04:00
|
|
|
unsigned resurrect:1;
|
2016-01-23 19:14:07 -05:00
|
|
|
|
2017-06-19 15:52:01 -04:00
|
|
|
// Credentials if relevant
|
2016-01-23 19:14:07 -05:00
|
|
|
const char *password;
|
2017-06-19 15:52:01 -04:00
|
|
|
char *auth_key;
|
2016-01-23 19:14:07 -05:00
|
|
|
|
|
|
|
// Device volume
|
|
|
|
int volume;
|
|
|
|
int relvol;
|
2020-12-14 17:47:21 -05:00
|
|
|
int max_volume;
|
2016-01-23 19:14:07 -05:00
|
|
|
|
2019-02-08 14:36:21 -05:00
|
|
|
// Quality of audio output
|
|
|
|
struct media_quality quality;
|
2023-05-05 19:00:22 -04:00
|
|
|
int format;
|
2019-02-08 14:36:21 -05:00
|
|
|
|
2016-01-23 19:14:07 -05:00
|
|
|
// Address
|
|
|
|
char *v4_address;
|
|
|
|
char *v6_address;
|
|
|
|
short v4_port;
|
|
|
|
short v6_port;
|
|
|
|
|
2023-05-08 14:46:16 -04:00
|
|
|
// Only used for streaming
|
|
|
|
int audio_fd;
|
|
|
|
int metadata_fd;
|
|
|
|
|
2019-03-05 16:45:38 -05:00
|
|
|
struct event *stop_timer;
|
|
|
|
|
2016-01-23 19:14:07 -05:00
|
|
|
// Opaque pointers to device and session data
|
|
|
|
void *extra_device_info;
|
2019-02-10 17:20:12 -05:00
|
|
|
void *session;
|
2016-01-23 19:14:07 -05:00
|
|
|
|
|
|
|
struct output_device *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct output_metadata
|
|
|
|
{
|
|
|
|
enum output_types type;
|
2019-03-18 18:04:34 -04:00
|
|
|
uint32_t item_id;
|
|
|
|
|
|
|
|
// Progress data, filled out by finalize_cb()
|
|
|
|
uint32_t pos_ms;
|
|
|
|
uint32_t len_ms;
|
|
|
|
struct timespec pts;
|
|
|
|
bool startup;
|
|
|
|
|
|
|
|
// Private output data made by the metadata_prepare()
|
|
|
|
void *priv;
|
2019-02-12 15:25:27 -05:00
|
|
|
|
2019-03-18 18:04:34 -04:00
|
|
|
struct event *ev;
|
|
|
|
|
|
|
|
// Finalize before right before sending, e.g. set playback position
|
|
|
|
output_metadata_finalize_cb finalize_cb;
|
2016-01-23 19:14:07 -05:00
|
|
|
};
|
|
|
|
|
2019-02-22 02:40:59 -05:00
|
|
|
struct output_data
|
2019-02-08 14:36:21 -05:00
|
|
|
{
|
|
|
|
struct media_quality quality;
|
|
|
|
struct evbuffer *evbuf;
|
|
|
|
uint8_t *buffer;
|
|
|
|
size_t bufsize;
|
|
|
|
int samples;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct output_buffer
|
|
|
|
{
|
2019-02-10 17:20:12 -05:00
|
|
|
struct timespec pts;
|
2019-12-26 18:10:32 -05:00
|
|
|
// The array is two larger than max quality subscriptions because element 0
|
|
|
|
// holds the original, untranscoded, data (which might not have any
|
|
|
|
// subscribers, and the last element is a zero terminator.
|
|
|
|
struct output_data data[OUTPUTS_MAX_QUALITY_SUBSCRIPTIONS + 2];
|
2020-06-28 18:19:03 -04:00
|
|
|
};
|
2019-02-08 14:36:21 -05:00
|
|
|
|
2016-01-23 19:14:07 -05:00
|
|
|
struct output_definition
|
|
|
|
{
|
|
|
|
// Name of the output
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
// Type of output
|
|
|
|
enum output_types type;
|
|
|
|
|
2021-01-03 14:28:26 -05:00
|
|
|
// Priority to give this output when autoselecting an output, or when
|
|
|
|
// selectinga which output definition to use for a device that has multiple,
|
|
|
|
// e.g. AirPlay 1 and 2.
|
2016-01-23 19:14:07 -05:00
|
|
|
// 1 = highest priority, 0 = don't autoselect
|
|
|
|
int priority;
|
|
|
|
|
|
|
|
// Set to 1 if the output initialization failed
|
|
|
|
int disabled;
|
|
|
|
|
|
|
|
// Initialization function called during startup
|
|
|
|
// Output must call device_cb when an output device becomes available/unavailable
|
|
|
|
int (*init)(void);
|
|
|
|
|
|
|
|
// Deinitialization function called at shutdown
|
|
|
|
void (*deinit)(void);
|
|
|
|
|
2020-05-01 14:11:45 -04:00
|
|
|
// For all the below that take callbacks, the return values are:
|
|
|
|
// - negative: error
|
|
|
|
// - zero: ok, won't make a callback
|
|
|
|
// - positive: number of callbacks that will be made
|
|
|
|
|
2016-01-23 19:14:07 -05:00
|
|
|
// Prepare a playback session on device and call back
|
2019-02-12 15:25:27 -05:00
|
|
|
int (*device_start)(struct output_device *device, int callback_id);
|
2016-01-23 19:14:07 -05:00
|
|
|
|
2019-02-10 17:20:12 -05:00
|
|
|
// Close a session prepared by device_start and call back
|
2019-02-12 15:25:27 -05:00
|
|
|
int (*device_stop)(struct output_device *device, int callback_id);
|
2016-01-23 19:14:07 -05:00
|
|
|
|
2019-02-16 18:19:13 -05:00
|
|
|
// Flush device session and call back
|
|
|
|
int (*device_flush)(struct output_device *device, int callback_id);
|
|
|
|
|
2016-01-23 19:14:07 -05:00
|
|
|
// Test the connection to a device and call back
|
2019-02-12 15:25:27 -05:00
|
|
|
int (*device_probe)(struct output_device *device, int callback_id);
|
2016-01-23 19:14:07 -05:00
|
|
|
|
|
|
|
// Set the volume and call back
|
2019-02-12 15:25:27 -05:00
|
|
|
int (*device_volume_set)(struct output_device *device, int callback_id);
|
2016-01-23 19:14:07 -05:00
|
|
|
|
2018-05-26 19:42:39 -04:00
|
|
|
// Convert device internal representation of volume to our pct scale
|
|
|
|
int (*device_volume_to_pct)(struct output_device *device, const char *volume);
|
|
|
|
|
2019-02-08 14:36:21 -05:00
|
|
|
// Request a change of quality from the device
|
2019-02-12 15:25:27 -05:00
|
|
|
int (*device_quality_set)(struct output_device *device, struct media_quality *quality, int callback_id);
|
2019-02-08 14:36:21 -05:00
|
|
|
|
2021-04-09 14:21:20 -04:00
|
|
|
// Authorize the server to use the device
|
2020-05-26 16:42:06 -04:00
|
|
|
int (*device_authorize)(struct output_device *device, const char *pin, int callback_id);
|
|
|
|
|
2019-02-10 17:20:12 -05:00
|
|
|
// Change the call back associated with a device
|
2019-02-12 15:25:27 -05:00
|
|
|
void (*device_cb_set)(struct output_device *device, int callback_id);
|
|
|
|
|
|
|
|
// Free the private device data
|
|
|
|
void (*device_free_extra)(struct output_device *device);
|
2019-02-10 17:20:12 -05:00
|
|
|
|
2016-01-23 19:14:07 -05:00
|
|
|
// Write stream data to the output devices
|
2019-02-10 17:20:12 -05:00
|
|
|
void (*write)(struct output_buffer *buffer);
|
2016-01-23 19:14:07 -05:00
|
|
|
|
2019-03-18 18:04:34 -04:00
|
|
|
// Called from worker thread for async preparation of metadata (e.g. getting
|
|
|
|
// artwork, which might involce downloading image data). The prepared data is
|
2023-01-22 11:23:32 -05:00
|
|
|
// saved to metadata->priv, which metadata_send() can use.
|
2019-03-18 18:04:34 -04:00
|
|
|
void *(*metadata_prepare)(struct output_metadata *metadata);
|
|
|
|
|
|
|
|
// Send metadata to outputs. Ownership of *metadata is transferred.
|
|
|
|
void (*metadata_send)(struct output_metadata *metadata);
|
|
|
|
|
|
|
|
// Output will cleanup all metadata (so basically like flush but for metadata)
|
2016-01-23 19:14:07 -05:00
|
|
|
void (*metadata_purge)(void);
|
|
|
|
};
|
|
|
|
|
2019-02-12 15:25:27 -05:00
|
|
|
/* ------------------------------- General use ------------------------------ */
|
|
|
|
|
|
|
|
struct output_device *
|
|
|
|
outputs_device_get(uint64_t device_id);
|
|
|
|
|
|
|
|
/* ----------------------- Called by backend modules ------------------------ */
|
|
|
|
|
|
|
|
int
|
|
|
|
outputs_device_session_add(uint64_t device_id, void *session);
|
|
|
|
|
|
|
|
void
|
|
|
|
outputs_device_session_remove(uint64_t device_id);
|
|
|
|
|
|
|
|
int
|
|
|
|
outputs_quality_subscribe(struct media_quality *quality);
|
|
|
|
|
|
|
|
void
|
|
|
|
outputs_quality_unsubscribe(struct media_quality *quality);
|
|
|
|
|
|
|
|
void
|
|
|
|
outputs_cb(int callback_id, uint64_t device_id, enum output_device_state);
|
|
|
|
|
2023-01-22 11:23:32 -05:00
|
|
|
void
|
|
|
|
outputs_metadata_free(struct output_metadata *metadata);
|
|
|
|
|
2023-05-01 17:35:34 -04:00
|
|
|
struct output_buffer *
|
|
|
|
outputs_buffer_copy(struct output_buffer *buffer);
|
|
|
|
|
|
|
|
void
|
|
|
|
outputs_buffer_free(struct output_buffer *buffer);
|
|
|
|
|
2019-02-12 15:25:27 -05:00
|
|
|
/* ---------------------------- Called by player ---------------------------- */
|
|
|
|
|
2019-02-17 07:12:29 -05:00
|
|
|
// Ownership of *add is transferred, so don't address after calling. Instead you
|
|
|
|
// can address the return value (which is not the same if the device was already
|
2021-01-03 14:28:26 -05:00
|
|
|
// in the list).
|
2019-02-17 07:12:29 -05:00
|
|
|
struct output_device *
|
2020-05-01 14:11:45 -04:00
|
|
|
outputs_device_add(struct output_device *add, bool new_deselect);
|
2019-02-12 17:43:54 -05:00
|
|
|
|
|
|
|
void
|
|
|
|
outputs_device_remove(struct output_device *remove);
|
|
|
|
|
2020-05-01 14:11:45 -04:00
|
|
|
void
|
2020-08-13 14:48:48 -04:00
|
|
|
outputs_device_select(struct output_device *device, int max_volume);
|
2020-05-01 14:11:45 -04:00
|
|
|
|
|
|
|
void
|
|
|
|
outputs_device_deselect(struct output_device *device);
|
|
|
|
|
2016-01-23 19:14:07 -05:00
|
|
|
int
|
2020-05-01 14:11:45 -04:00
|
|
|
outputs_device_start(struct output_device *device, output_status_cb cb, bool only_probe);
|
2016-01-23 19:14:07 -05:00
|
|
|
|
2019-02-09 19:54:52 -05:00
|
|
|
int
|
2019-02-10 17:20:12 -05:00
|
|
|
outputs_device_stop(struct output_device *device, output_status_cb cb);
|
2016-01-23 19:14:07 -05:00
|
|
|
|
2019-03-05 16:45:38 -05:00
|
|
|
int
|
|
|
|
outputs_device_stop_delayed(struct output_device *device, output_status_cb cb);
|
|
|
|
|
2019-02-16 18:19:13 -05:00
|
|
|
int
|
|
|
|
outputs_device_flush(struct output_device *device, output_status_cb cb);
|
|
|
|
|
2020-05-01 14:11:45 -04:00
|
|
|
void
|
|
|
|
outputs_device_volume_register(struct output_device *device, int absvol, int relvol);
|
2016-01-23 19:14:07 -05:00
|
|
|
|
|
|
|
int
|
|
|
|
outputs_device_volume_set(struct output_device *device, output_status_cb cb);
|
|
|
|
|
2018-05-26 19:42:39 -04:00
|
|
|
int
|
|
|
|
outputs_device_volume_to_pct(struct output_device *device, const char *value);
|
|
|
|
|
2019-02-08 14:36:21 -05:00
|
|
|
int
|
2019-02-12 15:25:27 -05:00
|
|
|
outputs_device_quality_set(struct output_device *device, struct media_quality *quality, output_status_cb cb);
|
2019-02-08 14:36:21 -05:00
|
|
|
|
2020-05-26 16:42:06 -04:00
|
|
|
int
|
|
|
|
outputs_device_authorize(struct output_device *device, const char *pin, output_status_cb cb);
|
|
|
|
|
2016-01-23 19:14:07 -05:00
|
|
|
void
|
2019-02-12 15:25:27 -05:00
|
|
|
outputs_device_cb_set(struct output_device *device, output_status_cb cb);
|
|
|
|
|
|
|
|
void
|
|
|
|
outputs_device_free(struct output_device *device);
|
2016-01-23 19:14:07 -05:00
|
|
|
|
|
|
|
int
|
2020-05-01 14:11:45 -04:00
|
|
|
outputs_start(output_status_cb started_cb, output_status_cb stopped_cb, bool only_probe);
|
2016-01-23 19:14:07 -05:00
|
|
|
|
2019-03-05 16:45:38 -05:00
|
|
|
int
|
|
|
|
outputs_stop(output_status_cb cb);
|
|
|
|
|
2020-05-01 14:11:45 -04:00
|
|
|
int
|
|
|
|
outputs_flush(output_status_cb cb);
|
|
|
|
|
2020-06-28 18:19:03 -04:00
|
|
|
int
|
|
|
|
outputs_volume_get(void);
|
|
|
|
|
2020-05-01 14:11:45 -04:00
|
|
|
int
|
|
|
|
outputs_volume_set(int volume, output_status_cb cb);
|
|
|
|
|
2019-03-05 16:45:38 -05:00
|
|
|
int
|
|
|
|
outputs_stop_delayed_cancel(void);
|
|
|
|
|
2020-05-01 14:11:45 -04:00
|
|
|
int
|
|
|
|
outputs_sessions_count(void);
|
|
|
|
|
2019-02-16 18:19:13 -05:00
|
|
|
void
|
2019-03-03 12:17:57 -05:00
|
|
|
outputs_write(void *buf, size_t bufsize, int nsamples, struct media_quality *quality, struct timespec *pts);
|
2019-02-16 18:19:13 -05:00
|
|
|
|
2016-01-23 19:14:07 -05:00
|
|
|
void
|
2019-03-18 18:04:34 -04:00
|
|
|
outputs_metadata_send(uint32_t item_id, bool startup, output_metadata_finalize_cb cb);
|
2016-01-23 19:14:07 -05:00
|
|
|
|
|
|
|
void
|
|
|
|
outputs_metadata_purge(void);
|
|
|
|
|
2016-04-04 15:54:37 -04:00
|
|
|
int
|
|
|
|
outputs_priority(struct output_device *device);
|
|
|
|
|
2016-01-23 19:14:07 -05:00
|
|
|
const char *
|
|
|
|
outputs_name(enum output_types type);
|
|
|
|
|
2020-06-28 18:19:03 -04:00
|
|
|
struct output_device *
|
|
|
|
outputs_list(void);
|
|
|
|
|
2016-01-23 19:14:07 -05:00
|
|
|
int
|
|
|
|
outputs_init(void);
|
|
|
|
|
|
|
|
void
|
|
|
|
outputs_deinit(void);
|
|
|
|
|
|
|
|
#endif /* !__OUTPUTS_H__ */
|