热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

linux下基于jrtplib库的实时传送实现

一、RTP是进行实时流媒体传输的标准协议和关键技术实时传输协议(Real-timeTransportProtocol,PRT)是在Inte
一、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); 




b、设置恰当的时戳单元,是 RTP 会话初始化过程所要进行的另外一项重要工作,这是通过调用 RTPSession 类的 SetTimestampUnit() 方法来实现的,该方法同样也只有一个参数,表示的是以秒为单元的时戳单元。


sess.SetTimestampUnit(1.0/8000.0);




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); 






e、对于流媒体数据的接收端,首先需要调用 RTPSession 类的 PollData() 方法来接收发送过来的 RTP 或者 RTCP 数据报。由于同一个 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 () 方法来进行设置。 下面是采用第三种接收模式的程序示例。


if (sess.GotoFirstSourceWithData()) {   


 do {   


  sess.AddToAcceptList(remoteIP, allports,portbase);


         sess.SetReceiveMode(RECEIVEMODE_ACCEPTSOME);




   RTPPacket *pack;         


   pack = sess.GetNextPacket();            // 处理接收到的数据    


   delete pack;   } 


 while (sess.GotoNextSourceWithData()); 


 }






 (2)程序流程图


发送:获得接 收端的 IP 地址和端口号        创建 RTP 会话        指定 RTP 数据接收端 设置 RTP 会话默认参数   发送流媒体数据


接收:获得用户指定的端口号  创建RTP会话  设置接收模式  接受RTP数据  检索RTP数据源  获取RTP数据报  删除RTP数据报






三、环境搭建及编译方法


引用
(1)Toolchain的安装
首先找到xscale-arm-toolchain.tgz文件,假设该文件包放在/tmp/下
#cd /
#tar -zxvf /tmp/xscale-arm-toolchain.tgz
再设置环境变量
#export PATH=/usr/local/arm-linux/bin:$PATH
最后检查一下交叉编译工具是否安装成功
#arm-linux-g++ --version
看是否显示arm-linux-g++的版本,如有则安装成功。
(2)JRTPLIB 库的交叉编译及安装
首先从 JRTPLIB 的网站(http://lumumba.luc.ac.be/jori/jrtplib/jrtplib.htmll) 下载最新的源码包,此处使用的是jrtplib-2.8.tar,假设下载后的源码包放在/tmp下,执 行下面的命令对其解压缩:
#cd /tmp
#tar -zxvf jrtplib-2.8.tar
然后要对jrtplib进行配置和编译
#cd jrtplib-2.8
#./configure CC=arm-linux-g++ cross-compile=yes
修改Makefile文件
将链接命令ld 和ar改为arm-linux-ld和 arm-linux-ar
#make
最后再执行如下命令就可以完成 JRTPLIB 的安装:
#make install
(3)程序编译
a、配置编译环境
可以用export来配置,也可以用编写Makefile的方法。这里采用Makefile。
编写Makefile&:
INCL = -I/usr/local/include
CFLAGS = -pipe -O2 -fno-strength-reduce
LFLAGS = /usr/local/lib/libjrtp.a -L/usr/X11R6/lib
LIBS = -LX11 -LXext /usr/local/lib/libjrtp.a
CC = arm-linux-g++

main:main.o
$(CC) $(LFLAGS) $(INCL) -o main main.o $(LIBS)
main.o:main.cpp

clean:
rm -f main
rm -f *.o

.SUFFIXES:.cpp
.cpp.o:
$(CC) -c $(CFLAGS) $(INCL) -o $&#64; $<        /*  $&#64;表示目标的完整名字      */
         /* $<表示第一个依赖文件的名字 */
b、编译
假设发送和接收程序分别放在/tmp/send和/tmp/receive目录下
#cd /tmp/send
#make
#cd /tmp/receive
#make




四、易出错误及注意问题


引用
1、找不到一些标准的最 基本的一些头文件。
 主要是因为Toolchain路径没安装对&#xff0c;要 严格按照步骤安装。
2、找不到使用的jrtplib库中的一些头文件。
 在 jrtplib的安装目录下&#xff0c;include路径下不能再有别的目录。
3、recieve函数接收数据包不能正确提出所要数据。
 由于每一个RTP数据报都由头部&#xff08;Header&#xff09;和负载&#xff08;Payload&#xff09;两个部分组成&#xff0c;若使用getrawdata()是返回整个数据包的数据&#xff0c;包含 传输媒体的类型、格式、序列号、时间戳以及是否有附加数据等信息。getpayload()函数是返回所发送的数据。两者一定要分清。
4、设置RECEIVEMODE_ACCEPTSOME  接收模式后&#xff0c;运行程序接收端不能接包。
 IP地址格式出了问题。iner_addr()与ntohl()函数要用对&#xff0c;否则参数传不进去&#xff0c;接受列表中无值&#xff0c;当然接收不了数据包。
5、编译通过&#xff0c;但测试时接收端不能接收到数据。
 可能是接收机防火墙未关闭。运行&#xff1a;
 #iptables -F
 也可能是IP地址没有设置好。运行&#xff1a;
 #ifocnfig eth0  *.*.*.*  netmask *.*.*.*
6、使用jrtolib库时&#xff0c;在程序中include 后最好加上库所在的路径。


五、程序




send:


view plainprint?
  1. #include   
  2. #include   
  3. #include "rtpsession.h"  
  4.   
  5. // 错误处理函数  
  6. void checkerror(int err)  
  7. {  
  8.   if (err < 0) {  
  9.     char* errstr &#61; RTPGetErrorString(err);  
  10.     printf("Error:%s\\n", errstr);  
  11.     exit(-1);  
  12.   }  
  13. }  
  14.   
  15. int main(int argc, char** argv)  
  16. {  
  17.   RTPSession sess;  
  18.   unsigned long destip;  
  19.   int destport;  
  20.   int portbase &#61; 6000;  
  21.   int status, index;  
  22.   char buffer[128];  
  23.   
  24.   if (argc !&#61; 3) {  
  25.     printf("Usage: ./sender destip destport\\n");  
  26.     return -1;  
  27.   }  
  28.   
  29.   // 获得接收端的IP地址和端口号  
  30.   destip &#61; inet_addr(argv[1]);  
  31.   if (destip &#61;&#61; INADDR_NONE) {  
  32.     printf("Bad IP address specified.\\n");  
  33.     return -1;  
  34.   }  
  35.   destip &#61; ntohl(destip);  
  36.   destport &#61; atoi(argv[2]);  
  37.   
  38.   // 创建RTP会话  
  39.   status &#61; sess.Create(portbase);  
  40.   checkerror(status);  
  41.   
  42.   // 指定RTP数据接收端  
  43.   status &#61; sess.AddDestination(destip, destport);  
  44.   checkerror(status);  
  45.   
  46.   // 设置RTP会话默认参数  
  47.   sess.SetDefaultPayloadType(0);  
  48.   sess.SetDefaultMark(false);  
  49.   sess.SetDefaultTimeStampIncrement(10);  
  50.   
  51.   // 发送流媒体数据  
  52.   index &#61; 1;  
  53.   do {  
  54.     sprintf(buffer, "%d: RTP packet", index &#43;&#43;);  
  55.     sess.SendPacket(buffer, strlen(buffer));  
  56.     printf("Send packet !\\n");  
  57.   } while(1);  
  58.   
  59.   return 0;  
  60. }  












receive:


view plainprint?
  1. #include   
  2. #include "rtpsession.h"  
  3. #include "rtppacket.h"  
  4.   
  5. // 错误处理函数  
  6. void checkerror(int err)  
  7. {  
  8.   if (err < 0) {  
  9.     char* errstr &#61; RTPGetErrorString(err);  
  10.     printf("Error:%s\\n", errstr);  
  11.     exit(-1);  
  12.   }  
  13. }  
  14.   
  15. int main(int argc, char** argv)  
  16. {  
  17.   RTPSession sess;  
  18.   int localport,portbase;  
  19.   int status;  
  20.   unsigned long remoteIP;  
  21.   if (argc !&#61; 4) {  
  22.     printf("Usage: ./sender localport\\n");  
  23.     return -1;  
  24.   }  
  25.   
  26.    // 获得用户指定的端口号  
  27.      
  28.   remoteIP &#61; inet_addr(argv[1]);  
  29.   localport &#61; atoi(argv[2]);  
  30.   portbase &#61; atoi(argv[3]);  
  31.   // 创建RTP会话  
  32.   status &#61; sess.Create(localport);  
  33.   checkerror(status);  
  34.     
  35.   //RTPHeader *rtphdr;  
  36.   unsigned long timestamp1;  
  37.   unsigned char * RawData;  
  38.   unsigned char temp[30];  
  39.   int lengh ,i;  
  40.   bool allports &#61; 1;  
  41.     
  42.   sess.AddToAcceptList(remoteIP, allports,portbase);  
  43.     
  44.      do {  
  45.  //设置接收模式  
  46.         sess.SetReceiveMode(RECEIVEMODE_ACCEPTSOME);  
  47.    sess.AddToAcceptList(remoteIP, allports,portbase);  
  48.   
  49.     // 接受RTP数据  
  50.     status &#61; sess.PollData();  
  51.   
  52.       
  53.  // 检索RTP数据源  
  54.     if (sess.GotoFirstSourceWithData()) {  
  55.       do {  
  56.           
  57.         RTPPacket* packet;  
  58.         // 获取RTP数据报  
  59.         while ((packet &#61; sess.GetNextPacket()) !&#61; NULL) {  
  60.           printf("Got packet !\n");  
  61.   
  62.    timestamp1 &#61; packet->GetTimeStamp();  
  63.    lengh&#61;packet->GetPayloadLength();  
  64.    RawData&#61;packet->GetPayload();  
  65.      
  66.    for(i&#61;0;i
  67.       temp[i]&#61;RawData[i];  
  68.   printf("%c",temp[i]);  
  69.    }  
  70.    temp[i]&#61;&#39;\0&#39;;  
  71.    printf("  timestamp: %d lengh&#61;%d data:%s\n",timestamp1,lengh,&temp);  
  72.           // 删除RTP数据报  
  73.      
  74.           delete packet;  
  75.         }  
  76.       } while (sess.GotoNextSourceWithData());  
  77.     }  
  78.   } while(1);  
  79.   
  80.   return 0;  
  81. }  


推荐阅读
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • 第七课主要内容:多进程多线程FIFO,LIFO,优先队列线程局部变量进程与线程的选择线程池异步IO概念及twisted案例股票数据抓取 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文介绍了作者在开发过程中遇到的问题,即播放框架内容安全策略设置不起作用的错误。作者通过使用编译时依赖注入的方式解决了这个问题,并分享了解决方案。文章详细描述了问题的出现情况、错误输出内容以及解决方案的具体步骤。如果你也遇到了类似的问题,本文可能对你有一定的参考价值。 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • 如何用JNI技术调用Java接口以及提高Java性能的详解
    本文介绍了如何使用JNI技术调用Java接口,并详细解析了如何通过JNI技术提高Java的性能。同时还讨论了JNI调用Java的private方法、Java开发中使用JNI技术的情况以及使用Java的JNI技术调用C++时的运行效率问题。文章还介绍了JNIEnv类型的使用方法,包括创建Java对象、调用Java对象的方法、获取Java对象的属性等操作。 ... [详细]
  • 本文介绍了使用readlink命令获取文件的完整路径的简单方法,并提供了一个示例命令来打印文件的完整路径。共有28种解决方案可供选择。 ... [详细]
  • 【重识云原生】第四章云网络4.8.3.2节——Open vSwitch工作原理详解
    2OpenvSwitch架构2.1OVS整体架构ovs-vswitchd:守护程序,实现交换功能,和Linux内核兼容模块一起,实现基于流的交换flow-basedswitchin ... [详细]
  • 正则表达式及其范例
    为什么80%的码农都做不了架构师?一、前言部分控制台输入的字符串,编译成java字符串之后才送进内存,比如控制台打\, ... [详细]
  • UNIX高级环境编程 第11、12章 线程及其属性
    第11章线程11.2线程概念线程资源:线程ID,一组寄存器,栈,调度优先级和策略,信号屏蔽字,e ... [详细]
author-avatar
123456ws1043
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有