mirror of
https://github.com/danog/libtgvoip.git
synced 2024-11-27 04:34:42 +01:00
5caaaafa42
I'm now using the entire audio processing module from WebRTC as opposed to individual DSP algorithms pulled from there before. Seems to work better this way.
205 lines
6.1 KiB
C++
Executable File
205 lines
6.1 KiB
C++
Executable File
//
|
|
// libtgvoip is free and unencumbered public domain software.
|
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
|
// you should have received with this source code distribution.
|
|
//
|
|
|
|
#include "OpusEncoder.h"
|
|
#include <assert.h>
|
|
#include "logging.h"
|
|
#include "VoIPServerConfig.h"
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <opus/opus.h>
|
|
#else
|
|
#include "opus.h"
|
|
#endif
|
|
|
|
tgvoip::OpusEncoder::OpusEncoder(MediaStreamItf *source, bool needSecondary):queue(11), bufferPool(960*2, 10){
|
|
this->source=source;
|
|
source->SetCallback(tgvoip::OpusEncoder::Callback, this);
|
|
enc=opus_encoder_create(48000, 1, OPUS_APPLICATION_VOIP, NULL);
|
|
opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(10));
|
|
opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC(15));
|
|
opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC(1));
|
|
opus_encoder_ctl(enc, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
|
|
opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND));
|
|
requestedBitrate=32000;
|
|
currentBitrate=0;
|
|
running=false;
|
|
echoCanceller=NULL;
|
|
complexity=10;
|
|
frameDuration=20;
|
|
levelMeter=NULL;
|
|
mediumCorrectionBitrate=static_cast<uint32_t>(ServerConfig::GetSharedInstance()->GetInt("audio_medium_fec_bitrate", 10000));
|
|
strongCorrectionBitrate=static_cast<uint32_t>(ServerConfig::GetSharedInstance()->GetInt("audio_strong_fec_bitrate", 8000));
|
|
mediumCorrectionMultiplier=ServerConfig::GetSharedInstance()->GetDouble("audio_medium_fec_multiplier", 1.5);
|
|
strongCorrectionMultiplier=ServerConfig::GetSharedInstance()->GetDouble("audio_strong_fec_multiplier", 2.0);
|
|
secondaryEncoderEnabled=false;
|
|
|
|
if(needSecondary){
|
|
secondaryEncoder=opus_encoder_create(48000, 1, OPUS_APPLICATION_VOIP, NULL);
|
|
opus_encoder_ctl(secondaryEncoder, OPUS_SET_COMPLEXITY(10));
|
|
opus_encoder_ctl(secondaryEncoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
|
|
//opus_encoder_ctl(secondaryEncoder, OPUS_SET_VBR(0));
|
|
opus_encoder_ctl(secondaryEncoder, OPUS_SET_BITRATE(8000));
|
|
opus_encoder_ctl(secondaryEncoder, OPUS_SET_INBAND_FEC(1));
|
|
opus_encoder_ctl(secondaryEncoder, OPUS_SET_PACKET_LOSS_PERC(15));
|
|
opus_encoder_ctl(secondaryEncoder, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND));
|
|
}else{
|
|
secondaryEncoder=NULL;
|
|
}
|
|
}
|
|
|
|
tgvoip::OpusEncoder::~OpusEncoder(){
|
|
opus_encoder_destroy(enc);
|
|
if(secondaryEncoder)
|
|
opus_encoder_destroy(secondaryEncoder);
|
|
}
|
|
|
|
void tgvoip::OpusEncoder::Start(){
|
|
if(running)
|
|
return;
|
|
running=true;
|
|
thread=new Thread(std::bind(&tgvoip::OpusEncoder::RunThread, this));
|
|
thread->SetName("OpusEncoder");
|
|
thread->Start();
|
|
thread->SetMaxPriority();
|
|
}
|
|
|
|
void tgvoip::OpusEncoder::Stop(){
|
|
if(!running)
|
|
return;
|
|
running=false;
|
|
queue.Put(NULL);
|
|
thread->Join();
|
|
delete thread;
|
|
}
|
|
|
|
|
|
void tgvoip::OpusEncoder::SetBitrate(uint32_t bitrate){
|
|
requestedBitrate=bitrate;
|
|
}
|
|
|
|
void tgvoip::OpusEncoder::Encode(int16_t* data, size_t len){
|
|
if(requestedBitrate!=currentBitrate){
|
|
opus_encoder_ctl(enc, OPUS_SET_BITRATE(requestedBitrate));
|
|
currentBitrate=requestedBitrate;
|
|
LOGV("opus_encoder: setting bitrate to %u", currentBitrate);
|
|
}
|
|
if(levelMeter)
|
|
levelMeter->Update(data, len);
|
|
int32_t r=opus_encode(enc, data, static_cast<int>(len), buffer, 4096);
|
|
if(r<=0){
|
|
LOGE("Error encoding: %d", r);
|
|
}else if(r==1){
|
|
LOGW("DTX");
|
|
}else if(running){
|
|
//LOGV("Packet size = %d", r);
|
|
int32_t secondaryLen=0;
|
|
unsigned char secondaryBuffer[128];
|
|
if(secondaryEncoderEnabled && secondaryEncoder){
|
|
secondaryLen=opus_encode(secondaryEncoder, data, static_cast<int>(len), secondaryBuffer, sizeof(secondaryBuffer));
|
|
//LOGV("secondaryLen %d", secondaryLen);
|
|
}
|
|
InvokeCallback(buffer, (size_t)r, secondaryBuffer, (size_t)secondaryLen);
|
|
}
|
|
}
|
|
|
|
size_t tgvoip::OpusEncoder::Callback(unsigned char *data, size_t len, void* param){
|
|
OpusEncoder* e=(OpusEncoder*)param;
|
|
unsigned char* buf=e->bufferPool.Get();
|
|
if(buf){
|
|
assert(len==960*2);
|
|
memcpy(buf, data, 960*2);
|
|
e->queue.Put(buf);
|
|
}else{
|
|
LOGW("opus_encoder: no buffer slots left");
|
|
if(e->complexity>1){
|
|
e->complexity--;
|
|
opus_encoder_ctl(e->enc, OPUS_SET_COMPLEXITY(e->complexity));
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
uint32_t tgvoip::OpusEncoder::GetBitrate(){
|
|
return requestedBitrate;
|
|
}
|
|
|
|
void tgvoip::OpusEncoder::SetEchoCanceller(EchoCanceller* aec){
|
|
echoCanceller=aec;
|
|
}
|
|
|
|
void tgvoip::OpusEncoder::RunThread(){
|
|
uint32_t bufferedCount=0;
|
|
uint32_t packetsPerFrame=frameDuration/20;
|
|
LOGV("starting encoder, packets per frame=%d", packetsPerFrame);
|
|
int16_t* frame;
|
|
if(packetsPerFrame>1)
|
|
frame=(int16_t*) malloc(960*2*packetsPerFrame);
|
|
else
|
|
frame=NULL;
|
|
while(running){
|
|
int16_t* packet=(int16_t*)queue.GetBlocking();
|
|
if(packet){
|
|
if(echoCanceller)
|
|
echoCanceller->ProcessInput(packet, 960);
|
|
if(packetsPerFrame==1){
|
|
Encode(packet, 960);
|
|
}else{
|
|
memcpy(frame+(960*bufferedCount), packet, 960*2);
|
|
bufferedCount++;
|
|
if(bufferedCount==packetsPerFrame){
|
|
Encode(frame, 960*packetsPerFrame);
|
|
bufferedCount=0;
|
|
}
|
|
}
|
|
bufferPool.Reuse(reinterpret_cast<unsigned char *>(packet));
|
|
}
|
|
}
|
|
if(frame)
|
|
free(frame);
|
|
}
|
|
|
|
|
|
void tgvoip::OpusEncoder::SetOutputFrameDuration(uint32_t duration){
|
|
frameDuration=duration;
|
|
}
|
|
|
|
|
|
void tgvoip::OpusEncoder::SetPacketLoss(int percent){
|
|
packetLossPercent=percent;
|
|
double multiplier=1;
|
|
if(currentBitrate<=strongCorrectionBitrate)
|
|
multiplier=strongCorrectionMultiplier;
|
|
else if(currentBitrate<=mediumCorrectionBitrate)
|
|
multiplier=mediumCorrectionMultiplier;
|
|
opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC((int)(percent*multiplier)));
|
|
}
|
|
|
|
int tgvoip::OpusEncoder::GetPacketLoss(){
|
|
return packetLossPercent;
|
|
}
|
|
|
|
void tgvoip::OpusEncoder::SetDTX(bool enable){
|
|
opus_encoder_ctl(enc, OPUS_SET_DTX(enable ? 1 : 0));
|
|
}
|
|
|
|
void tgvoip::OpusEncoder::SetLevelMeter(tgvoip::AudioLevelMeter *levelMeter){
|
|
this->levelMeter=levelMeter;
|
|
}
|
|
|
|
void tgvoip::OpusEncoder::SetCallback(void (*f)(unsigned char *, size_t, unsigned char *, size_t, void *), void *param){
|
|
callback=f;
|
|
callbackParam=param;
|
|
}
|
|
|
|
void tgvoip::OpusEncoder::InvokeCallback(unsigned char *data, size_t length, unsigned char *secondaryData, size_t secondaryLength){
|
|
callback(data, length, secondaryData, secondaryLength, callbackParam);
|
|
}
|
|
|
|
void tgvoip::OpusEncoder::SetSecondaryEncoderEnabled(bool enabled){
|
|
secondaryEncoderEnabled=enabled;
|
|
}
|