mirror of
https://github.com/badaix/snapcast.git
synced 2025-04-29 10:17:16 +02:00
fixed FLAC playback timing
This commit is contained in:
parent
7e1a8279df
commit
a3dc6bb5a1
3 changed files with 64 additions and 51 deletions
|
@ -16,13 +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 "flacDecoder.h"
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <FLAC/stream_decoder.h>
|
#include <FLAC/stream_decoder.h>
|
||||||
|
#include "flacDecoder.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,18 +54,26 @@ FlacDecoder::~FlacDecoder()
|
||||||
|
|
||||||
bool FlacDecoder::decode(msg::PcmChunk* chunk)
|
bool FlacDecoder::decode(msg::PcmChunk* chunk)
|
||||||
{
|
{
|
||||||
|
cacheInfo_.reset();
|
||||||
pcmChunk = chunk;
|
pcmChunk = chunk;
|
||||||
//logO << "Decode start: " << chunk->payloadSize << endl;
|
|
||||||
flacChunk->payload = (char*)realloc(flacChunk->payload, chunk->payloadSize);
|
flacChunk->payload = (char*)realloc(flacChunk->payload, chunk->payloadSize);
|
||||||
memcpy(flacChunk->payload, chunk->payload, chunk->payloadSize);
|
memcpy(flacChunk->payload, chunk->payload, chunk->payloadSize);
|
||||||
flacChunk->payloadSize = chunk->payloadSize;
|
flacChunk->payloadSize = chunk->payloadSize;
|
||||||
|
|
||||||
pcmChunk->payload = (char*)realloc(pcmChunk->payload, 0);
|
pcmChunk->payload = (char*)realloc(pcmChunk->payload, 0);
|
||||||
pcmChunk->payloadSize = 0;
|
pcmChunk->payloadSize = 0;
|
||||||
FLAC__stream_decoder_process_single(decoder);
|
while (flacChunk->payloadSize > 0)
|
||||||
if (flacChunk->payloadSize > 0)
|
|
||||||
FLAC__stream_decoder_process_single(decoder);
|
FLAC__stream_decoder_process_single(decoder);
|
||||||
//logO << "Decode end\n" << endl;
|
|
||||||
|
if ((cacheInfo_.cachedBlocks_ > 0) && (cacheInfo_.sampleRate_ != 0))
|
||||||
|
{
|
||||||
|
double diffMs = cacheInfo_.cachedBlocks_ / ((double)cacheInfo_.sampleRate_ / 1000.);
|
||||||
|
int32_t s = (diffMs / 1000);
|
||||||
|
int32_t us = (diffMs * 1000);
|
||||||
|
us %= 1000000;
|
||||||
|
logD << "Cached: " << cacheInfo_.cachedBlocks_ << ", " << diffMs << "ms, " << s << "s, " << us << "us\n";
|
||||||
|
chunk->timestamp = chunk->timestamp - tv(s, us);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,21 +84,19 @@ bool FlacDecoder::setHeader(msg::Header* chunk)
|
||||||
FLAC__bool ok = true;
|
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)
|
||||||
fprintf(stderr, "ERROR: allocating decoder\n");
|
{
|
||||||
return 1;
|
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)
|
||||||
// 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) {
|
logS(kLogErr) << "ERROR: initializing decoder: " << FLAC__StreamDecoderInitStatusString[init_status] << "\n";
|
||||||
fprintf(stderr, "ERROR: initializing decoder: %s\n", FLAC__StreamDecoderInitStatusString[init_status]);
|
|
||||||
ok = false;
|
ok = false;
|
||||||
}
|
}
|
||||||
// FLAC__stream_decoder_process_until_end_of_stream(decoder);
|
|
||||||
FLAC__stream_decoder_process_until_end_of_metadata(decoder);
|
FLAC__stream_decoder_process_until_end_of_metadata(decoder);
|
||||||
|
|
||||||
return ok;
|
return ok;
|
||||||
|
@ -106,23 +113,14 @@ FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder,
|
||||||
}
|
}
|
||||||
else if (flacChunk != NULL)
|
else if (flacChunk != NULL)
|
||||||
{
|
{
|
||||||
//logO << "Read: " << *bytes << "\t" << flacChunk->payloadSize << "\n";
|
((FlacDecoder*)client_data)->cacheInfo_.isCachedChunk_ = false;
|
||||||
FLAC__StreamDecoderReadStatus result = FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
|
||||||
if (*bytes > flacChunk->payloadSize)
|
if (*bytes > flacChunk->payloadSize)
|
||||||
*bytes = flacChunk->payloadSize;
|
*bytes = flacChunk->payloadSize;
|
||||||
// else
|
|
||||||
// result = FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
|
|
||||||
|
|
||||||
// if (flacChunk->payloadSize == 0)
|
|
||||||
// return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
|
|
||||||
|
|
||||||
memcpy(buffer, flacChunk->payload, *bytes);
|
memcpy(buffer, flacChunk->payload, *bytes);
|
||||||
memmove(flacChunk->payload, flacChunk->payload + *bytes, flacChunk->payloadSize - *bytes);
|
memmove(flacChunk->payload, flacChunk->payload + *bytes, flacChunk->payloadSize - *bytes);
|
||||||
flacChunk->payloadSize = flacChunk->payloadSize - *bytes;
|
flacChunk->payloadSize = flacChunk->payloadSize - *bytes;
|
||||||
flacChunk->payload = (char*)realloc(flacChunk->payload, flacChunk->payloadSize);
|
flacChunk->payload = (char*)realloc(flacChunk->payload, flacChunk->payloadSize);
|
||||||
//logO << "Read end\n";
|
|
||||||
// return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
||||||
}
|
}
|
||||||
|
@ -130,7 +128,6 @@ FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder,
|
||||||
|
|
||||||
FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
|
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;
|
(void)decoder;
|
||||||
|
|
||||||
/* if(channels != 2 || bps != 16) {
|
/* if(channels != 2 || bps != 16) {
|
||||||
|
@ -141,30 +138,34 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder
|
||||||
fprintf(stderr, "ERROR: This frame contains %d channels (should be 2)\n", frame->header.channels);
|
fprintf(stderr, "ERROR: This frame contains %d channels (should be 2)\n", frame->header.channels);
|
||||||
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
||||||
}
|
}
|
||||||
*/ if(buffer [0] == NULL) {
|
*/ if (buffer[0] == NULL)
|
||||||
fprintf(stderr, "ERROR: buffer [0] is NULL\n");
|
{
|
||||||
|
logS(kLogErr) << "ERROR: buffer [0] is NULL\n";
|
||||||
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
||||||
}
|
}
|
||||||
if(buffer [1] == NULL) {
|
|
||||||
fprintf(stderr, "ERROR: buffer [1] is NULL\n");
|
if (buffer[1] == NULL)
|
||||||
|
{
|
||||||
|
logS(kLogErr) << "ERROR: buffer [1] is NULL\n";
|
||||||
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pcmChunk != NULL)
|
if (pcmChunk != NULL)
|
||||||
{
|
{
|
||||||
|
//TODO: hard coded to 2 channels, 16 bit
|
||||||
size_t bytes = frame->header.blocksize * 4;
|
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 << "\tpayloadSize: " << pcmChunk->payloadSize + bytes << "\n";
|
if (((FlacDecoder*)client_data)->cacheInfo_.isCachedChunk_)
|
||||||
//flacChunk->payloadSize = 0;
|
((FlacDecoder*)client_data)->cacheInfo_.cachedBlocks_ += frame->header.blocksize;
|
||||||
|
|
||||||
pcmChunk->payload = (char*)realloc(pcmChunk->payload, pcmChunk->payloadSize + bytes);
|
pcmChunk->payload = (char*)realloc(pcmChunk->payload, pcmChunk->payloadSize + bytes);
|
||||||
|
|
||||||
for(size_t i = 0; i < frame->header.blocksize; i++)
|
for(size_t i = 0; i < frame->header.blocksize; i++)
|
||||||
{
|
{
|
||||||
memcpy(pcmChunk->payload + pcmChunk->payloadSize + 4*i, (char*)(buffer[0] + i), 2);
|
memcpy(pcmChunk->payload + pcmChunk->payloadSize + 4*i, (char*)(buffer[0] + i), 2);
|
||||||
memcpy(pcmChunk->payload + pcmChunk->payloadSize + 4*i+2, (char*)(buffer[1] + i), 2);
|
memcpy(pcmChunk->payload + pcmChunk->payloadSize + 4*i+2, (char*)(buffer[1] + i), 2);
|
||||||
}
|
}
|
||||||
pcmChunk->payloadSize += bytes;
|
pcmChunk->payloadSize += bytes;
|
||||||
//logO << "Write end: " << flacChunk->payloadSize << "\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||||
|
@ -173,11 +174,11 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder
|
||||||
|
|
||||||
void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
|
void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
|
||||||
{
|
{
|
||||||
(void)decoder, (void)client_data;
|
(void)decoder;
|
||||||
|
|
||||||
/* print some stats */
|
/* print some stats */
|
||||||
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;
|
||||||
logO << "sample rate : " << metadata->data.stream_info.sample_rate << "Hz\n";
|
logO << "sample rate : " << metadata->data.stream_info.sample_rate << "Hz\n";
|
||||||
logO << "channels : " << metadata->data.stream_info.channels << "\n";
|
logO << "channels : " << metadata->data.stream_info.channels << "\n";
|
||||||
logO << "bits per sample: " << metadata->data.stream_info.bits_per_sample << "\n";
|
logO << "bits per sample: " << metadata->data.stream_info.bits_per_sample << "\n";
|
||||||
|
@ -188,7 +189,7 @@ void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMet
|
||||||
void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
|
void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
|
||||||
{
|
{
|
||||||
(void)decoder, (void)client_data;
|
(void)decoder, (void)client_data;
|
||||||
fprintf(stderr, "Got error callback: %s\n", FLAC__StreamDecoderErrorStatusString[status]);
|
logS(kLogErr) << "Got error callback: " << FLAC__StreamDecoderErrorStatusString[status] << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,27 @@
|
||||||
|
|
||||||
#ifndef FLAC_DECODER_H
|
#ifndef FLAC_DECODER_H
|
||||||
#define FLAC_DECODER_H
|
#define FLAC_DECODER_H
|
||||||
|
|
||||||
#include "decoder.h"
|
#include "decoder.h"
|
||||||
|
|
||||||
|
struct CacheInfo
|
||||||
|
{
|
||||||
|
CacheInfo() : sampleRate_(0)
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
isCachedChunk_ = true;
|
||||||
|
cachedBlocks_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isCachedChunk_;
|
||||||
|
size_t cachedBlocks_;
|
||||||
|
size_t sampleRate_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class FlacDecoder : public Decoder
|
class FlacDecoder : public Decoder
|
||||||
{
|
{
|
||||||
|
@ -28,6 +47,8 @@ public:
|
||||||
virtual ~FlacDecoder();
|
virtual ~FlacDecoder();
|
||||||
virtual bool decode(msg::PcmChunk* chunk);
|
virtual bool decode(msg::PcmChunk* chunk);
|
||||||
virtual bool setHeader(msg::Header* chunk);
|
virtual bool setHeader(msg::Header* chunk);
|
||||||
|
|
||||||
|
CacheInfo cacheInfo_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -94,21 +94,12 @@ struct tv
|
||||||
tv result(*this);
|
tv result(*this);
|
||||||
result.sec -= other.sec;
|
result.sec -= other.sec;
|
||||||
result.usec -= other.usec;
|
result.usec -= other.usec;
|
||||||
if (result.usec > 0)
|
while (result.usec < 0)
|
||||||
{
|
{
|
||||||
result.sec += 1;
|
result.sec -= 1;
|
||||||
result.usec = 1000000 - result.usec;
|
result.usec += 1000000;
|
||||||
}
|
}
|
||||||
else if (result.usec < 0)
|
return result;
|
||||||
{
|
|
||||||
result.usec *= -1;
|
|
||||||
}
|
|
||||||
/* else if (result.usec >= 1000000)
|
|
||||||
{
|
|
||||||
result.usec -= 1000000;
|
|
||||||
result.sec += 1;
|
|
||||||
}
|
|
||||||
*/ return result;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue