#include <ftl/audio/software_encoder.hpp> #include <ftl/config.h> #ifdef HAVE_OPUS #include <opus/opus_multistream.h> #else struct OpusMSEncoder {}; #endif #define LOGURU_REPLACE_GLOG 1 #include <loguru.hpp> using ftl::audio::SoftwareEncoder; using ftl::codecs::codec_t; #define FRAME_SIZE 960 #define MAX_PACKET_SIZE (3*2*FRAME_SIZE) SoftwareEncoder::SoftwareEncoder() : ftl::audio::Encoder(), opus_encoder_(nullptr), cur_bitrate_(0) { } SoftwareEncoder::~SoftwareEncoder() { } bool SoftwareEncoder::encode(const std::vector<short> &in, ftl::codecs::Packet &pkt) { auto codec = (pkt.codec == codec_t::Any) ? codec_t::OPUS : pkt.codec; // Force RAW if no opus #ifndef HAVE_OPUS codec = codec_t::RAW; #endif pkt.codec = codec; switch (codec) { case codec_t::OPUS : return _encodeOpus(in, pkt); case codec_t::RAW : return _encodeRaw(in, pkt); default: return false; } } bool SoftwareEncoder::_createOpus(ftl::codecs::Packet &pkt) { #ifdef HAVE_OPUS bool stereo = pkt.flags & ftl::codecs::kFlagStereo; if (pkt.definition == cur_definition_ && stereo == cur_stereo_ && opus_encoder_) return true; cur_definition_ = pkt.definition; cur_stereo_ = stereo; if (opus_encoder_) { opus_multistream_encoder_destroy(opus_encoder_); opus_encoder_ = nullptr; } int sample_rate; switch (pkt.definition) { case ftl::codecs::definition_t::hz48000 : sample_rate = 48000; break; case ftl::codecs::definition_t::hz44100 : sample_rate = 44100; break; default: return false; } int errcode = 0; int channels = (stereo) ? 2 : 1; const unsigned char mapping[2] = {0,1}; opus_encoder_ = opus_multistream_encoder_create(sample_rate, channels, 1, channels-1, mapping, OPUS_APPLICATION_VOIP, &errcode); if (errcode < 0) return false; LOG(INFO) << "Created OPUS encoder"; #endif return true; } bool SoftwareEncoder::_encodeOpus(const std::vector<short> &in, ftl::codecs::Packet &pkt) { #ifdef HAVE_OPUS static const float MAX_BITRATE = 128000.0f; static const float MIN_BITRATE = 24000.0f; if (!_createOpus(pkt)) return false; if (pkt.bitrate != cur_bitrate_) { int bitrate = (MAX_BITRATE-MIN_BITRATE) * (float(pkt.bitrate)/255.0f) + MIN_BITRATE; if (!cur_stereo_) bitrate /= 2; int errcode = opus_multistream_encoder_ctl(opus_encoder_, OPUS_SET_BITRATE(bitrate)); if (errcode < 0) return false; LOG(INFO) << "OPUS encoder: bitrate = " << bitrate; cur_bitrate_ = pkt.bitrate; } int channels = (cur_stereo_) ? 2 : 1; pkt.data.resize(MAX_PACKET_SIZE); int count = 0; int frames = 0; unsigned char *outptr = pkt.data.data(); for (unsigned int i=0; i<in.size(); i+=channels*FRAME_SIZE) { short *len = (short*)outptr; outptr += 2; int nbBytes = opus_multistream_encode(opus_encoder_, &in.data()[i], FRAME_SIZE, outptr, MAX_PACKET_SIZE); if (nbBytes <= 0) return false; //if (nbBytes > 32000) LOG(WARNING) << "Packet exceeds size limit"; *len = nbBytes; count += nbBytes+2; outptr += nbBytes; ++frames; } pkt.data.resize(count); //LOG(INFO) << "Opus Encode = " << pkt.data.size() << ", " << frames; return true; #else return false; #endif } bool SoftwareEncoder::_encodeRaw(const std::vector<short> &in, ftl::codecs::Packet &pkt) { const unsigned char *ptr = (unsigned char*)in.data(); pkt.data = std::move(std::vector<unsigned char>(ptr, ptr+in.size()*sizeof(short))); return true; } void SoftwareEncoder::reset() { } bool SoftwareEncoder::supports(ftl::codecs::codec_t codec) { return false; }