mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-06 11:36:59 +03:00
Finalize buttons for AirPlay and BT, worked on coexistence
This commit is contained in:
@@ -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++) {
|
||||
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(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));
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ typedef struct {
|
||||
actrls_action_e normal[2], longpress[2], shifted[2], longshifted[2]; // [0] keypressed, [1] keyreleased
|
||||
} actrls_config_t;
|
||||
|
||||
void actrls_init(int n, actrls_config_t *config);
|
||||
void actrls_set_default(actrls_t controls);
|
||||
void actrls_set(actrls_t controls);
|
||||
void actrls_init(int n, const actrls_config_t *config);
|
||||
void actrls_set_default(const actrls_t controls);
|
||||
void actrls_set(const actrls_t controls);
|
||||
void actrls_unset(void);
|
||||
|
||||
@@ -36,10 +36,11 @@ static const char * TAG = "audio_controls";
|
||||
|
||||
static int n_buttons = 0;
|
||||
|
||||
#define BUTTON_STACK_SIZE 4096
|
||||
#define MAX_BUTTONS 16
|
||||
#define DEBOUNCE 50
|
||||
|
||||
static struct button_s {
|
||||
static EXT_RAM_ATTR struct button_s {
|
||||
void *id;
|
||||
int gpio, index;
|
||||
button_handler handler;
|
||||
@@ -149,6 +150,8 @@ void dummy_handler(void *id, button_event_e event, button_press_e press) {
|
||||
* Create buttons
|
||||
*/
|
||||
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;
|
||||
|
||||
@@ -157,7 +160,7 @@ void button_create(void *id, int gpio, int type, bool pull, button_handler handl
|
||||
if (!n_buttons) {
|
||||
button_evt_queue = xQueueCreate(10, sizeof(struct button_s));
|
||||
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
|
||||
|
||||
@@ -49,7 +49,7 @@ enum {
|
||||
};
|
||||
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);
|
||||
|
||||
/* 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_set_by_local_host(s_volume > 3 ? s_volume - 3 : 0);
|
||||
(*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) {
|
||||
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);
|
||||
//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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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_toggle, bt_play, // toggle, play
|
||||
bt_pause, bt_stop, // pause, stop
|
||||
@@ -127,16 +120,18 @@ static actrls_t controls = {
|
||||
bt_prev, bt_next, // prev, next
|
||||
};
|
||||
|
||||
void bt_sink_cmd(bt_sink_cmd_t event, ...) {
|
||||
switch(event) {
|
||||
case BT_SINK_DISCONNECTED:
|
||||
/* taking/giving audio system's control */
|
||||
void bt_master(bool on) {
|
||||
if (on) actrls_set(controls);
|
||||
else actrls_unset();
|
||||
}
|
||||
|
||||
/* disconnection */
|
||||
void bt_disconnect(void) {
|
||||
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();
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(BT_AV_TAG, "unhandled command %u", event);
|
||||
break;
|
||||
}
|
||||
ESP_LOGI(BT_AV_TAG, "forced disconnection");
|
||||
}
|
||||
|
||||
/* 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){
|
||||
memcpy(s_remote_bda, bda, 6);
|
||||
esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE);
|
||||
(*bt_app_a2d_cmd_cb)(BT_SINK_CONNECTED);
|
||||
actrls_set(controls);
|
||||
if (!(*bt_app_a2d_cmd_cb)(BT_SINK_CONNECTED)){
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
* @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__*/
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "log_util.h"
|
||||
|
||||
#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);
|
||||
|
||||
mdns_service_remove("_raop", "_tcp");
|
||||
}
|
||||
*/
|
||||
|
||||
NFREE(ctx->rtsp.aeskey);
|
||||
NFREE(ctx->rtsp.aesiv);
|
||||
NFREE(ctx->rtsp.fmtp);
|
||||
|
||||
// stop broadcasting devices
|
||||
#endif
|
||||
|
||||
NFREE(ctx->rtsp.aeskey);
|
||||
#else
|
||||
NFREE(ctx->rtsp.aesiv);
|
||||
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;
|
||||
@@ -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,7 +697,57 @@ 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);
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "freertos/timers.h"
|
||||
#include "nvs_utilities.h"
|
||||
#include "raop.h"
|
||||
#include "audio_controls.h"
|
||||
|
||||
#include "log_util.h"
|
||||
|
||||
@@ -22,14 +23,68 @@
|
||||
#define CONFIG_AIRPLAY_NAME "ESP32-AirPlay"
|
||||
#endif
|
||||
|
||||
static const char * TAG = "platform";
|
||||
|
||||
log_level raop_loglevel = lINFO;
|
||||
log_level util_loglevel;
|
||||
|
||||
static log_level *loglevel = &raop_loglevel;
|
||||
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
|
||||
*/
|
||||
@@ -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_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){
|
||||
memset(sink_name, 0x00, sizeof(sink_name));
|
||||
strncpy(sink_name,sink_name_buffer,sizeof(sink_name)-1 );
|
||||
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
|
||||
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) {
|
||||
raop_cmd(raop, event, param);
|
||||
void raop_disconnect(void) {
|
||||
LOG_INFO("forced disconnection");
|
||||
raop_cmd(raop, RAOP_STOP, NULL);
|
||||
actrls_unset();
|
||||
}
|
||||
|
||||
@@ -13,27 +13,31 @@
|
||||
|
||||
#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);
|
||||
|
||||
/**
|
||||
* @brief init sink mode (need to be provided)
|
||||
*/
|
||||
|
||||
void raop_sink_init(raop_cmd_cb_t cmd_cb, raop_data_cb_t data_cb);
|
||||
|
||||
/**
|
||||
* @brief deinit sink mode (need to be provided)
|
||||
*/
|
||||
|
||||
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*/
|
||||
@@ -139,7 +139,7 @@ typedef struct rtp_s {
|
||||
#ifdef WIN32
|
||||
pthread_t thread;
|
||||
#else
|
||||
TaskHandle_t thread, joiner;
|
||||
TaskHandle_t thread, joiner;
|
||||
StaticTask_t *xTaskBuffer;
|
||||
StackType_t xStack[RTP_STACK_SIZE] __attribute__ ((aligned (4)));
|
||||
#endif
|
||||
@@ -275,11 +275,11 @@ rtp_resp_t rtp_init(struct in_addr host, int latency, char *aeskey, char *aesiv,
|
||||
#ifdef WIN32
|
||||
pthread_create(&ctx->thread, NULL, rtp_thread_func, (void *) ctx);
|
||||
#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);
|
||||
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,
|
||||
CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1, ctx->xStack, ctx->xTaskBuffer );
|
||||
#endif
|
||||
} else {
|
||||
LOG_ERROR("[%p]: cannot start RTP", ctx);
|
||||
rtp_end(ctx);
|
||||
@@ -304,9 +304,8 @@ void rtp_end(rtp_t *ctx)
|
||||
#endif
|
||||
ctx->running = false;
|
||||
#ifdef WIN32
|
||||
pthread_join(ctx->thread, NULL);
|
||||
pthread_join(ctx->thread, NULL);
|
||||
#else
|
||||
xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);
|
||||
ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
|
||||
vTaskDelete(ctx->thread);
|
||||
heap_caps_free(ctx->xTaskBuffer);
|
||||
@@ -715,7 +714,7 @@ static void *rtp_thread_func(void *arg) {
|
||||
}
|
||||
|
||||
free(packet);
|
||||
LOG_INFO("[%p]: terminating", ctx);
|
||||
LOG_INFO("[%p]: terminating", ctx);
|
||||
|
||||
#ifndef WIN32
|
||||
xTaskNotifyGive(ctx->joiner);
|
||||
|
||||
@@ -69,7 +69,7 @@ static void lms_next(void) {
|
||||
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_toggle, lms_play, // toggle, play
|
||||
lms_pause, lms_stop, // pause, stop
|
||||
@@ -102,7 +102,7 @@ static void cli_send_cmd(char *cmd) {
|
||||
LOG_WARN("cannot send CLI %s", packet);
|
||||
}
|
||||
|
||||
close(sock);
|
||||
closesocket(sock);
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
|
||||
@@ -65,7 +65,7 @@ static void *decode_thread() {
|
||||
toend = (stream.state <= DISCONNECT);
|
||||
UNLOCK_S;
|
||||
LOCK_O;
|
||||
space = _buf_space(outputbuf);
|
||||
space = !output.external ? _buf_space(outputbuf) : 0;
|
||||
UNLOCK_O;
|
||||
|
||||
LOCK_D;
|
||||
@@ -117,10 +117,6 @@ static void *decode_thread() {
|
||||
}
|
||||
}
|
||||
|
||||
#if EMBEDDED
|
||||
deregister_external();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -246,6 +242,9 @@ void decode_close(void) {
|
||||
pthread_join(thread, NULL);
|
||||
#endif
|
||||
mutex_destroy(decode.mutex);
|
||||
#if EMBEDDED
|
||||
deregister_external();
|
||||
#endif
|
||||
}
|
||||
|
||||
void decode_flush(void) {
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#define LOCK_D mutex_lock(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 decodestate decode;
|
||||
@@ -60,8 +60,8 @@ static void sink_data_handler(const uint8_t *data, uint32_t len)
|
||||
{
|
||||
size_t bytes, space;
|
||||
|
||||
// would be better to lock decoder, but really, it does not matter
|
||||
if (decode.state != DECODE_STOPPED) {
|
||||
// would be better to lock output, but really, it does not matter
|
||||
if (!output.external) {
|
||||
LOG_SDEBUG("Cannot use external sink while LMS is controlling player");
|
||||
return;
|
||||
}
|
||||
@@ -99,19 +99,18 @@ static void sink_data_handler(const uint8_t *data, uint32_t len)
|
||||
* 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;
|
||||
|
||||
LOCK_D;
|
||||
|
||||
if (decode.state != DECODE_STOPPED) {
|
||||
LOG_WARN("Cannot use BT sink while LMS is controlling player");
|
||||
UNLOCK_D;
|
||||
bt_sink_cmd(BT_SINK_DISCONNECTED);
|
||||
return;
|
||||
// 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) {
|
||||
LOG_WARN("Cannot use BT sink while LMS/AirPlay is controlling player");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOCK_D;
|
||||
|
||||
va_start(args, cmd);
|
||||
|
||||
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:
|
||||
output.external = DECODE_BT;
|
||||
output.state = OUTPUT_STOPPED;
|
||||
output.frames_played = 0;
|
||||
_buf_flush(outputbuf);
|
||||
bt_master(true);
|
||||
LOG_INFO("BT sink started");
|
||||
break;
|
||||
case BT_SINK_DISCONNECTED:
|
||||
if (output.external == DECODE_BT) {
|
||||
output.external = 0;
|
||||
output.state = OUTPUT_OFF;
|
||||
bt_master(false);
|
||||
LOG_INFO("BT sink stopped");
|
||||
}
|
||||
break;
|
||||
@@ -155,6 +157,7 @@ static void bt_sink_cmd_handler(bt_sink_cmd_t cmd, ...)
|
||||
UNLOCK_D;
|
||||
|
||||
va_end(args);
|
||||
return true;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
@@ -171,16 +174,16 @@ static void raop_sink_data_handler(const uint8_t *data, uint32_t len, u32_t play
|
||||
/****************************************************************************************
|
||||
* 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;
|
||||
|
||||
if (decode.state != DECODE_STOPPED) {
|
||||
LOG_WARN("Cannot use Airplay sink while LMS is controlling player");
|
||||
UNLOCK_D;
|
||||
return;
|
||||
// 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) {
|
||||
LOG_WARN("Cannot use Airplay sink while LMS/BT is controlling player");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOCK_D;
|
||||
|
||||
if (event != RAOP_VOLUME) LOCK_O;
|
||||
|
||||
// this is async, so player might have been deleted
|
||||
@@ -233,6 +236,10 @@ void raop_sink_cmd_handler(raop_event_t event, void *param)
|
||||
case RAOP_SETUP:
|
||||
// we need a fair bit of space for RTP process
|
||||
_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);
|
||||
break;
|
||||
case RAOP_STREAM:
|
||||
@@ -242,16 +249,14 @@ void raop_sink_cmd_handler(raop_event_t event, void *param)
|
||||
raop_sync.idx = 0;
|
||||
raop_sync.start = true;
|
||||
raop_sync.enabled = !strcasestr(output.device, "BT");
|
||||
output.external = DECODE_AIRPLAY;
|
||||
output.next_sample_rate = output.current_sample_rate = RAOP_SAMPLE_RATE;
|
||||
output.state = OUTPUT_STOPPED;
|
||||
break;
|
||||
case RAOP_STOP:
|
||||
LOG_INFO("Stop", NULL);
|
||||
output.external = 0;
|
||||
output.state = OUTPUT_OFF;
|
||||
output.frames_played = 0;
|
||||
raop_state = event;
|
||||
raop_master(false);
|
||||
break;
|
||||
case RAOP_FLUSH:
|
||||
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;
|
||||
|
||||
UNLOCK_D;
|
||||
return true;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
@@ -300,7 +306,7 @@ void register_external(void) {
|
||||
} else {
|
||||
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);
|
||||
LOG_INFO("Initializing AirPlay sink");
|
||||
}
|
||||
@@ -311,8 +317,21 @@ void deregister_external(void) {
|
||||
bt_sink_deinit();
|
||||
LOG_INFO("Stopping BT sink");
|
||||
}
|
||||
if(enable_airplay){
|
||||
if (enable_airplay){
|
||||
raop_sink_deinit();
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -48,3 +48,9 @@ int pthread_create_name(pthread_t *thread, _CONST pthread_attr_t *attr,
|
||||
uint32_t _gettime_ms_(void) {
|
||||
return (uint32_t) (esp_timer_get_time() / 1000);
|
||||
}
|
||||
|
||||
extern void cli_controls_init(void);
|
||||
|
||||
void embedded_init(void) {
|
||||
cli_controls_init();
|
||||
}
|
||||
|
||||
@@ -43,8 +43,11 @@ uint32_t _gettime_ms_(void);
|
||||
int pthread_create_name(pthread_t *thread, _CONST pthread_attr_t *attr,
|
||||
void *(*start_routine)( void * ), void *arg, char *name);
|
||||
|
||||
void embedded_init(void);
|
||||
void register_external(void);
|
||||
void deregister_external(void);
|
||||
void decode_resume(int external);
|
||||
|
||||
void (*server_notify)(in_addr_t ip, u16_t hport, u16_t cport);
|
||||
|
||||
#endif // EMBEDDED_H
|
||||
|
||||
@@ -761,6 +761,7 @@ int main(int argc, char **argv) {
|
||||
stream_init(log_stream, stream_buf_size);
|
||||
|
||||
#if EMBEDDED
|
||||
embedded_init();
|
||||
output_init_embedded(log_output, output_device, output_buf_size, output_params, rates, rate_delay, idle);
|
||||
#else
|
||||
if (!strcmp(output_device, "-")) {
|
||||
|
||||
@@ -57,8 +57,6 @@ void output_init_embedded(log_level level, char *device, unsigned output_buf_siz
|
||||
output.start_frames = FRAME_BLOCK;
|
||||
output.rate_delay = rate_delay;
|
||||
|
||||
cli_controls_init();
|
||||
|
||||
if (strcasestr(device, "BT ")) {
|
||||
LOG_INFO("init Bluetooth");
|
||||
close_cb = &output_close_bt;
|
||||
|
||||
@@ -330,6 +330,12 @@ static void process_strm(u8_t *pkt, int len) {
|
||||
LOCK_O;
|
||||
output.state = jiffies ? OUTPUT_START_AT : OUTPUT_RUNNING;
|
||||
output.start_at = jiffies;
|
||||
#if EMBEDDED
|
||||
if (output.external) {
|
||||
decode_resume(output.external);
|
||||
output.external = 0;
|
||||
}
|
||||
#endif
|
||||
UNLOCK_O;
|
||||
|
||||
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);
|
||||
sentSTMu = sentSTMo = sentSTMl = false;
|
||||
LOCK_O;
|
||||
#if EMBEDDED
|
||||
if (output.external) decode_resume(output.external);
|
||||
output.external = 0;
|
||||
_buf_resize(outputbuf, output.init_size);
|
||||
#endif
|
||||
output.threshold = strm->output_threshold;
|
||||
output.next_replay_gain = unpackN(&strm->replay_gain);
|
||||
output.fade_mode = strm->transition_type - '0';
|
||||
|
||||
Reference in New Issue
Block a user