Skip to content

Commit

Permalink
Add support for the Opus codec
Browse files Browse the repository at this point in the history
Closes LubosD#101
  • Loading branch information
fbriere committed Dec 22, 2019
1 parent ba1819c commit ab08f4e
Show file tree
Hide file tree
Showing 23 changed files with 1,401 additions and 10 deletions.
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ sudo: required

env:
global:
- FLAGS="-DWITH_QT5=ON -DWITH_ALSA=ON -DWITH_GSM=ON -DWITH_SPEEX=ON -DWITH_ZRTP=ON"
- FLAGS="-DWITH_QT5=ON -DWITH_ALSA=ON -DWITH_GSM=ON -DWITH_SPEEX=ON -DWITH_OPUS=ON -DWITH_ZRTP=ON"
# (qttools5-dev-tools is explicitly included because of Debian bug #835295)
- PACKAGES="libasound2-dev libgsm1-dev libspeex-dev libspeexdsp-dev libzrtpcpp-dev qtdeclarative5-dev qttools5-dev qttools5-dev-tools"
- PACKAGES="libasound2-dev libgsm1-dev libspeex-dev libopus-dev libspeexdsp-dev libzrtpcpp-dev qtdeclarative5-dev qttools5-dev qttools5-dev-tools"
matrix:
# Test various compiler versions
- PACKAGES_ADD="g++-4.9" MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9"
Expand All @@ -16,7 +16,7 @@ env:
# failure when compiling with GCC 7 or Clang.
#- PACKAGES_ADD="g++-7" MATRIX_EVAL="CC=gcc-7 && CXX=g++-7"
# Test with all options disabled
- FLAGS="-DWITH_QT5=OFF -DWITH_ALSA=OFF -DWITH_GSM=OFF -DWITH_SPEEX=OFF -DWITH_ZRTP=OFF" PACKAGES=""
- FLAGS="-DWITH_QT5=OFF -DWITH_ALSA=OFF -DWITH_GSM=OFF -DWITH_SPEEX=OFF -DWITH_OPUS=OFF -DWITH_ZRTP=OFF" PACKAGES=""

# See https://docs.travis-ci.com/user/languages/cpp/#C11-C%2B%2B11-%28and-Beyond%29-and-Toolchain-Versioning
before_install:
Expand Down
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")

OPTION(WITH_ZRTP "Enable ZRTP encrypted calls" OFF)
OPTION(WITH_SPEEX "Enable the Speex codec" OFF)
OPTION(WITH_OPUS "Enable the Opus codec" OFF)
OPTION(WITH_ILBC "Enable the iLBC codec" OFF)
OPTION(WITH_ALSA "Enable ALSA support" ON)
OPTION(WITH_DIAMONDCARD "Enable Diamondcard integration" OFF)
Expand All @@ -20,6 +21,8 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
include (CheckIncludeFile)
include (CheckIncludeFiles)

find_package(PkgConfig REQUIRED)

find_package(LibXml2 REQUIRED)
find_package(LibMagic REQUIRED)
find_package(LibSndfile REQUIRED)
Expand Down Expand Up @@ -78,6 +81,11 @@ if (WITH_SPEEX)
endif (SPEEX_FOUND)
endif (WITH_SPEEX)

if (WITH_OPUS)
pkg_check_modules(Opus REQUIRED IMPORTED_TARGET opus)
set(HAVE_OPUS TRUE)
endif (WITH_OPUS)

if (WITH_ILBC)
find_package(Ilbc)

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ The following tools are also required:
* libzrtpcpp (version >= 0.9.0) [ZRTP library, ccRTP support must be enabled](http://www.gnutelephony.org/index.php/GNU_ZRTP)
* bcg729 [G.729A codec library](http://www.linphone.org/technical-corner/bcg729/overview)
* Speex and SpeexDSP [Speex codec library](http://www.speex.org/)
* libopus [Opus codec library](http://opus-codec.org/)
* iLBC [iLBC codec library](http://www.ilbcfreeware.org/)

## Build
Expand All @@ -42,6 +43,7 @@ All possible options are:
* ZRTP support: `-DWITH_ZRTP=On`
* G.729A codec support: `-DWITH_G729=On`
* Speex codec support: `-DWITH_SPEEX=On`
* Opus codec support: `-DWITH_OPUS=On`
* iLBC codec support: `-DWITH_ILBC=On`
* Diamondcard support: `-DWITH_DIAMONDCARD=On`

Expand Down
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ set(twinkle_LIBS
if (WITH_GSM)
list(APPEND twinkle_LIBS ${GSM_LIBRARY})
endif (WITH_GSM)
if (WITH_OPUS)
list(APPEND twinkle_LIBS PkgConfig::Opus)
endif (WITH_OPUS)

if (WITH_QT5)
add_subdirectory(gui)
Expand Down
39 changes: 39 additions & 0 deletions src/audio/audio_codecs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
*/

#include <cstdlib>
#include <sstream>
#include "audio_codecs.h"
#include "userintf.h"

unsigned short audio_sample_rate(t_audio_codec codec) {
switch(codec) {
Expand All @@ -36,6 +38,8 @@ unsigned short audio_sample_rate(t_audio_codec codec) {
return 16000;
case CODEC_SPEEX_UWB:
return 32000;
case CODEC_OPUS:
return 48000;
default:
// Use 8000 as default rate
return 8000;
Expand Down Expand Up @@ -97,3 +101,38 @@ short mix_linear_pcm(short pcm1, short pcm2) {

return short(mixed_sample);
}

#ifdef HAVE_OPUS
unsigned short opus_adjusted_ptime(unsigned short ptime) {
if (ptime <= 10) {
return 10;
} else if (ptime <= 20) {
return 20;
} else if (ptime <= 40) {
return 40;
} else if (ptime <= 60) {
return 60;
} else {
// Maximum duration of an Opus frame
// (Although libopus v1.2+ allows for values up to 120 ms, it
// merely encodes multiple frames into a single Opus packet)
return 60;
}
}

void log_opus_error(
const std::string &func_name,
const std::string &msg,
int opus_error,
bool display_msg)
{
std::stringstream ss;
ss << "Opus error: " << msg << ": " << opus_strerror(opus_error);
std::string s = ss.str();

log_file->write_report(s, func_name, LOG_NORMAL, LOG_CRITICAL);
if (display_msg) {
ui->cb_display_msg(s, MSG_CRITICAL);
}
}
#endif
32 changes: 32 additions & 0 deletions src/audio/audio_codecs.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@
#ifndef _AUDIO_CODECS_H
#define _AUDIO_CODECS_H

#include <string>
#include "twinkle_config.h"
#include "g711.h"
#include "g72x.h"
#include "log.h"

// Audio codecs
enum t_audio_codec {
Expand All @@ -31,6 +34,7 @@ enum t_audio_codec {
CODEC_SPEEX_NB,
CODEC_SPEEX_WB,
CODEC_SPEEX_UWB,
CODEC_OPUS,
CODEC_ILBC,
CODEC_G726_16,
CODEC_G726_24,
Expand All @@ -40,9 +44,24 @@ enum t_audio_codec {
CODEC_G729A
};

enum t_opus_bandwidth_sample_rate {
OPUS_SAMPLE_RATE_NB = 8000,
OPUS_SAMPLE_RATE_MB = 12000,
OPUS_SAMPLE_RATE_WB = 16000,
OPUS_SAMPLE_RATE_SWB = 24000,
OPUS_SAMPLE_RATE_FB = 48000,
};

// Format specific parameters, received on a "a=fmtp:" line during the SDP
// negotiation, to be passed to the corresponding encoder/decoder
struct t_codec_sdp_params {
#ifdef HAVE_OPUS
bool opus_cbr = 0;
unsigned int opus_maxplaybackrate = 0;
unsigned int opus_maxaveragebitrate = 0;
bool opus_useinbandfec = 0;
bool opus_usedtx = 0;
#endif
};

// Default ptime values (ms) for audio codecs
Expand All @@ -51,6 +70,7 @@ struct t_codec_sdp_params {
#define PTIME_G726 20
#define PTIME_GSM 20
#define PTIME_SPEEX 20
#define PTIME_OPUS 20
#define MIN_PTIME 10
#define MAX_PTIME 80

Expand Down Expand Up @@ -109,4 +129,16 @@ int resample(short *input_buf, int input_len, int input_sample_rate,
// Mix 2 16 bits signed linear PCM values
short mix_linear_pcm(short pcm1, short pcm2);

#ifdef HAVE_OPUS
// Bump an arbitrary ptime to the next value allowed by the Opus codec
unsigned short opus_adjusted_ptime(unsigned short ptime);

// Log an Opus error
void log_opus_error(
const std::string &func_name,
const std::string &msg,
int opus_error,
bool display_msg = true);
#endif

#endif
74 changes: 74 additions & 0 deletions src/audio/audio_decoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,80 @@ bool t_speex_audio_decoder::valid_payload_size(uint16 payload_size, uint16 sampl
}
#endif

#ifdef HAVE_OPUS
//////////////////////////////////////////
// class t_opus_audio_decoder
//////////////////////////////////////////

t_opus_audio_decoder::t_opus_audio_decoder(uint16 default_ptime, t_user *user_config) :
t_audio_decoder(default_ptime, true, user_config)
{
_codec = CODEC_OPUS;
if (_default_ptime == 0) {
_default_ptime = PTIME_OPUS;
}
_frame_size = audio_sample_rate(_codec) * _default_ptime / 1000;

int error;
dec = opus_decoder_create(audio_sample_rate(_codec), 1, &error);
if (error != OPUS_OK) {
log_opus_error("t_opus_audio_decoder::t_opus_audio_decoder",
"Cannot create decoder", error);
dec = NULL;
return;
}
}

t_opus_audio_decoder::~t_opus_audio_decoder() {
if (dec) {
opus_decoder_destroy(dec);
}
}

uint16 t_opus_audio_decoder::get_ptime(uint16 payload_size) const {
return get_default_ptime();
}

uint16 t_opus_audio_decoder::decode(uint8 *payload, uint16 payload_size,
int16 *pcm_buf, uint16 pcm_buf_size)
{
if (!dec) return 0;

assert(pcm_buf_size >= _frame_size);

int nsamples = opus_decode(dec, payload, payload_size, pcm_buf, pcm_buf_size, 0);

if (nsamples < 0) {
log_opus_error("t_opus_audio_decoder::decode",
"Error while decoding", nsamples);
return 0;
}

return nsamples;
}

uint16 t_opus_audio_decoder::conceal(int16 *pcm_buf, uint16 pcm_buf_size) {
if (!dec) return 0;

assert(pcm_buf_size >= _frame_size);

int nsamples = opus_decode(dec, NULL, 0, pcm_buf, _frame_size, 0);

if (nsamples < 0) {
log_opus_error("t_opus_audio_decoder::conceal",
"Error while concealing missing frame", nsamples);
return 0;
}

return nsamples;
}

bool t_opus_audio_decoder::valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const
{
return true;
}
#endif

#ifdef HAVE_ILBC
//////////////////////////////////////////
// class t_ilbc_audio_decoder
Expand Down
23 changes: 23 additions & 0 deletions src/audio/audio_decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ extern "C" {
#include <speex/speex_preprocess.h>
#endif

#ifdef HAVE_OPUS
#include <opus/opus.h>
#endif

#ifdef HAVE_BCG729
extern "C" {
# include <bcg729/decoder.h>
Expand Down Expand Up @@ -152,6 +156,25 @@ class t_speex_audio_decoder : public t_audio_decoder {
};
#endif

#ifdef HAVE_OPUS
// Opus
class t_opus_audio_decoder : public t_audio_decoder {
private:
OpusDecoder *dec;
unsigned short _frame_size; // Number of samples per frame

public:
t_opus_audio_decoder(uint16 default_ptime, t_user *user_config);
virtual ~t_opus_audio_decoder();

virtual uint16 get_ptime(uint16 payload_size) const;
virtual uint16 decode(uint8 *payload, uint16 payload_size,
int16 *pcm_buf, uint16 pcm_buf_size);
virtual uint16 conceal(int16 *pcm_buf, uint16 pcm_buf_size);
virtual bool valid_payload_size(uint16 payload_size, uint16 sample_buf_size) const;
};
#endif

#ifdef HAVE_ILBC
// iLBC
class t_ilbc_audio_decoder : public t_audio_decoder {
Expand Down
Loading

0 comments on commit ab08f4e

Please sign in to comment.