Update to 6.0.0 (1908)

This commit is contained in:
DrKLO 2020-03-30 15:00:09 +03:00
parent df90eface5
commit 80c4acfa3b
1160 changed files with 237084 additions and 5423 deletions

View File

@ -2,7 +2,7 @@ FROM gradle:6.0.1-jdk8
ENV ANDROID_SDK_URL https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip
ENV ANDROID_API_LEVEL android-29
ENV ANDROID_BUILD_TOOLS_VERSION 29.0.2
ENV ANDROID_BUILD_TOOLS_VERSION 29.0.3
ENV ANDROID_HOME /usr/local/android-sdk-linux
ENV ANDROID_NDK_VERSION 20.0.5594570
ENV ANDROID_VERSION 29

View File

@ -1,3 +1,5 @@
import java.security.MessageDigest
apply plugin: 'com.android.application'
repositories {
@ -21,15 +23,15 @@ dependencies {
compileOnly 'org.checkerframework:checker-qual:2.5.2'
compileOnly 'org.checkerframework:checker-compat-qual:2.5.0'
implementation 'com.google.firebase:firebase-messaging:20.1.0'
implementation 'com.google.firebase:firebase-config:19.1.1'
implementation 'com.google.firebase:firebase-messaging:20.1.3'
implementation 'com.google.firebase:firebase-config:19.1.3'
implementation 'com.google.android.gms:play-services-maps:17.0.0'
implementation 'com.google.android.gms:play-services-auth:17.0.0'
implementation 'com.google.android.gms:play-services-vision:16.2.0'
implementation 'com.google.android.gms:play-services-wallet:17.0.0'
implementation 'com.google.android.gms:play-services-wearable:17.0.0'
implementation 'com.google.android.gms:play-services-location:17.0.0'
implementation 'net.hockeyapp.android:HockeySDK:5.2.0'
implementation "com.microsoft.appcenter:appcenter-distribute:3.1.0"
implementation "com.microsoft.appcenter:appcenter-crashes:3.1.0"
implementation 'com.googlecode.mp4parser:isoparser:1.0.6'
implementation 'com.stripe:stripe-android:2.0.2'
implementation files('libs/libgsaverification-client.aar')
@ -37,7 +39,7 @@ dependencies {
android {
compileSdkVersion 29
buildToolsVersion '29.0.2'
buildToolsVersion '29.0.3'
ndkVersion "20.0.5594570"
defaultConfig.applicationId = "org.telegram.messenger"
@ -283,7 +285,12 @@ android {
}
}
defaultConfig.versionCode = 1869
defaultConfig.versionCode = 1908
def tgVoipDexFileName = "libtgvoip.dex"
def tgVoipDexClasses = ["AudioRecordJNI", "AudioTrackJNI", "NativeTgVoipDelegate", "NativeTgVoipInstance", "TgVoipNativeLoader", "Resampler", "VLog"]
def tgVoipDexClassesPath = "org/telegram/messenger/voip"
def dxUtilPath = "${sdkDirectory.path}/build-tools/${buildToolsVersion}/dx"
applicationVariants.all { variant ->
variant.outputs.all { output ->
@ -306,6 +313,61 @@ android {
file(manifestPath).write(manifestContent)
}
}
def assembleTgVoipDexTaskName = "assemble${variant.name.capitalize()}TgVoipDex"
task "${assembleTgVoipDexTaskName}" {
doLast {
def sourceDir = (File) android.sourceSets.main.java.srcDirs[0]
def classesDir = "${buildDir}/intermediates/javac/${variant.name}/classes"
def javaDir = "${buildDir}/intermediates/java_tgvoip/${variant.name}/java"
def tgVoipDir = new File(classesDir, tgVoipDexClassesPath)
def javaVoipDirFile = new File(javaDir, tgVoipDexClassesPath)
def tgVoipDexJavaFile = new File(javaVoipDirFile, "TgVoipDex.java")
if (!javaVoipDirFile.exists()) {
javaVoipDirFile.mkdirs()
}
def assetsDirFile = new File(buildDir, "intermediates/merged_assets/${variant.name}/out")
if (!assetsDirFile.exists()) {
assetsDirFile.mkdirs()
}
def tgVoipDexFile = new File(assetsDirFile, tgVoipDexFileName)
def classes = tgVoipDir.list(new FilenameFilter() {
@Override
boolean accept(File dir, String name) {
// handle inner and anonymous classes
return tgVoipDexClasses.any { name == "${it}.class" || name.startsWith("${it}\$") && name.endsWith(".class") }
}
}).collect { "${tgVoipDexClassesPath}/${it}" }
// 1. create libtgvoip.dex
exec {
workingDir classesDir
commandLine([dxUtilPath, "--dex", "--output=${tgVoipDexFile}"] + classes)
}
// 2. remove classes from the main dex
project.delete classes.collect { "${classesDir}/${it}" }
// 3. insert checksum of libtgvoip.dex into TgVoipDex.java
def digest = MessageDigest.getInstance("SHA1")
def fileInputStream = tgVoipDexFile.newInputStream()
def buffer = new byte[4096]
def len
while ((len = fileInputStream.read(buffer)) > 0) {
digest.update(buffer, 0, len)
}
def dexChecksum = new String(Base64.getEncoder().encode(digest.digest())).trim()
tgVoipDexJavaFile.write(new String(new File(sourceDir, "${tgVoipDexClassesPath}/TgVoipDex.java").readBytes()).replace("\$CHECKSUM\$", dexChecksum))
exec {
commandLine([findJavac(), "-d", classesDir, tgVoipDexJavaFile.absolutePath])
}
}
}
tasks.findByName("compile${variant.name.capitalize()}JavaWithJavac").finalizedBy(assembleTgVoipDexTaskName)
tasks.findByName("${assembleTgVoipDexTaskName}").mustRunAfter tasks.findByName("merge${variant.name.capitalize()}Assets")
}
variantFilter { variant ->
@ -318,7 +380,7 @@ android {
defaultConfig {
minSdkVersion 16
targetSdkVersion 28
versionName "5.15.0"
versionName "6.0.0"
vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi']
@ -333,4 +395,31 @@ android {
}
}
private static File findJavaHome() {
String javaPath = System.getProperty("java.home")
if (javaPath != null) {
File javaBase = new File(javaPath)
if (javaBase.exists()) {
if (javaBase.getName().equalsIgnoreCase("jre") && new File(javaBase.getParentFile(), "bin/java").exists()) {
return javaBase.getParentFile()
} else {
return javaBase
}
} else {
return null
}
} else {
return null
}
}
private static File findJavac() {
File javaHome = findJavaHome()
if (javaHome != null) {
return new File(javaHome, "bin/javac")
} else {
return null
}
}
apply plugin: 'com.google.gms.google-services'

View File

@ -95,10 +95,18 @@ endif
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
TGVOIP_NATIVE_VERSION := 1.1
TGVOIP_ADDITIONAL_CFLAGS := -DTGVOIP_NO_VIDEO
include $(MY_LOCAL_PATH)/libtgvoip/Android.mk
LOCAL_PATH := $(MY_LOCAL_PATH) # restore local path after include
include $(CLEAR_VARS)
TGVOIP_NATIVE_VERSION := 2.1
TGVOIP_ADDITIONAL_CFLAGS := -DTGVOIP_NO_VIDEO
include $(MY_LOCAL_PATH)/libtgvoip2/Android.mk
LOCAL_PATH := $(MY_LOCAL_PATH) # restore local path after include
include $(CLEAR_VARS)
LOCAL_CPPFLAGS := -Wall -std=c++14 -DANDROID -frtti -DHAVE_PTHREAD -finline-functions -ffast-math -Os
@ -351,7 +359,7 @@ LOCAL_CFLAGS += -Drestrict='' -D__EMX__ -DOPUS_BUILD -DFIXED_POINT -DUSE_ALLOCA
LOCAL_CFLAGS += -DANDROID_NDK -DDISABLE_IMPORTGL -fno-strict-aliasing -fprefetch-loop-arrays -DAVOID_TABLES -DANDROID_TILE_BASED_DECODE -DANDROID_ARMV6_IDCT -ffast-math -D__STDC_CONSTANT_MACROS
LOCAL_CPPFLAGS := -DBSD=1 -ffast-math -Os -funroll-loops -std=c++14 -DPACKAGE_NAME='"drinkless/org/ton"'
LOCAL_LDLIBS := -ljnigraphics -llog -lz -lEGL -lGLESv2 -landroid
LOCAL_STATIC_LIBRARIES := webp sqlite lz4 rlottie tgnet swscale avformat avcodec avresample avutil voip flac
LOCAL_STATIC_LIBRARIES := webp sqlite lz4 rlottie tgnet swscale avformat avcodec avresample avutil flac
LOCAL_SRC_FILES := \
./opus/src/opus.c \

View File

@ -1,3 +1,3 @@
APP_PLATFORM := android-16
NDK_TOOLCHAIN_VERSION := clang
APP_STL := c++_static
APP_STL := c++_shared

View File

@ -219,14 +219,15 @@ static int writeOggPage(ogg_page *page, FILE *os) {
return written;
}
const opus_int32 bitrate = 16000;
const opus_int32 rate = 16000;
const opus_int32 bitrate = 25000;
const opus_int32 frame_size = 960;
const int with_cvbr = 1;
const int max_ogg_delay = 0;
const int comment_padding = 512;
opus_int32 coding_rate = 16000;
opus_int32 rate = 48000;
opus_int32 coding_rate = 48000;
ogg_int32_t _packetId;
OpusEncoder *_encoder = 0;
uint8_t *_packet = 0;
@ -282,9 +283,12 @@ void cleanupRecorder() {
memset(&og, 0, sizeof(ogg_page));
}
int initRecorder(const char *path) {
int initRecorder(const char *path, opus_int32 sampleRate) {
cleanupRecorder();
coding_rate = sampleRate;
rate = sampleRate;
if (!path) {
return 0;
}
@ -305,7 +309,6 @@ int initRecorder(const char *path) {
inopt.skip = 0;
comment_init(&inopt.comments, &inopt.comments_length, opus_get_version_string());
coding_rate = 16000;
if (rate != coding_rate) {
LOGE("Invalid rate");
@ -329,6 +332,7 @@ int initRecorder(const char *path) {
_packet = malloc(max_frame_bytes);
result = opus_encoder_ctl(_encoder, OPUS_SET_BITRATE(bitrate));
result = opus_encoder_ctl(_encoder, OPUS_SET_COMPLEXITY(10));
if (result != OPUS_OK) {
LOGE("Error OPUS_SET_BITRATE returned: %s", opus_strerror(result));
return 0;
@ -409,7 +413,6 @@ int initRecorder(const char *path) {
return 1;
}
int writeFrame(uint8_t *framePcmBytes, uint32_t frameByteCount) {
size_t cur_frame_size = frame_size;
_packetId++;
@ -493,10 +496,10 @@ int writeFrame(uint8_t *framePcmBytes, uint32_t frameByteCount) {
return 1;
}
JNIEXPORT jint Java_org_telegram_messenger_MediaController_startRecord(JNIEnv *env, jclass class, jstring path) {
JNIEXPORT jint Java_org_telegram_messenger_MediaController_startRecord(JNIEnv *env, jclass class, jstring path, jint sampleRate) {
const char *pathStr = (*env)->GetStringUTFChars(env, path, 0);
int32_t result = initRecorder(pathStr);
int32_t result = initRecorder(pathStr, sampleRate);
if (pathStr != 0) {
(*env)->ReleaseStringUTFChars(env, path, pathStr);

View File

@ -742,8 +742,8 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env
const int divsum = SQUARE((div + 1) >> 1);
// Small buffers
int stack[div * 3];
zeroClearInt(stack, div * 3);
int stack[div * 4];
zeroClearInt(stack, div * 4);
int vmin[MAX(w, h)];
zeroClearInt(vmin, MAX(w, h));
@ -752,9 +752,11 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env
int *r = malloc(wh * sizeof(int));
int *g = malloc(wh * sizeof(int));
int *b = malloc(wh * sizeof(int));
int *a = malloc(wh * sizeof(int));
zeroClearInt(r, wh);
zeroClearInt(g, wh);
zeroClearInt(b, wh);
zeroClearInt(a, wh);
const size_t dvcount = 256 * divsum;
int *dv = malloc(sizeof(int) * dvcount);
@ -766,36 +768,40 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env
// Variables
int x, y;
int *sir;
int routsum, goutsum, boutsum;
int rinsum, ginsum, binsum;
int rsum, gsum, bsum, p, yp;
int routsum, goutsum, boutsum, aoutsum;
int rinsum, ginsum, binsum, ainsum;
int rsum, gsum, bsum, asum, p, yp;
int stackpointer;
int stackstart;
int rbs;
int yw = 0, yi = 0;
for (y = 0; y < h; y++) {
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
ainsum = aoutsum = asum = rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
for (i = -radius; i <= radius; i++) {
sir = &stack[(i + radius) * 3];
sir = &stack[(i + radius) * 4];
int offset = (y * stride + (MIN(wm, MAX(i, 0))) * 4);
sir[0] = pixels[offset];
sir[1] = pixels[offset + 1];
sir[2] = pixels[offset + 2];
sir[3] = pixels[offset + 3];
rbs = r1 - abs(i);
rsum += sir[0] * rbs;
gsum += sir[1] * rbs;
bsum += sir[2] * rbs;
asum += sir[3] * rbs;
if (i > 0) {
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
ainsum += sir[3];
} else {
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
aoutsum += sir[3];
}
}
stackpointer = radius;
@ -804,17 +810,20 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env
r[yi] = dv[rsum];
g[yi] = dv[gsum];
b[yi] = dv[bsum];
a[yi] = dv[asum];
rsum -= routsum;
gsum -= goutsum;
bsum -= boutsum;
asum -= aoutsum;
stackstart = stackpointer - radius + div;
sir = &stack[(stackstart % div) * 3];
sir = &stack[(stackstart % div) * 4];
routsum -= sir[0];
goutsum -= sir[1];
boutsum -= sir[2];
aoutsum -= sir[3];
if (y == 0) {
vmin[x] = MIN(x + radius + 1, wm);
@ -824,24 +833,29 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env
sir[0] = pixels[offset];
sir[1] = pixels[offset + 1];
sir[2] = pixels[offset + 2];
sir[3] = pixels[offset + 3];
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
ainsum += sir[3];
rsum += rinsum;
gsum += ginsum;
bsum += binsum;
asum += ainsum;
stackpointer = (stackpointer + 1) % div;
sir = &stack[(stackpointer % div) * 3];
sir = &stack[(stackpointer % div) * 4];
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
aoutsum += sir[3];
rinsum -= sir[0];
ginsum -= sir[1];
binsum -= sir[2];
ainsum -= sir[3];
yi++;
}
@ -849,31 +863,35 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env
}
for (x = 0; x < w; x++) {
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
ainsum = aoutsum = asum = rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
yp = -radius * w;
for (i = -radius; i <= radius; i++) {
yi = MAX(0, yp) + x;
sir = &stack[(i + radius) * 3];
sir = &stack[(i + radius) * 4];
sir[0] = r[yi];
sir[1] = g[yi];
sir[2] = b[yi];
sir[3] = a[yi];
rbs = r1 - abs(i);
rsum += r[yi] * rbs;
gsum += g[yi] * rbs;
bsum += b[yi] * rbs;
asum += a[yi] * rbs;
if (i > 0) {
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
ainsum += sir[3];
} else {
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
aoutsum += sir[3];
}
if (i < hm) {
@ -886,16 +904,19 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env
pixels[offset] = dv[rsum];
pixels[offset + 1] = dv[gsum];
pixels[offset + 2] = dv[bsum];
pixels[offset + 3] = dv[asum];
rsum -= routsum;
gsum -= goutsum;
bsum -= boutsum;
asum -= aoutsum;
stackstart = stackpointer - radius + div;
sir = &stack[(stackstart % div) * 3];
sir = &stack[(stackstart % div) * 4];
routsum -= sir[0];
goutsum -= sir[1];
boutsum -= sir[2];
aoutsum -= sir[3];
if (x == 0) {
vmin[y] = (MIN(y + r1, hm)) * w;
@ -905,25 +926,30 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env
sir[0] = r[p];
sir[1] = g[p];
sir[2] = b[p];
sir[3] = a[p];
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
ainsum += sir[3];
rsum += rinsum;
gsum += ginsum;
bsum += binsum;
asum += ainsum;
stackpointer = (stackpointer + 1) % div;
sir = &stack[stackpointer * 3];
sir = &stack[stackpointer * 4];
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
aoutsum += sir[3];
rinsum -= sir[0];
ginsum -= sir[1];
binsum -= sir[2];
ainsum -= sir[3];
yi += w;
}
@ -932,6 +958,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_stackBlurBitmap(JNIEnv* env
free(r);
free(g);
free(b);
free(a);
free(dv);
AndroidBitmap_unlockPixels(env, bitmap);
}

View File

@ -9,6 +9,7 @@
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <libtgvoip/client/android/org_telegram_messenger_voip_TgVoip.h>
#include "image.h"
#include "libtgvoip/client/android/tg_voip_jni.h"
@ -35,9 +36,13 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
if (registerNativeTgNetFunctions(vm, env) != JNI_TRUE) {
return -1;
}
/* if (tgvoipOnJniLoad(vm, env) != JNI_TRUE) {
return -1;
}*/
//tonLibOnLoad(vm, env);
tgvoipRegisterNatives(env);
//tgvoipRegisterNatives(env);
return JNI_VERSION_1_6;
}

View File

@ -1,13 +1,25 @@
LOCAL_PATH := $(call my-dir)
LOCAL_MODULE := voip
LOCAL_MODULE := tgvoip${TGVOIP_NATIVE_VERSION}
LOCAL_CPPFLAGS := -Wall -std=c++11 -DANDROID -finline-functions -ffast-math -Os -fno-strict-aliasing -O3 -frtti -D__STDC_LIMIT_MACROS -Wno-unknown-pragmas
LOCAL_CFLAGS := -O3 -DUSE_KISS_FFT -fexceptions -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_POSIX -DWEBRTC_ANDROID -D__STDC_LIMIT_MACROS -DFIXED_POINT -DWEBRTC_NS_FLOAT
LOCAL_EXPORT_LDLIBS := -llog -lOpenSLES
LOCAL_CPPFLAGS += -DBSD=1 -funroll-loops
LOCAL_CFLAGS := -O3 -DUSE_KISS_FFT -fexceptions -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_POSIX -DWEBRTC_ANDROID -D__STDC_LIMIT_MACROS -DWEBRTC_NS_FLOAT
LOCAL_CFLAGS += -DNULL=0 -DSOCKLEN_T=socklen_t -DLOCALE_NOT_USED -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
LOCAL_CFLAGS += -Drestrict='' -D__EMX__ -DOPUS_BUILD -DFIXED_POINT -DUSE_ALLOCA -DHAVE_LRINT -DHAVE_LRINTF -fno-math-errno
LOCAL_LDLIBS := -llog -lOpenSLES
LOCAL_STATIC_LIBRARIES := crypto
MY_DIR := libtgvoip
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../opus/include $(LOCAL_PATH)/../boringssl/include/ $(LOCAL_PATH)/webrtc_dsp/
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/../opus/include \
$(LOCAL_PATH)/../opus/silk \
$(LOCAL_PATH)/../opus/silk/fixed \
$(LOCAL_PATH)/../opus/celt \
$(LOCAL_PATH)/../opus/ \
$(LOCAL_PATH)/../opus/opusfile \
$(LOCAL_PATH)/../boringssl/include/ \
$(LOCAL_PATH)/webrtc_dsp/
ifeq ($(TARGET_ARCH_ABI),$(filter $(TARGET_ARCH_ABI),armeabi-v7a arm64-v8a))
CC_NEON := cc.neon
@ -20,6 +32,7 @@ LOCAL_CFLAGS += $(TGVOIP_ADDITIONAL_CFLAGS)
LOCAL_SRC_FILES := \
./logging.cpp \
./TgVoip.cpp \
./VoIPController.cpp \
./VoIPGroupController.cpp \
./Buffers.cpp \
@ -50,6 +63,7 @@ LOCAL_SRC_FILES := \
./video/ScreamCongestionController.cpp \
./os/android/VideoSourceAndroid.cpp \
./os/android/VideoRendererAndroid.cpp \
./client/android/org_telegram_messenger_voip_TgVoip.cpp \
./client/android/tg_voip_jni.cpp
# WebRTC signal processing
@ -271,7 +285,6 @@ LOCAL_SRC_FILES += \
./webrtc_dsp/common_audio/resampler/sinc_resampler.cc \
./webrtc_dsp/common_audio/resampler/sinusoidal_linear_chirp_source.cc \
./webrtc_dsp/common_audio/wav_file.cc \
./webrtc_dsp/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.c \
./webrtc_dsp/common_audio/third_party/fft4g/fft4g.c \
./webrtc_dsp/common_audio/audio_converter.cc \
./webrtc_dsp/common_audio/real_fourier.cc \
@ -289,7 +302,6 @@ LOCAL_SRC_FILES += \
./webrtc_dsp/common_audio/signal_processing/sqrt_of_one_minus_x_squared.c \
./webrtc_dsp/common_audio/signal_processing/downsample_fast.c \
./webrtc_dsp/common_audio/signal_processing/splitting_filter1.c \
./webrtc_dsp/common_audio/signal_processing/filter_ar_fast_q12.c \
./webrtc_dsp/common_audio/signal_processing/spl_init.c \
./webrtc_dsp/common_audio/signal_processing/lpc_to_refl_coef.c \
./webrtc_dsp/common_audio/signal_processing/cross_correlation.c \
@ -304,7 +316,6 @@ LOCAL_SRC_FILES += \
./webrtc_dsp/common_audio/signal_processing/resample_fractional.c \
./webrtc_dsp/common_audio/signal_processing/real_fft.c \
./webrtc_dsp/common_audio/signal_processing/ilbc_specific_functions.c \
./webrtc_dsp/common_audio/signal_processing/complex_bit_reverse.c \
./webrtc_dsp/common_audio/signal_processing/randomization_functions.c \
./webrtc_dsp/common_audio/signal_processing/copy_set_operations.c \
./webrtc_dsp/common_audio/signal_processing/resample_by_2.c \
@ -337,6 +348,11 @@ LOCAL_SRC_FILES += \
./webrtc_dsp/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor_arm.S.neon \
./webrtc_dsp/common_audio/signal_processing/complex_bit_reverse_arm.S.neon \
./webrtc_dsp/common_audio/signal_processing/filter_ar_fast_q12_armv7.S.neon
else
LOCAL_SRC_FILES += \
./webrtc_dsp/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.c \
./webrtc_dsp/common_audio/signal_processing/complex_bit_reverse.c \
./webrtc_dsp/common_audio/signal_processing/filter_ar_fast_q12.c
endif
ifeq ($(TARGET_ARCH_ABI),$(filter $(TARGET_ARCH_ABI),x86 x86_64))
@ -347,4 +363,198 @@ LOCAL_SRC_FILES += \
./webrtc_dsp/common_audio/resampler/sinc_resampler_sse.cc
endif
include $(BUILD_STATIC_LIBRARY)
# Opus
LOCAL_SRC_FILES += \
./../opus/src/opus.c \
./../opus/src/opus_decoder.c \
./../opus/src/opus_encoder.c \
./../opus/src/opus_multistream.c \
./../opus/src/opus_multistream_encoder.c \
./../opus/src/opus_multistream_decoder.c \
./../opus/src/repacketizer.c \
./../opus/src/analysis.c \
./../opus/src/mlp.c \
./../opus/src/mlp_data.c \
./../opus/src/opus_projection_encoder.c \
./../opus/src/opus_projection_decoder.c \
./../opus/src/mapping_matrix.c
ifeq ($(TARGET_ARCH_ABI),$(filter $(TARGET_ARCH_ABI),armeabi-v7a arm64-v8a))
LOCAL_ARM_MODE := arm
LOCAL_CPPFLAGS += -DLIBYUV_NEON
LOCAL_CFLAGS += -DLIBYUV_NEON
LOCAL_CFLAGS += -DOPUS_HAVE_RTCD -DOPUS_ARM_ASM
LOCAL_SRC_FILES += \
./../opus/celt/arm/celt_neon_intr.c.neon \
./../opus/celt/arm/pitch_neon_intr.c.neon \
./../opus/silk/arm/NSQ_neon.c.neon \
./../opus/silk/arm/arm_silk_map.c \
./../opus/silk/arm/LPC_inv_pred_gain_neon_intr.c.neon \
./../opus/silk/arm/NSQ_del_dec_neon_intr.c.neon \
./../opus/silk/arm/biquad_alt_neon_intr.c.neon \
./../opus/silk/fixed/arm/warped_autocorrelation_FIX_neon_intr.c.neon
# LOCAL_SRC_FILES += ./../opus/celt/arm/celt_pitch_xcorr_arm-gnu.S
else
ifeq ($(TARGET_ARCH_ABI),x86)
LOCAL_CFLAGS += -Dx86fix
LOCAL_CPPFLAGS += -Dx86fix
LOCAL_ARM_MODE := arm
# LOCAL_SRC_FILES += \
# ./libyuv/source/row_x86.asm
# LOCAL_SRC_FILES += \
# ./../opus/celt/x86/celt_lpc_sse.c \
# ./../opus/celt/x86/pitch_sse.c \
# ./../opus/celt/x86/pitch_sse2.c \
# ./../opus/celt/x86/pitch_sse4_1.c \
# ./../opus/celt/x86/vq_sse2.c \
# ./../opus/celt/x86/x86_celt_map.c \
# ./../opus/celt/x86/x86cpu.c \
# ./../opus/silk/fixed/x86/burg_modified_FIX_sse.c \
# ./../opus/silk/fixed/x86/vector_ops_FIX_sse.c \
# ./../opus/silk/x86/NSQ_del_dec_sse.c \
# ./../opus/silk/x86/NSQ_sse.c \
# ./../opus/silk/x86/VAD_sse.c \
# ./../opus/silk/x86/VQ_WMat_sse.c \
# ./../opus/silk/x86/x86_silk_map.c
endif
endif
LOCAL_SRC_FILES += \
./../opus/silk/CNG.c \
./../opus/silk/code_signs.c \
./../opus/silk/init_decoder.c \
./../opus/silk/decode_core.c \
./../opus/silk/decode_frame.c \
./../opus/silk/decode_parameters.c \
./../opus/silk/decode_indices.c \
./../opus/silk/decode_pulses.c \
./../opus/silk/decoder_set_fs.c \
./../opus/silk/dec_API.c \
./../opus/silk/enc_API.c \
./../opus/silk/encode_indices.c \
./../opus/silk/encode_pulses.c \
./../opus/silk/gain_quant.c \
./../opus/silk/interpolate.c \
./../opus/silk/LP_variable_cutoff.c \
./../opus/silk/NLSF_decode.c \
./../opus/silk/NSQ.c \
./../opus/silk/NSQ_del_dec.c \
./../opus/silk/PLC.c \
./../opus/silk/shell_coder.c \
./../opus/silk/tables_gain.c \
./../opus/silk/tables_LTP.c \
./../opus/silk/tables_NLSF_CB_NB_MB.c \
./../opus/silk/tables_NLSF_CB_WB.c \
./../opus/silk/tables_other.c \
./../opus/silk/tables_pitch_lag.c \
./../opus/silk/tables_pulses_per_block.c \
./../opus/silk/VAD.c \
./../opus/silk/control_audio_bandwidth.c \
./../opus/silk/quant_LTP_gains.c \
./../opus/silk/VQ_WMat_EC.c \
./../opus/silk/HP_variable_cutoff.c \
./../opus/silk/NLSF_encode.c \
./../opus/silk/NLSF_VQ.c \
./../opus/silk/NLSF_unpack.c \
./../opus/silk/NLSF_del_dec_quant.c \
./../opus/silk/process_NLSFs.c \
./../opus/silk/stereo_LR_to_MS.c \
./../opus/silk/stereo_MS_to_LR.c \
./../opus/silk/check_control_input.c \
./../opus/silk/control_SNR.c \
./../opus/silk/init_encoder.c \
./../opus/silk/control_codec.c \
./../opus/silk/A2NLSF.c \
./../opus/silk/ana_filt_bank_1.c \
./../opus/silk/biquad_alt.c \
./../opus/silk/bwexpander_32.c \
./../opus/silk/bwexpander.c \
./../opus/silk/debug.c \
./../opus/silk/decode_pitch.c \
./../opus/silk/inner_prod_aligned.c \
./../opus/silk/lin2log.c \
./../opus/silk/log2lin.c \
./../opus/silk/LPC_analysis_filter.c \
./../opus/silk/LPC_inv_pred_gain.c \
./../opus/silk/table_LSF_cos.c \
./../opus/silk/NLSF2A.c \
./../opus/silk/NLSF_stabilize.c \
./../opus/silk/NLSF_VQ_weights_laroia.c \
./../opus/silk/pitch_est_tables.c \
./../opus/silk/resampler.c \
./../opus/silk/resampler_down2_3.c \
./../opus/silk/resampler_down2.c \
./../opus/silk/resampler_private_AR2.c \
./../opus/silk/resampler_private_down_FIR.c \
./../opus/silk/resampler_private_IIR_FIR.c \
./../opus/silk/resampler_private_up2_HQ.c \
./../opus/silk/resampler_rom.c \
./../opus/silk/sigm_Q15.c \
./../opus/silk/sort.c \
./../opus/silk/sum_sqr_shift.c \
./../opus/silk/stereo_decode_pred.c \
./../opus/silk/stereo_encode_pred.c \
./../opus/silk/stereo_find_predictor.c \
./../opus/silk/stereo_quant_pred.c \
./../opus/silk/LPC_fit.c
LOCAL_SRC_FILES += \
./../opus/silk/fixed/LTP_analysis_filter_FIX.c \
./../opus/silk/fixed/LTP_scale_ctrl_FIX.c \
./../opus/silk/fixed/corrMatrix_FIX.c \
./../opus/silk/fixed/encode_frame_FIX.c \
./../opus/silk/fixed/find_LPC_FIX.c \
./../opus/silk/fixed/find_LTP_FIX.c \
./../opus/silk/fixed/find_pitch_lags_FIX.c \
./../opus/silk/fixed/find_pred_coefs_FIX.c \
./../opus/silk/fixed/noise_shape_analysis_FIX.c \
./../opus/silk/fixed/process_gains_FIX.c \
./../opus/silk/fixed/regularize_correlations_FIX.c \
./../opus/silk/fixed/residual_energy16_FIX.c \
./../opus/silk/fixed/residual_energy_FIX.c \
./../opus/silk/fixed/warped_autocorrelation_FIX.c \
./../opus/silk/fixed/apply_sine_window_FIX.c \
./../opus/silk/fixed/autocorr_FIX.c \
./../opus/silk/fixed/burg_modified_FIX.c \
./../opus/silk/fixed/k2a_FIX.c \
./../opus/silk/fixed/k2a_Q16_FIX.c \
./../opus/silk/fixed/pitch_analysis_core_FIX.c \
./../opus/silk/fixed/vector_ops_FIX.c \
./../opus/silk/fixed/schur64_FIX.c \
./../opus/silk/fixed/schur_FIX.c
LOCAL_SRC_FILES += \
./../opus/celt/bands.c \
./../opus/celt/celt.c \
./../opus/celt/celt_encoder.c \
./../opus/celt/celt_decoder.c \
./../opus/celt/cwrs.c \
./../opus/celt/entcode.c \
./../opus/celt/entdec.c \
./../opus/celt/entenc.c \
./../opus/celt/kiss_fft.c \
./../opus/celt/laplace.c \
./../opus/celt/mathops.c \
./../opus/celt/mdct.c \
./../opus/celt/modes.c \
./../opus/celt/pitch.c \
./../opus/celt/celt_lpc.c \
./../opus/celt/quant_bands.c \
./../opus/celt/rate.c \
./../opus/celt/vq.c \
./../opus/celt/arm/armcpu.c \
./../opus/celt/arm/arm_celt_map.c
LOCAL_SRC_FILES += \
./../opus/ogg/bitwise.c \
./../opus/ogg/framing.c \
./../opus/opusfile/info.c \
./../opus/opusfile/internal.c \
./../opus/opusfile/opusfile.c \
./../opus/opusfile/stream.c
include $(BUILD_SHARED_LIBRARY)

View File

@ -96,6 +96,7 @@ EchoCanceller::~EchoCanceller(){
#ifndef TGVOIP_NO_DSP
delete apm;
delete audioFrame;
delete farendBufferPool;
#endif
}

View File

@ -4,7 +4,8 @@ CFLAGS = -Wall -DHAVE_CONFIG_H -Wno-unknown-pragmas
lib_LTLIBRARIES = libtgvoip.la
SRC = VoIPController.cpp \
SRC = TgVoip.cpp \
VoIPController.cpp \
Buffers.cpp \
CongestionControl.cpp \
EchoCanceller.cpp \
@ -29,6 +30,7 @@ video/ScreamCongestionController.cpp \
json11.cpp
TGVOIP_HDRS = \
TgVoip.h \
VoIPController.h \
Buffers.h \
BlockingQueue.h \

View File

@ -808,11 +808,11 @@ am__installdirs = "$(DESTDIR)$(libdir)" \
"$(DESTDIR)$(tgvoipincludedir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
libtgvoip_la_LIBADD =
am__libtgvoip_la_SOURCES_DIST = VoIPController.cpp Buffers.cpp \
CongestionControl.cpp EchoCanceller.cpp JitterBuffer.cpp \
logging.cpp MediaStreamItf.cpp MessageThread.cpp \
NetworkSocket.cpp OpusDecoder.cpp OpusEncoder.cpp \
PacketReassembler.cpp VoIPGroupController.cpp \
am__libtgvoip_la_SOURCES_DIST = TgVoip.cpp VoIPController.cpp \
Buffers.cpp CongestionControl.cpp EchoCanceller.cpp \
JitterBuffer.cpp logging.cpp MediaStreamItf.cpp \
MessageThread.cpp NetworkSocket.cpp OpusDecoder.cpp \
OpusEncoder.cpp PacketReassembler.cpp VoIPGroupController.cpp \
VoIPServerConfig.cpp audio/AudioIO.cpp audio/AudioInput.cpp \
audio/AudioOutput.cpp audio/Resampler.cpp \
os/posix/NetworkSocketPosix.cpp video/VideoSource.cpp \
@ -1421,16 +1421,16 @@ am__libtgvoip_la_SOURCES_DIST = VoIPController.cpp Buffers.cpp \
webrtc_dsp/common_audio/vad/include/webrtc_vad.h \
webrtc_dsp/common_audio/vad/vad_gmm.h \
webrtc_dsp/common_audio/vad/vad_sp.h \
webrtc_dsp/common_audio/vad/vad_filterbank.h VoIPController.h \
Buffers.h BlockingQueue.h PrivateDefines.h CongestionControl.h \
EchoCanceller.h JitterBuffer.h logging.h threading.h \
MediaStreamItf.h MessageThread.h NetworkSocket.h OpusDecoder.h \
OpusEncoder.h PacketReassembler.h VoIPServerConfig.h \
audio/AudioIO.h audio/AudioInput.h audio/AudioOutput.h \
audio/Resampler.h os/posix/NetworkSocketPosix.h \
video/VideoSource.h video/VideoRenderer.h \
video/ScreamCongestionController.h json11.hpp utils.h \
os/darwin/AudioInputAudioUnit.h \
webrtc_dsp/common_audio/vad/vad_filterbank.h TgVoip.h \
VoIPController.h Buffers.h BlockingQueue.h PrivateDefines.h \
CongestionControl.h EchoCanceller.h JitterBuffer.h logging.h \
threading.h MediaStreamItf.h MessageThread.h NetworkSocket.h \
OpusDecoder.h OpusEncoder.h PacketReassembler.h \
VoIPServerConfig.h audio/AudioIO.h audio/AudioInput.h \
audio/AudioOutput.h audio/Resampler.h \
os/posix/NetworkSocketPosix.h video/VideoSource.h \
video/VideoRenderer.h video/ScreamCongestionController.h \
json11.hpp utils.h os/darwin/AudioInputAudioUnit.h \
os/darwin/AudioOutputAudioUnit.h os/darwin/AudioUnitIO.h \
os/darwin/AudioInputAudioUnitOSX.h \
os/darwin/AudioOutputAudioUnitOSX.h os/darwin/DarwinSpecific.h \
@ -1736,12 +1736,12 @@ am__dirstamp = $(am__leading_dot)dirstamp
@ENABLE_DSP_TRUE@@TARGET_CPU_ARM_FALSE@am__objects_10 = webrtc_dsp/common_audio/signal_processing/complex_bit_reverse.lo \
@ENABLE_DSP_TRUE@@TARGET_CPU_ARM_FALSE@ webrtc_dsp/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.lo
am__objects_11 =
am__objects_12 = VoIPController.lo Buffers.lo CongestionControl.lo \
EchoCanceller.lo JitterBuffer.lo logging.lo MediaStreamItf.lo \
MessageThread.lo NetworkSocket.lo OpusDecoder.lo \
OpusEncoder.lo PacketReassembler.lo VoIPGroupController.lo \
VoIPServerConfig.lo audio/AudioIO.lo audio/AudioInput.lo \
audio/AudioOutput.lo audio/Resampler.lo \
am__objects_12 = TgVoip.lo VoIPController.lo Buffers.lo \
CongestionControl.lo EchoCanceller.lo JitterBuffer.lo \
logging.lo MediaStreamItf.lo MessageThread.lo NetworkSocket.lo \
OpusDecoder.lo OpusEncoder.lo PacketReassembler.lo \
VoIPGroupController.lo VoIPServerConfig.lo audio/AudioIO.lo \
audio/AudioInput.lo audio/AudioOutput.lo audio/Resampler.lo \
os/posix/NetworkSocketPosix.lo video/VideoSource.lo \
video/VideoRenderer.lo video/ScreamCongestionController.lo \
json11.lo $(am__objects_1) $(am__objects_2) $(am__objects_3) \
@ -1777,7 +1777,7 @@ am__depfiles_remade = ./$(DEPDIR)/Buffers.Plo \
./$(DEPDIR)/MediaStreamItf.Plo ./$(DEPDIR)/MessageThread.Plo \
./$(DEPDIR)/NetworkSocket.Plo ./$(DEPDIR)/OpusDecoder.Plo \
./$(DEPDIR)/OpusEncoder.Plo ./$(DEPDIR)/PacketReassembler.Plo \
./$(DEPDIR)/VoIPController.Plo \
./$(DEPDIR)/TgVoip.Plo ./$(DEPDIR)/VoIPController.Plo \
./$(DEPDIR)/VoIPGroupController.Plo \
./$(DEPDIR)/VoIPServerConfig.Plo ./$(DEPDIR)/json11.Plo \
./$(DEPDIR)/logging.Plo \
@ -2153,8 +2153,8 @@ am__can_run_installinfo = \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
am__nobase_tgvoipinclude_HEADERS_DIST = VoIPController.h Buffers.h \
BlockingQueue.h PrivateDefines.h CongestionControl.h \
am__nobase_tgvoipinclude_HEADERS_DIST = TgVoip.h VoIPController.h \
Buffers.h BlockingQueue.h PrivateDefines.h CongestionControl.h \
EchoCanceller.h JitterBuffer.h logging.h threading.h \
MediaStreamItf.h MessageThread.h NetworkSocket.h OpusDecoder.h \
OpusEncoder.h PacketReassembler.h VoIPServerConfig.h \
@ -2347,7 +2347,7 @@ top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
AUTOMAKE_OPTIONS = foreign
lib_LTLIBRARIES = libtgvoip.la
SRC = VoIPController.cpp Buffers.cpp CongestionControl.cpp \
SRC = TgVoip.cpp VoIPController.cpp Buffers.cpp CongestionControl.cpp \
EchoCanceller.cpp JitterBuffer.cpp logging.cpp \
MediaStreamItf.cpp MessageThread.cpp NetworkSocket.cpp \
OpusDecoder.cpp OpusEncoder.cpp PacketReassembler.cpp \
@ -2359,7 +2359,7 @@ SRC = VoIPController.cpp Buffers.cpp CongestionControl.cpp \
$(am__append_10) $(am__append_12) $(am__append_14) \
$(am__append_16) $(am__append_18) $(am__append_21) \
$(am__append_22) $(am__append_23)
TGVOIP_HDRS = VoIPController.h Buffers.h BlockingQueue.h \
TGVOIP_HDRS = TgVoip.h VoIPController.h Buffers.h BlockingQueue.h \
PrivateDefines.h CongestionControl.h EchoCanceller.h \
JitterBuffer.h logging.h threading.h MediaStreamItf.h \
MessageThread.h NetworkSocket.h OpusDecoder.h OpusEncoder.h \
@ -3638,6 +3638,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OpusDecoder.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OpusEncoder.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PacketReassembler.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TgVoip.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/VoIPController.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/VoIPGroupController.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/VoIPServerConfig.Plo@am__quote@ # am--include-marker
@ -4506,6 +4507,7 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/OpusDecoder.Plo
-rm -f ./$(DEPDIR)/OpusEncoder.Plo
-rm -f ./$(DEPDIR)/PacketReassembler.Plo
-rm -f ./$(DEPDIR)/TgVoip.Plo
-rm -f ./$(DEPDIR)/VoIPController.Plo
-rm -f ./$(DEPDIR)/VoIPGroupController.Plo
-rm -f ./$(DEPDIR)/VoIPServerConfig.Plo
@ -4869,6 +4871,7 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/OpusDecoder.Plo
-rm -f ./$(DEPDIR)/OpusEncoder.Plo
-rm -f ./$(DEPDIR)/PacketReassembler.Plo
-rm -f ./$(DEPDIR)/TgVoip.Plo
-rm -f ./$(DEPDIR)/VoIPController.Plo
-rm -f ./$(DEPDIR)/VoIPGroupController.Plo
-rm -f ./$(DEPDIR)/VoIPServerConfig.Plo

View File

@ -364,8 +364,8 @@ bool NetworkSocketTCPObfuscated::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;
this->username=std::move(username);
this->password=std::move(password);
connectedAddress=NULL;
}

View File

@ -0,0 +1,433 @@
#include "TgVoip.h"
#include "VoIPController.h"
#include "VoIPServerConfig.h"
#include <stdarg.h>
#ifndef TGVOIP_USE_CUSTOM_CRYPTO
extern "C" {
#include <openssl/sha.h>
#include <openssl/aes.h>
//#include <openssl/modes.h>
#include <openssl/rand.h>
}
void tgvoip_openssl_aes_ige_encrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){
AES_KEY akey;
AES_set_encrypt_key(key, 32*8, &akey);
AES_ige_encrypt(in, out, length, &akey, iv, AES_ENCRYPT);
}
void tgvoip_openssl_aes_ige_decrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){
AES_KEY akey;
AES_set_decrypt_key(key, 32*8, &akey);
AES_ige_encrypt(in, out, length, &akey, iv, AES_DECRYPT);
}
void tgvoip_openssl_rand_bytes(uint8_t* buffer, size_t len){
RAND_bytes(buffer, len);
}
void tgvoip_openssl_sha1(uint8_t* msg, size_t len, uint8_t* output){
SHA1(msg, len, output);
}
void tgvoip_openssl_sha256(uint8_t* msg, size_t len, uint8_t* output){
SHA256(msg, len, output);
}
void tgvoip_openssl_aes_ctr_encrypt(uint8_t* inout, size_t length, uint8_t* key, uint8_t* iv, uint8_t* ecount, uint32_t* num){
AES_KEY akey;
AES_set_encrypt_key(key, 32*8, &akey);
AES_ctr128_encrypt(inout, inout, length, &akey, iv, ecount, num);
}
void tgvoip_openssl_aes_cbc_encrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){
AES_KEY akey;
AES_set_encrypt_key(key, 256, &akey);
AES_cbc_encrypt(in, out, length, &akey, iv, AES_ENCRYPT);
}
void tgvoip_openssl_aes_cbc_decrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){
AES_KEY akey;
AES_set_decrypt_key(key, 256, &akey);
AES_cbc_encrypt(in, out, length, &akey, iv, AES_DECRYPT);
}
tgvoip::CryptoFunctions tgvoip::VoIPController::crypto={
tgvoip_openssl_rand_bytes,
tgvoip_openssl_sha1,
tgvoip_openssl_sha256,
tgvoip_openssl_aes_ige_encrypt,
tgvoip_openssl_aes_ige_decrypt,
tgvoip_openssl_aes_ctr_encrypt,
tgvoip_openssl_aes_cbc_encrypt,
tgvoip_openssl_aes_cbc_decrypt
};
#else
struct TgVoipCrypto {
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);
void (*aes_cbc_encrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv);
void (*aes_cbc_decrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv);
};
tgvoip::CryptoFunctions tgvoip::VoIPController::crypto; // set it yourself upon initialization
#endif
class TgVoipImpl : public TgVoip {
private:
tgvoip::VoIPController *controller_;
std::function<void(TgVoipState)> onStateUpdated_;
std::function<void(int)> onSignalBarsUpdated_;
public:
TgVoipImpl(
std::vector<TgVoipEndpoint> const &endpoints,
TgVoipPersistentState const &persistentState,
std::unique_ptr<TgVoipProxy> const &proxy,
TgVoipConfig const &config,
TgVoipEncryptionKey const &encryptionKey,
TgVoipNetworkType initialNetworkType
#ifdef TGVOIP_USE_CUSTOM_CRYPTO
,
TgVoipCrypto const &crypto
#endif
#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO
,
TgVoipAudioDataCallbacks const &audioDataCallbacks
#endif
) {
#ifdef TGVOIP_USE_CUSTOM_CRYPTO
tgvoip::VoIPController::crypto.sha1 = crypto.sha1;
tgvoip::VoIPController::crypto.sha256 = crypto.sha256;
tgvoip::VoIPController::crypto.rand_bytes = crypto.rand_bytes;
tgvoip::VoIPController::crypto.aes_ige_encrypt = crypto.aes_ige_encrypt;
tgvoip::VoIPController::crypto.aes_ige_decrypt = crypto.aes_ige_decrypt;
tgvoip::VoIPController::crypto.aes_ctr_encrypt = crypto.aes_ctr_encrypt;
#endif
controller_ = new tgvoip::VoIPController();
controller_->implData = this;
#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO
controller_->SetAudioDataCallbacks(audioDataCallbacks.input, audioDataCallbacks.output, audioDataCallbacks.preprocessed);
#endif
controller_->SetPersistentState(persistentState.value);
if (proxy != nullptr) {
controller_->SetProxy(tgvoip::PROXY_SOCKS5, proxy->host, proxy->port, proxy->login, proxy->password);
}
auto callbacks = tgvoip::VoIPController::Callbacks();
callbacks.connectionStateChanged = &TgVoipImpl::controllerStateCallback;
callbacks.groupCallKeyReceived = nullptr;
callbacks.groupCallKeySent = nullptr;
callbacks.signalBarCountChanged = &TgVoipImpl::signalBarsCallback;
callbacks.upgradeToGroupCallRequested = nullptr;
controller_->SetCallbacks(callbacks);
std::vector<tgvoip::Endpoint> mappedEndpoints;
for (auto endpoint : endpoints) {
bool isIpv6 = false;
struct in6_addr addrIpV6;
if (inet_pton(AF_INET6, endpoint.host.c_str(), &addrIpV6)) {
isIpv6 = true;
}
tgvoip::Endpoint::Type mappedType;
switch (endpoint.type) {
case TgVoipEndpointType::UdpRelay:
mappedType = tgvoip::Endpoint::Type::UDP_RELAY;
break;
case TgVoipEndpointType::Lan:
mappedType = tgvoip::Endpoint::Type::UDP_P2P_LAN;
break;
case TgVoipEndpointType::Inet:
mappedType = tgvoip::Endpoint::Type::UDP_P2P_INET;
break;
case TgVoipEndpointType::TcpRelay:
mappedType = tgvoip::Endpoint::Type::TCP_RELAY;
break;
default:
mappedType = tgvoip::Endpoint::Type::UDP_RELAY;
break;
}
tgvoip::IPv4Address address(isIpv6 ? std::string() : endpoint.host);
tgvoip::IPv6Address addressv6(isIpv6 ? endpoint.host : std::string());
mappedEndpoints.emplace_back(endpoint.endpointId, endpoint.port, address, addressv6, mappedType, endpoint.peerTag);
}
int mappedDataSaving;
switch (config.dataSaving) {
case TgVoipDataSaving::Mobile:
mappedDataSaving = tgvoip::DATA_SAVING_MOBILE;
break;
case TgVoipDataSaving::Always:
mappedDataSaving = tgvoip::DATA_SAVING_ALWAYS;
break;
default:
mappedDataSaving = tgvoip::DATA_SAVING_NEVER;
break;
}
tgvoip::VoIPController::Config mappedConfig(
config.initializationTimeout,
config.receiveTimeout,
mappedDataSaving,
config.enableAEC,
config.enableNS,
config.enableAGC,
config.enableCallUpgrade
);
mappedConfig.logFilePath = config.logPath;
mappedConfig.statsDumpFilePath = "";
controller_->SetConfig(mappedConfig);
setNetworkType(initialNetworkType);
std::vector<uint8_t> encryptionKeyValue = encryptionKey.value;
controller_->SetEncryptionKey((char *)(encryptionKeyValue.data()), encryptionKey.isOutgoing);
controller_->SetRemoteEndpoints(mappedEndpoints, config.enableP2P, config.maxApiLayer);
controller_->Start();
controller_->Connect();
}
~TgVoipImpl() override = default;
void setOnStateUpdated(std::function<void(TgVoipState)> onStateUpdated) override {
onStateUpdated_ = onStateUpdated;
}
void setOnSignalBarsUpdated(std::function<void(int)> onSignalBarsUpdated) override {
onSignalBarsUpdated_ = onSignalBarsUpdated;
}
void setNetworkType(TgVoipNetworkType networkType) override {
int mappedType;
switch (networkType) {
case TgVoipNetworkType::Unknown:
mappedType = tgvoip::NET_TYPE_UNKNOWN;
break;
case TgVoipNetworkType::Gprs:
mappedType = tgvoip::NET_TYPE_GPRS;
break;
case TgVoipNetworkType::Edge:
mappedType = tgvoip::NET_TYPE_EDGE;
break;
case TgVoipNetworkType::ThirdGeneration:
mappedType = tgvoip::NET_TYPE_3G;
break;
case TgVoipNetworkType::Hspa:
mappedType = tgvoip::NET_TYPE_HSPA;
break;
case TgVoipNetworkType::Lte:
mappedType = tgvoip::NET_TYPE_LTE;
break;
case TgVoipNetworkType::WiFi:
mappedType = tgvoip::NET_TYPE_WIFI;
break;
case TgVoipNetworkType::Ethernet:
mappedType = tgvoip::NET_TYPE_ETHERNET;
break;
case TgVoipNetworkType::OtherHighSpeed:
mappedType = tgvoip::NET_TYPE_OTHER_HIGH_SPEED;
break;
case TgVoipNetworkType::OtherLowSpeed:
mappedType = tgvoip::NET_TYPE_OTHER_LOW_SPEED;
break;
case TgVoipNetworkType::OtherMobile:
mappedType = tgvoip::NET_TYPE_OTHER_MOBILE;
break;
case TgVoipNetworkType::Dialup:
mappedType = tgvoip::NET_TYPE_DIALUP;
break;
default:
mappedType = tgvoip::NET_TYPE_UNKNOWN;
break;
}
controller_->SetNetworkType(mappedType);
}
void setMuteMicrophone(bool muteMicrophone) override {
controller_->SetMicMute(muteMicrophone);
}
void setAudioOutputGainControlEnabled(bool enabled) override {
controller_->SetAudioOutputGainControlEnabled(enabled);
}
void setEchoCancellationStrength(int strength) override {
controller_->SetEchoCancellationStrength(strength);
}
std::string getLastError() override {
switch (controller_->GetLastError()) {
case tgvoip::ERROR_INCOMPATIBLE: return "ERROR_INCOMPATIBLE";
case tgvoip::ERROR_TIMEOUT: return "ERROR_TIMEOUT";
case tgvoip::ERROR_AUDIO_IO: return "ERROR_AUDIO_IO";
case tgvoip::ERROR_PROXY: return "ERROR_PROXY";
default: return "ERROR_UNKNOWN";
}
}
std::string getDebugInfo() override {
return controller_->GetDebugString();
}
int64_t getPreferredRelayId() override {
return controller_->GetPreferredRelayID();
}
TgVoipTrafficStats getTrafficStats() override {
tgvoip::VoIPController::TrafficStats stats;
controller_->GetStats(&stats);
return {
.bytesSentWifi = stats.bytesSentWifi,
.bytesReceivedWifi = stats.bytesRecvdWifi,
.bytesSentMobile = stats.bytesSentMobile,
.bytesReceivedMobile = stats.bytesRecvdMobile
};
}
TgVoipPersistentState getPersistentState() override {
return {controller_->GetPersistentState()};
}
TgVoipFinalState stop() override {
controller_->Stop();
TgVoipFinalState finalState = {
.trafficStats = getTrafficStats(),
.persistentState = getPersistentState(),
.debugLog = controller_->GetDebugLog(),
.isRatingSuggested = controller_->NeedRate()
};
delete controller_;
controller_ = nullptr;
return finalState;
}
static void controllerStateCallback(tgvoip::VoIPController *controller, int state) {
auto *self = (TgVoipImpl *) controller->implData;
if (self->onStateUpdated_) {
TgVoipState mappedState;
switch (state) {
case tgvoip::STATE_WAIT_INIT:
mappedState = TgVoipState::WaitInit;
break;
case tgvoip::STATE_WAIT_INIT_ACK:
mappedState = TgVoipState::WaitInitAck;
break;
case tgvoip::STATE_ESTABLISHED:
mappedState = TgVoipState::Estabilished;
break;
case tgvoip::STATE_FAILED:
mappedState = TgVoipState::Failed;
break;
case tgvoip::STATE_RECONNECTING:
mappedState = TgVoipState::Reconnecting;
break;
default:
mappedState = TgVoipState::Estabilished;
break;
}
self->onStateUpdated_(mappedState);
}
}
static void signalBarsCallback(tgvoip::VoIPController *controller, int signalBars) {
auto *self = (TgVoipImpl *) controller->implData;
if (self->onSignalBarsUpdated_) {
self->onSignalBarsUpdated_(signalBars);
}
}
};
std::function<void(std::string const &)> globalLoggingFunction;
void __tgvoip_call_tglog(const char *format, ...){
va_list vaArgs;
va_start(vaArgs, format);
va_list vaCopy;
va_copy(vaCopy, vaArgs);
const int length = std::vsnprintf(nullptr, 0, format, vaCopy);
va_end(vaCopy);
std::vector<char> zc(length + 1);
std::vsnprintf(zc.data(), zc.size(), format, vaArgs);
va_end(vaArgs);
if (globalLoggingFunction != nullptr) {
globalLoggingFunction(std::string(zc.data(), zc.size()));
}
}
void TgVoip::setLoggingFunction(std::function<void(std::string const &)> loggingFunction) {
globalLoggingFunction = loggingFunction;
}
void TgVoip::setGlobalServerConfig(const std::string &serverConfig) {
tgvoip::ServerConfig::GetSharedInstance()->Update(serverConfig);
}
int TgVoip::getConnectionMaxLayer() {
return tgvoip::VoIPController::GetConnectionMaxLayer();
}
std::string TgVoip::getVersion() {
return tgvoip::VoIPController::GetVersion();
}
TgVoip *TgVoip::makeInstance(
TgVoipConfig const &config,
TgVoipPersistentState const &persistentState,
std::vector<TgVoipEndpoint> const &endpoints,
std::unique_ptr<TgVoipProxy> const &proxy,
TgVoipNetworkType initialNetworkType,
TgVoipEncryptionKey const &encryptionKey
#ifdef TGVOIP_USE_CUSTOM_CRYPTO
,
TgVoipCrypto const &crypto
#endif
#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO
,
TgVoipAudioDataCallbacks const &audioDataCallbacks
#endif
) {
return new TgVoipImpl(
endpoints,
persistentState,
proxy,
config,
encryptionKey,
initialNetworkType
#ifdef TGVOIP_USE_CUSTOM_CRYPTO
,
crypto
#endif
#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO
,
audioDataCallbacks
#endif
);
}
TgVoip::~TgVoip() = default;

View File

@ -0,0 +1,160 @@
#ifndef __TGVOIP_H
#define __TGVOIP_H
#include <functional>
#include <vector>
#include <string>
#include <memory>
struct TgVoipProxy {
std::string host;
uint16_t port;
std::string login;
std::string password;
};
enum class TgVoipEndpointType {
Inet,
Lan,
UdpRelay,
TcpRelay
};
struct TgVoipEndpoint {
int64_t endpointId;
std::string host;
uint16_t port;
TgVoipEndpointType type;
unsigned char peerTag[16];
};
enum class TgVoipNetworkType {
Unknown,
Gprs,
Edge,
ThirdGeneration,
Hspa,
Lte,
WiFi,
Ethernet,
OtherHighSpeed,
OtherLowSpeed,
OtherMobile,
Dialup
};
enum class TgVoipDataSaving {
Never,
Mobile,
Always
};
struct TgVoipPersistentState {
std::vector<uint8_t> value;
};
#ifdef TGVOIP_USE_CUSTOM_CRYPTO
struct TgVoipCrypto {
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);
void (*aes_cbc_encrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv);
void (*aes_cbc_decrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv);
};
#endif
struct TgVoipConfig {
double initializationTimeout;
double receiveTimeout;
TgVoipDataSaving dataSaving;
bool enableP2P;
bool enableAEC;
bool enableNS;
bool enableAGC;
bool enableCallUpgrade;
std::string logPath;
int maxApiLayer;
};
struct TgVoipEncryptionKey {
std::vector<uint8_t> value;
bool isOutgoing;
};
enum class TgVoipState {
WaitInit,
WaitInitAck,
Estabilished,
Failed,
Reconnecting
};
struct TgVoipTrafficStats {
uint64_t bytesSentWifi;
uint64_t bytesReceivedWifi;
uint64_t bytesSentMobile;
uint64_t bytesReceivedMobile;
};
struct TgVoipFinalState {
TgVoipPersistentState persistentState;
std::string debugLog;
TgVoipTrafficStats trafficStats;
bool isRatingSuggested;
};
struct TgVoipAudioDataCallbacks {
std::function<void(int16_t*, size_t)> input;
std::function<void(int16_t*, size_t)> output;
std::function<void(int16_t*, size_t)> preprocessed;
};
class TgVoip {
protected:
TgVoip() = default;
public:
static void setLoggingFunction(std::function<void(std::string const &)> loggingFunction);
static void setGlobalServerConfig(std::string const &serverConfig);
static int getConnectionMaxLayer();
static std::string getVersion();
static TgVoip *makeInstance(
TgVoipConfig const &config,
TgVoipPersistentState const &persistentState,
std::vector<TgVoipEndpoint> const &endpoints,
std::unique_ptr<TgVoipProxy> const &proxy,
TgVoipNetworkType initialNetworkType,
TgVoipEncryptionKey const &encryptionKey
#ifdef TGVOIP_USE_CUSTOM_CRYPTO
,
TgVoipCrypto const &crypto
#endif
#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO
,
TgVoipAudioDataCallbacks const &audioDataCallbacks
#endif
);
virtual ~TgVoip();
virtual void setNetworkType(TgVoipNetworkType networkType) = 0;
virtual void setMuteMicrophone(bool muteMicrophone) = 0;
virtual void setAudioOutputGainControlEnabled(bool enabled) = 0;
virtual void setEchoCancellationStrength(int strength) = 0;
virtual std::string getLastError() = 0;
virtual std::string getDebugInfo() = 0;
virtual int64_t getPreferredRelayId() = 0;
virtual TgVoipTrafficStats getTrafficStats() = 0;
virtual TgVoipPersistentState getPersistentState() = 0;
virtual void setOnStateUpdated(std::function<void(TgVoipState)> onStateUpdated) = 0;
virtual void setOnSignalBarsUpdated(std::function<void(int)> onSignalBarsUpdated) = 0;
virtual TgVoipFinalState stop() = 0;
};
#endif

View File

@ -29,6 +29,11 @@
#include <sstream>
#include <inttypes.h>
#include <float.h>
#ifdef HAVE_CONFIG_H
#include <opus.h>
#else
#include <opus.h>
#endif
inline int pad4(int x){
@ -64,74 +69,6 @@ extern jclass jniUtilitiesClass;
#include "audio/AudioIOCallback.h"
#endif
#pragma mark - OpenSSL wrappers
#ifndef TGVOIP_USE_CUSTOM_CRYPTO
extern "C" {
#include <openssl/sha.h>
#include <openssl/aes.h>
//#include <openssl/modes.h>
#include <openssl/rand.h>
}
void tgvoip_openssl_aes_ige_encrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){
AES_KEY akey;
AES_set_encrypt_key(key, 32*8, &akey);
AES_ige_encrypt(in, out, length, &akey, iv, AES_ENCRYPT);
}
void tgvoip_openssl_aes_ige_decrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){
AES_KEY akey;
AES_set_decrypt_key(key, 32*8, &akey);
AES_ige_encrypt(in, out, length, &akey, iv, AES_DECRYPT);
}
void tgvoip_openssl_rand_bytes(uint8_t* buffer, size_t len){
RAND_bytes(buffer, len);
}
void tgvoip_openssl_sha1(uint8_t* msg, size_t len, uint8_t* output){
SHA1(msg, len, output);
}
void tgvoip_openssl_sha256(uint8_t* msg, size_t len, uint8_t* output){
SHA256(msg, len, output);
}
void tgvoip_openssl_aes_ctr_encrypt(uint8_t* inout, size_t length, uint8_t* key, uint8_t* iv, uint8_t* ecount, uint32_t* num){
AES_KEY akey;
AES_set_encrypt_key(key, 32*8, &akey);
AES_ctr128_encrypt(inout, inout, length, &akey, iv, ecount, num);
}
void tgvoip_openssl_aes_cbc_encrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){
AES_KEY akey;
AES_set_encrypt_key(key, 256, &akey);
AES_cbc_encrypt(in, out, length, &akey, iv, AES_ENCRYPT);
}
void tgvoip_openssl_aes_cbc_decrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){
AES_KEY akey;
AES_set_decrypt_key(key, 256, &akey);
AES_cbc_encrypt(in, out, length, &akey, iv, AES_DECRYPT);
}
CryptoFunctions VoIPController::crypto={
tgvoip_openssl_rand_bytes,
tgvoip_openssl_sha1,
tgvoip_openssl_sha256,
tgvoip_openssl_aes_ige_encrypt,
tgvoip_openssl_aes_ige_decrypt,
tgvoip_openssl_aes_ctr_encrypt,
tgvoip_openssl_aes_cbc_encrypt,
tgvoip_openssl_aes_cbc_decrypt
};
#else
CryptoFunctions VoIPController::crypto; // set it yourself upon initialization
#endif
extern FILE* tgvoipLogFile;
#pragma mark - Public API
@ -294,6 +231,12 @@ VoIPController::~VoIPController(){
tgvoipLogFile=NULL;
fclose(log);
}
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
if (preprocDecoder) {
opus_decoder_destroy(preprocDecoder);
preprocDecoder=nullptr;
}
#endif
}
void VoIPController::Stop(){
@ -773,10 +716,10 @@ string VoIPController::GetCurrentAudioOutputID(){
void VoIPController::SetProxy(int protocol, string address, uint16_t port, string username, string password){
proxyProtocol=protocol;
proxyAddress=address;
proxyAddress=std::move(address);
proxyPort=port;
proxyUsername=username;
proxyPassword=password;
proxyUsername=std::move(username);
proxyPassword=std::move(password);
}
int VoIPController::GetSignalBarsCount(){
@ -841,9 +784,11 @@ void VoIPController::SetEchoCancellationStrength(int strength){
}
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
void VoIPController::SetAudioDataCallbacks(std::function<void(int16_t*, size_t)> input, std::function<void(int16_t*, size_t)> output){
void VoIPController::SetAudioDataCallbacks(std::function<void(int16_t*, size_t)> input, std::function<void(int16_t*, size_t)> output, std::function<void(int16_t*, size_t)> preproc=nullptr){
audioInputDataCallback=input;
audioOutputDataCallback=output;
audioPreprocDataCallback=preproc;
preprocDecoder=preprocDecoder ? preprocDecoder : opus_decoder_create(48000, 1, NULL);
}
#endif
@ -894,7 +839,7 @@ void VoIPController::SetConfig(const Config& cfg){
void VoIPController::SetPersistentState(vector<uint8_t> state){
using namespace json11;
if(state.empty())
return;
string jsonErr;
@ -915,7 +860,7 @@ void VoIPController::SetPersistentState(vector<uint8_t> state){
vector<uint8_t> VoIPController::GetPersistentState(){
using namespace json11;
Json::object obj=Json::object{
{"ver", 1},
};
@ -1116,10 +1061,11 @@ void VoIPController::HandleAudioInput(unsigned char *data, size_t len, unsigned
}
unsentStreamPackets++;
size_t pktLength = pkt.GetLength();
PendingOutgoingPacket p{
/*.seq=*/GenerateOutSeq(),
/*.type=*/PKT_STREAM_DATA,
/*.len=*/pkt.GetLength(),
/*.len=*/pktLength,
/*.data=*/Buffer(move(pkt)),
/*.endpoint=*/0,
};
@ -1142,10 +1088,11 @@ void VoIPController::HandleAudioInput(unsigned char *data, size_t len, unsigned
pkt.WriteBytes(*ecData);
}
pktLength = pkt.GetLength();
PendingOutgoingPacket p{
GenerateOutSeq(),
PKT_STREAM_EC,
pkt.GetLength(),
pktLength,
Buffer(move(pkt)),
0
};
@ -1153,6 +1100,13 @@ void VoIPController::HandleAudioInput(unsigned char *data, size_t len, unsigned
}
audioTimestampOut+=outgoingStreams[0]->frameDuration;
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
if (audioPreprocDataCallback && preprocDecoder) {
int size=opus_decode(preprocDecoder, data, len, preprocBuffer, 4096, 0);
audioPreprocDataCallback(preprocBuffer, size);
}
#endif
}
void VoIPController::InitializeAudio(){
@ -1324,7 +1278,7 @@ void VoIPController::WritePacketHeader(uint32_t pseq, BufferOutputStream *s, uns
if(!currentExtras.empty()){
s->WriteByte(static_cast<unsigned char>(currentExtras.size()));
for(vector<UnacknowledgedExtraData>::iterator x=currentExtras.begin(); x!=currentExtras.end(); ++x){
LOGV("Writing extra into header: type %u, length %lu", x->type, x->data.Length());
LOGV("Writing extra into header: type %u, length %d", x->type, int(x->data.Length()));
assert(x->data.Length()<=254);
s->WriteByte(static_cast<unsigned char>(x->data.Length()+1));
s->WriteByte(x->type);
@ -1406,7 +1360,7 @@ void VoIPController::WritePacketHeader(uint32_t pseq, BufferOutputStream *s, uns
s->WriteByte(XPFLAG_HAS_EXTRA);
s->WriteByte(static_cast<unsigned char>(currentExtras.size()));
for(vector<UnacknowledgedExtraData>::iterator x=currentExtras.begin(); x!=currentExtras.end(); ++x){
LOGV("Writing extra into header: type %u, length %lu", x->type, x->data.Length());
LOGV("Writing extra into header: type %u, length %d", x->type, int(x->data.Length()));
assert(x->data.Length()<=254);
s->WriteByte(static_cast<unsigned char>(x->data.Length()+1));
s->WriteByte(x->type);
@ -1486,10 +1440,11 @@ void VoIPController::SendInit(){
out.WriteInt32(id);
}*/
}
size_t outLength = out.GetLength();
SendOrEnqueuePacket(PendingOutgoingPacket{
/*.seq=*/initSeq,
/*.type=*/PKT_INIT,
/*.len=*/out.GetLength(),
/*.len=*/outLength,
/*.data=*/Buffer(move(out)),
/*.endpoint=*/e.id
});
@ -1518,14 +1473,14 @@ void VoIPController::InitUDPProxy(){
ResetUdpAvailability();
return;
}
NetworkSocket* tcp=NetworkSocket::Create(PROTO_TCP);
tcp->Connect(resolvedProxyAddress, proxyPort);
vector<NetworkSocket*> writeSockets;
vector<NetworkSocket*> readSockets;
vector<NetworkSocket*> errorSockets;
while(!tcp->IsFailed() && !tcp->IsReadyToSend()){
writeSockets.push_back(tcp);
if(!NetworkSocket::Select(readSockets, writeSockets, errorSockets, selectCanceller)){
@ -1579,12 +1534,12 @@ void VoIPController::RunRecvThread(){
udpPingTimeoutID=messageThread.Post(std::bind(&VoIPController::SendUdpPings, this), 0.0, 0.5);
}
while(runReceiver){
if(proxyProtocol==PROXY_SOCKS5 && needReInitUdpProxy){
InitUDPProxy();
needReInitUdpProxy=false;
}
packet.data=*buffer;
packet.length=buffer.Length();
@ -1595,7 +1550,7 @@ void VoIPController::RunRecvThread(){
errorSockets.push_back(realUdpSocket);
if(!realUdpSocket->IsReadyToSend())
writeSockets.push_back(realUdpSocket);
{
MutexGuard m(endpointsMutex);
for(pair<const int64_t, Endpoint>& _e:endpoints){
@ -2132,7 +2087,7 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA
}
for(vector<UnacknowledgedExtraData>::iterator x=currentExtras.begin();x!=currentExtras.end();){
if(x->firstContainingSeq!=0 && (lastRemoteAckSeq==x->firstContainingSeq || seqgt(lastRemoteAckSeq, x->firstContainingSeq))){
LOGV("Peer acknowledged extra type %u length %lu", x->type, x->data.Length());
LOGV("Peer acknowledged extra type %u length %d", x->type, int(x->data.Length()));
ProcessAcknowledgedOutgoingExtra(*x);
x=currentExtras.erase(x);
continue;
@ -2310,10 +2265,11 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA
out.WriteByte((unsigned char) ((*s)->enabled ? 1 : 0));
}
LOGI("Sending init ack");
size_t outLength = out.GetLength();
SendOrEnqueuePacket(PendingOutgoingPacket{
/*.seq=*/GenerateOutSeq(),
/*.type=*/PKT_INIT_ACK,
/*.len=*/out.GetLength(),
/*.len=*/outLength,
/*.data=*/Buffer(move(out)),
/*.endpoint=*/0
});
@ -2541,10 +2497,11 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA
}
BufferOutputStream pkt(128);
pkt.WriteInt32(pseq);
size_t pktLength = pkt.GetLength();
SendOrEnqueuePacket(PendingOutgoingPacket{
/*.seq=*/GenerateOutSeq(),
/*.type=*/PKT_PONG,
/*.len=*/pkt.GetLength(),
/*.len=*/pktLength,
/*.data=*/Buffer(move(pkt)),
/*.endpoint=*/srcEndpoint.id,
});
@ -2799,7 +2756,7 @@ bool VoIPController::SendOrEnqueuePacket(PendingOutgoingPacket pkt, bool enqueue
abort();
return false;
}
bool canSend;
if(endpoint->type!=Endpoint::Type::TCP_RELAY){
@ -3055,7 +3012,7 @@ void VoIPController::AddTCPRelays(){
relays.push_back(tcpRelay);
}
for(Endpoint& e:relays){
endpoints[e.id]=move(e);
endpoints[e.id]=e;
}
didAddTcpRelays=true;
}
@ -3206,7 +3163,7 @@ void VoIPController::SendPacketReliably(unsigned char type, unsigned char *data,
void VoIPController::SendExtra(Buffer &data, unsigned char type){
MutexGuard m(queuedPacketsMutex);
LOGV("Sending extra type %u length %lu", type, data.Length());
LOGV("Sending extra type %u length %d", type, int(data.Length()));
for(vector<UnacknowledgedExtraData>::iterator x=currentExtras.begin();x!=currentExtras.end();++x){
if(x->type==type){
x->firstContainingSeq=0;
@ -3479,16 +3436,17 @@ void VoIPController::SendVideoFrame(const Buffer &frame, uint32_t flags, uint32_
pkt.WriteBytes(frame, offset, len);
uint32_t seq=GenerateOutSeq();
size_t pktLength = pkt.GetLength();
PendingOutgoingPacket p{
/*.seq=*/seq,
/*.type=*/PKT_STREAM_DATA,
/*.len=*/pkt.GetLength(),
/*.len=*/pktLength,
/*.data=*/Buffer(move(pkt)),
/*.endpoint=*/0,
};
unsentStreamPackets++;
SendOrEnqueuePacket(move(p));
videoCongestionControl.ProcessPacketSent(static_cast<unsigned int>(pkt.GetLength()));
videoCongestionControl.ProcessPacketSent(static_cast<unsigned int>(pktLength));
sentFrame.unacknowledgedPackets.push_back(seq);
}
MutexGuard m(sentVideoFramesMutex);
@ -3750,7 +3708,7 @@ void VoIPController::UpdateCongestion(){
wasExtraEC=true;
}
}
if(avgSendLossCount>0.08){
extraEcLevel=4;
}else if(avgSendLossCount>0.05){
@ -4019,7 +3977,7 @@ Endpoint::~Endpoint(){
#pragma mark - AudioInputTester
AudioInputTester::AudioInputTester(std::string deviceID) : deviceID(deviceID){
AudioInputTester::AudioInputTester(std::string deviceID) : deviceID(std::move(deviceID)){
io=audio::AudioIO::Create(deviceID, "default");
if(io->Failed()){
LOGE("Audio IO failed");

View File

@ -402,7 +402,7 @@ namespace tgvoip{
void SetPersistentState(std::vector<uint8_t> state);
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
void SetAudioDataCallbacks(std::function<void(int16_t*, size_t)> input, std::function<void(int16_t*, size_t)> output);
void SetAudioDataCallbacks(std::function<void(int16_t*, size_t)> input, std::function<void(int16_t*, size_t)> output, std::function<void(int16_t*, size_t)> preprocessed);
#endif
void SetVideoCodecSpecificData(const std::vector<Buffer>& data);
@ -749,6 +749,9 @@ namespace tgvoip{
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
std::function<void(int16_t*, size_t)> audioInputDataCallback;
std::function<void(int16_t*, size_t)> audioOutputDataCallback;
std::function<void(int16_t*, size_t)> audioPreprocDataCallback;
::OpusDecoder* preprocDecoder=nullptr;
int16_t preprocBuffer[4096];
#endif
#if defined(__APPLE__) && defined(TARGET_OS_OSX)
bool macAudioDuckingEnabled=true;

View File

@ -58,7 +58,7 @@ static const int16_t hann[960]={
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)
#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;

View File

@ -0,0 +1,402 @@
#include <jni.h>
#include <TgVoip.h>
#include <os/android/AudioOutputOpenSLES.h>
#include <os/android/AudioInputOpenSLES.h>
#include <os/android/JNIUtilities.h>
#include "org_telegram_messenger_voip_TgVoip.h"
using namespace tgvoip;
extern "C" int tgvoipOnJniLoad(JavaVM *vm, JNIEnv *env) {
return JNI_TRUE;
}
#pragma mark - Helpers
class JavaObject {
private:
JNIEnv *env;
jobject obj;
jclass clazz;
public:
JavaObject(JNIEnv *env, jobject obj) : JavaObject(env, obj, env->GetObjectClass(obj)) {
}
JavaObject(JNIEnv *env, jobject obj, jclass clazz) {
this->env = env;
this->obj = obj;
this->clazz = clazz;
}
jint getIntField(const char *name) {
return env->GetIntField(obj, env->GetFieldID(clazz, name, "I"));
}
jlong getLongField(const char *name) {
return env->GetLongField(obj, env->GetFieldID(clazz, name, "J"));
}
jboolean getBooleanField(const char *name) {
return env->GetBooleanField(obj, env->GetFieldID(clazz, name, "Z"));
}
jdouble getDoubleField(const char *name) {
return env->GetDoubleField(obj, env->GetFieldID(clazz, name, "D"));
}
jbyteArray getByteArrayField(const char *name) {
return (jbyteArray) env->GetObjectField(obj, env->GetFieldID(clazz, name, "[B"));
}
jstring getStringField(const char *name) {
return (jstring) env->GetObjectField(obj, env->GetFieldID(clazz, name, "Ljava/lang/String;"));
}
};
struct InstanceHolder {
TgVoip *nativeInstance;
jobject javaInstance;
};
jlong getInstanceHolderId(JNIEnv *env, jobject obj) {
return env->GetLongField(obj, env->GetFieldID(env->GetObjectClass(obj), "nativeInstanceId", "J"));
}
InstanceHolder *getInstanceHolder(JNIEnv *env, jobject obj) {
return reinterpret_cast<InstanceHolder *>(getInstanceHolderId(env, obj));
}
TgVoip *getTgVoip(JNIEnv *env, jobject obj) {
return getInstanceHolder(env, obj)->nativeInstance;
}
jint throwNewJavaException(JNIEnv *env, const char *className, const char *message) {
return env->ThrowNew(env->FindClass(className), message);
}
jint throwNewJavaIllegalArgumentException(JNIEnv *env, const char *message) {
return throwNewJavaException(env, "java/lang/IllegalStateException", message);
}
jbyteArray copyVectorToJavaByteArray(JNIEnv *env, const std::vector<uint8_t> &bytes) {
unsigned int size = bytes.size();
jbyteArray bytesArray = env->NewByteArray(size);
env->SetByteArrayRegion(bytesArray, 0, size, (jbyte *) bytes.data());
return bytesArray;
}
void readTgVoipPersistentState(const char *filePath, TgVoipPersistentState &tgVoipPersistentState) {
FILE *persistentStateFile = fopen(filePath, "r");
if (persistentStateFile) {
fseek(persistentStateFile, 0, SEEK_END);
auto len = static_cast<size_t>(ftell(persistentStateFile));
fseek(persistentStateFile, 0, SEEK_SET);
if (len < 1024 * 512 && len > 0) {
auto *buffer = static_cast<uint8_t *>(malloc(len));
fread(buffer, 1, len, persistentStateFile);
tgVoipPersistentState.value = std::vector<uint8_t>(buffer, buffer + len);
free(buffer);
}
fclose(persistentStateFile);
}
}
void saveTgVoipPersistentState(const char *filePath, const TgVoipPersistentState &tgVoipPersistentState) {
FILE *persistentStateFile = fopen(filePath, "w");
if (persistentStateFile) {
fwrite(tgVoipPersistentState.value.data(), 1, tgVoipPersistentState.value.size(), persistentStateFile);
fclose(persistentStateFile);
}
}
#pragma mark - Mappers
TgVoipNetworkType parseTgVoipNetworkType(jint networkType) {
switch (networkType) {
case org_telegram_messenger_voip_TgVoip_NET_TYPE_GPRS:
return TgVoipNetworkType::Gprs;
case org_telegram_messenger_voip_TgVoip_NET_TYPE_EDGE:
return TgVoipNetworkType::Edge;
case org_telegram_messenger_voip_TgVoip_NET_TYPE_3G:
return TgVoipNetworkType::ThirdGeneration;
case org_telegram_messenger_voip_TgVoip_NET_TYPE_HSPA:
return TgVoipNetworkType::Hspa;
case org_telegram_messenger_voip_TgVoip_NET_TYPE_LTE:
return TgVoipNetworkType::Lte;
case org_telegram_messenger_voip_TgVoip_NET_TYPE_WIFI:
return TgVoipNetworkType::WiFi;
case org_telegram_messenger_voip_TgVoip_NET_TYPE_ETHERNET:
return TgVoipNetworkType::Ethernet;
case org_telegram_messenger_voip_TgVoip_NET_TYPE_OTHER_HIGH_SPEED:
return TgVoipNetworkType::OtherHighSpeed;
case org_telegram_messenger_voip_TgVoip_NET_TYPE_OTHER_LOW_SPEED:
return TgVoipNetworkType::OtherLowSpeed;
case org_telegram_messenger_voip_TgVoip_NET_TYPE_DIALUP:
return TgVoipNetworkType::Dialup;
case org_telegram_messenger_voip_TgVoip_NET_TYPE_OTHER_MOBILE:
return TgVoipNetworkType::OtherMobile;
default:
return TgVoipNetworkType::Unknown;
}
}
TgVoipDataSaving parseTgVoipDataSaving(JNIEnv *env, jint dataSaving) {
switch (dataSaving) {
case org_telegram_messenger_voip_TgVoip_DATA_SAVING_NEVER:
return TgVoipDataSaving::Never;
case org_telegram_messenger_voip_TgVoip_DATA_SAVING_MOBILE:
return TgVoipDataSaving::Mobile;
case org_telegram_messenger_voip_TgVoip_DATA_SAVING_ALWAYS:
return TgVoipDataSaving::Always;
case org_telegram_messenger_voip_TgVoip_DATA_SAVING_ROAMING:
throwNewJavaIllegalArgumentException(env, "DATA_SAVING_ROAMING is not supported");
return TgVoipDataSaving::Never;
default:
throwNewJavaIllegalArgumentException(env, "Unknown data saving constant: " + dataSaving);
return TgVoipDataSaving::Never;
}
}
void parseTgVoipConfig(JNIEnv *env, jobject config, TgVoipConfig &tgVoipConfig) {
JavaObject configObject(env, config);
tgVoipConfig.initializationTimeout = configObject.getDoubleField("initializationTimeout");
tgVoipConfig.receiveTimeout = configObject.getDoubleField("receiveTimeout");
tgVoipConfig.dataSaving = parseTgVoipDataSaving(env, configObject.getIntField("dataSaving"));
tgVoipConfig.enableP2P = configObject.getBooleanField("enableP2p") == JNI_TRUE;
tgVoipConfig.enableAEC = configObject.getBooleanField("enableAec") == JNI_TRUE;
tgVoipConfig.enableNS = configObject.getBooleanField("enableNs") == JNI_TRUE;
tgVoipConfig.enableAGC = configObject.getBooleanField("enableAgc") == JNI_TRUE;
tgVoipConfig.enableCallUpgrade = configObject.getBooleanField("enableCallUpgrade") == JNI_TRUE;
tgVoipConfig.logPath = jni::JavaStringToStdString(env, configObject.getStringField("logPath"));
tgVoipConfig.maxApiLayer = configObject.getIntField("maxApiLayer");
}
TgVoipEndpointType parseTgVoipEndpointType(JNIEnv *env, jint endpointType) {
switch (endpointType) {
case org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_INET:
return TgVoipEndpointType::Inet;
case org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_LAN:
return TgVoipEndpointType::Lan;
case org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_TCP_RELAY:
return TgVoipEndpointType::TcpRelay;
case org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_UDP_RELAY:
return TgVoipEndpointType::UdpRelay;
default:
throwNewJavaIllegalArgumentException(env, std::string("Unknown endpoint type: ").append(std::to_string(endpointType)).c_str());
return TgVoipEndpointType::UdpRelay;
}
}
void parseTgVoipEndpoint(JNIEnv *env, jobject endpoint, TgVoipEndpoint &tgVoipEndpoint) {
JavaObject endpointObject(env, endpoint);
tgVoipEndpoint.endpointId = endpointObject.getLongField("id");
tgVoipEndpoint.host = jni::JavaStringToStdString(env, endpointObject.getStringField("ipv4"));
tgVoipEndpoint.port = static_cast<uint16_t>(endpointObject.getIntField("port"));
tgVoipEndpoint.type = parseTgVoipEndpointType(env, endpointObject.getIntField("type"));
jbyteArray peerTag = endpointObject.getByteArrayField("peerTag");
if (peerTag && env->GetArrayLength(peerTag)) {
jbyte *peerTagBytes = env->GetByteArrayElements(peerTag, nullptr);
memcpy(tgVoipEndpoint.peerTag, peerTagBytes, 16);
env->ReleaseByteArrayElements(peerTag, peerTagBytes, JNI_ABORT);
}
}
void parseTgVoipEndpoints(JNIEnv *env, jobjectArray endpoints, std::vector<TgVoipEndpoint> &tgVoipEndpoints) {
for (int i = 0, size = env->GetArrayLength(endpoints); i < size; i++) {
TgVoipEndpoint tgVoipEndpoint;
parseTgVoipEndpoint(env, env->GetObjectArrayElement(endpoints, i), tgVoipEndpoint);
tgVoipEndpoints.push_back(tgVoipEndpoint);
}
}
void parseTgVoipEncryptionKey(JNIEnv *env, jobject encryptionKey, TgVoipEncryptionKey &tgVoipEncryptionKey) {
JavaObject encryptionKeyObject(env, encryptionKey);
tgVoipEncryptionKey.isOutgoing = encryptionKeyObject.getBooleanField("isOutgoing") == JNI_TRUE;
jbyteArray valueByteArray = encryptionKeyObject.getByteArrayField("value");
auto *valueBytes = (uint8_t *) env->GetByteArrayElements(valueByteArray, nullptr);
tgVoipEncryptionKey.value = std::vector<uint8_t>(valueBytes, valueBytes + env->GetArrayLength(valueByteArray));
env->ReleaseByteArrayElements(valueByteArray, (jbyte *) valueBytes, JNI_ABORT);
}
void parseTgVoipProxy(JNIEnv *env, jobject proxy, std::unique_ptr<TgVoipProxy> &tgVoipProxy) {
if (!env->IsSameObject(proxy, nullptr)) {
JavaObject proxyObject(env, proxy);
tgVoipProxy = std::unique_ptr<TgVoipProxy>(new TgVoipProxy);
tgVoipProxy->host = jni::JavaStringToStdString(env, proxyObject.getStringField("host"));
tgVoipProxy->port = static_cast<uint16_t>(proxyObject.getIntField("port"));
tgVoipProxy->login = jni::JavaStringToStdString(env, proxyObject.getStringField("login"));
tgVoipProxy->password = jni::JavaStringToStdString(env, proxyObject.getStringField("password"));
} else {
tgVoipProxy = nullptr;
}
}
jint asJavaState(const TgVoipState &tgVoipState) {
switch (tgVoipState) {
case TgVoipState::WaitInit:
return org_telegram_messenger_voip_TgVoip_STATE_WAIT_INIT;
case TgVoipState::WaitInitAck:
return org_telegram_messenger_voip_TgVoip_STATE_WAIT_INIT_ACK;
case TgVoipState::Estabilished:
return org_telegram_messenger_voip_TgVoip_STATE_ESTABLISHED;
case TgVoipState::Failed:
return org_telegram_messenger_voip_TgVoip_STATE_FAILED;
case TgVoipState::Reconnecting:
return org_telegram_messenger_voip_TgVoip_STATE_RECONNECTING;
}
}
jobject asJavaTrafficStats(JNIEnv *env, const TgVoipTrafficStats &trafficStats) {
jclass clazz = env->FindClass("org/telegram/messenger/voip/TgVoip$TrafficStats");
jmethodID initMethodId = env->GetMethodID(clazz, "<init>", "(JJJJ)V");
return env->NewObject(clazz, initMethodId, trafficStats.bytesSentWifi, trafficStats.bytesReceivedWifi, trafficStats.bytesSentMobile, trafficStats.bytesReceivedMobile);
}
jobject asJavaFinalState(JNIEnv *env, const TgVoipFinalState &tgVoipFinalState) {
jbyteArray persistentState = copyVectorToJavaByteArray(env, tgVoipFinalState.persistentState.value);
jstring debugLog = env->NewStringUTF(tgVoipFinalState.debugLog.c_str());
jobject trafficStats = asJavaTrafficStats(env, tgVoipFinalState.trafficStats);
auto isRatingSuggested = static_cast<jboolean>(tgVoipFinalState.isRatingSuggested);
jclass finalStateClass = env->FindClass("org/telegram/messenger/voip/TgVoip$FinalState");
jmethodID finalStateInitMethodId = env->GetMethodID(finalStateClass, "<init>", "([BLjava/lang/String;Lorg/telegram/messenger/voip/TgVoip$TrafficStats;Z)V");
return env->NewObject(finalStateClass, finalStateInitMethodId, persistentState, debugLog, trafficStats, isRatingSuggested);
}
extern "C" {
#pragma mark - Static JNI Methods
JNIEXPORT jlong JNICALL Java_org_telegram_messenger_voip_NativeTgVoipDelegate_makeNativeInstance(JNIEnv *env, jobject obj, jobject instanceObj, jobject config, jstring persistentStateFilePath, jobjectArray endpoints, jobject proxy, jint networkType, jobject encryptionKey) {
// reading persistent state
TgVoipPersistentState tgVoipPersistentState;
readTgVoipPersistentState(jni::JavaStringToStdString(env, persistentStateFilePath).c_str(), tgVoipPersistentState);
// parsing config
TgVoipConfig tgVoipConfig;
parseTgVoipConfig(env, config, tgVoipConfig);
// parsing endpoints
std::vector<TgVoipEndpoint> tgVoipEndpoints;
parseTgVoipEndpoints(env, endpoints, tgVoipEndpoints);
// parsing proxy
std::unique_ptr<TgVoipProxy> tgVoipProxy;
parseTgVoipProxy(env, proxy, tgVoipProxy);
// parse encryption key
TgVoipEncryptionKey tgVoipEncryptionKey;
parseTgVoipEncryptionKey(env, encryptionKey, tgVoipEncryptionKey);
TgVoip *tgVoip = TgVoip::makeInstance(tgVoipConfig, tgVoipPersistentState, tgVoipEndpoints, tgVoipProxy, parseTgVoipNetworkType(networkType), tgVoipEncryptionKey);
if (env->ExceptionCheck() == JNI_TRUE) {
return 0;
}
jobject globalRef = env->NewGlobalRef(instanceObj);
tgVoip->setOnStateUpdated([globalRef](TgVoipState tgVoipState) {
jint state = asJavaState(tgVoipState);
jni::DoWithJNI([globalRef, state](JNIEnv *env) {
env->CallVoidMethod(globalRef, env->GetMethodID(env->GetObjectClass(globalRef), "onStateUpdated", "(I)V"), state);
});
});
tgVoip->setOnSignalBarsUpdated([globalRef](int signalBars) {
jni::DoWithJNI([globalRef, signalBars](JNIEnv *env) {
env->CallVoidMethod(globalRef, env->GetMethodID(env->GetObjectClass(globalRef), "onSignalBarsUpdated", "(I)V"), signalBars);
});
});
auto *instance = new InstanceHolder;
instance->nativeInstance = tgVoip;
instance->javaInstance = globalRef;
return reinterpret_cast<jlong>(instance);
}
JNIEXPORT void JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipDelegate_setGlobalServerConfig(JNIEnv *env, jobject obj, jstring serverConfigJson) {
TgVoip::setGlobalServerConfig(jni::JavaStringToStdString(env, serverConfigJson));
}
JNIEXPORT jint JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipDelegate_getConnectionMaxLayer(JNIEnv *env, jobject obj) {
return TgVoip::getConnectionMaxLayer();
}
JNIEXPORT jstring JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipDelegate_getVersion(JNIEnv *env, jobject obj) {
return env->NewStringUTF(TgVoip::getVersion().c_str());
}
JNIEXPORT void JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipDelegate_setBufferSize(JNIEnv *env, jobject obj, jint size) {
tgvoip::audio::AudioOutputOpenSLES::nativeBufferSize = (unsigned int) size;
tgvoip::audio::AudioInputOpenSLES::nativeBufferSize = (unsigned int) size;
}
#pragma mark - Virtual JNI Methods
JNIEXPORT void JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipInstance_setNetworkType(JNIEnv *env, jobject obj, jint networkType) {
getTgVoip(env, obj)->setNetworkType(parseTgVoipNetworkType(networkType));
}
JNIEXPORT void JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipInstance_setMuteMicrophone(JNIEnv *env, jobject obj, jboolean muteMicrophone) {
getTgVoip(env, obj)->setMuteMicrophone(muteMicrophone);
}
JNIEXPORT void JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipInstance_setAudioOutputGainControlEnabled(JNIEnv *env, jobject obj, jboolean enabled) {
getTgVoip(env, obj)->setAudioOutputGainControlEnabled(enabled);
}
JNIEXPORT void JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipInstance_setEchoCancellationStrength(JNIEnv *env, jobject obj, jint strength) {
getTgVoip(env, obj)->setEchoCancellationStrength(strength);
}
JNIEXPORT jstring JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipInstance_getLastError(JNIEnv *env, jobject obj) {
return env->NewStringUTF(getTgVoip(env, obj)->getLastError().c_str());
}
JNIEXPORT jstring JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipInstance_getDebugInfo(JNIEnv *env, jobject obj) {
return env->NewStringUTF(getTgVoip(env, obj)->getDebugInfo().c_str());
}
JNIEXPORT jlong JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipInstance_getPreferredRelayId(JNIEnv *env, jobject obj) {
return getTgVoip(env, obj)->getPreferredRelayId();
}
JNIEXPORT jobject JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipInstance_getTrafficStats(JNIEnv *env, jobject obj) {
return asJavaTrafficStats(env, getTgVoip(env, obj)->getTrafficStats());
}
JNIEXPORT jbyteArray JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipInstance_getPersistentState(JNIEnv *env, jobject obj) {
return copyVectorToJavaByteArray(env, getTgVoip(env, obj)->getPersistentState().value);
}
JNIEXPORT jobject JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipInstance_stop(JNIEnv *env, jobject obj) {
InstanceHolder *instance = getInstanceHolder(env, obj);
TgVoipFinalState tgVoipFinalState = instance->nativeInstance->stop();
// saving persistent state
const std::string &path = jni::JavaStringToStdString(env, JavaObject(env, obj).getStringField("persistentStateFilePath"));
saveTgVoipPersistentState(path.c_str(), tgVoipFinalState.persistentState);
// clean
env->DeleteGlobalRef(instance->javaInstance);
delete instance->nativeInstance;
delete instance;
return asJavaFinalState(env, tgVoipFinalState);
}
}

View File

@ -0,0 +1,63 @@
#include <jni.h>
#ifndef _Included_org_telegram_messenger_voip_TgVoip
#define _Included_org_telegram_messenger_voip_TgVoip
#ifdef __cplusplus
extern "C" {
#endif
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_UNKNOWN
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_UNKNOWN 0L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_GPRS
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_GPRS 1L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_EDGE
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_EDGE 2L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_3G
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_3G 3L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_HSPA
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_HSPA 4L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_LTE
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_LTE 5L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_WIFI
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_WIFI 6L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_ETHERNET
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_ETHERNET 7L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_OTHER_HIGH_SPEED
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_OTHER_HIGH_SPEED 8L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_OTHER_LOW_SPEED
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_OTHER_LOW_SPEED 9L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_DIALUP
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_DIALUP 10L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_OTHER_MOBILE
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_OTHER_MOBILE 11L
#undef org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_INET
#define org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_INET 0L
#undef org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_LAN
#define org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_LAN 1L
#undef org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_UDP_RELAY
#define org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_UDP_RELAY 2L
#undef org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_TCP_RELAY
#define org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_TCP_RELAY 3L
#undef org_telegram_messenger_voip_TgVoip_STATE_WAIT_INIT
#define org_telegram_messenger_voip_TgVoip_STATE_WAIT_INIT 1L
#undef org_telegram_messenger_voip_TgVoip_STATE_WAIT_INIT_ACK
#define org_telegram_messenger_voip_TgVoip_STATE_WAIT_INIT_ACK 2L
#undef org_telegram_messenger_voip_TgVoip_STATE_ESTABLISHED
#define org_telegram_messenger_voip_TgVoip_STATE_ESTABLISHED 3L
#undef org_telegram_messenger_voip_TgVoip_STATE_FAILED
#define org_telegram_messenger_voip_TgVoip_STATE_FAILED 4L
#undef org_telegram_messenger_voip_TgVoip_STATE_RECONNECTING
#define org_telegram_messenger_voip_TgVoip_STATE_RECONNECTING 5L
#undef org_telegram_messenger_voip_TgVoip_DATA_SAVING_NEVER
#define org_telegram_messenger_voip_TgVoip_DATA_SAVING_NEVER 0L
#undef org_telegram_messenger_voip_TgVoip_DATA_SAVING_MOBILE
#define org_telegram_messenger_voip_TgVoip_DATA_SAVING_MOBILE 1L
#undef org_telegram_messenger_voip_TgVoip_DATA_SAVING_ALWAYS
#define org_telegram_messenger_voip_TgVoip_DATA_SAVING_ALWAYS 2L
#undef org_telegram_messenger_voip_TgVoip_DATA_SAVING_ROAMING
#define org_telegram_messenger_voip_TgVoip_DATA_SAVING_ROAMING 3L
#undef org_telegram_messenger_voip_TgVoip_PEER_CAP_GROUP_CALLS
#define org_telegram_messenger_voip_TgVoip_PEER_CAP_GROUP_CALLS 1L
#ifdef __cplusplus
}
#endif
#endif

View File

@ -21,6 +21,7 @@
#include "../../os/android/JNIUtilities.h"
#include "../../PrivateDefines.h"
#include "../../logging.h"
#include "../../../c_utils.h"
#ifdef TGVOIP_HAS_CONFIG
#include <tgvoip_config.h>
@ -560,7 +561,20 @@ namespace tgvoip {
}
}
extern "C" void tgvoipRegisterNatives(JNIEnv* env){
extern "C" int tgvoipOnJniLoad(JavaVM *vm, JNIEnv *env);
extern "C" jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = 0;
srand(time(NULL));
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
if (tgvoipOnJniLoad(vm, env) != JNI_TRUE) {
return -1;
}
jclass controller=env->FindClass(TGVOIP_PACKAGE_PATH "/VoIPController");
jclass groupController=env->FindClass(TGVOIP_PACKAGE_PATH "/VoIPGroupController");
if(env->ExceptionCheck()){
@ -730,4 +744,6 @@ extern "C" void tgvoipRegisterNatives(JNIEnv* env){
};
env->RegisterNatives(vlog, vlogMethods, sizeof(vlogMethods)/sizeof(JNINativeMethod));
}
}
return JNI_VERSION_1_6;
}

View File

@ -16,10 +16,15 @@ extern JavaVM* sharedJVM;
namespace tgvoip{
namespace jni{
inline void DoWithJNI(std::function<void(JNIEnv*)> f){
JNIEnv *env=NULL;
bool didAttach=false;
inline JNIEnv *GetEnv() {
JNIEnv *env = nullptr;
sharedJVM->GetEnv((void **) &env, JNI_VERSION_1_6);
return env;
}
inline void DoWithJNI(std::function<void(JNIEnv*)> f){
JNIEnv *env=GetEnv();
bool didAttach=false;
if(!env){
sharedJVM->AttachCurrentThread(&env, NULL);
didAttach=true;
@ -59,7 +64,6 @@ namespace tgvoip{
env->ReleaseByteArrayElements(arr, elements, 0);
return arr;
}
}
}

View File

@ -39,7 +39,9 @@ class AudioOutputAudioUnit;
std::string currentInputDevice;
std::string currentOutputDevice;
bool duckingEnabled=true;
#ifndef TGVOIP_NO_OSX_PRIVATE_API
bool actualDuckingEnabled=true;
#endif // TGVOIP_NO_OSX_PRIVATE_API
AudioDeviceID currentOutputDeviceID;
#endif
AudioComponentInstance unit;

View File

@ -193,7 +193,7 @@ AudioPulse::AudioPulse(std::string inputDevice, std::string outputDevice){
isLocked=false;
output=new AudioOutputPulse(context, mainloop, outputDevice);
input=new AudioInputPulse(context, mainloop, outputDevice);
input=new AudioInputPulse(context, mainloop, inputDevice);
}
AudioPulse::~AudioPulse(){

26
TMessagesProj/jni/libtgvoip2/.gitignore vendored Normal file
View File

@ -0,0 +1,26 @@
.DS_Store
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/
DerivedData/
# Visual Studio 2015 cache/options directory
.vs/
xcuserdata/
autom4te.cache/

View File

@ -0,0 +1,560 @@
LOCAL_PATH := $(call my-dir)
LOCAL_MODULE := tgvoip${TGVOIP_NATIVE_VERSION}
LOCAL_CPPFLAGS := -Wall -std=c++11 -DANDROID -finline-functions -ffast-math -Os -fno-strict-aliasing -O3 -frtti -D__STDC_LIMIT_MACROS -Wno-unknown-pragmas
LOCAL_CPPFLAGS += -DBSD=1 -funroll-loops
LOCAL_CFLAGS := -O3 -DUSE_KISS_FFT -fexceptions -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_POSIX -DWEBRTC_ANDROID -D__STDC_LIMIT_MACROS -DWEBRTC_NS_FLOAT
LOCAL_CFLAGS += -DNULL=0 -DSOCKLEN_T=socklen_t -DLOCALE_NOT_USED -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64
LOCAL_CFLAGS += -Drestrict='' -D__EMX__ -DOPUS_BUILD -DFIXED_POINT -DUSE_ALLOCA -DHAVE_LRINT -DHAVE_LRINTF -fno-math-errno
LOCAL_LDLIBS := -llog -lOpenSLES
LOCAL_STATIC_LIBRARIES := crypto
MY_DIR := libtgvoip2
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/../opus/include \
$(LOCAL_PATH)/../opus/silk \
$(LOCAL_PATH)/../opus/silk/fixed \
$(LOCAL_PATH)/../opus/celt \
$(LOCAL_PATH)/../opus/ \
$(LOCAL_PATH)/../opus/opusfile \
$(LOCAL_PATH)/../boringssl/include/ \
$(LOCAL_PATH)/webrtc_dsp/
ifeq ($(TARGET_ARCH_ABI),$(filter $(TARGET_ARCH_ABI),armeabi-v7a arm64-v8a))
CC_NEON := cc.neon
LOCAL_CFLAGS += -DWEBRTC_HAS_NEON
else
CC_NEON := cc
endif
LOCAL_CFLAGS += $(TGVOIP_ADDITIONAL_CFLAGS)
LOCAL_SRC_FILES := \
./logging.cpp \
./TgVoip.cpp \
./VoIPController.cpp \
./VoIPGroupController.cpp \
./Buffers.cpp \
./BlockingQueue.cpp \
./audio/AudioInput.cpp \
./os/android/AudioInputOpenSLES.cpp \
./MediaStreamItf.cpp \
./audio/AudioOutput.cpp \
./OpusEncoder.cpp \
./os/android/AudioOutputOpenSLES.cpp \
./JitterBuffer.cpp \
./OpusDecoder.cpp \
./os/android/OpenSLEngineWrapper.cpp \
./os/android/AudioInputAndroid.cpp \
./os/android/AudioOutputAndroid.cpp \
./EchoCanceller.cpp \
./CongestionControl.cpp \
./VoIPServerConfig.cpp \
./audio/Resampler.cpp \
./NetworkSocket.cpp \
./os/posix/NetworkSocketPosix.cpp \
./PacketReassembler.cpp \
./MessageThread.cpp \
./json11.cpp \
./audio/AudioIO.cpp \
./video/VideoRenderer.cpp \
./video/VideoSource.cpp \
./video/ScreamCongestionController.cpp \
./os/android/VideoSourceAndroid.cpp \
./os/android/VideoRendererAndroid.cpp \
./client/android/org_telegram_messenger_voip_TgVoip.cpp \
./client/android/tg_voip_jni.cpp
# WebRTC signal processing
LOCAL_SRC_FILES += \
./webrtc_dsp/system_wrappers/source/field_trial.cc \
./webrtc_dsp/system_wrappers/source/metrics.cc \
./webrtc_dsp/system_wrappers/source/cpu_features.cc \
./webrtc_dsp/absl/strings/internal/memutil.cc \
./webrtc_dsp/absl/strings/string_view.cc \
./webrtc_dsp/absl/strings/ascii.cc \
./webrtc_dsp/absl/types/bad_optional_access.cc \
./webrtc_dsp/absl/types/optional.cc \
./webrtc_dsp/absl/base/internal/raw_logging.cc \
./webrtc_dsp/absl/base/internal/throw_delegate.cc \
./webrtc_dsp/rtc_base/race_checker.cc \
./webrtc_dsp/rtc_base/strings/string_builder.cc \
./webrtc_dsp/rtc_base/memory/aligned_malloc.cc \
./webrtc_dsp/rtc_base/timeutils.cc \
./webrtc_dsp/rtc_base/platform_file.cc \
./webrtc_dsp/rtc_base/string_to_number.cc \
./webrtc_dsp/rtc_base/thread_checker_impl.cc \
./webrtc_dsp/rtc_base/stringencode.cc \
./webrtc_dsp/rtc_base/stringutils.cc \
./webrtc_dsp/rtc_base/checks.cc \
./webrtc_dsp/rtc_base/platform_thread.cc \
./webrtc_dsp/rtc_base/criticalsection.cc \
./webrtc_dsp/rtc_base/platform_thread_types.cc \
./webrtc_dsp/rtc_base/event.cc \
./webrtc_dsp/rtc_base/event_tracer.cc \
./webrtc_dsp/rtc_base/logging_webrtc.cc \
./webrtc_dsp/third_party/rnnoise/src/rnn_vad_weights.cc \
./webrtc_dsp/third_party/rnnoise/src/kiss_fft.cc \
./webrtc_dsp/api/audio/audio_frame.cc \
./webrtc_dsp/api/audio/echo_canceller3_config.cc \
./webrtc_dsp/api/audio/echo_canceller3_factory.cc \
./webrtc_dsp/modules/third_party/fft/fft.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_estimator.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines_logist.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/filterbanks.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/transform.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_filter.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/filter_functions.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/decode.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lattice.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/intialize.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_tables.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/encode.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_analysis.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines_hist.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/entropy_coding.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/isac_vad.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/crc.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/decode_bwe.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/isac.c \
./webrtc_dsp/modules/audio_processing/rms_level.cc \
./webrtc_dsp/modules/audio_processing/echo_detector/normalized_covariance_estimator.cc \
./webrtc_dsp/modules/audio_processing/echo_detector/moving_max.cc \
./webrtc_dsp/modules/audio_processing/echo_detector/circular_buffer.cc \
./webrtc_dsp/modules/audio_processing/echo_detector/mean_variance_estimator.cc \
./webrtc_dsp/modules/audio_processing/splitting_filter.cc \
./webrtc_dsp/modules/audio_processing/gain_control_impl.cc \
./webrtc_dsp/modules/audio_processing/ns/nsx_core.c \
./webrtc_dsp/modules/audio_processing/ns/noise_suppression_x.c \
./webrtc_dsp/modules/audio_processing/ns/nsx_core_c.c \
./webrtc_dsp/modules/audio_processing/ns/ns_core.c \
./webrtc_dsp/modules/audio_processing/ns/noise_suppression.c \
./webrtc_dsp/modules/audio_processing/audio_buffer.cc \
./webrtc_dsp/modules/audio_processing/typing_detection.cc \
./webrtc_dsp/modules/audio_processing/include/audio_processing_statistics.cc \
./webrtc_dsp/modules/audio_processing/include/audio_generator_factory.cc \
./webrtc_dsp/modules/audio_processing/include/aec_dump.cc \
./webrtc_dsp/modules/audio_processing/include/audio_processing.cc \
./webrtc_dsp/modules/audio_processing/include/config.cc \
./webrtc_dsp/modules/audio_processing/agc2/interpolated_gain_curve.cc \
./webrtc_dsp/modules/audio_processing/agc2/agc2_common.cc \
./webrtc_dsp/modules/audio_processing/agc2/gain_applier.cc \
./webrtc_dsp/modules/audio_processing/agc2/adaptive_agc.cc \
./webrtc_dsp/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc \
./webrtc_dsp/modules/audio_processing/agc2/limiter.cc \
./webrtc_dsp/modules/audio_processing/agc2/saturation_protector.cc \
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc \
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/rnn.cc \
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc \
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/spectral_features.cc \
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_search.cc \
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/features_extraction.cc \
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/fft_util.cc \
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/lp_residual.cc \
./webrtc_dsp/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.cc \
./webrtc_dsp/modules/audio_processing/agc2/vector_float_frame.cc \
./webrtc_dsp/modules/audio_processing/agc2/noise_level_estimator.cc \
./webrtc_dsp/modules/audio_processing/agc2/agc2_testing_common.cc \
./webrtc_dsp/modules/audio_processing/agc2/fixed_digital_level_estimator.cc \
./webrtc_dsp/modules/audio_processing/agc2/fixed_gain_controller.cc \
./webrtc_dsp/modules/audio_processing/agc2/vad_with_level.cc \
./webrtc_dsp/modules/audio_processing/agc2/limiter_db_gain_curve.cc \
./webrtc_dsp/modules/audio_processing/agc2/down_sampler.cc \
./webrtc_dsp/modules/audio_processing/agc2/signal_classifier.cc \
./webrtc_dsp/modules/audio_processing/agc2/noise_spectrum_estimator.cc \
./webrtc_dsp/modules/audio_processing/agc2/compute_interpolated_gain_curve.cc \
./webrtc_dsp/modules/audio_processing/agc2/biquad_filter.cc \
./webrtc_dsp/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc \
./webrtc_dsp/modules/audio_processing/transient/moving_moments.cc \
./webrtc_dsp/modules/audio_processing/transient/wpd_tree.cc \
./webrtc_dsp/modules/audio_processing/transient/wpd_node.cc \
./webrtc_dsp/modules/audio_processing/transient/transient_suppressor.cc \
./webrtc_dsp/modules/audio_processing/transient/transient_detector.cc \
./webrtc_dsp/modules/audio_processing/low_cut_filter.cc \
./webrtc_dsp/modules/audio_processing/level_estimator_impl.cc \
./webrtc_dsp/modules/audio_processing/three_band_filter_bank.cc \
./webrtc_dsp/modules/audio_processing/aec/echo_cancellation.cc \
./webrtc_dsp/modules/audio_processing/aec/aec_resampler.cc \
./webrtc_dsp/modules/audio_processing/aec/aec_core.cc \
./webrtc_dsp/modules/audio_processing/voice_detection_impl.cc \
./webrtc_dsp/modules/audio_processing/echo_cancellation_impl.cc \
./webrtc_dsp/modules/audio_processing/gain_control_for_experimental_agc.cc \
./webrtc_dsp/modules/audio_processing/agc/agc.cc \
./webrtc_dsp/modules/audio_processing/agc/loudness_histogram.cc \
./webrtc_dsp/modules/audio_processing/agc/agc_manager_direct.cc \
./webrtc_dsp/modules/audio_processing/agc/legacy/analog_agc.c \
./webrtc_dsp/modules/audio_processing/agc/legacy/digital_agc.c \
./webrtc_dsp/modules/audio_processing/agc/utility.cc \
./webrtc_dsp/modules/audio_processing/audio_processing_impl.cc \
./webrtc_dsp/modules/audio_processing/audio_generator/file_audio_generator.cc \
./webrtc_dsp/modules/audio_processing/gain_controller2.cc \
./webrtc_dsp/modules/audio_processing/residual_echo_detector.cc \
./webrtc_dsp/modules/audio_processing/noise_suppression_impl.cc \
./webrtc_dsp/modules/audio_processing/aecm/aecm_core.cc \
./webrtc_dsp/modules/audio_processing/aecm/aecm_core_c.cc \
./webrtc_dsp/modules/audio_processing/aecm/echo_control_mobile.cc \
./webrtc_dsp/modules/audio_processing/aec3/render_reverb_model.cc \
./webrtc_dsp/modules/audio_processing/aec3/reverb_model_fallback.cc \
./webrtc_dsp/modules/audio_processing/aec3/echo_remover_metrics.cc \
./webrtc_dsp/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc \
./webrtc_dsp/modules/audio_processing/aec3/render_delay_buffer2.cc \
./webrtc_dsp/modules/audio_processing/aec3/echo_path_variability.cc \
./webrtc_dsp/modules/audio_processing/aec3/frame_blocker.cc \
./webrtc_dsp/modules/audio_processing/aec3/subtractor.cc \
./webrtc_dsp/modules/audio_processing/aec3/aec3_fft.cc \
./webrtc_dsp/modules/audio_processing/aec3/fullband_erle_estimator.cc \
./webrtc_dsp/modules/audio_processing/aec3/suppression_filter.$(CC_NEON) \
./webrtc_dsp/modules/audio_processing/aec3/block_processor.cc \
./webrtc_dsp/modules/audio_processing/aec3/subband_erle_estimator.cc \
./webrtc_dsp/modules/audio_processing/aec3/render_delay_controller_metrics.cc \
./webrtc_dsp/modules/audio_processing/aec3/render_delay_buffer.cc \
./webrtc_dsp/modules/audio_processing/aec3/vector_buffer.cc \
./webrtc_dsp/modules/audio_processing/aec3/erl_estimator.cc \
./webrtc_dsp/modules/audio_processing/aec3/aec_state.cc \
./webrtc_dsp/modules/audio_processing/aec3/adaptive_fir_filter.$(CC_NEON) \
./webrtc_dsp/modules/audio_processing/aec3/render_delay_controller.cc \
./webrtc_dsp/modules/audio_processing/aec3/skew_estimator.cc \
./webrtc_dsp/modules/audio_processing/aec3/echo_path_delay_estimator.cc \
./webrtc_dsp/modules/audio_processing/aec3/block_framer.cc \
./webrtc_dsp/modules/audio_processing/aec3/erle_estimator.cc \
./webrtc_dsp/modules/audio_processing/aec3/reverb_model.cc \
./webrtc_dsp/modules/audio_processing/aec3/cascaded_biquad_filter.cc \
./webrtc_dsp/modules/audio_processing/aec3/render_buffer.cc \
./webrtc_dsp/modules/audio_processing/aec3/subtractor_output.cc \
./webrtc_dsp/modules/audio_processing/aec3/stationarity_estimator.cc \
./webrtc_dsp/modules/audio_processing/aec3/render_signal_analyzer.cc \
./webrtc_dsp/modules/audio_processing/aec3/subtractor_output_analyzer.cc \
./webrtc_dsp/modules/audio_processing/aec3/suppression_gain.$(CC_NEON) \
./webrtc_dsp/modules/audio_processing/aec3/echo_audibility.cc \
./webrtc_dsp/modules/audio_processing/aec3/block_processor_metrics.cc \
./webrtc_dsp/modules/audio_processing/aec3/moving_average.cc \
./webrtc_dsp/modules/audio_processing/aec3/reverb_model_estimator.cc \
./webrtc_dsp/modules/audio_processing/aec3/aec3_common.cc \
./webrtc_dsp/modules/audio_processing/aec3/residual_echo_estimator.cc \
./webrtc_dsp/modules/audio_processing/aec3/matched_filter.$(CC_NEON) \
./webrtc_dsp/modules/audio_processing/aec3/reverb_decay_estimator.cc \
./webrtc_dsp/modules/audio_processing/aec3/render_delay_controller2.cc \
./webrtc_dsp/modules/audio_processing/aec3/suppression_gain_limiter.cc \
./webrtc_dsp/modules/audio_processing/aec3/main_filter_update_gain.cc \
./webrtc_dsp/modules/audio_processing/aec3/echo_remover.cc \
./webrtc_dsp/modules/audio_processing/aec3/downsampled_render_buffer.cc \
./webrtc_dsp/modules/audio_processing/aec3/matrix_buffer.cc \
./webrtc_dsp/modules/audio_processing/aec3/block_processor2.cc \
./webrtc_dsp/modules/audio_processing/aec3/echo_canceller3.cc \
./webrtc_dsp/modules/audio_processing/aec3/block_delay_buffer.cc \
./webrtc_dsp/modules/audio_processing/aec3/fft_buffer.cc \
./webrtc_dsp/modules/audio_processing/aec3/comfort_noise_generator.$(CC_NEON) \
./webrtc_dsp/modules/audio_processing/aec3/shadow_filter_update_gain.cc \
./webrtc_dsp/modules/audio_processing/aec3/filter_analyzer.cc \
./webrtc_dsp/modules/audio_processing/aec3/reverb_frequency_response.cc \
./webrtc_dsp/modules/audio_processing/aec3/decimator.cc \
./webrtc_dsp/modules/audio_processing/echo_control_mobile_impl.cc \
./webrtc_dsp/modules/audio_processing/logging/apm_data_dumper.cc \
./webrtc_dsp/modules/audio_processing/vad/voice_activity_detector.cc \
./webrtc_dsp/modules/audio_processing/vad/standalone_vad.cc \
./webrtc_dsp/modules/audio_processing/vad/pitch_internal.cc \
./webrtc_dsp/modules/audio_processing/vad/vad_circular_buffer.cc \
./webrtc_dsp/modules/audio_processing/vad/vad_audio_proc.cc \
./webrtc_dsp/modules/audio_processing/vad/pole_zero_filter.cc \
./webrtc_dsp/modules/audio_processing/vad/pitch_based_vad.cc \
./webrtc_dsp/modules/audio_processing/vad/gmm.cc \
./webrtc_dsp/modules/audio_processing/utility/ooura_fft.cc \
./webrtc_dsp/modules/audio_processing/utility/delay_estimator_wrapper.cc \
./webrtc_dsp/modules/audio_processing/utility/delay_estimator.cc \
./webrtc_dsp/modules/audio_processing/utility/block_mean_calculator.cc \
./webrtc_dsp/common_audio/window_generator.cc \
./webrtc_dsp/common_audio/channel_buffer.cc \
./webrtc_dsp/common_audio/fir_filter_factory.cc \
./webrtc_dsp/common_audio/wav_header.cc \
./webrtc_dsp/common_audio/real_fourier_ooura.cc \
./webrtc_dsp/common_audio/audio_util.cc \
./webrtc_dsp/common_audio/resampler/push_sinc_resampler.cc \
./webrtc_dsp/common_audio/resampler/resampler.cc \
./webrtc_dsp/common_audio/resampler/push_resampler.cc \
./webrtc_dsp/common_audio/resampler/sinc_resampler.cc \
./webrtc_dsp/common_audio/resampler/sinusoidal_linear_chirp_source.cc \
./webrtc_dsp/common_audio/wav_file.cc \
./webrtc_dsp/common_audio/third_party/fft4g/fft4g.c \
./webrtc_dsp/common_audio/audio_converter.cc \
./webrtc_dsp/common_audio/real_fourier.cc \
./webrtc_dsp/common_audio/sparse_fir_filter.cc \
./webrtc_dsp/common_audio/smoothing_filter.cc \
./webrtc_dsp/common_audio/fir_filter_c.cc \
./webrtc_dsp/common_audio/ring_buffer.c \
./webrtc_dsp/common_audio/signal_processing/complex_fft.c \
./webrtc_dsp/common_audio/signal_processing/filter_ma_fast_q12.c \
./webrtc_dsp/common_audio/signal_processing/levinson_durbin.c \
./webrtc_dsp/common_audio/signal_processing/dot_product_with_scale.cc \
./webrtc_dsp/common_audio/signal_processing/auto_corr_to_refl_coef.c \
./webrtc_dsp/common_audio/signal_processing/resample_by_2_internal.c \
./webrtc_dsp/common_audio/signal_processing/energy.c \
./webrtc_dsp/common_audio/signal_processing/sqrt_of_one_minus_x_squared.c \
./webrtc_dsp/common_audio/signal_processing/downsample_fast.c \
./webrtc_dsp/common_audio/signal_processing/splitting_filter1.c \
./webrtc_dsp/common_audio/signal_processing/spl_init.c \
./webrtc_dsp/common_audio/signal_processing/lpc_to_refl_coef.c \
./webrtc_dsp/common_audio/signal_processing/cross_correlation.c \
./webrtc_dsp/common_audio/signal_processing/division_operations.c \
./webrtc_dsp/common_audio/signal_processing/auto_correlation.c \
./webrtc_dsp/common_audio/signal_processing/get_scaling_square.c \
./webrtc_dsp/common_audio/signal_processing/resample.c \
./webrtc_dsp/common_audio/signal_processing/min_max_operations.c \
./webrtc_dsp/common_audio/signal_processing/refl_coef_to_lpc.c \
./webrtc_dsp/common_audio/signal_processing/filter_ar.c \
./webrtc_dsp/common_audio/signal_processing/vector_scaling_operations.c \
./webrtc_dsp/common_audio/signal_processing/resample_fractional.c \
./webrtc_dsp/common_audio/signal_processing/real_fft.c \
./webrtc_dsp/common_audio/signal_processing/ilbc_specific_functions.c \
./webrtc_dsp/common_audio/signal_processing/randomization_functions.c \
./webrtc_dsp/common_audio/signal_processing/copy_set_operations.c \
./webrtc_dsp/common_audio/signal_processing/resample_by_2.c \
./webrtc_dsp/common_audio/signal_processing/get_hanning_window.c \
./webrtc_dsp/common_audio/signal_processing/resample_48khz.c \
./webrtc_dsp/common_audio/signal_processing/spl_inl.c \
./webrtc_dsp/common_audio/signal_processing/spl_sqrt.c \
./webrtc_dsp/common_audio/vad/vad_sp.c \
./webrtc_dsp/common_audio/vad/vad.cc \
./webrtc_dsp/common_audio/vad/webrtc_vad.c \
./webrtc_dsp/common_audio/vad/vad_filterbank.c \
./webrtc_dsp/common_audio/vad/vad_core.c \
./webrtc_dsp/common_audio/vad/vad_gmm.c
ifeq ($(TARGET_ARCH_ABI),$(filter $(TARGET_ARCH_ABI),armeabi-v7a arm64-v8a))
LOCAL_SRC_FILES += \
./webrtc_dsp/modules/audio_processing/ns/nsx_core_neon.c.neon \
./webrtc_dsp/modules/audio_processing/aec/aec_core_neon.cc.neon \
./webrtc_dsp/modules/audio_processing/aecm/aecm_core_neon.cc.neon \
./webrtc_dsp/modules/audio_processing/utility/ooura_fft_neon.cc.neon \
./webrtc_dsp/common_audio/fir_filter_neon.cc.neon \
./webrtc_dsp/common_audio/resampler/sinc_resampler_neon.cc.neon \
./webrtc_dsp/common_audio/signal_processing/downsample_fast_neon.c.neon \
./webrtc_dsp/common_audio/signal_processing/min_max_operations_neon.c.neon \
./webrtc_dsp/common_audio/signal_processing/cross_correlation_neon.c.neon
endif
ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
LOCAL_SRC_FILES += \
./webrtc_dsp/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor_arm.S.neon \
./webrtc_dsp/common_audio/signal_processing/complex_bit_reverse_arm.S.neon \
./webrtc_dsp/common_audio/signal_processing/filter_ar_fast_q12_armv7.S.neon
else
LOCAL_SRC_FILES += \
./webrtc_dsp/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.c \
./webrtc_dsp/common_audio/signal_processing/complex_bit_reverse.c \
./webrtc_dsp/common_audio/signal_processing/filter_ar_fast_q12.c
endif
ifeq ($(TARGET_ARCH_ABI),$(filter $(TARGET_ARCH_ABI),x86 x86_64))
LOCAL_SRC_FILES += \
./webrtc_dsp/modules/audio_processing/aec/aec_core_sse2.cc \
./webrtc_dsp/modules/audio_processing/utility/ooura_fft_sse2.cc \
./webrtc_dsp/common_audio/fir_filter_sse.cc \
./webrtc_dsp/common_audio/resampler/sinc_resampler_sse.cc
endif
# Opus
LOCAL_SRC_FILES += \
./../opus/src/opus.c \
./../opus/src/opus_decoder.c \
./../opus/src/opus_encoder.c \
./../opus/src/opus_multistream.c \
./../opus/src/opus_multistream_encoder.c \
./../opus/src/opus_multistream_decoder.c \
./../opus/src/repacketizer.c \
./../opus/src/analysis.c \
./../opus/src/mlp.c \
./../opus/src/mlp_data.c \
./../opus/src/opus_projection_encoder.c \
./../opus/src/opus_projection_decoder.c \
./../opus/src/mapping_matrix.c
ifeq ($(TARGET_ARCH_ABI),$(filter $(TARGET_ARCH_ABI),armeabi-v7a arm64-v8a))
LOCAL_ARM_MODE := arm
LOCAL_CPPFLAGS += -DLIBYUV_NEON
LOCAL_CFLAGS += -DLIBYUV_NEON
LOCAL_CFLAGS += -DOPUS_HAVE_RTCD -DOPUS_ARM_ASM
LOCAL_SRC_FILES += \
./../opus/celt/arm/celt_neon_intr.c.neon \
./../opus/celt/arm/pitch_neon_intr.c.neon \
./../opus/silk/arm/NSQ_neon.c.neon \
./../opus/silk/arm/arm_silk_map.c \
./../opus/silk/arm/LPC_inv_pred_gain_neon_intr.c.neon \
./../opus/silk/arm/NSQ_del_dec_neon_intr.c.neon \
./../opus/silk/arm/biquad_alt_neon_intr.c.neon \
./../opus/silk/fixed/arm/warped_autocorrelation_FIX_neon_intr.c.neon
# LOCAL_SRC_FILES += ./../opus/celt/arm/celt_pitch_xcorr_arm-gnu.S
else
ifeq ($(TARGET_ARCH_ABI),x86)
LOCAL_CFLAGS += -Dx86fix
LOCAL_CPPFLAGS += -Dx86fix
LOCAL_ARM_MODE := arm
# LOCAL_SRC_FILES += \
# ./libyuv/source/row_x86.asm
# LOCAL_SRC_FILES += \
# ./../opus/celt/x86/celt_lpc_sse.c \
# ./../opus/celt/x86/pitch_sse.c \
# ./../opus/celt/x86/pitch_sse2.c \
# ./../opus/celt/x86/pitch_sse4_1.c \
# ./../opus/celt/x86/vq_sse2.c \
# ./../opus/celt/x86/x86_celt_map.c \
# ./../opus/celt/x86/x86cpu.c \
# ./../opus/silk/fixed/x86/burg_modified_FIX_sse.c \
# ./../opus/silk/fixed/x86/vector_ops_FIX_sse.c \
# ./../opus/silk/x86/NSQ_del_dec_sse.c \
# ./../opus/silk/x86/NSQ_sse.c \
# ./../opus/silk/x86/VAD_sse.c \
# ./../opus/silk/x86/VQ_WMat_sse.c \
# ./../opus/silk/x86/x86_silk_map.c
endif
endif
LOCAL_SRC_FILES += \
./../opus/silk/CNG.c \
./../opus/silk/code_signs.c \
./../opus/silk/init_decoder.c \
./../opus/silk/decode_core.c \
./../opus/silk/decode_frame.c \
./../opus/silk/decode_parameters.c \
./../opus/silk/decode_indices.c \
./../opus/silk/decode_pulses.c \
./../opus/silk/decoder_set_fs.c \
./../opus/silk/dec_API.c \
./../opus/silk/enc_API.c \
./../opus/silk/encode_indices.c \
./../opus/silk/encode_pulses.c \
./../opus/silk/gain_quant.c \
./../opus/silk/interpolate.c \
./../opus/silk/LP_variable_cutoff.c \
./../opus/silk/NLSF_decode.c \
./../opus/silk/NSQ.c \
./../opus/silk/NSQ_del_dec.c \
./../opus/silk/PLC.c \
./../opus/silk/shell_coder.c \
./../opus/silk/tables_gain.c \
./../opus/silk/tables_LTP.c \
./../opus/silk/tables_NLSF_CB_NB_MB.c \
./../opus/silk/tables_NLSF_CB_WB.c \
./../opus/silk/tables_other.c \
./../opus/silk/tables_pitch_lag.c \
./../opus/silk/tables_pulses_per_block.c \
./../opus/silk/VAD.c \
./../opus/silk/control_audio_bandwidth.c \
./../opus/silk/quant_LTP_gains.c \
./../opus/silk/VQ_WMat_EC.c \
./../opus/silk/HP_variable_cutoff.c \
./../opus/silk/NLSF_encode.c \
./../opus/silk/NLSF_VQ.c \
./../opus/silk/NLSF_unpack.c \
./../opus/silk/NLSF_del_dec_quant.c \
./../opus/silk/process_NLSFs.c \
./../opus/silk/stereo_LR_to_MS.c \
./../opus/silk/stereo_MS_to_LR.c \
./../opus/silk/check_control_input.c \
./../opus/silk/control_SNR.c \
./../opus/silk/init_encoder.c \
./../opus/silk/control_codec.c \
./../opus/silk/A2NLSF.c \
./../opus/silk/ana_filt_bank_1.c \
./../opus/silk/biquad_alt.c \
./../opus/silk/bwexpander_32.c \
./../opus/silk/bwexpander.c \
./../opus/silk/debug.c \
./../opus/silk/decode_pitch.c \
./../opus/silk/inner_prod_aligned.c \
./../opus/silk/lin2log.c \
./../opus/silk/log2lin.c \
./../opus/silk/LPC_analysis_filter.c \
./../opus/silk/LPC_inv_pred_gain.c \
./../opus/silk/table_LSF_cos.c \
./../opus/silk/NLSF2A.c \
./../opus/silk/NLSF_stabilize.c \
./../opus/silk/NLSF_VQ_weights_laroia.c \
./../opus/silk/pitch_est_tables.c \
./../opus/silk/resampler.c \
./../opus/silk/resampler_down2_3.c \
./../opus/silk/resampler_down2.c \
./../opus/silk/resampler_private_AR2.c \
./../opus/silk/resampler_private_down_FIR.c \
./../opus/silk/resampler_private_IIR_FIR.c \
./../opus/silk/resampler_private_up2_HQ.c \
./../opus/silk/resampler_rom.c \
./../opus/silk/sigm_Q15.c \
./../opus/silk/sort.c \
./../opus/silk/sum_sqr_shift.c \
./../opus/silk/stereo_decode_pred.c \
./../opus/silk/stereo_encode_pred.c \
./../opus/silk/stereo_find_predictor.c \
./../opus/silk/stereo_quant_pred.c \
./../opus/silk/LPC_fit.c
LOCAL_SRC_FILES += \
./../opus/silk/fixed/LTP_analysis_filter_FIX.c \
./../opus/silk/fixed/LTP_scale_ctrl_FIX.c \
./../opus/silk/fixed/corrMatrix_FIX.c \
./../opus/silk/fixed/encode_frame_FIX.c \
./../opus/silk/fixed/find_LPC_FIX.c \
./../opus/silk/fixed/find_LTP_FIX.c \
./../opus/silk/fixed/find_pitch_lags_FIX.c \
./../opus/silk/fixed/find_pred_coefs_FIX.c \
./../opus/silk/fixed/noise_shape_analysis_FIX.c \
./../opus/silk/fixed/process_gains_FIX.c \
./../opus/silk/fixed/regularize_correlations_FIX.c \
./../opus/silk/fixed/residual_energy16_FIX.c \
./../opus/silk/fixed/residual_energy_FIX.c \
./../opus/silk/fixed/warped_autocorrelation_FIX.c \
./../opus/silk/fixed/apply_sine_window_FIX.c \
./../opus/silk/fixed/autocorr_FIX.c \
./../opus/silk/fixed/burg_modified_FIX.c \
./../opus/silk/fixed/k2a_FIX.c \
./../opus/silk/fixed/k2a_Q16_FIX.c \
./../opus/silk/fixed/pitch_analysis_core_FIX.c \
./../opus/silk/fixed/vector_ops_FIX.c \
./../opus/silk/fixed/schur64_FIX.c \
./../opus/silk/fixed/schur_FIX.c
LOCAL_SRC_FILES += \
./../opus/celt/bands.c \
./../opus/celt/celt.c \
./../opus/celt/celt_encoder.c \
./../opus/celt/celt_decoder.c \
./../opus/celt/cwrs.c \
./../opus/celt/entcode.c \
./../opus/celt/entdec.c \
./../opus/celt/entenc.c \
./../opus/celt/kiss_fft.c \
./../opus/celt/laplace.c \
./../opus/celt/mathops.c \
./../opus/celt/mdct.c \
./../opus/celt/modes.c \
./../opus/celt/pitch.c \
./../opus/celt/celt_lpc.c \
./../opus/celt/quant_bands.c \
./../opus/celt/rate.c \
./../opus/celt/vq.c \
./../opus/celt/arm/armcpu.c \
./../opus/celt/arm/arm_celt_map.c
LOCAL_SRC_FILES += \
./../opus/ogg/bitwise.c \
./../opus/ogg/framing.c \
./../opus/opusfile/info.c \
./../opus/opusfile/internal.c \
./../opus/opusfile/opusfile.c \
./../opus/opusfile/stream.c
include $(BUILD_SHARED_LIBRARY)

View File

@ -0,0 +1,10 @@
//
// libtgvoip is free and unencumbered public domain software.
// For more information, see http://unlicense.org or the UNLICENSE file
// you should have received with this source code distribution.
//
#include "BlockingQueue.h"
using namespace tgvoip;

View File

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

View File

@ -0,0 +1,237 @@
//
// 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 "Buffers.h"
#include <assert.h>
#include <string.h>
#include <exception>
#include <stdexcept>
#include <stdlib.h>
#include "logging.h"
using namespace tgvoip;
#pragma mark - BufferInputStream
BufferInputStream::BufferInputStream(const unsigned char* data, size_t length){
this->buffer=data;
this->length=length;
offset=0;
}
BufferInputStream::BufferInputStream(const Buffer &buffer){
this->buffer=*buffer;
this->length=buffer.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::ReadBytes(Buffer &to){
ReadBytes(*to, to.Length());
}
BufferInputStream BufferInputStream::GetPartBuffer(size_t length, bool advance){
EnsureEnoughRemaining(length);
BufferInputStream s=BufferInputStream(buffer+offset, length);
if(advance)
offset+=length;
return s;
}
void BufferInputStream::EnsureEnoughRemaining(size_t need){
if(length-offset<need){
throw std::out_of_range("Not enough bytes in buffer");
}
}
#pragma mark - BufferOutputStream
BufferOutputStream::BufferOutputStream(size_t size){
buffer=(unsigned char*) malloc(size);
if(!buffer)
throw std::bad_alloc();
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 && buffer)
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(const unsigned char *bytes, size_t count){
this->ExpandBufferIfNeeded(count);
memcpy(buffer+offset, bytes, count);
offset+=count;
}
void BufferOutputStream::WriteBytes(const Buffer &buffer){
WriteBytes(*buffer, buffer.Length());
}
void BufferOutputStream::WriteBytes(const Buffer &buffer, size_t offset, size_t count){
if(offset+count>buffer.Length())
throw std::out_of_range("offset out of buffer bounds");
WriteBytes(*buffer+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");
}
unsigned char* new_buffer;
need = std::max(need, size_t{1024});
new_buffer = reinterpret_cast<unsigned char*>(std::realloc(buffer, size + need));
if (new_buffer == NULL) {
std::free(buffer);
buffer = NULL;
throw std::bad_alloc();
}
buffer = new_buffer;
size += need;
}
}
void BufferOutputStream::Reset(){
offset=0;
}
void BufferOutputStream::Rewind(size_t numBytes){
if(numBytes>offset)
throw std::out_of_range("buffer underflow");
offset-=numBytes;
}

View File

@ -0,0 +1,357 @@
//
// 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>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdexcept>
#include <array>
#include <limits>
#include <bitset>
#include <stddef.h>
#include "threading.h"
#include "utils.h"
namespace tgvoip{
class Buffer;
class BufferInputStream{
public:
BufferInputStream(const unsigned char* data, size_t length);
BufferInputStream(const Buffer& buffer);
~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);
void ReadBytes(Buffer& to);
BufferInputStream GetPartBuffer(size_t length, bool advance);
private:
void EnsureEnoughRemaining(size_t need);
const unsigned char* buffer;
size_t length;
size_t offset;
};
class BufferOutputStream{
friend class Buffer;
public:
TGVOIP_DISALLOW_COPY_AND_ASSIGN(BufferOutputStream);
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(const unsigned char* bytes, size_t count);
void WriteBytes(const Buffer& buffer);
void WriteBytes(const Buffer& buffer, size_t offset, size_t count);
unsigned char* GetBuffer();
size_t GetLength();
void Reset();
void Rewind(size_t numBytes);
BufferOutputStream& operator=(BufferOutputStream&& other){
if(this!=&other){
if(!bufferProvided && buffer)
free(buffer);
buffer=other.buffer;
offset=other.offset;
size=other.size;
bufferProvided=other.bufferProvided;
other.buffer=NULL;
}
return *this;
}
private:
void ExpandBufferIfNeeded(size_t need);
unsigned char* buffer=NULL;
size_t size;
size_t offset;
bool bufferProvided;
};
class Buffer{
public:
Buffer(size_t capacity){
if(capacity>0){
data=(unsigned char *) malloc(capacity);
if(!data)
throw std::bad_alloc();
}else{
data=NULL;
}
length=capacity;
};
TGVOIP_DISALLOW_COPY_AND_ASSIGN(Buffer); // use Buffer::CopyOf to copy contents explicitly
Buffer(Buffer&& other) noexcept {
data=other.data;
length=other.length;
freeFn=other.freeFn;
reallocFn=other.reallocFn;
other.data=NULL;
};
Buffer(BufferOutputStream&& stream){
data=stream.buffer;
length=stream.offset;
stream.buffer=NULL;
}
Buffer(){
data=NULL;
length=0;
}
~Buffer(){
if(data){
if(freeFn)
freeFn(data);
else
free(data);
}
data=NULL;
length=0;
};
Buffer& operator=(Buffer&& other){
if(this!=&other){
if(data){
if(freeFn)
freeFn(data);
else
free(data);
}
data=other.data;
length=other.length;
freeFn=other.freeFn;
reallocFn=other.reallocFn;
other.data=NULL;
other.length=0;
}
return *this;
}
unsigned char& operator[](size_t i){
if(i>=length)
throw std::out_of_range("");
return data[i];
}
const unsigned char& operator[](size_t i) const{
if(i>=length)
throw std::out_of_range("");
return data[i];
}
unsigned char* operator*(){
return data;
}
const unsigned char* operator*() const{
return data;
}
void CopyFrom(const Buffer& other, size_t count, size_t srcOffset=0, size_t dstOffset=0){
if(!other.data)
throw std::invalid_argument("CopyFrom can't copy from NULL");
if(other.length<srcOffset+count || length<dstOffset+count)
throw std::out_of_range("Out of offset+count bounds of either buffer");
memcpy(data+dstOffset, other.data+srcOffset, count);
}
void CopyFrom(const void* ptr, size_t dstOffset, size_t count){
if(length<dstOffset+count)
throw std::out_of_range("Offset+count is out of bounds");
memcpy(data+dstOffset, ptr, count);
}
void Resize(size_t newSize){
if(reallocFn)
data=(unsigned char *) reallocFn(data, newSize);
else
data=(unsigned char *) realloc(data, newSize);
if(!data)
throw std::bad_alloc();
length=newSize;
}
size_t Length() const{
return length;
}
bool IsEmpty() const{
return length==0 || !data;
}
static Buffer CopyOf(const Buffer& other){
if(other.IsEmpty())
return Buffer();
Buffer buf(other.length);
buf.CopyFrom(other, other.length);
return buf;
}
static Buffer CopyOf(const Buffer& other, size_t offset, size_t length){
if(offset+length>other.Length())
throw std::out_of_range("offset+length out of bounds");
Buffer buf(length);
buf.CopyFrom(other, length, offset);
return buf;
}
static Buffer Wrap(unsigned char* data, size_t size, std::function<void(void*)> freeFn, std::function<void*(void*, size_t)> reallocFn){
Buffer b=Buffer();
b.data=data;
b.length=size;
b.freeFn=freeFn;
b.reallocFn=reallocFn;
return b;
}
private:
unsigned char* data;
size_t length;
std::function<void(void*)> freeFn;
std::function<void*(void*, size_t)> reallocFn;
};
template <typename T, size_t size, typename AVG_T=T> class HistoricBuffer{
public:
HistoricBuffer(){
std::fill(data.begin(), data.end(), (T)0);
}
AVG_T Average() const {
AVG_T avg=(AVG_T)0;
for(T i:data){
avg+=i;
}
return avg/(AVG_T)size;
}
AVG_T Average(size_t firstN) const {
AVG_T avg=(AVG_T)0;
for(size_t i=0;i<firstN;i++){
avg+=(*this)[i];
}
return avg/(AVG_T)firstN;
}
AVG_T NonZeroAverage() const {
AVG_T avg=(AVG_T)0;
int nonZeroCount=0;
for(T i:data){
if(i!=0){
nonZeroCount++;
avg+=i;
}
}
if(nonZeroCount==0)
return (AVG_T)0;
return avg/(AVG_T)nonZeroCount;
}
void Add(T el){
data[offset]=el;
offset=(offset+1)%size;
}
T Min() const {
T min=std::numeric_limits<T>::max();
for(T i:data){
if(i<min)
min=i;
}
return min;
}
T Max() const {
T max=std::numeric_limits<T>::min();
for(T i:data){
if(i>max)
max=i;
}
return max;
}
void Reset(){
std::fill(data.begin(), data.end(), (T)0);
offset=0;
}
T operator[](size_t i) const {
assert(i<size);
// [0] should return the most recent entry, [1] the one before it, and so on
ptrdiff_t _i=offset-i-1;
if(_i<0)
_i=size+_i;
return data[_i];
}
T& operator[](size_t i){
assert(i<size);
// [0] should return the most recent entry, [1] the one before it, and so on
ptrdiff_t _i=offset-i-1;
if(_i<0)
_i=size+_i;
return data[_i];
}
size_t Size() const {
return size;
}
private:
std::array<T, size> data;
ptrdiff_t offset=0;
};
template <size_t bufSize, size_t bufCount> class BufferPool{
public:
TGVOIP_DISALLOW_COPY_AND_ASSIGN(BufferPool);
BufferPool(){
bufferStart=(unsigned char*)malloc(bufSize*bufCount);
if(!bufferStart)
throw std::bad_alloc();
};
~BufferPool(){
assert(usedBuffers.none());
free(bufferStart);
};
Buffer Get(){
auto freeFn=[this](void* _buf){
assert(_buf!=NULL);
unsigned char* buf=(unsigned char*)_buf;
size_t offset=buf-bufferStart;
assert(offset%bufSize==0);
size_t index=offset/bufSize;
assert(index<bufCount);
MutexGuard m(mutex);
assert(usedBuffers.test(index));
usedBuffers[index]=0;
};
auto resizeFn=[](void* buf, size_t newSize)->void*{
if(newSize>bufSize)
throw std::invalid_argument("newSize>bufferSize");
return buf;
};
MutexGuard m(mutex);
for(size_t i=0;i<bufCount;i++){
if(!usedBuffers[i]){
usedBuffers[i]=1;
return Buffer::Wrap(bufferStart+(bufSize*i), bufSize, freeFn, resizeFn);
}
}
throw std::bad_alloc();
}
private:
std::bitset<bufCount> usedBuffers;
unsigned char* bufferStart;
Mutex mutex;
};
}
#endif //LIBTGVOIP_BUFFERINPUTSTREAM_H

View File

@ -0,0 +1,148 @@
//
// 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 "PrivateDefines.h"
#include <math.h>
#include <assert.h>
using namespace tgvoip;
CongestionControl::CongestionControl(){
memset(inflightPackets, 0, sizeof(inflightPackets));
tmpRtt=0;
tmpRttCount=0;
lastSentSeq=0;
lastActionTime=0;
lastActionRtt=0;
stateTransitionTime=0;
inflightDataSize=0;
lossCount=0;
cwnd=(size_t) ServerConfig::GetSharedInstance()->GetInt("audio_congestion_window", 1024);
}
CongestionControl::~CongestionControl(){
}
size_t CongestionControl::GetAcknowledgedDataSize(){
return 0;
}
double CongestionControl::GetAverageRTT(){
return rttHistory.NonZeroAverage();
}
size_t CongestionControl::GetInflightDataSize(){
return inflightHistory.Average();
}
size_t CongestionControl::GetCongestionWindow(){
return cwnd;
}
double CongestionControl::GetMinimumRTT(){
return rttHistory.Min();
}
void CongestionControl::PacketAcknowledged(uint32_t seq){
for(int 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;
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::PacketLost(uint32_t seq){
for(int i=0;i<100;i++){
if(inflightPackets[i].seq==seq && inflightPackets[i].sendTime>0){
inflightPackets[i].sendTime=0;
inflightDataSize-=inflightPackets[i].size;
lossCount++;
break;
}
}
}
void CongestionControl::Tick(){
tickCount++;
if(tmpRttCount>0){
rttHistory.Add(tmpRtt/tmpRttCount);
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.Add(inflightDataSize);
}
int CongestionControl::GetBandwidthControlAction(){
if(VoIPController::GetCurrentTime()-lastActionTime<1)
return TGVOIP_CONCTL_ACT_NONE;
size_t inflightAvg=GetInflightDataSize();
size_t max=cwnd+cwnd/10;
size_t min=cwnd-cwnd/10;
if(inflightAvg<min){
lastActionTime=VoIPController::GetCurrentTime();
return TGVOIP_CONCTL_ACT_INCREASE;
}
if(inflightAvg>max){
lastActionTime=VoIPController::GetCurrentTime();
return TGVOIP_CONCTL_ACT_DECREASE;
}
return TGVOIP_CONCTL_ACT_NONE;
}
uint32_t CongestionControl::GetSendLossCount(){
return lossCount;
}

View File

@ -0,0 +1,63 @@
//
// 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"
#include "Buffers.h"
#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 PacketLost(uint32_t seq);
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:
HistoricBuffer<double, 100> rttHistory;
HistoricBuffer<size_t, 30> inflightHistory;
tgvoip_congestionctl_packet_t inflightPackets[100];
uint32_t lossCount;
double tmpRtt;
double lastActionTime;
double lastActionRtt;
double stateTransitionTime;
int tmpRttCount;
uint32_t lastSentSeq;
uint32_t tickCount;
size_t inflightDataSize;
size_t cwnd;
};
}
#endif //LIBTGVOIP_CONGESTIONCONTROL_H

View File

@ -0,0 +1,248 @@
//
// 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_NO_DSP
#include "webrtc_dsp/modules/audio_processing/include/audio_processing.h"
#include "webrtc_dsp/api/audio/audio_frame.h"
#endif
#include "EchoCanceller.h"
#include "audio/AudioOutput.h"
#include "audio/AudioInput.h"
#include "logging.h"
#include "VoIPServerConfig.h"
#include <string.h>
#include <stdio.h>
#include <math.h>
using namespace tgvoip;
EchoCanceller::EchoCanceller(bool enableAEC, bool enableNS, bool enableAGC){
#ifndef TGVOIP_NO_DSP
this->enableAEC=enableAEC;
this->enableAGC=enableAGC;
this->enableNS=enableNS;
isOn=true;
webrtc::Config extraConfig;
#ifdef TGVOIP_USE_DESKTOP_DSP
extraConfig.Set(new webrtc::DelayAgnostic(true));
#endif
apm=webrtc::AudioProcessingBuilder().Create(extraConfig);
webrtc::AudioProcessing::Config config;
config.echo_canceller.enabled = enableAEC;
#ifndef TGVOIP_USE_DESKTOP_DSP
config.echo_canceller.mobile_mode = true;
#else
config.echo_canceller.mobile_mode = false;
#endif
config.high_pass_filter.enabled = enableAEC;
config.gain_controller2.enabled = enableAGC;
apm->ApplyConfig(config);
webrtc::NoiseSuppression::Level nsLevel;
#ifdef __APPLE__
switch(ServerConfig::GetSharedInstance()->GetInt("webrtc_ns_level_vpio", 0)){
#else
switch(ServerConfig::GetSharedInstance()->GetInt("webrtc_ns_level", 2)){
#endif
case 0:
nsLevel=webrtc::NoiseSuppression::Level::kLow;
break;
case 1:
nsLevel=webrtc::NoiseSuppression::Level::kModerate;
break;
case 3:
nsLevel=webrtc::NoiseSuppression::Level::kVeryHigh;
break;
case 2:
default:
nsLevel=webrtc::NoiseSuppression::Level::kHigh;
break;
}
apm->noise_suppression()->set_level(nsLevel);
apm->noise_suppression()->Enable(enableNS);
if(enableAGC){
apm->gain_control()->set_mode(webrtc::GainControl::Mode::kAdaptiveDigital);
apm->gain_control()->set_target_level_dbfs(ServerConfig::GetSharedInstance()->GetInt("webrtc_agc_target_level", 9));
apm->gain_control()->enable_limiter(ServerConfig::GetSharedInstance()->GetBoolean("webrtc_agc_enable_limiter", true));
apm->gain_control()->set_compression_gain_db(ServerConfig::GetSharedInstance()->GetInt("webrtc_agc_compression_gain", 20));
}
apm->voice_detection()->set_likelihood(webrtc::VoiceDetection::Likelihood::kVeryLowLikelihood);
audioFrame=new webrtc::AudioFrame();
audioFrame->samples_per_channel_=480;
audioFrame->sample_rate_hz_=48000;
audioFrame->num_channels_=1;
farendQueue=new BlockingQueue<Buffer>(11);
running=true;
bufferFarendThread=new Thread(std::bind(&EchoCanceller::RunBufferFarendThread, this));
bufferFarendThread->SetName("VoipECBufferFarEnd");
bufferFarendThread->Start();
#else
this->enableAEC=this->enableAGC=enableAGC=this->enableNS=enableNS=false;
isOn=true;
#endif
}
EchoCanceller::~EchoCanceller(){
#ifndef TGVOIP_NO_DSP
farendQueue->Put(Buffer());
bufferFarendThread->Join();
delete bufferFarendThread;
delete farendQueue;
delete audioFrame;
delete apm;
#endif
}
void EchoCanceller::Start(){
}
void EchoCanceller::Stop(){
}
void EchoCanceller::SpeakerOutCallback(unsigned char* data, size_t len){
if(len!=960*2 || !enableAEC || !isOn)
return;
#ifndef TGVOIP_NO_DSP
try{
Buffer buf=farendBufferPool.Get();
buf.CopyFrom(data, 0, 960*2);
farendQueue->Put(std::move(buf));
}catch(std::bad_alloc& x){
LOGW("Echo canceller can't keep up with real time");
}
#endif
}
#ifndef TGVOIP_NO_DSP
void EchoCanceller::RunBufferFarendThread(){
webrtc::AudioFrame frame;
frame.num_channels_=1;
frame.sample_rate_hz_=48000;
frame.samples_per_channel_=480;
while(running){
Buffer buf=farendQueue->GetBlocking();
if(buf.IsEmpty()){
LOGI("Echo canceller buffer farend thread exiting");
return;
}
int16_t* samplesIn=(int16_t*)*buf;
memcpy(frame.mutable_data(), samplesIn, 480*2);
apm->ProcessReverseStream(&frame);
memcpy(frame.mutable_data(), samplesIn+480, 480*2);
apm->ProcessReverseStream(&frame);
didBufferFarend=true;
}
}
#endif
void EchoCanceller::Enable(bool enabled){
isOn=enabled;
}
void EchoCanceller::ProcessInput(int16_t* inOut, size_t numSamples, bool& hasVoice){
#ifndef TGVOIP_NO_DSP
if(!isOn || (!enableAEC && !enableAGC && !enableNS)){
return;
}
int delay=audio::AudioInput::GetEstimatedDelay()+audio::AudioOutput::GetEstimatedDelay();
assert(numSamples==960);
memcpy(audioFrame->mutable_data(), inOut, 480*2);
if(enableAEC)
apm->set_stream_delay_ms(delay);
apm->ProcessStream(audioFrame);
if(enableVAD)
hasVoice=apm->voice_detection()->stream_has_voice();
memcpy(inOut, audioFrame->data(), 480*2);
memcpy(audioFrame->mutable_data(), inOut+480, 480*2);
if(enableAEC)
apm->set_stream_delay_ms(delay);
apm->ProcessStream(audioFrame);
if(enableVAD){
hasVoice=hasVoice || apm->voice_detection()->stream_has_voice();
}
memcpy(inOut+480, audioFrame->data(), 480*2);
#endif
}
void EchoCanceller::SetAECStrength(int strength){
#ifndef TGVOIP_NO_DSP
/*if(aec){
#ifndef TGVOIP_USE_DESKTOP_DSP
AecmConfig cfg;
cfg.cngMode=AecmFalse;
cfg.echoMode=(int16_t) strength;
WebRtcAecm_set_config(aec, cfg);
#endif
}*/
#endif
}
void EchoCanceller::SetVoiceDetectionEnabled(bool enabled){
enableVAD=enabled;
#ifndef TGVOIP_NO_DSP
apm->voice_detection()->Enable(enabled);
#endif
}
using namespace tgvoip::effects;
AudioEffect::~AudioEffect(){
}
void AudioEffect::SetPassThrough(bool passThrough){
this->passThrough=passThrough;
}
Volume::Volume(){
}
Volume::~Volume(){
}
void Volume::Process(int16_t* inOut, size_t numSamples){
if(level==1.0f || passThrough){
return;
}
for(size_t i=0;i<numSamples;i++){
float sample=(float)inOut[i]*multiplier;
if(sample>32767.0f)
inOut[i]=INT16_MAX;
else if(sample<-32768.0f)
inOut[i]=INT16_MIN;
else
inOut[i]=(int16_t)sample;
}
}
void Volume::SetLevel(float level){
this->level=level;
float db;
if(level<1.0f)
db=-50.0f*(1.0f-level);
else if(level>1.0f && level<=2.0f)
db=10.0f*(level-1.0f);
else
db=0.0f;
multiplier=expf(db/20.0f * logf(10.0f));
}
float Volume::GetLevel(){
return level;
}

View File

@ -0,0 +1,83 @@
//
// 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 "Buffers.h"
#include "BlockingQueue.h"
#include "MediaStreamItf.h"
#include "utils.h"
namespace webrtc{
class AudioProcessing;
class AudioFrame;
}
namespace tgvoip{
class EchoCanceller{
public:
TGVOIP_DISALLOW_COPY_AND_ASSIGN(EchoCanceller);
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(int16_t* inOut, size_t numSamples, bool& hasVoice);
void SetAECStrength(int strength);
void SetVoiceDetectionEnabled(bool enabled);
private:
bool enableAEC;
bool enableAGC;
bool enableNS;
bool enableVAD=false;
bool isOn;
#ifndef TGVOIP_NO_DSP
webrtc::AudioProcessing* apm=NULL;
webrtc::AudioFrame* audioFrame=NULL;
void RunBufferFarendThread();
bool didBufferFarend;
Thread* bufferFarendThread;
BlockingQueue<Buffer>* farendQueue;
BufferPool<960*2, 10> farendBufferPool;
bool running;
#endif
};
namespace effects{
class AudioEffect{
public:
virtual ~AudioEffect()=0;
virtual void Process(int16_t* inOut, size_t numSamples)=0;
virtual void SetPassThrough(bool passThrough);
protected:
bool passThrough=false;
};
class Volume : public AudioEffect{
public:
Volume();
virtual ~Volume();
virtual void Process(int16_t* inOut, size_t numSamples);
/**
* Level is (0.0, 2.0]
*/
void SetLevel(float level);
float GetLevel();
private:
float level=1.0f;
float multiplier=1.0f;
};
}
}
#endif //LIBTGVOIP_ECHOCANCELLER_H

View File

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

View File

@ -0,0 +1,455 @@
//
// 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){
if(out)
out->SetCallback(JitterBuffer::CallbackOut, this);
this->step=step;
memset(slots, 0, sizeof(jitter_packet_t)*JITTER_SLOT_COUNT);
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", 2);
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);
#ifdef TGVOIP_DUMP_JITTER_STATS
#ifdef TGVOIP_JITTER_DUMP_FILE
dump=fopen(TGVOIP_JITTER_DUMP_FILE, "w");
#elif defined(__ANDROID__)
dump=fopen("/sdcard/tgvoip_jitter_dump.txt", "w");
#else
dump=fopen("tgvoip_jitter_dump.txt", "w");
#endif
tgvoip_log_file_write_header(dump);
fprintf(dump, "PTS\tRTS\tNumInBuf\tAJitter\tADelay\tTDelay\n");
#endif
Reset();
}
JitterBuffer::~JitterBuffer(){
Reset();
}
void JitterBuffer::SetMinPacketCount(uint32_t count){
LOGI("jitter: set min packet count %u", count);
minDelay=count;
minMinDelay=count;
//Reset();
}
int JitterBuffer::GetMinPacketCount(){
return (int)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, bool isEC){
MutexGuard m(mutex);
jitter_packet_t pkt;
pkt.size=len;
pkt.buffer=Buffer::Wrap(data, len, [](void*){}, [](void* a, size_t)->void*{return a;});
pkt.timestamp=timestamp;
pkt.isEC=isEC;
PutInternal(&pkt, !isEC);
//LOGV("in, ts=%d, ec=%d", timestamp, isEC);
}
void JitterBuffer::Reset(){
wasReset=true;
needBuffering=true;
lastPutTimestamp=0;
int i;
for(i=0;i<JITTER_SLOT_COUNT;i++){
if(!slots[i].buffer.IsEmpty()){
slots[i].buffer=Buffer();
}
}
delayHistory.Reset();
lateHistory.Reset();
adjustingDelay=false;
lostSinceReset=0;
gotSinceReset=0;
expectNextAtTime=0;
deviationHistory.Reset();
outstandingDelayChange=0;
dontChangeDelay=0;
}
size_t JitterBuffer::HandleOutput(unsigned char *buffer, size_t len, int offsetInSteps, bool advance, int& playbackScaledDuration, bool& isEC){
jitter_packet_t pkt;
pkt.buffer=Buffer::Wrap(buffer, len, [](void*){}, [](void* a, size_t)->void*{return a;});
pkt.size=len;
MutexGuard m(mutex);
if(first){
first=false;
unsigned int delay=GetCurrentDelay();
if(GetCurrentDelay()>5){
LOGW("jitter: delay too big upon start (%u), dropping packets", delay);
while(delay>GetMinPacketCount()){
for(int i=0;i<JITTER_SLOT_COUNT;i++){
if(slots[i].timestamp==nextTimestamp){
if(!slots[i].buffer.IsEmpty()){
slots[i].buffer=Buffer();
}
break;
}
}
Advance();
delay--;
}
}
}
int result=GetInternal(&pkt, offsetInSteps, advance);
if(outstandingDelayChange!=0){
if(outstandingDelayChange<0){
playbackScaledDuration=40;
outstandingDelayChange+=20;
}else{
playbackScaledDuration=80;
outstandingDelayChange-=20;
}
//LOGV("outstanding delay change: %d", outstandingDelayChange);
}else if(advance && GetCurrentDelay()==0){
//LOGV("stretching packet because the next one is late");
playbackScaledDuration=80;
}else{
playbackScaledDuration=60;
}
if(result==JR_OK){
isEC=pkt.isEC;
return pkt.size;
}else{
return 0;
}
}
int JitterBuffer::GetInternal(jitter_packet_t* pkt, int offset, bool advance){
/*if(needBuffering && lastPutTimestamp<nextTimestamp){
LOGV("jitter: don't have timestamp %lld, buffering", (long long int)nextTimestamp);
Advance();
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.IsEmpty() && 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;
pkt->buffer.CopyFrom(slots[i].buffer, slots[i].size);
pkt->isEC=slots[i].isEC;
}
}
slots[i].buffer=Buffer();
if(offset==0)
Advance();
lostCount=0;
needBuffering=false;
return JR_OK;
}
LOGV("jitter: found no packet for timestamp %lld (last put = %d, lost = %d)", (long long int)timestampToGet, lastPutTimestamp, lostCount);
if(advance)
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-=(int64_t)(minDelay-GetCurrentDelay());
lostCount=0;
Reset();
}
return JR_MISSING;
}
return JR_BUFFERING;
}
void JitterBuffer::PutInternal(jitter_packet_t* pkt, bool overwriteExisting){
if(pkt->size>JITTER_SLOT_SIZE){
LOGE("The packet is too big to fit into the jitter buffer");
return;
}
int i;
for(i=0;i<JITTER_SLOT_COUNT;i++){
if(!slots[i].buffer.IsEmpty() && slots[i].timestamp==pkt->timestamp){
//LOGV("Found existing packet for timestamp %u, overwrite %d", pkt->timestamp, overwriteExisting);
if(overwriteExisting){
slots[i].buffer.CopyFrom(pkt->buffer, pkt->size);
slots[i].size=pkt->size;
slots[i].isEC=pkt->isEC;
}
return;
}
}
gotSinceReset++;
if(wasReset){
wasReset=false;
outstandingDelayChange=0;
nextTimestamp=(int64_t)(((int64_t)pkt->timestamp)-step*minDelay);
first=true;
LOGI("jitter: resyncing, next timestamp = %lld (step=%d, minDelay=%f)", (long long int)nextTimestamp, step, double(minDelay));
}
for(i=0;i<JITTER_SLOT_COUNT;i++){
if(!slots[i].buffer.IsEmpty()){
if(slots[i].timestamp<nextTimestamp-1){
slots[i].buffer=Buffer();
}
}
}
/*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.Add(dev);
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.IsEmpty())
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.IsEmpty() && slots[i].timestamp<bestTimestamp){
toRemove=i;
bestTimestamp=slots[i].timestamp;
}
}
Advance();
slots[toRemove].buffer=Buffer();
i=toRemove;
}
slots[i].timestamp=pkt->timestamp;
slots[i].size=pkt->size;
slots[i].buffer=bufferPool.Get();
slots[i].recvTimeDiff=time-prevRecvTime;
slots[i].isEC=pkt->isEC;
slots[i].buffer.CopyFrom(pkt->buffer, pkt->size);
#ifdef TGVOIP_DUMP_JITTER_STATS
fprintf(dump, "%u\t%.03f\t%d\t%.03f\t%.03f\t%.03f\n", pkt->timestamp, time, GetCurrentDelay(), lastMeasuredJitter, lastMeasuredDelay, minDelay);
#endif
prevRecvTime=time;
}
void JitterBuffer::Advance(){
nextTimestamp+=step;
}
unsigned int JitterBuffer::GetCurrentDelay(){
unsigned int delay=0;
int i;
for(i=0;i<JITTER_SLOT_COUNT;i++){
if(!slots[i].buffer.IsEmpty())
delay++;
}
return delay;
}
void JitterBuffer::Tick(){
MutexGuard m(mutex);
int i;
lateHistory.Add(latePacketCount);
latePacketCount=0;
bool absolutelyNoLatePackets=lateHistory.Max()==0;
double avgLate16=lateHistory.Average(16);
//LOGV("jitter: avg late=%.1f, %.1f, %.1f", avgLate16, avgLate32, avgLate64);
if(avgLate16>=resyncThreshold){
LOGV("resyncing: avgLate16=%f, resyncThreshold=%f", avgLate16, resyncThreshold);
wasReset=true;
}
if(absolutelyNoLatePackets){
if(dontDecMinDelay>0)
dontDecMinDelay--;
}
delayHistory.Add(GetCurrentDelay());
avgDelay=delayHistory.Average(32);
double stddev=0;
double avgdev=deviationHistory.Average();
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=(int32_t)(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.store(minDelay + diff);
outstandingDelayChange+=diff*60;
dontChangeDelay+=32;
//LOGD("new delay from stddev %f", 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++;
}
void JitterBuffer::GetAverageLateCount(double *out){
double avgLate64=lateHistory.Average(), avgLate32=lateHistory.Average(32), avgLate16=lateHistory.Average(16);
out[0]=avgLate16;
out[1]=avgLate32;
out[2]=avgLate64;
}
int JitterBuffer::GetAndResetLostPacketCount(){
MutexGuard m(mutex);
int r=lostPackets;
lostPackets=0;
return r;
}
double JitterBuffer::GetLastMeasuredJitter(){
return lastMeasuredJitter;
}
double JitterBuffer::GetLastMeasuredDelay(){
return lastMeasuredDelay;
}
double JitterBuffer::GetAverageDelay(){
return avgDelay;
}

View File

@ -0,0 +1,98 @@
//
// 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 <atomic>
#include <stdio.h>
#include "MediaStreamItf.h"
#include "BlockingQueue.h"
#include "Buffers.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
namespace tgvoip{
class JitterBuffer{
public:
JitterBuffer(MediaStreamItf* out, uint32_t step);
~JitterBuffer();
void SetMinPacketCount(uint32_t count);
int GetMinPacketCount();
unsigned int GetCurrentDelay();
double GetAverageDelay();
void Reset();
void HandleInput(unsigned char* data, size_t len, uint32_t timestamp, bool isEC);
size_t HandleOutput(unsigned char* buffer, size_t len, int offsetInSteps, bool advance, int& playbackScaledDuration, bool& isEC);
void Tick();
void GetAverageLateCount(double* out);
int GetAndResetLostPacketCount();
double GetLastMeasuredJitter();
double GetLastMeasuredDelay();
private:
struct jitter_packet_t{
Buffer buffer=Buffer();
size_t size;
uint32_t timestamp;
bool isEC;
double recvTimeDiff;
};
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, bool overwriteExisting);
int GetInternal(jitter_packet_t* pkt, int offset, bool advance);
void Advance();
BufferPool<JITTER_SLOT_SIZE, JITTER_SLOT_COUNT> bufferPool;
Mutex mutex;
jitter_packet_t slots[JITTER_SLOT_COUNT];
int64_t nextTimestamp=0;
uint32_t step;
std::atomic<double> minDelay{6};
uint32_t minMinDelay;
uint32_t maxMinDelay;
uint32_t maxUsedSlots;
uint32_t lastPutTimestamp;
uint32_t lossesToReset;
double resyncThreshold;
unsigned int lostCount=0;
unsigned int lostSinceReset=0;
unsigned int gotSinceReset=0;
bool wasReset=true;
bool needBuffering=true;
HistoricBuffer<int, 64, double> delayHistory;
HistoricBuffer<int, 64, double> lateHistory;
bool adjustingDelay=false;
unsigned int tickCount=0;
unsigned int latePacketCount=0;
unsigned int dontIncMinDelay=0;
unsigned int dontDecMinDelay=0;
int lostPackets=0;
double prevRecvTime=0;
double expectNextAtTime=0;
HistoricBuffer<double, 64> deviationHistory;
double lastMeasuredJitter=0;
double lastMeasuredDelay=0;
int outstandingDelayChange=0;
unsigned int dontChangeDelay=0;
double avgDelay=0;
bool first=true;
#ifdef TGVOIP_DUMP_JITTER_STATS
FILE* dump;
#endif
};
}
#endif //LIBTGVOIP_JITTERBUFFER_H

View File

@ -0,0 +1,769 @@
AUTOMAKE_OPTIONS = foreign
CFLAGS = -Wall -DHAVE_CONFIG_H -Wno-unknown-pragmas
lib_LTLIBRARIES = libtgvoip.la
SRC = TgVoip.cpp \
VoIPController.cpp \
Buffers.cpp \
CongestionControl.cpp \
EchoCanceller.cpp \
JitterBuffer.cpp \
logging.cpp \
MediaStreamItf.cpp \
MessageThread.cpp \
NetworkSocket.cpp \
OpusDecoder.cpp \
OpusEncoder.cpp \
PacketReassembler.cpp \
VoIPGroupController.cpp \
VoIPServerConfig.cpp \
audio/AudioIO.cpp \
audio/AudioInput.cpp \
audio/AudioOutput.cpp \
audio/Resampler.cpp \
os/posix/NetworkSocketPosix.cpp \
video/VideoSource.cpp \
video/VideoRenderer.cpp \
video/VideoPacketSender.cpp \
video/VideoFEC.cpp \
video/ScreamCongestionController.cpp \
json11.cpp
TGVOIP_HDRS = \
TgVoip.h \
VoIPController.h \
Buffers.h \
BlockingQueue.h \
PrivateDefines.h \
CongestionControl.h \
EchoCanceller.h \
JitterBuffer.h \
logging.h \
threading.h \
MediaStreamItf.h \
MessageThread.h \
NetworkSocket.h \
OpusDecoder.h \
OpusEncoder.h \
PacketReassembler.h \
VoIPServerConfig.h \
audio/AudioIO.h \
audio/AudioInput.h \
audio/AudioOutput.h \
audio/Resampler.h \
os/posix/NetworkSocketPosix.h \
video/VideoSource.h \
video/VideoPacketSender.h \
video/VideoFEC.h \
video/VideoRenderer.h \
video/ScreamCongestionController.h \
json11.hpp \
utils.h
if TARGET_OS_OSX
SRC += \
os/darwin/AudioInputAudioUnit.cpp \
os/darwin/AudioOutputAudioUnit.cpp \
os/darwin/AudioUnitIO.cpp \
os/darwin/AudioInputAudioUnitOSX.cpp \
os/darwin/AudioOutputAudioUnitOSX.cpp \
os/darwin/DarwinSpecific.mm \
os/darwin/SampleBufferDisplayLayerRenderer.mm \
os/darwin/TGVVideoRenderer.mm \
os/darwin/TGVVideoSource.mm \
os/darwin/VideoToolboxEncoderSource.mm
TGVOIP_HDRS += \
os/darwin/AudioInputAudioUnit.h \
os/darwin/AudioOutputAudioUnit.h \
os/darwin/AudioUnitIO.h \
os/darwin/AudioInputAudioUnitOSX.h \
os/darwin/AudioOutputAudioUnitOSX.h \
os/darwin/DarwinSpecific.h \
os/darwin/SampleBufferDisplayLayerRenderer.h \
os/darwin/TGVVideoRenderer.h \
os/darwin/TGVVideoSource.h \
os/darwin/VideoToolboxEncoderSource.h
LDFLAGS += -framework Foundation -framework CoreFoundation -framework CoreAudio -framework AudioToolbox -framework VideoToolbox -framework CoreMedia -framework CoreVideo
else
# Linux-specific
if WITH_ALSA
SRC += \
os/linux/AudioInputALSA.cpp \
os/linux/AudioOutputALSA.cpp
TGVOIP_HDRS += \
os/linux/AudioInputALSA.h \
os/linux/AudioOutputALSA.h
endif
if WITH_PULSE
SRC += \
os/linux/AudioOutputPulse.cpp \
os/linux/AudioInputPulse.cpp \
os/linux/AudioPulse.cpp
TGVOIP_HDRS += \
os/linux/AudioOutputPulse.h \
os/linux/AudioInputPulse.h \
os/linux/AudioPulse.h \
os/linux/PulseFunctions.h
endif
endif
if ENABLE_DSP
CFLAGS += -DWEBRTC_POSIX -DWEBRTC_APM_DEBUG_DUMP=0 -DWEBRTC_NS_FLOAT -I$(top_srcdir)/webrtc_dsp
CCASFLAGS += -I$(top_srcdir)/webrtc_dsp
SRC += \
./webrtc_dsp/system_wrappers/source/field_trial.cc \
./webrtc_dsp/system_wrappers/source/metrics.cc \
./webrtc_dsp/system_wrappers/source/cpu_features.cc \
./webrtc_dsp/absl/strings/internal/memutil.cc \
./webrtc_dsp/absl/strings/string_view.cc \
./webrtc_dsp/absl/strings/ascii.cc \
./webrtc_dsp/absl/types/bad_optional_access.cc \
./webrtc_dsp/absl/types/optional.cc \
./webrtc_dsp/absl/base/internal/raw_logging.cc \
./webrtc_dsp/absl/base/internal/throw_delegate.cc \
./webrtc_dsp/rtc_base/race_checker.cc \
./webrtc_dsp/rtc_base/strings/string_builder.cc \
./webrtc_dsp/rtc_base/memory/aligned_malloc.cc \
./webrtc_dsp/rtc_base/timeutils.cc \
./webrtc_dsp/rtc_base/platform_file.cc \
./webrtc_dsp/rtc_base/string_to_number.cc \
./webrtc_dsp/rtc_base/thread_checker_impl.cc \
./webrtc_dsp/rtc_base/stringencode.cc \
./webrtc_dsp/rtc_base/stringutils.cc \
./webrtc_dsp/rtc_base/checks.cc \
./webrtc_dsp/rtc_base/platform_thread.cc \
./webrtc_dsp/rtc_base/logging_webrtc.cc \
./webrtc_dsp/rtc_base/criticalsection.cc \
./webrtc_dsp/rtc_base/platform_thread_types.cc \
./webrtc_dsp/rtc_base/event.cc \
./webrtc_dsp/rtc_base/event_tracer.cc \
./webrtc_dsp/third_party/rnnoise/src/rnn_vad_weights.cc \
./webrtc_dsp/third_party/rnnoise/src/kiss_fft.cc \
./webrtc_dsp/api/audio/audio_frame.cc \
./webrtc_dsp/api/audio/echo_canceller3_config.cc \
./webrtc_dsp/api/audio/echo_canceller3_factory.cc \
./webrtc_dsp/modules/third_party/fft/fft.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_estimator.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines_logist.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/filterbanks.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/transform.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_filter.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/filter_functions.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/decode.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lattice.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/intialize.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_tables.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/encode.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_analysis.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines_hist.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/entropy_coding.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/isac_vad.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/crc.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/decode_bwe.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.c \
./webrtc_dsp/modules/audio_coding/codecs/isac/main/source/isac.c \
./webrtc_dsp/modules/audio_processing/rms_level.cc \
./webrtc_dsp/modules/audio_processing/echo_detector/normalized_covariance_estimator.cc \
./webrtc_dsp/modules/audio_processing/echo_detector/moving_max.cc \
./webrtc_dsp/modules/audio_processing/echo_detector/circular_buffer.cc \
./webrtc_dsp/modules/audio_processing/echo_detector/mean_variance_estimator.cc \
./webrtc_dsp/modules/audio_processing/splitting_filter.cc \
./webrtc_dsp/modules/audio_processing/gain_control_impl.cc \
./webrtc_dsp/modules/audio_processing/ns/nsx_core.c \
./webrtc_dsp/modules/audio_processing/ns/noise_suppression_x.c \
./webrtc_dsp/modules/audio_processing/ns/nsx_core_c.c \
./webrtc_dsp/modules/audio_processing/ns/ns_core.c \
./webrtc_dsp/modules/audio_processing/ns/noise_suppression.c \
./webrtc_dsp/modules/audio_processing/audio_buffer.cc \
./webrtc_dsp/modules/audio_processing/typing_detection.cc \
./webrtc_dsp/modules/audio_processing/include/audio_processing_statistics.cc \
./webrtc_dsp/modules/audio_processing/include/audio_generator_factory.cc \
./webrtc_dsp/modules/audio_processing/include/aec_dump.cc \
./webrtc_dsp/modules/audio_processing/include/audio_processing.cc \
./webrtc_dsp/modules/audio_processing/include/config.cc \
./webrtc_dsp/modules/audio_processing/agc2/interpolated_gain_curve.cc \
./webrtc_dsp/modules/audio_processing/agc2/agc2_common.cc \
./webrtc_dsp/modules/audio_processing/agc2/gain_applier.cc \
./webrtc_dsp/modules/audio_processing/agc2/adaptive_agc.cc \
./webrtc_dsp/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc \
./webrtc_dsp/modules/audio_processing/agc2/limiter.cc \
./webrtc_dsp/modules/audio_processing/agc2/saturation_protector.cc \
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.cc \
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/rnn.cc \
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.cc \
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/spectral_features.cc \
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_search.cc \
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/features_extraction.cc \
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/fft_util.cc \
./webrtc_dsp/modules/audio_processing/agc2/rnn_vad/lp_residual.cc \
./webrtc_dsp/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.cc \
./webrtc_dsp/modules/audio_processing/agc2/vector_float_frame.cc \
./webrtc_dsp/modules/audio_processing/agc2/noise_level_estimator.cc \
./webrtc_dsp/modules/audio_processing/agc2/agc2_testing_common.cc \
./webrtc_dsp/modules/audio_processing/agc2/fixed_digital_level_estimator.cc \
./webrtc_dsp/modules/audio_processing/agc2/fixed_gain_controller.cc \
./webrtc_dsp/modules/audio_processing/agc2/vad_with_level.cc \
./webrtc_dsp/modules/audio_processing/agc2/limiter_db_gain_curve.cc \
./webrtc_dsp/modules/audio_processing/agc2/down_sampler.cc \
./webrtc_dsp/modules/audio_processing/agc2/signal_classifier.cc \
./webrtc_dsp/modules/audio_processing/agc2/noise_spectrum_estimator.cc \
./webrtc_dsp/modules/audio_processing/agc2/compute_interpolated_gain_curve.cc \
./webrtc_dsp/modules/audio_processing/agc2/biquad_filter.cc \
./webrtc_dsp/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc \
./webrtc_dsp/modules/audio_processing/transient/moving_moments.cc \
./webrtc_dsp/modules/audio_processing/transient/wpd_tree.cc \
./webrtc_dsp/modules/audio_processing/transient/wpd_node.cc \
./webrtc_dsp/modules/audio_processing/transient/transient_suppressor.cc \
./webrtc_dsp/modules/audio_processing/transient/transient_detector.cc \
./webrtc_dsp/modules/audio_processing/low_cut_filter.cc \
./webrtc_dsp/modules/audio_processing/level_estimator_impl.cc \
./webrtc_dsp/modules/audio_processing/three_band_filter_bank.cc \
./webrtc_dsp/modules/audio_processing/aec/echo_cancellation.cc \
./webrtc_dsp/modules/audio_processing/aec/aec_resampler.cc \
./webrtc_dsp/modules/audio_processing/aec/aec_core.cc \
./webrtc_dsp/modules/audio_processing/voice_detection_impl.cc \
./webrtc_dsp/modules/audio_processing/echo_cancellation_impl.cc \
./webrtc_dsp/modules/audio_processing/gain_control_for_experimental_agc.cc \
./webrtc_dsp/modules/audio_processing/agc/agc.cc \
./webrtc_dsp/modules/audio_processing/agc/loudness_histogram.cc \
./webrtc_dsp/modules/audio_processing/agc/agc_manager_direct.cc \
./webrtc_dsp/modules/audio_processing/agc/legacy/analog_agc.c \
./webrtc_dsp/modules/audio_processing/agc/legacy/digital_agc.c \
./webrtc_dsp/modules/audio_processing/agc/utility.cc \
./webrtc_dsp/modules/audio_processing/audio_processing_impl.cc \
./webrtc_dsp/modules/audio_processing/audio_generator/file_audio_generator.cc \
./webrtc_dsp/modules/audio_processing/gain_controller2.cc \
./webrtc_dsp/modules/audio_processing/residual_echo_detector.cc \
./webrtc_dsp/modules/audio_processing/noise_suppression_impl.cc \
./webrtc_dsp/modules/audio_processing/aecm/aecm_core.cc \
./webrtc_dsp/modules/audio_processing/aecm/aecm_core_c.cc \
./webrtc_dsp/modules/audio_processing/aecm/echo_control_mobile.cc \
./webrtc_dsp/modules/audio_processing/aec3/render_reverb_model.cc \
./webrtc_dsp/modules/audio_processing/aec3/reverb_model_fallback.cc \
./webrtc_dsp/modules/audio_processing/aec3/echo_remover_metrics.cc \
./webrtc_dsp/modules/audio_processing/aec3/matched_filter_lag_aggregator.cc \
./webrtc_dsp/modules/audio_processing/aec3/render_delay_buffer2.cc \
./webrtc_dsp/modules/audio_processing/aec3/echo_path_variability.cc \
./webrtc_dsp/modules/audio_processing/aec3/frame_blocker.cc \
./webrtc_dsp/modules/audio_processing/aec3/subtractor.cc \
./webrtc_dsp/modules/audio_processing/aec3/aec3_fft.cc \
./webrtc_dsp/modules/audio_processing/aec3/fullband_erle_estimator.cc \
./webrtc_dsp/modules/audio_processing/aec3/suppression_filter.cc \
./webrtc_dsp/modules/audio_processing/aec3/block_processor.cc \
./webrtc_dsp/modules/audio_processing/aec3/subband_erle_estimator.cc \
./webrtc_dsp/modules/audio_processing/aec3/render_delay_controller_metrics.cc \
./webrtc_dsp/modules/audio_processing/aec3/render_delay_buffer.cc \
./webrtc_dsp/modules/audio_processing/aec3/vector_buffer.cc \
./webrtc_dsp/modules/audio_processing/aec3/erl_estimator.cc \
./webrtc_dsp/modules/audio_processing/aec3/aec_state.cc \
./webrtc_dsp/modules/audio_processing/aec3/adaptive_fir_filter.cc \
./webrtc_dsp/modules/audio_processing/aec3/render_delay_controller.cc \
./webrtc_dsp/modules/audio_processing/aec3/skew_estimator.cc \
./webrtc_dsp/modules/audio_processing/aec3/echo_path_delay_estimator.cc \
./webrtc_dsp/modules/audio_processing/aec3/block_framer.cc \
./webrtc_dsp/modules/audio_processing/aec3/erle_estimator.cc \
./webrtc_dsp/modules/audio_processing/aec3/reverb_model.cc \
./webrtc_dsp/modules/audio_processing/aec3/cascaded_biquad_filter.cc \
./webrtc_dsp/modules/audio_processing/aec3/render_buffer.cc \
./webrtc_dsp/modules/audio_processing/aec3/subtractor_output.cc \
./webrtc_dsp/modules/audio_processing/aec3/stationarity_estimator.cc \
./webrtc_dsp/modules/audio_processing/aec3/render_signal_analyzer.cc \
./webrtc_dsp/modules/audio_processing/aec3/subtractor_output_analyzer.cc \
./webrtc_dsp/modules/audio_processing/aec3/suppression_gain.cc \
./webrtc_dsp/modules/audio_processing/aec3/echo_audibility.cc \
./webrtc_dsp/modules/audio_processing/aec3/block_processor_metrics.cc \
./webrtc_dsp/modules/audio_processing/aec3/moving_average.cc \
./webrtc_dsp/modules/audio_processing/aec3/reverb_model_estimator.cc \
./webrtc_dsp/modules/audio_processing/aec3/aec3_common.cc \
./webrtc_dsp/modules/audio_processing/aec3/residual_echo_estimator.cc \
./webrtc_dsp/modules/audio_processing/aec3/matched_filter.cc \
./webrtc_dsp/modules/audio_processing/aec3/reverb_decay_estimator.cc \
./webrtc_dsp/modules/audio_processing/aec3/render_delay_controller2.cc \
./webrtc_dsp/modules/audio_processing/aec3/suppression_gain_limiter.cc \
./webrtc_dsp/modules/audio_processing/aec3/main_filter_update_gain.cc \
./webrtc_dsp/modules/audio_processing/aec3/echo_remover.cc \
./webrtc_dsp/modules/audio_processing/aec3/downsampled_render_buffer.cc \
./webrtc_dsp/modules/audio_processing/aec3/matrix_buffer.cc \
./webrtc_dsp/modules/audio_processing/aec3/block_processor2.cc \
./webrtc_dsp/modules/audio_processing/aec3/echo_canceller3.cc \
./webrtc_dsp/modules/audio_processing/aec3/block_delay_buffer.cc \
./webrtc_dsp/modules/audio_processing/aec3/fft_buffer.cc \
./webrtc_dsp/modules/audio_processing/aec3/comfort_noise_generator.cc \
./webrtc_dsp/modules/audio_processing/aec3/shadow_filter_update_gain.cc \
./webrtc_dsp/modules/audio_processing/aec3/filter_analyzer.cc \
./webrtc_dsp/modules/audio_processing/aec3/reverb_frequency_response.cc \
./webrtc_dsp/modules/audio_processing/aec3/decimator.cc \
./webrtc_dsp/modules/audio_processing/echo_control_mobile_impl.cc \
./webrtc_dsp/modules/audio_processing/logging/apm_data_dumper.cc \
./webrtc_dsp/modules/audio_processing/vad/voice_activity_detector.cc \
./webrtc_dsp/modules/audio_processing/vad/standalone_vad.cc \
./webrtc_dsp/modules/audio_processing/vad/pitch_internal.cc \
./webrtc_dsp/modules/audio_processing/vad/vad_circular_buffer.cc \
./webrtc_dsp/modules/audio_processing/vad/vad_audio_proc.cc \
./webrtc_dsp/modules/audio_processing/vad/pole_zero_filter.cc \
./webrtc_dsp/modules/audio_processing/vad/pitch_based_vad.cc \
./webrtc_dsp/modules/audio_processing/vad/gmm.cc \
./webrtc_dsp/modules/audio_processing/utility/ooura_fft.cc \
./webrtc_dsp/modules/audio_processing/utility/delay_estimator_wrapper.cc \
./webrtc_dsp/modules/audio_processing/utility/delay_estimator.cc \
./webrtc_dsp/modules/audio_processing/utility/block_mean_calculator.cc \
./webrtc_dsp/common_audio/window_generator.cc \
./webrtc_dsp/common_audio/channel_buffer.cc \
./webrtc_dsp/common_audio/fir_filter_factory.cc \
./webrtc_dsp/common_audio/wav_header.cc \
./webrtc_dsp/common_audio/real_fourier_ooura.cc \
./webrtc_dsp/common_audio/audio_util.cc \
./webrtc_dsp/common_audio/fir_filter_sse.cc \
./webrtc_dsp/common_audio/resampler/push_sinc_resampler.cc \
./webrtc_dsp/common_audio/resampler/resampler.cc \
./webrtc_dsp/common_audio/resampler/sinc_resampler_sse.cc \
./webrtc_dsp/common_audio/resampler/push_resampler.cc \
./webrtc_dsp/common_audio/resampler/sinc_resampler.cc \
./webrtc_dsp/common_audio/resampler/sinusoidal_linear_chirp_source.cc \
./webrtc_dsp/common_audio/wav_file.cc \
./webrtc_dsp/common_audio/third_party/fft4g/fft4g.c \
./webrtc_dsp/common_audio/audio_converter.cc \
./webrtc_dsp/common_audio/real_fourier.cc \
./webrtc_dsp/common_audio/sparse_fir_filter.cc \
./webrtc_dsp/common_audio/smoothing_filter.cc \
./webrtc_dsp/common_audio/fir_filter_c.cc \
./webrtc_dsp/common_audio/ring_buffer.c \
./webrtc_dsp/common_audio/signal_processing/complex_fft.c \
./webrtc_dsp/common_audio/signal_processing/filter_ma_fast_q12.c \
./webrtc_dsp/common_audio/signal_processing/splitting_filter1.c \
./webrtc_dsp/common_audio/signal_processing/levinson_durbin.c \
./webrtc_dsp/common_audio/signal_processing/dot_product_with_scale.cc \
./webrtc_dsp/common_audio/signal_processing/auto_corr_to_refl_coef.c \
./webrtc_dsp/common_audio/signal_processing/resample_by_2_internal.c \
./webrtc_dsp/common_audio/signal_processing/energy.c \
./webrtc_dsp/common_audio/signal_processing/sqrt_of_one_minus_x_squared.c \
./webrtc_dsp/common_audio/signal_processing/downsample_fast.c \
./webrtc_dsp/common_audio/signal_processing/filter_ar_fast_q12.c \
./webrtc_dsp/common_audio/signal_processing/spl_init.c \
./webrtc_dsp/common_audio/signal_processing/lpc_to_refl_coef.c \
./webrtc_dsp/common_audio/signal_processing/cross_correlation.c \
./webrtc_dsp/common_audio/signal_processing/division_operations.c \
./webrtc_dsp/common_audio/signal_processing/auto_correlation.c \
./webrtc_dsp/common_audio/signal_processing/get_scaling_square.c \
./webrtc_dsp/common_audio/signal_processing/resample.c \
./webrtc_dsp/common_audio/signal_processing/min_max_operations.c \
./webrtc_dsp/common_audio/signal_processing/refl_coef_to_lpc.c \
./webrtc_dsp/common_audio/signal_processing/filter_ar.c \
./webrtc_dsp/common_audio/signal_processing/vector_scaling_operations.c \
./webrtc_dsp/common_audio/signal_processing/resample_fractional.c \
./webrtc_dsp/common_audio/signal_processing/real_fft.c \
./webrtc_dsp/common_audio/signal_processing/ilbc_specific_functions.c \
./webrtc_dsp/common_audio/signal_processing/randomization_functions.c \
./webrtc_dsp/common_audio/signal_processing/copy_set_operations.c \
./webrtc_dsp/common_audio/signal_processing/resample_by_2.c \
./webrtc_dsp/common_audio/signal_processing/get_hanning_window.c \
./webrtc_dsp/common_audio/signal_processing/resample_48khz.c \
./webrtc_dsp/common_audio/signal_processing/spl_inl.c \
./webrtc_dsp/common_audio/signal_processing/spl_sqrt.c \
./webrtc_dsp/common_audio/vad/vad_sp.c \
./webrtc_dsp/common_audio/vad/vad.cc \
./webrtc_dsp/common_audio/vad/webrtc_vad.c \
./webrtc_dsp/common_audio/vad/vad_filterbank.c \
./webrtc_dsp/common_audio/vad/vad_core.c \
./webrtc_dsp/common_audio/vad/vad_gmm.c
if TARGET_OS_OSX
CFLAGS += -DWEBRTC_MAC
SRC += \
webrtc_dsp/rtc_base/logging_mac.mm \
webrtc_dsp/rtc_base/logging_mac.h
else
CFLAGS += -DWEBRTC_LINUX
endif
if TARGET_CPU_X86
SRC += \
webrtc_dsp/modules/audio_processing/aec/aec_core_sse2.cc \
webrtc_dsp/modules/audio_processing/utility/ooura_fft_sse2.cc
endif
if ENABLE_AUDIO_CALLBACK
CFLAGS += -DTGVOIP_USE_CALLBACK_AUDIO_IO
SRC += \
audio/AudioIOCallback.cpp
TGVOIP_HDRS += \
audio/AudioIOCallback.h
endif
if TARGET_CPU_ARM
SRC += \
webrtc_dsp/common_audio/signal_processing/complex_bit_reverse_arm.S \
webrtc_dsp/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor_arm.S
if TARGET_CPU_ARMV7
CFLAGS += -mfpu=neon -mfloat-abi=hard
CCASFLAGS += -mfpu=neon -mfloat-abi=hard
SRC += \
webrtc_dsp/common_audio/signal_processing/cross_correlation_neon.c \
webrtc_dsp/common_audio/signal_processing/downsample_fast_neon.c \
webrtc_dsp/common_audio/signal_processing/min_max_operations_neon.c \
webrtc_dsp/modules/audio_processing/aec/aec_core_neon.cc \
webrtc_dsp/modules/audio_processing/aecm/aecm_core_neon.cc \
webrtc_dsp/modules/audio_processing/ns/nsx_core_neon.c \
webrtc_dsp/modules/audio_processing/utility/ooura_fft_neon.cc
# webrtc_dsp/common_audio/signal_processing/filter_ar_fast_q12_armv7.S
endif
else
SRC += \
webrtc_dsp/common_audio/signal_processing/complex_bit_reverse.c \
webrtc_dsp/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.c
endif
# headers
SRC += \
webrtc_dsp/system_wrappers/include/field_trial.h \
webrtc_dsp/system_wrappers/include/cpu_features_wrapper.h \
webrtc_dsp/system_wrappers/include/asm_defines.h \
webrtc_dsp/system_wrappers/include/metrics.h \
webrtc_dsp/system_wrappers/include/compile_assert_c.h \
webrtc_dsp/typedefs.h \
webrtc_dsp/absl/strings/internal/memutil.h \
webrtc_dsp/absl/strings/ascii.h \
webrtc_dsp/absl/strings/string_view.h \
webrtc_dsp/absl/types/optional.h \
webrtc_dsp/absl/types/bad_optional_access.h \
webrtc_dsp/absl/memory/memory.h \
webrtc_dsp/absl/meta/type_traits.h \
webrtc_dsp/absl/algorithm/algorithm.h \
webrtc_dsp/absl/container/inlined_vector.h \
webrtc_dsp/absl/base/policy_checks.h \
webrtc_dsp/absl/base/port.h \
webrtc_dsp/absl/base/config.h \
webrtc_dsp/absl/base/internal/invoke.h \
webrtc_dsp/absl/base/internal/inline_variable.h \
webrtc_dsp/absl/base/internal/atomic_hook.h \
webrtc_dsp/absl/base/internal/identity.h \
webrtc_dsp/absl/base/internal/raw_logging.h \
webrtc_dsp/absl/base/internal/throw_delegate.h \
webrtc_dsp/absl/base/attributes.h \
webrtc_dsp/absl/base/macros.h \
webrtc_dsp/absl/base/optimization.h \
webrtc_dsp/absl/base/log_severity.h \
webrtc_dsp/absl/utility/utility.h \
webrtc_dsp/rtc_base/string_to_number.h \
webrtc_dsp/rtc_base/constructormagic.h \
webrtc_dsp/rtc_base/strings/string_builder.h \
webrtc_dsp/rtc_base/event_tracer.h \
webrtc_dsp/rtc_base/stringencode.h \
webrtc_dsp/rtc_base/memory/aligned_malloc.h \
webrtc_dsp/rtc_base/event.h \
webrtc_dsp/rtc_base/ignore_wundef.h \
webrtc_dsp/rtc_base/stringutils.h \
webrtc_dsp/rtc_base/arraysize.h \
webrtc_dsp/rtc_base/swap_queue.h \
webrtc_dsp/rtc_base/trace_event.h \
webrtc_dsp/rtc_base/checks.h \
webrtc_dsp/rtc_base/deprecation.h \
webrtc_dsp/rtc_base/sanitizer.h \
webrtc_dsp/rtc_base/scoped_ref_ptr.h \
webrtc_dsp/rtc_base/logging.h \
webrtc_dsp/rtc_base/timeutils.h \
webrtc_dsp/rtc_base/atomicops.h \
webrtc_dsp/rtc_base/numerics/safe_minmax.h \
webrtc_dsp/rtc_base/numerics/safe_conversions.h \
webrtc_dsp/rtc_base/numerics/safe_conversions_impl.h \
webrtc_dsp/rtc_base/numerics/safe_compare.h \
webrtc_dsp/rtc_base/system/unused.h \
webrtc_dsp/rtc_base/system/inline.h \
webrtc_dsp/rtc_base/system/ignore_warnings.h \
webrtc_dsp/rtc_base/system/asm_defines.h \
webrtc_dsp/rtc_base/system/rtc_export.h \
webrtc_dsp/rtc_base/system/arch.h \
webrtc_dsp/rtc_base/platform_thread.h \
webrtc_dsp/rtc_base/platform_thread_types.h \
webrtc_dsp/rtc_base/protobuf_utils.h \
webrtc_dsp/rtc_base/thread_annotations.h \
webrtc_dsp/rtc_base/gtest_prod_util.h \
webrtc_dsp/rtc_base/function_view.h \
webrtc_dsp/rtc_base/criticalsection.h \
webrtc_dsp/rtc_base/refcount.h \
webrtc_dsp/rtc_base/thread_checker_impl.h \
webrtc_dsp/rtc_base/compile_assert_c.h \
webrtc_dsp/rtc_base/type_traits.h \
webrtc_dsp/rtc_base/platform_file.h \
webrtc_dsp/rtc_base/refcounter.h \
webrtc_dsp/rtc_base/thread_checker.h \
webrtc_dsp/rtc_base/race_checker.h \
webrtc_dsp/rtc_base/refcountedobject.h \
webrtc_dsp/third_party/rnnoise/src/rnn_activations.h \
webrtc_dsp/third_party/rnnoise/src/kiss_fft.h \
webrtc_dsp/third_party/rnnoise/src/rnn_vad_weights.h \
webrtc_dsp/api/audio/echo_canceller3_config.h \
webrtc_dsp/api/audio/echo_control.h \
webrtc_dsp/api/audio/audio_frame.h \
webrtc_dsp/api/audio/echo_canceller3_factory.h \
webrtc_dsp/api/array_view.h \
webrtc_dsp/modules/third_party/fft/fft.h \
webrtc_dsp/modules/audio_coding/codecs/isac/bandwidth_info.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/include/isac.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/os_specific_inline.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/entropy_coding.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/isac_vad.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/settings.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb12_tables.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/arith_routines.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/crc.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/isac_float_type.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_lag_tables.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/spectrum_ar_model_tables.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/codec.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_gain_tables.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_shape_swb16_tables.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_estimator.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/structs.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/filter_functions.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/encode_lpc_swb.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/pitch_filter.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_analysis.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/bandwidth_estimator.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_gain_swb_tables.h \
webrtc_dsp/modules/audio_coding/codecs/isac/main/source/lpc_tables.h \
webrtc_dsp/modules/audio_processing/echo_detector/moving_max.h \
webrtc_dsp/modules/audio_processing/echo_detector/circular_buffer.h \
webrtc_dsp/modules/audio_processing/echo_detector/normalized_covariance_estimator.h \
webrtc_dsp/modules/audio_processing/echo_detector/mean_variance_estimator.h \
webrtc_dsp/modules/audio_processing/gain_control_for_experimental_agc.h \
webrtc_dsp/modules/audio_processing/rms_level.h \
webrtc_dsp/modules/audio_processing/ns/ns_core.h \
webrtc_dsp/modules/audio_processing/ns/defines.h \
webrtc_dsp/modules/audio_processing/ns/noise_suppression.h \
webrtc_dsp/modules/audio_processing/ns/nsx_core.h \
webrtc_dsp/modules/audio_processing/ns/windows_private.h \
webrtc_dsp/modules/audio_processing/ns/noise_suppression_x.h \
webrtc_dsp/modules/audio_processing/ns/nsx_defines.h \
webrtc_dsp/modules/audio_processing/residual_echo_detector.h \
webrtc_dsp/modules/audio_processing/audio_processing_impl.h \
webrtc_dsp/modules/audio_processing/render_queue_item_verifier.h \
webrtc_dsp/modules/audio_processing/include/audio_generator.h \
webrtc_dsp/modules/audio_processing/include/config.h \
webrtc_dsp/modules/audio_processing/include/audio_frame_view.h \
webrtc_dsp/modules/audio_processing/include/mock_audio_processing.h \
webrtc_dsp/modules/audio_processing/include/gain_control.h \
webrtc_dsp/modules/audio_processing/include/audio_generator_factory.h \
webrtc_dsp/modules/audio_processing/include/aec_dump.h \
webrtc_dsp/modules/audio_processing/include/audio_processing_statistics.h \
webrtc_dsp/modules/audio_processing/include/audio_processing.h \
webrtc_dsp/modules/audio_processing/agc2/interpolated_gain_curve.h \
webrtc_dsp/modules/audio_processing/agc2/biquad_filter.h \
webrtc_dsp/modules/audio_processing/agc2/agc2_testing_common.h \
webrtc_dsp/modules/audio_processing/agc2/adaptive_mode_level_estimator.h \
webrtc_dsp/modules/audio_processing/agc2/signal_classifier.h \
webrtc_dsp/modules/audio_processing/agc2/vector_float_frame.h \
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/sequence_buffer.h \
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/rnn.h \
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/test_utils.h \
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_info.h \
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/lp_residual.h \
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/ring_buffer.h \
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/symmetric_matrix_buffer.h \
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/spectral_features.h \
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/features_extraction.h \
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/common.h \
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/spectral_features_internal.h \
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/fft_util.h \
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_search_internal.h \
webrtc_dsp/modules/audio_processing/agc2/rnn_vad/pitch_search.h \
webrtc_dsp/modules/audio_processing/agc2/fixed_gain_controller.h \
webrtc_dsp/modules/audio_processing/agc2/down_sampler.h \
webrtc_dsp/modules/audio_processing/agc2/saturation_protector.h \
webrtc_dsp/modules/audio_processing/agc2/agc2_common.h \
webrtc_dsp/modules/audio_processing/agc2/adaptive_mode_level_estimator_agc.h \
webrtc_dsp/modules/audio_processing/agc2/adaptive_digital_gain_applier.h \
webrtc_dsp/modules/audio_processing/agc2/vad_with_level.h \
webrtc_dsp/modules/audio_processing/agc2/limiter_db_gain_curve.h \
webrtc_dsp/modules/audio_processing/agc2/fixed_digital_level_estimator.h \
webrtc_dsp/modules/audio_processing/agc2/adaptive_agc.h \
webrtc_dsp/modules/audio_processing/agc2/gain_applier.h \
webrtc_dsp/modules/audio_processing/agc2/noise_level_estimator.h \
webrtc_dsp/modules/audio_processing/agc2/compute_interpolated_gain_curve.h \
webrtc_dsp/modules/audio_processing/agc2/noise_spectrum_estimator.h \
webrtc_dsp/modules/audio_processing/agc2/limiter.h \
webrtc_dsp/modules/audio_processing/transient/transient_detector.h \
webrtc_dsp/modules/audio_processing/transient/transient_suppressor.h \
webrtc_dsp/modules/audio_processing/transient/daubechies_8_wavelet_coeffs.h \
webrtc_dsp/modules/audio_processing/transient/common.h \
webrtc_dsp/modules/audio_processing/transient/wpd_node.h \
webrtc_dsp/modules/audio_processing/transient/moving_moments.h \
webrtc_dsp/modules/audio_processing/transient/wpd_tree.h \
webrtc_dsp/modules/audio_processing/transient/dyadic_decimator.h \
webrtc_dsp/modules/audio_processing/noise_suppression_impl.h \
webrtc_dsp/modules/audio_processing/aec/aec_resampler.h \
webrtc_dsp/modules/audio_processing/aec/echo_cancellation.h \
webrtc_dsp/modules/audio_processing/aec/aec_core.h \
webrtc_dsp/modules/audio_processing/aec/aec_core_optimized_methods.h \
webrtc_dsp/modules/audio_processing/aec/aec_common.h \
webrtc_dsp/modules/audio_processing/voice_detection_impl.h \
webrtc_dsp/modules/audio_processing/agc/legacy/analog_agc.h \
webrtc_dsp/modules/audio_processing/agc/legacy/gain_control.h \
webrtc_dsp/modules/audio_processing/agc/legacy/digital_agc.h \
webrtc_dsp/modules/audio_processing/agc/mock_agc.h \
webrtc_dsp/modules/audio_processing/agc/loudness_histogram.h \
webrtc_dsp/modules/audio_processing/agc/gain_map_internal.h \
webrtc_dsp/modules/audio_processing/agc/utility.h \
webrtc_dsp/modules/audio_processing/agc/agc_manager_direct.h \
webrtc_dsp/modules/audio_processing/agc/agc.h \
webrtc_dsp/modules/audio_processing/common.h \
webrtc_dsp/modules/audio_processing/audio_buffer.h \
webrtc_dsp/modules/audio_processing/echo_control_mobile_impl.h \
webrtc_dsp/modules/audio_processing/splitting_filter.h \
webrtc_dsp/modules/audio_processing/low_cut_filter.h \
webrtc_dsp/modules/audio_processing/audio_generator/file_audio_generator.h \
webrtc_dsp/modules/audio_processing/three_band_filter_bank.h \
webrtc_dsp/modules/audio_processing/echo_cancellation_impl.h \
webrtc_dsp/modules/audio_processing/level_estimator_impl.h \
webrtc_dsp/modules/audio_processing/gain_controller2.h \
webrtc_dsp/modules/audio_processing/aecm/aecm_core.h \
webrtc_dsp/modules/audio_processing/aecm/aecm_defines.h \
webrtc_dsp/modules/audio_processing/aecm/echo_control_mobile.h \
webrtc_dsp/modules/audio_processing/aec3/downsampled_render_buffer.h \
webrtc_dsp/modules/audio_processing/aec3/subtractor_output_analyzer.h \
webrtc_dsp/modules/audio_processing/aec3/residual_echo_estimator.h \
webrtc_dsp/modules/audio_processing/aec3/shadow_filter_update_gain.h \
webrtc_dsp/modules/audio_processing/aec3/aec_state.h \
webrtc_dsp/modules/audio_processing/aec3/suppression_filter.h \
webrtc_dsp/modules/audio_processing/aec3/block_delay_buffer.h \
webrtc_dsp/modules/audio_processing/aec3/adaptive_fir_filter.h \
webrtc_dsp/modules/audio_processing/aec3/cascaded_biquad_filter.h \
webrtc_dsp/modules/audio_processing/aec3/matched_filter.h \
webrtc_dsp/modules/audio_processing/aec3/subtractor_output.h \
webrtc_dsp/modules/audio_processing/aec3/render_signal_analyzer.h \
webrtc_dsp/modules/audio_processing/aec3/aec3_fft.h \
webrtc_dsp/modules/audio_processing/aec3/echo_remover_metrics.h \
webrtc_dsp/modules/audio_processing/aec3/filter_analyzer.h \
webrtc_dsp/modules/audio_processing/aec3/subtractor.h \
webrtc_dsp/modules/audio_processing/aec3/echo_path_delay_estimator.h \
webrtc_dsp/modules/audio_processing/aec3/block_processor_metrics.h \
webrtc_dsp/modules/audio_processing/aec3/fft_data.h \
webrtc_dsp/modules/audio_processing/aec3/render_delay_controller_metrics.h \
webrtc_dsp/modules/audio_processing/aec3/comfort_noise_generator.h \
webrtc_dsp/modules/audio_processing/aec3/erl_estimator.h \
webrtc_dsp/modules/audio_processing/aec3/echo_remover.h \
webrtc_dsp/modules/audio_processing/aec3/matrix_buffer.h \
webrtc_dsp/modules/audio_processing/aec3/reverb_model_estimator.h \
webrtc_dsp/modules/audio_processing/aec3/echo_path_variability.h \
webrtc_dsp/modules/audio_processing/aec3/moving_average.h \
webrtc_dsp/modules/audio_processing/aec3/render_reverb_model.h \
webrtc_dsp/modules/audio_processing/aec3/render_delay_controller.h \
webrtc_dsp/modules/audio_processing/aec3/suppression_gain.h \
webrtc_dsp/modules/audio_processing/aec3/erle_estimator.h \
webrtc_dsp/modules/audio_processing/aec3/subband_erle_estimator.h \
webrtc_dsp/modules/audio_processing/aec3/block_processor.h \
webrtc_dsp/modules/audio_processing/aec3/fullband_erle_estimator.h \
webrtc_dsp/modules/audio_processing/aec3/stationarity_estimator.h \
webrtc_dsp/modules/audio_processing/aec3/echo_canceller3.h \
webrtc_dsp/modules/audio_processing/aec3/skew_estimator.h \
webrtc_dsp/modules/audio_processing/aec3/render_buffer.h \
webrtc_dsp/modules/audio_processing/aec3/reverb_model_fallback.h \
webrtc_dsp/modules/audio_processing/aec3/vector_buffer.h \
webrtc_dsp/modules/audio_processing/aec3/reverb_frequency_response.h \
webrtc_dsp/modules/audio_processing/aec3/echo_audibility.h \
webrtc_dsp/modules/audio_processing/aec3/fft_buffer.h \
webrtc_dsp/modules/audio_processing/aec3/aec3_common.h \
webrtc_dsp/modules/audio_processing/aec3/vector_math.h \
webrtc_dsp/modules/audio_processing/aec3/decimator.h \
webrtc_dsp/modules/audio_processing/aec3/frame_blocker.h \
webrtc_dsp/modules/audio_processing/aec3/block_framer.h \
webrtc_dsp/modules/audio_processing/aec3/suppression_gain_limiter.h \
webrtc_dsp/modules/audio_processing/aec3/delay_estimate.h \
webrtc_dsp/modules/audio_processing/aec3/reverb_model.h \
webrtc_dsp/modules/audio_processing/aec3/main_filter_update_gain.h \
webrtc_dsp/modules/audio_processing/aec3/matched_filter_lag_aggregator.h \
webrtc_dsp/modules/audio_processing/aec3/reverb_decay_estimator.h \
webrtc_dsp/modules/audio_processing/aec3/render_delay_buffer.h \
webrtc_dsp/modules/audio_processing/gain_control_impl.h \
webrtc_dsp/modules/audio_processing/typing_detection.h \
webrtc_dsp/modules/audio_processing/logging/apm_data_dumper.h \
webrtc_dsp/modules/audio_processing/vad/vad_audio_proc_internal.h \
webrtc_dsp/modules/audio_processing/vad/vad_circular_buffer.h \
webrtc_dsp/modules/audio_processing/vad/pitch_based_vad.h \
webrtc_dsp/modules/audio_processing/vad/pole_zero_filter.h \
webrtc_dsp/modules/audio_processing/vad/gmm.h \
webrtc_dsp/modules/audio_processing/vad/common.h \
webrtc_dsp/modules/audio_processing/vad/vad_audio_proc.h \
webrtc_dsp/modules/audio_processing/vad/voice_gmm_tables.h \
webrtc_dsp/modules/audio_processing/vad/noise_gmm_tables.h \
webrtc_dsp/modules/audio_processing/vad/pitch_internal.h \
webrtc_dsp/modules/audio_processing/vad/standalone_vad.h \
webrtc_dsp/modules/audio_processing/vad/voice_activity_detector.h \
webrtc_dsp/modules/audio_processing/utility/ooura_fft_tables_neon_sse2.h \
webrtc_dsp/modules/audio_processing/utility/delay_estimator_internal.h \
webrtc_dsp/modules/audio_processing/utility/ooura_fft.h \
webrtc_dsp/modules/audio_processing/utility/block_mean_calculator.h \
webrtc_dsp/modules/audio_processing/utility/delay_estimator.h \
webrtc_dsp/modules/audio_processing/utility/ooura_fft_tables_common.h \
webrtc_dsp/modules/audio_processing/utility/delay_estimator_wrapper.h \
webrtc_dsp/common_audio/mocks/mock_smoothing_filter.h \
webrtc_dsp/common_audio/wav_file.h \
webrtc_dsp/common_audio/sparse_fir_filter.h \
webrtc_dsp/common_audio/fir_filter_sse.h \
webrtc_dsp/common_audio/window_generator.h \
webrtc_dsp/common_audio/ring_buffer.h \
webrtc_dsp/common_audio/fir_filter.h \
webrtc_dsp/common_audio/include/audio_util.h \
webrtc_dsp/common_audio/real_fourier_ooura.h \
webrtc_dsp/common_audio/smoothing_filter.h \
webrtc_dsp/common_audio/resampler/sinc_resampler.h \
webrtc_dsp/common_audio/resampler/include/push_resampler.h \
webrtc_dsp/common_audio/resampler/include/resampler.h \
webrtc_dsp/common_audio/resampler/push_sinc_resampler.h \
webrtc_dsp/common_audio/resampler/sinusoidal_linear_chirp_source.h \
webrtc_dsp/common_audio/fir_filter_factory.h \
webrtc_dsp/common_audio/audio_converter.h \
webrtc_dsp/common_audio/third_party/spl_sqrt_floor/spl_sqrt_floor.h \
webrtc_dsp/common_audio/third_party/fft4g/fft4g.h \
webrtc_dsp/common_audio/channel_buffer.h \
webrtc_dsp/common_audio/real_fourier.h \
webrtc_dsp/common_audio/fir_filter_neon.h \
webrtc_dsp/common_audio/fir_filter_c.h \
webrtc_dsp/common_audio/signal_processing/complex_fft_tables.h \
webrtc_dsp/common_audio/signal_processing/include/signal_processing_library.h \
webrtc_dsp/common_audio/signal_processing/include/real_fft.h \
webrtc_dsp/common_audio/signal_processing/include/spl_inl.h \
webrtc_dsp/common_audio/signal_processing/include/spl_inl_armv7.h \
webrtc_dsp/common_audio/signal_processing/dot_product_with_scale.h \
webrtc_dsp/common_audio/signal_processing/resample_by_2_internal.h \
webrtc_dsp/common_audio/wav_header.h \
webrtc_dsp/common_audio/vad/vad_core.h \
webrtc_dsp/common_audio/vad/include/vad.h \
webrtc_dsp/common_audio/vad/include/webrtc_vad.h \
webrtc_dsp/common_audio/vad/vad_gmm.h \
webrtc_dsp/common_audio/vad/vad_sp.h \
webrtc_dsp/common_audio/vad/vad_filterbank.h
else
CFLAGS += -DTGVOIP_NO_DSP
endif
libtgvoip_la_LDFLAGS = -version-number @LIBTGVOIP_MAJOR_VERSION@:@LIBTGVOIP_MINOR_VERSION@:@LIBTGVOIP_PATCH_VERSION@
libtgvoip_la_SOURCES = $(SRC) $(TGVOIP_HDRS)
tgvoipincludedir = $(includedir)/tgvoip
nobase_tgvoipinclude_HEADERS = $(TGVOIP_HDRS)
pkgconfig_DATA = tgvoip.pc
CXXFLAGS += -std=gnu++0x $(CFLAGS)
if TARGET_OS_OSX
OBJCFLAGS = $(CFLAGS)
OBJCXXFLAGS += -std=gnu++0x $(CFLAGS)
endif

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -0,0 +1,182 @@
//
// Created by Grishka on 17.06.2018.
//
#include <assert.h>
#include <time.h>
#include <math.h>
#include <float.h>
#include <stdint.h>
#ifndef _WIN32
#include <sys/time.h>
#endif
#include "MessageThread.h"
#include "VoIPController.h"
#include "logging.h"
using namespace tgvoip;
MessageThread::MessageThread() : Thread(std::bind(&MessageThread::Run, this)){
running=true;
SetName("MessageThread");
#ifdef _WIN32
#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY!=WINAPI_FAMILY_PHONE_APP
event=CreateEvent(NULL, false, false, NULL);
#else
event=CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS);
#endif
#else
pthread_cond_init(&cond, NULL);
#endif
}
MessageThread::~MessageThread(){
Stop();
#ifdef _WIN32
CloseHandle(event);
#else
pthread_cond_destroy(&cond);
#endif
}
void MessageThread::Stop(){
if(running){
running=false;
#ifdef _WIN32
SetEvent(event);
#else
pthread_cond_signal(&cond);
#endif
Join();
}
}
void MessageThread::Run(){
queueMutex.Lock();
while(running){
double currentTime=VoIPController::GetCurrentTime();
double waitTimeout;
{
MutexGuard _m(queueAccessMutex);
waitTimeout=queue.empty() ? DBL_MAX : (queue[0].deliverAt-currentTime);
}
//LOGW("MessageThread wait timeout %f", waitTimeout);
if(waitTimeout>0.0){
#ifdef _WIN32
queueMutex.Unlock();
DWORD actualWaitTimeout=waitTimeout==DBL_MAX ? INFINITE : ((DWORD)round(waitTimeout*1000.0));
#if !defined(WINAPI_FAMILY) || WINAPI_FAMILY!=WINAPI_FAMILY_PHONE_APP
WaitForSingleObject(event, actualWaitTimeout);
#else
WaitForSingleObjectEx(event, actualWaitTimeout, false);
#endif
// we don't really care if a context switch happens here and anything gets added to the queue by another thread
// since any new no-delay messages will get delivered on this iteration anyway
queueMutex.Lock();
#else
if(waitTimeout!=DBL_MAX){
struct timeval now;
struct timespec timeout;
gettimeofday(&now, NULL);
waitTimeout+=now.tv_sec;
waitTimeout+=(now.tv_usec/1000000.0);
timeout.tv_sec=(time_t)(floor(waitTimeout));
timeout.tv_nsec=(long)((waitTimeout-floor(waitTimeout))*1000000000.0);
pthread_cond_timedwait(&cond, queueMutex.NativeHandle(), &timeout);
}else{
pthread_cond_wait(&cond, queueMutex.NativeHandle());
}
#endif
}
if(!running){
queueMutex.Unlock();
return;
}
currentTime=VoIPController::GetCurrentTime();
std::vector<Message> msgsToDeliverNow;
{
MutexGuard _m(queueAccessMutex);
for(std::vector<Message>::iterator m=queue.begin();m!=queue.end();){
if(m->deliverAt==0.0 || currentTime>=m->deliverAt){
msgsToDeliverNow.push_back(*m);
m=queue.erase(m);
continue;
}
++m;
}
}
for(Message& m:msgsToDeliverNow){
//LOGI("MessageThread delivering %u", m.msg);
cancelCurrent=false;
if(m.deliverAt==0.0)
m.deliverAt=VoIPController::GetCurrentTime();
if(m.func!=nullptr){
m.func();
}
if(!cancelCurrent && m.interval>0.0){
m.deliverAt+=m.interval;
InsertMessageInternal(m);
}
}
}
queueMutex.Unlock();
}
uint32_t MessageThread::Post(std::function<void()> func, double delay, double interval){
assert(delay>=0);
//LOGI("MessageThread post [function] delay %f", delay);
double currentTime=VoIPController::GetCurrentTime();
Message m{lastMessageID++, delay==0.0 ? 0.0 : (currentTime+delay), interval, func};
InsertMessageInternal(m);
if(!IsCurrent()){
#ifdef _WIN32
SetEvent(event);
#else
pthread_cond_signal(&cond);
#endif
}
return m.id;
}
void MessageThread::InsertMessageInternal(MessageThread::Message &m){
MutexGuard _m(queueAccessMutex);
if(queue.empty()){
queue.push_back(m);
}else{
if(queue[0].deliverAt>m.deliverAt){
queue.insert(queue.begin(), m);
}else{
std::vector<Message>::iterator insertAfter=queue.begin();
for(; insertAfter!=queue.end(); ++insertAfter){
std::vector<Message>::iterator next=std::next(insertAfter);
if(next==queue.end() || (next->deliverAt>m.deliverAt && insertAfter->deliverAt<=m.deliverAt)){
queue.insert(next, m);
break;
}
}
}
}
}
void MessageThread::Cancel(uint32_t id){
MutexGuard _m(queueAccessMutex);
for(std::vector<Message>::iterator m=queue.begin();m!=queue.end();){
if(m->id==id){
m=queue.erase(m);
}else{
++m;
}
}
}
void MessageThread::CancelSelf(){
assert(IsCurrent());
cancelCurrent=true;
}

View File

@ -0,0 +1,54 @@
//
// Created by Grishka on 17.06.2018.
//
#ifndef LIBTGVOIP_MESSAGETHREAD_H
#define LIBTGVOIP_MESSAGETHREAD_H
#include "threading.h"
#include "utils.h"
#include <vector>
#include <functional>
#include <atomic>
namespace tgvoip{
class MessageThread : public Thread{
public:
TGVOIP_DISALLOW_COPY_AND_ASSIGN(MessageThread);
MessageThread();
virtual ~MessageThread();
uint32_t Post(std::function<void()> func, double delay=0, double interval=0);
void Cancel(uint32_t id);
void CancelSelf();
void Stop();
enum{
INVALID_ID=0
};
private:
struct Message{
uint32_t id;
double deliverAt;
double interval;
std::function<void()> func;
};
void Run();
void InsertMessageInternal(Message& m);
std::atomic<bool> running;
std::vector<Message> queue;
Mutex queueMutex;
Mutex queueAccessMutex;
uint32_t lastMessageID=1;
bool cancelCurrent=false;
#ifdef _WIN32
HANDLE event;
#else
pthread_cond_t cond;
#endif
};
}
#endif //LIBTGVOIP_MESSAGETHREAD_H

View File

@ -0,0 +1,650 @@
//
// Created by Grishka on 29.03.17.
//
#include <stdexcept>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#if defined(_WIN32)
#include <winsock2.h>
#include "os/windows/NetworkSocketWinsock.h"
#else
#include "os/posix/NetworkSocketPosix.h"
#endif
#include "logging.h"
#include "VoIPServerConfig.h"
#include "VoIPController.h"
#include "Buffers.h"
#include "NetworkSocket.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;
}
NetworkSocket::~NetworkSocket(){
}
std::string NetworkSocket::GetLocalInterfaceInfo(NetworkAddress *inet4addr, NetworkAddress *inet6addr){
std::string r="not implemented";
return r;
}
uint16_t NetworkSocket::GenerateLocalPort(){
uint16_t rnd;
VoIPController::crypto.rand_bytes(reinterpret_cast<uint8_t*>(&rnd), 2);
return (uint16_t) ((rnd%(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
}
NetworkAddress NetworkSocket::ResolveDomainName(std::string name){
#ifndef _WIN32
return NetworkSocketPosix::ResolveDomainName(name);
#else
return NetworkSocketWinsock::ResolveDomainName(name);
#endif
}
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=Receive(len);
if(pkt.IsEmpty())
return 0;
size_t actualLen=std::min(len, pkt.data.Length());
memcpy(buffer, *pkt.data, actualLen);
return actualLen;
}
bool NetworkAddress::operator==(const NetworkAddress &other) const{
if(isIPv6!=other.isIPv6)
return false;
if(!isIPv6){
return addr.ipv4==other.addr.ipv4;
}
return memcmp(addr.ipv6, other.addr.ipv6, 16)==0;
}
bool NetworkAddress::operator!=(const NetworkAddress &other) const{
return !(*this == other);
}
std::string NetworkAddress::ToString() const{
if(isIPv6){
#ifndef _WIN32
return NetworkSocketPosix::V6AddressToString(addr.ipv6);
#else
return NetworkSocketWinsock::V6AddressToString(addr.ipv6);
#endif
}else{
#ifndef _WIN32
return NetworkSocketPosix::V4AddressToString(addr.ipv4);
#else
return NetworkSocketWinsock::V4AddressToString(addr.ipv4);
#endif
}
}
bool NetworkAddress::IsEmpty() const{
if(isIPv6){
const uint64_t* a=reinterpret_cast<const uint64_t*>(addr.ipv6);
return a[0]==0LL && a[1]==0LL;
}
return addr.ipv4==0;
}
bool NetworkAddress::PrefixMatches(const unsigned int prefix, const NetworkAddress &other) const{
if(isIPv6!=other.isIPv6)
return false;
if(!isIPv6){
uint32_t mask=0xFFFFFFFF << (32-prefix);
return (addr.ipv4 & mask) == (other.addr.ipv4 & mask);
}
return false;
}
NetworkAddress NetworkAddress::Empty(){
NetworkAddress addr;
addr.isIPv6=false;
addr.addr.ipv4=0;
return addr;
}
NetworkAddress NetworkAddress::IPv4(std::string str){
NetworkAddress addr;
addr.isIPv6=false;
#ifndef _WIN32
addr.addr.ipv4=NetworkSocketPosix::StringToV4Address(str);
#else
addr.addr.ipv4=NetworkSocketWinsock::StringToV4Address(str);
#endif
return addr;
}
NetworkAddress NetworkAddress::IPv4(uint32_t addr){
NetworkAddress a;
a.isIPv6=false;
a.addr.ipv4=addr;
return a;
}
NetworkAddress NetworkAddress::IPv6(std::string str){
NetworkAddress addr;
addr.isIPv6=false;
#ifndef _WIN32
NetworkSocketPosix::StringToV6Address(str, addr.addr.ipv6);
#else
NetworkSocketWinsock::StringToV6Address(str, addr.addr.ipv6);
#endif
return addr;
}
NetworkAddress NetworkAddress::IPv6(const uint8_t addr[16]){
NetworkAddress a;
a.isIPv6=true;
memcpy(a.addr.ipv6, addr, 16);
return a;
}
bool NetworkSocket::Select(std::vector<NetworkSocket *> &readFds, std::vector<NetworkSocket*> &writeFds, std::vector<NetworkSocket *> &errorFds, SocketSelectCanceller *canceller){
#ifndef _WIN32
return NetworkSocketPosix::Select(readFds, writeFds, errorFds, canceller);
#else
return NetworkSocketWinsock::Select(readFds, writeFds, errorFds, canceller);
#endif
}
SocketSelectCanceller::~SocketSelectCanceller(){
}
SocketSelectCanceller *SocketSelectCanceller::Create(){
#ifndef _WIN32
return new SocketSelectCancellerPosix();
#else
return new SocketSelectCancellerWin32();
#endif
}
NetworkSocketTCPObfuscated::NetworkSocketTCPObfuscated(NetworkSocket *wrapped) : NetworkSocketWrapper(NetworkProtocol::TCP){
this->wrapped=wrapped;
}
NetworkSocketTCPObfuscated::~NetworkSocketTCPObfuscated(){
if(wrapped)
delete wrapped;
}
NetworkSocket *NetworkSocketTCPObfuscated::GetWrapped(){
return wrapped;
}
void NetworkSocketTCPObfuscated::InitConnection(){
Buffer buf(64);
GenerateTCPO2States(*buf, &recvState, &sendState);
wrapped->Send(NetworkPacket{
std::move(buf),
NetworkAddress::Empty(),
0,
NetworkProtocol::TCP
});
}
void NetworkSocketTCPObfuscated::Send(NetworkPacket packet){
BufferOutputStream os(packet.data.Length()+4);
size_t len=packet.data.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);
EncryptForTCPO2(os.GetBuffer(), os.GetLength(), &sendState);
wrapped->Send(NetworkPacket{
Buffer(std::move(os)),
NetworkAddress::Empty(),
0,
NetworkProtocol::TCP
});
//LOGD("Sent %u bytes", os.GetLength());
}
bool NetworkSocketTCPObfuscated::OnReadyToSend(){
LOGV("TCPO socket ready to send");
if(!initialized){
LOGV("Initializing TCPO2 connection");
initialized=true;
InitConnection();
readyToSend=true;
return false;
}
return wrapped->OnReadyToSend();
}
NetworkPacket NetworkSocketTCPObfuscated::Receive(size_t maxLen){
unsigned char len1;
size_t packetLen=0;
size_t offset=0;
size_t len;
len=wrapped->Receive(&len1, 1);
if(len<=0){
return NetworkPacket::Empty();
}
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){
return NetworkPacket::Empty();
}
EncryptForTCPO2(len2, 3, &recvState);
packetLen=((size_t)len2[0] | ((size_t)len2[1] << 8) | ((size_t)len2[2] << 16))*4;
}
if(packetLen>1500){
LOGW("packet too big to fit into buffer (%u vs %u)", (unsigned int)packetLen, (unsigned int)1500);
return NetworkPacket::Empty();
}
Buffer buf(packetLen);
while(offset<packetLen){
len=wrapped->Receive(*buf, packetLen-offset);
if(len<=0){
return NetworkPacket::Empty();
}
offset+=len;
}
EncryptForTCPO2(*buf, packetLen, &recvState);
return NetworkPacket{
std::move(buf),
wrapped->GetConnectedAddress(),
wrapped->GetConnectedPort(),
NetworkProtocol::TCP
};
}
void NetworkSocketTCPObfuscated::Open(){
}
void NetworkSocketTCPObfuscated::Close(){
wrapped->Close();
}
void NetworkSocketTCPObfuscated::Connect(const NetworkAddress address, uint16_t port){
wrapped->Connect(address, port);
}
bool NetworkSocketTCPObfuscated::IsFailed(){
return wrapped->IsFailed();
}
NetworkSocketSOCKS5Proxy::NetworkSocketSOCKS5Proxy(NetworkSocket *tcp, NetworkSocket *udp, std::string username, std::string password) : NetworkSocketWrapper(udp ? NetworkProtocol::UDP : NetworkProtocol::TCP){
this->tcp=tcp;
this->udp=udp;
this->username=username;
this->password=password;
}
NetworkSocketSOCKS5Proxy::~NetworkSocketSOCKS5Proxy(){
delete tcp;
}
void NetworkSocketSOCKS5Proxy::Send(NetworkPacket packet){
if(protocol==NetworkProtocol::TCP){
tcp->Send(std::move(packet));
}else if(protocol==NetworkProtocol::UDP){
BufferOutputStream out(1500);
out.WriteInt16(0); // RSV
out.WriteByte(0); // FRAG
if(!packet.address.isIPv6){
out.WriteByte(1); // ATYP (IPv4)
out.WriteInt32(packet.address.addr.ipv4);
}else{
out.WriteByte(4); // ATYP (IPv6)
out.WriteBytes(packet.address.addr.ipv6, 16);
}
out.WriteInt16(htons(packet.port));
out.WriteBytes(packet.data);
udp->Send(NetworkPacket{
Buffer(std::move(out)),
connectedAddress,
connectedPort,
NetworkProtocol::UDP
});
}
}
NetworkPacket NetworkSocketSOCKS5Proxy::Receive(size_t maxLen){
if(protocol==NetworkProtocol::TCP){
NetworkPacket packet=tcp->Receive();
packet.address=connectedAddress;
packet.port=connectedPort;
return packet;
}else{
NetworkPacket p=udp->Receive();
if(!p.IsEmpty() && p.address==connectedAddress && p.port==connectedPort){
BufferInputStream in(p.data);
in.ReadInt16(); // RSV
in.ReadByte(); // FRAG
unsigned char atyp=in.ReadByte();
NetworkAddress address=NetworkAddress::Empty();
if(atyp==1){ // IPv4
address=NetworkAddress::IPv4((uint32_t) in.ReadInt32());
}else if(atyp==4){ // IPv6
unsigned char addr[16];
in.ReadBytes(addr, 16);
address=NetworkAddress::IPv6(addr);
}
return NetworkPacket{
Buffer::CopyOf(p.data, in.GetOffset(), in.Remaining()),
address,
htons(in.ReadInt16()),
protocol
};
}
}
return NetworkPacket::Empty();
}
void NetworkSocketSOCKS5Proxy::Open(){
}
void NetworkSocketSOCKS5Proxy::Close(){
tcp->Close();
}
void NetworkSocketSOCKS5Proxy::Connect(const NetworkAddress address, uint16_t port){
connectedAddress=address;
connectedPort=port;
}
NetworkSocket *NetworkSocketSOCKS5Proxy::GetWrapped(){
return protocol==NetworkProtocol::TCP ? tcp : udp;
}
void NetworkSocketSOCKS5Proxy::InitConnection(){
}
bool NetworkSocketSOCKS5Proxy::IsFailed(){
return NetworkSocket::IsFailed() || tcp->IsFailed();
}
NetworkAddress NetworkSocketSOCKS5Proxy::GetConnectedAddress(){
return connectedAddress;
}
uint16_t NetworkSocketSOCKS5Proxy::GetConnectedPort(){
return connectedPort;
}
bool NetworkSocketSOCKS5Proxy::OnReadyToSend(){
//LOGV("on ready to send, state=%d", state);
if(state==ConnectionState::Initial){
BufferOutputStream p(16);
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(NetworkPacket{
Buffer(std::move(p)),
NetworkAddress::Empty(),
0,
NetworkProtocol::TCP
});
state=ConnectionState::WaitingForAuthMethod;
return false;
}
return udp ? udp->OnReadyToSend() : tcp->OnReadyToSend();
}
bool NetworkSocketSOCKS5Proxy::OnReadyToReceive(){
//LOGV("on ready to receive state=%d", state);
unsigned char buf[1024];
if(state==ConnectionState::WaitingForAuthMethod){
size_t l=tcp->Receive(buf, sizeof(buf));
if(l<2 || tcp->IsFailed()){
failed=true;
return false;
}
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 false;
}
if(chosenMethod==0){
// connected, no further auth needed
SendConnectionCommand();
}else if(chosenMethod==2 && !username.empty()){
BufferOutputStream p(512);
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(NetworkPacket{
Buffer(std::move(p)),
NetworkAddress::Empty(),
0,
NetworkProtocol::TCP
});
state=ConnectionState::WaitingForAuthResult;
}else{
LOGW("socks5: unsupported auth method");
failed=true;
return false;
}
return false;
}else if(state==ConnectionState::WaitingForAuthResult){
size_t l=tcp->Receive(buf, sizeof(buf));
if(l<2 || tcp->IsFailed()){
failed=true;
return false;
}
BufferInputStream in(buf, l);
uint8_t 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 false;
}
if(status!=0){
LOGW("socks5: username/password auth failed");
failed=true;
return false;
}
LOGV("socks5: authentication succeeded");
SendConnectionCommand();
return false;
}else if(state==ConnectionState::WaitingForCommandResult){
size_t l=tcp->Receive(buf, sizeof(buf));
if(protocol==NetworkProtocol::TCP){
if(l<2 || tcp->IsFailed()){
LOGW("socks5: connect failed")
failed=true;
return false;
}
BufferInputStream in(buf, l);
unsigned char ver=in.ReadByte();
if(ver!=5){
LOGW("socks5: connect: wrong ver in response");
failed=true;
return false;
}
unsigned char rep=in.ReadByte();
if(rep!=0){
LOGW("socks5: connect: failed with error %02X", rep);
failed=true;
return false;
}
LOGV("socks5: connect succeeded");
state=ConnectionState::Connected;
tcp=new NetworkSocketTCPObfuscated(tcp);
readyToSend=true;
return tcp->OnReadyToSend();
}else if(protocol==NetworkProtocol::UDP){
if(l<2 || tcp->IsFailed()){
LOGW("socks5: udp associate failed");
failed=true;
return false;
}
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 false;
}
if(rep!=0){
LOGW("socks5: udp associate failed with error %02X", rep);
failed=true;
return false;
}
in.ReadByte(); // RSV
unsigned char atyp=in.ReadByte();
if(atyp==1){
uint32_t addr=(uint32_t) in.ReadInt32();
connectedAddress=NetworkAddress::IPv4(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.IsEmpty()){
LOGW("socks5: failed to resolve domain name '%s'", domain);
failed=true;
return false;
}
}else if(atyp==4){
unsigned char addr[16];
in.ReadBytes(addr, 16);
connectedAddress=NetworkAddress::IPv6(addr);
}else{
LOGW("socks5: unknown address type %d", atyp);
failed=true;
return false;
}
connectedPort=(uint16_t)ntohs(in.ReadInt16());
state=ConnectionState::Connected;
readyToSend=true;
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;
}
}
}
return udp ? udp->OnReadyToReceive() : tcp->OnReadyToReceive();
}
void NetworkSocketSOCKS5Proxy::SendConnectionCommand(){
BufferOutputStream out(1024);
if(protocol==NetworkProtocol::TCP){
out.WriteByte(5); // VER
out.WriteByte(1); // CMD (CONNECT)
out.WriteByte(0); // RSV
if(!connectedAddress.isIPv6){
out.WriteByte(1); // ATYP (IPv4)
out.WriteInt32(connectedAddress.addr.ipv4);
}else{
out.WriteByte(4); // ATYP (IPv6)
out.WriteBytes((unsigned char*)connectedAddress.addr.ipv6, 16);
}
out.WriteInt16(htons(connectedPort)); // DST.PORT
}else if(protocol==NetworkProtocol::UDP){
LOGV("Sending udp associate");
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(NetworkPacket{
Buffer(std::move(out)),
NetworkAddress::Empty(),
0,
NetworkProtocol::TCP
});
state=ConnectionState::WaitingForCommandResult;
}
bool NetworkSocketSOCKS5Proxy::NeedSelectForSending(){
return state==ConnectionState::Initial || state==ConnectionState::Connected;
}

View File

@ -0,0 +1,201 @@
//
// Created by Grishka on 29.03.17.
//
#ifndef LIBTGVOIP_NETWORKSOCKET_H
#define LIBTGVOIP_NETWORKSOCKET_H
#include <stdint.h>
#include <string>
#include <vector>
#include <memory>
#include <atomic>
#include "utils.h"
#include "Buffers.h"
namespace tgvoip {
enum class NetworkProtocol{
UDP=0,
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() const;
bool operator==(const NetworkAddress& other) const;
bool operator!=(const NetworkAddress& other) const;
virtual ~NetworkAddress()=default;
virtual bool IsEmpty() const;
virtual bool PrefixMatches(const unsigned int prefix, const NetworkAddress& other) const;
static NetworkAddress Empty();
static NetworkAddress IPv4(std::string str);
static NetworkAddress IPv4(uint32_t addr);
static NetworkAddress IPv6(std::string str);
static NetworkAddress IPv6(const uint8_t addr[16]);
bool isIPv6=false;
union{
uint32_t ipv4;
uint8_t ipv6[16];
} addr;
private:
NetworkAddress(){};
};
struct NetworkPacket{
TGVOIP_MOVE_ONLY(NetworkPacket);
Buffer data;
NetworkAddress address;
uint16_t port;
NetworkProtocol protocol;
static NetworkPacket Empty(){
return NetworkPacket{Buffer(), NetworkAddress::Empty(), 0, NetworkProtocol::UDP};
}
bool IsEmpty(){
return data.IsEmpty() || (protocol==NetworkProtocol::UDP && (port==0 || address.IsEmpty()));
}
};
class SocketSelectCanceller{
public:
virtual ~SocketSelectCanceller();
virtual void CancelSelect()=0;
static SocketSelectCanceller* Create();
};
class NetworkSocket{
public:
friend class NetworkSocketPosix;
friend class NetworkSocketWinsock;
TGVOIP_DISALLOW_COPY_AND_ASSIGN(NetworkSocket);
NetworkSocket(NetworkProtocol protocol);
virtual ~NetworkSocket();
virtual void Send(NetworkPacket packet)=0;
virtual NetworkPacket Receive(size_t maxLen=0)=0;
size_t Receive(unsigned char* buffer, size_t len);
virtual void Open()=0;
virtual void Close()=0;
virtual uint16_t GetLocalPort(){ return 0; };
virtual void Connect(const NetworkAddress address, uint16_t port)=0;
virtual std::string GetLocalInterfaceInfo(NetworkAddress* inet4addr, NetworkAddress* inet6addr);
virtual void OnActiveInterfaceChanged(){};
virtual NetworkAddress GetConnectedAddress(){ return NetworkAddress::Empty(); };
virtual uint16_t GetConnectedPort(){ return 0; };
virtual void SetTimeouts(int sendTimeout, int recvTimeout){};
virtual bool IsFailed();
virtual bool IsReadyToSend(){
return readyToSend;
}
virtual bool OnReadyToSend(){ readyToSend=true; return true; };
virtual bool OnReadyToReceive(){ return true; };
void SetTimeout(double timeout){
this->timeout=timeout;
};
static NetworkSocket* Create(NetworkProtocol protocol);
static NetworkAddress ResolveDomainName(std::string name);
static bool Select(std::vector<NetworkSocket*>& readFds, std::vector<NetworkSocket*>& writeFds, 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];
std::atomic<bool> failed;
bool readyToSend=false;
double lastSuccessfulOperationTime=0.0;
double timeout=0.0;
NetworkProtocol protocol;
};
class NetworkSocketWrapper : public NetworkSocket{
public:
NetworkSocketWrapper(NetworkProtocol protocol) : NetworkSocket(protocol){};
virtual ~NetworkSocketWrapper(){};
virtual NetworkSocket* GetWrapped()=0;
virtual void InitConnection()=0;
virtual void SetNonBlocking(bool){};
};
class NetworkSocketTCPObfuscated : public NetworkSocketWrapper{
public:
NetworkSocketTCPObfuscated(NetworkSocket* wrapped);
virtual ~NetworkSocketTCPObfuscated();
virtual NetworkSocket* GetWrapped();
virtual void InitConnection();
virtual void Send(NetworkPacket packet) override;
virtual NetworkPacket Receive(size_t maxLen) override;
virtual void Open();
virtual void Close();
virtual void Connect(const NetworkAddress address, uint16_t port);
virtual bool OnReadyToSend();
virtual bool IsFailed();
virtual bool IsReadyToSend(){
return readyToSend && wrapped->IsReadyToSend();
};
private:
NetworkSocket* wrapped;
TCPO2State recvState;
TCPO2State sendState;
bool initialized=false;
};
class NetworkSocketSOCKS5Proxy : public NetworkSocketWrapper{
public:
NetworkSocketSOCKS5Proxy(NetworkSocket* tcp, NetworkSocket* udp, std::string username, std::string password);
virtual ~NetworkSocketSOCKS5Proxy();
virtual void Send(NetworkPacket packet) override;
virtual NetworkPacket Receive(size_t maxLen) override;
virtual void Open() override;
virtual void Close();
virtual void Connect(const NetworkAddress address, uint16_t port);
virtual NetworkSocket *GetWrapped();
virtual void InitConnection();
virtual bool IsFailed();
virtual NetworkAddress GetConnectedAddress();
virtual uint16_t GetConnectedPort();
virtual bool OnReadyToSend();
virtual bool OnReadyToReceive();
bool NeedSelectForSending();
private:
void SendConnectionCommand();
enum ConnectionState{
Initial,
WaitingForAuthMethod,
WaitingForAuthResult,
WaitingForCommandResult,
Connected
};
NetworkSocket* tcp;
NetworkSocket* udp;
std::string username;
std::string password;
NetworkAddress connectedAddress=NetworkAddress::Empty();
uint16_t connectedPort;
ConnectionState state=ConnectionState::Initial;
};
}
#endif //LIBTGVOIP_NETWORKSOCKET_H

View File

@ -0,0 +1,287 @@
//
// 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>
#include <math.h>
#include <algorithm>
#if defined HAVE_CONFIG_H || defined TGVOIP_USE_INSTALLED_OPUS
#include <opus/opus.h>
#else
#include "opus.h"
#endif
#include "VoIPController.h"
#define PACKET_SIZE (960 * 2)
using namespace tgvoip;
tgvoip::OpusDecoder::OpusDecoder(const std::shared_ptr<MediaStreamItf>& dst, bool isAsync, bool needEC) {
dst->SetCallback(OpusDecoder::Callback, this);
Initialize(isAsync, needEC);
}
tgvoip::OpusDecoder::OpusDecoder(const std::unique_ptr<MediaStreamItf>& dst, bool isAsync, bool needEC) {
dst->SetCallback(OpusDecoder::Callback, this);
Initialize(isAsync, needEC);
}
tgvoip::OpusDecoder::OpusDecoder(MediaStreamItf* dst, bool isAsync, bool needEC) {
dst->SetCallback(OpusDecoder::Callback, this);
Initialize(isAsync, needEC);
}
void tgvoip::OpusDecoder::Initialize(bool isAsync, bool needEC) {
async = isAsync;
if (async) {
decodedQueue = new BlockingQueue<Buffer>(33);
semaphore = new Semaphore(32, 0);
} else {
decodedQueue = NULL;
semaphore = NULL;
}
dec=opus_decoder_create(48000, 1, NULL);
if (needEC)
ecDec = opus_decoder_create(48000, 1, NULL);
else
ecDec = NULL;
// todo buffer = reinterpret_cast<unsigned char*>(aligned_alloc(2, 8192));
buffer=(unsigned char *) malloc(8192);
lastDecoded = NULL;
outputBufferSize = 0;
echoCanceller = NULL;
frameDuration = 20;
consecutiveLostPackets = 0;
enableDTX = false;
silentPacketCount = 0;
levelMeter = NULL;
nextLen = 0;
running = false;
remainingDataLen = 0;
processedBuffer = NULL;
prevWasEC = false;
prevLastSample = 0;
}
tgvoip::OpusDecoder::~OpusDecoder() {
opus_decoder_destroy(dec);
if (ecDec)
opus_decoder_destroy(ecDec);
free(buffer);
if (decodedQueue)
delete decodedQueue;
if (semaphore)
delete semaphore;
}
void tgvoip::OpusDecoder::SetEchoCanceller(EchoCanceller* canceller) {
echoCanceller = canceller;
}
size_t tgvoip::OpusDecoder::Callback(unsigned char* data, size_t len, void* param) {
return (reinterpret_cast<OpusDecoder*>(param)->HandleCallback(data, len));
}
size_t tgvoip::OpusDecoder::HandleCallback(unsigned char* data, size_t len) {
if (async) {
if (!running) {
memset(data, 0, len);
return 0;
}
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) {
Buffer lastDecoded=decodedQueue->GetBlocking();
if (lastDecoded.IsEmpty())
return 0;
memcpy(data, *lastDecoded, PACKET_SIZE);
semaphore->Release();
if (silentPacketCount > 0) {
silentPacketCount--;
if (levelMeter)
levelMeter->Update(reinterpret_cast<int16_t*>(data), 0);
return 0;
}
if (echoCanceller) {
echoCanceller->SpeakerOutCallback(data, PACKET_SIZE);
}
} else {
LOGE("Opus decoder buffer length != 960 samples");
abort();
}
} else {
if (remainingDataLen == 0 && silentPacketCount == 0) {
int duration = DecodeNextFrame();
remainingDataLen = static_cast<size_t>(duration) / 20 * 960 * 2;
}
if (silentPacketCount > 0 || remainingDataLen == 0 || !processedBuffer){
if (silentPacketCount > 0)
silentPacketCount--;
memset(data, 0, 960 * 2);
if (levelMeter)
levelMeter->Update(reinterpret_cast<int16_t*>(data), 0);
return 0;
}
memcpy(data, processedBuffer, 960 * 2);
remainingDataLen -= 960 * 2;
if (remainingDataLen > 0) {
memmove(processedBuffer, processedBuffer + 960 * 2, remainingDataLen);
}
}
if (levelMeter)
levelMeter->Update(reinterpret_cast<int16_t*>(data), len / 2);
return len;
}
void tgvoip::OpusDecoder::Start() {
if (!async)
return;
running = true;
thread = new Thread(std::bind(&tgvoip::OpusDecoder::RunThread, this));
thread->SetName("opus_decoder");
thread->SetMaxPriority();
thread->Start();
}
void tgvoip::OpusDecoder::Stop() {
if (!running || !async)
return;
running = false;
semaphore->Release();
thread->Join();
delete thread;
}
void tgvoip::OpusDecoder::RunThread() {
LOGI("decoder: packets per frame %d", packetsPerFrame);
while(running) {
int playbackDuration = DecodeNextFrame();
for (int i = 0; i < playbackDuration / 20; i++) {
semaphore->Acquire();
if (!running) {
LOGI("==== decoder exiting ====");
return;
}
try {
Buffer buf=bufferPool.Get();
if (remainingDataLen > 0) {
for (effects::AudioEffect*& effect:postProcEffects) {
effect->Process(reinterpret_cast<int16_t*>(processedBuffer+(PACKET_SIZE * i)), 960);
}
buf.CopyFrom(processedBuffer + (PACKET_SIZE * i), 0, PACKET_SIZE);
} else {
//LOGE("Error decoding, result=%d", size);
memset(*buf, 0, PACKET_SIZE);
}
decodedQueue->Put(std::move(buf));
} catch (const std::bad_alloc&) {
LOGW("decoder: no buffers left!");
}
}
}
}
int tgvoip::OpusDecoder::DecodeNextFrame() {
int playbackDuration = 0;
bool isEC = false;
size_t len = jitterBuffer->HandleOutput(buffer, 8192, 0, true, playbackDuration, isEC);
bool fec = false;
if (!len) {
fec = true;
len = jitterBuffer->HandleOutput(buffer, 8192, 0, false, playbackDuration, isEC);
//if(len)
// LOGV("Trying FEC...");
}
int size;
if (len) {
size = opus_decode(isEC ? ecDec : dec, buffer, len, reinterpret_cast<opus_int16*>(decodeBuffer), packetsPerFrame * 960, fec ? 1 : 0);
consecutiveLostPackets = 0;
if (prevWasEC != isEC && size) {
// It turns out the waveforms generated by the PLC feature are also great to help smooth out the
// otherwise audible transition between the frames from different decoders. Those are basically an extrapolation
// of the previous successfully decoded data -- which is exactly what we need here.
size = opus_decode(prevWasEC ? ecDec : dec, NULL, 0, reinterpret_cast<opus_int16*>(nextBuffer), packetsPerFrame * 960, 0);
if (size) {
int16_t* plcSamples = reinterpret_cast<int16_t*>(nextBuffer);
int16_t* samples = reinterpret_cast<int16_t*>(decodeBuffer);
constexpr float coeffs[] = {0.999802f, 0.995062f, 0.984031f, 0.966778f, 0.943413f, 0.914084f, 0.878975f, 0.838309f, 0.792344f, 0.741368f,
0.685706f, 0.625708f, 0.561754f, 0.494249f, 0.423619f, 0.350311f, 0.274788f, 0.197527f, 0.119018f, 0.039757f};
for (int i = 0; i < 20; i++) {
samples[i] = static_cast<int16_t>(round(plcSamples[i] * coeffs[i] + samples[i] * (1.f - coeffs[i])));
}
}
}
prevWasEC = isEC;
prevLastSample = decodeBuffer[size - 1];
} else { // do packet loss concealment
consecutiveLostPackets++;
if (consecutiveLostPackets > 2 && enableDTX) {
silentPacketCount += packetsPerFrame;
size = packetsPerFrame * 960;
} else {
size = opus_decode(prevWasEC ? ecDec : dec, NULL, 0, reinterpret_cast<opus_int16*>(decodeBuffer), packetsPerFrame * 960, 0);
//LOGV("PLC");
}
}
if (size < 0)
LOGW("decoder: opus_decode error %d", size);
remainingDataLen = size;
if (playbackDuration == 80) {
processedBuffer = buffer;
audio::Resampler::Rescale60To80(reinterpret_cast<int16_t*>(decodeBuffer),
reinterpret_cast<int16_t*>(processedBuffer));
} else if (playbackDuration == 40) {
processedBuffer = buffer;
audio::Resampler::Rescale60To40(reinterpret_cast<int16_t*>(decodeBuffer),
reinterpret_cast<int16_t*>(processedBuffer));
} else {
processedBuffer = decodeBuffer;
}
return playbackDuration;
}
void tgvoip::OpusDecoder::SetFrameDuration(uint32_t duration) {
frameDuration = duration;
packetsPerFrame = frameDuration / 20;
}
void tgvoip::OpusDecoder::SetJitterBuffer(std::shared_ptr<JitterBuffer> jitterBuffer) {
this->jitterBuffer = jitterBuffer;
}
void tgvoip::OpusDecoder::SetDTX(bool enable) {
enableDTX = enable;
}
void tgvoip::OpusDecoder::SetLevelMeter(AudioLevelMeter* levelMeter) {
this->levelMeter = levelMeter;
}
void tgvoip::OpusDecoder::AddAudioEffect(effects::AudioEffect* effect) {
postProcEffects.push_back(effect);
}
void tgvoip::OpusDecoder::RemoveAudioEffect(effects::AudioEffect *effect) {
std::vector<effects::AudioEffect*>::iterator it = std::find(postProcEffects.begin(), postProcEffects.end(), effect);
if (it != postProcEffects.end())
postProcEffects.erase(it);
}

View File

@ -0,0 +1,81 @@
//
// 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 "threading.h"
#include "BlockingQueue.h"
#include "Buffers.h"
#include "EchoCanceller.h"
#include "JitterBuffer.h"
#include "utils.h"
#include <stdio.h>
#include <vector>
#include <memory>
#include <atomic>
struct OpusDecoder;
namespace tgvoip{
class OpusDecoder {
public:
TGVOIP_DISALLOW_COPY_AND_ASSIGN(OpusDecoder);
virtual void Start();
virtual void Stop();
OpusDecoder(const std::shared_ptr<MediaStreamItf>& dst, bool isAsync, bool needEC);
OpusDecoder(const std::unique_ptr<MediaStreamItf>& dst, bool isAsync, bool needEC);
OpusDecoder(MediaStreamItf* dst, bool isAsync, bool needEC);
virtual ~OpusDecoder();
size_t HandleCallback(unsigned char* data, size_t len);
void SetEchoCanceller(EchoCanceller* canceller);
void SetFrameDuration(uint32_t duration);
void SetJitterBuffer(std::shared_ptr<JitterBuffer> jitterBuffer);
void SetDTX(bool enable);
void SetLevelMeter(AudioLevelMeter* levelMeter);
void AddAudioEffect(effects::AudioEffect* effect);
void RemoveAudioEffect(effects::AudioEffect* effect);
private:
void Initialize(bool isAsync, bool needEC);
static size_t Callback(unsigned char* data, size_t len, void* param);
void RunThread();
int DecodeNextFrame();
::OpusDecoder* dec;
::OpusDecoder* ecDec;
BlockingQueue<Buffer>* decodedQueue;
BufferPool<960*2, 32> bufferPool;
unsigned char* buffer;
unsigned char* lastDecoded;
unsigned char* processedBuffer;
size_t outputBufferSize;
std::atomic<bool> running;
Thread* thread;
Semaphore* semaphore;
uint32_t frameDuration;
EchoCanceller* echoCanceller;
std::shared_ptr<JitterBuffer> jitterBuffer;
AudioLevelMeter* levelMeter;
int consecutiveLostPackets;
bool enableDTX;
size_t silentPacketCount;
std::vector<effects::AudioEffect*> postProcEffects;
std::atomic<bool> async;
alignas(2) unsigned char nextBuffer[8192];
alignas(2) unsigned char decodeBuffer[8192];
size_t nextLen;
unsigned int packetsPerFrame;
ptrdiff_t remainingDataLen;
bool prevWasEC;
int16_t prevLastSample;
};
}
#endif //LIBTGVOIP_OPUSDECODER_H

View File

@ -0,0 +1,265 @@
//
// 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 <algorithm>
#include "logging.h"
#include "VoIPServerConfig.h"
#if defined HAVE_CONFIG_H || defined TGVOIP_USE_INSTALLED_OPUS
#include <opus/opus.h>
#else
#include "opus.h"
#endif
namespace{
int serverConfigValueToBandwidth(int config){
switch(config){
case 0:
return OPUS_BANDWIDTH_NARROWBAND;
case 1:
return OPUS_BANDWIDTH_MEDIUMBAND;
case 2:
return OPUS_BANDWIDTH_WIDEBAND;
case 3:
return OPUS_BANDWIDTH_SUPERWIDEBAND;
case 4:
default:
return OPUS_BANDWIDTH_FULLBAND;
}
}
}
tgvoip::OpusEncoder::OpusEncoder(MediaStreamItf *source, bool needSecondary):queue(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(1));
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_AUTO));
requestedBitrate=20000;
currentBitrate=0;
running=false;
echoCanceller=NULL;
complexity=10;
frameDuration=20;
levelMeter=NULL;
vadNoVoiceBitrate=static_cast<uint32_t>(ServerConfig::GetSharedInstance()->GetInt("audio_vad_no_voice_bitrate", 6000));
vadModeVoiceBandwidth=serverConfigValueToBandwidth(ServerConfig::GetSharedInstance()->GetInt("audio_vad_bandwidth", 3));
vadModeNoVoiceBandwidth=serverConfigValueToBandwidth(ServerConfig::GetSharedInstance()->GetInt("audio_vad_no_voice_bandwidth", 0));
secondaryEnabledBandwidth=serverConfigValueToBandwidth(ServerConfig::GetSharedInstance()->GetInt("audio_extra_ec_bandwidth", 2));
secondaryEncoderEnabled=false;
if(needSecondary){
secondaryEncoder=opus_encoder_create(48000, 1, OPUS_APPLICATION_VOIP, NULL);
opus_encoder_ctl(secondaryEncoder, OPUS_SET_COMPLEXITY(10));
opus_encoder_ctl(secondaryEncoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
opus_encoder_ctl(secondaryEncoder, OPUS_SET_BITRATE(8000));
}else{
secondaryEncoder=NULL;
}
}
tgvoip::OpusEncoder::~OpusEncoder(){
opus_encoder_destroy(enc);
if(secondaryEncoder)
opus_encoder_destroy(secondaryEncoder);
}
void tgvoip::OpusEncoder::Start(){
if(running)
return;
running=true;
thread=new Thread(std::bind(&tgvoip::OpusEncoder::RunThread, this));
thread->SetName("OpusEncoder");
thread->SetMaxPriority();
thread->Start();
}
void tgvoip::OpusEncoder::Stop(){
if(!running)
return;
running=false;
queue.Put(Buffer());
thread->Join();
delete thread;
}
void tgvoip::OpusEncoder::SetBitrate(uint32_t bitrate){
requestedBitrate=bitrate;
}
void tgvoip::OpusEncoder::Encode(int16_t* 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);
}
if(levelMeter)
levelMeter->Update(data, len);
if(secondaryEncoderEnabled!=wasSecondaryEncoderEnabled){
wasSecondaryEncoderEnabled=secondaryEncoderEnabled;
}
int32_t r=opus_encode(enc, data, static_cast<int>(len), buffer, 4096);
// int bw;
// opus_encoder_ctl(enc, OPUS_GET_BANDWIDTH(&bw));
// LOGV("Opus bandwidth: %d", bw);
if(r<=0){
LOGE("Error encoding: %d", r);
}else if(r==1){
LOGW("DTX");
}else if(running){
//LOGV("Packet size = %d", r);
int32_t secondaryLen=0;
unsigned char secondaryBuffer[128];
if(secondaryEncoderEnabled && secondaryEncoder){
secondaryLen=opus_encode(secondaryEncoder, data, static_cast<int>(len), secondaryBuffer, sizeof(secondaryBuffer));
//LOGV("secondaryLen %d", secondaryLen);
}
InvokeCallback(buffer, (size_t)r, secondaryBuffer, (size_t)secondaryLen);
}
}
size_t tgvoip::OpusEncoder::Callback(unsigned char *data, size_t len, void* param){
assert(len==960*2);
OpusEncoder* e=(OpusEncoder*)param;
try{
Buffer buf=e->bufferPool.Get();
buf.CopyFrom(data, 0, 960*2);
e->queue.Put(std::move(buf));
}catch(std::bad_alloc& x){
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::RunThread(){
uint32_t bufferedCount=0;
uint32_t packetsPerFrame=frameDuration/20;
LOGV("starting encoder, packets per frame=%d", packetsPerFrame);
int16_t* frame;
if(packetsPerFrame>1)
frame=(int16_t*) malloc(960*2*packetsPerFrame);
else
frame=NULL;
bool frameHasVoice=false;
bool wasVadMode=false;
while(running){
Buffer _packet=queue.GetBlocking();
if(!_packet.IsEmpty()){
int16_t* packet=(int16_t*)*_packet;
bool hasVoice=true;
if(echoCanceller)
echoCanceller->ProcessInput(packet, 960, hasVoice);
if(!postProcEffects.empty()){
for(effects::AudioEffect* effect:postProcEffects){
effect->Process(packet, 960);
}
}
if(packetsPerFrame==1){
Encode(packet, 960);
}else{
memcpy(frame+(960*bufferedCount), packet, 960*2);
frameHasVoice=frameHasVoice || hasVoice;
bufferedCount++;
if(bufferedCount==packetsPerFrame){
if(vadMode){
if(frameHasVoice){
opus_encoder_ctl(enc, OPUS_SET_BITRATE(currentBitrate));
if(secondaryEncoder){
opus_encoder_ctl(secondaryEncoder, OPUS_SET_BITRATE(currentBitrate));
}
}else{
opus_encoder_ctl(enc, OPUS_SET_BITRATE(vadNoVoiceBitrate));
if(secondaryEncoder){
opus_encoder_ctl(secondaryEncoder, OPUS_SET_BITRATE(vadNoVoiceBitrate));
}
}
wasVadMode=true;
}else if(wasVadMode){
wasVadMode=false;
opus_encoder_ctl(enc, OPUS_SET_BITRATE(currentBitrate));
if(secondaryEncoder){
opus_encoder_ctl(secondaryEncoder, OPUS_SET_BITRATE(currentBitrate));
}
}
Encode(frame, 960*packetsPerFrame);
bufferedCount=0;
frameHasVoice=false;
}
}
}else{
break;
}
}
if(frame)
free(frame);
}
void tgvoip::OpusEncoder::SetOutputFrameDuration(uint32_t duration){
frameDuration=duration;
}
void tgvoip::OpusEncoder::SetPacketLoss(int percent){
packetLossPercent=std::min(20, percent);
opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC(packetLossPercent));
opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC(percent>0 && !secondaryEncoderEnabled ? 1 : 0));
}
int tgvoip::OpusEncoder::GetPacketLoss(){
return packetLossPercent;
}
void tgvoip::OpusEncoder::SetDTX(bool enable){
opus_encoder_ctl(enc, OPUS_SET_DTX(enable ? 1 : 0));
}
void tgvoip::OpusEncoder::SetLevelMeter(tgvoip::AudioLevelMeter *levelMeter){
this->levelMeter=levelMeter;
}
void tgvoip::OpusEncoder::SetCallback(std::function <void(unsigned char*, size_t, unsigned char*, size_t)> f){
callback=f;
}
void tgvoip::OpusEncoder::InvokeCallback(unsigned char *data, size_t length, unsigned char *secondaryData, size_t secondaryLength){
callback(data, length, secondaryData, secondaryLength);
}
void tgvoip::OpusEncoder::SetSecondaryEncoderEnabled(bool enabled){
secondaryEncoderEnabled=enabled;
}
void tgvoip::OpusEncoder::SetVadMode(bool vad){
vadMode=vad;
}
void tgvoip::OpusEncoder::AddAudioEffect(effects::AudioEffect *effect){
postProcEffects.push_back(effect);
}
void tgvoip::OpusEncoder::RemoveAudioEffect(effects::AudioEffect *effect){
std::vector<effects::AudioEffect*>::iterator i=std::find(postProcEffects.begin(), postProcEffects.end(), effect);
if(i!=postProcEffects.end())
postProcEffects.erase(i);
}

View File

@ -0,0 +1,82 @@
//
// libtgvoip is free and unencumbered public domain software.
// For more information, see http://unlicense.org or the UNLICENSE file
// you should have received with this source code distribution.
//
#ifndef LIBTGVOIP_OPUSENCODER_H
#define LIBTGVOIP_OPUSENCODER_H
#include "MediaStreamItf.h"
#include "threading.h"
#include "BlockingQueue.h"
#include "Buffers.h"
#include "EchoCanceller.h"
#include "utils.h"
#include <stdint.h>
#include <atomic>
struct OpusEncoder;
namespace tgvoip{
class OpusEncoder{
public:
TGVOIP_DISALLOW_COPY_AND_ASSIGN(OpusEncoder);
OpusEncoder(MediaStreamItf* source, bool needSecondary);
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();
void SetDTX(bool enable);
void SetLevelMeter(AudioLevelMeter* levelMeter);
void SetCallback(std::function <void(unsigned char*, size_t, unsigned char*, size_t)> callback);
void SetSecondaryEncoderEnabled(bool enabled);
void SetVadMode(bool vad);
void AddAudioEffect(effects::AudioEffect* effect);
void RemoveAudioEffect(effects::AudioEffect* effect);
int GetComplexity(){
return complexity;
}
private:
static size_t Callback(unsigned char* data, size_t len, void* param);
void RunThread();
void Encode(int16_t* data, size_t len);
void InvokeCallback(unsigned char* data, size_t length, unsigned char* secondaryData, size_t secondaryLength);
MediaStreamItf* source;
::OpusEncoder* enc;
::OpusEncoder* secondaryEncoder;
unsigned char buffer[4096];
std::atomic<uint32_t> requestedBitrate;
uint32_t currentBitrate;
Thread* thread;
BlockingQueue<Buffer> queue;
BufferPool<960*2, 10> bufferPool;
EchoCanceller* echoCanceller;
std::atomic<int> complexity;
std::atomic<bool> running;
uint32_t frameDuration;
int packetLossPercent;
AudioLevelMeter* levelMeter;
std::atomic<bool> secondaryEncoderEnabled;
bool vadMode=false;
uint32_t vadNoVoiceBitrate;
std::vector<effects::AudioEffect*> postProcEffects;
int secondaryEnabledBandwidth;
int vadModeVoiceBandwidth;
int vadModeNoVoiceBandwidth;
bool wasSecondaryEncoderEnabled=false;
std::function <void(unsigned char*, size_t, unsigned char*, size_t)> callback;
};
}
#endif //LIBTGVOIP_OPUSENCODER_H

View File

@ -0,0 +1,226 @@
//
// Created by Grishka on 19.03.2018.
//
#include "PacketReassembler.h"
#include "logging.h"
#include "PrivateDefines.h"
#include "video/VideoFEC.h"
#include <assert.h>
#include <sstream>
#define NUM_OLD_PACKETS 3
#define NUM_FEC_PACKETS 10
using namespace tgvoip;
using namespace tgvoip::video;
PacketReassembler::PacketReassembler(){
}
PacketReassembler::~PacketReassembler(){
}
void PacketReassembler::Reset(){
}
void PacketReassembler::AddFragment(Buffer pkt, unsigned int fragmentIndex, unsigned int fragmentCount, uint32_t pts, uint8_t _fseq, bool keyframe, uint16_t rotation){
for(std::unique_ptr<Packet>& packet:packets){
if(packet->timestamp==pts){
if(fragmentCount!=packet->partCount){
LOGE("Received fragment total count %u inconsistent with previous %u", fragmentCount, packet->partCount);
return;
}
if(fragmentIndex>=packet->partCount){
LOGE("Received fragment index %u is greater than total %u", fragmentIndex, fragmentCount);
return;
}
packet->AddFragment(std::move(pkt), fragmentIndex);
return;
}
}
uint32_t fseq=(lastFrameSeq & 0xFFFFFF00) | (uint32_t)_fseq;
if((uint8_t)lastFrameSeq>_fseq)
fseq+=256;
//LOGV("fseq: %u", (unsigned int)fseq);
/*if(pts<maxTimestamp){
LOGW("Received fragment doesn't belong here (ts=%u < maxTs=%u)", pts, maxTimestamp);
return;
}*/
if(lastFrameSeq>3 && fseq<lastFrameSeq-3){
LOGW("Packet too late (fseq=%u, lastFseq=%u)", fseq, lastFrameSeq);
return;
}
if(fragmentIndex>=fragmentCount){
LOGE("Received fragment index %u is out of bounds %u", fragmentIndex, fragmentCount);
return;
}
if(fragmentCount>255){
LOGE("Received fragment total count too big %u", fragmentCount);
return;
}
maxTimestamp=std::max(maxTimestamp, pts);
packets.push_back(std::unique_ptr<Packet>(new Packet(fseq, pts, fragmentCount, 0, keyframe, rotation)));
packets[packets.size()-1]->AddFragment(std::move(pkt), fragmentIndex);
while(packets.size()>3){
std::unique_ptr<Packet>& _old=packets[0];
if(_old->receivedPartCount==_old->partCount){
std::unique_ptr<Packet> old=std::move(packets[0]);
packets.erase(packets.begin());
Buffer buffer=old->Reassemble();
callback(std::move(buffer), old->seq, old->isKeyframe, old->rotation);
oldPackets.push_back(std::move(old));
while(oldPackets.size()>NUM_OLD_PACKETS)
oldPackets.erase(oldPackets.begin());
}else{
LOGW("Packet %u not reassembled (%u of %u)", packets[0]->seq, packets[0]->receivedPartCount, packets[0]->partCount);
if(packets[0]->partCount-packets[0]->receivedPartCount==1 && !waitingForFEC){
bool found=false;
for(FecPacket& fec:fecPackets){
if(packets[0]->seq<=fec.seq && packets[0]->seq>fec.seq-fec.prevFrameCount){
LOGI("Found FEC packet: %u %u", fec.seq, fec.prevFrameCount);
found=true;
TryDecodeFEC(fec);
packets.erase(packets.begin());
break;
}
}
if(!found){
waitingForFEC=true;
break;
}
}else{
waitingForFEC=false;
LOGE("unrecoverable packet loss");
std::unique_ptr<Packet> old=std::move(packets[0]);
packets.erase(packets.begin());
oldPackets.push_back(std::move(old));
while(oldPackets.size()>NUM_OLD_PACKETS)
oldPackets.erase(oldPackets.begin());
}
}
}
lastFrameSeq=fseq;
}
void PacketReassembler::AddFEC(Buffer data, uint8_t _fseq, unsigned int frameCount, unsigned int fecScheme){
uint32_t fseq=(lastFrameSeq & 0xFFFFFF00) | (uint32_t)_fseq;
std::ostringstream _s;
for(unsigned int i=0;i<frameCount;i++){
_s << (fseq-i);
_s << " ";
}
//LOGV("Received FEC packet: len %u, scheme %u, frames %s", (unsigned int)data.Length(), fecScheme, _s.str().c_str());
FecPacket fec{
fseq,
frameCount,
fecScheme,
std::move(data)
};
if(waitingForFEC){
if(packets[0]->seq<=fec.seq && packets[0]->seq>fec.seq-fec.prevFrameCount){
LOGI("Found FEC packet: %u %u", fec.seq, fec.prevFrameCount);
TryDecodeFEC(fec);
packets.erase(packets.begin());
waitingForFEC=false;
}
}
fecPackets.push_back(std::move(fec));
while(fecPackets.size()>NUM_FEC_PACKETS)
fecPackets.erase(fecPackets.begin());
}
void PacketReassembler::SetCallback(std::function<void(Buffer packet, uint32_t pts, bool keyframe, uint16_t rotation)> callback){
this->callback=callback;
}
bool PacketReassembler::TryDecodeFEC(PacketReassembler::FecPacket &fec){
/*LOGI("Decoding FEC");
std::vector<Buffer> packetsForRecovery;
for(std::unique_ptr<Packet>& p:oldPackets){
if(p->seq<=fec.seq && p->seq>fec.seq-fec.prevFrameCount){
LOGD("Adding frame %u from old", p->seq);
for(uint32_t i=0;i<p->partCount;i++){
packetsForRecovery.push_back(i<p->parts.size() ? Buffer::CopyOf(p->parts[i]) : Buffer());
}
}
}
for(std::unique_ptr<Packet>& p:packets){
if(p->seq<=fec.seq && p->seq>fec.seq-fec.prevFrameCount){
LOGD("Adding frame %u from pending", p->seq);
for(uint32_t i=0;i<p->partCount;i++){
//LOGV("[%u] size %u", i, p.parts[i].Length());
packetsForRecovery.push_back(i<p->parts.size() ? Buffer::CopyOf(p->parts[i]) : Buffer());
}
}
}
if(fec.fecScheme==FEC_SCHEME_XOR){
Buffer recovered=ParityFEC::Decode(packetsForRecovery, fec.data);
LOGI("Recovered packet size %u", (unsigned int)recovered.Length());
if(!recovered.IsEmpty()){
std::unique_ptr<Packet>& pkt=packets[0];
if(pkt->parts.size()<pkt->partCount){
pkt->parts.push_back(std::move(recovered));
}else{
for(Buffer &b:pkt->parts){
if(b.IsEmpty()){
b=std::move(recovered);
break;
}
}
}
pkt->receivedPartCount++;
callback(pkt->Reassemble(), pkt->seq, pkt->isKeyframe, pkt->rotation);
}
}*/
return false;
}
#pragma mark - Packet
void PacketReassembler::Packet::AddFragment(Buffer pkt, uint32_t fragmentIndex){
//LOGV("Add fragment %u/%u to packet %u", fragmentIndex, partCount, timestamp);
if(parts.size()==fragmentIndex){
parts.push_back(std::move(pkt));
//LOGV("add1");
}else if(parts.size()>fragmentIndex){
assert(parts[fragmentIndex].IsEmpty());
parts[fragmentIndex]=std::move(pkt);
//LOGV("add2");
}else{
while(parts.size()<fragmentIndex)
parts.push_back(Buffer());
parts.push_back(std::move(pkt));
//LOGV("add3");
}
receivedPartCount++;
//assert(parts.size()>=receivedPartCount);
if(parts.size()<receivedPartCount)
LOGW("Received %u parts but parts.size is %u", (unsigned int)receivedPartCount, (unsigned int)parts.size());
}
Buffer PacketReassembler::Packet::Reassemble(){
assert(partCount==receivedPartCount);
assert(parts.size()==partCount);
if(partCount==1){
return Buffer::CopyOf(parts[0]);
}
BufferOutputStream out(10240);
for(unsigned int i=0;i<partCount;i++){
out.WriteBytes(parts[i]);
//parts[i]=Buffer();
}
return Buffer(std::move(out));
}

View File

@ -0,0 +1,64 @@
//
// Created by Grishka on 19.03.2018.
//
#ifndef TGVOIP_PACKETREASSEMBLER_H
#define TGVOIP_PACKETREASSEMBLER_H
#include <vector>
#include <functional>
#include <unordered_map>
#include <memory>
#include "Buffers.h"
#include "logging.h"
namespace tgvoip {
class PacketReassembler{
public:
PacketReassembler();
virtual ~PacketReassembler();
void Reset();
void AddFragment(Buffer pkt, unsigned int fragmentIndex, unsigned int fragmentCount, uint32_t pts, uint8_t fseq, bool keyframe, uint16_t rotation);
void AddFEC(Buffer data, uint8_t fseq, unsigned int frameCount, unsigned int fecScheme);
void SetCallback(std::function<void(Buffer packet, uint32_t pts, bool keyframe, uint16_t rotation)> callback);
private:
struct Packet{
uint32_t seq;
uint32_t timestamp;
uint32_t partCount;
uint32_t receivedPartCount;
bool isKeyframe;
uint16_t rotation;
std::vector<Buffer> parts;
Packet(uint32_t seq, uint32_t timestamp, uint32_t partCount, uint32_t receivedPartCount, bool keyframe, uint16_t rotation)
:seq(seq), timestamp(timestamp), partCount(partCount), receivedPartCount(receivedPartCount), isKeyframe(keyframe), rotation(rotation){
}
void AddFragment(Buffer pkt, uint32_t fragmentIndex);
Buffer Reassemble();
};
struct FecPacket{
uint32_t seq;
uint32_t prevFrameCount;
uint32_t fecScheme;
Buffer data;
};
bool TryDecodeFEC(FecPacket& fec);
std::function<void(Buffer, uint32_t, bool, uint16_t)> callback;
std::vector<std::unique_ptr<Packet>> packets;
std::vector<std::unique_ptr<Packet>> oldPackets; // for FEC
std::vector<FecPacket> fecPackets;
uint32_t maxTimestamp=0;
uint32_t lastFrameSeq=0;
bool waitingForFEC=false;
};
}
#endif //TGVOIP_PACKETREASSEMBLER_H

View File

@ -0,0 +1,64 @@
//
// Created by Grishka on 19/03/2019.
//
#ifndef LIBTGVOIP_PACKETSENDER_H
#define LIBTGVOIP_PACKETSENDER_H
#include "VoIPController.h"
#include <functional>
#include <stdint.h>
namespace tgvoip{
class PacketSender{
public:
PacketSender(VoIPController* controller) : controller(controller) {};
virtual ~PacketSender(){};
virtual void PacketAcknowledged(uint32_t seq, double sendTime, double ackTime, uint8_t type, uint32_t size)=0;
virtual void PacketLost(uint32_t seq, uint8_t type, uint32_t size)=0;
protected:
void SendExtra(Buffer& data, unsigned char type){
controller->SendExtra(data, type);
}
void IncrementUnsentStreamPackets(){
controller->unsentStreamPackets++;
}
uint32_t SendPacket(VoIPController::PendingOutgoingPacket pkt){
uint32_t seq=controller->GenerateOutSeq();
pkt.seq=seq;
controller->SendOrEnqueuePacket(std::move(pkt), true, this);
return seq;
}
double GetConnectionInitTime(){
return controller->connectionInitTime;
}
const HistoricBuffer<double, 32>& RTTHistory(){
return controller->rttHistory;
}
MessageThread& GetMessageThread(){
return controller->messageThread;
}
const VoIPController::ProtocolInfo& GetProtocolInfo(){
return controller->protocolInfo;
}
void SendStreamFlags(VoIPController::Stream& stm){
controller->SendStreamFlags(stm);
}
const VoIPController::Config& GetConfig(){
return controller->config;
}
VoIPController* controller;
};
}
#endif //LIBTGVOIP_PACKETSENDER_H

View File

@ -0,0 +1,142 @@
//
// Created by Grishka on 20.04.2018.
//
#ifndef TGVOIP_PRIVATEDEFINES_H
#define TGVOIP_PRIVATEDEFINES_H
#define PKT_INIT 1
#define PKT_INIT_ACK 2
#define PKT_STREAM_STATE 3
#define PKT_STREAM_DATA 4
#define PKT_UPDATE_STREAMS 5
#define PKT_PING 6
#define PKT_PONG 7
#define PKT_STREAM_DATA_X2 8
#define PKT_STREAM_DATA_X3 9
#define PKT_LAN_ENDPOINT 10
#define PKT_NETWORK_CHANGED 11
#define PKT_SWITCH_PREF_RELAY 12
#define PKT_SWITCH_TO_P2P 13
#define PKT_NOP 14
//#define PKT_GROUP_CALL_KEY 15 // replaced with 'extra' in 2.1 (protocol v6)
//#define PKT_REQUEST_GROUP 16
#define PKT_STREAM_EC 17
#define IS_MOBILE_NETWORK(x) (x==NET_TYPE_GPRS || x==NET_TYPE_EDGE || x==NET_TYPE_3G || x==NET_TYPE_HSPA || x==NET_TYPE_LTE || x==NET_TYPE_OTHER_MOBILE)
#define PROTOCOL_NAME 0x50567247 // "GrVP" in little endian (reversed here)
#define PROTOCOL_VERSION 9
#define MIN_PROTOCOL_VERSION 3
#define STREAM_DATA_FLAG_LEN16 0x40
#define STREAM_DATA_FLAG_HAS_MORE_FLAGS 0x80
// Since the data can't be larger than the MTU anyway,
// 5 top bits of data length are allocated for these flags
#define STREAM_DATA_XFLAG_KEYFRAME (1 << 15)
#define STREAM_DATA_XFLAG_FRAGMENTED (1 << 14)
#define STREAM_DATA_XFLAG_EXTRA_FEC (1 << 13)
#define STREAM_TYPE_AUDIO 1
#define STREAM_TYPE_VIDEO 2
#define FOURCC(a,b,c,d) ((uint32_t)d | ((uint32_t)c << 8) | ((uint32_t)b << 16) | ((uint32_t)a << 24))
#define PRINT_FOURCC(x) (char)(x >> 24), (char)(x >> 16), (char)(x >> 8), (char)x
#define CODEC_OPUS_OLD 1
#define CODEC_OPUS FOURCC('O','P','U','S')
#define CODEC_AVC FOURCC('A','V','C',' ')
#define CODEC_HEVC FOURCC('H','E','V','C')
#define CODEC_VP8 FOURCC('V','P','8','0')
#define CODEC_VP9 FOURCC('V','P','9','0')
#define CODEC_AV1 FOURCC('A','V','0','1')
#define DEFAULT_MTU 1100
/*flags:# voice_call_id:flags.2?int128 in_seq_no:flags.4?int out_seq_no:flags.4?int
* recent_received_mask:flags.5?int proto:flags.3?int extra:flags.1?string raw_data:flags.0?string*/
#define PFLAG_HAS_DATA 1
#define PFLAG_HAS_EXTRA 2
#define PFLAG_HAS_CALL_ID 4
#define PFLAG_HAS_PROTO 8
#define PFLAG_HAS_SEQ 16
#define PFLAG_HAS_RECENT_RECV 32
#define PFLAG_HAS_SENDER_TAG_HASH 64
#define XPFLAG_HAS_EXTRA 1
#define XPFLAG_HAS_RECV_TS 2
#define EXTRA_TYPE_STREAM_FLAGS 1
#define EXTRA_TYPE_STREAM_CSD 2
#define EXTRA_TYPE_LAN_ENDPOINT 3
#define EXTRA_TYPE_NETWORK_CHANGED 4
#define EXTRA_TYPE_GROUP_CALL_KEY 5
#define EXTRA_TYPE_REQUEST_GROUP 6
#define EXTRA_TYPE_IPV6_ENDPOINT 7
#define STREAM_FLAG_ENABLED 1
#define STREAM_FLAG_DTX 2
#define STREAM_FLAG_EXTRA_EC 4
#define STREAM_FLAG_PAUSED 8
#define STREAM_RFLAG_SUPPORTED 1
#define INIT_FLAG_DATA_SAVING_ENABLED 1
#define INIT_FLAG_GROUP_CALLS_SUPPORTED 2
#define INIT_FLAG_VIDEO_SEND_SUPPORTED 4
#define INIT_FLAG_VIDEO_RECV_SUPPORTED 8
#define INIT_VIDEO_RES_NONE 0
#define INIT_VIDEO_RES_240 1
#define INIT_VIDEO_RES_360 2
#define INIT_VIDEO_RES_480 3
#define INIT_VIDEO_RES_720 4
#define INIT_VIDEO_RES_1080 5
#define INIT_VIDEO_RES_1440 6
#define INIT_VIDEO_RES_4K 7
#define TLID_DECRYPTED_AUDIO_BLOCK 0xDBF948C1
#define TLID_SIMPLE_AUDIO_BLOCK 0xCC0D0E76
#define TLID_UDP_REFLECTOR_PEER_INFO 0x27D9371C
#define TLID_UDP_REFLECTOR_PEER_INFO_IPV6 0x83fc73b1
#define TLID_UDP_REFLECTOR_SELF_INFO 0xc01572c7
#define TLID_UDP_REFLECTOR_REQUEST_PACKETS_INFO 0x1a06fc96
#define TLID_UDP_REFLECTOR_LAST_PACKETS_INFO 0x0e107305
#define TLID_VECTOR 0x1cb5c415
#define PAD4(x) (4-(x+(x<=253 ? 1 : 0))%4)
#define MAX_RECENT_PACKETS 128
#define SHA1_LENGTH 20
#define SHA256_LENGTH 32
#ifdef _MSC_VER
#define MSC_STACK_FALLBACK(a, b) (b)
#else
#define MSC_STACK_FALLBACK(a, b) (a)
#endif
#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));
}
#define NEED_RATE_FLAG_SHITTY_INTERNET_MODE 1
#define NEED_RATE_FLAG_UDP_NA 2
#define NEED_RATE_FLAG_UDP_BAD 4
#define NEED_RATE_FLAG_RECONNECTING 8
#define VIDEO_FRAME_FLAG_KEYFRAME 1
#define VIDEO_ROTATION_MASK 3
#define VIDEO_ROTATION_0 0
#define VIDEO_ROTATION_90 1
#define VIDEO_ROTATION_180 2
#define VIDEO_ROTATION_270 3
#define FEC_SCHEME_XOR 1
#define FEC_SCHEME_CM256 2
#endif //TGVOIP_PRIVATEDEFINES_H

View File

@ -0,0 +1,426 @@
#include <mutex>
#include "TgVoip.h"
#include "VoIPController.h"
#include "VoIPServerConfig.h"
#include <stdarg.h>
#ifndef TGVOIP_USE_CUSTOM_CRYPTO
extern "C" {
#include <openssl/sha.h>
#include <openssl/aes.h>
//#include <openssl/modes.h>
#include <openssl/rand.h>
}
void tgvoip_openssl_aes_ige_encrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){
AES_KEY akey;
AES_set_encrypt_key(key, 32*8, &akey);
AES_ige_encrypt(in, out, length, &akey, iv, AES_ENCRYPT);
}
void tgvoip_openssl_aes_ige_decrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){
AES_KEY akey;
AES_set_decrypt_key(key, 32*8, &akey);
AES_ige_encrypt(in, out, length, &akey, iv, AES_DECRYPT);
}
void tgvoip_openssl_rand_bytes(uint8_t* buffer, size_t len){
RAND_bytes(buffer, len);
}
void tgvoip_openssl_sha1(uint8_t* msg, size_t len, uint8_t* output){
SHA1(msg, len, output);
}
void tgvoip_openssl_sha256(uint8_t* msg, size_t len, uint8_t* output){
SHA256(msg, len, output);
}
void tgvoip_openssl_aes_ctr_encrypt(uint8_t* inout, size_t length, uint8_t* key, uint8_t* iv, uint8_t* ecount, uint32_t* num){
AES_KEY akey;
AES_set_encrypt_key(key, 32*8, &akey);
AES_ctr128_encrypt(inout, inout, length, &akey, iv, ecount, num);
}
void tgvoip_openssl_aes_cbc_encrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){
AES_KEY akey;
AES_set_encrypt_key(key, 256, &akey);
AES_cbc_encrypt(in, out, length, &akey, iv, AES_ENCRYPT);
}
void tgvoip_openssl_aes_cbc_decrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){
AES_KEY akey;
AES_set_decrypt_key(key, 256, &akey);
AES_cbc_encrypt(in, out, length, &akey, iv, AES_DECRYPT);
}
tgvoip::CryptoFunctions tgvoip::VoIPController::crypto={
tgvoip_openssl_rand_bytes,
tgvoip_openssl_sha1,
tgvoip_openssl_sha256,
tgvoip_openssl_aes_ige_encrypt,
tgvoip_openssl_aes_ige_decrypt,
tgvoip_openssl_aes_ctr_encrypt,
tgvoip_openssl_aes_cbc_encrypt,
tgvoip_openssl_aes_cbc_decrypt
};
#endif
class TgVoipImpl : public TgVoip {
public:
TgVoipImpl(
std::vector<TgVoipEndpoint> const &endpoints,
TgVoipPersistentState const &persistentState,
std::unique_ptr<TgVoipProxy> const &proxy,
TgVoipConfig const &config,
TgVoipEncryptionKey const &encryptionKey,
TgVoipNetworkType initialNetworkType
#ifdef TGVOIP_USE_CUSTOM_CRYPTO
,
TgVoipCrypto const &crypto
#endif
#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO
,
TgVoipAudioDataCallbacks const &audioDataCallbacks
#endif
) {
#ifdef TGVOIP_USE_CUSTOM_CRYPTO
tgvoip::VoIPController::crypto.sha1 = crypto.sha1;
tgvoip::VoIPController::crypto.sha256 = crypto.sha256;
tgvoip::VoIPController::crypto.rand_bytes = crypto.rand_bytes;
tgvoip::VoIPController::crypto.aes_ige_encrypt = crypto.aes_ige_encrypt;
tgvoip::VoIPController::crypto.aes_ige_decrypt = crypto.aes_ige_decrypt;
tgvoip::VoIPController::crypto.aes_ctr_encrypt = crypto.aes_ctr_encrypt;
#endif
controller_ = new tgvoip::VoIPController();
controller_->implData = this;
#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO
controller_->SetAudioDataCallbacks(audioDataCallbacks.input, audioDataCallbacks.output, audioDataCallbacks.preprocessed);
#endif
controller_->SetPersistentState(persistentState.value);
if (proxy != nullptr) {
controller_->SetProxy(tgvoip::PROXY_SOCKS5, proxy->host, proxy->port, proxy->login, proxy->password);
}
auto callbacks = tgvoip::VoIPController::Callbacks();
callbacks.connectionStateChanged = &TgVoipImpl::controllerStateCallback;
callbacks.groupCallKeyReceived = nullptr;
callbacks.groupCallKeySent = nullptr;
callbacks.signalBarCountChanged = &TgVoipImpl::signalBarsCallback;
callbacks.upgradeToGroupCallRequested = nullptr;
controller_->SetCallbacks(callbacks);
std::vector<tgvoip::Endpoint> mappedEndpoints;
for (auto endpoint : endpoints) {
tgvoip::Endpoint::Type mappedType;
switch (endpoint.type) {
case TgVoipEndpointType::UdpRelay:
mappedType = tgvoip::Endpoint::Type::UDP_RELAY;
break;
case TgVoipEndpointType::Lan:
mappedType = tgvoip::Endpoint::Type::UDP_P2P_LAN;
break;
case TgVoipEndpointType::Inet:
mappedType = tgvoip::Endpoint::Type::UDP_P2P_INET;
break;
case TgVoipEndpointType::TcpRelay:
mappedType = tgvoip::Endpoint::Type::TCP_RELAY;
break;
default:
mappedType = tgvoip::Endpoint::Type::UDP_RELAY;
break;
}
tgvoip::IPv4Address address(endpoint.host.ipv4);
tgvoip::IPv6Address addressv6(endpoint.host.ipv6);
mappedEndpoints.emplace_back(endpoint.endpointId, endpoint.port, address, addressv6, mappedType, endpoint.peerTag);
}
int mappedDataSaving;
switch (config.dataSaving) {
case TgVoipDataSaving::Mobile:
mappedDataSaving = tgvoip::DATA_SAVING_MOBILE;
break;
case TgVoipDataSaving::Always:
mappedDataSaving = tgvoip::DATA_SAVING_ALWAYS;
break;
default:
mappedDataSaving = tgvoip::DATA_SAVING_NEVER;
break;
}
tgvoip::VoIPController::Config mappedConfig(
config.initializationTimeout,
config.receiveTimeout,
mappedDataSaving,
config.enableAEC,
config.enableNS,
config.enableAGC,
config.enableCallUpgrade
);
mappedConfig.logFilePath = config.logPath;
mappedConfig.statsDumpFilePath = {};
controller_->SetConfig(mappedConfig);
setNetworkType(initialNetworkType);
std::vector<uint8_t> encryptionKeyValue = encryptionKey.value;
controller_->SetEncryptionKey(reinterpret_cast<char*>(encryptionKeyValue.data()), encryptionKey.isOutgoing);
controller_->SetRemoteEndpoints(mappedEndpoints, config.enableP2P, config.maxApiLayer);
controller_->Start();
controller_->Connect();
}
~TgVoipImpl() {
}
void setOnStateUpdated(std::function<void(TgVoipState)> onStateUpdated) {
std::lock_guard<std::mutex> lock(m_onStateUpdated);
onStateUpdated_ = onStateUpdated;
}
void setOnSignalBarsUpdated(std::function<void(int)> onSignalBarsUpdated) {
std::lock_guard<std::mutex> lock(m_onSignalBarsUpdated);
onSignalBarsUpdated_ = onSignalBarsUpdated;
}
void setNetworkType(TgVoipNetworkType networkType) override {
int mappedType;
switch (networkType) {
case TgVoipNetworkType::Unknown:
mappedType = tgvoip::NET_TYPE_UNKNOWN;
break;
case TgVoipNetworkType::Gprs:
mappedType = tgvoip::NET_TYPE_GPRS;
break;
case TgVoipNetworkType::Edge:
mappedType = tgvoip::NET_TYPE_EDGE;
break;
case TgVoipNetworkType::ThirdGeneration:
mappedType = tgvoip::NET_TYPE_3G;
break;
case TgVoipNetworkType::Hspa:
mappedType = tgvoip::NET_TYPE_HSPA;
break;
case TgVoipNetworkType::Lte:
mappedType = tgvoip::NET_TYPE_LTE;
break;
case TgVoipNetworkType::WiFi:
mappedType = tgvoip::NET_TYPE_WIFI;
break;
case TgVoipNetworkType::Ethernet:
mappedType = tgvoip::NET_TYPE_ETHERNET;
break;
case TgVoipNetworkType::OtherHighSpeed:
mappedType = tgvoip::NET_TYPE_OTHER_HIGH_SPEED;
break;
case TgVoipNetworkType::OtherLowSpeed:
mappedType = tgvoip::NET_TYPE_OTHER_LOW_SPEED;
break;
case TgVoipNetworkType::OtherMobile:
mappedType = tgvoip::NET_TYPE_OTHER_MOBILE;
break;
case TgVoipNetworkType::Dialup:
mappedType = tgvoip::NET_TYPE_DIALUP;
break;
default:
mappedType = tgvoip::NET_TYPE_UNKNOWN;
break;
}
controller_->SetNetworkType(mappedType);
}
void setMuteMicrophone(bool muteMicrophone) override {
controller_->SetMicMute(muteMicrophone);
}
void setAudioOutputGainControlEnabled(bool enabled) override {
controller_->SetAudioOutputGainControlEnabled(enabled);
}
void setEchoCancellationStrength(int strength) override {
controller_->SetEchoCancellationStrength(strength);
}
std::string getLastError() override {
switch (controller_->GetLastError()) {
case tgvoip::ERROR_INCOMPATIBLE: return "ERROR_INCOMPATIBLE";
case tgvoip::ERROR_TIMEOUT: return "ERROR_TIMEOUT";
case tgvoip::ERROR_AUDIO_IO: return "ERROR_AUDIO_IO";
case tgvoip::ERROR_PROXY: return "ERROR_PROXY";
default: return "ERROR_UNKNOWN";
}
}
std::string getDebugInfo() override {
return controller_->GetDebugString();
}
int64_t getPreferredRelayId() override {
return controller_->GetPreferredRelayID();
}
TgVoipTrafficStats getTrafficStats() override {
tgvoip::VoIPController::TrafficStats stats;
controller_->GetStats(&stats);
return {
.bytesSentWifi = stats.bytesSentWifi,
.bytesReceivedWifi = stats.bytesRecvdWifi,
.bytesSentMobile = stats.bytesSentMobile,
.bytesReceivedMobile = stats.bytesRecvdMobile
};
}
TgVoipPersistentState getPersistentState() override {
return {controller_->GetPersistentState()};
}
TgVoipFinalState stop() override {
controller_->Stop();
TgVoipFinalState finalState = {
.persistentState = getPersistentState(),
.debugLog = controller_->GetDebugLog(),
.trafficStats = getTrafficStats(),
.isRatingSuggested = controller_->NeedRate()
};
delete controller_;
controller_ = nullptr;
return finalState;
}
static void controllerStateCallback(tgvoip::VoIPController* controller, int state) {
TgVoipImpl* self = reinterpret_cast<TgVoipImpl*>(controller->implData);
std::lock_guard<std::mutex> lock(self->m_onStateUpdated);
if (self->onStateUpdated_) {
TgVoipState mappedState;
switch (state) {
case tgvoip::STATE_WAIT_INIT:
mappedState = TgVoipState::WaitInit;
break;
case tgvoip::STATE_WAIT_INIT_ACK:
mappedState = TgVoipState::WaitInitAck;
break;
case tgvoip::STATE_ESTABLISHED:
mappedState = TgVoipState::Estabilished;
break;
case tgvoip::STATE_FAILED:
mappedState = TgVoipState::Failed;
break;
case tgvoip::STATE_RECONNECTING:
mappedState = TgVoipState::Reconnecting;
break;
default:
mappedState = TgVoipState::Estabilished;
break;
}
self->onStateUpdated_(mappedState);
}
}
static void signalBarsCallback(tgvoip::VoIPController* controller, int signalBars) {
TgVoipImpl* self = reinterpret_cast<TgVoipImpl*>(controller->implData);
std::lock_guard<std::mutex> lock(self->m_onSignalBarsUpdated);
if (self->onSignalBarsUpdated_) {
self->onSignalBarsUpdated_(signalBars);
}
}
private:
tgvoip::VoIPController *controller_;
std::function<void(TgVoipState)> onStateUpdated_;
std::function<void(int)> onSignalBarsUpdated_;
std::mutex m_onStateUpdated, m_onSignalBarsUpdated;
};
std::function<void(std::string const &)> globalLoggingFunction;
void __tgvoip_call_tglog(const char *format, ...){
va_list vaArgs;
va_start(vaArgs, format);
va_list vaCopy;
va_copy(vaCopy, vaArgs);
const int length = std::vsnprintf(nullptr, 0, format, vaCopy);
va_end(vaCopy);
std::vector<char> zc(length + 1);
std::vsnprintf(zc.data(), zc.size(), format, vaArgs);
va_end(vaArgs);
if (globalLoggingFunction != nullptr) {
globalLoggingFunction(std::string(zc.data(), zc.size()));
}
}
void TgVoip::setLoggingFunction(std::function<void(std::string const &)> loggingFunction) {
globalLoggingFunction = loggingFunction;
}
void TgVoip::setGlobalServerConfig(const std::string &serverConfig) {
tgvoip::ServerConfig::GetSharedInstance()->Update(serverConfig);
}
int TgVoip::getConnectionMaxLayer() {
return tgvoip::VoIPController::GetConnectionMaxLayer();
}
std::string TgVoip::getVersion() {
return tgvoip::VoIPController::GetVersion();
}
TgVoip *TgVoip::makeInstance(
TgVoipConfig const &config,
TgVoipPersistentState const &persistentState,
std::vector<TgVoipEndpoint> const &endpoints,
std::unique_ptr<TgVoipProxy> const &proxy,
TgVoipNetworkType initialNetworkType,
TgVoipEncryptionKey const &encryptionKey
#ifdef TGVOIP_USE_CUSTOM_CRYPTO
,
TgVoipCrypto const &crypto
#endif
#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO
,
TgVoipAudioDataCallbacks const &audioDataCallbacks
#endif
) {
return new TgVoipImpl(
endpoints,
persistentState,
proxy,
config,
encryptionKey,
initialNetworkType
#ifdef TGVOIP_USE_CUSTOM_CRYPTO
,
crypto
#endif
#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO
,
audioDataCallbacks
#endif
);
}
TgVoip::~TgVoip() = default;

View File

@ -0,0 +1,169 @@
#ifndef __TGVOIP_H
#define __TGVOIP_H
#include <functional>
#include <vector>
#include <string>
#include <memory>
struct TgVoipProxy {
std::string host;
uint16_t port;
std::string login;
std::string password;
};
enum class TgVoipEndpointType {
Inet,
Lan,
UdpRelay,
TcpRelay
};
struct TgVoipEdpointHost {
std::string ipv4;
std::string ipv6;
};
struct TgVoipEndpoint {
int64_t endpointId;
TgVoipEdpointHost host;
uint16_t port;
TgVoipEndpointType type;
unsigned char peerTag[16];
};
enum class TgVoipNetworkType {
Unknown,
Gprs,
Edge,
ThirdGeneration,
Hspa,
Lte,
WiFi,
Ethernet,
OtherHighSpeed,
OtherLowSpeed,
OtherMobile,
Dialup
};
enum class TgVoipDataSaving {
Never,
Mobile,
Always
};
struct TgVoipPersistentState {
std::vector<uint8_t> value;
};
#ifdef TGVOIP_USE_CUSTOM_CRYPTO
struct TgVoipCrypto {
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);
void (*aes_cbc_encrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv);
void (*aes_cbc_decrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv);
};
#endif
struct TgVoipConfig {
double initializationTimeout;
double receiveTimeout;
TgVoipDataSaving dataSaving;
bool enableP2P;
bool enableAEC;
bool enableNS;
bool enableAGC;
bool enableCallUpgrade;
#ifndef _WIN32
std::string logPath;
#else
std::wstring logPath;
#endif
int maxApiLayer;
};
struct TgVoipEncryptionKey {
std::vector<uint8_t> value;
bool isOutgoing;
};
enum class TgVoipState {
WaitInit,
WaitInitAck,
Estabilished,
Failed,
Reconnecting
};
struct TgVoipTrafficStats {
uint64_t bytesSentWifi;
uint64_t bytesReceivedWifi;
uint64_t bytesSentMobile;
uint64_t bytesReceivedMobile;
};
struct TgVoipFinalState {
TgVoipPersistentState persistentState;
std::string debugLog;
TgVoipTrafficStats trafficStats;
bool isRatingSuggested;
};
struct TgVoipAudioDataCallbacks {
std::function<void(int16_t*, size_t)> input;
std::function<void(int16_t*, size_t)> output;
std::function<void(int16_t*, size_t)> preprocessed;
};
class TgVoip {
protected:
TgVoip() = default;
public:
static void setLoggingFunction(std::function<void(std::string const &)> loggingFunction);
static void setGlobalServerConfig(std::string const &serverConfig);
static int getConnectionMaxLayer();
static std::string getVersion();
static TgVoip *makeInstance(
TgVoipConfig const &config,
TgVoipPersistentState const &persistentState,
std::vector<TgVoipEndpoint> const &endpoints,
std::unique_ptr<TgVoipProxy> const &proxy,
TgVoipNetworkType initialNetworkType,
TgVoipEncryptionKey const &encryptionKey
#ifdef TGVOIP_USE_CUSTOM_CRYPTO
,
TgVoipCrypto const &crypto
#endif
#ifdef TGVOIP_USE_CALLBACK_AUDIO_IO
,
TgVoipAudioDataCallbacks const &audioDataCallbacks
#endif
);
virtual ~TgVoip();
virtual void setNetworkType(TgVoipNetworkType networkType) = 0;
virtual void setMuteMicrophone(bool muteMicrophone) = 0;
virtual void setAudioOutputGainControlEnabled(bool enabled) = 0;
virtual void setEchoCancellationStrength(int strength) = 0;
virtual std::string getLastError() = 0;
virtual std::string getDebugInfo() = 0;
virtual int64_t getPreferredRelayId() = 0;
virtual TgVoipTrafficStats getTrafficStats() = 0;
virtual TgVoipPersistentState getPersistentState() = 0;
virtual void setOnStateUpdated(std::function<void(TgVoipState)> onStateUpdated) = 0;
virtual void setOnSignalBarsUpdated(std::function<void(int)> onSignalBarsUpdated) = 0;
virtual TgVoipFinalState stop() = 0;
};
#endif

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,887 @@
//
// 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>
#include "os/darwin/AudioUnitIO.h"
#endif
#include <stdint.h>
#include <vector>
#include <string>
#include <unordered_map>
#include <map>
#include <memory>
#include "video/VideoSource.h"
#include "video/VideoRenderer.h"
#include <atomic>
#include "video/ScreamCongestionController.h"
#include "audio/AudioInput.h"
#include "BlockingQueue.h"
#include "audio/AudioOutput.h"
#include "audio/AudioIO.h"
#include "JitterBuffer.h"
#include "OpusDecoder.h"
#include "OpusEncoder.h"
#include "EchoCanceller.h"
#include "CongestionControl.h"
#include "NetworkSocket.h"
#include "Buffers.h"
#include "PacketReassembler.h"
#include "MessageThread.h"
#include "utils.h"
#define LIBTGVOIP_VERSION "2.6"
#ifdef _WIN32
#undef GetCurrentTime
#undef ERROR_TIMEOUT
#endif
#define TGVOIP_PEER_CAP_GROUP_CALLS 1
#define TGVOIP_PEER_CAP_VIDEO_CAPTURE 2
#define TGVOIP_PEER_CAP_VIDEO_DISPLAY 4
namespace tgvoip{
enum{
PROXY_NONE=0,
PROXY_SOCKS5,
//PROXY_HTTP
};
enum{
STATE_WAIT_INIT=1,
STATE_WAIT_INIT_ACK,
STATE_ESTABLISHED,
STATE_FAILED,
STATE_RECONNECTING
};
enum{
ERROR_UNKNOWN=0,
ERROR_INCOMPATIBLE,
ERROR_TIMEOUT,
ERROR_AUDIO_IO,
ERROR_PROXY
};
enum{
NET_TYPE_UNKNOWN=0,
NET_TYPE_GPRS,
NET_TYPE_EDGE,
NET_TYPE_3G,
NET_TYPE_HSPA,
NET_TYPE_LTE,
NET_TYPE_WIFI,
NET_TYPE_ETHERNET,
NET_TYPE_OTHER_HIGH_SPEED,
NET_TYPE_OTHER_LOW_SPEED,
NET_TYPE_DIALUP,
NET_TYPE_OTHER_MOBILE
};
enum{
DATA_SAVING_NEVER=0,
DATA_SAVING_MOBILE,
DATA_SAVING_ALWAYS
};
struct CryptoFunctions{
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);
void (*aes_cbc_encrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv);
void (*aes_cbc_decrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv);
};
struct CellularCarrierInfo{
std::string name;
std::string mcc;
std::string mnc;
std::string countryCode;
};
// API compatibility
struct IPv4Address{
IPv4Address(std::string addr) : addr(addr){};
std::string addr;
};
struct IPv6Address{
IPv6Address(std::string addr) : addr(addr){};
std::string addr;
};
class Endpoint{
friend class VoIPController;
friend class VoIPGroupController;
public:
enum Type{
UDP_P2P_INET=1,
UDP_P2P_LAN,
UDP_RELAY,
TCP_RELAY
};
Endpoint(int64_t id, uint16_t port, const IPv4Address& address, const IPv6Address& v6address, Type type, unsigned char* peerTag);
Endpoint(int64_t id, uint16_t port, const NetworkAddress address, const NetworkAddress v6address, Type type, unsigned char* peerTag);
Endpoint();
~Endpoint();
const NetworkAddress& GetAddress() const;
NetworkAddress& GetAddress();
bool IsIPv6Only() const;
int64_t CleanID() const;
int64_t id;
uint16_t port;
NetworkAddress address;
NetworkAddress v6address;
Type type;
unsigned char peerTag[16];
private:
double lastPingTime;
uint32_t lastPingSeq;
HistoricBuffer<double, 6> rtts;
HistoricBuffer<double, 4> selfRtts;
std::map<int64_t, double> udpPingTimes;
double averageRTT;
std::shared_ptr<NetworkSocket> socket;
int udpPongCount;
int totalUdpPings=0;
int totalUdpPingReplies=0;
};
class AudioDevice{
public:
std::string id;
std::string displayName;
};
class AudioOutputDevice : public AudioDevice{
};
class AudioInputDevice : public AudioDevice{
};
class AudioInputTester{
public:
AudioInputTester(const std::string deviceID);
~AudioInputTester();
TGVOIP_DISALLOW_COPY_AND_ASSIGN(AudioInputTester);
float GetAndResetLevel();
bool Failed(){
return io && io->Failed();
}
private:
void Update(int16_t* samples, size_t count);
audio::AudioIO* io=NULL;
audio::AudioInput* input=NULL;
int16_t maxSample=0;
std::string deviceID;
};
class PacketSender;
namespace video{
class VideoPacketSender;
}
class VoIPController{
friend class VoIPGroupController;
friend class PacketSender;
public:
TGVOIP_DISALLOW_COPY_AND_ASSIGN(VoIPController);
struct Config{
Config(double initTimeout=30.0, double recvTimeout=20.0, int dataSaving=DATA_SAVING_NEVER, bool enableAEC=false, bool enableNS=false, bool enableAGC=false, bool enableCallUpgrade=false){
this->initTimeout=initTimeout;
this->recvTimeout=recvTimeout;
this->dataSaving=dataSaving;
this->enableAEC=enableAEC;
this->enableNS=enableNS;
this->enableAGC=enableAGC;
this->enableCallUpgrade=enableCallUpgrade;
}
double initTimeout;
double recvTimeout;
int dataSaving;
#ifndef _WIN32
std::string logFilePath="";
std::string statsDumpFilePath="";
#else
std::wstring logFilePath=L"";
std::wstring statsDumpFilePath=L"";
#endif
bool enableAEC;
bool enableNS;
bool enableAGC;
bool enableCallUpgrade;
bool logPacketStats=false;
bool enableVolumeControl=false;
bool enableVideoSend=false;
bool enableVideoReceive=false;
};
struct TrafficStats{
uint64_t bytesSentWifi;
uint64_t bytesRecvdWifi;
uint64_t bytesSentMobile;
uint64_t bytesRecvdMobile;
};
VoIPController();
virtual ~VoIPController();
/**
* Set the initial endpoints (relays)
* @param endpoints Endpoints converted from phone.PhoneConnection TL objects
* @param allowP2p Whether p2p connectivity is allowed
* @param connectionMaxLayer The max_layer field from the phoneCallProtocol object returned by Telegram server.
* DO NOT HARDCODE THIS VALUE, it's extremely important for backwards compatibility.
*/
void SetRemoteEndpoints(std::vector<Endpoint> endpoints, bool allowP2p, int32_t connectionMaxLayer);
/**
* Initialize and start all the internal threads
*/
void Start();
/**
* Stop any internal threads. Don't call any other methods after this.
*/
void Stop();
/**
* Initiate connection
*/
void Connect();
Endpoint& GetRemoteEndpoint();
/**
* Get the debug info string to be displayed in client UI
*/
virtual std::string GetDebugString();
/**
* Notify the library of network type change
* @param type The new network type
*/
virtual void SetNetworkType(int type);
/**
* Get the average round-trip time for network packets
* @return
*/
double GetAverageRTT();
static double GetCurrentTime();
/**
* Use this field to store any of your context data associated with this call
*/
void* implData;
/**
*
* @param mute
*/
virtual void SetMicMute(bool mute);
/**
*
* @param key
* @param isOutgoing
*/
void SetEncryptionKey(char* key, bool isOutgoing);
/**
*
* @param cfg
*/
void SetConfig(const Config& cfg);
void DebugCtl(int request, int param);
/**
*
* @param stats
*/
void GetStats(TrafficStats* stats);
/**
*
* @return
*/
int64_t GetPreferredRelayID();
/**
*
* @return
*/
int GetLastError();
/**
*
*/
static CryptoFunctions crypto;
/**
*
* @return
*/
static const char* GetVersion();
/**
*
* @return
*/
std::string GetDebugLog();
/**
*
* @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 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();
/**
* Enable or disable AGC (automatic gain control) on audio output. Should only be enabled on phones when the earpiece speaker is being used.
* The audio output will be louder with this on.
* AGC with speakerphone or other kinds of loud speakers has detrimental effects on some echo cancellation implementations.
* @param enabled I usually pick argument names to be self-explanatory
*/
void SetAudioOutputGainControlEnabled(bool enabled);
/**
* Get the additional capabilities of the peer client app
* @return corresponding TGVOIP_PEER_CAP_* flags OR'ed together
*/
uint32_t GetPeerCapabilities();
/**
* Send the peer the key for the group call to prepare this private call to an upgrade to a E2E group call.
* The peer must have the TGVOIP_PEER_CAP_GROUP_CALLS capability. After the peer acknowledges the key, Callbacks::groupCallKeySent will be called.
* @param key newly-generated group call key, must be exactly 265 bytes long
*/
void SendGroupCallKey(unsigned char* key);
/**
* In an incoming call, request the peer to generate a new encryption key, send it to you and upgrade this call to a E2E group call.
*/
void RequestCallUpgrade();
void SetEchoCancellationStrength(int strength);
int GetConnectionState();
bool NeedRate();
/**
* Get the maximum connection layer supported by this libtgvoip version.
* Pass this as <code>max_layer</code> in the phone.phoneConnection TL object when requesting and accepting calls.
*/
static int32_t GetConnectionMaxLayer(){
return 92;
};
/**
* Get the persistable state of the library, like proxy capabilities, to save somewhere on the disk. Call this at the end of the call.
* Using this will speed up the connection establishment in some cases.
*/
std::vector<uint8_t> GetPersistentState();
/**
* Load the persistable state. Call this before starting the call.
*/
void SetPersistentState(std::vector<uint8_t> state);
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
void SetAudioDataCallbacks(std::function<void(int16_t*, size_t)> input, std::function<void(int16_t*, size_t)> output, std::function<void(int16_t*, size_t)> preprocessed);
#endif
void SetVideoCodecSpecificData(const std::vector<Buffer>& data);
struct Callbacks{
void (*connectionStateChanged)(VoIPController*, int);
void (*signalBarCountChanged)(VoIPController*, int);
void (*groupCallKeySent)(VoIPController*);
void (*groupCallKeyReceived)(VoIPController*, const unsigned char*);
void (*upgradeToGroupCallRequested)(VoIPController*);
};
void SetCallbacks(Callbacks callbacks);
float GetOutputLevel(){
return 0.0f;
};
void SetVideoSource(video::VideoSource* source);
void SetVideoRenderer(video::VideoRenderer* renderer);
void SetInputVolume(float level);
void SetOutputVolume(float level);
#if defined(__APPLE__) && defined(TARGET_OS_OSX)
void SetAudioOutputDuckingEnabled(bool enabled);
#endif
struct PendingOutgoingPacket{
PendingOutgoingPacket(uint32_t seq, unsigned char type, size_t len, Buffer&& data, int64_t endpoint){
this->seq=seq;
this->type=type;
this->len=len;
this->data=std::move(data);
this->endpoint=endpoint;
}
PendingOutgoingPacket(PendingOutgoingPacket&& other){
seq=other.seq;
type=other.type;
len=other.len;
data=std::move(other.data);
endpoint=other.endpoint;
}
PendingOutgoingPacket& operator=(PendingOutgoingPacket&& other){
if(this!=&other){
seq=other.seq;
type=other.type;
len=other.len;
data=std::move(other.data);
endpoint=other.endpoint;
}
return *this;
}
TGVOIP_DISALLOW_COPY_AND_ASSIGN(PendingOutgoingPacket);
uint32_t seq;
unsigned char type;
size_t len;
Buffer data;
int64_t endpoint;
};
struct Stream{
int32_t userID;
unsigned char id;
unsigned char type;
uint32_t codec;
bool enabled;
bool extraECEnabled;
uint16_t frameDuration;
std::shared_ptr<JitterBuffer> jitterBuffer;
std::shared_ptr<OpusDecoder> decoder;
std::shared_ptr<PacketReassembler> packetReassembler;
std::shared_ptr<CallbackWrapper> callbackWrapper;
std::vector<Buffer> codecSpecificData;
bool csdIsValid=false;
bool paused=false;
int resolution;
unsigned int width=0;
unsigned int height=0;
uint16_t rotation=0;
};
struct ProtocolInfo{
uint32_t version;
uint32_t maxVideoResolution;
std::vector<uint32_t> videoDecoders;
bool videoCaptureSupported;
bool videoDisplaySupported;
bool callUpgradeSupported;
};
private:
struct UnacknowledgedExtraData;
protected:
struct RecentOutgoingPacket{
uint32_t seq;
uint16_t id; // for group calls only
double sendTime;
double ackTime;
uint8_t type;
uint32_t size;
PacketSender* sender;
bool lost;
};
struct QueuedPacket{
Buffer data;
unsigned char type;
HistoricBuffer<uint32_t, 16> seqs;
double firstSentTime;
double lastSentTime;
double retryInterval;
double timeout;
};
virtual void ProcessIncomingPacket(NetworkPacket& packet, Endpoint& srcEndpoint);
virtual void ProcessExtraData(Buffer& data);
virtual void WritePacketHeader(uint32_t seq, BufferOutputStream* s, unsigned char type, uint32_t length, PacketSender* source);
virtual void SendPacket(unsigned char* data, size_t len, Endpoint& ep, PendingOutgoingPacket& srcPacket);
virtual void SendInit();
virtual void SendUdpPing(Endpoint& endpoint);
virtual void SendRelayPings();
virtual void OnAudioOutputReady();
virtual void SendExtra(Buffer& data, unsigned char type);
void SendStreamFlags(Stream& stream);
void InitializeTimers();
void ResetEndpointPingStats();
void SendVideoFrame(const Buffer& frame, uint32_t flags, uint32_t rotation);
void ProcessIncomingVideoFrame(Buffer frame, uint32_t pts, bool keyframe, uint16_t rotation);
std::shared_ptr<Stream> GetStreamByType(int type, bool outgoing);
std::shared_ptr<Stream> GetStreamByID(unsigned char id, bool outgoing);
Endpoint* GetEndpointForPacket(const PendingOutgoingPacket& pkt);
bool SendOrEnqueuePacket(PendingOutgoingPacket pkt, bool enqueue=true, PacketSender* source=NULL);
static std::string NetworkTypeToString(int type);
CellularCarrierInfo GetCarrierInfo();
private:
struct UnacknowledgedExtraData{
unsigned char type;
Buffer data;
uint32_t firstContainingSeq;
};
struct RecentIncomingPacket{
uint32_t seq;
double recvTime;
};
enum{
UDP_UNKNOWN=0,
UDP_PING_PENDING,
UDP_PING_SENT,
UDP_AVAILABLE,
UDP_NOT_AVAILABLE,
UDP_BAD
};
struct DebugLoggedPacket{
int32_t seq;
double timestamp;
int32_t length;
};
struct RawPendingOutgoingPacket{
TGVOIP_MOVE_ONLY(RawPendingOutgoingPacket);
NetworkPacket packet;
std::shared_ptr<NetworkSocket> socket;
};
void RunRecvThread();
void RunSendThread();
void HandleAudioInput(unsigned char* data, size_t len, unsigned char* secondaryData, size_t secondaryLen);
void UpdateAudioBitrateLimit();
void SetState(int state);
void UpdateAudioOutputState();
void InitUDPProxy();
void UpdateDataSavingState();
void KDF(unsigned char* msgKey, size_t x, unsigned char* aesKey, unsigned char* aesIv);
void KDF2(unsigned char* msgKey, size_t x, unsigned char* aesKey, unsigned char* aesIv);
void SendPublicEndpointsRequest();
void SendPublicEndpointsRequest(const 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 ActuallySendPacket(NetworkPacket pkt, Endpoint& ep);
void InitializeAudio();
void StartAudio();
void ProcessAcknowledgedOutgoingExtra(UnacknowledgedExtraData& extra);
void AddIPv6Relays();
void AddTCPRelays();
void SendUdpPings();
void EvaluateUdpPingResults();
void UpdateRTT();
void UpdateCongestion();
void UpdateAudioBitrate();
void UpdateSignalBars();
void UpdateQueuedPackets();
void SendNopPacket();
void TickJitterBufferAndCongestionControl();
void ResetUdpAvailability();
std::string GetPacketTypeString(unsigned char type);
void SetupOutgoingVideoStream();
bool WasOutgoingPacketAcknowledged(uint32_t seq);
RecentOutgoingPacket* GetRecentOutgoingPacket(uint32_t seq);
void NetworkPacketReceived(std::shared_ptr<NetworkPacket> packet);
void TrySendQueuedPackets();
int state;
std::map<int64_t, Endpoint> endpoints;
int64_t currentEndpoint=0;
int64_t preferredRelay=0;
int64_t peerPreferredRelay=0;
std::atomic<bool> runReceiver;
std::atomic<uint32_t> seq;
uint32_t lastRemoteSeq;
uint32_t lastRemoteAckSeq;
uint32_t lastSentSeq;
std::vector<RecentOutgoingPacket> recentOutgoingPackets;
std::vector<uint32_t> recentIncomingPackets;
HistoricBuffer<uint32_t, 10, double> sendLossCountHistory;
uint32_t audioTimestampIn;
uint32_t audioTimestampOut;
tgvoip::audio::AudioIO* audioIO=NULL;
tgvoip::audio::AudioInput* audioInput=NULL;
tgvoip::audio::AudioOutput* audioOutput=NULL;
OpusEncoder* encoder;
std::vector<PendingOutgoingPacket> sendQueue;
EchoCanceller* echoCanceller;
std::atomic<bool> stopping;
bool audioOutStarted;
Thread* recvThread;
Thread* sendThread;
uint32_t packetsReceived;
uint32_t recvLossCount;
uint32_t prevSendLossCount;
uint32_t firstSentPing;
HistoricBuffer<double, 32> rttHistory;
bool waitingForAcks;
int networkType;
int dontSendPackets;
int lastError;
bool micMuted;
uint32_t maxBitrate;
std::vector<std::shared_ptr<Stream>> outgoingStreams;
std::vector<std::shared_ptr<Stream>> incomingStreams;
unsigned char encryptionKey[256];
unsigned char keyFingerprint[8];
unsigned char callID[16];
double stateChangeTime;
bool waitingForRelayPeerInfo;
bool allowP2p;
bool dataSavingMode;
bool dataSavingRequestedByPeer;
std::string activeNetItfName;
double publicEndpointsReqTime;
std::vector<QueuedPacket> queuedPackets;
double connectionInitTime;
double lastRecvPacketTime;
Config config;
int32_t peerVersion;
CongestionControl* conctl;
TrafficStats stats;
bool receivedInit;
bool receivedInitAck;
bool isOutgoing;
NetworkSocket* udpSocket;
NetworkSocket* realUdpSocket;
FILE* statsDump;
std::string currentAudioInput;
std::string currentAudioOutput;
bool useTCP;
bool useUDP;
bool didAddTcpRelays;
SocketSelectCanceller* selectCanceller;
HistoricBuffer<unsigned char, 4, int> signalBarsHistory;
bool audioStarted=false;
int udpConnectivityState;
double lastUdpPingTime;
int udpPingCount;
int echoCancellationStrength;
int proxyProtocol;
std::string proxyAddress;
uint16_t proxyPort;
std::string proxyUsername;
std::string proxyPassword;
NetworkAddress resolvedProxyAddress=NetworkAddress::Empty();
uint32_t peerCapabilities;
Callbacks callbacks;
bool didReceiveGroupCallKey;
bool didReceiveGroupCallKeyAck;
bool didSendGroupCallKey;
bool didSendUpgradeRequest;
bool didInvokeUpgradeCallback;
int32_t connectionMaxLayer;
bool useMTProto2;
bool setCurrentEndpointToTCP;
std::vector<UnacknowledgedExtraData> currentExtras;
std::unordered_map<uint8_t, uint64_t> lastReceivedExtrasByType;
bool useIPv6;
bool peerIPv6Available;
NetworkAddress myIPv6=NetworkAddress::Empty();
bool shittyInternetMode;
int extraEcLevel=0;
std::vector<Buffer> ecAudioPackets;
bool didAddIPv6Relays;
bool didSendIPv6Endpoint;
int publicEndpointsReqCount=0;
bool wasEstablished=false;
bool receivedFirstStreamPacket=false;
std::atomic<unsigned int> unsentStreamPackets;
HistoricBuffer<unsigned int, 5> unsentStreamPacketsHistory;
bool needReInitUdpProxy=true;
bool needRate=false;
std::vector<DebugLoggedPacket> debugLoggedPackets;
BufferPool<1024, 32> outgoingAudioBufferPool;
BlockingQueue<RawPendingOutgoingPacket> rawSendQueue;
uint32_t initTimeoutID=MessageThread::INVALID_ID;
uint32_t udpPingTimeoutID=MessageThread::INVALID_ID;
effects::Volume outputVolume;
effects::Volume inputVolume;
std::vector<uint32_t> peerVideoDecoders;
MessageThread messageThread;
// Locked whenever the endpoints vector is modified (but not endpoints themselves) and whenever iterated outside of messageThread.
// After the call is started, only messageThread is allowed to modify the endpoints vector.
Mutex endpointsMutex;
// Locked while audio i/o is being initialized and deinitialized so as to allow it to fully initialize before deinitialization begins.
Mutex audioIOMutex;
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
std::function<void(int16_t*, size_t)> audioInputDataCallback;
std::function<void(int16_t*, size_t)> audioOutputDataCallback;
std::function<void(int16_t*, size_t)> audioPreprocDataCallback;
::OpusDecoder* preprocDecoder=nullptr;
int16_t preprocBuffer[4096];
#endif
#if defined(__APPLE__) && defined(TARGET_OS_OSX)
bool macAudioDuckingEnabled=true;
#endif
video::VideoSource* videoSource=NULL;
video::VideoRenderer* videoRenderer=NULL;
uint32_t lastReceivedVideoFrameNumber=UINT32_MAX;
video::VideoPacketSender* videoPacketSender=NULL;
uint32_t sendLosses=0;
uint32_t unacknowledgedIncomingPacketCount=0;
ProtocolInfo protocolInfo={0};
/*** debug report problems ***/
bool wasReconnecting=false;
bool wasExtraEC=false;
bool wasEncoderLaggy=false;
bool wasNetworkHandover=false;
/*** persistable state values ***/
bool proxySupportsUDP=true;
bool proxySupportsTCP=true;
std::string lastTestedProxyServer="";
/*** server config values ***/
uint32_t maxAudioBitrate;
uint32_t maxAudioBitrateEDGE;
uint32_t maxAudioBitrateGPRS;
uint32_t maxAudioBitrateSaving;
uint32_t initAudioBitrate;
uint32_t initAudioBitrateEDGE;
uint32_t initAudioBitrateGPRS;
uint32_t initAudioBitrateSaving;
uint32_t minAudioBitrate;
uint32_t audioBitrateStepIncr;
uint32_t audioBitrateStepDecr;
double relaySwitchThreshold;
double p2pToRelaySwitchThreshold;
double relayToP2pSwitchThreshold;
double reconnectingTimeout;
uint32_t needRateFlags;
double rateMaxAcceptableRTT;
double rateMaxAcceptableSendLoss;
double packetLossToEnableExtraEC;
uint32_t maxUnsentStreamPackets;
uint32_t unackNopThreshold;
public:
#ifdef __APPLE__
static double machTimebase;
static uint64_t machTimestart;
#endif
#ifdef _WIN32
static int64_t win32TimeScale;
static bool didInitWin32TimeScale;
#endif
};
class VoIPGroupController : public VoIPController{
public:
VoIPGroupController(int32_t timeDifference);
virtual ~VoIPGroupController();
void SetGroupCallInfo(unsigned char* encryptionKey, unsigned char* reflectorGroupTag, unsigned char* reflectorSelfTag, unsigned char* reflectorSelfSecret, unsigned char* reflectorSelfTagHash, int32_t selfUserID, NetworkAddress reflectorAddress, NetworkAddress reflectorAddressV6, uint16_t reflectorPort);
void AddGroupCallParticipant(int32_t userID, unsigned char* memberTagHash, unsigned char* serializedStreams, size_t streamsLength);
void RemoveGroupCallParticipant(int32_t userID);
float GetParticipantAudioLevel(int32_t userID);
virtual void SetMicMute(bool mute);
void SetParticipantVolume(int32_t userID, float volume);
void SetParticipantStreams(int32_t userID, unsigned char* serializedStreams, size_t length);
static size_t GetInitialStreams(unsigned char* buf, size_t size);
struct Callbacks : public VoIPController::Callbacks{
void (*updateStreams)(VoIPGroupController*, unsigned char*, size_t);
void (*participantAudioStateChanged)(VoIPGroupController*, int32_t, bool);
};
void SetCallbacks(Callbacks callbacks);
virtual std::string GetDebugString();
virtual void SetNetworkType(int type);
protected:
virtual void ProcessIncomingPacket(NetworkPacket& packet, Endpoint& srcEndpoint);
virtual void SendInit();
virtual void SendUdpPing(Endpoint& endpoint);
virtual void SendRelayPings();
virtual void SendPacket(unsigned char* data, size_t len, Endpoint& ep, PendingOutgoingPacket& srcPacket);
virtual void WritePacketHeader(uint32_t seq, BufferOutputStream* s, unsigned char type, uint32_t length, PacketSender* sender=NULL);
virtual void OnAudioOutputReady();
private:
int32_t GetCurrentUnixtime();
std::vector<std::shared_ptr<Stream>> DeserializeStreams(BufferInputStream& in);
void SendRecentPacketsRequest();
void SendSpecialReflectorRequest(unsigned char* data, size_t len);
void SerializeAndUpdateOutgoingStreams();
struct GroupCallParticipant{
int32_t userID;
unsigned char memberTagHash[32];
std::vector<std::shared_ptr<Stream>> streams;
AudioLevelMeter* levelMeter;
};
std::vector<GroupCallParticipant> participants;
unsigned char reflectorSelfTag[16];
unsigned char reflectorSelfSecret[16];
unsigned char reflectorSelfTagHash[32];
int32_t userSelfID;
Endpoint groupReflector;
AudioMixer* audioMixer;
AudioLevelMeter selfLevelMeter;
Callbacks groupCallbacks;
struct PacketIdMapping{
uint32_t seq;
uint16_t id;
double ackTime;
};
std::vector<PacketIdMapping> recentSentPackets;
Mutex sentPacketsMutex;
Mutex participantsMutex;
int32_t timeDifference;
};
};
#endif

View File

@ -0,0 +1,816 @@
//
// 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 "logging.h"
#include "VoIPServerConfig.h"
#include "PrivateDefines.h"
#include <assert.h>
#include <math.h>
#include <time.h>
using namespace tgvoip;
using namespace std;
VoIPGroupController::VoIPGroupController(int32_t timeDifference){
audioMixer=new AudioMixer();
memset(&callbacks, 0, sizeof(callbacks));
userSelfID=0;
this->timeDifference=timeDifference;
LOGV("Created VoIPGroupController; timeDifference=%d", timeDifference);
}
VoIPGroupController::~VoIPGroupController(){
if(audioOutput){
audioOutput->Stop();
}
LOGD("before stop audio mixer");
audioMixer->Stop();
delete audioMixer;
for(vector<GroupCallParticipant>::iterator p=participants.begin();p!=participants.end();p++){
if(p->levelMeter)
delete p->levelMeter;
}
}
void VoIPGroupController::SetGroupCallInfo(unsigned char *encryptionKey, unsigned char *reflectorGroupTag, unsigned char *reflectorSelfTag, unsigned char *reflectorSelfSecret, unsigned char* reflectorSelfTagHash, int32_t selfUserID, NetworkAddress reflectorAddress, NetworkAddress reflectorAddressV6, uint16_t reflectorPort){
Endpoint e;
e.address=reflectorAddress;
e.v6address=reflectorAddressV6;
e.port=reflectorPort;
memcpy(e.peerTag, reflectorGroupTag, 16);
e.type=Endpoint::Type::UDP_RELAY;
e.id=FOURCC('G','R','P','R');
endpoints[e.id]=e;
groupReflector=e;
currentEndpoint=e.id;
memcpy(this->encryptionKey, encryptionKey, 256);
memcpy(this->reflectorSelfTag, reflectorSelfTag, 16);
memcpy(this->reflectorSelfSecret, reflectorSelfSecret, 16);
memcpy(this->reflectorSelfTagHash, reflectorSelfTagHash, 16);
uint8_t sha256[SHA256_LENGTH];
crypto.sha256((uint8_t*) encryptionKey, 256, sha256);
memcpy(callID, sha256+(SHA256_LENGTH-16), 16);
memcpy(keyFingerprint, sha256+(SHA256_LENGTH-16), 8);
this->userSelfID=selfUserID;
//LOGD("reflectorSelfTag = %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", reflectorSelfTag[0], reflectorSelfTag[1], reflectorSelfTag[2], reflectorSelfTag[3], reflectorSelfTag[4], reflectorSelfTag[5], reflectorSelfTag[6], reflectorSelfTag[7], reflectorSelfTag[8], reflectorSelfTag[9], reflectorSelfTag[10], reflectorSelfTag[11], reflectorSelfTag[12], reflectorSelfTag[13], reflectorSelfTag[14], reflectorSelfTag[15]);
//LOGD("reflectorSelfSecret = %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", reflectorSelfSecret[0], reflectorSelfSecret[1], reflectorSelfSecret[2], reflectorSelfSecret[3], reflectorSelfSecret[4], reflectorSelfSecret[5], reflectorSelfSecret[6], reflectorSelfSecret[7], reflectorSelfSecret[8], reflectorSelfSecret[9], reflectorSelfSecret[10], reflectorSelfSecret[11], reflectorSelfSecret[12], reflectorSelfSecret[13], reflectorSelfSecret[14], reflectorSelfSecret[15]);
//LOGD("reflectorSelfTagHash = %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", reflectorSelfTagHash[0], reflectorSelfTagHash[1], reflectorSelfTagHash[2], reflectorSelfTagHash[3], reflectorSelfTagHash[4], reflectorSelfTagHash[5], reflectorSelfTagHash[6], reflectorSelfTagHash[7], reflectorSelfTagHash[8], reflectorSelfTagHash[9], reflectorSelfTagHash[10], reflectorSelfTagHash[11], reflectorSelfTagHash[12], reflectorSelfTagHash[13], reflectorSelfTagHash[14], reflectorSelfTagHash[15]);
}
void VoIPGroupController::AddGroupCallParticipant(int32_t userID, unsigned char *memberTagHash, unsigned char* serializedStreams, size_t streamsLength){
if(userID==userSelfID)
return;
if(userSelfID==0)
return;
//if(streamsLength==0)
// return;
MutexGuard m(participantsMutex);
LOGV("Adding group call user %d, streams length %u", userID, (unsigned int)streamsLength);
for(vector<GroupCallParticipant>::iterator p=participants.begin();p!=participants.end();++p){
if(p->userID==userID){
LOGE("user %d already added", userID);
abort();
break;
}
}
GroupCallParticipant p;
p.userID=userID;
memcpy(p.memberTagHash, memberTagHash, sizeof(p.memberTagHash));
p.levelMeter=new AudioLevelMeter();
BufferInputStream ss(serializedStreams, streamsLength);
vector<shared_ptr<Stream>> streams=DeserializeStreams(ss);
unsigned char audioStreamID=0;
for(vector<shared_ptr<Stream>>::iterator _s=streams.begin();_s!=streams.end();++_s){
shared_ptr<Stream>& s=*_s;
s->userID=userID;
if(s->type==STREAM_TYPE_AUDIO && s->codec==CODEC_OPUS && !audioStreamID){
audioStreamID=s->id;
s->jitterBuffer=make_shared<JitterBuffer>(nullptr, s->frameDuration);
if(s->frameDuration>50)
s->jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_60", 2));
else if(s->frameDuration>30)
s->jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_40", 4));
else
s->jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_20", 6));
s->callbackWrapper=make_shared<CallbackWrapper>();
s->decoder=make_shared<OpusDecoder>(s->callbackWrapper, false, false);
s->decoder->SetJitterBuffer(s->jitterBuffer);
s->decoder->SetFrameDuration(s->frameDuration);
s->decoder->SetDTX(true);
s->decoder->SetLevelMeter(p.levelMeter);
audioMixer->AddInput(s->callbackWrapper);
}
incomingStreams.push_back(s);
}
if(!audioStreamID){
LOGW("User %d has no usable audio stream", userID);
}
p.streams.insert(p.streams.end(), streams.begin(), streams.end());
participants.push_back(p);
LOGI("Added group call participant %d", userID);
}
void VoIPGroupController::RemoveGroupCallParticipant(int32_t userID){
MutexGuard m(participantsMutex);
vector<shared_ptr<Stream>>::iterator stm=incomingStreams.begin();
while(stm!=incomingStreams.end()){
if((*stm)->userID==userID){
LOGI("Removed stream %d belonging to user %d", (*stm)->id, userID);
audioMixer->RemoveInput((*stm)->callbackWrapper);
(*stm)->decoder->Stop();
//delete (*stm)->decoder;
//delete (*stm)->jitterBuffer;
//delete (*stm)->callbackWrapper;
stm=incomingStreams.erase(stm);
continue;
}
++stm;
}
for(vector<GroupCallParticipant>::iterator p=participants.begin();p!=participants.end();++p){
if(p->userID==userID){
if(p->levelMeter)
delete p->levelMeter;
participants.erase(p);
LOGI("Removed group call participant %d", userID);
break;
}
}
}
vector<shared_ptr<VoIPController::Stream>> VoIPGroupController::DeserializeStreams(BufferInputStream& in){
vector<shared_ptr<Stream>> res;
try{
unsigned char count=in.ReadByte();
for(unsigned char i=0;i<count;i++){
uint16_t len=(uint16_t) in.ReadInt16();
BufferInputStream inner=in.GetPartBuffer(len, true);
shared_ptr<Stream> s=make_shared<Stream>();
s->id=inner.ReadByte();
s->type=inner.ReadByte();
s->codec=(uint32_t) inner.ReadInt32();
uint32_t flags=(uint32_t) inner.ReadInt32();
s->enabled=(flags & STREAM_FLAG_ENABLED)==STREAM_FLAG_ENABLED;
s->frameDuration=(uint16_t) inner.ReadInt16();
res.push_back(s);
}
}catch(out_of_range& x){
LOGW("Error deserializing streams: %s", x.what());
}
return res;
}
void VoIPGroupController::SetParticipantStreams(int32_t userID, unsigned char *serializedStreams, size_t length){
LOGD("Set participant streams for %d", userID);
MutexGuard m(participantsMutex);
for(vector<GroupCallParticipant>::iterator p=participants.begin();p!=participants.end();++p){
if(p->userID==userID){
BufferInputStream in(serializedStreams, length);
vector<shared_ptr<Stream>> streams=DeserializeStreams(in);
for(vector<shared_ptr<Stream>>::iterator ns=streams.begin();ns!=streams.end();++ns){
bool found=false;
for(vector<shared_ptr<Stream>>::iterator s=p->streams.begin();s!=p->streams.end();++s){
if((*s)->id==(*ns)->id){
(*s)->enabled=(*ns)->enabled;
if(groupCallbacks.participantAudioStateChanged)
groupCallbacks.participantAudioStateChanged(this, userID, (*s)->enabled);
found=true;
break;
}
}
if(!found){
LOGW("Tried to add stream %d for user %d but adding/removing streams is not supported", (*ns)->id, userID);
}
}
break;
}
}
}
size_t VoIPGroupController::GetInitialStreams(unsigned char *buf, size_t size){
BufferOutputStream s(buf, size);
s.WriteByte(1); // streams count
s.WriteInt16(12); // this object length
s.WriteByte(1); // stream id
s.WriteByte(STREAM_TYPE_AUDIO);
s.WriteInt32(CODEC_OPUS);
s.WriteInt32(STREAM_FLAG_ENABLED | STREAM_FLAG_DTX); // flags
s.WriteInt16(60); // frame duration
return s.GetLength();
}
void VoIPGroupController::SendInit(){
SendRecentPacketsRequest();
}
void VoIPGroupController::ProcessIncomingPacket(NetworkPacket &packet, Endpoint& srcEndpoint){
//LOGD("Received incoming packet from %s:%u, %u bytes", packet.address->ToString().c_str(), packet.port, packet.length);
/*if(packet.length<17 || packet.length>2000){
LOGW("Received packet has wrong length %d", (int)packet.length);
return;
}
BufferOutputStream sigData(packet.length);
sigData.WriteBytes(packet.data, packet.length-16);
sigData.WriteBytes(reflectorSelfSecret, 16);
unsigned char sig[32];
crypto.sha256(sigData.GetBuffer(), sigData.GetLength(), sig);
if(memcmp(sig, packet.data+(packet.length-16), 16)!=0){
LOGW("Received packet has incorrect signature");
return;
}
// reflector special response
if(memcmp(packet.data, reflectorSelfTagHash, 16)==0 && packet.length>60){
//LOGI("possible reflector special response");
unsigned char firstBlock[16];
unsigned char iv[16];
memcpy(iv, packet.data+16, 16);
unsigned char key[32];
crypto.sha256(reflectorSelfSecret, 16, key);
crypto.aes_cbc_decrypt(packet.data+32, firstBlock, 16, key, iv);
BufferInputStream in(firstBlock, 16);
in.Seek(8);
size_t len=(size_t) in.ReadInt32();
int32_t tlid=in.ReadInt32();
//LOGD("special response: len=%d, tlid=0x%08X", len, tlid);
if(len%4==0 && len+60<=packet.length && packet.length<=1500){
lastRecvPacketTime=GetCurrentTime();
memcpy(iv, packet.data+16, 16);
unsigned char buf[1500];
crypto.aes_cbc_decrypt(packet.data+32, buf, len+16, key, iv);
try{
if(tlid==TLID_UDP_REFLECTOR_LAST_PACKETS_INFO){
MutexGuard m(sentPacketsMutex);
//LOGV("received udpReflector.lastPacketsInfo");
in=BufferInputStream(buf, len+16);
in.Seek(16);
/*int32_t date=* /in.ReadInt32();
/*int64_t queryID=* /in.ReadInt64();
int32_t vectorMagic=in.ReadInt32();
if(vectorMagic!=TLID_VECTOR){
LOGW("last packets info: expected vector, got %08X", vectorMagic);
return;
}
int32_t recvCount=in.ReadInt32();
//LOGV("%d received packets", recvCount);
for(int i=0;i<recvCount;i++){
uint32_t p=(uint32_t) in.ReadInt32();
//LOGV("Relay received packet: %08X", p);
uint16_t id=(uint16_t) (p & 0xFFFF);
//LOGV("ack id %04X", id);
for(vector<PacketIdMapping>::iterator pkt=recentSentPackets.begin();pkt!=recentSentPackets.end();++pkt){
//LOGV("== sent id %04X", pkt->id);
if(pkt->id==id){
if(!pkt->ackTime){
pkt->ackTime=GetCurrentTime();
conctl->PacketAcknowledged(pkt->seq);
//LOGV("relay acknowledged packet %u", pkt->seq);
if(seqgt(pkt->seq, lastRemoteAckSeq))
lastRemoteAckSeq=pkt->seq;
}
break;
}
}
}
vectorMagic=in.ReadInt32();
if(vectorMagic!=TLID_VECTOR){
LOGW("last packets info: expected vector, got %08X", vectorMagic);
return;
}
int32_t sentCount=in.ReadInt32();
//LOGV("%d sent packets", sentCount);
for(int i=0;i<sentCount;i++){
/*int32_t p=* /in.ReadInt32();
//LOGV("Sent packet: %08X", p);
}
if(udpConnectivityState!=UDP_AVAILABLE)
udpConnectivityState=UDP_AVAILABLE;
if(state!=STATE_ESTABLISHED)
SetState(STATE_ESTABLISHED);
if(!audioInput){
InitializeAudio();
if(state!=STATE_FAILED){
// audioOutput->Start();
}
}
}
}catch(out_of_range& x){
LOGE("Error parsing special response: %s", x.what());
}
return;
}
}
if(packet.length<32)
return;
// it's a packet relayed from another participant - find the sender
MutexGuard m(participantsMutex);
GroupCallParticipant* sender=NULL;
for(vector<GroupCallParticipant>::iterator p=participants.begin();p!=participants.end();++p){
if(memcmp(packet.data, p->memberTagHash, 16)==0){
//LOGV("received data packet from user %d", p->userID);
sender=&*p;
break;
}
}
if(!sender){
LOGV("Received data packet is from unknown user");
return;
}
if(memcmp(packet.data+16, keyFingerprint, 8)!=0){
LOGW("received packet has wrong key fingerprint");
return;
}
BufferInputStream in(packet.data, packet.length-16);
in.Seek(16+8); // peer tag + key fingerprint
unsigned char msgKey[16];
in.ReadBytes(msgKey, 16);
unsigned char decrypted[1500];
unsigned char aesKey[32], aesIv[32];
KDF2(msgKey, 0, aesKey, aesIv);
size_t decryptedLen=in.Remaining()-16;
if(decryptedLen>sizeof(decrypted))
return;
//LOGV("-> MSG KEY: %08x %08x %08x %08x, hashed %u", *reinterpret_cast<int32_t*>(msgKey), *reinterpret_cast<int32_t*>(msgKey+4), *reinterpret_cast<int32_t*>(msgKey+8), *reinterpret_cast<int32_t*>(msgKey+12), decryptedLen-4);
uint8_t *decryptOffset = packet.data + in.GetOffset();
if ((((intptr_t)decryptOffset) % sizeof(long)) != 0) {
LOGE("alignment2 packet.data+in.GetOffset()");
}
if (decryptedLen % sizeof(long) != 0) {
LOGE("alignment2 decryptedLen");
}
crypto.aes_ige_decrypt(packet.data+in.GetOffset(), decrypted, decryptedLen, aesKey, aesIv);
in=BufferInputStream(decrypted, decryptedLen);
//LOGD("received packet length: %d", in.ReadInt32());
BufferOutputStream buf(decryptedLen+32);
size_t x=0;
buf.WriteBytes(encryptionKey+88+x, 32);
buf.WriteBytes(decrypted+4, decryptedLen-4);
unsigned char msgKeyLarge[32];
crypto.sha256(buf.GetBuffer(), buf.GetLength(), msgKeyLarge);
if(memcmp(msgKey, msgKeyLarge+8, 16)!=0){
LOGW("Received packet from user %d has wrong hash", sender->userID);
return;
}
uint32_t innerLen=(uint32_t) in.ReadInt32();
if(innerLen>decryptedLen-4){
LOGW("Received packet has wrong inner length (%d with total of %u)", (int)innerLen, (unsigned int)decryptedLen);
return;
}
if(decryptedLen-innerLen<12){
LOGW("Received packet has too little padding (%u)", (unsigned int)(decryptedLen-innerLen));
return;
}
in=BufferInputStream(decrypted+4, (size_t) innerLen);
uint32_t tlid=(uint32_t) in.ReadInt32();
if(tlid!=TLID_DECRYPTED_AUDIO_BLOCK){
LOGW("Received packet has unknown TL ID 0x%08x", tlid);
return;
}
in.Seek(in.GetOffset()+16); // random bytes
int32_t flags=in.ReadInt32();
if(!(flags & PFLAG_HAS_SEQ) || !(flags & PFLAG_HAS_SENDER_TAG_HASH)){
LOGW("Received packet has wrong flags");
return;
}
/*uint32_t seq=(uint32_t) * /in.ReadInt32();
unsigned char senderTagHash[16];
in.ReadBytes(senderTagHash, 16);
if(memcmp(senderTagHash, sender->memberTagHash, 16)!=0){
LOGW("Received packet has wrong inner sender tag hash");
return;
}
//int32_t oneMoreInnerLengthWhyDoWeEvenNeedThis;
if(flags & PFLAG_HAS_DATA){
/*oneMoreInnerLengthWhyDoWeEvenNeedThis=* /in.ReadTlLength();
}
unsigned char type=(unsigned char) ((flags >> 24) & 0xFF);
lastRecvPacketTime=GetCurrentTime();
if(type==PKT_STREAM_DATA || type==PKT_STREAM_DATA_X2 || type==PKT_STREAM_DATA_X3){
if(state!=STATE_ESTABLISHED && receivedInitAck)
SetState(STATE_ESTABLISHED);
int count;
switch(type){
case PKT_STREAM_DATA_X2:
count=2;
break;
case PKT_STREAM_DATA_X3:
count=3;
break;
case PKT_STREAM_DATA:
default:
count=1;
break;
}
int i;
//if(srcEndpoint->type==Endpoint::Type::UDP_RELAY && srcEndpoint!=peerPreferredRelay){
// peerPreferredRelay=srcEndpoint;
//}
for(i=0;i<count;i++){
unsigned char streamID=in.ReadByte();
unsigned char sflags=(unsigned char) (streamID & 0xC0);
uint16_t sdlen=(uint16_t) (sflags & STREAM_DATA_FLAG_LEN16 ? in.ReadInt16() : in.ReadByte());
uint32_t pts=(uint32_t) in.ReadInt32();
//LOGD("stream data, pts=%d, len=%d, rem=%d", pts, sdlen, in.Remaining());
audioTimestampIn=pts;
/*if(!audioOutStarted && audioOutput){
audioOutput->Start();
audioOutStarted=true;
}* /
if(in.GetOffset()+sdlen>in.GetLength()){
return;
}
for(vector<shared_ptr<Stream>>::iterator stm=sender->streams.begin();stm!=sender->streams.end();++stm){
if((*stm)->id==streamID){
if((*stm)->jitterBuffer){
(*stm)->jitterBuffer->HandleInput(decrypted+4+in.GetOffset(), sdlen, pts, false);
}
break;
}
}
if(i<count-1)
in.Seek(in.GetOffset()+sdlen);
}
}*/
}
void VoIPGroupController::SendUdpPing(Endpoint& endpoint){
}
void VoIPGroupController::SetNetworkType(int type){
networkType=type;
UpdateDataSavingState();
UpdateAudioBitrateLimit();
string itfName=udpSocket->GetLocalInterfaceInfo(NULL, NULL);
if(itfName!=activeNetItfName){
udpSocket->OnActiveInterfaceChanged();
LOGI("Active network interface changed: %s -> %s", activeNetItfName.c_str(), itfName.c_str());
bool isFirstChange=activeNetItfName.length()==0;
activeNetItfName=itfName;
if(isFirstChange)
return;
udpConnectivityState=UDP_UNKNOWN;
udpPingCount=0;
lastUdpPingTime=0;
if(proxyProtocol==PROXY_SOCKS5)
InitUDPProxy();
selectCanceller->CancelSelect();
}
}
void VoIPGroupController::SendRecentPacketsRequest(){
BufferOutputStream out(1024);
out.WriteInt32(TLID_UDP_REFLECTOR_REQUEST_PACKETS_INFO); // TL function
out.WriteInt32(GetCurrentUnixtime()); // date:int
out.WriteInt64(0); // query_id:long
out.WriteInt32(64); // recv_num:int
out.WriteInt32(0); // sent_num:int
SendSpecialReflectorRequest(out.GetBuffer(), out.GetLength());
}
void VoIPGroupController::SendSpecialReflectorRequest(unsigned char *data, size_t len){
/*BufferOutputStream out(1024);
unsigned char buf[1500];
crypto.rand_bytes(buf, 8);
out.WriteBytes(buf, 8);
out.WriteInt32((int32_t)len);
out.WriteBytes(data, len);
if(out.GetLength()%16!=0){
size_t paddingLen=16-(out.GetLength()%16);
crypto.rand_bytes(buf, paddingLen);
out.WriteBytes(buf, paddingLen);
}
unsigned char iv[16];
crypto.rand_bytes(iv, 16);
unsigned char key[32];
crypto.sha256(reflectorSelfSecret, 16, key);
unsigned char _iv[16];
memcpy(_iv, iv, 16);
size_t encryptedLen=out.GetLength();
crypto.aes_cbc_encrypt(out.GetBuffer(), buf, encryptedLen, key, _iv);
out.Reset();
out.WriteBytes(reflectorSelfTag, 16);
out.WriteBytes(iv, 16);
out.WriteBytes(buf, encryptedLen);
out.WriteBytes(reflectorSelfSecret, 16);
crypto.sha256(out.GetBuffer(), out.GetLength(), buf);
out.Rewind(16);
out.WriteBytes(buf, 16);
NetworkPacket pkt={0};
pkt.address=&groupReflector.address;
pkt.port=groupReflector.port;
pkt.protocol=PROTO_UDP;
pkt.data=out.GetBuffer();
pkt.length=out.GetLength();
ActuallySendPacket(pkt, groupReflector);*/
}
void VoIPGroupController::SendRelayPings(){
//LOGV("Send relay pings 2");
double currentTime=GetCurrentTime();
if(currentTime-groupReflector.lastPingTime>=0.25){
SendRecentPacketsRequest();
groupReflector.lastPingTime=currentTime;
}
}
void VoIPGroupController::OnAudioOutputReady(){
encoder->SetDTX(true);
audioMixer->SetOutput(audioOutput);
audioMixer->SetEchoCanceller(echoCanceller);
audioMixer->Start();
audioOutput->Start();
audioOutStarted=true;
encoder->SetLevelMeter(&selfLevelMeter);
}
void VoIPGroupController::WritePacketHeader(uint32_t seq, BufferOutputStream *s, unsigned char type, uint32_t length, PacketSender* source){
s->WriteInt32(TLID_DECRYPTED_AUDIO_BLOCK);
int64_t randomID;
crypto.rand_bytes((uint8_t *) &randomID, 8);
s->WriteInt64(randomID);
unsigned char randBytes[7];
crypto.rand_bytes(randBytes, 7);
s->WriteByte(7);
s->WriteBytes(randBytes, 7);
uint32_t pflags=PFLAG_HAS_SEQ | PFLAG_HAS_SENDER_TAG_HASH;
if(length>0)
pflags|=PFLAG_HAS_DATA;
pflags|=((uint32_t) type) << 24;
s->WriteInt32(pflags);
if(type==PKT_STREAM_DATA || type==PKT_STREAM_DATA_X2 || type==PKT_STREAM_DATA_X3){
conctl->PacketSent(seq, length);
}
/*if(pflags & PFLAG_HAS_CALL_ID){
s->WriteBytes(callID, 16);
}*/
//s->WriteInt32(lastRemoteSeq);
s->WriteInt32(seq);
s->WriteBytes(reflectorSelfTagHash, 16);
if(length>0){
if(length<=253){
s->WriteByte((unsigned char) length);
}else{
s->WriteByte(254);
s->WriteByte((unsigned char) (length & 0xFF));
s->WriteByte((unsigned char) ((length >> 8) & 0xFF));
s->WriteByte((unsigned char) ((length >> 16) & 0xFF));
}
}
}
void VoIPGroupController::SendPacket(unsigned char *data, size_t len, Endpoint& ep, PendingOutgoingPacket& srcPacket){
if(stopping)
return;
if(ep.type==Endpoint::Type::TCP_RELAY && !useTCP)
return;
BufferOutputStream out(len+128);
//LOGV("send group packet %u", len);
out.WriteBytes(reflectorSelfTag, 16);
if(len>0){
BufferOutputStream inner(len+128);
inner.WriteInt32((uint32_t)len);
inner.WriteBytes(data, len);
size_t padLen=16-inner.GetLength()%16;
if(padLen<12)
padLen+=16;
unsigned char padding[28];
crypto.rand_bytes((uint8_t *) padding, padLen);
inner.WriteBytes(padding, padLen);
assert(inner.GetLength()%16==0);
unsigned char key[32], iv[32], msgKey[16];
out.WriteBytes(keyFingerprint, 8);
BufferOutputStream buf(len+32);
size_t x=0;
buf.WriteBytes(encryptionKey+88+x, 32);
buf.WriteBytes(inner.GetBuffer()+4, inner.GetLength()-4);
unsigned char msgKeyLarge[32];
crypto.sha256(buf.GetBuffer(), buf.GetLength(), msgKeyLarge);
memcpy(msgKey, msgKeyLarge+8, 16);
KDF2(msgKey, 0, key, iv);
out.WriteBytes(msgKey, 16);
//LOGV("<- MSG KEY: %08x %08x %08x %08x, hashed %u", *reinterpret_cast<int32_t*>(msgKey), *reinterpret_cast<int32_t*>(msgKey+4), *reinterpret_cast<int32_t*>(msgKey+8), *reinterpret_cast<int32_t*>(msgKey+12), inner.GetLength()-4);
unsigned char aesOut[MSC_STACK_FALLBACK(inner.GetLength(), 1500)];
crypto.aes_ige_encrypt(inner.GetBuffer(), aesOut, inner.GetLength(), key, iv);
out.WriteBytes(aesOut, inner.GetLength());
}
// relay signature
out.WriteBytes(reflectorSelfSecret, 16);
unsigned char sig[32];
crypto.sha256(out.GetBuffer(), out.GetLength(), sig);
out.Rewind(16);
out.WriteBytes(sig, 16);
if(srcPacket.type==PKT_STREAM_DATA || srcPacket.type==PKT_STREAM_DATA_X2 || srcPacket.type==PKT_STREAM_DATA_X3){
PacketIdMapping mapping={srcPacket.seq, *reinterpret_cast<uint16_t*>(sig+14), 0};
MutexGuard m(sentPacketsMutex);
recentSentPackets.push_back(mapping);
//LOGD("sent packet with id: %04X", mapping.id);
while(recentSentPackets.size()>64)
recentSentPackets.erase(recentSentPackets.begin());
}
lastSentSeq=srcPacket.seq;
if(IS_MOBILE_NETWORK(networkType))
stats.bytesSentMobile+=(uint64_t)out.GetLength();
else
stats.bytesSentWifi+=(uint64_t)out.GetLength();
/*NetworkPacket pkt={0};
pkt.address=(NetworkAddress*)&ep.address;
pkt.port=ep.port;
pkt.length=out.GetLength();
pkt.data=out.GetBuffer();
pkt.protocol=ep.type==Endpoint::Type::TCP_RELAY ? PROTO_TCP : PROTO_UDP;
ActuallySendPacket(pkt, ep);*/
}
void VoIPGroupController::SetCallbacks(VoIPGroupController::Callbacks callbacks){
VoIPController::SetCallbacks(callbacks);
this->groupCallbacks=callbacks;
}
int32_t VoIPGroupController::GetCurrentUnixtime(){
return time(NULL)+timeDifference;
}
float VoIPGroupController::GetParticipantAudioLevel(int32_t userID){
if(userID==userSelfID)
return selfLevelMeter.GetLevel();
MutexGuard m(participantsMutex);
for(vector<GroupCallParticipant>::iterator p=participants.begin(); p!=participants.end(); ++p){
if(p->userID==userID){
return p->levelMeter->GetLevel();
}
}
return 0;
}
void VoIPGroupController::SetMicMute(bool mute){
micMuted=mute;
if(audioInput){
if(mute)
audioInput->Stop();
else
audioInput->Start();
if(!audioInput->IsInitialized()){
lastError=ERROR_AUDIO_IO;
SetState(STATE_FAILED);
return;
}
}
outgoingStreams[0]->enabled=!mute;
SerializeAndUpdateOutgoingStreams();
}
void VoIPGroupController::SetParticipantVolume(int32_t userID, float volume){
MutexGuard m(participantsMutex);
for(vector<GroupCallParticipant>::iterator p=participants.begin();p!=participants.end();++p){
if(p->userID==userID){
for(vector<shared_ptr<Stream>>::iterator s=p->streams.begin();s!=p->streams.end();++s){
if((*s)->type==STREAM_TYPE_AUDIO){
if((*s)->decoder){
float db;
if(volume==0.0f)
db=-INFINITY;
else if(volume<1.0f)
db=-50.0f*(1.0f-volume);
else if(volume>1.0f && volume<=2.0f)
db=10.0f*(volume-1.0f);
else
db=0.0f;
//LOGV("Setting user %u audio volume to %.2f dB", userID, db);
audioMixer->SetInputVolume((*s)->callbackWrapper, db);
}
break;
}
}
break;
}
}
}
void VoIPGroupController::SerializeAndUpdateOutgoingStreams(){
BufferOutputStream out(1024);
out.WriteByte((unsigned char) outgoingStreams.size());
for(vector<shared_ptr<Stream>>::iterator s=outgoingStreams.begin(); s!=outgoingStreams.end(); ++s){
BufferOutputStream o(128);
o.WriteByte((*s)->id);
o.WriteByte((*s)->type);
o.WriteInt32((*s)->codec);
o.WriteInt32((unsigned char) (((*s)->enabled ? STREAM_FLAG_ENABLED : 0) | STREAM_FLAG_DTX));
o.WriteInt16((*s)->frameDuration);
out.WriteInt16((int16_t) o.GetLength());
out.WriteBytes(o.GetBuffer(), o.GetLength());
}
if(groupCallbacks.updateStreams)
groupCallbacks.updateStreams(this, out.GetBuffer(), out.GetLength());
}
std::string VoIPGroupController::GetDebugString(){
std::string r="Remote endpoints: \n";
char buffer[2048];
for(pair<const int64_t, Endpoint>& _endpoint:endpoints){
Endpoint& endpoint=_endpoint.second;
const char* type;
switch(endpoint.type){
case Endpoint::Type::UDP_P2P_INET:
type="UDP_P2P_INET";
break;
case Endpoint::Type::UDP_P2P_LAN:
type="UDP_P2P_LAN";
break;
case Endpoint::Type::UDP_RELAY:
type="UDP_RELAY";
break;
case Endpoint::Type::TCP_RELAY:
type="TCP_RELAY";
break;
default:
type="UNKNOWN";
break;
}
snprintf(buffer, sizeof(buffer), "%s:%u %dms [%s%s]\n", endpoint.address.ToString().c_str(), endpoint.port, (int)(endpoint.averageRTT*1000), type, currentEndpoint==endpoint.id ? ", IN_USE" : "");
r+=buffer;
}
double avgLate[3];
shared_ptr<JitterBuffer> jitterBuffer=incomingStreams.size()==1 ? incomingStreams[0]->jitterBuffer : NULL;
if(jitterBuffer)
jitterBuffer->GetAverageLateCount(avgLate);
else
memset(avgLate, 0, 3*sizeof(double));
snprintf(buffer, sizeof(buffer),
"RTT avg/min: %d/%d\n"
"Congestion window: %d/%d bytes\n"
"Key fingerprint: %02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX\n"
"Last sent/ack'd seq: %u/%u\n"
"Send/recv losses: %u/%u (%d%%)\n"
"Audio bitrate: %d kbit\n"
"Bytes sent/recvd: %llu/%llu\n\n",
(int)(conctl->GetAverageRTT()*1000), (int)(conctl->GetMinimumRTT()*1000),
int(conctl->GetInflightDataSize()), int(conctl->GetCongestionWindow()),
keyFingerprint[0],keyFingerprint[1],keyFingerprint[2],keyFingerprint[3],keyFingerprint[4],keyFingerprint[5],keyFingerprint[6],keyFingerprint[7],
lastSentSeq, lastRemoteAckSeq,
conctl->GetSendLossCount(), recvLossCount, encoder ? encoder->GetPacketLoss() : 0,
encoder ? (encoder->GetBitrate()/1000) : 0,
(long long unsigned int)(stats.bytesSentMobile+stats.bytesSentWifi),
(long long unsigned int)(stats.bytesRecvdMobile+stats.bytesRecvdWifi));
MutexGuard m(participantsMutex);
for(vector<GroupCallParticipant>::iterator p=participants.begin();p!=participants.end();++p){
snprintf(buffer, sizeof(buffer), "Participant id: %d\n", p->userID);
r+=buffer;
for(vector<shared_ptr<Stream>>::iterator stm=p->streams.begin();stm!=p->streams.end();++stm){
char* codec=reinterpret_cast<char*>(&(*stm)->codec);
snprintf(buffer, sizeof(buffer), "Stream %d (type %d, codec '%c%c%c%c', %sabled)\n",
(*stm)->id, (*stm)->type, codec[3], codec[2], codec[1], codec[0], (*stm)->enabled ? "en" : "dis");
r+=buffer;
if((*stm)->enabled){
if((*stm)->jitterBuffer){
snprintf(buffer, sizeof(buffer), "Jitter buffer: %d/%.2f\n",
(*stm)->jitterBuffer->GetMinPacketCount(), (*stm)->jitterBuffer->GetAverageDelay());
r+=buffer;
}
}
}
r+="\n";
}
return r;
}

View File

@ -0,0 +1,70 @@
//
// 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"
#include <sstream>
#include <locale>
using namespace tgvoip;
ServerConfig* ServerConfig::sharedInstance=NULL;
ServerConfig::ServerConfig(){
}
ServerConfig::~ServerConfig(){
}
ServerConfig *ServerConfig::GetSharedInstance(){
if(!sharedInstance)
sharedInstance=new ServerConfig();
return sharedInstance;
}
bool ServerConfig::GetBoolean(std::string name, bool fallback){
MutexGuard sync(mutex);
if(ContainsKey(name) && config[name].is_bool())
return config[name].bool_value();
return fallback;
}
double ServerConfig::GetDouble(std::string name, double fallback){
MutexGuard sync(mutex);
if(ContainsKey(name) && config[name].is_number())
return config[name].number_value();
return fallback;
}
int32_t ServerConfig::GetInt(std::string name, int32_t fallback){
MutexGuard sync(mutex);
if(ContainsKey(name) && config[name].is_number())
return config[name].int_value();
return fallback;
}
std::string ServerConfig::GetString(std::string name, std::string fallback){
MutexGuard sync(mutex);
if(ContainsKey(name) && config[name].is_string())
return config[name].string_value();
return fallback;
}
void ServerConfig::Update(std::string jsonString){
MutexGuard sync(mutex);
LOGD("=== Updating voip config ===");
LOGD("%s", jsonString.c_str());
std::string jsonError;
config=json11::Json::parse(jsonString, jsonError);
if(!jsonError.empty())
LOGE("Error parsing server config: %s", jsonError.c_str());
}
bool ServerConfig::ContainsKey(std::string key){
return config.object_items().find(key)!=config.object_items().end();
}

View File

@ -0,0 +1,37 @@
//
// libtgvoip is free and unencumbered public domain software.
// For more information, see http://unlicense.org or the UNLICENSE file
// you should have received with this source code distribution.
//
#ifndef TGVOIP_VOIPSERVERCONFIG_H
#define TGVOIP_VOIPSERVERCONFIG_H
#include <map>
#include <string>
#include <stdint.h>
#include "threading.h"
#include "json11.hpp"
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::string jsonString);
private:
static ServerConfig* sharedInstance;
bool ContainsKey(std::string key);
json11::Json config;
Mutex mutex;
};
}
#endif //TGVOIP_VOIPSERVERCONFIG_H

10209
TMessagesProj/jni/libtgvoip2/aclocal.m4 vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,92 @@
//
// 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 "AudioIO.h"
#include "../logging.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
#include "AudioIOCallback.h"
#elif defined(__ANDROID__)
#include "../os/android/AudioInputAndroid.h"
#include "../os/android/AudioOutputAndroid.h"
#elif defined(__APPLE__)
#include <TargetConditionals.h>
#include "../os/darwin/AudioUnitIO.h"
#if TARGET_OS_OSX
#include "../os/darwin/AudioInputAudioUnitOSX.h"
#include "../os/darwin/AudioOutputAudioUnitOSX.h"
#endif
#elif defined(_WIN32)
#ifdef TGVOIP_WINXP_COMPAT
#include "../os/windows/AudioInputWave.h"
#include "../os/windows/AudioOutputWave.h"
#endif
#include "../os/windows/AudioInputWASAPI.h"
#include "../os/windows/AudioOutputWASAPI.h"
#elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__gnu_hurd__)
#ifndef WITHOUT_ALSA
#include "../os/linux/AudioInputALSA.h"
#include "../os/linux/AudioOutputALSA.h"
#endif
#ifndef WITHOUT_PULSE
#include "../os/linux/AudioPulse.h"
#endif
#else
#error "Unsupported operating system"
#endif
using namespace tgvoip;
using namespace tgvoip::audio;
using namespace std;
AudioIO* AudioIO::Create(std::string inputDevice, std::string outputDevice){
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
return new AudioIOCallback();
#elif defined(__ANDROID__)
return new ContextlessAudioIO<AudioInputAndroid, AudioOutputAndroid>();
#elif defined(__APPLE__)
#if TARGET_OS_OSX
if(kCFCoreFoundationVersionNumber<kCFCoreFoundationVersionNumber10_7)
return new ContextlessAudioIO<AudioInputAudioUnitLegacy, AudioOutputAudioUnitLegacy>(inputDevice, outputDevice);
#endif
return new AudioUnitIO(inputDevice, outputDevice);
#elif defined(_WIN32)
#ifdef TGVOIP_WINXP_COMPAT
if(LOBYTE(LOWORD(GetVersion()))<6)
return new ContextlessAudioIO<AudioInputWave, AudioOutputWave>(inputDevice, outputDevice);
#endif
return new ContextlessAudioIO<AudioInputWASAPI, AudioOutputWASAPI>(inputDevice, outputDevice);
#elif defined(__linux__)
#ifndef WITHOUT_ALSA
#ifndef WITHOUT_PULSE
if(AudioPulse::Load()){
AudioIO* io=new AudioPulse(inputDevice, outputDevice);
if(!io->Failed() && io->GetInput()->IsInitialized() && io->GetOutput()->IsInitialized())
return io;
LOGW("PulseAudio available but not working; trying ALSA");
delete io;
}
#endif
return new ContextlessAudioIO<AudioInputALSA, AudioOutputALSA>(inputDevice, outputDevice);
#else
return new AudioPulse(inputDevice, outputDevice);
#endif
#endif
}
bool AudioIO::Failed(){
return failed;
}
std::string AudioIO::GetErrorDescription(){
return error;
}

View File

@ -0,0 +1,65 @@
//
// 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_AUDIOIO_H
#define LIBTGVOIP_AUDIOIO_H
#include "AudioInput.h"
#include "AudioOutput.h"
#include "../utils.h"
#include <memory>
#include <string>
namespace tgvoip{
namespace audio {
class AudioIO{
public:
AudioIO(){};
virtual ~AudioIO(){};
TGVOIP_DISALLOW_COPY_AND_ASSIGN(AudioIO);
static AudioIO* Create(std::string inputDevice, std::string outputDevice);
virtual AudioInput* GetInput()=0;
virtual AudioOutput* GetOutput()=0;
bool Failed();
std::string GetErrorDescription();
protected:
bool failed=false;
std::string error;
};
template<class I, class O> class ContextlessAudioIO : public AudioIO{
public:
ContextlessAudioIO(){
input=new I();
output=new O();
}
ContextlessAudioIO(std::string inputDeviceID, std::string outputDeviceID){
input=new I(inputDeviceID);
output=new O(outputDeviceID);
}
virtual ~ContextlessAudioIO(){
delete input;
delete output;
}
virtual AudioInput* GetInput(){
return input;
}
virtual AudioOutput* GetOutput(){
return output;
}
private:
I* input;
O* output;
};
}
}
#endif //LIBTGVOIP_AUDIOIO_H

View File

@ -0,0 +1,121 @@
//
// libtgvoip is free and unencumbered public domain software.
// For more information, see http://unlicense.org or the UNLICENSE file
// you should have received with this source code distribution.
//
#include "AudioIOCallback.h"
#include "../VoIPController.h"
#include "../logging.h"
using namespace tgvoip;
using namespace tgvoip::audio;
#pragma mark - IO
AudioIOCallback::AudioIOCallback(){
input=new AudioInputCallback();
output=new AudioOutputCallback();
}
AudioIOCallback::~AudioIOCallback(){
delete input;
delete output;
}
AudioInput* AudioIOCallback::GetInput(){
return input;
}
AudioOutput* AudioIOCallback::GetOutput(){
return output;
}
#pragma mark - Input
AudioInputCallback::AudioInputCallback(){
thread=new Thread(std::bind(&AudioInputCallback::RunThread, this));
thread->SetName("AudioInputCallback");
}
AudioInputCallback::~AudioInputCallback(){
running=false;
thread->Join();
delete thread;
}
void AudioInputCallback::Start(){
if(!running){
running=true;
thread->Start();
}
recording=true;
}
void AudioInputCallback::Stop(){
recording=false;
}
void AudioInputCallback::SetDataCallback(std::function<void(int16_t*, size_t)> c){
dataCallback=c;
}
void AudioInputCallback::RunThread(){
int16_t buf[960];
while(running){
double t=VoIPController::GetCurrentTime();
memset(buf, 0, sizeof(buf));
dataCallback(buf, 960);
InvokeCallback(reinterpret_cast<unsigned char*>(buf), 960*2);
double sl=0.02-(VoIPController::GetCurrentTime()-t);
if(sl>0)
Thread::Sleep(sl);
}
}
#pragma mark - Output
AudioOutputCallback::AudioOutputCallback(){
thread=new Thread(std::bind(&AudioOutputCallback::RunThread, this));
thread->SetName("AudioOutputCallback");
}
AudioOutputCallback::~AudioOutputCallback(){
running=false;
thread->Join();
delete thread;
}
void AudioOutputCallback::Start(){
if(!running){
running=true;
thread->Start();
}
playing=true;
}
void AudioOutputCallback::Stop(){
playing=false;
}
bool AudioOutputCallback::IsPlaying(){
return playing;
}
void AudioOutputCallback::SetDataCallback(std::function<void(int16_t*, size_t)> c){
dataCallback=c;
}
void AudioOutputCallback::RunThread(){
int16_t buf[960];
while(running){
double t=VoIPController::GetCurrentTime();
InvokeCallback(reinterpret_cast<unsigned char*>(buf), 960*2);
dataCallback(buf, 960);
double sl=0.02-(VoIPController::GetCurrentTime()-t);
if(sl>0)
Thread::Sleep(sl);
}
}

View File

@ -0,0 +1,63 @@
//
// 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_AUDIO_IO_CALLBACK
#define LIBTGVOIP_AUDIO_IO_CALLBACK
#include "AudioIO.h"
#include <functional>
#include <atomic>
#include "../threading.h"
namespace tgvoip{
namespace audio{
class AudioInputCallback : public AudioInput{
public:
AudioInputCallback();
virtual ~AudioInputCallback();
virtual void Start() override;
virtual void Stop() override;
void SetDataCallback(std::function<void(int16_t*, size_t)> c);
private:
void RunThread();
std::atomic<bool> running{false};
bool recording=false;
Thread* thread;
std::function<void(int16_t*, size_t)> dataCallback;
};
class AudioOutputCallback : public AudioOutput{
public:
AudioOutputCallback();
virtual ~AudioOutputCallback();
virtual void Start() override;
virtual void Stop() override;
virtual bool IsPlaying() override;
void SetDataCallback(std::function<void(int16_t*, size_t)> c);
private:
void RunThread();
std::atomic<bool> running{false};
bool playing=false;
Thread* thread;
std::function<void(int16_t*, size_t)> dataCallback;
};
class AudioIOCallback : public AudioIO{
public:
AudioIOCallback();
virtual ~AudioIOCallback();
virtual AudioInput* GetInput() override;
virtual AudioOutput* GetOutput() override;
private:
AudioInputCallback* input;
AudioOutputCallback* output;
};
}
}
#endif /* LIBTGVOIP_AUDIO_IO_CALLBACK */

View File

@ -0,0 +1,97 @@
//
// libtgvoip is free and unencumbered public domain software.
// For more information, see http://unlicense.org or the UNLICENSE file
// you should have received with this source code distribution.
//
#include "AudioInput.h"
#include "../logging.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
// nothing
#elif 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__) || defined(__FreeBSD_kernel__) || defined(__gnu_hurd__)
#ifndef WITHOUT_ALSA
#include "../os/linux/AudioInputALSA.h"
#endif
#ifndef WITHOUT_PULSE
#include "../os/linux/AudioPulse.h"
#endif
#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(){
}
bool AudioInput::IsInitialized(){
return !failed;
}
void AudioInput::EnumerateDevices(std::vector<AudioInputDevice>& devs){
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
// not supported
#elif 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 !defined(WITHOUT_PULSE) && !defined(WITHOUT_ALSA)
if(!AudioInputPulse::EnumerateDevices(devs))
AudioInputALSA::EnumerateDevices(devs);
#elif defined(WITHOUT_PULSE)
AudioInputALSA::EnumerateDevices(devs);
#else
AudioInputPulse::EnumerateDevices(devs);
#endif
#endif
}
std::string AudioInput::GetCurrentDevice(){
return currentDevice;
}
void AudioInput::SetCurrentDevice(std::string deviceID){
}
int32_t AudioInput::GetEstimatedDelay(){
return estimatedDelay;
}

View File

@ -0,0 +1,41 @@
//
// libtgvoip is free and unencumbered public domain software.
// For more information, see http://unlicense.org or the UNLICENSE file
// you should have received with this source code distribution.
//
#ifndef LIBTGVOIP_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();
bool IsInitialized();
virtual std::string GetCurrentDevice();
virtual void SetCurrentDevice(std::string deviceID);
//static AudioInput* Create(std::string deviceID, void* platformSpecific);
static void EnumerateDevices(std::vector<AudioInputDevice>& devs);
static int32_t GetEstimatedDelay();
protected:
std::string currentDevice;
bool failed;
static int32_t estimatedDelay;
};
}}
#endif //LIBTGVOIP_AUDIOINPUT_H

View File

@ -0,0 +1,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.
//
#include "AudioOutput.h"
#include "../logging.h"
#include <stdlib.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
// nothing
#elif defined(__ANDROID__)
#include "../os/android/AudioOutputOpenSLES.h"
#include "../os/android/AudioOutputAndroid.h"
#include <sys/system_properties.h>
#elif defined(__APPLE__)
#include <TargetConditionals.h>
#include "../os/darwin/AudioOutputAudioUnit.h"
#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__) || defined(__FreeBSD_kernel__) || defined(__gnu_hurd__)
#ifndef WITHOUT_ALSA
#include "../os/linux/AudioOutputALSA.h"
#endif
#ifndef WITHOUT_PULSE
#include "../os/linux/AudioOutputPulse.h"
#include "../os/linux/AudioPulse.h"
#endif
#else
#error "Unsupported operating system"
#endif
using namespace tgvoip;
using namespace tgvoip::audio;
int32_t AudioOutput::estimatedDelay=60;
AudioOutput::AudioOutput() : currentDevice("default"){
failed=false;
}
AudioOutput::AudioOutput(std::string deviceID) : currentDevice(deviceID){
failed=false;
}
AudioOutput::~AudioOutput(){
}
int32_t AudioOutput::GetEstimatedDelay(){
#if defined(__ANDROID__)
char sdkNum[PROP_VALUE_MAX];
__system_property_get("ro.build.version.sdk", sdkNum);
int systemVersion=atoi(sdkNum);
return systemVersion<21 ? 150 : 50;
#endif
return estimatedDelay;
}
void AudioOutput::EnumerateDevices(std::vector<AudioOutputDevice>& devs){
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
// not supported
#elif 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 !defined(WITHOUT_PULSE) && !defined(WITHOUT_ALSA)
if(!AudioOutputPulse::EnumerateDevices(devs))
AudioOutputALSA::EnumerateDevices(devs);
#elif defined(WITHOUT_PULSE)
AudioOutputALSA::EnumerateDevices(devs);
#else
AudioOutputPulse::EnumerateDevices(devs);
#endif
#endif
}
std::string AudioOutput::GetCurrentDevice(){
return currentDevice;
}
void AudioOutput::SetCurrentDevice(std::string deviceID){
}
bool AudioOutput::IsInitialized(){
return !failed;
}

View File

@ -0,0 +1,42 @@
//
// libtgvoip is free and unencumbered public domain software.
// For more information, see http://unlicense.org or the UNLICENSE file
// you should have received with this source code distribution.
//
#ifndef LIBTGVOIP_AUDIOOUTPUT_H
#define LIBTGVOIP_AUDIOOUTPUT_H
#include <stdint.h>
#include <string>
#include <vector>
#include <memory>
#include "../MediaStreamItf.h"
namespace tgvoip{
class AudioInputDevice;
class AudioOutputDevice;
namespace audio{
class AudioOutput : public MediaStreamItf{
public:
AudioOutput();
AudioOutput(std::string deviceID);
virtual ~AudioOutput();
virtual bool IsPlaying()=0;
static int32_t GetEstimatedDelay();
virtual std::string GetCurrentDevice();
virtual void SetCurrentDevice(std::string deviceID);
//static std::unique_ptr<AudioOutput> Create(std::string deviceID, void* platformSpecific);
static void EnumerateDevices(std::vector<AudioOutputDevice>& devs);
bool IsInitialized();
protected:
std::string currentDevice;
bool failed;
static int32_t estimatedDelay;
};
}}
#endif //LIBTGVOIP_AUDIOOUTPUT_H

View File

@ -0,0 +1,123 @@
//
// Created by Grishka on 01.04.17.
//
#include <cmath>
#include <cstring>
#include "Resampler.h"
using namespace tgvoip::audio;
static constexpr 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;
for (unsigned int offset = 0; offset < outLen; offset++) {
float offsetf = offset * 160.0f / 147.0f;
float factor = offsetf - floorf(offsetf);
to[offset] = static_cast<int16_t>(from[static_cast<size_t>(floorf(offsetf))] * (1 - factor) +
from[static_cast<size_t>(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] = static_cast<int16_t>(from[static_cast<size_t>(floorf(offsetf))] * (1 - factor) +
from[static_cast<size_t>(ceilf(offsetf))] * factor);
}
return outLen;
}
size_t Resampler::Convert(int16_t* from, int16_t* to, size_t fromLen, size_t toLen, size_t num, size_t denom) {
size_t outLen = fromLen * num / denom;
if (toLen < outLen)
outLen = toLen;
unsigned int offset;
for (offset = 0; offset < outLen; offset++) {
float offsetf = offset * static_cast<float>(denom) / static_cast<float>(num);
float factor = offsetf - floorf(offsetf);
to[offset] = static_cast<int16_t>(from[static_cast<size_t>(floorf(offsetf))] * (1 - factor) +
from[static_cast<size_t>(ceilf(offsetf))] * factor);
}
return outLen;
}
void Resampler::Rescale60To80(int16_t* in, int16_t* out) {
std::memcpy(out, in, 960 * 2);
std::memcpy(out + 960 * 3, in + 960 * 2, 960 * 2);
for (int i = 0; i < 960; i++) {
out[960 + i] = static_cast<int16_t>(((static_cast<int32_t>(in[960 + i]) * hann[959 - i]) >> 15) +
((static_cast<int32_t>(in[480 + i]) * hann[i]) >> 15));
out[1920 + i] = static_cast<int16_t>(((static_cast<int32_t>(in[960 + 480 + i]) * hann[959 - i]) >> 15) +
((static_cast<int32_t>(in[480 + 480 + i]) * hann[i]) >> 15));
}
}
void Resampler::Rescale60To40(int16_t* in, int16_t* out) {
for (int i = 0; i < 960; i++) {
out[i] = static_cast<int16_t>(((static_cast<int32_t>(in[i]) * hann[959 - i]) >> 15) +
((static_cast<int32_t>(in[480 + i]) * hann[i]) >> 15));
out[960 + i] = static_cast<int16_t>(((static_cast<int32_t>(in[1920 + i]) * hann[i]) >> 15) +
((static_cast<int32_t>(in[1440 + i]) * hann[959 - i]) >> 15));
}
}

View File

@ -0,0 +1,24 @@
//
// 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, size_t num, size_t denom);
static void Rescale60To80(int16_t* in, int16_t* out);
static void Rescale60To40(int16_t* in, int16_t* out);
};
} // namespace audio
} // namespace tgvoip
#endif //LIBTGVOIP_RESAMPLER_H

View File

@ -0,0 +1,405 @@
#include <jni.h>
#include <TgVoip.h>
#include <os/android/AudioOutputOpenSLES.h>
#include <os/android/AudioInputOpenSLES.h>
#include <os/android/JNIUtilities.h>
#include "org_telegram_messenger_voip_TgVoip.h"
using namespace tgvoip;
extern "C" int tgvoipOnJniLoad(JavaVM *vm, JNIEnv *env) {
return JNI_TRUE;
}
#pragma mark - Helpers
class JavaObject {
private:
JNIEnv *env;
jobject obj;
jclass clazz;
public:
JavaObject(JNIEnv *env, jobject obj) : JavaObject(env, obj, env->GetObjectClass(obj)) {
}
JavaObject(JNIEnv *env, jobject obj, jclass clazz) {
this->env = env;
this->obj = obj;
this->clazz = clazz;
}
jint getIntField(const char *name) {
return env->GetIntField(obj, env->GetFieldID(clazz, name, "I"));
}
jlong getLongField(const char *name) {
return env->GetLongField(obj, env->GetFieldID(clazz, name, "J"));
}
jboolean getBooleanField(const char *name) {
return env->GetBooleanField(obj, env->GetFieldID(clazz, name, "Z"));
}
jdouble getDoubleField(const char *name) {
return env->GetDoubleField(obj, env->GetFieldID(clazz, name, "D"));
}
jbyteArray getByteArrayField(const char *name) {
return (jbyteArray) env->GetObjectField(obj, env->GetFieldID(clazz, name, "[B"));
}
jstring getStringField(const char *name) {
return (jstring) env->GetObjectField(obj, env->GetFieldID(clazz, name, "Ljava/lang/String;"));
}
};
struct InstanceHolder {
TgVoip *nativeInstance;
jobject javaInstance;
};
jlong getInstanceHolderId(JNIEnv *env, jobject obj) {
return env->GetLongField(obj, env->GetFieldID(env->GetObjectClass(obj), "nativeInstanceId", "J"));
}
InstanceHolder *getInstanceHolder(JNIEnv *env, jobject obj) {
return reinterpret_cast<InstanceHolder *>(getInstanceHolderId(env, obj));
}
TgVoip *getTgVoip(JNIEnv *env, jobject obj) {
return getInstanceHolder(env, obj)->nativeInstance;
}
jint throwNewJavaException(JNIEnv *env, const char *className, const char *message) {
return env->ThrowNew(env->FindClass(className), message);
}
jint throwNewJavaIllegalArgumentException(JNIEnv *env, const char *message) {
return throwNewJavaException(env, "java/lang/IllegalStateException", message);
}
jbyteArray copyVectorToJavaByteArray(JNIEnv *env, const std::vector<uint8_t> &bytes) {
unsigned int size = bytes.size();
jbyteArray bytesArray = env->NewByteArray(size);
env->SetByteArrayRegion(bytesArray, 0, size, (jbyte *) bytes.data());
return bytesArray;
}
void readTgVoipPersistentState(const char *filePath, TgVoipPersistentState &tgVoipPersistentState) {
FILE *persistentStateFile = fopen(filePath, "r");
if (persistentStateFile) {
fseek(persistentStateFile, 0, SEEK_END);
auto len = static_cast<size_t>(ftell(persistentStateFile));
fseek(persistentStateFile, 0, SEEK_SET);
if (len < 1024 * 512 && len > 0) {
auto *buffer = static_cast<uint8_t *>(malloc(len));
fread(buffer, 1, len, persistentStateFile);
tgVoipPersistentState.value = std::vector<uint8_t>(buffer, buffer + len);
free(buffer);
}
fclose(persistentStateFile);
}
}
void saveTgVoipPersistentState(const char *filePath, const TgVoipPersistentState &tgVoipPersistentState) {
FILE *persistentStateFile = fopen(filePath, "w");
if (persistentStateFile) {
fwrite(tgVoipPersistentState.value.data(), 1, tgVoipPersistentState.value.size(), persistentStateFile);
fclose(persistentStateFile);
}
}
#pragma mark - Mappers
TgVoipNetworkType parseTgVoipNetworkType(jint networkType) {
switch (networkType) {
case org_telegram_messenger_voip_TgVoip_NET_TYPE_GPRS:
return TgVoipNetworkType::Gprs;
case org_telegram_messenger_voip_TgVoip_NET_TYPE_EDGE:
return TgVoipNetworkType::Edge;
case org_telegram_messenger_voip_TgVoip_NET_TYPE_3G:
return TgVoipNetworkType::ThirdGeneration;
case org_telegram_messenger_voip_TgVoip_NET_TYPE_HSPA:
return TgVoipNetworkType::Hspa;
case org_telegram_messenger_voip_TgVoip_NET_TYPE_LTE:
return TgVoipNetworkType::Lte;
case org_telegram_messenger_voip_TgVoip_NET_TYPE_WIFI:
return TgVoipNetworkType::WiFi;
case org_telegram_messenger_voip_TgVoip_NET_TYPE_ETHERNET:
return TgVoipNetworkType::Ethernet;
case org_telegram_messenger_voip_TgVoip_NET_TYPE_OTHER_HIGH_SPEED:
return TgVoipNetworkType::OtherHighSpeed;
case org_telegram_messenger_voip_TgVoip_NET_TYPE_OTHER_LOW_SPEED:
return TgVoipNetworkType::OtherLowSpeed;
case org_telegram_messenger_voip_TgVoip_NET_TYPE_DIALUP:
return TgVoipNetworkType::Dialup;
case org_telegram_messenger_voip_TgVoip_NET_TYPE_OTHER_MOBILE:
return TgVoipNetworkType::OtherMobile;
default:
return TgVoipNetworkType::Unknown;
}
}
TgVoipDataSaving parseTgVoipDataSaving(JNIEnv *env, jint dataSaving) {
switch (dataSaving) {
case org_telegram_messenger_voip_TgVoip_DATA_SAVING_NEVER:
return TgVoipDataSaving::Never;
case org_telegram_messenger_voip_TgVoip_DATA_SAVING_MOBILE:
return TgVoipDataSaving::Mobile;
case org_telegram_messenger_voip_TgVoip_DATA_SAVING_ALWAYS:
return TgVoipDataSaving::Always;
case org_telegram_messenger_voip_TgVoip_DATA_SAVING_ROAMING:
throwNewJavaIllegalArgumentException(env, "DATA_SAVING_ROAMING is not supported");
return TgVoipDataSaving::Never;
default:
throwNewJavaIllegalArgumentException(env, "Unknown data saving constant: " + dataSaving);
return TgVoipDataSaving::Never;
}
}
void parseTgVoipConfig(JNIEnv *env, jobject config, TgVoipConfig &tgVoipConfig) {
JavaObject configObject(env, config);
tgVoipConfig.initializationTimeout = configObject.getDoubleField("initializationTimeout");
tgVoipConfig.receiveTimeout = configObject.getDoubleField("receiveTimeout");
tgVoipConfig.dataSaving = parseTgVoipDataSaving(env, configObject.getIntField("dataSaving"));
tgVoipConfig.enableP2P = configObject.getBooleanField("enableP2p") == JNI_TRUE;
tgVoipConfig.enableAEC = configObject.getBooleanField("enableAec") == JNI_TRUE;
tgVoipConfig.enableNS = configObject.getBooleanField("enableNs") == JNI_TRUE;
tgVoipConfig.enableAGC = configObject.getBooleanField("enableAgc") == JNI_TRUE;
tgVoipConfig.enableCallUpgrade = configObject.getBooleanField("enableCallUpgrade") == JNI_TRUE;
tgVoipConfig.logPath = jni::JavaStringToStdString(env, configObject.getStringField("logPath"));
tgVoipConfig.maxApiLayer = configObject.getIntField("maxApiLayer");
}
TgVoipEndpointType parseTgVoipEndpointType(JNIEnv *env, jint endpointType) {
switch (endpointType) {
case org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_INET:
return TgVoipEndpointType::Inet;
case org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_LAN:
return TgVoipEndpointType::Lan;
case org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_TCP_RELAY:
return TgVoipEndpointType::TcpRelay;
case org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_UDP_RELAY:
return TgVoipEndpointType::UdpRelay;
default:
throwNewJavaIllegalArgumentException(env, std::string("Unknown endpoint type: ").append(std::to_string(endpointType)).c_str());
return TgVoipEndpointType::UdpRelay;
}
}
void parseTgVoipEndpoint(JNIEnv *env, jobject endpoint, TgVoipEndpoint &tgVoipEndpoint) {
JavaObject endpointObject(env, endpoint);
tgVoipEndpoint.endpointId = endpointObject.getLongField("id");
tgVoipEndpoint.host = {
.ipv4 = jni::JavaStringToStdString(env, endpointObject.getStringField("ipv4")),
.ipv6 = jni::JavaStringToStdString(env, endpointObject.getStringField("ipv6"))
};
tgVoipEndpoint.port = static_cast<uint16_t>(endpointObject.getIntField("port"));
tgVoipEndpoint.type = parseTgVoipEndpointType(env, endpointObject.getIntField("type"));
jbyteArray peerTag = endpointObject.getByteArrayField("peerTag");
if (peerTag && env->GetArrayLength(peerTag)) {
jbyte *peerTagBytes = env->GetByteArrayElements(peerTag, nullptr);
memcpy(tgVoipEndpoint.peerTag, peerTagBytes, 16);
env->ReleaseByteArrayElements(peerTag, peerTagBytes, JNI_ABORT);
}
}
void parseTgVoipEndpoints(JNIEnv *env, jobjectArray endpoints, std::vector<TgVoipEndpoint> &tgVoipEndpoints) {
for (int i = 0, size = env->GetArrayLength(endpoints); i < size; i++) {
TgVoipEndpoint tgVoipEndpoint;
parseTgVoipEndpoint(env, env->GetObjectArrayElement(endpoints, i), tgVoipEndpoint);
tgVoipEndpoints.push_back(tgVoipEndpoint);
}
}
void parseTgVoipEncryptionKey(JNIEnv *env, jobject encryptionKey, TgVoipEncryptionKey &tgVoipEncryptionKey) {
JavaObject encryptionKeyObject(env, encryptionKey);
tgVoipEncryptionKey.isOutgoing = encryptionKeyObject.getBooleanField("isOutgoing") == JNI_TRUE;
jbyteArray valueByteArray = encryptionKeyObject.getByteArrayField("value");
auto *valueBytes = (uint8_t *) env->GetByteArrayElements(valueByteArray, nullptr);
tgVoipEncryptionKey.value = std::vector<uint8_t>(valueBytes, valueBytes + env->GetArrayLength(valueByteArray));
env->ReleaseByteArrayElements(valueByteArray, (jbyte *) valueBytes, JNI_ABORT);
}
void parseTgVoipProxy(JNIEnv *env, jobject proxy, std::unique_ptr<TgVoipProxy> &tgVoipProxy) {
if (!env->IsSameObject(proxy, nullptr)) {
JavaObject proxyObject(env, proxy);
tgVoipProxy = std::unique_ptr<TgVoipProxy>(new TgVoipProxy);
tgVoipProxy->host = jni::JavaStringToStdString(env, proxyObject.getStringField("host"));
tgVoipProxy->port = static_cast<uint16_t>(proxyObject.getIntField("port"));
tgVoipProxy->login = jni::JavaStringToStdString(env, proxyObject.getStringField("login"));
tgVoipProxy->password = jni::JavaStringToStdString(env, proxyObject.getStringField("password"));
} else {
tgVoipProxy = nullptr;
}
}
jint asJavaState(const TgVoipState &tgVoipState) {
switch (tgVoipState) {
case TgVoipState::WaitInit:
return org_telegram_messenger_voip_TgVoip_STATE_WAIT_INIT;
case TgVoipState::WaitInitAck:
return org_telegram_messenger_voip_TgVoip_STATE_WAIT_INIT_ACK;
case TgVoipState::Estabilished:
return org_telegram_messenger_voip_TgVoip_STATE_ESTABLISHED;
case TgVoipState::Failed:
return org_telegram_messenger_voip_TgVoip_STATE_FAILED;
case TgVoipState::Reconnecting:
return org_telegram_messenger_voip_TgVoip_STATE_RECONNECTING;
}
}
jobject asJavaTrafficStats(JNIEnv *env, const TgVoipTrafficStats &trafficStats) {
jclass clazz = env->FindClass("org/telegram/messenger/voip/TgVoip$TrafficStats");
jmethodID initMethodId = env->GetMethodID(clazz, "<init>", "(JJJJ)V");
return env->NewObject(clazz, initMethodId, trafficStats.bytesSentWifi, trafficStats.bytesReceivedWifi, trafficStats.bytesSentMobile, trafficStats.bytesReceivedMobile);
}
jobject asJavaFinalState(JNIEnv *env, const TgVoipFinalState &tgVoipFinalState) {
jbyteArray persistentState = copyVectorToJavaByteArray(env, tgVoipFinalState.persistentState.value);
jstring debugLog = env->NewStringUTF(tgVoipFinalState.debugLog.c_str());
jobject trafficStats = asJavaTrafficStats(env, tgVoipFinalState.trafficStats);
auto isRatingSuggested = static_cast<jboolean>(tgVoipFinalState.isRatingSuggested);
jclass finalStateClass = env->FindClass("org/telegram/messenger/voip/TgVoip$FinalState");
jmethodID finalStateInitMethodId = env->GetMethodID(finalStateClass, "<init>", "([BLjava/lang/String;Lorg/telegram/messenger/voip/TgVoip$TrafficStats;Z)V");
return env->NewObject(finalStateClass, finalStateInitMethodId, persistentState, debugLog, trafficStats, isRatingSuggested);
}
extern "C" {
#pragma mark - Static JNI Methods
JNIEXPORT jlong JNICALL Java_org_telegram_messenger_voip_NativeTgVoipDelegate_makeNativeInstance(JNIEnv *env, jobject obj, jobject instanceObj, jobject config, jstring persistentStateFilePath, jobjectArray endpoints, jobject proxy, jint networkType, jobject encryptionKey) {
// reading persistent state
TgVoipPersistentState tgVoipPersistentState;
readTgVoipPersistentState(jni::JavaStringToStdString(env, persistentStateFilePath).c_str(), tgVoipPersistentState);
// parsing config
TgVoipConfig tgVoipConfig;
parseTgVoipConfig(env, config, tgVoipConfig);
// parsing endpoints
std::vector<TgVoipEndpoint> tgVoipEndpoints;
parseTgVoipEndpoints(env, endpoints, tgVoipEndpoints);
// parsing proxy
std::unique_ptr<TgVoipProxy> tgVoipProxy;
parseTgVoipProxy(env, proxy, tgVoipProxy);
// parse encryption key
TgVoipEncryptionKey tgVoipEncryptionKey;
parseTgVoipEncryptionKey(env, encryptionKey, tgVoipEncryptionKey);
TgVoip *tgVoip = TgVoip::makeInstance(tgVoipConfig, tgVoipPersistentState, tgVoipEndpoints, tgVoipProxy, parseTgVoipNetworkType(networkType), tgVoipEncryptionKey);
if (env->ExceptionCheck() == JNI_TRUE) {
return 0;
}
jobject globalRef = env->NewGlobalRef(instanceObj);
tgVoip->setOnStateUpdated([globalRef](TgVoipState tgVoipState) {
jint state = asJavaState(tgVoipState);
jni::DoWithJNI([globalRef, state](JNIEnv *env) {
env->CallVoidMethod(globalRef, env->GetMethodID(env->GetObjectClass(globalRef), "onStateUpdated", "(I)V"), state);
});
});
tgVoip->setOnSignalBarsUpdated([globalRef](int signalBars) {
jni::DoWithJNI([globalRef, signalBars](JNIEnv *env) {
env->CallVoidMethod(globalRef, env->GetMethodID(env->GetObjectClass(globalRef), "onSignalBarsUpdated", "(I)V"), signalBars);
});
});
auto *instance = new InstanceHolder;
instance->nativeInstance = tgVoip;
instance->javaInstance = globalRef;
return reinterpret_cast<jlong>(instance);
}
JNIEXPORT void JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipDelegate_setGlobalServerConfig(JNIEnv *env, jobject obj, jstring serverConfigJson) {
TgVoip::setGlobalServerConfig(jni::JavaStringToStdString(env, serverConfigJson));
}
JNIEXPORT jint JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipDelegate_getConnectionMaxLayer(JNIEnv *env, jobject obj) {
return TgVoip::getConnectionMaxLayer();
}
JNIEXPORT jstring JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipDelegate_getVersion(JNIEnv *env, jobject obj) {
return env->NewStringUTF(TgVoip::getVersion().c_str());
}
JNIEXPORT void JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipDelegate_setBufferSize(JNIEnv *env, jobject obj, jint size) {
tgvoip::audio::AudioOutputOpenSLES::nativeBufferSize = (unsigned int) size;
tgvoip::audio::AudioInputOpenSLES::nativeBufferSize = (unsigned int) size;
}
#pragma mark - Virtual JNI Methods
JNIEXPORT void JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipInstance_setNetworkType(JNIEnv *env, jobject obj, jint networkType) {
getTgVoip(env, obj)->setNetworkType(parseTgVoipNetworkType(networkType));
}
JNIEXPORT void JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipInstance_setMuteMicrophone(JNIEnv *env, jobject obj, jboolean muteMicrophone) {
getTgVoip(env, obj)->setMuteMicrophone(muteMicrophone);
}
JNIEXPORT void JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipInstance_setAudioOutputGainControlEnabled(JNIEnv *env, jobject obj, jboolean enabled) {
getTgVoip(env, obj)->setAudioOutputGainControlEnabled(enabled);
}
JNIEXPORT void JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipInstance_setEchoCancellationStrength(JNIEnv *env, jobject obj, jint strength) {
getTgVoip(env, obj)->setEchoCancellationStrength(strength);
}
JNIEXPORT jstring JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipInstance_getLastError(JNIEnv *env, jobject obj) {
return env->NewStringUTF(getTgVoip(env, obj)->getLastError().c_str());
}
JNIEXPORT jstring JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipInstance_getDebugInfo(JNIEnv *env, jobject obj) {
return env->NewStringUTF(getTgVoip(env, obj)->getDebugInfo().c_str());
}
JNIEXPORT jlong JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipInstance_getPreferredRelayId(JNIEnv *env, jobject obj) {
return getTgVoip(env, obj)->getPreferredRelayId();
}
JNIEXPORT jobject JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipInstance_getTrafficStats(JNIEnv *env, jobject obj) {
return asJavaTrafficStats(env, getTgVoip(env, obj)->getTrafficStats());
}
JNIEXPORT jbyteArray JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipInstance_getPersistentState(JNIEnv *env, jobject obj) {
return copyVectorToJavaByteArray(env, getTgVoip(env, obj)->getPersistentState().value);
}
JNIEXPORT jobject JNICALL
Java_org_telegram_messenger_voip_NativeTgVoipInstance_stop(JNIEnv *env, jobject obj) {
InstanceHolder *instance = getInstanceHolder(env, obj);
TgVoipFinalState tgVoipFinalState = instance->nativeInstance->stop();
// saving persistent state
const std::string &path = jni::JavaStringToStdString(env, JavaObject(env, obj).getStringField("persistentStateFilePath"));
saveTgVoipPersistentState(path.c_str(), tgVoipFinalState.persistentState);
// clean
env->DeleteGlobalRef(instance->javaInstance);
delete instance->nativeInstance;
delete instance;
return asJavaFinalState(env, tgVoipFinalState);
}
}

View File

@ -0,0 +1,63 @@
#include <jni.h>
#ifndef _Included_org_telegram_messenger_voip_TgVoip
#define _Included_org_telegram_messenger_voip_TgVoip
#ifdef __cplusplus
extern "C" {
#endif
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_UNKNOWN
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_UNKNOWN 0L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_GPRS
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_GPRS 1L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_EDGE
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_EDGE 2L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_3G
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_3G 3L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_HSPA
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_HSPA 4L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_LTE
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_LTE 5L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_WIFI
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_WIFI 6L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_ETHERNET
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_ETHERNET 7L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_OTHER_HIGH_SPEED
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_OTHER_HIGH_SPEED 8L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_OTHER_LOW_SPEED
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_OTHER_LOW_SPEED 9L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_DIALUP
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_DIALUP 10L
#undef org_telegram_messenger_voip_TgVoip_NET_TYPE_OTHER_MOBILE
#define org_telegram_messenger_voip_TgVoip_NET_TYPE_OTHER_MOBILE 11L
#undef org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_INET
#define org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_INET 0L
#undef org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_LAN
#define org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_LAN 1L
#undef org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_UDP_RELAY
#define org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_UDP_RELAY 2L
#undef org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_TCP_RELAY
#define org_telegram_messenger_voip_TgVoip_ENDPOINT_TYPE_TCP_RELAY 3L
#undef org_telegram_messenger_voip_TgVoip_STATE_WAIT_INIT
#define org_telegram_messenger_voip_TgVoip_STATE_WAIT_INIT 1L
#undef org_telegram_messenger_voip_TgVoip_STATE_WAIT_INIT_ACK
#define org_telegram_messenger_voip_TgVoip_STATE_WAIT_INIT_ACK 2L
#undef org_telegram_messenger_voip_TgVoip_STATE_ESTABLISHED
#define org_telegram_messenger_voip_TgVoip_STATE_ESTABLISHED 3L
#undef org_telegram_messenger_voip_TgVoip_STATE_FAILED
#define org_telegram_messenger_voip_TgVoip_STATE_FAILED 4L
#undef org_telegram_messenger_voip_TgVoip_STATE_RECONNECTING
#define org_telegram_messenger_voip_TgVoip_STATE_RECONNECTING 5L
#undef org_telegram_messenger_voip_TgVoip_DATA_SAVING_NEVER
#define org_telegram_messenger_voip_TgVoip_DATA_SAVING_NEVER 0L
#undef org_telegram_messenger_voip_TgVoip_DATA_SAVING_MOBILE
#define org_telegram_messenger_voip_TgVoip_DATA_SAVING_MOBILE 1L
#undef org_telegram_messenger_voip_TgVoip_DATA_SAVING_ALWAYS
#define org_telegram_messenger_voip_TgVoip_DATA_SAVING_ALWAYS 2L
#undef org_telegram_messenger_voip_TgVoip_DATA_SAVING_ROAMING
#define org_telegram_messenger_voip_TgVoip_DATA_SAVING_ROAMING 3L
#undef org_telegram_messenger_voip_TgVoip_PEER_CAP_GROUP_CALLS
#define org_telegram_messenger_voip_TgVoip_PEER_CAP_GROUP_CALLS 1L
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,760 @@
//
// 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 "../../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 "../../os/android/VideoSourceAndroid.h"
#include "../../os/android/VideoRendererAndroid.h"
#include "../../audio/Resampler.h"
#include "../../os/android/JNIUtilities.h"
#include "../../PrivateDefines.h"
#include "../../logging.h"
#ifdef TGVOIP_HAS_CONFIG
#include <tgvoip_config.h>
#endif
JavaVM* sharedJVM;
jfieldID audioRecordInstanceFld=NULL;
jfieldID audioTrackInstanceFld=NULL;
jmethodID setStateMethod=NULL;
jmethodID setSignalBarsMethod=NULL;
jmethodID setSelfStreamsMethod=NULL;
jmethodID setParticipantAudioEnabledMethod=NULL;
jmethodID groupCallKeyReceivedMethod=NULL;
jmethodID groupCallKeySentMethod=NULL;
jmethodID callUpgradeRequestReceivedMethod=NULL;
jclass jniUtilitiesClass=NULL;
struct ImplDataAndroid{
jobject javaObject;
std::string persistentStateFile="";
};
#ifndef TGVOIP_PACKAGE_PATH
#define TGVOIP_PACKAGE_PATH "org/telegram/messenger/voip"
#endif
#ifndef TGVOIP_PEER_TAG_VARIABLE_NAME
#define TGVOIP_PEER_TAG_VARIABLE_NAME "peer_tag"
#endif
#ifndef TGVOIP_ENDPOINT_CLASS
#define TGVOIP_ENDPOINT_CLASS "org/telegram/tgnet/TLRPC$TL_phoneConnection"
#endif
using namespace tgvoip;
using namespace tgvoip::audio;
namespace tgvoip {
#pragma mark - Callbacks
void updateConnectionState(VoIPController *cntrlr, int state){
ImplDataAndroid *impl=(ImplDataAndroid *) cntrlr->implData;
jni::AttachAndCallVoidMethod(setStateMethod, impl->javaObject, state);
}
void updateSignalBarCount(VoIPController *cntrlr, int count){
ImplDataAndroid *impl=(ImplDataAndroid *) cntrlr->implData;
jni::AttachAndCallVoidMethod(setSignalBarsMethod, impl->javaObject, count);
}
void updateGroupCallStreams(VoIPGroupController *cntrlr, unsigned char *streams, size_t len){
ImplDataAndroid *impl=(ImplDataAndroid *) cntrlr->implData;
if(!impl->javaObject)
return;
jni::DoWithJNI([streams, len, &impl](JNIEnv* env){
if(setSelfStreamsMethod){
jbyteArray jstreams=env->NewByteArray(static_cast<jsize>(len));
jbyte *el=env->GetByteArrayElements(jstreams, NULL);
memcpy(el, streams, len);
env->ReleaseByteArrayElements(jstreams, el, 0);
env->CallVoidMethod(impl->javaObject, setSelfStreamsMethod, jstreams);
}
});
}
void groupCallKeyReceived(VoIPController *cntrlr, const unsigned char *key){
ImplDataAndroid *impl=(ImplDataAndroid *) cntrlr->implData;
if(!impl->javaObject)
return;
jni::DoWithJNI([key, &impl](JNIEnv* env){
if(groupCallKeyReceivedMethod){
jbyteArray jkey=env->NewByteArray(256);
jbyte *el=env->GetByteArrayElements(jkey, NULL);
memcpy(el, key, 256);
env->ReleaseByteArrayElements(jkey, el, 0);
env->CallVoidMethod(impl->javaObject, groupCallKeyReceivedMethod, jkey);
}
});
}
void groupCallKeySent(VoIPController *cntrlr){
ImplDataAndroid *impl=(ImplDataAndroid *) cntrlr->implData;
jni::AttachAndCallVoidMethod(groupCallKeySentMethod, impl->javaObject);
}
void callUpgradeRequestReceived(VoIPController* cntrlr){
ImplDataAndroid *impl=(ImplDataAndroid *) cntrlr->implData;
jni::AttachAndCallVoidMethod(callUpgradeRequestReceivedMethod, impl->javaObject);
}
void updateParticipantAudioState(VoIPGroupController *cntrlr, int32_t userID, bool enabled){
ImplDataAndroid *impl=(ImplDataAndroid *) cntrlr->implData;
jni::AttachAndCallVoidMethod(setParticipantAudioEnabledMethod, impl->javaObject, userID, enabled);
}
#pragma mark - VoIPController
uint32_t AndroidCodecToFOURCC(std::string mime){
if(mime=="video/avc")
return CODEC_AVC;
else if(mime=="video/hevc")
return CODEC_HEVC;
else if(mime=="video/x-vnd.on2.vp8")
return CODEC_VP8;
else if(mime=="video/x-vnd.on2.vp9")
return CODEC_VP9;
return 0;
}
jlong VoIPController_nativeInit(JNIEnv* env, jobject thiz, jstring persistentStateFile) {
ImplDataAndroid* impl=new ImplDataAndroid();
impl->javaObject=env->NewGlobalRef(thiz);
if(persistentStateFile){
impl->persistentStateFile=jni::JavaStringToStdString(env, persistentStateFile);
}
VoIPController* cntrlr=new VoIPController();
cntrlr->implData=impl;
VoIPController::Callbacks callbacks;
callbacks.connectionStateChanged=updateConnectionState;
callbacks.signalBarCountChanged=updateSignalBarCount;
callbacks.groupCallKeyReceived=groupCallKeyReceived;
callbacks.groupCallKeySent=groupCallKeySent;
callbacks.upgradeToGroupCallRequested=callUpgradeRequestReceived;
cntrlr->SetCallbacks(callbacks);
if(!impl->persistentStateFile.empty()){
FILE* f=fopen(impl->persistentStateFile.c_str(), "r");
if(f){
fseek(f, 0, SEEK_END);
size_t len=static_cast<size_t>(ftell(f));
fseek(f, 0, SEEK_SET);
if(len<1024*512 && len>0){
char *fbuf=static_cast<char *>(malloc(len));
fread(fbuf, 1, len, f);
std::vector<uint8_t> state(fbuf, fbuf+len);
free(fbuf);
cntrlr->SetPersistentState(state);
}
fclose(f);
}
}
#ifndef TGVOIP_NO_VIDEO
if(video::VideoRendererAndroid::availableDecoders.empty() || video::VideoSourceAndroid::availableEncoders.empty()){
video::VideoRendererAndroid::availableDecoders.clear();
video::VideoSourceAndroid::availableEncoders.clear();
jmethodID getCodecsMethod=env->GetStaticMethodID(jniUtilitiesClass, "getSupportedVideoCodecs", "()[[Ljava/lang/String;");
jobjectArray codecs=static_cast<jobjectArray>(env->CallStaticObjectMethod(jniUtilitiesClass, getCodecsMethod));
jobjectArray encoders=static_cast<jobjectArray>(env->GetObjectArrayElement(codecs, 0));
jobjectArray decoders=static_cast<jobjectArray>(env->GetObjectArrayElement(codecs, 1));
for(jsize i=0;i<env->GetArrayLength(encoders);i++){
std::string codec=jni::JavaStringToStdString(env, static_cast<jstring>(env->GetObjectArrayElement(encoders, i)));
uint32_t id=AndroidCodecToFOURCC(codec);
if(id)
video::VideoSourceAndroid::availableEncoders.push_back(id);
}
for(jsize i=0;i<env->GetArrayLength(decoders);i++){
std::string codec=jni::JavaStringToStdString(env, static_cast<jstring>(env->GetObjectArrayElement(decoders, i)));
uint32_t id=AndroidCodecToFOURCC(codec);
if(id)
video::VideoRendererAndroid::availableDecoders.push_back(id);
}
jmethodID getMaxResolutionMethod=env->GetStaticMethodID(jniUtilitiesClass, "getMaxVideoResolution", "()I");
video::VideoRendererAndroid::maxResolution=env->CallStaticIntMethod(jniUtilitiesClass, getMaxResolutionMethod);
}
#endif
return (jlong)(intptr_t)cntrlr;
}
void VoIPController_nativeStart(JNIEnv* env, jobject thiz, jlong inst){
((VoIPController*)(intptr_t)inst)->Start();
}
void VoIPController_nativeConnect(JNIEnv* env, jobject thiz, jlong inst){
((VoIPController*)(intptr_t)inst)->Connect();
}
void VoIPController_nativeSetProxy(JNIEnv* env, jobject thiz, jlong inst, jstring _address, jint port, jstring _username, jstring _password){
((VoIPController*)(intptr_t)inst)->SetProxy(PROXY_SOCKS5, jni::JavaStringToStdString(env, _address), (uint16_t)port, jni::JavaStringToStdString(env, _username), jni::JavaStringToStdString(env, _password));
}
void 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);
}
void VoIPController_nativeSetRemoteEndpoints(JNIEnv* env, jobject thiz, jlong inst, jobjectArray endpoints, jboolean allowP2p, jboolean tcp, jint connectionMaxLayer){
size_t len=(size_t) env->GetArrayLength(endpoints);
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, TGVOIP_PEER_TAG_VARIABLE_NAME, "[B");
jfieldID idFld=env->GetFieldID(epClass, "id", "J");
for(unsigned int 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);
IPv4Address v4addr(jni::JavaStringToStdString(env, ip));
IPv6Address v6addr("::0");
if(ipv6 && env->GetStringLength(ipv6)){
v6addr=IPv6Address(jni::JavaStringToStdString(env, ipv6));
}
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, tcp ? Endpoint::Type::TCP_RELAY : Endpoint::Type::UDP_RELAY, pTag));
}
((VoIPController*)(intptr_t)inst)->SetRemoteEndpoints(eps, allowP2p, connectionMaxLayer);
}
void VoIPController_nativeSetNativeBufferSize(JNIEnv* env, jclass thiz, jint size){
AudioOutputOpenSLES::nativeBufferSize=(unsigned int) size;
AudioInputOpenSLES::nativeBufferSize=(unsigned int) size;
}
void VoIPController_nativeRelease(JNIEnv* env, jobject thiz, jlong inst){
//env->DeleteGlobalRef(AudioInputAndroid::jniClass);
VoIPController* ctlr=((VoIPController*)(intptr_t)inst);
ImplDataAndroid* impl=(ImplDataAndroid*)ctlr->implData;
ctlr->Stop();
std::vector<uint8_t> state=ctlr->GetPersistentState();
delete ctlr;
env->DeleteGlobalRef(impl->javaObject);
if(!impl->persistentStateFile.empty()){
FILE* f=fopen(impl->persistentStateFile.c_str(), "w");
if(f){
fwrite(state.data(), 1, state.size(), f);
fclose(f);
}
}
delete impl;
}
jstring VoIPController_nativeGetDebugString(JNIEnv* env, jobject thiz, jlong inst){
std::string str=((VoIPController*)(intptr_t)inst)->GetDebugString();
return env->NewStringUTF(str.c_str());
}
void VoIPController_nativeSetNetworkType(JNIEnv* env, jobject thiz, jlong inst, jint type){
((VoIPController*)(intptr_t)inst)->SetNetworkType(type);
}
void VoIPController_nativeSetMicMute(JNIEnv* env, jobject thiz, jlong inst, jboolean mute){
((VoIPController*)(intptr_t)inst)->SetMicMute(mute);
}
void VoIPController_nativeSetConfig(JNIEnv* env, jobject thiz, jlong inst, jdouble recvTimeout, jdouble initTimeout, jint dataSavingMode, jboolean enableAEC, jboolean enableNS, jboolean enableAGC, jstring logFilePath, jstring statsDumpPath, jboolean logPacketStats){
VoIPController::Config cfg;
cfg.initTimeout=initTimeout;
cfg.recvTimeout=recvTimeout;
cfg.dataSaving=dataSavingMode;
cfg.enableAEC=enableAEC;
cfg.enableNS=enableNS;
cfg.enableAGC=enableAGC;
cfg.enableCallUpgrade=false;
cfg.logPacketStats=logPacketStats;
if(logFilePath){
cfg.logFilePath=jni::JavaStringToStdString(env, logFilePath);
}
if(statsDumpPath){
cfg.statsDumpFilePath=jni::JavaStringToStdString(env, statsDumpPath);
}
#ifndef TGVOIP_NO_VIDEO
cfg.enableVideoReceive=cfg.enableVideoSend=true;
#endif
((VoIPController*)(intptr_t)inst)->SetConfig(cfg);
}
void VoIPController_nativeDebugCtl(JNIEnv* env, jobject thiz, jlong inst, jint request, jint param){
((VoIPController*)(intptr_t)inst)->DebugCtl(request, param);
}
jstring VoIPController_nativeGetVersion(JNIEnv* env, jclass clasz){
return env->NewStringUTF(VoIPController::GetVersion());
}
jlong VoIPController_nativeGetPreferredRelayID(JNIEnv* env, jclass clasz, jlong inst){
return ((VoIPController*)(intptr_t)inst)->GetPreferredRelayID();
}
jint VoIPController_nativeGetLastError(JNIEnv* env, jclass clasz, jlong inst){
return ((VoIPController*)(intptr_t)inst)->GetLastError();
}
void VoIPController_nativeGetStats(JNIEnv* env, jclass clasz, jlong inst, jobject stats){
VoIPController::TrafficStats _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);
}
jstring 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());
}
void VoIPController_nativeSetAudioOutputGainControlEnabled(JNIEnv* env, jclass clasz, jlong inst, jboolean enabled){
((VoIPController*)(intptr_t)inst)->SetAudioOutputGainControlEnabled(enabled);
}
void VoIPController_nativeSetEchoCancellationStrength(JNIEnv* env, jclass cls, jlong inst, jint strength){
((VoIPController*)(intptr_t)inst)->SetEchoCancellationStrength(strength);
}
jint VoIPController_nativeGetPeerCapabilities(JNIEnv* env, jclass cls, jlong inst){
return ((VoIPController*)(intptr_t)inst)->GetPeerCapabilities();
}
void VoIPController_nativeSendGroupCallKey(JNIEnv* env, jclass cls, jlong inst, jbyteArray _key){
jbyte* key=env->GetByteArrayElements(_key, NULL);
((VoIPController*)(intptr_t)inst)->SendGroupCallKey((unsigned char *) key);
env->ReleaseByteArrayElements(_key, key, JNI_ABORT);
}
void VoIPController_nativeRequestCallUpgrade(JNIEnv* env, jclass cls, jlong inst){
((VoIPController*)(intptr_t)inst)->RequestCallUpgrade();
}
void VoIPController_nativeSetVideoSource(JNIEnv* env, jobject thiz, jlong inst, jlong source){
((VoIPController*)(intptr_t)inst)->SetVideoSource((video::VideoSource*)(intptr_t)source);
}
void VoIPController_nativeSetVideoRenderer(JNIEnv* env, jobject thiz, jlong inst, jlong renderer){
((VoIPController*)(intptr_t)inst)->SetVideoRenderer((video::VideoRenderer*)(intptr_t)renderer);
}
jboolean VoIPController_nativeNeedRate(JNIEnv* env, jclass cls, jlong inst){
return static_cast<jboolean>(((VoIPController*)(intptr_t)inst)->NeedRate());
}
jint VoIPController_getConnectionMaxLayer(JNIEnv* env, jclass cls){
return VoIPController::GetConnectionMaxLayer();
}
#pragma mark - AudioRecordJNI
void AudioRecordJNI_nativeCallback(JNIEnv* env, jobject thiz, jobject buffer){
jlong inst=env->GetLongField(thiz, audioRecordInstanceFld);
AudioInputAndroid* in=(AudioInputAndroid*)(intptr_t)inst;
in->HandleCallback(env, buffer);
}
#pragma mark - AudioTrackJNI
void AudioTrackJNI_nativeCallback(JNIEnv* env, jobject thiz, jbyteArray buffer){
jlong inst=env->GetLongField(thiz, audioTrackInstanceFld);
AudioOutputAndroid* in=(AudioOutputAndroid*)(intptr_t)inst;
in->HandleCallback(env, buffer);
}
#pragma mark - VoIPServerConfig
void VoIPServerConfig_nativeSetConfig(JNIEnv* env, jclass clasz, jstring jsonString){
ServerConfig::GetSharedInstance()->Update(jni::JavaStringToStdString(env, jsonString));
}
#pragma mark - Resampler
jint Resampler_convert44to48(JNIEnv* env, jclass cls, jobject from, jobject to){
return (jint)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));
}
jint Resampler_convert48to44(JNIEnv* env, jclass cls, jobject from, jobject to){
return (jint)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));
}
#pragma mark - VoIPGroupController
#ifndef TGVOIP_NO_GROUP_CALLS
jlong VoIPGroupController_nativeInit(JNIEnv* env, jobject thiz, jint timeDifference){
ImplDataAndroid* impl=(ImplDataAndroid*) malloc(sizeof(ImplDataAndroid));
impl->javaObject=env->NewGlobalRef(thiz);
VoIPGroupController* cntrlr=new VoIPGroupController(timeDifference);
cntrlr->implData=impl;
VoIPGroupController::Callbacks callbacks;
callbacks.connectionStateChanged=updateConnectionState;
callbacks.updateStreams=updateGroupCallStreams;
callbacks.participantAudioStateChanged=updateParticipantAudioState;
callbacks.signalBarCountChanged=NULL;
cntrlr->SetCallbacks(callbacks);
return (jlong)(intptr_t)cntrlr;
}
void VoIPGroupController_nativeSetGroupCallInfo(JNIEnv* env, jclass cls, jlong inst, jbyteArray _encryptionKey, jbyteArray _reflectorGroupTag, jbyteArray _reflectorSelfTag, jbyteArray _reflectorSelfSecret, jbyteArray _reflectorSelfTagHash, jint selfUserID, jstring reflectorAddress, jstring reflectorAddressV6, jint reflectorPort){
VoIPGroupController* ctlr=((VoIPGroupController*)(intptr_t)inst);
jbyte* encryptionKey=env->GetByteArrayElements(_encryptionKey, NULL);
jbyte* reflectorGroupTag=env->GetByteArrayElements(_reflectorGroupTag, NULL);
jbyte* reflectorSelfTag=env->GetByteArrayElements(_reflectorSelfTag, NULL);
jbyte* reflectorSelfSecret=env->GetByteArrayElements(_reflectorSelfSecret, NULL);
jbyte* reflectorSelfTagHash=env->GetByteArrayElements(_reflectorSelfTagHash, NULL);
const char* ipChars=env->GetStringUTFChars(reflectorAddress, NULL);
std::string ipLiteral(ipChars);
NetworkAddress v4addr=NetworkAddress::IPv4(ipLiteral);
NetworkAddress v6addr=NetworkAddress::Empty();
env->ReleaseStringUTFChars(reflectorAddress, ipChars);
if(reflectorAddressV6 && env->GetStringLength(reflectorAddressV6)){
const char* ipv6Chars=env->GetStringUTFChars(reflectorAddressV6, NULL);
v6addr=NetworkAddress::IPv6(ipv6Chars);
env->ReleaseStringUTFChars(reflectorAddressV6, ipv6Chars);
}
ctlr->SetGroupCallInfo((unsigned char *) encryptionKey, (unsigned char *) reflectorGroupTag, (unsigned char *) reflectorSelfTag, (unsigned char *) reflectorSelfSecret, (unsigned char*) reflectorSelfTagHash, selfUserID, v4addr, v6addr, (uint16_t)reflectorPort);
env->ReleaseByteArrayElements(_encryptionKey, encryptionKey, JNI_ABORT);
env->ReleaseByteArrayElements(_reflectorGroupTag, reflectorGroupTag, JNI_ABORT);
env->ReleaseByteArrayElements(_reflectorSelfTag, reflectorSelfTag, JNI_ABORT);
env->ReleaseByteArrayElements(_reflectorSelfSecret, reflectorSelfSecret, JNI_ABORT);
env->ReleaseByteArrayElements(_reflectorSelfTagHash, reflectorSelfTagHash, JNI_ABORT);
}
void VoIPGroupController_nativeAddGroupCallParticipant(JNIEnv* env, jclass cls, jlong inst, jint userID, jbyteArray _memberTagHash, jbyteArray _streams){
VoIPGroupController* ctlr=((VoIPGroupController*)(intptr_t)inst);
jbyte* memberTagHash=env->GetByteArrayElements(_memberTagHash, NULL);
jbyte* streams=_streams ? env->GetByteArrayElements(_streams, NULL) : NULL;
ctlr->AddGroupCallParticipant(userID, (unsigned char *) memberTagHash, (unsigned char *) streams, (size_t) env->GetArrayLength(_streams));
env->ReleaseByteArrayElements(_memberTagHash, memberTagHash, JNI_ABORT);
if(_streams)
env->ReleaseByteArrayElements(_streams, streams, JNI_ABORT);
}
void VoIPGroupController_nativeRemoveGroupCallParticipant(JNIEnv* env, jclass cls, jlong inst, jint userID){
VoIPGroupController* ctlr=((VoIPGroupController*)(intptr_t)inst);
ctlr->RemoveGroupCallParticipant(userID);
}
jfloat VoIPGroupController_nativeGetParticipantAudioLevel(JNIEnv* env, jclass cls, jlong inst, jint userID){
return ((VoIPGroupController*)(intptr_t)inst)->GetParticipantAudioLevel(userID);
}
void VoIPGroupController_nativeSetParticipantVolume(JNIEnv* env, jclass cls, jlong inst, jint userID, jfloat volume){
((VoIPGroupController*)(intptr_t)inst)->SetParticipantVolume(userID, volume);
}
jbyteArray VoIPGroupController_getInitialStreams(JNIEnv* env, jclass cls){
unsigned char buf[1024];
size_t len=VoIPGroupController::GetInitialStreams(buf, sizeof(buf));
jbyteArray arr=env->NewByteArray(len);
jbyte* arrElems=env->GetByteArrayElements(arr, NULL);
memcpy(arrElems, buf, len);
env->ReleaseByteArrayElements(arr, arrElems, 0);
return arr;
}
void VoIPGroupController_nativeSetParticipantStreams(JNIEnv* env, jclass cls, jlong inst, jint userID, jbyteArray _streams){
jbyte* streams=env->GetByteArrayElements(_streams, NULL);
((VoIPGroupController*)(intptr_t)inst)->SetParticipantStreams(userID, (unsigned char *) streams, (size_t) env->GetArrayLength(_streams));
env->ReleaseByteArrayElements(_streams, streams, JNI_ABORT);
}
#endif
#pragma mark - VideoSource
jlong VideoSource_nativeInit(JNIEnv* env, jobject thiz){
return (jlong)(intptr_t)new video::VideoSourceAndroid(env->NewGlobalRef(thiz));
}
void VideoSource_nativeRelease(JNIEnv* env, jobject thiz, jlong inst){
delete (video::VideoSource*)(intptr_t)inst;
}
void VideoSource_nativeSetVideoStreamParameters(JNIEnv* env, jobject thiz, jlong inst, jobjectArray _csd, jint width, jint height){
std::vector<Buffer> csd;
if(_csd){
for(int i=0; i<env->GetArrayLength(_csd); i++){
jobject _buf=env->GetObjectArrayElement(_csd, i);
size_t len=static_cast<size_t>(env->GetDirectBufferCapacity(_buf));
Buffer buf(len);
buf.CopyFrom(env->GetDirectBufferAddress(_buf), 0, len);
csd.push_back(std::move(buf));
}
}
((video::VideoSourceAndroid*)(intptr_t)inst)->SetStreamParameters(std::move(csd), width, height);
}
void VideoSource_nativeSendFrame(JNIEnv* env, jobject thiz, jlong inst, jobject buffer, jint offset, jint length, jint flags){
//size_t bufsize=(size_t)env->GetDirectBufferCapacity(buffer);
Buffer buf(static_cast<size_t>(length));
buf.CopyFrom(((char*)env->GetDirectBufferAddress(buffer))+offset, 0, static_cast<size_t>(length));
((video::VideoSourceAndroid*)(intptr_t)inst)->SendFrame(std::move(buf), static_cast<uint32_t>(flags));
}
void VideoSource_nativeSetRotation(JNIEnv* env, jobject thiz, jlong inst, jint rotation){
((video::VideoSourceAndroid*)(intptr_t)inst)->SetRotation((unsigned int)rotation);
}
void VideoSource_nativeSetPaused(JNIEnv* env, jobject thiz, jlong inst, jboolean paused){
((video::VideoSourceAndroid*)(intptr_t)inst)->SetStreamPaused((bool)paused);
}
#pragma mark - VideoRenderer
jlong VideoRenderer_nativeInit(JNIEnv* env, jobject thiz){
return (jlong)(intptr_t)new video::VideoRendererAndroid(env->NewGlobalRef(thiz));
}
#pragma mark - VLog
template<int level> void VLog_log(JNIEnv* env, jclass cls, jstring jmsg){
const char* format="[java] %s";
std::string msg=jni::JavaStringToStdString(env, jmsg);
switch(level){
case 0:
LOGV(format, msg.c_str());
break;
case 1:
LOGD(format, msg.c_str());
break;
case 2:
LOGI(format, msg.c_str());
break;
case 3:
LOGW(format, msg.c_str());
break;
case 4:
LOGE(format, msg.c_str());
break;
default:
break;
}
}
}
extern "C" int tgvoipOnJniLoad(JavaVM *vm, JNIEnv *env);
extern "C" jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = 0;
srand(time(NULL));
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
if (tgvoipOnJniLoad(vm, env) != JNI_TRUE) {
return -1;
}
jclass controller=env->FindClass(TGVOIP_PACKAGE_PATH "/VoIPController");
jclass groupController=env->FindClass(TGVOIP_PACKAGE_PATH "/VoIPGroupController");
if(env->ExceptionCheck()){
env->ExceptionClear(); // is returning NULL from FindClass not enough?
}
jclass audioRecordJNI=env->FindClass(TGVOIP_PACKAGE_PATH "/AudioRecordJNI");
jclass audioTrackJNI=env->FindClass(TGVOIP_PACKAGE_PATH "/AudioTrackJNI");
jclass serverConfig=env->FindClass(TGVOIP_PACKAGE_PATH "/VoIPServerConfig");
jclass resampler=env->FindClass(TGVOIP_PACKAGE_PATH "/Resampler");
jclass videoSource=env->FindClass(TGVOIP_PACKAGE_PATH "/VideoSource");
if(env->ExceptionCheck()){
env->ExceptionClear(); // is returning NULL from FindClass not enough?
}
jclass videoRenderer=env->FindClass(TGVOIP_PACKAGE_PATH "/VideoRenderer");
if(env->ExceptionCheck()){
env->ExceptionClear(); // is returning NULL from FindClass not enough?
}
jclass vlog=env->FindClass(TGVOIP_PACKAGE_PATH "/VLog");
if(env->ExceptionCheck()){
env->ExceptionClear();
}
assert(controller && audioRecordJNI && audioTrackJNI && serverConfig && resampler);
audioRecordInstanceFld=env->GetFieldID(audioRecordJNI, "nativeInst", "J");
audioTrackInstanceFld=env->GetFieldID(audioTrackJNI, "nativeInst", "J");
env->GetJavaVM(&sharedJVM);
if(!AudioInputAndroid::jniClass){
jclass cls=env->FindClass(TGVOIP_PACKAGE_PATH "/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");
AudioInputAndroid::getEnabledEffectsMaskMethod=env->GetMethodID(cls, "getEnabledEffectsMask", "()I");
cls=env->FindClass(TGVOIP_PACKAGE_PATH "/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");
if(videoRenderer){
video::VideoRendererAndroid::decodeAndDisplayMethod=env->GetMethodID(videoRenderer, "decodeAndDisplay", "(Ljava/nio/ByteBuffer;IJ)V");
video::VideoRendererAndroid::resetMethod=env->GetMethodID(videoRenderer, "reset", "(Ljava/lang/String;II[[B)V");
video::VideoRendererAndroid::setStreamEnabledMethod=env->GetMethodID(videoRenderer, "setStreamEnabled", "(ZZ)V");
video::VideoRendererAndroid::setRotationMethod=env->GetMethodID(videoRenderer, "setRotation", "(I)V");
}
}
setStateMethod=env->GetMethodID(controller, "handleStateChange", "(I)V");
setSignalBarsMethod=env->GetMethodID(controller, "handleSignalBarsChange", "(I)V");
groupCallKeyReceivedMethod=env->GetMethodID(controller, "groupCallKeyReceived", "([B)V");
groupCallKeySentMethod=env->GetMethodID(controller, "groupCallKeySent", "()V");
callUpgradeRequestReceivedMethod=env->GetMethodID(controller, "callUpgradeRequestReceived", "()V");
if(!jniUtilitiesClass)
jniUtilitiesClass=(jclass) env->NewGlobalRef(env->FindClass(TGVOIP_PACKAGE_PATH "/JNIUtilities"));
// VoIPController
JNINativeMethod controllerMethods[]={
{"nativeInit", "(Ljava/lang/String;)J", (void*)&tgvoip::VoIPController_nativeInit},
{"nativeStart", "(J)V", (void*)&tgvoip::VoIPController_nativeStart},
{"nativeConnect", "(J)V", (void*)&tgvoip::VoIPController_nativeConnect},
{"nativeSetProxy", "(JLjava/lang/String;ILjava/lang/String;Ljava/lang/String;)V", (void*)&tgvoip::VoIPController_nativeSetProxy},
{"nativeSetEncryptionKey", "(J[BZ)V", (void*)&tgvoip::VoIPController_nativeSetEncryptionKey},
{"nativeSetRemoteEndpoints", "(J[L" TGVOIP_ENDPOINT_CLASS ";ZZI)V", (void*)&tgvoip::VoIPController_nativeSetRemoteEndpoints},
{"nativeSetNativeBufferSize", "(I)V", (void*)&tgvoip::VoIPController_nativeSetNativeBufferSize},
{"nativeRelease", "(J)V", (void*)&tgvoip::VoIPController_nativeRelease},
{"nativeGetDebugString", "(J)Ljava/lang/String;", (void*)&tgvoip::VoIPController_nativeGetDebugString},
{"nativeSetNetworkType", "(JI)V", (void*)&tgvoip::VoIPController_nativeSetNetworkType},
{"nativeSetMicMute", "(JZ)V", (void*)&tgvoip::VoIPController_nativeSetMicMute},
{"nativeSetConfig", "(JDDIZZZLjava/lang/String;Ljava/lang/String;Z)V", (void*)&tgvoip::VoIPController_nativeSetConfig},
{"nativeDebugCtl", "(JII)V", (void*)&tgvoip::VoIPController_nativeDebugCtl},
{"nativeGetVersion", "()Ljava/lang/String;", (void*)&tgvoip::VoIPController_nativeGetVersion},
{"nativeGetPreferredRelayID", "(J)J", (void*)&tgvoip::VoIPController_nativeGetPreferredRelayID},
{"nativeGetLastError", "(J)I", (void*)&tgvoip::VoIPController_nativeGetLastError},
{"nativeGetStats", "(JL" TGVOIP_PACKAGE_PATH "/VoIPController$Stats;)V", (void*)&tgvoip::VoIPController_nativeGetStats},
{"nativeGetDebugLog", "(J)Ljava/lang/String;", (void*)&tgvoip::VoIPController_nativeGetDebugLog},
{"nativeSetAudioOutputGainControlEnabled", "(JZ)V", (void*)&tgvoip::VoIPController_nativeSetAudioOutputGainControlEnabled},
{"nativeSetEchoCancellationStrength", "(JI)V", (void*)&tgvoip::VoIPController_nativeSetEchoCancellationStrength},
{"nativeGetPeerCapabilities", "(J)I", (void*)&tgvoip::VoIPController_nativeGetPeerCapabilities},
{"nativeSendGroupCallKey", "(J[B)V", (void*)&tgvoip::VoIPController_nativeSendGroupCallKey},
{"nativeRequestCallUpgrade", "(J)V", (void*)&tgvoip::VoIPController_nativeRequestCallUpgrade},
{"nativeNeedRate", "(J)Z", (void*)&tgvoip::VoIPController_nativeNeedRate},
{"getConnectionMaxLayer", "()I", (void*)&tgvoip::VoIPController_getConnectionMaxLayer}
#ifndef TGVOIP_NO_VIDEO
,{"nativeSetVideoSource", "(JJ)V", (void*)&tgvoip::VoIPController_nativeSetVideoSource},
{"nativeSetVideoRenderer", "(JJ)V", (void*)&tgvoip::VoIPController_nativeSetVideoRenderer}
#endif
};
env->RegisterNatives(controller, controllerMethods, sizeof(controllerMethods)/sizeof(JNINativeMethod));
// VoIPGroupController
#ifndef TGVOIP_NO_GROUP_CALLS
if(groupController){
setStateMethod=env->GetMethodID(groupController, "handleStateChange", "(I)V");
setParticipantAudioEnabledMethod=env->GetMethodID(groupController, "setParticipantAudioEnabled", "(IZ)V");
setSelfStreamsMethod=env->GetMethodID(groupController, "setSelfStreams", "([B)V");
JNINativeMethod groupControllerMethods[]={
{"nativeInit", "(I)J", (void*)&tgvoip::VoIPGroupController_nativeInit},
{"nativeSetGroupCallInfo", "(J[B[B[B[B[BILjava/lang/String;Ljava/lang/String;I)V", (void*)&tgvoip::VoIPGroupController_nativeSetGroupCallInfo},
{"nativeAddGroupCallParticipant", "(JI[B[B)V", (void*)&tgvoip::VoIPGroupController_nativeAddGroupCallParticipant},
{"nativeRemoveGroupCallParticipant", "(JI)V", (void*)&tgvoip::VoIPGroupController_nativeRemoveGroupCallParticipant},
{"nativeGetParticipantAudioLevel", "(JI)F", (void*)&tgvoip::VoIPGroupController_nativeGetParticipantAudioLevel},
{"nativeSetParticipantVolume", "(JIF)V", (void*)&tgvoip::VoIPGroupController_nativeSetParticipantVolume},
{"getInitialStreams", "()[B", (void*)&tgvoip::VoIPGroupController_getInitialStreams},
{"nativeSetParticipantStreams", "(JI[B)V", (void*)&tgvoip::VoIPGroupController_nativeSetParticipantStreams}
};
env->RegisterNatives(groupController, groupControllerMethods, sizeof(groupControllerMethods)/sizeof(JNINativeMethod));
}
#endif
// AudioRecordJNI
JNINativeMethod audioRecordMethods[]={
{"nativeCallback", "(Ljava/nio/ByteBuffer;)V", (void*)&tgvoip::AudioRecordJNI_nativeCallback}
};
env->RegisterNatives(audioRecordJNI, audioRecordMethods, sizeof(audioRecordMethods)/sizeof(JNINativeMethod));
// AudioTrackJNI
JNINativeMethod audioTrackMethods[]={
{"nativeCallback", "([B)V", (void*)&tgvoip::AudioTrackJNI_nativeCallback}
};
env->RegisterNatives(audioTrackJNI, audioTrackMethods, sizeof(audioTrackMethods)/sizeof(JNINativeMethod));
// VoIPServerConfig
JNINativeMethod serverConfigMethods[]={
{"nativeSetConfig", "(Ljava/lang/String;)V", (void*)&tgvoip::VoIPServerConfig_nativeSetConfig}
};
env->RegisterNatives(serverConfig, serverConfigMethods, sizeof(serverConfigMethods)/sizeof(JNINativeMethod));
// Resampler
JNINativeMethod resamplerMethods[]={
{"convert44to48", "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)I", (void*)&tgvoip::Resampler_convert44to48},
{"convert48to44", "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)I", (void*)&tgvoip::Resampler_convert48to44}
};
env->RegisterNatives(resampler, resamplerMethods, sizeof(resamplerMethods)/sizeof(JNINativeMethod));
if(videoSource){
// VideoSource
JNINativeMethod videoSourceMethods[]={
{"nativeInit", "()J", (void *) &tgvoip::VideoSource_nativeInit},
{"nativeRelease", "(J)V", (void *) &tgvoip::VideoSource_nativeRelease},
{"nativeSetVideoStreamParameters", "(J[Ljava/nio/ByteBuffer;II)V", (void *) &tgvoip::VideoSource_nativeSetVideoStreamParameters},
{"nativeSendFrame", "(JLjava/nio/ByteBuffer;III)V", (void *) &tgvoip::VideoSource_nativeSendFrame},
{"nativeSetRotation", "(JI)V", (void*)&tgvoip::VideoSource_nativeSetRotation},
{"nativeSetPaused", "(JZ)V", (void*)&tgvoip::VideoSource_nativeSetPaused}
};
env->RegisterNatives(videoSource, videoSourceMethods, sizeof(videoSourceMethods)/sizeof(JNINativeMethod));
}
if(videoRenderer){
// VideoRenderer
JNINativeMethod videoRendererMethods[]={
{"nativeInit", "()J", (void *) &tgvoip::VideoRenderer_nativeInit}
};
env->RegisterNatives(videoRenderer, videoRendererMethods, sizeof(videoRendererMethods)/sizeof(JNINativeMethod));
}
if(vlog){
// VLog
JNINativeMethod vlogMethods[]={
{"v", "(Ljava/lang/String;)V", (void *) &tgvoip::VLog_log<0>},
{"d", "(Ljava/lang/String;)V", (void *) &tgvoip::VLog_log<1>},
{"i", "(Ljava/lang/String;)V", (void *) &tgvoip::VLog_log<2>},
{"w", "(Ljava/lang/String;)V", (void *) &tgvoip::VLog_log<3>},
{"e", "(Ljava/lang/String;)V", (void *) &tgvoip::VLog_log<4>}
};
env->RegisterNatives(vlog, vlogMethods, sizeof(vlogMethods)/sizeof(JNINativeMethod));
}
return JNI_VERSION_1_6;
}

View File

@ -0,0 +1,18 @@
//
// Created by Grishka on 14.08.2018.
//
#ifndef TELEGRAM_TG_VOIP_JNI_H
#define TELEGRAM_TG_VOIP_JNI_H
#include <jni.h>
#ifdef __cplusplus
extern "C"{
#endif
void tgvoipRegisterNatives(JNIEnv* env);
#ifdef __cplusplus
}
#endif
#endif //TELEGRAM_TG_VOIP_JNI_H

View File

@ -0,0 +1,348 @@
#! /bin/sh
# Wrapper for compilers which do not understand '-c -o'.
scriptversion=2018-03-07.03; # UTC
# Copyright (C) 1999-2018 Free Software Foundation, Inc.
# Written by Tom Tromey <tromey@cygnus.com>.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# This file is maintained in Automake, please report
# bugs to <bug-automake@gnu.org> or send patches to
# <automake-patches@gnu.org>.
nl='
'
# We need space, tab and new line, in precisely that order. Quoting is
# there to prevent tools from complaining about whitespace usage.
IFS=" "" $nl"
file_conv=
# func_file_conv build_file lazy
# Convert a $build file to $host form and store it in $file
# Currently only supports Windows hosts. If the determined conversion
# type is listed in (the comma separated) LAZY, no conversion will
# take place.
func_file_conv ()
{
file=$1
case $file in
/ | /[!/]*) # absolute file, and not a UNC file
if test -z "$file_conv"; then
# lazily determine how to convert abs files
case `uname -s` in
MINGW*)
file_conv=mingw
;;
CYGWIN*)
file_conv=cygwin
;;
*)
file_conv=wine
;;
esac
fi
case $file_conv/,$2, in
*,$file_conv,*)
;;
mingw/*)
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
;;
cygwin/*)
file=`cygpath -m "$file" || echo "$file"`
;;
wine/*)
file=`winepath -w "$file" || echo "$file"`
;;
esac
;;
esac
}
# func_cl_dashL linkdir
# Make cl look for libraries in LINKDIR
func_cl_dashL ()
{
func_file_conv "$1"
if test -z "$lib_path"; then
lib_path=$file
else
lib_path="$lib_path;$file"
fi
linker_opts="$linker_opts -LIBPATH:$file"
}
# func_cl_dashl library
# Do a library search-path lookup for cl
func_cl_dashl ()
{
lib=$1
found=no
save_IFS=$IFS
IFS=';'
for dir in $lib_path $LIB
do
IFS=$save_IFS
if $shared && test -f "$dir/$lib.dll.lib"; then
found=yes
lib=$dir/$lib.dll.lib
break
fi
if test -f "$dir/$lib.lib"; then
found=yes
lib=$dir/$lib.lib
break
fi
if test -f "$dir/lib$lib.a"; then
found=yes
lib=$dir/lib$lib.a
break
fi
done
IFS=$save_IFS
if test "$found" != yes; then
lib=$lib.lib
fi
}
# func_cl_wrapper cl arg...
# Adjust compile command to suit cl
func_cl_wrapper ()
{
# Assume a capable shell
lib_path=
shared=:
linker_opts=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
eat=1
case $2 in
*.o | *.[oO][bB][jJ])
func_file_conv "$2"
set x "$@" -Fo"$file"
shift
;;
*)
func_file_conv "$2"
set x "$@" -Fe"$file"
shift
;;
esac
;;
-I)
eat=1
func_file_conv "$2" mingw
set x "$@" -I"$file"
shift
;;
-I*)
func_file_conv "${1#-I}" mingw
set x "$@" -I"$file"
shift
;;
-l)
eat=1
func_cl_dashl "$2"
set x "$@" "$lib"
shift
;;
-l*)
func_cl_dashl "${1#-l}"
set x "$@" "$lib"
shift
;;
-L)
eat=1
func_cl_dashL "$2"
;;
-L*)
func_cl_dashL "${1#-L}"
;;
-static)
shared=false
;;
-Wl,*)
arg=${1#-Wl,}
save_ifs="$IFS"; IFS=','
for flag in $arg; do
IFS="$save_ifs"
linker_opts="$linker_opts $flag"
done
IFS="$save_ifs"
;;
-Xlinker)
eat=1
linker_opts="$linker_opts $2"
;;
-*)
set x "$@" "$1"
shift
;;
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
func_file_conv "$1"
set x "$@" -Tp"$file"
shift
;;
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
func_file_conv "$1" mingw
set x "$@" "$file"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -n "$linker_opts"; then
linker_opts="-link$linker_opts"
fi
exec "$@" $linker_opts
exit 1
}
eat=
case $1 in
'')
echo "$0: No command. Try '$0 --help' for more information." 1>&2
exit 1;
;;
-h | --h*)
cat <<\EOF
Usage: compile [--help] [--version] PROGRAM [ARGS]
Wrapper for compilers which do not understand '-c -o'.
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
arguments, and rename the output as expected.
If you are trying to build a whole package this is not the
right script to run: please start by reading the file 'INSTALL'.
Report bugs to <bug-automake@gnu.org>.
EOF
exit $?
;;
-v | --v*)
echo "compile $scriptversion"
exit $?
;;
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \
icl | *[/\\]icl | icl.exe | *[/\\]icl.exe )
func_cl_wrapper "$@" # Doesn't return...
;;
esac
ofile=
cfile=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
# So we strip '-o arg' only if arg is an object.
eat=1
case $2 in
*.o | *.obj)
ofile=$2
;;
*)
set x "$@" -o "$2"
shift
;;
esac
;;
*.c)
cfile=$1
set x "$@" "$1"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -z "$ofile" || test -z "$cfile"; then
# If no '-o' option was seen then we might have been invoked from a
# pattern rule where we don't need one. That is ok -- this is a
# normal compilation that the losing compiler can handle. If no
# '.c' file was seen then we are probably linking. That is also
# ok.
exec "$@"
fi
# Name of file we expect compiler to create.
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
# Create the lock directory.
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
# that we are using for the .o file. Also, base the name on the expected
# object file name, since that is what matters with a parallel build.
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
while true; do
if mkdir "$lockdir" >/dev/null 2>&1; then
break
fi
sleep 1
done
# FIXME: race condition here if user kills between mkdir and trap.
trap "rmdir '$lockdir'; exit 1" 1 2 15
# Run the compile.
"$@"
ret=$?
if test -f "$cofile"; then
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
elif test -f "${cofile}bj"; then
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
fi
rmdir "$lockdir"
exit $ret
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

1476
TMessagesProj/jni/libtgvoip2/config.guess vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,265 @@
/* config.h.in. Generated from configure.ac by autoheader. */
/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
systems. This function is required for `alloca.c' support on those systems.
*/
#undef CRAY_STACKSEG_END
/* Define to 1 if using `alloca.c'. */
#undef C_ALLOCA
/* Define to 1 if you have `alloca', as a function or macro. */
#undef HAVE_ALLOCA
/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
*/
#undef HAVE_ALLOCA_H
/* Define to 1 if you have the <arpa/inet.h> header file. */
#undef HAVE_ARPA_INET_H
/* Define to 1 if you have the `clock_gettime' function. */
#undef HAVE_CLOCK_GETTIME
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if you have the <float.h> header file. */
#undef HAVE_FLOAT_H
/* Define to 1 if you have the `floor' function. */
#undef HAVE_FLOOR
/* Define to 1 if you have the `gettimeofday' function. */
#undef HAVE_GETTIMEOFDAY
/* Define to 1 if you have the `inet_ntoa' function. */
#undef HAVE_INET_NTOA
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the `crypto' library (-lcrypto). */
#undef HAVE_LIBCRYPTO
/* Define to 1 if you have the `dl' library (-ldl). */
#undef HAVE_LIBDL
/* Define to 1 if you have the `m' library (-lm). */
#undef HAVE_LIBM
/* Define to 1 if you have the `opus' library (-lopus). */
#undef HAVE_LIBOPUS
/* Define to 1 if you have the `pthread' library (-lpthread). */
#undef HAVE_LIBPTHREAD
/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
to 0 otherwise. */
#undef HAVE_MALLOC
/* Define to 1 if you have the <malloc.h> header file. */
#undef HAVE_MALLOC_H
/* Define to 1 if you have the `memmove' function. */
#undef HAVE_MEMMOVE
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the `memset' function. */
#undef HAVE_MEMSET
/* Define to 1 if you have the <netdb.h> header file. */
#undef HAVE_NETDB_H
/* Define to 1 if you have the <netinet/in.h> header file. */
#undef HAVE_NETINET_IN_H
/* Define to 1 if the system has the type `ptrdiff_t'. */
#undef HAVE_PTRDIFF_T
/* Define to 1 if your system has a GNU libc compatible `realloc' function,
and to 0 otherwise. */
#undef HAVE_REALLOC
/* Define to 1 if you have the `select' function. */
#undef HAVE_SELECT
/* Define to 1 if you have the `socket' function. */
#undef HAVE_SOCKET
/* Define to 1 if you have the `sqrt' function. */
#undef HAVE_SQRT
/* Define to 1 if you have the <stddef.h> header file. */
#undef HAVE_STDDEF_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the `strcasecmp' function. */
#undef HAVE_STRCASECMP
/* Define to 1 if you have the `strchr' function. */
#undef HAVE_STRCHR
/* Define to 1 if you have the `strerror' function. */
#undef HAVE_STRERROR
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the `strncasecmp' function. */
#undef HAVE_STRNCASECMP
/* Define to 1 if you have the `strstr' function. */
#undef HAVE_STRSTR
/* Define to 1 if you have the `strtol' function. */
#undef HAVE_STRTOL
/* Define to 1 if you have the `strtoul' function. */
#undef HAVE_STRTOUL
/* Define to 1 if you have the <sys/ioctl.h> header file. */
#undef HAVE_SYS_IOCTL_H
/* Define to 1 if you have the <sys/socket.h> header file. */
#undef HAVE_SYS_SOCKET_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/time.h> header file. */
#undef HAVE_SYS_TIME_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the `uname' function. */
#undef HAVE_UNAME
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to 1 if you have the <wchar.h> header file. */
#undef HAVE_WCHAR_H
/* Define to 1 if the system has the type `_Bool'. */
#undef HAVE__BOOL
/* Define to the sub-directory where libtool stores uninstalled libraries. */
#undef LT_OBJDIR
/* Name of package */
#undef PACKAGE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* If using the C implementation of alloca, define if you know the
direction of stack growth for your system; otherwise it will be
automatically deduced at runtime.
STACK_DIRECTION > 0 => grows toward higher addresses
STACK_DIRECTION < 0 => grows toward lower addresses
STACK_DIRECTION = 0 => direction of growth unknown */
#undef STACK_DIRECTION
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Version number of package */
#undef VERSION
/* Define to disable ALSA support */
#undef WITHOUT_ALSA
/* Define to disable PulseAudio support */
#undef WITHOUT_PULSE
/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
#define below would cause a syntax error. */
#undef _UINT32_T
/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
#define below would cause a syntax error. */
#undef _UINT64_T
/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
#define below would cause a syntax error. */
#undef _UINT8_T
/* Define to `__inline__' or `__inline' if that's what the C compiler
calls it, or to nothing if 'inline' is not supported under any name. */
#ifndef __cplusplus
#undef inline
#endif
/* Define to the type of a signed integer type of width exactly 16 bits if
such a type exists and the standard includes do not define it. */
#undef int16_t
/* Define to the type of a signed integer type of width exactly 32 bits if
such a type exists and the standard includes do not define it. */
#undef int32_t
/* Define to the type of a signed integer type of width exactly 64 bits if
such a type exists and the standard includes do not define it. */
#undef int64_t
/* Define to the type of a signed integer type of width exactly 8 bits if such
a type exists and the standard includes do not define it. */
#undef int8_t
/* Define to rpl_malloc if the replacement function should be used. */
#undef malloc
/* Define to rpl_realloc if the replacement function should be used. */
#undef realloc
/* Define to `unsigned int' if <sys/types.h> does not define. */
#undef size_t
/* Define to `int' if <sys/types.h> does not define. */
#undef ssize_t
/* Define to the type of an unsigned integer type of width exactly 16 bits if
such a type exists and the standard includes do not define it. */
#undef uint16_t
/* Define to the type of an unsigned integer type of width exactly 32 bits if
such a type exists and the standard includes do not define it. */
#undef uint32_t
/* Define to the type of an unsigned integer type of width exactly 64 bits if
such a type exists and the standard includes do not define it. */
#undef uint64_t
/* Define to the type of an unsigned integer type of width exactly 8 bits if
such a type exists and the standard includes do not define it. */
#undef uint8_t

1801
TMessagesProj/jni/libtgvoip2/config.sub vendored Normal file

File diff suppressed because it is too large Load Diff

21185
TMessagesProj/jni/libtgvoip2/configure vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,125 @@
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
# Define version information
m4_define([libtgvoip_major_version], 2)
m4_define([libtgvoip_minor_version], 4)
m4_define([libtgvoip_patch_version], 4)
m4_define([libtgvoip_version],[libtgvoip_major_version.libtgvoip_minor_version.libtgvoip_patch_version])
AC_PREREQ([2.69])
AC_INIT([libtgvoip], [libtgvoip_version], [https://github.com/grishka/libtgvoip/issues])
AC_CONFIG_SRCDIR([config.h.in])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([subdir-objects])
AM_SILENT_RULES([yes])
LT_INIT
PKG_INSTALLDIR
# Checks for programs.
AC_PROG_CXX
AC_PROG_CC
AC_PROG_OBJCXX
AM_PROG_AS
AC_PROG_RANLIB
# Checks for libraries.
AC_CHECK_LIB([crypto], [SHA1], [], [AC_MSG_FAILURE([libssl-dev is required but not found])])
AC_CHECK_LIB([m], [floorf])
AC_CHECK_LIB([opus], [opus_decoder_create], [], [AC_MSG_FAILURE([libopus-dev is required but not found])])
AC_CHECK_LIB([pthread], [pthread_create])
AC_CANONICAL_HOST
AC_SUBST(LIBTGVOIP_MAJOR_VERSION, libtgvoip_major_version)
AC_SUBST(LIBTGVOIP_MINOR_VERSION, libtgvoip_minor_version)
AC_SUBST(LIBTGVOIP_PATCH_VERSION, libtgvoip_patch_version)
AS_CASE([$host_cpu],
[i?86], [cpu_x86=yes],
[x86_64], [cpu_x86=yes],
[arm*], [cpu_arm=yes],
[AS_ECHO("!! WARNING: libtgvoip wasn't tested with your CPU architecture ($host_cpu)")]
)
AS_CASE([$host_cpu],
[armv7*], [cpu_armv7=yes]
)
AS_ECHO("Detected CPU: $host_cpu")
AM_CONDITIONAL(TARGET_CPU_X86, test "x$cpu_x86" == xyes)
AM_CONDITIONAL(TARGET_CPU_ARM, test "x$cpu_arm" == xyes)
AM_CONDITIONAL(TARGET_CPU_ARMV7, test "x$cpu_armv7" == xyes)
AS_ECHO("Detected OS: $host_os")
AS_CASE([$host_os],
[darwin*], [os_osx=yes]
)
AM_CONDITIONAL(TARGET_OS_OSX, test "x$os_osx" == xyes)
AC_ARG_ENABLE([audio-callback], [AS_HELP_STRING([--enable-audio-callback], [enable callback-based audio I/O])], [], [enable_audio_callback=no])
AM_CONDITIONAL(ENABLE_AUDIO_CALLBACK, test "x$enable_audio_callback" == xyes)
AS_IF([test "x$os_osx" != xyes && test "x$enable_audio_callback" != xyes], [ # Linux
AC_CHECK_LIB([dl], [dlopen])
AC_ARG_WITH([pulse], [AS_HELP_STRING([--without-pulse], [disable PulseAudio support])], [], [with_pulse=yes])
AC_ARG_WITH([alsa], [AS_HELP_STRING([--without-alsa], [disable ALSA support])], [], [with_alsa=yes])
AS_IF([test "x$with_pulse" == xno && test "x$with_alsa" == xno], [
AC_MSG_FAILURE([You can only disable either ALSA or PulseAudio, not both]);
])
AS_IF([test "x$with_pulse" != xno], [
AC_CHECK_LIB([pulse], [pa_context_new], [
AS_ECHO_N( ) # what is the proper way to check for a library without linking it?
], [
AC_MSG_FAILURE([libpulse-dev is required during build, but you don't have it installed. Use --without-pulse to disable PulseAudio support.])
])
], [
AC_DEFINE([WITHOUT_PULSE], [1], [Define to disable PulseAudio support])
])
AS_IF([test "x$with_alsa" != xno], [
AC_CHECK_LIB([asound], [snd_pcm_open], [
AS_ECHO_N( ) # what is the proper way to check for a library without linking it?
], [
AC_MSG_FAILURE([libasound-dev is required during build, but you don't have it installed. Use --without-alsa to disable ALSA support.])
])
], [
AC_DEFINE([WITHOUT_ALSA], [1], [Define to disable ALSA support])
])
]);
AM_CONDITIONAL(WITH_PULSE, test "x$with_pulse" == xyes)
AM_CONDITIONAL(WITH_ALSA, test "x$with_alsa" == xyes)
AC_ARG_ENABLE([dsp], [AS_HELP_STRING([--disable-dsp], [disable signal processing (echo cancellation, noise suppression, and automatic gain control)])], [], [enable_dsp=yes])
AM_CONDITIONAL(ENABLE_DSP, test "x$enable_dsp" == xyes)
# Checks for header files.
AC_FUNC_ALLOCA
AC_CHECK_HEADERS([arpa/inet.h float.h malloc.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h sys/ioctl.h sys/socket.h sys/time.h unistd.h wchar.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_CHECK_HEADER_STDBOOL
AC_C_INLINE
AC_TYPE_INT16_T
AC_TYPE_INT32_T
AC_TYPE_INT64_T
AC_TYPE_INT8_T
AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T
AC_TYPE_UINT16_T
AC_TYPE_UINT32_T
AC_TYPE_UINT64_T
AC_TYPE_UINT8_T
AC_CHECK_TYPES([ptrdiff_t])
# Checks for library functions.
AC_FUNC_ERROR_AT_LINE
AC_FUNC_MALLOC
AC_FUNC_REALLOC
AC_CHECK_FUNCS([clock_gettime floor gettimeofday inet_ntoa memmove memset select socket sqrt strcasecmp strchr strerror strncasecmp strstr strtol strtoul uname])
AC_CONFIG_FILES([Makefile tgvoip.pc])
AC_OUTPUT

View File

@ -0,0 +1,791 @@
#! /bin/sh
# depcomp - compile a program generating dependencies as side-effects
scriptversion=2018-03-07.03; # UTC
# Copyright (C) 1999-2018 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
case $1 in
'')
echo "$0: No command. Try '$0 --help' for more information." 1>&2
exit 1;
;;
-h | --h*)
cat <<\EOF
Usage: depcomp [--help] [--version] PROGRAM [ARGS]
Run PROGRAMS ARGS to compile a file, generating dependencies
as side-effects.
Environment variables:
depmode Dependency tracking mode.
source Source file read by 'PROGRAMS ARGS'.
object Object file output by 'PROGRAMS ARGS'.
DEPDIR directory where to store dependencies.
depfile Dependency file to output.
tmpdepfile Temporary file to use when outputting dependencies.
libtool Whether libtool is used (yes/no).
Report bugs to <bug-automake@gnu.org>.
EOF
exit $?
;;
-v | --v*)
echo "depcomp $scriptversion"
exit $?
;;
esac
# Get the directory component of the given path, and save it in the
# global variables '$dir'. Note that this directory component will
# be either empty or ending with a '/' character. This is deliberate.
set_dir_from ()
{
case $1 in
*/*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
*) dir=;;
esac
}
# Get the suffix-stripped basename of the given path, and save it the
# global variable '$base'.
set_base_from ()
{
base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
}
# If no dependency file was actually created by the compiler invocation,
# we still have to create a dummy depfile, to avoid errors with the
# Makefile "include basename.Plo" scheme.
make_dummy_depfile ()
{
echo "#dummy" > "$depfile"
}
# Factor out some common post-processing of the generated depfile.
# Requires the auxiliary global variable '$tmpdepfile' to be set.
aix_post_process_depfile ()
{
# If the compiler actually managed to produce a dependency file,
# post-process it.
if test -f "$tmpdepfile"; then
# Each line is of the form 'foo.o: dependency.h'.
# Do two passes, one to just change these to
# $object: dependency.h
# and one to simply output
# dependency.h:
# which is needed to avoid the deleted-header problem.
{ sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
} > "$depfile"
rm -f "$tmpdepfile"
else
make_dummy_depfile
fi
}
# A tabulation character.
tab=' '
# A newline character.
nl='
'
# Character ranges might be problematic outside the C locale.
# These definitions help.
upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
lower=abcdefghijklmnopqrstuvwxyz
digits=0123456789
alpha=${upper}${lower}
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
echo "depcomp: Variables source, object and depmode must be set" 1>&2
exit 1
fi
# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
depfile=${depfile-`echo "$object" |
sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
rm -f "$tmpdepfile"
# Avoid interferences from the environment.
gccflag= dashmflag=
# Some modes work just like other modes, but use different flags. We
# parameterize here, but still list the modes in the big case below,
# to make depend.m4 easier to write. Note that we *cannot* use a case
# here, because this file can only contain one case statement.
if test "$depmode" = hp; then
# HP compiler uses -M and no extra arg.
gccflag=-M
depmode=gcc
fi
if test "$depmode" = dashXmstdout; then
# This is just like dashmstdout with a different argument.
dashmflag=-xM
depmode=dashmstdout
fi
cygpath_u="cygpath -u -f -"
if test "$depmode" = msvcmsys; then
# This is just like msvisualcpp but w/o cygpath translation.
# Just convert the backslash-escaped backslashes to single forward
# slashes to satisfy depend.m4
cygpath_u='sed s,\\\\,/,g'
depmode=msvisualcpp
fi
if test "$depmode" = msvc7msys; then
# This is just like msvc7 but w/o cygpath translation.
# Just convert the backslash-escaped backslashes to single forward
# slashes to satisfy depend.m4
cygpath_u='sed s,\\\\,/,g'
depmode=msvc7
fi
if test "$depmode" = xlc; then
# IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
gccflag=-qmakedep=gcc,-MF
depmode=gcc
fi
case "$depmode" in
gcc3)
## gcc 3 implements dependency tracking that does exactly what
## we want. Yay! Note: for some reason libtool 1.4 doesn't like
## it if -MD -MP comes after the -MF stuff. Hmm.
## Unfortunately, FreeBSD c89 acceptance of flags depends upon
## the command line argument order; so add the flags where they
## appear in depend2.am. Note that the slowdown incurred here
## affects only configure: in makefiles, %FASTDEP% shortcuts this.
for arg
do
case $arg in
-c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
*) set fnord "$@" "$arg" ;;
esac
shift # fnord
shift # $arg
done
"$@"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
mv "$tmpdepfile" "$depfile"
;;
gcc)
## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
## (see the conditional assignment to $gccflag above).
## There are various ways to get dependency output from gcc. Here's
## why we pick this rather obscure method:
## - Don't want to use -MD because we'd like the dependencies to end
## up in a subdir. Having to rename by hand is ugly.
## (We might end up doing this anyway to support other compilers.)
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
## -MM, not -M (despite what the docs say). Also, it might not be
## supported by the other compilers which use the 'gcc' depmode.
## - Using -M directly means running the compiler twice (even worse
## than renaming).
if test -z "$gccflag"; then
gccflag=-MD,
fi
"$@" -Wp,"$gccflag$tmpdepfile"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
echo "$object : \\" > "$depfile"
# The second -e expression handles DOS-style file names with drive
# letters.
sed -e 's/^[^:]*: / /' \
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
## This next piece of magic avoids the "deleted header file" problem.
## The problem is that when a header file which appears in a .P file
## is deleted, the dependency causes make to die (because there is
## typically no way to rebuild the header). We avoid this by adding
## dummy dependencies for each header file. Too bad gcc doesn't do
## this for us directly.
## Some versions of gcc put a space before the ':'. On the theory
## that the space means something, we add a space to the output as
## well. hp depmode also adds that space, but also prefixes the VPATH
## to the object. Take care to not repeat it in the output.
## Some versions of the HPUX 10.20 sed can't process this invocation
## correctly. Breaking it into two sed invocations is a workaround.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
hp)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
sgi)
if test "$libtool" = yes; then
"$@" "-Wp,-MDupdate,$tmpdepfile"
else
"$@" -MDupdate "$tmpdepfile"
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
echo "$object : \\" > "$depfile"
# Clip off the initial element (the dependent). Don't try to be
# clever and replace this with sed code, as IRIX sed won't handle
# lines with more than a fixed number of characters (4096 in
# IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
# the IRIX cc adds comments like '#:fec' to the end of the
# dependency line.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
| tr "$nl" ' ' >> "$depfile"
echo >> "$depfile"
# The second pass generates a dummy entry for each header file.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
>> "$depfile"
else
make_dummy_depfile
fi
rm -f "$tmpdepfile"
;;
xlc)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
aix)
# The C for AIX Compiler uses -M and outputs the dependencies
# in a .u file. In older versions, this file always lives in the
# current directory. Also, the AIX compiler puts '$object:' at the
# start of each line; $object doesn't have directory information.
# Version 6 uses the directory in both cases.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
tmpdepfile1=$dir$base.u
tmpdepfile2=$base.u
tmpdepfile3=$dir.libs/$base.u
"$@" -Wc,-M
else
tmpdepfile1=$dir$base.u
tmpdepfile2=$dir$base.u
tmpdepfile3=$dir$base.u
"$@" -M
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
do
test -f "$tmpdepfile" && break
done
aix_post_process_depfile
;;
tcc)
# tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
# FIXME: That version still under development at the moment of writing.
# Make that this statement remains true also for stable, released
# versions.
# It will wrap lines (doesn't matter whether long or short) with a
# trailing '\', as in:
#
# foo.o : \
# foo.c \
# foo.h \
#
# It will put a trailing '\' even on the last line, and will use leading
# spaces rather than leading tabs (at least since its commit 0394caf7
# "Emit spaces for -MD").
"$@" -MD -MF "$tmpdepfile"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
# Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
# We have to change lines of the first kind to '$object: \'.
sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
# And for each line of the second kind, we have to emit a 'dep.h:'
# dummy dependency, to avoid the deleted-header problem.
sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
rm -f "$tmpdepfile"
;;
## The order of this option in the case statement is important, since the
## shell code in configure will try each of these formats in the order
## listed in this file. A plain '-MD' option would be understood by many
## compilers, so we must ensure this comes after the gcc and icc options.
pgcc)
# Portland's C compiler understands '-MD'.
# Will always output deps to 'file.d' where file is the root name of the
# source file under compilation, even if file resides in a subdirectory.
# The object file name does not affect the name of the '.d' file.
# pgcc 10.2 will output
# foo.o: sub/foo.c sub/foo.h
# and will wrap long lines using '\' :
# foo.o: sub/foo.c ... \
# sub/foo.h ... \
# ...
set_dir_from "$object"
# Use the source, not the object, to determine the base name, since
# that's sadly what pgcc will do too.
set_base_from "$source"
tmpdepfile=$base.d
# For projects that build the same source file twice into different object
# files, the pgcc approach of using the *source* file root name can cause
# problems in parallel builds. Use a locking strategy to avoid stomping on
# the same $tmpdepfile.
lockdir=$base.d-lock
trap "
echo '$0: caught signal, cleaning up...' >&2
rmdir '$lockdir'
exit 1
" 1 2 13 15
numtries=100
i=$numtries
while test $i -gt 0; do
# mkdir is a portable test-and-set.
if mkdir "$lockdir" 2>/dev/null; then
# This process acquired the lock.
"$@" -MD
stat=$?
# Release the lock.
rmdir "$lockdir"
break
else
# If the lock is being held by a different process, wait
# until the winning process is done or we timeout.
while test -d "$lockdir" && test $i -gt 0; do
sleep 1
i=`expr $i - 1`
done
fi
i=`expr $i - 1`
done
trap - 1 2 13 15
if test $i -le 0; then
echo "$0: failed to acquire lock after $numtries attempts" >&2
echo "$0: check lockdir '$lockdir'" >&2
exit 1
fi
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
# Each line is of the form `foo.o: dependent.h',
# or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
# Do two passes, one to just change these to
# `$object: dependent.h' and one to simply `dependent.h:'.
sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process this invocation
# correctly. Breaking it into two sed invocations is a workaround.
sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
hp2)
# The "hp" stanza above does not work with aCC (C++) and HP's ia64
# compilers, which have integrated preprocessors. The correct option
# to use with these is +Maked; it writes dependencies to a file named
# 'foo.d', which lands next to the object file, wherever that
# happens to be.
# Much of this is similar to the tru64 case; see comments there.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir.libs/$base.d
"$@" -Wc,+Maked
else
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir$base.d
"$@" +Maked
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
do
test -f "$tmpdepfile" && break
done
if test -f "$tmpdepfile"; then
sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
# Add 'dependent.h:' lines.
sed -ne '2,${
s/^ *//
s/ \\*$//
s/$/:/
p
}' "$tmpdepfile" >> "$depfile"
else
make_dummy_depfile
fi
rm -f "$tmpdepfile" "$tmpdepfile2"
;;
tru64)
# The Tru64 compiler uses -MD to generate dependencies as a side
# effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
# dependencies in 'foo.d' instead, so we check for that too.
# Subdirectories are respected.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
# Libtool generates 2 separate objects for the 2 libraries. These
# two compilations output dependencies in $dir.libs/$base.o.d and
# in $dir$base.o.d. We have to check for both files, because
# one of the two compilations can be disabled. We should prefer
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
# automatically cleaned when .libs/ is deleted, while ignoring
# the former would cause a distcleancheck panic.
tmpdepfile1=$dir$base.o.d # libtool 1.5
tmpdepfile2=$dir.libs/$base.o.d # Likewise.
tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504
"$@" -Wc,-MD
else
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir$base.d
tmpdepfile3=$dir$base.d
"$@" -MD
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
do
test -f "$tmpdepfile" && break
done
# Same post-processing that is required for AIX mode.
aix_post_process_depfile
;;
msvc7)
if test "$libtool" = yes; then
showIncludes=-Wc,-showIncludes
else
showIncludes=-showIncludes
fi
"$@" $showIncludes > "$tmpdepfile"
stat=$?
grep -v '^Note: including file: ' "$tmpdepfile"
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
echo "$object : \\" > "$depfile"
# The first sed program below extracts the file names and escapes
# backslashes for cygpath. The second sed program outputs the file
# name when reading, but also accumulates all include files in the
# hold buffer in order to output them again at the end. This only
# works with sed implementations that can handle large buffers.
sed < "$tmpdepfile" -n '
/^Note: including file: *\(.*\)/ {
s//\1/
s/\\/\\\\/g
p
}' | $cygpath_u | sort -u | sed -n '
s/ /\\ /g
s/\(.*\)/'"$tab"'\1 \\/p
s/.\(.*\) \\/\1:/
H
$ {
s/.*/'"$tab"'/
G
p
}' >> "$depfile"
echo >> "$depfile" # make sure the fragment doesn't end with a backslash
rm -f "$tmpdepfile"
;;
msvc7msys)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
#nosideeffect)
# This comment above is used by automake to tell side-effect
# dependency tracking mechanisms from slower ones.
dashmstdout)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout, regardless of -o.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# Remove '-o $object'.
IFS=" "
for arg
do
case $arg in
-o)
shift
;;
$object)
shift
;;
*)
set fnord "$@" "$arg"
shift # fnord
shift # $arg
;;
esac
done
test -z "$dashmflag" && dashmflag=-M
# Require at least two characters before searching for ':'
# in the target name. This is to cope with DOS-style filenames:
# a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
"$@" $dashmflag |
sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
rm -f "$depfile"
cat < "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process this sed invocation
# correctly. Breaking it into two sed invocations is a workaround.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
dashXmstdout)
# This case only exists to satisfy depend.m4. It is never actually
# run, as this mode is specially recognized in the preamble.
exit 1
;;
makedepend)
"$@" || exit $?
# Remove any Libtool call
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# X makedepend
shift
cleared=no eat=no
for arg
do
case $cleared in
no)
set ""; shift
cleared=yes ;;
esac
if test $eat = yes; then
eat=no
continue
fi
case "$arg" in
-D*|-I*)
set fnord "$@" "$arg"; shift ;;
# Strip any option that makedepend may not understand. Remove
# the object too, otherwise makedepend will parse it as a source file.
-arch)
eat=yes ;;
-*|$object)
;;
*)
set fnord "$@" "$arg"; shift ;;
esac
done
obj_suffix=`echo "$object" | sed 's/^.*\././'`
touch "$tmpdepfile"
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
rm -f "$depfile"
# makedepend may prepend the VPATH from the source file name to the object.
# No need to regex-escape $object, excess matching of '.' is harmless.
sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process the last invocation
# correctly. Breaking it into two sed invocations is a workaround.
sed '1,2d' "$tmpdepfile" \
| tr ' ' "$nl" \
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile" "$tmpdepfile".bak
;;
cpp)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# Remove '-o $object'.
IFS=" "
for arg
do
case $arg in
-o)
shift
;;
$object)
shift
;;
*)
set fnord "$@" "$arg"
shift # fnord
shift # $arg
;;
esac
done
"$@" -E \
| sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
| sed '$ s: \\$::' > "$tmpdepfile"
rm -f "$depfile"
echo "$object : \\" > "$depfile"
cat < "$tmpdepfile" >> "$depfile"
sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
msvisualcpp)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
IFS=" "
for arg
do
case "$arg" in
-o)
shift
;;
$object)
shift
;;
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
set fnord "$@"
shift
shift
;;
*)
set fnord "$@" "$arg"
shift
shift
;;
esac
done
"$@" -E 2>/dev/null |
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
rm -f "$depfile"
echo "$object : \\" > "$depfile"
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
echo "$tab" >> "$depfile"
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
rm -f "$tmpdepfile"
;;
msvcmsys)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
none)
exec "$@"
;;
*)
echo "Unknown depmode $depmode" 1>&2
exit 1
;;
esac
exit 0
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

View File

@ -0,0 +1,518 @@
#!/bin/sh
# install - install a program, script, or datafile
scriptversion=2018-03-11.20; # UTC
# This originates from X11R5 (mit/util/scripts/install.sh), which was
# later released in X11R6 (xc/config/util/install.sh) with the
# following copyright and license.
#
# Copyright (C) 1994 X Consortium
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# 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
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Except as contained in this notice, the name of the X Consortium shall not
# be used in advertising or otherwise to promote the sale, use or other deal-
# ings in this Software without prior written authorization from the X Consor-
# tium.
#
#
# FSF changes to this file are in the public domain.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# 'make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch.
tab=' '
nl='
'
IFS=" $tab$nl"
# Set DOITPROG to "echo" to test this script.
doit=${DOITPROG-}
doit_exec=${doit:-exec}
# Put in absolute file names if you don't have them in your path;
# or use environment vars.
chgrpprog=${CHGRPPROG-chgrp}
chmodprog=${CHMODPROG-chmod}
chownprog=${CHOWNPROG-chown}
cmpprog=${CMPPROG-cmp}
cpprog=${CPPROG-cp}
mkdirprog=${MKDIRPROG-mkdir}
mvprog=${MVPROG-mv}
rmprog=${RMPROG-rm}
stripprog=${STRIPPROG-strip}
posix_mkdir=
# Desired mode of installed file.
mode=0755
chgrpcmd=
chmodcmd=$chmodprog
chowncmd=
mvcmd=$mvprog
rmcmd="$rmprog -f"
stripcmd=
src=
dst=
dir_arg=
dst_arg=
copy_on_change=false
is_target_a_directory=possibly
usage="\
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
or: $0 [OPTION]... SRCFILES... DIRECTORY
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
or: $0 [OPTION]... -d DIRECTORIES...
In the 1st form, copy SRCFILE to DSTFILE.
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
In the 4th, create DIRECTORIES.
Options:
--help display this help and exit.
--version display version info and exit.
-c (ignored)
-C install only if different (preserve the last data modification time)
-d create directories instead of installing files.
-g GROUP $chgrpprog installed files to GROUP.
-m MODE $chmodprog installed files to MODE.
-o USER $chownprog installed files to USER.
-s $stripprog installed files.
-t DIRECTORY install into DIRECTORY.
-T report an error if DSTFILE is a directory.
Environment variables override the default commands:
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
RMPROG STRIPPROG
"
while test $# -ne 0; do
case $1 in
-c) ;;
-C) copy_on_change=true;;
-d) dir_arg=true;;
-g) chgrpcmd="$chgrpprog $2"
shift;;
--help) echo "$usage"; exit $?;;
-m) mode=$2
case $mode in
*' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
echo "$0: invalid mode: $mode" >&2
exit 1;;
esac
shift;;
-o) chowncmd="$chownprog $2"
shift;;
-s) stripcmd=$stripprog;;
-t)
is_target_a_directory=always
dst_arg=$2
# Protect names problematic for 'test' and other utilities.
case $dst_arg in
-* | [=\(\)!]) dst_arg=./$dst_arg;;
esac
shift;;
-T) is_target_a_directory=never;;
--version) echo "$0 $scriptversion"; exit $?;;
--) shift
break;;
-*) echo "$0: invalid option: $1" >&2
exit 1;;
*) break;;
esac
shift
done
# We allow the use of options -d and -T together, by making -d
# take the precedence; this is for compatibility with GNU install.
if test -n "$dir_arg"; then
if test -n "$dst_arg"; then
echo "$0: target directory not allowed when installing a directory." >&2
exit 1
fi
fi
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
# When -d is used, all remaining arguments are directories to create.
# When -t is used, the destination is already specified.
# Otherwise, the last argument is the destination. Remove it from $@.
for arg
do
if test -n "$dst_arg"; then
# $@ is not empty: it contains at least $arg.
set fnord "$@" "$dst_arg"
shift # fnord
fi
shift # arg
dst_arg=$arg
# Protect names problematic for 'test' and other utilities.
case $dst_arg in
-* | [=\(\)!]) dst_arg=./$dst_arg;;
esac
done
fi
if test $# -eq 0; then
if test -z "$dir_arg"; then
echo "$0: no input file specified." >&2
exit 1
fi
# It's OK to call 'install-sh -d' without argument.
# This can happen when creating conditional directories.
exit 0
fi
if test -z "$dir_arg"; then
if test $# -gt 1 || test "$is_target_a_directory" = always; then
if test ! -d "$dst_arg"; then
echo "$0: $dst_arg: Is not a directory." >&2
exit 1
fi
fi
fi
if test -z "$dir_arg"; then
do_exit='(exit $ret); exit $ret'
trap "ret=129; $do_exit" 1
trap "ret=130; $do_exit" 2
trap "ret=141; $do_exit" 13
trap "ret=143; $do_exit" 15
# Set umask so as not to create temps with too-generous modes.
# However, 'strip' requires both read and write access to temps.
case $mode in
# Optimize common cases.
*644) cp_umask=133;;
*755) cp_umask=22;;
*[0-7])
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw='% 200'
fi
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
*)
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw=,u+rw
fi
cp_umask=$mode$u_plus_rw;;
esac
fi
for src
do
# Protect names problematic for 'test' and other utilities.
case $src in
-* | [=\(\)!]) src=./$src;;
esac
if test -n "$dir_arg"; then
dst=$src
dstdir=$dst
test -d "$dstdir"
dstdir_status=$?
else
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if test ! -f "$src" && test ! -d "$src"; then
echo "$0: $src does not exist." >&2
exit 1
fi
if test -z "$dst_arg"; then
echo "$0: no destination specified." >&2
exit 1
fi
dst=$dst_arg
# If destination is a directory, append the input filename.
if test -d "$dst"; then
if test "$is_target_a_directory" = never; then
echo "$0: $dst_arg: Is a directory" >&2
exit 1
fi
dstdir=$dst
dstbase=`basename "$src"`
case $dst in
*/) dst=$dst$dstbase;;
*) dst=$dst/$dstbase;;
esac
dstdir_status=0
else
dstdir=`dirname "$dst"`
test -d "$dstdir"
dstdir_status=$?
fi
fi
case $dstdir in
*/) dstdirslash=$dstdir;;
*) dstdirslash=$dstdir/;;
esac
obsolete_mkdir_used=false
if test $dstdir_status != 0; then
case $posix_mkdir in
'')
# Create intermediate dirs using mode 755 as modified by the umask.
# This is like FreeBSD 'install' as of 1997-10-28.
umask=`umask`
case $stripcmd.$umask in
# Optimize common cases.
*[2367][2367]) mkdir_umask=$umask;;
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
*[0-7])
mkdir_umask=`expr $umask + 22 \
- $umask % 100 % 40 + $umask % 20 \
- $umask % 10 % 4 + $umask % 2
`;;
*) mkdir_umask=$umask,go-w;;
esac
# With -d, create the new directory with the user-specified mode.
# Otherwise, rely on $mkdir_umask.
if test -n "$dir_arg"; then
mkdir_mode=-m$mode
else
mkdir_mode=
fi
posix_mkdir=false
case $umask in
*[123567][0-7][0-7])
# POSIX mkdir -p sets u+wx bits regardless of umask, which
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
;;
*)
# Note that $RANDOM variable is not portable (e.g. dash); Use it
# here however when possible just to lower collision chance.
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0
# Because "mkdir -p" follows existing symlinks and we likely work
# directly in world-writeable /tmp, make sure that the '$tmpdir'
# directory is successfully created first before we actually test
# 'mkdir -p' feature.
if (umask $mkdir_umask &&
$mkdirprog $mkdir_mode "$tmpdir" &&
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
then
if test -z "$dir_arg" || {
# Check for POSIX incompatibilities with -m.
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
# other-writable bit of parent directory when it shouldn't.
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
test_tmpdir="$tmpdir/a"
ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
case $ls_ld_tmpdir in
d????-?r-*) different_mode=700;;
d????-?--*) different_mode=755;;
*) false;;
esac &&
$mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
}
}
then posix_mkdir=:
fi
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
else
# Remove any dirs left behind by ancient mkdir implementations.
rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
fi
trap '' 0;;
esac;;
esac
if
$posix_mkdir && (
umask $mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
)
then :
else
# The umask is ridiculous, or mkdir does not conform to POSIX,
# or it failed possibly due to a race condition. Create the
# directory the slow way, step by step, checking for races as we go.
case $dstdir in
/*) prefix='/';;
[-=\(\)!]*) prefix='./';;
*) prefix='';;
esac
oIFS=$IFS
IFS=/
set -f
set fnord $dstdir
shift
set +f
IFS=$oIFS
prefixes=
for d
do
test X"$d" = X && continue
prefix=$prefix$d
if test -d "$prefix"; then
prefixes=
else
if $posix_mkdir; then
(umask=$mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
# Don't fail if two instances are running concurrently.
test -d "$prefix" || exit 1
else
case $prefix in
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
*) qprefix=$prefix;;
esac
prefixes="$prefixes '$qprefix'"
fi
fi
prefix=$prefix/
done
if test -n "$prefixes"; then
# Don't fail if two instances are running concurrently.
(umask $mkdir_umask &&
eval "\$doit_exec \$mkdirprog $prefixes") ||
test -d "$dstdir" || exit 1
obsolete_mkdir_used=true
fi
fi
fi
if test -n "$dir_arg"; then
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
else
# Make a couple of temp file names in the proper directory.
dsttmp=${dstdirslash}_inst.$$_
rmtmp=${dstdirslash}_rm.$$_
# Trap to clean up those temp files at exit.
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
# Copy the file name to the temp name.
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
# and set any options; do chmod last to preserve setuid bits.
#
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $cpprog $src $dsttmp" command.
#
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
# If -C, don't bother to copy if it wouldn't change the file.
if $copy_on_change &&
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
set -f &&
set X $old && old=:$2:$4:$5:$6 &&
set X $new && new=:$2:$4:$5:$6 &&
set +f &&
test "$old" = "$new" &&
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
then
rm -f "$dsttmp"
else
# Rename the file to the real destination.
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
# The rename failed, perhaps because mv can't rename something else
# to itself, or perhaps because mv is so ancient that it does not
# support -f.
{
# Now remove or move aside any old file at destination location.
# We try this two ways since rm can't unlink itself on some
# systems and the destination file might be busy for other
# reasons. In this case, the final cleanup might fail but the new
# file should still install successfully.
{
test ! -f "$dst" ||
$doit $rmcmd -f "$dst" 2>/dev/null ||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
} ||
{ echo "$0: cannot unlink or rename $dst" >&2
(exit 1); exit 1
}
} &&
# Now rename the file to the real destination.
$doit $mvcmd "$dsttmp" "$dst"
}
fi || exit 1
trap '' 0
fi
done
# Local variables:
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

View File

@ -0,0 +1,795 @@
/* Copyright (c) 2013 Dropbox, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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 OR COPYRIGHT HOLDERS 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.
*/
#include "json11.hpp"
#include <cassert>
#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <limits>
#include <sstream>
#include <locale>
namespace json11 {
static const int max_depth = 200;
using std::string;
using std::vector;
using std::map;
using std::make_shared;
using std::initializer_list;
using std::move;
/* Helper for representing null - just a do-nothing struct, plus comparison
* operators so the helpers in JsonValue work. We can't use nullptr_t because
* it may not be orderable.
*/
struct NullStruct {
bool operator==(NullStruct) const { return true; }
bool operator<(NullStruct) const { return false; }
};
/* * * * * * * * * * * * * * * * * * * *
* Serialization
*/
static void dump(NullStruct, string &out) {
out += "null";
}
static void dump(double value, string &out) {
if (std::isfinite(value)) {
std::ostringstream stm;
stm.imbue(std::locale("C"));
stm << value;
out += stm.str();
} else {
out += "null";
}
}
static void dump(int value, string &out) {
char buf[32];
snprintf(buf, sizeof buf, "%d", value);
out += buf;
}
static void dump(bool value, string &out) {
out += value ? "true" : "false";
}
static void dump(const string &value, string &out) {
out += '"';
for (size_t i = 0; i < value.length(); i++) {
const char ch = value[i];
if (ch == '\\') {
out += "\\\\";
} else if (ch == '"') {
out += "\\\"";
} else if (ch == '\b') {
out += "\\b";
} else if (ch == '\f') {
out += "\\f";
} else if (ch == '\n') {
out += "\\n";
} else if (ch == '\r') {
out += "\\r";
} else if (ch == '\t') {
out += "\\t";
} else if (static_cast<uint8_t>(ch) <= 0x1f) {
char buf[8];
snprintf(buf, sizeof buf, "\\u%04x", ch);
out += buf;
} else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
&& static_cast<uint8_t>(value[i+2]) == 0xa8) {
out += "\\u2028";
i += 2;
} else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80
&& static_cast<uint8_t>(value[i+2]) == 0xa9) {
out += "\\u2029";
i += 2;
} else {
out += ch;
}
}
out += '"';
}
static void dump(const Json::array &values, string &out) {
bool first = true;
out += "[";
for (const auto &value : values) {
if (!first)
out += ", ";
value.dump(out);
first = false;
}
out += "]";
}
static void dump(const Json::object &values, string &out) {
bool first = true;
out += "{";
for (const auto &kv : values) {
if (!first)
out += ", ";
dump(kv.first, out);
out += ": ";
kv.second.dump(out);
first = false;
}
out += "}";
}
void Json::dump(string &out) const {
m_ptr->dump(out);
}
/* * * * * * * * * * * * * * * * * * * *
* Value wrappers
*/
template <Json::Type tag, typename T>
class Value : public JsonValue {
protected:
// Constructors
explicit Value(const T &value) : m_value(value) {}
explicit Value(T &&value) : m_value(move(value)) {}
// Get type tag
Json::Type type() const override {
return tag;
}
// Comparisons
bool equals(const JsonValue * other) const override {
return m_value == static_cast<const Value<tag, T> *>(other)->m_value;
}
bool less(const JsonValue * other) const override {
return m_value < static_cast<const Value<tag, T> *>(other)->m_value;
}
const T m_value;
void dump(string &out) const override { json11::dump(m_value, out); }
};
class JsonDouble final : public Value<Json::NUMBER, double> {
double number_value() const override { return m_value; }
int int_value() const override { return static_cast<int>(m_value); }
bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
bool less(const JsonValue * other) const override { return m_value < other->number_value(); }
public:
explicit JsonDouble(double value) : Value(value) {}
};
class JsonInt final : public Value<Json::NUMBER, int> {
double number_value() const override { return m_value; }
int int_value() const override { return m_value; }
bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }
bool less(const JsonValue * other) const override { return m_value < other->number_value(); }
public:
explicit JsonInt(int value) : Value(value) {}
};
class JsonBoolean final : public Value<Json::BOOL, bool> {
bool bool_value() const override { return m_value; }
public:
explicit JsonBoolean(bool value) : Value(value) {}
};
class JsonString final : public Value<Json::STRING, string> {
const string &string_value() const override { return m_value; }
public:
explicit JsonString(const string &value) : Value(value) {}
explicit JsonString(string &&value) : Value(move(value)) {}
};
class JsonArray final : public Value<Json::ARRAY, Json::array> {
const Json::array &array_items() const override { return m_value; }
const Json & operator[](size_t i) const override;
public:
explicit JsonArray(const Json::array &value) : Value(value) {}
explicit JsonArray(Json::array &&value) : Value(move(value)) {}
};
class JsonObject final : public Value<Json::OBJECT, Json::object> {
const Json::object &object_items() const override { return m_value; }
const Json & operator[](const string &key) const override;
public:
explicit JsonObject(const Json::object &value) : Value(value) {}
explicit JsonObject(Json::object &&value) : Value(move(value)) {}
};
class JsonNull final : public Value<Json::NUL, NullStruct> {
public:
JsonNull() : Value({}) {}
};
/* * * * * * * * * * * * * * * * * * * *
* Static globals - static-init-safe
*/
struct Statics {
const std::shared_ptr<JsonValue> null = make_shared<JsonNull>();
const std::shared_ptr<JsonValue> t = make_shared<JsonBoolean>(true);
const std::shared_ptr<JsonValue> f = make_shared<JsonBoolean>(false);
const string empty_string;
const vector<Json> empty_vector;
const map<string, Json> empty_map;
Statics() {}
};
static const Statics & statics() {
static const Statics s {};
return s;
}
static const Json & static_null() {
// This has to be separate, not in Statics, because Json() accesses statics().null.
static const Json json_null;
return json_null;
}
/* * * * * * * * * * * * * * * * * * * *
* Constructors
*/
Json::Json() noexcept : m_ptr(statics().null) {}
Json::Json(std::nullptr_t) noexcept : m_ptr(statics().null) {}
Json::Json(double value) : m_ptr(make_shared<JsonDouble>(value)) {}
Json::Json(int value) : m_ptr(make_shared<JsonInt>(value)) {}
Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {}
Json::Json(const string &value) : m_ptr(make_shared<JsonString>(value)) {}
Json::Json(string &&value) : m_ptr(make_shared<JsonString>(move(value))) {}
Json::Json(const char * value) : m_ptr(make_shared<JsonString>(value)) {}
Json::Json(const Json::array &values) : m_ptr(make_shared<JsonArray>(values)) {}
Json::Json(Json::array &&values) : m_ptr(make_shared<JsonArray>(move(values))) {}
Json::Json(const Json::object &values) : m_ptr(make_shared<JsonObject>(values)) {}
Json::Json(Json::object &&values) : m_ptr(make_shared<JsonObject>(move(values))) {}
/* * * * * * * * * * * * * * * * * * * *
* Accessors
*/
Json::Type Json::type() const { return m_ptr->type(); }
double Json::number_value() const { return m_ptr->number_value(); }
int Json::int_value() const { return m_ptr->int_value(); }
bool Json::bool_value() const { return m_ptr->bool_value(); }
const string & Json::string_value() const { return m_ptr->string_value(); }
const vector<Json> & Json::array_items() const { return m_ptr->array_items(); }
const map<string, Json> & Json::object_items() const { return m_ptr->object_items(); }
const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; }
const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; }
double JsonValue::number_value() const { return 0; }
int JsonValue::int_value() const { return 0; }
bool JsonValue::bool_value() const { return false; }
const string & JsonValue::string_value() const { return statics().empty_string; }
const vector<Json> & JsonValue::array_items() const { return statics().empty_vector; }
const map<string, Json> & JsonValue::object_items() const { return statics().empty_map; }
const Json & JsonValue::operator[] (size_t) const { return static_null(); }
const Json & JsonValue::operator[] (const string &) const { return static_null(); }
const Json & JsonObject::operator[] (const string &key) const {
auto iter = m_value.find(key);
return (iter == m_value.end()) ? static_null() : iter->second;
}
const Json & JsonArray::operator[] (size_t i) const {
if (i >= m_value.size()) return static_null();
else return m_value[i];
}
/* * * * * * * * * * * * * * * * * * * *
* Comparison
*/
bool Json::operator== (const Json &other) const {
if (m_ptr == other.m_ptr)
return true;
if (m_ptr->type() != other.m_ptr->type())
return false;
return m_ptr->equals(other.m_ptr.get());
}
bool Json::operator< (const Json &other) const {
if (m_ptr == other.m_ptr)
return false;
if (m_ptr->type() != other.m_ptr->type())
return m_ptr->type() < other.m_ptr->type();
return m_ptr->less(other.m_ptr.get());
}
/* * * * * * * * * * * * * * * * * * * *
* Parsing
*/
/* esc(c)
*
* Format char c suitable for printing in an error message.
*/
static inline string esc(char c) {
char buf[12];
if (static_cast<uint8_t>(c) >= 0x20 && static_cast<uint8_t>(c) <= 0x7f) {
snprintf(buf, sizeof buf, "'%c' (%d)", c, c);
} else {
snprintf(buf, sizeof buf, "(%d)", c);
}
return string(buf);
}
static inline bool in_range(long x, long lower, long upper) {
return (x >= lower && x <= upper);
}
namespace {
/* JsonParser
*
* Object that tracks all state of an in-progress parse.
*/
struct JsonParser final {
/* State
*/
const string &str;
size_t i;
string &err;
bool failed;
const JsonParse strategy;
/* fail(msg, err_ret = Json())
*
* Mark this parse as failed.
*/
Json fail(string &&msg) {
return fail(move(msg), Json());
}
template <typename T>
T fail(string &&msg, const T err_ret) {
if (!failed)
err = std::move(msg);
failed = true;
return err_ret;
}
/* consume_whitespace()
*
* Advance until the current character is non-whitespace.
*/
void consume_whitespace() {
while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t')
i++;
}
/* consume_comment()
*
* Advance comments (c-style inline and multiline).
*/
bool consume_comment() {
bool comment_found = false;
if (str[i] == '/') {
i++;
if (i == str.size())
return fail("unexpected end of input after start of comment", false);
if (str[i] == '/') { // inline comment
i++;
// advance until next line, or end of input
while (i < str.size() && str[i] != '\n') {
i++;
}
comment_found = true;
}
else if (str[i] == '*') { // multiline comment
i++;
if (i > str.size()-2)
return fail("unexpected end of input inside multi-line comment", false);
// advance until closing tokens
while (!(str[i] == '*' && str[i+1] == '/')) {
i++;
if (i > str.size()-2)
return fail(
"unexpected end of input inside multi-line comment", false);
}
i += 2;
comment_found = true;
}
else
return fail("malformed comment", false);
}
return comment_found;
}
/* consume_garbage()
*
* Advance until the current character is non-whitespace and non-comment.
*/
void consume_garbage() {
consume_whitespace();
if(strategy == JsonParse::COMMENTS) {
bool comment_found = false;
do {
comment_found = consume_comment();
if (failed) return;
consume_whitespace();
}
while(comment_found);
}
}
/* get_next_token()
*
* Return the next non-whitespace character. If the end of the input is reached,
* flag an error and return 0.
*/
char get_next_token() {
consume_garbage();
if (failed) return (char)0;
if (i == str.size())
return fail("unexpected end of input", (char)0);
return str[i++];
}
/* encode_utf8(pt, out)
*
* Encode pt as UTF-8 and add it to out.
*/
void encode_utf8(long pt, string & out) {
if (pt < 0)
return;
if (pt < 0x80) {
out += static_cast<char>(pt);
} else if (pt < 0x800) {
out += static_cast<char>((pt >> 6) | 0xC0);
out += static_cast<char>((pt & 0x3F) | 0x80);
} else if (pt < 0x10000) {
out += static_cast<char>((pt >> 12) | 0xE0);
out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
out += static_cast<char>((pt & 0x3F) | 0x80);
} else {
out += static_cast<char>((pt >> 18) | 0xF0);
out += static_cast<char>(((pt >> 12) & 0x3F) | 0x80);
out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);
out += static_cast<char>((pt & 0x3F) | 0x80);
}
}
/* parse_string()
*
* Parse a string, starting at the current position.
*/
string parse_string() {
string out;
long last_escaped_codepoint = -1;
while (true) {
if (i == str.size())
return fail("unexpected end of input in string", "");
char ch = str[i++];
if (ch == '"') {
encode_utf8(last_escaped_codepoint, out);
return out;
}
if (in_range(ch, 0, 0x1f))
return fail("unescaped " + esc(ch) + " in string", "");
// The usual case: non-escaped characters
if (ch != '\\') {
encode_utf8(last_escaped_codepoint, out);
last_escaped_codepoint = -1;
out += ch;
continue;
}
// Handle escapes
if (i == str.size())
return fail("unexpected end of input in string", "");
ch = str[i++];
if (ch == 'u') {
// Extract 4-byte escape sequence
string esc = str.substr(i, 4);
// Explicitly check length of the substring. The following loop
// relies on std::string returning the terminating NUL when
// accessing str[length]. Checking here reduces brittleness.
if (esc.length() < 4) {
return fail("bad \\u escape: " + esc, "");
}
for (size_t j = 0; j < 4; j++) {
if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F')
&& !in_range(esc[j], '0', '9'))
return fail("bad \\u escape: " + esc, "");
}
long codepoint = strtol(esc.data(), nullptr, 16);
// JSON specifies that characters outside the BMP shall be encoded as a pair
// of 4-hex-digit \u escapes encoding their surrogate pair components. Check
// whether we're in the middle of such a beast: the previous codepoint was an
// escaped lead (high) surrogate, and this is a trail (low) surrogate.
if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF)
&& in_range(codepoint, 0xDC00, 0xDFFF)) {
// Reassemble the two surrogate pairs into one astral-plane character, per
// the UTF-16 algorithm.
encode_utf8((((last_escaped_codepoint - 0xD800) << 10)
| (codepoint - 0xDC00)) + 0x10000, out);
last_escaped_codepoint = -1;
} else {
encode_utf8(last_escaped_codepoint, out);
last_escaped_codepoint = codepoint;
}
i += 4;
continue;
}
encode_utf8(last_escaped_codepoint, out);
last_escaped_codepoint = -1;
if (ch == 'b') {
out += '\b';
} else if (ch == 'f') {
out += '\f';
} else if (ch == 'n') {
out += '\n';
} else if (ch == 'r') {
out += '\r';
} else if (ch == 't') {
out += '\t';
} else if (ch == '"' || ch == '\\' || ch == '/') {
out += ch;
} else {
return fail("invalid escape character " + esc(ch), "");
}
}
}
/* parse_number()
*
* Parse a double.
*/
Json parse_number() {
size_t start_pos = i;
if (str[i] == '-')
i++;
// Integer part
if (str[i] == '0') {
i++;
if (in_range(str[i], '0', '9'))
return fail("leading 0s not permitted in numbers");
} else if (in_range(str[i], '1', '9')) {
i++;
while (in_range(str[i], '0', '9'))
i++;
} else {
return fail("invalid " + esc(str[i]) + " in number");
}
if (str[i] != '.' && str[i] != 'e' && str[i] != 'E'
&& (i - start_pos) <= static_cast<size_t>(std::numeric_limits<int>::digits10)) {
return std::atoi(str.c_str() + start_pos);
}
// Decimal part
if (str[i] == '.') {
i++;
if (!in_range(str[i], '0', '9'))
return fail("at least one digit required in fractional part");
while (in_range(str[i], '0', '9'))
i++;
}
// Exponent part
if (str[i] == 'e' || str[i] == 'E') {
i++;
if (str[i] == '+' || str[i] == '-')
i++;
if (!in_range(str[i], '0', '9'))
return fail("at least one digit required in exponent");
while (in_range(str[i], '0', '9'))
i++;
}
std::istringstream stm(std::string(str.begin()+start_pos, str.end()));
stm.imbue(std::locale("C"));
double result;
stm >> result;
return result;
}
/* expect(str, res)
*
* Expect that 'str' starts at the character that was just read. If it does, advance
* the input and return res. If not, flag an error.
*/
Json expect(const string &expected, Json res) {
assert(i != 0);
i--;
if (str.compare(i, expected.length(), expected) == 0) {
i += expected.length();
return res;
} else {
return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length()));
}
}
/* parse_json()
*
* Parse a JSON object.
*/
Json parse_json(int depth) {
if (depth > max_depth) {
return fail("exceeded maximum nesting depth");
}
char ch = get_next_token();
if (failed)
return Json();
if (ch == '-' || (ch >= '0' && ch <= '9')) {
i--;
return parse_number();
}
if (ch == 't')
return expect("true", true);
if (ch == 'f')
return expect("false", false);
if (ch == 'n')
return expect("null", Json());
if (ch == '"')
return parse_string();
if (ch == '{') {
map<string, Json> data;
ch = get_next_token();
if (ch == '}')
return data;
while (1) {
if (ch != '"')
return fail("expected '\"' in object, got " + esc(ch));
string key = parse_string();
if (failed)
return Json();
ch = get_next_token();
if (ch != ':')
return fail("expected ':' in object, got " + esc(ch));
data[std::move(key)] = parse_json(depth + 1);
if (failed)
return Json();
ch = get_next_token();
if (ch == '}')
break;
if (ch != ',')
return fail("expected ',' in object, got " + esc(ch));
ch = get_next_token();
}
return data;
}
if (ch == '[') {
vector<Json> data;
ch = get_next_token();
if (ch == ']')
return data;
while (1) {
i--;
data.push_back(parse_json(depth + 1));
if (failed)
return Json();
ch = get_next_token();
if (ch == ']')
break;
if (ch != ',')
return fail("expected ',' in list, got " + esc(ch));
ch = get_next_token();
(void)ch;
}
return data;
}
return fail("expected value, got " + esc(ch));
}
};
}//namespace {
Json Json::parse(const string &in, string &err, JsonParse strategy) {
JsonParser parser { in, 0, err, false, strategy };
Json result = parser.parse_json(0);
// Check for any trailing garbage
parser.consume_garbage();
if (parser.failed)
return Json();
if (parser.i != in.size())
return parser.fail("unexpected trailing " + esc(in[parser.i]));
return result;
}
// Documented in json11.hpp
vector<Json> Json::parse_multi(const string &in,
std::string::size_type &parser_stop_pos,
string &err,
JsonParse strategy) {
JsonParser parser { in, 0, err, false, strategy };
parser_stop_pos = 0;
vector<Json> json_vec;
while (parser.i != in.size() && !parser.failed) {
json_vec.push_back(parser.parse_json(0));
if (parser.failed)
break;
// Check for another object
parser.consume_garbage();
if (parser.failed)
break;
parser_stop_pos = parser.i;
}
return json_vec;
}
/* * * * * * * * * * * * * * * * * * * *
* Shape-checking
*/
bool Json::has_shape(const shape & types, string & err) const {
if (!is_object()) {
err = "expected JSON object, got " + dump();
return false;
}
for (auto & item : types) {
if ((*this)[item.first].type() != item.second) {
err = "bad type for " + item.first + " in " + dump();
return false;
}
}
return true;
}
} // namespace json11

View File

@ -0,0 +1,232 @@
/* json11
*
* json11 is a tiny JSON library for C++11, providing JSON parsing and serialization.
*
* The core object provided by the library is json11::Json. A Json object represents any JSON
* value: null, bool, number (int or double), string (std::string), array (std::vector), or
* object (std::map).
*
* Json objects act like values: they can be assigned, copied, moved, compared for equality or
* order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and
* Json::parse (static) to parse a std::string as a Json object.
*
* Internally, the various types of Json object are represented by the JsonValue class
* hierarchy.
*
* A note on numbers - JSON specifies the syntax of number formatting but not its semantics,
* so some JSON implementations distinguish between integers and floating-point numbers, while
* some don't. In json11, we choose the latter. Because some JSON implementations (namely
* Javascript itself) treat all numbers as the same type, distinguishing the two leads
* to JSON that will be *silently* changed by a round-trip through those implementations.
* Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also
* provides integer helpers.
*
* Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the
* range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64
* or long long to avoid the Y2038K problem; a double storing microseconds since some epoch
* will be exact for +/- 275 years.)
*/
/* Copyright (c) 2013 Dropbox, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 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 OR COPYRIGHT HOLDERS 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.
*/
#pragma once
#include <string>
#include <vector>
#include <map>
#include <memory>
#include <initializer_list>
#ifdef _MSC_VER
#if _MSC_VER <= 1800 // VS 2013
#ifndef noexcept
#define noexcept throw()
#endif
#ifndef snprintf
#define snprintf _snprintf_s
#endif
#endif
#endif
namespace json11 {
enum JsonParse {
STANDARD, COMMENTS
};
class JsonValue;
class Json final {
public:
// Types
enum Type {
NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT
};
// Array and object typedefs
typedef std::vector<Json> array;
typedef std::map<std::string, Json> object;
// Constructors for the various types of JSON value.
Json() noexcept; // NUL
Json(std::nullptr_t) noexcept; // NUL
Json(double value); // NUMBER
Json(int value); // NUMBER
Json(bool value); // BOOL
Json(const std::string &value); // STRING
Json(std::string &&value); // STRING
Json(const char * value); // STRING
Json(const array &values); // ARRAY
Json(array &&values); // ARRAY
Json(const object &values); // OBJECT
Json(object &&values); // OBJECT
// Implicit constructor: anything with a to_json() function.
template <class T, class = decltype(&T::to_json)>
Json(const T & t) : Json(t.to_json()) {}
// Implicit constructor: map-like objects (std::map, std::unordered_map, etc)
template <class M, typename std::enable_if<
std::is_constructible<std::string, decltype(std::declval<M>().begin()->first)>::value
&& std::is_constructible<Json, decltype(std::declval<M>().begin()->second)>::value,
int>::type = 0>
Json(const M & m) : Json(object(m.begin(), m.end())) {}
// Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc)
template <class V, typename std::enable_if<
std::is_constructible<Json, decltype(*std::declval<V>().begin())>::value,
int>::type = 0>
Json(const V & v) : Json(array(v.begin(), v.end())) {}
// This prevents Json(some_pointer) from accidentally producing a bool. Use
// Json(bool(some_pointer)) if that behavior is desired.
Json(void *) = delete;
// Accessors
Type type() const;
bool is_null() const { return type() == NUL; }
bool is_number() const { return type() == NUMBER; }
bool is_bool() const { return type() == BOOL; }
bool is_string() const { return type() == STRING; }
bool is_array() const { return type() == ARRAY; }
bool is_object() const { return type() == OBJECT; }
// Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not
// distinguish between integer and non-integer numbers - number_value() and int_value()
// can both be applied to a NUMBER-typed object.
double number_value() const;
int int_value() const;
// Return the enclosed value if this is a boolean, false otherwise.
bool bool_value() const;
// Return the enclosed string if this is a string, "" otherwise.
const std::string &string_value() const;
// Return the enclosed std::vector if this is an array, or an empty vector otherwise.
const array &array_items() const;
// Return the enclosed std::map if this is an object, or an empty map otherwise.
const object &object_items() const;
// Return a reference to arr[i] if this is an array, Json() otherwise.
const Json & operator[](size_t i) const;
// Return a reference to obj[key] if this is an object, Json() otherwise.
const Json & operator[](const std::string &key) const;
// Serialize.
void dump(std::string &out) const;
std::string dump() const {
std::string out;
dump(out);
return out;
}
// Parse. If parse fails, return Json() and assign an error message to err.
static Json parse(const std::string & in,
std::string & err,
JsonParse strategy = JsonParse::STANDARD);
static Json parse(const char * in,
std::string & err,
JsonParse strategy = JsonParse::STANDARD) {
if (in) {
return parse(std::string(in), err, strategy);
} else {
err = "null input";
return nullptr;
}
}
// Parse multiple objects, concatenated or separated by whitespace
static std::vector<Json> parse_multi(
const std::string & in,
std::string::size_type & parser_stop_pos,
std::string & err,
JsonParse strategy = JsonParse::STANDARD);
static inline std::vector<Json> parse_multi(
const std::string & in,
std::string & err,
JsonParse strategy = JsonParse::STANDARD) {
std::string::size_type parser_stop_pos;
return parse_multi(in, parser_stop_pos, err, strategy);
}
bool operator== (const Json &rhs) const;
bool operator< (const Json &rhs) const;
bool operator!= (const Json &rhs) const { return !(*this == rhs); }
bool operator<= (const Json &rhs) const { return !(rhs < *this); }
bool operator> (const Json &rhs) const { return (rhs < *this); }
bool operator>= (const Json &rhs) const { return !(*this < rhs); }
/* has_shape(types, err)
*
* Return true if this is a JSON object and, for each item in types, has a field of
* the given type. If not, return false and set err to a descriptive message.
*/
typedef std::initializer_list<std::pair<std::string, Type>> shape;
bool has_shape(const shape & types, std::string & err) const;
private:
std::shared_ptr<JsonValue> m_ptr;
};
// Internal class hierarchy - JsonValue objects are not exposed to users of this API.
class JsonValue {
protected:
friend class Json;
friend class JsonInt;
friend class JsonDouble;
virtual Json::Type type() const = 0;
virtual bool equals(const JsonValue * other) const = 0;
virtual bool less(const JsonValue * other) const = 0;
virtual void dump(std::string &out) const = 0;
virtual double number_value() const;
virtual int int_value() const;
virtual bool bool_value() const;
virtual const std::string &string_value() const;
virtual const Json::array &array_items() const;
virtual const Json &operator[](size_t i) const;
virtual const Json::object &object_items() const;
virtual const Json &operator[](const std::string &key) const;
virtual ~JsonValue() {}
};
} // namespace json11

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,423 @@
<?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;noexcept=;_ALLOW_KEYWORD_MACROS;%(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>
<DisableSpecificWarnings>4068;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</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;noexcept=;_ALLOW_KEYWORD_MACROS;%(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\AudioIO.h" />
<ClInclude Include="audio\AudioOutput.h" />
<ClInclude Include="audio\Resampler.h" />
<ClInclude Include="BlockingQueue.h" />
<ClInclude Include="Buffers.h" />
<ClInclude Include="CongestionControl.h" />
<ClInclude Include="EchoCanceller.h" />
<ClInclude Include="JitterBuffer.h" />
<ClInclude Include="logging.h" />
<ClInclude Include="MediaStreamItf.h" />
<ClInclude Include="MessageThread.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="PacketReassembler.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\AudioIO.cpp" />
<ClCompile Include="audio\AudioOutput.cpp" />
<ClCompile Include="audio\Resampler.cpp" />
<ClCompile Include="BlockingQueue.cpp" />
<ClCompile Include="Buffers.cpp" />
<ClCompile Include="CongestionControl.cpp" />
<ClCompile Include="EchoCanceller.cpp" />
<ClCompile Include="JitterBuffer.cpp" />
<ClCompile Include="logging.cpp" />
<ClCompile Include="MediaStreamItf.cpp" />
<ClCompile Include="MessageThread.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="PacketReassembler.cpp" />
<ClCompile Include="VoIPController.cpp" />
<ClCompile Include="os\windows\CXWrapper.cpp" />
<ClCompile Include="VoIPServerConfig.cpp" />
<ClCompile Include="webrtc_dsp\webrtc\base\checks.cc" />
<ClCompile Include="webrtc_dsp\webrtc\base\stringutils.cc" />
<ClCompile Include="webrtc_dsp\webrtc\common_audio\audio_util.cc" />
<ClCompile Include="webrtc_dsp\webrtc\common_audio\channel_buffer.cc" />
<ClCompile Include="webrtc_dsp\webrtc\common_audio\fft4g.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\ring_buffer.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\auto_correlation.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\auto_corr_to_refl_coef.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\complex_bit_reverse.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\complex_fft.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\copy_set_operations.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\cross_correlation.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\cross_correlation_neon.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\division_operations.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\dot_product_with_scale.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\downsample_fast.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\downsample_fast_neon.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\energy.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ar.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ar_fast_q12.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\filter_ma_fast_q12.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\get_hanning_window.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\get_scaling_square.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\ilbc_specific_functions.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\levinson_durbin.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\lpc_to_refl_coef.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\min_max_operations.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\min_max_operations_neon.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\randomization_functions.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\real_fft.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\refl_coef_to_lpc.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_48khz.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_by_2.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_by_2_internal.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\resample_fractional.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\splitting_filter_impl.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_init.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_inl.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_sqrt.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\spl_sqrt_floor.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\sqrt_of_one_minus_x_squared.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\signal_processing\vector_scaling_operations.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\common_audio\sparse_fir_filter.cc" />
<ClCompile Include="webrtc_dsp\webrtc\common_audio\wav_file.cc" />
<ClCompile Include="webrtc_dsp\webrtc\common_audio\wav_header.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core_c.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\aecm_core_neon.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aecm\echo_control_mobile.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core_neon.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_core_sse2.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\aec_resampler.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\aec\echo_cancellation.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\analog_agc.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\agc\legacy\digital_agc.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\logging\apm_data_dumper.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\noise_suppression_x.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core_c.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\nsx_core_neon.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\ns\ns_core.c">
<CompileAsWinRT>false</CompileAsWinRT>
</ClCompile>
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\splitting_filter.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\three_band_filter_bank.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\block_mean_calculator.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\delay_estimator_wrapper.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_neon.cc" />
<ClCompile Include="webrtc_dsp\webrtc\modules\audio_processing\utility\ooura_fft_sse2.cc" />
<ClCompile Include="webrtc_dsp\webrtc\system_wrappers\source\cpu_features.cc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,488 @@
<?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="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>
<ClCompile Include="Buffers.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="VoIPController.h" />
<ClInclude Include="VoIPServerConfig.h" />
<ClInclude Include="BlockingQueue.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>
<ClInclude Include="Buffers.h" />
</ItemGroup>
</Project>

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,12 @@
<?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>FILEHEADER</key>
<string>
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.
</string>
</dict>
</plist>

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,8 @@
<?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>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,12 @@
<?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>FILEHEADER</key>
<string>
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.
</string>
</dict>
</plist>

View File

@ -0,0 +1,104 @@
//
// 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(FILE* file){
if(file){
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);
std::string sysver(sysname.sysname);
sysver+=" ";
sysver+=sysname.release;
sysver+=" (";
sysver+=sysname.version;
sysver+=")";
const char* systemVersion=sysver.c_str();
#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(file, "---------------\nlibtgvoip v" LIBTGVOIP_VERSION " on %s %s\nLog started on %d/%02d/%d at %d:%02d:%02d\n---------------\n", systemVersion, cpuArch, now->tm_mday, now->tm_mon+1, now->tm_year+1900, now->tm_hour, now->tm_min, now->tm_sec);
}
}

View File

@ -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 __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
#include <stdio.h>
void tgvoip_log_file_printf(char level, const char* msg, ...);
void tgvoip_log_file_write_header(FILE* file);
#if defined(__ANDROID__)
#include <android/log.h>
//#define _LOG_WRAP(...) __BASE_FILE__":"LSTR_INT(__LINE__)": "__VA_ARGS__
#define _LOG_WRAP(...) __VA_ARGS__
#define TAG "tgvoip"
#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>
#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>
#ifndef TGVOIP_NO_STDOUT_LOGS
#ifndef TGVOIP_NO_STDOUT_COLOR
#define _TGVOIP_LOG_PRINT(verb, color, msg, ...) {printf("\033[%dm%c/tgvoip: " msg "\033[0m\n", color, verb, ##__VA_ARGS__); tgvoip_log_file_printf(verb, msg, ##__VA_ARGS__);}
#else
#define _TGVOIP_LOG_PRINT(verb, color, msg, ...) {printf("%c/tgvoip: " msg "\n", verb, ##__VA_ARGS__); tgvoip_log_file_printf(verb, msg, ##__VA_ARGS__);}
#endif
#else
#define _TGVOIP_LOG_PRINT(verb, color, msg, ...) {tgvoip_log_file_printf(verb, msg, ##__VA_ARGS__);}
#endif
#define LOGV(msg, ...) _TGVOIP_LOG_PRINT('V', 90, msg, ##__VA_ARGS__)
#define LOGD(msg, ...) _TGVOIP_LOG_PRINT('D', 37, msg, ##__VA_ARGS__)
#define LOGI(msg, ...) _TGVOIP_LOG_PRINT('I', 94, msg, ##__VA_ARGS__)
#define LOGW(msg, ...) _TGVOIP_LOG_PRINT('W', 93, msg, ##__VA_ARGS__)
#define LOGE(msg, ...) _TGVOIP_LOG_PRINT('E', 91, msg, ##__VA_ARGS__)
#endif
#if !defined(snprintf) && defined(_WIN32) && defined(__cplusplus_winrt)
#define snprintf _snprintf
#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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,215 @@
#! /bin/sh
# Common wrapper for a few potentially missing GNU programs.
scriptversion=2018-03-07.03; # UTC
# Copyright (C) 1996-2018 Free Software Foundation, Inc.
# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
if test $# -eq 0; then
echo 1>&2 "Try '$0 --help' for more information"
exit 1
fi
case $1 in
--is-lightweight)
# Used by our autoconf macros to check whether the available missing
# script is modern enough.
exit 0
;;
--run)
# Back-compat with the calling convention used by older automake.
shift
;;
-h|--h|--he|--hel|--help)
echo "\
$0 [OPTION]... PROGRAM [ARGUMENT]...
Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
to PROGRAM being missing or too old.
Options:
-h, --help display this help and exit
-v, --version output version information and exit
Supported PROGRAM values:
aclocal autoconf autoheader autom4te automake makeinfo
bison yacc flex lex help2man
Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
'g' are ignored when checking the name.
Send bug reports to <bug-automake@gnu.org>."
exit $?
;;
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
echo "missing $scriptversion (GNU Automake)"
exit $?
;;
-*)
echo 1>&2 "$0: unknown '$1' option"
echo 1>&2 "Try '$0 --help' for more information"
exit 1
;;
esac
# Run the given program, remember its exit status.
"$@"; st=$?
# If it succeeded, we are done.
test $st -eq 0 && exit 0
# Also exit now if we it failed (or wasn't found), and '--version' was
# passed; such an option is passed most likely to detect whether the
# program is present and works.
case $2 in --version|--help) exit $st;; esac
# Exit code 63 means version mismatch. This often happens when the user
# tries to use an ancient version of a tool on a file that requires a
# minimum version.
if test $st -eq 63; then
msg="probably too old"
elif test $st -eq 127; then
# Program was missing.
msg="missing on your system"
else
# Program was found and executed, but failed. Give up.
exit $st
fi
perl_URL=https://www.perl.org/
flex_URL=https://github.com/westes/flex
gnu_software_URL=https://www.gnu.org/software
program_details ()
{
case $1 in
aclocal|automake)
echo "The '$1' program is part of the GNU Automake package:"
echo "<$gnu_software_URL/automake>"
echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
echo "<$gnu_software_URL/autoconf>"
echo "<$gnu_software_URL/m4/>"
echo "<$perl_URL>"
;;
autoconf|autom4te|autoheader)
echo "The '$1' program is part of the GNU Autoconf package:"
echo "<$gnu_software_URL/autoconf/>"
echo "It also requires GNU m4 and Perl in order to run:"
echo "<$gnu_software_URL/m4/>"
echo "<$perl_URL>"
;;
esac
}
give_advice ()
{
# Normalize program name to check for.
normalized_program=`echo "$1" | sed '
s/^gnu-//; t
s/^gnu//; t
s/^g//; t'`
printf '%s\n' "'$1' is $msg."
configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
case $normalized_program in
autoconf*)
echo "You should only need it if you modified 'configure.ac',"
echo "or m4 files included by it."
program_details 'autoconf'
;;
autoheader*)
echo "You should only need it if you modified 'acconfig.h' or"
echo "$configure_deps."
program_details 'autoheader'
;;
automake*)
echo "You should only need it if you modified 'Makefile.am' or"
echo "$configure_deps."
program_details 'automake'
;;
aclocal*)
echo "You should only need it if you modified 'acinclude.m4' or"
echo "$configure_deps."
program_details 'aclocal'
;;
autom4te*)
echo "You might have modified some maintainer files that require"
echo "the 'autom4te' program to be rebuilt."
program_details 'autom4te'
;;
bison*|yacc*)
echo "You should only need it if you modified a '.y' file."
echo "You may want to install the GNU Bison package:"
echo "<$gnu_software_URL/bison/>"
;;
lex*|flex*)
echo "You should only need it if you modified a '.l' file."
echo "You may want to install the Fast Lexical Analyzer package:"
echo "<$flex_URL>"
;;
help2man*)
echo "You should only need it if you modified a dependency" \
"of a man page."
echo "You may want to install the GNU Help2man package:"
echo "<$gnu_software_URL/help2man/>"
;;
makeinfo*)
echo "You should only need it if you modified a '.texi' file, or"
echo "any other file indirectly affecting the aspect of the manual."
echo "You might want to install the Texinfo package:"
echo "<$gnu_software_URL/texinfo/>"
echo "The spurious makeinfo call might also be the consequence of"
echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
echo "want to install GNU make:"
echo "<$gnu_software_URL/make/>"
;;
*)
echo "You might have modified some files without having the proper"
echo "tools for further handling them. Check the 'README' file, it"
echo "often tells you about the needed prerequisites for installing"
echo "this package. You may also peek at any GNU archive site, in"
echo "case some other package contains this missing '$1' program."
;;
esac
}
give_advice "$1" | sed -e '1s/^/WARNING: /' \
-e '2,$s/^/ /' >&2
# Propagate the correct exit status (expected to be 127 for a program
# not found, 63 for a program that failed due to version mismatch).
exit $st
# Local variables:
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

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