mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-15 00:35:03 -05:00
[chromecast] Fix host address, some more commands, add flush timer
This commit is contained in:
parent
5049426573
commit
7d6c670fee
@ -180,7 +180,6 @@ outputs_write(uint8_t *buf, uint64_t rtptime)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO output of this makes little sense
|
|
||||||
int
|
int
|
||||||
outputs_flush(output_status_cb cb, uint64_t rtptime)
|
outputs_flush(output_status_cb cb, uint64_t rtptime)
|
||||||
{
|
{
|
||||||
@ -194,7 +193,7 @@ outputs_flush(output_status_cb cb, uint64_t rtptime)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (outputs[i]->flush)
|
if (outputs[i]->flush)
|
||||||
ret = outputs[i]->flush(cb, rtptime);
|
ret += outputs[i]->flush(cb, rtptime);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -167,7 +167,7 @@ struct output_definition
|
|||||||
// Write stream data to the output devices
|
// Write stream data to the output devices
|
||||||
void (*write)(uint8_t *buf, uint64_t rtptime);
|
void (*write)(uint8_t *buf, uint64_t rtptime);
|
||||||
|
|
||||||
// Flush all sessions
|
// Flush all sessions, the return must be number of sessions pending the flush
|
||||||
int (*flush)(output_status_cb cb, uint64_t rtptime);
|
int (*flush)(output_status_cb cb, uint64_t rtptime);
|
||||||
|
|
||||||
// Change the call back associated with a session
|
// Change the call back associated with a session
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
#include <ifaddrs.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <endian.h>
|
#include <endian.h>
|
||||||
@ -90,6 +91,9 @@ struct cast_session
|
|||||||
|
|
||||||
float volume;
|
float volume;
|
||||||
|
|
||||||
|
// IP address URL of forked-daapd's mp3 stream
|
||||||
|
char stream_url[128];
|
||||||
|
|
||||||
// Outgoing request which have the USE_REQUEST_ID flag get a new id, and a
|
// Outgoing request which have the USE_REQUEST_ID flag get a new id, and a
|
||||||
// callback is registered. The callback is called when an incoming message
|
// callback is registered. The callback is called when an incoming message
|
||||||
// from the peer with that request id arrives.
|
// from the peer with that request id arrives.
|
||||||
@ -99,6 +103,7 @@ struct cast_session
|
|||||||
// Session info from the ChromeCast
|
// Session info from the ChromeCast
|
||||||
char *transport_id;
|
char *transport_id;
|
||||||
char *session_id;
|
char *session_id;
|
||||||
|
int media_session_id;
|
||||||
|
|
||||||
/* Do not dereference - only passed to the status cb */
|
/* Do not dereference - only passed to the status cb */
|
||||||
struct output_device *device;
|
struct output_device *device;
|
||||||
@ -122,6 +127,8 @@ enum cast_msg_types
|
|||||||
MEDIA_GET_STATUS,
|
MEDIA_GET_STATUS,
|
||||||
MEDIA_STATUS,
|
MEDIA_STATUS,
|
||||||
MEDIA_LOAD,
|
MEDIA_LOAD,
|
||||||
|
MEDIA_PLAY,
|
||||||
|
MEDIA_PAUSE,
|
||||||
MEDIA_STOP,
|
MEDIA_STOP,
|
||||||
MEDIA_LOAD_FAILED,
|
MEDIA_LOAD_FAILED,
|
||||||
SET_VOLUME,
|
SET_VOLUME,
|
||||||
@ -143,6 +150,7 @@ struct cast_msg_payload
|
|||||||
int request_id;
|
int request_id;
|
||||||
const char *session_id;
|
const char *session_id;
|
||||||
const char *transport_id;
|
const char *transport_id;
|
||||||
|
int media_session_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Array of the cast messages that we use. Must be in sync with cast_msg_types.
|
// Array of the cast messages that we use. Must be in sync with cast_msg_types.
|
||||||
@ -215,10 +223,22 @@ struct cast_msg_basic cast_msg[] =
|
|||||||
.payload = "{'currentTime':0,'media':{'contentId':'%s','streamType':'LIVE','contentType':'audio/mp3'},'customData':{},'sessionId':'%s','requestId':%d,'type':'LOAD','autoplay':1}",
|
.payload = "{'currentTime':0,'media':{'contentId':'%s','streamType':'LIVE','contentType':'audio/mp3'},'customData':{},'sessionId':'%s','requestId':%d,'type':'LOAD','autoplay':1}",
|
||||||
.flags = USE_TRANSPORT_ID | USE_REQUEST_ID,
|
.flags = USE_TRANSPORT_ID | USE_REQUEST_ID,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.type = MEDIA_PLAY,
|
||||||
|
.namespace = NS_MEDIA,
|
||||||
|
.payload = "{'mediaSessionId':%d,'sessionId':'%s','type':'PLAY','requestId':%d}",
|
||||||
|
.flags = USE_TRANSPORT_ID | USE_REQUEST_ID,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.type = MEDIA_PAUSE,
|
||||||
|
.namespace = NS_MEDIA,
|
||||||
|
.payload = "{'mediaSessionId':%d,'sessionId':'%s','type':'PAUSE','requestId':%d}",
|
||||||
|
.flags = USE_TRANSPORT_ID | USE_REQUEST_ID,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.type = MEDIA_STOP,
|
.type = MEDIA_STOP,
|
||||||
.namespace = NS_RECEIVER,
|
.namespace = NS_MEDIA,
|
||||||
.payload = "{'mediaSessionId':1,'sessionId':'%s','type':'STOP','requestId':%d}",
|
.payload = "{'mediaSessionId':%d,'sessionId':'%s','type':'STOP','requestId':%d}",
|
||||||
.flags = USE_TRANSPORT_ID | USE_REQUEST_ID,
|
.flags = USE_TRANSPORT_ID | USE_REQUEST_ID,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -242,8 +262,7 @@ extern struct event_base *evbase_player;
|
|||||||
/* Globals */
|
/* Globals */
|
||||||
static gnutls_certificate_credentials_t tls_credentials;
|
static gnutls_certificate_credentials_t tls_credentials;
|
||||||
static struct cast_session *sessions;
|
static struct cast_session *sessions;
|
||||||
|
static struct event *flush_timer;
|
||||||
static char cast_stream_url[1024];
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -311,6 +330,63 @@ tcp_close(int fd)
|
|||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
stream_url_make(char *out, size_t len, const char *peer_addr, int family)
|
||||||
|
{
|
||||||
|
struct ifaddrs *ifap;
|
||||||
|
struct ifaddrs *ifa;
|
||||||
|
union sockaddr_all haddr;
|
||||||
|
union sockaddr_all hmask;
|
||||||
|
union sockaddr_all paddr;
|
||||||
|
char host_addr[128];
|
||||||
|
unsigned short port;
|
||||||
|
int found;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (family == AF_INET)
|
||||||
|
ret = inet_pton(AF_INET, peer_addr, &paddr.sin.sin_addr);
|
||||||
|
else
|
||||||
|
ret = inet_pton(AF_INET6, peer_addr, &paddr.sin6.sin6_addr);
|
||||||
|
|
||||||
|
if (ret != 1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
found = 0;
|
||||||
|
getifaddrs(&ifap);
|
||||||
|
for (ifa = ifap; !found && ifa; ifa = ifa->ifa_next)
|
||||||
|
{
|
||||||
|
if (ifa->ifa_addr->sa_family != family)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (family == AF_INET)
|
||||||
|
{
|
||||||
|
memcpy(&haddr.sin, ifa->ifa_addr, sizeof(struct sockaddr_in));
|
||||||
|
memcpy(&hmask.sin, ifa->ifa_netmask, sizeof(struct sockaddr_in));
|
||||||
|
found = ((haddr.sin.sin_addr.s_addr & hmask.sin.sin_addr.s_addr) ==
|
||||||
|
(paddr.sin.sin_addr.s_addr & hmask.sin.sin_addr.s_addr));
|
||||||
|
if (found)
|
||||||
|
inet_ntop(family, &haddr.sin.sin_addr, host_addr, sizeof(host_addr));
|
||||||
|
}
|
||||||
|
else if (family == AF_INET6)
|
||||||
|
{
|
||||||
|
memcpy(&haddr.sin6, ifa->ifa_addr, sizeof(struct sockaddr_in6));
|
||||||
|
found = (memcmp(&haddr.sin6.sin6_addr.s6_addr, &paddr.sin6.sin6_addr.s6_addr, 8) == 0);
|
||||||
|
if (found)
|
||||||
|
inet_ntop(family, &haddr.sin6.sin6_addr, host_addr, sizeof(host_addr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
freeifaddrs(ifap);
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
port = cfg_getint(cfg_getsec(cfg, "library"), "port");
|
||||||
|
snprintf(out, len, "http://%s:%d/stream.mp3", host_addr, port);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
squote_to_dquote(char *buf)
|
squote_to_dquote(char *buf)
|
||||||
{
|
{
|
||||||
@ -403,9 +479,9 @@ cast_msg_send(struct cast_session *cs, enum cast_msg_types type, cast_reply_cb r
|
|||||||
if (cast_msg[type].flags & USE_REQUEST_ID_ONLY)
|
if (cast_msg[type].flags & USE_REQUEST_ID_ONLY)
|
||||||
snprintf(msg_buf, sizeof(msg_buf), cast_msg[type].payload, cs->request_id);
|
snprintf(msg_buf, sizeof(msg_buf), cast_msg[type].payload, cs->request_id);
|
||||||
else if (type == MEDIA_LOAD)
|
else if (type == MEDIA_LOAD)
|
||||||
snprintf(msg_buf, sizeof(msg_buf), cast_msg[type].payload, TEST_STREAM_URL, cs->session_id, cs->request_id);
|
snprintf(msg_buf, sizeof(msg_buf), cast_msg[type].payload, cs->stream_url, cs->session_id, cs->request_id);
|
||||||
else if (type == MEDIA_STOP)
|
else if ((type == MEDIA_PLAY) || (type == MEDIA_PAUSE) || (type == MEDIA_STOP))
|
||||||
snprintf(msg_buf, sizeof(msg_buf), cast_msg[type].payload, cs->session_id, cs->request_id);
|
snprintf(msg_buf, sizeof(msg_buf), cast_msg[type].payload, cs->media_session_id, cs->session_id, cs->request_id);
|
||||||
else if (type == SET_VOLUME)
|
else if (type == SET_VOLUME)
|
||||||
snprintf(msg_buf, sizeof(msg_buf), cast_msg[type].payload, cs->volume, cs->request_id);
|
snprintf(msg_buf, sizeof(msg_buf), cast_msg[type].payload, cs->volume, cs->request_id);
|
||||||
else
|
else
|
||||||
@ -479,10 +555,17 @@ cast_msg_parse(struct cast_msg_payload *payload, char *s)
|
|||||||
payload->request_id = json_object_get_int(needle);
|
payload->request_id = json_object_get_int(needle);
|
||||||
|
|
||||||
// Might be done now
|
// Might be done now
|
||||||
if (payload->type != RECEIVER_STATUS)
|
if ((payload->type != RECEIVER_STATUS) && (payload->type != MEDIA_STATUS))
|
||||||
return haystack;
|
return haystack;
|
||||||
|
|
||||||
// Isn't this marvelous
|
// Isn't this marvelous
|
||||||
|
if ( json_object_object_get_ex(haystack, "status", &needle) &&
|
||||||
|
(json_object_get_type(needle) == json_type_array) &&
|
||||||
|
(somehay = json_object_array_get_idx(needle, 0)) &&
|
||||||
|
json_object_object_get_ex(somehay, "mediaSessionId", &needle) &&
|
||||||
|
(json_object_get_type(needle) == json_type_int) )
|
||||||
|
payload->media_session_id = json_object_get_int(needle);
|
||||||
|
|
||||||
if ( ! (json_object_object_get_ex(haystack, "status", &somehay) &&
|
if ( ! (json_object_object_get_ex(haystack, "status", &somehay) &&
|
||||||
json_object_object_get_ex(somehay, "applications", &needle) &&
|
json_object_object_get_ex(somehay, "applications", &needle) &&
|
||||||
(json_object_get_type(needle) == json_type_array) &&
|
(json_object_get_type(needle) == json_type_array) &&
|
||||||
@ -706,9 +789,21 @@ cast_cb_startup_connect(struct cast_session *cs, struct cast_msg_payload *payloa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Not really a cb right now
|
||||||
static void
|
static void
|
||||||
cast_cb_close(struct cast_session *cs, struct cast_msg_payload *payload)
|
cast_cb_close(struct cast_session *cs, struct cast_msg_payload *payload)
|
||||||
{
|
{
|
||||||
|
output_status_cb status_cb;
|
||||||
|
|
||||||
|
cs->state = OUTPUT_STATE_STOPPED;
|
||||||
|
|
||||||
|
/* Session shut down, tell our user */
|
||||||
|
status_cb = cs->status_cb;
|
||||||
|
cs->status_cb = NULL;
|
||||||
|
|
||||||
|
status_cb(cs->device, cs->output_session, cs->state);
|
||||||
|
|
||||||
|
cast_session_cleanup(cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -722,6 +817,11 @@ cast_cb_load(struct cast_session *cs, struct cast_msg_payload *payload)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!payload->media_session_id)
|
||||||
|
DPRINTF(E_LOG, L_CAST, "Did not get a media session id?\n");
|
||||||
|
else
|
||||||
|
cs->media_session_id = payload->media_session_id;
|
||||||
|
|
||||||
cs->state = OUTPUT_STATE_STREAMING;
|
cs->state = OUTPUT_STATE_STREAMING;
|
||||||
|
|
||||||
DPRINTF(E_DBG, L_CAST, "Media loaded\n");
|
DPRINTF(E_DBG, L_CAST, "Media loaded\n");
|
||||||
@ -729,6 +829,19 @@ cast_cb_load(struct cast_session *cs, struct cast_msg_payload *payload)
|
|||||||
cs->status_cb(cs->device, cs->output_session, cs->state);
|
cs->status_cb(cs->device, cs->output_session, cs->state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cast_cb_stop(struct cast_session *cs, struct cast_msg_payload *payload)
|
||||||
|
{
|
||||||
|
output_status_cb status_cb;
|
||||||
|
|
||||||
|
cs->state = OUTPUT_STATE_CONNECTED;
|
||||||
|
|
||||||
|
status_cb = cs->status_cb;
|
||||||
|
cs->status_cb = NULL;
|
||||||
|
status_cb(cs->device, cs->output_session, cs->state);
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cast_cb_volume(struct cast_session *cs, struct cast_msg_payload *payload)
|
cast_cb_volume(struct cast_session *cs, struct cast_msg_payload *payload)
|
||||||
{
|
{
|
||||||
@ -739,6 +852,21 @@ cast_cb_volume(struct cast_session *cs, struct cast_msg_payload *payload)
|
|||||||
status_cb(cs->device, cs->output_session, cs->state);
|
status_cb(cs->device, cs->output_session, cs->state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cast_cb_flush(struct cast_session *cs, struct cast_msg_payload *payload)
|
||||||
|
{
|
||||||
|
output_status_cb status_cb;
|
||||||
|
|
||||||
|
DPRINTF(E_DBG, L_CAST, "Cast CB flush called\n");
|
||||||
|
|
||||||
|
cs->state = OUTPUT_STATE_CONNECTED;
|
||||||
|
|
||||||
|
/* Let our user know */
|
||||||
|
status_cb = cs->status_cb;
|
||||||
|
cs->status_cb = NULL;
|
||||||
|
status_cb(cs->device, cs->output_session, cs->state);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -852,7 +980,17 @@ cast_session_make(struct output_device *device, int family, output_status_cb cb)
|
|||||||
|
|
||||||
cs->server_fd = tcp_connect(address, port, family);
|
cs->server_fd = tcp_connect(address, port, family);
|
||||||
if (cs->server_fd < 0)
|
if (cs->server_fd < 0)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_CAST, "Could not connect to %s\n", device->name);
|
||||||
goto out_deinit_gnutls;
|
goto out_deinit_gnutls;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = stream_url_make(cs->stream_url, sizeof(cs->stream_url), address, family);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_CAST, "Bug! Could find a network interface on same subnet as %s\n", device->name);
|
||||||
|
goto out_close_connection;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO Add a timeout to detect connection problems
|
// TODO Add a timeout to detect connection problems
|
||||||
cs->ev = event_new(evbase_player, cs->server_fd, EV_READ | EV_PERSIST, cast_listen_cb, cs);
|
cs->ev = event_new(evbase_player, cs->server_fd, EV_READ | EV_PERSIST, cast_listen_cb, cs);
|
||||||
@ -1028,9 +1166,13 @@ cast_device_stop(struct output_session *session)
|
|||||||
struct cast_session *cs = session->session;
|
struct cast_session *cs = session->session;
|
||||||
|
|
||||||
if (!(cs->state & OUTPUT_STATE_F_CONNECTED))
|
if (!(cs->state & OUTPUT_STATE_F_CONNECTED))
|
||||||
|
{
|
||||||
cast_session_cleanup(cs);
|
cast_session_cleanup(cs);
|
||||||
else
|
return;
|
||||||
cast_msg_send(cs, CLOSE, cast_cb_close);
|
}
|
||||||
|
|
||||||
|
cast_msg_send(cs, CLOSE, NULL);
|
||||||
|
cast_cb_close(cs, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -1066,6 +1208,9 @@ cast_playback_start(uint64_t next_pkt, struct timespec *ts)
|
|||||||
{
|
{
|
||||||
struct cast_session *cs;
|
struct cast_session *cs;
|
||||||
|
|
||||||
|
if (evtimer_pending(flush_timer, NULL))
|
||||||
|
event_del(flush_timer);
|
||||||
|
|
||||||
for (cs = sessions; cs; cs = cs->next)
|
for (cs = sessions; cs; cs = cs->next)
|
||||||
{
|
{
|
||||||
if (cs->state == OUTPUT_STATE_CONNECTED)
|
if (cs->state == OUTPUT_STATE_CONNECTED)
|
||||||
@ -1073,6 +1218,66 @@ cast_playback_start(uint64_t next_pkt, struct timespec *ts)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cast_playback_stop(void)
|
||||||
|
{
|
||||||
|
struct cast_session *cs;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
for (cs = sessions; cs; cs = cs->next)
|
||||||
|
{
|
||||||
|
ret = cast_msg_send(cs, MEDIA_STOP, cast_cb_stop);
|
||||||
|
if (ret < 0)
|
||||||
|
DPRINTF(E_LOG, L_CAST, "MEDIA_STOP request failed!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cast_flush_timer_cb(int fd, short what, void *arg)
|
||||||
|
{
|
||||||
|
DPRINTF(E_DBG, L_CAST, "Flush timer expired; tearing down all sessions\n");
|
||||||
|
|
||||||
|
cast_playback_stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cast_flush(output_status_cb cb, uint64_t rtptime)
|
||||||
|
{
|
||||||
|
struct cast_session *cs;
|
||||||
|
struct cast_session *next;
|
||||||
|
struct timeval tv;
|
||||||
|
int pending;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
pending = 0;
|
||||||
|
for (cs = sessions; cs; cs = next)
|
||||||
|
{
|
||||||
|
next = cs->next;
|
||||||
|
|
||||||
|
if (cs->state != OUTPUT_STATE_STREAMING)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = cast_msg_send(cs, MEDIA_PAUSE, cast_cb_flush);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
cast_session_failure(cs);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cs->status_cb = cb;
|
||||||
|
pending++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pending > 0)
|
||||||
|
{
|
||||||
|
evutil_timerclear(&tv);
|
||||||
|
tv.tv_sec = 10;
|
||||||
|
evtimer_add(flush_timer, &tv);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pending;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cast_set_status_cb(struct output_session *session, output_status_cb cb)
|
cast_set_status_cb(struct output_session *session, output_status_cb cb)
|
||||||
{
|
{
|
||||||
@ -1081,12 +1286,9 @@ cast_set_status_cb(struct output_session *session, output_status_cb cb)
|
|||||||
cs->status_cb = cb;
|
cs->status_cb = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cast_init(void)
|
cast_init(void)
|
||||||
{
|
{
|
||||||
char hostname[256];
|
|
||||||
int port;
|
|
||||||
int mdns_flags;
|
int mdns_flags;
|
||||||
int i;
|
int i;
|
||||||
int ret;
|
int ret;
|
||||||
@ -1101,16 +1303,6 @@ cast_init(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = gethostname(hostname, sizeof(hostname));
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_CAST, "Could not determine own hostname: %s\n", strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
port = cfg_getint(cfg_getsec(cfg, "library"), "port");
|
|
||||||
|
|
||||||
snprintf(cast_stream_url, sizeof(cast_stream_url), "http://%s:%d/stream.mp3", hostname, port);
|
|
||||||
|
|
||||||
// TODO Setting the cert file may not be required
|
// TODO Setting the cert file may not be required
|
||||||
if ( ((ret = gnutls_global_init()) != GNUTLS_E_SUCCESS) ||
|
if ( ((ret = gnutls_global_init()) != GNUTLS_E_SUCCESS) ||
|
||||||
((ret = gnutls_certificate_allocate_credentials(&tls_credentials)) != GNUTLS_E_SUCCESS) ||
|
((ret = gnutls_certificate_allocate_credentials(&tls_credentials)) != GNUTLS_E_SUCCESS) ||
|
||||||
@ -1120,17 +1312,26 @@ cast_init(void)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flush_timer = evtimer_new(evbase_player, cast_flush_timer_cb, NULL);
|
||||||
|
if (!flush_timer)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_CAST, "Out of memory for flush timer\n");
|
||||||
|
goto out_tls_deinit;
|
||||||
|
}
|
||||||
|
|
||||||
mdns_flags = MDNS_WANT_V4 | MDNS_WANT_V6 | MDNS_WANT_V6LL;
|
mdns_flags = MDNS_WANT_V4 | MDNS_WANT_V6 | MDNS_WANT_V6LL;
|
||||||
|
|
||||||
ret = mdns_browse("_googlecast._tcp", mdns_flags, cast_device_cb);
|
ret = mdns_browse("_googlecast._tcp", mdns_flags, cast_device_cb);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_PLAYER, "Could not add mDNS browser for Chromecast devices\n");
|
DPRINTF(E_LOG, L_PLAYER, "Could not add mDNS browser for Chromecast devices\n");
|
||||||
goto out_tls_deinit;
|
goto out_free_flush_timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
out_free_flush_timer:
|
||||||
|
event_free(flush_timer);
|
||||||
out_tls_deinit:
|
out_tls_deinit:
|
||||||
gnutls_certificate_free_credentials(tls_credentials);
|
gnutls_certificate_free_credentials(tls_credentials);
|
||||||
gnutls_global_deinit();
|
gnutls_global_deinit();
|
||||||
@ -1149,6 +1350,8 @@ cast_deinit(void)
|
|||||||
cast_session_free(cs);
|
cast_session_free(cs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event_free(flush_timer);
|
||||||
|
|
||||||
gnutls_certificate_free_credentials(tls_credentials);
|
gnutls_certificate_free_credentials(tls_credentials);
|
||||||
gnutls_global_deinit();
|
gnutls_global_deinit();
|
||||||
}
|
}
|
||||||
@ -1163,13 +1366,13 @@ struct output_definition output_cast =
|
|||||||
.deinit = cast_deinit,
|
.deinit = cast_deinit,
|
||||||
.device_start = cast_device_start,
|
.device_start = cast_device_start,
|
||||||
.device_stop = cast_device_stop,
|
.device_stop = cast_device_stop,
|
||||||
/* .device_probe = cast_device_probe,
|
// .device_probe = cast_device_probe,
|
||||||
.device_free_extra = cast_device_free_extra,*/
|
// .device_free_extra is unset - nothing to free
|
||||||
.device_volume_set = cast_volume_set,
|
.device_volume_set = cast_volume_set,
|
||||||
.playback_start = cast_playback_start,
|
.playback_start = cast_playback_start,
|
||||||
/* .playback_stop = cast_playback_stop,
|
.playback_stop = cast_playback_stop,
|
||||||
.write = cast_write,
|
// .write is unset - we don't write, the Chromecast will read our mp3 stream
|
||||||
.flush = cast_flush,*/
|
.flush = cast_flush,
|
||||||
.status_cb = cast_set_status_cb,
|
.status_cb = cast_set_status_cb,
|
||||||
/* .metadata_prepare = cast_metadata_prepare,
|
/* .metadata_prepare = cast_metadata_prepare,
|
||||||
.metadata_send = cast_metadata_send,
|
.metadata_send = cast_metadata_send,
|
||||||
|
Loading…
Reference in New Issue
Block a user