庖丁解牛-----Live555源码彻底解密(RTP解包)
Live555 客户端解包
以testRTSPClient.cpp为例讲解:
Medium<-MediaSource<-FramedSource<-RTPSource<-MultiFramedRTPSource<-H264VideoRTPSource
其中,我们要重点关注的类是下面几个:
FramedSource,RTPSource,MultiFramedRTPSource。
continuePlaying()函数中调用Source类(以MultiFramedRTPSource为例,因为它以实现doGetFrame()函数)的getNextFrame()函数以得到发送数据,而getNextFrame()是通过调用doGetNextFrame(),继而是doGetNextFrame1(),最终在doNextFrame1中由语句fReorderingBuffer->getNextCompletedPacket()将存放在fReorderingBuffer中的数据取出交给Sink类来发送。
Boolean DummySink::continuePlaying() {
if (fSource == NULL) return False; // sanity check (should not happen)
// Request the next frame of data from our input source. "afterGettingFrame()" will get called later, when it arrives:
fSource->getNextFrame(fReceiveBuffer, DUMMY_SINK_RECEIVE_BUFFER_SIZE,
afterGettingFrame, this,
onSourceClosure, this);
return True;
}
fSource调用的是FrameSource类的getNextFrame函数,源码如下:
void FramedSource::getNextFrame(unsigned char* to, unsigned maxSize,
afterGettingFunc* afterGettingFunc,
void* afterGettingClientData,
onCloseFunc* onCloseFunc,
void* onCloseClientData) {
// Make sure we're not already being read:
if (fIsCurrentlyAwaitingData) {
envir() << "FramedSource[" << this << "]::getNextFrame(): attempting to read more than once at the same time!\n";
envir().internalError();
}
fTo = to;
fMaxSize = maxSize;
fNumTruncatedBytes = 0; // by default; could be changed by doGetNextFrame()
fDurationInMicroseconds = 0; // by default; could be changed by doGetNextFrame()
fAfterGettingFunc = afterGettingFunc;
fAfterGettingClientData = afterGettingClientData;
fOnCloseFunc = onCloseFunc;
fOnCloseClientData = onCloseClientData;
fIsCurrentlyAwaitingData = True;
doGetNextFrame();
}
跟踪进入到doGetNextFrame()在FramedSource类中为纯虚函数,在子类MultiFramedRTPSource中实现,源码如下:
void MultiFramedRTPSource::doGetNextFrame() {
if (!fAreDoingNetworkReads) {
// Turn on background read handling of incoming packets:
//将标志位置为TRUE
fAreDoingNetworkReads = True;
TaskScheduler::BackgroundHandlerProc* handler
= (TaskScheduler::BackgroundHandlerProc*)&networkReadHandler;
//如何获取数据:networkReadHandler1()函数不断从fRTPInterface所指向的socket读入, 最终执行语句fReorderingBuffer->storePacket(bPacket);实现。
//开始网络数据包接收
fRTPInterface.startNetworkReading(handler);
}
fSavedTo = fTo;
fSavedMaxSize = fMaxSize;
fFrameSize = 0; // for now
fNeedDelivery = True;
doGetNextFrame1();
}
networkReadHandler源码如下:
void MultiFramedRTPSource::networkReadHandler(MultiFramedRTPSource* source, int /*mask*/) {
source->networkReadHandler1();
}
networkReadHandler1源码如下:
void MultiFramedRTPSource::networkReadHandler1() {
BufferedPacket* bPacket = fPacketReadInProgress;
if (bPacket == NULL) {
// Normal case: Get a free BufferedPacket descriptor to hold the new network packet:
bPacket = fReorderingBuffer->getFreePacket(this);
}
// Read the network packet, and perform sanity checks on the RTP header:
//读出网络数据包,并检查RTP包头
Boolean readSuccess = False;
do {
Boolean packetReadWasIncomplete = fPacketReadInProgress != NULL;
if (!bPacket->fillInData(fRTPInterface, packetReadWasIncomplete)) {
if (bPacket->bytesAvailable() == 0) {
envir() << "MultiFramedRTPSource error: Hit limit when reading incoming packet over TCP. Increase \"MAX_PACKET_SIZE\"\n";
}
fPacketReadInProgress = NULL;
break;
}
if (packetReadWasIncomplete) {
// We need additional read(s) before we can process the incoming packet:
fPacketReadInProgress = bPacket;
return;
} else {
fPacketReadInProgress = NULL;
}
#ifdef TEST_LOSS
setPacketReorderingThresholdTime(0);
// don't wait for 'lost' packets to arrive out-of-order later
if ((our_random()%10) == 0) break; // simulate 10% packet loss
#endif
// Check for the 12-byte RTP header:
if (bPacket->dataSize() < 12) break;
unsigned rtpHdr = ntohl(*(u_int32_t*)(bPacket->data())); ADVANCE(4);
Boolean rtpMarkerBit = (rtpHdr&0x00800000) != 0;
unsigned rtpTimestamp = ntohl(*(u_int32_t*)(bPacket->data()));ADVANCE(4);
unsigned rtpSSRC = ntohl(*(u_int32_t*)(bPacket->data())); ADVANCE(4);
// Check the RTP version number (it should be 2):
if ((rtpHdr&0xC0000000) != 0x80000000) break;
// Skip over any CSRC identifiers in the header:
unsigned cc = (rtpHdr>>24)&0xF;
if (bPacket->dataSize() < cc) break;
ADVANCE(cc*4);
// Check for (& ignore) any RTP header extension
if (rtpHdr&0x10000000) {
if (bPacket->dataSize() < 4) break;
unsigned extHdr = ntohl(*(u_int32_t*)(bPacket->data())); ADVANCE(4);
unsigned remExtSize = 4*(extHdr&0xFFFF);
if (bPacket->dataSize() < remExtSize) break;
ADVANCE(remExtSize);
}
// Discard any padding bytes:
if (rtpHdr&0x20000000) {
if (bPacket->dataSize() == 0) break;
unsigned numPaddingBytes
= (unsigned)(bPacket->data())[bPacket->dataSize()-1];
if (bPacket->dataSize() < numPaddingBytes) break;
bPacket->removePadding(numPaddingBytes);
}
// Check the Payload Type.
if ((unsigned char)((rtpHdr&0x007F0000)>>16)
!= rtpPayloadFormat()) {
break;
}
// The rest of the packet is the usable data. Record and save it:
if (rtpSSRC != fLastReceivedSSRC) {
// The SSRC of incoming packets has changed. Unfortunately we don't yet handle streams that contain multiple SSRCs,
// but we can handle a single-SSRC stream where the SSRC changes occasionally:
fLastReceivedSSRC = rtpSSRC;
fReorderingBuffer->resetHaveSeenFirstPacket();
}
unsigned short rtpSeqNo = (unsigned short)(rtpHdr&0xFFFF);
Boolean usableInJitterCalculation
= packetIsUsableInJitterCalculation((bPacket->data()),
bPacket->dataSize());
struct timeval presentationTime; // computed by:
Boolean hasBeenSyncedUsingRTCP; // computed by:
receptionStatsDB()
.noteIncomingPacket(rtpSSRC, rtpSeqNo, rtpTimestamp,
timestampFrequency(),
usableInJitterCalculation, presentationTime,
hasBeenSyncedUsingRTCP, bPacket->dataSize());
// Fill in the rest of the packet descriptor, and store it:
struct timeval timeNow;
gettimeofday(&timeNow, NULL);
bPacket->assignMiscParams(rtpSeqNo, rtpTimestamp, presentationTime,
hasBeenSyncedUsingRTCP, rtpMarkerBit,
timeNow);
//将收到的包插入到fReorderingBuffer中
if (!fReorderingBuffer->storePacket(bPacket)) break;
readSuccess = True;
} while (0);
if (!readSuccess) fReorderingBuffer->freePacket(bPacket);
doGetNextFrame1();
// If we didn't get proper data this time, we'll get another chance
}
1)fillInData函数源码如下:
Boolean BufferedPacket::fillInData(RTPInterface& rtpInterface, Boolean& packetReadWasIncomplete) {
if (!packetReadWasIncomplete) reset();
unsigned numBytesRead;
struct sockaddr_in fromAddress;
unsigned const maxBytesToRead = bytesAvailable();
if (maxBytesToRead == 0) return False; // exceeded buffer size when reading over TCP
if (!rtpInterface.handleRead(&fBuf[fTail], maxBytesToRead, numBytesRead, fromAddress, packetReadWasIncomplete)) {
return False;
}
fTail += numBytesRead;
return True;
}
fillInData调用handleRead实现了将rtpInterface指向的数据存入fBuf中的功能。
源码如下:
Boolean RTPInterface::handleRead(unsigned char* buffer, unsigned bufferMaxSize,
unsigned& bytesRead, struct sockaddr_in& fromAddress, Boolean& packetReadWasIncomplete) {
packetReadWasIncomplete = False; // by default
Boolean readSuccess;
if (fNextTCPReadStreamSocketNum < 0) {
// Normal case: read from the (datagram) 'groupsock':
readSuccess = fGS->handleRead(buffer, bufferMaxSize, bytesRead, fromAddress);
} else {
// Read from the TCP connection:
bytesRead = 0;
unsigned totBytesToRead = fNextTCPReadSize;
if (totBytesToRead > bufferMaxSize) totBytesToRead = bufferMaxSize;
unsigned curBytesToRead = totBytesToRead;
int curBytesRead;
while ((curBytesRead = readSocket(envir(), fNextTCPReadStreamSocketNum,
&buffer[bytesRead], curBytesToRead,
fromAddress)) > 0) {
bytesRead += curBytesRead;
if (bytesRead >= totBytesToRead) break;
curBytesToRead -= curBytesRead;
}
fNextTCPReadSize -= bytesRead;
if (fNextTCPReadSize == 0) {
// We've read all of the data that we asked for
readSuccess = True;
} else if (curBytesRead < 0) {
// There was an error reading the socket
bytesRead = 0;
readSuccess = False;
} else {
// We need to read more bytes, and there was not an error reading the socket
packetReadWasIncomplete = True;
return True;
}
fNextTCPReadStreamSocketNum = -1; // default, for next time
}
if (readSuccess && fAuxReadHandlerFunc != NULL) {
// Also pass the newly-read packet data to our auxilliary handler:
(*fAuxReadHandlerFunc)(fAuxReadHandlerClientData, buffer, bytesRead);
}
return readSuccess;
}
该函数从socket中读数据包;
2)storePacket函数源码如下:
Boolean ReorderingPacketBuffer::storePacket(BufferedPacket* bPacket) {
unsigned short rtpSeqNo = bPacket->rtpSeqNo();
if (!fHaveSeenFirstPacket) {
fNextExpectedSeqNo = rtpSeqNo; // initialization
bPacket->isFirstPacket() = True;
fHaveSeenFirstPacket = True;
}
// Ignore this packet if its sequence number is less than the one
// that we're looking for (in this case, it's been excessively delayed).
if (seqNumLT(rtpSeqNo, fNextExpectedSeqNo)) return False;
if (fTailPacket == NULL) {
// Common case: There are no packets in the queue; this will be the first one:
bPacket->nextPacket() = NULL;
fHeadPacket = fTailPacket = bPacket;
return True;
}
if (seqNumLT(fTailPacket->rtpSeqNo(), rtpSeqNo)) {
// The next-most common case: There are packets already in the queue; this packet arrived in order => put it at the tail:
bPacket->nextPacket() = NULL;
fTailPacket->nextPacket() = bPacket;
fTailPacket = bPacket;
return True;
}
if (rtpSeqNo == fTailPacket->rtpSeqNo()) {
// This is a duplicate packet - ignore it
return False;
}
// Rare case: This packet is out-of-order. Run through the list (from the head), to figure out where it belongs:
BufferedPacket* beforePtr = NULL;
BufferedPacket* afterPtr = fHeadPacket;
while (afterPtr != NULL) {
if (seqNumLT(rtpSeqNo, afterPtr->rtpSeqNo())) break; // it comes here
if (rtpSeqNo == afterPtr->rtpSeqNo()) {
// This is a duplicate packet - ignore it
return False;
}
beforePtr = afterPtr;
afterPtr = afterPtr->nextPacket();
}
// Link our new packet between "beforePtr" and "afterPtr":
bPacket->nextPacket() = afterPtr;
if (beforePtr == NULL) {
fHeadPacket = bPacket;
} else {
beforePtr->nextPacket() = bPacket;
}
return True;
}
该函数中有do while(0)的妙用,可以参考:
http://www.cnblogs.com/flying_bat/archive/2008/01/18/1044693.html
继续调用doGetNextFrame1()函数,源码如下:
void MultiFramedRTPSource::doGetNextFrame1() {
//是否需要发送,交给sink处理
while (fNeedDelivery) {
// If we already have packet data available, then deliver it now.
Boolean packetLossPrecededThis;
//获取下一个完整的数据包
BufferedPacket* nextPacket
= fReorderingBuffer->getNextCompletedPacket(packetLossPrecededThis);
if (nextPacket == NULL) break;
//丢给Sink后将fNeedDelivery置为False
fNeedDelivery = False;
if (nextPacket->useCount() == 0) {
// Before using the packet, check whether it has a special header
// that needs to be processed:
unsigned specialHeaderSize;
//处理特殊包头,比如FU-A的分片包头
if (!processSpecialHeader(nextPacket, specialHeaderSize)) {
// Something's wrong with the header; reject the packet:
fReorderingBuffer->releaseUsedPacket(nextPacket);
fNeedDelivery = True;
break;
}
nextPacket->skip(specialHeaderSize);
}
// Check whether we're part of a multi-packet frame, and whether
// there was packet loss that would render this packet unusable:
if (fCurrentPacketBeginsFrame) {
if (packetLossPrecededThis || fPacketLossInFragmentedFrame) {
// We didn't get all of the previous frame.
// Forget any data that we used from it:
fTo = fSavedTo; fMaxSize = fSavedMaxSize;
fFrameSize = 0;
}
fPacketLossInFragmentedFrame = False;
} else if (packetLossPrecededThis) {
// We're in a multi-packet frame, with preceding packet loss
fPacketLossInFragmentedFrame = True;
}
if (fPacketLossInFragmentedFrame) {
// This packet is unusable; reject it:
fReorderingBuffer->releaseUsedPacket(nextPacket);
fNeedDelivery = True;
break;
}
// The packet is usable. Deliver all or part of it to our caller:
unsigned frameSize;
nextPacket->use(fTo, fMaxSize, frameSize, fNumTruncatedBytes,
fCurPacketRTPSeqNum, fCurPacketRTPTimestamp,
fPresentationTime, fCurPacketHasBeenSynchronizedUsingRTCP,
fCurPacketMarkerBit);
fFrameSize += frameSize;
if (!nextPacket->hasUsableData()) {
// We're completely done with this packet now
fReorderingBuffer->releaseUsedPacket(nextPacket);
}
//已经是完整的一帧数据了,回调给Sink处理
if (fCurrentPacketCompletesFrame) {
// We have all the data that the client wants.
if (fNumTruncatedBytes > 0) {
envir() << "MultiFramedRTPSource::doGetNextFrame1(): The total received frame size exceeds the client's buffer size ("
<< fSavedMaxSize << "). "
<< fNumTruncatedBytes << " bytes of trailing data will be dropped!\n";
}
// Call our own 'after getting' function, so that the downstream object can consume the data:
if (fReorderingBuffer->isEmpty()) {
// Common case optimization: There are no more queued incoming packets, so this code will not get
// executed again without having first returned to the event loop. Call our 'after getting' function
// directly, because there's no risk of a long chain of recursion (and thus stack overflow):
afterGetting(this);
} else {
// Special case: Call our 'after getting' function via the event loop.
nextTask() = envir().taskScheduler().scheduleDelayedTask(0,
(TaskFunc*)FramedSource::afterGetting, this);
}
} else {
// This packet contained fragmented data, and does not complete
// the data that the client wants. Keep getting data:
fTo += frameSize; fMaxSize -= frameSize;
fNeedDelivery = True;
}
}
}
其中几个重要的函数解析如下:
1) getNextCompletedPacket
BufferedPacket* ReorderingPacketBuffer
::getNextCompletedPacket(Boolean& packetLossPreceded) {
if (fHeadPacket == NULL) return NULL;
// Check whether the next packet we want is already at the head
// of the queue:
// ASSERT: fHeadPacket->rtpSeqNo() >= fNextExpectedSeqNo
if (fHeadPacket->rtpSeqNo() == fNextExpectedSeqNo) {
packetLossPreceded = fHeadPacket->isFirstPacket();
// (The very first packet is treated as if there was packet loss beforehand.)
return fHeadPacket;
}
// We're still waiting for our desired packet to arrive. However, if
// our time threshold has been exceeded, then forget it, and return
// the head packet instead:
Boolean timeThresholdHasBeenExceeded;
if (fThresholdTime == 0) {
timeThresholdHasBeenExceeded = True; // optimization
} else {
struct timeval timeNow;
gettimeofday(&timeNow, NULL);
unsigned uSecondsSinceReceived
= (timeNow.tv_sec - fHeadPacket->timeReceived().tv_sec)*1000000
+ (timeNow.tv_usec - fHeadPacket->timeReceived().tv_usec);
timeThresholdHasBeenExceeded = uSecondsSinceReceived > fThresholdTime;
}
if (timeThresholdHasBeenExceeded) {
fNextExpectedSeqNo = fHeadPacket->rtpSeqNo();
// we've given up on earlier packets now
packetLossPreceded = True;
return fHeadPacket;
}
// Otherwise, keep waiting for our desired packet to arrive:
return NULL;
}
2) processSpecialHeader
H264的特殊头处理函数如下:
Boolean H264VideoRTPSource
::processSpecialHeader(BufferedPacket* packet,
unsigned& resultSpecialHeaderSize) {
unsigned char* headerStart = packet->data();
unsigned packetSize = packet->dataSize();
// The header has a minimum size of 0, since the NAL header is used
// as a payload header
unsigned expectedHeaderSize = 0;
// Check if the type field is 28 (FU-A) or 29 (FU-B)
fCurPacketNALUnitType = (headerStart[0]&0x1F);
switch (fCurPacketNALUnitType) {
case 24: { // STAP-A
expectedHeaderSize = 1; // discard the type byte
break;
}
case 25: case 26: case 27: { // STAP-B, MTAP16, or MTAP24
expectedHeaderSize = 3; // discard the type byte, and the initial DON
break;
}
case 28: case 29: { // // FU-A or FU-B
// For these NALUs, the first two bytes are the FU indicator and the FU header.
// If the start bit is set, we reconstruct the original NAL header:
unsigned char startBit = headerStart[1]&0x80;
unsigned char endBit = headerStart[1]&0x40;
if (startBit) {
expectedHeaderSize = 1;
if (packetSize < expectedHeaderSize) return False;
headerStart[1] = (headerStart[0]&0xE0)+(headerStart[1]&0x1F);
fCurrentPacketBeginsFrame = True;
} else {
// If the startbit is not set, both the FU indicator and header
// can be discarded
expectedHeaderSize = 2;
if (packetSize < expectedHeaderSize) return False;
fCurrentPacketBeginsFrame = False;
}
fCurrentPacketCompletesFrame = (endBit != 0);
break;
}
default: {
// This packet contains one or more complete, decodable NAL units
fCurrentPacketBeginsFrame = fCurrentPacketCompletesFrame = True;
break;
}
}
resultSpecialHeaderSize = expectedHeaderSize;
return True;
}
3)use将解析的内容放到to的Buff中;
void BufferedPacket::use(unsigned char* to, unsigned toSize,
unsigned& bytesUsed, unsigned& bytesTruncated,
unsigned short& rtpSeqNo, unsigned& rtpTimestamp,
struct timeval& presentationTime,
Boolean& hasBeenSyncedUsingRTCP,
Boolean& rtpMarkerBit) {
unsigned char* origFramePtr = &fBuf[fHead];
unsigned char* newFramePtr = origFramePtr; // may change in the call below
unsigned frameSize, frameDurationInMicroseconds;
getNextEnclosedFrameParameters(newFramePtr, fTail - fHead,
frameSize, frameDurationInMicroseconds);
if (frameSize > toSize) {
bytesTruncated += frameSize - toSize;
bytesUsed = toSize;
} else {
bytesTruncated = 0;
bytesUsed = frameSize;
}
memmove(to, newFramePtr, bytesUsed);
fHead += (newFramePtr - origFramePtr) + frameSize;
++fUseCount;
rtpSeqNo = fRTPSeqNo;
rtpTimestamp = fRTPTimestamp;
presentationTime = fPresentationTime;
hasBeenSyncedUsingRTCP = fHasBeenSyncedUsingRTCP;
rtpMarkerBit = fRTPMarkerBit;
// Update "fPresentationTime" for the next enclosed frame (if any):
fPresentationTime.tv_usec += frameDurationInMicroseconds;
if (fPresentationTime.tv_usec >= 1000000) {
fPresentationTime.tv_sec += fPresentationTime.tv_usec/1000000;
fPresentationTime.tv_usec = fPresentationTime.tv_usec%1000000;
}
}
其中getNextEnclosedFrameParameters函数源码如下:
void BufferedPacket
::getNextEnclosedFrameParameters(unsigned char*& framePtr, unsigned dataSize,
unsigned& frameSize,
unsigned& frameDurationInMicroseconds) {
// By default, use the entire buffered data, even though it may consist
// of more than one frame, on the assumption that the client doesn't
// care. (This is more efficient than delivering a frame at a time)
// For backwards-compatibility with existing uses of (the now deprecated)
// "nextEnclosedFrameSize()", call that function to implement this one:
frameSize = nextEnclosedFrameSize(framePtr, dataSize);
frameDurationInMicroseconds = 0; // by default. Subclasses should correct this.
}
nextEnclosedFrameSize函数源码如下:
unsigned H264BufferedPacket
::nextEnclosedFrameSize(unsigned char*& framePtr, unsigned dataSize) {
unsigned resultNALUSize = 0; // if an error occurs
switch (fOurSource.fCurPacketNALUnitType) {
case 24: case 25: { // STAP-A or STAP-B
// The first two bytes are NALU size:
if (dataSize < 2) break;
resultNALUSize = (framePtr[0]<<8)|framePtr[1];
framePtr += 2;
break;
}
case 26: { // MTAP16
// The first two bytes are NALU size. The next three are the DOND and TS offset:
if (dataSize < 5) break;
resultNALUSize = (framePtr[0]<<8)|framePtr[1];
framePtr += 5;
break;
}
case 27: { // MTAP24
// The first two bytes are NALU size. The next four are the DOND and TS offset:
if (dataSize < 6) break;
resultNALUSize = (framePtr[0]<<8)|framePtr[1];
framePtr += 6;
break;
}
default: {
// Common case: We use the entire packet data:
return dataSize;
}
}
return (resultNALUSize <= dataSize) ? resultNALUSize : dataSize;
}
4)afterGetting
afterGetting表示一个Loop结束,调用的是FramedSource的afterGetting
void FramedSource::afterGetting(FramedSource* source) {
source->fIsCurrentlyAwaitingData = False;
// indicates that we can be read again
// Note that this needs to be done here, in case the "fAfterFunc"
// called below tries to read another frame (which it usually will)
// fAfterGettingFunc就是afterGettingFrame函数
if (source->fAfterGettingFunc != NULL) {
(*(source->fAfterGettingFunc))(source->fAfterGettingClientData,
source->fFrameSize, source->fNumTruncatedBytes,
source->fPresentationTime,
source->fDurationInMicroseconds);
}
}
最终调用表示将Source的数据传递到Sink,由Sink进行处理,
void DummySink::afterGettingFrame(void* clientData, unsigned frameSize, unsigned numTruncatedBytes,
struct timeval presentationTime, unsigned durationInMicroseconds) {
DummySink* sink = (DummySink*)clientData;
sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime, durationInMicroseconds);
}
void DummySink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,
struct timeval presentationTime, unsigned /*durationInMicroseconds*/) {
// We've just received a frame of data. (Optionally) print out information about it:
#ifdef DEBUG_PRINT_EACH_RECEIVED_FRAME
if (fStreamId != NULL) envir() << "Stream \"" << fStreamId << "\"; ";
envir() << fSubsession.mediumName() << "/" << fSubsession.codecName() << ":\tReceived " << frameSize << " bytes";
if (numTruncatedBytes > 0) envir() << " (with " << numTruncatedBytes << " bytes truncated)";
char uSecsStr[6+1]; // used to output the 'microseconds' part of the presentation time
sprintf(uSecsStr, "%06u", (unsigned)presentationTime.tv_usec);
envir() << ".\tPresentation time: " << (int)presentationTime.tv_sec << "." << uSecsStr;
if (fSubsession.rtpSource() != NULL && !fSubsession.rtpSource()->hasBeenSynchronizedUsingRTCP()) {
envir() << "!"; // mark the debugging output to indicate that this presentation time is not RTCP-synchronized
}
#ifdef DEBUG_PRINT_NPT
envir() << "\tNPT: " << fSubsession.getNormalPlayTime(presentationTime);
#endif
envir() << "\n";
#endif
// Then continue, to request the next frame of data:
//再次调用continuePlaying()获取下一帧数据
continuePlaying();
}
其中还有一个很重要的解析sps/pps函数,源码如下:
解析Sps/Pps函数:
SPropRecord* parseSPropParameterSets(char const* sPropParameterSetsStr,
// result parameter:
unsigned& numSPropRecords) {
// Make a copy of the input string, so we can replace the commas with '\0's:
char* inStr = strDup(sPropParameterSetsStr);
if (inStr == NULL) {
numSPropRecords = 0;
return NULL;
}
// Count the number of commas (and thus the number of parameter sets):
numSPropRecords = 1;
char* s;
for (s = inStr; *s != '\0'; ++s) {
if (*s == ',') {
++numSPropRecords;
*s = '\0';
}
}
// Allocate and fill in the result array:
SPropRecord* resultArray = new SPropRecord[numSPropRecords];
s = inStr;
for (unsigned i = 0; i < numSPropRecords; ++i) {
resultArray[i].sPropBytes = base64Decode(s, resultArray[i].sPropLength);
s += strlen(s) + 1;
}
delete[] inStr;
return resultArray;
}
from:http://blog.csdn.net/smilestone_322/article/details/18940005
庖丁解牛-----Live555源码彻底解密(RTP解包)的更多相关文章
- 庖丁解牛-----Live555源码彻底解密(RTP打包)
本文主要讲解live555的服务端RTP打包流程,根据MediaServer讲解RTP的打包流程,所以大家看这篇文章时,先看看下面这个链接的内容; 庖丁解牛-----Live555源码彻底解密(根据M ...
- 庖丁解牛-----Live555源码彻底解密(根据MediaServer讲解Rtsp的建立过程)
live555MediaServer.cpp服务端源码讲解 int main(int argc, char** argv) { // Begin by setting up our usage env ...
- 27 GroupSock概述(一)——live555源码阅读(四)网络
27 GroupSock概述(一)——live555源码阅读(四)网络 27 GroupSock概述(一)——live555源码阅读(四)网络 简介 1.网络通用数据类型定义 2.Tunnel隧道封装 ...
- vs2010编译live555源码
最近加入了公司的C++视频小组,利用中秋这个假期将研究了一些live555的源码,现在先将如何编译使用vs2010编译live555,整理出来,对以后分析代码有很大帮助. 1.下载live555源码, ...
- Windows下编译live555源码
Windos下编译live555源码 环境 Win7 64位 + VS2012 步骤 1)源码下载并解压 在官网上下载最新live555源码,并对其进行解压. 2)VS下建立工程项目 新建Win32项 ...
- 40 网络相关函数(八)——live555源码阅读(四)网络
40 网络相关函数(八)——live555源码阅读(四)网络 40 网络相关函数(八)——live555源码阅读(四)网络 简介 15)writeSocket向套接口写数据 TTL的概念 函数send ...
- 39 网络相关函数(七)——live555源码阅读(四)网络
39 网络相关函数(七)——live555源码阅读(四)网络 39 网络相关函数(七)——live555源码阅读(四)网络 简介 14)readSocket从套接口读取数据 recv/recvfrom ...
- 38 网络相关函数(六)——live555源码阅读(四)网络
38 网络相关函数(六)——live555源码阅读(四)网络 38 网络相关函数(六)——live555源码阅读(四)网络 简介 12)makeSocketNonBlocking和makeSocket ...
- 37 网络相关函数(五)——live555源码阅读(四)网络
37 网络相关函数(五)——live555源码阅读(四)网络 37 网络相关函数(五)——live555源码阅读(四)网络 简介 10)MAKE_SOCKADDR_IN构建sockaddr_in结构体 ...
随机推荐
- NYOJ-733 万圣节派对 AC 分类: NYOJ 2014-01-02 00:41 303人阅读 评论(0) 收藏
#include <stdio.h> #include <math.h> int main() { int t, a, b, i, j, n; scanf("%d&q ...
- VB程序破解之API断点[bp __vbaVarTstEq]
软件名称:风云足彩1.7软件大小:2.1M下载地址:http://free.ys168.com/?zhinengxuanhao软件保护:注册码编写软件:Microsoft Visual Basic 5 ...
- 弱弱的玩下Javascript
前言 好久没有更新博客了,也蛮少捣弄javascript,今儿看到一个题目,关于给你一个面板,你可以随意的在上面画矩形,可以移动和删除任意一个你创建的矩形,心血来潮搞着玩哈,实现起来挺简单的,但这代码 ...
- java.lang.IllegalArgumentException: Requested window android.os.BinderProxy@450b2f48 异常处理
晕死的错误,改了半天也没想到是这样的原因,基础正要呀... 先看一下警告信息: 07-07 08:32:19.540: WARN/WindowManager(74): Failed looking u ...
- NGINX的奇淫技巧 —— 5. NGINX实现金盾防火墙的功能(防CC)
NGINX的奇淫技巧 —— 5. NGINX实现金盾防火墙的功能(防CC) ARGUS 1月13日 发布 推荐 0 推荐 收藏 2 收藏,1.1k 浏览 文章整理中...... 实现思路 当服务器接收 ...
- linux源代码阅读笔记 八进制
c语言中,众所周知,以0x开头的数是16进制数.例如 0x8FFF 然而较少使用的是八进制数.它以0开头.例如 01234
- 2013年山东省第四届ACM大学生程序设计竞赛 Alice and Bob
Alice and Bob Time Limit: 1000ms Memory limit: 65536K 题目描述 Alice and Bob like playing games very ...
- 1829 A Bug's Life
A Bug's Life Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Tot ...
- python package list
argparse: 解析命令行参数:http://www.cnblogs.com/snow-backup/p/4010751.html logging: 写日志; http://blog.csdn.n ...
- GC垃圾回收之GC.KeepAlive方法
http://msdn.microsoft.com/zh-cn/library/system.gc.keepalive.aspx http://www.cnblogs.com/ren700622/ar ...