本文在linux环境下编译live555工程,并用cgdb调试工具对live555工程中的testProgs目录下的openRTSP的执行过程进行了跟踪分析,直到将从socket端读取视频数据并保存为对应的视频和音频数据为止。

进入testProgs目录,执行./openRTSP rtsp://xxxx/test.mp4

对于RTSP协议的处理部分,可设置断点在setupStreams函数中,并跟踪即可进行分析。

这里主要分析进入如下的while(1)循环中的代码

[cpp] view plaincopy
  1. void BasicTaskScheduler0::doEventLoop(char* watchVariable)
  2. {
  3. // Repeatedly loop, handling readble sockets and timed events:
  4. while (1)
  5. {
  6. if (watchVariable != NULL && *watchVariable != 0) break;
  7. SingleStep();
  8. }
  9. }

从这里可知,live555在客户端处理数据实际上是单线程的程序,不断执行SingleStep()函数中的代码。通过查看该函数代码里,下面一句代码为重点

[cpp] view plaincopy
  1. (*handler->handlerProc)(handler->clientData, resultConditionSet);

其中该条代码出现了两次,通过调试跟踪它的执行轨迹,第一次出现调用的函数是为了处理和RTSP服务器的通信协议的商定,而第二次出现调用的函数才是处理真正的视频和音频数据。对于RTSP通信协议的分析我们暂且不讨论,而直接进入第二次调用该函数的部分。

在我们的调试过程中在执行到上面的函数时就直接调用到livemedia目录下的如下函数

[cpp] view plaincopy
  1. void MultiFramedRTPSource::networkReadHandler(MultiFramedRTPSource* source, int /*mask*/)
  2. {
  3. source->networkReadHandler1();
  4. }

//下面这个函数实现的主要功能就是从socket端读取数据并存储数据

[cpp] view plaincopy
  1. void MultiFramedRTPSource::networkReadHandler1()
  2. {
  3. BufferedPacket* bPacket = fPacketReadInProgress;
  4. if (bPacket == NULL)
  5. {
  6. // Normal case: Get a free BufferedPacket descriptor to hold the new network packet:
  7. //分配一块新的存储空间来存储从socket端读取的数据
  8. bPacket = fReorderingBuffer->getFreePacket(this);
  9. }
  10. // Read the network packet, and perform sanity checks on the RTP header:
  11. Boolean readSuccess = False;
  12. do
  13. {
  14. Boolean packetReadWasIncomplete = fPacketReadInProgress != NULL;
  15. //fillInData()函数封装了从socket端获取数据的过程,到此函数执行完已经将数据保存到了bPacket对象中
  16. if (!bPacket->fillInData(fRTPInterface, packetReadWasIncomplete))
  17. {
  18. if (bPacket->bytesAvailable() == 0)
  19. {
  20. envir() << "MultiFramedRTPSource error: Hit limit when reading incoming packet over TCP. Increase \"MAX_PACKET_SIZE\"\n";
  21. }
  22. break;
  23. }
  24. if (packetReadWasIncomplete)
  25. {
  26. // We need additional read(s) before we can process the incoming packet:
  27. fPacketReadInProgress = bPacket;
  28. return;
  29. else
  30. {
  31. fPacketReadInProgress = NULL;
  32. }
  33. //省略关于RTP包的处理
  34. ...
  35. ...
  36. ...
  37. //fReorderingBuffer为MultiFramedRTPSource类中的对象,该对象建立了一个存储Packet数据包对象的链表
  38. //下面的storePacket()函数即将上面获取的数据包存储在链表中
  39. if (!fReorderingBuffer->storePacket(bPacket)) break;
  40. readSuccess = True;
  41. while (0);
  42. if (!readSuccess) fReorderingBuffer->freePacket(bPacket);
  43. doGetNextFrame1();
  44. // If we didn't get proper data this time, we'll get another chance
  45. }

//下面的这个函数则实现从上面函数中介绍的存储数据包链表的对象(即fReorderingBuffer)中取出数据包并调用相应函数使用它

//代码1.1

[cpp] view plaincopy
  1. void MultiFramedRTPSource::doGetNextFrame1()
  2. {
  3. while (fNeedDelivery)
  4. {
  5. // If we already have packet data available, then deliver it now.
  6. Boolean packetLossPrecededThis;
  7. //从fReorderingBuffer对象中取出一个数据包
  8. BufferedPacket* nextPacket
  9. = fReorderingBuffer->getNextCompletedPacket(packetLossPrecededThis);
  10. if (nextPacket == NULL) break;
  11. fNeedDelivery = False;
  12. if (nextPacket->useCount() == 0)
  13. {
  14. // Before using the packet, check whether it has a special header
  15. // that needs to be processed:
  16. unsigned specialHeaderSize;
  17. if (!processSpecialHeader(nextPacket, specialHeaderSize))
  18. {
  19. // Something's wrong with the header; reject the packet:
  20. fReorderingBuffer->releaseUsedPacket(nextPacket);
  21. fNeedDelivery = True;
  22. break;
  23. }
  24. nextPacket->skip(specialHeaderSize);
  25. }
  26. // Check whether we're part of a multi-packet frame, and whether
  27. // there was packet loss that would render this packet unusable:
  28. if (fCurrentPacketBeginsFrame)
  29. {
  30. if (packetLossPrecededThis || fPacketLossInFragmentedFrame)
  31. {
  32. // We didn't get all of the previous frame.
  33. // Forget any data that we used from it:
  34. fTo = fSavedTo; fMaxSize = fSavedMaxSize;
  35. fFrameSize = 0;
  36. }
  37. fPacketLossInFragmentedFrame = False;
  38. else if (packetLossPrecededThis)
  39. {
  40. // We're in a multi-packet frame, with preceding packet loss
  41. fPacketLossInFragmentedFrame = True;
  42. }
  43. if (fPacketLossInFragmentedFrame)
  44. {
  45. // This packet is unusable; reject it:
  46. fReorderingBuffer->releaseUsedPacket(nextPacket);
  47. fNeedDelivery = True;
  48. break;
  49. }
  50. // The packet is usable. Deliver all or part of it to our caller:
  51. unsigned frameSize;
  52. //将上面取出的数据包拷贝到fTo指针所指向的地址
  53. nextPacket->use(fTo, fMaxSize, frameSize, fNumTruncatedBytes,
  54. fCurPacketRTPSeqNum, fCurPacketRTPTimestamp,
  55. fPresentationTime, fCurPacketHasBeenSynchronizedUsingRTCP,
  56. fCurPacketMarkerBit);
  57. fFrameSize += frameSize;
  58. if (!nextPacket->hasUsableData())
  59. {
  60. // We're completely done with this packet now
  61. fReorderingBuffer->releaseUsedPacket(nextPacket);
  62. }
  63. if (fCurrentPacketCompletesFrame) //如果完整的取出了一帧数据,则可调用需要该帧数据的函数去处理它
  64. {
  65. // We have all the data that the client wants.
  66. if (fNumTruncatedBytes > 0)
  67. {
  68. envir() << "MultiFramedRTPSource::doGetNextFrame1(): The total received frame size exceeds the client's buffer size ("
  69. << fSavedMaxSize << ").  "
  70. << fNumTruncatedBytes << " bytes of trailing data will be dropped!\n";
  71. }
  72. // Call our own 'after getting' function, so that the downstream object can consume the data:
  73. if (fReorderingBuffer->isEmpty())
  74. {
  75. // Common case optimization: There are no more queued incoming packets, so this code will not get
  76. // executed again without having first returned to the event loop.  Call our 'after getting' function
  77. // directly, because there's no risk of a long chain of recursion (and thus stack overflow):
  78. afterGetting(this);  //调用函数去处理取出的数据帧
  79. else
  80. {
  81. // Special case: Call our 'after getting' function via the event loop.
  82. nextTask() = envir().taskScheduler().scheduleDelayedTask(0,
  83. (TaskFunc*)FramedSource::afterGetting, this);
  84. }
  85. }
  86. else
  87. {
  88. // This packet contained fragmented data, and does not complete
  89. // the data that the client wants.  Keep getting data:
  90. fTo += frameSize; fMaxSize -= frameSize;
  91. fNeedDelivery = True;
  92. }
  93. }
  94. }

//下面这个函数即开始调用执行需要该帧数据的函数

[cpp] view plaincopy
  1. void FramedSource::afterGetting(FramedSource* source)
  2. {
  3. source->fIsCurrentlyAwaitingData = False;
  4. // indicates that we can be read again
  5. // Note that this needs to be done here, in case the "fAfterFunc"
  6. // called below tries to read another frame (which it usually will)
  7. if (source->fAfterGettingFunc != NULL)
[cpp] view plaincopy
  1. {
  2. (*(source->fAfterGettingFunc))(source->fAfterGettingClientData,
  3. source->fFrameSize, source->fNumTruncatedBytes,
  4. source->fPresentationTime,
  5. source->fDurationInMicroseconds);
  6. }
  7. }

上面的fAfterGettingFunc为我们自己注册的函数,如果运行的是testProgs中的openRTSP实例,则该函数指向下列代码中通过调用getNextFrame()注册的afterGettingFrame()函数

[cpp] view plaincopy
  1. Boolean FileSink::continuePlaying()
  2. {
  3. if (fSource == NULL) return False;
  4. fSource->getNextFrame(fBuffer, fBufferSize,
  5. afterGettingFrame, this,
  6. onSourceClosure, this);
  7. return True;
  8. }

如果运行的是testProgs中的testRTSPClient中的实例,则该函数指向这里注册的afterGettingFrame()函数

[cpp] view plaincopy
  1. Boolean DummySink::continuePlaying()
  2. {
  3. if (fSource == NULL) return False; // sanity check (should not happen)
  4. // Request the next frame of data from our input source.  "afterGettingFrame()" will get called later, when it arrives:
  5. fSource->getNextFrame(fReceiveBuffer, DUMMY_SINK_RECEIVE_BUFFER_SIZE,
  6. afterGettingFrame, this,
  7. onSourceClosure, this);
  8. return True;
  9. }

从上面的代码中可以看到getNextFrame()函数的第一个参数为分别在各自类中定义的buffer,我们继续以openRTSP为运行程序来分析,fBuffer为FileSink类里定义的指针:unsigned char* fBuffer;

这里我们先绕一个弯,看看getNextFrame()函数里做了什么

[cpp] view plaincopy
  1. void FramedSource::getNextFrame(unsigned char* to, unsigned maxSize,
  2. afterGettingFunc* afterGettingFunc,
  3. void* afterGettingClientData,
  4. onCloseFunc* onCloseFunc,
  5. void* onCloseClientData)
  6. {
  7. // Make sure we're not already being read:
  8. if (fIsCurrentlyAwaitingData)
  9. {
  10. envir() << "FramedSource[" << this << "]::getNextFrame(): attempting to read more than once at the same time!\n";
  11. envir().internalError();
  12. }
  13. fTo = to;
  14. fMaxSize = maxSize;
  15. fNumTruncatedBytes = 0; // by default; could be changed by doGetNextFrame()
  16. fDurationInMicroseconds = 0; // by default; could be changed by doGetNextFrame()
  17. fAfterGettingFunc = afterGettingFunc;
  18. fAfterGettingClientData = afterGettingClientData;
  19. fOnCloseFunc = onCloseFunc;
  20. fOnCloseClientData = onCloseClientData;
  21. fIsCurrentlyAwaitingData = True;
  22. doGetNextFrame();
  23. }

从代码可以知道上面getNextFrame()中传入的第一个参数fBuffer指向了指针fTo,而我们在前面分析代码1.1中的void MultiFramedRTPSource::doGetNextFrame1()函数中有下面一段代码:

[cpp] view plaincopy
  1. //将上面取出的数据包拷贝到fTo指针所指向的地址
  2. nextPacket->use(fTo, fMaxSize, frameSize, fNumTruncatedBytes,
  3. fCurPacketRTPSeqNum, fCurPacketRTPTimestamp,
  4. fPresentationTime, fCurPacketHasBeenSynchronizedUsingRTCP,
  5. fCurPacketMarkerBit);

实际上现在应该明白了,从getNextFrame()函数中传入的第一个参数fBuffer最终存储的即是从数据包链表对象中取出的数据,并且在调用上面的use()函数后就可以使用了。
而在void MultiFramedRTPSource::doGetNextFrame1()函数中代码显示的最终调用我们注册的void FileSink::afterGettingFrame()正好是在use()函数调用之后的afterGetting(this)中调用。我们再看看afterGettingFrame()做了什么处理:

[cpp] view plaincopy
  1. void FileSink::afterGettingFrame(void* clientData, unsigned frameSize,
  2. unsigned numTruncatedBytes,
  3. struct timeval presentationTime,
  4. unsigned /*durationInMicroseconds*/)
  5. {
  6. FileSink* sink = (FileSink*)clientData;
  7. sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime);
  8. }
  9. void FileSink::afterGettingFrame(unsigned frameSize,
  10. unsigned numTruncatedBytes,
  11. struct timeval presentationTime)
  12. {
  13. if (numTruncatedBytes > 0)
  14. {
  15. envir() << "FileSink::afterGettingFrame(): The input frame data was too large for our buffer size ("
  16. << fBufferSize << ").  "
  17. << numTruncatedBytes << " bytes of trailing data was dropped!  Correct this by increasing the \"bufferSize\" parameter in the \"createNew()\" call to at least "
  18. << fBufferSize + numTruncatedBytes << "\n";
  19. }
  20. addData(fBuffer, frameSize, presentationTime);
  21. if (fOutFid == NULL || fflush(fOutFid) == EOF)
  22. {
  23. // The output file has closed.  Handle this the same way as if the
  24. // input source had closed:
  25. onSourceClosure(this);
  26. stopPlaying();
  27. return;
  28. }
  29. if (fPerFrameFileNameBuffer != NULL)
  30. {
  31. if (fOutFid != NULL) { fclose(fOutFid); fOutFid = NULL; }
  32. }
  33. // Then try getting the next frame:
  34. continuePlaying();
  35. }

从上面代码可以看到调用了addData()函数将数据保存到文件中,然后继续continuePlaying()又去获取下一帧数据然后处理,直到遇到循环结束然后依次退出调用函数。最后看看addData()函数的实现即可知:

[cpp] view plaincopy
  1. void FileSink::addData(unsigned char const* data, unsigned dataSize,
  2. struct timeval presentationTime)
  3. {
  4. if (fPerFrameFileNameBuffer != NULL)
  5. {
  6. // Special case: Open a new file on-the-fly for this frame
  7. sprintf(fPerFrameFileNameBuffer, "%s-%lu.%06lu", fPerFrameFileNamePrefix,
  8. presentationTime.tv_sec, presentationTime.tv_usec);
  9. fOutFid = OpenOutputFile(envir(), fPerFrameFileNameBuffer);
  10. }
  11. // Write to our file:
  12. #ifdef TEST_LOSS
  13. static unsigned const framesPerPacket = 10;
  14. static unsigned const frameCount = 0;
  15. static Boolean const packetIsLost;
  16. if ((frameCount++)%framesPerPacket == 0)
  17. {
  18. packetIsLost = (our_random()%10 == 0); // simulate 10% packet loss #####
  19. }
  20. if (!packetIsLost)
  21. #endif
  22. if (fOutFid != NULL && data != NULL)
  23. {
  24. fwrite(data, 1, dataSize, fOutFid);
  25. }
  26. }

最后调用系统函数fwrite()实现写入文件功能。

总结:从上面的分析可知,如果要取得从RTSP服务器端接收并保存的数据帧,我们只需要定义一个类并实现如下格式两个的函数,并声明一个指针地址buffer用于指向数据帧,再在continuePlaying()函数中调用getNextFrame(buffer,...)即可。

[cpp] view plaincopy
  1. typedef void (afterGettingFunc)(void* clientData, unsigned frameSize,
  2. unsigned numTruncatedBytes,
  3. struct timeval presentationTime,
  4. unsigned durationInMicroseconds);
  5. typedef void (onCloseFunc)(void* clientData);

然后再在afterGettingFunc的函数中即可使用buffer。.

转载于:https://www.cnblogs.com/shakin/p/3913151.html

live555从RTSP服务器读取数据到使用接收到的数据流程分析相关推荐

  1. 探索C++与Live555实现RTSP服务器的艺术

    探索C++与Live555实现RTSP服务器的艺术 一.引言(Introduction) 1.1 RTSP服务器的重要性(Importance of RTSP Server) 1.2 C++与Live ...

  2. live555搭建rtsp服务器推送实时流花屏问题解决

    使用live555搭建rtsp服务器推送实时流时总是出现花屏,查阅资料按照大多数人所说的修改做了以下几项修改: 1.修改OutPacketBuffer::maxSize=1024*1024 ; 2.扩 ...

  3. STM32单片机串口空闲中断+DMA接收不定长数据

    在上一篇文章STM32单片机串口空闲中断接收不定长数据中介绍了利用串口空闲中断接收不定长数据,这种方式有一个问题就是串口每接收到一个字节就会进入一次中断,如果发送的数据比较频繁,那么串口中断就会不停打 ...

  4. STM32单片机串口空闲中断接收不定长数据

    在使用单片机的串口通信功能时,常用的接收数据方法是通过固定的字节数来判断一帧数是否发送完成,或者是通过固定的结束标志位来表示一帧数据发送完成.但是有时候会遇到发送的数据长度不固定,也没有固定的结束标志 ...

  5. MM32F3277空闲中断+DMA接收不定长数据

    摘要:在实际项目中经常用到串口接收一些不定长的数据,怎么判断这一帧数据接收完成了呢?通常使用UART非空中断配合简单的数据协议,在数据中加入帧头.帧尾,在程序中判断是否接收到帧尾来确定数据接收完毕,对 ...

  6. CC1101丢包和工作一段时间,接收不到数据的问题

    目录 ReadMe: 问题: 背景描述: 发送端程序: 接收端程序: 问题研究过程: 最终程序: 发送端程序: 接收端程序: ReadMe: "问题研究过程"为我调程序中遇到问题, ...

  7. LIVE555再学习 -- live555实现RTSP直播服务器 分析

    上一篇文章 讲到了 live555实现RTSP直播服务器,但是篇幅有点长,没有来得及对源码进行分析. 这篇文章就好好看看,源码部分这次参看Linux版本下的 通过live555实现H264 RTSP直 ...

  8. LIVE555再学习 -- live555实现RTSP直播服务器

    分析完 testOnDemandRTSPServer 和 testH264VideoStreamer 的源码.我们现在就可以做相关的项目工程. 我之前写过一个,参看: DM368开发 -- 编码并实时 ...

  9. RTSP服务器实例live555源代码分析

    1. RTSP连接的建立过程      RTSPServer类用于构建一个RTSP服务器,该类同时在其内部定义了一个RTSPClientSession类,用于处理单独的客户会话.      首先创建R ...

最新文章

  1. java的异常与记录日志
  2. “完全自主”的木兰编程语言回应:承认基于Python二次开发,向中科院致歉
  3. AO 直接调用GeoProcessing 工具
  4. Python编程基础02:Python基本语法
  5. pythonsocket中tcp通信接收不到数据_简单说说Python Socket编程步骤?
  6. Python爬虫之JS逆向分析技巧
  7. 帆软报表嵌入python程序_C#教程之C#服务器端生成报告文档:使用帆软报表
  8. py-faster-rcnn标注FDDB人脸便于其在FDDB上进行测试
  9. 单语种语料库 平行语料库 多语种语料库 可比语料库
  10. JS中实现继承的几种方式
  11. eclipse护眼颜色
  12. Sample Science 909 Lab for Mac - 909鼓声虚拟乐器
  13. 若依集成minio实现分布式文件存储
  14. 隧道调频广播覆盖系统技术方案
  15. 一篇论文摘要计算机英语,计算机毕业论文英文摘要的写作方法.doc
  16. 这是一个全民销售的时代
  17. Linux的常用命令思维导图
  18. (转)FLASH技术分享
  19. Get和Post请求参数格式
  20. 花生壳绑定域名和动态ip访问本机服务。

热门文章

  1. mstsc VS vnc : 远程桌面最快的原因在于RDP协议
  2. 怎样冷静地看待“小米智能新品”?
  3. Neutron:Firewall as a Service(FWaaS)
  4. Git(一)之基本操作详解
  5. 三层交换机----VRRP协议学习
  6. 数据仓库自动抽取:通过 SQL Server 企业管理器中的数据转换服务 (DTS) 设计器 创建 Analysis Services 处理任务...
  7. OCP 12c最新考试原题及答案(071-3)
  8. svn命令行 批量添加(add)所有新增文件
  9. 调用webservice接口,数据不回滚问题
  10. 记一次MySQL字符集冲突导致的报错