Main Page
JRTPLIB
Author:
Jori Liesenborgs
Developed at the The Expertise Centre for Digital Media (EDM), a research institute of the Hasselt University
Acknowledgment(致谢)
I would like thank the people at the Expertise Centre for Digital Media for giving me the opportunity to create this rewrite of the library.
Introduction
This document describes JRTPLIB, an object-oriented library written in C++ which aims to help developers in using the Real-time Transport Protocol (RTP) as described in RFC 3550.
译:这个文档描述了JRTPLIB,一个用C++编写的面向对象的库,旨在帮助开发者使用RFC 3550中描述的实时传输协议(RTP)。
The library makes it possible for the user to send and receive data using RTP, without worrying about SSRC collisions, scheduling and transmitting RTCP data etc. The user only needs to provide the library with the payload data to be sent and the library gives the user access to incoming RTP and RTCP data.
译:这个库确保用户使用RTP发送和接收数据成为可能,而不用担心SSRC冲突,调度和发送RTCP数据等。用户只需要提供库和要发送的载荷数据,库就可以对进来的RTP和RTCP数据提供通道。
Design idea(设计理念)
The library provides several classes which can be helpful in creating RTP applications. Most users will probably only need the RTPSession class for building an application. This class provides the necessary functions for sending RTP data and handles the RTCP part internally.
这个库提供了几个可以帮助创建RTP应用程序的类。大多数用户将很可能只需要RTPSession类来建立一个应用程序。这个类(RTPSession类)提供了发送RTP数据和内部处理RTCP部分的必要功能。
Changes from version 2.x
One of the most important changes is probably the fact that this version is based on RFC 3550 and the 2.x versions were based upon RFC 1889 which is now obsolete.
译:最重要的改变之一实际上是这个版本(3.x版本)是基于RFC 3550而2.x版本是基于现在已经淘汰的RFC 1889.
Also, the 2.x series was created with the idea that the user would only need to use the RTPSession class which meant that the other classes were not very useful by themselves. This version on the other hand, aims to provide many useful components to aid the user in building RTP capable applications.
译:而且,2.x系列是基于用户将仅仅需要使用 RTPSession 类的想法,而这意味着其他类并不是很有用。另一方面,这个版本(3.x版本)提供了许多有用的东西,旨在帮助用户建立RTP应用程序。
In this version, the code which is specific for the underlying protocol by which RTP packets are transported, is bundled in a class which inherits its interface from a class called RTPTransmitter. This makes it easy for different underlying protocols to be supported. Currently there is support for UDP over IPv4 and UDP over IPv6.
译:在这个版本中,有一个专门针对传输RTP包的底层协议的代码,这个代码被捆绑成一个类,这个类继承了一个叫RTPTransmitter类的接口。这使得支持不同的底层协议变得容易。目前支持通过IPv4的UDP协议和通过IPv6的UDP协议。
For applications such as a mixer or translator using the RTPSession class will not be a good solution. Other components can be used for this purpose: a transmission component, an SSRC table, an RTCP scheduler etc. Using these, it should be much easier to build all kinds of applications.
译:对于像mixer或translator这样的应用程序,使用RTPSession类并不是一个好的解决方案。其他组件可以被用于以下目的:一个传输组件,一个SSRC表,一个RTCP调度等。使用这些,应该是更容易建立所有类型的应用程序。
Copyright license(版权许可)
The library code uses the following copyright license:
译:库代码使用以下版权许可:
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
译:现准许任何人免费 获得本软件及相关文档文件的副本 (“软件”),对软件的处理没有任何限制, 包括但不限于使用,复制,修改,合并的权利, 出版,分发,再许可和/或销售软件的副本, 并允许该软件的布置,这样做的人, 受限以下条件:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
译:上述版权声明和本许可通知应 包含在所有的副本或实质性部分的软件。
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
该软件提供的“AS IS” ,不附带任何 明示或暗示,包括但不仅限于 适销性,针对特定用途的适用性的 不侵权的。在任何情况下,作者或版权持有人 争取承担任何索赔,损害赔偿或其他责任,无论是在一个 ,合同,侵权行为或其他形式,从中产生,信息或采取行动 连接与软件或使用或其他买卖 软件。
There are two reasons for using this license. First, since this is the license of the 2.x series, it only seemed natural that this rewrite would contain the same license. Second, since the RTP protocol is deliberately incomplete RTP profiles can, for example, define additional header fields. The best way to deal with this is to adapt the library code itself and that's why I like to keep the license as free as possible.
译:使用这个许可证有两个原因。第一,因为这是2.x系列的许可证,这儿的重写将包含相同的许可证似乎是自然而然的。第二,因为RTP协议是故意不完整的RTP配置文件,例如,额外的头字段定义。处理这个问题的最好方式是改变库代码本身,这就是为什么我想尽可能保持免费许可证的原因。
Getting started with the RTPSession class(RTPSession类入门)
All classes and functions are part of the jrtplib namespace, so to simplify the code a bit, we'll declare that we're using this namespace:
译:所有的类和函数都是 jrtplib 命名空间的一部分,所以为了简化代码一点点,我们声明我们将使用这个命名空间:
using namespace jrtplib;
To use RTP, you'll have to create an RTPSession object. The constructor accepts two parameter, an instance of an RTPRandom object, and an instance of an RTPMemoryManager object. For now, we'll keep it simple and use the default settings, so this is our code so far:
译:为了使用RTP,你必须创建一个RTPSession对象。构造函数接受两个参数,一个RTPRandom对象实例和一个RTPMemoryManager对象的实例。现在,我们将使它简单点,使用默认设置,所以这就是我们目前的代码:
RTPSession session;
To actually create the session, you'll have to call the Create member function which takes three arguments: the first one is of type RTPSessionParams and specifies the general options for the session. One parameter of this class must be set explicitly, otherwise the session will not be created successfully. This parameter is the timestamp unit of the data you intend to send and can be calculated by dividing a certain time interval (in seconds) by the number of samples in that interval. So, assuming that we'll send 8000 Hz voice data, we can use this code:
译:为了实际创建会话,你必须调用Create成员函数,它有三个参数:第一个是RTPSessionParams型,指定了会话的一般选项。这个类的一个参数必须明确设置,否则会话将不会创建成功。这个参数是你打算发送数据的时间戳,它可以通过计算某个固定的时间除以固定时间内的抽样数的值来获得。所以,假设我们将发送8000Hz 的音频数据,我们就可以使用这个代码:
RTPSessionParams sessionparams;
sessionparams.SetOwnTimestampUnit(1.0/8000.0);
The other session parameters will probably depend on the actual RTP profile you intend to work with.
译:其他会话参数将很可能取决于你打算使用的RTP profile。
The second argument of the Create function is a pointer to an RTPTransmissionParams instance and describes the parameters for the transmission component. The third parameter selects the type of transmission component which will be used. By default, an UDP over IPv4 transmitter is used, and for this particular transmitter, the transmission parameters should be of type RTPUDPv4TransmissionParams. Assuming that we want our RTP portbase to be 8000, we can do the following:
译:Create函数的第二个参数是一个指向RTPTransmissionParams实例的指针,描述了transmission组件的参数。第三个参数设置了即将被用的transmission组件的类型。默认情况下,使用一个通过IPv4的UDP的 transmitter,对于这个特定的transmitter,这个transmission参数将会是RTPUDPv4TransmissionParams类型的。假设我们想要RTP的端口是8000,我们像下面这样做:
RTPUDPv4TransmissionParams transparams;
transparams.SetPortbase(8000);
Now, we're ready to call the Create member function of RTPSession. The return value is stored in the integer status so we can check if something went wrong. If this value is negative, it indicates that some error occurred. A description of what this error code means can be retrieved by calling RTPGetErrorString:
译:现在,我们准备调用RTPSession的Create成员函数,返回值储存为整数状态(status)。所以我们可以检测是否有什么出错。如果这个值为负数,它表明有错误发生。对错误代码意味着什么的描述可以通过调用RTPGetErrorString来检索。
int status = session.Create(sessionparams,&transparams);
if (status < 0)
{
std::cerr << RTPGetErrorString(status) << std::endl;
exit(-1);
}
If the session was created with success, this is probably a good point to specify to which destinations RTP and RTCP data should be sent. This is done by a call to the RTPSession member function AddDestination. This function takes an argument of type RTPAddress. This is an abstract class and for the UDP over IPv4 transmitter the actual class to be used is RTPIPv4Address. Suppose that we want to send our data to a process running on the same host at port 9000, we can do the following:
译:如果这个会话创建成功,这将是一个指定要发送的RTP和RTCP数据的目的地的好机会。可以通过调用RTPSession成员函数AddDestination来实现。这个函数拥有一个RTPAddress类型的参数。这是一个抽象的类,对于通过IPv4传输的UDP transmitter来说,真正使用的类是RTPIPv4Address。假设我们想要把数据发送给一个正在相同主机端口号为9000的正在运行的进程,我们向下面这样做:
uint8_t localip[]={127,0,0,1};
RTPIPv4Address addr(localip,9000);
status = session.AddDestination(addr);
if (status < 0)
{
std::cerr << RTPGetErrorString(status) << std::endl;
exit(-1);
}
If the library was compiled with JThread support, incoming data is processed in the background. If JThread support was not enabled at compile time or if you specified in the session parameters that no poll thread should be used, you'll have to call the RTPSession member function Poll regularly to process incoming data and to send RTCP data when necessary. For now, let's assume that we're working with the poll thread enabled.
译:如果这个库(JRTPLIB)编译时有JThread的支持,那么进来的数据将在后台处理。如果编译时不支持JTread或者你指定会话参数不使用poll thread,那么你将不得不调用RTPSession成员函数Poll处理进来的数据并在必要时发送RTCP数据。现在,我们假设我们可以使用poll thread。
Lets suppose that for a duration of one minute, we want to send packets containing 20 ms (or 160 samples) of silence and we want to indicate when a packet from someone else has been received. Also suppose we have L8 data as defined in RFC 3551 and want to use payload type 96. First, we'll set some default values:
译:假设一个一分钟的时间,我们想发送包含20ms(或者160个抽样点)的沉默并且想表明一个从别人来的包什么时候收到。同样假设我们有一个在RFC 3551 中定义的L8数据并使用类型为96的载荷。首先,我们将设定一些默认值:
session.SetDefaultPayloadType(96);
session.SetDefaultMark(false);
session.SetDefaultTimestampIncrement(160);
Next, we'll create the buffer which contains 160 silence samples and create an RTPTime instance which indicates 20 ms or 0.020 seconds. We'll also store the current time so we'll know when one minute has passed.
译:下面,我们将创建包含160个沉默抽样值的缓冲区,并创建一个表明20ms或0.020秒的RTPTime实例。我们将储存当前时间以便我们知道何时1分钟结束。
uint8_t silencebuffer[160];
for (int i = 0 ; i < 160 ; i++)
silencebuffer[i] = 128;
RTPTime delay(0.020);
RTPTime starttime = RTPTime::CurrentTime();
Next, the main loop will be shown. In this loop, a packet containing 160 bytes of payload data will be sent. Then, data handling can take place but this part is described later in the text. Finally, we'll wait 20 ms and check if sixty seconds have passed:
译:接下来,将展示主循环。在这个循环中,将发送一个包含160字节的包。接着,开始数据处理,但是这部分将会在本文以后来描述。最终,我们等待20ms并检查60秒是否已过。
bool done = false;
while (!done)
{
status = session.SendPacket(silencebuffer,160);
if (status < 0)
{
std::cerr << RTPGetErrorString(status) << std::endl;
exit(-1);
}
//
// Inspect incoming data here
//
RTPTime::Wait(delay);
RTPTime t = RTPTime::CurrentTime();
t -= starttime;
if (t > RTPTime(60.0))
done = true;
}
Information about participants in the session, packet retrieval etc, has to be done between calls to the RTPSession member functions BeginDataAccess and EndDataAccess. This ensures that the background thread doesn't try to change the same data you're trying to access. We'll iterate over the participants using the GotoFirstSource and GotoNextSource member functions. Packets from the currently selected participant can be retrieved using the GetNextPacket member function which returns a pointer to an instance of the RTPPacket class. When you don't need the packet anymore, it has to be deleted. The processing of incoming data will then be as follows:
译:会话中的参与者,包检索等信息需要调用RTPSession成员函数 BeginDataAccess和EndDataAccess。这确保了后台线程不会尝试改变你试图访问的相同数据。我们通过使用成员函数GotoFirstSource和GotoNextSource遍历会话参与者。来自当前选定的会话参与者的包通过使用成员函数GetNextPacket来检索,返回一个指向RTPPacket类实例的指针。当你不再需要这个包的时候,它不得不删除。传入数据的处理如下:
session.BeginDataAccess();
if (session.GotoFirstSource())
{
do
{
RTPPacket *packet;
while ((packet = session.GetNextPacket()) != 0)
{
std::cout << "Got packet with extended sequence number "
<< packet->GetExtendedSequenceNumber()
<< " from SSRC " << packet->GetSSRC()
<< std::endl;
session.DeletePacket(packet);
}
} while (session.GotoNextSource());
}
session.EndDataAccess();
Information about the currently selected source can be obtained by using the GetCurrentSourceInfo member function of the RTPSession class. This function returns a pointer to an instance of RTPSourceData which contains all information about that source: sender reports from that source, receiver reports, SDES info etc.
译:当前选定的源的信息可以通过使用RTPSession类的成员函数GetCurrentSourceInfo来获得。这个函数返回一个指向RTPSourceData实例的指针,这个实例包含了关于那个源的所有信息:来自这个源的发送报告,接收报告,SDES信息等。
When the main loop is finished, we'll send a BYE packet to inform other participants of our departure and clean up the RTPSession class. Also, we want to wait at most 10 seconds for the BYE packet to be sent, otherwise we'll just leave the session without sending a BYE packet.
译:当这个主循环完成后,我们将发送一个BYE包通知其他会话参与者清除RTPSession类。同样地,我们只想最多10秒等待即将发送的BYE包,否则我们将不发送BYE包就离开这个会话。
delay = RTPTime(10.0);
session.BYEDestroy(delay,"Time's up",9);
The complete code of the program is given in example2.cpp.
译:这个程序的完整代码在example2.cpp中给出。
Error codes
Unless specified otherwise, functions with a return type int will return a negative value when an error occurred and zero or a positive value upon success. A description of the error code can be obtained by using the RTPGetErrorString function, declared in rtperrors.h
译:除非指定,否则,当发生错误时,带有整型返回值的函数将会返回一个负值,零和正值表示成功。错误代码的描述可以通过使用在rtperrors.h中声明的RTPGetErrorString函数来获得。
Memory management(内存管理)
You can write you own memory manager by deriving a class from RTPMemoryManager. The following example shows a very basic implementation.
译:你可以通过派生RTPMemoryManager类来写自己的内存管理。下面的例子展示了一个非常基本的实现。
class MyMemoryManager : public RTPMemoryManager
{
public:
MyMemoryManager() { }
~MyMemoryManager() { }
void *AllocateBuffer(size_t numbytes, int memtype)
{
return malloc(numbytes);
}
void FreeBuffer(void *p)
{
free(p);
}
};
In the constructor of RTPSession, you can specify that you would like to use this memory manager:
译:在RTPSession的构造函数中,你可以指定你将会使用这个内存管理。
MyMemoryManager mgr;
RTPSession session(0, &mgr);
Now, all memory allocation and deallocation will be done using the AllocateBuffer and FreeBuffer implementations of mgr.
译:现在,所有的内存分配和释放都将通过使用mgr中的AllocateBuffer 和 FreeBuffer实现来完成。
The second parameter of the RTPMemoryManager::AllocateBuffer member function indicates what the purpose is of this memory block. This allows you to handle different kinds of data in different ways.
译:RTPMemoryManager::AllocateBuffer成员函数的第二个参数表明了这个内存块的目的是什么。这允许你以不同的方式处理不同类型的数据。
With the introduction of the memory management system, the RTPSession class was extended with member function RTPSession::DeletePacket and RTPSession::DeleteTransmissionInfo. These functions should be used to deallocate RTPPacket instances and RTPTransmissionInfo instances respectively.
译:由于内存管理系统的引进,RTPSession类扩展了成员函数RTPSession::DeletePacket 和 RTPSession::DeleteTransmissionInfo。这些成员函数分别被用于释放RTPPacket实例和 RTPTransmissionInfo 实例。
Contact
If you have any questions, remarks or requests about the library or if you think you've discovered a bug, you can contact me at jori(dot)liesenborgs(at)gmail(dot)com
The home page of the library is http://research.edm.uhasselt.be/jori/jrtplib/jrtplib.html
There is also a mailing list for the library. To subscribe to the list, send an e-mail to jrtplib-subscribe(at)edm(dot)uhasselt(dot)be and you'll receive further instructions.
译:如果关于图书馆你有任何问题,言论或要求,或者你认为你已经发现了一个错误的,你可以联系我的[email protected]
库的主页http://research.edm.uhasselt.be/jori/jrtplib/jrtplib.html
还有库的邮件列表。要订阅到列表中,请发送电子邮件 [email protected],您会收到进一步的说明。