// // 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 #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; }