From f4c4f792d158529aa71b6f8e68ee81bc01f4aa0a Mon Sep 17 00:00:00 2001 From: Grishka Date: Thu, 29 Nov 2018 03:03:15 +0300 Subject: [PATCH] Collect packet stats and accept json string for server config --- Buffers.h | 2 +- Makefile.am | 10 +- Makefile.in | 45 +- NetworkSocket.cpp | 4 +- VoIPController.cpp | 141 ++--- VoIPController.h | 32 +- VoIPServerConfig.cpp | 61 +- VoIPServerConfig.h | 6 +- client/android/tg_voip_jni.cpp | 21 +- json11.cpp | 788 ++++++++++++++++++++++++ json11.hpp | 232 +++++++ libtgvoip.gyp | 2 + libtgvoip.xcodeproj/project.pbxproj | 4 + libtgvoip_osx.xcodeproj/project.pbxproj | 4 + os/posix/NetworkSocketPosix.cpp | 2 +- 15 files changed, 1176 insertions(+), 178 deletions(-) create mode 100755 json11.cpp create mode 100755 json11.hpp diff --git a/Buffers.h b/Buffers.h index 2aad62725b..b432e93d0d 100644 --- a/Buffers.h +++ b/Buffers.h @@ -113,7 +113,7 @@ namespace tgvoip{ data=NULL; length=capacity; }; - Buffer(const Buffer& other)=delete; // use Buffer::CopyOf to copy contents explicitly + TGVOIP_DISALLOW_COPY_AND_ASSIGN(Buffer); // use Buffer::CopyOf to copy contents explicitly Buffer(Buffer&& other) noexcept { data=other.data; length=other.length; diff --git a/Makefile.am b/Makefile.am index 5956147fcd..f75a59d867 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,7 +22,10 @@ audio/AudioIO.cpp \ audio/AudioInput.cpp \ audio/AudioOutput.cpp \ audio/Resampler.cpp \ -os/posix/NetworkSocketPosix.cpp +os/posix/NetworkSocketPosix.cpp \ +video/VideoSource.cpp \ +video/VideoRenderer.cpp \ +json11.cpp TGVOIP_HDRS = \ VoIPController.h \ @@ -45,7 +48,10 @@ audio/AudioIO.h \ audio/AudioInput.h \ audio/AudioOutput.h \ audio/Resampler.h \ -os/posix/NetworkSocketPosix.h +os/posix/NetworkSocketPosix.h \ +video/VideoSource.h \ +video/VideoRenderer.h \ +json11.hpp if TARGET_OS_OSX diff --git a/Makefile.in b/Makefile.in index 0d4b934053..14896cf48e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -804,7 +804,8 @@ am__libtgvoip_la_SOURCES_DIST = VoIPController.cpp Buffers.cpp \ PacketReassembler.cpp VoIPGroupController.cpp \ VoIPServerConfig.cpp audio/AudioIO.cpp audio/AudioInput.cpp \ audio/AudioOutput.cpp audio/Resampler.cpp \ - os/posix/NetworkSocketPosix.cpp \ + os/posix/NetworkSocketPosix.cpp video/VideoSource.cpp \ + video/VideoRenderer.cpp json11.cpp \ os/darwin/AudioInputAudioUnit.cpp \ os/darwin/AudioOutputAudioUnit.cpp os/darwin/AudioUnitIO.cpp \ os/darwin/AudioInputAudioUnitOSX.cpp \ @@ -1412,6 +1413,7 @@ am__libtgvoip_la_SOURCES_DIST = VoIPController.cpp Buffers.cpp \ 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 json11.hpp \ os/darwin/AudioInputAudioUnit.h \ os/darwin/AudioOutputAudioUnit.h os/darwin/AudioUnitIO.h \ os/darwin/AudioInputAudioUnitOSX.h \ @@ -1717,7 +1719,8 @@ am__objects_11 = VoIPController.lo Buffers.lo CongestionControl.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 $(am__objects_1) \ + os/posix/NetworkSocketPosix.lo video/VideoSource.lo \ + video/VideoRenderer.lo json11.lo $(am__objects_1) \ $(am__objects_2) $(am__objects_3) $(am__objects_4) \ $(am__objects_5) $(am__objects_6) $(am__objects_7) \ $(am__objects_8) $(am__objects_9) $(am__objects_10) @@ -1752,7 +1755,8 @@ am__depfiles_remade = ./$(DEPDIR)/Buffers.Plo \ ./$(DEPDIR)/OpusEncoder.Plo ./$(DEPDIR)/PacketReassembler.Plo \ ./$(DEPDIR)/VoIPController.Plo \ ./$(DEPDIR)/VoIPGroupController.Plo \ - ./$(DEPDIR)/VoIPServerConfig.Plo ./$(DEPDIR)/logging.Plo \ + ./$(DEPDIR)/VoIPServerConfig.Plo ./$(DEPDIR)/json11.Plo \ + ./$(DEPDIR)/logging.Plo \ ./webrtc_dsp/absl/base/internal/$(DEPDIR)/raw_logging.Plo \ ./webrtc_dsp/absl/base/internal/$(DEPDIR)/throw_delegate.Plo \ ./webrtc_dsp/absl/strings/$(DEPDIR)/ascii.Plo \ @@ -2032,6 +2036,8 @@ am__depfiles_remade = ./$(DEPDIR)/Buffers.Plo \ os/linux/$(DEPDIR)/AudioOutputPulse.Plo \ os/linux/$(DEPDIR)/AudioPulse.Plo \ os/posix/$(DEPDIR)/NetworkSocketPosix.Plo \ + video/$(DEPDIR)/VideoRenderer.Plo \ + video/$(DEPDIR)/VideoSource.Plo \ webrtc_dsp/common_audio/signal_processing/$(DEPDIR)/complex_bit_reverse.Plo \ webrtc_dsp/common_audio/signal_processing/$(DEPDIR)/complex_bit_reverse_arm.Plo \ webrtc_dsp/common_audio/signal_processing/$(DEPDIR)/cross_correlation_neon.Plo \ @@ -2125,6 +2131,7 @@ am__nobase_tgvoipinclude_HEADERS_DIST = VoIPController.h Buffers.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 json11.hpp \ os/darwin/AudioInputAudioUnit.h \ os/darwin/AudioOutputAudioUnit.h os/darwin/AudioUnitIO.h \ os/darwin/AudioInputAudioUnitOSX.h \ @@ -2313,7 +2320,8 @@ SRC = VoIPController.cpp Buffers.cpp CongestionControl.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 $(am__append_1) \ + os/posix/NetworkSocketPosix.cpp video/VideoSource.cpp \ + video/VideoRenderer.cpp json11.cpp $(am__append_1) \ $(am__append_4) $(am__append_6) $(am__append_10) \ $(am__append_12) $(am__append_14) $(am__append_16) \ $(am__append_18) $(am__append_21) $(am__append_22) @@ -2323,8 +2331,9 @@ TGVOIP_HDRS = VoIPController.h Buffers.h BlockingQueue.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 $(am__append_2) $(am__append_5) \ - $(am__append_7) $(am__append_17) + os/posix/NetworkSocketPosix.h video/VideoSource.h \ + video/VideoRenderer.h json11.hpp $(am__append_2) \ + $(am__append_5) $(am__append_7) $(am__append_17) libtgvoip_la_SOURCES = $(SRC) $(TGVOIP_HDRS) tgvoipincludedir = $(includedir)/tgvoip nobase_tgvoipinclude_HEADERS = $(TGVOIP_HDRS) @@ -2438,6 +2447,16 @@ os/posix/$(DEPDIR)/$(am__dirstamp): @: > os/posix/$(DEPDIR)/$(am__dirstamp) os/posix/NetworkSocketPosix.lo: os/posix/$(am__dirstamp) \ os/posix/$(DEPDIR)/$(am__dirstamp) +video/$(am__dirstamp): + @$(MKDIR_P) video + @: > video/$(am__dirstamp) +video/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) video/$(DEPDIR) + @: > video/$(DEPDIR)/$(am__dirstamp) +video/VideoSource.lo: video/$(am__dirstamp) \ + video/$(DEPDIR)/$(am__dirstamp) +video/VideoRenderer.lo: video/$(am__dirstamp) \ + video/$(DEPDIR)/$(am__dirstamp) os/darwin/$(am__dirstamp): @$(MKDIR_P) os/darwin @: > os/darwin/$(am__dirstamp) @@ -3544,6 +3563,8 @@ mostlyclean-compile: -rm -f os/linux/*.lo -rm -f os/posix/*.$(OBJEXT) -rm -f os/posix/*.lo + -rm -f video/*.$(OBJEXT) + -rm -f video/*.lo -rm -f webrtc_dsp/common_audio/signal_processing/*.$(OBJEXT) -rm -f webrtc_dsp/common_audio/signal_processing/*.lo -rm -f webrtc_dsp/common_audio/third_party/spl_sqrt_floor/*.$(OBJEXT) @@ -3575,6 +3596,7 @@ distclean-compile: @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 +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/json11.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logging.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./webrtc_dsp/absl/base/internal/$(DEPDIR)/raw_logging.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./webrtc_dsp/absl/base/internal/$(DEPDIR)/throw_delegate.Plo@am__quote@ # am--include-marker @@ -3856,6 +3878,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@os/linux/$(DEPDIR)/AudioOutputPulse.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@os/linux/$(DEPDIR)/AudioPulse.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@os/posix/$(DEPDIR)/NetworkSocketPosix.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@video/$(DEPDIR)/VideoRenderer.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@video/$(DEPDIR)/VideoSource.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@webrtc_dsp/common_audio/signal_processing/$(DEPDIR)/complex_bit_reverse.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@webrtc_dsp/common_audio/signal_processing/$(DEPDIR)/complex_bit_reverse_arm.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@webrtc_dsp/common_audio/signal_processing/$(DEPDIR)/cross_correlation_neon.Plo@am__quote@ # am--include-marker @@ -4039,6 +4063,7 @@ clean-libtool: -rm -rf os/darwin/.libs os/darwin/_libs -rm -rf os/linux/.libs os/linux/_libs -rm -rf os/posix/.libs os/posix/_libs + -rm -rf video/.libs video/_libs -rm -rf webrtc_dsp/common_audio/signal_processing/.libs webrtc_dsp/common_audio/signal_processing/_libs -rm -rf webrtc_dsp/common_audio/third_party/spl_sqrt_floor/.libs webrtc_dsp/common_audio/third_party/spl_sqrt_floor/_libs -rm -rf webrtc_dsp/modules/audio_processing/aec/.libs webrtc_dsp/modules/audio_processing/aec/_libs @@ -4340,6 +4365,8 @@ distclean-generic: -rm -f os/linux/$(am__dirstamp) -rm -f os/posix/$(DEPDIR)/$(am__dirstamp) -rm -f os/posix/$(am__dirstamp) + -rm -f video/$(DEPDIR)/$(am__dirstamp) + -rm -f video/$(am__dirstamp) -rm -f webrtc_dsp/absl/base/internal/$(DEPDIR)/$(am__dirstamp) -rm -f webrtc_dsp/absl/base/internal/$(am__dirstamp) -rm -f webrtc_dsp/absl/strings/$(DEPDIR)/$(am__dirstamp) @@ -4432,6 +4459,7 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/VoIPController.Plo -rm -f ./$(DEPDIR)/VoIPGroupController.Plo -rm -f ./$(DEPDIR)/VoIPServerConfig.Plo + -rm -f ./$(DEPDIR)/json11.Plo -rm -f ./$(DEPDIR)/logging.Plo -rm -f ./webrtc_dsp/absl/base/internal/$(DEPDIR)/raw_logging.Plo -rm -f ./webrtc_dsp/absl/base/internal/$(DEPDIR)/throw_delegate.Plo @@ -4713,6 +4741,8 @@ distclean: distclean-am -rm -f os/linux/$(DEPDIR)/AudioOutputPulse.Plo -rm -f os/linux/$(DEPDIR)/AudioPulse.Plo -rm -f os/posix/$(DEPDIR)/NetworkSocketPosix.Plo + -rm -f video/$(DEPDIR)/VideoRenderer.Plo + -rm -f video/$(DEPDIR)/VideoSource.Plo -rm -f webrtc_dsp/common_audio/signal_processing/$(DEPDIR)/complex_bit_reverse.Plo -rm -f webrtc_dsp/common_audio/signal_processing/$(DEPDIR)/complex_bit_reverse_arm.Plo -rm -f webrtc_dsp/common_audio/signal_processing/$(DEPDIR)/cross_correlation_neon.Plo @@ -4787,6 +4817,7 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/VoIPController.Plo -rm -f ./$(DEPDIR)/VoIPGroupController.Plo -rm -f ./$(DEPDIR)/VoIPServerConfig.Plo + -rm -f ./$(DEPDIR)/json11.Plo -rm -f ./$(DEPDIR)/logging.Plo -rm -f ./webrtc_dsp/absl/base/internal/$(DEPDIR)/raw_logging.Plo -rm -f ./webrtc_dsp/absl/base/internal/$(DEPDIR)/throw_delegate.Plo @@ -5068,6 +5099,8 @@ maintainer-clean: maintainer-clean-am -rm -f os/linux/$(DEPDIR)/AudioOutputPulse.Plo -rm -f os/linux/$(DEPDIR)/AudioPulse.Plo -rm -f os/posix/$(DEPDIR)/NetworkSocketPosix.Plo + -rm -f video/$(DEPDIR)/VideoRenderer.Plo + -rm -f video/$(DEPDIR)/VideoSource.Plo -rm -f webrtc_dsp/common_audio/signal_processing/$(DEPDIR)/complex_bit_reverse.Plo -rm -f webrtc_dsp/common_audio/signal_processing/$(DEPDIR)/complex_bit_reverse_arm.Plo -rm -f webrtc_dsp/common_audio/signal_processing/$(DEPDIR)/cross_correlation_neon.Plo diff --git a/NetworkSocket.cpp b/NetworkSocket.cpp index bbfb460f15..6176c8e1c1 100644 --- a/NetworkSocket.cpp +++ b/NetworkSocket.cpp @@ -429,9 +429,7 @@ void NetworkSocketSOCKS5Proxy::Receive(NetworkPacket *packet){ } void NetworkSocketSOCKS5Proxy::Open(){ - if(protocol==PROTO_UDP){ - unsigned char buf[1024]; - } + } void NetworkSocketSOCKS5Proxy::Close(){ diff --git a/VoIPController.cpp b/VoIPController.cpp index 5b0e737b6d..a97e7d048b 100755 --- a/VoIPController.cpp +++ b/VoIPController.cpp @@ -19,6 +19,7 @@ #include "OpusDecoder.h" #include "VoIPServerConfig.h" #include "PrivateDefines.h" +#include "json11.hpp" #include #include #include @@ -436,23 +437,7 @@ void VoIPController::SetNetworkType(int type){ bool isFirstChange=activeNetItfName.length()==0 && state!=STATE_ESTABLISHED && state!=STATE_RECONNECTING; activeNetItfName=itfName; if(IS_MOBILE_NETWORK(networkType)){ - CellularCarrierInfo carrier; -#if defined(__APPLE__) && TARGET_OS_IOS - carrier=DarwinSpecific::GetCarrierInfo(); -#elif defined(__ANDROID__) - jni::DoWithJNI([&carrier](JNIEnv* env){ - jmethodID getCarrierInfoMethod=env->GetStaticMethodID(jniUtilitiesClass, "getCarrierInfo", "()[Ljava/lang/String;"); - jobjectArray jinfo=(jobjectArray) env->CallStaticObjectMethod(jniUtilitiesClass, getCarrierInfoMethod); - if(jinfo && env->GetArrayLength(jinfo)==4){ - carrier.name=jni::JavaStringToStdString(env, (jstring)env->GetObjectArrayElement(jinfo, 0)); - carrier.countryCode=jni::JavaStringToStdString(env, (jstring)env->GetObjectArrayElement(jinfo, 1)); - carrier.mcc=jni::JavaStringToStdString(env, (jstring)env->GetObjectArrayElement(jinfo, 2)); - carrier.mnc=jni::JavaStringToStdString(env, (jstring)env->GetObjectArrayElement(jinfo, 3)); - }else{ - LOGW("Failed to get carrier info"); - } - }); -#endif + CellularCarrierInfo carrier=GetCarrierInfo(); if(!carrier.name.empty()){ LOGI("Carrier: %s [%s; mcc=%s, mnc=%s]", carrier.name.c_str(), carrier.countryCode.c_str(), carrier.mcc.c_str(), carrier.mnc.c_str()); } @@ -674,27 +659,34 @@ void VoIPController::GetStats(TrafficStats *stats){ } string VoIPController::GetDebugLog(){ - string log="{\"events\":["; - - for(vector::iterator itr=debugLogs.begin();itr!=debugLogs.end();++itr){ - log+=(*itr); - if((itr+1)!=debugLogs.end()) - log+=","; + vector lpkts; + for(DebugLoggedPacket& lpkt:debugLoggedPackets){ + lpkts.push_back(json11::Json::array{lpkt.timestamp, lpkt.seq, lpkt.length}); } - log+="],\"libtgvoip_version\":\"" LIBTGVOIP_VERSION "\"}"; - return log; -} - -void VoIPController::GetDebugLog(char *buffer){ - strcpy(buffer, GetDebugLog().c_str()); -} - -size_t VoIPController::GetDebugLogLength(){ - size_t len=128; - for(vector::iterator itr=debugLogs.begin();itr!=debugLogs.end();++itr){ - len+=(*itr).length()+1; + map network{ + {"type", NetworkTypeToString(networkType)} + }; + if(IS_MOBILE_NETWORK(networkType)){ + CellularCarrierInfo carrier=GetCarrierInfo(); + if(!carrier.name.empty()){ + network["carrier"]=carrier.name; + network["country"]=carrier.countryCode; + network["mcc"]=carrier.mcc; + network["mnc"]=carrier.mnc; + } } - return len; + return json11::Json(json11::Json::object{ + {"log_type", "out_packet_stats"}, + {"libtgvoip_version", LIBTGVOIP_VERSION}, + {"network", network}, + {"protocol_version", std::min(peerVersion, PROTOCOL_VERSION)}, + {"total_losses", json11::Json::object{ + {"s", (int32_t)conctl->GetSendLossCount()}, + {"r", (int32_t)recvLossCount} + }}, + {"call_duration", GetCurrentTime()-connectionInitTime}, + {"out_packet_stats", lpkts} + }).dump(); } vector VoIPController::EnumerateAudioInputs(){ @@ -943,6 +935,29 @@ shared_ptr VoIPController::GetStreamByType(int type, boo return s; } +CellularCarrierInfo VoIPController::GetCarrierInfo(){ +#if defined(__APPLE__) && TARGET_OS_IOS + return DarwinSpecific::GetCarrierInfo(); +#elif defined(__ANDROID__) + CellularCarrierInfo carrier; + jni::DoWithJNI([&carrier](JNIEnv* env){ + jmethodID getCarrierInfoMethod=env->GetStaticMethodID(jniUtilitiesClass, "getCarrierInfo", "()[Ljava/lang/String;"); + jobjectArray jinfo=(jobjectArray) env->CallStaticObjectMethod(jniUtilitiesClass, getCarrierInfoMethod); + if(jinfo && env->GetArrayLength(jinfo)==4){ + carrier.name=jni::JavaStringToStdString(env, (jstring)env->GetObjectArrayElement(jinfo, 0)); + carrier.countryCode=jni::JavaStringToStdString(env, (jstring)env->GetObjectArrayElement(jinfo, 1)); + carrier.mcc=jni::JavaStringToStdString(env, (jstring)env->GetObjectArrayElement(jinfo, 2)); + carrier.mnc=jni::JavaStringToStdString(env, (jstring)env->GetObjectArrayElement(jinfo, 3)); + }else{ + LOGW("Failed to get carrier info"); + } + }); + return carrier; +#else + return CellularCarrierInfo(); +#endif +} + #pragma mark - Audio I/O void VoIPController::AudioInputCallback(unsigned char* data, size_t length, unsigned char* secondaryData, size_t secondaryLength, void* param){ @@ -1959,6 +1974,18 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA } } + if(config.logPacketStats){ + DebugLoggedPacket dpkt={ + static_cast(pseq), + GetCurrentTime()-connectionInitTime, + static_cast(packetInnerLen) + }; + debugLoggedPackets.push_back(dpkt); + if(debugLoggedPackets.size()>=2500){ + debugLoggedPackets.erase(debugLoggedPackets.begin(), debugLoggedPackets.begin()+500); + } + } + //LOGV("acks: %u -> %.2lf, %.2lf, %.2lf, %.2lf, %.2lf, %.2lf, %.2lf, %.2lf", lastRemoteAckSeq, remoteAcks[0], remoteAcks[1], remoteAcks[2], remoteAcks[3], remoteAcks[4], remoteAcks[5], remoteAcks[6], remoteAcks[7]); //LOGD("recv: %u -> %.2lf, %.2lf, %.2lf, %.2lf, %.2lf, %.2lf, %.2lf, %.2lf", lastRemoteSeq, recvPacketTimes[0], recvPacketTimes[1], recvPacketTimes[2], recvPacketTimes[3], recvPacketTimes[4], recvPacketTimes[5], recvPacketTimes[6], recvPacketTimes[7]); //LOGI("RTT = %.3lf", GetAverageRTT()); @@ -2035,7 +2062,6 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA if(srcEndpoint.type==Endpoint::Type::UDP_RELAY || (useTCP && srcEndpoint.type==Endpoint::Type::TCP_RELAY)) preferredRelay=srcEndpoint.id; } - LogDebugInfo(); } } if(type==PKT_INIT_ACK){ @@ -2885,43 +2911,6 @@ void VoIPController::DebugCtl(int request, int param){ } } - - -void VoIPController::LogDebugInfo(){ - string json="{\"endpoints\":["; - unsigned int i=0; - for(pair& _e:endpoints){ - Endpoint& e=_e.second; - char buffer[1024]; - const char* typeStr="unknown"; - switch(e.type){ - case Endpoint::Type::UDP_RELAY: - typeStr="udp_relay"; - break; - case Endpoint::Type::UDP_P2P_INET: - typeStr="udp_p2p_inet"; - break; - case Endpoint::Type::UDP_P2P_LAN: - typeStr="udp_p2p_lan"; - break; - case Endpoint::Type::TCP_RELAY: - typeStr="tcp_relay"; - break; - } - snprintf(buffer, 1024, "{\"address\":\"%s\",\"port\":%u,\"type\":\"%s\",\"rtt\":%u%s%s}", e.address.ToString().c_str(), e.port, typeStr, (unsigned int)round(e.averageRTT*1000), currentEndpoint==e.id ? ",\"in_use\":true" : "", preferredRelay==e.id ? ",\"preferred\":true" : ""); - json+=buffer; - if(i!=endpoints.size()-1) - json+=","; - i++; - } - json+="],"; - char buffer[1024]; - const char* netTypeStr; - snprintf(buffer, 1024, "\"time\":%u,\"network_type\":\"%s\"}", (unsigned int)time(NULL), NetworkTypeToString(networkType).c_str()); - json+=buffer; - debugLogs.push_back(json); -} - void VoIPController::SendUdpPing(Endpoint& endpoint){ if(endpoint.type!=Endpoint::Type::UDP_RELAY) return; @@ -3184,7 +3173,6 @@ void VoIPController::SendRelayPings(){ currentEndpoint=preferredRelay; _currentEndpoint=_preferredRelay; } - LogDebugInfo(); } if(_currentEndpoint->type==Endpoint::Type::UDP_RELAY){ constexpr int64_t p2pID=(int64_t)(FOURCC('P','2','P','4')) << 32; @@ -3195,12 +3183,10 @@ void VoIPController::SendRelayPings(){ if(endpoints.find(lanID)!=endpoints.end() && endpoints[lanID].averageRTT>0 && endpoints[lanID].averageRTT0 && p2p.averageRTT0 && minPing<_currentEndpoint->averageRTT*p2pToRelaySwitchThreshold){ LOGI("Switching to relay"); currentEndpoint=preferredRelay; - LogDebugInfo(); } } } diff --git a/VoIPController.h b/VoIPController.h index 418781654f..354510d9de 100755 --- a/VoIPController.h +++ b/VoIPController.h @@ -161,21 +161,6 @@ namespace tgvoip{ }; - struct EncodedVideoFrame{ - unsigned char* data; - size_t size; - uint32_t flags; - - EncodedVideoFrame(size_t size){ - this->size=size; - data=(unsigned char*)malloc(size); - } - - ~EncodedVideoFrame(){ - free(data); - } - }; - class VoIPController{ friend class VoIPGroupController; public: @@ -207,6 +192,8 @@ namespace tgvoip{ bool enableAGC; bool enableCallUpgrade; + + bool logPacketStats=false; }; struct TrafficStats{ @@ -306,12 +293,6 @@ namespace tgvoip{ * @return */ std::string GetDebugLog(); - /** - * - * @param buffer - */ - void GetDebugLog(char* buffer); - size_t GetDebugLogLength(); /** * * @return @@ -488,6 +469,7 @@ namespace tgvoip{ Endpoint* GetEndpointForPacket(const PendingOutgoingPacket& pkt); bool SendOrEnqueuePacket(PendingOutgoingPacket pkt, bool enqueue=true); static std::string NetworkTypeToString(int type); + CellularCarrierInfo GetCarrierInfo(); private: struct Stream{ @@ -533,6 +515,11 @@ namespace tgvoip{ UDP_NOT_AVAILABLE, UDP_BAD }; + struct DebugLoggedPacket{ + int32_t seq; + double timestamp; + int32_t length; + }; void RunRecvThread(); void RunSendThread(); @@ -550,7 +537,6 @@ namespace tgvoip{ Endpoint& GetEndpointByType(int type); void SendPacketReliably(unsigned char type, unsigned char* data, size_t len, double retryInterval, double timeout); uint32_t GenerateOutSeq(); - void LogDebugInfo(); void ActuallySendPacket(NetworkPacket& pkt, Endpoint& ep); void InitializeAudio(); void StartAudio(); @@ -630,7 +616,6 @@ namespace tgvoip{ TrafficStats stats; bool receivedInit; bool receivedInitAck; - std::vector debugLogs; bool isOutgoing; NetworkSocket* udpSocket; NetworkSocket* realUdpSocket; @@ -688,6 +673,7 @@ namespace tgvoip{ HistoricBuffer unsentStreamPacketsHistory; bool needReInitUdpProxy=true; bool needRate=false; + std::vector debugLoggedPackets; uint32_t initTimeoutID=MessageThread::INVALID_ID; uint32_t noStreamsNopID=MessageThread::INVALID_ID; diff --git a/VoIPServerConfig.cpp b/VoIPServerConfig.cpp index 56cc6c4d6a..27f15a78d5 100644 --- a/VoIPServerConfig.cpp +++ b/VoIPServerConfig.cpp @@ -28,74 +28,43 @@ ServerConfig *ServerConfig::GetSharedInstance(){ bool ServerConfig::GetBoolean(std::string name, bool fallback){ MutexGuard sync(mutex); - if(ContainsKey(name)){ - std::string val=config[name]; - if(val=="true") - return true; - if(val=="false") - return false; - } + 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)){ - std::string val=config[name]; - std::istringstream stm(val); - double rval=fallback; - stm.imbue(std::locale("C")); - stm >> rval; - if(!stm.fail()) - return rval; - } + 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)){ - std::string val=config[name]; - char* end; - const char* start=val.c_str(); - int32_t d=strtol(start, &end, 0); - if(end!=start){ - return d; - } - } + 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)) - return config[name]; + if(ContainsKey(name) && config[name].is_string()) + return config[name].string_value(); return fallback; } -void ServerConfig::Update(std::map newValues){ +void ServerConfig::Update(std::string jsonString){ MutexGuard sync(mutex); LOGD("=== Updating voip config ==="); - config.clear(); - for(std::map::iterator itr=newValues.begin();itr!=newValues.end();++itr){ - std::string key=itr->first; - std::string val=itr->second; - LOGV("%s -> %s", key.c_str(), val.c_str()); - config[key]=val; - } -} - -void ServerConfig::Update(const char **values, int count) { - std::map result; - for (int i = 0; i < count / 2; i++) { - result[values[i * 2 + 0]] = std::string(values[i * 2 + 1]); - } - Update(result); + 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.find(key)!=config.end(); + return config.object_items().find(key)!=config.object_items().end(); } - - diff --git a/VoIPServerConfig.h b/VoIPServerConfig.h index f1770a0526..3eb5124e72 100644 --- a/VoIPServerConfig.h +++ b/VoIPServerConfig.h @@ -11,6 +11,7 @@ #include #include #include "threading.h" +#include "json11.hpp" namespace tgvoip{ @@ -23,13 +24,12 @@ public: double GetDouble(std::string name, double fallback); std::string GetString(std::string name, std::string fallback); bool GetBoolean(std::string name, bool fallback); - void Update(std::map newValues); - void Update(const char **values, int count); + void Update(std::string jsonString); private: static ServerConfig* sharedInstance; bool ContainsKey(std::string key); - std::map config; + json11::Json config; Mutex mutex; }; } diff --git a/client/android/tg_voip_jni.cpp b/client/android/tg_voip_jni.cpp index 5174bb5027..a8a3b6ff96 100644 --- a/client/android/tg_voip_jni.cpp +++ b/client/android/tg_voip_jni.cpp @@ -214,7 +214,7 @@ namespace tgvoip { ((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){ + 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; @@ -223,6 +223,7 @@ namespace tgvoip { cfg.enableNS=enableNS; cfg.enableAGC=enableAGC; cfg.enableCallUpgrade=false; + cfg.logPacketStats=logPacketStats; if(logFilePath){ cfg.logFilePath=jni::JavaStringToStdString(env, logFilePath); } @@ -320,18 +321,8 @@ namespace tgvoip { #pragma mark - VoIPServerConfig - void VoIPServerConfig_nativeSetConfig(JNIEnv* env, jclass clasz, jobjectArray keys, jobjectArray values){ - std::map config; - int len=env->GetArrayLength(keys); - int i; - for(i=0;iGetObjectArrayElement(keys, i); - jstring jval=(jstring)env->GetObjectArrayElement(values, i); - if(jkey==NULL|| jval==NULL) - continue; - config[jni::JavaStringToStdString(env, jkey)]=jni::JavaStringToStdString(env, jval); - } - ServerConfig::GetSharedInstance()->Update(config); + void VoIPServerConfig_nativeSetConfig(JNIEnv* env, jclass clasz, jstring jsonString){ + ServerConfig::GetSharedInstance()->Update(jni::JavaStringToStdString(env, jsonString)); } #pragma mark - Resampler @@ -540,7 +531,7 @@ extern "C" void tgvoipRegisterNatives(JNIEnv* env){ {"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;)V", (void*)&tgvoip::VoIPController_nativeSetConfig}, + {"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}, @@ -594,7 +585,7 @@ extern "C" void tgvoipRegisterNatives(JNIEnv* env){ // VoIPServerConfig JNINativeMethod serverConfigMethods[]={ - {"nativeSetConfig", "([Ljava/lang/String;[Ljava/lang/String;)V", (void*)&tgvoip::VoIPServerConfig_nativeSetConfig} + {"nativeSetConfig", "(Ljava/lang/String;)V", (void*)&tgvoip::VoIPServerConfig_nativeSetConfig} }; env->RegisterNatives(serverConfig, serverConfigMethods, sizeof(serverConfigMethods)/sizeof(JNINativeMethod)); diff --git a/json11.cpp b/json11.cpp new file mode 100755 index 0000000000..9647846b6d --- /dev/null +++ b/json11.cpp @@ -0,0 +1,788 @@ +/* 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 +#include +#include +#include +#include + +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)) { + char buf[32]; + snprintf(buf, sizeof buf, "%.17g", value); + out += buf; + } 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(ch) <= 0x1f) { + char buf[8]; + snprintf(buf, sizeof buf, "\\u%04x", ch); + out += buf; + } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 + && static_cast(value[i+2]) == 0xa8) { + out += "\\u2028"; + i += 2; + } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 + && static_cast(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 +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 *>(other)->m_value; + } + bool less(const JsonValue * other) const override { + return m_value < static_cast *>(other)->m_value; + } + + const T m_value; + void dump(string &out) const override { json11::dump(m_value, out); } +}; + +class JsonDouble final : public Value { + double number_value() const override { return m_value; } + int int_value() const override { return static_cast(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 { + 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 { + bool bool_value() const override { return m_value; } +public: + explicit JsonBoolean(bool value) : Value(value) {} +}; + +class JsonString final : public Value { + 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 { + 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 { + 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 { +public: + JsonNull() : Value({}) {} +}; + +/* * * * * * * * * * * * * * * * * * * * + * Static globals - static-init-safe + */ +struct Statics { + const std::shared_ptr null = make_shared(); + const std::shared_ptr t = make_shared(true); + const std::shared_ptr f = make_shared(false); + const string empty_string; + const vector empty_vector; + const map 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(value)) {} +Json::Json(int value) : m_ptr(make_shared(value)) {} +Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {} +Json::Json(const string &value) : m_ptr(make_shared(value)) {} +Json::Json(string &&value) : m_ptr(make_shared(move(value))) {} +Json::Json(const char * value) : m_ptr(make_shared(value)) {} +Json::Json(const Json::array &values) : m_ptr(make_shared(values)) {} +Json::Json(Json::array &&values) : m_ptr(make_shared(move(values))) {} +Json::Json(const Json::object &values) : m_ptr(make_shared(values)) {} +Json::Json(Json::object &&values) : m_ptr(make_shared(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::array_items() const { return m_ptr->array_items(); } +const map & 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 & JsonValue::array_items() const { return statics().empty_vector; } +const map & 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(c) >= 0x20 && static_cast(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 + 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(pt); + } else if (pt < 0x800) { + out += static_cast((pt >> 6) | 0xC0); + out += static_cast((pt & 0x3F) | 0x80); + } else if (pt < 0x10000) { + out += static_cast((pt >> 12) | 0xE0); + out += static_cast(((pt >> 6) & 0x3F) | 0x80); + out += static_cast((pt & 0x3F) | 0x80); + } else { + out += static_cast((pt >> 18) | 0xF0); + out += static_cast(((pt >> 12) & 0x3F) | 0x80); + out += static_cast(((pt >> 6) & 0x3F) | 0x80); + out += static_cast((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(std::numeric_limits::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++; + } + + return std::strtod(str.c_str() + start_pos, nullptr); + } + + /* 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 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 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::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_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 diff --git a/json11.hpp b/json11.hpp new file mode 100755 index 0000000000..0c47d05093 --- /dev/null +++ b/json11.hpp @@ -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 +#include +#include +#include +#include + +#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 array; + typedef std::map 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 + Json(const T & t) : Json(t.to_json()) {} + + // Implicit constructor: map-like objects (std::map, std::unordered_map, etc) + template ().begin()->first)>::value + && std::is_constructible().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 ().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 parse_multi( + const std::string & in, + std::string::size_type & parser_stop_pos, + std::string & err, + JsonParse strategy = JsonParse::STANDARD); + + static inline std::vector 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> shape; + bool has_shape(const shape & types, std::string & err) const; + +private: + std::shared_ptr 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 diff --git a/libtgvoip.gyp b/libtgvoip.gyp index c93e96239c..8c62b56120 100644 --- a/libtgvoip.gyp +++ b/libtgvoip.gyp @@ -70,6 +70,8 @@ '<(tgvoip_src_loc)/video/VideoSource.h', '<(tgvoip_src_loc)/video/VideoRenderer.cpp', '<(tgvoip_src_loc)/video/VideoRenderer.h', + '<(tgvoip_src_loc)/json11.cpp', + '<(tgvoip_src_loc)/json11.hpp', # Windows '<(tgvoip_src_loc)/os/windows/NetworkSocketWinsock.cpp', diff --git a/libtgvoip.xcodeproj/project.pbxproj b/libtgvoip.xcodeproj/project.pbxproj index 36ba9a1365..9bd089ffa5 100644 --- a/libtgvoip.xcodeproj/project.pbxproj +++ b/libtgvoip.xcodeproj/project.pbxproj @@ -313,6 +313,7 @@ 69DD8D02218CD401001E8140 /* VideoRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 69DD8CFE218CD400001E8140 /* VideoRenderer.cpp */; }; 69DD8D03218CD401001E8140 /* VideoSource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 69DD8CFF218CD400001E8140 /* VideoSource.cpp */; }; 69E357B020F88955002E163B /* AudioIO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 69E357A720F88954002E163B /* AudioIO.cpp */; }; + 69E629EE21AF62A900377D0F /* json11.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 69E629ED21AF62A900377D0F /* json11.cpp */; }; 69FB0B2D20F6860E00827817 /* MessageThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 69FB0B2420F6860D00827817 /* MessageThread.cpp */; }; /* End PBXBuildFile section */ @@ -1022,6 +1023,7 @@ 69DD8D00218CD400001E8140 /* VideoSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VideoSource.h; sourceTree = ""; }; 69E357A720F88954002E163B /* AudioIO.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AudioIO.cpp; sourceTree = ""; }; 69E357AF20F88954002E163B /* AudioIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioIO.h; sourceTree = ""; }; + 69E629ED21AF62A900377D0F /* json11.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json11.cpp; sourceTree = ""; }; 69F842361E67540700C110F7 /* libtgvoip.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = libtgvoip.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 69FB0B2420F6860D00827817 /* MessageThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MessageThread.cpp; sourceTree = ""; }; 69FB0B2C20F6860D00827817 /* MessageThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MessageThread.h; sourceTree = ""; }; @@ -1067,6 +1069,7 @@ 692AB8A71E6759DD00706ACC /* Info.plist */, 692AB8A81E6759DD00706ACC /* JitterBuffer.cpp */, 692AB8A91E6759DD00706ACC /* JitterBuffer.h */, + 69E629ED21AF62A900377D0F /* json11.cpp */, 6915307A1E6B5BAB004F643F /* logging.cpp */, 692AB8AA1E6759DD00706ACC /* logging.h */, 692AB8AB1E6759DD00706ACC /* MediaStreamItf.cpp */, @@ -2701,6 +2704,7 @@ 697E9C4121A4ED6C00E03846 /* ns_core.c in Sources */, 69E357B020F88955002E163B /* AudioIO.cpp in Sources */, 697E9D3221A4ED6D00E03846 /* downsampled_render_buffer.cc in Sources */, + 69E629EE21AF62A900377D0F /* json11.cpp in Sources */, 697E9D6B21A4ED6E00E03846 /* ooura_fft_sse2.cc in Sources */, 697E9C6421A4ED6C00E03846 /* adaptive_digital_gain_applier.cc in Sources */, 697E9C9421A4ED6D00E03846 /* noise_spectrum_estimator.cc in Sources */, diff --git a/libtgvoip_osx.xcodeproj/project.pbxproj b/libtgvoip_osx.xcodeproj/project.pbxproj index 9a0c0aba0a..c7af1576b3 100644 --- a/libtgvoip_osx.xcodeproj/project.pbxproj +++ b/libtgvoip_osx.xcodeproj/project.pbxproj @@ -314,6 +314,7 @@ 697B6FD62136E1F3004C8E54 /* AudioIO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 697B6FD42136E1F3004C8E54 /* AudioIO.cpp */; }; 697B6FDA2136E2D9004C8E54 /* AudioIOCallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 697B6FD82136E2D9004C8E54 /* AudioIOCallback.cpp */; }; 697B6FDF2136F01E004C8E54 /* MockReflector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 697B6FDE2136F01E004C8E54 /* MockReflector.cpp */; }; + 69A2076521AF628A003AC4F9 /* json11.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 69A2076421AF6289003AC4F9 /* json11.cpp */; }; 69A6DF431E9614B700000E69 /* AudioInputAudioUnitOSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 69A6DF3F1E9614B700000E69 /* AudioInputAudioUnitOSX.cpp */; }; 69A6DF451E9614B700000E69 /* AudioOutputAudioUnitOSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 69A6DF411E9614B700000E69 /* AudioOutputAudioUnitOSX.cpp */; }; 69EBC7912136D220003CFE90 /* AudioInputAudioUnitOSX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C2A87DDC1F4B6A61002D3F73 /* AudioInputAudioUnitOSX.cpp */; }; @@ -1039,6 +1040,7 @@ 697B6FDC2136E673004C8E54 /* utils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = utils.h; sourceTree = ""; }; 697B6FDD2136F01E004C8E54 /* MockReflector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockReflector.h; sourceTree = ""; }; 697B6FDE2136F01E004C8E54 /* MockReflector.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MockReflector.cpp; sourceTree = ""; }; + 69A2076421AF6289003AC4F9 /* json11.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json11.cpp; sourceTree = ""; }; 69A6DF3F1E9614B700000E69 /* AudioInputAudioUnitOSX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AudioInputAudioUnitOSX.cpp; path = os/darwin/AudioInputAudioUnitOSX.cpp; sourceTree = SOURCE_ROOT; }; 69A6DF401E9614B700000E69 /* AudioInputAudioUnitOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AudioInputAudioUnitOSX.h; path = os/darwin/AudioInputAudioUnitOSX.h; sourceTree = SOURCE_ROOT; }; 69A6DF411E9614B700000E69 /* AudioOutputAudioUnitOSX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AudioOutputAudioUnitOSX.cpp; path = os/darwin/AudioOutputAudioUnitOSX.cpp; sourceTree = SOURCE_ROOT; }; @@ -2218,6 +2220,7 @@ 692AB8A71E6759DD00706ACC /* Info.plist */, 692AB8A81E6759DD00706ACC /* JitterBuffer.cpp */, 692AB8A91E6759DD00706ACC /* JitterBuffer.h */, + 69A2076421AF6289003AC4F9 /* json11.cpp */, 6915307A1E6B5BAB004F643F /* logging.cpp */, 692AB8AA1E6759DD00706ACC /* logging.h */, 692AB8AB1E6759DD00706ACC /* MediaStreamItf.cpp */, @@ -2825,6 +2828,7 @@ 691E063821A4FD7600F838EF /* real_fft.c in Sources */, 691E066C21A4FD7600F838EF /* arith_routines_logist.c in Sources */, 691E060221A4FD7600F838EF /* sinc_resampler.cc in Sources */, + 69A2076521AF628A003AC4F9 /* json11.cpp in Sources */, 691E076821A4FD7700F838EF /* subband_erle_estimator.cc in Sources */, 691E07A521A4FD7700F838EF /* echo_canceller3.cc in Sources */, 691E070621A4FD7700F838EF /* adaptive_mode_level_estimator.cc in Sources */, diff --git a/os/posix/NetworkSocketPosix.cpp b/os/posix/NetworkSocketPosix.cpp index 8c6f2757a6..26b449cee6 100644 --- a/os/posix/NetworkSocketPosix.cpp +++ b/os/posix/NetworkSocketPosix.cpp @@ -161,7 +161,7 @@ void NetworkSocketPosix::Send(NetworkPacket *packet){ LOGI("Network unreachable, trying NAT64"); } } - }else if(res!=packet->length && packet->protocol==PROTO_TCP){ + }else if((size_t)res!=packet->length && packet->protocol==PROTO_TCP){ if(pendingOutgoingPacket){ LOGE("send returned less than packet length but there's already a pending packet"); failed=true;