Compare commits

..

10 Commits

Author SHA1 Message Date
philippe44
be7fb4b669 Merge branch 'master-v4.3' of https://github.com/sle118/squeezelite-esp32 into master-v4.3 2023-12-31 18:50:29 -08:00
philippe44
3554af1460 add vorbis/ogg live metadata (and fix some ogg issues) - release 2023-12-31 18:50:25 -08:00
Sébastien
02d422c1f4 update workflow dependency 2023-12-28 09:11:46 -05:00
github-actions
343b728323 Update prebuilt objects [skip actions] 2023-12-28 13:56:07 +00:00
philippe44
9679c5c104 fix flush mode 2023-12-27 19:46:16 -08:00
philippe44
5c90086bbd sync with upstream/cspot 2023-11-21 12:52:55 -08:00
philippe44
807a0e547a airplay improvements 2023-11-19 22:57:21 -08:00
philippe44
1d54331224 Merge branch 'master-v4.3' of https://github.com/sle118/squeezelite-esp32 into master-v4.3 2023-11-16 22:52:23 -08:00
philippe44
e85a3cddf1 GDS optimization 2023-11-16 22:52:18 -08:00
github-actions
d4d97d5a60 Update prebuilt objects [skip actions] 2023-11-17 05:45:59 +00:00
52 changed files with 493 additions and 285 deletions

View File

@@ -88,7 +88,7 @@ jobs:
git commit -m "Update prebuilt objects [skip actions]"
git push https://${{secrets.github_token}}@github.com/sle118/squeezelite-esp32.git
- name: Locally store commonly built objects
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: prebuilt_objects
path: |
@@ -130,7 +130,7 @@ jobs:
git status
build_tools.py environment --build ${{ needs.bootstrap.outputs.build_number }} --env_file "$GITHUB_ENV" --node "${{matrix.node}}" --depth ${{matrix.depth}} --major 2 --docker sle118/squeezelite-esp32-idfv435
- uses: actions/download-artifact@master
- uses: actions/download-artifact@v4
name: Restore common objects
with:
name: prebuilt_objects

View File

@@ -1,3 +1,11 @@
2023-11-19
- more robust (?) airplay RTP frame recovery
- initialize of scratch string in monitor (trying to figure out random reboot)
2023-11-16
- add SH1122 support
- optimize GDS DrawPixel function
2023-11-09
- force gpio_pad_select_gpio in dac_controlset in case somebody uses UART gpio's (or other pre-programmed)

View File

@@ -88,11 +88,6 @@ static void Update( struct GDS_Device* Device ) {
#endif
}
static void DrawPixelFast4( struct GDS_Device* Device, int X, int Y, int Color ) {
uint8_t* FBOffset = Device->Framebuffer + ( (Y * Device->Width >> 1) + (X >> 1));
*FBOffset = X & 0x01 ? ((*FBOffset & 0xf0) | (Color & 0x0f)) : (*FBOffset & 0x0f) | ((Color & 0x0f) << 4);
}
static void SetLayout( struct GDS_Device* Device, struct GDS_Layout *Layout ) {
if (Layout->HFlip) {
Device->WriteCommand( Device, 0x40 + 0x20 );
@@ -165,7 +160,6 @@ static bool Init( struct GDS_Device* Device ) {
static const struct GDS_Device SH1122 = {
.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
.DrawPixelFast = DrawPixelFast4,
.SetLayout = SetLayout,
.Update = Update, .Init = Init,
.Mode = GDS_GRAYSCALE, .Depth = 4,

View File

@@ -109,7 +109,7 @@ void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2,
int c = x1;
// for a row that is not on a boundary, no optimization possible
while (r & 0x07 && r <= y2) {
for (c = x1; c <= x2; c++) DrawPixelFast( Device, c, r, Color );
for (c = x1; c <= x2; c++) Device->DrawPixelFast( Device, c, r, Color );
r++;
}
// go fast if we have more than 8 lines to write
@@ -117,7 +117,7 @@ void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2,
memset(optr + Width * r + x1, _Color, x2 - x1 + 1);
r += 8;
} else while (r <= y2) {
for (c = x1; c <= x2; c++) DrawPixelFast( Device, c, r, Color );
for (c = x1; c <= x2; c++) Device->DrawPixelFast( Device, c, r, Color );
r++;
}
}
@@ -133,10 +133,10 @@ void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2,
// try to do byte processing as much as possible
for (int r = y1; r <= y2; r++) {
int c = x1;
if (c & 0x01) DrawPixelFast( Device, c++, r, Color);
if (c & 0x01) Device->DrawPixelFast( Device, c++, r, Color);
int chunk = (x2 - c + 1) >> 1;
memset(optr + ((r * Width + c) >> 1), _Color, chunk);
if (c + chunk <= x2) DrawPixelFast( Device, x2, r, Color);
if (c + chunk <= x2) Device->DrawPixelFast( Device, x2, r, Color);
}
}
} else if (Device->Depth == 8) {
@@ -148,7 +148,7 @@ void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2,
} else {
for (int y = y1; y <= y2; y++) {
for (int x = x1; x <= x2; x++) {
DrawPixelFast( Device, x, y, Color);
Device->DrawPixelFast( Device, x, y, Color);
}
}
}
@@ -171,10 +171,76 @@ bool GDS_Reset( struct GDS_Device* Device ) {
return true;
}
static void IRAM_ATTR DrawPixel1Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
uint32_t YBit = ( Y & 0x07 );
uint8_t* FBOffset;
/*
* We only need to modify the Y coordinate since the pitch
* of the screen is the same as the width.
* Dividing Y by 8 gives us which row the pixel is in but not
* the bit position.
*/
Y>>= 3;
FBOffset = Device->Framebuffer + ( ( Y * Device->Width ) + X );
if ( Color == GDS_COLOR_XOR ) {
*FBOffset ^= BIT( YBit );
} else {
*FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( YBit ) : *FBOffset | BIT( YBit );
}
}
static void IRAM_ATTR DrawPixel4Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
uint8_t* FBOffset = Device->Framebuffer + ( (Y * Device->Width >> 1) + (X >> 1));
*FBOffset = X & 0x01 ? (*FBOffset & 0x0f) | ((Color & 0x0f) << 4) : ((*FBOffset & 0xf0) | (Color & 0x0f));
}
static void IRAM_ATTR DrawPixel4FastHigh( struct GDS_Device* Device, int X, int Y, int Color ) {
uint8_t* FBOffset = Device->Framebuffer + ( (Y * Device->Width >> 1) + (X >> 1));
*FBOffset = X & 0x01 ? ((*FBOffset & 0xf0) | (Color & 0x0f)) : (*FBOffset & 0x0f) | ((Color & 0x0f) << 4);
}
static void IRAM_ATTR DrawPixel8Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
Device->Framebuffer[Y * Device->Width + X] = Color;
}
// assumes that Color is 16 bits R..RG..GB..B from MSB to LSB and FB wants 1st serialized byte to start with R
static void IRAM_ATTR DrawPixel16Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
uint16_t* FBOffset = (uint16_t*) Device->Framebuffer + Y * Device->Width + X;
*FBOffset = __builtin_bswap16(Color);
}
// assumes that Color is 18 bits RGB from MSB to LSB RRRRRRGGGGGGBBBBBB, so byte[0] is B
// FB is 3-bytes packets and starts with R for serialization so 0,1,2 ... = xxRRRRRR xxGGGGGG xxBBBBBB
static void IRAM_ATTR DrawPixel18Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
uint8_t* FBOffset = Device->Framebuffer + (Y * Device->Width + X) * 3;
*FBOffset++ = Color >> 12; *FBOffset++ = (Color >> 6) & 0x3f; *FBOffset = Color & 0x3f;
}
// assumes that Color is 24 bits RGB from MSB to LSB RRRRRRRRGGGGGGGGBBBBBBBB, so byte[0] is B
// FB is 3-bytes packets and starts with R for serialization so 0,1,2 ... = RRRRRRRR GGGGGGGG BBBBBBBB
static void IRAM_ATTR DrawPixel24Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
uint8_t* FBOffset = Device->Framebuffer + (Y * Device->Width + X) * 3;
*FBOffset++ = Color >> 16; *FBOffset++ = Color >> 8; *FBOffset = Color;
}
bool GDS_Init( struct GDS_Device* Device ) {
if (Device->Depth > 8) Device->FramebufferSize = Device->Width * Device->Height * ((8 + Device->Depth - 1) / 8);
else Device->FramebufferSize = (Device->Width * Device->Height) / (8 / Device->Depth);
// set the proper DrawPixel function if not already set by driver
if (!Device->DrawPixelFast) {
if (Device->Depth == 1) Device->DrawPixelFast = DrawPixel1Fast;
else if (Device->Depth == 4 && Device->HighNibble) Device->DrawPixelFast = DrawPixel4FastHigh;
else if (Device->Depth == 4) Device->DrawPixelFast = DrawPixel4Fast;
else if (Device->Depth == 8) Device->DrawPixelFast = DrawPixel8Fast;
else if (Device->Depth == 16) Device->DrawPixelFast = DrawPixel16Fast;
else if (Device->Depth == 24 && Device->Mode == GDS_RGB666) Device->DrawPixelFast = DrawPixel18Fast;
else if (Device->Depth == 24 && Device->Mode == GDS_RGB888) Device->DrawPixelFast = DrawPixel24Fast;
}
// allocate FB unless explicitely asked not to
if (!(Device->Alloc & GDS_ALLOC_NONE)) {

View File

@@ -45,7 +45,7 @@ __attribute__( ( always_inline ) ) static inline void SwapInt( int* a, int* b )
}
void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) {
DrawPixelFast( Device, X, Y, Color );
Device->DrawPixelFast( Device, X, Y, Color );
}
void IRAM_ATTR GDS_DrawPixel( struct GDS_Device* Device, int X, int Y, int Color ) {
@@ -63,7 +63,7 @@ void GDS_DrawHLine( struct GDS_Device* Device, int x, int y, int Width, int Colo
if (y < 0) y = 0;
else if (y >= Device->Height) y = Device->Height - 1;
for ( ; x < XEnd; x++ ) DrawPixelFast( Device, x, y, Color );
for ( ; x < XEnd; x++ ) Device->DrawPixelFast( Device, x, y, Color );
}
void GDS_DrawVLine( struct GDS_Device* Device, int x, int y, int Height, int Color ) {
@@ -97,7 +97,7 @@ static inline void DrawWideLine( struct GDS_Device* Device, int x0, int y0, int
for ( ; x < x1; x++ ) {
if ( IsPixelVisible( Device, x, y ) == true ) {
DrawPixelFast( Device, x, y, Color );
Device->DrawPixelFast( Device, x, y, Color );
}
if ( Error > 0 ) {
@@ -126,7 +126,7 @@ static inline void DrawTallLine( struct GDS_Device* Device, int x0, int y0, int
for ( ; y < y1; y++ ) {
if ( IsPixelVisible( Device, x, y ) == true ) {
DrawPixelFast( Device, x, y, Color );
Device->DrawPixelFast( Device, x, y, Color );
}
if ( Error > 0 ) {
@@ -348,14 +348,14 @@ void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int
// don't know bitdepth, use brute-force solution
for (int i = Width * Height, r = 0, c = 0; --i >= 0;) {
uint8_t Byte = *Data++;
DrawPixelFast( Device, c, (r << 3) + 7, (Byte & 0x01) * Color ); Byte >>= 1;
DrawPixelFast( Device, c, (r << 3) + 6, (Byte & 0x01) * Color ); Byte >>= 1;
DrawPixelFast( Device, c, (r << 3) + 5, (Byte & 0x01) * Color ); Byte >>= 1;
DrawPixelFast( Device, c, (r << 3) + 4, (Byte & 0x01) * Color ); Byte >>= 1;
DrawPixelFast( Device, c, (r << 3) + 3, (Byte & 0x01) * Color ); Byte >>= 1;
DrawPixelFast( Device, c, (r << 3) + 2, (Byte & 0x01) * Color ); Byte >>= 1;
DrawPixelFast( Device, c, (r << 3) + 1, (Byte & 0x01) * Color ); Byte >>= 1;
DrawPixelFast( Device, c, (r << 3) + 0, (Byte & 0x01) * Color );
Device->DrawPixelFast( Device, c, (r << 3) + 7, (Byte & 0x01) * Color ); Byte >>= 1;
Device->DrawPixelFast( Device, c, (r << 3) + 6, (Byte & 0x01) * Color ); Byte >>= 1;
Device->DrawPixelFast( Device, c, (r << 3) + 5, (Byte & 0x01) * Color ); Byte >>= 1;
Device->DrawPixelFast( Device, c, (r << 3) + 4, (Byte & 0x01) * Color ); Byte >>= 1;
Device->DrawPixelFast( Device, c, (r << 3) + 3, (Byte & 0x01) * Color ); Byte >>= 1;
Device->DrawPixelFast( Device, c, (r << 3) + 2, (Byte & 0x01) * Color ); Byte >>= 1;
Device->DrawPixelFast( Device, c, (r << 3) + 1, (Byte & 0x01) * Color ); Byte >>= 1;
Device->DrawPixelFast( Device, c, (r << 3) + 0, (Byte & 0x01) * Color );
if (++r == Height) { c++; r = 0; }
}
/* for better understanding, here is the mundane version

View File

@@ -156,69 +156,9 @@ static inline bool IsPixelVisible( struct GDS_Device* Device, int x, int y ) {
return Result;
}
static inline void DrawPixel1Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
uint32_t YBit = ( Y & 0x07 );
uint8_t* FBOffset;
/*
* We only need to modify the Y coordinate since the pitch
* of the screen is the same as the width.
* Dividing Y by 8 gives us which row the pixel is in but not
* the bit position.
*/
Y>>= 3;
FBOffset = Device->Framebuffer + ( ( Y * Device->Width ) + X );
if ( Color == GDS_COLOR_XOR ) {
*FBOffset ^= BIT( YBit );
} else {
*FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( YBit ) : *FBOffset | BIT( YBit );
}
}
static inline void DrawPixel4Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
uint8_t* FBOffset = Device->Framebuffer + ( (Y * Device->Width >> 1) + (X >> 1));
*FBOffset = X & 0x01 ? (*FBOffset & 0x0f) | ((Color & 0x0f) << 4) : ((*FBOffset & 0xf0) | (Color & 0x0f));
}
static inline void DrawPixel8Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
Device->Framebuffer[Y * Device->Width + X] = Color;
}
// assumes that Color is 16 bits R..RG..GB..B from MSB to LSB and FB wants 1st serialized byte to start with R
static inline void DrawPixel16Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
uint16_t* FBOffset = (uint16_t*) Device->Framebuffer + Y * Device->Width + X;
*FBOffset = __builtin_bswap16(Color);
}
// assumes that Color is 18 bits RGB from MSB to LSB RRRRRRGGGGGGBBBBBB, so byte[0] is B
// FB is 3-bytes packets and starts with R for serialization so 0,1,2 ... = xxRRRRRR xxGGGGGG xxBBBBBB
static inline void DrawPixel18Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
uint8_t* FBOffset = Device->Framebuffer + (Y * Device->Width + X) * 3;
*FBOffset++ = Color >> 12; *FBOffset++ = (Color >> 6) & 0x3f; *FBOffset = Color & 0x3f;
}
// assumes that Color is 24 bits RGB from MSB to LSB RRRRRRRRGGGGGGGGBBBBBBBB, so byte[0] is B
// FB is 3-bytes packets and starts with R for serialization so 0,1,2 ... = RRRRRRRR GGGGGGGG BBBBBBBB
static inline void DrawPixel24Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
uint8_t* FBOffset = Device->Framebuffer + (Y * Device->Width + X) * 3;
*FBOffset++ = Color >> 16; *FBOffset++ = Color >> 8; *FBOffset = Color;
}
static inline void IRAM_ATTR DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) {
if (Device->DrawPixelFast) Device->DrawPixelFast( Device, X, Y, Color );
else if (Device->Depth == 4) DrawPixel4Fast( Device, X, Y, Color );
else if (Device->Depth == 1) DrawPixel1Fast( Device, X, Y, Color );
else if (Device->Depth == 16) DrawPixel16Fast( Device, X, Y, Color );
else if (Device->Depth == 24 && Device->Mode == GDS_RGB666) DrawPixel18Fast( Device, X, Y, Color );
else if (Device->Depth == 24 && Device->Mode == GDS_RGB888) DrawPixel24Fast( Device, X, Y, Color );
else if (Device->Depth == 8) DrawPixel8Fast( Device, X, Y, Color );
}
static inline void IRAM_ATTR DrawPixel( struct GDS_Device* Device, int x, int y, int Color ) {
if ( IsPixelVisible( Device, x, y ) == true ) {
DrawPixelFast( Device, x, y, Color );
Device->DrawPixelFast( Device, x, y, Color );
}
}

View File

@@ -108,7 +108,7 @@ bool GDS_TextLine(struct GDS_Device* Device, int N, int Pos, int Attr, char *Tex
int Y_min = max(0, Device->Lines[N].Y), Y_max = max(0, Device->Lines[N].Y + Device->Lines[N].Font->Height);
for (int c = (Attr & GDS_TEXT_CLEAR_EOL) ? X : 0; c < Device->TextWidth; c++)
for (int y = Y_min; y < Y_max; y++)
DrawPixelFast( Device, c, y, GDS_COLOR_BLACK );
Device->DrawPixelFast( Device, c, y, GDS_COLOR_BLACK );
}
GDS_FontDrawString( Device, X, Device->Lines[N].Y, Text, GDS_COLOR_WHITE );

View File

@@ -461,7 +461,7 @@ static void buffer_put_packet(rtp_t *ctx, seq_t seqno, unsigned rtptime, bool fi
return;
}
}
if (seqno == (u16_t) (ctx->ab_write+1)) {
// expected packet
abuf = ctx->audio_buffer + BUFIDX(seqno);
@@ -572,17 +572,25 @@ static void buffer_push_packet(rtp_t *ctx) {
}
LOG_SDEBUG("playtime %u %d [W:%hu R:%hu] %d", playtime, playtime - now, ctx->ab_write, ctx->ab_read, curframe->ready);
// try to request resend missing packet in order, explore up to 32 frames
for (int step = max((ctx->ab_write - ctx->ab_read + 1) / 32, 1),
i = 0, first = 0;
seq_order(ctx->ab_read + i, ctx->ab_write); i += step) {
abuf_t* frame = ctx->audio_buffer + BUFIDX(ctx->ab_read + i);
// each missing packet will be requested up to (latency_frames / 16) times
for (int i = 0; seq_order(ctx->ab_read + i, ctx->ab_write); i += 16) {
abuf_t *frame = ctx->audio_buffer + BUFIDX(ctx->ab_read + i);
if (!frame->ready && now - frame->last_resend > RESEND_TO) {
// stop if one fails
if (!rtp_request_resend(ctx, ctx->ab_read + i, ctx->ab_read + i)) break;
frame->last_resend = now;
}
}
// stop when we reach a ready frame or a recent pending resend
if (first && (frame->ready || now - frame->last_resend <= RESEND_TO)) {
if (!rtp_request_resend(ctx, first, ctx->ab_read + i - 1)) break;
first = 0;
i += step - 1;
} else if (!frame->ready && now - frame->last_resend > RESEND_TO) {
if (!first) first = ctx->ab_read + i;
frame->last_resend = now;
}
}
}
/*---------------------------------------------------------------------------*/

View File

@@ -62,7 +62,7 @@ static void task_stats( cJSON* top ) {
current.n = uxTaskGetSystemState( current.tasks, current.n, &current.total );
cJSON_AddNumberToObject(top,"ntasks",current.n);
char scratch[SCRATCH_SIZE] = { };
char scratch[SCRATCH_SIZE] = {0};
#ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS
#pragma message("Compiled with runtime stats")
@@ -143,11 +143,13 @@ static void monitor_trace(uint32_t now) {
heap_caps_get_minimum_free_size(MALLOC_CAP_DMA));
task_stats(top);
char * top_a= cJSON_PrintUnformatted(top);
if(top_a){
messaging_post_message(MESSAGING_INFO, MESSAGING_CLASS_STATS,top_a);
FREE_AND_NULL(top_a);
}
cJSON_Delete(top);
}

View File

@@ -227,7 +227,7 @@ int main(int argc, char *argv[]) {
} else if (!strcasecmp(arg, "-i")) {
identity = *++argv;
} else {
// nothing let's try to be smart and handle legacy crappy
// nothing let's try to be smart and handle legacy crap
if (!identity) identity = *argv;
else if (!type) (void) !asprintf(&type, "%s.local", *argv);
else if (!port) port = atoi(*argv);
@@ -235,6 +235,7 @@ int main(int argc, char *argv[]) {
txt = (const char**) malloc((argc + 1) * sizeof(char**));
memcpy(txt, argv, argc * sizeof(char**));
txt[argc] = NULL;
break;
}
argc--;
}
@@ -250,13 +251,14 @@ int main(int argc, char *argv[]) {
mdnsd_set_hostname(svr, hostname, host);
svc = mdnsd_register_svc(svr, identity, type, port, NULL, txt);
mdns_service_destroy(svc);
// mdns_service_destroy(svc);
#ifdef _WIN32
Sleep(INFINITE);
#else
pause();
#endif
mdns_service_remove(svr, svc);
mdnsd_stop(svr);
} else {
printf("Can't start server");
@@ -264,7 +266,7 @@ int main(int argc, char *argv[]) {
}
free(type);
free(txt);
if (txt) free(txt);
#ifdef _WIN32
winsock_close();

View File

@@ -144,11 +144,10 @@ static int create_recv_sock(uint32_t host) {
}
#if !defined(_WIN32)
on = sizeof(on);
socklen_t len;
if (!getsockopt(sd, SOL_SOCKET, SO_REUSEPORT,(char*) &on, &len)) {
socklen_t len = sizeof(on);
if (!getsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, &len)) {
on = 1;
if ((r = setsockopt(sd, SOL_SOCKET, SO_REUSEPORT,(char*) &on, sizeof(on))) < 0) {
if ((r = setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on))) < 0) {
log_message(LOG_ERR, "recv setsockopt(SO_REUSEPORT): %m\n", r);
}
}

