- 论坛徽章:
- 0
|
文档:http://research.edm.uhasselt.be/jori/jrtplib/documentation/index.html一些介绍:
http://doserver.net/read.php?1028http://doserver.net/read.php/1027.htmhttp://doserver.net/read.php/1685.htm现在开始对几个example分析一下,不过,就没有文档吗?这里,详细的解释了几个例子:http://hi.baidu.com/hanyuejun2006/blog/item/8a8ed939a9e344f53b87ce23.html这里算一个:http://xiyong8260.blog.163.com/blog/static/665146212008824112748499/重要的是里面还讲了一些嵌入式方面的内容。收和发的例子:http://blog.chinaunix.net/u2/61880/showart_728528.html比较详细的系列文章:http://www.cnitblog.com/tinnal/archive/2009/01/01/53342.html
PS:关于POLL的问题,发现只有调用者这个函数的时候,才会查询是否有包发过来,才会接收包。If you're not using the poll thread, this function must be called regularly to
process incoming data and to send RTCP data when necessary.关于例程可以分为以下几个部分:
(一)BASIC USAGE:1)SET PARAMS 关于会话的参数2)SET TRANSPARAMS 关于传输层的参数3)CREATE SESSION 创建会话4)SET PACKET ATTRIBUTES 包的属性设置5)ADD DESTINATION 添加目的地址 6)PREPARE DATA 准备数据7)SEND PACKET 发送包8)DATAACCESS 数据处理(锁定操作,是指此时POLL线程不能改变正在处理的数据)9)BYE 退出会话example:#include "rtpsession.h"#include "rtppacket.h"#include "rtpudpv4transmitter.h"#include "rtpsessionparams.h"#include "rtperrors.h"#include "rtpipv4address.h"#include netinet/in.h>#include arpa/inet.h>#include stdio.h>#include stdlib.h>#include iostream>#include string>void checkerror(int rtperr){ if(rtperr 0) { std::cout"ERROR:"RTPGetErrorString(rtperr)std::endl; exit(-1); }}int main(){ RTPSession sess; uint16_t portbase; int status; //set params RTPSessionParams sessparams; sessparams.SetOwnTimestampUnit(1.0/10.0); sessparams.SetAcceptOwnPackets(true); //set transparams RTPUDPv4TransmissionParams transparams; transparams.SetPortbase(8000); //create a session status = sess.Create(sessparams, &transparams); checkerror(status); //set default packet attributes sess.SetDefaultPayloadType(0); sess.SetDefaultMark(false); sess.SetDefaultTimestampIncrement(10); //destination address uint16_t destport; uint32_t destip; std::string ipstr; std::cout"Enter the IP: "std::endl; std::cin>>ipstr; destip = inet_addr(ipstr.c_str()); if(destip == INADDR_NONE) { std::cerr"Bad IP"std::endl; exit(-1); } destip = ntohl(destip); //uint8_t localip[] = {192, 168, 0, 3}; RTPIPv4Address addr(destip, 8000); //add destination status = sess.AddDestination(addr); checkerror(status); //prepare payload data uint8_t silencebuffer[160]; for(int i = 0; i 160; i++) { silencebuffer = 128; } //for time checking RTPTime delay(0.020); RTPTime starttime = RTPTime::CurrentTime(); bool done = false; sess.Poll(); while(!done) { status = sess.SendPacket(silencebuffer, 160); checkerror(status); //receive data sess.BeginDataAccess(); if(sess.GotoFirstSource()) { do { RTPPacket *pack; while((pack = sess.GetNextPacket()) != NULL) { std::cout"Got packet!"std::endl; std::cout"sequence number: "pack->GetSequenceNumber()std::endl; std::cout"Extended SN: "pack->GetExtendedSequenceNumber()std::endl; std::cout"SSRC: "pack->GetSSRC()std::endl; std::cout"Data: "pack->GetPayloadData()std::endl; sess.DeletePacket(pack); } }while(sess.GotoNextSource()); } sess.EndDataAccess(); RTPTime::Wait(delay); RTPTime t = RTPTime::CurrentTime(); t -= starttime; if(t > RTPTime(6, 0)) { done = true; } } delay = RTPTime(10.0); sess.BYEDestroy(delay, "Time's up", 9); return(0);}
(二)Your Own Session:
可以继承session创建MySession添加自己定制的处理, 这些处理是由session的protected methods 定义的。参考文档可以确定要定制的处理。
example[color="#0000CC"]:
#include "rtpsession.h"
#include "rtppacket.h"
#include "rtpudpv4transmitter.h"
#include "rtpipv4address.h"
#include "rtpsessionparams.h"
#include "rtperrors.h"
#include "rtpsourcedata.h"
#include stdlib.h>
#include stdio.h>
#include iostream>
#include string>
class MyRTPSession : public RTPSession
{
protected:
[color="#0000CC"]//what to do on new source?
void OnNewSource(RTPSourceData *dat)
{
if(dat->IsOwnSSRC())
{
return;
}
uint32_t ip;
uint16_t port;
if(dat->GetRTPDataAddress() != 0)
{
const RTPIPv4Address *addr = (const RTPIPv4Address *)
(dat->GetRTPDataAddress());
ip = addr->GetIP();
port = addr->GetPort();
}
else if(dat->GetRTCPDataAddress() != 0)
{
const RTPIPv4Address *addr = (const RTPIPv4Address *)
(dat->GetRTCPDataAddress());
ip = addr->GetIP();
port = addr->GetPort()-1;
}
else
{
return;
}
RTPIPv4Address dest(ip, port);
AddDestination(dest);
struct in_addr inaddr;
inaddr.s_addr = htonl(ip);
std::cout"Adding destination"std::string(inet_ntoa(inaddr))
":"portstd::endl;
}
//what to do on byepacket?
void OnBYEPacket(RTPSourceData *dat)
{
std::cout"OnBYEPacket"std::endl;
if(dat->IsOwnSSRC())
{
return;
}
uint32_t ip;
uint16_t port;
if(dat->GetRTPDataAddress() != 0)
{
const RTPIPv4Address *addr = (const RTPIPv4Address *)
(dat->GetRTPDataAddress());
ip = addr->GetIP();
port = addr->GetPort();
}
else if(dat->GetRTCPDataAddress() != 0)
{
const RTPIPv4Address *addr = (const RTPIPv4Address *)
(dat->GetRTCPDataAddress());
ip = addr->GetIP();
port = addr->GetPort()-1;
}
else
{
return;
}
RTPIPv4Address dest(ip, port);
DeleteDestination(dest);
struct in_addr inaddr;
inaddr.s_addr = htonl(ip);
std::cout"Deleting destination"
std::string(inet_ntoa(inaddr))
":"portstd::endl;
}
//what to do on remove source?
void OnRemoveSource(RTPSourceData *dat)
{
std::cout"OnRemoveSource"std::endl;
if(dat->IsOwnSSRC())
{
return;
}
if(dat->ReceivedBYE())
{
return;
}
uint32_t ip;
uint16_t port;
if(dat->GetRTPDataAddress() != 0)
{
const RTPIPv4Address *addr = (const RTPIPv4Address *)
(dat->GetRTPDataAddress());
ip = addr->GetIP();
port = addr->GetPort();
}
else if(dat->GetRTCPDataAddress() != 0)
{
const RTPIPv4Address *addr = (const RTPIPv4Address *)
(dat->GetRTCPDataAddress());
ip = addr->GetIP();
port = addr->GetPort()-1;
}
else
{
return;
}
RTPIPv4Address dest(ip, port);
DeleteDestination(dest);
struct in_addr inaddr;
inaddr.s_addr = htonl(ip);
std::cout"Deleting destination"
std::string(inet_ntoa(inaddr))
":"portstd::endl;
}
};[color="#0000FF"]
(三)Important Classes:分析参数:RTPSourceData-HOW TO KNOW RTCP?从(二)中,你可能已经了解到了On...等定义了session在收发包时的动作,而为了分析这些包的参数,你需要的是RTPSourceData这个参数。Source指的是一个会话的参与者,在本地保存了一个参与者列表,和与之相关的信息。这些信息是从该源发送的RTCP信息提取的。为了便于和理论分析比较,现在将5中RTCP分组报告和它们的实现列出:指的注意的是:据我目前的理解是,本地维护一个源的列表,而这些RTCP分组,并没有它们的实体,而是得到这些分组后就分析为和一个源描述符,即RTPSource关联的数据。应该是遍历这个源列表,而获得它们的信息。而如何遍历的问题,稍后讨论。
发送者报告(SR)
V| P| RC| PT=SR=200| LEN|
发送者SSRC (已关联)
NTP时间戳(高32位)
SR_GetNTPTimestamp
()
NTP时间戳(低32位)
RTP时间戳
SR_GetRTPTimestamp
()
发送者分组计数器
SR_GetPacketCount
()
发送者字节计数器
SR_GetByteCount
()
...(下面是这个发送者所发送的接收者报告,在下面和RR一起讨论)
附加信息:
这个源是否有发送发送者报告
SR_HasInfo
()
这个发送者报告接收的时间
SR_GetReceiveTime
()
以及以SR_Prev_开头的,获得倒数第二个发送者报告的信息。
接收者报告(RR)
[color="#0000CC"]V| P| RC| PT=SR=201| LEN|
SSRC1(第一个接收者报告块所关联的发送者) (已关联)分组丢失率 | 丢失分组总数|
RR_GetFractionLost
()|
RR_GetPacketsLost
()扩展的最高序号
RR_GetExtendedHighestSequenceNumber
()间隔抖动
RR_GetJitter
()最新的发送者报告时间戳(LSR)
RR_GetLastSRTimestamp
()SR最新间隔(DLSR)
RR_GetLastSRTimestamp
()附加信息:这个源是否有发送接收者报告
RR_HasInfo
()接收者报告接收时间
RR_GetReceiveTime
()以及以RR_Prev_开头的,获得倒数第二个接收者报告的信息。
源描叙分组(SDES)
V| P| RC| PT=SR=202| LEN|
SSRC/CSRC1 (已关联)
SDES项
由SDES_Get...等描述
可以判断的是存在一种机制,可以在本地定制自己要发送的SDES包含的信息。
BYE分组(BYE)
V| P| RC| PT=SR=202| LEN|
SSRC/CSRC (已关联)
原因长度| 退出会话原因
GetBYEReason
(size_t *len)
附加信息(见文档)
根据以上的信息,大概知道了如何获取一个源的信息;问题是如何遍历这个源列表来处理一个源?事实上,在大多数的例子里,使用的类只是RTPSession,通过这个类可以管理会话的大部分细节。管理会话:RTPSession-MANAGE A SESSION对一次会话的管理,大概有以下几方面:*创建会话*退出会话*管理目的地址(添加,忽略)*发送和接收数据包(用户只需要关心RTP包),另外,APP包也是由用户负责的*时间戳设定*管理SDES信息项*管理源列表*管理广播组...一般来说,使用一个类都是使用它的public接口,但是你可以像(二)描述的那样,通过继承来定制自己的一些行为。On...着重讲的是管理源列表。
bool
GotoFirstSource
()
Starts the iteration over the participants by going to the
first member in the table.
[/url]
bool
[url=http://research.edm.uhasselt.be/jori/jrtplib/documentation/classRTPSources.html#65bd8698115d3b763b5ac38fc3b42f6c]GotoNextSource
()
Sets the current source to be the next source in the table.
[/url]
bool
[url=http://research.edm.uhasselt.be/jori/jrtplib/documentation/classRTPSources.html#69f37b0e4d481a59f635fc1649e28e43]GotoPreviousSource
()
Sets the current source to be the previous source in the
table.
[/url]
bool
[url=http://research.edm.uhasselt.be/jori/jrtplib/documentation/classRTPSources.html#966a98e4afea3abada097c01735e4e67]GotoFirstSourceWithData
()
Sets the current source to be the first source in the table
which has
RTPPacket
instances that we
haven't extracted yet.
[/url]
bool
[url=http://research.edm.uhasselt.be/jori/jrtplib/documentation/classRTPSources.html#46ddc7441d44b7227d5baf3574e1d33d]GotoNextSourceWithData
()
Sets the current source to be the next source in the table
which has
RTPPacket
instances that we
haven't extracted yet.
[/url]
bool
[url=http://research.edm.uhasselt.be/jori/jrtplib/documentation/classRTPSources.html#53dc16afd0189a5b1c67426be7708386]GotoPreviousSourceWithData
()
Sets the current source to be the previous source in the
table which has
RTPPacket
instances
that we haven't extracted yet.
[/url]
[url=http://research.edm.uhasselt.be/jori/jrtplib/documentation/classRTPSourceData.html]RTPSourceData
*
GetCurrentSourceInfo
()
Returns the
RTPSourceData
instance for the currently
selected participant.
RTPSourceData
*
GetSourceInfo
(uint32_t ssrc)
Returns the
RTPSourceData
instance for the participant
identified by ssrc, or NULL if no such entry exists.
bool
GotEntry
(uint32_t ssrc)
Returns true if an entry for participant
ssrc exists and false otherwise.
RTPSourceData
*
GetOwnSourceInfo
()
If present, it returns the
RTPSourceData
instance of the entry which was
created by CreateOwnSSRC.
迭代的例程:
很明显,这是C++风格,使用了迭代器的抽象。而GetCurrentSourceInfo ()和GetSourceInfo (uint32_t ssrc)的返回类型-RTPSourceData可以使我们得以获取源列表的RTCP信息。
使用这种迭代的例程:
sess.BeginDataAccess();
if(sess.GotoFirstSource())
{
do
{
RTPPacket *pack;
while((pack = sess.GetNextPacket()) != NULL)
{
//deal with the packet
sess.DeletePacket(pack);
}
}while(sess.GotoNextSource());
}
sess.EndDataAccess();
以上,我们可以获得访问源的信息的机制。
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/93494/showart_1886539.html |
|