mirror of
				https://github.com/ossrs/srs.git
				synced 2025-03-09 15:49:59 +00:00 
			
		
		
		
	fix https://github.com/ossrs/srs/issues/3155 Build srt-1-fit fails with `standard attributes in middle of decl-specifiers` on GCC 12,Arch Linux. See https://github.com/Haivision/srt/releases/tag/v1.5.3
		
			
				
	
	
		
			730 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			730 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * 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 03/12/2011
 | |
| modified by
 | |
|    Haivision Systems Inc.
 | |
| *****************************************************************************/
 | |
| 
 | |
| #include "platform_sys.h"
 | |
| 
 | |
| #include <cmath>
 | |
| #include "buffer_snd.h"
 | |
| #include "packet.h"
 | |
| #include "core.h" // provides some constants
 | |
| #include "logging.h"
 | |
| 
 | |
| namespace srt {
 | |
| 
 | |
| using namespace std;
 | |
| using namespace srt_logging;
 | |
| using namespace sync;
 | |
| 
 | |
| CSndBuffer::CSndBuffer(int size, int maxpld, int authtag)
 | |
|     : m_BufLock()
 | |
|     , m_pBlock(NULL)
 | |
|     , m_pFirstBlock(NULL)
 | |
|     , m_pCurrBlock(NULL)
 | |
|     , m_pLastBlock(NULL)
 | |
|     , m_pBuffer(NULL)
 | |
|     , m_iNextMsgNo(1)
 | |
|     , m_iSize(size)
 | |
|     , m_iBlockLen(maxpld)
 | |
|     , m_iAuthTagSize(authtag)
 | |
|     , m_iCount(0)
 | |
|     , m_iBytesCount(0)
 | |
| {
 | |
|     // initial physical buffer of "size"
 | |
|     m_pBuffer           = new Buffer;
 | |
|     m_pBuffer->m_pcData = new char[m_iSize * m_iBlockLen];
 | |
|     m_pBuffer->m_iSize  = m_iSize;
 | |
|     m_pBuffer->m_pNext  = NULL;
 | |
| 
 | |
|     // circular linked list for out bound packets
 | |
|     m_pBlock  = new Block;
 | |
|     Block* pb = m_pBlock;
 | |
|     char* pc  = m_pBuffer->m_pcData;
 | |
| 
 | |
|     for (int i = 0; i < m_iSize; ++i)
 | |
|     {
 | |
|         pb->m_iMsgNoBitset = 0;
 | |
|         pb->m_pcData       = pc;
 | |
|         pc                += m_iBlockLen;
 | |
| 
 | |
|         if (i < m_iSize - 1)
 | |
|         {
 | |
|             pb->m_pNext        = new Block;
 | |
|             pb                 = pb->m_pNext;
 | |
|         }
 | |
|     }
 | |
|     pb->m_pNext = m_pBlock;
 | |
| 
 | |
|     m_pFirstBlock = m_pCurrBlock = m_pLastBlock = m_pBlock;
 | |
| 
 | |
|     setupMutex(m_BufLock, "Buf");
 | |
| }
 | |
| 
 | |
| CSndBuffer::~CSndBuffer()
 | |
| {
 | |
|     Block* pb = m_pBlock->m_pNext;
 | |
|     while (pb != m_pBlock)
 | |
|     {
 | |
|         Block* temp = pb;
 | |
|         pb          = pb->m_pNext;
 | |
|         delete temp;
 | |
|     }
 | |
|     delete m_pBlock;
 | |
| 
 | |
|     while (m_pBuffer != NULL)
 | |
|     {
 | |
|         Buffer* temp = m_pBuffer;
 | |
|         m_pBuffer    = m_pBuffer->m_pNext;
 | |
|         delete[] temp->m_pcData;
 | |
|         delete temp;
 | |
|     }
 | |
| 
 | |
|     releaseMutex(m_BufLock);
 | |
| }
 | |
| 
 | |
| void CSndBuffer::addBuffer(const char* data, int len, SRT_MSGCTRL& w_mctrl)
 | |
| {
 | |
|     int32_t& w_msgno     = w_mctrl.msgno;
 | |
|     int32_t& w_seqno     = w_mctrl.pktseq;
 | |
|     int64_t& w_srctime   = w_mctrl.srctime;
 | |
|     const int& ttl       = w_mctrl.msgttl;
 | |
|     const int iPktLen    = getMaxPacketLen();
 | |
|     const int iNumBlocks = countNumPacketsRequired(len, iPktLen);
 | |
| 
 | |
|     HLOGC(bslog.Debug,
 | |
|           log << "addBuffer: needs=" << iNumBlocks << " buffers for " << len << " bytes. Taken=" << m_iCount << "/" << m_iSize);
 | |
|     // Retrieve current time before locking the mutex to be closer to packet submission event.
 | |
|     const steady_clock::time_point tnow = steady_clock::now();
 | |
| 
 | |
|     ScopedLock bufferguard(m_BufLock);
 | |
|     // Dynamically increase sender buffer if there is not enough room.
 | |
|     while (iNumBlocks + m_iCount >= m_iSize)
 | |
|     {
 | |
|         HLOGC(bslog.Debug, log << "addBuffer: ... still lacking " << (iNumBlocks + m_iCount - m_iSize) << " buffers...");
 | |
|         increase();
 | |
|     }
 | |
| 
 | |
|     const int32_t inorder = w_mctrl.inorder ? MSGNO_PACKET_INORDER::mask : 0;
 | |
|     HLOGC(bslog.Debug,
 | |
|           log << CONID() << "addBuffer: adding " << iNumBlocks << " packets (" << len << " bytes) to send, msgno="
 | |
|               << (w_msgno > 0 ? w_msgno : m_iNextMsgNo) << (inorder ? "" : " NOT") << " in order");
 | |
| 
 | |
|     // Calculate origin time (same for all blocks of the message).
 | |
|     m_tsLastOriginTime = w_srctime ? time_point() + microseconds_from(w_srctime) : tnow;
 | |
|     // Rewrite back the actual value, even if it stays the same, so that the calling facilities can reuse it.
 | |
|     // May also be a subject to conversion error, thus the actual value is signalled back.
 | |
|     w_srctime = count_microseconds(m_tsLastOriginTime.time_since_epoch());
 | |
| 
 | |
|     // The sequence number passed to this function is the sequence number
 | |
|     // that the very first packet from the packet series should get here.
 | |
|     // If there's more than one packet, this function must increase it by itself
 | |
|     // and then return the accordingly modified sequence number in the reference.
 | |
| 
 | |
|     Block* s = m_pLastBlock;
 | |
| 
 | |
|     if (w_msgno == SRT_MSGNO_NONE) // DEFAULT-UNCHANGED msgno supplied
 | |
|     {
 | |
|         HLOGC(bslog.Debug, log << "addBuffer: using internally managed msgno=" << m_iNextMsgNo);
 | |
|         w_msgno = m_iNextMsgNo;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         HLOGC(bslog.Debug, log << "addBuffer: OVERWRITTEN by msgno supplied by caller: msgno=" << w_msgno);
 | |
|         m_iNextMsgNo = w_msgno;
 | |
|     }
 | |
| 
 | |
|     for (int i = 0; i < iNumBlocks; ++i)
 | |
|     {
 | |
|         int pktlen = len - i * iPktLen;
 | |
|         if (pktlen > iPktLen)
 | |
|             pktlen = iPktLen;
 | |
| 
 | |
|         HLOGC(bslog.Debug,
 | |
|               log << "addBuffer: %" << w_seqno << " #" << w_msgno << " offset=" << (i * iPktLen)
 | |
|                   << " size=" << pktlen << " TO BUFFER:" << (void*)s->m_pcData);
 | |
|         memcpy((s->m_pcData), data + i * iPktLen, pktlen);
 | |
|         s->m_iLength = pktlen;
 | |
| 
 | |
|         s->m_iSeqNo = w_seqno;
 | |
|         w_seqno     = CSeqNo::incseq(w_seqno);
 | |
| 
 | |
|         s->m_iMsgNoBitset = m_iNextMsgNo | inorder;
 | |
|         if (i == 0)
 | |
|             s->m_iMsgNoBitset |= PacketBoundaryBits(PB_FIRST);
 | |
|         if (i == iNumBlocks - 1)
 | |
|             s->m_iMsgNoBitset |= PacketBoundaryBits(PB_LAST);
 | |
|         // NOTE: if i is neither 0 nor size-1, it resuls with PB_SUBSEQUENT.
 | |
|         //       if i == 0 == size-1, it results with PB_SOLO.
 | |
|         // Packets assigned to one message can be:
 | |
|         // [PB_FIRST] [PB_SUBSEQUENT] [PB_SUBSEQUENT] [PB_LAST] - 4 packets per message
 | |
|         // [PB_FIRST] [PB_LAST] - 2 packets per message
 | |
|         // [PB_SOLO] - 1 packet per message
 | |
| 
 | |
|         s->m_iTTL = ttl;
 | |
|         s->m_tsRexmitTime = time_point();
 | |
|         s->m_tsOriginTime = m_tsLastOriginTime;
 | |
|         
 | |
|         // Should never happen, as the call to increase() should ensure enough buffers.
 | |
|         SRT_ASSERT(s->m_pNext);
 | |
|         s = s->m_pNext;
 | |
|     }
 | |
|     m_pLastBlock = s;
 | |
| 
 | |
|     m_iCount += iNumBlocks;
 | |
|     m_iBytesCount += len;
 | |
| 
 | |
|     m_rateEstimator.updateInputRate(m_tsLastOriginTime, iNumBlocks, len);
 | |
|     updAvgBufSize(m_tsLastOriginTime);
 | |
| 
 | |
|     // MSGNO_SEQ::mask has a form: 00000011111111...
 | |
|     // At least it's known that it's from some index inside til the end (to bit 0).
 | |
|     // If this value has been reached in a step of incrementation, it means that the
 | |
|     // maximum value has been reached. Casting to int32_t to ensure the same sign
 | |
|     // in comparison, although it's far from reaching the sign bit.
 | |
| 
 | |
|     const int nextmsgno = ++MsgNo(m_iNextMsgNo);
 | |
|     HLOGC(bslog.Debug, log << "CSndBuffer::addBuffer: updating msgno: #" << m_iNextMsgNo << " -> #" << nextmsgno);
 | |
|     m_iNextMsgNo = nextmsgno;
 | |
| }
 | |
| 
 | |
| int CSndBuffer::addBufferFromFile(fstream& ifs, int len)
 | |
| {
 | |
|     const int iPktLen    = getMaxPacketLen();
 | |
|     const int iNumBlocks = countNumPacketsRequired(len, iPktLen);
 | |
| 
 | |
|     HLOGC(bslog.Debug,
 | |
|           log << "addBufferFromFile: size=" << m_iCount << " reserved=" << m_iSize << " needs=" << iPktLen
 | |
|               << " buffers for " << len << " bytes");
 | |
| 
 | |
|     // dynamically increase sender buffer
 | |
|     while (iNumBlocks + m_iCount >= m_iSize)
 | |
|     {
 | |
|         HLOGC(bslog.Debug,
 | |
|               log << "addBufferFromFile: ... still lacking " << (iNumBlocks + m_iCount - m_iSize) << " buffers...");
 | |
|         increase();
 | |
|     }
 | |
| 
 | |
|     HLOGC(bslog.Debug,
 | |
|           log << CONID() << "addBufferFromFile: adding " << iPktLen << " packets (" << len
 | |
|               << " bytes) to send, msgno=" << m_iNextMsgNo);
 | |
| 
 | |
|     Block* s     = m_pLastBlock;
 | |
|     int    total = 0;
 | |
|     for (int i = 0; i < iNumBlocks; ++i)
 | |
|     {
 | |
|         if (ifs.bad() || ifs.fail() || ifs.eof())
 | |
|             break;
 | |
| 
 | |
|         int pktlen = len - i * iPktLen;
 | |
|         if (pktlen > iPktLen)
 | |
|             pktlen = iPktLen;
 | |
| 
 | |
|         HLOGC(bslog.Debug,
 | |
|               log << "addBufferFromFile: reading from=" << (i * iPktLen) << " size=" << pktlen
 | |
|                   << " TO BUFFER:" << (void*)s->m_pcData);
 | |
|         ifs.read(s->m_pcData, pktlen);
 | |
|         if ((pktlen = int(ifs.gcount())) <= 0)
 | |
|             break;
 | |
| 
 | |
|         // currently file transfer is only available in streaming mode, message is always in order, ttl = infinite
 | |
|         s->m_iMsgNoBitset = m_iNextMsgNo | MSGNO_PACKET_INORDER::mask;
 | |
|         if (i == 0)
 | |
|             s->m_iMsgNoBitset |= PacketBoundaryBits(PB_FIRST);
 | |
|         if (i == iNumBlocks - 1)
 | |
|             s->m_iMsgNoBitset |= PacketBoundaryBits(PB_LAST);
 | |
|         // NOTE: PB_FIRST | PB_LAST == PB_SOLO.
 | |
|         // none of PB_FIRST & PB_LAST == PB_SUBSEQUENT.
 | |
| 
 | |
|         s->m_iLength = pktlen;
 | |
|         s->m_iTTL    = SRT_MSGTTL_INF;
 | |
|         s            = s->m_pNext;
 | |
| 
 | |
|         total += pktlen;
 | |
|     }
 | |
|     m_pLastBlock = s;
 | |
| 
 | |
|     enterCS(m_BufLock);
 | |
|     m_iCount += iNumBlocks;
 | |
|     m_iBytesCount += total;
 | |
| 
 | |
|     leaveCS(m_BufLock);
 | |
| 
 | |
|     m_iNextMsgNo++;
 | |
|     if (m_iNextMsgNo == int32_t(MSGNO_SEQ::mask))
 | |
|         m_iNextMsgNo = 1;
 | |
| 
 | |
|     return total;
 | |
| }
 | |
| 
 | |
| int CSndBuffer::readData(CPacket& w_packet, steady_clock::time_point& w_srctime, int kflgs, int& w_seqnoinc)
 | |
| {
 | |
|     int readlen = 0;
 | |
|     w_seqnoinc = 0;
 | |
| 
 | |
|     ScopedLock bufferguard(m_BufLock);
 | |
|     while (m_pCurrBlock != m_pLastBlock)
 | |
|     {
 | |
|         // Make the packet REFLECT the data stored in the buffer.
 | |
|         w_packet.m_pcData = m_pCurrBlock->m_pcData;
 | |
|         readlen = m_pCurrBlock->m_iLength;
 | |
|         w_packet.setLength(readlen, m_iBlockLen);
 | |
|         w_packet.m_iSeqNo = m_pCurrBlock->m_iSeqNo;
 | |
| 
 | |
|         // 1. On submission (addBuffer), the KK flag is set to EK_NOENC (0).
 | |
|         // 2. The readData() is called to get the original (unique) payload not ever sent yet.
 | |
|         //    The payload must be encrypted for the first time if the encryption
 | |
|         //    is enabled (arg kflgs != EK_NOENC). The KK encryption flag of the data packet
 | |
|         //    header must be set and remembered accordingly (see EncryptionKeySpec).
 | |
|         // 3. The next time this packet is read (only for retransmission), the payload is already
 | |
|         //    encrypted, and the proper flag value is already stored.
 | |
|         
 | |
|         // TODO: Alternatively, encryption could happen before the packet is submitted to the buffer
 | |
|         // (before the addBuffer() call), and corresponding flags could be set accordingly.
 | |
|         // This may also put an encryption burden on the application thread, rather than the sending thread,
 | |
|         // which could be more efficient. Note that packet sequence number must be properly set in that case,
 | |
|         // as it is used as a counter for the AES encryption.
 | |
|         if (kflgs == -1)
 | |
|         {
 | |
|             HLOGC(bslog.Debug, log << CONID() << " CSndBuffer: ERROR: encryption required and not possible. NOT SENDING.");
 | |
|             readlen = 0;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             m_pCurrBlock->m_iMsgNoBitset |= MSGNO_ENCKEYSPEC::wrap(kflgs);
 | |
|         }
 | |
| 
 | |
|         Block* p = m_pCurrBlock;
 | |
|         w_packet.m_iMsgNo = m_pCurrBlock->m_iMsgNoBitset;
 | |
|         w_srctime = m_pCurrBlock->m_tsOriginTime;
 | |
|         m_pCurrBlock = m_pCurrBlock->m_pNext;
 | |
| 
 | |
|         if ((p->m_iTTL >= 0) && (count_milliseconds(steady_clock::now() - w_srctime) > p->m_iTTL))
 | |
|         {
 | |
|             LOGC(bslog.Warn, log << CONID() << "CSndBuffer: skipping packet %" << p->m_iSeqNo << " #" << p->getMsgSeq() << " with TTL=" << p->m_iTTL);
 | |
|             // Skip this packet due to TTL expiry.
 | |
|             readlen = 0;
 | |
|             ++w_seqnoinc;
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         HLOGC(bslog.Debug, log << CONID() << "CSndBuffer: extracting packet size=" << readlen << " to send");
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     return readlen;
 | |
| }
 | |
| 
 | |
| CSndBuffer::time_point CSndBuffer::peekNextOriginal() const
 | |
| {
 | |
|     ScopedLock bufferguard(m_BufLock);
 | |
|     if (m_pCurrBlock == m_pLastBlock)
 | |
|         return time_point();
 | |
| 
 | |
|     return m_pCurrBlock->m_tsOriginTime;
 | |
| }
 | |
| 
 | |
| int32_t CSndBuffer::getMsgNoAt(const int offset)
 | |
| {
 | |
|     ScopedLock bufferguard(m_BufLock);
 | |
| 
 | |
|     Block* p = m_pFirstBlock;
 | |
| 
 | |
|     if (p)
 | |
|     {
 | |
|         HLOGC(bslog.Debug,
 | |
|               log << "CSndBuffer::getMsgNoAt: FIRST MSG: size=" << p->m_iLength << " %" << p->m_iSeqNo << " #"
 | |
|                   << p->getMsgSeq() << " !" << BufferStamp(p->m_pcData, p->m_iLength));
 | |
|     }
 | |
| 
 | |
|     if (offset >= m_iCount)
 | |
|     {
 | |
|         // Prevent accessing the last "marker" block
 | |
|         LOGC(bslog.Error,
 | |
|              log << "CSndBuffer::getMsgNoAt: IPE: offset=" << offset << " not found, max offset=" << m_iCount);
 | |
|         return SRT_MSGNO_CONTROL;
 | |
|     }
 | |
| 
 | |
|     // XXX Suboptimal procedure to keep the blocks identifiable
 | |
|     // by sequence number. Consider using some circular buffer.
 | |
|     int       i;
 | |
|     Block* ee SRT_ATR_UNUSED = 0;
 | |
|     for (i = 0; i < offset && p; ++i)
 | |
|     {
 | |
|         ee = p;
 | |
|         p  = p->m_pNext;
 | |
|     }
 | |
| 
 | |
|     if (!p)
 | |
|     {
 | |
|         LOGC(bslog.Error,
 | |
|              log << "CSndBuffer::getMsgNoAt: IPE: offset=" << offset << " not found, stopped at " << i << " with #"
 | |
|                  << (ee ? ee->getMsgSeq() : SRT_MSGNO_NONE));
 | |
|         return SRT_MSGNO_CONTROL;
 | |
|     }
 | |
| 
 | |
|     HLOGC(bslog.Debug,
 | |
|           log << "CSndBuffer::getMsgNoAt: offset=" << offset << " found, size=" << p->m_iLength << " %" << p->m_iSeqNo
 | |
|               << " #" << p->getMsgSeq() << " !" << BufferStamp(p->m_pcData, p->m_iLength));
 | |
| 
 | |
|     return p->getMsgSeq();
 | |
| }
 | |
| 
 | |
| int CSndBuffer::readData(const int offset, CPacket& w_packet, steady_clock::time_point& w_srctime, int& w_msglen)
 | |
| {
 | |
|     int32_t& msgno_bitset = w_packet.m_iMsgNo;
 | |
| 
 | |
|     ScopedLock bufferguard(m_BufLock);
 | |
| 
 | |
|     Block* p = m_pFirstBlock;
 | |
| 
 | |
|     // XXX Suboptimal procedure to keep the blocks identifiable
 | |
|     // by sequence number. Consider using some circular buffer.
 | |
|     for (int i = 0; i < offset && p != m_pLastBlock; ++i)
 | |
|     {
 | |
|         p = p->m_pNext;
 | |
|     }
 | |
|     if (p == m_pLastBlock)
 | |
|     {
 | |
|         LOGC(qslog.Error, log << "CSndBuffer::readData: offset " << offset << " too large!");
 | |
|         return 0;
 | |
|     }
 | |
| #if ENABLE_HEAVY_LOGGING
 | |
|     const int32_t first_seq = p->m_iSeqNo;
 | |
|     int32_t last_seq = p->m_iSeqNo;
 | |
| #endif
 | |
| 
 | |
|     // Check if the block that is the next candidate to send (m_pCurrBlock pointing) is stale.
 | |
| 
 | |
|     // If so, then inform the caller that it should first take care of the whole
 | |
|     // message (all blocks with that message id). Shift the m_pCurrBlock pointer
 | |
|     // to the position past the last of them. Then return -1 and set the
 | |
|     // msgno_bitset return reference to the message id that should be dropped as
 | |
|     // a whole.
 | |
| 
 | |
|     // After taking care of that, the caller should immediately call this function again,
 | |
|     // this time possibly in order to find the real data to be sent.
 | |
| 
 | |
|     // if found block is stale
 | |
|     // (This is for messages that have declared TTL - messages that fail to be sent
 | |
|     // before the TTL defined time comes, will be dropped).
 | |
| 
 | |
|     if ((p->m_iTTL >= 0) && (count_milliseconds(steady_clock::now() - p->m_tsOriginTime) > p->m_iTTL))
 | |
|     {
 | |
|         int32_t msgno = p->getMsgSeq();
 | |
|         w_msglen      = 1;
 | |
|         p             = p->m_pNext;
 | |
|         bool move     = false;
 | |
|         while (p != m_pLastBlock && msgno == p->getMsgSeq())
 | |
|         {
 | |
| #if ENABLE_HEAVY_LOGGING
 | |
|             last_seq = p->m_iSeqNo;
 | |
| #endif
 | |
|             if (p == m_pCurrBlock)
 | |
|                 move = true;
 | |
|             p = p->m_pNext;
 | |
|             if (move)
 | |
|                 m_pCurrBlock = p;
 | |
|             w_msglen++;
 | |
|         }
 | |
| 
 | |
|         HLOGC(qslog.Debug,
 | |
|               log << "CSndBuffer::readData: due to TTL exceeded, SEQ " << first_seq << " - " << last_seq << ", "
 | |
|                   << w_msglen << " packets to drop, msgno=" << msgno);
 | |
| 
 | |
|         // If readData returns -1, then msgno_bitset is understood as a Message ID to drop.
 | |
|         // This means that in this case it should be written by the message sequence value only
 | |
|         // (not the whole 4-byte bitset written at PH_MSGNO).
 | |
|         msgno_bitset = msgno;
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     w_packet.m_pcData = p->m_pcData;
 | |
|     const int readlen = p->m_iLength;
 | |
|     w_packet.setLength(readlen, m_iBlockLen);
 | |
| 
 | |
|     // XXX Here the value predicted to be applied to PH_MSGNO field is extracted.
 | |
|     // As this function is predicted to extract the data to send as a rexmited packet,
 | |
|     // the packet must be in the form ready to send - so, in case of encryption,
 | |
|     // encrypted, and with all ENC flags already set. So, the first call to send
 | |
|     // the packet originally (the other overload of this function) must set these
 | |
|     // flags.
 | |
|     w_packet.m_iMsgNo = p->m_iMsgNoBitset;
 | |
|     w_srctime = p->m_tsOriginTime;
 | |
| 
 | |
|     // This function is called when packet retransmission is triggered.
 | |
|     // Therefore we are setting the rexmit time.
 | |
|     p->m_tsRexmitTime = steady_clock::now();
 | |
| 
 | |
|     HLOGC(qslog.Debug,
 | |
|           log << CONID() << "CSndBuffer: getting packet %" << p->m_iSeqNo << " as per %" << w_packet.m_iSeqNo
 | |
|               << " size=" << readlen << " to send [REXMIT]");
 | |
| 
 | |
|     return readlen;
 | |
| }
 | |
| 
 | |
| sync::steady_clock::time_point CSndBuffer::getPacketRexmitTime(const int offset)
 | |
| {
 | |
|     ScopedLock bufferguard(m_BufLock);
 | |
|     const Block* p = m_pFirstBlock;
 | |
| 
 | |
|     // XXX Suboptimal procedure to keep the blocks identifiable
 | |
|     // by sequence number. Consider using some circular buffer.
 | |
|     for (int i = 0; i < offset; ++i)
 | |
|     {
 | |
|         SRT_ASSERT(p);
 | |
|         p = p->m_pNext;
 | |
|     }
 | |
| 
 | |
|     SRT_ASSERT(p);
 | |
|     return p->m_tsRexmitTime;
 | |
| }
 | |
| 
 | |
| void CSndBuffer::ackData(int offset)
 | |
| {
 | |
|     ScopedLock bufferguard(m_BufLock);
 | |
| 
 | |
|     bool move = false;
 | |
|     for (int i = 0; i < offset; ++i)
 | |
|     {
 | |
|         m_iBytesCount -= m_pFirstBlock->m_iLength;
 | |
|         if (m_pFirstBlock == m_pCurrBlock)
 | |
|             move = true;
 | |
|         m_pFirstBlock = m_pFirstBlock->m_pNext;
 | |
|     }
 | |
|     if (move)
 | |
|         m_pCurrBlock = m_pFirstBlock;
 | |
| 
 | |
|     m_iCount -= offset;
 | |
| 
 | |
|     updAvgBufSize(steady_clock::now());
 | |
| }
 | |
| 
 | |
| int CSndBuffer::getCurrBufSize() const
 | |
| {
 | |
|     return m_iCount;
 | |
| }
 | |
| 
 | |
| int CSndBuffer::getMaxPacketLen() const
 | |
| {
 | |
|     return m_iBlockLen - m_iAuthTagSize;
 | |
| }
 | |
| 
 | |
| int CSndBuffer::countNumPacketsRequired(int iPldLen) const
 | |
| {
 | |
|     const int iPktLen = getMaxPacketLen();
 | |
|     return countNumPacketsRequired(iPldLen, iPktLen);
 | |
| }
 | |
| 
 | |
| int CSndBuffer::countNumPacketsRequired(int iPldLen, int iPktLen) const
 | |
| {
 | |
|     return (iPldLen + iPktLen - 1) / iPktLen;
 | |
| }
 | |
| 
 | |
| namespace {
 | |
| int round_val(double val)
 | |
| {
 | |
|     return static_cast<int>(round(val));
 | |
| }
 | |
| }
 | |
| 
 | |
| int CSndBuffer::getAvgBufSize(int& w_bytes, int& w_tsp)
 | |
| {
 | |
|     ScopedLock bufferguard(m_BufLock); /* Consistency of pkts vs. bytes vs. spantime */
 | |
| 
 | |
|     /* update stats in case there was no add/ack activity lately */
 | |
|     updAvgBufSize(steady_clock::now());
 | |
| 
 | |
|     // Average number of packets and timespan could be small,
 | |
|     // so rounding is beneficial, while for the number of
 | |
|     // bytes in the buffer is a higher value, so rounding can be omitted,
 | |
|     // but probably better to round all three values.
 | |
|     w_bytes = round_val(m_mavg.bytes());
 | |
|     w_tsp   = round_val(m_mavg.timespan_ms());
 | |
|     return round_val(m_mavg.pkts());
 | |
| }
 | |
| 
 | |
| void CSndBuffer::updAvgBufSize(const steady_clock::time_point& now)
 | |
| {
 | |
|     if (!m_mavg.isTimeToUpdate(now))
 | |
|         return;
 | |
| 
 | |
|     int       bytes       = 0;
 | |
|     int       timespan_ms = 0;
 | |
|     const int pkts        = getCurrBufSize((bytes), (timespan_ms));
 | |
|     m_mavg.update(now, pkts, bytes, timespan_ms);
 | |
| }
 | |
| 
 | |
| int CSndBuffer::getCurrBufSize(int& w_bytes, int& w_timespan) const
 | |
| {
 | |
|     w_bytes = m_iBytesCount;
 | |
|     /*
 | |
|      * Timespan can be less then 1000 us (1 ms) if few packets.
 | |
|      * Also, if there is only one pkt in buffer, the time difference will be 0.
 | |
|      * Therefore, always add 1 ms if not empty.
 | |
|      */
 | |
|     w_timespan = 0 < m_iCount ? (int) count_milliseconds(m_tsLastOriginTime - m_pFirstBlock->m_tsOriginTime) + 1 : 0;
 | |
| 
 | |
|     return m_iCount;
 | |
| }
 | |
| 
 | |
| CSndBuffer::duration CSndBuffer::getBufferingDelay(const time_point& tnow) const
 | |
| {
 | |
|     ScopedLock lck(m_BufLock);
 | |
|     SRT_ASSERT(m_pFirstBlock);
 | |
|     if (m_iCount == 0)
 | |
|         return duration(0);
 | |
| 
 | |
|     return tnow - m_pFirstBlock->m_tsOriginTime;
 | |
| }
 | |
| 
 | |
| int CSndBuffer::dropLateData(int& w_bytes, int32_t& w_first_msgno, const steady_clock::time_point& too_late_time)
 | |
| {
 | |
|     int     dpkts  = 0;
 | |
|     int     dbytes = 0;
 | |
|     bool    move   = false;
 | |
|     int32_t msgno  = 0;
 | |
| 
 | |
|     ScopedLock bufferguard(m_BufLock);
 | |
|     for (int i = 0; i < m_iCount && m_pFirstBlock->m_tsOriginTime < too_late_time; ++i)
 | |
|     {
 | |
|         dpkts++;
 | |
|         dbytes += m_pFirstBlock->m_iLength;
 | |
|         msgno = m_pFirstBlock->getMsgSeq();
 | |
| 
 | |
|         if (m_pFirstBlock == m_pCurrBlock)
 | |
|             move = true;
 | |
|         m_pFirstBlock = m_pFirstBlock->m_pNext;
 | |
|     }
 | |
| 
 | |
|     if (move)
 | |
|     {
 | |
|         m_pCurrBlock = m_pFirstBlock;
 | |
|     }
 | |
|     m_iCount -= dpkts;
 | |
| 
 | |
|     m_iBytesCount -= dbytes;
 | |
|     w_bytes = dbytes;
 | |
| 
 | |
|     // We report the increased number towards the last ever seen
 | |
|     // by the loop, as this last one is the last received. So remained
 | |
|     // (even if "should remain") is the first after the last removed one.
 | |
|     w_first_msgno = ++MsgNo(msgno);
 | |
| 
 | |
|     updAvgBufSize(steady_clock::now());
 | |
| 
 | |
|     return (dpkts);
 | |
| }
 | |
| 
 | |
| void CSndBuffer::increase()
 | |
| {
 | |
|     int unitsize = m_pBuffer->m_iSize;
 | |
| 
 | |
|     // new physical buffer
 | |
|     Buffer* nbuf = NULL;
 | |
|     try
 | |
|     {
 | |
|         nbuf           = new Buffer;
 | |
|         nbuf->m_pcData = new char[unitsize * m_iBlockLen];
 | |
|     }
 | |
|     catch (...)
 | |
|     {
 | |
|         delete nbuf;
 | |
|         throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0);
 | |
|     }
 | |
|     nbuf->m_iSize = unitsize;
 | |
|     nbuf->m_pNext = NULL;
 | |
| 
 | |
|     // insert the buffer at the end of the buffer list
 | |
|     Buffer* p = m_pBuffer;
 | |
|     while (p->m_pNext != NULL)
 | |
|         p = p->m_pNext;
 | |
|     p->m_pNext = nbuf;
 | |
| 
 | |
|     // new packet blocks
 | |
|     Block* nblk = NULL;
 | |
|     try
 | |
|     {
 | |
|         nblk = new Block;
 | |
|     }
 | |
|     catch (...)
 | |
|     {
 | |
|         delete nblk;
 | |
|         throw CUDTException(MJ_SYSTEMRES, MN_MEMORY, 0);
 | |
|     }
 | |
|     Block* pb = nblk;
 | |
|     for (int i = 1; i < unitsize; ++i)
 | |
|     {
 | |
|         pb->m_pNext = new Block;
 | |
|         pb          = pb->m_pNext;
 | |
|     }
 | |
| 
 | |
|     // insert the new blocks onto the existing one
 | |
|     pb->m_pNext           = m_pLastBlock->m_pNext;
 | |
|     m_pLastBlock->m_pNext = nblk;
 | |
| 
 | |
|     pb       = nblk;
 | |
|     char* pc = nbuf->m_pcData;
 | |
|     for (int i = 0; i < unitsize; ++i)
 | |
|     {
 | |
|         pb->m_pcData = pc;
 | |
|         pb           = pb->m_pNext;
 | |
|         pc += m_iBlockLen;
 | |
|     }
 | |
| 
 | |
|     m_iSize += unitsize;
 | |
| 
 | |
|     HLOGC(bslog.Debug,
 | |
|           log << "CSndBuffer: BUFFER FULL - adding " << (unitsize * m_iBlockLen) << " bytes spread to " << unitsize
 | |
|               << " blocks"
 | |
|               << " (total size: " << m_iSize << " bytes)");
 | |
| }
 | |
| 
 | |
| } // namespace srt
 |