live555 client 接收rtp数据

2014-03-08  22:05:58  

描述live555 client即openRTSP的流程,简单点说,playCommon.cpp,流为h264和g726。在实际项目中已成功应用。

以下为我所见所得,有错误之处请指正,谢谢!

1、live555的三种任务

socket handler,event handler,delay task。

这三种任务的特点是,前两个加入执行队列后会一直存在,而delay task在执行完一次后会立即弃掉。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

/*** socket handler ***/

//定义

// For handling socket operations in the background (from the event loop):

typedef void BackgroundHandlerProc(void* clientData, int mask);

//注册

void BasicTaskScheduler

  ::setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData) {}

//执行

BasicTaskScheduler::SingleStep(unsigned maxDelayTime)

{

  (*handler->handlerProc)(handler->clientData, resultConditionSet);

}

/*** event handler ***/

//定义

typedef void TaskFunc(void* clientData);

//注册

EventTriggerId EventTriggerId BasicTaskScheduler0

::createEventTrigger(TaskFunc* eventHandlerProc) {}

//执行

BasicTaskScheduler::SingleStep(unsigned maxDelayTime)

{

    (*fTriggeredEventHandlers[i])(fTriggeredEventClientDatas[i]);

}

/*** delay task ***/

//定义

typedef void TaskFunc(void* clientData);//跟event handler一样。

//注册

TaskToken BasicTaskScheduler0::

scheduleDelayedTask(int64_t microseconds,TaskFunc* proc, void* clientData) {}

//执行

BasicTaskScheduler::SingleStep(unsigned maxDelayTime)

{

  fDelayQueue.handleAlarm();

}

void DelayQueue::handleAlarm()

{

    if (head()->fDeltaTimeRemaining != DELAY_ZERO) synchronize();

    if (head()->fDeltaTimeRemaining == DELAY_ZERO)

    {

        // This event is due to be handled:

        DelayQueueEntry* toRemove = head();

        removeEntry(toRemove); // do this first, in case handler accesses queue

        toRemove->handleTimeout();   //仅执行一次后就remove

    }

}

2、rtsp交互

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

//OPTIONS--->DESCRIBE--->SETUP--->PLAY,这是最通用的交互了。

getOptions()--->continueAfterOPTIONS()--->

getSDPDescription()--->continueAfterDESCRIBE()

{

    session = MediaSession::createNew(*env, sdpDescription);

    while()

    {

        //音视频子会话

        subsession->initiate();

    }

    setupStreams();

}

--->

//setupStreams为递归函数(setupStreams-->continueAfterSETUP-->setupStreams)

//setupSubsession所有的子会话

setupStreams()

{

    while()

    {

        setupSubsession(subsession, streamUsingTCP, forceMulticastOnUnspecified, continueAfterSETUP);

    }

    startPlayingSession(session, initialSeekTime, endTime, scale, continueAfterPLAY);

}

3、以getOptions举例

getOptions(continueAfterOPTIONS),getOptions后怎么调用到continueAfterOPTIONS的,如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

//responseHandler* afterFunc 都由handler() 执行

getOptions(continueAfterOPTIONS)--->sendOptionsCommand()--->sendRequest()

{

--->openConnection()

{

    --->connectToServer()

    {

        setBackgroundHandling(,SOCKET_WRITABLE|SOCKET_EXCEPTION,connectionHandler,);

    }

    //连接server ok

    {

        setBackgroundHandling(,SOCKET_READABLE|SOCKET_EXCEPTION,incomingDataHandler,);

    }

}

if (connectionIsPending) {

      fRequestsAwaitingConnection.enqueue(request);

      return request->cseq();

    }

}

--->doEventLoop--->SingleStep()

{

    //socket状态符合,就执行注册好的函数,例如connectionHandler/incomingDataHandler等

    (*handler->handlerProc)(handler->clientData, resultConditionSet);

}

SingleStep()    //1th step,执行connectionHandler,SOCKET_WRITABLE

{

    handler->handlerProc = connectionHandler;

}

SingleStep()    //2th step,执行incomingDataHandler,SOCKET_READABLE

{

    handler->handlerProc = incomingDataHandler;

}

//incomingDataHandler会调用到continueAfterOPTIONS

void RTSPClient::incomingDataHandler(void* instance, int /*mask*/) {

  RTSPClient* client = (RTSPClient*)instance;

  client->incomingDataHandler1();

}

