git subrepo clone https://github.com/telegramdesktop/libtgvoip Telegram/ThirdParty/libtgvoip

subrepo:
  subdir:   "Telegram/ThirdParty/libtgvoip"
  merged:   "651594b3"
upstream:
  origin:   "https://github.com/telegramdesktop/libtgvoip"
  branch:   "tdesktop"
  commit:   "651594b3"
git-subrepo:
  version:  "0.3.1"
  origin:   "???"
  commit:   "???"
This commit is contained in:
Berkus Decker 2017-11-22 03:52:55 +02:00 committed by Berkus Decker
parent 32e988d251
commit 3062a8ef99
244 changed files with 55588 additions and 4 deletions

3
.gitmodules vendored
View File

@ -1,6 +1,3 @@
[submodule "Telegram/ThirdParty/libtgvoip"]
path = Telegram/ThirdParty/libtgvoip
url = https://github.com/telegramdesktop/libtgvoip
[submodule "Telegram/ThirdParty/variant"]
path = Telegram/ThirdParty/variant
url = https://github.com/mapbox/variant

@ -1 +0,0 @@
Subproject commit 9f78cb85e5db9b39a6c4c3f7847ea3e98c9f544a

View File

@ -0,0 +1,21 @@
bin
.idea
build
*/Debug/*
*/Release/*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
[Pp]review/
[Pp]roduction/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
# Visual Studio 2015 cache/options directory
.vs/

11
Telegram/ThirdParty/libtgvoip/.gitrepo vendored Normal file
View File

@ -0,0 +1,11 @@
; DO NOT EDIT (unless you know what you are doing)
;
; This subdirectory is a git "subrepo", and this file is maintained by the
; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
;
[subrepo]
remote = https://github.com/telegramdesktop/libtgvoip
branch = tdesktop
commit = 651594b3cef23051ca7370ddd14d7c23377d6341
parent = 5e31fcb2406d6d8ce1d6d61199bacbc06014c2d3
cmdver = 0.3.1

View File

@ -0,0 +1,58 @@
LOCAL_MODULE := WebRtcAec
LOCAL_SRC_FILES := ./libtgvoip/external/libWebRtcAec_android_$(TARGET_ARCH_ABI).a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := voip
LOCAL_CPPFLAGS := -Wall -std=c++11 -DANDROID -finline-functions -ffast-math -Os -fno-strict-aliasing -O3
LOCAL_CFLAGS := -O3 -DUSE_KISS_FFT -fexceptions
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
# LOCAL_CPPFLAGS += -mfloat-abi=softfp -mfpu=neon
# LOCAL_CFLAGS += -mfloat-abi=softfp -mfpu=neon -DFLOATING_POINT
# LOCAL_ARM_NEON := true
else
LOCAL_CFLAGS += -DFIXED_POINT
ifeq ($(TARGET_ARCH_ABI),armeabi)
# LOCAL_CPPFLAGS += -mfloat-abi=softfp -mfpu=neon
# LOCAL_CFLAGS += -mfloat-abi=softfp -mfpu=neon
else
ifeq ($(TARGET_ARCH_ABI),x86)
endif
endif
endif
MY_DIR := libtgvoip
LOCAL_C_INCLUDES := jni/opus/include jni/boringssl/include/
LOCAL_SRC_FILES := \
./libtgvoip/logging.cpp \
./libtgvoip/VoIPController.cpp \
./libtgvoip/BufferInputStream.cpp \
./libtgvoip/BufferOutputStream.cpp \
./libtgvoip/BlockingQueue.cpp \
./libtgvoip/audio/AudioInput.cpp \
./libtgvoip/os/android/AudioInputOpenSLES.cpp \
./libtgvoip/MediaStreamItf.cpp \
./libtgvoip/audio/AudioOutput.cpp \
./libtgvoip/OpusEncoder.cpp \
./libtgvoip/os/android/AudioOutputOpenSLES.cpp \
./libtgvoip/JitterBuffer.cpp \
./libtgvoip/OpusDecoder.cpp \
./libtgvoip/BufferPool.cpp \
./libtgvoip/os/android/OpenSLEngineWrapper.cpp \
./libtgvoip/os/android/AudioInputAndroid.cpp \
./libtgvoip/os/android/AudioOutputAndroid.cpp \
./libtgvoip/EchoCanceller.cpp \
./libtgvoip/CongestionControl.cpp \
./libtgvoip/VoIPServerConfig.cpp \
./libtgvoip/NetworkSocket.cpp
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)

View File

@ -0,0 +1,10 @@
//
// 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 "BlockingQueue.h"
using namespace tgvoip;

View File

@ -0,0 +1,94 @@
//
// 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 LIBTGVOIP_BLOCKINGQUEUE_H
#define LIBTGVOIP_BLOCKINGQUEUE_H
#include <stdlib.h>
#include <list>
#include "threading.h"
using namespace std;
namespace tgvoip{
template<typename T>
class BlockingQueue{
public:
BlockingQueue(size_t capacity) : semaphore(capacity, 0){
this->capacity=capacity;
overflowCallback=NULL;
init_mutex(mutex);
};
~BlockingQueue(){
semaphore.Release();
free_mutex(mutex);
}
void Put(T thing){
MutexGuard sync(mutex);
queue.push_back(thing);
bool didOverflow=false;
while(queue.size()>capacity){
didOverflow=true;
if(overflowCallback){
overflowCallback(queue.front());
queue.pop_front();
}else{
abort();
}
}
if(!didOverflow)
semaphore.Release();
}
T GetBlocking(){
semaphore.Acquire();
MutexGuard sync(mutex);
T r=GetInternal();
return r;
}
T Get(){
MutexGuard sync(mutex);
if(queue.size()>0)
semaphore.Acquire();
T r=GetInternal();
return r;
}
unsigned int Size(){
return queue.size();
}
void PrepareDealloc(){
}
void SetOverflowCallback(void (*overflowCallback)(T)){
this->overflowCallback=overflowCallback;
}
private:
T GetInternal(){
//if(queue.size()==0)
// return NULL;
T r=queue.front();
queue.pop_front();
return r;
}
list<T> queue;
size_t capacity;
//tgvoip_lock_t lock;
Semaphore semaphore;
tgvoip_mutex_t mutex;
void (*overflowCallback)(T);
};
}
#endif //LIBTGVOIP_BLOCKINGQUEUE_H

View File

@ -0,0 +1,106 @@
//
// 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 "BufferInputStream.h"
#include <assert.h>
#include <string.h>
#include <exception>
#include <stdexcept>
using namespace tgvoip;
BufferInputStream::BufferInputStream(unsigned char* data, size_t length){
this->buffer=data;
this->length=length;
offset=0;
}
BufferInputStream::~BufferInputStream(){
}
void BufferInputStream::Seek(size_t offset){
if(offset>length){
throw std::out_of_range("Not enough bytes in buffer");
}
this->offset=offset;
}
size_t BufferInputStream::GetLength(){
return length;
}
size_t BufferInputStream::GetOffset(){
return offset;
}
size_t BufferInputStream::Remaining(){
return length-offset;
}
unsigned char BufferInputStream::ReadByte(){
EnsureEnoughRemaining(1);
return (unsigned char)buffer[offset++];
}
int32_t BufferInputStream::ReadInt32(){
EnsureEnoughRemaining(4);
int32_t res=((int32_t)buffer[offset] & 0xFF) |
(((int32_t)buffer[offset+1] & 0xFF) << 8) |
(((int32_t)buffer[offset+2] & 0xFF) << 16) |
(((int32_t)buffer[offset+3] & 0xFF) << 24);
offset+=4;
return res;
}
int64_t BufferInputStream::ReadInt64(){
EnsureEnoughRemaining(8);
int64_t res=((int64_t)buffer[offset] & 0xFF) |
(((int64_t)buffer[offset+1] & 0xFF) << 8) |
(((int64_t)buffer[offset+2] & 0xFF) << 16) |
(((int64_t)buffer[offset+3] & 0xFF) << 24) |
(((int64_t)buffer[offset+4] & 0xFF) << 32) |
(((int64_t)buffer[offset+5] & 0xFF) << 40) |
(((int64_t)buffer[offset+6] & 0xFF) << 48) |
(((int64_t)buffer[offset+7] & 0xFF) << 56);
offset+=8;
return res;
}
int16_t BufferInputStream::ReadInt16(){
EnsureEnoughRemaining(2);
int16_t res=(uint16_t)buffer[offset] | ((uint16_t)buffer[offset+1] << 8);
offset+=2;
return res;
}
int32_t BufferInputStream::ReadTlLength(){
unsigned char l=ReadByte();
if(l<254)
return l;
assert(length-offset>=3);
EnsureEnoughRemaining(3);
int32_t res=((int32_t)buffer[offset] & 0xFF) |
(((int32_t)buffer[offset+1] & 0xFF) << 8) |
(((int32_t)buffer[offset+2] & 0xFF) << 16);
offset+=3;
return res;
}
void BufferInputStream::ReadBytes(unsigned char *to, size_t count){
EnsureEnoughRemaining(count);
memcpy(to, buffer+offset, count);
offset+=count;
}
void BufferInputStream::EnsureEnoughRemaining(size_t need){
if(length-offset<need){
throw std::out_of_range("Not enough bytes in buffer");
}
}

View File

@ -0,0 +1,38 @@
//
// 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 LIBTGVOIP_BUFFERINPUTSTREAM_H
#define LIBTGVOIP_BUFFERINPUTSTREAM_H
#include <stdio.h>
#include <stdint.h>
namespace tgvoip{
class BufferInputStream{
public:
BufferInputStream(unsigned char* data, size_t length);
~BufferInputStream();
void Seek(size_t offset);
size_t GetLength();
size_t GetOffset();
size_t Remaining();
unsigned char ReadByte();
int64_t ReadInt64();
int32_t ReadInt32();
int16_t ReadInt16();
int32_t ReadTlLength();
void ReadBytes(unsigned char* to, size_t count);
private:
void EnsureEnoughRemaining(size_t need);
unsigned char* buffer;
size_t length;
size_t offset;
};
}
#endif //LIBTGVOIP_BUFFERINPUTSTREAM_H

View File

@ -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 "BufferOutputStream.h"
#include <stdexcept>
#include <string.h>
using namespace tgvoip;
BufferOutputStream::BufferOutputStream(size_t size){
buffer=(unsigned char*) malloc(size);
offset=0;
this->size=size;
bufferProvided=false;
}
BufferOutputStream::BufferOutputStream(unsigned char *buffer, size_t size){
this->buffer=buffer;
this->size=size;
offset=0;
bufferProvided=true;
}
BufferOutputStream::~BufferOutputStream(){
if(!bufferProvided)
free(buffer);
}
void BufferOutputStream::WriteByte(unsigned char byte){
this->ExpandBufferIfNeeded(1);
buffer[offset++]=byte;
}
void BufferOutputStream::WriteInt32(int32_t i){
this->ExpandBufferIfNeeded(4);
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 BufferOutputStream::WriteInt64(int64_t i){
this->ExpandBufferIfNeeded(8);
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 BufferOutputStream::WriteInt16(int16_t i){
this->ExpandBufferIfNeeded(2);
buffer[offset+1]=(unsigned char)((i >> 8) & 0xFF);
buffer[offset]=(unsigned char)(i & 0xFF);
offset+=2;
}
void BufferOutputStream::WriteBytes(unsigned char *bytes, size_t count){
this->ExpandBufferIfNeeded(count);
memcpy(buffer+offset, bytes, count);
offset+=count;
}
unsigned char *BufferOutputStream::GetBuffer(){
return buffer;
}
size_t BufferOutputStream::GetLength(){
return offset;
}
void BufferOutputStream::ExpandBufferIfNeeded(size_t need){
if(offset+need>size){
if(bufferProvided){
throw std::out_of_range("buffer overflow");
}
if(need<1024){
buffer=(unsigned char *) realloc(buffer, size+1024);
size+=1024;
}else{
buffer=(unsigned char *) realloc(buffer, size+need);
size+=need;
}
}
}
void BufferOutputStream::Reset(){
offset=0;
}

View File

@ -0,0 +1,38 @@
//
// 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 LIBTGVOIP_BUFFEROUTPUTSTREAM_H
#define LIBTGVOIP_BUFFEROUTPUTSTREAM_H
#include <stdlib.h>
#include <stdint.h>
namespace tgvoip{
class BufferOutputStream{
public:
BufferOutputStream(size_t size);
BufferOutputStream(unsigned char* buffer, size_t size);
~BufferOutputStream();
void WriteByte(unsigned char byte);
void WriteInt64(int64_t i);
void WriteInt32(int32_t i);
void WriteInt16(int16_t i);
void WriteBytes(unsigned char* bytes, size_t count);
unsigned char* GetBuffer();
size_t GetLength();
void Reset();
private:
void ExpandBufferIfNeeded(size_t need);
unsigned char* buffer;
size_t size;
size_t offset;
bool bufferProvided;
};
}
#endif //LIBTGVOIP_BUFFEROUTPUTSTREAM_H

View File

@ -0,0 +1,66 @@
//
// 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 "BufferPool.h"
#include "logging.h"
#include <stdlib.h>
#include <assert.h>
using namespace tgvoip;
BufferPool::BufferPool(unsigned int size, unsigned int count){
assert(count<=64);
init_mutex(mutex);
buffers[0]=(unsigned char*) malloc(size*count);
bufferCount=count;
int i;
for(i=1;i<count;i++){
buffers[i]=buffers[0]+i*size;
}
usedBuffers=0;
this->size=size;
}
BufferPool::~BufferPool(){
free_mutex(mutex);
free(buffers[0]);
}
unsigned char* BufferPool::Get(){
lock_mutex(mutex);
int i;
for(i=0;i<bufferCount;i++){
if(!((usedBuffers >> i) & 1)){
usedBuffers|=(1LL << i);
unlock_mutex(mutex);
return buffers[i];
}
}
unlock_mutex(mutex);
return NULL;
}
void BufferPool::Reuse(unsigned char* buffer){
lock_mutex(mutex);
int i;
for(i=0;i<bufferCount;i++){
if(buffers[i]==buffer){
usedBuffers&= ~(1LL << i);
unlock_mutex(mutex);
return;
}
}
LOGE("pointer passed isn't a valid buffer from this pool");
abort();
}
size_t BufferPool::GetSingleBufferSize(){
return size;
}
size_t BufferPool::GetBufferCount(){
return (size_t) bufferCount;
}

View File

@ -0,0 +1,32 @@
//
// 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 LIBTGVOIP_BUFFERPOOL_H
#define LIBTGVOIP_BUFFERPOOL_H
#include <stdint.h>
#include "threading.h"
namespace tgvoip{
class BufferPool{
public:
BufferPool(unsigned int size, unsigned int count);
~BufferPool();
unsigned char* Get();
void Reuse(unsigned char* buffer);
size_t GetSingleBufferSize();
size_t GetBufferCount();
private:
uint64_t usedBuffers;
int bufferCount;
size_t size;
unsigned char* buffers[64];
tgvoip_mutex_t mutex;
};
}
#endif //LIBTGVOIP_BUFFERPOOL_H

View File

@ -0,0 +1,173 @@
//
// 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 "CongestionControl.h"
#include "VoIPController.h"
#include "logging.h"
#include "VoIPServerConfig.h"
#include <math.h>
#include <assert.h>
using namespace tgvoip;
CongestionControl::CongestionControl(){
memset(rttHistory, 0, sizeof(rttHistory));
memset(inflightPackets, 0, sizeof(inflightPackets));
memset(inflightHistory, 0, sizeof(inflightHistory));
tmpRtt=0;
tmpRttCount=0;
rttHistorySize=0;
rttHistoryTop=0;
lastSentSeq=0;
inflightHistoryTop=0;
state=TGVOIP_CONCTL_STARTUP;
lastActionTime=0;
lastActionRtt=0;
stateTransitionTime=0;
inflightDataSize=0;
lossCount=0;
cwnd=(size_t) ServerConfig::GetSharedInstance()->GetInt("audio_congestion_window", 1024);
init_mutex(mutex);
}
CongestionControl::~CongestionControl(){
free_mutex(mutex);
}
size_t CongestionControl::GetAcknowledgedDataSize(){
return 0;
}
double CongestionControl::GetAverageRTT(){
if(rttHistorySize==0)
return 0;
double avg=0;
int i;
for(i=0;i<30 && i<rttHistorySize;i++){
int x=(rttHistoryTop-i-1)%100;
avg+=rttHistory[x>=0 ? x : (100+x)];
//LOGV("adding [%d] %f", x>=0 ? x : (100+x), rttHistory[x>=0 ? x : (100+x)]);
}
return avg/i;
}
size_t CongestionControl::GetInflightDataSize(){
size_t avg=0;
int i;
for(i=0;i<30;i++){
avg+=inflightHistory[i];
}
return avg/30;
}
size_t CongestionControl::GetCongestionWindow(){
return cwnd;
}
double CongestionControl::GetMinimumRTT(){
int i;
double min=INFINITY;
for(i=0;i<100;i++){
if(rttHistory[i]>0 && rttHistory[i]<min)
min=rttHistory[i];
}
return min;
}
void CongestionControl::PacketAcknowledged(uint32_t seq){
MutexGuard sync(mutex);
int i;
for(i=0;i<100;i++){
if(inflightPackets[i].seq==seq && inflightPackets[i].sendTime>0){
tmpRtt+=(VoIPController::GetCurrentTime()-inflightPackets[i].sendTime);
tmpRttCount++;
inflightPackets[i].sendTime=0;
inflightDataSize-=inflightPackets[i].size;
break;
}
}
}
void CongestionControl::PacketSent(uint32_t seq, size_t size){
if(!seqgt(seq, lastSentSeq) || seq==lastSentSeq){
LOGW("Duplicate outgoing seq %u", seq);
return;
}
lastSentSeq=seq;
MutexGuard sync(mutex);
double smallestSendTime=INFINITY;
tgvoip_congestionctl_packet_t* slot=NULL;
int i;
for(i=0;i<100;i++){
if(inflightPackets[i].sendTime==0){
slot=&inflightPackets[i];
break;
}
if(smallestSendTime>inflightPackets[i].sendTime){
slot=&inflightPackets[i];
smallestSendTime=slot->sendTime;
}
}
assert(slot!=NULL);
if(slot->sendTime>0){
inflightDataSize-=slot->size;
lossCount++;
LOGD("Packet with seq %u was not acknowledged", slot->seq);
}
slot->seq=seq;
slot->size=size;
slot->sendTime=VoIPController::GetCurrentTime();
inflightDataSize+=size;
}
void CongestionControl::Tick(){
tickCount++;
MutexGuard sync(mutex);
if(tmpRttCount>0){
rttHistory[rttHistoryTop]=tmpRtt/tmpRttCount;
rttHistoryTop=(rttHistoryTop+1)%100;
if(rttHistorySize<100)
rttHistorySize++;
tmpRtt=0;
tmpRttCount=0;
}
int i;
for(i=0;i<100;i++){
if(inflightPackets[i].sendTime!=0 && VoIPController::GetCurrentTime()-inflightPackets[i].sendTime>2){
inflightPackets[i].sendTime=0;
inflightDataSize-=inflightPackets[i].size;
lossCount++;
LOGD("Packet with seq %u was not acknowledged", inflightPackets[i].seq);
}
}
inflightHistory[inflightHistoryTop]=inflightDataSize;
inflightHistoryTop=(inflightHistoryTop+1)%30;
}
int CongestionControl::GetBandwidthControlAction(){
if(VoIPController::GetCurrentTime()-lastActionTime<1)
return TGVOIP_CONCTL_ACT_NONE;
size_t inflightAvg=GetInflightDataSize();
size_t max=cwnd+cwnd/10;
size_t min=cwnd-cwnd/10;
if(inflightAvg<min){
lastActionTime=VoIPController::GetCurrentTime();
return TGVOIP_CONCTL_ACT_INCREASE;
}
if(inflightAvg>max){
lastActionTime=VoIPController::GetCurrentTime();
return TGVOIP_CONCTL_ACT_DECREASE;
}
return TGVOIP_CONCTL_ACT_NONE;
}
uint32_t CongestionControl::GetSendLossCount(){
return lossCount;
}

View File

@ -0,0 +1,71 @@
//
// 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 LIBTGVOIP_CONGESTIONCONTROL_H
#define LIBTGVOIP_CONGESTIONCONTROL_H
#include <stdlib.h>
#include <stdint.h>
#include "threading.h"
#define TGVOIP_CONCTL_STARTUP 0
#define TGVOIP_CONCTL_DRAIN 1
#define TGVOIP_CONCTL_PROBE_BW 2
#define TGVOIP_CONCTL_PROBE_RTT 3
#define TGVOIP_CONCTL_ACT_INCREASE 1
#define TGVOIP_CONCTL_ACT_DECREASE 2
#define TGVOIP_CONCTL_ACT_NONE 0
namespace tgvoip{
struct tgvoip_congestionctl_packet_t{
uint32_t seq;
double sendTime;
size_t size;
};
typedef struct tgvoip_congestionctl_packet_t tgvoip_congestionctl_packet_t;
class CongestionControl{
public:
CongestionControl();
~CongestionControl();
void PacketSent(uint32_t seq, size_t size);
void PacketAcknowledged(uint32_t seq);
double GetAverageRTT();
double GetMinimumRTT();
size_t GetInflightDataSize();
size_t GetCongestionWindow();
size_t GetAcknowledgedDataSize();
void Tick();
int GetBandwidthControlAction();
uint32_t GetSendLossCount();
private:
double rttHistory[100];
tgvoip_congestionctl_packet_t inflightPackets[100];
size_t inflightHistory[30];
int state;
uint32_t lossCount;
double tmpRtt;
double lastActionTime;
double lastActionRtt;
double stateTransitionTime;
int tmpRttCount;
char rttHistorySize;
unsigned int rttHistoryTop;
unsigned int inflightHistoryTop;
uint32_t lastSentSeq;
uint32_t tickCount;
size_t inflightDataSize;
size_t cwnd;
tgvoip_mutex_t mutex;
};
}
#endif //LIBTGVOIP_CONGESTIONCONTROL_H

View File

@ -0,0 +1,356 @@
//
// 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 "EchoCanceller.h"
#include "audio/AudioOutput.h"
#include "audio/AudioInput.h"
#include "logging.h"
#include <string.h>
#include <stdio.h>
#ifndef TGVOIP_NO_DSP
#ifndef TGVOIP_USE_DESKTOP_DSP
#include "webrtc/modules/audio_processing/aecm/echo_control_mobile.h"
#include "webrtc/modules/audio_processing/ns/noise_suppression_x.h"
#else
#include "webrtc/modules/audio_processing/aec/echo_cancellation.h"
//#include "webrtc/modules/audio_processing/ns/noise_suppression.h"
#include "webrtc/modules/audio_processing/ns/noise_suppression_x.h"
#endif
#include "webrtc/modules/audio_processing/splitting_filter.h"
#include "webrtc/common_audio/channel_buffer.h"
#include "webrtc/modules/audio_processing/agc/legacy/gain_control.h"
#endif
#define AEC_FRAME_SIZE 160
#define OFFSET_STEP AEC_FRAME_SIZE*2
//#define CLAMP(x, min, max) (x<max ? (x>min ? x : min) : max)
#define CLAMP(x, min, max) x
using namespace tgvoip;
#ifdef TGVOIP_USE_DESKTOP_DSP
namespace webrtc{
void WebRtcAec_enable_delay_agnostic(AecCore* self, int enable);
}
#endif
EchoCanceller::EchoCanceller(bool enableAEC, bool enableNS, bool enableAGC){
this->enableAEC=enableAEC;
this->enableAGC=enableAGC;
this->enableNS=enableNS;
#ifndef TGVOIP_NO_DSP
splittingFilter=new webrtc::SplittingFilter(1, 3, 960);
splittingFilterFarend=new webrtc::SplittingFilter(1, 3, 960);
splittingFilterIn=new webrtc::IFChannelBuffer(960, 1, 1);
splittingFilterFarendIn=new webrtc::IFChannelBuffer(960, 1, 1);
splittingFilterOut=new webrtc::IFChannelBuffer(960, 1, 3);
splittingFilterFarendOut=new webrtc::IFChannelBuffer(960, 1, 3);
if(enableAEC){
init_mutex(aecMutex);
#ifndef TGVOIP_USE_DESKTOP_DSP
aec=WebRtcAecm_Create();
WebRtcAecm_Init(aec, 16000);
AecmConfig cfg;
cfg.cngMode=AecmFalse;
cfg.echoMode=1;
WebRtcAecm_set_config(aec, cfg);
#else
aec=webrtc::WebRtcAec_Create();
webrtc::WebRtcAec_Init(aec, 48000, 48000);
//webrtc::WebRtcAec_enable_delay_agnostic(webrtc::WebRtcAec_aec_core(aec), 1);
webrtc::AecConfig config;
config.metricsMode=webrtc::kAecFalse;
config.nlpMode=webrtc::kAecNlpAggressive;
config.skewMode=webrtc::kAecFalse;
config.delay_logging=webrtc::kAecFalse;
webrtc::WebRtcAec_set_config(aec, config);
#endif
farendQueue=new BlockingQueue<int16_t*>(11);
farendBufferPool=new BufferPool(960*2, 10);
running=true;
start_thread(bufferFarendThread, EchoCanceller::StartBufferFarendThread, this);
}
if(enableNS){
//#ifndef TGVOIP_USE_DESKTOP_DSP
ns=WebRtcNsx_Create();
WebRtcNsx_Init((NsxHandle*)ns, 48000);
WebRtcNsx_set_policy((NsxHandle*)ns, 1);
/*#else
ns=WebRtcNs_Create();
WebRtcNs_Init((NsHandle*)ns, 48000);
WebRtcNs_set_policy((NsHandle*)ns, 1);
#endif*/
}
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;
}
#endif
}
EchoCanceller::~EchoCanceller(){
if(enableAEC){
running=false;
farendQueue->Put(NULL);
join_thread(bufferFarendThread);
delete farendQueue;
delete farendBufferPool;
#ifndef TGVOIP_USE_DESKTOP_DSP
WebRtcAecm_Free(aec);
#else
webrtc::WebRtcAec_Free(aec);
#endif
}
if(enableNS){
//#ifndef TGVOIP_USE_DESKTOP_DSP
WebRtcNsx_Free((NsxHandle*)ns);
/*#else
WebRtcNs_Free((NsHandle*)ns);
#endif*/
}
if(enableAGC){
WebRtcAgc_Free(agc);
}
//webrtc::WebRtcAec_Free(state);
delete (webrtc::SplittingFilter*)splittingFilter;
delete (webrtc::SplittingFilter*)splittingFilterFarend;
delete (webrtc::IFChannelBuffer*)splittingFilterIn;
delete (webrtc::IFChannelBuffer*)splittingFilterOut;
delete (webrtc::IFChannelBuffer*)splittingFilterFarendIn;
delete (webrtc::IFChannelBuffer*)splittingFilterFarendOut;
if (this->enableAEC) {
free_mutex(aecMutex);
}
}
void EchoCanceller::Start(){
}
void EchoCanceller::Stop(){
}
void EchoCanceller::SpeakerOutCallback(unsigned char* data, size_t len){
if(len!=960*2 || !enableAEC)
return;
/*size_t offset=0;
while(offset<len){
WebRtcAecm_BufferFarend(state, (int16_t*)(data+offset), AEC_FRAME_SIZE);
offset+=OFFSET_STEP;
}*/
int16_t* buf=(int16_t*)farendBufferPool->Get();
if(buf){
memcpy(buf, data, 960*2);
farendQueue->Put(buf);
}
}
void *EchoCanceller::StartBufferFarendThread(void *arg){
((EchoCanceller*)arg)->RunBufferFarendThread();
return NULL;
}
void EchoCanceller::RunBufferFarendThread(){
while(running){
int16_t* samplesIn=farendQueue->GetBlocking();
if(samplesIn){
webrtc::IFChannelBuffer* bufIn=(webrtc::IFChannelBuffer*) splittingFilterFarendIn;
webrtc::IFChannelBuffer* bufOut=(webrtc::IFChannelBuffer*) splittingFilterFarendOut;
memcpy(bufIn->ibuf()->bands(0)[0], samplesIn, 960*2);
farendBufferPool->Reuse((unsigned char *) samplesIn);
((webrtc::SplittingFilter*)splittingFilterFarend)->Analysis(bufIn, bufOut);
lock_mutex(aecMutex);
#ifndef TGVOIP_USE_DESKTOP_DSP
WebRtcAecm_BufferFarend(aec, bufOut->ibuf_const()->bands(0)[0], 160);
WebRtcAecm_BufferFarend(aec, bufOut->ibuf_const()->bands(0)[0]+160, 160);
#else
webrtc::WebRtcAec_BufferFarend(aec, bufOut->fbuf_const()->bands(0)[0], 160);
webrtc::WebRtcAec_BufferFarend(aec, bufOut->fbuf_const()->bands(0)[0]+160, 160);
#endif
unlock_mutex(aecMutex);
didBufferFarend=true;
}
}
}
void EchoCanceller::Enable(bool enabled){
//isOn=enabled;
}
void EchoCanceller::ProcessInput(unsigned char* data, unsigned char* out, size_t len){
int i;
if(!enableAEC && !enableAGC && !enableNS){
memcpy(out, data, len);
return;
}
int16_t* samplesIn=(int16_t*)data;
int16_t* samplesOut=(int16_t*)out;
webrtc::IFChannelBuffer* bufIn=(webrtc::IFChannelBuffer*) splittingFilterIn;
webrtc::IFChannelBuffer* bufOut=(webrtc::IFChannelBuffer*) splittingFilterOut;
memcpy(bufIn->ibuf()->bands(0)[0], samplesIn, 960*2);
((webrtc::SplittingFilter*)splittingFilter)->Analysis(bufIn, bufOut);
#ifndef TGVOIP_USE_DESKTOP_DSP
if(enableAEC && enableNS){
int16_t _nsOut[3][320];
int16_t* nsIn[3];
int16_t* nsOut[3];
for(i=0;i<3;i++){
nsIn[i]=(int16_t*)bufOut->ibuf_const()->bands(0)[i];
nsOut[i]=_nsOut[i];
}
WebRtcNsx_Process((NsxHandle*)ns, (const short *const *) nsIn, 3, nsOut);
for(i=0;i<3;i++){
nsOut[i]+=160;
nsIn[i]+=160;
}
WebRtcNsx_Process((NsxHandle*)ns, (const short *const *) nsIn, 3, nsOut);
memcpy(bufOut->ibuf()->bands(0)[1], _nsOut[1], 320*2*2);
lock_mutex(aecMutex);
WebRtcAecm_Process(aec, bufOut->ibuf()->bands(0)[0], _nsOut[0], samplesOut, AEC_FRAME_SIZE, (int16_t) tgvoip::audio::AudioOutput::GetEstimatedDelay());
WebRtcAecm_Process(aec, bufOut->ibuf()->bands(0)[0]+160, _nsOut[0]+160, samplesOut+160, AEC_FRAME_SIZE, (int16_t) (tgvoip::audio::AudioOutput::GetEstimatedDelay()+audio::AudioInput::GetEstimatedDelay()));
unlock_mutex(aecMutex);
memcpy(bufOut->ibuf()->bands(0)[0], samplesOut, 320*2);
}else if(enableAEC){
lock_mutex(aecMutex);
WebRtcAecm_Process(aec, bufOut->ibuf()->bands(0)[0], NULL, samplesOut, AEC_FRAME_SIZE, (int16_t) tgvoip::audio::AudioOutput::GetEstimatedDelay());
WebRtcAecm_Process(aec, bufOut->ibuf()->bands(0)[0]+160, NULL, samplesOut+160, AEC_FRAME_SIZE, (int16_t) (tgvoip::audio::AudioOutput::GetEstimatedDelay()+audio::AudioInput::GetEstimatedDelay()));
unlock_mutex(aecMutex);
memcpy(bufOut->ibuf()->bands(0)[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]=(int16_t*)bufOut->ibuf_const()->bands(0)[i];
nsOut[i]=_nsOut[i];
}
WebRtcNsx_Process((NsxHandle*)ns, (const short *const *) nsIn, 3, nsOut);
for(i=0;i<3;i++){
nsOut[i]+=160;
nsIn[i]+=160;
}
WebRtcNsx_Process((NsxHandle*)ns, (const short *const *) nsIn, 3, nsOut);
memcpy(bufOut->ibuf()->bands(0)[0], _nsOut[0], 320*2);
memcpy(bufOut->ibuf()->bands(0)[1], _nsOut[1], 320*2);
memcpy(bufOut->ibuf()->bands(0)[2], _nsOut[2], 320*2);
}
#else
/*if(enableNS){
float _nsOut[3][320];
const float* nsIn[3];
float* nsOut[3];
for(i=0;i<3;i++){
nsIn[i]=bufOut->fbuf_const()->bands(0)[i];
nsOut[i]=_nsOut[i];
}
WebRtcNs_Process((NsHandle*)ns, nsIn, 3, nsOut);
for(i=0;i<3;i++){
nsOut[i]+=160;
nsIn[i]+=160;
}
WebRtcNs_Process((NsHandle*)ns, nsIn, 3, nsOut);
memcpy(bufOut->fbuf()->bands(0)[0], _nsOut[0], 320*4);
memcpy(bufOut->fbuf()->bands(0)[1], _nsOut[1], 320*4);
memcpy(bufOut->fbuf()->bands(0)[2], _nsOut[2], 320*4);
}*/
if(enableNS){
int16_t _nsOut[3][320];
int16_t* nsIn[3];
int16_t* nsOut[3];
for(i=0;i<3;i++){
nsIn[i]=(int16_t*)bufOut->ibuf_const()->bands(0)[i];
nsOut[i]=_nsOut[i];
}
WebRtcNsx_Process((NsxHandle*)ns, (const short *const *)nsIn, 3, nsOut);
for(i=0;i<3;i++){
nsOut[i]+=160;
nsIn[i]+=160;
}
WebRtcNsx_Process((NsxHandle*)ns, (const short *const *)nsIn, 3, nsOut);
memcpy(bufOut->ibuf()->bands(0)[0], _nsOut[0], 320*2);
memcpy(bufOut->ibuf()->bands(0)[1], _nsOut[1], 320*2);
memcpy(bufOut->ibuf()->bands(0)[2], _nsOut[2], 320*2);
}
if(enableAEC){
const float* aecIn[3];
float* aecOut[3];
float _aecOut[3][320];
for(i=0;i<3;i++){
aecIn[i]=bufOut->fbuf_const()->bands(0)[i];
aecOut[i]=_aecOut[i];
}
webrtc::WebRtcAec_Process(aec, aecIn, 3, aecOut, AEC_FRAME_SIZE, audio::AudioOutput::GetEstimatedDelay()+audio::AudioInput::GetEstimatedDelay(), 0);
for(i=0;i<3;i++){
aecOut[i]+=160;
aecIn[i]+=160;
}
webrtc::WebRtcAec_Process(aec, aecIn, 3, aecOut, AEC_FRAME_SIZE, audio::AudioOutput::GetEstimatedDelay()+audio::AudioInput::GetEstimatedDelay(), 0);
memcpy(bufOut->fbuf()->bands(0)[0], _aecOut[0], 320*4);
memcpy(bufOut->fbuf()->bands(0)[1], _aecOut[1], 320*4);
memcpy(bufOut->fbuf()->bands(0)[2], _aecOut[2], 320*4);
}
#endif
if(enableAGC){
int16_t _agcOut[3][320];
int16_t* agcIn[3];
int16_t* agcOut[3];
for(i=0;i<3;i++){
agcIn[i]=(int16_t*)bufOut->ibuf_const()->bands(0)[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(bufOut->ibuf()->bands(0)[0], _agcOut[0], 320*2);
memcpy(bufOut->ibuf()->bands(0)[1], _agcOut[1], 320*2);
memcpy(bufOut->ibuf()->bands(0)[2], _agcOut[2], 320*2);
}
((webrtc::SplittingFilter*)splittingFilter)->Synthesis(bufOut, bufIn);
memcpy(samplesOut, bufIn->ibuf_const()->bands(0)[0], 960*2);
}

View File

@ -0,0 +1,53 @@
//
// 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 LIBTGVOIP_ECHOCANCELLER_H
#define LIBTGVOIP_ECHOCANCELLER_H
#include "threading.h"
#include "BufferPool.h"
#include "BlockingQueue.h"
namespace tgvoip{
class EchoCanceller{
public:
EchoCanceller(bool enableAEC, bool enableNS, bool enableAGC);
virtual ~EchoCanceller();
virtual void Start();
virtual void Stop();
void SpeakerOutCallback(unsigned char* data, size_t len);
void Enable(bool enabled);
void ProcessInput(unsigned char* data, unsigned char* out, size_t len);
private:
bool enableAEC;
bool enableAGC;
bool enableNS;
#ifndef TGVOIP_NO_DSP
static void* StartBufferFarendThread(void* arg);
void RunBufferFarendThread();
bool didBufferFarend;
tgvoip_mutex_t aecMutex;
void* aec;
void* splittingFilter; // webrtc::SplittingFilter
void* splittingFilterIn; // webrtc::IFChannelBuffer
void* splittingFilterOut; // webrtc::IFChannelBuffer
void* splittingFilterFarend; // webrtc::SplittingFilter
void* splittingFilterFarendIn; // webrtc::IFChannelBuffer
void* splittingFilterFarendOut; // webrtc::IFChannelBuffer
tgvoip_thread_t bufferFarendThread;
BlockingQueue<int16_t*>* farendQueue;
BufferPool* farendBufferPool;
bool running;
void* ns; // NsxHandle
void* agc;
int32_t agcMicLevel;
#endif
};
}
#endif //LIBTGVOIP_ECHOCANCELLER_H

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -0,0 +1,488 @@
//
// 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 "VoIPController.h"
#include "JitterBuffer.h"
#include "logging.h"
#include "VoIPServerConfig.h"
#include <math.h>
using namespace tgvoip;
JitterBuffer::JitterBuffer(MediaStreamItf *out, uint32_t step):bufferPool(JITTER_SLOT_SIZE, JITTER_SLOT_COUNT){
if(out)
out->SetCallback(JitterBuffer::CallbackOut, this);
this->step=step;
memset(slots, 0, sizeof(jitter_packet_t)*JITTER_SLOT_COUNT);
minDelay=6;
lostCount=0;
needBuffering=true;
tickCount=0;
dontIncMinDelay=0;
dontDecMinDelay=0;
lostPackets=0;
outstandingDelayChange=0;
if(step<30){
minMinDelay=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_min_delay_20", 6);
maxMinDelay=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_max_delay_20", 25);
maxUsedSlots=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_max_slots_20", 50);
}else if(step<50){
minMinDelay=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_min_delay_40", 4);
maxMinDelay=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_max_delay_40", 15);
maxUsedSlots=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_max_slots_40", 30);
}else{
minMinDelay=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_min_delay_60", 1);
maxMinDelay=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_max_delay_60", 10);
maxUsedSlots=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_max_slots_60", 20);
}
lossesToReset=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_losses_to_reset", 20);
resyncThreshold=ServerConfig::GetSharedInstance()->GetDouble("jitter_resync_threshold", 1.0);
//dump=fopen("/sdcard/tgvoip_jitter_dump.txt", "a");
//fprintf(dump, "==================================\n");
Reset();
init_mutex(mutex);
}
JitterBuffer::~JitterBuffer(){
Reset();
free_mutex(mutex);
}
void JitterBuffer::SetMinPacketCount(uint32_t count){
if(minDelay==count)
return;
minDelay=count;
Reset();
}
int JitterBuffer::GetMinPacketCount(){
return minDelay;
}
size_t JitterBuffer::CallbackIn(unsigned char *data, size_t len, void *param){
//((JitterBuffer*)param)->HandleInput(data, len);
return 0;
}
size_t JitterBuffer::CallbackOut(unsigned char *data, size_t len, void *param){
return 0; //((JitterBuffer*)param)->HandleOutput(data, len, 0, NULL);
}
void JitterBuffer::HandleInput(unsigned char *data, size_t len, uint32_t timestamp){
jitter_packet_t pkt;
pkt.size=len;
pkt.buffer=data;
pkt.timestamp=timestamp;
lock_mutex(mutex);
PutInternal(&pkt);
unlock_mutex(mutex);
//LOGV("in, ts=%d", timestamp);
}
void JitterBuffer::Reset(){
wasReset=true;
needBuffering=true;
lastPutTimestamp=0;
int i;
for(i=0;i<JITTER_SLOT_COUNT;i++){
if(slots[i].buffer){
bufferPool.Reuse(slots[i].buffer);
slots[i].buffer=NULL;
}
}
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;
outstandingDelayChange=0;
dontChangeDelay=0;
}
size_t JitterBuffer::HandleOutput(unsigned char *buffer, size_t len, int offsetInSteps, int* playbackScaledDuration){
jitter_packet_t pkt;
pkt.buffer=buffer;
pkt.size=len;
lock_mutex(mutex);
int result=GetInternal(&pkt, offsetInSteps);
if(playbackScaledDuration){
if(outstandingDelayChange!=0){
if(outstandingDelayChange<0){
*playbackScaledDuration=40;
outstandingDelayChange+=20;
}else{
*playbackScaledDuration=80;
outstandingDelayChange-=20;
}
LOGV("outstanding delay change: %d", outstandingDelayChange);
}else{
*playbackScaledDuration=60;
}
}
unlock_mutex(mutex);
if(result==JR_OK){
return pkt.size;
}else{
return 0;
}
}
int JitterBuffer::GetInternal(jitter_packet_t* pkt, int offset){
/*if(needBuffering && lastPutTimestamp<nextTimestamp){
LOGV("jitter: don't have timestamp %lld, buffering", (long long int)nextTimestamp);
Advance();
return JR_BUFFERING;
}*/
//needBuffering=false;
int64_t timestampToGet=nextTimestamp+offset*(int32_t)step;
int i;
for(i=0;i<JITTER_SLOT_COUNT;i++){
if(slots[i].buffer!=NULL && slots[i].timestamp==timestampToGet){
break;
}
}
if(i<JITTER_SLOT_COUNT){
if(pkt && pkt->size<slots[i].size){
LOGE("jitter: packet won't fit into provided buffer of %d (need %d)", int(slots[i].size), int(pkt->size));
}else{
if(pkt) {
pkt->size = slots[i].size;
pkt->timestamp = slots[i].timestamp;
memcpy(pkt->buffer, slots[i].buffer, slots[i].size);
}
}
bufferPool.Reuse(slots[i].buffer);
slots[i].buffer=NULL;
if(offset==0)
Advance();
lostCount=0;
needBuffering=false;
return JR_OK;
}
LOGW("jitter: found no packet for timestamp %lld (last put = %d, lost = %d)", (long long int)timestampToGet, lastPutTimestamp, lostCount);
if(offset==0)
Advance();
if(!needBuffering){
lostCount++;
if(offset==0){
lostPackets++;
lostSinceReset++;
}
if(lostCount>=lossesToReset || (gotSinceReset>minDelay*25 && lostSinceReset>gotSinceReset/2)){
LOGW("jitter: lost %d packets in a row, resetting", lostCount);
//minDelay++;
dontIncMinDelay=16;
dontDecMinDelay+=128;
if(GetCurrentDelay()<minDelay)
nextTimestamp-=(minDelay-GetCurrentDelay());
lostCount=0;
Reset();
}
return JR_MISSING;
}
return JR_BUFFERING;
}
void JitterBuffer::PutInternal(jitter_packet_t* pkt){
if(pkt->size>JITTER_SLOT_SIZE){
LOGE("The packet is too big to fit into the jitter buffer");
return;
}
gotSinceReset++;
int i;
if(wasReset){
wasReset=false;
outstandingDelayChange=0;
nextTimestamp=((int64_t)pkt->timestamp)-step*minDelay;
LOGI("jitter: resyncing, next timestamp = %lld (step=%d, minDelay=%d)", (long long int)nextTimestamp, step, minDelay);
}
for(i=0;i<JITTER_SLOT_COUNT;i++){
if(slots[i].buffer!=NULL){
if(slots[i].timestamp<nextTimestamp-1){
bufferPool.Reuse(slots[i].buffer);
slots[i].buffer=NULL;
}
}
}
/*double prevTime=0;
uint32_t closestTime=0;
for(i=0;i<JITTER_SLOT_COUNT;i++){
if(slots[i].buffer!=NULL && pkt->timestamp-slots[i].timestamp<pkt->timestamp-closestTime){
closestTime=slots[i].timestamp;
prevTime=slots[i].recvTime;
}
}*/
double time=VoIPController::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->timestamp<nextTimestamp){
LOGW("jitter: would drop packet with timestamp %d because it is late but not hopelessly", pkt->timestamp);
latePacketCount++;
lostPackets--;
}else if(pkt->timestamp<nextTimestamp-1){
LOGW("jitter: dropping packet with timestamp %d because it is too late", pkt->timestamp);
latePacketCount++;
return;
}
if(pkt->timestamp>lastPutTimestamp)
lastPutTimestamp=pkt->timestamp;
for(i=0;i<JITTER_SLOT_COUNT;i++){
if(slots[i].buffer==NULL)
break;
}
if(i==JITTER_SLOT_COUNT || GetCurrentDelay()>=maxUsedSlots){
int toRemove=JITTER_SLOT_COUNT;
uint32_t bestTimestamp=0xFFFFFFFF;
for(i=0;i<JITTER_SLOT_COUNT;i++){
if(slots[i].buffer!=NULL && slots[i].timestamp<bestTimestamp){
toRemove=i;
bestTimestamp=slots[i].timestamp;
}
}
Advance();
bufferPool.Reuse(slots[toRemove].buffer);
slots[toRemove].buffer=NULL;
i=toRemove;
}
slots[i].timestamp=pkt->timestamp;
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!!");
//fprintf(dump, "%f %d\n", time-prevRecvTime, GetCurrentDelay());
prevRecvTime=time;
}
void JitterBuffer::Advance(){
nextTimestamp+=step;
}
int JitterBuffer::GetCurrentDelay(){
int delay=0;
int i;
for(i=0;i<JITTER_SLOT_COUNT;i++){
if(slots[i].buffer!=NULL)
delay++;
}
return delay;
}
void JitterBuffer::Tick(){
lock_mutex(mutex);
int i;
int count=0;
memmove(&lateHistory[1], lateHistory, 63*sizeof(int));
lateHistory[0]=latePacketCount;
latePacketCount=0;
bool absolutelyNoLatePackets=true;
double avgLate64=0, avgLate32=0, avgLate16=0;
for(i=0;i<64;i++){
avgLate64+=lateHistory[i];
if(i<32)
avgLate32+=lateHistory[i];
if(i<16){
avgLate16+=lateHistory[i];
}
if(lateHistory[i]>0)
absolutelyNoLatePackets=false;
}
avgLate64/=64;
avgLate32/=32;
avgLate16/=16;
//LOGV("jitter: avg late=%.1f, %.1f, %.1f", avgLate16, avgLate32, avgLate64);
if(avgLate16>=resyncThreshold){
wasReset=true;
}
/*if(avgLate16>=0.3){
if(dontIncMinDelay==0 && minDelay<15){
minDelay++;
if(GetCurrentDelay()<minDelay)
nextTimestamp-=(minDelay-GetCurrentDelay());
dontIncMinDelay=16;
dontDecMinDelay+=128;
}
}else if(absolutelyNoLatePackets){
if(dontDecMinDelay>0)
dontDecMinDelay--;
if(dontDecMinDelay==0 && minDelay>minMinDelay){
minDelay--;
dontDecMinDelay=64;
dontIncMinDelay+=16;
}
}
if(dontIncMinDelay>0)
dontIncMinDelay--;*/
if(absolutelyNoLatePackets){
if(dontDecMinDelay>0)
dontDecMinDelay--;
}
memmove(&delayHistory[1], delayHistory, 63*sizeof(int));
delayHistory[0]=GetCurrentDelay();
avgDelay=0;
int min=100;
for(i=0;i<32;i++){
avgDelay+=delayHistory[i];
if(delayHistory[i]<min)
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(stddevDelay<minMinDelay)
stddevDelay=minMinDelay;
if(stddevDelay>maxMinDelay)
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;
outstandingDelayChange+=diff*60;
dontChangeDelay+=32;
LOGD("new delay from stddev %d", minDelay);
if(diff<0){
dontDecMinDelay+=25;
}
if(diff>0){
dontIncMinDelay=25;
}
}
}
lastMeasuredJitter=stddev;
lastMeasuredDelay=stddevDelay;
//LOGV("stddev=%.3f, avg=%.3f, ndelay=%d, dontDec=%u", stddev, avgdev, stddevDelay, dontDecMinDelay);
if(dontChangeDelay==0){
if(avgDelay>minDelay+0.5){
outstandingDelayChange-=avgDelay>minDelay+2 ? 60 : 20;
dontChangeDelay+=10;
}else if(avgDelay<minDelay-0.3){
outstandingDelayChange+=20;
dontChangeDelay+=10;
}
}
if(dontChangeDelay>0)
dontChangeDelay--;
//LOGV("jitter: avg delay=%d, delay=%d, late16=%.1f, dontDecMinDelay=%d", avgDelay, delayHistory[0], avgLate16, dontDecMinDelay);
/*if(!adjustingDelay) {
if (((minDelay==1 ? (avgDelay>=3) : (avgDelay>=minDelay/2)) && delayHistory[0]>minDelay && avgLate16<=0.1 && absolutelyNoLatePackets && dontDecMinDelay<32 && min>minDelay)) {
LOGI("jitter: need adjust");
adjustingDelay=true;
}
}else{
if(!absolutelyNoLatePackets){
LOGI("jitter: done adjusting because we're losing packets");
adjustingDelay=false;
}else if(tickCount%5==0){
LOGD("jitter: removing a packet to reduce delay");
GetInternal(NULL, 0);
expectNextAtTime=0;
if(GetCurrentDelay()<=minDelay || min<=minDelay){
adjustingDelay = false;
LOGI("jitter: done adjusting");
}
}
}*/
tickCount++;
unlock_mutex(mutex);
}
void JitterBuffer::GetAverageLateCount(double *out){
double avgLate64=0, avgLate32=0, avgLate16=0;
int i;
for(i=0;i<64;i++){
avgLate64+=lateHistory[i];
if(i<32)
avgLate32+=lateHistory[i];
if(i<16)
avgLate16+=lateHistory[i];
}
avgLate64/=64;
avgLate32/=32;
avgLate16/=16;
out[0]=avgLate16;
out[1]=avgLate32;
out[2]=avgLate64;
}
int JitterBuffer::GetAndResetLostPacketCount(){
lock_mutex(mutex);
int r=lostPackets;
lostPackets=0;
unlock_mutex(mutex);
return r;
}
double JitterBuffer::GetLastMeasuredJitter(){
return lastMeasuredJitter;
}
double JitterBuffer::GetLastMeasuredDelay(){
return lastMeasuredDelay;
}
double JitterBuffer::GetAverageDelay(){
return avgDelay;
}

View File

@ -0,0 +1,95 @@
//
// 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 LIBTGVOIP_JITTERBUFFER_H
#define LIBTGVOIP_JITTERBUFFER_H
#include <stdlib.h>
#include <vector>
#include <stdio.h>
#include "MediaStreamItf.h"
#include "BlockingQueue.h"
#include "BufferPool.h"
#include "threading.h"
#define JITTER_SLOT_COUNT 64
#define JITTER_SLOT_SIZE 1024
#define JR_OK 1
#define JR_MISSING 2
#define JR_BUFFERING 3
struct jitter_packet_t{
unsigned char* buffer;
size_t size;
uint32_t timestamp;
double recvTimeDiff;
};
typedef struct jitter_packet_t jitter_packet_t;
namespace tgvoip{
class JitterBuffer{
public:
JitterBuffer(MediaStreamItf* out, uint32_t step);
~JitterBuffer();
void SetMinPacketCount(uint32_t count);
int GetMinPacketCount();
int GetCurrentDelay();
double GetAverageDelay();
void Reset();
void HandleInput(unsigned char* data, size_t len, uint32_t timestamp);
size_t HandleOutput(unsigned char* buffer, size_t len, int offsetInSteps, int* playbackScaledDuration);
void Tick();
void GetAverageLateCount(double* out);
int GetAndResetLostPacketCount();
double GetLastMeasuredJitter();
double GetLastMeasuredDelay();
private:
static size_t CallbackIn(unsigned char* data, size_t len, void* param);
static size_t CallbackOut(unsigned char* data, size_t len, void* param);
void PutInternal(jitter_packet_t* pkt);
int GetInternal(jitter_packet_t* pkt, int offset);
void Advance();
BufferPool bufferPool;
tgvoip_mutex_t mutex;
jitter_packet_t slots[JITTER_SLOT_COUNT];
int64_t nextTimestamp;
uint32_t step;
uint32_t minDelay;
uint32_t minMinDelay;
uint32_t maxMinDelay;
uint32_t maxUsedSlots;
uint32_t lastPutTimestamp;
uint32_t lossesToReset;
double resyncThreshold;
int lostCount;
int lostSinceReset;
int gotSinceReset;
bool wasReset;
bool needBuffering;
int delayHistory[64];
int lateHistory[64];
bool adjustingDelay;
unsigned int tickCount;
unsigned int latePacketCount;
unsigned int dontIncMinDelay;
unsigned int dontDecMinDelay;
int lostPackets;
double prevRecvTime;
double expectNextAtTime;
double deviationHistory[64];
int deviationPtr;
double lastMeasuredJitter;
double lastMeasuredDelay;
int outstandingDelayChange;
unsigned int dontChangeDelay;
double avgDelay;
//FILE* dump;
};
}
#endif //LIBTGVOIP_JITTERBUFFER_H

View File

@ -0,0 +1,18 @@
//
// 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 "MediaStreamItf.h"
using namespace tgvoip;
void MediaStreamItf::SetCallback(size_t (*f)(unsigned char *, size_t, void*), void* param){
callback=f;
callbackParam=param;
}
size_t MediaStreamItf::InvokeCallback(unsigned char *data, size_t length){
return (*callback)(data, length, callbackParam);
}

View File

@ -0,0 +1,29 @@
//
// 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 LIBTGVOIP_MEDIASTREAMINPUT_H
#define LIBTGVOIP_MEDIASTREAMINPUT_H
#include <string.h>
namespace tgvoip{
class MediaStreamItf{
public:
virtual void Start()=0;
virtual void Stop()=0;
void SetCallback(size_t (*f)(unsigned char*, size_t, void*), void* param);
//protected:
size_t InvokeCallback(unsigned char* data, size_t length);
private:
size_t (*callback)(unsigned char*, size_t, void*);
void* callbackParam;
};
}
#endif //LIBTGVOIP_MEDIASTREAMINPUT_H

View File

@ -0,0 +1,608 @@
//
// Created by Grishka on 29.03.17.
//
#include "NetworkSocket.h"
#include <stdexcept>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#if defined(_WIN32)
#include "os/windows/NetworkSocketWinsock.h"
#include <winsock2.h>
#else
#include "os/posix/NetworkSocketPosix.h"
#endif
#include "logging.h"
#include "VoIPServerConfig.h"
#include "VoIPController.h"
#include "BufferInputStream.h"
#define MIN_UDP_PORT 16384
#define MAX_UDP_PORT 32768
using namespace tgvoip;
NetworkSocket::NetworkSocket(NetworkProtocol protocol) : protocol(protocol){
ipv6Timeout=ServerConfig::GetSharedInstance()->GetDouble("nat64_fallback_timeout", 3);
failed=false;
proxyAddress=NULL;
proxyPort=0;
proxyUsername=NULL;
proxyPassword=NULL;
}
NetworkSocket::~NetworkSocket(){
}
std::string NetworkSocket::GetLocalInterfaceInfo(IPv4Address *inet4addr, IPv6Address *inet6addr){
std::string r="not implemented";
return r;
}
uint16_t NetworkSocket::GenerateLocalPort(){
return (uint16_t) ((rand()%(MAX_UDP_PORT-MIN_UDP_PORT))+MIN_UDP_PORT);
}
void NetworkSocket::SetMaxPriority(){
}
bool NetworkSocket::IsFailed(){
return failed;
}
NetworkSocket *NetworkSocket::Create(NetworkProtocol protocol){
#ifndef _WIN32
return new NetworkSocketPosix(protocol);
#else
return new NetworkSocketWinsock(protocol);
#endif
}
IPv4Address *NetworkSocket::ResolveDomainName(std::string name){
#ifndef _WIN32
return NetworkSocketPosix::ResolveDomainName(name);
#else
return NetworkSocketWinsock::ResolveDomainName(name);
#endif
}
void NetworkSocket::SetSocksProxy(IPv4Address *addr, uint16_t port, char *username, char *password){
proxyAddress=addr;
proxyPort=port;
proxyUsername=username;
proxyPassword=password;
}
void NetworkSocket::GenerateTCPO2States(unsigned char* buffer, TCPO2State* recvState, TCPO2State* sendState){
memset(recvState, 0, sizeof(TCPO2State));
memset(sendState, 0, sizeof(TCPO2State));
unsigned char nonce[64];
uint32_t *first = reinterpret_cast<uint32_t*>(nonce), *second = first + 1;
uint32_t first1 = 0x44414548U, first2 = 0x54534f50U, first3 = 0x20544547U, first4 = 0x20544547U, first5 = 0xeeeeeeeeU;
uint32_t second1 = 0;
do {
VoIPController::crypto.rand_bytes(nonce, sizeof(nonce));
} while (*first == first1 || *first == first2 || *first == first3 || *first == first4 || *first == first5 || *second == second1 || *reinterpret_cast<unsigned char*>(nonce) == 0xef);
// prepare encryption key/iv
memcpy(sendState->key, nonce + 8, 32);
memcpy(sendState->iv, nonce + 8 + 32, 16);
// prepare decryption key/iv
char reversed[48];
memcpy(reversed, nonce + 8, sizeof(reversed));
std::reverse(reversed, reversed + sizeof(reversed));
memcpy(recvState->key, reversed, 32);
memcpy(recvState->iv, reversed + 32, 16);
// write protocol identifier
*reinterpret_cast<uint32_t*>(nonce + 56) = 0xefefefefU;
memcpy(buffer, nonce, 56);
EncryptForTCPO2(nonce, sizeof(nonce), sendState);
memcpy(buffer+56, nonce+56, 8);
}
void NetworkSocket::EncryptForTCPO2(unsigned char *buffer, size_t len, TCPO2State *state){
VoIPController::crypto.aes_ctr_encrypt(buffer, len, state->key, state->iv, state->ecount, &state->num);
}
size_t NetworkSocket::Receive(unsigned char *buffer, size_t len){
NetworkPacket pkt;
pkt.data=buffer;
pkt.length=len;
Receive(&pkt);
return pkt.length;
}
size_t NetworkSocket::Send(unsigned char *buffer, size_t len){
NetworkPacket pkt;
pkt.data=buffer;
pkt.length=len;
Send(&pkt);
return pkt.length;
}
bool NetworkAddress::operator==(const NetworkAddress &other){
IPv4Address* self4=dynamic_cast<IPv4Address*>(this);
IPv4Address* other4=dynamic_cast<IPv4Address*>((NetworkAddress*)&other);
if(self4 && other4){
return self4->GetAddress()==other4->GetAddress();
}
IPv6Address* self6=dynamic_cast<IPv6Address*>(this);
IPv6Address* other6=dynamic_cast<IPv6Address*>((NetworkAddress*)&other);
if(self6 && other6){
return memcmp(self6->GetAddress(), other6->GetAddress(), 16)==0;
}
return false;
}
bool NetworkAddress::operator!=(const NetworkAddress &other){
return !(*this == other);
}
IPv4Address::IPv4Address(std::string addr){
#ifndef _WIN32
this->address=NetworkSocketPosix::StringToV4Address(addr);
#else
this->address=NetworkSocketWinsock::StringToV4Address(addr);
#endif
}
IPv4Address::IPv4Address(uint32_t addr){
this->address=addr;
}
IPv4Address::IPv4Address(){
this->address=0;
}
std::string IPv4Address::ToString(){
#ifndef _WIN32
return NetworkSocketPosix::V4AddressToString(address);
#else
return NetworkSocketWinsock::V4AddressToString(address);
#endif
}
/*sockaddr &IPv4Address::ToSockAddr(uint16_t port){
sockaddr_in sa;
sa.sin_family=AF_INET;
sa.sin_addr=addr;
sa.sin_port=port;
return *((sockaddr *) &sa);
}*/
uint32_t IPv4Address::GetAddress(){
return address;
}
IPv6Address::IPv6Address(std::string addr){
#ifndef _WIN32
NetworkSocketPosix::StringToV6Address(addr, this->address);
#else
NetworkSocketWinsock::StringToV6Address(addr, this->address);
#endif
}
IPv6Address::IPv6Address(uint8_t addr[16]){
memcpy(address, addr, 16);
}
IPv6Address::IPv6Address(){
memset(address, 0, 16);
}
std::string IPv6Address::ToString(){
return "";
}
/*sockaddr &IPv6Address::ToSockAddr(uint16_t port){
sockaddr_in6 sa;
sa.sin6_family=AF_INET6;
sa.sin6_addr=addr;
sa.sin6_port=port;
return *((sockaddr *) &sa);
}*/
const uint8_t *IPv6Address::GetAddress(){
return address;
}
bool NetworkSocket::Select(std::vector<NetworkSocket *> &readFds, std::vector<NetworkSocket *> &errorFds, SocketSelectCanceller *canceller){
#ifndef _WIN32
return NetworkSocketPosix::Select(readFds, errorFds, canceller);
#else
return NetworkSocketWinsock::Select(readFds, errorFds, canceller);
#endif
}
SocketSelectCanceller::~SocketSelectCanceller(){
}
SocketSelectCanceller *SocketSelectCanceller::Create(){
#ifndef _WIN32
return new SocketSelectCancellerPosix();
#else
return new SocketSelectCancellerWin32();
#endif
}
NetworkSocketTCPObfuscated::NetworkSocketTCPObfuscated(NetworkSocket *wrapped) : NetworkSocketWrapper(PROTO_TCP){
this->wrapped=wrapped;
}
NetworkSocketTCPObfuscated::~NetworkSocketTCPObfuscated(){
if(wrapped)
delete wrapped;
}
NetworkSocket *NetworkSocketTCPObfuscated::GetWrapped(){
return wrapped;
}
void NetworkSocketTCPObfuscated::InitConnection(){
unsigned char buf[64];
GenerateTCPO2States(buf, &recvState, &sendState);
wrapped->Send(buf, 64);
}
void NetworkSocketTCPObfuscated::Send(NetworkPacket *packet){
BufferOutputStream os(packet->length+4);
size_t len=packet->length/4;
if(len<0x7F){
os.WriteByte((unsigned char)len);
}else{
os.WriteByte(0x7F);
os.WriteByte((unsigned char)(len & 0xFF));
os.WriteByte((unsigned char)((len >> 8) & 0xFF));
os.WriteByte((unsigned char)((len >> 16) & 0xFF));
}
os.WriteBytes(packet->data, packet->length);
EncryptForTCPO2(os.GetBuffer(), os.GetLength(), &sendState);
wrapped->Send(os.GetBuffer(), os.GetLength());
//LOGD("Sent %u bytes", os.GetLength());
}
void NetworkSocketTCPObfuscated::Receive(NetworkPacket *packet){
unsigned char len1;
size_t packetLen=0;
size_t offset=0;
size_t len;
wrapped->Receive(&len1, 1);
/*if(len<=0)
goto failed;*/
EncryptForTCPO2(&len1, 1, &recvState);
if(len1<0x7F){
packetLen=(size_t)len1*4;
}else{
unsigned char len2[3];
len=wrapped->Receive(len2, 3);
/*if(len<=0)
goto failed;*/
EncryptForTCPO2(len2, 3, &recvState);
packetLen=((size_t)len2[0] | ((size_t)len2[1] << 8) | ((size_t)len2[2] << 16))*4;
}
if(packetLen>packet->length){
LOGW("packet too big to fit into buffer (%u vs %u)", (unsigned int)packetLen, (unsigned int)packet->length);
packet->length=0;
return;
}
while(offset<packetLen){
len=wrapped->Receive(packet->data+offset, packetLen-offset);
/*if(len<=0)
goto failed;*/
offset+=len;
}
EncryptForTCPO2(packet->data, packetLen, &recvState);
//packet->address=&itr->address;
packet->length=packetLen;
//packet->port=itr->port;
packet->protocol=PROTO_TCP;
packet->address=wrapped->GetConnectedAddress();
packet->port=wrapped->GetConnectedPort();
}
void NetworkSocketTCPObfuscated::Open(){
}
void NetworkSocketTCPObfuscated::Close(){
wrapped->Close();
}
void NetworkSocketTCPObfuscated::Connect(NetworkAddress *address, uint16_t port){
}
bool NetworkSocketTCPObfuscated::IsFailed(){
return wrapped->IsFailed();
}
NetworkSocketSOCKS5Proxy::NetworkSocketSOCKS5Proxy(NetworkSocket *tcp, NetworkSocket *udp, std::string username, std::string password) : NetworkSocketWrapper(udp ? PROTO_UDP : PROTO_TCP){
this->tcp=tcp;
this->udp=udp;
this->username=username;
this->password=password;
connectedAddress=NULL;
}
NetworkSocketSOCKS5Proxy::~NetworkSocketSOCKS5Proxy(){
delete tcp;
if(connectedAddress)
delete connectedAddress;
}
void NetworkSocketSOCKS5Proxy::Send(NetworkPacket *packet){
if(protocol==PROTO_TCP){
tcp->Send(packet);
}else if(protocol==PROTO_UDP){
unsigned char buf[1500];
BufferOutputStream out(buf, sizeof(buf));
out.WriteInt16(0); // RSV
out.WriteByte(0); // FRAG
IPv4Address* v4=dynamic_cast<IPv4Address*>(packet->address);
IPv6Address* v6=dynamic_cast<IPv6Address*>(packet->address);
if(v4){
out.WriteByte(1); // ATYP (IPv4)
out.WriteInt32(v4->GetAddress());
}else{
out.WriteByte(4); // ATYP (IPv6)
out.WriteBytes((unsigned char *) v6->GetAddress(), 16);
}
out.WriteInt16(htons(packet->port));
out.WriteBytes(packet->data, packet->length);
NetworkPacket p;
p.data=buf;
p.length=out.GetLength();
p.address=connectedAddress;
p.port=connectedPort;
p.protocol=PROTO_UDP;
udp->Send(&p);
}
}
void NetworkSocketSOCKS5Proxy::Receive(NetworkPacket *packet){
if(protocol==PROTO_TCP){
tcp->Receive(packet);
}else if(protocol==PROTO_UDP){
unsigned char buf[1500];
NetworkPacket p;
p.data=buf;
p.length=sizeof(buf);
udp->Receive(&p);
if(p.length && p.address && *p.address==*connectedAddress && p.port==connectedPort){
BufferInputStream in(buf, p.length);
in.ReadInt16(); // RSV
in.ReadByte(); // FRAG
unsigned char atyp=in.ReadByte();
if(atyp==1){ // IPv4
lastRecvdV4=IPv4Address((uint32_t) in.ReadInt32());
packet->address=&lastRecvdV4;
}else if(atyp==4){ // IPv6
unsigned char addr[16];
in.ReadBytes(addr, 16);
lastRecvdV6=IPv6Address(addr);
packet->address=&lastRecvdV6;
}
packet->port=ntohs(in.ReadInt16());
if(packet->length>=in.Remaining()){
packet->length=in.Remaining();
in.ReadBytes(packet->data, in.Remaining());
}else{
packet->length=0;
LOGW("socks5: received packet too big");
}
}
}
}
void NetworkSocketSOCKS5Proxy::Open(){
if(protocol==PROTO_UDP){
unsigned char buf[1024];
BufferOutputStream out(buf, sizeof(buf));
out.WriteByte(5); // VER
out.WriteByte(3); // CMD (UDP ASSOCIATE)
out.WriteByte(0); // RSV
out.WriteByte(1); // ATYP (IPv4)
out.WriteInt32(0); // DST.ADDR
out.WriteInt16(0); // DST.PORT
tcp->Send(buf, out.GetLength());
size_t l=tcp->Receive(buf, sizeof(buf));
if(l<2 || tcp->IsFailed()){
LOGW("socks5: udp associate failed");
failed=true;
return;
}
try{
BufferInputStream in(buf, l);
unsigned char ver=in.ReadByte();
unsigned char rep=in.ReadByte();
if(ver!=5){
LOGW("socks5: udp associate: wrong ver in response");
failed=true;
return;
}
if(rep!=0){
LOGW("socks5: udp associate failed with error %02X", rep);
failed=true;
return;
}
in.ReadByte(); // RSV
unsigned char atyp=in.ReadByte();
if(atyp==1){
uint32_t addr=(uint32_t) in.ReadInt32();
connectedAddress=new IPv4Address(addr);
}else if(atyp==3){
unsigned char len=in.ReadByte();
char domain[256];
memset(domain, 0, sizeof(domain));
in.ReadBytes((unsigned char*)domain, len);
LOGD("address type is domain, address=%s", domain);
connectedAddress=ResolveDomainName(std::string(domain));
if(!connectedAddress){
LOGW("socks5: failed to resolve domain name '%s'", domain);
failed=true;
return;
}
}else if(atyp==4){
unsigned char addr[16];
in.ReadBytes(addr, 16);
connectedAddress=new IPv6Address(addr);
}else{
LOGW("socks5: unknown address type %d", atyp);
failed=true;
return;
}
connectedPort=(uint16_t)ntohs(in.ReadInt16());
tcp->SetTimeouts(0, 0);
LOGV("socks5: udp associate successful, given endpoint %s:%d", connectedAddress->ToString().c_str(), connectedPort);
}catch(std::out_of_range& x){
LOGW("socks5: udp associate response parse failed");
failed=true;
}
}
}
void NetworkSocketSOCKS5Proxy::Close(){
tcp->Close();
}
void NetworkSocketSOCKS5Proxy::Connect(NetworkAddress *address, uint16_t port){
if(!failed){
tcp->SetTimeouts(1, 2);
unsigned char buf[1024];
BufferOutputStream out(buf, sizeof(buf));
out.WriteByte(5); // VER
out.WriteByte(1); // CMD (CONNECT)
out.WriteByte(0); // RSV
IPv4Address* v4=dynamic_cast<IPv4Address*>(address);
IPv6Address* v6=dynamic_cast<IPv6Address*>(address);
if(v4){
out.WriteByte(1); // ATYP (IPv4)
out.WriteInt32(v4->GetAddress());
}else if(v6){
out.WriteByte(4); // ATYP (IPv6)
out.WriteBytes((unsigned char*)v6->GetAddress(), 16);
}else{
LOGW("socks5: unknown address type");
failed=true;
return;
}
out.WriteInt16(htons(port)); // DST.PORT
tcp->Send(buf, out.GetLength());
size_t l=tcp->Receive(buf, sizeof(buf));
if(l<2 || tcp->IsFailed()){
LOGW("socks5: connect failed")
failed=true;
return;
}
BufferInputStream in(buf, l);
unsigned char ver=in.ReadByte();
if(ver!=5){
LOGW("socks5: connect: wrong ver in response");
failed=true;
return;
}
unsigned char rep=in.ReadByte();
if(rep!=0){
LOGW("socks5: connect: failed with error %02X", rep);
failed=true;
return;
}
connectedAddress=v4 ? (NetworkAddress*)new IPv4Address(*v4) : (NetworkAddress*)new IPv6Address(*v6);
connectedPort=port;
LOGV("socks5: connect succeeded");
tcp->SetTimeouts(5, 60);
}
}
NetworkSocket *NetworkSocketSOCKS5Proxy::GetWrapped(){
return protocol==PROTO_TCP ? tcp : udp;
}
void NetworkSocketSOCKS5Proxy::InitConnection(){
unsigned char buf[1024];
tcp->SetTimeouts(1, 2);
BufferOutputStream p(buf, sizeof(buf));
p.WriteByte(5); // VER
if(!username.empty()){
p.WriteByte(2); // NMETHODS
p.WriteByte(0); // no auth
p.WriteByte(2); // user/pass
}else{
p.WriteByte(1); // NMETHODS
p.WriteByte(0); // no auth
}
tcp->Send(buf, p.GetLength());
size_t l=tcp->Receive(buf, sizeof(buf));
if(l<2 || tcp->IsFailed()){
failed=true;
return;
}
BufferInputStream in(buf, l);
unsigned char ver=in.ReadByte();
unsigned char chosenMethod=in.ReadByte();
LOGV("socks5: VER=%02X, METHOD=%02X", ver, chosenMethod);
if(ver!=5){
LOGW("socks5: incorrect VER in response");
failed=true;
return;
}
if(chosenMethod==0){
// connected, no further auth needed
}else if(chosenMethod==2 && !username.empty()){
p.Reset();
p.WriteByte(1); // VER
p.WriteByte((unsigned char)(username.length()>255 ? 255 : username.length())); // ULEN
p.WriteBytes((unsigned char*)username.c_str(), username.length()>255 ? 255 : username.length()); // UNAME
p.WriteByte((unsigned char)(password.length()>255 ? 255 : password.length())); // PLEN
p.WriteBytes((unsigned char*)password.c_str(), password.length()>255 ? 255 : password.length()); // PASSWD
tcp->Send(buf, p.GetLength());
l=tcp->Receive(buf, sizeof(buf));
if(l<2 || tcp->IsFailed()){
failed=true;
return;
}
in=BufferInputStream(buf, l);
ver=in.ReadByte();
unsigned char status=in.ReadByte();
LOGV("socks5: auth response VER=%02X, STATUS=%02X", ver, status);
if(ver!=1){
LOGW("socks5: auth response VER is incorrect");
failed=true;
return;
}
if(status!=0){
LOGW("socks5: username/password auth failed");
failed=true;
return;
}
}else{
LOGW("socks5: unsupported auth method");
failed=true;
return;
}
tcp->SetTimeouts(5, 60);
}
bool NetworkSocketSOCKS5Proxy::IsFailed(){
return NetworkSocket::IsFailed() || tcp->IsFailed();
}
NetworkAddress *NetworkSocketSOCKS5Proxy::GetConnectedAddress(){
return connectedAddress;
}
uint16_t NetworkSocketSOCKS5Proxy::GetConnectedPort(){
return connectedPort;
}

View File

@ -0,0 +1,173 @@
//
// Created by Grishka on 29.03.17.
//
#ifndef LIBTGVOIP_NETWORKSOCKET_H
#define LIBTGVOIP_NETWORKSOCKET_H
#include <stdint.h>
#include <string>
#include <vector>
namespace tgvoip {
enum NetworkProtocol{
PROTO_UDP=0,
PROTO_TCP
};
struct TCPO2State{
unsigned char key[32];
unsigned char iv[16];
unsigned char ecount[16];
uint32_t num;
};
class NetworkAddress{
public:
virtual std::string ToString()=0;
bool operator==(const NetworkAddress& other);
bool operator!=(const NetworkAddress& other);
virtual ~NetworkAddress()=default;
};
class IPv4Address : public NetworkAddress{
public:
IPv4Address(std::string addr);
IPv4Address(uint32_t addr);
IPv4Address();
virtual std::string ToString();
//virtual sockaddr& ToSockAddr(uint16_t port);
uint32_t GetAddress();
private:
uint32_t address;
};
class IPv6Address : public NetworkAddress{
public:
IPv6Address(std::string addr);
IPv6Address(uint8_t addr[16]);
IPv6Address();
virtual std::string ToString();
//virtual sockaddr& ToSockAddr(uint16_t port);
const uint8_t* GetAddress();
private:
uint8_t address[16];
};
struct NetworkPacket{
unsigned char* data;
size_t length;
NetworkAddress* address;
uint16_t port;
NetworkProtocol protocol;
};
typedef struct NetworkPacket NetworkPacket;
class SocketSelectCanceller{
public:
virtual ~SocketSelectCanceller();
virtual void CancelSelect()=0;
static SocketSelectCanceller* Create();
};
class NetworkSocket{
public:
NetworkSocket(NetworkProtocol protocol);
virtual ~NetworkSocket();
virtual void Send(NetworkPacket* packet)=0;
virtual void Receive(NetworkPacket* packet)=0;
size_t Receive(unsigned char* buffer, size_t len);
size_t Send(unsigned char* buffer, size_t len);
virtual void Open()=0;
virtual void Close()=0;
virtual uint16_t GetLocalPort(){ return 0; };
virtual void Connect(NetworkAddress* address, uint16_t port)=0;
virtual std::string GetLocalInterfaceInfo(IPv4Address* inet4addr, IPv6Address* inet6addr);
virtual void OnActiveInterfaceChanged(){};
virtual NetworkAddress* GetConnectedAddress(){ return NULL; };
virtual uint16_t GetConnectedPort(){ return 0; };
virtual void SetTimeouts(int sendTimeout, int recvTimeout){};
virtual bool IsFailed();
void SetSocksProxy(IPv4Address* addr, uint16_t port, char* username, char* password);
static NetworkSocket* Create(NetworkProtocol protocol);
static IPv4Address* ResolveDomainName(std::string name);
static bool Select(std::vector<NetworkSocket*>& readFds, std::vector<NetworkSocket*>& errorFds, SocketSelectCanceller* canceller);
protected:
virtual uint16_t GenerateLocalPort();
virtual void SetMaxPriority();
static void GenerateTCPO2States(unsigned char* buffer, TCPO2State* recvState, TCPO2State* sendState);
static void EncryptForTCPO2(unsigned char* buffer, size_t len, TCPO2State* state);
double ipv6Timeout;
unsigned char nat64Prefix[12];
bool failed;
NetworkProtocol protocol;
IPv4Address* proxyAddress;
uint16_t proxyPort;
char* proxyUsername;
char* proxyPassword;
};
class NetworkSocketWrapper : public NetworkSocket{
public:
NetworkSocketWrapper(NetworkProtocol protocol) : NetworkSocket(protocol){};
virtual ~NetworkSocketWrapper(){};
virtual NetworkSocket* GetWrapped()=0;
virtual void InitConnection()=0;
};
class NetworkSocketTCPObfuscated : public NetworkSocketWrapper{
public:
NetworkSocketTCPObfuscated(NetworkSocket* wrapped);
virtual ~NetworkSocketTCPObfuscated();
virtual NetworkSocket* GetWrapped();
virtual void InitConnection();
virtual void Send(NetworkPacket *packet);
virtual void Receive(NetworkPacket *packet);
virtual void Open();
virtual void Close();
virtual void Connect(NetworkAddress *address, uint16_t port);
virtual bool IsFailed();
private:
NetworkSocket* wrapped;
TCPO2State recvState;
TCPO2State sendState;
};
class NetworkSocketSOCKS5Proxy : public NetworkSocketWrapper{
public:
NetworkSocketSOCKS5Proxy(NetworkSocket* tcp, NetworkSocket* udp, std::string username, std::string password);
virtual ~NetworkSocketSOCKS5Proxy();
virtual void Send(NetworkPacket *packet);
virtual void Receive(NetworkPacket *packet);
virtual void Open();
virtual void Close();
virtual void Connect(NetworkAddress *address, uint16_t port);
virtual NetworkSocket *GetWrapped();
virtual void InitConnection();
virtual bool IsFailed();
virtual NetworkAddress *GetConnectedAddress();
virtual uint16_t GetConnectedPort();
private:
NetworkSocket* tcp;
NetworkSocket* udp;
std::string username;
std::string password;
NetworkAddress* connectedAddress;
uint16_t connectedPort;
IPv4Address lastRecvdV4;
IPv6Address lastRecvdV6;
};
}
#endif //LIBTGVOIP_NETWORKSOCKET_H

View File

@ -0,0 +1,257 @@
//
// 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 "OpusDecoder.h"
#include "audio/Resampler.h"
#include "logging.h"
#include <assert.h>
#define PACKET_SIZE (960*2)
using namespace tgvoip;
tgvoip::OpusDecoder::OpusDecoder(MediaStreamItf *dst) : semaphore(32, 0){
//this->source=source;
dst->SetCallback(OpusDecoder::Callback, this);
dec=opus_decoder_create(48000, 1, NULL);
//test=fopen("/sdcard/test.raw", "wb");
buffer=(unsigned char *) malloc(8192);
//lastDecoded=(unsigned char*) malloc(960*2);
lastDecoded=NULL;
lastDecodedLen=0;
outputBufferSize=0;
lastDecodedOffset=0;
decodedQueue=new BlockingQueue<unsigned char*>(33);
bufferPool=new BufferPool(PACKET_SIZE, 32);
echoCanceller=NULL;
frameDuration=20;
}
tgvoip::OpusDecoder::~OpusDecoder(){
opus_decoder_destroy(dec);
free(buffer);
delete bufferPool;
delete decodedQueue;
}
void tgvoip::OpusDecoder::SetEchoCanceller(EchoCanceller* canceller){
echoCanceller=canceller;
}
size_t tgvoip::OpusDecoder::Callback(unsigned char *data, size_t len, void *param){
((OpusDecoder*)param)->HandleCallback(data, len);
return 0;
}
void tgvoip::OpusDecoder::HandleCallback(unsigned char *data, size_t len){
if(!running){
memset(data, 0, len);
return;
}
if(outputBufferSize==0){
outputBufferSize=len;
int packetsNeeded;
if(len>PACKET_SIZE)
packetsNeeded=len/PACKET_SIZE;
else
packetsNeeded=1;
packetsNeeded*=2;
semaphore.Release(packetsNeeded);
}
assert(outputBufferSize==len && "output buffer size is supposed to be the same throughout callbacks");
if(len>PACKET_SIZE){
int count=len/PACKET_SIZE;
int i;
for(i=0;i<count;i++){
lastDecoded=(unsigned char*) decodedQueue->GetBlocking();
if(!lastDecoded)
return;
memcpy(data+(i*PACKET_SIZE), lastDecoded, PACKET_SIZE);
if(echoCanceller)
echoCanceller->SpeakerOutCallback(data, PACKET_SIZE);
bufferPool->Reuse(lastDecoded);
}
semaphore.Release(count);
}else if(len==PACKET_SIZE){
lastDecoded=(unsigned char*) decodedQueue->GetBlocking();
if(!lastDecoded)
return;
memcpy(data, lastDecoded, PACKET_SIZE);
bufferPool->Reuse(lastDecoded);
semaphore.Release();
lock_mutex(mutex);
if(echoCanceller)
echoCanceller->SpeakerOutCallback(data, PACKET_SIZE);
unlock_mutex(mutex);
}else if(len<PACKET_SIZE){
if(lastDecodedOffset==0){
lastDecoded=(unsigned char*) decodedQueue->GetBlocking();
}
if(!lastDecoded)
return;
memcpy(data, lastDecoded+lastDecodedOffset, len);
lastDecodedOffset+=len;
if(lastDecodedOffset>=PACKET_SIZE){
if(echoCanceller)
echoCanceller->SpeakerOutCallback(lastDecoded, PACKET_SIZE);
lastDecodedOffset=0;
bufferPool->Reuse(lastDecoded);
//LOGV("before req packet, qsize=%d", decodedQueue->Size());
if(decodedQueue->Size()==0)
semaphore.Release(2);
else
semaphore.Release();
}
}
/*if(lastDecodedLen){
LOGV("ldl=%d, l=%d", lastDecodedLen, len);
if(len==PACKET_SIZE){
memcpy(data, lastDecoded, len);
packetsNeeded=1;
}else if(len>PACKET_SIZE){
memcpy(data, lastDecoded, len);
//LOGV("ldl=%d, l=%d", lastDecodedLen, len);
packetsNeeded=len/PACKET_SIZE;
}else if(len<PACKET_SIZE){
memcpy(data, lastDecoded+lastDecodedOffset, len);
lastDecodedOffset+=len;
if(lastDecodedOffset>=PACKET_SIZE){
packetsNeeded=1;
lastDecodedOffset=0;
}
}
}else{
LOGW("skipping callback");
if(len>PACKET_SIZE)
packetsNeeded=len/PACKET_SIZE;
else
packetsNeeded=1;
}*/
/*if(packetsNeeded>0){
lock_mutex(mutex);
notify_lock(lock);
unlock_mutex(mutex);
}*/
}
void tgvoip::OpusDecoder::Start(){
init_mutex(mutex);
running=true;
start_thread(thread, OpusDecoder::StartThread, this);
set_thread_priority(thread, get_thread_max_priority());
set_thread_name(thread, "opus_decoder");
}
void tgvoip::OpusDecoder::Stop(){
if(!running)
return;
running=false;
semaphore.Release();
join_thread(thread);
free_mutex(mutex);
}
void* tgvoip::OpusDecoder::StartThread(void *param){
((tgvoip::OpusDecoder*)param)->RunThread();
return NULL;
}
void tgvoip::OpusDecoder::RunThread(){
unsigned char nextBuffer[8192];
unsigned char decodeBuffer[8192];
int i;
int packetsPerFrame=frameDuration/20;
bool first=true;
LOGI("decoder: packets per frame %d", packetsPerFrame);
size_t nextLen=0;
while(running){
//LOGV("after wait, running=%d", running);
//LOGD("Will get %d packets", packetsNeeded);
//lastDecodedLen=0;
memcpy(buffer, nextBuffer, nextLen);
size_t inLen=nextLen;
//nextLen=InvokeCallback(nextBuffer, 8192);
int playbackDuration=0;
nextLen=jitterBuffer->HandleOutput(nextBuffer, 8192, 0, &playbackDuration);
if(first){
first=false;
continue;
}
//LOGV("Before decode, len=%d", inLen);
if(!inLen){
LOGV("Trying to recover late packet");
inLen=jitterBuffer->HandleOutput(buffer, 8192, -2, &playbackDuration);
if(inLen)
LOGV("Decoding late packet");
}
int size;
if(inLen || nextLen)
size=opus_decode(dec, inLen ? buffer : nextBuffer, inLen ? inLen : nextLen, (opus_int16*) decodeBuffer, packetsPerFrame*960, inLen ? 0 : 1);
else{ // do packet loss concealment
size=opus_decode(dec, NULL, 0, (opus_int16 *) decodeBuffer, packetsPerFrame*960, 0);
LOGV("PLC");
}
if(size<0)
LOGW("decoder: opus_decode error %d", size);
//LOGV("After decode, size=%d", size);
//LOGD("playbackDuration=%d", playbackDuration);
unsigned char* processedBuffer;
if(playbackDuration==80){
processedBuffer=buffer;
audio::Resampler::Rescale60To80((int16_t*) decodeBuffer, (int16_t*) processedBuffer);
}else if(playbackDuration==40){
processedBuffer=buffer;
audio::Resampler::Rescale60To40((int16_t*) decodeBuffer, (int16_t*) processedBuffer);
}else{
processedBuffer=decodeBuffer;
}
for(i=0;i</*packetsPerFrame*/ playbackDuration/20;i++){
semaphore.Acquire();
if(!running){
LOGI("==== decoder exiting ====");
return;
}
unsigned char *buf=bufferPool->Get();
if(buf){
if(size>0){
memcpy(buf, processedBuffer+(PACKET_SIZE*i), PACKET_SIZE);
}else{
LOGE("Error decoding, result=%d", size);
memset(buf, 0, PACKET_SIZE);
}
decodedQueue->Put(buf);
}else{
LOGW("decoder: no buffers left!");
}
//LOGD("packets needed: %d", packetsNeeded);
}
}
}
void tgvoip::OpusDecoder::SetFrameDuration(uint32_t duration){
frameDuration=duration;
}
void tgvoip::OpusDecoder::ResetQueue(){
/*lock_mutex(mutex);
packetsNeeded=0;
unlock_mutex(mutex);
while(decodedQueue->Size()>0){
bufferPool->Reuse((unsigned char *) decodedQueue->Get());
}*/
}
void tgvoip::OpusDecoder::SetJitterBuffer(JitterBuffer* jitterBuffer){
this->jitterBuffer=jitterBuffer;
}

View File

@ -0,0 +1,56 @@
//
// 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 LIBTGVOIP_OPUSDECODER_H
#define LIBTGVOIP_OPUSDECODER_H
#include "MediaStreamItf.h"
#include "opus.h"
#include "threading.h"
#include "BlockingQueue.h"
#include "BufferPool.h"
#include "EchoCanceller.h"
#include "JitterBuffer.h"
#include <stdio.h>
namespace tgvoip{
class OpusDecoder {
public:
virtual void Start();
virtual void Stop();
OpusDecoder(MediaStreamItf* dst);
virtual ~OpusDecoder();
void HandleCallback(unsigned char* data, size_t len);
void SetEchoCanceller(EchoCanceller* canceller);
void SetFrameDuration(uint32_t duration);
void ResetQueue();
void SetJitterBuffer(JitterBuffer* jitterBuffer);
private:
static size_t Callback(unsigned char* data, size_t len, void* param);
static void* StartThread(void* param);
void RunThread();
::OpusDecoder* dec;
BlockingQueue<unsigned char*>* decodedQueue;
BufferPool* bufferPool;
unsigned char* buffer;
unsigned char* lastDecoded;
size_t lastDecodedLen, lastDecodedOffset;
size_t outputBufferSize;
bool running;
tgvoip_thread_t thread;
Semaphore semaphore;
tgvoip_mutex_t mutex;
uint32_t frameDuration;
EchoCanceller* echoCanceller;
JitterBuffer* jitterBuffer;
};
}
#endif //LIBTGVOIP_OPUSDECODER_H

View File

@ -0,0 +1,160 @@
//
// libtgvoip is free and unencumbered public domain software.
// For more information, see http://unlicense.org or the UNLICENSE file
// you should have received with this source code distribution.
//
#include "OpusEncoder.h"
#include <assert.h>
#include "logging.h"
#include "VoIPServerConfig.h"
tgvoip::OpusEncoder::OpusEncoder(MediaStreamItf *source):queue(11), bufferPool(960*2, 10){
this->source=source;
source->SetCallback(tgvoip::OpusEncoder::Callback, this);
enc=opus_encoder_create(48000, 1, OPUS_APPLICATION_VOIP, NULL);
opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(10));
opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC(15));
opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC(1));
opus_encoder_ctl(enc, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND));
requestedBitrate=32000;
currentBitrate=0;
running=false;
echoCanceller=NULL;
complexity=10;
frameDuration=20;
mediumCorrectionBitrate=ServerConfig::GetSharedInstance()->GetInt("audio_medium_fec_bitrate", 10000);
strongCorrectionBitrate=ServerConfig::GetSharedInstance()->GetInt("audio_strong_fec_bitrate", 8000);
mediumCorrectionMultiplier=ServerConfig::GetSharedInstance()->GetDouble("audio_medium_fec_multiplier", 1.5);
strongCorrectionMultiplier=ServerConfig::GetSharedInstance()->GetDouble("audio_strong_fec_multiplier", 2.0);
}
tgvoip::OpusEncoder::~OpusEncoder(){
opus_encoder_destroy(enc);
}
void tgvoip::OpusEncoder::Start(){
if(running)
return;
running=true;
start_thread(thread, StartThread, this);
set_thread_priority(thread, get_thread_max_priority());
set_thread_name(thread, "opus_encoder");
}
void tgvoip::OpusEncoder::Stop(){
if(!running)
return;
running=false;
queue.Put(NULL);
join_thread(thread);
}
void tgvoip::OpusEncoder::SetBitrate(uint32_t bitrate){
requestedBitrate=bitrate;
}
void tgvoip::OpusEncoder::Encode(unsigned char *data, size_t len){
if(requestedBitrate!=currentBitrate){
opus_encoder_ctl(enc, OPUS_SET_BITRATE(requestedBitrate));
currentBitrate=requestedBitrate;
LOGV("opus_encoder: setting bitrate to %u", currentBitrate);
}
int32_t r=opus_encode(enc, (int16_t*)data, len/2, buffer, 4096);
if(r<=0){
LOGE("Error encoding: %d", r);
}else if(r==1){
LOGW("DTX");
}else if(running){
//LOGV("Packet size = %d", r);
InvokeCallback(buffer, (size_t)r);
}
}
size_t tgvoip::OpusEncoder::Callback(unsigned char *data, size_t len, void* param){
OpusEncoder* e=(OpusEncoder*)param;
unsigned char* buf=e->bufferPool.Get();
if(buf){
assert(len==960*2);
memcpy(buf, data, 960*2);
e->queue.Put(buf);
}else{
LOGW("opus_encoder: no buffer slots left");
if(e->complexity>1){
e->complexity--;
opus_encoder_ctl(e->enc, OPUS_SET_COMPLEXITY(e->complexity));
}
}
return 0;
}
uint32_t tgvoip::OpusEncoder::GetBitrate(){
return requestedBitrate;
}
void tgvoip::OpusEncoder::SetEchoCanceller(EchoCanceller* aec){
echoCanceller=aec;
}
void* tgvoip::OpusEncoder::StartThread(void* arg){
((OpusEncoder*)arg)->RunThread();
return NULL;
}
void tgvoip::OpusEncoder::RunThread(){
unsigned char buf[960*2];
uint32_t bufferedCount=0;
uint32_t packetsPerFrame=frameDuration/20;
LOGV("starting encoder, packets per frame=%d", packetsPerFrame);
unsigned char* frame;
if(packetsPerFrame>1)
frame=(unsigned char *) malloc(960*2*packetsPerFrame);
else
frame=NULL;
while(running){
unsigned char* packet=(unsigned char*)queue.GetBlocking();
if(packet){
if(echoCanceller)
echoCanceller->ProcessInput(packet, buf, 960*2);
else
memcpy(buf, packet, 960*2);
if(packetsPerFrame==1){
Encode(buf, 960*2);
}else{
memcpy(frame+(960*2*bufferedCount), buf, 960*2);
bufferedCount++;
if(bufferedCount==packetsPerFrame){
Encode(frame, 960*2*packetsPerFrame);
bufferedCount=0;
}
}
bufferPool.Reuse(packet);
}
}
if(frame)
free(frame);
}
void tgvoip::OpusEncoder::SetOutputFrameDuration(uint32_t duration){
frameDuration=duration;
}
void tgvoip::OpusEncoder::SetPacketLoss(int percent){
packetLossPercent=percent;
double multiplier=1;
if(currentBitrate<=strongCorrectionBitrate)
multiplier=strongCorrectionMultiplier;
else if(currentBitrate<=mediumCorrectionBitrate)
multiplier=mediumCorrectionMultiplier;
opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC((int)(percent*multiplier)));
opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(percent>17 ? OPUS_AUTO : OPUS_BANDWIDTH_FULLBAND));
}
int tgvoip::OpusEncoder::GetPacketLoss(){
return packetLossPercent;
}

View File

@ -0,0 +1,59 @@
//
// 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 LIBTGVOIP_OPUSENCODER_H
#define LIBTGVOIP_OPUSENCODER_H
#include "MediaStreamItf.h"
#include "opus.h"
#include "threading.h"
#include "BlockingQueue.h"
#include "BufferPool.h"
#include "EchoCanceller.h"
#include <stdint.h>
namespace tgvoip{
class OpusEncoder : public MediaStreamItf{
public:
OpusEncoder(MediaStreamItf* source);
virtual ~OpusEncoder();
virtual void Start();
virtual void Stop();
void SetBitrate(uint32_t bitrate);
void SetEchoCanceller(EchoCanceller* aec);
void SetOutputFrameDuration(uint32_t duration);
void SetPacketLoss(int percent);
int GetPacketLoss();
uint32_t GetBitrate();
private:
static size_t Callback(unsigned char* data, size_t len, void* param);
static void* StartThread(void* arg);
void RunThread();
void Encode(unsigned char* data, size_t len);
MediaStreamItf* source;
::OpusEncoder* enc;
unsigned char buffer[4096];
uint32_t requestedBitrate;
uint32_t currentBitrate;
tgvoip_thread_t thread;
BlockingQueue<unsigned char*> queue;
BufferPool bufferPool;
EchoCanceller* echoCanceller;
int complexity;
bool running;
uint32_t frameDuration;
int packetLossPercent;
uint32_t mediumCorrectionBitrate;
uint32_t strongCorrectionBitrate;
double mediumCorrectionMultiplier;
double strongCorrectionMultiplier;
};
}
#endif //LIBTGVOIP_OPUSENCODER_H

24
Telegram/ThirdParty/libtgvoip/UNLICENSE vendored Normal file
View File

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,509 @@
//
// 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 __VOIPCONTROLLER_H
#define __VOIPCONTROLLER_H
#ifndef _WIN32
#include <arpa/inet.h>
#include <netinet/in.h>
#endif
#ifdef __APPLE__
#include <TargetConditionals.h>
#endif
#include <stdint.h>
#include <vector>
#include <string>
#include <map>
#include "audio/AudioInput.h"
#include "BlockingQueue.h"
#include "BufferOutputStream.h"
#include "audio/AudioOutput.h"
#include "JitterBuffer.h"
#include "OpusDecoder.h"
#include "OpusEncoder.h"
#include "EchoCanceller.h"
#include "CongestionControl.h"
#include "NetworkSocket.h"
#define LIBTGVOIP_VERSION "1.0"
#define STATE_WAIT_INIT 1
#define STATE_WAIT_INIT_ACK 2
#define STATE_ESTABLISHED 3
#define STATE_FAILED 4
#define STATE_RECONNECTING 5
#define TGVOIP_ERROR_UNKNOWN 0
#define TGVOIP_ERROR_INCOMPATIBLE 1
#define TGVOIP_ERROR_TIMEOUT 2
#define TGVOIP_ERROR_AUDIO_IO 3
#define NET_TYPE_UNKNOWN 0
#define NET_TYPE_GPRS 1
#define NET_TYPE_EDGE 2
#define NET_TYPE_3G 3
#define NET_TYPE_HSPA 4
#define NET_TYPE_LTE 5
#define NET_TYPE_WIFI 6
#define NET_TYPE_ETHERNET 7
#define NET_TYPE_OTHER_HIGH_SPEED 8
#define NET_TYPE_OTHER_LOW_SPEED 9
#define NET_TYPE_DIALUP 10
#define NET_TYPE_OTHER_MOBILE 11
#define EP_TYPE_UDP_P2P_INET 1
#define EP_TYPE_UDP_P2P_LAN 2
#define EP_TYPE_UDP_RELAY 3
#define EP_TYPE_TCP_RELAY 4
#define DATA_SAVING_NEVER 0
#define DATA_SAVING_MOBILE 1
#define DATA_SAVING_ALWAYS 2
#ifdef _WIN32
#undef GetCurrentTime
#endif
struct voip_stream_t{
int32_t userID;
unsigned char id;
unsigned char type;
unsigned char codec;
bool enabled;
uint16_t frameDuration;
};
typedef struct voip_stream_t voip_stream_t;
struct voip_queued_packet_t{
unsigned char type;
unsigned char* data;
size_t length;
uint32_t seqs[16];
double firstSentTime;
double lastSentTime;
double retryInterval;
double timeout;
};
typedef struct voip_queued_packet_t voip_queued_packet_t;
struct voip_config_t{
double init_timeout;
double recv_timeout;
int data_saving;
char logFilePath[256];
char statsDumpFilePath[256];
bool enableAEC;
bool enableNS;
bool enableAGC;
};
typedef struct voip_config_t voip_config_t;
#if defined(__APPLE__) && TARGET_OS_IPHONE
// temporary fix for nasty linking errors
struct voip_legacy_endpoint_t{
const char* address;
const char* address6;
uint16_t port;
int64_t id;
unsigned char peerTag[16];
};
typedef struct voip_legacy_endpoint_t voip_legacy_endpoint_t;
#endif
struct voip_stats_t{
uint64_t bytesSentWifi;
uint64_t bytesRecvdWifi;
uint64_t bytesSentMobile;
uint64_t bytesRecvdMobile;
};
typedef struct voip_stats_t voip_stats_t;
struct voip_crypto_functions_t{
void (*rand_bytes)(uint8_t* buffer, size_t length);
void (*sha1)(uint8_t* msg, size_t length, uint8_t* output);
void (*sha256)(uint8_t* msg, size_t length, uint8_t* output);
void (*aes_ige_encrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv);
void (*aes_ige_decrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv);
void (*aes_ctr_encrypt)(uint8_t* inout, size_t length, uint8_t* key, uint8_t* iv, uint8_t* ecount, uint32_t* num);
};
typedef struct voip_crypto_functions_t voip_crypto_functions_t;
#define SEQ_MAX 0xFFFFFFFF
inline bool seqgt(uint32_t s1, uint32_t s2){
return ((s1>s2) && (s1-s2<=SEQ_MAX/2)) || ((s1<s2) && (s2-s1>SEQ_MAX/2));
}
namespace tgvoip{
enum{
PROXY_NONE=0,
PROXY_SOCKS5,
//PROXY_HTTP
};
class Endpoint{
friend class VoIPController;
public:
Endpoint(int64_t id, uint16_t port, IPv4Address& address, IPv6Address& v6address, char type, unsigned char* peerTag);
Endpoint();
int64_t id;
uint16_t port;
IPv4Address address;
IPv6Address v6address;
char type;
unsigned char peerTag[16];
private:
double lastPingTime;
uint32_t lastPingSeq;
double rtts[6];
double averageRTT;
NetworkSocket* socket;
};
class AudioDevice{
public:
std::string id;
std::string displayName;
};
class AudioOutputDevice : public AudioDevice{
};
class AudioInputDevice : public AudioDevice{
};
class VoIPController
{
public:
VoIPController();
~VoIPController();
/**
* Set the initial endpoints (relays)
* @param endpoints Endpoints converted from phone.PhoneConnection TL objects
* @param allowP2p Whether p2p connectivity is allowed
*/
void SetRemoteEndpoints(std::vector<Endpoint> endpoints, bool allowP2p);
/**
* Initialize and start all the internal threads
*/
void Start();
/**
* Initiate connection
*/
void Connect();
Endpoint& GetRemoteEndpoint();
/**
* Get the debug info string to be displayed in client UI
* @param buffer The buffer to put the string into
* @param len The length of the buffer
*/
void GetDebugString(char* buffer, size_t len);
/**
* Notify the library of network type change
* @param type The new network type
*/
void SetNetworkType(int type);
/**
* Get the average round-trip time for network packets
* @return
*/
double GetAverageRTT();
/**
* Set the function to be called whenever the connection state changes
* @param f
*/
void SetStateCallback(void (*f)(VoIPController*, int));
static double GetCurrentTime();
/**
* Use this field to store any of your context data associated with this call
*/
void* implData;
/**
*
* @param mute
*/
void SetMicMute(bool mute);
/**
*
* @param key
* @param isOutgoing
*/
void SetEncryptionKey(char* key, bool isOutgoing);
/**
*
* @param cfg
*/
void SetConfig(voip_config_t* cfg);
float GetOutputLevel();
void DebugCtl(int request, int param);
/**
*
* @param stats
*/
void GetStats(voip_stats_t* stats);
/**
*
* @return
*/
int64_t GetPreferredRelayID();
/**
*
* @return
*/
int GetLastError();
/**
*
*/
static voip_crypto_functions_t crypto;
/**
*
* @return
*/
static const char* GetVersion();
#ifdef TGVOIP_USE_AUDIO_SESSION
void SetAcquireAudioSession(void (^)(void (^)()));
void ReleaseAudioSession(void (^completion)());
#endif
/**
*
* @return
*/
std::string GetDebugLog();
/**
*
* @param buffer
*/
void GetDebugLog(char* buffer);
size_t GetDebugLogLength();
/**
*
* @return
*/
static std::vector<AudioInputDevice> EnumerateAudioInputs();
/**
*
* @return
*/
static std::vector<AudioOutputDevice> EnumerateAudioOutputs();
/**
*
* @param id
*/
void SetCurrentAudioInput(std::string id);
/**
*
* @param id
*/
void SetCurrentAudioOutput(std::string id);
/**
*
* @return
*/
std::string GetCurrentAudioInputID();
/**
*
* @return
*/
std::string GetCurrentAudioOutputID();
/**
* Set the proxy server to route the data through. Call this before connecting.
* @param protocol PROXY_NONE, PROXY_SOCKS4, or PROXY_SOCKS5
* @param address IP address or domain name of the server
* @param port Port of the server
* @param username Username; empty string for anonymous
* @param password Password; empty string if none
*/
void SetProxy(int protocol, std::string address, uint16_t port, std::string username, std::string password);
/**
* Get the number of signal bars to display in the client UI.
* @return the number of signal bars, from 1 to 4
*/
int GetSignalBarsCount();
/**
* Set the callback to be called when the signal bar count changes.
* @param f
*/
void SetSignalBarsCountCallback(void (*f)(VoIPController*, int));
private:
struct PendingOutgoingPacket{
uint32_t seq;
unsigned char type;
size_t len;
unsigned char* data;
Endpoint* endpoint;
};
enum{
UDP_UNKNOWN=0,
UDP_PING_SENT,
UDP_AVAILABIE,
UDP_NOT_AVAILABLE
};
static void* StartRecvThread(void* arg);
static void* StartSendThread(void* arg);
static void* StartTickThread(void* arg);
void RunRecvThread();
void RunSendThread();
void RunTickThread();
void SendPacket(unsigned char* data, size_t len, Endpoint* ep);
void HandleAudioInput(unsigned char* data, size_t len);
void UpdateAudioBitrate();
void SetState(int state);
void UpdateAudioOutputState();
void SendInit();
void InitUDPProxy();
void UpdateDataSavingState();
void KDF(unsigned char* msgKey, size_t x, unsigned char* aesKey, unsigned char* aesIv);
void WritePacketHeader(uint32_t seq, BufferOutputStream* s, unsigned char type, uint32_t length);
static size_t AudioInputCallback(unsigned char* data, size_t length, void* param);
void SendPublicEndpointsRequest();
void SendPublicEndpointsRequest(Endpoint& relay);
Endpoint* GetEndpointByType(int type);
void SendPacketReliably(unsigned char type, unsigned char* data, size_t len, double retryInterval, double timeout);
uint32_t GenerateOutSeq();
void LogDebugInfo();
void SendUdpPing(Endpoint* endpoint);
int state;
std::vector<Endpoint*> endpoints;
Endpoint* currentEndpoint;
Endpoint* preferredRelay;
Endpoint* peerPreferredRelay;
bool runReceiver;
uint32_t seq;
uint32_t lastRemoteSeq;
uint32_t lastRemoteAckSeq;
uint32_t lastSentSeq;
double remoteAcks[32];
double sentPacketTimes[32];
double recvPacketTimes[32];
uint32_t sendLossCountHistory[32];
uint32_t audioTimestampIn;
uint32_t audioTimestampOut;
tgvoip::audio::AudioInput* audioInput;
tgvoip::audio::AudioOutput* audioOutput;
JitterBuffer* jitterBuffer;
OpusDecoder* decoder;
OpusEncoder* encoder;
BlockingQueue<PendingOutgoingPacket>* sendQueue;
EchoCanceller* echoCanceller;
tgvoip_mutex_t sendBufferMutex;
tgvoip_mutex_t endpointsMutex;
bool stopping;
bool audioOutStarted;
tgvoip_thread_t recvThread;
tgvoip_thread_t sendThread;
tgvoip_thread_t tickThread;
uint32_t packetsRecieved;
uint32_t recvLossCount;
uint32_t prevSendLossCount;
uint32_t firstSentPing;
double rttHistory[32];
bool waitingForAcks;
int networkType;
int dontSendPackets;
int lastError;
bool micMuted;
uint32_t maxBitrate;
void (*stateCallback)(VoIPController*, int);
std::vector<voip_stream_t*> outgoingStreams;
std::vector<voip_stream_t*> incomingStreams;
unsigned char encryptionKey[256];
unsigned char keyFingerprint[8];
unsigned char callID[16];
double stateChangeTime;
bool waitingForRelayPeerInfo;
bool allowP2p;
bool dataSavingMode;
bool dataSavingRequestedByPeer;
std::string activeNetItfName;
double publicEndpointsReqTime;
std::vector<voip_queued_packet_t*> queuedPackets;
tgvoip_mutex_t queuedPacketsMutex;
double connectionInitTime;
double lastRecvPacketTime;
voip_config_t config;
int32_t peerVersion;
CongestionControl* conctl;
voip_stats_t stats;
bool receivedInit;
bool receivedInitAck;
std::vector<std::string> debugLogs;
bool isOutgoing;
NetworkSocket* udpSocket;
NetworkSocket* realUdpSocket;
FILE* statsDump;
std::string currentAudioInput;
std::string currentAudioOutput;
bool useTCP;
bool useUDP;
bool didAddTcpRelays;
double setEstablishedAt;
SocketSelectCanceller* selectCanceller;
NetworkSocket* openingTcpSocket;
BufferPool outgoingPacketsBufferPool;
int udpConnectivityState;
double lastUdpPingTime;
int udpPingCount;
int proxyProtocol;
std::string proxyAddress;
uint16_t proxyPort;
std::string proxyUsername;
std::string proxyPassword;
IPv4Address* resolvedProxyAddress;
int signalBarCount;
void (*signalBarCountCallback)(VoIPController*, int);
/*** server config values ***/
uint32_t maxAudioBitrate;
uint32_t maxAudioBitrateEDGE;
uint32_t maxAudioBitrateGPRS;
uint32_t maxAudioBitrateSaving;
uint32_t initAudioBitrate;
uint32_t initAudioBitrateEDGE;
uint32_t initAudioBitrateGPRS;
uint32_t initAudioBitrateSaving;
uint32_t minAudioBitrate;
uint32_t audioBitrateStepIncr;
uint32_t audioBitrateStepDecr;
double relaySwitchThreshold;
double p2pToRelaySwitchThreshold;
double relayToP2pSwitchThreshold;
double reconnectingTimeout;
#ifdef TGVOIP_USE_AUDIO_SESSION
void (^acquireAudioSession)(void (^)());
bool needNotifyAcquiredAudioSession;
#endif
public:
#ifdef __APPLE__
static double machTimebase;
static uint64_t machTimestart;
#if TARGET_OS_IPHONE
// temporary fix for nasty linking errors
void SetRemoteEndpoints(voip_legacy_endpoint_t* buffer, size_t count, bool allowP2P);
#endif
#endif
#ifdef _WIN32
static int64_t win32TimeScale;
static bool didInitWin32TimeScale;
#endif
};
}
#endif

View File

@ -0,0 +1,101 @@
//
// 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 <stdlib.h>
#include "logging.h"
using namespace tgvoip;
ServerConfig* ServerConfig::sharedInstance=NULL;
ServerConfig::ServerConfig(){
init_mutex(mutex);
}
ServerConfig::~ServerConfig(){
free_mutex(mutex);
}
ServerConfig *ServerConfig::GetSharedInstance(){
if(!sharedInstance)
sharedInstance=new ServerConfig();
return sharedInstance;
}
bool ServerConfig::GetBoolean(std::string name, bool fallback){
MutexGuard sync(mutex);
if(ContainsKey(name)){
std::string val=config[name];
if(val=="true")
return true;
if(val=="false")
return false;
}
return fallback;
}
double ServerConfig::GetDouble(std::string name, double fallback){
MutexGuard 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 ServerConfig::GetInt(std::string name, int32_t fallback){
MutexGuard 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 ServerConfig::GetString(std::string name, std::string fallback){
MutexGuard sync(mutex);
if(ContainsKey(name))
return config[name];
return fallback;
}
void ServerConfig::Update(std::map<std::string, std::string> newValues){
MutexGuard sync(mutex);
LOGD("=== Updating voip config ===");
config.clear();
for(std::map<std::string, std::string>::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 ServerConfig::Update(const char **values, int count) {
std::map<std::string, std::string> result;
for (int i = 0; i < count / 2; i++) {
result[values[i * 2 + 0]] = std::string(values[i * 2 + 1]);
}
Update(result);
}
bool ServerConfig::ContainsKey(std::string key){
return config.find(key)!=config.end();
}

View File

@ -0,0 +1,37 @@
//
// 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 <map>
#include <string>
#include <stdint.h>
#include "threading.h"
namespace tgvoip{
class ServerConfig{
public:
ServerConfig();
~ServerConfig();
static ServerConfig* 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<std::string, std::string> newValues);
void Update(const char **values, int count);
private:
static ServerConfig* sharedInstance;
bool ContainsKey(std::string key);
std::map<std::string, std::string> config;
tgvoip_mutex_t mutex;
};
}
#endif //TGVOIP_VOIPSERVERCONFIG_H

View File

@ -0,0 +1,106 @@
//
// 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 "AudioInput.h"
#include "../logging.h"
#if defined(__ANDROID__)
#include "../os/android/AudioInputAndroid.h"
#elif defined(__APPLE__)
#include <TargetConditionals.h>
#include "../os/darwin/AudioInputAudioUnit.h"
#if TARGET_OS_OSX
#include "../os/darwin/AudioInputAudioUnitOSX.h"
#endif
#elif defined(_WIN32)
#ifdef TGVOIP_WINXP_COMPAT
#include "../os/windows/AudioInputWave.h"
#endif
#include "../os/windows/AudioInputWASAPI.h"
#elif defined(__linux__)
#include "../os/linux/AudioInputALSA.h"
#include "../os/linux/AudioInputPulse.h"
#else
#error "Unsupported operating system"
#endif
using namespace tgvoip;
using namespace tgvoip::audio;
int32_t AudioInput::estimatedDelay=60;
AudioInput::AudioInput() : currentDevice("default"){
failed=false;
}
AudioInput::AudioInput(std::string deviceID) : currentDevice(deviceID){
failed=false;
}
AudioInput *AudioInput::Create(std::string deviceID){
#if defined(__ANDROID__)
return new AudioInputAndroid();
#elif defined(__APPLE__)
#if TARGET_OS_OSX
if(kCFCoreFoundationVersionNumber<kCFCoreFoundationVersionNumber10_7)
return new AudioInputAudioUnitLegacy(deviceID);
#endif
return new AudioInputAudioUnit(deviceID);
#elif defined(_WIN32)
#ifdef TGVOIP_WINXP_COMPAT
if(LOBYTE(LOWORD(GetVersion()))<6)
return new AudioInputWave(deviceID);
#endif
return new AudioInputWASAPI(deviceID);
#elif defined(__linux__)
if(AudioInputPulse::IsAvailable()){
AudioInputPulse* aip=new AudioInputPulse(deviceID);
if(!aip->IsInitialized())
delete aip;
else
return aip;
LOGW("in: PulseAudio available but not working; trying ALSA");
}
return new AudioInputALSA(deviceID);
#endif
}
AudioInput::~AudioInput(){
}
bool AudioInput::IsInitialized(){
return !failed;
}
void AudioInput::EnumerateDevices(std::vector<AudioInputDevice>& devs){
#if defined(__APPLE__) && TARGET_OS_OSX
AudioInputAudioUnitLegacy::EnumerateDevices(devs);
#elif defined(_WIN32)
#ifdef TGVOIP_WINXP_COMPAT
if(LOBYTE(LOWORD(GetVersion()))<6){
AudioInputWave::EnumerateDevices(devs);
return;
}
#endif
AudioInputWASAPI::EnumerateDevices(devs);
#elif defined(__linux__) && !defined(__ANDROID__)
if(!AudioInputPulse::IsAvailable() || !AudioInputPulse::EnumerateDevices(devs))
AudioInputALSA::EnumerateDevices(devs);
#endif
}
std::string AudioInput::GetCurrentDevice(){
return currentDevice;
}
void AudioInput::SetCurrentDevice(std::string deviceID){
}
int32_t AudioInput::GetEstimatedDelay(){
return estimatedDelay;
}

View File

@ -0,0 +1,42 @@
//
// 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 LIBTGVOIP_AUDIOINPUT_H
#define LIBTGVOIP_AUDIOINPUT_H
#include <stdint.h>
#include <vector>
#include <string>
#include "../MediaStreamItf.h"
namespace tgvoip{
class AudioInputDevice;
class AudioOutputDevice;
namespace audio{
class AudioInput : public MediaStreamItf{
public:
AudioInput();
AudioInput(std::string deviceID);
virtual ~AudioInput();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels)=0;
bool IsInitialized();
virtual std::string GetCurrentDevice();
virtual void SetCurrentDevice(std::string deviceID);
static AudioInput* Create(std::string deviceID);
static void EnumerateDevices(std::vector<AudioInputDevice>& devs);
static int32_t GetEstimatedDelay();
protected:
std::string currentDevice;
bool failed;
static int32_t estimatedDelay;
};
}}
#endif //LIBTGVOIP_AUDIOINPUT_H

View File

@ -0,0 +1,121 @@
//
// 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 "AudioOutput.h"
#include "../logging.h"
#if defined(__ANDROID__)
#include "../os/android/AudioOutputOpenSLES.h"
#include "../os/android/AudioOutputAndroid.h"
#elif defined(__APPLE__)
#include <TargetConditionals.h>
#include "../os/darwin/AudioOutputAudioUnit.h"
#if TARGET_OS_OSX
#include "../os/darwin/AudioOutputAudioUnitOSX.h"
#endif
#elif defined(_WIN32)
#ifdef TGVOIP_WINXP_COMPAT
#include "../os/windows/AudioOutputWave.h"
#endif
#include "../os/windows/AudioOutputWASAPI.h"
#elif defined(__linux__)
#include "../os/linux/AudioOutputALSA.h"
#include "../os/linux/AudioOutputPulse.h"
#else
#error "Unsupported operating system"
#endif
using namespace tgvoip;
using namespace tgvoip::audio;
#if defined(__ANDROID__)
int AudioOutput::systemVersion;
#endif
int32_t AudioOutput::estimatedDelay=60;
AudioOutput *AudioOutput::Create(std::string deviceID){
#if defined(__ANDROID__)
if(systemVersion<21)
return new AudioOutputAndroid();
return new AudioOutputOpenSLES();
#elif defined(__APPLE__)
#if TARGET_OS_OSX
if(kCFCoreFoundationVersionNumber<kCFCoreFoundationVersionNumber10_7)
return new AudioOutputAudioUnitLegacy(deviceID);
#endif
return new AudioOutputAudioUnit(deviceID);
#elif defined(_WIN32)
#ifdef TGVOIP_WINXP_COMPAT
if(LOBYTE(LOWORD(GetVersion()))<6)
return new AudioOutputWave(deviceID);
#endif
return new AudioOutputWASAPI(deviceID);
#elif defined(__linux__)
if(AudioOutputPulse::IsAvailable()){
AudioOutputPulse* aop=new AudioOutputPulse(deviceID);
if(!aop->IsInitialized())
delete aop;
else
return aop;
LOGW("out: PulseAudio available but not working; trying ALSA");
}
return new AudioOutputALSA(deviceID);
#endif
}
AudioOutput::AudioOutput() : currentDevice("default"){
failed=false;
}
AudioOutput::AudioOutput(std::string deviceID) : currentDevice(deviceID){
failed=false;
}
AudioOutput::~AudioOutput(){
}
int32_t AudioOutput::GetEstimatedDelay(){
#if defined(__ANDROID__)
return systemVersion<21 ? 150 : 50;
#endif
return estimatedDelay;
}
float AudioOutput::GetLevel(){
return 0;
}
void AudioOutput::EnumerateDevices(std::vector<AudioOutputDevice>& devs){
#if defined(__APPLE__) && TARGET_OS_OSX
AudioOutputAudioUnitLegacy::EnumerateDevices(devs);
#elif defined(_WIN32)
#ifdef TGVOIP_WINXP_COMPAT
if(LOBYTE(LOWORD(GetVersion()))<6){
AudioOutputWave::EnumerateDevices(devs);
return;
}
#endif
AudioOutputWASAPI::EnumerateDevices(devs);
#elif defined(__linux__) && !defined(__ANDROID__)
if(!AudioOutputPulse::IsAvailable() || !AudioOutputPulse::EnumerateDevices(devs))
AudioOutputALSA::EnumerateDevices(devs);
#endif
}
std::string AudioOutput::GetCurrentDevice(){
return currentDevice;
}
void AudioOutput::SetCurrentDevice(std::string deviceID){
}
bool AudioOutput::IsInitialized(){
return !failed;
}

View File

@ -0,0 +1,47 @@
//
// 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 LIBTGVOIP_AUDIOOUTPUT_H
#define LIBTGVOIP_AUDIOOUTPUT_H
#include <stdint.h>
#include <string>
#include <vector>
#include "../MediaStreamItf.h"
namespace tgvoip{
class AudioInputDevice;
class AudioOutputDevice;
namespace audio{
class AudioOutput : public MediaStreamItf{
public:
AudioOutput();
AudioOutput(std::string deviceID);
virtual ~AudioOutput();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels)=0;
virtual bool IsPlaying()=0;
virtual float GetLevel();
static int32_t GetEstimatedDelay();
virtual std::string GetCurrentDevice();
virtual void SetCurrentDevice(std::string deviceID);
static AudioOutput* Create(std::string deviceID);
static void EnumerateDevices(std::vector<AudioOutputDevice>& devs);
bool IsInitialized();
#if defined(__ANDROID__)
static int systemVersion;
#endif
protected:
std::string currentDevice;
bool failed;
static int32_t estimatedDelay;
};
}}
#endif //LIBTGVOIP_AUDIOOUTPUT_H

View File

@ -0,0 +1,117 @@
//
// Created by Grishka on 01.04.17.
//
#include <math.h>
#include <string.h>
#include "Resampler.h"
using namespace tgvoip::audio;
static const int16_t hann[960]={
0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0002, 0x0003, 0x0004, 0x0006, 0x0007, 0x0009, 0x000B, 0x000D, 0x000F, 0x0011, 0x0014, 0x0016, 0x0019, 0x001C, 0x0020,
0x0023, 0x0027, 0x002A, 0x002E, 0x0033, 0x0037, 0x003B, 0x0040, 0x0045, 0x004A, 0x004F, 0x0054, 0x005A, 0x0060, 0x0065, 0x006B, 0x0072, 0x0078, 0x007F, 0x0085,
0x008C, 0x0093, 0x009B, 0x00A2, 0x00AA, 0x00B2, 0x00B9, 0x00C2, 0x00CA, 0x00D2, 0x00DB, 0x00E4, 0x00ED, 0x00F6, 0x00FF, 0x0109, 0x0113, 0x011C, 0x0127, 0x0131,
0x013B, 0x0146, 0x0150, 0x015B, 0x0166, 0x0172, 0x017D, 0x0189, 0x0194, 0x01A0, 0x01AC, 0x01B9, 0x01C5, 0x01D2, 0x01DF, 0x01EC, 0x01F9, 0x0206, 0x0213, 0x0221,
0x022F, 0x023D, 0x024B, 0x0259, 0x0268, 0x0276, 0x0285, 0x0294, 0x02A3, 0x02B3, 0x02C2, 0x02D2, 0x02E2, 0x02F2, 0x0302, 0x0312, 0x0323, 0x0333, 0x0344, 0x0355,
0x0366, 0x0378, 0x0389, 0x039B, 0x03AD, 0x03BF, 0x03D1, 0x03E3, 0x03F6, 0x0408, 0x041B, 0x042E, 0x0441, 0x0455, 0x0468, 0x047C, 0x0490, 0x04A4, 0x04B8, 0x04CC,
0x04E0, 0x04F5, 0x050A, 0x051F, 0x0534, 0x0549, 0x055F, 0x0574, 0x058A, 0x05A0, 0x05B6, 0x05CC, 0x05E2, 0x05F9, 0x0610, 0x0627, 0x063E, 0x0655, 0x066C, 0x0684,
0x069B, 0x06B3, 0x06CB, 0x06E3, 0x06FC, 0x0714, 0x072D, 0x0745, 0x075E, 0x0777, 0x0791, 0x07AA, 0x07C3, 0x07DD, 0x07F7, 0x0811, 0x082B, 0x0845, 0x0860, 0x087A,
0x0895, 0x08B0, 0x08CB, 0x08E6, 0x0902, 0x091D, 0x0939, 0x0955, 0x0971, 0x098D, 0x09A9, 0x09C6, 0x09E2, 0x09FF, 0x0A1C, 0x0A39, 0x0A56, 0x0A73, 0x0A91, 0x0AAE,
0x0ACC, 0x0AEA, 0x0B08, 0x0B26, 0x0B44, 0x0B63, 0x0B81, 0x0BA0, 0x0BBF, 0x0BDE, 0x0BFD, 0x0C1D, 0x0C3C, 0x0C5C, 0x0C7B, 0x0C9B, 0x0CBB, 0x0CDC, 0x0CFC, 0x0D1C,
0x0D3D, 0x0D5E, 0x0D7F, 0x0DA0, 0x0DC1, 0x0DE2, 0x0E04, 0x0E25, 0x0E47, 0x0E69, 0x0E8B, 0x0EAD, 0x0ECF, 0x0EF1, 0x0F14, 0x0F37, 0x0F59, 0x0F7C, 0x0F9F, 0x0FC2,
0x0FE6, 0x1009, 0x102D, 0x1051, 0x1074, 0x1098, 0x10BC, 0x10E1, 0x1105, 0x112A, 0x114E, 0x1173, 0x1198, 0x11BD, 0x11E2, 0x1207, 0x122D, 0x1252, 0x1278, 0x129D,
0x12C3, 0x12E9, 0x130F, 0x1336, 0x135C, 0x1383, 0x13A9, 0x13D0, 0x13F7, 0x141E, 0x1445, 0x146C, 0x1494, 0x14BB, 0x14E3, 0x150A, 0x1532, 0x155A, 0x1582, 0x15AA,
0x15D3, 0x15FB, 0x1623, 0x164C, 0x1675, 0x169E, 0x16C7, 0x16F0, 0x1719, 0x1742, 0x176C, 0x1795, 0x17BF, 0x17E9, 0x1813, 0x183D, 0x1867, 0x1891, 0x18BB, 0x18E6,
0x1910, 0x193B, 0x1965, 0x1990, 0x19BB, 0x19E6, 0x1A11, 0x1A3D, 0x1A68, 0x1A93, 0x1ABF, 0x1AEB, 0x1B17, 0x1B42, 0x1B6E, 0x1B9A, 0x1BC7, 0x1BF3, 0x1C1F, 0x1C4C,
0x1C78, 0x1CA5, 0x1CD2, 0x1CFF, 0x1D2C, 0x1D59, 0x1D86, 0x1DB3, 0x1DE0, 0x1E0E, 0x1E3B, 0x1E69, 0x1E97, 0x1EC4, 0x1EF2, 0x1F20, 0x1F4E, 0x1F7C, 0x1FAB, 0x1FD9,
0x2007, 0x2036, 0x2065, 0x2093, 0x20C2, 0x20F1, 0x2120, 0x214F, 0x217E, 0x21AD, 0x21DD, 0x220C, 0x223B, 0x226B, 0x229A, 0x22CA, 0x22FA, 0x232A, 0x235A, 0x238A,
0x23BA, 0x23EA, 0x241A, 0x244B, 0x247B, 0x24AB, 0x24DC, 0x250D, 0x253D, 0x256E, 0x259F, 0x25D0, 0x2601, 0x2632, 0x2663, 0x2694, 0x26C5, 0x26F7, 0x2728, 0x275A,
0x278B, 0x27BD, 0x27EE, 0x2820, 0x2852, 0x2884, 0x28B6, 0x28E8, 0x291A, 0x294C, 0x297E, 0x29B0, 0x29E3, 0x2A15, 0x2A47, 0x2A7A, 0x2AAC, 0x2ADF, 0x2B12, 0x2B44,
0x2B77, 0x2BAA, 0x2BDD, 0x2C10, 0x2C43, 0x2C76, 0x2CA9, 0x2CDC, 0x2D0F, 0x2D43, 0x2D76, 0x2DA9, 0x2DDD, 0x2E10, 0x2E44, 0x2E77, 0x2EAB, 0x2EDF, 0x2F12, 0x2F46,
0x2F7A, 0x2FAE, 0x2FE2, 0x3016, 0x304A, 0x307E, 0x30B2, 0x30E6, 0x311A, 0x314E, 0x3182, 0x31B7, 0x31EB, 0x321F, 0x3254, 0x3288, 0x32BD, 0x32F1, 0x3326, 0x335A,
0x338F, 0x33C3, 0x33F8, 0x342D, 0x3461, 0x3496, 0x34CB, 0x3500, 0x3535, 0x356A, 0x359F, 0x35D4, 0x3608, 0x363D, 0x3673, 0x36A8, 0x36DD, 0x3712, 0x3747, 0x377C,
0x37B1, 0x37E6, 0x381C, 0x3851, 0x3886, 0x38BB, 0x38F1, 0x3926, 0x395B, 0x3991, 0x39C6, 0x39FC, 0x3A31, 0x3A66, 0x3A9C, 0x3AD1, 0x3B07, 0x3B3C, 0x3B72, 0x3BA7,
0x3BDD, 0x3C12, 0x3C48, 0x3C7D, 0x3CB3, 0x3CE9, 0x3D1E, 0x3D54, 0x3D89, 0x3DBF, 0x3DF5, 0x3E2A, 0x3E60, 0x3E95, 0x3ECB, 0x3F01, 0x3F36, 0x3F6C, 0x3FA2, 0x3FD7,
0x400D, 0x4043, 0x4078, 0x40AE, 0x40E3, 0x4119, 0x414F, 0x4184, 0x41BA, 0x41F0, 0x4225, 0x425B, 0x4290, 0x42C6, 0x42FC, 0x4331, 0x4367, 0x439C, 0x43D2, 0x4407,
0x443D, 0x4472, 0x44A8, 0x44DD, 0x4513, 0x4548, 0x457E, 0x45B3, 0x45E9, 0x461E, 0x4654, 0x4689, 0x46BE, 0x46F4, 0x4729, 0x475E, 0x4793, 0x47C9, 0x47FE, 0x4833,
0x4868, 0x489E, 0x48D3, 0x4908, 0x493D, 0x4972, 0x49A7, 0x49DC, 0x4A11, 0x4A46, 0x4A7B, 0x4AB0, 0x4AE5, 0x4B1A, 0x4B4E, 0x4B83, 0x4BB8, 0x4BED, 0x4C21, 0x4C56,
0x4C8B, 0x4CBF, 0x4CF4, 0x4D28, 0x4D5D, 0x4D91, 0x4DC6, 0x4DFA, 0x4E2E, 0x4E63, 0x4E97, 0x4ECB, 0x4EFF, 0x4F33, 0x4F67, 0x4F9B, 0x4FCF, 0x5003, 0x5037, 0x506B,
0x509F, 0x50D3, 0x5106, 0x513A, 0x516E, 0x51A1, 0x51D5, 0x5208, 0x523C, 0x526F, 0x52A3, 0x52D6, 0x5309, 0x533C, 0x536F, 0x53A3, 0x53D6, 0x5409, 0x543B, 0x546E,
0x54A1, 0x54D4, 0x5507, 0x5539, 0x556C, 0x559E, 0x55D1, 0x5603, 0x5636, 0x5668, 0x569A, 0x56CC, 0x56FE, 0x5730, 0x5762, 0x5794, 0x57C6, 0x57F8, 0x5829, 0x585B,
0x588D, 0x58BE, 0x58F0, 0x5921, 0x5952, 0x5984, 0x59B5, 0x59E6, 0x5A17, 0x5A48, 0x5A79, 0x5AA9, 0x5ADA, 0x5B0B, 0x5B3B, 0x5B6C, 0x5B9C, 0x5BCD, 0x5BFD, 0x5C2D,
0x5C5D, 0x5C8D, 0x5CBD, 0x5CED, 0x5D1D, 0x5D4D, 0x5D7C, 0x5DAC, 0x5DDB, 0x5E0B, 0x5E3A, 0x5E69, 0x5E99, 0x5EC8, 0x5EF7, 0x5F26, 0x5F54, 0x5F83, 0x5FB2, 0x5FE0,
0x600F, 0x603D, 0x606B, 0x609A, 0x60C8, 0x60F6, 0x6124, 0x6152, 0x617F, 0x61AD, 0x61DB, 0x6208, 0x6235, 0x6263, 0x6290, 0x62BD, 0x62EA, 0x6317, 0x6344, 0x6370,
0x639D, 0x63CA, 0x63F6, 0x6422, 0x644E, 0x647B, 0x64A7, 0x64D3, 0x64FE, 0x652A, 0x6556, 0x6581, 0x65AD, 0x65D8, 0x6603, 0x662E, 0x6659, 0x6684, 0x66AF, 0x66DA,
0x6704, 0x672F, 0x6759, 0x6783, 0x67AD, 0x67D7, 0x6801, 0x682B, 0x6855, 0x687E, 0x68A8, 0x68D1, 0x68FB, 0x6924, 0x694D, 0x6976, 0x699F, 0x69C7, 0x69F0, 0x6A18,
0x6A41, 0x6A69, 0x6A91, 0x6AB9, 0x6AE1, 0x6B09, 0x6B30, 0x6B58, 0x6B7F, 0x6BA6, 0x6BCE, 0x6BF5, 0x6C1C, 0x6C42, 0x6C69, 0x6C90, 0x6CB6, 0x6CDC, 0x6D03, 0x6D29,
0x6D4F, 0x6D74, 0x6D9A, 0x6DC0, 0x6DE5, 0x6E0A, 0x6E30, 0x6E55, 0x6E7A, 0x6E9E, 0x6EC3, 0x6EE8, 0x6F0C, 0x6F30, 0x6F55, 0x6F79, 0x6F9D, 0x6FC0, 0x6FE4, 0x7008,
0x702B, 0x704E, 0x7071, 0x7094, 0x70B7, 0x70DA, 0x70FC, 0x711F, 0x7141, 0x7163, 0x7185, 0x71A7, 0x71C9, 0x71EB, 0x720C, 0x722E, 0x724F, 0x7270, 0x7291, 0x72B2,
0x72D2, 0x72F3, 0x7313, 0x7333, 0x7354, 0x7374, 0x7393, 0x73B3, 0x73D3, 0x73F2, 0x7411, 0x7430, 0x744F, 0x746E, 0x748D, 0x74AB, 0x74CA, 0x74E8, 0x7506, 0x7524,
0x7542, 0x7560, 0x757D, 0x759B, 0x75B8, 0x75D5, 0x75F2, 0x760F, 0x762B, 0x7648, 0x7664, 0x7680, 0x769C, 0x76B8, 0x76D4, 0x76F0, 0x770B, 0x7726, 0x7741, 0x775C,
0x7777, 0x7792, 0x77AC, 0x77C7, 0x77E1, 0x77FB, 0x7815, 0x782F, 0x7848, 0x7862, 0x787B, 0x7894, 0x78AD, 0x78C6, 0x78DF, 0x78F7, 0x7910, 0x7928, 0x7940, 0x7958,
0x7970, 0x7987, 0x799F, 0x79B6, 0x79CD, 0x79E4, 0x79FB, 0x7A11, 0x7A28, 0x7A3E, 0x7A54, 0x7A6A, 0x7A80, 0x7A96, 0x7AAB, 0x7AC1, 0x7AD6, 0x7AEB, 0x7B00, 0x7B14,
0x7B29, 0x7B3D, 0x7B51, 0x7B65, 0x7B79, 0x7B8D, 0x7BA1, 0x7BB4, 0x7BC7, 0x7BDA, 0x7BED, 0x7C00, 0x7C13, 0x7C25, 0x7C37, 0x7C49, 0x7C5B, 0x7C6D, 0x7C7F, 0x7C90,
0x7CA1, 0x7CB2, 0x7CC3, 0x7CD4, 0x7CE5, 0x7CF5, 0x7D05, 0x7D15, 0x7D25, 0x7D35, 0x7D45, 0x7D54, 0x7D63, 0x7D72, 0x7D81, 0x7D90, 0x7D9F, 0x7DAD, 0x7DBB, 0x7DC9,
0x7DD7, 0x7DE5, 0x7DF2, 0x7E00, 0x7E0D, 0x7E1A, 0x7E27, 0x7E34, 0x7E40, 0x7E4C, 0x7E59, 0x7E65, 0x7E71, 0x7E7C, 0x7E88, 0x7E93, 0x7E9E, 0x7EA9, 0x7EB4, 0x7EBF,
0x7EC9, 0x7ED3, 0x7EDE, 0x7EE7, 0x7EF1, 0x7EFB, 0x7F04, 0x7F0E, 0x7F17, 0x7F20, 0x7F28, 0x7F31, 0x7F39, 0x7F41, 0x7F4A, 0x7F51, 0x7F59, 0x7F61, 0x7F68, 0x7F6F,
0x7F76, 0x7F7D, 0x7F84, 0x7F8A, 0x7F90, 0x7F97, 0x7F9D, 0x7FA2, 0x7FA8, 0x7FAD, 0x7FB3, 0x7FB8, 0x7FBD, 0x7FC1, 0x7FC6, 0x7FCA, 0x7FCF, 0x7FD3, 0x7FD6, 0x7FDA,
0x7FDE, 0x7FE1, 0x7FE4, 0x7FE7, 0x7FEA, 0x7FED, 0x7FEF, 0x7FF1, 0x7FF3, 0x7FF5, 0x7FF7, 0x7FF9, 0x7FFA, 0x7FFB, 0x7FFC, 0x7FFD, 0x7FFE, 0x7FFE, 0x7FFF, 0x7FFF
};
#define MIN(a, b) (a<b ? a : b)
size_t Resampler::Convert48To44(int16_t *from, int16_t *to, size_t fromLen, size_t toLen){
size_t outLen=fromLen*147/160;
if(toLen<outLen)
outLen=toLen;
unsigned int offset;
for(offset=0;offset<outLen;offset++){
float offsetf=offset*160.0f/147.0f;
float factor=offsetf-floorf(offsetf);
to[offset]=(int16_t)((from[(int)floorf(offsetf)]*(1-factor))+(from[(int)ceilf(offsetf)]*(factor)));
}
return outLen;
}
size_t Resampler::Convert44To48(int16_t *from, int16_t *to, size_t fromLen, size_t toLen){
size_t outLen=fromLen*160/147;
if(toLen<outLen)
outLen=toLen;
unsigned int offset;
for(offset=0;offset<outLen;offset++){
float offsetf=offset*147.0f/160.0f;
float factor=offsetf-floorf(offsetf);
to[offset]=(int16_t)((from[(int)floorf(offsetf)]*(1-factor))+(from[(int)ceilf(offsetf)]*(factor)));
}
return outLen;
}
size_t Resampler::Convert(int16_t *from, int16_t *to, size_t fromLen, size_t toLen, int num, int denom){
size_t outLen=fromLen*num/denom;
if(toLen<outLen)
outLen=toLen;
unsigned int offset;
for(offset=0;offset<outLen;offset++){
float offsetf=offset*(float)denom/(float)num;
float factor=offsetf-floorf(offsetf);
to[offset]=(int16_t)((from[(int)floorf(offsetf)]*(1-factor))+(from[(int)ceilf(offsetf)]*(factor)));
}
return outLen;
}
void Resampler::Rescale60To80(int16_t *in, int16_t *out){
memcpy(out, in, 960*2);
memcpy(out+960*3, in+960*2, 960*2);
for(int i=0;i<960;i++){
out[960+i]=(int16_t)(((int32_t)in[960+i]*hann[959-i]) >> 15) + (int16_t)(((int32_t)in[480+i]*hann[i]) >> 15);
out[1920+i]=(int16_t)(((int32_t)in[960+480+i]*hann[959-i]) >> 15) + (int16_t)(((int32_t)in[960+i]*hann[i]) >> 15);
}
}
void Resampler::Rescale60To40(int16_t *in, int16_t *out){
for(int i=0;i<960;i++){
out[i]=(int16_t)(((int32_t)in[i]*hann[959-i]) >> 15) + (int16_t)(((int32_t)in[480+i]*hann[i]) >> 15);
out[960+i]=(int16_t)(((int32_t)in[1920+i]*hann[i]) >> 15) + (int16_t)(((int32_t)in[1440+i]*hann[959-i]) >> 15);
}
}

View File

@ -0,0 +1,22 @@
//
// Created by Grishka on 01.04.17.
//
#ifndef LIBTGVOIP_RESAMPLER_H
#define LIBTGVOIP_RESAMPLER_H
#include <stdlib.h>
#include <stdint.h>
namespace tgvoip{ namespace audio{
class Resampler{
public:
static size_t Convert48To44(int16_t* from, int16_t* to, size_t fromLen, size_t toLen);
static size_t Convert44To48(int16_t* from, int16_t* to, size_t fromLen, size_t toLen);
static size_t Convert(int16_t* from, int16_t* to, size_t fromLen, size_t toLen, int num, int denom);
static void Rescale60To80(int16_t* in, int16_t* out);
static void Rescale60To40(int16_t* in, int16_t* out);
};
}}
#endif //LIBTGVOIP_RESAMPLER_H

View File

@ -0,0 +1,308 @@
//
// 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 <jni.h>
#include <string.h>
#include <map>
#include <string>
#include <vector>
#include <libtgvoip/VoIPServerConfig.h>
#include "../../VoIPController.h"
#include "../../os/android/AudioOutputOpenSLES.h"
#include "../../os/android/AudioInputOpenSLES.h"
#include "../../os/android/AudioInputAndroid.h"
#include "../../os/android/AudioOutputAndroid.h"
#include "../../audio/Resampler.h"
JavaVM* sharedJVM;
jfieldID audioRecordInstanceFld=NULL;
jfieldID audioTrackInstanceFld=NULL;
jmethodID setStateMethod=NULL;
jmethodID setSignalBarsMethod=NULL;
struct impl_data_android_t{
jobject javaObject;
};
using namespace tgvoip;
using namespace tgvoip::audio;
void updateConnectionState(VoIPController* cntrlr, int state){
impl_data_android_t* impl=(impl_data_android_t*) cntrlr->implData;
if(!impl->javaObject)
return;
JNIEnv* env=NULL;
bool didAttach=false;
sharedJVM->GetEnv((void**) &env, JNI_VERSION_1_6);
if(!env){
sharedJVM->AttachCurrentThread(&env, NULL);
didAttach=true;
}
if(setStateMethod)
env->CallVoidMethod(impl->javaObject, setStateMethod, state);
if(didAttach){
sharedJVM->DetachCurrentThread();
}
}
void updateSignalBarCount(VoIPController* cntrlr, int count){
impl_data_android_t* impl=(impl_data_android_t*) cntrlr->implData;
if(!impl->javaObject)
return;
JNIEnv* env=NULL;
bool didAttach=false;
sharedJVM->GetEnv((void**) &env, JNI_VERSION_1_6);
if(!env){
sharedJVM->AttachCurrentThread(&env, NULL);
didAttach=true;
}
if(setSignalBarsMethod)
env->CallVoidMethod(impl->javaObject, setSignalBarsMethod, count);
if(didAttach){
sharedJVM->DetachCurrentThread();
}
}
extern "C" JNIEXPORT jlong Java_org_telegram_messenger_voip_VoIPController_nativeInit(JNIEnv* env, jobject thiz, jint systemVersion){
AudioOutputAndroid::systemVersion=systemVersion;
env->GetJavaVM(&sharedJVM);
if(!AudioInputAndroid::jniClass){
jclass cls=env->FindClass("org/telegram/messenger/voip/AudioRecordJNI");
AudioInputAndroid::jniClass=(jclass) env->NewGlobalRef(cls);
AudioInputAndroid::initMethod=env->GetMethodID(cls, "init", "(IIII)V");
AudioInputAndroid::releaseMethod=env->GetMethodID(cls, "release", "()V");
AudioInputAndroid::startMethod=env->GetMethodID(cls, "start", "()Z");
AudioInputAndroid::stopMethod=env->GetMethodID(cls, "stop", "()V");
cls=env->FindClass("org/telegram/messenger/voip/AudioTrackJNI");
AudioOutputAndroid::jniClass=(jclass) env->NewGlobalRef(cls);
AudioOutputAndroid::initMethod=env->GetMethodID(cls, "init", "(IIII)V");
AudioOutputAndroid::releaseMethod=env->GetMethodID(cls, "release", "()V");
AudioOutputAndroid::startMethod=env->GetMethodID(cls, "start", "()V");
AudioOutputAndroid::stopMethod=env->GetMethodID(cls, "stop", "()V");
}
setStateMethod=env->GetMethodID(env->GetObjectClass(thiz), "handleStateChange", "(I)V");
setSignalBarsMethod=env->GetMethodID(env->GetObjectClass(thiz), "handleSignalBarsChange", "(I)V");
impl_data_android_t* impl=(impl_data_android_t*) malloc(sizeof(impl_data_android_t));
impl->javaObject=env->NewGlobalRef(thiz);
VoIPController* cntrlr=new VoIPController();
cntrlr->implData=impl;
cntrlr->SetStateCallback(updateConnectionState);
cntrlr->SetSignalBarsCountCallback(updateSignalBarCount);
return (jlong)(intptr_t)cntrlr;
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeStart(JNIEnv* env, jobject thiz, jlong inst){
((VoIPController*)(intptr_t)inst)->Start();
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeConnect(JNIEnv* env, jobject thiz, jlong inst){
((VoIPController*)(intptr_t)inst)->Connect();
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeSetProxy(JNIEnv* env, jobject thiz, jlong inst, jstring _address, jint port, jstring _username, jstring _password){
const char* address=env->GetStringUTFChars(_address, NULL);
const char* username=_username ? env->GetStringUTFChars(_username, NULL) : NULL;
const char* password=_password ? env->GetStringUTFChars(_password, NULL) : NULL;
((VoIPController*)(intptr_t)inst)->SetProxy(PROXY_SOCKS5, address, (uint16_t)port, username ? username : "", password ? password : "");
env->ReleaseStringUTFChars(_address, address);
if(username)
env->ReleaseStringUTFChars(_username, username);
if(password)
env->ReleaseStringUTFChars(_password, password);
}
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);
((VoIPController*)(intptr_t)inst)->SetEncryptionKey((char *) akey, isOutgoing);
env->ReleaseByteArrayElements(key, akey, JNI_ABORT);
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeSetRemoteEndpoints(JNIEnv* env, jobject thiz, jlong inst, jobjectArray endpoints, jboolean allowP2p){
size_t len=(size_t) env->GetArrayLength(endpoints);
// voip_endpoint_t* eps=(voip_endpoint_t *) malloc(sizeof(voip_endpoint_t)*len);
std::vector<Endpoint> eps;
/*public String ip;
public String ipv6;
public int port;
public byte[] peer_tag;*/
jclass epClass=env->GetObjectClass(env->GetObjectArrayElement(endpoints, 0));
jfieldID ipFld=env->GetFieldID(epClass, "ip", "Ljava/lang/String;");
jfieldID ipv6Fld=env->GetFieldID(epClass, "ipv6", "Ljava/lang/String;");
jfieldID portFld=env->GetFieldID(epClass, "port", "I");
jfieldID peerTagFld=env->GetFieldID(epClass, "peer_tag", "[B");
jfieldID idFld=env->GetFieldID(epClass, "id", "J");
int i;
for(i=0;i<len;i++){
jobject endpoint=env->GetObjectArrayElement(endpoints, i);
jstring ip=(jstring) env->GetObjectField(endpoint, ipFld);
jstring ipv6=(jstring) env->GetObjectField(endpoint, ipv6Fld);
jint port=env->GetIntField(endpoint, portFld);
jlong id=env->GetLongField(endpoint, idFld);
jbyteArray peerTag=(jbyteArray) env->GetObjectField(endpoint, peerTagFld);
const char* ipChars=env->GetStringUTFChars(ip, NULL);
std::string ipLiteral(ipChars);
IPv4Address v4addr(ipLiteral);
IPv6Address v6addr("::0");
env->ReleaseStringUTFChars(ip, ipChars);
if(ipv6 && env->GetStringLength(ipv6)){
const char* ipv6Chars=env->GetStringUTFChars(ipv6, NULL);
v6addr=IPv6Address(ipv6Chars);
env->ReleaseStringUTFChars(ipv6, ipv6Chars);
}
unsigned char pTag[16];
if(peerTag && env->GetArrayLength(peerTag)){
jbyte* peerTagBytes=env->GetByteArrayElements(peerTag, NULL);
memcpy(pTag, peerTagBytes, 16);
env->ReleaseByteArrayElements(peerTag, peerTagBytes, JNI_ABORT);
}
eps.push_back(Endpoint((int64_t)id, (uint16_t)port, v4addr, v6addr, EP_TYPE_UDP_RELAY, pTag));
}
((VoIPController*)(intptr_t)inst)->SetRemoteEndpoints(eps, allowP2p);
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeSetNativeBufferSize(JNIEnv* env, jclass thiz, jint size){
AudioOutputOpenSLES::nativeBufferSize=size;
AudioInputOpenSLES::nativeBufferSize=size;
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeRelease(JNIEnv* env, jobject thiz, jlong inst){
//env->DeleteGlobalRef(AudioInputAndroid::jniClass);
VoIPController* ctlr=((VoIPController*)(intptr_t)inst);
impl_data_android_t* impl=(impl_data_android_t*)ctlr->implData;
delete ctlr;
env->DeleteGlobalRef(impl->javaObject);
((impl_data_android_t*)ctlr->implData)->javaObject=NULL;
free(impl);
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_AudioRecordJNI_nativeCallback(JNIEnv* env, jobject thiz, jobject buffer){
if(!audioRecordInstanceFld)
audioRecordInstanceFld=env->GetFieldID(env->GetObjectClass(thiz), "nativeInst", "J");
jlong inst=env->GetLongField(thiz, audioRecordInstanceFld);
AudioInputAndroid* in=(AudioInputAndroid*)(intptr_t)inst;
in->HandleCallback(env, buffer);
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_AudioTrackJNI_nativeCallback(JNIEnv* env, jobject thiz, jbyteArray buffer){
if(!audioTrackInstanceFld)
audioTrackInstanceFld=env->GetFieldID(env->GetObjectClass(thiz), "nativeInst", "J");
jlong inst=env->GetLongField(thiz, audioTrackInstanceFld);
AudioOutputAndroid* in=(AudioOutputAndroid*)(intptr_t)inst;
in->HandleCallback(env, buffer);
}
extern "C" JNIEXPORT jstring Java_org_telegram_messenger_voip_VoIPController_nativeGetDebugString(JNIEnv* env, jobject thiz, jlong inst){
char buf[10240];
((VoIPController*)(intptr_t)inst)->GetDebugString(buf, 10240);
return env->NewStringUTF(buf);
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeSetNetworkType(JNIEnv* env, jobject thiz, jlong inst, jint type){
((VoIPController*)(intptr_t)inst)->SetNetworkType(type);
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeSetMicMute(JNIEnv* env, jobject thiz, jlong inst, jboolean mute){
((VoIPController*)(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, jboolean enableAEC, jboolean enableNS, jboolean enableAGC, jstring logFilePath, jstring statsDumpPath){
voip_config_t cfg;
cfg.init_timeout=initTimeout;
cfg.recv_timeout=recvTimeout;
cfg.data_saving=dataSavingMode;
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));
}
if(statsDumpPath){
char* path=(char *) env->GetStringUTFChars(statsDumpPath, NULL);
strncpy(cfg.statsDumpFilePath, path, sizeof(cfg.statsDumpFilePath));
cfg.statsDumpFilePath[sizeof(cfg.logFilePath)-1]=0;
env->ReleaseStringUTFChars(logFilePath, path);
}else{
memset(cfg.statsDumpFilePath, 0, sizeof(cfg.statsDumpFilePath));
}
((VoIPController*)(intptr_t)inst)->SetConfig(&cfg);
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeDebugCtl(JNIEnv* env, jobject thiz, jlong inst, jint request, jint param){
((VoIPController*)(intptr_t)inst)->DebugCtl(request, param);
}
extern "C" JNIEXPORT jstring Java_org_telegram_messenger_voip_VoIPController_nativeGetVersion(JNIEnv* env, jclass clasz){
return env->NewStringUTF(VoIPController::GetVersion());
}
extern "C" JNIEXPORT jlong Java_org_telegram_messenger_voip_VoIPController_nativeGetPreferredRelayID(JNIEnv* env, jclass clasz, jlong inst){
return ((VoIPController*)(intptr_t)inst)->GetPreferredRelayID();
}
extern "C" JNIEXPORT jint Java_org_telegram_messenger_voip_VoIPController_nativeGetLastError(JNIEnv* env, jclass clasz, jlong inst){
return ((VoIPController*)(intptr_t)inst)->GetLastError();
}
extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeGetStats(JNIEnv* env, jclass clasz, jlong inst, jobject stats){
voip_stats_t _stats;
((VoIPController*)(intptr_t)inst)->GetStats(&_stats);
jclass cls=env->GetObjectClass(stats);
env->SetLongField(stats, env->GetFieldID(cls, "bytesSentWifi", "J"), _stats.bytesSentWifi);
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<std::string, std::string> config;
int len=env->GetArrayLength(keys);
int i;
for(i=0;i<len;i++){
jstring jkey=(jstring)env->GetObjectArrayElement(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;
}
ServerConfig::GetSharedInstance()->Update(config);
}
extern "C" JNIEXPORT jstring Java_org_telegram_messenger_voip_VoIPController_nativeGetDebugLog(JNIEnv* env, jobject thiz, jlong inst){
VoIPController* ctlr=((VoIPController*)(intptr_t)inst);
std::string log=ctlr->GetDebugLog();
return env->NewStringUTF(log.c_str());
}
extern "C" JNIEXPORT jint Java_org_telegram_messenger_voip_Resampler_convert44to48(JNIEnv* env, jclass cls, jobject from, jobject to){
return tgvoip::audio::Resampler::Convert44To48((int16_t *) env->GetDirectBufferAddress(from), (int16_t *) env->GetDirectBufferAddress(to), (size_t) (env->GetDirectBufferCapacity(from)/2), (size_t) (env->GetDirectBufferCapacity(to)/2));
}
extern "C" JNIEXPORT jint Java_org_telegram_messenger_voip_Resampler_convert48to44(JNIEnv* env, jclass cls, jobject from, jobject to){
return tgvoip::audio::Resampler::Convert48To44((int16_t *) env->GetDirectBufferAddress(from), (int16_t *) env->GetDirectBufferAddress(to), (size_t) (env->GetDirectBufferCapacity(from)/2), (size_t) (env->GetDirectBufferCapacity(to)/2));
}

View File

@ -0,0 +1,509 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM">
<Configuration>Debug</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM">
<Configuration>Release</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{88803693-7606-484b-9d2f-4bb789d57c29}</ProjectGuid>
<Keyword>WindowsRuntimeComponent</Keyword>
<RootNamespace>libtgvoip</RootNamespace>
<DefaultLanguage>en</DefaultLanguage>
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
<AppContainerApplication>true</AppContainerApplication>
<ApplicationType>Windows Store</ApplicationType>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
<WindowsTargetPlatformMinVersion>10.0.10240.0</WindowsTargetPlatformMinVersion>
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
<ProjectName>libtgvoip</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v141</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPlatformExtensionSDKLocation(`WindowsMobile, Version=10.0.15063.0`, $(TargetPlatformIdentifier), $(TargetPlatformVersion), $(SDKReferenceDirectoryRoot), $(SDKExtensionDirectoryRoot), $(SDKReferenceRegistryRoot)))\DesignTime\CommonConfiguration\Neutral\WindowsMobile.props" Condition="exists('$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPlatformExtensionSDKLocation(`WindowsMobile, Version=10.0.15063.0`, $(TargetPlatformIdentifier), $(TargetPlatformVersion), $(SDKReferenceDirectoryRoot), $(SDKExtensionDirectoryRoot), $(SDKReferenceRegistryRoot)))\DesignTime\CommonConfiguration\Neutral\WindowsMobile.props')" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPlatformExtensionSDKLocation(`WindowsMobile, Version=10.0.15063.0`, $(TargetPlatformIdentifier), $(TargetPlatformVersion), $(SDKReferenceDirectoryRoot), $(SDKExtensionDirectoryRoot), $(SDKReferenceRegistryRoot)))\DesignTime\CommonConfiguration\Neutral\WindowsMobile.props" Condition="exists('$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPlatformExtensionSDKLocation(`WindowsMobile, Version=10.0.15063.0`, $(TargetPlatformIdentifier), $(TargetPlatformVersion), $(SDKReferenceDirectoryRoot), $(SDKExtensionDirectoryRoot), $(SDKReferenceRegistryRoot)))\DesignTime\CommonConfiguration\Neutral\WindowsMobile.props')" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPlatformExtensionSDKLocation(`WindowsMobile, Version=10.0.15063.0`, $(TargetPlatformIdentifier), $(TargetPlatformVersion), $(SDKReferenceDirectoryRoot), $(SDKExtensionDirectoryRoot), $(SDKReferenceRegistryRoot)))\DesignTime\CommonConfiguration\Neutral\WindowsMobile.props" Condition="exists('$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPlatformExtensionSDKLocation(`WindowsMobile, Version=10.0.15063.0`, $(TargetPlatformIdentifier), $(TargetPlatformVersion), $(SDKReferenceDirectoryRoot), $(SDKExtensionDirectoryRoot), $(SDKReferenceRegistryRoot)))\DesignTime\CommonConfiguration\Neutral\WindowsMobile.props')" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPlatformExtensionSDKLocation(`WindowsMobile, Version=10.0.15063.0`, $(TargetPlatformIdentifier), $(TargetPlatformVersion), $(SDKReferenceDirectoryRoot), $(SDKExtensionDirectoryRoot), $(SDKReferenceRegistryRoot)))\DesignTime\CommonConfiguration\Neutral\WindowsMobile.props" Condition="exists('$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPlatformExtensionSDKLocation(`WindowsMobile, Version=10.0.15063.0`, $(TargetPlatformIdentifier), $(TargetPlatformVersion), $(SDKReferenceDirectoryRoot), $(SDKExtensionDirectoryRoot), $(SDKReferenceRegistryRoot)))\DesignTime\CommonConfiguration\Neutral\WindowsMobile.props')" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPlatformExtensionSDKLocation(`WindowsMobile, Version=10.0.15063.0`, $(TargetPlatformIdentifier), $(TargetPlatformVersion), $(SDKReferenceDirectoryRoot), $(SDKExtensionDirectoryRoot), $(SDKReferenceRegistryRoot)))\DesignTime\CommonConfiguration\Neutral\WindowsMobile.props" Condition="exists('$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPlatformExtensionSDKLocation(`WindowsMobile, Version=10.0.15063.0`, $(TargetPlatformIdentifier), $(TargetPlatformVersion), $(SDKReferenceDirectoryRoot), $(SDKExtensionDirectoryRoot), $(SDKReferenceRegistryRoot)))\DesignTime\CommonConfiguration\Neutral\WindowsMobile.props')" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPlatformExtensionSDKLocation(`WindowsMobile, Version=10.0.15063.0`, $(TargetPlatformIdentifier), $(TargetPlatformVersion), $(SDKReferenceDirectoryRoot), $(SDKExtensionDirectoryRoot), $(SDKReferenceRegistryRoot)))\DesignTime\CommonConfiguration\Neutral\WindowsMobile.props" Condition="exists('$([Microsoft.Build.Utilities.ToolLocationHelper]::GetPlatformExtensionSDKLocation(`WindowsMobile, Version=10.0.15063.0`, $(TargetPlatformIdentifier), $(TargetPlatformVersion), $(SDKReferenceDirectoryRoot), $(SDKExtensionDirectoryRoot), $(SDKReferenceRegistryRoot)))\DesignTime\CommonConfiguration\Neutral\WindowsMobile.props')" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<GenerateManifest>false</GenerateManifest>
<MappedPlatform>Debug</MappedPlatform>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<GenerateManifest>false</GenerateManifest>
<MappedPlatform>ARM\Debug</MappedPlatform>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<GenerateManifest>false</GenerateManifest>
<MappedPlatform>x64\Debug</MappedPlatform>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<GenerateManifest>false</GenerateManifest>
<MappedPlatform>Release</MappedPlatform>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<GenerateManifest>false</GenerateManifest>
<MappedPlatform>ARM\Release</MappedPlatform>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<GenerateManifest>false</GenerateManifest>
<MappedPlatform>x64\Release</MappedPlatform>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;NOMINMAX;WEBRTC_APM_DEBUG_DUMP=0;TGVOIP_USE_CUSTOM_CRYPTO;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<AdditionalIncludeDirectories>webrtc_dsp;../libopus/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<CompileAsWinRT>true</CompileAsWinRT>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>libopus.lib;ws2_32.lib;mmdevapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)$(MappedPlatform)\libopus\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;NOMINMAX;WEBRTC_APM_DEBUG_DUMP=0;TGVOIP_USE_CUSTOM_CRYPTO;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<AdditionalIncludeDirectories>webrtc_dsp;../libopus/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>libopus.lib;ws2_32.lib;mmdevapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)$(MappedPlatform)\libopus\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;NOMINMAX;WEBRTC_APM_DEBUG_DUMP=0;TGVOIP_USE_CUSTOM_CRYPTO;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<AdditionalIncludeDirectories>webrtc_dsp;../libopus/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>libopus.lib;ws2_32.lib;mmdevapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)$(MappedPlatform)\libopus\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;NOMINMAX;WEBRTC_APM_DEBUG_DUMP=0;NDEBUG;TGVOIP_USE_CUSTOM_CRYPTO;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<AdditionalIncludeDirectories>webrtc_dsp;../libopus/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>libopus.lib;ws2_32.lib;mmdevapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)$(MappedPlatform)\libopus\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;NOMINMAX;WEBRTC_APM_DEBUG_DUMP=0;NDEBUG;TGVOIP_USE_CUSTOM_CRYPTO;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<AdditionalIncludeDirectories>webrtc_dsp;../libopus/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>libopus.lib;ws2_32.lib;mmdevapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)$(MappedPlatform)\libopus\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;NOMINMAX;WEBRTC_APM_DEBUG_DUMP=0;NDEBUG;TGVOIP_USE_CUSTOM_CRYPTO;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
<DisableSpecificWarnings>28204</DisableSpecificWarnings>
<AdditionalIncludeDirectories>webrtc_dsp;../libopus/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>libopus.lib;ws2_32.lib;mmdevapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)$(MappedPlatform)\libopus\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="audio\AudioInput.h" />
<ClInclude Include="audio\AudioOutput.h" />
<ClInclude Include="audio\Resampler.h" />
<ClInclude Include="BlockingQueue.h" />
<ClInclude Include="BufferInputStream.h" />
<ClInclude Include="BufferOutputStream.h" />
<ClInclude Include="BufferPool.h" />
<ClInclude Include="CongestionControl.h" />
<ClInclude Include="EchoCanceller.h" />
<ClInclude Include="JitterBuffer.h" />
<ClInclude Include="logging.h" />
<ClInclude Include="MediaStreamItf.h" />
<ClInclude Include="NetworkSocket.h" />
<ClInclude Include="OpusDecoder.h" />
<ClInclude Include="OpusEncoder.h" />
<ClInclude Include="os\windows\AudioInputWASAPI.h" />
<ClInclude Include="os\windows\AudioOutputWASAPI.h" />
<ClInclude Include="os\windows\NetworkSocketWinsock.h" />
<ClInclude Include="os\windows\WindowsSandboxUtils.h" />
<ClInclude Include="threading.h" />
<ClInclude Include="VoIPController.h" />
<ClInclude Include="os\windows\CXWrapper.h" />
<ClInclude Include="VoIPServerConfig.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\array_view.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\atomicops.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\basictypes.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\checks.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\constructormagic.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\safe_compare.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\safe_conversions.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\safe_conversions_impl.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\sanitizer.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\stringutils.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\type_traits.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\channel_buffer.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\fft4g.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\include\audio_util.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\ring_buffer.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\complex_fft_tables.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\real_fft.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\signal_processing_library.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\spl_inl.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\spl_inl_armv7.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\spl_inl_mips.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_by_2_internal.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\sparse_fir_filter.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\wav_file.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\wav_header.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_defines.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\echo_control_mobile.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_common.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core_optimized_methods.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_resampler.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\echo_cancellation.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\analog_agc.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\digital_agc.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\gain_control.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\logging\apm_data_dumper.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\defines.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression_x.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_defines.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\ns_core.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\windows_private.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\splitting_filter.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\three_band_filter_bank.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\block_mean_calculator.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator_internal.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator_wrapper.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_tables_common.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_tables_neon_sse2.h" />
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\asm_defines.h" />
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\compile_assert_c.h" />
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\cpu_features_wrapper.h" />
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\metrics.h" />
<ClInclude Include="webrtc_dsp\webrtc\typedefs.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="audio\AudioInput.cpp" />
<ClCompile Include="audio\AudioOutput.cpp" />
<ClCompile Include="audio\Resampler.cpp" />
<ClCompile Include="BlockingQueue.cpp" />
<ClCompile Include="BufferInputStream.cpp" />
<ClCompile Include="BufferOutputStream.cpp" />
<ClCompile Include="BufferPool.cpp" />
<ClCompile Include="CongestionControl.cpp" />
<ClCompile Include="EchoCanceller.cpp" />
<ClCompile Include="JitterBuffer.cpp" />
<ClCompile Include="logging.cpp" />
<ClCompile Include="MediaStreamItf.cpp" />
<ClCompile Include="NetworkSocket.cpp" />
<ClCompile Include="OpusDecoder.cpp" />
<ClCompile Include="OpusEncoder.cpp" />
<ClCompile Include="os\windows\AudioInputWASAPI.cpp" />
<ClCompile Include="os\windows\AudioOutputWASAPI.cpp" />
<ClCompile Include="os\windows\NetworkSocketWinsock.cpp" />
<ClCompile Include="os\windows\WindowsSandboxUtils.cpp" />
<ClCompile Include="VoIPController.cpp" />
<ClCompile Include="os\windows\CXWrapper.cpp" />
<ClCompile Include="VoIPServerConfig.cpp" />
<ClCompile Include="webrtc_dsp\webrtc\base\checks.cc" />
<ClCompile Include="webrtc_dsp\webrtc\base\stringutils.cc" />
<ClCompile Include="webrtc_dsp\webrtc\common_audio\audio_util.cc" />
<ClCompile Include="webrtc_dsp\webrtc\common_audio\channel_buffer.cc" />
<ClCompile Include="webrtc_dsp\webrtc\common_audio\fft4g.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\ring_buffer.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\auto_correlation.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\auto_corr_to_refl_coef.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\complex_bit_reverse.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\complex_fft.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\copy_set_operations.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\cross_correlation.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\cross_correlation_neon.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\division_operations.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\dot_product_with_scale.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\downsample_fast.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\downsample_fast_neon.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\energy.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ar.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ar_fast_q12.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ma_fast_q12.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\get_hanning_window.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\get_scaling_square.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\ilbc_specific_functions.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\levinson_durbin.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\lpc_to_refl_coef.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\min_max_operations.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\min_max_operations_neon.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\randomization_functions.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\real_fft.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\refl_coef_to_lpc.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_48khz.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_by_2.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_by_2_internal.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_fractional.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\splitting_filter_impl.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_init.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_inl.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_sqrt.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_sqrt_floor.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\sqrt_of_one_minus_x_squared.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\vector_scaling_operations.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\sparse_fir_filter.cc" />
<ClCompile Include="webrtc_dsp\webrtc\common_audio\wav_file.cc" />
<ClCompile Include="webrtc_dsp\webrtc\common_audio\wav_header.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core_c.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core_neon.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\echo_control_mobile.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core_neon.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core_sse2.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_resampler.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\echo_cancellation.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\analog_agc.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\digital_agc.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\logging\apm_data_dumper.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression_x.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core_c.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core_neon.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\ns_core.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\splitting_filter.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\three_band_filter_bank.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\block_mean_calculator.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator_wrapper.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_neon.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_sse2.cc" />
<ClCompile Include="webrtc_dsp\webrtc\system_wrappers\source\cpu_features.cc" />
</ItemGroup>
<ItemGroup>
<SDKReference Include="WindowsMobile, Version=10.0.15063.0" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>

View File

@ -0,0 +1,492 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Resources">
<UniqueIdentifier>11199e80-17a0-460f-a780-9bfde20eb11c</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="audio">
<UniqueIdentifier>{c5b75146-c75a-4c56-aeb2-2781658d7b0a}</UniqueIdentifier>
</Filter>
<Filter Include="windows">
<UniqueIdentifier>{de1527d9-7564-4e96-9653-6e023b90d2bc}</UniqueIdentifier>
</Filter>
<Filter Include="webrtc_dsp">
<UniqueIdentifier>{3b15701a-65dd-4d52-92d4-a7b64a73b293}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="VoIPController.cpp" />
<ClCompile Include="VoIPServerConfig.cpp" />
<ClCompile Include="BlockingQueue.cpp" />
<ClCompile Include="BufferInputStream.cpp" />
<ClCompile Include="BufferOutputStream.cpp" />
<ClCompile Include="BufferPool.cpp" />
<ClCompile Include="CongestionControl.cpp" />
<ClCompile Include="EchoCanceller.cpp" />
<ClCompile Include="JitterBuffer.cpp" />
<ClCompile Include="logging.cpp" />
<ClCompile Include="MediaStreamItf.cpp" />
<ClCompile Include="NetworkSocket.cpp" />
<ClCompile Include="OpusDecoder.cpp" />
<ClCompile Include="OpusEncoder.cpp" />
<ClCompile Include="audio\AudioInput.cpp">
<Filter>audio</Filter>
</ClCompile>
<ClCompile Include="audio\AudioOutput.cpp">
<Filter>audio</Filter>
</ClCompile>
<ClCompile Include="audio\Resampler.cpp">
<Filter>audio</Filter>
</ClCompile>
<ClCompile Include="os\windows\AudioInputWASAPI.cpp">
<Filter>windows</Filter>
</ClCompile>
<ClCompile Include="os\windows\AudioOutputWASAPI.cpp">
<Filter>windows</Filter>
</ClCompile>
<ClCompile Include="os\windows\NetworkSocketWinsock.cpp">
<Filter>windows</Filter>
</ClCompile>
<ClCompile Include="os\windows\WindowsSandboxUtils.cpp">
<Filter>windows</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\base\checks.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\base\stringutils.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\audio_util.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\channel_buffer.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\fft4g.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\ring_buffer.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\auto_correlation.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\auto_corr_to_refl_coef.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\complex_bit_reverse.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\complex_fft.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\copy_set_operations.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\cross_correlation.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\cross_correlation_neon.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\division_operations.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\dot_product_with_scale.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\downsample_fast.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\downsample_fast_neon.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\energy.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ar.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ar_fast_q12.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ma_fast_q12.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\get_hanning_window.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\get_scaling_square.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\ilbc_specific_functions.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\levinson_durbin.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\lpc_to_refl_coef.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\min_max_operations.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\min_max_operations_neon.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\randomization_functions.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\real_fft.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\refl_coef_to_lpc.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_48khz.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_by_2.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_by_2_internal.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_fractional.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\splitting_filter_impl.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_init.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_inl.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_sqrt.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_sqrt_floor.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\sqrt_of_one_minus_x_squared.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\vector_scaling_operations.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\sparse_fir_filter.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\wav_file.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\wav_header.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core_neon.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core_sse2.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_resampler.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\echo_cancellation.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core_c.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core_neon.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\echo_control_mobile.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\analog_agc.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\digital_agc.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\logging\apm_data_dumper.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression_x.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core_c.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core_neon.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\ns_core.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\splitting_filter.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\three_band_filter_bank.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\block_mean_calculator.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator_wrapper.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_neon.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_sse2.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\system_wrappers\source\cpu_features.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="os\windows\CXWrapper.cpp">
<Filter>windows</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="VoIPController.h" />
<ClInclude Include="VoIPServerConfig.h" />
<ClInclude Include="BlockingQueue.h" />
<ClInclude Include="BufferInputStream.h" />
<ClInclude Include="BufferOutputStream.h" />
<ClInclude Include="BufferPool.h" />
<ClInclude Include="CongestionControl.h" />
<ClInclude Include="EchoCanceller.h" />
<ClInclude Include="JitterBuffer.h" />
<ClInclude Include="logging.h" />
<ClInclude Include="MediaStreamItf.h" />
<ClInclude Include="NetworkSocket.h" />
<ClInclude Include="OpusDecoder.h" />
<ClInclude Include="OpusEncoder.h" />
<ClInclude Include="threading.h" />
<ClInclude Include="audio\AudioInput.h">
<Filter>audio</Filter>
</ClInclude>
<ClInclude Include="audio\AudioOutput.h">
<Filter>audio</Filter>
</ClInclude>
<ClInclude Include="audio\Resampler.h">
<Filter>audio</Filter>
</ClInclude>
<ClInclude Include="os\windows\AudioInputWASAPI.h">
<Filter>windows</Filter>
</ClInclude>
<ClInclude Include="os\windows\AudioOutputWASAPI.h">
<Filter>windows</Filter>
</ClInclude>
<ClInclude Include="os\windows\NetworkSocketWinsock.h">
<Filter>windows</Filter>
</ClInclude>
<ClInclude Include="os\windows\WindowsSandboxUtils.h">
<Filter>windows</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\array_view.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\atomicops.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\basictypes.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\checks.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\constructormagic.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\safe_compare.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\safe_conversions.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\safe_conversions_impl.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\sanitizer.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\stringutils.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\type_traits.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\channel_buffer.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\fft4g.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\include\audio_util.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\ring_buffer.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\complex_fft_tables.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\real_fft.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\signal_processing_library.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\spl_inl.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\spl_inl_armv7.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\spl_inl_mips.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_by_2_internal.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\sparse_fir_filter.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\wav_file.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\wav_header.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_common.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core_optimized_methods.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_resampler.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\echo_cancellation.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_defines.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\echo_control_mobile.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\analog_agc.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\digital_agc.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\gain_control.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\logging\apm_data_dumper.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\defines.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression_x.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_defines.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\ns_core.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\windows_private.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\splitting_filter.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\three_band_filter_bank.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\block_mean_calculator.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator_internal.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator_wrapper.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_tables_common.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_tables_neon_sse2.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\asm_defines.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\compile_assert_c.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\cpu_features_wrapper.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\metrics.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\typedefs.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="os\windows\CXWrapper.h">
<Filter>windows</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -0,0 +1,420 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM">
<Configuration>Debug</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM">
<Configuration>Release</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{21f10158-c078-4bd7-a82a-9c4aeb8e2f8e}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<ProjectName>libtgvoip</ProjectName>
<RootNamespace>libtgvoip</RootNamespace>
<DefaultLanguage>en-US</DefaultLanguage>
<MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
<AppContainerApplication>true</AppContainerApplication>
<ApplicationType>Windows Phone Silverlight</ApplicationType>
<ApplicationTypeRevision>8.1</ApplicationTypeRevision>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;TGVOIP_WP_SILVERLIGHT;_CRT_SECURE_NO_WARNINGS;NOMINMAX;WEBRTC_APM_DEBUG_DUMP=0;TGVOIP_USE_CUSTOM_CRYPTO;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>webrtc_dsp;../TelegramClient/TelegramClient.Opus/opus/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>ws2_32.lib;phoneaudioses.lib;../TelegramClient/$(Platform)/$(Configuration)/TelegramClient.Opus/TelegramClient.Opus.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;TGVOIP_WP_SILVERLIGHT;NDEBUG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>webrtc_dsp;../TelegramClient/TelegramClient.Opus/opus/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>ws2_32.lib;phoneaudioses.lib;../TelegramClient/$(Platform)/$(Configuration)/TelegramClient.Opus/TelegramClient.Opus.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;TGVOIP_WP_SILVERLIGHT;_CRT_SECURE_NO_WARNINGS;NOMINMAX;WEBRTC_APM_DEBUG_DUMP=0;TGVOIP_USE_CUSTOM_CRYPTO;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>webrtc_dsp;../TelegramClient/TelegramClient.Opus/opus/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>ws2_32.lib;phoneaudioses.lib;../TelegramClient/$(Platform)/$(Configuration)/TelegramClient.Opus/TelegramClient.Opus.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PreprocessorDefinitions>_WINRT_DLL;TGVOIP_WP_SILVERLIGHT;NDEBUG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;WEBRTC_APM_DEBUG_DUMP=0;TGVOIP_USE_CUSTOM_CRYPTO;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>webrtc_dsp;../TelegramClient/TelegramClient.Opus/opus/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeTypeInfo>true</RuntimeTypeInfo>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
<AdditionalDependencies>ws2_32.lib;phoneaudioses.lib;../TelegramClient/$(Platform)/$(Configuration)/TelegramClient.Opus/TelegramClient.Opus.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="audio\AudioInput.h" />
<ClInclude Include="audio\AudioOutput.h" />
<ClInclude Include="audio\Resampler.h" />
<ClInclude Include="BlockingQueue.h" />
<ClInclude Include="BufferInputStream.h" />
<ClInclude Include="BufferOutputStream.h" />
<ClInclude Include="BufferPool.h" />
<ClInclude Include="CongestionControl.h" />
<ClInclude Include="EchoCanceller.h" />
<ClInclude Include="JitterBuffer.h" />
<ClInclude Include="logging.h" />
<ClInclude Include="MediaStreamItf.h" />
<ClInclude Include="NetworkSocket.h" />
<ClInclude Include="OpusDecoder.h" />
<ClInclude Include="OpusEncoder.h" />
<ClInclude Include="os\windows\AudioInputWASAPI.h" />
<ClInclude Include="os\windows\AudioOutputWASAPI.h" />
<ClInclude Include="os\windows\NetworkSocketWinsock.h" />
<ClInclude Include="os\windows\WindowsSandboxUtils.h" />
<ClInclude Include="threading.h" />
<ClInclude Include="VoIPController.h" />
<ClInclude Include="os\windows\CXWrapper.h" />
<ClInclude Include="VoIPServerConfig.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\array_view.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\atomicops.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\basictypes.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\checks.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\constructormagic.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\safe_compare.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\safe_conversions.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\safe_conversions_impl.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\sanitizer.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\stringutils.h" />
<ClInclude Include="webrtc_dsp\webrtc\base\type_traits.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\channel_buffer.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\fft4g.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\include\audio_util.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\ring_buffer.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\complex_fft_tables.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\real_fft.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\signal_processing_library.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\spl_inl.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\spl_inl_armv7.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\spl_inl_mips.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_by_2_internal.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\sparse_fir_filter.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\wav_file.h" />
<ClInclude Include="webrtc_dsp\webrtc\common_audio\wav_header.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_defines.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\echo_control_mobile.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_common.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core_optimized_methods.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_resampler.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\echo_cancellation.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\analog_agc.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\digital_agc.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\gain_control.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\logging\apm_data_dumper.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\defines.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression_x.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_defines.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\ns_core.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\windows_private.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\splitting_filter.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\three_band_filter_bank.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\block_mean_calculator.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator_internal.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator_wrapper.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_tables_common.h" />
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_tables_neon_sse2.h" />
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\asm_defines.h" />
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\compile_assert_c.h" />
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\cpu_features_wrapper.h" />
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\metrics.h" />
<ClInclude Include="webrtc_dsp\webrtc\typedefs.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="audio\AudioInput.cpp" />
<ClCompile Include="audio\AudioOutput.cpp" />
<ClCompile Include="audio\Resampler.cpp" />
<ClCompile Include="BlockingQueue.cpp" />
<ClCompile Include="BufferInputStream.cpp" />
<ClCompile Include="BufferOutputStream.cpp" />
<ClCompile Include="BufferPool.cpp" />
<ClCompile Include="CongestionControl.cpp" />
<ClCompile Include="EchoCanceller.cpp" />
<ClCompile Include="JitterBuffer.cpp" />
<ClCompile Include="logging.cpp" />
<ClCompile Include="MediaStreamItf.cpp" />
<ClCompile Include="NetworkSocket.cpp" />
<ClCompile Include="OpusDecoder.cpp" />
<ClCompile Include="OpusEncoder.cpp" />
<ClCompile Include="os\windows\AudioInputWASAPI.cpp" />
<ClCompile Include="os\windows\AudioOutputWASAPI.cpp" />
<ClCompile Include="os\windows\NetworkSocketWinsock.cpp" />
<ClCompile Include="os\windows\WindowsSandboxUtils.cpp" />
<ClCompile Include="VoIPController.cpp" />
<ClCompile Include="os\windows\CXWrapper.cpp" />
<ClCompile Include="VoIPServerConfig.cpp" />
<ClCompile Include="webrtc_dsp\webrtc\base\checks.cc" />
<ClCompile Include="webrtc_dsp\webrtc\base\stringutils.cc" />
<ClCompile Include="webrtc_dsp\webrtc\common_audio\audio_util.cc" />
<ClCompile Include="webrtc_dsp\webrtc\common_audio\channel_buffer.cc" />
<ClCompile Include="webrtc_dsp\webrtc\common_audio\fft4g.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\ring_buffer.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\auto_correlation.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\auto_corr_to_refl_coef.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\complex_bit_reverse.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\complex_fft.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\copy_set_operations.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\cross_correlation.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\cross_correlation_neon.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\division_operations.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\dot_product_with_scale.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\downsample_fast.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\downsample_fast_neon.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\energy.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ar.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ar_fast_q12.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ma_fast_q12.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\get_hanning_window.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\get_scaling_square.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\ilbc_specific_functions.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\levinson_durbin.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\lpc_to_refl_coef.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\min_max_operations.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\min_max_operations_neon.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\randomization_functions.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\real_fft.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\refl_coef_to_lpc.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_48khz.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_by_2.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_by_2_internal.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_fractional.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\splitting_filter_impl.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_init.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_inl.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_sqrt.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_sqrt_floor.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\sqrt_of_one_minus_x_squared.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\vector_scaling_operations.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\sparse_fir_filter.cc" />
<ClCompile Include="webrtc_dsp\webrtc\common_audio\wav_file.cc" />
<ClCompile Include="webrtc_dsp\webrtc\common_audio\wav_header.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core_c.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core_neon.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\echo_control_mobile.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core_neon.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core_sse2.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_resampler.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\echo_cancellation.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\analog_agc.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\digital_agc.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\logging\apm_data_dumper.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression_x.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core_c.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core_neon.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\ns_core.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\splitting_filter.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\three_band_filter_bank.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\block_mean_calculator.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator_wrapper.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_neon.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_sse2.cc" />
<ClCompile Include="webrtc_dsp\webrtc\system_wrappers\source\cpu_features.cc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,492 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Resources">
<UniqueIdentifier>11199e80-17a0-460f-a780-9bfde20eb11c</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="audio">
<UniqueIdentifier>{c5b75146-c75a-4c56-aeb2-2781658d7b0a}</UniqueIdentifier>
</Filter>
<Filter Include="windows">
<UniqueIdentifier>{de1527d9-7564-4e96-9653-6e023b90d2bc}</UniqueIdentifier>
</Filter>
<Filter Include="webrtc_dsp">
<UniqueIdentifier>{3b15701a-65dd-4d52-92d4-a7b64a73b293}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="VoIPController.cpp" />
<ClCompile Include="VoIPServerConfig.cpp" />
<ClCompile Include="BlockingQueue.cpp" />
<ClCompile Include="BufferInputStream.cpp" />
<ClCompile Include="BufferOutputStream.cpp" />
<ClCompile Include="BufferPool.cpp" />
<ClCompile Include="CongestionControl.cpp" />
<ClCompile Include="EchoCanceller.cpp" />
<ClCompile Include="JitterBuffer.cpp" />
<ClCompile Include="logging.cpp" />
<ClCompile Include="MediaStreamItf.cpp" />
<ClCompile Include="NetworkSocket.cpp" />
<ClCompile Include="OpusDecoder.cpp" />
<ClCompile Include="OpusEncoder.cpp" />
<ClCompile Include="audio\AudioInput.cpp">
<Filter>audio</Filter>
</ClCompile>
<ClCompile Include="audio\AudioOutput.cpp">
<Filter>audio</Filter>
</ClCompile>
<ClCompile Include="audio\Resampler.cpp">
<Filter>audio</Filter>
</ClCompile>
<ClCompile Include="os\windows\AudioInputWASAPI.cpp">
<Filter>windows</Filter>
</ClCompile>
<ClCompile Include="os\windows\AudioOutputWASAPI.cpp">
<Filter>windows</Filter>
</ClCompile>
<ClCompile Include="os\windows\NetworkSocketWinsock.cpp">
<Filter>windows</Filter>
</ClCompile>
<ClCompile Include="os\windows\WindowsSandboxUtils.cpp">
<Filter>windows</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\base\checks.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\base\stringutils.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\audio_util.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\channel_buffer.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\fft4g.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\ring_buffer.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\auto_correlation.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\auto_corr_to_refl_coef.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\complex_bit_reverse.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\complex_fft.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\copy_set_operations.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\cross_correlation.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\cross_correlation_neon.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\division_operations.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\dot_product_with_scale.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\downsample_fast.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\downsample_fast_neon.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\energy.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ar.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ar_fast_q12.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ma_fast_q12.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\get_hanning_window.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\get_scaling_square.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\ilbc_specific_functions.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\levinson_durbin.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\lpc_to_refl_coef.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\min_max_operations.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\min_max_operations_neon.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\randomization_functions.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\real_fft.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\refl_coef_to_lpc.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_48khz.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_by_2.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_by_2_internal.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_fractional.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\splitting_filter_impl.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_init.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_inl.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_sqrt.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_sqrt_floor.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\sqrt_of_one_minus_x_squared.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\vector_scaling_operations.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\sparse_fir_filter.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\wav_file.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\wav_header.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core_neon.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core_sse2.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_resampler.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\echo_cancellation.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core_c.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core_neon.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\echo_control_mobile.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\analog_agc.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\digital_agc.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\logging\apm_data_dumper.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression_x.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core_c.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core_neon.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\ns_core.c">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\splitting_filter.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\three_band_filter_bank.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\block_mean_calculator.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator_wrapper.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_neon.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_sse2.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\system_wrappers\source\cpu_features.cc">
<Filter>webrtc_dsp</Filter>
</ClCompile>
<ClCompile Include="os\windows\CXWrapper.cpp">
<Filter>windows</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="VoIPController.h" />
<ClInclude Include="VoIPServerConfig.h" />
<ClInclude Include="BlockingQueue.h" />
<ClInclude Include="BufferInputStream.h" />
<ClInclude Include="BufferOutputStream.h" />
<ClInclude Include="BufferPool.h" />
<ClInclude Include="CongestionControl.h" />
<ClInclude Include="EchoCanceller.h" />
<ClInclude Include="JitterBuffer.h" />
<ClInclude Include="logging.h" />
<ClInclude Include="MediaStreamItf.h" />
<ClInclude Include="NetworkSocket.h" />
<ClInclude Include="OpusDecoder.h" />
<ClInclude Include="OpusEncoder.h" />
<ClInclude Include="threading.h" />
<ClInclude Include="audio\AudioInput.h">
<Filter>audio</Filter>
</ClInclude>
<ClInclude Include="audio\AudioOutput.h">
<Filter>audio</Filter>
</ClInclude>
<ClInclude Include="audio\Resampler.h">
<Filter>audio</Filter>
</ClInclude>
<ClInclude Include="os\windows\AudioInputWASAPI.h">
<Filter>windows</Filter>
</ClInclude>
<ClInclude Include="os\windows\AudioOutputWASAPI.h">
<Filter>windows</Filter>
</ClInclude>
<ClInclude Include="os\windows\NetworkSocketWinsock.h">
<Filter>windows</Filter>
</ClInclude>
<ClInclude Include="os\windows\WindowsSandboxUtils.h">
<Filter>windows</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\array_view.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\atomicops.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\basictypes.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\checks.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\constructormagic.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\safe_compare.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\safe_conversions.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\safe_conversions_impl.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\sanitizer.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\stringutils.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\base\type_traits.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\channel_buffer.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\fft4g.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\include\audio_util.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\ring_buffer.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\complex_fft_tables.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\real_fft.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\signal_processing_library.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\spl_inl.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\spl_inl_armv7.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\include\spl_inl_mips.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_by_2_internal.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\sparse_fir_filter.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\wav_file.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\common_audio\wav_header.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_common.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core_optimized_methods.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_resampler.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aec\echo_cancellation.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_defines.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\echo_control_mobile.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\analog_agc.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\digital_agc.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\gain_control.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\logging\apm_data_dumper.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\defines.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression_x.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_defines.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\ns_core.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\ns\windows_private.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\splitting_filter.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\three_band_filter_bank.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\block_mean_calculator.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator_internal.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator_wrapper.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_tables_common.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_tables_neon_sse2.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\asm_defines.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\compile_assert_c.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\cpu_features_wrapper.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\system_wrappers\include\metrics.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="webrtc_dsp\webrtc\typedefs.h">
<Filter>webrtc_dsp</Filter>
</ClInclude>
<ClInclude Include="os\windows\CXWrapper.h">
<Filter>windows</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -0,0 +1,398 @@
# GYP project file for TDesktop
{
'targets': [
{
'target_name': 'libtgvoip',
'type': 'static_library',
'dependencies': [],
'defines': [
'WEBRTC_APM_DEBUG_DUMP=0',
'TGVOIP_USE_DESKTOP_DSP',
],
'variables': {
'tgvoip_src_loc': '.',
'official_build_target%': '',
'linux_path_opus_include%': '<(DEPTH)/../../../Libraries/opus/include',
},
'include_dirs': [
'<(tgvoip_src_loc)/webrtc_dsp',
'<(linux_path_opus_include)',
],
'direct_dependent_settings': {
'include_dirs': [
'<(tgvoip_src_loc)',
],
},
'export_dependent_settings': [],
'sources': [
'<(tgvoip_src_loc)/BlockingQueue.cpp',
'<(tgvoip_src_loc)/BlockingQueue.h',
'<(tgvoip_src_loc)/BufferInputStream.cpp',
'<(tgvoip_src_loc)/BufferInputStream.h',
'<(tgvoip_src_loc)/BufferOutputStream.cpp',
'<(tgvoip_src_loc)/BufferOutputStream.h',
'<(tgvoip_src_loc)/BufferPool.cpp',
'<(tgvoip_src_loc)/BufferPool.h',
'<(tgvoip_src_loc)/CongestionControl.cpp',
'<(tgvoip_src_loc)/CongestionControl.h',
'<(tgvoip_src_loc)/EchoCanceller.cpp',
'<(tgvoip_src_loc)/EchoCanceller.h',
'<(tgvoip_src_loc)/JitterBuffer.cpp',
'<(tgvoip_src_loc)/JitterBuffer.h',
'<(tgvoip_src_loc)/logging.cpp',
'<(tgvoip_src_loc)/logging.h',
'<(tgvoip_src_loc)/MediaStreamItf.cpp',
'<(tgvoip_src_loc)/MediaStreamItf.h',
'<(tgvoip_src_loc)/OpusDecoder.cpp',
'<(tgvoip_src_loc)/OpusDecoder.h',
'<(tgvoip_src_loc)/OpusEncoder.cpp',
'<(tgvoip_src_loc)/OpusEncoder.h',
'<(tgvoip_src_loc)/threading.h',
'<(tgvoip_src_loc)/VoIPController.cpp',
'<(tgvoip_src_loc)/VoIPController.h',
'<(tgvoip_src_loc)/VoIPServerConfig.cpp',
'<(tgvoip_src_loc)/VoIPServerConfig.h',
'<(tgvoip_src_loc)/audio/AudioInput.cpp',
'<(tgvoip_src_loc)/audio/AudioInput.h',
'<(tgvoip_src_loc)/audio/AudioOutput.cpp',
'<(tgvoip_src_loc)/audio/AudioOutput.h',
'<(tgvoip_src_loc)/audio/Resampler.cpp',
'<(tgvoip_src_loc)/audio/Resampler.h',
'<(tgvoip_src_loc)/NetworkSocket.cpp',
'<(tgvoip_src_loc)/NetworkSocket.h',
# Windows
'<(tgvoip_src_loc)/os/windows/NetworkSocketWinsock.cpp',
'<(tgvoip_src_loc)/os/windows/NetworkSocketWinsock.h',
'<(tgvoip_src_loc)/os/windows/AudioInputWave.cpp',
'<(tgvoip_src_loc)/os/windows/AudioInputWave.h',
'<(tgvoip_src_loc)/os/windows/AudioOutputWave.cpp',
'<(tgvoip_src_loc)/os/windows/AudioOutputWave.h',
'<(tgvoip_src_loc)/os/windows/AudioOutputWASAPI.cpp',
'<(tgvoip_src_loc)/os/windows/AudioOutputWASAPI.h',
'<(tgvoip_src_loc)/os/windows/AudioInputWASAPI.cpp',
'<(tgvoip_src_loc)/os/windows/AudioInputWASAPI.h',
# macOS
'<(tgvoip_src_loc)/os/darwin/AudioInputAudioUnit.cpp',
'<(tgvoip_src_loc)/os/darwin/AudioInputAudioUnit.h',
'<(tgvoip_src_loc)/os/darwin/AudioOutputAudioUnit.cpp',
'<(tgvoip_src_loc)/os/darwin/AudioOutputAudioUnit.h',
'<(tgvoip_src_loc)/os/darwin/AudioInputAudioUnitOSX.cpp',
'<(tgvoip_src_loc)/os/darwin/AudioInputAudioUnitOSX.h',
'<(tgvoip_src_loc)/os/darwin/AudioOutputAudioUnitOSX.cpp',
'<(tgvoip_src_loc)/os/darwin/AudioOutputAudioUnitOSX.h',
'<(tgvoip_src_loc)/os/darwin/AudioUnitIO.cpp',
'<(tgvoip_src_loc)/os/darwin/AudioUnitIO.h',
'<(tgvoip_src_loc)/os/darwin/DarwinSpecific.mm',
'<(tgvoip_src_loc)/os/darwin/DarwinSpecific.h',
# Linux
'<(tgvoip_src_loc)/os/linux/AudioInputALSA.cpp',
'<(tgvoip_src_loc)/os/linux/AudioInputALSA.h',
'<(tgvoip_src_loc)/os/linux/AudioOutputALSA.cpp',
'<(tgvoip_src_loc)/os/linux/AudioOutputALSA.h',
'<(tgvoip_src_loc)/os/linux/AudioOutputPulse.cpp',
'<(tgvoip_src_loc)/os/linux/AudioOutputPulse.h',
'<(tgvoip_src_loc)/os/linux/AudioInputPulse.cpp',
'<(tgvoip_src_loc)/os/linux/AudioInputPulse.h',
'<(tgvoip_src_loc)/os/linux/PulseAudioLoader.cpp',
'<(tgvoip_src_loc)/os/linux/PulseAudioLoader.h',
# POSIX
'<(tgvoip_src_loc)/os/posix/NetworkSocketPosix.cpp',
'<(tgvoip_src_loc)/os/posix/NetworkSocketPosix.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/base/array_view.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/base/atomicops.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/base/basictypes.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/base/checks.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/base/checks.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/base/constructormagic.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/base/safe_compare.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/base/safe_conversions.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/base/safe_conversions_impl.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/base/sanitizer.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/base/stringutils.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/base/stringutils.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/base/type_traits.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/audio_util.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/channel_buffer.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/channel_buffer.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/fft4g.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/fft4g.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/include/audio_util.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/ring_buffer.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/ring_buffer.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/auto_corr_to_refl_coef.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/auto_correlation.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/complex_bit_reverse.c',
# 'webrtc_dsp/webrtc/common_audio/signal_processing/complex_bit_reverse_arm.S',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/complex_fft.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/complex_fft_tables.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/copy_set_operations.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/cross_correlation.c',
# '<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/cross_correlation_neon.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/division_operations.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/dot_product_with_scale.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/downsample_fast.c',
# '<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/downsample_fast_neon.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/energy.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/filter_ar.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/filter_ar_fast_q12.c',
# 'webrtc_dsp/webrtc/common_audio/signal_processing/filter_ar_fast_q12_armv7.S',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/filter_ma_fast_q12.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/get_hanning_window.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/get_scaling_square.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/ilbc_specific_functions.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/include/real_fft.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/include/signal_processing_library.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/include/spl_inl.h',
# '<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/include/spl_inl_armv7.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/include/spl_inl_mips.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/levinson_durbin.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/lpc_to_refl_coef.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/min_max_operations.c',
# '<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/min_max_operations_neon.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/randomization_functions.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/real_fft.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/refl_coef_to_lpc.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/resample.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/resample_48khz.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/resample_by_2.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/resample_by_2_internal.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/resample_by_2_internal.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/resample_fractional.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/spl_init.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/spl_inl.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/spl_sqrt.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/spl_sqrt_floor.c',
#'webrtc_dsp/webrtc/common_audio/signal_processing/spl_sqrt_floor_arm.S',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/splitting_filter_impl.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/sqrt_of_one_minus_x_squared.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/signal_processing/vector_scaling_operations.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/sparse_fir_filter.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/sparse_fir_filter.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/wav_file.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/wav_file.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/wav_header.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/common_audio/wav_header.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/aec/aec_common.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/aec/aec_core.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/aec/aec_core.h',
# '<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/aec/aec_core_neon.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/aec/aec_core_optimized_methods.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/aec/aec_core_sse2.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/aec/aec_resampler.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/aec/aec_resampler.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/aec/echo_cancellation.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/aec/echo_cancellation.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/aecm/aecm_core.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/aecm/aecm_core.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/aecm/aecm_core_c.cc',
# '<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/aecm/aecm_core_neon.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/aecm/aecm_defines.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/aecm/echo_control_mobile.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/aecm/echo_control_mobile.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/agc/legacy/analog_agc.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/agc/legacy/analog_agc.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/agc/legacy/digital_agc.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/agc/legacy/digital_agc.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/agc/legacy/gain_control.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/logging/apm_data_dumper.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/logging/apm_data_dumper.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/ns/defines.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/ns/noise_suppression.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/ns/noise_suppression.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/ns/noise_suppression_x.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/ns/noise_suppression_x.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/ns/ns_core.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/ns/ns_core.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/ns/nsx_core.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/ns/nsx_core.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/ns/nsx_core_c.c',
# '<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/ns/nsx_core_neon.c',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/ns/nsx_defines.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/ns/windows_private.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/splitting_filter.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/splitting_filter.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/three_band_filter_bank.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/three_band_filter_bank.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/utility/block_mean_calculator.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/utility/block_mean_calculator.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/utility/delay_estimator.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/utility/delay_estimator.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/utility/delay_estimator_internal.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/utility/delay_estimator_wrapper.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/utility/ooura_fft.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/utility/ooura_fft.h',
# '<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/utility/ooura_fft_neon.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/utility/ooura_fft_sse2.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/utility/ooura_fft_tables_common.h',
# '<(tgvoip_src_loc)/webrtc_dsp/webrtc/modules/audio_processing/utility/ooura_fft_tables_neon_sse2.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/system_wrappers/include/asm_defines.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/system_wrappers/include/compile_assert_c.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/system_wrappers/include/cpu_features_wrapper.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/system_wrappers/include/metrics.h',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/system_wrappers/source/cpu_features.cc',
'<(tgvoip_src_loc)/webrtc_dsp/webrtc/typedefs.h',
],
'libraries': [],
'configurations': {
'Debug': {},
'Release': {},
},
'conditions': [
[
'"<(OS)" != "win"', {
'sources/': [['exclude', '<(tgvoip_src_loc)/os/windows/']],
}, {
'sources/': [['exclude', '<(tgvoip_src_loc)/os/posix/']],
},
],
[
'"<(OS)" != "mac"', {
'sources/': [['exclude', '<(tgvoip_src_loc)/os/darwin/']],
},
],
[
'"<(OS)" != "linux"', {
'sources/': [['exclude', '<(tgvoip_src_loc)/os/linux/']],
},
],
[
'"<(OS)" == "mac"', {
'xcode_settings': {
'CLANG_CXX_LANGUAGE_STANDARD': 'c++1z',
},
'defines': [
'WEBRTC_POSIX',
'WEBRTC_MAC',
'TARGET_OS_OSX',
],
'conditions': [
[ '"<(official_build_target)" == "mac32"', {
'xcode_settings': {
'MACOSX_DEPLOYMENT_TARGET': '10.6',
'OTHER_CPLUSPLUSFLAGS': [ '-nostdinc++' ],
},
'include_dirs': [
'/usr/local/macold/include/c++/v1',
'<(DEPTH)/../../../Libraries/macold/openssl/include',
],
}, {
'xcode_settings': {
'MACOSX_DEPLOYMENT_TARGET': '10.8',
'CLANG_CXX_LIBRARY': 'libc++',
},
'include_dirs': [
'<(DEPTH)/../../../Libraries/openssl/include',
],
}]
]
},
],
[
'"<(OS)" == "win"', {
'msbuild_toolset': 'v141',
'defines': [
'NOMINMAX',
'_USING_V110_SDK71_',
'TGVOIP_WINXP_COMPAT'
],
'libraries': [
'winmm',
'ws2_32',
'kernel32',
'user32',
],
'msvs_cygwin_shell': 0,
'msvs_settings': {
'VCCLCompilerTool': {
'ProgramDataBaseFileName': '$(OutDir)\\$(ProjectName).pdb',
'DebugInformationFormat': '3', # Program Database (/Zi)
'AdditionalOptions': [
'/MP', # Enable multi process build.
'/EHsc', # Catch C++ exceptions only, extern C functions never throw a C++ exception.
'/wd4068', # Disable "warning C4068: unknown pragma"
],
'TreatWChar_tAsBuiltInType': 'false',
},
},
'msvs_external_builder_build_cmd': [
'ninja.exe',
'-C',
'$(OutDir)',
'-k0',
'$(ProjectName)',
],
'configurations': {
'Debug': {
'defines': [
'_DEBUG',
],
'include_dirs': [
'<(DEPTH)/../../../Libraries/openssl/Debug/include',
],
'msvs_settings': {
'VCCLCompilerTool': {
'Optimization': '0', # Disabled (/Od)
'RuntimeLibrary': '1', # Multi-threaded Debug (/MTd)
'RuntimeTypeInfo': 'true',
},
'VCLibrarianTool': {
'AdditionalOptions': [
'/NODEFAULTLIB:LIBCMT'
]
}
},
},
'Release': {
'defines': [
'NDEBUG',
],
'include_dirs': [
'<(DEPTH)/../../../Libraries/openssl/Release/include',
],
'msvs_settings': {
'VCCLCompilerTool': {
'Optimization': '2', # Maximize Speed (/O2)
'InlineFunctionExpansion': '2', # Any suitable (/Ob2)
'EnableIntrinsicFunctions': 'true', # Yes (/Oi)
'FavorSizeOrSpeed': '1', # Favor fast code (/Ot)
'RuntimeLibrary': '0', # Multi-threaded (/MT)
'EnableEnhancedInstructionSet': '2', # Streaming SIMD Extensions 2 (/arch:SSE2)
'WholeProgramOptimization': 'true', # /GL
},
'VCLibrarianTool': {
'AdditionalOptions': [
'/LTCG',
]
},
},
},
},
},
],
[
'"<(OS)" == "linux"', {
'defines': [
'WEBRTC_POSIX',
],
'cflags_cc': [
'-msse2',
],
'direct_dependent_settings': {
'libraries': [
],
},
},
],
],
},
],
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:libtgvoip.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0820"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "69F842351E67540700C110F7"
BuildableName = "libtgvoip.framework"
BlueprintName = "libtgvoip"
ReferencedContainer = "container:libtgvoip.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "69F842351E67540700C110F7"
BuildableName = "libtgvoip.framework"
BlueprintName = "libtgvoip"
ReferencedContainer = "container:libtgvoip.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "69F842351E67540700C110F7"
BuildableName = "libtgvoip.framework"
BlueprintName = "libtgvoip"
ReferencedContainer = "container:libtgvoip.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>libtgvoip.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>69F842351E67540700C110F7</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>69F842351E67540700C110F7</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:libtgvoip.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0820"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "69F842351E67540700C110F7"
BuildableName = "libtgvoip.framework"
BlueprintName = "libtgvoip"
ReferencedContainer = "container:libtgvoip.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "69F842351E67540700C110F7"
BuildableName = "libtgvoip.framework"
BlueprintName = "libtgvoip"
ReferencedContainer = "container:libtgvoip.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "69F842351E67540700C110F7"
BuildableName = "libtgvoip.framework"
BlueprintName = "libtgvoip"
ReferencedContainer = "container:libtgvoip.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>libtgvoip.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>69F842351E67540700C110F7</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>69F842351E67540700C110F7</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@ -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 <stdio.h>
#include <stdarg.h>
#include <time.h>
#include "VoIPController.h"
#ifdef __ANDROID__
#include <sys/system_properties.h>
#elif defined(__linux__)
#include <sys/utsname.h>
#endif
#ifdef __APPLE__
#include <TargetConditionals.h>
#include "os/darwin/DarwinSpecific.h"
#endif
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);
}
}
void tgvoip_log_file_write_header(){
if(tgvoipLogFile){
time_t t = time(0);
struct tm *now = localtime(&t);
#if defined(_WIN32)
#if WINAPI_PARTITION_DESKTOP
char systemVersion[64];
OSVERSIONINFOA vInfo;
vInfo.dwOSVersionInfoSize=sizeof(vInfo);
GetVersionExA(&vInfo);
snprintf(systemVersion, sizeof(systemVersion), "Windows %d.%d.%d %s", vInfo.dwMajorVersion, vInfo.dwMinorVersion, vInfo.dwBuildNumber, vInfo.szCSDVersion);
#else
char* systemVersion="Windows RT";
#endif
#elif defined(__linux__)
#ifdef __ANDROID__
char systemVersion[128];
char sysRel[PROP_VALUE_MAX];
char deviceVendor[PROP_VALUE_MAX];
char deviceModel[PROP_VALUE_MAX];
__system_property_get("ro.build.version.release", sysRel);
__system_property_get("ro.product.manufacturer", deviceVendor);
__system_property_get("ro.product.model", deviceModel);
snprintf(systemVersion, sizeof(systemVersion), "Android %s (%s %s)", sysRel, deviceVendor, deviceModel);
#else
struct utsname sysname;
uname(&sysname);
char systemVersion[128];
snprintf(systemVersion, sizeof(systemVersion), "%s %s (%s)", sysname.sysname, sysname.release, sysname.version);
#endif
#elif defined(__APPLE__)
char osxVer[128];
tgvoip::DarwinSpecific::GetSystemName(osxVer, sizeof(osxVer));
char systemVersion[128];
#if TARGET_OS_OSX
snprintf(systemVersion, sizeof(systemVersion), "OS X %s", osxVer);
#elif TARGET_OS_IPHONE
snprintf(systemVersion, sizeof(systemVersion), "iOS %s", osxVer);
#else
snprintf(systemVersion, sizeof(systemVersion), "Unknown Darwin %s", osxVer);
#endif
#else
const char* systemVersion="Unknown OS";
#endif
#if defined(__aarch64__)
const char* cpuArch="ARM64";
#elif defined(__arm__) || defined(_M_ARM)
const char* cpuArch="ARM";
#elif defined(_M_X64) || defined(__x86_64__)
const char* cpuArch="x86_64";
#elif defined(_M_IX86) || defined(__i386__)
const char* cpuArch="x86";
#else
const char* cpuArch="Unknown CPU";
#endif
fprintf(tgvoipLogFile, "---------------\nlibtgvoip v" LIBTGVOIP_VERSION " on %s %s\nLog started on %d/%02d/%d at %d:%02d:%02d\n---------------\n", systemVersion, cpuArch, now->tm_mday, now->tm_mon+1, now->tm_year+1900, now->tm_hour, now->tm_min, now->tm_sec);
}
}

97
Telegram/ThirdParty/libtgvoip/logging.h vendored Normal file
View File

@ -0,0 +1,97 @@
//
// 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 __LOGGING_H
#define __LOGGING_H
#define LSTR_INT(x) LSTR_DO_INT(x)
#define LSTR_DO_INT(x) #x
#ifdef __APPLE__
#include <TargetConditionals.h>
#endif
void tgvoip_log_file_printf(char level, const char* msg, ...);
void tgvoip_log_file_write_header();
#if defined(__ANDROID__)
#include <android/log.h>
//#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__)); 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 && defined(TGVOIP_HAVE_TGLOG)
#include "os/darwin/TGLogWrapper.h"
#define LOGV(msg, ...) {__tgvoip_call_tglog("V/tgvoip: " msg, ##__VA_ARGS__); tgvoip_log_file_printf('V', msg, ##__VA_ARGS__);}
#define LOGD(msg, ...) {__tgvoip_call_tglog("D/tgvoip: " msg, ##__VA_ARGS__); tgvoip_log_file_printf('D', msg, ##__VA_ARGS__);}
#define LOGI(msg, ...) {__tgvoip_call_tglog("I/tgvoip: " msg, ##__VA_ARGS__); tgvoip_log_file_printf('I', msg, ##__VA_ARGS__);}
#define LOGW(msg, ...) {__tgvoip_call_tglog("W/tgvoip: " msg, ##__VA_ARGS__); tgvoip_log_file_printf('W', msg, ##__VA_ARGS__);}
#define LOGE(msg, ...) {__tgvoip_call_tglog("E/tgvoip: " msg, ##__VA_ARGS__); tgvoip_log_file_printf('E', msg, ##__VA_ARGS__);}
#elif defined(_WIN32) && defined(_DEBUG)
#include <windows.h>
#include <stdio.h>
#if !defined(snprintf) && defined(_WIN32) && defined(__cplusplus_winrt)
#define snprintf _snprintf
#endif
#define _TGVOIP_W32_LOG_PRINT(verb, msg, ...){ char __log_buf[1024]; snprintf(__log_buf, 1024, "%c/tgvoip: " msg "\n", verb, ##__VA_ARGS__); OutputDebugStringA(__log_buf); tgvoip_log_file_printf((char)verb, msg, __VA_ARGS__);}
#define LOGV(msg, ...) _TGVOIP_W32_LOG_PRINT('V', msg, ##__VA_ARGS__)
#define LOGD(msg, ...) _TGVOIP_W32_LOG_PRINT('D', msg, ##__VA_ARGS__)
#define LOGI(msg, ...) _TGVOIP_W32_LOG_PRINT('I', msg, ##__VA_ARGS__)
#define LOGW(msg, ...) _TGVOIP_W32_LOG_PRINT('W', msg, ##__VA_ARGS__)
#define LOGE(msg, ...) _TGVOIP_W32_LOG_PRINT('E', msg, ##__VA_ARGS__)
#else
#include <stdio.h>
#define _TGVOIP_LOG_PRINT(verb, msg, ...) {printf("%c/tgvoip: " msg "\n", verb, ##__VA_ARGS__); tgvoip_log_file_printf(verb, msg, ##__VA_ARGS__);}
#define LOGV(msg, ...) _TGVOIP_LOG_PRINT('V', msg, ##__VA_ARGS__)
#define LOGD(msg, ...) _TGVOIP_LOG_PRINT('D', msg, ##__VA_ARGS__)
#define LOGI(msg, ...) _TGVOIP_LOG_PRINT('I', msg, ##__VA_ARGS__)
#define LOGW(msg, ...) _TGVOIP_LOG_PRINT('W', msg, ##__VA_ARGS__)
#define LOGE(msg, ...) _TGVOIP_LOG_PRINT('E', msg, ##__VA_ARGS__)
#endif
#ifdef TGVOIP_LOG_VERBOSITY
#if TGVOIP_LOG_VERBOSITY<5
#undef LOGV
#define LOGV(msg, ...)
#endif
#if TGVOIP_LOG_VERBOSITY<4
#undef LOGD
#define LOGD(msg, ...)
#endif
#if TGVOIP_LOG_VERBOSITY<3
#undef LOGI
#define LOGI(msg, ...)
#endif
#if TGVOIP_LOG_VERBOSITY<2
#undef LOGW
#define LOGW(msg, ...)
#endif
#if TGVOIP_LOG_VERBOSITY<1
#undef LOGE
#define LOGE(msg, ...)
#endif
#endif
#endif //__LOGGING_H

View File

@ -0,0 +1,123 @@
//
// 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 "AudioInputAndroid.h"
#include <stdio.h>
#include "../../logging.h"
extern JavaVM* sharedJVM;
using namespace tgvoip;
using namespace tgvoip::audio;
jmethodID AudioInputAndroid::initMethod=NULL;
jmethodID AudioInputAndroid::releaseMethod=NULL;
jmethodID AudioInputAndroid::startMethod=NULL;
jmethodID AudioInputAndroid::stopMethod=NULL;
jclass AudioInputAndroid::jniClass=NULL;
AudioInputAndroid::AudioInputAndroid(){
JNIEnv* env=NULL;
bool didAttach=false;
sharedJVM->GetEnv((void**) &env, JNI_VERSION_1_6);
if(!env){
sharedJVM->AttachCurrentThread(&env, NULL);
didAttach=true;
}
jmethodID ctor=env->GetMethodID(jniClass, "<init>", "(J)V");
jobject obj=env->NewObject(jniClass, ctor, (jlong)(intptr_t)this);
javaObject=env->NewGlobalRef(obj);
if(didAttach){
sharedJVM->DetachCurrentThread();
}
running=false;
init_mutex(mutex);
}
AudioInputAndroid::~AudioInputAndroid(){
{
MutexGuard 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;
if(didAttach){
sharedJVM->DetachCurrentThread();
}
}
free_mutex(mutex);
}
void AudioInputAndroid::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
MutexGuard 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, initMethod, sampleRate, bitsPerSample, channels, 960*2);
if(didAttach){
sharedJVM->DetachCurrentThread();
}
}
void AudioInputAndroid::Start(){
MutexGuard guard(mutex);
JNIEnv* env=NULL;
bool didAttach=false;
sharedJVM->GetEnv((void**) &env, JNI_VERSION_1_6);
if(!env){
sharedJVM->AttachCurrentThread(&env, NULL);
didAttach=true;
}
failed=!env->CallBooleanMethod(javaObject, startMethod);
if(didAttach){
sharedJVM->DetachCurrentThread();
}
running=true;
}
void AudioInputAndroid::Stop(){
MutexGuard guard(mutex);
running=false;
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, stopMethod);
if(didAttach){
sharedJVM->DetachCurrentThread();
}
}
void AudioInputAndroid::HandleCallback(JNIEnv* env, jobject buffer){
if(!running)
return;
unsigned char* buf=(unsigned char*) env->GetDirectBufferAddress(buffer);
size_t len=(size_t) env->GetDirectBufferCapacity(buffer);
InvokeCallback(buf, len);
}

View File

@ -0,0 +1,38 @@
//
// 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 LIBTGVOIP_AUDIOINPUTANDROID_H
#define LIBTGVOIP_AUDIOINPUTANDROID_H
#include <jni.h>
#include "../../audio/AudioInput.h"
#include "../../threading.h"
namespace tgvoip{ namespace audio{
class AudioInputAndroid : public AudioInput{
public:
AudioInputAndroid();
virtual ~AudioInputAndroid();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
virtual void Start();
virtual void Stop();
void HandleCallback(JNIEnv* env, jobject buffer);
static jmethodID initMethod;
static jmethodID releaseMethod;
static jmethodID startMethod;
static jmethodID stopMethod;
static jclass jniClass;
private:
jobject javaObject;
bool running;
tgvoip_mutex_t mutex;
};
}}
#endif //LIBTGVOIP_AUDIOINPUTANDROID_H

View File

@ -0,0 +1,137 @@
//
// 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 <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "AudioInputOpenSLES.h"
#include "../../logging.h"
#include "OpenSLEngineWrapper.h"
#define CHECK_SL_ERROR(res, msg) if(res!=SL_RESULT_SUCCESS){ LOGE(msg); return; }
#define BUFFER_SIZE 960 // 20 ms
using namespace tgvoip;
using namespace tgvoip::audio;
int AudioInputOpenSLES::nativeBufferSize;
AudioInputOpenSLES::AudioInputOpenSLES(){
slEngine=OpenSLEngineWrapper::CreateEngine();
LOGI("Native buffer size is %u samples", nativeBufferSize);
if(nativeBufferSize<BUFFER_SIZE && BUFFER_SIZE % nativeBufferSize!=0){
LOGE("20ms is not divisible by native buffer size!!");
}else if(nativeBufferSize>BUFFER_SIZE && nativeBufferSize%BUFFER_SIZE!=0){
LOGE("native buffer size is not multiple of 20ms!!");
nativeBufferSize+=nativeBufferSize%BUFFER_SIZE;
}
if(nativeBufferSize==BUFFER_SIZE)
nativeBufferSize*=2;
LOGI("Adjusted native buffer size is %u", nativeBufferSize);
buffer=(int16_t*)calloc(BUFFER_SIZE, sizeof(int16_t));
nativeBuffer=(int16_t*)calloc((size_t) nativeBufferSize, sizeof(int16_t));
slRecorderObj=NULL;
}
AudioInputOpenSLES::~AudioInputOpenSLES(){
//Stop();
(*slBufferQueue)->Clear(slBufferQueue);
(*slRecorderObj)->Destroy(slRecorderObj);
slRecorderObj=NULL;
slRecorder=NULL;
slBufferQueue=NULL;
slEngine=NULL;
OpenSLEngineWrapper::DestroyEngine();
free(buffer);
buffer=NULL;
free(nativeBuffer);
nativeBuffer=NULL;
}
void AudioInputOpenSLES::BufferCallback(SLAndroidSimpleBufferQueueItf bq, void *context){
((AudioInputOpenSLES*)context)->HandleSLCallback();
}
void AudioInputOpenSLES::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
assert(slRecorderObj==NULL);
SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE,
SL_IODEVICE_AUDIOINPUT,
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
SLDataSource audioSrc = {&loc_dev, NULL};
SLDataLocator_AndroidSimpleBufferQueue loc_bq =
{SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1};
SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, channels, sampleRate*1000,
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
channels==2 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER, SL_BYTEORDER_LITTLEENDIAN};
SLDataSink audioSnk = {&loc_bq, &format_pcm};
const SLInterfaceID id[2] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION};
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
SLresult result = (*slEngine)->CreateAudioRecorder(slEngine, &slRecorderObj, &audioSrc, &audioSnk, 2, id, req);
CHECK_SL_ERROR(result, "Error creating recorder");
SLAndroidConfigurationItf recorderConfig;
result = (*slRecorderObj)->GetInterface(slRecorderObj, SL_IID_ANDROIDCONFIGURATION, &recorderConfig);
SLint32 streamType = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
result = (*recorderConfig)->SetConfiguration(recorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &streamType, sizeof(SLint32));
result=(*slRecorderObj)->Realize(slRecorderObj, SL_BOOLEAN_FALSE);
CHECK_SL_ERROR(result, "Error realizing recorder");
result=(*slRecorderObj)->GetInterface(slRecorderObj, SL_IID_RECORD, &slRecorder);
CHECK_SL_ERROR(result, "Error getting recorder interface");
result=(*slRecorderObj)->GetInterface(slRecorderObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &slBufferQueue);
CHECK_SL_ERROR(result, "Error getting buffer queue");
result=(*slBufferQueue)->RegisterCallback(slBufferQueue, AudioInputOpenSLES::BufferCallback, this);
CHECK_SL_ERROR(result, "Error setting buffer queue callback");
(*slBufferQueue)->Enqueue(slBufferQueue, nativeBuffer, nativeBufferSize*sizeof(int16_t));
}
void AudioInputOpenSLES::Start(){
SLresult result=(*slRecorder)->SetRecordState(slRecorder, SL_RECORDSTATE_RECORDING);
CHECK_SL_ERROR(result, "Error starting record");
}
void AudioInputOpenSLES::Stop(){
SLresult result=(*slRecorder)->SetRecordState(slRecorder, SL_RECORDSTATE_STOPPED);
CHECK_SL_ERROR(result, "Error stopping record");
}
void AudioInputOpenSLES::HandleSLCallback(){
//SLmillisecond pMsec = 0;
//(*slRecorder)->GetPosition(slRecorder, &pMsec);
//LOGI("Callback! pos=%lu", pMsec);
//InvokeCallback((unsigned char*)buffer, BUFFER_SIZE*sizeof(int16_t));
//fwrite(nativeBuffer, 1, nativeBufferSize*2, test);
if(nativeBufferSize==BUFFER_SIZE){
//LOGV("nativeBufferSize==BUFFER_SIZE");
InvokeCallback((unsigned char *) nativeBuffer, BUFFER_SIZE*sizeof(int16_t));
}else if(nativeBufferSize<BUFFER_SIZE){
//LOGV("nativeBufferSize<BUFFER_SIZE");
if(positionInBuffer>=BUFFER_SIZE){
InvokeCallback((unsigned char *) buffer, BUFFER_SIZE*sizeof(int16_t));
positionInBuffer=0;
}
memcpy(((unsigned char*)buffer)+positionInBuffer*2, nativeBuffer, (size_t)nativeBufferSize*2);
positionInBuffer+=nativeBufferSize;
}else if(nativeBufferSize>BUFFER_SIZE){
//LOGV("nativeBufferSize>BUFFER_SIZE");
for(unsigned int offset=0;offset<nativeBufferSize;offset+=BUFFER_SIZE){
InvokeCallback(((unsigned char *) nativeBuffer)+offset*2, BUFFER_SIZE*sizeof(int16_t));
}
}
(*slBufferQueue)->Enqueue(slBufferQueue, nativeBuffer, nativeBufferSize*sizeof(int16_t));
}

View File

@ -0,0 +1,40 @@
//
// 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 LIBTGVOIP_AUDIOINPUTOPENSLES_H
#define LIBTGVOIP_AUDIOINPUTOPENSLES_H
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include "../../audio/AudioInput.h"
namespace tgvoip{ namespace audio{
class AudioInputOpenSLES : public AudioInput{
public:
AudioInputOpenSLES();
virtual ~AudioInputOpenSLES();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
virtual void Start();
virtual void Stop();
static int nativeBufferSize;
private:
static void BufferCallback(SLAndroidSimpleBufferQueueItf bq, void *context);
void HandleSLCallback();
SLEngineItf slEngine;
SLObjectItf slRecorderObj;
SLRecordItf slRecorder;
SLAndroidSimpleBufferQueueItf slBufferQueue;
int16_t* buffer;
int16_t* nativeBuffer;
size_t positionInBuffer;
};
}}
#endif //LIBTGVOIP_AUDIOINPUTOPENSLES_H

View File

@ -0,0 +1,125 @@
//
// 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 "AudioOutputAndroid.h"
#include <stdio.h>
#include "../../logging.h"
extern JavaVM* sharedJVM;
using namespace tgvoip;
using namespace tgvoip::audio;
jmethodID AudioOutputAndroid::initMethod=NULL;
jmethodID AudioOutputAndroid::releaseMethod=NULL;
jmethodID AudioOutputAndroid::startMethod=NULL;
jmethodID AudioOutputAndroid::stopMethod=NULL;
jclass AudioOutputAndroid::jniClass=NULL;
AudioOutputAndroid::AudioOutputAndroid(){
JNIEnv* env=NULL;
bool didAttach=false;
sharedJVM->GetEnv((void**) &env, JNI_VERSION_1_6);
if(!env){
sharedJVM->AttachCurrentThread(&env, NULL);
didAttach=true;
}
jmethodID ctor=env->GetMethodID(jniClass, "<init>", "(J)V");
jobject obj=env->NewObject(jniClass, ctor, (jlong)(intptr_t)this);
javaObject=env->NewGlobalRef(obj);
if(didAttach){
sharedJVM->DetachCurrentThread();
}
running=false;
}
AudioOutputAndroid::~AudioOutputAndroid(){
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;
if(didAttach){
sharedJVM->DetachCurrentThread();
}
}
void AudioOutputAndroid::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
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, initMethod, sampleRate, bitsPerSample, channels, 960*2);
if(didAttach){
sharedJVM->DetachCurrentThread();
}
}
void AudioOutputAndroid::Start(){
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, startMethod);
if(didAttach){
sharedJVM->DetachCurrentThread();
}
running=true;
}
void AudioOutputAndroid::Stop(){
running=false;
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, stopMethod);
if(didAttach){
sharedJVM->DetachCurrentThread();
}
}
void AudioOutputAndroid::HandleCallback(JNIEnv* env, jbyteArray buffer){
if(!running)
return;
unsigned char* buf=(unsigned char*) env->GetByteArrayElements(buffer, NULL);
size_t len=(size_t) env->GetArrayLength(buffer);
InvokeCallback(buf, len);
env->ReleaseByteArrayElements(buffer, (jbyte *) buf, 0);
}
bool AudioOutputAndroid::IsPlaying(){
return false;
}
float AudioOutputAndroid::GetLevel(){
return 0;
}

View File

@ -0,0 +1,39 @@
//
// 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 LIBTGVOIP_AUDIOOUTPUTANDROID_H
#define LIBTGVOIP_AUDIOOUTPUTANDROID_H
#include <jni.h>
#include "../../audio/AudioOutput.h"
namespace tgvoip{ namespace audio{
class AudioOutputAndroid : public AudioOutput{
public:
AudioOutputAndroid();
virtual ~AudioOutputAndroid();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
virtual void Start();
virtual void Stop();
virtual bool IsPlaying() override;
virtual float GetLevel() override;
void HandleCallback(JNIEnv* env, jbyteArray buffer);
static jmethodID initMethod;
static jmethodID releaseMethod;
static jmethodID startMethod;
static jmethodID stopMethod;
static jclass jniClass;
private:
jobject javaObject;
bool running;
};
}}
#endif //LIBTGVOIP_AUDIOOUTPUTANDROID_H

View File

@ -0,0 +1,171 @@
//
// 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 <sys/time.h>
#include <unistd.h>
#include <assert.h>
#include "AudioOutputOpenSLES.h"
#include "../../logging.h"
#include "../../VoIPController.h"
#include "OpenSLEngineWrapper.h"
#include "AudioInputAndroid.h"
#define CHECK_SL_ERROR(res, msg) if(res!=SL_RESULT_SUCCESS){ LOGE(msg); return; }
#define BUFFER_SIZE 960 // 20 ms
using namespace tgvoip;
using namespace tgvoip::audio;
int AudioOutputOpenSLES::nativeBufferSize;
AudioOutputOpenSLES::AudioOutputOpenSLES(){
SLresult result;
slEngine=OpenSLEngineWrapper::CreateEngine();
const SLInterfaceID pOutputMixIDs[] = {};
const SLboolean pOutputMixRequired[] = {};
result = (*slEngine)->CreateOutputMix(slEngine, &slOutputMixObj, 0, pOutputMixIDs, pOutputMixRequired);
CHECK_SL_ERROR(result, "Error creating output mix");
result = (*slOutputMixObj)->Realize(slOutputMixObj, SL_BOOLEAN_FALSE);
CHECK_SL_ERROR(result, "Error realizing output mix");
LOGI("Native buffer size is %u samples", nativeBufferSize);
/*if(nativeBufferSize<BUFFER_SIZE && BUFFER_SIZE % nativeBufferSize!=0){
LOGE("20ms is not divisible by native buffer size!!");
nativeBufferSize=BUFFER_SIZE;
}else if(nativeBufferSize>BUFFER_SIZE && nativeBufferSize%BUFFER_SIZE!=0){
LOGE("native buffer size is not multiple of 20ms!!");
nativeBufferSize+=nativeBufferSize%BUFFER_SIZE;
}
LOGI("Adjusted native buffer size is %u", nativeBufferSize);*/
buffer=(int16_t*)calloc(BUFFER_SIZE, sizeof(int16_t));
nativeBuffer=(int16_t*)calloc((size_t) nativeBufferSize, sizeof(int16_t));
slPlayerObj=NULL;
remainingDataSize=0;
}
AudioOutputOpenSLES::~AudioOutputOpenSLES(){
if(!stopped)
Stop();
(*slBufferQueue)->Clear(slBufferQueue);
LOGV("destroy slPlayerObj");
(*slPlayerObj)->Destroy(slPlayerObj);
LOGV("destroy slOutputMixObj");
(*slOutputMixObj)->Destroy(slOutputMixObj);
OpenSLEngineWrapper::DestroyEngine();
free(buffer);
free(nativeBuffer);
}
void AudioOutputOpenSLES::SetNativeBufferSize(int size){
AudioOutputOpenSLES::nativeBufferSize=size;
}
void AudioOutputOpenSLES::BufferCallback(SLAndroidSimpleBufferQueueItf bq, void *context){
((AudioOutputOpenSLES*)context)->HandleSLCallback();
}
void AudioOutputOpenSLES::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
assert(slPlayerObj==NULL);
SLDataLocator_AndroidSimpleBufferQueue locatorBufferQueue =
{SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1};
SLDataFormat_PCM formatPCM = {SL_DATAFORMAT_PCM, channels, sampleRate*1000,
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
channels==2 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER, SL_BYTEORDER_LITTLEENDIAN};
SLDataSource audioSrc = {&locatorBufferQueue, &formatPCM};
SLDataLocator_OutputMix locatorOutMix = {SL_DATALOCATOR_OUTPUTMIX, slOutputMixObj};
SLDataSink audioSnk = {&locatorOutMix, NULL};
const SLInterfaceID id[2] = {SL_IID_BUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION};
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
SLresult result = (*slEngine)->CreateAudioPlayer(slEngine, &slPlayerObj, &audioSrc, &audioSnk, 2, id, req);
CHECK_SL_ERROR(result, "Error creating player");
SLAndroidConfigurationItf playerConfig;
result = (*slPlayerObj)->GetInterface(slPlayerObj, SL_IID_ANDROIDCONFIGURATION, &playerConfig);
SLint32 streamType = SL_ANDROID_STREAM_VOICE;
result = (*playerConfig)->SetConfiguration(playerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
result=(*slPlayerObj)->Realize(slPlayerObj, SL_BOOLEAN_FALSE);
CHECK_SL_ERROR(result, "Error realizing player");
result=(*slPlayerObj)->GetInterface(slPlayerObj, SL_IID_PLAY, &slPlayer);
CHECK_SL_ERROR(result, "Error getting player interface");
result=(*slPlayerObj)->GetInterface(slPlayerObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &slBufferQueue);
CHECK_SL_ERROR(result, "Error getting buffer queue");
result=(*slBufferQueue)->RegisterCallback(slBufferQueue, AudioOutputOpenSLES::BufferCallback, this);
CHECK_SL_ERROR(result, "Error setting buffer queue callback");
(*slBufferQueue)->Enqueue(slBufferQueue, nativeBuffer, nativeBufferSize*sizeof(int16_t));
}
bool AudioOutputOpenSLES::IsPhone(){
return false;
}
void AudioOutputOpenSLES::EnableLoudspeaker(bool enabled){
}
void AudioOutputOpenSLES::Start(){
stopped=false;
SLresult result=(*slPlayer)->SetPlayState(slPlayer, SL_PLAYSTATE_PLAYING);
CHECK_SL_ERROR(result, "Error starting player");
}
void AudioOutputOpenSLES::Stop(){
stopped=true;
LOGV("Stopping OpenSL output");
SLresult result=(*slPlayer)->SetPlayState(slPlayer, SL_PLAYSTATE_PAUSED);
CHECK_SL_ERROR(result, "Error starting player");
}
void AudioOutputOpenSLES::HandleSLCallback(){
/*if(stopped){
//LOGV("left HandleSLCallback early");
return;
}*/
//LOGV("before InvokeCallback");
if(!stopped){
while(remainingDataSize<nativeBufferSize*2){
assert(remainingDataSize+BUFFER_SIZE*2<10240);
InvokeCallback(remainingData+remainingDataSize, BUFFER_SIZE*2);
remainingDataSize+=BUFFER_SIZE*2;
}
memcpy(nativeBuffer, remainingData, nativeBufferSize*2);
remainingDataSize-=nativeBufferSize*2;
if(remainingDataSize>0)
memmove(remainingData, remainingData+nativeBufferSize*2, remainingDataSize);
//InvokeCallback((unsigned char *) nativeBuffer, nativeBufferSize*sizeof(int16_t));
}else{
memset(nativeBuffer, 0, nativeBufferSize*2);
}
(*slBufferQueue)->Enqueue(slBufferQueue, nativeBuffer, nativeBufferSize*sizeof(int16_t));
//LOGV("left HandleSLCallback");
}
bool AudioOutputOpenSLES::IsPlaying(){
if(slPlayer){
uint32_t state;
(*slPlayer)->GetPlayState(slPlayer, &state);
return state==SL_PLAYSTATE_PLAYING;
}
return false;
}
float AudioOutputOpenSLES::GetLevel(){
return 0; // we don't use this anyway
}

View File

@ -0,0 +1,47 @@
//
// 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 LIBTGVOIP_AUDIOOUTPUTOPENSLES_H
#define LIBTGVOIP_AUDIOOUTPUTOPENSLES_H
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include "../../audio/AudioOutput.h"
namespace tgvoip{ namespace audio{
class AudioOutputOpenSLES : public AudioOutput{
public:
AudioOutputOpenSLES();
virtual ~AudioOutputOpenSLES();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
virtual bool IsPhone();
virtual void EnableLoudspeaker(bool enabled);
virtual void Start();
virtual void Stop();
virtual bool IsPlaying();
virtual float GetLevel();
static void SetNativeBufferSize(int size);
static int nativeBufferSize;
private:
static void BufferCallback(SLAndroidSimpleBufferQueueItf bq, void *context);
void HandleSLCallback();
SLEngineItf slEngine;
SLObjectItf slPlayerObj;
SLObjectItf slOutputMixObj;
SLPlayItf slPlayer;
SLAndroidSimpleBufferQueueItf slBufferQueue;
int16_t* buffer;
int16_t* nativeBuffer;
bool stopped;
unsigned char remainingData[10240];
size_t remainingDataSize;
};
}}
#endif //LIBTGVOIP_AUDIOOUTPUTANDROID_H

View File

@ -0,0 +1,48 @@
//
// 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 <cstddef>
#include "OpenSLEngineWrapper.h"
#include "../../logging.h"
#define CHECK_SL_ERROR(res, msg) if(res!=SL_RESULT_SUCCESS){ LOGE(msg); return NULL; }
using namespace tgvoip;
using namespace tgvoip::audio;
SLObjectItf OpenSLEngineWrapper::sharedEngineObj=NULL;
SLEngineItf OpenSLEngineWrapper::sharedEngine=NULL;
int OpenSLEngineWrapper::count=0;
void OpenSLEngineWrapper::DestroyEngine(){
count--;
LOGI("release: engine instance count %d", count);
if(count==0){
(*sharedEngineObj)->Destroy(sharedEngineObj);
sharedEngineObj=NULL;
sharedEngine=NULL;
}
LOGI("after release");
}
SLEngineItf OpenSLEngineWrapper::CreateEngine(){
count++;
if(sharedEngine)
return sharedEngine;
const SLInterfaceID pIDs[1] = {SL_IID_ENGINE};
const SLboolean pIDsRequired[1] = {SL_BOOLEAN_TRUE};
SLresult result = slCreateEngine(&sharedEngineObj, 0, NULL, 1, pIDs, pIDsRequired);
CHECK_SL_ERROR(result, "Error creating engine");
result=(*sharedEngineObj)->Realize(sharedEngineObj, SL_BOOLEAN_FALSE);
CHECK_SL_ERROR(result, "Error realizing engine");
result = (*sharedEngineObj)->GetInterface(sharedEngineObj, SL_IID_ENGINE, &sharedEngine);
CHECK_SL_ERROR(result, "Error getting engine interface");
return sharedEngine;
}

View File

@ -0,0 +1,26 @@
//
// 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 LIBTGVOIP_OPENSLENGINEWRAPPER_H
#define LIBTGVOIP_OPENSLENGINEWRAPPER_H
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
namespace tgvoip{ namespace audio{
class OpenSLEngineWrapper{
public:
static SLEngineItf CreateEngine();
static void DestroyEngine();
private:
static SLObjectItf sharedEngineObj;
static SLEngineItf sharedEngine;
static int count;
};
}}
#endif //LIBTGVOIP_OPENSLENGINEWRAPPER_H

View File

@ -0,0 +1,82 @@
//
// 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 <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "AudioUnitIO.h"
#include "AudioInputAudioUnit.h"
#include "../../logging.h"
#define BUFFER_SIZE 960
using namespace tgvoip;
using namespace tgvoip::audio;
AudioInputAudioUnit::AudioInputAudioUnit(std::string deviceID){
remainingDataSize=0;
isRecording=false;
this->io=AudioUnitIO::Get();
#if TARGET_OS_OSX
io->SetCurrentDevice(true, deviceID);
#endif
io->AttachInput(this);
failed=io->IsFailed();
}
AudioInputAudioUnit::~AudioInputAudioUnit(){
io->DetachInput();
AudioUnitIO::Release();
}
void AudioInputAudioUnit::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
io->Configure(sampleRate, bitsPerSample, channels);
}
void AudioInputAudioUnit::Start(){
isRecording=true;
io->EnableInput(true);
failed=io->IsFailed();
}
void AudioInputAudioUnit::Stop(){
isRecording=false;
io->EnableInput(false);
}
void AudioInputAudioUnit::HandleBufferCallback(AudioBufferList *ioData){
int i;
int j;
for(i=0;i<ioData->mNumberBuffers;i++){
AudioBuffer buf=ioData->mBuffers[i];
#if TARGET_OS_OSX
assert(remainingDataSize+buf.mDataByteSize/2<10240);
float* src=reinterpret_cast<float*>(buf.mData);
int16_t* dst=reinterpret_cast<int16_t*>(remainingData+remainingDataSize);
for(j=0;j<buf.mDataByteSize/4;j++){
dst[j]=(int16_t)(src[j]*INT16_MAX);
}
remainingDataSize+=buf.mDataByteSize/2;
#else
assert(remainingDataSize+buf.mDataByteSize<10240);
memcpy(remainingData+remainingDataSize, buf.mData, buf.mDataByteSize);
remainingDataSize+=buf.mDataByteSize;
#endif
while(remainingDataSize>=BUFFER_SIZE*2){
InvokeCallback((unsigned char*)remainingData, BUFFER_SIZE*2);
remainingDataSize-=BUFFER_SIZE*2;
if(remainingDataSize>0){
memmove(remainingData, remainingData+(BUFFER_SIZE*2), remainingDataSize);
}
}
}
}
#if TARGET_OS_OSX
void AudioInputAudioUnit::SetCurrentDevice(std::string deviceID){
io->SetCurrentDevice(true, deviceID);
}
#endif

View File

@ -0,0 +1,37 @@
//
// 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 LIBTGVOIP_AUDIOINPUTAUDIOUNIT_H
#define LIBTGVOIP_AUDIOINPUTAUDIOUNIT_H
#include <AudioUnit/AudioUnit.h>
#include "../../audio/AudioInput.h"
namespace tgvoip{ namespace audio{
class AudioUnitIO;
class AudioInputAudioUnit : public AudioInput{
public:
AudioInputAudioUnit(std::string deviceID);
virtual ~AudioInputAudioUnit();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
virtual void Start();
virtual void Stop();
void HandleBufferCallback(AudioBufferList* ioData);
#if TARGET_OS_OSX
virtual void SetCurrentDevice(std::string deviceID);
#endif
private:
unsigned char remainingData[10240];
size_t remainingDataSize;
bool isRecording;
AudioUnitIO* io;
};
}}
#endif //LIBTGVOIP_AUDIOINPUTAUDIOUNIT_H

View File

@ -0,0 +1,309 @@
//
// 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 <stdlib.h>
#include <stdio.h>
#include "AudioInputAudioUnitOSX.h"
#include "../../logging.h"
#include "../../audio/Resampler.h"
#include "../../VoIPController.h"
#define BUFFER_SIZE 960
#define CHECK_AU_ERROR(res, msg) if(res!=noErr){ LOGE("input: " msg": OSStatus=%d", (int)res); failed=true; return; }
#define kOutputBus 0
#define kInputBus 1
using namespace tgvoip;
using namespace tgvoip::audio;
AudioInputAudioUnitLegacy::AudioInputAudioUnitLegacy(std::string deviceID) : AudioInput(deviceID){
remainingDataSize=0;
isRecording=false;
OSStatus status;
AudioComponentDescription inputDesc={
.componentType = kAudioUnitType_Output, .componentSubType = /*kAudioUnitSubType_HALOutput*/kAudioUnitSubType_VoiceProcessingIO, .componentFlags = 0, .componentFlagsMask = 0,
.componentManufacturer = kAudioUnitManufacturer_Apple
};
AudioComponent component=AudioComponentFindNext(NULL, &inputDesc);
status=AudioComponentInstanceNew(component, &unit);
CHECK_AU_ERROR(status, "Error creating AudioUnit");
UInt32 flag=0;
status = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag));
CHECK_AU_ERROR(status, "Error enabling AudioUnit output");
flag=1;
status = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag));
CHECK_AU_ERROR(status, "Error enabling AudioUnit input");
SetCurrentDevice(deviceID);
CFRunLoopRef theRunLoop = NULL;
AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyRunLoop,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster };
status = AudioObjectSetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioInputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = AudioInputAudioUnitLegacy::BufferCallback;
callbackStruct.inputProcRefCon=this;
status = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, kInputBus, &callbackStruct, sizeof(callbackStruct));
CHECK_AU_ERROR(status, "Error setting input buffer callback");
status=AudioUnitInitialize(unit);
CHECK_AU_ERROR(status, "Error initializing unit");
inBufferList.mBuffers[0].mData=malloc(10240);
inBufferList.mBuffers[0].mDataByteSize=10240;
inBufferList.mNumberBuffers=1;
}
AudioInputAudioUnitLegacy::~AudioInputAudioUnitLegacy(){
AudioObjectPropertyAddress propertyAddress;
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioInputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
AudioUnitUninitialize(unit);
AudioComponentInstanceDispose(unit);
free(inBufferList.mBuffers[0].mData);
}
void AudioInputAudioUnitLegacy::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
}
void AudioInputAudioUnitLegacy::Start(){
isRecording=true;
OSStatus status=AudioOutputUnitStart(unit);
CHECK_AU_ERROR(status, "Error starting AudioUnit");
}
void AudioInputAudioUnitLegacy::Stop(){
isRecording=false;
OSStatus status=AudioOutputUnitStart(unit);
CHECK_AU_ERROR(status, "Error stopping AudioUnit");
}
OSStatus AudioInputAudioUnitLegacy::BufferCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData){
AudioInputAudioUnitLegacy* input=(AudioInputAudioUnitLegacy*) inRefCon;
input->inBufferList.mBuffers[0].mDataByteSize=10240;
OSStatus res=AudioUnitRender(input->unit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &input->inBufferList);
input->HandleBufferCallback(&input->inBufferList);
return noErr;
}
void AudioInputAudioUnitLegacy::HandleBufferCallback(AudioBufferList *ioData){
int i;
for(i=0;i<ioData->mNumberBuffers;i++){
AudioBuffer buf=ioData->mBuffers[i];
size_t len=buf.mDataByteSize;
if(hardwareSampleRate!=48000){
len=tgvoip::audio::Resampler::Convert((int16_t*)buf.mData, (int16_t*)(remainingData+remainingDataSize), buf.mDataByteSize/2, (10240-(buf.mDataByteSize+remainingDataSize))/2, 48000, hardwareSampleRate)*2;
}else{
assert(remainingDataSize+buf.mDataByteSize<10240);
memcpy(remainingData+remainingDataSize, buf.mData, buf.mDataByteSize);
}
remainingDataSize+=len;
while(remainingDataSize>=BUFFER_SIZE*2){
InvokeCallback((unsigned char*)remainingData, BUFFER_SIZE*2);
remainingDataSize-=BUFFER_SIZE*2;
if(remainingDataSize>0){
memmove(remainingData, remainingData+(BUFFER_SIZE*2), remainingDataSize);
}
}
}
}
void AudioInputAudioUnitLegacy::EnumerateDevices(std::vector<AudioInputDevice>& devs){
AudioObjectPropertyAddress propertyAddress = {
kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
UInt32 dataSize = 0;
OSStatus status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize);
if(kAudioHardwareNoError != status) {
LOGE("AudioObjectGetPropertyDataSize (kAudioHardwarePropertyDevices) failed: %i", status);
return;
}
UInt32 deviceCount = (UInt32)(dataSize / sizeof(AudioDeviceID));
AudioDeviceID *audioDevices = (AudioDeviceID*)(malloc(dataSize));
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize, audioDevices);
if(kAudioHardwareNoError != status) {
LOGE("AudioObjectGetPropertyData (kAudioHardwarePropertyDevices) failed: %i", status);
free(audioDevices);
audioDevices = NULL;
return;
}
// Iterate through all the devices and determine which are input-capable
propertyAddress.mScope = kAudioDevicePropertyScopeInput;
for(UInt32 i = 0; i < deviceCount; ++i) {
// Query device UID
CFStringRef deviceUID = NULL;
dataSize = sizeof(deviceUID);
propertyAddress.mSelector = kAudioDevicePropertyDeviceUID;
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceUID);
if(kAudioHardwareNoError != status) {
LOGE("AudioObjectGetPropertyData (kAudioDevicePropertyDeviceUID) failed: %i", status);
continue;
}
// Query device name
CFStringRef deviceName = NULL;
dataSize = sizeof(deviceName);
propertyAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceName);
if(kAudioHardwareNoError != status) {
LOGE("AudioObjectGetPropertyData (kAudioDevicePropertyDeviceNameCFString) failed: %i", status);
continue;
}
// Determine if the device is an input device (it is an input device if it has input channels)
dataSize = 0;
propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
status = AudioObjectGetPropertyDataSize(audioDevices[i], &propertyAddress, 0, NULL, &dataSize);
if(kAudioHardwareNoError != status) {
LOGE("AudioObjectGetPropertyDataSize (kAudioDevicePropertyStreamConfiguration) failed: %i", status);
continue;
}
AudioBufferList *bufferList = (AudioBufferList*)(malloc(dataSize));
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, bufferList);
if(kAudioHardwareNoError != status || 0 == bufferList->mNumberBuffers) {
if(kAudioHardwareNoError != status)
LOGE("AudioObjectGetPropertyData (kAudioDevicePropertyStreamConfiguration) failed: %i", status);
free(bufferList);
bufferList = NULL;
continue;
}
free(bufferList);
bufferList = NULL;
AudioInputDevice dev;
char buf[1024];
CFStringGetCString(deviceName, buf, 1024, kCFStringEncodingUTF8);
dev.displayName=std::string(buf);
CFStringGetCString(deviceUID, buf, 1024, kCFStringEncodingUTF8);
dev.id=std::string(buf);
devs.push_back(dev);
}
free(audioDevices);
audioDevices = NULL;
}
void AudioInputAudioUnitLegacy::SetCurrentDevice(std::string deviceID){
UInt32 size=sizeof(AudioDeviceID);
AudioDeviceID inputDevice=NULL;
OSStatus status;
if(deviceID=="default"){
AudioObjectPropertyAddress propertyAddress;
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
UInt32 propsize = sizeof(AudioDeviceID);
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propsize, &inputDevice);
CHECK_AU_ERROR(status, "Error getting default input device");
}else{
AudioObjectPropertyAddress propertyAddress = {
kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
UInt32 dataSize = 0;
status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize);
CHECK_AU_ERROR(status, "Error getting devices size");
UInt32 deviceCount = (UInt32)(dataSize / sizeof(AudioDeviceID));
AudioDeviceID audioDevices[deviceCount];
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize, audioDevices);
CHECK_AU_ERROR(status, "Error getting device list");
for(UInt32 i = 0; i < deviceCount; ++i) {
// Query device UID
CFStringRef deviceUID = NULL;
dataSize = sizeof(deviceUID);
propertyAddress.mSelector = kAudioDevicePropertyDeviceUID;
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceUID);
CHECK_AU_ERROR(status, "Error getting device uid");
char buf[1024];
CFStringGetCString(deviceUID, buf, 1024, kCFStringEncodingUTF8);
if(deviceID==buf){
LOGV("Found device for id %s", buf);
inputDevice=audioDevices[i];
break;
}
}
if(!inputDevice){
LOGW("Requested device not found, using default");
SetCurrentDevice("default");
return;
}
}
status =AudioUnitSetProperty(unit,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
kInputBus,
&inputDevice,
size);
CHECK_AU_ERROR(status, "Error setting input device");
AudioStreamBasicDescription hardwareFormat;
size=sizeof(hardwareFormat);
status=AudioUnitGetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kInputBus, &hardwareFormat, &size);
CHECK_AU_ERROR(status, "Error getting hardware format");
hardwareSampleRate=hardwareFormat.mSampleRate;
AudioStreamBasicDescription desiredFormat={
.mSampleRate=hardwareFormat.mSampleRate, .mFormatID=kAudioFormatLinearPCM, .mFormatFlags=kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian,
.mFramesPerPacket=1, .mChannelsPerFrame=1, .mBitsPerChannel=16, .mBytesPerPacket=2, .mBytesPerFrame=2
};
status=AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &desiredFormat, sizeof(desiredFormat));
CHECK_AU_ERROR(status, "Error setting format");
LOGD("Switched capture device, new sample rate %d", hardwareSampleRate);
this->currentDevice=deviceID;
AudioObjectPropertyAddress propertyAddress = {
kAudioDevicePropertyBufferFrameSize,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
size=4;
UInt32 bufferFrameSize;
status=AudioObjectGetPropertyData(inputDevice, &propertyAddress, 0, NULL, &size, &bufferFrameSize);
if(status==noErr){
estimatedDelay=bufferFrameSize/48;
LOGD("CoreAudio buffer size for output device is %u frames (%u ms)", bufferFrameSize, estimatedDelay);
}
}
OSStatus AudioInputAudioUnitLegacy::DefaultDeviceChangedCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData){
LOGV("System default input device changed");
AudioInputAudioUnitLegacy* self=(AudioInputAudioUnitLegacy*)inClientData;
if(self->currentDevice=="default"){
self->SetCurrentDevice(self->currentDevice);
}
return noErr;
}

View File

@ -0,0 +1,40 @@
//
// 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 LIBTGVOIP_AUDIOINPUTAUDIOUNIT_OSX_H
#define LIBTGVOIP_AUDIOINPUTAUDIOUNIT_OSX_H
#include <AudioUnit/AudioUnit.h>
#import <AudioToolbox/AudioToolbox.h>
#import <CoreAudio/CoreAudio.h>
#include "../../audio/AudioInput.h"
namespace tgvoip{ namespace audio{
class AudioInputAudioUnitLegacy : public AudioInput{
public:
AudioInputAudioUnitLegacy(std::string deviceID);
virtual ~AudioInputAudioUnitLegacy();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
virtual void Start();
virtual void Stop();
void HandleBufferCallback(AudioBufferList* ioData);
static void EnumerateDevices(std::vector<AudioInputDevice>& devs);
virtual void SetCurrentDevice(std::string deviceID);
private:
static OSStatus BufferCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData);
static OSStatus DefaultDeviceChangedCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData);
unsigned char remainingData[10240];
size_t remainingDataSize;
bool isRecording;
AudioUnit unit;
AudioBufferList inBufferList;
int hardwareSampleRate;
};
}}
#endif //LIBTGVOIP_AUDIOINPUTAUDIOUNIT_OSX_H

View File

@ -0,0 +1,125 @@
//
// 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 <sys/time.h>
#include <unistd.h>
#include <assert.h>
#include "AudioOutputAudioUnit.h"
#include "../../logging.h"
#include "AudioUnitIO.h"
#define BUFFER_SIZE 960
const int8_t permutation[33]={0,1,2,3,4,4,5,5,5,5,6,6,6,6,6,7,7,7,7,8,8,8,9,9,9,9,9,9,9,9,9,9,9};
using namespace tgvoip;
using namespace tgvoip::audio;
AudioOutputAudioUnit::AudioOutputAudioUnit(std::string deviceID){
isPlaying=false;
remainingDataSize=0;
level=0.0;
this->io=AudioUnitIO::Get();
#if TARGET_OS_OSX
io->SetCurrentDevice(false, deviceID);
#endif
io->AttachOutput(this);
failed=io->IsFailed();
}
AudioOutputAudioUnit::~AudioOutputAudioUnit(){
io->DetachOutput();
AudioUnitIO::Release();
}
void AudioOutputAudioUnit::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
io->Configure(sampleRate, bitsPerSample, channels);
}
bool AudioOutputAudioUnit::IsPhone(){
return false;
}
void AudioOutputAudioUnit::EnableLoudspeaker(bool enabled){
}
void AudioOutputAudioUnit::Start(){
isPlaying=true;
io->EnableOutput(true);
failed=io->IsFailed();
}
void AudioOutputAudioUnit::Stop(){
isPlaying=false;
io->EnableOutput(false);
}
bool AudioOutputAudioUnit::IsPlaying(){
return isPlaying;
}
float AudioOutputAudioUnit::GetLevel(){
return level / 9.0;
}
void AudioOutputAudioUnit::HandleBufferCallback(AudioBufferList *ioData){
int i;
unsigned int k;
int16_t absVal=0;
for(i=0;i<ioData->mNumberBuffers;i++){
AudioBuffer buf=ioData->mBuffers[i];
if(!isPlaying){
memset(buf.mData, 0, buf.mDataByteSize);
return;
}
while(remainingDataSize<buf.mDataByteSize){
assert(remainingDataSize+BUFFER_SIZE*2<10240);
InvokeCallback(remainingData+remainingDataSize, BUFFER_SIZE*2);
remainingDataSize+=BUFFER_SIZE*2;
}
#if TARGET_OS_OSX
float* dst=reinterpret_cast<float*>(buf.mData);
int16_t* src=reinterpret_cast<int16_t*>(remainingData);
for(k=0;k<buf.mDataByteSize/4;k++){
dst[k]=src[k]/(float)INT16_MAX;
}
remainingDataSize-=buf.mDataByteSize/2;
memmove(remainingData, remainingData+buf.mDataByteSize/2, remainingDataSize);
#else
memcpy(buf.mData, remainingData, buf.mDataByteSize);
remainingDataSize-=buf.mDataByteSize;
memmove(remainingData, remainingData+buf.mDataByteSize, remainingDataSize);
#endif
/*unsigned int samples=buf.mDataByteSize/sizeof(int16_t);
for (k=0;k<samples;k++){
int16_t absolute=(int16_t)abs(*((int16_t *)buf.mData+k));
if (absolute>absVal)
absVal=absolute;
}
if (absVal>absMax)
absMax=absVal;
count++;
if (count>=10) {
count=0;
short position=absMax/1000;
if (position==0 && absMax>250) {
position=1;
}
level=permutation[position];
absMax>>=2;
}*/
}
}
#if TARGET_OS_OSX
void AudioOutputAudioUnit::SetCurrentDevice(std::string deviceID){
io->SetCurrentDevice(false, deviceID);
}
#endif

View File

@ -0,0 +1,43 @@
//
// 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 LIBTGVOIP_AUDIOOUTPUTAUDIOUNIT_H
#define LIBTGVOIP_AUDIOOUTPUTAUDIOUNIT_H
#include <AudioUnit/AudioUnit.h>
#include "../../audio/AudioOutput.h"
namespace tgvoip{ namespace audio{
class AudioUnitIO;
class AudioOutputAudioUnit : public AudioOutput{
public:
AudioOutputAudioUnit(std::string deviceID);
virtual ~AudioOutputAudioUnit();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
virtual bool IsPhone();
virtual void EnableLoudspeaker(bool enabled);
virtual void Start();
virtual void Stop();
virtual bool IsPlaying();
virtual float GetLevel();
void HandleBufferCallback(AudioBufferList* ioData);
#if TARGET_OS_OSX
virtual void SetCurrentDevice(std::string deviceID);
#endif
private:
bool isPlaying;
unsigned char remainingData[10240];
size_t remainingDataSize;
AudioUnitIO* io;
float level;
int16_t absMax;
int count;
};
}}
#endif //LIBTGVOIP_AUDIOOUTPUTAUDIOUNIT_H

View File

@ -0,0 +1,368 @@
//
// 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 <stdlib.h>
#include <stdio.h>
#include <sys/sysctl.h>
#include "AudioOutputAudioUnitOSX.h"
#include "../../logging.h"
#include "../../VoIPController.h"
#define BUFFER_SIZE 960
#define CHECK_AU_ERROR(res, msg) if(res!=noErr){ LOGE("output: " msg": OSStatus=%d", (int)res); return; }
#define kOutputBus 0
#define kInputBus 1
using namespace tgvoip;
using namespace tgvoip::audio;
AudioOutputAudioUnitLegacy::AudioOutputAudioUnitLegacy(std::string deviceID){
remainingDataSize=0;
isPlaying=false;
sysDevID=NULL;
OSStatus status;
AudioComponentDescription inputDesc={
.componentType = kAudioUnitType_Output, .componentSubType = kAudioUnitSubType_HALOutput, .componentFlags = 0, .componentFlagsMask = 0,
.componentManufacturer = kAudioUnitManufacturer_Apple
};
AudioComponent component=AudioComponentFindNext(NULL, &inputDesc);
status=AudioComponentInstanceNew(component, &unit);
CHECK_AU_ERROR(status, "Error creating AudioUnit");
UInt32 flag=1;
status = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag));
CHECK_AU_ERROR(status, "Error enabling AudioUnit output");
flag=0;
status = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag));
CHECK_AU_ERROR(status, "Error enabling AudioUnit input");
char model[128];
memset(model, 0, sizeof(model));
size_t msize=sizeof(model);
int mres=sysctlbyname("hw.model", model, &msize, NULL, 0);
if(mres==0){
LOGV("Mac model: %s", model);
isMacBookPro=(strncmp("MacBookPro", model, 10)==0);
}
SetCurrentDevice(deviceID);
CFRunLoopRef theRunLoop = NULL;
AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyRunLoop,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster };
status = AudioObjectSetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioOutputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
AudioStreamBasicDescription desiredFormat={
.mSampleRate=/*hardwareFormat.mSampleRate*/48000, .mFormatID=kAudioFormatLinearPCM, .mFormatFlags=kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian,
.mFramesPerPacket=1, .mChannelsPerFrame=1, .mBitsPerChannel=16, .mBytesPerPacket=2, .mBytesPerFrame=2
};
status=AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &desiredFormat, sizeof(desiredFormat));
CHECK_AU_ERROR(status, "Error setting format");
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = AudioOutputAudioUnitLegacy::BufferCallback;
callbackStruct.inputProcRefCon=this;
status = AudioUnitSetProperty(unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, kOutputBus, &callbackStruct, sizeof(callbackStruct));
CHECK_AU_ERROR(status, "Error setting input buffer callback");
status=AudioUnitInitialize(unit);
CHECK_AU_ERROR(status, "Error initializing unit");
}
AudioOutputAudioUnitLegacy::~AudioOutputAudioUnitLegacy(){
AudioObjectPropertyAddress propertyAddress;
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioOutputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
AudioObjectPropertyAddress dataSourceProp={
kAudioDevicePropertyDataSource,
kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMaster
};
if(isMacBookPro && sysDevID && AudioObjectHasProperty(sysDevID, &dataSourceProp)){
AudioObjectRemovePropertyListener(sysDevID, &dataSourceProp, AudioOutputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
}
AudioUnitUninitialize(unit);
AudioComponentInstanceDispose(unit);
}
void AudioOutputAudioUnitLegacy::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
}
void AudioOutputAudioUnitLegacy::Start(){
isPlaying=true;
OSStatus status=AudioOutputUnitStart(unit);
CHECK_AU_ERROR(status, "Error starting AudioUnit");
}
void AudioOutputAudioUnitLegacy::Stop(){
isPlaying=false;
OSStatus status=AudioOutputUnitStart(unit);
CHECK_AU_ERROR(status, "Error stopping AudioUnit");
}
OSStatus AudioOutputAudioUnitLegacy::BufferCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData){
AudioOutputAudioUnitLegacy* input=(AudioOutputAudioUnitLegacy*) inRefCon;
input->HandleBufferCallback(ioData);
return noErr;
}
bool AudioOutputAudioUnitLegacy::IsPlaying(){
return isPlaying;
}
void AudioOutputAudioUnitLegacy::HandleBufferCallback(AudioBufferList *ioData){
int i;
unsigned int k;
int16_t absVal=0;
for(i=0;i<ioData->mNumberBuffers;i++){
AudioBuffer buf=ioData->mBuffers[i];
if(!isPlaying){
memset(buf.mData, 0, buf.mDataByteSize);
return;
}
while(remainingDataSize<buf.mDataByteSize){
assert(remainingDataSize+BUFFER_SIZE*2<10240);
InvokeCallback(remainingData+remainingDataSize, BUFFER_SIZE*2);
remainingDataSize+=BUFFER_SIZE*2;
}
memcpy(buf.mData, remainingData, buf.mDataByteSize);
remainingDataSize-=buf.mDataByteSize;
memmove(remainingData, remainingData+buf.mDataByteSize, remainingDataSize);
}
}
void AudioOutputAudioUnitLegacy::EnumerateDevices(std::vector<AudioOutputDevice>& devs){
AudioObjectPropertyAddress propertyAddress = {
kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
UInt32 dataSize = 0;
OSStatus status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize);
if(kAudioHardwareNoError != status) {
LOGE("AudioObjectGetPropertyDataSize (kAudioHardwarePropertyDevices) failed: %i", status);
return;
}
UInt32 deviceCount = (UInt32)(dataSize / sizeof(AudioDeviceID));
AudioDeviceID *audioDevices = (AudioDeviceID*)(malloc(dataSize));
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize, audioDevices);
if(kAudioHardwareNoError != status) {
LOGE("AudioObjectGetPropertyData (kAudioHardwarePropertyDevices) failed: %i", status);
free(audioDevices);
audioDevices = NULL;
return;
}
// Iterate through all the devices and determine which are input-capable
propertyAddress.mScope = kAudioDevicePropertyScopeOutput;
for(UInt32 i = 0; i < deviceCount; ++i) {
// Query device UID
CFStringRef deviceUID = NULL;
dataSize = sizeof(deviceUID);
propertyAddress.mSelector = kAudioDevicePropertyDeviceUID;
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceUID);
if(kAudioHardwareNoError != status) {
LOGE("AudioObjectGetPropertyData (kAudioDevicePropertyDeviceUID) failed: %i", status);
continue;
}
// Query device name
CFStringRef deviceName = NULL;
dataSize = sizeof(deviceName);
propertyAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceName);
if(kAudioHardwareNoError != status) {
LOGE("AudioObjectGetPropertyData (kAudioDevicePropertyDeviceNameCFString) failed: %i", status);
continue;
}
// Determine if the device is an input device (it is an input device if it has input channels)
dataSize = 0;
propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
status = AudioObjectGetPropertyDataSize(audioDevices[i], &propertyAddress, 0, NULL, &dataSize);
if(kAudioHardwareNoError != status) {
LOGE("AudioObjectGetPropertyDataSize (kAudioDevicePropertyStreamConfiguration) failed: %i", status);
continue;
}
AudioBufferList *bufferList = (AudioBufferList*)(malloc(dataSize));
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, bufferList);
if(kAudioHardwareNoError != status || 0 == bufferList->mNumberBuffers) {
if(kAudioHardwareNoError != status)
LOGE("AudioObjectGetPropertyData (kAudioDevicePropertyStreamConfiguration) failed: %i", status);
free(bufferList);
bufferList = NULL;
continue;
}
free(bufferList);
bufferList = NULL;
AudioOutputDevice dev;
char buf[1024];
CFStringGetCString(deviceName, buf, 1024, kCFStringEncodingUTF8);
dev.displayName=std::string(buf);
CFStringGetCString(deviceUID, buf, 1024, kCFStringEncodingUTF8);
dev.id=std::string(buf);
devs.push_back(dev);
}
free(audioDevices);
audioDevices = NULL;
}
void AudioOutputAudioUnitLegacy::SetCurrentDevice(std::string deviceID){
UInt32 size=sizeof(AudioDeviceID);
AudioDeviceID outputDevice=NULL;
OSStatus status;
AudioObjectPropertyAddress dataSourceProp={
kAudioDevicePropertyDataSource,
kAudioDevicePropertyScopeOutput,
kAudioObjectPropertyElementMaster
};
if(isMacBookPro && sysDevID && AudioObjectHasProperty(sysDevID, &dataSourceProp)){
AudioObjectRemovePropertyListener(sysDevID, &dataSourceProp, AudioOutputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
}
if(deviceID=="default"){
AudioObjectPropertyAddress propertyAddress;
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
UInt32 propsize = sizeof(AudioDeviceID);
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propsize, &outputDevice);
CHECK_AU_ERROR(status, "Error getting default input device");
}else{
AudioObjectPropertyAddress propertyAddress = {
kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
UInt32 dataSize = 0;
status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize);
CHECK_AU_ERROR(status, "Error getting devices size");
UInt32 deviceCount = (UInt32)(dataSize / sizeof(AudioDeviceID));
AudioDeviceID audioDevices[deviceCount];
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize, audioDevices);
CHECK_AU_ERROR(status, "Error getting device list");
for(UInt32 i = 0; i < deviceCount; ++i) {
// Query device UID
CFStringRef deviceUID = NULL;
dataSize = sizeof(deviceUID);
propertyAddress.mSelector = kAudioDevicePropertyDeviceUID;
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceUID);
CHECK_AU_ERROR(status, "Error getting device uid");
char buf[1024];
CFStringGetCString(deviceUID, buf, 1024, kCFStringEncodingUTF8);
if(deviceID==buf){
LOGV("Found device for id %s", buf);
outputDevice=audioDevices[i];
break;
}
}
if(!outputDevice){
LOGW("Requested device not found, using default");
SetCurrentDevice("default");
return;
}
}
status =AudioUnitSetProperty(unit,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
kOutputBus,
&outputDevice,
size);
CHECK_AU_ERROR(status, "Error setting output device");
AudioStreamBasicDescription hardwareFormat;
size=sizeof(hardwareFormat);
status=AudioUnitGetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kOutputBus, &hardwareFormat, &size);
CHECK_AU_ERROR(status, "Error getting hardware format");
hardwareSampleRate=hardwareFormat.mSampleRate;
AudioStreamBasicDescription desiredFormat={
.mSampleRate=48000, .mFormatID=kAudioFormatLinearPCM, .mFormatFlags=kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian,
.mFramesPerPacket=1, .mChannelsPerFrame=1, .mBitsPerChannel=16, .mBytesPerPacket=2, .mBytesPerFrame=2
};
status=AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &desiredFormat, sizeof(desiredFormat));
CHECK_AU_ERROR(status, "Error setting format");
LOGD("Switched playback device, new sample rate %d", hardwareSampleRate);
this->currentDevice=deviceID;
sysDevID=outputDevice;
AudioObjectPropertyAddress propertyAddress = {
kAudioDevicePropertyBufferFrameSize,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
size=4;
UInt32 bufferFrameSize;
status=AudioObjectGetPropertyData(outputDevice, &propertyAddress, 0, NULL, &size, &bufferFrameSize);
if(status==noErr){
estimatedDelay=bufferFrameSize/48;
LOGD("CoreAudio buffer size for output device is %u frames (%u ms)", bufferFrameSize, estimatedDelay);
}
if(isMacBookPro){
if(AudioObjectHasProperty(outputDevice, &dataSourceProp)){
UInt32 dataSource;
size=4;
AudioObjectGetPropertyData(outputDevice, &dataSourceProp, 0, NULL, &size, &dataSource);
SetPanRight(dataSource=='ispk');
AudioObjectAddPropertyListener(outputDevice, &dataSourceProp, AudioOutputAudioUnitLegacy::DefaultDeviceChangedCallback, this);
}else{
SetPanRight(false);
}
}
}
void AudioOutputAudioUnitLegacy::SetPanRight(bool panRight){
LOGI("%sabling pan right on macbook pro", panRight ? "En" : "Dis");
int32_t channelMap[]={panRight ? -1 : 0, 0};
OSStatus status=AudioUnitSetProperty(unit, kAudioOutputUnitProperty_ChannelMap, kAudioUnitScope_Global, kOutputBus, channelMap, sizeof(channelMap));
CHECK_AU_ERROR(status, "Error setting channel map");
}
OSStatus AudioOutputAudioUnitLegacy::DefaultDeviceChangedCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData){
AudioOutputAudioUnitLegacy* self=(AudioOutputAudioUnitLegacy*)inClientData;
if(inAddresses[0].mSelector==kAudioHardwarePropertyDefaultOutputDevice){
LOGV("System default input device changed");
if(self->currentDevice=="default"){
self->SetCurrentDevice(self->currentDevice);
}
}else if(inAddresses[0].mSelector==kAudioDevicePropertyDataSource){
UInt32 dataSource;
UInt32 size=4;
AudioObjectGetPropertyData(inObjectID, inAddresses, 0, NULL, &size, &dataSource);
self->SetPanRight(dataSource=='ispk');
}
return noErr;
}

View File

@ -0,0 +1,43 @@
//
// 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 LIBTGVOIP_AUDIOINPUTAUDIOUNIT_OSX_H
#define LIBTGVOIP_AUDIOINPUTAUDIOUNIT_OSX_H
#include <AudioUnit/AudioUnit.h>
#import <AudioToolbox/AudioToolbox.h>
#import <CoreAudio/CoreAudio.h>
#include "../../audio/AudioOutput.h"
namespace tgvoip{ namespace audio{
class AudioOutputAudioUnitLegacy : public AudioOutput{
public:
AudioOutputAudioUnitLegacy(std::string deviceID);
virtual ~AudioOutputAudioUnitLegacy();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
virtual void Start();
virtual void Stop();
virtual bool IsPlaying();
void HandleBufferCallback(AudioBufferList* ioData);
static void EnumerateDevices(std::vector<AudioOutputDevice>& devs);
virtual void SetCurrentDevice(std::string deviceID);
private:
static OSStatus BufferCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData);
static OSStatus DefaultDeviceChangedCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData);
void SetPanRight(bool panRight);
unsigned char remainingData[10240];
size_t remainingDataSize;
bool isPlaying;
AudioUnit unit;
int hardwareSampleRate;
bool isMacBookPro;
AudioDeviceID sysDevID;
};
}}
#endif //LIBTGVOIP_AUDIOINPUTAUDIOUNIT_OSX_H

View File

@ -0,0 +1,317 @@
//
// 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 <stdio.h>
#include "AudioUnitIO.h"
#include "AudioInputAudioUnit.h"
#include "AudioOutputAudioUnit.h"
#include "../../logging.h"
#include "../../VoIPController.h"
#include "../../VoIPServerConfig.h"
#define CHECK_AU_ERROR(res, msg) if(res!=noErr){ LOGE(msg": OSStatus=%d", (int)res); failed=true; return; }
#define BUFFER_SIZE 960 // 20 ms
#define kOutputBus 0
#define kInputBus 1
using namespace tgvoip;
using namespace tgvoip::audio;
int AudioUnitIO::refCount=0;
AudioUnitIO* AudioUnitIO::sharedInstance=NULL;
AudioUnitIO::AudioUnitIO(){
input=NULL;
output=NULL;
inputEnabled=false;
outputEnabled=false;
failed=false;
started=false;
inBufferList.mBuffers[0].mData=malloc(10240);
inBufferList.mBuffers[0].mDataByteSize=10240;
inBufferList.mNumberBuffers=1;
OSStatus status;
AudioComponentDescription desc;
AudioComponent inputComponent;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_VoiceProcessingIO;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
inputComponent = AudioComponentFindNext(NULL, &desc);
status = AudioComponentInstanceNew(inputComponent, &unit);
UInt32 flag=1;
#if TARGET_OS_IPHONE
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");
#endif
#if TARGET_OS_IPHONE
flag=ServerConfig::GetSharedInstance()->GetBoolean("use_ios_vpio_agc", true) ? 1 : 0;
#else
flag=ServerConfig::GetSharedInstance()->GetBoolean("use_osx_vpio_agc", true) ? 1 : 0;
#endif
status=AudioUnitSetProperty(unit, kAUVoiceIOProperty_VoiceProcessingEnableAGC, kAudioUnitScope_Global, kInputBus, &flag, sizeof(flag));
CHECK_AU_ERROR(status, "Error disabling AGC");
AudioStreamBasicDescription audioFormat;
audioFormat.mSampleRate = 48000;
audioFormat.mFormatID = kAudioFormatLinearPCM;
#if TARGET_OS_IPHONE
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian;
audioFormat.mBitsPerChannel = 16;
audioFormat.mBytesPerPacket = 2;
audioFormat.mBytesPerFrame = 2;
#else // OS X
audioFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian;
audioFormat.mBitsPerChannel = 32;
audioFormat.mBytesPerPacket = 4;
audioFormat.mBytesPerFrame = 4;
#endif
audioFormat.mFramesPerPacket = 1;
audioFormat.mChannelsPerFrame = 1;
status = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &audioFormat, sizeof(audioFormat));
CHECK_AU_ERROR(status, "Error setting output format");
status = AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &audioFormat, sizeof(audioFormat));
CHECK_AU_ERROR(status, "Error setting input format");
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = AudioUnitIO::BufferCallback;
callbackStruct.inputProcRefCon = this;
status = AudioUnitSetProperty(unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, kOutputBus, &callbackStruct, sizeof(callbackStruct));
CHECK_AU_ERROR(status, "Error setting output buffer callback");
status = AudioUnitSetProperty(unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, kInputBus, &callbackStruct, sizeof(callbackStruct));
CHECK_AU_ERROR(status, "Error setting input buffer callback");
#if TARGET_OS_OSX
CFRunLoopRef theRunLoop = NULL;
AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyRunLoop,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster };
status = AudioObjectSetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioUnitIO::DefaultDeviceChangedCallback, this);
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioUnitIO::DefaultDeviceChangedCallback, this);
#endif
}
AudioUnitIO::~AudioUnitIO(){
AudioOutputUnitStop(unit);
AudioUnitUninitialize(unit);
AudioComponentInstanceDispose(unit);
free(inBufferList.mBuffers[0].mData);
#if TARGET_OS_OSX
AudioObjectPropertyAddress propertyAddress;
propertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioUnitIO::DefaultDeviceChangedCallback, this);
propertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &propertyAddress, AudioUnitIO::DefaultDeviceChangedCallback, this);
#endif
}
AudioUnitIO* AudioUnitIO::Get(){
if(refCount==0){
sharedInstance=new AudioUnitIO();
}
refCount++;
assert(refCount>0);
return sharedInstance;
}
void AudioUnitIO::Release(){
refCount--;
assert(refCount>=0);
if(refCount==0){
delete sharedInstance;
sharedInstance=NULL;
}
}
void AudioUnitIO::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
}
OSStatus AudioUnitIO::BufferCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData){
((AudioUnitIO*)inRefCon)->BufferCallback(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
return noErr;
}
void AudioUnitIO::BufferCallback(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 bus, UInt32 numFrames, AudioBufferList *ioData){
if(bus==kOutputBus){
if(output && outputEnabled){
output->HandleBufferCallback(ioData);
}else{
memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize);
}
}else if(bus==kInputBus){
inBufferList.mBuffers[0].mDataByteSize=10240;
AudioUnitRender(unit, ioActionFlags, inTimeStamp, bus, numFrames, &inBufferList);
if(input && inputEnabled){
input->HandleBufferCallback(&inBufferList);
}
}
}
void AudioUnitIO::AttachInput(AudioInputAudioUnit *i){
assert(input==NULL);
input=i;
}
void AudioUnitIO::AttachOutput(AudioOutputAudioUnit *o){
assert(output==NULL);
output=o;
}
void AudioUnitIO::DetachInput(){
assert(input!=NULL);
input=NULL;
inputEnabled=false;
}
void AudioUnitIO::DetachOutput(){
assert(output!=NULL);
output=NULL;
outputEnabled=false;
}
void AudioUnitIO::EnableInput(bool enabled){
inputEnabled=enabled;
StartIfNeeded();
}
void AudioUnitIO::EnableOutput(bool enabled){
outputEnabled=enabled;
StartIfNeeded();
}
void AudioUnitIO::StartIfNeeded(){
if(started)
return;
started=true;
OSStatus status = AudioUnitInitialize(unit);
CHECK_AU_ERROR(status, "Error initializing AudioUnit");
status=AudioOutputUnitStart(unit);
CHECK_AU_ERROR(status, "Error starting AudioUnit");
}
bool AudioUnitIO::IsFailed(){
return failed;
}
#if TARGET_OS_OSX
OSStatus AudioUnitIO::DefaultDeviceChangedCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData){
AudioUnitIO* self=(AudioUnitIO*)inClientData;
if(inAddresses[0].mSelector==kAudioHardwarePropertyDefaultOutputDevice){
LOGV("System default output device changed");
if(self->currentOutputDevice=="default"){
self->SetCurrentDevice(false, self->currentOutputDevice);
}
}else if(inAddresses[0].mSelector==kAudioHardwarePropertyDefaultInputDevice){
LOGV("System default input device changed");
if(self->currentInputDevice=="default"){
self->SetCurrentDevice(true, self->currentInputDevice);
}
}
return noErr;
}
void AudioUnitIO::SetCurrentDevice(bool input, std::string deviceID){
if(started){
AudioOutputUnitStop(unit);
AudioUnitUninitialize(unit);
}
UInt32 size=sizeof(AudioDeviceID);
AudioDeviceID device=NULL;
OSStatus status;
if(deviceID=="default"){
AudioObjectPropertyAddress propertyAddress;
propertyAddress.mSelector = input ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice;
propertyAddress.mScope = kAudioObjectPropertyScopeGlobal;
propertyAddress.mElement = kAudioObjectPropertyElementMaster;
UInt32 propsize = sizeof(AudioDeviceID);
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &propsize, &device);
CHECK_AU_ERROR(status, "Error getting default device");
}else{
AudioObjectPropertyAddress propertyAddress = {
kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
UInt32 dataSize = 0;
status = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize);
CHECK_AU_ERROR(status, "Error getting devices size");
UInt32 deviceCount = (UInt32)(dataSize / sizeof(AudioDeviceID));
AudioDeviceID audioDevices[deviceCount];
status = AudioObjectGetPropertyData(kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize, audioDevices);
CHECK_AU_ERROR(status, "Error getting device list");
for(UInt32 i = 0; i < deviceCount; ++i) {
// Query device UID
CFStringRef deviceUID = NULL;
dataSize = sizeof(deviceUID);
propertyAddress.mSelector = kAudioDevicePropertyDeviceUID;
status = AudioObjectGetPropertyData(audioDevices[i], &propertyAddress, 0, NULL, &dataSize, &deviceUID);
CHECK_AU_ERROR(status, "Error getting device uid");
char buf[1024];
CFStringGetCString(deviceUID, buf, 1024, kCFStringEncodingUTF8);
if(deviceID==buf){
LOGV("Found device for id %s", buf);
device=audioDevices[i];
break;
}
}
if(!device){
LOGW("Requested device not found, using default");
SetCurrentDevice(input, "default");
return;
}
}
status=AudioUnitSetProperty(unit,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
input ? kInputBus : kOutputBus,
&device,
size);
CHECK_AU_ERROR(status, "Error setting input device");
if(input)
currentInputDevice=deviceID;
else
currentOutputDevice=deviceID;
/*AudioObjectPropertyAddress propertyAddress = {
kAudioDevicePropertyBufferFrameSize,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
size=4;
UInt32 bufferFrameSize;
status=AudioObjectGetPropertyData(device, &propertyAddress, 0, NULL, &size, &bufferFrameSize);
if(status==noErr){
estimatedDelay=bufferFrameSize/48;
LOGD("CoreAudio buffer size for device is %u frames (%u ms)", bufferFrameSize, estimatedDelay);
}*/
if(started){
started=false;
StartIfNeeded();
}
}
#endif

View File

@ -0,0 +1,59 @@
//
// 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 LIBTGVOIP_AUDIOUNITIO_H
#define LIBTGVOIP_AUDIOUNITIO_H
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
#include "../../threading.h"
#include <string>
namespace tgvoip{ namespace audio{
class AudioInputAudioUnit;
class AudioOutputAudioUnit;
class AudioUnitIO{
public:
AudioUnitIO();
~AudioUnitIO();
void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
void AttachInput(AudioInputAudioUnit* i);
void AttachOutput(AudioOutputAudioUnit* o);
void DetachInput();
void DetachOutput();
void EnableInput(bool enabled);
void EnableOutput(bool enabled);
bool IsFailed();
static AudioUnitIO* Get();
static void Release();
#if TARGET_OS_OSX
void SetCurrentDevice(bool input, std::string deviceID);
#endif
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 StartIfNeeded();
#if TARGET_OS_OSX
static OSStatus DefaultDeviceChangedCallback(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData);
std::string currentInputDevice;
std::string currentOutputDevice;
#endif
AudioComponentInstance unit;
AudioInputAudioUnit* input;
AudioOutputAudioUnit* output;
AudioBufferList inBufferList;
bool inputEnabled;
bool outputEnabled;
bool failed;
bool started;
static int refCount;
static AudioUnitIO* sharedInstance;
};
}}
#endif /* LIBTGVOIP_AUDIOUNITIO_H */

View File

@ -0,0 +1,19 @@
//
// 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_DARWINSPECIFIC_H
#define TGVOIP_DARWINSPECIFIC_H
#include <string>
namespace tgvoip {
class DarwinSpecific{
public:
static void GetSystemName(char* buf, size_t len);
};
}
#endif //TGVOIP_DARWINSPECIFIC_H

View File

@ -0,0 +1,17 @@
//
// 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 "DarwinSpecific.h"
#import <Foundation/Foundation.h>
using namespace tgvoip;
void DarwinSpecific::GetSystemName(char* buf, size_t len){
NSString* v=[[NSProcessInfo processInfo] operatingSystemVersionString];
strcpy(buf, [v UTF8String]);
//[v getCString:buf maxLength:sizeof(buf) encoding:NSUTF8StringEncoding];
}

View File

@ -0,0 +1,20 @@
//
// 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_TGLOGWRAPPER_H
#define TGVOIP_TGLOGWRAPPER_H
#if defined __cplusplus
extern "C" {
#endif
void __tgvoip_call_tglog(const char* format, ...);
#if defined __cplusplus
};
#endif
#endif //TGVOIP_TGLOGWRAPPER_H

View File

@ -0,0 +1,10 @@
#import <Foundation/Foundation.h>
extern void TGLogv(NSString *format, va_list args);
void __tgvoip_call_tglog(const char* format, ...){
va_list args;
va_start(args, format);
TGLogv([[NSString alloc]initWithUTF8String:format], args);
va_end(args);
}

View File

@ -0,0 +1,179 @@
//
// 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 <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <dlfcn.h>
#include "AudioInputALSA.h"
#include "../../logging.h"
#include "../../VoIPController.h"
using namespace tgvoip::audio;
#define BUFFER_SIZE 960
#define CHECK_ERROR(res, msg) if(res<0){LOGE(msg ": %s", _snd_strerror(res));}
#define CHECK_DL_ERROR(res, msg) if(!res){LOGE(msg ": %s", dlerror()); failed=true; return;}
#define LOAD_FUNCTION(lib, name, ref) {ref=(typeof(ref))dlsym(lib, name); CHECK_DL_ERROR(ref, "Error getting entry point for " name);}
AudioInputALSA::AudioInputALSA(std::string devID){
isRecording=false;
handle=NULL;
lib=dlopen("libasound.so.2", RTLD_LAZY);
if(!lib)
lib=dlopen("libasound.so", RTLD_LAZY);
if(!lib){
LOGE("Error loading libasound: %s", dlerror());
failed=true;
return;
}
LOAD_FUNCTION(lib, "snd_pcm_open", _snd_pcm_open);
LOAD_FUNCTION(lib, "snd_pcm_set_params", _snd_pcm_set_params);
LOAD_FUNCTION(lib, "snd_pcm_close", _snd_pcm_close);
LOAD_FUNCTION(lib, "snd_pcm_readi", _snd_pcm_readi);
LOAD_FUNCTION(lib, "snd_pcm_recover", _snd_pcm_recover);
LOAD_FUNCTION(lib, "snd_strerror", _snd_strerror);
SetCurrentDevice(devID);
}
AudioInputALSA::~AudioInputALSA(){
if(handle)
_snd_pcm_close(handle);
if(lib)
dlclose(lib);
}
void AudioInputALSA::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
}
void AudioInputALSA::Start(){
if(failed || isRecording)
return;
isRecording=true;
start_thread(thread, AudioInputALSA::StartThread, this);
}
void AudioInputALSA::Stop(){
if(!isRecording)
return;
isRecording=false;
join_thread(thread);
}
void* AudioInputALSA::StartThread(void* arg){
((AudioInputALSA*)arg)->RunThread();
}
void AudioInputALSA::RunThread(){
unsigned char buffer[BUFFER_SIZE*2];
snd_pcm_sframes_t frames;
while(isRecording){
frames=_snd_pcm_readi(handle, buffer, BUFFER_SIZE);
if (frames < 0){
frames = _snd_pcm_recover(handle, frames, 0);
}
if (frames < 0) {
LOGE("snd_pcm_readi failed: %s\n", _snd_strerror(frames));
break;
}
InvokeCallback(buffer, sizeof(buffer));
}
}
void AudioInputALSA::SetCurrentDevice(std::string devID){
bool wasRecording=isRecording;
isRecording=false;
if(handle){
join_thread(thread);
_snd_pcm_close(handle);
}
currentDevice=devID;
int res=_snd_pcm_open(&handle, devID.c_str(), SND_PCM_STREAM_CAPTURE, 0);
if(res<0)
res=_snd_pcm_open(&handle, "default", SND_PCM_STREAM_CAPTURE, 0);
CHECK_ERROR(res, "snd_pcm_open failed");
res=_snd_pcm_set_params(handle, SND_PCM_FORMAT_S16, SND_PCM_ACCESS_RW_INTERLEAVED, 1, 48000, 1, 100000);
CHECK_ERROR(res, "snd_pcm_set_params failed");
if(wasRecording){
isRecording=true;
start_thread(thread, AudioInputALSA::StartThread, this);
}
}
void AudioInputALSA::EnumerateDevices(std::vector<AudioInputDevice>& devs){
int (*_snd_device_name_hint)(int card, const char* iface, void*** hints);
char* (*_snd_device_name_get_hint)(const void* hint, const char* id);
int (*_snd_device_name_free_hint)(void** hinst);
void* lib=dlopen("libasound.so.2", RTLD_LAZY);
if(!lib)
dlopen("libasound.so", RTLD_LAZY);
if(!lib)
return;
_snd_device_name_hint=(typeof(_snd_device_name_hint))dlsym(lib, "snd_device_name_hint");
_snd_device_name_get_hint=(typeof(_snd_device_name_get_hint))dlsym(lib, "snd_device_name_get_hint");
_snd_device_name_free_hint=(typeof(_snd_device_name_free_hint))dlsym(lib, "snd_device_name_free_hint");
if(!_snd_device_name_hint || !_snd_device_name_get_hint || !_snd_device_name_free_hint){
dlclose(lib);
return;
}
char** hints;
int err=_snd_device_name_hint(-1, "pcm", (void***)&hints);
if(err!=0){
dlclose(lib);
return;
}
char** n=hints;
while(*n){
char* name=_snd_device_name_get_hint(*n, "NAME");
if(strncmp(name, "surround", 8)==0 || strcmp(name, "null")==0){
free(name);
n++;
continue;
}
char* desc=_snd_device_name_get_hint(*n, "DESC");
char* ioid=_snd_device_name_get_hint(*n, "IOID");
if(!ioid || strcmp(ioid, "Input")==0){
char* l1=strtok(desc, "\n");
char* l2=strtok(NULL, "\n");
char* tmp=strtok(l1, ",");
char* actualName=tmp;
while((tmp=strtok(NULL, ","))){
actualName=tmp;
}
if(actualName[0]==' ')
actualName++;
AudioInputDevice dev;
dev.id=std::string(name);
if(l2){
char buf[256];
snprintf(buf, sizeof(buf), "%s (%s)", actualName, l2);
dev.displayName=std::string(buf);
}else{
dev.displayName=std::string(actualName);
}
devs.push_back(dev);
}
free(name);
free(desc);
free(ioid);
n++;
}
dlclose(lib);
}

View File

@ -0,0 +1,48 @@
//
// 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 LIBTGVOIP_AUDIOINPUTALSA_H
#define LIBTGVOIP_AUDIOINPUTALSA_H
#include "../../audio/AudioInput.h"
#include "../../threading.h"
#include <alsa/asoundlib.h>
namespace tgvoip{
namespace audio{
class AudioInputALSA : public AudioInput{
public:
AudioInputALSA(std::string devID);
virtual ~AudioInputALSA();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
virtual void Start();
virtual void Stop();
virtual void SetCurrentDevice(std::string devID);
static void EnumerateDevices(std::vector<AudioInputDevice>& devs);
private:
static void* StartThread(void* arg);
void RunThread();
int (*_snd_pcm_open)(snd_pcm_t** pcm, const char* name, snd_pcm_stream_t stream, int mode);
int (*_snd_pcm_set_params)(snd_pcm_t* pcm, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int channels, unsigned int rate, int soft_resample, unsigned int latency);
int (*_snd_pcm_close)(snd_pcm_t* pcm);
snd_pcm_sframes_t (*_snd_pcm_readi)(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size);
int (*_snd_pcm_recover)(snd_pcm_t* pcm, int err, int silent);
const char* (*_snd_strerror)(int errnum);
void* lib;
snd_pcm_t* handle;
tgvoip_thread_t thread;
bool isRecording;
};
}
}
#endif //LIBTGVOIP_AUDIOINPUTALSA_H

View File

@ -0,0 +1,333 @@
//
// 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 <assert.h>
#include <dlfcn.h>
#include <unistd.h>
#include "AudioInputPulse.h"
#include "../../logging.h"
#include "../../VoIPController.h"
#define TGVOIP_IN_AUDIO_IO
#include "PulseAudioLoader.h"
#undef TGVOIP_IN_AUDIO_IO
#define BUFFER_SIZE 960
#define CHECK_ERROR(res, msg) if(res!=0){LOGE(msg " failed: %s", pa_strerror(res)); failed=true; return;}
using namespace tgvoip::audio;
AudioInputPulse::AudioInputPulse(std::string devID){
isRecording=false;
isConnected=false;
didStart=false;
mainloop=NULL;
mainloopApi=NULL;
context=NULL;
stream=NULL;
remainingDataSize=0;
if(!PulseAudioLoader::IncRef()){
failed=true;
return;
}
mainloop=pa_threaded_mainloop_new();
if(!mainloop){
LOGE("Error initializing PulseAudio (pa_threaded_mainloop_new)");
failed=true;
return;
}
mainloopApi=pa_threaded_mainloop_get_api(mainloop);
char exePath[MAXPATHLEN];
char exeName[MAXPATHLEN];
ssize_t lres=readlink("/proc/self/exe", exePath, sizeof(exePath));
if(lres==-1)
lres=readlink("/proc/curproc/file", exePath, sizeof(exePath));
if(lres==-1)
lres=readlink("/proc/curproc/exe", exePath, sizeof(exePath));
if(lres>0){
strcpy(exeName, basename(exePath));
}else{
snprintf(exeName, sizeof(exeName), "Process %d", getpid());
}
context=pa_context_new(mainloopApi, exeName);
if(!context){
LOGE("Error initializing PulseAudio (pa_context_new)");
failed=true;
return;
}
pa_context_set_state_callback(context, AudioInputPulse::ContextStateCallback, this);
pa_threaded_mainloop_lock(mainloop);
isLocked=true;
int err=pa_threaded_mainloop_start(mainloop);
CHECK_ERROR(err, "pa_threaded_mainloop_start");
didStart=true;
err=pa_context_connect(context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL);
CHECK_ERROR(err, "pa_context_connect");
while(true){
pa_threaded_mainloop_lock(mainloop);
pa_context_state_t contextState=pa_context_get_state(context);
pa_threaded_mainloop_unlock(mainloop);
if(!PA_CONTEXT_IS_GOOD(contextState)){
LOGE("Error initializing PulseAudio (PA_CONTEXT_IS_GOOD)");
failed=true;
return;
}
if(contextState==PA_CONTEXT_READY)
break;
pa_threaded_mainloop_wait(mainloop);
}
pa_sample_spec sample_specifications{
.format=PA_SAMPLE_S16LE,
.rate=48000,
.channels=1
};
stream=pa_stream_new(context, "libtgvoip capture", &sample_specifications, NULL);
if(!stream){
LOGE("Error initializing PulseAudio (pa_stream_new)");
failed=true;
return;
}
pa_stream_set_state_callback(stream, AudioInputPulse::StreamStateCallback, this);
pa_stream_set_read_callback(stream, AudioInputPulse::StreamReadCallback, this);
pa_threaded_mainloop_unlock(mainloop);
isLocked=false;
SetCurrentDevice(devID);
}
AudioInputPulse::~AudioInputPulse(){
if(mainloop && didStart){
if(isLocked)
pa_threaded_mainloop_unlock(mainloop);
pa_threaded_mainloop_stop(mainloop);
}
if(stream){
pa_stream_disconnect(stream);
pa_stream_unref(stream);
}
if(context){
pa_context_disconnect(context);
pa_context_unref(context);
}
if(mainloop)
pa_threaded_mainloop_free(mainloop);
PulseAudioLoader::DecRef();
}
bool AudioInputPulse::IsAvailable(){
void* lib=dlopen("libpulse.so.0", RTLD_LAZY);
if(!lib)
lib=dlopen("libpulse.so", RTLD_LAZY);
if(lib){
dlclose(lib);
return true;
}
return false;
}
void AudioInputPulse::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
}
void AudioInputPulse::Start(){
if(failed || isRecording)
return;
isRecording=true;
pa_operation_unref(pa_stream_cork(stream, 0, AudioInputPulse::StreamSuccessCallback, mainloop));
}
void AudioInputPulse::Stop(){
if(!isRecording)
return;
isRecording=false;
pa_operation_unref(pa_stream_cork(stream, 1, AudioInputPulse::StreamSuccessCallback, mainloop));
}
bool AudioInputPulse::IsRecording(){
return isRecording;
}
void AudioInputPulse::SetCurrentDevice(std::string devID){
currentDevice=devID;
if(isRecording && isConnected){
pa_stream_disconnect(stream);
isConnected=false;
}
pa_buffer_attr bufferAttr={
.maxlength=960*6,
.tlength=960*6,
.prebuf=0,
.minreq=960*2
};
int streamFlags=PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING |
PA_STREAM_NOT_MONOTONIC | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_ADJUST_LATENCY;
int err=pa_stream_connect_record(stream, devID=="default" ? NULL : devID.c_str(), &bufferAttr, (pa_stream_flags_t)streamFlags);
if(err!=0 && devID!="default"){
SetCurrentDevice("default");
return;
}
CHECK_ERROR(err, "pa_stream_connect_record");
while(true){
pa_threaded_mainloop_lock(mainloop);
pa_stream_state_t streamState=pa_stream_get_state(stream);
pa_threaded_mainloop_unlock(mainloop);
if(!PA_STREAM_IS_GOOD(streamState)){
LOGE("Error connecting to audio device '%s'", devID.c_str());
failed=true;
return;
}
if(streamState==PA_STREAM_READY)
break;
pa_threaded_mainloop_wait(mainloop);
}
isConnected=true;
if(isRecording){
pa_operation_unref(pa_stream_cork(stream, 0, AudioInputPulse::StreamSuccessCallback, mainloop));
}
}
bool AudioInputPulse::EnumerateDevices(std::vector<AudioInputDevice>& devs){
if(!PulseAudioLoader::IncRef())
return false;
pa_mainloop* ml;
pa_mainloop_api* mlAPI;
pa_context* ctx;
pa_operation* op=NULL;
int state=0;
int paReady=0;
ml=pa_mainloop_new();
mlAPI=pa_mainloop_get_api(ml);
ctx=pa_context_new(mlAPI, "libtgvoip");
pa_context_connect(ctx, NULL, PA_CONTEXT_NOFLAGS, NULL);
pa_context_set_state_callback(ctx, AudioInputPulse::ContextStateCallbackEnum, &paReady);
while(true){
if(paReady==0){
pa_mainloop_iterate(ml, 1, NULL);
continue;
}
if(paReady==2){
pa_context_disconnect(ctx);
pa_context_unref(ctx);
pa_mainloop_free(ml);
PulseAudioLoader::DecRef();
return false;
}
if(!op){
op=pa_context_get_source_info_list(ctx, AudioInputPulse::DeviceEnumCallback, &devs);
continue;
}
if(pa_operation_get_state(op)==PA_OPERATION_DONE){
pa_operation_unref(op);
pa_context_disconnect(ctx);
pa_context_unref(ctx);
pa_mainloop_free(ml);
PulseAudioLoader::DecRef();
return true;
}
pa_mainloop_iterate(ml, 1, NULL);
}
}
void AudioInputPulse::ContextStateCallback(pa_context* context, void* arg) {
AudioInputPulse* self=(AudioInputPulse*) arg;
pa_threaded_mainloop_signal(self->mainloop, 0);
}
void AudioInputPulse::StreamStateCallback(pa_stream *s, void* arg) {
AudioInputPulse* self=(AudioInputPulse*) arg;
pa_threaded_mainloop_signal(self->mainloop, 0);
}
void AudioInputPulse::StreamReadCallback(pa_stream *stream, size_t requestedBytes, void *userdata){
((AudioInputPulse*)userdata)->StreamReadCallback(stream, requestedBytes);
}
void AudioInputPulse::StreamReadCallback(pa_stream *stream, size_t requestedBytes) {
int bytesRemaining = requestedBytes;
uint8_t *buffer = NULL;
while (bytesRemaining > 0) {
size_t bytesToFill = 102400;
size_t i;
if (bytesToFill > bytesRemaining) bytesToFill = bytesRemaining;
int err=pa_stream_peek(stream, (const void**) &buffer, &bytesToFill);
CHECK_ERROR(err, "pa_stream_peek");
if(isRecording){
if(remainingDataSize+bytesToFill>sizeof(remainingData)){
LOGE("Capture buffer is too big (%d)", (int)bytesToFill);
}
memcpy(remainingData+remainingDataSize, buffer, bytesToFill);
remainingDataSize+=bytesToFill;
while(remainingDataSize>=960*2){
InvokeCallback(remainingData, 960*2);
memmove(remainingData, remainingData+960*2, remainingDataSize-960*2);
remainingDataSize-=960*2;
}
}
err=pa_stream_drop(stream);
CHECK_ERROR(err, "pa_stream_drop");
bytesRemaining -= bytesToFill;
}
}
void AudioInputPulse::StreamSuccessCallback(pa_stream *stream, int success, void *userdata) {
return;
}
void AudioInputPulse::ContextStateCallbackEnum(pa_context* context, void* arg){
pa_context_state_t state;
int* pa_ready=(int*)arg;
state=pa_context_get_state(context);
switch(state){
case PA_CONTEXT_UNCONNECTED:
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
default:
break;
case PA_CONTEXT_FAILED:
case PA_CONTEXT_TERMINATED:
*pa_ready=2;
break;
case PA_CONTEXT_READY:
*pa_ready=1;
break;
}
}
void AudioInputPulse::DeviceEnumCallback(pa_context* ctx, const pa_source_info* info, int eol, void* userdata){
if(eol>0)
return;
std::vector<AudioInputDevice>* devs=(std::vector<AudioInputDevice>*)userdata;
AudioInputDevice dev;
dev.id=std::string(info->name);
dev.displayName=std::string(info->description);
devs->push_back(dev);
}

View File

@ -0,0 +1,58 @@
//
// 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 LIBTGVOIP_AUDIOINPUTPULSE_H
#define LIBTGVOIP_AUDIOINPUTPULSE_H
#include "../../audio/AudioInput.h"
#include "../../threading.h"
#include <pulse/pulseaudio.h>
#define DECLARE_DL_FUNCTION(name) typeof(name)* _import_##name
namespace tgvoip{
namespace audio{
class AudioInputPulse : public AudioInput{
public:
AudioInputPulse(std::string devID);
virtual ~AudioInputPulse();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
virtual void Start();
virtual void Stop();
virtual bool IsRecording();
virtual void SetCurrentDevice(std::string devID);
static bool EnumerateDevices(std::vector<AudioInputDevice>& devs);
static bool IsAvailable();
private:
static void ContextStateCallback(pa_context* context, void* arg);
static void ContextStateCallbackEnum(pa_context* context, void* arg);
static void StreamStateCallback(pa_stream* s, void* arg);
static void StreamSuccessCallback(pa_stream* stream, int success, void* userdata);
static void StreamReadCallback(pa_stream* stream, size_t requested_bytes, void* userdata);
static void DeviceEnumCallback(pa_context* ctx, const pa_source_info* info, int eol, void* userdata);
void StreamReadCallback(pa_stream* stream, size_t requestedBytes);
pa_threaded_mainloop* mainloop;
pa_mainloop_api* mainloopApi;
pa_context* context;
pa_stream* stream;
bool isRecording;
bool isConnected;
bool didStart;
bool isLocked;
unsigned char remainingData[960*8*2];
size_t remainingDataSize;
};
}
}
#undef DECLARE_DL_FUNCTION
#endif //LIBTGVOIP_AUDIOINPUTPULSE_H

View File

@ -0,0 +1,182 @@
//
// 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 <assert.h>
#include <dlfcn.h>
#include "AudioOutputALSA.h"
#include "../../logging.h"
#include "../../VoIPController.h"
#define BUFFER_SIZE 960
#define CHECK_ERROR(res, msg) if(res<0){LOGE(msg ": %s", _snd_strerror(res));}
#define CHECK_DL_ERROR(res, msg) if(!res){LOGE(msg ": %s", dlerror()); failed=true; return;}
#define LOAD_FUNCTION(lib, name, ref) {ref=(typeof(ref))dlsym(lib, name); CHECK_DL_ERROR(ref, "Error getting entry point for " name);}
using namespace tgvoip::audio;
AudioOutputALSA::AudioOutputALSA(std::string devID){
isPlaying=false;
handle=NULL;
lib=dlopen("libasound.so.2", RTLD_LAZY);
if(!lib)
lib=dlopen("libasound.so", RTLD_LAZY);
if(!lib){
LOGE("Error loading libasound: %s", dlerror());
failed=true;
return;
}
LOAD_FUNCTION(lib, "snd_pcm_open", _snd_pcm_open);
LOAD_FUNCTION(lib, "snd_pcm_set_params", _snd_pcm_set_params);
LOAD_FUNCTION(lib, "snd_pcm_close", _snd_pcm_close);
LOAD_FUNCTION(lib, "snd_pcm_writei", _snd_pcm_writei);
LOAD_FUNCTION(lib, "snd_pcm_recover", _snd_pcm_recover);
LOAD_FUNCTION(lib, "snd_strerror", _snd_strerror);
SetCurrentDevice(devID);
}
AudioOutputALSA::~AudioOutputALSA(){
if(handle)
_snd_pcm_close(handle);
if(lib)
dlclose(lib);
}
void AudioOutputALSA::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
}
void AudioOutputALSA::Start(){
if(failed || isPlaying)
return;
isPlaying=true;
start_thread(thread, AudioOutputALSA::StartThread, this);
}
void AudioOutputALSA::Stop(){
if(!isPlaying)
return;
isPlaying=false;
join_thread(thread);
}
bool AudioOutputALSA::IsPlaying(){
return isPlaying;
}
void* AudioOutputALSA::StartThread(void* arg){
((AudioOutputALSA*)arg)->RunThread();
}
void AudioOutputALSA::RunThread(){
unsigned char buffer[BUFFER_SIZE*2];
snd_pcm_sframes_t frames;
while(isPlaying){
InvokeCallback(buffer, sizeof(buffer));
frames=_snd_pcm_writei(handle, buffer, BUFFER_SIZE);
if (frames < 0){
frames = _snd_pcm_recover(handle, frames, 0);
}
if (frames < 0) {
LOGE("snd_pcm_writei failed: %s\n", _snd_strerror(frames));
break;
}
}
}
void AudioOutputALSA::SetCurrentDevice(std::string devID){
bool wasPlaying=isPlaying;
isPlaying=false;
if(handle){
join_thread(thread);
_snd_pcm_close(handle);
}
currentDevice=devID;
int res=_snd_pcm_open(&handle, devID.c_str(), SND_PCM_STREAM_PLAYBACK, 0);
if(res<0)
res=_snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
CHECK_ERROR(res, "snd_pcm_open failed");
res=_snd_pcm_set_params(handle, SND_PCM_FORMAT_S16, SND_PCM_ACCESS_RW_INTERLEAVED, 1, 48000, 1, 100000);
CHECK_ERROR(res, "snd_pcm_set_params failed");
if(wasPlaying){
isPlaying=true;
start_thread(thread, AudioOutputALSA::StartThread, this);
}
}
void AudioOutputALSA::EnumerateDevices(std::vector<AudioOutputDevice>& devs){
int (*_snd_device_name_hint)(int card, const char* iface, void*** hints);
char* (*_snd_device_name_get_hint)(const void* hint, const char* id);
int (*_snd_device_name_free_hint)(void** hinst);
void* lib=dlopen("libasound.so.2", RTLD_LAZY);
if(!lib)
dlopen("libasound.so", RTLD_LAZY);
if(!lib)
return;
_snd_device_name_hint=(typeof(_snd_device_name_hint))dlsym(lib, "snd_device_name_hint");
_snd_device_name_get_hint=(typeof(_snd_device_name_get_hint))dlsym(lib, "snd_device_name_get_hint");
_snd_device_name_free_hint=(typeof(_snd_device_name_free_hint))dlsym(lib, "snd_device_name_free_hint");
if(!_snd_device_name_hint || !_snd_device_name_get_hint || !_snd_device_name_free_hint){
dlclose(lib);
return;
}
char** hints;
int err=_snd_device_name_hint(-1, "pcm", (void***)&hints);
if(err!=0){
dlclose(lib);
return;
}
char** n=hints;
while(*n){
char* name=_snd_device_name_get_hint(*n, "NAME");
if(strncmp(name, "surround", 8)==0 || strcmp(name, "null")==0){
free(name);
n++;
continue;
}
char* desc=_snd_device_name_get_hint(*n, "DESC");
char* ioid=_snd_device_name_get_hint(*n, "IOID");
if(!ioid || strcmp(ioid, "Output")==0){
char* l1=strtok(desc, "\n");
char* l2=strtok(NULL, "\n");
char* tmp=strtok(l1, ",");
char* actualName=tmp;
while((tmp=strtok(NULL, ","))){
actualName=tmp;
}
if(actualName[0]==' ')
actualName++;
AudioOutputDevice dev;
dev.id=std::string(name);
if(l2){
char buf[256];
snprintf(buf, sizeof(buf), "%s (%s)", actualName, l2);
dev.displayName=std::string(buf);
}else{
dev.displayName=std::string(actualName);
}
devs.push_back(dev);
}
free(name);
free(desc);
free(ioid);
n++;
}
dlclose(lib);
}

View File

@ -0,0 +1,48 @@
//
// 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 LIBTGVOIP_AUDIOOUTPUTALSA_H
#define LIBTGVOIP_AUDIOOUTPUTALSA_H
#include "../../audio/AudioOutput.h"
#include "../../threading.h"
#include <alsa/asoundlib.h>
namespace tgvoip{
namespace audio{
class AudioOutputALSA : public AudioOutput{
public:
AudioOutputALSA(std::string devID);
virtual ~AudioOutputALSA();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
virtual void Start();
virtual void Stop();
virtual bool IsPlaying();
virtual void SetCurrentDevice(std::string devID);
static void EnumerateDevices(std::vector<AudioOutputDevice>& devs);
private:
static void* StartThread(void* arg);
void RunThread();
int (*_snd_pcm_open)(snd_pcm_t** pcm, const char* name, snd_pcm_stream_t stream, int mode);
int (*_snd_pcm_set_params)(snd_pcm_t* pcm, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int channels, unsigned int rate, int soft_resample, unsigned int latency);
int (*_snd_pcm_close)(snd_pcm_t* pcm);
snd_pcm_sframes_t (*_snd_pcm_writei)(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size);
int (*_snd_pcm_recover)(snd_pcm_t* pcm, int err, int silent);
const char* (*_snd_strerror)(int errnum);
void* lib;
snd_pcm_t* handle;
tgvoip_thread_t thread;
bool isPlaying;
};
}
}
#endif //LIBTGVOIP_AUDIOOUTPUTALSA_H

View File

@ -0,0 +1,340 @@
//
// 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 <assert.h>
#include <dlfcn.h>
#include <unistd.h>
#include "AudioOutputPulse.h"
#include "../../logging.h"
#include "../../VoIPController.h"
#define TGVOIP_IN_AUDIO_IO
#include "PulseAudioLoader.h"
#undef TGVOIP_IN_AUDIO_IO
#define BUFFER_SIZE 960
#define CHECK_ERROR(res, msg) if(res!=0){LOGE(msg " failed: %s", pa_strerror(res)); failed=true; return;}
using namespace tgvoip;
using namespace tgvoip::audio;
using tgvoip::PulseAudioLoader;
AudioOutputPulse::AudioOutputPulse(std::string devID){
isPlaying=false;
isConnected=false;
didStart=false;
isLocked=false;
mainloop=NULL;
mainloopApi=NULL;
context=NULL;
stream=NULL;
remainingDataSize=0;
if(!PulseAudioLoader::IncRef()){
failed=true;
return;
}
mainloop=pa_threaded_mainloop_new();
if(!mainloop){
LOGE("Error initializing PulseAudio (pa_threaded_mainloop_new)");
failed=true;
return;
}
mainloopApi=pa_threaded_mainloop_get_api(mainloop);
char exePath[MAXPATHLEN];
char exeName[MAXPATHLEN];
ssize_t lres=readlink("/proc/self/exe", exePath, sizeof(exePath));
if(lres==-1)
lres=readlink("/proc/curproc/file", exePath, sizeof(exePath));
if(lres==-1)
lres=readlink("/proc/curproc/exe", exePath, sizeof(exePath));
if(lres>0){
strcpy(exeName, basename(exePath));
}else{
snprintf(exeName, sizeof(exeName), "Process %d", getpid());
}
context=pa_context_new(mainloopApi, exeName);
if(!context){
LOGE("Error initializing PulseAudio (pa_context_new)");
failed=true;
return;
}
pa_context_set_state_callback(context, AudioOutputPulse::ContextStateCallback, this);
pa_threaded_mainloop_lock(mainloop);
isLocked=true;
int err=pa_threaded_mainloop_start(mainloop);
CHECK_ERROR(err, "pa_threaded_mainloop_start");
didStart=true;
err=pa_context_connect(context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL);
CHECK_ERROR(err, "pa_context_connect");
while(true){
pa_context_state_t contextState=pa_context_get_state(context);
if(!PA_CONTEXT_IS_GOOD(contextState)){
LOGE("Error initializing PulseAudio (PA_CONTEXT_IS_GOOD)");
failed=true;
return;
}
if(contextState==PA_CONTEXT_READY)
break;
pa_threaded_mainloop_wait(mainloop);
}
pa_sample_spec sample_specifications{
.format=PA_SAMPLE_S16LE,
.rate=48000,
.channels=1
};
stream=pa_stream_new(context, "libtgvoip playback", &sample_specifications, NULL);
if(!stream){
LOGE("Error initializing PulseAudio (pa_stream_new)");
failed=true;
return;
}
pa_stream_set_state_callback(stream, AudioOutputPulse::StreamStateCallback, this);
pa_stream_set_write_callback(stream, AudioOutputPulse::StreamWriteCallback, this);
pa_threaded_mainloop_unlock(mainloop);
isLocked=false;
SetCurrentDevice(devID);
}
AudioOutputPulse::~AudioOutputPulse(){
if(mainloop && didStart){
if(isLocked)
pa_threaded_mainloop_unlock(mainloop);
pa_threaded_mainloop_stop(mainloop);
}
if(stream){
pa_stream_disconnect(stream);
pa_stream_unref(stream);
}
if(context){
pa_context_disconnect(context);
pa_context_unref(context);
}
if(mainloop)
pa_threaded_mainloop_free(mainloop);
PulseAudioLoader::DecRef();
}
bool AudioOutputPulse::IsAvailable(){
void* lib=dlopen("libpulse.so.0", RTLD_LAZY);
if(!lib)
lib=dlopen("libpulse.so", RTLD_LAZY);
if(lib){
dlclose(lib);
return true;
}
return false;
}
void AudioOutputPulse::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
}
void AudioOutputPulse::Start(){
if(failed || isPlaying)
return;
isPlaying=true;
pa_operation_unref(pa_stream_cork(stream, 0, AudioOutputPulse::StreamSuccessCallback, mainloop));
}
void AudioOutputPulse::Stop(){
if(!isPlaying)
return;
isPlaying=false;
pa_operation_unref(pa_stream_cork(stream, 1, AudioOutputPulse::StreamSuccessCallback, mainloop));
}
bool AudioOutputPulse::IsPlaying(){
return isPlaying;
}
void AudioOutputPulse::SetCurrentDevice(std::string devID){
currentDevice=devID;
if(isPlaying && isConnected){
pa_stream_disconnect(stream);
isConnected=false;
}
pa_buffer_attr bufferAttr={
.maxlength=960*6,
.tlength=960*6,
.prebuf=0,
.minreq=960*2
};
int streamFlags=PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING |
PA_STREAM_NOT_MONOTONIC | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_ADJUST_LATENCY;
int err=pa_stream_connect_playback(stream, devID=="default" ? NULL : devID.c_str(), &bufferAttr, (pa_stream_flags_t)streamFlags, NULL, NULL);
if(err!=0 && devID!="default"){
SetCurrentDevice("default");
return;
}
CHECK_ERROR(err, "pa_stream_connect_playback");
while(true){
pa_threaded_mainloop_lock(mainloop);
pa_stream_state_t streamState=pa_stream_get_state(stream);
pa_threaded_mainloop_unlock(mainloop);
if(!PA_STREAM_IS_GOOD(streamState)){
LOGE("Error connecting to audio device '%s'", devID.c_str());
failed=true;
return;
}
if(streamState==PA_STREAM_READY)
break;
pa_threaded_mainloop_wait(mainloop);
}
isConnected=true;
if(isPlaying){
pa_operation_unref(pa_stream_cork(stream, 0, AudioOutputPulse::StreamSuccessCallback, mainloop));
}
}
bool AudioOutputPulse::EnumerateDevices(std::vector<AudioOutputDevice>& devs){
if(!PulseAudioLoader::IncRef())
return false;
pa_mainloop* ml;
pa_mainloop_api* mlAPI;
pa_context* ctx;
pa_operation* op=NULL;
int state=0;
int paReady=0;
ml=pa_mainloop_new();
mlAPI=pa_mainloop_get_api(ml);
ctx=pa_context_new(mlAPI, "libtgvoip");
pa_context_connect(ctx, NULL, PA_CONTEXT_NOFLAGS, NULL);
pa_context_set_state_callback(ctx, AudioOutputPulse::ContextStateCallbackEnum, &paReady);
while(true){
if(paReady==0){
pa_mainloop_iterate(ml, 1, NULL);
continue;
}
if(paReady==2){
pa_context_disconnect(ctx);
pa_context_unref(ctx);
pa_mainloop_free(ml);
PulseAudioLoader::DecRef();
return false;
}
if(!op){
op=pa_context_get_sink_info_list(ctx, AudioOutputPulse::DeviceEnumCallback, &devs);
continue;
}
if(pa_operation_get_state(op)==PA_OPERATION_DONE){
pa_operation_unref(op);
pa_context_disconnect(ctx);
pa_context_unref(ctx);
pa_mainloop_free(ml);
PulseAudioLoader::DecRef();
return true;
}
pa_mainloop_iterate(ml, 1, NULL);
}
}
void AudioOutputPulse::ContextStateCallback(pa_context* context, void* arg) {
AudioOutputPulse* self=(AudioOutputPulse*) arg;
pa_threaded_mainloop_signal(self->mainloop, 0);
}
void AudioOutputPulse::StreamStateCallback(pa_stream *s, void* arg) {
AudioOutputPulse* self=(AudioOutputPulse*) arg;
pa_threaded_mainloop_signal(self->mainloop, 0);
}
void AudioOutputPulse::StreamWriteCallback(pa_stream *stream, size_t requestedBytes, void *userdata){
((AudioOutputPulse*)userdata)->StreamWriteCallback(stream, requestedBytes);
}
void AudioOutputPulse::StreamWriteCallback(pa_stream *stream, size_t requestedBytes) {
int bytesRemaining = requestedBytes;
uint8_t *buffer = NULL;
while (bytesRemaining > 0) {
size_t bytesToFill = 102400;
size_t i;
if (bytesToFill > bytesRemaining) bytesToFill = bytesRemaining;
int err=pa_stream_begin_write(stream, (void**) &buffer, &bytesToFill);
CHECK_ERROR(err, "pa_stream_begin_write");
if(isPlaying){
while(remainingDataSize<bytesToFill){
if(remainingDataSize+960*2>=sizeof(remainingData)){
LOGE("Can't provide %d bytes of audio data at a time", (int)bytesToFill);
failed=true;
pa_threaded_mainloop_unlock(mainloop);
return;
}
InvokeCallback(remainingData+remainingDataSize, 960*2);
remainingDataSize+=960*2;
}
memcpy(buffer, remainingData, bytesToFill);
memmove(remainingData, remainingData+bytesToFill, remainingDataSize-bytesToFill);
remainingDataSize-=bytesToFill;
}else{
memset(buffer, 0, bytesToFill);
}
err=pa_stream_write(stream, buffer, bytesToFill, NULL, 0LL, PA_SEEK_RELATIVE);
CHECK_ERROR(err, "pa_stream_write");
bytesRemaining -= bytesToFill;
}
}
void AudioOutputPulse::StreamSuccessCallback(pa_stream *stream, int success, void *userdata) {
return;
}
void AudioOutputPulse::ContextStateCallbackEnum(pa_context* context, void* arg){
pa_context_state_t state;
int* pa_ready=(int*)arg;
state=pa_context_get_state(context);
switch(state){
case PA_CONTEXT_UNCONNECTED:
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
default:
break;
case PA_CONTEXT_FAILED:
case PA_CONTEXT_TERMINATED:
*pa_ready=2;
break;
case PA_CONTEXT_READY:
*pa_ready=1;
break;
}
}
void AudioOutputPulse::DeviceEnumCallback(pa_context* ctx, const pa_sink_info* info, int eol, void* userdata){
if(eol>0)
return;
std::vector<AudioOutputDevice>* devs=(std::vector<AudioOutputDevice>*)userdata;
AudioOutputDevice dev;
dev.id=std::string(info->name);
dev.displayName=std::string(info->description);
devs->push_back(dev);
}

View File

@ -0,0 +1,54 @@
//
// 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 LIBTGVOIP_AUDIOOUTPUTPULSE_H
#define LIBTGVOIP_AUDIOOUTPUTPULSE_H
#include "../../audio/AudioOutput.h"
#include "../../threading.h"
#include <pulse/pulseaudio.h>
namespace tgvoip{
namespace audio{
class AudioOutputPulse : public AudioOutput{
public:
AudioOutputPulse(std::string devID);
virtual ~AudioOutputPulse();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
virtual void Start();
virtual void Stop();
virtual bool IsPlaying();
virtual void SetCurrentDevice(std::string devID);
static bool EnumerateDevices(std::vector<AudioOutputDevice>& devs);
static bool IsAvailable();
private:
static void ContextStateCallback(pa_context* context, void* arg);
static void ContextStateCallbackEnum(pa_context* context, void* arg);
static void StreamStateCallback(pa_stream* s, void* arg);
static void StreamSuccessCallback(pa_stream* stream, int success, void* userdata);
static void StreamWriteCallback(pa_stream* stream, size_t requested_bytes, void* userdata);
static void DeviceEnumCallback(pa_context* ctx, const pa_sink_info* info, int eol, void* userdata);
void StreamWriteCallback(pa_stream* stream, size_t requestedBytes);
pa_threaded_mainloop* mainloop;
pa_mainloop_api* mainloopApi;
pa_context* context;
pa_stream* stream;
bool isPlaying;
bool isConnected;
bool didStart;
bool isLocked;
unsigned char remainingData[960*8*2];
size_t remainingDataSize;
};
}
}
#endif //LIBTGVOIP_AUDIOOUTPUTPULSE_H

View File

@ -0,0 +1,120 @@
//
// 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 "PulseAudioLoader.h"
#include <dlfcn.h>
#include "../../logging.h"
#define DECLARE_DL_FUNCTION(name) typeof(name)* PulseAudioLoader::_import_##name=NULL
#define CHECK_DL_ERROR(res, msg) if(!res){LOGE(msg ": %s", dlerror()); dlclose(lib); return false;}
#define LOAD_DL_FUNCTION(name) {_import_##name=(typeof(_import_##name))dlsym(lib, #name); CHECK_DL_ERROR(_import_##name, "Error getting entry point for " #name);}
using namespace tgvoip;
int PulseAudioLoader::refCount=0;
void* PulseAudioLoader::lib=NULL;
DECLARE_DL_FUNCTION(pa_threaded_mainloop_new);
DECLARE_DL_FUNCTION(pa_threaded_mainloop_get_api);
DECLARE_DL_FUNCTION(pa_context_new);
DECLARE_DL_FUNCTION(pa_context_set_state_callback);
DECLARE_DL_FUNCTION(pa_threaded_mainloop_lock);
DECLARE_DL_FUNCTION(pa_threaded_mainloop_unlock);
DECLARE_DL_FUNCTION(pa_threaded_mainloop_start);
DECLARE_DL_FUNCTION(pa_context_connect);
DECLARE_DL_FUNCTION(pa_context_get_state);
DECLARE_DL_FUNCTION(pa_threaded_mainloop_wait);
DECLARE_DL_FUNCTION(pa_stream_new);
DECLARE_DL_FUNCTION(pa_stream_set_state_callback);
DECLARE_DL_FUNCTION(pa_stream_set_write_callback);
DECLARE_DL_FUNCTION(pa_stream_connect_playback);
DECLARE_DL_FUNCTION(pa_operation_unref);
DECLARE_DL_FUNCTION(pa_stream_cork);
DECLARE_DL_FUNCTION(pa_threaded_mainloop_stop);
DECLARE_DL_FUNCTION(pa_stream_disconnect);
DECLARE_DL_FUNCTION(pa_stream_unref);
DECLARE_DL_FUNCTION(pa_context_disconnect);
DECLARE_DL_FUNCTION(pa_context_unref);
DECLARE_DL_FUNCTION(pa_threaded_mainloop_free);
DECLARE_DL_FUNCTION(pa_threaded_mainloop_signal);
DECLARE_DL_FUNCTION(pa_stream_begin_write);
DECLARE_DL_FUNCTION(pa_stream_write);
DECLARE_DL_FUNCTION(pa_stream_get_state);
DECLARE_DL_FUNCTION(pa_strerror);
DECLARE_DL_FUNCTION(pa_stream_set_read_callback);
DECLARE_DL_FUNCTION(pa_stream_connect_record);
DECLARE_DL_FUNCTION(pa_stream_peek);
DECLARE_DL_FUNCTION(pa_stream_drop);
DECLARE_DL_FUNCTION(pa_mainloop_new);
DECLARE_DL_FUNCTION(pa_mainloop_get_api);
DECLARE_DL_FUNCTION(pa_mainloop_iterate);
DECLARE_DL_FUNCTION(pa_mainloop_free);
DECLARE_DL_FUNCTION(pa_context_get_sink_info_list);
DECLARE_DL_FUNCTION(pa_context_get_source_info_list);
DECLARE_DL_FUNCTION(pa_operation_get_state);
bool PulseAudioLoader::IncRef(){
if(refCount==0){
lib=dlopen("libpulse.so.0", RTLD_LAZY);
if(!lib)
lib=dlopen("libpulse.so", RTLD_LAZY);
if(!lib){
LOGE("Error loading libpulse: %s", dlerror());
return false;
}
}
LOAD_DL_FUNCTION(pa_threaded_mainloop_new);
LOAD_DL_FUNCTION(pa_threaded_mainloop_get_api);
LOAD_DL_FUNCTION(pa_context_new);
LOAD_DL_FUNCTION(pa_context_set_state_callback);
LOAD_DL_FUNCTION(pa_threaded_mainloop_lock);
LOAD_DL_FUNCTION(pa_threaded_mainloop_unlock);
LOAD_DL_FUNCTION(pa_threaded_mainloop_start);
LOAD_DL_FUNCTION(pa_context_connect);
LOAD_DL_FUNCTION(pa_context_get_state);
LOAD_DL_FUNCTION(pa_threaded_mainloop_wait);
LOAD_DL_FUNCTION(pa_stream_new);
LOAD_DL_FUNCTION(pa_stream_set_state_callback);
LOAD_DL_FUNCTION(pa_stream_set_write_callback);
LOAD_DL_FUNCTION(pa_stream_connect_playback);
LOAD_DL_FUNCTION(pa_operation_unref);
LOAD_DL_FUNCTION(pa_stream_cork);
LOAD_DL_FUNCTION(pa_threaded_mainloop_stop);
LOAD_DL_FUNCTION(pa_stream_disconnect);
LOAD_DL_FUNCTION(pa_stream_unref);
LOAD_DL_FUNCTION(pa_context_disconnect);
LOAD_DL_FUNCTION(pa_context_unref);
LOAD_DL_FUNCTION(pa_threaded_mainloop_free);
LOAD_DL_FUNCTION(pa_threaded_mainloop_signal);
LOAD_DL_FUNCTION(pa_stream_begin_write);
LOAD_DL_FUNCTION(pa_stream_write);
LOAD_DL_FUNCTION(pa_stream_get_state);
LOAD_DL_FUNCTION(pa_strerror);
LOAD_DL_FUNCTION(pa_stream_set_read_callback);
LOAD_DL_FUNCTION(pa_stream_connect_record);
LOAD_DL_FUNCTION(pa_stream_peek);
LOAD_DL_FUNCTION(pa_stream_drop);
LOAD_DL_FUNCTION(pa_mainloop_new);
LOAD_DL_FUNCTION(pa_mainloop_get_api);
LOAD_DL_FUNCTION(pa_mainloop_iterate);
LOAD_DL_FUNCTION(pa_mainloop_free);
LOAD_DL_FUNCTION(pa_context_get_sink_info_list);
LOAD_DL_FUNCTION(pa_context_get_source_info_list);
LOAD_DL_FUNCTION(pa_operation_get_state);
refCount++;
return true;
}
void PulseAudioLoader::DecRef(){
if(refCount>0)
refCount--;
if(refCount==0){
dlclose(lib);
lib=NULL;
}
}

View File

@ -0,0 +1,109 @@
//
// 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 LIBTGVOIP_PULSEAUDIOLOADER_H
#define LIBTGVOIP_PULSEAUDIOLOADER_H
#include <pulse/pulseaudio.h>
#define DECLARE_DL_FUNCTION(name) static typeof(name)* _import_##name
namespace tgvoip{
class PulseAudioLoader{
public:
static bool IncRef();
static void DecRef();
DECLARE_DL_FUNCTION(pa_threaded_mainloop_new);
DECLARE_DL_FUNCTION(pa_threaded_mainloop_get_api);
DECLARE_DL_FUNCTION(pa_context_new);
DECLARE_DL_FUNCTION(pa_context_set_state_callback);
DECLARE_DL_FUNCTION(pa_threaded_mainloop_lock);
DECLARE_DL_FUNCTION(pa_threaded_mainloop_unlock);
DECLARE_DL_FUNCTION(pa_threaded_mainloop_start);
DECLARE_DL_FUNCTION(pa_context_connect);
DECLARE_DL_FUNCTION(pa_context_get_state);
DECLARE_DL_FUNCTION(pa_threaded_mainloop_wait);
DECLARE_DL_FUNCTION(pa_stream_new);
DECLARE_DL_FUNCTION(pa_stream_set_state_callback);
DECLARE_DL_FUNCTION(pa_stream_set_write_callback);
DECLARE_DL_FUNCTION(pa_stream_connect_playback);
DECLARE_DL_FUNCTION(pa_operation_unref);
DECLARE_DL_FUNCTION(pa_stream_cork);
DECLARE_DL_FUNCTION(pa_threaded_mainloop_stop);
DECLARE_DL_FUNCTION(pa_stream_disconnect);
DECLARE_DL_FUNCTION(pa_stream_unref);
DECLARE_DL_FUNCTION(pa_context_disconnect);
DECLARE_DL_FUNCTION(pa_context_unref);
DECLARE_DL_FUNCTION(pa_threaded_mainloop_free);
DECLARE_DL_FUNCTION(pa_threaded_mainloop_signal);
DECLARE_DL_FUNCTION(pa_stream_begin_write);
DECLARE_DL_FUNCTION(pa_stream_write);
DECLARE_DL_FUNCTION(pa_stream_get_state);
DECLARE_DL_FUNCTION(pa_strerror);
DECLARE_DL_FUNCTION(pa_stream_set_read_callback);
DECLARE_DL_FUNCTION(pa_stream_connect_record);
DECLARE_DL_FUNCTION(pa_stream_peek);
DECLARE_DL_FUNCTION(pa_stream_drop);
DECLARE_DL_FUNCTION(pa_mainloop_new);
DECLARE_DL_FUNCTION(pa_mainloop_get_api);
DECLARE_DL_FUNCTION(pa_mainloop_iterate);
DECLARE_DL_FUNCTION(pa_mainloop_free);
DECLARE_DL_FUNCTION(pa_context_get_sink_info_list);
DECLARE_DL_FUNCTION(pa_context_get_source_info_list);
DECLARE_DL_FUNCTION(pa_operation_get_state);
private:
static void* lib;
static int refCount;
};
}
#undef DECLARE_DL_FUNCTION
#ifdef TGVOIP_IN_AUDIO_IO
#define pa_threaded_mainloop_new PulseAudioLoader::_import_pa_threaded_mainloop_new
#define pa_threaded_mainloop_get_api PulseAudioLoader::_import_pa_threaded_mainloop_get_api
#define pa_context_new PulseAudioLoader::_import_pa_context_new
#define pa_context_set_state_callback PulseAudioLoader::_import_pa_context_set_state_callback
#define pa_threaded_mainloop_lock PulseAudioLoader::_import_pa_threaded_mainloop_lock
#define pa_threaded_mainloop_unlock PulseAudioLoader::_import_pa_threaded_mainloop_unlock
#define pa_threaded_mainloop_start PulseAudioLoader::_import_pa_threaded_mainloop_start
#define pa_context_connect PulseAudioLoader::_import_pa_context_connect
#define pa_context_get_state PulseAudioLoader::_import_pa_context_get_state
#define pa_threaded_mainloop_wait PulseAudioLoader::_import_pa_threaded_mainloop_wait
#define pa_stream_new PulseAudioLoader::_import_pa_stream_new
#define pa_stream_set_state_callback PulseAudioLoader::_import_pa_stream_set_state_callback
#define pa_stream_set_write_callback PulseAudioLoader::_import_pa_stream_set_write_callback
#define pa_stream_connect_playback PulseAudioLoader::_import_pa_stream_connect_playback
#define pa_operation_unref PulseAudioLoader::_import_pa_operation_unref
#define pa_stream_cork PulseAudioLoader::_import_pa_stream_cork
#define pa_threaded_mainloop_stop PulseAudioLoader::_import_pa_threaded_mainloop_stop
#define pa_stream_disconnect PulseAudioLoader::_import_pa_stream_disconnect
#define pa_stream_unref PulseAudioLoader::_import_pa_stream_unref
#define pa_context_disconnect PulseAudioLoader::_import_pa_context_disconnect
#define pa_context_unref PulseAudioLoader::_import_pa_context_unref
#define pa_threaded_mainloop_free PulseAudioLoader::_import_pa_threaded_mainloop_free
#define pa_threaded_mainloop_signal PulseAudioLoader::_import_pa_threaded_mainloop_signal
#define pa_stream_begin_write PulseAudioLoader::_import_pa_stream_begin_write
#define pa_stream_write PulseAudioLoader::_import_pa_stream_write
#define pa_strerror PulseAudioLoader::_import_pa_strerror
#define pa_stream_get_state PulseAudioLoader::_import_pa_stream_get_state
#define pa_stream_set_read_callback PulseAudioLoader::_import_pa_stream_set_read_callback
#define pa_stream_connect_record PulseAudioLoader::_import_pa_stream_connect_record
#define pa_stream_peek PulseAudioLoader::_import_pa_stream_peek
#define pa_stream_drop PulseAudioLoader::_import_pa_stream_drop
#define pa_mainloop_new PulseAudioLoader::_import_pa_mainloop_new
#define pa_mainloop_get_api PulseAudioLoader::_import_pa_mainloop_get_api
#define pa_mainloop_iterate PulseAudioLoader::_import_pa_mainloop_iterate
#define pa_mainloop_free PulseAudioLoader::_import_pa_mainloop_free
#define pa_context_get_sink_info_list PulseAudioLoader::_import_pa_context_get_sink_info_list
#define pa_context_get_source_info_list PulseAudioLoader::_import_pa_context_get_source_info_list
#define pa_operation_get_state PulseAudioLoader::_import_pa_operation_get_state
#endif
#endif // LIBTGVOIP_PULSEAUDIOLOADER_H

View File

@ -0,0 +1,522 @@
//
// 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 "NetworkSocketPosix.h"
#include <sys/socket.h>
#include <errno.h>
#include <assert.h>
#include <netdb.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <netinet/tcp.h>
#include "../../logging.h"
#include "../../VoIPController.h"
#include "../../BufferInputStream.h"
#include "../../BufferOutputStream.h"
using namespace tgvoip;
NetworkSocketPosix::NetworkSocketPosix(NetworkProtocol protocol) : NetworkSocket(protocol), lastRecvdV4(0), lastRecvdV6("::0"){
needUpdateNat64Prefix=true;
nat64Present=false;
switchToV6at=0;
isV4Available=false;
fd=-1;
useTCP=false;
closing=false;
tcpConnectedAddress=NULL;
tcpConnectedPort=0;
}
NetworkSocketPosix::~NetworkSocketPosix(){
if(tcpConnectedAddress)
delete tcpConnectedAddress;
}
void NetworkSocketPosix::SetMaxPriority(){
#ifdef __APPLE__
int prio=NET_SERVICE_TYPE_VO;
int res=setsockopt(fd, SOL_SOCKET, SO_NET_SERVICE_TYPE, &prio, sizeof(prio));
if(res<0){
LOGE("error setting darwin-specific net priority: %d / %s", errno, strerror(errno));
}
#else
int prio=5;
int res=setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio));
if(res<0){
LOGE("error setting priority: %d / %s", errno, strerror(errno));
}
prio=6 << 5;
res=setsockopt(fd, SOL_IP, IP_TOS, &prio, sizeof(prio));
if(res<0){
LOGE("error setting ip tos: %d / %s", errno, strerror(errno));
}
#endif
}
void NetworkSocketPosix::Send(NetworkPacket *packet){
if(!packet || !packet->address){
LOGW("tried to send null packet");
return;
}
int res;
if(protocol==PROTO_UDP){
sockaddr_in6 addr;
IPv4Address *v4addr=dynamic_cast<IPv4Address *>(packet->address);
if(v4addr){
if(needUpdateNat64Prefix && !isV4Available && VoIPController::GetCurrentTime()>switchToV6at && switchToV6at!=0){
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;
}
memset(&addr, 0, sizeof(sockaddr_in6));
addr.sin6_family=AF_INET6;
*((uint32_t *) &addr.sin6_addr.s6_addr[12])=v4addr->GetAddress();
if(nat64Present)
memcpy(addr.sin6_addr.s6_addr, nat64Prefix, 12);
else
addr.sin6_addr.s6_addr[11]=addr.sin6_addr.s6_addr[10]=0xFF;
}else{
IPv6Address *v6addr=dynamic_cast<IPv6Address *>(packet->address);
assert(v6addr!=NULL);
memcpy(addr.sin6_addr.s6_addr, v6addr->GetAddress(), 16);
}
addr.sin6_port=htons(packet->port);
res=sendto(fd, packet->data, packet->length, 0, (const sockaddr *) &addr, sizeof(addr));
}else{
res=send(fd, packet->data, packet->length, 0);
}
if(res<0){
LOGE("error sending: %d / %s", errno, strerror(errno));
if(errno==ENETUNREACH && !isV4Available && VoIPController::GetCurrentTime()<switchToV6at){
switchToV6at=VoIPController::GetCurrentTime();
LOGI("Network unreachable, trying NAT64");
}
}
}
void NetworkSocketPosix::Receive(NetworkPacket *packet){
if(protocol==PROTO_UDP){
int addrLen=sizeof(sockaddr_in6);
sockaddr_in6 srcAddr;
ssize_t len=recvfrom(fd, packet->data, packet->length, 0, (sockaddr *) &srcAddr, (socklen_t *) &addrLen);
if(len>0)
packet->length=(size_t) len;
else{
LOGE("error receiving %d / %s", errno, strerror(errno));
packet->length=0;
return;
}
//LOGV("Received %d bytes from %s:%d at %.5lf", len, inet_ntoa(srcAddr.sin_addr), ntohs(srcAddr.sin_port), GetCurrentTime());
if(!isV4Available && IN6_IS_ADDR_V4MAPPED(&srcAddr.sin6_addr)){
isV4Available=true;
LOGI("Detected IPv4 connectivity, will not try IPv6");
}
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]);
lastRecvdV4=IPv4Address(v4addr.s_addr);
packet->address=&lastRecvdV4;
}else{
lastRecvdV6=IPv6Address(srcAddr.sin6_addr.s6_addr);
packet->address=&lastRecvdV6;
}
packet->protocol=PROTO_UDP;
packet->port=ntohs(srcAddr.sin6_port);
}else if(protocol==PROTO_TCP){
int res=recv(fd, packet->data, packet->length, 0);
if(res<=0){
LOGE("Error receiving from TCP socket: %d / %s", errno, strerror(errno));
failed=true;
}else{
packet->length=(size_t)res;
packet->address=tcpConnectedAddress;
packet->port=tcpConnectedPort;
packet->protocol=PROTO_TCP;
}
}
}
void NetworkSocketPosix::Open(){
if(protocol!=PROTO_UDP)
return;
fd=socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if(fd<0){
LOGE("error creating socket: %d / %s", errno, strerror(errno));
failed=true;
return;
}
int flag=0;
int res=setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag));
if(res<0){
LOGE("error enabling dual stack socket: %d / %s", errno, strerror(errno));
failed=true;
return;
}
SetMaxPriority();
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(GenerateLocalPort());
res=::bind(fd, (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;
}
}
if(tries==10){
addr.sin6_port=0;
res=::bind(fd, (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);
failed=true;
return;
}
}
size_t addrLen=sizeof(sockaddr_in6);
getsockname(fd, (sockaddr*)&addr, (socklen_t*) &addrLen);
uint16_t localUdpPort=ntohs(addr.sin6_port);
LOGD("Bound to local UDP port %u", ntohs(addr.sin6_port));
needUpdateNat64Prefix=true;
isV4Available=false;
switchToV6at=VoIPController::GetCurrentTime()+ipv6Timeout;
}
void NetworkSocketPosix::Close(){
closing=true;
failed=true;
if (fd>=0) {
shutdown(fd, SHUT_RDWR);
close(fd);
}
}
void NetworkSocketPosix::Connect(NetworkAddress *address, uint16_t port){
IPv4Address* v4addr=dynamic_cast<IPv4Address*>(address);
IPv6Address* v6addr=dynamic_cast<IPv6Address*>(address);
sockaddr_in v4;
sockaddr_in6 v6;
sockaddr* addr=NULL;
size_t addrLen=0;
if(v4addr){
v4.sin_family=AF_INET;
v4.sin_addr.s_addr=v4addr->GetAddress();
v4.sin_port=htons(port);
addr=reinterpret_cast<sockaddr*>(&v4);
addrLen=sizeof(v4);
}else if(v6addr){
v6.sin6_family=AF_INET6;
memcpy(v6.sin6_addr.s6_addr, v6addr->GetAddress(), 16);
v6.sin6_flowinfo=0;
v6.sin6_scope_id=0;
v6.sin6_port=htons(port);
addr=reinterpret_cast<sockaddr*>(&v6);
addrLen=sizeof(v6);
}else{
LOGE("Unknown address type in TCP connect");
failed=true;
return;
}
fd=socket(addr->sa_family, SOCK_STREAM, IPPROTO_TCP);
if(fd<0){
LOGE("Error creating TCP socket: %d / %s", errno, strerror(errno));
failed=true;
return;
}
int opt=1;
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
timeval timeout;
timeout.tv_sec=5;
timeout.tv_usec=0;
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
timeout.tv_sec=60;
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
int res=connect(fd, (const sockaddr*) addr, addrLen);
if(res!=0){
LOGW("error connecting TCP socket to %s:%u: %d / %s; %d / %s", address->ToString().c_str(), port, res, strerror(res), errno, strerror(errno));
close(fd);
failed=true;
return;
}
tcpConnectedAddress=v4addr ? (NetworkAddress*)new IPv4Address(*v4addr) : (NetworkAddress*)new IPv6Address(*v6addr);
tcpConnectedPort=port;
LOGI("successfully connected to %s:%d", tcpConnectedAddress->ToString().c_str(), tcpConnectedPort);
}
void NetworkSocketPosix::OnActiveInterfaceChanged(){
needUpdateNat64Prefix=true;
isV4Available=false;
switchToV6at=VoIPController::GetCurrentTime()+ipv6Timeout;
}
std::string NetworkSocketPosix::GetLocalInterfaceInfo(IPv4Address *v4addr, IPv6Address *v6addr){
struct ifconf ifc;
struct ifreq* ifr;
char buf[16384];
int sd;
std::string name="";
sd=socket(PF_INET, SOCK_DGRAM, 0);
if(sd>0){
ifc.ifc_len=sizeof(buf);
ifc.ifc_ifcu.ifcu_buf=buf;
if(ioctl(sd, SIOCGIFCONF, &ifc)==0){
ifr=ifc.ifc_req;
int len;
int i;
for(i=0;i<ifc.ifc_len;){
#ifndef __linux__
len=IFNAMSIZ + ifr->ifr_addr.sa_len;
#else
len=sizeof(*ifr);
#endif
if(ifr->ifr_addr.sa_family==AF_INET){
if(ioctl(sd, SIOCGIFADDR, ifr)==0){
struct sockaddr_in* addr=(struct sockaddr_in *)(&ifr->ifr_addr);
LOGI("Interface %s, address %s\n", ifr->ifr_name, inet_ntoa(addr->sin_addr));
if(ioctl(sd, SIOCGIFFLAGS, ifr)==0){
if(!(ifr->ifr_flags & IFF_LOOPBACK) && (ifr->ifr_flags & IFF_UP) && (ifr->ifr_flags & IFF_RUNNING)){
//LOGV("flags = %08X", ifr->ifr_flags);
if((ntohl(addr->sin_addr.s_addr) & 0xFFFF0000)==0xA9FE0000){
LOGV("skipping link-local");
continue;
}
if(v4addr){
*v4addr=IPv4Address(addr->sin_addr.s_addr);
}
name=ifr->ifr_name;
}
}
}else{
LOGE("Error getting address for %s: %d\n", ifr->ifr_name, errno);
}
}
ifr=(struct ifreq*)((char*)ifr+len);
i+=len;
}
}else{
LOGE("Error getting LAN address: %d", errno);
}
}
close(sd);
return name;
}
uint16_t NetworkSocketPosix::GetLocalPort(){
sockaddr_in6 addr;
size_t addrLen=sizeof(sockaddr_in6);
getsockname(fd, (sockaddr*)&addr, (socklen_t*) &addrLen);
return ntohs(addr.sin6_port);
}
std::string NetworkSocketPosix::V4AddressToString(uint32_t address){
char buf[INET_ADDRSTRLEN];
in_addr addr;
addr.s_addr=address;
inet_ntop(AF_INET, &addr, buf, sizeof(buf));
return std::string(buf);
}
std::string NetworkSocketPosix::V6AddressToString(unsigned char *address){
char buf[INET6_ADDRSTRLEN];
in6_addr addr;
memcpy(addr.s6_addr, address, 16);
inet_ntop(AF_INET6, &addr, buf, sizeof(buf));
return std::string(buf);
}
uint32_t NetworkSocketPosix::StringToV4Address(std::string address){
in_addr addr;
inet_pton(AF_INET, address.c_str(), &addr);
return addr.s_addr;
}
void NetworkSocketPosix::StringToV6Address(std::string address, unsigned char *out){
in6_addr addr;
inet_pton(AF_INET6, address.c_str(), &addr);
memcpy(out, addr.s6_addr, 16);
}
IPv4Address *NetworkSocketPosix::ResolveDomainName(std::string name){
addrinfo* addr0;
IPv4Address* ret=NULL;
int res=getaddrinfo(name.c_str(), NULL, NULL, &addr0);
if(res!=0){
LOGW("Error updating NAT64 prefix: %d / %s", res, gai_strerror(res));
}else{
addrinfo* addrPtr;
for(addrPtr=addr0;addrPtr;addrPtr=addrPtr->ai_next){
if(addrPtr->ai_family==AF_INET){
sockaddr_in* addr=(sockaddr_in*)addrPtr->ai_addr;
ret=new IPv4Address(addr->sin_addr.s_addr);
break;
}
}
freeaddrinfo(addr0);
}
return ret;
}
NetworkAddress *NetworkSocketPosix::GetConnectedAddress(){
return tcpConnectedAddress;
}
uint16_t NetworkSocketPosix::GetConnectedPort(){
return tcpConnectedPort;
}
void NetworkSocketPosix::SetTimeouts(int sendTimeout, int recvTimeout){
timeval timeout;
timeout.tv_sec=sendTimeout;
timeout.tv_usec=0;
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
timeout.tv_sec=recvTimeout;
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
}
bool NetworkSocketPosix::Select(std::vector<NetworkSocket *> &readFds, std::vector<NetworkSocket *> &errorFds, SocketSelectCanceller* _canceller){
fd_set readSet;
fd_set errorSet;
FD_ZERO(&readSet);
FD_ZERO(&errorSet);
SocketSelectCancellerPosix* canceller=dynamic_cast<SocketSelectCancellerPosix*>(_canceller);
if(canceller)
FD_SET(canceller->pipeRead, &readSet);
int maxfd=canceller ? canceller->pipeRead : 0;
for(std::vector<NetworkSocket*>::iterator itr=readFds.begin();itr!=readFds.end();++itr){
int sfd=GetDescriptorFromSocket(*itr);
if(sfd==0){
LOGW("can't select on one of sockets because it's not a NetworkSocketPosix instance");
continue;
}
FD_SET(sfd, &readSet);
if(maxfd<sfd)
maxfd=sfd;
}
bool anyFailed=false;
for(std::vector<NetworkSocket*>::iterator itr=errorFds.begin();itr!=errorFds.end();++itr){
int sfd=GetDescriptorFromSocket(*itr);
if(sfd==0){
LOGW("can't select on one of sockets because it's not a NetworkSocketPosix instance");
continue;
}
anyFailed |= (*itr)->IsFailed();
FD_SET(sfd, &errorSet);
if(maxfd<sfd)
maxfd=sfd;
}
int res=select(maxfd+1, &readSet, NULL, &errorSet, NULL);
if(canceller && FD_ISSET(canceller->pipeRead, &readSet) && !anyFailed){
char c;
read(canceller->pipeRead, &c, 1);
return false;
}else if(anyFailed){
FD_ZERO(&readSet);
FD_ZERO(&errorSet);
}
std::vector<NetworkSocket*>::iterator itr=readFds.begin();
while(itr!=readFds.end()){
int sfd=GetDescriptorFromSocket(*itr);
if(sfd==0 || !FD_ISSET(sfd, &readSet)){
itr=readFds.erase(itr);
}else{
++itr;
}
}
itr=errorFds.begin();
while(itr!=errorFds.end()){
int sfd=GetDescriptorFromSocket(*itr);
if((sfd==0 || !FD_ISSET(sfd, &errorSet)) && !(*itr)->IsFailed()){
itr=errorFds.erase(itr);
}else{
++itr;
}
}
//LOGV("select fds left: read=%d, error=%d", readFds.size(), errorFds.size());
return readFds.size()>0 || errorFds.size()>0;
}
SocketSelectCancellerPosix::SocketSelectCancellerPosix(){
int p[2];
int pipeRes=pipe(p);
assert(pipeRes==0);
pipeRead=p[0];
pipeWrite=p[1];
}
SocketSelectCancellerPosix::~SocketSelectCancellerPosix(){
close(pipeRead);
close(pipeWrite);
}
void SocketSelectCancellerPosix::CancelSelect(){
char c=1;
write(pipeWrite, &c, 1);
}
int NetworkSocketPosix::GetDescriptorFromSocket(NetworkSocket *socket){
NetworkSocketPosix* sp=dynamic_cast<NetworkSocketPosix*>(socket);
if(sp)
return sp->fd;
NetworkSocketWrapper* sw=dynamic_cast<NetworkSocketWrapper*>(socket);
if(sw)
return GetDescriptorFromSocket(sw->GetWrapped());
return 0;
}

View File

@ -0,0 +1,74 @@
//
// 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 LIBTGVOIP_NETWORKSOCKETPOSIX_H
#define LIBTGVOIP_NETWORKSOCKETPOSIX_H
#include "../../NetworkSocket.h"
#include <vector>
#include <sys/select.h>
#include <pthread.h>
namespace tgvoip {
class SocketSelectCancellerPosix : public SocketSelectCanceller{
friend class NetworkSocketPosix;
public:
SocketSelectCancellerPosix();
virtual ~SocketSelectCancellerPosix();
virtual void CancelSelect();
private:
int pipeRead;
int pipeWrite;
};
class NetworkSocketPosix : public NetworkSocket{
public:
NetworkSocketPosix(NetworkProtocol protocol);
virtual ~NetworkSocketPosix();
virtual void Send(NetworkPacket* packet);
virtual void Receive(NetworkPacket* packet);
virtual void Open();
virtual void Close();
virtual void Connect(NetworkAddress* address, uint16_t port);
virtual std::string GetLocalInterfaceInfo(IPv4Address* v4addr, IPv6Address* v6addr);
virtual void OnActiveInterfaceChanged();
virtual uint16_t GetLocalPort();
static std::string V4AddressToString(uint32_t address);
static std::string V6AddressToString(unsigned char address[16]);
static uint32_t StringToV4Address(std::string address);
static void StringToV6Address(std::string address, unsigned char* out);
static IPv4Address* ResolveDomainName(std::string name);
static bool Select(std::vector<NetworkSocket*>& readFds, std::vector<NetworkSocket*>& errorFds, SocketSelectCanceller* canceller);
virtual NetworkAddress *GetConnectedAddress();
virtual uint16_t GetConnectedPort();
virtual void SetTimeouts(int sendTimeout, int recvTimeout);
protected:
virtual void SetMaxPriority();
private:
static int GetDescriptorFromSocket(NetworkSocket* socket);
int fd;
bool needUpdateNat64Prefix;
bool nat64Present;
double switchToV6at;
bool isV4Available;
bool useTCP;
bool closing;
IPv4Address lastRecvdV4;
IPv6Address lastRecvdV6;
NetworkAddress* tcpConnectedAddress;
uint16_t tcpConnectedPort;
};
}
#endif //LIBTGVOIP_NETWORKSOCKETPOSIX_H

View File

@ -0,0 +1,431 @@
//
// 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 <assert.h>
#include "AudioInputWASAPI.h"
#include "../../logging.h"
#include "../../VoIPController.h"
#define BUFFER_SIZE 960
#define CHECK_RES(res, msg) {if(FAILED(res)){LOGE("%s failed: HRESULT=0x%08X", msg, res); failed=true; return;}}
#define SCHECK_RES(res, msg) {if(FAILED(res)){LOGE("%s failed: HRESULT=0x%08X", msg, res); return;}}
template <class T> void SafeRelease(T **ppT)
{
if(*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
using namespace tgvoip::audio;
AudioInputWASAPI::AudioInputWASAPI(std::string deviceID){
isRecording=false;
remainingDataLen=0;
refCount=1;
HRESULT res;
res=CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
CHECK_RES(res, "CoInitializeEx");
#ifdef TGVOIP_WINXP_COMPAT
HANDLE (WINAPI *__CreateEventExA)(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCSTR lpName, DWORD dwFlags, DWORD dwDesiredAccess);
__CreateEventExA=(HANDLE (WINAPI *)(LPSECURITY_ATTRIBUTES, LPCSTR, DWORD, DWORD))GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateEventExA");
#undef CreateEventEx
#define CreateEventEx __CreateEventExA
#endif
shutdownEvent=CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
audioSamplesReadyEvent=CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
streamSwitchEvent=CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
ZeroMemory(&format, sizeof(format));
format.wFormatTag=WAVE_FORMAT_PCM;
format.nChannels=1;
format.nSamplesPerSec=48000;
format.nBlockAlign=2;
format.nAvgBytesPerSec=format.nSamplesPerSec*format.nBlockAlign;
format.wBitsPerSample=16;
#ifdef TGVOIP_WINDOWS_DESKTOP
res=CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&enumerator));
CHECK_RES(res, "CoCreateInstance(MMDeviceEnumerator)");
res=enumerator->RegisterEndpointNotificationCallback(this);
CHECK_RES(res, "enumerator->RegisterEndpointNotificationCallback");
audioSessionControl=NULL;
device=NULL;
#endif
audioClient=NULL;
captureClient=NULL;
thread=NULL;
started=false;
SetCurrentDevice(deviceID);
}
AudioInputWASAPI::~AudioInputWASAPI(){
if(audioClient && started){
audioClient->Stop();
}
#ifdef TGVOIP_WINDOWS_DESKTOP
if(audioSessionControl){
audioSessionControl->UnregisterAudioSessionNotification(this);
}
#endif
SetEvent(shutdownEvent);
if(thread){
WaitForSingleObjectEx(thread, INFINITE, false);
CloseHandle(thread);
}
#ifdef TGVOIP_WINDOWS_DESKTOP
SafeRelease(&audioSessionControl);
#endif
SafeRelease(&captureClient);
SafeRelease(&audioClient);
#ifdef TGVOIP_WINDOWS_DESKTOP
SafeRelease(&device);
#endif
CloseHandle(shutdownEvent);
CloseHandle(audioSamplesReadyEvent);
CloseHandle(streamSwitchEvent);
#ifdef TGVOIP_WINDOWS_DESKTOP
if(enumerator)
enumerator->UnregisterEndpointNotificationCallback(this);
SafeRelease(&enumerator);
#endif
}
void AudioInputWASAPI::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
}
void AudioInputWASAPI::Start(){
isRecording=true;
if(!thread){
thread=CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)AudioInputWASAPI::StartThread, this, 0, NULL);
}
started=true;
if(audioClient){
audioClient->Start();
}
}
void AudioInputWASAPI::Stop(){
isRecording=false;
}
bool AudioInputWASAPI::IsRecording(){
return isRecording;
}
void AudioInputWASAPI::EnumerateDevices(std::vector<tgvoip::AudioInputDevice>& devs){
#ifdef TGVOIP_WINDOWS_DESKTOP
HRESULT res;
res=CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
SCHECK_RES(res, "CoInitializeEx");
IMMDeviceEnumerator *deviceEnumerator = NULL;
IMMDeviceCollection *deviceCollection = NULL;
res=CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&deviceEnumerator));
SCHECK_RES(res, "CoCreateInstance(MMDeviceEnumerator)");
res=deviceEnumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &deviceCollection);
SCHECK_RES(res, "EnumAudioEndpoints");
UINT devCount;
res=deviceCollection->GetCount(&devCount);
SCHECK_RES(res, "GetCount");
for(UINT i=0;i<devCount;i++){
IMMDevice* device;
res=deviceCollection->Item(i, &device);
SCHECK_RES(res, "GetDeviceItem");
wchar_t* devID;
res=device->GetId(&devID);
SCHECK_RES(res, "get device id");
IPropertyStore* propStore;
res=device->OpenPropertyStore(STGM_READ, &propStore);
SafeRelease(&device);
SCHECK_RES(res, "OpenPropertyStore");
PROPVARIANT friendlyName;
PropVariantInit(&friendlyName);
res=propStore->GetValue(PKEY_Device_FriendlyName, &friendlyName);
SafeRelease(&propStore);
AudioInputDevice dev;
wchar_t actualFriendlyName[128];
if(friendlyName.vt==VT_LPWSTR){
wcsncpy(actualFriendlyName, friendlyName.pwszVal, sizeof(actualFriendlyName)/sizeof(wchar_t));
}else{
wcscpy(actualFriendlyName, L"Unknown");
}
PropVariantClear(&friendlyName);
char buf[256];
WideCharToMultiByte(CP_UTF8, 0, devID, -1, buf, sizeof(buf), NULL, NULL);
dev.id=buf;
WideCharToMultiByte(CP_UTF8, 0, actualFriendlyName, -1, buf, sizeof(buf), NULL, NULL);
dev.displayName=buf;
devs.push_back(dev);
CoTaskMemFree(devID);
}
SafeRelease(&deviceCollection);
SafeRelease(&deviceEnumerator);
#endif
}
void AudioInputWASAPI::SetCurrentDevice(std::string deviceID){
if(thread){
streamChangeToDevice=deviceID;
SetEvent(streamSwitchEvent);
}else{
ActuallySetCurrentDevice(deviceID);
}
}
void AudioInputWASAPI::ActuallySetCurrentDevice(std::string deviceID){
currentDevice=deviceID;
HRESULT res;
if(audioClient){
res=audioClient->Stop();
CHECK_RES(res, "audioClient->Stop");
}
#ifdef TGVOIP_WINDOWS_DESKTOP
if(audioSessionControl){
res=audioSessionControl->UnregisterAudioSessionNotification(this);
CHECK_RES(res, "audioSessionControl->UnregisterAudioSessionNotification");
}
SafeRelease(&audioSessionControl);
#endif
SafeRelease(&captureClient);
SafeRelease(&audioClient);
#ifdef TGVOIP_WINDOWS_DESKTOP
SafeRelease(&device);
IMMDeviceCollection *deviceCollection = NULL;
if(deviceID=="default"){
isDefaultDevice=true;
res=enumerator->GetDefaultAudioEndpoint(eCapture, eCommunications, &device);
CHECK_RES(res, "GetDefaultAudioEndpoint");
}else{
isDefaultDevice=false;
res=enumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &deviceCollection);
CHECK_RES(res, "EnumAudioEndpoints");
UINT devCount;
res=deviceCollection->GetCount(&devCount);
CHECK_RES(res, "GetCount");
for(UINT i=0;i<devCount;i++){
IMMDevice* device;
res=deviceCollection->Item(i, &device);
CHECK_RES(res, "GetDeviceItem");
wchar_t* _devID;
res=device->GetId(&_devID);
CHECK_RES(res, "get device id");
char devID[128];
WideCharToMultiByte(CP_UTF8, 0, _devID, -1, devID, 128, NULL, NULL);
CoTaskMemFree(_devID);
if(deviceID==devID){
this->device=device;
//device->AddRef();
break;
}
}
}
if(deviceCollection)
SafeRelease(&deviceCollection);
if(!device){
LOGE("Didn't find capture device; failing");
failed=true;
return;
}
res=device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&audioClient);
CHECK_RES(res, "device->Activate");
#else
Platform::String^ defaultDevID=Windows::Media::Devices::MediaDevice::GetDefaultAudioCaptureId(Windows::Media::Devices::AudioDeviceRole::Communications);
HRESULT res1, res2;
IAudioClient2* audioClient2=WindowsSandboxUtils::ActivateAudioDevice(defaultDevID->Data(), &res1, &res2);
CHECK_RES(res1, "activate1");
CHECK_RES(res2, "activate2");
AudioClientProperties properties={};
properties.cbSize=sizeof AudioClientProperties;
properties.eCategory=AudioCategory_Communications;
res = audioClient2->SetClientProperties(&properties);
CHECK_RES(res, "audioClient2->SetClientProperties");
audioClient=audioClient2;
#endif
// {2C693079-3F59-49FD-964F-61C005EAA5D3}
const GUID guid = { 0x2c693079, 0x3f59, 0x49fd, { 0x96, 0x4f, 0x61, 0xc0, 0x5, 0xea, 0xa5, 0xd3 } };
res = audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST | 0x80000000/*AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM*/, 60 * 10000, 0, &format, &guid);
CHECK_RES(res, "audioClient->Initialize");
uint32_t bufSize;
res = audioClient->GetBufferSize(&bufSize);
CHECK_RES(res, "audioClient->GetBufferSize");
LOGV("buffer size: %u", bufSize);
REFERENCE_TIME latency;
if(SUCCEEDED(audioClient->GetStreamLatency(&latency))){
estimatedDelay=latency ? latency/10000 : 60;
LOGD("capture latency: %d", estimatedDelay);
}else{
estimatedDelay=60;
}
res = audioClient->SetEventHandle(audioSamplesReadyEvent);
CHECK_RES(res, "audioClient->SetEventHandle");
res = audioClient->GetService(IID_PPV_ARGS(&captureClient));
CHECK_RES(res, "audioClient->GetService");
#ifdef TGVOIP_WINDOWS_DESKTOP
res=audioClient->GetService(IID_PPV_ARGS(&audioSessionControl));
CHECK_RES(res, "audioClient->GetService(IAudioSessionControl)");
res=audioSessionControl->RegisterAudioSessionNotification(this);
CHECK_RES(res, "audioSessionControl->RegisterAudioSessionNotification");
#endif
if(isRecording)
audioClient->Start();
LOGV("set current output device done");
}
DWORD AudioInputWASAPI::StartThread(void* arg) {
((AudioInputWASAPI*)arg)->RunThread();
return 0;
}
void AudioInputWASAPI::RunThread() {
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
HANDLE waitArray[]={shutdownEvent, streamSwitchEvent, audioSamplesReadyEvent};
HRESULT res=CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
CHECK_RES(res, "CoInitializeEx in capture thread");
uint32_t bufferSize=0;
uint32_t framesWritten=0;
bool running=true;
//double prevCallback=VoIPController::GetCurrentTime();
while(running){
DWORD waitResult=WaitForMultipleObjectsEx(3, waitArray, false, INFINITE, false);
if(waitResult==WAIT_OBJECT_0){ // shutdownEvent
LOGV("capture thread shutting down");
running=false;
}else if(waitResult==WAIT_OBJECT_0+1){ // streamSwitchEvent
LOGV("stream switch");
ActuallySetCurrentDevice(streamChangeToDevice);
ResetEvent(streamSwitchEvent);
bufferSize=0;
LOGV("stream switch done");
}else if(waitResult==WAIT_OBJECT_0+2){ // audioSamplesReadyEvent
if(!audioClient)
continue;
res=captureClient->GetNextPacketSize(&bufferSize);
CHECK_RES(res, "captureClient->GetNextPacketSize");
BYTE* data;
uint32_t framesAvailable=bufferSize;
DWORD flags;
res=captureClient->GetBuffer(&data, &framesAvailable, &flags, NULL, NULL);
CHECK_RES(res, "captureClient->GetBuffer");
size_t dataLen=framesAvailable*2;
assert(remainingDataLen+dataLen<sizeof(remainingData));
//double t=VoIPController::GetCurrentTime();
//LOGV("audio capture: %u, time %f", framesAvailable, t-prevCallback);
//prevCallback=t;
memcpy(remainingData+remainingDataLen, data, dataLen);
remainingDataLen+=dataLen;
while(remainingDataLen>960*2){
if(isRecording)
InvokeCallback(remainingData, 960*2);
memmove(remainingData, remainingData+(960*2), remainingDataLen-960*2);
remainingDataLen-=960*2;
}
res=captureClient->ReleaseBuffer(framesAvailable);
CHECK_RES(res, "captureClient->ReleaseBuffer");
framesWritten+=framesAvailable;
}
}
}
#ifdef TGVOIP_WINDOWS_DESKTOP
HRESULT AudioInputWASAPI::OnSessionDisconnected(AudioSessionDisconnectReason reason) {
if(!isDefaultDevice){
streamChangeToDevice="default";
SetEvent(streamSwitchEvent);
}
return S_OK;
}
HRESULT AudioInputWASAPI::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR newDevID) {
if(flow==eCapture && role==eCommunications && isDefaultDevice){
streamChangeToDevice="default";
SetEvent(streamSwitchEvent);
}
return S_OK;
}
ULONG AudioInputWASAPI::AddRef(){
return InterlockedIncrement(&refCount);
}
ULONG AudioInputWASAPI::Release(){
return InterlockedDecrement(&refCount);
}
HRESULT AudioInputWASAPI::QueryInterface(REFIID iid, void** obj){
if(!obj){
return E_POINTER;
}
*obj=NULL;
if(iid==IID_IUnknown){
*obj=static_cast<IUnknown*>(static_cast<IAudioSessionEvents*>(this));
AddRef();
}else if(iid==__uuidof(IMMNotificationClient)){
*obj=static_cast<IMMNotificationClient*>(this);
AddRef();
}else if(iid==__uuidof(IAudioSessionEvents)){
*obj=static_cast<IAudioSessionEvents*>(this);
AddRef();
}else{
return E_NOINTERFACE;
}
return S_OK;
}
#endif

View File

@ -0,0 +1,106 @@
//
// 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 LIBTGVOIP_AUDIOINPUTWASAPI_H
#define LIBTGVOIP_AUDIOINPUTWASAPI_H
#if WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP
#define TGVOIP_WINDOWS_PHONE
#endif
#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY==WINAPI_FAMILY_DESKTOP_APP
#define TGVOIP_WINDOWS_DESKTOP
#endif
#include <windows.h>
#include <string>
#include <vector>
#pragma warning(push)
#pragma warning(disable : 4201)
#ifndef TGVOIP_WP_SILVERLIGHT
#include <mmdeviceapi.h>
#endif
#ifdef TGVOIP_WINDOWS_DESKTOP
#include <audiopolicy.h>
#include <functiondiscoverykeys.h>
#else
#include <audioclient.h>
#include "WindowsSandboxUtils.h"
#endif
#pragma warning(pop)
#include "../../audio/AudioInput.h"
namespace tgvoip{
namespace audio{
#ifdef TGVOIP_WINDOWS_DESKTOP
class AudioInputWASAPI : public AudioInput, IMMNotificationClient, IAudioSessionEvents{
#else
class AudioInputWASAPI : public AudioInput{
#endif
public:
AudioInputWASAPI(std::string deviceID);
virtual ~AudioInputWASAPI();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
virtual void Start();
virtual void Stop();
virtual bool IsRecording();
virtual void SetCurrentDevice(std::string deviceID);
static void EnumerateDevices(std::vector<AudioInputDevice>& devs);
#ifdef TGVOIP_WINDOWS_DESKTOP
STDMETHOD_(ULONG, AddRef)();
STDMETHOD_(ULONG, Release)();
#endif
private:
void ActuallySetCurrentDevice(std::string deviceID);
static DWORD StartThread(void* arg);
void RunThread();
WAVEFORMATEX format;
bool isRecording;
HANDLE shutdownEvent;
HANDLE audioSamplesReadyEvent;
HANDLE streamSwitchEvent;
HANDLE thread;
IAudioClient* audioClient;
IAudioCaptureClient* captureClient;
#ifdef TGVOIP_WINDOWS_DESKTOP
IMMDeviceEnumerator* enumerator;
IAudioSessionControl* audioSessionControl;
IMMDevice* device;
#endif
unsigned char remainingData[10240];
size_t remainingDataLen;
bool isDefaultDevice;
ULONG refCount;
std::string streamChangeToDevice;
bool started;
#ifdef TGVOIP_WINDOWS_DESKTOP
STDMETHOD(OnDisplayNameChanged) (LPCWSTR /*NewDisplayName*/, LPCGUID /*EventContext*/) { return S_OK; };
STDMETHOD(OnIconPathChanged) (LPCWSTR /*NewIconPath*/, LPCGUID /*EventContext*/) { return S_OK; };
STDMETHOD(OnSimpleVolumeChanged) (float /*NewSimpleVolume*/, BOOL /*NewMute*/, LPCGUID /*EventContext*/) { return S_OK; }
STDMETHOD(OnChannelVolumeChanged) (DWORD /*ChannelCount*/, float /*NewChannelVolumes*/[], DWORD /*ChangedChannel*/, LPCGUID /*EventContext*/) { return S_OK; };
STDMETHOD(OnGroupingParamChanged) (LPCGUID /*NewGroupingParam*/, LPCGUID /*EventContext*/) { return S_OK; };
STDMETHOD(OnStateChanged) (AudioSessionState /*NewState*/) { return S_OK; };
STDMETHOD(OnSessionDisconnected) (AudioSessionDisconnectReason DisconnectReason);
STDMETHOD(OnDeviceStateChanged) (LPCWSTR /*DeviceId*/, DWORD /*NewState*/) { return S_OK; }
STDMETHOD(OnDeviceAdded) (LPCWSTR /*DeviceId*/) { return S_OK; };
STDMETHOD(OnDeviceRemoved) (LPCWSTR /*DeviceId(*/) { return S_OK; };
STDMETHOD(OnDefaultDeviceChanged) (EDataFlow Flow, ERole Role, LPCWSTR NewDefaultDeviceId);
STDMETHOD(OnPropertyValueChanged) (LPCWSTR /*DeviceId*/, const PROPERTYKEY /*Key*/) { return S_OK; };
//
// IUnknown
//
STDMETHOD(QueryInterface)(REFIID iid, void **pvObject);
#endif
};
}
}
#endif //LIBTGVOIP_AUDIOINPUTWASAPI_H

View File

@ -0,0 +1,170 @@
//
// 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 <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include "AudioInputWave.h"
#include "../../logging.h"
#include "../../VoIPController.h"
using namespace tgvoip::audio;
#define BUFFER_SIZE 960
#define CHECK_ERROR(res, msg) if(res!=MMSYSERR_NOERROR){wchar_t _buf[1024]; waveInGetErrorTextW(res, _buf, 1024); LOGE(msg ": %ws (MMRESULT=0x%08X)", _buf, res); failed=true;}
AudioInputWave::AudioInputWave(std::string deviceID){
isRecording=false;
for(int i=0;i<4;i++){
ZeroMemory(&buffers[i], sizeof(WAVEHDR));
buffers[i].dwBufferLength=960*2;
buffers[i].lpData=(char*)malloc(960*2);
}
hWaveIn=NULL;
SetCurrentDevice(deviceID);
}
AudioInputWave::~AudioInputWave(){
for(int i=0;i<4;i++){
free(buffers[i].lpData);
}
waveInClose(hWaveIn);
}
void AudioInputWave::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
}
void AudioInputWave::Start(){
isRecording=true;
MMRESULT res;
for(int i=0;i<4;i++){
res=waveInPrepareHeader(hWaveIn, &buffers[i], sizeof(WAVEHDR));
CHECK_ERROR(res, "waveInPrepareHeader failed");
res=waveInAddBuffer(hWaveIn, &buffers[i], sizeof(WAVEHDR));
CHECK_ERROR(res, "waveInAddBuffer failed");
}
res=waveInStart(hWaveIn);
CHECK_ERROR(res, "waveInStart failed");
}
void AudioInputWave::Stop(){
isRecording=false;
MMRESULT res=waveInStop(hWaveIn);
CHECK_ERROR(res, "waveInStop failed");
res=waveInReset(hWaveIn);
CHECK_ERROR(res, "waveInReset failed");
for(int i=0;i<4;i++){
res=waveInUnprepareHeader(hWaveIn, &buffers[i], sizeof(WAVEHDR));
CHECK_ERROR(res, "waveInUnprepareHeader failed");
}
}
void AudioInputWave::WaveInProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2){
if(uMsg==WIM_DATA){
((AudioInputWave*)dwInstance)->OnData((WAVEHDR*)dwParam1);
}
}
void AudioInputWave::OnData(WAVEHDR* hdr){
if(!isRecording)
return;
InvokeCallback((unsigned char*)hdr->lpData, hdr->dwBufferLength);
hdr->dwFlags&= ~WHDR_DONE;
MMRESULT res=waveInAddBuffer(hWaveIn, hdr, sizeof(WAVEHDR));
CHECK_ERROR(res, "waveInAddBuffer failed");
}
void AudioInputWave::EnumerateDevices(std::vector<tgvoip::AudioInputDevice>& devs){
UINT num=waveInGetNumDevs();
WAVEINCAPSW caps;
char nameBuf[512];
for(UINT i=0;i<num;i++){
waveInGetDevCapsW(i, &caps, sizeof(caps));
AudioInputDevice dev;
WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, nameBuf, sizeof(nameBuf), NULL, NULL);
dev.displayName=std::string(nameBuf);
dev.id=std::string(nameBuf);
devs.push_back(dev);
}
}
void AudioInputWave::SetCurrentDevice(std::string deviceID){
currentDevice=deviceID;
bool wasRecording=isRecording;
isRecording=false;
if(hWaveIn){
MMRESULT res;
if(isRecording){
res=waveInStop(hWaveIn);
CHECK_ERROR(res, "waveInStop failed");
res=waveInReset(hWaveIn);
CHECK_ERROR(res, "waveInReset failed");
for(int i=0;i<4;i++){
res=waveInUnprepareHeader(hWaveIn, &buffers[i], sizeof(WAVEHDR));
CHECK_ERROR(res, "waveInUnprepareHeader failed");
}
}
res=waveInClose(hWaveIn);
CHECK_ERROR(res, "waveInClose failed");
}
ZeroMemory(&format, sizeof(format));
format.cbSize=0;
format.wFormatTag=WAVE_FORMAT_PCM;
format.nSamplesPerSec=48000;
format.wBitsPerSample=16;
format.nChannels=1;
format.nBlockAlign=2;
LOGV("before open device %s", deviceID.c_str());
if(deviceID=="default"){
MMRESULT res=waveInOpen(&hWaveIn, WAVE_MAPPER, &format, (DWORD_PTR)AudioInputWave::WaveInProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
CHECK_ERROR(res, "waveInOpen failed");
}else{
UINT num=waveInGetNumDevs();
WAVEINCAPSW caps;
char nameBuf[512];
hWaveIn=NULL;
for(UINT i=0;i<num;i++){
waveInGetDevCapsW(i, &caps, sizeof(caps));
WideCharToMultiByte(CP_UTF8, 0, caps.szPname, -1, nameBuf, sizeof(nameBuf), NULL, NULL);
std::string name=std::string(nameBuf);
if(name==deviceID){
MMRESULT res=waveInOpen(&hWaveIn, i, &format, (DWORD_PTR)AudioInputWave::WaveInProc, (DWORD_PTR)this, CALLBACK_FUNCTION | WAVE_MAPPED);
CHECK_ERROR(res, "waveInOpen failed");
LOGD("Opened device %s", nameBuf);
break;
}
}
if(!hWaveIn){
SetCurrentDevice("default");
return;
}
}
isRecording=wasRecording;
if(isRecording){
MMRESULT res;
for(int i=0;i<4;i++){
res=waveInPrepareHeader(hWaveIn, &buffers[i], sizeof(WAVEHDR));
CHECK_ERROR(res, "waveInPrepareHeader failed");
res=waveInAddBuffer(hWaveIn, &buffers[i], sizeof(WAVEHDR));
CHECK_ERROR(res, "waveInAddBuffer failed");
}
res=waveInStart(hWaveIn);
CHECK_ERROR(res, "waveInStart failed");
}
}

View File

@ -0,0 +1,41 @@
//
// 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 LIBTGVOIP_AUDIOINPUTWAVE_H
#define LIBTGVOIP_AUDIOINPUTWAVE_H
#include <windows.h>
#include <string>
#include <vector>
#include "../../audio/AudioInput.h"
namespace tgvoip{
namespace audio{
class AudioInputWave : public AudioInput{
public:
AudioInputWave(std::string deviceID);
virtual ~AudioInputWave();
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
virtual void Start();
virtual void Stop();
virtual void SetCurrentDevice(std::string deviceID);
static void EnumerateDevices(std::vector<AudioInputDevice>& devs);
private:
static void CALLBACK WaveInProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2);
void OnData(WAVEHDR* hdr);
HWAVEIN hWaveIn;
WAVEFORMATEX format;
WAVEHDR buffers[4];
bool isRecording;
};
}
}
#endif //LIBTGVOIP_AUDIOINPUTWAVE_H

View File

@ -0,0 +1,439 @@
//
// 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 <assert.h>
#include "AudioOutputWASAPI.h"
#include "../../logging.h"
#include "../../VoIPController.h"
#define BUFFER_SIZE 960
#define CHECK_RES(res, msg) {if(FAILED(res)){LOGE("%s failed: HRESULT=0x%08X", msg, res); failed=true; return;}}
#define SCHECK_RES(res, msg) {if(FAILED(res)){LOGE("%s failed: HRESULT=0x%08X", msg, res); return;}}
template <class T> void SafeRelease(T **ppT)
{
if(*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
#ifdef TGVOIP_WINXP_COMPAT
#endif
using namespace tgvoip::audio;
AudioOutputWASAPI::AudioOutputWASAPI(std::string deviceID){
isPlaying=false;
remainingDataLen=0;
refCount=1;
HRESULT res;
res=CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
CHECK_RES(res, "CoInitializeEx");
#ifdef TGVOIP_WINXP_COMPAT
HANDLE (WINAPI *__CreateEventExA)(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCSTR lpName, DWORD dwFlags, DWORD dwDesiredAccess);
__CreateEventExA=(HANDLE (WINAPI *)(LPSECURITY_ATTRIBUTES, LPCSTR, DWORD, DWORD))GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateEventExA");
#undef CreateEventEx
#define CreateEventEx __CreateEventExA
#endif
shutdownEvent=CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
audioSamplesReadyEvent=CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
streamSwitchEvent=CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
ZeroMemory(&format, sizeof(format));
format.wFormatTag=WAVE_FORMAT_PCM;
format.nChannels=1;
format.nSamplesPerSec=48000;
format.nBlockAlign=2;
format.nAvgBytesPerSec=format.nSamplesPerSec*format.nBlockAlign;
format.wBitsPerSample=16;
#ifdef TGVOIP_WINDOWS_DESKTOP
res=CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&enumerator));
CHECK_RES(res, "CoCreateInstance(MMDeviceEnumerator)");
res=enumerator->RegisterEndpointNotificationCallback(this);
CHECK_RES(res, "enumerator->RegisterEndpointNotificationCallback");
audioSessionControl=NULL;
device=NULL;
#endif
audioClient=NULL;
renderClient=NULL;
thread=NULL;
SetCurrentDevice(deviceID);
}
AudioOutputWASAPI::~AudioOutputWASAPI(){
if(audioClient && isPlaying){
audioClient->Stop();
}
#ifdef TGVOIP_WINDOWS_DESKTOP
if(audioSessionControl){
audioSessionControl->UnregisterAudioSessionNotification(this);
}
#endif
SetEvent(shutdownEvent);
if(thread){
WaitForSingleObjectEx(thread, INFINITE, false);
CloseHandle(thread);
}
SafeRelease(&renderClient);
SafeRelease(&audioClient);
#ifdef TGVOIP_WINDOWS_DESKTOP
SafeRelease(&device);
SafeRelease(&audioSessionControl);
#endif
CloseHandle(shutdownEvent);
CloseHandle(audioSamplesReadyEvent);
CloseHandle(streamSwitchEvent);
#ifdef TGVOIP_WINDOWS_DESKTOP
if(enumerator)
enumerator->UnregisterEndpointNotificationCallback(this);
SafeRelease(&enumerator);
#endif
}
void AudioOutputWASAPI::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
}
void AudioOutputWASAPI::Start(){
isPlaying=true;
if(!thread){
thread=CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)AudioOutputWASAPI::StartThread, this, 0, NULL);
}
if(audioClient)
audioClient->Start();
}
void AudioOutputWASAPI::Stop(){
isPlaying=false;
if(audioClient)
audioClient->Stop();
}
bool AudioOutputWASAPI::IsPlaying(){
return isPlaying;
}
void AudioOutputWASAPI::EnumerateDevices(std::vector<tgvoip::AudioOutputDevice>& devs){
#ifdef TGVOIP_WINDOWS_DESKTOP
HRESULT res;
res=CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
SCHECK_RES(res, "CoInitializeEx");
IMMDeviceEnumerator *deviceEnumerator = NULL;
IMMDeviceCollection *deviceCollection = NULL;
res=CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&deviceEnumerator));
SCHECK_RES(res, "CoCreateInstance(MMDeviceEnumerator)");
res=deviceEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &deviceCollection);
SCHECK_RES(res, "EnumAudioEndpoints");
UINT devCount;
res=deviceCollection->GetCount(&devCount);
SCHECK_RES(res, "GetCount");
for(UINT i=0;i<devCount;i++){
IMMDevice* device;
res=deviceCollection->Item(i, &device);
SCHECK_RES(res, "GetDeviceItem");
wchar_t* devID;
res=device->GetId(&devID);
SCHECK_RES(res, "get device id");
IPropertyStore* propStore;
res=device->OpenPropertyStore(STGM_READ, &propStore);
SafeRelease(&device);
SCHECK_RES(res, "OpenPropertyStore");
PROPVARIANT friendlyName;
PropVariantInit(&friendlyName);
res=propStore->GetValue(PKEY_Device_FriendlyName, &friendlyName);
SafeRelease(&propStore);
AudioOutputDevice dev;
wchar_t actualFriendlyName[128];
if(friendlyName.vt==VT_LPWSTR){
wcsncpy(actualFriendlyName, friendlyName.pwszVal, sizeof(actualFriendlyName)/sizeof(wchar_t));
}else{
wcscpy(actualFriendlyName, L"Unknown");
}
PropVariantClear(&friendlyName);
char buf[256];
WideCharToMultiByte(CP_UTF8, 0, devID, -1, buf, sizeof(buf), NULL, NULL);
dev.id=buf;
WideCharToMultiByte(CP_UTF8, 0, actualFriendlyName, -1, buf, sizeof(buf), NULL, NULL);
dev.displayName=buf;
devs.push_back(dev);
CoTaskMemFree(devID);
}
SafeRelease(&deviceCollection);
SafeRelease(&deviceEnumerator);
#endif
}
void AudioOutputWASAPI::SetCurrentDevice(std::string deviceID){
if(thread){
streamChangeToDevice=deviceID;
SetEvent(streamSwitchEvent);
}else{
ActuallySetCurrentDevice(deviceID);
}
}
void AudioOutputWASAPI::ActuallySetCurrentDevice(std::string deviceID){
currentDevice=deviceID;
HRESULT res;
if(audioClient && isPlaying){
res=audioClient->Stop();
CHECK_RES(res, "audioClient->Stop");
}
#ifdef TGVOIP_WINDOWS_DESKTOP
if(audioSessionControl){
res=audioSessionControl->UnregisterAudioSessionNotification(this);
CHECK_RES(res, "audioSessionControl->UnregisterAudioSessionNotification");
}
SafeRelease(&audioSessionControl);
#endif
SafeRelease(&renderClient);
SafeRelease(&audioClient);
#ifdef TGVOIP_WINDOWS_DESKTOP
SafeRelease(&device);
IMMDeviceCollection *deviceCollection = NULL;
if(deviceID=="default"){
isDefaultDevice=true;
res=enumerator->GetDefaultAudioEndpoint(eRender, eCommunications, &device);
CHECK_RES(res, "GetDefaultAudioEndpoint");
}else{
isDefaultDevice=false;
res=enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &deviceCollection);
CHECK_RES(res, "EnumAudioEndpoints");
UINT devCount;
res=deviceCollection->GetCount(&devCount);
CHECK_RES(res, "GetCount");
for(UINT i=0;i<devCount;i++){
IMMDevice* device;
res=deviceCollection->Item(i, &device);
CHECK_RES(res, "GetDeviceItem");
wchar_t* _devID;
res=device->GetId(&_devID);
CHECK_RES(res, "get device id");
char devID[128];
WideCharToMultiByte(CP_UTF8, 0, _devID, -1, devID, 128, NULL, NULL);
CoTaskMemFree(_devID);
if(deviceID==devID){
this->device=device;
break;
}
}
}
if(deviceCollection)
SafeRelease(&deviceCollection);
if(!device){
LOGE("Didn't find playback device; failing");
failed=true;
return;
}
res=device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&audioClient);
CHECK_RES(res, "device->Activate");
#else
Platform::String^ defaultDevID=Windows::Media::Devices::MediaDevice::GetDefaultAudioRenderId(Windows::Media::Devices::AudioDeviceRole::Communications);
HRESULT res1, res2;
IAudioClient2* audioClient2=WindowsSandboxUtils::ActivateAudioDevice(defaultDevID->Data(), &res1, &res2);
CHECK_RES(res1, "activate1");
CHECK_RES(res2, "activate2");
AudioClientProperties properties={};
properties.cbSize=sizeof AudioClientProperties;
properties.eCategory=AudioCategory_Communications;
res = audioClient2->SetClientProperties(&properties);
CHECK_RES(res, "audioClient2->SetClientProperties");
audioClient = audioClient2;
#endif
// {2C693079-3F59-49FD-964F-61C005EAA5D3}
const GUID guid = { 0x2c693079, 0x3f59, 0x49fd, { 0x96, 0x4f, 0x61, 0xc0, 0x5, 0xea, 0xa5, 0xd3 } };
res = audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST | 0x80000000/*AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM*/, 60 * 10000, 0, &format, &guid);
CHECK_RES(res, "audioClient->Initialize");
uint32_t bufSize;
res = audioClient->GetBufferSize(&bufSize);
CHECK_RES(res, "audioClient->GetBufferSize");
LOGV("buffer size: %u", bufSize);
REFERENCE_TIME latency;
if(SUCCEEDED(audioClient->GetStreamLatency(&latency))){
estimatedDelay=latency ? latency/10000 : 60;
LOGD("playback latency: %d", estimatedDelay);
}else{
estimatedDelay=60;
}
res = audioClient->SetEventHandle(audioSamplesReadyEvent);
CHECK_RES(res, "audioClient->SetEventHandle");
res = audioClient->GetService(IID_PPV_ARGS(&renderClient));
CHECK_RES(res, "audioClient->GetService");
BYTE* data;
res = renderClient->GetBuffer(bufSize, &data);
CHECK_RES(res, "renderClient->GetBuffer");
res = renderClient->ReleaseBuffer(bufSize, AUDCLNT_BUFFERFLAGS_SILENT);
CHECK_RES(res, "renderClient->ReleaseBuffer");
#ifdef TGVOIP_WINDOWS_DESKTOP
res=audioClient->GetService(IID_PPV_ARGS(&audioSessionControl));
CHECK_RES(res, "audioClient->GetService(IAudioSessionControl)");
res=audioSessionControl->RegisterAudioSessionNotification(this);
CHECK_RES(res, "audioSessionControl->RegisterAudioSessionNotification");
#endif
if(isPlaying)
audioClient->Start();
LOGV("set current output device done");
}
DWORD AudioOutputWASAPI::StartThread(void* arg) {
((AudioOutputWASAPI*)arg)->RunThread();
return 0;
}
void AudioOutputWASAPI::RunThread() {
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
HANDLE waitArray[]={shutdownEvent, streamSwitchEvent, audioSamplesReadyEvent};
HRESULT res=CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
CHECK_RES(res, "CoInitializeEx in render thread");
uint32_t bufferSize;
res=audioClient->GetBufferSize(&bufferSize);
CHECK_RES(res, "audioClient->GetBufferSize");
uint32_t framesWritten=0;
bool running=true;
//double prevCallback=VoIPController::GetCurrentTime();
while(running){
DWORD waitResult=WaitForMultipleObjectsEx(3, waitArray, false, INFINITE, false);
if(waitResult==WAIT_OBJECT_0){ // shutdownEvent
LOGV("render thread shutting down");
running=false;
}else if(waitResult==WAIT_OBJECT_0+1){ // streamSwitchEvent
LOGV("stream switch");
ActuallySetCurrentDevice(streamChangeToDevice);
ResetEvent(streamSwitchEvent);
LOGV("stream switch done");
}else if(waitResult==WAIT_OBJECT_0+2){ // audioSamplesReadyEvent
if(!audioClient)
continue;
BYTE* data;
uint32_t padding;
uint32_t framesAvailable;
res=audioClient->GetCurrentPadding(&padding);
CHECK_RES(res, "audioClient->GetCurrentPadding");
framesAvailable=bufferSize-padding;
res=renderClient->GetBuffer(framesAvailable, &data);
CHECK_RES(res, "renderClient->GetBuffer");
//double t=VoIPController::GetCurrentTime();
//LOGV("framesAvail: %u, time: %f, isPlaying: %d", framesAvailable, t-prevCallback, isPlaying);
//prevCallback=t;
size_t bytesAvailable=framesAvailable*2;
while(bytesAvailable>remainingDataLen){
InvokeCallback(remainingData+remainingDataLen, 960*2);
remainingDataLen+=960*2;
}
memcpy(data, remainingData, bytesAvailable);
if(remainingDataLen>bytesAvailable){
memmove(remainingData, remainingData+bytesAvailable, remainingDataLen-bytesAvailable);
}
remainingDataLen-=bytesAvailable;
res=renderClient->ReleaseBuffer(framesAvailable, 0);
CHECK_RES(res, "renderClient->ReleaseBuffer");
framesWritten+=framesAvailable;
}
}
}
#ifdef TGVOIP_WINDOWS_DESKTOP
HRESULT AudioOutputWASAPI::OnSessionDisconnected(AudioSessionDisconnectReason reason) {
if(!isDefaultDevice){
streamChangeToDevice="default";
SetEvent(streamSwitchEvent);
}
return S_OK;
}
HRESULT AudioOutputWASAPI::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR newDevID) {
if(flow==eRender && role==eCommunications && isDefaultDevice){
streamChangeToDevice="default";
SetEvent(streamSwitchEvent);
}
return S_OK;
}
ULONG AudioOutputWASAPI::AddRef(){
return InterlockedIncrement(&refCount);
}
ULONG AudioOutputWASAPI::Release(){
return InterlockedDecrement(&refCount);
}
HRESULT AudioOutputWASAPI::QueryInterface(REFIID iid, void** obj){
if(!obj){
return E_POINTER;
}
*obj=NULL;
if(iid==IID_IUnknown){
*obj=static_cast<IUnknown*>(static_cast<IAudioSessionEvents*>(this));
AddRef();
}else if(iid==__uuidof(IMMNotificationClient)){
*obj=static_cast<IMMNotificationClient*>(this);
AddRef();
}else if(iid==__uuidof(IAudioSessionEvents)){
*obj=static_cast<IAudioSessionEvents*>(this);
AddRef();
}else{
return E_NOINTERFACE;
}
return S_OK;
}
#endif

Some files were not shown because too many files have changed in this diff Show More