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

@@ -86,7 +86,7 @@ void down(void *id, button_event_e event, button_press_e press, bool longpress)
/**************************************************************************************** /****************************************************************************************
* *
*/ */
void actrls_init(int n, actrls_config_t *config) { void actrls_init(int n, const actrls_config_t *config) {
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
button_create(config + i, config[i].gpio, config[i].type, config[i].pull, control_handler, config[i].long_press, config[i].shifter_gpio); button_create(config + i, config[i].gpio, config[i].type, config[i].pull, control_handler, config[i].long_press, config[i].shifter_gpio);
} }
@@ -95,7 +95,7 @@ void actrls_init(int n, actrls_config_t *config) {
/**************************************************************************************** /****************************************************************************************
* *
*/ */
void actrls_set_default(actrls_t controls) { void actrls_set_default(const actrls_t controls) {
memcpy(default_controls, controls, sizeof(actrls_t)); memcpy(default_controls, controls, sizeof(actrls_t));
memcpy(current_controls, default_controls, sizeof(actrls_t)); memcpy(current_controls, default_controls, sizeof(actrls_t));
} }
@@ -103,7 +103,7 @@ void actrls_set_default(actrls_t controls) {
/**************************************************************************************** /****************************************************************************************
* *
*/ */
void actrls_set(actrls_t controls) { void actrls_set(const actrls_t controls) {
memcpy(current_controls, controls, sizeof(actrls_t)); memcpy(current_controls, controls, sizeof(actrls_t));
} }

View File

@@ -36,7 +36,7 @@ typedef struct {
actrls_action_e normal[2], longpress[2], shifted[2], longshifted[2]; // [0] keypressed, [1] keyreleased actrls_action_e normal[2], longpress[2], shifted[2], longshifted[2]; // [0] keypressed, [1] keyreleased
} actrls_config_t; } actrls_config_t;
void actrls_init(int n, actrls_config_t *config); void actrls_init(int n, const actrls_config_t *config);
void actrls_set_default(actrls_t controls); void actrls_set_default(const actrls_t controls);
void actrls_set(actrls_t controls); void actrls_set(const actrls_t controls);
void actrls_unset(void); void actrls_unset(void);

View File

@@ -36,10 +36,11 @@ static const char * TAG = "audio_controls";
static int n_buttons = 0; static int n_buttons = 0;
#define MAX_BUTTONS 16 #define BUTTON_STACK_SIZE 4096
#define DEBOUNCE 50 #define MAX_BUTTONS 16
#define DEBOUNCE 50
static struct button_s { static EXT_RAM_ATTR struct button_s {
void *id; void *id;
int gpio, index; int gpio, index;
button_handler handler; button_handler handler;
@@ -95,7 +96,7 @@ static void buttons_timer( TimerHandle_t xTimer ) {
*/ */
static void buttons_task(void* arg) { static void buttons_task(void* arg) {
ESP_LOGI(TAG, "starting button tasks"); ESP_LOGI(TAG, "starting button tasks");
while (1) { while (1) {
struct button_s button; struct button_s button;
button_event_e event; button_event_e event;
@@ -149,7 +150,9 @@ void dummy_handler(void *id, button_event_e event, button_press_e press) {
* Create buttons * Create buttons
*/ */
void button_create(void *id, int gpio, int type, bool pull, button_handler handler, int long_press, int shifter_gpio) { void button_create(void *id, int gpio, int type, bool pull, button_handler handler, int long_press, int shifter_gpio) {
static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4)));
static EXT_RAM_ATTR StackType_t xStack[BUTTON_STACK_SIZE] __attribute__ ((aligned (4)));
if (n_buttons >= MAX_BUTTONS) return; if (n_buttons >= MAX_BUTTONS) return;
ESP_LOGI(TAG, "creating button using GPIO %u, type %u, pull-up/down %u, long press %u shifter %u", gpio, type, pull, long_press, shifter_gpio); ESP_LOGI(TAG, "creating button using GPIO %u, type %u, pull-up/down %u, long press %u shifter %u", gpio, type, pull, long_press, shifter_gpio);
@@ -157,7 +160,7 @@ void button_create(void *id, int gpio, int type, bool pull, button_handler handl
if (!n_buttons) { if (!n_buttons) {
button_evt_queue = xQueueCreate(10, sizeof(struct button_s)); button_evt_queue = xQueueCreate(10, sizeof(struct button_s));
gpio_install_isr_service(0); gpio_install_isr_service(0);
xTaskCreate(buttons_task, "buttons_task", 8192, NULL, ESP_TASK_PRIO_MIN + 1, NULL); xTaskCreateStatic( (TaskFunction_t) buttons_task, "buttons_thread", BUTTON_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer);
} }
// just in case this structure is allocated in a future release // just in case this structure is allocated in a future release

View File

@@ -49,7 +49,7 @@ enum {
}; };
char * bt_name = NULL; char * bt_name = NULL;
static void (*bt_app_a2d_cmd_cb)(bt_sink_cmd_t cmd, ...); static bool (*bt_app_a2d_cmd_cb)(bt_sink_cmd_t cmd, ...);
static void (*bt_app_a2d_data_cb)(const uint8_t *data, uint32_t len); static void (*bt_app_a2d_data_cb)(const uint8_t *data, uint32_t len);
/* handler for bluetooth stack enabled events */ /* handler for bluetooth stack enabled events */
@@ -84,42 +84,35 @@ static void bt_volume_down(void) {
// volume UP/DOWN buttons are not supported by iPhone/Android // volume UP/DOWN buttons are not supported by iPhone/Android
volume_set_by_local_host(s_volume > 3 ? s_volume - 3 : 0); volume_set_by_local_host(s_volume > 3 ? s_volume - 3 : 0);
(*bt_app_a2d_cmd_cb)(BT_SINK_VOLUME, s_volume); (*bt_app_a2d_cmd_cb)(BT_SINK_VOLUME, s_volume);
ESP_LOGD(BT_AV_TAG, "BT volume down %u", s_volume);
} }
static void bt_toggle(void) { static void bt_toggle(void) {
if (s_play_status != ESP_AVRC_PLAYBACK_PLAYING) esp_avrc_ct_send_passthrough_cmd(tl++, ESP_AVRC_PT_CMD_PLAY, ESP_AVRC_PT_CMD_STATE_PRESSED); if (s_play_status != ESP_AVRC_PLAYBACK_PLAYING) esp_avrc_ct_send_passthrough_cmd(tl++, ESP_AVRC_PT_CMD_PLAY, ESP_AVRC_PT_CMD_STATE_PRESSED);
else esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_STOP, ESP_AVRC_PT_CMD_STATE_PRESSED); else esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_STOP, ESP_AVRC_PT_CMD_STATE_PRESSED);
//s_audio_state = ESP_A2D_AUDIO_STATE_STOPPED; //s_audio_state = ESP_A2D_AUDIO_STATE_STOPPED;
ESP_LOGD(BT_AV_TAG, "BT play/pause toggle %u", s_volume);
} }
static void bt_play(void) { static void bt_play(void) {
esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_PLAY, ESP_AVRC_PT_CMD_STATE_PRESSED); esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_PLAY, ESP_AVRC_PT_CMD_STATE_PRESSED);
ESP_LOGD(BT_AV_TAG, "BT play");
} }
static void bt_pause(void) { static void bt_pause(void) {
esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_PAUSE, ESP_AVRC_PT_CMD_STATE_PRESSED); esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_PAUSE, ESP_AVRC_PT_CMD_STATE_PRESSED);
ESP_LOGD(BT_AV_TAG, "BT pause");
} }
static void bt_stop(void) { static void bt_stop(void) {
esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_STOP, ESP_AVRC_PT_CMD_STATE_PRESSED); esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_STOP, ESP_AVRC_PT_CMD_STATE_PRESSED);
ESP_LOGD(BT_AV_TAG, "BT stop");
} }
static void bt_prev(void) { static void bt_prev(void) {
esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_BACKWARD, ESP_AVRC_PT_CMD_STATE_PRESSED); esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_BACKWARD, ESP_AVRC_PT_CMD_STATE_PRESSED);
ESP_LOGD(BT_AV_TAG, "BT previous");
} }
static void bt_next(void) { static void bt_next(void) {
esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_FORWARD, ESP_AVRC_PT_CMD_STATE_PRESSED); esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_FORWARD, ESP_AVRC_PT_CMD_STATE_PRESSED);
ESP_LOGD(BT_AV_TAG, "BT next");
} }
static actrls_t controls = { const static actrls_t controls = {
bt_volume_up, bt_volume_down, // volume up, volume down bt_volume_up, bt_volume_down, // volume up, volume down
bt_toggle, bt_play, // toggle, play bt_toggle, bt_play, // toggle, play
bt_pause, bt_stop, // pause, stop bt_pause, bt_stop, // pause, stop
@@ -127,16 +120,18 @@ static actrls_t controls = {
bt_prev, bt_next, // prev, next bt_prev, bt_next, // prev, next
}; };
void bt_sink_cmd(bt_sink_cmd_t event, ...) { /* taking/giving audio system's control */
switch(event) { void bt_master(bool on) {
case BT_SINK_DISCONNECTED: if (on) actrls_set(controls);
esp_a2d_sink_disconnect(s_remote_bda); else actrls_unset();
actrls_unset(); }
break;
default: /* disconnection */
ESP_LOGW(BT_AV_TAG, "unhandled command %u", event); void bt_disconnect(void) {
break; esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_STOP, ESP_AVRC_PT_CMD_STATE_PRESSED);
} esp_a2d_sink_disconnect(s_remote_bda);
actrls_unset();
ESP_LOGI(BT_AV_TAG, "forced disconnection");
} }
/* callback for A2DP sink */ /* callback for A2DP sink */
@@ -218,8 +213,10 @@ static void bt_av_hdl_a2d_evt(uint16_t event, void *p_param)
} else if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTED){ } else if (a2d->conn_stat.state == ESP_A2D_CONNECTION_STATE_CONNECTED){
memcpy(s_remote_bda, bda, 6); memcpy(s_remote_bda, bda, 6);
esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE); esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE);
(*bt_app_a2d_cmd_cb)(BT_SINK_CONNECTED); if (!(*bt_app_a2d_cmd_cb)(BT_SINK_CONNECTED)){
actrls_set(controls); esp_avrc_ct_send_passthrough_cmd(tl++ & 0x0f, ESP_AVRC_PT_CMD_STOP, ESP_AVRC_PT_CMD_STATE_PRESSED);
esp_a2d_sink_disconnect(s_remote_bda);
}
} }
break; break;
} }

