Compare commits

...

22 Commits

Author SHA1 Message Date
philippe44
cb47ec855b Merge branch 'master-v4.3' of https://github.com/sle118/squeezelite-esp32 into master-v4.3 2024-03-20 22:36:28 -07:00
philippe44
787a5d9a6e spdif glitch at track transition 2024-03-20 22:36:22 -07:00
github-actions
7b9deb795c Update prebuilt objects [skip actions] 2024-01-28 06:33:54 +00:00
philippe44
4b1f8a8d4b see CHANGELOG - release 2024-01-27 22:32:01 -08:00
philippe44
4f8661100b Merge branch 'master-v4.3' of https://github.com/sle118/squeezelite-esp32 into master-v4.3 2024-01-19 16:14:12 -08:00
philippe44
6b2eb1b3c0 see CHANGELOG 2024-01-19 16:14:08 -08:00
github-actions
f3593fa2f4 Update prebuilt objects [skip actions] 2024-01-17 02:53:45 +00:00
philippe44
ccc7b86369 See CHANGELOG - release 2024-01-16 18:51:33 -08:00
philippe44
9e3c6dcf30 Update README.md 2024-01-10 21:53:03 -08:00
github-actions
7be81887a6 Update prebuilt objects [skip actions] 2024-01-11 05:41:18 +00:00
philippe44
29f71fc677 Merge branch 'master-v4.3' of https://github.com/sle118/squeezelite-esp32 into master-v4.3 2024-01-10 21:36:53 -08:00
philippe44
a14a6edc1b reset flac packet flag - release 2024-01-10 21:36:50 -08:00
github-actions
35bf0a3c10 Update prebuilt objects [skip actions] 2024-01-11 03:00:35 +00:00
philippe44
55a8658f3a OggFlac & displayer fixes (see CHANGELOG) - release 2024-01-10 18:58:47 -08:00
github-actions
b0d9e1668c Update prebuilt objects [skip actions] 2024-01-04 08:48:28 +00:00
philippe44
922367baf5 no false alarm on OggS miss + homogeneous use of u32/size_t/int - release 2024-01-04 00:46:36 -08:00
philippe44
01320db007 always copy granule unless it's -1 (not valid) 2024-01-03 23:47:00 -08:00
philippe44
f677695fc7 handle pages with no terminated packet 2024-01-03 23:25:08 -08:00
github-actions
7902af2bf0 Update prebuilt objects [skip actions] 2024-01-03 04:08:04 +00:00
philippe44
5faecb08f6 Merge branch 'master-v4.3' of https://github.com/sle118/squeezelite-esp32 into master-v4.3 2024-01-02 18:15:43 -08:00
philippe44
8e3d91020f bring ogg parsing context purely inside stream.c - release 2024-01-02 18:15:39 -08:00
github-actions
4f54a47c83 Update prebuilt objects [skip actions] 2024-01-02 08:50:27 +00:00
53 changed files with 338 additions and 179 deletions

View File

@@ -1,3 +1,24 @@
2024-01-27
- complete libflac fix and add chaining enablement
- fixed stream Ogg demux issue with unknown granule
2024-01-19
- fixed libflac with OggFlac
- AirPlay missed frame logging
2024-01-16
- catch-up with cspot latest
- refactor airplay flush/first packet
- new libFLAC that supports multi-stream OggFlac
- fix output threshold
- log missed frames
2024-01-10
- add OggFlac to stream metadata
- fix OggFlac deadlock in flac callback when not enough data in streambuf
- fix no displayer due to threadshold too high (use 500ms instead)
- reset outputbuf when cspot starts
2024-01-01
- ogg stream are parsed to foward metadata to LMS
- fix some ogg parsing on multi-stream containers
@@ -14,7 +35,7 @@
- force gpio_pad_select_gpio in dac_controlset in case somebody uses UART gpio's (or other pre-programmed)
2023-11-08
- execute dac_controlset even whne there is no i2s (for gpio)
- execute dac_controlset even when there is no i2s (for gpio)
2023-11-07
- led-vu gain + misc fixes

View File

@@ -637,7 +637,11 @@ You can install IDF manually on Linux or Windows (using the Subsystem for Linux)
**Use the esp-idf 4.3.5 https://github.com/espressif/esp-idf/tree/release/v4.3.5 ** or the 4.4.5 (and above version) if you want to build for esp32-s3
## Building SqueezeESP32
When initially cloning the repo, make sure you do it recursively. For example: `git clone --recursive https://github.com/sle118/squeezelite-esp32.git`
When initially cloning the repo, make sure you do it recursively. For example: `git clone --recursive https://github.com/sle118/squeezelite-esp32.git`. You also should install cspot additional components for protobuf use.
```
$ sudo pip3 install protobuf grpcio-tools
```
NB: I need to check on a fresh installation, but you might also require "protoc". You should do that within the esp32 local Python environment.
Don't forget to choose one of the config files in build_scripts/ and rename it sdkconfig.defaults or sdkconfig as many important WiFi/BT options are set there. **The codecs libraries will not be rebuilt by these scripts (it's a tedious process - see below)**

View File

