Nagram/TMessagesProj/jni/libtgvoip3/VoIPController.h
2020-04-24 12:21:58 +03:00

685 lines
22 KiB
C++

//
// 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
#include "utils.h"
#include "AudioDevice.h"
#include "BlockingQueue.h"
#include "Buffers.h"
#include "CongestionControl.h"
#include "EchoCanceller.h"
#include "Endpoint.h"
#include "JitterBuffer.h"
#include "MessageThread.h"
#include "NetworkSocket.h"
#include "OpusDecoder.h"
#include "OpusEncoder.h"
#include "PacketReassembler.h"
#include "VoIPControllerDeclarations.h"
#include "audio/AudioIO.h"
#include "audio/AudioInput.h"
#include "audio/AudioOutput.h"
#include "video/ScreamCongestionController.h"
#include "video/VideoRenderer.h"
#include "video/VideoSource.h"
#ifndef _WIN32
#include <arpa/inet.h>
#include <netinet/in.h>
#endif
#ifdef __APPLE__
#include "os/darwin/AudioUnitIO.h"
#include <TargetConditionals.h>
#endif
#include <atomic>
#include <map>
#include <memory>
#include <cstdint>
#include <string>
#include <unordered_map>
#include <vector>
#include <deque>
#define LIBTGVOIP_VERSION "2.7"
#ifdef _WIN32
#undef GetCurrentTime
#undef Error::TIMEOUT
#endif
namespace tgvoip
{
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, DataSaving dataSaving = DataSaving::NEVER,
bool enableAEC = false, bool enableNS = false, bool enableAGC = false, bool enableCallUpgrade = false);
double initTimeout;
double recvTimeout;
DataSaving 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
{
std::uint64_t bytesSentWifi;
std::uint64_t bytesRecvdWifi;
std::uint64_t bytesSentMobile;
std::uint64_t bytesRecvdMobile;
};
struct PendingOutgoingPacket
{
PendingOutgoingPacket(std::uint32_t seq, PktType type, std::size_t len, Buffer&& data, std::int64_t endpoint);
PendingOutgoingPacket(PendingOutgoingPacket&& other) noexcept;
PendingOutgoingPacket& operator=(PendingOutgoingPacket&& other) noexcept;
TGVOIP_DISALLOW_COPY_AND_ASSIGN(PendingOutgoingPacket);
std::uint32_t seq;
PktType type;
std::size_t len;
Buffer data;
std::int64_t endpoint;
};
struct Stream
{
std::int32_t userID;
std::uint8_t id;
StreamType type;
std::uint32_t codec;
bool enabled;
bool extraECEnabled;
std::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;
std::uint16_t rotation = 0;
};
struct ProtocolInfo
{
std::uint32_t version;
InitVideoRes maxVideoResolution;
std::vector<std::uint32_t> videoDecoders;
bool videoCaptureSupported;
bool videoDisplaySupported;
bool callUpgradeSupported;
};
/**
* Use this field to store any of your context data associated with this call
*/
void* implData;
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(const std::vector<Endpoint>& m_endpoints, bool m_allowP2p, std::int32_t m_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(NetType type);
/**
* Get the average round-trip time for network packets
* @return
*/
double GetAverageRTT();
static double GetCurrentTime();
virtual void SetMicMute(bool mute);
void SetEncryptionKey(char* key, bool m_isOutgoing);
void SetConfig(const Config& cfg);
void DebugCtl(int request, int param);
void GetStats(TrafficStats* m_stats);
std::int64_t GetPreferredRelayID();
Error GetLastError();
static CryptoFunctions crypto;
static const char* GetVersion();
std::string GetDebugLog();
static std::vector<AudioInputDevice> EnumerateAudioInputs();
static std::vector<AudioOutputDevice> EnumerateAudioOutputs();
void SetCurrentAudioInput(std::string id);
void SetCurrentAudioOutput(std::string id);
std::string GetCurrentAudioInputID() const;
std::string GetCurrentAudioOutputID() const;
/**
* 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(Proxy protocol, std::string address, std::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
*/
std::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(std::uint8_t* 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);
State GetConnectionState() const;
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 std::int32_t GetConnectionMaxLayer();
/**
* 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<std::uint8_t> GetPersistentState();
/**
* Load the persistable state. Call this before starting the call.
*/
void SetPersistentState(const std::vector<std::uint8_t>& m_state);
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
void SetAudioDataCallbacks(std::function<void(std::int16_t*, std::size_t)> input, std::function<void(std::int16_t*, std::size_t)> output, std::function<void(std::int16_t*, std::size_t)> preprocessed);
#endif
void SetVideoCodecSpecificData(const std::vector<Buffer>& data);
struct Callbacks
{
void (*connectionStateChanged)(VoIPController*, State);
void (*signalBarCountChanged)(VoIPController*, int);
void (*groupCallKeySent)(VoIPController*);
void (*groupCallKeyReceived)(VoIPController*, const std::uint8_t*);
void (*upgradeToGroupCallRequested)(VoIPController*);
};
void SetCallbacks(Callbacks m_callbacks);
float GetOutputLevel() const;
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
#ifdef __APPLE__
static double machTimebase;
static std::uint64_t machTimestart;
#endif
#ifdef _WIN32
static std::int64_t win32TimeScale;
static bool didInitWin32TimeScale;
#endif
protected:
struct RecentOutgoingPacket
{
std::uint32_t seq;
std::uint16_t id; // for group calls only
double sendTime;
double ackTime;
PktType type;
std::uint32_t size;
PacketSender* sender;
bool lost;
};
struct QueuedPacket
{
Buffer data;
PktType type;
HistoricBuffer<std::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(std::uint32_t pseq, BufferOutputStream* s, PktType type, std::uint32_t length, PacketSender* source);
virtual void SendPacket(std::uint8_t* data, std::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, ExtraType type);
void SendStreamFlags(Stream& stream);
void InitializeTimers();
void ResetEndpointPingStats();
void SendVideoFrame(const Buffer& frame, std::uint32_t flags, std::uint32_t rotation);
void ProcessIncomingVideoFrame(Buffer frame, std::uint32_t pts, bool keyframe, std::uint16_t rotation);
std::shared_ptr<Stream> GetStreamByType(StreamType, bool outgoing) const;
std::shared_ptr<Stream> GetStreamByID(std::uint8_t id, bool outgoing) const;
Endpoint* GetEndpointForPacket(const PendingOutgoingPacket& pkt);
bool SendOrEnqueuePacket(PendingOutgoingPacket pkt, bool enqueue = true, PacketSender* source = nullptr);
static std::string NetworkTypeToString(NetType type);
CellularCarrierInfo GetCarrierInfo();
private:
struct UnacknowledgedExtraData;
struct UnacknowledgedExtraData
{
ExtraType type;
Buffer data;
std::uint32_t firstContainingSeq;
};
struct RecentIncomingPacket
{
std::uint32_t seq;
double recvTime;
};
struct DebugLoggedPacket
{
std::int32_t seq;
double timestamp;
std::int32_t length;
};
struct RawPendingOutgoingPacket
{
TGVOIP_MOVE_ONLY(RawPendingOutgoingPacket);
NetworkPacket packet;
std::shared_ptr<NetworkSocket> socket;
};
enum class UdpState
{
UNKNOWN = 0,
PING_PENDING,
PING_SENT,
AVAILABLE,
NOT_AVAILABLE,
BAD,
};
void RunRecvThread();
void RunSendThread();
void HandleAudioInput(std::uint8_t* data, std::size_t len, std::uint8_t* secondaryData, std::size_t secondaryLen);
void UpdateAudioBitrateLimit();
void SetState(State m_state);
void UpdateAudioOutputState();
void InitUDPProxy();
void UpdateDataSavingState();
void KDF(std::uint8_t* msgKey, std::size_t x, std::uint8_t* aesKey, std::uint8_t* aesIv);
void KDF2(std::uint8_t* msgKey, std::size_t x, std::uint8_t* aesKey, std::uint8_t* aesIv);
void SendPublicEndpointsRequest();
void SendPublicEndpointsRequest(const Endpoint& relay);
Endpoint& GetEndpointByType(Endpoint::Type type);
void SendPacketReliably(PktType type, std::uint8_t* data, std::size_t len, double retryInterval, double timeout);
std::uint32_t GenerateOutSeq();
void ActuallySendPacket(NetworkPacket pkt, Endpoint& ep);
void InitializeAudio();
void StartAudio();
void ProcessRelaySpecialRequest(BufferInputStream& in, Endpoint& srcEndpoint);
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(PktType type);
void SetupOutgoingVideoStream();
bool WasOutgoingPacketAcknowledged(std::uint32_t seq);
RecentOutgoingPacket* GetRecentOutgoingPacket(std::uint32_t seq);
void NetworkPacketReceived(std::shared_ptr<NetworkPacket> packet);
void TrySendQueuedPackets();
TrafficStats m_stats =
{
.bytesSentWifi = 0,
.bytesRecvdWifi = 0,
.bytesSentMobile = 0,
.bytesRecvdMobile = 0
};
Config m_config;
State m_state = State::WAIT_INIT;
NetType m_networkType = NetType::UNKNOWN;
MessageThread m_messageThread;
std::int64_t m_preferredRelay = 0;
std::int64_t m_peerPreferredRelay = 0;
tgvoip::audio::AudioIO* m_audioIO = nullptr;
tgvoip::audio::AudioInput* m_audioInput = nullptr;
tgvoip::audio::AudioOutput* m_audioOutput = nullptr;
std::string m_currentAudioInput = "default";
std::string m_currentAudioOutput = "default";
std::uint32_t m_audioTimestampIn = 0;
std::uint32_t m_audioTimestampOut = 0;
OpusEncoder* m_encoder = nullptr;
EchoCanceller* m_echoCanceller = nullptr;
Thread* m_recvThread = nullptr;
Thread* m_sendThread = nullptr;
double m_stateChangeTime;
double m_publicEndpointsReqTime = 0;
double m_connectionInitTime = 0;
double m_lastRecvPacketTime = 0;
double m_lastUdpPingTime = 0;
CongestionControl* m_congestionControl;
NetworkSocket* m_udpSocket;
NetworkSocket* m_realUdpSocket;
UdpState m_udpConnectivityState = UdpState::UNKNOWN;
int m_udpPingCount = 0;
SocketSelectCanceller* m_selectCanceller;
FILE* m_statsDump = nullptr;
video::VideoSource* m_videoSource = nullptr;
video::VideoRenderer* m_videoRenderer = nullptr;
video::VideoPacketSender* m_videoPacketSender = nullptr;
std::vector<std::uint32_t> m_peerVideoDecoders;
double m_relaySwitchThreshold;
double m_p2pToRelaySwitchThreshold;
double m_relayToP2pSwitchThreshold;
double m_reconnectingTimeout;
double m_rateMaxAcceptableRTT;
double m_rateMaxAcceptableSendLoss;
double m_packetLossToEnableExtraEC;
HistoricBuffer<std::uint8_t, 4, int> m_signalBarsHistory;
std::deque<RecentOutgoingPacket> m_recentOutgoingPackets;
std::deque<std::uint32_t> m_recentIncomingPackets;
std::uint32_t m_packetsReceived = 0;
std::uint32_t m_recvLossCount = 0;
std::uint32_t m_prevSendLossCount = 0;
std::uint32_t m_firstSentPing;
std::list<PendingOutgoingPacket> m_sendQueue;
BlockingQueue<RawPendingOutgoingPacket> m_rawSendQueue;
std::list<QueuedPacket> m_queuedPackets;
std::list<UnacknowledgedExtraData> m_currentExtras;
HistoricBuffer<std::uint32_t, 5> m_unsentStreamPacketsHistory;
HistoricBuffer<std::uint32_t, 10, double> m_sendLossCountHistory;
HistoricBuffer<double, 32> m_RTTHistory;
std::unordered_map<ExtraType, std::uint64_t> m_lastReceivedExtrasByType;
BufferPool<1024, 32> m_outgoingAudioBufferPool;
std::vector<std::shared_ptr<Stream>> m_outgoingStreams;
std::vector<std::shared_ptr<Stream>> m_incomingStreams;
std::deque<Buffer> m_ecAudioPackets;
std::deque<DebugLoggedPacket> m_debugLoggedPackets;
effects::Volume m_outputVolume;
effects::Volume m_inputVolume;
std::string m_activeNetItfName = "";
std::string m_proxyAddress = "";
std::uint16_t m_proxyPort = 0;
std::string m_proxyUsername = "";
std::string m_proxyPassword = "";
std::string m_lastTestedProxyServer = "";
NetworkAddress m_resolvedProxyAddress = NetworkAddress::Empty();
NetworkAddress m_myIPv6 = NetworkAddress::Empty();
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
std::function<void(std::int16_t*, std::size_t)> m_audioInputDataCallback;
std::function<void(std::int16_t*, std::size_t)> m_audioOutputDataCallback;
std::function<void(std::int16_t*, std::size_t)> m_audioPreprocDataCallback;
::OpusDecoder* m_preprocDecoder = nullptr;
std::int16_t m_preprocBuffer[4096];
#endif
Callbacks m_callbacks =
{
.connectionStateChanged = nullptr,
.signalBarCountChanged = nullptr,
.groupCallKeySent = nullptr,
.groupCallKeyReceived = nullptr,
.upgradeToGroupCallRequested = nullptr
};
// 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 m_endpointsMutex;
// Locked while audio i/o is being initialized and deinitialized so as to allow it to fully initialize before deinitialization begins.
Mutex m_audioIOMutex;
ProtocolInfo m_protocolInfo =
{
.version = 0,
.maxVideoResolution = InitVideoRes::NONE,
.videoDecoders = {},
.videoCaptureSupported = false,
.videoDisplaySupported = false,
.callUpgradeSupported = false
};
std::unordered_map<std::int64_t, Endpoint> m_endpoints;
std::int64_t m_currentEndpoint = 0;
std::atomic<std::uint32_t> m_seq{1};
std::uint32_t m_lastRemoteSeq = 0;
std::uint32_t m_lastRemoteAckSeq = 0;
std::uint32_t m_lastSentSeq = 0;
int m_dontSendPackets = 0;
Error m_lastError;
std::uint32_t m_maxBitrate;
std::int32_t m_peerVersion = 0;
std::uint32_t m_peerCapabilities = 0;
Proxy m_proxyProtocol = Proxy::NONE;
std::int32_t m_connectionMaxLayer = 0;
std::atomic<std::uint32_t> m_unsentStreamPackets;
int m_echoCancellationStrength = 1;
int m_extraEcLevel = 0;
int m_publicEndpointsReqCount = 0;
std::uint32_t m_initTimeoutID = MessageThread::INVALID_ID;
std::uint32_t m_udpPingTimeoutID = MessageThread::INVALID_ID;
std::uint32_t m_lastReceivedVideoFrameNumber = std::numeric_limits<std::uint32_t>::max();
std::uint32_t m_sendLosses = 0;
std::uint32_t m_unacknowledgedIncomingPacketCount = 0;
/*** server config values ***/
std::uint32_t m_maxAudioBitrate;
std::uint32_t m_maxAudioBitrateEDGE;
std::uint32_t m_maxAudioBitrateGPRS;
std::uint32_t m_maxAudioBitrateSaving;
std::uint32_t m_initAudioBitrate;
std::uint32_t m_initAudioBitrateEDGE;
std::uint32_t m_initAudioBitrateGPRS;
std::uint32_t m_initAudioBitrateSaving;
std::uint32_t m_minAudioBitrate;
std::uint32_t m_audioBitrateStepIncr;
std::uint32_t m_audioBitrateStepDecr;
std::uint32_t m_maxUnsentStreamPackets;
std::uint32_t m_unackNopThreshold;
std::uint32_t m_needRateFlags;
std::uint8_t m_encryptionKey[256];
std::uint8_t m_keyFingerprint[8];
std::uint8_t m_callID[16];
std::atomic<bool> m_runReceiver{false};
std::atomic<bool> m_stopping{false};
bool m_audioOutStarted = false;
bool m_waitingForAcks = false;
bool m_micMuted = false;
bool m_waitingForRelayPeerInfo = false;
bool m_allowP2p = true;
bool m_dataSavingMode = false;
bool m_dataSavingRequestedByPeer = false;
bool m_receivedInit = false;
bool m_receivedInitAck = false;
bool m_isOutgoing;
bool m_useTCP = false;
bool m_useUDP = true;
bool m_didAddTcpRelays = false;
bool m_audioStarted = false;
bool m_didReceiveGroupCallKey = false;
bool m_didReceiveGroupCallKeyAck = false;
bool m_didSendGroupCallKey = false;
bool m_didSendUpgradeRequest = false;
bool m_didInvokeUpgradeCallback = false;
bool m_useMTProto2 = false;
bool m_setCurrentEndpointToTCP = false;
bool m_useIPv6 = false;
bool m_peerIPv6Available = false;
bool m_didAddIPv6Relays = false;
bool m_didSendIPv6Endpoint = false;
bool m_shittyInternetMode = false;
bool m_wasEstablished = false;
bool m_receivedFirstStreamPacket = false;
bool m_needReInitUdpProxy = true;
bool m_needRate = false;
#if defined(__APPLE__) && defined(TARGET_OS_OSX)
bool macAudioDuckingEnabled = true;
#endif
/*** debug report problems ***/
bool m_wasReconnecting = false;
bool m_wasExtraEC = false;
bool m_wasEncoderLaggy = false;
bool m_wasNetworkHandover = false;
/*** persistable state values ***/
bool m_proxySupportsUDP = true;
bool m_proxySupportsTCP = true;
};
} // namespace tgvoip
#endif // VOIPCONTROLLER_H