Skip to content
Permalink
Browse files

Merge PR #3431: Create OpusCodec class, similar to CeltCodec, in orde…

…r to load Opus' functions from a shared library
  • Loading branch information...
davidebeatrici committed Jul 6, 2018
2 parents 426cd1e + 8ca51d0 commit e0ee016e5c333dabd2603b01decbe8ef2ae030e2
@@ -45,19 +45,6 @@ win32 {
}

unix {
CONFIG += staticlib
CONFIG(sbcelt) {
# Before Opus 1.1 we used to be able to build Opus
# as C++ code to get C++ name mangling for free,
# allowing us to statically build both libcelt
# and libopus into the same binary while avoiding
# symbol clashes between the two libraries.
#
# Stock Opus 1.1 doesn't build in C++ mode, so error
# out for now.
error(Mumble cannot be built in SBCELT mode with Opus 1.1 - aborting build.)
}

contains(QMAKE_CFLAGS, -ffast-math) {
DEFINES += FLOAT_APPROX
}
@@ -10,6 +10,7 @@
#include "AudioInput.h"
#include "AudioOutput.h"
#include "CELTCodec.h"
#include "OpusCodec.h"
#include "Global.h"
#include "PacketDataStream.h"

@@ -25,13 +26,24 @@ LoopUser LoopUser::lpLoopy;
CodecInit ciInit;

void CodecInit::initialize() {
CELTCodec *codec = NULL;
#ifdef USE_OPUS
OpusCodec *oCodec = new OpusCodec();
if (oCodec->isValid()) {
oCodec->report();
g.oCodec = oCodec;
} else {
qWarning("CodecInit: Failed to load Opus, it will not be available for encoding/decoding audio.");
delete oCodec;
}
#endif

if (g.s.bDisableCELT) {
// Kill switch for CELT activated. Do not initialize it.
return;
}

CELTCodec *codec = NULL;

#ifdef USE_SBCELT
codec = new CELTCodecSBCELT();
if (codec->isValid()) {
@@ -74,6 +86,10 @@ void CodecInit::initialize() {
}

void CodecInit::destroy() {
#ifdef USE_OPUS
delete g.oCodec;
#endif

foreach(CELTCodec *codec, g.qmCodecs)
delete codec;
g.qmCodecs.clear();
@@ -9,6 +9,7 @@

#include "AudioOutput.h"
#include "CELTCodec.h"
#include "OpusCodec.h"
#include "ServerHandler.h"
#include "MainWindow.h"
#include "User.h"
@@ -19,10 +20,6 @@
#include "NetworkConfig.h"
#include "VoiceRecorder.h"

#ifdef USE_OPUS
#include "opus.h"
#endif

// Remember that we cannot use static member classes that are not pointers, as the constructor
// for AudioInputRegistrar() might be called before they are initialized, as the constructor
// is called from global initialization.
@@ -86,15 +83,18 @@ AudioInput::AudioInput() : opusBuffer(g.s.iFramesPerPacket * (SAMPLE_RATE / 100)
iFrameSize = SAMPLE_RATE / 100;

#ifdef USE_OPUS
if (!g.s.bUseOpusMusicEncoding) {
opusState = opus_encoder_create(SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP, NULL);
qWarning("AudioInput: Opus encoder set for VOIP");
} else {
opusState = opus_encoder_create(SAMPLE_RATE, 1, OPUS_APPLICATION_AUDIO, NULL);
qWarning("AudioInput: Opus encoder set for Music");
}
oCodec = g.oCodec;
if (oCodec) {
if (!g.s.bUseOpusMusicEncoding) {
opusState = oCodec->opus_encoder_create(SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP, NULL);
qWarning("AudioInput: Opus encoder set for VOIP");
} else {
opusState = oCodec->opus_encoder_create(SAMPLE_RATE, 1, OPUS_APPLICATION_AUDIO, NULL);
qWarning("AudioInput: Opus encoder set for Music");
}

opus_encoder_ctl(opusState, OPUS_SET_VBR(0)); // CBR
oCodec->opus_encoder_ctl(opusState, OPUS_SET_VBR(0)); // CBR
}
#endif

qWarning("AudioInput: %d bits/s, %d hz, %d sample", iAudioQuality, iSampleRate, iFrameSize);
@@ -149,8 +149,9 @@ AudioInput::~AudioInput() {
wait();

#ifdef USE_OPUS
if (opusState)
opus_encoder_destroy(opusState);
if (opusState) {
oCodec->opus_encoder_destroy(opusState);
}
#endif

if (ceEncoder) {
@@ -729,13 +730,13 @@ int AudioInput::encodeOpusFrame(short *source, int size, EncodingOutputBuffer& b
int len = 0;
#ifdef USE_OPUS
if (bResetEncoder) {
opus_encoder_ctl(opusState, OPUS_RESET_STATE, NULL);
oCodec->opus_encoder_ctl(opusState, OPUS_RESET_STATE, NULL);
bResetEncoder = false;
}

opus_encoder_ctl(opusState, OPUS_SET_BITRATE(iAudioQuality));
oCodec->opus_encoder_ctl(opusState, OPUS_SET_BITRATE(iAudioQuality));

len = opus_encode(opusState, source, size, &buffer[0], static_cast<opus_int32>(buffer.size()));
len = oCodec->opus_encode(opusState, source, size, &buffer[0], static_cast<opus_int32>(buffer.size()));
const int tenMsFrameCount = (size / iFrameSize);
iBitrate = (len * 100 * 8) / tenMsFrameCount;
#endif
@@ -23,6 +23,7 @@

class AudioInput;
class CELTCodec;
class OpusCodec;
struct CELTEncoder;
struct OpusEncoder;
typedef boost::shared_ptr<AudioInput> AudioInputPtr;
@@ -72,6 +73,7 @@ class AudioInput : public QThread {
inMixerFunc chooseMixer(const unsigned int nchan, SampleFormat sf, quint64 mask);
void resetAudioProcessor();

OpusCodec *oCodec;
OpusEncoder *opusState;
bool selectCodec();

@@ -9,14 +9,11 @@

#include "Audio.h"
#include "CELTCodec.h"
#include "OpusCodec.h"
#include "ClientUser.h"
#include "Global.h"
#include "PacketDataStream.h"

#ifdef USE_OPUS
#include "opus.h"
#endif

AudioOutputSpeech::AudioOutputSpeech(ClientUser *user, unsigned int freq, MessageHandler::UDPMessageType type) : AudioOutputUser(user->qsName) {
int err;
p = user;
@@ -26,6 +23,7 @@ AudioOutputSpeech::AudioOutputSpeech(ClientUser *user, unsigned int freq, Messag
cCodec = NULL;
cdDecoder = NULL;
dsSpeex = NULL;
oCodec = NULL;
opusState = NULL;

bHasTerminator = false;
@@ -37,8 +35,11 @@ AudioOutputSpeech::AudioOutputSpeech(ClientUser *user, unsigned int freq, Messag

if (umtType == MessageHandler::UDPVoiceOpus) {
#ifdef USE_OPUS
iAudioBufferSize *= 12;
opusState = opus_decoder_create(iSampleRate, bStereo ? 2 : 1, NULL);
oCodec = g.oCodec;
if (oCodec) {
iAudioBufferSize *= 12;
opusState = oCodec->opus_decoder_create(iSampleRate, bStereo ? 2 : 1, NULL);
}
#endif
} else if (umtType == MessageHandler::UDPVoiceSpeex) {
speex_bits_init(&sbBits);
@@ -87,7 +88,7 @@ AudioOutputSpeech::AudioOutputSpeech(ClientUser *user, unsigned int freq, Messag
AudioOutputSpeech::~AudioOutputSpeech() {
#ifdef USE_OPUS
if (opusState)
opus_decoder_destroy(opusState);
oCodec->opus_decoder_destroy(opusState);
#endif
if (cdDecoder) {
cCodec->celt_decoder_destroy(cdDecoder);
@@ -134,8 +135,10 @@ void AudioOutputSpeech::addFrameToBuffer(const QByteArray &qbaPacket, unsigned i
const unsigned char *packet = reinterpret_cast<const unsigned char*>(qba.constData());

#ifdef USE_OPUS
int frames = opus_packet_get_nb_frames(packet, size);
samples = frames * opus_packet_get_samples_per_frame(packet, SAMPLE_RATE);
if (oCodec) {
int frames = oCodec->opus_packet_get_nb_frames(packet, size);
samples = frames * oCodec->opus_packet_get_samples_per_frame(packet, SAMPLE_RATE);
}
#else
return;
#endif
@@ -288,14 +291,15 @@ bool AudioOutputSpeech::needSamples(unsigned int snum) {
memset(pOut, 0, sizeof(float) * iFrameSize);
} else if (umtType == MessageHandler::UDPVoiceOpus) {
#ifdef USE_OPUS
decodedSamples = opus_decode_float(opusState,
qba.isEmpty() ?
NULL :
reinterpret_cast<const unsigned char *>(qba.constData()),
qba.size(),
pOut,
iAudioBufferSize,
0);
decodedSamples = oCodec->opus_decode_float(opusState,
qba.isEmpty() ?
NULL :
reinterpret_cast<const unsigned char *>(qba.constData()),
qba.size(),
pOut,
iAudioBufferSize,
0);

if (decodedSamples < 0) {
decodedSamples = iFrameSize;
memset(pOut, 0, iFrameSize * sizeof(float));
@@ -350,7 +354,8 @@ bool AudioOutputSpeech::needSamples(unsigned int snum) {
memset(pOut, 0, sizeof(float) * iFrameSize);
} else if (umtType == MessageHandler::UDPVoiceOpus) {
#ifdef USE_OPUS
decodedSamples = opus_decode_float(opusState, NULL, 0, pOut, iFrameSize, 0);
decodedSamples = oCodec->opus_decode_float(opusState, NULL, 0, pOut, iFrameSize, 0);

if (decodedSamples < 0) {
decodedSamples = iFrameSize;
memset(pOut, 0, iFrameSize * sizeof(float));
@@ -18,6 +18,7 @@
#include "Message.h"

class CELTCodec;
class OpusCodec;
class ClientUser;
struct OpusDecoder;

@@ -51,6 +52,7 @@ class AudioOutputSpeech : public AudioOutputUser {
CELTCodec *cCodec;
CELTDecoder *cdDecoder;

OpusCodec *oCodec;
OpusDecoder *opusState;

SpeexBits sbBits;
@@ -29,6 +29,7 @@ class LCD;
class BonjourClient;
class OverlayClient;
class CELTCodec;
class OpusCodec;
class LogEmitter;
class DeveloperConsole;

@@ -74,6 +75,7 @@ struct Global Q_DECL_FINAL {
int iAudioBandwidth;
QDir qdBasePath;
QMap<int, CELTCodec *> qmCodecs;
OpusCodec *oCodec;
int iCodecAlpha, iCodecBeta;
bool bPreferAlpha;
bool bOpus;
@@ -15,6 +15,7 @@
#include "AudioWizard.h"
#include "BanEditor.h"
#include "CELTCodec.h"
#include "OpusCodec.h"
#include "Cert.h"
#include "Channel.h"
#include "Connection.h"
@@ -0,0 +1,92 @@
// Copyright 2005-2018 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.

#include "mumble_pch.hpp"

#include "OpusCodec.h"

#include "Audio.h"
#include "Version.h"
#include "MumbleApplication.h"

#ifdef Q_CC_GNU
#define RESOLVE(var) { var = reinterpret_cast<__typeof__(var)>(qlOpus.resolve(#var)); bValid = bValid && (var != NULL); }
#else
#define RESOLVE(var) { * reinterpret_cast<void **>(&var) = static_cast<void *>(qlOpus.resolve(#var)); bValid = bValid && (var != NULL); }
#endif

OpusCodec::OpusCodec() {
bValid = false;
qlOpus.setLoadHints(QLibrary::ResolveAllSymbolsHint);

QStringList alternatives;
#if defined(Q_OS_MAC)
alternatives << QString::fromLatin1("libopus0.dylib");
alternatives << QString::fromLatin1("opus0.dylib");
alternatives << QString::fromLatin1("libopus.dylib");
alternatives << QString::fromLatin1("opus.dylib");
#elif defined(Q_OS_UNIX)
alternatives << QString::fromLatin1("libopus0.so");
alternatives << QString::fromLatin1("libopus.so");
alternatives << QString::fromLatin1("opus.so");
#else
alternatives << QString::fromLatin1("opus0.dll");
#endif
foreach(const QString &lib, alternatives) {
qlOpus.setFileName(MumbleApplication::instance()->applicationVersionRootPath() + QLatin1String("/") + lib);
if (qlOpus.load()) {
bValid = true;
break;
}

#ifdef Q_OS_MAC
qlOpus.setFileName(QApplication::instance()->applicationDirPath() + QLatin1String("/../Codecs/") + lib);
if (qlOpus.load()) {
bValid = true;
break;
}
#endif

#ifdef PLUGIN_PATH
qlOpus.setFileName(QLatin1String(MUMTEXT(PLUGIN_PATH) "/") + lib);
if (qlOpus.load()) {
bValid = true;
break;
}
#endif

qlOpus.setFileName(lib);
if (qlOpus.load()) {
bValid = true;
break;
}
}

RESOLVE(opus_get_version_string);

RESOLVE(opus_encode);
RESOLVE(opus_decode_float);

RESOLVE(opus_encoder_create);
RESOLVE(opus_encoder_ctl);
RESOLVE(opus_encoder_destroy);
RESOLVE(opus_decoder_create);
RESOLVE(opus_decoder_destroy);

RESOLVE(opus_packet_get_nb_frames);
RESOLVE(opus_packet_get_samples_per_frame);
}

OpusCodec::~OpusCodec() {
qlOpus.unload();
}

bool OpusCodec::isValid() const {
return bValid;
}

void OpusCodec::report() const {
qDebug("%s from %s", opus_get_version_string(), qPrintable(qlOpus.fileName()));
}

0 comments on commit e0ee016

Please sign in to comment.
You can’t perform that action at this time.