mirror of
https://github.com/badaix/snapcast.git
synced 2025-05-23 22:16:16 +02:00
chronos
git-svn-id: svn://elaine/murooma/trunk@290 d8a302eb-03bc-478d-80e4-98257eca68ef
This commit is contained in:
parent
31ce469c35
commit
07fcca06ca
8 changed files with 91 additions and 29 deletions
|
@ -101,6 +101,10 @@ void Controller::worker()
|
||||||
{
|
{
|
||||||
double latency = (reply->received.sec - reply->sent.sec) + (reply->received.usec - reply->sent.usec) / 1000000.;
|
double latency = (reply->received.sec - reply->sent.sec) + (reply->received.usec - reply->sent.usec) / 1000000.;
|
||||||
TimeProvider::getInstance().setDiffToServer((reply->latency - latency) * 1000 / 2);
|
TimeProvider::getInstance().setDiffToServer((reply->latency - latency) * 1000 / 2);
|
||||||
|
cout << TimeProvider::sinceEpoche<chronos::usec>(chronos::hrc::now()).count() << "\n";
|
||||||
|
cout << TimeProvider::sinceEpoche<chronos::msec>(TimeProvider::now()).count() << "\n";
|
||||||
|
cout << TimeProvider::sinceEpoche<chronos::msec>(TimeProvider::serverNow()).count() << "\n";
|
||||||
|
cout << "Received: " << TimeProvider::sinceEpoche<chronos::msec>(TimeProvider::toTimePoint(reply->received)).count() << "\n\n";
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@ void Player::worker()
|
||||||
{
|
{
|
||||||
snd_pcm_avail_delay(pcm_handle, &avail, &delay);
|
snd_pcm_avail_delay(pcm_handle, &avail, &delay);
|
||||||
|
|
||||||
if (stream_->getPlayerChunk(buff, (float)delay / stream_->format.msRate(), frames, 500))
|
if (stream_->getPlayerChunk(buff, chronos::usec((chronos::usec::rep)(1000 * (double)delay / stream_->format.msRate())), frames, 500))
|
||||||
{
|
{
|
||||||
if ((pcm = snd_pcm_writei(pcm_handle, buff, frames)) == -EPIPE)
|
if ((pcm = snd_pcm_writei(pcm_handle, buff, frames)) == -EPIPE)
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
#include "timeProvider.h"
|
#include "timeProvider.h"
|
||||||
|
|
||||||
using namespace std;
|
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)
|
||||||
{
|
{
|
||||||
|
@ -13,14 +15,14 @@ Stream::Stream(const SampleFormat& sampleFormat) : format(format_), format_(samp
|
||||||
shortBuffer.setSize(100);
|
shortBuffer.setSize(100);
|
||||||
miniBuffer.setSize(20);
|
miniBuffer.setSize(20);
|
||||||
cardBuffer.setSize(50);
|
cardBuffer.setSize(50);
|
||||||
bufferMs = 500;
|
bufferMs = msec(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void Stream::setBufferLen(size_t bufferLenMs)
|
void Stream::setBufferLen(size_t bufferLenMs)
|
||||||
{
|
{
|
||||||
bufferMs = bufferLenMs;
|
bufferMs = msec(bufferLenMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -42,11 +44,11 @@ void Stream::addChunk(PcmChunk* chunk)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
time_point_ms Stream::getSilentPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer)
|
time_point_hrc Stream::getSilentPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer)
|
||||||
{
|
{
|
||||||
if (!chunk)
|
if (!chunk)
|
||||||
chunk = chunks.pop();
|
chunk = chunks.pop();
|
||||||
time_point_ms tp = chunk->timePoint();
|
time_point_hrc tp = chunk->timePoint();
|
||||||
memset(outputBuffer, 0, framesPerBuffer * format.frameSize);
|
memset(outputBuffer, 0, framesPerBuffer * format.frameSize);
|
||||||
return tp;
|
return tp;
|
||||||
}
|
}
|
||||||
|
@ -70,7 +72,7 @@ time_point_ms Stream::seekTo(const time_point_ms& to)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
time_point_ms Stream::seek(long ms)
|
time_point_hrc Stream::seek(long ms)
|
||||||
{
|
{
|
||||||
if (!chunk)
|
if (!chunk)
|
||||||
chunk = chunks.pop();
|
chunk = chunks.pop();
|
||||||
|
@ -89,13 +91,13 @@ time_point_ms Stream::seek(long ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
time_point_ms Stream::getNextPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer, size_t timeout, int correction)
|
time_point_hrc Stream::getNextPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer, size_t timeout, int correction)
|
||||||
{
|
{
|
||||||
if (!chunk)
|
if (!chunk)
|
||||||
if (!chunks.try_pop(chunk, std::chrono::milliseconds(timeout)))
|
if (!chunks.try_pop(chunk, chronos::msec(timeout)))
|
||||||
throw 0;
|
throw 0;
|
||||||
|
|
||||||
time_point_ms tp = chunk->timePoint();
|
time_point_hrc tp = chunk->timePoint();
|
||||||
int read = 0;
|
int read = 0;
|
||||||
int toRead = framesPerBuffer + correction*format.msRate();
|
int toRead = framesPerBuffer + correction*format.msRate();
|
||||||
char* buffer;
|
char* buffer;
|
||||||
|
@ -153,7 +155,7 @@ void Stream::resetBuffers()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Stream::getPlayerChunk(void* outputBuffer, double outputBufferDacTime, unsigned long framesPerBuffer, size_t timeout)
|
bool Stream::getPlayerChunk(void* outputBuffer, chronos::usec outputBufferDacTime, unsigned long framesPerBuffer, size_t timeout)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -174,7 +176,9 @@ bool Stream::getPlayerChunk(void* outputBuffer, double outputBufferDacTime, unsi
|
||||||
if (sleep < -msBuffer/2)
|
if (sleep < -msBuffer/2)
|
||||||
{
|
{
|
||||||
cout << "Sleep " << sleep;
|
cout << "Sleep " << sleep;
|
||||||
sleep = PcmChunk::getAge(getSilentPlayerChunk(outputBuffer, framesPerBuffer)) - bufferMs + outputBufferDacTime + TimeProvider::getInstance().getDiffToServerMs();
|
msec age = chrono::duration_cast<msec>(TimeProvider::serverNow() - getSilentPlayerChunk(outputBuffer, framesPerBuffer) - bufferMs + outputBufferDacTime);
|
||||||
|
sleep = age.count();
|
||||||
|
// sleep = PcmChunk::getAge(getSilentPlayerChunk(outputBuffer, framesPerBuffer)) - bufferMs + outputBufferDacTime + TimeProvider::getInstance().getDiffToServerMs();
|
||||||
std::cerr << " after: " << sleep << ", chunks: " << chunks.size() << "\n";
|
std::cerr << " after: " << sleep << ", chunks: " << chunks.size() << "\n";
|
||||||
// std::clog << kLogNotice << "sleep: " << sleep << std::endl;
|
// std::clog << kLogNotice << "sleep: " << sleep << std::endl;
|
||||||
// if (sleep > -msBuffer/2)
|
// if (sleep > -msBuffer/2)
|
||||||
|
@ -192,17 +196,18 @@ bool Stream::getPlayerChunk(void* outputBuffer, double outputBufferDacTime, unsi
|
||||||
cout << " after: " << sleep << "\n";
|
cout << " after: " << sleep << "\n";
|
||||||
*/
|
*/
|
||||||
if (!chunk)
|
if (!chunk)
|
||||||
if (!chunks.try_pop(chunk, std::chrono::milliseconds(timeout)))
|
if (!chunks.try_pop(chunk, chronos::msec(timeout)))
|
||||||
throw 0;
|
throw 0;
|
||||||
while (sleep > chunk->getDuration())
|
while (sleep > chunk->getDuration())
|
||||||
{
|
{
|
||||||
if (!chunks.try_pop(chunk, std::chrono::milliseconds(timeout)))
|
if (!chunks.try_pop(chunk, chronos::msec(timeout)))
|
||||||
throw 0;
|
throw 0;
|
||||||
sleep = chunk->getAge() - bufferMs + outputBufferDacTime + TimeProvider::getInstance().getDiffToServerMs();
|
msec age = std::chrono::duration_cast<msec>(TimeProvider::serverNow() - chunk->timePoint() - bufferMs + outputBufferDacTime);
|
||||||
// cout << "chunk->getAge() > chunk->getDuration(): " << chunk->getAge() - bufferMs + outputBufferDacTime << " > " << chunk->getDuration() << ", chunks: " << chunks.size() << ", out: " << outputBufferDacTime << ", needed: " << msBuffer << ", sleep: " << sleep << "\n";
|
sleep = age.count();
|
||||||
|
// cout << "chunk->getAge() > chunk->getDuration(): " << chunk->getAge() - bufferMs + outputBufferDacTime << " > " << chunk->getDuration() << ", chunks: " << chunks.size() << ", out: " << outputBufferDacTime << ", needed: " << msBuffer << ", sleep: " << sleep << "\n";
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
}
|
}
|
||||||
cout << "seek: " << PcmChunk::getAge(seek(sleep)) - bufferMs + outputBufferDacTime << "\n";
|
// cout << "seek: " << PcmChunk::getAge(seek(sleep)) - bufferMs + outputBufferDacTime << "\n";
|
||||||
sleep = 0;
|
sleep = 0;
|
||||||
}
|
}
|
||||||
else if (sleep < 0)
|
else if (sleep < 0)
|
||||||
|
@ -219,7 +224,7 @@ bool Stream::getPlayerChunk(void* outputBuffer, double outputBufferDacTime, unsi
|
||||||
|
|
||||||
|
|
||||||
long age(0);
|
long age(0);
|
||||||
age = PcmChunk::getAge(getNextPlayerChunk(outputBuffer, framesPerBuffer, timeout, correction)) - bufferMs + outputBufferDacTime + TimeProvider::getInstance().getDiffToServerMs();
|
age = std::chrono::duration_cast<msec>(TimeProvider::serverNow() - getNextPlayerChunk(outputBuffer, framesPerBuffer, timeout, correction) - bufferMs + outputBufferDacTime).count();
|
||||||
|
|
||||||
|
|
||||||
if (sleep == 0)
|
if (sleep == 0)
|
||||||
|
@ -262,7 +267,7 @@ bool Stream::getPlayerChunk(void* outputBuffer, double outputBufferDacTime, unsi
|
||||||
lastUpdate = now;
|
lastUpdate = now;
|
||||||
median = buffer.median();
|
median = buffer.median();
|
||||||
shortMedian = shortBuffer.median();
|
shortMedian = shortBuffer.median();
|
||||||
std::cerr << "Chunk: " << age << "\t" << miniBuffer.median() << "\t" << shortMedian << "\t" << median << "\t" << buffer.size() << "\t" << /*cardBuffer << "\t" <<*/ outputBufferDacTime << "\n";
|
std::cerr << "Chunk: " << age << "\t" << miniBuffer.median() << "\t" << shortMedian << "\t" << median << "\t" << buffer.size() << "\t" << /*cardBuffer << "\t" <<*/ outputBufferDacTime.count() << "\n";
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <chrono>
|
||||||
#include "doubleBuffer.h"
|
#include "doubleBuffer.h"
|
||||||
#include "message/message.h"
|
#include "message/message.h"
|
||||||
#include "message/pcmChunk.h"
|
#include "message/pcmChunk.h"
|
||||||
|
@ -21,14 +22,14 @@ public:
|
||||||
Stream(const SampleFormat& format);
|
Stream(const SampleFormat& format);
|
||||||
void addChunk(PcmChunk* chunk);
|
void addChunk(PcmChunk* chunk);
|
||||||
void clearChunks();
|
void clearChunks();
|
||||||
bool getPlayerChunk(void* outputBuffer, double outputBufferDacTime, unsigned long framesPerBuffer, size_t timeout);
|
bool getPlayerChunk(void* outputBuffer, chronos::usec outputBufferDacTime, unsigned long framesPerBuffer, size_t timeout);
|
||||||
void setBufferLen(size_t bufferLenMs);
|
void setBufferLen(size_t bufferLenMs);
|
||||||
const SampleFormat& format;
|
const SampleFormat& format;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
time_point_ms getNextPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer, size_t timeout, int correction = 0);
|
chronos::time_point_hrc getNextPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer, size_t timeout, int correction = 0);
|
||||||
time_point_ms getSilentPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer);
|
chronos::time_point_hrc getSilentPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer);
|
||||||
time_point_ms seek(long ms);
|
chronos::time_point_hrc seek(long ms);
|
||||||
// time_point_ms seekTo(const time_point_ms& to);
|
// time_point_ms seekTo(const time_point_ms& to);
|
||||||
void updateBuffers(int age);
|
void updateBuffers(int age);
|
||||||
void resetBuffers();
|
void resetBuffers();
|
||||||
|
@ -48,7 +49,7 @@ private:
|
||||||
int median;
|
int median;
|
||||||
int shortMedian;
|
int shortMedian;
|
||||||
time_t lastUpdate;
|
time_t lastUpdate;
|
||||||
int bufferMs;
|
chronos::msec bufferMs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,11 @@
|
||||||
#define TIME_PROVIDER_H
|
#define TIME_PROVIDER_H
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
#include "doubleBuffer.h"
|
#include "doubleBuffer.h"
|
||||||
|
#include "message/message.h"
|
||||||
|
#include "common/timeDefs.h"
|
||||||
|
|
||||||
|
|
||||||
class TimeProvider
|
class TimeProvider
|
||||||
{
|
{
|
||||||
|
@ -18,6 +22,28 @@ public:
|
||||||
long getPercentileDiffToServer(size_t percentile);
|
long getPercentileDiffToServer(size_t percentile);
|
||||||
long getDiffToServerMs();
|
long getDiffToServerMs();
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static T sinceEpoche(const chronos::time_point_hrc& point)
|
||||||
|
{
|
||||||
|
return std::chrono::duration_cast<T>(point.time_since_epoch());
|
||||||
|
}
|
||||||
|
|
||||||
|
static chronos::time_point_hrc toTimePoint(const tv& timeval)
|
||||||
|
{
|
||||||
|
return chronos::time_point_hrc(chronos::usec(timeval.usec) + chronos::sec(timeval.sec));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static chronos::time_point_hrc now()
|
||||||
|
{
|
||||||
|
return chronos::hrc::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static chronos::time_point_hrc serverNow()
|
||||||
|
{
|
||||||
|
return chronos::hrc::now() + chronos::usec(TimeProvider::getInstance().getDiffToServer());
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TimeProvider();
|
TimeProvider();
|
||||||
TimeProvider(TimeProvider const&); // Don't Implement
|
TimeProvider(TimeProvider const&); // Don't Implement
|
||||||
|
|
18
common/timeDefs.h
Normal file
18
common/timeDefs.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef TIME_DEFS_H
|
||||||
|
#define TIME_DEFS_H
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
namespace chronos
|
||||||
|
{
|
||||||
|
using hrc = std::chrono::high_resolution_clock;
|
||||||
|
using time_point_hrc = std::chrono::time_point<hrc>;
|
||||||
|
using sec = std::chrono::seconds;
|
||||||
|
using msec = std::chrono::milliseconds;
|
||||||
|
using usec = std::chrono::microseconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
#ifndef PCM_CHUNK_H
|
#ifndef PCM_CHUNK_H
|
||||||
#define PCM_CHUNK_H
|
#define PCM_CHUNK_H
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include "message.h"
|
#include "message.h"
|
||||||
#include "wireChunk.h"
|
#include "wireChunk.h"
|
||||||
#include "sampleFormat.h"
|
#include "sampleFormat.h"
|
||||||
|
#include "common/timeDefs.h"
|
||||||
|
|
||||||
typedef std::chrono::time_point<std::chrono::high_resolution_clock, std::chrono::milliseconds> time_point_ms;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,6 +19,15 @@ public:
|
||||||
int readFrames(void* outputBuffer, size_t frameCount);
|
int readFrames(void* outputBuffer, size_t frameCount);
|
||||||
bool isEndOfChunk() const;
|
bool isEndOfChunk() const;
|
||||||
|
|
||||||
|
inline chronos::time_point_hrc timePoint() const
|
||||||
|
{
|
||||||
|
return chronos::time_point_hrc(
|
||||||
|
chronos::sec(timestamp.sec) +
|
||||||
|
chronos::usec(timestamp.usec) +
|
||||||
|
chronos::usec((chronos::usec::rep)(1000000. * ((double)idx / (double)format.rate)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/*
|
||||||
inline time_point_ms timePoint() const
|
inline time_point_ms timePoint() const
|
||||||
{
|
{
|
||||||
time_point_ms tp;
|
time_point_ms tp;
|
||||||
|
@ -52,7 +60,7 @@ public:
|
||||||
{
|
{
|
||||||
return std::chrono::duration_cast<T>(std::chrono::high_resolution_clock::now() - time_point);
|
return std::chrono::duration_cast<T>(std::chrono::high_resolution_clock::now() - time_point);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
int seek(int frames);
|
int seek(int frames);
|
||||||
double getDuration() const;
|
double getDuration() const;
|
||||||
double getDurationUs() const;
|
double getDurationUs() const;
|
||||||
|
|
|
@ -22,9 +22,9 @@ public:
|
||||||
uint16_t sampleSize;
|
uint16_t sampleSize;
|
||||||
uint16_t frameSize;
|
uint16_t frameSize;
|
||||||
|
|
||||||
float msRate() const
|
double msRate() const
|
||||||
{
|
{
|
||||||
return (float)rate/1000.f;
|
return (double)rate/1000.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void read(std::istream& stream)
|
virtual void read(std::istream& stream)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue