Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ env:
-DWITH_ALSA=ON
-DWITH_DIAMONDCARD=ON
-DWITH_GSM=ON
-DWITH_OPUS=ON
-DWITH_SPEEX=ON
-DWITH_ZRTP=OFF
# Essential packages required by all builds
Expand All @@ -19,6 +20,7 @@ env:
flex
libccrtp-dev
libmagic-dev
libopus-dev
libreadline-dev
libsndfile1-dev
libucommon-dev
Expand Down Expand Up @@ -78,6 +80,7 @@ jobs:
-DWITH_QT5=OFF
-DWITH_ALSA=OFF
-DWITH_GSM=OFF
-DWITH_OPUS=OFF
-DWITH_SPEEX=OFF
-DWITH_ZRTP=OFF
# The empty string would evaluate to false and fail to override
Expand Down
9 changes: 9 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ include(CMakeDependentOption)

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)
CMAKE_DEPENDENT_OPTION(WITH_DIAMONDCARD "Enable Diamondcard integration" ON "WITH_QT5" OFF)
Expand All @@ -40,6 +41,8 @@ include (CMakePushCheckState)
include (CheckCXXSourceCompiles)
include (TestBigEndian)

find_package(PkgConfig REQUIRED)

find_package(LibXml2 REQUIRED)
find_package(LibMagic REQUIRED)
find_package(LibSndfile REQUIRED)
Expand Down Expand Up @@ -110,6 +113,12 @@ if (WITH_SPEEX)
endif (SPEEX_FOUND)
endif (WITH_SPEEX)

if (WITH_OPUS)
pkg_check_modules(Opus REQUIRED opus)
set(HAVE_OPUS TRUE)
include_directories(${Opus_INCLUDE_DIRS})
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 @@ -32,6 +32,7 @@ The following tools are also required:
* libzrtpcpp (version >= 0.9.0) [ZRTP library, ccRTP support must be enabled](https://www.gnu.org/software/ccrtp/zrtp.html)
* bcg729 [G.729A codec library](http://www.linphone.org/technical-corner/bcg729)
* 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/)
* [Akonadi](https://community.kde.org/KDE_PIM/Akonadi) (specifically, the `core` library) and [KContacts](https://cgit.kde.org/kcontacts.git)

Expand All @@ -47,6 +48,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`
* Akonadi support: `-DWITH_AKONADI=On` (requires `WITH_QT5`)
Expand Down
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ set(twinkle_LIBS
if (WITH_GSM)
list(APPEND twinkle_LIBS ${GSM_LIBRARY})
endif (WITH_GSM)
if (WITH_OPUS)
list(APPEND twinkle_LIBS ${Opus_LIBRARIES})
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 @@ -37,6 +39,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 @@ -112,3 +116,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
37 changes: 37 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_G722,
CODEC_G726_16,
Expand All @@ -41,13 +45,34 @@ 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
#define PTIME_G711_ALAW 20
#define PTIME_G711_ULAW 20
#define PTIME_G722 20
#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 @@ -111,4 +136,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

extern "C" {
# include "g722.h"
# include "g722_local.h"
Expand Down Expand Up @@ -157,6 +161,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