mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-06 19:47:02 +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++) {
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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__*/
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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*/
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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, "-")) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|||||||
Reference in New Issue
Block a user