|
@@ -43,7 +43,8 @@
|
|
|
#include "dmap_parser.h"
|
|
|
#include "log_util.h"
|
|
|
|
|
|
-#define RTSP_STACK_SIZE (8*1024)
|
|
|
+#define RTSP_STACK_SIZE (8*1024)
|
|
|
+#define SEARCH_STACK_SIZE (2*1048)
|
|
|
|
|
|
typedef struct raop_ctx_s {
|
|
|
#ifdef WIN32
|
|
@@ -60,7 +61,7 @@ typedef struct raop_ctx_s {
|
|
|
#else
|
|
|
TaskHandle_t thread, search_thread, joiner;
|
|
|
StaticTask_t *xTaskBuffer;
|
|
|
- StackType_t *xStack;
|
|
|
+ StackType_t xStack[RTSP_STACK_SIZE] __attribute__ ((aligned (4)));
|
|
|
#endif
|
|
|
unsigned char mac[6];
|
|
|
int latency;
|
|
@@ -71,14 +72,19 @@ typedef struct raop_ctx_s {
|
|
|
struct rtp_s *rtp;
|
|
|
raop_cmd_cb_t cmd_cb;
|
|
|
raop_data_cb_t data_cb;
|
|
|
- /*
|
|
|
struct {
|
|
|
char DACPid[32], id[32];
|
|
|
struct in_addr host;
|
|
|
u16_t port;
|
|
|
+#ifdef WIN32
|
|
|
struct mDNShandle_s *handle;
|
|
|
+#else
|
|
|
+ bool running;
|
|
|
+ TaskHandle_t thread, joiner;
|
|
|
+ StaticTask_t *xTaskBuffer;
|
|
|
+ StackType_t xStack[SEARCH_STACK_SIZE] __attribute__ ((aligned (4)));;
|
|
|
+#endif
|
|
|
} active_remote;
|
|
|
- */
|
|
|
void *owner;
|
|
|
} raop_ctx_t;
|
|
|
|
|
@@ -98,7 +104,7 @@ static void* search_remote(void *args);
|
|
|
extern char private_key[];
|
|
|
enum { RSA_MODE_KEY, RSA_MODE_AUTH };
|
|
|
|
|
|
-
static void on_dmap_string(void *ctx, const char *code, const char *name, const char *buf, size_t len);
|
|
|
+static void on_dmap_string(void *ctx, const char *code, const char *name, const char *buf, size_t len);
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
struct raop_ctx_s *raop_create(struct in_addr host, char *name,
|
|
@@ -186,26 +192,23 @@ struct raop_ctx_s *raop_create(struct in_addr host, char *name,
|
|
|
ESP_ERROR_CHECK( mdns_service_add(id, "_raop", "_tcp", ctx->port, txt, sizeof(txt) / sizeof(mdns_txt_item_t)) );
|
|
|
|
|
|
ctx->xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
|
|
- ctx->xStack = (StackType_t*) malloc(RTSP_STACK_SIZE);
|
|
|
ctx->thread = xTaskCreateStatic( (TaskFunction_t) rtsp_thread, "RTSP_thread", RTSP_STACK_SIZE, ctx, ESP_TASK_PRIO_MIN + 1, ctx->xStack, ctx->xTaskBuffer);
|
|
|
#endif
|
|
|
|
|
|
return ctx;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
void raop_delete(struct raop_ctx_s *ctx) {
|
|
|
+#ifdef WIN32
|
|
|
int sock;
|
|
|
struct sockaddr addr;
|
|
|
socklen_t nlen = sizeof(struct sockaddr);
|
|
|
-
|
|
|
- if (!ctx) return;
|
|
|
-
|
|
|
-#if !defined WIN32
|
|
|
- ctx->joiner = xTaskGetCurrentTaskHandle();
|
|
|
#endif
|
|
|
+
|
|
|
+if (!ctx) return;
|
|
|
|
|
|
+#ifdef WIN32
|
|
|
ctx->running = false;
|
|
|
|
|
|
// wake-up thread by connecting socket, needed for freeBSD
|
|
@@ -214,66 +217,99 @@ void raop_delete(struct raop_ctx_s *ctx) {
|
|
|
connect(sock, (struct sockaddr*) &addr, sizeof(addr));
|
|
|
closesocket(sock);
|
|
|
|
|
|
-#ifdef WIN32
|
|
|
pthread_join(ctx->thread, NULL);
|
|
|
-#else
|
|
|
- xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);
|
|
|
- vTaskDelete(ctx->thread);
|
|
|
- free(ctx->xStack);
|
|
|
- heap_caps_free(ctx->xTaskBuffer);
|
|
|
-#endif
|
|
|
|
|
|
rtp_end(ctx->rtp);
|
|
|
|
|
|
-#ifdef WIN32
|
|
|
shutdown(ctx->sock, SD_BOTH);
|
|
|
-#else
|
|
|
- shutdown(ctx->sock, SHUT_RDWR);
|
|
|
-#endif
|
|
|
closesocket(ctx->sock);
|
|
|
|
|
|
- /*
|
|
|
// terminate search, but do not reclaim memory of pthread if never launched
|
|
|
if (ctx->active_remote.handle) {
|
|
|
close_mDNS(ctx->active_remote.handle);
|
|
|
pthread_join(ctx->search_thread, NULL);
|
|
|
}
|
|
|
- */
|
|
|
|
|
|
- NFREE(ctx->rtsp.aeskey);
|
|
|
- NFREE(ctx->rtsp.aesiv);
|
|
|
- NFREE(ctx->rtsp.fmtp);
|
|
|
-
|
|
|
// stop broadcasting devices
|
|
|
-#ifdef WIN32
|
|
|
mdns_service_remove(ctx->svr, ctx->svc);
|
|
|
mdnsd_stop(ctx->svr);
|
|
|
-#else
|
|
|
+#else
|
|
|
+ // first stop the search task if any
|
|
|
+ if (ctx->active_remote.running) {
|
|
|
+ ctx->active_remote.joiner = xTaskGetCurrentTaskHandle();
|
|
|
+ ctx->active_remote.running = false;
|
|
|
+
|
|
|
+ vTaskResume(ctx->active_remote.thread);
|
|
|
+ ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
|
|
|
+ vTaskDelete(ctx->active_remote.thread);
|
|
|
+
|
|
|
+ heap_caps_free(ctx->active_remote.xTaskBuffer);
|
|
|
+ }
|
|
|
+
|
|
|
+ // then the RTSP task
|
|
|
+ ctx->joiner = xTaskGetCurrentTaskHandle();
|
|
|
+ ctx->running = false;
|
|
|
+
|
|
|
+ ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
|
|
|
+ vTaskDelete(ctx->thread);
|
|
|
+ heap_caps_free(ctx->xTaskBuffer);
|
|
|
+
|
|
|
+ rtp_end(ctx->rtp);
|
|
|
+
|
|
|
+ shutdown(ctx->sock, SHUT_RDWR);
|
|
|
+ closesocket(ctx->sock);
|
|
|
+
|
|
|
mdns_service_remove("_raop", "_tcp");
|
|
|
#endif
|
|
|
|
|
|
+ NFREE(ctx->rtsp.aeskey);
|
|
|
+ NFREE(ctx->rtsp.aesiv);
|
|
|
+ NFREE(ctx->rtsp.fmtp);
|
|
|
+
|
|
|
free(ctx);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
-void raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param) {
|
|
|
-/*
|
|
|
+void raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param) {
|
|
|
struct sockaddr_in addr;
|
|
|
int sock;
|
|
|
char *command = NULL;
|
|
|
|
|
|
// first notify the remote controller (if any)
|
|
|
switch(event) {
|
|
|
+ case RAOP_REW:
|
|
|
+ command = strdup("beginrew");
|
|
|
+ break;
|
|
|
+ case RAOP_FWD:
|
|
|
+ command = strdup("beginff");
|
|
|
+ break;
|
|
|
+ case RAOP_PREV:
|
|
|
+ command = strdup("previtem");
|
|
|
+ break;
|
|
|
+ case RAOP_NEXT:
|
|
|
+ command = strdup("nextitem");
|
|
|
+ break;
|
|
|
+ case RAOP_TOGGLE:
|
|
|
+ command = strdup("playpause");
|
|
|
+ break;
|
|
|
case RAOP_PAUSE:
|
|
|
command = strdup("pause");
|
|
|
break;
|
|
|
case RAOP_PLAY:
|
|
|
command = strdup("play");
|
|
|
break;
|
|
|
+ case RAOP_RESUME:
|
|
|
+ command = strdup("playresume");
|
|
|
+ break;
|
|
|
case RAOP_STOP:
|
|
|
command = strdup("stop");
|
|
|
break;
|
|
|
+ case RAOP_VOLUME_UP:
|
|
|
+ command = strdup("volumeup");
|
|
|
+ break;
|
|
|
+ case RAOP_VOLUME_DOWN:
|
|
|
+ command = strdup("volumedown");
|
|
|
+ break;
|
|
|
case RAOP_VOLUME: {
|
|
|
float Volume = *((float*) param);
|
|
|
Volume = Volume ? (Volume - 1) * 30 : -144;
|
|
@@ -286,9 +322,9 @@ void raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param) {
|
|
|
|
|
|
// no command to send to remote or no remote found yet
|
|
|
if (!command || !ctx->active_remote.port) {
|
|
|
-
NFREE(command);
|
|
|
-
return;
|
|
|
-
}
|
|
|
+ NFREE(command);
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
|
@@ -317,11 +353,7 @@ void raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param) {
|
|
|
}
|
|
|
|
|
|
free(command);
|
|
|
-
|
|
|
closesocket(sock);
|
|
|
-*/
|
|
|
- // then notify local system
|
|
|
- ctx->cmd_cb(event, param);
|
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
@@ -366,7 +398,7 @@ static void *rtsp_thread(void *arg) {
|
|
|
if (sock != -1) closesocket(sock);
|
|
|
|
|
|
#ifndef WIN32
|
|
|
- xTaskNotify(ctx->joiner, 0, eNoAction);
|
|
|
+ xTaskNotifyGive(ctx->joiner);
|
|
|
vTaskSuspend(NULL);
|
|
|
#endif
|
|
|
|
|
@@ -429,12 +461,27 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
|
|
|
NFREE(ctx->rtsp.aesiv);
|
|
|
NFREE(ctx->rtsp.fmtp);
|
|
|
|
|
|
- // LMS might has taken over the player, leaving us with a running RTP session
|
|
|
+ // LMS might has taken over the player, leaving us with a running RTP session (should not happen)
|
|
|
if (ctx->rtp) {
|
|
|
- LOG_INFO("[%p]: closing unfinished RTP session", ctx);
|
|
|
+ LOG_WARN("[%p]: closing unfinished RTP session", ctx);
|
|
|
rtp_end(ctx->rtp);
|
|
|
}
|
|
|
|
|
|
+ // same, should not happen unless we have missed a teardown ...
|
|
|
+ if (ctx->active_remote.running) {
|
|
|
+ ctx->active_remote.joiner = xTaskGetCurrentTaskHandle();
|
|
|
+ ctx->active_remote.running = false;
|
|
|
+
|
|
|
+ vTaskResume(ctx->active_remote.thread);
|
|
|
+ ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
|
|
|
+ vTaskDelete(ctx->active_remote.thread);
|
|
|
+
|
|
|
+ heap_caps_free(ctx->active_remote.xTaskBuffer);
|
|
|
+ memset(&ctx->active_remote, 0, sizeof(ctx->active_remote));
|
|
|
+
|
|
|
+ LOG_WARN("[%p]: closing unfinished mDNS search", ctx);
|
|
|
+ }
|
|
|
+
|
|
|
if ((p = strcasestr(body, "rsaaeskey")) != NULL) {
|
|
|
unsigned char *aeskey;
|
|
|
int len, outlen;
|
|
@@ -467,13 +514,17 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
|
|
|
}
|
|
|
|
|
|
// on announce, search remote
|
|
|
- /*
|
|
|
if ((buf = kd_lookup(headers, "DACP-ID")) != NULL) strcpy(ctx->active_remote.DACPid, buf);
|
|
|
if ((buf = kd_lookup(headers, "Active-Remote")) != NULL) strcpy(ctx->active_remote.id, buf);
|
|
|
|
|
|
+#ifdef WIN32
|
|
|
ctx->active_remote.handle = init_mDNS(false, ctx->host);
|
|
|
pthread_create(&ctx->search_thread, NULL, &search_remote, ctx);
|
|
|
- */
|
|
|
+#else
|
|
|
+ ctx->active_remote.running = true;
|
|
|
+ ctx->active_remote.xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
|
|
|
+ ctx->active_remote.thread = xTaskCreateStatic( (TaskFunction_t) search_remote, "search_remote", SEARCH_STACK_SIZE, ctx, ESP_TASK_PRIO_MIN + 1, ctx->active_remote.xStack, ctx->active_remote.xTaskBuffer);
|
|
|
+#endif
|
|
|
|
|
|
} else if (!strcmp(method, "SETUP") && ((buf = kd_lookup(headers, "Transport")) != NULL)) {
|
|
|
char *p;
|
|
@@ -481,7 +532,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
|
|
|
short unsigned tport = 0, cport = 0;
|
|
|
|
|
|
// we are about to stream, do something if needed
|
|
|
- ctx->cmd_cb(RAOP_SETUP, NULL);
|
|
|
+ success = ctx->cmd_cb(RAOP_SETUP, NULL);
|
|
|
|
|
|
if ((p = strcasestr(buf, "timing_port")) != NULL) sscanf(p, "%*[^=]=%hu", &tport);
|
|
|
if ((p = strcasestr(buf, "control_port")) != NULL) sscanf(p, "%*[^=]=%hu", &cport);
|
|
@@ -520,7 +571,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
|
|
|
|
|
|
if (ctx->rtp) rtp_record(ctx->rtp, seqno, rtptime);
|
|
|
|
|
|
- ctx->cmd_cb(RAOP_STREAM, NULL);
|
|
|
+ success = ctx->cmd_cb(RAOP_STREAM, NULL);
|
|
|
|
|
|
} else if (!strcmp(method, "FLUSH")) {
|
|
|
unsigned short seqno = 0;
|
|
@@ -533,7 +584,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
|
|
|
|
|
|
// only send FLUSH if useful (discards frames above buffer head and top)
|
|
|
if (ctx->rtp && rtp_flush(ctx->rtp, seqno, rtptime))
|
|
|
- ctx->cmd_cb(RAOP_FLUSH, NULL);
|
|
|
+ success = ctx->cmd_cb(RAOP_FLUSH, NULL);
|
|
|
|
|
|
} else if (!strcmp(method, "TEARDOWN")) {
|
|
|
|
|
@@ -541,20 +592,32 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
|
|
|
|
|
|
ctx->rtp = NULL;
|
|
|
|
|
|
- /*
|
|
|
// need to make sure no search is on-going and reclaim pthread memory
|
|
|
+#ifdef WIN32
|
|
|
if (ctx->active_remote.handle) close_mDNS(ctx->active_remote.handle);
|
|
|
pthread_join(ctx->search_thread, NULL);
|
|
|
- memset(&ctx->active_remote, 0, sizeof(ctx->active_remote));
|
|
|
- */
|
|
|
+#else
|
|
|
+ ctx->active_remote.joiner = xTaskGetCurrentTaskHandle();
|
|
|
+ ctx->active_remote.running = false;
|
|
|
+
|
|
|
+ // task might not need to be resumed anyway
|
|
|
+ vTaskResume(ctx->active_remote.thread);
|
|
|
+ ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
|
|
|
+ vTaskDelete(ctx->active_remote.thread);
|
|
|
|
|
|
+ heap_caps_free(ctx->active_remote.xTaskBuffer);
|
|
|
+
|
|
|
+ LOG_INFO("[%p]: mDNS search task terminated", ctx);
|
|
|
+#endif
|
|
|
+
|
|
|
+ memset(&ctx->active_remote, 0, sizeof(ctx->active_remote));
|
|
|
NFREE(ctx->rtsp.aeskey);
|
|
|
NFREE(ctx->rtsp.aesiv);
|
|
|
NFREE(ctx->rtsp.fmtp);
|
|
|
|
|
|
- ctx->cmd_cb(RAOP_STOP, NULL);
|
|
|
+ success = ctx->cmd_cb(RAOP_STOP, NULL);
|
|
|
|
|
|
- } if (!strcmp(method, "SET_PARAMETER")) {
|
|
|
+ } else if (!strcmp(method, "SET_PARAMETER")) {
|
|
|
char *p;
|
|
|
|
|
|
if (body && (p = strcasestr(body, "volume")) != NULL) {
|
|
@@ -563,7 +626,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
|
|
|
sscanf(p, "%*[^:]:%f", &volume);
|
|
|
LOG_INFO("[%p]: SET PARAMETER volume %f", ctx, volume);
|
|
|
volume = (volume == -144.0) ? 0 : (1 + volume / 30);
|
|
|
- ctx->cmd_cb(RAOP_VOLUME, &volume);
|
|
|
+ success = ctx->cmd_cb(RAOP_VOLUME, &volume);
|
|
|
}
|
|
|
/*
|
|
|
if (body && ((p = kd_lookup(headers, "Content-Type")) != NULL) && !strcasecmp(p, "application/x-dmap-tagged")) {
|
|
@@ -585,7 +648,6 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
|
|
|
}
|
|
|
|
|
|
// don't need to free "buf" because kd_lookup return a pointer, not a strdup
|
|
|
-
|
|
|
kd_add(resp, "Audio-Jack-Status", "connected; type=analog");
|
|
|
kd_add(resp, "CSeq", kd_lookup(headers, "CSeq"));
|
|
|
|
|
@@ -605,7 +667,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
|
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
-/*
|
|
|
+#ifdef WIN32
|
|
|
bool search_remote_cb(mDNSservice_t *slist, void *cookie, bool *stop) {
|
|
|
mDNSservice_t *s;
|
|
|
raop_ctx_t *ctx = (raop_ctx_t*) cookie;
|
|
@@ -625,11 +687,9 @@ bool search_remote_cb(mDNSservice_t *slist, void *cookie, bool *stop) {
|
|
|
// let caller clear list
|
|
|
return false;
|
|
|
}
|
|
|
-*/
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
-/*
|
|
|
static void* search_remote(void *args) {
|
|
|
raop_ctx_t *ctx = (raop_ctx_t*) args;
|
|
|
|
|
@@ -637,8 +697,58 @@ static void* search_remote(void *args) {
|
|
|
|
|
|
return NULL;
|
|
|
}
|
|
|
-
*/
|
|
|
|
|
|
+#else
|
|
|
+
|
|
|
+/*----------------------------------------------------------------------------*/
|
|
|
+static void* search_remote(void *args) {
|
|
|
+ raop_ctx_t *ctx = (raop_ctx_t*) args;
|
|
|
+ bool found = false;
|
|
|
+
|
|
|
+ LOG_INFO("starting remote search");
|
|
|
+
|
|
|
+ while (ctx->active_remote.running && !found) {
|
|
|
+ mdns_result_t *results = NULL;
|
|
|
+ mdns_result_t *r;
|
|
|
+ mdns_ip_addr_t *a;
|
|
|
+
|
|
|
+ if (mdns_query_ptr("_dacp", "_tcp", 3000, 32, &results)) {
|
|
|
+ LOG_ERROR("mDNS active remote query Failed");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (r = results; r && !strcasestr(r->instance_name, ctx->active_remote.DACPid); r = r->next);
|
|
|
+ if (r) {
|
|
|
+ for (a = r->addr; a && a->addr.type != IPADDR_TYPE_V4; a = a->next);
|
|
|
+ if (a) {
|
|
|
+ found = true;
|
|
|
+ ctx->active_remote.host.s_addr = a->addr.u_addr.ip4.addr;
|
|
|
+ ctx->active_remote.port = r->port;
|
|
|
+ LOG_INFO("found remote %s %s:%hu", r->instance_name, inet_ntoa(ctx->active_remote.host), ctx->active_remote.port);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ mdns_query_results_free(results);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ for some reason which is beyond me, if that tasks gives the semaphore
|
|
|
+ before the RTSP tasks waits for it, then freeRTOS crashes in queue
|
|
|
+ management caused by LWIP stack once the RTSP socket is closed. I have
|
|
|
+ no clue why, but so we'll suspend the tasks as soon as we're done with
|
|
|
+ search and wait for the resume then give the semaphore
|
|
|
+ */
|
|
|
+ // PS: I know this is not fully race-condition free
|
|
|
+ if (ctx->active_remote.running) vTaskSuspend(NULL);
|
|
|
+ xTaskNotifyGive(ctx->active_remote.joiner);
|
|
|
+
|
|
|
+ // now our context will be deleted
|
|
|
+ vTaskSuspend(NULL);
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
static char *rsa_apply(unsigned char *input, int inlen, int *outlen, int mode)
|
|
|
{
|