View File

@@ -14,7 +14,7 @@
typedef enum { BT_SINK_CONNECTED, BT_SINK_DISCONNECTED, BT_SINK_PLAY, BT_SINK_STOP, BT_SINK_PAUSE, typedef enum { BT_SINK_CONNECTED, BT_SINK_DISCONNECTED, BT_SINK_PLAY, BT_SINK_STOP, BT_SINK_PAUSE,
BT_SINK_RATE, BT_SINK_VOLUME, } bt_sink_cmd_t; BT_SINK_RATE, BT_SINK_VOLUME, } bt_sink_cmd_t;
typedef void (*bt_cmd_cb_t)(bt_sink_cmd_t cmd, ...); typedef bool (*bt_cmd_cb_t)(bt_sink_cmd_t cmd, ...);
typedef void (*bt_data_cb_t)(const uint8_t *data, uint32_t len); typedef void (*bt_data_cb_t)(const uint8_t *data, uint32_t len);
/** /**
@@ -28,8 +28,13 @@ void bt_sink_init(bt_cmd_cb_t cmd_cb, bt_data_cb_t data_cb);
void bt_sink_deinit(void); void bt_sink_deinit(void);
/** /**
* @brief local command mode (stop, play, volume ...) * @brief * @brief do what's necessary when becoming in charge
*/ */
void bt_sink_cmd(bt_sink_cmd_t event, ...); void bt_master(bool on);
/**
* @brief force disconnection
*/
void bt_disconnect(void);
#endif /* __BT_APP_SINK_H__*/ #endif /* __BT_APP_SINK_H__*/

