mirror of
https://github.com/danog/libtgvoip.git
synced 2024-11-27 12:44:37 +01:00
3216b76349
Better jitter buffer with packet rescaling Tried to fix some issues on Linux (telegramdesktop/tdesktop#3413) Fixes for Windows Phone
161 lines
4.4 KiB
C++
161 lines
4.4 KiB
C++
//
|
|
// 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"
|
|
|
|
tgvoip::OpusEncoder::OpusEncoder(MediaStreamItf *source):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;
|
|
mediumCorrectionBitrate=ServerConfig::GetSharedInstance()->GetInt("audio_medium_fec_bitrate", 10000);
|
|
strongCorrectionBitrate=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);
|
|
}
|
|
|
|
tgvoip::OpusEncoder::~OpusEncoder(){
|
|
opus_encoder_destroy(enc);
|
|
}
|
|
|
|
void tgvoip::OpusEncoder::Start(){
|
|
if(running)
|
|
return;
|
|
running=true;
|
|
start_thread(thread, StartThread, this);
|
|
set_thread_priority(thread, get_thread_max_priority());
|
|
set_thread_name(thread, "opus_encoder");
|
|
}
|
|
|
|
void tgvoip::OpusEncoder::Stop(){
|
|
if(!running)
|
|
return;
|
|
running=false;
|
|
queue.Put(NULL);
|
|
join_thread(thread);
|
|
}
|
|
|
|
|
|
void tgvoip::OpusEncoder::SetBitrate(uint32_t bitrate){
|
|
requestedBitrate=bitrate;
|
|
}
|
|
|
|
void tgvoip::OpusEncoder::Encode(unsigned char *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);
|
|
}
|
|
int32_t r=opus_encode(enc, (int16_t*)data, len/2, buffer, 4096);
|
|
if(r<=0){
|
|
LOGE("Error encoding: %d", r);
|
|
}else if(r==1){
|
|
LOGW("DTX");
|
|
}else if(running){
|
|
//LOGV("Packet size = %d", r);
|
|
InvokeCallback(buffer, (size_t)r);
|
|
}
|
|
}
|
|
|
|
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::StartThread(void* arg){
|
|
((OpusEncoder*)arg)->RunThread();
|
|
return NULL;
|
|
}
|
|
|
|
void tgvoip::OpusEncoder::RunThread(){
|
|
unsigned char buf[960*2];
|
|
uint32_t bufferedCount=0;
|
|
uint32_t packetsPerFrame=frameDuration/20;
|
|
LOGV("starting encoder, packets per frame=%d", packetsPerFrame);
|
|
unsigned char* frame;
|
|
if(packetsPerFrame>1)
|
|
frame=(unsigned char *) malloc(960*2*packetsPerFrame);
|
|
else
|
|
frame=NULL;
|
|
while(running){
|
|
unsigned char* packet=(unsigned char*)queue.GetBlocking();
|
|
if(packet){
|
|
if(echoCanceller)
|
|
echoCanceller->ProcessInput(packet, buf, 960*2);
|
|
else
|
|
memcpy(buf, packet, 960*2);
|
|
if(packetsPerFrame==1){
|
|
Encode(buf, 960*2);
|
|
}else{
|
|
memcpy(frame+(960*2*bufferedCount), buf, 960*2);
|
|
bufferedCount++;
|
|
if(bufferedCount==packetsPerFrame){
|
|
Encode(frame, 960*2*packetsPerFrame);
|
|
bufferedCount=0;
|
|
}
|
|
}
|
|
bufferPool.Reuse(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)));
|
|
opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(percent>17 ? OPUS_AUTO : OPUS_BANDWIDTH_FULLBAND));
|
|
}
|
|
|
|
int tgvoip::OpusEncoder::GetPacketLoss(){
|
|
return packetLossPercent;
|
|
}
|