mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
SRT: Build SRT from source by SRS. 4.0.115
This commit is contained in:
parent
262f0fc8c8
commit
90f1b482ab
115 changed files with 44513 additions and 19 deletions
360
trunk/3rdparty/srt-1-fit/srtcore/ATTIC/ccc.cpp
vendored
Normal file
360
trunk/3rdparty/srt-1-fit/srtcore/ATTIC/ccc.cpp
vendored
Normal file
|
@ -0,0 +1,360 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Yunhong Gu, last updated 02/21/2013
|
||||
modified by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#include "core.h"
|
||||
#include "ccc.h"
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
CCC::CCC():
|
||||
m_iSYNInterval(CUDT::m_iSYNInterval),
|
||||
m_dPktSndPeriod(1.0),
|
||||
m_dCWndSize(16.0),
|
||||
m_iBandwidth(),
|
||||
m_dMaxCWndSize(),
|
||||
m_iMSS(),
|
||||
m_iSndCurrSeqNo(),
|
||||
m_iRcvRate(),
|
||||
m_iRTT(),
|
||||
m_pcParam(NULL),
|
||||
m_iPSize(0),
|
||||
m_UDT(),
|
||||
m_iACKPeriod(0),
|
||||
m_iACKInterval(0),
|
||||
m_bUserDefinedRTO(false),
|
||||
m_iRTO(-1),
|
||||
m_PerfInfo()
|
||||
{
|
||||
}
|
||||
|
||||
CCC::~CCC()
|
||||
{
|
||||
delete [] m_pcParam;
|
||||
}
|
||||
|
||||
void CCC::setACKTimer(int msINT)
|
||||
{
|
||||
m_iACKPeriod = msINT > m_iSYNInterval ? m_iSYNInterval : msINT;
|
||||
}
|
||||
|
||||
void CCC::setACKInterval(int pktINT)
|
||||
{
|
||||
m_iACKInterval = pktINT;
|
||||
}
|
||||
|
||||
void CCC::setRTO(int usRTO)
|
||||
{
|
||||
m_bUserDefinedRTO = true;
|
||||
m_iRTO = usRTO;
|
||||
}
|
||||
|
||||
void CCC::sendCustomMsg(CPacket& pkt) const
|
||||
{
|
||||
CUDT* u = CUDT::getUDTHandle(m_UDT);
|
||||
|
||||
if (NULL != u)
|
||||
{
|
||||
pkt.m_iID = u->m_PeerID;
|
||||
#ifdef SRT_ENABLE_CTRLTSTAMP
|
||||
pkt.m_iTimeStamp = int(CTimer::getTime() - u->m_StartTime);
|
||||
#endif
|
||||
u->m_pSndQueue->sendto(u->m_pPeerAddr, pkt);
|
||||
}
|
||||
}
|
||||
|
||||
const CPerfMon* CCC::getPerfInfo()
|
||||
{
|
||||
try
|
||||
{
|
||||
CUDT* u = CUDT::getUDTHandle(m_UDT);
|
||||
if (NULL != u)
|
||||
u->sample(&m_PerfInfo, false);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &m_PerfInfo;
|
||||
}
|
||||
|
||||
void CCC::setMSS(int mss)
|
||||
{
|
||||
m_iMSS = mss;
|
||||
}
|
||||
|
||||
void CCC::setBandwidth(int bw)
|
||||
{
|
||||
m_iBandwidth = bw;
|
||||
}
|
||||
|
||||
void CCC::setSndCurrSeqNo(int32_t seqno)
|
||||
{
|
||||
m_iSndCurrSeqNo = seqno;
|
||||
}
|
||||
|
||||
void CCC::setRcvRate(int rcvrate)
|
||||
{
|
||||
m_iRcvRate = rcvrate;
|
||||
}
|
||||
|
||||
void CCC::setMaxCWndSize(int cwnd)
|
||||
{
|
||||
m_dMaxCWndSize = cwnd;
|
||||
}
|
||||
|
||||
void CCC::setRTT(int rtt)
|
||||
{
|
||||
m_iRTT = rtt;
|
||||
}
|
||||
|
||||
void CCC::setUserParam(const char* param, int size)
|
||||
{
|
||||
delete [] m_pcParam;
|
||||
m_pcParam = new char[size];
|
||||
memcpy(m_pcParam, param, size);
|
||||
m_iPSize = size;
|
||||
}
|
||||
|
||||
//
|
||||
CUDTCC::CUDTCC():
|
||||
m_iRCInterval(),
|
||||
m_LastRCTime(),
|
||||
m_bSlowStart(),
|
||||
m_iLastAck(),
|
||||
m_bLoss(),
|
||||
m_iLastDecSeq(),
|
||||
m_dLastDecPeriod(),
|
||||
m_iNAKCount(),
|
||||
m_iDecRandom(),
|
||||
m_iAvgNAKNum(),
|
||||
m_iDecCount()
|
||||
{
|
||||
}
|
||||
|
||||
void CUDTCC::init()
|
||||
{
|
||||
m_iRCInterval = m_iSYNInterval;
|
||||
m_LastRCTime = CTimer::getTime();
|
||||
setACKTimer(m_iRCInterval);
|
||||
|
||||
m_bSlowStart = true;
|
||||
m_iLastAck = m_iSndCurrSeqNo;
|
||||
m_bLoss = false;
|
||||
m_iLastDecSeq = CSeqNo::decseq(m_iLastAck);
|
||||
m_dLastDecPeriod = 1;
|
||||
m_iAvgNAKNum = 0;
|
||||
m_iNAKCount = 0;
|
||||
m_iDecRandom = 1;
|
||||
|
||||
m_dCWndSize = 16;
|
||||
m_dPktSndPeriod = 1;
|
||||
}
|
||||
|
||||
void CUDTCC::onACK(int32_t ack)
|
||||
{
|
||||
int64_t B = 0;
|
||||
double inc = 0;
|
||||
// Note: 1/24/2012
|
||||
// The minimum increase parameter is increased from "1.0 / m_iMSS" to 0.01
|
||||
// because the original was too small and caused sending rate to stay at low level
|
||||
// for long time.
|
||||
const double min_inc = 0.01;
|
||||
|
||||
uint64_t currtime = CTimer::getTime();
|
||||
if (currtime - m_LastRCTime < (uint64_t)m_iRCInterval)
|
||||
return;
|
||||
|
||||
m_LastRCTime = currtime;
|
||||
|
||||
#ifdef SRT_ENABLE_BSTATS
|
||||
//m_iRcvRate is bytes/sec
|
||||
if (m_bSlowStart)
|
||||
{
|
||||
m_dCWndSize += CSeqNo::seqlen(m_iLastAck, ack);
|
||||
m_iLastAck = ack;
|
||||
|
||||
if (m_dCWndSize > m_dMaxCWndSize)
|
||||
{
|
||||
m_bSlowStart = false;
|
||||
if (m_iRcvRate > 0)
|
||||
m_dPktSndPeriod = 1000000.0 / ((m_iRcvRate + m_iMSS - 1) / m_iMSS);
|
||||
else
|
||||
m_dPktSndPeriod = (m_iRTT + m_iRCInterval) / m_dCWndSize;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_dCWndSize = ((m_iRcvRate + m_iMSS -1) / m_iMSS) / 1000000.0 * (m_iRTT + m_iRCInterval) + 16;
|
||||
#else
|
||||
if (m_bSlowStart)
|
||||
{
|
||||
m_dCWndSize += CSeqNo::seqlen(m_iLastAck, ack);
|
||||
m_iLastAck = ack;
|
||||
|
||||
if (m_dCWndSize > m_dMaxCWndSize)
|
||||
{
|
||||
m_bSlowStart = false;
|
||||
if (m_iRcvRate > 0)
|
||||
m_dPktSndPeriod = 1000000.0 / m_iRcvRate;
|
||||
else
|
||||
m_dPktSndPeriod = (m_iRTT + m_iRCInterval) / m_dCWndSize;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_dCWndSize = m_iRcvRate / 1000000.0 * (m_iRTT + m_iRCInterval) + 16;
|
||||
#endif
|
||||
|
||||
// During Slow Start, no rate increase
|
||||
if (m_bSlowStart)
|
||||
return;
|
||||
|
||||
if (m_bLoss)
|
||||
{
|
||||
m_bLoss = false;
|
||||
return;
|
||||
}
|
||||
|
||||
//m_iBandwidth is pkts/sec
|
||||
B = (int64_t)(m_iBandwidth - 1000000.0 / m_dPktSndPeriod);
|
||||
if ((m_dPktSndPeriod > m_dLastDecPeriod) && ((m_iBandwidth / 9) < B))
|
||||
B = m_iBandwidth / 9;
|
||||
if (B <= 0)
|
||||
inc = min_inc;
|
||||
else
|
||||
{
|
||||
// inc = max(10 ^ ceil(log10( B * MSS * 8 ) * Beta / MSS, 1/MSS)
|
||||
// Beta = 1.5 * 10^(-6)
|
||||
|
||||
inc = pow(10.0, ceil(log10(B * m_iMSS * 8.0))) * 0.0000015 / m_iMSS;
|
||||
|
||||
if (inc < min_inc)
|
||||
inc = min_inc;
|
||||
}
|
||||
|
||||
m_dPktSndPeriod = (m_dPktSndPeriod * m_iRCInterval) / (m_dPktSndPeriod * inc + m_iRCInterval);
|
||||
}
|
||||
|
||||
void CUDTCC::onLoss(const int32_t* losslist, int)
|
||||
{
|
||||
//Slow Start stopped, if it hasn't yet
|
||||
if (m_bSlowStart)
|
||||
{
|
||||
m_bSlowStart = false;
|
||||
if (m_iRcvRate > 0)
|
||||
{
|
||||
// Set the sending rate to the receiving rate.
|
||||
#ifdef SRT_ENABLE_BSTATS
|
||||
//Need average packet size here for better send period
|
||||
m_dPktSndPeriod = 1000000.0 / ((m_iRcvRate + m_iMSS - 1) / m_iMSS);
|
||||
#else
|
||||
m_dPktSndPeriod = 1000000.0 / m_iRcvRate;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
// If no receiving rate is observed, we have to compute the sending
|
||||
// rate according to the current window size, and decrease it
|
||||
// using the method below.
|
||||
m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval);
|
||||
}
|
||||
|
||||
m_bLoss = true;
|
||||
|
||||
if (CSeqNo::seqcmp(losslist[0] & 0x7FFFFFFF, m_iLastDecSeq) > 0)
|
||||
{
|
||||
m_dLastDecPeriod = m_dPktSndPeriod;
|
||||
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.125);
|
||||
|
||||
m_iAvgNAKNum = (int)ceil(m_iAvgNAKNum * 0.875 + m_iNAKCount * 0.125);
|
||||
m_iNAKCount = 1;
|
||||
m_iDecCount = 1;
|
||||
|
||||
m_iLastDecSeq = m_iSndCurrSeqNo;
|
||||
|
||||
// remove global synchronization using randomization
|
||||
srand(m_iLastDecSeq);
|
||||
m_iDecRandom = (int)ceil(m_iAvgNAKNum * (double(rand()) / RAND_MAX));
|
||||
if (m_iDecRandom < 1)
|
||||
m_iDecRandom = 1;
|
||||
}
|
||||
else if ((m_iDecCount ++ < 5) && (0 == (++ m_iNAKCount % m_iDecRandom)))
|
||||
{
|
||||
// 0.875^5 = 0.51, rate should not be decreased by more than half within a congestion period
|
||||
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.125);
|
||||
m_iLastDecSeq = m_iSndCurrSeqNo;
|
||||
}
|
||||
}
|
||||
|
||||
void CUDTCC::onTimeout()
|
||||
{
|
||||
if (m_bSlowStart)
|
||||
{
|
||||
m_bSlowStart = false;
|
||||
if (m_iRcvRate > 0)
|
||||
#ifdef SRT_ENABLE_BSTATS
|
||||
// Need average packet size here
|
||||
m_dPktSndPeriod = 1000000.0 / ((m_iRcvRate + m_iMSS - 1) / m_iMSS);
|
||||
#else
|
||||
m_dPktSndPeriod = 1000000.0 / m_iRcvRate;
|
||||
#endif
|
||||
else
|
||||
m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
m_dLastDecPeriod = m_dPktSndPeriod;
|
||||
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 2);
|
||||
m_iLastDecSeq = m_iLastAck;
|
||||
*/
|
||||
}
|
||||
}
|
219
trunk/3rdparty/srt-1-fit/srtcore/ATTIC/ccc.h
vendored
Normal file
219
trunk/3rdparty/srt-1-fit/srtcore/ATTIC/ccc.h
vendored
Normal file
|
@ -0,0 +1,219 @@
|
|||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Yunhong Gu, last updated 02/28/2012
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __UDT_CCC_H__
|
||||
#define __UDT_CCC_H__
|
||||
|
||||
|
||||
#include "udt.h"
|
||||
#include "packet.h"
|
||||
|
||||
|
||||
class UDT_API CCC
|
||||
{
|
||||
friend class CUDT;
|
||||
|
||||
public:
|
||||
CCC();
|
||||
virtual ~CCC();
|
||||
|
||||
private:
|
||||
CCC(const CCC&);
|
||||
CCC& operator=(const CCC&) {return *this;}
|
||||
|
||||
public:
|
||||
|
||||
/// Callback function to be called (only) at the start of a UDT connection.
|
||||
/// note that this is different from CCC(), which is always called.
|
||||
|
||||
virtual void init() {}
|
||||
|
||||
/// Callback function to be called when a UDT connection is closed.
|
||||
|
||||
virtual void close() {}
|
||||
|
||||
/// Callback function to be called when an ACK packet is received.
|
||||
/// @param [in] ackno the data sequence number acknowledged by this ACK.
|
||||
|
||||
virtual void onACK(int32_t) {}
|
||||
|
||||
/// Callback function to be called when a loss report is received.
|
||||
/// @param [in] losslist list of sequence number of packets, in the format describled in packet.cpp.
|
||||
/// @param [in] size length of the loss list.
|
||||
|
||||
virtual void onLoss(const int32_t*, int) {}
|
||||
|
||||
/// Callback function to be called when a timeout event occurs.
|
||||
|
||||
virtual void onTimeout() {}
|
||||
|
||||
/// Callback function to be called when a data is sent.
|
||||
/// @param [in] seqno the data sequence number.
|
||||
/// @param [in] size the payload size.
|
||||
|
||||
virtual void onPktSent(const CPacket*) {}
|
||||
|
||||
/// Callback function to be called when a data is received.
|
||||
/// @param [in] seqno the data sequence number.
|
||||
/// @param [in] size the payload size.
|
||||
|
||||
virtual void onPktReceived(const CPacket*) {}
|
||||
|
||||
/// Callback function to Process a user defined packet.
|
||||
/// @param [in] pkt the user defined packet.
|
||||
|
||||
virtual void processCustomMsg(const CPacket*) {}
|
||||
|
||||
protected:
|
||||
|
||||
/// Set periodical acknowldging and the ACK period.
|
||||
/// @param [in] msINT the period to send an ACK.
|
||||
|
||||
void setACKTimer(int msINT);
|
||||
|
||||
/// Set packet-based acknowldging and the number of packets to send an ACK.
|
||||
/// @param [in] pktINT the number of packets to send an ACK.
|
||||
|
||||
void setACKInterval(int pktINT);
|
||||
|
||||
/// Set RTO value.
|
||||
/// @param [in] msRTO RTO in macroseconds.
|
||||
|
||||
void setRTO(int usRTO);
|
||||
|
||||
/// Send a user defined control packet.
|
||||
/// @param [in] pkt user defined packet.
|
||||
|
||||
void sendCustomMsg(CPacket& pkt) const;
|
||||
|
||||
/// retrieve performance information.
|
||||
/// @return Pointer to a performance info structure.
|
||||
|
||||
const CPerfMon* getPerfInfo();
|
||||
|
||||
/// Set user defined parameters.
|
||||
/// @param [in] param the paramters in one buffer.
|
||||
/// @param [in] size the size of the buffer.
|
||||
|
||||
void setUserParam(const char* param, int size);
|
||||
|
||||
private:
|
||||
void setMSS(int mss);
|
||||
void setMaxCWndSize(int cwnd);
|
||||
void setBandwidth(int bw);
|
||||
void setSndCurrSeqNo(int32_t seqno);
|
||||
void setRcvRate(int rcvrate);
|
||||
void setRTT(int rtt);
|
||||
|
||||
protected:
|
||||
const int32_t& m_iSYNInterval; // UDT constant parameter, SYN
|
||||
|
||||
double m_dPktSndPeriod; // Packet sending period, in microseconds
|
||||
double m_dCWndSize; // Congestion window size, in packets
|
||||
|
||||
int m_iBandwidth; // estimated bandwidth, packets per second
|
||||
double m_dMaxCWndSize; // maximum cwnd size, in packets
|
||||
|
||||
int m_iMSS; // Maximum Packet Size, including all packet headers
|
||||
int32_t m_iSndCurrSeqNo; // current maximum seq no sent out
|
||||
int m_iRcvRate; // packet arrive rate at receiver side, packets per second
|
||||
int m_iRTT; // current estimated RTT, microsecond
|
||||
|
||||
char* m_pcParam; // user defined parameter
|
||||
int m_iPSize; // size of m_pcParam
|
||||
|
||||
private:
|
||||
UDTSOCKET m_UDT; // The UDT entity that this congestion control algorithm is bound to
|
||||
|
||||
int m_iACKPeriod; // Periodical timer to send an ACK, in milliseconds
|
||||
int m_iACKInterval; // How many packets to send one ACK, in packets
|
||||
|
||||
bool m_bUserDefinedRTO; // if the RTO value is defined by users
|
||||
int m_iRTO; // RTO value, microseconds
|
||||
|
||||
CPerfMon m_PerfInfo; // protocol statistics information
|
||||
};
|
||||
|
||||
class CCCVirtualFactory
|
||||
{
|
||||
public:
|
||||
virtual ~CCCVirtualFactory() {}
|
||||
|
||||
virtual CCC* create() = 0;
|
||||
virtual CCCVirtualFactory* clone() = 0;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class CCCFactory: public CCCVirtualFactory
|
||||
{
|
||||
public:
|
||||
virtual ~CCCFactory() {}
|
||||
|
||||
virtual CCC* create() {return new T;}
|
||||
virtual CCCVirtualFactory* clone() {return new CCCFactory<T>;}
|
||||
};
|
||||
|
||||
class CUDTCC: public CCC
|
||||
{
|
||||
public:
|
||||
CUDTCC();
|
||||
|
||||
public:
|
||||
virtual void init();
|
||||
virtual void onACK(int32_t);
|
||||
virtual void onLoss(const int32_t*, int);
|
||||
virtual void onTimeout();
|
||||
|
||||
private:
|
||||
int m_iRCInterval; // UDT Rate control interval
|
||||
uint64_t m_LastRCTime; // last rate increase time
|
||||
bool m_bSlowStart; // if in slow start phase
|
||||
int32_t m_iLastAck; // last ACKed seq no
|
||||
bool m_bLoss; // if loss happened since last rate increase
|
||||
int32_t m_iLastDecSeq; // max pkt seq no sent out when last decrease happened
|
||||
double m_dLastDecPeriod; // value of pktsndperiod when last decrease happened
|
||||
int m_iNAKCount; // NAK counter
|
||||
int m_iDecRandom; // random threshold on decrease by number of loss events
|
||||
int m_iAvgNAKNum; // average number of NAKs per congestion
|
||||
int m_iDecCount; // number of decreases in a congestion epoch
|
||||
};
|
||||
|
||||
#endif
|
75
trunk/3rdparty/srt-1-fit/srtcore/README.md
vendored
Normal file
75
trunk/3rdparty/srt-1-fit/srtcore/README.md
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
SRT Core
|
||||
========
|
||||
|
||||
These files are contents of the SRT library. Beside files that are used exclusively
|
||||
and internally by the library, this directory also contains:
|
||||
|
||||
- common files: usually header files, which can be used also by other projects,
|
||||
even if they don't link against SRT
|
||||
|
||||
- public and protected header files - header files for the library, which will
|
||||
be picked up from here
|
||||
|
||||
Which header files are public, protected and private, it's defined in the manifest
|
||||
file together with all source files that the SRT library comprises of: `filelist.maf`.
|
||||
|
||||
|
||||
Common files
|
||||
============
|
||||
|
||||
This directory holds the files that may be used separately by both SRT library
|
||||
itself and the internal applications.
|
||||
|
||||
Source files are added to SRT library, so apps don't have to use them. However
|
||||
these source files might be used by some internal applications that do not
|
||||
link against SRT library.
|
||||
|
||||
Header files contained here might be required by internal applications no
|
||||
matter if they link against SRT or not. They are here because simultaneously
|
||||
they are used also by the SRT library.
|
||||
|
||||
|
||||
Utilities
|
||||
=========
|
||||
|
||||
1. threadname.h
|
||||
|
||||
This is a utility that is useful for debugging and it allows a thread to be given
|
||||
a name. This name is used in the logging messages, as well as you can see it also
|
||||
inside the debugger.
|
||||
|
||||
This is currently supported only on Linux; some more portable and more reliable
|
||||
way is needed.
|
||||
|
||||
2. utilities.h
|
||||
|
||||
A set of various reusable components, all defined as C++ classes or C++ inline
|
||||
functions.
|
||||
|
||||
3. `netinet_any.h`
|
||||
|
||||
This defines a `sockaddr_any` type, which simplifies dealing with the BSD socket API
|
||||
using `sockaddr`, `sockaddr_in` and `sockaddr_in6` structures.
|
||||
|
||||
|
||||
Compat and portability
|
||||
======================
|
||||
|
||||
1. `srt_compat.h`
|
||||
|
||||
This part contains some portability problem resolutions, including:
|
||||
- `strerror` in a version that is both portable and thread safe
|
||||
- `localtime` in a version that is both portable and thread safe
|
||||
|
||||
2. win directory
|
||||
|
||||
This contains various header files that are used on Windows platform only.
|
||||
They provide various facilities available OOTB on POSIX systems.
|
||||
|
||||
3. `platform_sys.h`
|
||||
|
||||
This is a file that is responsible to include whatever system include
|
||||
files must be included for whatever system API must be provided for
|
||||
the needs of SRT library. This is a part of public headers.
|
||||
|
||||
|
3245
trunk/3rdparty/srt-1-fit/srtcore/api.cpp
vendored
Normal file
3245
trunk/3rdparty/srt-1-fit/srtcore/api.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
300
trunk/3rdparty/srt-1-fit/srtcore/api.h
vendored
Normal file
300
trunk/3rdparty/srt-1-fit/srtcore/api.h
vendored
Normal file
|
@ -0,0 +1,300 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2010, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Yunhong Gu, last updated 09/28/2010
|
||||
modified by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __UDT_API_H__
|
||||
#define __UDT_API_H__
|
||||
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "netinet_any.h"
|
||||
#include "udt.h"
|
||||
#include "packet.h"
|
||||
#include "queue.h"
|
||||
#include "cache.h"
|
||||
#include "epoll.h"
|
||||
#include "handshake.h"
|
||||
|
||||
class CUDT;
|
||||
|
||||
class CUDTSocket
|
||||
{
|
||||
public:
|
||||
CUDTSocket();
|
||||
~CUDTSocket();
|
||||
|
||||
SRT_SOCKSTATUS m_Status; //< current socket state
|
||||
|
||||
/// Time when the socket is closed.
|
||||
/// When the socket is closed, it is not removed immediately from the list
|
||||
/// of sockets in order to prevent other methods from accessing invalid address.
|
||||
/// A timer is started and the socket will be removed after approximately
|
||||
/// 1 second (see CUDTUnited::checkBrokenSockets()).
|
||||
uint64_t m_ClosureTimeStamp;
|
||||
|
||||
int m_iIPversion; //< IP version
|
||||
sockaddr* m_pSelfAddr; //< pointer to the local address of the socket
|
||||
sockaddr* m_pPeerAddr; //< pointer to the peer address of the socket
|
||||
|
||||
SRTSOCKET m_SocketID; //< socket ID
|
||||
SRTSOCKET m_ListenSocket; //< ID of the listener socket; 0 means this is an independent socket
|
||||
|
||||
SRTSOCKET m_PeerID; //< peer socket ID
|
||||
int32_t m_iISN; //< initial sequence number, used to tell different connection from same IP:port
|
||||
|
||||
CUDT* m_pUDT; //< pointer to the UDT entity
|
||||
|
||||
std::set<SRTSOCKET>* m_pQueuedSockets; //< set of connections waiting for accept()
|
||||
std::set<SRTSOCKET>* m_pAcceptSockets; //< set of accept()ed connections
|
||||
|
||||
pthread_cond_t m_AcceptCond; //< used to block "accept" call
|
||||
pthread_mutex_t m_AcceptLock; //< mutex associated to m_AcceptCond
|
||||
|
||||
unsigned int m_uiBackLog; //< maximum number of connections in queue
|
||||
|
||||
int m_iMuxID; //< multiplexer ID
|
||||
|
||||
pthread_mutex_t m_ControlLock; //< lock this socket exclusively for control APIs: bind/listen/connect
|
||||
|
||||
static int64_t getPeerSpec(SRTSOCKET id, int32_t isn)
|
||||
{
|
||||
return (id << 30) + isn;
|
||||
}
|
||||
int64_t getPeerSpec()
|
||||
{
|
||||
return getPeerSpec(m_PeerID, m_iISN);
|
||||
}
|
||||
|
||||
private:
|
||||
CUDTSocket(const CUDTSocket&);
|
||||
CUDTSocket& operator=(const CUDTSocket&);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CUDTUnited
|
||||
{
|
||||
friend class CUDT;
|
||||
friend class CRendezvousQueue;
|
||||
|
||||
public:
|
||||
CUDTUnited();
|
||||
~CUDTUnited();
|
||||
|
||||
public:
|
||||
|
||||
static std::string CONID(SRTSOCKET sock);
|
||||
|
||||
/// initialize the UDT library.
|
||||
/// @return 0 if success, otherwise -1 is returned.
|
||||
|
||||
int startup();
|
||||
|
||||
/// release the UDT library.
|
||||
/// @return 0 if success, otherwise -1 is returned.
|
||||
|
||||
int cleanup();
|
||||
|
||||
/// Create a new UDT socket.
|
||||
/// @param [in] af IP version, IPv4 (AF_INET) or IPv6 (AF_INET6).
|
||||
/// @param [in] type (ignored)
|
||||
/// @return The new UDT socket ID, or INVALID_SOCK.
|
||||
|
||||
SRTSOCKET newSocket(int af, int );
|
||||
|
||||
/// Create a new UDT connection.
|
||||
/// @param [in] listen the listening UDT socket;
|
||||
/// @param [in] peer peer address.
|
||||
/// @param [in,out] hs handshake information from peer side (in), negotiated value (out);
|
||||
/// @return If the new connection is successfully created: 1 success, 0 already exist, -1 error.
|
||||
|
||||
int newConnection(const SRTSOCKET listen, const sockaddr* peer, CHandShake* hs, const CPacket& hspkt,
|
||||
ref_t<SRT_REJECT_REASON> r_error);
|
||||
|
||||
int installAcceptHook(const SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq);
|
||||
|
||||
/// look up the UDT entity according to its ID.
|
||||
/// @param [in] u the UDT socket ID.
|
||||
/// @return Pointer to the UDT entity.
|
||||
|
||||
CUDT* lookup(const SRTSOCKET u);
|
||||
|
||||
/// Check the status of the UDT socket.
|
||||
/// @param [in] u the UDT socket ID.
|
||||
/// @return UDT socket status, or NONEXIST if not found.
|
||||
|
||||
SRT_SOCKSTATUS getStatus(const SRTSOCKET u);
|
||||
|
||||
// socket APIs
|
||||
|
||||
int bind(const SRTSOCKET u, const sockaddr* name, int namelen);
|
||||
int bind(const SRTSOCKET u, UDPSOCKET udpsock);
|
||||
int listen(const SRTSOCKET u, int backlog);
|
||||
SRTSOCKET accept(const SRTSOCKET listen, sockaddr* addr, int* addrlen);
|
||||
int connect(const SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn);
|
||||
int close(const SRTSOCKET u);
|
||||
int getpeername(const SRTSOCKET u, sockaddr* name, int* namelen);
|
||||
int getsockname(const SRTSOCKET u, sockaddr* name, int* namelen);
|
||||
int select(ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout);
|
||||
int selectEx(const std::vector<SRTSOCKET>& fds, std::vector<SRTSOCKET>* readfds, std::vector<SRTSOCKET>* writefds, std::vector<SRTSOCKET>* exceptfds, int64_t msTimeOut);
|
||||
int epoll_create();
|
||||
int epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL);
|
||||
int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL);
|
||||
int epoll_remove_usock(const int eid, const SRTSOCKET u);
|
||||
int epoll_remove_ssock(const int eid, const SYSSOCKET s);
|
||||
int epoll_update_usock(const int eid, const SRTSOCKET u, const int* events = NULL);
|
||||
int epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL);
|
||||
int epoll_wait(const int eid, std::set<SRTSOCKET>* readfds, std::set<SRTSOCKET>* writefds, int64_t msTimeOut, std::set<SYSSOCKET>* lrfds = NULL, std::set<SYSSOCKET>* lwfds = NULL);
|
||||
int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut);
|
||||
int32_t epoll_set(const int eid, int32_t flags);
|
||||
int epoll_release(const int eid);
|
||||
|
||||
/// record the UDT exception.
|
||||
/// @param [in] e pointer to a UDT exception instance.
|
||||
|
||||
void setError(CUDTException* e);
|
||||
|
||||
/// look up the most recent UDT exception.
|
||||
/// @return pointer to a UDT exception instance.
|
||||
|
||||
CUDTException* getError();
|
||||
|
||||
private:
|
||||
// void init();
|
||||
|
||||
private:
|
||||
std::map<SRTSOCKET, CUDTSocket*> m_Sockets; // stores all the socket structures
|
||||
|
||||
pthread_mutex_t m_ControlLock; // used to synchronize UDT API
|
||||
|
||||
pthread_mutex_t m_IDLock; // used to synchronize ID generation
|
||||
SRTSOCKET m_SocketIDGenerator; // seed to generate a new unique socket ID
|
||||
|
||||
std::map<int64_t, std::set<SRTSOCKET> > m_PeerRec;// record sockets from peers to avoid repeated connection request, int64_t = (socker_id << 30) + isn
|
||||
|
||||
private:
|
||||
pthread_key_t m_TLSError; // thread local error record (last error)
|
||||
static void TLSDestroy(void* e) {if (NULL != e) delete (CUDTException*)e;}
|
||||
|
||||
private:
|
||||
CUDTSocket* locate(const SRTSOCKET u);
|
||||
CUDTSocket* locate(const sockaddr* peer, const SRTSOCKET id, int32_t isn);
|
||||
void updateMux(CUDTSocket* s, const sockaddr* addr = NULL, const UDPSOCKET* = NULL);
|
||||
void updateListenerMux(CUDTSocket* s, const CUDTSocket* ls);
|
||||
|
||||
private:
|
||||
std::map<int, CMultiplexer> m_mMultiplexer; // UDP multiplexer
|
||||
pthread_mutex_t m_MultiplexerLock;
|
||||
|
||||
private:
|
||||
CCache<CInfoBlock>* m_pCache; // UDT network information cache
|
||||
|
||||
private:
|
||||
volatile bool m_bClosing;
|
||||
pthread_mutex_t m_GCStopLock;
|
||||
pthread_cond_t m_GCStopCond;
|
||||
|
||||
pthread_mutex_t m_InitLock;
|
||||
int m_iInstanceCount; // number of startup() called by application
|
||||
bool m_bGCStatus; // if the GC thread is working (true)
|
||||
|
||||
pthread_t m_GCThread;
|
||||
static void* garbageCollect(void*);
|
||||
|
||||
std::map<SRTSOCKET, CUDTSocket*> m_ClosedSockets; // temporarily store closed sockets
|
||||
|
||||
void checkBrokenSockets();
|
||||
void removeSocket(const SRTSOCKET u);
|
||||
|
||||
CEPoll m_EPoll; // handling epoll data structures and events
|
||||
|
||||
private:
|
||||
CUDTUnited(const CUDTUnited&);
|
||||
CUDTUnited& operator=(const CUDTUnited&);
|
||||
};
|
||||
|
||||
// Debug support
|
||||
inline std::string SockaddrToString(const sockaddr* sadr)
|
||||
{
|
||||
void* addr =
|
||||
sadr->sa_family == AF_INET ?
|
||||
(void*)&((sockaddr_in*)sadr)->sin_addr
|
||||
: sadr->sa_family == AF_INET6 ?
|
||||
(void*)&((sockaddr_in6*)sadr)->sin6_addr
|
||||
: 0;
|
||||
// (cast to (void*) is required because otherwise the 2-3 arguments
|
||||
// of ?: operator would have different types, which isn't allowed in C++.
|
||||
if ( !addr )
|
||||
return "unknown:0";
|
||||
|
||||
std::ostringstream output;
|
||||
char hostbuf[1024];
|
||||
int flags;
|
||||
|
||||
#if ENABLE_GETNAMEINFO
|
||||
flags = NI_NAMEREQD;
|
||||
#else
|
||||
flags = NI_NUMERICHOST | NI_NUMERICSERV;
|
||||
#endif
|
||||
|
||||
if (!getnameinfo(sadr, sizeof(*sadr), hostbuf, 1024, NULL, 0, flags))
|
||||
{
|
||||
output << hostbuf;
|
||||
}
|
||||
|
||||
output << ":" << ntohs(((sockaddr_in*)sadr)->sin_port); // TRICK: sin_port and sin6_port have the same offset and size
|
||||
return output.str();
|
||||
}
|
||||
|
||||
|
||||
#endif
|
1955
trunk/3rdparty/srt-1-fit/srtcore/buffer.cpp
vendored
Normal file
1955
trunk/3rdparty/srt-1-fit/srtcore/buffer.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
510
trunk/3rdparty/srt-1-fit/srtcore/buffer.h
vendored
Normal file
510
trunk/3rdparty/srt-1-fit/srtcore/buffer.h
vendored
Normal file
|
@ -0,0 +1,510 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Yunhong Gu, last updated 05/05/2009
|
||||
modified by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __UDT_BUFFER_H__
|
||||
#define __UDT_BUFFER_H__
|
||||
|
||||
|
||||
#include "udt.h"
|
||||
#include "list.h"
|
||||
#include "queue.h"
|
||||
#include "utilities.h"
|
||||
#include <fstream>
|
||||
|
||||
class CSndBuffer
|
||||
{
|
||||
public:
|
||||
|
||||
// XXX There's currently no way to access the socket ID set for
|
||||
// whatever the buffer is currently working for. Required to find
|
||||
// some way to do this, possibly by having a "reverse pointer".
|
||||
// Currently just "unimplemented".
|
||||
std::string CONID() const { return ""; }
|
||||
|
||||
CSndBuffer(int size = 32, int mss = 1500);
|
||||
~CSndBuffer();
|
||||
|
||||
public:
|
||||
|
||||
/// Insert a user buffer into the sending list.
|
||||
/// @param [in] data pointer to the user data block.
|
||||
/// @param [in] len size of the block.
|
||||
/// @param [in] ttl time to live in milliseconds
|
||||
/// @param [in] order if the block should be delivered in order, for DGRAM only
|
||||
|
||||
void addBuffer(const char* data, int len, int ttl, bool order, uint64_t srctime, ref_t<int32_t> r_msgno);
|
||||
|
||||
/// Read a block of data from file and insert it into the sending list.
|
||||
/// @param [in] ifs input file stream.
|
||||
/// @param [in] len size of the block.
|
||||
/// @return actual size of data added from the file.
|
||||
|
||||
int addBufferFromFile(std::fstream& ifs, int len);
|
||||
|
||||
/// Find data position to pack a DATA packet from the furthest reading point.
|
||||
/// @param [out] data the pointer to the data position.
|
||||
/// @param [out] msgno message number of the packet.
|
||||
/// @param [out] origintime origin time stamp of the message
|
||||
/// @param [in] kflags Odd|Even crypto key flag
|
||||
/// @return Actual length of data read.
|
||||
|
||||
int readData(char** data, int32_t& msgno, uint64_t& origintime, int kflgs);
|
||||
|
||||
|
||||
/// Find data position to pack a DATA packet for a retransmission.
|
||||
/// @param [out] data the pointer to the data position.
|
||||
/// @param [in] offset offset from the last ACK point.
|
||||
/// @param [out] msgno message number of the packet.
|
||||
/// @param [out] origintime origin time stamp of the message
|
||||
/// @param [out] msglen length of the message
|
||||
/// @return Actual length of data read.
|
||||
|
||||
int readData(char** data, const int offset, int32_t& msgno, uint64_t& origintime, int& msglen);
|
||||
|
||||
/// Update the ACK point and may release/unmap/return the user data according to the flag.
|
||||
/// @param [in] offset number of packets acknowledged.
|
||||
|
||||
void ackData(int offset);
|
||||
|
||||
/// Read size of data still in the sending list.
|
||||
/// @return Current size of the data in the sending list.
|
||||
|
||||
int getCurrBufSize() const;
|
||||
|
||||
int dropLateData(int &bytes, uint64_t latetime);
|
||||
|
||||
#ifdef SRT_ENABLE_SNDBUFSZ_MAVG
|
||||
void updAvgBufSize(uint64_t time);
|
||||
int getAvgBufSize(ref_t<int> bytes, ref_t<int> timespan);
|
||||
#endif /* SRT_ENABLE_SNDBUFSZ_MAVG */
|
||||
int getCurrBufSize(ref_t<int> bytes, ref_t<int> timespan);
|
||||
|
||||
uint64_t getInRatePeriod() const { return m_InRatePeriod; }
|
||||
|
||||
/// Retrieve input bitrate in bytes per second
|
||||
int getInputRate() const { return m_iInRateBps; }
|
||||
|
||||
/// Update input rate calculation.
|
||||
/// @param [in] time current time in microseconds
|
||||
/// @param [in] pkts number of packets newly added to the buffer
|
||||
/// @param [in] bytes number of payload bytes in those newly added packets
|
||||
///
|
||||
/// @return Current size of the data in the sending list.
|
||||
void updateInputRate(uint64_t time, int pkts = 0, int bytes = 0);
|
||||
|
||||
|
||||
void resetInputRateSmpPeriod(bool disable = false)
|
||||
{
|
||||
setInputRateSmpPeriod(disable ? 0 : INPUTRATE_FAST_START_US);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void increase();
|
||||
void setInputRateSmpPeriod(int period);
|
||||
|
||||
private: // Constants
|
||||
|
||||
static const uint64_t INPUTRATE_FAST_START_US = 500000; // 500 ms
|
||||
static const uint64_t INPUTRATE_RUNNING_US = 1000000; // 1000 ms
|
||||
static const int64_t INPUTRATE_MAX_PACKETS = 2000; // ~ 21 Mbps of 1316 bytes payload
|
||||
static const int INPUTRATE_INITIAL_BYTESPS = BW_INFINITE;
|
||||
|
||||
private:
|
||||
pthread_mutex_t m_BufLock; // used to synchronize buffer operation
|
||||
|
||||
struct Block
|
||||
{
|
||||
char* m_pcData; // pointer to the data block
|
||||
int m_iLength; // length of the block
|
||||
|
||||
int32_t m_iMsgNoBitset; // message number
|
||||
uint64_t m_ullOriginTime_us; // original request time
|
||||
uint64_t m_ullSourceTime_us;
|
||||
int m_iTTL; // time to live (milliseconds)
|
||||
|
||||
Block* m_pNext; // next block
|
||||
|
||||
int32_t getMsgSeq()
|
||||
{
|
||||
// NOTE: this extracts message ID with regard to REXMIT flag.
|
||||
// This is valid only for message ID that IS GENERATED in this instance,
|
||||
// not provided by the peer. This can be otherwise sent to the peer - it doesn't matter
|
||||
// for the peer that it uses LESS bits to represent the message.
|
||||
return m_iMsgNoBitset & MSGNO_SEQ::mask;
|
||||
}
|
||||
|
||||
} *m_pBlock, *m_pFirstBlock, *m_pCurrBlock, *m_pLastBlock;
|
||||
|
||||
// m_pBlock: The head pointer
|
||||
// m_pFirstBlock: The first block
|
||||
// m_pCurrBlock: The current block
|
||||
// m_pLastBlock: The last block (if first == last, buffer is empty)
|
||||
|
||||
struct Buffer
|
||||
{
|
||||
char* m_pcData; // buffer
|
||||
int m_iSize; // size
|
||||
Buffer* m_pNext; // next buffer
|
||||
} *m_pBuffer; // physical buffer
|
||||
|
||||
int32_t m_iNextMsgNo; // next message number
|
||||
|
||||
int m_iSize; // buffer size (number of packets)
|
||||
int m_iMSS; // maximum seqment/packet size
|
||||
|
||||
int m_iCount; // number of used blocks
|
||||
|
||||
int m_iBytesCount; // number of payload bytes in queue
|
||||
uint64_t m_ullLastOriginTime_us;
|
||||
|
||||
#ifdef SRT_ENABLE_SNDBUFSZ_MAVG
|
||||
uint64_t m_LastSamplingTime;
|
||||
int m_iCountMAvg;
|
||||
int m_iBytesCountMAvg;
|
||||
int m_TimespanMAvg;
|
||||
#endif /* SRT_ENABLE_SNDBUFSZ_MAVG */
|
||||
|
||||
int m_iInRatePktsCount; // number of payload bytes added since InRateStartTime
|
||||
int m_iInRateBytesCount; // number of payload bytes added since InRateStartTime
|
||||
uint64_t m_InRateStartTime;
|
||||
uint64_t m_InRatePeriod; // usec
|
||||
int m_iInRateBps; // Input Rate in Bytes/sec
|
||||
int m_iAvgPayloadSz; // Average packet payload size
|
||||
|
||||
private:
|
||||
CSndBuffer(const CSndBuffer&);
|
||||
CSndBuffer& operator=(const CSndBuffer&);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class CRcvBuffer
|
||||
{
|
||||
public:
|
||||
|
||||
// XXX There's currently no way to access the socket ID set for
|
||||
// whatever the queue is currently working for. Required to find
|
||||
// some way to do this, possibly by having a "reverse pointer".
|
||||
// Currently just "unimplemented".
|
||||
std::string CONID() const { return ""; }
|
||||
|
||||
|
||||
/// Construct the buffer.
|
||||
/// @param [in] queue CUnitQueue that actually holds the units (packets)
|
||||
/// @param [in] bufsize_pkts in units (packets)
|
||||
CRcvBuffer(CUnitQueue* queue, int bufsize_pkts = 65536);
|
||||
~CRcvBuffer();
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/// Write data into the buffer.
|
||||
/// @param [in] unit pointer to a data unit containing new packet
|
||||
/// @param [in] offset offset from last ACK point.
|
||||
/// @return 0 is success, -1 if data is repeated.
|
||||
|
||||
int addData(CUnit* unit, int offset);
|
||||
|
||||
/// Read data into a user buffer.
|
||||
/// @param [in] data pointer to user buffer.
|
||||
/// @param [in] len length of user buffer.
|
||||
/// @return size of data read.
|
||||
|
||||
int readBuffer(char* data, int len);
|
||||
|
||||
/// Read data directly into file.
|
||||
/// @param [in] file C++ file stream.
|
||||
/// @param [in] len expected length of data to write into the file.
|
||||
/// @return size of data read.
|
||||
|
||||
int readBufferToFile(std::fstream& ofs, int len);
|
||||
|
||||
/// Update the ACK point of the buffer.
|
||||
/// @param [in] len number of units to be acknowledged.
|
||||
/// @return 1 if a user buffer is fulfilled, otherwise 0.
|
||||
|
||||
void ackData(int len);
|
||||
|
||||
/// Query how many buffer space left for data receiving.
|
||||
/// Actually only acknowledged packets, that are still in the buffer,
|
||||
/// are considered to take buffer space.
|
||||
///
|
||||
/// @return size of available buffer space (including user buffer) for data receiving.
|
||||
/// Not counting unacknowledged packets.
|
||||
|
||||
int getAvailBufSize() const;
|
||||
|
||||
/// Query how many data has been continuously received (for reading) and ready to play (tsbpdtime < now).
|
||||
/// @return size of valid (continous) data for reading.
|
||||
|
||||
int getRcvDataSize() const;
|
||||
|
||||
/// Query how many data was received and acknowledged.
|
||||
/// @param [out] bytes bytes
|
||||
/// @param [out] spantime spantime
|
||||
/// @return size in pkts of acked data.
|
||||
|
||||
int getRcvDataSize(int &bytes, int &spantime);
|
||||
#if SRT_ENABLE_RCVBUFSZ_MAVG
|
||||
|
||||
/// Query a 1 sec moving average of how many data was received and acknowledged.
|
||||
/// @param [out] bytes bytes
|
||||
/// @param [out] spantime spantime
|
||||
/// @return size in pkts of acked data.
|
||||
|
||||
int getRcvAvgDataSize(int &bytes, int &spantime);
|
||||
|
||||
/// Query how many data of the receive buffer is acknowledged.
|
||||
/// @param [in] now current time in us.
|
||||
/// @return none.
|
||||
|
||||
void updRcvAvgDataSize(uint64_t now);
|
||||
#endif /* SRT_ENABLE_RCVBUFSZ_MAVG */
|
||||
|
||||
/// Query the received average payload size.
|
||||
/// @return size (bytes) of payload size
|
||||
|
||||
int getRcvAvgPayloadSize() const;
|
||||
|
||||
|
||||
/// Mark the message to be dropped from the message list.
|
||||
/// @param [in] msgno message number.
|
||||
/// @param [in] using_rexmit_flag whether the MSGNO field uses rexmit flag (if not, one more bit is part of the msgno value)
|
||||
|
||||
void dropMsg(int32_t msgno, bool using_rexmit_flag);
|
||||
|
||||
/// read a message.
|
||||
/// @param [out] data buffer to write the message into.
|
||||
/// @param [in] len size of the buffer.
|
||||
/// @return actuall size of data read.
|
||||
|
||||
int readMsg(char* data, int len);
|
||||
|
||||
/// read a message.
|
||||
/// @param [out] data buffer to write the message into.
|
||||
/// @param [in] len size of the buffer.
|
||||
/// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay
|
||||
/// @return actuall size of data read.
|
||||
|
||||
int readMsg(char* data, int len, ref_t<SRT_MSGCTRL> mctrl);
|
||||
|
||||
/// Query if data is ready to read (tsbpdtime <= now if TsbPD is active).
|
||||
/// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay
|
||||
/// of next packet in recv buffer, ready or not.
|
||||
/// @param [out] curpktseq Sequence number of the packet if there is one ready to play
|
||||
/// @return true if ready to play, false otherwise (tsbpdtime may be !0 in
|
||||
/// both cases).
|
||||
|
||||
bool isRcvDataReady(ref_t<uint64_t> tsbpdtime, ref_t<int32_t> curpktseq);
|
||||
bool isRcvDataReady();
|
||||
bool isRcvDataAvailable()
|
||||
{
|
||||
return m_iLastAckPos != m_iStartPos;
|
||||
}
|
||||
CPacket* getRcvReadyPacket();
|
||||
|
||||
/// Set TimeStamp-Based Packet Delivery Rx Mode
|
||||
/// @param [in] timebase localtime base (uSec) of packet time stamps including buffering delay
|
||||
/// @param [in] delay aggreed TsbPD delay
|
||||
/// @return 0
|
||||
|
||||
int setRcvTsbPdMode(uint64_t timebase, uint32_t delay);
|
||||
|
||||
/// Add packet timestamp for drift caclculation and compensation
|
||||
/// @param [in] timestamp packet time stamp
|
||||
/// @param [ref] lock Mutex that should be locked for the operation
|
||||
|
||||
void addRcvTsbPdDriftSample(uint32_t timestamp, pthread_mutex_t& lock);
|
||||
|
||||
#ifdef SRT_DEBUG_TSBPD_DRIFT
|
||||
void printDriftHistogram(int64_t iDrift);
|
||||
void printDriftOffset(int tsbPdOffset, int tsbPdDriftAvg);
|
||||
#endif
|
||||
|
||||
/// Get information on the 1st message in queue.
|
||||
// Parameters (of the 1st packet queue, ready to play or not):
|
||||
/// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if none
|
||||
/// @param [out] passack true if 1st ready packet is not yet acknowleged (allowed to be delivered to the app)
|
||||
/// @param [out] skipseqno -1 or seq number of 1st unacknowledged pkt ready to play preceeded by missing packets.
|
||||
/// @retval true 1st packet ready to play (tsbpdtime <= now). Not yet acknowledged if passack == true
|
||||
/// @retval false IF tsbpdtime = 0: rcv buffer empty; ELSE:
|
||||
/// IF skipseqno != -1, packet ready to play preceeded by missing packets.;
|
||||
/// IF skipseqno == -1, no missing packet but 1st not ready to play.
|
||||
|
||||
|
||||
bool getRcvFirstMsg(ref_t<uint64_t> tsbpdtime, ref_t<bool> passack, ref_t<int32_t> skipseqno, ref_t<int32_t> curpktseq);
|
||||
|
||||
/// Update the ACK point of the buffer.
|
||||
/// @param [in] len size of data to be skip & acknowledged.
|
||||
|
||||
void skipData(int len);
|
||||
|
||||
#if ENABLE_HEAVY_LOGGING
|
||||
void reportBufferStats(); // Heavy logging Debug only
|
||||
#endif
|
||||
|
||||
private:
|
||||
/// Adjust receive queue to 1st ready to play message (tsbpdtime < now).
|
||||
// Parameters (of the 1st packet queue, ready to play or not):
|
||||
/// @param [out] tsbpdtime localtime-based (uSec) packet time stamp including buffering delay of 1st packet or 0 if none
|
||||
/// @retval true 1st packet ready to play without discontinuity (no hole)
|
||||
/// @retval false tsbpdtime = 0: no packet ready to play
|
||||
|
||||
|
||||
bool getRcvReadyMsg(ref_t<uint64_t> tsbpdtime, ref_t<int32_t> curpktseq);
|
||||
|
||||
public:
|
||||
|
||||
// (This is exposed as used publicly in logs)
|
||||
/// Get packet delivery local time base (adjusted for wrap around)
|
||||
/// @param [in] timestamp packet timestamp (relative to peer StartTime), wrapping around every ~72 min
|
||||
/// @return local delivery time (usec)
|
||||
uint64_t getTsbPdTimeBase(uint32_t timestamp_us);
|
||||
|
||||
/// Get packet local delivery time
|
||||
/// @param [in] timestamp packet timestamp (relative to peer StartTime), wrapping around every ~72 min
|
||||
/// @return local delivery time (usec)
|
||||
|
||||
public:
|
||||
uint64_t getPktTsbPdTime(uint32_t timestamp);
|
||||
int debugGetSize() const;
|
||||
bool empty() const;
|
||||
|
||||
// Required by PacketFilter facility to use as a storage
|
||||
// for provided packets
|
||||
CUnitQueue* getUnitQueue()
|
||||
{
|
||||
return m_pUnitQueue;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// thread safe bytes counter of the Recv & Ack buffer
|
||||
/// @param [in] pkts acked or removed pkts from rcv buffer (used with acked = true)
|
||||
/// @param [in] bytes number of bytes added/delete (if negative) to/from rcv buffer.
|
||||
/// @param [in] acked true when adding new pkt in RcvBuffer; false when acking/removing pkts to/from buffer
|
||||
|
||||
void countBytes(int pkts, int bytes, bool acked = false);
|
||||
|
||||
private:
|
||||
bool scanMsg(ref_t<int> start, ref_t<int> end, ref_t<bool> passack);
|
||||
|
||||
private:
|
||||
CUnit** m_pUnit; // pointer to the protocol buffer (array of CUnit* items)
|
||||
const int m_iSize; // size of the array of CUnit* items
|
||||
CUnitQueue* m_pUnitQueue; // the shared unit queue
|
||||
|
||||
int m_iStartPos; // the head position for I/O (inclusive)
|
||||
int m_iLastAckPos; // the last ACKed position (exclusive)
|
||||
// EMPTY: m_iStartPos = m_iLastAckPos FULL: m_iStartPos = m_iLastAckPos + 1
|
||||
int m_iMaxPos; // the furthest data position
|
||||
|
||||
int m_iNotch; // the starting read point of the first unit
|
||||
|
||||
pthread_mutex_t m_BytesCountLock; // used to protect counters operations
|
||||
int m_iBytesCount; // Number of payload bytes in the buffer
|
||||
int m_iAckedPktsCount; // Number of acknowledged pkts in the buffer
|
||||
int m_iAckedBytesCount; // Number of acknowledged payload bytes in the buffer
|
||||
int m_iAvgPayloadSz; // Average payload size for dropped bytes estimation
|
||||
|
||||
bool m_bTsbPdMode; // true: apply TimeStamp-Based Rx Mode
|
||||
uint32_t m_uTsbPdDelay; // aggreed delay
|
||||
uint64_t m_ullTsbPdTimeBase; // localtime base for TsbPd mode
|
||||
// Note: m_ullTsbPdTimeBase cumulates values from:
|
||||
// 1. Initial SRT_CMD_HSREQ packet returned value diff to current time:
|
||||
// == (NOW - PACKET_TIMESTAMP), at the time of HSREQ reception
|
||||
// 2. Timestamp overflow (@c CRcvBuffer::getTsbPdTimeBase), when overflow on packet detected
|
||||
// += CPacket::MAX_TIMESTAMP+1 (it's a hex round value, usually 0x1*e8).
|
||||
// 3. Time drift (CRcvBuffer::addRcvTsbPdDriftSample, executed exclusively
|
||||
// from UMSG_ACKACK handler). This is updated with (positive or negative) TSBPD_DRIFT_MAX_VALUE
|
||||
// once the value of average drift exceeds this value in whatever direction.
|
||||
// += (+/-)CRcvBuffer::TSBPD_DRIFT_MAX_VALUE
|
||||
//
|
||||
// XXX Application-supplied timestamps won't work therefore. This requires separate
|
||||
// calculation of all these things above.
|
||||
|
||||
bool m_bTsbPdWrapCheck; // true: check packet time stamp wrap around
|
||||
static const uint32_t TSBPD_WRAP_PERIOD = (30*1000000); //30 seconds (in usec)
|
||||
|
||||
static const int TSBPD_DRIFT_MAX_VALUE = 5000; // Max drift (usec) above which TsbPD Time Offset is adjusted
|
||||
static const int TSBPD_DRIFT_MAX_SAMPLES = 1000; // Number of samples (UMSG_ACKACK packets) to perform drift caclulation and compensation
|
||||
//int m_iTsbPdDrift; // recent drift in the packet time stamp
|
||||
//int64_t m_TsbPdDriftSum; // Sum of sampled drift
|
||||
//int m_iTsbPdDriftNbSamples; // Number of samples in sum and histogram
|
||||
DriftTracer<TSBPD_DRIFT_MAX_SAMPLES, TSBPD_DRIFT_MAX_VALUE> m_DriftTracer;
|
||||
#ifdef SRT_ENABLE_RCVBUFSZ_MAVG
|
||||
uint64_t m_LastSamplingTime;
|
||||
int m_TimespanMAvg;
|
||||
int m_iCountMAvg;
|
||||
int m_iBytesCountMAvg;
|
||||
#endif /* SRT_ENABLE_RCVBUFSZ_MAVG */
|
||||
#ifdef SRT_DEBUG_TSBPD_DRIFT
|
||||
int m_TsbPdDriftHisto100us[22]; // Histogram of 100us TsbPD drift (-1.0 .. +1.0 ms in 0.1ms increment)
|
||||
int m_TsbPdDriftHisto1ms[22]; // Histogram of TsbPD drift (-10.0 .. +10.0 ms, in 1.0 ms increment)
|
||||
static const int TSBPD_DRIFT_PRT_SAMPLES = 200; // Number of samples (UMSG_ACKACK packets) to print hostogram
|
||||
#endif /* SRT_DEBUG_TSBPD_DRIFT */
|
||||
|
||||
#ifdef SRT_DEBUG_TSBPD_OUTJITTER
|
||||
unsigned long m_ulPdHisto[4][10];
|
||||
#endif /* SRT_DEBUG_TSBPD_OUTJITTER */
|
||||
|
||||
private:
|
||||
CRcvBuffer();
|
||||
CRcvBuffer(const CRcvBuffer&);
|
||||
CRcvBuffer& operator=(const CRcvBuffer&);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
120
trunk/3rdparty/srt-1-fit/srtcore/cache.cpp
vendored
Normal file
120
trunk/3rdparty/srt-1-fit/srtcore/cache.cpp
vendored
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Yunhong Gu, last updated 05/05/2009
|
||||
*****************************************************************************/
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include "cache.h"
|
||||
#include "core.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
CInfoBlock& CInfoBlock::operator=(const CInfoBlock& obj)
|
||||
{
|
||||
std::copy(obj.m_piIP, obj.m_piIP + 4, m_piIP);
|
||||
m_iIPversion = obj.m_iIPversion;
|
||||
m_ullTimeStamp = obj.m_ullTimeStamp;
|
||||
m_iRTT = obj.m_iRTT;
|
||||
m_iBandwidth = obj.m_iBandwidth;
|
||||
m_iLossRate = obj.m_iLossRate;
|
||||
m_iReorderDistance = obj.m_iReorderDistance;
|
||||
m_dInterval = obj.m_dInterval;
|
||||
m_dCWnd = obj.m_dCWnd;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool CInfoBlock::operator==(const CInfoBlock& obj)
|
||||
{
|
||||
if (m_iIPversion != obj.m_iIPversion)
|
||||
return false;
|
||||
|
||||
else if (m_iIPversion == AF_INET)
|
||||
return (m_piIP[0] == obj.m_piIP[0]);
|
||||
|
||||
for (int i = 0; i < 4; ++ i)
|
||||
{
|
||||
if (m_piIP[i] != obj.m_piIP[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CInfoBlock* CInfoBlock::clone()
|
||||
{
|
||||
CInfoBlock* obj = new CInfoBlock;
|
||||
|
||||
std::copy(m_piIP, m_piIP + 4, obj->m_piIP);
|
||||
obj->m_iIPversion = m_iIPversion;
|
||||
obj->m_ullTimeStamp = m_ullTimeStamp;
|
||||
obj->m_iRTT = m_iRTT;
|
||||
obj->m_iBandwidth = m_iBandwidth;
|
||||
obj->m_iLossRate = m_iLossRate;
|
||||
obj->m_iReorderDistance = m_iReorderDistance;
|
||||
obj->m_dInterval = m_dInterval;
|
||||
obj->m_dCWnd = m_dCWnd;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
int CInfoBlock::getKey()
|
||||
{
|
||||
if (m_iIPversion == AF_INET)
|
||||
return m_piIP[0];
|
||||
|
||||
return m_piIP[0] + m_piIP[1] + m_piIP[2] + m_piIP[3];
|
||||
}
|
||||
|
||||
void CInfoBlock::convert(const sockaddr* addr, int ver, uint32_t ip[])
|
||||
{
|
||||
if (ver == AF_INET)
|
||||
{
|
||||
ip[0] = ((sockaddr_in*)addr)->sin_addr.s_addr;
|
||||
ip[1] = ip[2] = ip[3] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy((char*)ip, (char*)((sockaddr_in6*)addr)->sin6_addr.s6_addr, 16);
|
||||
}
|
||||
}
|
266
trunk/3rdparty/srt-1-fit/srtcore/cache.h
vendored
Normal file
266
trunk/3rdparty/srt-1-fit/srtcore/cache.h
vendored
Normal file
|
@ -0,0 +1,266 @@
|
|||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Yunhong Gu, last updated 01/27/2011
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __UDT_CACHE_H__
|
||||
#define __UDT_CACHE_H__
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#include "common.h"
|
||||
#include "udt.h"
|
||||
|
||||
class CCacheItem
|
||||
{
|
||||
public:
|
||||
virtual ~CCacheItem() {}
|
||||
|
||||
public:
|
||||
virtual CCacheItem& operator=(const CCacheItem&) = 0;
|
||||
|
||||
// The "==" operator SHOULD only compare key values.
|
||||
virtual bool operator==(const CCacheItem&) = 0;
|
||||
|
||||
/// get a deep copy clone of the current item
|
||||
/// @return Pointer to the new item, or NULL if failed.
|
||||
|
||||
virtual CCacheItem* clone() = 0;
|
||||
|
||||
/// get a random key value between 0 and MAX_INT to be used for the hash in cache
|
||||
/// @return A random hash key.
|
||||
|
||||
virtual int getKey() = 0;
|
||||
|
||||
// If there is any shared resources between the cache item and its clone,
|
||||
// the shared resource should be released by this function.
|
||||
virtual void release() {}
|
||||
};
|
||||
|
||||
template<typename T> class CCache
|
||||
{
|
||||
public:
|
||||
CCache(int size = 1024):
|
||||
m_iMaxSize(size),
|
||||
m_iHashSize(size * 3),
|
||||
m_iCurrSize(0)
|
||||
{
|
||||
m_vHashPtr.resize(m_iHashSize);
|
||||
CGuard::createMutex(m_Lock);
|
||||
}
|
||||
|
||||
~CCache()
|
||||
{
|
||||
clear();
|
||||
CGuard::releaseMutex(m_Lock);
|
||||
}
|
||||
|
||||
public:
|
||||
/// find the matching item in the cache.
|
||||
/// @param [in,out] data storage for the retrieved item; initially it must carry the key information
|
||||
/// @return 0 if found a match, otherwise -1.
|
||||
|
||||
int lookup(T* data)
|
||||
{
|
||||
CGuard cacheguard(m_Lock);
|
||||
|
||||
int key = data->getKey();
|
||||
if (key < 0)
|
||||
return -1;
|
||||
if (key >= m_iMaxSize)
|
||||
key %= m_iHashSize;
|
||||
|
||||
const ItemPtrList& item_list = m_vHashPtr[key];
|
||||
for (typename ItemPtrList::const_iterator i = item_list.begin(); i != item_list.end(); ++ i)
|
||||
{
|
||||
if (*data == ***i)
|
||||
{
|
||||
// copy the cached info
|
||||
*data = ***i;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// update an item in the cache, or insert one if it doesn't exist; oldest item may be removed
|
||||
/// @param [in] data the new item to updated/inserted to the cache
|
||||
/// @return 0 if success, otherwise -1.
|
||||
|
||||
int update(T* data)
|
||||
{
|
||||
CGuard cacheguard(m_Lock);
|
||||
|
||||
int key = data->getKey();
|
||||
if (key < 0)
|
||||
return -1;
|
||||
if (key >= m_iMaxSize)
|
||||
key %= m_iHashSize;
|
||||
|
||||
T* curr = NULL;
|
||||
|
||||
ItemPtrList& item_list = m_vHashPtr[key];
|
||||
for (typename ItemPtrList::iterator i = item_list.begin(); i != item_list.end(); ++ i)
|
||||
{
|
||||
if (*data == ***i)
|
||||
{
|
||||
// update the existing entry with the new value
|
||||
***i = *data;
|
||||
curr = **i;
|
||||
|
||||
// remove the current entry
|
||||
m_StorageList.erase(*i);
|
||||
item_list.erase(i);
|
||||
|
||||
// re-insert to the front
|
||||
m_StorageList.push_front(curr);
|
||||
item_list.push_front(m_StorageList.begin());
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// create new entry and insert to front
|
||||
curr = data->clone();
|
||||
m_StorageList.push_front(curr);
|
||||
item_list.push_front(m_StorageList.begin());
|
||||
|
||||
++ m_iCurrSize;
|
||||
if (m_iCurrSize >= m_iMaxSize)
|
||||
{
|
||||
// Cache overflow, remove oldest entry.
|
||||
T* last_data = m_StorageList.back();
|
||||
int last_key = last_data->getKey() % m_iHashSize;
|
||||
|
||||
ItemPtrList& last_item_list = m_vHashPtr[last_key];
|
||||
for (typename ItemPtrList::iterator i = last_item_list.begin(); i != last_item_list.end(); ++ i)
|
||||
{
|
||||
if (*last_data == ***i)
|
||||
{
|
||||
last_item_list.erase(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
last_data->release();
|
||||
delete last_data;
|
||||
m_StorageList.pop_back();
|
||||
-- m_iCurrSize;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Specify the cache size (i.e., max number of items).
|
||||
/// @param [in] size max cache size.
|
||||
|
||||
void setSizeLimit(int size)
|
||||
{
|
||||
m_iMaxSize = size;
|
||||
m_iHashSize = size * 3;
|
||||
m_vHashPtr.resize(m_iHashSize);
|
||||
}
|
||||
|
||||
/// Clear all entries in the cache, restore to initialization state.
|
||||
|
||||
void clear()
|
||||
{
|
||||
for (typename std::list<T*>::iterator i = m_StorageList.begin(); i != m_StorageList.end(); ++ i)
|
||||
{
|
||||
(*i)->release();
|
||||
delete *i;
|
||||
}
|
||||
m_StorageList.clear();
|
||||
for (typename std::vector<ItemPtrList>::iterator i = m_vHashPtr.begin(); i != m_vHashPtr.end(); ++ i)
|
||||
i->clear();
|
||||
m_iCurrSize = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
std::list<T*> m_StorageList;
|
||||
typedef typename std::list<T*>::iterator ItemPtr;
|
||||
typedef std::list<ItemPtr> ItemPtrList;
|
||||
std::vector<ItemPtrList> m_vHashPtr;
|
||||
|
||||
int m_iMaxSize;
|
||||
int m_iHashSize;
|
||||
int m_iCurrSize;
|
||||
|
||||
pthread_mutex_t m_Lock;
|
||||
|
||||
private:
|
||||
CCache(const CCache&);
|
||||
CCache& operator=(const CCache&);
|
||||
};
|
||||
|
||||
|
||||
class CInfoBlock
|
||||
{
|
||||
public:
|
||||
uint32_t m_piIP[4]; // IP address, machine read only, not human readable format
|
||||
int m_iIPversion; // IP version
|
||||
uint64_t m_ullTimeStamp; // last update time
|
||||
int m_iRTT; // RTT
|
||||
int m_iBandwidth; // estimated bandwidth
|
||||
int m_iLossRate; // average loss rate
|
||||
int m_iReorderDistance; // packet reordering distance
|
||||
double m_dInterval; // inter-packet time, congestion control
|
||||
double m_dCWnd; // congestion window size, congestion control
|
||||
|
||||
public:
|
||||
virtual ~CInfoBlock() {}
|
||||
virtual CInfoBlock& operator=(const CInfoBlock& obj);
|
||||
virtual bool operator==(const CInfoBlock& obj);
|
||||
virtual CInfoBlock* clone();
|
||||
virtual int getKey();
|
||||
virtual void release() {}
|
||||
|
||||
public:
|
||||
|
||||
/// convert sockaddr structure to an integer array
|
||||
/// @param [in] addr network address
|
||||
/// @param [in] ver IP version
|
||||
/// @param [out] ip the result machine readable IP address in integer array
|
||||
|
||||
static void convert(const sockaddr* addr, int ver, uint32_t ip[]);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
765
trunk/3rdparty/srt-1-fit/srtcore/channel.cpp
vendored
Normal file
765
trunk/3rdparty/srt-1-fit/srtcore/channel.cpp
vendored
Normal file
|
@ -0,0 +1,765 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
written by
|
||||
Yunhong Gu, last updated 01/27/2011
|
||||
modified by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef _WIN32
|
||||
#if __APPLE__
|
||||
#include "TargetConditionals.h"
|
||||
#endif
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <cerrno>
|
||||
#else
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <mswsock.h>
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip> // Logging
|
||||
#include <srt_compat.h>
|
||||
#include <csignal>
|
||||
|
||||
#include "channel.h"
|
||||
#include "packet.h"
|
||||
#include "api.h" // SockaddrToString - possibly move it to somewhere else
|
||||
#include "logging.h"
|
||||
#include "utilities.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
typedef int socklen_t;
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#define NET_ERROR errno
|
||||
#else
|
||||
#define NET_ERROR WSAGetLastError()
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using namespace srt_logging;
|
||||
|
||||
CChannel::CChannel():
|
||||
m_iIPversion(AF_INET),
|
||||
m_iSockAddrSize(sizeof(sockaddr_in)),
|
||||
m_iSocket(),
|
||||
#ifdef SRT_ENABLE_IPOPTS
|
||||
m_iIpTTL(-1), /* IPv4 TTL or IPv6 HOPs [1..255] (-1:undefined) */
|
||||
m_iIpToS(-1), /* IPv4 Type of Service or IPv6 Traffic Class [0x00..0xff] (-1:undefined) */
|
||||
#endif
|
||||
m_iSndBufSize(65536),
|
||||
m_iRcvBufSize(65536),
|
||||
m_iIpV6Only(-1)
|
||||
{
|
||||
}
|
||||
|
||||
CChannel::CChannel(int version):
|
||||
m_iIPversion(version),
|
||||
m_iSocket(),
|
||||
#ifdef SRT_ENABLE_IPOPTS
|
||||
m_iIpTTL(-1),
|
||||
m_iIpToS(-1),
|
||||
#endif
|
||||
m_iSndBufSize(65536),
|
||||
m_iRcvBufSize(65536),
|
||||
m_iIpV6Only(-1),
|
||||
m_BindAddr(version)
|
||||
{
|
||||
SRT_ASSERT(version == AF_INET || version == AF_INET6);
|
||||
m_iSockAddrSize = (AF_INET == m_iIPversion) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
|
||||
}
|
||||
|
||||
CChannel::~CChannel()
|
||||
{
|
||||
}
|
||||
|
||||
void CChannel::open(const sockaddr* addr)
|
||||
{
|
||||
// construct an socket
|
||||
m_iSocket = ::socket(m_iIPversion, SOCK_DGRAM, 0);
|
||||
|
||||
#ifdef _WIN32
|
||||
if (INVALID_SOCKET == m_iSocket)
|
||||
#else
|
||||
if (m_iSocket < 0)
|
||||
#endif
|
||||
throw CUDTException(MJ_SETUP, MN_NONE, NET_ERROR);
|
||||
|
||||
if ((m_iIpV6Only != -1) && (m_iIPversion == AF_INET6)) // (not an error if it fails)
|
||||
::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)(&m_iIpV6Only), sizeof(m_iIpV6Only));
|
||||
|
||||
if (NULL != addr)
|
||||
{
|
||||
socklen_t namelen = m_iSockAddrSize;
|
||||
|
||||
if (0 != ::bind(m_iSocket, addr, namelen))
|
||||
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
|
||||
memcpy(&m_BindAddr, addr, namelen);
|
||||
m_BindAddr.len = namelen;
|
||||
}
|
||||
else
|
||||
{
|
||||
//sendto or WSASendTo will also automatically bind the socket
|
||||
addrinfo hints;
|
||||
addrinfo* res;
|
||||
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
hints.ai_family = m_iIPversion;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
if (0 != ::getaddrinfo(NULL, "0", &hints, &res))
|
||||
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
|
||||
|
||||
// On Windows ai_addrlen has type size_t (unsigned), while bind takes int.
|
||||
if (0 != ::bind(m_iSocket, res->ai_addr, (socklen_t)res->ai_addrlen))
|
||||
{
|
||||
::freeaddrinfo(res);
|
||||
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
|
||||
}
|
||||
memcpy(&m_BindAddr, res->ai_addr, res->ai_addrlen);
|
||||
m_BindAddr.len = (socklen_t) res->ai_addrlen;
|
||||
|
||||
::freeaddrinfo(res);
|
||||
}
|
||||
|
||||
HLOGC(mglog.Debug, log << "CHANNEL: Bound to local address: " << SockaddrToString(&m_BindAddr));
|
||||
|
||||
setUDPSockOpt();
|
||||
}
|
||||
|
||||
void CChannel::attach(UDPSOCKET udpsock)
|
||||
{
|
||||
m_iSocket = udpsock;
|
||||
setUDPSockOpt();
|
||||
}
|
||||
|
||||
void CChannel::setUDPSockOpt()
|
||||
{
|
||||
#if defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
|
||||
// BSD system will fail setsockopt if the requested buffer size exceeds system maximum value
|
||||
int maxsize = 64000;
|
||||
if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&m_iRcvBufSize, sizeof(int)))
|
||||
::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&maxsize, sizeof(int));
|
||||
if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&m_iSndBufSize, sizeof(int)))
|
||||
::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&maxsize, sizeof(int));
|
||||
#else
|
||||
// for other systems, if requested is greated than maximum, the maximum value will be automactally used
|
||||
if ((0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char*)&m_iRcvBufSize, sizeof(int))) ||
|
||||
(0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char*)&m_iSndBufSize, sizeof(int))))
|
||||
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
|
||||
#endif
|
||||
|
||||
SRT_ASSERT(m_iIPversion == AF_INET || m_iIPversion == AF_INET6);
|
||||
|
||||
#ifdef SRT_ENABLE_IPOPTS
|
||||
if (-1 != m_iIpTTL)
|
||||
{
|
||||
if (m_iIPversion == AF_INET)
|
||||
{
|
||||
if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (const char*)&m_iIpTTL, sizeof(m_iIpTTL)))
|
||||
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If IPv6 address is unspecified, set BOTH IP_TTL and IPV6_UNICAST_HOPS.
|
||||
|
||||
// For specified IPv6 address, set IPV6_UNICAST_HOPS ONLY UNLESS it's an IPv4-mapped-IPv6
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || !IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr))
|
||||
{
|
||||
if (0 != ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (const char*)&m_iIpTTL, sizeof(m_iIpTTL)))
|
||||
{
|
||||
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
|
||||
}
|
||||
}
|
||||
// For specified IPv6 address, set IP_TTL ONLY WHEN it's an IPv4-mapped-IPv6
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr))
|
||||
{
|
||||
if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (const char*)&m_iIpTTL, sizeof(m_iIpTTL)))
|
||||
{
|
||||
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (-1 != m_iIpToS)
|
||||
{
|
||||
if (m_iIPversion == AF_INET)
|
||||
{
|
||||
if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (const char*)&m_iIpToS, sizeof(m_iIpToS)))
|
||||
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If IPv6 address is unspecified, set BOTH IP_TOS and IPV6_TCLASS.
|
||||
|
||||
#ifdef IPV6_TCLASS
|
||||
// For specified IPv6 address, set IPV6_TCLASS ONLY UNLESS it's an IPv4-mapped-IPv6
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || !IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr))
|
||||
{
|
||||
if (0 != ::setsockopt(m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, (const char*)&m_iIpToS, sizeof(m_iIpToS)))
|
||||
{
|
||||
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// For specified IPv6 address, set IP_TOS ONLY WHEN it's an IPv4-mapped-IPv6
|
||||
if (IN6_IS_ADDR_UNSPECIFIED(&m_BindAddr.sin6.sin6_addr) || IN6_IS_ADDR_V4MAPPED(&m_BindAddr.sin6.sin6_addr))
|
||||
{
|
||||
if (0 != ::setsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (const char*)&m_iIpToS, sizeof(m_iIpToS)))
|
||||
{
|
||||
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef UNIX
|
||||
// Set non-blocking I/O
|
||||
// UNIX does not support SO_RCVTIMEO
|
||||
int opts = ::fcntl(m_iSocket, F_GETFL);
|
||||
if (-1 == ::fcntl(m_iSocket, F_SETFL, opts | O_NONBLOCK))
|
||||
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
|
||||
#elif defined(_WIN32)
|
||||
u_long nonBlocking = 1;
|
||||
if (0 != ioctlsocket (m_iSocket, FIONBIO, &nonBlocking))
|
||||
throw CUDTException (MJ_SETUP, MN_NORES, NET_ERROR);
|
||||
#else
|
||||
timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
#if defined (BSD) || defined (OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
|
||||
// Known BSD bug as the day I wrote this code.
|
||||
// A small time out value will cause the socket to block forever.
|
||||
tv.tv_usec = 10000;
|
||||
#else
|
||||
tv.tv_usec = 100;
|
||||
#endif
|
||||
// Set receiving time-out value
|
||||
if (0 != ::setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(timeval)))
|
||||
throw CUDTException(MJ_SETUP, MN_NORES, NET_ERROR);
|
||||
#endif
|
||||
}
|
||||
|
||||
void CChannel::close() const
|
||||
{
|
||||
#ifndef _WIN32
|
||||
::close(m_iSocket);
|
||||
#else
|
||||
::closesocket(m_iSocket);
|
||||
#endif
|
||||
}
|
||||
|
||||
int CChannel::getSndBufSize()
|
||||
{
|
||||
socklen_t size = sizeof(socklen_t);
|
||||
::getsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, (char *)&m_iSndBufSize, &size);
|
||||
return m_iSndBufSize;
|
||||
}
|
||||
|
||||
int CChannel::getRcvBufSize()
|
||||
{
|
||||
socklen_t size = sizeof(socklen_t);
|
||||
::getsockopt(m_iSocket, SOL_SOCKET, SO_RCVBUF, (char *)&m_iRcvBufSize, &size);
|
||||
return m_iRcvBufSize;
|
||||
}
|
||||
|
||||
void CChannel::setSndBufSize(int size)
|
||||
{
|
||||
m_iSndBufSize = size;
|
||||
}
|
||||
|
||||
void CChannel::setRcvBufSize(int size)
|
||||
{
|
||||
m_iRcvBufSize = size;
|
||||
}
|
||||
|
||||
void CChannel::setIpV6Only(int ipV6Only)
|
||||
{
|
||||
m_iIpV6Only = ipV6Only;
|
||||
}
|
||||
|
||||
#ifdef SRT_ENABLE_IPOPTS
|
||||
int CChannel::getIpTTL() const
|
||||
{
|
||||
socklen_t size = sizeof(m_iIpTTL);
|
||||
if (m_iIPversion == AF_INET)
|
||||
{
|
||||
::getsockopt(m_iSocket, IPPROTO_IP, IP_TTL, (char *)&m_iIpTTL, &size);
|
||||
}
|
||||
else
|
||||
{
|
||||
::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *)&m_iIpTTL, &size);
|
||||
}
|
||||
return m_iIpTTL;
|
||||
}
|
||||
|
||||
int CChannel::getIpToS() const
|
||||
{
|
||||
socklen_t size = sizeof(m_iIpToS);
|
||||
if (m_iIPversion == AF_INET)
|
||||
{
|
||||
::getsockopt(m_iSocket, IPPROTO_IP, IP_TOS, (char *)&m_iIpToS, &size);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef IPV6_TCLASS
|
||||
::getsockopt(m_iSocket, IPPROTO_IPV6, IPV6_TCLASS, (char *)&m_iIpToS, &size);
|
||||
#endif
|
||||
}
|
||||
return m_iIpToS;
|
||||
}
|
||||
|
||||
void CChannel::setIpTTL(int ttl)
|
||||
{
|
||||
m_iIpTTL = ttl;
|
||||
}
|
||||
|
||||
void CChannel::setIpToS(int tos)
|
||||
{
|
||||
m_iIpToS = tos;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int CChannel::ioctlQuery(int SRT_ATR_UNUSED type) const
|
||||
{
|
||||
#ifdef unix
|
||||
int value = 0;
|
||||
int res = ::ioctl(m_iSocket, type, &value);
|
||||
if ( res != -1 )
|
||||
return value;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
int CChannel::sockoptQuery(int SRT_ATR_UNUSED level, int SRT_ATR_UNUSED option) const
|
||||
{
|
||||
#ifdef unix
|
||||
int value = 0;
|
||||
socklen_t len = sizeof (int);
|
||||
int res = ::getsockopt(m_iSocket, level, option, &value, &len);
|
||||
if ( res != -1 )
|
||||
return value;
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
void CChannel::getSockAddr(sockaddr* addr) const
|
||||
{
|
||||
socklen_t namelen = m_iSockAddrSize;
|
||||
::getsockname(m_iSocket, addr, &namelen);
|
||||
}
|
||||
|
||||
void CChannel::getPeerAddr(sockaddr* addr) const
|
||||
{
|
||||
socklen_t namelen = m_iSockAddrSize;
|
||||
::getpeername(m_iSocket, addr, &namelen);
|
||||
}
|
||||
|
||||
|
||||
int CChannel::sendto(const sockaddr* addr, CPacket& packet) const
|
||||
{
|
||||
#if ENABLE_HEAVY_LOGGING
|
||||
std::ostringstream spec;
|
||||
|
||||
if (packet.isControl())
|
||||
{
|
||||
spec << " type=CONTROL"
|
||||
<< " cmd=" << MessageTypeStr(packet.getType(), packet.getExtendedType())
|
||||
<< " arg=" << packet.header(SRT_PH_MSGNO);
|
||||
}
|
||||
else
|
||||
{
|
||||
spec << " type=DATA"
|
||||
<< " %" << packet.getSeqNo()
|
||||
<< " msgno=" << MSGNO_SEQ::unwrap(packet.m_iMsgNo)
|
||||
<< packet.MessageFlagStr()
|
||||
<< " !" << BufferStamp(packet.m_pcData, packet.getLength());
|
||||
}
|
||||
|
||||
LOGC(mglog.Debug, log << "CChannel::sendto: SENDING NOW DST=" << SockaddrToString(addr)
|
||||
<< " target=@" << packet.m_iID
|
||||
<< " size=" << packet.getLength()
|
||||
<< " pkt.ts=" << FormatTime(packet.m_iTimeStamp)
|
||||
<< spec.str());
|
||||
#endif
|
||||
|
||||
#ifdef SRT_TEST_FAKE_LOSS
|
||||
|
||||
#define FAKELOSS_STRING_0(x) #x
|
||||
#define FAKELOSS_STRING(x) FAKELOSS_STRING_0(x)
|
||||
const char* fakeloss_text = FAKELOSS_STRING(SRT_TEST_FAKE_LOSS);
|
||||
#undef FAKELOSS_STRING
|
||||
#undef FAKELOSS_WRAP
|
||||
|
||||
static int dcounter = 0;
|
||||
static int flwcounter = 0;
|
||||
|
||||
struct FakelossConfig
|
||||
{
|
||||
pair<int,int> config;
|
||||
FakelossConfig(const char* f)
|
||||
{
|
||||
vector<string> out;
|
||||
Split(f, '+', back_inserter(out));
|
||||
|
||||
config.first = atoi(out[0].c_str());
|
||||
config.second = out.size() > 1 ? atoi(out[1].c_str()) : 8;
|
||||
}
|
||||
};
|
||||
static FakelossConfig fakeloss = fakeloss_text;
|
||||
|
||||
if (!packet.isControl())
|
||||
{
|
||||
if (dcounter == 0)
|
||||
{
|
||||
timeval tv;
|
||||
gettimeofday(&tv, 0);
|
||||
srand(tv.tv_usec & 0xFFFF);
|
||||
}
|
||||
++dcounter;
|
||||
|
||||
if (flwcounter)
|
||||
{
|
||||
// This is a counter of how many packets in a row shall be lost
|
||||
--flwcounter;
|
||||
HLOGC(mglog.Debug, log << "CChannel: TEST: FAKE LOSS OF %" << packet.getSeqNo() << " (" << flwcounter << " more to drop)");
|
||||
return packet.getLength(); // fake successful sendinf
|
||||
}
|
||||
|
||||
if (dcounter > 8)
|
||||
{
|
||||
// Make a random number in the range between 8 and 24
|
||||
int rnd = rand() % 16 + SRT_TEST_FAKE_LOSS;
|
||||
|
||||
if (dcounter > rnd)
|
||||
{
|
||||
dcounter = 1;
|
||||
HLOGC(mglog.Debug, log << "CChannel: TEST: FAKE LOSS OF %" << packet.getSeqNo() << " (will drop " << fakeloss.config.first << " more)");
|
||||
flwcounter = fakeloss.config.first;
|
||||
return packet.getLength(); // fake successful sendinf
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// convert control information into network order
|
||||
// XXX USE HtoNLA!
|
||||
if (packet.isControl())
|
||||
for (ptrdiff_t i = 0, n = packet.getLength() / 4; i < n; ++i)
|
||||
*((uint32_t *)packet.m_pcData + i) = htonl(*((uint32_t *)packet.m_pcData + i));
|
||||
|
||||
// convert packet header into network order
|
||||
//for (int j = 0; j < 4; ++ j)
|
||||
// packet.m_nHeader[j] = htonl(packet.m_nHeader[j]);
|
||||
uint32_t* p = packet.m_nHeader;
|
||||
for (int j = 0; j < 4; ++ j)
|
||||
{
|
||||
*p = htonl(*p);
|
||||
++ p;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
msghdr mh;
|
||||
mh.msg_name = (sockaddr*)addr;
|
||||
mh.msg_namelen = m_iSockAddrSize;
|
||||
mh.msg_iov = (iovec*)packet.m_PacketVector;
|
||||
mh.msg_iovlen = 2;
|
||||
mh.msg_control = NULL;
|
||||
mh.msg_controllen = 0;
|
||||
mh.msg_flags = 0;
|
||||
|
||||
int res = ::sendmsg(m_iSocket, &mh, 0);
|
||||
#else
|
||||
DWORD size = (DWORD) (CPacket::HDR_SIZE + packet.getLength());
|
||||
int addrsize = m_iSockAddrSize;
|
||||
int res = ::WSASendTo(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, 0, addr, addrsize, NULL, NULL);
|
||||
res = (0 == res) ? size : -1;
|
||||
#endif
|
||||
|
||||
// convert back into local host order
|
||||
//for (int k = 0; k < 4; ++ k)
|
||||
// packet.m_nHeader[k] = ntohl(packet.m_nHeader[k]);
|
||||
p = packet.m_nHeader;
|
||||
for (int k = 0; k < 4; ++ k)
|
||||
{
|
||||
*p = ntohl(*p);
|
||||
++ p;
|
||||
}
|
||||
|
||||
if (packet.isControl())
|
||||
{
|
||||
for (ptrdiff_t l = 0, n = packet.getLength() / 4; l < n; ++ l)
|
||||
*((uint32_t *)packet.m_pcData + l) = ntohl(*((uint32_t *)packet.m_pcData + l));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
EReadStatus CChannel::recvfrom(sockaddr* addr, CPacket& packet) const
|
||||
{
|
||||
EReadStatus status = RST_OK;
|
||||
int msg_flags = 0;
|
||||
int recv_size = -1;
|
||||
|
||||
#if defined(UNIX) || defined(_WIN32)
|
||||
fd_set set;
|
||||
timeval tv;
|
||||
FD_ZERO(&set);
|
||||
FD_SET(m_iSocket, &set);
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 10000;
|
||||
const int select_ret = ::select((int) m_iSocket + 1, &set, NULL, &set, &tv);
|
||||
#else
|
||||
const int select_ret = 1; // the socket is expected to be in the blocking mode itself
|
||||
#endif
|
||||
|
||||
if (select_ret == 0) // timeout
|
||||
{
|
||||
packet.setLength(-1);
|
||||
return RST_AGAIN;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
if (select_ret > 0)
|
||||
{
|
||||
msghdr mh;
|
||||
mh.msg_name = addr;
|
||||
mh.msg_namelen = m_iSockAddrSize;
|
||||
mh.msg_iov = packet.m_PacketVector;
|
||||
mh.msg_iovlen = 2;
|
||||
mh.msg_control = NULL;
|
||||
mh.msg_controllen = 0;
|
||||
mh.msg_flags = 0;
|
||||
|
||||
recv_size = ::recvmsg(m_iSocket, &mh, 0);
|
||||
msg_flags = mh.msg_flags;
|
||||
}
|
||||
|
||||
// Note that there are exactly four groups of possible errors
|
||||
// reported by recvmsg():
|
||||
|
||||
// 1. Temporary error, can't get the data, but you can try again.
|
||||
// Codes: EAGAIN/EWOULDBLOCK, EINTR, ECONNREFUSED
|
||||
// Return: RST_AGAIN.
|
||||
//
|
||||
// 2. Problems that should never happen due to unused configurations.
|
||||
// Codes: ECONNREFUSED, ENOTCONN
|
||||
// Return: RST_ERROR, just formally treat this as IPE.
|
||||
//
|
||||
// 3. Unexpected runtime errors:
|
||||
// Codes: EINVAL, EFAULT, ENOMEM, ENOTSOCK
|
||||
// Return: RST_ERROR. Except ENOMEM, this can only be an IPE. ENOMEM
|
||||
// should make the program stop as lacking memory will kill the program anyway soon.
|
||||
//
|
||||
// 4. Expected socket closed in the meantime by another thread.
|
||||
// Codes: EBADF
|
||||
// Return: RST_ERROR. This will simply make the worker thread exit, which is
|
||||
// expected to happen after CChannel::close() is called by another thread.
|
||||
|
||||
// We do not handle <= SOCKET_ERROR as they are handled further by checking the recv_size
|
||||
if (select_ret == -1 || recv_size == -1)
|
||||
{
|
||||
const int err = NET_ERROR;
|
||||
if (err == EAGAIN || err == EINTR || err == ECONNREFUSED) // For EAGAIN, this isn't an error, just a useless call.
|
||||
{
|
||||
status = RST_AGAIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
HLOGC(mglog.Debug, log << CONID() << "(sys)recvmsg: " << SysStrError(err) << " [" << err << "]");
|
||||
status = RST_ERROR;
|
||||
}
|
||||
|
||||
goto Return_error;
|
||||
}
|
||||
|
||||
#else
|
||||
// XXX REFACTORING NEEDED!
|
||||
// This procedure uses the WSARecvFrom function that just reads
|
||||
// into one buffer. On Windows, the equivalent for recvmsg, WSARecvMsg
|
||||
// uses the equivalent of msghdr - WSAMSG, which has different field
|
||||
// names and also uses the equivalet of iovec - WSABUF, which has different
|
||||
// field names and layout. It is important that this code be translated
|
||||
// to the "proper" solution, however this requires that CPacket::m_PacketVector
|
||||
// also uses the "platform independent" (or, better, platform-suitable) type
|
||||
// which can be appropriate for the appropriate system function, not just iovec
|
||||
// (see a specifically provided definition for iovec for windows in packet.h).
|
||||
//
|
||||
// For the time being, the msg_flags variable is defined in both cases
|
||||
// so that it can be checked independently, however it won't have any other
|
||||
// value one Windows than 0, unless this procedure below is rewritten
|
||||
// to use WSARecvMsg().
|
||||
|
||||
int recv_ret = SOCKET_ERROR;
|
||||
DWORD flag = 0;
|
||||
|
||||
if (select_ret > 0) // the total number of socket handles that are ready
|
||||
{
|
||||
DWORD size = (DWORD) (CPacket::HDR_SIZE + packet.getLength());
|
||||
int addrsize = m_iSockAddrSize;
|
||||
|
||||
recv_ret = ::WSARecvFrom(m_iSocket, (LPWSABUF)packet.m_PacketVector, 2, &size, &flag, addr, &addrsize, NULL, NULL);
|
||||
if (recv_ret == 0)
|
||||
recv_size = size;
|
||||
}
|
||||
|
||||
// We do not handle <= SOCKET_ERROR as they are handled further by checking the recv_size
|
||||
if (select_ret == SOCKET_ERROR || recv_ret == SOCKET_ERROR) // == SOCKET_ERROR
|
||||
{
|
||||
recv_size = -1;
|
||||
// On Windows this is a little bit more complicated, so simply treat every error
|
||||
// as an "again" situation. This should still be probably fixed, but it needs more
|
||||
// thorough research. For example, the problem usually reported from here is
|
||||
// WSAETIMEDOUT, which isn't mentioned in the documentation of WSARecvFrom at all.
|
||||
//
|
||||
// These below errors are treated as "fatal", all others are treated as "again".
|
||||
static const int fatals [] =
|
||||
{
|
||||
WSAEFAULT,
|
||||
WSAEINVAL,
|
||||
WSAENETDOWN,
|
||||
WSANOTINITIALISED,
|
||||
WSA_OPERATION_ABORTED
|
||||
};
|
||||
static const int* fatals_end = fatals + Size(fatals);
|
||||
const int err = NET_ERROR;
|
||||
if (std::find(fatals, fatals_end, err) != fatals_end)
|
||||
{
|
||||
HLOGC(mglog.Debug, log << CONID() << "(sys)WSARecvFrom: " << SysStrError(err) << " [" << err << "]");
|
||||
status = RST_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = RST_AGAIN;
|
||||
}
|
||||
|
||||
goto Return_error;
|
||||
}
|
||||
|
||||
// Not sure if this problem has ever occurred on Windows, just a sanity check.
|
||||
if (flag & MSG_PARTIAL)
|
||||
msg_flags = 1;
|
||||
#endif
|
||||
|
||||
|
||||
// Sanity check for a case when it didn't fill in even the header
|
||||
if (size_t(recv_size) < CPacket::HDR_SIZE)
|
||||
{
|
||||
status = RST_AGAIN;
|
||||
HLOGC(mglog.Debug, log << CONID() << "POSSIBLE ATTACK: received too short packet with " << recv_size << " bytes");
|
||||
goto Return_error;
|
||||
}
|
||||
|
||||
// Fix for an issue with Linux Kernel found during tests at Tencent.
|
||||
//
|
||||
// There was a bug in older Linux Kernel which caused that when the internal
|
||||
// buffer was depleted during reading from the network, not the whole buffer
|
||||
// was copied from the packet, EVEN THOUGH THE GIVEN BUFFER WAS OF ENOUGH SIZE.
|
||||
// It was still very kind of the buggy procedure, though, that at least
|
||||
// they inform the caller about that this has happened by setting MSG_TRUNC
|
||||
// flag.
|
||||
//
|
||||
// Normally this flag should be set only if there was too small buffer given
|
||||
// by the caller, so as this code knows that the size is enough, it never
|
||||
// predicted this to happen. Just for a case then when you run this on a buggy
|
||||
// system that suffers of this problem, the fix for this case is left here.
|
||||
//
|
||||
// When this happens, then you have at best a fragment of the buffer and it's
|
||||
// useless anyway. This is solved by dropping the packet and fake that no
|
||||
// packet was received, so the packet will be then retransmitted.
|
||||
if ( msg_flags != 0 )
|
||||
{
|
||||
HLOGC(mglog.Debug, log << CONID() << "NET ERROR: packet size=" << recv_size
|
||||
<< " msg_flags=0x" << hex << msg_flags << ", possibly MSG_TRUNC (0x" << hex << int(MSG_TRUNC) << ")");
|
||||
status = RST_AGAIN;
|
||||
goto Return_error;
|
||||
}
|
||||
|
||||
packet.setLength(recv_size - CPacket::HDR_SIZE);
|
||||
|
||||
// convert back into local host order
|
||||
// XXX use NtoHLA().
|
||||
//for (int i = 0; i < 4; ++ i)
|
||||
// packet.m_nHeader[i] = ntohl(packet.m_nHeader[i]);
|
||||
{
|
||||
uint32_t* p = packet.m_nHeader;
|
||||
for (size_t i = 0; i < SRT_PH__SIZE; ++ i)
|
||||
{
|
||||
*p = ntohl(*p);
|
||||
++ p;
|
||||
}
|
||||
}
|
||||
|
||||
if (packet.isControl())
|
||||
{
|
||||
for (size_t j = 0, n = packet.getLength() / sizeof (uint32_t); j < n; ++ j)
|
||||
*((uint32_t *)packet.m_pcData + j) = ntohl(*((uint32_t *)packet.m_pcData + j));
|
||||
}
|
||||
|
||||
return RST_OK;
|
||||
|
||||
Return_error:
|
||||
packet.setLength(-1);
|
||||
return status;
|
||||
}
|
187
trunk/3rdparty/srt-1-fit/srtcore/channel.h
vendored
Normal file
187
trunk/3rdparty/srt-1-fit/srtcore/channel.h
vendored
Normal file
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Yunhong Gu, last updated 01/27/2011
|
||||
modified by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __UDT_CHANNEL_H__
|
||||
#define __UDT_CHANNEL_H__
|
||||
|
||||
|
||||
#include "udt.h"
|
||||
#include "packet.h"
|
||||
#include "netinet_any.h"
|
||||
|
||||
class CChannel
|
||||
{
|
||||
public:
|
||||
|
||||
// XXX There's currently no way to access the socket ID set for
|
||||
// whatever the channel is currently working for. Required to find
|
||||
// some way to do this, possibly by having a "reverse pointer".
|
||||
// Currently just "unimplemented".
|
||||
std::string CONID() const { return ""; }
|
||||
|
||||
CChannel();
|
||||
CChannel(int version);
|
||||
~CChannel();
|
||||
|
||||
/// Open a UDP channel.
|
||||
/// @param [in] addr The local address that UDP will use.
|
||||
|
||||
void open(const sockaddr* addr = NULL);
|
||||
|
||||
/// Open a UDP channel based on an existing UDP socket.
|
||||
/// @param [in] udpsock UDP socket descriptor.
|
||||
|
||||
void attach(UDPSOCKET udpsock);
|
||||
|
||||
/// Disconnect and close the UDP entity.
|
||||
|
||||
void close() const;
|
||||
|
||||
/// Get the UDP sending buffer size.
|
||||
/// @return Current UDP sending buffer size.
|
||||
|
||||
int getSndBufSize();
|
||||
|
||||
/// Get the UDP receiving buffer size.
|
||||
/// @return Current UDP receiving buffer size.
|
||||
|
||||
int getRcvBufSize();
|
||||
|
||||
/// Set the UDP sending buffer size.
|
||||
/// @param [in] size expected UDP sending buffer size.
|
||||
|
||||
void setSndBufSize(int size);
|
||||
|
||||
/// Set the UDP receiving buffer size.
|
||||
/// @param [in] size expected UDP receiving buffer size.
|
||||
|
||||
void setRcvBufSize(int size);
|
||||
|
||||
/// Set the IPV6ONLY option.
|
||||
/// @param [in] IPV6ONLY value.
|
||||
|
||||
void setIpV6Only(int ipV6Only);
|
||||
|
||||
/// Query the socket address that the channel is using.
|
||||
/// @param [out] addr pointer to store the returned socket address.
|
||||
|
||||
void getSockAddr(sockaddr* addr) const;
|
||||
|
||||
/// Query the peer side socket address that the channel is connect to.
|
||||
/// @param [out] addr pointer to store the returned socket address.
|
||||
|
||||
void getPeerAddr(sockaddr* addr) const;
|
||||
|
||||
/// Send a packet to the given address.
|
||||
/// @param [in] addr pointer to the destination address.
|
||||
/// @param [in] packet reference to a CPacket entity.
|
||||
/// @return Actual size of data sent.
|
||||
|
||||
int sendto(const sockaddr* addr, CPacket& packet) const;
|
||||
|
||||
/// Receive a packet from the channel and record the source address.
|
||||
/// @param [in] addr pointer to the source address.
|
||||
/// @param [in] packet reference to a CPacket entity.
|
||||
/// @return Actual size of data received.
|
||||
|
||||
EReadStatus recvfrom(sockaddr* addr, CPacket& packet) const;
|
||||
|
||||
#ifdef SRT_ENABLE_IPOPTS
|
||||
/// Set the IP TTL.
|
||||
/// @param [in] ttl IP Time To Live.
|
||||
/// @return none.
|
||||
|
||||
void setIpTTL(int ttl);
|
||||
|
||||
/// Set the IP Type of Service.
|
||||
/// @param [in] tos IP Type of Service.
|
||||
|
||||
void setIpToS(int tos);
|
||||
|
||||
/// Get the IP TTL.
|
||||
/// @param [in] ttl IP Time To Live.
|
||||
/// @return TTL.
|
||||
|
||||
int getIpTTL() const;
|
||||
|
||||
/// Get the IP Type of Service.
|
||||
/// @return ToS.
|
||||
|
||||
int getIpToS() const;
|
||||
#endif
|
||||
|
||||
int ioctlQuery(int type) const;
|
||||
int sockoptQuery(int level, int option) const;
|
||||
|
||||
const sockaddr* bindAddress() { return &m_BindAddr; }
|
||||
const sockaddr_any& bindAddressAny() { return m_BindAddr; }
|
||||
|
||||
private:
|
||||
void setUDPSockOpt();
|
||||
|
||||
private:
|
||||
const int m_iIPversion; // IP version
|
||||
int m_iSockAddrSize; // socket address structure size (pre-defined to avoid run-time test)
|
||||
|
||||
UDPSOCKET m_iSocket; // socket descriptor
|
||||
#ifdef SRT_ENABLE_IPOPTS
|
||||
int m_iIpTTL;
|
||||
int m_iIpToS;
|
||||
#endif
|
||||
int m_iSndBufSize; // UDP sending buffer size
|
||||
int m_iRcvBufSize; // UDP receiving buffer size
|
||||
int m_iIpV6Only; // IPV6_V6ONLY option (-1 if not set)
|
||||
sockaddr_any m_BindAddr;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
1044
trunk/3rdparty/srt-1-fit/srtcore/common.cpp
vendored
Normal file
1044
trunk/3rdparty/srt-1-fit/srtcore/common.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
850
trunk/3rdparty/srt-1-fit/srtcore/common.h
vendored
Normal file
850
trunk/3rdparty/srt-1-fit/srtcore/common.h
vendored
Normal file
|
@ -0,0 +1,850 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2009, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Yunhong Gu, last updated 08/01/2009
|
||||
modified by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __UDT_COMMON_H__
|
||||
#define __UDT_COMMON_H__
|
||||
|
||||
#define _CRT_SECURE_NO_WARNINGS 1 // silences windows complaints for sscanf
|
||||
|
||||
#include <cstdlib>
|
||||
#ifndef _WIN32
|
||||
#include <sys/time.h>
|
||||
#include <sys/uio.h>
|
||||
#else
|
||||
// #include <winsock2.h>
|
||||
//#include <windows.h>
|
||||
#endif
|
||||
#include <pthread.h>
|
||||
#include "udt.h"
|
||||
#include "utilities.h"
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
#include <assert.h>
|
||||
#define SRT_ASSERT(cond) assert(cond)
|
||||
#else
|
||||
#define SRT_ASSERT(cond)
|
||||
#endif
|
||||
|
||||
|
||||
enum UDTSockType
|
||||
{
|
||||
UDT_UNDEFINED = 0, // initial trap representation
|
||||
UDT_STREAM = 1,
|
||||
UDT_DGRAM
|
||||
};
|
||||
|
||||
|
||||
/// The message types used by UDT protocol. This is a part of UDT
|
||||
/// protocol and should never be changed.
|
||||
enum UDTMessageType
|
||||
{
|
||||
UMSG_HANDSHAKE = 0, //< Connection Handshake. Control: see @a CHandShake.
|
||||
UMSG_KEEPALIVE = 1, //< Keep-alive.
|
||||
UMSG_ACK = 2, //< Acknowledgement. Control: past-the-end sequence number up to which packets have been received.
|
||||
UMSG_LOSSREPORT = 3, //< Negative Acknowledgement (NAK). Control: Loss list.
|
||||
UMSG_CGWARNING = 4, //< Congestion warning.
|
||||
UMSG_SHUTDOWN = 5, //< Shutdown.
|
||||
UMSG_ACKACK = 6, //< Acknowledgement of Acknowledgement. Add info: The ACK sequence number
|
||||
UMSG_DROPREQ = 7, //< Message Drop Request. Add info: Message ID. Control Info: (first, last) number of the message.
|
||||
UMSG_PEERERROR = 8, //< Signal from the Peer side. Add info: Error code.
|
||||
// ... add extra code types here
|
||||
UMSG_END_OF_TYPES,
|
||||
UMSG_EXT = 0x7FFF //< For the use of user-defined control packets.
|
||||
};
|
||||
|
||||
// This side's role is: INITIATOR prepares the environment first, and sends
|
||||
// appropriate information to the peer. The peer must be RESPONDER and be ready
|
||||
// to receive it. It's important for the encryption: the INITIATOR side generates
|
||||
// the KM, and sends it to RESPONDER. RESPONDER awaits KM received from the
|
||||
// INITIATOR. Note that in bidirectional mode - that is always with HSv5 - the
|
||||
// INITIATOR creates both sending and receiving contexts, then sends the key to
|
||||
// RESPONDER, which creates both sending and receiving contexts, using the same
|
||||
// key received from INITIATOR.
|
||||
//
|
||||
// The method of selection:
|
||||
//
|
||||
// In HSv4, it's always data sender (the party that sets SRTO_SENDER flag on the
|
||||
// socket) INITIATOR, and receiver - RESPONDER. The HSREQ and KMREQ are done
|
||||
// AFTER the UDT connection is done using UMSG_EXT extension messages. As this
|
||||
// is unidirectional, the INITIATOR prepares the sending context only, the
|
||||
// RESPONDER - receiving context only.
|
||||
//
|
||||
// In HSv5, for caller-listener configuration, it's simple: caller is INITIATOR,
|
||||
// listener is RESPONDER. In case of rendezvous the parties are equivalent,
|
||||
// so the role is resolved by "cookie contest". Rendezvous sockets both know
|
||||
// each other's cookie generated during the URQ_WAVEAHAND handshake phase.
|
||||
// The cookies are simply compared as integer numbers; the party which's cookie
|
||||
// is a greater number becomes an INITIATOR, and the other party becomes a
|
||||
// RESPONDER.
|
||||
//
|
||||
// The case of a draw - that both occasionally have baked identical cookies -
|
||||
// is treated as an extremely rare and virtually impossible case, so this
|
||||
// results in connection rejected.
|
||||
enum HandshakeSide
|
||||
{
|
||||
HSD_DRAW,
|
||||
HSD_INITIATOR, //< Side that initiates HSREQ/KMREQ. HSv4: data sender, HSv5: connecting socket or winner rendezvous socket
|
||||
HSD_RESPONDER //< Side that expects HSREQ/KMREQ from the peer. HSv4: data receiver, HSv5: accepted socket or loser rendezvous socket
|
||||
};
|
||||
|
||||
// For debug
|
||||
std::string MessageTypeStr(UDTMessageType mt, uint32_t extt = 0);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Commonly used by various reading facilities
|
||||
enum EReadStatus
|
||||
{
|
||||
RST_OK = 0, //< A new portion of data has been received
|
||||
RST_AGAIN, //< Nothing has been received, try again
|
||||
RST_ERROR = -1 //< Irrecoverable error, please close descriptor and stop reading.
|
||||
};
|
||||
|
||||
enum EConnectStatus
|
||||
{
|
||||
CONN_ACCEPT = 0, //< Received final handshake that confirms connection established
|
||||
CONN_REJECT = -1, //< Error during processing handshake.
|
||||
CONN_CONTINUE = 1, //< induction->conclusion phase
|
||||
CONN_RENDEZVOUS = 2, //< pass to a separate rendezvous processing (HSv5 only)
|
||||
CONN_CONFUSED = 3, //< listener thinks it's connected, but caller missed conclusion
|
||||
CONN_RUNNING = 10, //< no connection in progress, already connected
|
||||
CONN_AGAIN = -2 //< No data was read, don't change any state.
|
||||
};
|
||||
|
||||
std::string ConnectStatusStr(EConnectStatus est);
|
||||
|
||||
|
||||
const int64_t BW_INFINITE = 1000000000/8; //Infinite=> 1 Gbps
|
||||
|
||||
|
||||
enum ETransmissionEvent
|
||||
{
|
||||
TEV_INIT, // --> After creation, and after any parameters were updated.
|
||||
TEV_ACK, // --> When handling UMSG_ACK - older CCC:onAck()
|
||||
TEV_ACKACK, // --> UDT does only RTT sync, can be read from CUDT::RTT().
|
||||
TEV_LOSSREPORT, // --> When handling UMSG_LOSSREPORT - older CCC::onLoss()
|
||||
TEV_CHECKTIMER, // --> See TEV_CHT_REXMIT
|
||||
TEV_SEND, // --> When the packet is scheduled for sending - older CCC::onPktSent
|
||||
TEV_RECEIVE, // --> When a data packet was received - older CCC::onPktReceived
|
||||
TEV_CUSTOM, // --> probably dead call - older CCC::processCustomMsg
|
||||
|
||||
TEV__SIZE
|
||||
};
|
||||
|
||||
std::string TransmissionEventStr(ETransmissionEvent ev);
|
||||
|
||||
// Special parameter for TEV_CHECKTIMER
|
||||
enum ECheckTimerStage
|
||||
{
|
||||
TEV_CHT_INIT, // --> UDT: just update parameters, don't call any CCC::*
|
||||
TEV_CHT_FASTREXMIT, // --> not available on UDT
|
||||
TEV_CHT_REXMIT // --> CCC::onTimeout() in UDT
|
||||
};
|
||||
|
||||
enum EInitEvent
|
||||
{
|
||||
TEV_INIT_RESET = 0,
|
||||
TEV_INIT_INPUTBW,
|
||||
TEV_INIT_OHEADBW
|
||||
};
|
||||
|
||||
class CPacket;
|
||||
|
||||
// XXX Use some more standard less hand-crafted solution, if possible
|
||||
// XXX Consider creating a mapping between TEV_* values and associated types,
|
||||
// so that the type is compiler-enforced when calling updateCC() and when
|
||||
// connecting signals to slots.
|
||||
struct EventVariant
|
||||
{
|
||||
enum Type {UNDEFINED, PACKET, ARRAY, ACK, STAGE, INIT} type;
|
||||
union U
|
||||
{
|
||||
CPacket* packet;
|
||||
int32_t ack;
|
||||
struct
|
||||
{
|
||||
int32_t* ptr;
|
||||
size_t len;
|
||||
} array;
|
||||
ECheckTimerStage stage;
|
||||
EInitEvent init;
|
||||
} u;
|
||||
|
||||
EventVariant()
|
||||
{
|
||||
type = UNDEFINED;
|
||||
memset(&u, 0, sizeof u);
|
||||
}
|
||||
|
||||
template<Type t>
|
||||
struct VariantFor;
|
||||
|
||||
template <Type tp, typename Arg>
|
||||
void Assign(Arg arg)
|
||||
{
|
||||
type = tp;
|
||||
(u.*(VariantFor<tp>::field())) = arg;
|
||||
//(u.*field) = arg;
|
||||
}
|
||||
|
||||
void operator=(CPacket* arg) { Assign<PACKET>(arg); };
|
||||
void operator=(int32_t arg) { Assign<ACK>(arg); };
|
||||
void operator=(ECheckTimerStage arg) { Assign<STAGE>(arg); };
|
||||
void operator=(EInitEvent arg) { Assign<INIT>(arg); };
|
||||
|
||||
// Note: UNDEFINED and ARRAY don't have assignment operator.
|
||||
// For ARRAY you'll use 'set' function. For UNDEFINED there's nothing.
|
||||
|
||||
|
||||
template <class T>
|
||||
EventVariant(T arg)
|
||||
{
|
||||
*this = arg;
|
||||
}
|
||||
|
||||
const int32_t* get_ptr() const
|
||||
{
|
||||
return u.array.ptr;
|
||||
}
|
||||
|
||||
size_t get_len()
|
||||
{
|
||||
return u.array.len;
|
||||
}
|
||||
|
||||
void set(int32_t* ptr, size_t len)
|
||||
{
|
||||
type = ARRAY;
|
||||
u.array.ptr = ptr;
|
||||
u.array.len = len;
|
||||
}
|
||||
|
||||
EventVariant(int32_t* ptr, size_t len)
|
||||
{
|
||||
set(ptr, len);
|
||||
}
|
||||
|
||||
template<Type T>
|
||||
typename VariantFor<T>::type get()
|
||||
{
|
||||
return u.*(VariantFor<T>::field());
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Maybe later.
|
||||
This had to be a solution for automatic extraction of the
|
||||
type hidden in particular EventArg for particular event so
|
||||
that it's not runtime-mistaken.
|
||||
|
||||
In order that this make sense there would be required an array
|
||||
indexed by event id (just like a slot array m_Slots in CUDT),
|
||||
where the "type distiller" function would be extracted and then
|
||||
combined with the user-connected slot function this would call
|
||||
it already with correct type. Note that also the ConnectSignal
|
||||
function would have to get the signal id by template parameter,
|
||||
not function parameter. For example:
|
||||
|
||||
m_parent->ConnectSignal<TEV_ACK>(SSLOT(updateOnSent));
|
||||
|
||||
in which updateOnSent would have to receive an appropriate type.
|
||||
This has a disadvantage that you can't connect multiple signals
|
||||
with different argument types to the same slot, you'd have to
|
||||
make slot wrappers to translate arguments.
|
||||
|
||||
It seems that a better idea would be to create binders that would
|
||||
translate the argument from EventArg to the correct type according
|
||||
to the rules imposed by particular event id. But I'd not make it
|
||||
until there's a green light on C++11 for SRT, so maybe in a far future.
|
||||
|
||||
template <ETransmissionEvent type>
|
||||
class EventArgType;
|
||||
#define MAP_EVENT_TYPE(tev, tp) template<> class EventArgType<tev> { typedef tp type; }
|
||||
*/
|
||||
|
||||
|
||||
// The 'type' field wouldn't be even necessary if we
|
||||
|
||||
template<> struct EventVariant::VariantFor<EventVariant::PACKET>
|
||||
{
|
||||
typedef CPacket* type;
|
||||
static type U::*field() {return &U::packet;}
|
||||
};
|
||||
|
||||
template<> struct EventVariant::VariantFor<EventVariant::ACK>
|
||||
{
|
||||
typedef int32_t type;
|
||||
static type U::*field() { return &U::ack; }
|
||||
};
|
||||
|
||||
template<> struct EventVariant::VariantFor<EventVariant::STAGE>
|
||||
{
|
||||
typedef ECheckTimerStage type;
|
||||
static type U::*field() { return &U::stage; }
|
||||
};
|
||||
|
||||
template<> struct EventVariant::VariantFor<EventVariant::INIT>
|
||||
{
|
||||
typedef EInitEvent type;
|
||||
static type U::*field() { return &U::init; }
|
||||
};
|
||||
|
||||
// Using a hand-crafted solution because there's a non-backward-compatible
|
||||
// change between C++03 and others on the way up to C++17 (and we want this
|
||||
// code to be compliant with all C++ standards):
|
||||
//
|
||||
// - there's std::mem_fun in C++03 - deprecated in C++11, removed in C++17
|
||||
// - std::function in C++11 would be perfect, but not in C++03
|
||||
|
||||
// This can be changed in future to use C++11 way, but only after C++03
|
||||
// compatibility is finally abaondoned. Until then, this stays with a custom
|
||||
// class.
|
||||
|
||||
class EventSlotBase
|
||||
{
|
||||
public:
|
||||
virtual void emit(ETransmissionEvent tev, EventVariant var) = 0;
|
||||
typedef void dispatcher_t(void* opaque, ETransmissionEvent tev, EventVariant var);
|
||||
|
||||
virtual ~EventSlotBase() {}
|
||||
};
|
||||
|
||||
class SimpleEventSlot: public EventSlotBase
|
||||
{
|
||||
public:
|
||||
void* opaque;
|
||||
dispatcher_t* dispatcher;
|
||||
|
||||
SimpleEventSlot(void* op, dispatcher_t* disp): opaque(op), dispatcher(disp) {}
|
||||
|
||||
void emit(ETransmissionEvent tev, EventVariant var) ATR_OVERRIDE
|
||||
{
|
||||
(*dispatcher)(opaque, tev, var);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Class>
|
||||
class ObjectEventSlot: public EventSlotBase
|
||||
{
|
||||
public:
|
||||
typedef void (Class::*method_ptr_t)(ETransmissionEvent tev, EventVariant var);
|
||||
|
||||
method_ptr_t pm;
|
||||
Class* po;
|
||||
|
||||
ObjectEventSlot(Class* o, method_ptr_t m): pm(m), po(o) {}
|
||||
|
||||
void emit(ETransmissionEvent tev, EventVariant var) ATR_OVERRIDE
|
||||
{
|
||||
(po->*pm)(tev, var);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct EventSlot
|
||||
{
|
||||
mutable EventSlotBase* slot;
|
||||
// Create empty slot. Calls are ignored.
|
||||
EventSlot(): slot(0) {}
|
||||
|
||||
// "Stealing" copy constructor, following the auto_ptr method.
|
||||
// This isn't very nice, but no other way to do it in C++03
|
||||
// without rvalue-reference and move.
|
||||
EventSlot(const EventSlot& victim)
|
||||
{
|
||||
slot = victim.slot; // Should MOVE.
|
||||
victim.slot = 0;
|
||||
}
|
||||
|
||||
EventSlot(void* op, EventSlotBase::dispatcher_t* disp)
|
||||
{
|
||||
slot = new SimpleEventSlot(op, disp);
|
||||
}
|
||||
|
||||
template <class ObjectClass>
|
||||
EventSlot(ObjectClass* obj, typename ObjectEventSlot<ObjectClass>::method_ptr_t method)
|
||||
{
|
||||
slot = new ObjectEventSlot<ObjectClass>(obj, method);
|
||||
}
|
||||
|
||||
void emit(ETransmissionEvent tev, EventVariant var)
|
||||
{
|
||||
if (!slot)
|
||||
return;
|
||||
slot->emit(tev, var);
|
||||
}
|
||||
|
||||
~EventSlot()
|
||||
{
|
||||
if (slot)
|
||||
delete slot;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Old UDT library specific classes, moved from utilities as utilities
|
||||
// should now be general-purpose.
|
||||
|
||||
class CTimer
|
||||
{
|
||||
public:
|
||||
CTimer();
|
||||
~CTimer();
|
||||
|
||||
public:
|
||||
|
||||
/// Sleep for "interval_tk" CCs.
|
||||
/// @param [in] interval_tk CCs to sleep.
|
||||
|
||||
void sleep(uint64_t interval_tk);
|
||||
|
||||
/// Seelp until CC "nexttime_tk".
|
||||
/// @param [in] nexttime_tk next time the caller is waken up.
|
||||
|
||||
void sleepto(uint64_t nexttime_tk);
|
||||
|
||||
/// Stop the sleep() or sleepto() methods.
|
||||
|
||||
void interrupt();
|
||||
|
||||
/// trigger the clock for a tick, for better granuality in no_busy_waiting timer.
|
||||
|
||||
void tick();
|
||||
|
||||
public:
|
||||
|
||||
/// Read the CPU clock cycle into x.
|
||||
/// @param [out] x to record cpu clock cycles.
|
||||
|
||||
static void rdtsc(uint64_t &x);
|
||||
|
||||
/// return the CPU frequency.
|
||||
/// @return CPU frequency.
|
||||
|
||||
static uint64_t getCPUFrequency();
|
||||
|
||||
/// check the current time, 64bit, in microseconds.
|
||||
/// @return current time in microseconds.
|
||||
|
||||
static uint64_t getTime();
|
||||
|
||||
/// trigger an event such as new connection, close, new data, etc. for "select" call.
|
||||
|
||||
static void triggerEvent();
|
||||
|
||||
enum EWait {WT_EVENT, WT_ERROR, WT_TIMEOUT};
|
||||
|
||||
/// wait for an event to br triggered by "triggerEvent".
|
||||
/// @retval WT_EVENT The event has happened
|
||||
/// @retval WT_TIMEOUT The event hasn't happened, the function exited due to timeout
|
||||
/// @retval WT_ERROR The function has exit due to an error
|
||||
|
||||
static EWait waitForEvent();
|
||||
|
||||
/// sleep for a short interval. exact sleep time does not matter
|
||||
|
||||
static void sleep();
|
||||
|
||||
/// Wait for condition with timeout
|
||||
/// @param [in] cond Condition variable to wait for
|
||||
/// @param [in] mutex locked mutex associated with the condition variable
|
||||
/// @param [in] delay timeout in microseconds
|
||||
/// @retval 0 Wait was successfull
|
||||
/// @retval ETIMEDOUT The wait timed out
|
||||
|
||||
static int condTimedWaitUS(pthread_cond_t* cond, pthread_mutex_t* mutex, uint64_t delay);
|
||||
|
||||
private:
|
||||
uint64_t getTimeInMicroSec();
|
||||
|
||||
private:
|
||||
uint64_t m_ullSchedTime_tk; // next schedulled time
|
||||
|
||||
pthread_cond_t m_TickCond;
|
||||
pthread_mutex_t m_TickLock;
|
||||
|
||||
static pthread_cond_t m_EventCond;
|
||||
static pthread_mutex_t m_EventLock;
|
||||
|
||||
private:
|
||||
static uint64_t s_ullCPUFrequency; // CPU frequency : clock cycles per microsecond
|
||||
static uint64_t readCPUFrequency();
|
||||
static bool m_bUseMicroSecond; // No higher resolution timer available, use gettimeofday().
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CGuard
|
||||
{
|
||||
public:
|
||||
/// Constructs CGuard, which locks the given mutex for
|
||||
/// the scope where this object exists.
|
||||
/// @param lock Mutex to lock
|
||||
/// @param if_condition If this is false, CGuard will do completely nothing
|
||||
CGuard(pthread_mutex_t& lock, bool if_condition = true);
|
||||
~CGuard();
|
||||
|
||||
public:
|
||||
static int enterCS(pthread_mutex_t& lock);
|
||||
static int leaveCS(pthread_mutex_t& lock);
|
||||
|
||||
static void createMutex(pthread_mutex_t& lock);
|
||||
static void releaseMutex(pthread_mutex_t& lock);
|
||||
|
||||
static void createCond(pthread_cond_t& cond);
|
||||
static void releaseCond(pthread_cond_t& cond);
|
||||
|
||||
void forceUnlock();
|
||||
|
||||
private:
|
||||
pthread_mutex_t& m_Mutex; // Alias name of the mutex to be protected
|
||||
int m_iLocked; // Locking status
|
||||
|
||||
CGuard& operator=(const CGuard&);
|
||||
};
|
||||
|
||||
class InvertedGuard
|
||||
{
|
||||
pthread_mutex_t* m_pMutex;
|
||||
public:
|
||||
|
||||
InvertedGuard(pthread_mutex_t* smutex): m_pMutex(smutex)
|
||||
{
|
||||
if ( !smutex )
|
||||
return;
|
||||
|
||||
CGuard::leaveCS(*smutex);
|
||||
}
|
||||
|
||||
~InvertedGuard()
|
||||
{
|
||||
if ( !m_pMutex )
|
||||
return;
|
||||
|
||||
CGuard::enterCS(*m_pMutex);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// UDT Sequence Number 0 - (2^31 - 1)
|
||||
|
||||
// seqcmp: compare two seq#, considering the wraping
|
||||
// seqlen: length from the 1st to the 2nd seq#, including both
|
||||
// seqoff: offset from the 2nd to the 1st seq#
|
||||
// incseq: increase the seq# by 1
|
||||
// decseq: decrease the seq# by 1
|
||||
// incseq: increase the seq# by a given offset
|
||||
|
||||
class CSeqNo
|
||||
{
|
||||
public:
|
||||
|
||||
/// This behaves like seq1 - seq2, in comparison to numbers,
|
||||
/// and with the statement that only the sign of the result matters.
|
||||
/// That is, it returns a negative value if seq1 < seq2,
|
||||
/// positive if seq1 > seq2, and zero if they are equal.
|
||||
/// The only correct application of this function is when you
|
||||
/// compare two values and it works faster than seqoff. However
|
||||
/// the result's meaning is only in its sign. DO NOT USE THE
|
||||
/// VALUE for any other purpose. It is not meant to be the
|
||||
/// distance between two sequence numbers.
|
||||
///
|
||||
/// Example: to check if (seq1 %> seq2): seqcmp(seq1, seq2) > 0.
|
||||
inline static int seqcmp(int32_t seq1, int32_t seq2)
|
||||
{return (abs(seq1 - seq2) < m_iSeqNoTH) ? (seq1 - seq2) : (seq2 - seq1);}
|
||||
|
||||
/// This function measures a length of the range from seq1 to seq2,
|
||||
/// WITH A PRECONDITION that certainly @a seq1 is earlier than @a seq2.
|
||||
/// This can also include an enormously large distance between them,
|
||||
/// that is, exceeding the m_iSeqNoTH value (can be also used to test
|
||||
/// if this distance is larger). Prior to calling this function the
|
||||
/// caller must be certain that @a seq2 is a sequence coming from a
|
||||
/// later time than @a seq1, and still, of course, this distance didn't
|
||||
/// exceed m_iMaxSeqNo.
|
||||
inline static int seqlen(int32_t seq1, int32_t seq2)
|
||||
{return (seq1 <= seq2) ? (seq2 - seq1 + 1) : (seq2 - seq1 + m_iMaxSeqNo + 2);}
|
||||
|
||||
/// This behaves like seq2 - seq1, with the precondition that the true
|
||||
/// distance between two sequence numbers never exceeds m_iSeqNoTH.
|
||||
/// That is, if the difference in numeric values of these two arguments
|
||||
/// exceeds m_iSeqNoTH, it is treated as if the later of these two
|
||||
/// sequence numbers has overflown and actually a segment of the
|
||||
/// MAX+1 value should be added to it to get the proper result.
|
||||
///
|
||||
/// Note: this function does more calculations than seqcmp, so it should
|
||||
/// be used if you need the exact distance between two sequences. If
|
||||
/// you are only interested with their relationship, use seqcmp.
|
||||
inline static int seqoff(int32_t seq1, int32_t seq2)
|
||||
{
|
||||
if (abs(seq1 - seq2) < m_iSeqNoTH)
|
||||
return seq2 - seq1;
|
||||
|
||||
if (seq1 < seq2)
|
||||
return seq2 - seq1 - m_iMaxSeqNo - 1;
|
||||
|
||||
return seq2 - seq1 + m_iMaxSeqNo + 1;
|
||||
}
|
||||
|
||||
inline static int32_t incseq(int32_t seq)
|
||||
{return (seq == m_iMaxSeqNo) ? 0 : seq + 1;}
|
||||
|
||||
inline static int32_t decseq(int32_t seq)
|
||||
{return (seq == 0) ? m_iMaxSeqNo : seq - 1;}
|
||||
|
||||
inline static int32_t incseq(int32_t seq, int32_t inc)
|
||||
{return (m_iMaxSeqNo - seq >= inc) ? seq + inc : seq - m_iMaxSeqNo + inc - 1;}
|
||||
// m_iMaxSeqNo >= inc + sec --- inc + sec <= m_iMaxSeqNo
|
||||
// if inc + sec > m_iMaxSeqNo then return seq + inc - (m_iMaxSeqNo+1)
|
||||
|
||||
inline static int32_t decseq(int32_t seq, int32_t dec)
|
||||
{
|
||||
// Check if seq - dec < 0, but before it would have happened
|
||||
if ( seq < dec )
|
||||
{
|
||||
int32_t left = dec - seq; // This is so many that is left after dragging dec to 0
|
||||
// So now decrement the (m_iMaxSeqNo+1) by "left"
|
||||
return m_iMaxSeqNo - left + 1;
|
||||
}
|
||||
return seq - dec;
|
||||
}
|
||||
|
||||
public:
|
||||
static const int32_t m_iSeqNoTH = 0x3FFFFFFF; // threshold for comparing seq. no.
|
||||
static const int32_t m_iMaxSeqNo = 0x7FFFFFFF; // maximum sequence number used in UDT
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// UDT ACK Sub-sequence Number: 0 - (2^31 - 1)
|
||||
|
||||
class CAckNo
|
||||
{
|
||||
public:
|
||||
inline static int32_t incack(int32_t ackno)
|
||||
{return (ackno == m_iMaxAckSeqNo) ? 0 : ackno + 1;}
|
||||
|
||||
public:
|
||||
static const int32_t m_iMaxAckSeqNo = 0x7FFFFFFF; // maximum ACK sub-sequence number used in UDT
|
||||
};
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct CIPAddress
|
||||
{
|
||||
static bool ipcmp(const struct sockaddr* addr1, const struct sockaddr* addr2, int ver = AF_INET);
|
||||
static void ntop(const struct sockaddr* addr, uint32_t ip[4], int ver = AF_INET);
|
||||
static void pton(struct sockaddr* addr, const uint32_t ip[4], int ver = AF_INET);
|
||||
static std::string show(const struct sockaddr* adr);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct CMD5
|
||||
{
|
||||
static void compute(const char* input, unsigned char result[16]);
|
||||
};
|
||||
|
||||
// Debug stats
|
||||
template <size_t SIZE>
|
||||
class StatsLossRecords
|
||||
{
|
||||
int32_t initseq;
|
||||
std::bitset<SIZE> array;
|
||||
|
||||
public:
|
||||
|
||||
StatsLossRecords(): initseq(-1) {}
|
||||
|
||||
// To check if this structure still keeps record of that sequence.
|
||||
// This is to check if the information about this not being found
|
||||
// is still reliable.
|
||||
bool exists(int32_t seq)
|
||||
{
|
||||
return initseq != -1 && CSeqNo::seqcmp(seq, initseq) >= 0;
|
||||
}
|
||||
|
||||
int32_t base() { return initseq; }
|
||||
|
||||
void clear()
|
||||
{
|
||||
initseq = -1;
|
||||
array.reset();
|
||||
}
|
||||
|
||||
void add(int32_t lo, int32_t hi)
|
||||
{
|
||||
int32_t end = CSeqNo::incseq(hi);
|
||||
for (int32_t i = lo; i != end; i = CSeqNo::incseq(i))
|
||||
add(i);
|
||||
}
|
||||
|
||||
void add(int32_t seq)
|
||||
{
|
||||
if ( array.none() )
|
||||
{
|
||||
// May happen it wasn't initialized. Set it as initial loss sequence.
|
||||
initseq = seq;
|
||||
array[0] = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate the distance between this seq and the oldest one.
|
||||
int seqdiff = CSeqNo::seqoff(initseq, seq);
|
||||
if ( seqdiff > int(SIZE) )
|
||||
{
|
||||
// Size exceeded. Drop the oldest sequences.
|
||||
// First calculate how many must be removed.
|
||||
size_t toremove = seqdiff - SIZE;
|
||||
// Now, since that position, find the nearest 1
|
||||
while ( !array[toremove] && toremove <= SIZE )
|
||||
++toremove;
|
||||
|
||||
// All have to be dropped, so simply reset the array
|
||||
if ( toremove == SIZE )
|
||||
{
|
||||
initseq = seq;
|
||||
array[0] = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Now do the shift of the first found 1 to position 0
|
||||
// and its index add to initseq
|
||||
initseq += toremove;
|
||||
seqdiff -= toremove;
|
||||
array >>= toremove;
|
||||
}
|
||||
|
||||
// Now set appropriate bit that represents this seq
|
||||
array[seqdiff] = true;
|
||||
}
|
||||
|
||||
StatsLossRecords& operator << (int32_t seq)
|
||||
{
|
||||
add(seq);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void remove(int32_t seq)
|
||||
{
|
||||
// Check if is in range. If not, ignore.
|
||||
int seqdiff = CSeqNo::seqoff(initseq, seq);
|
||||
if ( seqdiff < 0 )
|
||||
return; // already out of array
|
||||
if ( seqdiff > SIZE )
|
||||
return; // never was added!
|
||||
|
||||
array[seqdiff] = true;
|
||||
}
|
||||
|
||||
bool find(int32_t seq) const
|
||||
{
|
||||
int seqdiff = CSeqNo::seqoff(initseq, seq);
|
||||
if ( seqdiff < 0 )
|
||||
return false; // already out of array
|
||||
if ( size_t(seqdiff) > SIZE )
|
||||
return false; // never was added!
|
||||
|
||||
return array[seqdiff];
|
||||
}
|
||||
|
||||
#if HAVE_CXX11
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
std::string out;
|
||||
for (size_t i = 0; i < SIZE; ++i)
|
||||
{
|
||||
if ( array[i] )
|
||||
out += std::to_string(initseq+i) + " ";
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
// Version parsing
|
||||
inline ATR_CONSTEXPR uint32_t SrtVersion(int major, int minor, int patch)
|
||||
{
|
||||
return patch + minor*0x100 + major*0x10000;
|
||||
}
|
||||
|
||||
inline int32_t SrtParseVersion(const char* v)
|
||||
{
|
||||
int major, minor, patch;
|
||||
int result = sscanf(v, "%d.%d.%d", &major, &minor, &patch);
|
||||
|
||||
if (result != 3)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return major*0x10000 + minor*0x100 + patch;
|
||||
}
|
||||
|
||||
inline std::string SrtVersionString(int version)
|
||||
{
|
||||
int patch = version % 0x100;
|
||||
int minor = (version/0x100)%0x100;
|
||||
int major = version/0x10000;
|
||||
|
||||
char buf[20];
|
||||
sprintf(buf, "%d.%d.%d", major, minor, patch);
|
||||
return buf;
|
||||
}
|
||||
|
||||
#endif
|
643
trunk/3rdparty/srt-1-fit/srtcore/congctl.cpp
vendored
Normal file
643
trunk/3rdparty/srt-1-fit/srtcore/congctl.cpp
vendored
Normal file
|
@ -0,0 +1,643 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
// This is a controversial thing, so temporarily blocking
|
||||
//#define SRT_ENABLE_SYSTEMBUFFER_TRACE
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef SRT_ENABLE_SYSTEMBUFFER_TRACE
|
||||
#if defined(unix)
|
||||
// XXX will be nonportable
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <cmath>
|
||||
|
||||
|
||||
#include "common.h"
|
||||
#include "core.h"
|
||||
#include "queue.h"
|
||||
#include "packet.h"
|
||||
#include "congctl.h"
|
||||
#include "logging.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace srt_logging;
|
||||
|
||||
SrtCongestionControlBase::SrtCongestionControlBase(CUDT* parent)
|
||||
{
|
||||
m_parent = parent;
|
||||
m_dMaxCWndSize = m_parent->flowWindowSize();
|
||||
// RcvRate (deliveryRate()), RTT and Bandwidth can be read directly from CUDT when needed.
|
||||
m_dCWndSize = 1000;
|
||||
m_dPktSndPeriod = 1;
|
||||
}
|
||||
|
||||
void SrtCongestion::Check()
|
||||
{
|
||||
if (!congctl)
|
||||
throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0);
|
||||
}
|
||||
|
||||
// Useful macro to shorthand passing a method as argument
|
||||
// Requires "Me" name by which a class refers to itself
|
||||
#define SSLOT(method) EventSlot(this, &Me:: method)
|
||||
|
||||
class LiveCC: public SrtCongestionControlBase
|
||||
{
|
||||
int64_t m_llSndMaxBW; //Max bandwidth (bytes/sec)
|
||||
size_t m_zSndAvgPayloadSize; //Average Payload Size of packets to xmit
|
||||
size_t m_zMaxPayloadSize;
|
||||
|
||||
// NAKREPORT stuff.
|
||||
int m_iMinNakInterval_us; // Minimum NAK Report Period (usec)
|
||||
int m_iNakReportAccel; // NAK Report Period (RTT) accelerator
|
||||
|
||||
typedef LiveCC Me; // required for SSLOT macro
|
||||
|
||||
public:
|
||||
|
||||
LiveCC(CUDT* parent)
|
||||
: SrtCongestionControlBase(parent)
|
||||
{
|
||||
m_llSndMaxBW = BW_INFINITE; // 1 Gbbps in Bytes/sec BW_INFINITE
|
||||
m_zMaxPayloadSize = parent->OPT_PayloadSize();
|
||||
if ( m_zMaxPayloadSize == 0 )
|
||||
m_zMaxPayloadSize = parent->maxPayloadSize();
|
||||
m_zSndAvgPayloadSize = m_zMaxPayloadSize;
|
||||
|
||||
m_iMinNakInterval_us = 20000; //Minimum NAK Report Period (usec)
|
||||
m_iNakReportAccel = 2; //Default NAK Report Period (RTT) accelerator
|
||||
|
||||
HLOGC(cclog.Debug, log << "Creating LiveCC: bw=" << m_llSndMaxBW << " avgplsize=" << m_zSndAvgPayloadSize);
|
||||
|
||||
updatePktSndPeriod();
|
||||
|
||||
|
||||
// NOTE: TEV_SEND gets dispatched from Sending thread, all others
|
||||
// from receiving thread.
|
||||
parent->ConnectSignal(TEV_SEND, SSLOT(updatePayloadSize));
|
||||
|
||||
/*
|
||||
* Readjust the max SndPeriod onACK (and onTimeout)
|
||||
*/
|
||||
parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(updatePktSndPeriod_onTimer));
|
||||
parent->ConnectSignal(TEV_ACK, SSLOT(updatePktSndPeriod_onAck));
|
||||
}
|
||||
|
||||
bool checkTransArgs(SrtCongestion::TransAPI api, SrtCongestion::TransDir dir, const char* , size_t size, int , bool ) ATR_OVERRIDE
|
||||
{
|
||||
if (api != SrtCongestion::STA_MESSAGE)
|
||||
{
|
||||
LOGC(cclog.Error, log << "LiveCC: invalid API use. Only sendmsg/recvmsg allowed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dir == SrtCongestion::STAD_SEND)
|
||||
{
|
||||
// For sending, check if the size of data doesn't exceed the maximum live packet size.
|
||||
if (size > m_zMaxPayloadSize)
|
||||
{
|
||||
LOGC(cclog.Error, log << "LiveCC: payload size: " << size << " exceeds maximum allowed " << m_zMaxPayloadSize);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For receiving, check if the buffer has enough space to keep the payload.
|
||||
if (size < m_zMaxPayloadSize)
|
||||
{
|
||||
LOGC(cclog.Error, log << "LiveCC: buffer size: " << size << " is too small for the maximum possible " << m_zMaxPayloadSize);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// XXX You can decide here if the not-fully-packed packet should require immediate ACK or not.
|
||||
// bool needsQuickACK(const CPacket& pkt) ATR_OVERRIDE
|
||||
|
||||
virtual int64_t sndBandwidth() ATR_OVERRIDE { return m_llSndMaxBW; }
|
||||
|
||||
private:
|
||||
// SLOTS:
|
||||
|
||||
// TEV_SEND -> CPacket*.
|
||||
void updatePayloadSize(ETransmissionEvent, EventVariant var)
|
||||
{
|
||||
const CPacket& packet = *var.get<EventVariant::PACKET>();
|
||||
|
||||
// XXX NOTE: TEV_SEND is sent from CSndQueue::worker thread, which is
|
||||
// different to threads running any other events (TEV_CHECKTIMER and TEV_ACK).
|
||||
// The m_zSndAvgPayloadSize field is however left unguarded because
|
||||
// there's no other modifier of this field.
|
||||
// Worst case scenario, the procedure running in CRcvQueue::worker
|
||||
// thread will pick up a "slightly outdated" average value from this
|
||||
// field - this is insignificant.
|
||||
m_zSndAvgPayloadSize = avg_iir<128, size_t>(m_zSndAvgPayloadSize, packet.getLength());
|
||||
HLOGC(cclog.Debug, log << "LiveCC: avg payload size updated: " << m_zSndAvgPayloadSize);
|
||||
}
|
||||
|
||||
void updatePktSndPeriod_onTimer(ETransmissionEvent , EventVariant var)
|
||||
{
|
||||
if ( var.get<EventVariant::STAGE>() != TEV_CHT_INIT )
|
||||
updatePktSndPeriod();
|
||||
}
|
||||
|
||||
void updatePktSndPeriod_onAck(ETransmissionEvent , EventVariant )
|
||||
{
|
||||
updatePktSndPeriod();
|
||||
}
|
||||
|
||||
void updatePktSndPeriod()
|
||||
{
|
||||
// packet = payload + header
|
||||
const double pktsize = (double) m_zSndAvgPayloadSize + CPacket::SRT_DATA_HDR_SIZE;
|
||||
m_dPktSndPeriod = 1000 * 1000.0 * (pktsize / m_llSndMaxBW);
|
||||
HLOGC(cclog.Debug, log << "LiveCC: sending period updated: " << m_dPktSndPeriod
|
||||
<< " (pktsize=" << pktsize << ", bw=" << m_llSndMaxBW);
|
||||
}
|
||||
|
||||
void setMaxBW(int64_t maxbw)
|
||||
{
|
||||
m_llSndMaxBW = maxbw > 0 ? maxbw : BW_INFINITE;
|
||||
updatePktSndPeriod();
|
||||
|
||||
#ifdef SRT_ENABLE_NOCWND
|
||||
/*
|
||||
* UDT default flow control should not trigger under normal SRT operation
|
||||
* UDT stops sending if the number of packets in transit (not acknowledged)
|
||||
* is larger than the congestion window.
|
||||
* Up to SRT 1.0.6, this value was set at 1000 pkts, which may be insufficient
|
||||
* for satellite links with ~1000 msec RTT and high bit rate.
|
||||
*/
|
||||
// XXX Consider making this a socket option.
|
||||
m_dCWndSize = m_dMaxCWndSize;
|
||||
#else
|
||||
m_dCWndSize = 1000;
|
||||
#endif
|
||||
}
|
||||
|
||||
void updateBandwidth(int64_t maxbw, int64_t bw) ATR_OVERRIDE
|
||||
{
|
||||
// bw is the bandwidth calculated with regard to the
|
||||
// SRTO_INPUTBW and SRTO_OHEADBW parameters. The maxbw
|
||||
// value simply represents the SRTO_MAXBW setting.
|
||||
if (maxbw)
|
||||
{
|
||||
setMaxBW(maxbw);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bw == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
setMaxBW(bw);
|
||||
}
|
||||
|
||||
SrtCongestion::RexmitMethod rexmitMethod() ATR_OVERRIDE
|
||||
{
|
||||
return SrtCongestion::SRM_FASTREXMIT;
|
||||
}
|
||||
|
||||
uint64_t updateNAKInterval(uint64_t nakint_tk, int /*rcv_speed*/, size_t /*loss_length*/) ATR_OVERRIDE
|
||||
{
|
||||
/*
|
||||
* duB:
|
||||
* The RTT accounts for the time for the last NAK to reach sender and start resending lost pkts.
|
||||
* The rcv_speed add the time to resend all the pkts in the loss list.
|
||||
*
|
||||
* For realtime Transport Stream content, pkts/sec is not a good indication of time to transmit
|
||||
* since packets are not filled to m_iMSS and packet size average is lower than (7*188)
|
||||
* for low bit rates.
|
||||
* If NAK report is lost, another cycle (RTT) is requred which is bad for low latency so we
|
||||
* accelerate the NAK Reports frequency, at the cost of possible duplicate resend.
|
||||
* Finally, the UDT4 native minimum NAK interval (m_ullMinNakInt_tk) is 300 ms which is too high
|
||||
* (~10 i30 video frames) to maintain low latency.
|
||||
*/
|
||||
|
||||
// Note: this value will still be reshaped to defined minimum,
|
||||
// as per minNAKInterval.
|
||||
return nakint_tk / m_iNakReportAccel;
|
||||
}
|
||||
|
||||
uint64_t minNAKInterval() ATR_OVERRIDE
|
||||
{
|
||||
return m_iMinNakInterval_us * CTimer::getCPUFrequency();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
class FileCC : public SrtCongestionControlBase
|
||||
{
|
||||
typedef FileCC Me; // Required by SSLOT macro
|
||||
|
||||
// Fields from CUDTCC
|
||||
int m_iRCInterval; // UDT Rate control interval
|
||||
uint64_t m_LastRCTime; // last rate increase time
|
||||
bool m_bSlowStart; // if in slow start phase
|
||||
int32_t m_iLastAck; // last ACKed seq no
|
||||
bool m_bLoss; // if loss happened since last rate increase
|
||||
int32_t m_iLastDecSeq; // max pkt seq no sent out when last decrease happened
|
||||
double m_dLastDecPeriod; // value of pktsndperiod when last decrease happened
|
||||
int m_iNAKCount; // NAK counter
|
||||
int m_iDecRandom; // random threshold on decrease by number of loss events
|
||||
int m_iAvgNAKNum; // average number of NAKs per congestion
|
||||
int m_iDecCount; // number of decreases in a congestion epoch
|
||||
|
||||
int64_t m_maxSR;
|
||||
|
||||
public:
|
||||
|
||||
FileCC(CUDT* parent)
|
||||
: SrtCongestionControlBase(parent)
|
||||
, m_iRCInterval(CUDT::COMM_SYN_INTERVAL_US)
|
||||
, m_LastRCTime(CTimer::getTime())
|
||||
, m_bSlowStart(true)
|
||||
, m_iLastAck(parent->sndSeqNo())
|
||||
, m_bLoss(false)
|
||||
, m_iLastDecSeq(CSeqNo::decseq(m_iLastAck))
|
||||
, m_dLastDecPeriod(1)
|
||||
, m_iNAKCount(0)
|
||||
, m_iDecRandom(1)
|
||||
, m_iAvgNAKNum(0)
|
||||
, m_iDecCount(0)
|
||||
, m_maxSR(0)
|
||||
{
|
||||
// Note that this function is called at the moment of
|
||||
// calling m_Smoother.configure(this). It is placed more less
|
||||
// at the same position as the series-of-parameter-setting-then-init
|
||||
// in the original UDT code. So, old CUDTCC::init() can be moved
|
||||
// to constructor.
|
||||
|
||||
// SmotherBase
|
||||
m_dCWndSize = 16;
|
||||
m_dPktSndPeriod = 1;
|
||||
|
||||
parent->ConnectSignal(TEV_ACK, SSLOT(updateSndPeriod));
|
||||
parent->ConnectSignal(TEV_LOSSREPORT, SSLOT(slowdownSndPeriod));
|
||||
parent->ConnectSignal(TEV_CHECKTIMER, SSLOT(speedupToWindowSize));
|
||||
|
||||
HLOGC(cclog.Debug, log << "Creating FileCC");
|
||||
}
|
||||
|
||||
bool checkTransArgs(SrtCongestion::TransAPI, SrtCongestion::TransDir, const char*, size_t, int, bool) ATR_OVERRIDE
|
||||
{
|
||||
// XXX
|
||||
// The FileCC has currently no restrictions, although it should be
|
||||
// rather required that the "message" mode or "buffer" mode be used on both sides the same.
|
||||
// This must be somehow checked separately.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool needsQuickACK(const CPacket& pkt) ATR_OVERRIDE
|
||||
{
|
||||
// For FileCC, treat non-full-buffer situation as an end-of-message situation;
|
||||
// request ACK to be sent immediately.
|
||||
if (pkt.getLength() < m_parent->maxPayloadSize())
|
||||
{
|
||||
// This is not a regular fixed size packet...
|
||||
// an irregular sized packet usually indicates the end of a message, so send an ACK immediately
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void updateBandwidth(int64_t maxbw, int64_t) ATR_OVERRIDE
|
||||
{
|
||||
if (maxbw != 0)
|
||||
{
|
||||
m_maxSR = maxbw;
|
||||
HLOGC(cclog.Debug, log << "FileCC: updated BW: " << m_maxSR);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// SLOTS
|
||||
void updateSndPeriod(ETransmissionEvent, EventVariant arg)
|
||||
{
|
||||
const int ack = arg.get<EventVariant::ACK>();
|
||||
|
||||
const uint64_t currtime = CTimer::getTime();
|
||||
if (currtime - m_LastRCTime < (uint64_t)m_iRCInterval)
|
||||
return;
|
||||
|
||||
m_LastRCTime = currtime;
|
||||
|
||||
if (m_bSlowStart)
|
||||
{
|
||||
m_dCWndSize += CSeqNo::seqlen(m_iLastAck, ack);
|
||||
m_iLastAck = ack;
|
||||
|
||||
if (m_dCWndSize > m_dMaxCWndSize)
|
||||
{
|
||||
m_bSlowStart = false;
|
||||
if (m_parent->deliveryRate() > 0)
|
||||
{
|
||||
m_dPktSndPeriod = 1000000.0 / m_parent->deliveryRate();
|
||||
HLOGC(cclog.Debug, log << "FileCC: UPD (slowstart:ENDED) wndsize="
|
||||
<< m_dCWndSize << "/" << m_dMaxCWndSize
|
||||
<< " sndperiod=" << m_dPktSndPeriod << "us = 1M/("
|
||||
<< m_parent->deliveryRate() << " pkts/s)");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + m_iRCInterval);
|
||||
HLOGC(cclog.Debug, log << "FileCC: UPD (slowstart:ENDED) wndsize="
|
||||
<< m_dCWndSize << "/" << m_dMaxCWndSize
|
||||
<< " sndperiod=" << m_dPktSndPeriod << "us = wndsize/(RTT+RCIV) RTT="
|
||||
<< m_parent->RTT() << " RCIV=" << m_iRCInterval);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HLOGC(cclog.Debug, log << "FileCC: UPD (slowstart:KEPT) wndsize="
|
||||
<< m_dCWndSize << "/" << m_dMaxCWndSize
|
||||
<< " sndperiod=" << m_dPktSndPeriod << "us");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dCWndSize = m_parent->deliveryRate() / 1000000.0 * (m_parent->RTT() + m_iRCInterval) + 16;
|
||||
HLOGC(cclog.Debug, log << "FileCC: UPD (speed mode) wndsize="
|
||||
<< m_dCWndSize << "/" << m_dMaxCWndSize << " RTT = " << m_parent->RTT()
|
||||
<< " sndperiod=" << m_dPktSndPeriod << "us. deliverRate = "
|
||||
<< m_parent->deliveryRate() << " pkts/s)");
|
||||
}
|
||||
|
||||
if (!m_bSlowStart)
|
||||
{
|
||||
if (m_bLoss)
|
||||
{
|
||||
m_bLoss = false;
|
||||
}
|
||||
// During Slow Start, no rate increase
|
||||
else
|
||||
{
|
||||
double inc = 0;
|
||||
const int loss_bw = 2 * (1000000 / m_dLastDecPeriod); // 2 times last loss point
|
||||
const int bw_pktps = min(loss_bw, m_parent->bandwidth());
|
||||
|
||||
int64_t B = (int64_t)(bw_pktps - 1000000.0 / m_dPktSndPeriod);
|
||||
if ((m_dPktSndPeriod > m_dLastDecPeriod) && ((bw_pktps / 9) < B))
|
||||
B = bw_pktps / 9;
|
||||
if (B <= 0)
|
||||
inc = 1.0 / m_parent->MSS();
|
||||
else
|
||||
{
|
||||
// inc = max(10 ^ ceil(log10( B * MSS * 8 ) * Beta / MSS, 1/MSS)
|
||||
// Beta = 1.5 * 10^(-6)
|
||||
|
||||
inc = pow(10.0, ceil(log10(B * m_parent->MSS() * 8.0))) * 0.0000015 / m_parent->MSS();
|
||||
inc = max(inc, 1.0 / m_parent->MSS());
|
||||
}
|
||||
|
||||
HLOGC(cclog.Debug, log << "FileCC: UPD (slowstart:OFF) loss_bw=" << loss_bw
|
||||
<< " bandwidth=" << m_parent->bandwidth() << " inc=" << inc
|
||||
<< " m_dPktSndPeriod=" << m_dPktSndPeriod
|
||||
<< "->" << (m_dPktSndPeriod * m_iRCInterval) / (m_dPktSndPeriod * inc + m_iRCInterval));
|
||||
|
||||
m_dPktSndPeriod = (m_dPktSndPeriod * m_iRCInterval) / (m_dPktSndPeriod * inc + m_iRCInterval);
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_HEAVY_LOGGING
|
||||
// Try to do reverse-calculation for m_dPktSndPeriod, as per minSP below
|
||||
// sndperiod = mega / (maxbw / MSS)
|
||||
// 1/sndperiod = (maxbw/MSS) / mega
|
||||
// mega/sndperiod = maxbw/MSS
|
||||
// maxbw = (MSS*mega)/sndperiod
|
||||
uint64_t usedbw = (m_parent->MSS() * 1000000.0) / m_dPktSndPeriod;
|
||||
|
||||
#if defined(unix) && defined (SRT_ENABLE_SYSTEMBUFFER_TRACE)
|
||||
// Check the outgoing system queue level
|
||||
int udp_buffer_size = m_parent->sndQueue()->sockoptQuery(SOL_SOCKET, SO_SNDBUF);
|
||||
int udp_buffer_level = m_parent->sndQueue()->ioctlQuery(TIOCOUTQ);
|
||||
int udp_buffer_free = udp_buffer_size - udp_buffer_level;
|
||||
#else
|
||||
int udp_buffer_free = -1;
|
||||
#endif
|
||||
|
||||
HLOGC(cclog.Debug, log << "FileCC: UPD (slowstart:"
|
||||
<< (m_bSlowStart ? "ON" : "OFF") << ") wndsize=" << m_dCWndSize
|
||||
<< " sndperiod=" << m_dPktSndPeriod << "us BANDWIDTH USED:" << usedbw << " (limit: " << m_maxSR << ")"
|
||||
" SYSTEM BUFFER LEFT: " << udp_buffer_free);
|
||||
#endif
|
||||
|
||||
//set maximum transfer rate
|
||||
if (m_maxSR)
|
||||
{
|
||||
double minSP = 1000000.0 / (double(m_maxSR) / m_parent->MSS());
|
||||
if (m_dPktSndPeriod < minSP)
|
||||
{
|
||||
m_dPktSndPeriod = minSP;
|
||||
HLOGC(cclog.Debug, log << "FileCC: BW limited to " << m_maxSR
|
||||
<< " - SLOWDOWN sndperiod=" << m_dPktSndPeriod << "us");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// When a lossreport has been received, it might be due to having
|
||||
// reached the available bandwidth limit. Slowdown to avoid further losses.
|
||||
void slowdownSndPeriod(ETransmissionEvent, EventVariant arg)
|
||||
{
|
||||
const int32_t* losslist = arg.get_ptr();
|
||||
size_t losslist_size = arg.get_len();
|
||||
|
||||
// Sanity check. Should be impossible that TEV_LOSSREPORT event
|
||||
// is called with a nonempty loss list.
|
||||
if (losslist_size == 0)
|
||||
{
|
||||
LOGC(cclog.Error, log << "IPE: FileCC: empty loss list!");
|
||||
return;
|
||||
}
|
||||
|
||||
//Slow Start stopped, if it hasn't yet
|
||||
if (m_bSlowStart)
|
||||
{
|
||||
m_bSlowStart = false;
|
||||
if (m_parent->deliveryRate() > 0)
|
||||
{
|
||||
m_dPktSndPeriod = 1000000.0 / m_parent->deliveryRate();
|
||||
HLOGC(cclog.Debug, log << "FileCC: LOSS, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS mega/rate (rate="
|
||||
<< m_parent->deliveryRate() << ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + m_iRCInterval);
|
||||
HLOGC(cclog.Debug, log << "FileCC: LOSS, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS wndsize/(RTT+RCIV) (RTT="
|
||||
<< m_parent->RTT() << " RCIV=" << m_iRCInterval << ")");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
m_bLoss = true;
|
||||
|
||||
// TODO: const int pktsInFlight = CSeqNo::seqoff(m_iLastAck, m_parent->sndSeqNo());
|
||||
const int pktsInFlight = m_parent->RTT() / m_dPktSndPeriod;
|
||||
const int numPktsLost = m_parent->sndLossLength();
|
||||
const int lost_pcent_x10 = pktsInFlight > 0 ? (numPktsLost * 1000) / pktsInFlight : 0;
|
||||
|
||||
HLOGC(cclog.Debug, log << "FileCC: LOSS: "
|
||||
<< "sent=" << CSeqNo::seqlen(m_iLastAck, m_parent->sndSeqNo()) << ", inFlight=" << pktsInFlight
|
||||
<< ", lost=" << numPktsLost << " ("
|
||||
<< lost_pcent_x10 / 10 << "." << lost_pcent_x10 % 10 << "\%)");
|
||||
if (lost_pcent_x10 < 20) // 2.0%
|
||||
{
|
||||
HLOGC(cclog.Debug, log << "FileCC: LOSS: m_dLastDecPeriod=" << m_dLastDecPeriod << "->" << m_dPktSndPeriod);
|
||||
m_dLastDecPeriod = m_dPktSndPeriod;
|
||||
return;
|
||||
}
|
||||
|
||||
// In contradiction to UDT, TEV_LOSSREPORT will be reported also when
|
||||
// the lossreport is being sent again, periodically, as a result of
|
||||
// NAKREPORT feature. You should make sure that NAKREPORT is off when
|
||||
// using FileCC, so relying on SRTO_TRANSTYPE rather than
|
||||
// just SRTO_CONGESTION is recommended.
|
||||
int32_t lossbegin = SEQNO_VALUE::unwrap(losslist[0]);
|
||||
|
||||
if (CSeqNo::seqcmp(lossbegin, m_iLastDecSeq) > 0)
|
||||
{
|
||||
m_dLastDecPeriod = m_dPktSndPeriod;
|
||||
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.03);
|
||||
|
||||
const double loss_share_factor = 0.03;
|
||||
m_iAvgNAKNum = (int)ceil(m_iAvgNAKNum * (1 - loss_share_factor) + m_iNAKCount * loss_share_factor);
|
||||
m_iNAKCount = 1;
|
||||
m_iDecCount = 1;
|
||||
|
||||
m_iLastDecSeq = m_parent->sndSeqNo();
|
||||
|
||||
// remove global synchronization using randomization
|
||||
srand(m_iLastDecSeq);
|
||||
m_iDecRandom = (int)ceil(m_iAvgNAKNum * (double(rand()) / RAND_MAX));
|
||||
if (m_iDecRandom < 1)
|
||||
m_iDecRandom = 1;
|
||||
HLOGC(cclog.Debug, log << "FileCC: LOSS:NEW lseqno=" << lossbegin
|
||||
<< ", lastsentseqno=" << m_iLastDecSeq
|
||||
<< ", seqdiff=" << CSeqNo::seqoff(m_iLastDecSeq, lossbegin)
|
||||
<< ", rand=" << m_iDecRandom
|
||||
<< " avg NAK:" << m_iAvgNAKNum
|
||||
<< ", sndperiod=" << m_dPktSndPeriod << "us");
|
||||
}
|
||||
else if ((m_iDecCount++ < 5) && (0 == (++m_iNAKCount % m_iDecRandom)))
|
||||
{
|
||||
// 0.875^5 = 0.51, rate should not be decreased by more than half within a congestion period
|
||||
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.03);
|
||||
m_iLastDecSeq = m_parent->sndSeqNo();
|
||||
HLOGC(cclog.Debug, log << "FileCC: LOSS:PERIOD lseqno=" << lossbegin
|
||||
<< ", lastsentseqno=" << m_iLastDecSeq
|
||||
<< ", seqdiff=" << CSeqNo::seqoff(m_iLastDecSeq, lossbegin)
|
||||
<< ", deccnt=" << m_iDecCount
|
||||
<< ", decrnd=" << m_iDecRandom
|
||||
<< ", sndperiod=" << m_dPktSndPeriod << "us");
|
||||
}
|
||||
else
|
||||
{
|
||||
HLOGC(cclog.Debug, log << "FileCC: LOSS:STILL lseqno=" << lossbegin
|
||||
<< ", lastsentseqno=" << m_iLastDecSeq
|
||||
<< ", seqdiff=" << CSeqNo::seqoff(m_iLastDecSeq, lossbegin)
|
||||
<< ", deccnt=" << m_iDecCount
|
||||
<< ", decrnd=" << m_iDecRandom
|
||||
<< ", sndperiod=" << m_dPktSndPeriod << "us");
|
||||
}
|
||||
}
|
||||
|
||||
void speedupToWindowSize(ETransmissionEvent, EventVariant arg)
|
||||
{
|
||||
ECheckTimerStage stg = arg.get<EventVariant::STAGE>();
|
||||
|
||||
// TEV_INIT is in the beginning of checkTimers(), used
|
||||
// only to synchronize back the values (which is done in updateCC
|
||||
// after emitting the signal).
|
||||
if (stg == TEV_CHT_INIT)
|
||||
return;
|
||||
|
||||
if (m_bSlowStart)
|
||||
{
|
||||
m_bSlowStart = false;
|
||||
if (m_parent->deliveryRate() > 0)
|
||||
{
|
||||
m_dPktSndPeriod = 1000000.0 / m_parent->deliveryRate();
|
||||
HLOGC(cclog.Debug, log << "FileCC: CHKTIMER, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS mega/rate (rate="
|
||||
<< m_parent->deliveryRate() << ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dPktSndPeriod = m_dCWndSize / (m_parent->RTT() + m_iRCInterval);
|
||||
HLOGC(cclog.Debug, log << "FileCC: CHKTIMER, SLOWSTART:OFF, sndperiod=" << m_dPktSndPeriod << "us AS wndsize/(RTT+RCIV) (wndsize="
|
||||
<< setprecision(6) << m_dCWndSize << " RTT=" << m_parent->RTT() << " RCIV=" << m_iRCInterval << ")");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// XXX This code is a copy of legacy CUDTCC::onTimeout() body.
|
||||
// This part was commented out there already.
|
||||
/*
|
||||
m_dLastDecPeriod = m_dPktSndPeriod;
|
||||
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 2);
|
||||
m_iLastDecSeq = m_iLastAck;
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
SrtCongestion::RexmitMethod rexmitMethod() ATR_OVERRIDE
|
||||
{
|
||||
return SrtCongestion::SRM_LATEREXMIT;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#undef SSLOT
|
||||
|
||||
template <class Target>
|
||||
struct Creator
|
||||
{
|
||||
static SrtCongestionControlBase* Create(CUDT* parent) { return new Target(parent); }
|
||||
};
|
||||
|
||||
SrtCongestion::NamePtr SrtCongestion::congctls[N_CONTROLLERS] =
|
||||
{
|
||||
{"live", Creator<LiveCC>::Create },
|
||||
{"file", Creator<FileCC>::Create }
|
||||
};
|
||||
|
||||
|
||||
bool SrtCongestion::configure(CUDT* parent)
|
||||
{
|
||||
if (selector == N_CONTROLLERS)
|
||||
return false;
|
||||
|
||||
// Found a congctl, so call the creation function
|
||||
congctl = (*congctls[selector].second)(parent);
|
||||
|
||||
// The congctl should have pinned in all events
|
||||
// that are of its interest. It's stated that
|
||||
// it's ready after creation.
|
||||
return !!congctl;
|
||||
}
|
||||
|
||||
SrtCongestion::~SrtCongestion()
|
||||
{
|
||||
delete congctl;
|
||||
congctl = 0;
|
||||
}
|
206
trunk/3rdparty/srt-1-fit/srtcore/congctl.h
vendored
Normal file
206
trunk/3rdparty/srt-1-fit/srtcore/congctl.h
vendored
Normal file
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef INC__CONGCTL_H
|
||||
#define INC__CONGCTL_H
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
class CUDT;
|
||||
class SrtCongestionControlBase;
|
||||
|
||||
typedef SrtCongestionControlBase* srtcc_create_t(CUDT* parent);
|
||||
|
||||
class SrtCongestion
|
||||
{
|
||||
// Temporarily changed to linear searching, until this is exposed
|
||||
// for a user-defined controller.
|
||||
// Note that this is a pointer to function :)
|
||||
|
||||
static const size_t N_CONTROLLERS = 2;
|
||||
// The first/second is to mimic the map.
|
||||
typedef struct { const char* first; srtcc_create_t* second; } NamePtr;
|
||||
static NamePtr congctls[N_CONTROLLERS];
|
||||
|
||||
// This is a congctl container.
|
||||
SrtCongestionControlBase* congctl;
|
||||
size_t selector;
|
||||
|
||||
void Check();
|
||||
|
||||
public:
|
||||
|
||||
// If you predict to allow something to be done on controller also
|
||||
// before it is configured, call this first. If you need it configured,
|
||||
// you can rely on Check().
|
||||
bool ready() { return congctl; }
|
||||
SrtCongestionControlBase* operator->() { Check(); return congctl; }
|
||||
|
||||
// In the beginning it's uninitialized
|
||||
SrtCongestion(): congctl(), selector(N_CONTROLLERS) {}
|
||||
|
||||
struct IsName
|
||||
{
|
||||
std::string n;
|
||||
IsName(std::string nn): n(nn) {}
|
||||
bool operator()(NamePtr np) { return n == np.first; }
|
||||
};
|
||||
|
||||
// You can call select() multiple times, until finally
|
||||
// the 'configure' method is called.
|
||||
bool select(const std::string& name)
|
||||
{
|
||||
NamePtr* end = congctls+N_CONTROLLERS;
|
||||
NamePtr* try_selector = std::find_if(congctls, end, IsName(name));
|
||||
if (try_selector == end)
|
||||
return false;
|
||||
selector = try_selector - congctls;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string selected_name()
|
||||
{
|
||||
if (selector == N_CONTROLLERS)
|
||||
return "";
|
||||
return congctls[selector].first;
|
||||
}
|
||||
|
||||
// Copy constructor - important when listener-spawning
|
||||
// Things being done:
|
||||
// 1. The congctl is individual, so don't copy it. Set NULL.
|
||||
// 2. The selected name is copied so that it's configured correctly.
|
||||
SrtCongestion(const SrtCongestion& source): congctl(), selector(source.selector) {}
|
||||
|
||||
// This function will be called by the parent CUDT
|
||||
// in appropriate time. It should select appropriate
|
||||
// congctl basing on the value in selector, then
|
||||
// pin oneself in into CUDT for receiving event signals.
|
||||
bool configure(CUDT* parent);
|
||||
|
||||
// Will delete the pinned in congctl object.
|
||||
// This must be defined in *.cpp file due to virtual
|
||||
// destruction.
|
||||
~SrtCongestion();
|
||||
|
||||
enum RexmitMethod
|
||||
{
|
||||
SRM_LATEREXMIT,
|
||||
SRM_FASTREXMIT
|
||||
};
|
||||
|
||||
enum TransAPI
|
||||
{
|
||||
STA_MESSAGE = 0x1, // sendmsg/recvmsg functions
|
||||
STA_BUFFER = 0x2, // send/recv functions
|
||||
STA_FILE = 0x3, // sendfile/recvfile functions
|
||||
};
|
||||
|
||||
enum TransDir
|
||||
{
|
||||
STAD_RECV = 0,
|
||||
STAD_SEND = 1
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
class SrtCongestionControlBase
|
||||
{
|
||||
protected:
|
||||
// Here can be some common fields
|
||||
CUDT* m_parent;
|
||||
|
||||
double m_dPktSndPeriod;
|
||||
double m_dCWndSize;
|
||||
|
||||
//int m_iBandwidth; // NOT REQUIRED. Use m_parent->bandwidth() instead.
|
||||
double m_dMaxCWndSize;
|
||||
|
||||
//int m_iMSS; // NOT REQUIRED. Use m_parent->MSS() instead.
|
||||
//int32_t m_iSndCurrSeqNo; // NOT REQUIRED. Use m_parent->sndSeqNo().
|
||||
//int m_iRcvRate; // NOT REQUIRED. Use m_parent->deliveryRate() instead.
|
||||
//int m_RTT; // NOT REQUIRED. Use m_parent->RTT() instead.
|
||||
//char* m_pcParam; // Used to access m_llMaxBw. Use m_parent->maxBandwidth() instead.
|
||||
|
||||
// Constructor in protected section so that this class is semi-abstract.
|
||||
SrtCongestionControlBase(CUDT* parent);
|
||||
public:
|
||||
|
||||
// This could be also made abstract, but this causes a linkage
|
||||
// problem in C++: this would constitute the first virtual method,
|
||||
// and C++ compiler uses the location of the first virtual method as the
|
||||
// file to which it also emits the virtual call table. When this is
|
||||
// abstract, there would have to be simultaneously either defined
|
||||
// an empty method in congctl.cpp file (obviously never called),
|
||||
// or simply left empty body here.
|
||||
virtual ~SrtCongestionControlBase() { }
|
||||
|
||||
// All these functions that return values interesting for processing
|
||||
// by CUDT can be overridden. Normally they should refer to the fields
|
||||
// and these fields should keep the values as a state.
|
||||
virtual double pktSndPeriod_us() { return m_dPktSndPeriod; }
|
||||
virtual double cgWindowSize() { return m_dCWndSize; }
|
||||
virtual double cgWindowMaxSize() { return m_dMaxCWndSize; }
|
||||
|
||||
virtual int64_t sndBandwidth() { return 0; }
|
||||
|
||||
// If user-defined, will return nonzero value.
|
||||
// If not, it will be internally calculated.
|
||||
virtual int RTO() { return 0; }
|
||||
|
||||
// Maximum number of packets to trigger ACK sending.
|
||||
// Specifies the number of packets to receive before sending the ACK.
|
||||
// Used by CUDT together with ACKTimeout_us() to trigger ACK packet sending.
|
||||
virtual int ACKMaxPackets() const { return 0; }
|
||||
|
||||
// Periodical interval to send an ACK, in microseconds.
|
||||
// If user-defined, this value will be used to calculate
|
||||
// the next ACK time every time ACK is considered to be sent (see CUDT::checkTimers).
|
||||
// Otherwise this will be calculated internally in CUDT, normally taken
|
||||
// from CUDT::COMM_SYN_INTERVAL_US.
|
||||
virtual int ACKTimeout_us() const { return 0; }
|
||||
|
||||
// Called when the settings concerning m_llMaxBW were changed.
|
||||
// Arg 1: value of CUDT::m_llMaxBW
|
||||
// Arg 2: value calculated out of CUDT::m_llInputBW and CUDT::m_iOverheadBW.
|
||||
virtual void updateBandwidth(int64_t, int64_t) {}
|
||||
|
||||
virtual bool needsQuickACK(const CPacket&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Particular controller is allowed to agree or disagree on the use of particular API.
|
||||
virtual bool checkTransArgs(SrtCongestion::TransAPI , SrtCongestion::TransDir , const char* /*buffer*/, size_t /*size*/, int /*ttl*/, bool /*inorder*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual SrtCongestion::RexmitMethod rexmitMethod() = 0; // Implementation enforced.
|
||||
|
||||
virtual uint64_t updateNAKInterval(uint64_t nakint_tk, int rcv_speed, size_t loss_length)
|
||||
{
|
||||
if (rcv_speed > 0)
|
||||
nakint_tk += (loss_length * uint64_t(1000000) / rcv_speed) * CTimer::getCPUFrequency();
|
||||
|
||||
return nakint_tk;
|
||||
}
|
||||
|
||||
virtual uint64_t minNAKInterval()
|
||||
{
|
||||
return 0; // Leave default
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
9705
trunk/3rdparty/srt-1-fit/srtcore/core.cpp
vendored
Normal file
9705
trunk/3rdparty/srt-1-fit/srtcore/core.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
873
trunk/3rdparty/srt-1-fit/srtcore/core.h
vendored
Normal file
873
trunk/3rdparty/srt-1-fit/srtcore/core.h
vendored
Normal file
|
@ -0,0 +1,873 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Yunhong Gu, last updated 02/28/2012
|
||||
modified by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __UDT_CORE_H__
|
||||
#define __UDT_CORE_H__
|
||||
|
||||
#include <deque>
|
||||
#include <sstream>
|
||||
|
||||
#include "srt.h"
|
||||
#include "common.h"
|
||||
#include "list.h"
|
||||
#include "buffer.h"
|
||||
#include "window.h"
|
||||
#include "packet.h"
|
||||
#include "channel.h"
|
||||
#include "api.h"
|
||||
#include "cache.h"
|
||||
#include "queue.h"
|
||||
#include "handshake.h"
|
||||
#include "congctl.h"
|
||||
#include "packetfilter.h"
|
||||
#include "utilities.h"
|
||||
|
||||
#include <haicrypt.h>
|
||||
|
||||
namespace srt_logging
|
||||
{
|
||||
|
||||
extern Logger
|
||||
glog,
|
||||
// blog,
|
||||
mglog,
|
||||
dlog,
|
||||
tslog,
|
||||
rxlog,
|
||||
cclog;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// XXX Utility function - to be moved to utilities.h?
|
||||
template <class T>
|
||||
inline T CountIIR(T base, T newval, double factor)
|
||||
{
|
||||
if ( base == 0.0 )
|
||||
return newval;
|
||||
|
||||
T diff = newval - base;
|
||||
return base+T(diff*factor);
|
||||
}
|
||||
|
||||
// XXX Probably a better rework for that can be done - this can be
|
||||
// turned into a serializable structure, just like it's for CHandShake.
|
||||
enum AckDataItem
|
||||
{
|
||||
ACKD_RCVLASTACK = 0,
|
||||
ACKD_RTT = 1,
|
||||
ACKD_RTTVAR = 2,
|
||||
ACKD_BUFFERLEFT = 3,
|
||||
ACKD_TOTAL_SIZE_SMALL = 4,
|
||||
|
||||
// Extra fields existing in UDT (not always sent)
|
||||
|
||||
ACKD_RCVSPEED = 4, // length would be 16
|
||||
ACKD_BANDWIDTH = 5,
|
||||
ACKD_TOTAL_SIZE_UDTBASE = 6, // length = 24
|
||||
// Extra stats for SRT
|
||||
|
||||
ACKD_RCVRATE = 6,
|
||||
ACKD_TOTAL_SIZE_VER101 = 7, // length = 28
|
||||
ACKD_XMRATE = 7, // XXX This is a weird compat stuff. Version 1.1.3 defines it as ACKD_BANDWIDTH*m_iMaxSRTPayloadSize when set. Never got.
|
||||
// XXX NOTE: field number 7 may be used for something in future, need to confirm destruction of all !compat 1.0.2 version
|
||||
|
||||
ACKD_TOTAL_SIZE_VER102 = 8, // 32
|
||||
// FEATURE BLOCKED. Probably not to be restored.
|
||||
// ACKD_ACKBITMAP = 8,
|
||||
ACKD_TOTAL_SIZE = ACKD_TOTAL_SIZE_VER102 // length = 32 (or more)
|
||||
};
|
||||
const size_t ACKD_FIELD_SIZE = sizeof(int32_t);
|
||||
|
||||
// For HSv4 legacy handshake
|
||||
#define SRT_MAX_HSRETRY 10 /* Maximum SRT handshake retry */
|
||||
|
||||
enum SeqPairItems
|
||||
{
|
||||
SEQ_BEGIN = 0, SEQ_END = 1, SEQ_SIZE = 2
|
||||
};
|
||||
|
||||
// Extended SRT Congestion control class - only an incomplete definition required
|
||||
class CCryptoControl;
|
||||
|
||||
// XXX REFACTOR: The 'CUDT' class is to be merged with 'CUDTSocket'.
|
||||
// There's no reason for separating them, there's no case of having them
|
||||
// anyhow managed separately. After this is done, with a small help with
|
||||
// separating the internal abnormal path management (exceptions) from the
|
||||
// API (return values), through CUDTUnited, this class may become in future
|
||||
// an officially exposed C++ API.
|
||||
class CUDT
|
||||
{
|
||||
friend class CUDTSocket;
|
||||
friend class CUDTUnited;
|
||||
friend class CCC;
|
||||
friend struct CUDTComp;
|
||||
friend class CCache<CInfoBlock>;
|
||||
friend class CRendezvousQueue;
|
||||
friend class CSndQueue;
|
||||
friend class CRcvQueue;
|
||||
friend class CSndUList;
|
||||
friend class CRcvUList;
|
||||
friend class PacketFilter;
|
||||
|
||||
private: // constructor and desctructor
|
||||
|
||||
void construct();
|
||||
void clearData();
|
||||
CUDT();
|
||||
CUDT(const CUDT& ancestor);
|
||||
const CUDT& operator=(const CUDT&) {return *this;}
|
||||
~CUDT();
|
||||
|
||||
public: //API
|
||||
static int startup();
|
||||
static int cleanup();
|
||||
static SRTSOCKET socket(int af, int type = SOCK_STREAM, int protocol = 0);
|
||||
static int bind(SRTSOCKET u, const sockaddr* name, int namelen);
|
||||
static int bind(SRTSOCKET u, UDPSOCKET udpsock);
|
||||
static int listen(SRTSOCKET u, int backlog);
|
||||
static SRTSOCKET accept(SRTSOCKET u, sockaddr* addr, int* addrlen);
|
||||
static int connect(SRTSOCKET u, const sockaddr* name, int namelen, int32_t forced_isn);
|
||||
static int close(SRTSOCKET u);
|
||||
static int getpeername(SRTSOCKET u, sockaddr* name, int* namelen);
|
||||
static int getsockname(SRTSOCKET u, sockaddr* name, int* namelen);
|
||||
static int getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen);
|
||||
static int setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen);
|
||||
static int send(SRTSOCKET u, const char* buf, int len, int flags);
|
||||
static int recv(SRTSOCKET u, char* buf, int len, int flags);
|
||||
static int sendmsg(SRTSOCKET u, const char* buf, int len, int ttl = -1, bool inorder = false, uint64_t srctime = 0);
|
||||
static int recvmsg(SRTSOCKET u, char* buf, int len, uint64_t& srctime);
|
||||
static int sendmsg2(SRTSOCKET u, const char* buf, int len, ref_t<SRT_MSGCTRL> mctrl);
|
||||
static int recvmsg2(SRTSOCKET u, char* buf, int len, ref_t<SRT_MSGCTRL> mctrl);
|
||||
static int64_t sendfile(SRTSOCKET u, std::fstream& ifs, int64_t& offset, int64_t size, int block = SRT_DEFAULT_SENDFILE_BLOCK);
|
||||
static int64_t recvfile(SRTSOCKET u, std::fstream& ofs, int64_t& offset, int64_t size, int block = SRT_DEFAULT_RECVFILE_BLOCK);
|
||||
static int select(int nfds, ud_set* readfds, ud_set* writefds, ud_set* exceptfds, const timeval* timeout);
|
||||
static int selectEx(const std::vector<SRTSOCKET>& fds, std::vector<SRTSOCKET>* readfds, std::vector<SRTSOCKET>* writefds, std::vector<SRTSOCKET>* exceptfds, int64_t msTimeOut);
|
||||
static int epoll_create();
|
||||
static int epoll_add_usock(const int eid, const SRTSOCKET u, const int* events = NULL);
|
||||
static int epoll_add_ssock(const int eid, const SYSSOCKET s, const int* events = NULL);
|
||||
static int epoll_remove_usock(const int eid, const SRTSOCKET u);
|
||||
static int epoll_remove_ssock(const int eid, const SYSSOCKET s);
|
||||
static int epoll_update_usock(const int eid, const SRTSOCKET u, const int* events = NULL);
|
||||
static int epoll_update_ssock(const int eid, const SYSSOCKET s, const int* events = NULL);
|
||||
static int epoll_wait(const int eid, std::set<SRTSOCKET>* readfds, std::set<SRTSOCKET>* writefds, int64_t msTimeOut, std::set<SYSSOCKET>* lrfds = NULL, std::set<SYSSOCKET>* wrfds = NULL);
|
||||
static int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut);
|
||||
static int32_t epoll_set(const int eid, int32_t flags);
|
||||
static int epoll_release(const int eid);
|
||||
static CUDTException& getlasterror();
|
||||
static int bstats(SRTSOCKET u, CBytePerfMon* perf, bool clear = true, bool instantaneous = false);
|
||||
static SRT_SOCKSTATUS getsockstate(SRTSOCKET u);
|
||||
static bool setstreamid(SRTSOCKET u, const std::string& sid);
|
||||
static std::string getstreamid(SRTSOCKET u);
|
||||
static int getsndbuffer(SRTSOCKET u, size_t* blocks, size_t* bytes);
|
||||
static SRT_REJECT_REASON rejectReason(SRTSOCKET s);
|
||||
|
||||
static int setError(const CUDTException& e)
|
||||
{
|
||||
s_UDTUnited.setError(new CUDTException(e));
|
||||
return SRT_ERROR;
|
||||
}
|
||||
|
||||
public: // internal API
|
||||
static const SRTSOCKET INVALID_SOCK = -1; // invalid socket descriptor
|
||||
static const int ERROR = -1; // socket api error returned value
|
||||
|
||||
static const int HS_VERSION_UDT4 = 4;
|
||||
static const int HS_VERSION_SRT1 = 5;
|
||||
|
||||
// Parameters
|
||||
//
|
||||
// Note: use notation with X*1000*1000* ... instead of million zeros in a row.
|
||||
// In C++17 there is a possible notation of 5'000'000 for convenience, but that's
|
||||
// something only for a far future.
|
||||
static const int COMM_RESPONSE_TIMEOUT_MS = 5*1000; // 5 seconds
|
||||
static const int COMM_RESPONSE_MAX_EXP = 16;
|
||||
static const int SRT_TLPKTDROP_MINTHRESHOLD_MS = 1000;
|
||||
static const uint64_t COMM_KEEPALIVE_PERIOD_US = 1*1000*1000;
|
||||
static const int32_t COMM_SYN_INTERVAL_US = 10*1000;
|
||||
|
||||
int handshakeVersion()
|
||||
{
|
||||
return m_ConnRes.m_iVersion;
|
||||
}
|
||||
|
||||
std::string CONID() const
|
||||
{
|
||||
#if ENABLE_LOGGING
|
||||
std::ostringstream os;
|
||||
os << "%" << m_SocketID << ":";
|
||||
return os.str();
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
SRTSOCKET socketID() { return m_SocketID; }
|
||||
|
||||
static CUDT* getUDTHandle(SRTSOCKET u);
|
||||
static std::vector<SRTSOCKET> existingSockets();
|
||||
|
||||
void addressAndSend(CPacket& pkt);
|
||||
void sendSrtMsg(int cmd, uint32_t *srtdata_in = NULL, int srtlen_in = 0);
|
||||
|
||||
bool isTsbPd() { return m_bOPT_TsbPd; }
|
||||
int RTT() { return m_iRTT; }
|
||||
int32_t sndSeqNo() { return m_iSndCurrSeqNo; }
|
||||
int32_t rcvSeqNo() { return m_iRcvCurrSeqNo; }
|
||||
int flowWindowSize() { return m_iFlowWindowSize; }
|
||||
int32_t deliveryRate() { return m_iDeliveryRate; }
|
||||
int bandwidth() { return m_iBandwidth; }
|
||||
int64_t maxBandwidth() { return m_llMaxBW; }
|
||||
int MSS() { return m_iMSS; }
|
||||
size_t maxPayloadSize() { return m_iMaxSRTPayloadSize; }
|
||||
size_t OPT_PayloadSize() { return m_zOPT_ExpPayloadSize; }
|
||||
uint64_t minNAKInterval() { return m_ullMinNakInt_tk; }
|
||||
int32_t ISN() { return m_iISN; }
|
||||
int sndLossLength() { return m_pSndLossList->getLossLength(); }
|
||||
|
||||
// XXX See CUDT::tsbpd() to see how to implement it. This should
|
||||
// do the same as TLPKTDROP feature when skipping packets that are agreed
|
||||
// to be lost. Note that this is predicted to be called with TSBPD off.
|
||||
// This is to be exposed for the application so that it can require this
|
||||
// sequence to be skipped, if that packet has been otherwise arrived through
|
||||
// a different channel.
|
||||
void skipIncoming(int32_t seq);
|
||||
|
||||
void ConnectSignal(ETransmissionEvent tev, EventSlot sl);
|
||||
void DisconnectSignal(ETransmissionEvent tev);
|
||||
|
||||
private:
|
||||
/// initialize a UDT entity and bind to a local address.
|
||||
|
||||
void open();
|
||||
|
||||
/// Start listening to any connection request.
|
||||
|
||||
void setListenState();
|
||||
|
||||
/// Connect to a UDT entity listening at address "peer".
|
||||
/// @param peer [in] The address of the listening UDT entity.
|
||||
|
||||
void startConnect(const sockaddr* peer, int32_t forced_isn);
|
||||
|
||||
/// Process the response handshake packet. Failure reasons can be:
|
||||
/// * Socket is not in connecting state
|
||||
/// * Response @a pkt is not a handshake control message
|
||||
/// * Rendezvous socket has once processed a regular handshake
|
||||
/// @param pkt [in] handshake packet.
|
||||
/// @retval 0 Connection successful
|
||||
/// @retval 1 Connection in progress (m_ConnReq turned into RESPONSE)
|
||||
/// @retval -1 Connection failed
|
||||
|
||||
SRT_ATR_NODISCARD EConnectStatus processConnectResponse(const CPacket& pkt, CUDTException* eout, bool synchro) ATR_NOEXCEPT;
|
||||
|
||||
|
||||
// This function works in case of HSv5 rendezvous. It changes the state
|
||||
// according to the present state and received message type, as well as the
|
||||
// INITIATOR/RESPONDER side resolved through cookieContest().
|
||||
// The resulting data are:
|
||||
// - rsptype: handshake message type that should be sent back to the peer (nothing if URQ_DONE)
|
||||
// - needs_extension: the HSREQ/KMREQ or HSRSP/KMRSP extensions should be attached to the handshake message.
|
||||
// - RETURNED VALUE: if true, it means a URQ_CONCLUSION message was received with HSRSP/KMRSP extensions and needs HSRSP/KMRSP.
|
||||
void rendezvousSwitchState(ref_t<UDTRequestType> rsptype, ref_t<bool> needs_extension, ref_t<bool> needs_hsrsp);
|
||||
void cookieContest();
|
||||
|
||||
/// Interpret the incoming handshake packet in order to perform appropriate
|
||||
/// rendezvous FSM state transition if needed, and craft the response, serialized
|
||||
/// into the packet to be next sent.
|
||||
/// @param reqpkt Packet to be written with handshake data
|
||||
/// @param response incoming handshake response packet to be interpreted
|
||||
/// @param serv_addr incoming packet's address
|
||||
/// @param synchro True when this function was called in blocking mode
|
||||
/// @param rst Current read status to know if the HS packet was freshly received from the peer, or this is only a periodic update (RST_AGAIN)
|
||||
SRT_ATR_NODISCARD EConnectStatus processRendezvous(ref_t<CPacket> reqpkt, const CPacket &response, const sockaddr* serv_addr, bool synchro, EReadStatus);
|
||||
SRT_ATR_NODISCARD bool prepareConnectionObjects(const CHandShake &hs, HandshakeSide hsd, CUDTException *eout);
|
||||
SRT_ATR_NODISCARD EConnectStatus postConnect(const CPacket& response, bool rendezvous, CUDTException* eout, bool synchro);
|
||||
void applyResponseSettings();
|
||||
SRT_ATR_NODISCARD EConnectStatus processAsyncConnectResponse(const CPacket& pkt) ATR_NOEXCEPT;
|
||||
SRT_ATR_NODISCARD bool processAsyncConnectRequest(EReadStatus rst, EConnectStatus cst, const CPacket& response, const sockaddr* serv_addr);
|
||||
|
||||
void checkUpdateCryptoKeyLen(const char* loghdr, int32_t typefield);
|
||||
|
||||
SRT_ATR_NODISCARD size_t fillSrtHandshake_HSREQ(uint32_t* srtdata, size_t srtlen, int hs_version);
|
||||
SRT_ATR_NODISCARD size_t fillSrtHandshake_HSRSP(uint32_t* srtdata, size_t srtlen, int hs_version);
|
||||
SRT_ATR_NODISCARD size_t fillSrtHandshake(uint32_t* srtdata, size_t srtlen, int msgtype, int hs_version);
|
||||
|
||||
SRT_ATR_NODISCARD bool createSrtHandshake(ref_t<CPacket> reqpkt, ref_t<CHandShake> hs,
|
||||
int srths_cmd, int srtkm_cmd, const uint32_t* data, size_t datalen);
|
||||
|
||||
SRT_ATR_NODISCARD size_t prepareSrtHsMsg(int cmd, uint32_t* srtdata, size_t size);
|
||||
|
||||
SRT_ATR_NODISCARD bool processSrtMsg(const CPacket *ctrlpkt);
|
||||
SRT_ATR_NODISCARD int processSrtMsg_HSREQ(const uint32_t* srtdata, size_t len, uint32_t ts, int hsv);
|
||||
SRT_ATR_NODISCARD int processSrtMsg_HSRSP(const uint32_t* srtdata, size_t len, uint32_t ts, int hsv);
|
||||
SRT_ATR_NODISCARD bool interpretSrtHandshake(const CHandShake& hs, const CPacket& hspkt, uint32_t* out_data, size_t* out_len);
|
||||
SRT_ATR_NODISCARD bool checkApplyFilterConfig(const std::string& cs);
|
||||
|
||||
void updateAfterSrtHandshake(int srt_cmd, int hsv);
|
||||
|
||||
void updateSrtRcvSettings();
|
||||
void updateSrtSndSettings();
|
||||
|
||||
void checkNeedDrop(ref_t<bool> bCongestion);
|
||||
|
||||
/// Connect to a UDT entity listening at address "peer", which has sent "hs" request.
|
||||
/// @param peer [in] The address of the listening UDT entity.
|
||||
/// @param hs [in/out] The handshake information sent by the peer side (in), negotiated value (out).
|
||||
|
||||
void acceptAndRespond(const sockaddr* peer, CHandShake* hs, const CPacket& hspkt);
|
||||
bool runAcceptHook(CUDT* acore, const sockaddr* peer, const CHandShake* hs, const CPacket& hspkt);
|
||||
|
||||
/// Close the opened UDT entity.
|
||||
|
||||
bool close();
|
||||
|
||||
/// Request UDT to send out a data block "data" with size of "len".
|
||||
/// @param data [in] The address of the application data to be sent.
|
||||
/// @param len [in] The size of the data block.
|
||||
/// @return Actual size of data sent.
|
||||
|
||||
SRT_ATR_NODISCARD int send(const char* data, int len)
|
||||
{
|
||||
return sendmsg(data, len, -1, false, 0);
|
||||
}
|
||||
|
||||
/// Request UDT to receive data to a memory block "data" with size of "len".
|
||||
/// @param data [out] data received.
|
||||
/// @param len [in] The desired size of data to be received.
|
||||
/// @return Actual size of data received.
|
||||
|
||||
SRT_ATR_NODISCARD int recv(char* data, int len);
|
||||
|
||||
/// send a message of a memory block "data" with size of "len".
|
||||
/// @param data [out] data received.
|
||||
/// @param len [in] The desired size of data to be received.
|
||||
/// @param ttl [in] the time-to-live of the message.
|
||||
/// @param inorder [in] if the message should be delivered in order.
|
||||
/// @param srctime [in] Time when the data were ready to send.
|
||||
/// @return Actual size of data sent.
|
||||
|
||||
SRT_ATR_NODISCARD int sendmsg(const char* data, int len, int ttl, bool inorder, uint64_t srctime);
|
||||
/// Receive a message to buffer "data".
|
||||
/// @param data [out] data received.
|
||||
/// @param len [in] size of the buffer.
|
||||
/// @return Actual size of data received.
|
||||
|
||||
SRT_ATR_NODISCARD int sendmsg2(const char* data, int len, ref_t<SRT_MSGCTRL> m);
|
||||
|
||||
SRT_ATR_NODISCARD int recvmsg(char* data, int len, uint64_t& srctime);
|
||||
|
||||
SRT_ATR_NODISCARD int recvmsg2(char* data, int len, ref_t<SRT_MSGCTRL> m);
|
||||
|
||||
SRT_ATR_NODISCARD int receiveMessage(char* data, int len, ref_t<SRT_MSGCTRL> m);
|
||||
SRT_ATR_NODISCARD int receiveBuffer(char* data, int len);
|
||||
|
||||
/// Request UDT to send out a file described as "fd", starting from "offset", with size of "size".
|
||||
/// @param ifs [in] The input file stream.
|
||||
/// @param offset [in, out] From where to read and send data; output is the new offset when the call returns.
|
||||
/// @param size [in] How many data to be sent.
|
||||
/// @param block [in] size of block per read from disk
|
||||
/// @return Actual size of data sent.
|
||||
|
||||
SRT_ATR_NODISCARD int64_t sendfile(std::fstream& ifs, int64_t& offset, int64_t size, int block = 366000);
|
||||
|
||||
/// Request UDT to receive data into a file described as "fd", starting from "offset", with expected size of "size".
|
||||
/// @param ofs [out] The output file stream.
|
||||
/// @param offset [in, out] From where to write data; output is the new offset when the call returns.
|
||||
/// @param size [in] How many data to be received.
|
||||
/// @param block [in] size of block per write to disk
|
||||
/// @return Actual size of data received.
|
||||
|
||||
SRT_ATR_NODISCARD int64_t recvfile(std::fstream& ofs, int64_t& offset, int64_t size, int block = 7320000);
|
||||
|
||||
/// Configure UDT options.
|
||||
/// @param optName [in] The enum name of a UDT option.
|
||||
/// @param optval [in] The value to be set.
|
||||
/// @param optlen [in] size of "optval".
|
||||
|
||||
void setOpt(SRT_SOCKOPT optName, const void* optval, int optlen);
|
||||
|
||||
/// Read UDT options.
|
||||
/// @param optName [in] The enum name of a UDT option.
|
||||
/// @param optval [in] The value to be returned.
|
||||
/// @param optlen [out] size of "optval".
|
||||
|
||||
void getOpt(SRT_SOCKOPT optName, void* optval, int& optlen);
|
||||
|
||||
/// read the performance data with bytes counters since bstats()
|
||||
///
|
||||
/// @param perf [in, out] pointer to a CPerfMon structure to record the performance data.
|
||||
/// @param clear [in] flag to decide if the local performance trace should be cleared.
|
||||
/// @param instantaneous [in] flag to request instantaneous data
|
||||
/// instead of moving averages.
|
||||
void bstats(CBytePerfMon* perf, bool clear = true, bool instantaneous = false);
|
||||
|
||||
/// Mark sequence contained in the given packet as not lost. This
|
||||
/// removes the loss record from both current receiver loss list and
|
||||
/// the receiver fresh loss list.
|
||||
void unlose(const CPacket& oldpacket);
|
||||
void dropFromLossLists(int32_t from, int32_t to);
|
||||
|
||||
void considerLegacySrtHandshake(uint64_t timebase);
|
||||
void checkSndTimers(Whether2RegenKm regen = DONT_REGEN_KM);
|
||||
void handshakeDone()
|
||||
{
|
||||
m_iSndHsRetryCnt = 0;
|
||||
}
|
||||
|
||||
int64_t withOverhead(int64_t basebw)
|
||||
{
|
||||
return (basebw * (100 + m_iOverheadBW))/100;
|
||||
}
|
||||
|
||||
static double Bps2Mbps(int64_t basebw)
|
||||
{
|
||||
return double(basebw) * 8.0/1000000.0;
|
||||
}
|
||||
|
||||
bool stillConnected()
|
||||
{
|
||||
// Still connected is when:
|
||||
// - no "broken" condition appeared (security, protocol error, response timeout)
|
||||
return !m_bBroken
|
||||
// - still connected (no one called srt_close())
|
||||
&& m_bConnected
|
||||
// - isn't currently closing (srt_close() called, response timeout, shutdown)
|
||||
&& !m_bClosing;
|
||||
}
|
||||
|
||||
int sndSpaceLeft()
|
||||
{
|
||||
return sndBuffersLeft() * m_iMaxSRTPayloadSize;
|
||||
}
|
||||
|
||||
int sndBuffersLeft()
|
||||
{
|
||||
return m_iSndBufSize - m_pSndBuffer->getCurrBufSize();
|
||||
}
|
||||
|
||||
|
||||
// TSBPD thread main function.
|
||||
static void* tsbpd(void* param);
|
||||
|
||||
static CUDTUnited s_UDTUnited; // UDT global management base
|
||||
|
||||
private: // Identification
|
||||
SRTSOCKET m_SocketID; // UDT socket number
|
||||
|
||||
// XXX Deprecated field. In any place where it's used, UDT_DGRAM is
|
||||
// the only allowed value. The functionality of distinguishing the transmission
|
||||
// method is now in m_CongCtl.
|
||||
UDTSockType m_iSockType; // Type of the UDT connection (SOCK_STREAM or SOCK_DGRAM)
|
||||
SRTSOCKET m_PeerID; // peer id, for multiplexer
|
||||
|
||||
int m_iMaxSRTPayloadSize; // Maximum/regular payload size, in bytes
|
||||
size_t m_zOPT_ExpPayloadSize; // Expected average payload size (user option)
|
||||
|
||||
// Options
|
||||
int m_iMSS; // Maximum Segment Size, in bytes
|
||||
bool m_bSynSending; // Sending syncronization mode
|
||||
bool m_bSynRecving; // Receiving syncronization mode
|
||||
int m_iFlightFlagSize; // Maximum number of packets in flight from the peer side
|
||||
int m_iSndBufSize; // Maximum UDT sender buffer size
|
||||
int m_iRcvBufSize; // Maximum UDT receiver buffer size
|
||||
linger m_Linger; // Linger information on close
|
||||
int m_iUDPSndBufSize; // UDP sending buffer size
|
||||
int m_iUDPRcvBufSize; // UDP receiving buffer size
|
||||
int m_iIPversion; // IP version
|
||||
bool m_bRendezvous; // Rendezvous connection mode
|
||||
#ifdef SRT_ENABLE_CONNTIMEO
|
||||
int m_iConnTimeOut; // connect timeout in milliseconds
|
||||
#endif
|
||||
int m_iSndTimeOut; // sending timeout in milliseconds
|
||||
int m_iRcvTimeOut; // receiving timeout in milliseconds
|
||||
bool m_bReuseAddr; // reuse an exiting port or not, for UDP multiplexer
|
||||
int64_t m_llMaxBW; // maximum data transfer rate (threshold)
|
||||
#ifdef SRT_ENABLE_IPOPTS
|
||||
int m_iIpTTL;
|
||||
int m_iIpToS;
|
||||
#endif
|
||||
// These fields keep the options for encryption
|
||||
// (SRTO_PASSPHRASE, SRTO_PBKEYLEN). Crypto object is
|
||||
// created later and takes values from these.
|
||||
HaiCrypt_Secret m_CryptoSecret;
|
||||
int m_iSndCryptoKeyLen;
|
||||
|
||||
// XXX Consider removing. The m_bDataSender stays here
|
||||
// in order to maintain the HS side selection in HSv4.
|
||||
bool m_bDataSender;
|
||||
|
||||
// HSv4 (legacy handshake) support)
|
||||
uint64_t m_ullSndHsLastTime_us; //Last SRT handshake request time
|
||||
int m_iSndHsRetryCnt; //SRT handshake retries left
|
||||
|
||||
bool m_bMessageAPI;
|
||||
bool m_bOPT_TsbPd; // Whether AGENT will do TSBPD Rx (whether peer does, is not agent's problem)
|
||||
int m_iOPT_TsbPdDelay; // Agent's Rx latency
|
||||
int m_iOPT_PeerTsbPdDelay; // Peer's Rx latency for the traffic made by Agent's Tx.
|
||||
bool m_bOPT_TLPktDrop; // Whether Agent WILL DO TLPKTDROP on Rx.
|
||||
int m_iOPT_SndDropDelay; // Extra delay when deciding to snd-drop for TLPKTDROP, -1 to off
|
||||
bool m_bOPT_StrictEncryption; // Off by default. When on, any connection other than nopw-nopw & pw1-pw1 is rejected.
|
||||
std::string m_sStreamName;
|
||||
int m_iOPT_PeerIdleTimeout; // Timeout for hearing anything from the peer.
|
||||
|
||||
int m_iTsbPdDelay_ms; // Rx delay to absorb burst in milliseconds
|
||||
int m_iPeerTsbPdDelay_ms; // Tx delay that the peer uses to absorb burst in milliseconds
|
||||
bool m_bTLPktDrop; // Enable Too-late Packet Drop
|
||||
int64_t m_llInputBW; // Input stream rate (bytes/sec)
|
||||
int m_iOverheadBW; // Percent above input stream rate (applies if m_llMaxBW == 0)
|
||||
bool m_bRcvNakReport; // Enable Receiver Periodic NAK Reports
|
||||
int m_iIpV6Only; // IPV6_V6ONLY option (-1 if not set)
|
||||
private:
|
||||
UniquePtr<CCryptoControl> m_pCryptoControl; // congestion control SRT class (small data extension)
|
||||
CCache<CInfoBlock>* m_pCache; // network information cache
|
||||
|
||||
// Congestion control
|
||||
std::vector<EventSlot> m_Slots[TEV__SIZE];
|
||||
SrtCongestion m_CongCtl;
|
||||
|
||||
// Packet filtering
|
||||
PacketFilter m_PacketFilter;
|
||||
std::string m_OPT_PktFilterConfigString;
|
||||
SRT_ARQLevel m_PktFilterRexmitLevel;
|
||||
std::string m_sPeerPktFilterConfigString;
|
||||
|
||||
// Attached tool function
|
||||
void EmitSignal(ETransmissionEvent tev, EventVariant var);
|
||||
|
||||
// Internal state
|
||||
volatile bool m_bListening; // If the UDT entit is listening to connection
|
||||
volatile bool m_bConnecting; // The short phase when connect() is called but not yet completed
|
||||
volatile bool m_bConnected; // Whether the connection is on or off
|
||||
volatile bool m_bClosing; // If the UDT entity is closing
|
||||
volatile bool m_bShutdown; // If the peer side has shutdown the connection
|
||||
volatile bool m_bBroken; // If the connection has been broken
|
||||
volatile bool m_bPeerHealth; // If the peer status is normal
|
||||
volatile SRT_REJECT_REASON m_RejectReason;
|
||||
bool m_bOpened; // If the UDT entity has been opened
|
||||
int m_iBrokenCounter; // a counter (number of GC checks) to let the GC tag this socket as disconnected
|
||||
|
||||
int m_iEXPCount; // Expiration counter
|
||||
int m_iBandwidth; // Estimated bandwidth, number of packets per second
|
||||
int m_iRTT; // RTT, in microseconds
|
||||
int m_iRTTVar; // RTT variance
|
||||
int m_iDeliveryRate; // Packet arrival rate at the receiver side
|
||||
int m_iByteDeliveryRate; // Byte arrival rate at the receiver side
|
||||
|
||||
uint64_t m_ullLingerExpiration; // Linger expiration time (for GC to close a socket with data in sending buffer)
|
||||
|
||||
CHandShake m_ConnReq; // connection request
|
||||
CHandShake m_ConnRes; // connection response
|
||||
CHandShake::RendezvousState m_RdvState; // HSv5 rendezvous state
|
||||
HandshakeSide m_SrtHsSide; // HSv5 rendezvous handshake side resolved from cookie contest (DRAW if not yet resolved)
|
||||
int64_t m_llLastReqTime; // last time when a connection request is sent
|
||||
|
||||
private: // Sending related data
|
||||
CSndBuffer* m_pSndBuffer; // Sender buffer
|
||||
CSndLossList* m_pSndLossList; // Sender loss list
|
||||
CPktTimeWindow<16, 16> m_SndTimeWindow; // Packet sending time window
|
||||
|
||||
volatile uint64_t m_ullInterval_tk; // Inter-packet time, in CPU clock cycles
|
||||
uint64_t m_ullTimeDiff_tk; // aggregate difference in inter-packet time
|
||||
|
||||
volatile int m_iFlowWindowSize; // Flow control window size
|
||||
volatile double m_dCongestionWindow; // congestion window size
|
||||
|
||||
volatile int32_t m_iSndLastFullAck; // Last full ACK received
|
||||
volatile int32_t m_iSndLastAck; // Last ACK received
|
||||
volatile int32_t m_iSndLastDataAck; // The real last ACK that updates the sender buffer and loss list
|
||||
volatile int32_t m_iSndCurrSeqNo; // The largest sequence number that has been sent
|
||||
int32_t m_iLastDecSeq; // Sequence number sent last decrease occurs
|
||||
int32_t m_iSndLastAck2; // Last ACK2 sent back
|
||||
uint64_t m_ullSndLastAck2Time; // The time when last ACK2 was sent back
|
||||
int32_t m_iISN; // Initial Sequence Number
|
||||
bool m_bPeerTsbPd; // Peer accept TimeStamp-Based Rx mode
|
||||
bool m_bPeerTLPktDrop; // Enable sender late packet dropping
|
||||
bool m_bPeerNakReport; // Sender's peer (receiver) issues Periodic NAK Reports
|
||||
bool m_bPeerRexmitFlag; // Receiver supports rexmit flag in payload packets
|
||||
int32_t m_iReXmitCount; // Re-Transmit Count since last ACK
|
||||
|
||||
private: // Receiving related data
|
||||
CRcvBuffer* m_pRcvBuffer; //< Receiver buffer
|
||||
CRcvLossList* m_pRcvLossList; //< Receiver loss list
|
||||
std::deque<CRcvFreshLoss> m_FreshLoss; //< Lost sequence already added to m_pRcvLossList, but not yet sent UMSG_LOSSREPORT for.
|
||||
int m_iReorderTolerance; //< Current value of dynamic reorder tolerance
|
||||
int m_iMaxReorderTolerance; //< Maximum allowed value for dynamic reorder tolerance
|
||||
int m_iConsecEarlyDelivery; //< Increases with every OOO packet that came <TTL-2 time, resets with every increased reorder tolerance
|
||||
int m_iConsecOrderedDelivery; //< Increases with every packet coming in order or retransmitted, resets with every out-of-order packet
|
||||
|
||||
CACKWindow<1024> m_ACKWindow; //< ACK history window
|
||||
CPktTimeWindow<16, 64> m_RcvTimeWindow; //< Packet arrival time window
|
||||
|
||||
int32_t m_iRcvLastAck; //< Last sent ACK
|
||||
#ifdef ENABLE_LOGGING
|
||||
int32_t m_iDebugPrevLastAck;
|
||||
#endif
|
||||
int32_t m_iRcvLastSkipAck; // Last dropped sequence ACK
|
||||
uint64_t m_ullLastAckTime_tk; // Timestamp of last ACK
|
||||
int32_t m_iRcvLastAckAck; // Last sent ACK that has been acknowledged
|
||||
int32_t m_iAckSeqNo; // Last ACK sequence number
|
||||
int32_t m_iRcvCurrSeqNo; // Largest received sequence number
|
||||
int32_t m_iRcvCurrPhySeqNo; // Same as m_iRcvCurrSeqNo, but physical only (disregarding a filter)
|
||||
|
||||
uint64_t m_ullLastWarningTime; // Last time that a warning message is sent
|
||||
|
||||
int32_t m_iPeerISN; // Initial Sequence Number of the peer side
|
||||
uint64_t m_ullRcvPeerStartTime;
|
||||
|
||||
uint32_t m_lSrtVersion;
|
||||
uint32_t m_lMinimumPeerSrtVersion;
|
||||
uint32_t m_lPeerSrtVersion;
|
||||
uint32_t m_lPeerSrtFlags;
|
||||
|
||||
bool m_bTsbPd; // Peer sends TimeStamp-Based Packet Delivery Packets
|
||||
pthread_t m_RcvTsbPdThread; // Rcv TsbPD Thread handle
|
||||
pthread_cond_t m_RcvTsbPdCond;
|
||||
bool m_bTsbPdAckWakeup; // Signal TsbPd thread on Ack sent
|
||||
|
||||
CallbackHolder<srt_listen_callback_fn> m_cbAcceptHook;
|
||||
|
||||
// FORWARDER
|
||||
public:
|
||||
static int installAcceptHook(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq)
|
||||
{
|
||||
return s_UDTUnited.installAcceptHook(lsn, hook, opaq);
|
||||
}
|
||||
private:
|
||||
void installAcceptHook(srt_listen_callback_fn* hook, void* opaq)
|
||||
{
|
||||
m_cbAcceptHook.set(opaq, hook);
|
||||
}
|
||||
|
||||
|
||||
private: // synchronization: mutexes and conditions
|
||||
pthread_mutex_t m_ConnectionLock; // used to synchronize connection operation
|
||||
|
||||
pthread_cond_t m_SendBlockCond; // used to block "send" call
|
||||
pthread_mutex_t m_SendBlockLock; // lock associated to m_SendBlockCond
|
||||
|
||||
pthread_mutex_t m_RcvBufferLock; // Protects the state of the m_pRcvBuffer
|
||||
|
||||
// Protects access to m_iSndCurrSeqNo, m_iSndLastAck
|
||||
pthread_mutex_t m_RecvAckLock; // Protects the state changes while processing incomming ACK (UDT_EPOLL_OUT)
|
||||
|
||||
|
||||
pthread_cond_t m_RecvDataCond; // used to block "recv" when there is no data
|
||||
pthread_mutex_t m_RecvDataLock; // lock associated to m_RecvDataCond
|
||||
|
||||
pthread_mutex_t m_SendLock; // used to synchronize "send" call
|
||||
pthread_mutex_t m_RecvLock; // used to synchronize "recv" call
|
||||
|
||||
pthread_mutex_t m_RcvLossLock; // Protects the receiver loss list (access: CRcvQueue::worker, CUDT::tsbpd)
|
||||
|
||||
pthread_mutex_t m_StatsLock; // used to synchronize access to trace statistics
|
||||
|
||||
void initSynch();
|
||||
void destroySynch();
|
||||
void releaseSynch();
|
||||
|
||||
private: // Common connection Congestion Control setup
|
||||
SRT_REJECT_REASON setupCC();
|
||||
void updateCC(ETransmissionEvent, EventVariant arg);
|
||||
bool createCrypter(HandshakeSide side, bool bidi);
|
||||
|
||||
private: // Generation and processing of packets
|
||||
void sendCtrl(UDTMessageType pkttype, const void* lparam = NULL, void* rparam = NULL, int size = 0);
|
||||
|
||||
void processCtrl(CPacket& ctrlpkt);
|
||||
void sendLossReport(const std::vector< std::pair<int32_t, int32_t> >& losslist);
|
||||
void processCtrlAck(const CPacket& ctrlpkt, const uint64_t currtime_tk);
|
||||
|
||||
///
|
||||
/// @param ackdata_seqno sequence number of a data packet being acknowledged
|
||||
void updateSndLossListOnACK(int32_t ackdata_seqno);
|
||||
|
||||
/// Pack a packet from a list of lost packets.
|
||||
///
|
||||
/// @param packet [in, out] a packet structure to fill
|
||||
/// @param origintime [in, out] origin timestamp of the packet
|
||||
///
|
||||
/// @return payload size on success, <=0 on failure
|
||||
int packLostData(CPacket& packet, uint64_t& origintime);
|
||||
|
||||
int packData(CPacket& packet, uint64_t& ts);
|
||||
int processData(CUnit* unit);
|
||||
void processClose();
|
||||
SRT_REJECT_REASON processConnectRequest(const sockaddr* addr, CPacket& packet);
|
||||
static void addLossRecord(std::vector<int32_t>& lossrecord, int32_t lo, int32_t hi);
|
||||
int32_t bake(const sockaddr* addr, int32_t previous_cookie = 0, int correction = 0);
|
||||
|
||||
private: // Trace
|
||||
|
||||
struct CoreStats
|
||||
{
|
||||
uint64_t startTime; // timestamp when the UDT entity is started
|
||||
int64_t sentTotal; // total number of sent data packets, including retransmissions
|
||||
int64_t recvTotal; // total number of received packets
|
||||
int sndLossTotal; // total number of lost packets (sender side)
|
||||
int rcvLossTotal; // total number of lost packets (receiver side)
|
||||
int retransTotal; // total number of retransmitted packets
|
||||
int sentACKTotal; // total number of sent ACK packets
|
||||
int recvACKTotal; // total number of received ACK packets
|
||||
int sentNAKTotal; // total number of sent NAK packets
|
||||
int recvNAKTotal; // total number of received NAK packets
|
||||
int sndDropTotal;
|
||||
int rcvDropTotal;
|
||||
uint64_t bytesSentTotal; // total number of bytes sent, including retransmissions
|
||||
uint64_t bytesRecvTotal; // total number of received bytes
|
||||
uint64_t rcvBytesLossTotal; // total number of loss bytes (estimate)
|
||||
uint64_t bytesRetransTotal; // total number of retransmitted bytes
|
||||
uint64_t sndBytesDropTotal;
|
||||
uint64_t rcvBytesDropTotal;
|
||||
int m_rcvUndecryptTotal;
|
||||
uint64_t m_rcvBytesUndecryptTotal;
|
||||
|
||||
int sndFilterExtraTotal;
|
||||
int rcvFilterExtraTotal;
|
||||
int rcvFilterSupplyTotal;
|
||||
int rcvFilterLossTotal;
|
||||
|
||||
int64_t m_sndDurationTotal; // total real time for sending
|
||||
|
||||
uint64_t lastSampleTime; // last performance sample time
|
||||
int64_t traceSent; // number of packets sent in the last trace interval
|
||||
int64_t traceRecv; // number of packets received in the last trace interval
|
||||
int traceSndLoss; // number of lost packets in the last trace interval (sender side)
|
||||
int traceRcvLoss; // number of lost packets in the last trace interval (receiver side)
|
||||
int traceRetrans; // number of retransmitted packets in the last trace interval
|
||||
int sentACK; // number of ACKs sent in the last trace interval
|
||||
int recvACK; // number of ACKs received in the last trace interval
|
||||
int sentNAK; // number of NAKs sent in the last trace interval
|
||||
int recvNAK; // number of NAKs received in the last trace interval
|
||||
int traceSndDrop;
|
||||
int traceRcvDrop;
|
||||
int traceRcvRetrans;
|
||||
int traceReorderDistance;
|
||||
double traceBelatedTime;
|
||||
int64_t traceRcvBelated;
|
||||
uint64_t traceBytesSent; // number of bytes sent in the last trace interval
|
||||
uint64_t traceBytesRecv; // number of bytes sent in the last trace interval
|
||||
uint64_t traceRcvBytesLoss; // number of bytes bytes lost in the last trace interval (estimate)
|
||||
uint64_t traceBytesRetrans; // number of bytes retransmitted in the last trace interval
|
||||
uint64_t traceSndBytesDrop;
|
||||
uint64_t traceRcvBytesDrop;
|
||||
int traceRcvUndecrypt;
|
||||
uint64_t traceRcvBytesUndecrypt;
|
||||
|
||||
int sndFilterExtra;
|
||||
int rcvFilterExtra;
|
||||
int rcvFilterSupply;
|
||||
int rcvFilterLoss;
|
||||
|
||||
int64_t sndDuration; // real time for sending
|
||||
int64_t sndDurationCounter; // timers to record the sending duration
|
||||
} m_stats;
|
||||
|
||||
public:
|
||||
|
||||
static const int SELF_CLOCK_INTERVAL = 64; // ACK interval for self-clocking
|
||||
static const int SEND_LITE_ACK = sizeof(int32_t); // special size for ack containing only ack seq
|
||||
static const int PACKETPAIR_MASK = 0xF;
|
||||
|
||||
static const size_t MAX_SID_LENGTH = 512;
|
||||
|
||||
private: // Timers
|
||||
uint64_t m_ullCPUFrequency; // CPU clock frequency, used for Timer, ticks per microsecond
|
||||
uint64_t m_ullNextACKTime_tk; // Next ACK time, in CPU clock cycles, same below
|
||||
uint64_t m_ullNextNAKTime_tk; // Next NAK time
|
||||
|
||||
volatile uint64_t m_ullACKInt_tk; // ACK interval
|
||||
volatile uint64_t m_ullNAKInt_tk; // NAK interval
|
||||
volatile uint64_t m_ullLastRspTime_tk; // time stamp of last response from the peer
|
||||
volatile uint64_t m_ullLastRspAckTime_tk; // time stamp of last ACK from the peer, protect with m_RecvAckLock
|
||||
volatile uint64_t m_ullLastSndTime_tk; // time stamp of last data/ctrl sent (in system ticks)
|
||||
uint64_t m_ullMinNakInt_tk; // NAK timeout lower bound; too small value can cause unnecessary retransmission
|
||||
uint64_t m_ullMinExpInt_tk; // timeout lower bound threshold: too small timeout can cause problem
|
||||
|
||||
int m_iPktCount; // packet counter for ACK
|
||||
int m_iLightACKCount; // light ACK counter
|
||||
|
||||
uint64_t m_ullTargetTime_tk; // scheduled time of next packet sending
|
||||
|
||||
void checkTimers();
|
||||
void checkACKTimer (uint64_t currtime_tk);
|
||||
void checkNAKTimer(uint64_t currtime_tk);
|
||||
bool checkExpTimer (uint64_t currtime_tk); // returns true if the connection is expired
|
||||
void checkRexmitTimer(uint64_t currtime_tk);
|
||||
|
||||
public: // For the use of CCryptoControl
|
||||
// HaiCrypt configuration
|
||||
unsigned int m_uKmRefreshRatePkt;
|
||||
unsigned int m_uKmPreAnnouncePkt;
|
||||
|
||||
|
||||
private: // for UDP multiplexer
|
||||
CSndQueue* m_pSndQueue; // packet sending queue
|
||||
CRcvQueue* m_pRcvQueue; // packet receiving queue
|
||||
sockaddr* m_pPeerAddr; // peer address
|
||||
uint32_t m_piSelfIP[4]; // local UDP IP address
|
||||
CSNode* m_pSNode; // node information for UDT list used in snd queue
|
||||
CRNode* m_pRNode; // node information for UDT list used in rcv queue
|
||||
|
||||
public: // For SrtCongestion
|
||||
const CSndQueue* sndQueue() { return m_pSndQueue; }
|
||||
const CRcvQueue* rcvQueue() { return m_pRcvQueue; }
|
||||
|
||||
private: // for epoll
|
||||
std::set<int> m_sPollID; // set of epoll ID to trigger
|
||||
void addEPoll(const int eid);
|
||||
void removeEPoll(const int eid);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
887
trunk/3rdparty/srt-1-fit/srtcore/crypto.cpp
vendored
Normal file
887
trunk/3rdparty/srt-1-fit/srtcore/crypto.cpp
vendored
Normal file
|
@ -0,0 +1,887 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iterator>
|
||||
|
||||
#include "udt.h"
|
||||
#include "utilities.h"
|
||||
#include <haicrypt.h>
|
||||
#include "crypto.h"
|
||||
#include "logging.h"
|
||||
#include "core.h"
|
||||
|
||||
using namespace srt_logging;
|
||||
|
||||
#define SRT_MAX_KMRETRY 10
|
||||
|
||||
//#define SRT_CMD_KMREQ 3 /* HaiCryptTP SRT Keying Material */
|
||||
//#define SRT_CMD_KMRSP 4 /* HaiCryptTP SRT Keying Material ACK */
|
||||
#define SRT_CMD_KMREQ_SZ HCRYPT_MSG_KM_MAX_SZ /* */
|
||||
#if SRT_CMD_KMREQ_SZ > SRT_CMD_MAXSZ
|
||||
#error SRT_CMD_MAXSZ too small
|
||||
#endif
|
||||
/* Key Material Request (Network Order)
|
||||
See HaiCryptTP SRT (hcrypt_xpt_srt.c)
|
||||
*/
|
||||
|
||||
// 10* HAICRYPT_DEF_KM_PRE_ANNOUNCE
|
||||
const int SRT_CRYPT_KM_PRE_ANNOUNCE = 0x10000;
|
||||
|
||||
#if ENABLE_LOGGING
|
||||
std::string KmStateStr(SRT_KM_STATE state)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
#define TAKE(val) case SRT_KM_S_##val : return #val
|
||||
TAKE(UNSECURED);
|
||||
TAKE(SECURED);
|
||||
TAKE(SECURING);
|
||||
TAKE(NOSECRET);
|
||||
TAKE(BADSECRET);
|
||||
#undef TAKE
|
||||
default:
|
||||
{
|
||||
char buf[256];
|
||||
sprintf(buf, "??? (%d)", state);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string CCryptoControl::FormatKmMessage(std::string hdr, int cmd, size_t srtlen)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << hdr << ": cmd=" << cmd << "(" << (cmd == SRT_CMD_KMREQ ? "KMREQ":"KMRSP") <<") len="
|
||||
<< size_t(srtlen*sizeof(int32_t)) << " KmState: SND="
|
||||
<< KmStateStr(m_SndKmState)
|
||||
<< " RCV=" << KmStateStr(m_RcvKmState);
|
||||
return os.str();
|
||||
}
|
||||
#endif
|
||||
|
||||
void CCryptoControl::updateKmState(int cmd, size_t srtlen SRT_ATR_UNUSED)
|
||||
{
|
||||
if (cmd == SRT_CMD_KMREQ)
|
||||
{
|
||||
if ( SRT_KM_S_UNSECURED == m_SndKmState)
|
||||
{
|
||||
m_SndKmState = SRT_KM_S_SECURING;
|
||||
}
|
||||
LOGP(mglog.Note, FormatKmMessage("sendSrtMsg", cmd, srtlen));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGP(mglog.Note, FormatKmMessage("sendSrtMsg", cmd, srtlen));
|
||||
}
|
||||
}
|
||||
|
||||
void CCryptoControl::createFakeSndContext()
|
||||
{
|
||||
if (!m_iSndKmKeyLen)
|
||||
m_iSndKmKeyLen = 16;
|
||||
|
||||
if (!createCryptoCtx(Ref(m_hSndCrypto), m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX))
|
||||
{
|
||||
HLOGC(mglog.Debug, log << "Error: Can't create fake crypto context for sending - sending will return ERROR!");
|
||||
m_hSndCrypto = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int CCryptoControl::processSrtMsg_KMREQ(
|
||||
const uint32_t* srtdata SRT_ATR_UNUSED,
|
||||
size_t bytelen SRT_ATR_UNUSED,
|
||||
uint32_t* srtdata_out, ref_t<size_t> r_srtlen, int hsv SRT_ATR_UNUSED)
|
||||
{
|
||||
size_t& srtlen = *r_srtlen;
|
||||
//Receiver
|
||||
/* All 32-bit msg fields swapped on reception
|
||||
* But HaiCrypt expect network order message
|
||||
* Re-swap to cancel it.
|
||||
*/
|
||||
#ifdef SRT_ENABLE_ENCRYPTION
|
||||
srtlen = bytelen/sizeof(srtdata[SRT_KMR_KMSTATE]);
|
||||
HtoNLA(srtdata_out, srtdata, srtlen);
|
||||
unsigned char* kmdata = reinterpret_cast<unsigned char*>(srtdata_out);
|
||||
|
||||
std::vector<unsigned char> kmcopy(kmdata, kmdata + bytelen);
|
||||
|
||||
// The side that has received KMREQ is always an HSD_RESPONDER, regardless of
|
||||
// what has called this function. The HSv5 handshake only enforces bidirectional
|
||||
// connection.
|
||||
|
||||
bool bidirectional = hsv > CUDT::HS_VERSION_UDT4;
|
||||
|
||||
// Local macro to return rejection appropriately.
|
||||
// CHANGED. The first version made HSv5 reject the connection.
|
||||
// This isn't well handled by applications, so the connection is
|
||||
// still established, but unable to handle any transport.
|
||||
//#define KMREQ_RESULT_REJECTION() if (bidirectional) { return SRT_CMD_NONE; } else { srtlen = 1; goto HSv4_ErrorReport; }
|
||||
#define KMREQ_RESULT_REJECTION() { srtlen = 1; goto HSv4_ErrorReport; }
|
||||
|
||||
int rc = HAICRYPT_OK; // needed before 'goto' run from KMREQ_RESULT_REJECTION macro
|
||||
bool SRT_ATR_UNUSED wasb4 = false;
|
||||
size_t sek_len = 0;
|
||||
|
||||
// What we have to do:
|
||||
// If encryption is on (we know that by having m_KmSecret nonempty), create
|
||||
// the crypto context (if bidirectional, create for both sending and receiving).
|
||||
// Both crypto contexts should be set with the same length of the key.
|
||||
// The problem with interpretinting this should be reported as SRT_CMD_NONE,
|
||||
// should be appropriately handled by the caller, as it expects that this
|
||||
// function normally return SRT_CMD_KMRSP.
|
||||
if ( bytelen <= HCRYPT_MSG_KM_OFS_SALT ) //Sanity on message
|
||||
{
|
||||
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: size of the KM (" << bytelen << ") is too small, must be >" << HCRYPT_MSG_KM_OFS_SALT);
|
||||
m_RcvKmState = SRT_KM_S_BADSECRET;
|
||||
KMREQ_RESULT_REJECTION();
|
||||
}
|
||||
|
||||
HLOGC(mglog.Debug, log << "KMREQ: getting SEK and creating receiver crypto");
|
||||
sek_len = hcryptMsg_KM_GetSekLen(kmdata);
|
||||
if ( sek_len == 0 )
|
||||
{
|
||||
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: Received SEK is empty - REJECTING!");
|
||||
m_RcvKmState = SRT_KM_S_BADSECRET;
|
||||
KMREQ_RESULT_REJECTION();
|
||||
}
|
||||
|
||||
// Write the key length
|
||||
m_iRcvKmKeyLen = sek_len;
|
||||
// Overwrite the key length anyway - it doesn't make sense to somehow
|
||||
// keep the original setting because it will only make KMX impossible.
|
||||
#if ENABLE_HEAVY_LOGGING
|
||||
if (m_iSndKmKeyLen != m_iRcvKmKeyLen)
|
||||
{
|
||||
LOGC(mglog.Debug, log << "processSrtMsg_KMREQ: Agent's PBKEYLEN=" << m_iSndKmKeyLen
|
||||
<< " overwritten by Peer's PBKEYLEN=" << m_iRcvKmKeyLen);
|
||||
}
|
||||
#endif
|
||||
m_iSndKmKeyLen = m_iRcvKmKeyLen;
|
||||
|
||||
// This is checked only now so that the SRTO_PBKEYLEN return always the correct value,
|
||||
// even if encryption is not possible because Agent didn't set a password, or supplied
|
||||
// a wrong password.
|
||||
if (m_KmSecret.len == 0) //We have a shared secret <==> encryption is on
|
||||
{
|
||||
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: Agent does not declare encryption - won't decrypt incoming packets!");
|
||||
m_RcvKmState = SRT_KM_S_NOSECRET;
|
||||
KMREQ_RESULT_REJECTION();
|
||||
}
|
||||
wasb4 = m_hRcvCrypto;
|
||||
|
||||
if (!createCryptoCtx(Ref(m_hRcvCrypto), m_iRcvKmKeyLen, HAICRYPT_CRYPTO_DIR_RX))
|
||||
{
|
||||
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: Can't create RCV CRYPTO CTX - must reject...");
|
||||
m_RcvKmState = SRT_KM_S_NOSECRET;
|
||||
KMREQ_RESULT_REJECTION();
|
||||
}
|
||||
|
||||
if (!wasb4)
|
||||
{
|
||||
HLOGC(mglog.Debug, log << "processSrtMsg_KMREQ: created RX ENC with KeyLen=" << m_iRcvKmKeyLen);
|
||||
}
|
||||
// We have both sides set with password, so both are pending for security
|
||||
m_RcvKmState = SRT_KM_S_SECURING;
|
||||
// m_SndKmState is set to SECURING or UNSECURED in init(),
|
||||
// or it might have been set to SECURED, NOSECRET or BADSECRET in the previous
|
||||
// handshake iteration (handshakes may be sent multiple times for the same connection).
|
||||
|
||||
rc = HaiCrypt_Rx_Process(m_hRcvCrypto, kmdata, bytelen, NULL, NULL, 0);
|
||||
switch(rc >= 0 ? HAICRYPT_OK : rc)
|
||||
{
|
||||
case HAICRYPT_OK:
|
||||
m_RcvKmState = SRT_KM_S_SECURED;
|
||||
HLOGC(mglog.Debug, log << "KMREQ/rcv: (snd) Rx process successful - SECURED.");
|
||||
//Send back the whole message to confirm
|
||||
break;
|
||||
case HAICRYPT_ERROR_WRONG_SECRET: //Unmatched shared secret to decrypt wrapped key
|
||||
m_RcvKmState = m_SndKmState = SRT_KM_S_BADSECRET;
|
||||
//Send status KMRSP message to tel error
|
||||
srtlen = 1;
|
||||
LOGC(mglog.Error, log << "KMREQ/rcv: (snd) Rx process failure - BADSECRET");
|
||||
break;
|
||||
case HAICRYPT_ERROR: //Other errors
|
||||
default:
|
||||
m_RcvKmState = m_SndKmState = SRT_KM_S_NOSECRET;
|
||||
srtlen = 1;
|
||||
LOGC(mglog.Error, log << "KMREQ/rcv: (snd) Rx process failure (IPE) - NOSECRET");
|
||||
break;
|
||||
}
|
||||
|
||||
LOGP(mglog.Note, FormatKmMessage("processSrtMsg_KMREQ", SRT_CMD_KMREQ, bytelen));
|
||||
|
||||
// Since now, when CCryptoControl::decrypt() encounters an error, it will print it, ONCE,
|
||||
// until the next KMREQ is received as a key regeneration.
|
||||
m_bErrorReported = false;
|
||||
|
||||
|
||||
if (srtlen == 1)
|
||||
goto HSv4_ErrorReport;
|
||||
|
||||
// Configure the sender context also, if it succeeded to configure the
|
||||
// receiver context and we are using bidirectional mode.
|
||||
if ( bidirectional )
|
||||
{
|
||||
// Note: 'bidirectional' means that we want a bidirectional key update,
|
||||
// which happens only and exclusively with HSv5 handshake - not when the
|
||||
// usual key update through UMSG_EXT+SRT_CMD_KMREQ was done (which is used
|
||||
// in HSv4 versions also to initialize the first key, unlike HSv5).
|
||||
if (m_RcvKmState == SRT_KM_S_SECURED)
|
||||
{
|
||||
if (m_SndKmState == SRT_KM_S_SECURING && !m_hSndCrypto)
|
||||
{
|
||||
m_iSndKmKeyLen = m_iRcvKmKeyLen;
|
||||
if (HaiCrypt_Clone(m_hRcvCrypto, HAICRYPT_CRYPTO_DIR_TX, &m_hSndCrypto) != HAICRYPT_OK)
|
||||
{
|
||||
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: Can't create SND CRYPTO CTX - WILL NOT SEND-ENCRYPT correctly!");
|
||||
if (hasPassphrase())
|
||||
m_SndKmState = SRT_KM_S_BADSECRET;
|
||||
else
|
||||
m_SndKmState = SRT_KM_S_NOSECRET;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_SndKmState = SRT_KM_S_SECURED;
|
||||
}
|
||||
|
||||
LOGC(mglog.Note, log << FormatKmMessage("processSrtMsg_KMREQ", SRT_CMD_KMREQ, bytelen)
|
||||
<< " SndKeyLen=" << m_iSndKmKeyLen
|
||||
<< " TX CRYPTO CTX CLONED FROM RX"
|
||||
);
|
||||
|
||||
// Write the KM message into the field from which it will be next sent.
|
||||
memcpy(m_SndKmMsg[0].Msg, kmdata, bytelen);
|
||||
m_SndKmMsg[0].MsgLen = bytelen;
|
||||
m_SndKmMsg[0].iPeerRetry = 0; // Don't start sending them upon connection :)
|
||||
}
|
||||
else
|
||||
{
|
||||
HLOGC(mglog.Debug, log << "processSrtMsg_KMREQ: NOT cloning RX to TX crypto: already in "
|
||||
<< KmStateStr(m_SndKmState) << " state");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HLOGP(mglog.Debug, "processSrtMsg_KMREQ: NOT SECURED - not replaying failed security association to TX CRYPTO CTX");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HLOGC(mglog.Debug, log << "processSrtMsg_KMREQ: NOT REPLAYING the key update to TX CRYPTO CTX.");
|
||||
}
|
||||
|
||||
return SRT_CMD_KMRSP;
|
||||
|
||||
HSv4_ErrorReport:
|
||||
|
||||
if (bidirectional && hasPassphrase())
|
||||
{
|
||||
// If the Forward KMX process has failed, the reverse-KMX process was not done at all.
|
||||
// This will lead to incorrect object configuration and will fail to properly declare
|
||||
// the transmission state.
|
||||
// Create the "fake crypto" with the passphrsae you currently have.
|
||||
createFakeSndContext();
|
||||
}
|
||||
#undef KMREQ_RESULT_REJECTION
|
||||
|
||||
#else
|
||||
// It's ok that this is reported as error because this happens in a scenario,
|
||||
// when non-encryption-enabled SRT application is contacted by encryption-enabled SRT
|
||||
// application which tries to make a security association.
|
||||
LOGC(mglog.Error, log << "processSrtMsg_KMREQ: Encryption not enabled at compile time - must reject...");
|
||||
m_RcvKmState = SRT_KM_S_NOSECRET;
|
||||
#endif
|
||||
|
||||
srtlen = 1;
|
||||
|
||||
srtdata_out[SRT_KMR_KMSTATE] = m_RcvKmState;
|
||||
return SRT_CMD_KMRSP;
|
||||
}
|
||||
|
||||
int CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, int /* XXX unused? hsv*/)
|
||||
{
|
||||
/* All 32-bit msg fields (if present) swapped on reception
|
||||
* But HaiCrypt expect network order message
|
||||
* Re-swap to cancel it.
|
||||
*/
|
||||
uint32_t srtd[SRTDATA_MAXSIZE];
|
||||
size_t srtlen = len/sizeof(uint32_t);
|
||||
HtoNLA(srtd, srtdata, srtlen);
|
||||
|
||||
int retstatus = -1;
|
||||
|
||||
// Unused?
|
||||
//bool bidirectional = hsv > CUDT::HS_VERSION_UDT4;
|
||||
|
||||
// Since now, when CCryptoControl::decrypt() encounters an error, it will print it, ONCE,
|
||||
// until the next KMREQ is received as a key regeneration.
|
||||
m_bErrorReported = false;
|
||||
|
||||
if (srtlen == 1) // Error report. Set accordingly.
|
||||
{
|
||||
SRT_KM_STATE peerstate = SRT_KM_STATE(srtd[SRT_KMR_KMSTATE]); /* Bad or no passphrase */
|
||||
m_SndKmMsg[0].iPeerRetry = 0;
|
||||
m_SndKmMsg[1].iPeerRetry = 0;
|
||||
|
||||
switch (peerstate)
|
||||
{
|
||||
case SRT_KM_S_BADSECRET:
|
||||
m_SndKmState = m_RcvKmState = SRT_KM_S_BADSECRET;
|
||||
retstatus = -1;
|
||||
break;
|
||||
|
||||
// Default embraces two cases:
|
||||
// NOSECRET: this KMRSP was sent by secured Peer, but Agent supplied no password.
|
||||
// UNSECURED: this KMRSP was sent by unsecure Peer because Agent sent KMREQ.
|
||||
|
||||
case SRT_KM_S_NOSECRET:
|
||||
// This means that the peer did not set the password, while Agent did.
|
||||
m_RcvKmState = SRT_KM_S_UNSECURED;
|
||||
m_SndKmState = SRT_KM_S_NOSECRET;
|
||||
retstatus = -1;
|
||||
break;
|
||||
|
||||
case SRT_KM_S_UNSECURED:
|
||||
// This means that KMRSP was sent without KMREQ, to inform the Agent,
|
||||
// that the Peer, unlike Agent, does use password. Agent can send then,
|
||||
// but can't decrypt what Peer would send.
|
||||
m_RcvKmState = SRT_KM_S_NOSECRET;
|
||||
m_SndKmState = SRT_KM_S_UNSECURED;
|
||||
retstatus = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
LOGC(mglog.Fatal, log << "processSrtMsg_KMRSP: IPE: unknown peer error state: "
|
||||
<< KmStateStr(peerstate) << " (" << int(peerstate) << ")");
|
||||
m_RcvKmState = SRT_KM_S_NOSECRET;
|
||||
m_SndKmState = SRT_KM_S_NOSECRET;
|
||||
retstatus = -1; //This is IPE
|
||||
break;
|
||||
}
|
||||
|
||||
LOGC(mglog.Error, log << "processSrtMsg_KMRSP: received failure report. STATE: " << KmStateStr(m_RcvKmState));
|
||||
}
|
||||
else
|
||||
{
|
||||
HLOGC(mglog.Debug, log << "processSrtMsg_KMRSP: received key response len=" << len);
|
||||
// XXX INSECURE << ": [" << FormatBinaryString((uint8_t*)srtd, len) << "]";
|
||||
bool key1 = getKmMsg_acceptResponse(0, srtd, len);
|
||||
bool key2 = true;
|
||||
if ( !key1 )
|
||||
key2 = getKmMsg_acceptResponse(1, srtd, len); // <--- NOTE SEQUENCING!
|
||||
|
||||
if (key1 || key2)
|
||||
{
|
||||
m_SndKmState = m_RcvKmState = SRT_KM_S_SECURED;
|
||||
HLOGC(mglog.Debug, log << "processSrtMsg_KMRSP: KM response matches " << (key1 ? "EVEN" : "ODD") << " key");
|
||||
retstatus = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
retstatus = -1;
|
||||
LOGC(mglog.Error, log << "processSrtMsg_KMRSP: IPE??? KM response key matches no key");
|
||||
/* XXX INSECURE
|
||||
LOGC(mglog.Error, log << "processSrtMsg_KMRSP: KM response: [" << FormatBinaryString((uint8_t*)srtd, len)
|
||||
<< "] matches no key 0=[" << FormatBinaryString((uint8_t*)m_SndKmMsg[0].Msg, m_SndKmMsg[0].MsgLen)
|
||||
<< "] 1=[" << FormatBinaryString((uint8_t*)m_SndKmMsg[1].Msg, m_SndKmMsg[1].MsgLen) << "]");
|
||||
*/
|
||||
|
||||
m_SndKmState = m_RcvKmState = SRT_KM_S_BADSECRET;
|
||||
}
|
||||
HLOGC(mglog.Debug, log << "processSrtMsg_KMRSP: key[0]: len=" << m_SndKmMsg[0].MsgLen << " retry=" << m_SndKmMsg[0].iPeerRetry
|
||||
<< "; key[1]: len=" << m_SndKmMsg[1].MsgLen << " retry=" << m_SndKmMsg[1].iPeerRetry);
|
||||
}
|
||||
|
||||
LOGP(mglog.Note, FormatKmMessage("processSrtMsg_KMRSP", SRT_CMD_KMRSP, len));
|
||||
|
||||
return retstatus;
|
||||
}
|
||||
|
||||
void CCryptoControl::sendKeysToPeer(Whether2RegenKm regen SRT_ATR_UNUSED)
|
||||
{
|
||||
if ( !m_hSndCrypto || m_SndKmState == SRT_KM_S_UNSECURED)
|
||||
{
|
||||
HLOGC(mglog.Debug, log << "sendKeysToPeer: NOT sending/regenerating keys: "
|
||||
<< (m_hSndCrypto ? "CONNECTION UNSECURED" : "NO TX CRYPTO CTX created"));
|
||||
return;
|
||||
}
|
||||
#ifdef SRT_ENABLE_ENCRYPTION
|
||||
uint64_t now = 0;
|
||||
/*
|
||||
* Crypto Key Distribution to peer:
|
||||
* If...
|
||||
* - we want encryption; and
|
||||
* - we have not tried more than CSRTCC_MAXRETRY times (peer may not be SRT); and
|
||||
* - and did not get answer back from peer; and
|
||||
* - last sent Keying Material req should have been replied (RTT*1.5 elapsed);
|
||||
* then (re-)send handshake request.
|
||||
*/
|
||||
if ( ((m_SndKmMsg[0].iPeerRetry > 0) || (m_SndKmMsg[1].iPeerRetry > 0))
|
||||
&& ((m_SndKmLastTime + ((m_parent->RTT() * 3)/2)) <= (now = CTimer::getTime())))
|
||||
{
|
||||
for (int ki = 0; ki < 2; ki++)
|
||||
{
|
||||
if (m_SndKmMsg[ki].iPeerRetry > 0 && m_SndKmMsg[ki].MsgLen > 0)
|
||||
{
|
||||
m_SndKmMsg[ki].iPeerRetry--;
|
||||
HLOGC(mglog.Debug, log << "sendKeysToPeer: SENDING ki=" << ki << " len=" << m_SndKmMsg[ki].MsgLen
|
||||
<< " retry(updated)=" << m_SndKmMsg[ki].iPeerRetry);
|
||||
m_SndKmLastTime = now;
|
||||
m_parent->sendSrtMsg(SRT_CMD_KMREQ, (uint32_t *)m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen/sizeof(uint32_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (now == 0)
|
||||
{
|
||||
HLOGC(mglog.Debug, log << "sendKeysToPeer: NO KEYS RESENT, will " <<
|
||||
(regen ? "" : "NOT ") << "regenerate.");
|
||||
}
|
||||
|
||||
if (regen)
|
||||
regenCryptoKm(
|
||||
true, // send UMSG_EXT + SRT_CMD_KMREQ to the peer, if regenerated the key
|
||||
false // Do not apply the regenerated key to the to the receiver context
|
||||
); // regenerate and send
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SRT_ENABLE_ENCRYPTION
|
||||
void CCryptoControl::regenCryptoKm(bool sendit, bool bidirectional)
|
||||
{
|
||||
if (!m_hSndCrypto)
|
||||
return;
|
||||
|
||||
void *out_p[2];
|
||||
size_t out_len_p[2];
|
||||
int nbo = HaiCrypt_Tx_ManageKeys(m_hSndCrypto, out_p, out_len_p, 2);
|
||||
int sent = 0;
|
||||
|
||||
HLOGC(mglog.Debug, log << "regenCryptoKm: regenerating crypto keys nbo=" << nbo <<
|
||||
" THEN=" << (sendit ? "SEND" : "KEEP") << " DIR=" << (bidirectional ? "BOTH" : "SENDER"));
|
||||
|
||||
for (int i = 0; i < nbo && i < 2; i++)
|
||||
{
|
||||
/*
|
||||
* New connection keying material
|
||||
* or regenerated after crypto_cfg.km_refresh_rate_pkt packets .
|
||||
* Send to peer
|
||||
*/
|
||||
// XXX Need to make it clearer and less hardcoded values
|
||||
int kix = hcryptMsg_KM_GetKeyIndex((unsigned char *)(out_p[i]));
|
||||
int ki = kix & 0x1;
|
||||
if ((out_len_p[i] != m_SndKmMsg[ki].MsgLen)
|
||||
|| (0 != memcmp(out_p[i], m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen)))
|
||||
{
|
||||
|
||||
uint8_t* oldkey SRT_ATR_UNUSED = m_SndKmMsg[ki].Msg;
|
||||
HLOGC(mglog.Debug, log << "new key[" << ki << "] index=" << kix
|
||||
<< " OLD=[" << m_SndKmMsg[ki].MsgLen << "]"
|
||||
<< FormatBinaryString(m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen)
|
||||
<< " NEW=[" << out_len_p[i] << "]"
|
||||
<< FormatBinaryString((const uint8_t*)out_p[i], out_len_p[i]));
|
||||
|
||||
/* New Keying material, send to peer */
|
||||
memcpy(m_SndKmMsg[ki].Msg, out_p[i], out_len_p[i]);
|
||||
m_SndKmMsg[ki].MsgLen = out_len_p[i];
|
||||
m_SndKmMsg[ki].iPeerRetry = SRT_MAX_KMRETRY;
|
||||
|
||||
if (bidirectional && !sendit)
|
||||
{
|
||||
// "Send" this key also to myself, just to be applied to the receiver crypto,
|
||||
// exactly the same way how this key is interpreted on the peer side into its receiver crypto
|
||||
int rc = HaiCrypt_Rx_Process(m_hRcvCrypto, m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen, NULL, NULL, 0);
|
||||
if ( rc < 0 )
|
||||
{
|
||||
LOGC(mglog.Fatal, log << "regenCryptoKm: IPE: applying key generated in snd crypto into rcv crypto: failed code=" << rc);
|
||||
// The party won't be able to decrypt incoming data!
|
||||
// Not sure if anything has to be reported.
|
||||
}
|
||||
}
|
||||
|
||||
if (sendit)
|
||||
{
|
||||
HLOGC(mglog.Debug, log << "regenCryptoKm: SENDING ki=" << ki << " len=" << m_SndKmMsg[ki].MsgLen
|
||||
<< " retry(updated)=" << m_SndKmMsg[ki].iPeerRetry);
|
||||
m_parent->sendSrtMsg(SRT_CMD_KMREQ, (uint32_t *)m_SndKmMsg[ki].Msg, m_SndKmMsg[ki].MsgLen/sizeof(uint32_t));
|
||||
sent++;
|
||||
}
|
||||
}
|
||||
else if (out_len_p[i] == 0)
|
||||
{
|
||||
HLOGC(mglog.Debug, log << "no key[" << ki << "] index=" << kix << ": not generated");
|
||||
}
|
||||
else
|
||||
{
|
||||
HLOGC(mglog.Debug, log << "no key[" << ki << "] index=" << kix << ": key unchanged");
|
||||
}
|
||||
}
|
||||
|
||||
HLOGC(mglog.Debug, log << "regenCryptoKm: key[0]: len=" << m_SndKmMsg[0].MsgLen << " retry=" << m_SndKmMsg[0].iPeerRetry
|
||||
<< "; key[1]: len=" << m_SndKmMsg[1].MsgLen << " retry=" << m_SndKmMsg[1].iPeerRetry);
|
||||
|
||||
if (sent)
|
||||
m_SndKmLastTime = CTimer::getTime();
|
||||
}
|
||||
#endif
|
||||
|
||||
CCryptoControl::CCryptoControl(CUDT* parent, SRTSOCKET id):
|
||||
m_parent(parent), // should be initialized in createCC()
|
||||
m_SocketID(id),
|
||||
m_iSndKmKeyLen(0),
|
||||
m_iRcvKmKeyLen(0),
|
||||
m_SndKmState(SRT_KM_S_UNSECURED),
|
||||
m_RcvKmState(SRT_KM_S_UNSECURED),
|
||||
m_KmRefreshRatePkt(0),
|
||||
m_KmPreAnnouncePkt(0),
|
||||
m_bErrorReported(false)
|
||||
{
|
||||
|
||||
m_KmSecret.len = 0;
|
||||
//send
|
||||
m_SndKmLastTime = 0;
|
||||
m_SndKmMsg[0].MsgLen = 0;
|
||||
m_SndKmMsg[0].iPeerRetry = 0;
|
||||
m_SndKmMsg[1].MsgLen = 0;
|
||||
m_SndKmMsg[1].iPeerRetry = 0;
|
||||
m_hSndCrypto = NULL;
|
||||
//recv
|
||||
m_hRcvCrypto = NULL;
|
||||
}
|
||||
|
||||
bool CCryptoControl::init(HandshakeSide side, bool bidirectional SRT_ATR_UNUSED)
|
||||
{
|
||||
// NOTE: initiator creates m_hSndCrypto. When bidirectional,
|
||||
// it creates also m_hRcvCrypto with the same key length.
|
||||
// Acceptor creates nothing - it will create appropriate
|
||||
// contexts when receiving KMREQ from the initiator.
|
||||
|
||||
HLOGC(mglog.Debug, log << "CCryptoControl::init: HS SIDE:"
|
||||
<< (side == HSD_INITIATOR ? "INITIATOR" : "RESPONDER")
|
||||
<< " DIRECTION:" << (bidirectional ? "BOTH" : (side == HSD_INITIATOR) ? "SENDER" : "RECEIVER"));
|
||||
|
||||
// Set UNSECURED state as default
|
||||
m_RcvKmState = SRT_KM_S_UNSECURED;
|
||||
|
||||
// Set security-pending state, if a password was set.
|
||||
m_SndKmState = hasPassphrase() ? SRT_KM_S_SECURING : SRT_KM_S_UNSECURED;
|
||||
|
||||
m_KmPreAnnouncePkt = m_parent->m_uKmPreAnnouncePkt;
|
||||
m_KmRefreshRatePkt = m_parent->m_uKmRefreshRatePkt;
|
||||
|
||||
if ( side == HSD_INITIATOR )
|
||||
{
|
||||
if (hasPassphrase())
|
||||
{
|
||||
#ifdef SRT_ENABLE_ENCRYPTION
|
||||
if (m_iSndKmKeyLen == 0)
|
||||
{
|
||||
HLOGC(mglog.Debug, log << "CCryptoControl::init: PBKEYLEN still 0, setting default 16");
|
||||
m_iSndKmKeyLen = 16;
|
||||
}
|
||||
|
||||
bool ok = createCryptoCtx(Ref(m_hSndCrypto), m_iSndKmKeyLen, HAICRYPT_CRYPTO_DIR_TX);
|
||||
HLOGC(mglog.Debug, log << "CCryptoControl::init: creating SND crypto context: " << ok);
|
||||
|
||||
if (ok && bidirectional)
|
||||
{
|
||||
m_iRcvKmKeyLen = m_iSndKmKeyLen;
|
||||
int st = HaiCrypt_Clone(m_hSndCrypto, HAICRYPT_CRYPTO_DIR_RX, &m_hRcvCrypto);
|
||||
HLOGC(mglog.Debug, log << "CCryptoControl::init: creating CLONED RCV crypto context: status=" << st);
|
||||
ok = st == 0;
|
||||
}
|
||||
|
||||
// Note: this is sanity check, it should never happen.
|
||||
if (!ok)
|
||||
{
|
||||
m_SndKmState = SRT_KM_S_NOSECRET; // wanted to secure, but error occurred.
|
||||
if (bidirectional)
|
||||
m_RcvKmState = SRT_KM_S_NOSECRET;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
regenCryptoKm(
|
||||
false, // Do not send the key (will be attached it to the HSv5 handshake)
|
||||
bidirectional // replicate the key to the receiver context, if bidirectional
|
||||
);
|
||||
#else
|
||||
LOGC(mglog.Error, log << "CCryptoControl::init: encryption not supported");
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
HLOGC(mglog.Debug, log << "CCryptoControl::init: CAN'T CREATE crypto: key length for SND = " << m_iSndKmKeyLen);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HLOGC(mglog.Debug, log << "CCryptoControl::init: NOT creating crypto contexts - will be created upon reception of KMREQ");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CCryptoControl::close()
|
||||
{
|
||||
/* Wipeout secrets */
|
||||
memset(&m_KmSecret, 0, sizeof(m_KmSecret));
|
||||
}
|
||||
|
||||
std::string CCryptoControl::CONID() const
|
||||
{
|
||||
if ( m_SocketID == 0 )
|
||||
return "";
|
||||
|
||||
std::ostringstream os;
|
||||
os << "%" << m_SocketID << ":";
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
#if ENABLE_HEAVY_LOGGING
|
||||
static std::string CryptoFlags(int flg)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
vector<string> f;
|
||||
if (flg & HAICRYPT_CFG_F_CRYPTO)
|
||||
f.push_back("crypto");
|
||||
if (flg & HAICRYPT_CFG_F_TX)
|
||||
f.push_back("TX");
|
||||
if (flg & HAICRYPT_CFG_F_FEC)
|
||||
f.push_back("fec");
|
||||
|
||||
ostringstream os;
|
||||
copy(f.begin(), f.end(), ostream_iterator<string>(os, "|"));
|
||||
return os.str();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SRT_ENABLE_ENCRYPTION
|
||||
bool CCryptoControl::createCryptoCtx(ref_t<HaiCrypt_Handle> hCrypto, size_t keylen, HaiCrypt_CryptoDir cdir)
|
||||
{
|
||||
|
||||
if (*hCrypto)
|
||||
{
|
||||
// XXX You can check here if the existing handle represents
|
||||
// a correctly defined crypto. But this doesn't seem to be
|
||||
// necessary - the whole CCryptoControl facility seems to be valid only
|
||||
// within the frames of one connection.
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((m_KmSecret.len <= 0) || (keylen <= 0))
|
||||
{
|
||||
LOGC(mglog.Error, log << CONID() << "cryptoCtx: missing secret (" << m_KmSecret.len << ") or key length (" << keylen << ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
HaiCrypt_Cfg crypto_cfg;
|
||||
memset(&crypto_cfg, 0, sizeof(crypto_cfg));
|
||||
#if 0//test key refresh (fast rate)
|
||||
m_KmRefreshRatePkt = 2000;
|
||||
m_KmPreAnnouncePkt = 500;
|
||||
#endif
|
||||
crypto_cfg.flags = HAICRYPT_CFG_F_CRYPTO | (cdir == HAICRYPT_CRYPTO_DIR_TX ? HAICRYPT_CFG_F_TX : 0);
|
||||
crypto_cfg.xport = HAICRYPT_XPT_SRT;
|
||||
crypto_cfg.cryspr = HaiCryptCryspr_Get_Instance();
|
||||
crypto_cfg.key_len = (size_t)keylen;
|
||||
crypto_cfg.data_max_len = HAICRYPT_DEF_DATA_MAX_LENGTH; //MTU
|
||||
crypto_cfg.km_tx_period_ms = 0;//No HaiCrypt KM inject period, handled in SRT;
|
||||
crypto_cfg.km_refresh_rate_pkt = m_KmRefreshRatePkt == 0 ? HAICRYPT_DEF_KM_REFRESH_RATE : m_KmRefreshRatePkt;
|
||||
crypto_cfg.km_pre_announce_pkt = m_KmPreAnnouncePkt == 0 ? SRT_CRYPT_KM_PRE_ANNOUNCE : m_KmPreAnnouncePkt;
|
||||
crypto_cfg.secret = m_KmSecret;
|
||||
//memcpy(&crypto_cfg.secret, &m_KmSecret, sizeof(crypto_cfg.secret));
|
||||
|
||||
HLOGC(mglog.Debug, log << "CRYPTO CFG: flags=" << CryptoFlags(crypto_cfg.flags) << " xport=" << crypto_cfg.xport << " cryspr=" << crypto_cfg.cryspr
|
||||
<< " keylen=" << crypto_cfg.key_len << " passphrase_length=" << crypto_cfg.secret.len);
|
||||
|
||||
if (HaiCrypt_Create(&crypto_cfg, &hCrypto.get()) != HAICRYPT_OK)
|
||||
{
|
||||
LOGC(mglog.Error, log << CONID() << "cryptoCtx: could not create " << (cdir == HAICRYPT_CRYPTO_DIR_TX ? "tx" : "rx") << " crypto ctx");
|
||||
return false;
|
||||
}
|
||||
|
||||
HLOGC(mglog.Debug, log << CONID() << "cryptoCtx: CREATED crypto for dir=" << (cdir == HAICRYPT_CRYPTO_DIR_TX ? "tx" : "rx") << " keylen=" << keylen);
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
bool CCryptoControl::createCryptoCtx(ref_t<HaiCrypt_Handle>, size_t, HaiCrypt_CryptoDir)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
EncryptionStatus CCryptoControl::encrypt(ref_t<CPacket> r_packet SRT_ATR_UNUSED)
|
||||
{
|
||||
#ifdef SRT_ENABLE_ENCRYPTION
|
||||
// Encryption not enabled - do nothing.
|
||||
if ( getSndCryptoFlags() == EK_NOENC )
|
||||
return ENCS_CLEAR;
|
||||
|
||||
CPacket& packet = *r_packet;
|
||||
int rc = HaiCrypt_Tx_Data(m_hSndCrypto, (uint8_t*)packet.getHeader(), (uint8_t*)packet.m_pcData, packet.getLength());
|
||||
if (rc < 0)
|
||||
{
|
||||
return ENCS_FAILED;
|
||||
}
|
||||
else if ( rc > 0 )
|
||||
{
|
||||
// XXX what happens if the encryption is said to be "succeeded",
|
||||
// but the length is 0? Shouldn't this be treated as unwanted?
|
||||
packet.setLength(rc);
|
||||
}
|
||||
|
||||
return ENCS_CLEAR;
|
||||
#else
|
||||
return ENCS_NOTSUP;
|
||||
#endif
|
||||
}
|
||||
|
||||
EncryptionStatus CCryptoControl::decrypt(ref_t<CPacket> r_packet SRT_ATR_UNUSED)
|
||||
{
|
||||
#ifdef SRT_ENABLE_ENCRYPTION
|
||||
CPacket& packet = *r_packet;
|
||||
|
||||
if (packet.getMsgCryptoFlags() == EK_NOENC)
|
||||
{
|
||||
HLOGC(mglog.Debug, log << "CPacket::decrypt: packet not encrypted");
|
||||
return ENCS_CLEAR; // not encrypted, no need do decrypt, no flags to be modified
|
||||
}
|
||||
|
||||
if (m_RcvKmState == SRT_KM_S_UNSECURED)
|
||||
{
|
||||
if (m_KmSecret.len != 0)
|
||||
{
|
||||
// We were unaware that the peer has set password,
|
||||
// but now here we are.
|
||||
m_RcvKmState = SRT_KM_S_SECURING;
|
||||
LOGC(mglog.Note, log << "SECURITY UPDATE: Peer has surprised Agent with encryption, but KMX is pending - current packet size="
|
||||
<< packet.getLength() << " dropped");
|
||||
return ENCS_FAILED;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Peer has set a password, but Agent did not,
|
||||
// which means that it will be unable to decrypt
|
||||
// sent payloads anyway.
|
||||
m_RcvKmState = SRT_KM_S_NOSECRET;
|
||||
LOGP(mglog.Error, "SECURITY FAILURE: Agent has no PW, but Peer sender has declared one, can't decrypt");
|
||||
// This only informs about the state change; it will be also caught by the condition below
|
||||
}
|
||||
}
|
||||
|
||||
if (m_RcvKmState != SRT_KM_S_SECURED)
|
||||
{
|
||||
// If not "secured", it means that it won't be able to decrypt packets,
|
||||
// so there's no point to even try to send them to HaiCrypt_Rx_Data.
|
||||
// Actually the current conditions concerning m_hRcvCrypto are such that this object
|
||||
// is cretaed in case of SRT_KM_S_BADSECRET, so it will simply fail to decrypt,
|
||||
// but with SRT_KM_S_NOSECRET m_hRcvCrypto is not even created (is NULL), which
|
||||
// will then cause an error to be reported, misleadingly. Simply don't try to
|
||||
// decrypt anything as long as you are not sure that the connection is secured.
|
||||
|
||||
// This problem will occur every time a packet comes in, it's worth reporting,
|
||||
// but not with every single packet arriving. Print it once and turn off the flag;
|
||||
// it will be restored at the next attempt of KMX.
|
||||
if (!m_bErrorReported)
|
||||
{
|
||||
m_bErrorReported = true;
|
||||
LOGC(mglog.Error, log << "SECURITY STATUS: " << KmStateStr(m_RcvKmState) << " - can't decrypt packet.");
|
||||
}
|
||||
HLOGC(mglog.Debug, log << "Packet still not decrypted, status=" << KmStateStr(m_RcvKmState)
|
||||
<< " - dropping size=" << packet.getLength());
|
||||
return ENCS_FAILED;
|
||||
}
|
||||
|
||||
int rc = HaiCrypt_Rx_Data(m_hRcvCrypto, (uint8_t *)packet.getHeader(), (uint8_t *)packet.m_pcData, packet.getLength());
|
||||
if ( rc <= 0 )
|
||||
{
|
||||
LOGC(mglog.Error, log << "decrypt ERROR (IPE): HaiCrypt_Rx_Data failure=" << rc << " - returning failed decryption");
|
||||
// -1: decryption failure
|
||||
// 0: key not received yet
|
||||
return ENCS_FAILED;
|
||||
}
|
||||
// Otherwise: rc == decrypted text length.
|
||||
packet.setLength(rc); /* In case clr txt size is different from cipher txt */
|
||||
|
||||
// Decryption succeeded. Update flags.
|
||||
packet.setMsgCryptoFlags(EK_NOENC);
|
||||
|
||||
HLOGC(mglog.Debug, log << "decrypt: successfully decrypted, resulting length=" << rc);
|
||||
return ENCS_CLEAR;
|
||||
#else
|
||||
return ENCS_NOTSUP;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
CCryptoControl::~CCryptoControl()
|
||||
{
|
||||
#ifdef SRT_ENABLE_ENCRYPTION
|
||||
if (m_hSndCrypto)
|
||||
{
|
||||
HaiCrypt_Close(m_hSndCrypto);
|
||||
}
|
||||
|
||||
if (m_hRcvCrypto)
|
||||
{
|
||||
HaiCrypt_Close(m_hRcvCrypto);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
std::string SrtFlagString(int32_t flags)
|
||||
{
|
||||
#define LEN(arr) (sizeof (arr)/(sizeof ((arr)[0])))
|
||||
|
||||
std::string output;
|
||||
static std::string namera[] = { "TSBPD-snd", "TSBPD-rcv", "haicrypt", "TLPktDrop", "NAKReport", "ReXmitFlag", "StreamAPI" };
|
||||
|
||||
size_t i = 0;
|
||||
for ( ; i < LEN(namera); ++i )
|
||||
{
|
||||
if ( (flags & 1) == 1 )
|
||||
{
|
||||
output += "+" + namera[i] + " ";
|
||||
}
|
||||
else
|
||||
{
|
||||
output += "-" + namera[i] + " ";
|
||||
}
|
||||
|
||||
flags >>= 1;
|
||||
//if ( flags == 0 )
|
||||
// break;
|
||||
}
|
||||
|
||||
#undef LEN
|
||||
|
||||
if ( flags != 0 )
|
||||
{
|
||||
output += "+unknown";
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
268
trunk/3rdparty/srt-1-fit/srtcore/crypto.h
vendored
Normal file
268
trunk/3rdparty/srt-1-fit/srtcore/crypto.h
vendored
Normal file
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef INC__CRYPTO_H
|
||||
#define INC__CRYPTO_H
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
// UDT
|
||||
#include "udt.h"
|
||||
#include "packet.h"
|
||||
#include "utilities.h"
|
||||
#include "logging.h"
|
||||
|
||||
#include <haicrypt.h>
|
||||
#include <hcrypt_msg.h>
|
||||
|
||||
#if ENABLE_LOGGING
|
||||
|
||||
std::string KmStateStr(SRT_KM_STATE state);
|
||||
|
||||
namespace srt_logging
|
||||
{
|
||||
extern Logger mglog;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// For KMREQ/KMRSP. Only one field is used.
|
||||
const size_t SRT_KMR_KMSTATE = 0;
|
||||
|
||||
#define SRT_CMD_MAXSZ HCRYPT_MSG_KM_MAX_SZ /* Maximum SRT custom messages payload size (bytes) */
|
||||
const size_t SRTDATA_MAXSIZE = SRT_CMD_MAXSZ/sizeof(int32_t);
|
||||
|
||||
enum Whether2RegenKm {DONT_REGEN_KM = 0, REGEN_KM = 1};
|
||||
|
||||
class CCryptoControl
|
||||
{
|
||||
//public:
|
||||
class CUDT* m_parent;
|
||||
SRTSOCKET m_SocketID;
|
||||
|
||||
size_t m_iSndKmKeyLen; //Key length
|
||||
size_t m_iRcvKmKeyLen; //Key length from rx KM
|
||||
|
||||
// Temporarily allow these to be accessed.
|
||||
public:
|
||||
SRT_KM_STATE m_SndKmState; //Sender Km State (imposed by agent)
|
||||
SRT_KM_STATE m_RcvKmState; //Receiver Km State (informed by peer)
|
||||
|
||||
private:
|
||||
// Partial haicrypt configuration, consider
|
||||
// putting the whole HaiCrypt_Cfg object here.
|
||||
int m_KmRefreshRatePkt;
|
||||
int m_KmPreAnnouncePkt;
|
||||
|
||||
HaiCrypt_Secret m_KmSecret; //Key material shared secret
|
||||
// Sender
|
||||
uint64_t m_SndKmLastTime;
|
||||
struct {
|
||||
unsigned char Msg[HCRYPT_MSG_KM_MAX_SZ];
|
||||
size_t MsgLen;
|
||||
int iPeerRetry;
|
||||
} m_SndKmMsg[2];
|
||||
HaiCrypt_Handle m_hSndCrypto;
|
||||
// Receiver
|
||||
HaiCrypt_Handle m_hRcvCrypto;
|
||||
|
||||
bool m_bErrorReported;
|
||||
|
||||
public:
|
||||
|
||||
bool sendingAllowed()
|
||||
{
|
||||
// This function is called to state as to whether the
|
||||
// crypter allows the packet to be sent over the link.
|
||||
// This is possible in two cases:
|
||||
// - when Agent didn't set a password, no matter the crypto state
|
||||
if (m_KmSecret.len == 0)
|
||||
return true;
|
||||
// - when Agent did set a password and the crypto state is SECURED.
|
||||
if (m_KmSecret.len > 0 && m_SndKmState == SRT_KM_S_SECURED
|
||||
// && m_iRcvPeerKmState == SRT_KM_S_SECURED ?
|
||||
)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasPassphrase() const
|
||||
{
|
||||
return m_KmSecret.len > 0;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
#ifdef SRT_ENABLE_ENCRYPTION
|
||||
void regenCryptoKm(bool sendit, bool bidirectional);
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
size_t KeyLen() { return m_iSndKmKeyLen; }
|
||||
|
||||
// Needed for CUDT
|
||||
void updateKmState(int cmd, size_t srtlen);
|
||||
|
||||
// Detailed processing
|
||||
int processSrtMsg_KMREQ(const uint32_t* srtdata, size_t len, uint32_t* srtdata_out, ref_t<size_t> r_srtlen, int hsv);
|
||||
|
||||
// This returns:
|
||||
// 1 - the given payload is the same as the currently used key
|
||||
// 0 - there's no key in agent or the payload is error message with agent NOSECRET.
|
||||
// -1 - the payload is error message with other state or it doesn't match the key
|
||||
int processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, int hsv);
|
||||
void createFakeSndContext();
|
||||
|
||||
const unsigned char* getKmMsg_data(size_t ki) const { return m_SndKmMsg[ki].Msg; }
|
||||
size_t getKmMsg_size(size_t ki) const { return m_SndKmMsg[ki].MsgLen; }
|
||||
|
||||
/// Check if the key stored at @c ki shall be sent. When during the handshake,
|
||||
/// it only matters if the KM message for that index is recorded at all.
|
||||
/// Otherwise returns true only if also the retry counter didn't expire.
|
||||
///
|
||||
/// @param ki Key index (0 or 1)
|
||||
/// @param runtime True, if this happens as a key update
|
||||
/// during transmission (otherwise it's during the handshake)
|
||||
/// @return Whether the KM message at given index needs to be sent.
|
||||
bool getKmMsg_needSend(size_t ki, bool runtime) const
|
||||
{
|
||||
if (runtime)
|
||||
return (m_SndKmMsg[ki].iPeerRetry > 0 && m_SndKmMsg[ki].MsgLen > 0);
|
||||
else
|
||||
return m_SndKmMsg[ki].MsgLen > 0;
|
||||
}
|
||||
|
||||
/// Mark the key as already sent. When no 'runtime' (during the handshake)
|
||||
/// it actually does nothing so that this will be retried as long as the handshake
|
||||
/// itself is being retried. Otherwise this is during transmission and will expire
|
||||
/// after several retries.
|
||||
///
|
||||
/// @param ki Key index (0 or 1)
|
||||
/// @param runtime True, if this happens as a key update
|
||||
/// during transmission (otherwise it's during the handshake)
|
||||
void getKmMsg_markSent(size_t ki, bool runtime)
|
||||
{
|
||||
#if ENABLE_LOGGING
|
||||
using srt_logging::mglog;
|
||||
#endif
|
||||
|
||||
m_SndKmLastTime = CTimer::getTime();
|
||||
if (runtime)
|
||||
{
|
||||
m_SndKmMsg[ki].iPeerRetry--;
|
||||
HLOGC(mglog.Debug, log << "getKmMsg_markSent: key[" << ki << "]: len=" << m_SndKmMsg[ki].MsgLen << " retry=" << m_SndKmMsg[ki].iPeerRetry);
|
||||
}
|
||||
else
|
||||
{
|
||||
HLOGC(mglog.Debug, log << "getKmMsg_markSent: key[" << ki << "]: len=" << m_SndKmMsg[ki].MsgLen << " STILL IN USE.");
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the response returned by KMRSP matches the recorded KM message.
|
||||
/// When it is, set also the retry counter to 0 to prevent further retries.
|
||||
///
|
||||
/// @param ki KM message index (0 or 1)
|
||||
/// @param srtmsg Message received through KMRSP
|
||||
/// @param bytesize Size of the message
|
||||
/// @return True if the message is identical to the recorded KM message at given index.
|
||||
bool getKmMsg_acceptResponse(size_t ki, const uint32_t* srtmsg, size_t bytesize)
|
||||
{
|
||||
if ( m_SndKmMsg[ki].MsgLen == bytesize
|
||||
&& 0 == memcmp(m_SndKmMsg[ki].Msg, srtmsg, m_SndKmMsg[ki].MsgLen))
|
||||
{
|
||||
m_SndKmMsg[ki].iPeerRetry = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CCryptoControl(CUDT* parent, SRTSOCKET id);
|
||||
|
||||
// DEBUG PURPOSES:
|
||||
std::string CONID() const;
|
||||
std::string FormatKmMessage(std::string hdr, int cmd, size_t srtlen);
|
||||
|
||||
bool init(HandshakeSide, bool);
|
||||
void close();
|
||||
|
||||
// This function is used in:
|
||||
// - HSv4 (initial key material exchange - in HSv5 it's attached to handshake)
|
||||
// - case of key regeneration, which should be then exchanged again
|
||||
void sendKeysToPeer(Whether2RegenKm regen);
|
||||
|
||||
|
||||
void setCryptoSecret(const HaiCrypt_Secret& secret)
|
||||
{
|
||||
m_KmSecret = secret;
|
||||
//memcpy(&m_KmSecret, &secret, sizeof(m_KmSecret));
|
||||
}
|
||||
|
||||
void setCryptoKeylen(size_t keylen)
|
||||
{
|
||||
m_iSndKmKeyLen = keylen;
|
||||
m_iRcvKmKeyLen = keylen;
|
||||
}
|
||||
|
||||
bool createCryptoCtx(ref_t<HaiCrypt_Handle> rh, size_t keylen, HaiCrypt_CryptoDir tx);
|
||||
|
||||
int getSndCryptoFlags() const
|
||||
{
|
||||
#ifdef SRT_ENABLE_ENCRYPTION
|
||||
return(m_hSndCrypto ?
|
||||
HaiCrypt_Tx_GetKeyFlags(m_hSndCrypto) :
|
||||
// When encryption isn't on, check if it was required
|
||||
// If it was, return -1 as flags, which means that
|
||||
// encryption was requested and not possible.
|
||||
hasPassphrase() ? -1 :
|
||||
0);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool isSndEncryptionOK() const
|
||||
{
|
||||
// Similar to this above, just quickly check if the encryption
|
||||
// is required and possible, or not possible
|
||||
if (!hasPassphrase())
|
||||
return true; // no encryption required
|
||||
|
||||
if (m_hSndCrypto)
|
||||
return true; // encryption is required and possible
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Encrypts the packet. If encryption is not turned on, it
|
||||
/// does nothing. If the encryption is not correctly configured,
|
||||
/// the encryption will fail.
|
||||
/// XXX Encryption flags in the PH_MSGNO
|
||||
/// field in the header must be correctly set before calling.
|
||||
EncryptionStatus encrypt(ref_t<CPacket> r_packet);
|
||||
|
||||
/// Decrypts the packet. If the packet has ENCKEYSPEC part
|
||||
/// in PH_MSGNO set to EK_NOENC, it does nothing. It decrypts
|
||||
/// only if the encryption correctly configured, otherwise it
|
||||
/// fails. After successful decryption, the ENCKEYSPEC part
|
||||
// in PH_MSGNO is set to EK_NOENC.
|
||||
EncryptionStatus decrypt(ref_t<CPacket> r_packet);
|
||||
|
||||
~CCryptoControl();
|
||||
};
|
||||
|
||||
#endif // SRT_CONGESTION_CONTROL_H
|
727
trunk/3rdparty/srt-1-fit/srtcore/epoll.cpp
vendored
Normal file
727
trunk/3rdparty/srt-1-fit/srtcore/epoll.cpp
vendored
Normal file
|
@ -0,0 +1,727 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Yunhong Gu, last updated 01/01/2011
|
||||
modified by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifdef LINUX
|
||||
#include <sys/epoll.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#if __APPLE__
|
||||
#include "TargetConditionals.h"
|
||||
#endif
|
||||
#if defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
|
||||
#include <sys/types.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#if defined(__ANDROID__) || defined(ANDROID)
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
|
||||
#include "common.h"
|
||||
#include "epoll.h"
|
||||
#include "logging.h"
|
||||
#include "udt.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace srt_logging
|
||||
{
|
||||
extern Logger mglog;
|
||||
}
|
||||
|
||||
using namespace srt_logging;
|
||||
|
||||
#if ENABLE_HEAVY_LOGGING
|
||||
#define IF_DIRNAME(tested, flag, name) (tested & flag ? name : "")
|
||||
#endif
|
||||
|
||||
CEPoll::CEPoll():
|
||||
m_iIDSeed(0)
|
||||
{
|
||||
CGuard::createMutex(m_EPollLock);
|
||||
}
|
||||
|
||||
CEPoll::~CEPoll()
|
||||
{
|
||||
CGuard::releaseMutex(m_EPollLock);
|
||||
}
|
||||
|
||||
int CEPoll::create()
|
||||
{
|
||||
CGuard pg(m_EPollLock);
|
||||
|
||||
if (++ m_iIDSeed >= 0x7FFFFFFF)
|
||||
m_iIDSeed = 0;
|
||||
|
||||
// Check if an item already exists. Should not ever happen.
|
||||
if (m_mPolls.find(m_iIDSeed) != m_mPolls.end())
|
||||
throw CUDTException(MJ_SETUP, MN_NONE);
|
||||
|
||||
int localid = 0;
|
||||
|
||||
#ifdef LINUX
|
||||
localid = epoll_create(1024);
|
||||
/* Possible reasons of -1 error:
|
||||
EMFILE: The per-user limit on the number of epoll instances imposed by /proc/sys/fs/epoll/max_user_instances was encountered.
|
||||
ENFILE: The system limit on the total number of open files has been reached.
|
||||
ENOMEM: There was insufficient memory to create the kernel object.
|
||||
*/
|
||||
if (localid < 0)
|
||||
throw CUDTException(MJ_SETUP, MN_NONE, errno);
|
||||
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
|
||||
localid = kqueue();
|
||||
if (localid < 0)
|
||||
throw CUDTException(MJ_SETUP, MN_NONE, errno);
|
||||
#else
|
||||
// on Solaris, use /dev/poll
|
||||
// on Windows, select
|
||||
#endif
|
||||
|
||||
pair<map<int, CEPollDesc>::iterator, bool> res = m_mPolls.insert(make_pair(m_iIDSeed, CEPollDesc(m_iIDSeed, localid)));
|
||||
if (!res.second) // Insertion failed (no memory?)
|
||||
throw CUDTException(MJ_SETUP, MN_NONE);
|
||||
|
||||
return m_iIDSeed;
|
||||
}
|
||||
|
||||
int CEPoll::add_ssock(const int eid, const SYSSOCKET& s, const int* events)
|
||||
{
|
||||
CGuard pg(m_EPollLock);
|
||||
|
||||
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
|
||||
if (p == m_mPolls.end())
|
||||
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
|
||||
|
||||
#ifdef LINUX
|
||||
epoll_event ev;
|
||||
memset(&ev, 0, sizeof(epoll_event));
|
||||
|
||||
if (NULL == events)
|
||||
ev.events = EPOLLIN | EPOLLOUT | EPOLLERR;
|
||||
else
|
||||
{
|
||||
ev.events = 0;
|
||||
if (*events & UDT_EPOLL_IN)
|
||||
ev.events |= EPOLLIN;
|
||||
if (*events & UDT_EPOLL_OUT)
|
||||
ev.events |= EPOLLOUT;
|
||||
if (*events & UDT_EPOLL_ERR)
|
||||
ev.events |= EPOLLERR;
|
||||
}
|
||||
|
||||
ev.data.fd = s;
|
||||
if (::epoll_ctl(p->second.m_iLocalID, EPOLL_CTL_ADD, s, &ev) < 0)
|
||||
throw CUDTException();
|
||||
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
|
||||
struct kevent ke[2];
|
||||
int num = 0;
|
||||
|
||||
if (NULL == events)
|
||||
{
|
||||
EV_SET(&ke[num++], s, EVFILT_READ, EV_ADD, 0, 0, NULL);
|
||||
EV_SET(&ke[num++], s, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*events & UDT_EPOLL_IN)
|
||||
{
|
||||
EV_SET(&ke[num++], s, EVFILT_READ, EV_ADD, 0, 0, NULL);
|
||||
}
|
||||
if (*events & UDT_EPOLL_OUT)
|
||||
{
|
||||
EV_SET(&ke[num++], s, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
|
||||
}
|
||||
}
|
||||
if (kevent(p->second.m_iLocalID, ke, num, NULL, 0, NULL) < 0)
|
||||
throw CUDTException();
|
||||
#else
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Microsoft Visual Studio doesn't support the #warning directive - nonstandard anyway.
|
||||
// Use #pragma message with the same text.
|
||||
// All other compilers should be ok :)
|
||||
#pragma message("WARNING: Unsupported system for epoll. The epoll_add_ssock() API call won't work on this platform.")
|
||||
#else
|
||||
#warning "Unsupported system for epoll. The epoll_add_ssock() API call won't work on this platform."
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
p->second.m_sLocals.insert(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CEPoll::remove_ssock(const int eid, const SYSSOCKET& s)
|
||||
{
|
||||
CGuard pg(m_EPollLock);
|
||||
|
||||
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
|
||||
if (p == m_mPolls.end())
|
||||
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
|
||||
|
||||
#ifdef LINUX
|
||||
epoll_event ev; // ev is ignored, for compatibility with old Linux kernel only.
|
||||
if (::epoll_ctl(p->second.m_iLocalID, EPOLL_CTL_DEL, s, &ev) < 0)
|
||||
throw CUDTException();
|
||||
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
|
||||
struct kevent ke;
|
||||
|
||||
//
|
||||
// Since I don't know what was set before
|
||||
// Just clear out both read and write
|
||||
//
|
||||
EV_SET(&ke, s, EVFILT_READ, EV_DELETE, 0, 0, NULL);
|
||||
kevent(p->second.m_iLocalID, &ke, 1, NULL, 0, NULL);
|
||||
EV_SET(&ke, s, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
|
||||
kevent(p->second.m_iLocalID, &ke, 1, NULL, 0, NULL);
|
||||
#endif
|
||||
|
||||
p->second.m_sLocals.erase(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Need this to atomically modify polled events (ex: remove write/keep read)
|
||||
int CEPoll::update_usock(const int eid, const SRTSOCKET& u, const int* events)
|
||||
{
|
||||
CGuard pg(m_EPollLock);
|
||||
|
||||
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
|
||||
if (p == m_mPolls.end())
|
||||
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
|
||||
|
||||
CEPollDesc& d = p->second;
|
||||
|
||||
int32_t evts = events ? *events : uint32_t(SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR);
|
||||
bool edgeTriggered = evts & SRT_EPOLL_ET;
|
||||
evts &= ~SRT_EPOLL_ET;
|
||||
if (evts)
|
||||
{
|
||||
pair<CEPollDesc::ewatch_t::iterator, bool> iter_new = d.addWatch(u, evts, edgeTriggered);
|
||||
CEPollDesc::Wait& wait = iter_new.first->second;
|
||||
if (!iter_new.second)
|
||||
{
|
||||
// The object exists. We only are certain about the `u`
|
||||
// parameter, but others are probably unchanged. Change them
|
||||
// forcefully and take out notices that are no longer valid.
|
||||
const int removable = wait.watch & ~evts;
|
||||
|
||||
// Check if there are any events that would be removed.
|
||||
// If there are no removed events watched (for example, when
|
||||
// only new events are being added to existing socket),
|
||||
// there's nothing to remove, but might be something to update.
|
||||
if (removable)
|
||||
{
|
||||
d.removeExcessEvents(wait, evts);
|
||||
}
|
||||
|
||||
// Update the watch configuration, including edge
|
||||
wait.watch = evts;
|
||||
if (edgeTriggered)
|
||||
wait.edge = evts;
|
||||
|
||||
// Now it should look exactly like newly added
|
||||
// and the state is also updated
|
||||
}
|
||||
|
||||
const int newstate = wait.watch & wait.state;
|
||||
if (newstate)
|
||||
{
|
||||
d.addEventNotice(wait, u, newstate);
|
||||
}
|
||||
}
|
||||
else if (edgeTriggered)
|
||||
{
|
||||
// Specified only SRT_EPOLL_ET flag, but no event flag. Error.
|
||||
throw CUDTException(MJ_NOTSUP, MN_INVAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update with no events means to remove subscription
|
||||
d.removeSubscription(u);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CEPoll::update_ssock(const int eid, const SYSSOCKET& s, const int* events)
|
||||
{
|
||||
CGuard pg(m_EPollLock);
|
||||
|
||||
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
|
||||
if (p == m_mPolls.end())
|
||||
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
|
||||
|
||||
#ifdef LINUX
|
||||
epoll_event ev;
|
||||
memset(&ev, 0, sizeof(epoll_event));
|
||||
|
||||
if (NULL == events)
|
||||
ev.events = EPOLLIN | EPOLLOUT | EPOLLERR;
|
||||
else
|
||||
{
|
||||
ev.events = 0;
|
||||
if (*events & UDT_EPOLL_IN)
|
||||
ev.events |= EPOLLIN;
|
||||
if (*events & UDT_EPOLL_OUT)
|
||||
ev.events |= EPOLLOUT;
|
||||
if (*events & UDT_EPOLL_ERR)
|
||||
ev.events |= EPOLLERR;
|
||||
}
|
||||
|
||||
ev.data.fd = s;
|
||||
if (::epoll_ctl(p->second.m_iLocalID, EPOLL_CTL_MOD, s, &ev) < 0)
|
||||
throw CUDTException();
|
||||
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
|
||||
struct kevent ke[2];
|
||||
int num = 0;
|
||||
|
||||
//
|
||||
// Since I don't know what was set before
|
||||
// Just clear out both read and write
|
||||
//
|
||||
EV_SET(&ke[0], s, EVFILT_READ, EV_DELETE, 0, 0, NULL);
|
||||
kevent(p->second.m_iLocalID, ke, 1, NULL, 0, NULL);
|
||||
EV_SET(&ke[0], s, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
|
||||
kevent(p->second.m_iLocalID, ke, 1, NULL, 0, NULL);
|
||||
if (NULL == events)
|
||||
{
|
||||
EV_SET(&ke[num++], s, EVFILT_READ, EV_ADD, 0, 0, NULL);
|
||||
EV_SET(&ke[num++], s, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*events & UDT_EPOLL_IN)
|
||||
{
|
||||
EV_SET(&ke[num++], s, EVFILT_READ, EV_ADD, 0, 0, NULL);
|
||||
}
|
||||
if (*events & UDT_EPOLL_OUT)
|
||||
{
|
||||
EV_SET(&ke[num++], s, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
|
||||
}
|
||||
}
|
||||
if (kevent(p->second.m_iLocalID, ke, num, NULL, 0, NULL) < 0)
|
||||
throw CUDTException();
|
||||
#endif
|
||||
// Assuming add is used if not inserted
|
||||
// p->second.m_sLocals.insert(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CEPoll::setflags(const int eid, int32_t flags)
|
||||
{
|
||||
CGuard pg(m_EPollLock);
|
||||
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
|
||||
if (p == m_mPolls.end())
|
||||
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
|
||||
CEPollDesc& ed = p->second;
|
||||
|
||||
int32_t oflags = ed.flags();
|
||||
|
||||
if (flags == -1)
|
||||
return oflags;
|
||||
|
||||
if (flags == 0)
|
||||
{
|
||||
ed.clr_flags(~int32_t());
|
||||
}
|
||||
else
|
||||
{
|
||||
ed.set_flags(flags);
|
||||
}
|
||||
|
||||
return oflags;
|
||||
}
|
||||
|
||||
int CEPoll::uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut)
|
||||
{
|
||||
// It is allowed to call this function witn fdsSize == 0
|
||||
// and therefore also NULL fdsSet. This will then only report
|
||||
// the number of ready sockets, just without information which.
|
||||
if (fdsSize < 0 || (fdsSize > 0 && !fdsSet))
|
||||
throw CUDTException(MJ_NOTSUP, MN_INVAL);
|
||||
|
||||
int64_t entertime = CTimer::getTime();
|
||||
|
||||
while (true)
|
||||
{
|
||||
{
|
||||
CGuard pg(m_EPollLock);
|
||||
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
|
||||
if (p == m_mPolls.end())
|
||||
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
|
||||
CEPollDesc& ed = p->second;
|
||||
|
||||
if (!ed.flags(SRT_EPOLL_ENABLE_EMPTY) && ed.watch_empty())
|
||||
{
|
||||
// Empty EID is not allowed, report error.
|
||||
throw CUDTException(MJ_NOTSUP, MN_INVAL);
|
||||
}
|
||||
|
||||
if (ed.flags(SRT_EPOLL_ENABLE_OUTPUTCHECK) && (fdsSet == NULL || fdsSize == 0))
|
||||
{
|
||||
// Empty EID is not allowed, report error.
|
||||
throw CUDTException(MJ_NOTSUP, MN_INVAL);
|
||||
}
|
||||
|
||||
if (!ed.m_sLocals.empty())
|
||||
{
|
||||
// XXX Add error log
|
||||
// uwait should not be used with EIDs subscribed to system sockets
|
||||
throw CUDTException(MJ_NOTSUP, MN_INVAL);
|
||||
}
|
||||
|
||||
int total = 0; // This is a list, so count it during iteration
|
||||
CEPollDesc::enotice_t::iterator i = ed.enotice_begin();
|
||||
while (i != ed.enotice_end())
|
||||
{
|
||||
int pos = total; // previous past-the-end position
|
||||
++total;
|
||||
|
||||
if (total > fdsSize)
|
||||
break;
|
||||
|
||||
fdsSet[pos] = *i;
|
||||
|
||||
ed.checkEdge(i++); // NOTE: potentially deletes `i`
|
||||
}
|
||||
if (total)
|
||||
return total;
|
||||
}
|
||||
|
||||
if ((msTimeOut >= 0) && (int64_t(CTimer::getTime() - entertime) >= msTimeOut * int64_t(1000)))
|
||||
break; // official wait does: throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0);
|
||||
|
||||
CTimer::waitForEvent();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CEPoll::wait(const int eid, set<SRTSOCKET>* readfds, set<SRTSOCKET>* writefds, int64_t msTimeOut, set<SYSSOCKET>* lrfds, set<SYSSOCKET>* lwfds)
|
||||
{
|
||||
// if all fields is NULL and waiting time is infinite, then this would be a deadlock
|
||||
if (!readfds && !writefds && !lrfds && !lwfds && (msTimeOut < 0))
|
||||
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
|
||||
|
||||
// Clear these sets in case the app forget to do it.
|
||||
if (readfds) readfds->clear();
|
||||
if (writefds) writefds->clear();
|
||||
if (lrfds) lrfds->clear();
|
||||
if (lwfds) lwfds->clear();
|
||||
|
||||
int total = 0;
|
||||
|
||||
int64_t entertime = CTimer::getTime();
|
||||
|
||||
HLOGC(mglog.Debug, log << "CEPoll::wait: START for eid=" << eid);
|
||||
|
||||
while (true)
|
||||
{
|
||||
{
|
||||
CGuard epollock(m_EPollLock);
|
||||
|
||||
map<int, CEPollDesc>::iterator p = m_mPolls.find(eid);
|
||||
if (p == m_mPolls.end())
|
||||
{
|
||||
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
|
||||
}
|
||||
|
||||
CEPollDesc& ed = p->second;
|
||||
|
||||
if (!ed.flags(SRT_EPOLL_ENABLE_EMPTY) && ed.watch_empty() && ed.m_sLocals.empty())
|
||||
{
|
||||
// Empty EID is not allowed, report error.
|
||||
throw CUDTException(MJ_NOTSUP, MN_INVAL);
|
||||
}
|
||||
|
||||
if (ed.flags(SRT_EPOLL_ENABLE_OUTPUTCHECK))
|
||||
{
|
||||
// Empty report is not allowed, report error.
|
||||
if (!ed.m_sLocals.empty() && (!lrfds || !lwfds))
|
||||
throw CUDTException(MJ_NOTSUP, MN_INVAL);
|
||||
|
||||
if (!ed.watch_empty() && (!readfds || !writefds))
|
||||
throw CUDTException(MJ_NOTSUP, MN_INVAL);
|
||||
}
|
||||
|
||||
IF_HEAVY_LOGGING(int total_noticed = 0);
|
||||
IF_HEAVY_LOGGING(ostringstream debug_sockets);
|
||||
// Sockets with exceptions are returned to both read and write sets.
|
||||
for (CEPollDesc::enotice_t::iterator it = ed.enotice_begin(), it_next = it; it != ed.enotice_end(); it = it_next)
|
||||
{
|
||||
++it_next;
|
||||
IF_HEAVY_LOGGING(++total_noticed);
|
||||
if (readfds && ((it->events & UDT_EPOLL_IN) || (it->events & UDT_EPOLL_ERR)))
|
||||
{
|
||||
if (readfds->insert(it->fd).second)
|
||||
++total;
|
||||
}
|
||||
|
||||
if (writefds && ((it->events & UDT_EPOLL_OUT) || (it->events & UDT_EPOLL_ERR)))
|
||||
{
|
||||
if (writefds->insert(it->fd).second)
|
||||
++total;
|
||||
}
|
||||
|
||||
IF_HEAVY_LOGGING(debug_sockets << " " << it->fd << ":"
|
||||
<< IF_DIRNAME(it->events, SRT_EPOLL_IN, "R")
|
||||
<< IF_DIRNAME(it->events, SRT_EPOLL_OUT, "W")
|
||||
<< IF_DIRNAME(it->events, SRT_EPOLL_ERR, "E"));
|
||||
|
||||
if (ed.checkEdge(it)) // NOTE: potentially erases 'it'.
|
||||
{
|
||||
IF_HEAVY_LOGGING(debug_sockets << "!");
|
||||
}
|
||||
}
|
||||
|
||||
HLOGC(mglog.Debug, log << "CEPoll::wait: REPORTED " << total << "/" << total_noticed
|
||||
<< debug_sockets.str());
|
||||
|
||||
if (lrfds || lwfds)
|
||||
{
|
||||
#ifdef LINUX
|
||||
const int max_events = ed.m_sLocals.size();
|
||||
epoll_event ev[max_events];
|
||||
int nfds = ::epoll_wait(ed.m_iLocalID, ev, max_events, 0);
|
||||
|
||||
IF_HEAVY_LOGGING(const int prev_total = total);
|
||||
for (int i = 0; i < nfds; ++ i)
|
||||
{
|
||||
if ((NULL != lrfds) && (ev[i].events & EPOLLIN))
|
||||
{
|
||||
lrfds->insert(ev[i].data.fd);
|
||||
++ total;
|
||||
}
|
||||
if ((NULL != lwfds) && (ev[i].events & EPOLLOUT))
|
||||
{
|
||||
lwfds->insert(ev[i].data.fd);
|
||||
++ total;
|
||||
}
|
||||
}
|
||||
HLOGC(mglog.Debug, log << "CEPoll::wait: LINUX: picking up " << (total - prev_total) << " ready fds.");
|
||||
|
||||
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
|
||||
struct timespec tmout = {0, 0};
|
||||
const int max_events = ed.m_sLocals.size();
|
||||
struct kevent ke[max_events];
|
||||
|
||||
int nfds = kevent(ed.m_iLocalID, NULL, 0, ke, max_events, &tmout);
|
||||
IF_HEAVY_LOGGING(const int prev_total = total);
|
||||
|
||||
for (int i = 0; i < nfds; ++ i)
|
||||
{
|
||||
if ((NULL != lrfds) && (ke[i].filter == EVFILT_READ))
|
||||
{
|
||||
lrfds->insert(ke[i].ident);
|
||||
++ total;
|
||||
}
|
||||
if ((NULL != lwfds) && (ke[i].filter == EVFILT_WRITE))
|
||||
{
|
||||
lwfds->insert(ke[i].ident);
|
||||
++ total;
|
||||
}
|
||||
}
|
||||
|
||||
HLOGC(mglog.Debug, log << "CEPoll::wait: Darwin/BSD: picking up " << (total - prev_total) << " ready fds.");
|
||||
|
||||
#else
|
||||
//currently "select" is used for all non-Linux platforms.
|
||||
//faster approaches can be applied for specific systems in the future.
|
||||
|
||||
//"select" has a limitation on the number of sockets
|
||||
int max_fd = 0;
|
||||
|
||||
fd_set rqreadfds;
|
||||
fd_set rqwritefds;
|
||||
FD_ZERO(&rqreadfds);
|
||||
FD_ZERO(&rqwritefds);
|
||||
|
||||
for (set<SYSSOCKET>::const_iterator i = ed.m_sLocals.begin(); i != ed.m_sLocals.end(); ++ i)
|
||||
{
|
||||
if (lrfds)
|
||||
FD_SET(*i, &rqreadfds);
|
||||
if (lwfds)
|
||||
FD_SET(*i, &rqwritefds);
|
||||
if ((int)*i > max_fd)
|
||||
max_fd = *i;
|
||||
}
|
||||
|
||||
IF_HEAVY_LOGGING(const int prev_total = total);
|
||||
timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
if (::select(max_fd + 1, &rqreadfds, &rqwritefds, NULL, &tv) > 0)
|
||||
{
|
||||
for (set<SYSSOCKET>::const_iterator i = ed.m_sLocals.begin(); i != ed.m_sLocals.end(); ++ i)
|
||||
{
|
||||
if (lrfds && FD_ISSET(*i, &rqreadfds))
|
||||
{
|
||||
lrfds->insert(*i);
|
||||
++ total;
|
||||
}
|
||||
if (lwfds && FD_ISSET(*i, &rqwritefds))
|
||||
{
|
||||
lwfds->insert(*i);
|
||||
++ total;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HLOGC(mglog.Debug, log << "CEPoll::wait: select(otherSYS): picking up " << (total - prev_total) << " ready fds.");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // END-LOCK: m_EPollLock
|
||||
|
||||
HLOGC(mglog.Debug, log << "CEPoll::wait: Total of " << total << " READY SOCKETS");
|
||||
|
||||
if (total > 0)
|
||||
return total;
|
||||
|
||||
if ((msTimeOut >= 0) && (int64_t(CTimer::getTime() - entertime) >= msTimeOut * int64_t(1000)))
|
||||
{
|
||||
HLOGP(mglog.Debug, "... not waiting longer - timeout");
|
||||
throw CUDTException(MJ_AGAIN, MN_XMTIMEOUT, 0);
|
||||
}
|
||||
|
||||
CTimer::EWait wt ATR_UNUSED = CTimer::waitForEvent();
|
||||
HLOGC(mglog.Debug, log << "CEPoll::wait: EVENT WAITING: "
|
||||
<< (wt == CTimer::WT_TIMEOUT ? "CHECKPOINT" : wt == CTimer::WT_EVENT ? "TRIGGERED" : "ERROR"));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CEPoll::release(const int eid)
|
||||
{
|
||||
CGuard pg(m_EPollLock);
|
||||
|
||||
map<int, CEPollDesc>::iterator i = m_mPolls.find(eid);
|
||||
if (i == m_mPolls.end())
|
||||
throw CUDTException(MJ_NOTSUP, MN_EIDINVAL);
|
||||
|
||||
#ifdef LINUX
|
||||
// release local/system epoll descriptor
|
||||
::close(i->second.m_iLocalID);
|
||||
#elif defined(BSD) || defined(OSX) || (TARGET_OS_IOS == 1) || (TARGET_OS_TV == 1)
|
||||
::close(i->second.m_iLocalID);
|
||||
#endif
|
||||
|
||||
m_mPolls.erase(i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int CEPoll::update_events(const SRTSOCKET& uid, std::set<int>& eids, const int events, const bool enable)
|
||||
{
|
||||
vector<int> lost;
|
||||
|
||||
CGuard pg(m_EPollLock);
|
||||
for (set<int>::iterator i = eids.begin(); i != eids.end(); ++ i)
|
||||
{
|
||||
map<int, CEPollDesc>::iterator p = m_mPolls.find(*i);
|
||||
if (p == m_mPolls.end())
|
||||
{
|
||||
// EID invalid, though still present in the socket's subscriber list
|
||||
// (dangling in the socket). Postpone to fix the subscruption and continue.
|
||||
lost.push_back(*i);
|
||||
continue;
|
||||
}
|
||||
|
||||
CEPollDesc& ed = p->second;
|
||||
|
||||
// Check if this EID is subscribed for this socket.
|
||||
CEPollDesc::Wait* pwait = ed.watch_find(uid);
|
||||
if (!pwait)
|
||||
{
|
||||
// As this is mapped in the socket's data, it should be impossible.
|
||||
continue;
|
||||
}
|
||||
|
||||
// compute new states
|
||||
|
||||
// New state to be set into the permanent state
|
||||
const int newstate = enable ? pwait->state | events // SET event bits if enable
|
||||
: pwait->state & (~events); // CLEAR event bits
|
||||
|
||||
// compute states changes!
|
||||
int changes = pwait->state ^ newstate; // oldState XOR newState
|
||||
if (!changes)
|
||||
continue; // no changes!
|
||||
// assign new state
|
||||
pwait->state = newstate;
|
||||
// filter change relating what is watching
|
||||
changes &= pwait->watch;
|
||||
if (!changes)
|
||||
continue; // no change watching
|
||||
// set events changes!
|
||||
|
||||
// This function will update the notice object associated with
|
||||
// the given events, that is:
|
||||
// - if enable, it will set event flags, possibly in a new notice object
|
||||
// - if !enable, it will clear event flags, possibly remove notice if resulted in 0
|
||||
ed.updateEventNotice(*pwait, uid, events, enable);
|
||||
}
|
||||
|
||||
for (vector<int>::iterator i = lost.begin(); i != lost.end(); ++ i)
|
||||
eids.erase(*i);
|
||||
|
||||
return 0;
|
||||
}
|
395
trunk/3rdparty/srt-1-fit/srtcore/epoll.h
vendored
Executable file
395
trunk/3rdparty/srt-1-fit/srtcore/epoll.h
vendored
Executable file
|
@ -0,0 +1,395 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2010, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Yunhong Gu, last updated 08/20/2010
|
||||
modified by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __UDT_EPOLL_H__
|
||||
#define __UDT_EPOLL_H__
|
||||
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <list>
|
||||
#include "udt.h"
|
||||
|
||||
|
||||
struct CEPollDesc
|
||||
{
|
||||
const int m_iID; // epoll ID
|
||||
|
||||
struct Wait;
|
||||
|
||||
struct Notice: public SRT_EPOLL_EVENT
|
||||
{
|
||||
Wait* parent;
|
||||
|
||||
Notice(Wait* p, SRTSOCKET sock, int ev): parent(p)
|
||||
{
|
||||
fd = sock;
|
||||
events = ev;
|
||||
}
|
||||
};
|
||||
|
||||
/// The type for `m_USockEventNotice`, the pair contains:
|
||||
/// * The back-pointer to the subscriber object for which this event notice serves
|
||||
/// * The events currently being on
|
||||
typedef std::list<Notice> enotice_t;
|
||||
|
||||
struct Wait
|
||||
{
|
||||
/// Events the subscriber is interested with. Only those will be
|
||||
/// regarded when updating event flags.
|
||||
int watch;
|
||||
|
||||
/// Which events should be edge-triggered. When the event isn't
|
||||
/// mentioned in `watch`, this bit flag is disregarded. Otherwise
|
||||
/// it means that the event is to be waited for persistent state
|
||||
/// if this flag is not present here, and for edge trigger, if
|
||||
/// the flag is present here.
|
||||
int edge;
|
||||
|
||||
/// The current persistent state. This is usually duplicated in
|
||||
/// a dedicated state object in `m_USockEventNotice`, however the state
|
||||
/// here will stay forever as is, regardless of the edge/persistent
|
||||
/// subscription mode for the event.
|
||||
int state;
|
||||
|
||||
/// The iterator to `m_USockEventNotice` container that contains the
|
||||
/// event notice object for this subscription, or the value from
|
||||
/// `nullNotice()` if there is no such object.
|
||||
enotice_t::iterator notit;
|
||||
|
||||
Wait(int sub, bool etr, enotice_t::iterator i)
|
||||
:watch(sub)
|
||||
,edge(etr ? sub : 0)
|
||||
,state(0)
|
||||
,notit(i)
|
||||
{
|
||||
}
|
||||
|
||||
int edgeOnly() { return edge & watch; }
|
||||
};
|
||||
|
||||
typedef std::map<SRTSOCKET, Wait> ewatch_t;
|
||||
|
||||
private:
|
||||
|
||||
/// Sockets that are subscribed for events in this eid.
|
||||
ewatch_t m_USockWatchState;
|
||||
|
||||
/// Objects representing changes in SRT sockets.
|
||||
/// Objects are removed from here when an event is registerred as edge-triggered.
|
||||
/// Otherwise it is removed only when all events as per subscription
|
||||
/// are no longer on.
|
||||
enotice_t m_USockEventNotice;
|
||||
|
||||
// Special behavior
|
||||
int32_t m_Flags;
|
||||
|
||||
enotice_t::iterator nullNotice() { return m_USockEventNotice.end(); }
|
||||
|
||||
public:
|
||||
|
||||
CEPollDesc(int id, int localID)
|
||||
: m_iID(id)
|
||||
, m_Flags(0)
|
||||
, m_iLocalID(localID)
|
||||
{
|
||||
}
|
||||
|
||||
static const int32_t EF_NOCHECK_EMPTY = 1 << 0;
|
||||
static const int32_t EF_CHECK_REP = 1 << 1;
|
||||
|
||||
int32_t flags() { return m_Flags; }
|
||||
bool flags(int32_t f) { return (m_Flags & f) != 0; }
|
||||
void set_flags(int32_t flg) { m_Flags |= flg; }
|
||||
void clr_flags(int32_t flg) { m_Flags &= ~flg; }
|
||||
|
||||
// Container accessors for ewatch_t.
|
||||
bool watch_empty() { return m_USockWatchState.empty(); }
|
||||
Wait* watch_find(SRTSOCKET sock)
|
||||
{
|
||||
ewatch_t::iterator i = m_USockWatchState.find(sock);
|
||||
if (i == m_USockWatchState.end())
|
||||
return NULL;
|
||||
return &i->second;
|
||||
}
|
||||
|
||||
// Container accessors for enotice_t.
|
||||
enotice_t::iterator enotice_begin() { return m_USockEventNotice.begin(); }
|
||||
enotice_t::iterator enotice_end() { return m_USockEventNotice.end(); }
|
||||
|
||||
const int m_iLocalID; // local system epoll ID
|
||||
std::set<SYSSOCKET> m_sLocals; // set of local (non-UDT) descriptors
|
||||
|
||||
std::pair<ewatch_t::iterator, bool> addWatch(SRTSOCKET sock, int32_t events, bool edgeTrg)
|
||||
{
|
||||
return m_USockWatchState.insert(std::make_pair(sock, Wait(events, edgeTrg, nullNotice())));
|
||||
}
|
||||
|
||||
void addEventNotice(Wait& wait, SRTSOCKET sock, int events)
|
||||
{
|
||||
// `events` contains bits to be set, so:
|
||||
//
|
||||
// 1. If no notice object exists, add it exactly with `events`.
|
||||
// 2. If it exists, only set the bits from `events`.
|
||||
// ASSUME: 'events' is not 0, that is, we have some readiness
|
||||
|
||||
if (wait.notit == nullNotice()) // No notice object
|
||||
{
|
||||
// Add new event notice and bind to the wait object.
|
||||
m_USockEventNotice.push_back(Notice(&wait, sock, events));
|
||||
wait.notit = --m_USockEventNotice.end();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// We have an existing event notice, so update it
|
||||
wait.notit->events |= events;
|
||||
}
|
||||
|
||||
// This function only updates the corresponding event notice object
|
||||
// according to the change in the events.
|
||||
void updateEventNotice(Wait& wait, SRTSOCKET sock, int events, bool enable)
|
||||
{
|
||||
if (enable)
|
||||
{
|
||||
addEventNotice(wait, sock, events);
|
||||
}
|
||||
else
|
||||
{
|
||||
removeExcessEvents(wait, ~events);
|
||||
}
|
||||
}
|
||||
|
||||
void removeSubscription(SRTSOCKET u)
|
||||
{
|
||||
std::map<SRTSOCKET, Wait>::iterator i = m_USockWatchState.find(u);
|
||||
if (i == m_USockWatchState.end())
|
||||
return;
|
||||
|
||||
if (i->second.notit != nullNotice())
|
||||
{
|
||||
m_USockEventNotice.erase(i->second.notit);
|
||||
// NOTE: no need to update the Wait::notit field
|
||||
// because the Wait object is about to be removed anyway.
|
||||
}
|
||||
m_USockWatchState.erase(i);
|
||||
}
|
||||
|
||||
void removeExistingNotices(Wait& wait)
|
||||
{
|
||||
m_USockEventNotice.erase(wait.notit);
|
||||
wait.notit = nullNotice();
|
||||
}
|
||||
|
||||
void removeEvents(Wait& wait)
|
||||
{
|
||||
if (wait.notit == nullNotice())
|
||||
return;
|
||||
removeExistingNotices(wait);
|
||||
}
|
||||
|
||||
// This function removes notices referring to
|
||||
// events that are NOT present in @a nevts, but
|
||||
// may be among subscriptions and therefore potentially
|
||||
// have an associated notice.
|
||||
void removeExcessEvents(Wait& wait, int nevts)
|
||||
{
|
||||
// Update the event notice, should it exist
|
||||
// If the watch points to a null notice, there's simply
|
||||
// no notice there, so nothing to update or prospectively
|
||||
// remove - but may be something to add.
|
||||
if (wait.notit == nullNotice())
|
||||
return;
|
||||
|
||||
// `events` contains bits to be cleared.
|
||||
// 1. If there is no notice event, do nothing - clear already.
|
||||
// 2. If there is a notice event, update by clearing the bits
|
||||
// 2.1. If this made resulting state to be 0, also remove the notice.
|
||||
|
||||
const int newstate = wait.notit->events & nevts;
|
||||
if (newstate)
|
||||
{
|
||||
wait.notit->events = newstate;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the new state is full 0 (no events),
|
||||
// then remove the corresponding notice object
|
||||
removeExistingNotices(wait);
|
||||
}
|
||||
}
|
||||
|
||||
bool checkEdge(enotice_t::iterator i)
|
||||
{
|
||||
// This function should check if this event was subscribed
|
||||
// as edge-triggered, and if so, clear the event from the notice.
|
||||
// Update events and check edge mode at the subscriber
|
||||
i->events &= ~i->parent->edgeOnly();
|
||||
if(!i->events)
|
||||
{
|
||||
removeExistingNotices(*i->parent);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class CEPoll
|
||||
{
|
||||
friend class CUDT;
|
||||
friend class CRendezvousQueue;
|
||||
|
||||
public:
|
||||
CEPoll();
|
||||
~CEPoll();
|
||||
|
||||
public: // for CUDTUnited API
|
||||
|
||||
/// create a new EPoll.
|
||||
/// @return new EPoll ID if success, otherwise an error number.
|
||||
|
||||
int create();
|
||||
|
||||
/// add a UDT socket to an EPoll.
|
||||
/// @param [in] eid EPoll ID.
|
||||
/// @param [in] u UDT Socket ID.
|
||||
/// @param [in] events events to watch.
|
||||
/// @return 0 if success, otherwise an error number.
|
||||
|
||||
int add_usock(const int eid, const SRTSOCKET& u, const int* events = NULL) { return update_usock(eid, u, events); }
|
||||
|
||||
/// add a system socket to an EPoll.
|
||||
/// @param [in] eid EPoll ID.
|
||||
/// @param [in] s system Socket ID.
|
||||
/// @param [in] events events to watch.
|
||||
/// @return 0 if success, otherwise an error number.
|
||||
|
||||
int add_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL);
|
||||
|
||||
/// remove a UDT socket event from an EPoll; socket will be removed if no events to watch.
|
||||
/// @param [in] eid EPoll ID.
|
||||
/// @param [in] u UDT socket ID.
|
||||
/// @return 0 if success, otherwise an error number.
|
||||
|
||||
int remove_usock(const int eid, const SRTSOCKET& u) { static const int Null(0); return update_usock(eid, u, &Null);}
|
||||
|
||||
/// remove a system socket event from an EPoll; socket will be removed if no events to watch.
|
||||
/// @param [in] eid EPoll ID.
|
||||
/// @param [in] s system socket ID.
|
||||
/// @return 0 if success, otherwise an error number.
|
||||
|
||||
int remove_ssock(const int eid, const SYSSOCKET& s);
|
||||
/// update a UDT socket events from an EPoll.
|
||||
/// @param [in] eid EPoll ID.
|
||||
/// @param [in] u UDT socket ID.
|
||||
/// @param [in] events events to watch.
|
||||
/// @return 0 if success, otherwise an error number.
|
||||
|
||||
int update_usock(const int eid, const SRTSOCKET& u, const int* events);
|
||||
|
||||
/// update a system socket events from an EPoll.
|
||||
/// @param [in] eid EPoll ID.
|
||||
/// @param [in] u UDT socket ID.
|
||||
/// @param [in] events events to watch.
|
||||
/// @return 0 if success, otherwise an error number.
|
||||
|
||||
int update_ssock(const int eid, const SYSSOCKET& s, const int* events = NULL);
|
||||
|
||||
/// wait for EPoll events or timeout.
|
||||
/// @param [in] eid EPoll ID.
|
||||
/// @param [out] readfds UDT sockets available for reading.
|
||||
/// @param [out] writefds UDT sockets available for writing.
|
||||
/// @param [in] msTimeOut timeout threshold, in milliseconds.
|
||||
/// @param [out] lrfds system file descriptors for reading.
|
||||
/// @param [out] lwfds system file descriptors for writing.
|
||||
/// @return number of sockets available for IO.
|
||||
|
||||
int wait(const int eid, std::set<SRTSOCKET>* readfds, std::set<SRTSOCKET>* writefds, int64_t msTimeOut, std::set<SYSSOCKET>* lrfds, std::set<SYSSOCKET>* lwfds);
|
||||
|
||||
/// wait for EPoll events or timeout optimized with explicit EPOLL_ERR event and the edge mode option.
|
||||
/// @param [in] eid EPoll ID.
|
||||
/// @param [out] fdsSet array of user socket events (SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR).
|
||||
/// @param [int] fdsSize of fds array
|
||||
/// @param [in] msTimeOut timeout threshold, in milliseconds.
|
||||
/// @return total of available events in the epoll system (can be greater than fdsSize)
|
||||
|
||||
int uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut);
|
||||
|
||||
/// close and release an EPoll.
|
||||
/// @param [in] eid EPoll ID.
|
||||
/// @return 0 if success, otherwise an error number.
|
||||
|
||||
int release(const int eid);
|
||||
|
||||
public: // for CUDT to acknowledge IO status
|
||||
|
||||
/// Update events available for a UDT socket.
|
||||
/// @param [in] uid UDT socket ID.
|
||||
/// @param [in] eids EPoll IDs to be set
|
||||
/// @param [in] events Combination of events to update
|
||||
/// @param [in] enable true -> enable, otherwise disable
|
||||
/// @return 0 if success, otherwise an error number
|
||||
|
||||
int update_events(const SRTSOCKET& uid, std::set<int>& eids, int events, bool enable);
|
||||
|
||||
int setflags(const int eid, int32_t flags);
|
||||
|
||||
private:
|
||||
int m_iIDSeed; // seed to generate a new ID
|
||||
pthread_mutex_t m_SeedLock;
|
||||
|
||||
std::map<int, CEPollDesc> m_mPolls; // all epolls
|
||||
pthread_mutex_t m_EPollLock;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
2253
trunk/3rdparty/srt-1-fit/srtcore/fec.cpp
vendored
Normal file
2253
trunk/3rdparty/srt-1-fit/srtcore/fec.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
248
trunk/3rdparty/srt-1-fit/srtcore/fec.h
vendored
Normal file
248
trunk/3rdparty/srt-1-fit/srtcore/fec.h
vendored
Normal file
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2019 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef INC__SRT_FEC_H
|
||||
#define INC__SRT_FEC_H
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
#include "packetfilter_api.h"
|
||||
|
||||
class FECFilterBuiltin: public SrtPacketFilterBase
|
||||
{
|
||||
SrtFilterConfig cfg;
|
||||
size_t m_number_cols;
|
||||
size_t m_number_rows;
|
||||
|
||||
// Configuration
|
||||
SRT_ARQLevel m_fallback_level;
|
||||
bool m_cols_only;
|
||||
bool m_arrangement_staircase;
|
||||
|
||||
public:
|
||||
|
||||
size_t numberCols() const { return m_number_cols; }
|
||||
size_t numberRows() const { return m_number_rows; }
|
||||
|
||||
size_t sizeCol() const { return m_number_rows; }
|
||||
size_t sizeRow() const { return m_number_cols; }
|
||||
|
||||
struct Group
|
||||
{
|
||||
int32_t base; //< Sequence of the first packet in the group
|
||||
size_t step; //< by how many packets the sequence should increase to get the next packet
|
||||
size_t drop; //< by how much the sequence should increase to get to the next series
|
||||
size_t collected; //< how many packets were taken to collect the clip
|
||||
|
||||
Group(): base(CSeqNo::m_iMaxSeqNo), step(0), drop(0), collected(0)
|
||||
{
|
||||
}
|
||||
|
||||
uint16_t length_clip;
|
||||
uint8_t flag_clip;
|
||||
uint32_t timestamp_clip;
|
||||
std::vector<char> payload_clip;
|
||||
|
||||
// This is mutable because it's an intermediate buffer for
|
||||
// the purpose of output.
|
||||
//mutable vector<char> output_buffer;
|
||||
|
||||
enum Type
|
||||
{
|
||||
HORIZ, // Horizontal, recursive
|
||||
VERT, // Vertical, recursive
|
||||
|
||||
// NOTE: HORIZ/VERT are defined as 0/1 so that not-inversion
|
||||
// can flip between them.
|
||||
SINGLE // Horizontal-only with no recursion
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
struct RcvGroup: Group
|
||||
{
|
||||
bool fec;
|
||||
bool dismissed;
|
||||
RcvGroup(): fec(false), dismissed(false) {}
|
||||
|
||||
#if ENABLE_HEAVY_LOGGING
|
||||
std::string DisplayStats()
|
||||
{
|
||||
if (base == CSeqNo::m_iMaxSeqNo)
|
||||
return "UNINITIALIZED!!!";
|
||||
|
||||
std::ostringstream os;
|
||||
os << "base=" << base << " step=" << step << " drop=" << drop << " collected=" << collected
|
||||
<< " " << (fec ? "+" : "-") << "FEC " << (dismissed ? "DISMISSED" : "active");
|
||||
return os.str();
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
// Row Groups: every item represents a single row group and collects clips for one row.
|
||||
// Col Groups: every item represents a signel column group and collect clips for packets represented in one column
|
||||
|
||||
struct Send
|
||||
{
|
||||
// We need only ONE horizontal group. Simply after the group
|
||||
// is closed (last packet supplied), and the FEC packet extracted,
|
||||
// the group is no longer in use.
|
||||
Group row;
|
||||
std::vector<Group> cols;
|
||||
} snd;
|
||||
|
||||
struct Receive
|
||||
{
|
||||
SRTSOCKET id;
|
||||
bool order_required;
|
||||
|
||||
Receive(std::vector<SrtPacket>& provided): id(SRT_INVALID_SOCK), order_required(false), rebuilt(provided)
|
||||
{
|
||||
}
|
||||
|
||||
// In reception we need to keep as many horizontal groups as required
|
||||
// for possible later tracking. A horizontal group should be dismissed
|
||||
// when the size of this container exceeds the `m_number_rows` (size of the column).
|
||||
//
|
||||
// The 'std::deque' type is used here for a trial implementation. A desired solution
|
||||
// would be a kind of a ring buffer where new groups are added and old (exceeding
|
||||
// the size) automatically dismissed.
|
||||
std::deque<RcvGroup> rowq;
|
||||
|
||||
// Base index at the oldest column platform determines
|
||||
// the base index of the queue. Meaning, first you need
|
||||
// to determnine the column index, where the index 0 is
|
||||
// the fistmost element of this queue. After determining
|
||||
// the column index, there must be also a second factor
|
||||
// deteremined - which column series it is. So, this can
|
||||
// start by extracting the base sequence of the element
|
||||
// at the index column. This is the series 0. Now, the
|
||||
// distance between these two sequences, divided by
|
||||
// rowsize*colsize should return %index-in-column,
|
||||
// /number-series. The latter multiplied by the row size
|
||||
// is the offset between the firstmost column and the
|
||||
// searched column.
|
||||
std::deque<RcvGroup> colq;
|
||||
|
||||
// This keeps the value of "packet received or not".
|
||||
// The sequence number of the first cell is rowq[0].base.
|
||||
// When dropping a row,
|
||||
// - the firstmost element of rowq is removed
|
||||
// - the length of one row is removed from this std::vector
|
||||
int32_t cell_base;
|
||||
std::deque<bool> cells;
|
||||
|
||||
// Note this function will automatically extend the container
|
||||
// with empty cells if the index exceeds the size, HOWEVER
|
||||
// the caller must make sure that this index isn't any "crazy",
|
||||
// that is, it fits somehow in reasonable ranges.
|
||||
bool CellAt(size_t index)
|
||||
{
|
||||
if (index >= cells.size())
|
||||
{
|
||||
// Cells not prepared for this sequence yet,
|
||||
// so extend in advance.
|
||||
cells.resize(index+1, false);
|
||||
return false; // It wasn't marked, anyway.
|
||||
}
|
||||
|
||||
return cells[index];
|
||||
}
|
||||
|
||||
typedef SrtPacket PrivPacket;
|
||||
std::vector<PrivPacket>& rebuilt;
|
||||
} rcv;
|
||||
|
||||
void ConfigureGroup(Group& g, int32_t seqno, size_t gstep, size_t drop);
|
||||
template <class Container>
|
||||
void ConfigureColumns(Container& which, int32_t isn);
|
||||
|
||||
void ResetGroup(Group& g);
|
||||
|
||||
// Universal
|
||||
void ClipData(Group& g, uint16_t length_net, uint8_t kflg,
|
||||
uint32_t timestamp_hw, const char* payload, size_t payload_size);
|
||||
void ClipPacket(Group& g, const CPacket& pkt);
|
||||
|
||||
// Sending
|
||||
bool CheckGroupClose(Group& g, size_t pos, size_t size);
|
||||
void PackControl(const Group& g, signed char groupix, SrtPacket& pkt, int32_t seqno);
|
||||
|
||||
// Receiving
|
||||
void CheckLargeDrop(int32_t seqno);
|
||||
int ExtendRows(int rowx);
|
||||
int ExtendColumns(int colgx);
|
||||
void MarkCellReceived(int32_t seq);
|
||||
bool HangHorizontal(const CPacket& pkt, bool fec_ctl, loss_seqs_t& irrecover);
|
||||
bool HangVertical(const CPacket& pkt, signed char fec_colx, loss_seqs_t& irrecover);
|
||||
void ClipControlPacket(Group& g, const CPacket& pkt);
|
||||
void ClipRebuiltPacket(Group& g, Receive::PrivPacket& pkt);
|
||||
void RcvRebuild(Group& g, int32_t seqno, Group::Type tp);
|
||||
int32_t RcvGetLossSeqHoriz(Group& g);
|
||||
int32_t RcvGetLossSeqVert(Group& g);
|
||||
|
||||
static void TranslateLossRecords(const std::set<int32_t>& loss, loss_seqs_t& irrecover);
|
||||
void RcvCheckDismissColumn(int32_t seqno, int colgx, loss_seqs_t& irrecover);
|
||||
int RcvGetRowGroupIndex(int32_t seq);
|
||||
int RcvGetColumnGroupIndex(int32_t seq);
|
||||
void CollectIrrecoverRow(RcvGroup& g, loss_seqs_t& irrecover) const;
|
||||
bool IsLost(int32_t seq) const;
|
||||
|
||||
public:
|
||||
|
||||
FECFilterBuiltin(const SrtFilterInitializer& init, std::vector<SrtPacket>& provided, const std::string& confstr);
|
||||
|
||||
// Sender side
|
||||
|
||||
// This function creates and stores the FEC control packet with
|
||||
// a prediction to be immediately sent. This is called in the function
|
||||
// that normally is prepared for extracting a data packet from the sender
|
||||
// buffer and send it over the channel.
|
||||
virtual bool packControlPacket(SrtPacket& r_packet, int32_t seq) ATR_OVERRIDE;
|
||||
|
||||
// This is called at the moment when the sender queue decided to pick up
|
||||
// a new packet from the scheduled packets. This should be then used to
|
||||
// continue filling the group, possibly followed by final calculating the
|
||||
// FEC control packet ready to send.
|
||||
virtual void feedSource(CPacket& r_packet) ATR_OVERRIDE;
|
||||
|
||||
// Receiver side
|
||||
|
||||
// This function is called at the moment when a new data packet has
|
||||
// arrived (no matter if subsequent or recovered). The 'state' value
|
||||
// defines the configured level of loss state required to send the
|
||||
// loss report.
|
||||
virtual bool receive(const CPacket& pkt, loss_seqs_t& loss_seqs) ATR_OVERRIDE;
|
||||
|
||||
// Configuration
|
||||
|
||||
// This is the size that is needed extra by packets operated by this corrector.
|
||||
// It should be subtracted from a current maximum value for SRTO_PAYLOADSIZE
|
||||
|
||||
// The default FEC uses extra space only for FEC/CTL packet.
|
||||
// The timestamp clip is placed in the timestamp field in the header.
|
||||
// The payload contains:
|
||||
// - the length clip
|
||||
// - the flag spec
|
||||
// - the payload clip
|
||||
// The payload clip takes simply the current length of SRTO_PAYLOADSIZE.
|
||||
// So extra 4 bytes are needed, 2 for flags, 2 for length clip.
|
||||
static const size_t EXTRA_SIZE = 4;
|
||||
|
||||
virtual SRT_ARQLevel arqLevel() ATR_OVERRIDE { return m_fallback_level; }
|
||||
};
|
||||
|
||||
#endif
|
57
trunk/3rdparty/srt-1-fit/srtcore/filelist.maf
vendored
Normal file
57
trunk/3rdparty/srt-1-fit/srtcore/filelist.maf
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
|
||||
|
||||
SOURCES
|
||||
api.cpp
|
||||
buffer.cpp
|
||||
cache.cpp
|
||||
channel.cpp
|
||||
common.cpp
|
||||
core.cpp
|
||||
crypto.cpp
|
||||
epoll.cpp
|
||||
fec.cpp
|
||||
handshake.cpp
|
||||
list.cpp
|
||||
md5.cpp
|
||||
packet.cpp
|
||||
packetfilter.cpp
|
||||
queue.cpp
|
||||
congctl.cpp
|
||||
srt_c_api.cpp
|
||||
window.cpp
|
||||
srt_compat.c
|
||||
|
||||
PUBLIC HEADERS
|
||||
srt.h
|
||||
logging_api.h
|
||||
|
||||
PROTECTED HEADERS
|
||||
platform_sys.h
|
||||
udt.h
|
||||
srt4udt.h
|
||||
|
||||
PRIVATE HEADERS
|
||||
api.h
|
||||
buffer.h
|
||||
cache.h
|
||||
channel.h
|
||||
common.h
|
||||
core.h
|
||||
crypto.h
|
||||
epoll.h
|
||||
handshake.h
|
||||
list.h
|
||||
logging.h
|
||||
md5.h
|
||||
netinet_any.h
|
||||
packet.h
|
||||
queue.h
|
||||
congctl.h
|
||||
srt4udt.h
|
||||
srt_compat.h
|
||||
threadname.h
|
||||
utilities.h
|
||||
window.h
|
||||
|
||||
SOURCES WIN32 SHARED
|
||||
srt_shared.rc
|
260
trunk/3rdparty/srt-1-fit/srtcore/handshake.cpp
vendored
Normal file
260
trunk/3rdparty/srt-1-fit/srtcore/handshake.cpp
vendored
Normal file
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
|
||||
#include "udt.h"
|
||||
#include "core.h"
|
||||
#include "handshake.h"
|
||||
#include "utilities.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
CHandShake::CHandShake():
|
||||
m_iVersion(0),
|
||||
m_iType(0), // Universal: UDT_UNDEFINED or no flags
|
||||
m_iISN(0),
|
||||
m_iMSS(0),
|
||||
m_iFlightFlagSize(0),
|
||||
m_iReqType(URQ_WAVEAHAND),
|
||||
m_iID(0),
|
||||
m_iCookie(0),
|
||||
m_extension(false)
|
||||
{
|
||||
for (int i = 0; i < 4; ++ i)
|
||||
m_piPeerIP[i] = 0;
|
||||
}
|
||||
|
||||
int CHandShake::store_to(char* buf, ref_t<size_t> r_size)
|
||||
{
|
||||
size_t& size = *r_size;
|
||||
if (size < m_iContentSize)
|
||||
return -1;
|
||||
|
||||
int32_t* p = reinterpret_cast<int32_t*>(buf);
|
||||
*p++ = m_iVersion;
|
||||
*p++ = m_iType;
|
||||
*p++ = m_iISN;
|
||||
*p++ = m_iMSS;
|
||||
*p++ = m_iFlightFlagSize;
|
||||
*p++ = int32_t(m_iReqType);
|
||||
*p++ = m_iID;
|
||||
*p++ = m_iCookie;
|
||||
for (int i = 0; i < 4; ++ i)
|
||||
*p++ = m_piPeerIP[i];
|
||||
|
||||
size = m_iContentSize;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CHandShake::load_from(const char* buf, size_t size)
|
||||
{
|
||||
if (size < m_iContentSize)
|
||||
return -1;
|
||||
|
||||
const int32_t* p = reinterpret_cast<const int32_t*>(buf);
|
||||
|
||||
m_iVersion = *p++;
|
||||
m_iType = *p++;
|
||||
m_iISN = *p++;
|
||||
m_iMSS = *p++;
|
||||
m_iFlightFlagSize = *p++;
|
||||
m_iReqType = UDTRequestType(*p++);
|
||||
m_iID = *p++;
|
||||
m_iCookie = *p++;
|
||||
for (int i = 0; i < 4; ++ i)
|
||||
m_piPeerIP[i] = *p++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_LOGGING
|
||||
|
||||
const char* srt_rejectreason_name [] = {
|
||||
"UNKNOWN",
|
||||
"SYSTEM",
|
||||
"PEER",
|
||||
"RESOURCE",
|
||||
"ROGUE",
|
||||
"BACKLOG",
|
||||
"IPE",
|
||||
"CLOSE",
|
||||
"VERSION",
|
||||
"RDVCOOKIE",
|
||||
"BADSECRET",
|
||||
"UNSECURE",
|
||||
"MESSAGEAPI",
|
||||
"CONGESTION",
|
||||
"FILTER",
|
||||
};
|
||||
|
||||
std::string RequestTypeStr(UDTRequestType rq)
|
||||
{
|
||||
if (rq >= URQ_FAILURE_TYPES)
|
||||
{
|
||||
SRT_REJECT_REASON rej = RejectReasonForURQ(rq);
|
||||
int id = rej;
|
||||
return std::string("ERROR:") + srt_rejectreason_name[id];
|
||||
}
|
||||
|
||||
switch ( rq )
|
||||
{
|
||||
case URQ_INDUCTION: return "induction";
|
||||
case URQ_WAVEAHAND: return "waveahand";
|
||||
case URQ_CONCLUSION: return "conclusion";
|
||||
case URQ_AGREEMENT: return "agreement";
|
||||
|
||||
default: return "INVALID";
|
||||
}
|
||||
}
|
||||
|
||||
string CHandShake::RdvStateStr(CHandShake::RendezvousState s)
|
||||
{
|
||||
switch (s)
|
||||
{
|
||||
case RDV_WAVING: return "waving";
|
||||
case RDV_ATTENTION: return "attention";
|
||||
case RDV_FINE: return "fine";
|
||||
case RDV_INITIATED: return "initiated";
|
||||
case RDV_CONNECTED: return "connected";
|
||||
default: ;
|
||||
}
|
||||
|
||||
return "invalid";
|
||||
}
|
||||
#endif
|
||||
|
||||
string CHandShake::show()
|
||||
{
|
||||
ostringstream so;
|
||||
|
||||
so << "version=" << m_iVersion << " type=" << hex << m_iType << dec
|
||||
<< " ISN=" << m_iISN << " MSS=" << m_iMSS << " FLW=" << m_iFlightFlagSize
|
||||
<< " reqtype=" << RequestTypeStr(m_iReqType) << " srcID=" << m_iID
|
||||
<< " cookie=" << hex << m_iCookie << dec
|
||||
<< " srcIP=";
|
||||
|
||||
const unsigned char* p = (const unsigned char*)m_piPeerIP;
|
||||
const unsigned char* pe = p + 4 * (sizeof(uint32_t));
|
||||
|
||||
copy(p, pe, ostream_iterator<unsigned>(so, "."));
|
||||
|
||||
// XXX HS version symbols should be probably declared inside
|
||||
// CHandShake, not CUDT.
|
||||
if ( m_iVersion > CUDT::HS_VERSION_UDT4 )
|
||||
{
|
||||
so << "EXT: ";
|
||||
if (m_iType == 0) // no flags at all
|
||||
so << "none";
|
||||
else
|
||||
so << ExtensionFlagStr(m_iType);
|
||||
}
|
||||
|
||||
return so.str();
|
||||
}
|
||||
|
||||
string CHandShake::ExtensionFlagStr(int32_t fl)
|
||||
{
|
||||
std::ostringstream out;
|
||||
if ( fl & HS_EXT_HSREQ )
|
||||
out << " hsx";
|
||||
if ( fl & HS_EXT_KMREQ )
|
||||
out << " kmx";
|
||||
if ( fl & HS_EXT_CONFIG )
|
||||
out << " config";
|
||||
|
||||
int kl = SrtHSRequest::SRT_HSTYPE_ENCFLAGS::unwrap(fl) << 6;
|
||||
if (kl != 0)
|
||||
{
|
||||
out << " AES-" << kl;
|
||||
}
|
||||
else
|
||||
{
|
||||
out << " no-pbklen";
|
||||
}
|
||||
|
||||
return out.str();
|
||||
}
|
||||
|
||||
|
||||
// XXX This code isn't currently used. Left here because it can
|
||||
// be used in future, should any refactoring for the "manual word placement"
|
||||
// code be done.
|
||||
bool SrtHSRequest::serialize(char* buf, size_t size) const
|
||||
{
|
||||
if (size < SRT_HS_SIZE)
|
||||
return false;
|
||||
|
||||
int32_t* p = reinterpret_cast<int32_t*>(buf);
|
||||
|
||||
*p++ = m_iSrtVersion;
|
||||
*p++ = m_iSrtFlags;
|
||||
*p++ = m_iSrtTsbpd;
|
||||
*p++ = 0; // SURPRISE! Seriously, use (something) if this "reserved" is going to be used for something.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SrtHSRequest::deserialize(const char* buf, size_t size)
|
||||
{
|
||||
m_iSrtVersion = 0; // just to let users recognize if it succeeded or not.
|
||||
|
||||
if (size < SRT_HS_SIZE)
|
||||
return false;
|
||||
|
||||
const int32_t* p = reinterpret_cast<const int32_t*>(buf);
|
||||
|
||||
m_iSrtVersion = (*p++);
|
||||
m_iSrtFlags = (*p++);
|
||||
m_iSrtTsbpd = (*p++);
|
||||
m_iSrtReserved = (*p++);
|
||||
return true;
|
||||
}
|
342
trunk/3rdparty/srt-1-fit/srtcore/handshake.h
vendored
Normal file
342
trunk/3rdparty/srt-1-fit/srtcore/handshake.h
vendored
Normal file
|
@ -0,0 +1,342 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef INC__HANDSHAKE_H
|
||||
#define INC__HANDSHAKE_H
|
||||
|
||||
#include "crypto.h"
|
||||
#include "utilities.h"
|
||||
|
||||
typedef Bits<31, 16> HS_CMDSPEC_CMD;
|
||||
typedef Bits<15, 0> HS_CMDSPEC_SIZE;
|
||||
|
||||
// NOTE: Some of these flags represent CAPABILITIES, that is,
|
||||
// as long as these flags are defined, they must be always set
|
||||
// (unless they are deprecated).
|
||||
enum SrtOptions
|
||||
{
|
||||
SRT_OPT_TSBPDSND = BIT(0), /* Timestamp-based Packet delivery real-time data sender */
|
||||
SRT_OPT_TSBPDRCV = BIT(1), /* Timestamp-based Packet delivery real-time data receiver */
|
||||
SRT_OPT_HAICRYPT = BIT(2), /* CAPABILITY: HaiCrypt AES-128/192/256-CTR */
|
||||
SRT_OPT_TLPKTDROP = BIT(3), /* Drop real-time data packets too late to be processed in time */
|
||||
SRT_OPT_NAKREPORT = BIT(4), /* Periodic NAK report */
|
||||
SRT_OPT_REXMITFLG = BIT(5), // CAPABILITY: One bit in payload packet msgno is "retransmitted" flag
|
||||
// (this flag can be reused for something else, when pre-1.2.0 versions are all abandoned)
|
||||
SRT_OPT_STREAM = BIT(6), // STREAM MODE (not MESSAGE mode)
|
||||
SRT_OPT_FILTERCAP = BIT(7), // CAPABILITY: Packet filter supported
|
||||
};
|
||||
|
||||
inline int SrtVersionCapabilities()
|
||||
{
|
||||
// NOTE: SRT_OPT_REXMITFLG is not included here because
|
||||
// SRT is prepared to handle also peers that don't have this
|
||||
// capability, so a listener responding to a peer that doesn't
|
||||
// support it should NOT set this flag.
|
||||
//
|
||||
// This state will remain until this backward compatibility is
|
||||
// decided to be broken, in which case this flag will be always
|
||||
// set, and clients that do not support this capability will be
|
||||
// rejected.
|
||||
return SRT_OPT_HAICRYPT | SRT_OPT_FILTERCAP;
|
||||
}
|
||||
|
||||
|
||||
std::string SrtFlagString(int32_t flags);
|
||||
|
||||
const int SRT_CMD_REJECT = 0, // REJECT is only a symbol for return type
|
||||
SRT_CMD_HSREQ = 1,
|
||||
SRT_CMD_HSRSP = 2,
|
||||
SRT_CMD_KMREQ = 3,
|
||||
SRT_CMD_KMRSP = 4,
|
||||
SRT_CMD_SID = 5,
|
||||
SRT_CMD_CONGESTION = 6,
|
||||
SRT_CMD_FILTER = 7,
|
||||
SRT_CMD_NONE = -1; // for cases when {no pong for ping is required} | {no extension block found}
|
||||
|
||||
enum SrtDataStruct
|
||||
{
|
||||
SRT_HS_VERSION = 0,
|
||||
SRT_HS_FLAGS,
|
||||
SRT_HS_LATENCY,
|
||||
|
||||
// Keep it always last
|
||||
SRT_HS__SIZE
|
||||
};
|
||||
|
||||
// For HSv5 the lo and hi part is used for particular side's latency
|
||||
typedef Bits<31, 16> SRT_HS_LATENCY_RCV;
|
||||
typedef Bits<15, 0> SRT_HS_LATENCY_SND;
|
||||
// For HSv4 only the lower part is used.
|
||||
typedef Bits<15, 0> SRT_HS_LATENCY_LEG;
|
||||
|
||||
|
||||
// XXX These structures are currently unused. The code can be changed
|
||||
// so that these are used instead of manual tailoring of the messages.
|
||||
struct SrtHandshakeExtension
|
||||
{
|
||||
protected:
|
||||
|
||||
uint32_t m_SrtCommand; // Used only in extension
|
||||
|
||||
public:
|
||||
SrtHandshakeExtension(int cmd)
|
||||
{
|
||||
m_SrtCommand = cmd;
|
||||
}
|
||||
|
||||
void setCommand(int cmd)
|
||||
{
|
||||
m_SrtCommand = cmd;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct SrtHSRequest: public SrtHandshakeExtension
|
||||
{
|
||||
|
||||
typedef Bits<31, 16> SRT_HSTYPE_ENCFLAGS;
|
||||
typedef Bits<15, 0> SRT_HSTYPE_HSFLAGS;
|
||||
|
||||
// For translating PBKEYLEN into crypto flags
|
||||
// This value is 16, 24, 32; after cutting off
|
||||
// the leftmost 3 bits, it is 2, 3, 4.
|
||||
typedef Bits<5, 3> SRT_PBKEYLEN_BITS;
|
||||
|
||||
// This value fits ins SRT_HSTYPE_HSFLAGS.
|
||||
// .... HAIVISIOn
|
||||
static const int32_t SRT_MAGIC_CODE = 0x4A17;
|
||||
|
||||
static int32_t wrapFlags(bool withmagic, int crypto_keylen)
|
||||
{
|
||||
int32_t base = withmagic ? SRT_MAGIC_CODE : 0;
|
||||
return base | SRT_HSTYPE_ENCFLAGS::wrap( SRT_PBKEYLEN_BITS::unwrap(crypto_keylen) );
|
||||
}
|
||||
|
||||
private:
|
||||
friend class CHandShake;
|
||||
|
||||
static const size_t SRT_HS_SIZE = 4*sizeof(uint32_t); // 4 existing fields
|
||||
static const size_t SRT_EXT_HS_SIZE = 2*sizeof(uint32_t) + SRT_HS_SIZE; // SRT magic and SRT HS type, used only in UDT HS ext
|
||||
|
||||
typedef Bits<15, 0> SRT_TSBPD_DELAY;
|
||||
|
||||
uint32_t m_iSrtVersion;
|
||||
uint32_t m_iSrtFlags;
|
||||
uint32_t m_iSrtTsbpd;
|
||||
uint32_t m_iSrtReserved;
|
||||
|
||||
public:
|
||||
|
||||
SrtHSRequest(): SrtHandshakeExtension(SRT_CMD_HSREQ), m_iSrtVersion(), m_iSrtFlags(), m_iSrtTsbpd(), m_iSrtReserved() {}
|
||||
|
||||
void setVersion(uint32_t v) { m_iSrtVersion = v; }
|
||||
uint32_t version() const { return m_iSrtVersion; }
|
||||
|
||||
void setFlag(SrtOptions opt) { m_iSrtFlags |= uint32_t(opt); }
|
||||
void clearFlag(SrtOptions opt) { m_iSrtFlags &= ~opt; }
|
||||
uint32_t flags() const { return m_iSrtFlags; }
|
||||
|
||||
void setTsbPdDelay(uint16_t delay) { m_iSrtTsbpd |= SRT_TSBPD_DELAY::wrap(delay); }
|
||||
// Unknown what the 1-16 bits have to be used for.
|
||||
uint16_t tsbPdDelay() const
|
||||
{
|
||||
return SRT_TSBPD_DELAY::unwrap(m_iSrtTsbpd);
|
||||
}
|
||||
|
||||
size_t size() const { return SRT_EXT_HS_SIZE; }
|
||||
|
||||
bool serialize(char* p, size_t size) const;
|
||||
bool deserialize(const char* mem, size_t size);
|
||||
};
|
||||
|
||||
struct SrtKMRequest: public SrtHandshakeExtension
|
||||
{
|
||||
uint32_t m_iKmState;
|
||||
char m_aKey[1]; // dynamic size
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
enum UDTRequestType
|
||||
{
|
||||
URQ_INDUCTION_TYPES = 0, // XXX used to check in one place. Consdr rm.
|
||||
|
||||
URQ_INDUCTION = 1, // First part for client-server connection
|
||||
URQ_WAVEAHAND = 0, // First part for rendezvous connection
|
||||
|
||||
URQ_CONCLUSION = -1, // Second part of handshake negotiation
|
||||
URQ_AGREEMENT = -2, // Extra (last) step for rendezvous only
|
||||
URQ_DONE = -3, // Special value used only in state-switching, to state that nothing should be sent in response
|
||||
|
||||
// Note: the client-server connection uses:
|
||||
// --> INDUCTION (empty)
|
||||
// <-- INDUCTION (cookie)
|
||||
// --> CONCLUSION (cookie)
|
||||
// <-- CONCLUSION (ok)
|
||||
|
||||
// The rendezvous HSv4 (legacy):
|
||||
// --> WAVEAHAND (effective only if peer is also connecting)
|
||||
// <-- CONCLUSION (empty) (consider yourself connected upon reception)
|
||||
// --> AGREEMENT (sent as a response for conclusion, requires no response)
|
||||
|
||||
// The rendezvous HSv5 (using SRT extensions):
|
||||
// --> WAVEAHAND (with cookie)
|
||||
// --- (selecting INITIATOR/RESPONDER by cookie contest - comparing one another's cookie)
|
||||
// <-- CONCLUSION (without extensions, if RESPONDER, with extensions, if INITIATOR)
|
||||
// --> CONCLUSION (with response extensions, if RESPONDER)
|
||||
// <-- AGREEMENT (sent exclusively by INITIATOR upon reception of CONCLUSIOn with response extensions)
|
||||
|
||||
// Errors reported by the peer, also used as useless error codes
|
||||
// in handshake processing functions.
|
||||
URQ_FAILURE_TYPES = 1000
|
||||
|
||||
// NOTE: codes above 1000 are reserved for failure codes for
|
||||
// rejection reason, as per `SRT_REJECT_REASON` enum. DO NOT
|
||||
// add any new values here.
|
||||
};
|
||||
|
||||
inline UDTRequestType URQFailure(SRT_REJECT_REASON reason)
|
||||
{
|
||||
return UDTRequestType(URQ_FAILURE_TYPES + int(reason));
|
||||
}
|
||||
|
||||
inline SRT_REJECT_REASON RejectReasonForURQ(UDTRequestType req)
|
||||
{
|
||||
if (req < URQ_FAILURE_TYPES || req - URQ_FAILURE_TYPES >= SRT_REJ__SIZE)
|
||||
return SRT_REJ_UNKNOWN;
|
||||
return SRT_REJECT_REASON(req - URQ_FAILURE_TYPES);
|
||||
}
|
||||
|
||||
// DEPRECATED values. Use URQFailure(SRT_REJECT_REASON).
|
||||
const UDTRequestType URQ_ERROR_REJECT SRT_ATR_DEPRECATED = (UDTRequestType)1002; // == 1000 + SRT_REJ_PEER
|
||||
const UDTRequestType URQ_ERROR_INVALID SRT_ATR_DEPRECATED = (UDTRequestType)1004; // == 1000 + SRT_REJ_ROGUE
|
||||
|
||||
// XXX Change all uses of that field to UDTRequestType when possible
|
||||
#if ENABLE_LOGGING
|
||||
std::string RequestTypeStr(UDTRequestType);
|
||||
#else
|
||||
inline std::string RequestTypeStr(UDTRequestType) { return ""; }
|
||||
#endif
|
||||
|
||||
|
||||
class CHandShake
|
||||
{
|
||||
public:
|
||||
CHandShake();
|
||||
|
||||
int store_to(char* buf, ref_t<size_t> size);
|
||||
int load_from(const char* buf, size_t size);
|
||||
|
||||
public:
|
||||
// This is the size of SERIALIZED handshake.
|
||||
// Might be defined as simply sizeof(CHandShake), but the
|
||||
// enum values would have to be forced as int32_t, which is only
|
||||
// available in C++11. Theoretically they are all 32-bit, but
|
||||
// such a statement is not reliable and not portable.
|
||||
static const size_t m_iContentSize = 48; // Size of hand shake data
|
||||
|
||||
// Extension flags
|
||||
|
||||
static const int32_t HS_EXT_HSREQ = BIT(0);
|
||||
static const int32_t HS_EXT_KMREQ = BIT(1);
|
||||
static const int32_t HS_EXT_CONFIG = BIT(2);
|
||||
|
||||
static std::string ExtensionFlagStr(int32_t fl);
|
||||
|
||||
// Applicable only when m_iVersion == HS_VERSION_SRT1
|
||||
int32_t flags() { return m_iType; }
|
||||
|
||||
public:
|
||||
int32_t m_iVersion; // UDT version (HS_VERSION_* symbols)
|
||||
int32_t m_iType; // UDT4: socket type (only UDT_DGRAM is valid); SRT1: extension flags
|
||||
int32_t m_iISN; // random initial sequence number
|
||||
int32_t m_iMSS; // maximum segment size
|
||||
int32_t m_iFlightFlagSize; // flow control window size
|
||||
UDTRequestType m_iReqType; // handshake stage
|
||||
int32_t m_iID; // socket ID
|
||||
int32_t m_iCookie; // cookie
|
||||
uint32_t m_piPeerIP[4]; // The IP address that the peer's UDP port is bound to
|
||||
|
||||
bool m_extension;
|
||||
|
||||
std::string show();
|
||||
|
||||
// The rendezvous state machine used in HSv5 only (in HSv4 everything is happening the old way).
|
||||
//
|
||||
// The WAVING state is the very initial state of the rendezvous connection and restored after the
|
||||
// connection is closed.
|
||||
// The ATTENTION and FINE are two alternative states that are transited to from WAVING. The possible
|
||||
// situations are:
|
||||
// - "serial arrangement": one party transits to ATTENTION and the other party transits to FINE
|
||||
// - "parallel arrangement" both parties transit to ATTENTION
|
||||
//
|
||||
// Parallel arrangement is a "virtually impossible" case, in which both parties must send the first
|
||||
// URQ_WAVEAHAND message in a perfect time synchronization, when they are started at exactly the same
|
||||
// time, on machines with exactly the same performance and all things preceding the message sending
|
||||
// have taken perfectly identical amount of time. This isn't anyhow possible otherwise because if
|
||||
// the clients have started at different times, the one who started first sends a message and the
|
||||
// system of the receiver buffers this message even before the client binds the port for enough long
|
||||
// time so that it outlasts also the possible second, repeated waveahand.
|
||||
enum RendezvousState
|
||||
{
|
||||
RDV_INVALID, //< This socket wasn't prepared for rendezvous process. Reject any events.
|
||||
RDV_WAVING, //< Initial state for rendezvous. No contact seen from the peer.
|
||||
RDV_ATTENTION, //< When received URQ_WAVEAHAND. [WAVING]:URQ_WAVEAHAND --> [ATTENTION].
|
||||
RDV_FINE, //< When received URQ_CONCLUSION. [WAVING]:URQ_CONCLUSION --> [FINE].
|
||||
RDV_INITIATED, //< When received URQ_CONCLUSION+HSREQ extension in ATTENTION state.
|
||||
RDV_CONNECTED //< Final connected state. [ATTENTION]:URQ_CONCLUSION --> [CONNECTED] <-- [FINE]:URQ_AGREEMENT.
|
||||
};
|
||||
|
||||
#if ENABLE_LOGGING
|
||||
static std::string RdvStateStr(RendezvousState s);
|
||||
#else
|
||||
static std::string RdvStateStr(RendezvousState) { return ""; }
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
769
trunk/3rdparty/srt-1-fit/srtcore/list.cpp
vendored
Normal file
769
trunk/3rdparty/srt-1-fit/srtcore/list.cpp
vendored
Normal file
|
@ -0,0 +1,769 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Yunhong Gu, last updated 01/22/2011
|
||||
modified by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "list.h"
|
||||
#include "packet.h"
|
||||
|
||||
CSndLossList::CSndLossList(int size):
|
||||
m_caSeq(),
|
||||
m_iHead(-1),
|
||||
m_iLength(0),
|
||||
m_iSize(size),
|
||||
m_iLastInsertPos(-1),
|
||||
m_ListLock()
|
||||
{
|
||||
m_caSeq = new Seq[size];
|
||||
|
||||
// -1 means there is no data in the node
|
||||
for (int i = 0; i < size; ++ i)
|
||||
{
|
||||
m_caSeq[i].data1 = -1;
|
||||
m_caSeq[i].data2 = -1;
|
||||
}
|
||||
|
||||
// sender list needs mutex protection
|
||||
pthread_mutex_init(&m_ListLock, 0);
|
||||
}
|
||||
|
||||
CSndLossList::~CSndLossList()
|
||||
{
|
||||
delete [] m_caSeq;
|
||||
pthread_mutex_destroy(&m_ListLock);
|
||||
}
|
||||
|
||||
int CSndLossList::insert(int32_t seqno1, int32_t seqno2)
|
||||
{
|
||||
CGuard listguard(m_ListLock);
|
||||
|
||||
if (0 == m_iLength)
|
||||
{
|
||||
// insert data into an empty list
|
||||
|
||||
m_iHead = 0;
|
||||
m_caSeq[m_iHead].data1 = seqno1;
|
||||
if (seqno2 != seqno1)
|
||||
m_caSeq[m_iHead].data2 = seqno2;
|
||||
|
||||
m_caSeq[m_iHead].next = -1;
|
||||
m_iLastInsertPos = m_iHead;
|
||||
|
||||
m_iLength += CSeqNo::seqlen(seqno1, seqno2);
|
||||
|
||||
return m_iLength;
|
||||
}
|
||||
|
||||
// otherwise find the position where the data can be inserted
|
||||
int origlen = m_iLength;
|
||||
int offset = CSeqNo::seqoff(m_caSeq[m_iHead].data1, seqno1);
|
||||
int loc = (m_iHead + offset + m_iSize) % m_iSize;
|
||||
|
||||
if (offset < 0)
|
||||
{
|
||||
// Insert data prior to the head pointer
|
||||
|
||||
m_caSeq[loc].data1 = seqno1;
|
||||
if (seqno2 != seqno1)
|
||||
m_caSeq[loc].data2 = seqno2;
|
||||
|
||||
// new node becomes head
|
||||
m_caSeq[loc].next = m_iHead;
|
||||
m_iHead = loc;
|
||||
m_iLastInsertPos = loc;
|
||||
|
||||
m_iLength += CSeqNo::seqlen(seqno1, seqno2);
|
||||
}
|
||||
else if (offset > 0)
|
||||
{
|
||||
if (seqno1 == m_caSeq[loc].data1)
|
||||
{
|
||||
m_iLastInsertPos = loc;
|
||||
|
||||
// first seqno is equivlent, compare the second
|
||||
if (-1 == m_caSeq[loc].data2)
|
||||
{
|
||||
if (seqno2 != seqno1)
|
||||
{
|
||||
m_iLength += CSeqNo::seqlen(seqno1, seqno2) - 1;
|
||||
m_caSeq[loc].data2 = seqno2;
|
||||
}
|
||||
}
|
||||
else if (CSeqNo::seqcmp(seqno2, m_caSeq[loc].data2) > 0)
|
||||
{
|
||||
// new seq pair is longer than old pair, e.g., insert [3, 7] to [3, 5], becomes [3, 7]
|
||||
m_iLength += CSeqNo::seqlen(m_caSeq[loc].data2, seqno2) - 1;
|
||||
m_caSeq[loc].data2 = seqno2;
|
||||
}
|
||||
else
|
||||
// Do nothing if it is already there
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// searching the prior node
|
||||
int i;
|
||||
if ((-1 != m_iLastInsertPos) && (CSeqNo::seqcmp(m_caSeq[m_iLastInsertPos].data1, seqno1) < 0))
|
||||
i = m_iLastInsertPos;
|
||||
else
|
||||
i = m_iHead;
|
||||
|
||||
while ((-1 != m_caSeq[i].next) && (CSeqNo::seqcmp(m_caSeq[m_caSeq[i].next].data1, seqno1) < 0))
|
||||
i = m_caSeq[i].next;
|
||||
|
||||
if ((-1 == m_caSeq[i].data2) || (CSeqNo::seqcmp(m_caSeq[i].data2, seqno1) < 0))
|
||||
{
|
||||
m_iLastInsertPos = loc;
|
||||
|
||||
// no overlap, create new node
|
||||
m_caSeq[loc].data1 = seqno1;
|
||||
if (seqno2 != seqno1)
|
||||
m_caSeq[loc].data2 = seqno2;
|
||||
|
||||
m_caSeq[loc].next = m_caSeq[i].next;
|
||||
m_caSeq[i].next = loc;
|
||||
|
||||
m_iLength += CSeqNo::seqlen(seqno1, seqno2);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iLastInsertPos = i;
|
||||
|
||||
// overlap, coalesce with prior node, insert(3, 7) to [2, 5], ... becomes [2, 7]
|
||||
if (CSeqNo::seqcmp(m_caSeq[i].data2, seqno2) < 0)
|
||||
{
|
||||
m_iLength += CSeqNo::seqlen(m_caSeq[i].data2, seqno2) - 1;
|
||||
m_caSeq[i].data2 = seqno2;
|
||||
|
||||
loc = i;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iLastInsertPos = m_iHead;
|
||||
|
||||
// insert to head node
|
||||
if (seqno2 != seqno1)
|
||||
{
|
||||
if (-1 == m_caSeq[loc].data2)
|
||||
{
|
||||
m_iLength += CSeqNo::seqlen(seqno1, seqno2) - 1;
|
||||
m_caSeq[loc].data2 = seqno2;
|
||||
}
|
||||
else if (CSeqNo::seqcmp(seqno2, m_caSeq[loc].data2) > 0)
|
||||
{
|
||||
m_iLength += CSeqNo::seqlen(m_caSeq[loc].data2, seqno2) - 1;
|
||||
m_caSeq[loc].data2 = seqno2;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// coalesce with next node. E.g., [3, 7], ..., [6, 9] becomes [3, 9]
|
||||
while ((-1 != m_caSeq[loc].next) && (-1 != m_caSeq[loc].data2))
|
||||
{
|
||||
const int i = m_caSeq[loc].next;
|
||||
|
||||
if (CSeqNo::seqcmp(m_caSeq[i].data1, CSeqNo::incseq(m_caSeq[loc].data2)) > 0)
|
||||
break;
|
||||
|
||||
// coalesce if there is overlap
|
||||
if (-1 != m_caSeq[i].data2)
|
||||
{
|
||||
if (CSeqNo::seqcmp(m_caSeq[i].data2, m_caSeq[loc].data2) > 0)
|
||||
{
|
||||
if (CSeqNo::seqcmp(m_caSeq[loc].data2, m_caSeq[i].data1) >= 0)
|
||||
m_iLength -= CSeqNo::seqlen(m_caSeq[i].data1, m_caSeq[loc].data2);
|
||||
|
||||
m_caSeq[loc].data2 = m_caSeq[i].data2;
|
||||
}
|
||||
else
|
||||
m_iLength -= CSeqNo::seqlen(m_caSeq[i].data1, m_caSeq[i].data2);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_caSeq[i].data1 == CSeqNo::incseq(m_caSeq[loc].data2))
|
||||
m_caSeq[loc].data2 = m_caSeq[i].data1;
|
||||
else
|
||||
m_iLength--;
|
||||
}
|
||||
|
||||
m_caSeq[i].data1 = -1;
|
||||
m_caSeq[i].data2 = -1;
|
||||
m_caSeq[loc].next = m_caSeq[i].next;
|
||||
}
|
||||
|
||||
return m_iLength - origlen;
|
||||
}
|
||||
|
||||
void CSndLossList::remove(int32_t seqno)
|
||||
{
|
||||
CGuard listguard(m_ListLock);
|
||||
|
||||
if (0 == m_iLength)
|
||||
return;
|
||||
|
||||
// Remove all from the head pointer to a node with a larger seq. no. or the list is empty
|
||||
int offset = CSeqNo::seqoff(m_caSeq[m_iHead].data1, seqno);
|
||||
int loc = (m_iHead + offset + m_iSize) % m_iSize;
|
||||
|
||||
if (0 == offset)
|
||||
{
|
||||
// It is the head. Remove the head and point to the next node
|
||||
loc = (loc + 1) % m_iSize;
|
||||
|
||||
if (-1 == m_caSeq[m_iHead].data2)
|
||||
loc = m_caSeq[m_iHead].next;
|
||||
else
|
||||
{
|
||||
m_caSeq[loc].data1 = CSeqNo::incseq(seqno);
|
||||
if (CSeqNo::seqcmp(m_caSeq[m_iHead].data2, CSeqNo::incseq(seqno)) > 0)
|
||||
m_caSeq[loc].data2 = m_caSeq[m_iHead].data2;
|
||||
|
||||
m_caSeq[m_iHead].data2 = -1;
|
||||
|
||||
m_caSeq[loc].next = m_caSeq[m_iHead].next;
|
||||
}
|
||||
|
||||
m_caSeq[m_iHead].data1 = -1;
|
||||
|
||||
if (m_iLastInsertPos == m_iHead)
|
||||
m_iLastInsertPos = -1;
|
||||
|
||||
m_iHead = loc;
|
||||
|
||||
m_iLength --;
|
||||
}
|
||||
else if (offset > 0)
|
||||
{
|
||||
int h = m_iHead;
|
||||
|
||||
if (seqno == m_caSeq[loc].data1)
|
||||
{
|
||||
// target node is not empty, remove part/all of the seqno in the node.
|
||||
int temp = loc;
|
||||
loc = (loc + 1) % m_iSize;
|
||||
|
||||
if (-1 == m_caSeq[temp].data2)
|
||||
m_iHead = m_caSeq[temp].next;
|
||||
else
|
||||
{
|
||||
// remove part, e.g., [3, 7] becomes [], [4, 7] after remove(3)
|
||||
m_caSeq[loc].data1 = CSeqNo::incseq(seqno);
|
||||
if (CSeqNo::seqcmp(m_caSeq[temp].data2, m_caSeq[loc].data1) > 0)
|
||||
m_caSeq[loc].data2 = m_caSeq[temp].data2;
|
||||
m_iHead = loc;
|
||||
m_caSeq[loc].next = m_caSeq[temp].next;
|
||||
m_caSeq[temp].next = loc;
|
||||
m_caSeq[temp].data2 = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// target node is empty, check prior node
|
||||
int i = m_iHead;
|
||||
while ((-1 != m_caSeq[i].next) && (CSeqNo::seqcmp(m_caSeq[m_caSeq[i].next].data1, seqno) < 0))
|
||||
i = m_caSeq[i].next;
|
||||
|
||||
loc = (loc + 1) % m_iSize;
|
||||
|
||||
if (-1 == m_caSeq[i].data2)
|
||||
m_iHead = m_caSeq[i].next;
|
||||
else if (CSeqNo::seqcmp(m_caSeq[i].data2, seqno) > 0)
|
||||
{
|
||||
// remove part/all seqno in the prior node
|
||||
m_caSeq[loc].data1 = CSeqNo::incseq(seqno);
|
||||
if (CSeqNo::seqcmp(m_caSeq[i].data2, m_caSeq[loc].data1) > 0)
|
||||
m_caSeq[loc].data2 = m_caSeq[i].data2;
|
||||
|
||||
m_caSeq[i].data2 = seqno;
|
||||
|
||||
m_caSeq[loc].next = m_caSeq[i].next;
|
||||
m_caSeq[i].next = loc;
|
||||
|
||||
m_iHead = loc;
|
||||
}
|
||||
else
|
||||
m_iHead = m_caSeq[i].next;
|
||||
}
|
||||
|
||||
// Remove all nodes prior to the new head
|
||||
while (h != m_iHead)
|
||||
{
|
||||
if (m_caSeq[h].data2 != -1)
|
||||
{
|
||||
m_iLength -= CSeqNo::seqlen(m_caSeq[h].data1, m_caSeq[h].data2);
|
||||
m_caSeq[h].data2 = -1;
|
||||
}
|
||||
else
|
||||
m_iLength --;
|
||||
|
||||
m_caSeq[h].data1 = -1;
|
||||
|
||||
if (m_iLastInsertPos == h)
|
||||
m_iLastInsertPos = -1;
|
||||
|
||||
h = m_caSeq[h].next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int CSndLossList::getLossLength() const
|
||||
{
|
||||
CGuard listguard(m_ListLock);
|
||||
|
||||
return m_iLength;
|
||||
}
|
||||
|
||||
int32_t CSndLossList::popLostSeq()
|
||||
{
|
||||
CGuard listguard(m_ListLock);
|
||||
|
||||
if (0 == m_iLength)
|
||||
return -1;
|
||||
|
||||
if (m_iLastInsertPos == m_iHead)
|
||||
m_iLastInsertPos = -1;
|
||||
|
||||
// return the first loss seq. no.
|
||||
int32_t seqno = m_caSeq[m_iHead].data1;
|
||||
|
||||
// head moves to the next node
|
||||
if (-1 == m_caSeq[m_iHead].data2)
|
||||
{
|
||||
//[3, -1] becomes [], and head moves to next node in the list
|
||||
m_caSeq[m_iHead].data1 = -1;
|
||||
m_iHead = m_caSeq[m_iHead].next;
|
||||
}
|
||||
else
|
||||
{
|
||||
// shift to next node, e.g., [3, 7] becomes [], [4, 7]
|
||||
int loc = (m_iHead + 1) % m_iSize;
|
||||
|
||||
m_caSeq[loc].data1 = CSeqNo::incseq(seqno);
|
||||
if (CSeqNo::seqcmp(m_caSeq[m_iHead].data2, m_caSeq[loc].data1) > 0)
|
||||
m_caSeq[loc].data2 = m_caSeq[m_iHead].data2;
|
||||
|
||||
m_caSeq[m_iHead].data1 = -1;
|
||||
m_caSeq[m_iHead].data2 = -1;
|
||||
|
||||
m_caSeq[loc].next = m_caSeq[m_iHead].next;
|
||||
m_iHead = loc;
|
||||
}
|
||||
|
||||
m_iLength --;
|
||||
|
||||
return seqno;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CRcvLossList::CRcvLossList(int size):
|
||||
m_caSeq(),
|
||||
m_iHead(-1),
|
||||
m_iTail(-1),
|
||||
m_iLength(0),
|
||||
m_iSize(size)
|
||||
{
|
||||
m_caSeq = new Seq[m_iSize];
|
||||
|
||||
// -1 means there is no data in the node
|
||||
for (int i = 0; i < size; ++ i)
|
||||
{
|
||||
m_caSeq[i].data1 = -1;
|
||||
m_caSeq[i].data2 = -1;
|
||||
}
|
||||
}
|
||||
|
||||
CRcvLossList::~CRcvLossList()
|
||||
{
|
||||
delete [] m_caSeq;
|
||||
}
|
||||
|
||||
void CRcvLossList::insert(int32_t seqno1, int32_t seqno2)
|
||||
{
|
||||
// Data to be inserted must be larger than all those in the list
|
||||
// guaranteed by the UDT receiver
|
||||
|
||||
if (0 == m_iLength)
|
||||
{
|
||||
// insert data into an empty list
|
||||
m_iHead = 0;
|
||||
m_iTail = 0;
|
||||
m_caSeq[m_iHead].data1 = seqno1;
|
||||
if (seqno2 != seqno1)
|
||||
m_caSeq[m_iHead].data2 = seqno2;
|
||||
|
||||
m_caSeq[m_iHead].next = -1;
|
||||
m_caSeq[m_iHead].prior = -1;
|
||||
m_iLength += CSeqNo::seqlen(seqno1, seqno2);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise searching for the position where the node should be
|
||||
int offset = CSeqNo::seqoff(m_caSeq[m_iHead].data1, seqno1);
|
||||
int loc = (m_iHead + offset) % m_iSize;
|
||||
|
||||
if ((-1 != m_caSeq[m_iTail].data2) && (CSeqNo::incseq(m_caSeq[m_iTail].data2) == seqno1))
|
||||
{
|
||||
// coalesce with prior node, e.g., [2, 5], [6, 7] becomes [2, 7]
|
||||
loc = m_iTail;
|
||||
m_caSeq[loc].data2 = seqno2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// create new node
|
||||
m_caSeq[loc].data1 = seqno1;
|
||||
|
||||
if (seqno2 != seqno1)
|
||||
m_caSeq[loc].data2 = seqno2;
|
||||
|
||||
m_caSeq[m_iTail].next = loc;
|
||||
m_caSeq[loc].prior = m_iTail;
|
||||
m_caSeq[loc].next = -1;
|
||||
m_iTail = loc;
|
||||
}
|
||||
|
||||
m_iLength += CSeqNo::seqlen(seqno1, seqno2);
|
||||
}
|
||||
|
||||
bool CRcvLossList::remove(int32_t seqno)
|
||||
{
|
||||
if (0 == m_iLength)
|
||||
return false;
|
||||
|
||||
// locate the position of "seqno" in the list
|
||||
int offset = CSeqNo::seqoff(m_caSeq[m_iHead].data1, seqno);
|
||||
if (offset < 0)
|
||||
return false;
|
||||
|
||||
int loc = (m_iHead + offset) % m_iSize;
|
||||
|
||||
if (seqno == m_caSeq[loc].data1)
|
||||
{
|
||||
// This is a seq. no. that starts the loss sequence
|
||||
|
||||
if (-1 == m_caSeq[loc].data2)
|
||||
{
|
||||
// there is only 1 loss in the sequence, delete it from the node
|
||||
if (m_iHead == loc)
|
||||
{
|
||||
m_iHead = m_caSeq[m_iHead].next;
|
||||
if (-1 != m_iHead)
|
||||
m_caSeq[m_iHead].prior = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_caSeq[m_caSeq[loc].prior].next = m_caSeq[loc].next;
|
||||
if (-1 != m_caSeq[loc].next)
|
||||
m_caSeq[m_caSeq[loc].next].prior = m_caSeq[loc].prior;
|
||||
else
|
||||
m_iTail = m_caSeq[loc].prior;
|
||||
}
|
||||
|
||||
m_caSeq[loc].data1 = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// there are more than 1 loss in the sequence
|
||||
// move the node to the next and update the starter as the next loss inSeqNo(seqno)
|
||||
|
||||
// find next node
|
||||
int i = (loc + 1) % m_iSize;
|
||||
|
||||
// remove the "seqno" and change the starter as next seq. no.
|
||||
m_caSeq[i].data1 = CSeqNo::incseq(m_caSeq[loc].data1);
|
||||
|
||||
// process the sequence end
|
||||
if (CSeqNo::seqcmp(m_caSeq[loc].data2, CSeqNo::incseq(m_caSeq[loc].data1)) > 0)
|
||||
m_caSeq[i].data2 = m_caSeq[loc].data2;
|
||||
|
||||
// remove the current node
|
||||
m_caSeq[loc].data1 = -1;
|
||||
m_caSeq[loc].data2 = -1;
|
||||
|
||||
// update list pointer
|
||||
m_caSeq[i].next = m_caSeq[loc].next;
|
||||
m_caSeq[i].prior = m_caSeq[loc].prior;
|
||||
|
||||
if (m_iHead == loc)
|
||||
m_iHead = i;
|
||||
else
|
||||
m_caSeq[m_caSeq[i].prior].next = i;
|
||||
|
||||
if (m_iTail == loc)
|
||||
m_iTail = i;
|
||||
else
|
||||
m_caSeq[m_caSeq[i].next].prior = i;
|
||||
}
|
||||
|
||||
m_iLength --;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// There is no loss sequence in the current position
|
||||
// the "seqno" may be contained in a previous node
|
||||
|
||||
// searching previous node
|
||||
int i = (loc - 1 + m_iSize) % m_iSize;
|
||||
while (-1 == m_caSeq[i].data1)
|
||||
i = (i - 1 + m_iSize) % m_iSize;
|
||||
|
||||
// not contained in this node, return
|
||||
if ((-1 == m_caSeq[i].data2) || (CSeqNo::seqcmp(seqno, m_caSeq[i].data2) > 0))
|
||||
return false;
|
||||
|
||||
if (seqno == m_caSeq[i].data2)
|
||||
{
|
||||
// it is the sequence end
|
||||
|
||||
if (seqno == CSeqNo::incseq(m_caSeq[i].data1))
|
||||
m_caSeq[i].data2 = -1;
|
||||
else
|
||||
m_caSeq[i].data2 = CSeqNo::decseq(seqno);
|
||||
}
|
||||
else
|
||||
{
|
||||
// split the sequence
|
||||
|
||||
// construct the second sequence from CSeqNo::incseq(seqno) to the original sequence end
|
||||
// located at "loc + 1"
|
||||
loc = (loc + 1) % m_iSize;
|
||||
|
||||
m_caSeq[loc].data1 = CSeqNo::incseq(seqno);
|
||||
if (CSeqNo::seqcmp(m_caSeq[i].data2, m_caSeq[loc].data1) > 0)
|
||||
m_caSeq[loc].data2 = m_caSeq[i].data2;
|
||||
|
||||
// the first (original) sequence is between the original sequence start to CSeqNo::decseq(seqno)
|
||||
if (seqno == CSeqNo::incseq(m_caSeq[i].data1))
|
||||
m_caSeq[i].data2 = -1;
|
||||
else
|
||||
m_caSeq[i].data2 = CSeqNo::decseq(seqno);
|
||||
|
||||
// update the list pointer
|
||||
m_caSeq[loc].next = m_caSeq[i].next;
|
||||
m_caSeq[i].next = loc;
|
||||
m_caSeq[loc].prior = i;
|
||||
|
||||
if (m_iTail == i)
|
||||
m_iTail = loc;
|
||||
else
|
||||
m_caSeq[m_caSeq[loc].next].prior = loc;
|
||||
}
|
||||
|
||||
m_iLength --;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CRcvLossList::remove(int32_t seqno1, int32_t seqno2)
|
||||
{
|
||||
if (seqno1 <= seqno2)
|
||||
{
|
||||
for (int32_t i = seqno1; i <= seqno2; ++ i)
|
||||
remove(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int32_t j = seqno1; j < CSeqNo::m_iMaxSeqNo; ++ j)
|
||||
remove(j);
|
||||
for (int32_t k = 0; k <= seqno2; ++ k)
|
||||
remove(k);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CRcvLossList::find(int32_t seqno1, int32_t seqno2) const
|
||||
{
|
||||
if (0 == m_iLength)
|
||||
return false;
|
||||
|
||||
int p = m_iHead;
|
||||
|
||||
while (-1 != p)
|
||||
{
|
||||
if ((CSeqNo::seqcmp(m_caSeq[p].data1, seqno1) == 0) ||
|
||||
((CSeqNo::seqcmp(m_caSeq[p].data1, seqno1) > 0) && (CSeqNo::seqcmp(m_caSeq[p].data1, seqno2) <= 0)) ||
|
||||
((CSeqNo::seqcmp(m_caSeq[p].data1, seqno1) < 0) && (m_caSeq[p].data2 != -1) && CSeqNo::seqcmp(m_caSeq[p].data2, seqno1) >= 0))
|
||||
return true;
|
||||
|
||||
p = m_caSeq[p].next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int CRcvLossList::getLossLength() const
|
||||
{
|
||||
return m_iLength;
|
||||
}
|
||||
|
||||
int CRcvLossList::getFirstLostSeq() const
|
||||
{
|
||||
if (0 == m_iLength)
|
||||
return -1;
|
||||
|
||||
return m_caSeq[m_iHead].data1;
|
||||
}
|
||||
|
||||
void CRcvLossList::getLossArray(int32_t* array, int& len, int limit)
|
||||
{
|
||||
len = 0;
|
||||
|
||||
int i = m_iHead;
|
||||
|
||||
while ((len < limit - 1) && (-1 != i))
|
||||
{
|
||||
array[len] = m_caSeq[i].data1;
|
||||
if (-1 != m_caSeq[i].data2)
|
||||
{
|
||||
// there are more than 1 loss in the sequence
|
||||
array[len] |= LOSSDATA_SEQNO_RANGE_FIRST;
|
||||
++ len;
|
||||
array[len] = m_caSeq[i].data2;
|
||||
}
|
||||
|
||||
++ len;
|
||||
|
||||
i = m_caSeq[i].next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CRcvFreshLoss::CRcvFreshLoss(int32_t seqlo, int32_t seqhi, int initial_age): ttl(initial_age)
|
||||
{
|
||||
CTimer::rdtsc(timestamp);
|
||||
seq[0] = seqlo;
|
||||
seq[1] = seqhi;
|
||||
}
|
||||
|
||||
|
||||
CRcvFreshLoss::Emod CRcvFreshLoss::revoke(int32_t sequence)
|
||||
{
|
||||
int32_t diffbegin = CSeqNo::seqcmp(sequence, seq[0]);
|
||||
int32_t diffend = CSeqNo::seqcmp(sequence, seq[1]);
|
||||
|
||||
if ( diffbegin < 0 || diffend > 0 )
|
||||
{
|
||||
return NONE; // not within the range at all.
|
||||
}
|
||||
|
||||
if ( diffbegin == 0 )
|
||||
{
|
||||
if ( diffend == 0 ) // exactly at begin and end
|
||||
{
|
||||
return DELETE;
|
||||
}
|
||||
|
||||
// only exactly at begin. Shrink the range
|
||||
seq[0] = CSeqNo::incseq(seq[0]);
|
||||
return STRIPPED;
|
||||
}
|
||||
|
||||
if ( diffend == 0 ) // exactly at end
|
||||
{
|
||||
seq[1] = CSeqNo::decseq(seq[1]);
|
||||
return STRIPPED;
|
||||
}
|
||||
|
||||
return SPLIT;
|
||||
}
|
||||
|
||||
CRcvFreshLoss::Emod CRcvFreshLoss::revoke(int32_t lo, int32_t hi)
|
||||
{
|
||||
// This should only if the range lo-hi is anyhow covered by seq[0]-seq[1].
|
||||
|
||||
// Note: if the checked item contains sequences that are OLDER
|
||||
// than the oldest sequence in this range, they should be deleted,
|
||||
// even though this wasn't explicitly requested.
|
||||
|
||||
// LOHI: <lo, hi>
|
||||
// ITEM: <lo, hi> <--- delete
|
||||
// If the sequence range is older than the range to be revoked,
|
||||
// delete it anyway.
|
||||
if ( CSeqNo::seqcmp(lo, seq[1]) > 0 )
|
||||
return DELETE;
|
||||
|
||||
// LOHI: <lo, hi>
|
||||
// ITEM: <lo, hi> <-- NOTFOUND
|
||||
// This element is newer than the given sequence, so match failed.
|
||||
if ( CSeqNo::seqcmp(hi, seq[0]) < 0 )
|
||||
return NONE;
|
||||
|
||||
// LOHI: <lo, hi>
|
||||
// ITEM: <lo, ! hi>
|
||||
// RESULT: <lo, hi>
|
||||
// 2. If the 'hi' is in the middle (less than seq[1]), delete partially.
|
||||
// That is, take care of this range for itself and return STRIPPED.
|
||||
if ( CSeqNo::seqcmp(hi, seq[1]) < 0 )
|
||||
{
|
||||
seq[0] = CSeqNo::incseq(hi);
|
||||
return STRIPPED;
|
||||
}
|
||||
|
||||
// LOHI: <lo, hi>
|
||||
// ITEM: <lo, ! hi>
|
||||
// RESULT: DELETE.
|
||||
// 3. Otherwise delete the record, even if this was covering only part of this range.
|
||||
// This is not possible that the sequences OLDER THAN THIS are not required to be
|
||||
// revoken together with this one.
|
||||
|
||||
return DELETE;
|
||||
}
|
250
trunk/3rdparty/srt-1-fit/srtcore/list.h
vendored
Normal file
250
trunk/3rdparty/srt-1-fit/srtcore/list.h
vendored
Normal file
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Yunhong Gu, last updated 01/22/2011
|
||||
modified by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __UDT_LIST_H__
|
||||
#define __UDT_LIST_H__
|
||||
|
||||
|
||||
#include "udt.h"
|
||||
#include "common.h"
|
||||
|
||||
|
||||
class CSndLossList
|
||||
{
|
||||
public:
|
||||
CSndLossList(int size = 1024);
|
||||
~CSndLossList();
|
||||
|
||||
/// Insert a seq. no. into the sender loss list.
|
||||
/// @param [in] seqno1 sequence number starts.
|
||||
/// @param [in] seqno2 sequence number ends.
|
||||
/// @return number of packets that are not in the list previously.
|
||||
|
||||
int insert(int32_t seqno1, int32_t seqno2);
|
||||
|
||||
/// Remove ALL the seq. no. that are not greater than the parameter.
|
||||
/// @param [in] seqno sequence number.
|
||||
|
||||
void remove(int32_t seqno);
|
||||
|
||||
/// Read the loss length.
|
||||
/// @return The length of the list.
|
||||
|
||||
int getLossLength() const;
|
||||
|
||||
/// Read the first (smallest) loss seq. no. in the list and remove it.
|
||||
/// @return The seq. no. or -1 if the list is empty.
|
||||
|
||||
int32_t popLostSeq();
|
||||
|
||||
private:
|
||||
struct Seq
|
||||
{
|
||||
int32_t data1; // sequence number starts
|
||||
int32_t data2; // seqnence number ends
|
||||
int next; // next node in the list
|
||||
}* m_caSeq;
|
||||
|
||||
int m_iHead; // first node
|
||||
int m_iLength; // loss length
|
||||
int m_iSize; // size of the static array
|
||||
int m_iLastInsertPos; // position of last insert node
|
||||
|
||||
mutable pthread_mutex_t m_ListLock; // used to synchronize list operation
|
||||
|
||||
private:
|
||||
CSndLossList(const CSndLossList&);
|
||||
CSndLossList& operator=(const CSndLossList&);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CRcvLossList
|
||||
{
|
||||
public:
|
||||
CRcvLossList(int size = 1024);
|
||||
~CRcvLossList();
|
||||
|
||||
/// Insert a series of loss seq. no. between "seqno1" and "seqno2" into the receiver's loss list.
|
||||
/// @param [in] seqno1 sequence number starts.
|
||||
/// @param [in] seqno2 seqeunce number ends.
|
||||
|
||||
void insert(int32_t seqno1, int32_t seqno2);
|
||||
|
||||
/// Remove a loss seq. no. from the receiver's loss list.
|
||||
/// @param [in] seqno sequence number.
|
||||
/// @return if the packet is removed (true) or no such lost packet is found (false).
|
||||
|
||||
bool remove(int32_t seqno);
|
||||
|
||||
/// Remove all packets between seqno1 and seqno2.
|
||||
/// @param [in] seqno1 start sequence number.
|
||||
/// @param [in] seqno2 end sequence number.
|
||||
/// @return if the packet is removed (true) or no such lost packet is found (false).
|
||||
|
||||
bool remove(int32_t seqno1, int32_t seqno2);
|
||||
|
||||
/// Find if there is any lost packets whose sequence number falling seqno1 and seqno2.
|
||||
/// @param [in] seqno1 start sequence number.
|
||||
/// @param [in] seqno2 end sequence number.
|
||||
/// @return True if found; otherwise false.
|
||||
|
||||
bool find(int32_t seqno1, int32_t seqno2) const;
|
||||
|
||||
/// Read the loss length.
|
||||
/// @return the length of the list.
|
||||
|
||||
int getLossLength() const;
|
||||
|
||||
/// Read the first (smallest) seq. no. in the list.
|
||||
/// @return the sequence number or -1 if the list is empty.
|
||||
|
||||
int getFirstLostSeq() const;
|
||||
|
||||
/// Get a encoded loss array for NAK report.
|
||||
/// @param [out] array the result list of seq. no. to be included in NAK.
|
||||
/// @param [out] len physical length of the result array.
|
||||
/// @param [in] limit maximum length of the array.
|
||||
|
||||
void getLossArray(int32_t* array, int& len, int limit);
|
||||
|
||||
private:
|
||||
struct Seq
|
||||
{
|
||||
int32_t data1; // sequence number starts
|
||||
int32_t data2; // sequence number ends
|
||||
int next; // next node in the list
|
||||
int prior; // prior node in the list;
|
||||
}* m_caSeq;
|
||||
|
||||
int m_iHead; // first node in the list
|
||||
int m_iTail; // last node in the list;
|
||||
int m_iLength; // loss length
|
||||
int m_iSize; // size of the static array
|
||||
|
||||
private:
|
||||
CRcvLossList(const CRcvLossList&);
|
||||
CRcvLossList& operator=(const CRcvLossList&);
|
||||
public:
|
||||
|
||||
struct iterator
|
||||
{
|
||||
int32_t head;
|
||||
Seq* seq;
|
||||
|
||||
iterator(Seq* str, int32_t v): head(v), seq(str) {}
|
||||
|
||||
iterator next() const
|
||||
{
|
||||
if ( head == -1 )
|
||||
return *this; // should report error, but we can only throw exception, so simply ignore it.
|
||||
|
||||
return iterator(seq, seq[head].next);
|
||||
}
|
||||
|
||||
iterator& operator++()
|
||||
{
|
||||
*this = next();
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++(int)
|
||||
{
|
||||
iterator old (seq, head);
|
||||
*this = next();
|
||||
return old;
|
||||
}
|
||||
|
||||
bool operator==(const iterator& second) const
|
||||
{
|
||||
// Ignore seq - should be the same and this is only a sanity check.
|
||||
return head == second.head;
|
||||
}
|
||||
|
||||
bool operator!=(const iterator& second) const { return !(*this == second); }
|
||||
|
||||
std::pair<int32_t, int32_t> operator*()
|
||||
{
|
||||
return std::make_pair(seq[head].data1, seq[head].data2);
|
||||
}
|
||||
};
|
||||
|
||||
iterator begin() { return iterator(m_caSeq, m_iHead); }
|
||||
iterator end() { return iterator(m_caSeq, -1); }
|
||||
|
||||
};
|
||||
|
||||
struct CRcvFreshLoss
|
||||
{
|
||||
int32_t seq[2];
|
||||
int ttl;
|
||||
uint64_t timestamp;
|
||||
|
||||
CRcvFreshLoss(int32_t seqlo, int32_t seqhi, int initial_ttl);
|
||||
|
||||
// Don't WTF when looking at this. The Windows system headers define
|
||||
// a publicly visible preprocessor macro with that name. REALLY!
|
||||
#ifdef DELETE
|
||||
#undef DELETE
|
||||
#endif
|
||||
enum Emod {
|
||||
NONE, //< the given sequence was not found in this range
|
||||
STRIPPED, //< it was equal to first or last, already taken care of
|
||||
SPLIT, //< found in the middle, you have to split this range into two
|
||||
DELETE //< This was a range of one element exactly equal to sequence. Simply delete it.
|
||||
};
|
||||
|
||||
Emod revoke(int32_t sequence);
|
||||
Emod revoke(int32_t lo, int32_t hi);
|
||||
};
|
||||
|
||||
#endif
|
481
trunk/3rdparty/srt-1-fit/srtcore/logging.h
vendored
Normal file
481
trunk/3rdparty/srt-1-fit/srtcore/logging.h
vendored
Normal file
|
@ -0,0 +1,481 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef INC__SRT_LOGGING_H
|
||||
#define INC__SRT_LOGGING_H
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <cstdarg>
|
||||
#ifdef _WIN32
|
||||
#include "win/wintime.h"
|
||||
#include <sys/timeb.h>
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#include <pthread.h>
|
||||
#if HAVE_CXX11
|
||||
#include <mutex>
|
||||
#endif
|
||||
|
||||
#include "srt.h"
|
||||
#include "utilities.h"
|
||||
#include "threadname.h"
|
||||
#include "logging_api.h"
|
||||
#include "srt_compat.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define PRINTF_LIKE __attribute__((format(printf,2,3)))
|
||||
#else
|
||||
#define PRINTF_LIKE
|
||||
#endif
|
||||
|
||||
#if ENABLE_LOGGING
|
||||
|
||||
// GENERAL NOTE: All logger functions ADD THEIR OWN \n (EOL). Don't add any your own EOL character.
|
||||
// The logging system may not add the EOL character, if appropriate flag was set in log settings.
|
||||
// Anyway, treat the whole contents of eventually formatted message as exactly one line.
|
||||
|
||||
// LOGC uses an iostream-like syntax, using the special 'log' symbol.
|
||||
// This symbol isn't visible outside the log macro parameters.
|
||||
// Usage: LOGC(mglog.Debug, log << param1 << param2 << param3);
|
||||
#define LOGC(logdes, args) if (logdes.CheckEnabled()) { srt_logging::LogDispatcher::Proxy log(logdes); log.setloc(__FILE__, __LINE__, __FUNCTION__); args; }
|
||||
|
||||
// LOGF uses printf-like style formatting.
|
||||
// Usage: LOGF(mglog.Debug, "%s: %d", param1.c_str(), int(param2));
|
||||
#define LOGF(logdes, ...) if (logdes.CheckEnabled()) logdes().setloc(__FILE__, __LINE__, __FUNCTION__).form(__VA_ARGS__)
|
||||
|
||||
// LOGP is C++11 only OR with only one string argument.
|
||||
// Usage: LOGP(mglog.Debug, param1, param2, param3);
|
||||
#define LOGP(logdes, ...) if (logdes.CheckEnabled()) logdes.printloc(__FILE__, __LINE__, __FUNCTION__,##__VA_ARGS__)
|
||||
|
||||
#if ENABLE_HEAVY_LOGGING
|
||||
|
||||
#define HLOGC LOGC
|
||||
#define HLOGP LOGP
|
||||
#define HLOGF LOGF
|
||||
|
||||
#define IF_HEAVY_LOGGING(instr) instr
|
||||
|
||||
#else
|
||||
|
||||
#define HLOGC(...)
|
||||
#define HLOGF(...)
|
||||
#define HLOGP(...)
|
||||
|
||||
#define IF_HEAVY_LOGGING(instr) (void)0
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#define LOGC(...)
|
||||
#define LOGF(...)
|
||||
#define LOGP(...)
|
||||
|
||||
#define HLOGC(...)
|
||||
#define HLOGF(...)
|
||||
#define HLOGP(...)
|
||||
|
||||
#define IF_HEAVY_LOGGING(instr) (void)0
|
||||
|
||||
#endif
|
||||
|
||||
namespace srt_logging
|
||||
{
|
||||
|
||||
struct LogConfig
|
||||
{
|
||||
typedef std::bitset<SRT_LOGFA_LASTNONE+1> fa_bitset_t;
|
||||
fa_bitset_t enabled_fa; // NOTE: assumed atomic reading
|
||||
LogLevel::type max_level; // NOTE: assumed atomic reading
|
||||
std::ostream* log_stream;
|
||||
SRT_LOG_HANDLER_FN* loghandler_fn;
|
||||
void* loghandler_opaque;
|
||||
pthread_mutex_t mutex;
|
||||
int flags;
|
||||
|
||||
LogConfig(const fa_bitset_t& initial_fa):
|
||||
enabled_fa(initial_fa),
|
||||
max_level(LogLevel::warning),
|
||||
log_stream(&std::cerr)
|
||||
{
|
||||
pthread_mutex_init(&mutex, 0);
|
||||
}
|
||||
LogConfig(const fa_bitset_t& efa, LogLevel::type l, std::ostream* ls):
|
||||
enabled_fa(efa), max_level(l), log_stream(ls)
|
||||
{
|
||||
pthread_mutex_init(&mutex, 0);
|
||||
}
|
||||
|
||||
~LogConfig()
|
||||
{
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
|
||||
void lock() { pthread_mutex_lock(&mutex); }
|
||||
void unlock() { pthread_mutex_unlock(&mutex); }
|
||||
};
|
||||
|
||||
// The LogDispatcher class represents the object that is responsible for
|
||||
// a decision whether to log something or not, and if so, print the log.
|
||||
struct SRT_API LogDispatcher
|
||||
{
|
||||
private:
|
||||
int fa;
|
||||
LogLevel::type level;
|
||||
static const size_t MAX_PREFIX_SIZE = 32;
|
||||
char prefix[MAX_PREFIX_SIZE+1];
|
||||
LogConfig* src_config;
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
bool isset(int flg) { return (src_config->flags & flg) != 0; }
|
||||
|
||||
public:
|
||||
|
||||
LogDispatcher(int functional_area, LogLevel::type log_level, const char* your_pfx,
|
||||
const char* logger_pfx /*[[nullable]]*/, LogConfig& config):
|
||||
fa(functional_area),
|
||||
level(log_level),
|
||||
src_config(&config)
|
||||
{
|
||||
// XXX stpcpy desired, but not enough portable
|
||||
// Composing the exact prefix is not critical, so simply
|
||||
// cut the prefix, if the length is exceeded
|
||||
|
||||
// See Logger::Logger; we know this has normally 2 characters,
|
||||
// except !!FATAL!!, which has 9. Still less than 32.
|
||||
strcpy(prefix, your_pfx);
|
||||
|
||||
// If the size of the FA name together with severity exceeds the size,
|
||||
// just skip the former.
|
||||
if (logger_pfx && strlen(prefix) + strlen(logger_pfx) + 1 < MAX_PREFIX_SIZE)
|
||||
{
|
||||
strcat(prefix, ":");
|
||||
strcat(prefix, logger_pfx);
|
||||
}
|
||||
pthread_mutex_init(&mutex, 0);
|
||||
}
|
||||
|
||||
~LogDispatcher()
|
||||
{
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
|
||||
bool CheckEnabled();
|
||||
|
||||
void CreateLogLinePrefix(std::ostringstream&);
|
||||
void SendLogLine(const char* file, int line, const std::string& area, const std::string& sl);
|
||||
|
||||
// log.Debug("This is the ", nth, " time"); <--- C++11 only.
|
||||
// log.Debug() << "This is the " << nth << " time"; <--- C++03 available.
|
||||
|
||||
#if HAVE_CXX11
|
||||
|
||||
template <class... Args>
|
||||
void PrintLogLine(const char* file, int line, const std::string& area, Args&&... args);
|
||||
|
||||
template<class Arg1, class... Args>
|
||||
void operator()(Arg1&& arg1, Args&&... args)
|
||||
{
|
||||
PrintLogLine("UNKNOWN.c++", 0, "UNKNOWN", arg1, args...);
|
||||
}
|
||||
|
||||
template<class Arg1, class... Args>
|
||||
void printloc(const char* file, int line, const std::string& area, Arg1&& arg1, Args&&... args)
|
||||
{
|
||||
PrintLogLine(file, line, area, arg1, args...);
|
||||
}
|
||||
#else
|
||||
template <class Arg>
|
||||
void PrintLogLine(const char* file, int line, const std::string& area, const Arg& arg);
|
||||
|
||||
// For C++03 (older) standard provide only with one argument.
|
||||
template <class Arg>
|
||||
void operator()(const Arg& arg)
|
||||
{
|
||||
PrintLogLine("UNKNOWN.c++", 0, "UNKNOWN", arg);
|
||||
}
|
||||
|
||||
void printloc(const char* file, int line, const std::string& area, const std::string& arg1)
|
||||
{
|
||||
PrintLogLine(file, line, area, arg1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLE_LOGGING
|
||||
|
||||
struct Proxy;
|
||||
friend struct Proxy;
|
||||
|
||||
Proxy operator()();
|
||||
#else
|
||||
|
||||
// Dummy proxy that does nothing
|
||||
struct DummyProxy
|
||||
{
|
||||
DummyProxy(LogDispatcher&)
|
||||
{
|
||||
}
|
||||
|
||||
template <class T>
|
||||
DummyProxy& operator<<(const T& ) // predicted for temporary objects
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
DummyProxy& form(const char*, ...)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
DummyProxy& setloc(const char* , int , std::string)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
DummyProxy operator()()
|
||||
{
|
||||
return DummyProxy(*this);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#if ENABLE_LOGGING
|
||||
|
||||
struct LogDispatcher::Proxy
|
||||
{
|
||||
LogDispatcher& that;
|
||||
|
||||
std::ostringstream os;
|
||||
|
||||
// Cache the 'enabled' state in the beginning. If the logging
|
||||
// becomes enabled or disabled in the middle of the log, we don't
|
||||
// want it to be partially printed anyway.
|
||||
bool that_enabled;
|
||||
int flags;
|
||||
|
||||
// CACHE!!!
|
||||
const char* i_file;
|
||||
int i_line;
|
||||
std::string area;
|
||||
|
||||
Proxy& setloc(const char* f, int l, std::string a)
|
||||
{
|
||||
i_file = f;
|
||||
i_line = l;
|
||||
area = a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Left for future. Not sure if it's more convenient
|
||||
// to use this to translate __PRETTY_FUNCTION__ to
|
||||
// something short, or just let's leave __FUNCTION__
|
||||
// or better __func__.
|
||||
std::string ExtractName(std::string pretty_function);
|
||||
|
||||
Proxy(LogDispatcher& guy);
|
||||
|
||||
// Copy constructor is needed due to noncopyable ostringstream.
|
||||
// This is used only in creation of the default object, so just
|
||||
// use the default values, just copy the location cache.
|
||||
Proxy(const Proxy& p): that(p.that), area(p.area)
|
||||
{
|
||||
i_file = p.i_file;
|
||||
i_line = p.i_line;
|
||||
that_enabled = false;
|
||||
flags = p.flags;
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
Proxy& operator<<(const T& arg) // predicted for temporary objects
|
||||
{
|
||||
if ( that_enabled )
|
||||
{
|
||||
os << arg;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~Proxy()
|
||||
{
|
||||
if ( that_enabled )
|
||||
{
|
||||
if ( (flags & SRT_LOGF_DISABLE_EOL) == 0 )
|
||||
os << std::endl;
|
||||
that.SendLogLine(i_file, i_line, area, os.str());
|
||||
}
|
||||
// Needed in destructor?
|
||||
//os.clear();
|
||||
//os.str("");
|
||||
}
|
||||
|
||||
Proxy& form(const char* fmts, ...) PRINTF_LIKE
|
||||
{
|
||||
if ( !that_enabled )
|
||||
return *this;
|
||||
|
||||
if ( !fmts || fmts[0] == '\0' )
|
||||
return *this;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmts);
|
||||
vform(fmts, ap);
|
||||
va_end(ap);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Proxy& vform(const char* fmts, va_list ap)
|
||||
{
|
||||
char buf[512];
|
||||
|
||||
vsprintf(buf, fmts, ap);
|
||||
size_t len = strlen(buf);
|
||||
if ( buf[len-1] == '\n' )
|
||||
{
|
||||
// Remove EOL character, should it happen to be at the end.
|
||||
// The EOL will be added at the end anyway.
|
||||
buf[len-1] = '\0';
|
||||
}
|
||||
|
||||
os << buf;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
class Logger
|
||||
{
|
||||
int m_fa;
|
||||
LogConfig& m_config;
|
||||
|
||||
public:
|
||||
|
||||
LogDispatcher Debug;
|
||||
LogDispatcher Note;
|
||||
LogDispatcher Warn;
|
||||
LogDispatcher Error;
|
||||
LogDispatcher Fatal;
|
||||
|
||||
Logger(int functional_area, LogConfig& config, const char* logger_pfx = NULL):
|
||||
m_fa(functional_area),
|
||||
m_config(config),
|
||||
Debug ( m_fa, LogLevel::debug, " D", logger_pfx, m_config ),
|
||||
Note ( m_fa, LogLevel::note, ".N", logger_pfx, m_config ),
|
||||
Warn ( m_fa, LogLevel::warning, "!W", logger_pfx, m_config ),
|
||||
Error ( m_fa, LogLevel::error, "*E", logger_pfx, m_config ),
|
||||
Fatal ( m_fa, LogLevel::fatal, "!!FATAL!!", logger_pfx, m_config )
|
||||
{
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
inline bool LogDispatcher::CheckEnabled()
|
||||
{
|
||||
// Don't use enabler caching. Check enabled state every time.
|
||||
|
||||
// These assume to be atomically read, so the lock is not needed
|
||||
// (note that writing to this field is still mutex-protected).
|
||||
// It's also no problem if the level was changed at the moment
|
||||
// when the enabler check is tested here. Worst case, the log
|
||||
// will be printed just a moment after it was turned off.
|
||||
const LogConfig* config = src_config; // to enforce using const operator[]
|
||||
int configured_enabled_fa = config->enabled_fa[fa];
|
||||
int configured_maxlevel = config->max_level;
|
||||
|
||||
return configured_enabled_fa && level <= configured_maxlevel;
|
||||
}
|
||||
|
||||
SRT_API std::string FormatTime(uint64_t time);
|
||||
|
||||
#if HAVE_CXX11
|
||||
|
||||
//extern std::mutex Debug_mutex;
|
||||
|
||||
inline void PrintArgs(std::ostream&) {}
|
||||
|
||||
template <class Arg1, class... Args>
|
||||
inline void PrintArgs(std::ostream& serr, Arg1&& arg1, Args&&... args)
|
||||
{
|
||||
serr << arg1;
|
||||
PrintArgs(serr, args...);
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
inline void LogDispatcher::PrintLogLine(const char* file ATR_UNUSED, int line ATR_UNUSED, const std::string& area ATR_UNUSED, Args&&... args ATR_UNUSED)
|
||||
{
|
||||
#ifdef ENABLE_LOGGING
|
||||
std::ostringstream serr;
|
||||
CreateLogLinePrefix(serr);
|
||||
PrintArgs(serr, args...);
|
||||
|
||||
if ( !isset(SRT_LOGF_DISABLE_EOL) )
|
||||
serr << std::endl;
|
||||
|
||||
// Not sure, but it wasn't ever used.
|
||||
SendLogLine(file, line, area, serr.str());
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
template <class Arg>
|
||||
inline void LogDispatcher::PrintLogLine(const char* file ATR_UNUSED, int line ATR_UNUSED, const std::string& area ATR_UNUSED, const Arg& arg ATR_UNUSED)
|
||||
{
|
||||
#ifdef ENABLE_LOGGING
|
||||
std::ostringstream serr;
|
||||
CreateLogLinePrefix(serr);
|
||||
serr << arg;
|
||||
|
||||
if ( !isset(SRT_LOGF_DISABLE_EOL) )
|
||||
serr << std::endl;
|
||||
|
||||
// Not sure, but it wasn't ever used.
|
||||
SendLogLine(file, line, area, serr.str());
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// SendLogLine can be compiled normally. It's intermediately used by:
|
||||
// - Proxy object, which is replaced by DummyProxy when !ENABLE_LOGGING
|
||||
// - PrintLogLine, which has empty body when !ENABLE_LOGGING
|
||||
inline void LogDispatcher::SendLogLine(const char* file, int line, const std::string& area, const std::string& msg)
|
||||
{
|
||||
src_config->lock();
|
||||
if ( src_config->loghandler_fn )
|
||||
{
|
||||
(*src_config->loghandler_fn)(src_config->loghandler_opaque, int(level), file, line, area.c_str(), msg.c_str());
|
||||
}
|
||||
else if ( src_config->log_stream )
|
||||
{
|
||||
(*src_config->log_stream) << msg;
|
||||
(*src_config->log_stream).flush();
|
||||
}
|
||||
src_config->unlock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
108
trunk/3rdparty/srt-1-fit/srtcore/logging_api.h
vendored
Normal file
108
trunk/3rdparty/srt-1-fit/srtcore/logging_api.h
vendored
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef INC__SRT_LOGGING_API_H
|
||||
#define INC__SRT_LOGGING_API_H
|
||||
|
||||
// These are required for access functions:
|
||||
// - adding FA (requires set)
|
||||
// - setting a log stream (requires iostream)
|
||||
#ifdef __cplusplus
|
||||
#include <set>
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
#ifdef _WIN32
|
||||
#include "win/syslog_defs.h"
|
||||
#else
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
|
||||
// Syslog is included so that it provides log level names.
|
||||
// Haivision log standard requires the same names plus extra one:
|
||||
#ifndef LOG_DEBUG_TRACE
|
||||
#define LOG_DEBUG_TRACE 8
|
||||
#endif
|
||||
// It's unused anyway, just for the record.
|
||||
#define SRT_LOG_LEVEL_MIN LOG_CRIT
|
||||
#define SRT_LOG_LEVEL_MAX LOG_DEBUG
|
||||
|
||||
// Flags
|
||||
#define SRT_LOGF_DISABLE_TIME 1
|
||||
#define SRT_LOGF_DISABLE_THREADNAME 2
|
||||
#define SRT_LOGF_DISABLE_SEVERITY 4
|
||||
#define SRT_LOGF_DISABLE_EOL 8
|
||||
|
||||
// Handler type.
|
||||
typedef void SRT_LOG_HANDLER_FN(void* opaque, int level, const char* file, int line, const char* area, const char* message);
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace srt_logging
|
||||
{
|
||||
|
||||
|
||||
struct LogFA
|
||||
{
|
||||
private:
|
||||
int value;
|
||||
public:
|
||||
operator int() const { return value; }
|
||||
|
||||
LogFA(int v): value(v)
|
||||
{
|
||||
// Generally this was what it has to be used for.
|
||||
// Unfortunately it couldn't be agreed with the
|
||||
//logging_fa_all.insert(v);
|
||||
}
|
||||
};
|
||||
|
||||
const LogFA LOGFA_GENERAL = 0;
|
||||
|
||||
|
||||
|
||||
namespace LogLevel
|
||||
{
|
||||
// There are 3 general levels:
|
||||
|
||||
// A. fatal - this means the application WILL crash.
|
||||
// B. unexpected:
|
||||
// - error: this was unexpected for the library
|
||||
// - warning: this was expected by the library, but may be harmful for the application
|
||||
// C. expected:
|
||||
// - note: a significant, but rarely occurring event
|
||||
// - debug: may occur even very often and enabling it can harm performance
|
||||
|
||||
enum type
|
||||
{
|
||||
fatal = LOG_CRIT,
|
||||
// Fatal vs. Error: with Error, you can still continue.
|
||||
error = LOG_ERR,
|
||||
// Error vs. Warning: Warning isn't considered a problem for the library.
|
||||
warning = LOG_WARNING,
|
||||
// Warning vs. Note: Note means something unusual, but completely correct behavior.
|
||||
note = LOG_NOTICE,
|
||||
// Note vs. Debug: Debug may occur even multiple times in a millisecond.
|
||||
// (Well, worth noting that Error and Warning potentially also can).
|
||||
debug = LOG_DEBUG
|
||||
};
|
||||
}
|
||||
|
||||
class Logger;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
381
trunk/3rdparty/srt-1-fit/srtcore/md5.cpp
vendored
Normal file
381
trunk/3rdparty/srt-1-fit/srtcore/md5.cpp
vendored
Normal file
|
@ -0,0 +1,381 @@
|
|||
/*
|
||||
Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
L. Peter Deutsch
|
||||
ghost@aladdin.com
|
||||
|
||||
*/
|
||||
/* $Id: md5.cpp,v 1.3 2008/01/20 22:52:04 lilyco Exp $ */
|
||||
/*
|
||||
Independent implementation of MD5 (RFC 1321).
|
||||
|
||||
This code implements the MD5 Algorithm defined in RFC 1321, whose
|
||||
text is available at
|
||||
http://www.ietf.org/rfc/rfc1321.txt
|
||||
The code is derived from the text of the RFC, including the test suite
|
||||
(section A.5) but excluding the rest of Appendix A. It does not include
|
||||
any code or documentation that is identified in the RFC as being
|
||||
copyrighted.
|
||||
|
||||
The original and principal author of md5.c is L. Peter Deutsch
|
||||
<ghost@aladdin.com>. Other authors are noted in the change history
|
||||
that follows (in reverse chronological order):
|
||||
|
||||
2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
|
||||
either statically or dynamically; added missing #include <string.h>
|
||||
in library.
|
||||
2002-03-11 lpd Corrected argument list for main(), and added int return
|
||||
type, in test program and T value program.
|
||||
2002-02-21 lpd Added missing #include <stdio.h> in test program.
|
||||
2000-07-03 lpd Patched to eliminate warnings about "constant is
|
||||
unsigned in ANSI C, signed in traditional"; made test program
|
||||
self-checking.
|
||||
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
|
||||
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
|
||||
1999-05-03 lpd Original version.
|
||||
*/
|
||||
|
||||
#include "md5.h"
|
||||
#include <string.h>
|
||||
|
||||
#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
|
||||
#ifdef ARCH_IS_BIG_ENDIAN
|
||||
# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
|
||||
#else
|
||||
# define BYTE_ORDER 0
|
||||
#endif
|
||||
|
||||
#define T_MASK ((md5_word_t)~0)
|
||||
#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
|
||||
#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
|
||||
#define T3 0x242070db
|
||||
#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
|
||||
#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
|
||||
#define T6 0x4787c62a
|
||||
#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
|
||||
#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
|
||||
#define T9 0x698098d8
|
||||
#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
|
||||
#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
|
||||
#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
|
||||
#define T13 0x6b901122
|
||||
#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
|
||||
#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
|
||||
#define T16 0x49b40821
|
||||
#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
|
||||
#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
|
||||
#define T19 0x265e5a51
|
||||
#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
|
||||
#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
|
||||
#define T22 0x02441453
|
||||
#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
|
||||
#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
|
||||
#define T25 0x21e1cde6
|
||||
#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
|
||||
#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
|
||||
#define T28 0x455a14ed
|
||||
#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
|
||||
#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
|
||||
#define T31 0x676f02d9
|
||||
#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
|
||||
#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
|
||||
#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
|
||||
#define T35 0x6d9d6122
|
||||
#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
|
||||
#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
|
||||
#define T38 0x4bdecfa9
|
||||
#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
|
||||
#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
|
||||
#define T41 0x289b7ec6
|
||||
#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
|
||||
#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
|
||||
#define T44 0x04881d05
|
||||
#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
|
||||
#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
|
||||
#define T47 0x1fa27cf8
|
||||
#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
|
||||
#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
|
||||
#define T50 0x432aff97
|
||||
#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
|
||||
#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
|
||||
#define T53 0x655b59c3
|
||||
#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
|
||||
#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
|
||||
#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
|
||||
#define T57 0x6fa87e4f
|
||||
#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
|
||||
#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
|
||||
#define T60 0x4e0811a1
|
||||
#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
|
||||
#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
|
||||
#define T63 0x2ad7d2bb
|
||||
#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
|
||||
|
||||
|
||||
static void
|
||||
md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
|
||||
{
|
||||
md5_word_t
|
||||
a = pms->abcd[0], b = pms->abcd[1],
|
||||
c = pms->abcd[2], d = pms->abcd[3];
|
||||
md5_word_t t;
|
||||
#if BYTE_ORDER > 0
|
||||
/* Define storage only for big-endian CPUs. */
|
||||
md5_word_t X[16];
|
||||
#else
|
||||
/* Define storage for little-endian or both types of CPUs. */
|
||||
md5_word_t xbuf[16];
|
||||
const md5_word_t *X;
|
||||
#endif
|
||||
|
||||
{
|
||||
#if BYTE_ORDER == 0
|
||||
/*
|
||||
* Determine dynamically whether this is a big-endian or
|
||||
* little-endian machine, since we can use a more efficient
|
||||
* algorithm on the latter.
|
||||
*/
|
||||
static const int w = 1;
|
||||
|
||||
if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
|
||||
#endif
|
||||
#if BYTE_ORDER <= 0 /* little-endian */
|
||||
{
|
||||
/*
|
||||
* On little-endian machines, we can process properly aligned
|
||||
* data without copying it.
|
||||
*/
|
||||
if (!((data - (const md5_byte_t *)0) & 3)) {
|
||||
/* data are properly aligned */
|
||||
X = (const md5_word_t *)data;
|
||||
} else {
|
||||
/* not aligned */
|
||||
memcpy(xbuf, data, 64);
|
||||
X = xbuf;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if BYTE_ORDER == 0
|
||||
else /* dynamic big-endian */
|
||||
#endif
|
||||
#if BYTE_ORDER >= 0 /* big-endian */
|
||||
{
|
||||
/*
|
||||
* On big-endian machines, we must arrange the bytes in the
|
||||
* right order.
|
||||
*/
|
||||
const md5_byte_t *xp = data;
|
||||
int i;
|
||||
|
||||
# if BYTE_ORDER == 0
|
||||
X = xbuf; /* (dynamic only) */
|
||||
# else
|
||||
# define xbuf X /* (static only) */
|
||||
# endif
|
||||
for (i = 0; i < 16; ++i, xp += 4)
|
||||
xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
|
||||
|
||||
/* Round 1. */
|
||||
/* Let [abcd k s i] denote the operation
|
||||
a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + F(b,c,d) + X[k] + Ti;\
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 0, 7, T1);
|
||||
SET(d, a, b, c, 1, 12, T2);
|
||||
SET(c, d, a, b, 2, 17, T3);
|
||||
SET(b, c, d, a, 3, 22, T4);
|
||||
SET(a, b, c, d, 4, 7, T5);
|
||||
SET(d, a, b, c, 5, 12, T6);
|
||||
SET(c, d, a, b, 6, 17, T7);
|
||||
SET(b, c, d, a, 7, 22, T8);
|
||||
SET(a, b, c, d, 8, 7, T9);
|
||||
SET(d, a, b, c, 9, 12, T10);
|
||||
SET(c, d, a, b, 10, 17, T11);
|
||||
SET(b, c, d, a, 11, 22, T12);
|
||||
SET(a, b, c, d, 12, 7, T13);
|
||||
SET(d, a, b, c, 13, 12, T14);
|
||||
SET(c, d, a, b, 14, 17, T15);
|
||||
SET(b, c, d, a, 15, 22, T16);
|
||||
#undef SET
|
||||
|
||||
/* Round 2. */
|
||||
/* Let [abcd k s i] denote the operation
|
||||
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + G(b,c,d) + X[k] + Ti;\
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 1, 5, T17);
|
||||
SET(d, a, b, c, 6, 9, T18);
|
||||
SET(c, d, a, b, 11, 14, T19);
|
||||
SET(b, c, d, a, 0, 20, T20);
|
||||
SET(a, b, c, d, 5, 5, T21);
|
||||
SET(d, a, b, c, 10, 9, T22);
|
||||
SET(c, d, a, b, 15, 14, T23);
|
||||
SET(b, c, d, a, 4, 20, T24);
|
||||
SET(a, b, c, d, 9, 5, T25);
|
||||
SET(d, a, b, c, 14, 9, T26);
|
||||
SET(c, d, a, b, 3, 14, T27);
|
||||
SET(b, c, d, a, 8, 20, T28);
|
||||
SET(a, b, c, d, 13, 5, T29);
|
||||
SET(d, a, b, c, 2, 9, T30);
|
||||
SET(c, d, a, b, 7, 14, T31);
|
||||
SET(b, c, d, a, 12, 20, T32);
|
||||
#undef SET
|
||||
|
||||
/* Round 3. */
|
||||
/* Let [abcd k s t] denote the operation
|
||||
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define H(x, y, z) ((x) ^ (y) ^ (z))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + H(b,c,d) + X[k] + Ti;\
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 5, 4, T33);
|
||||
SET(d, a, b, c, 8, 11, T34);
|
||||
SET(c, d, a, b, 11, 16, T35);
|
||||
SET(b, c, d, a, 14, 23, T36);
|
||||
SET(a, b, c, d, 1, 4, T37);
|
||||
SET(d, a, b, c, 4, 11, T38);
|
||||
SET(c, d, a, b, 7, 16, T39);
|
||||
SET(b, c, d, a, 10, 23, T40);
|
||||
SET(a, b, c, d, 13, 4, T41);
|
||||
SET(d, a, b, c, 0, 11, T42);
|
||||
SET(c, d, a, b, 3, 16, T43);
|
||||
SET(b, c, d, a, 6, 23, T44);
|
||||
SET(a, b, c, d, 9, 4, T45);
|
||||
SET(d, a, b, c, 12, 11, T46);
|
||||
SET(c, d, a, b, 15, 16, T47);
|
||||
SET(b, c, d, a, 2, 23, T48);
|
||||
#undef SET
|
||||
|
||||
/* Round 4. */
|
||||
/* Let [abcd k s t] denote the operation
|
||||
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + I(b,c,d) + X[k] + Ti;\
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 0, 6, T49);
|
||||
SET(d, a, b, c, 7, 10, T50);
|
||||
SET(c, d, a, b, 14, 15, T51);
|
||||
SET(b, c, d, a, 5, 21, T52);
|
||||
SET(a, b, c, d, 12, 6, T53);
|
||||
SET(d, a, b, c, 3, 10, T54);
|
||||
SET(c, d, a, b, 10, 15, T55);
|
||||
SET(b, c, d, a, 1, 21, T56);
|
||||
SET(a, b, c, d, 8, 6, T57);
|
||||
SET(d, a, b, c, 15, 10, T58);
|
||||
SET(c, d, a, b, 6, 15, T59);
|
||||
SET(b, c, d, a, 13, 21, T60);
|
||||
SET(a, b, c, d, 4, 6, T61);
|
||||
SET(d, a, b, c, 11, 10, T62);
|
||||
SET(c, d, a, b, 2, 15, T63);
|
||||
SET(b, c, d, a, 9, 21, T64);
|
||||
#undef SET
|
||||
|
||||
/* Then perform the following additions. (That is increment each
|
||||
of the four registers by the value it had before this block
|
||||
was started.) */
|
||||
pms->abcd[0] += a;
|
||||
pms->abcd[1] += b;
|
||||
pms->abcd[2] += c;
|
||||
pms->abcd[3] += d;
|
||||
}
|
||||
|
||||
void
|
||||
md5_init(md5_state_t *pms)
|
||||
{
|
||||
pms->count[0] = pms->count[1] = 0;
|
||||
pms->abcd[0] = 0x67452301;
|
||||
pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
|
||||
pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
|
||||
pms->abcd[3] = 0x10325476;
|
||||
}
|
||||
|
||||
void
|
||||
md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
|
||||
{
|
||||
const md5_byte_t *p = data;
|
||||
int left = nbytes;
|
||||
int offset = (pms->count[0] >> 3) & 63;
|
||||
md5_word_t nbits = (md5_word_t)(nbytes << 3);
|
||||
|
||||
if (nbytes <= 0)
|
||||
return;
|
||||
|
||||
/* Update the message length. */
|
||||
pms->count[1] += nbytes >> 29;
|
||||
pms->count[0] += nbits;
|
||||
if (pms->count[0] < nbits)
|
||||
pms->count[1]++;
|
||||
|
||||
/* Process an initial partial block. */
|
||||
if (offset) {
|
||||
int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
|
||||
|
||||
memcpy(pms->buf + offset, p, copy);
|
||||
if (offset + copy < 64)
|
||||
return;
|
||||
p += copy;
|
||||
left -= copy;
|
||||
md5_process(pms, pms->buf);
|
||||
}
|
||||
|
||||
/* Process full blocks. */
|
||||
for (; left >= 64; p += 64, left -= 64)
|
||||
md5_process(pms, p);
|
||||
|
||||
/* Process a final partial block. */
|
||||
if (left)
|
||||
memcpy(pms->buf, p, left);
|
||||
}
|
||||
|
||||
void
|
||||
md5_finish(md5_state_t *pms, md5_byte_t digest[16])
|
||||
{
|
||||
static const md5_byte_t pad[64] = {
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
md5_byte_t data[8];
|
||||
int i;
|
||||
|
||||
/* Save the length before padding. */
|
||||
for (i = 0; i < 8; ++i)
|
||||
data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
|
||||
/* Pad to 56 bytes mod 64. */
|
||||
md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
|
||||
/* Append the length. */
|
||||
md5_append(pms, data, 8);
|
||||
for (i = 0; i < 16; ++i)
|
||||
digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
|
||||
}
|
91
trunk/3rdparty/srt-1-fit/srtcore/md5.h
vendored
Normal file
91
trunk/3rdparty/srt-1-fit/srtcore/md5.h
vendored
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
L. Peter Deutsch
|
||||
ghost@aladdin.com
|
||||
|
||||
*/
|
||||
/* $Id: md5.h,v 1.2 2007/12/24 05:58:37 lilyco Exp $ */
|
||||
/*
|
||||
Independent implementation of MD5 (RFC 1321).
|
||||
|
||||
This code implements the MD5 Algorithm defined in RFC 1321, whose
|
||||
text is available at
|
||||
http://www.ietf.org/rfc/rfc1321.txt
|
||||
The code is derived from the text of the RFC, including the test suite
|
||||
(section A.5) but excluding the rest of Appendix A. It does not include
|
||||
any code or documentation that is identified in the RFC as being
|
||||
copyrighted.
|
||||
|
||||
The original and principal author of md5.h is L. Peter Deutsch
|
||||
<ghost@aladdin.com>. Other authors are noted in the change history
|
||||
that follows (in reverse chronological order):
|
||||
|
||||
2002-04-13 lpd Removed support for non-ANSI compilers; removed
|
||||
references to Ghostscript; clarified derivation from RFC 1321;
|
||||
now handles byte order either statically or dynamically.
|
||||
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
|
||||
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
|
||||
added conditionalization for C++ compilation from Martin
|
||||
Purschke <purschke@bnl.gov>.
|
||||
1999-05-03 lpd Original version.
|
||||
*/
|
||||
|
||||
#ifndef md5_INCLUDED
|
||||
# define md5_INCLUDED
|
||||
|
||||
/*
|
||||
* This package supports both compile-time and run-time determination of CPU
|
||||
* byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
|
||||
* compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
|
||||
* defined as non-zero, the code will be compiled to run only on big-endian
|
||||
* CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
|
||||
* run on either big- or little-endian CPUs, but will run slightly less
|
||||
* efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
|
||||
*/
|
||||
|
||||
typedef unsigned char md5_byte_t; /* 8-bit byte */
|
||||
typedef unsigned int md5_word_t; /* 32-bit word */
|
||||
|
||||
/* Define the state of the MD5 Algorithm. */
|
||||
typedef struct md5_state_s {
|
||||
md5_word_t count[2]; /* message length in bits, lsw first */
|
||||
md5_word_t abcd[4]; /* digest buffer */
|
||||
md5_byte_t buf[64]; /* accumulate block */
|
||||
} md5_state_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* Initialize the algorithm. */
|
||||
void md5_init(md5_state_t *pms);
|
||||
|
||||
/* Append a string to the message. */
|
||||
void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
|
||||
|
||||
/* Finish the message and return the digest. */
|
||||
void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* md5_INCLUDED */
|
133
trunk/3rdparty/srt-1-fit/srtcore/netinet_any.h
vendored
Normal file
133
trunk/3rdparty/srt-1-fit/srtcore/netinet_any.h
vendored
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef INC__NETINET_ANY_H
|
||||
#define INC__NETINET_ANY_H
|
||||
|
||||
#include <cstring>
|
||||
#include "platform_sys.h"
|
||||
|
||||
// This structure should replace every use of sockaddr and its currently
|
||||
// used specializations, sockaddr_in and sockaddr_in6. This is to simplify
|
||||
// the use of the original BSD API that relies on type-violating type casts.
|
||||
// You can use the instances of sockaddr_any in every place where sockaddr is
|
||||
// required.
|
||||
|
||||
struct sockaddr_any
|
||||
{
|
||||
union
|
||||
{
|
||||
sockaddr_in sin;
|
||||
sockaddr_in6 sin6;
|
||||
sockaddr sa;
|
||||
};
|
||||
socklen_t len;
|
||||
|
||||
sockaddr_any(int domain = AF_INET)
|
||||
{
|
||||
memset(this, 0, sizeof *this);
|
||||
sa.sa_family = domain;
|
||||
len = size();
|
||||
}
|
||||
|
||||
socklen_t size() const
|
||||
{
|
||||
switch (sa.sa_family)
|
||||
{
|
||||
case AF_INET: return socklen_t(sizeof sin);
|
||||
case AF_INET6: return socklen_t(sizeof sin6);
|
||||
|
||||
default: return 0; // fallback, impossible
|
||||
}
|
||||
}
|
||||
|
||||
int family() const { return sa.sa_family; }
|
||||
|
||||
// port is in exactly the same location in both sin and sin6
|
||||
// and has the same size. This is actually yet another common
|
||||
// field, just not mentioned in the sockaddr structure.
|
||||
uint16_t& r_port() { return sin.sin_port; }
|
||||
uint16_t r_port() const { return sin.sin_port; }
|
||||
int hport() const { return ntohs(sin.sin_port); }
|
||||
|
||||
void hport(int value)
|
||||
{
|
||||
// Port is fortunately located at the same position
|
||||
// in both sockaddr_in and sockaddr_in6 and has the
|
||||
// same size.
|
||||
sin.sin_port = htons(value);
|
||||
}
|
||||
|
||||
sockaddr* get() { return &sa; }
|
||||
sockaddr* operator&() { return &sa; }
|
||||
|
||||
const sockaddr* get() const { return &sa; }
|
||||
const sockaddr* operator&() const { return &sa; }
|
||||
|
||||
template <int> struct TypeMap;
|
||||
|
||||
template <int af_domain>
|
||||
typename TypeMap<af_domain>::type& get();
|
||||
|
||||
struct Equal
|
||||
{
|
||||
bool operator()(const sockaddr_any& c1, const sockaddr_any& c2)
|
||||
{
|
||||
return memcmp(&c1, &c2, sizeof(c1)) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct EqualAddress
|
||||
{
|
||||
bool operator()(const sockaddr_any& c1, const sockaddr_any& c2)
|
||||
{
|
||||
if ( c1.sa.sa_family == AF_INET )
|
||||
{
|
||||
return c1.sin.sin_addr.s_addr == c2.sin.sin_addr.s_addr;
|
||||
}
|
||||
|
||||
if ( c1.sa.sa_family == AF_INET6 )
|
||||
{
|
||||
return memcmp(&c1.sin6.sin6_addr, &c2.sin6.sin6_addr, sizeof (in6_addr)) == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
bool equal_address(const sockaddr_any& rhs) const
|
||||
{
|
||||
return EqualAddress()(*this, rhs);
|
||||
}
|
||||
|
||||
struct Less
|
||||
{
|
||||
bool operator()(const sockaddr_any& c1, const sockaddr_any& c2)
|
||||
{
|
||||
return memcmp(&c1, &c2, sizeof(c1)) < 0;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template<> struct sockaddr_any::TypeMap<AF_INET> { typedef sockaddr_in type; };
|
||||
template<> struct sockaddr_any::TypeMap<AF_INET6> { typedef sockaddr_in6 type; };
|
||||
|
||||
template <>
|
||||
inline sockaddr_any::TypeMap<AF_INET>::type& sockaddr_any::get<AF_INET>() { return sin; }
|
||||
template <>
|
||||
inline sockaddr_any::TypeMap<AF_INET6>::type& sockaddr_any::get<AF_INET6>() { return sin6; }
|
||||
|
||||
#endif
|
494
trunk/3rdparty/srt-1-fit/srtcore/packet.cpp
vendored
Normal file
494
trunk/3rdparty/srt-1-fit/srtcore/packet.cpp
vendored
Normal file
|
@ -0,0 +1,494 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Yunhong Gu, last updated 02/12/2011
|
||||
modified by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Packet Header |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | |
|
||||
// ~ Data / Control Information Field ~
|
||||
// | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |0| Sequence Number |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |ff |o|kf |r| Message Number |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Time Stamp |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Destination Socket ID |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// bit 0:
|
||||
// 0: Data Packet
|
||||
// 1: Control Packet
|
||||
// bit ff:
|
||||
// 11: solo message packet
|
||||
// 10: first packet of a message
|
||||
// 01: last packet of a message
|
||||
// bit o:
|
||||
// 0: in order delivery not required
|
||||
// 1: in order delivery required
|
||||
// bit kf: HaiCrypt Key Flags
|
||||
// 00: not encrypted
|
||||
// 01: encrypted with even key
|
||||
// 10: encrypted with odd key
|
||||
// bit r: retransmission flag (set to 1 if this packet was sent again)
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |1| Type | Reserved |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Additional Info |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Time Stamp |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Destination Socket ID |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// bit 1-15: Message type -- see @a UDTMessageType
|
||||
// 0: Protocol Connection Handshake (UMSG_HANDSHAKE}
|
||||
// Add. Info: Undefined
|
||||
// Control Info: Handshake information (see @a CHandShake)
|
||||
// 1: Keep-alive (UMSG_KEEPALIVE)
|
||||
// Add. Info: Undefined
|
||||
// Control Info: None
|
||||
// 2: Acknowledgement (UMSG_ACK)
|
||||
// Add. Info: The ACK sequence number
|
||||
// Control Info: The sequence number to which (but not include) all the previous packets have beed received
|
||||
// Optional: RTT
|
||||
// RTT Variance
|
||||
// available receiver buffer size (in bytes)
|
||||
// advertised flow window size (number of packets)
|
||||
// estimated bandwidth (number of packets per second)
|
||||
// 3: Negative Acknowledgement (UMSG_LOSSREPORT)
|
||||
// Add. Info: Undefined
|
||||
// Control Info: Loss list (see loss list coding below)
|
||||
// 4: Congestion/Delay Warning (UMSG_CGWARNING)
|
||||
// Add. Info: Undefined
|
||||
// Control Info: None
|
||||
// 5: Shutdown (UMSG_SHUTDOWN)
|
||||
// Add. Info: Undefined
|
||||
// Control Info: None
|
||||
// 6: Acknowledgement of Acknowledement (UMSG_ACKACK)
|
||||
// Add. Info: The ACK sequence number
|
||||
// Control Info: None
|
||||
// 7: Message Drop Request (UMSG_DROPREQ)
|
||||
// Add. Info: Message ID
|
||||
// Control Info: first sequence number of the message
|
||||
// last seqeunce number of the message
|
||||
// 8: Error Signal from the Peer Side (UMSG_PEERERROR)
|
||||
// Add. Info: Error code
|
||||
// Control Info: None
|
||||
// 0x7FFF: Explained by bits 16 - 31 (UMSG_EXT)
|
||||
//
|
||||
// bit 16 - 31:
|
||||
// This space is used for future expansion or user defined control packets.
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |1| Sequence Number a (first) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |0| Sequence Number b (last) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |0| Sequence Number (single) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// Loss List Field Coding:
|
||||
// For any consectutive lost seqeunce numbers that the differnece between
|
||||
// the last and first is more than 1, only record the first (a) and the
|
||||
// the last (b) sequence numbers in the loss list field, and modify the
|
||||
// the first bit of a to 1.
|
||||
// For any single loss or consectutive loss less than 2 packets, use
|
||||
// the original sequence numbers in the field.
|
||||
|
||||
|
||||
#include <cstring>
|
||||
#include "packet.h"
|
||||
#include "logging.h"
|
||||
|
||||
namespace srt_logging
|
||||
{
|
||||
extern Logger mglog;
|
||||
}
|
||||
using namespace srt_logging;
|
||||
|
||||
// Set up the aliases in the constructure
|
||||
CPacket::CPacket():
|
||||
__pad(),
|
||||
m_data_owned(false),
|
||||
m_iSeqNo((int32_t&)(m_nHeader[SRT_PH_SEQNO])),
|
||||
m_iMsgNo((int32_t&)(m_nHeader[SRT_PH_MSGNO])),
|
||||
m_iTimeStamp((int32_t&)(m_nHeader[SRT_PH_TIMESTAMP])),
|
||||
m_iID((int32_t&)(m_nHeader[SRT_PH_ID])),
|
||||
m_pcData((char*&)(m_PacketVector[PV_DATA].dataRef()))
|
||||
{
|
||||
m_nHeader.clear();
|
||||
|
||||
// The part at PV_HEADER will be always set to a builtin buffer
|
||||
// containing SRT header.
|
||||
m_PacketVector[PV_HEADER].set(m_nHeader.raw(), HDR_SIZE);
|
||||
|
||||
// The part at PV_DATA is zero-initialized. It should be
|
||||
// set (through m_pcData and setLength()) to some externally
|
||||
// provided buffer before calling CChannel::sendto().
|
||||
m_PacketVector[PV_DATA].set(NULL, 0);
|
||||
}
|
||||
|
||||
void CPacket::allocate(size_t alloc_buffer_size)
|
||||
{
|
||||
m_PacketVector[PV_DATA].set(new char[alloc_buffer_size], alloc_buffer_size);
|
||||
m_data_owned = true;
|
||||
}
|
||||
|
||||
void CPacket::deallocate()
|
||||
{
|
||||
if (m_data_owned)
|
||||
delete [] (char*)m_PacketVector[PV_DATA].data();
|
||||
m_PacketVector[PV_DATA].set(NULL, 0);
|
||||
}
|
||||
|
||||
CPacket::~CPacket()
|
||||
{
|
||||
// PV_HEADER is always owned, PV_DATA may use a "borrowed" buffer.
|
||||
// Delete the internal buffer only if it was declared as owned.
|
||||
if (m_data_owned)
|
||||
delete[](char*)m_PacketVector[PV_DATA].data();
|
||||
}
|
||||
|
||||
|
||||
size_t CPacket::getLength() const
|
||||
{
|
||||
return m_PacketVector[PV_DATA].size();
|
||||
}
|
||||
|
||||
void CPacket::setLength(size_t len)
|
||||
{
|
||||
m_PacketVector[PV_DATA].setLength(len);
|
||||
}
|
||||
|
||||
void CPacket::pack(UDTMessageType pkttype, const void* lparam, void* rparam, int size)
|
||||
{
|
||||
// Set (bit-0 = 1) and (bit-1~15 = type)
|
||||
setControl(pkttype);
|
||||
|
||||
// Set additional information and control information field
|
||||
switch (pkttype)
|
||||
{
|
||||
case UMSG_ACK: //0010 - Acknowledgement (ACK)
|
||||
// ACK packet seq. no.
|
||||
if (NULL != lparam)
|
||||
m_nHeader[SRT_PH_MSGNO] = *(int32_t *)lparam;
|
||||
|
||||
// data ACK seq. no.
|
||||
// optional: RTT (microsends), RTT variance (microseconds) advertised flow window size (packets), and estimated link capacity (packets per second)
|
||||
m_PacketVector[PV_DATA].set(rparam, size);
|
||||
|
||||
break;
|
||||
|
||||
case UMSG_ACKACK: //0110 - Acknowledgement of Acknowledgement (ACK-2)
|
||||
// ACK packet seq. no.
|
||||
m_nHeader[SRT_PH_MSGNO] = *(int32_t *)lparam;
|
||||
|
||||
// control info field should be none
|
||||
// but "writev" does not allow this
|
||||
m_PacketVector[PV_DATA].set((void *)&__pad, 4);
|
||||
|
||||
break;
|
||||
|
||||
case UMSG_LOSSREPORT: //0011 - Loss Report (NAK)
|
||||
// loss list
|
||||
m_PacketVector[PV_DATA].set(rparam, size);
|
||||
|
||||
break;
|
||||
|
||||
case UMSG_CGWARNING: //0100 - Congestion Warning
|
||||
// control info field should be none
|
||||
// but "writev" does not allow this
|
||||
m_PacketVector[PV_DATA].set((void *)&__pad, 4);
|
||||
|
||||
break;
|
||||
|
||||
case UMSG_KEEPALIVE: //0001 - Keep-alive
|
||||
// control info field should be none
|
||||
// but "writev" does not allow this
|
||||
m_PacketVector[PV_DATA].set((void *)&__pad, 4);
|
||||
|
||||
break;
|
||||
|
||||
case UMSG_HANDSHAKE: //0000 - Handshake
|
||||
// control info filed is handshake info
|
||||
m_PacketVector[PV_DATA].set(rparam, size);
|
||||
|
||||
break;
|
||||
|
||||
case UMSG_SHUTDOWN: //0101 - Shutdown
|
||||
// control info field should be none
|
||||
// but "writev" does not allow this
|
||||
m_PacketVector[PV_DATA].set((void *)&__pad, 4);
|
||||
|
||||
break;
|
||||
|
||||
case UMSG_DROPREQ: //0111 - Message Drop Request
|
||||
// msg id
|
||||
m_nHeader[SRT_PH_MSGNO] = *(int32_t *)lparam;
|
||||
|
||||
//first seq no, last seq no
|
||||
m_PacketVector[PV_DATA].set(rparam, size);
|
||||
|
||||
break;
|
||||
|
||||
case UMSG_PEERERROR: //1000 - Error Signal from the Peer Side
|
||||
// Error type
|
||||
m_nHeader[SRT_PH_MSGNO] = *(int32_t *)lparam;
|
||||
|
||||
// control info field should be none
|
||||
// but "writev" does not allow this
|
||||
m_PacketVector[PV_DATA].set((void *)&__pad, 4);
|
||||
|
||||
break;
|
||||
|
||||
case UMSG_EXT: //0x7FFF - Reserved for user defined control packets
|
||||
// for extended control packet
|
||||
// "lparam" contains the extended type information for bit 16 - 31
|
||||
// "rparam" is the control information
|
||||
m_nHeader[SRT_PH_SEQNO] |= *(int32_t *)lparam;
|
||||
|
||||
if (NULL != rparam)
|
||||
{
|
||||
m_PacketVector[PV_DATA].set(rparam, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_PacketVector[PV_DATA].set((void *)&__pad, 4);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
IOVector* CPacket::getPacketVector()
|
||||
{
|
||||
return m_PacketVector;
|
||||
}
|
||||
|
||||
UDTMessageType CPacket::getType() const
|
||||
{
|
||||
return UDTMessageType(SEQNO_MSGTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]));
|
||||
}
|
||||
|
||||
int CPacket::getExtendedType() const
|
||||
{
|
||||
return SEQNO_EXTTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]);
|
||||
}
|
||||
|
||||
int32_t CPacket::getAckSeqNo() const
|
||||
{
|
||||
// read additional information field
|
||||
// This field is used only in UMSG_ACK and UMSG_ACKACK,
|
||||
// so 'getAckSeqNo' symbolically defines the only use of it
|
||||
// in case of CONTROL PACKET.
|
||||
return m_nHeader[SRT_PH_MSGNO];
|
||||
}
|
||||
|
||||
uint16_t CPacket::getControlFlags() const
|
||||
{
|
||||
// This returns exactly the "extended type" value,
|
||||
// which is not used at all in case when the standard
|
||||
// type message is interpreted. This can be used to pass
|
||||
// additional special flags.
|
||||
return SEQNO_EXTTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]);
|
||||
}
|
||||
|
||||
PacketBoundary CPacket::getMsgBoundary() const
|
||||
{
|
||||
return PacketBoundary(MSGNO_PACKET_BOUNDARY::unwrap(m_nHeader[SRT_PH_MSGNO]));
|
||||
}
|
||||
|
||||
bool CPacket::getMsgOrderFlag() const
|
||||
{
|
||||
return 0!= MSGNO_PACKET_INORDER::unwrap(m_nHeader[SRT_PH_MSGNO]);
|
||||
}
|
||||
|
||||
int32_t CPacket::getMsgSeq(bool has_rexmit) const
|
||||
{
|
||||
if ( has_rexmit )
|
||||
{
|
||||
return MSGNO_SEQ::unwrap(m_nHeader[SRT_PH_MSGNO]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return MSGNO_SEQ_OLD::unwrap(m_nHeader[SRT_PH_MSGNO]);
|
||||
}
|
||||
}
|
||||
|
||||
bool CPacket::getRexmitFlag() const
|
||||
{
|
||||
// return false; //
|
||||
return 0 != MSGNO_REXMIT::unwrap(m_nHeader[SRT_PH_MSGNO]);
|
||||
}
|
||||
|
||||
EncryptionKeySpec CPacket::getMsgCryptoFlags() const
|
||||
{
|
||||
return EncryptionKeySpec(MSGNO_ENCKEYSPEC::unwrap(m_nHeader[SRT_PH_MSGNO]));
|
||||
}
|
||||
|
||||
// This is required as the encryption/decryption happens in place.
|
||||
// This is required to clear off the flags after decryption or set
|
||||
// crypto flags after encrypting a packet.
|
||||
void CPacket::setMsgCryptoFlags(EncryptionKeySpec spec)
|
||||
{
|
||||
int32_t clr_msgno = m_nHeader[SRT_PH_MSGNO] & ~MSGNO_ENCKEYSPEC::mask;
|
||||
m_nHeader[SRT_PH_MSGNO] = clr_msgno | EncryptionKeyBits(spec);
|
||||
}
|
||||
|
||||
/*
|
||||
Leaving old code for historical reasons. This is moved to CSRTCC.
|
||||
EncryptionStatus CPacket::encrypt(HaiCrypt_Handle hcrypto)
|
||||
{
|
||||
if ( !hcrypto )
|
||||
{
|
||||
LOGC(mglog.Error, log << "IPE: NULL crypto passed to CPacket::encrypt!");
|
||||
return ENCS_FAILED;
|
||||
}
|
||||
|
||||
int rc = HaiCrypt_Tx_Data(hcrypto, (uint8_t *)m_nHeader.raw(), (uint8_t *)m_pcData, m_PacketVector[PV_DATA].iov_len);
|
||||
if ( rc < 0 )
|
||||
{
|
||||
// -1: encryption failure
|
||||
// 0: key not received yet
|
||||
return ENCS_FAILED;
|
||||
} else if (rc > 0) {
|
||||
m_PacketVector[PV_DATA].iov_len = rc;
|
||||
}
|
||||
return ENCS_CLEAR;
|
||||
}
|
||||
|
||||
EncryptionStatus CPacket::decrypt(HaiCrypt_Handle hcrypto)
|
||||
{
|
||||
if (getMsgCryptoFlags() == EK_NOENC)
|
||||
{
|
||||
//HLOGC(mglog.Debug, log << "CPacket::decrypt: packet not encrypted");
|
||||
return ENCS_CLEAR; // not encrypted, no need do decrypt, no flags to be modified
|
||||
}
|
||||
|
||||
if (!hcrypto)
|
||||
{
|
||||
LOGC(mglog.Error, log << "IPE: NULL crypto passed to CPacket::decrypt!");
|
||||
return ENCS_FAILED; // "invalid argument" (leave encryption flags untouched)
|
||||
}
|
||||
|
||||
int rc = HaiCrypt_Rx_Data(hcrypto, (uint8_t *)m_nHeader.raw(), (uint8_t *)m_pcData, m_PacketVector[PV_DATA].iov_len);
|
||||
if ( rc <= 0 )
|
||||
{
|
||||
// -1: decryption failure
|
||||
// 0: key not received yet
|
||||
return ENCS_FAILED;
|
||||
}
|
||||
// Otherwise: rc == decrypted text length.
|
||||
m_PacketVector[PV_DATA].iov_len = rc; // In case clr txt size is different from cipher txt
|
||||
|
||||
// Decryption succeeded. Update flags.
|
||||
m_nHeader[SRT_PH_MSGNO] &= ~MSGNO_ENCKEYSPEC::mask; // sets EK_NOENC to ENCKEYSPEC bits.
|
||||
|
||||
return ENCS_CLEAR;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
uint32_t CPacket::getMsgTimeStamp() const
|
||||
{
|
||||
// SRT_DEBUG_TSBPD_WRAP may enable smaller timestamp for faster wraparoud handling tests
|
||||
return (uint32_t)m_nHeader[SRT_PH_TIMESTAMP] & TIMESTAMP_MASK;
|
||||
}
|
||||
|
||||
CPacket* CPacket::clone() const
|
||||
{
|
||||
CPacket* pkt = new CPacket;
|
||||
memcpy(pkt->m_nHeader, m_nHeader, HDR_SIZE);
|
||||
pkt->m_pcData = new char[m_PacketVector[PV_DATA].size()];
|
||||
memcpy(pkt->m_pcData, m_pcData, m_PacketVector[PV_DATA].size());
|
||||
pkt->m_PacketVector[PV_DATA].setLength(m_PacketVector[PV_DATA].size());
|
||||
|
||||
return pkt;
|
||||
}
|
||||
|
||||
// Useful for debugging
|
||||
std::string PacketMessageFlagStr(uint32_t msgno_field)
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
stringstream out;
|
||||
|
||||
static const char* const boundary [] = { "PB_SUBSEQUENT", "PB_LAST", "PB_FIRST", "PB_SOLO" };
|
||||
static const char* const order [] = { "ORD_RELAXED", "ORD_REQUIRED" };
|
||||
static const char* const crypto [] = { "EK_NOENC", "EK_EVEN", "EK_ODD", "EK*ERROR" };
|
||||
static const char* const rexmit [] = { "SN_ORIGINAL", "SN_REXMIT" };
|
||||
|
||||
out << boundary[MSGNO_PACKET_BOUNDARY::unwrap(msgno_field)] << " ";
|
||||
out << order[MSGNO_PACKET_INORDER::unwrap(msgno_field)] << " ";
|
||||
out << crypto[MSGNO_ENCKEYSPEC::unwrap(msgno_field)] << " ";
|
||||
out << rexmit[MSGNO_REXMIT::unwrap(msgno_field)];
|
||||
|
||||
return out.str();
|
||||
}
|
421
trunk/3rdparty/srt-1-fit/srtcore/packet.h
vendored
Normal file
421
trunk/3rdparty/srt-1-fit/srtcore/packet.h
vendored
Normal file
|
@ -0,0 +1,421 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Yunhong Gu, last updated 01/02/2011
|
||||
modified by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __UDT_PACKET_H__
|
||||
#define __UDT_PACKET_H__
|
||||
|
||||
#include "udt.h"
|
||||
#include "common.h"
|
||||
#include "utilities.h"
|
||||
#include "packetfilter_api.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// The purpose of the IOVector class is to proide a platform-independet interface
|
||||
// to the WSABUF on Windows and iovec on Linux, that can be easilly converted
|
||||
// to the native structure for use in WSARecvFrom() and recvmsg(...) functions
|
||||
class IOVector
|
||||
#ifdef _WIN32
|
||||
: public WSABUF
|
||||
#else
|
||||
: public iovec
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
|
||||
inline void set(void *buffer, size_t length)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
len = (ULONG)length;
|
||||
buf = (CHAR*)buffer;
|
||||
#else
|
||||
iov_base = (void*)buffer;
|
||||
iov_len = length;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline char*& dataRef()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return buf;
|
||||
#else
|
||||
return (char*&) iov_base;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline char* data()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return buf;
|
||||
#else
|
||||
return (char*)iov_base;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline size_t size() const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (size_t) len;
|
||||
#else
|
||||
return iov_len;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void setLength(size_t length)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
len = length;
|
||||
#else
|
||||
iov_len = length;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// To define packets in order in the buffer. This is public due to being used in buffer.
|
||||
enum PacketBoundary
|
||||
{
|
||||
PB_SUBSEQUENT = 0, // 00
|
||||
/// 01: last packet of a message
|
||||
PB_LAST = 1, // 01
|
||||
/// 10: first packet of a message
|
||||
PB_FIRST = 2, // 10
|
||||
/// 11: solo message packet
|
||||
PB_SOLO = 3, // 11
|
||||
};
|
||||
|
||||
// Breakdown of the PM_SEQNO field in the header:
|
||||
// C| X X ... X, where:
|
||||
typedef Bits<31> SEQNO_CONTROL;
|
||||
// 1|T T T T T T T T T T T T T T T|E E...E
|
||||
typedef Bits<30, 16> SEQNO_MSGTYPE;
|
||||
typedef Bits<15, 0> SEQNO_EXTTYPE;
|
||||
// 0|S S ... S
|
||||
typedef Bits<30, 0> SEQNO_VALUE;
|
||||
|
||||
// This bit cannot be used by SEQNO anyway, so it's additionally used
|
||||
// in LOSSREPORT data specification to define that this value is the
|
||||
// BEGIN value for a SEQNO range (to distinguish it from a SOLO loss SEQNO value).
|
||||
const int32_t LOSSDATA_SEQNO_RANGE_FIRST = SEQNO_CONTROL::mask;
|
||||
|
||||
// Just cosmetics for readability.
|
||||
const int32_t LOSSDATA_SEQNO_RANGE_LAST = 0, LOSSDATA_SEQNO_SOLO = 0;
|
||||
|
||||
inline int32_t CreateControlSeqNo(UDTMessageType type)
|
||||
{
|
||||
return SEQNO_CONTROL::mask | SEQNO_MSGTYPE::wrap(size_t(type));
|
||||
}
|
||||
|
||||
inline int32_t CreateControlExtSeqNo(int exttype)
|
||||
{
|
||||
return SEQNO_CONTROL::mask | SEQNO_MSGTYPE::wrap(size_t(UMSG_EXT)) | SEQNO_EXTTYPE::wrap(exttype);
|
||||
}
|
||||
|
||||
// MSGNO breakdown: B B|O|K K|R|M M M M M M M M M M...M
|
||||
typedef Bits<31, 30> MSGNO_PACKET_BOUNDARY;
|
||||
typedef Bits<29> MSGNO_PACKET_INORDER;
|
||||
typedef Bits<28, 27> MSGNO_ENCKEYSPEC;
|
||||
#if 1 // can block rexmit flag
|
||||
// New bit breakdown - rexmit flag supported.
|
||||
typedef Bits<26> MSGNO_REXMIT;
|
||||
typedef Bits<25, 0> MSGNO_SEQ;
|
||||
// Old bit breakdown - no rexmit flag
|
||||
typedef Bits<26, 0> MSGNO_SEQ_OLD;
|
||||
// This symbol is for older SRT version, where the peer does not support the MSGNO_REXMIT flag.
|
||||
// The message should be extracted as PMASK_MSGNO_SEQ, if REXMIT is supported, and PMASK_MSGNO_SEQ_OLD otherwise.
|
||||
|
||||
const uint32_t PACKET_SND_NORMAL = 0, PACKET_SND_REXMIT = MSGNO_REXMIT::mask;
|
||||
|
||||
#else
|
||||
// Old bit breakdown - no rexmit flag
|
||||
typedef Bits<26, 0> MSGNO_SEQ;
|
||||
#endif
|
||||
|
||||
|
||||
// constexpr in C++11 !
|
||||
inline int32_t PacketBoundaryBits(PacketBoundary o) { return MSGNO_PACKET_BOUNDARY::wrap(int32_t(o)); }
|
||||
|
||||
|
||||
enum EncryptionKeySpec
|
||||
{
|
||||
EK_NOENC = 0,
|
||||
EK_EVEN = 1,
|
||||
EK_ODD = 2
|
||||
};
|
||||
|
||||
enum EncryptionStatus
|
||||
{
|
||||
ENCS_CLEAR = 0,
|
||||
ENCS_FAILED = -1,
|
||||
ENCS_NOTSUP = -2
|
||||
};
|
||||
|
||||
const int32_t PMASK_MSGNO_ENCKEYSPEC = MSGNO_ENCKEYSPEC::mask;
|
||||
inline int32_t EncryptionKeyBits(EncryptionKeySpec f)
|
||||
{
|
||||
return MSGNO_ENCKEYSPEC::wrap(int32_t(f));
|
||||
}
|
||||
inline EncryptionKeySpec GetEncryptionKeySpec(int32_t msgno)
|
||||
{
|
||||
return EncryptionKeySpec(MSGNO_ENCKEYSPEC::unwrap(msgno));
|
||||
}
|
||||
|
||||
const int32_t PUMASK_SEQNO_PROBE = 0xF;
|
||||
|
||||
std::string PacketMessageFlagStr(uint32_t msgno_field);
|
||||
|
||||
class CChannel;
|
||||
|
||||
class CPacket
|
||||
{
|
||||
friend class CChannel;
|
||||
friend class CSndQueue;
|
||||
friend class CRcvQueue;
|
||||
|
||||
public:
|
||||
CPacket();
|
||||
~CPacket();
|
||||
|
||||
void allocate(size_t size);
|
||||
void deallocate();
|
||||
|
||||
/// Get the payload or the control information field length.
|
||||
/// @return the payload or the control information field length.
|
||||
|
||||
size_t getLength() const;
|
||||
|
||||
/// Set the payload or the control information field length.
|
||||
/// @param len [in] the payload or the control information field length.
|
||||
|
||||
void setLength(size_t len);
|
||||
|
||||
/// Pack a Control packet.
|
||||
/// @param pkttype [in] packet type filed.
|
||||
/// @param lparam [in] pointer to the first data structure, explained by the packet type.
|
||||
/// @param rparam [in] pointer to the second data structure, explained by the packet type.
|
||||
/// @param size [in] size of rparam, in number of bytes;
|
||||
|
||||
void pack(UDTMessageType pkttype, const void* lparam = NULL, void* rparam = NULL, int size = 0);
|
||||
|
||||
/// Read the packet vector.
|
||||
/// @return Pointer to the packet vector.
|
||||
|
||||
IOVector* getPacketVector();
|
||||
|
||||
uint32_t* getHeader() { return m_nHeader; }
|
||||
|
||||
/// Read the packet flag.
|
||||
/// @return packet flag (0 or 1).
|
||||
|
||||
// XXX DEPRECATED. Use isControl() instead
|
||||
ATR_DEPRECATED
|
||||
int getFlag() const
|
||||
{
|
||||
return isControl() ? 1 : 0;
|
||||
}
|
||||
|
||||
/// Read the packet type.
|
||||
/// @return packet type filed (000 ~ 111).
|
||||
|
||||
UDTMessageType getType() const;
|
||||
|
||||
bool isControl(UDTMessageType type) const
|
||||
{
|
||||
return isControl() && type == getType();
|
||||
}
|
||||
|
||||
bool isControl() const
|
||||
{
|
||||
// read bit 0
|
||||
return 0!= SEQNO_CONTROL::unwrap(m_nHeader[SRT_PH_SEQNO]);
|
||||
}
|
||||
|
||||
void setControl(UDTMessageType type)
|
||||
{
|
||||
m_nHeader[SRT_PH_SEQNO] = SEQNO_CONTROL::mask | SEQNO_MSGTYPE::wrap(type);
|
||||
}
|
||||
|
||||
/// Read the extended packet type.
|
||||
/// @return extended packet type filed (0x000 ~ 0xFFF).
|
||||
|
||||
int getExtendedType() const;
|
||||
|
||||
/// Read the ACK-2 seq. no.
|
||||
/// @return packet header field (bit 16~31).
|
||||
|
||||
int32_t getAckSeqNo() const;
|
||||
uint16_t getControlFlags() const;
|
||||
|
||||
// Note: this will return a "singular" value, if the packet
|
||||
// contains the control message
|
||||
int32_t getSeqNo() const
|
||||
{
|
||||
return m_nHeader[SRT_PH_SEQNO];
|
||||
}
|
||||
|
||||
/// Read the message boundary flag bit.
|
||||
/// @return packet header field [1] (bit 0~1).
|
||||
|
||||
PacketBoundary getMsgBoundary() const;
|
||||
|
||||
/// Read the message inorder delivery flag bit.
|
||||
/// @return packet header field [1] (bit 2).
|
||||
|
||||
bool getMsgOrderFlag() const;
|
||||
|
||||
/// Read the rexmit flag (true if the packet was sent due to retransmission).
|
||||
/// If the peer does not support retransmission flag, the current agent cannot use it as well
|
||||
/// (because the peer will understand this bit as a part of MSGNO field).
|
||||
|
||||
bool getRexmitFlag() const;
|
||||
|
||||
/// Read the message sequence number.
|
||||
/// @return packet header field [1]
|
||||
|
||||
int32_t getMsgSeq(bool has_rexmit = true) const;
|
||||
|
||||
/// Read the message crypto key bits.
|
||||
/// @return packet header field [1] (bit 3~4).
|
||||
|
||||
EncryptionKeySpec getMsgCryptoFlags() const;
|
||||
void setMsgCryptoFlags(EncryptionKeySpec spec);
|
||||
|
||||
/// Read the message time stamp.
|
||||
/// @return packet header field [2] (bit 0~31, bit 0-26 if SRT_DEBUG_TSBPD_WRAP).
|
||||
|
||||
uint32_t getMsgTimeStamp() const;
|
||||
|
||||
#ifdef SRT_DEBUG_TSBPD_WRAP //Receiver
|
||||
static const uint32_t MAX_TIMESTAMP = 0x07FFFFFF; //27 bit fast wraparound for tests (~2m15s)
|
||||
#else
|
||||
static const uint32_t MAX_TIMESTAMP = 0xFFFFFFFF; //Full 32 bit (01h11m35s)
|
||||
#endif
|
||||
|
||||
protected:
|
||||
static const uint32_t TIMESTAMP_MASK = MAX_TIMESTAMP; // this value to be also used as a mask
|
||||
public:
|
||||
|
||||
/// Clone this packet.
|
||||
/// @return Pointer to the new packet.
|
||||
|
||||
CPacket* clone() const;
|
||||
|
||||
enum PacketVectorFields
|
||||
{
|
||||
PV_HEADER = 0,
|
||||
PV_DATA = 1,
|
||||
|
||||
PV_SIZE = 2
|
||||
};
|
||||
|
||||
protected:
|
||||
// Length in bytes
|
||||
|
||||
// DynamicStruct is the same as array of given type and size, just it
|
||||
// enforces that you index it using a symbol from symbolic enum type, not by a bare integer.
|
||||
|
||||
typedef DynamicStruct<uint32_t, SRT_PH__SIZE, SrtPktHeaderFields> HEADER_TYPE;
|
||||
HEADER_TYPE m_nHeader; //< The 128-bit header field
|
||||
|
||||
// XXX NOTE: iovec here is not portable. On Windows there's a different
|
||||
// (although similar) structure defined, which means that this way the
|
||||
// Windows function that is an equivalent of `recvmsg` cannot be used.
|
||||
// For example, something like that:
|
||||
// class IoVector: public iovec { public: size_t size() { return iov_len; } char* data() { return iov_base; } };
|
||||
// class IoVector: public WSAMSG { public: size_t size() { return len; } char* data() { return buf; } };
|
||||
IOVector m_PacketVector[PV_SIZE]; //< The 2-demension vector of UDT packet [header, data]
|
||||
|
||||
int32_t __pad;
|
||||
bool m_data_owned;
|
||||
|
||||
protected:
|
||||
CPacket& operator=(const CPacket&);
|
||||
CPacket (const CPacket&);
|
||||
|
||||
public:
|
||||
|
||||
int32_t& m_iSeqNo; // alias: sequence number
|
||||
int32_t& m_iMsgNo; // alias: message number
|
||||
int32_t& m_iTimeStamp; // alias: timestamp
|
||||
int32_t& m_iID; // alias: socket ID
|
||||
char*& m_pcData; // alias: data/control information
|
||||
|
||||
//static const int m_iPktHdrSize; // packet header size
|
||||
static const size_t HDR_SIZE = sizeof(HEADER_TYPE); // packet header size = SRT_PH__SIZE * sizeof(uint32_t)
|
||||
|
||||
// Used in many computations
|
||||
// Actually this can be also calculated as: sizeof(struct ether_header) + sizeof(struct ip) + sizeof(struct udphdr).
|
||||
static const size_t UDP_HDR_SIZE = 28; // 20 bytes IPv4 + 8 bytes of UDP { u16 sport, dport, len, csum }.
|
||||
|
||||
static const size_t SRT_DATA_HDR_SIZE = UDP_HDR_SIZE + HDR_SIZE;
|
||||
|
||||
// Some well known data
|
||||
static const size_t ETH_MAX_MTU_SIZE = 1500;
|
||||
|
||||
// And derived
|
||||
static const size_t SRT_MAX_PAYLOAD_SIZE = ETH_MAX_MTU_SIZE - SRT_DATA_HDR_SIZE;
|
||||
|
||||
// Packet interface
|
||||
char* data() { return m_pcData; }
|
||||
const char* data() const { return m_pcData; }
|
||||
size_t size() const { return getLength(); }
|
||||
uint32_t header(SrtPktHeaderFields field) const { return m_nHeader[field]; }
|
||||
|
||||
std::string MessageFlagStr()
|
||||
#if ENABLE_LOGGING
|
||||
{ return PacketMessageFlagStr(m_nHeader[SRT_PH_MSGNO]); }
|
||||
#else
|
||||
{ return ""; }
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
#endif
|
294
trunk/3rdparty/srt-1-fit/srtcore/packetfilter.cpp
vendored
Normal file
294
trunk/3rdparty/srt-1-fit/srtcore/packetfilter.cpp
vendored
Normal file
|
@ -0,0 +1,294 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2019 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <iterator>
|
||||
|
||||
#include "packetfilter.h"
|
||||
#include "packetfilter_builtin.h"
|
||||
#include "core.h"
|
||||
#include "packet.h"
|
||||
#include "logging.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace srt_logging;
|
||||
|
||||
bool ParseFilterConfig(std::string s, SrtFilterConfig& out)
|
||||
{
|
||||
vector<string> parts;
|
||||
Split(s, ',', back_inserter(parts));
|
||||
|
||||
out.type = parts[0];
|
||||
PacketFilter::Factory* fac = PacketFilter::find(out.type);
|
||||
if (!fac)
|
||||
return false;
|
||||
|
||||
for (vector<string>::iterator i = parts.begin()+1; i != parts.end(); ++i)
|
||||
{
|
||||
vector<string> keyval;
|
||||
Split(*i, ':', back_inserter(keyval));
|
||||
if (keyval.size() != 2)
|
||||
return false;
|
||||
out.parameters[keyval[0]] = keyval[1];
|
||||
}
|
||||
|
||||
// Extract characteristic data
|
||||
out.extra_size = fac->ExtraSize();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct SortBySequence
|
||||
{
|
||||
bool operator()(const CUnit* u1, const CUnit* u2)
|
||||
{
|
||||
int32_t s1 = u1->m_Packet.getSeqNo();
|
||||
int32_t s2 = u2->m_Packet.getSeqNo();
|
||||
|
||||
return CSeqNo::seqcmp(s1, s2) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
void PacketFilter::receive(CUnit* unit, ref_t< std::vector<CUnit*> > r_incoming, ref_t<loss_seqs_t> r_loss_seqs)
|
||||
{
|
||||
const CPacket& rpkt = unit->m_Packet;
|
||||
|
||||
if (m_filter->receive(rpkt, *r_loss_seqs))
|
||||
{
|
||||
// For the sake of rebuilding MARK THIS UNIT GOOD, otherwise the
|
||||
// unit factory will supply it from getNextAvailUnit() as if it were not in use.
|
||||
unit->m_iFlag = CUnit::GOOD;
|
||||
HLOGC(mglog.Debug, log << "FILTER: PASSTHRU current packet %" << unit->m_Packet.getSeqNo());
|
||||
r_incoming.get().push_back(unit);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Packet not to be passthru, update stats
|
||||
CGuard lg(m_parent->m_StatsLock);
|
||||
++m_parent->m_stats.rcvFilterExtra;
|
||||
++m_parent->m_stats.rcvFilterExtraTotal;
|
||||
}
|
||||
|
||||
// r_loss_seqs enters empty into this function and can be only filled here.
|
||||
for (loss_seqs_t::iterator i = r_loss_seqs.get().begin();
|
||||
i != r_loss_seqs.get().end(); ++i)
|
||||
{
|
||||
// Sequences here are low-high, if there happens any negative distance
|
||||
// here, simply skip and report IPE.
|
||||
int dist = CSeqNo::seqoff(i->first, i->second) + 1;
|
||||
if (dist > 0)
|
||||
{
|
||||
CGuard lg(m_parent->m_StatsLock);
|
||||
m_parent->m_stats.rcvFilterLoss += dist;
|
||||
m_parent->m_stats.rcvFilterLossTotal += dist;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGC(mglog.Error, log << "FILTER: IPE: loss record: invalid loss: %"
|
||||
<< i->first << " - %" << i->second);
|
||||
}
|
||||
}
|
||||
|
||||
// Pack first recovered packets, if any.
|
||||
if (!m_provided.empty())
|
||||
{
|
||||
HLOGC(mglog.Debug, log << "FILTER: inserting REBUILT packets (" << m_provided.size() << "):");
|
||||
|
||||
size_t nsupply = m_provided.size();
|
||||
InsertRebuilt(*r_incoming, m_unitq);
|
||||
|
||||
CGuard lg(m_parent->m_StatsLock);
|
||||
m_parent->m_stats.rcvFilterSupply += nsupply;
|
||||
m_parent->m_stats.rcvFilterSupplyTotal += nsupply;
|
||||
}
|
||||
|
||||
// Now that all units have been filled as they should be,
|
||||
// SET THEM ALL FREE. This is because now it's up to the
|
||||
// buffer to decide as to whether it wants them or not.
|
||||
// Wanted units will be set GOOD flag, unwanted will remain
|
||||
// with FREE and therefore will be returned at the next
|
||||
// call to getNextAvailUnit().
|
||||
unit->m_iFlag = CUnit::FREE;
|
||||
vector<CUnit*>& inco = *r_incoming;
|
||||
for (vector<CUnit*>::iterator i = inco.begin(); i != inco.end(); ++i)
|
||||
{
|
||||
CUnit* u = *i;
|
||||
u->m_iFlag = CUnit::FREE;
|
||||
}
|
||||
|
||||
// Packets must be sorted by sequence number, ascending, in order
|
||||
// not to challenge the SRT's contiguity checker.
|
||||
sort(inco.begin(), inco.end(), SortBySequence());
|
||||
|
||||
// For now, report immediately the irrecoverable packets
|
||||
// from the row.
|
||||
|
||||
// Later, the `irrecover_row` or `irrecover_col` will be
|
||||
// reported only, depending on level settings. For example,
|
||||
// with default LATELY level, packets will be reported as
|
||||
// irrecoverable only when they are irrecoverable in the
|
||||
// vertical group.
|
||||
|
||||
// With "always", do not report any losses, SRT will simply check
|
||||
// them itself.
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
bool PacketFilter::packControlPacket(ref_t<CPacket> r_packet, int32_t seq, int kflg)
|
||||
{
|
||||
bool have = m_filter->packControlPacket(m_sndctlpkt, seq);
|
||||
if (!have)
|
||||
return false;
|
||||
|
||||
// Now this should be repacked back to CPacket.
|
||||
// The header must be copied, it's always part of CPacket.
|
||||
uint32_t* hdr = r_packet.get().getHeader();
|
||||
memcpy(hdr, m_sndctlpkt.hdr, SRT_PH__SIZE * sizeof(*hdr));
|
||||
|
||||
// The buffer can be assigned.
|
||||
r_packet.get().m_pcData = m_sndctlpkt.buffer;
|
||||
r_packet.get().setLength(m_sndctlpkt.length);
|
||||
|
||||
// This sets only the Packet Boundary flags, while all other things:
|
||||
// - Order
|
||||
// - Rexmit
|
||||
// - Crypto
|
||||
// - Message Number
|
||||
// will be set to 0/false
|
||||
r_packet.get().m_iMsgNo = MSGNO_PACKET_BOUNDARY::wrap(PB_SOLO);
|
||||
|
||||
// ... and then fix only the Crypto flags
|
||||
r_packet.get().setMsgCryptoFlags(EncryptionKeySpec(kflg));
|
||||
|
||||
// Don't set the ID, it will be later set for any kind of packet.
|
||||
// Write the timestamp clip into the timestamp field.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PacketFilter::InsertRebuilt(vector<CUnit*>& incoming, CUnitQueue* uq)
|
||||
{
|
||||
if (m_provided.empty())
|
||||
return;
|
||||
|
||||
for (vector<SrtPacket>::iterator i = m_provided.begin(); i != m_provided.end(); ++i)
|
||||
{
|
||||
CUnit* u = uq->getNextAvailUnit();
|
||||
if (!u)
|
||||
{
|
||||
LOGC(mglog.Error, log << "FILTER: LOCAL STORAGE DEPLETED. Can't return rebuilt packets.");
|
||||
break;
|
||||
}
|
||||
|
||||
// LOCK the unit as GOOD because otherwise the next
|
||||
// call to getNextAvailUnit will return THE SAME UNIT.
|
||||
u->m_iFlag = CUnit::GOOD;
|
||||
// After returning from this function, all units will be
|
||||
// set back to FREE so that the buffer can decide whether
|
||||
// it wants them or not.
|
||||
|
||||
CPacket& packet = u->m_Packet;
|
||||
|
||||
memcpy(packet.getHeader(), i->hdr, CPacket::HDR_SIZE);
|
||||
memcpy(packet.m_pcData, i->buffer, i->length);
|
||||
packet.setLength(i->length);
|
||||
|
||||
HLOGC(mglog.Debug, log << "FILTER: PROVIDING rebuilt packet %" << packet.getSeqNo());
|
||||
|
||||
incoming.push_back(u);
|
||||
}
|
||||
|
||||
m_provided.clear();
|
||||
}
|
||||
|
||||
bool PacketFilter::IsBuiltin(const string& s)
|
||||
{
|
||||
return builtin_filters.count(s);
|
||||
}
|
||||
|
||||
std::set<std::string> PacketFilter::builtin_filters;
|
||||
PacketFilter::filters_map_t PacketFilter::filters;
|
||||
|
||||
PacketFilter::Factory::~Factory()
|
||||
{
|
||||
}
|
||||
|
||||
void PacketFilter::globalInit()
|
||||
{
|
||||
// Add here builtin packet filters and mark them
|
||||
// as builtin. This will disallow users to register
|
||||
// external filters with the same name.
|
||||
|
||||
filters["fec"] = new Creator<FECFilterBuiltin>;
|
||||
builtin_filters.insert("fec");
|
||||
}
|
||||
|
||||
bool PacketFilter::configure(CUDT* parent, CUnitQueue* uq, const std::string& confstr)
|
||||
{
|
||||
m_parent = parent;
|
||||
|
||||
SrtFilterConfig cfg;
|
||||
if (!ParseFilterConfig(confstr, cfg))
|
||||
return false;
|
||||
|
||||
// Extract the "type" key from parameters, or use
|
||||
// builtin if lacking.
|
||||
filters_map_t::iterator selector = filters.find(cfg.type);
|
||||
if (selector == filters.end())
|
||||
return false;
|
||||
|
||||
SrtFilterInitializer init;
|
||||
init.socket_id = parent->socketID();
|
||||
init.snd_isn = parent->sndSeqNo();
|
||||
init.rcv_isn = parent->rcvSeqNo();
|
||||
init.payload_size = parent->OPT_PayloadSize();
|
||||
|
||||
|
||||
// Found a filter, so call the creation function
|
||||
m_filter = selector->second->Create(init, m_provided, confstr);
|
||||
if (!m_filter)
|
||||
return false;
|
||||
|
||||
m_unitq = uq;
|
||||
|
||||
// The filter should have pinned in all events
|
||||
// that are of its interest. It's stated that
|
||||
// it's ready after creation.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PacketFilter::correctConfig(const SrtFilterConfig& conf)
|
||||
{
|
||||
const string* pname = map_getp(conf.parameters, "type");
|
||||
|
||||
if (!pname)
|
||||
return true; // default, parameters ignored
|
||||
|
||||
if (*pname == "adaptive")
|
||||
return true;
|
||||
|
||||
filters_map_t::iterator x = filters.find(*pname);
|
||||
if (x == filters.end())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PacketFilter::~PacketFilter()
|
||||
{
|
||||
delete m_filter;
|
||||
}
|
||||
|
198
trunk/3rdparty/srt-1-fit/srtcore/packetfilter.h
vendored
Normal file
198
trunk/3rdparty/srt-1-fit/srtcore/packetfilter.h
vendored
Normal file
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2019 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef INC__PACKETFILTER_H
|
||||
#define INC__PACKETFILTER_H
|
||||
|
||||
#include <cstdlib>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "packet.h"
|
||||
#include "queue.h"
|
||||
#include "utilities.h"
|
||||
#include "packetfilter_api.h"
|
||||
|
||||
class PacketFilter
|
||||
{
|
||||
friend class SrtPacketFilterBase;
|
||||
|
||||
public:
|
||||
|
||||
typedef std::vector< std::pair<int32_t, int32_t> > loss_seqs_t;
|
||||
|
||||
typedef SrtPacketFilterBase* filter_create_t(const SrtFilterInitializer& init, std::vector<SrtPacket>&, const std::string& config);
|
||||
|
||||
private:
|
||||
friend bool ParseFilterConfig(std::string s, SrtFilterConfig& out);
|
||||
class Factory
|
||||
{
|
||||
public:
|
||||
virtual SrtPacketFilterBase* Create(const SrtFilterInitializer& init, std::vector<SrtPacket>& provided, const std::string& confstr) = 0;
|
||||
|
||||
// Characteristic data
|
||||
virtual size_t ExtraSize() = 0;
|
||||
|
||||
virtual ~Factory();
|
||||
};
|
||||
|
||||
template <class Target>
|
||||
class Creator: public Factory
|
||||
{
|
||||
virtual SrtPacketFilterBase* Create(const SrtFilterInitializer& init,
|
||||
std::vector<SrtPacket>& provided,
|
||||
const std::string& confstr) ATR_OVERRIDE
|
||||
{ return new Target(init, provided, confstr); }
|
||||
|
||||
// Import the extra size data
|
||||
virtual size_t ExtraSize() ATR_OVERRIDE { return Target::EXTRA_SIZE; }
|
||||
|
||||
public:
|
||||
Creator() {}
|
||||
virtual ~Creator() {}
|
||||
};
|
||||
|
||||
|
||||
// We need a private wrapper for the auto-pointer, can't use
|
||||
// std::unique_ptr here due to no C++11.
|
||||
struct ManagedPtr
|
||||
{
|
||||
Factory* f;
|
||||
mutable bool owns;
|
||||
|
||||
// Accept whatever
|
||||
ManagedPtr(Factory* ff): f(ff), owns(true) {}
|
||||
ManagedPtr(): f(NULL), owns(false) {}
|
||||
~ManagedPtr()
|
||||
{
|
||||
if (owns)
|
||||
delete f;
|
||||
}
|
||||
|
||||
void copy_internal(const ManagedPtr& other)
|
||||
{
|
||||
other.owns = false;
|
||||
f = other.f;
|
||||
owns = true;
|
||||
}
|
||||
|
||||
ManagedPtr(const ManagedPtr& other)
|
||||
{
|
||||
copy_internal(other);
|
||||
}
|
||||
|
||||
void operator=(const ManagedPtr& other)
|
||||
{
|
||||
if (owns)
|
||||
delete f;
|
||||
copy_internal(other);
|
||||
}
|
||||
|
||||
Factory* operator->() { return f; }
|
||||
Factory* get() { return f; }
|
||||
};
|
||||
|
||||
// The list of builtin names that are reserved.
|
||||
static std::set<std::string> builtin_filters;
|
||||
|
||||
// Temporarily changed to linear searching, until this is exposed
|
||||
// for a user-defined filter.
|
||||
typedef std::map<std::string, ManagedPtr> filters_map_t;
|
||||
static filters_map_t filters;
|
||||
|
||||
// This is a filter container.
|
||||
SrtPacketFilterBase* m_filter;
|
||||
void Check()
|
||||
{
|
||||
#if ENABLE_DEBUG
|
||||
if (!m_filter)
|
||||
abort();
|
||||
#endif
|
||||
// Don't do any check for now.
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
static void globalInit();
|
||||
|
||||
static bool IsBuiltin(const std::string&);
|
||||
|
||||
template <class NewFilter>
|
||||
static bool add(const std::string& name)
|
||||
{
|
||||
if (IsBuiltin(name))
|
||||
return false;
|
||||
|
||||
filters[name] = new Creator<NewFilter>;
|
||||
return true;
|
||||
}
|
||||
|
||||
static Factory* find(const std::string& type)
|
||||
{
|
||||
filters_map_t::iterator i = filters.find(type);
|
||||
if (i == filters.end())
|
||||
return NULL; // No matter what to return - this is "undefined behavior" to be prevented
|
||||
return i->second.get();
|
||||
}
|
||||
|
||||
// Filter is optional, so this check should be done always
|
||||
// manually.
|
||||
bool installed() const { return m_filter; }
|
||||
operator bool() const { return installed(); }
|
||||
|
||||
SrtPacketFilterBase* operator->() { Check(); return m_filter; }
|
||||
|
||||
// In the beginning it's initialized as first, builtin default.
|
||||
// Still, it will be created only when requested.
|
||||
PacketFilter(): m_filter(), m_parent(), m_sndctlpkt(0), m_unitq() {}
|
||||
|
||||
// Copy constructor - important when listener-spawning
|
||||
// Things being done:
|
||||
// 1. The filter is individual, so don't copy it. Set NULL.
|
||||
// 2. This will be configued anyway basing on possibly a new rule set.
|
||||
PacketFilter(const PacketFilter& source SRT_ATR_UNUSED): m_filter(), m_sndctlpkt(0), m_unitq() {}
|
||||
|
||||
// This function will be called by the parent CUDT
|
||||
// in appropriate time. It should select appropriate
|
||||
// filter basing on the value in selector, then
|
||||
// pin oneself in into CUDT for receiving event signals.
|
||||
bool configure(CUDT* parent, CUnitQueue* uq, const std::string& confstr);
|
||||
|
||||
static bool correctConfig(const SrtFilterConfig& c);
|
||||
|
||||
// Will delete the pinned in filter object.
|
||||
// This must be defined in *.cpp file due to virtual
|
||||
// destruction.
|
||||
~PacketFilter();
|
||||
|
||||
// Simple wrappers
|
||||
void feedSource(ref_t<CPacket> r_packet);
|
||||
SRT_ARQLevel arqLevel();
|
||||
bool packControlPacket(ref_t<CPacket> r_packet, int32_t seq, int kflg);
|
||||
void receive(CUnit* unit, ref_t< std::vector<CUnit*> > r_incoming, ref_t<loss_seqs_t> r_loss_seqs);
|
||||
|
||||
protected:
|
||||
void InsertRebuilt(std::vector<CUnit*>& incoming, CUnitQueue* uq);
|
||||
|
||||
CUDT* m_parent;
|
||||
|
||||
// Sender part
|
||||
SrtPacket m_sndctlpkt;
|
||||
|
||||
// Receiver part
|
||||
CUnitQueue* m_unitq;
|
||||
std::vector<SrtPacket> m_provided;
|
||||
};
|
||||
|
||||
|
||||
inline void PacketFilter::feedSource(ref_t<CPacket> r_packet) { SRT_ASSERT(m_filter); return m_filter->feedSource(*r_packet); }
|
||||
inline SRT_ARQLevel PacketFilter::arqLevel() { SRT_ASSERT(m_filter); return m_filter->arqLevel(); }
|
||||
|
||||
#endif
|
140
trunk/3rdparty/srt-1-fit/srtcore/packetfilter_api.h
vendored
Normal file
140
trunk/3rdparty/srt-1-fit/srtcore/packetfilter_api.h
vendored
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2019 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef INC__PACKETFILTER_API_H
|
||||
#define INC__PACKETFILTER_API_H
|
||||
|
||||
enum SrtPktHeaderFields
|
||||
{
|
||||
SRT_PH_SEQNO = 0, //< sequence number
|
||||
SRT_PH_MSGNO = 1, //< message number
|
||||
SRT_PH_TIMESTAMP = 2, //< time stamp
|
||||
SRT_PH_ID = 3, //< socket ID
|
||||
|
||||
// Must be the last value - this is size of all, not a field id
|
||||
SRT_PH__SIZE
|
||||
};
|
||||
|
||||
|
||||
enum SRT_ARQLevel
|
||||
{
|
||||
SRT_ARQ_NEVER, //< Never send LOSSREPORT
|
||||
SRT_ARQ_ONREQ, //< Only record the loss, but report only those that are returned in receive()
|
||||
SRT_ARQ_ALWAYS, //< always send LOSSREPORT immediately after detecting a loss
|
||||
};
|
||||
|
||||
|
||||
struct SrtFilterConfig
|
||||
{
|
||||
std::string type;
|
||||
std::map<std::string, std::string> parameters;
|
||||
size_t extra_size; // needed for filter option check against payload size
|
||||
};
|
||||
|
||||
struct SrtFilterInitializer
|
||||
{
|
||||
SRTSOCKET socket_id;
|
||||
int32_t snd_isn;
|
||||
int32_t rcv_isn;
|
||||
size_t payload_size;
|
||||
};
|
||||
|
||||
struct SrtPacket
|
||||
{
|
||||
uint32_t hdr[SRT_PH__SIZE];
|
||||
char buffer[SRT_LIVE_MAX_PLSIZE];
|
||||
size_t length;
|
||||
|
||||
SrtPacket(size_t size): length(size)
|
||||
{
|
||||
memset(hdr, 0, sizeof(hdr));
|
||||
}
|
||||
|
||||
uint32_t header(SrtPktHeaderFields field) { return hdr[field]; }
|
||||
char* data() { return buffer; }
|
||||
const char* data() const { return buffer; }
|
||||
size_t size() const { return length; }
|
||||
};
|
||||
|
||||
|
||||
bool ParseFilterConfig(std::string s, SrtFilterConfig& out);
|
||||
|
||||
|
||||
class SrtPacketFilterBase
|
||||
{
|
||||
SrtFilterInitializer initParams;
|
||||
|
||||
protected:
|
||||
|
||||
SRTSOCKET socketID() const { return initParams.socket_id; }
|
||||
int32_t sndISN() const { return initParams.snd_isn; }
|
||||
int32_t rcvISN() const { return initParams.rcv_isn; }
|
||||
size_t payloadSize() const { return initParams.payload_size; }
|
||||
|
||||
friend class PacketFilter;
|
||||
|
||||
// Beside the size of the rows, special values:
|
||||
// 0: if you have 0 specified for rows, there are only columns
|
||||
// -1: Only during the handshake, use the value specified by peer.
|
||||
// -N: The N value still specifies the size, but in particular
|
||||
// dimension there is no filter control packet formed nor expected.
|
||||
|
||||
public:
|
||||
|
||||
typedef std::vector< std::pair<int32_t, int32_t> > loss_seqs_t;
|
||||
|
||||
protected:
|
||||
|
||||
SrtPacketFilterBase(const SrtFilterInitializer& i): initParams(i)
|
||||
{
|
||||
}
|
||||
|
||||
// Sender side
|
||||
|
||||
/// This function creates and stores the filter control packet with
|
||||
/// a prediction to be immediately sent. This is called in the function
|
||||
/// that normally is prepared for extracting a data packet from the sender
|
||||
/// buffer and send it over the channel. The returned value informs the
|
||||
/// caller whether the control packet was available and therefore provided.
|
||||
/// @param [OUT] packet Target place where the packet should be stored
|
||||
/// @param [IN] seq Sequence number of the packet last requested for sending
|
||||
/// @return true if the control packet has been provided
|
||||
virtual bool packControlPacket(SrtPacket& packet, int32_t seq) = 0;
|
||||
|
||||
/// This is called at the moment when the sender queue decided to pick up
|
||||
/// a new packet from the scheduled packets. This should be then used to
|
||||
/// continue filling the group, possibly followed by final calculating the
|
||||
/// control packet ready to send. The packet received by this function is
|
||||
/// potentially allowed to be modified.
|
||||
/// @param [INOUT] packet The packet about to send
|
||||
virtual void feedSource(CPacket& packet) = 0;
|
||||
|
||||
|
||||
// Receiver side
|
||||
|
||||
// This function is called at the moment when a new data packet has
|
||||
// arrived (no matter if subsequent or recovered). The 'state' value
|
||||
// defines the configured level of loss state required to send the
|
||||
// loss report.
|
||||
virtual bool receive(const CPacket& pkt, loss_seqs_t& loss_seqs) = 0;
|
||||
|
||||
// Backward configuration.
|
||||
// This should have some stable value after the configuration is parsed,
|
||||
// and it should be a stable value set ONCE, after the filter module is ready.
|
||||
virtual SRT_ARQLevel arqLevel() = 0;
|
||||
|
||||
virtual ~SrtPacketFilterBase()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
18
trunk/3rdparty/srt-1-fit/srtcore/packetfilter_builtin.h
vendored
Normal file
18
trunk/3rdparty/srt-1-fit/srtcore/packetfilter_builtin.h
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2019 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef INC__PACKETFILTER_BUILTIN_H
|
||||
#define INC__PACKETFILTER_BUILTIN_H
|
||||
|
||||
// Integration header
|
||||
#include "fec.h"
|
||||
|
||||
#endif
|
34
trunk/3rdparty/srt-1-fit/srtcore/platform_sys.h
vendored
Normal file
34
trunk/3rdparty/srt-1-fit/srtcore/platform_sys.h
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
#ifndef INC__PLATFORM_SYS_H
|
||||
#define INC__PLATFORM_SYS_H
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <ws2ipdef.h>
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(disable:4251)
|
||||
#endif
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
#endif
|
1684
trunk/3rdparty/srt-1-fit/srtcore/queue.cpp
vendored
Normal file
1684
trunk/3rdparty/srt-1-fit/srtcore/queue.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
548
trunk/3rdparty/srt-1-fit/srtcore/queue.h
vendored
Normal file
548
trunk/3rdparty/srt-1-fit/srtcore/queue.h
vendored
Normal file
|
@ -0,0 +1,548 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Yunhong Gu, last updated 01/12/2011
|
||||
modified by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#ifndef __UDT_QUEUE_H__
|
||||
#define __UDT_QUEUE_H__
|
||||
|
||||
#include "channel.h"
|
||||
#include "common.h"
|
||||
#include "packet.h"
|
||||
#include "netinet_any.h"
|
||||
#include "utilities.h"
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
||||
class CUDT;
|
||||
|
||||
struct CUnit
|
||||
{
|
||||
CPacket m_Packet; // packet
|
||||
enum Flag { FREE = 0, GOOD = 1, PASSACK = 2, DROPPED = 3 };
|
||||
Flag m_iFlag; // 0: free, 1: occupied, 2: msg read but not freed (out-of-order), 3: msg dropped
|
||||
};
|
||||
|
||||
class CUnitQueue
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
CUnitQueue();
|
||||
~CUnitQueue();
|
||||
|
||||
public: // Storage size operations
|
||||
|
||||
/// Initialize the unit queue.
|
||||
/// @param [in] size queue size
|
||||
/// @param [in] mss maximum segment size
|
||||
/// @param [in] version IP version
|
||||
/// @return 0: success, -1: failure.
|
||||
|
||||
int init(int size, int mss, int version);
|
||||
|
||||
/// Increase (double) the unit queue size.
|
||||
/// @return 0: success, -1: failure.
|
||||
|
||||
int increase();
|
||||
|
||||
/// Decrease (halve) the unit queue size.
|
||||
/// @return 0: success, -1: failure.
|
||||
|
||||
int shrink();
|
||||
|
||||
public: // Operations on units
|
||||
|
||||
/// find an available unit for incoming packet.
|
||||
/// @return Pointer to the available unit, NULL if not found.
|
||||
|
||||
CUnit* getNextAvailUnit();
|
||||
|
||||
|
||||
void makeUnitFree(CUnit * unit);
|
||||
|
||||
void makeUnitGood(CUnit * unit);
|
||||
|
||||
public:
|
||||
|
||||
inline int getIPversion() const { return m_iIPversion; }
|
||||
|
||||
|
||||
private:
|
||||
struct CQEntry
|
||||
{
|
||||
CUnit* m_pUnit; // unit queue
|
||||
char* m_pBuffer; // data buffer
|
||||
int m_iSize; // size of each queue
|
||||
|
||||
CQEntry* m_pNext;
|
||||
}
|
||||
*m_pQEntry, // pointer to the first unit queue
|
||||
*m_pCurrQueue, // pointer to the current available queue
|
||||
*m_pLastQueue; // pointer to the last unit queue
|
||||
|
||||
CUnit* m_pAvailUnit; // recent available unit
|
||||
|
||||
int m_iSize; // total size of the unit queue, in number of packets
|
||||
int m_iCount; // total number of valid packets in the queue
|
||||
|
||||
int m_iMSS; // unit buffer size
|
||||
int m_iIPversion; // IP version
|
||||
|
||||
private:
|
||||
CUnitQueue(const CUnitQueue&);
|
||||
CUnitQueue& operator=(const CUnitQueue&);
|
||||
};
|
||||
|
||||
struct CSNode
|
||||
{
|
||||
CUDT* m_pUDT; // Pointer to the instance of CUDT socket
|
||||
uint64_t m_llTimeStamp_tk; // Time Stamp
|
||||
|
||||
int m_iHeapLoc; // location on the heap, -1 means not on the heap
|
||||
};
|
||||
|
||||
class CSndUList
|
||||
{
|
||||
friend class CSndQueue;
|
||||
|
||||
public:
|
||||
CSndUList();
|
||||
~CSndUList();
|
||||
|
||||
public:
|
||||
|
||||
enum EReschedule { DONT_RESCHEDULE = 0, DO_RESCHEDULE = 1 };
|
||||
|
||||
static EReschedule rescheduleIf(bool cond) { return cond ? DO_RESCHEDULE : DONT_RESCHEDULE; }
|
||||
|
||||
/// Update the timestamp of the UDT instance on the list.
|
||||
/// @param [in] u pointer to the UDT instance
|
||||
/// @param [in] reschedule if the timestamp should be rescheduled
|
||||
|
||||
void update(const CUDT* u, EReschedule reschedule);
|
||||
|
||||
/// Retrieve the next packet and peer address from the first entry, and reschedule it in the queue.
|
||||
/// @param [out] addr destination address of the next packet
|
||||
/// @param [out] pkt the next packet to be sent
|
||||
/// @return 1 if successfully retrieved, -1 if no packet found.
|
||||
|
||||
int pop(sockaddr*& addr, CPacket& pkt);
|
||||
|
||||
/// Remove UDT instance from the list.
|
||||
/// @param [in] u pointer to the UDT instance
|
||||
|
||||
void remove(const CUDT* u);
|
||||
|
||||
/// Retrieve the next scheduled processing time.
|
||||
/// @return Scheduled processing time of the first UDT socket in the list.
|
||||
|
||||
uint64_t getNextProcTime();
|
||||
|
||||
private:
|
||||
|
||||
/// Doubles the size of the list.
|
||||
///
|
||||
void realloc_();
|
||||
|
||||
/// Insert a new UDT instance into the list with realloc if required.
|
||||
///
|
||||
/// @param [in] ts time stamp: next processing time
|
||||
/// @param [in] u pointer to the UDT instance
|
||||
void insert_(int64_t ts, const CUDT* u);
|
||||
|
||||
/// Insert a new UDT instance into the list without realloc.
|
||||
/// Should be called if there is a gauranteed space for the element.
|
||||
///
|
||||
/// @param [in] ts time stamp: next processing time
|
||||
/// @param [in] u pointer to the UDT instance
|
||||
void insert_norealloc_(int64_t ts, const CUDT* u);
|
||||
|
||||
void remove_(const CUDT* u);
|
||||
|
||||
private:
|
||||
CSNode** m_pHeap; // The heap array
|
||||
int m_iArrayLength; // physical length of the array
|
||||
int m_iLastEntry; // position of last entry on the heap array
|
||||
|
||||
pthread_mutex_t m_ListLock;
|
||||
|
||||
pthread_mutex_t* m_pWindowLock;
|
||||
pthread_cond_t* m_pWindowCond;
|
||||
|
||||
CTimer* m_pTimer;
|
||||
|
||||
private:
|
||||
CSndUList(const CSndUList&);
|
||||
CSndUList& operator=(const CSndUList&);
|
||||
};
|
||||
|
||||
struct CRNode
|
||||
{
|
||||
CUDT* m_pUDT; // Pointer to the instance of CUDT socket
|
||||
uint64_t m_llTimeStamp_tk; // Time Stamp
|
||||
|
||||
CRNode* m_pPrev; // previous link
|
||||
CRNode* m_pNext; // next link
|
||||
|
||||
bool m_bOnList; // if the node is already on the list
|
||||
};
|
||||
|
||||
class CRcvUList
|
||||
{
|
||||
public:
|
||||
CRcvUList();
|
||||
~CRcvUList();
|
||||
|
||||
public:
|
||||
|
||||
/// Insert a new UDT instance to the list.
|
||||
/// @param [in] u pointer to the UDT instance
|
||||
|
||||
void insert(const CUDT* u);
|
||||
|
||||
/// Remove the UDT instance from the list.
|
||||
/// @param [in] u pointer to the UDT instance
|
||||
|
||||
void remove(const CUDT* u);
|
||||
|
||||
/// Move the UDT instance to the end of the list, if it already exists; otherwise, do nothing.
|
||||
/// @param [in] u pointer to the UDT instance
|
||||
|
||||
void update(const CUDT* u);
|
||||
|
||||
public:
|
||||
CRNode* m_pUList; // the head node
|
||||
|
||||
private:
|
||||
CRNode* m_pLast; // the last node
|
||||
|
||||
private:
|
||||
CRcvUList(const CRcvUList&);
|
||||
CRcvUList& operator=(const CRcvUList&);
|
||||
};
|
||||
|
||||
class CHash
|
||||
{
|
||||
public:
|
||||
CHash();
|
||||
~CHash();
|
||||
|
||||
public:
|
||||
|
||||
/// Initialize the hash table.
|
||||
/// @param [in] size hash table size
|
||||
|
||||
void init(int size);
|
||||
|
||||
/// Look for a UDT instance from the hash table.
|
||||
/// @param [in] id socket ID
|
||||
/// @return Pointer to a UDT instance, or NULL if not found.
|
||||
|
||||
CUDT* lookup(int32_t id);
|
||||
|
||||
/// Insert an entry to the hash table.
|
||||
/// @param [in] id socket ID
|
||||
/// @param [in] u pointer to the UDT instance
|
||||
|
||||
void insert(int32_t id, CUDT* u);
|
||||
|
||||
/// Remove an entry from the hash table.
|
||||
/// @param [in] id socket ID
|
||||
|
||||
void remove(int32_t id);
|
||||
|
||||
private:
|
||||
struct CBucket
|
||||
{
|
||||
int32_t m_iID; // Socket ID
|
||||
CUDT* m_pUDT; // Socket instance
|
||||
|
||||
CBucket* m_pNext; // next bucket
|
||||
} **m_pBucket; // list of buckets (the hash table)
|
||||
|
||||
int m_iHashSize; // size of hash table
|
||||
|
||||
private:
|
||||
CHash(const CHash&);
|
||||
CHash& operator=(const CHash&);
|
||||
};
|
||||
|
||||
class CRendezvousQueue
|
||||
{
|
||||
public:
|
||||
CRendezvousQueue();
|
||||
~CRendezvousQueue();
|
||||
|
||||
public:
|
||||
void insert(const SRTSOCKET& id, CUDT* u, int ipv, const sockaddr* addr, uint64_t ttl);
|
||||
|
||||
// The should_lock parameter is given here to state as to whether
|
||||
// the lock should be applied here. If called from some internals
|
||||
// and the lock IS ALREADY APPLIED, use false here to prevent
|
||||
// double locking and deadlock in result.
|
||||
void remove(const SRTSOCKET& id, bool should_lock);
|
||||
CUDT* retrieve(const sockaddr* addr, ref_t<SRTSOCKET> id);
|
||||
|
||||
void updateConnStatus(EReadStatus rst, EConnectStatus, const CPacket& response);
|
||||
|
||||
private:
|
||||
struct CRL
|
||||
{
|
||||
SRTSOCKET m_iID; // UDT socket ID (self)
|
||||
CUDT* m_pUDT; // UDT instance
|
||||
int m_iIPversion; // IP version
|
||||
sockaddr* m_pPeerAddr; // UDT sonnection peer address
|
||||
uint64_t m_ullTTL; // the time that this request expires
|
||||
};
|
||||
std::list<CRL> m_lRendezvousID; // The sockets currently in rendezvous mode
|
||||
|
||||
pthread_mutex_t m_RIDVectorLock;
|
||||
};
|
||||
|
||||
class CSndQueue
|
||||
{
|
||||
friend class CUDT;
|
||||
friend class CUDTUnited;
|
||||
|
||||
public:
|
||||
CSndQueue();
|
||||
~CSndQueue();
|
||||
|
||||
public:
|
||||
|
||||
// XXX There's currently no way to access the socket ID set for
|
||||
// whatever the queue is currently working for. Required to find
|
||||
// some way to do this, possibly by having a "reverse pointer".
|
||||
// Currently just "unimplemented".
|
||||
std::string CONID() const { return ""; }
|
||||
|
||||
/// Initialize the sending queue.
|
||||
/// @param [in] c UDP channel to be associated to the queue
|
||||
/// @param [in] t Timer
|
||||
|
||||
void init(CChannel* c, CTimer* t);
|
||||
|
||||
/// Send out a packet to a given address.
|
||||
/// @param [in] addr destination address
|
||||
/// @param [in] packet packet to be sent out
|
||||
/// @return Size of data sent out.
|
||||
|
||||
int sendto(const sockaddr* addr, CPacket& packet);
|
||||
|
||||
#ifdef SRT_ENABLE_IPOPTS
|
||||
/// Get the IP TTL.
|
||||
/// @param [in] ttl IP Time To Live.
|
||||
/// @return TTL.
|
||||
|
||||
int getIpTTL() const;
|
||||
|
||||
/// Get the IP Type of Service.
|
||||
/// @return ToS.
|
||||
|
||||
int getIpToS() const;
|
||||
#endif
|
||||
|
||||
int ioctlQuery(int type) const { return m_pChannel->ioctlQuery(type); }
|
||||
int sockoptQuery(int level, int type) const { return m_pChannel->sockoptQuery(level, type); }
|
||||
|
||||
void setClosing()
|
||||
{
|
||||
m_bClosing = true;
|
||||
}
|
||||
|
||||
private:
|
||||
static void* worker(void* param);
|
||||
pthread_t m_WorkerThread;
|
||||
|
||||
|
||||
private:
|
||||
CSndUList* m_pSndUList; // List of UDT instances for data sending
|
||||
CChannel* m_pChannel; // The UDP channel for data sending
|
||||
CTimer* m_pTimer; // Timing facility
|
||||
|
||||
pthread_mutex_t m_WindowLock;
|
||||
pthread_cond_t m_WindowCond;
|
||||
|
||||
volatile bool m_bClosing; // closing the worker
|
||||
|
||||
#if defined(SRT_DEBUG_SNDQ_HIGHRATE)//>>debug high freq worker
|
||||
uint64_t m_ullDbgPeriod;
|
||||
uint64_t m_ullDbgTime;
|
||||
struct {
|
||||
unsigned long lIteration; //
|
||||
unsigned long lSleepTo; //SleepTo
|
||||
unsigned long lNotReadyPop; //Continue
|
||||
unsigned long lSendTo;
|
||||
unsigned long lNotReadyTs;
|
||||
unsigned long lCondWait; //block on m_WindowCond
|
||||
} m_WorkerStats;
|
||||
#endif /* SRT_DEBUG_SNDQ_HIGHRATE */
|
||||
|
||||
private:
|
||||
CSndQueue(const CSndQueue&);
|
||||
CSndQueue& operator=(const CSndQueue&);
|
||||
};
|
||||
|
||||
class CRcvQueue
|
||||
{
|
||||
friend class CUDT;
|
||||
friend class CUDTUnited;
|
||||
|
||||
public:
|
||||
CRcvQueue();
|
||||
~CRcvQueue();
|
||||
|
||||
public:
|
||||
|
||||
// XXX There's currently no way to access the socket ID set for
|
||||
// whatever the queue is currently working. Required to find
|
||||
// some way to do this, possibly by having a "reverse pointer".
|
||||
// Currently just "unimplemented".
|
||||
std::string CONID() const { return ""; }
|
||||
|
||||
/// Initialize the receiving queue.
|
||||
/// @param [in] size queue size
|
||||
/// @param [in] mss maximum packet size
|
||||
/// @param [in] version IP version
|
||||
/// @param [in] hsize hash table size
|
||||
/// @param [in] c UDP channel to be associated to the queue
|
||||
/// @param [in] t timer
|
||||
|
||||
void init(int size, int payload, int version, int hsize, CChannel* c, CTimer* t);
|
||||
|
||||
/// Read a packet for a specific UDT socket id.
|
||||
/// @param [in] id Socket ID
|
||||
/// @param [out] packet received packet
|
||||
/// @return Data size of the packet
|
||||
|
||||
int recvfrom(int32_t id, ref_t<CPacket> packet);
|
||||
|
||||
void setClosing()
|
||||
{
|
||||
m_bClosing = true;
|
||||
}
|
||||
|
||||
private:
|
||||
static void* worker(void* param);
|
||||
pthread_t m_WorkerThread;
|
||||
// Subroutines of worker
|
||||
EReadStatus worker_RetrieveUnit(ref_t<int32_t> id, ref_t<CUnit*> unit, sockaddr* sa);
|
||||
EConnectStatus worker_ProcessConnectionRequest(CUnit* unit, const sockaddr* sa);
|
||||
EConnectStatus worker_TryAsyncRend_OrStore(int32_t id, CUnit* unit, const sockaddr* sa);
|
||||
EConnectStatus worker_ProcessAddressedPacket(int32_t id, CUnit* unit, const sockaddr* sa);
|
||||
|
||||
private:
|
||||
CUnitQueue m_UnitQueue; // The received packet queue
|
||||
|
||||
CRcvUList* m_pRcvUList; // List of UDT instances that will read packets from the queue
|
||||
CHash* m_pHash; // Hash table for UDT socket looking up
|
||||
CChannel* m_pChannel; // UDP channel for receving packets
|
||||
CTimer* m_pTimer; // shared timer with the snd queue
|
||||
|
||||
int m_iPayloadSize; // packet payload size
|
||||
|
||||
volatile bool m_bClosing; // closing the worker
|
||||
|
||||
private:
|
||||
int setListener(CUDT* u);
|
||||
void removeListener(const CUDT* u);
|
||||
|
||||
void registerConnector(const SRTSOCKET& id, CUDT* u, int ipv, const sockaddr* addr, uint64_t ttl);
|
||||
void removeConnector(const SRTSOCKET& id, bool should_lock = true);
|
||||
|
||||
void setNewEntry(CUDT* u);
|
||||
bool ifNewEntry();
|
||||
CUDT* getNewEntry();
|
||||
|
||||
void storePkt(int32_t id, CPacket* pkt);
|
||||
|
||||
private:
|
||||
pthread_mutex_t m_LSLock;
|
||||
CUDT* m_pListener; // pointer to the (unique, if any) listening UDT entity
|
||||
CRendezvousQueue* m_pRendezvousQueue; // The list of sockets in rendezvous mode
|
||||
|
||||
std::vector<CUDT*> m_vNewEntry; // newly added entries, to be inserted
|
||||
pthread_mutex_t m_IDLock;
|
||||
|
||||
std::map<int32_t, std::queue<CPacket*> > m_mBuffer; // temporary buffer for rendezvous connection request
|
||||
pthread_mutex_t m_PassLock;
|
||||
pthread_cond_t m_PassCond;
|
||||
|
||||
private:
|
||||
CRcvQueue(const CRcvQueue&);
|
||||
CRcvQueue& operator=(const CRcvQueue&);
|
||||
};
|
||||
|
||||
struct CMultiplexer
|
||||
{
|
||||
CSndQueue* m_pSndQueue; // The sending queue
|
||||
CRcvQueue* m_pRcvQueue; // The receiving queue
|
||||
CChannel* m_pChannel; // The UDP channel for sending and receiving
|
||||
CTimer* m_pTimer; // The timer
|
||||
|
||||
int m_iPort; // The UDP port number of this multiplexer
|
||||
int m_iIPversion; // IP version
|
||||
#ifdef SRT_ENABLE_IPOPTS
|
||||
int m_iIpTTL;
|
||||
int m_iIpToS;
|
||||
#endif
|
||||
int m_iMSS; // Maximum Segment Size
|
||||
int m_iRefCount; // number of UDT instances that are associated with this multiplexer
|
||||
int m_iIpV6Only; // IPV6_V6ONLY option
|
||||
bool m_bReusable; // if this one can be shared with others
|
||||
|
||||
int m_iID; // multiplexer ID
|
||||
};
|
||||
|
||||
#endif
|
747
trunk/3rdparty/srt-1-fit/srtcore/srt.h
vendored
Normal file
747
trunk/3rdparty/srt-1-fit/srtcore/srt.h
vendored
Normal file
|
@ -0,0 +1,747 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef INC__SRTC_H
|
||||
#define INC__SRTC_H
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#include "platform_sys.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "srt4udt.h"
|
||||
#include "logging_api.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//if compiling on VC6.0 or pre-WindowsXP systems
|
||||
//use -DLEGACY_WIN32
|
||||
|
||||
//if compiling with MinGW, it only works on XP or above
|
||||
//use -D_WIN32_WINNT=0x0501
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef __MINGW__
|
||||
// Explicitly define 32-bit and 64-bit numbers
|
||||
typedef __int32 int32_t;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
#ifndef LEGACY_WIN32
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#else
|
||||
// VC 6.0 does not support unsigned __int64: may cause potential problems.
|
||||
typedef __int64 uint64_t;
|
||||
#endif
|
||||
|
||||
#ifdef SRT_DYNAMIC
|
||||
#ifdef SRT_EXPORTS
|
||||
#define SRT_API __declspec(dllexport)
|
||||
#else
|
||||
#define SRT_API __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
#define SRT_API
|
||||
#endif
|
||||
#else // __MINGW__
|
||||
#define SRT_API
|
||||
#endif
|
||||
#else
|
||||
#define SRT_API __attribute__ ((visibility("default")))
|
||||
#endif
|
||||
|
||||
|
||||
// For feature tests if you need.
|
||||
// You can use these constants with SRTO_MINVERSION option.
|
||||
#define SRT_VERSION_FEAT_HSv5 0x010300
|
||||
|
||||
// When compiling in C++17 mode, use the standard C++17 attributes
|
||||
// (out of these, only [[deprecated]] is supported in C++14, so
|
||||
// for all lesser standard use compiler-specific attributes).
|
||||
#if defined(SRT_NO_DEPRECATED)
|
||||
|
||||
#define SRT_ATR_UNUSED
|
||||
#define SRT_ATR_DEPRECATED
|
||||
#define SRT_ATR_NODISCARD
|
||||
|
||||
#elif defined(__cplusplus) && __cplusplus > 201406
|
||||
|
||||
#define SRT_ATR_UNUSED [[maybe_unused]]
|
||||
#define SRT_ATR_DEPRECATED [[deprecated]]
|
||||
#define SRT_ATR_NODISCARD [[nodiscard]]
|
||||
|
||||
// GNUG is GNU C/C++; this syntax is also supported by Clang
|
||||
#elif defined( __GNUC__)
|
||||
#define SRT_ATR_UNUSED __attribute__((unused))
|
||||
#define SRT_ATR_DEPRECATED __attribute__((deprecated))
|
||||
#define SRT_ATR_NODISCARD __attribute__((warn_unused_result))
|
||||
#else
|
||||
#define SRT_ATR_UNUSED
|
||||
#define SRT_ATR_DEPRECATED
|
||||
#define SRT_ATR_NODISCARD
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef int SRTSOCKET; // SRTSOCKET is a typedef to int anyway, and it's not even in UDT namespace :)
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef __MINGW__
|
||||
typedef SOCKET SYSSOCKET;
|
||||
#else
|
||||
typedef int SYSSOCKET;
|
||||
#endif
|
||||
#else
|
||||
typedef int SYSSOCKET;
|
||||
#endif
|
||||
|
||||
typedef SYSSOCKET UDPSOCKET;
|
||||
|
||||
|
||||
// Values returned by srt_getsockstate()
|
||||
typedef enum SRT_SOCKSTATUS {
|
||||
SRTS_INIT = 1,
|
||||
SRTS_OPENED,
|
||||
SRTS_LISTENING,
|
||||
SRTS_CONNECTING,
|
||||
SRTS_CONNECTED,
|
||||
SRTS_BROKEN,
|
||||
SRTS_CLOSING,
|
||||
SRTS_CLOSED,
|
||||
SRTS_NONEXIST
|
||||
} SRT_SOCKSTATUS;
|
||||
|
||||
// This is a duplicate enum. Must be kept in sync with the original UDT enum for
|
||||
// backward compatibility until all compat is destroyed.
|
||||
typedef enum SRT_SOCKOPT {
|
||||
|
||||
SRTO_MSS = 0, // the Maximum Transfer Unit
|
||||
SRTO_SNDSYN = 1, // if sending is blocking
|
||||
SRTO_RCVSYN = 2, // if receiving is blocking
|
||||
SRTO_ISN = 3, // Initial Sequence Number (valid only after srt_connect or srt_accept-ed sockets)
|
||||
SRTO_FC = 4, // Flight flag size (window size)
|
||||
SRTO_SNDBUF = 5, // maximum buffer in sending queue
|
||||
SRTO_RCVBUF = 6, // UDT receiving buffer size
|
||||
SRTO_LINGER = 7, // waiting for unsent data when closing
|
||||
SRTO_UDP_SNDBUF = 8, // UDP sending buffer size
|
||||
SRTO_UDP_RCVBUF = 9, // UDP receiving buffer size
|
||||
// XXX Free space for 2 options
|
||||
// after deprecated ones are removed
|
||||
SRTO_RENDEZVOUS = 12, // rendezvous connection mode
|
||||
SRTO_SNDTIMEO = 13, // send() timeout
|
||||
SRTO_RCVTIMEO = 14, // recv() timeout
|
||||
SRTO_REUSEADDR = 15, // reuse an existing port or create a new one
|
||||
SRTO_MAXBW = 16, // maximum bandwidth (bytes per second) that the connection can use
|
||||
SRTO_STATE = 17, // current socket state, see UDTSTATUS, read only
|
||||
SRTO_EVENT = 18, // current available events associated with the socket
|
||||
SRTO_SNDDATA = 19, // size of data in the sending buffer
|
||||
SRTO_RCVDATA = 20, // size of data available for recv
|
||||
SRTO_SENDER = 21, // Sender mode (independent of conn mode), for encryption, tsbpd handshake.
|
||||
SRTO_TSBPDMODE = 22, // Enable/Disable TsbPd. Enable -> Tx set origin timestamp, Rx deliver packet at origin time + delay
|
||||
SRTO_LATENCY = 23, // NOT RECOMMENDED. SET: to both SRTO_RCVLATENCY and SRTO_PEERLATENCY. GET: same as SRTO_RCVLATENCY.
|
||||
SRTO_TSBPDDELAY = 23, // DEPRECATED. ALIAS: SRTO_LATENCY
|
||||
SRTO_INPUTBW = 24, // Estimated input stream rate.
|
||||
SRTO_OHEADBW, // MaxBW ceiling based on % over input stream rate. Applies when UDT_MAXBW=0 (auto).
|
||||
SRTO_PASSPHRASE = 26, // Crypto PBKDF2 Passphrase size[0,10..64] 0:disable crypto
|
||||
SRTO_PBKEYLEN, // Crypto key len in bytes {16,24,32} Default: 16 (128-bit)
|
||||
SRTO_KMSTATE, // Key Material exchange status (UDT_SRTKmState)
|
||||
SRTO_IPTTL = 29, // IP Time To Live (passthru for system sockopt IPPROTO_IP/IP_TTL)
|
||||
SRTO_IPTOS, // IP Type of Service (passthru for system sockopt IPPROTO_IP/IP_TOS)
|
||||
SRTO_TLPKTDROP = 31, // Enable receiver pkt drop
|
||||
SRTO_SNDDROPDELAY = 32, // Extra delay towards latency for sender TLPKTDROP decision (-1 to off)
|
||||
SRTO_NAKREPORT = 33, // Enable receiver to send periodic NAK reports
|
||||
SRTO_VERSION = 34, // Local SRT Version
|
||||
SRTO_PEERVERSION, // Peer SRT Version (from SRT Handshake)
|
||||
SRTO_CONNTIMEO = 36, // Connect timeout in msec. Ccaller default: 3000, rendezvous (x 10)
|
||||
// deprecated: SRTO_TWOWAYDATA, SRTO_SNDPBKEYLEN, SRTO_RCVPBKEYLEN (@c below)
|
||||
_DEPRECATED_SRTO_SNDPBKEYLEN = 38, // (needed to use inside the code without generating -Wswitch)
|
||||
//
|
||||
SRTO_SNDKMSTATE = 40, // (GET) the current state of the encryption at the peer side
|
||||
SRTO_RCVKMSTATE, // (GET) the current state of the encryption at the agent side
|
||||
SRTO_LOSSMAXTTL, // Maximum possible packet reorder tolerance (number of packets to receive after loss to send lossreport)
|
||||
SRTO_RCVLATENCY, // TsbPd receiver delay (mSec) to absorb burst of missed packet retransmission
|
||||
SRTO_PEERLATENCY, // Minimum value of the TsbPd receiver delay (mSec) for the opposite side (peer)
|
||||
SRTO_MINVERSION, // Minimum SRT version needed for the peer (peers with less version will get connection reject)
|
||||
SRTO_STREAMID, // A string set to a socket and passed to the listener's accepted socket
|
||||
SRTO_CONGESTION, // Congestion controller type selection
|
||||
SRTO_MESSAGEAPI, // In File mode, use message API (portions of data with boundaries)
|
||||
SRTO_PAYLOADSIZE, // Maximum payload size sent in one UDP packet (0 if unlimited)
|
||||
SRTO_TRANSTYPE = 50, // Transmission type (set of options required for given transmission type)
|
||||
SRTO_KMREFRESHRATE, // After sending how many packets the encryption key should be flipped to the new key
|
||||
SRTO_KMPREANNOUNCE, // How many packets before key flip the new key is annnounced and after key flip the old one decommissioned
|
||||
SRTO_ENFORCEDENCRYPTION, // Connection to be rejected or quickly broken when one side encryption set or bad password
|
||||
SRTO_IPV6ONLY, // IPV6_V6ONLY mode
|
||||
SRTO_PEERIDLETIMEO, // Peer-idle timeout (max time of silence heard from peer) in [ms]
|
||||
// (some space left)
|
||||
SRTO_PACKETFILTER = 60 // Add and configure a packet filter
|
||||
} SRT_SOCKOPT;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
typedef SRT_ATR_DEPRECATED SRT_SOCKOPT SRT_SOCKOPT_DEPRECATED;
|
||||
#define SRT_DEPRECATED_OPTION(value) ((SRT_SOCKOPT_DEPRECATED)value)
|
||||
|
||||
#else
|
||||
|
||||
// deprecated enum labels are supported only since gcc 6, so in C there
|
||||
// will be a whole deprecated enum type, as it's not an error in C to mix
|
||||
// enum types
|
||||
enum SRT_ATR_DEPRECATED SRT_SOCKOPT_DEPRECATED
|
||||
{
|
||||
|
||||
// Dummy last option, as every entry ends with a comma
|
||||
SRTO_DEPRECATED_END = 0
|
||||
|
||||
};
|
||||
#define SRT_DEPRECATED_OPTION(value) ((enum SRT_SOCKOPT_DEPRECATED)value)
|
||||
#endif
|
||||
|
||||
// DEPRECATED OPTIONS:
|
||||
|
||||
// SRTO_TWOWAYDATA: not to be used. SRT connection is always bidirectional if
|
||||
// both clients support HSv5 - that is, since version 1.3.0. This flag was
|
||||
// introducted around 1.2.0 version when full bidirectional support was added,
|
||||
// but the bidirectional feature was decided no to be enabled due to huge
|
||||
// differences between bidirectional support (especially concerning encryption)
|
||||
// with HSv4 and HSv5 (that is, HSv4 was decided to remain unidirectional only,
|
||||
// even though partial support is already provided in this version).
|
||||
|
||||
#define SRTO_TWOWAYDATA SRT_DEPRECATED_OPTION(37)
|
||||
|
||||
// This has been deprecated a long time ago, treat this as never implemented.
|
||||
// The value is also already reused for another option.
|
||||
#define SRTO_TSBPDMAXLAG SRT_DEPRECATED_OPTION(32)
|
||||
|
||||
// This option is a derivative from UDT; the mechanism that uses it is now
|
||||
// settable by SRTO_CONGESTION, or more generally by SRTO_TRANSTYPE. The freed
|
||||
// number has been reused for a read-only option SRTO_ISN. This option should
|
||||
// have never been used anywhere, just for safety this is temporarily declared
|
||||
// as deprecated.
|
||||
#define SRTO_CC SRT_DEPRECATED_OPTION(3)
|
||||
|
||||
// These two flags were derived from UDT, but they were never used.
|
||||
// Probably it didn't make sense anyway. The maximum size of the message
|
||||
// in File/Message mode is defined by SRTO_SNDBUF, and the MSGTTL is
|
||||
// a parameter used in `srt_sendmsg` and `srt_sendmsg2`.
|
||||
#define SRTO_MAXMSG SRT_DEPRECATED_OPTION(10)
|
||||
#define SRTO_MSGTTL SRT_DEPRECATED_OPTION(11)
|
||||
|
||||
// These flags come from an older experimental implementation of bidirectional
|
||||
// encryption support, which were used two different SEKs, KEKs and passphrases
|
||||
// per direction. The current implementation uses just one in both directions,
|
||||
// so SRTO_PBKEYLEN should be used for both cases.
|
||||
#define SRTO_SNDPBKEYLEN SRT_DEPRECATED_OPTION(38)
|
||||
#define SRTO_RCVPBKEYLEN SRT_DEPRECATED_OPTION(39)
|
||||
|
||||
// Keeping old name for compatibility (deprecated)
|
||||
#define SRTO_SMOOTHER SRT_DEPRECATED_OPTION(47)
|
||||
#define SRTO_STRICTENC SRT_DEPRECATED_OPTION(53)
|
||||
|
||||
typedef enum SRT_TRANSTYPE
|
||||
{
|
||||
SRTT_LIVE,
|
||||
SRTT_FILE,
|
||||
SRTT_INVALID
|
||||
} SRT_TRANSTYPE;
|
||||
|
||||
// These sizes should be used for Live mode. In Live mode you should not
|
||||
// exceed the size that fits in a single MTU.
|
||||
|
||||
// This is for MPEG TS and it's a default SRTO_PAYLOADSIZE for SRTT_LIVE.
|
||||
static const int SRT_LIVE_DEF_PLSIZE = 1316; // = 188*7, recommended for MPEG TS
|
||||
|
||||
// This is the maximum payload size for Live mode, should you have a different
|
||||
// payload type than MPEG TS.
|
||||
static const int SRT_LIVE_MAX_PLSIZE = 1456; // MTU(1500) - UDP.hdr(28) - SRT.hdr(16)
|
||||
|
||||
// Latency for Live transmission: default is 120
|
||||
static const int SRT_LIVE_DEF_LATENCY_MS = 120;
|
||||
|
||||
// Importrant note: please add new fields to this structure to the end and don't remove any existing fields
|
||||
struct CBytePerfMon
|
||||
{
|
||||
// global measurements
|
||||
int64_t msTimeStamp; // time since the UDT entity is started, in milliseconds
|
||||
int64_t pktSentTotal; // total number of sent data packets, including retransmissions
|
||||
int64_t pktRecvTotal; // total number of received packets
|
||||
int pktSndLossTotal; // total number of lost packets (sender side)
|
||||
int pktRcvLossTotal; // total number of lost packets (receiver side)
|
||||
int pktRetransTotal; // total number of retransmitted packets
|
||||
int pktSentACKTotal; // total number of sent ACK packets
|
||||
int pktRecvACKTotal; // total number of received ACK packets
|
||||
int pktSentNAKTotal; // total number of sent NAK packets
|
||||
int pktRecvNAKTotal; // total number of received NAK packets
|
||||
int64_t usSndDurationTotal; // total time duration when UDT is sending data (idle time exclusive)
|
||||
//>new
|
||||
int pktSndDropTotal; // number of too-late-to-send dropped packets
|
||||
int pktRcvDropTotal; // number of too-late-to play missing packets
|
||||
int pktRcvUndecryptTotal; // number of undecrypted packets
|
||||
uint64_t byteSentTotal; // total number of sent data bytes, including retransmissions
|
||||
uint64_t byteRecvTotal; // total number of received bytes
|
||||
#ifdef SRT_ENABLE_LOSTBYTESCOUNT
|
||||
uint64_t byteRcvLossTotal; // total number of lost bytes
|
||||
#endif
|
||||
uint64_t byteRetransTotal; // total number of retransmitted bytes
|
||||
uint64_t byteSndDropTotal; // number of too-late-to-send dropped bytes
|
||||
uint64_t byteRcvDropTotal; // number of too-late-to play missing bytes (estimate based on average packet size)
|
||||
uint64_t byteRcvUndecryptTotal; // number of undecrypted bytes
|
||||
//<
|
||||
|
||||
// local measurements
|
||||
int64_t pktSent; // number of sent data packets, including retransmissions
|
||||
int64_t pktRecv; // number of received packets
|
||||
int pktSndLoss; // number of lost packets (sender side)
|
||||
int pktRcvLoss; // number of lost packets (receiver side)
|
||||
int pktRetrans; // number of retransmitted packets
|
||||
int pktRcvRetrans; // number of retransmitted packets received
|
||||
int pktSentACK; // number of sent ACK packets
|
||||
int pktRecvACK; // number of received ACK packets
|
||||
int pktSentNAK; // number of sent NAK packets
|
||||
int pktRecvNAK; // number of received NAK packets
|
||||
double mbpsSendRate; // sending rate in Mb/s
|
||||
double mbpsRecvRate; // receiving rate in Mb/s
|
||||
int64_t usSndDuration; // busy sending time (i.e., idle time exclusive)
|
||||
int pktReorderDistance; // size of order discrepancy in received sequences
|
||||
double pktRcvAvgBelatedTime; // average time of packet delay for belated packets (packets with sequence past the ACK)
|
||||
int64_t pktRcvBelated; // number of received AND IGNORED packets due to having come too late
|
||||
//>new
|
||||
int pktSndDrop; // number of too-late-to-send dropped packets
|
||||
int pktRcvDrop; // number of too-late-to play missing packets
|
||||
int pktRcvUndecrypt; // number of undecrypted packets
|
||||
uint64_t byteSent; // number of sent data bytes, including retransmissions
|
||||
uint64_t byteRecv; // number of received bytes
|
||||
#ifdef SRT_ENABLE_LOSTBYTESCOUNT
|
||||
uint64_t byteRcvLoss; // number of retransmitted bytes
|
||||
#endif
|
||||
uint64_t byteRetrans; // number of retransmitted bytes
|
||||
uint64_t byteSndDrop; // number of too-late-to-send dropped bytes
|
||||
uint64_t byteRcvDrop; // number of too-late-to play missing bytes (estimate based on average packet size)
|
||||
uint64_t byteRcvUndecrypt; // number of undecrypted bytes
|
||||
//<
|
||||
|
||||
// instant measurements
|
||||
double usPktSndPeriod; // packet sending period, in microseconds
|
||||
int pktFlowWindow; // flow window size, in number of packets
|
||||
int pktCongestionWindow; // congestion window size, in number of packets
|
||||
int pktFlightSize; // number of packets on flight
|
||||
double msRTT; // RTT, in milliseconds
|
||||
double mbpsBandwidth; // estimated bandwidth, in Mb/s
|
||||
int byteAvailSndBuf; // available UDT sender buffer size
|
||||
int byteAvailRcvBuf; // available UDT receiver buffer size
|
||||
//>new
|
||||
double mbpsMaxBW; // Transmit Bandwidth ceiling (Mbps)
|
||||
int byteMSS; // MTU
|
||||
|
||||
int pktSndBuf; // UnACKed packets in UDT sender
|
||||
int byteSndBuf; // UnACKed bytes in UDT sender
|
||||
int msSndBuf; // UnACKed timespan (msec) of UDT sender
|
||||
int msSndTsbPdDelay; // Timestamp-based Packet Delivery Delay
|
||||
|
||||
int pktRcvBuf; // Undelivered packets in UDT receiver
|
||||
int byteRcvBuf; // Undelivered bytes of UDT receiver
|
||||
int msRcvBuf; // Undelivered timespan (msec) of UDT receiver
|
||||
int msRcvTsbPdDelay; // Timestamp-based Packet Delivery Delay
|
||||
|
||||
int pktSndFilterExtraTotal; // number of control packets supplied by packet filter
|
||||
int pktRcvFilterExtraTotal; // number of control packets received and not supplied back
|
||||
int pktRcvFilterSupplyTotal; // number of packets that the filter supplied extra (e.g. FEC rebuilt)
|
||||
int pktRcvFilterLossTotal; // number of packet loss not coverable by filter
|
||||
|
||||
int pktSndFilterExtra; // number of control packets supplied by packet filter
|
||||
int pktRcvFilterExtra; // number of control packets received and not supplied back
|
||||
int pktRcvFilterSupply; // number of packets that the filter supplied extra (e.g. FEC rebuilt)
|
||||
int pktRcvFilterLoss; // number of packet loss not coverable by filter
|
||||
int pktReorderTolerance; // packet reorder tolerance value
|
||||
//<
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Error codes - define outside the CUDTException class
|
||||
// because otherwise you'd have to use CUDTException::MJ_SUCCESS etc.
|
||||
// in all throw CUDTException expressions.
|
||||
enum CodeMajor
|
||||
{
|
||||
MJ_UNKNOWN = -1,
|
||||
MJ_SUCCESS = 0,
|
||||
MJ_SETUP = 1,
|
||||
MJ_CONNECTION = 2,
|
||||
MJ_SYSTEMRES = 3,
|
||||
MJ_FILESYSTEM = 4,
|
||||
MJ_NOTSUP = 5,
|
||||
MJ_AGAIN = 6,
|
||||
MJ_PEERERROR = 7
|
||||
};
|
||||
|
||||
enum CodeMinor
|
||||
{
|
||||
// These are "minor" error codes from various "major" categories
|
||||
// MJ_SETUP
|
||||
MN_NONE = 0,
|
||||
MN_TIMEOUT = 1,
|
||||
MN_REJECTED = 2,
|
||||
MN_NORES = 3,
|
||||
MN_SECURITY = 4,
|
||||
// MJ_CONNECTION
|
||||
MN_CONNLOST = 1,
|
||||
MN_NOCONN = 2,
|
||||
// MJ_SYSTEMRES
|
||||
MN_THREAD = 1,
|
||||
MN_MEMORY = 2,
|
||||
// MJ_FILESYSTEM
|
||||
MN_SEEKGFAIL = 1,
|
||||
MN_READFAIL = 2,
|
||||
MN_SEEKPFAIL = 3,
|
||||
MN_WRITEFAIL = 4,
|
||||
// MJ_NOTSUP
|
||||
MN_ISBOUND = 1,
|
||||
MN_ISCONNECTED = 2,
|
||||
MN_INVAL = 3,
|
||||
MN_SIDINVAL = 4,
|
||||
MN_ISUNBOUND = 5,
|
||||
MN_NOLISTEN = 6,
|
||||
MN_ISRENDEZVOUS = 7,
|
||||
MN_ISRENDUNBOUND = 8,
|
||||
MN_INVALMSGAPI = 9,
|
||||
MN_INVALBUFFERAPI = 10,
|
||||
MN_BUSY = 11,
|
||||
MN_XSIZE = 12,
|
||||
MN_EIDINVAL = 13,
|
||||
// MJ_AGAIN
|
||||
MN_WRAVAIL = 1,
|
||||
MN_RDAVAIL = 2,
|
||||
MN_XMTIMEOUT = 3,
|
||||
MN_CONGESTION = 4
|
||||
};
|
||||
|
||||
static const enum CodeMinor MN_ISSTREAM SRT_ATR_DEPRECATED = (enum CodeMinor)(9);
|
||||
static const enum CodeMinor MN_ISDGRAM SRT_ATR_DEPRECATED = (enum CodeMinor)(10);
|
||||
|
||||
// Stupid, but effective. This will be #undefined, so don't worry.
|
||||
#define MJ(major) (1000 * MJ_##major)
|
||||
#define MN(major, minor) (1000 * MJ_##major + MN_##minor)
|
||||
|
||||
// Some better way to define it, and better for C language.
|
||||
typedef enum SRT_ERRNO
|
||||
{
|
||||
SRT_EUNKNOWN = -1,
|
||||
SRT_SUCCESS = MJ_SUCCESS,
|
||||
|
||||
SRT_ECONNSETUP = MJ(SETUP),
|
||||
SRT_ENOSERVER = MN(SETUP, TIMEOUT),
|
||||
SRT_ECONNREJ = MN(SETUP, REJECTED),
|
||||
SRT_ESOCKFAIL = MN(SETUP, NORES),
|
||||
SRT_ESECFAIL = MN(SETUP, SECURITY),
|
||||
|
||||
SRT_ECONNFAIL = MJ(CONNECTION),
|
||||
SRT_ECONNLOST = MN(CONNECTION, CONNLOST),
|
||||
SRT_ENOCONN = MN(CONNECTION, NOCONN),
|
||||
|
||||
SRT_ERESOURCE = MJ(SYSTEMRES),
|
||||
SRT_ETHREAD = MN(SYSTEMRES, THREAD),
|
||||
SRT_ENOBUF = MN(SYSTEMRES, MEMORY),
|
||||
|
||||
SRT_EFILE = MJ(FILESYSTEM),
|
||||
SRT_EINVRDOFF = MN(FILESYSTEM, SEEKGFAIL),
|
||||
SRT_ERDPERM = MN(FILESYSTEM, READFAIL),
|
||||
SRT_EINVWROFF = MN(FILESYSTEM, SEEKPFAIL),
|
||||
SRT_EWRPERM = MN(FILESYSTEM, WRITEFAIL),
|
||||
|
||||
SRT_EINVOP = MJ(NOTSUP),
|
||||
SRT_EBOUNDSOCK = MN(NOTSUP, ISBOUND),
|
||||
SRT_ECONNSOCK = MN(NOTSUP, ISCONNECTED),
|
||||
SRT_EINVPARAM = MN(NOTSUP, INVAL),
|
||||
SRT_EINVSOCK = MN(NOTSUP, SIDINVAL),
|
||||
SRT_EUNBOUNDSOCK = MN(NOTSUP, ISUNBOUND),
|
||||
SRT_ENOLISTEN = MN(NOTSUP, NOLISTEN),
|
||||
SRT_ERDVNOSERV = MN(NOTSUP, ISRENDEZVOUS),
|
||||
SRT_ERDVUNBOUND = MN(NOTSUP, ISRENDUNBOUND),
|
||||
SRT_EINVALMSGAPI = MN(NOTSUP, INVALMSGAPI),
|
||||
SRT_EINVALBUFFERAPI = MN(NOTSUP, INVALBUFFERAPI),
|
||||
SRT_EDUPLISTEN = MN(NOTSUP, BUSY),
|
||||
SRT_ELARGEMSG = MN(NOTSUP, XSIZE),
|
||||
SRT_EINVPOLLID = MN(NOTSUP, EIDINVAL),
|
||||
|
||||
SRT_EASYNCFAIL = MJ(AGAIN),
|
||||
SRT_EASYNCSND = MN(AGAIN, WRAVAIL),
|
||||
SRT_EASYNCRCV = MN(AGAIN, RDAVAIL),
|
||||
SRT_ETIMEOUT = MN(AGAIN, XMTIMEOUT),
|
||||
SRT_ECONGEST = MN(AGAIN, CONGESTION),
|
||||
|
||||
SRT_EPEERERR = MJ(PEERERROR)
|
||||
} SRT_ERRNO;
|
||||
|
||||
static const SRT_ERRNO SRT_EISSTREAM SRT_ATR_DEPRECATED = (SRT_ERRNO) MN(NOTSUP, INVALMSGAPI);
|
||||
static const SRT_ERRNO SRT_EISDGRAM SRT_ATR_DEPRECATED = (SRT_ERRNO) MN(NOTSUP, INVALBUFFERAPI);
|
||||
|
||||
#undef MJ
|
||||
#undef MN
|
||||
|
||||
enum SRT_REJECT_REASON
|
||||
{
|
||||
SRT_REJ_UNKNOWN, // initial set when in progress
|
||||
SRT_REJ_SYSTEM, // broken due to system function error
|
||||
SRT_REJ_PEER, // connection was rejected by peer
|
||||
SRT_REJ_RESOURCE, // internal problem with resource allocation
|
||||
SRT_REJ_ROGUE, // incorrect data in handshake messages
|
||||
SRT_REJ_BACKLOG, // listener's backlog exceeded
|
||||
SRT_REJ_IPE, // internal program error
|
||||
SRT_REJ_CLOSE, // socket is closing
|
||||
SRT_REJ_VERSION, // peer is older version than agent's minimum set
|
||||
SRT_REJ_RDVCOOKIE, // rendezvous cookie collision
|
||||
SRT_REJ_BADSECRET, // wrong password
|
||||
SRT_REJ_UNSECURE, // password required or unexpected
|
||||
SRT_REJ_MESSAGEAPI, // streamapi/messageapi collision
|
||||
SRT_REJ_CONGESTION, // incompatible congestion-controller type
|
||||
SRT_REJ_FILTER, // incompatible packet filter
|
||||
|
||||
SRT_REJ__SIZE,
|
||||
};
|
||||
|
||||
// Logging API - specialization for SRT.
|
||||
|
||||
// Define logging functional areas for log selection.
|
||||
// Use values greater than 0. Value 0 is reserved for LOGFA_GENERAL,
|
||||
// which is considered always enabled.
|
||||
|
||||
// Logger Functional Areas
|
||||
// Note that 0 is "general".
|
||||
|
||||
// Made by #define so that it's available also for C API.
|
||||
#define SRT_LOGFA_GENERAL 0
|
||||
#define SRT_LOGFA_BSTATS 1
|
||||
#define SRT_LOGFA_CONTROL 2
|
||||
#define SRT_LOGFA_DATA 3
|
||||
#define SRT_LOGFA_TSBPD 4
|
||||
#define SRT_LOGFA_REXMIT 5
|
||||
#define SRT_LOGFA_HAICRYPT 6
|
||||
#define SRT_LOGFA_CONGEST 7
|
||||
|
||||
// To make a typical int32_t size, although still use std::bitset.
|
||||
// C API will carry it over.
|
||||
#define SRT_LOGFA_LASTNONE 31
|
||||
|
||||
enum SRT_KM_STATE
|
||||
{
|
||||
SRT_KM_S_UNSECURED = 0, //No encryption
|
||||
SRT_KM_S_SECURING = 1, //Stream encrypted, exchanging Keying Material
|
||||
SRT_KM_S_SECURED = 2, //Stream encrypted, keying Material exchanged, decrypting ok.
|
||||
SRT_KM_S_NOSECRET = 3, //Stream encrypted and no secret to decrypt Keying Material
|
||||
SRT_KM_S_BADSECRET = 4 //Stream encrypted and wrong secret, cannot decrypt Keying Material
|
||||
};
|
||||
|
||||
enum SRT_EPOLL_OPT
|
||||
{
|
||||
SRT_EPOLL_OPT_NONE = 0x0, // fallback
|
||||
// this values are defined same as linux epoll.h
|
||||
// so that if system values are used by mistake, they should have the same effect
|
||||
SRT_EPOLL_IN = 0x1,
|
||||
SRT_EPOLL_OUT = 0x4,
|
||||
SRT_EPOLL_ERR = 0x8,
|
||||
SRT_EPOLL_ET = 1u << 31
|
||||
};
|
||||
// These are actually flags - use a bit container:
|
||||
typedef int32_t SRT_EPOLL_T;
|
||||
|
||||
enum SRT_EPOLL_FLAGS
|
||||
{
|
||||
/// This allows the EID container to be empty when calling the waiting
|
||||
/// function with infinite time. This means an infinite hangup, although
|
||||
/// a socket can be added to this EID from a separate thread.
|
||||
SRT_EPOLL_ENABLE_EMPTY = 1,
|
||||
|
||||
/// This makes the waiting function check if there is output container
|
||||
/// passed to it, and report an error if it isn't. By default it is allowed
|
||||
/// that the output container is 0 size or NULL and therefore the readiness
|
||||
/// state is reported only as a number of ready sockets from return value.
|
||||
SRT_EPOLL_ENABLE_OUTPUTCHECK = 2
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
// In C++ these enums cannot be treated as int and glued by operator |.
|
||||
// Unless this operator is defined.
|
||||
inline SRT_EPOLL_OPT operator|(SRT_EPOLL_OPT a1, SRT_EPOLL_OPT a2)
|
||||
{
|
||||
return SRT_EPOLL_OPT( (int)a1 | (int)a2 );
|
||||
}
|
||||
|
||||
inline bool operator&(int flags, SRT_EPOLL_OPT eflg)
|
||||
{
|
||||
// Using an enum prevents treating int automatically as enum,
|
||||
// requires explicit enum to be passed here, and minimizes the
|
||||
// risk that the right side value will contain multiple flags.
|
||||
return (flags & int(eflg)) != 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
typedef struct CBytePerfMon SRT_TRACEBSTATS;
|
||||
|
||||
static const SRTSOCKET SRT_INVALID_SOCK = -1;
|
||||
static const int SRT_ERROR = -1;
|
||||
|
||||
// library initialization
|
||||
SRT_API int srt_startup(void);
|
||||
SRT_API int srt_cleanup(void);
|
||||
|
||||
//
|
||||
// Socket operations
|
||||
//
|
||||
SRT_API SRTSOCKET srt_socket (int af, int type, int protocol);
|
||||
SRT_API SRTSOCKET srt_create_socket();
|
||||
SRT_API int srt_bind (SRTSOCKET u, const struct sockaddr* name, int namelen);
|
||||
SRT_API int srt_bind_peerof (SRTSOCKET u, UDPSOCKET udpsock);
|
||||
SRT_API int srt_listen (SRTSOCKET u, int backlog);
|
||||
SRT_API SRTSOCKET srt_accept (SRTSOCKET u, struct sockaddr* addr, int* addrlen);
|
||||
typedef int srt_listen_callback_fn (void* opaq, SRTSOCKET ns, int hsversion, const struct sockaddr* peeraddr, const char* streamid);
|
||||
SRT_API int srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook_fn, void* hook_opaque);
|
||||
SRT_API int srt_connect (SRTSOCKET u, const struct sockaddr* name, int namelen);
|
||||
SRT_API int srt_connect_debug(SRTSOCKET u, const struct sockaddr* name, int namelen, int forced_isn);
|
||||
SRT_API int srt_rendezvous (SRTSOCKET u, const struct sockaddr* local_name, int local_namelen,
|
||||
const struct sockaddr* remote_name, int remote_namelen);
|
||||
SRT_API int srt_close (SRTSOCKET u);
|
||||
SRT_API int srt_getpeername (SRTSOCKET u, struct sockaddr* name, int* namelen);
|
||||
SRT_API int srt_getsockname (SRTSOCKET u, struct sockaddr* name, int* namelen);
|
||||
SRT_API int srt_getsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, void* optval, int* optlen);
|
||||
SRT_API int srt_setsockopt (SRTSOCKET u, int level /*ignored*/, SRT_SOCKOPT optname, const void* optval, int optlen);
|
||||
SRT_API int srt_getsockflag (SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen);
|
||||
SRT_API int srt_setsockflag (SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen);
|
||||
|
||||
|
||||
// XXX Note that the srctime functionality doesn't work yet and needs fixing.
|
||||
typedef struct SRT_MsgCtrl_
|
||||
{
|
||||
int flags; // Left for future
|
||||
int msgttl; // TTL for a message, default -1 (no TTL limitation)
|
||||
int inorder; // Whether a message is allowed to supersede partially lost one. Unused in stream and live mode.
|
||||
int boundary; // 0:mid pkt, 1(01b):end of frame, 2(11b):complete frame, 3(10b): start of frame
|
||||
uint64_t srctime; // source timestamp (usec), 0: use internal time
|
||||
int32_t pktseq; // sequence number of the first packet in received message (unused for sending)
|
||||
int32_t msgno; // message number (output value for both sending and receiving)
|
||||
} SRT_MSGCTRL;
|
||||
|
||||
// You are free to use either of these two methods to set SRT_MSGCTRL object
|
||||
// to default values: either call srt_msgctrl_init(&obj) or obj = srt_msgctrl_default.
|
||||
SRT_API void srt_msgctrl_init(SRT_MSGCTRL* mctrl);
|
||||
SRT_API extern const SRT_MSGCTRL srt_msgctrl_default;
|
||||
|
||||
// The send/receive functions.
|
||||
// These functions have different names due to different sets of parameters
|
||||
// to be supplied. Not all of them are needed or make sense in all modes:
|
||||
|
||||
// Plain: supply only the buffer and its size.
|
||||
// Msg: supply additionally
|
||||
// - TTL (message is not delivered when exceeded) and
|
||||
// - INORDER (when false, the message is allowed to be delivered in different
|
||||
// order than when it was sent, when the later message is earlier ready to
|
||||
// deliver)
|
||||
// Msg2: Supply extra parameters in SRT_MSGCTRL. When receiving, these
|
||||
// parameters will be filled, as needed. NULL is acceptable, in which case
|
||||
// the defaults are used.
|
||||
|
||||
// NOTE: srt_send and srt_recv have the last "..." left to allow ignore a
|
||||
// deprecated and unused "flags" parameter. After confirming that all
|
||||
// compat applications that pass useless 0 there are fixed, this will be
|
||||
// removed.
|
||||
|
||||
//
|
||||
// Sending functions
|
||||
//
|
||||
SRT_API int srt_send (SRTSOCKET u, const char* buf, int len);
|
||||
SRT_API int srt_sendmsg (SRTSOCKET u, const char* buf, int len, int ttl/* = -1*/, int inorder/* = false*/);
|
||||
SRT_API int srt_sendmsg2(SRTSOCKET u, const char* buf, int len, SRT_MSGCTRL *mctrl);
|
||||
|
||||
//
|
||||
// Receiving functions
|
||||
//
|
||||
SRT_API int srt_recv (SRTSOCKET u, char* buf, int len);
|
||||
|
||||
// srt_recvmsg is actually an alias to srt_recv, it stays under the old name for compat reasons.
|
||||
SRT_API int srt_recvmsg (SRTSOCKET u, char* buf, int len);
|
||||
SRT_API int srt_recvmsg2(SRTSOCKET u, char *buf, int len, SRT_MSGCTRL *mctrl);
|
||||
|
||||
|
||||
// Special send/receive functions for files only.
|
||||
#define SRT_DEFAULT_SENDFILE_BLOCK 364000
|
||||
#define SRT_DEFAULT_RECVFILE_BLOCK 7280000
|
||||
SRT_API int64_t srt_sendfile(SRTSOCKET u, const char* path, int64_t* offset, int64_t size, int block);
|
||||
SRT_API int64_t srt_recvfile(SRTSOCKET u, const char* path, int64_t* offset, int64_t size, int block);
|
||||
|
||||
|
||||
// last error detection
|
||||
SRT_API const char* srt_getlasterror_str(void);
|
||||
SRT_API int srt_getlasterror(int* errno_loc);
|
||||
SRT_API const char* srt_strerror(int code, int errnoval);
|
||||
SRT_API void srt_clearlasterror(void);
|
||||
|
||||
// performance track
|
||||
// perfmon with Byte counters for better bitrate estimation.
|
||||
SRT_API int srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear);
|
||||
// permon with Byte counters and instantaneous stats instead of moving averages for Snd/Rcvbuffer sizes.
|
||||
SRT_API int srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous);
|
||||
|
||||
// Socket Status (for problem tracking)
|
||||
SRT_API SRT_SOCKSTATUS srt_getsockstate(SRTSOCKET u);
|
||||
|
||||
SRT_API int srt_epoll_create(void);
|
||||
SRT_API int srt_epoll_add_usock(int eid, SRTSOCKET u, const int* events);
|
||||
SRT_API int srt_epoll_add_ssock(int eid, SYSSOCKET s, const int* events);
|
||||
SRT_API int srt_epoll_remove_usock(int eid, SRTSOCKET u);
|
||||
SRT_API int srt_epoll_remove_ssock(int eid, SYSSOCKET s);
|
||||
SRT_API int srt_epoll_update_usock(int eid, SRTSOCKET u, const int* events);
|
||||
SRT_API int srt_epoll_update_ssock(int eid, SYSSOCKET s, const int* events);
|
||||
|
||||
SRT_API int srt_epoll_wait(int eid, SRTSOCKET* readfds, int* rnum, SRTSOCKET* writefds, int* wnum, int64_t msTimeOut,
|
||||
SYSSOCKET* lrfds, int* lrnum, SYSSOCKET* lwfds, int* lwnum);
|
||||
typedef struct SRT_EPOLL_EVENT_
|
||||
{
|
||||
SRTSOCKET fd;
|
||||
int events; // SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR
|
||||
} SRT_EPOLL_EVENT;
|
||||
SRT_API int srt_epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut);
|
||||
|
||||
SRT_API int32_t srt_epoll_set(int eid, int32_t flags);
|
||||
SRT_API int srt_epoll_release(int eid);
|
||||
|
||||
// Logging control
|
||||
|
||||
SRT_API void srt_setloglevel(int ll);
|
||||
SRT_API void srt_addlogfa(int fa);
|
||||
SRT_API void srt_dellogfa(int fa);
|
||||
SRT_API void srt_resetlogfa(const int* fara, size_t fara_size);
|
||||
// This isn't predicted, will be only available in SRT C++ API.
|
||||
// For the time being, until this API is ready, use UDT::setlogstream.
|
||||
// SRT_API void srt_setlogstream(std::ostream& stream);
|
||||
SRT_API void srt_setloghandler(void* opaque, SRT_LOG_HANDLER_FN* handler);
|
||||
SRT_API void srt_setlogflags(int flags);
|
||||
|
||||
|
||||
SRT_API int srt_getsndbuffer(SRTSOCKET sock, size_t* blocks, size_t* bytes);
|
||||
|
||||
SRT_API enum SRT_REJECT_REASON srt_getrejectreason(SRTSOCKET sock);
|
||||
SRT_API extern const char* const srt_rejectreason_msg [];
|
||||
const char* srt_rejectreason_str(enum SRT_REJECT_REASON id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
71
trunk/3rdparty/srt-1-fit/srtcore/srt4udt.h
vendored
Normal file
71
trunk/3rdparty/srt-1-fit/srtcore/srt4udt.h
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef SRT4UDT_H
|
||||
#define SRT4UDT_H
|
||||
|
||||
#ifndef INC__SRTC_H
|
||||
#error "This is protected header, used by udt.h. This shouldn't be included directly"
|
||||
#endif
|
||||
|
||||
//undef SRT_ENABLE_ECN 1 /* Early Congestion Notification (for source bitrate control) */
|
||||
|
||||
//undef SRT_DEBUG_TSBPD_OUTJITTER 1 /* Packet Delivery histogram */
|
||||
//undef SRT_DEBUG_TSBPD_DRIFT 1 /* Debug Encoder-Decoder Drift) */
|
||||
//undef SRT_DEBUG_TSBPD_WRAP 1 /* Debug packet timestamp wraparound */
|
||||
//undef SRT_DEBUG_TLPKTDROP_DROPSEQ 1
|
||||
//undef SRT_DEBUG_SNDQ_HIGHRATE 1
|
||||
|
||||
|
||||
/*
|
||||
* SRT_ENABLE_CONNTIMEO
|
||||
* Option UDT_CONNTIMEO added to the API to set/get the connection timeout.
|
||||
* The UDT hard coded default of 3000 msec is too small for some large RTT (satellite) use cases.
|
||||
* The SRT handshake (2 exchanges) needs 2 times the RTT to complete with no packet loss.
|
||||
*/
|
||||
#define SRT_ENABLE_CONNTIMEO 1
|
||||
|
||||
/*
|
||||
* SRT_ENABLE_NOCWND
|
||||
* Set the congestion window at its max (then disabling it) to prevent stopping transmission
|
||||
* when too many packets are not acknowledged.
|
||||
* The congestion windows is the maximum distance in pkts since the last acknowledged packets.
|
||||
*/
|
||||
#define SRT_ENABLE_NOCWND 1
|
||||
|
||||
/*
|
||||
* SRT_ENABLE_NAKREPORT
|
||||
* Send periodic NAK report for more efficient retransmission instead of relying on ACK timeout
|
||||
* to retransmit all non-ACKed packets, very inefficient with real-time and no congestion window.
|
||||
*/
|
||||
#define SRT_ENABLE_NAKREPORT 1
|
||||
|
||||
#define SRT_ENABLE_RCVBUFSZ_MAVG 1 /* Recv buffer size moving average */
|
||||
#define SRT_ENABLE_SNDBUFSZ_MAVG 1 /* Send buffer size moving average */
|
||||
#define SRT_MAVG_SAMPLING_RATE 40 /* Max sampling rate */
|
||||
|
||||
#define SRT_ENABLE_LOSTBYTESCOUNT 1
|
||||
|
||||
|
||||
/*
|
||||
* SRT_ENABLE_IPOPTS
|
||||
* Enable IP TTL and ToS setting
|
||||
*/
|
||||
#define SRT_ENABLE_IPOPTS 1
|
||||
|
||||
|
||||
#define SRT_ENABLE_CLOSE_SYNCH 1
|
||||
|
||||
#endif /* SRT4UDT_H */
|
318
trunk/3rdparty/srt-1-fit/srtcore/srt_c_api.cpp
vendored
Normal file
318
trunk/3rdparty/srt-1-fit/srtcore/srt_c_api.cpp
vendored
Normal file
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <iterator>
|
||||
#include <fstream>
|
||||
#if __APPLE__
|
||||
#include "TargetConditionals.h"
|
||||
#endif
|
||||
#include "srt.h"
|
||||
#include "common.h"
|
||||
#include "core.h"
|
||||
#include "utilities.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
||||
int srt_startup() { return CUDT::startup(); }
|
||||
int srt_cleanup() { return CUDT::cleanup(); }
|
||||
|
||||
SRTSOCKET srt_socket(int af, int type, int protocol) { return CUDT::socket(af, type, protocol); }
|
||||
SRTSOCKET srt_create_socket()
|
||||
{
|
||||
// XXX This must include rework around m_iIPVersion. This must be
|
||||
// abandoned completely and all "IP VERSION" thing should rely on
|
||||
// the exact specification in the 'sockaddr' objects passed to other functions,
|
||||
// that is, the "current IP Version" remains undefined until any of
|
||||
// srt_bind() or srt_connect() function is done. And when any of these
|
||||
// functions are being called, the IP version is contained in the
|
||||
// sockaddr object passed there.
|
||||
|
||||
// Until this rework is done, srt_create_socket() will set the
|
||||
// default AF_INET family.
|
||||
|
||||
// Note that all arguments except the first one here are ignored.
|
||||
return CUDT::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
}
|
||||
|
||||
int srt_bind(SRTSOCKET u, const struct sockaddr * name, int namelen) { return CUDT::bind(u, name, namelen); }
|
||||
int srt_bind_peerof(SRTSOCKET u, UDPSOCKET udpsock) { return CUDT::bind(u, udpsock); }
|
||||
int srt_listen(SRTSOCKET u, int backlog) { return CUDT::listen(u, backlog); }
|
||||
SRTSOCKET srt_accept(SRTSOCKET u, struct sockaddr * addr, int * addrlen) { return CUDT::accept(u, addr, addrlen); }
|
||||
int srt_connect(SRTSOCKET u, const struct sockaddr * name, int namelen) { return CUDT::connect(u, name, namelen, 0); }
|
||||
int srt_connect_debug(SRTSOCKET u, const struct sockaddr * name, int namelen, int forced_isn) { return CUDT::connect(u, name, namelen, forced_isn); }
|
||||
|
||||
int srt_rendezvous(SRTSOCKET u, const struct sockaddr* local_name, int local_namelen,
|
||||
const struct sockaddr* remote_name, int remote_namelen)
|
||||
{
|
||||
bool yes = 1;
|
||||
CUDT::setsockopt(u, 0, UDT_RENDEZVOUS, &yes, sizeof yes);
|
||||
|
||||
// Note: PORT is 16-bit and at the same location in both sockaddr_in and sockaddr_in6.
|
||||
// Just as a safety precaution, check the structs.
|
||||
if ( (local_name->sa_family != AF_INET && local_name->sa_family != AF_INET6)
|
||||
|| local_name->sa_family != remote_name->sa_family)
|
||||
return SRT_EINVPARAM;
|
||||
|
||||
sockaddr_in* local_sin = (sockaddr_in*)local_name;
|
||||
sockaddr_in* remote_sin = (sockaddr_in*)remote_name;
|
||||
|
||||
if (local_sin->sin_port != remote_sin->sin_port)
|
||||
return SRT_EINVPARAM;
|
||||
|
||||
int st = srt_bind(u, local_name, local_namelen);
|
||||
if ( st != 0 )
|
||||
return st;
|
||||
|
||||
return srt_connect(u, remote_name, remote_namelen);
|
||||
}
|
||||
|
||||
int srt_close(SRTSOCKET u)
|
||||
{
|
||||
SRT_SOCKSTATUS st = srt_getsockstate(u);
|
||||
|
||||
if ((st == SRTS_NONEXIST) ||
|
||||
(st == SRTS_CLOSED) ||
|
||||
(st == SRTS_CLOSING) )
|
||||
{
|
||||
// It's closed already. Do nothing.
|
||||
return 0;
|
||||
}
|
||||
|
||||
return CUDT::close(u);
|
||||
}
|
||||
|
||||
int srt_getpeername(SRTSOCKET u, struct sockaddr * name, int * namelen) { return CUDT::getpeername(u, name, namelen); }
|
||||
int srt_getsockname(SRTSOCKET u, struct sockaddr * name, int * namelen) { return CUDT::getsockname(u, name, namelen); }
|
||||
int srt_getsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, void * optval, int * optlen)
|
||||
{ return CUDT::getsockopt(u, level, optname, optval, optlen); }
|
||||
int srt_setsockopt(SRTSOCKET u, int level, SRT_SOCKOPT optname, const void * optval, int optlen)
|
||||
{ return CUDT::setsockopt(u, level, optname, optval, optlen); }
|
||||
|
||||
int srt_getsockflag(SRTSOCKET u, SRT_SOCKOPT opt, void* optval, int* optlen)
|
||||
{ return CUDT::getsockopt(u, 0, opt, optval, optlen); }
|
||||
int srt_setsockflag(SRTSOCKET u, SRT_SOCKOPT opt, const void* optval, int optlen)
|
||||
{ return CUDT::setsockopt(u, 0, opt, optval, optlen); }
|
||||
|
||||
int srt_send(SRTSOCKET u, const char * buf, int len) { return CUDT::send(u, buf, len, 0); }
|
||||
int srt_recv(SRTSOCKET u, char * buf, int len) { return CUDT::recv(u, buf, len, 0); }
|
||||
int srt_sendmsg(SRTSOCKET u, const char * buf, int len, int ttl, int inorder) { return CUDT::sendmsg(u, buf, len, ttl, 0!= inorder); }
|
||||
int srt_recvmsg(SRTSOCKET u, char * buf, int len) { uint64_t ign_srctime; return CUDT::recvmsg(u, buf, len, ign_srctime); }
|
||||
int64_t srt_sendfile(SRTSOCKET u, const char* path, int64_t* offset, int64_t size, int block)
|
||||
{
|
||||
if (!path || !offset )
|
||||
{
|
||||
return CUDT::setError(CUDTException(MJ_NOTSUP, MN_INVAL, 0));
|
||||
}
|
||||
fstream ifs(path, ios::binary | ios::in);
|
||||
if (!ifs)
|
||||
{
|
||||
return CUDT::setError(CUDTException(MJ_FILESYSTEM, MN_READFAIL, 0));
|
||||
}
|
||||
int64_t ret = CUDT::sendfile(u, ifs, *offset, size, block);
|
||||
ifs.close();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int64_t srt_recvfile(SRTSOCKET u, const char* path, int64_t* offset, int64_t size, int block)
|
||||
{
|
||||
if (!path || !offset )
|
||||
{
|
||||
return CUDT::setError(CUDTException(MJ_NOTSUP, MN_INVAL, 0));
|
||||
}
|
||||
fstream ofs(path, ios::binary | ios::out);
|
||||
if (!ofs)
|
||||
{
|
||||
return CUDT::setError(CUDTException(MJ_FILESYSTEM, MN_WRAVAIL, 0));
|
||||
}
|
||||
int64_t ret = CUDT::recvfile(u, ofs, *offset, size, block);
|
||||
ofs.close();
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern const SRT_MSGCTRL srt_msgctrl_default = { 0, -1, false, 0, 0, 0, 0 };
|
||||
|
||||
void srt_msgctrl_init(SRT_MSGCTRL* mctrl)
|
||||
{
|
||||
*mctrl = srt_msgctrl_default;
|
||||
}
|
||||
|
||||
int srt_sendmsg2(SRTSOCKET u, const char * buf, int len, SRT_MSGCTRL *mctrl)
|
||||
{
|
||||
// Allow NULL mctrl in the API, but not internally.
|
||||
if (mctrl)
|
||||
return CUDT::sendmsg2(u, buf, len, Ref(*mctrl));
|
||||
SRT_MSGCTRL mignore = srt_msgctrl_default;
|
||||
return CUDT::sendmsg2(u, buf, len, Ref(mignore));
|
||||
}
|
||||
|
||||
int srt_recvmsg2(SRTSOCKET u, char * buf, int len, SRT_MSGCTRL *mctrl)
|
||||
{
|
||||
if (mctrl)
|
||||
return CUDT::recvmsg2(u, buf, len, Ref(*mctrl));
|
||||
SRT_MSGCTRL mignore = srt_msgctrl_default;
|
||||
return CUDT::recvmsg2(u, buf, len, Ref(mignore));
|
||||
}
|
||||
|
||||
const char* srt_getlasterror_str() { return UDT::getlasterror().getErrorMessage(); }
|
||||
|
||||
int srt_getlasterror(int* loc_errno)
|
||||
{
|
||||
if ( loc_errno )
|
||||
*loc_errno = UDT::getlasterror().getErrno();
|
||||
return CUDT::getlasterror().getErrorCode();
|
||||
}
|
||||
|
||||
const char* srt_strerror(int code, int err)
|
||||
{
|
||||
static CUDTException e;
|
||||
e = CUDTException(CodeMajor(code/1000), CodeMinor(code%1000), err);
|
||||
return(e.getErrorMessage());
|
||||
}
|
||||
|
||||
|
||||
void srt_clearlasterror()
|
||||
{
|
||||
UDT::getlasterror().clear();
|
||||
}
|
||||
|
||||
int srt_bstats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear) { return CUDT::bstats(u, perf, 0!= clear); }
|
||||
int srt_bistats(SRTSOCKET u, SRT_TRACEBSTATS * perf, int clear, int instantaneous) { return CUDT::bstats(u, perf, 0!= clear, 0!= instantaneous); }
|
||||
|
||||
SRT_SOCKSTATUS srt_getsockstate(SRTSOCKET u) { return SRT_SOCKSTATUS((int)CUDT::getsockstate(u)); }
|
||||
|
||||
// event mechanism
|
||||
int srt_epoll_create() { return CUDT::epoll_create(); }
|
||||
|
||||
// You can use either SRT_EPOLL_* flags or EPOLL* flags from <sys/epoll.h>, both are the same. IN/OUT/ERR only.
|
||||
// events == NULL accepted, in which case all flags are set.
|
||||
int srt_epoll_add_usock(int eid, SRTSOCKET u, const int * events) { return CUDT::epoll_add_usock(eid, u, events); }
|
||||
|
||||
int srt_epoll_add_ssock(int eid, SYSSOCKET s, const int * events)
|
||||
{
|
||||
int flag = 0;
|
||||
|
||||
if (events) {
|
||||
flag = *events;
|
||||
} else {
|
||||
flag = SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR;
|
||||
}
|
||||
|
||||
// call UDT native function
|
||||
return CUDT::epoll_add_ssock(eid, s, &flag);
|
||||
}
|
||||
|
||||
int srt_epoll_remove_usock(int eid, SRTSOCKET u) { return CUDT::epoll_remove_usock(eid, u); }
|
||||
int srt_epoll_remove_ssock(int eid, SYSSOCKET s) { return CUDT::epoll_remove_ssock(eid, s); }
|
||||
|
||||
int srt_epoll_update_usock(int eid, SRTSOCKET u, const int * events)
|
||||
{
|
||||
return CUDT::epoll_update_usock(eid, u, events);
|
||||
}
|
||||
|
||||
int srt_epoll_update_ssock(int eid, SYSSOCKET s, const int * events)
|
||||
{
|
||||
int flag = 0;
|
||||
|
||||
if (events) {
|
||||
flag = *events;
|
||||
} else {
|
||||
flag = SRT_EPOLL_IN | SRT_EPOLL_OUT | SRT_EPOLL_ERR;
|
||||
}
|
||||
|
||||
// call UDT native function
|
||||
return CUDT::epoll_update_ssock(eid, s, &flag);
|
||||
}
|
||||
|
||||
int srt_epoll_wait(
|
||||
int eid,
|
||||
SRTSOCKET* readfds, int* rnum, SRTSOCKET* writefds, int* wnum,
|
||||
int64_t msTimeOut,
|
||||
SYSSOCKET* lrfds, int* lrnum, SYSSOCKET* lwfds, int* lwnum)
|
||||
{
|
||||
return UDT::epoll_wait2(
|
||||
eid,
|
||||
readfds, rnum, writefds, wnum,
|
||||
msTimeOut,
|
||||
lrfds, lrnum, lwfds, lwnum);
|
||||
}
|
||||
|
||||
int srt_epoll_uwait(int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut)
|
||||
{
|
||||
return UDT::epoll_uwait(
|
||||
eid,
|
||||
fdsSet,
|
||||
fdsSize,
|
||||
msTimeOut);
|
||||
}
|
||||
|
||||
// use this function to set flags. Default flags are always "everything unset".
|
||||
// Pass 0 here to clear everything, or nonzero to set a desired flag.
|
||||
// Pass -1 to not change anything (but still get the current flag value).
|
||||
int32_t srt_epoll_set(int eid, int32_t flags) { return CUDT::epoll_set(eid, flags); }
|
||||
|
||||
int srt_epoll_release(int eid) { return CUDT::epoll_release(eid); }
|
||||
|
||||
void srt_setloglevel(int ll)
|
||||
{
|
||||
UDT::setloglevel(srt_logging::LogLevel::type(ll));
|
||||
}
|
||||
|
||||
void srt_addlogfa(int fa)
|
||||
{
|
||||
UDT::addlogfa(srt_logging::LogFA(fa));
|
||||
}
|
||||
|
||||
void srt_dellogfa(int fa)
|
||||
{
|
||||
UDT::dellogfa(srt_logging::LogFA(fa));
|
||||
}
|
||||
|
||||
void srt_resetlogfa(const int* fara, size_t fara_size)
|
||||
{
|
||||
UDT::resetlogfa(fara, fara_size);
|
||||
}
|
||||
|
||||
void srt_setloghandler(void* opaque, SRT_LOG_HANDLER_FN* handler)
|
||||
{
|
||||
UDT::setloghandler(opaque, handler);
|
||||
}
|
||||
|
||||
void srt_setlogflags(int flags)
|
||||
{
|
||||
UDT::setlogflags(flags);
|
||||
}
|
||||
|
||||
int srt_getsndbuffer(SRTSOCKET sock, size_t* blocks, size_t* bytes)
|
||||
{
|
||||
return CUDT::getsndbuffer(sock, blocks, bytes);
|
||||
}
|
||||
|
||||
enum SRT_REJECT_REASON srt_getrejectreason(SRTSOCKET sock)
|
||||
{
|
||||
return CUDT::rejectReason(sock);
|
||||
}
|
||||
|
||||
int srt_listen_callback(SRTSOCKET lsn, srt_listen_callback_fn* hook, void* opaq)
|
||||
{
|
||||
if (!hook)
|
||||
return CUDT::setError(CUDTException(MJ_NOTSUP, MN_INVAL));
|
||||
|
||||
return CUDT::installAcceptHook(lsn, hook, opaq);
|
||||
}
|
||||
|
||||
}
|
140
trunk/3rdparty/srt-1-fit/srtcore/srt_compat.c
vendored
Normal file
140
trunk/3rdparty/srt-1-fit/srtcore/srt_compat.c
vendored
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
// Implementation file for srt_compat.h
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
// Prevents from misconfiguration through preprocessor.
|
||||
|
||||
#include <srt_compat.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#if defined(__unix__) && !defined(BSD)
|
||||
#include <features.h>
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
|
||||
static const char* SysStrError_Fallback(int errnum, char* buf, size_t buflen)
|
||||
{
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
_snprintf(buf, buflen - 1, "ERROR CODE %d", errnum);
|
||||
buf[buflen - 1] = '\0';
|
||||
#else
|
||||
snprintf(buf, buflen, "ERROR CODE %d", errnum);
|
||||
#endif
|
||||
return buf;
|
||||
}
|
||||
|
||||
// This function is a portable and thread-safe version of `strerror`.
|
||||
// It requires a user-supplied buffer to store the message. The returned
|
||||
// value is always equal to the given buffer pointer. If the system
|
||||
// error message is longer than the given buflen, it will be trimmed.
|
||||
// When the error code is incorrect for the given error message function,
|
||||
// a fallback message will be returned, either as returned by the underlying
|
||||
// function, or crafted by this function as a response to error in an
|
||||
// underlying function.
|
||||
extern const char * SysStrError(int errnum, char * buf, size_t buflen)
|
||||
{
|
||||
if (buf == NULL || buflen < 4) // Required to put ??? into it as a fallback
|
||||
{
|
||||
errno = EFAULT;
|
||||
return buf;
|
||||
}
|
||||
|
||||
buf[0] = '\0';
|
||||
|
||||
#if defined(_WIN32)
|
||||
const char* lpMsgBuf;
|
||||
|
||||
// Note: Intentionally the "fixed char size" types are used despite using
|
||||
// character size dependent FormatMessage (instead of FormatMessageA) so that
|
||||
// your compilation fails when you use wide characters.
|
||||
// The problem is that when TCHAR != char, then the buffer written this way
|
||||
// would have to be converted to ASCII, not just copied by strncpy.
|
||||
FormatMessage(0
|
||||
| FORMAT_MESSAGE_ALLOCATE_BUFFER
|
||||
| FORMAT_MESSAGE_FROM_SYSTEM
|
||||
| FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, // no lpSource
|
||||
errnum, // dwMessageId (as controlled by FORMAT_MESSAGE_FROM_SYSTEM)
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
// This below parameter normally should contain a pointer to an allocated buffer,
|
||||
// and this way it's LPTSTR. But when FORMAT_MESSAGE_ALLOCATE_BUFFER, then it is
|
||||
// expected to be a the value of LPTSTR* type, converted to LPTSTR, that designates
|
||||
// a pointer to a variable of type LPTSTR, to which the newly allocated buffer is
|
||||
// assigned. This buffer should be freed afterwards using LocalFree().
|
||||
(LPSTR)&lpMsgBuf,
|
||||
0, NULL);
|
||||
|
||||
if (lpMsgBuf)
|
||||
{
|
||||
strncpy(buf, lpMsgBuf, buflen-1);
|
||||
buf[buflen-1] = 0;
|
||||
LocalFree((HLOCAL)lpMsgBuf);
|
||||
}
|
||||
else
|
||||
{
|
||||
SysStrError_Fallback(errnum, buf, buflen);
|
||||
}
|
||||
|
||||
return buf;
|
||||
|
||||
#elif (!defined(__GNU_LIBRARY__) && !defined(__GLIBC__) ) \
|
||||
|| (( (_POSIX_C_SOURCE >= 200112L) || (_XOPEN_SOURCE >= 600)) && ! _GNU_SOURCE )
|
||||
// POSIX/XSI-compliant version.
|
||||
// Overall general POSIX version: returns status.
|
||||
// 0 for success, otherwise it's:
|
||||
// - possibly -1 and the error code is in ::errno
|
||||
// - possibly the error code itself
|
||||
// The details of the errror are not interesting; simply
|
||||
// craft a fallback message in this case.
|
||||
if (strerror_r(errnum, buf, buflen) != 0)
|
||||
{
|
||||
return SysStrError_Fallback(errnum, buf, buflen);
|
||||
}
|
||||
return buf;
|
||||
#else
|
||||
// GLIBC is non-standard under these conditions.
|
||||
// GNU version: returns the pointer to the message.
|
||||
// This is either equal to the local buffer (buf)
|
||||
// or some system-wide (constant) storage. To maintain
|
||||
// stability of the API, this overall function shall
|
||||
// always return the local buffer and the message in
|
||||
// this buffer - so these cases should be distinguished
|
||||
// and the internal storage copied to the buffer.
|
||||
|
||||
char * gnu_buffer = strerror_r(errnum, buf, buflen);
|
||||
if (!gnu_buffer)
|
||||
{
|
||||
// This should never happen, so just a paranoid check
|
||||
return SysStrError_Fallback(errnum, buf, buflen);
|
||||
}
|
||||
|
||||
// If they are the same, the message is already copied
|
||||
// (and it's usually a "fallback message" for an error case).
|
||||
if (gnu_buffer != buf)
|
||||
{
|
||||
strncpy(buf, gnu_buffer, buflen-1);
|
||||
buf[buflen-1] = 0; // guarantee what strncpy doesn't
|
||||
}
|
||||
|
||||
return buf;
|
||||
#endif
|
||||
}
|
105
trunk/3rdparty/srt-1-fit/srtcore/srt_compat.h
vendored
Normal file
105
trunk/3rdparty/srt-1-fit/srtcore/srt_compat.h
vendored
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef HAISRT_COMPAT_H__
|
||||
#define HAISRT_COMPAT_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifndef SRT_API
|
||||
#ifdef _WIN32
|
||||
#ifndef __MINGW__
|
||||
#ifdef SRT_DYNAMIC
|
||||
#ifdef SRT_EXPORTS
|
||||
#define SRT_API __declspec(dllexport)
|
||||
#else
|
||||
#define SRT_API __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
#define SRT_API
|
||||
#endif
|
||||
#else
|
||||
#define SRT_API
|
||||
#endif
|
||||
#else
|
||||
#define SRT_API __attribute__ ((visibility("default")))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
// https://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx
|
||||
// printf() Format for ssize_t
|
||||
#if !defined(PRIzd)
|
||||
#define PRIzd "Id"
|
||||
#endif
|
||||
// printf() Format for size_t
|
||||
#if !defined(PRIzu)
|
||||
#define PRIzu "Iu"
|
||||
#endif
|
||||
#else
|
||||
// http://www.gnu.org/software/libc/manual/html_node/Integer-Conversions.html
|
||||
// printf() Format for ssize_t
|
||||
#if !defined(PRIzd)
|
||||
#define PRIzd "zd"
|
||||
#endif
|
||||
// printf() Format for size_t
|
||||
#if !defined(PRIzu)
|
||||
#define PRIzu "zu"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Ensures that we store the error in the buffer and return the bufer. */
|
||||
SRT_API const char * SysStrError(int errnum, char * buf, size_t buflen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern C
|
||||
|
||||
|
||||
// Extra C++ stuff. Included only in C++ mode.
|
||||
|
||||
|
||||
#include <string>
|
||||
inline std::string SysStrError(int errnum)
|
||||
{
|
||||
char buf[1024];
|
||||
return SysStrError(errnum, buf, 1024);
|
||||
}
|
||||
|
||||
inline struct tm SysLocalTime(time_t tt)
|
||||
{
|
||||
struct tm tms;
|
||||
memset(&tms, 0, sizeof tms);
|
||||
#ifdef _WIN32
|
||||
errno_t rr = localtime_s(&tms, &tt);
|
||||
if (rr == 0)
|
||||
return tms;
|
||||
#else
|
||||
tms = *localtime_r(&tt, &tms);
|
||||
#endif
|
||||
|
||||
return tms;
|
||||
}
|
||||
|
||||
|
||||
#endif // defined C++
|
||||
|
||||
#endif // HAISRT_COMPAT_H__
|
45
trunk/3rdparty/srt-1-fit/srtcore/srt_shared.rc
vendored
Normal file
45
trunk/3rdparty/srt-1-fit/srtcore/srt_shared.rc
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "version.h"
|
||||
#include "winres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
#ifdef SRT_VERSION_BUILD
|
||||
FILEVERSION SRT_VERSION_MAJOR, SRT_VERSION_MINOR, SRT_VERSION_PATCH, SRT_VERSION_BUILD
|
||||
#else
|
||||
FILEVERSION SRT_VERSION_MAJOR, SRT_VERSION_MINOR, SRT_VERSION_PATCH
|
||||
#endif
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "080904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "SRT Alliance"
|
||||
VALUE "FileDescription", "SRT Local Build"
|
||||
VALUE "InternalName", "srt.dll"
|
||||
VALUE "LegalCopyright", ""
|
||||
VALUE "OriginalFilename", "srt.dll"
|
||||
VALUE "ProductName", "SRT"
|
||||
VALUE "ProductVersion", SRT_VERSION_STRING
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x809, 1200
|
||||
END
|
||||
END
|
||||
|
87
trunk/3rdparty/srt-1-fit/srtcore/threadname.h
vendored
Normal file
87
trunk/3rdparty/srt-1-fit/srtcore/threadname.h
vendored
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef INC__THREADNAME_H
|
||||
#define INC__THREADNAME_H
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <sys/prctl.h>
|
||||
|
||||
class ThreadName
|
||||
{
|
||||
char old_name[128];
|
||||
char new_name[128];
|
||||
bool good;
|
||||
|
||||
public:
|
||||
static const size_t BUFSIZE = 128;
|
||||
|
||||
static bool get(char* namebuf)
|
||||
{
|
||||
return prctl(PR_GET_NAME, (unsigned long)namebuf, 0, 0) != -1;
|
||||
}
|
||||
|
||||
static bool set(const char* name)
|
||||
{
|
||||
return prctl(PR_SET_NAME, (unsigned long)name, 0, 0) != -1;
|
||||
}
|
||||
|
||||
|
||||
ThreadName(const char* name)
|
||||
{
|
||||
if ( (good = get(old_name)) )
|
||||
{
|
||||
snprintf(new_name, 127, "%s", name);
|
||||
new_name[127] = 0;
|
||||
prctl(PR_SET_NAME, (unsigned long)new_name, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
~ThreadName()
|
||||
{
|
||||
if ( good )
|
||||
prctl(PR_SET_NAME, (unsigned long)old_name, 0, 0);
|
||||
}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
// Fake class, which does nothing. You can also take a look how
|
||||
// this works in other systems that are not supported here and add
|
||||
// the support. This is a fallback for systems that do not support
|
||||
// thread names.
|
||||
|
||||
class ThreadName
|
||||
{
|
||||
public:
|
||||
|
||||
static bool get(char*) { return false; }
|
||||
static bool set(const char*) { return false; }
|
||||
|
||||
ThreadName(const char*)
|
||||
{
|
||||
}
|
||||
|
||||
~ThreadName() // just to make it "non-trivially-destructible" for compatibility with normal version
|
||||
{
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
#endif
|
413
trunk/3rdparty/srt-1-fit/srtcore/udt.h
vendored
Normal file
413
trunk/3rdparty/srt-1-fit/srtcore/udt.h
vendored
Normal file
|
@ -0,0 +1,413 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Yunhong Gu, last updated 01/18/2011
|
||||
modified by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
/* WARNING!!!
|
||||
* Since now this file is a "C and C++ header".
|
||||
* It should be then able to be interpreted by C compiler, so
|
||||
* all C++-oriented things must be ifdef'd-out by __cplusplus.
|
||||
*
|
||||
* Mind also comments - to prevent any portability problems,
|
||||
* B/C++ comments (// -> EOL) should not be used unless the
|
||||
* area is under __cplusplus condition already.
|
||||
*
|
||||
* NOTE: this file contains _STRUCTURES_ that are common to C and C++,
|
||||
* plus some functions and other functionalities ONLY FOR C++. This
|
||||
* file doesn't contain _FUNCTIONS_ predicted to be used in C - see udtc.h
|
||||
*/
|
||||
|
||||
#ifndef __UDT_H__
|
||||
#define __UDT_H__
|
||||
|
||||
#include "srt.h"
|
||||
|
||||
/*
|
||||
* SRT_ENABLE_THREADCHECK (THIS IS SET IN MAKEFILE NOT HERE)
|
||||
*/
|
||||
#if defined(SRT_ENABLE_THREADCHECK)
|
||||
#include <threadcheck.h>
|
||||
#else
|
||||
#define THREAD_STATE_INIT(name)
|
||||
#define THREAD_EXIT()
|
||||
#define THREAD_PAUSED()
|
||||
#define THREAD_RESUMED()
|
||||
#define INCREMENT_THREAD_ITERATIONS()
|
||||
#endif
|
||||
|
||||
/* Obsolete way to define MINGW */
|
||||
#ifndef __MINGW__
|
||||
#if defined(__MINGW32__) || defined(__MINGW64__)
|
||||
#define __MINGW__ 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <fstream>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#endif
|
||||
|
||||
|
||||
// Legacy/backward/deprecated
|
||||
#define UDT_API SRT_API
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//if compiling on VC6.0 or pre-WindowsXP systems
|
||||
//use -DLEGACY_WIN32
|
||||
|
||||
//if compiling with MinGW, it only works on XP or above
|
||||
//use -D_WIN32_WINNT=0x0501
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef __cplusplus
|
||||
// This facility is used only for select() function.
|
||||
// This is considered obsolete and the epoll() functionality rather should be used.
|
||||
typedef std::set<SRTSOCKET> ud_set;
|
||||
#define UD_CLR(u, uset) ((uset)->erase(u))
|
||||
#define UD_ISSET(u, uset) ((uset)->find(u) != (uset)->end())
|
||||
#define UD_SET(u, uset) ((uset)->insert(u))
|
||||
#define UD_ZERO(uset) ((uset)->clear())
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Legacy names
|
||||
|
||||
#define UDT_MSS SRTO_MSS
|
||||
#define UDT_SNDSYN SRTO_SNDSYN
|
||||
#define UDT_RCVSYN SRTO_RCVSYN
|
||||
#define UDT_FC SRTO_FC
|
||||
#define UDT_SNDBUF SRTO_SNDBUF
|
||||
#define UDT_RCVBUF SRTO_RCVBUF
|
||||
#define UDT_LINGER SRTO_LINGER
|
||||
#define UDP_SNDBUF SRTO_UDP_SNDBUF
|
||||
#define UDP_RCVBUF SRTO_UDP_RCVBUF
|
||||
#define UDT_MAXMSG SRTO_MAXMSG
|
||||
#define UDT_MSGTTL SRTO_MSGTTL
|
||||
#define UDT_RENDEZVOUS SRTO_RENDEZVOUS
|
||||
#define UDT_SNDTIMEO SRTO_SNDTIMEO
|
||||
#define UDT_RCVTIMEO SRTO_RCVTIMEO
|
||||
#define UDT_REUSEADDR SRTO_REUSEADDR
|
||||
#define UDT_MAXBW SRTO_MAXBW
|
||||
#define UDT_STATE SRTO_STATE
|
||||
#define UDT_EVENT SRTO_EVENT
|
||||
#define UDT_SNDDATA SRTO_SNDDATA
|
||||
#define UDT_RCVDATA SRTO_RCVDATA
|
||||
#define SRT_SENDER SRTO_SENDER
|
||||
#define SRT_TSBPDMODE SRTO_TSBPDMODE
|
||||
#define SRT_TSBPDDELAY SRTO_TSBPDDELAY
|
||||
#define SRT_INPUTBW SRTO_INPUTBW
|
||||
#define SRT_OHEADBW SRTO_OHEADBW
|
||||
#define SRT_PASSPHRASE SRTO_PASSPHRASE
|
||||
#define SRT_PBKEYLEN SRTO_PBKEYLEN
|
||||
#define SRT_KMSTATE SRTO_KMSTATE
|
||||
#define SRT_IPTTL SRTO_IPTTL
|
||||
#define SRT_IPTOS SRTO_IPTOS
|
||||
#define SRT_TLPKTDROP SRTO_TLPKTDROP
|
||||
#define SRT_TSBPDMAXLAG SRTO_TSBPDMAXLAG
|
||||
#define SRT_RCVNAKREPORT SRTO_NAKREPORT
|
||||
#define SRT_CONNTIMEO SRTO_CONNTIMEO
|
||||
#define SRT_SNDPBKEYLEN SRTO_SNDPBKEYLEN
|
||||
#define SRT_RCVPBKEYLEN SRTO_RCVPBKEYLEN
|
||||
#define SRT_SNDPEERKMSTATE SRTO_SNDPEERKMSTATE
|
||||
#define SRT_RCVKMSTATE SRTO_RCVKMSTATE
|
||||
|
||||
#define UDT_EPOLL_OPT SRT_EPOLL_OPT
|
||||
#define UDT_EPOLL_IN SRT_EPOLL_IN
|
||||
#define UDT_EPOLL_OUT SRT_EPOLL_OUT
|
||||
#define UDT_EPOLL_ERR SRT_EPOLL_ERR
|
||||
|
||||
/* Binary backward compatibility obsolete options */
|
||||
#define SRT_NAKREPORT SRT_RCVNAKREPORT
|
||||
|
||||
#if !defined(SRT_DISABLE_LEGACY_UDTSTATUS)
|
||||
#define UDTSTATUS SRT_SOCKSTATUS
|
||||
#define INIT SRTS_INIT
|
||||
#define OPENED SRTS_OPENED
|
||||
#define LISTENING SRTS_LISTENING
|
||||
#define CONNECTING SRTS_CONNECTING
|
||||
#define CONNECTED SRTS_CONNECTED
|
||||
#define BROKEN SRTS_BROKEN
|
||||
#define CLOSING SRTS_CLOSING
|
||||
#define CLOSED SRTS_CLOSED
|
||||
#define NONEXIST SRTS_NONEXIST
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct CPerfMon
|
||||
{
|
||||
// global measurements
|
||||
int64_t msTimeStamp; // time since the UDT entity is started, in milliseconds
|
||||
int64_t pktSentTotal; // total number of sent data packets, including retransmissions
|
||||
int64_t pktRecvTotal; // total number of received packets
|
||||
int pktSndLossTotal; // total number of lost packets (sender side)
|
||||
int pktRcvLossTotal; // total number of lost packets (receiver side)
|
||||
int pktRetransTotal; // total number of retransmitted packets
|
||||
int pktRcvRetransTotal; // total number of retransmitted packets received
|
||||
int pktSentACKTotal; // total number of sent ACK packets
|
||||
int pktRecvACKTotal; // total number of received ACK packets
|
||||
int pktSentNAKTotal; // total number of sent NAK packets
|
||||
int pktRecvNAKTotal; // total number of received NAK packets
|
||||
int64_t usSndDurationTotal; // total time duration when UDT is sending data (idle time exclusive)
|
||||
|
||||
// local measurements
|
||||
int64_t pktSent; // number of sent data packets, including retransmissions
|
||||
int64_t pktRecv; // number of received packets
|
||||
int pktSndLoss; // number of lost packets (sender side)
|
||||
int pktRcvLoss; // number of lost packets (receiver side)
|
||||
int pktRetrans; // number of retransmitted packets
|
||||
int pktRcvRetrans; // number of retransmitted packets received
|
||||
int pktSentACK; // number of sent ACK packets
|
||||
int pktRecvACK; // number of received ACK packets
|
||||
int pktSentNAK; // number of sent NAK packets
|
||||
int pktRecvNAK; // number of received NAK packets
|
||||
double mbpsSendRate; // sending rate in Mb/s
|
||||
double mbpsRecvRate; // receiving rate in Mb/s
|
||||
int64_t usSndDuration; // busy sending time (i.e., idle time exclusive)
|
||||
int pktReorderDistance; // size of order discrepancy in received sequences
|
||||
double pktRcvAvgBelatedTime; // average time of packet delay for belated packets (packets with sequence past the ACK)
|
||||
int64_t pktRcvBelated; // number of received AND IGNORED packets due to having come too late
|
||||
|
||||
// instant measurements
|
||||
double usPktSndPeriod; // packet sending period, in microseconds
|
||||
int pktFlowWindow; // flow window size, in number of packets
|
||||
int pktCongestionWindow; // congestion window size, in number of packets
|
||||
int pktFlightSize; // number of packets on flight
|
||||
double msRTT; // RTT, in milliseconds
|
||||
double mbpsBandwidth; // estimated bandwidth, in Mb/s
|
||||
int byteAvailSndBuf; // available UDT sender buffer size
|
||||
int byteAvailRcvBuf; // available UDT receiver buffer size
|
||||
};
|
||||
|
||||
typedef SRTSOCKET UDTSOCKET; //legacy alias
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
// Class CUDTException exposed for C++ API.
|
||||
// This is actually useless, unless you'd use a DIRECT C++ API,
|
||||
// however there's no such API so far. The current C++ API for UDT/SRT
|
||||
// is predicted to NEVER LET ANY EXCEPTION out of implementation,
|
||||
// so it's useless to catch this exception anyway.
|
||||
|
||||
class UDT_API CUDTException
|
||||
{
|
||||
public:
|
||||
|
||||
CUDTException(CodeMajor major = MJ_SUCCESS, CodeMinor minor = MN_NONE, int err = -1);
|
||||
CUDTException(const CUDTException& e);
|
||||
|
||||
~CUDTException();
|
||||
|
||||
/// Get the description of the exception.
|
||||
/// @return Text message for the exception description.
|
||||
|
||||
const char* getErrorMessage();
|
||||
|
||||
/// Get the system errno for the exception.
|
||||
/// @return errno.
|
||||
|
||||
int getErrorCode() const;
|
||||
|
||||
/// Get the system network errno for the exception.
|
||||
/// @return errno.
|
||||
|
||||
int getErrno() const;
|
||||
/// Clear the error code.
|
||||
|
||||
void clear();
|
||||
|
||||
private:
|
||||
CodeMajor m_iMajor; // major exception categories
|
||||
CodeMinor m_iMinor; // for specific error reasons
|
||||
int m_iErrno; // errno returned by the system if there is any
|
||||
std::string m_strMsg; // text error message
|
||||
|
||||
std::string m_strAPI; // the name of UDT function that returns the error
|
||||
std::string m_strDebug; // debug information, set to the original place that causes the error
|
||||
|
||||
public: // Legacy Error Code
|
||||
|
||||
static const int EUNKNOWN = SRT_EUNKNOWN;
|
||||
static const int SUCCESS = SRT_SUCCESS;
|
||||
static const int ECONNSETUP = SRT_ECONNSETUP;
|
||||
static const int ENOSERVER = SRT_ENOSERVER;
|
||||
static const int ECONNREJ = SRT_ECONNREJ;
|
||||
static const int ESOCKFAIL = SRT_ESOCKFAIL;
|
||||
static const int ESECFAIL = SRT_ESECFAIL;
|
||||
static const int ECONNFAIL = SRT_ECONNFAIL;
|
||||
static const int ECONNLOST = SRT_ECONNLOST;
|
||||
static const int ENOCONN = SRT_ENOCONN;
|
||||
static const int ERESOURCE = SRT_ERESOURCE;
|
||||
static const int ETHREAD = SRT_ETHREAD;
|
||||
static const int ENOBUF = SRT_ENOBUF;
|
||||
static const int EFILE = SRT_EFILE;
|
||||
static const int EINVRDOFF = SRT_EINVRDOFF;
|
||||
static const int ERDPERM = SRT_ERDPERM;
|
||||
static const int EINVWROFF = SRT_EINVWROFF;
|
||||
static const int EWRPERM = SRT_EWRPERM;
|
||||
static const int EINVOP = SRT_EINVOP;
|
||||
static const int EBOUNDSOCK = SRT_EBOUNDSOCK;
|
||||
static const int ECONNSOCK = SRT_ECONNSOCK;
|
||||
static const int EINVPARAM = SRT_EINVPARAM;
|
||||
static const int EINVSOCK = SRT_EINVSOCK;
|
||||
static const int EUNBOUNDSOCK = SRT_EUNBOUNDSOCK;
|
||||
static const int ESTREAMILL = SRT_EINVALMSGAPI;
|
||||
static const int EDGRAMILL = SRT_EINVALBUFFERAPI;
|
||||
static const int ENOLISTEN = SRT_ENOLISTEN;
|
||||
static const int ERDVNOSERV = SRT_ERDVNOSERV;
|
||||
static const int ERDVUNBOUND = SRT_ERDVUNBOUND;
|
||||
static const int EINVALMSGAPI = SRT_EINVALMSGAPI;
|
||||
static const int EINVALBUFFERAPI = SRT_EINVALBUFFERAPI;
|
||||
static const int EDUPLISTEN = SRT_EDUPLISTEN;
|
||||
static const int ELARGEMSG = SRT_ELARGEMSG;
|
||||
static const int EINVPOLLID = SRT_EINVPOLLID;
|
||||
static const int EASYNCFAIL = SRT_EASYNCFAIL;
|
||||
static const int EASYNCSND = SRT_EASYNCSND;
|
||||
static const int EASYNCRCV = SRT_EASYNCRCV;
|
||||
static const int ETIMEOUT = SRT_ETIMEOUT;
|
||||
static const int ECONGEST = SRT_ECONGEST;
|
||||
static const int EPEERERR = SRT_EPEERERR;
|
||||
};
|
||||
|
||||
namespace UDT
|
||||
{
|
||||
|
||||
typedef CUDTException ERRORINFO;
|
||||
//typedef UDT_SOCKOPT SOCKOPT;
|
||||
typedef CPerfMon TRACEINFO;
|
||||
typedef CBytePerfMon TRACEBSTATS;
|
||||
typedef ud_set UDSET;
|
||||
|
||||
UDT_API extern const SRTSOCKET INVALID_SOCK;
|
||||
#undef ERROR
|
||||
UDT_API extern const int ERROR;
|
||||
|
||||
UDT_API int startup();
|
||||
UDT_API int cleanup();
|
||||
UDT_API UDTSOCKET socket(int af, int type, int protocol);
|
||||
UDT_API int bind(UDTSOCKET u, const struct sockaddr* name, int namelen);
|
||||
UDT_API int bind2(UDTSOCKET u, UDPSOCKET udpsock);
|
||||
UDT_API int listen(UDTSOCKET u, int backlog);
|
||||
UDT_API UDTSOCKET accept(UDTSOCKET u, struct sockaddr* addr, int* addrlen);
|
||||
UDT_API int connect(UDTSOCKET u, const struct sockaddr* name, int namelen);
|
||||
UDT_API int close(UDTSOCKET u);
|
||||
UDT_API int getpeername(UDTSOCKET u, struct sockaddr* name, int* namelen);
|
||||
UDT_API int getsockname(UDTSOCKET u, struct sockaddr* name, int* namelen);
|
||||
UDT_API int getsockopt(UDTSOCKET u, int level, SRT_SOCKOPT optname, void* optval, int* optlen);
|
||||
UDT_API int setsockopt(UDTSOCKET u, int level, SRT_SOCKOPT optname, const void* optval, int optlen);
|
||||
UDT_API int send(UDTSOCKET u, const char* buf, int len, int flags);
|
||||
UDT_API int recv(UDTSOCKET u, char* buf, int len, int flags);
|
||||
|
||||
UDT_API int sendmsg(UDTSOCKET u, const char* buf, int len, int ttl = -1, bool inorder = false, uint64_t srctime = 0);
|
||||
UDT_API int recvmsg(UDTSOCKET u, char* buf, int len, uint64_t& srctime);
|
||||
UDT_API int recvmsg(UDTSOCKET u, char* buf, int len);
|
||||
|
||||
UDT_API int64_t sendfile(UDTSOCKET u, std::fstream& ifs, int64_t& offset, int64_t size, int block = 364000);
|
||||
UDT_API int64_t recvfile(UDTSOCKET u, std::fstream& ofs, int64_t& offset, int64_t size, int block = 7280000);
|
||||
UDT_API int64_t sendfile2(UDTSOCKET u, const char* path, int64_t* offset, int64_t size, int block = 364000);
|
||||
UDT_API int64_t recvfile2(UDTSOCKET u, const char* path, int64_t* offset, int64_t size, int block = 7280000);
|
||||
|
||||
// select and selectEX are DEPRECATED; please use epoll.
|
||||
UDT_API int select(int nfds, UDSET* readfds, UDSET* writefds, UDSET* exceptfds, const struct timeval* timeout);
|
||||
UDT_API int selectEx(const std::vector<UDTSOCKET>& fds, std::vector<UDTSOCKET>* readfds,
|
||||
std::vector<UDTSOCKET>* writefds, std::vector<UDTSOCKET>* exceptfds, int64_t msTimeOut);
|
||||
|
||||
UDT_API int epoll_create();
|
||||
UDT_API int epoll_add_usock(int eid, UDTSOCKET u, const int* events = NULL);
|
||||
UDT_API int epoll_add_ssock(int eid, SYSSOCKET s, const int* events = NULL);
|
||||
UDT_API int epoll_remove_usock(int eid, UDTSOCKET u);
|
||||
UDT_API int epoll_remove_ssock(int eid, SYSSOCKET s);
|
||||
UDT_API int epoll_update_usock(int eid, UDTSOCKET u, const int* events = NULL);
|
||||
UDT_API int epoll_update_ssock(int eid, SYSSOCKET s, const int* events = NULL);
|
||||
UDT_API int epoll_wait(int eid, std::set<UDTSOCKET>* readfds, std::set<UDTSOCKET>* writefds, int64_t msTimeOut,
|
||||
std::set<SYSSOCKET>* lrfds = NULL, std::set<SYSSOCKET>* wrfds = NULL);
|
||||
UDT_API int epoll_wait2(int eid, UDTSOCKET* readfds, int* rnum, UDTSOCKET* writefds, int* wnum, int64_t msTimeOut,
|
||||
SYSSOCKET* lrfds = NULL, int* lrnum = NULL, SYSSOCKET* lwfds = NULL, int* lwnum = NULL);
|
||||
UDT_API int epoll_uwait(const int eid, SRT_EPOLL_EVENT* fdsSet, int fdsSize, int64_t msTimeOut);
|
||||
UDT_API int epoll_release(int eid);
|
||||
UDT_API ERRORINFO& getlasterror();
|
||||
UDT_API int getlasterror_code();
|
||||
UDT_API const char* getlasterror_desc();
|
||||
UDT_API int bstats(UDTSOCKET u, TRACEBSTATS* perf, bool clear = true);
|
||||
UDT_API SRT_SOCKSTATUS getsockstate(UDTSOCKET u);
|
||||
|
||||
// This is a C++ SRT API extension. This is not a part of legacy UDT API.
|
||||
UDT_API void setloglevel(srt_logging::LogLevel::type ll);
|
||||
UDT_API void addlogfa(srt_logging::LogFA fa);
|
||||
UDT_API void dellogfa(srt_logging::LogFA fa);
|
||||
UDT_API void resetlogfa(std::set<srt_logging::LogFA> fas);
|
||||
UDT_API void resetlogfa(const int* fara, size_t fara_size);
|
||||
UDT_API void setlogstream(std::ostream& stream);
|
||||
UDT_API void setloghandler(void* opaque, SRT_LOG_HANDLER_FN* handler);
|
||||
UDT_API void setlogflags(int flags);
|
||||
|
||||
UDT_API bool setstreamid(UDTSOCKET u, const std::string& sid);
|
||||
UDT_API std::string getstreamid(UDTSOCKET u);
|
||||
|
||||
} // namespace UDT
|
||||
|
||||
// This is a log configuration used inside SRT.
|
||||
// Applications using SRT, if they want to use the logging mechanism
|
||||
// are free to create their own logger configuration objects for their
|
||||
// own logger FA objects, or create their own. The object of this type
|
||||
// is required to initialize the logger FA object.
|
||||
namespace srt_logging { struct LogConfig; }
|
||||
UDT_API extern srt_logging::LogConfig srt_logger_config;
|
||||
|
||||
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif
|
969
trunk/3rdparty/srt-1-fit/srtcore/utilities.h
vendored
Executable file
969
trunk/3rdparty/srt-1-fit/srtcore/utilities.h
vendored
Executable file
|
@ -0,0 +1,969 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef INC__SRT_UTILITIES_H
|
||||
#define INC__SRT_UTILITIES_H
|
||||
|
||||
|
||||
#ifdef __GNUG__
|
||||
#define ATR_UNUSED __attribute__((unused))
|
||||
#define ATR_DEPRECATED __attribute__((deprecated))
|
||||
#else
|
||||
#define ATR_UNUSED
|
||||
#define ATR_DEPRECATED
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus) && __cplusplus > 199711L
|
||||
#define HAVE_CXX11 1
|
||||
|
||||
// For gcc 4.7, claim C++11 is supported, as long as experimental C++0x is on,
|
||||
// however it's only the "most required C++11 support".
|
||||
#if defined(__GXX_EXPERIMENTAL_CXX0X__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 7 // 4.7 only!
|
||||
#define ATR_NOEXCEPT
|
||||
#define ATR_CONSTEXPR
|
||||
#define ATR_OVERRIDE
|
||||
#define ATR_FINAL
|
||||
#else
|
||||
#define HAVE_FULL_CXX11 1
|
||||
#define ATR_NOEXCEPT noexcept
|
||||
#define ATR_CONSTEXPR constexpr
|
||||
#define ATR_OVERRIDE override
|
||||
#define ATR_FINAL final
|
||||
#endif
|
||||
|
||||
// Microsoft Visual Studio supports C++11, but not fully,
|
||||
// and still did not change the value of __cplusplus. Treat
|
||||
// this special way.
|
||||
// _MSC_VER == 1800 means Microsoft Visual Studio 2013.
|
||||
#elif defined(_MSC_VER) && _MSC_VER >= 1800
|
||||
#define HAVE_CXX11 1
|
||||
#if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023026
|
||||
#define HAVE_FULL_CXX11 1
|
||||
#define ATR_NOEXCEPT noexcept
|
||||
#define ATR_CONSTEXPR constexpr
|
||||
#define ATR_OVERRIDE override
|
||||
#define ATR_FINAL final
|
||||
#else
|
||||
#define ATR_NOEXCEPT
|
||||
#define ATR_CONSTEXPR
|
||||
#define ATR_OVERRIDE
|
||||
#define ATR_FINAL
|
||||
#endif
|
||||
#else
|
||||
#define HAVE_CXX11 0
|
||||
#define ATR_NOEXCEPT // throw() - bad idea
|
||||
#define ATR_CONSTEXPR
|
||||
#define ATR_OVERRIDE
|
||||
#define ATR_FINAL
|
||||
|
||||
#endif
|
||||
|
||||
#if !HAVE_CXX11 && defined(REQUIRE_CXX11) && REQUIRE_CXX11 == 1
|
||||
#error "The currently compiled application required C++11, but your compiler doesn't support it."
|
||||
#endif
|
||||
|
||||
|
||||
// Windows warning disabler
|
||||
#define _CRT_SECURE_NO_WARNINGS 1
|
||||
|
||||
#include "platform_sys.h"
|
||||
|
||||
// Happens that these are defined, undefine them in advance
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <bitset>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#if HAVE_CXX11
|
||||
#include <type_traits>
|
||||
#endif
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
// -------------- UTILITIES ------------------------
|
||||
|
||||
// --- ENDIAN ---
|
||||
// Copied from: https://gist.github.com/panzi/6856583
|
||||
// License: Public Domain.
|
||||
|
||||
#if (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) && !defined(__WINDOWS__)
|
||||
|
||||
# define __WINDOWS__
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__CYGWIN__) || defined(__GNU__)
|
||||
|
||||
# include <endian.h>
|
||||
|
||||
// GLIBC-2.8 and earlier does not provide these macros.
|
||||
// See http://linux.die.net/man/3/endian
|
||||
// From https://gist.github.com/panzi/6856583
|
||||
# if defined(__GLIBC__) \
|
||||
&& ( !defined(__GLIBC_MINOR__) \
|
||||
|| ((__GLIBC__ < 2) \
|
||||
|| ((__GLIBC__ == 2) && (__GLIBC_MINOR__ < 9))) )
|
||||
# include <arpa/inet.h>
|
||||
# if defined(__BYTE_ORDER) && (__BYTE_ORDER == __LITTLE_ENDIAN)
|
||||
|
||||
# define htole32(x) (x)
|
||||
# define le32toh(x) (x)
|
||||
|
||||
# elif defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN)
|
||||
|
||||
# define htole16(x) ((((((uint16_t)(x)) >> 8))|((((uint16_t)(x)) << 8)))
|
||||
# define le16toh(x) ((((((uint16_t)(x)) >> 8))|((((uint16_t)(x)) << 8)))
|
||||
|
||||
# define htole32(x) (((uint32_t)htole16(((uint16_t)(((uint32_t)(x)) >> 16)))) | (((uint32_t)htole16(((uint16_t)(x)))) << 16))
|
||||
# define le32toh(x) (((uint32_t)le16toh(((uint16_t)(((uint32_t)(x)) >> 16)))) | (((uint32_t)le16toh(((uint16_t)(x)))) << 16))
|
||||
|
||||
# else
|
||||
# error Byte Order not supported or not defined.
|
||||
# endif
|
||||
# endif
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
# include <libkern/OSByteOrder.h>
|
||||
|
||||
# define htobe16(x) OSSwapHostToBigInt16(x)
|
||||
# define htole16(x) OSSwapHostToLittleInt16(x)
|
||||
# define be16toh(x) OSSwapBigToHostInt16(x)
|
||||
# define le16toh(x) OSSwapLittleToHostInt16(x)
|
||||
|
||||
# define htobe32(x) OSSwapHostToBigInt32(x)
|
||||
# define htole32(x) OSSwapHostToLittleInt32(x)
|
||||
# define be32toh(x) OSSwapBigToHostInt32(x)
|
||||
# define le32toh(x) OSSwapLittleToHostInt32(x)
|
||||
|
||||
# define htobe64(x) OSSwapHostToBigInt64(x)
|
||||
# define htole64(x) OSSwapHostToLittleInt64(x)
|
||||
# define be64toh(x) OSSwapBigToHostInt64(x)
|
||||
# define le64toh(x) OSSwapLittleToHostInt64(x)
|
||||
|
||||
# define __BYTE_ORDER BYTE_ORDER
|
||||
# define __BIG_ENDIAN BIG_ENDIAN
|
||||
# define __LITTLE_ENDIAN LITTLE_ENDIAN
|
||||
# define __PDP_ENDIAN PDP_ENDIAN
|
||||
|
||||
#elif defined(__OpenBSD__)
|
||||
|
||||
# include <sys/endian.h>
|
||||
|
||||
#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
|
||||
# include <sys/endian.h>
|
||||
|
||||
#ifndef be16toh
|
||||
# define be16toh(x) betoh16(x)
|
||||
#endif
|
||||
#ifndef le16toh
|
||||
# define le16toh(x) letoh16(x)
|
||||
#endif
|
||||
|
||||
#ifndef be32toh
|
||||
# define be32toh(x) betoh32(x)
|
||||
#endif
|
||||
#ifndef le32toh
|
||||
# define le32toh(x) letoh32(x)
|
||||
#endif
|
||||
|
||||
#ifndef be64toh
|
||||
# define be64toh(x) betoh64(x)
|
||||
#endif
|
||||
#ifndef le64toh
|
||||
# define le64toh(x) letoh64(x)
|
||||
#endif
|
||||
|
||||
#elif defined(__WINDOWS__)
|
||||
|
||||
# include <winsock2.h>
|
||||
|
||||
# if BYTE_ORDER == LITTLE_ENDIAN
|
||||
|
||||
# define htobe16(x) htons(x)
|
||||
# define htole16(x) (x)
|
||||
# define be16toh(x) ntohs(x)
|
||||
# define le16toh(x) (x)
|
||||
|
||||
# define htobe32(x) htonl(x)
|
||||
# define htole32(x) (x)
|
||||
# define be32toh(x) ntohl(x)
|
||||
# define le32toh(x) (x)
|
||||
|
||||
# define htobe64(x) htonll(x)
|
||||
# define htole64(x) (x)
|
||||
# define be64toh(x) ntohll(x)
|
||||
# define le64toh(x) (x)
|
||||
|
||||
# elif BYTE_ORDER == BIG_ENDIAN
|
||||
|
||||
/* that would be xbox 360 */
|
||||
# define htobe16(x) (x)
|
||||
# define htole16(x) __builtin_bswap16(x)
|
||||
# define be16toh(x) (x)
|
||||
# define le16toh(x) __builtin_bswap16(x)
|
||||
|
||||
# define htobe32(x) (x)
|
||||
# define htole32(x) __builtin_bswap32(x)
|
||||
# define be32toh(x) (x)
|
||||
# define le32toh(x) __builtin_bswap32(x)
|
||||
|
||||
# define htobe64(x) (x)
|
||||
# define htole64(x) __builtin_bswap64(x)
|
||||
# define be64toh(x) (x)
|
||||
# define le64toh(x) __builtin_bswap64(x)
|
||||
|
||||
# else
|
||||
|
||||
# error byte order not supported
|
||||
|
||||
# endif
|
||||
|
||||
# define __BYTE_ORDER BYTE_ORDER
|
||||
# define __BIG_ENDIAN BIG_ENDIAN
|
||||
# define __LITTLE_ENDIAN LITTLE_ENDIAN
|
||||
# define __PDP_ENDIAN PDP_ENDIAN
|
||||
|
||||
#else
|
||||
|
||||
# error Endian: platform not supported
|
||||
|
||||
#endif
|
||||
|
||||
// Hardware <--> Network (big endian) convention
|
||||
inline void HtoNLA(uint32_t* dst, const uint32_t* src, size_t size)
|
||||
{
|
||||
for (size_t i = 0; i < size; ++ i)
|
||||
dst[i] = htonl(src[i]);
|
||||
}
|
||||
|
||||
inline void NtoHLA(uint32_t* dst, const uint32_t* src, size_t size)
|
||||
{
|
||||
for (size_t i = 0; i < size; ++ i)
|
||||
dst[i] = ntohl(src[i]);
|
||||
}
|
||||
|
||||
// Hardware <--> Intel (little endian) convention
|
||||
inline void HtoILA(uint32_t* dst, const uint32_t* src, size_t size)
|
||||
{
|
||||
for (size_t i = 0; i < size; ++ i)
|
||||
dst[i] = htole32(src[i]);
|
||||
}
|
||||
|
||||
inline void ItoHLA(uint32_t* dst, const uint32_t* src, size_t size)
|
||||
{
|
||||
for (size_t i = 0; i < size; ++ i)
|
||||
dst[i] = le32toh(src[i]);
|
||||
}
|
||||
|
||||
// Bit numbering utility.
|
||||
//
|
||||
// This is something that allows you to turn 32-bit integers into bit fields.
|
||||
// Although bitfields are part of C++ language, they are not designed to be
|
||||
// interchanged with 32-bit numbers, and any attempt to doing it (by placing
|
||||
// inside a union, for example) is nonportable (order of bitfields inside
|
||||
// same-covering 32-bit integer number is dependent on the endian), so they are
|
||||
// popularly disregarded as useless. Instead the 32-bit numbers with bits
|
||||
// individually selected is preferred, with usually manual playing around with
|
||||
// & and | operators, as well as << and >>. This tool is designed to simplify
|
||||
// the use of them. This can be used to qualify a range of bits inside a 32-bit
|
||||
// number to be a separate number, you can "wrap" it by placing the integer
|
||||
// value in the range of these bits, as well as "unwrap" (extract) it from
|
||||
// the given place. For your own safety, use one prefix to all constants that
|
||||
// concern bit ranges intended to be inside the same "bit container".
|
||||
//
|
||||
// Usage: typedef Bits<leftmost, rightmost> MASKTYPE; // MASKTYPE is a name of your choice.
|
||||
//
|
||||
// With this defined, you can use the following members:
|
||||
// - MASKTYPE::mask - to get the int32_t value with bimask (used bits set to 1, others to 0)
|
||||
// - MASKTYPE::offset - to get the lowermost bit number, or number of bits to shift
|
||||
// - MASKTYPE::wrap(int value) - to create a bitset where given value is encoded in given bits
|
||||
// - MASKTYPE::unwrap(int bitset) - to extract an integer value from the bitset basing on mask definition
|
||||
// (rightmost defaults to leftmost)
|
||||
// REMEMBER: leftmost > rightmost because bit 0 is the LEAST significant one!
|
||||
|
||||
template <size_t L, size_t R, bool parent_correct = true>
|
||||
struct BitsetMask
|
||||
{
|
||||
static const bool correct = L >= R;
|
||||
static const uint32_t value = (1u << L) | BitsetMask<L-1, R, correct>::value;
|
||||
};
|
||||
|
||||
// This is kind-of functional programming. This describes a special case that is
|
||||
// a "terminal case" in case when decreased L-1 (see above) reached == R.
|
||||
template<size_t R>
|
||||
struct BitsetMask<R, R, true>
|
||||
{
|
||||
static const bool correct = true;
|
||||
static const uint32_t value = 1 << R;
|
||||
};
|
||||
|
||||
// This is a trap for a case that BitsetMask::correct in the master template definition
|
||||
// evaluates to false. This trap causes compile error and prevents from continuing
|
||||
// recursive unwinding in wrong direction (and challenging the compiler's resistiveness
|
||||
// for infinite loops).
|
||||
template <size_t L, size_t R>
|
||||
struct BitsetMask<L, R, false>
|
||||
{
|
||||
};
|
||||
|
||||
template <size_t L, size_t R = L>
|
||||
struct Bits
|
||||
{
|
||||
// DID YOU GET a kind-of error: 'mask' is not a member of 'Bits<3u, 5u, false>'?
|
||||
// See the the above declaration of 'correct'!
|
||||
static const uint32_t mask = BitsetMask<L, R>::value;
|
||||
static const uint32_t offset = R;
|
||||
static const size_t size = L - R + 1;
|
||||
|
||||
// Example: if our bitset mask is 00111100, this checks if given value fits in
|
||||
// 00001111 mask (that is, does not exceed <0, 15>.
|
||||
static bool fit(uint32_t value) { return (BitsetMask<L-R, 0>::value & value) == value; }
|
||||
|
||||
/// 'wrap' gets some given value that should be placed in appropriate bit range and
|
||||
/// returns a whole 32-bit word that has the value already at specified place.
|
||||
/// To create a 32-bit container that contains already all values destined for different
|
||||
/// bit ranges, simply use wrap() for each of them and bind them with | operator.
|
||||
static uint32_t wrap(uint32_t baseval) { return (baseval << offset) & mask; }
|
||||
|
||||
/// Extracts appropriate bit range and returns them as normal integer value.
|
||||
static uint32_t unwrap(uint32_t bitset) { return (bitset & mask) >> offset; }
|
||||
|
||||
template<class T>
|
||||
static T unwrapt(uint32_t bitset) { return static_cast<T>(unwrap(bitset)); }
|
||||
};
|
||||
|
||||
|
||||
//inline int32_t Bit(size_t b) { return 1 << b; }
|
||||
// XXX This would work only with 'constexpr', but this is
|
||||
// available only in C++11. In C++03 this can be only done
|
||||
// using a macro.
|
||||
//
|
||||
// Actually this can be expressed in C++11 using a better technique,
|
||||
// such as user-defined literals:
|
||||
// 2_bit --> 1 >> 2
|
||||
|
||||
#ifdef BIT
|
||||
#undef BIT
|
||||
#endif
|
||||
#define BIT(x) (1 << (x))
|
||||
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// This is something that reminds a structure consisting of fields
|
||||
// of the same type, implemented as an array. It's parametrized
|
||||
// by the type of fields and the type, which's values should be
|
||||
// used for indexing (preferably an enum type). Whatever type is
|
||||
// used for indexing, it is converted to size_t for indexing the
|
||||
// actual array.
|
||||
//
|
||||
// The user should use it as an array: ds[DS_NAME], stating
|
||||
// that DS_NAME is of enum type passed as 3rd parameter.
|
||||
// However trying to do ds[0] would cause a compile error.
|
||||
template <typename FieldType, size_t NoOfFields, typename IndexerType>
|
||||
struct DynamicStruct
|
||||
{
|
||||
FieldType inarray[NoOfFields];
|
||||
|
||||
void clear()
|
||||
{
|
||||
// As a standard library, it can be believed that this call
|
||||
// can be optimized when FieldType is some integer.
|
||||
std::fill(inarray, inarray + NoOfFields, FieldType());
|
||||
}
|
||||
|
||||
FieldType operator[](IndexerType ix) const { return inarray[size_t(ix)]; }
|
||||
FieldType& operator[](IndexerType ix) { return inarray[size_t(ix)]; }
|
||||
|
||||
template<class AnyOther>
|
||||
FieldType operator[](AnyOther ix) const
|
||||
{
|
||||
// If you can see a compile error here ('int' is not a class or struct, or
|
||||
// that there's no definition of 'type' in given type), it means that you
|
||||
// have used invalid data type passed to [] operator. See the definition
|
||||
// of this type as DynamicStruct and see which type is required for indexing.
|
||||
typename AnyOther::type wrong_usage_of_operator_index = AnyOther::type;
|
||||
return inarray[size_t(ix)];
|
||||
}
|
||||
|
||||
template<class AnyOther>
|
||||
FieldType& operator[](AnyOther ix)
|
||||
{
|
||||
// If you can see a compile error here ('int' is not a class or struct, or
|
||||
// that there's no definition of 'type' in given type), it means that you
|
||||
// have used invalid data type passed to [] operator. See the definition
|
||||
// of this type as DynamicStruct and see which type is required for indexing.
|
||||
typename AnyOther::type wrong_usage_of_operator_index = AnyOther::type;
|
||||
return inarray[size_t(ix)];
|
||||
}
|
||||
|
||||
operator FieldType* () { return inarray; }
|
||||
operator const FieldType* () const { return inarray; }
|
||||
|
||||
char* raw() { return (char*)inarray; }
|
||||
};
|
||||
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
inline bool IsSet(int32_t bitset, int32_t flagset)
|
||||
{
|
||||
return (bitset & flagset) == flagset;
|
||||
}
|
||||
|
||||
// Homecooked version of ref_t. It's a copy of std::reference_wrapper
|
||||
// voided of unwanted properties and renamed to ref_t.
|
||||
|
||||
|
||||
#if HAVE_CXX11
|
||||
#include <functional>
|
||||
#endif
|
||||
|
||||
template<typename Type>
|
||||
class ref_t
|
||||
{
|
||||
Type* m_data;
|
||||
|
||||
public:
|
||||
typedef Type type;
|
||||
|
||||
#if HAVE_CXX11
|
||||
explicit ref_t(Type& __indata)
|
||||
: m_data(std::addressof(__indata))
|
||||
{ }
|
||||
#else
|
||||
explicit ref_t(Type& __indata)
|
||||
: m_data((Type*)(&(char&)(__indata)))
|
||||
{ }
|
||||
#endif
|
||||
|
||||
ref_t(const ref_t<Type>& inref)
|
||||
: m_data(inref.m_data)
|
||||
{ }
|
||||
|
||||
#if HAVE_CXX11
|
||||
ref_t(const std::reference_wrapper<Type>& i): m_data(std::addressof(i.get())) {}
|
||||
#endif
|
||||
|
||||
Type& operator*() { return *m_data; }
|
||||
|
||||
Type& get() const
|
||||
{ return *m_data; }
|
||||
|
||||
Type operator->() const
|
||||
{ return *m_data; }
|
||||
};
|
||||
|
||||
// This is required for Printable function if you have a container of pairs,
|
||||
// but this function has a different definition for C++11 and C++03.
|
||||
namespace srt_pair_op
|
||||
{
|
||||
template <class Value1, class Value2>
|
||||
std::ostream& operator<<(std::ostream& s, const std::pair<Value1, Value2>& v)
|
||||
{
|
||||
s << "{" << v.first << " " << v.second << "}";
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_CXX11
|
||||
|
||||
// This alias was created so that 'Ref' (not 'ref') is used everywhere.
|
||||
// Normally the C++11 'ref' fits perfectly here, however in C++03 mode
|
||||
// it would have to be newly created. This would then cause a conflict
|
||||
// between C++03 SRT and C++11 applications as well as between C++ standard
|
||||
// library and SRT when SRT is compiled in C++11 mode (as it happens on
|
||||
// Darwin/clang).
|
||||
template <class In>
|
||||
inline auto Ref(In& i) -> decltype(std::ref(i)) { return std::ref(i); }
|
||||
|
||||
template <class In>
|
||||
inline auto Move(In& i) -> decltype(std::move(i)) { return std::move(i); }
|
||||
|
||||
// Gluing string of any type, wrapper for operator <<
|
||||
|
||||
template <class Stream>
|
||||
inline Stream& Print(Stream& in) { return in;}
|
||||
|
||||
template <class Stream, class Arg1, class... Args>
|
||||
inline Stream& Print(Stream& sout, Arg1&& arg1, Args&&... args)
|
||||
{
|
||||
sout << arg1;
|
||||
return Print(sout, args...);
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
inline std::string Sprint(Args&&... args)
|
||||
{
|
||||
std::ostringstream sout;
|
||||
Print(sout, args...);
|
||||
return sout.str();
|
||||
}
|
||||
|
||||
// We need to use UniquePtr, in the form of C++03 it will be a #define.
|
||||
// Naturally will be used std::move() so that it can later painlessly
|
||||
// switch to C++11.
|
||||
template <class T>
|
||||
using UniquePtr = std::unique_ptr<T>;
|
||||
|
||||
// Some utilities borrowed from tumux, as this is using options
|
||||
// similar way.
|
||||
template <class Container, class Value = typename Container::value_type, typename... Args> inline
|
||||
std::string Printable(const Container& in, Value /*pseudoargument*/, Args&&... args)
|
||||
{
|
||||
using namespace srt_pair_op;
|
||||
std::ostringstream os;
|
||||
Print(os, args...);
|
||||
os << "[ ";
|
||||
for (auto i: in)
|
||||
os << Value(i) << " ";
|
||||
os << "]";
|
||||
return os.str();
|
||||
}
|
||||
|
||||
template <class Container> inline
|
||||
std::string Printable(const Container& in)
|
||||
{
|
||||
using namespace srt_pair_op;
|
||||
using Value = typename Container::value_type;
|
||||
return Printable(in, Value());
|
||||
}
|
||||
|
||||
template<typename Map, typename Key>
|
||||
auto map_get(Map& m, const Key& key, typename Map::mapped_type def = typename Map::mapped_type()) -> typename Map::mapped_type
|
||||
{
|
||||
auto it = m.find(key);
|
||||
return it == m.end() ? def : it->second;
|
||||
}
|
||||
|
||||
template<typename Map, typename Key>
|
||||
auto map_getp(Map& m, const Key& key) -> typename Map::mapped_type*
|
||||
{
|
||||
auto it = m.find(key);
|
||||
return it == m.end() ? nullptr : std::addressof(it->second);
|
||||
}
|
||||
|
||||
template<typename Map, typename Key>
|
||||
auto map_getp(const Map& m, const Key& key) -> typename Map::mapped_type const*
|
||||
{
|
||||
auto it = m.find(key);
|
||||
return it == m.end() ? nullptr : std::addressof(it->second);
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
template <class Type>
|
||||
ref_t<Type> Ref(Type& arg)
|
||||
{
|
||||
return ref_t<Type>(arg);
|
||||
}
|
||||
|
||||
// The unique_ptr requires C++11, and the rvalue-reference feature,
|
||||
// so here we're simulate the behavior using the old std::auto_ptr.
|
||||
|
||||
// This is only to make a "move" call transparent and look ok towards
|
||||
// the C++11 code.
|
||||
template <class T>
|
||||
std::auto_ptr_ref<T> Move(const std::auto_ptr_ref<T>& in) { return in; }
|
||||
|
||||
// We need to provide also some fixes for this type that were not present in auto_ptr,
|
||||
// but they are present in unique_ptr.
|
||||
|
||||
// C++03 doesn't have a templated typedef, but still we need some things
|
||||
// that can only function as a class.
|
||||
template <class T>
|
||||
class UniquePtr: public std::auto_ptr<T>
|
||||
{
|
||||
typedef std::auto_ptr<T> Base;
|
||||
|
||||
public:
|
||||
|
||||
// This is a template - so method names must be declared explicitly
|
||||
typedef typename Base::element_type element_type;
|
||||
using Base::get;
|
||||
using Base::reset;
|
||||
|
||||
// All constructor declarations must be repeated.
|
||||
// "Constructor delegation" is also only C++11 feature.
|
||||
explicit UniquePtr(element_type* __p = 0) throw() : Base(__p) {}
|
||||
UniquePtr(UniquePtr& __a) throw() : Base(__a) { }
|
||||
template<typename _Tp1>
|
||||
UniquePtr(UniquePtr<_Tp1>& __a) throw() : Base(__a) {}
|
||||
|
||||
UniquePtr& operator=(UniquePtr& __a) throw() { return Base::operator=(__a); }
|
||||
template<typename _Tp1>
|
||||
UniquePtr& operator=(UniquePtr<_Tp1>& __a) throw() { return Base::operator=(__a); }
|
||||
|
||||
// Good, now we need to add some parts of the API of unique_ptr.
|
||||
|
||||
bool operator==(const UniquePtr& two) const { return get() == two.get(); }
|
||||
bool operator!=(const UniquePtr& two) const { return get() != two.get(); }
|
||||
|
||||
bool operator==(const element_type* two) const { return get() == two; }
|
||||
bool operator!=(const element_type* two) const { return get() != two; }
|
||||
|
||||
operator bool () { return 0!= get(); }
|
||||
};
|
||||
|
||||
// A primitive one-argument version of Printable
|
||||
template <class Container> inline
|
||||
std::string Printable(const Container& in)
|
||||
{
|
||||
using namespace srt_pair_op;
|
||||
typedef typename Container::value_type Value;
|
||||
std::ostringstream os;
|
||||
os << "[ ";
|
||||
for (typename Container::const_iterator i = in.begin(); i != in.end(); ++i)
|
||||
os << Value(*i) << " ";
|
||||
os << "]";
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
template<typename Map, typename Key>
|
||||
typename Map::mapped_type map_get(Map& m, const Key& key, typename Map::mapped_type def = typename Map::mapped_type())
|
||||
{
|
||||
typename Map::iterator it = m.find(key);
|
||||
return it == m.end() ? def : it->second;
|
||||
}
|
||||
|
||||
template<typename Map, typename Key>
|
||||
typename Map::mapped_type map_get(const Map& m, const Key& key, typename Map::mapped_type def = typename Map::mapped_type())
|
||||
{
|
||||
typename Map::const_iterator it = m.find(key);
|
||||
return it == m.end() ? def : it->second;
|
||||
}
|
||||
|
||||
template<typename Map, typename Key>
|
||||
typename Map::mapped_type* map_getp(Map& m, const Key& key)
|
||||
{
|
||||
typename Map::iterator it = m.find(key);
|
||||
return it == m.end() ? (typename Map::mapped_type*)0 : &(it->second);
|
||||
}
|
||||
|
||||
template<typename Map, typename Key>
|
||||
typename Map::mapped_type const* map_getp(const Map& m, const Key& key)
|
||||
{
|
||||
typename Map::const_iterator it = m.find(key);
|
||||
return it == m.end() ? (typename Map::mapped_type*)0 : &(it->second);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
template <class Signature>
|
||||
struct CallbackHolder
|
||||
{
|
||||
void* opaque;
|
||||
Signature* fn;
|
||||
|
||||
CallbackHolder(): opaque(NULL), fn(NULL) {}
|
||||
|
||||
void set(void* o, Signature* f)
|
||||
{
|
||||
// Test if the pointer is a pointer to function. Don't let
|
||||
// other type of pointers here.
|
||||
#if HAVE_CXX11
|
||||
static_assert(std::is_function<Signature>::value, "CallbackHolder is for functions only!");
|
||||
#else
|
||||
// This is a poor-man's replacement, which should in most compilers
|
||||
// generate a warning, if `Signature` resolves to a value type.
|
||||
// This would make an illegal pointer cast from a value to a function type.
|
||||
// Casting function-to-function, however, should not. Unfortunately
|
||||
// newer compilers disallow that, too (when a signature differs), but
|
||||
// then they should better use the C++11 way, much more reliable and safer.
|
||||
void* (*testfn)(void*) ATR_UNUSED = (void*(*)(void*))f;
|
||||
#endif
|
||||
opaque = o;
|
||||
fn = f;
|
||||
}
|
||||
|
||||
operator bool() { return fn != NULL; }
|
||||
};
|
||||
|
||||
#define CALLBACK_CALL(holder,...) (*holder.fn)(holder.opaque, __VA_ARGS__)
|
||||
|
||||
inline std::string FormatBinaryString(const uint8_t* bytes, size_t size)
|
||||
{
|
||||
if ( size == 0 )
|
||||
return "";
|
||||
|
||||
//char buf[256];
|
||||
using namespace std;
|
||||
|
||||
ostringstream os;
|
||||
|
||||
// I know, it's funny to use sprintf and ostringstream simultaneously,
|
||||
// but " %02X" in iostream is: << " " << hex << uppercase << setw(2) << setfill('0') << VALUE << setw(1)
|
||||
// Too noisy. OTOH ostringstream solves the problem of memory allocation
|
||||
// for a string of unpredictable size.
|
||||
//sprintf(buf, "%02X", int(bytes[0]));
|
||||
|
||||
os.fill('0');
|
||||
os.width(2);
|
||||
os.setf(ios::basefield, ios::hex);
|
||||
os.setf(ios::uppercase);
|
||||
|
||||
//os << buf;
|
||||
os << int(bytes[0]);
|
||||
|
||||
|
||||
for (size_t i = 1; i < size; ++i)
|
||||
{
|
||||
//sprintf(buf, " %02X", int(bytes[i]));
|
||||
//os << buf;
|
||||
os << int(bytes[i]);
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
||||
|
||||
/// This class is useful in every place where
|
||||
/// the time drift should be traced. It's currently in use in every
|
||||
/// solution that implements any kind of TSBPD.
|
||||
template<unsigned MAX_SPAN, int MAX_DRIFT, bool CLEAR_ON_UPDATE = true>
|
||||
class DriftTracer
|
||||
{
|
||||
int64_t m_qDrift;
|
||||
int64_t m_qOverdrift;
|
||||
|
||||
int64_t m_qDriftSum;
|
||||
unsigned m_uDriftSpan;
|
||||
|
||||
public:
|
||||
DriftTracer()
|
||||
: m_qDrift(0)
|
||||
, m_qOverdrift(0)
|
||||
, m_qDriftSum(0)
|
||||
, m_uDriftSpan(0)
|
||||
{}
|
||||
|
||||
bool update(int64_t driftval)
|
||||
{
|
||||
m_qDriftSum += driftval;
|
||||
++m_uDriftSpan;
|
||||
|
||||
if (m_uDriftSpan < MAX_SPAN)
|
||||
return false;
|
||||
|
||||
if (CLEAR_ON_UPDATE)
|
||||
m_qOverdrift = 0;
|
||||
|
||||
// Calculate the median of all drift values.
|
||||
// In most cases, the divisor should be == MAX_SPAN.
|
||||
m_qDrift = m_qDriftSum / m_uDriftSpan;
|
||||
|
||||
// And clear the collection
|
||||
m_qDriftSum = 0;
|
||||
m_uDriftSpan = 0;
|
||||
|
||||
// In case of "overdrift", save the overdriven value in 'm_qOverdrift'.
|
||||
// In clear mode, you should add this value to the time base when update()
|
||||
// returns true. The drift value will be since now measured with the
|
||||
// overdrift assumed to be added to the base.
|
||||
if (std::abs(m_qDrift) > MAX_DRIFT)
|
||||
{
|
||||
m_qOverdrift = m_qDrift < 0 ? -MAX_DRIFT : MAX_DRIFT;
|
||||
m_qDrift -= m_qOverdrift;
|
||||
}
|
||||
|
||||
// printDriftOffset(m_qOverdrift, m_qDrift);
|
||||
|
||||
// Timebase is separate
|
||||
// m_qTimeBase += m_qOverdrift;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// These values can be read at any time, however if you want
|
||||
// to depend on the fact that they have been changed lately,
|
||||
// you have to check the return value from update().
|
||||
//
|
||||
// IMPORTANT: drift() can be called at any time, just remember
|
||||
// that this value may look different than before only if the
|
||||
// last update() returned true, which need not be important for you.
|
||||
//
|
||||
// CASE: CLEAR_ON_UPDATE = true
|
||||
// overdrift() should be read only immediately after update() returned
|
||||
// true. It will stay available with this value until the next time when
|
||||
// update() returns true, in which case the value will be cleared.
|
||||
// Therefore, after calling update() if it retuns true, you should read
|
||||
// overdrift() immediately an make some use of it. Next valid overdrift
|
||||
// will be then relative to every previous overdrift.
|
||||
//
|
||||
// CASE: CLEAR_ON_UPDATE = false
|
||||
// overdrift() will start from 0, but it will always keep track on
|
||||
// any changes in overdrift. By manipulating the MAX_DRIFT parameter
|
||||
// you can decide how high the drift can go relatively to stay below
|
||||
// overdrift.
|
||||
int64_t drift() const { return m_qDrift; }
|
||||
int64_t overdrift() const { return m_qOverdrift; }
|
||||
};
|
||||
|
||||
template <class KeyType, class ValueType>
|
||||
struct MapProxy
|
||||
{
|
||||
std::map<KeyType, ValueType>& mp;
|
||||
const KeyType& key;
|
||||
|
||||
MapProxy(std::map<KeyType, ValueType>& m, const KeyType& k): mp(m), key(k) {}
|
||||
|
||||
void operator=(const ValueType& val)
|
||||
{
|
||||
mp[key] = val;
|
||||
}
|
||||
|
||||
typename std::map<KeyType, ValueType>::iterator find()
|
||||
{
|
||||
return mp.find(key);
|
||||
}
|
||||
|
||||
typename std::map<KeyType, ValueType>::const_iterator find() const
|
||||
{
|
||||
return mp.find(key);
|
||||
}
|
||||
|
||||
operator ValueType() const
|
||||
{
|
||||
typename std::map<KeyType, ValueType>::const_iterator p = find();
|
||||
if (p == mp.end())
|
||||
return "";
|
||||
return p->second;
|
||||
}
|
||||
|
||||
ValueType deflt(const ValueType& defval) const
|
||||
{
|
||||
typename std::map<KeyType, ValueType>::const_iterator p = find();
|
||||
if (p == mp.end())
|
||||
return defval;
|
||||
return p->second;
|
||||
}
|
||||
|
||||
bool exists() const
|
||||
{
|
||||
return find() != mp.end();
|
||||
}
|
||||
};
|
||||
|
||||
inline std::string BufferStamp(const char* mem, size_t size)
|
||||
{
|
||||
using namespace std;
|
||||
char spread[16];
|
||||
|
||||
int n = 16-size;
|
||||
if (n > 0)
|
||||
memset(spread+16-n, 0, n);
|
||||
memcpy(spread, mem, min(size_t(16), size));
|
||||
|
||||
// Now prepare 4 cells for uint32_t.
|
||||
union
|
||||
{
|
||||
uint32_t sum;
|
||||
char cells[4];
|
||||
};
|
||||
memset(cells, 0, 4);
|
||||
|
||||
for (size_t x = 0; x < 4; ++x)
|
||||
for (size_t y = 0; y < 4; ++y)
|
||||
{
|
||||
cells[x] += spread[x+4*y];
|
||||
}
|
||||
|
||||
// Convert to hex string
|
||||
|
||||
ostringstream os;
|
||||
|
||||
os << hex << uppercase << setfill('0') << setw(8) << sum;
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
template <class OutputIterator>
|
||||
inline void Split(const std::string & str, char delimiter, OutputIterator tokens)
|
||||
{
|
||||
if ( str.empty() )
|
||||
return; // May cause crash and won't extract anything anyway
|
||||
|
||||
std::size_t start;
|
||||
std::size_t end = -1;
|
||||
|
||||
do
|
||||
{
|
||||
start = end + 1;
|
||||
end = str.find(delimiter, start);
|
||||
*tokens = str.substr(
|
||||
start,
|
||||
(end == std::string::npos) ? std::string::npos : end - start);
|
||||
++tokens;
|
||||
} while (end != std::string::npos);
|
||||
}
|
||||
|
||||
inline std::string SelectNot(const std::string& unwanted, const std::string& s1, const std::string& s2)
|
||||
{
|
||||
if (s1 == unwanted)
|
||||
return s2; // might be unwanted, too, but then, there's nothing you can do anyway
|
||||
if (s2 == unwanted)
|
||||
return s1;
|
||||
|
||||
// Both have wanted values, so now compare if they are same
|
||||
if (s1 == s2)
|
||||
return s1; // occasionally there's a winner
|
||||
|
||||
// Irresolvable situation.
|
||||
return std::string();
|
||||
}
|
||||
|
||||
inline std::string SelectDefault(const std::string& checked, const std::string& def)
|
||||
{
|
||||
if (checked == "")
|
||||
return def;
|
||||
return checked;
|
||||
}
|
||||
|
||||
template <class It>
|
||||
inline size_t safe_advance(It& it, size_t num, It end)
|
||||
{
|
||||
while ( it != end && num )
|
||||
{
|
||||
--num;
|
||||
++it;
|
||||
}
|
||||
|
||||
return num; // will be effectively 0, if reached the required point, or >0, if end was by that number earlier
|
||||
}
|
||||
|
||||
// This is available only in C++17, dunno why not C++11 as it's pretty useful.
|
||||
template <class V, size_t N> inline
|
||||
ATR_CONSTEXPR size_t Size(const V (&)[N]) ATR_NOEXCEPT { return N; }
|
||||
|
||||
template <size_t DEPRLEN, typename ValueType>
|
||||
inline ValueType avg_iir(ValueType old_value, ValueType new_value)
|
||||
{
|
||||
return (old_value*(DEPRLEN-1) + new_value)/DEPRLEN;
|
||||
}
|
||||
|
||||
#endif
|
34
trunk/3rdparty/srt-1-fit/srtcore/version.h.in
vendored
Normal file
34
trunk/3rdparty/srt-1-fit/srtcore/version.h.in
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef INC__SRT_VERSION_H
|
||||
#define INC__SRT_VERSION_H
|
||||
|
||||
// To construct version value
|
||||
#define SRT_MAKE_VERSION(major, minor, patch) \
|
||||
((patch) + ((minor)*0x100) + ((major)*0x10000))
|
||||
#define SRT_MAKE_VERSION_VALUE SRT_MAKE_VERSION
|
||||
|
||||
#define SRT_VERSION_MAJOR @SRT_VERSION_MAJOR@
|
||||
#define SRT_VERSION_MINOR @SRT_VERSION_MINOR@
|
||||
#define SRT_VERSION_PATCH @SRT_VERSION_PATCH@
|
||||
#cmakedefine SRT_VERSION_BUILD @APPVEYOR_BUILD_NUMBER_STRING@
|
||||
|
||||
#define SRT_VERSION_STRING "@SRT_VERSION@"
|
||||
#define SRT_VERSION_VALUE \
|
||||
SRT_MAKE_VERSION_VALUE( \
|
||||
SRT_VERSION_MAJOR, SRT_VERSION_MINOR, SRT_VERSION_PATCH )
|
||||
|
||||
#endif // INC__SRT_VERSION_H
|
250
trunk/3rdparty/srt-1-fit/srtcore/window.cpp
vendored
Normal file
250
trunk/3rdparty/srt-1-fit/srtcore/window.cpp
vendored
Normal file
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Yunhong Gu, last updated 01/22/2011
|
||||
modified by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include "common.h"
|
||||
#include "window.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace ACKWindowTools
|
||||
{
|
||||
|
||||
void store(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t ack)
|
||||
{
|
||||
r_aSeq[r_iHead].iACKSeqNo = seq;
|
||||
r_aSeq[r_iHead].iACK = ack;
|
||||
r_aSeq[r_iHead].TimeStamp = CTimer::getTime();
|
||||
|
||||
r_iHead = (r_iHead + 1) % size;
|
||||
|
||||
// overwrite the oldest ACK since it is not likely to be acknowledged
|
||||
if (r_iHead == r_iTail)
|
||||
r_iTail = (r_iTail + 1) % size;
|
||||
}
|
||||
|
||||
int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t& r_ack)
|
||||
{
|
||||
if (r_iHead >= r_iTail)
|
||||
{
|
||||
// Head has not exceeded the physical boundary of the window
|
||||
|
||||
for (int i = r_iTail, n = r_iHead; i < n; ++ i)
|
||||
{
|
||||
// looking for indentical ACK Seq. No.
|
||||
if (seq == r_aSeq[i].iACKSeqNo)
|
||||
{
|
||||
// return the Data ACK it carried
|
||||
r_ack = r_aSeq[i].iACK;
|
||||
|
||||
// calculate RTT
|
||||
int rtt = int(CTimer::getTime() - r_aSeq[i].TimeStamp);
|
||||
|
||||
if (i + 1 == r_iHead)
|
||||
{
|
||||
r_iTail = r_iHead = 0;
|
||||
r_aSeq[0].iACKSeqNo = -1;
|
||||
}
|
||||
else
|
||||
r_iTail = (i + 1) % size;
|
||||
|
||||
return rtt;
|
||||
}
|
||||
}
|
||||
|
||||
// Bad input, the ACK node has been overwritten
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Head has exceeded the physical window boundary, so it is behind tail
|
||||
for (int j = r_iTail, n = r_iHead + size; j < n; ++ j)
|
||||
{
|
||||
// looking for indentical ACK seq. no.
|
||||
if (seq == r_aSeq[j % size].iACKSeqNo)
|
||||
{
|
||||
// return Data ACK
|
||||
j %= size;
|
||||
r_ack = r_aSeq[j].iACK;
|
||||
|
||||
// calculate RTT
|
||||
int rtt = int(CTimer::getTime() - r_aSeq[j].TimeStamp);
|
||||
|
||||
if (j == r_iHead)
|
||||
{
|
||||
r_iTail = r_iHead = 0;
|
||||
r_aSeq[0].iACKSeqNo = -1;
|
||||
}
|
||||
else
|
||||
r_iTail = (j + 1) % size;
|
||||
|
||||
return rtt;
|
||||
}
|
||||
}
|
||||
|
||||
// bad input, the ACK node has been overwritten
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void CPktTimeWindowTools::initializeWindowArrays(int* r_pktWindow, int* r_probeWindow, int* r_bytesWindow, size_t asize, size_t psize)
|
||||
{
|
||||
for (size_t i = 0; i < asize; ++ i)
|
||||
r_pktWindow[i] = 1000000; //1 sec -> 1 pkt/sec
|
||||
|
||||
for (size_t k = 0; k < psize; ++ k)
|
||||
r_probeWindow[k] = 1000; //1 msec -> 1000 pkts/sec
|
||||
|
||||
for (size_t i = 0; i < asize; ++ i)
|
||||
r_bytesWindow[i] = CPacket::SRT_MAX_PAYLOAD_SIZE; //based on 1 pkt/sec set in r_pktWindow[i]
|
||||
}
|
||||
|
||||
|
||||
int CPktTimeWindowTools::getPktRcvSpeed_in(const int* window, int* replica, const int* abytes, size_t asize, int& bytesps)
|
||||
{
|
||||
// get median value, but cannot change the original value order in the window
|
||||
std::copy(window, window + asize, replica);
|
||||
std::nth_element(replica, replica + (asize / 2), replica + asize);
|
||||
//std::sort(replica, replica + asize);
|
||||
int median = replica[asize / 2];
|
||||
|
||||
unsigned count = 0;
|
||||
int sum = 0;
|
||||
int upper = median << 3;
|
||||
int lower = median >> 3;
|
||||
|
||||
bytesps = 0;
|
||||
unsigned long bytes = 0;
|
||||
const int* bp = abytes;
|
||||
// median filtering
|
||||
const int* p = window;
|
||||
for (int i = 0, n = asize; i < n; ++ i)
|
||||
{
|
||||
if ((*p < upper) && (*p > lower))
|
||||
{
|
||||
++ count; //packet counter
|
||||
sum += *p; //usec counter
|
||||
bytes += (unsigned long)*bp; //byte counter
|
||||
}
|
||||
++ p; //advance packet pointer
|
||||
++ bp; //advance bytes pointer
|
||||
}
|
||||
|
||||
// claculate speed, or return 0 if not enough valid value
|
||||
if (count > (asize >> 1))
|
||||
{
|
||||
bytes += (CPacket::SRT_DATA_HDR_SIZE * count); //Add protocol headers to bytes received
|
||||
bytesps = (unsigned long)ceil(1000000.0 / (double(sum) / double(bytes)));
|
||||
return (int)ceil(1000000.0 / (sum / count));
|
||||
}
|
||||
else
|
||||
{
|
||||
bytesps = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int CPktTimeWindowTools::getBandwidth_in(const int* window, int* replica, size_t psize)
|
||||
{
|
||||
// This calculation does more-less the following:
|
||||
//
|
||||
// 1. Having example window:
|
||||
// - 50, 51, 100, 55, 80, 1000, 600, 1500, 1200, 10, 90
|
||||
// 2. This window is now sorted, but we only know the value in the middle:
|
||||
// - 10, 50, 51, 55, 80, [[90]], 100, 600, 1000, 1200, 1500
|
||||
// 3. Now calculate:
|
||||
// - lower: 90/8 = 11.25
|
||||
// - upper: 90*8 = 720
|
||||
// 4. Now calculate the arithmetic median from all these values,
|
||||
// but drop those from outside the <lower, upper> range:
|
||||
// - 10, (11<) [ 50, 51, 55, 80, 90, 100, 600, ] (>720) 1000, 1200, 1500
|
||||
// 5. Calculate the median from the extracted range,
|
||||
// NOTE: the median is actually repeated once, so size is +1.
|
||||
//
|
||||
// values = { 50, 51, 55, 80, 90, 100, 600 };
|
||||
// sum = 90 + accumulate(values); ==> 1026
|
||||
// median = sum/(1 + values.size()); ==> 147
|
||||
//
|
||||
// For comparison: the overall arithmetic median from this window == 430
|
||||
//
|
||||
// 6. Returned value = 1M/median
|
||||
|
||||
// get median value, but cannot change the original value order in the window
|
||||
std::copy(window, window + psize - 1, replica);
|
||||
std::nth_element(replica, replica + (psize / 2), replica + psize - 1);
|
||||
//std::sort(replica, replica + psize); <--- was used for debug, just leave it as a mark
|
||||
int median = replica[psize / 2];
|
||||
|
||||
int count = 1;
|
||||
int sum = median;
|
||||
int upper = median << 3; // median*8
|
||||
int lower = median >> 3; // median/8
|
||||
|
||||
// median filtering
|
||||
const int* p = window;
|
||||
for (int i = 0, n = psize; i < n; ++ i)
|
||||
{
|
||||
if ((*p < upper) && (*p > lower))
|
||||
{
|
||||
++ count;
|
||||
sum += *p;
|
||||
}
|
||||
++ p;
|
||||
}
|
||||
|
||||
return (int)ceil(1000000.0 / (double(sum) / double(count)));
|
||||
}
|
||||
|
||||
|
352
trunk/3rdparty/srt-1-fit/srtcore/window.h
vendored
Normal file
352
trunk/3rdparty/srt-1-fit/srtcore/window.h
vendored
Normal file
|
@ -0,0 +1,352 @@
|
|||
/*
|
||||
* SRT - Secure, Reliable, Transport
|
||||
* Copyright (c) 2018 Haivision Systems Inc.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the
|
||||
above copyright notice, this list of conditions
|
||||
and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the University of Illinois
|
||||
nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
written by
|
||||
Yunhong Gu, last updated 01/22/2011
|
||||
modified by
|
||||
Haivision Systems Inc.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __UDT_WINDOW_H__
|
||||
#define __UDT_WINDOW_H__
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#endif
|
||||
#include "udt.h"
|
||||
#include "packet.h"
|
||||
|
||||
namespace ACKWindowTools
|
||||
{
|
||||
struct Seq
|
||||
{
|
||||
int32_t iACKSeqNo; // Seq. No. for the ACK packet
|
||||
int32_t iACK; // Data Seq. No. carried by the ACK packet
|
||||
uint64_t TimeStamp; // The timestamp when the ACK was sent
|
||||
};
|
||||
|
||||
void store(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t ack);
|
||||
int acknowledge(Seq* r_aSeq, const size_t size, int& r_iHead, int& r_iTail, int32_t seq, int32_t& r_ack);
|
||||
}
|
||||
|
||||
template <size_t SIZE>
|
||||
class CACKWindow
|
||||
{
|
||||
public:
|
||||
CACKWindow() :
|
||||
m_aSeq(),
|
||||
m_iHead(0),
|
||||
m_iTail(0)
|
||||
{
|
||||
m_aSeq[0].iACKSeqNo = -1;
|
||||
}
|
||||
|
||||
~CACKWindow() {}
|
||||
|
||||
/// Write an ACK record into the window.
|
||||
/// @param [in] seq ACK seq. no.
|
||||
/// @param [in] ack DATA ACK no.
|
||||
|
||||
void store(int32_t seq, int32_t ack)
|
||||
{
|
||||
return ACKWindowTools::store(m_aSeq, SIZE, m_iHead, m_iTail, seq, ack);
|
||||
}
|
||||
|
||||
/// Search the ACK-2 "seq" in the window, find out the DATA "ack" and caluclate RTT .
|
||||
/// @param [in] seq ACK-2 seq. no.
|
||||
/// @param [out] ack the DATA ACK no. that matches the ACK-2 no.
|
||||
/// @return RTT.
|
||||
|
||||
int acknowledge(int32_t seq, int32_t& r_ack)
|
||||
{
|
||||
return ACKWindowTools::acknowledge(m_aSeq, SIZE, m_iHead, m_iTail, seq, r_ack);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
typedef ACKWindowTools::Seq Seq;
|
||||
|
||||
Seq m_aSeq[SIZE];
|
||||
int m_iHead; // Pointer to the lastest ACK record
|
||||
int m_iTail; // Pointer to the oldest ACK record
|
||||
|
||||
private:
|
||||
CACKWindow(const CACKWindow&);
|
||||
CACKWindow& operator=(const CACKWindow&);
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CPktTimeWindowTools
|
||||
{
|
||||
public:
|
||||
static int getPktRcvSpeed_in(const int* window, int* replica, const int* bytes, size_t asize, int& bytesps);
|
||||
static int getBandwidth_in(const int* window, int* replica, size_t psize);
|
||||
|
||||
static void initializeWindowArrays(int* r_pktWindow, int* r_probeWindow, int* r_bytesWindow, size_t asize, size_t psize);
|
||||
};
|
||||
|
||||
template <size_t ASIZE = 16, size_t PSIZE = 16>
|
||||
class CPktTimeWindow: CPktTimeWindowTools
|
||||
{
|
||||
public:
|
||||
CPktTimeWindow():
|
||||
m_aPktWindow(),
|
||||
m_aBytesWindow(),
|
||||
m_iPktWindowPtr(0),
|
||||
m_aProbeWindow(),
|
||||
m_iProbeWindowPtr(0),
|
||||
m_iLastSentTime(0),
|
||||
m_iMinPktSndInt(1000000),
|
||||
m_LastArrTime(),
|
||||
m_CurrArrTime(),
|
||||
m_ProbeTime(),
|
||||
m_Probe1Sequence(-1)
|
||||
{
|
||||
pthread_mutex_init(&m_lockPktWindow, NULL);
|
||||
pthread_mutex_init(&m_lockProbeWindow, NULL);
|
||||
m_LastArrTime = CTimer::getTime();
|
||||
CPktTimeWindowTools::initializeWindowArrays(m_aPktWindow, m_aProbeWindow, m_aBytesWindow, ASIZE, PSIZE);
|
||||
}
|
||||
|
||||
~CPktTimeWindow()
|
||||
{
|
||||
pthread_mutex_destroy(&m_lockPktWindow);
|
||||
pthread_mutex_destroy(&m_lockProbeWindow);
|
||||
}
|
||||
|
||||
|
||||
/// read the minimum packet sending interval.
|
||||
/// @return minimum packet sending interval (microseconds).
|
||||
|
||||
int getMinPktSndInt() const { return m_iMinPktSndInt; }
|
||||
|
||||
/// Calculate the packets arrival speed.
|
||||
/// @return Packet arrival speed (packets per second).
|
||||
|
||||
int getPktRcvSpeed(ref_t<int> bytesps) const
|
||||
{
|
||||
// Lock access to the packet Window
|
||||
CGuard cg(m_lockPktWindow);
|
||||
|
||||
int pktReplica[ASIZE]; // packet information window (inter-packet time)
|
||||
return getPktRcvSpeed_in(m_aPktWindow, pktReplica, m_aBytesWindow, ASIZE, *bytesps);
|
||||
}
|
||||
|
||||
int getPktRcvSpeed() const
|
||||
{
|
||||
int bytesps;
|
||||
return getPktRcvSpeed(Ref(bytesps));
|
||||
}
|
||||
|
||||
/// Estimate the bandwidth.
|
||||
/// @return Estimated bandwidth (packets per second).
|
||||
|
||||
int getBandwidth() const
|
||||
{
|
||||
// Lock access to the packet Window
|
||||
CGuard cg(m_lockProbeWindow);
|
||||
|
||||
int probeReplica[PSIZE];
|
||||
return getBandwidth_in(m_aProbeWindow, probeReplica, PSIZE);
|
||||
}
|
||||
|
||||
/// Record time information of a packet sending.
|
||||
/// @param currtime timestamp of the packet sending.
|
||||
|
||||
void onPktSent(int currtime)
|
||||
{
|
||||
int interval = currtime - m_iLastSentTime;
|
||||
|
||||
if ((interval < m_iMinPktSndInt) && (interval > 0))
|
||||
m_iMinPktSndInt = interval;
|
||||
|
||||
m_iLastSentTime = currtime;
|
||||
}
|
||||
|
||||
/// Record time information of an arrived packet.
|
||||
|
||||
void onPktArrival(int pktsz = 0)
|
||||
{
|
||||
CGuard cg(m_lockPktWindow);
|
||||
|
||||
m_CurrArrTime = CTimer::getTime();
|
||||
|
||||
// record the packet interval between the current and the last one
|
||||
m_aPktWindow[m_iPktWindowPtr] = int(m_CurrArrTime - m_LastArrTime);
|
||||
m_aBytesWindow[m_iPktWindowPtr] = pktsz;
|
||||
|
||||
// the window is logically circular
|
||||
++ m_iPktWindowPtr;
|
||||
if (m_iPktWindowPtr == ASIZE)
|
||||
m_iPktWindowPtr = 0;
|
||||
|
||||
// remember last packet arrival time
|
||||
m_LastArrTime = m_CurrArrTime;
|
||||
}
|
||||
|
||||
/// Shortcut to test a packet for possible probe 1 or 2
|
||||
void probeArrival(const CPacket& pkt, bool unordered)
|
||||
{
|
||||
const int inorder16 = pkt.m_iSeqNo & PUMASK_SEQNO_PROBE;
|
||||
|
||||
// for probe1, we want 16th packet
|
||||
if (inorder16 == 0)
|
||||
{
|
||||
probe1Arrival(pkt, unordered);
|
||||
}
|
||||
|
||||
if (unordered)
|
||||
return;
|
||||
|
||||
// for probe2, we want 17th packet
|
||||
if (inorder16 == 1)
|
||||
{
|
||||
probe2Arrival(pkt);
|
||||
}
|
||||
}
|
||||
|
||||
/// Record the arrival time of the first probing packet.
|
||||
void probe1Arrival(const CPacket& pkt, bool unordered)
|
||||
{
|
||||
if (unordered && pkt.m_iSeqNo == m_Probe1Sequence)
|
||||
{
|
||||
// Reset the starting probe into "undefined", when
|
||||
// a packet has come as retransmitted before the
|
||||
// measurement at arrival of 17th could be taken.
|
||||
m_Probe1Sequence = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
m_ProbeTime = CTimer::getTime();
|
||||
m_Probe1Sequence = pkt.m_iSeqNo; // Record the sequence where 16th packet probe was taken
|
||||
}
|
||||
|
||||
/// Record the arrival time of the second probing packet and the interval between packet pairs.
|
||||
|
||||
void probe2Arrival(const CPacket& pkt)
|
||||
{
|
||||
// Reject probes that don't refer to the very next packet
|
||||
// towards the one that was lately notified by probe1Arrival.
|
||||
// Otherwise the result can be stupid.
|
||||
|
||||
// Simply, in case when this wasn't called exactly for the
|
||||
// expected packet pair, behave as if the 17th packet was lost.
|
||||
|
||||
// no start point yet (or was reset) OR not very next packet
|
||||
if (m_Probe1Sequence == -1 || CSeqNo::incseq(m_Probe1Sequence) != pkt.m_iSeqNo)
|
||||
return;
|
||||
|
||||
// Grab the current time before trying to acquire
|
||||
// a mutex. This might add extra delay and therefore
|
||||
// screw up the measurement.
|
||||
const uint64_t now = CTimer::getTime();
|
||||
|
||||
// Lock access to the packet Window
|
||||
CGuard cg(m_lockProbeWindow);
|
||||
|
||||
m_CurrArrTime = now;
|
||||
|
||||
// Reset the starting probe to prevent checking if the
|
||||
// measurement was already taken.
|
||||
m_Probe1Sequence = -1;
|
||||
|
||||
// record the probing packets interval
|
||||
// Adjust the time for what a complete packet would have take
|
||||
const int64_t timediff = m_CurrArrTime - m_ProbeTime;
|
||||
const int64_t timediff_times_pl_size = timediff * CPacket::SRT_MAX_PAYLOAD_SIZE;
|
||||
|
||||
// Let's take it simpler than it is coded here:
|
||||
// (stating that a packet has never zero size)
|
||||
//
|
||||
// probe_case = (now - previous_packet_time) * SRT_MAX_PAYLOAD_SIZE / pktsz;
|
||||
//
|
||||
// Meaning: if the packet is fully packed, probe_case = timediff.
|
||||
// Otherwise the timediff will be "converted" to a time that a fully packed packet "would take",
|
||||
// provided the arrival time is proportional to the payload size and skipping
|
||||
// the ETH+IP+UDP+SRT header part elliminates the constant packet delivery time influence.
|
||||
//
|
||||
const size_t pktsz = pkt.getLength();
|
||||
m_aProbeWindow[m_iProbeWindowPtr] = pktsz ? timediff_times_pl_size / pktsz : int(timediff);
|
||||
|
||||
// OLD CODE BEFORE BSTATS:
|
||||
// record the probing packets interval
|
||||
// m_aProbeWindow[m_iProbeWindowPtr] = int(m_CurrArrTime - m_ProbeTime);
|
||||
|
||||
// the window is logically circular
|
||||
++ m_iProbeWindowPtr;
|
||||
if (m_iProbeWindowPtr == PSIZE)
|
||||
m_iProbeWindowPtr = 0;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
int m_aPktWindow[ASIZE]; // packet information window (inter-packet time)
|
||||
int m_aBytesWindow[ASIZE]; //
|
||||
int m_iPktWindowPtr; // position pointer of the packet info. window.
|
||||
mutable pthread_mutex_t m_lockPktWindow; // used to synchronize access to the packet window
|
||||
|
||||
int m_aProbeWindow[PSIZE]; // record inter-packet time for probing packet pairs
|
||||
int m_iProbeWindowPtr; // position pointer to the probing window
|
||||
mutable pthread_mutex_t m_lockProbeWindow; // used to synchronize access to the probe window
|
||||
|
||||
int m_iLastSentTime; // last packet sending time
|
||||
int m_iMinPktSndInt; // Minimum packet sending interval
|
||||
|
||||
uint64_t m_LastArrTime; // last packet arrival time
|
||||
uint64_t m_CurrArrTime; // current packet arrival time
|
||||
uint64_t m_ProbeTime; // arrival time of the first probing packet
|
||||
int32_t m_Probe1Sequence; // sequence number for which the arrival time was notified
|
||||
|
||||
private:
|
||||
CPktTimeWindow(const CPktTimeWindow&);
|
||||
CPktTimeWindow &operator=(const CPktTimeWindow&);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue