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
 |