【live555】 OutPacketBuffer在live555中如何使用源码分析

1 篇文章 0 订阅
1 篇文章 0 订阅

本文对 OutPacketBuffer做全面解析,从以下两个方面进行分析:
    1.OutPacketBuffer在Live555中如何使用
    2. 设计原理和代码分析

一.  Live555-Server端收到play命令开始传输数据,使用OutPacketBuffer做为数据缓冲区,MultiFramedRTPSink从文件解析出帧数据,将帧数据分解成RTP包数据,调用RTPInterface将 
RTP包数据传送到client端
实现如图:

 第一步: OutPacketBuffer缓冲区标记第一帧数据

Boolean MultiFramedRTPSink::continuePlaying()
{
	// Send the first packet.
	// (This will also schedule any future sends.)
	buildAndSendPacket(True);
	return True;
}

第二步:在OutPacketBuffer缓冲区增加rtp头信息

void MultiFramedRTPSink::buildAndSendPacket(Boolean isFirstPacket)
{
	nextTask() = NULL;
	fIsFirstPacket = isFirstPacket;  //是不是第一帧

	// Set up the RTP header:
	unsigned rtpHdr = 0x80000000; // RTP version 2; marker ('M') bit not set (by default; it can be set later)
	rtpHdr |= (fRTPPayloadType << 16);
	rtpHdr |= fSeqNo; // sequence number
	fOutBuf->enqueueWord(rtpHdr); //增加rtp信息头

	// Note where the RTP timestamp will go.
	// (We can't fill this in until we start packing payload frames.)
	fTimestampPosition = fOutBuf->curPacketSize();
	fOutBuf->skipBytes(4); // leave a hole for the timestamp

	fOutBuf->enqueueWord(SSRC());

	// Allow for a special, payload-format-specific header following the
	// RTP header:
	fSpecialHeaderPosition = fOutBuf->curPacketSize();
	fSpecialHeaderSize = specialHeaderSize();
	fOutBuf->skipBytes(fSpecialHeaderSize);

	// Begin packing as many (complete) frames into the packet as we can:
	fTotalFrameSpecificHeaderSizes = 0;
	fNoFramesLeft = False;
	fNumFramesUsedSoFar = 0;
	packFrame();
}

第三步: 请求数据

void MultiFramedRTPSink::packFrame()
{
	
   ............

	if (fOutBuf->haveOverflowData())
	{
	
		unsigned frameSize = fOutBuf->overflowDataSize();
		struct timeval presentationTime = fOutBuf->overflowPresentationTime();
		unsigned durationInMicroseconds = fOutBuf->overflowDurationInMicroseconds();
		fOutBuf->useOverflowData();

		afterGettingFrame1(frameSize, 0, presentationTime, durationInMicroseconds);
	}
	else
	{
		// Normal case: we need to read a new frame from the source
		if (fSource == NULL) 
			return;

		fSource->getNextFrame(fOutBuf->curPtr(), 
			fOutBuf->totalBytesAvailable(),
			afterGettingFrame, 
			this, 
			ourHandleClosure, 
			this);
			
	}
}

fOutBuf->haveOverflowData()是否有数据?如果有,调用fOutBuf->useOverflowData();

 afterGettingFrame1(frameSize, 0, presentationTime, durationInMicroseconds);

如果没有,从文件读取,fOutBuf->curPtr()缓冲地址,fOutBuf->totalBytesAvailable()缓冲区可用长度如图:

 afterGettingFrame是回调函数,从文件读到数据时调用。

 第四步:分包

void MultiFramedRTPSink::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
	struct timeval presentationTime,
	unsigned durationInMicroseconds)
{
	

    ......................

	if (numFrameBytesToUse > 0)
	{
		
		if (fOutBuf->wouldOverflow(frameSize))
		{
		
			if (isTooBigForAPacket(frameSize)
				&& (fNumFramesUsedSoFar == 0 || allowFragmentationAfterStart()))
			{
				// We need to fragment this frame, and use some of it now:
				overflowBytes = computeOverflowForNewFrame(frameSize);
				numFrameBytesToUse -= overflowBytes;
				fCurFragmentationOffset += numFrameBytesToUse;
			}
			


			fOutBuf->setOverflowData(fOutBuf->curPacketSize() + numFrameBytesToUse,
				overflowBytes, presentationTime, durationInMicroseconds);
		}

	}

	if (numFrameBytesToUse == 0 && frameSize > 0)
	{
		// Send our packet now, because we have filled it up:
		sendPacketIfNecessary();
	}
	else
	{
		
		............
		
		if (fOutBuf->isPreferredSize()
			|| fOutBuf->wouldOverflow(numFrameBytesToUse)
			|| (fPreviousFrameEndedFragmentation &&
				!allowOtherFramesAfterLastFragment())
			|| !frameCanAppearAfterPacketStart(fOutBuf->curPtr() - frameSize,
				frameSize))
		{
			// The packet is ready to be sent now
			sendPacketIfNecessary();
		}
		...................
	}
}

