merge PR#4

This commit is contained in:
badaix 2019-11-01 23:07:32 +01:00
commit 0c7691756c
10 changed files with 235 additions and 7 deletions

View file

@ -18,6 +18,7 @@ The Snapserver reads PCM chunks from the pipe `/tmp/snapfifo`. The chunk is enco
* **PCM** lossless uncompressed * **PCM** lossless uncompressed
* **FLAC** lossless compressed [default] * **FLAC** lossless compressed [default]
* **Vorbis** lossy compression * **Vorbis** lossy compression
* **Opus** lossy low-latency compression
The encoded chunk is sent via a TCP connection to the Snapclients. The encoded chunk is sent via a TCP connection to the Snapclients.
Each client does continuos time synchronization with the server, so that the client is always aware of the local server time. Each client does continuos time synchronization with the server, so that the client is always aware of the local server time.

View file

@ -42,9 +42,9 @@ ifneq ($(SANITIZE), )
LDFLAGS += -fsanitize=$(SANITIZE) LDFLAGS += -fsanitize=$(SANITIZE)
endif endif
CXXFLAGS += $(ADD_CFLAGS) -std=c++14 -Wall -Wextra -Wpedantic -Wno-unused-function -DBOOST_ERROR_CODE_HEADER_ONLY -DHAS_FLAC -DHAS_OGG -DVERSION=\"$(VERSION)\" -I. -I.. -I../common CXXFLAGS += $(ADD_CFLAGS) -std=c++14 -Wall -Wextra -Wpedantic -Wno-unused-function -DBOOST_ERROR_CODE_HEADER_ONLY -DHAS_FLAC -DHAS_OGG -DHAS_OPUS -DVERSION=\"$(VERSION)\" -I. -I.. -I../common
LDFLAGS += $(ADD_LDFLAGS) -logg -lFLAC LDFLAGS += $(ADD_LDFLAGS) -logg -lFLAC -lopus
OBJ = snapclient.o stream.o client_connection.o time_provider.o player/player.o decoder/pcm_decoder.o decoder/ogg_decoder.o decoder/flac_decoder.o controller.o ../common/sample_format.o OBJ = snapclient.o stream.o client_connection.o time_provider.o player/player.o decoder/pcm_decoder.o decoder/ogg_decoder.o decoder/flac_decoder.o decoder/opus_decoder.o controller.o ../common/sample_format.o
ifneq (,$(TARGET)) ifneq (,$(TARGET))

View file

@ -27,6 +27,9 @@
#if defined(HAS_FLAC) #if defined(HAS_FLAC)
#include "decoder/flac_decoder.hpp" #include "decoder/flac_decoder.hpp"
#endif #endif
#if defined(HAS_OPUS)
#include "decoder/opus_decoder.hpp"
#endif
#include "common/aixlog.hpp" #include "common/aixlog.hpp"
#include "common/snap_exception.hpp" #include "common/snap_exception.hpp"
#include "message/hello.hpp" #include "message/hello.hpp"
@ -109,6 +112,10 @@ void Controller::onMessageReceived(ClientConnection* /*connection*/, const msg::
#if defined(HAS_FLAC) #if defined(HAS_FLAC)
else if (headerChunk_->codec == "flac") else if (headerChunk_->codec == "flac")
decoder_.reset(new FlacDecoder()); decoder_.reset(new FlacDecoder());
#endif
#if defined(HAS_OPUS)
else if (headerChunk_->codec == "opus")
decoder_.reset(new OpusDecoderWrapper());
#endif #endif
else else
throw SnapException("codec not supported: \"" + headerChunk_->codec + "\""); throw SnapException("codec not supported: \"" + headerChunk_->codec + "\"");

View file

@ -0,0 +1,80 @@
/***
This file is part of snapcast
Copyright (C) 2015 Hannes Ellinger
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 <http://www.gnu.org/licenses/>.
***/
#include "opus_decoder.hpp"
#include "common/aixlog.hpp"
OpusDecoderWrapper::OpusDecoderWrapper() : Decoder()
{
int error;
dec = opus_decoder_create(48000, 2, &error); // fixme
if (error != 0)
{
LOG(ERROR) << "Failed to initialize opus decoder: " << error << '\n' << " Rate: " << 48000 << '\n' << " Channels: " << 2 << '\n';
}
}
OpusDecoderWrapper::~OpusDecoderWrapper()
{
}
bool OpusDecoderWrapper::decode(msg::PcmChunk* /*chunk*/)
{
// int samples = 480;
// // reserve space for decoded audio
// opus_int16 pcm[samples * 2];
// msg::PcmChunk* decodedChunk = new msg::PcmChunk();
// decodedChunk->payloadSize = samples * 4;
// decodedChunk->payload = (char*)realloc(decodedChunk->payload, decodedChunk->payloadSize);
// // decode
// int frame_size = opus_decode(dec, (unsigned char*)chunk->payload, chunk->payloadSize, pcm, samples, 0);
// if (frame_size < 0)
// {
// LOG(ERROR) << "Failed to decode chunk: " << frame_size << '\n'
// << " IN size: " << chunk->payloadSize << '\n'
// << " OUT size: " << decodedChunk->payloadSize << '\n';
// }
// else
// {
// // logD << "Decoded chunk: size " << chunk->payloadSize << " bytes, decoded " << frame_size << " bytes" << '\n';
// // copy encoded data to chunk
// chunk->payloadSize = samples * 4;
// chunk->payload = (char*)realloc(chunk->payload, chunk->payloadSize);
// // uninterleave audio
// for (int i = 0; i < samples * 2; i++)
// {
// chunk->payload[2 * i + 1] = (char)(pcm[i] >> 8);
// chunk->payload[2 * i] = (char)(pcm[i] & 0x00ff);
// }
// }
return true;
}
SampleFormat OpusDecoderWrapper::setHeader(msg::CodecHeader* /*chunk*/)
{
return {48000, 16, 2};
}

View file

@ -0,0 +1,35 @@
/***
This file is part of snapcast
Copyright (C) 2015 Hannes Ellinger
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 <http://www.gnu.org/licenses/>.
***/
#pragma once
#include "decoder/decoder.hpp"
#include <opus/opus.h>
class OpusDecoderWrapper : public Decoder
{
public:
OpusDecoderWrapper();
~OpusDecoderWrapper();
bool decode(msg::PcmChunk* chunk) override;
SampleFormat setHeader(msg::CodecHeader* chunk) override;
private:
OpusDecoder* dec;
};

View file

@ -42,9 +42,9 @@ ifneq ($(SANITIZE), )
LDFLAGS += -fsanitize=$(SANITIZE) LDFLAGS += -fsanitize=$(SANITIZE)
endif endif
CXXFLAGS += $(ADD_CFLAGS) -std=c++14 -Wall -Wextra -Wpedantic -Wno-unused-function -DBOOST_ERROR_CODE_HEADER_ONLY -DHAS_FLAC -DHAS_OGG -DHAS_VORBIS -DHAS_VORBIS_ENC -DVERSION=\"$(VERSION)\" -I. -I.. -I../common CXXFLAGS += $(ADD_CFLAGS) -std=c++14 -Wall -Wextra -Wpedantic -Wno-unused-function -DBOOST_ERROR_CODE_HEADER_ONLY -DHAS_FLAC -DHAS_OGG -DHAS_VORBIS -DHAS_VORBIS_ENC -DHAS_OPUS -DVERSION=\"$(VERSION)\" -I. -I.. -I../common
LDFLAGS += $(ADD_LDFLAGS) -lvorbis -lvorbisenc -logg -lFLAC LDFLAGS += $(ADD_LDFLAGS) -lvorbis -lvorbisenc -logg -lFLAC -lopus
OBJ = snapserver.o config.o control_server.o control_session_tcp.o control_session_http.o stream_server.o stream_session.o streamreader/stream_uri.o streamreader/base64.o streamreader/stream_manager.o streamreader/pcm_stream.o streamreader/pipe_stream.o streamreader/file_stream.o streamreader/process_stream.o streamreader/airplay_stream.o streamreader/librespot_stream.o streamreader/watchdog.o encoder/encoder_factory.o encoder/flac_encoder.o encoder/pcm_encoder.o encoder/ogg_encoder.o ../common/sample_format.o OBJ = snapserver.o config.o control_server.o control_session_tcp.o control_session_http.o stream_server.o stream_session.o streamreader/stream_uri.o streamreader/base64.o streamreader/stream_manager.o streamreader/pcm_stream.o streamreader/pipe_stream.o streamreader/file_stream.o streamreader/process_stream.o streamreader/airplay_stream.o streamreader/librespot_stream.o streamreader/watchdog.o encoder/encoder_factory.o encoder/flac_encoder.o encoder/opus_encoder.o encoder/pcm_encoder.o encoder/ogg_encoder.o ../common/sample_format.o
ifneq (,$(TARGET)) ifneq (,$(TARGET))
CXXFLAGS += -D$(TARGET) CXXFLAGS += -D$(TARGET)

View file

@ -24,6 +24,9 @@
#if defined(HAS_FLAC) #if defined(HAS_FLAC)
#include "flac_encoder.hpp" #include "flac_encoder.hpp"
#endif #endif
#if defined(HAS_OPUS)
#include "opus_encoder.hpp"
#endif
#include "common/aixlog.hpp" #include "common/aixlog.hpp"
#include "common/snap_exception.hpp" #include "common/snap_exception.hpp"
#include "common/utils/string_utils.hpp" #include "common/utils/string_utils.hpp"

View file

@ -0,0 +1,69 @@
/***
This file is part of snapcast
Copyright (C) 2015 Hannes Ellinger
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 <http://www.gnu.org/licenses/>.
***/
#include "opus_encoder.hpp"
#include "common/aixlog.hpp"
// const msg::SampleFormat& format);
OpusEncoderWrapper::OpusEncoderWrapper(const std::string& codecOptions) : Encoder(codecOptions)
{
headerChunk_.reset(new msg::CodecHeader("opus"));
// int error;
// enc = opus_encoder_create(format.rate, format.channels, OPUS_APPLICATION_RESTRICTED_LOWDELAY, &error);
// if (error != 0)
// {
// LOG(ERROR) << "Failed to initialize opus encoder: " << error << '\n'
// << " Rate: " << format.rate << '\n'
// << " Channels: " << format.channels << '\n';
// }
}
void OpusEncoderWrapper::encode(const msg::PcmChunk* /*chunk*/)
{
// int samples = chunk->payloadSize / 4;
// opus_int16 pcm[samples * 2];
// unsigned char encoded[chunk->payloadSize];
// // get 16 bit samples
// for (int i = 0; i < samples * 2; i++)
// {
// pcm[i] = (opus_int16)(((opus_int16)chunk->payload[2 * i + 1] << 8) | (0x00ff & (opus_int16)chunk->payload[2 * i]));
// }
// // encode
// int len = opus_encode(enc, pcm, samples, encoded, chunk->payloadSize);
// // logD << "Encoded: size " << chunk->payloadSize << " bytes, encoded: " << len << " bytes" << '\n';
// if (len > 0)
// {
// // copy encoded data to chunk
// chunk->payloadSize = len;
// chunk->payload = (char*)realloc(chunk->payload, chunk->payloadSize);
// memcpy(chunk->payload, encoded, len);
// }
// else
// {
// LOG(ERROR) << "Failed to encode chunk: " << len << '\n' << " Frame size: " << samples / 2 << '\n' << " Max bytes: " << chunk->payloadSize << '\n';
// }
// // return chunk duration
// return (double)samples / ((double)sampleFormat.rate / 1000.);
}

View file

@ -0,0 +1,33 @@
/***
This file is part of snapcast
Copyright (C) 2015 Hannes Ellinger
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 <http://www.gnu.org/licenses/>.
***/
#pragma once
#include "encoder.hpp"
#include <opus/opus.h>
class OpusEncoderWrapper : public Encoder
{
public:
OpusEncoderWrapper(const std::string& codecOptions = "");
void encode(const msg::PcmChunk* chunk) override;
private:
OpusEncoder* enc;
};

View file

@ -84,7 +84,7 @@ int main(int argc, char* argv[])
conf.add<Value<size_t>>("", "stream.threads", "number of streaming threads", num_threads, &num_threads); conf.add<Value<size_t>>("", "stream.threads", "number of streaming threads", num_threads, &num_threads);
conf.add<Value<string>>("", "stream.sampleformat", "Default sample format", settings.stream.sampleFormat, &settings.stream.sampleFormat); conf.add<Value<string>>("", "stream.sampleformat", "Default sample format", settings.stream.sampleFormat, &settings.stream.sampleFormat);
conf.add<Value<string>>("c", "stream.codec", "Default transport codec\n(flac|ogg|pcm)[:options]\nType codec:? to get codec specific options", conf.add<Value<string>>("c", "stream.codec", "Default transport codec\n(flac|ogg|opus|pcm)[:options]\nType codec:? to get codec specific options",
settings.stream.codec, &settings.stream.codec); settings.stream.codec, &settings.stream.codec);
conf.add<Value<size_t>>("", "stream.stream_buffer", "Default stream read buffer [ms]", settings.stream.streamReadMs, &settings.stream.streamReadMs); conf.add<Value<size_t>>("", "stream.stream_buffer", "Default stream read buffer [ms]", settings.stream.streamReadMs, &settings.stream.streamReadMs);
conf.add<Value<int>>("b", "stream.buffer", "Buffer [ms]", settings.stream.bufferMs, &settings.stream.bufferMs); conf.add<Value<int>>("b", "stream.buffer", "Buffer [ms]", settings.stream.bufferMs, &settings.stream.bufferMs);