diff --git a/BlockingQueue.h b/BlockingQueue.h index 212b861137..92fa318bf3 100644 --- a/BlockingQueue.h +++ b/BlockingQueue.h @@ -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); }; } diff --git a/BufferInputStream.cpp b/BufferInputStream.cpp index f4858bf450..e4319f7093 100644 --- a/BufferInputStream.cpp +++ b/BufferInputStream.cpp @@ -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-offsetoffset) + throw std::out_of_range("buffer underflow"); + offset-=numBytes; +} diff --git a/BufferOutputStream.h b/BufferOutputStream.h index a9eaccf679..fa64408e42 100644 --- a/BufferOutputStream.h +++ b/BufferOutputStream.h @@ -25,6 +25,7 @@ public: unsigned char* GetBuffer(); size_t GetLength(); void Reset(); + void Rewind(size_t numBytes); private: void ExpandBufferIfNeeded(size_t need); diff --git a/BufferPool.cpp b/BufferPool.cpp index 9dc6b488ef..59c03a5b27 100644 --- a/BufferPool.cpp +++ b/BufferPool.cpp @@ -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> 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;iGetInt("audio_congestion_window", 1024); - init_mutex(mutex); } CongestionControl::~CongestionControl(){ - free_mutex(mutex); } size_t CongestionControl::GetAcknowledgedDataSize(){ diff --git a/CongestionControl.h b/CongestionControl.h index 2890f0a654..d58dd98a5c 100644 --- a/CongestionControl.h +++ b/CongestionControl.h @@ -64,7 +64,7 @@ private: uint32_t tickCount; size_t inflightDataSize; size_t cwnd; - tgvoip_mutex_t mutex; + Mutex mutex; }; } diff --git a/EchoCanceller.cpp b/EchoCanceller.cpp index fc28c47096..fa794236d8 100644 --- a/EchoCanceller.cpp +++ b/EchoCanceller.cpp @@ -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::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]; diff --git a/EchoCanceller.h b/EchoCanceller.h index 7a0dcc54b5..f59363608a 100644 --- a/EchoCanceller.h +++ b/EchoCanceller.h @@ -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* farendQueue; BufferPool* farendBufferPool; bool running; diff --git a/JitterBuffer.cpp b/JitterBuffer.cpp index e589389d78..83b9c33267 100644 --- a/JitterBuffer.cpp +++ b/JitterBuffer.cpp @@ -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 && lastPutTimestamptimestamptimestamp); + //LOGW("jitter: would drop packet with timestamp %d because it is late but not hopelessly", pkt->timestamp); latePacketCount++; lostPackets--; }else if(pkt->timestamptimestamp); + //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=resyncThreshold){ wasReset=true; } - /*if(avgLate16>=0.3){ - if(dontIncMinDelay==0 && minDelay<15){ - minDelay++; - if(GetCurrentDelay()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; } diff --git a/JitterBuffer.h b/JitterBuffer.h index 8785ec56d9..6abab45413 100644 --- a/JitterBuffer.h +++ b/JitterBuffer.h @@ -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 }; } diff --git a/MediaStreamItf.cpp b/MediaStreamItf.cpp index 6e8b729530..7e8121ca44 100644 --- a/MediaStreamItf.cpp +++ b/MediaStreamItf.cpp @@ -4,7 +4,13 @@ // you should have received with this source code distribution. // +#include "logging.h" #include "MediaStreamItf.h" +#include "EchoCanceller.h" +#include +#include +#include +#include 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::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::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::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(data); + int16_t input[960]; + float out[960]; + memset(out, 0, 960*4); + int usedInputs=0; + for(std::vector::iterator in=inputs.begin();in!=inputs.end();++in){ + size_t res=in->source->InvokeCallback(reinterpret_cast(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;kabsValue) + 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; + } +} diff --git a/MediaStreamItf.h b/MediaStreamItf.h index 97e236805d..7592545a23 100644 --- a/MediaStreamItf.h +++ b/MediaStreamItf.h @@ -8,8 +8,16 @@ #define LIBTGVOIP_MEDIASTREAMINPUT_H #include +#include +#include +#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 inputs; + Thread* thread; + BufferPool bufferPool; + BlockingQueue 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 diff --git a/NetworkSocket.cpp b/NetworkSocket.cpp index 3f7f02e635..815c47c47c 100644 --- a/NetworkSocket.cpp +++ b/NetworkSocket.cpp @@ -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; diff --git a/OpusDecoder.cpp b/OpusDecoder.cpp index 77fcb8dacc..701611997a 100644 --- a/OpusDecoder.cpp +++ b/OpusDecoder.cpp @@ -10,32 +10,49 @@ #include #include +#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(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(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;iGetBlocking(); - 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(lenGetBlocking(); - } - 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){ 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(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(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(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::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;iAcquire(); if(!running){ LOGI("==== decoder exiting ===="); return; } unsigned char *buf=bufferPool->Get(); if(buf){ - if(size>0){ + if(remainingDataLen>0){ for(std::vector::iterator effect=postProcEffects.begin();effect!=postProcEffects.end();++effect){ (*effect)->Process(reinterpret_cast(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); } diff --git a/OpusDecoder.h b/OpusDecoder.h index 1a88ce5d76..27be0c8e25 100644 --- a/OpusDecoder.h +++ b/OpusDecoder.h @@ -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* 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 postProcEffects; + bool async; + unsigned char nextBuffer[8192]; + unsigned char decodeBuffer[8192]; + bool first; + size_t nextLen; + unsigned int packetsPerFrame; + ssize_t remainingDataLen; }; } diff --git a/OpusEncoder.cpp b/OpusEncoder.cpp index bf42cd7929..eb9bec13a5 100644 --- a/OpusEncoder.cpp +++ b/OpusEncoder.cpp @@ -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::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(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; +} diff --git a/OpusEncoder.h b/OpusEncoder.h index 9329f89ae7..aaf3da162f 100644 --- a/OpusEncoder.h +++ b/OpusEncoder.h @@ -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 queue; BufferPool bufferPool; EchoCanceller* echoCanceller; @@ -53,6 +54,7 @@ private: uint32_t strongCorrectionBitrate; double mediumCorrectionMultiplier; double strongCorrectionMultiplier; + AudioLevelMeter* levelMeter; }; } diff --git a/VoIPController.cpp b/VoIPController.cpp index 2f793f3c01..058927ee6b 100644 --- a/VoIPController.cpp +++ b/VoIPController.cpp @@ -41,11 +41,13 @@ #define PKT_SWITCH_PREF_RELAY 12 #define PKT_SWITCH_TO_P2P 13 #define PKT_NOP 14 +#define PKT_GROUP_CALL_KEY 15 +#define PKT_REQUEST_GROUP 16 #define IS_MOBILE_NETWORK(x) (x==NET_TYPE_GPRS || x==NET_TYPE_EDGE || x==NET_TYPE_3G || x==NET_TYPE_HSPA || x==NET_TYPE_LTE || x==NET_TYPE_OTHER_MOBILE) #define PROTOCOL_NAME 0x50567247 // "GrVP" in little endian (reversed here) -#define PROTOCOL_VERSION 3 +#define PROTOCOL_VERSION 5 #define MIN_PROTOCOL_VERSION 3 #define STREAM_DATA_FLAG_LEN16 0x40 @@ -54,7 +56,10 @@ #define STREAM_TYPE_AUDIO 1 #define STREAM_TYPE_VIDEO 2 -#define CODEC_OPUS 1 +#define FOURCC(a,b,c,d) ((uint32_t)d | ((uint32_t)c << 8) | ((uint32_t)b << 16) | ((uint32_t)a << 24)) + +#define CODEC_OPUS_OLD 1 +#define CODEC_OPUS FOURCC('O','P','U','S') /*flags:# voice_call_id:flags.2?int128 in_seq_no:flags.4?int out_seq_no:flags.4?int * recent_received_mask:flags.5?int proto:flags.3?int extra:flags.1?string raw_data:flags.0?string*/ @@ -64,16 +69,28 @@ #define PFLAG_HAS_PROTO 8 #define PFLAG_HAS_SEQ 16 #define PFLAG_HAS_RECENT_RECV 32 +#define PFLAG_HAS_SENDER_TAG_HASH 64 + +#define STREAM_FLAG_ENABLED 1 +#define STREAM_FLAG_DTX 2 + +#define STREAM_RFLAG_SUPPORTED 1 #define INIT_FLAG_DATA_SAVING_ENABLED 1 +#define INIT_FLAG_GROUP_CALLS_SUPPORTED 2 #define TLID_DECRYPTED_AUDIO_BLOCK 0xDBF948C1 #define TLID_SIMPLE_AUDIO_BLOCK 0xCC0D0E76 #define TLID_UDP_REFLECTOR_PEER_INFO 0x27D9371C #define TLID_UDP_REFLECTOR_PEER_INFO_IPV6 0x83fc73b1 #define TLID_UDP_REFLECTOR_SELF_INFO 0xc01572c7 +#define TLID_UDP_REFLECTOR_REQUEST_PACKETS_INFO 0x1a06fc96 +#define TLID_UDP_REFLECTOR_LAST_PACKETS_INFO 0x0e107305 +#define TLID_VECTOR 0x1cb5c415 #define PAD4(x) (4-(x+(x<=253 ? 1 : 0))%4) +#define MAX_RECENT_PACKETS 64 + #define MAX(a,b) (a>b ? a : b) #define MIN(a,b) (a(21); - init_mutex(sendBufferMutex); - memset(remoteAcks, 0, sizeof(double)*32); - memset(sentPacketTimes, 0, sizeof(double)*32); memset(recvPacketTimes, 0, sizeof(double)*32); memset(rttHistory, 0, sizeof(double)*32); memset(sendLossCountHistory, 0, sizeof(uint32_t)*32); @@ -195,7 +220,6 @@ VoIPController::VoIPController() : activeNetItfName(""), packetsRecieved=0; waitingForAcks=false; networkType=NET_TYPE_UNKNOWN; - stateCallback=NULL; echoCanceller=NULL; dontSendPackets=0; micMuted=false; @@ -204,8 +228,6 @@ VoIPController::VoIPController() : activeNetItfName(""), allowP2p=true; dataSavingMode=false; publicEndpointsReqTime=0; - init_mutex(queuedPacketsMutex); - init_mutex(endpointsMutex); connectionInitTime=0; lastRecvPacketTime=0; dataSavingRequestedByPeer=false; @@ -229,7 +251,6 @@ VoIPController::VoIPController() : activeNetItfName(""), resolvedProxyAddress=NULL; signalBarCount=0; - signalBarCountCallback=NULL; selectCanceller=SocketSelectCanceller::Create(); udpSocket=NetworkSocket::Create(PROTO_UDP); @@ -239,6 +260,21 @@ VoIPController::VoIPController() : activeNetItfName(""), outputAGC=NULL; outputAGCEnabled=false; + peerCapabilities=0; + callbacks={0}; + didReceiveGroupCallKey=false; + didReceiveGroupCallKeyAck=false; + didSendGroupCallKey=false; + didSendUpgradeRequest=false; + didInvokeUpdateCallback=false; + + connectionMaxLayer=0; + useMTProto2=false; + setCurrentEndpointToTCP=false; + + sendThread=NULL; + recvThread=NULL; + tickThread=NULL; maxAudioBitrate=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("audio_max_bitrate", 20000); maxAudioBitrateGPRS=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("audio_max_bitrate_gprs", 8000); @@ -257,59 +293,71 @@ VoIPController::VoIPController() : activeNetItfName(""), reconnectingTimeout=ServerConfig::GetSharedInstance()->GetDouble("reconnecting_state_timeout", 2.0); #ifdef __APPLE__ - machTimestart=0; -#ifdef TGVOIP_USE_AUDIO_SESSION - needNotifyAcquiredAudioSession=false; + machTimestart=0; +#if TARGET_OS_OSX + if(kCFCoreFoundationVersionNumber>=kCFCoreFoundationVersionNumber10_7){ +#endif + appleAudioIO=new audio::AudioUnitIO(); +#if TARGET_OS_OSX + }else{ + appleAudioIO=NULL; + } #endif #endif - voip_stream_t* stm=(voip_stream_t *) malloc(sizeof(voip_stream_t)); - stm->id=1; - stm->type=STREAM_TYPE_AUDIO; - stm->codec=CODEC_OPUS; - stm->enabled=1; - stm->frameDuration=60; + Stream stm; + stm.id=1; + stm.type=STREAM_TYPE_AUDIO; + stm.codec=CODEC_OPUS; + stm.enabled=1; + stm.frameDuration=60; outgoingStreams.push_back(stm); - + memset(signalBarsHistory, 0, sizeof(signalBarsHistory)); } +VoIPGroupController::VoIPGroupController(int32_t timeDifference){ + audioMixer=new AudioMixer(); + memset(&callbacks, 0, sizeof(callbacks)); + userSelfID=0; + this->timeDifference=timeDifference; + LOGV("Created VoIPGroupController; timeDifference=%d", timeDifference); +} + +VoIPGroupController::~VoIPGroupController(){ + if(audioOutput){ + audioOutput->Stop(); + } + LOGD("before stop audio mixer"); + audioMixer->Stop(); + delete audioMixer; + + for(std::vector::iterator p=participants.begin();p!=participants.end();p++){ + if(p->levelMeter) + delete p->levelMeter; + } +} + VoIPController::~VoIPController(){ LOGD("Entered VoIPController::~VoIPController"); - if(audioInput) - audioInput->Stop(); - if(audioOutput) - audioOutput->Stop(); - stopping=true; - runReceiver=false; - LOGD("before shutdown socket"); - if(udpSocket) - udpSocket->Close(); - if(realUdpSocket!=udpSocket) - realUdpSocket->Close(); - selectCanceller->CancelSelect(); - sendQueue->Put(PendingOutgoingPacket{0}); - if(openingTcpSocket) - openingTcpSocket->Close(); - LOGD("before join sendThread"); - join_thread(sendThread); - LOGD("before join recvThread"); - join_thread(recvThread); - LOGD("before join tickThread"); - join_thread(tickThread); - free_mutex(sendBufferMutex); + if(!stopping){ + LOGE("!!!!!!!!!!!!!!!!!!!! CALL controller->Stop() BEFORE DELETING THE CONTROLLER OBJECT !!!!!!!!!!!!!!!!!!!!!!!1"); + abort(); + } LOGD("before close socket"); if(udpSocket) delete udpSocket; if(udpSocket!=realUdpSocket) delete realUdpSocket; - LOGD("before delete jitter buffer"); - if(jitterBuffer){ - delete jitterBuffer; - } - LOGD("before stop decoder"); - if(decoder){ - decoder->Stop(); + for(std::vector::iterator stm=incomingStreams.begin();stm!=incomingStreams.end();++stm){ + LOGD("before delete jitter buffer"); + if(stm->jitterBuffer){ + delete stm->jitterBuffer; + } + LOGD("before stop decoder"); + if(stm->decoder){ + stm->decoder->Stop(); + } } LOGD("before delete audio input"); if(audioInput){ @@ -324,9 +372,17 @@ VoIPController::~VoIPController(){ if(audioOutput){ delete audioOutput; } - LOGD("before delete decoder"); - if(decoder){ - delete decoder; +#ifdef __APPLE__ + LOGD("before delete AudioUnitIO"); + if(appleAudioIO){ + delete appleAudioIO; + } +#endif + for(std::vector::iterator stm=incomingStreams.begin();stm!=incomingStreams.end();++stm){ + LOGD("before delete decoder"); + if(stm->decoder){ + delete stm->decoder; + } } LOGD("before delete echo canceller"); if(echoCanceller){ @@ -335,16 +391,6 @@ VoIPController::~VoIPController(){ } delete sendQueue; unsigned int i; - for(i=0;idata) free(queuedPackets[i]->data); @@ -373,51 +419,69 @@ VoIPController::~VoIPController(){ LOGD("Left VoIPController::~VoIPController"); } -void VoIPController::SetRemoteEndpoints(std::vector endpoints, bool allowP2p){ - LOGW("Set remote endpoints"); - preferredRelay=NULL; - size_t i; - lock_mutex(endpointsMutex); - this->endpoints.clear(); - didAddTcpRelays=false; - useTCP=true; - for(std::vector::iterator itrtr=endpoints.begin();itrtr!=endpoints.end();++itrtr){ - this->endpoints.push_back(new Endpoint(*itrtr)); - if(itrtr->type==EP_TYPE_TCP_RELAY) - didAddTcpRelays=true; - if(itrtr->type==EP_TYPE_UDP_RELAY) - useTCP=false; - LOGV("Adding endpoint: %s:%d, %s", itrtr->address.ToString().c_str(), itrtr->port, itrtr->type==EP_TYPE_UDP_RELAY ? "UDP" : "TCP"); +void VoIPController::Stop(){ + LOGD("Entered VoIPController::Stop"); + if(audioInput) + audioInput->Stop(); + if(audioOutput) + audioOutput->Stop(); + stopping=true; + runReceiver=false; + LOGD("before shutdown socket"); + if(udpSocket) + udpSocket->Close(); + if(realUdpSocket!=udpSocket) + realUdpSocket->Close(); + selectCanceller->CancelSelect(); + sendQueue->Put(PendingOutgoingPacket{0}); + if(openingTcpSocket) + openingTcpSocket->Close(); + LOGD("before join sendThread"); + if(sendThread){ + sendThread->Join(); + delete sendThread; + } + LOGD("before join recvThread"); + if(recvThread){ + recvThread->Join(); + delete recvThread; + } + LOGD("before join tickThread"); + if(tickThread){ + tickThread->Join(); + delete tickThread; + } + LOGD("Left VoIPController::Stop"); +} + +void VoIPController::SetRemoteEndpoints(std::vector endpoints, bool allowP2p, int32_t connectionMaxLayer){ + LOGW("Set remote endpoints, allowP2P=%d, connectionMaxLayer=%u", allowP2p ? 1 : 0, connectionMaxLayer); + preferredRelay=NULL; + { + MutexGuard m(endpointsMutex); + this->endpoints.clear(); + didAddTcpRelays=false; + useTCP=true; + for(std::vector::iterator itrtr=endpoints.begin();itrtr!=endpoints.end();++itrtr){ + this->endpoints.push_back(new Endpoint(*itrtr)); + if(itrtr->type==Endpoint::TYPE_TCP_RELAY) + didAddTcpRelays=true; + if(itrtr->type==Endpoint::TYPE_UDP_RELAY) + useTCP=false; + LOGV("Adding endpoint: %s:%d, %s", itrtr->address.ToString().c_str(), itrtr->port, itrtr->type==Endpoint::TYPE_UDP_RELAY ? "UDP" : "TCP"); + } } - unlock_mutex(endpointsMutex); currentEndpoint=this->endpoints[0]; preferredRelay=currentEndpoint; this->allowP2p=allowP2p; + this->connectionMaxLayer=connectionMaxLayer; + if(connectionMaxLayer>=74){ + useMTProto2=true; + } } -void* VoIPController::StartRecvThread(void* controller){ - ((VoIPController*)controller)->RunRecvThread(); - return NULL; -} - -void* VoIPController::StartSendThread(void* controller){ - ((VoIPController*)controller)->RunSendThread(); - return NULL; -} - - -void* VoIPController::StartTickThread(void* controller){ - ((VoIPController*) controller)->RunTickThread(); - return NULL; -} - - void VoIPController::Start(){ - int res; LOGW("Starting voip controller"); - int32_t cfgFrameSize=60; //ServerConfig::GetSharedInstance()->GetInt("audio_frame_size", 60); - if(cfgFrameSize==20 || cfgFrameSize==40 || cfgFrameSize==60) - outgoingStreams[0]->frameDuration=(uint16_t) cfgFrameSize; udpSocket->Open(); if(udpSocket->IsFailed()){ SetState(STATE_FAILED); @@ -427,15 +491,15 @@ void VoIPController::Start(){ //SendPacket(NULL, 0, currentEndpoint); runReceiver=true; - start_thread(recvThread, StartRecvThread, this); - set_thread_priority(recvThread, get_thread_max_priority()); - set_thread_name(recvThread, "voip-recv"); - start_thread(sendThread, StartSendThread, this); - set_thread_priority(sendThread, get_thread_max_priority()); - set_thread_name(sendThread, "voip-send"); - start_thread(tickThread, StartTickThread, this); - set_thread_priority(tickThread, get_thread_max_priority()); - set_thread_name(tickThread, "voip-tick"); + recvThread=new Thread(new MethodPointer(&VoIPController::RunRecvThread, this), NULL); + recvThread->SetName("VoipRecv"); + recvThread->Start(); + sendThread=new Thread(new MethodPointer(&VoIPController::RunSendThread, this), NULL); + sendThread->SetName("VoipSend"); + sendThread->Start(); + tickThread=new Thread(new MethodPointer(&VoIPController::RunTickThread, this), NULL); + tickThread->SetName("VoipTick"); + tickThread->Start(); } size_t VoIPController::AudioInputCallback(unsigned char* data, size_t length, void* param){ @@ -474,7 +538,7 @@ void VoIPController::HandleAudioInput(unsigned char *data, size_t len){ sendQueue->Put(p); } - audioTimestampOut+=outgoingStreams[0]->frameDuration; + audioTimestampOut+=outgoingStreams[0].frameDuration; } void VoIPController::Connect(){ @@ -504,6 +568,181 @@ void VoIPController::SetEncryptionKey(char *key, bool isOutgoing){ this->isOutgoing=isOutgoing; } +void VoIPGroupController::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){ + Endpoint* e=new Endpoint(); + e->address=reflectorAddress; + e->v6address=reflectorAddressV6; + e->port=reflectorPort; + memcpy(e->peerTag, reflectorGroupTag, 16); + e->type=Endpoint::TYPE_UDP_RELAY; + endpoints.push_back(e); + groupReflector=e; + currentEndpoint=e; + + memcpy(this->encryptionKey, encryptionKey, 256); + memcpy(this->reflectorSelfTag, reflectorSelfTag, 16); + memcpy(this->reflectorSelfSecret, reflectorSelfSecret, 16); + memcpy(this->reflectorSelfTagHash, reflectorSelfTagHash, 16); + uint8_t sha256[SHA256_LENGTH]; + crypto.sha256((uint8_t*) encryptionKey, 256, sha256); + memcpy(callID, sha256+(SHA256_LENGTH-16), 16); + memcpy(keyFingerprint, sha256+(SHA256_LENGTH-16), 8); + this->userSelfID=selfUserID; + + //LOGD("reflectorSelfTag = %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", reflectorSelfTag[0], reflectorSelfTag[1], reflectorSelfTag[2], reflectorSelfTag[3], reflectorSelfTag[4], reflectorSelfTag[5], reflectorSelfTag[6], reflectorSelfTag[7], reflectorSelfTag[8], reflectorSelfTag[9], reflectorSelfTag[10], reflectorSelfTag[11], reflectorSelfTag[12], reflectorSelfTag[13], reflectorSelfTag[14], reflectorSelfTag[15]); + //LOGD("reflectorSelfSecret = %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", reflectorSelfSecret[0], reflectorSelfSecret[1], reflectorSelfSecret[2], reflectorSelfSecret[3], reflectorSelfSecret[4], reflectorSelfSecret[5], reflectorSelfSecret[6], reflectorSelfSecret[7], reflectorSelfSecret[8], reflectorSelfSecret[9], reflectorSelfSecret[10], reflectorSelfSecret[11], reflectorSelfSecret[12], reflectorSelfSecret[13], reflectorSelfSecret[14], reflectorSelfSecret[15]); + //LOGD("reflectorSelfTagHash = %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", reflectorSelfTagHash[0], reflectorSelfTagHash[1], reflectorSelfTagHash[2], reflectorSelfTagHash[3], reflectorSelfTagHash[4], reflectorSelfTagHash[5], reflectorSelfTagHash[6], reflectorSelfTagHash[7], reflectorSelfTagHash[8], reflectorSelfTagHash[9], reflectorSelfTagHash[10], reflectorSelfTagHash[11], reflectorSelfTagHash[12], reflectorSelfTagHash[13], reflectorSelfTagHash[14], reflectorSelfTagHash[15]); +} + +void VoIPGroupController::AddGroupCallParticipant(int32_t userID, unsigned char *memberTagHash, unsigned char* serializedStreams, size_t streamsLength){ + if(userID==userSelfID) + return; + if(userSelfID==0) + return; + //if(streamsLength==0) + // return; + MutexGuard m(participantsMutex); + LOGV("Adding group call user %d, streams length %u, streams %X", userID, streamsLength, serializedStreams); + + for(std::vector::iterator p=participants.begin();p!=participants.end();++p){ + if(p->userID==userID){ + LOGE("user %d already added", userID); + abort(); + break; + } + } + + GroupCallParticipant p; + p.userID=userID; + memcpy(p.memberTagHash, memberTagHash, sizeof(p.memberTagHash)); + p.levelMeter=new AudioLevelMeter(); + + BufferInputStream ss(serializedStreams, streamsLength); + std::vector streams=DeserializeStreams(ss); + + unsigned char audioStreamID=0; + + for(std::vector::iterator s=streams.begin();s!=streams.end();++s){ + s->userID=userID; + if(s->type==STREAM_TYPE_AUDIO && s->codec==CODEC_OPUS && !audioStreamID){ + audioStreamID=s->id; + s->jitterBuffer=new JitterBuffer(NULL, s->frameDuration); + if(s->frameDuration>50) + s->jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_60", 2)); + else if(s->frameDuration>30) + s->jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_40", 4)); + else + s->jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_20", 6)); + s->callbackWrapper=new CallbackWrapper(); + s->decoder=new OpusDecoder(s->callbackWrapper, false); + s->decoder->SetJitterBuffer(s->jitterBuffer); + s->decoder->SetFrameDuration(s->frameDuration); + s->decoder->SetDTX(true); + s->decoder->SetLevelMeter(p.levelMeter); + audioMixer->AddInput(s->callbackWrapper); + } + incomingStreams.push_back(*s); + } + + if(!audioStreamID){ + LOGW("User %d has no usable audio stream", userID); + } + + p.streams.insert(p.streams.end(), streams.begin(), streams.end()); + participants.push_back(p); + LOGI("Added group call participant %d", userID); +} + +void VoIPGroupController::RemoveGroupCallParticipant(int32_t userID){ + MutexGuard m(participantsMutex); + std::vector::iterator stm=incomingStreams.begin(); + while(stm!=incomingStreams.end()){ + if(stm->userID==userID){ + LOGI("Removed stream %d belonging to user %d", stm->id, userID); + audioMixer->RemoveInput(stm->callbackWrapper); + stm->decoder->Stop(); + delete stm->decoder; + delete stm->jitterBuffer; + delete stm->callbackWrapper; + stm=incomingStreams.erase(stm); + continue; + } + ++stm; + } + for(std::vector::iterator p=participants.begin();p!=participants.end();++p){ + if(p->userID==userID){ + if(p->levelMeter) + delete p->levelMeter; + participants.erase(p); + LOGI("Removed group call participant %d", userID); + break; + } + } +} + +std::vector VoIPGroupController::DeserializeStreams(BufferInputStream& in){ + std::vector res; + try{ + unsigned char count=in.ReadByte(); + for(unsigned char i=0;i::iterator p=participants.begin();p!=participants.end();++p){ + if(p->userID==userID){ + BufferInputStream in(serializedStreams, length); + std::vector streams=DeserializeStreams(in); + for(std::vector::iterator ns=streams.begin();ns!=streams.end();++ns){ + bool found=false; + for(std::vector::iterator s=p->streams.begin();s!=p->streams.end();++s){ + if(s->id==ns->id){ + s->enabled=ns->enabled; + if(groupCallbacks.participantAudioStateChanged) + groupCallbacks.participantAudioStateChanged(this, userID, s->enabled); + found=true; + break; + } + } + if(!found){ + LOGW("Tried to add stream %d for user %d but adding/removing streams is not supported", ns->id, userID); + } + } + break; + } + } +} + +size_t VoIPGroupController::GetInitialStreams(unsigned char *buf, size_t size){ + BufferOutputStream s(buf, size); + s.WriteByte(1); // streams count + + s.WriteInt16(12); // this object length + s.WriteByte(1); // stream id + s.WriteByte(STREAM_TYPE_AUDIO); + s.WriteInt32(CODEC_OPUS); + s.WriteInt32(STREAM_FLAG_ENABLED | STREAM_FLAG_DTX); // flags + s.WriteInt16(60); // frame duration + + return s.GetLength(); +} + uint32_t VoIPController::GenerateOutSeq(){ return seq++; } @@ -584,8 +823,15 @@ void VoIPController::WritePacketHeader(uint32_t pseq, BufferOutputStream *s, uns if(type==PKT_STREAM_DATA || type==PKT_STREAM_DATA_X2 || type==PKT_STREAM_DATA_X3) conctl->PacketSent(pseq, length); - memmove(&sentPacketTimes[1], sentPacketTimes, 31*sizeof(double)); - sentPacketTimes[0]=GetCurrentTime(); + MutexGuard m(queuedPacketsMutex); + recentOutgoingPackets.push_back(RecentOutgoingPacket{ + pseq, + 0, + GetCurrentTime(), + 0 + }); + while(recentOutgoingPackets.size()>MAX_RECENT_PACKETS) + recentOutgoingPackets.erase(recentOutgoingPackets.begin()); lastSentSeq=pseq; //LOGI("packet header size %d", s->GetLength()); } @@ -611,39 +857,55 @@ void VoIPController::UpdateAudioBitrate(){ void VoIPController::SendInit(){ - lock_mutex(endpointsMutex); - uint32_t initSeq=GenerateOutSeq(); - for(std::vector::iterator itr=endpoints.begin();itr!=endpoints.end();++itr){ - if((*itr)->type==EP_TYPE_TCP_RELAY && !useTCP) - continue; - unsigned char* pkt=outgoingPacketsBufferPool.Get(); - if(!pkt){ - LOGW("can't send init, queue overflow"); - continue; + { + MutexGuard m(endpointsMutex); + uint32_t initSeq=GenerateOutSeq(); + for(std::vector::iterator itr=endpoints.begin(); itr!=endpoints.end(); ++itr){ + if((*itr)->type==Endpoint::TYPE_TCP_RELAY && !useTCP) + continue; + unsigned char *pkt=outgoingPacketsBufferPool.Get(); + if(!pkt){ + LOGW("can't send init, queue overflow"); + continue; + } + BufferOutputStream out(pkt, outgoingPacketsBufferPool.GetSingleBufferSize()); + out.WriteInt32(PROTOCOL_VERSION); + out.WriteInt32(MIN_PROTOCOL_VERSION); + uint32_t flags=0; + if(config.enableCallUpgrade) + flags|=INIT_FLAG_GROUP_CALLS_SUPPORTED; + if(dataSavingMode) + flags|=INIT_FLAG_DATA_SAVING_ENABLED; + out.WriteInt32(flags); + if(connectionMaxLayer<74){ + out.WriteByte(2); // audio codecs count + out.WriteByte(CODEC_OPUS_OLD); + out.WriteByte(0); + out.WriteByte(0); + out.WriteByte(0); + out.WriteInt32(CODEC_OPUS); + out.WriteByte(0); // video codecs count + }else{ + out.WriteByte(1); + out.WriteInt32(CODEC_OPUS); + out.WriteByte(0); + } + sendQueue->Put(PendingOutgoingPacket{ + /*.seq=*/initSeq, + /*.type=*/PKT_INIT, + /*.len=*/out.GetLength(), + /*.data=*/pkt, + /*.endpoint=*/*itr + }); } - BufferOutputStream out(pkt, outgoingPacketsBufferPool.GetSingleBufferSize()); - //WritePacketHeader(out, PKT_INIT, 15); - out.WriteInt32(PROTOCOL_VERSION); - out.WriteInt32(MIN_PROTOCOL_VERSION); - uint32_t flags=0; - if(dataSavingMode) - flags|=INIT_FLAG_DATA_SAVING_ENABLED; - out.WriteInt32(flags); - out.WriteByte(1); // audio codecs count - out.WriteByte(CODEC_OPUS); - out.WriteByte(0); // video codecs count - sendQueue->Put(PendingOutgoingPacket{ - /*.seq=*/initSeq, - /*.type=*/PKT_INIT, - /*.len=*/out.GetLength(), - /*.data=*/pkt, - /*.endpoint=*/*itr - }); } - unlock_mutex(endpointsMutex); SetState(STATE_WAIT_INIT_ACK); } +void VoIPGroupController::SendInit(){ + SendRecentPacketsRequest(); +} + void VoIPController::InitUDPProxy(){ if(realUdpSocket!=udpSocket){ udpSocket->Close(); @@ -672,13 +934,13 @@ void VoIPController::InitUDPProxy(){ } } -void VoIPController::RunRecvThread(){ +void VoIPController::RunRecvThread(void* arg){ LOGI("Receive thread starting"); - unsigned char buffer[1024]; - NetworkPacket packet; + unsigned char *buffer = (unsigned char *)malloc(1500); + NetworkPacket packet={0}; while(runReceiver){ packet.data=buffer; - packet.length=1024; + packet.length=1500; std::vector readSockets; std::vector errorSockets; @@ -687,7 +949,7 @@ void VoIPController::RunRecvThread(){ //if(useTCP){ for(std::vector::iterator itr=endpoints.begin();itr!=endpoints.end();++itr){ - if((*itr)->type==EP_TYPE_TCP_RELAY){ + if((*itr)->type==Endpoint::TYPE_TCP_RELAY){ if((*itr)->socket){ readSockets.push_back((*itr)->socket); errorSockets.push_back((*itr)->socket); @@ -720,7 +982,7 @@ void VoIPController::RunRecvThread(){ socket=readSockets[0]; }else{ LOGI("no sockets to read from"); - lock_mutex(endpointsMutex); + MutexGuard m(endpointsMutex); for(std::vector::iterator itr=errorSockets.begin();itr!=errorSockets.end();++itr){ for(std::vector::iterator e=endpoints.begin();e!=endpoints.end();++e){ if((*e)->socket && (*e)->socket==*itr){ @@ -732,7 +994,6 @@ void VoIPController::RunRecvThread(){ } } } - unlock_mutex(endpointsMutex); continue; } @@ -751,16 +1012,15 @@ void VoIPController::RunRecvThread(){ IPv4Address* src4=dynamic_cast(packet.address); if(src4){ - lock_mutex(endpointsMutex); + MutexGuard m(endpointsMutex); for(std::vector::iterator itrtr=endpoints.begin();itrtr!=endpoints.end();++itrtr){ if((*itrtr)->address==*src4 && (*itrtr)->port==packet.port){ - if(((*itrtr)->type!=EP_TYPE_TCP_RELAY && packet.protocol==PROTO_UDP) || ((*itrtr)->type==EP_TYPE_TCP_RELAY && packet.protocol==PROTO_TCP)){ + if(((*itrtr)->type!=Endpoint::TYPE_TCP_RELAY && packet.protocol==PROTO_UDP) || ((*itrtr)->type==Endpoint::TYPE_TCP_RELAY && packet.protocol==PROTO_TCP)){ srcEndpoint=*itrtr; break; } } } - unlock_mutex(endpointsMutex); } if(!srcEndpoint){ @@ -775,586 +1035,29 @@ void VoIPController::RunRecvThread(){ stats.bytesRecvdMobile+=(uint64_t)len; else stats.bytesRecvdWifi+=(uint64_t)len; - BufferInputStream in(buffer, (size_t)len); try{ - if(memcmp(buffer, srcEndpoint->type==EP_TYPE_UDP_RELAY || srcEndpoint->type==EP_TYPE_TCP_RELAY ? (void*)srcEndpoint->peerTag : (void*)callID, 16)!=0){ - LOGW("Received packet has wrong peerTag"); - - continue; - } - in.Seek(16); - if(in.Remaining()>=16 && (srcEndpoint->type==EP_TYPE_UDP_RELAY || srcEndpoint->type==EP_TYPE_TCP_RELAY) - && *reinterpret_cast(buffer+16)==0xFFFFFFFFFFFFFFFFLL && *reinterpret_cast(buffer+24)==0xFFFFFFFF){ - // relay special request response - in.Seek(16+12); - uint32_t tlid=(uint32_t) in.ReadInt32(); - - if(tlid==TLID_UDP_REFLECTOR_SELF_INFO){ - if(srcEndpoint->type==EP_TYPE_UDP_RELAY && udpConnectivityState==UDP_PING_SENT && in.Remaining()>=32){ - int32_t date=in.ReadInt32(); - int64_t queryID=in.ReadInt64(); - unsigned char myIP[16]; - in.ReadBytes(myIP, 16); - int32_t myPort=in.ReadInt32(); - udpConnectivityState=UDP_AVAILABIE; - //LOGV("Received UDP ping reply from %s:%d: date=%d, queryID=%lld, my IP=%s, my port=%d", srcEndpoint->address.ToString().c_str(), srcEndpoint->port, date, queryID, IPv6Address(myIP).ToString().c_str(), myPort); - } - }else if(tlid==TLID_UDP_REFLECTOR_PEER_INFO){ - if(waitingForRelayPeerInfo && in.Remaining()>=16){ - lock_mutex(endpointsMutex); - uint32_t myAddr=(uint32_t) in.ReadInt32(); - uint32_t myPort=(uint32_t) in.ReadInt32(); - uint32_t peerAddr=(uint32_t) in.ReadInt32(); - uint32_t peerPort=(uint32_t) in.ReadInt32(); - for(std::vector::iterator itrtr=endpoints.begin(); itrtr!=endpoints.end(); ++itrtr){ - Endpoint *ep=*itrtr; - if(ep->type==EP_TYPE_UDP_P2P_INET){ - if(currentEndpoint==ep) - currentEndpoint=preferredRelay; - delete ep; - endpoints.erase(itrtr); - break; - } - } - for(std::vector::iterator itrtr=endpoints.begin(); itrtr!=endpoints.end(); ++itrtr){ - Endpoint *ep=*itrtr; - if(ep->type==EP_TYPE_UDP_P2P_LAN){ - if(currentEndpoint==ep) - currentEndpoint=preferredRelay; - delete ep; - endpoints.erase(itrtr); - break; - } - } - IPv4Address _peerAddr(peerAddr); - IPv6Address emptyV6("::0"); - unsigned char peerTag[16]; - endpoints.push_back(new Endpoint(0, (uint16_t) peerPort, _peerAddr, emptyV6, EP_TYPE_UDP_P2P_INET, peerTag)); - LOGW("Received reflector peer info, my=%08X:%u, peer=%08X:%u", myAddr, myPort, peerAddr, peerPort); - if(myAddr==peerAddr){ - LOGW("Detected LAN"); - IPv4Address lanAddr(0); - udpSocket->GetLocalInterfaceInfo(&lanAddr, NULL); - - BufferOutputStream pkt(8); - pkt.WriteInt32(lanAddr.GetAddress()); - pkt.WriteInt32(udpSocket->GetLocalPort()); - SendPacketReliably(PKT_LAN_ENDPOINT, pkt.GetBuffer(), pkt.GetLength(), 0.5, 10); - } - unlock_mutex(endpointsMutex); - waitingForRelayPeerInfo=false; - } - }else{ - LOGV("Received relay response with unknown tl id: 0x%08X", tlid); - } - continue; - } - if(in.Remaining()<40){ - LOGV("Received packet is too small"); - continue; - } - - unsigned char fingerprint[8], msgHash[16]; - in.ReadBytes(fingerprint, 8); - in.ReadBytes(msgHash, 16); - if(memcmp(fingerprint, keyFingerprint, 8)!=0){ - LOGW("Received packet has wrong key fingerprint"); - - continue; - } - unsigned char key[32], iv[32]; - KDF(msgHash, isOutgoing ? 8 : 0, key, iv); - unsigned char aesOut[MSC_STACK_FALLBACK(in.Remaining(), 1024)]; - crypto.aes_ige_decrypt((unsigned char *) buffer+in.GetOffset(), aesOut, in.Remaining(), key, iv); - memcpy(buffer+in.GetOffset(), aesOut, in.Remaining()); - unsigned char sha[SHA1_LENGTH]; - uint32_t _len=(uint32_t) in.ReadInt32(); - if(_len>in.Remaining()) - _len=in.Remaining(); - crypto.sha1((uint8_t *) (buffer+in.GetOffset()-4), (size_t) (_len+4), sha); - if(memcmp(msgHash, sha+(SHA1_LENGTH-16), 16)!=0){ - LOGW("Received packet has wrong hash after decryption"); - - continue; - } - - lastRecvPacketTime=GetCurrentTime(); - - - /*decryptedAudioBlock random_id:long random_bytes:string flags:# voice_call_id:flags.2?int128 in_seq_no:flags.4?int out_seq_no:flags.4?int - * recent_received_mask:flags.5?int proto:flags.3?int extra:flags.1?string raw_data:flags.0?string = DecryptedAudioBlock -simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedAudioBlock; -*/ - uint32_t ackId, pseq, acks; - unsigned char type; - uint32_t tlid=(uint32_t) in.ReadInt32(); - uint32_t packetInnerLen; - if(tlid==TLID_DECRYPTED_AUDIO_BLOCK){ - in.ReadInt64(); // random id - uint32_t randLen=(uint32_t) in.ReadTlLength(); - in.Seek(in.GetOffset()+randLen+pad4(randLen)); - uint32_t flags=(uint32_t) in.ReadInt32(); - type=(unsigned char) ((flags >> 24) & 0xFF); - if(!(flags & PFLAG_HAS_SEQ && flags & PFLAG_HAS_RECENT_RECV)){ - LOGW("Received packet doesn't have PFLAG_HAS_SEQ, PFLAG_HAS_RECENT_RECV, or both"); - - continue; - } - if(flags & PFLAG_HAS_CALL_ID){ - unsigned char pktCallID[16]; - in.ReadBytes(pktCallID, 16); - if(memcmp(pktCallID, callID, 16)!=0){ - LOGW("Received packet has wrong call id"); - - lastError=TGVOIP_ERROR_UNKNOWN; - SetState(STATE_FAILED); - return; - } - } - ackId=(uint32_t) in.ReadInt32(); - pseq=(uint32_t) in.ReadInt32(); - acks=(uint32_t) in.ReadInt32(); - if(flags & PFLAG_HAS_PROTO){ - uint32_t proto=(uint32_t) in.ReadInt32(); - if(proto!=PROTOCOL_NAME){ - LOGW("Received packet uses wrong protocol"); - - lastError=TGVOIP_ERROR_INCOMPATIBLE; - SetState(STATE_FAILED); - return; - } - } - if(flags & PFLAG_HAS_EXTRA){ - uint32_t extraLen=(uint32_t) in.ReadTlLength(); - in.Seek(in.GetOffset()+extraLen+pad4(extraLen)); - } - if(flags & PFLAG_HAS_DATA){ - packetInnerLen=in.ReadTlLength(); - } - }else if(tlid==TLID_SIMPLE_AUDIO_BLOCK){ - in.ReadInt64(); // random id - uint32_t randLen=(uint32_t) in.ReadTlLength(); - in.Seek(in.GetOffset()+randLen+pad4(randLen)); - packetInnerLen=in.ReadTlLength(); - type=in.ReadByte(); - ackId=(uint32_t) in.ReadInt32(); - pseq=(uint32_t) in.ReadInt32(); - acks=(uint32_t) in.ReadInt32(); - }else{ - LOGW("Received a packet of unknown type %08X", tlid); - - continue; - } - packetsRecieved++; - if(seqgt(pseq, lastRemoteSeq)){ - uint32_t diff=pseq-lastRemoteSeq; - if(diff>31){ - memset(recvPacketTimes, 0, 32*sizeof(double)); - }else{ - memmove(&recvPacketTimes[diff], recvPacketTimes, (32-diff)*sizeof(double)); - if(diff>1){ - memset(recvPacketTimes, 0, diff*sizeof(double)); - } - recvPacketTimes[0]=GetCurrentTime(); - } - lastRemoteSeq=pseq; - }else if(!seqgt(pseq, lastRemoteSeq) && lastRemoteSeq-pseq<32){ - if(recvPacketTimes[lastRemoteSeq-pseq]!=0){ - LOGW("Received duplicated packet for seq %u", pseq); - - continue; - } - recvPacketTimes[lastRemoteSeq-pseq]=GetCurrentTime(); - }else if(lastRemoteSeq-pseq>=32){ - LOGW("Packet %u is out of order and too late", pseq); - - continue; - } - if(seqgt(ackId, lastRemoteAckSeq)){ - uint32_t diff=ackId-lastRemoteAckSeq; - if(diff>31){ - memset(remoteAcks, 0, 32*sizeof(double)); - }else{ - memmove(&remoteAcks[diff], remoteAcks, (32-diff)*sizeof(double)); - if(diff>1){ - memset(remoteAcks, 0, diff*sizeof(double)); - } - remoteAcks[0]=GetCurrentTime(); - } - if(waitingForAcks && lastRemoteAckSeq>=firstSentPing){ - memset(rttHistory, 0, 32*sizeof(double)); - waitingForAcks=false; - dontSendPackets=10; - LOGI("resuming sending"); - } - lastRemoteAckSeq=ackId; - conctl->PacketAcknowledged(ackId); - int i; - for(i=0;i<31;i++){ - if(remoteAcks[i+1]==0){ - if((acks >> (31-i)) & 1){ - remoteAcks[i+1]=GetCurrentTime(); - conctl->PacketAcknowledged(ackId-(i+1)); - } - } - } - lock_mutex(queuedPacketsMutex); - for(i=0;iseqs[j]); - if(qp->seqs[j]==0) - break; - int remoteAcksIndex=lastRemoteAckSeq-qp->seqs[j]; - LOGV("remote acks index %u, value %f", remoteAcksIndex, remoteAcksIndex>=0 && remoteAcksIndex<32 ? remoteAcks[remoteAcksIndex] : -1); - if(seqgt(lastRemoteAckSeq, qp->seqs[j]) && remoteAcksIndex>=0 && remoteAcksIndex<32 && remoteAcks[remoteAcksIndex]>0){ - LOGD("did ack seq %u, removing", qp->seqs[j]); - didAck=true; - break; - } - } - if(didAck){ - if(qp->data) - free(qp->data); - free(qp); - queuedPackets.erase(queuedPackets.begin()+i); - i--; - continue; - } - } - unlock_mutex(queuedPacketsMutex); - } - - if(srcEndpoint!=currentEndpoint && (srcEndpoint->type==EP_TYPE_UDP_RELAY || srcEndpoint->type==EP_TYPE_TCP_RELAY) && ((currentEndpoint->type!=EP_TYPE_UDP_RELAY && currentEndpoint->type!=EP_TYPE_TCP_RELAY) || currentEndpoint->averageRTT==0)){ - if(seqgt(lastSentSeq-32, lastRemoteAckSeq)){ - currentEndpoint=srcEndpoint; - LOGI("Peer network address probably changed, switching to relay"); - if(allowP2p) - SendPublicEndpointsRequest(); - } - } - //LOGV("acks: %u -> %.2lf, %.2lf, %.2lf, %.2lf, %.2lf, %.2lf, %.2lf, %.2lf", lastRemoteAckSeq, remoteAcks[0], remoteAcks[1], remoteAcks[2], remoteAcks[3], remoteAcks[4], remoteAcks[5], remoteAcks[6], remoteAcks[7]); - //LOGD("recv: %u -> %.2lf, %.2lf, %.2lf, %.2lf, %.2lf, %.2lf, %.2lf, %.2lf", lastRemoteSeq, recvPacketTimes[0], recvPacketTimes[1], recvPacketTimes[2], recvPacketTimes[3], recvPacketTimes[4], recvPacketTimes[5], recvPacketTimes[6], recvPacketTimes[7]); - //LOGI("RTT = %.3lf", GetAverageRTT()); - //LOGV("Packet %u type is %d", pseq, type); - if(type==PKT_INIT){ - LOGD("Received init"); - if(!receivedInit){ - receivedInit=true; - currentEndpoint=srcEndpoint; - if(srcEndpoint->type==EP_TYPE_UDP_RELAY || (useTCP && srcEndpoint->type==EP_TYPE_TCP_RELAY)) - preferredRelay=srcEndpoint; - LogDebugInfo(); - } - peerVersion=(uint32_t) in.ReadInt32(); - LOGI("Peer version is %d", peerVersion); - uint32_t minVer=(uint32_t) in.ReadInt32(); - if(minVer>PROTOCOL_VERSION || peerVersion=2 ? 10 : 2)+(peerVersion>=2 ? 6 : 4)*outgoingStreams.size()); - - out.WriteInt32(PROTOCOL_VERSION); - out.WriteInt32(MIN_PROTOCOL_VERSION); - - out.WriteByte((unsigned char) outgoingStreams.size()); - for(i=0; iid); - out.WriteByte(outgoingStreams[i]->type); - out.WriteByte(outgoingStreams[i]->codec); - out.WriteInt16(outgoingStreams[i]->frameDuration); - out.WriteByte((unsigned char) (outgoingStreams[i]->enabled ? 1 : 0)); - } - sendQueue->Put(PendingOutgoingPacket{ - /*.seq=*/GenerateOutSeq(), - /*.type=*/PKT_INIT_ACK, - /*.len=*/out.GetLength(), - /*.data=*/buf, - /*.endpoint=*/NULL - }); - } - } - if(type==PKT_INIT_ACK){ - LOGD("Received init ack"); - - if(!receivedInitAck){ - receivedInitAck=true; - if(packetInnerLen>10){ - peerVersion=in.ReadInt32(); - uint32_t minVer=(uint32_t) in.ReadInt32(); - if(minVer>PROTOCOL_VERSION || peerVersionid=in.ReadByte(); - stm->type=in.ReadByte(); - stm->codec=in.ReadByte(); - if(peerVersion>=2) - stm->frameDuration=(uint16_t) in.ReadInt16(); - else - stm->frameDuration=20; - stm->enabled=in.ReadByte()==1; - incomingStreams.push_back(stm); - if(stm->type==STREAM_TYPE_AUDIO && !incomingAudioStream) - incomingAudioStream=stm; - } - if(!incomingAudioStream) - continue; - - voip_stream_t *outgoingAudioStream=outgoingStreams[0]; - - if(!audioInput){ - LOGI("before create audio io"); - audioInput=tgvoip::audio::AudioInput::Create(currentAudioInput); - audioInput->Configure(48000, 16, 1); - audioOutput=tgvoip::audio::AudioOutput::Create(currentAudioOutput); - audioOutput->Configure(48000, 16, 1); - echoCanceller=new EchoCanceller(config.enableAEC, config.enableNS, config.enableAGC); - echoCanceller->SetAECStrength(echoCancellationStrength); - encoder=new OpusEncoder(audioInput); - encoder->SetCallback(AudioInputCallback, this); - encoder->SetOutputFrameDuration(outgoingAudioStream->frameDuration); - encoder->SetEchoCanceller(echoCanceller); - encoder->Start(); - if(!micMuted){ - audioInput->Start(); - if(!audioInput->IsInitialized()){ - LOGE("Error initializing audio capture"); - lastError=TGVOIP_ERROR_AUDIO_IO; - - SetState(STATE_FAILED); - return; - } - } - if(!audioOutput->IsInitialized()){ - LOGE("Error initializing audio playback"); - lastError=TGVOIP_ERROR_AUDIO_IO; - - SetState(STATE_FAILED); - return; - } - UpdateAudioBitrate(); - - jitterBuffer=new JitterBuffer(NULL, incomingAudioStream->frameDuration); - outputAGC=new AutomaticGainControl(); - outputAGC->SetPassThrough(!outputAGCEnabled); - decoder=new OpusDecoder(audioOutput); - decoder->SetEchoCanceller(echoCanceller); - decoder->SetJitterBuffer(jitterBuffer); - decoder->SetFrameDuration(incomingAudioStream->frameDuration); - decoder->AddAudioEffect(outputAGC); - decoder->Start(); - if(incomingAudioStream->frameDuration>50) - jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_60", 3)); - else if(incomingAudioStream->frameDuration>30) - jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_40", 4)); - else - jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_20", 6)); - //audioOutput->Start(); -#ifdef TGVOIP_USE_AUDIO_SESSION -#ifdef __APPLE__ - if(acquireAudioSession){ - acquireAudioSession(^(){ - LOGD("Audio session acquired"); - needNotifyAcquiredAudioSession=true; - }); - }else{ - audio::AudioUnitIO::AudioSessionAcquired(); - } -#endif -#endif - } - setEstablishedAt=GetCurrentTime()+ServerConfig::GetSharedInstance()->GetDouble("established_delay_if_no_stream_data", 1.5); - if(allowP2p) - SendPublicEndpointsRequest(); - } - } - if(type==PKT_STREAM_DATA || type==PKT_STREAM_DATA_X2 || type==PKT_STREAM_DATA_X3){ - if(state!=STATE_ESTABLISHED && receivedInitAck) - SetState(STATE_ESTABLISHED); - int count; - switch(type){ - case PKT_STREAM_DATA_X2: - count=2; - break; - case PKT_STREAM_DATA_X3: - count=3; - break; - case PKT_STREAM_DATA: - default: - count=1; - break; - } - int i; - if(srcEndpoint->type==EP_TYPE_UDP_RELAY && srcEndpoint!=peerPreferredRelay){ - peerPreferredRelay=srcEndpoint; - } - for(i=0;iStart(); - audioOutStarted=true; - } - if(jitterBuffer && in.Remaining()>=sdlen) - jitterBuffer->HandleInput((unsigned char*) (buffer+in.GetOffset()), sdlen, pts); - if(iToString().c_str(), srcEndpoint->port); - if(srcEndpoint->type!=EP_TYPE_UDP_RELAY && srcEndpoint->type!=EP_TYPE_TCP_RELAY && !allowP2p){ - LOGW("Received p2p ping but p2p is disabled by manual override"); - continue; - } - unsigned char* buf=outgoingPacketsBufferPool.Get(); - if(!buf){ - LOGW("Dropping pong packet, queue overflow"); - continue; - } - BufferOutputStream pkt(buf, outgoingPacketsBufferPool.GetSingleBufferSize()); - pkt.WriteInt32(pseq); - sendQueue->Put(PendingOutgoingPacket{ - /*.seq=*/GenerateOutSeq(), - /*.type=*/PKT_PONG, - /*.len=*/pkt.GetLength(), - /*.data=*/buf, - /*.endpoint=*/srcEndpoint, - }); - } - if(type==PKT_PONG){ - if(packetInnerLen>=4){ - uint32_t pingSeq=(uint32_t) in.ReadInt32(); - if(pingSeq==srcEndpoint->lastPingSeq){ - memmove(&srcEndpoint->rtts[1], srcEndpoint->rtts, sizeof(double)*5); - srcEndpoint->rtts[0]=GetCurrentTime()-srcEndpoint->lastPingTime; - int i; - srcEndpoint->averageRTT=0; - for(i=0;i<6;i++){ - if(srcEndpoint->rtts[i]==0) - break; - srcEndpoint->averageRTT+=srcEndpoint->rtts[i]; - } - srcEndpoint->averageRTT/=i; - LOGD("Current RTT via %s: %.3f, average: %.3f", packet.address->ToString().c_str(), srcEndpoint->rtts[0], srcEndpoint->averageRTT); - } - } - /*if(currentEndpoint!=srcEndpoint && (srcEndpoint->type==EP_TYPE_UDP_P2P_INET || srcEndpoint->type==EP_TYPE_UDP_P2P_LAN)){ - LOGI("Switching to P2P now!"); - currentEndpoint=srcEndpoint; - needSendP2pPing=false; - }*/ - } - if(type==PKT_STREAM_STATE){ - unsigned char id=in.ReadByte(); - unsigned char enabled=in.ReadByte(); - int i; - for(i=0;iid==id){ - incomingStreams[i]->enabled=enabled==1; - UpdateAudioOutputState(); - break; - } - } - } - if(type==PKT_LAN_ENDPOINT){ - LOGV("received lan endpoint"); - uint32_t peerAddr=(uint32_t) in.ReadInt32(); - uint16_t peerPort=(uint16_t) in.ReadInt32(); - lock_mutex(endpointsMutex); - for(std::vector::iterator itrtr=endpoints.begin();itrtr!=endpoints.end();++itrtr){ - if((*itrtr)->type==EP_TYPE_UDP_P2P_LAN){ - if(currentEndpoint==*itrtr) - currentEndpoint=preferredRelay; - delete *itrtr; - endpoints.erase(itrtr); - break; - } - } - IPv4Address v4addr(peerAddr); - IPv6Address v6addr("::0"); - unsigned char peerTag[16]; - endpoints.push_back(new Endpoint(0, peerPort, v4addr, v6addr, EP_TYPE_UDP_P2P_LAN, peerTag)); - unlock_mutex(endpointsMutex); - } - if(type==PKT_NETWORK_CHANGED && currentEndpoint->type!=EP_TYPE_UDP_RELAY && currentEndpoint->type!=EP_TYPE_TCP_RELAY){ - currentEndpoint=preferredRelay; - if(allowP2p) - SendPublicEndpointsRequest(); - if(peerVersion>=2){ - uint32_t flags=(uint32_t) in.ReadInt32(); - dataSavingRequestedByPeer=(flags & INIT_FLAG_DATA_SAVING_ENABLED)==INIT_FLAG_DATA_SAVING_ENABLED; - UpdateDataSavingState(); - UpdateAudioBitrate(); - } - } + ProcessIncomingPacket(packet, srcEndpoint); }catch(std::out_of_range x){ LOGW("Error parsing packet: %s", x.what()); } } + free(buffer); LOGI("=== recv thread exiting ==="); } -void VoIPController::RunSendThread(){ +void VoIPController::RunSendThread(void* arg){ unsigned char buf[1500]; while(runReceiver){ PendingOutgoingPacket pkt=sendQueue->GetBlocking(); if(pkt.data){ - lock_mutex(endpointsMutex); + MutexGuard m(endpointsMutex); Endpoint *endpoint=pkt.endpoint ? pkt.endpoint : currentEndpoint; - if((endpoint->type==EP_TYPE_TCP_RELAY && useTCP) || (endpoint->type!=EP_TYPE_TCP_RELAY && useUDP)){ + if((endpoint->type==Endpoint::TYPE_TCP_RELAY && useTCP) || (endpoint->type!=Endpoint::TYPE_TCP_RELAY && useUDP)){ BufferOutputStream p(buf, sizeof(buf)); - WritePacketHeader(pkt.seq, &p, pkt.type, pkt.len); + WritePacketHeader(pkt.seq, &p, pkt.type, (uint32_t)pkt.len); p.WriteBytes(pkt.data, pkt.len); - SendPacket(p.GetBuffer(), p.GetLength(), endpoint); + SendPacket(p.GetBuffer(), p.GetLength(), endpoint, pkt); } - unlock_mutex(endpointsMutex); outgoingPacketsBufferPool.Reuse(pkt.data); }else{ LOGE("tried to send null packet"); @@ -1363,10 +1066,893 @@ void VoIPController::RunSendThread(){ LOGI("=== send thread exiting ==="); } +void VoIPController::ProcessIncomingPacket(NetworkPacket &packet, Endpoint* srcEndpoint){ + unsigned char* buffer=packet.data; + size_t len=packet.length; + BufferInputStream in(buffer, (size_t)len); + if(memcmp(buffer, srcEndpoint->type==Endpoint::TYPE_UDP_RELAY || srcEndpoint->type==Endpoint::TYPE_TCP_RELAY ? (void*)srcEndpoint->peerTag : (void*)callID, 16)!=0){ + LOGW("Received packet has wrong peerTag"); + return; + } + in.Seek(16); + if(in.Remaining()>=16 && (srcEndpoint->type==Endpoint::TYPE_UDP_RELAY || srcEndpoint->type==Endpoint::TYPE_TCP_RELAY) + && *reinterpret_cast(buffer+16)==0xFFFFFFFFFFFFFFFFLL && *reinterpret_cast(buffer+24)==0xFFFFFFFF){ + // relay special request response + in.Seek(16+12); + uint32_t tlid=(uint32_t) in.ReadInt32(); -void VoIPController::RunTickThread(){ + if(tlid==TLID_UDP_REFLECTOR_SELF_INFO){ + if(srcEndpoint->type==Endpoint::TYPE_UDP_RELAY /*&& udpConnectivityState==UDP_PING_SENT*/ && in.Remaining()>=32){ + /*int32_t date=*/in.ReadInt32(); + /*int64_t queryID=*/in.ReadInt64(); + unsigned char myIP[16]; + in.ReadBytes(myIP, 16); + /*int32_t myPort=*/in.ReadInt32(); + //udpConnectivityState=UDP_AVAILABLE; + //LOGV("Received UDP ping reply from %s:%d: date=%d, queryID=%lld, my IP=%s, my port=%d", srcEndpoint->address.ToString().c_str(), srcEndpoint->port, date, queryID, IPv6Address(myIP).ToString().c_str(), myPort); + srcEndpoint->udpPongCount++; + } + }else if(tlid==TLID_UDP_REFLECTOR_PEER_INFO){ + if(waitingForRelayPeerInfo && in.Remaining()>=16){ + MutexGuard _m(endpointsMutex); + uint32_t myAddr=(uint32_t) in.ReadInt32(); + uint32_t myPort=(uint32_t) in.ReadInt32(); + uint32_t peerAddr=(uint32_t) in.ReadInt32(); + uint32_t peerPort=(uint32_t) in.ReadInt32(); + for(std::vector::iterator itrtr=endpoints.begin(); itrtr!=endpoints.end(); ++itrtr){ + Endpoint *ep=*itrtr; + if(ep->type==Endpoint::TYPE_UDP_P2P_INET){ + if(currentEndpoint==ep) + currentEndpoint=preferredRelay; + delete ep; + endpoints.erase(itrtr); + break; + } + } + for(std::vector::iterator itrtr=endpoints.begin(); itrtr!=endpoints.end(); ++itrtr){ + Endpoint *ep=*itrtr; + if(ep->type==Endpoint::TYPE_UDP_P2P_LAN){ + if(currentEndpoint==ep) + currentEndpoint=preferredRelay; + delete ep; + endpoints.erase(itrtr); + break; + } + } + IPv4Address _peerAddr(peerAddr); + IPv6Address emptyV6("::0"); + unsigned char peerTag[16]; + endpoints.push_back(new Endpoint(0, (uint16_t) peerPort, _peerAddr, emptyV6, Endpoint::TYPE_UDP_P2P_INET, peerTag)); + LOGW("Received reflector peer info, my=%08X:%u, peer=%08X:%u", myAddr, myPort, peerAddr, peerPort); + if(myAddr==peerAddr){ + LOGW("Detected LAN"); + IPv4Address lanAddr(0); + udpSocket->GetLocalInterfaceInfo(&lanAddr, NULL); + + BufferOutputStream pkt(8); + pkt.WriteInt32(lanAddr.GetAddress()); + pkt.WriteInt32(udpSocket->GetLocalPort()); + SendPacketReliably(PKT_LAN_ENDPOINT, pkt.GetBuffer(), pkt.GetLength(), 0.5, 10); + } + waitingForRelayPeerInfo=false; + } + }else{ + LOGV("Received relay response with unknown tl id: 0x%08X", tlid); + } + return; + } + if(in.Remaining()<40){ + LOGV("Received packet is too small"); + return; + } + + bool retryWith2=false; + + if(!useMTProto2){ + unsigned char fingerprint[8], msgHash[16]; + in.ReadBytes(fingerprint, 8); + in.ReadBytes(msgHash, 16); + if(memcmp(fingerprint, keyFingerprint, 8)!=0){ + LOGW("Received packet has wrong key fingerprint"); + return; + } + unsigned char key[32], iv[32]; + KDF(msgHash, isOutgoing ? 8 : 0, key, iv); + unsigned char aesOut[MSC_STACK_FALLBACK(in.Remaining(), 1500)]; + if(in.Remaining()>sizeof(aesOut)) + return; + crypto.aes_ige_decrypt((unsigned char *) buffer+in.GetOffset(), aesOut, in.Remaining(), key, iv); + BufferInputStream _in(aesOut, in.Remaining()); + unsigned char sha[SHA1_LENGTH]; + uint32_t _len=(uint32_t) _in.ReadInt32(); + if(_len>_in.Remaining()) + _len=_in.Remaining(); + crypto.sha1((uint8_t *) (aesOut), (size_t) (_len+4), sha); + if(memcmp(msgHash, sha+(SHA1_LENGTH-16), 16)!=0){ + LOGW("Received packet has wrong hash after decryption"); + if(state==STATE_WAIT_INIT || state==STATE_WAIT_INIT_ACK) + retryWith2=true; + else + return; + }else{ + memcpy(buffer+in.GetOffset(), aesOut, in.Remaining()); + in.ReadInt32(); + } + } + + if(useMTProto2 || retryWith2){ + in.Seek(16); // peer tag + + unsigned char fingerprint[8], msgKey[16]; + in.ReadBytes(fingerprint, 8); + if(memcmp(fingerprint, keyFingerprint, 8)!=0){ + LOGW("Received packet has wrong key fingerprint"); + return; + } + in.ReadBytes(msgKey, 16); + + unsigned char decrypted[1500]; + unsigned char aesKey[32], aesIv[32]; + KDF2(msgKey, isOutgoing ? 8 : 0, aesKey, aesIv); + size_t decryptedLen=in.Remaining(); + if(decryptedLen>sizeof(decrypted)) + return; + //LOGV("-> MSG KEY: %08x %08x %08x %08x, hashed %u", *reinterpret_cast(msgKey), *reinterpret_cast(msgKey+4), *reinterpret_cast(msgKey+8), *reinterpret_cast(msgKey+12), decryptedLen-4); + + uint8_t *decryptOffset = packet.data + in.GetOffset(); + if ((((intptr_t)decryptOffset) % sizeof(long)) != 0) { + LOGE("alignment2 packet.data+in.GetOffset()"); + } + if (decryptedLen % sizeof(long) != 0) { + LOGE("alignment2 decryptedLen"); + } + crypto.aes_ige_decrypt(packet.data+in.GetOffset(), decrypted, decryptedLen, aesKey, aesIv); + + in=BufferInputStream(decrypted, decryptedLen); + //LOGD("received packet length: %d", in.ReadInt32()); + + BufferOutputStream buf(decryptedLen+32); + size_t x=isOutgoing ? 8 : 0; + buf.WriteBytes(encryptionKey+88+x, 32); + buf.WriteBytes(decrypted+4, decryptedLen-4); + unsigned char msgKeyLarge[32]; + crypto.sha256(buf.GetBuffer(), buf.GetLength(), msgKeyLarge); + + if(memcmp(msgKey, msgKeyLarge+8, 16)!=0){ + LOGW("Received packet has wrong hash"); + return; + } + + uint32_t innerLen=(uint32_t) in.ReadInt32(); + if(innerLen>decryptedLen-4){ + LOGW("Received packet has wrong inner length (%d with total of %u)", innerLen, decryptedLen); + return; + } + if(decryptedLen-innerLen<12){ + LOGW("Received packet has too little padding (%u)", decryptedLen-innerLen); + return; + } + memcpy(buffer, decrypted+4, innerLen); + in=BufferInputStream(buffer, (size_t) innerLen); + if(retryWith2){ + LOGD("Successfully decrypted packet in MTProto2.0 fallback, upgrading"); + useMTProto2=true; + } + } + + lastRecvPacketTime=GetCurrentTime(); + + + /*decryptedAudioBlock random_id:long random_bytes:string flags:# voice_call_id:flags.2?int128 in_seq_no:flags.4?int out_seq_no:flags.4?int + * recent_received_mask:flags.5?int proto:flags.3?int extra:flags.1?string raw_data:flags.0?string = DecryptedAudioBlock +simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedAudioBlock; +*/ + uint32_t ackId, pseq, acks; + unsigned char type; + uint32_t tlid=(uint32_t) in.ReadInt32(); + uint32_t packetInnerLen=0; + if(tlid==TLID_DECRYPTED_AUDIO_BLOCK){ + in.ReadInt64(); // random id + uint32_t randLen=(uint32_t) in.ReadTlLength(); + in.Seek(in.GetOffset()+randLen+pad4(randLen)); + uint32_t flags=(uint32_t) in.ReadInt32(); + type=(unsigned char) ((flags >> 24) & 0xFF); + if(!(flags & PFLAG_HAS_SEQ && flags & PFLAG_HAS_RECENT_RECV)){ + LOGW("Received packet doesn't have PFLAG_HAS_SEQ, PFLAG_HAS_RECENT_RECV, or both"); + + return; + } + if(flags & PFLAG_HAS_CALL_ID){ + unsigned char pktCallID[16]; + in.ReadBytes(pktCallID, 16); + if(memcmp(pktCallID, callID, 16)!=0){ + LOGW("Received packet has wrong call id"); + + lastError=ERROR_UNKNOWN; + SetState(STATE_FAILED); + return; + } + } + ackId=(uint32_t) in.ReadInt32(); + pseq=(uint32_t) in.ReadInt32(); + acks=(uint32_t) in.ReadInt32(); + if(flags & PFLAG_HAS_PROTO){ + uint32_t proto=(uint32_t) in.ReadInt32(); + if(proto!=PROTOCOL_NAME){ + LOGW("Received packet uses wrong protocol"); + + lastError=ERROR_INCOMPATIBLE; + SetState(STATE_FAILED); + return; + } + } + if(flags & PFLAG_HAS_EXTRA){ + uint32_t extraLen=(uint32_t) in.ReadTlLength(); + in.Seek(in.GetOffset()+extraLen+pad4(extraLen)); + } + if(flags & PFLAG_HAS_DATA){ + packetInnerLen=in.ReadTlLength(); + } + }else if(tlid==TLID_SIMPLE_AUDIO_BLOCK){ + in.ReadInt64(); // random id + uint32_t randLen=(uint32_t) in.ReadTlLength(); + in.Seek(in.GetOffset()+randLen+pad4(randLen)); + packetInnerLen=in.ReadTlLength(); + type=in.ReadByte(); + ackId=(uint32_t) in.ReadInt32(); + pseq=(uint32_t) in.ReadInt32(); + acks=(uint32_t) in.ReadInt32(); + }else{ + LOGW("Received a packet of unknown type %08X", tlid); + + return; + } + if(type==PKT_GROUP_CALL_KEY && didSendGroupCallKey){ + LOGE("Received group call key after we sent one"); + return; + } + packetsRecieved++; + if(seqgt(pseq, lastRemoteSeq)){ + uint32_t diff=pseq-lastRemoteSeq; + if(diff>31){ + memset(recvPacketTimes, 0, 32*sizeof(double)); + }else{ + memmove(&recvPacketTimes[diff], recvPacketTimes, (32-diff)*sizeof(double)); + if(diff>1){ + memset(recvPacketTimes, 0, diff*sizeof(double)); + } + recvPacketTimes[0]=GetCurrentTime(); + } + lastRemoteSeq=pseq; + }else if(!seqgt(pseq, lastRemoteSeq) && lastRemoteSeq-pseq<32){ + if(recvPacketTimes[lastRemoteSeq-pseq]!=0){ + LOGW("Received duplicated packet for seq %u", pseq); + + return; + } + recvPacketTimes[lastRemoteSeq-pseq]=GetCurrentTime(); + }else if(lastRemoteSeq-pseq>=32){ + LOGW("Packet %u is out of order and too late", pseq); + + return; + } + if(seqgt(ackId, lastRemoteAckSeq)){ + //uint32_t diff=ackId-lastRemoteAckSeq; + /*if(diff>31){ + memset(remoteAcks, 0, 32*sizeof(double)); + }else{ + memmove(&remoteAcks[diff], remoteAcks, (32-diff)*sizeof(double)); + if(diff>1){ + memset(remoteAcks, 0, diff*sizeof(double)); + } + remoteAcks[0]=GetCurrentTime(); + }*/ + MutexGuard _m(queuedPacketsMutex); + if(waitingForAcks && lastRemoteAckSeq>=firstSentPing){ + memset(rttHistory, 0, 32*sizeof(double)); + waitingForAcks=false; + dontSendPackets=10; + LOGI("resuming sending"); + } + lastRemoteAckSeq=ackId; + conctl->PacketAcknowledged(ackId); + unsigned int i; + for(i=0;i<31;i++){ + for(std::vector::iterator itr=recentOutgoingPackets.begin();itr!=recentOutgoingPackets.end();++itr){ + if(itr->ackTime!=0) + continue; + if(((acks >> (31-i)) & 1) && itr->seq==ackId-(i+1)){ + itr->ackTime=GetCurrentTime(); + conctl->PacketAcknowledged(itr->seq); + } + } + /*if(remoteAcks[i+1]==0){ + if((acks >> (31-i)) & 1){ + remoteAcks[i+1]=GetCurrentTime(); + conctl->PacketAcknowledged(ackId-(i+1)); + } + }*/ + } + for(i=0;iseqs[j]); + if(qp->seqs[j]==0) + break; + int remoteAcksIndex=lastRemoteAckSeq-qp->seqs[j]; + //LOGV("remote acks index %u, value %f", remoteAcksIndex, remoteAcksIndex>=0 && remoteAcksIndex<32 ? remoteAcks[remoteAcksIndex] : -1); + if(seqgt(lastRemoteAckSeq, qp->seqs[j]) && remoteAcksIndex>=0 && remoteAcksIndex<32){ + for(std::vector::iterator itr=recentOutgoingPackets.begin();itr!=recentOutgoingPackets.end();++itr){ + if(itr->seq==qp->seqs[j] && itr->ackTime>0){ + LOGD("did ack seq %u, removing", qp->seqs[j]); + didAck=true; + break; + } + } + if(didAck) + break; + } + } + if(didAck){ + if(qp->type==PKT_GROUP_CALL_KEY && !didReceiveGroupCallKeyAck){ + didReceiveGroupCallKeyAck=true; + if(callbacks.groupCallKeySent) + callbacks.groupCallKeySent(this); + } + if(qp->data) + free(qp->data); + free(qp); + queuedPackets.erase(queuedPackets.begin()+i); + i--; + continue; + } + } + } + + if(srcEndpoint!=currentEndpoint && (srcEndpoint->type==Endpoint::TYPE_UDP_RELAY || srcEndpoint->type==Endpoint::TYPE_TCP_RELAY) && ((currentEndpoint->type!=Endpoint::TYPE_UDP_RELAY && currentEndpoint->type!=Endpoint::TYPE_TCP_RELAY) || currentEndpoint->averageRTT==0)){ + if(seqgt(lastSentSeq-32, lastRemoteAckSeq)){ + currentEndpoint=srcEndpoint; + LOGI("Peer network address probably changed, switching to relay"); + if(allowP2p) + SendPublicEndpointsRequest(); + } + } + + //LOGV("acks: %u -> %.2lf, %.2lf, %.2lf, %.2lf, %.2lf, %.2lf, %.2lf, %.2lf", lastRemoteAckSeq, remoteAcks[0], remoteAcks[1], remoteAcks[2], remoteAcks[3], remoteAcks[4], remoteAcks[5], remoteAcks[6], remoteAcks[7]); + //LOGD("recv: %u -> %.2lf, %.2lf, %.2lf, %.2lf, %.2lf, %.2lf, %.2lf, %.2lf", lastRemoteSeq, recvPacketTimes[0], recvPacketTimes[1], recvPacketTimes[2], recvPacketTimes[3], recvPacketTimes[4], recvPacketTimes[5], recvPacketTimes[6], recvPacketTimes[7]); + //LOGI("RTT = %.3lf", GetAverageRTT()); + //LOGV("Packet %u type is %d", pseq, type); + if(type==PKT_INIT){ + LOGD("Received init"); + if(!receivedInit){ + receivedInit=true; + if((srcEndpoint->type==Endpoint::TYPE_UDP_RELAY && udpConnectivityState!=UDP_BAD && udpConnectivityState!=UDP_NOT_AVAILABLE) || srcEndpoint->type==Endpoint::TYPE_TCP_RELAY){ + currentEndpoint=srcEndpoint; + if(srcEndpoint->type==Endpoint::TYPE_UDP_RELAY || (useTCP && srcEndpoint->type==Endpoint::TYPE_TCP_RELAY)) + preferredRelay=srcEndpoint; + } + LogDebugInfo(); + } + peerVersion=(uint32_t) in.ReadInt32(); + LOGI("Peer version is %d", peerVersion); + uint32_t minVer=(uint32_t) in.ReadInt32(); + if(minVer>PROTOCOL_VERSION || peerVersion=2 ? 10 : 2)+(peerVersion>=2 ? 6 : 4)*outgoingStreams.size()); + + out.WriteInt32(PROTOCOL_VERSION); + out.WriteInt32(MIN_PROTOCOL_VERSION); + + out.WriteByte((unsigned char) outgoingStreams.size()); + for(i=0; iPut(PendingOutgoingPacket{ + /*.seq=*/GenerateOutSeq(), + /*.type=*/PKT_INIT_ACK, + /*.len=*/out.GetLength(), + /*.data=*/buf, + /*.endpoint=*/NULL + }); + } + } + if(type==PKT_INIT_ACK){ + LOGD("Received init ack"); + + if(!receivedInitAck){ + receivedInitAck=true; + if(packetInnerLen>10){ + peerVersion=in.ReadInt32(); + uint32_t minVer=(uint32_t) in.ReadInt32(); + if(minVer>PROTOCOL_VERSION || peerVersion50) + stm.jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_60", 3)); + else if(stm.frameDuration>30) + stm.jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_40", 4)); + else + stm.jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_20", 6)); + stm.decoder=NULL; + incomingStreams.push_back(stm); + if(stm.type==STREAM_TYPE_AUDIO && !incomingAudioStream) + incomingAudioStream=&stm; + } + if(!incomingAudioStream) + return; + + if(peerVersion>=5 && !useMTProto2){ + useMTProto2=true; + LOGD("MTProto2 wasn't initially enabled for whatever reason but peer supports it; upgrading"); + } + + if(!audioInput){ + StartAudio(); + } + setEstablishedAt=GetCurrentTime()+ServerConfig::GetSharedInstance()->GetDouble("established_delay_if_no_stream_data", 1.5); + if(allowP2p) + SendPublicEndpointsRequest(); + } + } + if(type==PKT_STREAM_DATA || type==PKT_STREAM_DATA_X2 || type==PKT_STREAM_DATA_X3){ + if(state!=STATE_ESTABLISHED && receivedInitAck) + SetState(STATE_ESTABLISHED); + int count; + switch(type){ + case PKT_STREAM_DATA_X2: + count=2; + break; + case PKT_STREAM_DATA_X3: + count=3; + break; + case PKT_STREAM_DATA: + default: + count=1; + break; + } + int i; + if(srcEndpoint->type==Endpoint::TYPE_UDP_RELAY && srcEndpoint!=peerPreferredRelay){ + peerPreferredRelay=srcEndpoint; + } + for(i=0;iStart(); + audioOutStarted=true; + } + if(in.GetOffset()+sdlen>len){ + return; + } + if(incomingStreams.size()>0 && incomingStreams[0].jitterBuffer) + incomingStreams[0].jitterBuffer->HandleInput((unsigned char*) (buffer+in.GetOffset()), sdlen, pts); + if(iToString().c_str(), srcEndpoint->port); + if(srcEndpoint->type!=Endpoint::TYPE_UDP_RELAY && srcEndpoint->type!=Endpoint::TYPE_TCP_RELAY && !allowP2p){ + LOGW("Received p2p ping but p2p is disabled by manual override"); + return; + } + unsigned char* buf=outgoingPacketsBufferPool.Get(); + if(!buf){ + LOGW("Dropping pong packet, queue overflow"); + return; + } + BufferOutputStream pkt(buf, outgoingPacketsBufferPool.GetSingleBufferSize()); + pkt.WriteInt32(pseq); + sendQueue->Put(PendingOutgoingPacket{ + /*.seq=*/GenerateOutSeq(), + /*.type=*/PKT_PONG, + /*.len=*/pkt.GetLength(), + /*.data=*/buf, + /*.endpoint=*/srcEndpoint, + }); + } + if(type==PKT_PONG){ + if(packetInnerLen>=4){ + uint32_t pingSeq=(uint32_t) in.ReadInt32(); + if(pingSeq==srcEndpoint->lastPingSeq){ + memmove(&srcEndpoint->rtts[1], srcEndpoint->rtts, sizeof(double)*5); + srcEndpoint->rtts[0]=GetCurrentTime()-srcEndpoint->lastPingTime; + int i; + srcEndpoint->averageRTT=0; + for(i=0;i<6;i++){ + if(srcEndpoint->rtts[i]==0) + break; + srcEndpoint->averageRTT+=srcEndpoint->rtts[i]; + } + srcEndpoint->averageRTT/=i; + LOGD("Current RTT via %s: %.3f, average: %.3f", packet.address->ToString().c_str(), srcEndpoint->rtts[0], srcEndpoint->averageRTT); + } + } + } + if(type==PKT_STREAM_STATE){ + unsigned char id=in.ReadByte(); + unsigned char enabled=in.ReadByte(); + unsigned int i; + for(i=0;i::iterator itrtr=endpoints.begin();itrtr!=endpoints.end();++itrtr){ + if((*itrtr)->type==Endpoint::TYPE_UDP_P2P_LAN){ + if(currentEndpoint==*itrtr) + currentEndpoint=preferredRelay; + found=true; + (*itrtr)->address=peerAddr; + break; + } + } + if(!found){ + IPv4Address v4addr(peerAddr); + IPv6Address v6addr("::0"); + unsigned char peerTag[16]; + endpoints.push_back(new Endpoint(0, peerPort, v4addr, v6addr, Endpoint::TYPE_UDP_P2P_LAN, peerTag)); + } + } + if(type==PKT_NETWORK_CHANGED && currentEndpoint->type!=Endpoint::TYPE_UDP_RELAY && currentEndpoint->type!=Endpoint::TYPE_TCP_RELAY){ + currentEndpoint=preferredRelay; + if(allowP2p) + SendPublicEndpointsRequest(); + if(peerVersion>=2){ + uint32_t flags=(uint32_t) in.ReadInt32(); + dataSavingRequestedByPeer=(flags & INIT_FLAG_DATA_SAVING_ENABLED)==INIT_FLAG_DATA_SAVING_ENABLED; + UpdateDataSavingState(); + UpdateAudioBitrate(); + } + } + if(type==PKT_GROUP_CALL_KEY && !didReceiveGroupCallKey && !didSendGroupCallKey){ + unsigned char groupKey[256]; + in.ReadBytes(groupKey, 256); + if(callbacks.groupCallKeyReceived) + callbacks.groupCallKeyReceived(this, groupKey); + didReceiveGroupCallKey=true; + } + if(type==PKT_REQUEST_GROUP && !didInvokeUpdateCallback){ + if(callbacks.upgradeToGroupCallRequested) + callbacks.upgradeToGroupCallRequested(this); + didInvokeUpdateCallback=true; + } +} + +void VoIPGroupController::ProcessIncomingPacket(NetworkPacket &packet, Endpoint *srcEndpoint){ + //LOGD("Received incoming packet from %s:%u, %u bytes", packet.address->ToString().c_str(), packet.port, packet.length); + if(packet.length<17 || packet.length>2000){ + LOGW("Received packet has wrong length %d", packet.length); + return; + } + BufferOutputStream sigData(packet.length); + sigData.WriteBytes(packet.data, packet.length-16); + sigData.WriteBytes(reflectorSelfSecret, 16); + unsigned char sig[32]; + crypto.sha256(sigData.GetBuffer(), sigData.GetLength(), sig); + if(memcmp(sig, packet.data+(packet.length-16), 16)!=0){ + LOGW("Received packet has incorrect signature"); + return; + } + + // reflector special response + if(memcmp(packet.data, reflectorSelfTagHash, 16)==0 && packet.length>60){ + //LOGI("possible reflector special response"); + unsigned char firstBlock[16]; + unsigned char iv[16]; + memcpy(iv, packet.data+16, 16); + unsigned char key[32]; + crypto.sha256(reflectorSelfSecret, 16, key); + crypto.aes_cbc_decrypt(packet.data+32, firstBlock, 16, key, iv); + BufferInputStream in(firstBlock, 16); + in.Seek(8); + size_t len=(size_t) in.ReadInt32(); + int32_t tlid=in.ReadInt32(); + //LOGD("special response: len=%d, tlid=0x%08X", len, tlid); + if(len%4==0 && len+60<=packet.length && packet.length<=1500){ + lastRecvPacketTime=GetCurrentTime(); + memcpy(iv, packet.data+16, 16); + unsigned char buf[1500]; + crypto.aes_cbc_decrypt(packet.data+32, buf, len+16, key, iv); + try{ + if(tlid==TLID_UDP_REFLECTOR_LAST_PACKETS_INFO){ + MutexGuard m(sentPacketsMutex); + //LOGV("received udpReflector.lastPacketsInfo"); + in=BufferInputStream(buf, len+16); + in.Seek(16); + /*int32_t date=*/in.ReadInt32(); + /*int64_t queryID=*/in.ReadInt64(); + int32_t vectorMagic=in.ReadInt32(); + if(vectorMagic!=TLID_VECTOR){ + LOGW("last packets info: expected vector, got %08X", vectorMagic); + return; + } + int32_t recvCount=in.ReadInt32(); + //LOGV("%d received packets", recvCount); + for(int i=0;i::iterator pkt=recentSentPackets.begin();pkt!=recentSentPackets.end();++pkt){ + //LOGV("== sent id %04X", pkt->id); + if(pkt->id==id){ + if(!pkt->ackTime){ + pkt->ackTime=GetCurrentTime(); + conctl->PacketAcknowledged(pkt->seq); + //LOGV("relay acknowledged packet %u", pkt->seq); + if(seqgt(pkt->seq, lastRemoteAckSeq)) + lastRemoteAckSeq=pkt->seq; + } + break; + } + } + } + vectorMagic=in.ReadInt32(); + if(vectorMagic!=TLID_VECTOR){ + LOGW("last packets info: expected vector, got %08X", vectorMagic); + return; + } + int32_t sentCount=in.ReadInt32(); + //LOGV("%d sent packets", sentCount); + for(int i=0;iStart(); + } + } + } + }catch(std::out_of_range& x){ + LOGE("Error parsing special response: %s", x.what()); + } + return; + } + } + + if(packet.length<32) + return; + + // it's a packet relayed from another participant - find the sender + MutexGuard m(participantsMutex); + GroupCallParticipant* sender=NULL; + for(std::vector::iterator p=participants.begin();p!=participants.end();++p){ + if(memcmp(packet.data, p->memberTagHash, 16)==0){ + //LOGV("received data packet from user %d", p->userID); + sender=&*p; + break; + } + } + if(!sender){ + LOGV("Received data packet is from unknown user"); + return; + } + + if(memcmp(packet.data+16, keyFingerprint, 8)!=0){ + LOGW("received packet has wrong key fingerprint"); + return; + } + + BufferInputStream in(packet.data, packet.length-16); + in.Seek(16+8); // peer tag + key fingerprint + + unsigned char msgKey[16]; + in.ReadBytes(msgKey, 16); + + unsigned char decrypted[1500]; + unsigned char aesKey[32], aesIv[32]; + KDF2(msgKey, 0, aesKey, aesIv); + size_t decryptedLen=in.Remaining()-16; + if(decryptedLen>sizeof(decrypted)) + return; + //LOGV("-> MSG KEY: %08x %08x %08x %08x, hashed %u", *reinterpret_cast(msgKey), *reinterpret_cast(msgKey+4), *reinterpret_cast(msgKey+8), *reinterpret_cast(msgKey+12), decryptedLen-4); + uint8_t *decryptOffset = packet.data + in.GetOffset(); + if ((((intptr_t)decryptOffset) % sizeof(long)) != 0) { + LOGE("alignment2 packet.data+in.GetOffset()"); + } + if (decryptedLen % sizeof(long) != 0) { + LOGE("alignment2 decryptedLen"); + } + crypto.aes_ige_decrypt(packet.data+in.GetOffset(), decrypted, decryptedLen, aesKey, aesIv); + + in=BufferInputStream(decrypted, decryptedLen); + //LOGD("received packet length: %d", in.ReadInt32()); + + BufferOutputStream buf(decryptedLen+32); + size_t x=0; + buf.WriteBytes(encryptionKey+88+x, 32); + buf.WriteBytes(decrypted+4, decryptedLen-4); + unsigned char msgKeyLarge[32]; + crypto.sha256(buf.GetBuffer(), buf.GetLength(), msgKeyLarge); + + if(memcmp(msgKey, msgKeyLarge+8, 16)!=0){ + LOGW("Received packet from user %d has wrong hash", sender->userID); + return; + } + + uint32_t innerLen=(uint32_t) in.ReadInt32(); + if(innerLen>decryptedLen-4){ + LOGW("Received packet has wrong inner length (%d with total of %u)", innerLen, decryptedLen); + return; + } + if(decryptedLen-innerLen<12){ + LOGW("Received packet has too little padding (%u)", decryptedLen-innerLen); + return; + } + in=BufferInputStream(decrypted+4, (size_t) innerLen); + + uint32_t tlid=(uint32_t) in.ReadInt32(); + if(tlid!=TLID_DECRYPTED_AUDIO_BLOCK){ + LOGW("Received packet has unknown TL ID 0x%08x", tlid); + return; + } + in.Seek(in.GetOffset()+16); // random bytes + int32_t flags=in.ReadInt32(); + if(!(flags & PFLAG_HAS_SEQ) || !(flags & PFLAG_HAS_SENDER_TAG_HASH)){ + LOGW("Received packet has wrong flags"); + return; + } + /*uint32_t seq=(uint32_t) */in.ReadInt32(); + unsigned char senderTagHash[16]; + in.ReadBytes(senderTagHash, 16); + if(memcmp(senderTagHash, sender->memberTagHash, 16)!=0){ + LOGW("Received packet has wrong inner sender tag hash"); + return; + } + + //int32_t oneMoreInnerLengthWhyDoWeEvenNeedThis; + if(flags & PFLAG_HAS_DATA){ + /*oneMoreInnerLengthWhyDoWeEvenNeedThis=*/in.ReadTlLength(); + } + unsigned char type=(unsigned char) ((flags >> 24) & 0xFF); + lastRecvPacketTime=GetCurrentTime(); + + if(type==PKT_STREAM_DATA || type==PKT_STREAM_DATA_X2 || type==PKT_STREAM_DATA_X3){ + if(state!=STATE_ESTABLISHED && receivedInitAck) + SetState(STATE_ESTABLISHED); + int count; + switch(type){ + case PKT_STREAM_DATA_X2: + count=2; + break; + case PKT_STREAM_DATA_X3: + count=3; + break; + case PKT_STREAM_DATA: + default: + count=1; + break; + } + int i; + //if(srcEndpoint->type==Endpoint::TYPE_UDP_RELAY && srcEndpoint!=peerPreferredRelay){ + // peerPreferredRelay=srcEndpoint; + //} + for(i=0;iStart(); + audioOutStarted=true; + }*/ + if(in.GetOffset()+sdlen>in.GetLength()){ + return; + } + for(std::vector::iterator stm=sender->streams.begin();stm!=sender->streams.end();++stm){ + if(stm->id==streamID){ + if(stm->jitterBuffer){ + stm->jitterBuffer->HandleInput(decrypted+4+in.GetOffset(), sdlen, pts); + } + break; + } + } + if(iSize()); - if(jitterBuffer){ - int lostCount=jitterBuffer->GetAndResetLostPacketCount(); - if(lostCount>0 || (lostCount<0 && recvLossCount>((uint32_t)-lostCount))) - recvLossCount+=lostCount; + for(std::vector::iterator stm=incomingStreams.begin();stm!=incomingStreams.end();++stm){ + if(stm->jitterBuffer){ + int lostCount=stm->jitterBuffer->GetAndResetLostPacketCount(); + if(lostCount>0 || (lostCount<0 && recvLossCount>((uint32_t) -lostCount))) + recvLossCount+=lostCount; + } } } if(dontSendPackets>0) dontSendPackets--; - int i; + unsigned int i; conctl->Tick(); if(useTCP && !didAddTcpRelays){ std::vector relays; for(std::vector::iterator itr=endpoints.begin(); itr!=endpoints.end(); ++itr){ - if((*itr)->type!=EP_TYPE_UDP_RELAY) + if((*itr)->type!=Endpoint::TYPE_UDP_RELAY) continue; Endpoint *tcpRelay=new Endpoint(**itr); - tcpRelay->type=EP_TYPE_TCP_RELAY; + tcpRelay->type=Endpoint::TYPE_TCP_RELAY; tcpRelay->averageRTT=0; tcpRelay->lastPingSeq=0; tcpRelay->lastPingTime=0; memset(tcpRelay->rtts, 0, sizeof(tcpRelay->rtts)); + tcpRelay->udpPongCount=0; + if(setCurrentEndpointToTCP && currentEndpoint->type!=Endpoint::TYPE_TCP_RELAY){ + setCurrentEndpointToTCP=false; + currentEndpoint=tcpRelay; + preferredRelay=tcpRelay; + } relays.push_back(tcpRelay); } endpoints.insert(endpoints.end(), relays.begin(), relays.end()); @@ -1436,7 +2028,7 @@ void VoIPController::RunTickThread(){ if(state==STATE_ESTABLISHED && encoder && conctl){ if((audioInput && !audioInput->IsInitialized()) || (audioOutput && !audioOutput->IsInitialized())){ LOGE("Audio I/O failed"); - lastError=TGVOIP_ERROR_AUDIO_IO; + lastError=ERROR_AUDIO_IO; SetState(STATE_FAILED); } @@ -1460,37 +2052,42 @@ void VoIPController::RunTickThread(){ for(i=0;i<10;i++){ avgSendLossCount+=sendLossCountHistory[i]; } - double packetsPerSec=1000/(double)outgoingStreams[0]->frameDuration; + double packetsPerSec=1000/(double)outgoingStreams[0].frameDuration; avgSendLossCount=avgSendLossCount/10/packetsPerSec; //LOGV("avg send loss: %.1f%%", avgSendLossCount*100); if(avgSendLossCount>0.1){ encoder->SetPacketLoss(40); + signalBarCount=1; }else if(avgSendLossCount>0.075){ encoder->SetPacketLoss(35); + signalBarCount=MIN(signalBarCount, 2); }else if(avgSendLossCount>0.0625){ encoder->SetPacketLoss(30); + signalBarCount=MIN(signalBarCount, 2); }else if(avgSendLossCount>0.05){ encoder->SetPacketLoss(25); + signalBarCount=MIN(signalBarCount, 3); }else if(avgSendLossCount>0.025){ encoder->SetPacketLoss(20); + signalBarCount=MIN(signalBarCount, 3); }else if(avgSendLossCount>0.01){ encoder->SetPacketLoss(17); }else{ encoder->SetPacketLoss(15); } - if(encoder->GetPacketLoss()>30) - signalBarCount=MIN(signalBarCount, 2); - else if(encoder->GetPacketLoss()>20) - signalBarCount=MIN(signalBarCount, 3); } } + if(currentEndpoint->type==Endpoint::TYPE_TCP_RELAY){ + signalBarCount=MIN(signalBarCount, 3); + } + bool areThereAnyEnabledStreams=false; for(i=0;ienabled) + if(outgoingStreams[i].enabled) areThereAnyEnabledStreams=true; } @@ -1516,163 +2113,141 @@ void VoIPController::RunTickThread(){ SendPublicEndpointsRequest(); } - lock_mutex(queuedPacketsMutex); - for(i=0;itimeout>0 && qp->firstSentTime>0 && GetCurrentTime()-qp->firstSentTime>=qp->timeout){ - LOGD("Removing queued packet because of timeout"); - if(qp->data) - free(qp->data); - free(qp); - queuedPackets.erase(queuedPackets.begin()+i); - i--; - continue; - } - if(GetCurrentTime()-qp->lastSentTime>=qp->retryInterval){ - unsigned char* buf=outgoingPacketsBufferPool.Get(); - if(buf){ - uint32_t seq=GenerateOutSeq(); - memmove(&qp->seqs[1], qp->seqs, 4*9); - qp->seqs[0]=seq; - qp->lastSentTime=GetCurrentTime(); - LOGD("Sending queued packet, seq=%u, type=%u, len=%u", seq, qp->type, unsigned(qp->length)); - if(qp->firstSentTime==0) - qp->firstSentTime=qp->lastSentTime; - if(qp->length) - memcpy(buf, qp->data, qp->length); - sendQueue->Put(PendingOutgoingPacket{ - /*.seq=*/seq, - /*.type=*/qp->type, - /*.len=*/qp->length, - /*.data=*/buf, - /*.endpoint=*/NULL - }); - } - } - } - unlock_mutex(queuedPacketsMutex); - - if(jitterBuffer){ - jitterBuffer->Tick(); - double avgDelay=jitterBuffer->GetAverageDelay(); - double avgLateCount[3]; - jitterBuffer->GetAverageLateCount(avgLateCount); - /*if(avgDelay>=5) - signalBarCount=1; - else if(avgDelay>=4) - signalBarCount=MIN(signalBarCount, 2); - else if(avgDelay>=3) - signalBarCount=MIN(signalBarCount, 3);*/ - - if(avgLateCount[2]>=0.2) - signalBarCount=1; - else if(avgLateCount[2]>=0.1) - signalBarCount=MIN(signalBarCount, 2); - - } - - lock_mutex(endpointsMutex); - if(state==STATE_ESTABLISHED || state==STATE_RECONNECTING){ - Endpoint* minPingRelay=preferredRelay; - double minPing=preferredRelay->averageRTT; - for(std::vector::iterator e=endpoints.begin();e!=endpoints.end();++e){ - Endpoint* endpoint=*e; - if(endpoint->type==EP_TYPE_TCP_RELAY && !useTCP) + { + MutexGuard m(queuedPacketsMutex); + for(i=0; itimeout>0 && qp->firstSentTime>0 && GetCurrentTime()-qp->firstSentTime>=qp->timeout){ + LOGD("Removing queued packet because of timeout"); + if(qp->data) + free(qp->data); + free(qp); + queuedPackets.erase(queuedPackets.begin()+i); + i--; continue; - if(GetCurrentTime()-endpoint->lastPingTime>=10){ - LOGV("Sending ping to %s", endpoint->address.ToString().c_str()); - unsigned char* buf=outgoingPacketsBufferPool.Get(); + } + if(qp->type==PKT_GROUP_CALL_KEY && didReceiveGroupCallKey){ + LOGW("Not sending group call key packet because we already received one"); + continue; + } + if(GetCurrentTime()-qp->lastSentTime>=qp->retryInterval){ + unsigned char *buf=outgoingPacketsBufferPool.Get(); if(buf){ + uint32_t seq=GenerateOutSeq(); + memmove(&qp->seqs[1], qp->seqs, 4*9); + qp->seqs[0]=seq; + qp->lastSentTime=GetCurrentTime(); + LOGD("Sending queued packet, seq=%u, type=%u, len=%u", seq, qp->type, unsigned(qp->length)); + if(qp->firstSentTime==0) + qp->firstSentTime=qp->lastSentTime; + if(qp->length) + memcpy(buf, qp->data, qp->length); sendQueue->Put(PendingOutgoingPacket{ - /*.seq=*/(endpoint->lastPingSeq=GenerateOutSeq()), - /*.type=*/PKT_PING, - /*.len=*/0, + /*.seq=*/seq, + /*.type=*/qp->type, + /*.len=*/qp->length, /*.data=*/buf, - /*.endpoint=*/endpoint + /*.endpoint=*/NULL }); } - endpoint->lastPingTime=GetCurrentTime(); } - if(endpoint->type==EP_TYPE_UDP_RELAY || (useTCP && endpoint->type==EP_TYPE_TCP_RELAY)){ - double k=endpoint->type==EP_TYPE_UDP_RELAY ? 1 : 2; - if(endpoint->averageRTT>0 && endpoint->averageRTT*kaverageRTT*k; - minPingRelay=endpoint; + } + } + + for(std::vector::iterator stm=incomingStreams.begin();stm!=incomingStreams.end();++stm){ + if(stm->jitterBuffer){ + stm->jitterBuffer->Tick(); + //double avgDelay=stm->jitterBuffer->GetAverageDelay(); + double avgLateCount[3]; + stm->jitterBuffer->GetAverageLateCount(avgLateCount); + /*if(avgDelay>=5) + signalBarCount=1; + else if(avgDelay>=4) + signalBarCount=MIN(signalBarCount, 2); + else if(avgDelay>=3) + signalBarCount=MIN(signalBarCount, 3);*/ + + if(avgLateCount[2]>=0.2) + signalBarCount=1; + else if(avgLateCount[2]>=0.1) + signalBarCount=MIN(signalBarCount, 2); + } + } + + { + MutexGuard m(endpointsMutex); + SendRelayPings(); + if(udpConnectivityState==UDP_UNKNOWN){ + for(std::vector::iterator itr=endpoints.begin(); itr!=endpoints.end(); ++itr){ + if((*itr)->type==Endpoint::TYPE_UDP_RELAY){ + SendUdpPing(*itr); } } - } - if(minPingRelay!=preferredRelay){ - preferredRelay=minPingRelay; - LOGV("set preferred relay to %s", preferredRelay->address.ToString().c_str()); - if(currentEndpoint->type==EP_TYPE_UDP_RELAY || currentEndpoint->type==EP_TYPE_TCP_RELAY) - currentEndpoint=preferredRelay; - LogDebugInfo(); - /*BufferOutputStream pkt(32); - pkt.WriteInt64(preferredRelay->id); - SendPacketReliably(PKT_SWITCH_PREF_RELAY, pkt.GetBuffer(), pkt.GetLength(), 1, 9);*/ - } - if(currentEndpoint->type==EP_TYPE_UDP_RELAY){ - Endpoint* p2p=GetEndpointByType(EP_TYPE_UDP_P2P_INET); - if(p2p){ - Endpoint* lan=GetEndpointByType(EP_TYPE_UDP_P2P_LAN); - if(lan && lan->averageRTT>0 && lan->averageRTT=(udpPingCount::iterator itr=endpoints.begin(); itr!=endpoints.end(); ++itr){ + if((*itr)->type==Endpoint::TYPE_UDP_RELAY){ + SendUdpPing(*itr); + } + } + udpPingCount++; + lastUdpPingTime=time; }else{ - if(p2p->averageRTT>0 && p2p->averageRTT::iterator itr=endpoints.begin();itr!=endpoints.end();++itr){ + if((*itr)->type==Endpoint::TYPE_UDP_RELAY){ + if((*itr)->udpPongCount>0){ + avgPongs+=(double) (*itr)->udpPongCount; + count++; + } + } } + if(count>0) + avgPongs/=(double)count; + else + avgPongs=0.0; + LOGI("UDP ping reply count: %.2f", avgPongs); + bool configUseTCP=ServerConfig::GetSharedInstance()->GetBoolean("use_tcp", true); + if(configUseTCP){ + if(avgPongs==0.0 || (udpConnectivityState==UDP_BAD && avgPongs<7.0)){ + udpConnectivityState=UDP_NOT_AVAILABLE; + useTCP=true; + useUDP=false; + waitingForRelayPeerInfo=false; + if(currentEndpoint->type!=Endpoint::TYPE_TCP_RELAY) + setCurrentEndpointToTCP=true; + }else if(avgPongs<3.0){ + udpConnectivityState=UDP_BAD; + useTCP=true; + setCurrentEndpointToTCP=true; + }else{ + udpConnectivityState=UDP_AVAILABLE; + } + }else{ + udpConnectivityState=UDP_NOT_AVAILABLE; + } + //LOGW("No UDP ping replies received; assuming no connectivity and trying TCP") + //udpConnectivityState=UDP_NOT_AVAILABLE; + //useTCP=true; } } - }else{ - if(minPing>0 && minPingaverageRTT*p2pToRelaySwitchThreshold){ - LOGI("Switching to relay"); - currentEndpoint=preferredRelay; - LogDebugInfo(); - } } } - if(udpConnectivityState==UDP_UNKNOWN){ - for(std::vector::iterator itr=endpoints.begin();itr!=endpoints.end();++itr){ - if((*itr)->type==EP_TYPE_UDP_RELAY){ - SendUdpPing(*itr); - } - } - udpConnectivityState=UDP_PING_SENT; - lastUdpPingTime=time; - udpPingCount=1; - }else if(udpConnectivityState==UDP_PING_SENT){ - if(time-lastUdpPingTime>=0.5){ - if(udpPingCount<4){ - for(std::vector::iterator itr=endpoints.begin();itr!=endpoints.end();++itr){ - if((*itr)->type==EP_TYPE_UDP_RELAY){ - SendUdpPing(*itr); - } - } - udpPingCount++; - lastUdpPingTime=time; - }else{ - LOGW("No UDP ping replies received; assuming no connectivity and trying TCP") - udpConnectivityState=UDP_NOT_AVAILABLE; - useTCP=true; - } - } - } - unlock_mutex(endpointsMutex); if(state==STATE_ESTABLISHED || state==STATE_RECONNECTING){ if(time-lastRecvPacketTime>=config.recv_timeout){ - if(currentEndpoint && currentEndpoint->type!=EP_TYPE_UDP_RELAY && currentEndpoint->type!=EP_TYPE_TCP_RELAY){ + if(currentEndpoint && currentEndpoint->type!=Endpoint::TYPE_UDP_RELAY && currentEndpoint->type!=Endpoint::TYPE_TCP_RELAY){ LOGW("Packet receive timeout, switching to relay"); currentEndpoint=preferredRelay; for(std::vector::iterator itrtr=endpoints.begin();itrtr!=endpoints.end();++itrtr){ Endpoint* e=*itrtr; - if(e->type==EP_TYPE_UDP_P2P_INET || e->type==EP_TYPE_UDP_P2P_LAN){ + if(e->type==Endpoint::TYPE_UDP_P2P_INET || e->type==Endpoint::TYPE_UDP_P2P_LAN){ e->averageRTT=0; memset(e->rtts, 0, sizeof(e->rtts)); } @@ -1688,14 +2263,14 @@ void VoIPController::RunTickThread(){ lastRecvPacketTime=time; }else{ LOGW("Packet receive timeout, disconnecting"); - lastError=TGVOIP_ERROR_TIMEOUT; + lastError=ERROR_TIMEOUT; SetState(STATE_FAILED); } } }else if(state==STATE_WAIT_INIT || state==STATE_WAIT_INIT_ACK){ if(GetCurrentTime()-connectionInitTime>=config.init_timeout){ LOGW("Init timeout, disconnecting"); - lastError=TGVOIP_ERROR_TIMEOUT; + lastError=ERROR_TIMEOUT; SetState(STATE_FAILED); } } @@ -1709,16 +2284,20 @@ void VoIPController::RunTickThread(){ setEstablishedAt=0; } - signalBarsHistory[tickCount%sizeof(signalBarsHistory)]=(unsigned char)signalBarCount; - int _signalBarCount=GetSignalBarsCount(); - if(_signalBarCount!=prevSignalBarCount){ - LOGD("SIGNAL BAR COUNT CHANGED: %d", _signalBarCount); - if(signalBarCountCallback) - signalBarCountCallback(this, _signalBarCount); + if(tickCount%10==0){ + signalBarsHistory[(tickCount/10)%sizeof(signalBarsHistory)]=(unsigned char) signalBarCount; + //LOGV("Signal bar count history %08X", *reinterpret_cast(&signalBarsHistory)); + int _signalBarCount=GetSignalBarsCount(); + if(_signalBarCount!=prevSignalBarCount){ + LOGD("SIGNAL BAR COUNT CHANGED: %d", _signalBarCount); + if(callbacks.signalBarCountChanged) + callbacks.signalBarCountChanged(this, _signalBarCount); + } } - if(statsDump){ + if(statsDump && incomingStreams.size()==1){ + JitterBuffer* jitterBuffer=incomingStreams[0].jitterBuffer; //fprintf(statsDump, "Time\tRTT\tLISeq\tLASeq\tCWnd\tBitrate\tJitter\tJDelay\tAJDelay\n"); fprintf(statsDump, "%.3f\t%.3f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%.3f\t%.3f\t%.3f\n", GetCurrentTime()-startTime, @@ -1736,73 +2315,103 @@ void VoIPController::RunTickThread(){ jitterBuffer ? jitterBuffer->GetAverageDelay()*0.06 : 0); } -#if defined(__APPLE__) && defined(TGVOIP_USE_AUDIO_SESSION) - if(needNotifyAcquiredAudioSession){ - needNotifyAcquiredAudioSession=false; - audio::AudioUnitIO::AudioSessionAcquired(); - } -#endif } LOGI("=== tick thread exiting ==="); } Endpoint& VoIPController::GetRemoteEndpoint(){ - //return useLan ? &remoteLanEp : &remotePublicEp; return *currentEndpoint; } -void VoIPController::SendPacket(unsigned char *data, size_t len, Endpoint* ep){ +void VoIPController::SendPacket(unsigned char *data, size_t len, Endpoint* ep, PendingOutgoingPacket& srcPacket){ if(stopping) return; - if(ep->type==EP_TYPE_TCP_RELAY && !useTCP) + if(ep->type==Endpoint::TYPE_TCP_RELAY && !useTCP) return; - //dst.sin_addr=ep->address; - //dst.sin_port=htons(ep->port); - //dst.sin_family=AF_INET; BufferOutputStream out(len+128); - if(ep->type==EP_TYPE_UDP_RELAY || ep->type==EP_TYPE_TCP_RELAY) + if(ep->type==Endpoint::TYPE_UDP_RELAY || ep->type==Endpoint::TYPE_TCP_RELAY) out.WriteBytes((unsigned char*)ep->peerTag, 16); else out.WriteBytes(callID, 16); if(len>0){ - BufferOutputStream inner(len+128); - inner.WriteInt32(len); - inner.WriteBytes(data, len); - if(inner.GetLength()%16!=0){ + if(useMTProto2){ + BufferOutputStream inner(len+128); + inner.WriteInt32((uint32_t)len); + inner.WriteBytes(data, len); size_t padLen=16-inner.GetLength()%16; - unsigned char padding[16]; + if(padLen<12) + padLen+=16; + unsigned char padding[28]; crypto.rand_bytes((uint8_t *) padding, padLen); inner.WriteBytes(padding, padLen); + assert(inner.GetLength()%16==0); + + unsigned char key[32], iv[32], msgKey[16]; + out.WriteBytes(keyFingerprint, 8); + BufferOutputStream buf(len+32); + size_t x=isOutgoing ? 0 : 8; + buf.WriteBytes(encryptionKey+88+x, 32); + buf.WriteBytes(inner.GetBuffer()+4, inner.GetLength()-4); + unsigned char msgKeyLarge[32]; + crypto.sha256(buf.GetBuffer(), buf.GetLength(), msgKeyLarge); + memcpy(msgKey, msgKeyLarge+8, 16); + KDF2(msgKey, isOutgoing ? 0 : 8, key, iv); + out.WriteBytes(msgKey, 16); + //LOGV("<- MSG KEY: %08x %08x %08x %08x, hashed %u", *reinterpret_cast(msgKey), *reinterpret_cast(msgKey+4), *reinterpret_cast(msgKey+8), *reinterpret_cast(msgKey+12), inner.GetLength()-4); + + unsigned char aesOut[MSC_STACK_FALLBACK(inner.GetLength(), 1500)]; + crypto.aes_ige_encrypt(inner.GetBuffer(), aesOut, inner.GetLength(), key, iv); + out.WriteBytes(aesOut, inner.GetLength()); + }else{ + BufferOutputStream inner(len+128); + inner.WriteInt32(len); + inner.WriteBytes(data, len); + if(inner.GetLength()%16!=0){ + size_t padLen=16-inner.GetLength()%16; + unsigned char padding[16]; + crypto.rand_bytes((uint8_t *) padding, padLen); + inner.WriteBytes(padding, padLen); + } + assert(inner.GetLength()%16==0); + unsigned char key[32], iv[32], msgHash[SHA1_LENGTH]; + crypto.sha1((uint8_t *) inner.GetBuffer(), len+4, msgHash); + out.WriteBytes(keyFingerprint, 8); + out.WriteBytes((msgHash+(SHA1_LENGTH-16)), 16); + KDF(msgHash+(SHA1_LENGTH-16), isOutgoing ? 0 : 8, key, iv); + unsigned char aesOut[MSC_STACK_FALLBACK(inner.GetLength(), 1500)]; + crypto.aes_ige_encrypt(inner.GetBuffer(), aesOut, inner.GetLength(), key, iv); + out.WriteBytes(aesOut, inner.GetLength()); } - assert(inner.GetLength()%16==0); - unsigned char key[32], iv[32], msgHash[SHA1_LENGTH]; - crypto.sha1((uint8_t *) inner.GetBuffer(), len+4, msgHash); - out.WriteBytes(keyFingerprint, 8); - out.WriteBytes((msgHash+(SHA1_LENGTH-16)), 16); - KDF(msgHash+(SHA1_LENGTH-16), isOutgoing ? 0 : 8, key, iv); - unsigned char aesOut[MSC_STACK_FALLBACK(inner.GetLength(), 1500)]; - crypto.aes_ige_encrypt(inner.GetBuffer(), aesOut, inner.GetLength(), key, iv); - out.WriteBytes(aesOut, inner.GetLength()); } //LOGV("Sending %d bytes to %s:%d", out.GetLength(), ep->address.ToString().c_str(), ep->port); - if(IS_MOBILE_NETWORK(networkType)) - stats.bytesSentMobile+=(uint64_t)out.GetLength(); - else - stats.bytesSentWifi+=(uint64_t)out.GetLength(); - NetworkPacket pkt; + NetworkPacket pkt={0}; pkt.address=(NetworkAddress*)&ep->address; pkt.port=ep->port; pkt.length=out.GetLength(); pkt.data=out.GetBuffer(); - pkt.protocol=ep->type==EP_TYPE_TCP_RELAY ? PROTO_TCP : PROTO_UDP; - //socket->Send(&pkt); - if(ep->type==EP_TYPE_TCP_RELAY){ - if(ep->socket){ + pkt.protocol=ep->type==Endpoint::TYPE_TCP_RELAY ? PROTO_TCP : PROTO_UDP; + ActuallySendPacket(pkt, ep); +} + +void VoIPController::ActuallySendPacket(NetworkPacket &pkt, Endpoint* ep){ + //LOGI("Sending packet of %d bytes", pkt.length); + if(IS_MOBILE_NETWORK(networkType)) + stats.bytesSentMobile+=(uint64_t)pkt.length; + else + stats.bytesSentWifi+=(uint64_t)pkt.length; + if(ep->type==Endpoint::TYPE_TCP_RELAY){ + if(ep->socket && !ep->socket->IsFailed()){ ep->socket->Send(&pkt); }else{ + if(ep->socket){ + LOGD("closing failed TCP socket: %s:%u", ep->address.ToString().c_str(), ep->port); + ep->socket->Close(); + delete ep->socket; + ep->socket=NULL; + } LOGI("connecting to tcp: %s:%u", ep->address.ToString().c_str(), ep->port); NetworkSocket* s; if(proxyProtocol==PROXY_NONE){ @@ -1863,7 +2472,6 @@ void VoIPController::SendPacket(unsigned char *data, size_t len, Endpoint* ep){ } } - void VoIPController::SetNetworkType(int type){ networkType=type; UpdateDataSavingState(); @@ -1876,33 +2484,32 @@ void VoIPController::SetNetworkType(int type){ activeNetItfName=itfName; if(isFirstChange) return; - if(currentEndpoint && currentEndpoint->type!=EP_TYPE_UDP_RELAY){ - if(preferredRelay->type==EP_TYPE_UDP_RELAY) + if(currentEndpoint && currentEndpoint->type!=Endpoint::TYPE_UDP_RELAY){ + if(preferredRelay->type==Endpoint::TYPE_UDP_RELAY) currentEndpoint=preferredRelay; - lock_mutex(endpointsMutex); + MutexGuard m(endpointsMutex); for(std::vector::iterator itr=endpoints.begin();itr!=endpoints.end();){ Endpoint* endpoint=*itr; - if(endpoint->type==EP_TYPE_UDP_RELAY && useTCP){ + if(endpoint->type==Endpoint::TYPE_UDP_RELAY && useTCP){ useTCP=false; - if(preferredRelay->type==EP_TYPE_TCP_RELAY){ + if(preferredRelay->type==Endpoint::TYPE_TCP_RELAY){ preferredRelay=endpoint; currentEndpoint=endpoint; } - }else if(endpoint->type==EP_TYPE_TCP_RELAY && endpoint->socket){ + }else if(endpoint->type==Endpoint::TYPE_TCP_RELAY && endpoint->socket){ endpoint->socket->Close(); } - //if(endpoint->type==EP_TYPE_UDP_P2P_INET){ + //if(endpoint->type==Endpoint::TYPE_UDP_P2P_INET){ endpoint->averageRTT=0; memset(endpoint->rtts, 0, sizeof(endpoint->rtts)); //} - if(endpoint->type==EP_TYPE_UDP_P2P_LAN){ + if(endpoint->type==Endpoint::TYPE_UDP_P2P_LAN){ delete endpoint; itr=endpoints.erase(itr); }else{ ++itr; } } - unlock_mutex(endpointsMutex); } udpConnectivityState=UDP_UNKNOWN; udpPingCount=0; @@ -1918,10 +2525,27 @@ void VoIPController::SetNetworkType(int type){ selectCanceller->CancelSelect(); } LOGI("set network type: %d, active interface %s", type, activeNetItfName.c_str()); - /*if(type==NET_TYPE_GPRS || type==NET_TYPE_EDGE) - audioPacketGrouping=2; - else - audioPacketGrouping=1;*/ +} + +void VoIPGroupController::SetNetworkType(int type){ + networkType=type; + UpdateDataSavingState(); + UpdateAudioBitrate(); + std::string itfName=udpSocket->GetLocalInterfaceInfo(NULL, NULL); + if(itfName!=activeNetItfName){ + udpSocket->OnActiveInterfaceChanged(); + LOGI("Active network interface changed: %s -> %s", activeNetItfName.c_str(), itfName.c_str()); + bool isFirstChange=activeNetItfName.length()==0; + activeNetItfName=itfName; + if(isFirstChange) + return; + udpConnectivityState=UDP_UNKNOWN; + udpPingCount=0; + lastUdpPingTime=0; + if(proxyProtocol==PROXY_SOCKS5) + InitUDPProxy(); + selectCanceller->CancelSelect(); + } } @@ -1930,14 +2554,19 @@ double VoIPController::GetAverageRTT(){ uint32_t diff=lastSentSeq-lastRemoteAckSeq; //LOGV("rtt diff=%u", diff); if(diff<32){ - int i; double res=0; int count=0; - for(i=diff;i<32;i++){ + /*for(i=diff;i<32;i++){ if(remoteAcks[i-diff]>0){ res+=(remoteAcks[i-diff]-sentPacketTimes[i]); count++; } + }*/ + for(std::vector::iterator itr=recentOutgoingPackets.begin();itr!=recentOutgoingPackets.end();++itr){ + if(itr->ackTime>0){ + res+=(itr->ackTime-itr->sendTime); + count++; + } } if(count>0) res/=count; @@ -1979,21 +2608,15 @@ double VoIPController::GetCurrentTime(){ #endif } -void VoIPController::SetStateCallback(void (* f)(VoIPController*, int)){ - stateCallback=f; - if(stateCallback){ - stateCallback(this, state); - } -} - - void VoIPController::SetState(int state){ this->state=state; LOGV("Call state changed to %d", state); stateChangeTime=GetCurrentTime(); - if(stateCallback){ - stateCallback(this, state); + if(callbacks.connectionStateChanged){ + callbacks.connectionStateChanged(this, state); } + if(state==STATE_ESTABLISHED) + SetMicMute(micMuted); } @@ -2005,21 +2628,23 @@ void VoIPController::SetMicMute(bool mute){ else audioInput->Start(); if(!audioInput->IsInitialized()){ - lastError=TGVOIP_ERROR_AUDIO_IO; + lastError=ERROR_AUDIO_IO; SetState(STATE_FAILED); return; } } if(echoCanceller) echoCanceller->Enable(!mute); - int i; - for(i=0;itype==STREAM_TYPE_AUDIO){ - unsigned char buf[2]; - buf[0]=outgoingStreams[i]->id; - buf[1]=(char) (mute ? 0 : 1); - SendPacketReliably(PKT_STREAM_STATE, buf, 2, .5f, 20); - outgoingStreams[i]->enabled=!mute; + if(state==STATE_ESTABLISHED){ + unsigned int i; + for(i=0; itype==STREAM_TYPE_AUDIO && incomingStreams[i]->enabled) + if(incomingStreams[i].type==STREAM_TYPE_AUDIO && incomingStreams[i].enabled) areAnyAudioStreamsEnabled=true; } - if(jitterBuffer){ + /*if(jitterBuffer){ jitterBuffer->Reset(); } if(decoder){ decoder->ResetQueue(); - } + }*/ if(audioOutput){ if(audioOutput->IsPlaying()!=areAnyAudioStreamsEnabled){ if(areAnyAudioStreamsEnabled) @@ -2082,24 +2707,45 @@ void VoIPController::KDF(unsigned char* msgKey, size_t x, unsigned char* aesKey, memcpy(aesIv, buf.GetBuffer(), 32); } +void VoIPController::KDF2(unsigned char* msgKey, size_t x, unsigned char *aesKey, unsigned char *aesIv){ + uint8_t sA[32], sB[32]; + BufferOutputStream buf(128); + buf.WriteBytes(msgKey, 16); + buf.WriteBytes(encryptionKey+x, 36); + crypto.sha256(buf.GetBuffer(), buf.GetLength(), sA); + buf.Reset(); + buf.WriteBytes(encryptionKey+40+x, 36); + buf.WriteBytes(msgKey, 16); + crypto.sha256(buf.GetBuffer(), buf.GetLength(), sB); + buf.Reset(); + buf.WriteBytes(sA, 8); + buf.WriteBytes(sB+8, 16); + buf.WriteBytes(sA+24, 8); + memcpy(aesKey, buf.GetBuffer(), 32); + buf.Reset(); + buf.WriteBytes(sB, 8); + buf.WriteBytes(sA+8, 16); + buf.WriteBytes(sB+24, 8); + memcpy(aesIv, buf.GetBuffer(), 32); +} + void VoIPController::GetDebugString(char *buffer, size_t len){ char endpointsBuf[10240]; memset(endpointsBuf, 0, 10240); - int i; for(std::vector::iterator itrtr=endpoints.begin();itrtr!=endpoints.end();++itrtr){ const char* type; Endpoint* endpoint=*itrtr; switch(endpoint->type){ - case EP_TYPE_UDP_P2P_INET: + case Endpoint::TYPE_UDP_P2P_INET: type="UDP_P2P_INET"; break; - case EP_TYPE_UDP_P2P_LAN: + case Endpoint::TYPE_UDP_P2P_LAN: type="UDP_P2P_LAN"; break; - case EP_TYPE_UDP_RELAY: + case Endpoint::TYPE_UDP_RELAY: type="UDP_RELAY"; break; - case EP_TYPE_TCP_RELAY: + case Endpoint::TYPE_TCP_RELAY: type="TCP_RELAY"; break; default: @@ -2108,9 +2754,10 @@ void VoIPController::GetDebugString(char *buffer, size_t len){ } if(strlen(endpointsBuf)>10240-1024) break; - sprintf(endpointsBuf+strlen(endpointsBuf), "%s:%u %dms [%s%s]\n", endpoint->address.ToString().c_str(), endpoint->port, (int)(endpoint->averageRTT*1000), type, currentEndpoint==endpoint ? ", IN_USE" : ""); + sprintf(endpointsBuf+strlen(endpointsBuf), "%s:%u %dms %d [%s%s]\n", endpoint->address.ToString().c_str(), endpoint->port, (int)(endpoint->averageRTT*1000), endpoint->udpPongCount, type, currentEndpoint==endpoint ? ", IN_USE" : ""); } double avgLate[3]; + JitterBuffer* jitterBuffer=incomingStreams.size()==1 ? incomingStreams[0].jitterBuffer : NULL; if(jitterBuffer) jitterBuffer->GetAverageLateCount(avgLate); else @@ -2120,7 +2767,7 @@ void VoIPController::GetDebugString(char *buffer, size_t len){ "Jitter buffer: %d/%.2f | %.1f, %.1f, %.1f\n" "RTT avg/min: %d/%d\n" "Congestion window: %d/%d bytes\n" - "Key fingerprint: %02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX\n" + "Key fingerprint: %02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%s\n" "Last sent/ack'd seq: %u/%u\n" "Last recvd seq: %u\n" "Send/recv losses: %u/%u (%d%%)\n" @@ -2134,11 +2781,12 @@ void VoIPController::GetDebugString(char *buffer, size_t len){ (int)(conctl->GetAverageRTT()*1000), (int)(conctl->GetMinimumRTT()*1000), int(conctl->GetInflightDataSize()), int(conctl->GetCongestionWindow()), keyFingerprint[0],keyFingerprint[1],keyFingerprint[2],keyFingerprint[3],keyFingerprint[4],keyFingerprint[5],keyFingerprint[6],keyFingerprint[7], + useMTProto2 ? " (MTProto2.0)" : "", lastSentSeq, lastRemoteAckSeq, lastRemoteSeq, conctl->GetSendLossCount(), recvLossCount, encoder ? encoder->GetPacketLoss() : 0, encoder ? (encoder->GetBitrate()/1000) : 0, // audioPacketGrouping, - outgoingStreams[0]->frameDuration, incomingStreams.size()>0 ? incomingStreams[0]->frameDuration : 0, + outgoingStreams[0].frameDuration, incomingStreams.size()>0 ? incomingStreams[0].frameDuration : 0, (long long unsigned int)(stats.bytesSentMobile+stats.bytesSentWifi), (long long unsigned int)(stats.bytesRecvdMobile+stats.bytesRecvdWifi)); } @@ -2163,7 +2811,7 @@ void VoIPController::SendPublicEndpointsRequest(Endpoint& relay){ unsigned char buf[32]; memcpy(buf, relay.peerTag, 16); memset(buf+16, 0xFF, 16); - NetworkPacket pkt; + NetworkPacket pkt={0}; pkt.data=buf; pkt.length=32; pkt.address=(NetworkAddress*)&relay.address; @@ -2173,7 +2821,7 @@ void VoIPController::SendPublicEndpointsRequest(Endpoint& relay){ } Endpoint* VoIPController::GetEndpointByType(int type){ - if(type==EP_TYPE_UDP_RELAY && preferredRelay) + if(type==Endpoint::TYPE_UDP_RELAY && preferredRelay) return preferredRelay; for(std::vector::iterator itrtr=endpoints.begin();itrtr!=endpoints.end();++itrtr){ if((*itrtr)->type==type) @@ -2205,9 +2853,8 @@ void VoIPController::SendPacketReliably(unsigned char type, unsigned char *data, pkt->timeout=timeout; pkt->firstSentTime=0; pkt->lastSentTime=0; - lock_mutex(queuedPacketsMutex); + MutexGuard m(queuedPacketsMutex); queuedPackets.push_back(pkt); - unlock_mutex(queuedPacketsMutex); } @@ -2215,19 +2862,26 @@ void VoIPController::SetConfig(voip_config_t *cfg){ memcpy(&config, cfg, sizeof(voip_config_t)); if(tgvoipLogFile){ fclose(tgvoipLogFile); + tgvoipLogFile=NULL; } if(strlen(cfg->logFilePath)){ tgvoipLogFile=fopen(cfg->logFilePath, "a"); - tgvoip_log_file_write_header(); + tgvoip_log_file_write_header(tgvoipLogFile); + }else{ + tgvoipLogFile=NULL; } - if(statsDump) + if(statsDump){ fclose(statsDump); + statsDump=NULL; + } if(strlen(cfg->statsDumpFilePath)){ statsDump=fopen(cfg->statsDumpFilePath, "w"); if(statsDump) fprintf(statsDump, "Time\tRTT\tLRSeq\tLSSeq\tLASeq\tLostR\tLostS\tCWnd\tBitrate\tLoss%%\tJitter\tJDelay\tAJDelay\n"); else LOGW("Failed to open stats dump file %s for writing", cfg->statsDumpFilePath); + }else{ + statsDump=NULL; } UpdateDataSavingState(); UpdateAudioBitrate(); @@ -2259,7 +2913,7 @@ void VoIPController::DebugCtl(int request, int param){ } }else if(request==3){ // force enable/disable p2p allowP2p=param==1; - if(!allowP2p && currentEndpoint && currentEndpoint->type!=EP_TYPE_UDP_RELAY){ + if(!allowP2p && currentEndpoint && currentEndpoint->type!=Endpoint::TYPE_UDP_RELAY){ currentEndpoint=preferredRelay; }else if(allowP2p){ SendPublicEndpointsRequest(); @@ -2312,16 +2966,16 @@ void VoIPController::LogDebugInfo(){ char buffer[1024]; const char* typeStr="unknown"; switch(e->type){ - case EP_TYPE_UDP_RELAY: + case Endpoint::TYPE_UDP_RELAY: typeStr="udp_relay"; break; - case EP_TYPE_UDP_P2P_INET: + case Endpoint::TYPE_UDP_P2P_INET: typeStr="udp_p2p_inet"; break; - case EP_TYPE_UDP_P2P_LAN: + case Endpoint::TYPE_UDP_P2P_LAN: typeStr="udp_p2p_lan"; break; - case EP_TYPE_TCP_RELAY: + case Endpoint::TYPE_TCP_RELAY: typeStr="tcp_relay"; break; } @@ -2441,7 +3095,7 @@ void VoIPController::SetProxy(int protocol, std::string address, uint16_t port, } void VoIPController::SendUdpPing(Endpoint *endpoint){ - if(endpoint->type!=EP_TYPE_UDP_RELAY) + if(endpoint->type!=Endpoint::TYPE_UDP_RELAY) return; LOGV("Sending UDP ping to %s:%d", endpoint->address.ToString().c_str(), endpoint->port); BufferOutputStream p(1024); @@ -2451,7 +3105,7 @@ void VoIPController::SendUdpPing(Endpoint *endpoint){ p.WriteInt32(-1); p.WriteInt32(-2); p.WriteInt64(12345); - NetworkPacket pkt; + NetworkPacket pkt={0}; pkt.address=&endpoint->address; pkt.port=endpoint->port; pkt.protocol=PROTO_UDP; @@ -2460,15 +3114,405 @@ void VoIPController::SendUdpPing(Endpoint *endpoint){ udpSocket->Send(&pkt); } +void VoIPGroupController::SendRecentPacketsRequest(){ + BufferOutputStream out(1024); + out.WriteInt32(TLID_UDP_REFLECTOR_REQUEST_PACKETS_INFO); // TL function + out.WriteInt32(GetCurrentUnixtime()); // date:int + out.WriteInt64(0); // query_id:long + out.WriteInt32(64); // recv_num:int + out.WriteInt32(0); // sent_num:int + SendSpecialReflectorRequest(out.GetBuffer(), out.GetLength()); +} + +void VoIPGroupController::SendSpecialReflectorRequest(unsigned char *data, size_t len){ + BufferOutputStream out(1024); + unsigned char buf[1500]; + crypto.rand_bytes(buf, 8); + out.WriteBytes(buf, 8); + out.WriteInt32((int32_t)len); + out.WriteBytes(data, len); + if(out.GetLength()%16!=0){ + size_t paddingLen=16-(out.GetLength()%16); + crypto.rand_bytes(buf, paddingLen); + out.WriteBytes(buf, paddingLen); + } + unsigned char iv[16]; + crypto.rand_bytes(iv, 16); + unsigned char key[32]; + crypto.sha256(reflectorSelfSecret, 16, key); + unsigned char _iv[16]; + memcpy(_iv, iv, 16); + size_t encryptedLen=out.GetLength(); + crypto.aes_cbc_encrypt(out.GetBuffer(), buf, encryptedLen, key, _iv); + out.Reset(); + out.WriteBytes(reflectorSelfTag, 16); + out.WriteBytes(iv, 16); + out.WriteBytes(buf, encryptedLen); + out.WriteBytes(reflectorSelfSecret, 16); + crypto.sha256(out.GetBuffer(), out.GetLength(), buf); + out.Rewind(16); + out.WriteBytes(buf, 16); + + NetworkPacket pkt={0}; + pkt.address=&groupReflector->address; + pkt.port=groupReflector->port; + pkt.protocol=PROTO_UDP; + pkt.data=out.GetBuffer(); + pkt.length=out.GetLength(); + ActuallySendPacket(pkt, groupReflector); +} + +void VoIPController::SendRelayPings(){ + //LOGV("Send relay pings 1"); + if((state==STATE_ESTABLISHED || state==STATE_RECONNECTING) && endpoints.size()>1){ + Endpoint* minPingRelay=preferredRelay; + double minPing=preferredRelay->averageRTT*(preferredRelay->type==Endpoint::TYPE_TCP_RELAY ? 2 : 1); + for(std::vector::iterator e=endpoints.begin();e!=endpoints.end();++e){ + Endpoint* endpoint=*e; + if(endpoint->type==Endpoint::TYPE_TCP_RELAY && !useTCP) + continue; + if(GetCurrentTime()-endpoint->lastPingTime>=10){ + LOGV("Sending ping to %s", endpoint->address.ToString().c_str()); + unsigned char* buf=outgoingPacketsBufferPool.Get(); + if(buf){ + sendQueue->Put(PendingOutgoingPacket{ + /*.seq=*/(endpoint->lastPingSeq=GenerateOutSeq()), + /*.type=*/PKT_PING, + /*.len=*/0, + /*.data=*/buf, + /*.endpoint=*/endpoint + }); + } + endpoint->lastPingTime=GetCurrentTime(); + } + if(endpoint->type==Endpoint::TYPE_UDP_RELAY || (useTCP && endpoint->type==Endpoint::TYPE_TCP_RELAY)){ + double k=endpoint->type==Endpoint::TYPE_UDP_RELAY ? 1 : 2; + if(endpoint->averageRTT>0 && endpoint->averageRTT*kaverageRTT*k; + minPingRelay=endpoint; + } + } + } + if(minPingRelay!=preferredRelay){ + preferredRelay=minPingRelay; + LOGV("set preferred relay to %s", preferredRelay->address.ToString().c_str()); + if(currentEndpoint->type==Endpoint::TYPE_UDP_RELAY || currentEndpoint->type==Endpoint::TYPE_TCP_RELAY) + currentEndpoint=preferredRelay; + LogDebugInfo(); + /*BufferOutputStream pkt(32); + pkt.WriteInt64(preferredRelay->id); + SendPacketReliably(PKT_SWITCH_PREF_RELAY, pkt.GetBuffer(), pkt.GetLength(), 1, 9);*/ + } + if(currentEndpoint->type==Endpoint::TYPE_UDP_RELAY){ + Endpoint* p2p=GetEndpointByType(Endpoint::TYPE_UDP_P2P_INET); + if(p2p){ + Endpoint* lan=GetEndpointByType(Endpoint::TYPE_UDP_P2P_LAN); + if(lan && lan->averageRTT>0 && lan->averageRTTaverageRTT>0 && p2p->averageRTT0 && minPingaverageRTT*p2pToRelaySwitchThreshold){ + LOGI("Switching to relay"); + currentEndpoint=preferredRelay; + LogDebugInfo(); + } + } + } +} + +void VoIPGroupController::SendRelayPings(){ + //LOGV("Send relay pings 2"); + double currentTime=GetCurrentTime(); + if(currentTime-groupReflector->lastPingTime>=0.25){ + SendRecentPacketsRequest(); + groupReflector->lastPingTime=currentTime; + } +} + +void VoIPController::StartAudio(){ + Stream& outgoingAudioStream=outgoingStreams[0]; + LOGI("before create audio io"); + void* platformSpecific=NULL; +#ifdef __APPLE__ + platformSpecific=appleAudioIO; +#endif + audioInput=tgvoip::audio::AudioInput::Create(currentAudioInput, platformSpecific); + audioInput->Configure(48000, 16, 1); + audioOutput=tgvoip::audio::AudioOutput::Create(currentAudioOutput, platformSpecific); + audioOutput->Configure(48000, 16, 1); + echoCanceller=new EchoCanceller(config.enableAEC, config.enableNS, config.enableAGC); + encoder=new OpusEncoder(audioInput); + encoder->SetCallback(AudioInputCallback, this); + encoder->SetOutputFrameDuration(outgoingAudioStream.frameDuration); + encoder->SetEchoCanceller(echoCanceller); + encoder->Start(); + if(!micMuted){ + audioInput->Start(); + if(!audioInput->IsInitialized()){ + LOGE("Erorr initializing audio capture"); + lastError=ERROR_AUDIO_IO; + + SetState(STATE_FAILED); + return; + } + } + if(!audioOutput->IsInitialized()){ + LOGE("Erorr initializing audio playback"); + lastError=ERROR_AUDIO_IO; + + SetState(STATE_FAILED); + return; + } + UpdateAudioBitrate(); + + /*voip_stream_t* incomingAudioStream=incomingStreams[0]; + jitterBuffer=new JitterBuffer(NULL, incomingAudioStream->frameDuration); + decoder=new OpusDecoder(audioOutput); + decoder->SetEchoCanceller(echoCanceller); + decoder->SetJitterBuffer(jitterBuffer); + decoder->SetFrameDuration(incomingAudioStream->frameDuration); + decoder->Start(); + if(incomingAudioStream->frameDuration>50) + jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_60", 3)); + else if(incomingAudioStream->frameDuration>30) + jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_40", 4)); + else + jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_20", 6));*/ + //audioOutput->Start(); + OnAudioOutputReady(); +} + +void VoIPController::OnAudioOutputReady(){ + Stream& stm=incomingStreams[0]; + outputAGC=new AutomaticGainControl(); + outputAGC->SetPassThrough(!outputAGCEnabled); + stm.decoder=new OpusDecoder(audioOutput, true); + stm.decoder->AddAudioEffect(outputAGC); + stm.decoder->SetEchoCanceller(echoCanceller); + stm.decoder->SetJitterBuffer(stm.jitterBuffer); + stm.decoder->SetFrameDuration(stm.frameDuration); + stm.decoder->Start(); +} + +void VoIPGroupController::OnAudioOutputReady(){ + encoder->SetDTX(true); + audioMixer->SetOutput(audioOutput); + audioMixer->SetEchoCanceller(echoCanceller); + audioMixer->Start(); + audioOutput->Start(); + audioOutStarted=true; + encoder->SetLevelMeter(&selfLevelMeter); +} + +void VoIPGroupController::WritePacketHeader(uint32_t seq, BufferOutputStream *s, unsigned char type, uint32_t length){ + s->WriteInt32(TLID_DECRYPTED_AUDIO_BLOCK); + int64_t randomID; + crypto.rand_bytes((uint8_t *) &randomID, 8); + s->WriteInt64(randomID); + unsigned char randBytes[7]; + crypto.rand_bytes(randBytes, 7); + s->WriteByte(7); + s->WriteBytes(randBytes, 7); + uint32_t pflags=PFLAG_HAS_SEQ | PFLAG_HAS_SENDER_TAG_HASH; + if(length>0) + pflags|=PFLAG_HAS_DATA; + pflags|=((uint32_t) type) << 24; + s->WriteInt32(pflags); + + if(type==PKT_STREAM_DATA || type==PKT_STREAM_DATA_X2 || type==PKT_STREAM_DATA_X3){ + conctl->PacketSent(seq, length); + } + + /*if(pflags & PFLAG_HAS_CALL_ID){ + s->WriteBytes(callID, 16); + }*/ + //s->WriteInt32(lastRemoteSeq); + s->WriteInt32(seq); + s->WriteBytes(reflectorSelfTagHash, 16); + if(length>0){ + if(length<=253){ + s->WriteByte((unsigned char) length); + }else{ + s->WriteByte(254); + s->WriteByte((unsigned char) (length & 0xFF)); + s->WriteByte((unsigned char) ((length >> 8) & 0xFF)); + s->WriteByte((unsigned char) ((length >> 16) & 0xFF)); + } + } +} + +void VoIPGroupController::SendPacket(unsigned char *data, size_t len, Endpoint *ep, PendingOutgoingPacket& srcPacket){ + if(stopping) + return; + if(ep->type==Endpoint::TYPE_TCP_RELAY && !useTCP) + return; + BufferOutputStream out(len+128); + //LOGV("send group packet %u", len); + + out.WriteBytes(reflectorSelfTag, 16); + + if(len>0){ + BufferOutputStream inner(len+128); + inner.WriteInt32((uint32_t)len); + inner.WriteBytes(data, len); + size_t padLen=16-inner.GetLength()%16; + if(padLen<12) + padLen+=16; + unsigned char padding[28]; + crypto.rand_bytes((uint8_t *) padding, padLen); + inner.WriteBytes(padding, padLen); + assert(inner.GetLength()%16==0); + + unsigned char key[32], iv[32], msgKey[16]; + out.WriteBytes(keyFingerprint, 8); + BufferOutputStream buf(len+32); + size_t x=0; + buf.WriteBytes(encryptionKey+88+x, 32); + buf.WriteBytes(inner.GetBuffer()+4, inner.GetLength()-4); + unsigned char msgKeyLarge[32]; + crypto.sha256(buf.GetBuffer(), buf.GetLength(), msgKeyLarge); + memcpy(msgKey, msgKeyLarge+8, 16); + KDF2(msgKey, 0, key, iv); + out.WriteBytes(msgKey, 16); + //LOGV("<- MSG KEY: %08x %08x %08x %08x, hashed %u", *reinterpret_cast(msgKey), *reinterpret_cast(msgKey+4), *reinterpret_cast(msgKey+8), *reinterpret_cast(msgKey+12), inner.GetLength()-4); + + unsigned char aesOut[MSC_STACK_FALLBACK(inner.GetLength(), 1500)]; + crypto.aes_ige_encrypt(inner.GetBuffer(), aesOut, inner.GetLength(), key, iv); + out.WriteBytes(aesOut, inner.GetLength()); + } + + // relay signature + out.WriteBytes(reflectorSelfSecret, 16); + unsigned char sig[32]; + crypto.sha256(out.GetBuffer(), out.GetLength(), sig); + out.Rewind(16); + out.WriteBytes(sig, 16); + + if(srcPacket.type==PKT_STREAM_DATA || srcPacket.type==PKT_STREAM_DATA_X2 || srcPacket.type==PKT_STREAM_DATA_X3){ + PacketIdMapping mapping={srcPacket.seq, *reinterpret_cast(sig+14), 0}; + MutexGuard m(sentPacketsMutex); + recentSentPackets.push_back(mapping); + //LOGD("sent packet with id: %04X", mapping.id); + while(recentSentPackets.size()>64) + recentSentPackets.erase(recentSentPackets.begin()); + } + lastSentSeq=srcPacket.seq; + + if(IS_MOBILE_NETWORK(networkType)) + stats.bytesSentMobile+=(uint64_t)out.GetLength(); + else + stats.bytesSentWifi+=(uint64_t)out.GetLength(); + + NetworkPacket pkt={0}; + pkt.address=(NetworkAddress*)&ep->address; + pkt.port=ep->port; + pkt.length=out.GetLength(); + pkt.data=out.GetBuffer(); + pkt.protocol=ep->type==Endpoint::TYPE_TCP_RELAY ? PROTO_TCP : PROTO_UDP; + ActuallySendPacket(pkt, ep); +} + int VoIPController::GetSignalBarsCount(){ unsigned char avg=0; - for(int i=0;i> 2; } -void VoIPController::SetSignalBarsCountCallback(void (*f)(VoIPController *, int)){ - signalBarCountCallback=f; +float VoIPGroupController::GetParticipantAudioLevel(int32_t userID){ + if(userID==userSelfID) + return selfLevelMeter.GetLevel(); + MutexGuard m(participantsMutex); + for(std::vector::iterator p=participants.begin(); p!=participants.end(); ++p){ + if(p->userID==userID){ + return p->levelMeter->GetLevel(); + } + } + return 0; +} + +void VoIPGroupController::SetMicMute(bool mute){ + micMuted=mute; + if(audioInput){ + if(mute) + audioInput->Stop(); + else + audioInput->Start(); + if(!audioInput->IsInitialized()){ + lastError=ERROR_AUDIO_IO; + SetState(STATE_FAILED); + return; + } + } + outgoingStreams[0].enabled=!mute; + SerializeAndUpdateOutgoingStreams(); +} + +void VoIPGroupController::SetParticipantVolume(int32_t userID, float volume){ + MutexGuard m(participantsMutex); + for(std::vector::iterator p=participants.begin();p!=participants.end();++p){ + if(p->userID==userID){ + for(std::vector::iterator s=p->streams.begin();s!=p->streams.end();++s){ + if(s->type==STREAM_TYPE_AUDIO){ + if(s->decoder){ + float db; + if(volume==0.0f) + db=-INFINITY; + else if(volume<1.0f) + db=-50.0f*(1.0f-volume); + else if(volume>1.0f && volume<=2.0f) + db=10.0f*(volume-1.0f); + else + db=0.0f; + //LOGV("Setting user %u audio volume to %.2f dB", userID, db); + audioMixer->SetInputVolume(s->callbackWrapper, db); + } + break; + } + } + break; + } + } +} + +void VoIPGroupController::SerializeAndUpdateOutgoingStreams(){ + BufferOutputStream out(1024); + out.WriteByte((unsigned char) outgoingStreams.size()); + + for(std::vector::iterator s=outgoingStreams.begin(); s!=outgoingStreams.end(); ++s){ + BufferOutputStream o(128); + o.WriteByte(s->id); + o.WriteByte(s->type); + o.WriteInt32(s->codec); + o.WriteInt32((unsigned char) ((s->enabled ? STREAM_FLAG_ENABLED : 0) | STREAM_FLAG_DTX)); + o.WriteInt16(s->frameDuration); + out.WriteInt16((int16_t) o.GetLength()); + out.WriteBytes(o.GetBuffer(), o.GetLength()); + } + if(groupCallbacks.updateStreams) + groupCallbacks.updateStreams(this, out.GetBuffer(), out.GetLength()); +} + +void VoIPGroupController::SetCallbacks(VoIPGroupController::Callbacks callbacks){ + VoIPController::SetCallbacks(callbacks); + this->groupCallbacks=callbacks; +} + +void VoIPController::SetCallbacks(VoIPController::Callbacks callbacks){ + this->callbacks=callbacks; + if(callbacks.connectionStateChanged) + callbacks.connectionStateChanged(this, state); } void VoIPController::SetAudioOutputGainControlEnabled(bool enabled){ @@ -2478,6 +3522,127 @@ void VoIPController::SetAudioOutputGainControlEnabled(bool enabled){ outputAGC->SetPassThrough(!enabled); } +uint32_t VoIPController::GetPeerCapabilities(){ + return peerCapabilities; +} + +void VoIPController::SendGroupCallKey(unsigned char *key){ + if(!(peerCapabilities & TGVOIP_PEER_CAP_GROUP_CALLS)){ + LOGE("Tried to send group call key but peer isn't capable of them"); + return; + } + if(didSendGroupCallKey){ + LOGE("Tried to send a group call key repeatedly"); + return; + } + if(!isOutgoing){ + LOGE("You aren't supposed to send group call key in an incoming call, use VoIPController::RequestCallUpgrade() instead"); + return; + } + didSendGroupCallKey=true; + SendPacketReliably(PKT_GROUP_CALL_KEY, key, 256, 0.5, 20); +} + +void VoIPController::RequestCallUpgrade(){ + if(!(peerCapabilities & TGVOIP_PEER_CAP_GROUP_CALLS)){ + LOGE("Tried to send group call key but peer isn't capable of them"); + return; + } + if(didSendUpgradeRequest){ + LOGE("Tried to send upgrade request repeatedly"); + return; + } + if(isOutgoing){ + LOGE("You aren't supposed to send an upgrade request in an outgoing call, generate an encryption key and use VoIPController::SendGroupCallKey instead"); + return; + } + didSendUpgradeRequest=true; + SendPacketReliably(PKT_REQUEST_GROUP, NULL, 0, 0.5, 20); +} + +void VoIPGroupController::GetDebugString(char *buffer, size_t len){ + char endpointsBuf[10240]; + memset(endpointsBuf, 0, 10240); + for(std::vector::iterator itrtr=endpoints.begin();itrtr!=endpoints.end();++itrtr){ + const char* type; + Endpoint* endpoint=*itrtr; + switch(endpoint->type){ + case Endpoint::TYPE_UDP_P2P_INET: + type="UDP_P2P_INET"; + break; + case Endpoint::TYPE_UDP_P2P_LAN: + type="UDP_P2P_LAN"; + break; + case Endpoint::TYPE_UDP_RELAY: + type="UDP_RELAY"; + break; + case Endpoint::TYPE_TCP_RELAY: + type="TCP_RELAY"; + break; + default: + type="UNKNOWN"; + break; + } + if(strlen(endpointsBuf)>10240-1024) + break; + sprintf(endpointsBuf+strlen(endpointsBuf), "%s:%u %dms [%s%s]\n", endpoint->address.ToString().c_str(), endpoint->port, (int)(endpoint->averageRTT*1000), type, currentEndpoint==endpoint ? ", IN_USE" : ""); + } + double avgLate[3]; + JitterBuffer* jitterBuffer=incomingStreams.size()==1 ? incomingStreams[0].jitterBuffer : NULL; + if(jitterBuffer) + jitterBuffer->GetAverageLateCount(avgLate); + else + memset(avgLate, 0, 3*sizeof(double)); + snprintf(buffer, len, + "Remote endpoints: \n%s" + "RTT avg/min: %d/%d\n" + "Congestion window: %d/%d bytes\n" + "Key fingerprint: %02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX\n" + "Last sent/ack'd seq: %u/%u\n" + "Send/recv losses: %u/%u (%d%%)\n" + "Audio bitrate: %d kbit\n" + "Bytes sent/recvd: %llu/%llu\n\n", + endpointsBuf, + (int)(conctl->GetAverageRTT()*1000), (int)(conctl->GetMinimumRTT()*1000), + int(conctl->GetInflightDataSize()), int(conctl->GetCongestionWindow()), + keyFingerprint[0],keyFingerprint[1],keyFingerprint[2],keyFingerprint[3],keyFingerprint[4],keyFingerprint[5],keyFingerprint[6],keyFingerprint[7], + lastSentSeq, lastRemoteAckSeq, + conctl->GetSendLossCount(), recvLossCount, encoder ? encoder->GetPacketLoss() : 0, + encoder ? (encoder->GetBitrate()/1000) : 0, + (long long unsigned int)(stats.bytesSentMobile+stats.bytesSentWifi), + (long long unsigned int)(stats.bytesRecvdMobile+stats.bytesRecvdWifi)); + + MutexGuard m(participantsMutex); + for(std::vector::iterator p=participants.begin();p!=participants.end();++p){ + snprintf(endpointsBuf, sizeof(endpointsBuf), "Participant id: %d\n", p->userID); + if(strlen(buffer)+strlen(endpointsBuf)>len) + return; + strcat(buffer, endpointsBuf); + for(std::vector::iterator stm=p->streams.begin();stm!=p->streams.end();++stm){ + char* codec=reinterpret_cast(&stm->codec); + snprintf(endpointsBuf, sizeof(endpointsBuf), "Stream %d (type %d, codec '%c%c%c%c', %sabled)\n", + stm->id, stm->type, codec[3], codec[2], codec[1], codec[0], stm->enabled ? "en" : "dis"); + if(strlen(buffer)+strlen(endpointsBuf)>len) + return; + strcat(buffer, endpointsBuf); + if(stm->enabled){ + if(stm->jitterBuffer){ + snprintf(endpointsBuf, sizeof(endpointsBuf), "Jitter buffer: %d/%.2f\n", + stm->jitterBuffer->GetMinPacketCount(), stm->jitterBuffer->GetAverageDelay()); + if(strlen(buffer)+strlen(endpointsBuf)>len) + return; + strcat(buffer, endpointsBuf); + } + } + } + strcat(buffer, "\n"); + } +} + +int32_t VoIPGroupController::GetCurrentUnixtime(){ + return time(NULL)+timeDifference; +} + void VoIPController::SetEchoCancellationStrength(int strength){ echoCancellationStrength=strength; if(echoCanceller) @@ -2489,13 +3654,15 @@ Endpoint::Endpoint(int64_t id, uint16_t port, IPv4Address& _address, IPv6Address this->port=port; this->type=type; memcpy(this->peerTag, peerTag, 16); - LOGV("new endpoint %lld: %s:%u", (long long int)id, address.ToString().c_str(), port); + if(type==TYPE_UDP_RELAY && ServerConfig::GetSharedInstance()->GetBoolean("force_tcp", false)) + this->type=TYPE_TCP_RELAY; lastPingSeq=0; lastPingTime=0; averageRTT=0; memset(rtts, 0, sizeof(rtts)); socket=NULL; + udpPongCount=0; } Endpoint::Endpoint() : address(0), v6address("::0") { @@ -2504,17 +3671,5 @@ Endpoint::Endpoint() : address(0), v6address("::0") { averageRTT=0; memset(rtts, 0, sizeof(rtts)); socket=NULL; + udpPongCount=0; } - -#if defined(__APPLE__) && TARGET_OS_IPHONE -void VoIPController::SetRemoteEndpoints(voip_legacy_endpoint_t* buffer, size_t count, bool allowP2P){ - std::vector endpoints; - for(size_t i=0;iSetRemoteEndpoints(endpoints, allowP2P); -} -#endif diff --git a/VoIPController.h b/VoIPController.h index 3a68739889..3701ffe1ea 100644 --- a/VoIPController.h +++ b/VoIPController.h @@ -13,6 +13,7 @@ #endif #ifdef __APPLE__ #include +#include "os/darwin/AudioUnitIO.h" #endif #include #include @@ -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 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 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 EnumerateAudioInputs(); + static std::vector EnumerateAudioInputs(); /** * * @return */ - static std::vector EnumerateAudioOutputs(); + static std::vector 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 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 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* 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 outgoingStreams; + std::vector 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 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 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 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* 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 outgoingStreams; - std::vector 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 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 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 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 streams; + AudioLevelMeter* levelMeter; + }; + std::vector 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 recentSentPackets; + Mutex sentPacketsMutex; + Mutex participantsMutex; + int32_t timeDifference; + }; + }; -} - #endif diff --git a/VoIPServerConfig.cpp b/VoIPServerConfig.cpp index dee561dc69..bbc2188c2d 100644 --- a/VoIPServerConfig.cpp +++ b/VoIPServerConfig.cpp @@ -13,11 +13,9 @@ using namespace tgvoip; ServerConfig* ServerConfig::sharedInstance=NULL; ServerConfig::ServerConfig(){ - init_mutex(mutex); } ServerConfig::~ServerConfig(){ - free_mutex(mutex); } ServerConfig *ServerConfig::GetSharedInstance(){ diff --git a/VoIPServerConfig.h b/VoIPServerConfig.h index dc20318d73..f1770a0526 100644 --- a/VoIPServerConfig.h +++ b/VoIPServerConfig.h @@ -30,7 +30,7 @@ private: static ServerConfig* sharedInstance; bool ContainsKey(std::string key); std::map config; - tgvoip_mutex_t mutex; + Mutex mutex; }; } diff --git a/audio/AudioInput.cpp b/audio/AudioInput.cpp index cc552abbb5..062ca067fc 100644 --- a/audio/AudioInput.cpp +++ b/audio/AudioInput.cpp @@ -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(platformSpecific)); #elif defined(_WIN32) #ifdef TGVOIP_WINXP_COMPAT if(LOBYTE(LOWORD(GetVersion()))<6) diff --git a/audio/AudioInput.h b/audio/AudioInput.h index 18420c94f8..5824e3d806 100644 --- a/audio/AudioInput.h +++ b/audio/AudioInput.h @@ -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& devs); static int32_t GetEstimatedDelay(); diff --git a/audio/AudioOutput.cpp b/audio/AudioOutput.cpp index a6364bc255..da964eca8a 100644 --- a/audio/AudioOutput.cpp +++ b/audio/AudioOutput.cpp @@ -6,9 +6,11 @@ #include "AudioOutput.h" #include "../logging.h" +#include #if defined(__ANDROID__) #include "../os/android/AudioOutputOpenSLES.h" #include "../os/android/AudioOutputAndroid.h" +#include #elif defined(__APPLE__) #include #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(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; diff --git a/audio/AudioOutput.h b/audio/AudioOutput.h index 02225b4f3e..1be31784c0 100644 --- a/audio/AudioOutput.h +++ b/audio/AudioOutput.h @@ -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& devs); bool IsInitialized(); -#if defined(__ANDROID__) - static int systemVersion; -#endif - protected: std::string currentDevice; bool failed; diff --git a/client/android/tg_voip_jni.cpp b/client/android/tg_voip_jni.cpp index 9097744ae5..67f73298c1 100644 --- a/client/android/tg_voip_jni.cpp +++ b/client/android/tg_voip_jni.cpp @@ -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 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(); +} \ No newline at end of file diff --git a/libtgvoip.xcodeproj/project.pbxproj b/libtgvoip.xcodeproj/project.pbxproj index 97a3fd3078..5f4bc88314 100644 --- a/libtgvoip.xcodeproj/project.pbxproj +++ b/libtgvoip.xcodeproj/project.pbxproj @@ -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 = ""; }; 69A6DE1A1E95ECF000000E69 /* wav_header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wav_header.h; sourceTree = ""; }; 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 = ""; }; /* 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 = ""; @@ -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; diff --git a/libtgvoip.xcodeproj/xcuserdata/grishka.xcuserdatad/xcschemes/xcschememanagement.plist b/libtgvoip.xcodeproj/xcuserdata/grishka.xcuserdatad/xcschemes/xcschememanagement.plist index 253fba52b6..9e9e68e11c 100644 --- a/libtgvoip.xcodeproj/xcuserdata/grishka.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/libtgvoip.xcodeproj/xcuserdata/grishka.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,7 +7,7 @@ libtgvoip.xcscheme orderHint - 0 + 1 SuppressBuildableAutocreation diff --git a/logging.cpp b/logging.cpp index bc90dcb33d..1033154d6a 100644 --- a/logging.cpp +++ b/logging.cpp @@ -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); } } diff --git a/logging.h b/logging.h index 3168bc491d..2ddae18167 100644 --- a/logging.h +++ b/logging.h @@ -13,8 +13,10 @@ #include #endif +#include + 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__) diff --git a/os/android/AudioInputAndroid.cpp b/os/android/AudioInputAndroid.cpp index b9499512eb..f84bf78271 100644 --- a/os/android/AudioInputAndroid.cpp +++ b/os/android/AudioInputAndroid.cpp @@ -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){ diff --git a/os/android/AudioInputAndroid.h b/os/android/AudioInputAndroid.h index 6a042d6fd7..2662aa1a08 100644 --- a/os/android/AudioInputAndroid.h +++ b/os/android/AudioInputAndroid.h @@ -30,7 +30,7 @@ public: private: jobject javaObject; bool running; - tgvoip_mutex_t mutex; + Mutex mutex; }; }} diff --git a/os/android/AudioInputOpenSLES.cpp b/os/android/AudioInputOpenSLES.cpp index bfba064726..70e7cbafe4 100644 --- a/os/android/AudioInputOpenSLES.cpp +++ b/os/android/AudioInputOpenSLES.cpp @@ -18,7 +18,7 @@ using namespace tgvoip; using namespace tgvoip::audio; -int AudioInputOpenSLES::nativeBufferSize; +unsigned int AudioInputOpenSLES::nativeBufferSize; AudioInputOpenSLES::AudioInputOpenSLES(){ slEngine=OpenSLEngineWrapper::CreateEngine(); diff --git a/os/android/AudioInputOpenSLES.h b/os/android/AudioInputOpenSLES.h index 655be250f4..64e196e2e0 100644 --- a/os/android/AudioInputOpenSLES.h +++ b/os/android/AudioInputOpenSLES.h @@ -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); diff --git a/os/android/AudioOutputOpenSLES.cpp b/os/android/AudioOutputOpenSLES.cpp index b290ffb96c..94ccf12717 100644 --- a/os/android/AudioOutputOpenSLES.cpp +++ b/os/android/AudioOutputOpenSLES.cpp @@ -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; } diff --git a/os/android/AudioOutputOpenSLES.h b/os/android/AudioOutputOpenSLES.h index 67da812376..cfca089095 100644 --- a/os/android/AudioOutputOpenSLES.h +++ b/os/android/AudioOutputOpenSLES.h @@ -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); diff --git a/os/darwin/AudioInputAudioUnit.cpp b/os/darwin/AudioInputAudioUnit.cpp index decd086056..638d763181 100644 --- a/os/darwin/AudioInputAudioUnit.cpp +++ b/os/darwin/AudioInputAudioUnit.cpp @@ -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){ diff --git a/os/darwin/AudioInputAudioUnit.h b/os/darwin/AudioInputAudioUnit.h index e66a80b24a..52dad73bf5 100644 --- a/os/darwin/AudioInputAudioUnit.h +++ b/os/darwin/AudioInputAudioUnit.h @@ -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(); diff --git a/os/darwin/AudioOutputAudioUnit.cpp b/os/darwin/AudioOutputAudioUnit.cpp index 791dfb22ae..48eb4175b4 100644 --- a/os/darwin/AudioOutputAudioUnit.cpp +++ b/os/darwin/AudioOutputAudioUnit.cpp @@ -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;imNumberBuffers;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(remainingDataSizeabsVal) - 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; - }*/ } } diff --git a/os/darwin/AudioOutputAudioUnit.h b/os/darwin/AudioOutputAudioUnit.h index 18561b6a15..27873ca932 100644 --- a/os/darwin/AudioOutputAudioUnit.h +++ b/os/darwin/AudioOutputAudioUnit.h @@ -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(); diff --git a/os/darwin/AudioUnitIO.cpp b/os/darwin/AudioUnitIO.cpp index 7b9f305c67..acf97ce102 100644 --- a/os/darwin/AudioUnitIO.cpp +++ b/os/darwin/AudioUnitIO.cpp @@ -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){ } diff --git a/os/darwin/AudioUnitIO.h b/os/darwin/AudioUnitIO.h index 42f08ff83e..823b21a861 100644 --- a/os/darwin/AudioUnitIO.h +++ b/os/darwin/AudioUnitIO.h @@ -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; }; }} diff --git a/os/darwin/SetupLogging.h b/os/darwin/SetupLogging.h new file mode 100644 index 0000000000..ad10754e41 --- /dev/null +++ b/os/darwin/SetupLogging.h @@ -0,0 +1,3 @@ +#import + +extern void (*TGVoipLoggingFunction)(NSString *); diff --git a/os/darwin/TGLogWrapper.h b/os/darwin/TGLogWrapper.h index 2da1bedc5e..8f111774ff 100644 --- a/os/darwin/TGLogWrapper.h +++ b/os/darwin/TGLogWrapper.h @@ -11,8 +11,6 @@ extern "C" { #endif -extern void (*TGVoipLoggingFunction)(NSString *); - void __tgvoip_call_tglog(const char* format, ...); #if defined __cplusplus diff --git a/os/darwin/TGLogWrapper.m b/os/darwin/TGLogWrapper.m index 3ce569e773..4739ec8e45 100644 --- a/os/darwin/TGLogWrapper.m +++ b/os/darwin/TGLogWrapper.m @@ -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); } } diff --git a/os/linux/AudioInputALSA.cpp b/os/linux/AudioInputALSA.cpp index 7612715959..38334eebc9 100644 --- a/os/linux/AudioInputALSA.cpp +++ b/os/linux/AudioInputALSA.cpp @@ -58,7 +58,9 @@ void AudioInputALSA::Start(){ return; isRecording=true; - start_thread(thread, AudioInputALSA::StartThread, this); + thread=new Thread(new MethodPointer(&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(); } } diff --git a/os/linux/AudioInputALSA.h b/os/linux/AudioInputALSA.h index 8bcd4353b5..0e76fc397c 100644 --- a/os/linux/AudioInputALSA.h +++ b/os/linux/AudioInputALSA.h @@ -26,8 +26,7 @@ public: static void EnumerateDevices(std::vector& 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; }; diff --git a/os/linux/AudioOutputALSA.cpp b/os/linux/AudioOutputALSA.cpp index af4ca4755e..0cf3398159 100644 --- a/os/linux/AudioOutputALSA.cpp +++ b/os/linux/AudioOutputALSA.cpp @@ -57,7 +57,9 @@ void AudioOutputALSA::Start(){ return; isPlaying=true; - start_thread(thread, AudioOutputALSA::StartThread, this); + thread=new Thread(new MethodPointer(&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(); } } diff --git a/os/linux/AudioOutputALSA.h b/os/linux/AudioOutputALSA.h index e967d1a2e6..b7d10424a4 100644 --- a/os/linux/AudioOutputALSA.h +++ b/os/linux/AudioOutputALSA.h @@ -26,8 +26,7 @@ public: static void EnumerateDevices(std::vector& 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; }; diff --git a/os/posix/NetworkSocketPosix.cpp b/os/posix/NetworkSocketPosix.cpp index a0ddd5f193..2bacfa44a3 100644 --- a/os/posix/NetworkSocketPosix.cpp +++ b/os/posix/NetworkSocketPosix.cpp @@ -18,6 +18,13 @@ #include "../../BufferInputStream.h" #include "../../BufferOutputStream.h" +#ifdef __ANDROID__ +#include +#include +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 &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 &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]; } diff --git a/os/windows/CXWrapper.cpp b/os/windows/CXWrapper.cpp index 570d92b64e..08fda34d9b 100755 --- a/os/windows/CXWrapper.cpp +++ b/os/windows/CXWrapper.cpp @@ -62,7 +62,7 @@ void VoIPControllerWrapper::SetPublicEndpoints(const Platform::Arrayid; - 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); diff --git a/threading.h b/threading.h index 8a6f438426..0b4933cb08 100644 --- a/threading.h +++ b/threading.h @@ -7,34 +7,108 @@ #ifndef __THREADING_H #define __THREADING_H +namespace tgvoip{ + class MethodPointerBase{ + public: + virtual ~MethodPointerBase(){ + + } + virtual void Invoke(void* arg)=0; + }; + + template 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 #include #include -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(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 @@ -113,39 +187,100 @@ private: #include #include -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(§ion); +#else + InitializeCriticalSectionEx(§ion, 0, 0); +#endif + } + + ~Mutex(){ + DeleteCriticalSection(§ion); + } + + void Lock(){ + EnterCriticalSection(§ion); + } + + void Unlock(){ + LeaveCriticalSection(§ion); + } + + 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(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; }; }