This commit is contained in:
badaix 2015-07-10 09:39:12 +02:00
parent 1d22986619
commit aba04a0675
10 changed files with 202 additions and 221 deletions

View file

@ -16,9 +16,9 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "pcmChunk.h"
#include <string.h>
#include <iostream>
#include "pcmChunk.h"
#include "common/log.h"

View file

@ -75,6 +75,11 @@ public:
return (payloadSize / format.frameSize);
}
inline size_t getSampleCount() const
{
return (payloadSize / format.sampleSize);
}
SampleFormat format;
private:

View file

@ -16,12 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "sampleFormat.h"
#include <vector>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <iostream>
#include "sampleFormat.h"
#include "common/log.h"
namespace msg
{
@ -64,6 +66,7 @@ void SampleFormat::setFormat(uint32_t rate, uint16_t bits, uint16_t channels)
if (bits == 24)
sampleSize = 4;
frameSize = channels*sampleSize;
logD << "SampleFormat: " << rate << ":" << bits << ":" << channels << "\n";
}
}

View file

@ -25,6 +25,16 @@
namespace msg
{
// sample and frame as defined in alsa:
// http://www.alsa-project.org/main/index.php/FramesPeriods
// Say we want to work with a stereo, 16-bit, 44.1 KHz stream, one-way (meaning, either in playback or in capture direction). Then we have:
// 'stereo' = number of channels: 2
// 1 analog sample is represented with 16 bits = 2 bytes
// 1 frame represents 1 analog sample from all channels; here we have 2 channels, and so:
// 1 frame = (num_channels) * (1 sample in bytes) = (2 channels) * (2 bytes (16 bits) per sample) = 4 bytes (32 bits)
// To sustain 2x 44.1 KHz analog rate - the system must be capable of data transfer rate, in Bytes/sec:
// Bps_rate = (num_channels) * (1 sample in bytes) * (analog_rate) = (1 frame) * (analog_rate) = ( 2 channels ) * (2 bytes/sample) * (44100 samples/sec) = 2*2*44100 = 176400 Bytes/sec (link to formula img)
class SampleFormat : public BaseMessage
{
public:
@ -39,7 +49,10 @@ public:
uint16_t bits;
uint16_t channels;
// size in [bytes] of a single mono sample, e.g. 2 bytes (= 16 bits)
uint16_t sampleSize;
// size in [bytes] of a frame (sum of sample sizes = #channel*sampleSize), e.g. 4 bytes (= 2 channel * 16 bit)
uint16_t frameSize;
inline double msRate() const

View file

@ -26,26 +26,26 @@
class Encoder
{
public:
Encoder(const msg::SampleFormat& format) : sampleFormat(format), headerChunk(NULL)
Encoder(const msg::SampleFormat& format) : sampleFormat_(format), headerChunk_(NULL)
{
}
virtual ~Encoder()
{
if (headerChunk != NULL)
delete headerChunk;
if (headerChunk_ != NULL)
delete headerChunk_;
}
virtual double encode(msg::PcmChunk* chunk) = 0;
virtual msg::Header* getHeader()
{
return headerChunk;
return headerChunk_;
}
protected:
msg::SampleFormat sampleFormat;
msg::Header* headerChunk;
msg::SampleFormat sampleFormat_;
msg::Header* headerChunk_;
};

View file

@ -22,65 +22,87 @@
using namespace std;
#define READSIZE 16384
static FLAC__int32 pcm[READSIZE/*samples*/ * 2/*channels*/];
size_t encodedSamples = 0;
static msg::PcmChunk* encodedChunk = NULL;
FlacEncoder::FlacEncoder(const msg::SampleFormat& format) : Encoder(format), encoder(NULL)
FlacEncoder::FlacEncoder(const msg::SampleFormat& format) : Encoder(format), encoder_(NULL), pcmBufferSize_(0), encodedSamples_(0)
{
encodedChunk = new msg::PcmChunk();
headerChunk = new msg::Header("flac");
encodedChunk_ = new msg::PcmChunk();
headerChunk_ = new msg::Header("flac");
pcmBuffer_ = (FLAC__int32*)malloc(pcmBufferSize_ * sizeof(FLAC__int32));
initEncoder();
}
FlacEncoder::~FlacEncoder()
{
if (encoder != NULL)
if (encoder_ != NULL)
{
FLAC__stream_encoder_finish(encoder);
FLAC__metadata_object_delete(metadata[0]);
FLAC__metadata_object_delete(metadata[1]);
FLAC__stream_encoder_delete(encoder);
FLAC__stream_encoder_finish(encoder_);
FLAC__metadata_object_delete(metadata_[0]);
FLAC__metadata_object_delete(metadata_[1]);
FLAC__stream_encoder_delete(encoder_);
}
delete encodedChunk;
}
msg::Header* FlacEncoder::getHeaderChunk()
{
return headerChunk;
delete encodedChunk_;
free(pcmBuffer_);
}
double FlacEncoder::encode(msg::PcmChunk* chunk)
{
logD << "payload: " << chunk->payloadSize << "\tsamples: " << chunk->payloadSize/4 << "\n";
int samples = chunk->payloadSize / 4;
for(int i=0; i<samples*2/*channels*/; i++)
{
pcm[i] = (FLAC__int32)(((FLAC__int16)(FLAC__int8)chunk->payload[2*i+1] << 8) | (FLAC__int16)(0x00ff&chunk->payload[2*i]));
}
FLAC__stream_encoder_process_interleaved(encoder, pcm, samples);
int samples = chunk->getSampleCount();
int frames = chunk->getFrameCount();
logO << "payload: " << chunk->payloadSize << "\tframes: " << frames << "\tsamples: " << samples << "\tduration: " << chunk->duration<chronos::msec>().count() << "\n";
double res = encodedSamples / ((double)sampleFormat.rate / 1000.);
if (encodedSamples > 0)
if (pcmBufferSize_ < samples)
{
logD << "encoded: " << chunk->payloadSize << "\tsamples: " << encodedSamples << "\tres: " << res << "\n";
encodedSamples = 0;
chunk->payloadSize = encodedChunk->payloadSize;
chunk->payload = (char*)realloc(chunk->payload, encodedChunk->payloadSize);
memcpy(chunk->payload, encodedChunk->payload, encodedChunk->payloadSize);
encodedChunk->payloadSize = 0;
encodedChunk->payload = (char*)realloc(encodedChunk->payload, 0);
pcmBufferSize_ = samples;
pcmBuffer_ = (FLAC__int32*)realloc(pcmBuffer_, pcmBufferSize_ * sizeof(FLAC__int32));
}
return res;//chunk->duration<chronos::msec>().count();
for(int i=0; i<samples; i++)
{
pcmBuffer_[i] = (FLAC__int32)(((FLAC__int16)(FLAC__int8)chunk->payload[2*i+1] << 8) | (FLAC__int16)(0x00ff&chunk->payload[2*i]));
}
FLAC__stream_encoder_process_interleaved(encoder_, pcmBuffer_, frames);
double resMs = encodedSamples_ / ((double)sampleFormat_.rate / 1000.);
if (encodedSamples_ > 0)
{
logD << "encoded: " << chunk->payloadSize << "\tframes: " << encodedSamples_ << "\tres: " << resMs << "\n";
encodedSamples_ = 0;
chunk->payloadSize = encodedChunk_->payloadSize;
chunk->payload = (char*)realloc(chunk->payload, encodedChunk_->payloadSize);
memcpy(chunk->payload, encodedChunk_->payload, encodedChunk_->payloadSize);
encodedChunk_->payloadSize = 0;
encodedChunk_->payload = (char*)realloc(encodedChunk_->payload, 0);
}
return resMs;//chunk->duration<chronos::msec>().count();
}
FLAC__StreamEncoderWriteStatus FlacEncoder::write_callback(const FLAC__StreamEncoder *encoder,
const FLAC__byte buffer[],
size_t bytes,
unsigned samples,
unsigned current_frame)
{
logD << "write_callback: " << bytes << ", " << samples << ", " << current_frame << "\n";
if ((current_frame == 0) && (bytes > 0) && (samples == 0))
{
headerChunk_->payload = (char*)realloc(headerChunk_->payload, headerChunk_->payloadSize + bytes);
memcpy(headerChunk_->payload + headerChunk_->payloadSize, buffer, bytes);
headerChunk_->payloadSize += bytes;
}
else
{
encodedChunk_->payload = (char*)realloc(encodedChunk_->payload, encodedChunk_->payloadSize + bytes);
memcpy(encodedChunk_->payload + encodedChunk_->payloadSize, buffer, bytes);
encodedChunk_->payloadSize += bytes;
encodedSamples_ += samples;
}
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
}
@ -91,27 +113,9 @@ FLAC__StreamEncoderWriteStatus write_callback(const FLAC__StreamEncoder *encoder
unsigned current_frame,
void *client_data)
{
logD << "write_callback: " << bytes << ", " << samples << ", " << current_frame << "\n";
FlacEncoder* flacEncoder = (FlacEncoder*)client_data;
if ((current_frame == 0) && (bytes > 0) && (samples == 0))
{
msg::Header* headerChunk = flacEncoder->getHeaderChunk();
headerChunk->payload = (char*)realloc(headerChunk->payload, headerChunk->payloadSize + bytes);
memcpy(headerChunk->payload + headerChunk->payloadSize, buffer, bytes);
headerChunk->payloadSize += bytes;
return flacEncoder->write_callback(encoder, buffer, bytes, samples, current_frame);
}
else
{
encodedChunk->payload = (char*)realloc(encodedChunk->payload, encodedChunk->payloadSize + bytes);
memcpy(encodedChunk->payload + encodedChunk->payloadSize, buffer, bytes);
encodedChunk->payloadSize += bytes;
encodedSamples += samples;
}
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
}
void FlacEncoder::initEncoder()
@ -121,93 +125,53 @@ void FlacEncoder::initEncoder()
FLAC__StreamMetadata_VorbisComment_Entry entry;
// allocate the encoder
if((encoder = FLAC__stream_encoder_new()) == NULL) {
if ((encoder_ = FLAC__stream_encoder_new()) == NULL)
{
fprintf(stderr, "ERROR: allocating encoder\n");
return;
}
ok &= FLAC__stream_encoder_set_verify(encoder, true);
ok &= FLAC__stream_encoder_set_compression_level(encoder, 5);
ok &= FLAC__stream_encoder_set_channels(encoder, sampleFormat.channels);
ok &= FLAC__stream_encoder_set_bits_per_sample(encoder, sampleFormat.bits);
ok &= FLAC__stream_encoder_set_sample_rate(encoder, sampleFormat.rate);
ok &= FLAC__stream_encoder_set_verify(encoder_, true);
// compression levels (0-8):
// https://xiph.org/flac/api/group__flac__stream__encoder.html#gae49cf32f5256cb47eecd33779493ac85
// latency:
// 0-2: 1152 frames, ~26.1224ms
// 3-8: 4096 frames, ~92.8798ms
ok &= FLAC__stream_encoder_set_compression_level(encoder_, 2);
ok &= FLAC__stream_encoder_set_channels(encoder_, sampleFormat_.channels);
ok &= FLAC__stream_encoder_set_bits_per_sample(encoder_, sampleFormat_.bits);
ok &= FLAC__stream_encoder_set_sample_rate(encoder_, sampleFormat_.rate);
// now add some metadata; we'll add some tags and a padding block
if(ok) {
if (ok)
{
if (
(metadata[0] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT)) == NULL ||
(metadata[1] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING)) == NULL ||
(metadata_[0] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT)) == NULL ||
(metadata_[1] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING)) == NULL ||
// there are many tag (vorbiscomment) functions but these are convenient for this particular use:
!FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, "ARTIST", "Some Artist") ||
!FLAC__metadata_object_vorbiscomment_append_comment(metadata[0], entry, false) ||
!FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, "YEAR", "1984") ||
!FLAC__metadata_object_vorbiscomment_append_comment(metadata[0], entry, false)
) {
!FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, "TITLE", "SnapStream") ||
!FLAC__metadata_object_vorbiscomment_append_comment(metadata_[0], entry, false) ||
!FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, "VERSION", VERSION) ||
!FLAC__metadata_object_vorbiscomment_append_comment(metadata_[0], entry, false)
)
{
fprintf(stderr, "ERROR: out of memory or tag error\n");
ok = false;
}
metadata[1]->length = 1234; // set the padding length
ok = FLAC__stream_encoder_set_metadata(encoder, metadata, 2);
metadata_[1]->length = 1234; // set the padding length
ok = FLAC__stream_encoder_set_metadata(encoder_, metadata_, 2);
}
// initialize encoder
if(ok) {
init_status = FLAC__stream_encoder_init_stream(encoder, write_callback, NULL, NULL, NULL, this);
if(init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
if (ok)
{
init_status = FLAC__stream_encoder_init_stream(encoder_, ::write_callback, NULL, NULL, NULL, this);
if(init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK)
{
fprintf(stderr, "ERROR: initializing encoder: %s\n", FLAC__StreamEncoderInitStatusString[init_status]);
ok = false;
}
}
/*
// read blocks of samples from WAVE file and feed to encoder
if(ok) {
size_t left = (size_t)total_samples;
while(ok && left) {
size_t need = (left>READSIZE? (size_t)READSIZE : (size_t)left);
if(fread(buffer, channels*(bps/8), need, fin) != need) {
fprintf(stderr, "ERROR: reading from WAVE file\n");
ok = false;
}
else {
// convert the packed little-endian 16-bit PCM samples from WAVE into an interleaved FLAC__int32 buffer for libFLAC
size_t i;
for(i = 0; i < need*channels; i++) {
// inefficient but simple and works on big- or little-endian machines
pcm[i] = (FLAC__int32)(((FLAC__int16)(FLAC__int8)buffer[2*i+1] << 8) | (FLAC__int16)buffer[2*i]);
}
// feed samples to encoder
ok = FLAC__stream_encoder_process_interleaved(encoder, pcm, need);
}
left -= need;
}
}
ok &= FLAC__stream_encoder_finish(encoder);
fprintf(stderr, "encoding: %s\n", ok? "succeeded" : "FAILED");
fprintf(stderr, " state: %s\n", FLAC__StreamEncoderStateString[FLAC__stream_encoder_get_state(encoder)]);
// now that encoding is finished, the metadata can be freed
FLAC__metadata_object_delete(metadata[0]);
FLAC__metadata_object_delete(metadata[1]);
FLAC__stream_encoder_delete(encoder);
fclose(fin);
return 0;
*/
}
/*
void progress_callback(const FLAC__StreamEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate, void *client_data)
{
(void)encoder, (void)client_data;
fprintf(stderr, "wrote %d bytes, %d, %u samples, %u/%u frames\n", bytes_written, samples_written, total_samples, frames_written, total_frames_estimate);
}
*/

View file

@ -33,13 +33,20 @@ public:
FlacEncoder(const msg::SampleFormat& format);
~FlacEncoder();
virtual double encode(msg::PcmChunk* chunk);
msg::Header* getHeaderChunk();
FLAC__StreamEncoderWriteStatus write_callback(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame);
protected:
void initEncoder();
FLAC__StreamEncoder *encoder;
FLAC__StreamMetadata *metadata[2];
// virtual void progress_callback(FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate);
FLAC__StreamEncoder *encoder_;
FLAC__StreamMetadata *metadata_[2];
FLAC__int32 *pcmBuffer_;
int pcmBufferSize_;
msg::PcmChunk* encodedChunk_;
size_t encodedSamples_;
};