View File

@@ -43,7 +43,8 @@
#include "dmap_parser.h" #include "dmap_parser.h"
#include "log_util.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 { typedef struct raop_ctx_s {
#ifdef WIN32 #ifdef WIN32
@@ -60,7 +61,7 @@ typedef struct raop_ctx_s {
#else #else
TaskHandle_t thread, search_thread, joiner; TaskHandle_t thread, search_thread, joiner;
StaticTask_t *xTaskBuffer; StaticTask_t *xTaskBuffer;
StackType_t *xStack; StackType_t xStack[RTSP_STACK_SIZE] __attribute__ ((aligned (4)));
#endif #endif
unsigned char mac[6]; unsigned char mac[6];
int latency; int latency;
@@ -71,14 +72,19 @@ typedef struct raop_ctx_s {
struct rtp_s *rtp; struct rtp_s *rtp;
raop_cmd_cb_t cmd_cb; raop_cmd_cb_t cmd_cb;
raop_data_cb_t data_cb; raop_data_cb_t data_cb;
/*
struct { struct {
char DACPid[32], id[32]; char DACPid[32], id[32];
struct in_addr host; struct in_addr host;
u16_t port; u16_t port;
#ifdef WIN32
struct mDNShandle_s *handle; 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; } active_remote;
*/
void *owner; void *owner;
} raop_ctx_t; } raop_ctx_t;
@@ -98,7 +104,7 @@ static void* search_remote(void *args);
extern char private_key[]; extern char private_key[];
enum { RSA_MODE_KEY, RSA_MODE_AUTH }; 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);
/*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/
@@ -186,26 +192,23 @@ struct raop_ctx_s *raop_create(struct in_addr host, char *name,
id[63] = '\0'; id[63] = '\0';
ctx->svc = mdnsd_register_svc(ctx->svr, id, "_raop._tcp.local", ctx->port, NULL, (const char**) txt); 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);
pthread_create(&ctx->thread, NULL, &rtsp_thread, ctx);
#else #else
LOG_INFO("starting mDNS with %s", id); 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)) ); 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->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); ctx->thread = xTaskCreateStatic( (TaskFunction_t) rtsp_thread, "RTSP_thread", RTSP_STACK_SIZE, ctx, ESP_TASK_PRIO_MIN + 1, ctx->xStack, ctx->xTaskBuffer);
#endif #endif
return ctx; return ctx;
} }
/*----------------------------------------------------------------------------*/
void raop_delete(struct raop_ctx_s *ctx) {
int sock;
/*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/
void raop_delete(struct raop_ctx_s *ctx) {
#ifdef WIN32
int sock; int sock;
struct sockaddr addr;
socklen_t nlen = sizeof(struct sockaddr); socklen_t nlen = sizeof(struct sockaddr);
#endif #endif
@@ -214,66 +217,99 @@ void raop_delete(struct raop_ctx_s *ctx) {
#ifdef WIN32 #ifdef WIN32
ctx->running = false; ctx->running = false;
// wake-up thread by connecting socket, needed for freeBSD // 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); sock = socket(AF_INET, SOCK_STREAM, 0);
getsockname(ctx->sock, (struct sockaddr *) &addr, &nlen); getsockname(ctx->sock, (struct sockaddr *) &addr, &nlen);
connect(sock, (struct sockaddr*) &addr, sizeof(addr)); connect(sock, (struct sockaddr*) &addr, sizeof(addr));
vTaskDelete(ctx->thread);
closesocket(sock); closesocket(sock);
heap_caps_free(ctx->xTaskBuffer);
#endif
pthread_join(ctx->thread, NULL); pthread_join(ctx->thread, NULL);
#ifdef WIN32
rtp_end(ctx->rtp); rtp_end(ctx->rtp);
shutdown(ctx->sock, SD_BOTH); shutdown(ctx->sock, SD_BOTH);
closesocket(ctx->sock); 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); shutdown(ctx->sock, SHUT_RDWR);
closesocket(ctx->sock); 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"); mdns_service_remove("_raop", "_tcp");
#endif #endif
NFREE(ctx->rtsp.aeskey); NFREE(ctx->rtsp.aeskey);
#else
NFREE(ctx->rtsp.aesiv); NFREE(ctx->rtsp.aesiv);
#endif NFREE(ctx->rtsp.fmtp);
free(ctx); 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: case RAOP_NEXT:
command = strdup("nextitem"); command = strdup("nextitem");
break; break;
case RAOP_TOGGLE: case RAOP_TOGGLE:
command = strdup("playpause"); command = strdup("playpause");
break; break;
case RAOP_PAUSE:
command = strdup("pause");
break;
case RAOP_PLAY: case RAOP_PLAY:
command = strdup("play"); command = strdup("play");
break; break;
case RAOP_RESUME:
command = strdup("playresume");
break;
case RAOP_STOP:
command = strdup("stop");
break;
case RAOP_VOLUME_UP: case RAOP_VOLUME_UP:
command = strdup("volumeup"); command = strdup("volumeup");
break; 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); asprintf(&command,"setproperty?dmcp.device-volume=%0.4lf", Volume);
break; break;
} }
} default:
default: break;
break; }
// no command to send to remote or no remote found yet // no command to send to remote or no remote found yet
if (!command || !ctx->active_remote.port) { 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'; if (len > 0) resp[len-1] = '\0';
LOG_INFO("[%p]: sending airplay remote\n%s<== received ==>\n%s", ctx, buf, resp); LOG_INFO("[%p]: sending airplay remote\n%s<== received ==>\n%s", ctx, buf, resp);
len = recv(sock, resp, 512, 0);
NFREE(method); NFREE(method);
LOG_INFO("[%p]: sending airplay remote\n%s<== received ==>\n%s", ctx, buf, resp);
NFREE(method);
NFREE(buf); NFREE(buf);
kd_free(headers); kd_free(headers);
} }
@@ -366,7 +398,7 @@ static void *rtsp_thread(void *arg) {
closesocket(sock); closesocket(sock);
LOG_INFO("RTSP close %u", sock); LOG_INFO("RTSP close %u", sock);
sock = -1; sock = -1;
if (n < 0 || !res) { }
} }
if (sock != -1) closesocket(sock); 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"); kd_add(resp, "Public", "ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER");
} else if (!strcmp(method, "ANNOUNCE")) { } else if (!strcmp(method, "ANNOUNCE")) {
char *padded, *p;
NFREE(ctx->rtsp.aeskey);
NFREE(ctx->rtsp.aesiv); NFREE(ctx->rtsp.aesiv);
NFREE(ctx->rtsp.fmtp); 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); heap_caps_free(ctx->active_remote.xTaskBuffer);
memset(&ctx->active_remote, 0, sizeof(ctx->active_remote)); 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) { if ((p = strcasestr(body, "fmtp")) != NULL) {
NFREE(padded);
p = strextract(p, ":", "\r\n"); p = strextract(p, ":", "\r\n");
ctx->rtsp.fmtp = strdup(p); ctx->rtsp.fmtp = strdup(p);
NFREE(p); NFREE(p);
}
// on announce, search remote // 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); pthread_create(&ctx->search_thread, NULL, &search_remote, ctx);
#else #else
ctx->active_remote.running = true; 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); 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 #endif
ctx->active_remote.handle = init_mDNS(false, ctx->host); } else if (!strcmp(method, "SETUP") && ((buf = kd_lookup(headers, "Transport")) != NULL)) {
char *p; char *p;
rtp_resp_t rtp = { 0 }; rtp_resp_t rtp = { 0 };
short unsigned tport = 0, cport = 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); 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, "seq")) != NULL) sscanf(p, "%*[^=]=%hu", &seqno);
if (buf && (p = strcasestr(buf, "rtptime")) != NULL) sscanf(p, "%*[^=]=%u", &rtptime); 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; unsigned rtptime = 0;
char *p; 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, "seq")) != NULL) sscanf(p, "%*[^=]=%hu", &seqno);
if ((p = strcasestr(buf, "rtptime")) != NULL) sscanf(p, "%*[^=]=%u", &rtptime); 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)) if (ctx->rtp && rtp_flush(ctx->rtp, seqno, rtptime))
success = ctx->cmd_cb(RAOP_FLUSH, NULL); success = ctx->cmd_cb(RAOP_FLUSH, NULL);
// only send FLUSH if useful (discards frames above buffer head and top)
} else if (!strcmp(method, "TEARDOWN")) { } else if (!strcmp(method, "TEARDOWN")) {
rtp_end(ctx->rtp); 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 #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); heap_caps_free(ctx->active_remote.xTaskBuffer);
LOG_INFO("[%p]: mDNS search task terminated", ctx); LOG_INFO("[%p]: mDNS search task terminated", ctx);
#endif #endif
// need to make sure no search is on-going and reclaim pthread memory
memset(&ctx->active_remote, 0, sizeof(ctx->active_remote)); 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.aesiv);
NFREE(ctx->rtsp.fmtp); NFREE(ctx->rtsp.fmtp);
@@ -563,7 +626,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock)
} else if (!strcmp(method, "SET_PARAMETER")) { } else if (!strcmp(method, "SET_PARAMETER")) {
char *p; char *p;
if (body && (p = strcasestr(body, "volume")) != NULL) {
float volume; float volume;
sscanf(p, "%*[^:]:%f", &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", LOG_INFO("[%p]: received metadata\n\tartist: %s\n\talbum: %s\n\ttitle: %s",
ctx, metadata.artist, metadata.album, metadata.title); ctx, metadata.artist, metadata.album, metadata.title);
free_metadata(&metadata); 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(body);
NFREE(buf); NFREE(buf);
kd_free(resp); kd_free(resp);
kd_free(headers);
return true; 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); inet_ntoa(ctx->active_remote.host), ctx->active_remote.port);
*stop = true; *stop = true;
break; break;
LOG_INFO("[%p]: found ActiveRemote for %s at %s:%u", ctx, ctx->active_remote.DACPid,
} }
} }
}
// let caller clear list // let caller clear list
return false; return false;
} }
@@ -637,8 +697,58 @@ static void* search_remote(void *args) {
/*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/
static void* search_remote(void *args) { static void* search_remote(void *args) {
raop_ctx_t *ctx = (raop_ctx_t*) 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); vTaskSuspend(NULL);
return NULL; return NULL;

View File

@@ -24,7 +24,7 @@
#include "platform.h" #include "platform.h"
#include "raop_sink.h" #include "raop_sink.h"
struct raop_ctx_s* raop_create(struct in_addr host, char *name, unsigned char mac[6], int latency, struct raop_ctx_s* raop_create(struct in_addr host, char *name, unsigned char mac[6], int latency,
raop_cmd_cb_t cmd_cb, raop_data_cb_t data_cb); raop_cmd_cb_t cmd_cb, raop_data_cb_t data_cb);
void raop_delete(struct raop_ctx_s *ctx); void raop_delete(struct raop_ctx_s *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);

View File

@@ -13,6 +13,7 @@
#include "freertos/timers.h" #include "freertos/timers.h"
#include "nvs_utilities.h" #include "nvs_utilities.h"
#include "raop.h" #include "raop.h"
#include "audio_controls.h"
#include "log_util.h" #include "log_util.h"
@@ -22,14 +23,68 @@
#define CONFIG_AIRPLAY_NAME "ESP32-AirPlay" #define CONFIG_AIRPLAY_NAME "ESP32-AirPlay"
#endif #endif
static const char * TAG = "platform";
log_level raop_loglevel = lINFO; log_level raop_loglevel = lINFO;
log_level util_loglevel; log_level util_loglevel;
static log_level *loglevel = &raop_loglevel; static log_level *loglevel = &raop_loglevel;
static struct raop_ctx_s *raop; static struct raop_ctx_s *raop;
static void raop_volume_up(void) {
raop_cmd(raop, RAOP_VOLUME_UP, NULL);
LOG_INFO("AirPlay volume up");
}
static void raop_volume_down(void) {
raop_cmd(raop, RAOP_VOLUME_DOWN, NULL);
LOG_INFO("AirPlay volume down");
}
static void raop_toggle(void) {
raop_cmd(raop, RAOP_TOGGLE, NULL);
LOG_INFO("AirPlay play/pause");
}
static void raop_pause(void) {
raop_cmd(raop, RAOP_PAUSE, NULL);
LOG_INFO("AirPlay pause");
}
static void raop_play(void) {
raop_cmd(raop, RAOP_PLAY, NULL);
LOG_INFO("AirPlay play");
}
static void raop_stop(void) {
raop_cmd(raop, RAOP_STOP, NULL);
LOG_INFO("AirPlay stop");
}
static void raop_prev(void) {
raop_cmd(raop, RAOP_PREV, NULL);
LOG_INFO("AirPlay previous");
}
static void raop_next(void) {
raop_cmd(raop, RAOP_NEXT, NULL);
LOG_INFO("AirPlay next");
}
const static actrls_t controls = {
raop_volume_up, raop_volume_down, // volume up, volume down
raop_toggle, raop_play, // toggle, play
raop_pause, raop_stop, // pause, stop
NULL, NULL, // rew, fwd
raop_prev, raop_next, // prev, next
};
/****************************************************************************************
* Airplay taking/giving audio system's control
*/
void raop_master(bool on) {
if (on) actrls_set(controls);
else actrls_unset();
}
/**************************************************************************************** /****************************************************************************************
* Airplay sink de-initialization * Airplay sink de-initialization
*/ */
@@ -56,14 +111,14 @@ void raop_sink_init(raop_cmd_cb_t cmd_cb, raop_data_cb_t data_cb) {
ESP_ERROR_CHECK( mdns_init() ); ESP_ERROR_CHECK( mdns_init() );
ESP_ERROR_CHECK( mdns_hostname_set(hostname) ); ESP_ERROR_CHECK( mdns_hostname_set(hostname) );
char * sink_name_buffer= (char *)config_alloc_get(NVS_TYPE_STR, "airplay_name"); char * sink_name_buffer= (char *)config_alloc_get(NVS_TYPE_STR,"AirPlay_name");
if(sink_name_buffer != NULL){ if(sink_name_buffer != NULL){
memset(sink_name, 0x00, sizeof(sink_name)); memset(sink_name, 0x00, sizeof(sink_name));
strncpy(sink_name,sink_name_buffer,sizeof(sink_name)-1 ); strncpy(sink_name,sink_name_buffer,sizeof(sink_name)-1 );
free(sink_name_buffer); free(sink_name_buffer);
} }
ESP_LOGI(TAG, "mdns hostname set to: [%s] with servicename %s", hostname, sink_name); LOG_INFO( "mdns hostname set to: [%s] with servicename %s", hostname, sink_name);
// create RAOP instance, latency is set by controller // create RAOP instance, latency is set by controller
uint8_t mac[6]; uint8_t mac[6];
@@ -72,8 +127,10 @@ void raop_sink_init(raop_cmd_cb_t cmd_cb, raop_data_cb_t data_cb) {
} }
/**************************************************************************************** /****************************************************************************************
* Airplay local command (stop, start, volume ...) * Airplay forced disconnection
*/ */
void raop_sink_cmd(raop_event_t event, void *param) { void raop_disconnect(void) {
raop_cmd(raop, event, param); LOG_INFO("forced disconnection");
raop_cmd(raop, RAOP_STOP, NULL);
actrls_unset();
} }

View File

@@ -13,27 +13,31 @@
#define RAOP_SAMPLE_RATE 44100 #define RAOP_SAMPLE_RATE 44100
typedef enum { RAOP_SETUP, RAOP_STREAM, RAOP_PLAY, RAOP_FLUSH, RAOP_PAUSE, RAOP_STOP, RAOP_VOLUME, RAOP_TIMING } raop_event_t ; typedef enum { RAOP_SETUP, RAOP_STREAM, RAOP_PLAY, RAOP_FLUSH, RAOP_PAUSE, RAOP_STOP,
RAOP_VOLUME, RAOP_TIMING, RAOP_PREV, RAOP_NEXT, RAOP_REW, RAOP_FWD,
RAOP_VOLUME_UP, RAOP_VOLUME_DOWN, RAOP_RESUME, RAOP_TOGGLE } raop_event_t ;
typedef void (*raop_cmd_cb_t)(raop_event_t event, void *param); typedef bool (*raop_cmd_cb_t)(raop_event_t event, void *param);
typedef void (*raop_data_cb_t)(const u8_t *data, size_t len, u32_t playtime); typedef void (*raop_data_cb_t)(const u8_t *data, size_t len, u32_t playtime);
/** /**
* @brief init sink mode (need to be provided) * @brief init sink mode (need to be provided)
*/ */
void raop_sink_init(raop_cmd_cb_t cmd_cb, raop_data_cb_t data_cb); void raop_sink_init(raop_cmd_cb_t cmd_cb, raop_data_cb_t data_cb);
/** /**
* @brief deinit sink mode (need to be provided) * @brief deinit sink mode (need to be provided)
*/ */
void raop_sink_deinit(void); void raop_sink_deinit(void);
/** /**
* @brief init sink mode (need to be provided) * @brief do what's necessary when becoming in charge
*/ */
void raop_master(bool on);
void raop_sink_cmd(raop_event_t event, void *param); /**
* @brief force disconnection
*/
void raop_disconnect(void);
#endif /* RAOP_SINK_H*/ #endif /* RAOP_SINK_H*/

View File

@@ -139,7 +139,7 @@ typedef struct rtp_s {
#ifdef WIN32 #ifdef WIN32
pthread_t thread; pthread_t thread;
#else #else
TaskHandle_t thread, joiner; TaskHandle_t thread, joiner;
StaticTask_t *xTaskBuffer; StaticTask_t *xTaskBuffer;
StackType_t xStack[RTP_STACK_SIZE] __attribute__ ((aligned (4))); StackType_t xStack[RTP_STACK_SIZE] __attribute__ ((aligned (4)));
#endif #endif
@@ -275,11 +275,11 @@ rtp_resp_t rtp_init(struct in_addr host, int latency, char *aeskey, char *aesiv,
#ifdef WIN32 #ifdef WIN32
pthread_create(&ctx->thread, NULL, rtp_thread_func, (void *) ctx); pthread_create(&ctx->thread, NULL, rtp_thread_func, (void *) ctx);
#else #else
// xTaskCreate((TaskFunction_t) rtp_thread_func, "RTP_thread", RTP_TASK_SIZE, ctx, CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1 , &ctx->thread);
// xTaskCreate((TaskFunction_t) rtp_thread_func, "RTP_thread", RTP_TASK_SIZE, ctx, CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1 , &ctx->thread); // xTaskCreate((TaskFunction_t) rtp_thread_func, "RTP_thread", RTP_TASK_SIZE, ctx, CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1 , &ctx->thread);
ctx->xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); ctx->xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
ctx->thread = xTaskCreateStatic( (TaskFunction_t) rtp_thread_func, "RTP_thread", RTP_STACK_SIZE, ctx, ctx->thread = xTaskCreateStatic( (TaskFunction_t) rtp_thread_func, "RTP_thread", RTP_STACK_SIZE, ctx,
CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1, ctx->xStack, ctx->xTaskBuffer ); CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1, ctx->xStack, ctx->xTaskBuffer );
#endif
} else { } else {
LOG_ERROR("[%p]: cannot start RTP", ctx); LOG_ERROR("[%p]: cannot start RTP", ctx);
rtp_end(ctx); rtp_end(ctx);
@@ -304,9 +304,8 @@ void rtp_end(rtp_t *ctx)
#endif #endif
ctx->running = false; ctx->running = false;
#ifdef WIN32 #ifdef WIN32
pthread_join(ctx->thread, NULL); pthread_join(ctx->thread, NULL);
#else #else
xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);
ulTaskNotifyTake(pdFALSE, portMAX_DELAY); ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
vTaskDelete(ctx->thread); vTaskDelete(ctx->thread);
heap_caps_free(ctx->xTaskBuffer); heap_caps_free(ctx->xTaskBuffer);
@@ -715,7 +714,7 @@ static void *rtp_thread_func(void *arg) {
} }
free(packet); free(packet);
LOG_INFO("[%p]: terminating", ctx); LOG_INFO("[%p]: terminating", ctx);
#ifndef WIN32 #ifndef WIN32
xTaskNotifyGive(ctx->joiner); xTaskNotifyGive(ctx->joiner);

