#include #include #include #include #include "VoIPController.h" #include "logging.h" #include "threading.h" #include "BufferOutputStream.h" #include "BufferInputStream.h" #include "OpusEncoder.h" #include "OpusDecoder.h" #include #include #include #include #include #include #include #include #include #include #ifdef __APPLE__ #include double CVoIPController::machTimebase=0; uint64_t CVoIPController::machTimestart=0; #endif #define SHA1_LENGTH 20 #define SHA256_LENGTH 32 #ifndef USE_CUSTOM_CRYPTO #include #include #include void tgvoip_openssl_aes_ige_encrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){ AES_KEY akey; AES_set_encrypt_key(key, 32*8, &akey); AES_ige_encrypt(in, out, length, &akey, iv, AES_ENCRYPT); } void tgvoip_openssl_aes_ige_decrypt(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv){ AES_KEY akey; AES_set_decrypt_key(key, 32*8, &akey); AES_ige_encrypt(in, out, length, &akey, iv, AES_DECRYPT); } void tgvoip_openssl_rand_bytes(uint8_t* buffer, size_t len){ RAND_bytes(buffer, len); } void tgvoip_openssl_sha1(uint8_t* msg, size_t len, uint8_t* output){ SHA1(msg, len, output); } void tgvoip_openssl_sha256(uint8_t* msg, size_t len, uint8_t* output){ SHA256(msg, len, output); } voip_crypto_functions_t CVoIPController::crypto={ tgvoip_openssl_rand_bytes, tgvoip_openssl_sha1, tgvoip_openssl_sha256, tgvoip_openssl_aes_ige_encrypt, tgvoip_openssl_aes_ige_decrypt }; #else voip_crypto_functions_t CVoIPController::crypto; // set it yourself upon initialization #endif CVoIPController::CVoIPController(){ seq=1; lastRemoteSeq=0; state=STATE_WAIT_INIT; audioInput=NULL; audioOutput=NULL; decoder=NULL; encoder=NULL; jitterBuffer=NULL; audioOutStarted=false; audioTimestampIn=0; audioTimestampOut=0; stopping=false; int i; for(i=0;i<20;i++){ emptySendBuffers.push_back(new CBufferOutputStream(1024)); } sendQueue=new CBlockingQueue(20); init_mutex(sendBufferMutex); memset(remoteAcks, 0, sizeof(double)*32); memset(sentPacketTimes, 0, sizeof(double)*32); memset(recvPacketTimes, 0, sizeof(double)*32); memset(rttHistory, 0, sizeof(double)*32); memset(sendLossCountHistory, 0, sizeof(uint32_t)*32); memset(&stats, 0, sizeof(voip_stats_t)); lastRemoteAckSeq=0; lastSentSeq=0; recvLossCount=0; packetsRecieved=0; waitingForAcks=false; networkType=NET_TYPE_UNKNOWN; audioPacketGrouping=3; audioPacketsWritten=0; currentAudioPacket=NULL; stateCallback=NULL; echoCanceller=NULL; dontSendPackets=0; micMuted=false; currentEndpoint=NULL; needSendP2pPing=false; waitingForRelayPeerInfo=false; lastP2pPingTime=0; p2pPingCount=0; allowP2p=true; dataSavingMode=false; memset(activeNetItfName, 0, 32); publicEndpointsReqTime=0; init_mutex(queuedPacketsMutex); connectionInitTime=0; lastRecvPacketTime=0; dataSavingRequestedByPeer=false; peerVersion=0; conctl=new CCongestionControl(); prevSendLossCount=0; enableAEC=true; #ifdef __APPLE__ machTimestart=0; #endif voip_stream_t* stm=(voip_stream_t *) malloc(sizeof(voip_stream_t)); stm->id=1; stm->type=STREAM_TYPE_AUDIO; stm->codec=CODEC_OPUS; stm->enabled=1; stm->frameDuration=60; outgoingStreams.push_back(stm); } CVoIPController::~CVoIPController(){ LOGD("Entered CVoIPController::~CVoIPController"); if(audioInput) audioInput->Stop(); if(audioOutput) audioOutput->Stop(); stopping=true; runReceiver=false; LOGD("before shutdown socket"); shutdown(udpSocket, SHUT_RDWR); sendQueue->Put(NULL); close(udpSocket); LOGD("before join sendThread"); join_thread(sendThread); LOGD("before join recvThread"); join_thread(recvThread); LOGD("before join tickThread"); join_thread(tickThread); free_mutex(sendBufferMutex); LOGD("before close socket"); LOGD("before free send buffers"); while(emptySendBuffers.size()>0){ delete emptySendBuffers[emptySendBuffers.size()-1]; emptySendBuffers.pop_back(); } while(sendQueue->Size()>0){ void* p=sendQueue->Get(); if(p) delete (CBufferOutputStream*)p; } LOGD("before delete jitter buffer"); if(jitterBuffer){ delete jitterBuffer; } LOGD("before stop decoder"); if(decoder){ decoder->Stop(); } LOGD("before delete audio input"); if(audioInput){ delete audioInput; } LOGD("before delete encoder"); if(encoder){ encoder->Stop(); delete encoder; } LOGD("before delete audio output"); if(audioOutput){ delete audioOutput; } LOGD("before delete decoder"); if(decoder){ delete decoder; } LOGD("before delete echo canceller"); if(echoCanceller){ echoCanceller->Stop(); delete echoCanceller; } delete sendQueue; int i; for(i=0;idata) free(queuedPackets[i]->data); free(queuedPackets[i]); } delete conctl; LOGD("Left CVoIPController::~CVoIPController"); } void CVoIPController::SetRemoteEndpoints(voip_endpoint_t* endpoints, size_t count, bool allowP2p){ LOGW("Set remote endpoints"); assert(count>0); preferredRelay=NULL; size_t i; for(i=0;i_averageRtt=0; ep->_lastPingTime=0; memset(ep->_rtts, 0, sizeof(double)*6); this->endpoints.push_back(ep); if(ep->type==EP_TYPE_UDP_RELAY && !preferredRelay) preferredRelay=ep; } currentEndpoint=this->endpoints[0]; this->allowP2p=allowP2p; } void* CVoIPController::StartRecvThread(void* controller){ ((CVoIPController*)controller)->RunRecvThread(); return NULL; } void* CVoIPController::StartSendThread(void* controller){ ((CVoIPController*)controller)->RunSendThread(); return NULL; } void* CVoIPController::StartTickThread(void* controller){ ((CVoIPController*) controller)->RunTickThread(); return NULL; } void CVoIPController::Start(){ int res; LOGW("Starting voip controller"); udpSocket=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(udpSocket<0){ LOGE("error creating socket: %d / %s", errno, strerror(errno)); } #ifdef __APPLE__ int prio=NET_SERVICE_TYPE_VO; res=setsockopt(udpSocket, SOL_SOCKET, SO_NET_SERVICE_TYPE, &prio, sizeof(prio)); if(res<0){ LOGE("error setting darwin-specific net priority: %d / %s", errno, strerror(errno)); } #else int prio=5; res=setsockopt(udpSocket, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)); if(res<0){ LOGE("error setting priority: %d / %s", errno, strerror(errno)); } prio<<=5; res=setsockopt(udpSocket, SOL_IP, IP_TOS, &prio, sizeof(prio)); if(res<0){ LOGE("error setting ip tos: %d / %s", errno, strerror(errno)); } #endif sockaddr_in addr; addr.sin_addr.s_addr=0; //addr.sin_port=htons(3497); addr.sin_port=0; addr.sin_family=AF_INET; res=bind(udpSocket, (sockaddr*)&addr, sizeof(sockaddr_in)); if(res<0){ LOGE("error binding: %d / %s", errno, strerror(errno)); } size_t addrLen=sizeof(sockaddr_in); getsockname(udpSocket, (sockaddr*)&addr, (socklen_t*) &addrLen); localUdpPort=ntohs(addr.sin_port); LOGD("Bound to local UDP port %u", addr.sin_port); SendPacket(NULL, 0, currentEndpoint); runReceiver=true; start_thread(recvThread, StartRecvThread, this); set_thread_priority(recvThread, get_thread_max_priority()); set_thread_name(recvThread, "voip-recv"); start_thread(sendThread, StartSendThread, this); set_thread_priority(sendThread, get_thread_max_priority()); set_thread_name(sendThread, "voip-send"); start_thread(tickThread, StartTickThread, this); set_thread_priority(tickThread, get_thread_max_priority()); set_thread_name(tickThread, "voip-tick"); } size_t CVoIPController::AudioInputCallback(unsigned char* data, size_t length, void* param){ ((CVoIPController*)param)->HandleAudioInput(data, length); return 0; } void CVoIPController::HandleAudioInput(unsigned char *data, size_t len){ if(stopping) return; if(waitingForAcks || dontSendPackets>0){ LOGV("waiting for RLC, dropping outgoing audio packet"); return; } int audioPacketGrouping=1; CBufferOutputStream* pkt=NULL; if(audioPacketsWritten==0){ pkt=GetOutgoingPacketBuffer(); if(!pkt){ LOGW("Dropping data packet, queue overflow"); return; } currentAudioPacket=pkt; }else{ pkt=currentAudioPacket; } unsigned char flags=(unsigned char) (len>255 ? STREAM_DATA_FLAG_LEN16 : 0); pkt->WriteByte((unsigned char) (1 | flags)); // streamID + flags if(len>255) pkt->WriteInt16((int16_t)len); else pkt->WriteByte((unsigned char)len); pkt->WriteInt32(audioTimestampOut); pkt->WriteBytes((char *) data, len); audioPacketsWritten++; if(audioPacketsWritten>=audioPacketGrouping){ uint32_t pl=pkt->GetLength(); char tmp[pl]; memcpy(tmp, pkt->GetBuffer(), pl); pkt->Reset(); unsigned char type; switch(audioPacketGrouping){ case 2: type=PKT_STREAM_DATA_X2; break; case 3: type=PKT_STREAM_DATA_X3; break; default: type=PKT_STREAM_DATA; break; } WritePacketHeader(pkt, type, pl); pkt->WriteBytes(tmp, pl); //LOGI("payload size %u", pl); if(pl<253) pl+=1; for(;pl%4>0;pl++) pkt->WriteByte(0); sendQueue->Put(pkt); audioPacketsWritten=0; } audioTimestampOut+=outgoingStreams[0]->frameDuration; } void CVoIPController::Connect(){ assert(state!=STATE_WAIT_INIT_ACK); connectionInitTime=GetCurrentTime(); SendInit(); } void CVoIPController::SetEncryptionKey(char *key){ memcpy(encryptionKey, key, 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); } uint32_t CVoIPController::WritePacketHeader(CBufferOutputStream *s, unsigned char type, uint32_t length){ uint32_t acks=0; int i; for(i=0;i<32;i++){ if(recvPacketTimes[i]>0) acks|=1; if(i<31) acks<<=1; } uint32_t pseq=seq++; 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((char *) 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 & 8)); s->WriteByte((unsigned char) ((length >> 8) & 8)); s->WriteByte((unsigned char) ((length >> 16) & 8)); } } }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((char *) 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 & 8)); s->WriteByte((unsigned char) ((lenWithHeader >> 8) & 8)); s->WriteByte((unsigned char) ((lenWithHeader >> 16) & 8)); } } s->WriteByte(type); s->WriteInt32(lastRemoteSeq); s->WriteInt32(pseq); s->WriteInt32(acks); } if(type==PKT_STREAM_DATA || type==PKT_STREAM_DATA_X2 || type==PKT_STREAM_DATA_X3) conctl->PacketSent(pseq, length); memmove(&sentPacketTimes[1], sentPacketTimes, 31*sizeof(double)); sentPacketTimes[0]=GetCurrentTime(); lastSentSeq=pseq; //LOGI("packet header size %d", s->GetLength()); return pseq; } void CVoIPController::UpdateAudioBitrate(){ if(encoder){ if(networkType==NET_TYPE_GPRS || dataSavingMode || dataSavingRequestedByPeer) encoder->SetBitrate(maxBitrate=8000); else if(networkType==NET_TYPE_EDGE){ maxBitrate=16000; encoder->SetBitrate(8000); }else{ maxBitrate=25000; encoder->SetBitrate(16000); } } } void CVoIPController::SendInit(){ CBufferOutputStream* out=new CBufferOutputStream(1024); WritePacketHeader(out, PKT_INIT, 15); out->WriteInt32(PROTOCOL_VERSION); out->WriteInt32(MIN_PROTOCOL_VERSION); uint32_t flags=0; if(dataSavingMode) flags|=INIT_FLAG_DATA_SAVING_ENABLED; out->WriteInt32(flags); out->WriteByte(1); // audio codecs count out->WriteByte(CODEC_OPUS); out->WriteByte(0); // video codecs count SendPacket(out->GetBuffer(), out->GetLength(), currentEndpoint); SetState(STATE_WAIT_INIT_ACK); delete out; } void CVoIPController::SendInitAck(){ } void CVoIPController::RunRecvThread(){ LOGI("Receive thread starting"); char buffer[1024]; sockaddr_in srcAddr; int addrLen; while(runReceiver){ //LOGI("Before recv"); addrLen=sizeof(sockaddr_in); ssize_t len=recvfrom(udpSocket, buffer, 1024, 0, (sockaddr *) &srcAddr, (socklen_t *) &addrLen); //LOGV("Received %d bytes from %s:%d at %.5lf", len, inet_ntoa(srcAddr.sin_addr), ntohs(srcAddr.sin_port), GetCurrentTime()); voip_endpoint_t* srcEndpoint=NULL; int _i; for(_i=0;_iaddress.s_addr==srcAddr.sin_addr.s_addr && endpoints[_i]->port==ntohs(srcAddr.sin_port)){ srcEndpoint=endpoints[_i]; break; } } if(!srcEndpoint){ LOGW("Received a packet from unknown source %s:%u", inet_ntoa(srcAddr.sin_addr), ntohs(srcAddr.sin_port)); continue; } if(len<=0){ LOGW("error receiving: %d / %s", errno, strerror(errno)); continue; } if(IS_MOBILE_NETWORK(networkType)) stats.bytesRecvdMobile+=(uint64_t)len; else stats.bytesRecvdWifi+=(uint64_t)len; CBufferInputStream* in=new CBufferInputStream(buffer, (size_t)len); if(memcmp(buffer, srcEndpoint->type==EP_TYPE_UDP_RELAY ? srcEndpoint->peerTag : callID, 16)!=0){ LOGW("Received packet has wrong peerTag"); delete in; continue; } in->Seek(16); if(waitingForRelayPeerInfo && in->Remaining()>=32){ bool isPublicIpResponse=true; int i; for(i=0;i<12;i++){ if((unsigned char)buffer[in->GetOffset()+i]!=0xFF){ isPublicIpResponse=false; break; } } if(isPublicIpResponse){ waitingForRelayPeerInfo=false; in->Seek(in->GetOffset()+12); uint32_t tlid=(uint32_t) in->ReadInt32(); if(tlid==TLID_UDP_REFLECTOR_PEER_INFO){ uint32_t myAddr=(uint32_t) in->ReadInt32(); uint32_t myPort=(uint32_t) in->ReadInt32(); uint32_t peerAddr=(uint32_t) in->ReadInt32(); uint32_t peerPort=(uint32_t) in->ReadInt32(); voip_endpoint_t* p2pEndpoint=NULL; for(i=0;itype==EP_TYPE_UDP_P2P_INET){ p2pEndpoint=endpoints[i]; break; } } if(!p2pEndpoint){ p2pEndpoint=(voip_endpoint_t *) malloc(sizeof(voip_endpoint_t)); endpoints.push_back(p2pEndpoint); } memset(p2pEndpoint, 0, sizeof(voip_endpoint_t)); p2pEndpoint->type=EP_TYPE_UDP_P2P_INET; p2pEndpoint->port=peerPort; p2pEndpoint->address.s_addr=peerAddr;//ntohl(peerAddr); LOGW("Received reflector peer info, my=%08X:%u, peer=%08X:%u", myAddr, myPort, peerAddr, peerPort); if(myAddr==peerAddr){ LOGW("Detected LAN"); in_addr lanAddr; GetLocalNetworkItfInfo(&lanAddr, NULL); CBufferOutputStream* pkt=GetOutgoingPacketBuffer(); if(pkt){ WritePacketHeader(pkt, PKT_LAN_ENDPOINT, 8); pkt->WriteInt32(lanAddr.s_addr); pkt->WriteInt32(localUdpPort); sendQueue->Put(pkt); } }else{ for(i=0;itype==EP_TYPE_UDP_P2P_LAN){ free(endpoints[i]); endpoints.erase(endpoints.begin()+i); break; } } } p2pPingCount=0; lastP2pPingTime=0; needSendP2pPing=true; }else{ LOGE("It looks like a reflector response but tlid is %08X, expected %08X", tlid, TLID_UDP_REFLECTOR_PEER_INFO); } delete in; continue; } } if(in->Remaining()<40){ delete in; continue; } unsigned char fingerprint[8], msgHash[16]; in->ReadBytes((char *) fingerprint, 8); in->ReadBytes((char *) msgHash, 16); if(memcmp(fingerprint, keyFingerprint, 8)!=0){ LOGW("Received packet has wrong key fingerprint"); delete in; continue; } unsigned char key[32], iv[32]; KDF(msgHash, key, iv); unsigned char aesOut[in->Remaining()]; crypto.aes_ige_decrypt((unsigned char *) buffer+in->GetOffset(), aesOut, in->Remaining(), key, iv); memcpy(buffer+in->GetOffset(), aesOut, in->Remaining()); unsigned char sha[SHA1_LENGTH]; int32_t _len=in->ReadInt32(); crypto.sha1((uint8_t *) (buffer+in->GetOffset()-4), (size_t) (_len+4), sha); if(memcmp(msgHash, sha+(SHA1_LENGTH-16), 16)!=0){ LOGW("Received packet has wrong hash after decryption"); delete in; continue; } lastRecvPacketTime=GetCurrentTime(); try{ /*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; */ uint32_t ackId, pseq, acks; unsigned char type; uint32_t tlid=(uint32_t) in->ReadInt32(); uint32_t packetInnerLen; if(tlid==TLID_DECRYPTED_AUDIO_BLOCK){ in->ReadInt64(); // random id uint32_t randLen=(uint32_t) in->ReadTlLength(); in->Seek(in->GetOffset()+randLen+(randLen+(randLen<=253 ? 1 : 0))%4); uint32_t flags=(uint32_t) in->ReadInt32(); 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"); delete in; continue; } if(flags & PFLAG_HAS_CALL_ID){ unsigned char pktCallID[16]; in->ReadBytes((char *) pktCallID, 16); } ackId=(uint32_t) in->ReadInt32(); pseq=(uint32_t) in->ReadInt32(); acks=(uint32_t) in->ReadInt32(); if(flags & PFLAG_HAS_PROTO){ uint32_t proto=(uint32_t) in->ReadInt32(); if(proto!=PROTOCOL_NAME){ LOGW("Received packet uses wrong protocol"); delete in; lastError=ERROR_INCOMPATIBLE; SetState(STATE_FAILED); return; } } if(flags & PFLAG_HAS_EXTRA){ uint32_t extraLen=(uint32_t) in->ReadTlLength(); in->Seek(in->GetOffset()+extraLen+(extraLen+(extraLen<=253 ? 1 : 0))%4); } if(flags & PFLAG_HAS_DATA){ packetInnerLen=in->ReadTlLength(); } }else if(tlid==TLID_SIMPLE_AUDIO_BLOCK){ in->ReadInt64(); // random id uint32_t randLen=(uint32_t) in->ReadTlLength(); in->Seek(in->GetOffset()+randLen+(randLen+(randLen<=253 ? 1 : 0))%4); packetInnerLen=in->ReadTlLength(); type=in->ReadByte(); ackId=(uint32_t) in->ReadInt32(); pseq=(uint32_t) in->ReadInt32(); acks=(uint32_t) in->ReadInt32(); }else{ LOGW("Received a packet of unknown type %08X", tlid); delete in; continue; } packetsRecieved++; if(seqgt(pseq, lastRemoteSeq)){ uint32_t diff=pseq-lastRemoteSeq; if(diff>31){ memset(recvPacketTimes, 0, 32*sizeof(double)); }else{ memmove(&recvPacketTimes[diff], recvPacketTimes, (32-diff)*sizeof(double)); if(diff>1){ memset(recvPacketTimes, 0, diff*sizeof(double)); } recvPacketTimes[0]=GetCurrentTime(); } lastRemoteSeq=pseq; }else if(!seqgt(pseq, lastRemoteSeq) && lastRemoteSeq-pseq<32){ if(recvPacketTimes[lastRemoteSeq-pseq]!=0){ LOGW("Received duplicated packet for seq %u", pseq); delete in; continue; } recvPacketTimes[lastRemoteSeq-pseq]=GetCurrentTime(); }else if(lastRemoteSeq-pseq>=32){ LOGW("Packet %u is out of order and too late", pseq); delete in; continue; } if(seqgt(ackId, lastRemoteAckSeq)){ uint32_t diff=ackId-lastRemoteAckSeq; if(diff>31){ memset(remoteAcks, 0, 32*sizeof(double)); }else{ memmove(&remoteAcks[diff], remoteAcks, (32-diff)*sizeof(double)); if(diff>1){ memset(remoteAcks, 0, diff*sizeof(double)); } remoteAcks[0]=GetCurrentTime(); } if(waitingForAcks && lastRemoteAckSeq>=firstSentPing){ memset(rttHistory, 0, 32*sizeof(double)); waitingForAcks=false; dontSendPackets=10; LOGI("resuming sending"); } lastRemoteAckSeq=ackId; conctl->PacketAcknowledged(ackId); int i; for(i=0;i<31;i++){ if(remoteAcks[i+1]==0){ if((acks >> (31-i)) & 1){ remoteAcks[i+1]=GetCurrentTime(); conctl->PacketAcknowledged(ackId-(i+1)); } } } lock_mutex(queuedPacketsMutex); for(i=0;iseqs[j]); if(qp->seqs[j]==0) break; int remoteAcksIndex=lastRemoteAckSeq-qp->seqs[j]; LOGV("remote acks index %u, value %llf", remoteAcksIndex, remoteAcksIndex>=0 && remoteAcksIndex<32 ? remoteAcks[remoteAcksIndex] : -1); if(seqgt(lastRemoteAckSeq, qp->seqs[j]) && remoteAcksIndex>=0 && remoteAcksIndex<32 && remoteAcks[remoteAcksIndex]>0){ LOGD("did ack seq %u, removing", qp->seqs[j]); didAck=true; break; } } if(didAck){ if(qp->data) free(qp->data); free(qp); queuedPackets.erase(queuedPackets.begin()+i); i--; continue; } } unlock_mutex(queuedPacketsMutex); } if(srcEndpoint!=currentEndpoint && srcEndpoint->type==EP_TYPE_UDP_RELAY && currentEndpoint->type!=EP_TYPE_UDP_RELAY){ if(seqgt(lastSentSeq-32, lastRemoteAckSeq)){ currentEndpoint=srcEndpoint; LOGI("Peer network address probably changed, switching to relay"); if(allowP2p) SendPublicEndpointsRequest(); } } //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"); peerVersion=(uint32_t) in->ReadInt32(); LOGI("Peer version is %d", peerVersion); uint32_t minVer=(uint32_t) in->ReadInt32(); if(minVer>PROTOCOL_VERSION || peerVersionReadInt32(); if(flags & INIT_FLAG_DATA_SAVING_ENABLED){ dataSavingRequestedByPeer=true; UpdateDataSavingState(); UpdateAudioBitrate(); } int i; int numSupportedAudioCodecs=in->ReadByte(); for(i=0;iReadByte(); // ignore for now } int numSupportedVideoCodecs=in->ReadByte(); for(i=0;iReadByte(); // ignore for now } CBufferOutputStream* out=new CBufferOutputStream(1024); WritePacketHeader(out, PKT_INIT_ACK, (peerVersion>=2 ? 10 : 2)+(peerVersion>=2 ? 6 : 4)*outgoingStreams.size()); if(peerVersion>=2){ out->WriteInt32(PROTOCOL_VERSION); out->WriteInt32(MIN_PROTOCOL_VERSION); } out->WriteByte((unsigned char) outgoingStreams.size()); for(i=0;iWriteByte(outgoingStreams[i]->id); out->WriteByte(outgoingStreams[i]->type); out->WriteByte(outgoingStreams[i]->codec); if(peerVersion>=2) out->WriteInt16(outgoingStreams[i]->frameDuration); else outgoingStreams[i]->frameDuration=20; out->WriteByte((unsigned char)(outgoingStreams[i]->enabled ? 1 : 0)); } SendPacket(out->GetBuffer(), out->GetLength(), currentEndpoint); delete out; } if(type==PKT_INIT_ACK){ LOGD("Received init ack"); if(packetInnerLen>10){ peerVersion=in->ReadInt32(); uint32_t minVer=(uint32_t) in->ReadInt32(); if(minVer>PROTOCOL_VERSION || peerVersionReadByte(); if(streamCount==0) goto malformed_packet; int i; voip_stream_t* incomingAudioStream=NULL; for(i=0;iid=in->ReadByte(); stm->type=in->ReadByte(); stm->codec=in->ReadByte(); if(peerVersion>=2) stm->frameDuration=(uint16_t) in->ReadInt16(); else stm->frameDuration=20; stm->enabled=in->ReadByte()==1; incomingStreams.push_back(stm); if(stm->type==STREAM_TYPE_AUDIO && !incomingAudioStream) incomingAudioStream=stm; } if(!incomingAudioStream) goto malformed_packet; voip_stream_t* outgoingAudioStream=outgoingStreams[0]; if(!audioInput){ LOGI("before create audio io"); audioInput=CAudioInput::Create(); audioInput->Configure(48000, 16, 1); audioOutput=CAudioOutput::Create(); audioOutput->Configure(48000, 16, 1); if(enableAEC) echoCanceller=new CEchoCanceller(); else echoCanceller=NULL; encoder=new COpusEncoder(audioInput); encoder->SetCallback(AudioInputCallback, this); encoder->SetOutputFrameDuration(outgoingAudioStream->frameDuration); encoder->SetEchoCanceller(echoCanceller); encoder->Start(); if(!micMuted) audioInput->Start(); UpdateAudioBitrate(); jitterBuffer=new CJitterBuffer(NULL, incomingAudioStream->frameDuration); decoder=new COpusDecoder(audioOutput); decoder->SetEchoCanceller(echoCanceller); decoder->SetJitterBuffer(jitterBuffer); decoder->SetFrameDuration(incomingAudioStream->frameDuration); decoder->Start(); if(incomingAudioStream->frameDuration>50) jitterBuffer->SetMinPacketCount(3); else if(incomingAudioStream->frameDuration>30) jitterBuffer->SetMinPacketCount(4); //audioOutput->Start(); } SetState(STATE_ESTABLISHED); if(allowP2p) SendPublicEndpointsRequest(); } if(type==PKT_STREAM_DATA || type==PKT_STREAM_DATA_X2 || type==PKT_STREAM_DATA_X3){ 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; for(i=0;iReadByte(); unsigned char flags=(unsigned char) (streamID & 0xC0); uint16_t sdlen=(uint16_t) (flags & STREAM_DATA_FLAG_LEN16 ? in->ReadInt16() : in->ReadByte()); uint32_t pts=(uint32_t) in->ReadInt32(); //LOGD("stream data, pts=%d, len=%d, rem=%d", pts, sdlen, in->Remaining()); audioTimestampIn=pts; if(!audioOutStarted && audioOutput){ audioOutput->Start(); audioOutStarted=true; } if(jitterBuffer) jitterBuffer->HandleInput((unsigned char*) (buffer+in->GetOffset()), sdlen, pts); if(iSeek(in->GetOffset()+sdlen); } } if(type==PKT_PING){ LOGD("Received ping from %s:%d", inet_ntoa(srcEndpoint->address), srcEndpoint->port); if(srcEndpoint->type!=EP_TYPE_UDP_RELAY && !allowP2p){ LOGW("Received p2p ping but p2p is disabled by manual override"); delete in; continue; } if(srcEndpoint==currentEndpoint){ CBufferOutputStream *pkt=GetOutgoingPacketBuffer(); if(!pkt){ LOGW("Dropping pong packet, queue overflow"); continue; } WritePacketHeader(pkt, PKT_PONG, 4); pkt->WriteInt32(pseq); sendQueue->Put(pkt); }else{ CBufferOutputStream pkt(32); WritePacketHeader(&pkt, PKT_PONG, 4); pkt.WriteInt32(pseq); SendPacket(pkt.GetBuffer(), pkt.GetLength(), srcEndpoint); } } if(type==PKT_PONG){ if(packetInnerLen>=4){ uint32_t pingSeq=(uint32_t) in->ReadInt32(); if(pingSeq==srcEndpoint->_lastPingSeq){ memmove(&srcEndpoint->_rtts[1], srcEndpoint->_rtts, sizeof(double)*5); srcEndpoint->_rtts[0]=GetCurrentTime()-srcEndpoint->_lastPingTime; int i; srcEndpoint->_averageRtt=0; for(i=0;i<6;i++){ if(srcEndpoint->_rtts[i]==0) break; srcEndpoint->_averageRtt+=srcEndpoint->_rtts[i]; } srcEndpoint->_averageRtt/=i; LOGD("Current RTT via %s: %.3llf, average: %.3llf", inet_ntoa(srcEndpoint->address), srcEndpoint->_rtts[0], srcEndpoint->_averageRtt); } } /*if(currentEndpoint!=srcEndpoint && (srcEndpoint->type==EP_TYPE_UDP_P2P_INET || srcEndpoint->type==EP_TYPE_UDP_P2P_LAN)){ LOGI("Switching to P2P now!"); currentEndpoint=srcEndpoint; needSendP2pPing=false; }*/ } if(type==PKT_STREAM_STATE){ unsigned char id=in->ReadByte(); unsigned char enabled=in->ReadByte(); int i; for(i=0;iid==id){ incomingStreams[i]->enabled=enabled==1; UpdateAudioOutputState(); break; } } } if(type==PKT_LAN_ENDPOINT){ uint32_t peerAddr=(uint32_t) in->ReadInt32(); uint16_t peerPort=(uint16_t) in->ReadInt32(); voip_endpoint_t* p2pEndpoint=GetEndpointByType(EP_TYPE_UDP_P2P_LAN); if(!p2pEndpoint){ p2pEndpoint=(voip_endpoint_t *) malloc(sizeof(voip_endpoint_t)); endpoints.push_back(p2pEndpoint); } memset(p2pEndpoint, 0, sizeof(voip_endpoint_t)); p2pEndpoint->type=EP_TYPE_UDP_P2P_LAN; p2pEndpoint->port=peerPort; p2pEndpoint->address.s_addr=peerAddr;//ntohl(peerAddr); } if(type==PKT_NETWORK_CHANGED){ currentEndpoint=preferredRelay; if(allowP2p) SendPublicEndpointsRequest(); if(peerVersion>=2){ uint32_t flags=(uint32_t) in->ReadInt32(); dataSavingRequestedByPeer=(flags & INIT_FLAG_DATA_SAVING_ENABLED)==INIT_FLAG_DATA_SAVING_ENABLED; UpdateDataSavingState(); UpdateAudioBitrate(); } } if(type==PKT_SWITCH_PREF_RELAY){ uint64_t relayId=(uint64_t) in->ReadInt64(); int i; for(i=0;itype==EP_TYPE_UDP_RELAY && endpoints[i]->id==relayId){ preferredRelay=endpoints[i]; LOGD("Switching preferred relay to %s:%d", inet_ntoa(preferredRelay->address), preferredRelay->port); break; } } if(currentEndpoint->type==EP_TYPE_UDP_RELAY) currentEndpoint=preferredRelay; } /*if(type==PKT_SWITCH_TO_P2P && allowP2p){ voip_endpoint_t* p2p=GetEndpointByType(EP_TYPE_UDP_P2P_INET); if(p2p){ voip_endpoint_t* lan=GetEndpointByType(EP_TYPE_UDP_P2P_LAN); if(lan && lan->_averageRtt>0){ LOGI("Switching to p2p (LAN)"); currentEndpoint=lan; }else{ if(lan) lan->_lastPingTime=0; if(p2p->_averageRtt>0){ LOGI("Switching to p2p (Inet)"); currentEndpoint=p2p; }else{ p2p->_lastPingTime=0; } } } }*/ }catch(std::out_of_range x){ LOGW("Error parsing packet: %s", x.what()); } malformed_packet: delete in; } LOGI("=== recv thread exiting ==="); } void CVoIPController::RunSendThread(){ while(runReceiver){ CBufferOutputStream* pkt=(CBufferOutputStream *) sendQueue->GetBlocking(); if(pkt){ SendPacket(pkt->GetBuffer(), pkt->GetLength(), currentEndpoint); pkt->Reset(); lock_mutex(sendBufferMutex); emptySendBuffers.push_back(pkt); unlock_mutex(sendBufferMutex); } } LOGI("=== send thread exiting ==="); } void CVoIPController::RunTickThread(){ uint32_t tickCount=0; bool wasWaitingForAcks=false; while(runReceiver){ usleep(100000); tickCount++; if(tickCount%5==0 && state==STATE_ESTABLISHED){ memmove(&rttHistory[1], rttHistory, 31*sizeof(double)); rttHistory[0]=GetAverageRTT(); /*if(rttHistory[16]>0){ LOGI("rtt diff: %.3lf", rttHistory[0]-rttHistory[16]); }*/ int i; double v=0; for(i=1;i<32;i++){ v+=rttHistory[i-1]-rttHistory[i]; } v=v/32; if(rttHistory[0]>10.0 && rttHistory[8]>10.0 && (networkType==NET_TYPE_EDGE || networkType==NET_TYPE_GPRS)){ waitingForAcks=true; }else{ waitingForAcks=false; } if(waitingForAcks) wasWaitingForAcks=false; //LOGI("%.3lf/%.3lf, rtt diff %.3lf, waiting=%d, queue=%d", rttHistory[0], rttHistory[8], v, waitingForAcks, sendQueue->Size()); if(jitterBuffer) recvLossCount+=jitterBuffer->GetAndResetLostPacketCount(); } if(dontSendPackets>0) dontSendPackets--; int i; conctl->Tick(); if(state==STATE_ESTABLISHED){ int act=conctl->GetBandwidthControlAction(); if(act==TGVOIP_CONCTL_ACT_DECREASE){ uint32_t bitrate=encoder->GetBitrate(); if(bitrate>8000) encoder->SetBitrate(bitrate<9000 ? 8000 : (bitrate-1000)); }else if(act==TGVOIP_CONCTL_ACT_INCREASE){ uint32_t bitrate=encoder->GetBitrate(); if(bitrateSetBitrate(bitrate+1000); } if(tickCount%10==0 && encoder){ uint32_t sendLossCount=conctl->GetSendLossCount(); memmove(sendLossCountHistory+1, sendLossCountHistory, 31*sizeof(uint32_t)); sendLossCountHistory[0]=sendLossCount-prevSendLossCount; prevSendLossCount=sendLossCount; double avgSendLossCount=0; for(i=0;i<10;i++){ avgSendLossCount+=sendLossCountHistory[i]; } double packetsPerSec=1000/(double)outgoingStreams[0]->frameDuration; avgSendLossCount=avgSendLossCount/10/packetsPerSec; //LOGV("avg send loss: %.1f%%", avgSendLossCount*100); if(avgSendLossCount>0.1){ encoder->SetPacketLoss(40); }else if(avgSendLossCount>0.075){ encoder->SetPacketLoss(35); }else if(avgSendLossCount>0.0625){ encoder->SetPacketLoss(30); }else if(avgSendLossCount>0.05){ encoder->SetPacketLoss(25); }else if(avgSendLossCount>0.025){ encoder->SetPacketLoss(20); }else if(avgSendLossCount>0.01){ encoder->SetPacketLoss(17); }else{ encoder->SetPacketLoss(15); } } } bool areThereAnyEnabledStreams=false; for(i=0;ienabled) areThereAnyEnabledStreams=true; } if((waitingForAcks && tickCount%10==0) || (!areThereAnyEnabledStreams && tickCount%2==0)){ CBufferOutputStream* pkt=GetOutgoingPacketBuffer(); if(!pkt){ LOGW("Dropping ping packet, queue overflow"); return; } uint32_t seq=WritePacketHeader(pkt, PKT_NOP, 0); firstSentPing=seq; sendQueue->Put(pkt); LOGV("sent ping"); } if(state==STATE_WAIT_INIT_ACK && GetCurrentTime()-stateChangeTime>.5){ SendInit(); } /*if(needSendP2pPing){ if(GetCurrentTime()-lastP2pPingTime>2){ if(p2pPingCount<10){ // try hairpin routing first, even if we have a LAN address SendP2pPing(EP_TYPE_UDP_P2P_INET); } if(p2pPingCount>=5 && p2pPingCount<15){ // last resort to get p2p SendP2pPing(EP_TYPE_UDP_P2P_LAN); } p2pPingCount++; } }*/ if(waitingForRelayPeerInfo && GetCurrentTime()-publicEndpointsReqTime>5){ LOGD("Resending peer relay info request"); SendPublicEndpointsRequest(); } lock_mutex(queuedPacketsMutex); for(i=0;itimeout>0 && qp->firstSentTime>0 && GetCurrentTime()-qp->firstSentTime>=qp->timeout){ LOGD("Removing queued packet because of timeout"); if(qp->data) free(qp->data); free(qp); queuedPackets.erase(queuedPackets.begin()+i); i--; continue; } if(GetCurrentTime()-qp->lastSentTime>=qp->retryInterval){ CBufferOutputStream* pkt=GetOutgoingPacketBuffer(); if(pkt){ uint32_t seq=WritePacketHeader(pkt, qp->type, qp->length); memmove(&qp->seqs[1], qp->seqs, 4*9); qp->seqs[0]=seq; qp->lastSentTime=GetCurrentTime(); LOGD("Sending queued packet, seq=%u, type=%u, len=%u", seq, qp->type, qp->length); if(qp->firstSentTime==0) qp->firstSentTime=qp->lastSentTime; if(qp->length) pkt->WriteBytes(qp->data, qp->length); sendQueue->Put(pkt); } } } unlock_mutex(queuedPacketsMutex); if(jitterBuffer) jitterBuffer->Tick(); if(state==STATE_ESTABLISHED){ voip_endpoint_t* minPingRelay=preferredRelay; double minPing=preferredRelay->_averageRtt; for(i=0;i_lastPingTime>=10){ LOGV("Sending ping to %s", inet_ntoa(e->address)); CBufferOutputStream pkt(32); uint32_t seq=WritePacketHeader(&pkt, PKT_PING, 0); e->_lastPingTime=GetCurrentTime(); e->_lastPingSeq=seq; SendPacket(pkt.GetBuffer(), pkt.GetLength(), e); } if(e->type==EP_TYPE_UDP_RELAY){ if(e->_averageRtt>0 && e->_averageRtt_averageRtt; minPingRelay=e; } } } if(minPingRelay!=preferredRelay){ preferredRelay=minPingRelay; if(currentEndpoint->type==EP_TYPE_UDP_RELAY) currentEndpoint=preferredRelay; /*CBufferOutputStream pkt(32); pkt.WriteInt64(preferredRelay->id); SendPacketReliably(PKT_SWITCH_PREF_RELAY, pkt.GetBuffer(), pkt.GetLength(), 1, 9);*/ } if(currentEndpoint->type==EP_TYPE_UDP_RELAY){ voip_endpoint_t *p2p=GetEndpointByType(EP_TYPE_UDP_P2P_INET); if(p2p){ voip_endpoint_t *lan=GetEndpointByType(EP_TYPE_UDP_P2P_LAN); if(lan && lan->_averageRtt>0){ SendPacketReliably(PKT_SWITCH_TO_P2P, NULL, 0, 1, 5); currentEndpoint=lan; LOGI("Switching to p2p (LAN)"); }else{ if(p2p->_averageRtt>0 && p2p->_averageRtt0 && minPing_averageRtt*.6){ LOGI("Switching to relay"); currentEndpoint=preferredRelay; } } } if(state==STATE_ESTABLISHED){ if(GetCurrentTime()-lastRecvPacketTime>=config.recv_timeout){ if(currentEndpoint && currentEndpoint->type!=EP_TYPE_UDP_RELAY){ LOGW("Packet receive timeout, switching to relay"); currentEndpoint=GetEndpointByType(EP_TYPE_UDP_RELAY); if(allowP2p){ SendPublicEndpointsRequest(); } UpdateDataSavingState(); UpdateAudioBitrate(); CBufferOutputStream s(4); s.WriteInt32(dataSavingMode ? INIT_FLAG_DATA_SAVING_ENABLED : 0); SendPacketReliably(PKT_NETWORK_CHANGED, s.GetBuffer(), s.GetLength(), 1, 20); lastRecvPacketTime=GetCurrentTime(); }else{ LOGW("Packet receive timeout, disconnecting"); lastError=ERROR_TIMEOUT; SetState(STATE_FAILED); } } }else if(state==STATE_WAIT_INIT){ if(GetCurrentTime()-connectionInitTime>=config.init_timeout){ LOGW("Init timeout, disconnecting"); lastError=ERROR_TIMEOUT; SetState(STATE_FAILED); } } } LOGI("=== tick thread exiting ==="); } voip_endpoint_t *CVoIPController::GetRemoteEndpoint(){ //return useLan ? &remoteLanEp : &remotePublicEp; return currentEndpoint; } void CVoIPController::SendPacket(char *data, size_t len, voip_endpoint_t* ep){ if(stopping) return; sockaddr_in dst; dst.sin_addr=ep->address; dst.sin_port=htons(ep->port); dst.sin_family=AF_INET; CBufferOutputStream out(len+128); if(ep->type==EP_TYPE_UDP_RELAY) out.WriteBytes(ep->peerTag, 16); else out.WriteBytes(callID, 16); if(len>0){ CBufferOutputStream inner(len+128); inner.WriteInt32(len); inner.WriteBytes(data, len); if(inner.GetLength()%16!=0){ size_t padLen=16-inner.GetLength()%16; char padding[padLen]; 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((char *) (msgHash+(SHA1_LENGTH-16)), 16); KDF(msgHash+(SHA1_LENGTH-16), key, iv); unsigned char aesOut[inner.GetLength()]; crypto.aes_ige_encrypt((unsigned char *) inner.GetBuffer(), aesOut, inner.GetLength(), key, iv); out.WriteBytes((char*)aesOut, inner.GetLength()); } //LOGV("Sending %d bytes to %s:%d", out.GetLength(), inet_ntoa(ep->address), ep->port); if(IS_MOBILE_NETWORK(networkType)) stats.bytesSentMobile+=(uint64_t)out.GetLength(); else stats.bytesSentWifi+=(uint64_t)out.GetLength(); int res=sendto(udpSocket, out.GetBuffer(), out.GetLength(), 0, (const sockaddr *) &dst, sizeof(sockaddr_in)); if(res<0){ LOGE("error sending: %d / %s", errno, strerror(errno)); } } void CVoIPController::SetNetworkType(int type){ networkType=type; UpdateDataSavingState(); UpdateAudioBitrate(); char itfName[32]; GetLocalNetworkItfInfo(NULL, itfName); if(strcmp(itfName, activeNetItfName)!=0){ LOGI("Active network interface changed: %s -> %s", activeNetItfName, itfName); bool isFirstChange=strlen(activeNetItfName)==0; strcpy(activeNetItfName, itfName); if(isFirstChange) return; if(currentEndpoint && currentEndpoint->type!=EP_TYPE_UDP_RELAY){ int i; currentEndpoint=preferredRelay; for(i=0;itype==EP_TYPE_UDP_P2P_LAN){ free(endpoints[i]); endpoints.erase(endpoints.begin()+i); break; } } } if(allowP2p && currentEndpoint){ SendPublicEndpointsRequest(); } CBufferOutputStream s(4); s.WriteInt32(dataSavingMode ? INIT_FLAG_DATA_SAVING_ENABLED : 0); SendPacketReliably(PKT_NETWORK_CHANGED, s.GetBuffer(), s.GetLength(), 1, 20); } LOGI("set network type: %d, active interface %s", type, activeNetItfName); /*if(type==NET_TYPE_GPRS || type==NET_TYPE_EDGE) audioPacketGrouping=2; else audioPacketGrouping=1;*/ } double CVoIPController::GetAverageRTT(){ if(lastSentSeq>=lastRemoteAckSeq){ uint32_t diff=lastSentSeq-lastRemoteAckSeq; //LOGV("rtt diff=%u", diff); if(diff<32){ int i; double res=0; int count=0; for(i=diff;i<32;i++){ if(remoteAcks[i-diff]>0){ res+=(remoteAcks[i-diff]-sentPacketTimes[i]); count++; } } if(count>0) res/=count; return res; } } return 999; } double CVoIPController::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__) if(!machTimestart){ mach_timebase_info_data_t tb = { 0, 0 }; mach_timebase_info(&tb); machTimebase = tb.numer; machTimebase /= tb.denom; machTimestart = mach_absolute_time(); } return (mach_absolute_time() - machTimestart) * machTimebase / 1000000000.0f; #endif } void CVoIPController::SetStateCallback(void (* f)(CVoIPController*, int)){ stateCallback=f; if(stateCallback){ stateCallback(this, state); } } void CVoIPController::SetState(int state){ this->state=state; stateChangeTime=GetCurrentTime(); if(stateCallback){ stateCallback(this, state); } } void CVoIPController::SetMicMute(bool mute){ micMuted=mute; if(audioInput){ if(mute) audioInput->Stop(); else audioInput->Start(); } if(echoCanceller) echoCanceller->Enable(!mute); int i; for(i=0;itype==STREAM_TYPE_AUDIO){ char buf[2]; buf[0]=outgoingStreams[i]->id; buf[1]=(char) (mute ? 0 : 1); SendPacketReliably(PKT_STREAM_STATE, buf, 2, .5f, 20); outgoingStreams[i]->enabled=!mute; } } } void CVoIPController::UpdateAudioOutputState(){ bool areAnyAudioStreamsEnabled=false; int i; for(i=0;itype==STREAM_TYPE_AUDIO && incomingStreams[i]->enabled) areAnyAudioStreamsEnabled=true; } if(jitterBuffer){ jitterBuffer->Reset(); } if(decoder){ decoder->ResetQueue(); } if(audioOutput){ if(audioOutput->IsPlaying()!=areAnyAudioStreamsEnabled){ if(areAnyAudioStreamsEnabled) audioOutput->Start(); else audioOutput->Stop(); } } } CBufferOutputStream *CVoIPController::GetOutgoingPacketBuffer(){ CBufferOutputStream* pkt=NULL; lock_mutex(sendBufferMutex); if(emptySendBuffers.size()>0){ pkt=emptySendBuffers[emptySendBuffers.size()-1]; emptySendBuffers.pop_back(); } unlock_mutex(sendBufferMutex); return pkt; } void CVoIPController::KDF(unsigned char* msgKey, unsigned char* aesKey, unsigned char* aesIv){ uint8_t sA[SHA1_LENGTH], sB[SHA1_LENGTH], sC[SHA1_LENGTH], sD[SHA1_LENGTH]; size_t x=0; CBufferOutputStream buf(128); buf.WriteBytes((char *) msgKey, 16); buf.WriteBytes(encryptionKey+x, 32); crypto.sha1((uint8_t *) buf.GetBuffer(), buf.GetLength(), sA); buf.Reset(); buf.WriteBytes(encryptionKey+32+x, 16); buf.WriteBytes((char *) msgKey, 16); buf.WriteBytes(encryptionKey+48+x, 16); crypto.sha1((uint8_t *) buf.GetBuffer(), buf.GetLength(), sB); buf.Reset(); buf.WriteBytes(encryptionKey+64+x, 32); buf.WriteBytes((char *) msgKey, 16); crypto.sha1((uint8_t *) buf.GetBuffer(), buf.GetLength(), sC); buf.Reset(); buf.WriteBytes((char *) msgKey, 16); buf.WriteBytes(encryptionKey+96+x, 32); crypto.sha1(( uint8_t *) buf.GetBuffer(), buf.GetLength(), sD); buf.Reset(); buf.WriteBytes((char *) sA, 8); buf.WriteBytes((char *) sB+8, 12); buf.WriteBytes((char *) sC+4, 12); assert(buf.GetLength()==32); memcpy(aesKey, buf.GetBuffer(), 32); buf.Reset(); buf.WriteBytes((char *) sA+8, 12); buf.WriteBytes((char *) sB, 8); buf.WriteBytes((char *) sC+16, 4); buf.WriteBytes((char *) sD, 8); assert(buf.GetLength()==32); memcpy(aesIv, buf.GetBuffer(), 32); } void CVoIPController::GetDebugString(char *buffer, size_t len){ char endpointsBuf[10240]; memset(endpointsBuf, 0, 10240); int i; for(i=0;itype){ case EP_TYPE_UDP_P2P_INET: type="UDP_P2P_INET"; break; case EP_TYPE_UDP_P2P_LAN: type="UDP_P2P_LAN"; break; case EP_TYPE_UDP_RELAY: type="UDP_RELAY"; break; case EP_TYPE_TCP_RELAY: type="TCP_RELAY"; break; default: type="UNKNOWN"; break; } if(strlen(endpointsBuf)>10240-1024) break; sprintf(endpointsBuf+strlen(endpointsBuf), "%s:%u %dms [%s%s]\n", inet_ntoa(endpoints[i]->address), endpoints[i]->port, (int)(endpoints[i]->_averageRtt*1000), type, currentEndpoint==endpoints[i] ? ", IN_USE" : ""); } double avgLate[3]; if(jitterBuffer) jitterBuffer->GetAverageLateCount(avgLate); else memset(avgLate, 0, 3*sizeof(double)); snprintf(buffer, len, "Remote endpoints: \n%s" "Jitter buffer: %d/%d | %.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\n" "Last sent/ack'd seq: %u/%u\n" "Last recvd seq: %u\n" "Send/recv losses: %u/%u\n" "Audio bitrate: %d kbit\n" // "Packet grouping: %d\n" "Frame size out/in: %d/%d\n" "Bytes sent/recvd: %llu/%llu", endpointsBuf, jitterBuffer ? jitterBuffer->GetMinPacketCount() : 0, jitterBuffer ? jitterBuffer->GetCurrentDelay() : 0, avgLate[0], avgLate[1], avgLate[2], // (int)(GetAverageRTT()*1000), 0, (int)(conctl->GetAverageRTT()*1000), (int)(conctl->GetMinimumRTT()*1000), conctl->GetInflightDataSize(), conctl->GetCongestionWindow(), keyFingerprint[0],keyFingerprint[1],keyFingerprint[2],keyFingerprint[3],keyFingerprint[4],keyFingerprint[5],keyFingerprint[6],keyFingerprint[7], lastSentSeq, lastRemoteAckSeq, lastRemoteSeq, conctl->GetSendLossCount(), recvLossCount, encoder ? (encoder->GetBitrate()/1000) : 0, // audioPacketGrouping, outgoingStreams[0]->frameDuration, incomingStreams.size()>0 ? incomingStreams[0]->frameDuration : 0, stats.bytesSentMobile+stats.bytesSentWifi, stats.bytesRecvdMobile+stats.bytesRecvdWifi); } void CVoIPController::SendPublicEndpointsRequest(){ LOGI("Sending public endpoints request"); voip_endpoint_t* relay=GetEndpointByType(EP_TYPE_UDP_RELAY); if(!relay) return; publicEndpointsReqTime=GetCurrentTime(); waitingForRelayPeerInfo=true; char buf[32]; memcpy(buf, relay->peerTag, 16); memset(buf+16, 0xFF, 16); sockaddr_in dst; dst.sin_addr=relay->address; dst.sin_port=htons(relay->port); dst.sin_family=AF_INET; int res=sendto(udpSocket, buf, 32, 0, (const sockaddr *) &dst, sizeof(sockaddr_in)); if(res<0){ LOGE("error sending: %d / %s", errno, strerror(errno)); } } void CVoIPController::SendP2pPing(int endpointType){ LOGD("Sending ping for p2p, endpoint type %d", endpointType); voip_endpoint_t* endpoint=GetEndpointByType(endpointType); if(!endpoint) return; lastP2pPingTime=GetCurrentTime(); CBufferOutputStream pkt(32); uint32_t seq=WritePacketHeader(&pkt, PKT_PING, 0); SendPacket(pkt.GetBuffer(), pkt.GetLength(), endpoint); } void CVoIPController::GetLocalNetworkItfInfo(in_addr *outAddr, char *outName){ struct ifconf ifc; struct ifreq* ifr; char buf[16384]; int sd; sd=socket(PF_INET, SOCK_DGRAM, 0); if(sd>0){ ifc.ifc_len=sizeof(buf); ifc.ifc_ifcu.ifcu_buf=buf; if(ioctl(sd, SIOCGIFCONF, &ifc)==0){ ifr=ifc.ifc_req; int len; int i; for(i=0;iifr_addr.sa_len; #else len=sizeof(*ifr); #endif if(ifr->ifr_addr.sa_family==AF_INET){ if(ioctl(sd, SIOCGIFADDR, ifr)==0){ struct sockaddr_in* addr=(struct sockaddr_in *)(&ifr->ifr_addr); LOGI("Interface %s, address %s\n", ifr->ifr_name, inet_ntoa(addr->sin_addr)); if(strcmp(ifr->ifr_name, "lo0")!=0 && strcmp(ifr->ifr_name, "lo")!=0 && addr->sin_addr.s_addr!=inet_addr("127.0.0.1")){ if(outAddr) memcpy(outAddr, &addr->sin_addr, sizeof(in_addr)); if(outName) strcpy(outName, ifr->ifr_name); } }else{ LOGE("Error getting address for %s: %d\n", ifr->ifr_name, errno); } } ifr=(struct ifreq*)((char*)ifr+len); i+=len; } }else{ LOGE("Error getting LAN address: %d", errno); } } close(sd); } voip_endpoint_t *CVoIPController::GetEndpointByType(int type){ if(type==EP_TYPE_UDP_RELAY && preferredRelay) return preferredRelay; int i; for(i=0;itype==type) return endpoints[i]; } return NULL; } float CVoIPController::GetOutputLevel(){ if(!audioOutput || !audioOutStarted){ return 0.0; } return audioOutput->GetLevel(); } char * CVoIPController::SendPacketReliably(unsigned char type, char *data, size_t len, double retryInterval, double timeout){ LOGD("Send reliably, type=%u, len=%u, retry=%.3llf, timeout=%.3llf", type, len, retryInterval, timeout); voip_queued_packet_t* pkt=(voip_queued_packet_t *) malloc(sizeof(voip_queued_packet_t)); memset(pkt, 0, sizeof(voip_queued_packet_t)); pkt->type=type; if(data){ pkt->data=(char *) malloc(len); memcpy(pkt->data, data, len); pkt->length=len; } pkt->retryInterval=retryInterval; pkt->timeout=timeout; pkt->firstSentTime=0; pkt->lastSentTime=0; lock_mutex(queuedPacketsMutex); queuedPackets.push_back(pkt); unlock_mutex(queuedPacketsMutex); } void CVoIPController::SetConfig(voip_config_t *cfg){ memcpy(&config, cfg, sizeof(voip_config_t)); #ifdef __ANDROID__ enableAEC=cfg->enableAEC; #endif UpdateDataSavingState(); UpdateAudioBitrate(); if(cfg->frame_size) outgoingStreams[0]->frameDuration=cfg->frame_size; } void CVoIPController::UpdateDataSavingState(){ if(config.data_saving==DATA_SAVING_ALWAYS){ dataSavingMode=true; }else if(config.data_saving==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.data_saving, dataSavingMode, dataSavingRequestedByPeer); } void CVoIPController::DebugCtl(int request, int param){ if(request==1){ // set bitrate maxBitrate=param; if(encoder){ encoder->SetBitrate(maxBitrate); } }else if(request==2){ // set packet loss if(encoder){ encoder->SetPacketLoss(param); } }else if(request==3){ // force enable/disable p2p allowP2p=param==1; if(!allowP2p && currentEndpoint && currentEndpoint->type!=EP_TYPE_UDP_RELAY){ currentEndpoint=preferredRelay; needSendP2pPing=false; }else if(allowP2p){ SendPublicEndpointsRequest(); } CBufferOutputStream s(4); s.WriteInt32(dataSavingMode ? INIT_FLAG_DATA_SAVING_ENABLED : 0); SendPacketReliably(PKT_NETWORK_CHANGED, s.GetBuffer(), s.GetLength(), 1, 20); }else if(request==4){ if(echoCanceller) echoCanceller->Enable(param==1); } } char* CVoIPController::GetVersion(){ return LIBTGVOIP_VERSION; } int64_t CVoIPController::GetPreferredRelayID(){ if(preferredRelay) return preferredRelay->id; return 0; } int CVoIPController::GetLastError(){ return lastError; } void CVoIPController::GetStats(voip_stats_t *stats){ memcpy(stats, &this->stats, sizeof(voip_stats_t)); }