diff --git a/client/Makefile b/client/Makefile index 153655be..def5c69b 100644 --- a/client/Makefile +++ b/client/Makefile @@ -1,9 +1,9 @@ VERSION = 0.1 CC = /usr/bin/g++ CFLAGS = -std=gnu++0x -static-libgcc -static-libstdc++ -Wall -Wno-unused-function -O3 -D_REENTRANT -DVERSION=\"$(VERSION)\" -I.. -LDFLAGS = -lrt -lpthread -lboost_system -lboost_program_options -lasound -logg -lvorbis -lvorbisenc +LDFLAGS = -lrt -lpthread -lboost_system -lboost_program_options -lasound -logg -lvorbis -lvorbisenc -lFLAC -OBJ = snapClient.o stream.o alsaPlayer.o clientConnection.o timeProvider.o oggDecoder.o pcmDecoder.o controller.o ../message/pcmChunk.o ../common/log.o ../message/sampleFormat.o +OBJ = snapClient.o stream.o alsaPlayer.o clientConnection.o timeProvider.o oggDecoder.o pcmDecoder.o flacDecoder.o controller.o ../message/pcmChunk.o ../common/log.o ../message/sampleFormat.o BIN = snapclient all: client diff --git a/client/alsaPlayer.cpp b/client/alsaPlayer.cpp index da34f855..4713ccd7 100644 --- a/client/alsaPlayer.cpp +++ b/client/alsaPlayer.cpp @@ -97,7 +97,7 @@ void Player::start() snd_pcm_sw_params(pcm_handle_, swparams); active_ = true; - playerThread_ = new thread(&Player::worker, this); + playerThread_ = thread(&Player::worker, this); } @@ -108,12 +108,10 @@ Player::~Player() void Player::stop() { - active_ = false; - if (playerThread_ != NULL) + if (active_) { - playerThread_->join(); - delete playerThread_; - playerThread_ = NULL; + active_ = false; + playerThread_.join(); } if (pcm_handle_ != NULL) diff --git a/client/alsaPlayer.h b/client/alsaPlayer.h index f09f4a1d..7356305f 100644 --- a/client/alsaPlayer.h +++ b/client/alsaPlayer.h @@ -26,7 +26,7 @@ private: char *buff_; std::atomic active_; Stream* stream_; - std::thread* playerThread_; + std::thread playerThread_; PcmDevice pcmDevice_; }; diff --git a/client/controller.cpp b/client/controller.cpp index e66a04e2..ce011d86 100644 --- a/client/controller.cpp +++ b/client/controller.cpp @@ -5,6 +5,7 @@ #include #include "oggDecoder.h" #include "pcmDecoder.h" +#include "flacDecoder.h" #include "alsaPlayer.h" #include "timeProvider.h" #include "common/log.h" @@ -99,6 +100,8 @@ void Controller::worker() decoder_ = new OggDecoder(); else if (headerChunk->codec == "pcm") decoder_ = new PcmDecoder(); + else if (headerChunk->codec == "flac") + decoder_ = new FlacDecoder(); decoder_->setHeader(headerChunk.get()); msg::Request timeReq(kTime); @@ -149,8 +152,8 @@ void Controller::worker() delete decoder_; decoder_ = NULL; logO << "done" << endl; - if (active_) - usleep(500*1000); + for (size_t n=0; (n<10) && active_; ++n) + usleep(100*1000); } } logD << "Thread stopped\n"; diff --git a/client/flacDecoder.cpp b/client/flacDecoder.cpp new file mode 100644 index 00000000..ee930a1d --- /dev/null +++ b/client/flacDecoder.cpp @@ -0,0 +1,187 @@ +#include "flacDecoder.h" +#include +#include +#include +#include +#include "common/log.h" + +using namespace std; + + +static FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); +static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data); +static void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); +static void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); + +static FLAC__uint64 total_samples = 0; +static unsigned sample_rate = 0; +static unsigned channels = 0; +static unsigned bps = 0; +static msg::Header* flacHeader = NULL; +static msg::PcmChunk* flacChunk = NULL; +static FLAC__StreamDecoder *decoder = 0; + + +FlacDecoder::FlacDecoder() : Decoder() +{ +} + + +FlacDecoder::~FlacDecoder() +{ +} + + +bool FlacDecoder::decode(msg::PcmChunk* chunk) +{ + flacChunk = chunk; +//logO << "Decode start: " << chunk->payloadSize << endl; +// flacChunk->payload = (char*)realloc(flacChunk->payload, chunk->payloadSize); +// memcpy(flacChunk->payload, chunk->payload, chunk->payloadSize); +// flacChunk->payloadSize = chunk->payloadSize; + FLAC__stream_decoder_process_single(decoder); + FLAC__stream_decoder_process_single(decoder); +//logO << "Decode end\n" << endl; + return true; +} + + +bool FlacDecoder::setHeader(msg::Header* chunk) +{ + flacHeader = chunk; + FLAC__bool ok = true; + FLAC__StreamDecoderInitStatus init_status; + + if((decoder = FLAC__stream_decoder_new()) == NULL) { + fprintf(stderr, "ERROR: allocating decoder\n"); + return 1; + } + +// (void)FLAC__stream_decoder_set_md5_checking(decoder, true); + + init_status = FLAC__stream_decoder_init_stream(decoder, read_callback, NULL, NULL, NULL, NULL, write_callback, metadata_callback, error_callback, this); + +// init_status = FLAC__stream_decoder_init_file(decoder, argv[1], write_callback, metadata_callback, error_callback, fout); + if(init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) { + fprintf(stderr, "ERROR: initializing decoder: %s\n", FLAC__StreamDecoderInitStatusString[init_status]); + ok = false; + } +// FLAC__stream_decoder_process_until_end_of_stream(decoder); + FLAC__stream_decoder_process_until_end_of_metadata(decoder); + + return ok; +} + + +FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +{ + if (flacHeader != NULL) + { + *bytes = flacHeader->payloadSize; + memcpy(buffer, flacHeader->payload, *bytes); + flacHeader = NULL; + } + else if (flacChunk != NULL) + { +//logO << "Read: " << *bytes << "\t" << flacChunk->payloadSize << "\n"; + if (*bytes > flacChunk->payloadSize) + *bytes = flacChunk->payloadSize; + + if (flacChunk->payloadSize == 0) + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + + memcpy(buffer, flacChunk->payload, *bytes); + memmove(flacChunk->payload, flacChunk->payload + *bytes, flacChunk->payloadSize - *bytes); + flacChunk->payloadSize = flacChunk->payloadSize - *bytes; + flacChunk->payload = (char*)realloc(flacChunk->payload, flacChunk->payloadSize); +//logO << "Read end\n"; +// return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + } + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; +} + + +FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ +//logO << "Write start\n"; + (void)decoder; + + if(channels != 2 || bps != 16) { + fprintf(stderr, "ERROR: this example only supports 16bit stereo streams\n"); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + if(frame->header.channels != 2) { + fprintf(stderr, "ERROR: This frame contains %d channels (should be 2)\n", frame->header.channels); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + if(buffer [0] == NULL) { + fprintf(stderr, "ERROR: buffer [0] is NULL\n"); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + if(buffer [1] == NULL) { + fprintf(stderr, "ERROR: buffer [1] is NULL\n"); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + + if (flacChunk != NULL) + { + size_t bytes = frame->header.blocksize * 4; +//logO << "blocksize: " << frame->header.blocksize << "\tframe_number: " << frame->header.number.frame_number << "\tsample_number: " << frame->header.number.sample_number << "\n"; +//logO << "Write: " << bytes << "\n"; +//flacChunk->payloadSize = 0; + flacChunk->payload = (char*)realloc(flacChunk->payload, flacChunk->payloadSize + bytes); + for(size_t i = 0; i < frame->header.blocksize; i++) + { + memcpy(flacChunk->payload + flacChunk->payloadSize + 4*i, (char*)(buffer[0] + i), 2); + memcpy(flacChunk->payload + flacChunk->payloadSize + 4*i+2, (char*)(buffer[1] + i), 2); +//logO << 4*i+2 << "\t" << bytes << "\n"; + } + flacChunk->payloadSize += bytes; +//logO << "Write end: " << flacChunk->payloadSize << "\n"; + } + + /* write decoded PCM samples */ +/* for(i = 0; i < frame->header.blocksize; i++) { + if( + !write_little_endian_int16(f, (FLAC__int16)buffer[0][i]) || // left channel + !write_little_endian_int16(f, (FLAC__int16)buffer[1][i]) // right channel + ) { + fprintf(stderr, "ERROR: write error\n"); + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + } +*/ + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + + +void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) +{ + (void)decoder, (void)client_data; + + /* print some stats */ + if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { + /* save for later */ + total_samples = metadata->data.stream_info.total_samples; + sample_rate = metadata->data.stream_info.sample_rate; + channels = metadata->data.stream_info.channels; + bps = metadata->data.stream_info.bits_per_sample; + + logO << "sample rate : " << sample_rate << "Hz\n"; + logO << "channels : " << channels << "\n"; + logO << "bits per sample: " << bps << "\n"; + logO << "total samples : " << total_samples << "\n"; + } +} + + +void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +{ + (void)decoder, (void)client_data; + fprintf(stderr, "Got error callback: %s\n", FLAC__StreamDecoderErrorStatusString[status]); +} + + + + + diff --git a/client/flacDecoder.h b/client/flacDecoder.h new file mode 100644 index 00000000..0f358439 --- /dev/null +++ b/client/flacDecoder.h @@ -0,0 +1,18 @@ +#ifndef FLAC_DECODER_H +#define FLAC_DECODER_H +#include "decoder.h" + + +class FlacDecoder : public Decoder +{ +public: + FlacDecoder(); + virtual ~FlacDecoder(); + virtual bool decode(msg::PcmChunk* chunk); + virtual bool setHeader(msg::Header* chunk); +}; + + +#endif + + diff --git a/server/Makefile b/server/Makefile index 8ac227bb..5fa6973c 100644 --- a/server/Makefile +++ b/server/Makefile @@ -1,9 +1,9 @@ VERSION = 0.1 CC = /usr/bin/g++ CFLAGS = -std=gnu++0x -Wall -Wno-unused-function -O3 -D_REENTRANT -DVERSION=\"$(VERSION)\" -I.. -LDFLAGS = -lrt -lpthread -lboost_system -lboost_program_options -lvorbis -lvorbisenc -logg +LDFLAGS = -lrt -lpthread -lboost_system -lboost_program_options -lvorbis -lvorbisenc -logg -lFLAC -OBJ = snapServer.o controlServer.o pcmEncoder.o oggEncoder.o serverSession.o ../common/log.o ../message/pcmChunk.o ../message/sampleFormat.o +OBJ = snapServer.o controlServer.o flacEncoder.o pcmEncoder.o oggEncoder.o serverSession.o ../common/log.o ../message/pcmChunk.o ../message/sampleFormat.o BIN = snapserver all: server diff --git a/server/flacEncoder.cpp b/server/flacEncoder.cpp index 7c20ffe4..95b88e52 100644 --- a/server/flacEncoder.cpp +++ b/server/flacEncoder.cpp @@ -5,28 +5,75 @@ using namespace std; -FlacEncoder::FlacEncoder(const msg::SampleFormat& format) : Encoder(format) +FlacEncoder::FlacEncoder(const msg::SampleFormat& format) : Encoder(format), encoder(0) { - headerChunk = new HeaderMessage("flac"); + initEncoder(); +} + + +#define READSIZE 16384 + +static FLAC__int32 pcm[READSIZE/*samples*/ * 2/*channels*/]; +size_t encodedSamples = 0; +static msg::PcmChunk* encodedChunk; + + +msg::Header* FlacEncoder::getHeaderChunk() +{ + return headerChunk; } double FlacEncoder::encode(msg::PcmChunk* chunk) { - return chunk->duration().count(); +logD << "payload: " << chunk->payloadSize << "\tsamples: " << chunk->payloadSize/4 << "\n"; + int samples = chunk->payloadSize / 4; + for(int i=0; ipayload[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 (encodedSamples > 0) + { +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); + } + + return res;//chunk->duration().count(); } -#define READSIZE 1024 -static unsigned total_samples = 0; /* can use a 32-bit number due to WAVE size limitations */ -static FLAC__byte buffer[READSIZE/*samples*/ * 2/*bytes_per_sample*/ * 2/*channels*/]; /* we read the WAVE data into here */ -static FLAC__int32 pcm[READSIZE/*samples*/ * 2/*channels*/]; - - - -static void write_callback(const FLAC__StreamEncoder *encoder, const unsigned char*, long unsigned int bytes, unsigned int samples, unsigned int current_frame, void *client_data) +FLAC__StreamEncoderWriteStatus write_callback(const FLAC__StreamEncoder *encoder, + const FLAC__byte buffer[], + size_t bytes, + unsigned samples, + unsigned current_frame, + void *client_data) { - cout << "write_callback: " << bytes << ", " << samples << ", " << current_frame << "\n"; + 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; } @@ -35,8 +82,9 @@ static void write_callback(const FLAC__StreamEncoder *encoder, const unsigned ch void FlacEncoder::initEncoder() { + encodedChunk = new msg::PcmChunk(); + headerChunk = new msg::Header("flac"); FLAC__bool ok = true; - FLAC__StreamEncoder *encoder = 0; FLAC__StreamEncoderInitStatus init_status; FLAC__StreamMetadata *metadata[2]; FLAC__StreamMetadata_VorbisComment_Entry entry; @@ -48,11 +96,10 @@ void FlacEncoder::initEncoder() } ok &= FLAC__stream_encoder_set_verify(encoder, true); - ok &= FLAC__stream_encoder_set_compression_level(encoder, 5); + ok &= FLAC__stream_encoder_set_compression_level(encoder, 8); 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_total_samples_estimate(encoder, 0); // now add some metadata; we'll add some tags and a padding block if(ok) { @@ -76,7 +123,7 @@ void FlacEncoder::initEncoder() // initialize encoder if(ok) { - init_status = FLAC__stream_encoder_init_stream(encoder, write_callback, NULL, NULL, NULL, NULL); + 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; @@ -122,12 +169,13 @@ void FlacEncoder::initEncoder() */ } - +/* 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); } +*/ diff --git a/server/flacEncoder.h b/server/flacEncoder.h index 4a91673d..7a220017 100644 --- a/server/flacEncoder.h +++ b/server/flacEncoder.h @@ -14,9 +14,11 @@ class FlacEncoder : public Encoder public: FlacEncoder(const msg::SampleFormat& format); virtual double encode(msg::PcmChunk* chunk); + msg::Header* getHeaderChunk(); protected: void initEncoder(); + FLAC__StreamEncoder *encoder; // virtual void progress_callback(FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate); }; diff --git a/server/snapServer.cpp b/server/snapServer.cpp index d2ade856..09978dca 100644 --- a/server/snapServer.cpp +++ b/server/snapServer.cpp @@ -10,6 +10,7 @@ #include "message/message.h" #include "pcmEncoder.h" #include "oggEncoder.h" +#include "flacEncoder.h" #include "controlServer.h" @@ -92,10 +93,7 @@ int main(int argc, char* argv[]) else if (codec == "pcm") encoder.reset(new PcmEncoder(sampleFormat)); else if (codec == "flac") - { - logO << "Not yet supported\n"; - return 1; - } + encoder.reset(new FlacEncoder(sampleFormat)); else { logO << "unknown codec: " << codec << "\n"; @@ -144,8 +142,8 @@ int main(int argc, char* argv[]) controlServer->send(chunk); //logD << chunk->tv_sec << ", " << chunk->tv_usec / 1000 << "\n"; // addUs(tvChunk, 1000*chunk->getDuration()); - chronos::addUs(tvChunk, chunkDuration * 1000); nextTick += duration; + chronos::addUs(tvChunk, chunkDuration * 1000); long currentTick = chronos::getTickCount(); if (nextTick > currentTick) {