mirror of
https://github.com/badaix/snapcast.git
synced 2025-05-25 15:06:21 +02:00
decoder gets sample format from header
This commit is contained in:
parent
1d1ef239b2
commit
eed7f287fb
10 changed files with 216 additions and 114 deletions
|
@ -36,7 +36,7 @@
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
Controller::Controller() : MessageReceiver(), active_(false), sampleFormat_(NULL), decoder_(NULL), player_(nullptr), asyncException_(false)
|
Controller::Controller() : MessageReceiver(), active_(false), stream_(NULL), decoder_(NULL), player_(nullptr), asyncException_(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,9 +55,9 @@ void Controller::onMessageReceived(ClientConnection* connection, const msg::Base
|
||||||
{
|
{
|
||||||
if ((stream_ != NULL) && (decoder_ != NULL))
|
if ((stream_ != NULL) && (decoder_ != NULL))
|
||||||
{
|
{
|
||||||
msg::PcmChunk* pcmChunk = new msg::PcmChunk(*sampleFormat_, 0);
|
msg::PcmChunk* pcmChunk = new msg::PcmChunk(sampleFormat_, 0);
|
||||||
pcmChunk->deserialize(baseMessage, buffer);
|
pcmChunk->deserialize(baseMessage, buffer);
|
||||||
//logD << "chunk: " << pcmChunk->payloadSize;
|
// logD << "chunk: " << pcmChunk->payloadSize << ", sampleFormat: " << sampleFormat_.rate << "\n";
|
||||||
if (decoder_->decode(pcmChunk))
|
if (decoder_->decode(pcmChunk))
|
||||||
{
|
{
|
||||||
//TODO: do decoding in thread?
|
//TODO: do decoding in thread?
|
||||||
|
@ -146,9 +146,9 @@ void Controller::worker()
|
||||||
while (active_ && !(serverSettings = clientConnection_->sendReq<msg::ServerSettings>(&requestMsg)));
|
while (active_ && !(serverSettings = clientConnection_->sendReq<msg::ServerSettings>(&requestMsg)));
|
||||||
logO << "ServerSettings - buffer: " << serverSettings->bufferMs << ", latency: " << serverSettings->latency << ", volume: " << serverSettings->volume << ", muted: " << serverSettings->muted << "\n";
|
logO << "ServerSettings - buffer: " << serverSettings->bufferMs << ", latency: " << serverSettings->latency << ", volume: " << serverSettings->volume << ", muted: " << serverSettings->muted << "\n";
|
||||||
|
|
||||||
requestMsg.request = kSampleFormat;
|
// requestMsg.request = kSampleFormat;
|
||||||
while (active_ && !(sampleFormat_ = clientConnection_->sendReq<msg::SampleFormat>(&requestMsg)));
|
// while (active_ && !(sampleFormat_ = clientConnection_->sendReq<msg::SampleFormat>(&requestMsg)));
|
||||||
logO << "SampleFormat rate: " << sampleFormat_->rate << ", bits: " << sampleFormat_->bits << ", channels: " << sampleFormat_->channels << "\n";
|
// logO << "SampleFormat rate: " << sampleFormat_->rate << ", bits: " << sampleFormat_->bits << ", channels: " << sampleFormat_->channels << "\n";
|
||||||
|
|
||||||
requestMsg.request = kHeader;
|
requestMsg.request = kHeader;
|
||||||
shared_ptr<msg::Header> headerChunk(NULL);
|
shared_ptr<msg::Header> headerChunk(NULL);
|
||||||
|
@ -162,7 +162,10 @@ void Controller::worker()
|
||||||
#endif
|
#endif
|
||||||
else if (headerChunk->codec == "flac")
|
else if (headerChunk->codec == "flac")
|
||||||
decoder_ = new FlacDecoder();
|
decoder_ = new FlacDecoder();
|
||||||
decoder_->setHeader(headerChunk.get());
|
sampleFormat_ = decoder_->setHeader(headerChunk.get());
|
||||||
|
logO << "sample rate : " << sampleFormat_.rate << "Hz\n";
|
||||||
|
logO << "bits per sample: " << sampleFormat_.bits << "\n";
|
||||||
|
logO << "channels : " << sampleFormat_.channels << "\n";
|
||||||
|
|
||||||
msg::Request timeReq(kTime);
|
msg::Request timeReq(kTime);
|
||||||
for (size_t n=0; n<50 && active_; ++n)
|
for (size_t n=0; n<50 && active_; ++n)
|
||||||
|
@ -177,7 +180,7 @@ void Controller::worker()
|
||||||
}
|
}
|
||||||
logO << "diff to server [ms]: " << (float)TimeProvider::getInstance().getDiffToServer<chronos::usec>().count() / 1000.f << "\n";
|
logO << "diff to server [ms]: " << (float)TimeProvider::getInstance().getDiffToServer<chronos::usec>().count() / 1000.f << "\n";
|
||||||
|
|
||||||
stream_ = new Stream(*sampleFormat_);
|
stream_ = new Stream(sampleFormat_);
|
||||||
stream_->setBufferLen(serverSettings->bufferMs - latency_);
|
stream_->setBufferLen(serverSettings->bufferMs - latency_);
|
||||||
|
|
||||||
#ifndef ANDROID
|
#ifndef ANDROID
|
||||||
|
|
|
@ -62,7 +62,7 @@ private:
|
||||||
ClientConnection* clientConnection_;
|
ClientConnection* clientConnection_;
|
||||||
Stream* stream_;
|
Stream* stream_;
|
||||||
std::string ip_;
|
std::string ip_;
|
||||||
std::shared_ptr<msg::SampleFormat> sampleFormat_;
|
msg::SampleFormat sampleFormat_;
|
||||||
Decoder* decoder_;
|
Decoder* decoder_;
|
||||||
PcmDevice pcmDevice_;
|
PcmDevice pcmDevice_;
|
||||||
size_t latency_;
|
size_t latency_;
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
#define DECODER_H
|
#define DECODER_H
|
||||||
#include "message/pcmChunk.h"
|
#include "message/pcmChunk.h"
|
||||||
#include "message/header.h"
|
#include "message/header.h"
|
||||||
|
#include "message/sampleFormat.h"
|
||||||
|
|
||||||
|
|
||||||
class Decoder
|
class Decoder
|
||||||
{
|
{
|
||||||
|
@ -27,7 +29,7 @@ public:
|
||||||
Decoder() {};
|
Decoder() {};
|
||||||
virtual ~Decoder() {};
|
virtual ~Decoder() {};
|
||||||
virtual bool decode(msg::PcmChunk* chunk) = 0;
|
virtual bool decode(msg::PcmChunk* chunk) = 0;
|
||||||
virtual bool setHeader(msg::Header* chunk) = 0;
|
virtual msg::SampleFormat setHeader(msg::Header* chunk) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <FLAC/stream_decoder.h>
|
#include <FLAC/stream_decoder.h>
|
||||||
#include "flacDecoder.h"
|
#include "flacDecoder.h"
|
||||||
|
#include "common/snapException.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,9 +37,11 @@ static void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecod
|
||||||
static msg::Header* flacHeader = NULL;
|
static msg::Header* flacHeader = NULL;
|
||||||
static msg::PcmChunk* flacChunk = NULL;
|
static msg::PcmChunk* flacChunk = NULL;
|
||||||
static msg::PcmChunk* pcmChunk = NULL;
|
static msg::PcmChunk* pcmChunk = NULL;
|
||||||
|
static msg::SampleFormat sampleFormat;
|
||||||
static FLAC__StreamDecoder *decoder = NULL;
|
static FLAC__StreamDecoder *decoder = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
FlacDecoder::FlacDecoder() : Decoder()
|
FlacDecoder::FlacDecoder() : Decoder()
|
||||||
{
|
{
|
||||||
flacChunk = new msg::PcmChunk();
|
flacChunk = new msg::PcmChunk();
|
||||||
|
@ -78,28 +81,25 @@ bool FlacDecoder::decode(msg::PcmChunk* chunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool FlacDecoder::setHeader(msg::Header* chunk)
|
msg::SampleFormat FlacDecoder::setHeader(msg::Header* chunk)
|
||||||
{
|
{
|
||||||
flacHeader = chunk;
|
flacHeader = chunk;
|
||||||
FLAC__bool ok = true;
|
|
||||||
FLAC__StreamDecoderInitStatus init_status;
|
FLAC__StreamDecoderInitStatus init_status;
|
||||||
|
|
||||||
if ((decoder = FLAC__stream_decoder_new()) == NULL)
|
if ((decoder = FLAC__stream_decoder_new()) == NULL)
|
||||||
{
|
throw SnapException("ERROR: allocating decoder");
|
||||||
logS(kLogErr) << "ERROR: allocating decoder\n";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// (void)FLAC__stream_decoder_set_md5_checking(decoder, true);
|
// (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_stream(decoder, read_callback, NULL, NULL, NULL, NULL, write_callback, metadata_callback, error_callback, this);
|
||||||
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]));
|
||||||
logS(kLogErr) << "ERROR: initializing decoder: " << FLAC__StreamDecoderInitStatusString[init_status] << "\n";
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
FLAC__stream_decoder_process_until_end_of_metadata(decoder);
|
|
||||||
|
|
||||||
return ok;
|
sampleFormat.rate = 0;
|
||||||
|
FLAC__stream_decoder_process_until_end_of_metadata(decoder);
|
||||||
|
if (sampleFormat.rate == 0)
|
||||||
|
throw SnapException("Sample format not found");
|
||||||
|
|
||||||
|
return sampleFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -179,9 +179,10 @@ void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMet
|
||||||
if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO)
|
if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO)
|
||||||
{
|
{
|
||||||
((FlacDecoder*)client_data)->cacheInfo_.sampleRate_ = metadata->data.stream_info.sample_rate;
|
((FlacDecoder*)client_data)->cacheInfo_.sampleRate_ = metadata->data.stream_info.sample_rate;
|
||||||
logO << "sample rate : " << metadata->data.stream_info.sample_rate << "Hz\n";
|
sampleFormat.setFormat(
|
||||||
logO << "bits per sample: " << metadata->data.stream_info.bits_per_sample << "\n";
|
metadata->data.stream_info.sample_rate,
|
||||||
logO << "channels : " << metadata->data.stream_info.channels << "\n";
|
metadata->data.stream_info.bits_per_sample,
|
||||||
|
metadata->data.stream_info.channels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ public:
|
||||||
FlacDecoder();
|
FlacDecoder();
|
||||||
virtual ~FlacDecoder();
|
virtual ~FlacDecoder();
|
||||||
virtual bool decode(msg::PcmChunk* chunk);
|
virtual bool decode(msg::PcmChunk* chunk);
|
||||||
virtual bool setHeader(msg::Header* chunk);
|
virtual msg::SampleFormat setHeader(msg::Header* chunk);
|
||||||
|
|
||||||
CacheInfo cacheInfo_;
|
CacheInfo cacheInfo_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,12 +16,15 @@
|
||||||
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 "oggDecoder.h"
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <vorbis/vorbisenc.h>
|
#include <vorbis/vorbisenc.h>
|
||||||
|
|
||||||
|
#include "oggDecoder.h"
|
||||||
|
#include "common/snapException.h"
|
||||||
|
#include "common/log.h"
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
@ -54,24 +57,24 @@ bool OggDecoder::decode(msg::PcmChunk* chunk)
|
||||||
stream initial header) We need the first page to get the stream
|
stream initial header) We need the first page to get the stream
|
||||||
serialno. */
|
serialno. */
|
||||||
bytes = chunk->payloadSize;
|
bytes = chunk->payloadSize;
|
||||||
buffer=ogg_sync_buffer(&oy, bytes);
|
buffer = ogg_sync_buffer(&oy, bytes);
|
||||||
memcpy(buffer, chunk->payload, bytes);
|
memcpy(buffer, chunk->payload, bytes);
|
||||||
ogg_sync_wrote(&oy,bytes);
|
ogg_sync_wrote(&oy,bytes);
|
||||||
|
|
||||||
|
|
||||||
chunk->payloadSize = 0;
|
chunk->payloadSize = 0;
|
||||||
convsize=4096;//bytes/vi.channels;
|
convsize = 4096;//bytes/vi.channels;
|
||||||
/* The rest is just a straight decode loop until end of stream */
|
/* The rest is just a straight decode loop until end of stream */
|
||||||
// while(!eos){
|
// while(!eos){
|
||||||
while(true)
|
while(true)
|
||||||
{
|
{
|
||||||
int result=ogg_sync_pageout(&oy,&og);
|
int result = ogg_sync_pageout(&oy, &og);
|
||||||
if (result==0)
|
if (result == 0)
|
||||||
break; /* need more data */
|
break; /* need more data */
|
||||||
if(result<0)
|
if(result < 0)
|
||||||
{
|
{
|
||||||
/* missing or corrupt data at this page position */
|
/* missing or corrupt data at this page position */
|
||||||
fprintf(stderr,"Corrupt or missing data in bitstream; continuing...\n");
|
logE << "Corrupt or missing data in bitstream; continuing...\n";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,19 +82,19 @@ bool OggDecoder::decode(msg::PcmChunk* chunk)
|
||||||
this point */
|
this point */
|
||||||
while(1)
|
while(1)
|
||||||
{
|
{
|
||||||
result=ogg_stream_packetout(&os,&op);
|
result = ogg_stream_packetout(&os, &op);
|
||||||
|
|
||||||
if(result==0)
|
if (result == 0)
|
||||||
break; /* need more data */
|
break; /* need more data */
|
||||||
if(result<0)
|
if (result < 0)
|
||||||
continue; /* missing or corrupt data at this page position */
|
continue; /* missing or corrupt data at this page position */
|
||||||
/* no reason to complain; already complained above */
|
/* no reason to complain; already complained above */
|
||||||
/* we have a packet. Decode it */
|
/* we have a packet. Decode it */
|
||||||
float **pcm;
|
float **pcm;
|
||||||
int samples;
|
int samples;
|
||||||
|
|
||||||
if(vorbis_synthesis(&vb,&op)==0) /* test for success! */
|
if (vorbis_synthesis(&vb,&op) == 0) /* test for success! */
|
||||||
vorbis_synthesis_blockin(&vd,&vb);
|
vorbis_synthesis_blockin(&vd, &vb);
|
||||||
/*
|
/*
|
||||||
|
|
||||||
**pcm is a multichannel float vector. In stereo, for
|
**pcm is a multichannel float vector. In stereo, for
|
||||||
|
@ -99,17 +102,17 @@ bool OggDecoder::decode(msg::PcmChunk* chunk)
|
||||||
the size of each channel. Convert the float values
|
the size of each channel. Convert the float values
|
||||||
(-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)
|
||||||
{
|
{
|
||||||
int bout=(samples<convsize?samples:convsize);
|
int bout = (samples<convsize?samples:convsize);
|
||||||
//cout << "samples: " << samples << ", convsize: " << convsize << "\n";
|
//cout << "samples: " << samples << ", convsize: " << convsize << "\n";
|
||||||
/* convert floats to 16 bit signed ints (host order) and
|
/* convert floats to 16 bit signed ints (host order) and
|
||||||
interleave */
|
interleave */
|
||||||
for(int i=0; i<vi.channels; i++)
|
for(int i=0; i<vi.channels; i++)
|
||||||
{
|
{
|
||||||
ogg_int16_t *ptr=convbuffer+i;
|
ogg_int16_t *ptr=convbuffer+i;
|
||||||
float *mono=pcm[i];
|
float *mono=pcm[i];
|
||||||
for(int j=0; j<bout; j++)
|
for (int j=0; j<bout; j++)
|
||||||
{
|
{
|
||||||
int val=floor(mono[j]*32767.f+.5f);
|
int val=floor(mono[j]*32767.f+.5f);
|
||||||
/* might as well guard against clipping */
|
/* might as well guard against clipping */
|
||||||
|
@ -140,73 +143,56 @@ bool OggDecoder::decode(msg::PcmChunk* chunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool OggDecoder::setHeader(msg::Header* chunk)
|
msg::SampleFormat OggDecoder::setHeader(msg::Header* chunk)
|
||||||
{
|
{
|
||||||
bytes = chunk->payloadSize;
|
bytes = chunk->payloadSize;
|
||||||
buffer=ogg_sync_buffer(&oy, bytes);
|
buffer=ogg_sync_buffer(&oy, bytes);
|
||||||
memcpy(buffer, chunk->payload, bytes);
|
memcpy(buffer, chunk->payload, bytes);
|
||||||
ogg_sync_wrote(&oy,bytes);
|
ogg_sync_wrote(&oy, bytes);
|
||||||
|
|
||||||
if(ogg_sync_pageout(&oy,&og)!=1)
|
if (ogg_sync_pageout(&oy, &og) != 1)
|
||||||
{
|
throw SnapException("Input does not appear to be an Ogg bitstream");
|
||||||
fprintf(stderr,"Input does not appear to be an Ogg bitstream.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ogg_stream_init(&os,ogg_page_serialno(&og));
|
ogg_stream_init(&os,ogg_page_serialno(&og));
|
||||||
|
|
||||||
vorbis_info_init(&vi);
|
vorbis_info_init(&vi);
|
||||||
vorbis_comment_init(&vc);
|
vorbis_comment_init(&vc);
|
||||||
if(ogg_stream_pagein(&os,&og)<0)
|
if (ogg_stream_pagein(&os, &og) < 0)
|
||||||
{
|
throw SnapException("Error reading first page of Ogg bitstream data");
|
||||||
fprintf(stderr,"Error reading first page of Ogg bitstream data.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ogg_stream_packetout(&os,&op)!=1)
|
if (ogg_stream_packetout(&os, &op) != 1)
|
||||||
{
|
throw SnapException("Error reading initial header packet");
|
||||||
fprintf(stderr,"Error reading initial header packet.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(vorbis_synthesis_headerin(&vi,&vc,&op)<0)
|
|
||||||
{
|
|
||||||
fprintf(stderr,"This Ogg bitstream does not contain Vorbis audio data.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (vorbis_synthesis_headerin(&vi, &vc, &op) < 0)
|
||||||
|
throw SnapException("This Ogg bitstream does not contain Vorbis audio data");
|
||||||
|
|
||||||
int i(0);
|
int i(0);
|
||||||
while(i<2)
|
while (i < 2)
|
||||||
{
|
{
|
||||||
while(i<2)
|
while (i < 2)
|
||||||
{
|
{
|
||||||
int result=ogg_sync_pageout(&oy,&og);
|
int result=ogg_sync_pageout(&oy, &og);
|
||||||
if(result==0)
|
if (result == 0)
|
||||||
break; /* Need more data */
|
break; /* Need more data */
|
||||||
/* Don't complain about missing or corrupt data yet. We'll
|
/* Don't complain about missing or corrupt data yet. We'll
|
||||||
catch it at the packet output phase */
|
catch it at the packet output phase */
|
||||||
if(result==1)
|
if (result == 1)
|
||||||
{
|
{
|
||||||
ogg_stream_pagein(&os,&og); /* we can ignore any errors here as they'll also become apparent at packetout */
|
ogg_stream_pagein(&os, &og); /* we can ignore any errors here as they'll also become apparent at packetout */
|
||||||
while(i<2)
|
while (i < 2)
|
||||||
{
|
{
|
||||||
result=ogg_stream_packetout(&os,&op);
|
result=ogg_stream_packetout(&os, &op);
|
||||||
if(result==0)
|
if (result == 0)
|
||||||
break;
|
break;
|
||||||
if(result<0)
|
/// Uh oh; data at some point was corrupted or missing!
|
||||||
{
|
/// We can't tolerate that in a header. Die. */
|
||||||
/* Uh oh; data at some point was corrupted or missing!
|
if (result < 0)
|
||||||
We can't tolerate that in a header. Die. */
|
throw SnapException("Corrupt secondary header. Exiting.");
|
||||||
fprintf(stderr,"Corrupt secondary header. Exiting.\n");
|
|
||||||
return false;
|
result=vorbis_synthesis_headerin(&vi, &vc, &op);
|
||||||
}
|
if (result < 0)
|
||||||
result=vorbis_synthesis_headerin(&vi,&vc,&op);
|
throw SnapException("Corrupt secondary header. Exiting.");
|
||||||
if(result<0)
|
|
||||||
{
|
|
||||||
fprintf(stderr,"Corrupt secondary header. Exiting.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,18 +206,19 @@ bool OggDecoder::setHeader(msg::Header* chunk)
|
||||||
fprintf(stderr,"%s\n",*ptr);
|
fprintf(stderr,"%s\n",*ptr);
|
||||||
++ptr;
|
++ptr;
|
||||||
}
|
}
|
||||||
fprintf(stderr,"\nBitstream is %d channel, %ldHz\n",vi.channels,vi.rate);
|
|
||||||
fprintf(stderr,"Encoded by: %s\n\n",vc.vendor);
|
|
||||||
|
|
||||||
/* OK, got and parsed all three headers. Initialize the Vorbis
|
logE << "Encoded by: " << vc.vendor << "\n";
|
||||||
packet->PCM decoder. */
|
|
||||||
if(vorbis_synthesis_init(&vd,&vi)==0) /* central decode state */
|
/// OK, got and parsed all three headers. Initialize the Vorbis packet->PCM decoder.
|
||||||
vorbis_block_init(&vd,&vb); /* local state for most of the decode
|
if (vorbis_synthesis_init(&vd, &vi) == 0)
|
||||||
so multiple block decodes can
|
vorbis_block_init(&vd, &vb);
|
||||||
proceed in parallel. We could init
|
/// central decode state
|
||||||
multiple vorbis_block structures
|
/// local state for most of the decode so multiple block decodes can proceed
|
||||||
for vd here */
|
/// in parallel. We could init multiple vorbis_block structures for vd here
|
||||||
return false;
|
|
||||||
|
|
||||||
|
msg::SampleFormat sampleFormat(vi.rate, 16, vi.channels);
|
||||||
|
return sampleFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,24 +28,22 @@ public:
|
||||||
OggDecoder();
|
OggDecoder();
|
||||||
virtual ~OggDecoder();
|
virtual ~OggDecoder();
|
||||||
virtual bool decode(msg::PcmChunk* chunk);
|
virtual bool decode(msg::PcmChunk* chunk);
|
||||||
virtual bool setHeader(msg::Header* chunk);
|
virtual msg::SampleFormat setHeader(msg::Header* chunk);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool decodePayload(msg::PcmChunk* chunk);
|
bool decodePayload(msg::PcmChunk* chunk);
|
||||||
|
|
||||||
ogg_sync_state oy; /* sync and verify incoming physical bitstream */
|
ogg_sync_state oy; /// sync and verify incoming physical bitstream
|
||||||
ogg_stream_state os; /* take physical pages, weld into a logical
|
ogg_stream_state os; /// take physical pages, weld into a logical stream of packets
|
||||||
stream of packets */
|
ogg_page og; /// one Ogg bitstream page. Vorbis packets are inside
|
||||||
ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */
|
ogg_packet op; /// one raw packet of data for decode
|
||||||
ogg_packet op; /* one raw packet of data for decode */
|
|
||||||
|
|
||||||
vorbis_info vi; /* struct that stores all the static vorbis bitstream
|
vorbis_info vi; /// struct that stores all the static vorbis bitstream settings
|
||||||
settings */
|
vorbis_comment vc; /// struct that stores all the bitstream user comments
|
||||||
vorbis_comment vc; /* struct that stores all the bitstream user comments */
|
vorbis_dsp_state vd; /// central working state for the packet->PCM decoder
|
||||||
vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
|
vorbis_block vb; /// local working space for packet->PCM decode
|
||||||
vorbis_block vb; /* local working space for packet->PCM decode */
|
|
||||||
|
|
||||||
ogg_int16_t* convbuffer; /* take 8k out of the data segment, not the stack */
|
ogg_int16_t* convbuffer; /// take 8k out of the data segment, not the stack
|
||||||
int convsize;
|
int convsize;
|
||||||
|
|
||||||
char *buffer;
|
char *buffer;
|
||||||
|
|
|
@ -16,8 +16,42 @@
|
||||||
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 "common/snapException.h"
|
||||||
|
#include "common/log.h"
|
||||||
#include "pcmDecoder.h"
|
#include "pcmDecoder.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define ID_RIFF 0x46464952
|
||||||
|
#define ID_WAVE 0x45564157
|
||||||
|
#define ID_FMT 0x20746d66
|
||||||
|
#define ID_DATA 0x61746164
|
||||||
|
|
||||||
|
struct riff_wave_header
|
||||||
|
{
|
||||||
|
uint32_t riff_id;
|
||||||
|
uint32_t riff_sz;
|
||||||
|
uint32_t wave_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct chunk_header
|
||||||
|
{
|
||||||
|
uint32_t id;
|
||||||
|
uint32_t sz;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct chunk_fmt
|
||||||
|
{
|
||||||
|
uint16_t audio_format;
|
||||||
|
uint16_t num_channels;
|
||||||
|
uint32_t sample_rate;
|
||||||
|
uint32_t byte_rate;
|
||||||
|
uint16_t block_align;
|
||||||
|
uint16_t bits_per_sample;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
PcmDecoder::PcmDecoder() : Decoder()
|
PcmDecoder::PcmDecoder() : Decoder()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -29,9 +63,61 @@ bool PcmDecoder::decode(msg::PcmChunk* chunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool PcmDecoder::setHeader(msg::Header* chunk)
|
msg::SampleFormat PcmDecoder::setHeader(msg::Header* chunk)
|
||||||
{
|
{
|
||||||
return true;
|
if (chunk->payloadSize < 44)
|
||||||
|
throw SnapException("PCM header too small");
|
||||||
|
|
||||||
|
struct riff_wave_header riff_wave_header;
|
||||||
|
struct chunk_header chunk_header;
|
||||||
|
struct chunk_fmt chunk_fmt;
|
||||||
|
chunk_fmt.sample_rate = 0;
|
||||||
|
|
||||||
|
size_t pos(0);
|
||||||
|
memcpy(&riff_wave_header, chunk->payload + pos, sizeof(riff_wave_header));
|
||||||
|
pos += sizeof(riff_wave_header);
|
||||||
|
if ((riff_wave_header.riff_id != ID_RIFF) || (riff_wave_header.wave_id != ID_WAVE))
|
||||||
|
throw SnapException("Not a riff/wave header");
|
||||||
|
|
||||||
|
bool moreChunks(true);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (pos + sizeof(chunk_header) > chunk->payloadSize)
|
||||||
|
throw SnapException("riff/wave header incomplete");
|
||||||
|
memcpy(&chunk_header, chunk->payload + pos, sizeof(chunk_header));
|
||||||
|
pos += sizeof(chunk_header);
|
||||||
|
switch (chunk_header.id)
|
||||||
|
{
|
||||||
|
case ID_FMT:
|
||||||
|
if (pos + sizeof(chunk_fmt) > chunk->payloadSize)
|
||||||
|
throw SnapException("riff/wave header incomplete");
|
||||||
|
memcpy(&chunk_fmt, chunk->payload + pos, sizeof(chunk_fmt));
|
||||||
|
pos += sizeof(chunk_fmt);
|
||||||
|
/// If the format header is larger, skip the rest
|
||||||
|
if (chunk_header.sz > sizeof(chunk_fmt))
|
||||||
|
pos += (chunk_header.sz - sizeof(chunk_fmt));
|
||||||
|
break;
|
||||||
|
case ID_DATA:
|
||||||
|
/// Stop looking for chunks
|
||||||
|
moreChunks = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/// Unknown chunk, skip bytes
|
||||||
|
pos += chunk_header.sz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (moreChunks);
|
||||||
|
|
||||||
|
|
||||||
|
if (chunk_fmt.sample_rate == 0)
|
||||||
|
throw SnapException("Sample format not found");
|
||||||
|
|
||||||
|
msg::SampleFormat sampleFormat(
|
||||||
|
chunk_fmt.sample_rate,
|
||||||
|
chunk_fmt.bits_per_sample,
|
||||||
|
chunk_fmt.num_channels);
|
||||||
|
|
||||||
|
return sampleFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ class PcmDecoder : public Decoder
|
||||||
public:
|
public:
|
||||||
PcmDecoder();
|
PcmDecoder();
|
||||||
virtual bool decode(msg::PcmChunk* chunk);
|
virtual bool decode(msg::PcmChunk* chunk);
|
||||||
virtual bool setHeader(msg::Header* chunk);
|
virtual msg::SampleFormat setHeader(msg::Header* chunk);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "pcmEncoder.h"
|
#include "pcmEncoder.h"
|
||||||
|
|
||||||
|
|
||||||
PcmEncoder::PcmEncoder(const std::string& codecOptions) : Encoder(codecOptions)
|
PcmEncoder::PcmEncoder(const std::string& codecOptions) : Encoder(codecOptions)
|
||||||
{
|
{
|
||||||
headerChunk_ = new msg::Header("pcm");
|
headerChunk_ = new msg::Header("pcm");
|
||||||
|
@ -34,6 +35,30 @@ void PcmEncoder::encode(const msg::PcmChunk* chunk)
|
||||||
|
|
||||||
void PcmEncoder::initEncoder()
|
void PcmEncoder::initEncoder()
|
||||||
{
|
{
|
||||||
|
//TODO: Endianess
|
||||||
|
headerChunk_->payloadSize = 44;
|
||||||
|
headerChunk_->payload = (char*)malloc(headerChunk_->payloadSize);
|
||||||
|
memcpy(headerChunk_->payload, "RIFF", 4);
|
||||||
|
uint32_t int32 = 36;
|
||||||
|
memcpy(headerChunk_->payload + 4, reinterpret_cast<const char *>(&int32), sizeof(uint32_t));
|
||||||
|
memcpy(headerChunk_->payload + 8, "WAVEfmt ", 8);
|
||||||
|
int32 = 16;
|
||||||
|
memcpy(headerChunk_->payload + 16, reinterpret_cast<const char *>(&int32), sizeof(uint32_t));
|
||||||
|
uint16_t int16 = 1;
|
||||||
|
memcpy(headerChunk_->payload + 20, reinterpret_cast<const char *>(&int16), sizeof(uint16_t));
|
||||||
|
int16 = sampleFormat_.channels;
|
||||||
|
memcpy(headerChunk_->payload + 22, reinterpret_cast<const char *>(&int16), sizeof(uint16_t));
|
||||||
|
int32 = sampleFormat_.rate;
|
||||||
|
memcpy(headerChunk_->payload + 24, reinterpret_cast<const char *>(&int32), sizeof(uint32_t));
|
||||||
|
int32 = sampleFormat_.rate * sampleFormat_.bits * sampleFormat_.channels / 8;
|
||||||
|
memcpy(headerChunk_->payload + 28, reinterpret_cast<const char *>(&int32), sizeof(uint32_t));
|
||||||
|
int16 = sampleFormat_.channels * ((sampleFormat_.bits + 7) / 8);
|
||||||
|
memcpy(headerChunk_->payload + 32, reinterpret_cast<const char *>(&int16), sizeof(uint16_t));
|
||||||
|
int16 = sampleFormat_.bits;
|
||||||
|
memcpy(headerChunk_->payload + 34, reinterpret_cast<const char *>(&int16), sizeof(uint16_t));
|
||||||
|
memcpy(headerChunk_->payload + 36, "data", 4);
|
||||||
|
int32 = 0;
|
||||||
|
memcpy(headerChunk_->payload + 40, reinterpret_cast<const char *>(&int32), sizeof(uint32_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue