心胸决定格局,眼界决定境界...

JRTPLIB使用实例[转]

linux 下基于jrtplib库的实时传送实现
一、RTP 是进行实时流媒体传输的标准协议和关键技术
实时传输协议(Real-time Transport Protocol,PRT)是在 Internet 上处理多媒体数据流的一种网络协议

,利用它能够在一对一(unicast,单播)或者一对多(multicast,多播)的网络环境中实现传流媒体数据的

实时传输。RTP 通常使用 UDP 来进行多媒体数据的传输,但如果需要的话可以使用 TCP 或者 ATM 等其它协

议。

协议分析 :每一个RTP数据报都由头部(Header)和负载(Payload)两个部分组成,其中头部前 12 个字节

的含义是固定的,而负载则可以是音频或者视频数据。

     RTP 是目前解决流媒体实时传输问题的最好办法,要在 Linux 平台上进行实时传送编程,可以考虑使用

一些开放源代码的 RTP 库,如 LIBRTP、JRTPLIB 等。JRTPLIB 是一个面向对象的 RTP 库,它完全遵循 RFC

1889 设计,在很多场合下是一个非常不错的选择。JRTPLIB 是一个用 C++ 语言实现的 RTP 库,这个库使用

socket 机制实现网络通讯 因此可以运行在 Windows、Linux、FreeBSD、Solaris、Unix和VxWorks 等多种操

作系统上。

二、JRTPLIB 库的使用方法及程序实现
(1)JRTPLIB  函数 的使用
a、在使用 JRTPLIB 进行实时流媒体数据传输之前,首先应该生成 RTPSession 类的一个实例来表示此次 RTP

会话,然后调用 Create() 方法来对其进行初始化操作。RTPSession 类的 Create() 方法只有一个参数,用

来指明此次 RTP 会话所采用的端口号
RTPSession sess;  sess.Create(5000);

JRTPLIB-3.7中已经修改了Create(prot)方法。新的Create方法被修改为Crea(sessparams,&transparams)。其中的两个参数需要如下先定义:

RTPUDPv4TransmissionParams transparams;
RTPSessionParams sessparams;

sessparams.SetOwnTimestampUnit(1.0/8000.0);/*设置时间戳,1/8000表示1秒钟采样8000次,即录音时的8KHz*/

sessparams.SetAcceptOwnPackets(true);

transparams.SetPortbase(portbase);/*本地通讯端口*/

b、设置恰当的时戳单元,是 RTP 会话初始化过程所要进行的另外一项重要工作,这是通过调用 RTPSession

类的 SetTimestampUnit() 方法来实现的,前面已经提过。

c、当 RTP 会话成功建立起来之后,接下去就可以开始进行流媒体数据的实时传输了。首先需要设置好数据发

送的目标地址,RTP 协议允许同一会话存在多个目标地址,这可以通过调用 RTPSession 类的

AddDestination()、DeleteDestination() 和 ClearDestinations() 方法来完成。例如,下面的语句表示的

是让 RTP 会话将数据发送到本地主机的 6000 端口

unsigned long addr = ntohl(inet_addr("127.0.0.1")); 
sess.AddDestination(addr, 6000);

d、目标地址全部指定之后,接着就可以调用 RTPSession 类的 SendPacket() 方法,向所有的目标地址发送

流媒体数据。SendPacket() 是 RTPSession 类提供的一个重载函数
对于同一个 RTP 会话来讲,负载类型、标识和时戳增量通常来讲都是相同的,JRTPLIB 允许将它们设置为会

话的默认参数,这是通过调用 RTPSession 类的 SetDefaultPayloadType()、SetDefaultMark() 和

SetDefaultTimeStampIncrement() 方法来完成的。为 RTP 会话设置这些默认参数的好处是可以简化数据的发

送,例如,如果为 RTP 会话设置了默认参数:

sess.SetDefaultPayloadType(0);
 sess.SetDefaultMark(false);  
sess.SetDefaultTimeStampIncrement(10);

之后在进行数据发送时只需指明要发送的数据及其长度就可以了

sess.SendPacket(buffer, 5);

在真正的语音传输中,上面的buffer就是我们录音时所得到的buffer。使用上面的函数可以简单的发送,???但无法真正的实现RTP传输,我们需要调用另一个接口:sess.SendPacket((void *)buffer,sizeof(buffer),0,false,8000);详细的说明可以查看JRTPLIB的说明文档。

e、对于流媒体数据的接收端,首先需要调用 RTPSession 类的 PollData() 方法来接收发送过来的 RTP 或者

RTCP 数据报

JRTPLIB-3.7中修改PollData()方法为Poll(),使用都一样

由于同一个 RTP 会话中允许有多个参与者(源),你既可以通过调用 RTPSession 类的

GotoFirstSource() 和 GotoNextSource() 方法来遍历所有的,也可以通过调用 RTPSession 类的

GotoFirstSourceWithData() 和 GotoNextSourceWithData() 方法来遍历那些携带有数据的源。在从 RTP 会

话中检测出有效的数据源之后,接下去就可以调用 RTPSession 类的 GetNextPacket() 方法从中抽取 RTP 数

据报,当接收到的 RTP 数据报处理完之后,一定要记得及时释放。

JRTPLIB 为 RTP 数据报定义了三种接收模式,其中每种接收模式都具体规定了哪些到达的 RTP 数据报将会被

接受,而哪些到达的 RTP 数据报将会被拒绝。通过调用 RTPSession 类的 SetReceiveMode() 方法可以设置

下列这些接收模式: 
RECEIVEMODE_ALL  缺省的接收模式,所有到达的 RTP 数据报都将被接受; 
RECEIVEMODE_IGNORESOME  除了某些特定的发送者之外,所有到达的 RTP 数据报都将被接受,而被拒绝

的发送者列表可以通过调用 AddToIgnoreList()、DeleteFromIgnoreList() 和 ClearIgnoreList() 方法来进

行设置; 
RECEIVEMODE_ACCEPTSOME  除了某些特定的发送者之外,所有到达的 RTP 数据报都将被拒绝,而被接受

的发送者列表可以通过调用 AddToAcceptList ()、DeleteFromAcceptList 和 ClearAcceptList () 方法来进

行设置。

http://www.cnblogs.com/einyboy/archive/2012/12/01/2796950.html

 

 下面是采用第三种接收模式的程序示例。
if (sess.GotoFirstSourceWithData()) {   
 do {   
  sess.AddToAcceptList(remoteIP, allports,portbase);
         sess.SetReceiveMode(RECEIVEMODE_ACCEPTSOME);

   RTPPacket *pack;         
   pack = sess.GetNextPacket();            // 处理接收到的数据    
   delete pack;   } 
 while (sess.GotoNextSourceWithData()); 
 }

完整的代码中,首先需调用Poll()方法接收RTP数据报,然后在BeginDataAccess()和EndDataAccess()之间进行数据接收的操作。此时,我们设定程序一直do-while等待并处理数据

do{//end   while(1)

#ifndef RTP_SUPPORT_THREAD
                error_status = sess_client.Poll();//接收RTP数据报
                checkerror(error_status);//检查状态
#endif // RTP_SUPPORT_THREAD
                sess_client.BeginDataAccess();//开始数据

                // check incoming packets
                if (sess_client.GotoFirstSourceWithData())//获取第一个数据源(会话者)
                {
                               printf("Begin play/n");
                        do
                        {
                                RTPPacket *pack;

                                while ((pack = sess_client.GetNextPacket()) != NULL)//遍历获取,抽取rtp数据报
                                {
                                        // You can examine the data here
                                        printf("Got packet !/n");


                                        timestamp1 = pack->GetTimestamp();//获取时间戳
                                        lengh=pack->GetPayloadLength();//获取负载长度
                                        RawData=pack->GetPayloadData();   //得到数据

                                       printf("  timestamp: %d lengh=%d/n",timestamp1,lengh);
                                        // we don't longer need the packet, so
                                        // we'll delete it

                                        //Begin play
                                            int fd = open("/dev/dsp", O_RDWR);//建立文件
                                            int status = write(fd, RawData,lengh );//写文件
                                            printf("Play bytes:%d/n",status);
                                            if (status != lengh)
                                              perror("wrote wrong number of bytes");

                                            status = ioctl(fd, SOUND_PCM_SYNC, 0);//
                                            if (status == -1)
                                             perror("SOUND_PCM_SYNC ioctl failed");
                                            printf("Play end/n");
                                             close(fd);//关闭文件
                                        sess_client.DeletePacket(pack);//删除rtp包

                                }
                        } while (sess_client.GotoNextSourceWithData());//遍历下一个源
                         //return 0;

                }

                sess_client.EndDataAccess();//结束数据


            }while(1);


 (2)程序流程图
发送:获得接收端的 IP 地址和端口号        创建 RTP 会话        指定 RTP 数据接收端 设置 RTP 会话 默认参数   发送流媒体数据
接收:获得用户指定的端口号  创建RTP会话  设置接收模式  接受RTP数据  检索RTP数据源  获取RTP数据报 删除RTP数据报

因为是关于JRTPLIB的文章,所以贴出的录音和放音代码不多。需要的朋友可以留下邮箱。

 

 

 

 

 

 

 

 

 

 

一、简述

RTP 是目前解决流媒体实时传输问题的最好办法,而JRTPLIB 是一个用C++语言实现的RTP库包括UDP通讯。刚使用JRTPLIB,对JRTPLIB的理解还不够深,当做使用时,积累的一些经验写个笔记吧。

二、RTP协议

实时传送协议(Real-time Transport Protocol或简写RTP,也可以写成RTTP)是一个网络传输协议,RTP协议详细说明了在互联网上传递音频和视频的标准数据包格式。它一开始被设计为一个多播协议,但后来被用在很多单播应用中。RTP协议常用于流媒体系统(配合RTCP协议或者RTSP协议)。因为RTP自身具有Time stamp所以在ffmpeg 中被用做一种formate。

RTP协议的详细介绍,请参考这篇文章http://www.360doc.com/content/11/1009/15/496343_154624612.shtml

三、RTPSession类

这里不介绍jrtplib的编译安装,这个很简单,网上很多地方都有讲解。

jrtplib的使用中,主要是围绕这个类来实现的,因此大家有必要去查看源码,看这类的实现。为了方便使用,我在这做了RTPSession的继承封装,下面直接贴代码了。

RTPSessionUtils.h

 

  1. #include "rtpsession.h"  
  2. #include "rtppacket.h"  
  3. #include "rtpudpv4transmitter.h"  
  4. #include "rtpipv4address.h"  
  5. #include "rtpsessionparams.h"  
  6. #include "rtperrors.h"  
  7. #ifndef WIN32  
  8. #include <netinet/in.h>  
  9. #include <arpa/inet.h>  
  10. #else  
  11. #include <winsock2.h>  
  12. #endif // WIN32  
  13. #include "rtpsourcedata.h"  
  14. #include <stdlib.h>  
  15. #include <stdio.h>  
  16. #include <iostream>  
  17. #include <string>  
  18.   
  19. //jrtplib应用需链接的lib  
  20. #pragma comment(lib,"ws2_32.lib")  
  21. #pragma comment(lib, "jrtplib_d.lib")  
  22. #pragma comment(lib,"jthread_d.lib")  
  23.   
  24. namespace jrtplib  
  25. {  
  26.     class RTPSessionUtils : public RTPSession  
  27.     {  
  28.         typedef RTPSession base_type;  
  29.     public:  
  30.         RTPSessionUtils();  
  31.         ~RTPSessionUtils();  
  32.   
  33.         int AddDestination(const std::string& ip, uint16_t port);  
  34.         int DeleteDestination(const std::string& ip, uint16_t port);  
  35.         int CreateDefault(uint16_t port);  
  36.     protected:  
  37.         void OnNewSource(RTPSourceData *dat);  
  38.         void OnBYEPacket(RTPSourceData *dat);  
  39.         void OnRemoveSource(RTPSourceData *dat);  
  40.         void OnRTPPacket(RTPPacket *pack,const RTPTime &receivetime,  
  41.             const RTPAddress *senderaddress);  
  42.         void OnRTCPCompoundPacket(RTCPCompoundPacket *pack,const RTPTime &receivetime,  
  43.             const RTPAddress *senderaddress);  
  44.         void OnPollThreadStep();  
  45.     private:  
  46.         int GetAddrFromSource(RTPSourceData *dat, uint32_t& ip, uint16_t& port);  
  47.     };  
  48. }  
  49.   
  50. //整形的ip转成字符串ip  
  51. static std::string IPToString(const unsigned int iIP)  
  52. {  
  53.     struct in_addr inaddr;  
  54.     inaddr.s_addr = htonl(iIP);  
  55.     return std::string(inet_ntoa(inaddr));  
  56. }  
  57.   
  58. //字符串ip转成整形ip  
  59. static unsigned int IPToInt(const std::string& sIP)  
  60. {  
  61.     return inet_addr(sIP.c_str());  
  62. }  


RTPSessionUtils.cpp

 

 

  1. #include "RTPSessionUtils.h"  
  2.   
  3. namespace jrtplib{  
  4.     RTPSessionUtils::RTPSessionUtils()  
  5.     {  
  6. #ifdef WIN32  
  7.         WSADATA dat;  
  8.         WSAStartup(MAKEWORD(2,2),&dat);  //开启Winsock 2 DL
  9. #endif // WIN32  
  10.     }  
  11.   
  12.     RTPSessionUtils::~RTPSessionUtils()  
  13.     {  
  14. #ifdef WIN32  
  15.         WSACleanup();  // 终止Winsock 2 DL
  16. #endif // WIN32  
  17.     }  
  18.   
  19.     int RTPSessionUtils::CreateDefault(uint16_t port)  
  20.     {  
  21.         RTPUDPv4TransmissionParams transparams;  
  22.         RTPSessionParams sessparams;  
  23.         sessparams.SetOwnTimestampUnit(1.0/10.0);//必须设置  
  24.         transparams.SetPortbase(port);//port必须是偶数  
  25.         return base_type::Create(sessparams, &transparams);  //调用父类的Create
  26.   
  27.         base_type::SetDefaultPayloadType(0);  
  28.         base_type::SetDefaultTimestampIncrement(0);  
  29.         base_type::SetDefaultMark(false);  
  30.     }  
  31.   
  32.     int RTPSessionUtils::AddDestination(const std::string& ip, uint16_t port)  //因为定义,覆盖了父类,所以调用父类的成员函数时
  33.     {  
  34.         return base_type::AddDestination(RTPIPv4Address(ntohl(inet_addr(ip.c_str())), port));  //必须添加前缀
  35.     }  
  36.   
  37.     int RTPSessionUtils::DeleteDestination(const std::string& ip, uint16_t port)  
  38.     {  
  39.         return base_type::DeleteDestination(RTPIPv4Address(ntohl(inet_addr(ip.c_str())), port));  
  40.     }  
  41.   
  42.     int RTPSessionUtils::GetAddrFromSource(RTPSourceData *dat, uint32_t& ip, uint16_t& port) //从RTP源数据中获取地址和端口
  43.     {  
  44.         if (dat->IsOwnSSRC()) //如果参与者是通过RTPSources的成员函数CreateOwnSSRC加入的,则返回true,否则返回false。
  45.             return -1;  
  46.   
  47.         if (dat->GetRTPDataAddress() != 0)  //先从RTP中获取地址
  48.         {  
  49.             const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTPDataAddress());  
  50.             ip = addr->GetIP();  //ip 
  51.             port = addr->GetPort(); //端口
  52.         }  
  53.         else if (dat->GetRTCPDataAddress() != 0)  //在尝试从RTCP中获取
  54.         {  
  55.             const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTCPDataAddress());  
  56.             ip = addr->GetIP();  
  57.             port = addr->GetPort()-1;  //注意-1
  58.         }  
  59.   
  60.         return 0;  
  61.     }  
  62.   
  63.     void RTPSessionUtils::OnNewSource(RTPSourceData *dat)  //添加一个新源
  64.     {  
  65.         uint32_t ip;  
  66.         uint16_t port;  
  67.   
  68.         if (GetAddrFromSource(dat, ip, port)) // 从源获取地址
  69.             return;  
  70.           
  71.   
  72.         RTPIPv4Address dest(ip,port);  
  73.         base_type::AddDestination(dest);  //获取后,添加源
  74.   
  75.         std::cout << "OnNewSource Adding destination " << IPToString(ip) << ":" << port << std::endl;  
  76.     }  
  77.   
  78.     void RTPSessionUtils::OnRemoveSource(RTPSourceData *dat)  //删除一个源(会话)
  79.     {  
  80.         if (dat->ReceivedBYE())  //如果收到了该参与者的BYE信息则返回true,否则返回false。
  81.             return;  
  82.   
  83.         uint32_t ip;  
  84.         uint16_t port;  
  85.   
  86.         if (GetAddrFromSource(dat, ip, port))  
  87.             return;  
  88.   
  89.         RTPIPv4Address dest(ip,port);  
  90.         base_type::DeleteDestination(dest);  //删除
  91.   
  92.         std::cout << "OnRemoveSource Deleting destination " << IPToString(ip) << ":" << port << std::endl;  
  93.     }  
  94.   
  95.     void RTPSessionUtils::OnBYEPacket(RTPSourceData *dat)  //与上一个函数区别,没有添加一个判断
  96.     {  
  97.         uint32_t ip;  
  98.         uint16_t port;  
  99.   
  100.         if (GetAddrFromSource(dat, ip, port))  
  101.             return;  
  102.   
  103.         RTPIPv4Address dest(ip,port);  
  104.         base_type::DeleteDestination(dest);  
  105.   
  106.         std::cout << "OnBYEPacket Deleting destination " << IPToString(ip) << ":" << port << std::endl;  
  107.     }  
  108.   
  109.     //只要有rtp包就会触发  
  110.     void RTPSessionUtils::OnRTPPacket(RTPPacket *pack,const RTPTime &receivetime,  
  111.         const RTPAddress *senderaddress)  
  112.     {  
  113.         std::cout << "OnRTPPacket: data:" << pack->GetPayloadData() << std::endl;  
  114.     }  
  115.   
  116.     //收到rtcp包触发  
  117.     void RTPSessionUtils::OnRTCPCompoundPacket(RTCPCompoundPacket *pack,const RTPTime &receivetime,  
  118.         const RTPAddress *senderaddress)  
  119.     {  
  120.         std::cout << "OnRTCPCompoundPacket: data:" << pack->GetCompoundPacketData() << std::endl;  
  121.     }  
  122.   
  123.     //隔段时间就会触发,也可以用于收包回调函数  
  124.     //void RTPSessionUtils::OnPollThreadStep()  
  125.     //{  
  126.     //  BeginDataAccess();  
  127.   
  128.     //  // check incoming packets  
  129.     //  if (GotoFirstSourceWithData())  //获取第一个源  
  130.     //  {  
  131.     //      do  
  132.     //      {  
  133.     //          RTPPacket *pack;  
  134.     //          RTPSourceData *srcdat;  
  135.   
  136.     //          srcdat = GetCurrentSourceInfo();  //返回当前选定的参与者的RTPSourceData对象
  137.   
  138.     //          while ((pack = GetNextPacket()) != NULL)  //比那里获取数据报
  139.     //          {  //GetExtendedSequenceNumber-返回数据包的扩展序列号。当数据包刚刚收到时,只有底16位被设置,高16位随后填充。
  140.                     //GetSSRC返回数据包中的SSRC标识符
  141.     //              std::cout << "Got packet " << pack->GetExtendedSequenceNumber() << " from SSRC " << srcdat->GetSSRC() << std::endl;  
  142.     //              DeletePacket(pack);  
  143.     //          }  
  144.     //      } while (GotoNextSourceWithData());  
  145.     //  }  
  146.   
  147.     //  EndDataAccess();  
  148.     //}  
  149. }  


server.cpp

 

 

  1. #include <iostream>  
  2. #include "RTPSessionUtils.h"  
  3. using namespace jrtplib;  
  4. void main()  
  5. {  
  6.     int status;  
  7.     RTPSessionUtils sess;  
  8.     status = sess.CreateDefault(8888); //服务器端, 仅仅设置端口
  9.     if(status)  
  10.     {  
  11.         std::cout << "RTP error:" << RTPGetErrorString(status) << std::endl;  
  12.         return;  
  13.     }  
  14.   
  15.     while (1)  
  16.     {  
  17.         std::string buf;  
  18.         std::cout << "Input send data:" ;  
  19.         std::cin >> buf;  
  20.   
  21.         sess.SendPacket((void*)buf.c_str(), buf.length(), 0, false, 0);  //不停发送数据
  22.         if(status)  
  23.         {  
  24.             std::cout << "RTP error:" << RTPGetErrorString(status) << std::endl;  
  25.             continue;  
  26.         }  
  27.     }  
  28.   
  29.     system("pause");  
  30. }  

 

client.cpp

 

    1. #include <iostream>  
    2. #include "RTPSessionUtils.h"  
    3. using namespace jrtplib;  
    4.   
    5. void main()  
    6. {  
    7.     int status;  
    8.     RTPSessionUtils sess;  
    9.     status = sess.CreateDefault(6666);  //客户端的端口
    10.     if(status)  
    11.     {  
    12.         std::cout << "RTP error:" << RTPGetErrorString(status) << std::endl;  
    13.         return;  
    14.     }  
    15.     status = sess.AddDestination("127.0.0.1", 8888);  //添加一个会话
    16.     if(status)  
    17.     {  
    18.         std::cout << "RTP error:" << RTPGetErrorString(status) << std::endl;  
    19.         return;  
    20.     }  
    21.   
    22.     while (1)  
    23.     {  
    24.         std::string buf;  
    25.         std::cout << "Input send data:" ;  
    26.         std::cin >> buf;  
    27.   
    28.         sess.SendPacket((void*)buf.c_str(), buf.length(), 0, false, 0);  
    29.         if(status)  
    30.         {  
    31.             std::cout << "RTP error:" << RTPGetErrorString(status) << std::endl;  
    32.             continue;  
    33.         }  
    34.     }  
    35.   
    36.     system("pause");  
    37. }  
posted @ 2015-12-09 15:09  WELEN  阅读(1793)  评论(0)    收藏  举报