mirror of
https://github.com/mumble-voip/mumble.git
synced 2025-10-26 11:19:16 +00:00
Merge PR #3431: Create OpusCodec class, similar to CeltCodec, in order to load Opus' functions from a shared library
This commit is contained in:
commit
e0ee016e5c
13
3rdparty/opus-build/opus-build.pro
vendored
13
3rdparty/opus-build/opus-build.pro
vendored
@ -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 @@ public:
|
||||
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"
|
||||
|
||||
92
src/mumble/OpusCodec.cpp
Normal file
92
src/mumble/OpusCodec.cpp
Normal file
@ -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()));
|
||||
}
|
||||
46
src/mumble/OpusCodec.h
Normal file
46
src/mumble/OpusCodec.h
Normal file
@ -0,0 +1,46 @@
|
||||
// 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>.
|
||||
|
||||
#ifndef MUMBLE_MUMBLE_OPUSCODEC_H_
|
||||
#define MUMBLE_MUMBLE_OPUSCODEC_H_
|
||||
|
||||
#include <opus.h>
|
||||
|
||||
#include <QtCore/QLibrary>
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
#define __cdecl
|
||||
#endif
|
||||
|
||||
/// Loads Opus from a shared library and acts as a wrapper for its functions.
|
||||
class OpusCodec {
|
||||
private:
|
||||
Q_DISABLE_COPY(OpusCodec)
|
||||
protected:
|
||||
QLibrary qlOpus;
|
||||
bool bValid;
|
||||
public:
|
||||
OpusCodec();
|
||||
virtual ~OpusCodec();
|
||||
|
||||
bool isValid() const;
|
||||
void report() const;
|
||||
|
||||
const char *(__cdecl *opus_get_version_string)();
|
||||
|
||||
OpusEncoder *(__cdecl *opus_encoder_create)(opus_int32 Fs, int channels, int application, int *error);
|
||||
int (__cdecl *opus_encoder_ctl)(OpusEncoder *st, int request, ...);
|
||||
void (__cdecl *opus_encoder_destroy)(OpusEncoder *st);
|
||||
OpusDecoder *(__cdecl *opus_decoder_create)(opus_int32 Fs, int channels, int *error);
|
||||
void (__cdecl *opus_decoder_destroy)(OpusDecoder *st);
|
||||
|
||||
int (__cdecl *opus_encode)(OpusEncoder *st, const opus_int16 *pcm, int frame_size, unsigned char *compressed, int nbCompressedBytes);
|
||||
int (__cdecl *opus_decode_float)(OpusDecoder *st, const unsigned char *data, opus_int32 len, float *pcm, int frame_size, int decode_fec);
|
||||
|
||||
int (__cdecl *opus_packet_get_nb_frames)(const unsigned char packet[], opus_int32 len);
|
||||
int (__cdecl *opus_packet_get_samples_per_frame)(const unsigned char *data, opus_int32 Fs);
|
||||
};
|
||||
|
||||
#endif // OPUSCODEC_H_
|
||||
@ -86,6 +86,7 @@ HEADERS *= BanEditor.h \
|
||||
AudioOutputSpeech.h \
|
||||
AudioOutputUser.h \
|
||||
CELTCodec.h \
|
||||
OpusCodec.h \
|
||||
CustomElements.h \
|
||||
MainWindow.h \
|
||||
ServerHandler.h \
|
||||
@ -156,6 +157,7 @@ SOURCES *= BanEditor.cpp \
|
||||
AudioOutputUser.cpp \
|
||||
main.cpp \
|
||||
CELTCodec.cpp \
|
||||
OpusCodec.cpp \
|
||||
CustomElements.cpp \
|
||||
MainWindow.cpp \
|
||||
ServerHandler.cpp \
|
||||
@ -358,7 +360,6 @@ unix:!CONFIG(bundled-opus):system(pkg-config --exists opus) {
|
||||
CONFIG(opus) {
|
||||
INCLUDEPATH *= ../../3rdparty/opus-src/celt ../../3rdparty/opus-src/include ../../3rdparty/opus-src/src ../../3rdparty/opus-build/src
|
||||
DEFINES *= USE_OPUS
|
||||
LIBS *= -lopus
|
||||
unix {
|
||||
QMAKE_CFLAGS *= "-I../../3rdparty/opus-src/celt" "-isystem ../../3rdparty/opus-src/celt"
|
||||
QMAKE_CFLAGS *= "-I../../3rdparty/opus-src/include" "-isystem ../../3rdparty/opus-src/include"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user