mirror of https://github.com/procxx/kepka.git
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:
parent
db59505e7d
commit
ee6ef233f3
|
|
@ -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
|
||||
|
|
@ -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/
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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
7
Telegram/ThirdParty/libtgvoip/libtgvoip.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
vendored
Normal file
7
Telegram/ThirdParty/libtgvoip/libtgvoip.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:libtgvoip.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
BIN
Telegram/ThirdParty/libtgvoip/libtgvoip.xcodeproj/project.xcworkspace/xcuserdata/grishka.xcuserdatad/UserInterfaceState.xcuserstate
generated
vendored
Normal file
BIN
Telegram/ThirdParty/libtgvoip/libtgvoip.xcodeproj/project.xcworkspace/xcuserdata/grishka.xcuserdatad/UserInterfaceState.xcuserstate
generated
vendored
Normal file
Binary file not shown.
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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
7
Telegram/ThirdParty/libtgvoip/libtgvoip_osx.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
vendored
Normal file
7
Telegram/ThirdParty/libtgvoip/libtgvoip_osx.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:libtgvoip.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
BIN
Telegram/ThirdParty/libtgvoip/libtgvoip_osx.xcodeproj/project.xcworkspace/xcuserdata/grishka.xcuserdatad/UserInterfaceState.xcuserstate
generated
vendored
Normal file
BIN
Telegram/ThirdParty/libtgvoip/libtgvoip_osx.xcodeproj/project.xcworkspace/xcuserdata/grishka.xcuserdatad/UserInterfaceState.xcuserstate
generated
vendored
Normal file
Binary file not shown.
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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
|
||||
|
|
@ -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];
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
Loading…
Reference in New Issue