@@ -1,6 +1,6 @@
/* libFLAC - Free Lossless Audio Codec library
* Copyright (C) 2000-2009 Josh Coalson
* Copyright (C) 2011-2022 Xiph.Org Foundation
* Copyright (C) 2011-2023 Xiph.Org Foundation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -782,6 +782,25 @@ FLAC_API void FLAC__stream_decoder_delete(FLAC__StreamDecoder *decoder);
*/
FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_serial_number(FLAC__StreamDecoder *decoder, long serial_number);
/** Set the "allow Ogg chaining" flag. If set, the Ogg decoder will
* prepare to receive a new stream once the last Ogg page arrives for
* the stream encapsulating the FLAC audio data. This can be used to
* support chained Ogg FLAC streams; a new \c STREAMINFO signals the
* beginning of a new stream.
*
* \note
* This function has no effect with native FLAC decoding.
*
* \default \c false
* \param decoder A decoder instance to set.
* \param allow Whether to allow chained streams.
* \assert
* \code decoder != NULL \endcode
* \retval FLAC__bool
* \c false if the decoder is already initialized, else \c true.
*/
FLAC_API FLAC__bool FLAC__stream_decoder_set_ogg_chaining(FLAC__StreamDecoder* decoder, FLAC__bool value);
/** Set the "MD5 signature checking" flag. If \c true, the decoder will
* compute the MD5 signature of the unencoded audio data while decoding
* and compare it to the signature from the STREAMINFO block, if it
@@ -906,6 +925,17 @@ FLAC_API FLAC__StreamDecoderState FLAC__stream_decoder_get_state(const FLAC__Str
*/
FLAC_API const char *FLAC__stream_decoder_get_resolved_state_string(const FLAC__StreamDecoder *decoder);
/** Get the "allow Ogg chaining" flag as described in
* \code FLAC__stream_decoder_set_ogg_chaining \endcode.
*
* \param decoder A decoder instance to query.
* \assert
* \code decoder != NULL \endcode
* \retval FLAC__bool
* See above.
*/
FLAC_API FLAC__bool FLAC__stream_decoder_get_ogg_chaining(const FLAC__StreamDecoder* decoder);
/** Get the "MD5 signature checking" flag.
* This is the value of the setting, not whether or not the decoder is
* currently checking the MD5 (remember, it can be turned off automatically

Binary file not shown.

View File

@@ -96,6 +96,7 @@ typedef struct __attribute__((__packed__)) audio_buffer_entry { // decoded aud
u16_t len;
u8_t ready;
u8_t allocated;
u8_t missed;
} abuf_t;
typedef struct rtp_s {
@@ -126,11 +127,6 @@ typedef struct rtp_s {
u32_t rtp, time;
u8_t status;
} synchro;
struct {
u32_t time;
seq_t seqno;
u32_t rtptime;
} record;
int latency; // rtp hold depth in samples
u32_t resent_req, resent_rec; // total resent + recovered frames
u32_t silent_frames; // total silence frames
@@ -147,8 +143,8 @@ typedef struct rtp_s {
#endif
struct alac_codec_s *alac_codec;
int flush_seqno;
bool playing;
int first_seqno;
enum { RTP_WAIT, RTP_STREAM, RTP_PLAY } state;
int stalled;
raop_data_cb_t data_cb;
raop_cmd_cb_t cmd_cb;
@@ -231,7 +227,7 @@ rtp_resp_t rtp_init(struct in_addr host, int latency, char *aeskey, char *aesiv,
ctx->rtp_host.sin_family = AF_INET;
ctx->rtp_host.sin_addr.s_addr = INADDR_ANY;
pthread_mutex_init(&ctx->ab_mutex, 0);
ctx->flush_seqno = -1;
ctx->first_seqno = -1;
ctx->latency = latency;
ctx->ab_read = ctx->ab_write;
@@ -339,24 +335,23 @@ void rtp_end(rtp_t *ctx)
/*---------------------------------------------------------------------------*/
bool rtp_flush(rtp_t *ctx, unsigned short seqno, unsigned int rtptime, bool exit_locked)
{
bool rc = true;
u32_t now = gettime_ms();
{
pthread_mutex_lock(&ctx->ab_mutex);
// always store flush seqno as we only want stricly above it, even when equal to RECORD
ctx->first_seqno = seqno;
bool flushed = false;
if (now < ctx->record.time + 250 || (ctx->record.seqno == seqno && ctx->record.rtptime == rtptime)) {
rc = false;
LOG_ERROR("[%p]: FLUSH ignored as same as RECORD (%hu - %u)", ctx, seqno, rtptime);
} else {
pthread_mutex_lock(&ctx->ab_mutex);
buffer_reset(ctx->audio_buffer);
ctx->playing = false;
ctx->flush_seqno = seqno;
if (!exit_locked) pthread_mutex_unlock(&ctx->ab_mutex);
// no need to stop playing if recent or equal to record - but first_seqno is needed
if (ctx->state == RTP_PLAY) {
buffer_reset(ctx->audio_buffer);
ctx->state = RTP_WAIT;
flushed = true;
LOG_INFO("[%p]: FLUSH packets below %hu - %u", ctx, seqno, rtptime);
}
LOG_INFO("[%p]: flush %hu %u", ctx, seqno, rtptime);
return rc;
if (!exit_locked || !flushed) pthread_mutex_unlock(&ctx->ab_mutex);
return flushed;
}
/*---------------------------------------------------------------------------*/
@@ -367,11 +362,9 @@ void rtp_flush_release(rtp_t *ctx) {
/*---------------------------------------------------------------------------*/
void rtp_record(rtp_t *ctx, unsigned short seqno, unsigned rtptime) {
ctx->record.seqno = seqno;
ctx->record.rtptime = rtptime;
ctx->record.time = gettime_ms();
LOG_INFO("[%p]: record %hu %u", ctx, seqno, rtptime);
ctx->first_seqno = (seqno || rtptime) ? seqno : -1;
ctx->state = RTP_WAIT;
LOG_INFO("[%p]: record %hu - %u", ctx, seqno, rtptime);
}
/*---------------------------------------------------------------------------*/
@@ -442,29 +435,54 @@ static void alac_decode(rtp_t *ctx, s16_t *dest, char *buf, int len, u16_t *outs
/*---------------------------------------------------------------------------*/
static void buffer_put_packet(rtp_t *ctx, seq_t seqno, unsigned rtptime, bool first, char *data, int len) {
abuf_t *abuf = NULL;
u32_t playtime;
pthread_mutex_lock(&ctx->ab_mutex);
if (!ctx->playing) {
if ((ctx->flush_seqno == -1 || seq_order(ctx->flush_seqno, seqno)) &&
(ctx->synchro.status & RTP_SYNC) && (ctx->synchro.status & NTP_SYNC)) {
ctx->ab_write = seqno-1;
ctx->ab_read = seqno;
ctx->flush_seqno = -1;
ctx->playing = true;
ctx->resent_req = ctx->resent_rec = ctx->silent_frames = ctx->discarded = 0;
playtime = ctx->synchro.time + ((rtptime - ctx->synchro.rtp) * 10) / (RAOP_SAMPLE_RATE / 100);
ctx->cmd_cb(RAOP_PLAY, playtime);
} else {
pthread_mutex_unlock(&ctx->ab_mutex);
return;
}
}
/* if we have received a RECORD with a seqno, then this is the first allowed rtp sequence number
* and we are in RTP_WAIT state. If seqno was 0, then we are waiting for a flush that will tell
* us what should be our first allowed packet but we must accept everything, wait and clean when
* we the it arrives. This means that first packet moves us to RTP_STREAM state where we accept
* frames but wait for the FLUSH. If this was a FLUSH while playing, then we are also in RTP_WAIT
* state but we do have an allowed seqno and we should not accept any frame before we have it */
// if we have a pending first seqno and we are below, always ignore it
if (ctx->first_seqno != -1 && seq_order(seqno, ctx->first_seqno)) {
pthread_mutex_unlock(&ctx->ab_mutex);
return;
}
if (ctx->state == RTP_WAIT) {
ctx->ab_write = seqno - 1;
ctx->ab_read = ctx->ab_write + 1;
ctx->resent_req = ctx->resent_rec = ctx->silent_frames = ctx->discarded = 0;
if (ctx->first_seqno != -1) {
LOG_INFO("[%p]: 1st accepted packet:%d, now playing", ctx, seqno);
ctx->state = RTP_PLAY;
ctx->first_seqno = -1;
u32_t playtime = ctx->synchro.time + ((rtptime - ctx->synchro.rtp) * 10) / (RAOP_SAMPLE_RATE / 100);
ctx->cmd_cb(RAOP_PLAY, playtime);
} else {
ctx->state = RTP_STREAM;
LOG_INFO("[%p]: 1st accepted packet:%hu, waiting for FLUSH", ctx, seqno);
}
} else if (ctx->state == RTP_STREAM && ctx->first_seqno != -1 && seq_order(ctx->first_seqno, seqno + 1)) {
// now we're talking, but first discard all packets with a seqno below first_seqno AND not ready
while (seq_order(ctx->ab_read, ctx->first_seqno) ||
!ctx->audio_buffer[BUFIDX(ctx->ab_read)].ready) {
ctx->audio_buffer[BUFIDX(ctx->ab_read)].ready = false;
ctx->ab_read++;
}
LOG_INFO("[%p]: done waiting for FLUSH with packet:%d, now playing starting:%hu", ctx, seqno, ctx->ab_read);
ctx->state = RTP_PLAY;
ctx->first_seqno = -1;
u32_t playtime = ctx->synchro.time + ((rtptime - ctx->synchro.rtp) * 10) / (RAOP_SAMPLE_RATE / 100);
ctx->cmd_cb(RAOP_PLAY, playtime);
}
abuf = ctx->audio_buffer + BUFIDX(seqno);
if (seqno == (u16_t) (ctx->ab_write+1)) {
// expected packet
abuf = ctx->audio_buffer + BUFIDX(seqno);
ctx->ab_write = seqno;
LOG_SDEBUG("packet expected seqno:%hu rtptime:%u (W:%hu R:%hu)", seqno, rtptime, ctx->ab_write, ctx->ab_read);
} else if (seq_order(ctx->ab_write, seqno)) {
@@ -475,7 +493,7 @@ static void buffer_put_packet(rtp_t *ctx, seq_t seqno, unsigned rtptime, bool fi
ctx->ab_read = seqno;
} else {
// request re-send missed frames and evaluate resent date as a whole *after*
rtp_request_resend(ctx, ctx->ab_write + 1, seqno-1);
if (ctx->state == RTP_PLAY) rtp_request_resend(ctx, ctx->ab_write + 1, seqno-1);
// resend date is after all requests have been sent
u32_t now = gettime_ms();
@@ -488,16 +506,15 @@ static void buffer_put_packet(rtp_t *ctx, seq_t seqno, unsigned rtptime, bool fi
LOG_DEBUG("[%p]: packet newer seqno:%hu rtptime:%u (W:%hu R:%hu)", ctx, seqno, rtptime, ctx->ab_write, ctx->ab_read);
}
abuf = ctx->audio_buffer + BUFIDX(seqno);
ctx->ab_write = seqno;
} else if (seq_order(ctx->ab_read, seqno + 1)) {
// recovered packet, not yet sent
abuf = ctx->audio_buffer + BUFIDX(seqno);
ctx->resent_rec++;
LOG_DEBUG("[%p]: packet recovered seqno:%hu rtptime:%u (W:%hu R:%hu)", ctx, seqno, rtptime, ctx->ab_write, ctx->ab_read);
} else {
// too late
LOG_DEBUG("[%p]: packet too late seqno:%hu rtptime:%u (W:%hu R:%hu)", ctx, seqno, rtptime, ctx->ab_write, ctx->ab_read);
// too late
if (abuf->missed) LOG_INFO("[%p]: packet too late seqno:%hu rtptime:%u (W:%hu R:%hu)", ctx, seqno, rtptime, ctx->ab_write, ctx->ab_read);
abuf = NULL;
}
if (ctx->in_frames++ > 1000) {
@@ -508,6 +525,7 @@ static void buffer_put_packet(rtp_t *ctx, seq_t seqno, unsigned rtptime, bool fi
if (abuf) {
alac_decode(ctx, abuf->data, data, len, &abuf->len);
abuf->ready = 1;
abuf->missed = 0;
// this is the local rtptime when this frame is expected to play
abuf->rtptime = rtptime;
buffer_push_packet(ctx);
@@ -528,7 +546,7 @@ static void buffer_push_packet(rtp_t *ctx) {
u32_t now, playtime, hold = max((ctx->latency * 1000) / (8 * RAOP_SAMPLE_RATE), 100);
// not ready to play yet
if (!ctx->playing || ctx->synchro.status != (RTP_SYNC | NTP_SYNC)) return;
if (ctx->state != RTP_PLAY || ctx->synchro.status != (RTP_SYNC | NTP_SYNC)) return;
// there is always at least one frame in the buffer
do {
@@ -551,6 +569,7 @@ static void buffer_push_packet(rtp_t *ctx) {
LOG_DEBUG("[%p]: created zero frame (W:%hu R:%hu)", ctx, ctx->ab_write, ctx->ab_read);
ctx->data_cb(silence_frame, ctx->frame_size * 4, playtime);
ctx->silent_frames++;
curframe->missed = 1;
}
} else if (curframe->ready) {
ctx->data_cb((const u8_t*) curframe->data, curframe->len, playtime);

View File

@@ -22482,9 +22482,13 @@ mg_init_library(unsigned features)
file_mutex_init =
pthread_mutex_init(&global_log_file_lock, &pthread_mutex_attr);
if (file_mutex_init == 0) {
#ifdef WINSOCK_START
/* Start WinSock */
WSADATA data;
failed = wsa = WSAStartup(MAKEWORD(2, 2), &data);
#else
failed = wsa = 0;
#endif
}
#else
mutexattr_init = pthread_mutexattr_init(&pthread_mutex_attr);
@@ -22498,7 +22502,9 @@ mg_init_library(unsigned features)
if (failed) {
#if defined(_WIN32)
if (wsa == 0) {
#ifdef WINSOCK_START
(void)WSACleanup();
#endif
}
if (file_mutex_init == 0) {
(void)pthread_mutex_destroy(&global_log_file_lock);
@@ -22598,7 +22604,9 @@ mg_exit_library(void)
#endif
#if defined(_WIN32)
#ifdef WINSOCK_START
(void)WSACleanup();
#endif
(void)pthread_mutex_destroy(&global_log_file_lock);
#else
(void)pthread_mutexattr_destroy(&pthread_mutex_attr);

View File

@@ -12,6 +12,8 @@
using namespace bell;
std::mutex BellHTTPServer::initMutex;
class WebSocketHandler : public CivetWebSocketHandler {
public:
BellHTTPServer::WSDataHandler dataHandler;
@@ -187,6 +189,7 @@ bool BellHTTPServer::handlePost(CivetServer* server,
}
BellHTTPServer::BellHTTPServer(int serverPort) {
std::lock_guard lock(initMutex);
mg_init_library(0);
BELL_LOG(info, "HttpServer", "Server listening on port %d", serverPort);
this->serverPort = serverPort;
@@ -197,6 +200,11 @@ BellHTTPServer::BellHTTPServer(int serverPort) {
server = std::make_unique<CivetServer>(civetWebOptions);
}
BellHTTPServer::~BellHTTPServer() {
std::lock_guard lock(initMutex);
mg_exit_library();
}
std::unique_ptr<BellHTTPServer::HTTPResponse> BellHTTPServer::makeJsonResponse(
const std::string& json, int status) {
auto response = std::make_unique<BellHTTPServer::HTTPResponse>();

View File

@@ -19,6 +19,7 @@ namespace bell {
class BellHTTPServer : public CivetHandler {
public:
BellHTTPServer(int serverPort);
~BellHTTPServer();
enum class WSState { CONNECTED, READY, CLOSED };
@@ -100,6 +101,8 @@ class BellHTTPServer : public CivetHandler {
std::mutex responseMutex;
HTTPHandler notFoundHandler;
static std::mutex initMutex;
bool handleGet(CivetServer* server, struct mg_connection* conn);
bool handlePost(CivetServer* server, struct mg_connection* conn);
};

View File

@@ -6,6 +6,7 @@
#include <cstring>
#include <vector>
#include <mutex>
#include <atomic>
#if __has_include("avahi-client/client.h")
#include <avahi-client/client.h>
@@ -41,8 +42,9 @@ class implMDNSService : public MDNSService {
#endif
static struct mdnsd* mdnsServer;
static in_addr_t host;
static std::atomic<size_t> instances;
implMDNSService(struct mdns_service* service) : service(service){};
implMDNSService(struct mdns_service* service) : service(service){ instances++; };
#ifndef BELL_DISABLE_AVAHI
implMDNSService(AvahiEntryGroup* avahiGroup) : avahiGroup(avahiGroup){};
#endif
@@ -51,6 +53,7 @@ class implMDNSService : public MDNSService {
struct mdnsd* implMDNSService::mdnsServer = NULL;
in_addr_t implMDNSService::host = INADDR_ANY;
std::atomic<size_t> implMDNSService::instances = 0;
static std::mutex registerMutex;
#ifndef BELL_DISABLE_AVAHI
AvahiClient* implMDNSService::avahiClient = NULL;
@@ -66,11 +69,21 @@ void implMDNSService::unregisterService() {
#ifndef BELL_DISABLE_AVAHI
if (avahiGroup) {
avahi_entry_group_free(avahiGroup);
if (!--instances && implMDNSService::avahiClient) {
avahi_client_free(implMDNSService::avahiClient);
avahi_simple_poll_free(implMDNSService::avahiPoll);
implMDNSService::avahiClient = nullptr;
implMDNSService::avahiPoll = nullptr;
}
} else
#endif
{
mdns_service_remove(implMDNSService::mdnsServer, service);
}
if (!--instances && implMDNSService::mdnsServer) {
mdnsd_stop(implMDNSService::mdnsServer);
implMDNSService::mdnsServer = nullptr;
}
}
}
std::unique_ptr<MDNSService> MDNSService::registerService(
@@ -180,19 +193,14 @@ std::unique_ptr<MDNSService> MDNSService::registerService(
std::string type(serviceType + "." + serviceProto + ".local");
BELL_LOG(info, "MDNS", "using built-in mDNS for %s", serviceName.c_str());
struct mdns_service* mdnsService =
auto service =
mdnsd_register_svc(implMDNSService::mdnsServer, serviceName.c_str(),
type.c_str(), servicePort, NULL, txt.data());
if (mdnsService) {
auto service =
mdnsd_register_svc(implMDNSService::mdnsServer, serviceName.c_str(),
type.c_str(), servicePort, NULL, txt.data());
return std::make_unique<implMDNSService>(service);
}
if (service) return std::make_unique<implMDNSService>(service);
}
BELL_LOG(error, "MDNS", "cannot start any mDNS listener for %s",
serviceName.c_str());
return NULL;
return nullptr;
}

View File

@@ -19,13 +19,12 @@ using namespace bell;
class implMDNSService : public MDNSService {
private:
struct mdns_service* service;
void unregisterService(void) {
mdns_service_remove(implMDNSService::mdnsServer, service);
};
void unregisterService(void);
public:
static struct mdnsd* mdnsServer;
implMDNSService(struct mdns_service* service) : service(service){};
static std::atomic<size_t> instances;
implMDNSService(struct mdns_service* service) : service(service) { instances++; };
};
/**
@@ -33,8 +32,18 @@ class implMDNSService : public MDNSService {
**/
struct mdnsd* implMDNSService::mdnsServer = NULL;
std::atomic<size_t> implMDNSService::instances = 0;
static std::mutex registerMutex;
void implMDNSService::unregisterService() {
mdns_service_remove(implMDNSService::mdnsServer, service);
if (!--instances && implMDNSService::mdnsServer) {
mdnsd_stop(implMDNSService::mdnsServer);
implMDNSService::mdnsServer = nullptr;
}
}
std::unique_ptr<MDNSService> MDNSService::registerService(
const std::string& serviceName, const std::string& serviceType,
const std::string& serviceProto, const std::string& serviceHost,
@@ -94,5 +103,5 @@ std::unique_ptr<MDNSService> MDNSService::registerService(
mdnsd_register_svc(implMDNSService::mdnsServer, serviceName.c_str(),
type.c_str(), servicePort, NULL, txt.data());
return std::make_unique<implMDNSService>(service);
return service ? std::make_unique<implMDNSService>(service) : nullptr;
}

View File

@@ -72,7 +72,11 @@ class Task {
(LPTHREAD_START_ROUTINE)taskEntryFunc, this, 0, NULL);
return thread != NULL;
#else
return (pthread_create(&thread, NULL, taskEntryFunc, this) == 0);
if (!pthread_create(&thread, NULL, taskEntryFunc, this)) {
pthread_detach(thread);
return true;
}
return false;
#endif
}
@@ -108,13 +112,7 @@ class Task {
#endif
static void* taskEntryFunc(void* This) {
Task* self = (Task*)This;
self->runTask();
#if _WIN32
WaitForSingleObject(self->thread, INFINITE);
#else
pthread_join(self->thread, NULL);
#endif
((Task*)This)->runTask();
return NULL;
}
};

View File

@@ -24,7 +24,7 @@ class CDNAudioFile;
// Used in got track info event
struct TrackInfo {
std::string name, album, artist, imageUrl, trackId;
uint32_t duration;
uint32_t duration, number, discNumber;
void loadPbTrack(Track* pbTrack, const std::vector<uint8_t>& gid);
void loadPbEpisode(Episode* pbEpisode, const std::vector<uint8_t>& gid);

View File

@@ -32,6 +32,8 @@ message Artist {
message Track {
optional bytes gid = 1;
optional string name = 2;
optional sint32 number = 5;
optional sint32 disc_number = 6;
optional sint32 duration = 0x7;
optional Album album = 0x3;
repeated Artist artist = 0x4;
@@ -44,6 +46,7 @@ message Episode {
optional bytes gid = 1;
optional string name = 2;
optional sint32 duration = 7;
optional sint32 number = 65;
repeated AudioFile file = 12;
repeated Restriction restriction = 0x4B;
optional ImageGroup covers = 0x44;

View File

@@ -97,6 +97,8 @@ void TrackInfo::loadPbTrack(Track* pbTrack, const std::vector<uint8_t>& gid) {
}
}
number = pbTrack->has_number ? pbTrack->number : 0;
discNumber = pbTrack->has_disc_number ? pbTrack->disc_number : 0;
duration = pbTrack->duration;
}
@@ -113,6 +115,8 @@ void TrackInfo::loadPbEpisode(Episode* pbEpisode,
imageUrl = "https://i.scdn.co/image/" + bytesToHexString(imageId);
}
number = pbEpisode->has_number ? pbEpisode->number : 0;
discNumber = 0;
duration = pbEpisode->duration;
}

View File

@@ -288,6 +288,7 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
output.frames_played = 0;
output.external = DECODE_RAOP;
output.state = OUTPUT_STOPPED;
if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
LOG_INFO("resizing buffer %u", outputbuf->size);
break;
@@ -377,6 +378,7 @@ static bool cspot_cmd_handler(cspot_event_t cmd, va_list args)
output.state = OUTPUT_STOPPED;
sink_state = SINK_ABORT;
_buf_flush(outputbuf);
_buf_limit(outputbuf, 0);
if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
LOG_INFO("CSpot start track");
break;

View File

@@ -70,6 +70,7 @@ struct flac {
);
FLAC__bool (* FLAC__stream_decoder_process_single)(FLAC__StreamDecoder *decoder);
FLAC__StreamDecoderState (* FLAC__stream_decoder_get_state)(const FLAC__StreamDecoder *decoder);
FLAC__bool (*FLAC__stream_decoder_set_ogg_chaining)(FLAC__StreamDecoder* decoder, FLAC__bool allow);
#endif
};
@@ -121,6 +122,9 @@ static FLAC__StreamDecoderReadStatus read_cb(const FLAC__StreamDecoder *decoder,
_buf_inc_readp(streambuf, bytes);
UNLOCK_S;
// give some time for stream to acquire data otherwise flac will hammer us
if (!end && !bytes) usleep(100 * 1000);
*want = bytes;
return end ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
@@ -137,8 +141,8 @@ static FLAC__StreamDecoderWriteStatus write_cb(const FLAC__StreamDecoder *decode
FLAC__int32 *rptr = (FLAC__int32 *)buffer[channels > 1 ? 1 : 0];
if (decode.new_stream) {
LOG_INFO("setting track_start");
LOCK_O;
LOG_INFO("setting track_start");
output.track_start = outputbuf->writep;
decode.new_stream = false;
@@ -253,6 +257,7 @@ static void flac_open(u8_t sample_size, u8_t sample_rate, u8_t channels, u8_t en
if ( f->container == 'o' ) {
LOG_INFO("ogg/flac container - using init_ogg_stream");
FLAC(f, stream_decoder_set_ogg_chaining, f->decoder, true);
FLAC(f, stream_decoder_init_ogg_stream, f->decoder, &read_cb, NULL, NULL, NULL, NULL, &write_cb, NULL, &error_cb, NULL);
} else {
FLAC(f, stream_decoder_init_stream, f->decoder, &read_cb, NULL, NULL, NULL, NULL, &write_cb, NULL, &error_cb, NULL);
@@ -295,6 +300,7 @@ static bool load_flac() {
f->FLAC__stream_decoder_init_ogg_stream = dlsym(handle, "FLAC__stream_decoder_init_ogg_stream");
f->FLAC__stream_decoder_process_single = dlsym(handle, "FLAC__stream_decoder_process_single");
f->FLAC__stream_decoder_get_state = dlsym(handle, "FLAC__stream_decoder_get_state");
f->FLAC__stream_decoder_set_ogg_chaining = dlsym(handle, "FLAC__stream_decoder_set_ogg_chaining");
if ((err = dlerror()) != NULL) {
LOG_INFO("dlerror: %s", err);

View File

@@ -194,7 +194,7 @@ static int read_opus_header(void) {
// nothing has been found and we have no more bytes, come back later
if (status <= 0) break;
// always set stream serialno if we have a new one
// always set stream serialno if we have a new one (no multiplexed streams)
if (OG(&go, page_bos, &u->page)) OG(&go, stream_reset_serialno, &u->state, OG(&go, page_serialno, &u->page));
// bring new page in if we want it (otherwise we're just skipping)

View File

@@ -60,7 +60,7 @@ frames_t _output_frames(frames_t avail) {
silence = false;
// start when threshold met
if (output.state == OUTPUT_BUFFER && (frames * BYTES_PER_FRAME) > output.threshold * output.next_sample_rate / 10 && frames > output.start_frames) {
if (output.state == OUTPUT_BUFFER && frames > output.threshold * output.next_sample_rate / 10 && frames > output.start_frames) {
output.state = OUTPUT_RUNNING;
LOG_INFO("start buffer frames: %u", frames);
wake_controller();

View File

@@ -139,8 +139,11 @@ static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t g
u8_t *buf = silencebuf;
memcpy(btout + oframes * BYTES_PER_FRAME, buf, out_frames * BYTES_PER_FRAME);
}
output_visu_export(btout + oframes * BYTES_PER_FRAME, out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
// don't update visu if we don't have enough data in buffer (500 ms)
if (silence || _buf_used(outputbuf) > BYTES_PER_FRAME * output.current_sample_rate / 2) {
output_visu_export(btout + oframes * BYTES_PER_FRAME, out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
}
oframes += out_frames;

View File

@@ -439,7 +439,7 @@ void output_init_i2s(log_level level, char *device, unsigned output_buf_size, ch
static DRAM_ATTR StaticTask_t xTaskBuffer __attribute__ ((aligned (4)));
static EXT_RAM_ATTR StackType_t xStack[OUTPUT_THREAD_STACK_SIZE] __attribute__ ((aligned (4)));
output_i2s_task = xTaskCreateStaticPinnedToCore( (TaskFunction_t) output_thread_i2s, "output_i2s", OUTPUT_THREAD_STACK_SIZE,
NULL, CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1, xStack, &xTaskBuffer, 0 );
NULL, CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 10, xStack, &xTaskBuffer, 0 );
}
}
@@ -485,8 +485,8 @@ static int _i2s_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32
memcpy(obuf + oframes * BYTES_PER_FRAME, silencebuf, out_frames * BYTES_PER_FRAME);
}
// don't update visu if we don't have enough data in buffer
if (silence || output.external || _buf_used(outputbuf) > outputbuf->size >> 2 ) {
// don't update visu if we don't have enough data in buffer (500 ms)
if (silence || _buf_used(outputbuf) > BYTES_PER_FRAME * output.current_sample_rate / 2) {
output_visu_export(obuf + oframes * BYTES_PER_FRAME, out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
}

View File

@@ -393,7 +393,9 @@ static void process_strm(u8_t *pkt, int len) {
stream_file(header, header_len, strm->threshold * 1024);
autostart -= 2;
} else {
stream_sock(ip, port, strm->format, header, header_len, strm->threshold * 1024, autostart >= 2);
stream_sock(ip, port, strm->flags & 0x20,
strm->format == 'o' || strm->format == 'u' || (strm->format == 'f' && strm->pcm_sample_size == 'o'),
header, header_len, strm->threshold * 1024, autostart >= 2);
}
sendSTAT("STMc", 0);
sentSTMu = sentSTMo = sentSTMl = false;
@@ -412,8 +414,8 @@ static void process_strm(u8_t *pkt, int len) {
output.fade_secs = strm->transition_period;
output.invert = (strm->flags & 0x03) == 0x03;
output.channels = (strm->flags & 0x0c) >> 2;
UNLOCK_O;
LOG_DEBUG("set fade: %u, channels: %u, invert: %u", output.fade_mode, output.channels, output.invert);
UNLOCK_O;
}
break;
default:

View File

@@ -580,26 +580,12 @@ struct streamstate {
u32_t meta_next;
u32_t meta_left;
bool meta_send;
struct {
enum { STREAM_OGG_OFF, STREAM_OGG_SYNC, STREAM_OGG_HEADER, STREAM_OGG_SEGMENTS, STREAM_OGG_PAGE } state;
u32_t want, miss, match;
u8_t* data, segments[255];
#pragma pack(push, 1)
struct {
char pattern[4];
u8_t version, type;
u64_t granule;
u32_t serial, page, checksum;
u8_t count;
} header;
} ogg;
#pragma pack(pop)
};
void stream_init(log_level level, unsigned stream_buf_size);
void stream_close(void);
void stream_file(const char *header, size_t header_len, unsigned threshold);
void stream_sock(u32_t ip, u16_t port, char codec, const char *header, size_t header_len, unsigned threshold, bool cont_wait);
void stream_sock(u32_t ip, u16_t port, bool use_ssl, bool use_ogg, const char *header, size_t header_len, unsigned threshold, bool cont_wait);
bool stream_disconnect(void);
// decode.c

View File

@@ -61,6 +61,23 @@ static sockfd fd;
struct EXT_RAM_ATTR streamstate stream;
static EXT_RAM_ATTR struct {
bool flac;
u64_t serial;
enum { OGG_OFF, OGG_SYNC, OGG_HEADER, OGG_SEGMENTS, OGG_PAGE } state;
size_t want, miss, match;
u8_t* data, segments[255];
#pragma pack(push, 1)
struct {
char pattern[4];
u8_t version, type;
u64_t granule;
u32_t serial, page, checksum;
u8_t count;
} header;
#pragma pack(pop)
} ogg;
#if USE_SSL
static SSL_CTX *SSLctx;
SSL *ssl;
@@ -113,7 +130,6 @@ static int _poll(SSL *ssl, struct pollfd *pollinfo, int timeout) {
}
#endif
static bool send_header(void) {
char *ptr = stream.header;
int len = stream.header_len;
@@ -148,8 +164,8 @@ static bool running = true;
static void _disconnect(stream_state state, disconnect_code disconnect) {
stream.state = state;
stream.disconnect = disconnect;
if (stream.ogg.state == STREAM_OGG_PAGE && stream.ogg.data) free(stream.ogg.data);
stream.ogg.data = NULL;
if (ogg.state == OGG_PAGE && ogg.data) free(ogg.data);
ogg.data = NULL;
#if USE_SSL
if (ssl) {
SSL_shutdown(ssl);
@@ -162,81 +178,96 @@ static void _disconnect(stream_state state, disconnect_code disconnect) {
wake_controller();
}
static u32_t memfind(const u8_t* haystack, u32_t n, const char* needle, u32_t len, u32_t *offset) {
int i;
static size_t memfind(const u8_t* haystack, size_t n, const char* needle, size_t len, size_t* offset) {
size_t i;
for (i = 0; i < n && *offset != len; i++) *offset = (haystack[i] == needle[*offset]) ? *offset + 1 : 0;
return i;
}
/* https://xiph.org/ogg/doc/framing.html
* https://xiph.org/flac/ogg_mapping.html
* https://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-610004.2 */
static void stream_ogg(size_t n) {
if (stream.ogg.state == STREAM_OGG_OFF) return;
if (ogg.state == OGG_OFF) return;
u8_t* p = streambuf->writep;
while (n) {
size_t consumed = min(stream.ogg.miss, n);
size_t consumed = min(ogg.miss, n);
// copy as many bytes as possible and come back later if we do'nt have enough
if (stream.ogg.data) {
memcpy(stream.ogg.data + stream.ogg.want - stream.ogg.miss, p, consumed);
stream.ogg.miss -= consumed;
if (stream.ogg.miss) return;
if (ogg.data) {
memcpy(ogg.data + ogg.want - ogg.miss, p, consumed);
ogg.miss -= consumed;
if (ogg.miss) return;
}
// we have what we want, let's parse
switch (stream.ogg.state) {
case STREAM_OGG_SYNC: {
stream.ogg.miss -= consumed;
switch (ogg.state) {
case OGG_SYNC: {
ogg.miss -= consumed;
if (consumed) break;
// we have to memorize position in case any of last 3 bytes match...
int pos = memfind(p, n, "OggS", 4, &stream.ogg.match);
if (stream.ogg.match == 4) {
consumed = pos - stream.ogg.match;
stream.ogg.state = STREAM_OGG_HEADER;
stream.ogg.miss = stream.ogg.want = sizeof(stream.ogg.header);
stream.ogg.data = (u8_t*) &stream.ogg.header;
stream.ogg.match = 0;
size_t pos = memfind(p, n, "OggS", 4, &ogg.match);
if (ogg.match == 4) {
consumed = pos - ogg.match;
ogg.state = OGG_HEADER;
ogg.miss = ogg.want = sizeof(ogg.header);
ogg.data = (u8_t*) &ogg.header;
ogg.match = 0;
} else {
LOG_INFO("OggS not at expected position");
if (!ogg.match) LOG_INFO("OggS not at expected position %zu/%zu", pos, n);
LOG_INFO("OggS not at expected position %zu/%zu", pos, n);
return;
}
break;
}
case STREAM_OGG_HEADER:
if (!memcmp(stream.ogg.header.pattern, "OggS", 4)) {
stream.ogg.miss = stream.ogg.want = stream.ogg.header.count;
stream.ogg.data = stream.ogg.segments;
stream.ogg.state = STREAM_OGG_SEGMENTS;
case OGG_HEADER:
if (!memcmp(ogg.header.pattern, "OggS", 4)) {
ogg.miss = ogg.want = ogg.header.count;
ogg.data = ogg.segments;
ogg.state = OGG_SEGMENTS;
} else {
stream.ogg.state = STREAM_OGG_SYNC;
stream.ogg.data = NULL;
ogg.state = OGG_SYNC;
ogg.data = NULL;
}
break;
case STREAM_OGG_SEGMENTS:
case OGG_SEGMENTS:
// calculate size of page using lacing values
for (int i = 0; i < stream.ogg.want; i++) stream.ogg.miss += stream.ogg.data[i];
stream.ogg.want = stream.ogg.miss;
for (size_t i = 0; i < ogg.want; i++) ogg.miss += ogg.data[i];
ogg.want = ogg.miss;
// acquire serial number when we are looking for headers and hit a bos
if (ogg.serial == ULLONG_MAX && (ogg.header.type & 0x02)) ogg.serial = ogg.header.serial;
if (stream.ogg.header.granule == 0) {
// granule 0 means a new stream, so let's look into it
stream.ogg.state = STREAM_OGG_PAGE;
stream.ogg.data = malloc(stream.ogg.want);
} else {
// we have overshot and missed header, reset serial number to restart search (O and -1 are le/be)
if (ogg.header.serial == ogg.serial && ogg.header.granule && ogg.header.granule != -1) ogg.serial = ULLONG_MAX;
// not our serial (the above protected us from granule > 0)
if (ogg.header.serial != ogg.serial) {
// otherwise, jump over data
stream.ogg.state = STREAM_OGG_SYNC;
stream.ogg.data = NULL;
ogg.state = OGG_SYNC;
ogg.data = NULL;
} else {
ogg.state = OGG_PAGE;
ogg.data = malloc(ogg.want);
}
break;
case STREAM_OGG_PAGE: {
u32_t offset = 0;
case OGG_PAGE: {
char** tag = (char* []){ "\x3vorbis", "OpusTags", NULL };
size_t ofs = 0;
// try to find one of valid Ogg pattern (vorbis, opus)
for (char** tag = (char*[]) { "\x3vorbis", "OpusTags", NULL }; *tag; tag++, offset = 0) {
u32_t pos = memfind(stream.ogg.data, stream.ogg.want, *tag, strlen(*tag), &offset);
if (offset != strlen(*tag)) continue;
/* with OggFlac, we need the next page (packet) - VorbisComment is wrapped into a FLAC_METADATA
* and except with vorbis, comment packet starts a new page but even in vorbis, it won't span
* accross multiple pages */
if (ogg.flac) ofs = 4;
else if (!memcmp(ogg.data, "\x7f""FLAC", 5)) ogg.flac = true;
else for (size_t n = 0; *tag; tag++, ofs = 0) if ((ofs = memfind(ogg.data, ogg.want, *tag, strlen(*tag), &n)) && n == strlen(*tag)) break;
if (ofs) {
// u32:len,char[]:vendorId, u32:N, N x (u32:len,char[]:comment)
char* p = (char*) stream.ogg.data + pos;
char* p = (char*) ogg.data + ofs;
p += *p + 4;
u32_t count = *p;
p += 4;
@@ -256,16 +287,19 @@ static void stream_ogg(size_t n) {
stream.header[stream.header_len++] = len;
memcpy(stream.header + stream.header_len, p, len);
stream.header_len += len;
LOG_INFO("metadata: %.*s", len, p);
}
}
ogg.flac = false;
ogg.serial = ULLONG_MAX;
stream.meta_send = true;
wake_controller();
LOG_INFO("Ogg metadata length: %u", stream.header_len - 3);
break;
}
free(stream.ogg.data);
stream.ogg.state = STREAM_OGG_SYNC;
free(ogg.data);
ogg.data = NULL;
ogg.state = OGG_SYNC;
break;
}
default:
@@ -603,7 +637,7 @@ void stream_file(const char *header, size_t header_len, unsigned threshold) {
UNLOCK;
}
void stream_sock(u32_t ip, u16_t port, char codec, const char *header, size_t header_len, unsigned threshold, bool cont_wait) {
void stream_sock(u32_t ip, u16_t port, bool use_ssl, bool use_ogg, const char *header, size_t header_len, unsigned threshold, bool cont_wait) {
struct sockaddr_in addr;
#if EMBEDDED
@@ -703,8 +737,10 @@ void stream_sock(u32_t ip, u16_t port, char codec, const char *header, size_t he
stream.bytes = 0;
stream.threshold = threshold;
stream.ogg.miss = stream.ogg.match = 0;
stream.ogg.state = (codec == 'o' || codec == 'p') ? STREAM_OGG_SYNC : STREAM_OGG_OFF;
ogg.miss = ogg.match = 0;
ogg.state = use_ogg ? OGG_SYNC : OGG_OFF;
ogg.flac = false;
ogg.serial = ULLONG_MAX;
UNLOCK;
}
@@ -725,8 +761,8 @@ bool stream_disconnect(void) {
disc = true;
}
stream.state = STOPPED;
if (stream.ogg.state == STREAM_OGG_PAGE && stream.ogg.data) free(stream.ogg.data);
stream.ogg.data = NULL;
if (ogg.state == OGG_PAGE && ogg.data) free(ogg.data);
ogg.data = NULL;
UNLOCK;
return disc;
}

View File

@@ -201,7 +201,7 @@ static int read_vorbis_header(void) {
// nothing has been found and we have no more bytes, come back later
if (status <= 0) break;
// always set stream serialno if we have a new one
// always set stream serialno if we have a new one (no multiplexed streams)
if (OG(&go, page_bos, &v->page)) OG(&go, stream_reset_serialno, &v->state, OG(&go, page_serialno, &v->page));
// bring new page in if we want it (otherwise we're just skipping)

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

View File

@@ -73,6 +73,12 @@ declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getStatus(): {};
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
declare function getRadioButton(entry: any): string;
@@ -223,6 +229,9 @@ declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare let sd: {};
declare let rf: boolean;
declare function refreshStatus(): void;

View File

@@ -1,5 +1,5 @@
target_add_binary_data( __idf_wifi-manager webapp/dist/css/index.4bbe29a78a667faa2b6f.css.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/favicon-32x32.png BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/index.html.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/index.105fd5.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/node_vendors.105fd5.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/index.4ae048.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/node_vendors.4ae048.bundle.js.gz BINARY)

View File

@@ -6,29 +6,29 @@ extern const uint8_t _favicon_32x32_png_start[] asm("_binary_favicon_32x32_png_s
extern const uint8_t _favicon_32x32_png_end[] asm("_binary_favicon_32x32_png_end");
extern const uint8_t _index_html_gz_start[] asm("_binary_index_html_gz_start");
extern const uint8_t _index_html_gz_end[] asm("_binary_index_html_gz_end");
extern const uint8_t _index_105fd5_bundle_js_gz_start[] asm("_binary_index_105fd5_bundle_js_gz_start");
extern const uint8_t _index_105fd5_bundle_js_gz_end[] asm("_binary_index_105fd5_bundle_js_gz_end");
extern const uint8_t _node_vendors_105fd5_bundle_js_gz_start[] asm("_binary_node_vendors_105fd5_bundle_js_gz_start");
extern const uint8_t _node_vendors_105fd5_bundle_js_gz_end[] asm("_binary_node_vendors_105fd5_bundle_js_gz_end");
extern const uint8_t _index_4ae048_bundle_js_gz_start[] asm("_binary_index_4ae048_bundle_js_gz_start");
extern const uint8_t _index_4ae048_bundle_js_gz_end[] asm("_binary_index_4ae048_bundle_js_gz_end");
extern const uint8_t _node_vendors_4ae048_bundle_js_gz_start[] asm("_binary_node_vendors_4ae048_bundle_js_gz_start");
extern const uint8_t _node_vendors_4ae048_bundle_js_gz_end[] asm("_binary_node_vendors_4ae048_bundle_js_gz_end");
const char * resource_lookups[] = {
"/css/index.4bbe29a78a667faa2b6f.css.gz",
"/favicon-32x32.png",
"/index.html.gz",
"/js/index.105fd5.bundle.js.gz",
"/js/node_vendors.105fd5.bundle.js.gz",
"/js/index.4ae048.bundle.js.gz",
"/js/node_vendors.4ae048.bundle.js.gz",
""
};
const uint8_t * resource_map_start[] = {
_index_4bbe29a78a667faa2b6f_css_gz_start,
_favicon_32x32_png_start,
_index_html_gz_start,
_index_105fd5_bundle_js_gz_start,
_node_vendors_105fd5_bundle_js_gz_start
_index_4ae048_bundle_js_gz_start,
_node_vendors_4ae048_bundle_js_gz_start
};
const uint8_t * resource_map_end[] = {
_index_4bbe29a78a667faa2b6f_css_gz_end,
_favicon_32x32_png_end,
_index_html_gz_end,
_index_105fd5_bundle_js_gz_end,
_node_vendors_105fd5_bundle_js_gz_end
_index_4ae048_bundle_js_gz_end,
_node_vendors_4ae048_bundle_js_gz_end
};

View File

@@ -1,6 +1,6 @@
/***********************************
webpack_headers
dist/css/index.4bbe29a78a667faa2b6f.css.gz,dist/favicon-32x32.png,dist/index.html.gz,dist/js/index.105fd5.bundle.js.gz,dist/js/node_vendors.105fd5.bundle.js.gz
dist/css/index.4bbe29a78a667faa2b6f.css.gz,dist/favicon-32x32.png,dist/index.html.gz,dist/js/index.4ae048.bundle.js.gz,dist/js/node_vendors.4ae048.bundle.js.gz
***********************************/
#pragma once
#include <inttypes.h>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
server_certs/r2m01.cer.44 Normal file

Binary file not shown.

BIN
server_certs/r2m01.cer.45 Normal file

Binary file not shown.

BIN
server_certs/r2m01.cer.46 Normal file

Binary file not shown.

BIN
server_certs/r2m01.cer.47 Normal file

Binary file not shown.

BIN
server_certs/r2m01.cer.48 Normal file

Binary file not shown.

BIN
server_certs/r2m01.cer.49 Normal file

Binary file not shown.

BIN
server_certs/r2m01.cer.50 Normal file

Binary file not shown.