gipOpus is a GlistEngine component that wraps libopus for real-time voice encoding and decoding.
It is intended for voice-chat pipelines where raw microphone PCM would be too large to send over the network. The component owns one Opus encoder and one Opus decoder, provides C++ lifetime management, and reports libopus errors as readable strings.
Libopus is tracked as the external/opus git submodule and pinned to the official v1.5.2 release. It is built from source as a static library. The libopus command-line programs, upstream tests, pkg-config installation, and CMake package installation are disabled.
The submodule path must not be added to .gitignore: Git stores the selected libopus commit as a gitlink in the gipOpus repository.
Clone the plugin and all nested dependencies into the GlistEngine plugin directory:
cd path/to/glist/glistplugins
git clone --recursive https://github.com/GlistPlugins/gipOpus.gitIf the repository was cloned without --recursive, initialize the dependency afterwards:
cd gipOpus
git submodule update --init --recursiveAdd the component to the application's plugin list:
set(PLUGINS gipOpus)CMake stops with an explicit error and the required submodule command when external/opus is not initialized.
The Teamchat example uses these settings:
- 48 kHz sample rate
- Mono signed 16-bit PCM
- 20 ms frames, or 960 samples at 48 kHz
OPUS_APPLICATION_VOIP- 24 kbit/s target bitrate
#include "gipOpus.h"
#include <array>
#include <cstdint>
gipOpus codec;
if (!codec.setup(48000, 1, 24000)) {
// codec.getLastError() contains the libopus error.
}
std::array<std::int16_t, 960> pcm;
std::array<unsigned char, 1275> packet;
int encodedbytes = codec.encode(pcm.data(), pcm.size(), packet.data(), packet.size());
if (encodedbytes > 0) {
std::array<std::int16_t, 960> decodedpcm;
int decodedframes = codec.decode(packet.data(), encodedbytes, decodedpcm.data(), decodedpcm.size());
}setup(sampleRate, channels, bitrate)creates the encoder and decoder.setBitrate(bitrate)changes the encoder target bitrate.encode(...)converts one valid Opus PCM frame into a compressed packet.decode(...)converts one Opus packet back into PCM frames.close()releases codec resources and is also called by the destructor.getLastError()returns the most recent initialization or codec error.
The codec object should be initialized before an audio callback starts. In a real-time voice pipeline, microphone callbacks should move PCM through a bounded ring buffer and perform Opus work on a separate worker thread.