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/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
***/ ***/
#include "pcmChunk.h"
#include <string.h> #include <string.h>
#include <iostream> #include <iostream>
#include "pcmChunk.h"
#include "common/log.h" #include "common/log.h"

View file

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

View file

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

View file

@ -25,6 +25,16 @@
namespace msg 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 class SampleFormat : public BaseMessage
{ {
public: public:
@ -39,7 +49,10 @@ public:
uint16_t bits; uint16_t bits;
uint16_t channels; uint16_t channels;
// size in [bytes] of a single mono sample, e.g. 2 bytes (= 16 bits)
uint16_t sampleSize; 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; uint16_t frameSize;
inline double msRate() const inline double msRate() const

View file

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

View file

@ -22,65 +22,87 @@
using namespace std; using namespace std;
#define READSIZE 16384
static FLAC__int32 pcm[READSIZE/*samples*/ * 2/*channels*/]; FlacEncoder::FlacEncoder(const msg::SampleFormat& format) : Encoder(format), encoder_(NULL), pcmBufferSize_(0), encodedSamples_(0)
size_t encodedSamples = 0;
static msg::PcmChunk* encodedChunk = NULL;
FlacEncoder::FlacEncoder(const msg::SampleFormat& format) : Encoder(format), encoder(NULL)
{ {
encodedChunk = new msg::PcmChunk(); encodedChunk_ = new msg::PcmChunk();
headerChunk = new msg::Header("flac"); headerChunk_ = new msg::Header("flac");
pcmBuffer_ = (FLAC__int32*)malloc(pcmBufferSize_ * sizeof(FLAC__int32));
initEncoder(); initEncoder();
} }
FlacEncoder::~FlacEncoder() FlacEncoder::~FlacEncoder()
{ {
if (encoder != NULL) if (encoder_ != NULL)
{ {
FLAC__stream_encoder_finish(encoder); FLAC__stream_encoder_finish(encoder_);
FLAC__metadata_object_delete(metadata[0]); FLAC__metadata_object_delete(metadata_[0]);
FLAC__metadata_object_delete(metadata[1]); FLAC__metadata_object_delete(metadata_[1]);
FLAC__stream_encoder_delete(encoder); FLAC__stream_encoder_delete(encoder_);
} }
delete encodedChunk; delete encodedChunk_;
} free(pcmBuffer_);
msg::Header* FlacEncoder::getHeaderChunk()
{
return headerChunk;
} }
double FlacEncoder::encode(msg::PcmChunk* chunk) double FlacEncoder::encode(msg::PcmChunk* chunk)
{ {
logD << "payload: " << chunk->payloadSize << "\tsamples: " << chunk->payloadSize/4 << "\n"; int samples = chunk->getSampleCount();
int samples = chunk->payloadSize / 4; int frames = chunk->getFrameCount();
for(int i=0; i<samples*2/*channels*/; i++) logO << "payload: " << chunk->payloadSize << "\tframes: " << frames << "\tsamples: " << samples << "\tduration: " << chunk->duration<chronos::msec>().count() << "\n";
{
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);
double res = encodedSamples / ((double)sampleFormat.rate / 1000.); if (pcmBufferSize_ < samples)
if (encodedSamples > 0)
{ {
logD << "encoded: " << chunk->payloadSize << "\tsamples: " << encodedSamples << "\tres: " << res << "\n"; pcmBufferSize_ = samples;
encodedSamples = 0; pcmBuffer_ = (FLAC__int32*)realloc(pcmBuffer_, pcmBufferSize_ * sizeof(FLAC__int32));
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(); 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,29 +113,11 @@ FLAC__StreamEncoderWriteStatus write_callback(const FLAC__StreamEncoder *encoder
unsigned current_frame, unsigned current_frame,
void *client_data) void *client_data)
{ {
logD << "write_callback: " << bytes << ", " << samples << ", " << current_frame << "\n";
FlacEncoder* flacEncoder = (FlacEncoder*)client_data; FlacEncoder* flacEncoder = (FlacEncoder*)client_data;
if ((current_frame == 0) && (bytes > 0) && (samples == 0)) return flacEncoder->write_callback(encoder, buffer, bytes, samples, current_frame);
{
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;
} }
void FlacEncoder::initEncoder() void FlacEncoder::initEncoder()
{ {
FLAC__bool ok = true; FLAC__bool ok = true;
@ -121,93 +125,53 @@ void FlacEncoder::initEncoder()
FLAC__StreamMetadata_VorbisComment_Entry entry; FLAC__StreamMetadata_VorbisComment_Entry entry;
// allocate the encoder // allocate the encoder
if((encoder = FLAC__stream_encoder_new()) == NULL) { if ((encoder_ = FLAC__stream_encoder_new()) == NULL)
{
fprintf(stderr, "ERROR: allocating encoder\n"); fprintf(stderr, "ERROR: allocating encoder\n");
return; return;
} }
ok &= FLAC__stream_encoder_set_verify(encoder, true); ok &= FLAC__stream_encoder_set_verify(encoder_, true);
ok &= FLAC__stream_encoder_set_compression_level(encoder, 5); // compression levels (0-8):
ok &= FLAC__stream_encoder_set_channels(encoder, sampleFormat.channels); // https://xiph.org/flac/api/group__flac__stream__encoder.html#gae49cf32f5256cb47eecd33779493ac85
ok &= FLAC__stream_encoder_set_bits_per_sample(encoder, sampleFormat.bits); // latency:
ok &= FLAC__stream_encoder_set_sample_rate(encoder, sampleFormat.rate); // 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 // 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 || if (
(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: // 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_entry_from_name_value_pair(&entry, "TITLE", "SnapStream") ||
!FLAC__metadata_object_vorbiscomment_append_comment(metadata[0], entry, false) || !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_entry_from_name_value_pair(&entry, "VERSION", VERSION) ||
!FLAC__metadata_object_vorbiscomment_append_comment(metadata[0], entry, false) !FLAC__metadata_object_vorbiscomment_append_comment(metadata_[0], entry, false)
) { )
{
fprintf(stderr, "ERROR: out of memory or tag error\n"); fprintf(stderr, "ERROR: out of memory or tag error\n");
ok = false; ok = false;
} }
metadata[1]->length = 1234; // set the padding length metadata_[1]->length = 1234; // set the padding length
ok = FLAC__stream_encoder_set_metadata(encoder_, metadata_, 2);
ok = FLAC__stream_encoder_set_metadata(encoder, metadata, 2);
} }
// initialize encoder // initialize encoder
if(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) { 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]); fprintf(stderr, "ERROR: initializing encoder: %s\n", FLAC__StreamEncoderInitStatusString[init_status]);
ok = false; 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(const msg::SampleFormat& format);
~FlacEncoder(); ~FlacEncoder();
virtual double encode(msg::PcmChunk* chunk); 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: protected:
void initEncoder(); void initEncoder();
FLAC__StreamEncoder *encoder;
FLAC__StreamMetadata *metadata[2]; FLAC__StreamEncoder *encoder_;
// virtual void progress_callback(FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate); FLAC__StreamMetadata *metadata_[2];
FLAC__int32 *pcmBuffer_;
int pcmBufferSize_;
msg::PcmChunk* encodedChunk_;
size_t encodedSamples_;
}; };

View file

@ -24,10 +24,9 @@
using namespace std; 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(); init();
lastGranulepos = -1;
} }
@ -35,21 +34,14 @@ OggEncoder::OggEncoder(const msg::SampleFormat& format) : Encoder(format), eos(0
double OggEncoder::encode(msg::PcmChunk* chunk) double OggEncoder::encode(msg::PcmChunk* chunk)
{ {
double res = 0; 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; int bytes = chunk->payloadSize / 4;
float **buffer=vorbis_analysis_buffer(&vd, bytes); float **buffer=vorbis_analysis_buffer(&vd, bytes);
/* uninterleave samples */ /* uninterleave samples */
for(int i=0; i<bytes; i++) for (int i=0; i<bytes; i++)
{ {
int idx = 4*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[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; 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 more involved (potentially parallel) processing. Get a single
block for encoding now */ block for encoding now */
size_t pos = 0; 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 */ /* analysis, assume we want to use bitrate management */
vorbis_analysis(&vb,NULL); vorbis_analysis(&vb,NULL);
vorbis_bitrate_addblock(&vb); vorbis_bitrate_addblock(&vb);
while(vorbis_bitrate_flushpacket(&vd,&op)) while (vorbis_bitrate_flushpacket(&vd,&op))
{ {
/* weld the packet into the bitstream */ /* weld the packet into the bitstream */
ogg_stream_packetin(&os,&op); ogg_stream_packetin(&os, &op);
/* write out pages (if any) */ /* 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) if (result == 0)
break; break;
res = true; res = os.granulepos - lastGranulepos;
size_t nextLen = pos + og.header_len + og.body_len; size_t nextLen = pos + og.header_len + og.body_len;
// make chunk larger
if (chunk->payloadSize < nextLen) if (chunk->payloadSize < nextLen)
chunk->payload = (char*)realloc(chunk->payload, 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 /= (sampleFormat_.rate / 1000.);
res = os.granulepos; logO << "res: " << res << "\n";
else
res = os.granulepos - lastGranulepos;
res /= (sampleFormat.rate / 1000.);
lastGranulepos = os.granulepos; lastGranulepos = os.granulepos;
// make chunk smaller
chunk->payload = (char*)realloc(chunk->payload, pos); chunk->payload = (char*)realloc(chunk->payload, pos);
chunk->payloadSize = pos; chunk->payloadSize = pos;
} }
return res; 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 /* 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,
will return 'OV_EIMPL') */ will return 'OV_EIMPL') */
if(ret)exit(1); if (ret)
exit(1);
/* add a comment */ /* add a comment */
vorbis_comment_init(&vc); 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 */ /* set up the analysis state and auxiliary encoding storage */
vorbis_analysis_init(&vd,&vi); vorbis_analysis_init(&vd,&vi);
@ -185,28 +179,21 @@ void OggEncoder::init()
/* This ensures the actual /* This ensures the actual
* audio data will start on a new page, as per spec * audio data will start on a new page, as per spec
*/ */
// while(!eos){
size_t pos(0); size_t pos(0);
headerChunk = new msg::Header("ogg"); headerChunk_ = new msg::Header("ogg");
while (true) while (true)
{ {
int result=ogg_stream_flush(&os,&og); int result=ogg_stream_flush(&os,&og);
if (result == 0) if (result == 0)
break; break;
headerChunk->payloadSize += og.header_len + og.body_len; headerChunk_->payloadSize += og.header_len + og.body_len;
headerChunk->payload = (char*)realloc(headerChunk->payload, headerChunk->payloadSize); headerChunk_->payload = (char*)realloc(headerChunk_->payload, headerChunk_->payloadSize);
logD << "HeadLen: " << og.header_len << ", bodyLen: " << og.body_len << ", result: " << result << "\n"; 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; 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; 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) 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; return 1;
} }
if (runAsDaemon)
{
daemonize("/var/run/snapserver.pid");
logS(kLogNotice) << "daemon started." << endl;
}
std::clog.rdbuf(new Log("snapserver", LOG_DAEMON)); 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); msg::SampleFormat format(sampleFormat);
size_t duration = 50;
//size_t chunkSize = duration*format.rate*format.frameSize / 1000;
std::unique_ptr<Encoder> encoder; std::unique_ptr<Encoder> encoder;
if (codec == "ogg") if (codec == "ogg")
encoder.reset(new OggEncoder(sampleFormat)); encoder.reset(new OggEncoder(format));
else if (codec == "pcm") else if (codec == "pcm")
encoder.reset(new PcmEncoder(sampleFormat)); encoder.reset(new PcmEncoder(format));
else if (codec == "flac") else if (codec == "flac")
encoder.reset(new FlacEncoder(sampleFormat)); encoder.reset(new FlacEncoder(format));
else else
{ {
cout << "unknown codec: " << codec << "\n"; cout << "unknown codec: " << codec << "\n";
return 1; 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; msg::ServerSettings serverSettings;
serverSettings.bufferMs = bufferMs; 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* controlServer = new ControlServer(port);
controlServer->setServerSettings(&serverSettings); controlServer->setServerSettings(&serverSettings);
controlServer->setFormat(&format); controlServer->setFormat(&format);
controlServer->setHeader(encoder->getHeader()); controlServer->setHeader(encoder->getHeader());
controlServer->start(); controlServer->start();
signal(SIGHUP, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
PublishAvahi publishAvahi("SnapCast"); PublishAvahi publishAvahi("SnapCast");
std::vector<AvahiService> services; std::vector<AvahiService> services;
services.push_back(AvahiService("_snapcast._tcp", port)); services.push_back(AvahiService("_snapcast._tcp", port));
publishAvahi.publish(services); publishAvahi.publish(services);
timeval tvChunk;
gettimeofday(&tvChunk, NULL);
long nextTick = chronos::getTickCount();
size_t pcmReadMs = 20;
while (!g_terminated) while (!g_terminated)
{ {
int fd = open(fifoName.c_str(), O_RDONLY | O_NONBLOCK);
try try
{ {
shared_ptr<msg::PcmChunk> chunk; shared_ptr<msg::PcmChunk> chunk;
while (!g_terminated)//cin.good()) while (!g_terminated)//cin.good())
{ {
chunk.reset(new msg::PcmChunk(sampleFormat, duration)); chunk.reset(new msg::PcmChunk(sampleFormat, pcmReadMs));
int toRead = chunk->payloadSize; int toRead = chunk->payloadSize;
int len = 0; int len = 0;
do do
@ -166,9 +168,9 @@ int main(int argc, char* argv[])
double chunkDuration = encoder->encode(chunk.get()); double chunkDuration = encoder->encode(chunk.get());
if (chunkDuration > 0) if (chunkDuration > 0)
controlServer->send(chunk); controlServer->send(chunk);
//logD << chunk->tv_sec << ", " << chunk->tv_usec / 1000 << "\n"; // logO << chunkDuration << "\n";
// addUs(tvChunk, 1000*chunk->getDuration()); // addUs(tvChunk, 1000*chunk->getDuration());
nextTick += duration; nextTick += pcmReadMs;
chronos::addUs(tvChunk, chunkDuration * 1000); chronos::addUs(tvChunk, chunkDuration * 1000);
long currentTick = chronos::getTickCount(); long currentTick = chronos::getTickCount();
if (nextTick > currentTick) if (nextTick > currentTick)