View File

@@ -5,6 +5,7 @@
#include <unistd.h>
#include <cstring>
#include <vector>
#include <mutex>
#if __has_include("avahi-client/client.h")
#include <avahi-client/client.h>

View File

@@ -201,21 +201,23 @@ void SpircHandler::handleFrame(std::vector<uint8_t>& data) {
break;
}
case MessageType_kMessageTypeReplace: {
CSPOT_LOG(debug, "Got replace frame %d", playbackState->remoteTracks.size());
CSPOT_LOG(debug, "Got replace frame %d",
playbackState->remoteTracks.size());
playbackState->syncWithRemote();
// 1st track is the current one, but update the position
bool cleared = trackQueue->updateTracks(
playbackState->remoteFrame.state.position_ms +
ctx->timeProvider->getSyncedTimestamp() -
playbackState->innerFrame.state.position_measured_at);
ctx->timeProvider->getSyncedTimestamp() -
playbackState->innerFrame.state.position_measured_at,
false);
this->notify();
// need to re-load all if streaming track is completed
if (cleared) {
sendEvent(EventType::FLUSH);
trackPlayer->resetState();
sendEvent(EventType::FLUSH);
trackPlayer->resetState();
}
break;
}

View File

@@ -609,16 +609,16 @@ bool TrackQueue::updateTracks(uint32_t requestedPosition, bool initial) {
playableSemaphore->give();
} else if (preloadedTracks[0]->loading) {
// try to not re-load track if we are still loading it
// remove everything except first track
preloadedTracks.erase(preloadedTracks.begin() + 1, preloadedTracks.end());
// try to not re-load track if we are still loading it
// Push a song on the preloaded queue
CSPOT_LOG(info, "Keeping current track %d", currentTracksIndex);
queueNextTrack(1);
// remove everything except first track
preloadedTracks.erase(preloadedTracks.begin() + 1, preloadedTracks.end());
cleared = false;
// Push a song on the preloaded queue
CSPOT_LOG(info, "Keeping current track %d", currentTracksIndex);
queueNextTrack(1);
cleared = false;
} else {
// Clear preloaded tracks
preloadedTracks.clear();

View File

@@ -44,7 +44,7 @@
#define MAX_OPUS_FRAMES 5760
struct opus {
enum {OGG_SYNC, OGG_ID_HEADER, OGG_COMMENT_HEADER} status;
enum { OGG_ID_HEADER, OGG_COMMENT_HEADER } status;
ogg_stream_state state;
ogg_packet packet;
ogg_sync_state sync;
@@ -131,95 +131,108 @@ static opus_uint32 parse_uint32(const unsigned char* _data) {
(opus_uint32)_data[2] << 16 | (opus_uint32)_data[3] << 24;
}
static int get_opus_packet(void) {
static int get_audio_packet(void) {
int status, packet = -1;
LOCK_S;
size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
while (!(status = OG(&go, stream_packetout, &u->state, &u->packet)) && bytes) {
// if sync_pageout (or sync_pageseek) is not called here, sync builds ups
while (!(status = OG(&go, sync_pageout, &u->sync, &u->page)) && bytes) {
// if sync_pageout (or sync_pageseek) is not called here, sync buffers build up
while (!(status = OG(&go, sync_pageout, &u->sync, &u->page)) && bytes) {
size_t consumed = min(bytes, 4096);
char* buffer = OG(&gu, sync_buffer, &u->sync, consumed);
char* buffer = OG(&go, sync_buffer, &u->sync, consumed);
memcpy(buffer, streambuf->readp, consumed);
OG(&gu, sync_wrote, &u->sync, consumed);
OG(&go, sync_wrote, &u->sync, consumed);
_buf_inc_readp(streambuf, consumed);
bytes -= consumed;
}
}
// if we have a new page, put it in
if (status) OG(&go, stream_pagein, &u->state, &u->page);
}
// only return a negative value when true end of streaming is reached
if (status > 0) packet = status;
else if (stream.state > DISCONNECT || _buf_used(streambuf)) packet = 0;
// if we have a new page, put it in and reset serialno at BoS
if (status) {
OG(&go, stream_pagein, &u->state, &u->page);
if (OG(&go, page_bos, &u->page)) OG(&go, stream_reset_serialno, &u->state, OG(&go, page_serialno, &u->page));
}
}
/* discard header packets. With no packet, we return a negative value
* when there is really nothing more to proceed */
if (status > 0 && memcmp(u->packet.packet, "OpusHead", 8) && memcmp(u->packet.packet, "OpusTags", 8)) packet = status;
else if (stream.state > DISCONNECT || _buf_used(streambuf)) packet = 0;
UNLOCK_S;
return packet;
}
static int read_opus_header(void) {
int status = 0;
bool fetch = true;
int done = 0;
bool fetch = true;
LOCK_S;
size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
while (bytes && !status) {
// first fetch a page if we need one
if (fetch) {
while (bytes && !done) {
int status;
// get aligned to a page and ready to bring it in
do {
size_t consumed = min(bytes, 4096);
char* buffer = OG(&gu, sync_buffer, &u->sync, consumed);
char* buffer = OG(&go, sync_buffer, &u->sync, consumed);
memcpy(buffer, streambuf->readp, consumed);
OG(&gu, sync_wrote, &u->sync, consumed);
OG(&go, sync_wrote, &u->sync, consumed);
_buf_inc_readp(streambuf, consumed);
bytes -= consumed;
if (!OG(&gu, sync_pageseek, &u->sync, &u->page)) continue;
}
status = fetch ? OG(&go, sync_pageout, &u->sync, &u->page) :
OG(&go, sync_pageseek, &u->sync, &u->page);
} while (bytes && status <= 0);
switch (u->status) {
case OGG_SYNC:
u->status = OGG_ID_HEADER;
OG(&gu, stream_reset_serialno, &u->state, OG(&gu, page_serialno, &u->page));
fetch = false;
break;
case OGG_ID_HEADER:
status = OG(&gu, stream_pagein, &u->state, &u->page);
if (OG(&gu, stream_packetout, &u->state, &u->packet)) {
if (u->packet.bytes < 19 || memcmp(u->packet.packet, "OpusHead", 8)) {
LOG_ERROR("wrong opus header packet (size:%u)", u->packet.bytes);
status = -100;
break;
}
u->status = OGG_COMMENT_HEADER;
// 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
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)
if (fetch) OG(&go, stream_pagein, &u->state, &u->page);
// no need for a switch...case
if (u->status == OGG_ID_HEADER) {
// we need the id packet, get more pages if we don't
if (OG(&go, stream_packetout, &u->state, &u->packet) <= 0) continue;
// make sure this is a valid packet
if (u->packet.bytes < 19 || memcmp(u->packet.packet, "OpusHead", 8)) {
LOG_ERROR("wrong header packet (size:%u)", u->packet.bytes);
done = -100;
} else {
u->status = OGG_COMMENT_HEADER;
u->channels = u->packet.packet[9];
u->pre_skip = parse_uint16(u->packet.packet + 10);
u->rate = parse_uint32(u->packet.packet + 12);
u->gain = parse_int16(u->packet.packet + 16);
u->decoder = OP(&gu, decoder_create, 48000, u->channels, &status);
fetch = false;
if (!u->decoder || status != OPUS_OK) {
LOG_ERROR("can't create decoder %d (channels:%u)", status, u->channels);
}
else {
LOG_INFO("codec up and running");
}
}
fetch = true;
break;
case OGG_COMMENT_HEADER:
// skip packets to consume VorbisComment. With opus, header packets align on pages
status = OG(&gu, page_packets, &u->page);
break;
default:
break;
} else if (u->status == OGG_COMMENT_HEADER) {
// don't consume VorbisComment which could be a huge packet, just skip it
if (!OG(&go, page_packets, &u->page)) continue;
done = 1;
}
}
UNLOCK_S;
return status;
return done;
}
static decode_state opus_decompress(void) {
@@ -271,7 +284,7 @@ static decode_state opus_decompress(void) {
memcpy(write_buf, u->overbuf, u->overframes * BYTES_PER_FRAME);
n = u->overframes;
u->overframes = 0;
} else if ((packet = get_opus_packet()) > 0) {
} else if ((packet = get_audio_packet()) > 0) {
if (frames < MAX_OPUS_FRAMES) {
// don't have enough contiguous space, use the overflow buffer
n = OP(&gu, decode, u->decoder, u->packet.packet, u->packet.bytes, (opus_int16*) u->overbuf, MAX_OPUS_FRAMES, 0);
@@ -286,7 +299,7 @@ static decode_state opus_decompress(void) {
* outputbuf and streambuf for maybe a long time while we process it all, so don't do that */
n = OP(&gu, decode, u->decoder, u->packet.packet, u->packet.bytes, (opus_int16*) write_buf, frames, 0);
}
} else if (!packet && !OG(&go, page_eos, &u->page)) {
} else if (!packet) {
UNLOCK_O_direct;
return DECODE_RUNNING;
}
@@ -342,7 +355,7 @@ static decode_state opus_decompress(void) {
} else {
LOG_INFO("opus decode error: %d", n);
LOG_INFO("decode error: %d", n);
UNLOCK_O_direct;
return DECODE_COMPLETE;
}
@@ -357,7 +370,7 @@ static void opus_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) {
if (!u->overbuf) u->overbuf = malloc(MAX_OPUS_FRAMES * BYTES_PER_FRAME);
u->status = OGG_SYNC;
u->status = OGG_ID_HEADER;
u->overframes = 0;
OG(&go, stream_clear, &u->state);

View File

@@ -443,7 +443,7 @@ void output_close_common(void) {
}
void output_flush(void) {
LOG_INFO("flush output buffer");
LOG_INFO("flush output buffer (full)");
buf_flush(outputbuf);
LOCK;
output.fade = FADE_INACTIVE;
@@ -457,3 +457,15 @@ void output_flush(void) {
output.frames_played = 0;
UNLOCK;
}
bool output_flush_streaming(void) {
LOG_INFO("flush output buffer (streaming)");
LOCK;
bool flushed = output.track_start != NULL;
if (output.track_start) {
outputbuf->writep = output.track_start;
output.track_start = NULL;
}
UNLOCK;
return flushed;
}

View File

@@ -305,8 +305,18 @@ static void process_strm(u8_t *pkt, int len) {
sendSTAT("STMt", strm->replay_gain); // STMt replay_gain is no longer used to track latency, but support it
break;
case 'f':
{
decode_flush(false);
bool flushed = false;
if (!output.external) flushed |= output_flush_streaming();
// we can have fully finished the current streaming, that's still a flush
if (stream_disconnect() || flushed) sendSTAT("STMf", 0);
buf_flush(streambuf);
output.stop_time = gettime_ms();
break;
}
case 'q':
decode_flush(strm->command == 'q');
decode_flush(true);
if (!output.external) output_flush();
status.frames_played = 0;
if (stream_disconnect() && strm->command == 'f') sendSTAT("STMf", 0);
@@ -383,7 +393,7 @@ static void process_strm(u8_t *pkt, int len) {
stream_file(header, header_len, strm->threshold * 1024);
autostart -= 2;
} else {
stream_sock(ip, port, header, header_len, strm->threshold * 1024, autostart >= 2);
stream_sock(ip, port, strm->format, header, header_len, strm->threshold * 1024, autostart >= 2);
}
sendSTAT("STMc", 0);
sentSTMu = sentSTMo = sentSTMl = false;

View File

@@ -580,12 +580,26 @@ 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;
#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, const char *header, size_t header_len, unsigned threshold, bool cont_wait);
void stream_sock(u32_t ip, u16_t port, char codec, const char *header, size_t header_len, unsigned threshold, bool cont_wait);
bool stream_disconnect(void);
// decode.c
@@ -727,6 +741,7 @@ struct outputstate {
void output_init_common(log_level level, const char *device, unsigned output_buf_size, unsigned rates[], unsigned idle);
void output_close_common(void);
void output_flush(void);
bool output_flush_streaming(void);
// _* called with mutex locked
frames_t _output_frames(frames_t avail);
void _checkfade(bool);

View File

@@ -148,6 +148,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_HEADER && stream.ogg.data) free(stream.ogg.data);
stream.ogg.data = NULL;
#if USE_SSL
if (ssl) {
SSL_shutdown(ssl);
@@ -160,6 +162,122 @@ 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;
for (i = 0; i < n && *offset != len; i++) *offset = (haystack[i] == needle[*offset]) ? *offset + 1 : 0;
return i;
}
static void stream_ogg(size_t n) {
if (stream.ogg.state == STREAM_OGG_OFF) return;
u8_t* p = streambuf->writep;
while (n) {
size_t consumed = min(stream.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;
}
// we have what we want, let's parse
switch (stream.ogg.state) {
case STREAM_OGG_SYNC: {
stream.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;
} else {
LOG_INFO("OggS not at expected position");
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 = malloc(stream.ogg.miss);
stream.ogg.state = STREAM_OGG_SEGMENTS;
} else {
stream.ogg.state = STREAM_OGG_SYNC;
stream.ogg.data = NULL;
}
break;
case STREAM_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;
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 = realloc(stream.ogg.data, stream.ogg.want);
} else {
// otherwise, jump over data
stream.ogg.state = STREAM_OGG_SYNC;
free(stream.ogg.data);
stream.ogg.data = NULL;
}
break;
case STREAM_OGG_PAGE: {
u32_t offset = 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;
// u32:len,char[]:vendorId, u32:N, N x (u32:len,char[]:comment)
char* p = (char*) stream.ogg.data + pos;
p += *p + 4;
u32_t count = *p;
p += 4;
// LMS metadata format for Ogg is "Ogg", N x (u16:len,char[]:comment)
memcpy(stream.header, "Ogg", 3);
stream.header_len = 3;
for (u32_t len; count--; p += len) {
len = *p;
p += 4;
// only report what we use and don't overflow (network byte order)
if (!strncasecmp(p, "TITLE=", 6) || !strncasecmp(p, "ARTIST=", 7) || !strncasecmp(p, "ALBUM=", 6)) {
if (stream.header_len + len > MAX_HEADER) break;
stream.header[stream.header_len++] = len >> 8;
stream.header[stream.header_len++] = len;
memcpy(stream.header + stream.header_len, p, len);
stream.header_len += len;
}
}
stream.meta_send = true;
wake_controller();
LOG_INFO("Ogg meta len: %u", stream.header_len);
break;
}
free(stream.ogg.data);
stream.ogg.state = STREAM_OGG_SYNC;
break;
}
default:
break;
}
p += consumed;
n -= consumed;
}
}
static void *stream_thread() {
while (running) {
@@ -343,6 +461,7 @@ static void *stream_thread() {
}
if (n > 0) {
stream_ogg(n);
_buf_inc_writep(streambuf, n);
stream.bytes += n;
if (stream.meta_interval) {
@@ -485,7 +604,7 @@ void stream_file(const char *header, size_t header_len, unsigned threshold) {
UNLOCK;
}
void stream_sock(u32_t ip, u16_t port, const char *header, size_t header_len, unsigned threshold, bool cont_wait) {
void stream_sock(u32_t ip, u16_t port, char codec, const char *header, size_t header_len, unsigned threshold, bool cont_wait) {
struct sockaddr_in addr;
#if EMBEDDED
@@ -584,6 +703,9 @@ void stream_sock(u32_t ip, u16_t port, const char *header, size_t header_len, un
stream.sent_headers = false;
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;
UNLOCK;
}
@@ -604,6 +726,8 @@ bool stream_disconnect(void) {
disc = true;
}
stream.state = STOPPED;
if (stream.ogg.state == STREAM_OGG_HEADER && stream.ogg.data) free(stream.ogg.data);
stream.ogg.data = NULL;
UNLOCK;
return disc;
}

View File

@@ -50,7 +50,7 @@ static inline int32_t clip15(int32_t x) {
struct vorbis {
bool opened;
enum { OGG_SYNC, OGG_ID_HEADER, OGG_COMMENT_HEADER, OGG_SETUP_HEADER } status;
enum { OGG_ID_HEADER, OGG_COMMENT_HEADER, OGG_SETUP_HEADER } status;
struct {
ogg_stream_state state;
ogg_packet packet;
@@ -138,48 +138,16 @@ extern struct processstate process;
#define OG(h, fn, ...) (h)->ogg_ ## fn(__VA_ARGS__)
#endif
static int get_ogg_packet(void) {
static int get_audio_packet(void) {
int status, packet = -1;
LOCK_S;
size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
while (!(status = OG(&go, stream_packetout, &v->state, &v->packet)) && bytes) {
// if sync_pageout (or sync_pageseek) is not called first, sync buffers build ups
while (!(status = OG(&go, sync_pageout, &v->sync, &v->page)) && bytes) {
size_t consumed = min(bytes, 4096);
char* buffer = OG(&gv, sync_buffer, &v->sync, consumed);
memcpy(buffer, streambuf->readp, consumed);
OG(&gv, sync_wrote, &v->sync, consumed);
_buf_inc_readp(streambuf, consumed);
bytes -= consumed;
}
// if we have a new page, put it in
if (status) OG(&go, stream_pagein, &v->state, &v->page);
}
// only return a negative value when true end of streaming is reached
if (status > 0) packet = status;
else if (stream.state > DISCONNECT || _buf_used(streambuf)) packet = 0;
UNLOCK_S;
return packet;
}
static int read_vorbis_header(void) {
int status = 0;
bool fetch = true;
LOCK_S;
size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
while (bytes && !status) {
// first fetch a page if we need one
if (fetch) {
// if sync_pageout (or sync_pageseek) is not called here, sync buffers build up
while (!(status = OG(&go, sync_pageout, &v->sync, &v->page)) && bytes) {
size_t consumed = min(bytes, 4096);
char* buffer = OG(&go, sync_buffer, &v->sync, consumed);
memcpy(buffer, streambuf->readp, consumed);
@@ -187,81 +155,122 @@ static int read_vorbis_header(void) {
_buf_inc_readp(streambuf, consumed);
bytes -= consumed;
if (!OG(&go, sync_pageseek, &v->sync, &v->page)) continue;
}
switch (v->status) {
case OGG_SYNC:
v->status = OGG_ID_HEADER;
OG(&go, stream_reset_serialno, &v->state, OG(&go, page_serialno, &v->page));
fetch = false;
break;
case OGG_ID_HEADER:
status = OG(&go, stream_pagein, &v->state, &v->page);
if (!OG(&go, stream_packetout, &v->state, &v->packet)) break;
// if we have a new page, put it in and reset serialno at BoS
if (status) {
OG(&go, stream_pagein, &v->state, &v->page);
if (OG(&go, page_bos, &v->page)) OG(&go, stream_reset_serialno, &v->state, OG(&go, page_serialno, &v->page));
}
}
/* odd packets are not audio and should be discarded. With no packet, we
* return a negative value when there is really nothing more to proceed */
if (status > 0 && (v->packet.packet[0] & 0x01) == 0) packet = status;
else if (stream.state > DISCONNECT || _buf_used(streambuf)) packet = 0;
UNLOCK_S;
return packet;
}
static int read_vorbis_header(void) {
int done = 0;
bool fetch = true;
LOCK_S;
size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
while (bytes && !done) {
int status;
// get aligned to a page and ready to bring it in
do {
size_t consumed = min(bytes, 4096);
char* buffer = OG(&go, sync_buffer, &v->sync, consumed);
memcpy(buffer, streambuf->readp, consumed);
OG(&go, sync_wrote, &v->sync, consumed);
_buf_inc_readp(streambuf, consumed);
bytes -= consumed;
status = fetch ? OG(&go, sync_pageout, &v->sync, &v->page) :
OG(&go, sync_pageseek, &v->sync, &v->page);
} while (bytes && status <= 0);
// 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
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)
if (fetch) OG(&go, stream_pagein, &v->state, &v->page);
// not a switch...case b/c we might have multiple packets in a page in vorbis
if (v->status == OGG_ID_HEADER) {
// we need the id packet, get more pages if we don't
if (!OG(&go, stream_packetout, &v->state, &v->packet)) continue;
OV(&gv, info_init, &v->info);
status = OV(&gv, synthesis_headerin, &v->info, &v->comment, &v->packet);
if (status) {
LOG_ERROR("vorbis id header packet error %d", status);
status = -1;
LOG_ERROR("id header packet error %d", status);
done = -1;
} else {
v->channels = v->info.channels;
v->rate = v->info.rate;
v->status = OGG_COMMENT_HEADER;
// only fetch if no other packet already in (they should not)
fetch = OG(&go, page_packets, &v->page) <= 1;
if (!fetch) LOG_INFO("id packet should terminate page");
fetch = false;
LOG_INFO("id acquired");
// we should only have one packet, so get next pages
if (OG(&go, page_packets, &v->page) == 1) continue;
}
break;
case OGG_SETUP_HEADER:
// header packets don't align with pages on Vorbis (contrary to Opus)
if (fetch) OG(&go, stream_pagein, &v->state, &v->page);
}
if (v->status == OGG_COMMENT_HEADER) {
// don't consume VorbisComment which could be a huge packet, just skip it
int packets = OG(&go, page_packets, &v->page);
if (!packets) continue;
// we have a "fake" comment packet that is just has the last page...
v->status = OGG_SETUP_HEADER;
OG(&go, stream_pagein, &v->state, &v->page);
OG(&go, stream_packetout, &v->state, &v->packet);
OV(&gv, comment_init, &v->comment);
v->comment.vendor = "N/A";
fetch = true;
LOG_INFO("comment skipped succesfully");
// because of lack of page alignment, we might have the setup page already fully in
if (packets == 1) continue;
}
if (v->status == OGG_SETUP_HEADER) {
// we need the setup packet, get more pages if we don't
if (OG(&go, stream_packetout, &v->state, &v->packet) <= 0) continue;
// finally build a codec if we have the packet
status = OG(&go, stream_packetout, &v->state, &v->packet);
if (status && ((status = OV(&gv, synthesis_headerin, &v->info, &v->comment, &v->packet)) ||
(status = OV(&gv, synthesis_init, &v->decoder, &v->info)))) {
LOG_ERROR("vorbis setup header packet error %d", status);
if (OV(&gv, synthesis_headerin, &v->info, &v->comment, &v->packet) ||
OV(&gv, synthesis_init, &v->decoder, &v->info)) {
LOG_ERROR("setup header packet error %d", status);
// no need to free comment, it's fake
OV(&gv, info_clear, &v->info);
status = -1;
done = -1;
} else {
OV(&gv, block_init, &v->decoder, &v->block);
v->opened = true;
LOG_INFO("codec up and running (rate: %d, channels:%d)", v->rate, v->channels);
status = 1;
LOG_INFO("codec up and running");
done = 1;
}
//@FIXME: can we have audio on that page as well?
break;
case OGG_COMMENT_HEADER: {
// don't consume VorbisComment, just skip it
int packets = OG(&go, page_packets, &v->page);
if (packets) {
v->status = OGG_SETUP_HEADER;
OG(&go, stream_pagein, &v->state, &v->page);
OG(&go, stream_packetout, &v->state, &v->packet);
OV(&gv, comment_init, &v->comment);
v->comment.vendor = "N/A";
// because of lack of page alignment, we might have the setup page already fully in
if (packets > 1) fetch = false;
LOG_INFO("comment skipped succesfully");
}
break;
}
default:
break;
}
}
UNLOCK_S;
return status;
return done;
}
inline int pcm_out(vorbis_dsp_state* decoder, void*** pcm) {
@@ -317,12 +326,12 @@ static decode_state vorbis_decode(void) {
if (v->overflow) {
n = pcm_out(&v->decoder, &pcm);
v->overflow = n - min(n, frames);
} else if ((packet = get_ogg_packet()) > 0) {
} else if ((packet = get_audio_packet()) > 0) {
n = OV(&gv, synthesis, &v->block, &v->packet);
if (n == 0) n = OV(&gv, synthesis_blockin, &v->decoder, &v->block);
if (n == 0) n = pcm_out(&v->decoder, &pcm);
v->overflow = n - min(n, frames);
} else if (!packet && !OG(&go, page_eos, &v->page)) {
} else if (!packet) {
UNLOCK_O_direct;
return DECODE_RUNNING;
}
@@ -410,7 +419,7 @@ static void vorbis_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) {
}
v->opened = false;
v->status = OGG_SYNC;
v->status = OGG_ID_HEADER;
v->overflow = 0;
OG(&go, stream_clear, &v->state);

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -71,6 +71,8 @@ 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;
@@ -217,6 +219,7 @@ 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.0ba488.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/node_vendors.0ba488.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/index.4cb8fa.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/node_vendors.4cb8fa.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_0ba488_bundle_js_gz_start[] asm("_binary_index_0ba488_bundle_js_gz_start");
extern const uint8_t _index_0ba488_bundle_js_gz_end[] asm("_binary_index_0ba488_bundle_js_gz_end");
extern const uint8_t _node_vendors_0ba488_bundle_js_gz_start[] asm("_binary_node_vendors_0ba488_bundle_js_gz_start");
extern const uint8_t _node_vendors_0ba488_bundle_js_gz_end[] asm("_binary_node_vendors_0ba488_bundle_js_gz_end");
extern const uint8_t _index_4cb8fa_bundle_js_gz_start[] asm("_binary_index_4cb8fa_bundle_js_gz_start");
extern const uint8_t _index_4cb8fa_bundle_js_gz_end[] asm("_binary_index_4cb8fa_bundle_js_gz_end");
extern const uint8_t _node_vendors_4cb8fa_bundle_js_gz_start[] asm("_binary_node_vendors_4cb8fa_bundle_js_gz_start");
extern const uint8_t _node_vendors_4cb8fa_bundle_js_gz_end[] asm("_binary_node_vendors_4cb8fa_bundle_js_gz_end");
const char * resource_lookups[] = {
"/css/index.4bbe29a78a667faa2b6f.css.gz",
"/favicon-32x32.png",
"/index.html.gz",
"/js/index.0ba488.bundle.js.gz",
"/js/node_vendors.0ba488.bundle.js.gz",
"/js/index.4cb8fa.bundle.js.gz",
"/js/node_vendors.4cb8fa.bundle.js.gz",
""
};
const uint8_t * resource_map_start[] = {
_index_4bbe29a78a667faa2b6f_css_gz_start,
_favicon_32x32_png_start,
_index_html_gz_start,
_index_0ba488_bundle_js_gz_start,
_node_vendors_0ba488_bundle_js_gz_start
_index_4cb8fa_bundle_js_gz_start,
_node_vendors_4cb8fa_bundle_js_gz_start
};
const uint8_t * resource_map_end[] = {
_index_4bbe29a78a667faa2b6f_css_gz_end,
_favicon_32x32_png_end,
_index_html_gz_end,
_index_0ba488_bundle_js_gz_end,
_node_vendors_0ba488_bundle_js_gz_end
_index_4cb8fa_bundle_js_gz_end,
_node_vendors_4cb8fa_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.0ba488.bundle.js.gz,dist/js/node_vendors.0ba488.bundle.js.gz
dist/css/index.4bbe29a78a667faa2b6f.css.gz,dist/favicon-32x32.png,dist/index.html.gz,dist/js/index.4cb8fa.bundle.js.gz,dist/js/node_vendors.4cb8fa.bundle.js.gz
***********************************/
#pragma once
#include <inttypes.h>

Binary file not shown.

Binary file not shown.

BIN
server_certs/r2m01.cer.40 Normal file

Binary file not shown.

BIN
server_certs/r2m01.cer.41 Normal file

Binary file not shown.