void RTSPClient::incomingDataHandler1() {

  struct sockaddr_in dummy; // ‘from‘ address - not used

  int bytesRead = readSocket(envir(), fInputSocketNum, (unsigned char*)&fResponseBuffer[fResponseBytesAlreadySeen], fResponseBufferBytesLeft, dummy);

  handleResponseBytes(bytesRead)

  {

    //call continueAfterOPTIONS() ,etc.

    (*foundRequest->handler())(this, resultCode, resultString);

  }

}

4、client get rtp_packet

●  先从setupStreams先入手吧


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

void setupStreams()

{

    createOutputFiles()

    {

        while ((subsession = iter.next()) != NULL)

        {

            //h264

            fileSink = H264VideoFileSink::createNew(*env, outFileName,

                                subsession->fmtp_spropparametersets(),

                                fileSinkBufferSize, oneFilePerFrame);

            //g726

            // Normal case:

            fileSink = FileSink::createNew(*env, outFileName,

                               fileSinkBufferSize, oneFilePerFrame);

            

            subsession->sink->startPlaying(*(subsession->readSource()),

                               subsessionAfterPlaying,

                               subsession);

        }

    }

}

//------->

Boolean MediaSink::startPlaying(MediaSource& source,

                afterPlayingFunc* afterFunc,

                void* afterClientData)

  fSource = (FramedSource*)&source;

  fAfterFunc = afterFunc;

  fAfterClientData = afterClientData;

  return continuePlaying(); 

}

//------->

Boolean FileSink::continuePlaying()

{

  if (fSource == NULL) return False;

  fSource->getNextFrame(fBuffer, fBufferSize,

            afterGettingFrame, this,

            onSourceClosure, this);

  return True;

}

●  再从FileSink::continuePlaying入手

FileSink::continuePlaying()

FramedSource::getNextFrame()

MultiFramedRTPSource::doGetNextFrame()

MultiFramedRTPSource::doGetNextFrame1()

//以下::仅表示static func声明所在的类

static void FramedSource::afterGetting(FramedSource* source);

static void FileSink::afterGettingFrame(void* clientData, unsigned frameSize,unsigned numTruncatedBytes,

struct timeval presentationTime,unsigned durationInMicroseconds);

  • MultiFramedRTPSource::doGetNextFrame1()

是递归函数,退出条件为


1

2

3

4

5

6

7

while (fNeedDelivery) //正常测试接收时,fNeedDelivery == 1

{

    if (nextPacket == NULL)

    {

       break;

    }

}

(Enter->Exit):即时

(Enter,) :和最近的(,Exit)配对

(,Exit):和最近的(Enter,)配对

static afterGetting::nth(Enter,) <--->static afterGetting::n+1th(,Exit)

从第一次调用continuePlaying()跟踪。可以直接跳到3th。

4.1 step1th

continuePlaying()--->getNextFrame()--->doGetNextFrame()--->

doGetNextFrame1(Enter->Exit[nextPacket == NULL])--->......自己可以trace--->

startPlayingSession()--->setupStreams()--->SingleStep()::1th--->

networkReadHandler1(Enter,)--->doGetNextFrame1(Enter,)--->

static afterGetting(Enter,)--->static afterGettingFrame(Enter,)--->H264or5VideoFileSink::afterGettingFrame()-->

FileSink::afterGettingFrame(){addData();continuePlaying()}-->

4.2 step2th

continuePlaying()-->getNextFrame()--->doGetNextFrame()--->

doGetNextFrame1(Enter->Exit[nextPacket == NULL])--->static afterGettingFrame(,Exit)--->static afterGetting(,Exit)--->

doGetNextFrame1(,Exit[nextPacket == NULL])--->networkReadHandler1(,Eixt)--->

SingleStep()::2th--->incomingReportHandler1()--->

SingleStep()::3th--->incomingDataHandler1()--->continueAfterPLAY()--->

SingleStep()::4th--->

networkReadHandler1(Enter,)--->doGetNextFrame1(Enter,)--->

static afterGetting(Enter,)--->static afterGettingFrame(Enter,)--->H264or5VideoFileSink::afterGettingFrame()-->

FileSink::afterGettingFrame(){addData();continuePlaying()}-->

//上述1/2th还有rtsp交互和一些初始化以及h264 SPS的处理,从3th就纯粹了。

//当接收到一帧完整的帧才会调用static afterGetting()

//FileSink::afterGettingFrame(){addData();continuePlaying()}

//addData():保存一帧h264到文件。continuePlaying()继续干活吧。

4.3 step3th

continuePlaying()-->getNextFrame()--->doGetNextFrame()--->

doGetNextFrame1(Enter->Exit[nextPacket == NULL])--->static afterGettingFrame(,Exit)--->static afterGetting(,Exit)--->

doGetNextFrame1(,Exit[nextPacket == NULL])--->networkReadHandler1(,Eixt)--->

SingleStep()::5th--->

networkReadHandler1(Enter,)--->doGetNextFrame1(Enter,)--->

static afterGetting(Enter,)--->static afterGettingFrame(Enter,)--->H264or5VideoFileSink::afterGettingFrame()-->

FileSink::afterGettingFrame(){addData();continuePlaying()}-->

?4.4 step4?th

continuePlaying()-->getNextFrame()--->doGetNextFrame()--->

doGetNextFrame1(Enter->Exit[nextPacket == NULL])--->static afterGettingFrame(,Exit)--->static afterGetting(,Exit)--->

doGetNextFrame1( , Exit[nextPacket == NULL])--->networkReadHandler1(,Eixt)--->

//以上为针对3th的Exit结合networkReadHandler1::3th(Enter,)和networkReadHandler1::4th(,Eixt)可以看出doGetNextFrame1为递归函数。

//networkReadHandler1--->doGetNextFrame1--->static afterGetting--->continuePlaying()--->doGetNextFrame1

// SingleStep()生生不息,networkReadHandler1()进进出出

SingleStep()::6th--->

networkReadHandler1(Enter,)--->doGetNextFrame1(Enter,)--->

static afterGetting(Enter,)--->static afterGettingFrame(Enter,)--->H264or5VideoFileSink::afterGettingFrame()-->

FileSink::afterGettingFrame(){addData();continuePlaying()}-->

//nth :multi-packet frame,会有多次SingleStep(),但不会调用static afterGetting()

//只有在收完所有的sliece,即一个完整的frame后,才会调用static afterGetting()

continuePlaying()-->getNextFrame()--->doGetNextFrame()--->

doGetNextFrame1(Enter->Exit[nextPacket == NULL])--->static afterGettingFrame(,Exit)--->static afterGetting(,Exit)--->

doGetNextFrame1(,Exit[nextPacket == NULL])--->networkReadHandler1(,Eixt)--->

SingleStep()::nth--->

networkReadHandler1(Enter,)--->doGetNextFrame1(Enter,Exit)--->

networkReadHandler1(,Exit)--->

SingleStep()::n+1th--->

networkReadHandler1(Enter,)--->doGetNextFrame1(Enter,Exit)--->

networkReadHandler1(,Exit)--->

。。。。。。。。。。。。。

//最后一个packet

SingleStep()::n+mth--->

networkReadHandler1(Enter,)--->doGetNextFrame1(Enter,)--->

//此处才会调用static afterGetting

static afterGetting(Enter,)--->static afterGettingFrame(Enter,)--->H264or5VideoFileSink::afterGettingFrame()-->

FileSink::afterGettingFrame(){addData();continuePlaying()}-->

//n+1th

continuePlaying()-->getNextFrame()--->doGetNextFrame()--->

doGetNextFrame1(Enter->Exit[nextPacket == NULL])--->static afterGettingFrame(,Exit)--->static afterGetting(,Exit)--->?

doGetNextFrame1(,Exit[nextPacket == NULL])--->networkReadHandler1(,Eixt)--->

SingleStep()::n+m+1th--->

networkReadHandler1(Enter,)--->doGetNextFrame1(Enter,Exit)--->

networkReadHandler1(,Exit)--->

。。。。。。。。。。。。。。。。

来自为知笔记(Wiz)

时间: 2024-08-25 16:14:59

live555 client 接收rtp数据的相关文章

vlc源码分析之调用live555接收RTSP数据

首先了解RTSP/RTP/RTCP相关概念,尤其是了解RTP协议:RTP与RTCP协议介绍(转载). vlc使用模块加载机制调用live555,调用live555的文件是live555.cpp. 一.几个重要的类 以下向左箭头("<-")为继承关系. 1. RTPInterface RTPInterface是RTPSource的成员变量,其成员函数handleRead会读取网络数据存入BufferedPacket内,该类最终会调到UDP的发送接收函数. Boolean RTPIn

live555 播放视频 play 不能推送rtp数据

