This commit is contained in:
badaix 2015-02-14 21:49:23 +01:00
parent 84cdd5b8ff
commit e57971c6cb
7 changed files with 671 additions and 52 deletions

100
bug.txt Normal file
View file

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

201
client/browseAvahi.cpp Normal file
View file

@ -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 <config.h>
#endif
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <time.h>
#include <avahi-client/client.h>
#include <avahi-client/lookup.h>
#include <avahi-common/simple-watch.h>
#include <avahi-common/malloc.h>
#include <avahi-common/error.h>
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;
}

View file

@ -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<chronos::msec>().count() > 10000)
while (chunks_.size() * chunk->duration<cs::msec>().count() > 10000)
chunks_.pop();
chunks_.push(shared_ptr<msg::PcmChunk>(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<chronos::msec>().count())
while (ms > chunk_->duration<cs::msec>().count())
{
chunk_ = chunks_.pop();
ms -= min(ms, (long)chunk_->durationLeft<chronos::msec>().count());
ms -= min(ms, (long)chunk_->durationLeft<cs::msec>().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<usec>(bufferMs_).count() << "\n";
sleep_ = chronos::usec(0);
logO << "outputBufferDacTime > bufferMs: " << cs::duration<cs::msec>(outputBufferDacTime) << " > " << cs::duration<cs::msec>(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<usec>(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<cs::usec>(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<cs::msec>(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<cs::msec>(sleep_) << " < " << -cs::duration<cs::msec>(bufferDuration)/2 << ", ";
// We're early: not enough chunks_. play silence. Reference chunk_ is the oldest (front) one
sleep_ = chrono::duration_cast<usec>(TimeProvider::serverNow() - getSilentPlayerChunk(outputBuffer, framesPerBuffer) - bufferMs_ + outputBufferDacTime);
//logD << "-sleep: " << sleep_.count() << " " << -bufferDuration.count() / 2000 << "\n";
sleep_ = chrono::duration_cast<cs::usec>(TimeProvider::serverNow() - getSilentPlayerChunk(outputBuffer, framesPerBuffer) - bufferMs_ + outputBufferDacTime);
logO << "sleep: " << cs::duration<cs::msec>(sleep_) << "\n";
if (sleep_ < -bufferDuration/2)
return true;
}
else if (sleep_ > bufferDuration/2)
{
logO << "sleep > bufferDuration/2: " << cs::duration<cs::msec>(sleep_) << " > " << cs::duration<cs::msec>(bufferDuration)/2 << "\n";
// We're late: discard oldest chunks
while (sleep_ > chunk_->duration<chronos::usec>())
while (sleep_ > chunk_->duration<cs::usec>())
{
logO << "sleep > chunk_->getDuration(): " << sleep_.count() << " > " << chunk_->duration<chronos::msec>().count() << ", chunks: " << chunks_.size() << ", out: " << outputBufferDacTime.count() << ", needed: " << bufferDuration.count() << "\n";
sleep_ = std::chrono::duration_cast<usec>(TimeProvider::serverNow() - chunk_->start() - bufferMs_ + outputBufferDacTime);
logO << "sleep > chunkDuration: " << cs::duration<cs::msec>(sleep_) << " > " << chunk_->duration<cs::msec>().count() << ", chunks: " << chunks_.size() << ", out: " << cs::duration<cs::msec>(outputBufferDacTime) << ", needed: " << cs::duration<cs::msec>(bufferDuration) << "\n";
sleep_ = std::chrono::duration_cast<cs::usec>(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<cs::msec>(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<usec>(TimeProvider::serverNow() - getNextPlayerChunk(outputBuffer, outputBufferDacTime, framesPerBuffer, framesCorrection) - bufferMs_ + outputBufferDacTime);
age = std::chrono::duration_cast<cs::usec>(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<usec>(bufferDuration).count() << "\n";
logO << "Sleep " << cs::duration<cs::msec>(sleep_) << ", age: " << cs::duration<cs::msec>(age) << ", bufferDuration: " << cs::duration<cs::msec>(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<cs::msec>(outputBufferDacTime) << "\n";
}
return true;
}
catch(int e)
{
sleep_ = chronos::usec(0);
sleep_ = cs::usec(0);
return false;
}
}

View file

@ -41,6 +41,12 @@ namespace chronos
Rep x = d.count();
return std::chrono::duration<Rep, Period>(x >= 0 ? x : -x);
}
template <class ToDuration, class Rep, class Period>
int64_t duration(std::chrono::duration<Rep, Period> d)
{
return std::chrono::duration_cast<ToDuration>(d).count();
}
}

271
server/publishAvahi.cpp Normal file
View file

@ -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 <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <avahi-client/client.h>
#include <avahi-client/publish.h>
#include <avahi-common/alternative.h>
#include <avahi-common/simple-watch.h>
#include <avahi-common/malloc.h>
#include <avahi-common/error.h>
#include <avahi-common/timeval.h>
#include <boost/bind.hpp>
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;
}

33
server/publishAvahi.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef PUBLISH_AVAHI_H
#define PUBLISH_AVAHI_H
#include <avahi-client/client.h>
#include <avahi-client/publish.h>
#include <avahi-common/alternative.h>
#include <avahi-common/simple-watch.h>
#include <avahi-common/malloc.h>
#include <avahi-common/error.h>
#include <avahi-common/timeval.h>
#include <string>
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

View file

@ -118,10 +118,10 @@ int main(int argc, char* argv[])
int fd = open(fifoName.c_str(), O_RDONLY | O_NONBLOCK);
try
{
shared_ptr<msg::PcmChunk> chunk;//(new WireChunk());
shared_ptr<msg::PcmChunk> 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