[misc/player] Introduce output ability to announce supported formats
Also introduce default output format and selected device format, should the user want another format. As part of this, change enum player_format in player.h to enum media_format in misc.h so that it is akin to struct media_quality. Modify json API to support this.
This commit is contained in:
parent
9f719ca155
commit
62b42ce354
|
@ -342,6 +342,7 @@ GET /api/outputs
|
|||
| needs_auth_key | boolean | `true` if output requires an authorization key (device verification) |
|
||||
| volume | integer | Volume in percent (0 - 100) |
|
||||
| format | string | Stream format |
|
||||
| supported_formats | array | Array of formats supported by output |
|
||||
|
||||
**Example**
|
||||
|
||||
|
@ -361,7 +362,8 @@ curl -X GET "http://localhost:3689/api/outputs"
|
|||
"requires_auth": false,
|
||||
"needs_auth_key": false,
|
||||
"volume": 0,
|
||||
"format": "alac"
|
||||
"format": "alac",
|
||||
"supported_formats": [ "alac" ]
|
||||
},
|
||||
{
|
||||
"id": "0",
|
||||
|
@ -372,7 +374,8 @@ curl -X GET "http://localhost:3689/api/outputs"
|
|||
"requires_auth": false,
|
||||
"needs_auth_key": false,
|
||||
"volume": 19,
|
||||
"format": "pcm"
|
||||
"format": "pcm",
|
||||
"supported_formats": [ "pcm" ]
|
||||
},
|
||||
{
|
||||
"id": "100",
|
||||
|
@ -383,7 +386,8 @@ curl -X GET "http://localhost:3689/api/outputs"
|
|||
"requires_auth": false,
|
||||
"needs_auth_key": false,
|
||||
"volume": 0,
|
||||
"format": "pcm"
|
||||
"format": "pcm",
|
||||
"supported_formats": [ "pcm" ]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -453,6 +457,7 @@ curl -X GET "http://localhost:3689/api/outputs/0"
|
|||
"needs_auth_key": false,
|
||||
"volume": 3
|
||||
"format": "pcm",
|
||||
"supported_formats": [ "pcm" ]
|
||||
}
|
||||
```
|
||||
|
||||
|
|
40
src/cache.c
40
src/cache.c
|
@ -37,8 +37,9 @@
|
|||
|
||||
#include "conffile.h"
|
||||
#include "logger.h"
|
||||
#include "httpd.h"
|
||||
#include "httpd.h" // TODO get rid of this, only used for httpd_gzip_deflate
|
||||
#include "httpd_daap.h"
|
||||
#include "transcode.h"
|
||||
#include "db.h"
|
||||
#include "cache.h"
|
||||
#include "listener.h"
|
||||
|
@ -189,6 +190,7 @@ static struct cache_db_def cache_artwork_db_def[] = {
|
|||
static sqlite3 *cache_xcode_hdl;
|
||||
static struct event *cache_xcode_updateev;
|
||||
static struct event *cache_xcode_prepareev;
|
||||
static bool cache_xcode_is_enabled;
|
||||
static int cache_xcode_last_file;
|
||||
static struct cache_db_def cache_xcode_db_def[] = {
|
||||
DB_DEF_ADMIN,
|
||||
|
@ -897,6 +899,20 @@ xcode_header_get(void *arg, int *retval)
|
|||
#undef Q_TMPL
|
||||
}
|
||||
|
||||
static enum command_state
|
||||
xcode_toggle(void *arg, int *retval)
|
||||
{
|
||||
bool *enable = arg;
|
||||
|
||||
cache_xcode_is_enabled = *enable;
|
||||
|
||||
if (cache_xcode_is_enabled)
|
||||
event_active(cache_xcode_updateev, 0, 0);
|
||||
|
||||
*retval = 0;
|
||||
return COMMAND_END;
|
||||
}
|
||||
|
||||
static int
|
||||
xcode_add_entry(sqlite3 *hdl, uint32_t id, uint32_t ts, const char *path)
|
||||
{
|
||||
|
@ -982,8 +998,6 @@ xcode_sync_with_files(sqlite3 *hdl)
|
|||
int i;
|
||||
int ret;
|
||||
|
||||
DPRINTF(E_INFO, L_CACHE, "Beginning transcode sync\n");
|
||||
|
||||
// Both lists must be sorted by id, otherwise the compare below won't work
|
||||
ret = sqlite3_prepare_v2(hdl, "SELECT id, time_modified FROM files ORDER BY id;", -1, &stmt, 0);
|
||||
if (ret != SQLITE_OK)
|
||||
|
@ -1044,8 +1058,6 @@ xcode_sync_with_files(sqlite3 *hdl)
|
|||
}
|
||||
db_query_end(&qp);
|
||||
|
||||
DPRINTF(E_INFO, L_CACHE, "Transcode sync completed\n");
|
||||
|
||||
free(cachelist);
|
||||
return 0;
|
||||
|
||||
|
@ -1068,7 +1080,12 @@ xcode_prepare_header(sqlite3 *hdl, const char *format, int id, const char *path)
|
|||
DPRINTF(E_DBG, L_CACHE, "Preparing %s header for '%s' (file id %d)\n", format, path, id);
|
||||
|
||||
#if 1
|
||||
ret = httpd_prepare_header(&header, format, path); // Proceed even if error, we also cache that
|
||||
if (strcmp(format, "mp4") == 0)
|
||||
ret = transcode_prepare_header(&header, XCODE_MP4_ALAC, path);
|
||||
else
|
||||
ret = -1;
|
||||
|
||||
// Proceed even if error, we also cache that
|
||||
if (ret == 0)
|
||||
{
|
||||
datalen = evbuffer_get_length(header);
|
||||
|
@ -1202,7 +1219,7 @@ cache_database_update(void *arg, int *retval)
|
|||
|
||||
// TODO unlink or rename cache.db
|
||||
|
||||
// if (prefer_format && strcmp(prefer_format, "alac")) // TODO Ugly
|
||||
if (cache_xcode_is_enabled)
|
||||
event_add(cache_xcode_updateev, &delay_xcode);
|
||||
|
||||
*retval = 0;
|
||||
|
@ -1731,6 +1748,15 @@ cache_xcode_header_get(struct evbuffer *evbuf, int *cached, uint32_t id, const c
|
|||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
cache_xcode_toggle(bool enable)
|
||||
{
|
||||
if (!cache_is_initialized)
|
||||
return -1;
|
||||
|
||||
return commands_exec_sync(cmdbase, xcode_toggle, NULL, &enable);
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------- Artwork cache API -------------------------- */
|
||||
|
||||
|
|
|
@ -27,6 +27,10 @@ cache_daap_threshold_get(void);
|
|||
int
|
||||
cache_xcode_header_get(struct evbuffer *evbuf, int *cached, uint32_t id, const char *format);
|
||||
|
||||
int
|
||||
cache_xcode_toggle(bool enable);
|
||||
|
||||
|
||||
/* ---------------------------- Artwork cache API -------------------------- */
|
||||
|
||||
#define CACHE_ARTWORK_GROUP 0
|
||||
|
|
4
src/db.c
4
src/db.c
|
@ -4790,7 +4790,7 @@ db_speaker_save(struct output_device *device)
|
|||
#define Q_TMPL "INSERT OR REPLACE INTO speakers (id, selected, volume, name, auth_key, format) VALUES (%" PRIi64 ", %d, %d, %Q, %Q, %d);"
|
||||
char *query;
|
||||
|
||||
query = sqlite3_mprintf(Q_TMPL, device->id, device->selected, device->volume, device->name, device->auth_key, device->format);
|
||||
query = sqlite3_mprintf(Q_TMPL, device->id, device->selected, device->volume, device->name, device->auth_key, device->selected_format);
|
||||
|
||||
return db_query_run(query, 1, 0);
|
||||
#undef Q_TMPL
|
||||
|
@ -4841,7 +4841,7 @@ db_speaker_get(struct output_device *device, uint64_t id)
|
|||
free(device->auth_key);
|
||||
device->auth_key = safe_strdup((char *)sqlite3_column_text(stmt, 3));
|
||||
|
||||
device->format = sqlite3_column_int(stmt, 4);
|
||||
device->selected_format = sqlite3_column_int(stmt, 4);
|
||||
|
||||
#ifdef DB_PROFILE
|
||||
while (db_blocking_step(stmt) == SQLITE_ROW)
|
||||
|
|
50
src/httpd.c
50
src/httpd.c
|
@ -49,6 +49,8 @@
|
|||
#include "httpd_internal.h"
|
||||
#include "transcode.h"
|
||||
#include "cache.h"
|
||||
#include "listener.h"
|
||||
#include "player.h"
|
||||
#ifdef LASTFM
|
||||
# include "lastfm.h"
|
||||
#endif
|
||||
|
@ -905,6 +907,39 @@ stream_fail_cb(void *arg)
|
|||
}
|
||||
|
||||
|
||||
/* -------------------------- SPEAKER/CACHE HANDLING ------------------------ */
|
||||
|
||||
// Thread: player (must not block)
|
||||
static void
|
||||
speaker_enum_cb(struct player_speaker_info *spk, void *arg)
|
||||
{
|
||||
bool *want_mp4 = arg;
|
||||
|
||||
*want_mp4 = *want_mp4 || (spk->format == MEDIA_FORMAT_ALAC && strcmp(spk->output_type, "RCP/SoundBridge") == 0);
|
||||
}
|
||||
|
||||
// Thread: worker
|
||||
static void
|
||||
speaker_update_handler_cb(void *arg)
|
||||
{
|
||||
const char *prefer_format = cfg_getstr(cfg_getsec(cfg, "library"), "prefer_format");
|
||||
bool want_mp4;
|
||||
|
||||
want_mp4 = (prefer_format && strcmp(prefer_format, "alac"));
|
||||
if (!want_mp4)
|
||||
player_speaker_enumerate(speaker_enum_cb, &want_mp4);
|
||||
|
||||
cache_xcode_toggle(want_mp4);
|
||||
}
|
||||
|
||||
// Thread: player (must not block)
|
||||
static void
|
||||
httpd_speaker_update_handler(short event_mask)
|
||||
{
|
||||
worker_execute(speaker_update_handler_cb, NULL, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------- REQUEST CALLBACKS --------------------------- */
|
||||
|
||||
// Worker thread, invoked by request_cb() below
|
||||
|
@ -1220,15 +1255,6 @@ httpd_gzip_deflate(struct evbuffer *in)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
httpd_prepare_header(struct evbuffer **header, const char *format, const char *path)
|
||||
{
|
||||
if (strcmp(format, "mp4") == 0)
|
||||
return transcode_prepare_header(header, XCODE_MP4_ALAC, path);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
// The httpd_send functions below can be called from a worker thread (with
|
||||
// hreq->is_async) or directly from the httpd thread. In the former case, they
|
||||
// will command sending from the httpd thread, since it is not safe to access
|
||||
|
@ -1543,6 +1569,10 @@ httpd_init(const char *webroot)
|
|||
goto error;
|
||||
}
|
||||
|
||||
// We need to know about speaker format changes so we can ask the cache to
|
||||
// start preparing headers for mp4/alac if selected
|
||||
listener_add(httpd_speaker_update_handler, LISTENER_SPEAKER);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
|
@ -1554,6 +1584,8 @@ httpd_init(const char *webroot)
|
|||
void
|
||||
httpd_deinit(void)
|
||||
{
|
||||
listener_remove(httpd_speaker_update_handler);
|
||||
|
||||
// Give modules a chance to hang up connections nicely
|
||||
modules_deinit();
|
||||
|
||||
|
|
11
src/httpd.h
11
src/httpd.h
|
@ -13,17 +13,6 @@
|
|||
struct evbuffer *
|
||||
httpd_gzip_deflate(struct evbuffer *in);
|
||||
|
||||
/*
|
||||
* Passthrough to transcode, which will create a transcoded file header for path
|
||||
*
|
||||
* @out header Newly created evbuffer with the header
|
||||
* @in format Which format caller wants a header for
|
||||
* @in path Path to the file
|
||||
* @return 0 if ok, otherwise -1
|
||||
*/
|
||||
int
|
||||
httpd_prepare_header(struct evbuffer **header, const char *format, const char *path);
|
||||
|
||||
int
|
||||
httpd_init(const char *webroot);
|
||||
|
||||
|
|
|
@ -1522,48 +1522,23 @@ struct outputs_param
|
|||
int output_volume;
|
||||
};
|
||||
|
||||
static enum player_format
|
||||
plformat_from_string(const char *format)
|
||||
{
|
||||
if (strcmp(format, "pcm") == 0)
|
||||
return PLAYER_FORMAT_PCM;
|
||||
if (strcmp(format, "wav") == 0)
|
||||
return PLAYER_FORMAT_WAV;
|
||||
if (strcmp(format, "mp3") == 0)
|
||||
return PLAYER_FORMAT_MP3;
|
||||
if (strcmp(format, "alac") == 0)
|
||||
return PLAYER_FORMAT_ALAC;
|
||||
if (strcmp(format, "opus") == 0)
|
||||
return PLAYER_FORMAT_OPUS;
|
||||
|
||||
return PLAYER_FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
static const char *
|
||||
plformat_to_string(enum player_format format)
|
||||
{
|
||||
if (format == PLAYER_FORMAT_PCM)
|
||||
return "pcm";
|
||||
if (format == PLAYER_FORMAT_WAV)
|
||||
return "wav";
|
||||
if (format == PLAYER_FORMAT_MP3)
|
||||
return "mp3";
|
||||
if (format == PLAYER_FORMAT_ALAC)
|
||||
return "alac";
|
||||
if (format == PLAYER_FORMAT_OPUS)
|
||||
return "opus";
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
static json_object *
|
||||
speaker_to_json(struct player_speaker_info *spk)
|
||||
{
|
||||
json_object *output;
|
||||
json_object *supported_formats;
|
||||
char output_id[21];
|
||||
enum media_format format;
|
||||
|
||||
output = json_object_new_object();
|
||||
|
||||
supported_formats = json_object_new_array();
|
||||
for (format = MEDIA_FORMAT_FIRST; format <= MEDIA_FORMAT_LAST; format = MEDIA_FORMAT_NEXT(format))
|
||||
{
|
||||
if (format & spk->supported_formats)
|
||||
json_object_array_add(supported_formats, json_object_new_string(media_format_to_string(format)));
|
||||
}
|
||||
|
||||
snprintf(output_id, sizeof(output_id), "%" PRIu64, spk->id);
|
||||
json_object_object_add(output, "id", json_object_new_string(output_id));
|
||||
json_object_object_add(output, "name", json_object_new_string(spk->name));
|
||||
|
@ -1573,7 +1548,8 @@ speaker_to_json(struct player_speaker_info *spk)
|
|||
json_object_object_add(output, "requires_auth", json_object_new_boolean(spk->requires_auth));
|
||||
json_object_object_add(output, "needs_auth_key", json_object_new_boolean(spk->needs_auth_key));
|
||||
json_object_object_add(output, "volume", json_object_new_int(spk->absvol));
|
||||
json_object_object_add(output, "format", json_object_new_string(plformat_to_string(spk->format)));
|
||||
json_object_object_add(output, "format", json_object_new_string(media_format_to_string(spk->format)));
|
||||
json_object_object_add(output, "supported_formats", supported_formats);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
@ -1684,13 +1660,13 @@ jsonapi_reply_outputs_put_byid(struct httpd_request *hreq)
|
|||
{
|
||||
format = jparse_str_from_obj(request, "format");
|
||||
if (format)
|
||||
ret = player_speaker_format_set(output_id, plformat_from_string(format));
|
||||
ret = player_speaker_format_set(output_id, media_format_from_string(format));
|
||||
}
|
||||
|
||||
jparse_free(request);
|
||||
|
||||
if (ret < 0)
|
||||
return HTTP_INTERNAL;
|
||||
return HTTP_BADREQUEST;
|
||||
|
||||
return HTTP_NOCONTENT;
|
||||
}
|
||||
|
|
|
@ -228,7 +228,7 @@ session_free(struct streaming_session *session)
|
|||
}
|
||||
|
||||
static struct streaming_session *
|
||||
session_new(struct httpd_request *hreq, bool icy_is_requested, enum player_format format, struct media_quality quality)
|
||||
session_new(struct httpd_request *hreq, bool icy_is_requested, enum media_format format, struct media_quality quality)
|
||||
{
|
||||
struct streaming_session *session;
|
||||
int audio_fd;
|
||||
|
@ -279,7 +279,7 @@ streaming_mp3_handler(struct httpd_request *hreq)
|
|||
httpd_header_add(hreq->out_headers, "icy-metaint", buf);
|
||||
}
|
||||
|
||||
session = session_new(hreq, icy_is_requested, PLAYER_FORMAT_MP3, streaming_default_quality);
|
||||
session = session_new(hreq, icy_is_requested, MEDIA_FORMAT_MP3, streaming_default_quality);
|
||||
if (!session)
|
||||
return -1; // Error sent by caller
|
||||
|
||||
|
|
34
src/misc.c
34
src/misc.c
|
@ -1731,6 +1731,40 @@ quality_is_equal(struct media_quality *a, struct media_quality *b)
|
|||
return (a->sample_rate == b->sample_rate && a->bits_per_sample == b->bits_per_sample && a->channels == b->channels && a->bit_rate == b->bit_rate);
|
||||
}
|
||||
|
||||
enum media_format
|
||||
media_format_from_string(const char *s)
|
||||
{
|
||||
if (strcmp(s, "pcm") == 0)
|
||||
return MEDIA_FORMAT_PCM;
|
||||
if (strcmp(s, "wav") == 0)
|
||||
return MEDIA_FORMAT_WAV;
|
||||
if (strcmp(s, "mp3") == 0)
|
||||
return MEDIA_FORMAT_MP3;
|
||||
if (strcmp(s, "alac") == 0)
|
||||
return MEDIA_FORMAT_ALAC;
|
||||
if (strcmp(s, "opus") == 0)
|
||||
return MEDIA_FORMAT_OPUS;
|
||||
|
||||
return MEDIA_FORMAT_UNKNOWN;
|
||||
}
|
||||
|
||||
const char *
|
||||
media_format_to_string(enum media_format format)
|
||||
{
|
||||
if (format == MEDIA_FORMAT_PCM)
|
||||
return "pcm";
|
||||
if (format == MEDIA_FORMAT_WAV)
|
||||
return "wav";
|
||||
if (format == MEDIA_FORMAT_MP3)
|
||||
return "mp3";
|
||||
if (format == MEDIA_FORMAT_ALAC)
|
||||
return "alac";
|
||||
if (format == MEDIA_FORMAT_OPUS)
|
||||
return "opus";
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------- Misc utility functions ------------------------ */
|
||||
|
||||
|
|
21
src/misc.h
21
src/misc.h
|
@ -272,6 +272,21 @@ timespec_reltoabs(struct timespec relative);
|
|||
|
||||
/* ------------------------------- Media quality ---------------------------- */
|
||||
|
||||
// Bit flags for the sake of outputs announcing what they support
|
||||
enum media_format {
|
||||
MEDIA_FORMAT_UNKNOWN = 0,
|
||||
MEDIA_FORMAT_PCM = (1 << 0),
|
||||
MEDIA_FORMAT_WAV = (1 << 1),
|
||||
MEDIA_FORMAT_MP3 = (1 << 2),
|
||||
MEDIA_FORMAT_ALAC = (1 << 3),
|
||||
MEDIA_FORMAT_OPUS = (1 << 4),
|
||||
};
|
||||
|
||||
// For iteration
|
||||
#define MEDIA_FORMAT_FIRST MEDIA_FORMAT_PCM
|
||||
#define MEDIA_FORMAT_LAST MEDIA_FORMAT_OPUS
|
||||
#define MEDIA_FORMAT_NEXT(f) (f << 1)
|
||||
|
||||
// Remember to adjust quality_is_equal() if adding elements
|
||||
struct media_quality {
|
||||
int sample_rate;
|
||||
|
@ -283,6 +298,12 @@ struct media_quality {
|
|||
bool
|
||||
quality_is_equal(struct media_quality *a, struct media_quality *b);
|
||||
|
||||
enum media_format
|
||||
media_format_from_string(const char *s);
|
||||
|
||||
const char *
|
||||
media_format_to_string(enum media_format format);
|
||||
|
||||
|
||||
/* -------------------------- Misc utility functions ------------------------ */
|
||||
|
||||
|
|
|
@ -134,7 +134,11 @@ struct output_device
|
|||
|
||||
// Quality of audio output
|
||||
struct media_quality quality;
|
||||
int format;
|
||||
|
||||
// selected_format only set (not UNKNOWN) in case of active user selection
|
||||
enum media_format selected_format;
|
||||
enum media_format default_format;
|
||||
uint32_t supported_formats;
|
||||
|
||||
// Address
|
||||
char *v4_address;
|
||||
|
|
|
@ -3737,6 +3737,7 @@ airplay_device_cb(const char *name, const char *type, const char *domain, const
|
|||
rd->type = OUTPUT_TYPE_AIRPLAY;
|
||||
rd->type_name = outputs_name(rd->type);
|
||||
rd->extra_device_info = re;
|
||||
rd->supported_formats = MEDIA_FORMAT_ALAC;
|
||||
|
||||
if (port < 0)
|
||||
{
|
||||
|
|
|
@ -1374,6 +1374,7 @@ alsa_device_add(cfg_t* cfg_audio, int id)
|
|||
device->type = OUTPUT_TYPE_ALSA;
|
||||
device->type_name = outputs_name(device->type);
|
||||
device->extra_device_info = ae;
|
||||
device->supported_formats = MEDIA_FORMAT_PCM;
|
||||
|
||||
// The audio section will have no title, so there we get the value from the
|
||||
// "card" option
|
||||
|
|
|
@ -1778,6 +1778,7 @@ cast_device_cb(const char *name, const char *type, const char *domain, const cha
|
|||
device->name = strdup(name);
|
||||
device->type = OUTPUT_TYPE_CAST;
|
||||
device->type_name = outputs_name(device->type);
|
||||
device->supported_formats = MEDIA_FORMAT_OPUS;
|
||||
|
||||
if (port < 0)
|
||||
{
|
||||
|
|
|
@ -491,6 +491,7 @@ fifo_init(void)
|
|||
device->type_name = outputs_name(device->type);
|
||||
device->has_video = 0;
|
||||
device->extra_device_info = path;
|
||||
device->supported_formats = MEDIA_FORMAT_PCM;
|
||||
DPRINTF(E_INFO, L_FIFO, "Adding fifo output device '%s' with path '%s'\n", nickname, path);
|
||||
|
||||
player_device_add(device);
|
||||
|
|
|
@ -436,6 +436,7 @@ sinklist_cb(pa_context *ctx, const pa_sink_info *info, int eol, void *userdata)
|
|||
device->type = OUTPUT_TYPE_PULSE;
|
||||
device->type_name = outputs_name(device->type);
|
||||
device->extra_device_info = strdup(info->name);
|
||||
device->supported_formats = MEDIA_FORMAT_PCM;
|
||||
|
||||
player_device_add(device);
|
||||
}
|
||||
|
|
|
@ -4230,6 +4230,7 @@ raop_device_cb(const char *name, const char *type, const char *domain, const cha
|
|||
rd->type = OUTPUT_TYPE_RAOP;
|
||||
rd->type_name = outputs_name(rd->type);
|
||||
rd->extra_device_info = re;
|
||||
rd->supported_formats = MEDIA_FORMAT_ALAC;
|
||||
|
||||
if (port < 0)
|
||||
{
|
||||
|
|
|
@ -1295,6 +1295,8 @@ rcp_mdns_device_cb(const char *name, const char *type, const char *domain, const
|
|||
device->name = strdup(name);
|
||||
device->type = OUTPUT_TYPE_RCP;
|
||||
device->type_name = outputs_name(device->type);
|
||||
device->default_format = MEDIA_FORMAT_WAV;
|
||||
device->supported_formats = MEDIA_FORMAT_WAV | MEDIA_FORMAT_MP3 | MEDIA_FORMAT_ALAC;
|
||||
|
||||
if (port < 0 || !address)
|
||||
{
|
||||
|
|
|
@ -65,7 +65,7 @@ struct streaming_wanted
|
|||
struct pipepair audio[WANTED_PIPES_MAX];
|
||||
struct pipepair metadata[WANTED_PIPES_MAX];
|
||||
|
||||
enum player_format format;
|
||||
enum media_format format;
|
||||
struct media_quality quality;
|
||||
|
||||
struct evbuffer *audio_in;
|
||||
|
@ -113,7 +113,7 @@ extern struct event_base *evbase_player;
|
|||
/* ------------------------------- Helpers ---------------------------------- */
|
||||
|
||||
static struct encode_ctx *
|
||||
encoder_setup(enum player_format format, struct media_quality *quality)
|
||||
encoder_setup(enum media_format format, struct media_quality *quality)
|
||||
{
|
||||
struct transcode_encode_setup_args encode_args = { .profile = XCODE_MP3, .quality = quality };
|
||||
struct encode_ctx *encode_ctx = NULL;
|
||||
|
@ -132,7 +132,7 @@ encoder_setup(enum player_format format, struct media_quality *quality)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (format == PLAYER_FORMAT_MP3)
|
||||
if (format == MEDIA_FORMAT_MP3)
|
||||
encode_ctx = transcode_encode_setup(encode_args);
|
||||
|
||||
if (!encode_ctx)
|
||||
|
@ -217,7 +217,7 @@ pipe_index_find_byreadfd(struct pipepair *p, int readfd)
|
|||
}
|
||||
|
||||
static struct streaming_wanted *
|
||||
wanted_new(enum player_format format, struct media_quality quality)
|
||||
wanted_new(enum media_format format, struct media_quality quality)
|
||||
{
|
||||
struct streaming_wanted *w;
|
||||
|
||||
|
@ -277,7 +277,7 @@ wanted_remove(struct streaming_wanted **wanted, struct streaming_wanted *remove)
|
|||
}
|
||||
|
||||
static struct streaming_wanted *
|
||||
wanted_add(struct streaming_wanted **wanted, enum player_format format, struct media_quality quality)
|
||||
wanted_add(struct streaming_wanted **wanted, enum media_format format, struct media_quality quality)
|
||||
{
|
||||
struct streaming_wanted *w;
|
||||
|
||||
|
@ -289,7 +289,7 @@ wanted_add(struct streaming_wanted **wanted, enum player_format format, struct m
|
|||
}
|
||||
|
||||
static struct streaming_wanted *
|
||||
wanted_find_byformat(struct streaming_wanted *wanted, enum player_format format, struct media_quality quality)
|
||||
wanted_find_byformat(struct streaming_wanted *wanted, enum media_format format, struct media_quality quality)
|
||||
{
|
||||
struct streaming_wanted *w;
|
||||
|
||||
|
@ -623,9 +623,9 @@ streaming_start(struct output_device *device, int callback_id)
|
|||
int ret;
|
||||
|
||||
pthread_mutex_lock(&streaming_wanted_lck);
|
||||
w = wanted_find_byformat(streaming.wanted, device->format, device->quality);
|
||||
w = wanted_find_byformat(streaming.wanted, device->selected_format, device->quality);
|
||||
if (!w)
|
||||
w = wanted_add(&streaming.wanted, device->format, device->quality);
|
||||
w = wanted_add(&streaming.wanted, device->selected_format, device->quality);
|
||||
ret = wanted_session_add(&device->audio_fd, &device->metadata_fd, w);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
|
38
src/player.c
38
src/player.c
|
@ -152,7 +152,7 @@ struct speaker_attr_param
|
|||
bool busy;
|
||||
|
||||
struct media_quality quality;
|
||||
enum player_format format;
|
||||
enum media_format format;
|
||||
|
||||
int audio_fd;
|
||||
int metadata_fd;
|
||||
|
@ -2526,7 +2526,15 @@ device_to_speaker_info(struct player_speaker_info *spk, struct output_device *de
|
|||
spk->output_type[sizeof(spk->output_type) - 1] = '\0';
|
||||
spk->relvol = device->relvol;
|
||||
spk->absvol = device->volume;
|
||||
spk->format = device->format;
|
||||
|
||||
spk->supported_formats = device->supported_formats;
|
||||
// Devices supporting more than one format should at least have default_format set
|
||||
if (device->selected_format != MEDIA_FORMAT_UNKNOWN)
|
||||
spk->format = device->selected_format;
|
||||
else if (device->default_format != MEDIA_FORMAT_UNKNOWN)
|
||||
spk->format = device->default_format;
|
||||
else
|
||||
spk->format = device->supported_formats;
|
||||
|
||||
spk->selected = OUTPUTS_DEVICE_DISPLAY_SELECTED(device);
|
||||
|
||||
|
@ -2814,19 +2822,25 @@ speaker_format_set(void *arg, int *retval)
|
|||
struct speaker_attr_param *param = arg;
|
||||
struct output_device *device;
|
||||
|
||||
*retval = -1;
|
||||
|
||||
if (param->format == PLAYER_FORMAT_UNKNOWN)
|
||||
return COMMAND_END;
|
||||
if (param->format == MEDIA_FORMAT_UNKNOWN)
|
||||
goto error;
|
||||
|
||||
device = outputs_device_get(param->spk_id);
|
||||
if (!device)
|
||||
return COMMAND_END;
|
||||
goto error;
|
||||
|
||||
device->format = param->format;
|
||||
if (!(param->format & device->supported_formats))
|
||||
goto error;
|
||||
|
||||
device->selected_format = param->format;
|
||||
|
||||
*retval = 0;
|
||||
return COMMAND_END;
|
||||
|
||||
error:
|
||||
DPRINTF(E_LOG, L_PLAYER, "Error setting format '%s', device unknown or format unsupported\n", media_format_to_string(param->format));
|
||||
*retval = -1;
|
||||
return COMMAND_END;
|
||||
}
|
||||
|
||||
// Attempts to reactivate a speaker that has failed. That includes restarting
|
||||
|
@ -2933,7 +2947,7 @@ streaming_register(void *arg, int *retval)
|
|||
.type_name = "streaming",
|
||||
.name = "streaming",
|
||||
.quality = param->quality,
|
||||
.format = param->format,
|
||||
.selected_format = param->format,
|
||||
};
|
||||
|
||||
*retval = outputs_device_start(&device, NULL, false);
|
||||
|
@ -3500,18 +3514,18 @@ player_speaker_authorize(uint64_t id, const char *pin)
|
|||
}
|
||||
|
||||
int
|
||||
player_speaker_format_set(uint64_t id, enum player_format format)
|
||||
player_speaker_format_set(uint64_t id, enum media_format format)
|
||||
{
|
||||
struct speaker_attr_param param;
|
||||
|
||||
param.spk_id = id;
|
||||
param.format = format;
|
||||
|
||||
return commands_exec_sync(cmdbase, speaker_format_set, NULL, ¶m);
|
||||
return commands_exec_sync(cmdbase, speaker_format_set, speaker_generic_bh, ¶m);
|
||||
}
|
||||
|
||||
int
|
||||
player_streaming_register(int *audio_fd, int *metadata_fd, enum player_format format, struct media_quality quality)
|
||||
player_streaming_register(int *audio_fd, int *metadata_fd, enum media_format format, struct media_quality quality)
|
||||
{
|
||||
struct speaker_attr_param param;
|
||||
int ret;
|
||||
|
|
16
src/player.h
16
src/player.h
|
@ -28,15 +28,6 @@ enum player_seek_mode {
|
|||
PLAYER_SEEK_RELATIVE = 2,
|
||||
};
|
||||
|
||||
enum player_format {
|
||||
PLAYER_FORMAT_UNKNOWN = -1,
|
||||
PLAYER_FORMAT_PCM = 0,
|
||||
PLAYER_FORMAT_WAV = 1,
|
||||
PLAYER_FORMAT_MP3 = 2,
|
||||
PLAYER_FORMAT_ALAC = 3,
|
||||
PLAYER_FORMAT_OPUS = 4,
|
||||
};
|
||||
|
||||
struct player_speaker_info {
|
||||
uint64_t id;
|
||||
uint32_t active_remote;
|
||||
|
@ -45,7 +36,8 @@ struct player_speaker_info {
|
|||
int relvol;
|
||||
int absvol;
|
||||
|
||||
enum player_format format;
|
||||
enum media_format format;
|
||||
uint32_t supported_formats;
|
||||
|
||||
bool selected;
|
||||
bool has_password;
|
||||
|
@ -130,10 +122,10 @@ int
|
|||
player_speaker_authorize(uint64_t id, const char *pin);
|
||||
|
||||
int
|
||||
player_speaker_format_set(uint64_t id, enum player_format format);
|
||||
player_speaker_format_set(uint64_t id, enum media_format format);
|
||||
|
||||
int
|
||||
player_streaming_register(int *audio_fd, int *metadata_fd, enum player_format format, struct media_quality quality);
|
||||
player_streaming_register(int *audio_fd, int *metadata_fd, enum media_format format, struct media_quality quality);
|
||||
|
||||
int
|
||||
player_streaming_deregister(int id);
|
||||
|
|
Loading…
Reference in New Issue