Compare commits

...

12 Commits

Author SHA1 Message Date
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
44 changed files with 221 additions and 116 deletions

View File

@@ -1,3 +1,15 @@
2024-01-16
- catch-up with cspot latest
- refactor airplay flush/first packet
- new libFLAC that supports multi-stream OggFlac
- fix output threshold
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

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)**

Binary file not shown.

View File

@@ -126,11 +126,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 +142,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 +226,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 +334,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 +361,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,26 +434,50 @@ 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);
}
if (seqno == (u16_t) (ctx->ab_write+1)) {
// expected packet
abuf = ctx->audio_buffer + BUFIDX(seqno);
@@ -475,7 +491,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();
@@ -528,7 +544,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 {

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

@@ -121,6 +121,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;

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

@@ -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;

View File

@@ -585,7 +585,7 @@ struct streamstate {
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

@@ -62,8 +62,10 @@ static sockfd fd;
struct EXT_RAM_ATTR streamstate stream;
static EXT_RAM_ATTR struct {
bool flac;
enum { OGG_OFF, OGG_SYNC, OGG_HEADER, OGG_SEGMENTS, OGG_PAGE } state;
u32_t want, miss, match;
size_t want, miss, match;
u64_t granule;
u8_t* data, segments[255];
#pragma pack(push, 1)
struct {
@@ -128,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;
@@ -177,12 +178,16 @@ 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 (ogg.state == OGG_OFF) return;
u8_t* p = streambuf->writep;
@@ -204,7 +209,7 @@ static void stream_ogg(size_t n) {
if (consumed) break;
// we have to memorize position in case any of last 3 bytes match...
int pos = memfind(p, n, "OggS", 4, &ogg.match);
size_t pos = memfind(p, n, "OggS", 4, &ogg.match);
if (ogg.match == 4) {
consumed = pos - ogg.match;
ogg.state = OGG_HEADER;
@@ -212,7 +217,8 @@ static void stream_ogg(size_t n) {
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;
@@ -229,10 +235,10 @@ static void stream_ogg(size_t n) {
break;
case OGG_SEGMENTS:
// calculate size of page using lacing values
for (int i = 0; i < ogg.want; i++) ogg.miss += ogg.data[i];
for (size_t i = 0; i < ogg.want; i++) ogg.miss += ogg.data[i];
ogg.want = ogg.miss;
if (ogg.header.granule == 0) {
if (ogg.header.granule == 0 || (ogg.header.granule == -1 && ogg.granule == 0)) {
// granule 0 means a new stream, so let's look into it
ogg.state = OGG_PAGE;
ogg.data = malloc(ogg.want);
@@ -241,17 +247,24 @@ static void stream_ogg(size_t n) {
ogg.state = OGG_SYNC;
ogg.data = NULL;
}
// memorize granule for next page
if (ogg.header.granule != -1) ogg.granule = ogg.header.granule;
break;
case OGG_PAGE: {
u32_t offset = 0;
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(ogg.data, 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*) ogg.data + pos;
char* p = (char*) ogg.data + ofs;
p += *p + 4;
u32_t count = *p;
p += 4;
@@ -271,15 +284,17 @@ 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;
stream.meta_send = true;
wake_controller();
LOG_INFO("Ogg metadata length: %u", stream.header_len - 3);
break;
}
free(ogg.data);
ogg.data = NULL;
ogg.state = OGG_SYNC;
break;
}
@@ -618,7 +633,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
@@ -719,7 +734,8 @@ void stream_sock(u32_t ip, u16_t port, char codec, const char *header, size_t he
stream.threshold = threshold;
ogg.miss = ogg.match = 0;
ogg.state = (codec == 'o' || codec == 'p') ? OGG_SYNC : OGG_OFF;
ogg.state = use_ogg ? OGG_SYNC : OGG_OFF;
ogg.flac = false;
UNLOCK;
}

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

View File

@@ -74,6 +74,10 @@ 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;
@@ -226,6 +230,8 @@ 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.c8ae2b.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/node_vendors.c8ae2b.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_c8ae2b_bundle_js_gz_start[] asm("_binary_index_c8ae2b_bundle_js_gz_start");
extern const uint8_t _index_c8ae2b_bundle_js_gz_end[] asm("_binary_index_c8ae2b_bundle_js_gz_end");
extern const uint8_t _node_vendors_c8ae2b_bundle_js_gz_start[] asm("_binary_node_vendors_c8ae2b_bundle_js_gz_start");
extern const uint8_t _node_vendors_c8ae2b_bundle_js_gz_end[] asm("_binary_node_vendors_c8ae2b_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.c8ae2b.bundle.js.gz",
"/js/node_vendors.c8ae2b.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_c8ae2b_bundle_js_gz_start,
_node_vendors_c8ae2b_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_c8ae2b_bundle_js_gz_end,
_node_vendors_c8ae2b_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.c8ae2b.bundle.js.gz,dist/js/node_vendors.c8ae2b.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.

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.