jrtplib.h 14.7 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
namespace jrtplib // So that links are created automatically
{

/**

\htmlonly
<style type="text/css">
body {
    counter-reset: section;
}

h2 {
    counter-increment: section;
    counter-reset: subsection;
}

h3 {
    counter-increment: subsection;
    counter-reset: subsubsection;
}

h4 {
    counter-increment: subsubsection;
}

h2:before {
    content: counter(section) ". ";
}

h3:before {
    content: counter(section) "." counter(subsection) " ";
}

h4:before {
    content: counter(section) "." counter(subsection) "." counter(subsubsection) " ";
}
</style>
\endhtmlonly

\mainpage JRTPLIB

\author Jori Liesenborgs
\author Developed at the the [Expertise Centre for Digital Media (EDM)](http://www.edm.uhasselt.be), 
a research institute of the [Hasselt University](http://www.uhasselt.be)

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](https://www.ietf.org/rfc/rfc3550.txt).

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.

### 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, or derive a class
from RTPSecureSession for SRTP support. These classes
provide the necessary functions for sending RTP data and handle
the RTCP part internally.

### 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.

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.

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.

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.

Copyright license
-----------------
    
The library code uses the following copyright license:

~~~{.c}
    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.
~~~

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.

Getting started with the RTPSession class
-----------------------------------------
    
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:

~~~{.cpp}
    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:

~~~{.cpp}
    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:

~~~{.cpp}
    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. 

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:

~~~{.cpp}
    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:

~~~{.cpp}
    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:
    
~~~{.cpp}
    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.

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:
    
~~~{.cpp}
    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.
    
~~~{.cpp}
    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:
    
~~~{.cpp}
    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, can be done between calls to the RTPSession member
functions RTPSession::BeginDataAccess and RTPSession::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 
RTPSession::GotoFirstSource and RTPSession::GotoNextSource member functions. 
Packets from 
the currently selected participant can be retrieved using the 
RTPSession::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:
    
~~~{.cpp}
    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. 

Alternatively, packets can also be handled directly, without iterating
over the sources, by overriding the RTPSession::OnValidatedRTPPacket
member function. The example code in `example6.cpp` illustrates this
approach.

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.
    
~~~{.cpp}
    delay = RTPTime(10.0);
    session.BYEDestroy(delay,"Time's up",9);
~~~
    
The complete code of the program is given in `example2.cpp`.

SRTP support
------------

Support for Secure RTP (SRTP) is provided through the RTPSecureSession
class, which used [libsrtp](https://github.com/cisco/libsrtp) for
encryption/decryption of the data. This class itself is not meant to provide 
a complete ready-to-use solution, since there's a wide variety of options
that can be configured in `libsrtp`.

Instead, the class provides a means to initialize a `libsrtp`-context,
which you can then obtain and configure further for your needs. Incoming
and outgoing packets will be decrypted and encrypted respectively, using
the context that was constructed and completed this way.

The example code in `example7.cpp` illustrates the use of this class, where
a single key for sender and receiver is used, together with the default
encryption algorithm.

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                                            

Memory management
-----------------

You can write you own memory manager by deriving a class from RTPMemoryManager.
The following example shows a very basic implementation.
    
~~~{.cpp}
    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:
    
~~~{.cpp}
    MyMemoryManager mgr;
    RTPSession session(0, &mgr);
~~~

Now, all memory allocation and deallocation will be done using the `AllocateBuffer`
and `FreeBuffer` implementations of `mgr`.

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.

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.

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.
Special thanks go to Wim Lamotte for getting me started on the RTP
journey many years ago.

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

*/

}