View file

@ -24,10 +24,9 @@
using namespace std;
OggEncoder::OggEncoder(const msg::SampleFormat& format) : Encoder(format), eos(0)
OggEncoder::OggEncoder(const msg::SampleFormat& format) : Encoder(format), lastGranulepos(0), eos(0)
{
init();
lastGranulepos = -1;
}
@ -35,7 +34,7 @@ OggEncoder::OggEncoder(const msg::SampleFormat& format) : Encoder(format), eos(0
double OggEncoder::encode(msg::PcmChunk* chunk)
{
double res = 0;
//logD << "-> pcm: " << wireChunk->length << endl;
logO << "payload: " << chunk->payloadSize << "\tframes: " << chunk->getFrameCount() << "\tduration: " << chunk->duration<chronos::msec>().count() << "\n";
int bytes = chunk->payloadSize / 4;
float **buffer=vorbis_analysis_buffer(&vd, bytes);
@ -43,13 +42,6 @@ double OggEncoder::encode(msg::PcmChunk* chunk)
for (int i=0; i<bytes; i++)
{
int idx = 4*i;
/* int8_t high = chunk->payload[idx+1];
int8_t low = chunk->payload[idx];
buffer[0][i]=((high << 8) | (0x00ff&low))/32768.f;
high = chunk->payload[idx+3];
low = chunk->payload[idx+2];
buffer[1][i]=((high << 8) | (0x00ff&low))/32768.f;
*/
buffer[0][i]=((((int8_t)chunk->payload[idx+1]) << 8) | (0x00ff&((int8_t)chunk->payload[idx])))/32768.f;
buffer[1][i]=((((int8_t)chunk->payload[idx+3]) << 8) | (0x00ff&((int8_t)chunk->payload[idx+2])))/32768.f;
}
@ -75,13 +67,13 @@ double OggEncoder::encode(msg::PcmChunk* chunk)
/* write out pages (if any) */
while (true)
{
// int result = ogg_stream_pageout(&os,&og);
int result = ogg_stream_flush(&os, &og);
if (result == 0)
break;
res = true;
res = os.granulepos - lastGranulepos;
size_t nextLen = pos + og.header_len + og.body_len;
// make chunk larger
if (chunk->payloadSize < nextLen)
chunk->payload = (char*)realloc(chunk->payload, nextLen);
@ -95,17 +87,17 @@ double OggEncoder::encode(msg::PcmChunk* chunk)
}
}
}
if (res)
if (res > 0)
{
if (lastGranulepos == -1)
res = os.granulepos;
else
res = os.granulepos - lastGranulepos;
res /= (sampleFormat.rate / 1000.);
res /= (sampleFormat_.rate / 1000.);
logO << "res: " << res << "\n";
lastGranulepos = os.granulepos;
// make chunk smaller
chunk->payload = (char*)realloc(chunk->payload, pos);
chunk->payloadSize = pos;
}
return res;
}
@ -144,17 +136,19 @@ void OggEncoder::init()
*********************************************************************/
ret=vorbis_encode_init_vbr(&vi, sampleFormat.channels, sampleFormat.rate, 1.0);
ret = vorbis_encode_init_vbr(&vi, sampleFormat_.channels, sampleFormat_.rate, 1.0);
/* do not continue if setup failed; this can happen if we ask for a
mode that libVorbis does not support (eg, too low a bitrate, etc,
will return 'OV_EIMPL') */
if(ret)exit(1);
if (ret)
exit(1);
/* add a comment */
vorbis_comment_init(&vc);
vorbis_comment_add_tag(&vc,"ENCODER","snapstream");
vorbis_comment_add_tag(&vc, "TITLE", "SnapStream");
vorbis_comment_add_tag(&vc, "VERSION", VERSION);
/* set up the analysis state and auxiliary encoding storage */
vorbis_analysis_init(&vd,&vi);
@ -185,28 +179,21 @@ void OggEncoder::init()
/* This ensures the actual
* audio data will start on a new page, as per spec
*/
// while(!eos){
size_t pos(0);
headerChunk = new msg::Header("ogg");
headerChunk_ = new msg::Header("ogg");
while (true)
{
int result=ogg_stream_flush(&os,&og);
if (result == 0)
break;
headerChunk->payloadSize += og.header_len + og.body_len;
headerChunk->payload = (char*)realloc(headerChunk->payload, headerChunk->payloadSize);
headerChunk_->payloadSize += og.header_len + og.body_len;
headerChunk_->payload = (char*)realloc(headerChunk_->payload, headerChunk_->payloadSize);
logD << "HeadLen: " << og.header_len << ", bodyLen: " << og.body_len << ", result: " << result << "\n";
memcpy(headerChunk->payload + pos, og.header, og.header_len);
memcpy(headerChunk_->payload + pos, og.header, og.header_len);
pos += og.header_len;
memcpy(headerChunk->payload + pos, og.body, og.body_len);
memcpy(headerChunk_->payload + pos, og.body, og.body_len);
pos += og.body_len;
}
// fwrite(og.header,1,og.header_len,stdout);
// fwrite(og.body,1,og.body_len,stdout);
// }
}

