本文对 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置零
又重复到第一步操作。
本次分享到这里就结束啦,在今后的博客中,还会努力呈现出更多精彩内容,感谢大家的支持,如要问题欢迎提问。