experimental FLAC support

This commit is contained in:
badaix 2015-01-04 12:32:49 +01:00
parent 176ed8abc0
commit d99c3cb9a0
10 changed files with 289 additions and 35 deletions

View file

@ -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

View file

@ -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)

View file

@ -26,7 +26,7 @@ private:
char *buff_;
std::atomic<bool> active_;
Stream* stream_;
std::thread* playerThread_;
std::thread playerThread_;
PcmDevice pcmDevice_;
};

View file

@ -5,6 +5,7 @@
#include <unistd.h>
#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";

187
client/flacDecoder.cpp Normal file
View file

@ -0,0 +1,187 @@
#include "flacDecoder.h"
#include <iostream>
#include <cstring>
#include <cmath>
#include <FLAC/stream_decoder.h>
#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]);
}

18
client/flacDecoder.h Normal file
View file

@ -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

View file

@ -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

View file

@ -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<chronos::msec>().count();
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);
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<chronos::msec>().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);
}
*/

View file

@ -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);
};

View file

@ -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)
{