jrtplib-3.7.1笔记--流程一遍

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

实时传输协议(Real-time Transport ProtocolPRT)是在 Internet 上处理多媒体数据流的一种网络协议,利用它能够在一对一(unicast,单播)或者一对多(multicast,多播)的网络环境中实现传流媒体数据的实时传输。RTP 通常使用 UDP 来进行多媒体数据的传输,但如果需要的话可以使用 TCP 或者 ATM 等其它协议。
 
协议分析 每一个RTP数据报都由头部(Header)和负载(Payload)两个部分组成,其中头部前 12 个字节的含义是固定的,而负载则可以是音频或者视频数据。

RTP 
是目前解决流媒体实时传输问题的最好办法,要在 Linux 平台上进行实时传送编程,可以考虑使用一些开放源代码的 RTP 库, LIBRTPJRTPLIB JRTPLIB 是一个面向对象的 RTP 库,它完全遵循 RFC 1889 设计,在很多场合下是一个非常不错的选择。JRTPLIB 是一个用 C++ 语言实现的 RTP 库,这个库使用socket 机制实现网络通讯 因此可以运行在 WindowsLinuxFreeBSDSolarisUnixVxWorks 等多种操作系统上。

嵌入式版的环境搭建

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

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

[root@linuxgam src]# cd jthread-1.2.1

[root@linuxgam jthread-1.2.1]# ./configure  -host=arm-linux  –prefix=/usr/local/arm/2.95.3

 

