diff --git a/BlockingQueue.cpp b/BlockingQueue.cpp index f44ae2cf5c..70c8a8ece7 100644 --- a/BlockingQueue.cpp +++ b/BlockingQueue.cpp @@ -8,6 +8,7 @@ CBlockingQueue::CBlockingQueue(size_t capacity){ this->capacity=capacity; + overflowCallback=NULL; init_lock(lock); init_mutex(mutex); } @@ -29,7 +30,12 @@ void CBlockingQueue::Put(void *thing){ } queue.push_back(thing); while(queue.size()>capacity){ - queue.pop_front(); + if(overflowCallback){ + overflowCallback(queue.front()); + queue.pop_front(); + }else{ + abort(); + } } unlock_mutex(mutex); } @@ -72,3 +78,7 @@ void CBlockingQueue::PrepareDealloc(){ unlock_mutex(mutex); } + +void CBlockingQueue::SetOverflowCallback(void (*overflowCallback)(void *)){ + this->overflowCallback=overflowCallback; +} diff --git a/BlockingQueue.h b/BlockingQueue.h index 673a4fdd8b..3ed40fed83 100644 --- a/BlockingQueue.h +++ b/BlockingQueue.h @@ -22,6 +22,7 @@ public: void* Get(); unsigned int Size(); void PrepareDealloc(); + void SetOverflowCallback(void (*overflowCallback)(void*)); private: void* GetInternal(); @@ -29,6 +30,7 @@ private: size_t capacity; tgvoip_lock_t lock; tgvoip_mutex_t mutex; + void (*overflowCallback)(void*); }; diff --git a/BufferInputStream.cpp b/BufferInputStream.cpp index 055de77ef1..ce2ef5bb89 100644 --- a/BufferInputStream.cpp +++ b/BufferInputStream.cpp @@ -10,7 +10,7 @@ #include #include -CBufferInputStream::CBufferInputStream(char* data, size_t length){ +CBufferInputStream::CBufferInputStream(unsigned char* data, size_t length){ this->buffer=data; this->length=length; offset=0; @@ -22,7 +22,9 @@ CBufferInputStream::~CBufferInputStream(){ void CBufferInputStream::Seek(size_t offset){ - assert(offset<=length); + if(offset>length){ + throw std::out_of_range("Not enough bytes in buffer"); + } this->offset=offset; } @@ -88,7 +90,7 @@ int32_t CBufferInputStream::ReadTlLength(){ return res; } -void CBufferInputStream::ReadBytes(char *to, size_t count){ +void CBufferInputStream::ReadBytes(unsigned char *to, size_t count){ EnsureEnoughRemaining(count); memcpy(to, buffer+offset, count); offset+=count; diff --git a/BufferInputStream.h b/BufferInputStream.h index e7d21f068d..a728ba19db 100644 --- a/BufferInputStream.h +++ b/BufferInputStream.h @@ -13,7 +13,7 @@ class CBufferInputStream{ public: - CBufferInputStream(char* data, size_t length); + CBufferInputStream(unsigned char* data, size_t length); ~CBufferInputStream(); void Seek(size_t offset); size_t GetLength(); @@ -24,11 +24,11 @@ public: int32_t ReadInt32(); int16_t ReadInt16(); int32_t ReadTlLength(); - void ReadBytes(char* to, size_t count); + void ReadBytes(unsigned char* to, size_t count); private: void EnsureEnoughRemaining(size_t need); - char* buffer; + unsigned char* buffer; size_t length; size_t offset; }; diff --git a/BufferOutputStream.cpp b/BufferOutputStream.cpp index f7e5cf4287..035d03c705 100644 --- a/BufferOutputStream.cpp +++ b/BufferOutputStream.cpp @@ -8,7 +8,7 @@ #include CBufferOutputStream::CBufferOutputStream(size_t size){ - buffer=(char*) malloc(size); + buffer=(unsigned char*) malloc(size); offset=0; this->size=size; } @@ -24,40 +24,40 @@ void CBufferOutputStream::WriteByte(unsigned char byte){ void CBufferOutputStream::WriteInt32(int32_t i){ this->ExpandBufferIfNeeded(4); - buffer[offset+3]=(char)((i >> 24) & 0xFF); - buffer[offset+2]=(char)((i >> 16) & 0xFF); - buffer[offset+1]=(char)((i >> 8) & 0xFF); - buffer[offset]=(char)(i & 0xFF); + buffer[offset+3]=(unsigned char)((i >> 24) & 0xFF); + buffer[offset+2]=(unsigned char)((i >> 16) & 0xFF); + buffer[offset+1]=(unsigned char)((i >> 8) & 0xFF); + buffer[offset]=(unsigned char)(i & 0xFF); offset+=4; } void CBufferOutputStream::WriteInt64(int64_t i){ this->ExpandBufferIfNeeded(8); - buffer[offset+7]=(char)((i >> 56) & 0xFF); - buffer[offset+6]=(char)((i >> 48) & 0xFF); - buffer[offset+5]=(char)((i >> 40) & 0xFF); - buffer[offset+4]=(char)((i >> 32) & 0xFF); - buffer[offset+3]=(char)((i >> 24) & 0xFF); - buffer[offset+2]=(char)((i >> 16) & 0xFF); - buffer[offset+1]=(char)((i >> 8) & 0xFF); - buffer[offset]=(char)(i & 0xFF); + buffer[offset+7]=(unsigned char)((i >> 56) & 0xFF); + buffer[offset+6]=(unsigned char)((i >> 48) & 0xFF); + buffer[offset+5]=(unsigned char)((i >> 40) & 0xFF); + buffer[offset+4]=(unsigned char)((i >> 32) & 0xFF); + buffer[offset+3]=(unsigned char)((i >> 24) & 0xFF); + buffer[offset+2]=(unsigned char)((i >> 16) & 0xFF); + buffer[offset+1]=(unsigned char)((i >> 8) & 0xFF); + buffer[offset]=(unsigned char)(i & 0xFF); offset+=8; } void CBufferOutputStream::WriteInt16(int16_t i){ this->ExpandBufferIfNeeded(2); - buffer[offset+1]=(char)((i >> 8) & 0xFF); - buffer[offset]=(char)(i & 0xFF); + buffer[offset+1]=(unsigned char)((i >> 8) & 0xFF); + buffer[offset]=(unsigned char)(i & 0xFF); offset+=2; } -void CBufferOutputStream::WriteBytes(char *bytes, size_t count){ +void CBufferOutputStream::WriteBytes(unsigned char *bytes, size_t count){ this->ExpandBufferIfNeeded(count); memcpy(buffer+offset, bytes, count); offset+=count; } -char *CBufferOutputStream::GetBuffer(){ +unsigned char *CBufferOutputStream::GetBuffer(){ return buffer; } @@ -68,10 +68,10 @@ size_t CBufferOutputStream::GetLength(){ void CBufferOutputStream::ExpandBufferIfNeeded(size_t need){ if(offset+need>size){ if(need<1024){ - buffer=(char *) realloc(buffer, size+1024); + buffer=(unsigned char *) realloc(buffer, size+1024); size+=1024; }else{ - buffer=(char *) realloc(buffer, size+need); + buffer=(unsigned char *) realloc(buffer, size+need); size+=need; } } diff --git a/BufferOutputStream.h b/BufferOutputStream.h index 22e4a74a69..0420355646 100644 --- a/BufferOutputStream.h +++ b/BufferOutputStream.h @@ -18,14 +18,14 @@ public: void WriteInt64(int64_t i); void WriteInt32(int32_t i); void WriteInt16(int16_t i); - void WriteBytes(char* bytes, size_t count); - char* GetBuffer(); + void WriteBytes(unsigned char* bytes, size_t count); + unsigned char* GetBuffer(); size_t GetLength(); void Reset(); private: void ExpandBufferIfNeeded(size_t need); - char* buffer; + unsigned char* buffer; size_t size; size_t offset; }; diff --git a/CongestionControl.cpp b/CongestionControl.cpp index 1ad0faf6ac..71f4c58085 100644 --- a/CongestionControl.cpp +++ b/CongestionControl.cpp @@ -7,6 +7,7 @@ #include "CongestionControl.h" #include "VoIPController.h" #include "logging.h" +#include "VoIPServerConfig.h" #include #include @@ -26,7 +27,7 @@ CCongestionControl::CCongestionControl(){ stateTransitionTime=0; inflightDataSize=0; lossCount=0; - cwnd=1024; + cwnd=(size_t) CVoIPServerConfig::GetSharedInstance()->GetInt("audio_congestion_window", 1024); init_mutex(mutex); } diff --git a/CongestionControl.h b/CongestionControl.h index 840884e485..2838f19c8e 100644 --- a/CongestionControl.h +++ b/CongestionControl.h @@ -55,8 +55,8 @@ private: double stateTransitionTime; int tmpRttCount; char rttHistorySize; - char rttHistoryTop; - char inflightHistoryTop; + unsigned int rttHistoryTop; + unsigned int inflightHistoryTop; uint32_t lastSentSeq; uint32_t tickCount; size_t inflightDataSize; diff --git a/EchoCanceller.cpp b/EchoCanceller.cpp index 94360b6ea2..45d61f9fb0 100644 --- a/EchoCanceller.cpp +++ b/EchoCanceller.cpp @@ -8,6 +8,7 @@ #include "audio/AudioOutput.h" #include "logging.h" #include +#include #define AEC_FRAME_SIZE 160 #define OFFSET_STEP AEC_FRAME_SIZE*2 @@ -19,49 +20,73 @@ void WebRtcAec_enable_delay_agnostic(AecCore* self, int enable); }*/ -CEchoCanceller::CEchoCanceller(){ -#ifndef TGVOIP_NO_AEC - init_mutex(mutex); - state=WebRtcAecm_Create(); - WebRtcAecm_Init(state, 16000); - AecmConfig cfg; - cfg.cngMode=AecmFalse; - cfg.echoMode=1; - WebRtcAecm_set_config(state, cfg); +CEchoCanceller::CEchoCanceller(bool enableAEC, bool enableNS, bool enableAGC){ + this->enableAEC=enableAEC; + this->enableAGC=enableAGC; + this->enableNS=enableNS; - //ns=WebRtcNsx_Create(); - //WebRtcNsx_Init(ns, 16000); + splittingFilter=tgvoip_splitting_filter_create(); + splittingFilterFarend=tgvoip_splitting_filter_create(); + + if(enableAEC){ + init_mutex(aecMutex); + aec=WebRtcAecm_Create(); + WebRtcAecm_Init(aec, 16000); + AecmConfig cfg; + cfg.cngMode=AecmFalse; + cfg.echoMode=1; + WebRtcAecm_set_config(aec, cfg); + + farendQueue=new CBlockingQueue(11); + farendBufferPool=new CBufferPool(960*2, 10); + running=true; + + start_thread(bufferFarendThread, CEchoCanceller::StartBufferFarendThread, this); + } + + if(enableNS){ + ns=WebRtcNsx_Create(); + WebRtcNsx_Init(ns, 48000); + WebRtcNsx_set_policy(ns, 2); + } + + if(enableAGC){ + agc=WebRtcAgc_Create(); + WebRtcAgcConfig agcConfig; + agcConfig.compressionGaindB = 9; + agcConfig.limiterEnable = 1; + agcConfig.targetLevelDbfs = 3; + WebRtcAgc_Init(agc, 0, 255, kAgcModeAdaptiveAnalog, 48000); + WebRtcAgc_set_config(agc, agcConfig); + agcMicLevel=128; + } /*state=webrtc::WebRtcAec_Create(); webrtc::WebRtcAec_Init(state, 16000, 16000); webrtc::WebRtcAec_enable_delay_agnostic(webrtc::WebRtcAec_aec_core(state), 1);*/ - splittingFilter=tgvoip_splitting_filter_create(); - splittingFilterFarend=tgvoip_splitting_filter_create(); - - farendQueue=new CBlockingQueue(10); - farendBufferPool=new CBufferPool(960*2, 10); - running=true; - - start_thread(bufferFarendThread, CEchoCanceller::StartBufferFarendThread, this); - - isOn=true; -#endif } CEchoCanceller::~CEchoCanceller(){ -#ifndef TGVOIP_NO_AEC - running=false; - farendQueue->Put(NULL); - join_thread(bufferFarendThread); - delete farendQueue; - delete farendBufferPool; - WebRtcAecm_Free(state); - //WebRtcNsx_Free(ns); + if(enableAEC){ + running=false; + farendQueue->Put(NULL); + join_thread(bufferFarendThread); + delete farendQueue; + delete farendBufferPool; + WebRtcAecm_Free(aec); + } + if(enableNS){ + WebRtcNsx_Free(ns); + } + if(enableAGC){ + WebRtcAgc_Free(agc); + } //webrtc::WebRtcAec_Free(state); tgvoip_splitting_filter_free(splittingFilter); tgvoip_splitting_filter_free(splittingFilterFarend); - free_mutex(mutex); -#endif + if (this->enableAEC) { + free_mutex(aecMutex); + } } void CEchoCanceller::Start(){ @@ -74,8 +99,7 @@ void CEchoCanceller::Stop(){ void CEchoCanceller::SpeakerOutCallback(unsigned char* data, size_t len){ -#ifndef TGVOIP_NO_AEC - if(len!=960*2 || !isOn) + if(len!=960*2 || !enableAEC) return; /*size_t offset=0; while(offsetPut(buf); } -#endif } -#ifndef TGVOIP_NO_AEC void *CEchoCanceller::StartBufferFarendThread(void *arg){ ((CEchoCanceller*)arg)->RunBufferFarendThread(); return NULL; @@ -101,35 +123,27 @@ void CEchoCanceller::RunBufferFarendThread(){ int16_t* samplesIn=(int16_t *) farendQueue->GetBlocking(); if(samplesIn){ int i; - for(i=0;i<960;i++){ - splittingFilterFarend->bufferIn[i]=samplesIn[i]/(float)32767; - } + memcpy(splittingFilterFarend->bufferIn, samplesIn, 960*2); farendBufferPool->Reuse((unsigned char *) samplesIn); tgvoip_splitting_filter_analyze(splittingFilterFarend); + lock_mutex(aecMutex); //webrtc::WebRtcAec_BufferFarend(state, splittingFilterFarend->bufferOut[0], 160); //webrtc::WebRtcAec_BufferFarend(state, &splittingFilterFarend->bufferOut[0][160], 160); - int16_t farend[320]; - for(i=0;i<320;i++){ - farend[i]=(int16_t) (CLAMP(splittingFilterFarend->bufferOut[0][i], -1, 1)*32767); - } - lock_mutex(mutex); - WebRtcAecm_BufferFarend(state, farend, 160); - WebRtcAecm_BufferFarend(state, farend+160, 160); - unlock_mutex(mutex); + WebRtcAecm_BufferFarend(aec, splittingFilterFarend->bufferOut[0], 160); + WebRtcAecm_BufferFarend(aec, splittingFilterFarend->bufferOut[0]+160, 160); + unlock_mutex(aecMutex); didBufferFarend=true; } } } -#endif void CEchoCanceller::Enable(bool enabled){ - isOn=enabled; + //isOn=enabled; } void CEchoCanceller::ProcessInput(unsigned char* data, unsigned char* out, size_t len){ -#ifndef TGVOIP_NO_AEC int i; - if(!isOn){ + if(!enableAEC && !enableAGC && !enableNS){ memcpy(out, data, len); return; } @@ -137,43 +151,77 @@ void CEchoCanceller::ProcessInput(unsigned char* data, unsigned char* out, size_ int16_t* samplesOut=(int16_t*)out; //int16_t samplesAfterNs[320]; //float fout[3][320]; - for(i=0;i<960;i++){ - splittingFilter->bufferIn[i]=samplesIn[i]/(float)32767; - } + memcpy(splittingFilter->bufferIn, samplesIn, 960*2); tgvoip_splitting_filter_analyze(splittingFilter); - - for(i=0;i<320;i++){ - samplesIn[i]=(int16_t) (CLAMP(splittingFilter->bufferOut[0][i], -1, 1)*32767); + + if(enableAGC){ + int16_t _agcOut[3][320]; + int16_t* agcIn[3]; + int16_t* agcOut[3]; + for(i=0;i<3;i++){ + agcIn[i]=splittingFilter->bufferOut[i]; + agcOut[i]=_agcOut[i]; + } + uint8_t saturation; + WebRtcAgc_AddMic(agc, agcIn, 3, 160); + WebRtcAgc_Process(agc, (const int16_t *const *) agcIn, 3, 160, agcOut, agcMicLevel, &agcMicLevel, 0, &saturation); + for(i=0;i<3;i++){ + agcOut[i]+=160; + agcIn[i]+=160; + } + WebRtcAgc_AddMic(agc, agcIn, 3, 160); + WebRtcAgc_Process(agc, (const int16_t *const *) agcIn, 3, 160, agcOut, agcMicLevel, &agcMicLevel, 0, &saturation); + //LOGV("AGC mic level %d", agcMicLevel); + memcpy(splittingFilter->bufferOut[0], _agcOut[0], 960*2); } - lock_mutex(mutex); - /*float* aecIn[3]; - float* aecOut[3]; - aecIn[0]=splittingFilter->bufferOut[0]; - aecIn[1]=splittingFilter->bufferOut[1]; - aecIn[2]=splittingFilter->bufferOut[2]; - aecOut[0]=fout[0]; - aecOut[1]=fout[1]; - aecOut[2]=fout[2]; - webrtc::WebRtcAec_Process(state, (const float *const *) aecIn, 1, (float *const *) aecOut, 160, 0, 0); - aecIn[0]+=160; - aecIn[1]+=160; - aecIn[2]+=160; - aecOut[0]+=160; - aecOut[1]+=160; - aecOut[2]+=160; - webrtc::WebRtcAec_Process(state, (const float *const *) aecIn, 1, (float *const *) aecOut, 160, 0, 0);*/ - //int16_t* nsIn=samplesIn; - //int16_t* nsOut=samplesAfterNs; - //WebRtcNsx_Process(ns, (const short *const *) &nsIn, 1, (short *const *) &nsOut); - //nsIn+=160; - //nsOut+=160; - //WebRtcNsx_Process(ns, (const short *const *) &nsIn, 1, (short *const *) &nsOut); - WebRtcAecm_Process(state, samplesIn, NULL, samplesOut, AEC_FRAME_SIZE, (int16_t) CAudioOutput::GetEstimatedDelay()); - WebRtcAecm_Process(state, samplesIn+160, NULL, samplesOut+160, AEC_FRAME_SIZE, (int16_t) CAudioOutput::GetEstimatedDelay()); - unlock_mutex(mutex); - for(i=0;i<320;i++){ - splittingFilter->bufferOut[0][i]=samplesOut[i]/(float)32767; + + if(enableAEC && enableNS){ + int16_t _nsOut[3][320]; + int16_t* nsIn[3]; + int16_t* nsOut[3]; + for(i=0;i<3;i++){ + nsIn[i]=splittingFilter->bufferOut[i]; + nsOut[i]=_nsOut[i]; + } + WebRtcNsx_Process(ns, (const short *const *) nsIn, 3, nsOut); + for(i=0;i<3;i++){ + nsOut[i]+=160; + nsIn[i]+=160; + } + WebRtcNsx_Process(ns, (const short *const *) nsIn, 3, nsOut); + + memcpy(splittingFilter->bufferOut[1], _nsOut[1], 320*2*2); + + lock_mutex(aecMutex); + WebRtcAecm_Process(aec, splittingFilter->bufferOut[0], _nsOut[0], samplesOut, AEC_FRAME_SIZE, (int16_t) CAudioOutput::GetEstimatedDelay()); + WebRtcAecm_Process(aec, splittingFilter->bufferOut[0]+160, _nsOut[0]+160, samplesOut+160, AEC_FRAME_SIZE, (int16_t) CAudioOutput::GetEstimatedDelay()); + unlock_mutex(aecMutex); + memcpy(splittingFilter->bufferOut[0], samplesOut, 320*2); + }else if(enableAEC){ + lock_mutex(aecMutex); + WebRtcAecm_Process(aec, splittingFilter->bufferOut[0], NULL, samplesOut, AEC_FRAME_SIZE, (int16_t) CAudioOutput::GetEstimatedDelay()); + WebRtcAecm_Process(aec, splittingFilter->bufferOut[0]+160, NULL, samplesOut+160, AEC_FRAME_SIZE, (int16_t) CAudioOutput::GetEstimatedDelay()); + unlock_mutex(aecMutex); + memcpy(splittingFilter->bufferOut[0], samplesOut, 320*2); + }else if(enableNS){ + int16_t _nsOut[3][320]; + int16_t* nsIn[3]; + int16_t* nsOut[3]; + for(i=0;i<3;i++){ + nsIn[i]=splittingFilter->bufferOut[i]; + nsOut[i]=_nsOut[i]; + } + WebRtcNsx_Process(ns, (const short *const *) nsIn, 3, nsOut); + for(i=0;i<3;i++){ + nsOut[i]+=160; + nsIn[i]+=160; + } + WebRtcNsx_Process(ns, (const short *const *) nsIn, 3, nsOut); + + memcpy(splittingFilter->bufferOut[0], _nsOut[0], 960*2); + //memcpy(splittingFilter->bufferOut[1], _nsOut[1], 320*2); + //memcpy(splittingFilter->bufferOut[2], _nsOut[2], 320*2); } //memcpy(splittingFilter->bufferOut[0], fout[0], 320*sizeof(float)); @@ -182,11 +230,6 @@ void CEchoCanceller::ProcessInput(unsigned char* data, unsigned char* out, size_ tgvoip_splitting_filter_synthesize(splittingFilter); - for(i=0;i<960;i++){ - samplesOut[i]=(int16_t) (CLAMP(splittingFilter->bufferIn[i], -1, 1)*32767); - } -#else - memcpy(out, data, len); -#endif + memcpy(samplesOut, splittingFilter->bufferIn, 960*2); } diff --git a/EchoCanceller.h b/EchoCanceller.h index 486ca1d923..21599db329 100644 --- a/EchoCanceller.h +++ b/EchoCanceller.h @@ -11,9 +11,9 @@ #include #endif -#if TARGET_OS_IPHONE +/*#if TARGET_OS_IPHONE #define TGVOIP_NO_AEC -#endif +#endif*/ #include "threading.h" #include "BufferPool.h" @@ -23,12 +23,13 @@ //#include "external/include/webrtc/echo_cancellation.h" #include "external/include/webrtc/splitting_filter_wrapper.h" #include "external/include/webrtc/noise_suppression_x.h" +#include "external/include/webrtc/gain_control.h" #endif class CEchoCanceller{ public: - CEchoCanceller(); + CEchoCanceller(bool enableAEC, bool enableNS, bool enableAGC); virtual ~CEchoCanceller(); virtual void Start(); virtual void Stop(); @@ -37,20 +38,24 @@ public: void ProcessInput(unsigned char* data, unsigned char* out, size_t len); private: - bool isOn; + bool enableAEC; + bool enableAGC; + bool enableNS; #ifndef TGVOIP_NO_AEC static void* StartBufferFarendThread(void* arg); void RunBufferFarendThread(); bool didBufferFarend; - tgvoip_mutex_t mutex; - void* state; - splitting_filter_t* splittingFilter; - splitting_filter_t* splittingFilterFarend; + tgvoip_mutex_t aecMutex; + void* aec; + tgvoip_splitting_filter_t* splittingFilter; + tgvoip_splitting_filter_t* splittingFilterFarend; tgvoip_thread_t bufferFarendThread; CBlockingQueue* farendQueue; CBufferPool* farendBufferPool; bool running; NsxHandle* ns; + void* agc; + int32_t agcMicLevel; #endif }; diff --git a/Info.plist b/Info.plist new file mode 100644 index 0000000000..fbe1e6b314 --- /dev/null +++ b/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/JitterBuffer.cpp b/JitterBuffer.cpp index ab8910996f..0942045b33 100644 --- a/JitterBuffer.cpp +++ b/JitterBuffer.cpp @@ -4,8 +4,11 @@ // you should have received with this source code distribution. // +#include "VoIPController.h" #include "JitterBuffer.h" #include "logging.h" +#include "VoIPServerConfig.h" +#include CJitterBuffer::CJitterBuffer(CMediaStreamItf *out, uint32_t step):bufferPool(JITTER_SLOT_SIZE, JITTER_SLOT_COUNT){ if(out) @@ -19,12 +22,21 @@ CJitterBuffer::CJitterBuffer(CMediaStreamItf *out, uint32_t step):bufferPool(JIT dontIncMinDelay=0; dontDecMinDelay=0; lostPackets=0; - if(step<30) - minMinDelay=2; - else if(step<50) - minMinDelay=4; - else - minMinDelay=6; + if(step<30){ + minMinDelay=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("jitter_min_delay_20", 6); + maxMinDelay=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("jitter_max_delay_20", 25); + maxUsedSlots=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("jitter_max_slots_20", 50); + }else if(step<50){ + minMinDelay=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("jitter_min_delay_40", 4); + maxMinDelay=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("jitter_max_delay_40", 15); + maxUsedSlots=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("jitter_max_slots_40", 30); + }else{ + minMinDelay=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("jitter_min_delay_60", 1); + maxMinDelay=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("jitter_max_delay_60", 10); + maxUsedSlots=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("jitter_max_slots_60", 20); + } + lossesToReset=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("jitter_losses_to_reset", 20); + resyncThreshold=CVoIPServerConfig::GetSharedInstance()->GetDouble("jitter_resync_threshold", 1.0); Reset(); init_mutex(mutex); } @@ -76,9 +88,14 @@ void CJitterBuffer::Reset(){ slots[i].buffer=NULL; } } - memset(delayHistory, 0, sizeof(int)*64); - memset(lateHistory, 0, sizeof(int)*64); + memset(delayHistory, 0, sizeof(delayHistory)); + memset(lateHistory, 0, sizeof(lateHistory)); adjustingDelay=false; + lostSinceReset=0; + gotSinceReset=0; + expectNextAtTime=0; + memset(deviationHistory, 0, sizeof(deviationHistory)); + deviationPtr=0; } @@ -134,16 +151,18 @@ int CJitterBuffer::GetInternal(jitter_packet_t* pkt, int offset){ return JR_OK; } - LOGW("jitter: found no packet for timestamp %lld (last put = %d)", timestampToGet, lastPutTimestamp); + LOGW("jitter: found no packet for timestamp %lld (last put = %d, lost = %d)", timestampToGet, lastPutTimestamp, lostCount); if(offset==0) Advance(); if(!needBuffering){ lostCount++; - if(offset==0) + if(offset==0){ lostPackets++; - if(lostCount>=10){ + lostSinceReset++; + } + if(lostCount>=lossesToReset || (gotSinceReset>minDelay*25 && lostSinceReset>gotSinceReset/2)){ LOGW("jitter: lost %d packets in a row, resetting", lostCount); //minDelay++; dontIncMinDelay=16; @@ -164,21 +183,42 @@ void CJitterBuffer::PutInternal(jitter_packet_t* pkt){ LOGE("The packet is too big to fit into the jitter buffer"); return; } + gotSinceReset++; int i; if(wasReset){ wasReset=false; nextTimestamp=((int64_t)pkt->timestamp)-step*minDelay; LOGI("jitter: resyncing, next timestamp = %lld (step=%d, minDelay=%d)", nextTimestamp, step, minDelay); - for(i=0;itimestamp-slots[i].timestamptimestamp-closestTime){ + closestTime=slots[i].timestamp; + prevTime=slots[i].recvTime; + } + }*/ + double time=CVoIPController::GetCurrentTime(); + if(expectNextAtTime!=0){ + double dev=expectNextAtTime-time; + //LOGV("packet dev %f", dev); + deviationHistory[deviationPtr]=dev; + deviationPtr=(deviationPtr+1)%64; + expectNextAtTime+=step/1000.0; + }else{ + expectNextAtTime=time+step/1000.0; + } + if(pkt->timestamptimestamp); latePacketCount++; @@ -196,15 +236,16 @@ void CJitterBuffer::PutInternal(jitter_packet_t* pkt){ if(slots[i].buffer==NULL) break; } - if(i==JITTER_SLOT_COUNT){ + if(i==JITTER_SLOT_COUNT || GetCurrentDelay()>=maxUsedSlots){ int toRemove=JITTER_SLOT_COUNT; uint32_t bestTimestamp=0xFFFFFFFF; for(i=0;itimestamp; slots[i].size=pkt->size; slots[i].buffer=bufferPool.Get(); + slots[i].recvTimeDiff=time-prevRecvTime; if(slots[i].buffer) memcpy(slots[i].buffer, pkt->buffer, pkt->size); else LOGE("WTF!!"); + prevRecvTime=time; } @@ -238,14 +281,7 @@ void CJitterBuffer::Tick(){ lock_mutex(mutex); int i; - for(i=0;i=0.3){ + if(avgLate16>=resyncThreshold){ + wasReset=true; + } + /*if(avgLate16>=0.3){ if(dontIncMinDelay==0 && minDelay<15){ minDelay++; if(GetCurrentDelay()0) - dontIncMinDelay--; + dontIncMinDelay--;*/ + + if(absolutelyNoLatePackets){ + if(dontDecMinDelay>0) + dontDecMinDelay--; + } memmove(&delayHistory[1], delayHistory, 63*sizeof(int)); delayHistory[0]=GetCurrentDelay(); @@ -299,9 +343,49 @@ void CJitterBuffer::Tick(){ min=delayHistory[i]; } avgDelay/=32; + + double stddev=0; + double avgdev=0; + for(i=0;i<64;i++){ + avgdev+=deviationHistory[i]; + } + avgdev/=64; + for(i=0;i<64;i++){ + double d=(deviationHistory[i]-avgdev); + stddev+=(d*d); + } + stddev=sqrt(stddev/64); + uint32_t stddevDelay=(uint32_t)ceil(stddev*2*1000/step); + if(stddevDelaymaxMinDelay) + stddevDelay=maxMinDelay; + if(stddevDelay!=minDelay){ + int32_t diff=stddevDelay-minDelay; + if(diff>0){ + dontDecMinDelay=100; + } + if(diff<-1) + diff=-1; + if(diff>1) + diff=1; + if((diff>0 && dontIncMinDelay==0) || (diff<0 && dontDecMinDelay==0)){ + nextTimestamp+=diff*(int32_t)step; + minDelay+=diff; + LOGD("new delay from stddev %d", minDelay); + if(diff<0){ + dontDecMinDelay+=25; + } + if(diff>0){ + dontIncMinDelay=25; + } + } + } + //LOGV("stddev=%.3f, avg=%.3f, ndelay=%d, dontDec=%u", stddev, avgdev, stddevDelay, dontDecMinDelay); + //LOGV("jitter: avg delay=%d, delay=%d, late16=%.1f, dontDecMinDelay=%d", avgDelay, delayHistory[0], avgLate16, dontDecMinDelay); if(!adjustingDelay) { - if (avgDelay>=minDelay/2 && delayHistory[0]>minDelay && avgLate16<=0.1 && absolutelyNoLatePackets && dontDecMinDelay<32 && min>minDelay) { + if (((minDelay==1 ? (avgDelay>=3) : (avgDelay>=minDelay/2)) && delayHistory[0]>minDelay && avgLate16<=0.1 && absolutelyNoLatePackets && dontDecMinDelay<32 && min>minDelay) /*|| (avgRecvTimeDiff>0 && delayHistory[0]>minDelay/2 && avgRecvTimeDiff*1000 #include "logging.h" +#include "VoIPServerConfig.h" -COpusEncoder::COpusEncoder(CMediaStreamItf *source):queue(10), bufferPool(960*2, 10){ +COpusEncoder::COpusEncoder(CMediaStreamItf *source):queue(11), bufferPool(960*2, 10){ this->source=source; source->SetCallback(COpusEncoder::Callback, this); enc=opus_encoder_create(48000, 1, OPUS_APPLICATION_VOIP, NULL); @@ -16,13 +17,17 @@ COpusEncoder::COpusEncoder(CMediaStreamItf *source):queue(10), bufferPool(960*2, opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC(15)); opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC(1)); opus_encoder_ctl(enc, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); - //opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND)); + opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND)); requestedBitrate=32000; currentBitrate=0; running=false; echoCanceller=NULL; complexity=10; frameDuration=20; + mediumCorrectionBitrate=CVoIPServerConfig::GetSharedInstance()->GetInt("audio_medium_fec_bitrate", 10000); + strongCorrectionBitrate=CVoIPServerConfig::GetSharedInstance()->GetInt("audio_strong_fec_bitrate", 8000); + mediumCorrectionMultiplier=CVoIPServerConfig::GetSharedInstance()->GetDouble("audio_medium_fec_multiplier", 1.5); + strongCorrectionMultiplier=CVoIPServerConfig::GetSharedInstance()->GetDouble("audio_strong_fec_multiplier", 2.0); } COpusEncoder::~COpusEncoder(){ @@ -140,5 +145,16 @@ void COpusEncoder::SetOutputFrameDuration(uint32_t duration){ void COpusEncoder::SetPacketLoss(int percent){ - opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC(percent)); + packetLossPercent=percent; + double multiplier=1; + if(currentBitrate<=strongCorrectionBitrate) + multiplier=strongCorrectionMultiplier; + else if(currentBitrate<=mediumCorrectionBitrate) + multiplier=mediumCorrectionMultiplier; + opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC((int)(percent*multiplier))); + opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(percent>17 ? OPUS_AUTO : OPUS_BANDWIDTH_FULLBAND)); +} + +int COpusEncoder::GetPacketLoss(){ + return packetLossPercent; } diff --git a/OpusEncoder.h b/OpusEncoder.h index 4e082926f8..0e1fb17ce2 100644 --- a/OpusEncoder.h +++ b/OpusEncoder.h @@ -27,6 +27,7 @@ public: void SetEchoCanceller(CEchoCanceller* aec); void SetOutputFrameDuration(uint32_t duration); void SetPacketLoss(int percent); + int GetPacketLoss(); uint32_t GetBitrate(); private: @@ -46,6 +47,11 @@ private: int complexity; bool running; uint32_t frameDuration; + int packetLossPercent; + uint32_t mediumCorrectionBitrate; + uint32_t strongCorrectionBitrate; + double mediumCorrectionMultiplier; + double strongCorrectionMultiplier; }; diff --git a/VoIPController.cpp b/VoIPController.cpp index 600b8b45bc..56b115c009 100644 --- a/VoIPController.cpp +++ b/VoIPController.cpp @@ -9,6 +9,7 @@ #include "BufferInputStream.h" #include "OpusEncoder.h" #include "OpusDecoder.h" +#include "VoIPServerConfig.h" #include #include #include @@ -19,17 +20,20 @@ #include #include #include +#include #ifdef __APPLE__ +#include "os/darwin/AudioUnitIO.h" #include double CVoIPController::machTimebase=0; uint64_t CVoIPController::machTimestart=0; +#import #endif #define SHA1_LENGTH 20 #define SHA256_LENGTH 32 -#ifndef USE_CUSTOM_CRYPTO +#ifndef TGVOIP_USE_CUSTOM_CRYPTO #include #include #include @@ -70,6 +74,8 @@ voip_crypto_functions_t CVoIPController::crypto={ voip_crypto_functions_t CVoIPController::crypto; // set it yourself upon initialization #endif +extern FILE* tgvoipLogFile; + CVoIPController::CVoIPController(){ seq=1; lastRemoteSeq=0; @@ -87,7 +93,7 @@ CVoIPController::CVoIPController(){ for(i=0;i<20;i++){ emptySendBuffers.push_back(new CBufferOutputStream(1024)); } - sendQueue=new CBlockingQueue(20); + sendQueue=new CBlockingQueue(21); init_mutex(sendBufferMutex); memset(remoteAcks, 0, sizeof(double)*32); memset(sentPacketTimes, 0, sizeof(double)*32); @@ -124,10 +130,32 @@ CVoIPController::CVoIPController(){ peerVersion=0; conctl=new CCongestionControl(); prevSendLossCount=0; - enableAEC=true; - + receivedInit=false; + receivedInitAck=false; + + needUpdateNat64Prefix=true; + nat64Present=false; + + maxAudioBitrate=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("audio_max_bitrate", 20000); + maxAudioBitrateGPRS=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("audio_max_bitrate_gprs", 8000); + maxAudioBitrateEDGE=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("audio_max_bitrate_edge", 16000); + maxAudioBitrateSaving=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("audio_max_bitrate_saving", 8000); + initAudioBitrate=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("audio_init_bitrate", 16000); + initAudioBitrateGPRS=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("audio_init_bitrate_gprs", 8000); + initAudioBitrateEDGE=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("audio_init_bitrate_edge", 8000); + initAudioBitrateSaving=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("audio_init_bitrate_saving", 8000); + audioBitrateStepIncr=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("audio_bitrate_step_incr", 1000); + audioBitrateStepDecr=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("audio_bitrate_step_decr", 1000); + minAudioBitrate=(uint32_t) CVoIPServerConfig::GetSharedInstance()->GetInt("audio_min_bitrate", 8000); + relaySwitchThreshold=CVoIPServerConfig::GetSharedInstance()->GetDouble("relay_switch_threshold", 0.8); + p2pToRelaySwitchThreshold=CVoIPServerConfig::GetSharedInstance()->GetDouble("p2p_to_relay_switch_threshold", 0.6); + relayToP2pSwitchThreshold=CVoIPServerConfig::GetSharedInstance()->GetDouble("relay_to_p2p_switch_threshold", 0.8); + #ifdef __APPLE__ machTimestart=0; +#ifdef TGVOIP_USE_AUDIO_SESSION + needNotifyAcquiredAudioSession=false; +#endif #endif voip_stream_t* stm=(voip_stream_t *) malloc(sizeof(voip_stream_t)); @@ -200,7 +228,7 @@ CVoIPController::~CVoIPController(){ delete echoCanceller; } delete sendQueue; - int i; + unsigned int i; for(i=0;iGetInt("audio_frame_size", 60); + if(cfgFrameSize==20 || cfgFrameSize==40 || cfgFrameSize==60) + outgoingStreams[0]->frameDuration=(uint16_t) cfgFrameSize; + udpSocket=socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); if(udpSocket<0){ LOGE("error creating socket: %d / %s", errno, strerror(errno)); } + int flag=0; + res=setsockopt(udpSocket, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)); + if(res<0){ + LOGE("error enabling dual stack socket: %d / %s", errno, strerror(errno)); + } #ifdef __APPLE__ int prio=NET_SERVICE_TYPE_VO; res=setsockopt(udpSocket, SOL_SOCKET, SO_NET_SERVICE_TYPE, &prio, sizeof(prio)); @@ -277,25 +318,41 @@ void CVoIPController::Start(){ if(res<0){ LOGE("error setting priority: %d / %s", errno, strerror(errno)); } - prio<<=5; + prio=6 << 5; res=setsockopt(udpSocket, SOL_IP, IP_TOS, &prio, sizeof(prio)); if(res<0){ LOGE("error setting ip tos: %d / %s", errno, strerror(errno)); } #endif - sockaddr_in addr; - addr.sin_addr.s_addr=0; - //addr.sin_port=htons(3497); - addr.sin_port=0; - addr.sin_family=AF_INET; - res=bind(udpSocket, (sockaddr*)&addr, sizeof(sockaddr_in)); - if(res<0){ - LOGE("error binding: %d / %s", errno, strerror(errno)); + int tries=0; + sockaddr_in6 addr; + //addr.sin6_addr.s_addr=0; + memset(&addr, 0, sizeof(sockaddr_in6)); + //addr.sin6_len=sizeof(sa_family_t); + addr.sin6_family=AF_INET6; + for(tries=0;tries<10;tries++){ + addr.sin6_port=htons(GenerateLocalUDPPort()); + res=::bind(udpSocket, (sockaddr *) &addr, sizeof(sockaddr_in6)); + LOGV("trying bind to port %u", ntohs(addr.sin6_port)); + if(res<0){ + LOGE("error binding to port %u: %d / %s", ntohs(addr.sin6_port), errno, strerror(errno)); + }else{ + break; + } } - size_t addrLen=sizeof(sockaddr_in); + if(tries==10){ + addr.sin6_port=0; + res=::bind(udpSocket, (sockaddr *) &addr, sizeof(sockaddr_in6)); + if(res<0){ + LOGE("error binding to port %u: %d / %s", ntohs(addr.sin6_port), errno, strerror(errno)); + SetState(STATE_FAILED); + return; + } + } + size_t addrLen=sizeof(sockaddr_in6); getsockname(udpSocket, (sockaddr*)&addr, (socklen_t*) &addrLen); - localUdpPort=ntohs(addr.sin_port); - LOGD("Bound to local UDP port %u", addr.sin_port); + localUdpPort=ntohs(addr.sin6_port); + LOGD("Bound to local UDP port %u", ntohs(addr.sin6_port)); SendPacket(NULL, 0, currentEndpoint); @@ -342,11 +399,11 @@ void CVoIPController::HandleAudioInput(unsigned char *data, size_t len){ else pkt->WriteByte((unsigned char)len); pkt->WriteInt32(audioTimestampOut); - pkt->WriteBytes((char *) data, len); + pkt->WriteBytes(data, len); audioPacketsWritten++; if(audioPacketsWritten>=audioPacketGrouping){ uint32_t pl=pkt->GetLength(); - char tmp[pl]; + unsigned char tmp[pl]; memcpy(tmp, pkt->GetBuffer(), pl); pkt->Reset(); unsigned char type; @@ -381,7 +438,7 @@ void CVoIPController::Connect(){ } -void CVoIPController::SetEncryptionKey(char *key){ +void CVoIPController::SetEncryptionKey(char *key, bool isOutgoing){ memcpy(encryptionKey, key, 256); uint8_t sha1[SHA1_LENGTH]; crypto.sha1((uint8_t*) encryptionKey, 256, sha1); @@ -389,6 +446,7 @@ void CVoIPController::SetEncryptionKey(char *key){ uint8_t sha256[SHA256_LENGTH]; crypto.sha256((uint8_t*) encryptionKey, 256, sha256); memcpy(callID, sha256+(SHA256_LENGTH-16), 16); + this->isOutgoing=isOutgoing; } uint32_t CVoIPController::WritePacketHeader(CBufferOutputStream *s, unsigned char type, uint32_t length){ @@ -411,7 +469,7 @@ uint32_t CVoIPController::WritePacketHeader(CBufferOutputStream *s, unsigned cha unsigned char randBytes[7]; crypto.rand_bytes(randBytes, 7); s->WriteByte(7); - s->WriteBytes((char *) randBytes, 7); + s->WriteBytes(randBytes, 7); uint32_t pflags=PFLAG_HAS_RECENT_RECV | PFLAG_HAS_SEQ; if(length>0) pflags|=PFLAG_HAS_DATA; @@ -435,9 +493,9 @@ uint32_t CVoIPController::WritePacketHeader(CBufferOutputStream *s, unsigned cha s->WriteByte((unsigned char) length); }else{ s->WriteByte(254); - s->WriteByte((unsigned char) (length & 8)); - s->WriteByte((unsigned char) ((length >> 8) & 8)); - s->WriteByte((unsigned char) ((length >> 16) & 8)); + s->WriteByte((unsigned char) (length & 0xFF)); + s->WriteByte((unsigned char) ((length >> 8) & 0xFF)); + s->WriteByte((unsigned char) ((length >> 16) & 0xFF)); } } }else{ @@ -448,16 +506,16 @@ uint32_t CVoIPController::WritePacketHeader(CBufferOutputStream *s, unsigned cha unsigned char randBytes[7]; crypto.rand_bytes(randBytes, 7); s->WriteByte(7); - s->WriteBytes((char *) randBytes, 7); + s->WriteBytes(randBytes, 7); uint32_t lenWithHeader=length+13; if(lenWithHeader>0){ if(lenWithHeader<=253){ s->WriteByte((unsigned char) lenWithHeader); }else{ s->WriteByte(254); - s->WriteByte((unsigned char) (lenWithHeader & 8)); - s->WriteByte((unsigned char) ((lenWithHeader >> 8) & 8)); - s->WriteByte((unsigned char) ((lenWithHeader >> 16) & 8)); + s->WriteByte((unsigned char) (lenWithHeader & 0xFF)); + s->WriteByte((unsigned char) ((lenWithHeader >> 8) & 0xFF)); + s->WriteByte((unsigned char) ((lenWithHeader >> 16) & 0xFF)); } } s->WriteByte(type); @@ -480,14 +538,18 @@ uint32_t CVoIPController::WritePacketHeader(CBufferOutputStream *s, unsigned cha void CVoIPController::UpdateAudioBitrate(){ if(encoder){ - if(networkType==NET_TYPE_GPRS || dataSavingMode || dataSavingRequestedByPeer) - encoder->SetBitrate(maxBitrate=8000); - else if(networkType==NET_TYPE_EDGE){ - maxBitrate=16000; - encoder->SetBitrate(8000); + if(dataSavingMode || dataSavingRequestedByPeer){ + maxBitrate=maxAudioBitrateSaving; + encoder->SetBitrate(initAudioBitrateSaving); + }else if(networkType==NET_TYPE_GPRS){ + maxBitrate=maxAudioBitrateGPRS; + encoder->SetBitrate(initAudioBitrateGPRS); + }else if(networkType==NET_TYPE_EDGE){ + maxBitrate=maxAudioBitrateEDGE; + encoder->SetBitrate(initAudioBitrateEDGE); }else{ - maxBitrate=25000; - encoder->SetBitrate(16000); + maxBitrate=maxAudioBitrate; + encoder->SetBitrate(initAudioBitrate); } } } @@ -505,7 +567,9 @@ void CVoIPController::SendInit(){ out->WriteByte(1); // audio codecs count out->WriteByte(CODEC_OPUS); out->WriteByte(0); // video codecs count - SendPacket(out->GetBuffer(), out->GetLength(), currentEndpoint); + for(std::vector::const_iterator itr=endpoints.begin();itr!=endpoints.end();++itr){ + SendPacket(out->GetBuffer(), out->GetLength(), *itr); + } SetState(STATE_WAIT_INIT_ACK); delete out; } @@ -516,24 +580,28 @@ void CVoIPController::SendInitAck(){ void CVoIPController::RunRecvThread(){ LOGI("Receive thread starting"); - char buffer[1024]; - sockaddr_in srcAddr; + unsigned char buffer[1024]; + sockaddr_in6 srcAddr; int addrLen; while(runReceiver){ //LOGI("Before recv"); - addrLen=sizeof(sockaddr_in); + addrLen=sizeof(sockaddr_in6); ssize_t len=recvfrom(udpSocket, buffer, 1024, 0, (sockaddr *) &srcAddr, (socklen_t *) &addrLen); //LOGV("Received %d bytes from %s:%d at %.5lf", len, inet_ntoa(srcAddr.sin_addr), ntohs(srcAddr.sin_port), GetCurrentTime()); voip_endpoint_t* srcEndpoint=NULL; - int _i; - for(_i=0;_iaddress.s_addr==srcAddr.sin_addr.s_addr && endpoints[_i]->port==ntohs(srcAddr.sin_port)){ - srcEndpoint=endpoints[_i]; - break; + if(IN6_IS_ADDR_V4MAPPED(&srcAddr.sin6_addr) || (nat64Present && memcmp(nat64Prefix, srcAddr.sin6_addr.s6_addr, 12)==0)){ + in_addr v4addr=*((in_addr*)&srcAddr.sin6_addr.s6_addr[12]); + int _i; + for(_i=0;_iaddress.s_addr==v4addr.s_addr && endpoints[_i]->port==ntohs(srcAddr.sin6_port)){ + srcEndpoint=endpoints[_i]; + break; + } } } if(!srcEndpoint){ - LOGW("Received a packet from unknown source %s:%u", inet_ntoa(srcAddr.sin_addr), ntohs(srcAddr.sin_port)); + char abuf[INET6_ADDRSTRLEN]; + LOGW("Received a packet from unknown source %s:%u", inet_ntop(AF_INET6, &srcAddr.sin6_addr, abuf, INET6_ADDRSTRLEN), ntohs(srcAddr.sin6_port)); continue; } if(len<=0){ @@ -545,6 +613,7 @@ void CVoIPController::RunRecvThread(){ else stats.bytesRecvdWifi+=(uint64_t)len; CBufferInputStream* in=new CBufferInputStream(buffer, (size_t)len); + try{ if(memcmp(buffer, srcEndpoint->type==EP_TYPE_UDP_RELAY ? srcEndpoint->peerTag : callID, 16)!=0){ LOGW("Received packet has wrong peerTag"); delete in; @@ -622,20 +691,22 @@ void CVoIPController::RunRecvThread(){ } unsigned char fingerprint[8], msgHash[16]; - in->ReadBytes((char *) fingerprint, 8); - in->ReadBytes((char *) msgHash, 16); + in->ReadBytes(fingerprint, 8); + in->ReadBytes(msgHash, 16); if(memcmp(fingerprint, keyFingerprint, 8)!=0){ LOGW("Received packet has wrong key fingerprint"); delete in; continue; } unsigned char key[32], iv[32]; - KDF(msgHash, key, iv); + KDF(msgHash, isOutgoing ? 8 : 0, key, iv); unsigned char aesOut[in->Remaining()]; 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]; - int32_t _len=in->ReadInt32(); + 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"); @@ -646,7 +717,6 @@ void CVoIPController::RunRecvThread(){ lastRecvPacketTime=GetCurrentTime(); - try{ /*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; @@ -658,7 +728,7 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA if(tlid==TLID_DECRYPTED_AUDIO_BLOCK){ in->ReadInt64(); // random id uint32_t randLen=(uint32_t) in->ReadTlLength(); - in->Seek(in->GetOffset()+randLen+(randLen+(randLen<=253 ? 1 : 0))%4); + 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)){ @@ -668,7 +738,14 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA } if(flags & PFLAG_HAS_CALL_ID){ unsigned char pktCallID[16]; - in->ReadBytes((char *) pktCallID, 16); + in->ReadBytes(pktCallID, 16); + if(memcmp(pktCallID, callID, 16)!=0){ + LOGW("Received packet has wrong call id"); + delete in; + lastError=ERROR_UNKNOWN; + SetState(STATE_FAILED); + return; + } } ackId=(uint32_t) in->ReadInt32(); pseq=(uint32_t) in->ReadInt32(); @@ -685,7 +762,7 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA } if(flags & PFLAG_HAS_EXTRA){ uint32_t extraLen=(uint32_t) in->ReadTlLength(); - in->Seek(in->GetOffset()+extraLen+(extraLen+(extraLen<=253 ? 1 : 0))%4); + in->Seek(in->GetOffset()+extraLen+pad4(extraLen)); } if(flags & PFLAG_HAS_DATA){ packetInnerLen=in->ReadTlLength(); @@ -693,7 +770,7 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA }else if(tlid==TLID_SIMPLE_AUDIO_BLOCK){ in->ReadInt64(); // random id uint32_t randLen=(uint32_t) in->ReadTlLength(); - in->Seek(in->GetOffset()+randLen+(randLen+(randLen<=253 ? 1 : 0))%4); + in->Seek(in->GetOffset()+randLen+pad4(randLen)); packetInnerLen=in->ReadTlLength(); type=in->ReadByte(); ackId=(uint32_t) in->ReadInt32(); @@ -767,7 +844,7 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA if(qp->seqs[j]==0) break; int remoteAcksIndex=lastRemoteAckSeq-qp->seqs[j]; - LOGV("remote acks index %u, value %llf", remoteAcksIndex, remoteAcksIndex>=0 && remoteAcksIndex<32 ? remoteAcks[remoteAcksIndex] : -1); + 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; @@ -800,7 +877,13 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA //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) + preferredRelay=srcEndpoint; + LogDebugInfo(); + } peerVersion=(uint32_t) in->ReadInt32(); LOGI("Peer version is %d", peerVersion); uint32_t minVer=(uint32_t) in->ReadInt32(); @@ -816,26 +899,26 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA UpdateDataSavingState(); UpdateAudioBitrate(); } - + int i; int numSupportedAudioCodecs=in->ReadByte(); - for(i=0;iReadByte(); // ignore for now } int numSupportedVideoCodecs=in->ReadByte(); - for(i=0;iReadByte(); // ignore for now } - - CBufferOutputStream* out=new CBufferOutputStream(1024); + CBufferOutputStream *out=new CBufferOutputStream(1024); WritePacketHeader(out, PKT_INIT_ACK, (peerVersion>=2 ? 10 : 2)+(peerVersion>=2 ? 6 : 4)*outgoingStreams.size()); if(peerVersion>=2){ out->WriteInt32(PROTOCOL_VERSION); out->WriteInt32(MIN_PROTOCOL_VERSION); } + out->WriteByte((unsigned char) outgoingStreams.size()); - for(i=0;iWriteByte(outgoingStreams[i]->id); out->WriteByte(outgoingStreams[i]->type); out->WriteByte(outgoingStreams[i]->codec); @@ -843,7 +926,7 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA out->WriteInt16(outgoingStreams[i]->frameDuration); else outgoingStreams[i]->frameDuration=20; - out->WriteByte((unsigned char)(outgoingStreams[i]->enabled ? 1 : 0)); + out->WriteByte((unsigned char) (outgoingStreams[i]->enabled ? 1 : 0)); } SendPacket(out->GetBuffer(), out->GetLength(), currentEndpoint); delete out; @@ -851,80 +934,101 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA if(type==PKT_INIT_ACK){ LOGD("Received init ack"); - if(packetInnerLen>10){ - peerVersion=in->ReadInt32(); - uint32_t minVer=(uint32_t) in->ReadInt32(); - if(minVer>PROTOCOL_VERSION || peerVersion10){ + peerVersion=in->ReadInt32(); + uint32_t minVer=(uint32_t) in->ReadInt32(); + if(minVer>PROTOCOL_VERSION || peerVersionReadByte(); + if(streamCount==0) + goto malformed_packet; + + int i; + voip_stream_t *incomingAudioStream=NULL; + for(i=0; iid=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) + goto malformed_packet; + + voip_stream_t *outgoingAudioStream=outgoingStreams[0]; + + if(!audioInput){ + LOGI("before create audio io"); + audioInput=CAudioInput::Create(); + audioInput->Configure(48000, 16, 1); + audioOutput=CAudioOutput::Create(); + audioOutput->Configure(48000, 16, 1); + echoCanceller=new CEchoCanceller(config.enableAEC, config.enableNS, config.enableAGC); + encoder=new COpusEncoder(audioInput); + encoder->SetCallback(AudioInputCallback, this); + encoder->SetOutputFrameDuration(outgoingAudioStream->frameDuration); + encoder->SetEchoCanceller(echoCanceller); + encoder->Start(); + if(!micMuted){ + audioInput->Start(); + if(!audioInput->IsInitialized()){ + lastError=ERROR_AUDIO_IO; + delete in; + SetState(STATE_FAILED); + return; + } + } + UpdateAudioBitrate(); + + jitterBuffer=new CJitterBuffer(NULL, incomingAudioStream->frameDuration); + decoder=new COpusDecoder(audioOutput); + decoder->SetEchoCanceller(echoCanceller); + decoder->SetJitterBuffer(jitterBuffer); + decoder->SetFrameDuration(incomingAudioStream->frameDuration); + decoder->Start(); + if(incomingAudioStream->frameDuration>50) + jitterBuffer->SetMinPacketCount(CVoIPServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_60", 3)); + else if(incomingAudioStream->frameDuration>30) + jitterBuffer->SetMinPacketCount(CVoIPServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_40", 4)); + else + jitterBuffer->SetMinPacketCount(CVoIPServerConfig::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{ + CAudioUnitIO::AudioSessionAcquired(); + } +#endif +#endif + } + SetState(STATE_ESTABLISHED); + if(allowP2p) + SendPublicEndpointsRequest(); } - - LOGI("peer version from init ack %d", peerVersion); - - unsigned char streamCount=in->ReadByte(); - if(streamCount==0) - goto malformed_packet; - - int i; - voip_stream_t* incomingAudioStream=NULL; - for(i=0;iid=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) - goto malformed_packet; - - voip_stream_t* outgoingAudioStream=outgoingStreams[0]; - - if(!audioInput){ - LOGI("before create audio io"); - audioInput=CAudioInput::Create(); - audioInput->Configure(48000, 16, 1); - audioOutput=CAudioOutput::Create(); - audioOutput->Configure(48000, 16, 1); - if(enableAEC) - echoCanceller=new CEchoCanceller(); - else - echoCanceller=NULL; - encoder=new COpusEncoder(audioInput); - encoder->SetCallback(AudioInputCallback, this); - encoder->SetOutputFrameDuration(outgoingAudioStream->frameDuration); - encoder->SetEchoCanceller(echoCanceller); - encoder->Start(); - if(!micMuted) - audioInput->Start(); - UpdateAudioBitrate(); - - jitterBuffer=new CJitterBuffer(NULL, incomingAudioStream->frameDuration); - decoder=new COpusDecoder(audioOutput); - decoder->SetEchoCanceller(echoCanceller); - decoder->SetJitterBuffer(jitterBuffer); - decoder->SetFrameDuration(incomingAudioStream->frameDuration); - decoder->Start(); - if(incomingAudioStream->frameDuration>50) - jitterBuffer->SetMinPacketCount(3); - else if(incomingAudioStream->frameDuration>30) - jitterBuffer->SetMinPacketCount(4); - //audioOutput->Start(); - } - SetState(STATE_ESTABLISHED); - if(allowP2p) - SendPublicEndpointsRequest(); } if(type==PKT_STREAM_DATA || type==PKT_STREAM_DATA_X2 || type==PKT_STREAM_DATA_X3){ int count; @@ -969,6 +1073,7 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA CBufferOutputStream *pkt=GetOutgoingPacketBuffer(); if(!pkt){ LOGW("Dropping pong packet, queue overflow"); + delete in; continue; } WritePacketHeader(pkt, PKT_PONG, 4); @@ -1122,8 +1227,11 @@ void CVoIPController::RunTickThread(){ if(waitingForAcks) wasWaitingForAcks=false; //LOGI("%.3lf/%.3lf, rtt diff %.3lf, waiting=%d, queue=%d", rttHistory[0], rttHistory[8], v, waitingForAcks, sendQueue->Size()); - if(jitterBuffer) - recvLossCount+=jitterBuffer->GetAndResetLostPacketCount(); + if(jitterBuffer){ + int lostCount=jitterBuffer->GetAndResetLostPacketCount(); + if(lostCount>0 || (lostCount<0 && recvLossCount>((uint32_t)-lostCount))) + recvLossCount+=lostCount; + } } if(dontSendPackets>0) dontSendPackets--; @@ -1137,11 +1245,11 @@ void CVoIPController::RunTickThread(){ if(act==TGVOIP_CONCTL_ACT_DECREASE){ uint32_t bitrate=encoder->GetBitrate(); if(bitrate>8000) - encoder->SetBitrate(bitrate<9000 ? 8000 : (bitrate-1000)); + encoder->SetBitrate(bitrate<(minAudioBitrate+audioBitrateStepDecr) ? minAudioBitrate : (bitrate-audioBitrateStepDecr)); }else if(act==TGVOIP_CONCTL_ACT_INCREASE){ uint32_t bitrate=encoder->GetBitrate(); if(bitrateSetBitrate(bitrate+1000); + encoder->SetBitrate(bitrate+audioBitrateStepIncr); } if(tickCount%10==0 && encoder){ @@ -1262,7 +1370,7 @@ void CVoIPController::RunTickThread(){ SendPacket(pkt.GetBuffer(), pkt.GetLength(), e); } if(e->type==EP_TYPE_UDP_RELAY){ - if(e->_averageRtt>0 && e->_averageRtt_averageRtt>0 && e->_averageRtt_averageRtt; minPingRelay=e; } @@ -1272,6 +1380,7 @@ void CVoIPController::RunTickThread(){ preferredRelay=minPingRelay; if(currentEndpoint->type==EP_TYPE_UDP_RELAY) currentEndpoint=preferredRelay; + LogDebugInfo(); /*CBufferOutputStream pkt(32); pkt.WriteInt64(preferredRelay->id); SendPacketReliably(PKT_SWITCH_PREF_RELAY, pkt.GetBuffer(), pkt.GetLength(), 1, 9);*/ @@ -1280,22 +1389,25 @@ void CVoIPController::RunTickThread(){ voip_endpoint_t *p2p=GetEndpointByType(EP_TYPE_UDP_P2P_INET); if(p2p){ voip_endpoint_t *lan=GetEndpointByType(EP_TYPE_UDP_P2P_LAN); - if(lan && lan->_averageRtt>0){ - SendPacketReliably(PKT_SWITCH_TO_P2P, NULL, 0, 1, 5); + if(lan && lan->_averageRtt>0 && lan->_averageRtt_averageRtt>0 && p2p->_averageRtt_averageRtt>0 && p2p->_averageRtt0 && minPing_averageRtt*.6){ + if(minPing>0 && minPing_averageRtt*p2pToRelaySwitchThreshold){ LOGI("Switching to relay"); currentEndpoint=preferredRelay; + LogDebugInfo(); } } } @@ -1304,7 +1416,13 @@ void CVoIPController::RunTickThread(){ if(GetCurrentTime()-lastRecvPacketTime>=config.recv_timeout){ if(currentEndpoint && currentEndpoint->type!=EP_TYPE_UDP_RELAY){ LOGW("Packet receive timeout, switching to relay"); - currentEndpoint=GetEndpointByType(EP_TYPE_UDP_RELAY); + currentEndpoint=preferredRelay; + for(i=0;itype==EP_TYPE_UDP_P2P_INET || endpoints[i]->type==EP_TYPE_UDP_P2P_LAN){ + endpoints[i]->_averageRtt=0; + memset(endpoints[i]->_rtts, 0, sizeof(voip_endpoint_t::_rtts)); + } + } if(allowP2p){ SendPublicEndpointsRequest(); } @@ -1327,6 +1445,13 @@ void CVoIPController::RunTickThread(){ SetState(STATE_FAILED); } } + +#if defined(__APPLE__) && defined(TGVOIP_USE_AUDIO_SESSION) + if(needNotifyAcquiredAudioSession){ + needNotifyAcquiredAudioSession=false; + CAudioUnitIO::AudioSessionAcquired(); + } +#endif } LOGI("=== tick thread exiting ==="); } @@ -1338,13 +1463,13 @@ voip_endpoint_t *CVoIPController::GetRemoteEndpoint(){ } -void CVoIPController::SendPacket(char *data, size_t len, voip_endpoint_t* ep){ +void CVoIPController::SendPacket(unsigned char *data, size_t len, voip_endpoint_t* ep){ if(stopping) return; - sockaddr_in dst; - dst.sin_addr=ep->address; - dst.sin_port=htons(ep->port); - dst.sin_family=AF_INET; + sockaddr_in6 dst(MakeInetAddress(ep->address, ep->port)); + //dst.sin_addr=ep->address; + //dst.sin_port=htons(ep->port); + //dst.sin_family=AF_INET; CBufferOutputStream out(len+128); if(ep->type==EP_TYPE_UDP_RELAY) out.WriteBytes(ep->peerTag, 16); @@ -1356,7 +1481,7 @@ void CVoIPController::SendPacket(char *data, size_t len, voip_endpoint_t* ep){ inner.WriteBytes(data, len); if(inner.GetLength()%16!=0){ size_t padLen=16-inner.GetLength()%16; - char padding[padLen]; + unsigned char padding[padLen]; crypto.rand_bytes((uint8_t *) padding, padLen); inner.WriteBytes(padding, padLen); } @@ -1364,18 +1489,18 @@ void CVoIPController::SendPacket(char *data, size_t len, voip_endpoint_t* ep){ unsigned char key[32], iv[32], msgHash[SHA1_LENGTH]; crypto.sha1((uint8_t *) inner.GetBuffer(), len+4, msgHash); out.WriteBytes(keyFingerprint, 8); - out.WriteBytes((char *) (msgHash+(SHA1_LENGTH-16)), 16); - KDF(msgHash+(SHA1_LENGTH-16), key, iv); + out.WriteBytes((msgHash+(SHA1_LENGTH-16)), 16); + KDF(msgHash+(SHA1_LENGTH-16), isOutgoing ? 0 : 8, key, iv); unsigned char aesOut[inner.GetLength()]; - crypto.aes_ige_encrypt((unsigned char *) inner.GetBuffer(), aesOut, inner.GetLength(), key, iv); - out.WriteBytes((char*)aesOut, inner.GetLength()); + 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(), inet_ntoa(ep->address), ep->port); if(IS_MOBILE_NETWORK(networkType)) stats.bytesSentMobile+=(uint64_t)out.GetLength(); else stats.bytesSentWifi+=(uint64_t)out.GetLength(); - int res=sendto(udpSocket, out.GetBuffer(), out.GetLength(), 0, (const sockaddr *) &dst, sizeof(sockaddr_in)); + int res=sendto(udpSocket, out.GetBuffer(), out.GetLength(), 0, (const sockaddr *) &dst, sizeof(dst)); if(res<0){ LOGE("error sending: %d / %s", errno, strerror(errno)); } @@ -1389,19 +1514,24 @@ void CVoIPController::SetNetworkType(int type){ char itfName[32]; GetLocalNetworkItfInfo(NULL, itfName); if(strcmp(itfName, activeNetItfName)!=0){ + needUpdateNat64Prefix=true; LOGI("Active network interface changed: %s -> %s", activeNetItfName, itfName); bool isFirstChange=strlen(activeNetItfName)==0; strcpy(activeNetItfName, itfName); if(isFirstChange) return; if(currentEndpoint && currentEndpoint->type!=EP_TYPE_UDP_RELAY){ - int i; currentEndpoint=preferredRelay; - for(i=0;itype==EP_TYPE_UDP_P2P_LAN){ - free(endpoints[i]); - endpoints.erase(endpoints.begin()+i); - break; + for(std::vector::iterator itr=endpoints.begin();itr!=endpoints.end();){ + if((*itr)->type==EP_TYPE_UDP_P2P_INET){ + (*itr)->_averageRtt=0; + memset((*itr)->_rtts, 0, sizeof((*itr)->_rtts)); + } + if((*itr)->type==EP_TYPE_UDP_P2P_LAN){ + free((*itr)); + itr=endpoints.erase(itr); + }else{ + ++itr; } } } @@ -1442,19 +1572,24 @@ double CVoIPController::GetAverageRTT(){ return 999; } +#if defined(__APPLE__) +static void initMachTimestart() { + mach_timebase_info_data_t tb = { 0, 0 }; + mach_timebase_info(&tb); + CVoIPController::machTimebase = tb.numer; + CVoIPController::machTimebase /= tb.denom; + CVoIPController::machTimestart = mach_absolute_time(); +} +#endif + double CVoIPController::GetCurrentTime(){ #if defined(__linux__) struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec+(double)ts.tv_nsec/1000000000.0; #elif defined(__APPLE__) - if(!machTimestart){ - mach_timebase_info_data_t tb = { 0, 0 }; - mach_timebase_info(&tb); - machTimebase = tb.numer; - machTimebase /= tb.denom; - machTimestart = mach_absolute_time(); - } + static pthread_once_t token = PTHREAD_ONCE_INIT; + pthread_once(&token, &initMachTimestart); return (mach_absolute_time() - machTimestart) * machTimebase / 1000000000.0f; #endif } @@ -1483,13 +1618,18 @@ void CVoIPController::SetMicMute(bool mute){ audioInput->Stop(); else audioInput->Start(); + if(!audioInput->IsInitialized()){ + lastError=ERROR_AUDIO_IO; + SetState(STATE_FAILED); + return; + } } if(echoCanceller) echoCanceller->Enable(!mute); int i; for(i=0;itype==STREAM_TYPE_AUDIO){ - char buf[2]; + unsigned char buf[2]; buf[0]=outgoingStreams[i]->id; buf[1]=(char) (mute ? 0 : 1); SendPacketReliably(PKT_STREAM_STATE, buf, 2, .5f, 20); @@ -1535,37 +1675,36 @@ CBufferOutputStream *CVoIPController::GetOutgoingPacketBuffer(){ } -void CVoIPController::KDF(unsigned char* msgKey, unsigned char* aesKey, unsigned char* aesIv){ +void CVoIPController::KDF(unsigned char* msgKey, size_t x, unsigned char* aesKey, unsigned char* aesIv){ uint8_t sA[SHA1_LENGTH], sB[SHA1_LENGTH], sC[SHA1_LENGTH], sD[SHA1_LENGTH]; - size_t x=0; CBufferOutputStream buf(128); - buf.WriteBytes((char *) msgKey, 16); + buf.WriteBytes(msgKey, 16); buf.WriteBytes(encryptionKey+x, 32); - crypto.sha1((uint8_t *) buf.GetBuffer(), buf.GetLength(), sA); + crypto.sha1(buf.GetBuffer(), buf.GetLength(), sA); buf.Reset(); buf.WriteBytes(encryptionKey+32+x, 16); - buf.WriteBytes((char *) msgKey, 16); + buf.WriteBytes(msgKey, 16); buf.WriteBytes(encryptionKey+48+x, 16); - crypto.sha1((uint8_t *) buf.GetBuffer(), buf.GetLength(), sB); + crypto.sha1(buf.GetBuffer(), buf.GetLength(), sB); buf.Reset(); buf.WriteBytes(encryptionKey+64+x, 32); - buf.WriteBytes((char *) msgKey, 16); - crypto.sha1((uint8_t *) buf.GetBuffer(), buf.GetLength(), sC); + buf.WriteBytes(msgKey, 16); + crypto.sha1(buf.GetBuffer(), buf.GetLength(), sC); buf.Reset(); - buf.WriteBytes((char *) msgKey, 16); + buf.WriteBytes(msgKey, 16); buf.WriteBytes(encryptionKey+96+x, 32); - crypto.sha1(( uint8_t *) buf.GetBuffer(), buf.GetLength(), sD); + crypto.sha1(buf.GetBuffer(), buf.GetLength(), sD); buf.Reset(); - buf.WriteBytes((char *) sA, 8); - buf.WriteBytes((char *) sB+8, 12); - buf.WriteBytes((char *) sC+4, 12); + buf.WriteBytes(sA, 8); + buf.WriteBytes(sB+8, 12); + buf.WriteBytes(sC+4, 12); assert(buf.GetLength()==32); memcpy(aesKey, buf.GetBuffer(), 32); buf.Reset(); - buf.WriteBytes((char *) sA+8, 12); - buf.WriteBytes((char *) sB, 8); - buf.WriteBytes((char *) sC+16, 4); - buf.WriteBytes((char *) sD, 8); + buf.WriteBytes(sA+8, 12); + buf.WriteBytes(sB, 8); + buf.WriteBytes(sC+16, 4); + buf.WriteBytes(sD, 8); assert(buf.GetLength()==32); memcpy(aesIv, buf.GetBuffer(), 32); } @@ -1610,7 +1749,7 @@ void CVoIPController::GetDebugString(char *buffer, size_t len){ "Key fingerprint: %02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX\n" "Last sent/ack'd seq: %u/%u\n" "Last recvd seq: %u\n" - "Send/recv losses: %u/%u\n" + "Send/recv losses: %u/%u (%d%%)\n" "Audio bitrate: %d kbit\n" // "Packet grouping: %d\n" "Frame size out/in: %d/%d\n" @@ -1622,7 +1761,7 @@ void CVoIPController::GetDebugString(char *buffer, size_t len){ conctl->GetInflightDataSize(), conctl->GetCongestionWindow(), keyFingerprint[0],keyFingerprint[1],keyFingerprint[2],keyFingerprint[3],keyFingerprint[4],keyFingerprint[5],keyFingerprint[6],keyFingerprint[7], lastSentSeq, lastRemoteAckSeq, lastRemoteSeq, - conctl->GetSendLossCount(), recvLossCount, + conctl->GetSendLossCount(), recvLossCount, encoder ? encoder->GetPacketLoss() : 0, encoder ? (encoder->GetBitrate()/1000) : 0, // audioPacketGrouping, outgoingStreams[0]->frameDuration, incomingStreams.size()>0 ? incomingStreams[0]->frameDuration : 0, @@ -1640,11 +1779,8 @@ void CVoIPController::SendPublicEndpointsRequest(){ char buf[32]; memcpy(buf, relay->peerTag, 16); memset(buf+16, 0xFF, 16); - sockaddr_in dst; - dst.sin_addr=relay->address; - dst.sin_port=htons(relay->port); - dst.sin_family=AF_INET; - int res=sendto(udpSocket, buf, 32, 0, (const sockaddr *) &dst, sizeof(sockaddr_in)); + sockaddr_in6 dst(MakeInetAddress(relay->address, relay->port)); + int res=sendto(udpSocket, buf, 32, 0, (const sockaddr *) &dst, sizeof(dst)); if(res<0){ LOGE("error sending: %d / %s", errno, strerror(errno)); } @@ -1727,13 +1863,13 @@ float CVoIPController::GetOutputLevel(){ } -char * CVoIPController::SendPacketReliably(unsigned char type, char *data, size_t len, double retryInterval, double timeout){ +void CVoIPController::SendPacketReliably(unsigned char type, unsigned char *data, size_t len, double retryInterval, double timeout){ LOGD("Send reliably, type=%u, len=%u, retry=%.3llf, timeout=%.3llf", type, len, retryInterval, timeout); voip_queued_packet_t* pkt=(voip_queued_packet_t *) malloc(sizeof(voip_queued_packet_t)); memset(pkt, 0, sizeof(voip_queued_packet_t)); pkt->type=type; if(data){ - pkt->data=(char *) malloc(len); + pkt->data=(unsigned char *) malloc(len); memcpy(pkt->data, data, len); pkt->length=len; } @@ -1749,13 +1885,13 @@ char * CVoIPController::SendPacketReliably(unsigned char type, char *data, size_ void CVoIPController::SetConfig(voip_config_t *cfg){ memcpy(&config, cfg, sizeof(voip_config_t)); -#ifdef __ANDROID__ - enableAEC=cfg->enableAEC; -#endif + if(tgvoipLogFile){ + fclose(tgvoipLogFile); + } + if(strlen(cfg->logFilePath)) + tgvoipLogFile=fopen(cfg->logFilePath, "w"); UpdateDataSavingState(); UpdateAudioBitrate(); - if(cfg->frame_size) - outgoingStreams[0]->frameDuration=cfg->frame_size; } @@ -1800,7 +1936,7 @@ void CVoIPController::DebugCtl(int request, int param){ } -char* CVoIPController::GetVersion(){ +const char* CVoIPController::GetVersion(){ return LIBTGVOIP_VERSION; } @@ -1821,3 +1957,164 @@ void CVoIPController::GetStats(voip_stats_t *stats){ memcpy(stats, &this->stats, sizeof(voip_stats_t)); } + +uint16_t CVoIPController::GenerateLocalUDPPort(){ + uint16_t rnd; + crypto.rand_bytes((uint8_t *) &rnd, 2); + return (uint16_t) ((rnd%(MAX_UDP_PORT-MIN_UDP_PORT))+MIN_UDP_PORT); +} + +#ifdef TGVOIP_USE_AUDIO_SESSION +void CVoIPController::SetAcquireAudioSession(void (^completion)(void (^)())) { + this->acquireAudioSession = [completion copy]; +} + +void CVoIPController::ReleaseAudioSession(void (^completion)()) { + completion(); +} +#endif + +void CVoIPController::LogDebugInfo(){ + std::string json="{\"endpoints\":["; + for(std::vector::iterator itr=endpoints.begin();itr!=endpoints.end();++itr){ + voip_endpoint_t* e=*itr; + char buffer[1024]; + const char* typeStr="unknown"; + switch(e->type){ + case EP_TYPE_UDP_RELAY: + typeStr="udp_relay"; + break; + case EP_TYPE_UDP_P2P_INET: + typeStr="udp_p2p_inet"; + break; + case EP_TYPE_UDP_P2P_LAN: + typeStr="udp_p2p_lan"; + break; + } + snprintf(buffer, 1024, "{\"address\":\"%s\",\"port\":%u,\"type\":\"%s\",\"rtt\":%u%s%s}", inet_ntoa(e->address), e->port, typeStr, (unsigned int)round(e->_averageRtt*1000), currentEndpoint==e ? ",\"in_use\":true" : "", preferredRelay==e ? ",\"preferred\":true" : ""); + json+=buffer; + if(std::next(itr)!=endpoints.end()) + json+=","; + } + json+="],"; + char buffer[1024]; + const char* netTypeStr; + switch(networkType){ + case NET_TYPE_WIFI: + netTypeStr="wifi"; + break; + case NET_TYPE_GPRS: + netTypeStr="gprs"; + break; + case NET_TYPE_EDGE: + netTypeStr="edge"; + break; + case NET_TYPE_3G: + netTypeStr="3g"; + break; + case NET_TYPE_HSPA: + netTypeStr="hspa"; + break; + case NET_TYPE_LTE: + netTypeStr="lte"; + break; + case NET_TYPE_ETHERNET: + netTypeStr="ethernet"; + break; + case NET_TYPE_OTHER_HIGH_SPEED: + netTypeStr="other_high_speed"; + break; + case NET_TYPE_OTHER_LOW_SPEED: + netTypeStr="other_low_speed"; + break; + case NET_TYPE_DIALUP: + netTypeStr="dialup"; + break; + case NET_TYPE_OTHER_MOBILE: + netTypeStr="other_mobile"; + break; + default: + netTypeStr="unknown"; + break; + } + snprintf(buffer, 1024, "\"time\":%u,\"network_type\":\"%s\"}", (unsigned int)time(NULL), netTypeStr); + json+=buffer; + debugLogs.push_back(json); +} + +std::string CVoIPController::GetDebugLog(){ + std::string log="{\"events\":["; + + for(std::vector::iterator itr=debugLogs.begin();itr!=debugLogs.end();++itr){ + log+=(*itr); + if(std::next(itr)!=debugLogs.end()) + log+=","; + } + log+="],\"libtgvoip_version\":\"" LIBTGVOIP_VERSION "\"}"; + return log; +} + +void CVoIPController::GetDebugLog(char *buffer){ + strcpy(buffer, GetDebugLog().c_str()); +} + +size_t CVoIPController::GetDebugLogLength(){ + size_t len=128; + for(std::vector::iterator itr=debugLogs.begin();itr!=debugLogs.end();++itr){ + len+=(*itr).length()+1; + } + return len; +} + + +sockaddr_in6 CVoIPController::MakeInetAddress(in_addr addr, uint16_t port){ + // TODO: refactor the hell out of this by at least moving sockets to a separate class + if(needUpdateNat64Prefix){ + LOGV("Updating NAT64 prefix"); + nat64Present=false; + addrinfo* addr0; + int res=getaddrinfo("ipv4only.arpa", NULL, NULL, &addr0); + if(res!=0){ + LOGW("Error updating NAT64 prefix: %d / %s", res, gai_strerror(res)); + }else{ + addrinfo* addrPtr; + unsigned char* addr170=NULL; + unsigned char* addr171=NULL; + for(addrPtr=addr0;addrPtr;addrPtr=addrPtr->ai_next){ + if(addrPtr->ai_family==AF_INET6){ + sockaddr_in6* translatedAddr=(sockaddr_in6*)addrPtr->ai_addr; + uint32_t v4part=*((uint32_t*)&translatedAddr->sin6_addr.s6_addr[12]); + if(v4part==0xAA0000C0 && !addr170){ + addr170=translatedAddr->sin6_addr.s6_addr; + } + if(v4part==0xAB0000C0 && !addr171){ + addr171=translatedAddr->sin6_addr.s6_addr; + } + char buf[INET6_ADDRSTRLEN]; + LOGV("Got translated address: %s", inet_ntop(AF_INET6, &translatedAddr->sin6_addr, buf, sizeof(buf))); + } + } + if(addr170 && addr171 && memcmp(addr170, addr171, 12)==0){ + nat64Present=true; + memcpy(nat64Prefix, addr170, 12); + char buf[INET6_ADDRSTRLEN]; + LOGV("Found nat64 prefix from %s", inet_ntop(AF_INET6, addr170, buf, sizeof(buf))); + }else{ + LOGV("Didn't find nat64"); + } + freeaddrinfo(addr0); + } + needUpdateNat64Prefix=false; + } + sockaddr_in6 r; + memset(&r, 0, sizeof(sockaddr_in6)); + r.sin6_port=htons(port); + r.sin6_family=AF_INET6; + *((in_addr*)&r.sin6_addr.s6_addr[12])=addr; + if(nat64Present) + memcpy(r.sin6_addr.s6_addr, nat64Prefix, 12); + else + r.sin6_addr.s6_addr[11]=r.sin6_addr.s6_addr[10]=0xFF; + //r.sin6_len=sizeof(sa_family_t); + return r; +} diff --git a/VoIPController.h b/VoIPController.h index 3cddaeaceb..c884d6ae94 100644 --- a/VoIPController.h +++ b/VoIPController.h @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include "audio/AudioInput.h" #include "BlockingQueue.h" #include "BufferOutputStream.h" @@ -15,7 +17,11 @@ #include "EchoCanceller.h" #include "CongestionControl.h" -#define LIBTGVOIP_VERSION "0.2.4" +#ifdef __APPLE__ +#import +#endif + +#define LIBTGVOIP_VERSION "0.3.1" #define PKT_INIT 1 #define PKT_INIT_ACK 2 @@ -40,6 +46,7 @@ #define ERROR_UNKNOWN 0 #define ERROR_INCOMPATIBLE 1 #define ERROR_TIMEOUT 2 +#define ERROR_AUDIO_IO 3 #define NET_TYPE_UNKNOWN 0 #define NET_TYPE_GPRS 1 @@ -59,6 +66,8 @@ #define PROTOCOL_NAME 0x50567247 // "GrVP" in little endian (reversed here) #define PROTOCOL_VERSION 3 #define MIN_PROTOCOL_VERSION 3 +#define MIN_UDP_PORT 16384 +#define MAX_UDP_PORT 32768 #define STREAM_DATA_FLAG_LEN16 0x40 #define STREAM_DATA_FLAG_HAS_MORE_FLAGS 0x80 @@ -91,6 +100,14 @@ #define TLID_DECRYPTED_AUDIO_BLOCK 0xDBF948C1 #define TLID_SIMPLE_AUDIO_BLOCK 0xCC0D0E76 #define TLID_UDP_REFLECTOR_PEER_INFO 0x27D9371C +#define PAD4(x) (4-(x+(x<=253 ? 1 : 0))%4) + +inline int pad4(int x){ + int r=PAD4(x); + if(r==4) + return 0; + return r; +} struct voip_endpoint_t{ // make this a class maybe? int64_t id; @@ -98,7 +115,7 @@ struct voip_endpoint_t{ // make this a class maybe? in_addr address; in6_addr address6; char type; - char peerTag[16]; + unsigned char peerTag[16]; double _lastPingTime; uint32_t _lastPingSeq; @@ -119,7 +136,7 @@ typedef struct voip_stream_t voip_stream_t; struct voip_queued_packet_t{ unsigned char type; - char* data; + unsigned char* data; size_t length; uint32_t seqs[16]; double firstSentTime; @@ -133,10 +150,11 @@ struct voip_config_t{ double init_timeout; double recv_timeout; int data_saving; - int frame_size; -#ifdef __ANDROID__ + char logFilePath[256]; + bool enableAEC; -#endif + bool enableNS; + bool enableAGC; }; typedef struct voip_config_t voip_config_t; @@ -180,7 +198,7 @@ public: static double GetCurrentTime(); void* implData; void SetMicMute(bool mute); - void SetEncryptionKey(char* key); + void SetEncryptionKey(char* key, bool isOutgoing); void SetConfig(voip_config_t* cfg); float GetOutputLevel(); void DebugCtl(int request, int param); @@ -188,7 +206,14 @@ public: int64_t GetPreferredRelayID(); int GetLastError(); static voip_crypto_functions_t crypto; - static char* GetVersion(); + static const char* GetVersion(); +#ifdef TGVOIP_USE_AUDIO_SESSION + void SetAcquireAudioSession(void (^)(void (^)())); + void ReleaseAudioSession(void (^completion)()); +#endif + std::string GetDebugLog(); + void GetDebugLog(char* buffer); + size_t GetDebugLogLength(); private: static void* StartRecvThread(void* arg); @@ -197,7 +222,7 @@ private: void RunRecvThread(); void RunSendThread(); void RunTickThread(); - void SendPacket(char* data, size_t len, voip_endpoint_t* ep); + void SendPacket(unsigned char* data, size_t len, voip_endpoint_t* ep); void HandleAudioInput(unsigned char* data, size_t len); void UpdateAudioBitrate(); void SetState(int state); @@ -206,14 +231,17 @@ private: void SendInitAck(); void UpdateDataSavingState(); void SendP2pPing(int endpointType); - void KDF(unsigned char* msgKey, unsigned char* aesKey, unsigned char* aesIv); + void KDF(unsigned char* msgKey, size_t x, unsigned char* aesKey, unsigned char* aesIv); void GetLocalNetworkItfInfo(in_addr *addr, char *outName); + uint16_t GenerateLocalUDPPort(); CBufferOutputStream* GetOutgoingPacketBuffer(); uint32_t WritePacketHeader(CBufferOutputStream* s, unsigned char type, uint32_t length); static size_t AudioInputCallback(unsigned char* data, size_t length, void* param); void SendPublicEndpointsRequest(); voip_endpoint_t* GetEndpointByType(int type); - char * SendPacketReliably(unsigned char type, char* data, size_t len, double retryInterval, double timeout); + void SendPacketReliably(unsigned char type, unsigned char* data, size_t len, double retryInterval, double timeout); + void LogDebugInfo(); + sockaddr_in6 MakeInetAddress(in_addr addr, uint16_t port); int state; int udpSocket; std::vector endpoints; @@ -261,9 +289,9 @@ private: void (*stateCallback)(CVoIPController*, int); std::vector outgoingStreams; std::vector incomingStreams; - char encryptionKey[256]; - char keyFingerprint[8]; - char callID[16]; + unsigned char encryptionKey[256]; + unsigned char keyFingerprint[8]; + unsigned char callID[16]; double stateChangeTime; bool needSendP2pPing; bool waitingForRelayPeerInfo; @@ -284,9 +312,38 @@ private: int32_t peerVersion; CCongestionControl* conctl; voip_stats_t stats; - bool enableAEC; + bool receivedInit; + bool receivedInitAck; + std::vector debugLogs; + bool isOutgoing; + + unsigned char nat64Prefix[12]; + bool needUpdateNat64Prefix; + bool nat64Present; + + /*** 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; + +#ifdef TGVOIP_USE_AUDIO_SESSION + void (^acquireAudioSession)(void (^)()); + bool needNotifyAcquiredAudioSession; +#endif #ifdef __APPLE__ +public: static double machTimebase; static uint64_t machTimestart; #endif diff --git a/VoIPServerConfig.cpp b/VoIPServerConfig.cpp new file mode 100644 index 0000000000..9002d6b2c3 --- /dev/null +++ b/VoIPServerConfig.cpp @@ -0,0 +1,99 @@ +// +// libtgvoip is free and unencumbered public domain software. +// For more information, see http://unlicense.org or the UNLICENSE file +// you should have received with this source code distribution. +// + +#include "VoIPServerConfig.h" +#include +#include "logging.h" + +CVoIPServerConfig* CVoIPServerConfig::sharedInstance=NULL; + +CVoIPServerConfig::CVoIPServerConfig(){ + init_mutex(mutex); +} + +CVoIPServerConfig::~CVoIPServerConfig(){ + free_mutex(mutex); +} + +CVoIPServerConfig *CVoIPServerConfig::GetSharedInstance(){ + if(!sharedInstance) + sharedInstance=new CVoIPServerConfig(); + return sharedInstance; +} + +bool CVoIPServerConfig::GetBoolean(std::string name, bool fallback){ + CMutexGuard sync(mutex); + if(ContainsKey(name)){ + std::string val=config[name]; + if(val=="true") + return true; + if(val=="false") + return false; + } + return fallback; +} + +double CVoIPServerConfig::GetDouble(std::string name, double fallback){ + CMutexGuard sync(mutex); + if(ContainsKey(name)){ + std::string val=config[name]; + char* end; + const char* start=val.c_str(); + double d=strtod(start, &end); + if(end!=start){ + return d; + } + } + return fallback; +} + +int32_t CVoIPServerConfig::GetInt(std::string name, int32_t fallback){ + CMutexGuard sync(mutex); + if(ContainsKey(name)){ + std::string val=config[name]; + char* end; + const char* start=val.c_str(); + int32_t d=strtol(start, &end, 0); + if(end!=start){ + return d; + } + } + return fallback; +} + +std::string CVoIPServerConfig::GetString(std::string name, std::string fallback){ + CMutexGuard sync(mutex); + if(ContainsKey(name)) + return config[name]; + return fallback; +} + +void CVoIPServerConfig::Update(std::map newValues){ + CMutexGuard sync(mutex); + LOGD("=== Updating voip config ==="); + config.clear(); + for(std::map::iterator itr=newValues.begin();itr!=newValues.end();++itr){ + std::string key=itr->first; + std::string val=itr->second; + LOGV("%s -> %s", key.c_str(), val.c_str()); + config[key]=val; + } +} + +void CVoIPServerConfig::Update(const char **values, int count) { + std::map result; + for (int i = 0; i < count / 2; i++) { + result[values[i * 2 + 0]] = std::string(values[i * 2 + 1]); + } + Update(result); +} + + +bool CVoIPServerConfig::ContainsKey(std::string key){ + return config.find(key)!=config.end(); +} + + diff --git a/VoIPServerConfig.h b/VoIPServerConfig.h new file mode 100644 index 0000000000..9477e9f272 --- /dev/null +++ b/VoIPServerConfig.h @@ -0,0 +1,35 @@ +// +// libtgvoip is free and unencumbered public domain software. +// For more information, see http://unlicense.org or the UNLICENSE file +// you should have received with this source code distribution. +// + +#ifndef TGVOIP_VOIPSERVERCONFIG_H +#define TGVOIP_VOIPSERVERCONFIG_H + +#include +#include +#include "threading.h" + + +class CVoIPServerConfig{ +public: + CVoIPServerConfig(); + ~CVoIPServerConfig(); + static CVoIPServerConfig* GetSharedInstance(); + int32_t GetInt(std::string name, int32_t fallback); + double GetDouble(std::string name, double fallback); + std::string GetString(std::string name, std::string fallback); + bool GetBoolean(std::string name, bool fallback); + void Update(std::map newValues); + void Update(const char **values, int count); + +private: + static CVoIPServerConfig* sharedInstance; + bool ContainsKey(std::string key); + std::map config; + tgvoip_mutex_t mutex; +}; + + +#endif //TGVOIP_VOIPSERVERCONFIG_H diff --git a/audio/AudioInput.cpp b/audio/AudioInput.cpp index 6e9c356926..3f3814823b 100644 --- a/audio/AudioInput.cpp +++ b/audio/AudioInput.cpp @@ -14,6 +14,10 @@ #error "Unsupported operating system" #endif +CAudioInput::CAudioInput(){ + failed=false; +} + CAudioInput *CAudioInput::Create(){ #if defined(__ANDROID__) return new CAudioInputAndroid(); @@ -29,3 +33,6 @@ CAudioInput::~CAudioInput(){ #endif } +bool CAudioInput::IsInitialized(){ + return !failed; +} diff --git a/audio/AudioInput.h b/audio/AudioInput.h index 8d91b971f0..03e6175c7b 100644 --- a/audio/AudioInput.h +++ b/audio/AudioInput.h @@ -12,10 +12,15 @@ class CAudioInput : public CMediaStreamItf{ public: + CAudioInput(); virtual ~CAudioInput(); virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels)=0; + bool IsInitialized(); static CAudioInput* Create(); + +protected: + bool failed; }; diff --git a/client/android/tg_voip_jni.cpp b/client/android/tg_voip_jni.cpp index 55a810c521..2d9b5e21de 100644 --- a/client/android/tg_voip_jni.cpp +++ b/client/android/tg_voip_jni.cpp @@ -7,6 +7,9 @@ #include #include #include +#include +#include +#include #include "../../VoIPController.h" #include "../../os/android/AudioOutputOpenSLES.h" #include "../../os/android/AudioInputOpenSLES.h" @@ -24,6 +27,8 @@ struct impl_data_android_t{ void updateConnectionState(CVoIPController* 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); @@ -44,19 +49,21 @@ extern "C" JNIEXPORT jlong Java_org_telegram_messenger_voip_VoIPController_nativ CAudioOutputAndroid::systemVersion=systemVersion; env->GetJavaVM(&sharedJVM); - jclass cls=env->FindClass("org/telegram/messenger/voip/AudioRecordJNI"); - CAudioInputAndroid::jniClass=(jclass) env->NewGlobalRef(cls); - CAudioInputAndroid::initMethod=env->GetMethodID(cls, "init", "(IIII)V"); - CAudioInputAndroid::releaseMethod=env->GetMethodID(cls, "release", "()V"); - CAudioInputAndroid::startMethod=env->GetMethodID(cls, "start", "()V"); - CAudioInputAndroid::stopMethod=env->GetMethodID(cls, "stop", "()V"); + if(!CAudioInputAndroid::jniClass){ + jclass cls=env->FindClass("org/telegram/messenger/voip/AudioRecordJNI"); + CAudioInputAndroid::jniClass=(jclass) env->NewGlobalRef(cls); + CAudioInputAndroid::initMethod=env->GetMethodID(cls, "init", "(IIII)V"); + CAudioInputAndroid::releaseMethod=env->GetMethodID(cls, "release", "()V"); + CAudioInputAndroid::startMethod=env->GetMethodID(cls, "start", "()Z"); + CAudioInputAndroid::stopMethod=env->GetMethodID(cls, "stop", "()V"); - cls=env->FindClass("org/telegram/messenger/voip/AudioTrackJNI"); - CAudioOutputAndroid::jniClass=(jclass) env->NewGlobalRef(cls); - CAudioOutputAndroid::initMethod=env->GetMethodID(cls, "init", "(IIII)V"); - CAudioOutputAndroid::releaseMethod=env->GetMethodID(cls, "release", "()V"); - CAudioOutputAndroid::startMethod=env->GetMethodID(cls, "start", "()V"); - CAudioOutputAndroid::stopMethod=env->GetMethodID(cls, "stop", "()V"); + cls=env->FindClass("org/telegram/messenger/voip/AudioTrackJNI"); + CAudioOutputAndroid::jniClass=(jclass) env->NewGlobalRef(cls); + CAudioOutputAndroid::initMethod=env->GetMethodID(cls, "init", "(IIII)V"); + CAudioOutputAndroid::releaseMethod=env->GetMethodID(cls, "release", "()V"); + CAudioOutputAndroid::startMethod=env->GetMethodID(cls, "start", "()V"); + CAudioOutputAndroid::stopMethod=env->GetMethodID(cls, "stop", "()V"); + } setStateMethod=env->GetMethodID(env->GetObjectClass(thiz), "handleStateChange", "(I)V"); @@ -76,9 +83,9 @@ extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_native ((CVoIPController*)(intptr_t)inst)->Connect(); } -extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeSetEncryptionKey(JNIEnv* env, jobject thiz, jlong inst, jbyteArray key){ +extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeSetEncryptionKey(JNIEnv* env, jobject thiz, jlong inst, jbyteArray key, jboolean isOutgoing){ jbyte* akey=env->GetByteArrayElements(key, NULL); - ((CVoIPController*)(intptr_t)inst)->SetEncryptionKey((char *) akey); + ((CVoIPController*)(intptr_t)inst)->SetEncryptionKey((char *) akey, isOutgoing); env->ReleaseByteArrayElements(key, akey, JNI_ABORT); } @@ -130,12 +137,14 @@ extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_native } extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeRelease(JNIEnv* env, jobject thiz, jlong inst){ - env->DeleteGlobalRef(CAudioInputAndroid::jniClass); + //env->DeleteGlobalRef(CAudioInputAndroid::jniClass); CVoIPController* ctlr=((CVoIPController*)(intptr_t)inst); - env->DeleteGlobalRef(((impl_data_android_t*)ctlr->implData)->javaObject); - free(ctlr->implData); + impl_data_android_t* impl=(impl_data_android_t*)ctlr->implData; delete ctlr; + env->DeleteGlobalRef(impl->javaObject); + ((impl_data_android_t*)ctlr->implData)->javaObject=NULL; + free(impl); } @@ -171,13 +180,22 @@ extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_native ((CVoIPController*)(intptr_t)inst)->SetMicMute(mute); } -extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeSetConfig(JNIEnv* env, jobject thiz, jlong inst, jdouble recvTimeout, jdouble initTimeout, jint dataSavingMode, jint frameSize, jboolean enableAEC){ +extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeSetConfig(JNIEnv* env, jobject thiz, jlong inst, jdouble recvTimeout, jdouble initTimeout, jint dataSavingMode, jboolean enableAEC, jboolean enableNS, jboolean enableAGC, jstring logFilePath){ voip_config_t cfg; cfg.init_timeout=initTimeout; cfg.recv_timeout=recvTimeout; cfg.data_saving=dataSavingMode; - cfg.frame_size=frameSize; cfg.enableAEC=enableAEC; + cfg.enableNS=enableNS; + cfg.enableAGC=enableAGC; + if(logFilePath){ + char* path=(char *) env->GetStringUTFChars(logFilePath, NULL); + strncpy(cfg.logFilePath, path, sizeof(cfg.logFilePath)); + cfg.logFilePath[sizeof(cfg.logFilePath)-1]=0; + env->ReleaseStringUTFChars(logFilePath, path); + }else{ + memset(cfg.logFilePath, 0, sizeof(cfg.logFilePath)); + } ((CVoIPController*)(intptr_t)inst)->SetConfig(&cfg); } @@ -205,4 +223,30 @@ extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_native env->SetLongField(stats, env->GetFieldID(cls, "bytesSentMobile", "J"), _stats.bytesSentMobile); env->SetLongField(stats, env->GetFieldID(cls, "bytesRecvdWifi", "J"), _stats.bytesRecvdWifi); env->SetLongField(stats, env->GetFieldID(cls, "bytesRecvdMobile", "J"), _stats.bytesRecvdMobile); +} + +extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPServerConfig_nativeSetConfig(JNIEnv* env, jclass clasz, jobjectArray keys, jobjectArray values){ + std::map config; + int len=env->GetArrayLength(keys); + int i; + for(i=0;iGetObjectArrayElement(keys, i); + jstring jval=(jstring)env->GetObjectArrayElement(values, i); + if(jkey==NULL|| jval==NULL) + continue; + const char* ckey=env->GetStringUTFChars(jkey, NULL); + const char* cval=env->GetStringUTFChars(jval, NULL); + std::string key(ckey); + std::string val(cval); + env->ReleaseStringUTFChars(jkey, ckey); + env->ReleaseStringUTFChars(jval, cval); + config[key]=val; + } + CVoIPServerConfig::GetSharedInstance()->Update(config); +} + +extern "C" JNIEXPORT jstring Java_org_telegram_messenger_voip_VoIPController_nativeGetDebugLog(JNIEnv* env, jobject thiz, jlong inst){ + CVoIPController* ctlr=((CVoIPController*)(intptr_t)inst); + std::string log=ctlr->GetDebugLog(); + return env->NewStringUTF(log.c_str()); } \ No newline at end of file diff --git a/external/include/webrtc/gain_control.h b/external/include/webrtc/gain_control.h new file mode 100644 index 0000000000..3bb263e0be --- /dev/null +++ b/external/include/webrtc/gain_control.h @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_AGC_LEGACY_GAIN_CONTROL_H_ +#define WEBRTC_MODULES_AUDIO_PROCESSING_AGC_LEGACY_GAIN_CONTROL_H_ + +//#include "webrtc/typedefs.h" + +// Errors +#define AGC_UNSPECIFIED_ERROR 18000 +#define AGC_UNSUPPORTED_FUNCTION_ERROR 18001 +#define AGC_UNINITIALIZED_ERROR 18002 +#define AGC_NULL_POINTER_ERROR 18003 +#define AGC_BAD_PARAMETER_ERROR 18004 + +// Warnings +#define AGC_BAD_PARAMETER_WARNING 18050 + +enum { + kAgcModeUnchanged, + kAgcModeAdaptiveAnalog, + kAgcModeAdaptiveDigital, + kAgcModeFixedDigital +}; + +enum { kAgcFalse = 0, kAgcTrue }; + +typedef struct { + int16_t targetLevelDbfs; // default 3 (-3 dBOv) + int16_t compressionGaindB; // default 9 dB + uint8_t limiterEnable; // default kAgcTrue (on) +} WebRtcAgcConfig; + +#if defined(__cplusplus) +extern "C" { +#endif + +/* + * This function analyses the number of samples passed to + * farend and produces any error code that could arise. + * + * Input: + * - agcInst : AGC instance. + * - samples : Number of samples in input vector. + * + * Return value: + * : 0 - Normal operation. + * : -1 - Error. + */ +int WebRtcAgc_GetAddFarendError(void* state, size_t samples); + +/* + * This function processes a 10 ms frame of far-end speech to determine + * if there is active speech. The length of the input speech vector must be + * given in samples (80 when FS=8000, and 160 when FS=16000, FS=32000 or + * FS=48000). + * + * Input: + * - agcInst : AGC instance. + * - inFar : Far-end input speech vector + * - samples : Number of samples in input vector + * + * Return value: + * : 0 - Normal operation. + * : -1 - Error + */ +int WebRtcAgc_AddFarend(void* agcInst, const int16_t* inFar, size_t samples); + +/* + * This function processes a 10 ms frame of microphone speech to determine + * if there is active speech. The length of the input speech vector must be + * given in samples (80 when FS=8000, and 160 when FS=16000, FS=32000 or + * FS=48000). For very low input levels, the input signal is increased in level + * by multiplying and overwriting the samples in inMic[]. + * + * This function should be called before any further processing of the + * near-end microphone signal. + * + * Input: + * - agcInst : AGC instance. + * - inMic : Microphone input speech vector for each band + * - num_bands : Number of bands in input vector + * - samples : Number of samples in input vector + * + * Return value: + * : 0 - Normal operation. + * : -1 - Error + */ +int WebRtcAgc_AddMic(void* agcInst, + int16_t* const* inMic, + size_t num_bands, + size_t samples); + +/* + * This function replaces the analog microphone with a virtual one. + * It is a digital gain applied to the input signal and is used in the + * agcAdaptiveDigital mode where no microphone level is adjustable. The length + * of the input speech vector must be given in samples (80 when FS=8000, and 160 + * when FS=16000, FS=32000 or FS=48000). + * + * Input: + * - agcInst : AGC instance. + * - inMic : Microphone input speech vector for each band + * - num_bands : Number of bands in input vector + * - samples : Number of samples in input vector + * - micLevelIn : Input level of microphone (static) + * + * Output: + * - inMic : Microphone output after processing (L band) + * - inMic_H : Microphone output after processing (H band) + * - micLevelOut : Adjusted microphone level after processing + * + * Return value: + * : 0 - Normal operation. + * : -1 - Error + */ +int WebRtcAgc_VirtualMic(void* agcInst, + int16_t* const* inMic, + size_t num_bands, + size_t samples, + int32_t micLevelIn, + int32_t* micLevelOut); + +/* + * This function processes a 10 ms frame and adjusts (normalizes) the gain both + * analog and digitally. The gain adjustments are done only during active + * periods of speech. The length of the speech vectors must be given in samples + * (80 when FS=8000, and 160 when FS=16000, FS=32000 or FS=48000). The echo + * parameter can be used to ensure the AGC will not adjust upward in the + * presence of echo. + * + * This function should be called after processing the near-end microphone + * signal, in any case after any echo cancellation. + * + * Input: + * - agcInst : AGC instance + * - inNear : Near-end input speech vector for each band + * - num_bands : Number of bands in input/output vector + * - samples : Number of samples in input/output vector + * - inMicLevel : Current microphone volume level + * - echo : Set to 0 if the signal passed to add_mic is + * almost certainly free of echo; otherwise set + * to 1. If you have no information regarding echo + * set to 0. + * + * Output: + * - outMicLevel : Adjusted microphone volume level + * - out : Gain-adjusted near-end speech vector + * : May be the same vector as the input. + * - saturationWarning : A returned value of 1 indicates a saturation event + * has occurred and the volume cannot be further + * reduced. Otherwise will be set to 0. + * + * Return value: + * : 0 - Normal operation. + * : -1 - Error + */ +int WebRtcAgc_Process(void* agcInst, + const int16_t* const* inNear, + size_t num_bands, + size_t samples, + int16_t* const* out, + int32_t inMicLevel, + int32_t* outMicLevel, + int16_t echo, + uint8_t* saturationWarning); + +/* + * This function sets the config parameters (targetLevelDbfs, + * compressionGaindB and limiterEnable). + * + * Input: + * - agcInst : AGC instance + * - config : config struct + * + * Output: + * + * Return value: + * : 0 - Normal operation. + * : -1 - Error + */ +int WebRtcAgc_set_config(void* agcInst, WebRtcAgcConfig config); + +/* + * This function returns the config parameters (targetLevelDbfs, + * compressionGaindB and limiterEnable). + * + * Input: + * - agcInst : AGC instance + * + * Output: + * - config : config struct + * + * Return value: + * : 0 - Normal operation. + * : -1 - Error + */ +int WebRtcAgc_get_config(void* agcInst, WebRtcAgcConfig* config); + +/* + * This function creates and returns an AGC instance, which will contain the + * state information for one (duplex) channel. + */ +void* WebRtcAgc_Create(); + +/* + * This function frees the AGC instance created at the beginning. + * + * Input: + * - agcInst : AGC instance. + */ +void WebRtcAgc_Free(void* agcInst); + +/* + * This function initializes an AGC instance. + * + * Input: + * - agcInst : AGC instance. + * - minLevel : Minimum possible mic level + * - maxLevel : Maximum possible mic level + * - agcMode : 0 - Unchanged + * : 1 - Adaptive Analog Automatic Gain Control -3dBOv + * : 2 - Adaptive Digital Automatic Gain Control -3dBOv + * : 3 - Fixed Digital Gain 0dB + * - fs : Sampling frequency + * + * Return value : 0 - Ok + * -1 - Error + */ +int WebRtcAgc_Init(void* agcInst, + int32_t minLevel, + int32_t maxLevel, + int16_t agcMode, + uint32_t fs); + +#if defined(__cplusplus) +} +#endif + +#endif // WEBRTC_MODULES_AUDIO_PROCESSING_AGC_LEGACY_GAIN_CONTROL_H_ diff --git a/external/include/webrtc/splitting_filter_wrapper.h b/external/include/webrtc/splitting_filter_wrapper.h index 25eb553b99..cd4735df82 100644 --- a/external/include/webrtc/splitting_filter_wrapper.h +++ b/external/include/webrtc/splitting_filter_wrapper.h @@ -1,20 +1,22 @@ #ifndef __WEBRTC_SPLITTING_FILTER_WRAPPER #define __WEBRTC_SPLITTING_FILTER_WRAPPER -struct splitting_filter_t{ +#include + +struct tgvoip_splitting_filter_t{ /*IFCHannelBuffer*/ void* _bufferIn; /*IFCHannelBuffer*/ void* _bufferOut; /*SplittingFilter*/ void* _splittingFilter; - float* bufferIn; - float* bufferOut[3]; + int16_t bufferIn[960]; + int16_t bufferOut[3][320]; }; extern "C"{ -splitting_filter_t* tgvoip_splitting_filter_create(); -void tgvoip_splitting_filter_free(splitting_filter_t* filter); +tgvoip_splitting_filter_t* tgvoip_splitting_filter_create(); +void tgvoip_splitting_filter_free(tgvoip_splitting_filter_t* filter); -void tgvoip_splitting_filter_analyze(splitting_filter_t* filter); -void tgvoip_splitting_filter_synthesize(splitting_filter_t* filter); +void tgvoip_splitting_filter_analyze(tgvoip_splitting_filter_t* filter); +void tgvoip_splitting_filter_synthesize(tgvoip_splitting_filter_t* filter); } #endif // __WEBRTC_SPLITTING_FILTER_WRAPPER \ No newline at end of file diff --git a/external/libWebRtcAec_android_armeabi-v7a.a b/external/libWebRtcAec_android_armeabi-v7a.a index 253ee69515..3b55b94529 100644 Binary files a/external/libWebRtcAec_android_armeabi-v7a.a and b/external/libWebRtcAec_android_armeabi-v7a.a differ diff --git a/external/libWebRtcAec_android_x86.a b/external/libWebRtcAec_android_x86.a index a44b9080cc..7b150797fe 100644 Binary files a/external/libWebRtcAec_android_x86.a and b/external/libWebRtcAec_android_x86.a differ diff --git a/external/libWebRtcAec_darwin.a b/external/libWebRtcAec_darwin.a index 6b8229b17e..7686648a06 100644 Binary files a/external/libWebRtcAec_darwin.a and b/external/libWebRtcAec_darwin.a differ diff --git a/libtgvoip.xcodeproj/project.pbxproj b/libtgvoip.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..822ad7801c --- /dev/null +++ b/libtgvoip.xcodeproj/project.pbxproj @@ -0,0 +1,865 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 6915307B1E6B5BAB004F643F /* logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6915307A1E6B5BAB004F643F /* logging.cpp */; }; + 692AB8CB1E6759DD00706ACC /* AudioInput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 692AB8881E6759DD00706ACC /* AudioInput.cpp */; }; + 692AB8CC1E6759DD00706ACC /* AudioInput.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB8891E6759DD00706ACC /* AudioInput.h */; }; + 692AB8CD1E6759DD00706ACC /* AudioOutput.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 692AB88A1E6759DD00706ACC /* AudioOutput.cpp */; }; + 692AB8CE1E6759DD00706ACC /* AudioOutput.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB88B1E6759DD00706ACC /* AudioOutput.h */; }; + 692AB8CF1E6759DD00706ACC /* BlockingQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 692AB88C1E6759DD00706ACC /* BlockingQueue.cpp */; }; + 692AB8D01E6759DD00706ACC /* BlockingQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB88D1E6759DD00706ACC /* BlockingQueue.h */; }; + 692AB8D11E6759DD00706ACC /* BufferInputStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 692AB88E1E6759DD00706ACC /* BufferInputStream.cpp */; }; + 692AB8D21E6759DD00706ACC /* BufferInputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB88F1E6759DD00706ACC /* BufferInputStream.h */; }; + 692AB8D31E6759DD00706ACC /* BufferOutputStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 692AB8901E6759DD00706ACC /* BufferOutputStream.cpp */; }; + 692AB8D41E6759DD00706ACC /* BufferOutputStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB8911E6759DD00706ACC /* BufferOutputStream.h */; }; + 692AB8D51E6759DD00706ACC /* BufferPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 692AB8921E6759DD00706ACC /* BufferPool.cpp */; }; + 692AB8D61E6759DD00706ACC /* BufferPool.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB8931E6759DD00706ACC /* BufferPool.h */; }; + 692AB8D81E6759DD00706ACC /* CongestionControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 692AB8971E6759DD00706ACC /* CongestionControl.cpp */; }; + 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 */; }; + 692AB8DC1E6759DD00706ACC /* echo_cancellation.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB89E1E6759DD00706ACC /* echo_cancellation.h */; }; + 692AB8DD1E6759DD00706ACC /* echo_control_mobile.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB89F1E6759DD00706ACC /* echo_control_mobile.h */; }; + 692AB8DE1E6759DD00706ACC /* gain_control.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB8A01E6759DD00706ACC /* gain_control.h */; }; + 692AB8DF1E6759DD00706ACC /* noise_suppression.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB8A11E6759DD00706ACC /* noise_suppression.h */; }; + 692AB8E01E6759DD00706ACC /* noise_suppression_x.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB8A21E6759DD00706ACC /* noise_suppression_x.h */; }; + 692AB8E11E6759DD00706ACC /* splitting_filter_wrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB8A31E6759DD00706ACC /* splitting_filter_wrapper.h */; }; + 692AB8E41E6759DD00706ACC /* libWebRtcAec_darwin.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 692AB8A61E6759DD00706ACC /* libWebRtcAec_darwin.a */; }; + 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 */; }; + 692AB8E91E6759DD00706ACC /* MediaStreamItf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 692AB8AB1E6759DD00706ACC /* MediaStreamItf.cpp */; }; + 692AB8EA1E6759DD00706ACC /* MediaStreamItf.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB8AC1E6759DD00706ACC /* MediaStreamItf.h */; }; + 692AB8EB1E6759DD00706ACC /* OpusDecoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 692AB8AD1E6759DD00706ACC /* OpusDecoder.cpp */; }; + 692AB8EC1E6759DD00706ACC /* OpusDecoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB8AE1E6759DD00706ACC /* OpusDecoder.h */; }; + 692AB8ED1E6759DD00706ACC /* OpusEncoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 692AB8AF1E6759DD00706ACC /* OpusEncoder.cpp */; }; + 692AB8EE1E6759DD00706ACC /* OpusEncoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB8B01E6759DD00706ACC /* OpusEncoder.h */; }; + 692AB8F91E6759DD00706ACC /* AudioInputAudioUnit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 692AB8BE1E6759DD00706ACC /* AudioInputAudioUnit.cpp */; }; + 692AB8FA1E6759DD00706ACC /* AudioInputAudioUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB8BF1E6759DD00706ACC /* AudioInputAudioUnit.h */; }; + 692AB8FB1E6759DD00706ACC /* AudioOutputAudioUnit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 692AB8C01E6759DD00706ACC /* AudioOutputAudioUnit.cpp */; }; + 692AB8FC1E6759DD00706ACC /* AudioOutputAudioUnit.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB8C11E6759DD00706ACC /* AudioOutputAudioUnit.h */; }; + 692AB8FD1E6759DD00706ACC /* AudioUnitIO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 692AB8C21E6759DD00706ACC /* AudioUnitIO.cpp */; }; + 692AB8FE1E6759DD00706ACC /* AudioUnitIO.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB8C31E6759DD00706ACC /* AudioUnitIO.h */; }; + 692AB8FF1E6759DD00706ACC /* TGLogWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB8C41E6759DD00706ACC /* TGLogWrapper.h */; }; + 692AB9001E6759DD00706ACC /* TGLogWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 692AB8C51E6759DD00706ACC /* TGLogWrapper.m */; }; + 692AB9011E6759DD00706ACC /* threading.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB8C61E6759DD00706ACC /* threading.h */; }; + 692AB9021E6759DD00706ACC /* VoIPController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 692AB8C71E6759DD00706ACC /* VoIPController.cpp */; }; + 692AB9031E6759DD00706ACC /* VoIPController.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB8C81E6759DD00706ACC /* VoIPController.h */; }; + 692AB9041E6759DD00706ACC /* VoIPServerConfig.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 692AB8C91E6759DD00706ACC /* VoIPServerConfig.cpp */; }; + 692AB9051E6759DD00706ACC /* VoIPServerConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 692AB8CA1E6759DD00706ACC /* VoIPServerConfig.h */; }; + 692AB91F1E675F7000706ACC /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 692AB91C1E675F7000706ACC /* AudioToolbox.framework */; }; + 692AB9201E675F7000706ACC /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 692AB91D1E675F7000706ACC /* AudioUnit.framework */; }; + 692AB9211E675F7000706ACC /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 692AB91E1E675F7000706ACC /* CoreAudio.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 692AB9101E675E8800706ACC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 692AB9071E675E8800706ACC /* Telegraph.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = D08805AC156E8F3600311537; + remoteInfo = Telegraph; + }; + 692AB9121E675E8800706ACC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 692AB9071E675E8800706ACC /* Telegraph.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = D02601D71A55CA2300716290; + remoteInfo = Share; + }; + 692AB9141E675E8800706ACC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 692AB9071E675E8800706ACC /* Telegraph.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 68744C0D1BB1A9F700FE6542; + remoteInfo = watchkitapp; + }; + 692AB9161E675E8800706ACC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 692AB9071E675E8800706ACC /* Telegraph.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 68744C191BB1A9F700FE6542; + remoteInfo = "watchkitapp Extension"; + }; + 692AB9181E675E8800706ACC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 692AB9071E675E8800706ACC /* Telegraph.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = D020FADD1D99466A00F279AA; + remoteInfo = SiriIntents; + }; + 692AB91A1E675E8800706ACC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 692AB9071E675E8800706ACC /* Telegraph.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = D020FB0A1D99637100F279AA; + remoteInfo = LegacyDatabase; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 6915307A1E6B5BAB004F643F /* logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = logging.cpp; sourceTree = ""; }; + 692AB8881E6759DD00706ACC /* AudioInput.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AudioInput.cpp; sourceTree = ""; }; + 692AB8891E6759DD00706ACC /* AudioInput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioInput.h; sourceTree = ""; }; + 692AB88A1E6759DD00706ACC /* AudioOutput.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AudioOutput.cpp; sourceTree = ""; }; + 692AB88B1E6759DD00706ACC /* AudioOutput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioOutput.h; sourceTree = ""; }; + 692AB88C1E6759DD00706ACC /* BlockingQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BlockingQueue.cpp; sourceTree = ""; }; + 692AB88D1E6759DD00706ACC /* BlockingQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlockingQueue.h; sourceTree = ""; }; + 692AB88E1E6759DD00706ACC /* BufferInputStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BufferInputStream.cpp; sourceTree = ""; }; + 692AB88F1E6759DD00706ACC /* BufferInputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BufferInputStream.h; sourceTree = ""; }; + 692AB8901E6759DD00706ACC /* BufferOutputStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BufferOutputStream.cpp; sourceTree = ""; }; + 692AB8911E6759DD00706ACC /* BufferOutputStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BufferOutputStream.h; sourceTree = ""; }; + 692AB8921E6759DD00706ACC /* BufferPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BufferPool.cpp; sourceTree = ""; }; + 692AB8931E6759DD00706ACC /* BufferPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BufferPool.h; sourceTree = ""; }; + 692AB8971E6759DD00706ACC /* CongestionControl.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = CongestionControl.cpp; sourceTree = ""; }; + 692AB8981E6759DD00706ACC /* CongestionControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CongestionControl.h; sourceTree = ""; }; + 692AB8991E6759DD00706ACC /* EchoCanceller.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EchoCanceller.cpp; sourceTree = ""; }; + 692AB89A1E6759DD00706ACC /* EchoCanceller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EchoCanceller.h; sourceTree = ""; }; + 692AB89E1E6759DD00706ACC /* echo_cancellation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = echo_cancellation.h; sourceTree = ""; }; + 692AB89F1E6759DD00706ACC /* echo_control_mobile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = echo_control_mobile.h; sourceTree = ""; }; + 692AB8A01E6759DD00706ACC /* gain_control.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = gain_control.h; sourceTree = ""; }; + 692AB8A11E6759DD00706ACC /* noise_suppression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = noise_suppression.h; sourceTree = ""; }; + 692AB8A21E6759DD00706ACC /* noise_suppression_x.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = noise_suppression_x.h; sourceTree = ""; }; + 692AB8A31E6759DD00706ACC /* splitting_filter_wrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = splitting_filter_wrapper.h; sourceTree = ""; }; + 692AB8A61E6759DD00706ACC /* libWebRtcAec_darwin.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libWebRtcAec_darwin.a; sourceTree = ""; }; + 692AB8A71E6759DD00706ACC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 692AB8A81E6759DD00706ACC /* JitterBuffer.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = JitterBuffer.cpp; sourceTree = ""; }; + 692AB8A91E6759DD00706ACC /* JitterBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JitterBuffer.h; sourceTree = ""; }; + 692AB8AA1E6759DD00706ACC /* logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = logging.h; sourceTree = ""; }; + 692AB8AB1E6759DD00706ACC /* MediaStreamItf.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MediaStreamItf.cpp; sourceTree = ""; }; + 692AB8AC1E6759DD00706ACC /* MediaStreamItf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaStreamItf.h; sourceTree = ""; }; + 692AB8AD1E6759DD00706ACC /* OpusDecoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OpusDecoder.cpp; sourceTree = ""; }; + 692AB8AE1E6759DD00706ACC /* OpusDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpusDecoder.h; sourceTree = ""; }; + 692AB8AF1E6759DD00706ACC /* OpusEncoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OpusEncoder.cpp; sourceTree = ""; }; + 692AB8B01E6759DD00706ACC /* OpusEncoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpusEncoder.h; sourceTree = ""; }; + 692AB8BE1E6759DD00706ACC /* AudioInputAudioUnit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AudioInputAudioUnit.cpp; sourceTree = ""; }; + 692AB8BF1E6759DD00706ACC /* AudioInputAudioUnit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioInputAudioUnit.h; sourceTree = ""; }; + 692AB8C01E6759DD00706ACC /* AudioOutputAudioUnit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AudioOutputAudioUnit.cpp; sourceTree = ""; }; + 692AB8C11E6759DD00706ACC /* AudioOutputAudioUnit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioOutputAudioUnit.h; sourceTree = ""; }; + 692AB8C21E6759DD00706ACC /* AudioUnitIO.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = AudioUnitIO.cpp; sourceTree = ""; }; + 692AB8C31E6759DD00706ACC /* AudioUnitIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioUnitIO.h; sourceTree = ""; }; + 692AB8C41E6759DD00706ACC /* TGLogWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLogWrapper.h; sourceTree = ""; }; + 692AB8C51E6759DD00706ACC /* TGLogWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLogWrapper.m; sourceTree = ""; }; + 692AB8C61E6759DD00706ACC /* threading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = threading.h; sourceTree = ""; }; + 692AB8C71E6759DD00706ACC /* VoIPController.cpp */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = VoIPController.cpp; sourceTree = ""; }; + 692AB8C81E6759DD00706ACC /* VoIPController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VoIPController.h; sourceTree = ""; }; + 692AB8C91E6759DD00706ACC /* VoIPServerConfig.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VoIPServerConfig.cpp; sourceTree = ""; }; + 692AB8CA1E6759DD00706ACC /* VoIPServerConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VoIPServerConfig.h; sourceTree = ""; }; + 692AB9071E675E8800706ACC /* Telegraph.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Telegraph.xcodeproj; path = ../../Telegraph.xcodeproj; sourceTree = ""; }; + 692AB91C1E675F7000706ACC /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; + 692AB91D1E675F7000706ACC /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; }; + 692AB91E1E675F7000706ACC /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; }; + 69F842361E67540700C110F7 /* libtgvoip.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = libtgvoip.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 69F842321E67540700C110F7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 692AB91F1E675F7000706ACC /* AudioToolbox.framework in Frameworks */, + 692AB9201E675F7000706ACC /* AudioUnit.framework in Frameworks */, + 692AB9211E675F7000706ACC /* CoreAudio.framework in Frameworks */, + 692AB8E41E6759DD00706ACC /* libWebRtcAec_darwin.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 692AB8861E6759BF00706ACC /* libtgvoip */ = { + isa = PBXGroup; + children = ( + 692AB8871E6759DD00706ACC /* audio */, + 692AB88C1E6759DD00706ACC /* BlockingQueue.cpp */, + 692AB88D1E6759DD00706ACC /* BlockingQueue.h */, + 692AB88E1E6759DD00706ACC /* BufferInputStream.cpp */, + 692AB88F1E6759DD00706ACC /* BufferInputStream.h */, + 692AB8901E6759DD00706ACC /* BufferOutputStream.cpp */, + 692AB8911E6759DD00706ACC /* BufferOutputStream.h */, + 692AB8921E6759DD00706ACC /* BufferPool.cpp */, + 692AB8931E6759DD00706ACC /* BufferPool.h */, + 692AB8971E6759DD00706ACC /* CongestionControl.cpp */, + 692AB8981E6759DD00706ACC /* CongestionControl.h */, + 692AB8991E6759DD00706ACC /* EchoCanceller.cpp */, + 692AB89A1E6759DD00706ACC /* EchoCanceller.h */, + 692AB89B1E6759DD00706ACC /* external */, + 692AB8A71E6759DD00706ACC /* Info.plist */, + 692AB8A81E6759DD00706ACC /* JitterBuffer.cpp */, + 692AB8A91E6759DD00706ACC /* JitterBuffer.h */, + 6915307A1E6B5BAB004F643F /* logging.cpp */, + 692AB8AA1E6759DD00706ACC /* logging.h */, + 692AB8AB1E6759DD00706ACC /* MediaStreamItf.cpp */, + 692AB8AC1E6759DD00706ACC /* MediaStreamItf.h */, + 692AB8AD1E6759DD00706ACC /* OpusDecoder.cpp */, + 692AB8AE1E6759DD00706ACC /* OpusDecoder.h */, + 692AB8AF1E6759DD00706ACC /* OpusEncoder.cpp */, + 692AB8B01E6759DD00706ACC /* OpusEncoder.h */, + 692AB8B11E6759DD00706ACC /* os */, + 692AB8C61E6759DD00706ACC /* threading.h */, + 692AB8C71E6759DD00706ACC /* VoIPController.cpp */, + 692AB8C81E6759DD00706ACC /* VoIPController.h */, + 692AB8C91E6759DD00706ACC /* VoIPServerConfig.cpp */, + 692AB8CA1E6759DD00706ACC /* VoIPServerConfig.h */, + ); + name = libtgvoip; + sourceTree = ""; + }; + 692AB8871E6759DD00706ACC /* audio */ = { + isa = PBXGroup; + children = ( + 692AB8881E6759DD00706ACC /* AudioInput.cpp */, + 692AB8891E6759DD00706ACC /* AudioInput.h */, + 692AB88A1E6759DD00706ACC /* AudioOutput.cpp */, + 692AB88B1E6759DD00706ACC /* AudioOutput.h */, + ); + path = audio; + sourceTree = ""; + }; + 692AB89B1E6759DD00706ACC /* external */ = { + isa = PBXGroup; + children = ( + 692AB89C1E6759DD00706ACC /* include */, + 692AB8A61E6759DD00706ACC /* libWebRtcAec_darwin.a */, + ); + path = external; + sourceTree = ""; + }; + 692AB89C1E6759DD00706ACC /* include */ = { + isa = PBXGroup; + children = ( + 692AB89D1E6759DD00706ACC /* webrtc */, + ); + path = include; + sourceTree = ""; + }; + 692AB89D1E6759DD00706ACC /* webrtc */ = { + isa = PBXGroup; + children = ( + 692AB89E1E6759DD00706ACC /* echo_cancellation.h */, + 692AB89F1E6759DD00706ACC /* echo_control_mobile.h */, + 692AB8A01E6759DD00706ACC /* gain_control.h */, + 692AB8A11E6759DD00706ACC /* noise_suppression.h */, + 692AB8A21E6759DD00706ACC /* noise_suppression_x.h */, + 692AB8A31E6759DD00706ACC /* splitting_filter_wrapper.h */, + ); + path = webrtc; + sourceTree = ""; + }; + 692AB8B11E6759DD00706ACC /* os */ = { + isa = PBXGroup; + children = ( + 692AB8BD1E6759DD00706ACC /* darwin */, + ); + path = os; + sourceTree = ""; + }; + 692AB8BD1E6759DD00706ACC /* darwin */ = { + isa = PBXGroup; + children = ( + 692AB8BE1E6759DD00706ACC /* AudioInputAudioUnit.cpp */, + 692AB8BF1E6759DD00706ACC /* AudioInputAudioUnit.h */, + 692AB8C01E6759DD00706ACC /* AudioOutputAudioUnit.cpp */, + 692AB8C11E6759DD00706ACC /* AudioOutputAudioUnit.h */, + 692AB8C21E6759DD00706ACC /* AudioUnitIO.cpp */, + 692AB8C31E6759DD00706ACC /* AudioUnitIO.h */, + 692AB8C41E6759DD00706ACC /* TGLogWrapper.h */, + 692AB8C51E6759DD00706ACC /* TGLogWrapper.m */, + ); + path = darwin; + sourceTree = ""; + }; + 692AB9061E675E8700706ACC /* Frameworks */ = { + isa = PBXGroup; + children = ( + 692AB91C1E675F7000706ACC /* AudioToolbox.framework */, + 692AB91D1E675F7000706ACC /* AudioUnit.framework */, + 692AB91E1E675F7000706ACC /* CoreAudio.framework */, + 692AB9071E675E8800706ACC /* Telegraph.xcodeproj */, + ); + name = Frameworks; + sourceTree = ""; + }; + 692AB9081E675E8800706ACC /* Products */ = { + isa = PBXGroup; + children = ( + 692AB9111E675E8800706ACC /* Telegram.app */, + 692AB9131E675E8800706ACC /* Share.appex */, + 692AB9151E675E8800706ACC /* watchkitapp.app */, + 692AB9171E675E8800706ACC /* watchkitapp Extension.appex */, + 692AB9191E675E8800706ACC /* SiriIntents.appex */, + 692AB91B1E675E8800706ACC /* LegacyDatabase.framework */, + ); + name = Products; + sourceTree = ""; + }; + 69F8422C1E67540700C110F7 = { + isa = PBXGroup; + children = ( + 692AB8861E6759BF00706ACC /* libtgvoip */, + 69F842371E67540700C110F7 /* Products */, + 692AB9061E675E8700706ACC /* Frameworks */, + ); + sourceTree = ""; + }; + 69F842371E67540700C110F7 /* Products */ = { + isa = PBXGroup; + children = ( + 69F842361E67540700C110F7 /* libtgvoip.framework */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 69F842331E67540700C110F7 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 692AB8DE1E6759DD00706ACC /* gain_control.h in Headers */, + 692AB9011E6759DD00706ACC /* threading.h in Headers */, + 692AB8EA1E6759DD00706ACC /* MediaStreamItf.h in Headers */, + 692AB8EE1E6759DD00706ACC /* OpusEncoder.h in Headers */, + 692AB8E11E6759DD00706ACC /* splitting_filter_wrapper.h in Headers */, + 692AB8CE1E6759DD00706ACC /* AudioOutput.h in Headers */, + 692AB8D91E6759DD00706ACC /* CongestionControl.h in Headers */, + 692AB8CC1E6759DD00706ACC /* AudioInput.h in Headers */, + 692AB8EC1E6759DD00706ACC /* OpusDecoder.h in Headers */, + 692AB8E01E6759DD00706ACC /* noise_suppression_x.h in Headers */, + 692AB8E81E6759DD00706ACC /* logging.h in Headers */, + 692AB8FF1E6759DD00706ACC /* TGLogWrapper.h in Headers */, + 692AB8D41E6759DD00706ACC /* BufferOutputStream.h in Headers */, + 692AB8DF1E6759DD00706ACC /* noise_suppression.h in Headers */, + 692AB9051E6759DD00706ACC /* VoIPServerConfig.h in Headers */, + 692AB9031E6759DD00706ACC /* VoIPController.h in Headers */, + 692AB8FC1E6759DD00706ACC /* AudioOutputAudioUnit.h in Headers */, + 692AB8D01E6759DD00706ACC /* BlockingQueue.h in Headers */, + 692AB8FE1E6759DD00706ACC /* AudioUnitIO.h in Headers */, + 692AB8FA1E6759DD00706ACC /* AudioInputAudioUnit.h in Headers */, + 692AB8DD1E6759DD00706ACC /* echo_control_mobile.h in Headers */, + 692AB8DB1E6759DD00706ACC /* EchoCanceller.h in Headers */, + 692AB8D61E6759DD00706ACC /* BufferPool.h in Headers */, + 692AB8E71E6759DD00706ACC /* JitterBuffer.h in Headers */, + 692AB8D21E6759DD00706ACC /* BufferInputStream.h in Headers */, + 692AB8DC1E6759DD00706ACC /* echo_cancellation.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 69F842351E67540700C110F7 /* libtgvoip */ = { + isa = PBXNativeTarget; + buildConfigurationList = 69F8423E1E67540700C110F7 /* Build configuration list for PBXNativeTarget "libtgvoip" */; + buildPhases = ( + 69F842311E67540700C110F7 /* Sources */, + 69F842321E67540700C110F7 /* Frameworks */, + 69F842331E67540700C110F7 /* Headers */, + 69F842341E67540700C110F7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libtgvoip; + productName = libtgvoip; + productReference = 69F842361E67540700C110F7 /* libtgvoip.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 69F8422D1E67540700C110F7 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0820; + ORGANIZATIONNAME = Grishka; + TargetAttributes = { + 69F842351E67540700C110F7 = { + CreatedOnToolsVersion = 8.2.1; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 69F842301E67540700C110F7 /* Build configuration list for PBXProject "libtgvoip" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 69F8422C1E67540700C110F7; + productRefGroup = 69F842371E67540700C110F7 /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 692AB9081E675E8800706ACC /* Products */; + ProjectRef = 692AB9071E675E8800706ACC /* Telegraph.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 69F842351E67540700C110F7 /* libtgvoip */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 692AB9111E675E8800706ACC /* Telegram.app */ = { + isa = PBXReferenceProxy; + fileType = wrapper.application; + path = Telegram.app; + remoteRef = 692AB9101E675E8800706ACC /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 692AB9131E675E8800706ACC /* Share.appex */ = { + isa = PBXReferenceProxy; + fileType = "wrapper.app-extension"; + path = Share.appex; + remoteRef = 692AB9121E675E8800706ACC /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 692AB9151E675E8800706ACC /* watchkitapp.app */ = { + isa = PBXReferenceProxy; + fileType = wrapper.application; + path = watchkitapp.app; + remoteRef = 692AB9141E675E8800706ACC /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 692AB9171E675E8800706ACC /* watchkitapp Extension.appex */ = { + isa = PBXReferenceProxy; + fileType = "wrapper.app-extension"; + path = "watchkitapp Extension.appex"; + remoteRef = 692AB9161E675E8800706ACC /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 692AB9191E675E8800706ACC /* SiriIntents.appex */ = { + isa = PBXReferenceProxy; + fileType = "wrapper.app-extension"; + path = SiriIntents.appex; + remoteRef = 692AB9181E675E8800706ACC /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 692AB91B1E675E8800706ACC /* LegacyDatabase.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = LegacyDatabase.framework; + remoteRef = 692AB91A1E675E8800706ACC /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 69F842341E67540700C110F7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 692AB8E51E6759DD00706ACC /* Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 69F842311E67540700C110F7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6915307B1E6B5BAB004F643F /* logging.cpp in Sources */, + 692AB9041E6759DD00706ACC /* VoIPServerConfig.cpp in Sources */, + 692AB9021E6759DD00706ACC /* VoIPController.cpp in Sources */, + 692AB8D81E6759DD00706ACC /* CongestionControl.cpp in Sources */, + 692AB8FB1E6759DD00706ACC /* AudioOutputAudioUnit.cpp in Sources */, + 692AB8EB1E6759DD00706ACC /* OpusDecoder.cpp in Sources */, + 692AB8E61E6759DD00706ACC /* JitterBuffer.cpp in Sources */, + 692AB9001E6759DD00706ACC /* TGLogWrapper.m in Sources */, + 692AB8F91E6759DD00706ACC /* AudioInputAudioUnit.cpp in Sources */, + 692AB8D11E6759DD00706ACC /* BufferInputStream.cpp in Sources */, + 692AB8E91E6759DD00706ACC /* MediaStreamItf.cpp in Sources */, + 692AB8DA1E6759DD00706ACC /* EchoCanceller.cpp in Sources */, + 692AB8D31E6759DD00706ACC /* BufferOutputStream.cpp in Sources */, + 692AB8CF1E6759DD00706ACC /* BlockingQueue.cpp in Sources */, + 692AB8D51E6759DD00706ACC /* BufferPool.cpp in Sources */, + 692AB8CB1E6759DD00706ACC /* AudioInput.cpp in Sources */, + 692AB8FD1E6759DD00706ACC /* AudioUnitIO.cpp in Sources */, + 692AB8CD1E6759DD00706ACC /* AudioOutput.cpp in Sources */, + 692AB8ED1E6759DD00706ACC /* OpusEncoder.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 69F8423C1E67540700C110F7 /* Debug */ = { + 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; + }; + 69F8423D1E67540700C110F7 /* Release */ = { + 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; + }; + 69F8423F1E67540700C110F7 /* Debug */ = { + 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 = ( + ../../Telegraph/thirdparty/opus/include/opus, + "$(inherited)", + ../../Telegraph, + ); + 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)", + "$(PROJECT_DIR)/libtgvoip/external", + "$(PROJECT_DIR)/external", + ); + MACH_O_TYPE = staticlib; + MACOSX_DEPLOYMENT_TARGET = 10.6; + OTHER_CFLAGS = ( + "-DTGVOIP_USE_AUDIO_SESSION", + "-DTGVOIP_USE_CUSTOM_CRYPTO", + ); + PRODUCT_BUNDLE_IDENTIFIER = me.grishka.libtgvoip; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 69F842401E67540700C110F7 /* Release */ = { + 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 = ( + ../../Telegraph/thirdparty/opus/include/opus, + "$(inherited)", + ../../Telegraph, + ); + 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)", + "$(PROJECT_DIR)/libtgvoip/external", + "$(PROJECT_DIR)/external", + ); + MACH_O_TYPE = staticlib; + MACOSX_DEPLOYMENT_TARGET = 10.6; + OTHER_CFLAGS = ( + "-DTGVOIP_USE_AUDIO_SESSION", + "-DTGVOIP_USE_CUSTOM_CRYPTO", + ); + PRODUCT_BUNDLE_IDENTIFIER = me.grishka.libtgvoip; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; + D04D01C31E678C0D0086DDC0 /* Debug 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; + 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 AppStore"; + }; + D04D01C41E678C0D0086DDC0 /* Debug 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 = ( + ../../Telegraph/thirdparty/opus/include/opus, + "$(inherited)", + ../../Telegraph, + ); + 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)", + "$(PROJECT_DIR)/libtgvoip/external", + "$(PROJECT_DIR)/external", + ); + MACH_O_TYPE = staticlib; + MACOSX_DEPLOYMENT_TARGET = 10.6; + OTHER_CFLAGS = ( + "-DTGVOIP_USE_AUDIO_SESSION", + "-DTGVOIP_USE_CUSTOM_CRYPTO", + ); + PRODUCT_BUNDLE_IDENTIFIER = me.grishka.libtgvoip; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = "Debug AppStore"; + }; + D04D01CB1E678C230086DDC0 /* 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 = Hockeyapp; + }; + D04D01CC1E678C230086DDC0 /* 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 = ( + ../../Telegraph/thirdparty/opus/include/opus, + "$(inherited)", + ../../Telegraph, + ); + 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)", + "$(PROJECT_DIR)/libtgvoip/external", + "$(PROJECT_DIR)/external", + ); + MACH_O_TYPE = staticlib; + MACOSX_DEPLOYMENT_TARGET = 10.6; + OTHER_CFLAGS = ( + "-DTGVOIP_USE_AUDIO_SESSION", + "-DTGVOIP_USE_CUSTOM_CRYPTO", + ); + PRODUCT_BUNDLE_IDENTIFIER = me.grishka.libtgvoip; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Hockeyapp; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 69F842301E67540700C110F7 /* Build configuration list for PBXProject "libtgvoip" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 69F8423C1E67540700C110F7 /* Debug */, + D04D01C31E678C0D0086DDC0 /* Debug AppStore */, + 69F8423D1E67540700C110F7 /* Release */, + D04D01CB1E678C230086DDC0 /* Hockeyapp */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 69F8423E1E67540700C110F7 /* Build configuration list for PBXNativeTarget "libtgvoip" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 69F8423F1E67540700C110F7 /* Debug */, + D04D01C41E678C0D0086DDC0 /* Debug AppStore */, + 69F842401E67540700C110F7 /* Release */, + D04D01CC1E678C230086DDC0 /* Hockeyapp */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 69F8422D1E67540700C110F7 /* Project object */; +} diff --git a/libtgvoip.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/libtgvoip.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..55fe874237 --- /dev/null +++ b/libtgvoip.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/libtgvoip.xcodeproj/project.xcworkspace/xcuserdata/grishka.xcuserdatad/UserInterfaceState.xcuserstate b/libtgvoip.xcodeproj/project.xcworkspace/xcuserdata/grishka.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000..24e6306be3 Binary files /dev/null and b/libtgvoip.xcodeproj/project.xcworkspace/xcuserdata/grishka.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/libtgvoip.xcodeproj/xcuserdata/grishka.xcuserdatad/xcschemes/libtgvoip.xcscheme b/libtgvoip.xcodeproj/xcuserdata/grishka.xcuserdatad/xcschemes/libtgvoip.xcscheme new file mode 100644 index 0000000000..a24e940e34 --- /dev/null +++ b/libtgvoip.xcodeproj/xcuserdata/grishka.xcuserdatad/xcschemes/libtgvoip.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libtgvoip.xcodeproj/xcuserdata/grishka.xcuserdatad/xcschemes/xcschememanagement.plist b/libtgvoip.xcodeproj/xcuserdata/grishka.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000000..253fba52b6 --- /dev/null +++ b/libtgvoip.xcodeproj/xcuserdata/grishka.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + libtgvoip.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + 69F842351E67540700C110F7 + + primary + + + + + diff --git a/libtgvoip.xcodeproj/xcuserdata/peter.xcuserdatad/xcschemes/xcschememanagement.plist b/libtgvoip.xcodeproj/xcuserdata/peter.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000000..3572d3d9ca --- /dev/null +++ b/libtgvoip.xcodeproj/xcuserdata/peter.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SuppressBuildableAutocreation + + 69F842351E67540700C110F7 + + primary + + + + + diff --git a/logging.cpp b/logging.cpp new file mode 100644 index 0000000000..fe2e42071c --- /dev/null +++ b/logging.cpp @@ -0,0 +1,25 @@ +// +// libtgvoip is free and unencumbered public domain software. +// For more information, see http://unlicense.org or the UNLICENSE file +// you should have received with this source code distribution. +// + + +#include +#include +#include + +FILE* tgvoipLogFile=NULL; + +void tgvoip_log_file_printf(char level, const char* msg, ...){ + if(tgvoipLogFile){ + va_list argptr; + va_start(argptr, msg); + time_t t = time(0); + struct tm *now = localtime(&t); + fprintf(tgvoipLogFile, "%02d-%02d %02d:%02d:%02d %c: ", now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec, level); + vfprintf(tgvoipLogFile, msg, argptr); + fprintf(tgvoipLogFile, "\n"); + fflush(tgvoipLogFile); + } +} diff --git a/logging.h b/logging.h index 0cb5b90ddb..4c807e09f0 100644 --- a/logging.h +++ b/logging.h @@ -6,10 +6,15 @@ #ifndef __LOGGING_H #define __LOGGING_H - #define LSTR_INT(x) LSTR_DO_INT(x) #define LSTR_DO_INT(x) #x +#ifdef __APPLE__ +#include +#endif + +void tgvoip_log_file_printf(char level, const char* msg, ...); + #if defined(__ANDROID__) #include @@ -17,11 +22,11 @@ //#define _LOG_WRAP(...) __BASE_FILE__":"LSTR_INT(__LINE__)": "__VA_ARGS__ #define _LOG_WRAP(...) __VA_ARGS__ #define TAG "tg-voip-native" -#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, _LOG_WRAP(__VA_ARGS__)) -#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, _LOG_WRAP(__VA_ARGS__)) -#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, _LOG_WRAP(__VA_ARGS__)) -#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, _LOG_WRAP(__VA_ARGS__)) -#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, _LOG_WRAP(__VA_ARGS__)) +#define LOGV(...) {__android_log_print(ANDROID_LOG_VERBOSE, TAG, _LOG_WRAP(__VA_ARGS__)); tgvoip_log_file_printf('V', __VA_ARGS__);} +#define LOGD(...) {__android_log_print(ANDROID_LOG_DEBUG, TAG, _LOG_WRAP(__VA_ARGS__)); tgvoip_log_file_printf('D', __VA_ARGS__);} +#define LOGI(...) {__android_log_print(ANDROID_LOG_INFO, TAG, _LOG_WRAP(__VA_ARGS__)); tgvoip_log_file_printf('I', __VA_ARGS__);} +#define LOGW(...) {__android_log_print(ANDROID_LOG_WARN, TAG, _LOG_WRAP(__VA_ARGS__)); tgvoip_log_file_printf('W', __VA_ARGS__);} +#define LOGE(...) {__android_log_print(ANDROID_LOG_ERROR, TAG, _LOG_WRAP(__VA_ARGS__)); tgvoip_log_file_printf('E', __VA_ARGS__);} #elif defined(__APPLE__) && TARGET_OS_IPHONE diff --git a/os/android/AudioInputAndroid.cpp b/os/android/AudioInputAndroid.cpp index 9674c0284b..2110da6cec 100644 --- a/os/android/AudioInputAndroid.cpp +++ b/os/android/AudioInputAndroid.cpp @@ -10,11 +10,11 @@ extern JavaVM* sharedJVM; -jmethodID CAudioInputAndroid::initMethod; -jmethodID CAudioInputAndroid::releaseMethod; -jmethodID CAudioInputAndroid::startMethod; -jmethodID CAudioInputAndroid::stopMethod; -jclass CAudioInputAndroid::jniClass; +jmethodID CAudioInputAndroid::initMethod=NULL; +jmethodID CAudioInputAndroid::releaseMethod=NULL; +jmethodID CAudioInputAndroid::startMethod=NULL; +jmethodID CAudioInputAndroid::stopMethod=NULL; +jclass CAudioInputAndroid::jniClass=NULL; CAudioInputAndroid::CAudioInputAndroid(){ JNIEnv* env=NULL; @@ -33,27 +33,33 @@ CAudioInputAndroid::CAudioInputAndroid(){ sharedJVM->DetachCurrentThread(); } running=false; + init_mutex(mutex); } CAudioInputAndroid::~CAudioInputAndroid(){ - JNIEnv* env=NULL; - bool didAttach=false; - sharedJVM->GetEnv((void**) &env, JNI_VERSION_1_6); - if(!env){ - sharedJVM->AttachCurrentThread(&env, NULL); - didAttach=true; - } + { + CMutexGuard guard(mutex); + JNIEnv *env=NULL; + bool didAttach=false; + sharedJVM->GetEnv((void **) &env, JNI_VERSION_1_6); + if(!env){ + sharedJVM->AttachCurrentThread(&env, NULL); + didAttach=true; + } - env->CallVoidMethod(javaObject, releaseMethod); - env->DeleteGlobalRef(javaObject); - javaObject=NULL; + env->CallVoidMethod(javaObject, releaseMethod); + env->DeleteGlobalRef(javaObject); + javaObject=NULL; - if(didAttach){ - sharedJVM->DetachCurrentThread(); + if(didAttach){ + sharedJVM->DetachCurrentThread(); + } } + free_mutex(mutex); } void CAudioInputAndroid::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){ + CMutexGuard guard(mutex); JNIEnv* env=NULL; bool didAttach=false; sharedJVM->GetEnv((void**) &env, JNI_VERSION_1_6); @@ -70,6 +76,7 @@ void CAudioInputAndroid::Configure(uint32_t sampleRate, uint32_t bitsPerSample, } void CAudioInputAndroid::Start(){ + CMutexGuard guard(mutex); JNIEnv* env=NULL; bool didAttach=false; sharedJVM->GetEnv((void**) &env, JNI_VERSION_1_6); @@ -78,7 +85,7 @@ void CAudioInputAndroid::Start(){ didAttach=true; } - env->CallVoidMethod(javaObject, startMethod); + failed=!env->CallBooleanMethod(javaObject, startMethod); if(didAttach){ sharedJVM->DetachCurrentThread(); @@ -87,6 +94,7 @@ void CAudioInputAndroid::Start(){ } void CAudioInputAndroid::Stop(){ + CMutexGuard guard(mutex); running=false; JNIEnv* env=NULL; bool didAttach=false; diff --git a/os/android/AudioInputAndroid.h b/os/android/AudioInputAndroid.h index 8911b799a4..62af95c348 100644 --- a/os/android/AudioInputAndroid.h +++ b/os/android/AudioInputAndroid.h @@ -9,6 +9,7 @@ #include #include "../../audio/AudioInput.h" +#include "../../threading.h" class CAudioInputAndroid : public CAudioInput{ @@ -28,6 +29,7 @@ public: private: jobject javaObject; bool running; + tgvoip_mutex_t mutex; }; diff --git a/os/android/AudioOutputAndroid.cpp b/os/android/AudioOutputAndroid.cpp index edffe5429e..d047806b6c 100644 --- a/os/android/AudioOutputAndroid.cpp +++ b/os/android/AudioOutputAndroid.cpp @@ -10,11 +10,11 @@ extern JavaVM* sharedJVM; -jmethodID CAudioOutputAndroid::initMethod; -jmethodID CAudioOutputAndroid::releaseMethod; -jmethodID CAudioOutputAndroid::startMethod; -jmethodID CAudioOutputAndroid::stopMethod; -jclass CAudioOutputAndroid::jniClass; +jmethodID CAudioOutputAndroid::initMethod=NULL; +jmethodID CAudioOutputAndroid::releaseMethod=NULL; +jmethodID CAudioOutputAndroid::startMethod=NULL; +jmethodID CAudioOutputAndroid::stopMethod=NULL; +jclass CAudioOutputAndroid::jniClass=NULL; CAudioOutputAndroid::CAudioOutputAndroid(){ JNIEnv* env=NULL; diff --git a/os/darwin/AudioUnitIO.cpp b/os/darwin/AudioUnitIO.cpp index 1338e80937..8d80ec722a 100644 --- a/os/darwin/AudioUnitIO.cpp +++ b/os/darwin/AudioUnitIO.cpp @@ -9,6 +9,7 @@ #include "AudioInputAudioUnit.h" #include "AudioOutputAudioUnit.h" #include "../../logging.h" +#include "../../VoIPController.h" #define CHECK_AU_ERROR(res, msg) if(res!=noErr){ LOGE(msg": OSStatus=%d", (int)res); return; } #define BUFFER_SIZE 960 // 20 ms @@ -18,8 +19,34 @@ int CAudioUnitIO::refCount=0; CAudioUnitIO* CAudioUnitIO::sharedInstance=NULL; +bool CAudioUnitIO::haveAudioSession=false; CAudioUnitIO::CAudioUnitIO(){ + input=NULL; + output=NULL; + configured=false; + inputEnabled=false; + outputEnabled=false; + inBufferList.mBuffers[0].mData=malloc(10240); + inBufferList.mBuffers[0].mDataByteSize=10240; + inBufferList.mNumberBuffers=1; + if(haveAudioSession) + ProcessAudioSessionAcquired(); +} + +CAudioUnitIO::~CAudioUnitIO(){ + if(runFakeIO){ + runFakeIO=false; + join_thread(fakeIOThread); + } + AudioOutputUnitStop(unit); + AudioUnitUninitialize(unit); + AudioComponentInstanceDispose(unit); + free(inBufferList.mBuffers[0].mData); + haveAudioSession=false; +} + +void CAudioUnitIO::ProcessAudioSessionAcquired(){ OSStatus status; AudioComponentDescription desc; AudioComponent inputComponent; @@ -36,21 +63,8 @@ CAudioUnitIO::CAudioUnitIO(){ inputComponent = AudioComponentFindNext(NULL, &desc); status = AudioComponentInstanceNew(inputComponent, &unit); - input=NULL; - output=NULL; - configured=false; - inputEnabled=false; - outputEnabled=false; - inBufferList.mBuffers[0].mData=malloc(10240); - inBufferList.mBuffers[0].mDataByteSize=10240; - inBufferList.mNumberBuffers=1; -} - -CAudioUnitIO::~CAudioUnitIO(){ - AudioOutputUnitStop(unit); - AudioUnitUninitialize(unit); - AudioComponentInstanceDispose(unit); - free(inBufferList.mBuffers[0].mData); + if(configured) + ActuallyConfigure(cfgSampleRate, cfgBitsPerSample, cfgChannels); } CAudioUnitIO* CAudioUnitIO::Get(){ @@ -71,26 +85,42 @@ void CAudioUnitIO::Release(){ } } +void CAudioUnitIO::AudioSessionAcquired(){ + haveAudioSession=true; + if(sharedInstance) + sharedInstance->ProcessAudioSessionAcquired(); +} + void CAudioUnitIO::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){ if(configured) return; + + runFakeIO=true; + start_thread(fakeIOThread, CAudioUnitIO::StartFakeIOThread, this); + set_thread_priority(fakeIOThread, get_thread_max_priority()); + + if(haveAudioSession){ + ActuallyConfigure(sampleRate, bitsPerSample, channels); + }else{ + cfgSampleRate=sampleRate; + cfgBitsPerSample=bitsPerSample; + cfgChannels=channels; + } + + configured=true; +} + +void CAudioUnitIO::ActuallyConfigure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){ UInt32 flag=1; OSStatus status = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag)); CHECK_AU_ERROR(status, "Error enabling AudioUnit output"); status = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag)); CHECK_AU_ERROR(status, "Error enabling AudioUnit input"); - flag=1; + flag=0; status=AudioUnitSetProperty(unit, kAUVoiceIOProperty_VoiceProcessingEnableAGC, kAudioUnitScope_Global, kInputBus, &flag, sizeof(flag)); CHECK_AU_ERROR(status, "Error disabling AGC"); - /*AudioStreamBasicDescription nativeInFormat, nativeOutFormat; - UInt32 size=sizeof(AudioStreamBasicDescription); - AudioUnitGetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &nativeInFormat, &size); - size=sizeof(AudioStreamBasicDescription); - AudioUnitGetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &nativeOutFormat, &size); - LOGI("native in format: %d hz, flags %08X, %d channels, %d bits", (int)nativeInFormat.mSampleRate, nativeInFormat.mFormatFlags, nativeInFormat.mChannelsPerFrame, nativeInFormat.mBitsPerChannel); - LOGI("native out format: %d hz, flags %08X, %d channels, %d bits", (int)nativeOutFormat.mSampleRate, nativeOutFormat.mFormatFlags, nativeOutFormat.mChannelsPerFrame, nativeOutFormat.mBitsPerChannel);*/ Float64 nativeSampleRate; UInt32 size=sizeof(Float64); status=AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate, &size, &nativeSampleRate); @@ -123,8 +153,6 @@ void CAudioUnitIO::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32 CHECK_AU_ERROR(status, "Error initializing AudioUnit"); status=AudioOutputUnitStart(unit); CHECK_AU_ERROR(status, "Error starting AudioUnit"); - - configured=true; } OSStatus CAudioUnitIO::BufferCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData){ @@ -133,6 +161,7 @@ OSStatus CAudioUnitIO::BufferCallback(void *inRefCon, AudioUnitRenderActionFlags } void CAudioUnitIO::BufferCallback(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 bus, UInt32 numFrames, AudioBufferList *ioData){ + runFakeIO=false; if(bus==kOutputBus){ if(output && outputEnabled){ output->HandleBufferCallback(ioData); @@ -178,3 +207,34 @@ void CAudioUnitIO::EnableOutput(bool enabled){ outputEnabled=enabled; } +void* CAudioUnitIO::StartFakeIOThread(void *arg){ + ((CAudioUnitIO*)arg)->RunFakeIOThread(); + return NULL; +} + +void CAudioUnitIO::RunFakeIOThread(){ + double neededDataDuration=0; + double prevTime=CVoIPController::GetCurrentTime(); + while(runFakeIO){ + double t=CVoIPController::GetCurrentTime(); + neededDataDuration+=t-prevTime; + prevTime=t; + while(neededDataDuration>=0.020){ + if(output && outputEnabled){ + inBufferList.mBuffers[0].mDataByteSize=960*2; + output->HandleBufferCallback(&inBufferList); + } + if((output && outputEnabled) || (input && inputEnabled)){ + memset(inBufferList.mBuffers[0].mData, 0, 960*2); + } + if(input && inputEnabled){ + inBufferList.mBuffers[0].mDataByteSize=960*2; + input->HandleBufferCallback(&inBufferList); + } + neededDataDuration-=0.020; + } + usleep(5000); + } + LOGD("FakeIO thread exiting"); +} + diff --git a/os/darwin/AudioUnitIO.h b/os/darwin/AudioUnitIO.h index f1ffff2a60..e5d5c7a7d2 100644 --- a/os/darwin/AudioUnitIO.h +++ b/os/darwin/AudioUnitIO.h @@ -8,6 +8,7 @@ #define LIBTGVOIP_AUDIOUNITIO_H #include +#include "../../threading.h" class CAudioInputAudioUnit; class CAudioOutputAudioUnit; @@ -25,10 +26,16 @@ public: void EnableOutput(bool enabled); static CAudioUnitIO* Get(); static void Release(); + static void* StartFakeIOThread(void* arg); + static void AudioSessionAcquired(); private: static OSStatus BufferCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData); void BufferCallback(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 bus, UInt32 numFrames, AudioBufferList* ioData); + void RunFakeIOThread(); + void Init(); + void ActuallyConfigure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels); + void ProcessAudioSessionAcquired(); AudioComponentInstance unit; CAudioInputAudioUnit* input; CAudioOutputAudioUnit* output; @@ -36,8 +43,14 @@ private: bool configured; bool inputEnabled; bool outputEnabled; + bool runFakeIO; + uint32_t cfgSampleRate; + uint32_t cfgBitsPerSample; + uint32_t cfgChannels; + tgvoip_thread_t fakeIOThread; static int refCount; static CAudioUnitIO* sharedInstance; + static bool haveAudioSession; }; #endif /* LIBTGVOIP_AUDIOUNITIO_H */ diff --git a/os/darwin/TGLogWrapper.m b/os/darwin/TGLogWrapper.m index f0da01dac5..4dbf5d1bfd 100644 --- a/os/darwin/TGLogWrapper.m +++ b/os/darwin/TGLogWrapper.m @@ -1,8 +1,10 @@ #import +#import "ASCommon.h" + void __tgvoip_call_tglog(char* format, ...){ va_list args; va_start(args, format); - TGLog([[[NSString alloc] initWithFormat:[NSString stringWithCString:format encoding:NSUTF8StringEncoding] arguments:args] stringByReplacingOccurrencesOfString:@"%" withString:@"%%"]); + TGLogv([[NSString alloc]initWithCString:format], args); va_end(args); -} \ No newline at end of file +} diff --git a/threading.h b/threading.h index 0c61ef16b8..b568ced908 100644 --- a/threading.h +++ b/threading.h @@ -39,4 +39,16 @@ typedef pthread_cond_t tgvoip_lock_t; #error "No threading implementation for your operating system" #endif +class CMutexGuard{ +public: + CMutexGuard(tgvoip_mutex_t &mutex) : mutex(mutex) { + lock_mutex(mutex); + } + ~CMutexGuard(){ + unlock_mutex(mutex); + } +private: + tgvoip_mutex_t &mutex; +}; + #endif //__THREADING_H