diff --git a/client/alsaPlayer.cpp b/client/alsaPlayer.cpp index e23641ce..457b80ff 100644 --- a/client/alsaPlayer.cpp +++ b/client/alsaPlayer.cpp @@ -121,14 +121,15 @@ void Player::stop() void Player::worker() { unsigned int pcm; - snd_pcm_sframes_t avail; - snd_pcm_sframes_t delay; + snd_pcm_sframes_t framesAvail; + snd_pcm_sframes_t framesDelay; active_ = true; while (active_) { - snd_pcm_avail_delay(pcm_handle, &avail, &delay); - - if (stream_->getPlayerChunk(buff, chronos::usec((chronos::usec::rep)(1000 * (double)delay / stream_->format.msRate())), frames)) + snd_pcm_avail_delay(pcm_handle, &framesAvail, &framesDelay); + + chronos::usec delay((chronos::usec::rep)(1000 * (double)framesDelay / stream_->format.msRate())); + if (stream_->getPlayerChunk(buff, delay, frames)) { if ((pcm = snd_pcm_writei(pcm_handle, buff, frames)) == -EPIPE) { diff --git a/client/doubleBuffer.h b/client/doubleBuffer.h index 47a181bd..8a5b14e2 100644 --- a/client/doubleBuffer.h +++ b/client/doubleBuffer.h @@ -67,6 +67,10 @@ public: bufferSize = size; } + const std::deque& getBuffer() const + { + return &buffer; + } private: size_t bufferSize; diff --git a/client/stream.cpp b/client/stream.cpp index e214378f..6f4fdd1e 100644 --- a/client/stream.cpp +++ b/client/stream.cpp @@ -8,16 +8,35 @@ using namespace std; using namespace chronos; -Stream::Stream(const SampleFormat& sampleFormat) : format(format_), format_(sampleFormat), sleep(0), median(0), shortMedian(0), lastUpdate(0) +Stream::Stream(const SampleFormat& sampleFormat) : format(format_), format_(sampleFormat), sleep(0), median(0), shortMedian(0), lastUpdate(0), playedFrames(0) { buffer.setSize(500); shortBuffer.setSize(100); miniBuffer.setSize(20); // cardBuffer.setSize(50); bufferMs = msec(500); + + playedSamples = 0; +/* +48000 x +------- = ----- +47999,2 x - 1 + +x = 1,000016667 / (1,000016667 - 1) +*/ + setRealSampleRate(format.rate); } +void Stream::setRealSampleRate(double sampleRate) +{ + if (sampleRate == format.rate) + correctAfterXFrames = 0; + else + correctAfterXFrames = round((format.rate / sampleRate) / (format.rate / sampleRate - 1.)); +// cout << "Correct after X: " << correctAfterXFrames << " (Real rate: " << sampleRate << ", rate: " << format.rate << ")\n"; +} + void Stream::setBufferLen(size_t bufferLenMs) { @@ -91,47 +110,44 @@ time_point_hrc Stream::seek(long ms) */ -time_point_hrc Stream::getNextPlayerChunk(void* outputBuffer, const chronos::usec& timeout, unsigned long framesPerBuffer, const chronos::usec& correction) +time_point_hrc Stream::getNextPlayerChunk(void* outputBuffer, const chronos::usec& timeout, unsigned long framesPerBuffer) { if (!chunk && !chunks.try_pop(chunk, timeout)) throw 0; -//cout << "duration: " << chunk->duration().count() << ", " << chunk->duration().count() << ", " << chunk->duration().count() << "\n"; time_point_hrc tp = chunk->start(); - int read = 0; - int toRead = framesPerBuffer + correction.count()*format.usRate(); - char* buffer; - - if (correction.count() != 0) + char* buffer = (char*)outputBuffer; + unsigned long read = 0; + while (read < framesPerBuffer) { -// chronos::usec usBuffer = (chronos::usec::rep)(framesPerBuffer / format.usRate()); -// if (abs(correction) > usBuffer / 2) -// correction = copysign(usBuffer / 2, correction); - buffer = (char*)malloc(toRead * format.frameSize); - } - else - buffer = (char*)outputBuffer; - - while (read < toRead) - { - read += chunk->readFrames(buffer + read*format.frameSize, toRead - read); + read += chunk->readFrames(buffer + read*format.frameSize, framesPerBuffer - read); if (chunk->isEndOfChunk() && !chunks.try_pop(chunk, timeout)) throw 0; } + return tp; +} - if (correction.count() != 0) + +time_point_hrc Stream::getNextPlayerChunk(void* outputBuffer, const chronos::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); + + float factor = (float)toRead / framesPerBuffer;//(float)(framesPerBuffer*channels_); + if (abs(framesCorrection) > 1) + std::cout << "correction: " << framesCorrection << ", factor: " << factor << "\n"; + float idx = 0; + for (size_t n=0; n(chronos::hrc::now() + outputBufferDacTime - playedSamplesTime); +if (since.count() > 0) + cout << (double)playedSamples / (double)since.count() << "\n"; +*/ if (outputBufferDacTime > bufferMs) return false; if (!chunk && !chunks.try_pop(chunk, outputBufferDacTime)) 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))) { @@ -196,7 +221,12 @@ bool Stream::getPlayerChunk(void* outputBuffer, const chronos::usec& outputBuffe { cout << "sleep > chunk->getDuration(): " << sleep.count() << " > " << chunk->duration().count() << ", chunks: " << chunks.size() << ", out: " << outputBufferDacTime.count() << ", needed: " << bufferDuration.count() << "\n"; if (!chunks.try_pop(chunk, outputBufferDacTime)) + { + cout << "no chunks available\n"; + chunk = NULL; + sleep = chronos::usec(0); return false; + } sleep = std::chrono::duration_cast(TimeProvider::serverNow() - chunk->start() - bufferMs + outputBufferDacTime); } @@ -221,15 +251,34 @@ bool Stream::getPlayerChunk(void* outputBuffer, const chronos::usec& outputBuffe } } - age = std::chrono::duration_cast(TimeProvider::serverNow() - getNextPlayerChunk(outputBuffer, outputBufferDacTime, framesPerBuffer, correction) - bufferMs + outputBufferDacTime); + long framesCorrection = correction.count()*format.usRate(); + if ((correctAfterXFrames != 0) && (playedFrames >= (unsigned long)abs(correctAfterXFrames))) + { + framesCorrection += (correctAfterXFrames > 0)?1:-1; + playedFrames -= abs(correctAfterXFrames); + } + age = std::chrono::duration_cast(TimeProvider::serverNow() - getNextPlayerChunk(outputBuffer, outputBufferDacTime, framesPerBuffer, framesCorrection) - bufferMs + outputBufferDacTime); + +// setRealSampleRate(format.rate); if (sleep.count() == 0) { - if (buffer.full() && (chronos::usec(abs(median)) > chronos::msec(1))) + if (buffer.full()) { - cout << "pBuffer->full() && (abs(median) > 1): " << median << "\n"; - sleep = chronos::usec(median); - } + if (chronos::usec(abs(median)) > chronos::msec(1)) + { + cout << "pBuffer->full() && (abs(median) > 1): " << median << "\n"; + sleep = chronos::usec(shortMedian); + } +/* else if (chronos::usec(median) > chronos::usec(300)) + { + setRealSampleRate(format.rate - format.rate / 1000); + } + else if (chronos::usec(median) < -chronos::usec(300)) + { + setRealSampleRate(format.rate + format.rate / 1000); + } +*/ } else if (shortBuffer.full() && (chronos::usec(abs(shortMedian)) > chronos::msec(5))) { cout << "pShortBuffer->full() && (abs(shortMedian) > 5): " << shortMedian << "\n"; diff --git a/client/stream.h b/client/stream.h index 9db66abc..28b3f2c4 100644 --- a/client/stream.h +++ b/client/stream.h @@ -27,18 +27,23 @@ public: const SampleFormat& format; private: - chronos::time_point_hrc getNextPlayerChunk(void* outputBuffer, const chronos::usec& timeout, unsigned long framesPerBuffer, const chronos::usec& correction = chronos::usec(0)); + chronos::time_point_hrc getNextPlayerChunk(void* outputBuffer, const chronos::usec& timeout, unsigned long framesPerBuffer); + chronos::time_point_hrc getNextPlayerChunk(void* outputBuffer, const chronos::usec& timeout, unsigned long framesPerBuffer, long framesCorrection); chronos::time_point_hrc getSilentPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer); chronos::time_point_hrc seek(long ms); // time_point_ms seekTo(const time_point_ms& to); void updateBuffers(int age); void resetBuffers(); + void setRealSampleRate(double sampleRate); SampleFormat format_; long lastTick; chronos::usec sleep; +unsigned long playedSamples; +chronos::time_point_hrc playedSamplesTime; + Queue> chunks; // DoubleBuffer cardBuffer; DoubleBuffer miniBuffer; @@ -50,6 +55,8 @@ private: int shortMedian; time_t lastUpdate; chronos::msec bufferMs; + unsigned long playedFrames; + long correctAfterXFrames; }; diff --git a/server/Makefile b/server/Makefile index f736ac71..4b345c83 100644 --- a/server/Makefile +++ b/server/Makefile @@ -1,7 +1,7 @@ VERSION = 0.01 CC = /usr/bin/g++ CFLAGS = -std=gnu++0x -Wall -Wno-unused-function -D_REENTRANT -DVERSION=\"$(VERSION)\" -I.. -LDFLAGS = -lrt -lpthread -lboost_system -lboost_program_options -lvorbis -lvorbisenc -logg +LDFLAGS = -lrt -lpthread -lboost_system -lboost_program_options -lvorbis -lvorbisenc -logg -lFLAC OBJ = snapServer.o controlServer.o pcmEncoder.o oggEncoder.o serverSession.o ../common/log.o ../message/pcmChunk.o ../message/sampleFormat.o BIN = snapserver