Skip to content

Commit

Permalink
0.9.0
Browse files Browse the repository at this point in the history
  • Loading branch information
philippe44 committed Jan 15, 2024
1 parent 32793f1 commit e96edcc
Show file tree
Hide file tree
Showing 15 changed files with 64 additions and 69 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
0.9.0
- (spotupnp) scratch is dynamically allocated
- (spotupnp) on flow, allow icy with all codecs as long as player wants it (and user allowed)
- (spotupnp) only re-acquire state on play action completion. This is not needed on stop/pause and will create issues like fake stops
- (spotupnp) HTTP won't restart from 0 when fully served (case of Sonos asking the same track when no nextURI has been set already)
- (spotraop) clean config entries (a few remains from squeeze2raop)

0.8.7
- fix loglevels
- 'soptraop) update version number

0.8.6
- (spotupnp) on error, reset counter to 0 if player responds

Expand Down
Binary file renamed SpotRaop-0.8.6.zip → SpotRaop-0.9.0.zip
Binary file not shown.
Binary file renamed SpotUPnP-0.8.6.zip → SpotUPnP-0.9.0.zip
Binary file not shown.
2 changes: 1 addition & 1 deletion common/crosstools
Submodule crosstools updated 1 files
+1 −0 src/platform.h
15 changes: 0 additions & 15 deletions spotraop/src/config_raop.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,7 @@
/* globals */
/*----------------------------------------------------------------------------*/

extern log_level slimproto_loglevel;
extern log_level stream_loglevel;
extern log_level decode_loglevel;
extern log_level output_loglevel;
extern log_level main_loglevel;
extern log_level slimmain_loglevel;
extern log_level util_loglevel;
extern log_level raop_loglevel;
extern bool log_cmdline;
Expand Down Expand Up @@ -63,12 +58,7 @@ void SaveConfig(char *name, void *ref, bool full) {
common = (IXML_Node*) XMLAddNode(doc, root, "common", NULL);
}

