From 5e239cfbb2b5cb205bdf0b633da77c435d2f50cc Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Sat, 30 Jan 2016 00:36:05 +0100 Subject: [PATCH] [chromecast] Volume support and fix double free --- src/outputs/cast.c | 87 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 76 insertions(+), 11 deletions(-) diff --git a/src/outputs/cast.c b/src/outputs/cast.c index 7a7ed611..090450b9 100644 --- a/src/outputs/cast.c +++ b/src/outputs/cast.c @@ -89,7 +89,7 @@ struct cast_session char *address; unsigned short port; - int volume; + float volume; // 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 @@ -125,6 +125,7 @@ enum cast_msg_types MEDIA_LOAD, MEDIA_STOP, MEDIA_LOAD_FAILED, + SET_VOLUME, }; struct cast_msg_basic @@ -225,6 +226,12 @@ struct cast_msg_basic cast_msg[] = .type = MEDIA_LOAD_FAILED, .tag = "LOAD_FAILED", }, + { + .type = SET_VOLUME, + .namespace = NS_RECEIVER, + .payload = "{'type':'SET_VOLUME','volume':{'level':%.2f,'muted':0},'requestId':%d}", + .flags = USE_REQUEST_ID, + }, { .type = 0, }, @@ -392,13 +399,15 @@ cast_msg_send(struct cast_session *cs, enum cast_msg_types type, cast_reply_cb r cs->callback_register[cs->request_id % CALLBACK_REGISTER_SIZE] = reply_cb; } - // Special handling of MEDIA_LOAD and MEDIA_STOP for now + // Special handling of some message types if (cast_msg[type].flags & USE_REQUEST_ID_ONLY) snprintf(msg_buf, sizeof(msg_buf), cast_msg[type].payload, cs->request_id); else if (type == MEDIA_LOAD) snprintf(msg_buf, sizeof(msg_buf), cast_msg[type].payload, TEST_STREAM_URL, cs->session_id, cs->request_id); else if (type == MEDIA_STOP) snprintf(msg_buf, sizeof(msg_buf), cast_msg[type].payload, cs->session_id, cs->request_id); + else if (type == SET_VOLUME) + snprintf(msg_buf, sizeof(msg_buf), cast_msg[type].payload, cs->volume, cs->request_id); else snprintf(msg_buf, sizeof(msg_buf), "%s", cast_msg[type].payload); @@ -623,7 +632,7 @@ cast_cb_startup_volume(struct cast_session *cs, struct cast_msg_payload *payload static void cast_cb_startup_media(struct cast_session *cs, struct cast_msg_payload *payload) { - cs->state = OUTPUT_STATE_RECORD; + int ret; if (payload->type != MEDIA_STATUS) { @@ -634,7 +643,12 @@ cast_cb_startup_media(struct cast_session *cs, struct cast_msg_payload *payload) } // TODO Send a volume message with the cb - cast_cb_startup_volume(cs, payload); + ret = cast_msg_send(cs, SET_VOLUME, cast_cb_startup_volume); + if (ret < 0) + { + cast_msg_send(cs, CLOSE, NULL); + cast_session_failure(cs); + } } static void @@ -654,7 +668,6 @@ cast_cb_startup_launch(struct cast_session *cs, struct cast_msg_payload *payload DPRINTF(E_LOG, L_CAST, "Ooops, memleaking...\n"); cs->transport_id = strdup(payload->transport_id); - cs->state = OUTPUT_STATE_ANNOUNCE; ret = cast_msg_send(cs, MEDIA_CONNECT, NULL); if (ret == 0) @@ -684,7 +697,6 @@ cast_cb_startup_connect(struct cast_session *cs, struct cast_msg_payload *payloa DPRINTF(E_LOG, L_CAST, "Ooops, memleaking...\n"); cs->session_id = strdup(payload->session_id); - cs->state = OUTPUT_STATE_OPTIONS; ret = cast_msg_send(cs, LAUNCH, cast_cb_startup_launch); if (ret < 0) @@ -717,6 +729,18 @@ cast_cb_load(struct cast_session *cs, struct cast_msg_payload *payload) cs->status_cb(cs->device, cs->output_session, cs->state); } +static void +cast_cb_volume(struct cast_session *cs, struct cast_msg_payload *payload) +{ + output_status_cb status_cb; + + status_cb = cs->status_cb; + cs->status_cb = NULL; + status_cb(cs->device, cs->output_session, cs->state); +} + + + static void cast_listen_cb(int fd, short what, void *arg) { @@ -724,13 +748,15 @@ cast_listen_cb(int fd, short what, void *arg) uint8_t buffer[MAX_BUF + 1]; // Not sure about the +1, but is copied from gnutls examples uint32_t be; size_t len; + int processed; int ret; cs = (struct cast_session *)arg; DPRINTF(E_DBG, L_CAST, "New data from %s\n", cs->devname); - while ((ret = gnutls_record_recv(cs->tls_session, buffer, MAX_BUF)) > 0) + processed = 0; + while ((ret = gnutls_record_recv(cs->tls_session, buffer + processed, MAX_BUF - processed)) > 0) { DPRINTF(E_DBG, L_CAST, "Received %d bytes\n", ret); @@ -742,7 +768,14 @@ cast_listen_cb(int fd, short what, void *arg) } else { - cast_msg_process(cs, buffer, ret); + processed += ret; + } + + if (processed >= MAX_BUF) + { + DPRINTF(E_LOG, L_CAST, "Receive buffer exhausted!\n"); + cast_session_failure(cs); + return; } } @@ -750,7 +783,11 @@ cast_listen_cb(int fd, short what, void *arg) { DPRINTF(E_LOG, L_CAST, "Session error: %s\n", gnutls_strerror(ret)); cast_session_failure(cs); + return; } + + if (processed) + cast_msg_process(cs, buffer, processed); } static struct cast_session * @@ -841,7 +878,7 @@ cast_session_make(struct output_device *device, int family, output_status_cb cb) cs->devname = strdup(device->name); cs->address = strdup(address); - cs->volume = device->volume; + cs->volume = 0.01 * device->volume; cs->next = sessions; sessions = cs; @@ -996,6 +1033,34 @@ cast_device_stop(struct output_session *session) cast_msg_send(cs, CLOSE, cast_cb_close); } +static int +cast_volume_set(struct output_device *device, output_status_cb cb) +{ + struct cast_session *cs; + int ret; + + if (!device->session || !device->session->session) + return 0; + + cs = device->session->session; + + if (!(cs->state & OUTPUT_STATE_F_CONNECTED)) + return 0; + + cs->volume = 0.01 * device->volume; + + ret = cast_msg_send(cs, SET_VOLUME, cast_cb_volume); + if (ret < 0) + { + cast_session_failure(cs); + return 0; + } + + cs->status_cb = cb; + + return 1; +} + static void cast_playback_start(uint64_t next_pkt, struct timespec *ts) { @@ -1087,8 +1152,8 @@ struct output_definition output_cast = .device_start = cast_device_start, .device_stop = cast_device_stop, /* .device_probe = cast_device_probe, - .device_free_extra = cast_device_free_extra, - .device_volume_set = cast_set_volume_one,*/ + .device_free_extra = cast_device_free_extra,*/ + .device_volume_set = cast_volume_set, .playback_start = cast_playback_start, /* .playback_stop = cast_playback_stop, .write = cast_write,