// // 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 #include #endif #ifdef __APPLE__ #include #include "os/darwin/AudioUnitIO.h" #endif #include #include #include #include #include #include #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" #define LIBTGVOIP_VERSION "2.2.3" #ifdef _WIN32 #undef GetCurrentTime #undef ERROR_TIMEOUT #endif #define TGVOIP_PEER_CAP_GROUP_CALLS 1 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); }; class Endpoint{ friend class VoIPController; friend class VoIPGroupController; public: enum{ TYPE_UDP_P2P_INET=1, TYPE_UDP_P2P_LAN, TYPE_UDP_RELAY, TYPE_TCP_RELAY }; Endpoint(int64_t id, uint16_t port, IPv4Address& address, IPv6Address& v6address, char type, unsigned char* peerTag); Endpoint(); ~Endpoint(); NetworkAddress& GetAddress(); bool IsIPv6Only(); int64_t id; uint16_t port; IPv4Address address; IPv6Address v6address; char type; unsigned char peerTag[16]; private: double lastPingTime; uint32_t lastPingSeq; HistoricBuffer rtts; double averageRTT; NetworkSocket* socket; int udpPongCount; }; class AudioDevice{ public: std::string id; std::string displayName; }; class AudioOutputDevice : public AudioDevice{ }; class AudioInputDevice : public AudioDevice{ }; 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: 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; std::string logFilePath=""; std::string statsDumpFilePath=""; bool enableAEC; bool enableNS; bool enableAGC; bool enableCallUpgrade; }; 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 */ void SetRemoteEndpoints(std::vector 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(); /** * * @param buffer */ void GetDebugLog(char* buffer); size_t GetDebugLogLength(); /** * * @return */ static std::vector EnumerateAudioInputs(); /** * * @return */ static std::vector 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); 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; }; private: struct Stream; struct UnacknowledgedExtraData; protected: struct RecentOutgoingPacket{ uint32_t seq; uint16_t id; // for group calls only double sendTime; double ackTime; }; struct PendingOutgoingPacket{ uint32_t seq; unsigned char type; size_t len; Buffer data; int64_t endpoint; }; struct SegmentedPacket{ unsigned char type; }; struct QueuedPacket{ Buffer data; unsigned char type; HistoricBuffer seqs; double firstSentTime; double lastSentTime; double retryInterval; double timeout; }; virtual void ProcessIncomingPacket(NetworkPacket& packet, std::shared_ptr srcEndpoint); virtual void ProcessExtraData(Buffer& data); virtual void WritePacketHeader(uint32_t seq, BufferOutputStream* s, unsigned char type, uint32_t length); virtual void SendPacket(unsigned char* data, size_t len, std::shared_ptr ep, PendingOutgoingPacket& srcPacket); virtual void SendInit(); virtual void SendUdpPing(std::shared_ptr endpoint); virtual void SendRelayPings(); virtual void OnAudioOutputReady(); virtual void SendExtra(Buffer& data, unsigned char type); void SendStreamFlags(Stream& stream); void InitializeTimers(); void ResetEndpointPingStats(); private: 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; std::shared_ptr decoder; std::shared_ptr packetReassembler; std::shared_ptr callbackWrapper; std::vector codecSpecificData; }; struct UnacknowledgedExtraData{ unsigned char type; Buffer data; uint32_t firstContainingSeq; }; enum{ UDP_UNKNOWN=0, UDP_PING_PENDING, UDP_PING_SENT, UDP_AVAILABLE, UDP_NOT_AVAILABLE, UDP_BAD }; void RunRecvThread(void* arg); void RunSendThread(void* arg); void HandleAudioInput(unsigned char* data, size_t len, unsigned char* secondaryData, size_t secondaryLen); void HandleVideoInput(EncodedVideoFrame& frame); 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); static void AudioInputCallback(unsigned char* data, size_t length, unsigned char* secondaryData, size_t secondaryLength, void* param); void SendPublicEndpointsRequest(); void SendPublicEndpointsRequest(Endpoint& relay); std::shared_ptr GetEndpointByType(int type); std::shared_ptr GetEndpointByID(int64_t id); void SendPacketReliably(unsigned char type, unsigned char* data, size_t len, double retryInterval, double timeout); uint32_t GenerateOutSeq(); void LogDebugInfo(); void ActuallySendPacket(NetworkPacket& pkt, std::shared_ptr ep); 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 TickJitterBufferAngCongestionControl(); void ResetUdpAvailability(); int state; std::vector> endpoints; std::shared_ptr currentEndpoint; std::shared_ptr preferredRelay; std::shared_ptr peerPreferredRelay; bool runReceiver; uint32_t seq; uint32_t lastRemoteSeq; uint32_t lastRemoteAckSeq; uint32_t lastSentSeq; std::vector recentOutgoingPackets; double recvPacketTimes[32]; HistoricBuffer sendLossCountHistory; uint32_t audioTimestampIn; uint32_t audioTimestampOut; std::shared_ptr audioIO; tgvoip::audio::AudioInput* audioInput; tgvoip::audio::AudioOutput* audioOutput; OpusEncoder* encoder; BlockingQueue* sendQueue; EchoCanceller* echoCanceller; Mutex sendBufferMutex; Mutex endpointsMutex; Mutex socketSelectMutex; bool stopping; bool audioOutStarted; Thread* recvThread; Thread* sendThread; uint32_t packetsReceived; uint32_t recvLossCount; uint32_t prevSendLossCount; uint32_t firstSentPing; HistoricBuffer rttHistory; bool waitingForAcks; int networkType; int dontSendPackets; int lastError; bool micMuted; uint32_t maxBitrate; std::vector> outgoingStreams; std::vector> 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 queuedPackets; Mutex audioIOMutex; Mutex queuedPacketsMutex; double connectionInitTime; double lastRecvPacketTime; Config config; int32_t peerVersion; CongestionControl* conctl; TrafficStats stats; bool receivedInit; bool receivedInitAck; std::vector debugLogs; bool isOutgoing; NetworkSocket* udpSocket; NetworkSocket* realUdpSocket; FILE* statsDump; std::string currentAudioInput; std::string currentAudioOutput; bool useTCP; bool useUDP; bool didAddTcpRelays; SocketSelectCanceller* selectCanceller; NetworkSocket* openingTcpSocket; HistoricBuffer signalBarsHistory; int udpConnectivityState; double lastUdpPingTime; int udpPingCount; int echoCancellationStrength; int proxyProtocol; std::string proxyAddress; uint16_t proxyPort; std::string proxyUsername; std::string proxyPassword; IPv4Address* resolvedProxyAddress; AutomaticGainControl* outputAGC; bool outputAGCEnabled; uint32_t peerCapabilities; Callbacks callbacks; bool didReceiveGroupCallKey; bool didReceiveGroupCallKeyAck; bool didSendGroupCallKey; bool didSendUpgradeRequest; bool didInvokeUpgradeCallback; int32_t connectionMaxLayer; bool useMTProto2; bool setCurrentEndpointToTCP; std::vector currentExtras; std::unordered_map lastReceivedExtrasByType; bool useIPv6; bool peerIPv6Available; IPv6Address myIPv6; bool shittyInternetMode; std::vector ecAudioPackets; bool didAddIPv6Relays; bool didSendIPv6Endpoint; int publicEndpointsReqCount=0; MessageThread messageThread; bool wasEstablished=false; bool receivedFirstStreamPacket=false; std::atomic unsentStreamPackets; uint32_t initTimeoutID=MessageThread::INVALID_ID; uint32_t noStreamsNopID=MessageThread::INVALID_ID; uint32_t udpPingTimeoutID=MessageThread::INVALID_ID; /*** 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; 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, IPv4Address reflectorAddress, IPv6Address 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, std::shared_ptr srcEndpoint); virtual void SendInit(); virtual void SendUdpPing(std::shared_ptr endpoint); virtual void SendRelayPings(); virtual void SendPacket(unsigned char* data, size_t len, std::shared_ptr ep, PendingOutgoingPacket& srcPacket); virtual void WritePacketHeader(uint32_t seq, BufferOutputStream* s, unsigned char type, uint32_t length); virtual void OnAudioOutputReady(); private: int32_t GetCurrentUnixtime(); std::vector> 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> streams; AudioLevelMeter* levelMeter; }; std::vector participants; unsigned char reflectorSelfTag[16]; unsigned char reflectorSelfSecret[16]; unsigned char reflectorSelfTagHash[32]; int32_t userSelfID; std::shared_ptr groupReflector; AudioMixer* audioMixer; AudioLevelMeter selfLevelMeter; Callbacks groupCallbacks; struct PacketIdMapping{ uint32_t seq; uint16_t id; double ackTime; }; std::vector recentSentPackets; Mutex sentPacketsMutex; Mutex participantsMutex; int32_t timeDifference; }; }; #endif