[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

 

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

RTPUDPv4TransmissionParams transparams;

RTPSessionParams sessparams;


// 创建RTP会话 sessparams.SetAcceptOwnPackets(true); transparams.SetPortbase(portbase); status = sess.Create(sessparams, &transparams); checkerror(status);

 

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

sessparams.SetOwnTimestampUnit(1.0/8000.0);


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

 

// 指定RTP数据接收端

uint16_t portbase, destport;

uint32_t destip;

RTPIPv4Address addr(destip, destport);

status = sess.AddDestination(addr);

checkerror(status);

 

d、对于同一个RTP会话来讲,负载类型、标识和时戳增量通常来讲都是相同的,JRTPLIB允许将它们设置为会话的默认参数,这是通过调用 RTPSession类的SetDefaultPayloadType()SetDefaultMark() SetDefaultTimeStampIncrement()方法来完成的。为RTP会话设置这些默认参数的好处是可以简化数据的发送,例如,如果为 RTP会话设置了默认参数:

 

// 设置RTP会话默认参数

sess.SetDefaultPayloadType(96);

sess.SetDefaultMark(false);

sess.SetDefaultTimestampIncrement(160);

 

 

e、之后就可以调用 RTPSession 类的 SendPacket() 方法向所有的目标地址发送流媒体数据,进行数据发送时只需指明要发送的数据及其长度就可以了(SendPacket()最典型的用法是类似于下面的语句,其中第一个参数是要被发送的数据,而第二个参数则指明将要发送数据的长度,再往后依次RTP负载类型标识时戳增量至少要用到前两个参数):

 

status = sess.SendPacket(send_buf, sizeof(send_buf), 0, false, 10);

checkerror(status);

 

f、接收实时流媒体数据,同 一个RTP 会话中允许有多个参与者( 源),通 过调用GotoFirstSourceWithData()和 GotoNextSourceWithData()函数来实现源的遍历,通过 GetNextPacket()函数提取 RTP 数据包。数据包接受以RTPSession的成员函数BeginDataAccess开始、EndDataAccess结束,BeginDataAccess确保轮询(poll)线程不会在这期间访问source table 。EndDataAccess 调用完成后,轮询(poll)线程会得到锁而继续访问。

sess.BeginDataAccess();

 

// check incoming packets if (sess.GotoFirstSourceWithData()) { do { RTPPacket *pack; uint8_t *data; size_t length; while ((pack = sess.GetNextPacket()) != NULL) { data = pack->GetPayloadData(); length = pack->GetPayloadLength(); printf("Got %d-%d: %d/%d:%s Length:%d/n", portbase, destport, i, num, data, length); sess.DeletePacket(pack); } } while (sess.GotoNextSourceWithData()); } sess.EndDataAccess();

 

下面的函数要在BeginDataAccess 和EndDataAccess之间被调用:

int RTPSession::BeginDataAccess() {

if (!created)

return ERR_RTP_SESSION_NOTCREATED;

SOURCES_LOCK

return 0;

}

int RTPSession::EndDataAccess() {

if (!created)

return ERR_RTP_SESSION_NOTCREATED;

SOURCES_UNLOCK

return 0;

}

 

/Start

• bool GotoFirstSource()
      开始递归参与者的第一个流,如果找到了,就返回tree,否则返回false。ps:我们通过这个函数和下面的GotoNextSource遍历source table中的每一个source。
      bool RTPSources::GotoFirstSource(){

sourcelist.GotoFirstElement();

if (sourcelist.HasCurrentElement())

return true;

return false;

}

• bool GotoNextSource()
      设置当前的源(source)为source table中的下一个源。如果已经到尾部了就返回false.

bool RTPSources::GotoNextSource(){

sourcelist.GotoNextElement();

if (sourcelist.HasCurrentElement())

return true;

return false;

}

• bool GotoPreviousSource()
      设置当前的源(source)为source table中上一个源。如果已经到头部了就返回false.
bool RTPSources::GotoPreviousSource(){ sourcelist.GotoPreviousElement(); if (sourcelist.HasCurrentElement()) return true; return false; }
• bool GotoFirstSourceWithData()
      开始递归参与者中第一个有RTP数据的流,如果找到了,就返回tree,否则返回false。PS:在接收数据是我们常用的是这套函数,因为如果没有数据要来都没用。

bool RTPSources::GotoFirstSourceWithData(){

bool found = false;


sourcelist.GotoFirstElement();

while (!found && sourcelist.HasCurrentElement()){

RTPInternalSourceData *srcdat;


srcdat = sourcelist.GetCurrentElement();

if (srcdat->HasData())

found = true;

else

sourcelist.GotoNextElement();

}


return found;

}

• bool GotoNextSourceWithData()
      设置当前的源(source)为source table中有RTP数据的下一个源。如果已经到尾部了就返回false.

bool RTPSources::GotoNextSourceWithData(){

bool found = false;


sourcelist.GotoNextElement();

while (!found && sourcelist.HasCurrentElement()){

RTPInternalSourceData *srcdat;


srcdat = sourcelist.GetCurrentElement();

if (srcdat->HasData())

found = true;

else

sourcelist.GotoNextElement();

}


return found;

}

• bool GotoPreviousSourceWithData()
      设置当前的源(source)为source table中有RTP数据的上一个源。如果已经到头部了就返回false.

bool RTPSources::GotoPreviousSourceWithData(){

bool found = false;


sourcelist.GotoPreviousElement();

while (!found && sourcelist.HasCurrentElement()){

RTPInternalSourceData *srcdat;


srcdat = sourcelist.GetCurrentElement();

if (srcdat->HasData())

found = true;

else

sourcelist.GotoNextElement();

}


return found;

}

• RTPSourceData *GetCurrentSourceInfo()
      返回当前参与者的当前源(source)的RTPSourceData 实列。ps:返回的这个RTPSourceData 就是本进程从期它参与者的RTCP数据包中收集得到的信息,对我们来说其实很有用,只是作者的例程没有用上,国内的网络也没有提到。在RFC3550中有关RTCP的东西都在这了,看过RFC3550的人都知到,里头谈得最多的就是RTCP。这个类我们以后会专门说。

RTPSourceData *RTPSources::GetCurrentSourceInfo(){

if (!sourcelist.HasCurrentElement())

return 0;

return sourcelist.GetCurrentElement();

}     

• RTPSourceData *GetSourceInfo(uint32 t ssrc)
      返回由ssrc指定的RTPSourceData ,或都NULL(当这个条目不存在)。ps:这个函数也很有用。因为GetCurrentSourceInfo只有在GotoFirstSource等上下文当中才能用。如果我们是在RTPSource子类的成员函数中,我们没有这个上下文,就只能用这个函数。

RTPSourceData *RTPSources::GetSourceInfo(uint32_t ssrc){

if (sourcelist.GotoElement(ssrc) < 0)

return 0;

if (!sourcelist.HasCurrentElement())

return 0;

return sourcelist.GetCurrentElement();

• RTPPacket *GetNextPacket()
      得到当前参与者当前媒体流的下一个RTP数据包。

 

/End

g、数据传输结束,释放资源

//释放资源最多等待十秒超时,然后释放所有占有资源

sess.BYEDestroy(RTPTime(10,0),0,0); 


三、上面代码和媒体流没关,就是example里就发发一些数字把JRTPLIB测试过而已。

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
如果在Android开发中遇到了"Unsupport mime 'audio/g711-alaw'"的错误提示,这是由于Android系统本身并不支持G711A和G711U的音频格式,而只支持G711标准的PCM编码格式。因此,如果要在Android中使用G711A或G711U的音频格式,需要使用第三方的库进行编解码。 这里推荐使用JRTPLIB库,它是一个开源的RTP协议库,支持G711A、G711U、G722、L16等音频格式的编解码。使用JRTPLIB库可以方便地实现RTP协议的编解码,并支持自定义的音频格式。 以下是使用JRTPLIB库进行G711A编解码的示例代码: ```java // 初始化JRTPLIB库 jrtplib::RTPSessionParams sessionParams; sessionParams.SetOwnTimestampUnit(1.0/8000.0); jrtplib::RTPSession rtpSession; rtpSession.Create(sessionParams); // 创建G711A编解码器 jrtplib::G711Codec g711Codec; // 编码 unsigned char encodedData[1024]; int encodedDataLen = 0; g711Codec.Encode(g711Data, g711DataLen, encodedData, 1024, encodedDataLen); // 解码 unsigned char decodedData[1024]; int decodedDataLen = 0; g711Codec.Decode(encodedData, encodedDataLen, decodedData, 1024, decodedDataLen); ``` 在上述代码中,首先使用jrtplib::RTPSessionParams对象设置了音频采样率,并创建了一个RTPSession对象。然后使用jrtplib::G711Codec对象进行G711A的编解码操作。 需要注意的是,JRTPLIB库需要在C++环境下编写,并使用JNI技术将C++代码与Java代码进行交互。因此,使用JRTPLIB库需要一定的C++编程经验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值