jrtplib-3.7.1笔记--流程一遍

转自http://blog.csdn.net/heanyu/article/details/6077827

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

[c-sharp]  view plain copy
  1. //函数原型 jrtplib-3.7.1/src/rtpsession.cpp(151)  
  2. int RTPSession::Create(const RTPSessionParams &sessparams, RTPTransmitter *transmitter)  
  3. {  
  4.     int status;  
  5.       
  6.     if (created)  
  7.         return ERR_RTP_SESSION_ALREADYCREATED;  
  8.     usingpollthread = sessparams.IsUsingPollThread();  
  9.     useSR_BYEifpossible = sessparams.GetSenderReportForBYE();  
  10.     sentpackets = false;  
  11.       
  12.     // Check max packet size  
  13.       
  14.     if ((maxpacksize = sessparams.GetMaximumPacketSize()) < RTP_MINPACKETSIZE)  
  15.         return ERR_RTP_SESSION_MAXPACKETSIZETOOSMALL;  
  16.           
  17.     rtptrans = transmitter;  
  18.     if ((status = rtptrans->SetMaximumPacketSize(maxpacksize)) < 0)  
  19.         return status;  
  20.     deletetransmitter = false;  
  21.     return InternalCreate(sessparams);  
  22. }  
 

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

sessparams.SetOwnTimestampUnit(1.0/8000.0);

[cpp]  view plain copy
  1. int RTPSession::SetTimestampUnit(double u)  
  2. {  
  3.     if (!created)  
  4.         return ERR_RTP_SESSION_NOTCREATED;  
  5.     int status;  
  6.     BUILDER_LOCK  
  7.     status = rtcpbuilder.SetTimestampUnit(u);  
  8.     BUILDER_UNLOCK  
  9.     return status;  
  10. }  

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

[cpp]  view plain copy
  1. //函数原型 jrtplib-3.7.1/src/rtppacketbuilder.h(215)  
  2. inline int RTPPacketBuilder::SetDefaultPayloadType(uint8_t pt)  
  3. {  
  4.     if (!init)  
  5.         return ERR_RTP_PACKBUILD_NOTINIT;  
  6.     defptset = true;  
  7.     defaultpayloadtype = pt;  
  8.     return 0;  
  9. }  
  10. inline int RTPPacketBuilder::SetDefaultMark(bool m)  
  11. {  
  12.     if (!init)  
  13.         return ERR_RTP_PACKBUILD_NOTINIT;  
  14.     defmarkset = true;  
  15.     defaultmark = m;  
  16.     return 0;  
  17. }  
  18. inline int RTPPacketBuilder::SetDefaultTimestampIncrement(uint32_t timestampinc)  
  19. {  
  20.     if (!init)  
  21.         return ERR_RTP_PACKBUILD_NOTINIT;  
  22.     deftsset = true;  
  23.     defaulttimestampinc = timestampinc;  
  24.     return 0;  
  25. }  
 

 

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

 

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

checkerror(status);

[cpp]  view plain copy
  1. 函数原型 jrtplib-3.7.1/src/rtpsession.cpp(552)  
  2. int RTPSession::SendPacket(const void *data,size_t len)  
  3. {  
  4.     int status;  
  5.       
  6.     if (!created)  
  7.         return ERR_RTP_SESSION_NOTCREATED;  
  8.     BUILDER_LOCK  
  9.     if ((status = packetbuilder.BuildPacket(data,len)) < 0)  
  10.     {  
  11.         BUILDER_UNLOCK  
  12.         return status;  
  13.     }  
  14.     if ((status = rtptrans->SendRTPData(packetbuilder.GetPacket(),packetbuilder.GetPacketLength())) < 0)  
  15.     {  
  16.         BUILDER_UNLOCK  
  17.         return status;  
  18.     }  
  19.     BUILDER_UNLOCK  
  20.     SOURCES_LOCK  
  21.     sources.SentRTPPacket();  
  22.     SOURCES_UNLOCK  
  23.     PACKSENT_LOCK  
  24.     sentpackets = true;  
  25.     PACKSENT_UNLOCK  
  26.     return 0;  
  27. }  
  28. int RTPSession::SendPacket(const void *data,size_t len,  
  29.                 uint8_t pt,bool mark,uint32_t timestampinc)  
  30. {  
  31.     int status;  
  32.     if (!created)  
  33.         return ERR_RTP_SESSION_NOTCREATED;  
  34.       
  35.     BUILDER_LOCK  
  36.     if ((status = packetbuilder.BuildPacket(data,len,pt,mark,timestampinc)) < 0)  
  37.     {  
  38.         BUILDER_UNLOCK  
  39.         return status;  
  40.     }  
  41.     if ((status = rtptrans->SendRTPData(packetbuilder.GetPacket(),packetbuilder.GetPacketLength())) < 0)  
  42.     {  
  43.         BUILDER_UNLOCK  
  44.         return status;  
  45.     }  
  46.     BUILDER_UNLOCK  
  47.       
  48.     SOURCES_LOCK  
  49.     sources.SentRTPPacket();  
  50.     SOURCES_UNLOCK  
  51.     PACKSENT_LOCK  
  52.     sentpackets = true;  
  53.     PACKSENT_UNLOCK  
  54.     return 0;  
  55. }  
  56. int RTPSession::SendPacketEx(const void *data,size_t len,  
  57.                   uint16_t hdrextID,const void *hdrextdata,size_t numhdrextwords)  
  58. {  
  59.     int status;  
  60.       
  61.     if (!created)  
  62.         return ERR_RTP_SESSION_NOTCREATED;  
  63.     BUILDER_LOCK  
  64.     if ((status = packetbuilder.BuildPacketEx(data,len,hdrextID,hdrextdata,numhdrextwords)) < 0)  
  65.     {  
  66.         BUILDER_UNLOCK  
  67.         return status;  
  68.     }  
  69.     if ((status = rtptrans->SendRTPData(packetbuilder.GetPacket(),packetbuilder.GetPacketLength())) < 0)  
  70.     {  
  71.         BUILDER_UNLOCK  
  72.         return status;  
  73.     }  
  74.     BUILDER_UNLOCK  
  75.     SOURCES_LOCK  
  76.     sources.SentRTPPacket();  
  77.     SOURCES_UNLOCK  
  78.     PACKSENT_LOCK  
  79.     sentpackets = true;  
  80.     PACKSENT_UNLOCK  
  81.     return 0;  
  82. }  
  83. int RTPSession::SendPacketEx(const void *data,size_t len,  
  84.                   uint8_t pt,bool mark,uint32_t timestampinc,  
  85.                   uint16_t hdrextID,const void *hdrextdata,size_t numhdrextwords)  
  86. {  
  87.     int status;  
  88.       
  89.     if (!created)  
  90.         return ERR_RTP_SESSION_NOTCREATED;  
  91.       
  92.     BUILDER_LOCK  
  93.     if ((status = packetbuilder.BuildPacketEx(data,len,pt,mark,timestampinc,hdrextID,hdrextdata,numhdrextwords)) < 0)  
  94.     {  
  95.         BUILDER_UNLOCK  
  96.         return status;  
  97.     }  
  98.     if ((status = rtptrans->SendRTPData(packetbuilder.GetPacket(),packetbuilder.GetPacketLength())) < 0)  
  99.     {  
  100.         BUILDER_UNLOCK  
  101.         return status;  
  102.     }  
  103.     BUILDER_UNLOCK  
  104.     SOURCES_LOCK  
  105.     sources.SentRTPPacket();  
  106.     SOURCES_UNLOCK  
  107.     PACKSENT_LOCK  
  108.     sentpackets = true;  
  109.     PACKSENT_UNLOCK  
  110.     return 0;  
  111. }  
 

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数据包。

[cpp]  view plain copy
  1. inline RTPPacket *RTPSourceData::GetNextPacket()  
  2. {  
  3.     if (!validated)  
  4.         return 0;  
  5.     RTPPacket *p;  
  6.     if (packetlist.empty())  
  7.         return 0;  
  8.     p = *(packetlist.begin());  
  9.     packetlist.pop_front();  
  10.     return p;  
  11. }  
  12. /  
  13. RTPPacket *RTPSources::GetNextPacket()  
  14. {  
  15.     if (!sourcelist.HasCurrentElement())  
  16.         return 0;  
  17.       
  18.     RTPInternalSourceData *srcdat = sourcelist.GetCurrentElement();  
  19.     RTPPacket *pack = srcdat->GetNextPacket();  
  20.     return pack;  
  21. }  
  22. //  
  23. RTPRawPacket *RTPUDPv4Transmitter::GetNextPacket()  
  24. {  
  25.     if (!init)  
  26.         return 0;  
  27.       
  28.     MAINMUTEX_LOCK  
  29.       
  30.     RTPRawPacket *p;  
  31.       
  32.     if (!created)  
  33.     {  
  34.         MAINMUTEX_UNLOCK  
  35.         return 0;  
  36.     }  
  37.     if (rawpacketlist.empty())  
  38.     {  
  39.         MAINMUTEX_UNLOCK  
  40.         return 0;  
  41.     }  
  42.     p = *(rawpacketlist.begin());  
  43.     rawpacketlist.pop_front();  
  44.     MAINMUTEX_UNLOCK  
  45.     return p;  
  46. }  
  47. //  
  48. RTPRawPacket *RTPUDPv6Transmitter::GetNextPacket()  
  49. {  
  50.     if (!init)  
  51.         return 0;  
  52.       
  53.     MAINMUTEX_LOCK  
  54.       
  55.     RTPRawPacket *p;  
  56.       
  57.     if (!created)  
  58.     {  
  59.         MAINMUTEX_UNLOCK  
  60.         return 0;  
  61.     }  
  62.     if (rawpacketlist.empty())  
  63.     {  
  64.         MAINMUTEX_UNLOCK  
  65.         return 0;  
  66.     }  
  67.     p = *(rawpacketlist.begin());  
  68.     rawpacketlist.pop_front();  
  69.     MAINMUTEX_UNLOCK  
  70.     return p;  
  71. }  
 

/End

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

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

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


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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值