mirror of
https://github.com/badaix/snapcast.git
synced 2025-05-20 20:46:16 +02:00
clean up
This commit is contained in:
parent
1d22986619
commit
aba04a0675
10 changed files with 202 additions and 221 deletions
|
@ -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"
|
||||
|
||||
|
||||
|
|
|
@ -75,6 +75,11 @@ public:
|
|||
return (payloadSize / format.frameSize);
|
||||
}
|
||||
|
||||
inline size_t getSampleCount() const
|
||||
{
|
||||
return (payloadSize / format.sampleSize);
|
||||
}
|
||||
|
||||
SampleFormat format;
|
||||
|
||||
private:
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
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 res;//chunk->duration<chronos::msec>().count();
|
||||
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,29 +113,11 @@ 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;
|
||||
}
|
||||
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;
|
||||
return flacEncoder->write_callback(encoder, buffer, bytes, samples, current_frame);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void FlacEncoder::initEncoder()
|
||||
{
|
||||
FLAC__bool ok = true;
|
||||
|
@ -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(
|
||||
(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)
|
||||
) {
|
||||
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 ||
|
||||
// there are many tag (vorbiscomment) functions but these are convenient for this particular use:
|
||||
!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);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -30,16 +30,23 @@
|
|||
class FlacEncoder : public Encoder
|
||||
{
|
||||
public:
|
||||
FlacEncoder(const msg::SampleFormat& format);
|
||||
~FlacEncoder();
|
||||
virtual double encode(msg::PcmChunk* chunk);
|
||||
msg::Header* getHeaderChunk();
|
||||
|
||||
FlacEncoder(const msg::SampleFormat& format);
|
||||
~FlacEncoder();
|
||||
virtual double encode(msg::PcmChunk* chunk);
|
||||
|
||||
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);
|
||||
void initEncoder();
|
||||
|
||||
FLAC__StreamEncoder *encoder_;
|
||||
FLAC__StreamMetadata *metadata_[2];
|
||||
|
||||
FLAC__int32 *pcmBuffer_;
|
||||
int pcmBufferSize_;
|
||||
|
||||
msg::PcmChunk* encodedChunk_;
|
||||
size_t encodedSamples_;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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,21 +34,14 @@ 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);
|
||||
|
||||
/* uninterleave samples */
|
||||
for(int i=0; i<bytes; i++)
|
||||
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;
|
||||
*/
|
||||
int idx = 4*i;
|
||||
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;
|
||||
}
|
||||
|
@ -61,27 +53,27 @@ double OggEncoder::encode(msg::PcmChunk* chunk)
|
|||
more involved (potentially parallel) processing. Get a single
|
||||
block for encoding now */
|
||||
size_t pos = 0;
|
||||
while(vorbis_analysis_blockout(&vd,&vb)==1)
|
||||
while (vorbis_analysis_blockout(&vd,&vb)==1)
|
||||
{
|
||||
/* analysis, assume we want to use bitrate management */
|
||||
vorbis_analysis(&vb,NULL);
|
||||
vorbis_bitrate_addblock(&vb);
|
||||
|
||||
while(vorbis_bitrate_flushpacket(&vd,&op))
|
||||
while (vorbis_bitrate_flushpacket(&vd,&op))
|
||||
{
|
||||
/* weld the packet into the bitstream */
|
||||
ogg_stream_packetin(&os,&op);
|
||||
ogg_stream_packetin(&os, &op);
|
||||
|
||||
/* write out pages (if any) */
|
||||
while(true)
|
||||
while (true)
|
||||
{
|
||||
// int result = ogg_stream_pageout(&os,&og);
|
||||
int result = ogg_stream_flush(&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);
|
||||
|
||||
|
@ -91,21 +83,21 @@ double OggEncoder::encode(msg::PcmChunk* chunk)
|
|||
pos += og.body_len;
|
||||
|
||||
if (ogg_page_eos(&og))
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
@ -173,11 +167,11 @@ void OggEncoder::init()
|
|||
make the headers, then pass them to libvorbis one at a time;
|
||||
libvorbis handles the additional Ogg bitstream constraints */
|
||||
|
||||
ogg_packet header;
|
||||
ogg_packet header_comm;
|
||||
ogg_packet header_code;
|
||||
|
||||
vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
|
||||
ogg_packet header;
|
||||
ogg_packet header_comm;
|
||||
ogg_packet header_code;
|
||||
|
||||
vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
|
||||
ogg_stream_packetin(&os,&header);
|
||||
ogg_stream_packetin(&os,&header_comm);
|
||||
ogg_stream_packetin(&os,&header_code);
|
||||
|
@ -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);
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
PcmEncoder::PcmEncoder(const msg::SampleFormat& format) : Encoder(format)
|
||||
{
|
||||
headerChunk = new msg::Header("pcm");
|
||||
headerChunk_ = new msg::Header("pcm");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue