1
0
mirror of https://github.com/danog/libtgvoip.git synced 2024-11-26 20:24:38 +01:00
libtgvoip/VoIPController.cpp

4418 lines
150 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 _WIN32
#include <unistd.h>
#include <sys/time.h>
#endif
#include <errno.h>
#include <string.h>
#include <wchar.h>
#include "VoIPController.h"
#include "tools/logging.h"
#include "tools/threading.h"
#include "tools/Buffers.h"
#include "controller/audio/OpusEncoder.h"
#include "controller/audio/OpusDecoder.h"
#include "VoIPServerConfig.h"
#include "controller/PrivateDefines.h"
#include "controller/net/Endpoint.h"
#include "tools/json11.hpp"
#include "controller/PacketSender.h"
#include "video/VideoPacketSender.h"
#include <assert.h>
#include <time.h>
#include <math.h>
#include <exception>
#include <stdexcept>
#include <algorithm>
#include <sstream>
#include <inttypes.h>
#include <float.h>
inline int pad4(int x)
{
int r = PAD4(x);
if (r == 4)
return 0;
return r;
}
using namespace tgvoip;
using namespace std;
#ifdef __APPLE__
#include "os/darwin/AudioUnitIO.h"
#include <mach/mach_time.h>
double VoIPController::machTimebase = 0;
uint64_t VoIPController::machTimestart = 0;
#endif
#ifdef _WIN32
int64_t VoIPController::win32TimeScale = 0;
bool VoIPController::didInitWin32TimeScale = false;
#endif
#ifdef __ANDROID__
#include "os/android/JNIUtilities.h"
#include "os/android/AudioInputAndroid.h"
#include "controller/net/NetworkSocket.h"
extern jclass jniUtilitiesClass;
#endif
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
#include "audio/AudioIOCallback.h"
#endif
#define ENFORCE_MSG_THREAD assert(messageThread.IsCurrent())
extern FILE *tgvoipLogFile;
#pragma mark - Public API
VoIPController::VoIPController() : ecAudioPackets(4),
rawSendQueue(64)
{
selectCanceller = SocketSelectCanceller::Create();
udpSocket = NetworkSocket::Create(NetworkProtocol::UDP);
realUdpSocket = udpSocket;
maxAudioBitrate = ServerConfig::GetSharedInstance()->GetUInt("audio_max_bitrate", 20000);
maxAudioBitrateGPRS = ServerConfig::GetSharedInstance()->GetUInt("audio_max_bitrate_gprs", 8000);
maxAudioBitrateEDGE = ServerConfig::GetSharedInstance()->GetUInt("audio_max_bitrate_edge", 16000);
maxAudioBitrateSaving = ServerConfig::GetSharedInstance()->GetUInt("audio_max_bitrate_saving", 8000);
initAudioBitrate = ServerConfig::GetSharedInstance()->GetUInt("audio_init_bitrate", 16000);
initAudioBitrateGPRS = ServerConfig::GetSharedInstance()->GetUInt("audio_init_bitrate_gprs", 8000);
initAudioBitrateEDGE = ServerConfig::GetSharedInstance()->GetUInt("audio_init_bitrate_edge", 8000);
initAudioBitrateSaving = ServerConfig::GetSharedInstance()->GetUInt("audio_init_bitrate_saving", 8000);
audioBitrateStepIncr = ServerConfig::GetSharedInstance()->GetUInt("audio_bitrate_step_incr", 1000);
audioBitrateStepDecr = ServerConfig::GetSharedInstance()->GetUInt("audio_bitrate_step_decr", 1000);
minAudioBitrate = ServerConfig::GetSharedInstance()->GetUInt("audio_min_bitrate", 8000);
relaySwitchThreshold = ServerConfig::GetSharedInstance()->GetDouble("relay_switch_threshold", 0.8);
p2pToRelaySwitchThreshold = ServerConfig::GetSharedInstance()->GetDouble("p2p_to_relay_switch_threshold", 0.6);
relayToP2pSwitchThreshold = ServerConfig::GetSharedInstance()->GetDouble("relay_to_p2p_switch_threshold", 0.8);
reconnectingTimeout = ServerConfig::GetSharedInstance()->GetDouble("reconnecting_state_timeout", 2.0);
needRateFlags = ServerConfig::GetSharedInstance()->GetUInt("rate_flags", 0xFFFFFFFF);
rateMaxAcceptableRTT = ServerConfig::GetSharedInstance()->GetDouble("rate_min_rtt", 0.6);
rateMaxAcceptableSendLoss = ServerConfig::GetSharedInstance()->GetDouble("rate_min_send_loss", 0.2);
packetLossToEnableExtraEC = ServerConfig::GetSharedInstance()->GetDouble("packet_loss_for_extra_ec", 0.02);
maxUnsentStreamPackets = ServerConfig::GetSharedInstance()->GetUInt("max_unsent_stream_packets", 2);
unackNopThreshold = ServerConfig::GetSharedInstance()->GetUInt("unack_nop_threshold", 10);
shared_ptr<Stream> stm = make_shared<Stream>();
stm->id = 1;
stm->type = STREAM_TYPE_AUDIO;
stm->codec = CODEC_OPUS;
stm->enabled = 1;
stm->frameDuration = 60;
outgoingStreams.push_back(stm);
}
VoIPController::~VoIPController()
{
LOGD("Entered VoIPController::~VoIPController");
if (!stopping)
{
LOGE("!!!!!!!!!!!!!!!!!!!! CALL controller->Stop() BEFORE DELETING THE CONTROLLER OBJECT !!!!!!!!!!!!!!!!!!!!!!!1");
abort();
}
for (auto _stm = incomingStreams.begin(); _stm != incomingStreams.end(); ++_stm)
{
shared_ptr<Stream> stm = *_stm;
LOGD("before stop decoder");
if (stm->decoder)
{
stm->decoder->Stop();
}
}
LOGD("before delete echo canceller");
if (echoCanceller)
{
echoCanceller->Stop();
}
LOGD("Left VoIPController::~VoIPController");
if (tgvoipLogFile)
{
FILE *log = tgvoipLogFile;
tgvoipLogFile = nullptr;
fclose(log);
}
}
void VoIPController::Stop()
{
LOGD("Entered VoIPController::Stop");
stopping = true;
runReceiver = false;
LOGD("before shutdown socket");
if (udpSocket)
udpSocket->Close();
if (realUdpSocket != udpSocket)
realUdpSocket->Close();
selectCanceller->CancelSelect();
//Buffer emptyBuf(0);
//PendingOutgoingPacket emptyPacket{0, 0, 0, move(emptyBuf), 0};
//sendQueue->Put(move(emptyPacket));
rawSendQueue.Put(RawPendingOutgoingPacket{NetworkPacket::Empty(), nullptr});
LOGD("before join sendThread");
if (sendThread)
{
sendThread->Join();
}
LOGD("before join recvThread");
if (recvThread)
{
recvThread->Join();
}
LOGD("before stop messageThread");
messageThread.Stop();
{
LOGD("Before stop audio I/O");
MutexGuard m(audioIOMutex);
if (audioInput)
{
audioInput->Stop();
audioInput->SetCallback(NULL, NULL);
}
if (audioOutput)
{
audioOutput->Stop();
audioOutput->SetCallback(NULL, NULL);
}
}
LOGD("Left VoIPController::Stop [need rate = %d]", (int)needRate);
}
bool VoIPController::NeedRate()
{
return needRate && ServerConfig::GetSharedInstance()->GetBoolean("bad_call_rating", false);
}
void VoIPController::SetRemoteEndpoints(vector<Endpoint> endpoints, bool allowP2p, int32_t connectionMaxLayer)
{
LOGW("Set remote endpoints, allowP2P=%d, connectionMaxLayer=%u", allowP2p ? 1 : 0, connectionMaxLayer);
assert(!runReceiver);
preferredRelay = 0;
this->endpoints.clear();
didAddTcpRelays = false;
useTCP = true;
for (auto it = endpoints.begin(); it != endpoints.end(); ++it)
{
if (this->endpoints.find(it->id) != this->endpoints.end())
LOGE("Endpoint IDs are not unique!");
this->endpoints[it->id] = *it;
if (currentEndpoint == 0)
currentEndpoint = it->id;
if (it->type == Endpoint::Type::UDP_RELAY)
useTCP = false;
else if (it->type == Endpoint::Type::TCP_RELAY)
didAddTcpRelays = true;
LOGV("Adding endpoint: %s:%d, %s", it->address.ToString().c_str(), it->port, it->type == Endpoint::Type::UDP_RELAY ? "UDP" : "TCP");
}
preferredRelay = currentEndpoint;
this->allowP2p = allowP2p;
this->connectionMaxLayer = connectionMaxLayer;
if (connectionMaxLayer >= 74)
{
useMTProto2 = true;
}
AddIPv6Relays();
}
void VoIPController::Start()
{
LOGW("Starting voip controller");
udpSocket->Open();
if (udpSocket->IsFailed())
{
SetState(STATE_FAILED);
return;
}
runReceiver = true;
recvThread.reset(new Thread(bind(&VoIPController::RunRecvThread, this)));
recvThread->SetName("VoipRecv");
recvThread->Start();
messageThread.Start();
}
void VoIPController::Connect()
{
assert(state != STATE_WAIT_INIT_ACK);
connectionInitTime = GetCurrentTime();
if (config.initTimeout == 0.0)
{
LOGE("Init timeout is 0 -- did you forget to set config?");
config.initTimeout = 30.0;
}
//InitializeTimers();
//SendInit();
sendThread.reset(new Thread(bind(&VoIPController::RunSendThread, this)));
sendThread->SetName("VoipSend");
sendThread->Start();
}
void VoIPController::SetEncryptionKey(std::vector<uint8_t> key, bool isOutgoing)
{
memcpy(encryptionKey, key.data(), 256);
uint8_t sha1[SHA1_LENGTH];
crypto.sha1((uint8_t *)encryptionKey, 256, sha1);
memcpy(keyFingerprint, sha1 + (SHA1_LENGTH - 8), 8);
uint8_t sha256[SHA256_LENGTH];
crypto.sha256((uint8_t *)encryptionKey, 256, sha256);
memcpy(callID, sha256 + (SHA256_LENGTH - 16), 16);
this->isOutgoing = isOutgoing;
}
void VoIPController::SetNetworkType(int type)
{
networkType = type;
UpdateDataSavingState();
UpdateAudioBitrateLimit();
myIPv6 = NetworkAddress::Empty();
string itfName = udpSocket->GetLocalInterfaceInfo(NULL, &myIPv6);
LOGI("set network type: %s, active interface %s", NetworkTypeToString(type).c_str(), itfName.c_str());
LOGI("Local IPv6 address: %s", myIPv6.ToString().c_str());
if (IS_MOBILE_NETWORK(networkType))
{
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());
}
}
if (itfName != activeNetItfName)
{
udpSocket->OnActiveInterfaceChanged();
LOGI("Active network interface changed: %s -> %s", activeNetItfName.c_str(), itfName.c_str());
bool isFirstChange = activeNetItfName.length() == 0 && state != STATE_ESTABLISHED && state != STATE_RECONNECTING;
activeNetItfName = itfName;
if (isFirstChange)
return;
messageThread.Post([this] {
wasNetworkHandover = true;
if (currentEndpoint)
{
const Endpoint &_currentEndpoint = endpoints.at(currentEndpoint);
const Endpoint &_preferredRelay = endpoints.at(preferredRelay);
if (_currentEndpoint.type != Endpoint::Type::UDP_RELAY)
{
if (_preferredRelay.type == Endpoint::Type::UDP_RELAY)
currentEndpoint = preferredRelay;
MutexGuard m(endpointsMutex);
constexpr int64_t lanID = static_cast<int64_t>(FOURCC('L', 'A', 'N', '4')) << 32;
endpoints.erase(lanID);
for (pair<const int64_t, Endpoint> &e : endpoints)
{
Endpoint &endpoint = e.second;
if (endpoint.type == Endpoint::Type::UDP_RELAY && useTCP)
{
useTCP = false;
if (_preferredRelay.type == Endpoint::Type::TCP_RELAY)
{
preferredRelay = currentEndpoint = endpoint.id;
}
}
else if (endpoint.type == Endpoint::Type::TCP_RELAY && endpoint.socket)
{
endpoint.socket->Close();
}
endpoint.averageRTT = 0;
endpoint.rtts.Reset();
}
}
}
lastUdpPingTime = 0;
if (proxyProtocol == PROXY_SOCKS5)
InitUDPProxy();
if (allowP2p && currentEndpoint)
{
SendPublicEndpointsRequest();
}
BufferOutputStream s(4);
s.WriteInt32(dataSavingMode ? INIT_FLAG_DATA_SAVING_ENABLED : 0);
if (peerVersion < 6)
{
SendPacketReliably(PKT_NETWORK_CHANGED, s.GetBuffer(), s.GetLength(), 1, 20);
}
else
{
Buffer buf(move(s));
SendExtra(buf, EXTRA_TYPE_NETWORK_CHANGED);
}
needReInitUdpProxy = true;
selectCanceller->CancelSelect();
didSendIPv6Endpoint = false;
AddIPv6Relays();
ResetUdpAvailability();
ResetEndpointPingStats();
});
}
}
double VoIPController::GetAverageRTT()
{
ENFORCE_MSG_THREAD;
if (lastSentSeq >= lastRemoteAckSeq)
{
uint32_t diff = lastSentSeq - lastRemoteAckSeq;
//LOGV("rtt diff=%u", diff);
if (diff < 32)
{
double res = 0;
int count = 0;
for (const auto &packet : recentOutgoingPackets)
{
if (packet.ackTime > 0)
{
res += (packet.ackTime - packet.sendTime);
count++;
}
}
if (count > 0)
res /= count;
return res;
}
}
return 999;
}
void VoIPController::SetMicMute(bool mute)
{
if (micMuted == mute)
return;
micMuted = mute;
if (audioInput)
{
if (mute)
audioInput->Stop();
else
audioInput->Start();
if (!audioInput->IsInitialized())
{
lastError = ERROR_AUDIO_IO;
SetState(STATE_FAILED);
return;
}
}
if (echoCanceller)
echoCanceller->Enable(!mute);
if (state == STATE_ESTABLISHED)
{
messageThread.Post([this] {
for (shared_ptr<Stream> &s : outgoingStreams)
{
if (s->type == STREAM_TYPE_AUDIO)
{
s->enabled = !micMuted;
if (peerVersion < 6)
{
unsigned char buf[2];
buf[0] = s->id;
buf[1] = (char)(micMuted ? 0 : 1);
SendPacketReliably(PKT_STREAM_STATE, buf, 2, .5f, 20);
}
else
{
SendStreamFlags(*s);
}
}
}
});
}
}
string VoIPController::GetDebugString()
{
string r = "Remote endpoints: \n";
char buffer[2048];
MutexGuard m(endpointsMutex);
for (pair<const int64_t, Endpoint> &_e : endpoints)
{
Endpoint &endpoint = _e.second;
const char *type;
switch (endpoint.type)
{
case Endpoint::Type::UDP_P2P_INET:
type = "UDP_P2P_INET";
break;
case Endpoint::Type::UDP_P2P_LAN:
type = "UDP_P2P_LAN";
break;
case Endpoint::Type::UDP_RELAY:
type = "UDP_RELAY";
break;
case Endpoint::Type::TCP_RELAY:
type = "TCP_RELAY";
break;
default:
type = "UNKNOWN";
break;
}
snprintf(buffer, sizeof(buffer), "%s:%u %dms %d 0x%" PRIx64 " [%s%s]\n", endpoint.address.IsEmpty() ? ("[" + endpoint.v6address.ToString() + "]").c_str() : endpoint.address.ToString().c_str(), endpoint.port, (int)(endpoint.averageRTT * 1000), endpoint.udpPongCount, (uint64_t)endpoint.id, type, currentEndpoint == endpoint.id ? ", IN_USE" : "");
r += buffer;
}
if (shittyInternetMode)
{
snprintf(buffer, sizeof(buffer), "ShittyInternetMode: level %u\n", extraEcLevel);
r += buffer;
}
double avgLate[3];
shared_ptr<Stream> stm = GetStreamByType(STREAM_TYPE_AUDIO, false);
shared_ptr<JitterBuffer> jitterBuffer;
if (stm)
jitterBuffer = stm->jitterBuffer;
if (jitterBuffer)
jitterBuffer->GetAverageLateCount(avgLate);
else
memset(avgLate, 0, 3 * sizeof(double));
snprintf(buffer, sizeof(buffer),
"Jitter buffer: %d/%.2f | %.1f, %.1f, %.1f\n"
"RTT avg/min: %d/%d\n"
"Congestion window: %d/%d bytes\n"
"Key fingerprint: %02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX%s\n"
"Last sent/ack'd seq: %u/%u\n"
"Last recvd seq: %u\n"
"Send/recv losses: %u/%u (%d%%)\n"
"Audio bitrate: %d kbit\n"
"Outgoing queue: %u\n"
// "Packet grouping: %d\n"
"Frame size out/in: %d/%d\n"
"Bytes sent/recvd: %llu/%llu",
jitterBuffer ? jitterBuffer->GetMinPacketCount() : 0, jitterBuffer ? jitterBuffer->GetAverageDelay() : 0, avgLate[0], avgLate[1], avgLate[2],
// (int)(GetAverageRTT()*1000), 0,
(int)(conctl.GetAverageRTT() * 1000), (int)(conctl.GetMinimumRTT() * 1000),
int(conctl.GetInflightDataSize()), int(conctl.GetCongestionWindow()),
keyFingerprint[0], keyFingerprint[1], keyFingerprint[2], keyFingerprint[3], keyFingerprint[4], keyFingerprint[5], keyFingerprint[6], keyFingerprint[7],
useMTProto2 ? " (MTProto2.0)" : "",
lastSentSeq, lastRemoteAckSeq, lastRemoteSeq,
sendLosses, recvLossCount, encoder ? encoder->GetPacketLoss() : 0,
encoder ? (encoder->GetBitrate() / 1000) : 0,
static_cast<unsigned int>(unsentStreamPackets),
// audioPacketGrouping,
outgoingStreams[0]->frameDuration, incomingStreams.size() > 0 ? incomingStreams[0]->frameDuration : 0,
(long long unsigned int)(stats.bytesSentMobile + stats.bytesSentWifi),
(long long unsigned int)(stats.bytesRecvdMobile + stats.bytesRecvdWifi));
r += buffer;
if (config.enableVideoSend)
{
shared_ptr<Stream> vstm = GetStreamByType(STREAM_TYPE_VIDEO, true);
if (vstm && vstm->enabled && videoPacketSender)
{
snprintf(buffer, sizeof(buffer), "\nVideo out: %ux%u '%c%c%c%c' %u kbit", vstm->width, vstm->height, PRINT_FOURCC(vstm->codec), videoPacketSender->GetBitrate());
r += buffer;
}
}
if (!peerVideoDecoders.empty())
{
r += "\nPeer codecs: ";
for (uint32_t codec : peerVideoDecoders)
{
snprintf(buffer, sizeof(buffer), "'%c%c%c%c' ", PRINT_FOURCC(codec));
r += buffer;
}
}
if (config.enableVideoReceive)
{
shared_ptr<Stream> vstm = GetStreamByType(STREAM_TYPE_VIDEO, false);
if (vstm && vstm->enabled)
{
snprintf(buffer, sizeof(buffer), "\nVideo in: %ux%u '%c%c%c%c'", vstm->width, vstm->height, PRINT_FOURCC(vstm->codec));
r += buffer;
}
}
return r;
}
const char *VoIPController::GetVersion()
{
return LIBTGVOIP_VERSION;
}
int64_t VoIPController::GetPreferredRelayID()
{
return preferredRelay;
}
int VoIPController::GetLastError()
{
return lastError;
}
void VoIPController::GetStats(TrafficStats *stats)
{
memcpy(stats, &this->stats, sizeof(TrafficStats));
}
string VoIPController::GetDebugLog()
{
map<string, json11::Json> 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;
}
}
else if (networkType == NET_TYPE_WIFI)
{
#ifdef __ANDROID__
jni::DoWithJNI([&](JNIEnv *env) {
jmethodID getWifiInfoMethod = env->GetStaticMethodID(jniUtilitiesClass, "getWifiInfo", "()[I");
jintArray res = static_cast<jintArray>(env->CallStaticObjectMethod(jniUtilitiesClass, getWifiInfoMethod));
if (res)
{
jint *wifiInfo = env->GetIntArrayElements(res, NULL);
network["rssi"] = wifiInfo[0];
network["link_speed"] = wifiInfo[1];
env->ReleaseIntArrayElements(res, wifiInfo, JNI_ABORT);
}
});
#endif
}
/*vector<json11::Json> lpkts;
for(DebugLoggedPacket& lpkt:debugLoggedPackets){
lpkts.push_back(json11::Json::array{lpkt.timestamp, lpkt.seq, lpkt.length});
}
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<json11::Json> _endpoints;
for (pair<const int64_t, Endpoint> &_e : endpoints)
{
Endpoint &e = _e.second;
string type;
map<string, json11::Json> je{
{"rtt", (int)(e.averageRTT * 1000.0)}};
int64_t id = 0;
if (e.type == Endpoint::Type::UDP_RELAY)
{
je["type"] = e.IsIPv6Only() ? "udp_relay6" : "udp_relay";
id = e.CleanID();
if (e.totalUdpPings == 0)
je["udp_pings"] = 0.0;
else
je["udp_pings"] = (double)e.totalUdpPingReplies / (double)e.totalUdpPings;
je["self_rtt"] = (int)(e.selfRtts.Average() * 1000.0);
}
else if (e.type == Endpoint::Type::TCP_RELAY)
{
je["type"] = e.IsIPv6Only() ? "tcp_relay6" : "tcp_relay";
id = e.CleanID();
}
else if (e.type == Endpoint::Type::UDP_P2P_INET)
{
je["type"] = e.IsIPv6Only() ? "p2p_inet6" : "p2p_inet";
}
else if (e.type == Endpoint::Type::UDP_P2P_LAN)
{
je["type"] = "p2p_lan";
}
if (preferredRelay == e.id && wasEstablished)
je["pref"] = true;
if (id)
{
ostringstream s;
s << id;
je["id"] = s.str();
}
_endpoints.push_back(je);
}
string p2pType = "none";
Endpoint &cur = endpoints[currentEndpoint];
if (cur.type == Endpoint::Type::UDP_P2P_INET)
p2pType = cur.IsIPv6Only() ? "inet6" : "inet";
else if (cur.type == Endpoint::Type::UDP_P2P_LAN)
p2pType = "lan";
vector<string> problems;
if (lastError == ERROR_TIMEOUT)
problems.push_back("timeout");
if (wasReconnecting)
problems.push_back("reconnecting");
if (wasExtraEC)
problems.push_back("extra_ec");
if (wasEncoderLaggy)
problems.push_back("encoder_lag");
if (!wasEstablished)
problems.push_back("not_inited");
if (wasNetworkHandover)
problems.push_back("network_handover");
return json11::Json(json11::Json::object{
{"log_type", "call_stats"},
{"libtgvoip_version", LIBTGVOIP_VERSION},
{"network", network},
{"protocol_version", std::min(peerVersion, PROTOCOL_VERSION)},
{"udp_avail", udpConnectivityState == UDP_AVAILABLE},
{"tcp_used", useTCP},
{"p2p_type", p2pType},
{"packet_stats", json11::Json::object{
{"out", (int)seq},
{"in", (int)packetsReceived},
{"lost_out", (int)conctl.GetSendLossCount()},
{"lost_in", (int)recvLossCount}}},
{"endpoints", _endpoints},
{"problems", problems}})
.dump();
}
vector<AudioInputDevice> VoIPController::EnumerateAudioInputs()
{
vector<AudioInputDevice> devs;
audio::AudioInput::EnumerateDevices(devs);
return devs;
}
vector<AudioOutputDevice> VoIPController::EnumerateAudioOutputs()
{
vector<AudioOutputDevice> devs;
audio::AudioOutput::EnumerateDevices(devs);
return devs;
}
void VoIPController::SetCurrentAudioInput(string id)
{
currentAudioInput = id;
if (audioInput)
audioInput->SetCurrentDevice(id);
}
void VoIPController::SetCurrentAudioOutput(string id)
{
currentAudioOutput = id;
if (audioOutput)
audioOutput->SetCurrentDevice(id);
}
string VoIPController::GetCurrentAudioInputID()
{
return currentAudioInput;
}
string VoIPController::GetCurrentAudioOutputID()
{
return currentAudioOutput;
}
void VoIPController::SetProxy(int protocol, string address, uint16_t port, string username, string password)
{
proxyProtocol = protocol;
proxyAddress = std::move(address);
proxyPort = port;
proxyUsername = std::move(username);
proxyPassword = std::move(password);
}
int VoIPController::GetSignalBarsCount()
{
return signalBarsHistory.NonZeroAverage();
}
void VoIPController::SetCallbacks(VoIPController::Callbacks callbacks)
{
this->callbacks = callbacks;
if (callbacks.connectionStateChanged)
callbacks.connectionStateChanged(this, state);
}
void VoIPController::SetAudioOutputGainControlEnabled(bool enabled)
{
LOGD("New output AGC state: %d", enabled);
}
uint32_t VoIPController::GetPeerCapabilities()
{
return peerCapabilities;
}
void VoIPController::SendGroupCallKey(unsigned char *key)
{
Buffer buf(256);
buf.CopyFrom(key, 0, 256);
shared_ptr<Buffer> keyPtr = make_shared<Buffer>(move(buf));
messageThread.Post([this, keyPtr] {
if (!(peerCapabilities & TGVOIP_PEER_CAP_GROUP_CALLS))
{
LOGE("Tried to send group call key but peer isn't capable of them");
return;
}
if (didSendGroupCallKey)
{
LOGE("Tried to send a group call key repeatedly");
return;
}
if (!isOutgoing)
{
LOGE("You aren't supposed to send group call key in an incoming call, use VoIPController::RequestCallUpgrade() instead");
return;
}
didSendGroupCallKey = true;
SendExtra(*keyPtr, EXTRA_TYPE_GROUP_CALL_KEY);
});
}
void VoIPController::RequestCallUpgrade()
{
messageThread.Post([this] {
if (!(peerCapabilities & TGVOIP_PEER_CAP_GROUP_CALLS))
{
LOGE("Tried to send group call key but peer isn't capable of them");
return;
}
if (didSendUpgradeRequest)
{
LOGE("Tried to send upgrade request repeatedly");
return;
}
if (isOutgoing)
{
LOGE("You aren't supposed to send an upgrade request in an outgoing call, generate an encryption key and use VoIPController::SendGroupCallKey instead");
return;
}
didSendUpgradeRequest = true;
Buffer empty(0);
SendExtra(empty, EXTRA_TYPE_REQUEST_GROUP);
});
}
void VoIPController::SetEchoCancellationStrength(int strength)
{
echoCancellationStrength = strength;
if (echoCanceller)
echoCanceller->SetAECStrength(strength);
}
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
void VoIPController::SetAudioDataCallbacks(std::function<void(int16_t *, size_t)> input, std::function<void(int16_t *, size_t)> output, std::function<void(int16_t *, size_t)> preproc = nullptr)
{
audioInputDataCallback = input;
audioOutputDataCallback = output;
audioPreprocDataCallback = preproc;
}
#endif
int VoIPController::GetConnectionState()
{
return state;
}
void VoIPController::SetConfig(const Config &cfg)
{
config = cfg;
if (tgvoipLogFile)
{
fclose(tgvoipLogFile);
tgvoipLogFile = nullptr;
}
if (!config.logFilePath.empty())
{
#ifndef _WIN32
tgvoipLogFile = fopen(config.logFilePath.c_str(), "a");
#else
if (_wfopen_s(&tgvoipLogFile, config.logFilePath.c_str(), L"a") != 0)
{
tgvoipLogFile = nullptr;
}
#endif
tgvoip_log_file_write_header(tgvoipLogFile);
}
else
{
tgvoipLogFile = nullptr;
}
if (!config.statsDumpFilePath.empty())
{
statsDump.open(config.statsDumpFilePath);
if (statsDump)
statsDump << "Time\tRTT\tLRSeq\tLSSeq\tLASeq\tLostR\tLostS\tCWnd\tBitrate\tLoss%%\tJitter\tJDelay\tAJDelay\n";
}
else
{
statsDump.close();
}
UpdateDataSavingState();
UpdateAudioBitrateLimit();
}
void VoIPController::SetPersistentState(vector<uint8_t> state)
{
using namespace json11;
if (state.empty())
return;
string jsonErr;
string json = string(state.begin(), state.end());
Json _obj = Json::parse(json, jsonErr);
if (!jsonErr.empty())
{
LOGE("Error parsing persistable state: %s", jsonErr.c_str());
return;
}
Json::object obj = _obj.object_items();
if (obj.find("proxy") != obj.end())
{
Json::object proxy = obj["proxy"].object_items();
lastTestedProxyServer = proxy["server"].string_value();
proxySupportsUDP = proxy["udp"].bool_value();
proxySupportsTCP = proxy["tcp"].bool_value();
}
}
vector<uint8_t> VoIPController::GetPersistentState()
{
using namespace json11;
Json::object obj = Json::object{
{"ver", 1},
};
if (proxyProtocol == PROXY_SOCKS5)
{
char pbuf[128];
snprintf(pbuf, sizeof(pbuf), "%s:%u", proxyAddress.c_str(), proxyPort);
obj.insert({"proxy", Json::object{
{"server", string(pbuf)},
{"udp", proxySupportsUDP},
{"tcp", proxySupportsTCP}}});
}
string _jstr = Json(obj).dump();
const char *jstr = _jstr.c_str();
return vector<uint8_t>(jstr, jstr + strlen(jstr));
}
void VoIPController::SetOutputVolume(float level)
{
outputVolume->SetLevel(level);
}
void VoIPController::SetInputVolume(float level)
{
inputVolume->SetLevel(level);
}
#if defined(__APPLE__) && TARGET_OS_OSX
void VoIPController::SetAudioOutputDuckingEnabled(bool enabled)
{
macAudioDuckingEnabled = enabled;
audio::AudioUnitIO *osxAudio = dynamic_cast<audio::AudioUnitIO *>(audioIO.get());
if (osxAudio)
{
osxAudio->SetDuckingEnabled(enabled);
}
}
#endif
#pragma mark - Internal intialization
void VoIPController::InitializeTimers()
{
initTimeoutID = messageThread.Post(
[this] {
LOGW("Init timeout, disconnecting");
lastError = ERROR_TIMEOUT;
SetState(STATE_FAILED);
},
config.initTimeout);
if (!config.statsDumpFilePath.empty())
{
messageThread.Post(
[this] {
if (statsDump && incomingStreams.size() == 1)
{
shared_ptr<JitterBuffer> &jitterBuffer = incomingStreams[0]->jitterBuffer;
statsDump << std::setprecision(3)
<< GetCurrentTime() - connectionInitTime
<< endpoints.at(currentEndpoint).rtts[0]
<< lastRemoteSeq
<< (uint32_t)seq
<< lastRemoteAckSeq
<< recvLossCount
<< conctl.GetSendLossCount()
<< (int)conctl.GetInflightDataSize()
<< (encoder ? encoder->GetBitrate() : 0)
<< (encoder ? encoder->GetPacketLoss() : 0)
<< (jitterBuffer ? jitterBuffer->GetLastMeasuredJitter() : 0)
<< (jitterBuffer ? jitterBuffer->GetLastMeasuredDelay() * 0.06 : 0)
<< (jitterBuffer ? jitterBuffer->GetAverageDelay() * 0.06 : 0);
}
},
0.1, 0.1);
}
messageThread.Post(std::bind(&VoIPController::SendRelayPings, this), 0.0, 2.0);
}
void VoIPController::RunSendThread()
{
InitializeAudio();
InitializeTimers();
messageThread.Post(bind(&VoIPController::SendInit, this));
while (true)
{
RawPendingOutgoingPacket pkt = rawSendQueue.GetBlocking();
if (pkt.packet.IsEmpty())
break;
if (IS_MOBILE_NETWORK(networkType))
stats.bytesSentMobile += static_cast<uint64_t>(pkt.packet.data.Length());
else
stats.bytesSentWifi += static_cast<uint64_t>(pkt.packet.data.Length());
if (pkt.packet.protocol == NetworkProtocol::TCP)
{
if (pkt.socket && !pkt.socket->IsFailed())
{
pkt.socket->Send(std::move(pkt.packet));
}
}
else
{
udpSocket->Send(std::move(pkt.packet));
}
}
LOGI("=== send thread exiting ===");
}
#pragma mark - Miscellaneous
void VoIPController::SetState(int state)
{
this->state = state;
LOGV("Call state changed to %d", state);
stateChangeTime = GetCurrentTime();
messageThread.Post([this, state] {
if (callbacks.connectionStateChanged)
callbacks.connectionStateChanged(this, state);
});
if (state == STATE_ESTABLISHED)
{
SetMicMute(micMuted);
if (!wasEstablished)
{
wasEstablished = true;
messageThread.Post(std::bind(&VoIPController::UpdateRTT, this), 0.1, 0.5);
messageThread.Post(std::bind(&VoIPController::UpdateAudioBitrate, this), 0.0, 0.3);
messageThread.Post(std::bind(&VoIPController::UpdateCongestion, this), 0.0, 1.0);
messageThread.Post(std::bind(&VoIPController::UpdateSignalBars, this), 1.0, 1.0);
messageThread.Post(std::bind(&VoIPController::TickJitterBufferAndCongestionControl, this), 0.0, 0.1);
}
}
}
void VoIPController::SendStreamFlags(Stream &stream)
{
ENFORCE_MSG_THREAD;
BufferOutputStream s(5);
s.WriteByte(stream.id);
uint32_t flags = 0;
if (stream.enabled)
flags |= STREAM_FLAG_ENABLED;
if (stream.extraECEnabled)
flags |= STREAM_FLAG_EXTRA_EC;
if (stream.paused)
flags |= STREAM_FLAG_PAUSED;
s.WriteInt32(flags);
LOGV("My stream state: id %u flags %u", (unsigned int)stream.id, (unsigned int)flags);
Buffer buf(move(s));
SendExtra(buf, EXTRA_TYPE_STREAM_FLAGS);
}
shared_ptr<VoIPController::Stream> VoIPController::GetStreamByType(int type, bool outgoing)
{
shared_ptr<Stream> s;
for (shared_ptr<Stream> &ss : (outgoing ? outgoingStreams : incomingStreams))
{
if (ss->type == type)
return ss;
}
return s;
}
shared_ptr<VoIPController::Stream> VoIPController::GetStreamByID(unsigned char id, bool outgoing)
{
shared_ptr<Stream> s;
for (shared_ptr<Stream> &ss : (outgoing ? outgoingStreams : incomingStreams))
{
if (ss->id == id)
return ss;
}
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::HandleAudioInput(unsigned char *data, size_t len, unsigned char *secondaryData, size_t secondaryLen)
{
if (stopping)
return;
// TODO make an AudioPacketSender
Buffer dataBuf = outgoingAudioBufferPool.Get();
Buffer secondaryDataBuf = secondaryLen && secondaryData ? outgoingAudioBufferPool.Get() : Buffer();
dataBuf.CopyFrom(data, 0, len);
if (secondaryLen && secondaryData)
{
secondaryDataBuf.CopyFrom(secondaryData, 0, secondaryLen);
}
shared_ptr<Buffer> dataBufPtr = make_shared<Buffer>(move(dataBuf));
shared_ptr<Buffer> secondaryDataBufPtr = make_shared<Buffer>(move(secondaryDataBuf));
messageThread.Post([this, dataBufPtr, secondaryDataBufPtr, len, secondaryLen]() {
unsentStreamPacketsHistory.Add(static_cast<unsigned int>(unsentStreamPackets));
if (unsentStreamPacketsHistory.Average() >= maxUnsentStreamPackets && !videoPacketSender)
{
LOGW("Resetting stalled send queue");
sendQueue.clear();
unsentStreamPacketsHistory.Reset();
unsentStreamPackets = 0;
}
if (waitingForAcks || dontSendPackets > 0 || ((unsigned int)unsentStreamPackets >= maxUnsentStreamPackets /*&& endpoints[currentEndpoint].type==Endpoint::Type::TCP_RELAY*/))
{
LOGV("waiting for queue, dropping outgoing audio packet, %d %d %d [%d]", (unsigned int)unsentStreamPackets, waitingForAcks, dontSendPackets, maxUnsentStreamPackets);
return;
}
//LOGV("Audio packet size %u", (unsigned int)len);
if (!receivedInitAck)
return;
BufferOutputStream pkt(1500);
bool hasExtraFEC = peerVersion >= 7 && secondaryLen && shittyInternetMode;
unsigned char flags = (unsigned char)(len > 255 || hasExtraFEC ? STREAM_DATA_FLAG_LEN16 : 0);
pkt.WriteByte((unsigned char)(1 | flags)); // streamID + flags
if (len > 255 || hasExtraFEC)
{
int16_t lenAndFlags = static_cast<int16_t>(len);
if (hasExtraFEC)
lenAndFlags |= STREAM_DATA_XFLAG_EXTRA_FEC;
pkt.WriteInt16(lenAndFlags);
}
else
{
pkt.WriteByte((unsigned char)len);
}
pkt.WriteInt32(audioTimestampOut);
pkt.WriteBytes(*dataBufPtr, 0, len);
if (hasExtraFEC)
{
Buffer ecBuf(secondaryLen);
ecBuf.CopyFrom(*secondaryDataBufPtr, 0, secondaryLen);
if (ecAudioPackets.size() == 4)
{
ecAudioPackets.pop_front();
}
ecAudioPackets.push_back(move(ecBuf));
uint8_t fecCount = std::min(static_cast<uint8_t>(ecAudioPackets.size()), extraEcLevel);
pkt.WriteByte(fecCount);
for (auto ecData = ecAudioPackets.end() - fecCount; ecData != ecAudioPackets.end(); ++ecData)
{
pkt.WriteByte((unsigned char)ecData->Length());
pkt.WriteBytes(*ecData);
}
}
unsentStreamPackets++;
PendingOutgoingPacket p{
/*.seq=*/GenerateOutSeq(),
/*.type=*/PKT_STREAM_DATA,
/*.len=*/pkt.GetLength(),
/*.data=*/Buffer(move(pkt)),
/*.endpoint=*/0,
};
conctl.PacketSent(p.seq, p.len);
SendOrEnqueuePacket(move(p));
if (peerVersion < 7 && secondaryLen && shittyInternetMode)
{
Buffer ecBuf(secondaryLen);
ecBuf.CopyFrom(*secondaryDataBufPtr, 0, secondaryLen);
if (ecAudioPackets.size() == 4)
{
ecAudioPackets.pop_front();
}
ecAudioPackets.push_back(move(ecBuf));
pkt = BufferOutputStream(1500);
pkt.WriteByte(outgoingStreams[0]->id);
pkt.WriteInt32(audioTimestampOut);
uint8_t fecCount = std::min(static_cast<uint8_t>(ecAudioPackets.size()), extraEcLevel);
pkt.WriteByte(fecCount);
for (auto ecData = ecAudioPackets.end() - fecCount; ecData != ecAudioPackets.end(); ++ecData)
{
pkt.WriteByte((unsigned char)ecData->Length());
pkt.WriteBytes(*ecData);
}
PendingOutgoingPacket p{
GenerateOutSeq(),
PKT_STREAM_EC,
pkt.GetLength(),
Buffer(move(pkt)),
0};
SendOrEnqueuePacket(move(p));
}
audioTimestampOut += outgoingStreams[0]->frameDuration;
});
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
if (audioPreprocDataCallback)
{
int size = opus_decode(preprocDecoder.get(), data, len, preprocBuffer, 4096, 0);
audioPreprocDataCallback(preprocBuffer, size);
}
#endif
}
void VoIPController::InitializeAudio()
{
double t = GetCurrentTime();
shared_ptr<Stream> outgoingAudioStream = GetStreamByType(STREAM_TYPE_AUDIO, true);
LOGI("before create audio io");
audioIO = audio::AudioIO::Create(currentAudioInput, currentAudioOutput);
audioInput = audioIO->GetInput();
audioOutput = audioIO->GetOutput();
#ifdef __ANDROID__
audio::AudioInputAndroid *androidInput = dynamic_cast<audio::AudioInputAndroid *>(audioInput.get());
if (androidInput)
{
unsigned int effects = androidInput->GetEnabledEffects();
if (!(effects & audio::AudioInputAndroid::EFFECT_AEC))
{
config.enableAEC = true;
LOGI("Forcing software AEC because built-in is not good");
}
if (!(effects & audio::AudioInputAndroid::EFFECT_NS))
{
config.enableNS = true;
LOGI("Forcing software NS because built-in is not good");
}
}
#elif defined(__APPLE__) && TARGET_OS_OSX
SetAudioOutputDuckingEnabled(macAudioDuckingEnabled);
#endif
LOGI("AEC: %d NS: %d AGC: %d", config.enableAEC, config.enableNS, config.enableAGC);
echoCanceller.reset(new EchoCanceller(config.enableAEC, config.enableNS, config.enableAGC));
encoder.reset(new OpusEncoder(audioInput, true));
encoder->SetCallback(bind(&VoIPController::HandleAudioInput, this, placeholders::_1, placeholders::_2, placeholders::_3, placeholders::_4));
encoder->SetOutputFrameDuration(outgoingAudioStream->frameDuration);
encoder->SetEchoCanceller(echoCanceller);
encoder->SetSecondaryEncoderEnabled(false);
if (config.enableVolumeControl)
{
encoder->AddAudioEffect(inputVolume);
}
#if defined(TGVOIP_USE_CALLBACK_AUDIO_IO)
dynamic_cast<audio::AudioInputCallback *>(audioInput.get())->SetDataCallback(audioInputDataCallback);
dynamic_cast<audio::AudioOutputCallback *>(audioOutput.get())->SetDataCallback(audioOutputDataCallback);
#endif
if (!audioOutput->IsInitialized())
{
LOGE("Error initializing audio playback");
lastError = ERROR_AUDIO_IO;
SetState(STATE_FAILED);
return;
}
UpdateAudioBitrateLimit();
LOGI("Audio initialization took %f seconds", GetCurrentTime() - t);
}
void VoIPController::StartAudio()
{
OnAudioOutputReady();
encoder->Start();
if (!micMuted)
{
audioInput->Start();
if (!audioInput->IsInitialized())
{
LOGE("Error initializing audio capture");
lastError = ERROR_AUDIO_IO;
SetState(STATE_FAILED);
return;
}
}
}
void VoIPController::OnAudioOutputReady()
{
LOGI("Audio I/O ready");
auto &stm = incomingStreams[0];
stm->decoder = make_shared<OpusDecoder>(audioOutput, true, peerVersion >= 6);
stm->decoder->SetEchoCanceller(echoCanceller);
if (config.enableVolumeControl)
{
stm->decoder->AddAudioEffect(outputVolume);
}
stm->decoder->SetJitterBuffer(stm->jitterBuffer);
stm->decoder->SetFrameDuration(stm->frameDuration);
stm->decoder->Start();
}
void VoIPController::UpdateAudioOutputState()
{
bool areAnyAudioStreamsEnabled = false;
for (auto s = incomingStreams.begin(); s != incomingStreams.end(); ++s)
{
if ((*s)->type == STREAM_TYPE_AUDIO && (*s)->enabled)
areAnyAudioStreamsEnabled = true;
}
if (audioOutput)
{
LOGV("New audio output state: %d", areAnyAudioStreamsEnabled);
if (audioOutput->IsPlaying() != areAnyAudioStreamsEnabled)
{
if (areAnyAudioStreamsEnabled)
audioOutput->Start();
else
audioOutput->Stop();
}
}
}
#pragma mark - Bandwidth management
void VoIPController::UpdateAudioBitrateLimit()
{
if (encoder)
{
if (dataSavingMode || dataSavingRequestedByPeer)
{
maxBitrate = maxAudioBitrateSaving;
encoder->SetBitrate(initAudioBitrateSaving);
}
else if (networkType == NET_TYPE_GPRS)
{
maxBitrate = maxAudioBitrateGPRS;
encoder->SetBitrate(initAudioBitrateGPRS);
}
else if (networkType == NET_TYPE_EDGE)
{
maxBitrate = maxAudioBitrateEDGE;
encoder->SetBitrate(initAudioBitrateEDGE);
}
else
{
maxBitrate = maxAudioBitrate;
encoder->SetBitrate(initAudioBitrate);
}
encoder->SetVadMode(dataSavingMode || dataSavingRequestedByPeer);
if (echoCanceller)
echoCanceller->SetVoiceDetectionEnabled(dataSavingMode || dataSavingRequestedByPeer);
}
}
void VoIPController::UpdateDataSavingState()
{
if (config.dataSaving == DATA_SAVING_ALWAYS)
{
dataSavingMode = true;
}
else if (config.dataSaving == DATA_SAVING_MOBILE)
{
dataSavingMode = networkType == NET_TYPE_GPRS || networkType == NET_TYPE_EDGE ||
networkType == NET_TYPE_3G || networkType == NET_TYPE_HSPA || networkType == NET_TYPE_LTE || networkType == NET_TYPE_OTHER_MOBILE;
}
else
{
dataSavingMode = false;
}
LOGI("update data saving mode, config %d, enabled %d, reqd by peer %d", config.dataSaving, dataSavingMode, dataSavingRequestedByPeer);
}
#pragma mark - Networking & crypto
void VoIPController::WritePacketHeader(uint32_t pseq, BufferOutputStream *s, unsigned char type, uint32_t length, PacketSender *source)
{
uint32_t acks = 0;
uint32_t distance;
for (const uint32_t &seq : recentIncomingSeqs)
{
distance = lastRemoteSeq - seq;
if (distance > 0 && distance <= 32)
{
acks |= (1 << (32 - distance));
}
}
if (peerVersion >= 8 || (!peerVersion && connectionMaxLayer >= 92))
{
s->WriteByte(type);
s->WriteInt32(lastRemoteSeq);
s->WriteInt32(pseq);
s->WriteInt32(acks);
unsigned char flags;
if (currentExtras.empty())
{
flags = 0;
}
else
{
flags = XPFLAG_HAS_EXTRA;
}
shared_ptr<Stream> videoStream = GetStreamByType(STREAM_TYPE_VIDEO, false);
if (peerVersion >= 9 && videoStream && videoStream->enabled)
flags |= XPFLAG_HAS_RECV_TS;
s->WriteByte(flags);
if (!currentExtras.empty())
{
s->WriteByte(static_cast<unsigned char>(currentExtras.size()));
for (vector<UnacknowledgedExtraData>::iterator x = currentExtras.begin(); x != currentExtras.end(); ++x)
{
LOGV("Writing extra into header: type %u, length %d", x->type, int(x->data.Length()));
assert(x->data.Length() <= 254);
s->WriteByte(static_cast<unsigned char>(x->data.Length() + 1));
s->WriteByte(x->type);
s->WriteBytes(*x->data, x->data.Length());
if (x->firstContainingSeq == 0)
x->firstContainingSeq = pseq;
}
}
if (peerVersion >= 9 && videoStream && videoStream->enabled)
{
s->WriteInt32(static_cast<uint32_t>((lastRecvPacketTime - connectionInitTime) * 1000.0));
}
}
else
{
if (state == STATE_WAIT_INIT || state == STATE_WAIT_INIT_ACK)
{
s->WriteInt32(TLID_DECRYPTED_AUDIO_BLOCK);
int64_t randomID;
crypto.rand_bytes((uint8_t *)&randomID, 8);
s->WriteInt64(randomID);
unsigned char randBytes[7];
crypto.rand_bytes(randBytes, 7);
s->WriteByte(7);
s->WriteBytes(randBytes, 7);
uint32_t pflags = PFLAG_HAS_RECENT_RECV | PFLAG_HAS_SEQ;
if (length > 0)
pflags |= PFLAG_HAS_DATA;
if (state == STATE_WAIT_INIT || state == STATE_WAIT_INIT_ACK)
{
pflags |= PFLAG_HAS_CALL_ID | PFLAG_HAS_PROTO;
}
pflags |= ((uint32_t)type) << 24;
s->WriteInt32(pflags);
if (pflags & PFLAG_HAS_CALL_ID)
{
s->WriteBytes(callID, 16);
}
s->WriteInt32(lastRemoteSeq);
s->WriteInt32(pseq);
s->WriteInt32(acks);
if (pflags & PFLAG_HAS_PROTO)
{
s->WriteInt32(PROTOCOL_NAME);
}
if (length > 0)
{
if (length <= 253)
{
s->WriteByte((unsigned char)length);
}
else
{
s->WriteByte(254);
s->WriteByte((unsigned char)(length & 0xFF));
s->WriteByte((unsigned char)((length >> 8) & 0xFF));
s->WriteByte((unsigned char)((length >> 16) & 0xFF));
}
}
}
else
{
s->WriteInt32(TLID_SIMPLE_AUDIO_BLOCK);
int64_t randomID;
crypto.rand_bytes((uint8_t *)&randomID, 8);
s->WriteInt64(randomID);
unsigned char randBytes[7];
crypto.rand_bytes(randBytes, 7);
s->WriteByte(7);
s->WriteBytes(randBytes, 7);
uint32_t lenWithHeader = length + 13;
if (lenWithHeader > 0)
{
if (lenWithHeader <= 253)
{
s->WriteByte((unsigned char)lenWithHeader);
}
else
{
s->WriteByte(254);
s->WriteByte((unsigned char)(lenWithHeader & 0xFF));
s->WriteByte((unsigned char)((lenWithHeader >> 8) & 0xFF));
s->WriteByte((unsigned char)((lenWithHeader >> 16) & 0xFF));
}
}
s->WriteByte(type);
s->WriteInt32(lastRemoteSeq);
s->WriteInt32(pseq);
s->WriteInt32(acks);
if (peerVersion >= 6)
{
if (currentExtras.empty())
{
s->WriteByte(0);
}
else
{
s->WriteByte(XPFLAG_HAS_EXTRA);
s->WriteByte(static_cast<unsigned char>(currentExtras.size()));
for (vector<UnacknowledgedExtraData>::iterator x = currentExtras.begin(); x != currentExtras.end(); ++x)
{
LOGV("Writing extra into header: type %u, length %d", x->type, int(x->data.Length()));
assert(x->data.Length() <= 254);
s->WriteByte(static_cast<unsigned char>(x->data.Length() + 1));
s->WriteByte(x->type);
s->WriteBytes(*x->data, x->data.Length());
if (x->firstContainingSeq == 0)
x->firstContainingSeq = pseq;
}
}
}
}
}
unacknowledgedIncomingPacketCount = 0;
recentOutgoingPackets.push_back(RecentOutgoingPacket{
pseq,
0,
GetCurrentTime(),
0,
type,
length,
source,
false});
while (recentOutgoingPackets.size() > MAX_RECENT_PACKETS)
{
recentOutgoingPackets.erase(recentOutgoingPackets.begin());
}
lastSentSeq = pseq;
//LOGI("packet header size %d", s->GetLength());
}
void VoIPController::SendInit()
{
ENFORCE_MSG_THREAD;
uint32_t initSeq = GenerateOutSeq();
for (pair<const int64_t, Endpoint> &_e : endpoints)
{
Endpoint &e = _e.second;
if (e.type == Endpoint::Type::TCP_RELAY && !useTCP)
continue;
BufferOutputStream out(1024);
out.WriteInt32(PROTOCOL_VERSION);
out.WriteInt32(MIN_PROTOCOL_VERSION);
uint32_t flags = 0;
if (config.enableCallUpgrade)
flags |= INIT_FLAG_GROUP_CALLS_SUPPORTED;
if (config.enableVideoReceive)
flags |= INIT_FLAG_VIDEO_RECV_SUPPORTED;
if (config.enableVideoSend)
flags |= INIT_FLAG_VIDEO_SEND_SUPPORTED;
if (dataSavingMode)
flags |= INIT_FLAG_DATA_SAVING_ENABLED;
out.WriteInt32(flags);
if (connectionMaxLayer < 74)
{
out.WriteByte(2); // audio codecs count
out.WriteByte(CODEC_OPUS_OLD);
out.WriteByte(0);
out.WriteByte(0);
out.WriteByte(0);
out.WriteInt32(CODEC_OPUS);
out.WriteByte(0); // video codecs count (decode)
out.WriteByte(0); // video codecs count (encode)
}
else
{
out.WriteByte(1);
out.WriteInt32(CODEC_OPUS);
vector<uint32_t> decoders = config.enableVideoReceive ? video::VideoRenderer::GetAvailableDecoders() : vector<uint32_t>();
vector<uint32_t> encoders = config.enableVideoSend ? video::VideoSource::GetAvailableEncoders() : vector<uint32_t>();
out.WriteByte((unsigned char)decoders.size());
for (uint32_t id : decoders)
{
out.WriteInt32(id);
}
if (connectionMaxLayer >= 92)
out.WriteByte((unsigned char)video::VideoRenderer::GetMaximumResolution());
else
out.WriteByte(0);
}
SendOrEnqueuePacket(PendingOutgoingPacket{
/*.seq=*/initSeq,
/*.type=*/PKT_INIT,
/*.len=*/out.GetLength(),
/*.data=*/Buffer(move(out)),
/*.endpoint=*/e.id});
}
if (state == STATE_WAIT_INIT)
SetState(STATE_WAIT_INIT_ACK);
messageThread.Post(
[this] {
if (state == STATE_WAIT_INIT_ACK)
{
SendInit();
}
},
0.5);
}
void VoIPController::InitUDPProxy()
{
if (realUdpSocket != udpSocket)
{
udpSocket->Close();
udpSocket = realUdpSocket;
}
char sbuf[128];
snprintf(sbuf, sizeof(sbuf), "%s:%u", proxyAddress.c_str(), proxyPort);
string proxyHostPort(sbuf);
if (proxyHostPort == lastTestedProxyServer && !proxySupportsUDP)
{
LOGI("Proxy does not support UDP - using UDP directly instead");
messageThread.Post(bind(&VoIPController::ResetUdpAvailability, this));
return;
}
std::shared_ptr<NetworkSocket> tcp = NetworkSocket::Create(NetworkProtocol::TCP);
tcp->Connect(resolvedProxyAddress, proxyPort);
vector<std::shared_ptr<NetworkSocket>> writeSockets;
vector<std::shared_ptr<NetworkSocket>> readSockets;
vector<std::shared_ptr<NetworkSocket>> errorSockets;
while (!tcp->IsFailed() && !tcp->IsReadyToSend())
{
writeSockets.push_back(tcp);
if (!NetworkSocket::Select(readSockets, writeSockets, errorSockets, selectCanceller))
{
LOGW("Select canceled while waiting for proxy control socket to connect");
return;
}
}
LOGV("UDP proxy control socket ready to send");
std::shared_ptr<NetworkSocketSOCKS5Proxy> udpProxy = std::make_shared<NetworkSocketSOCKS5Proxy>(tcp, realUdpSocket, proxyUsername, proxyPassword);
udpProxy->OnReadyToSend();
writeSockets.clear();
while (!udpProxy->IsFailed() && !tcp->IsFailed() && !udpProxy->IsReadyToSend())
{
readSockets.clear();
errorSockets.clear();
readSockets.push_back(tcp);
errorSockets.push_back(tcp);
if (!NetworkSocket::Select(readSockets, writeSockets, errorSockets, selectCanceller))
{
LOGW("Select canceled while waiting for UDP proxy to initialize");
return;
}
if (!readSockets.empty())
udpProxy->OnReadyToReceive();
}
LOGV("UDP proxy initialized");
if (udpProxy->IsFailed())
{
udpProxy->Close();
proxySupportsUDP = false;
}
else
{
udpSocket = udpProxy;
}
messageThread.Post(bind(&VoIPController::ResetUdpAvailability, this));
}
void VoIPController::RunRecvThread()
{
LOGI("Receive thread starting");
if (proxyProtocol == PROXY_SOCKS5)
{
resolvedProxyAddress = NetworkSocket::ResolveDomainName(proxyAddress);
if (resolvedProxyAddress.IsEmpty())
{
LOGW("Error resolving proxy address %s", proxyAddress.c_str());
SetState(STATE_FAILED);
return;
}
}
else
{
udpConnectivityState = UDP_PING_PENDING;
udpPingTimeoutID = messageThread.Post(std::bind(&VoIPController::SendUdpPings, this), 0.0, 0.5);
}
while (runReceiver)
{
if (proxyProtocol == PROXY_SOCKS5 && needReInitUdpProxy)
{
InitUDPProxy();
needReInitUdpProxy = false;
}
vector<std::shared_ptr<NetworkSocket>> readSockets;
vector<std::shared_ptr<NetworkSocket>> errorSockets;
vector<std::shared_ptr<NetworkSocket>> writeSockets;
readSockets.push_back(udpSocket);
errorSockets.push_back(realUdpSocket);
if (!realUdpSocket->IsReadyToSend())
writeSockets.push_back(realUdpSocket);
{
MutexGuard m(endpointsMutex);
for (pair<const int64_t, Endpoint> &_e : endpoints)
{
const Endpoint &e = _e.second;
if (e.type == Endpoint::Type::TCP_RELAY)
{
if (e.socket)
{
readSockets.push_back(e.socket);
errorSockets.push_back(e.socket);
if (!e.socket->IsReadyToSend())
{
NetworkSocketSOCKS5Proxy *proxy = dynamic_cast<NetworkSocketSOCKS5Proxy *>(&*e.socket);
if (!proxy || proxy->NeedSelectForSending())
writeSockets.push_back(e.socket);
}
}
}
}
}
{
bool selRes = NetworkSocket::Select(readSockets, writeSockets, errorSockets, selectCanceller);
if (!selRes)
{
LOGV("Select canceled");
continue;
}
}
if (!runReceiver)
return;
if (!errorSockets.empty())
{
if (find(errorSockets.begin(), errorSockets.end(), realUdpSocket) != errorSockets.end())
{
LOGW("UDP socket failed");
SetState(STATE_FAILED);
return;
}
MutexGuard m(endpointsMutex);
for (std::shared_ptr<NetworkSocket> &socket : errorSockets)
{
for (pair<const int64_t, Endpoint> &_e : endpoints)
{
Endpoint &e = _e.second;
if (e.socket == socket)
{
e.socket->Close();
e.socket.reset();
LOGI("Closing failed TCP socket for %s:%u", e.GetAddress().ToString().c_str(), e.port);
}
}
}
continue;
}
for (std::shared_ptr<NetworkSocket> &socket : readSockets)
{
//while(packet.length){
NetworkPacket packet = socket->Receive();
if (packet.address.IsEmpty())
{
LOGE("Packet has null address. This shouldn't happen.");
continue;
}
if (packet.data.IsEmpty())
{
LOGE("Packet has zero length.");
continue;
}
//LOGV("Received %d bytes from %s:%d at %.5lf", len, packet.address->ToString().c_str(), packet.port, GetCurrentTime());
messageThread.Post(bind(&VoIPController::NetworkPacketReceived, this, make_shared<NetworkPacket>(move(packet))));
}
if (!writeSockets.empty())
{
messageThread.Post(bind(&VoIPController::TrySendQueuedPackets, this));
}
}
LOGI("=== recv thread exiting ===");
}
void VoIPController::TrySendQueuedPackets()
{
ENFORCE_MSG_THREAD;
for (vector<PendingOutgoingPacket>::iterator opkt = sendQueue.begin(); opkt != sendQueue.end();)
{
Endpoint *endpoint = GetEndpointForPacket(*opkt);
if (!endpoint)
{
opkt = sendQueue.erase(opkt);
LOGE("SendQueue contained packet for nonexistent endpoint");
continue;
}
bool canSend;
if (endpoint->type != Endpoint::Type::TCP_RELAY)
canSend = realUdpSocket->IsReadyToSend();
else
canSend = endpoint->socket && endpoint->socket->IsReadyToSend();
if (canSend)
{
LOGI("Sending queued packet");
SendOrEnqueuePacket(move(*opkt), false);
opkt = sendQueue.erase(opkt);
}
else
{
++opkt;
}
}
}
bool VoIPController::WasOutgoingPacketAcknowledged(uint32_t seq)
{
RecentOutgoingPacket *pkt = GetRecentOutgoingPacket(seq);
if (!pkt)
return false;
return pkt->ackTime != 0.0;
}
VoIPController::RecentOutgoingPacket *VoIPController::GetRecentOutgoingPacket(uint32_t seq)
{
for (RecentOutgoingPacket &opkt : recentOutgoingPackets)
{
if (opkt.seq == seq)
{
return &opkt;
}
}
return NULL;
}
void VoIPController::NetworkPacketReceived(shared_ptr<NetworkPacket> _packet)
{
ENFORCE_MSG_THREAD;
NetworkPacket &packet = *_packet;
int64_t srcEndpointID = 0;
if (!packet.address.isIPv6)
{
for (pair<const int64_t, Endpoint> &_e : endpoints)
{
const Endpoint &e = _e.second;
if (e.address == packet.address && e.port == packet.port)
{
if ((e.type != Endpoint::Type::TCP_RELAY && packet.protocol == NetworkProtocol::UDP) || (e.type == Endpoint::Type::TCP_RELAY && packet.protocol == NetworkProtocol::TCP))
{
srcEndpointID = e.id;
break;
}
}
}
if (!srcEndpointID && packet.protocol == NetworkProtocol::UDP)
{
try
{
Endpoint &p2p = GetEndpointByType(Endpoint::Type::UDP_P2P_INET);
if (p2p.rtts[0] == 0.0 && p2p.address.PrefixMatches(24, packet.address))
{
LOGD("Packet source matches p2p endpoint partially: %s:%u", packet.address.ToString().c_str(), packet.port);
srcEndpointID = p2p.id;
}
}
catch (out_of_range &ex)
{
}
}
}
else
{
for (pair<const int64_t, Endpoint> &_e : endpoints)
{
const Endpoint &e = _e.second;
if (e.v6address == packet.address && e.port == packet.port && e.IsIPv6Only())
{
if ((e.type != Endpoint::Type::TCP_RELAY && packet.protocol == NetworkProtocol::UDP) || (e.type == Endpoint::Type::TCP_RELAY && packet.protocol == NetworkProtocol::TCP))
{
srcEndpointID = e.id;
break;
}
}
}
}
if (!srcEndpointID)
{
LOGW("Received a packet from unknown source %s:%u", packet.address.ToString().c_str(), packet.port);
return;
}
/*if(len<=0){
//LOGW("error receiving: %d / %s", errno, strerror(errno));
continue;
}*/
if (IS_MOBILE_NETWORK(networkType))
stats.bytesRecvdMobile += (uint64_t)packet.data.Length();
else
stats.bytesRecvdWifi += (uint64_t)packet.data.Length();
try
{
ProcessIncomingPacket(packet, endpoints.at(srcEndpointID));
}
catch (out_of_range &x)
{
LOGW("Error parsing packet: %s", x.what());
}
}
void VoIPController::ProcessIncomingPacket(NetworkPacket &packet, Endpoint &srcEndpoint)
{
ENFORCE_MSG_THREAD;
// Initial packet decryption and recognition
unsigned char *buffer = *packet.data;
size_t len = packet.data.Length();
BufferInputStream in(packet.data);
bool hasPeerTag = false;
if (peerVersion < 9 || srcEndpoint.IsReflector())
{
if (memcmp(buffer, srcEndpoint.IsReflector() ? (void *)srcEndpoint.peerTag : (void *)callID, 16) != 0)
{
LOGW("Received packet has wrong peerTag");
return;
}
in.Seek(16);
hasPeerTag = true;
}
if (in.Remaining() >= 16 && srcEndpoint.IsReflector() && *reinterpret_cast<uint64_t *>(buffer + 16) == 0xFFFFFFFFFFFFFFFFLL && *reinterpret_cast<uint32_t *>(buffer + 24) == 0xFFFFFFFF)
{
// relay special request response
in.Seek(16 + 12);
uint32_t tlid = in.ReadUInt32();
if (tlid == TLID_UDP_REFLECTOR_SELF_INFO)
{
if (srcEndpoint.type == Endpoint::Type::UDP_RELAY /*&& udpConnectivityState==UDP_PING_SENT*/ && in.Remaining() >= 32)
{
int32_t date = in.ReadInt32();
int64_t queryID = in.ReadInt64();
unsigned char myIP[16];
in.ReadBytes(myIP, 16);
int32_t myPort = in.ReadInt32();
//udpConnectivityState=UDP_AVAILABLE;
double selfRTT = 0.0;
srcEndpoint.udpPongCount++;
srcEndpoint.totalUdpPingReplies++;
if (srcEndpoint.udpPingTimes.find(queryID) != srcEndpoint.udpPingTimes.end())
{
double sendTime = srcEndpoint.udpPingTimes[queryID];
srcEndpoint.udpPingTimes.erase(queryID);
srcEndpoint.selfRtts.Add(selfRTT = GetCurrentTime() - sendTime);
}
LOGV("Received UDP ping reply from %s:%d: date=%d, queryID=%ld, my IP=%s, my port=%d, selfRTT=%f", srcEndpoint.address.ToString().c_str(), srcEndpoint.port, date, (long int)queryID, NetworkAddress::IPv4(*reinterpret_cast<uint32_t *>(myIP + 12)).ToString().c_str(), myPort, selfRTT);
if (srcEndpoint.IsIPv6Only() && !didSendIPv6Endpoint)
{
NetworkAddress realAddr = NetworkAddress::IPv6(myIP);
if (realAddr == myIPv6)
{
LOGI("Public IPv6 matches local address");
useIPv6 = true;
if (allowP2p)
{
didSendIPv6Endpoint = true;
BufferOutputStream o(18);
o.WriteBytes(myIP, 16);
o.WriteInt16(udpSocket->GetLocalPort());
Buffer b(move(o));
SendExtra(b, EXTRA_TYPE_IPV6_ENDPOINT);
}
}
}
}
}
else if (tlid == TLID_UDP_REFLECTOR_PEER_INFO)
{
if (in.Remaining() >= 16)
{
uint32_t myAddr = in.ReadUInt32();
uint32_t myPort = in.ReadUInt32();
uint32_t peerAddr = in.ReadUInt32();
uint32_t peerPort = in.ReadUInt32();
constexpr int64_t p2pID = static_cast<int64_t>(FOURCC('P', '2', 'P', '4')) << 32;
constexpr int64_t lanID = static_cast<int64_t>(FOURCC('L', 'A', 'N', '4')) << 32;
if (currentEndpoint == p2pID || currentEndpoint == lanID)
currentEndpoint = preferredRelay;
if (endpoints.find(lanID) != endpoints.end())
{
MutexGuard m(endpointsMutex);
endpoints.erase(lanID);
}
unsigned char peerTag[16];
LOGW("Received reflector peer info, my=%s:%u, peer=%s:%u", NetworkAddress::IPv4(myAddr).ToString().c_str(), myPort, NetworkAddress::IPv4(peerAddr).ToString().c_str(), peerPort);
if (waitingForRelayPeerInfo)
{
Endpoint p2p(p2pID, (uint16_t)peerPort, NetworkAddress::IPv4(peerAddr), NetworkAddress::Empty(), Endpoint::Type::UDP_P2P_INET, peerTag);
{
MutexGuard m(endpointsMutex);
endpoints[p2pID] = p2p;
}
if (myAddr == peerAddr)
{
LOGW("Detected LAN");
NetworkAddress lanAddr = NetworkAddress::IPv4(0);
udpSocket->GetLocalInterfaceInfo(&lanAddr, NULL);
BufferOutputStream pkt(8);
pkt.WriteInt32(lanAddr.addr.ipv4);
pkt.WriteInt32(udpSocket->GetLocalPort());
if (peerVersion < 6)
{
SendPacketReliably(PKT_LAN_ENDPOINT, pkt.GetBuffer(), pkt.GetLength(), 0.5, 10);
}
else
{
Buffer buf(move(pkt));
SendExtra(buf, EXTRA_TYPE_LAN_ENDPOINT);
}
}
waitingForRelayPeerInfo = false;
}
}
}
else
{
LOGV("Received relay response with unknown tl id: 0x%08X", tlid);
}
return;
}
if (in.Remaining() < 40)
{
LOGV("Received packet is too small");
return;
}
bool retryWith2 = false;
size_t innerLen = 0;
bool shortFormat = peerVersion >= 8 || (!peerVersion && connectionMaxLayer >= 92);
if (!useMTProto2)
{
unsigned char fingerprint[8], msgHash[16];
in.ReadBytes(fingerprint, 8);
in.ReadBytes(msgHash, 16);
unsigned char key[32], iv[32];
KDF(msgHash, isOutgoing ? 8 : 0, key, iv);
unsigned char aesOut[MSC_STACK_FALLBACK(in.Remaining(), 1500)];
if (in.Remaining() > sizeof(aesOut))
return;
crypto.aes_ige_decrypt((unsigned char *)buffer + in.GetOffset(), aesOut, in.Remaining(), key, iv);
BufferInputStream _in(aesOut, in.Remaining());
unsigned char sha[SHA1_LENGTH];
uint32_t _len = _in.ReadUInt32();
if (_len > _in.Remaining())
_len = (uint32_t)_in.Remaining();
crypto.sha1((uint8_t *)(aesOut), (size_t)(_len + 4), sha);
if (memcmp(msgHash, sha + (SHA1_LENGTH - 16), 16) != 0)
{
LOGW("Received packet has wrong hash after decryption");
if (state == STATE_WAIT_INIT || state == STATE_WAIT_INIT_ACK)
retryWith2 = true;
else
return;
}
else
{
memcpy(buffer + in.GetOffset(), aesOut, in.Remaining());
in.ReadInt32();
}
}
if (useMTProto2 || retryWith2)
{
if (hasPeerTag)
in.Seek(16); // peer tag
unsigned char fingerprint[8], msgKey[16];
if (!shortFormat)
{
in.ReadBytes(fingerprint, 8);
if (memcmp(fingerprint, keyFingerprint, 8) != 0)
{
LOGW("Received packet has wrong key fingerprint");
return;
}
}
in.ReadBytes(msgKey, 16);
unsigned char decrypted[1500];
unsigned char aesKey[32], aesIv[32];
KDF2(msgKey, isOutgoing ? 8 : 0, aesKey, aesIv);
size_t decryptedLen = in.Remaining();
if (decryptedLen > sizeof(decrypted))
return;
if (decryptedLen % 16 != 0)
{
LOGW("wrong decrypted length");
return;
}
crypto.aes_ige_decrypt(*packet.data + in.GetOffset(), decrypted, decryptedLen, aesKey, aesIv);
in = BufferInputStream(decrypted, decryptedLen);
//LOGD("received packet length: %d", in.ReadInt32());
size_t sizeSize = shortFormat ? 0 : 4;
BufferOutputStream buf(decryptedLen + 32);
size_t x = isOutgoing ? 8 : 0;
buf.WriteBytes(encryptionKey + 88 + x, 32);
buf.WriteBytes(decrypted + sizeSize, decryptedLen - sizeSize);
unsigned char msgKeyLarge[32];
crypto.sha256(buf.GetBuffer(), buf.GetLength(), msgKeyLarge);
if (memcmp(msgKey, msgKeyLarge + 8, 16) != 0)
{
LOGW("Received packet has wrong hash");
return;
}
innerLen = static_cast<uint32_t>(shortFormat ? in.ReadInt16() : in.ReadInt32());
if (innerLen > decryptedLen - sizeSize)
{
LOGW("Received packet has wrong inner length (%d with total of %u)", (int)innerLen, (unsigned int)decryptedLen);
return;
}
if (decryptedLen - innerLen < (shortFormat ? 16 : 12))
{
LOGW("Received packet has too little padding (%u)", (unsigned int)(decryptedLen - innerLen));
return;
}
memcpy(buffer, decrypted + (shortFormat ? 2 : 4), innerLen);
in = BufferInputStream(buffer, (size_t)innerLen);
if (retryWith2)
{
LOGD("Successfully decrypted packet in MTProto2.0 fallback, upgrading");
useMTProto2 = true;
}
}
lastRecvPacketTime = GetCurrentTime();
if (state == STATE_RECONNECTING)
{
LOGI("Received a valid packet while reconnecting - setting state to established");
SetState(STATE_ESTABLISHED);
}
if (srcEndpoint.type == Endpoint::Type::UDP_P2P_INET && !srcEndpoint.IsIPv6Only())
{
if (srcEndpoint.port != packet.port || srcEndpoint.address != packet.address)
{
if (!packet.address.isIPv6)
{
LOGI("Incoming packet was decrypted successfully, changing P2P endpoint to %s:%u", packet.address.ToString().c_str(), packet.port);
srcEndpoint.address = packet.address;
srcEndpoint.port = packet.port;
}
}
}
// decryptedAudioBlock random_id:long random_bytes:string flags:# voice_call_id:flags.2?int128 in_seq_no:flags.4?int out_seq_no:flags.4?int
// recent_received_mask:flags.5?int proto:flags.3?int extra:flags.1?string raw_data:flags.0?string = DecryptedAudioBlock
//
// simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedAudioBlock;
// Version-specific extraction of packet fields ackId (last received packet seq on remote), (incoming packet seq) pseq, (ack mask) acks, (packet type) type, (flags) pflags, packet length
uint32_t ackId; // Last received packet seqno on remote
uint32_t pseq; // Incoming packet seqno
uint32_t acks; // Ack mask
unsigned char type, pflags; // Packet type, flags
size_t packetInnerLen = 0;
if (shortFormat)
{
type = in.ReadByte();
ackId = in.ReadUInt32();
pseq = in.ReadUInt32();
acks = in.ReadUInt32();
pflags = in.ReadByte();
packetInnerLen = innerLen - 14;
}
else
{
uint32_t tlid = in.ReadUInt32();
if (tlid == TLID_DECRYPTED_AUDIO_BLOCK)
{
in.ReadInt64(); // random id
uint32_t randLen = in.ReadTlLength();
in.Seek(in.GetOffset() + randLen + pad4(randLen));
uint32_t flags = in.ReadUInt32();
type = (unsigned char)((flags >> 24) & 0xFF);
if (!(flags & PFLAG_HAS_SEQ && flags & PFLAG_HAS_RECENT_RECV))
{
LOGW("Received packet doesn't have PFLAG_HAS_SEQ, PFLAG_HAS_RECENT_RECV, or both");
return;
}
if (flags & PFLAG_HAS_CALL_ID)
{
unsigned char pktCallID[16];
in.ReadBytes(pktCallID, 16);
if (memcmp(pktCallID, callID, 16) != 0)
{
LOGW("Received packet has wrong call id");
lastError = ERROR_UNKNOWN;
SetState(STATE_FAILED);
return;
}
}
ackId = in.ReadUInt32();
pseq = in.ReadUInt32();
acks = in.ReadUInt32();
if (flags & PFLAG_HAS_PROTO)
{
uint32_t proto = in.ReadUInt32();
if (proto != PROTOCOL_NAME)
{
LOGW("Received packet uses wrong protocol");
lastError = ERROR_INCOMPATIBLE;
SetState(STATE_FAILED);
return;
}
}
if (flags & PFLAG_HAS_EXTRA)
{
uint32_t extraLen = in.ReadTlLength();
in.Seek(in.GetOffset() + extraLen + pad4(extraLen));
}
if (flags & PFLAG_HAS_DATA)
{
packetInnerLen = in.ReadTlLength();
}
pflags = 0;
}
else if (tlid == TLID_SIMPLE_AUDIO_BLOCK)
{
in.ReadInt64(); // random id
uint32_t randLen = in.ReadTlLength();
in.Seek(in.GetOffset() + randLen + pad4(randLen));
packetInnerLen = in.ReadTlLength();
type = in.ReadByte();
ackId = in.ReadUInt32();
pseq = in.ReadUInt32();
acks = in.ReadUInt32();
if (peerVersion >= 6)
pflags = in.ReadByte();
else
pflags = 0;
}
else
{
LOGW("Received a packet of unknown type %08X", tlid);
return;
}
}
packetsReceived++;
// Duplicate and moving window check
if (seqgt(pseq, lastRemoteSeq - MAX_RECENT_PACKETS))
{
if (find(recentIncomingSeqs.begin(), recentIncomingSeqs.end(), pseq) != recentIncomingSeqs.end())
{
LOGW("Received duplicated packet for seq %u", pseq);
return;
}
recentIncomingSeqs[recentIncomingSeqIdx++] = pseq;
recentIncomingSeqIdx %= recentIncomingSeqs.size();
if (seqgt(pseq, lastRemoteSeq))
lastRemoteSeq = pseq;
}
else
{
LOGW("Packet %u is out of order and too late", pseq);
return;
}
// Extra data
if (pflags & XPFLAG_HAS_EXTRA)
{
unsigned char extraCount = in.ReadByte();
for (int i = 0; i < extraCount; i++)
{
size_t extraLen = in.ReadByte();
Buffer xbuffer(extraLen);
in.ReadBytes(*xbuffer, extraLen);
ProcessExtraData(xbuffer);
}
}
uint32_t recvTS = 0;
if (pflags & XPFLAG_HAS_RECV_TS)
{
recvTS = static_cast<uint32_t>(in.ReadInt32());
}
if (seqgt(ackId, lastRemoteAckSeq))
{
if (waitingForAcks && lastRemoteAckSeq >= firstSentPing)
{
rttHistory.Reset();
waitingForAcks = false;
dontSendPackets = 10;
messageThread.Post(
[this] {
dontSendPackets = 0;
},
1.0);
LOGI("resuming sending");
}
lastRemoteAckSeq = ackId;
conctl.PacketAcknowledged(ackId);
// Status list of acked seqnos, starting from the seq explicitly present in the packet + up to 32 seqs ago
std::array<uint32_t, 33> peerAcks{0};
peerAcks[0] = ackId;
for (unsigned int i = 1; i <= 32; i++)
{
if ((acks >> (32 - i)) & 1)
{
peerAcks[i] = ackId - i;
}
}
for (RecentOutgoingPacket &opkt : recentOutgoingPackets)
{
if (opkt.ackTime != 0.0)
continue;
if (find(peerAcks.begin(), peerAcks.end(), opkt.seq) != peerAcks.end())
{
opkt.ackTime = GetCurrentTime();
if (opkt.lost)
{
LOGW("acknowledged lost packet %u", opkt.seq);
sendLosses--;
}
if (opkt.sender && !opkt.lost)
{ // don't report lost packets as acknowledged to PacketSenders
opkt.sender->PacketAcknowledged(opkt.seq, opkt.sendTime, recvTS / 1000.0f, opkt.type, opkt.size);
}
// TODO move this to a PacketSender
conctl.PacketAcknowledged(opkt.seq);
}
}
if (peerVersion < 6)
{
for (unsigned int i = 0; i < queuedPackets.size(); i++)
{
QueuedPacket &qp = queuedPackets[i];
int j;
bool didAck = false;
for (j = 0; j < 16; j++)
{
LOGD("queued packet %u, seq %u=%u", i, j, qp.seqs[j]);
if (qp.seqs[j] == 0)
break;
int remoteAcksIndex = lastRemoteAckSeq - qp.seqs[j];
//LOGV("remote acks index %u, value %f", remoteAcksIndex, remoteAcksIndex>=0 && remoteAcksIndex<32 ? remoteAcks[remoteAcksIndex] : -1);
if (seqgt(lastRemoteAckSeq, qp.seqs[j]) && remoteAcksIndex >= 0 && remoteAcksIndex < 32)
{
for (RecentOutgoingPacket &opkt : recentOutgoingPackets)
{
if (opkt.seq == qp.seqs[j] && opkt.ackTime > 0)
{
LOGD("did ack seq %u, removing", qp.seqs[j]);
didAck = true;
break;
}
}
if (didAck)
break;
}
}
if (didAck)
{
queuedPackets.erase(queuedPackets.begin() + i);
i--;
continue;
}
}
}
else
{
for (auto x = currentExtras.begin(); x != currentExtras.end();)
{
if (x->firstContainingSeq != 0 && seqgte(lastRemoteAckSeq, x->firstContainingSeq))
{
LOGV("Peer acknowledged extra type %u length %u", x->type, (unsigned int)x->data.Length());
ProcessAcknowledgedOutgoingExtra(*x);
x = currentExtras.erase(x);
continue;
}
++x;
}
}
}
Endpoint *_currentEndpoint = &endpoints.at(currentEndpoint);
if (srcEndpoint.id != currentEndpoint && srcEndpoint.IsReflector() && (_currentEndpoint->IsP2P() || _currentEndpoint->averageRTT == 0))
{
if (seqgt(lastSentSeq - 32, lastRemoteAckSeq))
{
currentEndpoint = srcEndpoint.id;
_currentEndpoint = &srcEndpoint;
LOGI("Peer network address probably changed, switching to relay");
if (allowP2p)
SendPublicEndpointsRequest();
}
}
if (config.logPacketStats)
{
DebugLoggedPacket dpkt = {
static_cast<int32_t>(pseq),
GetCurrentTime() - connectionInitTime,
static_cast<int32_t>(packet.data.Length())};
debugLoggedPackets.push_back(dpkt);
if (debugLoggedPackets.size() >= 2500)
{
debugLoggedPackets.erase(debugLoggedPackets.begin(), debugLoggedPackets.begin() + 500);
}
}
unacknowledgedIncomingPacketCount++;
if (unacknowledgedIncomingPacketCount > unackNopThreshold)
{
//LOGV("Sending nop packet as ack");
SendNopPacket();
}
#ifdef LOG_PACKETS
LOGV("Received: from=%s:%u, seq=%u, length=%u, type=%s", srcEndpoint.GetAddress().ToString().c_str(), srcEndpoint.port, pseq, (unsigned int)packet.data.Length(), GetPacketTypeString(type).c_str());
#endif
//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());
//LOGV("Packet %u type is %d", pseq, type);
if (type == PKT_INIT)
{
LOGD("Received init");
uint32_t ver = in.ReadUInt32();
if (!receivedInit)
peerVersion = ver;
LOGI("Peer version is %d", peerVersion);
uint32_t minVer = in.ReadUInt32();
if (minVer > PROTOCOL_VERSION || peerVersion < MIN_PROTOCOL_VERSION)
{
lastError = ERROR_INCOMPATIBLE;
SetState(STATE_FAILED);
return;
}
uint32_t flags = in.ReadUInt32();
if (!receivedInit)
{
if (flags & INIT_FLAG_DATA_SAVING_ENABLED)
{
dataSavingRequestedByPeer = true;
UpdateDataSavingState();
UpdateAudioBitrateLimit();
}
if (flags & INIT_FLAG_GROUP_CALLS_SUPPORTED)
{
peerCapabilities |= TGVOIP_PEER_CAP_GROUP_CALLS;
}
if (flags & INIT_FLAG_VIDEO_RECV_SUPPORTED)
{
peerCapabilities |= TGVOIP_PEER_CAP_VIDEO_DISPLAY;
}
if (flags & INIT_FLAG_VIDEO_SEND_SUPPORTED)
{
peerCapabilities |= TGVOIP_PEER_CAP_VIDEO_CAPTURE;
}
}
unsigned int i;
unsigned int numSupportedAudioCodecs = in.ReadByte();
for (i = 0; i < numSupportedAudioCodecs; i++)
{
if (peerVersion < 5)
in.ReadByte(); // ignore for now
else
in.ReadInt32();
}
if (!receivedInit && ((flags & INIT_FLAG_VIDEO_SEND_SUPPORTED && config.enableVideoReceive) || (flags & INIT_FLAG_VIDEO_RECV_SUPPORTED && config.enableVideoSend)))
{
LOGD("Peer video decoders:");
unsigned int numSupportedVideoDecoders = in.ReadByte();
for (i = 0; i < numSupportedVideoDecoders; i++)
{
uint32_t id = static_cast<uint32_t>(in.ReadInt32());
peerVideoDecoders.push_back(id);
char *_id = reinterpret_cast<char *>(&id);
LOGD("%c%c%c%c", _id[3], _id[2], _id[1], _id[0]);
}
protocolInfo.maxVideoResolution = in.ReadByte();
SetupOutgoingVideoStream();
}
BufferOutputStream out(1024);
out.WriteInt32(PROTOCOL_VERSION);
out.WriteInt32(MIN_PROTOCOL_VERSION);
out.WriteByte((unsigned char)outgoingStreams.size());
for (vector<shared_ptr<Stream>>::iterator s = outgoingStreams.begin(); s != outgoingStreams.end(); ++s)
{
out.WriteByte((*s)->id);
out.WriteByte((*s)->type);
if (peerVersion < 5)
out.WriteByte((unsigned char)((*s)->codec == CODEC_OPUS ? CODEC_OPUS_OLD : 0));
else
out.WriteInt32((*s)->codec);
out.WriteInt16((*s)->frameDuration);
out.WriteByte((unsigned char)((*s)->enabled ? 1 : 0));
}
LOGI("Sending init ack");
size_t outLength = out.GetLength();
SendOrEnqueuePacket(PendingOutgoingPacket{
/*.seq=*/GenerateOutSeq(),
/*.type=*/PKT_INIT_ACK,
/*.len=*/outLength,
/*.data=*/Buffer(move(out)),
/*.endpoint=*/0});
if (!receivedInit)
{
receivedInit = true;
if ((srcEndpoint.type == Endpoint::Type::UDP_RELAY && udpConnectivityState != UDP_BAD && udpConnectivityState != UDP_NOT_AVAILABLE) || srcEndpoint.type == Endpoint::Type::TCP_RELAY)
{
currentEndpoint = srcEndpoint.id;
if (srcEndpoint.type == Endpoint::Type::UDP_RELAY || (useTCP && srcEndpoint.type == Endpoint::Type::TCP_RELAY))
preferredRelay = srcEndpoint.id;
}
}
if (!audioStarted && receivedInitAck)
{
StartAudio();
audioStarted = true;
}
}
if (type == PKT_INIT_ACK)
{
LOGD("Received init ack");
if (!receivedInitAck)
{
receivedInitAck = true;
messageThread.Cancel(initTimeoutID);
initTimeoutID = MessageThread::INVALID_ID;
if (packetInnerLen > 10)
{
peerVersion = in.ReadInt32();
uint32_t minVer = in.ReadUInt32();
if (minVer > PROTOCOL_VERSION || peerVersion < MIN_PROTOCOL_VERSION)
{
lastError = ERROR_INCOMPATIBLE;
SetState(STATE_FAILED);
return;
}
}
else
{
peerVersion = 1;
}
LOGI("peer version from init ack %d", peerVersion);
unsigned char streamCount = in.ReadByte();
if (streamCount == 0)
return;
int i;
shared_ptr<Stream> incomingAudioStream = nullptr;
for (i = 0; i < streamCount; i++)
{
shared_ptr<Stream> stm = make_shared<Stream>();
stm->id = in.ReadByte();
stm->type = in.ReadByte();
if (peerVersion < 5)
{
unsigned char codec = in.ReadByte();
if (codec == CODEC_OPUS_OLD)
stm->codec = CODEC_OPUS;
}
else
{
stm->codec = in.ReadUInt32();
}
in.ReadInt16();
stm->frameDuration = 60;
stm->enabled = in.ReadByte() == 1;
if (stm->type == STREAM_TYPE_VIDEO && peerVersion < 9)
{
LOGV("Skipping video stream for old protocol version");
continue;
}
if (stm->type == STREAM_TYPE_AUDIO)
{
stm->jitterBuffer = make_shared<JitterBuffer>(stm->frameDuration);
if (stm->frameDuration > 50)
stm->jitterBuffer->SetMinPacketCount(ServerConfig::GetSharedInstance()->GetUInt("jitter_initial_delay_60", 2));
else if (stm->frameDuration > 30)
stm->jitterBuffer->SetMinPacketCount(ServerConfig::GetSharedInstance()->GetUInt("jitter_initial_delay_40", 4));
else
stm->jitterBuffer->SetMinPacketCount(ServerConfig::GetSharedInstance()->GetUInt("jitter_initial_delay_20", 6));
stm->decoder = nullptr;
}
else if (stm->type == STREAM_TYPE_VIDEO)
{
if (!stm->packetReassembler)
{
stm->packetReassembler = make_shared<PacketReassembler>();
stm->packetReassembler->SetCallback(bind(&VoIPController::ProcessIncomingVideoFrame, this, placeholders::_1, placeholders::_2, placeholders::_3, placeholders::_4));
}
}
else
{
LOGW("Unknown incoming stream type: %d", stm->type);
continue;
}
incomingStreams.push_back(stm);
if (stm->type == STREAM_TYPE_AUDIO && !incomingAudioStream)
incomingAudioStream = stm;
}
if (!incomingAudioStream)
return;
if (peerVersion >= 5 && !useMTProto2)
{
useMTProto2 = true;
LOGD("MTProto2 wasn't initially enabled for whatever reason but peer supports it; upgrading");
}
if (!audioStarted && receivedInit)
{
StartAudio();
audioStarted = true;
}
messageThread.Post(
[this] {
if (state == STATE_WAIT_INIT_ACK)
{
SetState(STATE_ESTABLISHED);
}
},
ServerConfig::GetSharedInstance()->GetDouble("established_delay_if_no_stream_data", 1.5));
if (allowP2p)
SendPublicEndpointsRequest();
}
}
if (type == PKT_STREAM_DATA || type == PKT_STREAM_DATA_X2 || type == PKT_STREAM_DATA_X3)
{
if (!receivedFirstStreamPacket)
{
receivedFirstStreamPacket = true;
if (state != STATE_ESTABLISHED && receivedInitAck)
{
messageThread.Post(
[this]() {
SetState(STATE_ESTABLISHED);
},
.5);
LOGW("First audio packet - setting state to ESTABLISHED");
}
}
int count;
switch (type)
{
case PKT_STREAM_DATA_X2:
count = 2;
break;
case PKT_STREAM_DATA_X3:
count = 3;
break;
case PKT_STREAM_DATA:
default:
count = 1;
break;
}
int i;
if (srcEndpoint.type == Endpoint::Type::UDP_RELAY && srcEndpoint.id != peerPreferredRelay)
{
peerPreferredRelay = srcEndpoint.id;
}
for (i = 0; i < count; i++)
{
unsigned char streamID = in.ReadByte();
unsigned char flags = (unsigned char)(streamID & 0xC0);
streamID &= 0x3F;
uint16_t sdlen = (uint16_t)(flags & STREAM_DATA_FLAG_LEN16 ? in.ReadInt16() : in.ReadByte());
uint32_t pts = in.ReadUInt32();
unsigned char fragmentCount = 1;
unsigned char fragmentIndex = 0;
//LOGD("stream data, pts=%d, len=%d, rem=%d", pts, sdlen, in.Remaining());
audioTimestampIn = pts;
if (!audioOutStarted && audioOutput)
{
MutexGuard m(audioIOMutex);
audioOutput->Start();
audioOutStarted = true;
}
bool fragmented = static_cast<bool>(sdlen & STREAM_DATA_XFLAG_FRAGMENTED);
bool extraFEC = static_cast<bool>(sdlen & STREAM_DATA_XFLAG_EXTRA_FEC);
bool keyframe = static_cast<bool>(sdlen & STREAM_DATA_XFLAG_KEYFRAME);
if (fragmented)
{
fragmentIndex = in.ReadByte();
fragmentCount = in.ReadByte();
}
sdlen &= 0x7FF;
if (in.GetOffset() + sdlen > len)
{
return;
}
shared_ptr<Stream> stm;
for (shared_ptr<Stream> &ss : incomingStreams)
{
if (ss->id == streamID)
{
stm = ss;
break;
}
}
if (stm && stm->type == STREAM_TYPE_AUDIO)
{
if (stm->jitterBuffer)
{
stm->jitterBuffer->HandleInput(static_cast<unsigned char *>(buffer + in.GetOffset()), sdlen, pts, false);
if (extraFEC)
{
in.Seek(in.GetOffset() + sdlen);
unsigned int fecCount = in.ReadByte();
for (unsigned int j = 0; j < fecCount; j++)
{
unsigned char dlen = in.ReadByte();
unsigned char data[256];
in.ReadBytes(data, dlen);
stm->jitterBuffer->HandleInput(data, dlen, pts - (fecCount - j - 1) * stm->frameDuration, true);
}
}
}
}
else if (stm && stm->type == STREAM_TYPE_VIDEO)
{
if (stm->packetReassembler)
{
uint8_t frameSeq = in.ReadByte();
Buffer pdata(sdlen);
uint16_t rotation = 0;
if (fragmentIndex == 0)
{
unsigned char _rotation = in.ReadByte() & (unsigned char)VIDEO_ROTATION_MASK;
switch (_rotation)
{
case VIDEO_ROTATION_0:
rotation = 0;
break;
case VIDEO_ROTATION_90:
rotation = 90;
break;
case VIDEO_ROTATION_180:
rotation = 180;
break;
case VIDEO_ROTATION_270:
rotation = 270;
break;
default: // unreachable on sane CPUs
abort();
}
//if(rotation!=stm->rotation){
// stm->rotation=rotation;
// LOGI("Video rotation: %u", rotation);
//}
}
pdata.CopyFrom(buffer + in.GetOffset(), 0, sdlen);
stm->packetReassembler->AddFragment(std::move(pdata), fragmentIndex, fragmentCount, pts, frameSeq, keyframe, rotation);
}
//LOGV("Received video fragment %u of %u", fragmentIndex, fragmentCount);
}
else
{
LOGW("received packet for unknown stream %u", (unsigned int)streamID);
}
if (i < count - 1)
in.Seek(in.GetOffset() + sdlen);
}
}
if (type == PKT_PING)
{
//LOGD("Received ping from %s:%d", srcEndpoint.address.ToString().c_str(), srcEndpoint.port);
if (srcEndpoint.type != Endpoint::Type::UDP_RELAY && srcEndpoint.type != Endpoint::Type::TCP_RELAY && !allowP2p)
{
LOGW("Received p2p ping but p2p is disabled by manual override");
return;
}
BufferOutputStream pkt(128);
pkt.WriteInt32(pseq);
size_t pktLength = pkt.GetLength();
SendOrEnqueuePacket(PendingOutgoingPacket{
/*.seq=*/GenerateOutSeq(),
/*.type=*/PKT_PONG,
/*.len=*/pktLength,
/*.data=*/Buffer(move(pkt)),
/*.endpoint=*/srcEndpoint.id,
});
}
if (type == PKT_PONG)
{
if (packetInnerLen >= 4)
{
uint32_t pingSeq = in.ReadUInt32();
#ifdef LOG_PACKETS
LOGD("Received pong for ping in seq %u", pingSeq);
#endif
if (pingSeq == srcEndpoint.lastPingSeq)
{
srcEndpoint.rtts.Add(GetCurrentTime() - srcEndpoint.lastPingTime);
srcEndpoint.averageRTT = srcEndpoint.rtts.NonZeroAverage();
LOGD("Current RTT via %s: %.3f, average: %.3f", packet.address.ToString().c_str(), srcEndpoint.rtts[0], srcEndpoint.averageRTT);
if (srcEndpoint.averageRTT > rateMaxAcceptableRTT)
needRate = true;
}
}
}
if (type == PKT_STREAM_STATE)
{
unsigned char id = in.ReadByte();
unsigned char enabled = in.ReadByte();
LOGV("Peer stream state: id %u flags %u", (int)id, (int)enabled);
for (vector<shared_ptr<Stream>>::iterator s = incomingStreams.begin(); s != incomingStreams.end(); ++s)
{
if ((*s)->id == id)
{
(*s)->enabled = enabled == 1;
UpdateAudioOutputState();
break;
}
}
}
if (type == PKT_LAN_ENDPOINT)
{
LOGV("received lan endpoint");
uint32_t peerAddr = in.ReadUInt32();
uint16_t peerPort = (uint16_t)in.ReadInt32();
constexpr int64_t lanID = static_cast<int64_t>(FOURCC('L', 'A', 'N', '4')) << 32;
unsigned char peerTag[16];
Endpoint lan(lanID, peerPort, NetworkAddress::IPv4(peerAddr), NetworkAddress::Empty(), Endpoint::Type::UDP_P2P_LAN, peerTag);
if (currentEndpoint == lanID)
currentEndpoint = preferredRelay;
MutexGuard m(endpointsMutex);
endpoints[lanID] = lan;
}
if (type == PKT_NETWORK_CHANGED && _currentEndpoint->type != Endpoint::Type::UDP_RELAY && _currentEndpoint->type != Endpoint::Type::TCP_RELAY)
{
currentEndpoint = preferredRelay;
if (allowP2p)
SendPublicEndpointsRequest();
if (peerVersion >= 2)
{
uint32_t flags = in.ReadUInt32();
dataSavingRequestedByPeer = (flags & INIT_FLAG_DATA_SAVING_ENABLED) == INIT_FLAG_DATA_SAVING_ENABLED;
UpdateDataSavingState();
UpdateAudioBitrateLimit();
ResetEndpointPingStats();
}
}
if (type == PKT_STREAM_EC)
{
unsigned char streamID = in.ReadByte();
if (peerVersion < 7)
{
uint32_t lastTimestamp = in.ReadUInt32();
unsigned char count = in.ReadByte();
for (shared_ptr<Stream> &stm : incomingStreams)
{
if (stm->id == streamID)
{
for (unsigned int i = 0; i < count; i++)
{
unsigned char dlen = in.ReadByte();
unsigned char data[256];
in.ReadBytes(data, dlen);
if (stm->jitterBuffer)
{
stm->jitterBuffer->HandleInput(data, dlen, lastTimestamp - (count - i - 1) * stm->frameDuration, true);
}
}
break;
}
}
}
else
{
shared_ptr<Stream> stm = GetStreamByID(streamID, false);
if (!stm)
{
LOGW("Received FEC packet for unknown stream %u", streamID);
return;
}
if (stm->type != STREAM_TYPE_VIDEO)
{
LOGW("Received FEC packet for non-video stream %u", streamID);
return;
}
if (!stm->packetReassembler)
return;
uint8_t fseq = in.ReadByte();
unsigned char fecScheme = in.ReadByte();
unsigned char prevFrameCount = in.ReadByte();
uint16_t fecLen = in.ReadUInt16();
if (fecLen > in.Remaining())
return;
Buffer fecData(fecLen);
in.ReadBytes(fecData);
stm->packetReassembler->AddFEC(std::move(fecData), fseq, prevFrameCount, fecScheme);
}
}
}
void VoIPController::ProcessExtraData(Buffer &data)
{
BufferInputStream in(*data, data.Length());
unsigned char type = in.ReadByte();
unsigned char fullHash[SHA1_LENGTH];
crypto.sha1(*data, data.Length(), fullHash);
uint64_t hash = *reinterpret_cast<uint64_t *>(fullHash);
if (lastReceivedExtrasByType[type] == hash)
{
return;
}
LOGE("ProcessExtraData");
lastReceivedExtrasByType[type] = hash;
if (type == EXTRA_TYPE_STREAM_FLAGS)
{
unsigned char id = in.ReadByte();
uint32_t flags = static_cast<uint32_t>(in.ReadInt32());
LOGV("Peer stream state: id %u flags %u", (unsigned int)id, (unsigned int)flags);
for (shared_ptr<Stream> &s : incomingStreams)
{
if (s->id == id)
{
bool prevEnabled = s->enabled;
bool prevPaused = s->paused;
s->enabled = (flags & STREAM_FLAG_ENABLED) == STREAM_FLAG_ENABLED;
s->paused = (flags & STREAM_FLAG_PAUSED) == STREAM_FLAG_PAUSED;
if (flags & STREAM_FLAG_EXTRA_EC)
{
if (!s->extraECEnabled)
{
s->extraECEnabled = true;
if (s->jitterBuffer)
s->jitterBuffer->SetMinPacketCount(4);
}
}
else
{
if (s->extraECEnabled)
{
s->extraECEnabled = false;
if (s->jitterBuffer)
s->jitterBuffer->SetMinPacketCount(2);
}
}
if (prevEnabled != s->enabled && s->type == STREAM_TYPE_VIDEO && videoRenderer)
videoRenderer->SetStreamEnabled(s->enabled);
if (prevPaused != s->paused && s->type == STREAM_TYPE_VIDEO && videoRenderer)
videoRenderer->SetStreamPaused(s->paused);
UpdateAudioOutputState();
break;
}
}
}
else if (type == EXTRA_TYPE_STREAM_CSD)
{
LOGI("Received codec specific data");
/*
os.WriteByte(stream.id);
os.WriteByte(static_cast<unsigned char>(stream.codecSpecificData.size()));
for(Buffer& b:stream.codecSpecificData){
assert(b.Length()<255);
os.WriteByte(static_cast<unsigned char>(b.Length()));
os.WriteBytes(b);
}
Buffer buf(move(os));
SendExtra(buf, EXTRA_TYPE_STREAM_CSD);
*/
unsigned char streamID = in.ReadByte();
for (shared_ptr<Stream> &stm : incomingStreams)
{
if (stm->id == streamID)
{
stm->codecSpecificData.clear();
stm->csdIsValid = false;
stm->width = static_cast<unsigned int>(in.ReadInt16());
stm->height = static_cast<unsigned int>(in.ReadInt16());
size_t count = (size_t)in.ReadByte();
for (size_t i = 0; i < count; i++)
{
size_t len = (size_t)in.ReadByte();
Buffer csd(len);
in.ReadBytes(*csd, len);
stm->codecSpecificData.push_back(move(csd));
}
break;
}
}
}
else if (type == EXTRA_TYPE_LAN_ENDPOINT)
{
if (!allowP2p)
return;
LOGV("received lan endpoint (extra)");
uint32_t peerAddr = in.ReadUInt32();
uint16_t peerPort = (uint16_t)in.ReadInt32();
constexpr int64_t lanID = static_cast<int64_t>(FOURCC('L', 'A', 'N', '4')) << 32;
if (currentEndpoint == lanID)
currentEndpoint = preferredRelay;
unsigned char peerTag[16];
Endpoint lan(lanID, peerPort, NetworkAddress::IPv4(peerAddr), NetworkAddress::Empty(), Endpoint::Type::UDP_P2P_LAN, peerTag);
MutexGuard m(endpointsMutex);
endpoints[lanID] = lan;
}
else if (type == EXTRA_TYPE_NETWORK_CHANGED)
{
LOGI("Peer network changed");
wasNetworkHandover = true;
const Endpoint &_currentEndpoint = endpoints.at(currentEndpoint);
if (_currentEndpoint.type != Endpoint::Type::UDP_RELAY && _currentEndpoint.type != Endpoint::Type::TCP_RELAY)
currentEndpoint = preferredRelay;
if (allowP2p)
SendPublicEndpointsRequest();
uint32_t flags = in.ReadUInt32();
dataSavingRequestedByPeer = (flags & INIT_FLAG_DATA_SAVING_ENABLED) == INIT_FLAG_DATA_SAVING_ENABLED;
UpdateDataSavingState();
UpdateAudioBitrateLimit();
ResetEndpointPingStats();
}
else if (type == EXTRA_TYPE_GROUP_CALL_KEY)
{
if (!didReceiveGroupCallKey && !didSendGroupCallKey)
{
unsigned char groupKey[256];
in.ReadBytes(groupKey, 256);
messageThread.Post([this, &groupKey] {
if (callbacks.groupCallKeyReceived)
callbacks.groupCallKeyReceived(this, groupKey);
});
didReceiveGroupCallKey = true;
}
}
else if (type == EXTRA_TYPE_REQUEST_GROUP)
{
if (!didInvokeUpgradeCallback)
{
messageThread.Post([this] {
if (callbacks.upgradeToGroupCallRequested)
callbacks.upgradeToGroupCallRequested(this);
});
didInvokeUpgradeCallback = true;
}
}
else if (type == EXTRA_TYPE_IPV6_ENDPOINT)
{
if (!allowP2p)
return;
unsigned char _addr[16];
in.ReadBytes(_addr, 16);
NetworkAddress addr = NetworkAddress::IPv6(_addr);
uint16_t port = static_cast<uint16_t>(in.ReadInt16());
peerIPv6Available = true;
LOGV("Received peer IPv6 endpoint [%s]:%u", addr.ToString().c_str(), port);
constexpr int64_t p2pID = static_cast<int64_t>(FOURCC('P', '2', 'P', '6')) << 32;
Endpoint ep;
ep.type = Endpoint::Type::UDP_P2P_INET;
ep.port = port;
ep.v6address = addr;
ep.id = p2pID;
endpoints[p2pID] = ep;
if (!myIPv6.IsEmpty())
currentEndpoint = p2pID;
}
}
void VoIPController::ProcessAcknowledgedOutgoingExtra(UnacknowledgedExtraData &extra)
{
if (extra.type == EXTRA_TYPE_GROUP_CALL_KEY)
{
if (!didReceiveGroupCallKeyAck)
{
didReceiveGroupCallKeyAck = true;
messageThread.Post([this] {
if (callbacks.groupCallKeySent)
callbacks.groupCallKeySent(this);
});
}
}
}
Endpoint &VoIPController::GetRemoteEndpoint()
{
return endpoints.at(currentEndpoint);
}
Endpoint *VoIPController::GetEndpointForPacket(const PendingOutgoingPacket &pkt)
{
Endpoint *endpoint = nullptr;
if (pkt.endpoint)
{
try
{
endpoint = &endpoints.at(pkt.endpoint);
}
catch (out_of_range &x)
{
LOGW("Unable to send packet via nonexistent endpoint %" PRIu64, pkt.endpoint);
return NULL;
}
}
if (!endpoint)
endpoint = &endpoints.at(currentEndpoint);
return endpoint;
}
bool VoIPController::SendOrEnqueuePacket(PendingOutgoingPacket pkt, bool enqueue, PacketSender *source)
{
ENFORCE_MSG_THREAD;
Endpoint *endpoint = GetEndpointForPacket(pkt);
if (!endpoint)
{
abort();
return false;
}
bool canSend;
if (endpoint->type != Endpoint::Type::TCP_RELAY)
{
canSend = realUdpSocket->IsReadyToSend();
}
else
{
if (!endpoint->socket)
{
LOGV("Connecting to %s:%u", endpoint->GetAddress().ToString().c_str(), endpoint->port);
if (proxyProtocol == PROXY_NONE)
{
endpoint->socket = make_shared<NetworkSocketTCPObfuscated>(NetworkSocket::Create(NetworkProtocol::TCP));
endpoint->socket->Connect(endpoint->GetAddress(), endpoint->port);
}
else if (proxyProtocol == PROXY_SOCKS5)
{
std::shared_ptr<NetworkSocket> tcp = NetworkSocket::Create(NetworkProtocol::TCP);
tcp->Connect(resolvedProxyAddress, proxyPort);
shared_ptr<NetworkSocketSOCKS5Proxy> proxy = make_shared<NetworkSocketSOCKS5Proxy>(tcp, nullptr, proxyUsername, proxyPassword);
endpoint->socket = proxy;
endpoint->socket->Connect(endpoint->GetAddress(), endpoint->port);
}
selectCanceller->CancelSelect();
}
canSend = endpoint->socket && endpoint->socket->IsReadyToSend();
}
if (!canSend)
{
if (enqueue)
{
LOGW("Not ready to send - enqueueing");
sendQueue.push_back(move(pkt));
}
return false;
}
if ((endpoint->type == Endpoint::Type::TCP_RELAY && useTCP) || (endpoint->type != Endpoint::Type::TCP_RELAY && useUDP))
{
//BufferOutputStream p(buf, sizeof(buf));
BufferOutputStream p(1500);
WritePacketHeader(pkt.seq, &p, pkt.type, (uint32_t)pkt.len, source);
p.WriteBytes(pkt.data);
SendPacket(p.GetBuffer(), p.GetLength(), *endpoint, pkt);
if (pkt.type == PKT_STREAM_DATA)
{
unsentStreamPackets--;
}
}
return true;
}
void VoIPController::SendPacket(unsigned char *data, size_t len, Endpoint &ep, PendingOutgoingPacket &srcPacket)
{
if (stopping)
return;
if (ep.type == Endpoint::Type::TCP_RELAY && !useTCP)
return;
BufferOutputStream out(len + 128);
if (ep.IsReflector())
out.WriteBytes((unsigned char *)ep.peerTag, 16);
else if (peerVersion < 9)
out.WriteBytes(callID, 16);
if (len > 0)
{
if (useMTProto2)
{
BufferOutputStream inner(len + 128);
size_t sizeSize;
if (peerVersion >= 8 || (!peerVersion && connectionMaxLayer >= 92))
{
inner.WriteInt16((uint16_t)len);
sizeSize = 0;
}
else
{
inner.WriteInt32((uint32_t)len);
out.WriteBytes(keyFingerprint, 8);
sizeSize = 4;
}
inner.WriteBytes(data, len);
size_t padLen = 16 - inner.GetLength() % 16;
if (padLen < 16)
padLen += 16;
unsigned char padding[32];
crypto.rand_bytes((uint8_t *)padding, padLen);
inner.WriteBytes(padding, padLen);
assert(inner.GetLength() % 16 == 0);
unsigned char key[32], iv[32], msgKey[16];
BufferOutputStream buf(len + 32);
size_t x = isOutgoing ? 0 : 8;
buf.WriteBytes(encryptionKey + 88 + x, 32);
buf.WriteBytes(inner.GetBuffer() + sizeSize, inner.GetLength() - sizeSize);
unsigned char msgKeyLarge[32];
crypto.sha256(buf.GetBuffer(), buf.GetLength(), msgKeyLarge);
memcpy(msgKey, msgKeyLarge + 8, 16);
KDF2(msgKey, isOutgoing ? 0 : 8, key, iv);
out.WriteBytes(msgKey, 16);
//LOGV("<- MSG KEY: %08x %08x %08x %08x, hashed %u", *reinterpret_cast<int32_t*>(msgKey), *reinterpret_cast<int32_t*>(msgKey+4), *reinterpret_cast<int32_t*>(msgKey+8), *reinterpret_cast<int32_t*>(msgKey+12), inner.GetLength()-4);
unsigned char aesOut[MSC_STACK_FALLBACK(inner.GetLength(), 1500)];
crypto.aes_ige_encrypt(inner.GetBuffer(), aesOut, inner.GetLength(), key, iv);
out.WriteBytes(aesOut, inner.GetLength());
}
else
{
BufferOutputStream inner(len + 128);
inner.WriteInt32(static_cast<int32_t>(len));
inner.WriteBytes(data, len);
if (inner.GetLength() % 16 != 0)
{
size_t padLen = 16 - inner.GetLength() % 16;
unsigned char padding[16];
crypto.rand_bytes((uint8_t *)padding, padLen);
inner.WriteBytes(padding, padLen);
}
assert(inner.GetLength() % 16 == 0);
unsigned char key[32], iv[32], msgHash[SHA1_LENGTH];
crypto.sha1((uint8_t *)inner.GetBuffer(), len + 4, msgHash);
out.WriteBytes(keyFingerprint, 8);
out.WriteBytes((msgHash + (SHA1_LENGTH - 16)), 16);
KDF(msgHash + (SHA1_LENGTH - 16), isOutgoing ? 0 : 8, key, iv);
unsigned char aesOut[MSC_STACK_FALLBACK(inner.GetLength(), 1500)];
crypto.aes_ige_encrypt(inner.GetBuffer(), aesOut, inner.GetLength(), key, iv);
out.WriteBytes(aesOut, inner.GetLength());
}
}
//LOGV("Sending %d bytes to %s:%d", out.GetLength(), ep.address.ToString().c_str(), ep.port);
#ifdef LOG_PACKETS
LOGV("Sending: to=%s:%u, seq=%u, length=%u, type=%s", ep.GetAddress().ToString().c_str(), ep.port, srcPacket.seq, (unsigned int)out.GetLength(), GetPacketTypeString(srcPacket.type).c_str());
#endif
rawSendQueue.Put(
RawPendingOutgoingPacket{
NetworkPacket{
Buffer(std::move(out)),
ep.GetAddress(),
ep.port,
ep.type == Endpoint::Type::TCP_RELAY ? NetworkProtocol::TCP : NetworkProtocol::UDP},
ep.type == Endpoint::Type::TCP_RELAY ? ep.socket : nullptr});
}
void VoIPController::ActuallySendPacket(NetworkPacket pkt, Endpoint &ep)
{
//LOGI("Sending packet of %d bytes", pkt.length);
if (IS_MOBILE_NETWORK(networkType))
stats.bytesSentMobile += (uint64_t)pkt.data.Length();
else
stats.bytesSentWifi += (uint64_t)pkt.data.Length();
if (ep.type == Endpoint::Type::TCP_RELAY)
{
if (ep.socket && !ep.socket->IsFailed())
{
ep.socket->Send(std::move(pkt));
}
}
else
{
udpSocket->Send(std::move(pkt));
}
}
std::string VoIPController::NetworkTypeToString(int type)
{
switch (type)
{
case NET_TYPE_WIFI:
return "wifi";
case NET_TYPE_GPRS:
return "gprs";
case NET_TYPE_EDGE:
return "edge";
case NET_TYPE_3G:
return "3g";
case NET_TYPE_HSPA:
return "hspa";
case NET_TYPE_LTE:
return "lte";
case NET_TYPE_ETHERNET:
return "ethernet";
case NET_TYPE_OTHER_HIGH_SPEED:
return "other_high_speed";
case NET_TYPE_OTHER_LOW_SPEED:
return "other_low_speed";
case NET_TYPE_DIALUP:
return "dialup";
case NET_TYPE_OTHER_MOBILE:
return "other_mobile";
default:
return "unknown";
}
}
std::string VoIPController::GetPacketTypeString(unsigned char type)
{
switch (type)
{
case PKT_INIT:
return "init";
case PKT_INIT_ACK:
return "init_ack";
case PKT_STREAM_STATE:
return "stream_state";
case PKT_STREAM_DATA:
return "stream_data";
case PKT_PING:
return "ping";
case PKT_PONG:
return "pong";
case PKT_LAN_ENDPOINT:
return "lan_endpoint";
case PKT_NETWORK_CHANGED:
return "network_changed";
case PKT_NOP:
return "nop";
case PKT_STREAM_EC:
return "stream_ec";
}
return string("unknown(") + std::to_string(type) + ')';
}
void VoIPController::AddIPv6Relays()
{
if (!myIPv6.IsEmpty() && !didAddIPv6Relays)
{
unordered_map<string, vector<Endpoint>> endpointsByAddress;
for (pair<const int64_t, Endpoint> &_e : endpoints)
{
Endpoint &e = _e.second;
if ((e.IsReflector()) && !e.v6address.IsEmpty() && !e.address.IsEmpty())
{
endpointsByAddress[e.v6address.ToString()].push_back(e);
}
}
MutexGuard m(endpointsMutex);
for (pair<const string, vector<Endpoint>> &addr : endpointsByAddress)
{
for (Endpoint &e : addr.second)
{
didAddIPv6Relays = true;
e.address = NetworkAddress::Empty();
e.id = e.id ^ (static_cast<int64_t>(FOURCC('I', 'P', 'v', '6')) << 32);
e.averageRTT = 0;
e.lastPingSeq = 0;
e.lastPingTime = 0;
e.rtts.Reset();
e.udpPongCount = 0;
endpoints[e.id] = e;
LOGD("Adding IPv6-only endpoint [%s]:%u", e.v6address.ToString().c_str(), e.port);
}
}
}
}
void VoIPController::AddTCPRelays()
{
if (!didAddTcpRelays)
{
bool wasSetCurrentToTCP = setCurrentEndpointToTCP;
LOGV("Adding TCP relays");
vector<Endpoint> relays;
for (pair<const int64_t, Endpoint> &_e : endpoints)
{
Endpoint &e = _e.second;
if (e.type != Endpoint::Type::UDP_RELAY)
continue;
if (wasSetCurrentToTCP && !useUDP)
{
e.rtts.Reset();
e.averageRTT = 0;
e.lastPingSeq = 0;
}
Endpoint tcpRelay(e);
tcpRelay.type = Endpoint::Type::TCP_RELAY;
tcpRelay.averageRTT = 0;
tcpRelay.lastPingSeq = 0;
tcpRelay.lastPingTime = 0;
tcpRelay.rtts.Reset();
tcpRelay.udpPongCount = 0;
tcpRelay.id = tcpRelay.id ^ (static_cast<int64_t>(FOURCC('T', 'C', 'P', 0)) << 32);
if (setCurrentEndpointToTCP && endpoints.at(currentEndpoint).type != Endpoint::Type::TCP_RELAY)
{
LOGV("Setting current endpoint to TCP");
setCurrentEndpointToTCP = false;
currentEndpoint = tcpRelay.id;
preferredRelay = tcpRelay.id;
}
relays.push_back(tcpRelay);
}
MutexGuard m(endpointsMutex);
for (Endpoint &e : relays)
{
endpoints[e.id] = e;
}
didAddTcpRelays = true;
}
}
#if defined(__APPLE__)
static void initMachTimestart()
{
mach_timebase_info_data_t tb = {0, 0};
mach_timebase_info(&tb);
VoIPController::machTimebase = tb.numer;
VoIPController::machTimebase /= tb.denom;
VoIPController::machTimestart = mach_absolute_time();
}
#endif
double VoIPController::GetCurrentTime()
{
#if defined(__linux__)
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec + (double)ts.tv_nsec / 1000000000.0;
#elif defined(__APPLE__)
static pthread_once_t token = PTHREAD_ONCE_INIT;
pthread_once(&token, &initMachTimestart);
return (mach_absolute_time() - machTimestart) * machTimebase / 1000000000.0f;
#elif defined(_WIN32)
if (!didInitWin32TimeScale)
{
LARGE_INTEGER scale;
QueryPerformanceFrequency(&scale);
win32TimeScale = scale.QuadPart;
didInitWin32TimeScale = true;
}
LARGE_INTEGER t;
QueryPerformanceCounter(&t);
return (double)t.QuadPart / (double)win32TimeScale;
#endif
}
void VoIPController::KDF(unsigned char *msgKey, size_t x, unsigned char *aesKey, unsigned char *aesIv)
{
uint8_t sA[SHA1_LENGTH], sB[SHA1_LENGTH], sC[SHA1_LENGTH], sD[SHA1_LENGTH];
BufferOutputStream buf(128);
buf.WriteBytes(msgKey, 16);
buf.WriteBytes(encryptionKey + x, 32);
crypto.sha1(buf.GetBuffer(), buf.GetLength(), sA);
buf.Reset();
buf.WriteBytes(encryptionKey + 32 + x, 16);
buf.WriteBytes(msgKey, 16);
buf.WriteBytes(encryptionKey + 48 + x, 16);
crypto.sha1(buf.GetBuffer(), buf.GetLength(), sB);
buf.Reset();
buf.WriteBytes(encryptionKey + 64 + x, 32);
buf.WriteBytes(msgKey, 16);
crypto.sha1(buf.GetBuffer(), buf.GetLength(), sC);
buf.Reset();
buf.WriteBytes(msgKey, 16);
buf.WriteBytes(encryptionKey + 96 + x, 32);
crypto.sha1(buf.GetBuffer(), buf.GetLength(), sD);
buf.Reset();
buf.WriteBytes(sA, 8);
buf.WriteBytes(sB + 8, 12);
buf.WriteBytes(sC + 4, 12);
assert(buf.GetLength() == 32);
memcpy(aesKey, buf.GetBuffer(), 32);
buf.Reset();
buf.WriteBytes(sA + 8, 12);
buf.WriteBytes(sB, 8);
buf.WriteBytes(sC + 16, 4);
buf.WriteBytes(sD, 8);
assert(buf.GetLength() == 32);
memcpy(aesIv, buf.GetBuffer(), 32);
}
void VoIPController::KDF2(unsigned char *msgKey, size_t x, unsigned char *aesKey, unsigned char *aesIv)
{
uint8_t sA[32], sB[32];
BufferOutputStream buf(128);
buf.WriteBytes(msgKey, 16);
buf.WriteBytes(encryptionKey + x, 36);
crypto.sha256(buf.GetBuffer(), buf.GetLength(), sA);
buf.Reset();
buf.WriteBytes(encryptionKey + 40 + x, 36);
buf.WriteBytes(msgKey, 16);
crypto.sha256(buf.GetBuffer(), buf.GetLength(), sB);
buf.Reset();
buf.WriteBytes(sA, 8);
buf.WriteBytes(sB + 8, 16);
buf.WriteBytes(sA + 24, 8);
memcpy(aesKey, buf.GetBuffer(), 32);
buf.Reset();
buf.WriteBytes(sB, 8);
buf.WriteBytes(sA + 8, 16);
buf.WriteBytes(sB + 24, 8);
memcpy(aesIv, buf.GetBuffer(), 32);
}
void VoIPController::SendPublicEndpointsRequest(const Endpoint &relay)
{
if (!useUDP)
return;
LOGD("Sending public endpoints request to %s:%d", relay.address.ToString().c_str(), relay.port);
publicEndpointsReqTime = GetCurrentTime();
waitingForRelayPeerInfo = true;
Buffer buf(32);
memcpy(*buf, relay.peerTag, 16);
memset(*buf + 16, 0xFF, 16);
udpSocket->Send(NetworkPacket{
std::move(buf),
relay.address,
relay.port,
NetworkProtocol::UDP});
}
Endpoint &VoIPController::GetEndpointByType(const Endpoint::Type type)
{
if (type == Endpoint::Type::UDP_RELAY && preferredRelay)
return endpoints.at(preferredRelay);
for (pair<const int64_t, Endpoint> &e : endpoints)
{
if (e.second.type == type)
return e.second;
}
throw out_of_range("no endpoint");
}
void VoIPController::SendPacketReliably(unsigned char type, unsigned char *data, size_t len, double retryInterval, double timeout)
{
ENFORCE_MSG_THREAD;
LOGD("Send reliably, type=%u, len=%u, retry=%.3f, timeout=%.3f", type, unsigned(len), retryInterval, timeout);
QueuedPacket pkt;
if (data)
{
Buffer b(len);
b.CopyFrom(data, 0, len);
pkt.data = move(b);
}
pkt.type = type;
pkt.retryInterval = retryInterval;
pkt.timeout = timeout;
pkt.firstSentTime = 0;
pkt.lastSentTime = 0;
queuedPackets.push_back(move(pkt));
messageThread.Post(std::bind(&VoIPController::UpdateQueuedPackets, this));
if (timeout > 0.0)
{
messageThread.Post(std::bind(&VoIPController::UpdateQueuedPackets, this), timeout);
}
}
void VoIPController::SendExtra(Buffer &data, unsigned char type)
{
ENFORCE_MSG_THREAD;
LOGV("Sending extra type %u length %u", type, (unsigned int)data.Length());
for (vector<UnacknowledgedExtraData>::iterator x = currentExtras.begin(); x != currentExtras.end(); ++x)
{
if (x->type == type)
{
x->firstContainingSeq = 0;
x->data = move(data);
return;
}
}
UnacknowledgedExtraData xd = {type, move(data), 0};
currentExtras.push_back(move(xd));
}
void VoIPController::DebugCtl(int request, int param)
{
}
void VoIPController::SendUdpPing(Endpoint &endpoint)
{
if (endpoint.type != Endpoint::Type::UDP_RELAY)
return;
BufferOutputStream p(1024);
p.WriteBytes(endpoint.peerTag, 16);
p.WriteInt32(-1);
p.WriteInt32(-1);
p.WriteInt32(-1);
p.WriteInt32(-2);
int64_t id;
crypto.rand_bytes(reinterpret_cast<uint8_t *>(&id), 8);
p.WriteInt64(id);
endpoint.udpPingTimes[id] = GetCurrentTime();
udpSocket->Send(NetworkPacket{
Buffer(std::move(p)),
endpoint.GetAddress(),
endpoint.port,
NetworkProtocol::UDP});
endpoint.totalUdpPings++;
LOGV("Sending UDP ping to %s:%d, id %" PRId64, endpoint.GetAddress().ToString().c_str(), endpoint.port, id);
}
void VoIPController::ResetUdpAvailability()
{
ENFORCE_MSG_THREAD;
LOGI("Resetting UDP availability");
if (udpPingTimeoutID != MessageThread::INVALID_ID)
{
messageThread.Cancel(udpPingTimeoutID);
}
{
for (pair<const int64_t, Endpoint> &e : endpoints)
{
e.second.udpPongCount = 0;
e.second.udpPingTimes.clear();
}
}
udpPingCount = 0;
udpConnectivityState = UDP_PING_PENDING;
udpPingTimeoutID = messageThread.Post(std::bind(&VoIPController::SendUdpPings, this), 0.0, 0.5);
}
void VoIPController::ResetEndpointPingStats()
{
ENFORCE_MSG_THREAD;
for (pair<const int64_t, Endpoint> &e : endpoints)
{
e.second.averageRTT = 0.0;
e.second.rtts.Reset();
}
}
#pragma mark - Video
void VoIPController::SetVideoSource(video::VideoSource *source)
{
shared_ptr<Stream> stm = GetStreamByType(STREAM_TYPE_VIDEO, true);
if (!stm)
{
LOGE("Can't set video source when there is no outgoing video stream");
return;
}
if (source)
{
if (!stm->enabled)
{
stm->enabled = true;
messageThread.Post([this, stm] { SendStreamFlags(*stm); });
}
if (!videoPacketSender)
videoPacketSender.reset(new video::VideoPacketSender(this, source, stm));
else
videoPacketSender->SetSource(source);
}
else
{
if (stm->enabled)
{
stm->enabled = false;
messageThread.Post([this, stm] { SendStreamFlags(*stm); });
}
if (videoPacketSender)
{
videoPacketSender->SetSource(NULL);
}
}
}
void VoIPController::SetVideoRenderer(video::VideoRenderer *renderer)
{
videoRenderer = renderer;
}
void VoIPController::SetVideoCodecSpecificData(const std::vector<Buffer> &data)
{
outgoingStreams[1]->codecSpecificData.clear();
for (const Buffer &csd : data)
{
outgoingStreams[1]->codecSpecificData.push_back(Buffer::CopyOf(csd));
}
LOGI("Set outgoing video stream CSD");
}
void VoIPController::SendVideoFrame(const Buffer &frame, uint32_t flags, uint32_t rotation)
{
//LOGI("Send video frame %u flags %u", (unsigned int)frame.Length(), flags);
shared_ptr<Stream> stm = GetStreamByType(STREAM_TYPE_VIDEO, true);
if (stm)
{
}
}
void VoIPController::ProcessIncomingVideoFrame(Buffer frame, uint32_t pts, bool keyframe, uint16_t rotation)
{
//LOGI("Incoming video frame size %u pts %u", (unsigned int)frame.Length(), pts);
if (frame.Length() == 0)
{
LOGE("EMPTY FRAME");
}
if (videoRenderer)
{
shared_ptr<Stream> stm = GetStreamByType(STREAM_TYPE_VIDEO, false);
size_t offset = 0;
if (keyframe)
{
BufferInputStream in(frame);
uint16_t width = in.ReadUInt16();
uint16_t height = in.ReadUInt16();
uint8_t sizeAndFlag = in.ReadByte();
int size = sizeAndFlag & 0x0F;
bool reset = (sizeAndFlag & 0x80) == 0x80;
if (reset || !stm->csdIsValid || stm->width != width || stm->height != height)
{
stm->width = width;
stm->height = height;
stm->codecSpecificData.clear();
for (int i = 0; i < size; i++)
{
size_t len = in.ReadByte();
Buffer b(len);
in.ReadBytes(b);
stm->codecSpecificData.push_back(move(b));
}
stm->csdIsValid = false;
}
else
{
for (int i = 0; i < size; i++)
{
size_t len = in.ReadByte();
in.Seek(in.GetOffset() + len);
}
}
offset = in.GetOffset();
}
if (!stm->csdIsValid && stm->width && stm->height)
{
videoRenderer->Reset(stm->codec, stm->width, stm->height, stm->codecSpecificData);
stm->csdIsValid = true;
}
if (lastReceivedVideoFrameNumber == UINT32_MAX || lastReceivedVideoFrameNumber == pts - 1 || keyframe)
{
lastReceivedVideoFrameNumber = pts;
//LOGV("3 before decode %u", (unsigned int)frame.Length());
if (stm->rotation != rotation)
{
stm->rotation = rotation;
videoRenderer->SetRotation(rotation);
}
if (offset == 0)
{
videoRenderer->DecodeAndDisplay(move(frame), pts);
}
else
{
videoRenderer->DecodeAndDisplay(Buffer::CopyOf(frame, offset, frame.Length() - offset), pts);
}
}
else
{
LOGW("Skipping non-keyframe after packet loss...");
}
}
}
void VoIPController::SetupOutgoingVideoStream()
{
vector<uint32_t> myEncoders = video::VideoSource::GetAvailableEncoders();
shared_ptr<Stream> vstm = make_shared<Stream>();
vstm->id = 2;
vstm->type = STREAM_TYPE_VIDEO;
if (find(myEncoders.begin(), myEncoders.end(), CODEC_HEVC) != myEncoders.end() && find(peerVideoDecoders.begin(), peerVideoDecoders.end(), CODEC_HEVC) != peerVideoDecoders.end())
{
vstm->codec = CODEC_HEVC;
}
else if (find(myEncoders.begin(), myEncoders.end(), CODEC_AVC) != myEncoders.end() && find(peerVideoDecoders.begin(), peerVideoDecoders.end(), CODEC_AVC) != peerVideoDecoders.end())
{
vstm->codec = CODEC_AVC;
}
else if (find(myEncoders.begin(), myEncoders.end(), CODEC_VP8) != myEncoders.end() && find(peerVideoDecoders.begin(), peerVideoDecoders.end(), CODEC_VP8) != peerVideoDecoders.end())
{
vstm->codec = CODEC_VP8;
}
else
{
LOGW("Can't setup outgoing video stream: no codecs in common");
return;
}
vstm->enabled = false;
outgoingStreams.push_back(vstm);
}
#pragma mark - Timer methods
void VoIPController::SendUdpPings()
{
LOGW("Send udp pings");
ENFORCE_MSG_THREAD;
for (pair<const int64_t, Endpoint> &e : endpoints)
{
if (e.second.type == Endpoint::Type::UDP_RELAY)
{
SendUdpPing(e.second);
}
}
if (udpConnectivityState == UDP_UNKNOWN || udpConnectivityState == UDP_PING_PENDING)
udpConnectivityState = UDP_PING_SENT;
udpPingCount++;
if (udpPingCount == 4 || udpPingCount == 10)
{
messageThread.CancelSelf();
udpPingTimeoutID = messageThread.Post(std::bind(&VoIPController::EvaluateUdpPingResults, this), 1.0);
}
}
void VoIPController::EvaluateUdpPingResults()
{
double avgPongs = 0;
int count = 0;
for (pair<const int64_t, Endpoint> &_e : endpoints)
{
Endpoint &e = _e.second;
if (e.type == Endpoint::Type::UDP_RELAY)
{
if (e.udpPongCount > 0)
{
avgPongs += (double)e.udpPongCount;
count++;
}
}
}
if (count > 0)
avgPongs /= (double)count;
else
avgPongs = 0.0;
LOGI("UDP ping reply count: %.2f", avgPongs);
if (avgPongs == 0.0 && proxyProtocol == PROXY_SOCKS5 && udpSocket != realUdpSocket)
{
LOGI("Proxy does not let UDP through, closing proxy connection and using UDP directly");
std::shared_ptr<NetworkSocket> proxySocket = udpSocket;
proxySocket->Close();
udpSocket = realUdpSocket;
selectCanceller->CancelSelect();
proxySupportsUDP = false;
ResetUdpAvailability();
return;
}
bool configUseTCP = ServerConfig::GetSharedInstance()->GetBoolean("use_tcp", true);
if (configUseTCP)
{
if (avgPongs == 0.0 || (udpConnectivityState == UDP_BAD && avgPongs < 7.0))
{
if (needRateFlags & NEED_RATE_FLAG_UDP_NA)
needRate = true;
udpConnectivityState = UDP_NOT_AVAILABLE;
useTCP = true;
useUDP = avgPongs > 1.0;
if (endpoints.at(currentEndpoint).type != Endpoint::Type::TCP_RELAY)
setCurrentEndpointToTCP = true;
AddTCPRelays();
waitingForRelayPeerInfo = false;
}
else if (avgPongs < 3.0)
{
if (needRateFlags & NEED_RATE_FLAG_UDP_BAD)
needRate = true;
udpConnectivityState = UDP_BAD;
useTCP = true;
setCurrentEndpointToTCP = true;
AddTCPRelays();
udpPingTimeoutID = messageThread.Post(std::bind(&VoIPController::SendUdpPings, this), 0.5, 0.5);
}
else
{
udpPingTimeoutID = MessageThread::INVALID_ID;
udpConnectivityState = UDP_AVAILABLE;
}
}
else
{
udpPingTimeoutID = MessageThread::INVALID_ID;
udpConnectivityState = UDP_NOT_AVAILABLE;
}
}
void VoIPController::SendRelayPings()
{
ENFORCE_MSG_THREAD;
if ((state == STATE_ESTABLISHED || state == STATE_RECONNECTING) && endpoints.size() > 1)
{
Endpoint *_preferredRelay = &endpoints.at(preferredRelay);
Endpoint *_currentEndpoint = &endpoints.at(currentEndpoint);
Endpoint *minPingRelay = _preferredRelay;
double minPing = _preferredRelay->averageRTT * (_preferredRelay->type == Endpoint::Type::TCP_RELAY ? 2 : 1);
if (minPing == 0.0) // force the switch to an available relay, if any
minPing = DBL_MAX;
for (pair<const int64_t, Endpoint> &_endpoint : endpoints)
{
Endpoint &endpoint = _endpoint.second;
if (endpoint.type == Endpoint::Type::TCP_RELAY && !useTCP)
continue;
if (endpoint.type == Endpoint::Type::UDP_RELAY && !useUDP)
continue;
if (GetCurrentTime() - endpoint.lastPingTime >= 10)
{
LOGV("Sending ping to %s", endpoint.GetAddress().ToString().c_str());
SendOrEnqueuePacket(PendingOutgoingPacket{
/*.seq=*/(endpoint.lastPingSeq = GenerateOutSeq()),
/*.type=*/PKT_PING,
/*.len=*/0,
/*.data=*/Buffer(),
/*.endpoint=*/endpoint.id});
endpoint.lastPingTime = GetCurrentTime();
}
if ((useUDP && endpoint.type == Endpoint::Type::UDP_RELAY) || (useTCP && endpoint.type == Endpoint::Type::TCP_RELAY))
{
double k = endpoint.type == Endpoint::Type::UDP_RELAY ? 1 : 2;
if (endpoint.averageRTT > 0 && endpoint.averageRTT * k < minPing * relaySwitchThreshold)
{
minPing = endpoint.averageRTT * k;
minPingRelay = &endpoint;
}
}
}
if (minPingRelay->id != preferredRelay)
{
preferredRelay = minPingRelay->id;
_preferredRelay = minPingRelay;
LOGV("set preferred relay to %s", _preferredRelay->address.ToString().c_str());
if (_currentEndpoint->IsReflector())
{
currentEndpoint = preferredRelay;
_currentEndpoint = _preferredRelay;
}
}
if (_currentEndpoint->type == Endpoint::Type::UDP_RELAY && useUDP)
{
constexpr int64_t p2pID = static_cast<int64_t>(FOURCC('P', '2', 'P', '4')) << 32;
constexpr int64_t lanID = static_cast<int64_t>(FOURCC('L', 'A', 'N', '4')) << 32;
if (endpoints.find(p2pID) != endpoints.end())
{
Endpoint &p2p = endpoints[p2pID];
if (endpoints.find(lanID) != endpoints.end() && endpoints[lanID].averageRTT > 0 && endpoints[lanID].averageRTT < minPing * relayToP2pSwitchThreshold)
{
currentEndpoint = lanID;
LOGI("Switching to p2p (LAN)");
}
else
{
if (p2p.averageRTT > 0 && p2p.averageRTT < minPing * relayToP2pSwitchThreshold)
{
currentEndpoint = p2pID;
LOGI("Switching to p2p (Inet)");
}
}
}
}
else
{
if (minPing > 0 && minPing < _currentEndpoint->averageRTT * p2pToRelaySwitchThreshold)
{
LOGI("Switching to relay");
currentEndpoint = preferredRelay;
}
}
}
}
void VoIPController::UpdateRTT()
{
rttHistory.Add(GetAverageRTT());
//double v=rttHistory.Average();
if (rttHistory[0] > 10.0 && rttHistory[8] > 10.0 && (networkType == NET_TYPE_EDGE || networkType == NET_TYPE_GPRS))
{
waitingForAcks = true;
}
else
{
waitingForAcks = false;
}
//LOGI("%.3lf/%.3lf, rtt diff %.3lf, waiting=%d, queue=%d", rttHistory[0], rttHistory[8], v, waitingForAcks, sendQueue->Size());
for (vector<shared_ptr<Stream>>::iterator stm = incomingStreams.begin(); stm != incomingStreams.end(); ++stm)
{
if ((*stm)->jitterBuffer)
{
int lostCount = (*stm)->jitterBuffer->GetAndResetLostPacketCount();
if (lostCount > 0 || (lostCount < 0 && recvLossCount > ((uint32_t)-lostCount)))
recvLossCount += lostCount;
}
}
}
void VoIPController::UpdateCongestion()
{
if (encoder)
{
uint32_t sendLossCount = conctl.GetSendLossCount();
sendLossCountHistory.Add(sendLossCount - prevSendLossCount);
prevSendLossCount = sendLossCount;
double packetsPerSec = 1000 / (double)outgoingStreams[0]->frameDuration;
double avgSendLossCount = sendLossCountHistory.Average() / packetsPerSec;
//LOGV("avg send loss: %.3f%%", avgSendLossCount*100);
if (avgSendLossCount > packetLossToEnableExtraEC && networkType != NET_TYPE_GPRS && networkType != NET_TYPE_EDGE)
{
if (!shittyInternetMode)
{
// Shitty Internet Mode™. Redundant redundancy you can trust.
shittyInternetMode = true;
for (shared_ptr<Stream> &s : outgoingStreams)
{
if (s->type == STREAM_TYPE_AUDIO)
{
s->extraECEnabled = true;
SendStreamFlags(*s);
break;
}
}
if (encoder)
encoder->SetSecondaryEncoderEnabled(true);
LOGW("Enabling extra EC");
if (needRateFlags & NEED_RATE_FLAG_SHITTY_INTERNET_MODE)
needRate = true;
wasExtraEC = true;
}
}
if (avgSendLossCount > 0.08)
{
extraEcLevel = 4;
}
else if (avgSendLossCount > 0.05)
{
extraEcLevel = 3;
}
else if (avgSendLossCount > 0.02)
{
extraEcLevel = 2;
}
else
{
extraEcLevel = 0;
}
encoder->SetPacketLoss((int)(avgSendLossCount * 100.0));
if (avgSendLossCount > rateMaxAcceptableSendLoss)
needRate = true;
if ((avgSendLossCount < packetLossToEnableExtraEC || networkType == NET_TYPE_EDGE || networkType == NET_TYPE_GPRS) && shittyInternetMode)
{
shittyInternetMode = false;
for (shared_ptr<Stream> &s : outgoingStreams)
{
if (s->type == STREAM_TYPE_AUDIO)
{
s->extraECEnabled = false;
SendStreamFlags(*s);
break;
}
}
if (encoder)
encoder->SetSecondaryEncoderEnabled(false);
LOGW("Disabling extra EC");
}
if (!wasEncoderLaggy && encoder->GetComplexity() < 10)
wasEncoderLaggy = true;
}
}
void VoIPController::UpdateAudioBitrate()
{
if (encoder)
{
double time = GetCurrentTime();
if ((audioInput && !audioInput->IsInitialized()) || (audioOutput && !audioOutput->IsInitialized()))
{
LOGE("Audio I/O failed");
lastError = ERROR_AUDIO_IO;
SetState(STATE_FAILED);
}
int act = conctl.GetBandwidthControlAction();
if (shittyInternetMode)
{
encoder->SetBitrate(8000);
}
else if (act == TGVOIP_CONCTL_ACT_DECREASE)
{
uint32_t bitrate = encoder->GetBitrate();
if (bitrate > 8000)
encoder->SetBitrate(bitrate < (minAudioBitrate + audioBitrateStepDecr) ? minAudioBitrate : (bitrate - audioBitrateStepDecr));
}
else if (act == TGVOIP_CONCTL_ACT_INCREASE)
{
uint32_t bitrate = encoder->GetBitrate();
if (bitrate < maxBitrate)
encoder->SetBitrate(bitrate + audioBitrateStepIncr);
}
if (state == STATE_ESTABLISHED && time - lastRecvPacketTime >= reconnectingTimeout)
{
SetState(STATE_RECONNECTING);
if (needRateFlags & NEED_RATE_FLAG_RECONNECTING)
needRate = true;
wasReconnecting = true;
ResetUdpAvailability();
}
if (state == STATE_ESTABLISHED || state == STATE_RECONNECTING)
{
if (time - lastRecvPacketTime >= config.recvTimeout)
{
const Endpoint &_currentEndpoint = endpoints.at(currentEndpoint);
if (_currentEndpoint.type != Endpoint::Type::UDP_RELAY && _currentEndpoint.type != Endpoint::Type::TCP_RELAY)
{
LOGW("Packet receive timeout, switching to relay");
currentEndpoint = preferredRelay;
for (pair<const int64_t, Endpoint> &_e : endpoints)
{
Endpoint &e = _e.second;
if (e.IsP2P())
{
e.averageRTT = 0;
e.rtts.Reset();
}
}
if (allowP2p)
{
SendPublicEndpointsRequest();
}
UpdateDataSavingState();
UpdateAudioBitrateLimit();
BufferOutputStream s(4);
s.WriteInt32(dataSavingMode ? INIT_FLAG_DATA_SAVING_ENABLED : 0);
if (peerVersion < 6)
{
SendPacketReliably(PKT_NETWORK_CHANGED, s.GetBuffer(), s.GetLength(), 1, 20);
}
else
{
Buffer buf(move(s));
SendExtra(buf, EXTRA_TYPE_NETWORK_CHANGED);
}
lastRecvPacketTime = time;
}
else
{
LOGW("Packet receive timeout, disconnecting");
lastError = ERROR_TIMEOUT;
SetState(STATE_FAILED);
}
}
}
}
}
void VoIPController::UpdateSignalBars()
{
int prevSignalBarCount = GetSignalBarsCount();
double packetsPerSec = 1000 / (double)outgoingStreams[0]->frameDuration;
double avgSendLossCount = sendLossCountHistory.Average() / packetsPerSec;
int signalBarCount = 4;
if (state == STATE_RECONNECTING || waitingForAcks)
signalBarCount = 1;
if (endpoints.at(currentEndpoint).type == Endpoint::Type::TCP_RELAY)
{
signalBarCount = std::min(signalBarCount, 3);
}
if (avgSendLossCount > 0.1)
{
signalBarCount = 1;
}
else if (avgSendLossCount > 0.0625)
{
signalBarCount = std::min(signalBarCount, 2);
}
else if (avgSendLossCount > 0.025)
{
signalBarCount = std::min(signalBarCount, 3);
}
for (shared_ptr<Stream> &stm : incomingStreams)
{
if (stm->jitterBuffer)
{
double avgLateCount[3];
stm->jitterBuffer->GetAverageLateCount(avgLateCount);
if (avgLateCount[2] >= 0.2)
signalBarCount = 1;
else if (avgLateCount[2] >= 0.1)
signalBarCount = std::min(signalBarCount, 2);
}
}
signalBarsHistory.Add(static_cast<unsigned char>(signalBarCount));
//LOGV("Signal bar count history %08X", *reinterpret_cast<uint32_t *>(&signalBarsHistory));
int _signalBarCount = GetSignalBarsCount();
if (_signalBarCount != prevSignalBarCount)
{
LOGD("SIGNAL BAR COUNT CHANGED: %d", _signalBarCount);
if (callbacks.signalBarCountChanged)
callbacks.signalBarCountChanged(this, _signalBarCount);
}
}
void VoIPController::UpdateQueuedPackets()
{
vector<PendingOutgoingPacket> packetsToSend;
for (std::vector<QueuedPacket>::iterator qp = queuedPackets.begin(); qp != queuedPackets.end();)
{
if (qp->timeout > 0 && qp->firstSentTime > 0 && GetCurrentTime() - qp->firstSentTime >= qp->timeout)
{
LOGD("Removing queued packet because of timeout");
qp = queuedPackets.erase(qp);
continue;
}
if (GetCurrentTime() - qp->lastSentTime >= qp->retryInterval)
{
messageThread.Post(std::bind(&VoIPController::UpdateQueuedPackets, this), qp->retryInterval);
uint32_t seq = GenerateOutSeq();
qp->seqs.Add(seq);
qp->lastSentTime = GetCurrentTime();
//LOGD("Sending queued packet, seq=%u, type=%u, len=%u", seq, qp.type, qp.data.Length());
Buffer buf(qp->data.Length());
if (qp->firstSentTime == 0)
qp->firstSentTime = qp->lastSentTime;
if (qp->data.Length())
buf.CopyFrom(qp->data, qp->data.Length());
packetsToSend.push_back(PendingOutgoingPacket{
/*.seq=*/seq,
/*.type=*/qp->type,
/*.len=*/qp->data.Length(),
/*.data=*/move(buf),
/*.endpoint=*/0});
}
++qp;
}
for (PendingOutgoingPacket &pkt : packetsToSend)
{
SendOrEnqueuePacket(move(pkt));
}
}
void VoIPController::SendNopPacket()
{
if (state != STATE_ESTABLISHED)
return;
SendOrEnqueuePacket(PendingOutgoingPacket{
/*.seq=*/(firstSentPing = GenerateOutSeq()),
/*.type=*/PKT_NOP,
/*.len=*/0,
/*.data=*/Buffer(),
/*.endpoint=*/0});
}
void VoIPController::SendPublicEndpointsRequest()
{
ENFORCE_MSG_THREAD;
if (!allowP2p)
return;
LOGI("Sending public endpoints request");
for (pair<const int64_t, Endpoint> &e : endpoints)
{
if (e.second.type == Endpoint::Type::UDP_RELAY && !e.second.IsIPv6Only())
{
SendPublicEndpointsRequest(e.second);
}
}
publicEndpointsReqCount++;
if (publicEndpointsReqCount < 10)
{
messageThread.Post(
[this] {
if (waitingForRelayPeerInfo)
{
LOGW("Resending peer relay info request");
SendPublicEndpointsRequest();
}
},
5.0);
}
else
{
publicEndpointsReqCount = 0;
}
}
void VoIPController::TickJitterBufferAndCongestionControl()
{
// TODO get rid of this and update states of these things internally and retroactively
for (shared_ptr<Stream> &stm : incomingStreams)
{
if (stm->jitterBuffer)
{
stm->jitterBuffer->Tick();
}
}
conctl.Tick();
//MutexGuard m(queuedPacketsMutex);
double currentTime = GetCurrentTime();
double rtt = GetAverageRTT();
double packetLossTimeout = std::max(rtt * 2.0, 0.1);
for (RecentOutgoingPacket &pkt : recentOutgoingPackets)
{
if (pkt.ackTime != 0.0 || pkt.lost)
continue;
if (currentTime - pkt.sendTime > packetLossTimeout)
{
pkt.lost = true;
sendLosses++;
LOGW("Outgoing packet lost: seq=%u, type=%s, size=%u", pkt.seq, GetPacketTypeString(pkt.type).c_str(), (unsigned int)pkt.size);
if (pkt.sender)
{
pkt.sender->PacketLost(pkt.seq, pkt.type, pkt.size);
}
else if (pkt.type == PKT_STREAM_DATA)
{
conctl.PacketLost(pkt.seq);
}
}
}
}