View File

@@ -69,7 +69,7 @@ static void lms_next(void) {
cli_send_cmd("button fwd"); cli_send_cmd("button fwd");
} }
static actrls_t controls = { const static actrls_t controls = {
lms_volume_up, lms_volume_down, // volume up, volume down lms_volume_up, lms_volume_down, // volume up, volume down
lms_toggle, lms_play, // toggle, play lms_toggle, lms_play, // toggle, play
lms_pause, lms_stop, // pause, stop lms_pause, lms_stop, // pause, stop
@@ -102,7 +102,7 @@ static void cli_send_cmd(char *cmd) {
LOG_WARN("cannot send CLI %s", packet); LOG_WARN("cannot send CLI %s", packet);
} }
close(sock); closesocket(sock);
} }
/**************************************************************************************** /****************************************************************************************

View File

@@ -65,7 +65,7 @@ static void *decode_thread() {
toend = (stream.state <= DISCONNECT); toend = (stream.state <= DISCONNECT);
UNLOCK_S; UNLOCK_S;
LOCK_O; LOCK_O;
space = _buf_space(outputbuf); space = !output.external ? _buf_space(outputbuf) : 0;
UNLOCK_O; UNLOCK_O;
LOCK_D; LOCK_D;
@@ -117,10 +117,6 @@ static void *decode_thread() {
} }
} }
#if EMBEDDED
deregister_external();
#endif
return 0; return 0;
} }
@@ -246,6 +242,9 @@ void decode_close(void) {
pthread_join(thread, NULL); pthread_join(thread, NULL);
#endif #endif
mutex_destroy(decode.mutex); mutex_destroy(decode.mutex);
#if EMBEDDED
deregister_external();
#endif
} }
void decode_flush(void) { void decode_flush(void) {

View File

@@ -29,7 +29,7 @@
#define LOCK_D mutex_lock(decode.mutex); #define LOCK_D mutex_lock(decode.mutex);
#define UNLOCK_D mutex_unlock(decode.mutex); #define UNLOCK_D mutex_unlock(decode.mutex);
enum { DECODE_BT = 1, DECODE_AIRPLAY }; enum { DECODE_BT = 1, DECODE_RAOP };
extern struct outputstate output; extern struct outputstate output;
extern struct decodestate decode; extern struct decodestate decode;
@@ -60,8 +60,8 @@ static void sink_data_handler(const uint8_t *data, uint32_t len)
{ {
size_t bytes, space; size_t bytes, space;
// would be better to lock decoder, but really, it does not matter // would be better to lock output, but really, it does not matter
if (decode.state != DECODE_STOPPED) { if (!output.external) {
LOG_SDEBUG("Cannot use external sink while LMS is controlling player"); LOG_SDEBUG("Cannot use external sink while LMS is controlling player");
return; return;
} }
@@ -99,19 +99,18 @@ static void sink_data_handler(const uint8_t *data, uint32_t len)
* BT sink command handler * BT sink command handler
*/ */
static void bt_sink_cmd_handler(bt_sink_cmd_t cmd, ...) static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, ...)
{ {
va_list args; va_list args;
LOCK_D; // don't LOCK_O as there is always a chance that LMS takes control later anyway
if (output.external != DECODE_BT && output.state > OUTPUT_STOPPED) {
if (decode.state != DECODE_STOPPED) { LOG_WARN("Cannot use BT sink while LMS/AirPlay is controlling player");
LOG_WARN("Cannot use BT sink while LMS is controlling player"); return false;
UNLOCK_D;
bt_sink_cmd(BT_SINK_DISCONNECTED);
return;
} }
LOCK_D;
va_start(args, cmd); va_start(args, cmd);
if (cmd != BT_SINK_VOLUME) LOCK_O; if (cmd != BT_SINK_VOLUME) LOCK_O;
@@ -120,12 +119,15 @@ static void bt_sink_cmd_handler(bt_sink_cmd_t cmd, ...)
case BT_SINK_CONNECTED: case BT_SINK_CONNECTED:
output.external = DECODE_BT; output.external = DECODE_BT;
output.state = OUTPUT_STOPPED; output.state = OUTPUT_STOPPED;
output.frames_played = 0;
_buf_flush(outputbuf);
bt_master(true);
LOG_INFO("BT sink started"); LOG_INFO("BT sink started");
break; break;
case BT_SINK_DISCONNECTED: case BT_SINK_DISCONNECTED:
if (output.external == DECODE_BT) { if (output.external == DECODE_BT) {
output.external = 0;
output.state = OUTPUT_OFF; output.state = OUTPUT_OFF;
bt_master(false);
LOG_INFO("BT sink stopped"); LOG_INFO("BT sink stopped");
} }
break; break;
@@ -155,6 +157,7 @@ static void bt_sink_cmd_handler(bt_sink_cmd_t cmd, ...)
UNLOCK_D; UNLOCK_D;
va_end(args); va_end(args);
return true;
} }
/**************************************************************************************** /****************************************************************************************
@@ -171,15 +174,15 @@ static void raop_sink_data_handler(const uint8_t *data, uint32_t len, u32_t play
/**************************************************************************************** /****************************************************************************************
* AirPlay sink command handler * AirPlay sink command handler
*/ */
void raop_sink_cmd_handler(raop_event_t event, void *param) static bool raop_sink_cmd_handler(raop_event_t event, void *param)
{ {
LOCK_D; // don't LOCK_O as there is always a chance that LMS takes control later anyway
if (output.external != DECODE_RAOP && output.state > OUTPUT_STOPPED) {
if (decode.state != DECODE_STOPPED) { LOG_WARN("Cannot use Airplay sink while LMS/BT is controlling player");
LOG_WARN("Cannot use Airplay sink while LMS is controlling player"); return false;
UNLOCK_D;
return;
} }
LOCK_D;
if (event != RAOP_VOLUME) LOCK_O; if (event != RAOP_VOLUME) LOCK_O;
@@ -233,6 +236,10 @@ void raop_sink_cmd_handler(raop_event_t event, void *param)
case RAOP_SETUP: case RAOP_SETUP:
// we need a fair bit of space for RTP process // we need a fair bit of space for RTP process
_buf_resize(outputbuf, RAOP_OUTPUT_SIZE); _buf_resize(outputbuf, RAOP_OUTPUT_SIZE);
output.frames_played = 0;
output.external = DECODE_RAOP;
output.state = OUTPUT_STOPPED;
raop_master(true);
LOG_INFO("resizing buffer %u", outputbuf->size); LOG_INFO("resizing buffer %u", outputbuf->size);
break; break;
case RAOP_STREAM: case RAOP_STREAM:
@@ -242,16 +249,14 @@ void raop_sink_cmd_handler(raop_event_t event, void *param)
raop_sync.idx = 0; raop_sync.idx = 0;
raop_sync.start = true; raop_sync.start = true;
raop_sync.enabled = !strcasestr(output.device, "BT"); raop_sync.enabled = !strcasestr(output.device, "BT");
output.external = DECODE_AIRPLAY;
output.next_sample_rate = output.current_sample_rate = RAOP_SAMPLE_RATE; output.next_sample_rate = output.current_sample_rate = RAOP_SAMPLE_RATE;
output.state = OUTPUT_STOPPED;
break; break;
case RAOP_STOP: case RAOP_STOP:
LOG_INFO("Stop", NULL); LOG_INFO("Stop", NULL);
output.external = 0;
output.state = OUTPUT_OFF; output.state = OUTPUT_OFF;
output.frames_played = 0; output.frames_played = 0;
raop_state = event; raop_state = event;
raop_master(false);
break; break;
case RAOP_FLUSH: case RAOP_FLUSH:
LOG_INFO("Flush", NULL); LOG_INFO("Flush", NULL);
@@ -286,6 +291,7 @@ void raop_sink_cmd_handler(raop_event_t event, void *param)
if (event != RAOP_VOLUME) UNLOCK_O; if (event != RAOP_VOLUME) UNLOCK_O;
UNLOCK_D; UNLOCK_D;
return true;
} }
/**************************************************************************************** /****************************************************************************************
@@ -300,7 +306,7 @@ void register_external(void) {
} else { } else {
LOG_WARN("Cannot be a BT sink and source"); LOG_WARN("Cannot be a BT sink and source");
} }
if(enable_airplay){ if (enable_airplay){
raop_sink_init(raop_sink_cmd_handler, raop_sink_data_handler); raop_sink_init(raop_sink_cmd_handler, raop_sink_data_handler);
LOG_INFO("Initializing AirPlay sink"); LOG_INFO("Initializing AirPlay sink");
} }
@@ -311,8 +317,21 @@ void deregister_external(void) {
bt_sink_deinit(); bt_sink_deinit();
LOG_INFO("Stopping BT sink"); LOG_INFO("Stopping BT sink");
} }
if(enable_airplay){ if (enable_airplay){
raop_sink_deinit(); raop_sink_deinit();
LOG_INFO("Stopping AirPlay sink"); LOG_INFO("Stopping AirPlay sink");
} }
} }
void decode_resume(int external) {
switch (external) {
case DECODE_BT:
bt_disconnect();
break;
case DECODE_RAOP:
raop_disconnect();
raop_state = RAOP_STOP;
break;
}
_buf_resize(outputbuf, output.init_size);
}

View File

@@ -48,3 +48,9 @@ int pthread_create_name(pthread_t *thread, _CONST pthread_attr_t *attr,
uint32_t _gettime_ms_(void) { uint32_t _gettime_ms_(void) {
return (uint32_t) (esp_timer_get_time() / 1000); return (uint32_t) (esp_timer_get_time() / 1000);
} }
extern void cli_controls_init(void);
void embedded_init(void) {
cli_controls_init();
}

View File

@@ -43,8 +43,11 @@ uint32_t _gettime_ms_(void);
int pthread_create_name(pthread_t *thread, _CONST pthread_attr_t *attr, int pthread_create_name(pthread_t *thread, _CONST pthread_attr_t *attr,
void *(*start_routine)( void * ), void *arg, char *name); void *(*start_routine)( void * ), void *arg, char *name);
void embedded_init(void);
void register_external(void); void register_external(void);
void deregister_external(void); void deregister_external(void);
void decode_resume(int external);
void (*server_notify)(in_addr_t ip, u16_t hport, u16_t cport); void (*server_notify)(in_addr_t ip, u16_t hport, u16_t cport);
#endif // EMBEDDED_H #endif // EMBEDDED_H

View File

@@ -761,6 +761,7 @@ int main(int argc, char **argv) {
stream_init(log_stream, stream_buf_size); stream_init(log_stream, stream_buf_size);
#if EMBEDDED #if EMBEDDED
embedded_init();
output_init_embedded(log_output, output_device, output_buf_size, output_params, rates, rate_delay, idle); output_init_embedded(log_output, output_device, output_buf_size, output_params, rates, rate_delay, idle);
#else #else
if (!strcmp(output_device, "-")) { if (!strcmp(output_device, "-")) {

View File

@@ -57,8 +57,6 @@ void output_init_embedded(log_level level, char *device, unsigned output_buf_siz
output.start_frames = FRAME_BLOCK; output.start_frames = FRAME_BLOCK;
output.rate_delay = rate_delay; output.rate_delay = rate_delay;
cli_controls_init();
if (strcasestr(device, "BT ")) { if (strcasestr(device, "BT ")) {
LOG_INFO("init Bluetooth"); LOG_INFO("init Bluetooth");
close_cb = &output_close_bt; close_cb = &output_close_bt;

View File

@@ -330,6 +330,12 @@ static void process_strm(u8_t *pkt, int len) {
LOCK_O; LOCK_O;
output.state = jiffies ? OUTPUT_START_AT : OUTPUT_RUNNING; output.state = jiffies ? OUTPUT_START_AT : OUTPUT_RUNNING;
output.start_at = jiffies; output.start_at = jiffies;
#if EMBEDDED
if (output.external) {
decode_resume(output.external);
output.external = 0;
}
#endif
UNLOCK_O; UNLOCK_O;
LOG_DEBUG("unpause at: %u now: %u", jiffies, gettime_ms()); LOG_DEBUG("unpause at: %u now: %u", jiffies, gettime_ms());
@@ -373,8 +379,10 @@ static void process_strm(u8_t *pkt, int len) {
sendSTAT("STMc", 0); sendSTAT("STMc", 0);
sentSTMu = sentSTMo = sentSTMl = false; sentSTMu = sentSTMo = sentSTMl = false;
LOCK_O; LOCK_O;
#if EMBEDDED
if (output.external) decode_resume(output.external);
output.external = 0; output.external = 0;
_buf_resize(outputbuf, output.init_size); #endif
output.threshold = strm->output_threshold; output.threshold = strm->output_threshold;
output.next_replay_gain = unpackN(&strm->replay_gain); output.next_replay_gain = unpackN(&strm->replay_gain);
output.fade_mode = strm->transition_type - '0'; output.fade_mode = strm->transition_type - '0';