mirror of
https://github.com/badaix/snapcast.git
synced 2025-05-18 11:36:14 +02:00
Change SampleFormat variables to getters
This commit is contained in:
parent
96856ae5a6
commit
1bc8f74c41
17 changed files with 164 additions and 140 deletions
|
@ -62,7 +62,7 @@ void Controller::onMessageReceived(ClientConnection* /*connection*/, const msg::
|
||||||
{
|
{
|
||||||
auto pcmChunk = make_unique<msg::PcmChunk>(sampleFormat_, 0);
|
auto pcmChunk = make_unique<msg::PcmChunk>(sampleFormat_, 0);
|
||||||
pcmChunk->deserialize(baseMessage, buffer);
|
pcmChunk->deserialize(baseMessage, buffer);
|
||||||
// LOG(DEBUG) << "chunk: " << pcmChunk->payloadSize << ", sampleFormat: " << sampleFormat_.rate << "\n";
|
// LOG(DEBUG) << "chunk: " << pcmChunk->payloadSize << ", sampleFormat: " << sampleFormat_.getFormat() << "\n";
|
||||||
if (decoder_->decode(pcmChunk.get()))
|
if (decoder_->decode(pcmChunk.get()))
|
||||||
{
|
{
|
||||||
// TODO: do decoding in thread?
|
// TODO: do decoding in thread?
|
||||||
|
@ -119,7 +119,7 @@ void Controller::onMessageReceived(ClientConnection* /*connection*/, const msg::
|
||||||
throw SnapException("codec not supported: \"" + headerChunk_->codec + "\"");
|
throw SnapException("codec not supported: \"" + headerChunk_->codec + "\"");
|
||||||
|
|
||||||
sampleFormat_ = decoder_->setHeader(headerChunk_.get());
|
sampleFormat_ = decoder_->setHeader(headerChunk_.get());
|
||||||
LOG(NOTICE) << TAG("state") << "sampleformat: " << sampleFormat_.rate << ":" << sampleFormat_.bits << ":" << sampleFormat_.channels << "\n";
|
LOG(NOTICE) << TAG("state") << "sampleformat: " << sampleFormat_.getFormat() << "\n";
|
||||||
|
|
||||||
stream_ = make_shared<Stream>(sampleFormat_, settings_.player.sample_format);
|
stream_ = make_shared<Stream>(sampleFormat_, settings_.player.sample_format);
|
||||||
stream_->setBufferLen(serverSettings_->getBufferMs() - settings_.player.latency);
|
stream_->setBufferLen(serverSettings_->getBufferMs() - settings_.player.latency);
|
||||||
|
|
|
@ -91,12 +91,11 @@ bool FlacDecoder::decode(msg::PcmChunk* chunk)
|
||||||
|
|
||||||
if ((cacheInfo_.cachedBlocks_ > 0) && (cacheInfo_.sampleRate_ != 0))
|
if ((cacheInfo_.cachedBlocks_ > 0) && (cacheInfo_.sampleRate_ != 0))
|
||||||
{
|
{
|
||||||
double diffMs = cacheInfo_.cachedBlocks_ / ((double)cacheInfo_.sampleRate_ / 1000.);
|
double diffMs = static_cast<double>(cacheInfo_.cachedBlocks_) / (static_cast<double>(cacheInfo_.sampleRate_) / 1000.);
|
||||||
int32_t s = (diffMs / 1000);
|
auto us = static_cast<uint64_t>(diffMs * 1000.);
|
||||||
int32_t us = (diffMs * 1000);
|
tv diff(us / 1000000, us % 1000000);
|
||||||
us %= 1000000;
|
LOG(DEBUG) << "Cached: " << cacheInfo_.cachedBlocks_ << ", " << diffMs << "ms, " << diff.sec << "s, " << diff.usec << "us\n";
|
||||||
LOG(DEBUG) << "Cached: " << cacheInfo_.cachedBlocks_ << ", " << diffMs << "ms, " << s << "s, " << us << "us\n";
|
chunk->timestamp = chunk->timestamp - diff;
|
||||||
chunk->timestamp = chunk->timestamp - tv(s, us);
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -116,9 +115,8 @@ SampleFormat FlacDecoder::setHeader(msg::CodecHeader* chunk)
|
||||||
if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK)
|
if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK)
|
||||||
throw SnapException("ERROR: initializing decoder: " + string(FLAC__StreamDecoderInitStatusString[init_status]));
|
throw SnapException("ERROR: initializing decoder: " + string(FLAC__StreamDecoderInitStatusString[init_status]));
|
||||||
|
|
||||||
sampleFormat.rate = 0;
|
|
||||||
FLAC__stream_decoder_process_until_end_of_metadata(decoder);
|
FLAC__stream_decoder_process_until_end_of_metadata(decoder);
|
||||||
if (sampleFormat.rate == 0)
|
if (sampleFormat.rate() == 0)
|
||||||
throw SnapException("Sample format not found");
|
throw SnapException("Sample format not found");
|
||||||
|
|
||||||
return sampleFormat;
|
return sampleFormat;
|
||||||
|
@ -158,7 +156,7 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder* /*decod
|
||||||
{
|
{
|
||||||
if (pcmChunk != nullptr)
|
if (pcmChunk != nullptr)
|
||||||
{
|
{
|
||||||
size_t bytes = frame->header.blocksize * sampleFormat.frameSize;
|
size_t bytes = frame->header.blocksize * sampleFormat.frameSize();
|
||||||
|
|
||||||
FlacDecoder* flacDecoder = static_cast<FlacDecoder*>(client_data);
|
FlacDecoder* flacDecoder = static_cast<FlacDecoder*>(client_data);
|
||||||
if (flacDecoder->cacheInfo_.isCachedChunk_)
|
if (flacDecoder->cacheInfo_.isCachedChunk_)
|
||||||
|
@ -166,7 +164,7 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder* /*decod
|
||||||
|
|
||||||
pcmChunk->payload = (char*)realloc(pcmChunk->payload, pcmChunk->payloadSize + bytes);
|
pcmChunk->payload = (char*)realloc(pcmChunk->payload, pcmChunk->payloadSize + bytes);
|
||||||
|
|
||||||
for (size_t channel = 0; channel < sampleFormat.channels; ++channel)
|
for (size_t channel = 0; channel < sampleFormat.channels(); ++channel)
|
||||||
{
|
{
|
||||||
if (buffer[channel] == nullptr)
|
if (buffer[channel] == nullptr)
|
||||||
{
|
{
|
||||||
|
@ -174,23 +172,23 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder* /*decod
|
||||||
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sampleFormat.sampleSize == 1)
|
if (sampleFormat.sampleSize() == 1)
|
||||||
{
|
{
|
||||||
int8_t* chunkBuffer = (int8_t*)(pcmChunk->payload + pcmChunk->payloadSize);
|
int8_t* chunkBuffer = (int8_t*)(pcmChunk->payload + pcmChunk->payloadSize);
|
||||||
for (size_t i = 0; i < frame->header.blocksize; i++)
|
for (size_t i = 0; i < frame->header.blocksize; i++)
|
||||||
chunkBuffer[sampleFormat.channels * i + channel] = (int8_t)(buffer[channel][i]);
|
chunkBuffer[sampleFormat.channels() * i + channel] = (int8_t)(buffer[channel][i]);
|
||||||
}
|
}
|
||||||
else if (sampleFormat.sampleSize == 2)
|
else if (sampleFormat.sampleSize() == 2)
|
||||||
{
|
{
|
||||||
int16_t* chunkBuffer = (int16_t*)(pcmChunk->payload + pcmChunk->payloadSize);
|
int16_t* chunkBuffer = (int16_t*)(pcmChunk->payload + pcmChunk->payloadSize);
|
||||||
for (size_t i = 0; i < frame->header.blocksize; i++)
|
for (size_t i = 0; i < frame->header.blocksize; i++)
|
||||||
chunkBuffer[sampleFormat.channels * i + channel] = SWAP_16((int16_t)(buffer[channel][i]));
|
chunkBuffer[sampleFormat.channels() * i + channel] = SWAP_16((int16_t)(buffer[channel][i]));
|
||||||
}
|
}
|
||||||
else if (sampleFormat.sampleSize == 4)
|
else if (sampleFormat.sampleSize() == 4)
|
||||||
{
|
{
|
||||||
int32_t* chunkBuffer = (int32_t*)(pcmChunk->payload + pcmChunk->payloadSize);
|
int32_t* chunkBuffer = (int32_t*)(pcmChunk->payload + pcmChunk->payloadSize);
|
||||||
for (size_t i = 0; i < frame->header.blocksize; i++)
|
for (size_t i = 0; i < frame->header.blocksize; i++)
|
||||||
chunkBuffer[sampleFormat.channels * i + channel] = SWAP_32((int32_t)(buffer[channel][i]));
|
chunkBuffer[sampleFormat.channels() * i + channel] = SWAP_32((int32_t)(buffer[channel][i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pcmChunk->payloadSize += bytes;
|
pcmChunk->payloadSize += bytes;
|
||||||
|
|
|
@ -104,16 +104,16 @@ bool OggDecoder::decode(msg::PcmChunk* chunk)
|
||||||
(-1.<=range<=1.) to whatever PCM format and write it out */
|
(-1.<=range<=1.) to whatever PCM format and write it out */
|
||||||
while ((samples = vorbis_synthesis_pcmout(&vd, &pcm)) > 0)
|
while ((samples = vorbis_synthesis_pcmout(&vd, &pcm)) > 0)
|
||||||
{
|
{
|
||||||
size_t bytes = sampleFormat_.sampleSize * vi.channels * samples;
|
size_t bytes = sampleFormat_.sampleSize() * vi.channels * samples;
|
||||||
chunk->payload = (char*)realloc(chunk->payload, chunk->payloadSize + bytes);
|
chunk->payload = (char*)realloc(chunk->payload, chunk->payloadSize + bytes);
|
||||||
for (int channel = 0; channel < vi.channels; ++channel)
|
for (int channel = 0; channel < vi.channels; ++channel)
|
||||||
{
|
{
|
||||||
if (sampleFormat_.sampleSize == 1)
|
if (sampleFormat_.sampleSize() == 1)
|
||||||
{
|
{
|
||||||
int8_t* chunkBuffer = (int8_t*)(chunk->payload + chunk->payloadSize);
|
int8_t* chunkBuffer = (int8_t*)(chunk->payload + chunk->payloadSize);
|
||||||
for (int i = 0; i < samples; i++)
|
for (int i = 0; i < samples; i++)
|
||||||
{
|
{
|
||||||
int8_t& val = chunkBuffer[sampleFormat_.channels * i + channel];
|
int8_t& val = chunkBuffer[sampleFormat_.channels() * i + channel];
|
||||||
#ifdef HAS_TREMOR
|
#ifdef HAS_TREMOR
|
||||||
val = clip<int8_t>(pcm[channel][i], -128, 127);
|
val = clip<int8_t>(pcm[channel][i], -128, 127);
|
||||||
#else
|
#else
|
||||||
|
@ -121,12 +121,12 @@ bool OggDecoder::decode(msg::PcmChunk* chunk)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (sampleFormat_.sampleSize == 2)
|
else if (sampleFormat_.sampleSize() == 2)
|
||||||
{
|
{
|
||||||
int16_t* chunkBuffer = (int16_t*)(chunk->payload + chunk->payloadSize);
|
int16_t* chunkBuffer = (int16_t*)(chunk->payload + chunk->payloadSize);
|
||||||
for (int i = 0; i < samples; i++)
|
for (int i = 0; i < samples; i++)
|
||||||
{
|
{
|
||||||
int16_t& val = chunkBuffer[sampleFormat_.channels * i + channel];
|
int16_t& val = chunkBuffer[sampleFormat_.channels() * i + channel];
|
||||||
#ifdef HAS_TREMOR
|
#ifdef HAS_TREMOR
|
||||||
val = SWAP_16(clip<int16_t>(pcm[channel][i] >> 9, -32768, 32767));
|
val = SWAP_16(clip<int16_t>(pcm[channel][i] >> 9, -32768, 32767));
|
||||||
#else
|
#else
|
||||||
|
@ -134,12 +134,12 @@ bool OggDecoder::decode(msg::PcmChunk* chunk)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (sampleFormat_.sampleSize == 4)
|
else if (sampleFormat_.sampleSize() == 4)
|
||||||
{
|
{
|
||||||
int32_t* chunkBuffer = (int32_t*)(chunk->payload + chunk->payloadSize);
|
int32_t* chunkBuffer = (int32_t*)(chunk->payload + chunk->payloadSize);
|
||||||
for (int i = 0; i < samples; i++)
|
for (int i = 0; i < samples; i++)
|
||||||
{
|
{
|
||||||
int32_t& val = chunkBuffer[sampleFormat_.channels * i + channel];
|
int32_t& val = chunkBuffer[sampleFormat_.channels() * i + channel];
|
||||||
#ifdef HAS_TREMOR
|
#ifdef HAS_TREMOR
|
||||||
val = SWAP_32(clip<int32_t>(pcm[channel][i] << 7, -2147483648, 2147483647));
|
val = SWAP_32(clip<int32_t>(pcm[channel][i] << 7, -2147483648, 2147483647));
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -50,13 +50,13 @@ bool OpusDecoder::decode(msg::PcmChunk* chunk)
|
||||||
{
|
{
|
||||||
int frame_size = 0;
|
int frame_size = 0;
|
||||||
|
|
||||||
while ((frame_size = opus_decode(dec_, (unsigned char*)chunk->payload, chunk->payloadSize, pcm_.data(), pcm_.size() / sample_format_.channels, 0)) ==
|
while ((frame_size = opus_decode(dec_, (unsigned char*)chunk->payload, chunk->payloadSize, pcm_.data(), pcm_.size() / sample_format_.channels(), 0)) ==
|
||||||
OPUS_BUFFER_TOO_SMALL)
|
OPUS_BUFFER_TOO_SMALL)
|
||||||
{
|
{
|
||||||
if (pcm_.size() < const_max_frame_size * sample_format_.channels)
|
if (pcm_.size() < const_max_frame_size * sample_format_.channels())
|
||||||
{
|
{
|
||||||
pcm_.resize(pcm_.size() * 2);
|
pcm_.resize(pcm_.size() * 2);
|
||||||
LOG(INFO) << "OPUS encoding buffer too small, resizing to " << pcm_.size() / sample_format_.channels << " samples per channel\n";
|
LOG(INFO) << "OPUS encoding buffer too small, resizing to " << pcm_.size() / sample_format_.channels() << " samples per channel\n";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
|
@ -72,7 +72,7 @@ bool OpusDecoder::decode(msg::PcmChunk* chunk)
|
||||||
LOG(DEBUG) << "Decoded chunk: size " << chunk->payloadSize << " bytes, decoded " << frame_size << " samples" << '\n';
|
LOG(DEBUG) << "Decoded chunk: size " << chunk->payloadSize << " bytes, decoded " << frame_size << " samples" << '\n';
|
||||||
|
|
||||||
// copy encoded data to chunk
|
// copy encoded data to chunk
|
||||||
chunk->payloadSize = frame_size * sample_format_.channels * sizeof(opus_int16);
|
chunk->payloadSize = frame_size * sample_format_.channels() * sizeof(opus_int16);
|
||||||
chunk->payload = (char*)realloc(chunk->payload, chunk->payloadSize);
|
chunk->payload = (char*)realloc(chunk->payload, chunk->payloadSize);
|
||||||
memcpy(chunk->payload, (char*)pcm_.data(), chunk->payloadSize);
|
memcpy(chunk->payload, (char*)pcm_.data(), chunk->payloadSize);
|
||||||
return true;
|
return true;
|
||||||
|
@ -105,7 +105,7 @@ SampleFormat OpusDecoder::setHeader(msg::CodecHeader* chunk)
|
||||||
|
|
||||||
// create the decoder
|
// create the decoder
|
||||||
int error;
|
int error;
|
||||||
dec_ = opus_decoder_create(sample_format_.rate, sample_format_.channels, &error);
|
dec_ = opus_decoder_create(sample_format_.rate(), sample_format_.channels(), &error);
|
||||||
if (error != 0)
|
if (error != 0)
|
||||||
throw SnapException("Failed to initialize Opus decoder: " + std::string(opus_strerror(error)));
|
throw SnapException("Failed to initialize Opus decoder: " + std::string(opus_strerror(error)));
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,8 @@ void AlsaPlayer::initAlsa()
|
||||||
snd_pcm_hw_params_t* params;
|
snd_pcm_hw_params_t* params;
|
||||||
|
|
||||||
const SampleFormat& format = stream_->getFormat();
|
const SampleFormat& format = stream_->getFormat();
|
||||||
rate = format.rate;
|
rate = format.rate();
|
||||||
channels = format.channels;
|
channels = format.channels();
|
||||||
|
|
||||||
/* Open the PCM device in playback mode */
|
/* Open the PCM device in playback mode */
|
||||||
if ((pcm = snd_pcm_open(&handle_, pcmDevice_.name.c_str(), SND_PCM_STREAM_PLAYBACK, 0)) < 0)
|
if ((pcm = snd_pcm_open(&handle_, pcmDevice_.name.c_str(), SND_PCM_STREAM_PLAYBACK, 0)) < 0)
|
||||||
|
@ -61,16 +61,16 @@ void AlsaPlayer::initAlsa()
|
||||||
throw SnapException("Can't set interleaved mode: " + string(snd_strerror(pcm)));
|
throw SnapException("Can't set interleaved mode: " + string(snd_strerror(pcm)));
|
||||||
|
|
||||||
snd_pcm_format_t snd_pcm_format;
|
snd_pcm_format_t snd_pcm_format;
|
||||||
if (format.bits == 8)
|
if (format.bits() == 8)
|
||||||
snd_pcm_format = SND_PCM_FORMAT_S8;
|
snd_pcm_format = SND_PCM_FORMAT_S8;
|
||||||
else if (format.bits == 16)
|
else if (format.bits() == 16)
|
||||||
snd_pcm_format = SND_PCM_FORMAT_S16_LE;
|
snd_pcm_format = SND_PCM_FORMAT_S16_LE;
|
||||||
else if ((format.bits == 24) && (format.sampleSize == 4))
|
else if ((format.bits() == 24) && (format.sampleSize() == 4))
|
||||||
snd_pcm_format = SND_PCM_FORMAT_S24_LE;
|
snd_pcm_format = SND_PCM_FORMAT_S24_LE;
|
||||||
else if (format.bits == 32)
|
else if (format.bits() == 32)
|
||||||
snd_pcm_format = SND_PCM_FORMAT_S32_LE;
|
snd_pcm_format = SND_PCM_FORMAT_S32_LE;
|
||||||
else
|
else
|
||||||
throw SnapException("Unsupported sample format: " + cpt::to_string(format.bits));
|
throw SnapException("Unsupported sample format: " + cpt::to_string(format.bits()));
|
||||||
|
|
||||||
pcm = snd_pcm_hw_params_set_format(handle_, params, snd_pcm_format);
|
pcm = snd_pcm_hw_params_set_format(handle_, params, snd_pcm_format);
|
||||||
if (pcm == -EINVAL)
|
if (pcm == -EINVAL)
|
||||||
|
@ -233,8 +233,8 @@ void AlsaPlayer::worker()
|
||||||
chronos::usec delay(static_cast<chronos::usec::rep>(1000 * (double)framesDelay / format.msRate()));
|
chronos::usec delay(static_cast<chronos::usec::rep>(1000 * (double)framesDelay / format.msRate()));
|
||||||
// LOG(TRACE) << "delay: " << framesDelay << ", delay[ms]: " << delay.count() / 1000 << ", avail: " << framesAvail << "\n";
|
// LOG(TRACE) << "delay: " << framesDelay << ", delay[ms]: " << delay.count() / 1000 << ", avail: " << framesAvail << "\n";
|
||||||
|
|
||||||
if (buffer_.size() < static_cast<size_t>(framesAvail * format.frameSize))
|
if (buffer_.size() < static_cast<size_t>(framesAvail * format.frameSize()))
|
||||||
buffer_.resize(framesAvail * format.frameSize);
|
buffer_.resize(framesAvail * format.frameSize());
|
||||||
if (stream_->getPlayerChunk(buffer_.data(), delay, framesAvail))
|
if (stream_->getPlayerChunk(buffer_.data(), delay, framesAvail))
|
||||||
{
|
{
|
||||||
lastChunkTick = chronos::getTickCount();
|
lastChunkTick = chronos::getTickCount();
|
||||||
|
|
|
@ -155,12 +155,12 @@ void CoreAudioPlayer::initAudioQueue()
|
||||||
const SampleFormat& sampleFormat = pubStream_->getFormat();
|
const SampleFormat& sampleFormat = pubStream_->getFormat();
|
||||||
|
|
||||||
AudioStreamBasicDescription format;
|
AudioStreamBasicDescription format;
|
||||||
format.mSampleRate = sampleFormat.rate;
|
format.mSampleRate = sampleFormat.rate();
|
||||||
format.mFormatID = kAudioFormatLinearPCM;
|
format.mFormatID = kAudioFormatLinearPCM;
|
||||||
format.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; // | kAudioFormatFlagIsPacked;
|
format.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; // | kAudioFormatFlagIsPacked;
|
||||||
format.mBitsPerChannel = sampleFormat.bits;
|
format.mBitsPerChannel = sampleFormat.bits;
|
||||||
format.mChannelsPerFrame = sampleFormat.channels;
|
format.mChannelsPerFrame = sampleFormat.channels();
|
||||||
format.mBytesPerFrame = sampleFormat.frameSize;
|
format.mBytesPerFrame = sampleFormat.frameSize();
|
||||||
format.mFramesPerPacket = 1;
|
format.mFramesPerPacket = 1;
|
||||||
format.mBytesPerPacket = format.mBytesPerFrame * format.mFramesPerPacket;
|
format.mBytesPerPacket = format.mBytesPerFrame * format.mFramesPerPacket;
|
||||||
format.mReserved = 0;
|
format.mReserved = 0;
|
||||||
|
@ -176,9 +176,9 @@ void CoreAudioPlayer::initAudioQueue()
|
||||||
//
|
//
|
||||||
// For 100ms @ 48000:16:2 we have 19.2K
|
// For 100ms @ 48000:16:2 we have 19.2K
|
||||||
// frames: 4800, ms: 100, buffer size: 19200
|
// frames: 4800, ms: 100, buffer size: 19200
|
||||||
frames_ = (sampleFormat.rate * ms_) / 1000;
|
frames_ = (sampleFormat.rate() * ms_) / 1000;
|
||||||
ms_ = frames_ * 1000 / sampleFormat.rate;
|
ms_ = frames_ * 1000 / sampleFormat.rate();
|
||||||
buff_size_ = frames_ * sampleFormat.frameSize;
|
buff_size_ = frames_ * sampleFormat.frameSize();
|
||||||
LOG(INFO) << "frames: " << frames_ << ", ms: " << ms_ << ", buffer size: " << buff_size_ << "\n";
|
LOG(INFO) << "frames: " << frames_ << ", ms: " << ms_ << ", buffer size: " << buff_size_ << "\n";
|
||||||
|
|
||||||
AudioQueueBufferRef buffers[NUM_BUFFERS];
|
AudioQueueBufferRef buffers[NUM_BUFFERS];
|
||||||
|
|
|
@ -161,10 +161,10 @@ void OpenslPlayer::initOpensl()
|
||||||
const SampleFormat& format = stream_->getFormat();
|
const SampleFormat& format = stream_->getFormat();
|
||||||
|
|
||||||
|
|
||||||
frames_ = format.rate / (1000 / ms_); // * format.channels; // 1920; // 48000 * 2 / 50 // => 50ms
|
frames_ = format.rate() / (1000 / ms_); // * format.channels(); // 1920; // 48000 * 2 / 50 // => 50ms
|
||||||
|
|
||||||
buff_size = frames_ * format.frameSize /* 2 -> sample size */;
|
buff_size = frames_ * format.frameSize() /* 2 -> sample size */;
|
||||||
LOG(INFO, LOG_TAG) << "frames: " << frames_ << ", channels: " << format.channels << ", rate: " << format.rate << ", buff: " << buff_size << "\n";
|
LOG(INFO, LOG_TAG) << "frames: " << frames_ << ", channels: " << format.channels() << ", rate: " << format.rate() << ", buff: " << buff_size << "\n";
|
||||||
|
|
||||||
SLresult result;
|
SLresult result;
|
||||||
// create engine
|
// create engine
|
||||||
|
@ -181,7 +181,7 @@ void OpenslPlayer::initOpensl()
|
||||||
throwUnsuccess(kPhaseInit, "OutputMixObject::Realize", result);
|
throwUnsuccess(kPhaseInit, "OutputMixObject::Realize", result);
|
||||||
|
|
||||||
SLuint32 samplesPerSec = SL_SAMPLINGRATE_48;
|
SLuint32 samplesPerSec = SL_SAMPLINGRATE_48;
|
||||||
switch (format.rate)
|
switch (format.rate())
|
||||||
{
|
{
|
||||||
case 8000:
|
case 8000:
|
||||||
samplesPerSec = SL_SAMPLINGRATE_8;
|
samplesPerSec = SL_SAMPLINGRATE_8;
|
||||||
|
@ -250,7 +250,7 @@ void OpenslPlayer::initOpensl()
|
||||||
|
|
||||||
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
|
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
|
||||||
SLDataFormat_PCM format_pcm = {
|
SLDataFormat_PCM format_pcm = {
|
||||||
SL_DATAFORMAT_PCM, format.channels, samplesPerSec, bitsPerSample, containerSize, SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
|
SL_DATAFORMAT_PCM, format.channels(), samplesPerSec, bitsPerSample, containerSize, SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
|
||||||
SL_BYTEORDER_LITTLEENDIAN};
|
SL_BYTEORDER_LITTLEENDIAN};
|
||||||
|
|
||||||
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
|
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
|
||||||
|
|
|
@ -68,12 +68,12 @@ void Player::adjustVolume(char* buffer, size_t frames)
|
||||||
if ((volume < 1.0) || (volCorrection_ != 1.))
|
if ((volume < 1.0) || (volCorrection_ != 1.))
|
||||||
{
|
{
|
||||||
volume *= volCorrection_;
|
volume *= volCorrection_;
|
||||||
if (sampleFormat.sampleSize == 1)
|
if (sampleFormat.sampleSize() == 1)
|
||||||
adjustVolume<int8_t>(buffer, frames * sampleFormat.channels, volume);
|
adjustVolume<int8_t>(buffer, frames * sampleFormat.channels(), volume);
|
||||||
else if (sampleFormat.sampleSize == 2)
|
else if (sampleFormat.sampleSize() == 2)
|
||||||
adjustVolume<int16_t>(buffer, frames * sampleFormat.channels, volume);
|
adjustVolume<int16_t>(buffer, frames * sampleFormat.channels(), volume);
|
||||||
else if (sampleFormat.sampleSize == 4)
|
else if (sampleFormat.sampleSize() == 4)
|
||||||
adjustVolume<int32_t>(buffer, frames * sampleFormat.channels, volume);
|
adjustVolume<int32_t>(buffer, frames * sampleFormat.channels(), volume);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ Stream::Stream(const SampleFormat& in_format, const SampleFormat& out_format)
|
||||||
shortBuffer_.setSize(100);
|
shortBuffer_.setSize(100);
|
||||||
miniBuffer_.setSize(20);
|
miniBuffer_.setSize(20);
|
||||||
|
|
||||||
if (out_format.rate != 0)
|
if (out_format.rate() != 0)
|
||||||
format_ = out_format;
|
format_ = out_format;
|
||||||
else
|
else
|
||||||
format_ = in_format_;
|
format_ = in_format_;
|
||||||
|
@ -50,30 +50,30 @@ Stream::Stream(const SampleFormat& in_format, const SampleFormat& out_format)
|
||||||
|
|
||||||
x = 1,000016667 / (1,000016667 - 1)
|
x = 1,000016667 / (1,000016667 - 1)
|
||||||
*/
|
*/
|
||||||
// setRealSampleRate(format_.rate);
|
// setRealSampleRate(format_.rate());
|
||||||
|
|
||||||
if ((format_.rate != in_format_.rate) || (format_.bits != in_format_.bits))
|
if ((format_.rate() != in_format_.rate()) || (format_.bits() != in_format_.bits()))
|
||||||
{
|
{
|
||||||
LOG(INFO, LOG_TAG) << "Resampling from " << in_format_.getFormat() << " to " << format_.getFormat() << "\n";
|
LOG(INFO, LOG_TAG) << "Resampling from " << in_format_.getFormat() << " to " << format_.getFormat() << "\n";
|
||||||
soxr_error_t error;
|
soxr_error_t error;
|
||||||
|
|
||||||
soxr_datatype_t in_type = SOXR_INT16_I;
|
soxr_datatype_t in_type = SOXR_INT16_I;
|
||||||
soxr_datatype_t out_type = SOXR_INT16_I;
|
soxr_datatype_t out_type = SOXR_INT16_I;
|
||||||
if (in_format_.sampleSize > 2)
|
if (in_format_.sampleSize() > 2)
|
||||||
in_type = SOXR_INT32_I;
|
in_type = SOXR_INT32_I;
|
||||||
if (format_.sampleSize > 2)
|
if (format_.sampleSize() > 2)
|
||||||
out_type = SOXR_INT32_I;
|
out_type = SOXR_INT32_I;
|
||||||
soxr_io_spec_t iospec = soxr_io_spec(in_type, out_type);
|
soxr_io_spec_t iospec = soxr_io_spec(in_type, out_type);
|
||||||
// HQ should be fine: http://sox.sourceforge.net/Docs/FAQ
|
// HQ should be fine: http://sox.sourceforge.net/Docs/FAQ
|
||||||
soxr_quality_spec_t q_spec = soxr_quality_spec(SOXR_HQ, 0);
|
soxr_quality_spec_t q_spec = soxr_quality_spec(SOXR_HQ, 0);
|
||||||
soxr_ = soxr_create(static_cast<double>(in_format_.rate), static_cast<double>(format_.rate), format_.channels, &error, &iospec, &q_spec, NULL);
|
soxr_ = soxr_create(static_cast<double>(in_format_.rate()), static_cast<double>(format_.rate()), format_.channels(), &error, &iospec, &q_spec, NULL);
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
LOG(ERROR, LOG_TAG) << "Error soxr_create: " << error << "\n";
|
LOG(ERROR, LOG_TAG) << "Error soxr_create: " << error << "\n";
|
||||||
soxr_ = nullptr;
|
soxr_ = nullptr;
|
||||||
}
|
}
|
||||||
// initialize the buffer with 20ms (~latency of the reampler)
|
// initialize the buffer with 20ms (~latency of the reampler)
|
||||||
resample_buffer_.resize(format_.frameSize * ceil(format_.msRate()) * 20);
|
resample_buffer_.resize(format_.frameSize() * ceil(format_.msRate()) * 20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,14 +87,14 @@ Stream::~Stream()
|
||||||
|
|
||||||
void Stream::setRealSampleRate(double sampleRate)
|
void Stream::setRealSampleRate(double sampleRate)
|
||||||
{
|
{
|
||||||
if (sampleRate == format_.rate)
|
if (sampleRate == format_.rate())
|
||||||
{
|
{
|
||||||
correctAfterXFrames_ = 0;
|
correctAfterXFrames_ = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
correctAfterXFrames_ = round((format_.rate / sampleRate) / (format_.rate / sampleRate - 1.));
|
correctAfterXFrames_ = round((format_.rate() / sampleRate) / (format_.rate() / sampleRate - 1.));
|
||||||
// LOG(TRACE, LOG_TAG) << "Correct after X: " << correctAfterXFrames_ << " (Real rate: " << sampleRate << ", rate: " << format_.rate << ")\n";
|
// LOG(TRACE, LOG_TAG) << "Correct after X: " << correctAfterXFrames_ << " (Real rate: " << sampleRate << ", rate: " << format_.rate() << ")\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ void Stream::addChunk(unique_ptr<msg::PcmChunk> chunk)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (in_format_.bits == 24)
|
if (in_format_.bits() == 24)
|
||||||
{
|
{
|
||||||
// sox expects 32 bit input, shift 8 bits left
|
// sox expects 32 bit input, shift 8 bits left
|
||||||
int32_t* frames = (int32_t*)chunk->payload;
|
int32_t* frames = (int32_t*)chunk->payload;
|
||||||
|
@ -145,7 +145,7 @@ void Stream::addChunk(unique_ptr<msg::PcmChunk> chunk)
|
||||||
|
|
||||||
size_t idone;
|
size_t idone;
|
||||||
size_t odone;
|
size_t odone;
|
||||||
auto resample_buffer_framesize = resample_buffer_.size() / format_.frameSize;
|
auto resample_buffer_framesize = resample_buffer_.size() / format_.frameSize();
|
||||||
auto error = soxr_process(soxr_, chunk->payload, chunk->getFrameCount(), &idone, resample_buffer_.data(), resample_buffer_framesize, &odone);
|
auto error = soxr_process(soxr_, chunk->payload, chunk->getFrameCount(), &idone, resample_buffer_.data(), resample_buffer_framesize, &odone);
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
|
@ -154,7 +154,7 @@ void Stream::addChunk(unique_ptr<msg::PcmChunk> chunk)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG(TRACE, LOG_TAG) << "Resample idone: " << idone << "/" << chunk->getFrameCount() << ", odone: " << odone << "/"
|
LOG(TRACE, LOG_TAG) << "Resample idone: " << idone << "/" << chunk->getFrameCount() << ", odone: " << odone << "/"
|
||||||
<< resample_buffer_.size() / format_.frameSize << ", delay: " << soxr_delay(soxr_) << "\n";
|
<< resample_buffer_.size() / format_.frameSize() << ", delay: " << soxr_delay(soxr_) << "\n";
|
||||||
|
|
||||||
// some data has been resampled (odone frames) and some is still in the pipe (soxr_delay frames)
|
// some data has been resampled (odone frames) and some is still in the pipe (soxr_delay frames)
|
||||||
if (odone > 0)
|
if (odone > 0)
|
||||||
|
@ -170,11 +170,11 @@ void Stream::addChunk(unique_ptr<msg::PcmChunk> chunk)
|
||||||
resampled_chunk->timestamp.usec = us % 1000000;
|
resampled_chunk->timestamp.usec = us % 1000000;
|
||||||
|
|
||||||
// copy from the resample_buffer to the resampled chunk
|
// copy from the resample_buffer to the resampled chunk
|
||||||
resampled_chunk->payloadSize = odone * format_.frameSize;
|
resampled_chunk->payloadSize = odone * format_.frameSize();
|
||||||
resampled_chunk->payload = (char*)realloc(resampled_chunk->payload, resampled_chunk->payloadSize);
|
resampled_chunk->payload = (char*)realloc(resampled_chunk->payload, resampled_chunk->payloadSize);
|
||||||
memcpy(resampled_chunk->payload, resample_buffer_.data(), resampled_chunk->payloadSize);
|
memcpy(resampled_chunk->payload, resample_buffer_.data(), resampled_chunk->payloadSize);
|
||||||
|
|
||||||
if (format_.bits == 24)
|
if (format_.bits() == 24)
|
||||||
{
|
{
|
||||||
// sox has quantized to 32 bit, shift 8 bits right
|
// sox has quantized to 32 bit, shift 8 bits right
|
||||||
int32_t* frames = (int32_t*)resampled_chunk->payload;
|
int32_t* frames = (int32_t*)resampled_chunk->payload;
|
||||||
|
@ -192,7 +192,7 @@ void Stream::addChunk(unique_ptr<msg::PcmChunk> chunk)
|
||||||
if (odone == resample_buffer_framesize)
|
if (odone == resample_buffer_framesize)
|
||||||
{
|
{
|
||||||
// buffer for resampled data too small, add space for 5ms
|
// buffer for resampled data too small, add space for 5ms
|
||||||
resample_buffer_.resize(resample_buffer_.size() + format_.frameSize * ceil(format_.msRate()) * 5);
|
resample_buffer_.resize(resample_buffer_.size() + format_.frameSize() * ceil(format_.msRate()) * 5);
|
||||||
LOG(DEBUG, LOG_TAG) << "Resample buffer completely filled, adding space for 5ms; new buffer size: " << resample_buffer_.size()
|
LOG(DEBUG, LOG_TAG) << "Resample buffer completely filled, adding space for 5ms; new buffer size: " << resample_buffer_.size()
|
||||||
<< " bytes\n";
|
<< " bytes\n";
|
||||||
}
|
}
|
||||||
|
@ -216,7 +216,7 @@ bool Stream::waitForChunk(const std::chrono::milliseconds& timeout) const
|
||||||
|
|
||||||
void Stream::getSilentPlayerChunk(void* outputBuffer, uint32_t frames) const
|
void Stream::getSilentPlayerChunk(void* outputBuffer, uint32_t frames) const
|
||||||
{
|
{
|
||||||
memset(outputBuffer, 0, frames * format_.frameSize);
|
memset(outputBuffer, 0, frames * format_.frameSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -229,7 +229,7 @@ cs::time_point_clk Stream::getNextPlayerChunk(void* outputBuffer, uint32_t frame
|
||||||
uint32_t read = 0;
|
uint32_t read = 0;
|
||||||
while (read < frames)
|
while (read < frames)
|
||||||
{
|
{
|
||||||
read += chunk_->readFrames(static_cast<char*>(outputBuffer) + read * format_.frameSize, frames - read);
|
read += chunk_->readFrames(static_cast<char*>(outputBuffer) + read * format_.frameSize(), frames - read);
|
||||||
if (chunk_->isEndOfChunk() && !chunks_.try_pop(chunk_))
|
if (chunk_->isEndOfChunk() && !chunks_.try_pop(chunk_))
|
||||||
throw 0;
|
throw 0;
|
||||||
}
|
}
|
||||||
|
@ -251,8 +251,8 @@ cs::time_point_clk Stream::getNextPlayerChunk(void* outputBuffer, uint32_t frame
|
||||||
frame_delta_ -= framesCorrection;
|
frame_delta_ -= framesCorrection;
|
||||||
|
|
||||||
uint32_t toRead = frames + framesCorrection;
|
uint32_t toRead = frames + framesCorrection;
|
||||||
if (toRead * format_.frameSize > read_buffer_.size())
|
if (toRead * format_.frameSize() > read_buffer_.size())
|
||||||
read_buffer_.resize(toRead * format_.frameSize);
|
read_buffer_.resize(toRead * format_.frameSize());
|
||||||
cs::time_point_clk tp = getNextPlayerChunk(read_buffer_.data(), toRead);
|
cs::time_point_clk tp = getNextPlayerChunk(read_buffer_.data(), toRead);
|
||||||
|
|
||||||
const auto max = framesCorrection < 0 ? frames : toRead;
|
const auto max = framesCorrection < 0 ? frames : toRead;
|
||||||
|
@ -284,14 +284,16 @@ cs::time_point_clk Stream::getNextPlayerChunk(void* outputBuffer, uint32_t frame
|
||||||
// Read one frame less per slice from the input, but write a duplicated frame per slice to the output
|
// Read one frame less per slice from the input, but write a duplicated frame per slice to the output
|
||||||
// LOG(TRACE, LOG_TAG) << "duplicate - requested: " << frames << ", read: " << toRead << ", slice: " << n << ", size: " << size << ", out pos: " <<
|
// LOG(TRACE, LOG_TAG) << "duplicate - requested: " << frames << ", read: " << toRead << ", slice: " << n << ", size: " << size << ", out pos: " <<
|
||||||
// pos << ", source pos: " << pos - n << "\n";
|
// pos << ", source pos: " << pos - n << "\n";
|
||||||
memcpy(static_cast<char*>(outputBuffer) + pos * format_.frameSize, read_buffer_.data() + (pos - n) * format_.frameSize, size * format_.frameSize);
|
memcpy(static_cast<char*>(outputBuffer) + pos * format_.frameSize(), read_buffer_.data() + (pos - n) * format_.frameSize(),
|
||||||
|
size * format_.frameSize());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Read all input frames, but skip a frame per slice when writing to the output.
|
// Read all input frames, but skip a frame per slice when writing to the output.
|
||||||
// LOG(TRACE, LOG_TAG) << "remove - requested: " << frames << ", read: " << toRead << ", slice: " << n << ", size: " << size << ", out pos: " << pos
|
// LOG(TRACE, LOG_TAG) << "remove - requested: " << frames << ", read: " << toRead << ", slice: " << n << ", size: " << size << ", out pos: " << pos
|
||||||
// - n << ", source pos: " << pos << "\n";
|
// - n << ", source pos: " << pos << "\n";
|
||||||
memcpy(static_cast<char*>(outputBuffer) + (pos - n) * format_.frameSize, read_buffer_.data() + pos * format_.frameSize, size * format_.frameSize);
|
memcpy(static_cast<char*>(outputBuffer) + (pos - n) * format_.frameSize(), read_buffer_.data() + pos * format_.frameSize(),
|
||||||
|
size * format_.frameSize());
|
||||||
}
|
}
|
||||||
pos += size;
|
pos += size;
|
||||||
}
|
}
|
||||||
|
@ -381,7 +383,7 @@ bool Stream::getPlayerChunk(void* outputBuffer, const cs::usec& outputBufferDacT
|
||||||
LOG(DEBUG, LOG_TAG) << "Silent frames: " << silent_frames << ", frames: " << frames
|
LOG(DEBUG, LOG_TAG) << "Silent frames: " << silent_frames << ", frames: " << frames
|
||||||
<< ", age: " << std::chrono::duration_cast<cs::usec>(age).count() / 1000. << "\n";
|
<< ", age: " << std::chrono::duration_cast<cs::usec>(age).count() / 1000. << "\n";
|
||||||
getSilentPlayerChunk(outputBuffer, silent_frames);
|
getSilentPlayerChunk(outputBuffer, silent_frames);
|
||||||
getNextPlayerChunk((char*)outputBuffer + (chunk_->format.frameSize * silent_frames), frames - silent_frames);
|
getNextPlayerChunk((char*)outputBuffer + (chunk_->format.frameSize() * silent_frames), frames - silent_frames);
|
||||||
|
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
|
@ -410,7 +412,7 @@ bool Stream::getPlayerChunk(void* outputBuffer, const cs::usec& outputBufferDacT
|
||||||
cs::usec age = std::chrono::duration_cast<cs::usec>(TimeProvider::serverNow() - getNextPlayerChunk(outputBuffer, frames, framesCorrection) - bufferMs_ +
|
cs::usec age = std::chrono::duration_cast<cs::usec>(TimeProvider::serverNow() - getNextPlayerChunk(outputBuffer, frames, framesCorrection) - bufferMs_ +
|
||||||
outputBufferDacTime);
|
outputBufferDacTime);
|
||||||
|
|
||||||
setRealSampleRate(format_.rate);
|
setRealSampleRate(format_.rate());
|
||||||
// check if we need a hard sync
|
// check if we need a hard sync
|
||||||
if (buffer_.full() && (cs::usec(abs(median_)) > cs::msec(2)))
|
if (buffer_.full() && (cs::usec(abs(median_)) > cs::msec(2)))
|
||||||
{
|
{
|
||||||
|
@ -444,7 +446,7 @@ bool Stream::getPlayerChunk(void* outputBuffer, const cs::usec& outputBufferDacT
|
||||||
// LOG(INFO, LOG_TAG) << "Rate: " << rate << "\n";
|
// LOG(INFO, LOG_TAG) << "Rate: " << rate << "\n";
|
||||||
// we are late (age > 0), this means we are not playing fast enough
|
// we are late (age > 0), this means we are not playing fast enough
|
||||||
// => the real sample rate seems to be lower, we have to drop some frames
|
// => the real sample rate seems to be lower, we have to drop some frames
|
||||||
setRealSampleRate(format_.rate * rate); // 0.9999);
|
setRealSampleRate(format_.rate() * rate); // 0.9999);
|
||||||
}
|
}
|
||||||
else if ((cs::usec(shortMedian_) < -kCorrectionBegin) && (cs::usec(miniMedian) < -cs::usec(50)) && (cs::usec(age) < -cs::usec(50)))
|
else if ((cs::usec(shortMedian_) < -kCorrectionBegin) && (cs::usec(miniMedian) < -cs::usec(50)) && (cs::usec(age) < -cs::usec(50)))
|
||||||
{
|
{
|
||||||
|
@ -453,7 +455,7 @@ bool Stream::getPlayerChunk(void* outputBuffer, const cs::usec& outputBufferDacT
|
||||||
// LOG(INFO, LOG_TAG) << "Rate: " << rate << "\n";
|
// LOG(INFO, LOG_TAG) << "Rate: " << rate << "\n";
|
||||||
// we are early (age > 0), this means we are playing too fast
|
// we are early (age > 0), this means we are playing too fast
|
||||||
// => the real sample rate seems to be higher, we have to insert some frames
|
// => the real sample rate seems to be higher, we have to insert some frames
|
||||||
setRealSampleRate(format_.rate * rate); // 1.0001);
|
setRealSampleRate(format_.rate() * rate); // 1.0001);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ namespace msg
|
||||||
class PcmChunk : public WireChunk
|
class PcmChunk : public WireChunk
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PcmChunk(const SampleFormat& sampleFormat, size_t ms) : WireChunk(sampleFormat.rate * sampleFormat.frameSize * ms / 1000), format(sampleFormat), idx_(0)
|
PcmChunk(const SampleFormat& sampleFormat, size_t ms) : WireChunk(sampleFormat.rate() * sampleFormat.frameSize() * ms / 1000), format(sampleFormat), idx_(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,18 +59,18 @@ public:
|
||||||
|
|
||||||
int readFrames(void* outputBuffer, size_t frameCount)
|
int readFrames(void* outputBuffer, size_t frameCount)
|
||||||
{
|
{
|
||||||
// logd << "read: " << frameCount << ", total: " << (wireChunk->length / format.frameSize) << ", idx: " << idx;// << std::endl;
|
// logd << "read: " << frameCount << ", total: " << (wireChunk->length / format.frameSize()) << ", idx: " << idx;// << std::endl;
|
||||||
int result = frameCount;
|
int result = frameCount;
|
||||||
if (idx_ + frameCount > (payloadSize / format.frameSize))
|
if (idx_ + frameCount > (payloadSize / format.frameSize()))
|
||||||
result = (payloadSize / format.frameSize) - idx_;
|
result = (payloadSize / format.frameSize()) - idx_;
|
||||||
|
|
||||||
// logd << ", from: " << format.frameSize*idx << ", to: " << format.frameSize*idx + format.frameSize*result;
|
// logd << ", from: " << format.frameSize()*idx << ", to: " << format.frameSize()*idx + format.frameSize()*result;
|
||||||
if (outputBuffer != nullptr)
|
if (outputBuffer != nullptr)
|
||||||
memcpy((char*)outputBuffer, (char*)(payload) + format.frameSize * idx_, format.frameSize * result);
|
memcpy((char*)outputBuffer, (char*)(payload) + format.frameSize() * idx_, format.frameSize() * result);
|
||||||
|
|
||||||
idx_ += result;
|
idx_ += result;
|
||||||
// logd << ", new idx: " << idx << ", result: " << result << ", wireChunk->length: " << wireChunk->length << ", format.frameSize: " << format.frameSize
|
// logd << ", new idx: " << idx << ", result: " << result << ", wireChunk->length: " << wireChunk->length << ", format.frameSize(): " <<
|
||||||
// << "\n";//std::endl;
|
// format.frameSize() << "\n";
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ public:
|
||||||
chronos::time_point_clk start() const override
|
chronos::time_point_clk start() const override
|
||||||
{
|
{
|
||||||
return chronos::time_point_clk(chronos::sec(timestamp.sec) + chronos::usec(timestamp.usec) +
|
return chronos::time_point_clk(chronos::sec(timestamp.sec) + chronos::usec(timestamp.usec) +
|
||||||
chronos::usec(static_cast<chronos::usec::rep>(1000000. * ((double)idx_ / (double)format.rate))));
|
chronos::usec(static_cast<chronos::usec::rep>(1000000. * ((double)idx_ / (double)format.rate()))));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline chronos::time_point_clk end() const
|
inline chronos::time_point_clk end() const
|
||||||
|
@ -117,12 +117,12 @@ public:
|
||||||
|
|
||||||
inline size_t getFrameCount() const
|
inline size_t getFrameCount() const
|
||||||
{
|
{
|
||||||
return (payloadSize / format.frameSize);
|
return (payloadSize / format.frameSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t getSampleCount() const
|
inline size_t getSampleCount() const
|
||||||
{
|
{
|
||||||
return (payloadSize / format.sampleSize);
|
return (payloadSize / format.sampleSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
SampleFormat format;
|
SampleFormat format;
|
||||||
|
|
|
@ -51,7 +51,7 @@ SampleFormat::SampleFormat(uint32_t sampleRate, uint16_t bitsPerSample, uint16_t
|
||||||
string SampleFormat::getFormat() const
|
string SampleFormat::getFormat() const
|
||||||
{
|
{
|
||||||
stringstream ss;
|
stringstream ss;
|
||||||
ss << rate << ":" << bits << ":" << channels;
|
ss << rate_ << ":" << bits_ << ":" << channels_;
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,12 +70,12 @@ void SampleFormat::setFormat(uint32_t rate, uint16_t bits, uint16_t channels)
|
||||||
// needs something like:
|
// needs something like:
|
||||||
// 24_4 = 3 bytes, padded to 4
|
// 24_4 = 3 bytes, padded to 4
|
||||||
// 32 = 4 bytes
|
// 32 = 4 bytes
|
||||||
this->rate = rate;
|
rate_ = rate;
|
||||||
this->bits = bits;
|
bits_ = bits;
|
||||||
this->channels = channels;
|
channels_ = channels;
|
||||||
sampleSize = bits / 8;
|
sample_size_ = bits / 8;
|
||||||
if (bits == 24)
|
if (bits_ == 24)
|
||||||
sampleSize = 4;
|
sample_size_ = 4;
|
||||||
frameSize = channels * sampleSize;
|
frame_size_ = channels_ * sample_size_;
|
||||||
// LOG(DEBUG) << "SampleFormat: " << rate << ":" << bits << ":" << channels << "\n";
|
// LOG(DEBUG) << "SampleFormat: " << rate << ":" << bits << ":" << channels << "\n";
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,30 +46,54 @@ public:
|
||||||
void setFormat(const std::string& format);
|
void setFormat(const std::string& format);
|
||||||
void setFormat(uint32_t rate, uint16_t bits, uint16_t channels);
|
void setFormat(uint32_t rate, uint16_t bits, uint16_t channels);
|
||||||
|
|
||||||
uint32_t rate;
|
uint32_t rate() const
|
||||||
uint16_t bits;
|
{
|
||||||
uint16_t channels;
|
return rate_;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t bits() const
|
||||||
|
{
|
||||||
|
return bits_;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t channels() const
|
||||||
|
{
|
||||||
|
return channels_;
|
||||||
|
}
|
||||||
|
|
||||||
// size in [bytes] of a single mono sample, e.g. 2 bytes (= 16 bits)
|
// size in [bytes] of a single mono sample, e.g. 2 bytes (= 16 bits)
|
||||||
uint16_t sampleSize;
|
uint16_t sampleSize() const
|
||||||
|
{
|
||||||
|
return sample_size_;
|
||||||
|
}
|
||||||
|
|
||||||
// size in [bytes] of a frame (sum of sample sizes = #channel*sampleSize), e.g. 4 bytes (= 2 channel * 16 bit)
|
// size in [bytes] of a frame (sum of sample sizes = #channel*sampleSize), e.g. 4 bytes (= 2 channel * 16 bit)
|
||||||
uint16_t frameSize;
|
uint16_t frameSize() const
|
||||||
|
{
|
||||||
|
return frame_size_;
|
||||||
|
}
|
||||||
|
|
||||||
inline double msRate() const
|
inline double msRate() const
|
||||||
{
|
{
|
||||||
return (double)rate / 1000.;
|
return (double)rate_ / 1000.;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline double usRate() const
|
inline double usRate() const
|
||||||
{
|
{
|
||||||
return (double)rate / 1000000.;
|
return (double)rate_ / 1000000.;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline double nsRate() const
|
inline double nsRate() const
|
||||||
{
|
{
|
||||||
return (double)rate / 1000000000.;
|
return (double)rate_ / 1000000000.;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint16_t sample_size_;
|
||||||
|
uint16_t frame_size_;
|
||||||
|
uint32_t rate_;
|
||||||
|
uint16_t bits_;
|
||||||
|
uint16_t channels_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -82,19 +82,19 @@ void FlacEncoder::encode(const msg::PcmChunk* chunk)
|
||||||
pcmBuffer_ = (FLAC__int32*)realloc(pcmBuffer_, pcmBufferSize_ * sizeof(FLAC__int32));
|
pcmBuffer_ = (FLAC__int32*)realloc(pcmBuffer_, pcmBufferSize_ * sizeof(FLAC__int32));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sampleFormat_.sampleSize == 1)
|
if (sampleFormat_.sampleSize() == 1)
|
||||||
{
|
{
|
||||||
FLAC__int8* buffer = (FLAC__int8*)chunk->payload;
|
FLAC__int8* buffer = (FLAC__int8*)chunk->payload;
|
||||||
for (int i = 0; i < samples; i++)
|
for (int i = 0; i < samples; i++)
|
||||||
pcmBuffer_[i] = (FLAC__int32)(buffer[i]);
|
pcmBuffer_[i] = (FLAC__int32)(buffer[i]);
|
||||||
}
|
}
|
||||||
else if (sampleFormat_.sampleSize == 2)
|
else if (sampleFormat_.sampleSize() == 2)
|
||||||
{
|
{
|
||||||
FLAC__int16* buffer = (FLAC__int16*)chunk->payload;
|
FLAC__int16* buffer = (FLAC__int16*)chunk->payload;
|
||||||
for (int i = 0; i < samples; i++)
|
for (int i = 0; i < samples; i++)
|
||||||
pcmBuffer_[i] = (FLAC__int32)(buffer[i]);
|
pcmBuffer_[i] = (FLAC__int32)(buffer[i]);
|
||||||
}
|
}
|
||||||
else if (sampleFormat_.sampleSize == 4)
|
else if (sampleFormat_.sampleSize() == 4)
|
||||||
{
|
{
|
||||||
FLAC__int32* buffer = (FLAC__int32*)chunk->payload;
|
FLAC__int32* buffer = (FLAC__int32*)chunk->payload;
|
||||||
for (int i = 0; i < samples; i++)
|
for (int i = 0; i < samples; i++)
|
||||||
|
@ -106,7 +106,7 @@ void FlacEncoder::encode(const msg::PcmChunk* chunk)
|
||||||
|
|
||||||
if (encodedSamples_ > 0)
|
if (encodedSamples_ > 0)
|
||||||
{
|
{
|
||||||
double resMs = encodedSamples_ / ((double)sampleFormat_.rate / 1000.);
|
double resMs = encodedSamples_ / sampleFormat_.msRate();
|
||||||
// LOG(INFO) << "encoded: " << chunk->payloadSize << "\tframes: " << encodedSamples_ << "\tres: " << resMs << "\n";
|
// LOG(INFO) << "encoded: " << chunk->payloadSize << "\tframes: " << encodedSamples_ << "\tres: " << resMs << "\n";
|
||||||
encodedSamples_ = 0;
|
encodedSamples_ = 0;
|
||||||
listener_->onChunkEncoded(this, flacChunk_, resMs);
|
listener_->onChunkEncoded(this, flacChunk_, resMs);
|
||||||
|
@ -176,9 +176,9 @@ void FlacEncoder::initEncoder()
|
||||||
// 0-2: 1152 frames, ~26.1224ms
|
// 0-2: 1152 frames, ~26.1224ms
|
||||||
// 3-8: 4096 frames, ~92.8798ms
|
// 3-8: 4096 frames, ~92.8798ms
|
||||||
ok &= FLAC__stream_encoder_set_compression_level(encoder_, quality);
|
ok &= FLAC__stream_encoder_set_compression_level(encoder_, quality);
|
||||||
ok &= FLAC__stream_encoder_set_channels(encoder_, sampleFormat_.channels);
|
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_bits_per_sample(encoder_, sampleFormat_.bits());
|
||||||
ok &= FLAC__stream_encoder_set_sample_rate(encoder_, sampleFormat_.rate);
|
ok &= FLAC__stream_encoder_set_sample_rate(encoder_, sampleFormat_.rate());
|
||||||
|
|
||||||
if (!ok)
|
if (!ok)
|
||||||
throw SnapException("error setting up encoder");
|
throw SnapException("error setting up encoder");
|
||||||
|
|
|
@ -73,25 +73,25 @@ void OggEncoder::encode(const msg::PcmChunk* chunk)
|
||||||
float** buffer = vorbis_analysis_buffer(&vd_, frames);
|
float** buffer = vorbis_analysis_buffer(&vd_, frames);
|
||||||
|
|
||||||
/* uninterleave samples */
|
/* uninterleave samples */
|
||||||
for (size_t channel = 0; channel < sampleFormat_.channels; ++channel)
|
for (size_t channel = 0; channel < sampleFormat_.channels(); ++channel)
|
||||||
{
|
{
|
||||||
if (sampleFormat_.sampleSize == 1)
|
if (sampleFormat_.sampleSize() == 1)
|
||||||
{
|
{
|
||||||
int8_t* chunkBuffer = (int8_t*)chunk->payload;
|
int8_t* chunkBuffer = (int8_t*)chunk->payload;
|
||||||
for (int i = 0; i < frames; i++)
|
for (int i = 0; i < frames; i++)
|
||||||
buffer[channel][i] = chunkBuffer[sampleFormat_.channels * i + channel] / 128.f;
|
buffer[channel][i] = chunkBuffer[sampleFormat_.channels() * i + channel] / 128.f;
|
||||||
}
|
}
|
||||||
else if (sampleFormat_.sampleSize == 2)
|
else if (sampleFormat_.sampleSize() == 2)
|
||||||
{
|
{
|
||||||
int16_t* chunkBuffer = (int16_t*)chunk->payload;
|
int16_t* chunkBuffer = (int16_t*)chunk->payload;
|
||||||
for (int i = 0; i < frames; i++)
|
for (int i = 0; i < frames; i++)
|
||||||
buffer[channel][i] = chunkBuffer[sampleFormat_.channels * i + channel] / 32768.f;
|
buffer[channel][i] = chunkBuffer[sampleFormat_.channels() * i + channel] / 32768.f;
|
||||||
}
|
}
|
||||||
else if (sampleFormat_.sampleSize == 4)
|
else if (sampleFormat_.sampleSize() == 4)
|
||||||
{
|
{
|
||||||
int32_t* chunkBuffer = (int32_t*)chunk->payload;
|
int32_t* chunkBuffer = (int32_t*)chunk->payload;
|
||||||
for (int i = 0; i < frames; i++)
|
for (int i = 0; i < frames; i++)
|
||||||
buffer[channel][i] = chunkBuffer[sampleFormat_.channels * i + channel] / 2147483648.f;
|
buffer[channel][i] = chunkBuffer[sampleFormat_.channels() * i + channel] / 2147483648.f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ void OggEncoder::encode(const msg::PcmChunk* chunk)
|
||||||
|
|
||||||
if (res > 0)
|
if (res > 0)
|
||||||
{
|
{
|
||||||
res /= (sampleFormat_.rate / 1000.);
|
res /= sampleFormat_.msRate();
|
||||||
// LOG(INFO) << "res: " << res << "\n";
|
// LOG(INFO) << "res: " << res << "\n";
|
||||||
lastGranulepos_ = os_.granulepos;
|
lastGranulepos_ = os_.granulepos;
|
||||||
// make oggChunk smaller
|
// make oggChunk smaller
|
||||||
|
@ -209,7 +209,7 @@ void OggEncoder::initEncoder()
|
||||||
|
|
||||||
*********************************************************************/
|
*********************************************************************/
|
||||||
|
|
||||||
int ret = vorbis_encode_init_vbr(&vi_, sampleFormat_.channels, sampleFormat_.rate, quality);
|
int ret = vorbis_encode_init_vbr(&vi_, sampleFormat_.channels(), sampleFormat_.rate(), quality);
|
||||||
|
|
||||||
/* do not continue if setup failed; this can happen if we ask for a
|
/* 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,
|
mode that libVorbis does not support (eg, too low a bitrate, etc,
|
||||||
|
|
|
@ -77,7 +77,7 @@ void OpusEncoder::initEncoder()
|
||||||
{
|
{
|
||||||
// Opus is quite restrictive in sample rate and bit depth
|
// Opus is quite restrictive in sample rate and bit depth
|
||||||
// It can handle mono signals, but we will check for stereo
|
// It can handle mono signals, but we will check for stereo
|
||||||
if ((sampleFormat_.rate != 48000) || (sampleFormat_.bits != 16) || (sampleFormat_.channels != 2))
|
if ((sampleFormat_.rate() != 48000) || (sampleFormat_.bits() != 16) || (sampleFormat_.channels() != 2))
|
||||||
throw SnapException("Opus sampleformat must be 48000:16:2");
|
throw SnapException("Opus sampleformat must be 48000:16:2");
|
||||||
|
|
||||||
opus_int32 bitrate = 192000;
|
opus_int32 bitrate = 192000;
|
||||||
|
@ -135,7 +135,7 @@ void OpusEncoder::initEncoder()
|
||||||
LOG(INFO) << "Opus bitrate: " << bitrate << " bps, complexity: " << complexity << "\n";
|
LOG(INFO) << "Opus bitrate: " << bitrate << " bps, complexity: " << complexity << "\n";
|
||||||
|
|
||||||
int error;
|
int error;
|
||||||
enc_ = opus_encoder_create(sampleFormat_.rate, sampleFormat_.channels, OPUS_APPLICATION_RESTRICTED_LOWDELAY, &error);
|
enc_ = opus_encoder_create(sampleFormat_.rate(), sampleFormat_.channels(), OPUS_APPLICATION_RESTRICTED_LOWDELAY, &error);
|
||||||
if (error != 0)
|
if (error != 0)
|
||||||
{
|
{
|
||||||
throw SnapException("Failed to initialize Opus encoder: " + std::string(opus_strerror(error)));
|
throw SnapException("Failed to initialize Opus encoder: " + std::string(opus_strerror(error)));
|
||||||
|
@ -149,9 +149,9 @@ void OpusEncoder::initEncoder()
|
||||||
headerChunk_->payload = (char*)realloc(headerChunk_->payload, headerChunk_->payloadSize);
|
headerChunk_->payload = (char*)realloc(headerChunk_->payload, headerChunk_->payloadSize);
|
||||||
char* payload = headerChunk_->payload;
|
char* payload = headerChunk_->payload;
|
||||||
assign(payload, SWAP_32(ID_OPUS));
|
assign(payload, SWAP_32(ID_OPUS));
|
||||||
assign(payload + 4, SWAP_32(sampleFormat_.rate));
|
assign(payload + 4, SWAP_32(sampleFormat_.rate()));
|
||||||
assign(payload + 8, SWAP_16(sampleFormat_.bits));
|
assign(payload + 8, SWAP_16(sampleFormat_.bits()));
|
||||||
assign(payload + 10, SWAP_16(sampleFormat_.channels));
|
assign(payload + 10, SWAP_16(sampleFormat_.channels()));
|
||||||
|
|
||||||
remainder_ = std::make_unique<msg::PcmChunk>(sampleFormat_, 10);
|
remainder_ = std::make_unique<msg::PcmChunk>(sampleFormat_, 10);
|
||||||
remainder_max_size_ = remainder_->payloadSize;
|
remainder_max_size_ = remainder_->payloadSize;
|
||||||
|
@ -192,7 +192,7 @@ void OpusEncoder::encode(const msg::PcmChunk* chunk)
|
||||||
std::vector<size_t> chunk_durations{60, 40, 20, 10};
|
std::vector<size_t> chunk_durations{60, 40, 20, 10};
|
||||||
for (const auto duration : chunk_durations)
|
for (const auto duration : chunk_durations)
|
||||||
{
|
{
|
||||||
auto ms2bytes = [this](size_t ms) { return (ms * sampleFormat_.msRate() * sampleFormat_.frameSize); };
|
auto ms2bytes = [this](size_t ms) { return (ms * sampleFormat_.msRate() * sampleFormat_.frameSize()); };
|
||||||
uint32_t bytes = ms2bytes(duration);
|
uint32_t bytes = ms2bytes(duration);
|
||||||
while (chunk->payloadSize - offset >= bytes)
|
while (chunk->payloadSize - offset >= bytes)
|
||||||
{
|
{
|
||||||
|
@ -217,7 +217,7 @@ void OpusEncoder::encode(const SampleFormat& format, const char* data, size_t si
|
||||||
{
|
{
|
||||||
// void* buffer;
|
// void* buffer;
|
||||||
// LOG(INFO) << "frames: " << chunk->readFrames(buffer, std::chrono::milliseconds(10)) << "\n";
|
// LOG(INFO) << "frames: " << chunk->readFrames(buffer, std::chrono::milliseconds(10)) << "\n";
|
||||||
int samples_per_channel = size / format.frameSize;
|
int samples_per_channel = size / format.frameSize();
|
||||||
if (encoded_.size() < size)
|
if (encoded_.size() < size)
|
||||||
encoded_.resize(size);
|
encoded_.resize(size);
|
||||||
|
|
||||||
|
@ -231,7 +231,7 @@ void OpusEncoder::encode(const SampleFormat& format, const char* data, size_t si
|
||||||
opusChunk->payloadSize = len;
|
opusChunk->payloadSize = len;
|
||||||
opusChunk->payload = (char*)realloc(opusChunk->payload, opusChunk->payloadSize);
|
opusChunk->payload = (char*)realloc(opusChunk->payload, opusChunk->payloadSize);
|
||||||
memcpy(opusChunk->payload, encoded_.data(), len);
|
memcpy(opusChunk->payload, encoded_.data(), len);
|
||||||
listener_->onChunkEncoded(this, opusChunk, (double)samples_per_channel / ((double)sampleFormat_.rate / 1000.));
|
listener_->onChunkEncoded(this, opusChunk, (double)samples_per_channel / sampleFormat_.msRate());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -65,11 +65,11 @@ void PcmEncoder::initEncoder()
|
||||||
assign(payload + 12, SWAP_32(ID_FMT));
|
assign(payload + 12, SWAP_32(ID_FMT));
|
||||||
assign(payload + 16, SWAP_32(16));
|
assign(payload + 16, SWAP_32(16));
|
||||||
assign(payload + 20, SWAP_16(1));
|
assign(payload + 20, SWAP_16(1));
|
||||||
assign(payload + 22, SWAP_16(sampleFormat_.channels));
|
assign(payload + 22, SWAP_16(sampleFormat_.channels()));
|
||||||
assign(payload + 24, SWAP_32(sampleFormat_.rate));
|
assign(payload + 24, SWAP_32(sampleFormat_.rate()));
|
||||||
assign(payload + 28, SWAP_32(sampleFormat_.rate * sampleFormat_.bits * sampleFormat_.channels / 8));
|
assign(payload + 28, SWAP_32(sampleFormat_.rate() * sampleFormat_.bits() * sampleFormat_.channels() / 8));
|
||||||
assign(payload + 32, SWAP_16(sampleFormat_.channels * ((sampleFormat_.bits + 7) / 8)));
|
assign(payload + 32, SWAP_16(sampleFormat_.channels() * ((sampleFormat_.bits() + 7) / 8)));
|
||||||
assign(payload + 34, SWAP_16(sampleFormat_.bits));
|
assign(payload + 34, SWAP_16(sampleFormat_.bits()));
|
||||||
assign(payload + 36, SWAP_32(ID_DATA));
|
assign(payload + 36, SWAP_32(ID_DATA));
|
||||||
assign(payload + 40, SWAP_32(0));
|
assign(payload + 40, SWAP_32(0));
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ void PosixStream::connect()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
idle_bytes_ = 0;
|
idle_bytes_ = 0;
|
||||||
max_idle_bytes_ = sampleFormat_.rate * sampleFormat_.frameSize * dryout_ms_ / 1000;
|
max_idle_bytes_ = sampleFormat_.rate() * sampleFormat_.frameSize() * dryout_ms_ / 1000;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -89,7 +89,7 @@ void PosixStream::do_read()
|
||||||
{
|
{
|
||||||
// nothing to read for a longer time now, set the chunk to silent
|
// nothing to read for a longer time now, set the chunk to silent
|
||||||
LOG(DEBUG, LOG_TAG) << "count < 0: " << errno
|
LOG(DEBUG, LOG_TAG) << "count < 0: " << errno
|
||||||
<< " && idleBytes < maxIdleBytes, ms: " << 1000 * chunk_->payloadSize / (sampleFormat_.rate * sampleFormat_.frameSize)
|
<< " && idleBytes < maxIdleBytes, ms: " << 1000 * chunk_->payloadSize / (sampleFormat_.rate() * sampleFormat_.frameSize())
|
||||||
<< "\n";
|
<< "\n";
|
||||||
memset(chunk_->payload + len, 0, toRead - len);
|
memset(chunk_->payload + len, 0, toRead - len);
|
||||||
idle_bytes_ += toRead - len;
|
idle_bytes_ += toRead - len;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue