关于开源的RTP——jrtplib的使用

来源:互联网 发布:php 500错误如何排查 编辑:程序博客网 时间:2024/06/15 21:54

关于开源的RTP——jrtplib的使用

标签: nulldeletesocketreportstructwindows
9639人阅读 评论(2)收藏举报
本文章已收录于:
分类:
作者同类文章X

    [c-sharp] view plain copy
    print?
    1. session.BeginDataAccess();     
    2. if (session.GotoFirstSource()){     
    3. do{     
    4.      RTPPacket *packet;     
    5. while ((packet = session.GetNextPacket()) != 0){     
    6.             cout << "Got packet with extended sequence number "    
    7.                   << packet->GetExtendedSequenceNumber()     
    8.                  << " from SSRC " << packet->GetSSRC() <<endl;     
    9.               session.DeletePacket(packet);     
    10. }     
    11.      } while (session.GotoNextSource());     
    12.    }     
    13.    session.EndDataAccess();    

    关于jrtplib环境的建立,可以见我以前写的总结,现在我主要来谈谈jrtplib3.71下的几个example的学习。 一、sample是一个简单的IPv4的列子,它实现了RTP在本机上的数据的传输。 1、初始化。我们知道RTP是通常是使用UDP协议来实现数据的传输,在windows环境下,当然要用到我们熟悉的套接字的使用,所以我们先要进行初始化,加载套接字库。

    函数RTPGetErrorString(…)根据出错返回的一个负数来判断,利用jrtplib采用的统一出错机制返回的是一个C++中标准的字符串std::string,表示出错的信息,比喻我们指定的Portbase不是一个偶数。(为什么,以下要讲)
    2、关于RTPSession对象设置
       由于要使用套接字,故我们在使用之前一定要为我们的监听socket指定一个监听端口,也就是这里的portbase的值。我们可以通过调用RTPSession的第二个参数RTPUDPv4TransmissionParams的成员函数来设定,
       transparams.SetPortbase(portbase);
     
       还有就是我们要通过RTPSession的第一个参数来设定以下一些值:
      
    sessparams.SetOwnTimestampUnit(1.0/10.0);
    //注意这个值我们一定要设置,The local timestamp unit MUST be set, otherwise
    //RTCP Sender Report info will be calculated wrong, In this case, we'll be sending
    //10 samples each second, so we'll put the timestamp unit to (1.0/10.0)     
    //设置时间戳是一件很重要的事情,是RTP会话初始化过程所要进行的另外一项重要工
    //作,他的单位是秒,如:当使用RTP会话传输8000Hz采样的音频数据时,由于时戳
    //每秒钟将递增8000,所以时戳单元相应地应该被设置成1/8000:
    sessparams.SetAcceptOwnPackets(true);
    //通过这个函数我们可以设置是不是接收我们自定义的数据包。
     
    3、数据发送
       我想当我们要想建立连接的时候,我们就要让发送端知道要发送到的主机的IP地址,在jrtplib中我们可以通过RTPSession成员函数AddDestination()、DeleteDestination()和 ClearDestinations()来完成。如下面是将数据发送给本机的6000号端口:
     
    unsigned long addr = ntohl(inet_addr("127.0.0.1"));
    sess.AddDestination(addr, 6000);
     
    当然我们还可以这样来完成加入一个客户端,
     
    RTPIPv4Address addr(destip,destport);//destip为客户端IP地址,destport为客户端//端口号
    status = sess.AddDestination(addr);
    checkerror(status);  
      
       目标地址全部指定之后,接着就可以调用RTPSession类的SendPacket()方法,向所有的目标地址发送流媒体数据。SendPacket()是RTPSession类提供的一个重载函数,它具有下列多种形式:
    int SendPacket(void *data,int len)
    int SendPacket(void *data,int len,unsigned char pt,bool mark,
    unsigned long timestampinc)
    int SendPacket(void *data,int len,unsigned short hdrextID,void *hdrextdata,
    int numhdrextwords)
    int SendPacket(void *data,int len,unsigned char pt,bool mark,
    unsigned long timestampinc,unsigned short hdrextID,
    void *hdrextdata,int numhdrextwords)
    SendPacket()最典型的用法是类似于下面的语句,其中第一个参数是要被发送的数据,而第二个参数则指明将要发送数据的长度,再往后依次是RTP负载类型、标识和时戳增量。如下所示:
    status = sess.SendPacket((void *)"1234567890",10,0,false,10);
    checkerror(status);
    对于同一个RTP会话来讲,负载类型、标识和时戳增量通常来讲都是相同的,JRTPLIB允许将它们设置为会话的默认参数,这是通过调用 RTPSession类的SetDefaultPayloadType()、SetDefaultMark()和 SetDefaultTimeStampIncrement()方法来完成的。为RTP会话设置这些默认参数的好处是可以简化数据的发送,例如,如果为 RTP会话设置了默认参数:
    session.SetDefaultPayloadType(96);//注意这个参数不能随便设置,参考RFC3551
    session.SetDefaultMark(false);
    session.SetDefaultTimestampIncrement(160);
    在设置了以上的值后,我们可以这样来发送数据:
    status = sess.SendPacket((void *)"1234567890",10);
    3、数据接收
    对于流媒体数据的接收端,首先需要调用RTPSession类的PollData()方法来接收发送过来的RTP或者RTCP数据报。由于同一个 RTP会话中允许有多个参与者(源),你既可以通过调用RTPSession类的GotoFirstSource()和GotoNextSource() 方法来遍历所有的源,也可以通过调用RTPSession类的GotoFirstSourceWithData()和 GotoNextSourceWithData()方法来遍历那些携带有数据的源。在从RTP会话中检测出有效的数据源之后,接下去就可以调用 RTPSession类的GetNextPacket()方法从中抽取RTP数据报,当接收到的RTP数据报处理完之后,一定要记得及时释放。下面的代码示范了该如何对接收到的RTP数据报进行处理:

     

     

    [c-sharp] view plain copy
    print?
    1. #ifdef WIN32     
    2. WSADATA dat;  
    3. WSAStartup(MAKEWORD(2,2),&dat);  
    4. #endif // WIN32     
    5. RTPSession sess;  
    6.  
    7. #ifdef WIN32     
    8.         WSACleanup();  
    9. #endif // WIN32     
    10.  
    11. #include "rtpsession.h"  
    12. #include "rtppacket.h"  
    13. #include "rtpudpv4transmitter.h"  
    14. //RTPUDPv4TransmissionParams     
    15. #include "rtpipv4address.h"  
    16. #include "rtpsessionparams.h"  
    17. #include "rtperrors.h"  
    18.   
    19. void checkerror(int rtperr)  
    20. {  
    21.         if (rtperr < 0)  
    22.         {  
    23.                 std::cout << "ERROR: " << RTPGetErrorString(rtperr) << std::endl;  
    24.                 exit(-1);  
    25.         }  
    26. }  

     

    JRTPLIB为RTP数据报定义了三种接收模式,其中每种接收模式都具体规定了哪些到达的RTP数据报将会被接受,而哪些到达的RTP数据报将会被拒绝。通过调用RTPSession类的SetReceiveMode()方法可以设置下列这些接收模式:
    ① RECEIVEMODE_ALL 缺省的接收模式,所有到达的RTP数据报都将被接受;
    ② RECEIVEMODE_IGNORESOME  除了某些特定的发送者之外,所有到达的RTP数据报都将被接受,而被拒绝的发送者列表可以通过调用AddToIgnoreList()、ClearIgnoreList()     和DeleteFromIgnoreList()方法来进行设置;
    ③ RECEIVEMODE_ACCEPTSOME  除了某些特定的发送者之外,所有到达的RTP数据报都将被拒绝,而被接受的发送者列表可以通过调用AddToAcceptList ()、ClearAcceptList ()和eleteFromAcceptList方法来进行设置。
    4、运行程序
       在运行程序之前,我们要知道,这是基于UDP协议的数据报服务。我们的example1是在本机上实现的RTP传输,并且只有一个RTPSession对象,所以我们的监听和接收的端口是同一个,所以此时我们的portBase要和我们加入到RTPSession对象中的客户端的端口一致,如上面我们可以都设置成6000.但是如果是在两个线程中,我们就要注意了,服务器端口和客户端口一定不一样,这也是基本的知识,用的时候要注意,多思考。

     

    [c-sharp] view plain copy
    print?
    1. 1.设置基本参数       
    2. RTPSession   m_RTPSocket;                 
    3. m_RTPSocket.Create(m_SPort);   
    4. m_RTPSocket.SetTimestampUnit(1.0/25.0);   
    5. m_RTPSocket.SetSessionBandwidth(900.0);   
    6. m_RTPSocket.SetMaxPacketSize(65535);   
    7. m_RTPSocket.SetDefaultPayloadType(0);   
    8. m_RTPSocket.SetDefaultMark(false);   
    9. m_RTPSocket.SetDefaultTimeStampIncrement(10);   
    10. 2.发送   
    11. int   status   = m_RTPSocket.SendPacket(pFrame,wSize,1,name,10);   
    12. 3,接收   
    13. int   size   =   0;   
    14. SOCKET   sockRtp,sockRtcp;   
    15. struct   timeval   rttprev   =   {0,0},rtt,tv;   
    16. fd_set   fdset;   
    17. bool   frist   =   true;   
    18. int   port   =   ((CRNetVideoCtrl*)m_pMainClass)-> m_destport;   
    19. if(port   ==   -1)   
    20. StopThread();   
    21. RTPSession   m_RTPSocket;   
    22. m_RTPSocket.Create(port);   
    23. m_RTPSocket.SetSessionBandwidth(64);   
    24.   
    25. m_RTPSocket.GetRTPSocket(&sockRtp);   
    26. m_RTPSocket.GetRTCPSocket(&sockRtcp);   
    27.   
    28. m_pDecoder   =   new   CVideoDecode();   
    29. m_pDecoder-> dec_init(1);     
    30.   
    31. unsigned   char*   pBuffer   =   new   unsigned   char   [320*240*2];   
    32.   
    33. while(!m_bClose){   
    34. tv.tv_sec   =   0;   
    35. tv.tv_usec   =   100000;   
    36. FD_ZERO(&fdset);   
    37. FD_SET(sockRtcp,&fdset);   
    38. FD_SET(sockRtp,&fdset);   
    39. int   maxSize   =   max(sockRtp,   sockRtcp);   
    40. int   selectRet   =   select(maxSize   +   1,&fdset,NULL,NULL,&tv);   
    41. if   (-1   ==   selectRet   ||   0   ==   selectRet)   
    42. continue;   
    43. /*   poll   for   incoming   data   *///调用这个,这样才能够获得数据   
    44. m_RTPSocket.PollData();   
    45. /*   check   incoming   packets   *///原来是rtp数据包   
    46. if   (FD_ISSET(sockRtp,   &fdset)){   
    47.   
    48. if   (m_RTPSocket.GotoFirstSourceWithData()){   
    49. RTPPacket   *pack;   
    50. while   ((pack   =   m_RTPSocket.GetNextPacket())   !=   NULL){   
    51. if(frist){   
    52. //检查是否是关键帧   
    53. if(pack-> IsMarked()){   
    54. if((CRNetVideoCtrl*)m_pMainClass){   
    55. m_pDecoder-> dec_main(pack-> GetPayload(),pBuffer,pack-> GetPayloadLength(),&size);   
    56. ((CRNetVideoCtrl*)m_pMainClass)-> DecodeVideo(pBuffer,size);   
    57. TRACE( "Client   Received   IFrame/n ");   
    58. frist   =   false;   
    59. }   
    60. }   
    61. }   
    62. else{   
    63. //解码并显示   
    64. m_pDecoder-> dec_main(pack-> GetPayload(),pBuffer,pack-> GetPayloadLength(),&size);   
    65. ((CRNetVideoCtrl*)m_pMainClass)-> DecodeVideo(pBuffer,size);   
    66. }   
    67. delete   pack;   
    68. }   
    69. }   
    70. }   
    71. if   (FD_ISSET(sockRtcp,   &fdset)){   
    72. if   (m_RTPSocket.GotoFirstSource())   
    73. {   
    74. do   
    75. {   
    76. RTPSourceData   *srcdat;   
    77. srcdat   =   m_RTPSocket.GetCurrentSourceInfo();   
    78. if   (NULL   !=   srcdat)   
    79. {   
    80. rtt   =   srcdat-> INF_GetRoundTripTime();   
    81. if   (rtt.tv_sec   !=   0   ||   rtt.tv_usec   !=   0)   
    82. {   
    83. double   t;   
    84. t   =   (double)rtt.tv_sec;   
    85. t   +=   ((double)rtt.tv_usec)/1000000.0;   
    86. t   *=   1000.0;   //   we   want   milliseconds;   
    87. TRACE( "rtt:   %f   ms/n ",   (float)t);   
    88. rttprev   =   rtt;   
    89. }   
    90. if   (parent-> m_canSend)   
    91. {   
    92. sprintf(msg,   "丢包:   %f/n ",   srcdat-> RR_GetPacketsLost());   
    93. sprintf(msg,   "包序号:   %d/n ",   srcdat-> RR_GetExtendedHighestSequenceNumber());   
    94. sprintf(msg,   "jitter:   %d/n ",   srcdat-> RR_GetJitter());   
    95. reportNum   =   0;   
    96. }   
    97. srcdat-> FlushPackets();   //   we   don 't   need   the   actual   data   
    98. }   
    99. srcdat   =   NULL;   
    100. }   while   (m_RTPSocket.GotoNextSource());   
    101. }   
    102.   
    103. }   
    104.   
    105. }   
    106.   
    107. if(m_pDecoder)   
    108. delete   m_pDecoder;   
    109. m_pDecoder   =   NULL;   
    110.   
    111. delete   []pBuffer;   
    112.   
    113. return   1L;  

     

     

    0
    0
     
     

      相关文章推荐
    • 关于开源的RTP——jrtplib的使用(2)
    • 开源RTP库JRTPLIB 初学
    • 关于开源的RTP——jrtplib的使用
    • Windows下编译jrtplib.lib和jthread.lib
    • 视频会议及流媒体十大开源项目
    • jrtplib库的使用
    • 自己动手写RTP服务器——用RTP协议传输TS流
    • 使用jrtplib(RTP)传输H.264视频文件(2)
    • VC 6 RTP流媒体传输协议编程实例(jrtplib)
    • RTP/RTCP工程实践与问题解决方案(合集)
    原创粉丝点击