Finalize buttons for AirPlay and BT, worked on coexistence

This commit is contained in:
philippe44
2020-01-04 02:03:34 -08:00
parent d0d98c778b
commit 540466e746
18 changed files with 361 additions and 152 deletions

View File

@@ -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);
/*----------------------------------------------------------------------------*/
@@ -186,26 +192,23 @@ struct raop_ctx_s *raop_create(struct in_addr host, char *name,
id[63] = '\0';
ctx->svc = mdnsd_register_svc(ctx->svr, id, "_raop._tcp.local", ctx->port, NULL, (const char**) txt);
pthread_create(&ctx->thread, NULL, &rtsp_thread, ctx);
pthread_create(&ctx->thread, NULL, &rtsp_thread, ctx);
#else
LOG_INFO("starting mDNS with %s", id);
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) {
int sock;
/*----------------------------------------------------------------------------*/
void raop_delete(struct raop_ctx_s *ctx) {
#ifdef WIN32
int sock;
struct sockaddr addr;
socklen_t nlen = sizeof(struct sockaddr);
#endif
@@ -214,66 +217,99 @@ void raop_delete(struct raop_ctx_s *ctx) {
#ifdef WIN32
ctx->running = false;
// wake-up thread by connecting socket, needed for freeBSD
sock = socket(AF_INET, SOCK_STREAM, 0);
getsockname(ctx->sock, (struct sockaddr *) &addr, &nlen);
connect(sock, (struct sockaddr*) &addr, sizeof(addr));
closesocket(sock);
#ifdef WIN32
sock = socket(AF_INET, SOCK_STREAM, 0);
getsockname(ctx->sock, (struct sockaddr *) &addr, &nlen);
connect(sock, (struct sockaddr*) &addr, sizeof(addr));
vTaskDelete(ctx->thread);
closesocket(sock);
heap_caps_free(ctx->xTaskBuffer);
#endif
pthread_join(ctx->thread, NULL);
#ifdef WIN32
rtp_end(ctx->rtp);
shutdown(ctx->sock, SD_BOTH);
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);
}
// stop broadcasting devices
mdns_service_remove(ctx->svr, ctx->svc);
mdnsd_stop(ctx->svr);
#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);
pthread_join(ctx->search_thread, NULL);
}
*/
NFREE(ctx->rtsp.aeskey);
NFREE(ctx->rtsp.aesiv);
NFREE(ctx->rtsp.fmtp);
mdns_service_remove("_raop", "_tcp");
#endif
NFREE(ctx->rtsp.aeskey);
#else
NFREE(ctx->rtsp.aesiv);
#endif
NFREE(ctx->rtsp.fmtp);
free(ctx);
}
/*----------------------------------------------------------------------------*/
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;
@@ -286,9 +322,9 @@ void raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param) {
asprintf(&command,"setproperty?dmcp.device-volume=%0.4lf", Volume);
break;
}
}
default:
break;
default:
break;
}
// no command to send to remote or no remote found yet
if (!command || !ctx->active_remote.port) {
@@ -317,11 +353,7 @@ void raop_cmd(struct raop_ctx_s *ctx, raop_event_t event, void *param) {
if (len > 0) resp[len-1] = '\0';
LOG_INFO("[%p]: sending airplay remote\n%s<== received ==>\n%s", ctx, buf, resp);
len = recv(sock, resp, 512, 0);
NFREE(method);
LOG_INFO("[%p]: sending airplay remote\n%s<== received ==>\n%s", ctx, buf, resp);
NFREE(method);
NFREE(buf);
kd_free(headers);
}
@@ -366,7 +398,7 @@ static void *rtsp_thread(void *arg) {
closesocket(sock);
LOG_INFO("RTSP close %u", sock);
sock = -1;
if (n < 0 || !res) {
}
}
if (sock != -1) closesocket(sock);
@@ -429,12 +461,27 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
kd_add(resp, "Public", "ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER");
} else if (!strcmp(method, "ANNOUNCE")) {
char *padded, *p;
NFREE(ctx->rtsp.aeskey);
NFREE(ctx->rtsp.aesiv);
NFREE(ctx->rtsp.fmtp);
// LMS might has taken over the player, leaving us with a running RTP session (should not happen)
if (ctx->rtp) {
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));
@@ -467,13 +514,17 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
}
if ((p = strcasestr(body, "fmtp")) != NULL) {
NFREE(padded);
p = strextract(p, ":", "\r\n");
ctx->rtsp.fmtp = strdup(p);
NFREE(p);
}
// on announce, search remote
NFREE(p);
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;
@@ -481,7 +532,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
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
ctx->active_remote.handle = init_mDNS(false, ctx->host);
} else if (!strcmp(method, "SETUP") && ((buf = kd_lookup(headers, "Transport")) != NULL)) {
char *p;
rtp_resp_t rtp = { 0 };
short unsigned tport = 0, cport = 0;
@@ -520,7 +571,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
kd_add(resp, "Audio-Latency", latency);
}
snprintf(latency, 6, "%u", ctx->latency);
buf = kd_lookup(headers, "RTP-Info");
if (buf && (p = strcasestr(buf, "seq")) != NULL) sscanf(p, "%*[^=]=%hu", &seqno);
if (buf && (p = strcasestr(buf, "rtptime")) != NULL) sscanf(p, "%*[^=]=%u", &rtptime);
@@ -533,7 +584,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
unsigned rtptime = 0;
char *p;
unsigned short seqno = 0;
buf = kd_lookup(headers, "RTP-Info");
if ((p = strcasestr(buf, "seq")) != NULL) sscanf(p, "%*[^=]=%hu", &seqno);
if ((p = strcasestr(buf, "rtptime")) != NULL) sscanf(p, "%*[^=]=%u", &rtptime);
@@ -541,20 +592,32 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
if (ctx->rtp && rtp_flush(ctx->rtp, seqno, rtptime))
success = ctx->cmd_cb(RAOP_FLUSH, NULL);
// only send FLUSH if useful (discards frames above buffer head and top)
} else if (!strcmp(method, "TEARDOWN")) {
rtp_end(ctx->rtp);
} else if (!strcmp(method, "TEARDOWN")) {
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);
#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
// need to make sure no search is on-going and reclaim pthread memory
memset(&ctx->active_remote, 0, sizeof(ctx->active_remote));
pthread_join(ctx->search_thread, NULL);
NFREE(ctx->rtsp.aeskey);
NFREE(ctx->rtsp.aesiv);
NFREE(ctx->rtsp.fmtp);
@@ -563,7 +626,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
} else if (!strcmp(method, "SET_PARAMETER")) {
char *p;
if (body && (p = strcasestr(body, "volume")) != NULL) {
float volume;
sscanf(p, "%*[^:]:%f", &volume);
@@ -585,7 +648,6 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
LOG_INFO("[%p]: received metadata\n\tartist: %s\n\talbum: %s\n\ttitle: %s",
ctx, metadata.artist, metadata.album, metadata.title);
free_metadata(&metadata);
if (!dmap_parse(&settings, body, len)) {
}
}
*/
@@ -605,7 +667,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
NFREE(body);
NFREE(buf);
kd_free(resp);
kd_free(headers);
return true;
}
@@ -625,11 +687,9 @@ bool search_remote_cb(mDNSservice_t *slist, void *cookie, bool *stop) {
inet_ntoa(ctx->active_remote.host), ctx->active_remote.port);
*stop = true;
break;
LOG_INFO("[%p]: found ActiveRemote for %s at %s:%u", ctx, ctx->active_remote.DACPid,
}
}
}
// let caller clear list
return false;
}
@@ -637,8 +697,58 @@ static void* search_remote(void *args) {
/*----------------------------------------------------------------------------*/
static void* search_remote(void *args) {
raop_ctx_t *ctx = (raop_ctx_t*) args;
query_mDNS(ctx->active_remote.handle, "_dacp._tcp.local", 0, 0, &search_remote_cb, (void*) ctx);
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;