从文件读到视频数据大小frameSize,fOutBuf->wouldOverflow(frameSize)打成RTP包是否有溢出,注意:RTP包的最大长度1500, 但在Live555框架中最大长度设置为1452。如果有分包,fOutBuf->setOverflowData(...)记录溢出数据位置和长度。后面再详细解析OutPacketBuffer结构。

第五步:发送

void MultiFramedRTPSink::sendPacketIfNecessary()
{

    ...................

        if (!fRTPInterface.sendPacket(fOutBuf->packet(), fOutBuf->curPacketSize()))
		{

		}
			


	if (fOutBuf->haveOverflowData()
		&& fOutBuf->totalBytesAvailable() > fOutBuf->totalBufferSize() / 2)
	{
		
		unsigned newPacketStart = fOutBuf->curPacketSize() - (rtpHeaderSize + fSpecialHeaderSize + frameSpecificHeaderSize());


		fOutBuf->adjustPacketStart(newPacketStart);
	}
	else
	{
		
		fOutBuf->resetPacketStart();
	}
	fOutBuf->resetOffset();
	fNumFramesUsedSoFar = 0;

	............................
    		nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecondsToGo, (TaskFunc*)sendNext, this);

	
}

void MultiFramedRTPSink::sendNext(void* firstArg)
{
	MultiFramedRTPSink* sink = (MultiFramedRTPSink*)firstArg;
	sink->buildAndSendPacket(False);
}

fRTPInterface.sendPacket(fOutBuf->packet(), fOutBuf->curPacketSize())把rtp包发送出去,fOutBuf->packet()包的地址,fOutBuf->curPacketSize()包大小,fOutBuf->resetOffset()重置,        nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecondsToGo,(TaskFunc*)sendNext, this);  sendNext处理一下个数据。

最后总结一下OutPacketBuffer的使用:

 1. 创建:fOutBuf = new OutPacketBuffer(preferredPacketSize, maxPacketSize);   preferredPacketSize 包的理想值大小(可不关注),maxPacketSize指定包的最大长度(rtp包最大长度为1452)

2. 填写数据:  enqueue(...)、insert(...)、enqueueXXX(....)

3. 可选操作: setOverflowData(...),useOverflowData(...)

4. 使用:packet(), curPacketSize()。

二.  OutPacketBuffer 设计原理和代码分析

1.构造函数:

OutPacketBuffer::OutPacketBuffer(unsigned preferredPacketSize, unsigned maxPacketSize, unsigned maxBufferSize)
	: fPreferred(preferredPacketSize), fMax(maxPacketSize),
	fOverflowDataSize(0)
{
	if (maxBufferSize == 0) 
		maxBufferSize = maxSize;

	unsigned maxNumPackets = (maxBufferSize + (maxPacketSize - 1)) / maxPacketSize;
	fLimit = maxNumPackets * maxPacketSize;
	fBuf = new unsigned char[fLimit];
	resetPacketStart();
	resetOffset();
	resetOverflowData();

OutPacketBuffer::maxSize = 60000; maxBufferSize默认值6000;

maxPacketSize rtp包最大值1452,也就是fMax值1452

fLimit计算方式:

unsigned maxNumPackets = (maxBufferSize + (maxPacketSize - 1)) / maxPacketSize;
fLimit = maxNumPackets * maxPacketSize;

其目的是让fLimit大小是maxPacketSize的整数倍

内存分配图:

 2. OutPacketBuffer设计原理

private:
	unsigned fPacketStart, fCurOffset, fPreferred, fMax, fLimit;
	unsigned char* fBuf;

fBuf缓冲区大小为fLimit,fMax是rtp包最大长度, fCurOffset是当前包大小也就已用区域大小。

例如:向fBuf缓冲区增加数据  from作为数据源,numBytes作为数据源长度

        1. 先计算fBuf缓冲区可用区域大小  fLimit - (fPacketStart + fCurOffset),与numBytes 比较两者取最小值,

        2. 当前fBuf缓冲区可用区域指针: curPtr= &fBuf[fPacketStart + fCurOffset];

       3.  复制数据 memmove(curPtr, from, numBytes)

       4.  修改已用区域大小: fCurOffset += numBytes;

代码:


/// 向缓冲区增加 numBytes 个数据
void OutPacketBuffer::skipBytes(unsigned numBytes)
{
	if (numBytes > totalBytesAvailable())
	{
		numBytes = totalBytesAvailable();
	}

	increment(numBytes);
}


/// from数据源, numBytes数据源大小
void OutPacketBuffer::enqueue(unsigned char const* from, unsigned numBytes)
{

    /// numBytes 与可用区域比较 取最小值
	if (numBytes > totalBytesAvailable()) 
	{
		numBytes = totalBytesAvailable();
	}

	if (curPtr() != from) 
		memmove(curPtr(), from, numBytes); // 复制数据

	increment(numBytes);  修改已用区域
}

    unsigned char* curPtr() const
	{
		return &fBuf[fPacketStart + fCurOffset];
	}

	unsigned totalBytesAvailable() const
	{
		return fLimit - (fPacketStart + fCurOffset);
	}

   void increment(unsigned numBytes) { fCurOffset += numBytes; }

取出包:

.....
// 取出包数据
if (!fRTPInterface.sendPacket(fOutBuf->packet(), fOutBuf->curPacketSize()))
   {
		// if failure handler has been specified, call it
		if (fOnSendErrorFunc != NULL) (*fOnSendErrorFunc)(fOnSendErrorData);
	}
......




1. 缓冲区指针
unsigned char* packet() const 
{
    return &fBuf[fPacketStart];
}
2. 数据包大小
unsigned curPacketSize() const 
{
  return fCurOffset;
}

3. OutPacketBuffer数据溢出处理

 这里的数据溢出指的是 要处理数据长度大于fMax(rtp包最大长度)时,要分段发送,例如:

 RTP 信息头大小12, 视频帧大小 4000。

  1.计算溢出数据长度(未能本次发送的数据长度)

  2. 设置溢出数据信息

 3. 发送数据

 4. 重置包属性(清零)

 5.  使用溢出数据

 6.  重复步骤1

实现代码:   

	unsigned OutPacketBuffer::numOverflowBytes(unsigned numBytes) const
	{
		return (fCurOffset + numBytes) - fMax;
	}


unsigned MultiFramedRTPSink::computeOverflowForNewFrame(unsigned newFrameSize) const
{
	// default implementation: Just call numOverflowBytes()
	return fOutBuf->numOverflowBytes(newFrameSize);
}

void MultiFramedRTPSink::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
	struct timeval presentationTime,
	unsigned durationInMicroseconds)
{

    /// frameSize 视频帧数据大小  
    ....
                // 计算溢出数据长度
    			overflowBytes = computeOverflowForNewFrame(frameSize);   
            ///  overflowBytes  = (fCurOffset + numBytes) - fMax
				numFrameBytesToUse -= overflowBytes;
    ...
    			/ 设置溢出数据信息
                fOutBuf->setOverflowData(fOutBuf->curPacketSize() + numFrameBytesToUse,
				overflowBytes, presentationTime, durationInMicroseconds);

}

1. 计算溢出数据  overflowBytes = computeOverflowForNewFrame(frameSize); 假设

    RTP 信息头大小12, 视频帧大小 4000; frameSize=4000,fCurOffset=12,fMax=1452, 那么    overflowBytes = (12+4000) - 1452 )= 2560 下次发送

 numFrameBytesToUse = frameSize -  overflowBytes = 1440本次要发送的长度.

2. 设置溢出数据信息    fOutBuf->setOverflowData(fOutBuf->curPacketSize() + numFrameBytesToUse, overflowBytes, presentationTime, durationInMicroseconds);这时OutPacketBuffer中fOverflowDataSize= 2560, fOverflowDataOffset = 1452 如图:

 3. 发送数据 fRTPInterface.sendPacket(fOutBuf->packet(), fOutBuf->curPacketSize())

4. 重置包属性(清零)fOutBuf->resetOffset(); 把fCurOffset置零

5  使用溢出数据 

	void OutPacketBuffer::useOverflowData()
{
	enqueue(&fBuf[fPacketStart + fOverflowDataOffset], fOverflowDataSize);
	fCurOffset -= fOverflowDataSize; // undoes increment performed by "enqueue"
	resetOverflowData();
}


if (fOutBuf->haveOverflowData())
	{
		// Use this frame before reading a new one from the source
		unsigned frameSize = fOutBuf->overflowDataSize();
		struct timeval presentationTime = fOutBuf->overflowPresentationTime();
		unsigned durationInMicroseconds = fOutBuf->overflowDurationInMicroseconds();
		fOutBuf->useOverflowData();

		afterGettingFrame1(frameSize, 0, presentationTime, durationInMicroseconds);
	}

unsigned frameSize = fOutBuf->overflowDataSize(); 取得当前数据帧长度,enqueue(&fBuf[fPacketStart + fOverflowDataOffset], fOverflowDataSize); 将fOverflowDataSize的buffer移动到开始位置。然后resetOverflowData()将fOverflowDataSize和fOverflowDataOffset置零

又重复到第一步操作。

本次分享到这里就结束啦,在今后的博客中,还会努力呈现出更多精彩内容,感谢大家的支持,如要问题欢迎提问。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq00769539

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值