HardBirch

基于JRTPLIB库的RTP数据传输设计文档

时间:10-03-02 栏目:系统技术篇 作者:鲁智森也有文化 评论:2 点击: 2,406 次

-->-->-->
-->
-->
-->

 

基于

JRTPLIB

库的

RTP

数据传输设计文档


 

 

关键词:

JRTPLIB



 
RTP




 
ARM




 


交叉编译

BCNG 

















 

 

 

基于

JRTPLIB

库的

RTP

数据传输设计文档


 

 





本:

1.

1

 






者:

XXX

        






别:

嵌入式项目组

      






期:

2008-

11

-

18

 

 

 

[

版权所有

]



  





 

-->一、 


-->........

概述
...
3


-->二、 


-->........

流媒体传输系统设计与实现
...
3


1.
......................................................................................................................

Linux
平台实时流媒体编程
   
3


1)
.........................................................................................................................

PC
机上的环境搭建
   
3


2)
.......................................................................................................................

嵌入式版的环境搭建
   
3


3)
...............................................................................................................................................

初始化
   
4


4)
...........................................................................................................................................

数据发送
   
4


5)
...........................................................................................................................................

数据接收
   
4


2.
..................................................................................................................

Window
平台实时流媒体编程
   
5


3.
................................................................................................................................................

具体流程图
   
7


-->三、 


-->........

调试记录
...
9


-->四、 


-->........

存在的问题和拟采取的解决方案
...
12


-->五、 


-->........

作者简介
...
12







.


概述

此模块是在基于
S3C2440
的嵌入式开发板上实现对摄像头采集并经过
H.264
压缩过的数据的
RTP
传输,并在接收端
PC
上能正确接收的功能。

RTP
即实时传输协议,用于
Internet
上针对多媒体数据流的传输。它通常使用
UDP
协议来传送数据,起初是为了“
multicast
”传输情况而设计的,目的是提供时间信息和保证流同步,不过现在也用于一对一的传输情况。
RTP
协议主要完成对数据包进行编号,加盖时戳,丢包检查,安全与内容认证等工作。通过这些工作,应用程序会利用
RTP
协议的数据信息保证流数据的同步和实时传输。

 



.


流媒体传输系统设计与实现

1.


Linux

平台实时流媒体编程

RTP
是目前解决流媒体实时传输问题的最好办法,如果需要在
Linux
平台上进行实时流媒体编程,可以考虑使用一些开放源代码的
RTP
库,如
LIBRTP

JRTPLIB
等。
JRTPLIB
是一个面向对象的
RTP
库,它完全遵循
RFC 1889
设计,我拟采用
JRTPLIB
实现
RTP/RTCP
协议,下面就以
JRTPLIB
为例,讲述如何在
Linux
平台上运用
RTP
协议进行实时流媒体编程。

1)




PC


机上的环境搭建

JRTPLIB
是一个用
C++
语言实现的
RTP
库,目前已经可以运行在
Windows

Linux

FreeBSD

Solaris

Unix

VxWorks
等多种操作系统上。要为
Linux
系统安装
JRTPLIB
,首先从
JRTPLIB
的网站(
http:
//research.edm.uhasselt.be/~jori/page/index.php?n=CS.Jrtplib

)下载最新的源码包,此处使用的是
jrtplib-3.7.1

jthread-1.2.1(
控制线程库
)
。假设下载后的源码包保存在
/root
目录下,对其进行解压缩,接下去需要对
JRTPLIB
进行配置和编译:

[root@linuxgam src]# cd jrtplib-3.7.1

  
[root@linuxgam jrtplib-3.7.1]# ./configure

  
[root@linuxgam jrtplib-3.7.1]# make



最后再执行如下命令就可以完成
JRTPLIB
的安装:

[root@linuxgam jrtplib-3.7.1]#
make install

 

再进入
jthread-1.2.1
目录下重复以上操作:

[root@linuxgam src]# cd jthread-1.2.1

  
[root@linuxgam jrtplib-3.7.1]# ./configure

  
[root@linuxgam jrtplib-3.7.1]# make

[root@linuxgam jrtplib-3.7.1]#
make install

 

生成的动态库安装到了
/usr/local/lib
目录下

头文件在
/usr/local/include
目录下

2)


嵌入式版的环境搭建

嵌入式版上的环境搭建和
PC
机上有些不同,如不注意可能导致两个库都不能使用。

首先,必须先安装
jthread
库,再安装
jrtplib
库;其次,要交叉编译,需修改
configure
文件。具体步骤为:

 

[root@linuxgam src]# cd jthread-1.2.1

[root@linuxgam jrtplib-3.7.1]#
./configure 
-host=arm-linux 


prefix=/usr/local/arm/2.95.3

[root@linuxgam jrtplib-3.7.1]#
make

[root@linuxgam jrtplib-3.7.1]#
make install

 

[root@linuxgam src]# cd jrtplib-3.7.1
[root@linuxgam jrtplib-3.7.1]# ./configure 

-host=arm-linux 

-prefix=/usr/local/arm/2.95.3

cross_compile=yes

注意:

1.

rtperrors.h
中添加头文件
stdio.h
,否则编译会出现相应的问题。

2.
修改
rtpconfig_unix.h
文件(具体方法在调试记录部分有阐述):

注释掉
src/rtpconfig_unix.h
(或者
rtpconfig_win.h
)这个文件,里面果然定义了一个
RTP_BIG_ENDIAN

3.

修改
rtpsession.cpp
文件如下:

if (!gotlogin)

 
{

 
//      
char
*logname = getenv("LOGNAME");

           

char *logname =
"root";

           
if (logname == 0)

                    
return
ERR_RTP_SESSION_CANTGETLOGINNAME;

           
strncpy((char
*)buffer,logname,*bufferlength);

 
}

 

然后再运行

[root@linuxgam jrtplib-3.7.1]#

make clean;
make

[root@linuxgam jrtplib-3.7.1]#
make install

1)


 

初始化


在使用
JRTPLIB
进行实时流媒体数据传输之前,首先应该生成
RTPSession
类的一个实例来表示此次
RTP
会话,然后调用
Create()
方法来对其进行初始化操作。
RTPSession
类的
Create()
方法只有一个参数,用来指明此次
RTP
会话所采用的端口号。

2)


数据发送



RTP
会话成功建立起来之后,接下来就可以开始进行流媒体数据的实时传输了。首先需要设置好数据发送的目标地址,
RTP
协议允许同一会话存在多个目标地址,这可以通过调用
RTPSession
类的
AddDestination()

DeleteDestination()

ClearDestinations()
方法来完成。目标地址全部指定之后,接着就可以调用
RTPSession
类的
SendPacket()
方法,向所有的目标地址发送流媒体数据。
SendPacket()
最典型的用法是类似于下面的语句,其中第一个参数是要被发送的数据,而第二个参数则指明将要发送数据的长度,再往后依次是
RTP
负载类型、标识和时戳增量。


sess.SendPacket(buffer, 5, 0,
false, 10);

对于同一个
RTP
会话来讲,负载类型、标识和时戳增量通常来讲都是相同的,
JRTPLIB
允许将它们设置为会话的默认参数,这是通过调用
RTPSession
类的
SetDefaultPayloadType()

SetDefaultMark()

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


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

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


sess.SendPacket(buffer, 5);

3)


数据接收


对于流媒体数据的接收端,首先需要调用
PollData()
方法来接收发送过来的
RTP
或者
RTCP
数据报。由于同一个
RTP
会话中允许有多个参与者(源),因此既可以通过调用
GotoFirstSource()

GotoNextSource()
方法来遍历所有的源,也可以通过调用
GotoFisstSourceWithDat()

GotoNextSourceWithData()
方法来遍历那些携带有数据的源。在从
RTP
会话中检测出有效的数据源之后,接下去就可以调用
RTPSession
类的
GetNextPacket()
方法从中抽取
RTP
数据报,当接收到的
RTP
数据报处理完之后,要及时释放。下面的代码示范了该如何对接收到的
RTP
数据报进行处理:

do{



   
sess.BeginDataAccess();

                  
if
(sess.GotoFirstSourceWithData())

                  
{

                           
do

                           
{

                                     
RTPPacket
*pack;

                                     
uint8_t
*data;

                           
        
size_t length;

                                     
if((outfile=open("/mnt/hgfs/download/temp1.YUV",O_WRONLY
| O_CREAT | O_APPEND))<0)

                                     
{

                                              
perror("open
outfile error");

                                              
return
-1;

                                     
}

                                     
while
((pack = sess.GetNextPacket()) != NULL)

                                     
{

                                              
data=pack->GetPayloadData();

                                              
length=pack->GetPayloadLength();

                                              
if((write(outfile,data,length))<0)//

将接收到的图片信息写入文中。

                                              
{

                                                       
perror("write
outfile error;");

                                                       
return
-1;

                                              
}

                                              
sess.DeletePacket(pack);

                                     
}

                           
}
while (sess.GotoNextSourceWithData());

                  
}

                  
sess.EndDataAccess();

                  
close(outfile);

JRTPLIB

RTP
数据报定义了
3
种接收模块,通过调用
RTPSession
类的
SetReceiveMode()
方法可以设置下列这些接收模式:

RECEIVEMODE_ALL
:缺省的接收模式,所有到达的
RTP
数据报都将被接受;

RECEIVEMODE_IGNORESOME
:除了某些特定的发送者之外,所有到达的
RTP
数据报都将被接受,而被拒绝的发送者列表可以通过调用
AddToIgnoreList()

DeleteFromIgnoreList()

ClearIgnoreList()
方法来进行设置;

   

 
RECEIVEMODE_ACCEPTSOME
:除了某些特定的发送者之外,所有到达的
RTP
数据报都将被拒绝,而被接受的发送者列表可以通过调用
AddToAcceptList()

DeleteFromAcceptList

ClearAcceptList()
方法来进行设置。

1.


 

Window

平台实时流媒体编程

压缩包可以从这里获得:


http://www.bairuitech.com/upimg/soft/jrtplib-3.7.1.rar



下载
jrtplib-3.7.1.rar
后,首先将其解压到一个临时文件夹中,然后开始后续工作。

首先需要强调的是,
jrtplib
是一个库而不是应用程序,编译后我们获得的是
.lib
文件。这个文件是用来实现
RTP

协议的,意义和我们在写
WIN32
程序时用到的
kernel.lib
一样。

解压后的文件夹中包含两个目录,
jrtplib-3.7.1

jthread-1.2.1
,打开这两个目录后我们可以看到下面又有两个同名的目录,为了后面能顺利编译,我们把同名目录下的文件全部考到上一级目录中,就是说把
f:/jrtplib-3.7.1/jrtplib-3.7.1/*.*
复制到
f:/jrtplib-3.7.1/
。同理,把
f:/jthread-1.2.1/jthread-1.2.1/*.*
复制到
f:/jthread-1.2.1/

完成上述步骤后我们就可以开始编译库文件了。

Windows
平台下建议使用
Visual C++6.0

首先编译多线程库
jthread
,在
vc6
中直接打开工作区文件
jthread.dsw
,改变工程设置,选中
source file
下的文件,点右键选择
setting
,确保
code generation

Use
run-time library


debug mulitithreaded DLL

debug mulitithreaded

然后选
build
就可以了,和上面一样的方法完成
jrtpthread
的编译。这个底下的文件比
jthread
多一些。

默认产生的文件是
jthread.lib

jrtplib.Lib,
这两个文件分别位于两个文件夹下的
debug
文件夹下,将它们复制到
VC6

lib
文件夹下。

完成上述工作后我们就可以开始尝试编译
jrtplib
附带的
examples

创建一个新的
Win32
Console

应用程序项目,添加
example
文件到
source files
文件夹中,然后添加
jrtplib
工程下的所有
.h
头文件,这里我们可以用
VC6
提供的一个功能偷懒:)将
jrtplib
项目添加到本工作区,然后将
Header Files
下的所有文件复制到我们创建的工程的
Header Files
文件夹里面。

修改
example.cpp
文件,在文件开始添加:

#pragma comment(lib,
"jrtplib.lib")

#pragma comment(lib,
"jthread.lib")

#pragma comment(lib,
"WS2_32.lib")

或者在
VC

a)
Project->Settings->Link


Object/library modules:
添加
jthread.lib
jrtplib.lib



b) Link

中添加
ws2_32.lib

检查
code
generation

->debug mulitithreaded
DLL


debug mulitithreaded
,方法同上文中检查库文件的方法。

最后就可以编译、连接、生成可执行文件了。

3
、具体流程图(略)



.

 

调试记录

(1).
输入端口,
IP
后出错

ERROR: Can't retrieve login name

这是
rtpsession.cpp
中的
createCNAME
函数有问题

if (!gotlogin)

 
{

 
//    
char *logname =
getenv("LOGNAME");

        
if (logname
== 0)

               
return
ERR_RTP_SESSION_CANTGETLOGINNAME;

        
strncpy((char
*)buffer,logname,*bufferlength);

 
}

logname
要求获得登陆名,而板子一般没有登陆名,将其强制改为
root
即可

if (!gotlogin)

 
{

 
//    
char *logname =
getenv("LOGNAME");

        
char
*logname = "root";

        
if (logname
== 0)

               
return
ERR_RTP_SESSION_CANTGETLOGINNAME;

        
strncpy((char
*)buffer,logname,*bufferlength);

 
}

 


2
)板子和
PC
收发数据不能接收


PC
和板子上同时运行
jrtplib
例子程序
example1
(此程序可同时收发),在
PC
和板子之间收发数据,程序能够运行但双方都接收不到数据,结果如下:

查阅资料发现是字节序和位域的问题,


x86

pc
机是用小端字节序
(little endian),
而嵌入式平台一般是大端字节序
(big endian),
可能是由于字节序的不同,导致了明明存在数据包,却认不出来的问题。

这是一个位域结构体,
jrtplib
库使用哪种字节序完全取决于
RTP_BIG_ENDIAN
的定义,这样问题就简单化了。

看了一下我编译
arm

jrtplib
库的
rtpconfig_unix.h
(或者
rtpconfig_win.h

这个文件,里面果然定义了一个
RTP_BIG_ENDIAN
,所以要和
pc
采用的小端字节序一样,先是直接在
rtpconfig_unix.h
中注释掉了

然后在重新编译库,执行

./configure

host=arm-linux

prefix=/usr/local/arm/2.95.3

make

make install

完了再次运行
example1
,还是不行,查看
rtpconfig_unix.h
发现刚注释掉到内容又恢复了,

最后查找发现是此文件是由
./configure
命令生成的,所以
先执行
./configure
命令,然后再注释上面的内容
,最后

make

make install

编译完成再次运行
example1
,能受到数据包,结果如下:

 


3
)自己写的接收程序写文件出错

接收端程序是在
example3
的基础上修改的,收到到数据包信息全部存在

RTPPacket *pack;

这个类指针当中,可以通过

uint8_t *data;

size_t *length;

data=pack->GetPayloadData();

length=pack->getPayloadLength();

提取出负载数据和负载长度。

收到数据以后以文件形式存下来。

if((write(outfile,data,length))<0)

 
{

        
perror("write
outfile error;");

        
return -1;

 
}

最开始把打开文件放在开头,写入文件放在接收数据之后,但一直不能正确写文件,提示:

bad file descriptor

后来发现把打开文件放到写文件之前(即在接收数据到
while
循环之内)可以正确写数据,分析原因觉得可能是由于接收程序是一个多线程控制的而引起的。但是这样每次接收都要打开文件,会导致接收速度变慢,试着把打开文件放到循环外边发现也可以正确写数据,具体是什么原因导致这样暂时还不清楚。

 


4
)接收数据时有数据丢失现象,发送端发送数据时发现发送速度太快,所以数据瞬间发完,而不像例子程序一样一包一包的发送,最开始一直以为是设置
时戳单元

时戳增量
有问题,

sessparams.SetOwnTimestampUnit(1.0/1000.0);

sess.SetDefaultTimestampIncrement(10);

但改了几次还是没有变化,最后仔细对比例子程序,发现是

RTPTime ::Wait(RTPTime(0,0));

这个函数的位置放错了,此函数的作用就是发完一个包后等待一定时间(其中括号中第一个参数表示秒,第二个表示微秒),发送程序中将其放到了
while
循环之外,没有了这个等待时间而接收端还是以此间隔接收数据当然会丢失数据了,将其挪到循环之内就可以了。

 



.

 

存在的问题和拟采取的解决方案

现在接收到的数据是以文件的形式存下来的,但是最后想要达到的目的是与
MPlayer
结合起来,使视频采集,压缩后的数据在接收端能够实时的播放出来,现在存在的问题就是如何把收到的数据流传到
MPlayer
中实时播放,下一阶段的工作首先是将
MPlayer
的源代码研究清楚,然后再想办法将接收数据实时传给
MPlayer

 



.

 

作者简介

XXX

198X

xx

xx
日生,
XX

xx
市人。
XX
大学

07

级硕士研究生,

研究方向为:
xxx

E_mail:

 

声明: 本文由( 鲁智森也有文化 )原创编译,转载请保留链接: 基于JRTPLIB库的RTP数据传输设计文档

基于JRTPLIB库的RTP数据传输设计文档:目前有2 条留言

  1. 板凳
    tx409732728:

    请问这个错误怎么解决啊c:/program files/microsoft visual studio/vc98/include/rtpsession.h(55) : fatal error C1083: Cannot open include file: 'jmutex.h': No such file or directory

    2011-01-03 00:40 [回复]
  2. 有没有你介绍的后续你的工作,关于实时传输给Mplayer的呀,嘻嘻

    2011-09-01 11:35 [回复]

发表评论


QQ群互动

Linux系统与内核学习群:194051772

WP建站技术学习交流群:194062106

魔豆之路QR

魔豆的Linux内核之路

魔豆的Linux内核之路

优秀工程师当看优秀书籍

优秀程序员,要看优秀书!

赞助商广告

友荐云推荐