diff --git a/client/controller.cpp b/client/controller.cpp index 3976e750..074b710e 100644 --- a/client/controller.cpp +++ b/client/controller.cpp @@ -62,7 +62,7 @@ void Controller::onMessageReceived(ClientConnection* /*connection*/, const msg:: { auto* pcmChunk = new msg::PcmChunk(sampleFormat_, 0); pcmChunk->deserialize(baseMessage, buffer); - // LOG(DEBUG) << "chunk: " << pcmChunk->payloadSize << ", sampleFormat: " << sampleFormat_.rate << "\n"; + // LOG(DEBUG) << "chunk: " << pcmChunk->payloadSize << ", sampleFormat: " << sampleFormat_.rate << "\n"; if (decoder_->decode(pcmChunk)) { // TODO: do decoding in thread? @@ -115,7 +115,7 @@ void Controller::onMessageReceived(ClientConnection* /*connection*/, const msg:: #endif #if defined(HAS_OPUS) else if (headerChunk_->codec == "opus") - decoder_.reset(new OpusDecoderWrapper()); + decoder_.reset(new OpusDecoder()); #endif else throw SnapException("codec not supported: \"" + headerChunk_->codec + "\""); diff --git a/client/decoder/opus_decoder.cpp b/client/decoder/opus_decoder.cpp index 93ca634e..c35d43ba 100644 --- a/client/decoder/opus_decoder.cpp +++ b/client/decoder/opus_decoder.cpp @@ -20,10 +20,10 @@ #include "common/aixlog.hpp" -OpusDecoderWrapper::OpusDecoderWrapper() : Decoder() +OpusDecoder::OpusDecoder() : Decoder() { int error; - dec = opus_decoder_create(48000, 2, &error); // fixme + 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'; @@ -31,50 +31,41 @@ OpusDecoderWrapper::OpusDecoderWrapper() : Decoder() } -OpusDecoderWrapper::~OpusDecoderWrapper() +OpusDecoder::~OpusDecoder() { + if (dec_ != nullptr) + opus_decoder_destroy(dec_); } -bool OpusDecoderWrapper::decode(msg::PcmChunk* /*chunk*/) +bool OpusDecoder::decode(msg::PcmChunk* chunk) { - // int samples = 480; - // // reserve space for decoded audio - // opus_int16 pcm[samples * 2]; + size_t samples = 480; + // reserve space for decoded audio + if (pcm_.size() < samples * 2) + pcm_.resize(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_.data(), samples, 0); + if (frame_size < 0) + { + LOG(ERROR) << "Failed to decode chunk: " << frame_size << '\n' << " IN size: " << chunk->payloadSize << '\n' << " OUT size: " << samples * 4 << '\n'; + return false; + } + else + { + LOG(DEBUG) << "Decoded chunk: size " << chunk->payloadSize << " bytes, decoded " << frame_size << " bytes" << '\n'; - // // 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; + // copy encoded data to chunk + chunk->payloadSize = samples * 4; + chunk->payload = (char*)realloc(chunk->payload, chunk->payloadSize); + memcpy(chunk->payload, (char*)pcm_.data(), samples * 4); + return true; + } } -SampleFormat OpusDecoderWrapper::setHeader(msg::CodecHeader* /*chunk*/) +SampleFormat OpusDecoder::setHeader(msg::CodecHeader* /*chunk*/) { return {48000, 16, 2}; } diff --git a/client/decoder/opus_decoder.hpp b/client/decoder/opus_decoder.hpp index f203d2ce..1334858e 100644 --- a/client/decoder/opus_decoder.hpp +++ b/client/decoder/opus_decoder.hpp @@ -22,14 +22,15 @@ #include -class OpusDecoderWrapper : public Decoder +class OpusDecoder : public Decoder { public: - OpusDecoderWrapper(); - ~OpusDecoderWrapper(); + OpusDecoder(); + ~OpusDecoder(); bool decode(msg::PcmChunk* chunk) override; SampleFormat setHeader(msg::CodecHeader* chunk) override; private: - OpusDecoder* dec; + OpusDecoder* dec_; + std::vector pcm_; }; diff --git a/server/encoder/encoder_factory.cpp b/server/encoder/encoder_factory.cpp index e76625eb..6fd924f7 100644 --- a/server/encoder/encoder_factory.cpp +++ b/server/encoder/encoder_factory.cpp @@ -54,6 +54,10 @@ Encoder* EncoderFactory::createEncoder(const std::string& codecSettings) const #if defined(HAS_FLAC) else if (codec == "flac") encoder = new FlacEncoder(codecOptions); +#endif +#if defined(HAS_OPUS) + else if (codec == "opus") + encoder = new OpusEncoder(codecOptions); #endif else { diff --git a/server/encoder/opus_encoder.cpp b/server/encoder/opus_encoder.cpp index cf641371..a5a6d09f 100644 --- a/server/encoder/opus_encoder.cpp +++ b/server/encoder/opus_encoder.cpp @@ -18,52 +18,62 @@ #include "opus_encoder.hpp" #include "common/aixlog.hpp" +#include "common/snap_exception.hpp" +#include "common/str_compat.hpp" // const msg::SampleFormat& format); -OpusEncoderWrapper::OpusEncoderWrapper(const std::string& codecOptions) : Encoder(codecOptions) +OpusEncoder::OpusEncoder(const std::string& codecOptions) : Encoder(codecOptions), enc_(nullptr) { +} + + +OpusEncoder::~OpusEncoder() +{ + if (enc_ != nullptr) + opus_encoder_destroy(enc_); +} + + +std::string OpusEncoder::name() const +{ + return "opus"; +} + + +void OpusEncoder::initEncoder() +{ + int error; + enc_ = opus_encoder_create(sampleFormat_.rate, sampleFormat_.channels, OPUS_APPLICATION_RESTRICTED_LOWDELAY, &error); + if (error != 0) + { + throw SnapException("Failed to initialize opus encoder: " + cpt::to_string(error)); + } 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*/) +void OpusEncoder::encode(const msg::PcmChunk* chunk) { - // int samples = chunk->payloadSize / 4; - // opus_int16 pcm[samples * 2]; + int samples = chunk->payloadSize / 4; + if (encoded_.size() < chunk->payloadSize) + encoded_.resize(chunk->payloadSize); - // unsigned char encoded[chunk->payloadSize]; + // encode + opus_int32 len = opus_encode(enc_, (opus_int16*)chunk->payload, samples, encoded_.data(), chunk->payloadSize); + LOG(DEBUG) << "Encoded: size " << chunk->payloadSize << " bytes, encoded: " << len << " bytes" << '\n'; - // // 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.); + if (len > 0) + { + // copy encoded data to chunk + auto* opusChunk = new msg::PcmChunk(chunk->format, 0); + opusChunk->payloadSize = len; + opusChunk->payload = (char*)realloc(opusChunk->payload, opusChunk->payloadSize); + memcpy(opusChunk->payload, encoded_.data(), len); + listener_->onChunkEncoded(this, opusChunk, (double)samples / ((double)sampleFormat_.rate / 1000.)); + } + else + { + LOG(ERROR) << "Failed to encode chunk: " << len << '\n' << " Frame size: " << samples / 2 << '\n' << " Max bytes: " << chunk->payloadSize << '\n'; + } } diff --git a/server/encoder/opus_encoder.hpp b/server/encoder/opus_encoder.hpp index f26a316a..e4706e98 100644 --- a/server/encoder/opus_encoder.hpp +++ b/server/encoder/opus_encoder.hpp @@ -22,12 +22,17 @@ #include -class OpusEncoderWrapper : public Encoder +class OpusEncoder : public Encoder { public: - OpusEncoderWrapper(const std::string& codecOptions = ""); - void encode(const msg::PcmChunk* chunk) override; + OpusEncoder(const std::string& codecOptions = ""); + ~OpusEncoder() override; -private: - OpusEncoder* enc; + void encode(const msg::PcmChunk* chunk) override; + std::string name() const override; + +protected: + void initEncoder() override; + OpusEncoder* enc_; + std::vector encoded_; };