XMLUpdateNode(doc, root, false, "slimproto_log", level2debug(slimproto_loglevel));
XMLUpdateNode(doc, root, false, "stream_log", level2debug(stream_loglevel));
XMLUpdateNode(doc, root, false, "output_log", level2debug(output_loglevel));
XMLUpdateNode(doc, root, false, "decode_log", level2debug(decode_loglevel));
XMLUpdateNode(doc, root, false, "main_log",level2debug(main_loglevel));
XMLUpdateNode(doc, root, false, "slimmain_log", level2debug(slimmain_loglevel));
XMLUpdateNode(doc, root, false, "raop_log",level2debug(raop_loglevel));
XMLUpdateNode(doc, root, false, "util_log",level2debug(util_loglevel));
XMLUpdateNode(doc, root, false, "log_limit", "%d", (int32_t) glLogLimit);
Expand Down Expand Up @@ -172,12 +162,7 @@ static void LoadGlobalItem(char *name, char *val) {
if (!strcmp(name, "interface")) strncpy(glInterface, val, sizeof(glInterface));
if (!strcmp(name, "credentials")) glCredentials = atol(val);
if (!strcmp(name, "credentials_path")) strncpy(glCredentialsPath, val, sizeof(glCredentialsPath) - 1);
if (!strcmp(name, "slimproto_log")) slimproto_loglevel = debug2level(val);
if (!strcmp(name, "stream_log")) stream_loglevel = debug2level(val);
if (!strcmp(name, "output_log")) output_loglevel = debug2level(val);
if (!strcmp(name, "decode_log")) decode_loglevel = debug2level(val);
if (!strcmp(name, "main_log")) main_loglevel = debug2level(val);
if (!strcmp(name, "slimmain_log")) slimmain_loglevel = debug2level(val);
if (!strcmp(name, "raop_log")) raop_loglevel = debug2level(val);
if (!strcmp(name, "util_log")) util_loglevel = debug2level(val);
if (!strcmp(name, "log_limit")) glLogLimit = atol(val);
Expand Down
30 changes: 8 additions & 22 deletions spotraop/src/spotraop.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,7 @@ struct sMR glMRDevices[MAX_RENDERERS];
char glCredentialsPath[STR_LEN];
bool glCredentials;

log_level slimproto_loglevel = lINFO;
log_level stream_loglevel = lWARN;
log_level decode_loglevel = lWARN;
log_level output_loglevel = lINFO;
log_level main_loglevel = lINFO;
log_level slimmain_loglevel = lINFO;
log_level util_loglevel = lINFO;
log_level raop_loglevel = lINFO;
bool log_cmdline = false;
Expand Down Expand Up @@ -147,7 +142,9 @@ static char usage[] =
" -m <n1,n2...> exclude devices whose model include tokens\n"
" -n <m1,m2,...> exclude devices whose name includes tokens\n"
" -o <m1,m2,...> include only listed models; overrides -m and -n (use <NULL> if player don't return a model)\n"
" -d <log>=<level> set logging level, logs: all|main|util|upnp, level: error|warn|info|debug|sdebug\n"
" -d <log>=<level> set logging level\n"
" logs: all|main|util|raop\n"
" level: error|warn|info|debug|sdebug\n"

#if LINUX || FREEBSD || SUNOS
" -z daemonize\n"
Expand Down Expand Up @@ -1159,15 +1156,10 @@ static bool ParseArgs(int argc, char **argv) {
if (!strcmp(v, "info")) new = lINFO;
if (!strcmp(v, "debug")) new = lDEBUG;
if (!strcmp(v, "sdebug")) new = lSDEBUG;
if (!strcmp(l, "all") || !strcmp(l, "slimproto")) slimproto_loglevel = new;
if (!strcmp(l, "all") || !strcmp(l, "stream")) stream_loglevel = new;
if (!strcmp(l, "all") || !strcmp(l, "decode")) decode_loglevel = new;
if (!strcmp(l, "all") || !strcmp(l, "output")) output_loglevel = new;
if (!strcmp(l, "all") || !strcmp(l, "main")) main_loglevel = new;
if (!strcmp(l, "all") || !strcmp(l, "util")) util_loglevel = new;
if (!strcmp(l, "all") || !strcmp(l, "raop")) raop_loglevel = new;
if (!strcmp(l, "all") || !strcmp(l, "slimmain")) slimmain_loglevel = new; }
else {
if (!strcmp(l, "all") || !strcmp(l, "main")) main_loglevel = new;
if (!strcmp(l, "all") || !strcmp(l, "util")) util_loglevel = new;
if (!strcmp(l, "all") || !strcmp(l, "raop")) raop_loglevel = new;
} else {
printf("%s", usage);
return false;
}
Expand All @@ -1186,8 +1178,7 @@ static bool ParseArgs(int argc, char **argv) {
/*----------------------------------------------------------------------------*/
/* */
/*----------------------------------------------------------------------------*/
int main(int argc, char *argv[])
{
int main(int argc, char *argv[]) {
int i;
char resp[20] = "";
signal(SIGINT, sighandler);
Expand Down Expand Up @@ -1352,11 +1343,6 @@ int main(int argc, char *argv[])
#endif
#endif

SET_LOGLEVEL(stream);
SET_LOGLEVEL(output);
SET_LOGLEVEL(decode);
SET_LOGLEVEL(slimproto);
SET_LOGLEVEL(slimmain);
SET_LOGLEVEL(main);
SET_LOGLEVEL(util);
SET_LOGLEVEL(raop);
Expand Down
2 changes: 1 addition & 1 deletion spotraop/src/spotraop.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
#include "cross_util.h"
#include "spotify.h"

#define VERSION "v0.8.5" " (" __DATE__ " @ " __TIME__ ")"
#define VERSION "v0.9.0" " (" __DATE__ " @ " __TIME__ ")"

/*----------------------------------------------------------------------------*/
/* typedefs */
Expand Down
20 changes: 17 additions & 3 deletions spotupnp/src/HTTPstreamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ HTTPstreamer::HTTPstreamer(struct in_addr addr, std::string id, unsigned index,

// now estimate the content-length
setContentLength(contentLength);

scratchLen = flow ? encoder->icyInterval : 16384;
scratch = new uint8_t[scratchLen];

struct sockaddr_in host;
host.sin_addr = addr;
Expand Down Expand Up @@ -201,6 +204,7 @@ HTTPstreamer::~HTTPstreamer() {
isRunning = false;
std::scoped_lock lock(runningMutex);
if (listenSock > 0) closesocket(listenSock);
delete[] scratch;
CSPOT_LOG(info, "HTTP streamer %s deleted", streamId.c_str());
}

Expand Down Expand Up @@ -312,7 +316,7 @@ bool HTTPstreamer::connect(int sock) {

// check if icy metadata is requested
if (auto it = headers.find("icy-metadata"); it != headers.end() && flow) {
icy.remain = icy.interval = std::max(sizeof(scratch), (size_t) 16384);
icy.remain = icy.interval = std::max(scratchLen, encoder->icyInterval);
response["icy-metaint"] = std::to_string(icy.interval);
}

Expand Down Expand Up @@ -380,7 +384,17 @@ bool HTTPstreamer::connect(int sock) {
cache->total, length, cache->total - avail);
length = 0;
}
} else if (state == DRAINED) {
sendBody = false;
status = "410 Gone";
response.clear();
CSPOT_LOG(info, "won't resend from start when already fully served");
}
} else if (state == DRAINED) {
sendBody = false;
status = "410 Gone";
response.clear();
CSPOT_LOG(info, "won't resend from start when already fully served");
} else if (cache->total) {
// restart from the beginning if we have cache (see note above regarding Sonos)
if (isSonos) length = INT64_MAX;
Expand Down Expand Up @@ -446,13 +460,13 @@ ssize_t HTTPstreamer::streamBody(int sock, struct timeval& timeout) {

// cache has priority
if (useCache) {
size = cache->read(scratch, sizeof(scratch));
size = cache->read(scratch, scratchLen);
if (!size) useCache = false;
}

// not using cache or empty cache, get fresh data from encoder
if (!size) {
size = encoder->read(scratch, sizeof(scratch), 0, state == DRAINING);
size = encoder->read(scratch, scratchLen, 0, state == DRAINING);
// cache what we have anyway
cache->write(scratch, size);
}
Expand Down
4 changes: 2 additions & 2 deletions spotupnp/src/HTTPstreamer.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ class HTTPstreamer : public bell::Task {
int64_t contentLength = HTTP_CL_NONE;
std::unique_ptr<baseCodec> encoder;
std::unique_ptr<cacheBuffer> cache;
size_t useCache;
uint8_t scratch[32768];
size_t useCache, scratchLen;
uint8_t *scratch;
bool flow, chunked;
int cacheMode;
struct {
Expand Down
6 changes: 4 additions & 2 deletions spotupnp/src/codecs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ baseCodec::baseCodec(codecSettings settings, std::string mimeType, bool store) :
storage = fopen(name.c_str(), "wb");
}

icyInterval = 16 * 1024;
pcmBitrate = settings.rate * settings.channels * settings.size * 8;
pcm = std::make_shared<byteBuffer>(storage);
encoded = pcm;
Expand Down Expand Up @@ -174,6 +175,7 @@ class pcmCodec : public::baseCodec {

pcmCodec::pcmCodec(codecSettings settings, bool store) :
baseCodec(settings, "audio/L16;rate=44100;channels=2", store) {
icyInterval = 128 * 1024;
mimeType = "audio/L" + std::to_string(settings.size * 8) + ";rate=" + std::to_string(settings.rate) +
";channels=" + std::to_string(settings.channels);
}
Expand Down Expand Up @@ -223,7 +225,7 @@ class wavCodec : public::baseCodec {
size_t position = 0;

public:
wavCodec(codecSettings settings, bool store = false) : baseCodec(settings, "audio/wav", store) { }
wavCodec(codecSettings settings, bool store = false) : baseCodec(settings, "audio/wav", store) { icyInterval = 128 * 1024; }
virtual int64_t initialize(int64_t duration);
};

Expand Down Expand Up @@ -287,7 +289,7 @@ class flacCodec : public::baseCodec {
bool drained = false;

public:
flacCodec(codecSettings settings, bool store = false) : baseCodec(settings, "audio/flac", store) { }
flacCodec(codecSettings settings, bool store = false) : baseCodec(settings, "audio/flac", store) { icyInterval = 128 * 1024; }
virtual ~flacCodec(void);
virtual int64_t initialize(int64_t duration);
virtual bool pcmWrite(const uint8_t* data, size_t size);
Expand Down
1 change: 1 addition & 0 deletions spotupnp/src/codecs.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class baseCodec {

public:
std::string mimeType;
size_t icyInterval;

baseCodec(codecSettings settings, std::string mimeType, bool store = false);
virtual ~baseCodec(void) { }
Expand Down
34 changes: 15 additions & 19 deletions spotupnp/src/spotupnp.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ bool glCredentials;
log_level main_loglevel = lINFO;
log_level util_loglevel = lWARN;
log_level upnp_loglevel = lINFO;
log_level spot_loglevel = lINFO;

tMRConfig glMRConfig = {
true, // Enabled
Expand Down Expand Up @@ -140,7 +139,7 @@ static char usage[] =
"Usage: [options]\n"
" -b <ip>[:<port>] network interface and UPnP port to use\n"
" -a <port>[:<count>] set inbound port and range for RTP and HTTP\n"
" -r 96|160|320 set Spotify vorbis codec rate (160)\n"
" -r 96|160|320 set Spotify vorbis codec rate (160)\n"
" -J <path> path to Spotify credentials files\n"
" -j store Spotify credentials in XML config file\n"
" -U <user> Spotify username\n"
Expand All @@ -159,7 +158,9 @@ static char usage[] =
" -m <n1,n2...> exclude devices whose model include tokens\n"
" -n <m1,m2,...> exclude devices whose name includes tokens\n"
" -o <m1,m2,...> include only listed models; overrides -m and -n (use <NULL> if player don't return a model)\n"
" -d <log>=<level> set logging level, logs: all|main|util|upnp, level: error|warn|info|debug|sdebug\n"
" -d <log>=<level> set logging level\n"
" logs: all|main|util|upnp\n"
" level: error|warn|info|debug|sdebug\n"
" -c mp3[:<rate>]|opus[:<rate>]|vorbis[:rate]|flc[:0..9]|wav|pcm audio format send to player (flac)\n"

#if LINUX || FREEBSD
Expand Down Expand Up @@ -338,7 +339,7 @@ void shadowRequest(struct shadowPlayer *shadow, enum spotEvent event, ...) {
else MetaData = va_arg(args, metadata_t*);

// reset these counters to avoid false rollover
Device->Elapsed = Device->ElapsedOffset = 0;
Device->Elapsed = Device->ElapsedAccrued = 0;

LOG_INFO("[%p]: spotify LOAD request", Device);

Expand Down Expand Up @@ -524,17 +525,12 @@ int ActionHandler(Upnp_EventType EventType, const void *Event, void *Cookie) {
p->StartCookie = p->WaitCookie;
_ProcessQueue(p);

/*
when certain waited action has been completed, the state need
to be re-acquired because a 'stop' state might be missed when
(eg) repositionning where two consecutive status update will
give 'playing', the 'stop' in the middle being unseen
*/
if (Resp && (!strcasecmp(Resp, "StopResponse") ||
!strcasecmp(Resp, "PlayResponse") ||
!strcasecmp(Resp, "PauseResponse"))) {
p->State = UNKNOWN;
}
/* when play action has been completed, the state need to be re-acquired because we
* might have missed a state in-between. For example, while seeking there is a very
* stop/play so the STOPPED state will be missed and the PLAYING event will be as
* well. This should not be done for stop/pause actions otherwise we might create a fake STOPPED event state and think
* we stopped when in fact it's just the re-acquisition of current state */
if (Resp && !strcasecmp(Resp, "PlayResponse") && p->State == PLAYING) p->State = UNKNOWN;

break;
}
Expand Down Expand Up @@ -605,7 +601,7 @@ int ActionHandler(Upnp_EventType EventType, const void *Event, void *Cookie) {
if (strcasecmp(p->TrackURI, r)) {
strncpy(p->TrackURI, r, sizeof(p->TrackURI));
p->TrackURI[sizeof(p->TrackURI) - 1] = '\0';
p->ElapsedOffset = 0;
p->ElapsedAccrued = 0;
}
//spotNotify(p->SpotPlayer, SHADOW_TRACK, r + p->PrefixLength);
spotNotify(p->SpotPlayer, SHADOW_TRACK, r);
Expand All @@ -619,14 +615,14 @@ int ActionHandler(Upnp_EventType EventType, const void *Event, void *Cookie) {
uint32_t Elapsed = ConvertTime(r);
// some players (Sonos) restart from 0 when they decode a icy metadata
if (p->Config.Flow && Elapsed + 15 < p->Elapsed) {
LOG_INFO("[%p]: detecting elapsed rollover %d / %d / %d", p, Elapsed, p->Elapsed, p->ElapsedOffset);
p->ElapsedOffset += p->Elapsed;
LOG_INFO("[%p]: detecting elapsed rollover %d / %d / %d", p, Elapsed, p->Elapsed, p->ElapsedAccrued);
p->ElapsedAccrued += p->Elapsed;
p->Elapsed = Elapsed;
}

/* Some player seems to send previous' track position (WX) or even backward
* position so the callee cannot really on just one call */
spotNotify(p->SpotPlayer, SHADOW_TIME, (Elapsed + p->ElapsedOffset) * 1000);
spotNotify(p->SpotPlayer, SHADOW_TIME, (Elapsed + p->ElapsedAccrued) * 1000);
p->Elapsed = Elapsed;

free(r);
Expand Down
4 changes: 2 additions & 2 deletions spotupnp/src/spotupnp.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
#include "metadata.h"
#include "spotify.h"

#define VERSION "v0.8.6"" ("__DATE__" @ "__TIME__")"
#define VERSION "v0.9.0"" ("__DATE__" @ "__TIME__")"

/*----------------------------------------------------------------------------*/
/* typedefs */
Expand Down Expand Up @@ -77,7 +77,7 @@ struct sMR {
struct spotPlayer *SpotPlayer;
metadata_t MetaData;
enum spotEvent SpotState;
uint32_t Elapsed, ElapsedOffset;
uint32_t Elapsed, ElapsedAccrued;
uint32_t LastSeen;
uint8_t *seqN;
void *WaitCookie, *StartCookie, *LastCookie;
Expand Down

0 comments on commit e96edcc

Please sign in to comment.