View file

@ -20,7 +20,7 @@
PcmEncoder::PcmEncoder(const msg::SampleFormat& format) : Encoder(format)
{
headerChunk = new msg::Header("pcm");
headerChunk_ = new msg::Header("pcm");
}

View file

@ -88,66 +88,68 @@ int main(int argc, char* argv[])
return 1;
}
if (runAsDaemon)
{
daemonize("/var/run/snapserver.pid");
logS(kLogNotice) << "daemon started." << endl;
}
std::clog.rdbuf(new Log("snapserver", LOG_DAEMON));
using namespace std; // For atoi.
timeval tvChunk;
gettimeofday(&tvChunk, NULL);
long nextTick = chronos::getTickCount();
umask(0);
mkfifo(fifoName.c_str(), 0666);
msg::SampleFormat format(sampleFormat);
size_t duration = 50;
//size_t chunkSize = duration*format.rate*format.frameSize / 1000;
std::unique_ptr<Encoder> encoder;
if (codec == "ogg")
encoder.reset(new OggEncoder(sampleFormat));
encoder.reset(new OggEncoder(format));
else if (codec == "pcm")
encoder.reset(new PcmEncoder(sampleFormat));
encoder.reset(new PcmEncoder(format));
else if (codec == "flac")
encoder.reset(new FlacEncoder(sampleFormat));
encoder.reset(new FlacEncoder(format));
else
{
cout << "unknown codec: " << codec << "\n";
return 1;
}
umask(0);
mkfifo(fifoName.c_str(), 0666);
int fd = open(fifoName.c_str(), O_RDONLY | O_NONBLOCK);
if (fd == -1)
{
cout << "failed to open fifo: " << fifoName << "\n";
return 1;
}
msg::ServerSettings serverSettings;
serverSettings.bufferMs = bufferMs;
signal(SIGHUP, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
if (runAsDaemon)
{
daemonize("/var/run/snapserver.pid");
logS(kLogNotice) << "daemon started." << endl;
}
ControlServer* controlServer = new ControlServer(port);
controlServer->setServerSettings(&serverSettings);
controlServer->setFormat(&format);
controlServer->setHeader(encoder->getHeader());
controlServer->start();
signal(SIGHUP, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
PublishAvahi publishAvahi("SnapCast");
std::vector<AvahiService> services;
services.push_back(AvahiService("_snapcast._tcp", port));
publishAvahi.publish(services);
timeval tvChunk;
gettimeofday(&tvChunk, NULL);
long nextTick = chronos::getTickCount();
size_t pcmReadMs = 20;
while (!g_terminated)
{
int fd = open(fifoName.c_str(), O_RDONLY | O_NONBLOCK);
try
{
shared_ptr<msg::PcmChunk> chunk;
while (!g_terminated)//cin.good())
{
chunk.reset(new msg::PcmChunk(sampleFormat, duration));
chunk.reset(new msg::PcmChunk(sampleFormat, pcmReadMs));
int toRead = chunk->payloadSize;
int len = 0;
do
@ -166,9 +168,9 @@ int main(int argc, char* argv[])
double chunkDuration = encoder->encode(chunk.get());
if (chunkDuration > 0)
controlServer->send(chunk);
//logD << chunk->tv_sec << ", " << chunk->tv_usec / 1000 << "\n";
// logO << chunkDuration << "\n";
// addUs(tvChunk, 1000*chunk->getDuration());
nextTick += duration;
nextTick += pcmReadMs;
chronos::addUs(tvChunk, chunkDuration * 1000);
long currentTick = chronos::getTickCount();
if (nextTick > currentTick)