这几天在看关于JRTPLIB方面的东西。在网上看了不少文章linux jrtplib 使用,其中有很大部份使用的JRTPLIB版本在3.0以下。

在网上下载了一个JRTPLIB-3.7的库查看linux是什么系统,发觉上面的函数插口做了一些更改。现奉上一篇基于JRTPLIB-3.7的网路

语音传送实例,希望有兴趣的同学一起参详研究。

——-chuckGao

第一部份JRTPLIB的编译及安装

这是必行的一步,在网上可以找到相关的文章,这儿就不啰嗦了。不过可能有些同事会碰到JRTPLIB

未能正常编译的情况,出现error:'memcpy'wasnotdeclaredinthisscope的错误。这是因为JRTPLIB编

译中未能找到memcpy这个函数。在网上有memcpy的patch。内容如下:

diff--gita/src/rtcpcompoundpacketbuilder.cppb/src/rtcpcompoundpacketbuilder.cpp

index8172007..8fd4510100644

—a/src/rtcpcompoundpacketbuilder.cpp

+++b/src/rtcpcompoundpacketbuilder.cpp

@@-30,6+30,8@@

*/

+#include

#include”rtcpcompoundpacketbuilder.h”

#include”rtcpsrpacket.h”

#include”rtcprrpacket.h”

diff--gita/src/rtppacket.cppb/src/rtppacket.cpp

indexb6d5fda..8c516c7100644

—a/src/rtppacket.cpp

+++b/src/rtppacket.cpp

@@-30,6+30,8@@

*/

+#include

#include”rtppacket.h”

#include”rtpstructs.h”

#include”rtpdefines.h”

+代表添加,-代表删掉相应内容

第二部份JRTPLIB编程

下边先转载一部份网上的手册linux设置环境变量,白色标记是JRTPLIB-3.7修了后的使用方式

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

一、RTP是进行实时流媒体传输的标准合同和关键技术

实时传输合同(Real-timeTransportProtocol,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会话所采用的端标语。

RTPSessionsess;sess.Create(5000);

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

RTPUDPv4TransmissionParamstransparams;

RTPSessionParamssessparams;

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

sessparams.SetAcceptOwnPackets(true);

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

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

类的SetTimestampUnit()方式来实现的,上面早已提过。

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

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

AddDestination()、DeleteDestination()和ClearDestinations()方式来完成。诸如,下边的句子表示的

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

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

在真正的语音传输中linux jrtplib 使用,前面的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()方式来进

行设置。下边是采用第三种接收模式的程序示例。

if(sess.GotoFirstSourceWithData()){

do{

sess.AddToAcceptList(remoteIP,allports,portbase);

sess.SetReceiveMode(RECEIVEMODE_ACCEPTSOME);

RTPPacket*pack;

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

deletepack;}

while(sess.GotoNextSourceWithData());

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

do{

#ifndefRTP_SUPPORT_THREAD

error_status=sess_client.Poll();

checkerror(error_status);

#endif//RTP_SUPPORT_THREAD

sess_client.BeginDataAccess();

//checkincomingpackets

if(sess_client.GotoFirstSourceWithData())

printf(“Beginplay/n”);

do

RTPPacket*pack;

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

//Youcanexaminethedatahere

printf(“Gotpacket!/n”);

timestamp1=pack->GetTimestamp();

lengh=pack->GetPayloadLength();

RawData=pack->GetPayloadData();//得到数据

printf(“timestamp:%dlengh=%d/n”,timestamp1,lengh);

//wedon'tlongerneedthepacket,so

//we'lldeleteit

//Beginplay

intfd=open(“/dev/dsp”,O_RDWR);

intstatus=write(fd,RawData,lengh);

printf(“Playbytes:%d/n”,status);

if(status!=lengh)

perror(“wrotewrongnumberofbytes”);

status=ioctl(fd,SOUND_PCM_SYNC,0);

if(status==-1)

perror(“SOUND_PCM_SYNCioctlfailed”);

printf(“Playend/n”);

close(fd);

sess_client.DeletePacket(pack);

}while(sess_client.GotoNextSourceWithData());

//return0;

sess_client.EndDataAccess();

}while(1);

(2)程序流程图

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

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

由于是关于JRTPLIB的文章,所以贴出的录音和放音代码不多。须要的同学可以留下邮箱。

Tagged:
Author

这篇优质的内容由TA贡献而来

刘遄

《Linux就该这么学》书籍作者,RHCA认证架构师,教育学(计算机专业硕士)。

发表回复