[chromecast] Try to improve session handling + actually remove device on mdns cb
This commit is contained in:
parent
e4d209f8a0
commit
483a428bae
|
@ -83,9 +83,11 @@ typedef void (*cast_reply_cb)(struct cast_session *cs, struct cast_msg_payload *
|
|||
// Session is starting up
|
||||
#define CAST_STATE_F_STARTUP (1 << 14)
|
||||
// The default receiver app is ready
|
||||
#define CAST_STATE_F_MEDIA_CONNECTED (1 << 15)
|
||||
#define CAST_STATE_F_MEDIA_CONNECTED (1 << 14)
|
||||
// Media is loaded in the receiver app
|
||||
#define CAST_STATE_F_MEDIA_LOADED (1 << 16)
|
||||
#define CAST_STATE_F_MEDIA_LOADED (1 << 15)
|
||||
// Media is playing in the receiver app
|
||||
#define CAST_STATE_F_MEDIA_PLAYING (1 << 16)
|
||||
|
||||
// Beware, the order of this enum has meaning
|
||||
enum cast_state
|
||||
|
@ -104,14 +106,12 @@ enum cast_state
|
|||
CAST_STATE_MEDIA_CONNECTED = CAST_STATE_F_MEDIA_CONNECTED,
|
||||
// Receiver app has loaded our media
|
||||
CAST_STATE_MEDIA_LOADED = CAST_STATE_F_MEDIA_CONNECTED | CAST_STATE_F_MEDIA_LOADED,
|
||||
// After LOAD
|
||||
CAST_STATE_MEDIA_BUFFERING = CAST_STATE_F_MEDIA_CONNECTED | CAST_STATE_F_MEDIA_LOADED | 0x01,
|
||||
// After PLAY
|
||||
CAST_STATE_MEDIA_PLAYING = CAST_STATE_F_MEDIA_CONNECTED | CAST_STATE_F_MEDIA_LOADED | 0x02,
|
||||
// After PAUSE
|
||||
CAST_STATE_MEDIA_PAUSED = CAST_STATE_F_MEDIA_CONNECTED | CAST_STATE_F_MEDIA_LOADED | 0x03,
|
||||
// After STOP
|
||||
CAST_STATE_MEDIA_IDLE = CAST_STATE_F_MEDIA_CONNECTED | CAST_STATE_F_MEDIA_LOADED | 0x04,
|
||||
CAST_STATE_MEDIA_PAUSED = CAST_STATE_F_MEDIA_CONNECTED | CAST_STATE_F_MEDIA_LOADED | 0x01,
|
||||
// After LOAD
|
||||
CAST_STATE_MEDIA_BUFFERING = CAST_STATE_F_MEDIA_CONNECTED | CAST_STATE_F_MEDIA_LOADED | CAST_STATE_F_MEDIA_PLAYING,
|
||||
// After PLAY
|
||||
CAST_STATE_MEDIA_PLAYING = CAST_STATE_F_MEDIA_CONNECTED | CAST_STATE_F_MEDIA_LOADED | CAST_STATE_F_MEDIA_PLAYING | 0x01,
|
||||
};
|
||||
|
||||
struct cast_session
|
||||
|
@ -197,6 +197,7 @@ struct cast_msg_payload
|
|||
const char *app_id;
|
||||
const char *session_id;
|
||||
const char *transport_id;
|
||||
const char *player_state;
|
||||
int media_session_id;
|
||||
};
|
||||
|
||||
|
@ -326,7 +327,7 @@ extern struct event_base *evbase_player;
|
|||
static gnutls_certificate_credentials_t tls_credentials;
|
||||
static struct cast_session *sessions;
|
||||
static struct event *flush_timer;
|
||||
|
||||
static struct timeval cast_timeout = { 8, 0 };
|
||||
|
||||
/* ------------------------------- MISC HELPERS ----------------------------- */
|
||||
|
||||
|
@ -533,7 +534,7 @@ cast_msg_send(struct cast_session *cs, enum cast_msg_types type, cast_reply_cb r
|
|||
int ret;
|
||||
|
||||
#ifdef DEBUG_LOG_MODE
|
||||
DPRINTF(E_DBG, L_CAST, "Preparing to send message type %d to %s\n", type, cs->devname);
|
||||
DPRINTF(E_DBG, L_CAST, "Preparing to send message type %d to '%s'\n", type, cs->devname);
|
||||
#endif
|
||||
|
||||
msg.source_id = "sender-0";
|
||||
|
@ -646,28 +647,35 @@ cast_msg_parse(struct cast_msg_payload *payload, char *s)
|
|||
// 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);
|
||||
(somehay = json_object_array_get_idx(needle, 0)) )
|
||||
{
|
||||
if ( 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) &&
|
||||
json_object_object_get_ex(somehay, "applications", &needle) &&
|
||||
(json_object_get_type(needle) == json_type_array) &&
|
||||
(somehay = json_object_array_get_idx(needle, 0))) )
|
||||
return haystack;
|
||||
if ( json_object_object_get_ex(somehay, "playerState", &needle) &&
|
||||
(json_object_get_type(needle) == json_type_string) )
|
||||
payload->player_state = json_object_get_string(needle);
|
||||
}
|
||||
|
||||
if ( json_object_object_get_ex(somehay, "appId", &needle) &&
|
||||
(json_object_get_type(needle) == json_type_string) )
|
||||
payload->app_id = json_object_get_string(needle);
|
||||
|
||||
if ( json_object_object_get_ex(somehay, "sessionId", &needle) &&
|
||||
(json_object_get_type(needle) == json_type_string) )
|
||||
payload->session_id = json_object_get_string(needle);
|
||||
if ( json_object_object_get_ex(haystack, "status", &somehay) &&
|
||||
json_object_object_get_ex(somehay, "applications", &needle) &&
|
||||
(json_object_get_type(needle) == json_type_array) &&
|
||||
(somehay = json_object_array_get_idx(needle, 0)) )
|
||||
{
|
||||
if ( json_object_object_get_ex(somehay, "appId", &needle) &&
|
||||
(json_object_get_type(needle) == json_type_string) )
|
||||
payload->app_id = json_object_get_string(needle);
|
||||
|
||||
if ( json_object_object_get_ex(somehay, "transportId", &needle) &&
|
||||
(json_object_get_type(needle) == json_type_string) )
|
||||
payload->transport_id = json_object_get_string(needle);
|
||||
if ( json_object_object_get_ex(somehay, "sessionId", &needle) &&
|
||||
(json_object_get_type(needle) == json_type_string) )
|
||||
payload->session_id = json_object_get_string(needle);
|
||||
|
||||
if ( json_object_object_get_ex(somehay, "transportId", &needle) &&
|
||||
(json_object_get_type(needle) == json_type_string) )
|
||||
payload->transport_id = json_object_get_string(needle);
|
||||
}
|
||||
|
||||
return haystack;
|
||||
}
|
||||
|
@ -714,8 +722,6 @@ cast_msg_process(struct cast_session *cs, const uint8_t *data, size_t len)
|
|||
if (payload.type == UNKNOWN)
|
||||
goto out_free_parsed;
|
||||
|
||||
// TODO: UPDATE SESSION STATUS AND READ BROADCASTS
|
||||
|
||||
i = payload.request_id % CALLBACK_REGISTER_SIZE;
|
||||
if (payload.request_id && cs->callback_register[i])
|
||||
{
|
||||
|
@ -724,22 +730,36 @@ cast_msg_process(struct cast_session *cs, const uint8_t *data, size_t len)
|
|||
goto out_free_parsed;
|
||||
}
|
||||
|
||||
// TODO: UPDATE SESSION STATUS AND READ BROADCASTS
|
||||
|
||||
if (payload.type == RECEIVER_STATUS && (cs->state & CAST_STATE_F_MEDIA_CONNECTED))
|
||||
{
|
||||
unknown_app_id = payload.app_id && (strcmp(payload.app_id, CAST_APP_ID) != 0);
|
||||
unknown_session_id = payload.session_id && (strcmp(payload.session_id, cs->session_id) != 0);
|
||||
if (unknown_app_id || unknown_session_id)
|
||||
{
|
||||
DPRINTF(E_WARN, L_CAST, "Our session on %s was hijacked\n", cs->devname);
|
||||
DPRINTF(E_WARN, L_CAST, "Our session on '%s' was hijacked\n", cs->devname);
|
||||
|
||||
// Downgrade state, we don't have the receiver app any more
|
||||
cs->state = CAST_STATE_CONNECTED;
|
||||
cast_session_shutdown(cs, CAST_STATE_FAILED);
|
||||
DPRINTF(E_WARN, L_CAST, "POINT 1\n");
|
||||
goto out_free_parsed;
|
||||
}
|
||||
}
|
||||
|
||||
if (payload.type == MEDIA_STATUS && (cs->state & CAST_STATE_F_MEDIA_PLAYING))
|
||||
{
|
||||
if (payload.player_state && (strcmp(payload.player_state, "PAUSED") == 0))
|
||||
{
|
||||
DPRINTF(E_WARN, L_CAST, "Something paused our session on '%s'\n", cs->devname);
|
||||
|
||||
/* cs->state = CAST_STATE_MEDIA_CONNECTED;
|
||||
// Kill the session, the player will need to restart it
|
||||
cast_session_shutdown(cs, CAST_STATE_NULL);
|
||||
goto out_free_parsed;
|
||||
*/ }
|
||||
}
|
||||
|
||||
out_free_parsed:
|
||||
cast_msg_parse_free(hdl);
|
||||
out_free_unpacked:
|
||||
|
@ -772,7 +792,10 @@ cast_status(struct cast_session *cs)
|
|||
case CAST_STATE_MEDIA_CONNECTED:
|
||||
state = OUTPUT_STATE_CONNECTED;
|
||||
break;
|
||||
case CAST_STATE_MEDIA_LOADED ... CAST_STATE_MEDIA_IDLE:
|
||||
case CAST_STATE_MEDIA_LOADED ... CAST_STATE_MEDIA_PAUSED:
|
||||
state = OUTPUT_STATE_CONNECTED;
|
||||
break;
|
||||
case CAST_STATE_MEDIA_BUFFERING ... CAST_STATE_MEDIA_PLAYING:
|
||||
state = OUTPUT_STATE_STREAMING;
|
||||
break;
|
||||
default:
|
||||
|
@ -934,7 +957,7 @@ cast_cb_load(struct cast_session *cs, struct cast_msg_payload *payload)
|
|||
{
|
||||
if ((payload->type == MEDIA_LOAD_FAILED) || (payload->type == MEDIA_LOAD_CANCELLED))
|
||||
{
|
||||
DPRINTF(E_LOG, L_CAST, "The device %s could not start playback\n", cs->devname);
|
||||
DPRINTF(E_LOG, L_CAST, "The device '%s' could not start playback\n", cs->devname);
|
||||
cast_session_shutdown(cs, CAST_STATE_FAILED);
|
||||
return;
|
||||
}
|
||||
|
@ -947,7 +970,9 @@ cast_cb_load(struct cast_session *cs, struct cast_msg_payload *payload)
|
|||
}
|
||||
|
||||
cs->media_session_id = payload->media_session_id;
|
||||
cs->state = CAST_STATE_MEDIA_LOADED;
|
||||
// TODO don't autoplay
|
||||
// cs->state = CAST_STATE_MEDIA_LOADED;
|
||||
cs->state = CAST_STATE_MEDIA_PLAYING;
|
||||
|
||||
cast_status(cs);
|
||||
}
|
||||
|
@ -963,7 +988,7 @@ cast_cb_flush(struct cast_session *cs, struct cast_msg_payload *payload)
|
|||
{
|
||||
if (payload->type != MEDIA_STATUS)
|
||||
{
|
||||
DPRINTF(E_LOG, L_CAST, "Unexpected reply to PAUSE request from %s - will continue\n", cs->devname);
|
||||
DPRINTF(E_LOG, L_CAST, "Unexpected reply to PAUSE request from '%s' - will continue\n", cs->devname);
|
||||
}
|
||||
|
||||
cs->state = CAST_STATE_MEDIA_PAUSED;
|
||||
|
@ -986,8 +1011,16 @@ cast_listen_cb(int fd, short what, void *arg)
|
|||
|
||||
cs = (struct cast_session *)arg;
|
||||
|
||||
if (what == EV_TIMEOUT)
|
||||
{
|
||||
DPRINTF(E_LOG, L_CAST, "No heartbeat from '%s', shutting down\n", cs->devname);
|
||||
cs->state = CAST_STATE_CONNECTED;
|
||||
cast_session_shutdown(cs, CAST_STATE_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LOG_MODE
|
||||
DPRINTF(E_DBG, L_CAST, "New data from %s\n", cs->devname);
|
||||
DPRINTF(E_DBG, L_CAST, "New data from '%s'\n", cs->devname);
|
||||
#endif
|
||||
|
||||
received = 0;
|
||||
|
@ -1039,29 +1072,21 @@ static void
|
|||
cast_device_cb(const char *name, const char *type, const char *domain, const char *hostname, int family, const char *address, int port, struct keyval *txt)
|
||||
{
|
||||
struct output_device *device;
|
||||
const char *p;
|
||||
uint32_t id;
|
||||
|
||||
p = keyval_get(txt, "id");
|
||||
if (p)
|
||||
id = djb_hash(p, strlen(p));
|
||||
else
|
||||
id = 0;
|
||||
|
||||
id = djb_hash(name, strlen(name));
|
||||
if (!id)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Could not extract ChromeCast device ID (%s)\n", name);
|
||||
|
||||
DPRINTF(E_LOG, L_CAST, "Could not hash ChromeCast device name (%s)\n", name);
|
||||
return;
|
||||
}
|
||||
|
||||
DPRINTF(E_DBG, L_PLAYER, "Event for Chromecast device %s (port %d, id %" PRIu32 ")\n", name, port, id);
|
||||
DPRINTF(E_DBG, L_CAST, "Event for Chromecast device '%s' (port %d, id %" PRIu32 ")\n", name, port, id);
|
||||
|
||||
device = calloc(1, sizeof(struct output_device));
|
||||
if (!device)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Out of memory for new Chromecast device\n");
|
||||
|
||||
DPRINTF(E_LOG, L_CAST, "Out of memory for new Chromecast device\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1089,7 +1114,7 @@ cast_device_cb(const char *name, const char *type, const char *domain, const cha
|
|||
return;
|
||||
}
|
||||
|
||||
DPRINTF(E_INFO, L_PLAYER, "Adding Chromecast device %s\n", name);
|
||||
DPRINTF(E_INFO, L_CAST, "Adding Chromecast device '%s'\n", name);
|
||||
|
||||
device->advertised = 1;
|
||||
|
||||
|
@ -1207,7 +1232,7 @@ cast_session_make(struct output_device *device, int family, output_status_cb cb)
|
|||
flags = fcntl(cs->server_fd, F_GETFL, 0);
|
||||
fcntl(cs->server_fd, F_SETFL, flags | O_NONBLOCK);
|
||||
|
||||
event_add(cs->ev, NULL);
|
||||
event_add(cs->ev, &cast_timeout);
|
||||
|
||||
cs->devname = strdup(device->name);
|
||||
cs->address = strdup(address);
|
||||
|
@ -1219,7 +1244,7 @@ cast_session_make(struct output_device *device, int family, output_status_cb cb)
|
|||
|
||||
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);
|
||||
DPRINTF(E_INFO, L_CAST, "Connection to '%s' established using %s\n", cs->devname, proto);
|
||||
|
||||
return cs;
|
||||
|
||||
|
@ -1243,9 +1268,14 @@ cast_session_shutdown(struct cast_session *cs, enum cast_state wanted_state)
|
|||
int pending;
|
||||
int ret;
|
||||
|
||||
if (wanted_state >= cs->state)
|
||||
if (cs->state == wanted_state)
|
||||
{
|
||||
DPRINTF(E_LOG, L_CAST, "Bug! Shutdown request wanted_state should be lower than current state\n");
|
||||
cast_status(cs);
|
||||
return;
|
||||
}
|
||||
else if (cs->state < wanted_state)
|
||||
{
|
||||
DPRINTF(E_LOG, L_CAST, "Bug! Shutdown request got wanted_state that is higher than current state\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1254,7 +1284,7 @@ cast_session_shutdown(struct cast_session *cs, enum cast_state wanted_state)
|
|||
pending = 0;
|
||||
switch (cs->state)
|
||||
{
|
||||
case CAST_STATE_MEDIA_LOADED ... CAST_STATE_MEDIA_IDLE:
|
||||
case CAST_STATE_MEDIA_LOADED ... CAST_STATE_MEDIA_PLAYING:
|
||||
ret = cast_msg_send(cs, MEDIA_STOP, cast_cb_stop_media);
|
||||
pending = 1;
|
||||
break;
|
||||
|
@ -1483,7 +1513,7 @@ cast_flush(output_status_cb cb, uint64_t rtptime)
|
|||
{
|
||||
next = cs->next;
|
||||
|
||||
if (!(cs->state & CAST_STATE_F_MEDIA_LOADED))
|
||||
if (!(cs->state & CAST_STATE_F_MEDIA_PLAYING))
|
||||
continue;
|
||||
|
||||
ret = cast_msg_send(cs, MEDIA_PAUSE, cast_cb_flush);
|
||||
|
@ -1553,7 +1583,7 @@ cast_init(void)
|
|||
ret = mdns_browse("_googlecast._tcp", mdns_flags, cast_device_cb);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Could not add mDNS browser for Chromecast devices\n");
|
||||
DPRINTF(E_LOG, L_CAST, "Could not add mDNS browser for Chromecast devices\n");
|
||||
goto out_free_flush_timer;
|
||||
}
|
||||
|
||||
|
|
36
src/player.c
36
src/player.c
|
@ -1885,20 +1885,21 @@ device_streaming_cb(struct output_device *device, struct output_session *session
|
|||
|
||||
DPRINTF(E_DBG, L_PLAYER, "CALLBACK streaming_cb %d\n", status);
|
||||
|
||||
ret = device_check(device);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Output device disappeared during streaming!\n");
|
||||
|
||||
output_sessions--;
|
||||
return;
|
||||
}
|
||||
|
||||
if (status == OUTPUT_STATE_FAILED)
|
||||
{
|
||||
output_sessions--;
|
||||
|
||||
ret = device_check(device);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_WARN, L_PLAYER, "Output device disappeared during streaming!\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
DPRINTF(E_LOG, L_PLAYER, "The %s device '%s' FAILED\n", device->type_name, device->name);
|
||||
|
||||
output_sessions--;
|
||||
|
||||
if (player_state == PLAY_PLAYING)
|
||||
speaker_deselect_output(device);
|
||||
|
||||
|
@ -1906,21 +1907,16 @@ device_streaming_cb(struct output_device *device, struct output_session *session
|
|||
|
||||
if (!device->advertised)
|
||||
device_remove(device);
|
||||
|
||||
if (output_sessions == 0)
|
||||
playback_abort();
|
||||
}
|
||||
else if (status == OUTPUT_STATE_STOPPED)
|
||||
{
|
||||
output_sessions--;
|
||||
|
||||
ret = device_check(device);
|
||||
if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_WARN, L_PLAYER, "Output device disappeared during streaming!\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
DPRINTF(E_INFO, L_PLAYER, "The %s device '%s' stopped\n", device->type_name, device->name);
|
||||
|
||||
output_sessions--;
|
||||
|
||||
device->session = NULL;
|
||||
|
||||
if (!device->advertised)
|
||||
|
|
Loading…
Reference in New Issue