2.0-alpha4

This commit is contained in:
Grishka 2018-05-15 21:23:46 +03:00
parent a3feec022c
commit b52eb581fa
55 changed files with 4190 additions and 1867 deletions

View File

@ -21,12 +21,10 @@ public:
BlockingQueue(size_t capacity) : semaphore(capacity, 0){
this->capacity=capacity;
overflowCallback=NULL;
init_mutex(mutex);
};
~BlockingQueue(){
semaphore.Release();
free_mutex(mutex);
}
void Put(T thing){
@ -86,7 +84,7 @@ private:
size_t capacity;
//tgvoip_lock_t lock;
Semaphore semaphore;
tgvoip_mutex_t mutex;
Mutex mutex;
void (*overflowCallback)(T);
};
}

View File

@ -98,6 +98,13 @@ void BufferInputStream::ReadBytes(unsigned char *to, size_t count){
offset+=count;
}
BufferInputStream BufferInputStream::GetPartBuffer(size_t length, bool advance){
EnsureEnoughRemaining(length);
BufferInputStream s=BufferInputStream(buffer+offset, length);
if(advance)
offset+=length;
return s;
}
void BufferInputStream::EnsureEnoughRemaining(size_t need){
if(length-offset<need){

View File

@ -26,6 +26,7 @@ public:
int16_t ReadInt16();
int32_t ReadTlLength();
void ReadBytes(unsigned char* to, size_t count);
BufferInputStream GetPartBuffer(size_t length, bool advance);
private:
void EnsureEnoughRemaining(size_t need);

View File

@ -97,3 +97,8 @@ void BufferOutputStream::Reset(){
offset=0;
}
void BufferOutputStream::Rewind(size_t numBytes){
if(numBytes>offset)
throw std::out_of_range("buffer underflow");
offset-=numBytes;
}

View File

@ -25,6 +25,7 @@ public:
unsigned char* GetBuffer();
size_t GetLength();
void Reset();
void Rewind(size_t numBytes);
private:
void ExpandBufferIfNeeded(size_t need);

View File

@ -13,10 +13,9 @@ using namespace tgvoip;
BufferPool::BufferPool(unsigned int size, unsigned int count){
assert(count<=64);
init_mutex(mutex);
buffers[0]=(unsigned char*) malloc(size*count);
bufferCount=count;
int i;
unsigned int i;
for(i=1;i<count;i++){
buffers[i]=buffers[0]+i*size;
}
@ -25,31 +24,27 @@ BufferPool::BufferPool(unsigned int size, unsigned int count){
}
BufferPool::~BufferPool(){
free_mutex(mutex);
free(buffers[0]);
}
unsigned char* BufferPool::Get(){
lock_mutex(mutex);
MutexGuard m(mutex);
int i;
for(i=0;i<bufferCount;i++){
if(!((usedBuffers >> i) & 1)){
usedBuffers|=(1LL << i);
unlock_mutex(mutex);
return buffers[i];
}
}
unlock_mutex(mutex);
return NULL;
}
void BufferPool::Reuse(unsigned char* buffer){
lock_mutex(mutex);
MutexGuard m(mutex);
int i;
for(i=0;i<bufferCount;i++){
if(buffers[i]==buffer){
usedBuffers&= ~(1LL << i);
unlock_mutex(mutex);
return;
}
}

View File

@ -25,7 +25,7 @@ private:
int bufferCount;
size_t size;
unsigned char* buffers[64];
tgvoip_mutex_t mutex;
Mutex mutex;
};
}

View File

@ -30,11 +30,9 @@ CongestionControl::CongestionControl(){
inflightDataSize=0;
lossCount=0;
cwnd=(size_t) ServerConfig::GetSharedInstance()->GetInt("audio_congestion_window", 1024);
init_mutex(mutex);
}
CongestionControl::~CongestionControl(){
free_mutex(mutex);
}
size_t CongestionControl::GetAcknowledgedDataSize(){

View File

@ -64,7 +64,7 @@ private:
uint32_t tickCount;
size_t inflightDataSize;
size_t cwnd;
tgvoip_mutex_t mutex;
Mutex mutex;
};
}

View File

@ -55,7 +55,6 @@ EchoCanceller::EchoCanceller(bool enableAEC, bool enableNS, bool enableAGC){
splittingFilterFarendOut=new webrtc::IFChannelBuffer(960, 1, 3);
if(enableAEC){
init_mutex(aecMutex);
#ifndef TGVOIP_USE_DESKTOP_DSP
aec=WebRtcAecm_Create();
WebRtcAecm_Init(aec, 16000);
@ -79,7 +78,8 @@ EchoCanceller::EchoCanceller(bool enableAEC, bool enableNS, bool enableAGC){
farendBufferPool=new BufferPool(960*2, 10);
running=true;
start_thread(bufferFarendThread, EchoCanceller::StartBufferFarendThread, this);
bufferFarendThread=new Thread(new MethodPointer<EchoCanceller>(&EchoCanceller::RunBufferFarendThread, this), NULL);
bufferFarendThread->Start();
}else{
aec=NULL;
}
@ -117,7 +117,8 @@ EchoCanceller::~EchoCanceller(){
if(enableAEC){
running=false;
farendQueue->Put(NULL);
join_thread(bufferFarendThread);
bufferFarendThread->Join();
delete bufferFarendThread;
delete farendQueue;
delete farendBufferPool;
#ifndef TGVOIP_USE_DESKTOP_DSP
@ -145,10 +146,6 @@ EchoCanceller::~EchoCanceller(){
delete (webrtc::IFChannelBuffer*)splittingFilterOut;
delete (webrtc::IFChannelBuffer*)splittingFilterFarendIn;
delete (webrtc::IFChannelBuffer*)splittingFilterFarendOut;
if (this->enableAEC) {
free_mutex(aecMutex);
}
}
void EchoCanceller::Start(){
@ -175,12 +172,7 @@ void EchoCanceller::SpeakerOutCallback(unsigned char* data, size_t len){
}
}
void *EchoCanceller::StartBufferFarendThread(void *arg){
((EchoCanceller*)arg)->RunBufferFarendThread();
return NULL;
}
void EchoCanceller::RunBufferFarendThread(){
void EchoCanceller::RunBufferFarendThread(void* arg){
while(running){
int16_t* samplesIn=farendQueue->GetBlocking();
if(samplesIn){
@ -189,7 +181,7 @@ void EchoCanceller::RunBufferFarendThread(){
memcpy(bufIn->ibuf()->bands(0)[0], samplesIn, 960*2);
farendBufferPool->Reuse((unsigned char *) samplesIn);
((webrtc::SplittingFilter*)splittingFilterFarend)->Analysis(bufIn, bufOut);
lock_mutex(aecMutex);
aecMutex.Lock();
#ifndef TGVOIP_USE_DESKTOP_DSP
WebRtcAecm_BufferFarend(aec, bufOut->ibuf_const()->bands(0)[0], 160);
WebRtcAecm_BufferFarend(aec, bufOut->ibuf_const()->bands(0)[0]+160, 160);
@ -197,7 +189,7 @@ void EchoCanceller::RunBufferFarendThread(){
webrtc::WebRtcAec_BufferFarend(aec, bufOut->fbuf_const()->bands(0)[0], 160);
webrtc::WebRtcAec_BufferFarend(aec, bufOut->fbuf_const()->bands(0)[0]+160, 160);
#endif
unlock_mutex(aecMutex);
aecMutex.Unlock();
didBufferFarend=true;
}
}
@ -241,16 +233,16 @@ void EchoCanceller::ProcessInput(unsigned char* data, unsigned char* out, size_t
memcpy(bufOut->ibuf()->bands(0)[1], _nsOut[1], 320*2*2);
lock_mutex(aecMutex);
aecMutex.Lock();
WebRtcAecm_Process(aec, bufOut->ibuf()->bands(0)[0], _nsOut[0], samplesOut, AEC_FRAME_SIZE, (int16_t) tgvoip::audio::AudioOutput::GetEstimatedDelay());
WebRtcAecm_Process(aec, bufOut->ibuf()->bands(0)[0]+160, _nsOut[0]+160, samplesOut+160, AEC_FRAME_SIZE, (int16_t) (tgvoip::audio::AudioOutput::GetEstimatedDelay()+audio::AudioInput::GetEstimatedDelay()));
unlock_mutex(aecMutex);
aecMutex.Unlock();
memcpy(bufOut->ibuf()->bands(0)[0], samplesOut, 320*2);
}else if(enableAEC){
lock_mutex(aecMutex);
aecMutex.Lock();
WebRtcAecm_Process(aec, bufOut->ibuf()->bands(0)[0], NULL, samplesOut, AEC_FRAME_SIZE, (int16_t) tgvoip::audio::AudioOutput::GetEstimatedDelay());
WebRtcAecm_Process(aec, bufOut->ibuf()->bands(0)[0]+160, NULL, samplesOut+160, AEC_FRAME_SIZE, (int16_t) (tgvoip::audio::AudioOutput::GetEstimatedDelay()+audio::AudioInput::GetEstimatedDelay()));
unlock_mutex(aecMutex);
aecMutex.Unlock();
memcpy(bufOut->ibuf()->bands(0)[0], samplesOut, 320*2);
}else if(enableNS){
int16_t _nsOut[3][320];

View File

@ -30,10 +30,9 @@ private:
bool enableAGC;
bool enableNS;
#ifndef TGVOIP_NO_DSP
static void* StartBufferFarendThread(void* arg);
void RunBufferFarendThread();
void RunBufferFarendThread(void* arg);
bool didBufferFarend;
tgvoip_mutex_t aecMutex;
Mutex aecMutex;
void* aec;
void* splittingFilter; // webrtc::SplittingFilter
void* splittingFilterIn; // webrtc::IFChannelBuffer
@ -41,7 +40,7 @@ private:
void* splittingFilterFarend; // webrtc::SplittingFilter
void* splittingFilterFarendIn; // webrtc::IFChannelBuffer
void* splittingFilterFarendOut; // webrtc::IFChannelBuffer
tgvoip_thread_t bufferFarendThread;
Thread* bufferFarendThread;
BlockingQueue<int16_t*>* farendQueue;
BufferPool* farendBufferPool;
bool running;

View File

@ -40,15 +40,22 @@ JitterBuffer::JitterBuffer(MediaStreamItf *out, uint32_t step):bufferPool(JITTER
}
lossesToReset=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_losses_to_reset", 20);
resyncThreshold=ServerConfig::GetSharedInstance()->GetDouble("jitter_resync_threshold", 1.0);
//dump=fopen("/sdcard/tgvoip_jitter_dump.txt", "a");
//fprintf(dump, "==================================\n");
#ifdef TGVOIP_DUMP_JITTER_STATS
#ifdef TGVOIP_JITTER_DUMP_FILE
dump=fopen(TGVOIP_JITTER_DUMP_FILE, "w");
#elif defined(__ANDROID__)
dump=fopen("/sdcard/tgvoip_jitter_dump.txt", "w");
#else
dump=fopen("tgvoip_jitter_dump.txt", "w");
#endif
tgvoip_log_file_write_header(dump);
fprintf(dump, "PTS\tRTS\tNumInBuf\tAJitter\tADelay\tTDelay\n");
#endif
Reset();
init_mutex(mutex);
}
JitterBuffer::~JitterBuffer(){
Reset();
free_mutex(mutex);
}
void JitterBuffer::SetMinPacketCount(uint32_t count){
@ -76,9 +83,9 @@ void JitterBuffer::HandleInput(unsigned char *data, size_t len, uint32_t timesta
pkt.size=len;
pkt.buffer=data;
pkt.timestamp=timestamp;
lock_mutex(mutex);
mutex.Lock();
PutInternal(&pkt);
unlock_mutex(mutex);
mutex.Unlock();
//LOGV("in, ts=%d", timestamp);
}
@ -106,12 +113,12 @@ void JitterBuffer::Reset(){
}
size_t JitterBuffer::HandleOutput(unsigned char *buffer, size_t len, int offsetInSteps, int* playbackScaledDuration){
size_t JitterBuffer::HandleOutput(unsigned char *buffer, size_t len, int offsetInSteps, bool advance, int* playbackScaledDuration){
jitter_packet_t pkt;
pkt.buffer=buffer;
pkt.size=len;
lock_mutex(mutex);
int result=GetInternal(&pkt, offsetInSteps);
MutexGuard m(mutex);
int result=GetInternal(&pkt, offsetInSteps, advance);
if(playbackScaledDuration){
if(outstandingDelayChange!=0){
if(outstandingDelayChange<0){
@ -121,12 +128,14 @@ size_t JitterBuffer::HandleOutput(unsigned char *buffer, size_t len, int offsetI
*playbackScaledDuration=80;
outstandingDelayChange-=20;
}
LOGV("outstanding delay change: %d", outstandingDelayChange);
//LOGV("outstanding delay change: %d", outstandingDelayChange);
}else if(advance && GetCurrentDelay()==0){
//LOGV("stretching packet because the next one is late");
*playbackScaledDuration=80;
}else{
*playbackScaledDuration=60;
}
}
unlock_mutex(mutex);
if(result==JR_OK){
return pkt.size;
}else{
@ -135,7 +144,7 @@ size_t JitterBuffer::HandleOutput(unsigned char *buffer, size_t len, int offsetI
}
int JitterBuffer::GetInternal(jitter_packet_t* pkt, int offset){
int JitterBuffer::GetInternal(jitter_packet_t* pkt, int offset, bool advance){
/*if(needBuffering && lastPutTimestamp<nextTimestamp){
LOGV("jitter: don't have timestamp %lld, buffering", (long long int)nextTimestamp);
Advance();
@ -172,9 +181,9 @@ int JitterBuffer::GetInternal(jitter_packet_t* pkt, int offset){
return JR_OK;
}
LOGW("jitter: found no packet for timestamp %lld (last put = %d, lost = %d)", (long long int)timestampToGet, lastPutTimestamp, lostCount);
//LOGV("jitter: found no packet for timestamp %lld (last put = %d, lost = %d)", (long long int)timestampToGet, lastPutTimestamp, lostCount);
if(offset==0)
if(advance)
Advance();
if(!needBuffering){
@ -242,11 +251,11 @@ void JitterBuffer::PutInternal(jitter_packet_t* pkt){
}
if(pkt->timestamp<nextTimestamp){
LOGW("jitter: would drop packet with timestamp %d because it is late but not hopelessly", pkt->timestamp);
//LOGW("jitter: would drop packet with timestamp %d because it is late but not hopelessly", pkt->timestamp);
latePacketCount++;
lostPackets--;
}else if(pkt->timestamp<nextTimestamp-1){
LOGW("jitter: dropping packet with timestamp %d because it is too late", pkt->timestamp);
//LOGW("jitter: dropping packet with timestamp %d because it is too late", pkt->timestamp);
latePacketCount++;
return;
}
@ -280,7 +289,9 @@ void JitterBuffer::PutInternal(jitter_packet_t* pkt){
memcpy(slots[i].buffer, pkt->buffer, pkt->size);
else
LOGE("WTF!!");
//fprintf(dump, "%f %d\n", time-prevRecvTime, GetCurrentDelay());
#ifdef TGVOIP_DUMP_JITTER_STATS
fprintf(dump, "%u\t%.03f\t%d\t%.03f\t%.03f\t%.03f\n", pkt->timestamp, time, GetCurrentDelay(), lastMeasuredJitter, lastMeasuredDelay, minDelay);
#endif
prevRecvTime=time;
}
@ -290,8 +301,8 @@ void JitterBuffer::Advance(){
}
int JitterBuffer::GetCurrentDelay(){
int delay=0;
unsigned int JitterBuffer::GetCurrentDelay(){
unsigned int delay=0;
int i;
for(i=0;i<JITTER_SLOT_COUNT;i++){
if(slots[i].buffer!=NULL)
@ -301,11 +312,9 @@ int JitterBuffer::GetCurrentDelay(){
}
void JitterBuffer::Tick(){
lock_mutex(mutex);
MutexGuard m(mutex);
int i;
int count=0;
memmove(&lateHistory[1], lateHistory, 63*sizeof(int));
lateHistory[0]=latePacketCount;
latePacketCount=0;
@ -329,26 +338,6 @@ void JitterBuffer::Tick(){
if(avgLate16>=resyncThreshold){
wasReset=true;
}
/*if(avgLate16>=0.3){
if(dontIncMinDelay==0 && minDelay<15){
minDelay++;
if(GetCurrentDelay()<minDelay)
nextTimestamp-=(minDelay-GetCurrentDelay());
dontIncMinDelay=16;
dontDecMinDelay+=128;
}
}else if(absolutelyNoLatePackets){
if(dontDecMinDelay>0)
dontDecMinDelay--;
if(dontDecMinDelay==0 && minDelay>minMinDelay){
minDelay--;
dontDecMinDelay=64;
dontIncMinDelay+=16;
}
}
if(dontIncMinDelay>0)
dontIncMinDelay--;*/
if(absolutelyNoLatePackets){
if(dontDecMinDelay>0)
@ -444,7 +433,6 @@ void JitterBuffer::Tick(){
tickCount++;
unlock_mutex(mutex);
}
@ -468,10 +456,9 @@ void JitterBuffer::GetAverageLateCount(double *out){
int JitterBuffer::GetAndResetLostPacketCount(){
lock_mutex(mutex);
MutexGuard m(mutex);
int r=lostPackets;
lostPackets=0;
unlock_mutex(mutex);
return r;
}

View File

@ -36,11 +36,11 @@ public:
~JitterBuffer();
void SetMinPacketCount(uint32_t count);
int GetMinPacketCount();
int GetCurrentDelay();
unsigned int GetCurrentDelay();
double GetAverageDelay();
void Reset();
void HandleInput(unsigned char* data, size_t len, uint32_t timestamp);
size_t HandleOutput(unsigned char* buffer, size_t len, int offsetInSteps, int* playbackScaledDuration);
size_t HandleOutput(unsigned char* buffer, size_t len, int offsetInSteps, bool advance, int* playbackScaledDuration);
void Tick();
void GetAverageLateCount(double* out);
int GetAndResetLostPacketCount();
@ -51,24 +51,24 @@ private:
static size_t CallbackIn(unsigned char* data, size_t len, void* param);
static size_t CallbackOut(unsigned char* data, size_t len, void* param);
void PutInternal(jitter_packet_t* pkt);
int GetInternal(jitter_packet_t* pkt, int offset);
int GetInternal(jitter_packet_t* pkt, int offset, bool advance);
void Advance();
BufferPool bufferPool;
tgvoip_mutex_t mutex;
Mutex mutex;
jitter_packet_t slots[JITTER_SLOT_COUNT];
int64_t nextTimestamp;
uint32_t step;
uint32_t minDelay;
double minDelay;
uint32_t minMinDelay;
uint32_t maxMinDelay;
uint32_t maxUsedSlots;
uint32_t lastPutTimestamp;
uint32_t lossesToReset;
double resyncThreshold;
int lostCount;
int lostSinceReset;
int gotSinceReset;
unsigned int lostCount;
unsigned int lostSinceReset;
unsigned int gotSinceReset;
bool wasReset;
bool needBuffering;
int delayHistory[64];
@ -88,7 +88,9 @@ private:
int outstandingDelayChange;
unsigned int dontChangeDelay;
double avgDelay;
//FILE* dump;
#ifdef TGVOIP_DUMP_JITTER_STATS
FILE* dump;
#endif
};
}

View File

@ -4,7 +4,13 @@
// you should have received with this source code distribution.
//
#include "logging.h"
#include "MediaStreamItf.h"
#include "EchoCanceller.h"
#include <stdint.h>
#include <algorithm>
#include <math.h>
#include <assert.h>
using namespace tgvoip;
@ -16,3 +22,190 @@ void MediaStreamItf::SetCallback(size_t (*f)(unsigned char *, size_t, void*), vo
size_t MediaStreamItf::InvokeCallback(unsigned char *data, size_t length){
return (*callback)(data, length, callbackParam);
}
AudioMixer::AudioMixer() : bufferPool(960*2, 16), processedQueue(16), semaphore(16, 0){
output=NULL;
running=false;
}
AudioMixer::~AudioMixer(){
}
void AudioMixer::SetOutput(MediaStreamItf *output){
if(this->output)
this->output->SetCallback(NULL, NULL);
this->output=output;
output->SetCallback(OutputCallback, this);
}
void AudioMixer::Start(){
assert(!running);
running=true;
thread=new Thread(new MethodPointer<AudioMixer>(&AudioMixer::RunThread, this), NULL);
thread->Start();
thread->SetName("AudioMixer");
}
void AudioMixer::Stop(){
if(!running){
LOGE("Tried to stop AudioMixer that wasn't started");
return;
}
running=false;
semaphore.Release();
thread->Join();
delete thread;
thread=NULL;
}
void AudioMixer::DoCallback(unsigned char *data, size_t length){
//memset(data, 0, 960*2);
//LOGD("audio mixer callback, %d inputs", inputs.size());
if(processedQueue.Size()==0)
semaphore.Release(2);
else
semaphore.Release();
unsigned char* buf=processedQueue.GetBlocking();
memcpy(data, buf, 960*2);
bufferPool.Reuse(buf);
}
size_t AudioMixer::OutputCallback(unsigned char *data, size_t length, void *arg){
((AudioMixer*)arg)->DoCallback(data, length);
return 960*2;
}
void AudioMixer::AddInput(MediaStreamItf *input){
MutexGuard m(inputsMutex);
MixerInput in;
in.multiplier=1;
in.source=input;
inputs.push_back(in);
}
void AudioMixer::RemoveInput(MediaStreamItf *input){
MutexGuard m(inputsMutex);
for(std::vector<MixerInput>::iterator i=inputs.begin();i!=inputs.end();++i){
if(i->source==input){
inputs.erase(i);
return;
}
}
}
void AudioMixer::SetInputVolume(MediaStreamItf *input, float volumeDB){
MutexGuard m(inputsMutex);
for(std::vector<MixerInput>::iterator i=inputs.begin();i!=inputs.end();++i){
if(i->source==input){
if(volumeDB==-INFINITY)
i->multiplier=0;
else
i->multiplier=expf(volumeDB/20.0f * logf(10.0f));
return;
}
}
}
void AudioMixer::RunThread(void* arg){
LOGV("AudioMixer thread started");
while(running){
semaphore.Acquire();
if(!running)
break;
unsigned char* data=bufferPool.Get();
//LOGV("Audio mixer processing a frame");
if(!data){
LOGE("AudioMixer: no buffers left");
continue;
}
MutexGuard m(inputsMutex);
int16_t* buf=reinterpret_cast<int16_t*>(data);
int16_t input[960];
float out[960];
memset(out, 0, 960*4);
int usedInputs=0;
for(std::vector<MixerInput>::iterator in=inputs.begin();in!=inputs.end();++in){
size_t res=in->source->InvokeCallback(reinterpret_cast<unsigned char*>(input), 960*2);
if(!res || in->multiplier==0){
//LOGV("AudioMixer: skipping silent packet");
continue;
}
usedInputs++;
float k=in->multiplier;
if(k!=1){
for(size_t i=0; i<960; i++){
out[i]+=(float)input[i]*k;
}
}else{
for(size_t i=0;i<960;i++){
out[i]+=(float)input[i];
}
}
}
if(usedInputs>0){
for(size_t i=0; i<960; i++){
if(out[i]>32767.0f)
buf[i]=INT16_MAX;
else if(out[i]<-32768.0f)
buf[i]=INT16_MIN;
else
buf[i]=(int16_t)out[i];
}
}else{
memset(data, 0, 960*2);
}
if(echoCanceller)
echoCanceller->SpeakerOutCallback(data, 960*2);
processedQueue.Put(data);
}
LOGI("======== audio mixer thread exiting =========");
}
void AudioMixer::SetEchoCanceller(EchoCanceller *aec){
echoCanceller=aec;
}
AudioLevelMeter::AudioLevelMeter(){
absMax=0;
count=0;
currentLevel=0;
currentLevelFullRange=0;
}
float AudioLevelMeter::GetLevel(){
return currentLevel/9.0f;
}
void AudioLevelMeter::Update(int16_t *samples, size_t count){
// Number of bars on the indicator.
// Note that the number of elements is specified because we are indexing it
// in the range of 0-32
const int8_t permutation[33]={0,1,2,3,4,4,5,5,5,5,6,6,6,6,6,7,7,7,7,8,8,8,9,9,9,9,9,9,9,9,9,9,9};
int16_t absValue=0;
for(unsigned int k=0;k<count;k++){
int16_t absolute=(int16_t)abs(samples[k]);
if (absolute>absValue)
absValue=absolute;
}
if(absValue>absMax)
absMax = absValue;
// Update level approximately 10 times per second
if (this->count++==10){
currentLevelFullRange=absMax;
this->count=0;
// Highest value for a int16_t is 0x7fff = 32767
// Divide with 1000 to get in the range of 0-32 which is the range of
// the permutation vector
int32_t position=absMax/1000;
// Make it less likely that the bar stays at position 0. I.e. only if
// its in the range 0-250 (instead of 0-1000)
/*if ((position==0) && (absMax>250)){
position=1;
}*/
currentLevel=permutation[position];
// Decay the absolute maximum (divide by 4)
absMax >>= 2;
}
}

View File

@ -8,8 +8,16 @@
#define LIBTGVOIP_MEDIASTREAMINPUT_H
#include <string.h>
#include <vector>
#include <stdint.h>
#include "threading.h"
#include "BlockingQueue.h"
#include "BufferPool.h"
namespace tgvoip{
class EchoCanceller;
class MediaStreamItf{
public:
virtual void Start()=0;
@ -23,7 +31,58 @@ private:
size_t (*callback)(unsigned char*, size_t, void*);
void* callbackParam;
};
}
class AudioMixer : public MediaStreamItf{
public:
AudioMixer();
virtual ~AudioMixer();
void SetOutput(MediaStreamItf* output);
virtual void Start();
virtual void Stop();
void AddInput(MediaStreamItf* input);
void RemoveInput(MediaStreamItf* input);
void SetInputVolume(MediaStreamItf* input, float volumeDB);
void SetEchoCanceller(EchoCanceller* aec);
private:
void RunThread(void* arg);
struct MixerInput{
MediaStreamItf* source;
float multiplier;
};
Mutex inputsMutex;
void DoCallback(unsigned char* data, size_t length);
static size_t OutputCallback(unsigned char* data, size_t length, void* arg);
MediaStreamItf* output;
std::vector<MixerInput> inputs;
Thread* thread;
BufferPool bufferPool;
BlockingQueue<unsigned char*> processedQueue;
Semaphore semaphore;
EchoCanceller* echoCanceller;
bool running;
};
class CallbackWrapper : public MediaStreamItf{
public:
CallbackWrapper(){};
virtual ~CallbackWrapper(){};
virtual void Start(){};
virtual void Stop(){};
};
class AudioLevelMeter{
public:
AudioLevelMeter();
float GetLevel();
void Update(int16_t* samples, size_t count);
private:
int16_t absMax;
int16_t count;
int8_t currentLevel;
int16_t currentLevelFullRange;
};
};
#endif //LIBTGVOIP_MEDIASTREAMINPUT_H

View File

@ -110,7 +110,7 @@ void NetworkSocket::EncryptForTCPO2(unsigned char *buffer, size_t len, TCPO2Stat
}
size_t NetworkSocket::Receive(unsigned char *buffer, size_t len){
NetworkPacket pkt;
NetworkPacket pkt={0};
pkt.data=buffer;
pkt.length=len;
Receive(&pkt);
@ -118,7 +118,7 @@ size_t NetworkSocket::Receive(unsigned char *buffer, size_t len){
}
size_t NetworkSocket::Send(unsigned char *buffer, size_t len){
NetworkPacket pkt;
NetworkPacket pkt={0};
pkt.data=buffer;
pkt.length=len;
Send(&pkt);
@ -361,7 +361,7 @@ void NetworkSocketSOCKS5Proxy::Send(NetworkPacket *packet){
}
out.WriteInt16(htons(packet->port));
out.WriteBytes(packet->data, packet->length);
NetworkPacket p;
NetworkPacket p={0};
p.data=buf;
p.length=out.GetLength();
p.address=connectedAddress;
@ -376,7 +376,7 @@ void NetworkSocketSOCKS5Proxy::Receive(NetworkPacket *packet){
tcp->Receive(packet);
}else if(protocol==PROTO_UDP){
unsigned char buf[1500];
NetworkPacket p;
NetworkPacket p={0};
p.data=buf;
p.length=sizeof(buf);
udp->Receive(&p);
@ -587,6 +587,7 @@ void NetworkSocketSOCKS5Proxy::InitConnection(){
failed=true;
return;
}
LOGV("socks5: authentication succeeded");
}else{
LOGW("socks5: unsupported auth method");
failed=true;

View File

@ -10,32 +10,49 @@
#include <assert.h>
#include <algorithm>
#include "VoIPController.h"
#define PACKET_SIZE (960*2)
using namespace tgvoip;
tgvoip::OpusDecoder::OpusDecoder(MediaStreamItf *dst) : semaphore(32, 0){
//this->source=source;
tgvoip::OpusDecoder::OpusDecoder(MediaStreamItf *dst, bool isAsync){
async=isAsync;
dst->SetCallback(OpusDecoder::Callback, this);
if(async){
decodedQueue=new BlockingQueue<unsigned char*>(33);
bufferPool=new BufferPool(PACKET_SIZE, 32);
semaphore=new Semaphore(32, 0);
}else{
decodedQueue=NULL;
bufferPool=NULL;
semaphore=NULL;
}
dec=opus_decoder_create(48000, 1, NULL);
//test=fopen("/sdcard/test.raw", "wb");
buffer=(unsigned char *) malloc(8192);
//lastDecoded=(unsigned char*) malloc(960*2);
lastDecoded=NULL;
lastDecodedLen=0;
outputBufferSize=0;
lastDecodedOffset=0;
decodedQueue=new BlockingQueue<unsigned char*>(33);
bufferPool=new BufferPool(PACKET_SIZE, 32);
echoCanceller=NULL;
frameDuration=20;
consecutiveLostPackets=0;
enableDTX=false;
silentPacketCount=0;
levelMeter=NULL;
nextLen=0;
running=false;
remainingDataLen=0;
processedBuffer=NULL;
}
tgvoip::OpusDecoder::~OpusDecoder(){
opus_decoder_destroy(dec);
free(buffer);
delete bufferPool;
delete decodedQueue;
if(bufferPool)
delete bufferPool;
if(decodedQueue)
delete decodedQueue;
if(semaphore)
delete semaphore;
}
@ -44,215 +61,177 @@ void tgvoip::OpusDecoder::SetEchoCanceller(EchoCanceller* canceller){
}
size_t tgvoip::OpusDecoder::Callback(unsigned char *data, size_t len, void *param){
((OpusDecoder*)param)->HandleCallback(data, len);
return 0;
return ((OpusDecoder*)param)->HandleCallback(data, len);
}
void tgvoip::OpusDecoder::HandleCallback(unsigned char *data, size_t len){
if(!running){
memset(data, 0, len);
return;
}
if(outputBufferSize==0){
outputBufferSize=len;
int packetsNeeded;
if(len>PACKET_SIZE)
packetsNeeded=len/PACKET_SIZE;
else
packetsNeeded=1;
packetsNeeded*=2;
semaphore.Release(packetsNeeded);
}
assert(outputBufferSize==len && "output buffer size is supposed to be the same throughout callbacks");
if(len>PACKET_SIZE){
int count=len/PACKET_SIZE;
int i;
for(i=0;i<count;i++){
lastDecoded=(unsigned char*) decodedQueue->GetBlocking();
if(!lastDecoded)
return;
memcpy(data+(i*PACKET_SIZE), lastDecoded, PACKET_SIZE);
if(echoCanceller)
echoCanceller->SpeakerOutCallback(data, PACKET_SIZE);
bufferPool->Reuse(lastDecoded);
size_t tgvoip::OpusDecoder::HandleCallback(unsigned char *data, size_t len){
if(async){
if(!running){
memset(data, 0, len);
return 0;
}
semaphore.Release(count);
}else if(len==PACKET_SIZE){
lastDecoded=(unsigned char*) decodedQueue->GetBlocking();
if(!lastDecoded)
return;
memcpy(data, lastDecoded, PACKET_SIZE);
bufferPool->Reuse(lastDecoded);
semaphore.Release();
lock_mutex(mutex);
if(echoCanceller)
echoCanceller->SpeakerOutCallback(data, PACKET_SIZE);
unlock_mutex(mutex);
}else if(len<PACKET_SIZE){
if(lastDecodedOffset==0){
lastDecoded=(unsigned char*) decodedQueue->GetBlocking();
}
if(!lastDecoded)
return;
memcpy(data, lastDecoded+lastDecodedOffset, len);
lastDecodedOffset+=len;
if(lastDecodedOffset>=PACKET_SIZE){
if(echoCanceller)
echoCanceller->SpeakerOutCallback(lastDecoded, PACKET_SIZE);
lastDecodedOffset=0;
bufferPool->Reuse(lastDecoded);
//LOGV("before req packet, qsize=%d", decodedQueue->Size());
if(decodedQueue->Size()==0)
semaphore.Release(2);
if(outputBufferSize==0){
outputBufferSize=len;
int packetsNeeded;
if(len>PACKET_SIZE)
packetsNeeded=len/PACKET_SIZE;
else
semaphore.Release();
}
}
/*if(lastDecodedLen){
LOGV("ldl=%d, l=%d", lastDecodedLen, len);
if(len==PACKET_SIZE){
memcpy(data, lastDecoded, len);
packetsNeeded=1;
}else if(len>PACKET_SIZE){
memcpy(data, lastDecoded, len);
//LOGV("ldl=%d, l=%d", lastDecodedLen, len);
packetsNeeded=len/PACKET_SIZE;
}else if(len<PACKET_SIZE){
memcpy(data, lastDecoded+lastDecodedOffset, len);
lastDecodedOffset+=len;
if(lastDecodedOffset>=PACKET_SIZE){
packetsNeeded=1;
lastDecodedOffset=0;
packetsNeeded*=2;
semaphore->Release(packetsNeeded);
}
assert(outputBufferSize==len && "output buffer size is supposed to be the same throughout callbacks");
if(len==PACKET_SIZE){
lastDecoded=(unsigned char *) decodedQueue->GetBlocking();
if(!lastDecoded)
return 0;
memcpy(data, lastDecoded, PACKET_SIZE);
bufferPool->Reuse(lastDecoded);
semaphore->Release();
if(silentPacketCount>0){
silentPacketCount--;
if(levelMeter)
levelMeter->Update(reinterpret_cast<int16_t *>(data), 0);
return 0;
}
if(echoCanceller){
echoCanceller->SpeakerOutCallback(data, PACKET_SIZE);
}
}else{
LOGE("Opus decoder buffer length != 960 samples");
abort();
}
}else{
LOGW("skipping callback");
if(len>PACKET_SIZE)
packetsNeeded=len/PACKET_SIZE;
else
packetsNeeded=1;
}*/
/*if(packetsNeeded>0){
lock_mutex(mutex);
notify_lock(lock);
unlock_mutex(mutex);
}*/
if(remainingDataLen==0 && silentPacketCount==0){
int duration=DecodeNextFrame();
remainingDataLen=(size_t) (duration/20*960*2);
}
if(silentPacketCount>0 || remainingDataLen==0 || !processedBuffer){
if(silentPacketCount>0)
silentPacketCount--;
memset(data, 0, 960*2);
if(levelMeter)
levelMeter->Update(reinterpret_cast<int16_t *>(data), 0);
return 0;
}
memcpy(data, processedBuffer, 960*2);
remainingDataLen-=960*2;
if(remainingDataLen>0){
memmove(processedBuffer, processedBuffer+960*2, remainingDataLen);
}
}
if(levelMeter)
levelMeter->Update(reinterpret_cast<int16_t *>(data), len/2);
return len;
}
void tgvoip::OpusDecoder::Start(){
init_mutex(mutex);
if(!async)
return;
running=true;
start_thread(thread, OpusDecoder::StartThread, this);
set_thread_priority(thread, get_thread_max_priority());
set_thread_name(thread, "opus_decoder");
thread=new Thread(new MethodPointer<tgvoip::OpusDecoder>(&tgvoip::OpusDecoder::RunThread, this), NULL);
thread->SetName("opus_decoder");
thread->SetMaxPriority();
thread->Start();
}
void tgvoip::OpusDecoder::Stop(){
if(!running)
if(!running || !async)
return;
running=false;
semaphore.Release();
join_thread(thread);
free_mutex(mutex);
semaphore->Release();
thread->Join();
delete thread;
}
void* tgvoip::OpusDecoder::StartThread(void *param){
((tgvoip::OpusDecoder*)param)->RunThread();
return NULL;
}
void tgvoip::OpusDecoder::RunThread(){
unsigned char nextBuffer[8192];
unsigned char decodeBuffer[8192];
void tgvoip::OpusDecoder::RunThread(void* param){
int i;
int packetsPerFrame=frameDuration/20;
bool first=true;
LOGI("decoder: packets per frame %d", packetsPerFrame);
size_t nextLen=0;
while(running){
//LOGV("after wait, running=%d", running);
//LOGD("Will get %d packets", packetsNeeded);
//lastDecodedLen=0;
memcpy(buffer, nextBuffer, nextLen);
size_t inLen=nextLen;
//nextLen=InvokeCallback(nextBuffer, 8192);
int playbackDuration=0;
nextLen=jitterBuffer->HandleOutput(nextBuffer, 8192, 0, &playbackDuration);
if(first){
first=false;
continue;
}
//LOGV("Before decode, len=%d", inLen);
if(!inLen){
LOGV("Trying to recover late packet");
inLen=jitterBuffer->HandleOutput(buffer, 8192, -2, &playbackDuration);
if(inLen)
LOGV("Decoding late packet");
}
int size;
if(inLen || nextLen)
size=opus_decode(dec, inLen ? buffer : nextBuffer, inLen ? inLen : nextLen, (opus_int16*) decodeBuffer, packetsPerFrame*960, inLen ? 0 : 1);
else{ // do packet loss concealment
size=opus_decode(dec, NULL, 0, (opus_int16 *) decodeBuffer, packetsPerFrame*960, 0);
LOGV("PLC");
}
if(size<0)
LOGW("decoder: opus_decode error %d", size);
//LOGV("After decode, size=%d", size);
//LOGD("playbackDuration=%d", playbackDuration);
unsigned char* processedBuffer;
if(playbackDuration==80){
processedBuffer=buffer;
audio::Resampler::Rescale60To80((int16_t*) decodeBuffer, (int16_t*) processedBuffer);
}else if(playbackDuration==40){
processedBuffer=buffer;
audio::Resampler::Rescale60To40((int16_t*) decodeBuffer, (int16_t*) processedBuffer);
}else{
processedBuffer=decodeBuffer;
}
for(i=0;i</*packetsPerFrame*/ playbackDuration/20;i++){
semaphore.Acquire();
int playbackDuration=DecodeNextFrame();
for(i=0;i<playbackDuration/20;i++){
semaphore->Acquire();
if(!running){
LOGI("==== decoder exiting ====");
return;
}
unsigned char *buf=bufferPool->Get();
if(buf){
if(size>0){
if(remainingDataLen>0){
for(std::vector<AudioEffect*>::iterator effect=postProcEffects.begin();effect!=postProcEffects.end();++effect){
(*effect)->Process(reinterpret_cast<int16_t*>(processedBuffer+(PACKET_SIZE*i)), 960);
}
memcpy(buf, processedBuffer+(PACKET_SIZE*i), PACKET_SIZE);
}else{
LOGE("Error decoding, result=%d", size);
//LOGE("Error decoding, result=%d", size);
memset(buf, 0, PACKET_SIZE);
}
decodedQueue->Put(buf);
}else{
LOGW("decoder: no buffers left!");
}
//LOGD("packets needed: %d", packetsNeeded);
}
}
}
int tgvoip::OpusDecoder::DecodeNextFrame(){
/*memcpy(buffer, nextBuffer, nextLen);
size_t inLen=nextLen;
int playbackDuration=0;
nextLen=jitterBuffer->HandleOutput(nextBuffer, 8192, 0, &playbackDuration);
if(first){
first=false;
return 0;
}
if(!inLen){
LOGV("Trying to recover late packet");
inLen=jitterBuffer->HandleOutput(buffer, 8192, -2, &playbackDuration);
if(inLen)
LOGV("Decoding late packet");
}*/
int playbackDuration=0;
size_t len=jitterBuffer->HandleOutput(buffer, 8192, 0, true, &playbackDuration);
bool fec=false;
if(!len){
fec=true;
len=jitterBuffer->HandleOutput(buffer, 8192, 0, false, &playbackDuration);
if(len)
LOGV("Trying FEC...");
}
int size;
if(len){
size=opus_decode(dec, buffer, len, (opus_int16 *) decodeBuffer, packetsPerFrame*960, fec ? 1 : 0);
consecutiveLostPackets=0;
}else{ // do packet loss concealment
consecutiveLostPackets++;
if(consecutiveLostPackets>2 && enableDTX){
silentPacketCount+=packetsPerFrame;
size=packetsPerFrame*960;
}else{
size=opus_decode(dec, NULL, 0, (opus_int16 *) decodeBuffer, packetsPerFrame*960, 0);
//LOGV("PLC");
}
}
if(size<0)
LOGW("decoder: opus_decode error %d", size);
remainingDataLen=size;
if(playbackDuration==80){
processedBuffer=buffer;
audio::Resampler::Rescale60To80((int16_t*) decodeBuffer, (int16_t*) processedBuffer);
}else if(playbackDuration==40){
processedBuffer=buffer;
audio::Resampler::Rescale60To40((int16_t*) decodeBuffer, (int16_t*) processedBuffer);
}else{
processedBuffer=decodeBuffer;
}
return playbackDuration;
}
void tgvoip::OpusDecoder::SetFrameDuration(uint32_t duration){
frameDuration=duration;
}
void tgvoip::OpusDecoder::ResetQueue(){
/*lock_mutex(mutex);
packetsNeeded=0;
unlock_mutex(mutex);
while(decodedQueue->Size()>0){
bufferPool->Reuse((unsigned char *) decodedQueue->Get());
}*/
packetsPerFrame=frameDuration/20;
}
@ -260,6 +239,14 @@ void tgvoip::OpusDecoder::SetJitterBuffer(JitterBuffer* jitterBuffer){
this->jitterBuffer=jitterBuffer;
}
void tgvoip::OpusDecoder::SetDTX(bool enable){
enableDTX=enable;
}
void tgvoip::OpusDecoder::SetLevelMeter(AudioLevelMeter *levelMeter){
this->levelMeter=levelMeter;
}
void tgvoip::OpusDecoder::AddAudioEffect(AudioEffect *effect){
postProcEffects.push_back(effect);
}

View File

@ -25,35 +25,46 @@ public:
virtual void Stop();
OpusDecoder(MediaStreamItf* dst);
OpusDecoder(MediaStreamItf* dst, bool isAsync);
virtual ~OpusDecoder();
void HandleCallback(unsigned char* data, size_t len);
size_t HandleCallback(unsigned char* data, size_t len);
void SetEchoCanceller(EchoCanceller* canceller);
void SetFrameDuration(uint32_t duration);
void ResetQueue();
void SetJitterBuffer(JitterBuffer* jitterBuffer);
void SetDTX(bool enable);
void SetLevelMeter(AudioLevelMeter* levelMeter);
void AddAudioEffect(AudioEffect* effect);
void RemoveAudioEffect(AudioEffect* effect);
private:
static size_t Callback(unsigned char* data, size_t len, void* param);
static void* StartThread(void* param);
void RunThread();
void RunThread(void* param);
int DecodeNextFrame();
::OpusDecoder* dec;
BlockingQueue<unsigned char*>* decodedQueue;
BufferPool* bufferPool;
unsigned char* buffer;
unsigned char* lastDecoded;
size_t lastDecodedLen, lastDecodedOffset;
unsigned char* processedBuffer;
size_t outputBufferSize;
bool running;
tgvoip_thread_t thread;
Semaphore semaphore;
tgvoip_mutex_t mutex;
Thread* thread;
Semaphore* semaphore;
uint32_t frameDuration;
EchoCanceller* echoCanceller;
JitterBuffer* jitterBuffer;
AudioLevelMeter* levelMeter;
int consecutiveLostPackets;
bool enableDTX;
size_t silentPacketCount;
std::vector<AudioEffect*> postProcEffects;
bool async;
unsigned char nextBuffer[8192];
unsigned char decodeBuffer[8192];
bool first;
size_t nextLen;
unsigned int packetsPerFrame;
ssize_t remainingDataLen;
};
}

View File

@ -24,6 +24,7 @@ tgvoip::OpusEncoder::OpusEncoder(MediaStreamItf *source):queue(11), bufferPool(9
echoCanceller=NULL;
complexity=10;
frameDuration=20;
levelMeter=NULL;
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);
@ -38,9 +39,10 @@ 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");
thread=new Thread(new MethodPointer<tgvoip::OpusEncoder>(&tgvoip::OpusEncoder::RunThread, this), NULL);
thread->SetName("OpusEncoder");
thread->Start();
thread->SetMaxPriority();
}
void tgvoip::OpusEncoder::Stop(){
@ -48,7 +50,8 @@ void tgvoip::OpusEncoder::Stop(){
return;
running=false;
queue.Put(NULL);
join_thread(thread);
thread->Join();
delete thread;
}
@ -62,6 +65,8 @@ void tgvoip::OpusEncoder::Encode(unsigned char *data, size_t len){
currentBitrate=requestedBitrate;
LOGV("opus_encoder: setting bitrate to %u", currentBitrate);
}
if(levelMeter)
levelMeter->Update(reinterpret_cast<int16_t *>(data), len/2);
int32_t r=opus_encode(enc, (int16_t*)data, len/2, buffer, 4096);
if(r<=0){
LOGE("Error encoding: %d", r);
@ -99,12 +104,7 @@ void tgvoip::OpusEncoder::SetEchoCanceller(EchoCanceller* aec){
echoCanceller=aec;
}
void* tgvoip::OpusEncoder::StartThread(void* arg){
((OpusEncoder*)arg)->RunThread();
return NULL;
}
void tgvoip::OpusEncoder::RunThread(){
void tgvoip::OpusEncoder::RunThread(void* arg){
unsigned char buf[960*2];
uint32_t bufferedCount=0;
uint32_t packetsPerFrame=frameDuration/20;
@ -158,3 +158,11 @@ void tgvoip::OpusEncoder::SetPacketLoss(int percent){
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;
}

View File

@ -30,18 +30,19 @@ public:
void SetPacketLoss(int percent);
int GetPacketLoss();
uint32_t GetBitrate();
void SetDTX(bool enable);
void SetLevelMeter(AudioLevelMeter* levelMeter);
private:
static size_t Callback(unsigned char* data, size_t len, void* param);
static void* StartThread(void* arg);
void RunThread();
void RunThread(void* arg);
void Encode(unsigned char* data, size_t len);
MediaStreamItf* source;
::OpusEncoder* enc;
unsigned char buffer[4096];
uint32_t requestedBitrate;
uint32_t currentBitrate;
tgvoip_thread_t thread;
Thread* thread;
BlockingQueue<unsigned char*> queue;
BufferPool bufferPool;
EchoCanceller* echoCanceller;
@ -53,6 +54,7 @@ private:
uint32_t strongCorrectionBitrate;
double mediumCorrectionMultiplier;
double strongCorrectionMultiplier;
AudioLevelMeter* levelMeter;
};
}

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@
#endif
#ifdef __APPLE__
#include <TargetConditionals.h>
#include "os/darwin/AudioUnitIO.h"
#endif
#include <stdint.h>
#include <vector>
@ -28,55 +29,15 @@
#include "EchoCanceller.h"
#include "CongestionControl.h"
#include "NetworkSocket.h"
#include "BufferInputStream.h"
#define LIBTGVOIP_VERSION "1.0.3"
#define STATE_WAIT_INIT 1
#define STATE_WAIT_INIT_ACK 2
#define STATE_ESTABLISHED 3
#define STATE_FAILED 4
#define STATE_RECONNECTING 5
#define TGVOIP_ERROR_UNKNOWN 0
#define TGVOIP_ERROR_INCOMPATIBLE 1
#define TGVOIP_ERROR_TIMEOUT 2
#define TGVOIP_ERROR_AUDIO_IO 3
#define NET_TYPE_UNKNOWN 0
#define NET_TYPE_GPRS 1
#define NET_TYPE_EDGE 2
#define NET_TYPE_3G 3
#define NET_TYPE_HSPA 4
#define NET_TYPE_LTE 5
#define NET_TYPE_WIFI 6
#define NET_TYPE_ETHERNET 7
#define NET_TYPE_OTHER_HIGH_SPEED 8
#define NET_TYPE_OTHER_LOW_SPEED 9
#define NET_TYPE_DIALUP 10
#define NET_TYPE_OTHER_MOBILE 11
#define EP_TYPE_UDP_P2P_INET 1
#define EP_TYPE_UDP_P2P_LAN 2
#define EP_TYPE_UDP_RELAY 3
#define EP_TYPE_TCP_RELAY 4
#define DATA_SAVING_NEVER 0
#define DATA_SAVING_MOBILE 1
#define DATA_SAVING_ALWAYS 2
#define LIBTGVOIP_VERSION "2.0-alpha4"
#ifdef _WIN32
#undef GetCurrentTime
#endif
struct voip_stream_t{
int32_t userID;
unsigned char id;
unsigned char type;
unsigned char codec;
bool enabled;
uint16_t frameDuration;
};
typedef struct voip_stream_t voip_stream_t;
#define TGVOIP_PEER_CAP_GROUP_CALLS 1
struct voip_queued_packet_t{
unsigned char type;
@ -100,21 +61,11 @@ struct voip_config_t{
bool enableAEC;
bool enableNS;
bool enableAGC;
bool enableCallUpgrade;
};
typedef struct voip_config_t voip_config_t;
#if defined(__APPLE__) && TARGET_OS_IPHONE
// temporary fix for nasty linking errors
struct voip_legacy_endpoint_t{
const char* address;
const char* address6;
uint16_t port;
int64_t id;
unsigned char peerTag[16];
};
typedef struct voip_legacy_endpoint_t voip_legacy_endpoint_t;
#endif
struct voip_stats_t{
uint64_t bytesSentWifi;
uint64_t bytesRecvdWifi;
@ -130,6 +81,8 @@ struct voip_crypto_functions_t{
void (*aes_ige_encrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv);
void (*aes_ige_decrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv);
void (*aes_ctr_encrypt)(uint8_t* inout, size_t length, uint8_t* key, uint8_t* iv, uint8_t* ecount, uint32_t* num);
void (*aes_cbc_encrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv);
void (*aes_cbc_decrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv);
};
typedef struct voip_crypto_functions_t voip_crypto_functions_t;
@ -147,193 +100,229 @@ namespace tgvoip{
//PROXY_HTTP
};
class Endpoint{
friend class VoIPController;
public:
Endpoint(int64_t id, uint16_t port, IPv4Address& address, IPv6Address& v6address, char type, unsigned char* peerTag);
Endpoint();
int64_t id;
uint16_t port;
IPv4Address address;
IPv6Address v6address;
char type;
unsigned char peerTag[16];
enum{
STATE_WAIT_INIT=1,
STATE_WAIT_INIT_ACK,
STATE_ESTABLISHED,
STATE_FAILED,
STATE_RECONNECTING
};
private:
double lastPingTime;
uint32_t lastPingSeq;
double rtts[6];
double averageRTT;
NetworkSocket* socket;
};
enum{
ERROR_UNKNOWN=0,
ERROR_INCOMPATIBLE,
ERROR_TIMEOUT,
ERROR_AUDIO_IO
};
class AudioDevice{
public:
std::string id;
std::string displayName;
};
enum{
NET_TYPE_UNKNOWN=0,
NET_TYPE_GPRS,
NET_TYPE_EDGE,
NET_TYPE_3G,
NET_TYPE_HSPA,
NET_TYPE_LTE,
NET_TYPE_WIFI,
NET_TYPE_ETHERNET,
NET_TYPE_OTHER_HIGH_SPEED,
NET_TYPE_OTHER_LOW_SPEED,
NET_TYPE_DIALUP,
NET_TYPE_OTHER_MOBILE
};
class AudioOutputDevice : public AudioDevice{
enum{
DATA_SAVING_NEVER=0,
DATA_SAVING_MOBILE,
DATA_SAVING_ALWAYS
};
};
class Endpoint{
friend class VoIPController;
friend class VoIPGroupController;
public:
class AudioInputDevice : public AudioDevice{
enum{
TYPE_UDP_P2P_INET=1,
TYPE_UDP_P2P_LAN,
TYPE_UDP_RELAY,
TYPE_TCP_RELAY
};
};
Endpoint(int64_t id, uint16_t port, IPv4Address& address, IPv6Address& v6address, char type, unsigned char* peerTag);
Endpoint();
int64_t id;
uint16_t port;
IPv4Address address;
IPv6Address v6address;
char type;
unsigned char peerTag[16];
class VoIPController
{
public:
VoIPController();
~VoIPController();
private:
double lastPingTime;
uint32_t lastPingSeq;
double rtts[6];
double averageRTT;
NetworkSocket* socket;
int udpPongCount;
};
/**
* Set the initial endpoints (relays)
* @param endpoints Endpoints converted from phone.PhoneConnection TL objects
* @param allowP2p Whether p2p connectivity is allowed
*/
void SetRemoteEndpoints(std::vector<Endpoint> endpoints, bool allowP2p);
/**
* Initialize and start all the internal threads
*/
void Start();
/**
* Initiate connection
*/
void Connect();
Endpoint& GetRemoteEndpoint();
/**
* Get the debug info string to be displayed in client UI
* @param buffer The buffer to put the string into
* @param len The length of the buffer
*/
void GetDebugString(char* buffer, size_t len);
/**
* Notify the library of network type change
* @param type The new network type
*/
void SetNetworkType(int type);
/**
* Get the average round-trip time for network packets
* @return
*/
double GetAverageRTT();
/**
* Set the function to be called whenever the connection state changes
* @param f
*/
void SetStateCallback(void (*f)(VoIPController*, int));
static double GetCurrentTime();
/**
* Use this field to store any of your context data associated with this call
*/
void* implData;
/**
*
* @param mute
*/
void SetMicMute(bool mute);
/**
*
* @param key
* @param isOutgoing
*/
void SetEncryptionKey(char* key, bool isOutgoing);
/**
*
* @param cfg
*/
void SetConfig(voip_config_t* cfg);
float GetOutputLevel();
void DebugCtl(int request, int param);
/**
*
* @param stats
*/
void GetStats(voip_stats_t* stats);
/**
*
* @return
*/
int64_t GetPreferredRelayID();
/**
*
* @return
*/
int GetLastError();
/**
*
*/
static voip_crypto_functions_t crypto;
/**
*
* @return
*/
static const char* GetVersion();
#ifdef TGVOIP_USE_AUDIO_SESSION
void SetAcquireAudioSession(void (^)(void (^)()));
void ReleaseAudioSession(void (^completion)());
#endif
class AudioDevice{
public:
std::string id;
std::string displayName;
};
class AudioOutputDevice : public AudioDevice{
};
class AudioInputDevice : public AudioDevice{
};
class VoIPController{
friend class VoIPGroupController;
public:
VoIPController();
virtual ~VoIPController();
/**
* Set the initial endpoints (relays)
* @param endpoints Endpoints converted from phone.PhoneConnection TL objects
* @param allowP2p Whether p2p connectivity is allowed
*/
void SetRemoteEndpoints(std::vector<Endpoint> endpoints, bool allowP2p, int32_t connectionMaxLayer);
/**
* Initialize and start all the internal threads
*/
void Start();
/**
* Stop any internal threads. Don't call any other methods after this.
*/
void Stop();
/**
* Initiate connection
*/
void Connect();
Endpoint& GetRemoteEndpoint();
/**
* Get the debug info string to be displayed in client UI
* @param buffer The buffer to put the string into
* @param len The length of the buffer
*/
virtual void GetDebugString(char* buffer, size_t len);
/**
* Notify the library of network type change
* @param type The new network type
*/
virtual void SetNetworkType(int type);
/**
* Get the average round-trip time for network packets
* @return
*/
double GetAverageRTT();
static double GetCurrentTime();
/**
* Use this field to store any of your context data associated with this call
*/
void* implData;
/**
*
* @param mute
*/
virtual void SetMicMute(bool mute);
/**
*
* @param key
* @param isOutgoing
*/
void SetEncryptionKey(char* key, bool isOutgoing);
/**
*
* @param cfg
*/
void SetConfig(voip_config_t* cfg);
float GetOutputLevel();
void DebugCtl(int request, int param);
/**
*
* @param stats
*/
void GetStats(voip_stats_t* stats);
/**
*
* @return
*/
std::string GetDebugLog();
int64_t GetPreferredRelayID();
/**
*
* @return
*/
int GetLastError();
/**
*
*/
static voip_crypto_functions_t crypto;
/**
*
* @return
*/
static const char* GetVersion();
/**
*
* @return
*/
std::string GetDebugLog();
/**
*
* @param buffer
*/
void GetDebugLog(char* buffer);
size_t GetDebugLogLength();
void GetDebugLog(char* buffer);
size_t GetDebugLogLength();
/**
*
* @return
*/
static std::vector<AudioInputDevice> EnumerateAudioInputs();
static std::vector<AudioInputDevice> EnumerateAudioInputs();
/**
*
* @return
*/
static std::vector<AudioOutputDevice> EnumerateAudioOutputs();
static std::vector<AudioOutputDevice> EnumerateAudioOutputs();
/**
*
* @param id
*/
void SetCurrentAudioInput(std::string id);
void SetCurrentAudioInput(std::string id);
/**
*
* @param id
*/
void SetCurrentAudioOutput(std::string id);
void SetCurrentAudioOutput(std::string id);
/**
*
* @return
*/
std::string GetCurrentAudioInputID();
std::string GetCurrentAudioInputID();
/**
*
* @return
*/
std::string GetCurrentAudioOutputID();
/**
* Set the proxy server to route the data through. Call this before connecting.
* @param protocol PROXY_NONE or PROXY_SOCKS5
* @param address IP address or domain name of the server
* @param port Port of the server
* @param username Username; empty string for anonymous
* @param password Password; empty string if none
*/
void SetProxy(int protocol, std::string address, uint16_t port, std::string username, std::string password);
std::string GetCurrentAudioOutputID();
/**
* Set the proxy server to route the data through. Call this before connecting.
* @param protocol PROXY_NONE or PROXY_SOCKS5
* @param address IP address or domain name of the server
* @param port Port of the server
* @param username Username; empty string for anonymous
* @param password Password; empty string if none
*/
void SetProxy(int protocol, std::string address, uint16_t port, std::string username, std::string password);
/**
* Get the number of signal bars to display in the client UI.
* @return the number of signal bars, from 1 to 4
*/
int GetSignalBarsCount();
/**
* Set the callback to be called when the signal bar count changes.
* @param f
*/
void SetSignalBarsCountCallback(void (*f)(VoIPController*, int));
/**
* Enable or disable AGC (automatic gain control) on audio output. Should only be enabled on phones when the earpiece speaker is being used.
* The audio output will be louder with this on.
@ -341,182 +330,292 @@ public:
* @param enabled I usually pick argument names to be self-explanatory
*/
void SetAudioOutputGainControlEnabled(bool enabled);
/**
* Get the additional capabilities of the peer client app
* @return corresponding TGVOIP_PEER_CAP_* flags OR'ed together
*/
uint32_t GetPeerCapabilities();
/**
* Send the peer the key for the group call to prepare this private call to an upgrade to a E2E group call.
* The peer must have the TGVOIP_PEER_CAP_GROUP_CALLS capability. After the peer acknowledges the key, Callbacks::groupCallKeySent will be called.
* @param key newly-generated group call key, must be exactly 265 bytes long
*/
void SendGroupCallKey(unsigned char* key);
/**
* In an incoming call, request the peer to generate a new encryption key, send it to you and upgrade this call to a E2E group call.
*/
void RequestCallUpgrade();
void SetEchoCancellationStrength(int strength);
private:
struct PendingOutgoingPacket{
struct Callbacks{
void (*connectionStateChanged)(VoIPController*, int);
void (*signalBarCountChanged)(VoIPController*, int);
void (*groupCallKeySent)(VoIPController*);
void (*groupCallKeyReceived)(VoIPController*, unsigned char*);
void (*upgradeToGroupCallRequested)(VoIPController*);
};
void SetCallbacks(Callbacks callbacks);
protected:
struct RecentOutgoingPacket{
uint32_t seq;
uint16_t id; // for group calls only
double sendTime;
double ackTime;
};
struct PendingOutgoingPacket{
uint32_t seq;
unsigned char type;
size_t len;
unsigned char* data;
Endpoint* endpoint;
};
virtual void ProcessIncomingPacket(NetworkPacket& packet, Endpoint* srcEndpoint);
virtual void WritePacketHeader(uint32_t seq, BufferOutputStream* s, unsigned char type, uint32_t length);
virtual void SendPacket(unsigned char* data, size_t len, Endpoint* ep, PendingOutgoingPacket& srcPacket);
virtual void SendInit();
virtual void SendUdpPing(Endpoint* endpoint);
virtual void SendRelayPings();
virtual void OnAudioOutputReady();
private:
struct Stream{
int32_t userID;
unsigned char id;
unsigned char type;
uint32_t codec;
bool enabled;
uint16_t frameDuration;
JitterBuffer* jitterBuffer;
OpusDecoder* decoder;
CallbackWrapper* callbackWrapper;
};
enum{
UDP_UNKNOWN=0,
UDP_PING_SENT,
UDP_AVAILABLE,
UDP_NOT_AVAILABLE,
UDP_BAD
};
void RunRecvThread(void* arg);
void RunSendThread(void* arg);
void RunTickThread(void* arg);
void HandleAudioInput(unsigned char* data, size_t len);
void UpdateAudioBitrate();
void SetState(int state);
void UpdateAudioOutputState();
void InitUDPProxy();
void UpdateDataSavingState();
void KDF(unsigned char* msgKey, size_t x, unsigned char* aesKey, unsigned char* aesIv);
void KDF2(unsigned char* msgKey, size_t x, unsigned char* aesKey, unsigned char* aesIv);
static size_t AudioInputCallback(unsigned char* data, size_t length, void* param);
void SendPublicEndpointsRequest();
void SendPublicEndpointsRequest(Endpoint& relay);
Endpoint* GetEndpointByType(int type);
void SendPacketReliably(unsigned char type, unsigned char* data, size_t len, double retryInterval, double timeout);
uint32_t GenerateOutSeq();
void LogDebugInfo();
void ActuallySendPacket(NetworkPacket& pkt, Endpoint* ep);
void StartAudio();
int state;
std::vector<Endpoint*> endpoints;
Endpoint* currentEndpoint;
Endpoint* preferredRelay;
Endpoint* peerPreferredRelay;
bool runReceiver;
uint32_t seq;
unsigned char type;
size_t len;
unsigned char* data;
Endpoint* endpoint;
};
enum{
UDP_UNKNOWN=0,
UDP_PING_SENT,
UDP_AVAILABIE,
UDP_NOT_AVAILABLE
};
uint32_t lastRemoteSeq;
uint32_t lastRemoteAckSeq;
uint32_t lastSentSeq;
std::vector<RecentOutgoingPacket> recentOutgoingPackets;
double recvPacketTimes[32];
uint32_t sendLossCountHistory[32];
uint32_t audioTimestampIn;
uint32_t audioTimestampOut;
tgvoip::audio::AudioInput* audioInput;
tgvoip::audio::AudioOutput* audioOutput;
OpusEncoder* encoder;
BlockingQueue<PendingOutgoingPacket>* sendQueue;
EchoCanceller* echoCanceller;
Mutex sendBufferMutex;
Mutex endpointsMutex;
bool stopping;
bool audioOutStarted;
Thread* recvThread;
Thread* sendThread;
Thread* tickThread;
uint32_t packetsRecieved;
uint32_t recvLossCount;
uint32_t prevSendLossCount;
uint32_t firstSentPing;
double rttHistory[32];
bool waitingForAcks;
int networkType;
int dontSendPackets;
int lastError;
bool micMuted;
uint32_t maxBitrate;
std::vector<Stream> outgoingStreams;
std::vector<Stream> incomingStreams;
unsigned char encryptionKey[256];
unsigned char keyFingerprint[8];
unsigned char callID[16];
double stateChangeTime;
bool waitingForRelayPeerInfo;
bool allowP2p;
bool dataSavingMode;
bool dataSavingRequestedByPeer;
std::string activeNetItfName;
double publicEndpointsReqTime;
std::vector<voip_queued_packet_t*> queuedPackets;
Mutex queuedPacketsMutex;
double connectionInitTime;
double lastRecvPacketTime;
voip_config_t config;
int32_t peerVersion;
CongestionControl* conctl;
voip_stats_t stats;
bool receivedInit;
bool receivedInitAck;
std::vector<std::string> debugLogs;
bool isOutgoing;
NetworkSocket* udpSocket;
NetworkSocket* realUdpSocket;
FILE* statsDump;
std::string currentAudioInput;
std::string currentAudioOutput;
bool useTCP;
bool useUDP;
bool didAddTcpRelays;
double setEstablishedAt;
SocketSelectCanceller* selectCanceller;
NetworkSocket* openingTcpSocket;
unsigned char signalBarsHistory[4];
static void* StartRecvThread(void* arg);
static void* StartSendThread(void* arg);
static void* StartTickThread(void* arg);
void RunRecvThread();
void RunSendThread();
void RunTickThread();
void SendPacket(unsigned char* data, size_t len, Endpoint* ep);
void HandleAudioInput(unsigned char* data, size_t len);
void UpdateAudioBitrate();
void SetState(int state);
void UpdateAudioOutputState();
void SendInit();
void InitUDPProxy();
void UpdateDataSavingState();
void KDF(unsigned char* msgKey, size_t x, unsigned char* aesKey, unsigned char* aesIv);
void WritePacketHeader(uint32_t seq, BufferOutputStream* s, unsigned char type, uint32_t length);
static size_t AudioInputCallback(unsigned char* data, size_t length, void* param);
void SendPublicEndpointsRequest();
void SendPublicEndpointsRequest(Endpoint& relay);
Endpoint* GetEndpointByType(int type);
void SendPacketReliably(unsigned char type, unsigned char* data, size_t len, double retryInterval, double timeout);
uint32_t GenerateOutSeq();
void LogDebugInfo();
void SendUdpPing(Endpoint* endpoint);
int state;
std::vector<Endpoint*> endpoints;
Endpoint* currentEndpoint;
Endpoint* preferredRelay;
Endpoint* peerPreferredRelay;
bool runReceiver;
uint32_t seq;
uint32_t lastRemoteSeq;
uint32_t lastRemoteAckSeq;
uint32_t lastSentSeq;
double remoteAcks[32];
double sentPacketTimes[32];
double recvPacketTimes[32];
uint32_t sendLossCountHistory[32];
uint32_t audioTimestampIn;
uint32_t audioTimestampOut;
tgvoip::audio::AudioInput* audioInput;
tgvoip::audio::AudioOutput* audioOutput;
JitterBuffer* jitterBuffer;
OpusDecoder* decoder;
OpusEncoder* encoder;
BlockingQueue<PendingOutgoingPacket>* sendQueue;
EchoCanceller* echoCanceller;
tgvoip_mutex_t sendBufferMutex;
tgvoip_mutex_t endpointsMutex;
bool stopping;
bool audioOutStarted;
tgvoip_thread_t recvThread;
tgvoip_thread_t sendThread;
tgvoip_thread_t tickThread;
uint32_t packetsRecieved;
uint32_t recvLossCount;
uint32_t prevSendLossCount;
uint32_t firstSentPing;
double rttHistory[32];
bool waitingForAcks;
int networkType;
int dontSendPackets;
int lastError;
bool micMuted;
uint32_t maxBitrate;
void (*stateCallback)(VoIPController*, int);
std::vector<voip_stream_t*> outgoingStreams;
std::vector<voip_stream_t*> incomingStreams;
unsigned char encryptionKey[256];
unsigned char keyFingerprint[8];
unsigned char callID[16];
double stateChangeTime;
bool waitingForRelayPeerInfo;
bool allowP2p;
bool dataSavingMode;
bool dataSavingRequestedByPeer;
std::string activeNetItfName;
double publicEndpointsReqTime;
std::vector<voip_queued_packet_t*> queuedPackets;
tgvoip_mutex_t queuedPacketsMutex;
double connectionInitTime;
double lastRecvPacketTime;
voip_config_t config;
int32_t peerVersion;
CongestionControl* conctl;
voip_stats_t stats;
bool receivedInit;
bool receivedInitAck;
std::vector<std::string> debugLogs;
bool isOutgoing;
NetworkSocket* udpSocket;
NetworkSocket* realUdpSocket;
FILE* statsDump;
std::string currentAudioInput;
std::string currentAudioOutput;
bool useTCP;
bool useUDP;
bool didAddTcpRelays;
double setEstablishedAt;
SocketSelectCanceller* selectCanceller;
NetworkSocket* openingTcpSocket;
unsigned char signalBarsHistory[4];
BufferPool outgoingPacketsBufferPool;
int udpConnectivityState;
double lastUdpPingTime;
int udpPingCount;
int echoCancellationStrength;
BufferPool outgoingPacketsBufferPool;
int udpConnectivityState;
double lastUdpPingTime;
int udpPingCount;
int echoCancellationStrength;
int proxyProtocol;
std::string proxyAddress;
uint16_t proxyPort;
std::string proxyUsername;
std::string proxyPassword;
IPv4Address* resolvedProxyAddress;
int proxyProtocol;
std::string proxyAddress;
uint16_t proxyPort;
std::string proxyUsername;
std::string proxyPassword;
IPv4Address* resolvedProxyAddress;
int signalBarCount;
void (*signalBarCountCallback)(VoIPController*, int);
AutomaticGainControl* outputAGC;
bool outputAGCEnabled;
/*** server config values ***/
uint32_t maxAudioBitrate;
uint32_t maxAudioBitrateEDGE;
uint32_t maxAudioBitrateGPRS;
uint32_t maxAudioBitrateSaving;
uint32_t initAudioBitrate;
uint32_t initAudioBitrateEDGE;
uint32_t initAudioBitrateGPRS;
uint32_t initAudioBitrateSaving;
uint32_t minAudioBitrate;
uint32_t audioBitrateStepIncr;
uint32_t audioBitrateStepDecr;
double relaySwitchThreshold;
double p2pToRelaySwitchThreshold;
double relayToP2pSwitchThreshold;
double reconnectingTimeout;
uint32_t peerCapabilities;
Callbacks callbacks;
bool didReceiveGroupCallKey;
bool didReceiveGroupCallKeyAck;
bool didSendGroupCallKey;
bool didSendUpgradeRequest;
bool didInvokeUpdateCallback;
#ifdef TGVOIP_USE_AUDIO_SESSION
void (^acquireAudioSession)(void (^)());
bool needNotifyAcquiredAudioSession;
#endif
int32_t connectionMaxLayer;
bool useMTProto2;
bool setCurrentEndpointToTCP;
public:
/*** server config values ***/
uint32_t maxAudioBitrate;
uint32_t maxAudioBitrateEDGE;
uint32_t maxAudioBitrateGPRS;
uint32_t maxAudioBitrateSaving;
uint32_t initAudioBitrate;
uint32_t initAudioBitrateEDGE;
uint32_t initAudioBitrateGPRS;
uint32_t initAudioBitrateSaving;
uint32_t minAudioBitrate;
uint32_t audioBitrateStepIncr;
uint32_t audioBitrateStepDecr;
double relaySwitchThreshold;
double p2pToRelaySwitchThreshold;
double relayToP2pSwitchThreshold;
double reconnectingTimeout;
/*** platform-specific things **/
#ifdef __APPLE__
static double machTimebase;
static uint64_t machTimestart;
#if TARGET_OS_IPHONE
// temporary fix for nasty linking errors
void SetRemoteEndpoints(voip_legacy_endpoint_t* buffer, size_t count, bool allowP2P);
audio::AudioUnitIO* appleAudioIO;
#endif
public:
#ifdef __APPLE__
static double machTimebase;
static uint64_t machTimestart;
#endif
#ifdef _WIN32
static int64_t win32TimeScale;
static bool didInitWin32TimeScale;
static int64_t win32TimeScale;
static bool didInitWin32TimeScale;
#endif
};
class VoIPGroupController : public VoIPController{
public:
VoIPGroupController(int32_t timeDifference);
virtual ~VoIPGroupController();
void SetGroupCallInfo(unsigned char* encryptionKey, unsigned char* reflectorGroupTag, unsigned char* reflectorSelfTag, unsigned char* reflectorSelfSecret, unsigned char* reflectorSelfTagHash, int32_t selfUserID, IPv4Address reflectorAddress, IPv6Address reflectorAddressV6, uint16_t reflectorPort);
void AddGroupCallParticipant(int32_t userID, unsigned char* memberTagHash, unsigned char* serializedStreams, size_t streamsLength);
void RemoveGroupCallParticipant(int32_t userID);
float GetParticipantAudioLevel(int32_t userID);
virtual void SetMicMute(bool mute);
void SetParticipantVolume(int32_t userID, float volume);
void SetParticipantStreams(int32_t userID, unsigned char* serializedStreams, size_t length);
static size_t GetInitialStreams(unsigned char* buf, size_t size);
struct Callbacks : public VoIPController::Callbacks{
void (*updateStreams)(VoIPGroupController*, unsigned char*, size_t);
void (*participantAudioStateChanged)(VoIPGroupController*, int32_t, bool);
};
void SetCallbacks(Callbacks callbacks);
virtual void GetDebugString(char* buffer, size_t length);
virtual void SetNetworkType(int type);
protected:
virtual void ProcessIncomingPacket(NetworkPacket& packet, Endpoint* srcEndpoint);
virtual void SendInit();
virtual void SendUdpPing(Endpoint* endpoint);
virtual void SendRelayPings();
virtual void SendPacket(unsigned char* data, size_t len, Endpoint* ep, PendingOutgoingPacket& srcPacket);
virtual void WritePacketHeader(uint32_t seq, BufferOutputStream* s, unsigned char type, uint32_t length);
virtual void OnAudioOutputReady();
private:
int32_t GetCurrentUnixtime();
std::vector<Stream> DeserializeStreams(BufferInputStream& in);
void SendRecentPacketsRequest();
void SendSpecialReflectorRequest(unsigned char* data, size_t len);
void SerializeAndUpdateOutgoingStreams();
struct GroupCallParticipant{
int32_t userID;
unsigned char memberTagHash[32];
std::vector<Stream> streams;
AudioLevelMeter* levelMeter;
};
std::vector<GroupCallParticipant> participants;
unsigned char reflectorSelfTag[16];
unsigned char reflectorSelfSecret[16];
unsigned char reflectorSelfTagHash[32];
int32_t userSelfID;
Endpoint* groupReflector;
AudioMixer* audioMixer;
AudioLevelMeter selfLevelMeter;
Callbacks groupCallbacks;
struct PacketIdMapping{
uint32_t seq;
uint16_t id;
double ackTime;
};
std::vector<PacketIdMapping> recentSentPackets;
Mutex sentPacketsMutex;
Mutex participantsMutex;
int32_t timeDifference;
};
};
}
#endif

View File

@ -13,11 +13,9 @@ using namespace tgvoip;
ServerConfig* ServerConfig::sharedInstance=NULL;
ServerConfig::ServerConfig(){
init_mutex(mutex);
}
ServerConfig::~ServerConfig(){
free_mutex(mutex);
}
ServerConfig *ServerConfig::GetSharedInstance(){

View File

@ -30,7 +30,7 @@ private:
static ServerConfig* sharedInstance;
bool ContainsKey(std::string key);
std::map<std::string, std::string> config;
tgvoip_mutex_t mutex;
Mutex mutex;
};
}

View File

@ -39,7 +39,7 @@ AudioInput::AudioInput(std::string deviceID) : currentDevice(deviceID){
failed=false;
}
AudioInput *AudioInput::Create(std::string deviceID){
AudioInput *AudioInput::Create(std::string deviceID, void* platformSpecific){
#if defined(__ANDROID__)
return new AudioInputAndroid();
#elif defined(__APPLE__)
@ -47,7 +47,7 @@ AudioInput *AudioInput::Create(std::string deviceID){
if(kCFCoreFoundationVersionNumber<kCFCoreFoundationVersionNumber10_7)
return new AudioInputAudioUnitLegacy(deviceID);
#endif
return new AudioInputAudioUnit(deviceID);
return new AudioInputAudioUnit(deviceID, reinterpret_cast<AudioUnitIO*>(platformSpecific));
#elif defined(_WIN32)
#ifdef TGVOIP_WINXP_COMPAT
if(LOBYTE(LOWORD(GetVersion()))<6)

View File

@ -28,7 +28,7 @@ public:
bool IsInitialized();
virtual std::string GetCurrentDevice();
virtual void SetCurrentDevice(std::string deviceID);
static AudioInput* Create(std::string deviceID);
static AudioInput* Create(std::string deviceID, void* platformSpecific);
static void EnumerateDevices(std::vector<AudioInputDevice>& devs);
static int32_t GetEstimatedDelay();

View File

@ -6,9 +6,11 @@
#include "AudioOutput.h"
#include "../logging.h"
#include <stdlib.h>
#if defined(__ANDROID__)
#include "../os/android/AudioOutputOpenSLES.h"
#include "../os/android/AudioOutputAndroid.h"
#include <sys/system_properties.h>
#elif defined(__APPLE__)
#include <TargetConditionals.h>
#include "../os/darwin/AudioOutputAudioUnit.h"
@ -30,13 +32,13 @@
using namespace tgvoip;
using namespace tgvoip::audio;
#if defined(__ANDROID__)
int AudioOutput::systemVersion;
#endif
int32_t AudioOutput::estimatedDelay=60;
AudioOutput *AudioOutput::Create(std::string deviceID){
AudioOutput *AudioOutput::Create(std::string deviceID, void* platformSpecific){
#if defined(__ANDROID__)
char sdkNum[PROP_VALUE_MAX];
__system_property_get("ro.build.version.sdk", sdkNum);
int systemVersion=atoi(sdkNum);
//if(systemVersion<21)
return new AudioOutputAndroid();
//return new AudioOutputOpenSLES();
@ -45,7 +47,7 @@ AudioOutput *AudioOutput::Create(std::string deviceID){
if(kCFCoreFoundationVersionNumber<kCFCoreFoundationVersionNumber10_7)
return new AudioOutputAudioUnitLegacy(deviceID);
#endif
return new AudioOutputAudioUnit(deviceID);
return new AudioOutputAudioUnit(deviceID, reinterpret_cast<AudioUnitIO*>(platformSpecific));
#elif defined(_WIN32)
#ifdef TGVOIP_WINXP_COMPAT
if(LOBYTE(LOWORD(GetVersion()))<6)
@ -80,6 +82,9 @@ AudioOutput::~AudioOutput(){
int32_t AudioOutput::GetEstimatedDelay(){
#if defined(__ANDROID__)
char sdkNum[PROP_VALUE_MAX];
__system_property_get("ro.build.version.sdk", sdkNum);
int systemVersion=atoi(sdkNum);
return systemVersion<21 ? 150 : 50;
#endif
return estimatedDelay;

View File

@ -29,14 +29,10 @@ public:
static int32_t GetEstimatedDelay();
virtual std::string GetCurrentDevice();
virtual void SetCurrentDevice(std::string deviceID);
static AudioOutput* Create(std::string deviceID);
static AudioOutput* Create(std::string deviceID, void* platformSpecific);
static void EnumerateDevices(std::vector<AudioOutputDevice>& devs);
bool IsInitialized();
#if defined(__ANDROID__)
static int systemVersion;
#endif
protected:
std::string currentDevice;
bool failed;

View File

@ -22,6 +22,12 @@ jfieldID audioRecordInstanceFld=NULL;
jfieldID audioTrackInstanceFld=NULL;
jmethodID setStateMethod=NULL;
jmethodID setSignalBarsMethod=NULL;
jmethodID setSelfStreamsMethod=NULL;
jmethodID setParticipantAudioEnabledMethod=NULL;
jmethodID groupCallKeyReceivedMethod=NULL;
jmethodID groupCallKeySentMethod=NULL;
jmethodID callUpgradeRequestReceivedMethod=NULL;
jclass jniUtilitiesClass=NULL;
struct impl_data_android_t{
jobject javaObject;
@ -30,48 +36,164 @@ struct impl_data_android_t{
using namespace tgvoip;
using namespace tgvoip::audio;
void updateConnectionState(VoIPController* cntrlr, int state){
impl_data_android_t* impl=(impl_data_android_t*) cntrlr->implData;
if(!impl->javaObject)
return;
JNIEnv* env=NULL;
bool didAttach=false;
sharedJVM->GetEnv((void**) &env, JNI_VERSION_1_6);
if(!env){
sharedJVM->AttachCurrentThread(&env, NULL);
didAttach=true;
namespace tgvoip {
void updateConnectionState(VoIPController *cntrlr, int state){
impl_data_android_t *impl=(impl_data_android_t *) cntrlr->implData;
if(!impl->javaObject)
return;
JNIEnv *env=NULL;
bool didAttach=false;
sharedJVM->GetEnv((void **) &env, JNI_VERSION_1_6);
if(!env){
sharedJVM->AttachCurrentThread(&env, NULL);
didAttach=true;
}
if(setStateMethod)
env->CallVoidMethod(impl->javaObject, setStateMethod, state);
if(didAttach){
sharedJVM->DetachCurrentThread();
}
}
if(setStateMethod)
env->CallVoidMethod(impl->javaObject, setStateMethod, state);
void updateSignalBarCount(VoIPController *cntrlr, int count){
impl_data_android_t *impl=(impl_data_android_t *) cntrlr->implData;
if(!impl->javaObject)
return;
JNIEnv *env=NULL;
bool didAttach=false;
sharedJVM->GetEnv((void **) &env, JNI_VERSION_1_6);
if(!env){
sharedJVM->AttachCurrentThread(&env, NULL);
didAttach=true;
}
if(didAttach){
sharedJVM->DetachCurrentThread();
}
}
if(setSignalBarsMethod)
env->CallVoidMethod(impl->javaObject, setSignalBarsMethod, count);
void updateSignalBarCount(VoIPController* cntrlr, int count){
impl_data_android_t* impl=(impl_data_android_t*) cntrlr->implData;
if(!impl->javaObject)
return;
JNIEnv* env=NULL;
bool didAttach=false;
sharedJVM->GetEnv((void**) &env, JNI_VERSION_1_6);
if(!env){
sharedJVM->AttachCurrentThread(&env, NULL);
didAttach=true;
if(didAttach){
sharedJVM->DetachCurrentThread();
}
}
if(setSignalBarsMethod)
env->CallVoidMethod(impl->javaObject, setSignalBarsMethod, count);
void updateGroupCallStreams(VoIPGroupController *cntrlr, unsigned char *streams, size_t len){
impl_data_android_t *impl=(impl_data_android_t *) cntrlr->implData;
if(!impl->javaObject)
return;
JNIEnv *env=NULL;
bool didAttach=false;
sharedJVM->GetEnv((void **) &env, JNI_VERSION_1_6);
if(!env){
sharedJVM->AttachCurrentThread(&env, NULL);
didAttach=true;
}
if(didAttach){
sharedJVM->DetachCurrentThread();
if(setSelfStreamsMethod){
jbyteArray jstreams=env->NewByteArray(len);
jbyte *el=env->GetByteArrayElements(jstreams, NULL);
memcpy(el, streams, len);
env->ReleaseByteArrayElements(jstreams, el, 0);
env->CallVoidMethod(impl->javaObject, setSelfStreamsMethod, jstreams);
}
if(didAttach){
sharedJVM->DetachCurrentThread();
}
}
void groupCallKeyReceived(VoIPController *cntrlr, unsigned char *key){
impl_data_android_t *impl=(impl_data_android_t *) cntrlr->implData;
if(!impl->javaObject)
return;
JNIEnv *env=NULL;
bool didAttach=false;
sharedJVM->GetEnv((void **) &env, JNI_VERSION_1_6);
if(!env){
sharedJVM->AttachCurrentThread(&env, NULL);
didAttach=true;
}
if(groupCallKeyReceivedMethod){
jbyteArray jkey=env->NewByteArray(256);
jbyte *el=env->GetByteArrayElements(jkey, NULL);
memcpy(el, key, 256);
env->ReleaseByteArrayElements(jkey, el, 0);
env->CallVoidMethod(impl->javaObject, groupCallKeyReceivedMethod, jkey);
}
if(didAttach){
sharedJVM->DetachCurrentThread();
}
}
void groupCallKeySent(VoIPController *cntrlr){
impl_data_android_t *impl=(impl_data_android_t *) cntrlr->implData;
if(!impl->javaObject)
return;
JNIEnv *env=NULL;
bool didAttach=false;
sharedJVM->GetEnv((void **) &env, JNI_VERSION_1_6);
if(!env){
sharedJVM->AttachCurrentThread(&env, NULL);
didAttach=true;
}
if(groupCallKeySentMethod){
env->CallVoidMethod(impl->javaObject, groupCallKeySentMethod);
}
if(didAttach){
sharedJVM->DetachCurrentThread();
}
}
void callUpgradeRequestReceived(VoIPController* cntrlr){
impl_data_android_t *impl=(impl_data_android_t *) cntrlr->implData;
if(!impl->javaObject)
return;
JNIEnv *env=NULL;
bool didAttach=false;
sharedJVM->GetEnv((void **) &env, JNI_VERSION_1_6);
if(!env){
sharedJVM->AttachCurrentThread(&env, NULL);
didAttach=true;
}
if(groupCallKeySentMethod){
env->CallVoidMethod(impl->javaObject, callUpgradeRequestReceivedMethod);
}
if(didAttach){
sharedJVM->DetachCurrentThread();
}
}
void updateParticipantAudioState(VoIPGroupController *cntrlr, int32_t userID, bool enabled){
impl_data_android_t *impl=(impl_data_android_t *) cntrlr->implData;
if(!impl->javaObject)
return;
JNIEnv *env=NULL;
bool didAttach=false;
sharedJVM->GetEnv((void **) &env, JNI_VERSION_1_6);
if(!env){
sharedJVM->AttachCurrentThread(&env, NULL);
didAttach=true;
}
if(setParticipantAudioEnabledMethod){
env->CallVoidMethod(impl->javaObject, setParticipantAudioEnabledMethod, userID, enabled);
}
if(didAttach){
sharedJVM->DetachCurrentThread();
}
}
}
extern "C" JNIEXPORT jlong Java_org_telegram_messenger_voip_VoIPController_nativeInit(JNIEnv* env, jobject thiz, jint systemVersion){
AudioOutputAndroid::systemVersion=systemVersion;
env->GetJavaVM(&sharedJVM);
if(!AudioInputAndroid::jniClass){
@ -90,15 +212,27 @@ extern "C" JNIEXPORT jlong Java_org_telegram_messenger_voip_VoIPController_nativ
AudioOutputAndroid::stopMethod=env->GetMethodID(cls, "stop", "()V");
}
setStateMethod=env->GetMethodID(env->GetObjectClass(thiz), "handleStateChange", "(I)V");
setSignalBarsMethod=env->GetMethodID(env->GetObjectClass(thiz), "handleSignalBarsChange", "(I)V");
jclass thisClass=env->FindClass("org/telegram/messenger/voip/VoIPController");
setStateMethod=env->GetMethodID(thisClass, "handleStateChange", "(I)V");
setSignalBarsMethod=env->GetMethodID(thisClass, "handleSignalBarsChange", "(I)V");
groupCallKeyReceivedMethod=env->GetMethodID(thisClass, "groupCallKeyReceived", "([B)V");
groupCallKeySentMethod=env->GetMethodID(thisClass, "groupCallKeySent", "()V");
callUpgradeRequestReceivedMethod=env->GetMethodID(thisClass, "callUpgradeRequestReceived", "()V");
if(!jniUtilitiesClass)
jniUtilitiesClass=(jclass) env->NewGlobalRef(env->FindClass("org/telegram/messenger/voip/JNIUtilities"));
impl_data_android_t* impl=(impl_data_android_t*) malloc(sizeof(impl_data_android_t));
impl->javaObject=env->NewGlobalRef(thiz);
VoIPController* cntrlr=new VoIPController();
cntrlr->implData=impl;
cntrlr->SetStateCallback(updateConnectionState);
cntrlr->SetSignalBarsCountCallback(updateSignalBarCount);
VoIPController::Callbacks callbacks;
callbacks.connectionStateChanged=updateConnectionState;
callbacks.signalBarCountChanged=updateSignalBarCount;
callbacks.groupCallKeyReceived=groupCallKeyReceived;
callbacks.groupCallKeySent=groupCallKeySent;
callbacks.upgradeToGroupCallRequested=callUpgradeRequestReceived;
cntrlr->SetCallbacks(callbacks);
return (jlong)(intptr_t)cntrlr;
}
@ -128,7 +262,7 @@ extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_native
env->ReleaseByteArrayElements(key, akey, JNI_ABORT);
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeSetRemoteEndpoints(JNIEnv* env, jobject thiz, jlong inst, jobjectArray endpoints, jboolean allowP2p){
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeSetRemoteEndpoints(JNIEnv* env, jobject thiz, jlong inst, jobjectArray endpoints, jboolean allowP2p, jboolean tcp, jint connectionMaxLayer){
size_t len=(size_t) env->GetArrayLength(endpoints);
// voip_endpoint_t* eps=(voip_endpoint_t *) malloc(sizeof(voip_endpoint_t)*len);
std::vector<Endpoint> eps;
@ -166,23 +300,25 @@ extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_native
memcpy(pTag, peerTagBytes, 16);
env->ReleaseByteArrayElements(peerTag, peerTagBytes, JNI_ABORT);
}
eps.push_back(Endpoint((int64_t)id, (uint16_t)port, v4addr, v6addr, EP_TYPE_UDP_RELAY, pTag));
eps.push_back(Endpoint((int64_t)id, (uint16_t)port, v4addr, v6addr, (char) (tcp ? Endpoint::TYPE_TCP_RELAY : Endpoint::TYPE_UDP_RELAY), pTag));
}
((VoIPController*)(intptr_t)inst)->SetRemoteEndpoints(eps, allowP2p);
((VoIPController*)(intptr_t)inst)->SetRemoteEndpoints(eps, allowP2p, connectionMaxLayer);
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeSetNativeBufferSize(JNIEnv* env, jclass thiz, jint size){
AudioOutputOpenSLES::nativeBufferSize=size;
AudioInputOpenSLES::nativeBufferSize=size;
AudioOutputOpenSLES::nativeBufferSize=(unsigned int) size;
AudioInputOpenSLES::nativeBufferSize=(unsigned int) size;
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeRelease(JNIEnv* env, jobject thiz, jlong inst){
//env->DeleteGlobalRef(AudioInputAndroid::jniClass);
VoIPController* ctlr=((VoIPController*)(intptr_t)inst);
impl_data_android_t* impl=(impl_data_android_t*)ctlr->implData;
jobject jobj=impl->javaObject;
ctlr->Stop();
delete ctlr;
env->DeleteGlobalRef(impl->javaObject);
free(impl);
env->DeleteGlobalRef(jobj);
}
@ -226,6 +362,7 @@ extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_native
cfg.enableAEC=enableAEC;
cfg.enableNS=enableNS;
cfg.enableAGC=enableAGC;
cfg.enableCallUpgrade=false;
if(logFilePath){
char* path=(char *) env->GetStringUTFChars(logFilePath, NULL);
strncpy(cfg.logFilePath, path, sizeof(cfg.logFilePath));
@ -312,3 +449,131 @@ extern "C" JNIEXPORT jint Java_org_telegram_messenger_voip_Resampler_convert44to
extern "C" JNIEXPORT jint Java_org_telegram_messenger_voip_Resampler_convert48to44(JNIEnv* env, jclass cls, jobject from, jobject to){
return tgvoip::audio::Resampler::Convert48To44((int16_t *) env->GetDirectBufferAddress(from), (int16_t *) env->GetDirectBufferAddress(to), (size_t) (env->GetDirectBufferCapacity(from)/2), (size_t) (env->GetDirectBufferCapacity(to)/2));
}
extern "C" JNIEXPORT jlong Java_org_telegram_messenger_voip_VoIPGroupController_nativeInit(JNIEnv* env, jobject thiz, jint timeDifference){
env->GetJavaVM(&sharedJVM);
if(!AudioInputAndroid::jniClass){
jclass cls=env->FindClass("org/telegram/messenger/voip/AudioRecordJNI");
AudioInputAndroid::jniClass=(jclass) env->NewGlobalRef(cls);
AudioInputAndroid::initMethod=env->GetMethodID(cls, "init", "(IIII)V");
AudioInputAndroid::releaseMethod=env->GetMethodID(cls, "release", "()V");
AudioInputAndroid::startMethod=env->GetMethodID(cls, "start", "()Z");
AudioInputAndroid::stopMethod=env->GetMethodID(cls, "stop", "()V");
cls=env->FindClass("org/telegram/messenger/voip/AudioTrackJNI");
AudioOutputAndroid::jniClass=(jclass) env->NewGlobalRef(cls);
AudioOutputAndroid::initMethod=env->GetMethodID(cls, "init", "(IIII)V");
AudioOutputAndroid::releaseMethod=env->GetMethodID(cls, "release", "()V");
AudioOutputAndroid::startMethod=env->GetMethodID(cls, "start", "()V");
AudioOutputAndroid::stopMethod=env->GetMethodID(cls, "stop", "()V");
}
setStateMethod=env->GetMethodID(env->GetObjectClass(thiz), "handleStateChange", "(I)V");
setParticipantAudioEnabledMethod=env->GetMethodID(env->GetObjectClass(thiz), "setParticipantAudioEnabled", "(IZ)V");
setSelfStreamsMethod=env->GetMethodID(env->GetObjectClass(thiz), "setSelfStreams", "([B)V");
impl_data_android_t* impl=(impl_data_android_t*) malloc(sizeof(impl_data_android_t));
impl->javaObject=env->NewGlobalRef(thiz);
VoIPGroupController* cntrlr=new VoIPGroupController(timeDifference);
cntrlr->implData=impl;
VoIPGroupController::Callbacks callbacks;
callbacks.connectionStateChanged=updateConnectionState;
callbacks.updateStreams=updateGroupCallStreams;
callbacks.participantAudioStateChanged=updateParticipantAudioState;
callbacks.signalBarCountChanged=NULL;
cntrlr->SetCallbacks(callbacks);
return (jlong)(intptr_t)cntrlr;
}
/*
private native void nativeSetGroupCallInfo(long inst, byte[] encryptionKey, byte[] reflectorGroupTag, byte[] reflectorSelfTag, byte[] reflectorSelfSecret, String reflectorAddress, String reflectorAddressV6, int reflectorPort);
private native void addGroupCallParticipant(long inst, int userID, byte[] memberTagHash);
private native void removeGroupCallParticipant(long inst, int userID);
*/
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPGroupController_nativeSetGroupCallInfo(JNIEnv* env, jclass cls, jlong inst, jbyteArray _encryptionKey, jbyteArray _reflectorGroupTag, jbyteArray _reflectorSelfTag, jbyteArray _reflectorSelfSecret, jbyteArray _reflectorSelfTagHash, jint selfUserID, jstring reflectorAddress, jstring reflectorAddressV6, jint reflectorPort){
VoIPGroupController* ctlr=((VoIPGroupController*)(intptr_t)inst);
jbyte* encryptionKey=env->GetByteArrayElements(_encryptionKey, NULL);
jbyte* reflectorGroupTag=env->GetByteArrayElements(_reflectorGroupTag, NULL);
jbyte* reflectorSelfTag=env->GetByteArrayElements(_reflectorSelfTag, NULL);
jbyte* reflectorSelfSecret=env->GetByteArrayElements(_reflectorSelfSecret, NULL);
jbyte* reflectorSelfTagHash=env->GetByteArrayElements(_reflectorSelfTagHash, NULL);
const char* ipChars=env->GetStringUTFChars(reflectorAddress, NULL);
std::string ipLiteral(ipChars);
IPv4Address v4addr(ipLiteral);
IPv6Address v6addr("::0");
env->ReleaseStringUTFChars(reflectorAddress, ipChars);
if(reflectorAddressV6 && env->GetStringLength(reflectorAddressV6)){
const char* ipv6Chars=env->GetStringUTFChars(reflectorAddressV6, NULL);
v6addr=IPv6Address(ipv6Chars);
env->ReleaseStringUTFChars(reflectorAddressV6, ipv6Chars);
}
ctlr->SetGroupCallInfo((unsigned char *) encryptionKey, (unsigned char *) reflectorGroupTag, (unsigned char *) reflectorSelfTag, (unsigned char *) reflectorSelfSecret, (unsigned char*) reflectorSelfTagHash, selfUserID, v4addr, v6addr, (uint16_t)reflectorPort);
env->ReleaseByteArrayElements(_encryptionKey, encryptionKey, JNI_ABORT);
env->ReleaseByteArrayElements(_reflectorGroupTag, reflectorGroupTag, JNI_ABORT);
env->ReleaseByteArrayElements(_reflectorSelfTag, reflectorSelfTag, JNI_ABORT);
env->ReleaseByteArrayElements(_reflectorSelfSecret, reflectorSelfSecret, JNI_ABORT);
env->ReleaseByteArrayElements(_reflectorSelfTagHash, reflectorSelfTagHash, JNI_ABORT);
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPGroupController_nativeAddGroupCallParticipant(JNIEnv* env, jclass cls, jlong inst, jint userID, jbyteArray _memberTagHash, jbyteArray _streams){
VoIPGroupController* ctlr=((VoIPGroupController*)(intptr_t)inst);
jbyte* memberTagHash=env->GetByteArrayElements(_memberTagHash, NULL);
jbyte* streams=_streams ? env->GetByteArrayElements(_streams, NULL) : NULL;
ctlr->AddGroupCallParticipant(userID, (unsigned char *) memberTagHash, (unsigned char *) streams, (size_t) env->GetArrayLength(_streams));
env->ReleaseByteArrayElements(_memberTagHash, memberTagHash, JNI_ABORT);
if(_streams)
env->ReleaseByteArrayElements(_streams, streams, JNI_ABORT);
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPGroupController_nativeRemoveGroupCallParticipant(JNIEnv* env, jclass cls, jlong inst, jint userID){
VoIPGroupController* ctlr=((VoIPGroupController*)(intptr_t)inst);
ctlr->RemoveGroupCallParticipant(userID);
}
extern "C" JNIEXPORT jfloat Java_org_telegram_messenger_voip_VoIPGroupController_nativeGetParticipantAudioLevel(JNIEnv* env, jclass cls, jlong inst, jint userID){
return ((VoIPGroupController*)(intptr_t)inst)->GetParticipantAudioLevel(userID);
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPGroupController_nativeSetParticipantVolume(JNIEnv* env, jclass cls, jlong inst, jint userID, jfloat volume){
((VoIPGroupController*)(intptr_t)inst)->SetParticipantVolume(userID, volume);
}
extern "C" JNIEXPORT jbyteArray Java_org_telegram_messenger_voip_VoIPGroupController_getInitialStreams(JNIEnv* env, jclass cls){
unsigned char buf[1024];
size_t len=VoIPGroupController::GetInitialStreams(buf, sizeof(buf));
jbyteArray arr=env->NewByteArray(len);
jbyte* arrElems=env->GetByteArrayElements(arr, NULL);
memcpy(arrElems, buf, len);
env->ReleaseByteArrayElements(arr, arrElems, 0);
return arr;
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPGroupController_nativeSetParticipantStreams(JNIEnv* env, jclass cls, jlong inst, jint userID, jbyteArray _streams){
jbyte* streams=env->GetByteArrayElements(_streams, NULL);
((VoIPGroupController*)(intptr_t)inst)->SetParticipantStreams(userID, (unsigned char *) streams, (size_t) env->GetArrayLength(_streams));
env->ReleaseByteArrayElements(_streams, streams, JNI_ABORT);
}
extern "C" JNIEXPORT jint Java_org_telegram_messenger_voip_VoIPController_nativeGetPeerCapabilities(JNIEnv* env, jclass cls, jlong inst){
return ((VoIPController*)(intptr_t)inst)->GetPeerCapabilities();
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeSendGroupCallKey(JNIEnv* env, jclass cls, jlong inst, jbyteArray _key){
jbyte* key=env->GetByteArrayElements(_key, NULL);
((VoIPController*)(intptr_t)inst)->SendGroupCallKey((unsigned char *) key);
env->ReleaseByteArrayElements(_key, key, JNI_ABORT);
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeRequestCallUpgrade(JNIEnv* env, jclass cls, jlong inst){
((VoIPController*)(intptr_t)inst)->RequestCallUpgrade();
}

View File

@ -26,7 +26,6 @@
692AB8D91E6759DD00706ACC /* CongestionControl.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB8981E6759DD00706ACC /* CongestionControl.h */; };
692AB8DA1E6759DD00706ACC /* EchoCanceller.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 692AB8991E6759DD00706ACC /* EchoCanceller.cpp */; };
692AB8DB1E6759DD00706ACC /* EchoCanceller.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB89A1E6759DD00706ACC /* EchoCanceller.h */; };
692AB8E51E6759DD00706ACC /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 692AB8A71E6759DD00706ACC /* Info.plist */; };
692AB8E61E6759DD00706ACC /* JitterBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 692AB8A81E6759DD00706ACC /* JitterBuffer.cpp */; };
692AB8E71E6759DD00706ACC /* JitterBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB8A91E6759DD00706ACC /* JitterBuffer.h */; };
692AB8E81E6759DD00706ACC /* logging.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB8AA1E6759DD00706ACC /* logging.h */; };
@ -192,6 +191,7 @@
69A6DE1C1E95ECF000000E69 /* wav_file.h in Headers */ = {isa = PBXBuildFile; fileRef = 69A6DE181E95ECF000000E69 /* wav_file.h */; };
69A6DE1D1E95ECF000000E69 /* wav_header.cc in Sources */ = {isa = PBXBuildFile; fileRef = 69A6DE191E95ECF000000E69 /* wav_header.cc */; };
69A6DE1E1E95ECF000000E69 /* wav_header.h in Headers */ = {isa = PBXBuildFile; fileRef = 69A6DE1A1E95ECF000000E69 /* wav_header.h */; };
D00ACA4F20222F5D0045D427 /* SetupLogging.h in Headers */ = {isa = PBXBuildFile; fileRef = D00ACA4D20222F5D0045D427 /* SetupLogging.h */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -434,6 +434,7 @@
69A6DE191E95ECF000000E69 /* wav_header.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wav_header.cc; sourceTree = "<group>"; };
69A6DE1A1E95ECF000000E69 /* wav_header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wav_header.h; sourceTree = "<group>"; };
69F842361E67540700C110F7 /* libtgvoip.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = libtgvoip.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D00ACA4D20222F5D0045D427 /* SetupLogging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SetupLogging.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -535,6 +536,7 @@
692AB8C51E6759DD00706ACC /* TGLogWrapper.m */,
69960A021EF85C2900F9D091 /* DarwinSpecific.h */,
69960A031EF85C2900F9D091 /* DarwinSpecific.mm */,
D00ACA4D20222F5D0045D427 /* SetupLogging.h */,
);
path = darwin;
sourceTree = "<group>";
@ -905,6 +907,7 @@
69A6DDC11E95EC7700000E69 /* spl_inl_armv7.h in Headers */,
69A6DE031E95EC7800000E69 /* three_band_filter_bank.h in Headers */,
69A6DDFB1E95EC7700000E69 /* nsx_core.h in Headers */,
D00ACA4F20222F5D0045D427 /* SetupLogging.h in Headers */,
69A6DDE41E95EC7700000E69 /* echo_cancellation.h in Headers */,
69A6DDF71E95EC7700000E69 /* noise_suppression_x.h in Headers */,
69A6DD9B1E95EC7700000E69 /* safe_conversions.h in Headers */,
@ -1074,7 +1077,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
692AB8E51E6759DD00706ACC /* Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1301,6 +1303,7 @@
"$(inherited)",
"$(PROJECT_DIR)/../../Telegraph",
webrtc_dsp,
"$(PROJECT_DIR)/../TelegramUI/third-party/opus/include/opus",
);
INFOPLIST_FILE = "$(SRCROOT)/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@ -1336,6 +1339,7 @@
"$(inherited)",
"$(PROJECT_DIR)/../../Telegraph",
webrtc_dsp,
"$(PROJECT_DIR)/../TelegramUI/third-party/opus/include/opus",
);
INFOPLIST_FILE = "$(SRCROOT)/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@ -1356,6 +1360,336 @@
};
name = Release;
};
D00ACA51202234510045D427 /* Debug Hockeyapp */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.2;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = "Debug Hockeyapp";
};
D00ACA52202234510045D427 /* Debug Hockeyapp */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
CODE_SIGN_IDENTITY = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
HEADER_SEARCH_PATHS = (
"$(PROJECT_DIR)/../../Telegraph/thirdparty/opus/include/opus",
"$(inherited)",
"$(PROJECT_DIR)/../../Telegraph",
webrtc_dsp,
"$(PROJECT_DIR)/../TelegramUI/third-party/opus/include/opus",
);
INFOPLIST_FILE = "$(SRCROOT)/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
MACH_O_TYPE = staticlib;
MACOSX_DEPLOYMENT_TARGET = 10.6;
OTHER_CFLAGS = (
"-DTGVOIP_USE_CUSTOM_CRYPTO",
"-DWEBRTC_APM_DEBUG_DUMP=0",
"-DWEBRTC_POSIX",
"-DTGVOIP_HAVE_TGLOG",
);
PRODUCT_BUNDLE_IDENTIFIER = me.grishka.libtgvoip;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = "Debug Hockeyapp";
};
D00ACA532022347C0045D427 /* Release Hockeyapp */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.2;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = "Release Hockeyapp";
};
D00ACA542022347C0045D427 /* Release Hockeyapp */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
CODE_SIGN_IDENTITY = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
HEADER_SEARCH_PATHS = (
"$(PROJECT_DIR)/../../Telegraph/thirdparty/opus/include/opus",
"$(inherited)",
"$(PROJECT_DIR)/../../Telegraph",
webrtc_dsp,
"$(PROJECT_DIR)/../TelegramUI/third-party/opus/include/opus",
);
INFOPLIST_FILE = "$(SRCROOT)/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
MACH_O_TYPE = staticlib;
MACOSX_DEPLOYMENT_TARGET = 10.6;
OTHER_CFLAGS = (
"-DTGVOIP_USE_CUSTOM_CRYPTO",
"-DWEBRTC_APM_DEBUG_DUMP=0",
"-DWEBRTC_POSIX",
"-DTGVOIP_HAVE_TGLOG",
);
PRODUCT_BUNDLE_IDENTIFIER = me.grishka.libtgvoip;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = "Release Hockeyapp";
};
D00ACA55202234840045D427 /* Release Hockeyapp Internal */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.2;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = "Release Hockeyapp Internal";
};
D00ACA56202234840045D427 /* Release Hockeyapp Internal */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
CODE_SIGN_IDENTITY = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
HEADER_SEARCH_PATHS = (
"$(PROJECT_DIR)/../../Telegraph/thirdparty/opus/include/opus",
"$(inherited)",
"$(PROJECT_DIR)/../../Telegraph",
webrtc_dsp,
"$(PROJECT_DIR)/../TelegramUI/third-party/opus/include/opus",
);
INFOPLIST_FILE = "$(SRCROOT)/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
MACH_O_TYPE = staticlib;
MACOSX_DEPLOYMENT_TARGET = 10.6;
OTHER_CFLAGS = (
"-DTGVOIP_USE_CUSTOM_CRYPTO",
"-DWEBRTC_APM_DEBUG_DUMP=0",
"-DWEBRTC_POSIX",
"-DTGVOIP_HAVE_TGLOG",
);
PRODUCT_BUNDLE_IDENTIFIER = me.grishka.libtgvoip;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = "Release Hockeyapp Internal";
};
D00ACA5B2022A70D0045D427 /* Release AppStore */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.2;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = "Release AppStore";
};
D00ACA5C2022A70D0045D427 /* Release AppStore */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
CODE_SIGN_IDENTITY = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
HEADER_SEARCH_PATHS = (
"$(PROJECT_DIR)/../../Telegraph/thirdparty/opus/include/opus",
"$(inherited)",
"$(PROJECT_DIR)/../../Telegraph",
webrtc_dsp,
"$(PROJECT_DIR)/../TelegramUI/third-party/opus/include/opus",
);
INFOPLIST_FILE = "$(SRCROOT)/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
MACH_O_TYPE = staticlib;
MACOSX_DEPLOYMENT_TARGET = 10.6;
OTHER_CFLAGS = (
"-DTGVOIP_USE_CUSTOM_CRYPTO",
"-DWEBRTC_APM_DEBUG_DUMP=0",
"-DWEBRTC_POSIX",
"-DTGVOIP_HAVE_TGLOG",
);
PRODUCT_BUNDLE_IDENTIFIER = me.grishka.libtgvoip;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = "Release AppStore";
};
D04D01C31E678C0D0086DDC0 /* Debug AppStore */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -1422,6 +1756,7 @@
"$(inherited)",
"$(PROJECT_DIR)/../../Telegraph",
webrtc_dsp,
"$(PROJECT_DIR)/../TelegramUI/third-party/opus/include/opus",
);
INFOPLIST_FILE = "$(SRCROOT)/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@ -1502,6 +1837,7 @@
"$(inherited)",
"$(PROJECT_DIR)/../../Telegraph",
webrtc_dsp,
"$(PROJECT_DIR)/../TelegramUI/third-party/opus/include/opus",
);
INFOPLIST_FILE = "$(SRCROOT)/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@ -1522,6 +1858,86 @@
};
name = Hockeyapp;
};
D077B8DF1F45EA870046D27A /* Release Hockeyapp */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.2;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = "Release Hockeyapp";
};
D077B8E01F45EA870046D27A /* Release Hockeyapp */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
CODE_SIGN_IDENTITY = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
HEADER_SEARCH_PATHS = (
"$(PROJECT_DIR)/../../Telegraph/thirdparty/opus/include/opus",
"$(inherited)",
"$(PROJECT_DIR)/../../Telegraph",
webrtc_dsp,
);
INFOPLIST_FILE = "$(SRCROOT)/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = "$(inherited)";
MACH_O_TYPE = staticlib;
MACOSX_DEPLOYMENT_TARGET = 10.6;
OTHER_CFLAGS = (
"-DTGVOIP_USE_CUSTOM_CRYPTO",
"-DWEBRTC_APM_DEBUG_DUMP=0",
"-DWEBRTC_POSIX",
"-DTGVOIP_HAVE_TGLOG",
);
PRODUCT_BUNDLE_IDENTIFIER = me.grishka.libtgvoip;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = "Release Hockeyapp";
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@ -1529,9 +1945,14 @@
isa = XCConfigurationList;
buildConfigurations = (
69F8423C1E67540700C110F7 /* Debug */,
D00ACA51202234510045D427 /* Debug Hockeyapp */,
D04D01C31E678C0D0086DDC0 /* Debug AppStore */,
69F8423D1E67540700C110F7 /* Release */,
D00ACA5B2022A70D0045D427 /* Release AppStore */,
D00ACA532022347C0045D427 /* Release Hockeyapp */,
D00ACA55202234840045D427 /* Release Hockeyapp Internal */,
D04D01CB1E678C230086DDC0 /* Hockeyapp */,
D077B8DF1F45EA870046D27A /* Release Hockeyapp */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
@ -1540,9 +1961,14 @@
isa = XCConfigurationList;
buildConfigurations = (
69F8423F1E67540700C110F7 /* Debug */,
D00ACA52202234510045D427 /* Debug Hockeyapp */,
D04D01C41E678C0D0086DDC0 /* Debug AppStore */,
69F842401E67540700C110F7 /* Release */,
D00ACA5C2022A70D0045D427 /* Release AppStore */,
D00ACA542022347C0045D427 /* Release Hockeyapp */,
D00ACA56202234840045D427 /* Release Hockeyapp Internal */,
D04D01CC1E678C230086DDC0 /* Hockeyapp */,
D077B8E01F45EA870046D27A /* Release Hockeyapp */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;

View File

@ -7,7 +7,7 @@
<key>libtgvoip.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
<integer>1</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>

View File

@ -37,8 +37,8 @@ void tgvoip_log_file_printf(char level, const char* msg, ...){
}
}
void tgvoip_log_file_write_header(){
if(tgvoipLogFile){
void tgvoip_log_file_write_header(FILE* file){
if(file){
time_t t = time(0);
struct tm *now = localtime(&t);
#if defined(_WIN32)
@ -94,6 +94,6 @@ void tgvoip_log_file_write_header(){
const char* cpuArch="Unknown CPU";
#endif
fprintf(tgvoipLogFile, "---------------\nlibtgvoip v" LIBTGVOIP_VERSION " on %s %s\nLog started on %d/%02d/%d at %d:%02d:%02d\n---------------\n", systemVersion, cpuArch, now->tm_mday, now->tm_mon+1, now->tm_year+1900, now->tm_hour, now->tm_min, now->tm_sec);
fprintf(file, "---------------\nlibtgvoip v" LIBTGVOIP_VERSION " on %s %s\nLog started on %d/%02d/%d at %d:%02d:%02d\n---------------\n", systemVersion, cpuArch, now->tm_mday, now->tm_mon+1, now->tm_year+1900, now->tm_hour, now->tm_min, now->tm_sec);
}
}

View File

@ -13,8 +13,10 @@
#include <TargetConditionals.h>
#endif
#include <stdio.h>
void tgvoip_log_file_printf(char level, const char* msg, ...);
void tgvoip_log_file_write_header();
void tgvoip_log_file_write_header(FILE* file);
#if defined(__ANDROID__)

View File

@ -36,7 +36,6 @@ AudioInputAndroid::AudioInputAndroid(){
sharedJVM->DetachCurrentThread();
}
running=false;
init_mutex(mutex);
}
AudioInputAndroid::~AudioInputAndroid(){
@ -58,7 +57,6 @@ AudioInputAndroid::~AudioInputAndroid(){
sharedJVM->DetachCurrentThread();
}
}
free_mutex(mutex);
}
void AudioInputAndroid::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){

View File

@ -30,7 +30,7 @@ public:
private:
jobject javaObject;
bool running;
tgvoip_mutex_t mutex;
Mutex mutex;
};
}}

View File

@ -18,7 +18,7 @@
using namespace tgvoip;
using namespace tgvoip::audio;
int AudioInputOpenSLES::nativeBufferSize;
unsigned int AudioInputOpenSLES::nativeBufferSize;
AudioInputOpenSLES::AudioInputOpenSLES(){
slEngine=OpenSLEngineWrapper::CreateEngine();

View File

@ -22,7 +22,7 @@ public:
virtual void Start();
virtual void Stop();
static int nativeBufferSize;
static unsigned int nativeBufferSize;
private:
static void BufferCallback(SLAndroidSimpleBufferQueueItf bq, void *context);

View File

@ -13,13 +13,13 @@
#include "OpenSLEngineWrapper.h"
#include "AudioInputAndroid.h"
#define CHECK_SL_ERROR(res, msg) if(res!=SL_RESULT_SUCCESS){ LOGE(msg); return; }
#define CHECK_SL_ERROR(res, msg) if(res!=SL_RESULT_SUCCESS){ LOGE(msg); failed=true; return; }
#define BUFFER_SIZE 960 // 20 ms
using namespace tgvoip;
using namespace tgvoip::audio;
int AudioOutputOpenSLES::nativeBufferSize;
unsigned int AudioOutputOpenSLES::nativeBufferSize;
AudioOutputOpenSLES::AudioOutputOpenSLES(){
SLresult result;
@ -63,7 +63,7 @@ AudioOutputOpenSLES::~AudioOutputOpenSLES(){
}
void AudioOutputOpenSLES::SetNativeBufferSize(int size){
void AudioOutputOpenSLES::SetNativeBufferSize(unsigned int size){
AudioOutputOpenSLES::nativeBufferSize=size;
}

View File

@ -25,8 +25,8 @@ public:
virtual bool IsPlaying();
virtual float GetLevel();
static void SetNativeBufferSize(int size);
static int nativeBufferSize;
static void SetNativeBufferSize(unsigned int size);
static unsigned int nativeBufferSize;
private:
static void BufferCallback(SLAndroidSimpleBufferQueueItf bq, void *context);

View File

@ -16,10 +16,10 @@
using namespace tgvoip;
using namespace tgvoip::audio;
AudioInputAudioUnit::AudioInputAudioUnit(std::string deviceID){
AudioInputAudioUnit::AudioInputAudioUnit(std::string deviceID, AudioUnitIO* io){
remainingDataSize=0;
isRecording=false;
this->io=AudioUnitIO::Get();
this->io=io;
#if TARGET_OS_OSX
io->SetCurrentDevice(true, deviceID);
#endif
@ -29,7 +29,6 @@ AudioInputAudioUnit::AudioInputAudioUnit(std::string deviceID){
AudioInputAudioUnit::~AudioInputAudioUnit(){
io->DetachInput();
AudioUnitIO::Release();
}
void AudioInputAudioUnit::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){

View File

@ -16,7 +16,7 @@ class AudioUnitIO;
class AudioInputAudioUnit : public AudioInput{
public:
AudioInputAudioUnit(std::string deviceID);
AudioInputAudioUnit(std::string deviceID, AudioUnitIO* io);
virtual ~AudioInputAudioUnit();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
virtual void Start();

View File

@ -12,16 +12,15 @@
#include "AudioUnitIO.h"
#define BUFFER_SIZE 960
const int8_t permutation[33]={0,1,2,3,4,4,5,5,5,5,6,6,6,6,6,7,7,7,7,8,8,8,9,9,9,9,9,9,9,9,9,9,9};
using namespace tgvoip;
using namespace tgvoip::audio;
AudioOutputAudioUnit::AudioOutputAudioUnit(std::string deviceID){
AudioOutputAudioUnit::AudioOutputAudioUnit(std::string deviceID, AudioUnitIO* io){
isPlaying=false;
remainingDataSize=0;
level=0.0;
this->io=AudioUnitIO::Get();
this->io=io;
#if TARGET_OS_OSX
io->SetCurrentDevice(false, deviceID);
#endif
@ -31,7 +30,6 @@ AudioOutputAudioUnit::AudioOutputAudioUnit(std::string deviceID){
AudioOutputAudioUnit::~AudioOutputAudioUnit(){
io->DetachOutput();
AudioUnitIO::Release();
}
void AudioOutputAudioUnit::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
@ -67,8 +65,6 @@ float AudioOutputAudioUnit::GetLevel(){
void AudioOutputAudioUnit::HandleBufferCallback(AudioBufferList *ioData){
int i;
unsigned int k;
int16_t absVal=0;
for(i=0;i<ioData->mNumberBuffers;i++){
AudioBuffer buf=ioData->mBuffers[i];
if(!isPlaying){
@ -76,6 +72,7 @@ void AudioOutputAudioUnit::HandleBufferCallback(AudioBufferList *ioData){
return;
}
#if TARGET_OS_OSX
unsigned int k;
while(remainingDataSize<buf.mDataByteSize/2){
assert(remainingDataSize+BUFFER_SIZE*2<sizeof(remainingData));
InvokeCallback(remainingData+remainingDataSize, BUFFER_SIZE*2);
@ -98,28 +95,6 @@ void AudioOutputAudioUnit::HandleBufferCallback(AudioBufferList *ioData){
remainingDataSize-=buf.mDataByteSize;
memmove(remainingData, remainingData+buf.mDataByteSize, remainingDataSize);
#endif
/*unsigned int samples=buf.mDataByteSize/sizeof(int16_t);
for (k=0;k<samples;k++){
int16_t absolute=(int16_t)abs(*((int16_t *)buf.mData+k));
if (absolute>absVal)
absVal=absolute;
}
if (absVal>absMax)
absMax=absVal;
count++;
if (count>=10) {
count=0;
short position=absMax/1000;
if (position==0 && absMax>250) {
position=1;
}
level=permutation[position];
absMax>>=2;
}*/
}
}

View File

@ -15,7 +15,7 @@ class AudioUnitIO;
class AudioOutputAudioUnit : public AudioOutput{
public:
AudioOutputAudioUnit(std::string deviceID);
AudioOutputAudioUnit(std::string deviceID, AudioUnitIO* io);
virtual ~AudioOutputAudioUnit();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
virtual bool IsPhone();

View File

@ -26,9 +26,6 @@
using namespace tgvoip;
using namespace tgvoip::audio;
int AudioUnitIO::refCount=0;
AudioUnitIO* AudioUnitIO::sharedInstance=NULL;
AudioUnitIO::AudioUnitIO(){
input=NULL;
output=NULL;
@ -130,24 +127,6 @@ AudioUnitIO::~AudioUnitIO(){
free(inBufferList.mBuffers[0].mData);
}
AudioUnitIO* AudioUnitIO::Get(){
if(refCount==0){
sharedInstance=new AudioUnitIO();
}
refCount++;
assert(refCount>0);
return sharedInstance;
}
void AudioUnitIO::Release(){
refCount--;
assert(refCount>=0);
if(refCount==0){
delete sharedInstance;
sharedInstance=NULL;
}
}
void AudioUnitIO::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
}

View File

@ -28,8 +28,6 @@ public:
void EnableInput(bool enabled);
void EnableOutput(bool enabled);
bool IsFailed();
static AudioUnitIO* Get();
static void Release();
#if TARGET_OS_OSX
void SetCurrentDevice(bool input, std::string deviceID);
#endif
@ -51,8 +49,6 @@ private:
bool outputEnabled;
bool failed;
bool started;
static int refCount;
static AudioUnitIO* sharedInstance;
};
}}

3
os/darwin/SetupLogging.h Normal file
View File

@ -0,0 +1,3 @@
#import <Foundation/Foundation.h>
extern void (*TGVoipLoggingFunction)(NSString *);

View File

@ -11,8 +11,6 @@
extern "C" {
#endif
extern void (*TGVoipLoggingFunction)(NSString *);
void __tgvoip_call_tglog(const char* format, ...);
#if defined __cplusplus

View File

@ -3,10 +3,11 @@
void (*TGVoipLoggingFunction)(NSString *) = NULL;
void __tgvoip_call_tglog(const char* format, ...){
if (TGVoipLoggingFunction != nil) {
va_list args;
va_start(args, format);
TGVoipLoggingFunction([[NSString alloc]initWithFormat:[[NSString alloc] initWithUTF8String:format] arguments:args]);
va_end(args);
va_list args;
va_start(args, format);
NSString *string = [[NSString alloc] initWithFormat:[[NSString alloc]initWithUTF8String:format] arguments:args];
va_end(args);
if (TGVoipLoggingFunction) {
TGVoipLoggingFunction(string);
}
}

View File

@ -58,7 +58,9 @@ void AudioInputALSA::Start(){
return;
isRecording=true;
start_thread(thread, AudioInputALSA::StartThread, this);
thread=new Thread(new MethodPointer<AudioInputALSA>(&AudioInputALSA::RunThread, this), NULL);
thread->SetName("AudioInputALSA");
thread->Start();
}
void AudioInputALSA::Stop(){
@ -66,15 +68,12 @@ void AudioInputALSA::Stop(){
return;
isRecording=false;
join_thread(thread);
thread->Join();
delete thread;
thread=NULL;
}
void* AudioInputALSA::StartThread(void* arg){
((AudioInputALSA*)arg)->RunThread();
return NULL;
}
void AudioInputALSA::RunThread(){
void AudioInputALSA::RunThread(void* arg){
unsigned char buffer[BUFFER_SIZE*2];
snd_pcm_sframes_t frames;
while(isRecording){
@ -94,7 +93,7 @@ void AudioInputALSA::SetCurrentDevice(std::string devID){
bool wasRecording=isRecording;
isRecording=false;
if(handle){
join_thread(thread);
thread->Join();
_snd_pcm_close(handle);
}
currentDevice=devID;
@ -109,7 +108,7 @@ void AudioInputALSA::SetCurrentDevice(std::string devID){
if(wasRecording){
isRecording=true;
start_thread(thread, AudioInputALSA::StartThread, this);
thread->Start();
}
}

View File

@ -26,8 +26,7 @@ public:
static void EnumerateDevices(std::vector<AudioInputDevice>& devs);
private:
static void* StartThread(void* arg);
void RunThread();
void RunThread(void* arg);
int (*_snd_pcm_open)(snd_pcm_t** pcm, const char* name, snd_pcm_stream_t stream, int mode);
int (*_snd_pcm_set_params)(snd_pcm_t* pcm, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int channels, unsigned int rate, int soft_resample, unsigned int latency);
@ -38,7 +37,7 @@ private:
void* lib;
snd_pcm_t* handle;
tgvoip_thread_t thread;
Thread* thread;
bool isRecording;
};

View File

@ -57,7 +57,9 @@ void AudioOutputALSA::Start(){
return;
isPlaying=true;
start_thread(thread, AudioOutputALSA::StartThread, this);
thread=new Thread(new MethodPointer<AudioOutputALSA>(&AudioOutputALSA::RunThread, this), NULL);
thread->SetName("AudioOutputALSA");
thread->Start();
}
void AudioOutputALSA::Stop(){
@ -65,19 +67,15 @@ void AudioOutputALSA::Stop(){
return;
isPlaying=false;
join_thread(thread);
thread->Join();
delete thread;
thread=NULL;
}
bool AudioOutputALSA::IsPlaying(){
return isPlaying;
}
void* AudioOutputALSA::StartThread(void* arg){
((AudioOutputALSA*)arg)->RunThread();
return NULL;
}
void AudioOutputALSA::RunThread(){
void AudioOutputALSA::RunThread(void* arg){
unsigned char buffer[BUFFER_SIZE*2];
snd_pcm_sframes_t frames;
while(isPlaying){
@ -97,7 +95,7 @@ void AudioOutputALSA::SetCurrentDevice(std::string devID){
bool wasPlaying=isPlaying;
isPlaying=false;
if(handle){
join_thread(thread);
thread->Join();
_snd_pcm_close(handle);
}
currentDevice=devID;
@ -112,7 +110,7 @@ void AudioOutputALSA::SetCurrentDevice(std::string devID){
if(wasPlaying){
isPlaying=true;
start_thread(thread, AudioOutputALSA::StartThread, this);
thread->Start();
}
}

View File

@ -26,8 +26,7 @@ public:
static void EnumerateDevices(std::vector<AudioOutputDevice>& devs);
private:
static void* StartThread(void* arg);
void RunThread();
void RunThread(void* arg);
int (*_snd_pcm_open)(snd_pcm_t** pcm, const char* name, snd_pcm_stream_t stream, int mode);
int (*_snd_pcm_set_params)(snd_pcm_t* pcm, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int channels, unsigned int rate, int soft_resample, unsigned int latency);
@ -38,7 +37,7 @@ private:
void* lib;
snd_pcm_t* handle;
tgvoip_thread_t thread;
Thread* thread;
bool isPlaying;
};

View File

@ -18,6 +18,13 @@
#include "../../BufferInputStream.h"
#include "../../BufferOutputStream.h"
#ifdef __ANDROID__
#include <jni.h>
#include <sys/system_properties.h>
extern JavaVM* sharedJVM;
extern jclass jniUtilitiesClass;
#endif
using namespace tgvoip;
@ -61,7 +68,7 @@ void NetworkSocketPosix::SetMaxPriority(){
}
void NetworkSocketPosix::Send(NetworkPacket *packet){
if(!packet || !packet->address){
if(!packet || (protocol==PROTO_UDP && !packet->address)){
LOGW("tried to send null packet");
return;
}
@ -222,7 +229,6 @@ void NetworkSocketPosix::Open(){
}
size_t addrLen=sizeof(sockaddr_in6);
getsockname(fd, (sockaddr*)&addr, (socklen_t*) &addrLen);
uint16_t localUdpPort=ntohs(addr.sin6_port);
LOGD("Bound to local UDP port %u", ntohs(addr.sin6_port));
needUpdateNat64Prefix=true;
@ -237,6 +243,7 @@ void NetworkSocketPosix::Close(){
if (fd>=0) {
shutdown(fd, SHUT_RDWR);
close(fd);
fd=-1;
}
}
@ -299,13 +306,45 @@ void NetworkSocketPosix::OnActiveInterfaceChanged(){
}
std::string NetworkSocketPosix::GetLocalInterfaceInfo(IPv4Address *v4addr, IPv6Address *v6addr){
#ifdef __ANDROID__
char sdkNum[PROP_VALUE_MAX];
__system_property_get("ro.build.version.sdk", sdkNum);
int systemVersion=atoi(sdkNum);
char androidInterfaceName[128];
if(systemVersion>23){
JNIEnv *env=NULL;
bool didAttach=false;
sharedJVM->GetEnv((void **) &env, JNI_VERSION_1_6);
if(!env){
sharedJVM->AttachCurrentThread(&env, NULL);
didAttach=true;
}
jmethodID getActiveInterfaceMethod=env->GetStaticMethodID(jniUtilitiesClass, "getCurrentNetworkInterfaceName", "()Ljava/lang/String;");
jstring jitf=(jstring) env->CallStaticObjectMethod(jniUtilitiesClass, getActiveInterfaceMethod);
if(jitf){
const char* itfchars=env->GetStringUTFChars(jitf, NULL);
strncpy(androidInterfaceName, itfchars, sizeof(androidInterfaceName));
env->ReleaseStringUTFChars(jitf, itfchars);
}else{
memset(androidInterfaceName, 0, sizeof(androidInterfaceName));
}
LOGV("Android active network interface: '%s'", androidInterfaceName);
if(didAttach){
sharedJVM->DetachCurrentThread();
}
}else{
memset(androidInterfaceName, 0, sizeof(androidInterfaceName));
}
#endif
struct ifconf ifc;
struct ifreq* ifr;
char buf[16384];
int sd;
std::string name="";
sd=socket(PF_INET, SOCK_DGRAM, 0);
if(sd>0){
if(sd>=0){
ifc.ifc_len=sizeof(buf);
ifc.ifc_ifcu.ifcu_buf=buf;
if(ioctl(sd, SIOCGIFCONF, &ifc)==0){
@ -319,6 +358,14 @@ std::string NetworkSocketPosix::GetLocalInterfaceInfo(IPv4Address *v4addr, IPv6A
len=sizeof(*ifr);
#endif
if(ifr->ifr_addr.sa_family==AF_INET){
#ifdef __ANDROID__
if(strlen(androidInterfaceName) && strcmp(androidInterfaceName, ifr->ifr_name)!=0){
LOGI("Skipping interface %s as non-active [android-only]", ifr->ifr_name);
ifr=(struct ifreq*)((char*)ifr+len);
i+=len;
continue;
}
#endif
if(ioctl(sd, SIOCGIFADDR, ifr)==0){
struct sockaddr_in* addr=(struct sockaddr_in *)(&ifr->ifr_addr);
LOGI("Interface %s, address %s\n", ifr->ifr_name, inet_ntoa(addr->sin_addr));
@ -327,6 +374,8 @@ std::string NetworkSocketPosix::GetLocalInterfaceInfo(IPv4Address *v4addr, IPv6A
//LOGV("flags = %08X", ifr->ifr_flags);
if((ntohl(addr->sin_addr.s_addr) & 0xFFFF0000)==0xA9FE0000){
LOGV("skipping link-local");
ifr=(struct ifreq*)((char*)ifr+len);
i+=len;
continue;
}
if(v4addr){
@ -345,8 +394,8 @@ std::string NetworkSocketPosix::GetLocalInterfaceInfo(IPv4Address *v4addr, IPv6A
}else{
LOGE("Error getting LAN address: %d", errno);
}
close(sd);
}
close(sd);
return name;
}
@ -458,7 +507,7 @@ bool NetworkSocketPosix::Select(std::vector<NetworkSocket *> &readFds, std::vect
maxfd=sfd;
}
int res=select(maxfd+1, &readSet, NULL, &errorSet, NULL);
select(maxfd+1, &readSet, NULL, &errorSet, NULL);
if(canceller && FD_ISSET(canceller->pipeRead, &readSet) && !anyFailed){
char c;
@ -496,7 +545,10 @@ bool NetworkSocketPosix::Select(std::vector<NetworkSocket *> &readFds, std::vect
SocketSelectCancellerPosix::SocketSelectCancellerPosix(){
int p[2];
int pipeRes=pipe(p);
assert(pipeRes==0);
if(pipeRes!=0){
LOGE("pipe() failed");
abort();
}
pipeRead=p[0];
pipeWrite=p[1];
}

View File

@ -62,7 +62,7 @@ void VoIPControllerWrapper::SetPublicEndpoints(const Platform::Array<libtgvoip::
libtgvoip::Endpoint^ _ep = endpoints[i];
tgvoip::Endpoint ep;
ep.id = _ep->id;
ep.type = EP_TYPE_UDP_RELAY;
ep.type = Endpoint::TYPE_UDP_RELAY;
char buf[128];
if (_ep->ipv4){
WideCharToMultiByte(CP_UTF8, 0, _ep->ipv4->Data(), -1, buf, sizeof(buf), NULL, NULL);

View File

@ -7,34 +7,108 @@
#ifndef __THREADING_H
#define __THREADING_H
namespace tgvoip{
class MethodPointerBase{
public:
virtual ~MethodPointerBase(){
}
virtual void Invoke(void* arg)=0;
};
template<typename T> class MethodPointer : public MethodPointerBase{
public:
MethodPointer(void (T::*method)(void*), T* obj){
this->method=method;
this->obj=obj;
}
virtual void Invoke(void* arg){
(obj->*method)(arg);
}
private:
void (T::*method)(void*);
T* obj;
};
}
#if defined(_POSIX_THREADS) || defined(_POSIX_VERSION) || defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
#include <pthread.h>
#include <semaphore.h>
#include <sched.h>
typedef pthread_t tgvoip_thread_t;
typedef pthread_mutex_t tgvoip_mutex_t;
typedef pthread_cond_t tgvoip_lock_t;
namespace tgvoip{
class Mutex{
public:
Mutex(){
pthread_mutex_init(&mtx, NULL);
}
#define start_thread(ref, entry, arg) pthread_create(&ref, NULL, entry, arg)
#define join_thread(thread) pthread_join(thread, NULL)
~Mutex(){
pthread_mutex_destroy(&mtx);
}
void Lock(){
pthread_mutex_lock(&mtx);
}
void Unlock(){
pthread_mutex_unlock(&mtx);
}
private:
Mutex(const Mutex& other);
pthread_mutex_t mtx;
};
class Thread{
public:
Thread(MethodPointerBase* entry, void* arg) : entry(entry), arg(arg){
name=NULL;
}
~Thread(){
delete entry;
}
void Start(){
pthread_create(&thread, NULL, Thread::ActualEntryPoint, this);
}
void Join(){
pthread_join(thread, NULL);
}
void SetName(const char* name){
this->name=name;
}
void SetMaxPriority(){
}
private:
static void* ActualEntryPoint(void* arg){
Thread* self=reinterpret_cast<Thread*>(arg);
if(self->name){
#ifndef __APPLE__
#define set_thread_name(thread, name) pthread_setname_np(thread, name)
pthread_setname_np(self->thread, self->name);
#else
#define set_thread_name(thread, name)
pthread_setname_np(self->name);
#endif
#define set_thread_priority(thread, priority) {sched_param __param; __param.sched_priority=priority; int __result=pthread_setschedparam(thread, SCHED_RR, &__param); if(__result!=0){LOGE("can't set thread priority: %s", strerror(__result));}};
#define get_thread_max_priority() sched_get_priority_max(SCHED_RR)
#define get_thread_min_priority() sched_get_priority_min(SCHED_RR)
#define init_mutex(mutex) pthread_mutex_init(&mutex, NULL)
#define free_mutex(mutex) pthread_mutex_destroy(&mutex)
#define lock_mutex(mutex) pthread_mutex_lock(&mutex)
#define unlock_mutex(mutex) pthread_mutex_unlock(&mutex)
#define init_lock(lock) pthread_cond_init(&lock, NULL)
#define free_lock(lock) pthread_cond_destroy(&lock)
#define wait_lock(lock, mutex) pthread_cond_wait(&lock, &mutex)
#define notify_lock(lock) pthread_cond_broadcast(&lock)
}
self->entry->Invoke(self->arg);
return NULL;
}
MethodPointerBase* entry;
void* arg;
pthread_t thread;
const char* name;
};
}
#ifdef __APPLE__
#include <dispatch/dispatch.h>
@ -113,39 +187,100 @@ private:
#include <Windows.h>
#include <assert.h>
typedef HANDLE tgvoip_thread_t;
typedef CRITICAL_SECTION tgvoip_mutex_t;
typedef HANDLE tgvoip_lock_t; // uncomment for XP compatibility
//typedef CONDITION_VARIABLE tgvoip_lock_t;
#define start_thread(ref, entry, arg) (ref=CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)entry, arg, 0, NULL))
#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY!=WINAPI_FAMILY_PHONE_APP
#define join_thread(thread) {WaitForSingleObject(thread, INFINITE); CloseHandle(thread);}
#else
#define join_thread(thread) {WaitForSingleObjectEx(thread, INFINITE, false); CloseHandle(thread);}
#endif
#define set_thread_name(thread, name) // threads in Windows don't have names
#define set_thread_priority(thread, priority) SetThreadPriority(thread, priority)
#define get_thread_max_priority() THREAD_PRIORITY_HIGHEST
#define get_thread_min_priority() THREAD_PRIORITY_LOWEST
#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY!=WINAPI_FAMILY_PHONE_APP
#define init_mutex(mutex) InitializeCriticalSection(&mutex)
#else
#define init_mutex(mutex) InitializeCriticalSectionEx(&mutex, 0, 0)
#endif
#define free_mutex(mutex) DeleteCriticalSection(&mutex)
#define lock_mutex(mutex) EnterCriticalSection(&mutex)
#define unlock_mutex(mutex) LeaveCriticalSection(&mutex)
#define init_lock(lock) (lock=CreateEvent(NULL, false, false, NULL))
#define free_lock(lock) CloseHandle(lock)
#define wait_lock(lock, mutex) {LeaveCriticalSection(&mutex); WaitForSingleObject(lock, INFINITE); EnterCriticalSection(&mutex);}
#define notify_lock(lock) PulseEvent(lock)
//#define init_lock(lock) InitializeConditionVariable(&lock)
//#define free_lock(lock) // ?
//#define wait_lock(lock, mutex) SleepConditionVariableCS(&lock, &mutex, INFINITE)
//#define notify_lock(lock) WakeAllConditionVariable(&lock)
namespace tgvoip{
class Mutex{
public:
Mutex(){
#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY!=WINAPI_FAMILY_PHONE_APP
InitializeCriticalSection(&section);
#else
InitializeCriticalSectionEx(&section, 0, 0);
#endif
}
~Mutex(){
DeleteCriticalSection(&section);
}
void Lock(){
EnterCriticalSection(&section);
}
void Unlock(){
LeaveCriticalSection(&section);
}
private:
Mutex(const Mutex& other);
CRITICAL_SECTION section;
};
class Thread{
public:
Thread(MethodPointerBase* entry, void* arg) : entry(entry), arg(arg){
name=NULL;
}
~Thread(){
delete entry;
}
void Start(){
thread=CreateThread(NULL, 0, Thread::ActualEntryPoint, this, 0, NULL);
}
void Join(){
#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY!=WINAPI_FAMILY_PHONE_APP
WaitForSingleObject(thread, INFINITE);
#else
WaitForSingleObjectEx(thread, INFINITE, false);
#endif
CloseHandle(thread);
}
void SetName(const char* name){
this->name=name;
}
void SetMaxPriority(){
SetThreadPriority(thread, THREAD_PRIORITY_HIGHEST);
}
private:
static const DWORD MS_VC_EXCEPTION=0x406D1388;
#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)
static DWORD WINAPI ActualEntryPoint(void* arg){
Thread* self=reinterpret_cast<Thread*>(arg);
if(self->name){
THREADNAME_INFO info;
info.dwType=0x1000;
info.szName=self->name;
info.dwThreadID=-1;
info.dwFlags=0;
__try{
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info);
}__except(EXCEPTION_EXECUTE_HANDLER){}
}
self->entry->Invoke(self->arg);
return 0;
}
MethodPointerBase* entry;
void* arg;
HANDLE thread;
const char* name;
};
class Semaphore{
public:
Semaphore(unsigned int maxCount, unsigned int initValue){
@ -193,14 +328,14 @@ private:
namespace tgvoip{
class MutexGuard{
public:
MutexGuard(tgvoip_mutex_t &mutex) : mutex(mutex) {
lock_mutex(mutex);
MutexGuard(Mutex &mutex) : mutex(mutex) {
mutex.Lock();
}
~MutexGuard(){
unlock_mutex(mutex);
mutex.Unlock();
}
private:
tgvoip_mutex_t &mutex;
Mutex &mutex;
};
}