mirror of
https://github.com/badaix/snapcast.git
synced 2025-05-21 13:06:15 +02:00
pcmDevice and chronos in stream
git-svn-id: svn://elaine/murooma/trunk@298 d8a302eb-03bc-478d-80e4-98257eca68ef
This commit is contained in:
parent
fe79a78efe
commit
d0c07e3b67
13 changed files with 239 additions and 118 deletions
|
@ -2,13 +2,12 @@
|
||||||
#include <alsa/asoundlib.h>
|
#include <alsa/asoundlib.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#define PCM_DEVICE "default"
|
|
||||||
#define BUFFER_TIME 100000
|
#define BUFFER_TIME 100000
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
Player::Player(Stream* stream) : active_(false), stream_(stream)
|
Player::Player(const PcmDevice& pcmDevice, Stream* stream) : active_(false), stream_(stream), pcmDevice_(pcmDevice)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,8 +24,8 @@ void Player::start()
|
||||||
|
|
||||||
|
|
||||||
/* Open the PCM device in playback mode */
|
/* Open the PCM device in playback mode */
|
||||||
if ((pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
|
if ((pcm = snd_pcm_open(&pcm_handle, pcmDevice_.name.c_str(), SND_PCM_STREAM_PLAYBACK, 0)) < 0)
|
||||||
cout << "ERROR: Can't open " << PCM_DEVICE << " PCM device. " << snd_strerror(pcm) << "\n";
|
cout << "ERROR: Can't open " << pcmDevice_.name << " PCM device. " << snd_strerror(pcm) << "\n";
|
||||||
|
|
||||||
/* struct snd_pcm_playback_info_t pinfo;
|
/* struct snd_pcm_playback_info_t pinfo;
|
||||||
if ( (pcm = snd_pcm_playback_info( pcm_handle, &pinfo )) < 0 )
|
if ( (pcm = snd_pcm_playback_info( pcm_handle, &pinfo )) < 0 )
|
||||||
|
@ -149,3 +148,55 @@ void Player::worker()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
vector<PcmDevice> Player::pcm_list(void)
|
||||||
|
{
|
||||||
|
void **hints, **n;
|
||||||
|
char *name, *descr, *io;
|
||||||
|
vector<PcmDevice> result;
|
||||||
|
PcmDevice pcmDevice;
|
||||||
|
|
||||||
|
if (snd_device_name_hint(-1, "pcm", &hints) < 0)
|
||||||
|
return result;
|
||||||
|
n = hints;
|
||||||
|
size_t idx(0);
|
||||||
|
while (*n != NULL) {
|
||||||
|
name = snd_device_name_get_hint(*n, "NAME");
|
||||||
|
descr = snd_device_name_get_hint(*n, "DESC");
|
||||||
|
io = snd_device_name_get_hint(*n, "IOID");
|
||||||
|
if (io != NULL && strcmp(io, "Output") != 0)
|
||||||
|
goto __end;
|
||||||
|
pcmDevice.name = name;
|
||||||
|
pcmDevice.description = descr;
|
||||||
|
pcmDevice.idx = idx++;
|
||||||
|
result.push_back(pcmDevice);
|
||||||
|
// printf("%s\n", name);
|
||||||
|
//cout << "Name: " << name << "\n";
|
||||||
|
//cout << "Desc: " << descr << "\n";
|
||||||
|
/*
|
||||||
|
if ((descr1 = descr) != NULL) {
|
||||||
|
printf(" ");
|
||||||
|
while (*descr1) {
|
||||||
|
if (*descr1 == '\n')
|
||||||
|
printf("\n ");
|
||||||
|
else
|
||||||
|
putchar(*descr1);
|
||||||
|
descr1++;
|
||||||
|
}
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
__end:
|
||||||
|
if (name != NULL)
|
||||||
|
free(name);
|
||||||
|
if (descr != NULL)
|
||||||
|
free(descr);
|
||||||
|
if (io != NULL)
|
||||||
|
free(io);
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
snd_device_name_free_hint(hints);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,16 +4,19 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <vector>
|
||||||
#include <alsa/asoundlib.h>
|
#include <alsa/asoundlib.h>
|
||||||
#include "stream.h"
|
#include "stream.h"
|
||||||
|
#include "pcmDevice.h"
|
||||||
|
|
||||||
|
|
||||||
class Player
|
class Player
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Player(Stream* stream);
|
Player(const PcmDevice& pcmDevice, Stream* stream);
|
||||||
void start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
|
static std::vector<PcmDevice> pcm_list(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void worker();
|
void worker();
|
||||||
|
@ -23,6 +26,7 @@ private:
|
||||||
std::atomic<bool> active_;
|
std::atomic<bool> active_;
|
||||||
Stream* stream_;
|
Stream* stream_;
|
||||||
std::thread* playerThread;
|
std::thread* playerThread;
|
||||||
|
PcmDevice pcmDevice_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -47,9 +47,10 @@ void Controller::onMessageReceived(ClientConnection* connection, const BaseMessa
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Controller::start(const std::string& _ip, size_t _port)
|
void Controller::start(const PcmDevice& pcmDevice, const std::string& _ip, size_t _port)
|
||||||
{
|
{
|
||||||
ip = _ip;
|
ip = _ip;
|
||||||
|
pcmDevice_ = pcmDevice;
|
||||||
clientConnection = new ClientConnection(this, ip, _port);
|
clientConnection = new ClientConnection(this, ip, _port);
|
||||||
controllerThread = new thread(&Controller::worker, this);
|
controllerThread = new thread(&Controller::worker, this);
|
||||||
}
|
}
|
||||||
|
@ -92,25 +93,22 @@ void Controller::worker()
|
||||||
decoder->setHeader(headerChunk.get());
|
decoder->setHeader(headerChunk.get());
|
||||||
|
|
||||||
RequestMsg timeReq("time");
|
RequestMsg timeReq("time");
|
||||||
for (size_t n=0; n<30; ++n)
|
for (size_t n=0; n<50; ++n)
|
||||||
{
|
{
|
||||||
shared_ptr<TimeMsg> reply = clientConnection->sendReq<TimeMsg>(&timeReq, 2000);
|
shared_ptr<TimeMsg> reply = clientConnection->sendReq<TimeMsg>(&timeReq, 2000);
|
||||||
if (reply)
|
if (reply)
|
||||||
{
|
{
|
||||||
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";
|
usleep(1000);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cout << "diff to server [ms]: " << TimeProvider::getInstance().getDiffToServer<chronos::msec>().count() << "\n";
|
||||||
|
|
||||||
stream = new Stream(*sampleFormat);
|
stream = new Stream(*sampleFormat);
|
||||||
stream->setBufferLen(serverSettings->bufferMs);
|
stream->setBufferLen(serverSettings->bufferMs);
|
||||||
|
|
||||||
Player player(stream);
|
Player player(pcmDevice_, stream);
|
||||||
player.start();
|
player.start();
|
||||||
|
|
||||||
RequestMsg startStream("startStream");
|
RequestMsg startStream("startStream");
|
||||||
|
|
|
@ -7,13 +7,14 @@
|
||||||
#include "clientConnection.h"
|
#include "clientConnection.h"
|
||||||
#include "decoder.h"
|
#include "decoder.h"
|
||||||
#include "stream.h"
|
#include "stream.h"
|
||||||
|
#include "pcmDevice.h"
|
||||||
|
|
||||||
|
|
||||||
class Controller : public MessageReceiver
|
class Controller : public MessageReceiver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Controller();
|
Controller();
|
||||||
void start(const std::string& _ip, size_t _port);
|
void start(const PcmDevice& pcmDevice, const std::string& _ip, size_t _port);
|
||||||
void stop();
|
void stop();
|
||||||
virtual void onMessageReceived(ClientConnection* connection, const BaseMessage& baseMessage, char* buffer);
|
virtual void onMessageReceived(ClientConnection* connection, const BaseMessage& baseMessage, char* buffer);
|
||||||
virtual void onException(ClientConnection* connection, const std::exception& exception);
|
virtual void onException(ClientConnection* connection, const std::exception& exception);
|
||||||
|
@ -27,6 +28,7 @@ private:
|
||||||
std::string ip;
|
std::string ip;
|
||||||
std::shared_ptr<SampleFormat> sampleFormat;
|
std::shared_ptr<SampleFormat> sampleFormat;
|
||||||
Decoder* decoder;
|
Decoder* decoder;
|
||||||
|
PcmDevice pcmDevice_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
18
client/pcmDevice.h
Normal file
18
client/pcmDevice.h
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef PCM_DEVICE_H
|
||||||
|
#define PCM_DEVICE_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
class PcmDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PcmDevice() : idx(-1){};
|
||||||
|
int idx;
|
||||||
|
std::string name;
|
||||||
|
std::string description;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -12,31 +12,57 @@
|
||||||
#include "common/utils.h"
|
#include "common/utils.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
|
#include "alsaPlayer.h"
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
namespace po = boost::program_options;
|
namespace po = boost::program_options;
|
||||||
|
|
||||||
|
|
||||||
|
PcmDevice getPcmDevice(const std::string& soundcard)
|
||||||
|
{
|
||||||
|
vector<PcmDevice> pcmDevices = Player::pcm_list();
|
||||||
|
int soundcardIdx = -1;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
soundcardIdx = boost::lexical_cast<int>(soundcard);
|
||||||
|
for (auto dev: pcmDevices)
|
||||||
|
if (dev.idx == soundcardIdx)
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto dev: pcmDevices)
|
||||||
|
if (dev.name.find(soundcard) != string::npos)
|
||||||
|
return dev;
|
||||||
|
|
||||||
|
PcmDevice pcmDevice;
|
||||||
|
return pcmDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int main (int argc, char *argv[])
|
int main (int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int deviceIdx;
|
string soundcard;
|
||||||
string ip;
|
string ip;
|
||||||
// int bufferMs;
|
// int bufferMs;
|
||||||
size_t port;
|
size_t port;
|
||||||
bool runAsDaemon;
|
bool runAsDaemon;
|
||||||
|
bool listPcmDevices;
|
||||||
// string sampleFormat;
|
// string sampleFormat;
|
||||||
po::options_description desc("Allowed options");
|
po::options_description desc("Allowed options");
|
||||||
desc.add_options()
|
desc.add_options()
|
||||||
("help,h", "produce help message")
|
("help,h", "produce help message")
|
||||||
("port,p", po::value<size_t>(&port)->default_value(98765), "port where the server listens on")
|
("port,p", po::value<size_t>(&port)->default_value(98765), "port where the server listens on")
|
||||||
("ip,i", po::value<string>(&ip)->default_value("192.168.0.2"), "server IP")
|
("ip,i", po::value<string>(&ip)->default_value("192.168.0.2"), "server IP")
|
||||||
("soundcard,s", po::value<int>(&deviceIdx)->default_value(-1), "index of the soundcard")
|
("soundcard,s", po::value<string>(&soundcard)->default_value("default"), "index or name of the soundcard")
|
||||||
// ("sampleformat,f", po::value<string>(&sampleFormat)->default_value("48000:16:2"), "sample format")
|
// ("sampleformat,f", po::value<string>(&sampleFormat)->default_value("48000:16:2"), "sample format")
|
||||||
// ("buffer,b", po::value<int>(&bufferMs)->default_value(300), "buffer size [ms]")
|
// ("buffer,b", po::value<int>(&bufferMs)->default_value(300), "buffer size [ms]")
|
||||||
("daemon,d", po::bool_switch(&runAsDaemon)->default_value(false), "daemonize")
|
("daemon,d", po::bool_switch(&runAsDaemon)->default_value(false), "daemonize")
|
||||||
|
("list,l", po::bool_switch(&listPcmDevices)->default_value(false), "list pcm devices")
|
||||||
;
|
;
|
||||||
|
|
||||||
po::variables_map vm;
|
po::variables_map vm;
|
||||||
|
@ -49,6 +75,26 @@ int main (int argc, char *argv[])
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (listPcmDevices)
|
||||||
|
{
|
||||||
|
vector<PcmDevice> pcmDevices = Player::pcm_list();
|
||||||
|
for (auto dev: pcmDevices)
|
||||||
|
{
|
||||||
|
cout << dev.idx << ": " << dev.name << "\n";
|
||||||
|
cout << dev.description << "\n\n";
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
PcmDevice pcmDevice = getPcmDevice(soundcard);
|
||||||
|
if (pcmDevice.idx != -1)
|
||||||
|
cout << pcmDevice.idx << ": " << pcmDevice.name << "\n";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cout << "soundcard \"" << soundcard << "\" not found\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
std::clog.rdbuf(new Log("snapclient", LOG_DAEMON));
|
std::clog.rdbuf(new Log("snapclient", LOG_DAEMON));
|
||||||
if (runAsDaemon)
|
if (runAsDaemon)
|
||||||
{
|
{
|
||||||
|
@ -56,8 +102,9 @@ int main (int argc, char *argv[])
|
||||||
std::clog << kLogNotice << "daemon started" << std::endl;
|
std::clog << kLogNotice << "daemon started" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Controller controller;
|
Controller controller;
|
||||||
controller.start(ip, port);
|
controller.start(pcmDevice, ip, port);
|
||||||
|
|
||||||
while(true)
|
while(true)
|
||||||
usleep(10000);
|
usleep(10000);
|
||||||
|
|
|
@ -48,7 +48,7 @@ time_point_hrc Stream::getSilentPlayerChunk(void* outputBuffer, unsigned long fr
|
||||||
{
|
{
|
||||||
if (!chunk)
|
if (!chunk)
|
||||||
chunk = chunks.pop();
|
chunk = chunks.pop();
|
||||||
time_point_hrc tp = chunk->timePoint();
|
time_point_hrc tp = chunk->start();
|
||||||
memset(outputBuffer, 0, framesPerBuffer * format.frameSize);
|
memset(outputBuffer, 0, framesPerBuffer * format.frameSize);
|
||||||
return tp;
|
return tp;
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ time_point_hrc Stream::seek(long ms)
|
||||||
chunk = chunks.pop();
|
chunk = chunks.pop();
|
||||||
|
|
||||||
if (ms <= 0)
|
if (ms <= 0)
|
||||||
return chunk->timePoint();
|
return chunk->start();
|
||||||
|
|
||||||
// time_point_ms tp = chunk->timePoint();
|
// time_point_ms tp = chunk->timePoint();
|
||||||
while (ms > chunk->duration<chronos::msec>().count())
|
while (ms > chunk->duration<chronos::msec>().count())
|
||||||
|
@ -87,27 +87,27 @@ time_point_hrc Stream::seek(long ms)
|
||||||
ms -= min(ms, (long)chunk->durationLeft<chronos::msec>().count());
|
ms -= min(ms, (long)chunk->durationLeft<chronos::msec>().count());
|
||||||
}
|
}
|
||||||
chunk->seek(ms * format.msRate());
|
chunk->seek(ms * format.msRate());
|
||||||
return chunk->timePoint();
|
return chunk->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
time_point_hrc 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, const chronos::usec& correction)
|
||||||
{
|
{
|
||||||
if (!chunk)
|
if (!chunk)
|
||||||
if (!chunks.try_pop(chunk, chronos::msec(timeout)))
|
if (!chunks.try_pop(chunk, chronos::msec(timeout)))
|
||||||
throw 0;
|
throw 0;
|
||||||
|
|
||||||
//cout << "duration: " << chunk->duration<chronos::msec>().count() << ", " << chunk->duration<chronos::usec>().count() << ", " << chunk->duration<chronos::nsec>().count() << "\n";
|
//cout << "duration: " << chunk->duration<chronos::msec>().count() << ", " << chunk->duration<chronos::usec>().count() << ", " << chunk->duration<chronos::nsec>().count() << "\n";
|
||||||
time_point_hrc tp = chunk->timePoint();
|
time_point_hrc tp = chunk->start();
|
||||||
int read = 0;
|
int read = 0;
|
||||||
int toRead = framesPerBuffer + correction*format.msRate();
|
int toRead = framesPerBuffer + correction.count()*format.usRate();
|
||||||
char* buffer;
|
char* buffer;
|
||||||
|
|
||||||
if (correction != 0)
|
if (correction.count() != 0)
|
||||||
{
|
{
|
||||||
int msBuffer = floor(framesPerBuffer / format.msRate());
|
// chronos::usec usBuffer = (chronos::usec::rep)(framesPerBuffer / format.usRate());
|
||||||
if (abs(correction) > msBuffer / 2)
|
// if (abs(correction) > usBuffer / 2)
|
||||||
correction = copysign(msBuffer / 2, correction);
|
// correction = copysign(usBuffer / 2, correction);
|
||||||
buffer = (char*)malloc(toRead * format.frameSize);
|
buffer = (char*)malloc(toRead * format.frameSize);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -121,10 +121,10 @@ time_point_hrc Stream::getNextPlayerChunk(void* outputBuffer, unsigned long fram
|
||||||
throw 0;
|
throw 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (correction != 0)
|
if (correction.count() != 0)
|
||||||
{
|
{
|
||||||
float factor = (float)toRead / framesPerBuffer;//(float)(framesPerBuffer*channels_);
|
float factor = (float)toRead / framesPerBuffer;//(float)(framesPerBuffer*channels_);
|
||||||
std::cout << "correction: " << correction << ", factor: " << factor << "\n";
|
std::cout << "correction: " << correction.count() << ", factor: " << factor << "\n";
|
||||||
float idx = 0;
|
float idx = 0;
|
||||||
for (size_t n=0; n<framesPerBuffer; ++n)
|
for (size_t n=0; n<framesPerBuffer; ++n)
|
||||||
{
|
{
|
||||||
|
@ -156,128 +156,113 @@ void Stream::resetBuffers()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool Stream::getPlayerChunk(void* outputBuffer, chronos::usec outputBufferDacTime, unsigned long framesPerBuffer, size_t timeout)
|
bool Stream::getPlayerChunk(void* outputBuffer, chronos::usec outputBufferDacTime, unsigned long framesPerBuffer, size_t timeout)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//cout << "framesPerBuffer: " << framesPerBuffer << "\tms: " << framesPerBuffer*2 / PLAYER_CHUNK_MS_SIZE << "\t" << PLAYER_CHUNK_SIZE << "\n";
|
//cout << "framesPerBuffer: " << framesPerBuffer << "\tms: " << framesPerBuffer*2 / PLAYER_CHUNK_MS_SIZE << "\t" << PLAYER_CHUNK_SIZE << "\n";
|
||||||
int msBuffer = framesPerBuffer / format_.msRate();
|
chronos::nsec bufferDuration = chronos::nsec(chronos::usec::rep(framesPerBuffer / format_.nsRate()));
|
||||||
//cout << msBuffer << " ms, " << framesPerBuffer << "\t" << format_.rate/1000 << "\n";
|
// cout << "buffer duration: " << bufferDuration.count() << "\n";
|
||||||
int ticks = 0;
|
|
||||||
long currentTick = getTickCount();
|
chronos::usec correction = chronos::usec(0);
|
||||||
if (lastTick == 0)
|
if (sleep.count() != 0)
|
||||||
lastTick = currentTick;
|
|
||||||
ticks = currentTick - lastTick;
|
|
||||||
lastTick = currentTick;
|
|
||||||
/*
|
|
||||||
pShortBuffer->full() && (abs(shortMedian) > 5): 25
|
|
||||||
Sleep: 25
|
|
||||||
Chunk: 25 25 25 25 23 105941
|
|
||||||
Chunk: 25 25 25 25 55 107210
|
|
||||||
Chunk: 25 25 25 25 88 104671
|
|
||||||
pShortBuffer->full() && (abs(shortMedian) > 5): 25
|
|
||||||
Sleep: 25
|
|
||||||
Chunk: 25 25 25 25 20 99614
|
|
||||||
Chunk: 25 25 25 25 53 96802
|
|
||||||
Chunk: 25 25 25 25 86 117732
|
|
||||||
*/
|
|
||||||
int correction = 0;
|
|
||||||
if (sleep != 0)
|
|
||||||
{
|
{
|
||||||
|
// cout << "Sleep " << sleep.count() << "\n";
|
||||||
resetBuffers();
|
resetBuffers();
|
||||||
if (sleep < -msBuffer/2)
|
if (sleep < -bufferDuration/2)
|
||||||
{
|
{
|
||||||
cout << "Sleep " << sleep;
|
usec age = chrono::duration_cast<usec>(TimeProvider::serverNow() - getSilentPlayerChunk(outputBuffer, framesPerBuffer) - bufferMs + outputBufferDacTime);
|
||||||
msec age = chrono::duration_cast<msec>(TimeProvider::serverNow() - getSilentPlayerChunk(outputBuffer, framesPerBuffer) - bufferMs + outputBufferDacTime);
|
sleep = age;
|
||||||
sleep = age.count();
|
|
||||||
// sleep = PcmChunk::getAge(getSilentPlayerChunk(outputBuffer, framesPerBuffer)) - bufferMs + outputBufferDacTime + TimeProvider::getInstance().getDiffToServerMs();
|
// sleep = PcmChunk::getAge(getSilentPlayerChunk(outputBuffer, framesPerBuffer)) - bufferMs + outputBufferDacTime + TimeProvider::getInstance().getDiffToServerMs();
|
||||||
std::cerr << " after: " << sleep << ", chunks: " << chunks.size() << "\n";
|
std::cerr << " after: " << sleep.count() << ", 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)
|
||||||
// sleep = 0;
|
// sleep = 0;
|
||||||
if (sleep < -msBuffer/2)
|
if (sleep < -bufferDuration/2)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (sleep > msBuffer/2)
|
else if (sleep > bufferDuration/2)
|
||||||
{
|
{
|
||||||
if (!chunk)
|
if (!chunk)
|
||||||
if (!chunks.try_pop(chunk, chronos::msec(timeout)))
|
if (!chunks.try_pop(chunk, chronos::msec(timeout)))
|
||||||
throw 0;
|
throw 0;
|
||||||
while (sleep > chunk->duration<chronos::msec>().count())
|
while (sleep > chunk->duration<chronos::msec>())
|
||||||
{
|
{
|
||||||
cout << "sleep > chunk->getDuration(): " << sleep << " > " << chunk->duration<chronos::msec>().count() << ", chunks: " << chunks.size() << ", out: " << outputBufferDacTime.count() << ", needed: " << msBuffer << "\n";
|
cout << "sleep > chunk->getDuration(): " << sleep.count() << " > " << chunk->duration<chronos::msec>().count() << ", chunks: " << chunks.size() << ", out: " << outputBufferDacTime.count() << ", needed: " << bufferDuration.count() << "\n";
|
||||||
if (!chunks.try_pop(chunk, chronos::msec(timeout)))
|
if (!chunks.try_pop(chunk, chronos::msec(timeout)))
|
||||||
throw 0;
|
throw 0;
|
||||||
msec age = std::chrono::duration_cast<msec>(TimeProvider::serverNow() - chunk->timePoint() - bufferMs + outputBufferDacTime);
|
msec age = std::chrono::duration_cast<msec>(TimeProvider::serverNow() - chunk->start() - bufferMs + outputBufferDacTime);
|
||||||
sleep = age.count();
|
sleep = age;
|
||||||
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 = chronos::usec(0);
|
||||||
}
|
}
|
||||||
else if (sleep < 0)
|
else if (sleep < -chronos::usec(100))
|
||||||
{
|
{
|
||||||
++sleep;
|
sleep += chronos::usec(100);
|
||||||
correction = -1;
|
correction = -chronos::usec(100);
|
||||||
}
|
}
|
||||||
else if (sleep > 0)
|
else if (sleep > chronos::usec(100))
|
||||||
{
|
{
|
||||||
--sleep;
|
sleep -= chronos::usec(100);
|
||||||
correction = 1;
|
correction = chronos::usec(100);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cout << "Sleep " << sleep.count() << "\n";
|
||||||
|
correction = sleep;
|
||||||
|
sleep = chronos::usec(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
long age(0);
|
chronos::usec age = std::chrono::duration_cast<usec>(TimeProvider::serverNow() - getNextPlayerChunk(outputBuffer, framesPerBuffer, timeout, correction) - bufferMs + outputBufferDacTime);
|
||||||
age = std::chrono::duration_cast<msec>(TimeProvider::serverNow() - getNextPlayerChunk(outputBuffer, framesPerBuffer, timeout, correction) - bufferMs + outputBufferDacTime).count();
|
|
||||||
|
|
||||||
|
if (sleep.count() == 0)
|
||||||
if (sleep == 0)
|
|
||||||
{
|
{
|
||||||
if (buffer.full() && (abs(median) > 1))
|
if (buffer.full() && (chronos::usec(abs(median)) > chronos::msec(1)))
|
||||||
{
|
{
|
||||||
cout << "pBuffer->full() && (abs(median) > 1): " << median << "\n";
|
cout << "pBuffer->full() && (abs(median) > 1): " << median << "\n";
|
||||||
sleep = median;
|
sleep = chronos::usec(median);
|
||||||
}
|
}
|
||||||
else if (shortBuffer.full() && (abs(shortMedian) > 5))
|
else if (shortBuffer.full() && (chronos::usec(abs(shortMedian)) > chronos::msec(5)))
|
||||||
{
|
{
|
||||||
cout << "pShortBuffer->full() && (abs(shortMedian) > 5): " << shortMedian << "\n";
|
cout << "pShortBuffer->full() && (abs(shortMedian) > 5): " << shortMedian << "\n";
|
||||||
sleep = shortMedian;
|
sleep = chronos::usec(shortMedian);
|
||||||
}
|
}
|
||||||
else if (miniBuffer.full() && (abs(miniBuffer.median()) > 50))
|
else if (miniBuffer.full() && (chronos::usec(abs(miniBuffer.median())) > chronos::msec(50)))
|
||||||
{
|
{
|
||||||
cout << "pMiniBuffer->full() && (abs(pMiniBuffer->mean()) > 50): " << miniBuffer.median() << "\n";
|
cout << "pMiniBuffer->full() && (abs(pMiniBuffer->mean()) > 50): " << miniBuffer.median() << "\n";
|
||||||
sleep = miniBuffer.mean();
|
sleep = chronos::usec((chronos::msec::rep)miniBuffer.mean());
|
||||||
}
|
}
|
||||||
else if (abs(age) > 200)
|
else if (chronos::abs(age) > chronos::msec(200))
|
||||||
{
|
{
|
||||||
cout << "age > 50: " << age << "\n";
|
cout << "age > 50: " << age.count() << "\n";
|
||||||
sleep = age;
|
sleep = age;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sleep != 0)
|
if (sleep.count() != 0)
|
||||||
std::cerr << "Sleep: " << sleep << "\n";
|
std::cerr << "Sleep: " << sleep.count() << "\n";
|
||||||
|
|
||||||
|
|
||||||
|
updateBuffers(age.count());
|
||||||
|
|
||||||
// std::cerr << "Chunk: " << age << "\t" << outputBufferDacTime*1000 << "\n";
|
// std::cerr << "Chunk: " << age << "\t" << outputBufferDacTime*1000 << "\n";
|
||||||
if (ticks > 2)
|
|
||||||
{
|
|
||||||
// cout << age << "\n";
|
|
||||||
updateBuffers(age);
|
|
||||||
}
|
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
if (now != lastUpdate)
|
if (now != lastUpdate)
|
||||||
{
|
{
|
||||||
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.count() << "\n";
|
std::cerr << "Chunk: " << age.count()/100 << "\t" << miniBuffer.median()/100 << "\t" << shortMedian/100 << "\t" << median/100 << "\t" << buffer.size() << "\t" << /*cardBuffer << "\t" <<*/ outputBufferDacTime.count() << "\n";
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch(int e)
|
catch(int e)
|
||||||
{
|
{
|
||||||
sleep = 0;
|
sleep = chronos::usec(0);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ public:
|
||||||
const SampleFormat& format;
|
const SampleFormat& format;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
chronos::time_point_hrc 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, const chronos::usec& correction = chronos::usec(0));
|
||||||
chronos::time_point_hrc getSilentPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer);
|
chronos::time_point_hrc getSilentPlayerChunk(void* outputBuffer, unsigned long framesPerBuffer);
|
||||||
chronos::time_point_hrc 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);
|
||||||
|
@ -37,7 +37,7 @@ private:
|
||||||
SampleFormat format_;
|
SampleFormat format_;
|
||||||
|
|
||||||
long lastTick;
|
long lastTick;
|
||||||
long sleep;
|
chronos::usec sleep;
|
||||||
|
|
||||||
Queue<std::shared_ptr<PcmChunk>> chunks;
|
Queue<std::shared_ptr<PcmChunk>> chunks;
|
||||||
DoubleBuffer<long> cardBuffer;
|
DoubleBuffer<long> cardBuffer;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
TimeProvider::TimeProvider() : diffToServer(0)
|
TimeProvider::TimeProvider() : diffToServer(0)
|
||||||
{
|
{
|
||||||
diffBuffer.setSize(120);
|
diffBuffer.setSize(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,22 +13,10 @@ void TimeProvider::setDiffToServer(double ms)
|
||||||
diffToServer = diffBuffer.median();
|
diffToServer = diffBuffer.median();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
long TimeProvider::getDiffToServer()
|
|
||||||
{
|
|
||||||
return diffToServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
long TimeProvider::getDiffToServerMs()
|
|
||||||
{
|
|
||||||
return diffToServer / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
long TimeProvider::getPercentileDiffToServer(size_t percentile)
|
long TimeProvider::getPercentileDiffToServer(size_t percentile)
|
||||||
{
|
{
|
||||||
return diffBuffer.percentile(percentile);
|
return diffBuffer.percentile(percentile);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
|
@ -18,10 +18,17 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void setDiffToServer(double ms);
|
void setDiffToServer(double ms);
|
||||||
long getDiffToServer();
|
|
||||||
long getPercentileDiffToServer(size_t percentile);
|
|
||||||
long getDiffToServerMs();
|
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline T getDiffToServer() const
|
||||||
|
{
|
||||||
|
return std::chrono::duration_cast<T>(chronos::usec(diffToServer));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* chronos::usec::rep getDiffToServer();
|
||||||
|
chronos::usec::rep getPercentileDiffToServer(size_t percentile);
|
||||||
|
long getDiffToServerMs();
|
||||||
|
*/
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static T sinceEpoche(const chronos::time_point_hrc& point)
|
static T sinceEpoche(const chronos::time_point_hrc& point)
|
||||||
|
@ -41,7 +48,7 @@ public:
|
||||||
|
|
||||||
inline static chronos::time_point_hrc serverNow()
|
inline static chronos::time_point_hrc serverNow()
|
||||||
{
|
{
|
||||||
return chronos::hrc::now() + chronos::usec(TimeProvider::getInstance().getDiffToServer());
|
return chronos::hrc::now() + TimeProvider::getInstance().getDiffToServer<chronos::usec>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -49,8 +56,8 @@ private:
|
||||||
TimeProvider(TimeProvider const&); // Don't Implement
|
TimeProvider(TimeProvider const&); // Don't Implement
|
||||||
void operator=(TimeProvider const&); // Don't implement
|
void operator=(TimeProvider const&); // Don't implement
|
||||||
|
|
||||||
DoubleBuffer<long> diffBuffer;
|
DoubleBuffer<chronos::usec::rep> diffBuffer;
|
||||||
std::atomic<long> diffToServer;
|
std::atomic<chronos::usec::rep> diffToServer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,13 @@
|
||||||
|
|
||||||
namespace chronos
|
namespace chronos
|
||||||
{
|
{
|
||||||
|
template <class Rep, class Period>
|
||||||
|
std::chrono::duration<Rep, Period> abs(std::chrono::duration<Rep, Period> d)
|
||||||
|
{
|
||||||
|
Rep x = d.count();
|
||||||
|
return std::chrono::duration<Rep, Period>(x >= 0 ? x : -x);
|
||||||
|
}
|
||||||
|
|
||||||
typedef std::chrono::high_resolution_clock hrc;
|
typedef std::chrono::high_resolution_clock hrc;
|
||||||
typedef std::chrono::time_point<hrc> time_point_hrc;
|
typedef std::chrono::time_point<hrc> time_point_hrc;
|
||||||
typedef std::chrono::seconds sec;
|
typedef std::chrono::seconds sec;
|
||||||
|
|
|
@ -17,8 +17,9 @@ public:
|
||||||
~PcmChunk();
|
~PcmChunk();
|
||||||
|
|
||||||
int readFrames(void* outputBuffer, size_t frameCount);
|
int readFrames(void* outputBuffer, size_t frameCount);
|
||||||
|
int seek(int frames);
|
||||||
|
|
||||||
inline chronos::time_point_hrc timePoint() const
|
inline chronos::time_point_hrc start() const
|
||||||
{
|
{
|
||||||
return chronos::time_point_hrc(
|
return chronos::time_point_hrc(
|
||||||
chronos::sec(timestamp.sec) +
|
chronos::sec(timestamp.sec) +
|
||||||
|
@ -27,7 +28,10 @@ public:
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
int seek(int frames);
|
inline chronos::time_point_hrc end() const
|
||||||
|
{
|
||||||
|
return start() + durationLeft<chronos::usec>();
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline T duration() const
|
inline T duration() const
|
||||||
|
|
|
@ -22,9 +22,19 @@ public:
|
||||||
uint16_t sampleSize;
|
uint16_t sampleSize;
|
||||||
uint16_t frameSize;
|
uint16_t frameSize;
|
||||||
|
|
||||||
double msRate() const
|
inline double msRate() const
|
||||||
{
|
{
|
||||||
return (double)rate/1000.f;
|
return (double)rate/1000.;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double usRate() const
|
||||||
|
{
|
||||||
|
return (double)rate/1000000.;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double nsRate() const
|
||||||
|
{
|
||||||
|
return (double)rate/1000000000.;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void read(std::istream& stream)
|
virtual void read(std::istream& stream)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue