diff --git a/VoIPController.cpp b/VoIPController.cpp index cd285e2..620ebb3 100755 --- a/VoIPController.cpp +++ b/VoIPController.cpp @@ -845,6 +845,46 @@ void VoIPController::SetConfig(const Config& cfg){ UpdateAudioBitrateLimit(); } +void VoIPController::SetPersistentState(vector state){ + using namespace json11; + + if(state.empty()) + return; + string jsonErr; + string json=string(state.begin(), state.end()); + Json _obj=Json::parse(json, jsonErr); + if(!jsonErr.empty()){ + LOGE("Error parsing persistable state: %s", jsonErr.c_str()); + return; + } + Json::object obj=_obj.object_items(); + if(obj.find("proxy")!=obj.end()){ + Json::object proxy=obj["proxy"].object_items(); + lastTestedProxyServer=proxy["server"].string_value(); + proxySupportsUDP=proxy["udp"].bool_value(); + proxySupportsTCP=proxy["tcp"].bool_value(); + } +} + +vector VoIPController::GetPersistentState(){ + using namespace json11; + + Json::object obj=Json::object{ + {"ver", 1}, + }; + if(proxyProtocol==PROXY_SOCKS5){ + char pbuf[128]; + snprintf(pbuf, sizeof(pbuf), "%s:%u", proxyAddress.c_str(), proxyPort); + obj.insert({"proxy", Json::object{ + {"server", string(pbuf)}, + {"udp", proxySupportsUDP}, + {"tcp", proxySupportsTCP} + }}); + } + const char* jstr=Json(obj).dump().c_str(); + return vector(jstr, jstr+strlen(jstr)); +} + #pragma mark - Internal intialization void VoIPController::InitializeTimers(){ @@ -877,8 +917,6 @@ void VoIPController::InitializeTimers(){ }, 0.1, 0.1); } - udpConnectivityState=UDP_PING_PENDING; - udpPingTimeoutID=messageThread.Post(std::bind(&VoIPController::SendUdpPings, this), 0.0, 0.5); messageThread.Post(std::bind(&VoIPController::SendRelayPings, this), 0.0, 2.0); } @@ -1373,6 +1411,14 @@ void VoIPController::InitUDPProxy(){ delete udpSocket; udpSocket=realUdpSocket; } + char sbuf[128]; + snprintf(sbuf, sizeof(sbuf), "%s:%u", proxyAddress.c_str(), proxyPort); + string proxyHostPort(sbuf); + if(proxyHostPort==lastTestedProxyServer && !proxySupportsUDP){ + LOGI("Proxy does not support UDP - using UDP directly instead"); + return; + } + NetworkSocket* tcp=NetworkSocket::Create(PROTO_TCP); tcp->Connect(resolvedProxyAddress, proxyPort); @@ -1410,12 +1456,11 @@ void VoIPController::InitUDPProxy(){ if(udpProxy->IsFailed()){ udpProxy->Close(); delete udpProxy; - useTCP=true; - useUDP=false; - udpConnectivityState=UDP_NOT_AVAILABLE; + proxySupportsUDP=false; }else{ udpSocket=udpProxy; } + ResetUdpAvailability(); } void VoIPController::RunRecvThread(){ @@ -1429,6 +1474,9 @@ void VoIPController::RunRecvThread(){ SetState(STATE_FAILED); return; } + }else{ + udpConnectivityState=UDP_PING_PENDING; + udpPingTimeoutID=messageThread.Post(std::bind(&VoIPController::SendUdpPings, this), 0.0, 0.5); } while(runReceiver){ @@ -3144,6 +3192,17 @@ void VoIPController::EvaluateUdpPingResults(){ else avgPongs=0.0; LOGI("UDP ping reply count: %.2f", avgPongs); + if(avgPongs==0.0 && proxyProtocol==PROXY_SOCKS5 && udpSocket!=realUdpSocket){ + LOGI("Proxy does not let UDP through, closing proxy connection and using UDP directly"); + NetworkSocket* proxySocket=udpSocket; + proxySocket->Close(); + udpSocket=realUdpSocket; + selectCanceller->CancelSelect(); + delete proxySocket; + proxySupportsUDP=false; + ResetUdpAvailability(); + return; + } bool configUseTCP=ServerConfig::GetSharedInstance()->GetBoolean("use_tcp", true); if(configUseTCP){ if(avgPongs==0.0 || (udpConnectivityState==UDP_BAD && avgPongs<7.0)){ diff --git a/VoIPController.h b/VoIPController.h index 929345c..8eb30b3 100755 --- a/VoIPController.h +++ b/VoIPController.h @@ -369,6 +369,15 @@ namespace tgvoip{ 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 GetPersistentState(); + /** + * Load the persistable state. Call this before starting the call. + */ + void SetPersistentState(std::vector state); #if defined(TGVOIP_USE_CALLBACK_AUDIO_IO) void SetAudioDataCallbacks(std::function input, std::function output); @@ -692,10 +701,15 @@ namespace tgvoip{ std::function audioInputDataCallback; std::function audioOutputDataCallback; #endif - + video::VideoSource* videoSource=NULL; video::VideoRenderer* videoRenderer=NULL; double firstVideoFrameTime=0.0; + + /*** persistable state values ***/ + bool proxySupportsUDP=true; + bool proxySupportsTCP=true; + std::string lastTestedProxyServer=""; /*** server config values ***/ uint32_t maxAudioBitrate; diff --git a/client/android/tg_voip_jni.cpp b/client/android/tg_voip_jni.cpp index a8a3b6f..34ad8e0 100644 --- a/client/android/tg_voip_jni.cpp +++ b/client/android/tg_voip_jni.cpp @@ -38,6 +38,7 @@ jclass jniUtilitiesClass=NULL; struct ImplDataAndroid{ jobject javaObject; + std::string persistentStateFile=""; }; #ifndef TGVOIP_PACKAGE_PATH @@ -115,9 +116,12 @@ namespace tgvoip { #pragma mark - VoIPController - jlong VoIPController_nativeInit(JNIEnv* env, jobject thiz) { - ImplDataAndroid* impl=(ImplDataAndroid*) malloc(sizeof(ImplDataAndroid)); + 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; @@ -127,6 +131,22 @@ namespace tgvoip { 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(ftell(f)); + fseek(f, 0, SEEK_SET); + if(len<1024*512 && len>0){ + char *fbuf=static_cast(malloc(len)); + fread(fbuf, 1, len, f); + std::vector state(fbuf, fbuf+len); + free(fbuf); + cntrlr->SetPersistentState(state); + } + fclose(f); + } + } return (jlong)(intptr_t)cntrlr; } @@ -196,9 +216,17 @@ namespace tgvoip { VoIPController* ctlr=((VoIPController*)(intptr_t)inst); ImplDataAndroid* impl=(ImplDataAndroid*)ctlr->implData; ctlr->Stop(); + std::vector state=ctlr->GetPersistentState(); delete ctlr; env->DeleteGlobalRef(impl->javaObject); - free(impl); + 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){ @@ -520,7 +548,7 @@ extern "C" void tgvoipRegisterNatives(JNIEnv* env){ // VoIPController JNINativeMethod controllerMethods[]={ - {"nativeInit", "()J", (void*)&tgvoip::VoIPController_nativeInit}, + {"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},