mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge pull request #86 from MarcusLlewellyn/opus-plugin
Add support for the Opus audio plugin.
This commit is contained in:
commit
dcb575a178
17 changed files with 744 additions and 5 deletions
13
cmake/macros/TargetOpus.cmake
Normal file
13
cmake/macros/TargetOpus.cmake
Normal file
|
@ -0,0 +1,13 @@
|
|||
#
|
||||
# Created by Michael Bailey on 12/20/2019
|
||||
# Copyright 2019 Michael Bailey
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
macro(TARGET_opus)
|
||||
find_library(OPUS_LIBRARY_RELEASE NAMES opus PATHS ${VCPKG_INSTALL_ROOT}/lib)
|
||||
find_library(OPUS_LIBRARY_DEBUG NAMES opus PATHS ${VCPKG_INSTALL_ROOT}/debug/lib)
|
||||
select_library_configurations(OPUS)
|
||||
target_link_libraries(${TARGET_NAME} ${OPUS_LIBRARY})
|
||||
endmacro()
|
|
@ -1,4 +1,4 @@
|
|||
Source: hifi-deps
|
||||
Version: 0.3
|
||||
Version: 0.4
|
||||
Description: Collected dependencies for High Fidelity applications
|
||||
Build-Depends: bullet3, draco, etc2comp, glm, nvtt, openexr (!android), openssl (windows), tbb (!android&!osx), zlib, webrtc (!android)
|
||||
Build-Depends: bullet3, draco, etc2comp, glm, nvtt, openexr (!android), openssl (windows), opus, tbb (!android&!osx), zlib, webrtc (!android)
|
||||
|
|
3
cmake/ports/opus/CONTROL
Normal file
3
cmake/ports/opus/CONTROL
Normal file
|
@ -0,0 +1,3 @@
|
|||
Source: opus
|
||||
Version: 1.3.1
|
||||
Description: Totally open, royalty-free, highly versatile audio codec
|
28
cmake/ports/opus/portfile.cmake
Normal file
28
cmake/ports/opus/portfile.cmake
Normal file
|
@ -0,0 +1,28 @@
|
|||
include(vcpkg_common_functions)
|
||||
|
||||
vcpkg_from_github(
|
||||
OUT_SOURCE_PATH
|
||||
SOURCE_PATH
|
||||
REPO
|
||||
xiph/opus
|
||||
REF
|
||||
e85ed7726db5d677c9c0677298ea0cb9c65bdd23
|
||||
SHA512
|
||||
a8c7e5bf383c06f1fdffd44d9b5f658f31eb4800cb59d12da95ddaeb5646f7a7b03025f4663362b888b1374d4cc69154f006ba07b5840ec61ddc1a1af01d6c54
|
||||
HEAD_REF
|
||||
master)
|
||||
|
||||
vcpkg_configure_cmake(SOURCE_PATH ${SOURCE_PATH} PREFER_NINJA)
|
||||
vcpkg_install_cmake()
|
||||
vcpkg_fixup_cmake_targets(CONFIG_PATH lib/cmake/Opus)
|
||||
vcpkg_copy_pdbs()
|
||||
|
||||
file(INSTALL
|
||||
${SOURCE_PATH}/COPYING
|
||||
DESTINATION
|
||||
${CURRENT_PACKAGES_DIR}/share/opus
|
||||
RENAME copyright)
|
||||
|
||||
file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/lib/cmake
|
||||
${CURRENT_PACKAGES_DIR}/lib/cmake
|
||||
${CURRENT_PACKAGES_DIR}/debug/include)
|
|
@ -1227,8 +1227,8 @@
|
|||
"name": "codec_preference_order",
|
||||
"label": "Audio Codec Preference Order",
|
||||
"help": "List of codec names in order of preferred usage",
|
||||
"placeholder": "hifiAC, zlib, pcm",
|
||||
"default": "hifiAC,zlib,pcm",
|
||||
"placeholder": "opus, hifiAC, zlib, pcm",
|
||||
"default": "opus,hifiAC,zlib,pcm",
|
||||
"advanced": true
|
||||
}
|
||||
]
|
||||
|
|
|
@ -43,6 +43,8 @@ set(DIR "pcmCodec")
|
|||
add_subdirectory(${DIR})
|
||||
set(DIR "hifiCodec")
|
||||
add_subdirectory(${DIR})
|
||||
set(DIR "opusCodec")
|
||||
add_subdirectory(${DIR})
|
||||
|
||||
# example plugins
|
||||
set(DIR "KasenAPIExample")
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
set(TARGET_NAME hifiCodec)
|
||||
setup_hifi_client_server_plugin()
|
||||
link_hifi_libraries(audio plugins)
|
||||
link_hifi_libraries(shared audio plugins)
|
||||
target_hifiAudioCodec()
|
||||
if (BUILD_SERVER)
|
||||
install_beside_console()
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <AudioCodec.h>
|
||||
#include <AudioConstants.h>
|
||||
#include <PerfStat.h>
|
||||
|
||||
|
||||
const char* HiFiCodec::NAME { "hifiAC" };
|
||||
|
@ -44,6 +45,8 @@ public:
|
|||
}
|
||||
|
||||
virtual void encode(const QByteArray& decodedBuffer, QByteArray& encodedBuffer) override {
|
||||
PerformanceTimer perfTimer("HiFiEncoder::encode");
|
||||
|
||||
encodedBuffer.resize(_encodedSize);
|
||||
AudioEncoder::process((const int16_t*)decodedBuffer.constData(), (int16_t*)encodedBuffer.data(), AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
|
||||
}
|
||||
|
@ -58,11 +61,15 @@ public:
|
|||
}
|
||||
|
||||
virtual void decode(const QByteArray& encodedBuffer, QByteArray& decodedBuffer) override {
|
||||
PerformanceTimer perfTimer("HiFiEncoder::decode");
|
||||
|
||||
decodedBuffer.resize(_decodedSize);
|
||||
AudioDecoder::process((const int16_t*)encodedBuffer.constData(), (int16_t*)decodedBuffer.data(), AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL, true);
|
||||
}
|
||||
|
||||
virtual void lostFrame(QByteArray& decodedBuffer) override {
|
||||
PerformanceTimer perfTimer("HiFiEncoder::lostFrame");
|
||||
|
||||
decodedBuffer.resize(_decodedSize);
|
||||
// this performs packet loss interpolation
|
||||
AudioDecoder::process(nullptr, (int16_t*)decodedBuffer.data(), AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL, false);
|
||||
|
|
16
plugins/opusCodec/CMakeLists.txt
Normal file
16
plugins/opusCodec/CMakeLists.txt
Normal file
|
@ -0,0 +1,16 @@
|
|||
#
|
||||
# Created by Michael Bailey on 12/20/2019
|
||||
# Copyright 2019 Michael Bailey
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http:#www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
set(TARGET_NAME opusCodec)
|
||||
setup_hifi_client_server_plugin()
|
||||
link_hifi_libraries(shared audio plugins)
|
||||
target_opus()
|
||||
|
||||
if (BUILD_SERVER)
|
||||
install_beside_console()
|
||||
endif ()
|
58
plugins/opusCodec/src/OpusCodecManager.cpp
Normal file
58
plugins/opusCodec/src/OpusCodecManager.cpp
Normal file
|
@ -0,0 +1,58 @@
|
|||
//
|
||||
// opusCodec.cpp
|
||||
// plugins/opusCodec/src
|
||||
//
|
||||
// Created by Michael Bailey on 12/20/2019
|
||||
// Copyright 2019 Michael Bailey
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "OpusCodecManager.h"
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
#include <PerfStat.h>
|
||||
|
||||
#include "OpusEncoder.h"
|
||||
#include "OpusDecoder.h"
|
||||
|
||||
const char* AthenaOpusCodec::NAME { "opus" };
|
||||
|
||||
void AthenaOpusCodec::init() {
|
||||
}
|
||||
|
||||
void AthenaOpusCodec::deinit() {
|
||||
}
|
||||
|
||||
bool AthenaOpusCodec::activate() {
|
||||
CodecPlugin::activate();
|
||||
return true;
|
||||
}
|
||||
|
||||
void AthenaOpusCodec::deactivate() {
|
||||
CodecPlugin::deactivate();
|
||||
}
|
||||
|
||||
|
||||
bool AthenaOpusCodec::isSupported() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Encoder* AthenaOpusCodec::createEncoder(int sampleRate, int numChannels) {
|
||||
return new AthenaOpusEncoder(sampleRate, numChannels);
|
||||
}
|
||||
|
||||
Decoder* AthenaOpusCodec::createDecoder(int sampleRate, int numChannels) {
|
||||
return new AthenaOpusDecoder(sampleRate, numChannels);
|
||||
}
|
||||
|
||||
void AthenaOpusCodec::releaseEncoder(Encoder* encoder) {
|
||||
delete encoder;
|
||||
}
|
||||
|
||||
void AthenaOpusCodec::releaseDecoder(Decoder* decoder) {
|
||||
delete decoder;
|
||||
}
|
42
plugins/opusCodec/src/OpusCodecManager.h
Normal file
42
plugins/opusCodec/src/OpusCodecManager.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// OpusCodecManager.h
|
||||
// plugins/opusCodec/src
|
||||
//
|
||||
// Created by Michael Bailey on 12/20/2019
|
||||
// Copyright 2019 Michael Bailey
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi__OpusCodecManager_h
|
||||
#define hifi__OpusCodecManager_h
|
||||
|
||||
#include <plugins/CodecPlugin.h>
|
||||
|
||||
class AthenaOpusCodec : public CodecPlugin {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// Plugin functions
|
||||
bool isSupported() const override;
|
||||
const QString getName() const override { return NAME; }
|
||||
|
||||
void init() override;
|
||||
void deinit() override;
|
||||
|
||||
/// Called when a plugin is being activated for use. May be called multiple times.
|
||||
bool activate() override;
|
||||
/// Called when a plugin is no longer being used. May be called multiple times.
|
||||
void deactivate() override;
|
||||
|
||||
virtual Encoder* createEncoder(int sampleRate, int numChannels) override;
|
||||
virtual Decoder* createDecoder(int sampleRate, int numChannels) override;
|
||||
virtual void releaseEncoder(Encoder* encoder) override;
|
||||
virtual void releaseDecoder(Decoder* decoder) override;
|
||||
|
||||
private:
|
||||
static const char* NAME;
|
||||
};
|
||||
|
||||
#endif // hifi__opusCodecManager_h
|
45
plugins/opusCodec/src/OpusCodecProvider.cpp
Normal file
45
plugins/opusCodec/src/OpusCodecProvider.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// Created by Michael Bailey on 12/20/2019
|
||||
// Copyright 2019 Michael Bailey
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QtPlugin>
|
||||
#include <QtCore/QStringList>
|
||||
|
||||
#include <plugins/RuntimePlugin.h>
|
||||
#include <plugins/CodecPlugin.h>
|
||||
|
||||
#include "OpusCodecManager.h"
|
||||
|
||||
class AthenaOpusCodecProvider : public QObject, public CodecProvider {
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID CodecProvider_iid FILE "plugin.json")
|
||||
Q_INTERFACES(CodecProvider)
|
||||
|
||||
public:
|
||||
AthenaOpusCodecProvider(QObject* parent = nullptr) : QObject(parent) {}
|
||||
virtual ~AthenaOpusCodecProvider() {}
|
||||
|
||||
virtual CodecPluginList getCodecPlugins() override {
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [&] {
|
||||
|
||||
CodecPluginPointer opusCodec(new AthenaOpusCodec());
|
||||
if (opusCodec->isSupported()) {
|
||||
_codecPlugins.push_back(opusCodec);
|
||||
}
|
||||
});
|
||||
return _codecPlugins;
|
||||
}
|
||||
|
||||
private:
|
||||
CodecPluginList _codecPlugins;
|
||||
};
|
||||
|
||||
#include "OpusCodecProvider.moc"
|
131
plugins/opusCodec/src/OpusDecoder.cpp
Normal file
131
plugins/opusCodec/src/OpusDecoder.cpp
Normal file
|
@ -0,0 +1,131 @@
|
|||
//
|
||||
// OpusCodecManager.h
|
||||
// plugins/opusCodec/src
|
||||
//
|
||||
// Copyright 2020 Dale Glass
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <PerfStat.h>
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <AudioConstants.h>
|
||||
|
||||
#include "OpusDecoder.h"
|
||||
|
||||
static QLoggingCategory decoder("AthenaOpusDecoder");
|
||||
|
||||
static QString error_to_string(int error) {
|
||||
switch (error) {
|
||||
case OPUS_OK:
|
||||
return "OK";
|
||||
case OPUS_BAD_ARG:
|
||||
return "One or more invalid/out of range arguments.";
|
||||
case OPUS_BUFFER_TOO_SMALL:
|
||||
return "The mode struct passed is invalid.";
|
||||
case OPUS_INTERNAL_ERROR:
|
||||
return "An internal error was detected.";
|
||||
case OPUS_INVALID_PACKET:
|
||||
return "The compressed data passed is corrupted.";
|
||||
case OPUS_UNIMPLEMENTED:
|
||||
return "Invalid/unsupported request number.";
|
||||
case OPUS_INVALID_STATE:
|
||||
return "An encoder or decoder structure is invalid or already freed.";
|
||||
default:
|
||||
return QString("Unknown error code: %i").arg(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AthenaOpusDecoder::AthenaOpusDecoder(int sampleRate, int numChannels) {
|
||||
int error;
|
||||
|
||||
_opusSampleRate = sampleRate;
|
||||
_opusNumChannels = numChannels;
|
||||
|
||||
_decoder = opus_decoder_create(sampleRate, numChannels, &error);
|
||||
|
||||
if (error != OPUS_OK) {
|
||||
qCCritical(decoder) << "Failed to initialize Opus encoder: " << error_to_string(error);
|
||||
_decoder = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
qCDebug(decoder) << "Opus decoder initialized, sampleRate = " << sampleRate << "; numChannels = " << numChannels;
|
||||
}
|
||||
|
||||
AthenaOpusDecoder::~AthenaOpusDecoder() {
|
||||
if (_decoder) {
|
||||
opus_decoder_destroy(_decoder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AthenaOpusDecoder::decode(const QByteArray &encodedBuffer, QByteArray &decodedBuffer) {
|
||||
assert(_decoder);
|
||||
PerformanceTimer perfTimer("AthenaOpusDecoder::decode");
|
||||
|
||||
// The audio system encodes and decodes always in fixed size chunks
|
||||
int bufferSize = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * static_cast<int>(sizeof(int16_t))
|
||||
* _opusNumChannels;
|
||||
|
||||
decodedBuffer.resize(bufferSize);
|
||||
int bufferFrames = decodedBuffer.size() / _opusNumChannels / static_cast<int>(sizeof(opus_int16));
|
||||
int decoded_frames = opus_decode(_decoder, reinterpret_cast<const unsigned char*>(encodedBuffer.data()),
|
||||
encodedBuffer.length(), reinterpret_cast<opus_int16*>(decodedBuffer.data()), bufferFrames, 0);
|
||||
|
||||
if (decoded_frames >= 0) {
|
||||
|
||||
if (decoded_frames < bufferFrames) {
|
||||
qCWarning(decoder) << "Opus decoder returned " << decoded_frames << ", but " << bufferFrames
|
||||
<< " were expected!";
|
||||
|
||||
int start = decoded_frames * static_cast<int>(sizeof(int16_t)) * _opusNumChannels;
|
||||
memset( &decodedBuffer.data()[start], 0, static_cast<size_t>(decodedBuffer.length() - start));
|
||||
} else if (decoded_frames > bufferFrames) {
|
||||
// This should never happen
|
||||
qCCritical(decoder) << "Opus decoder returned " << decoded_frames << ", but only " << bufferFrames
|
||||
<< " were expected! Buffer overflow!?";
|
||||
}
|
||||
} else {
|
||||
qCCritical(decoder) << "Failed to decode audio: " << error_to_string(decoded_frames);
|
||||
decodedBuffer.fill('\0');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AthenaOpusDecoder::lostFrame(QByteArray &decodedBuffer) {
|
||||
assert(_decoder);
|
||||
|
||||
PerformanceTimer perfTimer("AthenaOpusDecoder::lostFrame");
|
||||
|
||||
int bufferSize = AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * static_cast<int>(sizeof(int16_t))
|
||||
* _opusNumChannels;
|
||||
decodedBuffer.resize(bufferSize);
|
||||
int bufferFrames = decodedBuffer.size() / _opusNumChannels / static_cast<int>(sizeof(opus_int16));
|
||||
|
||||
int decoded_frames = opus_decode(_decoder, nullptr, 0, reinterpret_cast<opus_int16*>(decodedBuffer.data()),
|
||||
bufferFrames, 1);
|
||||
|
||||
if (decoded_frames >= 0) {
|
||||
|
||||
if ( decoded_frames < bufferFrames ) {
|
||||
qCWarning(decoder) << "Opus decoder returned " << decoded_frames << ", but " << bufferFrames
|
||||
<< " were expected!";
|
||||
|
||||
int start = decoded_frames * static_cast<int>(sizeof(int16_t)) * _opusNumChannels;
|
||||
memset( &decodedBuffer.data()[start], 0, static_cast<size_t>(decodedBuffer.length() - start));
|
||||
} else if (decoded_frames > bufferFrames) {
|
||||
// This should never happen
|
||||
qCCritical(decoder) << "Opus decoder returned " << decoded_frames << ", but only " << bufferFrames
|
||||
<< " were expected! Buffer overflow!?";
|
||||
}
|
||||
|
||||
} else {
|
||||
qCCritical(decoder) << "Failed to decode lost frame: " << error_to_string(decoded_frames);
|
||||
decodedBuffer.fill('\0');
|
||||
}
|
||||
|
||||
}
|
39
plugins/opusCodec/src/OpusDecoder.h
Normal file
39
plugins/opusCodec/src/OpusDecoder.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// OpusCodecManager.h
|
||||
// plugins/opusCodec/src
|
||||
//
|
||||
// Copyright 2020 Dale Glass
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef OPUSDECODER_H
|
||||
#define OPUSDECODER_H
|
||||
|
||||
|
||||
#include <plugins/CodecPlugin.h>
|
||||
#include <opus/opus.h>
|
||||
|
||||
|
||||
class AthenaOpusDecoder : public Decoder {
|
||||
public:
|
||||
AthenaOpusDecoder(int sampleRate, int numChannels);
|
||||
~AthenaOpusDecoder() override;
|
||||
|
||||
|
||||
virtual void decode(const QByteArray& encodedBuffer, QByteArray& decodedBuffer) override;
|
||||
virtual void lostFrame(QByteArray &decodedBuffer) override;
|
||||
|
||||
|
||||
private:
|
||||
int _encodedSize;
|
||||
|
||||
OpusDecoder* _decoder = nullptr;
|
||||
int _opusSampleRate = 0;
|
||||
int _opusNumChannels = 0;
|
||||
int _decodedSize = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif // OPUSDECODER_H
|
273
plugins/opusCodec/src/OpusEncoder.cpp
Normal file
273
plugins/opusCodec/src/OpusEncoder.cpp
Normal file
|
@ -0,0 +1,273 @@
|
|||
//
|
||||
// OpusCodecManager.h
|
||||
// plugins/opusCodec/src
|
||||
//
|
||||
// Copyright 2020 Dale Glass
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <PerfStat.h>
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <opus/opus.h>
|
||||
|
||||
#include "OpusEncoder.h"
|
||||
|
||||
static QLoggingCategory encoder("AthenaOpusEncoder");
|
||||
|
||||
static QString errorToString(int error) {
|
||||
switch (error) {
|
||||
case OPUS_OK:
|
||||
return "OK";
|
||||
case OPUS_BAD_ARG:
|
||||
return "One or more invalid/out of range arguments.";
|
||||
case OPUS_BUFFER_TOO_SMALL:
|
||||
return "The mode struct passed is invalid.";
|
||||
case OPUS_INTERNAL_ERROR:
|
||||
return "An internal error was detected.";
|
||||
case OPUS_INVALID_PACKET:
|
||||
return "The compressed data passed is corrupted.";
|
||||
case OPUS_UNIMPLEMENTED:
|
||||
return "Invalid/unsupported request number.";
|
||||
case OPUS_INVALID_STATE:
|
||||
return "An encoder or decoder structure is invalid or already freed.";
|
||||
default:
|
||||
return QString("Unknown error code: %i").arg(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
AthenaOpusEncoder::AthenaOpusEncoder(int sampleRate, int numChannels) {
|
||||
_opusSampleRate = sampleRate;
|
||||
_opusChannels = numChannels;
|
||||
|
||||
int error;
|
||||
|
||||
_encoder = opus_encoder_create(sampleRate, numChannels, DEFAULT_APPLICATION, &error);
|
||||
|
||||
if (error != OPUS_OK) {
|
||||
qCCritical(encoder) << "Failed to initialize Opus encoder: " << errorToString(error);
|
||||
_encoder = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
setBitrate(DEFAULT_BITRATE);
|
||||
setComplexity(DEFAULT_COMPLEXITY);
|
||||
setApplication(DEFAULT_APPLICATION);
|
||||
setSignal(DEFAULT_SIGNAL);
|
||||
|
||||
qCDebug(encoder) << "Opus encoder initialized, sampleRate = " << sampleRate << "; numChannels = " << numChannels;
|
||||
}
|
||||
|
||||
AthenaOpusEncoder::~AthenaOpusEncoder() {
|
||||
opus_encoder_destroy(_encoder);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AthenaOpusEncoder::encode(const QByteArray& decodedBuffer, QByteArray& encodedBuffer) {
|
||||
|
||||
PerformanceTimer perfTimer("AthenaOpusEncoder::encode");
|
||||
assert(_encoder);
|
||||
|
||||
encodedBuffer.resize(decodedBuffer.size());
|
||||
int frameSize = decodedBuffer.length() / _opusChannels / static_cast<int>(sizeof(opus_int16));
|
||||
|
||||
int bytes = opus_encode(_encoder, reinterpret_cast<const opus_int16*>(decodedBuffer.constData()), frameSize,
|
||||
reinterpret_cast<unsigned char*>(encodedBuffer.data()), encodedBuffer.size() );
|
||||
|
||||
if (bytes >= 0) {
|
||||
encodedBuffer.resize(bytes);
|
||||
} else {
|
||||
encodedBuffer.resize(0);
|
||||
|
||||
qCWarning(encoder) << "Error when encoding " << decodedBuffer.length() << " bytes of audio: "
|
||||
<< errorToString(bytes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int AthenaOpusEncoder::getComplexity() const {
|
||||
assert(_encoder);
|
||||
int returnValue;
|
||||
opus_encoder_ctl(_encoder, OPUS_GET_COMPLEXITY(&returnValue));
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void AthenaOpusEncoder::setComplexity(int complexity) {
|
||||
assert(_encoder);
|
||||
int returnValue = opus_encoder_ctl(_encoder, OPUS_SET_COMPLEXITY(complexity));
|
||||
|
||||
if (returnValue != OPUS_OK) {
|
||||
qCWarning(encoder) << "Error when setting complexity to " << complexity << ": " << errorToString(returnValue);
|
||||
}
|
||||
}
|
||||
|
||||
int AthenaOpusEncoder::getBitrate() const {
|
||||
assert(_encoder);
|
||||
int returnValue;
|
||||
opus_encoder_ctl(_encoder, OPUS_GET_BITRATE(&returnValue));
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void AthenaOpusEncoder::setBitrate(int bitrate) {
|
||||
assert(_encoder);
|
||||
int errorCode = opus_encoder_ctl(_encoder, OPUS_SET_BITRATE(bitrate));
|
||||
|
||||
if (errorCode != OPUS_OK) {
|
||||
qCWarning(encoder) << "Error when setting bitrate to " << bitrate << ": " << errorToString(errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
int AthenaOpusEncoder::getVBR() const {
|
||||
assert(_encoder);
|
||||
int returnValue;
|
||||
opus_encoder_ctl(_encoder, OPUS_GET_VBR(&returnValue));
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void AthenaOpusEncoder::setVBR(int vbr) {
|
||||
assert(_encoder);
|
||||
int errorCode = opus_encoder_ctl(_encoder, OPUS_SET_VBR(vbr));
|
||||
|
||||
if (errorCode != OPUS_OK) {
|
||||
qCWarning(encoder) << "Error when setting VBR to " << vbr << ": " << errorToString(errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
int AthenaOpusEncoder::getVBRConstraint() const {
|
||||
assert(_encoder);
|
||||
int returnValue;
|
||||
opus_encoder_ctl(_encoder, OPUS_GET_VBR_CONSTRAINT(&returnValue));
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void AthenaOpusEncoder::setVBRConstraint(int vbr_const) {
|
||||
assert(_encoder);
|
||||
int errorCode = opus_encoder_ctl(_encoder, OPUS_SET_VBR_CONSTRAINT(vbr_const));
|
||||
|
||||
if (errorCode != OPUS_OK) {
|
||||
qCWarning(encoder) << "Error when setting VBR constraint to " << vbr_const << ": " << errorToString(errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
int AthenaOpusEncoder::getMaxBandwidth() const {
|
||||
assert(_encoder);
|
||||
int returnValue;
|
||||
opus_encoder_ctl(_encoder, OPUS_GET_MAX_BANDWIDTH(&returnValue));
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void AthenaOpusEncoder::setMaxBandwidth(int maxBandwidth) {
|
||||
assert(_encoder);
|
||||
int errorCode = opus_encoder_ctl(_encoder, OPUS_SET_MAX_BANDWIDTH(maxBandwidth));
|
||||
|
||||
if (errorCode != OPUS_OK) {
|
||||
qCWarning(encoder) << "Error when setting max bandwidth to " << maxBandwidth << ": " << errorToString(errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
int AthenaOpusEncoder::getBandwidth() const {
|
||||
assert(_encoder);
|
||||
int bandwidth;
|
||||
opus_encoder_ctl(_encoder, OPUS_GET_BANDWIDTH(&bandwidth));
|
||||
return bandwidth;
|
||||
}
|
||||
|
||||
void AthenaOpusEncoder::setBandwidth(int bandwidth) {
|
||||
assert(_encoder);
|
||||
int errorCode = opus_encoder_ctl(_encoder, OPUS_SET_BANDWIDTH(bandwidth));
|
||||
|
||||
if (errorCode != OPUS_OK) {
|
||||
qCWarning(encoder) << "Error when setting bandwidth to " << bandwidth << ": " << errorToString(errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
int AthenaOpusEncoder::getSignal() const {
|
||||
assert(_encoder);
|
||||
int signal;
|
||||
opus_encoder_ctl(_encoder, OPUS_GET_SIGNAL(&signal));
|
||||
return signal;
|
||||
}
|
||||
|
||||
void AthenaOpusEncoder::setSignal(int signal) {
|
||||
assert(_encoder);
|
||||
int errorCode = opus_encoder_ctl(_encoder, OPUS_SET_SIGNAL(signal));
|
||||
|
||||
if (errorCode != OPUS_OK) {
|
||||
qCWarning(encoder) << "Error when setting signal to " << signal << ": " << errorToString(errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
int AthenaOpusEncoder::getApplication() const {
|
||||
assert(_encoder);
|
||||
int applicationValue;
|
||||
opus_encoder_ctl(_encoder, OPUS_GET_APPLICATION(&applicationValue));
|
||||
return applicationValue;
|
||||
}
|
||||
|
||||
void AthenaOpusEncoder::setApplication(int application) {
|
||||
assert(_encoder);
|
||||
int errorCode = opus_encoder_ctl(_encoder, OPUS_SET_APPLICATION(application));
|
||||
|
||||
if (errorCode != OPUS_OK) {
|
||||
qCWarning(encoder) << "Error when setting application to " << application << ": " << errorToString(errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
int AthenaOpusEncoder::getLookahead() const {
|
||||
assert(_encoder);
|
||||
int lookAhead;
|
||||
opus_encoder_ctl(_encoder, OPUS_GET_LOOKAHEAD(&lookAhead));
|
||||
return lookAhead;
|
||||
}
|
||||
|
||||
int AthenaOpusEncoder::getInbandFEC() const {
|
||||
assert(_encoder);
|
||||
int fec;
|
||||
opus_encoder_ctl(_encoder, OPUS_GET_INBAND_FEC(&fec));
|
||||
return fec;
|
||||
}
|
||||
|
||||
void AthenaOpusEncoder::setInbandFEC(int inBandFEC) {
|
||||
assert(_encoder);
|
||||
int errorCode = opus_encoder_ctl(_encoder, OPUS_SET_INBAND_FEC(inBandFEC));
|
||||
|
||||
if (errorCode != OPUS_OK) {
|
||||
qCWarning(encoder) << "Error when setting inband FEC to " << inBandFEC << ": " << errorToString(errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
int AthenaOpusEncoder::getExpectedPacketLossPercentage() const {
|
||||
assert(_encoder);
|
||||
int lossPercentage;
|
||||
opus_encoder_ctl(_encoder, OPUS_GET_PACKET_LOSS_PERC(&lossPercentage));
|
||||
return lossPercentage;
|
||||
}
|
||||
|
||||
void AthenaOpusEncoder::setExpectedPacketLossPercentage(int percentage) {
|
||||
assert(_encoder);
|
||||
int errorCode = opus_encoder_ctl(_encoder, OPUS_SET_PACKET_LOSS_PERC(percentage));
|
||||
|
||||
if (errorCode != OPUS_OK) {
|
||||
qCWarning(encoder) << "Error when setting loss percent to " << percentage << ": " << errorToString(errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
int AthenaOpusEncoder::getDTX() const {
|
||||
assert(_encoder);
|
||||
int dtx;
|
||||
opus_encoder_ctl(_encoder, OPUS_GET_DTX(&dtx));
|
||||
return dtx;
|
||||
}
|
||||
|
||||
void AthenaOpusEncoder::setDTX(int dtx) {
|
||||
assert(_encoder);
|
||||
int errorCode = opus_encoder_ctl(_encoder, OPUS_SET_DTX(dtx));
|
||||
|
||||
if (errorCode != OPUS_OK) {
|
||||
qCWarning(encoder) << "Error when setting DTX to " << dtx << ": " << errorToString(errorCode);
|
||||
}
|
||||
}
|
78
plugins/opusCodec/src/OpusEncoder.h
Normal file
78
plugins/opusCodec/src/OpusEncoder.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
//
|
||||
// OpusCodecManager.h
|
||||
// plugins/opusCodec/src
|
||||
//
|
||||
// Copyright 2020 Dale Glass
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef OPUSENCODER_H
|
||||
#define OPUSENCODER_H
|
||||
#include <plugins/CodecPlugin.h>
|
||||
#include <opus/opus.h>
|
||||
|
||||
|
||||
class AthenaOpusEncoder : public Encoder {
|
||||
public:
|
||||
|
||||
AthenaOpusEncoder(int sampleRate, int numChannels);
|
||||
~AthenaOpusEncoder() override;
|
||||
|
||||
virtual void encode(const QByteArray& decodedBuffer, QByteArray& encodedBuffer) override;
|
||||
|
||||
|
||||
int getComplexity() const;
|
||||
void setComplexity(int complexity);
|
||||
|
||||
int getBitrate() const;
|
||||
void setBitrate(int bitrate);
|
||||
|
||||
int getVBR() const;
|
||||
void setVBR(int vbr);
|
||||
|
||||
int getVBRConstraint() const;
|
||||
void setVBRConstraint(int vbrConstraint);
|
||||
|
||||
int getMaxBandwidth() const;
|
||||
void setMaxBandwidth(int maxBandwidth);
|
||||
|
||||
int getBandwidth() const;
|
||||
void setBandwidth(int bandwidth);
|
||||
|
||||
int getSignal() const;
|
||||
void setSignal(int signal);
|
||||
|
||||
int getApplication() const;
|
||||
void setApplication(int application);
|
||||
|
||||
int getLookahead() const;
|
||||
|
||||
int getInbandFEC() const;
|
||||
void setInbandFEC(int inBandFEC);
|
||||
|
||||
int getExpectedPacketLossPercentage() const;
|
||||
void setExpectedPacketLossPercentage(int percentage);
|
||||
|
||||
int getDTX() const;
|
||||
void setDTX(int dtx);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
const int DEFAULT_BITRATE = 128000;
|
||||
const int DEFAULT_COMPLEXITY = 10;
|
||||
const int DEFAULT_APPLICATION = OPUS_APPLICATION_VOIP;
|
||||
const int DEFAULT_SIGNAL = OPUS_AUTO;
|
||||
|
||||
int _opusSampleRate = 0;
|
||||
int _opusChannels = 0;
|
||||
int _opusExpectedLoss = 0;
|
||||
|
||||
|
||||
OpusEncoder* _encoder = nullptr;
|
||||
};
|
||||
|
||||
|
||||
#endif // OPUSENCODER_H
|
4
plugins/opusCodec/src/plugin.json
Normal file
4
plugins/opusCodec/src/plugin.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "Opus Codec",
|
||||
"version": 1
|
||||
}
|
Loading…
Reference in a new issue