/*** This file is part of snapcast Copyright (C) 2014-2019 Johannes Pohl This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ***/ #include "flacDecoder.h" #include "common/aixlog.hpp" #include "common/endian.hpp" #include "common/snapException.h" #include #include #include 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 msg::CodecHeader* flacHeader = nullptr; static msg::PcmChunk* flacChunk = nullptr; static msg::PcmChunk* pcmChunk = nullptr; static SampleFormat sampleFormat; static FLAC__StreamDecoder* decoder = nullptr; FlacDecoder::FlacDecoder() : Decoder(), lastError_(nullptr) { flacChunk = new msg::PcmChunk(); } FlacDecoder::~FlacDecoder() { std::lock_guard lock(mutex_); delete flacChunk; delete decoder; } bool FlacDecoder::decode(msg::PcmChunk* chunk) { std::lock_guard lock(mutex_); cacheInfo_.reset(); pcmChunk = chunk; flacChunk->payload = (char*)realloc(flacChunk->payload, chunk->payloadSize); memcpy(flacChunk->payload, chunk->payload, chunk->payloadSize); flacChunk->payloadSize = chunk->payloadSize; pcmChunk->payload = (char*)realloc(pcmChunk->payload, 0); pcmChunk->payloadSize = 0; while (flacChunk->payloadSize > 0) { if (!FLAC__stream_decoder_process_single(decoder)) { return false; } if (lastError_) { LOG(ERROR) << "FLAC decode error: " << FLAC__StreamDecoderErrorStatusString[*lastError_] << "\n"; lastError_ = nullptr; return false; } } 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; LOG(DEBUG) << "Cached: " << cacheInfo_.cachedBlocks_ << ", " << diffMs << "ms, " << s << "s, " << us << "us\n"; chunk->timestamp = chunk->timestamp - tv(s, us); } return true; } SampleFormat FlacDecoder::setHeader(msg::CodecHeader* chunk) { flacHeader = chunk; FLAC__StreamDecoderInitStatus init_status; if ((decoder = FLAC__stream_decoder_new()) == nullptr) throw SnapException("ERROR: allocating decoder"); // (void)FLAC__stream_decoder_set_md5_checking(decoder, true); init_status = FLAC__stream_decoder_init_stream(decoder, read_callback, nullptr, nullptr, nullptr, nullptr, write_callback, metadata_callback, error_callback, this); if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) throw SnapException("ERROR: initializing decoder: " + string(FLAC__StreamDecoderInitStatusString[init_status])); sampleFormat.rate = 0; FLAC__stream_decoder_process_until_end_of_metadata(decoder); if (sampleFormat.rate == 0) throw SnapException("Sample format not found"); return sampleFormat; } FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder* decoder, FLAC__byte buffer[], size_t* bytes, void* client_data) { if (flacHeader != nullptr) { *bytes = flacHeader->payloadSize; memcpy(buffer, flacHeader->payload, *bytes); flacHeader = nullptr; } else if (flacChunk != nullptr) { // cerr << "read_callback: " << *bytes << ", avail: " << flacChunk->payloadSize << "\n"; static_cast(client_data)->cacheInfo_.isCachedChunk_ = false; if (*bytes > flacChunk->payloadSize) *bytes = flacChunk->payloadSize; // if (*bytes == 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); } 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) { (void)decoder; if (pcmChunk != nullptr) { size_t bytes = frame->header.blocksize * sampleFormat.frameSize; FlacDecoder* flacDecoder = static_cast(client_data); if (flacDecoder->cacheInfo_.isCachedChunk_) flacDecoder->cacheInfo_.cachedBlocks_ += frame->header.blocksize; pcmChunk->payload = (char*)realloc(pcmChunk->payload, pcmChunk->payloadSize + bytes); for (size_t channel = 0; channel < sampleFormat.channels; ++channel) { if (buffer[channel] == nullptr) { SLOG(ERROR) << "ERROR: buffer[" << channel << "] is NULL\n"; return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; } if (sampleFormat.sampleSize == 1) { int8_t* chunkBuffer = (int8_t*)(pcmChunk->payload + pcmChunk->payloadSize); for (size_t i = 0; i < frame->header.blocksize; i++) chunkBuffer[sampleFormat.channels * i + channel] = (int8_t)(buffer[channel][i]); } else if (sampleFormat.sampleSize == 2) { int16_t* chunkBuffer = (int16_t*)(pcmChunk->payload + pcmChunk->payloadSize); for (size_t i = 0; i < frame->header.blocksize; i++) chunkBuffer[sampleFormat.channels * i + channel] = SWAP_16((int16_t)(buffer[channel][i])); } else if (sampleFormat.sampleSize == 4) { int32_t* chunkBuffer = (int32_t*)(pcmChunk->payload + pcmChunk->payloadSize); for (size_t i = 0; i < frame->header.blocksize; i++) chunkBuffer[sampleFormat.channels * i + channel] = SWAP_32((int32_t)(buffer[channel][i])); } } pcmChunk->payloadSize += bytes; } return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } void metadata_callback(const FLAC__StreamDecoder* decoder, const FLAC__StreamMetadata* metadata, void* client_data) { (void)decoder; /* print some stats */ if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { static_cast(client_data)->cacheInfo_.sampleRate_ = metadata->data.stream_info.sample_rate; sampleFormat.setFormat(metadata->data.stream_info.sample_rate, metadata->data.stream_info.bits_per_sample, metadata->data.stream_info.channels); } } void error_callback(const FLAC__StreamDecoder* decoder, FLAC__StreamDecoderErrorStatus status, void* client_data) { (void)decoder, (void)client_data; SLOG(ERROR) << "Got error callback: " << FLAC__StreamDecoderErrorStatusString[status] << "\n"; static_cast(client_data)->lastError_ = std::unique_ptr(new FLAC__StreamDecoderErrorStatus(status)); }