在项目开发过程中遇到一个问题,play之后,不能推送rtp数据包,跟踪代码调试发现,在获取H264视频数据,封包,发送,这条循环的链断开了,导致该问题的原因是: 在H264VideoStreamFramer.cpp中 unsigned H264VideoStreamParser::parse() { #if DEBUG_SHOWCHN printf("Parser() chn:%d\n", fchn); #endif try{ // The stream must start with

(转)live555 RTSP Server RTP over TCP BUG

最近碰到一个非常棘手的问题,NVR通过ONVIF协议接入IPC进行录像,在录像时,会发现其中有个别IPC会出现录像断断续续的情况.这种情况很难复现,但是这种情况一旦出现,整个过程会一直持续很长时间,一般是直到重启RTSP Server. 通过苦逼型的大规模测试发现: 1.IPC与NVR之间是通过RTP over TCP的方式传输数据(这个测试结果很简单就可以知道): 2.开启1个客户端(通过RTP over TCP传输数据),打开rtsp流后,使用任务管理器强制结束.紧接着马上再开1个客户端,打

live555 RTSP Server RTP over TCP BUG

最近碰到一个非常棘手的问题,NVR通过ONVIF协议接入IPC进行录像,在录像时,会发现其中有个别IPC会出现录像断断续续的情况.这种情况很难复现,但是这种情况一旦出现,整个过程会一直持续很长时间,一般是直到重启RTSP Server. 通过苦逼型的大规模测试发现: 1.IPC与NVR之间是通过RTP over TCP的方式传输数据(这个测试结果很简单就可以知道): 2.开启1个客户端(通过RTP over TCP传输数据),打开rtsp流后,使用任务管理器强制结束.紧接着马上再开1个客户端,打

【python】-- Socket接收大数据

Socket接收大数据 上一篇博客,就是说当服务器发送至客户端的数据,大于客户端设置的数据,则就会把数据服务端发过来的数据剩余数据存在IO缓冲区中,这样就会造成我们想要获取数据的完整性. 解决思路: 1.改大客户端接收的数据的大小,因为官方建议最多只能接收8k的数据,那服务端发送过来的数据很容易就会大于8K,这个思路并不能从根本上解决问题(不建议使用) 2.客户端可以多收几次,服务端给客户端发数据之前,先计算一下要发给客户端数据大小(len()判断文件长度) ,比如说要发给客户端数据是5k大小,

DE2-115 以太网通信之一88E1111网卡接收PC数据

想利用手头上的DE2-115 写一个关于以太网通信的驱动,经过了这么多天的实验调试终于有了一些认识. 1.我在观察网卡发送数据与接收数据的过程中发现,我从fpga上的一个网卡发送数据,然后另一个网卡接收数据,接收到的数据前面会有55h这8bit的数据.我从PC上发送数据,用fpga上的网卡接收数据,那么在接收到的数据前面会有55h,55h,55h,55h,55h,55h,55h,5dh这64bit的数据.那么如果55h这8bit数据是PHY发送时自动添加那么从PC上接收到的最后应该是55而不应该

C#串口通信—向串口发送数据,同步接收返回数据

最近写C#串口通信程序,系统是B/S架构.SerialPort类有一个DataReceived事件,用来接收串口返回的数据,但这种方式在C/S架构下很好用,但B/S就不好处理了.所以写了一个同步模式接收返回数据的方法,不使用DataReceived事件.经过测试,可以正常使用(不支持多线程调用). 一.Machine类 1.Machine类有一个静态变量,定义如下: private static SerialPort serialPort = null; 2.向串口发送数据,同步接收返回数据的方

用普通IO接收串口数据

<pre name="code" class="cpp">//文件urece.h #ifndef _URECE_H_ #define _URECE_H_ #define V_BATOU 0x80 //电池充满 #define V_BATLV 0x40 //电池低电压 #define V_BATOI 0X20 //电池放电过流 #define V_BATOTP 0x10 //电池过温 #define V_BATOTIM 0x08 //电池充电超时 #def

Struts2初学 Struts2的action接收用户数据方式

一.简介    开发Web应用程序,首先应会遇到对用户输入数据的接收,传统的Web应用程序是由开发人员调用HttpServletRequest的getparameter(String name)方法从请求中获取数据,而Web框架都提供了数据绑定机制,由框架从请求中获取数据然后绑定到一个JavaBean对象中. Struts2提供了多种方式用于接收用户输入的数据. Struts2的action完全与Web解耦,要获取Web层的数据,需要使用ActionContext,它为action提供了一个执行