From e57971c6cbfa3efa43834c7a0b5a368ae7983b7d Mon Sep 17 00:00:00 2001 From: badaix Date: Sat, 14 Feb 2015 21:49:23 +0100 Subject: [PATCH] logging --- bug.txt | 100 +++++++++++++++ client/browseAvahi.cpp | 201 +++++++++++++++++++++++++++++ client/stream.cpp | 108 ++++++++-------- common/timeDefs.h | 6 + server/publishAvahi.cpp | 271 ++++++++++++++++++++++++++++++++++++++++ server/publishAvahi.h | 33 +++++ server/snapServer.cpp | 4 +- 7 files changed, 671 insertions(+), 52 deletions(-) create mode 100644 bug.txt create mode 100644 client/browseAvahi.cpp create mode 100644 server/publishAvahi.cpp create mode 100644 server/publishAvahi.h diff --git a/bug.txt b/bug.txt new file mode 100644 index 00000000..236ceb9d --- /dev/null +++ b/bug.txt @@ -0,0 +1,100 @@ +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 2552778 > 92, chunks: 35, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 2461512 > 92, chunks: 34, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 2369340 > 92, chunks: 33, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 2276977 > 92, chunks: 32, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 2184579 > 92, chunks: 31, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 2092190 > 92, chunks: 30, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 1999795 > 92, chunks: 29, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 1907409 > 92, chunks: 28, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 1816011 > 92, chunks: 27, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 1723643 > 92, chunks: 26, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 1631253 > 92, chunks: 25, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 1538985 > 92, chunks: 24, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 1446600 > 92, chunks: 23, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 1354213 > 92, chunks: 22, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 1266210 > 92, chunks: 22, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 1173955 > 92, chunks: 21, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 1081592 > 92, chunks: 20, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 989316 > 92, chunks: 19, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 896943 > 92, chunks: 18, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 804551 > 92, chunks: 17, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 712152 > 92, chunks: 16, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 619757 > 92, chunks: 15, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 527375 > 92, chunks: 14, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 434978 > 92, chunks: 13, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 342585 > 92, chunks: 12, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 250190 > 92, chunks: 11, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] sleep > chunk_->getDuration(): 157787 > 92, chunks: 10, out: 100000, needed: 25034013 +2015-02-11 06-53-30 [out] correction: 4, factor: 1.00362 +2015-02-11 06-53-30 [out] Sleep 65289, age: -27457, bufferDuration: 25034 +2015-02-11 06-53-30 [out] Chunk: -274 -274 -274 -274 1 100000 +2015-02-11 06-53-40 [out] correction: 4, factor: 1.00362 +2015-02-11 06-53-40 [out] Sleep 65189, age: 9939282, bufferDuration: 25034 +2015-02-11 06-53-40 [out] Chunk: 99392 99392 99392 99392 1 100000 +2015-02-11 06-53-50 [out] correction: 4, factor: 1.00362 +2015-02-11 06-53-50 [out] Sleep 65089, age: 19914052, bufferDuration: 25034 +2015-02-11 06-53-50 [out] Chunk: 199140 199140 199140 199140 1 100000 +2015-02-11 06-54-00 [out] correction: 4, factor: 1.00362 +2015-02-11 06-54-00 [out] Sleep 64989, age: 29888939, bufferDuration: 25034 +2015-02-11 06-54-00 [out] Chunk: 298889 298889 298889 298889 1 100000 +2015-02-11 06-54-10 [out] correction: 4, factor: 1.00362 +2015-02-11 06-54-10 [out] Sleep 64889, age: 19245105, bufferDuration: 25034 +2015-02-11 06-54-10 [out] Chunk: 192451 192451 192451 192451 1 100000 +2015-02-11 06-54-20 [out] correction: 4, factor: 1.00362 +2015-02-11 06-54-20 [out] Sleep 64789, age: 29219324, bufferDuration: 25034 +2015-02-11 06-54-20 [out] Chunk: 292193 292193 292193 292193 1 100000 +2015-02-11 06-54-30 [out] correction: 4, factor: 1.00362 +2015-02-11 06-54-30 [out] Sleep 64689, age: 39194206, bufferDuration: 25034 +2015-02-11 06-54-30 [out] Chunk: 391942 391942 391942 391942 1 100000 +... +2015-02-11 07-16-00 [out] correction: 4, factor: 1.00362 +2015-02-11 07-16-00 [out] Sleep 51789, age: 29166609, bufferDuration: 25034 +2015-02-11 07-16-00 [out] Chunk: 291666 291666 291666 291666 1 100000 +2015-02-11 07-16-10 [out] correction: 4, factor: 1.00362 +2015-02-11 07-16-10 [out] Sleep 51689, age: 39141449, bufferDuration: 25034 +2015-02-11 07-16-10 [out] Chunk: 391414 391414 391414 391414 1 100000 +2015-02-11 07-16-20 [out] correction: 4, factor: 1.00362 +2015-02-11 07-16-20 [out] Sleep 51589, age: 19208741, bufferDuration: 25034 +2015-02-11 07-16-20 [out] Chunk: 192087 192087 192087 192087 1 100000 +2015-02-11 07-16-30 [out] correction: 4, factor: 1.00362 +2015-02-11 07-16-30 [out] Sleep 51489, age: 29183951, bufferDuration: 25034 +2015-02-11 07-16-30 [out] Chunk: 291839 291839 291839 291839 1 100000 +2015-02-11 07-16-40 [out] correction: 4, factor: 1.00362 +2015-02-11 07-16-40 [out] Sleep 51389, age: 39158778, bufferDuration: 25034 +2015-02-11 07-16-40 [out] Chunk: 391587 391587 391587 391587 1 100000 +2015-02-11 07-16-50 [out] correction: 4, factor: 1.00362 +2015-02-11 07-16-50 [out] Sleep 51289, age: 49133134, bufferDuration: 25034 +2015-02-11 07-16-50 [out] Chunk: 491331 491331 491331 491331 1 100000 +2015-02-11 07-17-00 [out] correction: 4, factor: 1.00362 +2015-02-11 07-17-00 [out] Sleep 51189, age: 19170398, bufferDuration: 25034 +2015-02-11 07-17-00 [out] Chunk: 191703 191703 191703 191703 1 100000 +2015-02-11 07-17-10 [out] correction: 4, factor: 1.00362 +2015-02-11 07-17-10 [out] Sleep 51089, age: 29145503, bufferDuration: 25034 +2015-02-11 07-17-10 [out] Chunk: 291455 291455 291455 291455 1 100000 +2015-02-11 07-17-20 [out] correction: 4, factor: 1.00362 +2015-02-11 07-17-20 [out] Sleep 50989, age: 39119549, bufferDuration: 25034 +2015-02-11 07-17-20 [out] Chunk: 391195 391195 391195 391195 1 100000 +2015-02-11 07-17-30 [out] correction: 4, factor: 1.00362 +2015-02-11 07-17-30 [out] Sleep 50889, age: 49094582, bufferDuration: 25034 +2015-02-11 07-17-30 [out] Chunk: 490945 490945 490945 490945 1 100000 +2015-02-11 07-17-40 [out] correction: 4, factor: 1.00362 +2015-02-11 07-17-40 [out] Sleep 50789, age: 19224557, bufferDuration: 25034 +2015-02-11 07-17-40 [out] Chunk: 192245 192245 192245 192245 1 100000 +2015-02-11 07-17-50 [out] correction: 4, factor: 1.00362 +2015-02-11 07-17-50 [out] Sleep 50689, age: 29199351, bufferDuration: 25034 +2015-02-11 07-17-50 [out] Chunk: 291993 291993 291993 291993 1 100000 +2015-02-11 07-18-00 [out] correction: 4, factor: 1.00362 +2015-02-11 07-18-00 [out] Sleep 50589, age: 39174527, bufferDuration: 25034 +2015-02-11 07-18-00 [out] Chunk: 391745 391745 391745 391745 1 100000 +2015-02-11 07-18-10 [out] correction: 4, factor: 1.00362 +2015-02-11 07-18-10 [out] Sleep 50489, age: 19242269, bufferDuration: 25034 +2015-02-11 07-18-10 [out] Chunk: 192422 192422 192422 192422 1 100000 +2015-02-11 07-18-20 [out] correction: 4, factor: 1.00362 +2015-02-11 07-18-20 [out] Sleep 50389, age: 29216343, bufferDuration: 25034 +2015-02-11 07-18-20 [out] Chunk: 292163 292163 292163 292163 1 100000 +^[[5~2015-02-11 07-18-30 [out] correction: 4, factor: 1.00362 +2015-02-11 07-18-30 [out] Sleep 50289, age: 39191836, bufferDuration: 25034 +2015-02-11 07-18-30 [out] Chunk: 391918 391918 391918 391918 1 100000 + + + diff --git a/client/browseAvahi.cpp b/client/browseAvahi.cpp new file mode 100644 index 00000000..35282338 --- /dev/null +++ b/client/browseAvahi.cpp @@ -0,0 +1,201 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +static AvahiSimplePoll *simple_poll = NULL; + +static void resolve_callback( + AvahiServiceResolver *r, + AVAHI_GCC_UNUSED AvahiIfIndex interface, + AVAHI_GCC_UNUSED AvahiProtocol protocol, + AvahiResolverEvent event, + const char *name, + const char *type, + const char *domain, + const char *host_name, + const AvahiAddress *address, + uint16_t port, + AvahiStringList *txt, + AvahiLookupResultFlags flags, + AVAHI_GCC_UNUSED void* userdata) { + + assert(r); + + /* Called whenever a service has been resolved successfully or timed out */ + + switch (event) { + case AVAHI_RESOLVER_FAILURE: + fprintf(stderr, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r)))); + break; + + case AVAHI_RESOLVER_FOUND: { + char a[AVAHI_ADDRESS_STR_MAX], *t; + + fprintf(stderr, "Service '%s' of type '%s' in domain '%s':\n", name, type, domain); + + avahi_address_snprint(a, sizeof(a), address); + t = avahi_string_list_to_string(txt); + fprintf(stderr, + "\t%s:%u (%s)\n" + "\tTXT=%s\n" + "\tProto=%i\n" + "\tcookie is %u\n" + "\tis_local: %i\n" + "\tour_own: %i\n" + "\twide_area: %i\n" + "\tmulticast: %i\n" + "\tcached: %i\n", + host_name, port, a, + t, + (int)protocol, + avahi_string_list_get_service_cookie(txt), + !!(flags & AVAHI_LOOKUP_RESULT_LOCAL), + !!(flags & AVAHI_LOOKUP_RESULT_OUR_OWN), + !!(flags & AVAHI_LOOKUP_RESULT_WIDE_AREA), + !!(flags & AVAHI_LOOKUP_RESULT_MULTICAST), + !!(flags & AVAHI_LOOKUP_RESULT_CACHED)); + + avahi_free(t); + } + } + + avahi_service_resolver_free(r); +} + +static void browse_callback( + AvahiServiceBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *type, + const char *domain, + AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, + void* userdata) { + + AvahiClient *c = (AvahiClient*)userdata; + assert(b); + + /* Called whenever a new services becomes available on the LAN or is removed from the LAN */ + + switch (event) { + case AVAHI_BROWSER_FAILURE: + + fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b)))); + avahi_simple_poll_quit(simple_poll); + return; + + case AVAHI_BROWSER_NEW: + fprintf(stderr, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain); + + /* We ignore the returned resolver object. In the callback + function we free it. If the server is terminated before + the callback function is called the server will free + the resolver for us. */ + + if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, (AvahiLookupFlags)0, resolve_callback, c))) + fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_client_errno(c))); + + break; + + case AVAHI_BROWSER_REMOVE: + fprintf(stderr, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain); + break; + + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + fprintf(stderr, "(Browser) %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW"); + break; + } +} + +static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) { + assert(c); + + /* Called whenever the client or server state changes */ + + if (state == AVAHI_CLIENT_FAILURE) { + fprintf(stderr, "Server connection failure: %s\n", avahi_strerror(avahi_client_errno(c))); + avahi_simple_poll_quit(simple_poll); + } +} + +int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) { + AvahiClient *client = NULL; + AvahiServiceBrowser *sb = NULL; + int error; + int ret = 1; + + /* Allocate main loop object */ + if (!(simple_poll = avahi_simple_poll_new())) { + fprintf(stderr, "Failed to create simple poll object.\n"); + goto fail; + } + + /* Allocate a new client */ + client = avahi_client_new(avahi_simple_poll_get(simple_poll), (AvahiClientFlags)0, client_callback, NULL, &error); + + /* Check wether creating the client object succeeded */ + if (!client) { + fprintf(stderr, "Failed to create client: %s\n", avahi_strerror(error)); + goto fail; + } + + /* Create the service browser */ + if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_snapcast._tcp", NULL, (AvahiLookupFlags)0, browse_callback, client))) { + fprintf(stderr, "Failed to create service browser: %s\n", avahi_strerror(avahi_client_errno(client))); + goto fail; + } + + /* Run the main loop */ + avahi_simple_poll_loop(simple_poll); + + ret = 0; + +fail: + + /* Cleanup things */ + if (sb) + avahi_service_browser_free(sb); + + if (client) + avahi_client_free(client); + + if (simple_poll) + avahi_simple_poll_free(simple_poll); + + return ret; +} + + diff --git a/client/stream.cpp b/client/stream.cpp index 5ff49c4d..327018c3 100644 --- a/client/stream.cpp +++ b/client/stream.cpp @@ -5,7 +5,8 @@ #include "timeProvider.h" using namespace std; -using namespace chronos; +//using namespace chronos; +namespace cs = chronos; Stream::Stream(const msg::SampleFormat& sampleFormat) : format_(sampleFormat), sleep_(0), median_(0), shortMedian_(0), lastUpdate_(0), playedFrames_(0) @@ -14,7 +15,7 @@ Stream::Stream(const msg::SampleFormat& sampleFormat) : format_(sampleFormat), s shortBuffer_.setSize(100); miniBuffer_.setSize(20); // cardBuffer_.setSize(50); - bufferMs_ = msec(500); + bufferMs_ = cs::msec(500); /* 48000 x @@ -39,7 +40,7 @@ void Stream::setRealSampleRate(double sampleRate) void Stream::setBufferLen(size_t bufferLenMs) { - bufferMs_ = msec(bufferLenMs); + bufferMs_ = cs::msec(bufferLenMs); } @@ -53,7 +54,7 @@ void Stream::clearChunks() void Stream::addChunk(msg::PcmChunk* chunk) { - while (chunks_.size() * chunk->duration().count() > 10000) + while (chunks_.size() * chunk->duration().count() > 10000) chunks_.pop(); chunks_.push(shared_ptr(chunk)); // logD << "new chunk: " << chunk_->getDuration() << ", Chunks: " << chunks_.size() << "\n"; @@ -61,11 +62,11 @@ void Stream::addChunk(msg::PcmChunk* chunk) -time_point_hrc Stream::getSilentPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer) +cs::time_point_hrc Stream::getSilentPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer) { if (!chunk_) chunk_ = chunks_.pop(); - time_point_hrc tp = chunk_->start(); + cs::time_point_hrc tp = chunk_->start(); memset(outputBuffer, 0, framesPerBuffer * format_.frameSize); return tp; } @@ -98,10 +99,10 @@ time_point_hrc Stream::seek(long ms) return chunk_->start(); // time_point_ms tp = chunk_->timePoint(); - while (ms > chunk_->duration().count()) + while (ms > chunk_->duration().count()) { chunk_ = chunks_.pop(); - ms -= min(ms, (long)chunk_->durationLeft().count()); + ms -= min(ms, (long)chunk_->durationLeft().count()); } chunk_->seek(ms * format_.msRate()); return chunk_->start(); @@ -109,12 +110,12 @@ time_point_hrc Stream::seek(long ms) */ -time_point_hrc Stream::getNextPlayerChunk(void* outputBuffer, const chronos::usec& timeout, unsigned long framesPerBuffer) +cs::time_point_hrc Stream::getNextPlayerChunk(void* outputBuffer, const cs::usec& timeout, unsigned long framesPerBuffer) { if (!chunk_ && !chunks_.try_pop(chunk_, timeout)) throw 0; - time_point_hrc tp = chunk_->start(); + cs::time_point_hrc tp = chunk_->start(); char* buffer = (char*)outputBuffer; unsigned long read = 0; while (read < framesPerBuffer) @@ -127,14 +128,14 @@ time_point_hrc Stream::getNextPlayerChunk(void* outputBuffer, const chronos::use } -time_point_hrc Stream::getNextPlayerChunk(void* outputBuffer, const chronos::usec& timeout, unsigned long framesPerBuffer, long framesCorrection) +cs::time_point_hrc Stream::getNextPlayerChunk(void* outputBuffer, const cs::usec& timeout, unsigned long framesPerBuffer, long framesCorrection) { if (framesCorrection == 0) return getNextPlayerChunk(outputBuffer, timeout, framesPerBuffer); long toRead = framesPerBuffer + framesCorrection; char* buffer = (char*)malloc(toRead * format_.frameSize); - time_point_hrc tp = getNextPlayerChunk(buffer, timeout, toRead); + cs::time_point_hrc tp = getNextPlayerChunk(buffer, timeout, toRead); float factor = (float)toRead / framesPerBuffer;//(float)(framesPerBuffer*channels_); if (abs(framesCorrection) > 1) @@ -170,28 +171,33 @@ void Stream::resetBuffers() -bool Stream::getPlayerChunk(void* outputBuffer, const chronos::usec& outputBufferDacTime, unsigned long framesPerBuffer) +bool Stream::getPlayerChunk(void* outputBuffer, const cs::usec& outputBufferDacTime, unsigned long framesPerBuffer) { if (outputBufferDacTime > bufferMs_) { - logO << "outputBufferDacTime > bufferMs: " << outputBufferDacTime.count() << " > " << std::chrono::duration_cast(bufferMs_).count() << "\n"; - sleep_ = chronos::usec(0); + logO << "outputBufferDacTime > bufferMs: " << cs::duration(outputBufferDacTime) << " > " << cs::duration(bufferMs_) << "\n"; + sleep_ = cs::usec(0); return false; } if (!chunk_ && !chunks_.try_pop(chunk_, outputBufferDacTime)) { - logO << "!chunk_ && !chunks_.try_pop(chunk_, outputBufferDacTime)\n"; - sleep_ = chronos::usec(0); + logO << "no chunks available\n"; + sleep_ = cs::usec(0); return false; } playedFrames_ += framesPerBuffer; - chronos::usec age = std::chrono::duration_cast(TimeProvider::serverNow() - chunk_->start() - bufferMs_ + outputBufferDacTime); - if ((sleep_.count() == 0) && (chronos::abs(age) > chronos::msec(200))) + /// we have a chunk + /// age = chunk age (server now - rec time: some positive value) - buffer (e.g. 1000ms) + time to DAC + /// age = 0 => play now + /// age < 0 => play in -age + /// age > 0 => too old + cs::usec age = std::chrono::duration_cast(TimeProvider::serverNow() - chunk_->start() - bufferMs_ + outputBufferDacTime); + if ((sleep_.count() == 0) && (cs::abs(age) > cs::msec(200))) { - logO << "age > 200: " << age.count() << "\n"; + logO << "age > 200: " << cs::duration(age) << "\n"; sleep_ = age; } @@ -199,54 +205,56 @@ bool Stream::getPlayerChunk(void* outputBuffer, const chronos::usec& outputBuffe { //logD << "framesPerBuffer: " << framesPerBuffer << "\tms: " << framesPerBuffer*2 / PLAYER_CHUNK_MS_SIZE << "\t" << PLAYER_CHUNK_SIZE << "\n"; - chronos::nsec bufferDuration = chronos::nsec(chronos::usec::rep(framesPerBuffer / format_.nsRate())); + cs::nsec bufferDuration = cs::nsec(cs::nsec::rep(framesPerBuffer / format_.nsRate())); // logD << "buffer duration: " << bufferDuration.count() << "\n"; - chronos::usec correction = chronos::usec(0); + cs::usec correction = cs::usec(0); if (sleep_.count() != 0) { resetBuffers(); if (sleep_ < -bufferDuration/2) { + logO << "sleep < -bufferDuration/2: " << cs::duration(sleep_) << " < " << -cs::duration(bufferDuration)/2 << ", "; // We're early: not enough chunks_. play silence. Reference chunk_ is the oldest (front) one - sleep_ = chrono::duration_cast(TimeProvider::serverNow() - getSilentPlayerChunk(outputBuffer, framesPerBuffer) - bufferMs_ + outputBufferDacTime); -//logD << "-sleep: " << sleep_.count() << " " << -bufferDuration.count() / 2000 << "\n"; + sleep_ = chrono::duration_cast(TimeProvider::serverNow() - getSilentPlayerChunk(outputBuffer, framesPerBuffer) - bufferMs_ + outputBufferDacTime); + logO << "sleep: " << cs::duration(sleep_) << "\n"; if (sleep_ < -bufferDuration/2) return true; } else if (sleep_ > bufferDuration/2) { + logO << "sleep > bufferDuration/2: " << cs::duration(sleep_) << " > " << cs::duration(bufferDuration)/2 << "\n"; // We're late: discard oldest chunks - while (sleep_ > chunk_->duration()) + while (sleep_ > chunk_->duration()) { - logO << "sleep > chunk_->getDuration(): " << sleep_.count() << " > " << chunk_->duration().count() << ", chunks: " << chunks_.size() << ", out: " << outputBufferDacTime.count() << ", needed: " << bufferDuration.count() << "\n"; - sleep_ = std::chrono::duration_cast(TimeProvider::serverNow() - chunk_->start() - bufferMs_ + outputBufferDacTime); + logO << "sleep > chunkDuration: " << cs::duration(sleep_) << " > " << chunk_->duration().count() << ", chunks: " << chunks_.size() << ", out: " << cs::duration(outputBufferDacTime) << ", needed: " << cs::duration(bufferDuration) << "\n"; + sleep_ = std::chrono::duration_cast(TimeProvider::serverNow() - chunk_->start() - bufferMs_ + outputBufferDacTime); if (!chunks_.try_pop(chunk_, outputBufferDacTime)) { logO << "no chunks available\n"; chunk_ = NULL; - sleep_ = chronos::usec(0); + sleep_ = cs::usec(0); return false; } } } // out of sync, can be corrected by playing faster/slower - if (sleep_ < -chronos::usec(100)) + if (sleep_ < -cs::usec(100)) { - sleep_ += chronos::usec(100); - correction = -chronos::usec(100); + sleep_ += cs::usec(100); + correction = -cs::usec(100); } - else if (sleep_ > chronos::usec(100)) + else if (sleep_ > cs::usec(100)) { - sleep_ -= chronos::usec(100); - correction = chronos::usec(100); + sleep_ -= cs::usec(100); + correction = cs::usec(100); } else { - logO << "Sleep " << sleep_.count() << "\n"; + logO << "Sleep " << cs::duration(sleep_) << "\n"; correction = sleep_; - sleep_ = chronos::usec(0); + sleep_ = cs::usec(0); } } @@ -257,55 +265,55 @@ bool Stream::getPlayerChunk(void* outputBuffer, const chronos::usec& outputBuffe playedFrames_ -= abs(correctAfterXFrames_); } - age = std::chrono::duration_cast(TimeProvider::serverNow() - getNextPlayerChunk(outputBuffer, outputBufferDacTime, framesPerBuffer, framesCorrection) - bufferMs_ + outputBufferDacTime); + age = std::chrono::duration_cast(TimeProvider::serverNow() - getNextPlayerChunk(outputBuffer, outputBufferDacTime, framesPerBuffer, framesCorrection) - bufferMs_ + outputBufferDacTime); setRealSampleRate(format_.rate); if (sleep_.count() == 0) { if (buffer_.full()) { - if (chronos::usec(abs(median_)) > chronos::msec(1)) + if (cs::usec(abs(median_)) > cs::msec(1)) { logO << "pBuffer->full() && (abs(median_) > 1): " << median_ << "\n"; - sleep_ = chronos::usec(shortMedian_); + sleep_ = cs::usec(shortMedian_); } -/* else if (chronos::usec(median_) > chronos::usec(300)) +/* else if (cs::usec(median_) > cs::usec(300)) { setRealSampleRate(format_.rate - format_.rate / 1000); } - else if (chronos::usec(median_) < -chronos::usec(300)) + else if (cs::usec(median_) < -cs::usec(300)) { setRealSampleRate(format_.rate + format_.rate / 1000); } */ } else if (shortBuffer_.full()) { - if (chronos::usec(abs(shortMedian_)) > chronos::msec(5)) + if (cs::usec(abs(shortMedian_)) > cs::msec(5)) { logO << "pShortBuffer->full() && (abs(shortMedian_) > 5): " << shortMedian_ << "\n"; - sleep_ = chronos::usec(shortMedian_); + sleep_ = cs::usec(shortMedian_); } /* else { setRealSampleRate(format_.rate + -shortMedian_ / 100); } */ } - else if (miniBuffer_.full() && (chronos::usec(abs(miniBuffer_.median())) > chronos::msec(50))) + else if (miniBuffer_.full() && (cs::usec(abs(miniBuffer_.median())) > cs::msec(50))) { logO << "pMiniBuffer->full() && (abs(pMiniBuffer->mean()) > 50): " << miniBuffer_.median() << "\n"; - sleep_ = chronos::usec((chronos::msec::rep)miniBuffer_.mean()); + sleep_ = cs::usec((cs::msec::rep)miniBuffer_.mean()); } } if (sleep_.count() != 0) { - logO << "Sleep " << sleep_.count() << ", age: " << age.count() << ", bufferDuration: " << std::chrono::duration_cast(bufferDuration).count() << "\n"; + logO << "Sleep " << cs::duration(sleep_) << ", age: " << cs::duration(age) << ", bufferDuration: " << cs::duration(bufferDuration) << "\n"; } else if (shortBuffer_.full()) { - if (chronos::usec(shortMedian_) > chronos::usec(100)) + if (cs::usec(shortMedian_) > cs::usec(100)) setRealSampleRate(format_.rate * 0.9999); - else if (chronos::usec(shortMedian_) < -chronos::usec(100)) + else if (cs::usec(shortMedian_) < -cs::usec(100)) setRealSampleRate(format_.rate * 1.0001); } @@ -318,13 +326,13 @@ bool Stream::getPlayerChunk(void* outputBuffer, const chronos::usec& outputBuffe lastUpdate_ = now; median_ = buffer_.median(); shortMedian_ = shortBuffer_.median(); - logO << "Chunk: " << age.count()/100 << "\t" << miniBuffer_.median()/100 << "\t" << shortMedian_/100 << "\t" << median_/100 << "\t" << buffer_.size() << "\t" << outputBufferDacTime.count() << "\n"; + logO << "Chunk: " << age.count()/100 << "\t" << miniBuffer_.median()/100 << "\t" << shortMedian_/100 << "\t" << median_/100 << "\t" << buffer_.size() << "\t" << cs::duration(outputBufferDacTime) << "\n"; } return true; } catch(int e) { - sleep_ = chronos::usec(0); + sleep_ = cs::usec(0); return false; } } diff --git a/common/timeDefs.h b/common/timeDefs.h index f5069597..a37e7f92 100644 --- a/common/timeDefs.h +++ b/common/timeDefs.h @@ -41,6 +41,12 @@ namespace chronos Rep x = d.count(); return std::chrono::duration(x >= 0 ? x : -x); } + + template + int64_t duration(std::chrono::duration d) + { + return std::chrono::duration_cast(d).count(); + } } diff --git a/server/publishAvahi.cpp b/server/publishAvahi.cpp new file mode 100644 index 00000000..7a07f29a --- /dev/null +++ b/server/publishAvahi.cpp @@ -0,0 +1,271 @@ +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#include "publishAvahi.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + + +PublishAvahi::PublishAvahi() : group(NULL), simple_poll(NULL), name(NULL) +{ +} + + +PublishAvahi::~PublishAvahi() +{ +} + + +void PublishAvahi::entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) { + assert(g == group || group == NULL); + group = g; + + /* Called whenever the entry group state changes */ + + switch (state) { + case AVAHI_ENTRY_GROUP_ESTABLISHED : + /* The entry group has been established successfully */ + fprintf(stderr, "Service '%s' successfully established.\n", name); + break; + + case AVAHI_ENTRY_GROUP_COLLISION : { + char *n; + + /* A service name collision with a remote service + * happened. Let's pick a new name */ + n = avahi_alternative_service_name(name); + avahi_free(name); + name = n; + + fprintf(stderr, "Service name collision, renaming service to '%s'\n", name); + + /* And recreate the services */ + create_services(avahi_entry_group_get_client(g)); + break; + } + + case AVAHI_ENTRY_GROUP_FAILURE : + + fprintf(stderr, "Entry group failure: %s\n", avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g)))); + + /* Some kind of failure happened while we were registering our services */ + avahi_simple_poll_quit(simple_poll); + break; + + case AVAHI_ENTRY_GROUP_UNCOMMITED: + case AVAHI_ENTRY_GROUP_REGISTERING: + ; + } +} + +void PublishAvahi::create_services(AvahiClient *c) { + char *n, r[128]; + int ret; + assert(c); + + /* If this is the first time we're called, let's create a new + * entry group if necessary */ + + if (!group) + { + if (!(group = avahi_entry_group_new(c, (void(*)(AvahiEntryGroup*, AvahiEntryGroupState, void*))std::bind(&PublishAvahi::entry_group_callback, this), NULL))) { + fprintf(stderr, "avahi_entry_group_new() failed: %s\n", avahi_strerror(avahi_client_errno(c))); + goto fail; + } + } + /* If the group is empty (either because it was just created, or + * because it was reset previously, add our entries. */ + + if (avahi_entry_group_is_empty(group)) { + fprintf(stderr, "Adding service '%s'\n", name); + + /* Create some random TXT data */ + snprintf(r, sizeof(r), "random=%i", rand()); + + /* We will now add two services and one subtype to the entry + * group. The two services have the same name, but differ in + * the service type (IPP vs. BSD LPR). Only services with the + * same name should be put in the same entry group. */ + + /* Add the service for IPP */ +/* if ((ret = avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, name, "_ipp._tcp", NULL, NULL, 651, "test=blah", r, NULL)) < 0) { + + if (ret == AVAHI_ERR_COLLISION) + goto collision; + + fprintf(stderr, "Failed to add _ipp._tcp service: %s\n", avahi_strerror(ret)); + goto fail; + } +*/ + /* Add the same service for BSD LPR */ + if ((ret = avahi_entry_group_add_service(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AvahiPublishFlags(0), name, "_snapcast._tcp", NULL, NULL, 515, NULL)) < 0) { + + if (ret == AVAHI_ERR_COLLISION) + goto collision; + + fprintf(stderr, "Failed to add _snapcast._tcp service: %s\n", avahi_strerror(ret)); + goto fail; + } + + /* Add an additional (hypothetic) subtype */ +/* if ((ret = avahi_entry_group_add_service_subtype(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AvahiPublishFlags(0), name, "_printer._tcp", NULL, "_magic._sub._printer._tcp") < 0)) { + fprintf(stderr, "Failed to add subtype _magic._sub._printer._tcp: %s\n", avahi_strerror(ret)); + goto fail; + } +*/ + /* Tell the server to register the service */ + if ((ret = avahi_entry_group_commit(group)) < 0) { + fprintf(stderr, "Failed to commit entry group: %s\n", avahi_strerror(ret)); + goto fail; + } + } + + return; + +collision: + + /* A service name collision with a local service happened. Let's + * pick a new name */ + n = avahi_alternative_service_name(name); + avahi_free(name); + name = n; + + fprintf(stderr, "Service name collision, renaming service to '%s'\n", name); + + avahi_entry_group_reset(group); + + create_services(c); + return; + +fail: + avahi_simple_poll_quit(simple_poll); +} + +void PublishAvahi::client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) { + assert(c); + + /* Called whenever the client or server state changes */ + + switch (state) { + case AVAHI_CLIENT_S_RUNNING: + + /* The server has startup successfully and registered its host + * name on the network, so it's time to create our services */ + create_services(c); + break; + + case AVAHI_CLIENT_FAILURE: + + fprintf(stderr, "Client failure: %s\n", avahi_strerror(avahi_client_errno(c))); + avahi_simple_poll_quit(simple_poll); + + break; + + case AVAHI_CLIENT_S_COLLISION: + + /* Let's drop our registered services. When the server is back + * in AVAHI_SERVER_RUNNING state we will register them + * again with the new host name. */ + + case AVAHI_CLIENT_S_REGISTERING: + + /* The server records are now being established. This + * might be caused by a host name change. We need to wait + * for our own records to register until the host name is + * properly esatblished. */ + + if (group) + avahi_entry_group_reset(group); + + break; + + case AVAHI_CLIENT_CONNECTING: + ; + } +} + + +int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) { + PublishAvahi publishAvahi; + + AvahiClient *client = NULL; + int error; + int ret = 1; + struct timeval tv; + + /* Allocate main loop object */ + if (!(simple_poll = avahi_simple_poll_new())) { + fprintf(stderr, "Failed to create simple poll object.\n"); + goto fail; + } + + name = avahi_strdup("MegaPrinter"); + + /* Allocate a new client */ + client = avahi_client_new(avahi_simple_poll_get(simple_poll), AVAHI_CLIENT_IGNORE_USER_CONFIG, client_callback, NULL, &error); + + /* Check wether creating the client object succeeded */ + if (!client) { + fprintf(stderr, "Failed to create client: %s\n", avahi_strerror(error)); + goto fail; + } + + /* After 10s do some weird modification to the service */ +/* avahi_simple_poll_get(simple_poll)->timeout_new( + avahi_simple_poll_get(simple_poll), + avahi_elapse_time(&tv, 1000*10, 0), + modify_callback, + client); +*/ + /* Run the main loop */ + while (avahi_simple_poll_iterate(simple_poll, 100) == 0) + printf("1"); + + ret = 0; + +fail: + + /* Cleanup things */ + + if (client) + avahi_client_free(client); + + if (simple_poll) + avahi_simple_poll_free(simple_poll); + + avahi_free(name); + + return ret; +} + + diff --git a/server/publishAvahi.h b/server/publishAvahi.h new file mode 100644 index 00000000..a3098395 --- /dev/null +++ b/server/publishAvahi.h @@ -0,0 +1,33 @@ +#ifndef PUBLISH_AVAHI_H +#define PUBLISH_AVAHI_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +class PublishAvahi +{ +public: + PublishAvahi(); + ~PublishAvahi(); + +private: + void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata); + void create_services(AvahiClient *c); + void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata); + + AvahiEntryGroup *group; + AvahiSimplePoll *simple_poll; + char* name; +}; + + +#endif + + diff --git a/server/snapServer.cpp b/server/snapServer.cpp index 09978dca..6c546c0a 100644 --- a/server/snapServer.cpp +++ b/server/snapServer.cpp @@ -118,10 +118,10 @@ int main(int argc, char* argv[]) int fd = open(fifoName.c_str(), O_RDONLY | O_NONBLOCK); try { - shared_ptr chunk;//(new WireChunk()); + shared_ptr chunk; while (!g_terminated)//cin.good()) { - chunk.reset(new msg::PcmChunk(sampleFormat, duration));//2*WIRE_CHUNK_SIZE)); + chunk.reset(new msg::PcmChunk(sampleFormat, duration)); int toRead = chunk->payloadSize; int len = 0; do