Merge branch 'example_improvements'

# Conflicts:
#	CMakeLists.txt
#	src/formats/h264.hh
#	src/formats/h265.hh
#	src/formats/h266.hh
#	src/formats/h26x.cc
#	src/formats/h26x.hh
#	src/lib.cc
#	src/pkt_dispatch.hh
#	src/rtp.cc
#	uvgRTP.pro
This commit is contained in:
Joni Räsänen 2022-02-22 13:45:41 +02:00
commit f62ade9c70
94 changed files with 1668 additions and 549 deletions

View File

@ -1,12 +1,15 @@
# Simple CircleCI configuration for building uvgRTP
# ToDo: add tests
version: 2.1
jobs:
build:
docker:
- image: cimg/base:2021.04
resource_class: small
steps:
- checkout
- run:
@ -20,8 +23,74 @@ jobs:
command: 'cmake -H. -Bbuild'
- run:
name: Build the uvgRTP library
command: 'cmake --build build -j'
command: 'cmake --build build'
- run:
name: Build test application
# ToDo: add -lcrypto++
command: 'mkdir include/uvgrtp && cp -rf include/*.hh include/uvgrtp/ && g++ -D__RTP_NO_CRYPTO__ docs/examples/configuration.cc -Iinclude/ build/libuvgrtp.a -lpthread -o example_configuration'
name: Build binding example
command: 'g++ -D__RTP_NO_CRYPTO__ docs/examples/binding.cc -Iinclude/ build/libuvgrtp.a -lpthread -o example_binding'
- run:
name: Run binding example
command: './example_binding'
- run:
name: Build configuration example
command: 'g++ -D__RTP_NO_CRYPTO__ docs/examples/configuration.cc -Iinclude/ build/libuvgrtp.a -lpthread -o example_configuration'
- run:
name: Run configuration example
command: './example_configuration'
- run:
name: Build timestamp example
command: 'g++ -D__RTP_NO_CRYPTO__ docs/examples/custom_timestamps.cc -Iinclude/ build/libuvgrtp.a -lpthread -o example_timestamp'
- run:
name: Run timestamp example
command: './example_timestamp'
- run:
name: Build hook receiving example
command: 'g++ -D__RTP_NO_CRYPTO__ docs/examples/receiving_hook.cc -Iinclude/ build/libuvgrtp.a -lpthread -o example_hook'
- run:
name: Run RTP hook example
command: './example_hook'
- run:
name: Build poll receiving example
command: 'g++ -D__RTP_NO_CRYPTO__ docs/examples/receiving_poll.cc -Iinclude/ build/libuvgrtp.a -lpthread -o example_poll'
- run:
name: Run RTP polling example
command: './example_poll'
- run:
name: Build RTCP example
command: 'g++ -D__RTP_NO_CRYPTO__ docs/examples/rtcp_hook.cc -Iinclude/ build/libuvgrtp.a -lpthread -o example_rtcp'
- run:
name: Run RTCP hook example
command: './example_rtcp'
- run:
name: Build sending example
command: 'g++ -D__RTP_NO_CRYPTO__ docs/examples/sending.cc -Iinclude/ build/libuvgrtp.a -lpthread -o example_sending'
- run:
name: Run sending example
command: './example_sending'
- run:
name: Build generic example
command: 'g++ -D__RTP_NO_CRYPTO__ docs/examples/sending_generic.cc -Iinclude/ build/libuvgrtp.a -lpthread -o example_generic'
- run:
name: Run generic format example
command: './example_generic'
- run:
name: Install Crypto++
command: 'sudo apt-get install -y libcrypto++-dev'
- run:
name: Create build files for crypto++ build
command: 'cmake -H. -Bbuild_c'
- run:
name: Build the uvgRTP library with Crypto++ enabled
command: 'cmake --build build_c'
- run:
name: Build user managed SRTP key example
command: 'g++ docs/examples/srtp_user.cc -Iinclude/ build_c/libuvgrtp.a -lpthread -lcryptopp -o example_srtp'
- run:
name: Run user managed SRTP key example
command: './example_srtp'
- run:
name: Build ZRTP + SRTP multistream example
command: 'g++ docs/examples/zrtp_multistream.cc -Iinclude/ build_c/libuvgrtp.a -lpthread -lcryptopp -o example_zrtp'
- run:
name: Run ZRTP + SRTP multistream key example
command: './example_zrtp'

View File

@ -109,17 +109,17 @@ target_sources(${PROJECT_NAME} PRIVATE
src/srtp/srtp.hh
src/srtp/srtcp.hh
include/util.hh
include/clock.hh
include/crypto.hh
include/debug.hh
include/frame.hh
include/lib.hh
include/media_stream.hh
include/rtcp.hh
include/runner.hh
include/session.hh
include/socket.hh
include/uvgrtp/util.hh
include/uvgrtp/clock.hh
include/uvgrtp/crypto.hh
include/uvgrtp/debug.hh
include/uvgrtp/frame.hh
include/uvgrtp/lib.hh
include/uvgrtp/media_stream.hh
include/uvgrtp/rtcp.hh
include/uvgrtp/runner.hh
include/uvgrtp/session.hh
include/uvgrtp/socket.hh
)
if(WIN32)
@ -212,7 +212,7 @@ install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}_version EXPORT ${PROJECT_NAME}Ta
COMPONENT ${PROJECT_NAME}_Runtime)
#Copy all header files to the <prefix>/include/uvgrtp directory
file(GLOB DEPLOY_FILES_AND_DIRS "${CMAKE_SOURCE_DIR}/include/*")
file(GLOB DEPLOY_FILES_AND_DIRS "${CMAKE_SOURCE_DIR}/include/uvgrtp/*")
install(FILES ${DEPLOY_FILES_AND_DIRS}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/
COMPONENT ${PROJECT_NAME}_Develop)

View File

@ -1,4 +1,4 @@
#include "version.hh"
#include "uvgrtp/version.hh"
#include <cstdint>
#include <string>

75
docs/examples/.gitignore vendored Normal file
View File

@ -0,0 +1,75 @@
# This file is used to ignore files which are generated
# ----------------------------------------------------------------------------
*~
*.autosave
*.a
*.core
*.moc
*.o
*.obj
*.orig
*.rej
*.so
*.so.*
*_pch.h.cpp
*_resource.rc
*.qm
.#*
*.*#
core
!core/
tags
.DS_Store
.directory
*.debug
Makefile*
*.prl
*.app
moc_*.cpp
ui_*.h
qrc_*.cpp
Thumbs.db
*.res
*.rc
/.qmake.cache
/.qmake.stash
# qtcreator generated files
*.pro.user*
# xemacs temporary files
*.flc
# Vim temporary files
.*.swp
# Visual Studio generated files
*.ib_pdb_index
*.idb
*.ilk
*.pdb
*.sln
*.suo
*.vcproj
*vcproj.*.*.user
*.ncb
*.sdf
*.opensdf
*.vcxproj
*vcxproj.*
# MinGW generated files
*.Debug
*.Release
# Python byte code
*.pyc
# Binaries
# --------
*.dll
*.exe
release/*
debug/*

View File

@ -1,54 +1,134 @@
#include <uvgrtp/lib.hh>
#define PAYLOAD_MAXLEN 256
#include <iostream>
void hook(void *arg, uvgrtp::frame::rtp_frame *frame)
{
uvgrtp::frame::dealloc_frame(frame);
}
/* Some NATs may close the hole created in the firewall if the stream is not bidirectional,
* i.e., only one participant produces and the other consumes.
*
* To prevent the connection from closing, uvgRTP can be instructed to keep the hole open
* by periodically sending 1-byte datagram to remote (once every 2 seconds).
*
* All RFC 3550 compatible implementations should ignore the packet as it is not recognized
* to be a valid RTP frame and the stream should work without problems.
*
* This feature is enabled by giving RCE_HOLEPUNCH_KEEPALIVE flag to the unidirectional
* media_stream that acts as the receiver. Please note that this flag is only necessary
* if you're using the created media_stream object as a unidirectional stream and you are
* noticing that after a while the packets are no longer passing through the firewall
* In this example, we demonstrate the functionality of RCE_HOLEPUNCH_KEEPALIVE by sending
* a dummy stream from sender to receiver with hole punching feature enabled by configuration
* flag of the receiver.
*/
// network parameters of the example
constexpr char LOCAL_INTERFACE[] = "127.0.0.1";
constexpr uint16_t LOCAL_PORT = 8888;
constexpr char REMOTE_ADDRESS[] = "127.0.0.1";
constexpr uint16_t REMOTE_PORT = 8890;
// Parameters of sent dummy frames
constexpr uint16_t PAYLOAD_LEN = 256; // how large are test packets
constexpr int AMOUNT_OF_PACKETS = 100; // how many
constexpr int PACKET_INTERVAL_MS = 1000/30; // how often
// Function where received frames are processed
void frame_process_hook(void *arg, uvgrtp::frame::rtp_frame *frame);
void wait_until_next_frame(std::chrono::steady_clock::time_point& start, int frame_index);
void cleanup(uvgrtp::context& rtp_ctx,
uvgrtp::session *sending_session, uvgrtp::session *receiving_session,
uvgrtp::media_stream *send, uvgrtp::media_stream *recv);
int main(void)
{
/* See sending.cc for more details */
std::cout << "Starting uvgRTP binding example" << std::endl;
uvgrtp::context rtp_ctx;
uvgrtp::session *sending_session = rtp_ctx.create_session(LOCAL_INTERFACE, REMOTE_ADDRESS);
uvgrtp::media_stream *send = sending_session->create_stream(LOCAL_PORT, REMOTE_PORT,
RTP_FORMAT_H265, RCE_NO_FLAGS);
/* Start session with remote at IP address 10.21.25.2
* and bind ourselves to interface pointed to by the IP address 10.21.25.200 */
uvgrtp::session *s1 = rtp_ctx.create_session("10.21.25.2", "10.21.25.200");
/* RCE flags or RTP Context Enable flags are given when creating the Media Stream.
Notice the RCE_HOLEPUNCH_KEEPALIVE flag which keeps the NAT/firewall open */
int flags = RCE_HOLEPUNCH_KEEPALIVE;
uvgrtp::session *receiving_session = rtp_ctx.create_session(REMOTE_ADDRESS, LOCAL_INTERFACE);
uvgrtp::media_stream *recv = receiving_session->create_stream(REMOTE_PORT, LOCAL_PORT,
RTP_FORMAT_H265, flags);
/* 8888 is source port or the port for the interface where data is received (ie. 10.21.25.200:8888)
* 8889 is remote port or the port for the interface where the data is sent (ie. 10.21.25.2:8889) */
uvgrtp::media_stream *send = s1->create_stream(8888, 8889, RTP_FORMAT_H265, RTP_NO_FLAGS);
/* Some NATs may close the hole created in the firewall if the stream is not bidirectional,
* i.e., only one participant produces and the other consumes.
*
* To prevent the connection from closing, uvgRTP can be instructed to keep the hole open
* by periodically sending 1-byte datagram to remote (once every 2 seconds).
*
* This is done by giving RCE_HOLEPUNCH_KEEPALIVE to the unidirectional media_stream that
* acts as the receiver
*
* All RFC 3550 compatible implementations should ignore the packet as it is not recognized
* to be a valid RTP frame and the stream should work without problems.
*
* NOTE: this flag is only necessary if you're using the created media_stream object
* as a unidirectional stream and you are noticing that after a while the packets are no longer
* passing through the firewall */
uvgrtp::media_stream *recv = s1->create_stream(7777, 6666, RTP_FORMAT_H265, RCE_HOLEPUNCH_KEEPALIVE);
/* install receive hook for asynchronous reception */
recv->install_receive_hook(nullptr, hook);
while (true) {
std::unique_ptr<uint8_t[]> buffer = std::unique_ptr<uint8_t[]>(new uint8_t[PAYLOAD_MAXLEN]);
if (send->push_frame(std::move(buffer), PAYLOAD_MAXLEN, RTP_NO_FLAGS) != RTP_OK)
fprintf(stderr, "failed to push hevc frame\n");
std::this_thread::sleep_for(std::chrono::milliseconds(800));
// install receive hook for asynchronous reception
if (!recv || recv->install_receive_hook(nullptr, frame_process_hook) != RTP_OK)
{
std::cerr << "Failed to install receive hook!" << std::endl;
cleanup(rtp_ctx, sending_session, receiving_session, send, recv);
return EXIT_FAILURE;
}
rtp_ctx.destroy_session(s1);
rtp_ctx.destroy_session(s2);
if (send)
{
auto start = std::chrono::steady_clock::now();
for (unsigned int i = 0; i < AMOUNT_OF_PACKETS; ++i)
{
std::cout << "Sending frame " << i + 1 << '/' << AMOUNT_OF_PACKETS << std::endl;
std::unique_ptr<uint8_t[]> dummy_frame =
std::unique_ptr<uint8_t[]>(new uint8_t[PAYLOAD_LEN]);
if (send->push_frame(std::move(dummy_frame), PAYLOAD_LEN, RTP_NO_FLAGS) != RTP_OK)
{
std::cerr << "Failed to send frame" << std::endl;
cleanup(rtp_ctx, sending_session, receiving_session, send, recv);
return EXIT_FAILURE;
}
/* Send frames at constant intervals. This example makes sure the frames are
* sent exactly at the right time by calculating the timeslots for each frame.
* If the full data is already available in real life, you can send it as fast
* as your network can handle, but here we simulate how a 30 fps camera would
* send frames. */
wait_until_next_frame(start, i);
}
}
cleanup(rtp_ctx, sending_session, receiving_session, send, recv);
return EXIT_SUCCESS;
}
void frame_process_hook(void *arg, uvgrtp::frame::rtp_frame *frame)
{
std::cout << "Received frame. Payload size: " << frame->payload_len << std::endl;
/* Use the hook function for handing over the frame to other thread.
* It is not recommended to perform heavy computation in hook function
* as this may interfere with uvgRTP:s ability to receive frames. */
uvgrtp::frame::dealloc_frame(frame);
}
void wait_until_next_frame(std::chrono::steady_clock::time_point &start, int frame_index)
{
// wait until it is time to send the next frame. Simulates a steady sending pace
// and included only for demostration purposes since you can use uvgRTP to send
// packets as fast as desired
auto time_since_start = std::chrono::steady_clock::now() - start;
auto next_frame_time = (frame_index + 1)*std::chrono::milliseconds(PACKET_INTERVAL_MS);
if (next_frame_time > time_since_start)
{
std::this_thread::sleep_for(next_frame_time - time_since_start);
}
}
void cleanup(uvgrtp::context &rtp_ctx,
uvgrtp::session *sending_session, uvgrtp::session *receiving_session,
uvgrtp::media_stream *send, uvgrtp::media_stream *recv)
{
if (send)
sending_session->destroy_stream(send);
if (recv)
receiving_session->destroy_stream(recv);
if (sending_session)
rtp_ctx.destroy_session(sending_session);
if (receiving_session)
rtp_ctx.destroy_session(receiving_session);
}

View File

@ -1,45 +1,143 @@
#include <uvgrtp/lib.hh>
#define PAYLOAD_MAXLEN 4096
#include <iostream>
/* This example demonstrates using the configuration options of uvgRTP.
* There are three types of configuration flags: RCE, RCC and RTP flags
* RCE (RTP Context Enable) flags are used to enable different features
* of uvgRTP and are passed when a new media_stream is created.
*
* RCC (RTP Context Configuration) flags can be used to modify the behavior of media
* stream. They are used by calling configure_ctx-function of media_stream
* and using the flag and value as parameters.
*
* Lastly, RTP flags can be added to modify the sending process of uvgRTP.
*/
/* This example implements one sender and one receiver.
* These are their used interfaces and ports. You may
* edit these if you wish to test this example on different machines */
constexpr char LOCAL_ADDRESS[] = "127.0.0.1";
constexpr uint16_t LOCAL_PORT = 8888;
constexpr char REMOTE_ADDRESS[] = "127.0.0.1";
constexpr uint16_t REMOTE_PORT = 8890;
// parameters for this example
constexpr int BUFFER_SIZE_MB = 40 * 1000 * 1000;
constexpr int MAX_PACKET_INTERVAL_MS = 150;
constexpr size_t PAYLOAD_LEN = 4096;
constexpr int SEND_TEST_PACKETS = 1000;
void receive_process_hook(void *arg, uvgrtp::frame::rtp_frame *frame);
void cleanup(uvgrtp::context& ctx, uvgrtp::session *local_session, uvgrtp::session *remote_session,
uvgrtp::media_stream *send, uvgrtp::media_stream *receive);
int main(void)
{
/* See sending.cc for more details */
uvgrtp::context ctx;
std::cout << "Starting uvgRTP configuration example" << std::endl;
/* See sending.cc for more details */
uvgrtp::session *sess = ctx.create_session("127.0.0.1");
/* Some of the functionality of uvgRTP can be enabled/disabled using RCE_* flags.
/* Some of the functionality of uvgRTP can be enabled or disabled using RCE_* flags.
*
* For example, here the created MediaStream object has RTCP enabled,
* does not utilize system call clustering to reduce the possibility of packet dropping
* and prepends a 4-byte HEVC start code (0x00000001) before each NAL unit */
unsigned flags =
* does not utilize system call clustering to reduce the possibility of packet dropping */
int send_flags =
RCE_RTCP | /* enable RTCP */
RCE_NO_SYSTEM_CALL_CLUSTERING; /* disable system call clustering */
/* Prepends a 4-byte HEVC start code (0x00000001) before each NAL unit.
* This way the stream can be saved into a file and played by a media player */
int receive_flags =
RCE_RTCP | /* enable RTCP */
RCE_NO_SYSTEM_CALL_CLUSTERING | /* disable system call clustering */
RCE_H26X_PREPEND_SC; /* prepend a start code before each NAL unit */
uvgrtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_H265, flags);
uvgrtp::context ctx;
uvgrtp::session *local_session = ctx.create_session(REMOTE_ADDRESS);
uvgrtp::media_stream *send = local_session->create_stream(LOCAL_PORT, REMOTE_PORT, RTP_FORMAT_H265, send_flags);
/* uvgRTP context can also be configured using RCC_* flags
* These flags do not enable/disable functionality but alter default behaviour of uvgRTP
*
* For example, here UDP send/recv buffers are increased to 40MB
* and frame delay is set 150 milliseconds to allow frames to arrive a little late */
hevc->configure_ctx(RCC_UDP_RCV_BUF_SIZE, 40 * 1000 * 1000);
hevc->configure_ctx(RCC_UDP_SND_BUF_SIZE, 40 * 1000 * 1000);
hevc->configure_ctx(RCC_PKT_MAX_DELAY, 150);
uvgrtp::session *remote_session = ctx.create_session(LOCAL_ADDRESS);
uvgrtp::media_stream *receive = remote_session->create_stream(REMOTE_PORT, LOCAL_PORT, RTP_FORMAT_H265, receive_flags);
for (;;) {
auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[PAYLOAD_MAXLEN]);
if (receive)
{
/* uvgRTP context can also be configured using RCC_* flags
* These flags do not enable/disable functionality but alter default behaviour of uvgRTP
*
* For example, here UDP receive buffer is increased to BUFFER_SIZE_MB
* and frame delay is set PACKET_MAX_DELAY_MS to allow frames to arrive a little late */
receive->configure_ctx(RCC_UDP_RCV_BUF_SIZE, BUFFER_SIZE_MB);
receive->configure_ctx(RCC_PKT_MAX_DELAY, MAX_PACKET_INTERVAL_MS);
if (hevc->push_frame(std::move(buffer), PAYLOAD_MAXLEN, RTP_NO_FLAGS) != RTP_OK)
fprintf(stderr, "Failed to send RTP frame!");
// install receive hook for asynchronous reception
receive->install_receive_hook(nullptr, receive_process_hook);
}
else
{
std::cerr << "Failed to install receive hook!" << std::endl;
cleanup(ctx, local_session, remote_session, send, receive);
return EXIT_FAILURE;
}
/* Session must be destroyed manually */
ctx.destroy_session(sess);
if (send)
{
return 0;
/* Here, the UDP send buffer is increased to BUFFER_SIZE_MB */
send->configure_ctx(RCC_UDP_SND_BUF_SIZE, BUFFER_SIZE_MB);
for (int i = 0; i < SEND_TEST_PACKETS; ++i)
{
auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[PAYLOAD_LEN]);
if ((i+1)%10 == 0 || i == 0) // print every 10 frames and first
std::cout << "Sending frame " << i + 1 << '/' << SEND_TEST_PACKETS << std::endl;
if (send->push_frame(std::move(buffer), PAYLOAD_LEN, RTP_NO_FLAGS) != RTP_OK)
{
std::cerr << "Failed to send RTP frame!" << std::endl;
cleanup(ctx, local_session, remote_session, send, receive);
return EXIT_FAILURE;
}
}
}
cleanup(ctx, local_session, remote_session, send, receive);
return EXIT_SUCCESS;
}
void receive_process_hook(void *arg, uvgrtp::frame::rtp_frame *frame)
{
std::cout << "Received frame. Payload size: " << frame->payload_len << std::endl;
uvgrtp::frame::dealloc_frame(frame);
}
void cleanup(uvgrtp::context &ctx, uvgrtp::session *local_session, uvgrtp::session *remote_session,
uvgrtp::media_stream *send, uvgrtp::media_stream *receive)
{
if (send)
{
local_session->destroy_stream(send);
}
if (receive)
{
remote_session->destroy_stream(receive);
}
if (local_session)
{
// Session must be destroyed manually
ctx.destroy_session(local_session);
}
if (remote_session)
{
// Session must be destroyed manually
ctx.destroy_session(remote_session);
}
}

View File

@ -1,41 +1,132 @@
#include <uvgrtp/lib.hh>
#define PAYLOAD_MAXLEN 100
#include <cstdlib>
#include <iostream>
/* This example demostrates the usage of custom timestamps. Often when
* streaming multiple media, there are different lengths steps in in the
* processing flow. Setting the timestamps manually instead of using uvgRTPs
* internal timestamp system eliminates the timestamp offset.
*
* As a concrete example, audio encoding is usually faster than video encoding and
* this can cause small offset between video and audio even before reaching uvgRTP.
* This can be mitigated by capturing the timestamp at the time of recording video/audio
* and using that as the custom timestamp. This is also how RFC 3550 (RTP) recommends
* going about this. */
// set these as you want. Take care that the RTCP is set as RTP port +1
constexpr char LOCAL_ADDRESS[] = "127.0.0.1";
constexpr uint16_t LOCAL_PORT = 8888;
constexpr char REMOTE_ADDRESS[] = "127.0.0.1";
constexpr uint16_t REMOTE_PORT = 8890;
// RTP clock for video increments 90000 in RTP payload format RFC
constexpr uint32_t VIDEO_CLOCK_RATE = 90000;
constexpr uint32_t VIDEO_FRAME_RATE = 30;
constexpr size_t PAYLOAD_LEN = 100;
constexpr int SEND_TEST_PACKETS = 500;
void process_received_frame_hook(void *arg, uvgrtp::frame::rtp_frame *frame);
void cleanup(uvgrtp::context& ctx, uvgrtp::session *local_session, uvgrtp::session *remote_session,
uvgrtp::media_stream *send, uvgrtp::media_stream *receive);
int main(void)
{
/* To use the library, one must create a global RTP context object */
std::cout << "Starting uvgRTP custom timestamp example" << std::endl;
uvgrtp::context ctx;
/* See sending.cc for more details */
uvgrtp::session *sess = ctx.create_session("127.0.0.1");
int flags = RCE_RTCP; // enable RTCP
uvgrtp::session *local_session = ctx.create_session(REMOTE_ADDRESS);
uvgrtp::media_stream *send = local_session->create_stream(LOCAL_PORT, REMOTE_PORT,
RTP_FORMAT_H265, flags);
/* Create MediaStream and enable RTCP for the stream */
uvgrtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_H265, RCE_RTCP);
uvgrtp::session *remote_session = ctx.create_session(LOCAL_ADDRESS);
uvgrtp::media_stream *receive = remote_session->create_stream(REMOTE_PORT, LOCAL_PORT,
RTP_FORMAT_H265, flags);
uint8_t *buffer = new uint8_t[PAYLOAD_MAXLEN];
uint32_t clock_rate = 90000 / 30;
uint32_t timestamp = 0;
/* If you don't want uvgRTP to handle timestamping but wish to do that yourself
* AND you want to use RTCP, timestamping info must be provided for the RTCP so
* it is able calculate sensible values for synchronization info
*
* The first parameter is NTP time associated with the corresponding RTP timestamp,
* second parameter is clock rate and the third parameter is RTP timestamp for t = 0
* (it can be zero or some random number, does not matter) */
hevc->get_rtcp()->set_ts_info(uvgrtp::clock::ntp::now(), clock_rate, timestamp);
for (int i = 0; i < 10; ++i) {
/* The timestamp is given as the third parameter and it should be advanced
* in accordance with the media stream clock rate. For example, for HEVC, the clock rate is 90000. */
if (hevc->push_frame(buffer, PAYLOAD_MAXLEN, clock_rate * timestamp++, RTP_NO_FLAGS) != RTP_OK)
fprintf(stderr, "Failed to send RTP frame!");
if (receive)
{
/* install receive hook for asynchronous reception */
if (receive->install_receive_hook(nullptr, process_received_frame_hook) != RTP_OK)
{
cleanup(ctx, local_session, remote_session, send, receive);
return EXIT_FAILURE;
}
}
/* Session must be destroyed manually */
delete[] buffer;
ctx.destroy_session(sess);
if (send)
{
// RTP specification says there should be a random initial offset
srand (time(NULL));
uint32_t start_timestamp = rand()%UINT32_MAX;
return 0;
/* If you don't want uvgRTP to handle timestamping but wish to do that yourself
* AND you want to use RTCP, timestamping info must be provided for the RTCP so
* it is able calculate sensible values for synchronization info
*
* The first parameter is NTP time associated with the corresponding RTP timestamp,
* second parameter is clock rate and the third parameter is RTP timestamp for t = 0
* (it can be zero or some random number, does not matter) */
send->get_rtcp()->set_ts_info(uvgrtp::clock::ntp::now(), VIDEO_CLOCK_RATE, start_timestamp);
for (int i = 0; i < SEND_TEST_PACKETS; ++i)
{
auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[PAYLOAD_LEN]);
// fake timestamp. This way the receiver can play the frames at a lower pace, even if
// we generate them really fast.
uint32_t timestamp = start_timestamp + i*VIDEO_CLOCK_RATE/VIDEO_FRAME_RATE;
std::cout << "Sending frame " << i + 1 << '/' << SEND_TEST_PACKETS <<
" with timestamp: " << timestamp << std::endl;
/* The timestamp is given as the third parameter and it should be advanced
* in accordance with the media stream clock rate. For example, for HEVC, the clock rate is 90000. */
if (send->push_frame(std::move(buffer), PAYLOAD_LEN, timestamp, RTP_NO_FLAGS) != RTP_OK)
{
std::cerr << "Failed to send RTP frame!";
cleanup(ctx, local_session, remote_session, send, receive);
return EXIT_FAILURE;
}
}
}
cleanup(ctx, local_session, remote_session, send, receive);
return EXIT_SUCCESS;
}
void process_received_frame_hook(void *arg, uvgrtp::frame::rtp_frame *frame)
{
std::cout << "Received frame. Timestamp: " << frame->header.timestamp << std::endl;
uvgrtp::frame::dealloc_frame(frame);
}
void cleanup(uvgrtp::context &ctx, uvgrtp::session *local_session, uvgrtp::session *remote_session,
uvgrtp::media_stream *send, uvgrtp::media_stream *receive)
{
if (send)
{
local_session->destroy_stream(send);
}
if (receive)
{
remote_session->destroy_stream(receive);
}
if (local_session)
{
// Session must be destroyed manually
ctx.destroy_session(local_session);
}
if (remote_session)
{
// Session must be destroyed manually
ctx.destroy_session(remote_session);
}
}

View File

@ -1,34 +0,0 @@
#include <uvgrtp/lib.hh>
#include <uvgrtp/formats/rawvideo.hh>
#define PAYLOAD_MAXLEN 4096
int main(void)
{
uvgrtp::context ctx;
uvgrtp::session *sess = ctx.create_session("127.0.0.1");
uvgrtp::media_stream *rwv = sess->create_stream(8888, 8889, RTP_FORMAT_RAW_VIDEO, RTP_NO_FLAGS);
uvgrtp::formats::rwv_config conf = {
.pixfmt = uvgrtp::formats::RWV_FMT_YUV420,
.progressive = false, /* progressive */
.width = 640,
.height = 480,
.depth = 8 /* bit depth */
};
rwv->configure_ctx(&conf);
for (;;) {
auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[PAYLOAD_MAXLEN]);
if (rwv->push_frame(std::move(buffer), PAYLOAD_MAXLEN, RTP_NO_FLAGS) != RTP_OK)
fprintf(stderr, "Failed to send RTP frame!");
}
/* Session must be destroyed manually */
ctx.destroy_session(sess);
return 0;
}

View File

@ -1,27 +1,47 @@
#include <uvgrtp/lib.hh>
#include <thread>
void receive_hook(void *arg, uvgrtp::frame::rtp_frame *frame)
{
/* Now we own the frame. Here you could give the frame to the application
* if f.ex "arg" was some application-specific pointer
*
* arg->copy_frame(frame) or whatever
*
* When we're done with the frame, it must be deallocated manually */
(void)uvgrtp::frame::dealloc_frame(frame);
}
/* There are two main ways of getting received RTP frames from uvgRTP.
* This example demonstrates the usage of hook function to receive RTP frames.
*
* The advantage of using a hook function is minimal CPU usage and delay between
* uvgRTP receiving the frame and application processing the frame. When using
* the hook method, the application must take care that it is not using the hook
* function for heavy processing since this may block RTP frame reception.
*
* Hook based frame reception is generally recommended for most serious applications,
* but there can be situations where polling method is better, especially if performance
* is not a huge concern or if there needs to be tight control when the frame is
* received by the application.
*
* This example only implements the receiving, but it can be used together with the
* sending example to test the functionality.
*/
// parameters for this test. You can change these to suit your network environment
constexpr uint16_t LOCAL_PORT = 8890;
constexpr char REMOTE_ADDRESS[] = "127.0.0.1";
constexpr uint16_t REMOTE_PORT = 8888;
// This example runs for 5 seconds
constexpr auto RECEIVE_TIME_S = std::chrono::seconds(3);
void rtp_receive_hook(void *arg, uvgrtp::frame::rtp_frame *frame);
void cleanup(uvgrtp::context& ctx, uvgrtp::session *sess, uvgrtp::media_stream *receiver);
int main(void)
{
/* See sending.cc for more details */
std::cout << "Starting uvgRTP RTP receive hook example" << std::endl;
uvgrtp::context ctx;
/* There remote address and port are needed if the .
uvgRTP API is */
/* See sending.cc for more details */
uvgrtp::session *sess = ctx.create_session("127.0.0.1");
/* See sending.cc for more details */
uvgrtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_H265, 0);
uvgrtp::session *sess = ctx.create_session(REMOTE_ADDRESS);
int flags = RTP_NO_FLAGS;
uvgrtp::media_stream *receiver = sess->create_stream(LOCAL_PORT, REMOTE_PORT, RTP_FORMAT_H265, flags);
/* Receive hook can be installed and uvgRTP will call this hook when an RTP frame is received
*
@ -32,10 +52,45 @@ int main(void)
* specfic object if the application needs to be called inside the hook
*
* If it's not needed, it should be set to nullptr */
hevc->install_receive_hook(nullptr, receive_hook);
if (!receiver || receiver->install_receive_hook(nullptr, rtp_receive_hook) != RTP_OK)
{
std::cerr << "Failed to install RTP reception hook";
cleanup(ctx, sess, receiver);
return EXIT_FAILURE;
}
/* Session must be destroyed manually */
ctx.destroy_session(sess);
std::cout << "Waiting incoming packets for " << RECEIVE_TIME_S.count() << " s" << std::endl;
return 0;
std::this_thread::sleep_for(RECEIVE_TIME_S); // lets this example run for some time
cleanup(ctx, sess, receiver);
return EXIT_SUCCESS;
}
void rtp_receive_hook(void *arg, uvgrtp::frame::rtp_frame *frame)
{
std::cout << "Received RTP frame" << std::endl;
/* Now we own the frame. Here you could give the frame to the application
* if f.ex "arg" was some application-specific pointer
*
* arg->copy_frame(frame) or whatever
*
* When we're done with the frame, it must be deallocated manually */
(void)uvgrtp::frame::dealloc_frame(frame);
}
void cleanup(uvgrtp::context& ctx, uvgrtp::session *sess, uvgrtp::media_stream *receiver)
{
if (receiver)
{
sess->destroy_stream(receiver);
}
if (sess)
{
/* Session must be destroyed manually */
ctx.destroy_session(sess);
}
}

View File

@ -1,37 +1,85 @@
#include <uvgrtp/lib.hh>
#include <iostream>
#include <thread>
/* This example demostrates using polling to receive RTP frames. Polling in
* uvgRTP can be done with function pull_frame in media_streamer. This pull_frame
* function can be used with or without a timeout argument. If used without a timeout
* argument, the function will return when a frame is received or the media stream
* is destroyed. At this point I would recommend using it with timeout and not
* destroying the media stream since this functionality has not been verified.
*
* Compared to hook function, polling offers more control on frame reception,
* but I would recommend using a hook function where possible due to reduced
* CPU usage and latency.
*
* This example implements only the reception of the stream, but it can be paired
* with the sending example to complete the demonstration.
*/
// parameters of this example. You may change these to reflect you network environment
constexpr uint16_t LOCAL_PORT = 8890;
constexpr char REMOTE_ADDRESS[] = "127.0.0.1";
constexpr uint16_t REMOTE_PORT = 8888;
// How long this example will run
constexpr auto RECEIVE_TIME_MS = std::chrono::milliseconds(3000);
constexpr int RECEIVER_WAIT_TIME_MS = 100;
void process_frame(uvgrtp::frame::rtp_frame *frame);
int main(void)
{
/* See sending.cc for more details */
std::cout << "Starting uvgRTP RTP receive hook example" << std::endl;
uvgrtp::context ctx;
uvgrtp::session *sess = ctx.create_session(REMOTE_ADDRESS);
int flags = RCE_NO_FLAGS;
/* See sending.cc for more details */
uvgrtp::session *sess = ctx.create_session("127.0.0.1");
uvgrtp::media_stream *receiver = sess->create_stream(LOCAL_PORT, REMOTE_PORT,
RTP_FORMAT_H265, flags);
/* See sending.cc for more details */
uvgrtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_H265, 0);
// TODO: Explain how to stop poll in middle of the wait
/* pull_frame() will block until a frame is received.
*
* If that is not acceptable, a separate thread for the reader should be created */
uvgrtp::frame::rtp_frame *frame = nullptr;
if (receiver)
{
uvgrtp::frame::rtp_frame *frame = nullptr;
while (!(frame = hevc->pull_frame())) {
/* When we receive a frame, the ownership of the frame belongs to us and
* when we're done with it, we need to deallocate the frame */
(void)uvgrtp::frame::dealloc_frame(frame);
std::cout << "Start receiving frames for " << RECEIVE_TIME_MS.count() << " ms" << std::endl;
auto start = std::chrono::steady_clock::now();
while (std::chrono::steady_clock::now() - start < RECEIVE_TIME_MS)
{
/* You can specify a timeout for the operation and if the a frame is not received
* within that time limit, pull_frame() returns a nullptr
*
* The parameter tells how long time a frame is waited in milliseconds */
frame = receiver->pull_frame(RECEIVER_WAIT_TIME_MS);
if (frame)
process_frame(frame);
}
sess->destroy_stream(receiver);
}
/* You can also specify for a timeout for the operation and if the a frame is not received
* within that time limit, pull_frame() returns a nullptr
*
* The parameter tells how long time a frame is waited in milliseconds */
frame = hevc->pull_frame(200);
if (sess)
{
/* Session must be destroyed manually */
ctx.destroy_session(sess);
}
/* Frame must be freed manually */
uvgrtp::frame::dealloc_frame(frame);
ctx.destroy_session(sess);
return 0;
return EXIT_SUCCESS;
}
void process_frame(uvgrtp::frame::rtp_frame *frame)
{
std::cout << "Received an RTP frame" << std::endl;
/* When we receive a frame, the ownership of the frame belongs to us and
* when we're done with it, we need to deallocate the frame */
(void)uvgrtp::frame::dealloc_frame(frame);
}

View File

@ -1,55 +1,192 @@
#include <uvgrtp/lib.hh>
#include <cstring>
/* uvgRTP calls this hook when it receives an RTCP Receiver Report
/* RTCP (RTP Control Protocol) is used to monitor the quality
* of the RTP stream. This example demonstrates the usage of
* sender and receiver reports. RTCP also includes SDES, APP and BYE
* packets which are not demostrated in this example.
*
* This example shows the usage of rtcp while also transmitting RTP
* stream. The rtcp reports are sent only every 10 seconds and the
* sender/receiver reports are printed.
*/
constexpr char LOCAL_INTERFACE[] = "127.0.0.1";
constexpr uint16_t LOCAL_PORT = 8888;
constexpr char REMOTE_ADDRESS[] = "127.0.0.1";
constexpr uint16_t REMOTE_PORT = 8890;
constexpr uint16_t PAYLOAD_LEN = 256;
constexpr uint16_t FRAME_RATE = 30;
constexpr uint32_t EXAMPLE_RUN_TIME_S = 30;
constexpr int SEND_TEST_PACKETS = FRAME_RATE*EXAMPLE_RUN_TIME_S;
constexpr int PACKET_INTERVAL_MS = 1000/FRAME_RATE;
/* uvgRTP calls this hook when it receives an RTCP Report
*
* NOTE: If application uses hook, it must also free the frame when it's done with i
* Frame must deallocated using uvgrtp::frame::dealloc_frame() function */
void receiver_hook(uvgrtp::frame::rtcp_receiver_report *frame);
void sender_hook(uvgrtp::frame::rtcp_sender_report *frame);
void wait_until_next_frame(std::chrono::steady_clock::time_point& start, int frame_index);
void cleanup(uvgrtp::context& ctx, uvgrtp::session *local_session, uvgrtp::session *remote_session,
uvgrtp::media_stream *send, uvgrtp::media_stream *receive);
int main(void)
{
std::cout << "Starting uvgRTP RTCP hook example" << std::endl;
// Creation of RTP stream. See sending example for more details
uvgrtp::context ctx;
uvgrtp::session *local_session = ctx.create_session(REMOTE_ADDRESS);
uvgrtp::session *remote_session = ctx.create_session(LOCAL_INTERFACE);
int flags = RCE_RTCP;
uvgrtp::media_stream *local_stream = local_session->create_stream(LOCAL_PORT, REMOTE_PORT,
RTP_FORMAT_GENERIC, flags);
uvgrtp::media_stream *remote_stream = remote_session->create_stream(REMOTE_PORT, LOCAL_PORT,
RTP_FORMAT_GENERIC, flags);
// TODO: There is a bug in uvgRTP in how sender reports are implemented and this text reflects
// that wrong thinking. Sender reports are sent by the sender
/* In this example code, local_stream acts as the sender and because it is the only sender,
* it does not send any RTCP frames but only receives RTCP Receiver reports from remote_stream.
*
* Because local_stream only sends and remote_stream only receives, we only need to install
* receive hook for local_stream.
*
* By default, all media_stream that have RTCP enabled start as receivers and only if/when they
* call push_frame() are they converted into senders. */
if (!local_stream || local_stream->get_rtcp()->install_receiver_hook(receiver_hook) != RTP_OK)
{
std::cerr << "Failed to install RTCP receiver report hook" << std::endl;
cleanup(ctx, local_session, remote_session, local_stream, remote_stream);
return EXIT_FAILURE;
}
if (!remote_stream || remote_stream->get_rtcp()->install_sender_hook(sender_hook) != RTP_OK)
{
std::cerr << "Failed to install RTCP sender report hook" << std::endl;
cleanup(ctx, local_session, remote_session, local_stream, remote_stream);
return EXIT_FAILURE;
}
if (local_stream)
{
// Send dummy data so there's some RTP data to analyze
uint8_t buffer[PAYLOAD_LEN] = { 0 };
memset(buffer, 'a', PAYLOAD_LEN);
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
for (unsigned int i = 0; i < SEND_TEST_PACKETS; ++i)
{
if ((i+1)%10 == 0 || i == 0) // print every 10 frames and first
{
std::cout << "Sending RTP frame " << (i + 1) << "/" << SEND_TEST_PACKETS
<< " Total data sent: " << (i + 1)*PAYLOAD_LEN << std::endl;
}
local_stream->push_frame((uint8_t *)buffer, PAYLOAD_LEN, RTP_NO_FLAGS);
// send frames at constant interval to mimic a real camera stream
wait_until_next_frame(start, i);
}
std::cout << "Sending finished, total time: " <<
(std::chrono::steady_clock::now() - start).count()/1000000 <<" ms" << std::endl;
}
cleanup(ctx, local_session, remote_session, local_stream, remote_stream);
return EXIT_SUCCESS;
}
void receiver_hook(uvgrtp::frame::rtcp_receiver_report *frame)
{
LOG_INFO("Received an RTCP Receiver Report");
std::cout << "RTCP receiver report! ----------" << std::endl;
for (auto& block : frame->report_blocks) {
fprintf(stderr, "ssrc: %x\n", block.ssrc);
fprintf(stderr, "fraction: %u\n", block.fraction);
fprintf(stderr, "lost: %d\n", block.lost);
fprintf(stderr, "last_seq: %u\n", block.last_seq);
fprintf(stderr, "jitter: %u\n", block.jitter);
fprintf(stderr, "lsr: %u\n", block.lsr);
fprintf(stderr, "dlsr (ms): %u\n", uvgrtp::clock::jiffies_to_ms(block.dlsr));
for (auto& block : frame->report_blocks)
{
std::cout << "ssrc: " << block.ssrc << std::endl;
std::cout << "fraction: " << block.fraction << std::endl;
std::cout << "lost: " << block.lost << std::endl;
std::cout << "last_seq: " << block.last_seq << std::endl;
std::cout << "jitter: " << block.jitter << std::endl;
std::cout << "lsr: " << block.lsr << std::endl;
std::cout << "dlsr (ms): " << uvgrtp::clock::jiffies_to_ms(block.dlsr)
<< std::endl << std::endl;
}
/* RTCP frames can be deallocated using delete */
delete frame;
}
int main(void)
void sender_hook(uvgrtp::frame::rtcp_sender_report *frame)
{
/* See sending.cc for more details */
uvgrtp::context ctx;
std::cout << "RTCP sender report! ----------" << std::endl;
std::cout << "NTP msw: " << frame->sender_info.ntp_msw << std::endl;
std::cout << "NTP lsw: " << frame->sender_info.ntp_lsw << std::endl;
std::cout << "RTP timestamp: " << frame->sender_info.rtp_ts << std::endl;
std::cout << "packet count: " << frame->sender_info.pkt_cnt << std::endl;
std::cout << "byte count: " << frame->sender_info.byte_cnt << std::endl;
/* See sending.cc for more details */
uvgrtp::session *sess = ctx.create_session("127.0.0.1");
/* For s1, RTCP runner is using port 7778 and for s2 port 8889 */
uvgrtp::media_stream *s1 = sess->create_stream(7777, 8888, RTP_FORMAT_GENERIC, RCE_RTCP);
uvgrtp::media_stream *s2 = sess->create_stream(8888, 7777, RTP_FORMAT_GENERIC, RCE_RTCP);
/* In this example code, s1 acts as the sender and because it is the only sender,
* it does not send any RTCP frames but only receives RTCP Receiver reports from s2.
*
* Because s1 only sends and s2 only receives, we only need to install receive hook for s1
*
* By default, all media_stream that have RTCP enabled start as receivers and only if/when they
* call push_frame() are they converted into senders. */
(void)s1->get_rtcp()->install_receiver_hook(receiver_hook);
/* Send dummy data so there's some RTCP data to send */
uint8_t buffer[50] = { 0 };
memset(buffer, 'a', 50);
while (true) {
s1->push_frame((uint8_t *)buffer, 50, RTP_NO_FLAGS);
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
for (auto& block : frame->report_blocks)
{
std::cout << "ssrc: " << block.ssrc << std::endl;
std::cout << "fraction: " << block.fraction << std::endl;
std::cout << "lost: " << block.lost << std::endl;
std::cout << "last_seq: " << block.last_seq << std::endl;
std::cout << "jitter: " << block.jitter << std::endl;
std::cout << "lsr: " << block.lsr << std::endl;
std::cout << "dlsr (ms): " << uvgrtp::clock::jiffies_to_ms(block.dlsr)
<< std::endl << std::endl;
}
/* RTCP frames can be deallocated using delete */
delete frame;
}
void wait_until_next_frame(std::chrono::steady_clock::time_point &start, int frame_index)
{
// wait until it is time to send the next frame. Simulates a steady sending pace
// and included only for demostration purposes since you can use uvgRTP to send
// packets as fast as desired
auto time_since_start = std::chrono::steady_clock::now() - start;
auto next_frame_time = (frame_index + 1)*std::chrono::milliseconds(PACKET_INTERVAL_MS);
if (next_frame_time > time_since_start)
{
std::this_thread::sleep_for(next_frame_time - time_since_start);
}
}
void cleanup(uvgrtp::context &ctx, uvgrtp::session *local_session, uvgrtp::session *remote_session,
uvgrtp::media_stream *send, uvgrtp::media_stream *receive)
{
if (send)
{
local_session->destroy_stream(send);
}
if (receive)
{
remote_session->destroy_stream(receive);
}
if (local_session)
{
// Session must be destroyed manually
ctx.destroy_session(local_session);
}
if (remote_session)
{
// Session must be destroyed manually
ctx.destroy_session(remote_session);
}
}

View File

@ -1,20 +1,39 @@
#include <uvgrtp/lib.hh>
#define PAYLOAD_MAXLEN 100
#include <iostream>
/* RTP is a protocol for real-time streaming. The simplest usage
* scenario is sending one RTP stream and receiving it. This example
* Shows how to send one RTP stream. These examples perform a simple
* test if they are run. You may run the receiving examples at the same
* time to see the whole demo. */
/* parameters of this example. You may change these to reflect
* you network environment. */
constexpr uint16_t LOCAL_PORT = 8888;
constexpr char REMOTE_ADDRESS[] = "127.0.0.1";
constexpr uint16_t REMOTE_PORT = 8890;
// the parameters of demostration
constexpr size_t PAYLOAD_LEN = 100;
constexpr int AMOUNT_OF_TEST_PACKETS = 100;
constexpr auto END_WAIT = std::chrono::seconds(5);
int main(void)
{
std::cout << "Starting uvgRTP RTP sending example" << std::endl;
/* To use the library, one must create a global RTP context object */
uvgrtp::context ctx;
/* Each new IP address requires a separate RTP session */
uvgrtp::session *sess = ctx.create_session("127.0.0.1");
// A session represents
uvgrtp::session *sess = ctx.create_session(REMOTE_ADDRESS);
/* Each RTP session has one or more media streams. These media streams are bidirectional
* and they require both source and destination ports for the connection. One must also
* specify the media format for the stream and any configuration flags if needed.
*
* See configuration.cc for more details about configuration.
* See Configuration example for more details about configuration.
*
* First port is source port aka the port that we listen to and second port is the port
* that remote listens to
@ -22,18 +41,42 @@ int main(void)
* This same object is used for both sending and receiving media
*
* In this example, we have one media stream with the remote participant: H265 */
uvgrtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_H265, RTP_NO_FLAGS);
uint8_t *buffer = new uint8_t[PAYLOAD_MAXLEN];
int flags = RTP_NO_FLAGS;
uvgrtp::media_stream *hevc = sess->create_stream(LOCAL_PORT, REMOTE_PORT,
RTP_FORMAT_H265, flags);
for (int i = 0; i < 10; ++i) {
if (hevc->push_frame(buffer, PAYLOAD_MAXLEN, RTP_NO_FLAGS) != RTP_OK)
fprintf(stderr, "Failed to send RTP frame!");
if (hevc)
{
/* In this example we send packets as fast as possible. The source can be
* a file or a real-time encoded stream */
for (int i = 0; i < AMOUNT_OF_TEST_PACKETS; ++i)
{
std::unique_ptr<uint8_t[]> dummy_frame = std::unique_ptr<uint8_t[]>(new uint8_t[PAYLOAD_LEN]);
if ((i+1)%10 == 0 || i == 0) // print every 10 frames and first
std::cout << "Sending frame " << i + 1 << '/' << AMOUNT_OF_TEST_PACKETS << std::endl;
if (hevc->push_frame(std::move(dummy_frame), PAYLOAD_LEN, RTP_NO_FLAGS) != RTP_OK)
{
std::cout << "Failed to send RTP frame!" << std::endl;
}
}
std::cout << "Sending finished. Waiting "<< END_WAIT.count()
<< " seconds before exiting." << std::endl;
// wait a little bit so pop-up console users have time to see the results
std::this_thread::sleep_for(END_WAIT);
sess->destroy_stream(hevc);
}
/* Session must be destroyed manually */
delete[] buffer;
ctx.destroy_session(sess);
if (sess)
{
/* Session must be destroyed manually */
ctx.destroy_session(sess);
}
return 0;
return EXIT_SUCCESS;
}

View File

@ -1,15 +1,40 @@
#include <uvgrtp/lib.hh>
#include <climits>
#define PAYLOAD_MAXLEN (0xffff - 0x1000)
/* Generic sending API means, that the user takes the responsibility
* for RTP payload format. uvgRTP does help a little bit by offering
* fragmentation function for sending. This means that that if the
* packet is larger than the specified RTP payload size (default is 1500)
* the packets are fragmented. This is useful with raw audio of high quality,
* but unfortenately uvgRTP does not offer full raw-format support.
* Contributions are welcome.
*
* This example demonstrates both sending and receiving of a generic stream. */
// network parameters of the example
constexpr char LOCAL_INTERFACE[] = "127.0.0.1";
constexpr uint16_t LOCAL_PORT = 8888;
constexpr char REMOTE_ADDRESS[] = "127.0.0.1";
constexpr uint16_t REMOTE_PORT = 8890;
// demonstration parameters of the example
constexpr uint32_t PAYLOAD_MAXLEN = (0xffff - 0x1000);
constexpr int TEST_PACKETS = 100;
void rtp_receive_hook(void *arg, uvgrtp::frame::rtp_frame *frame);
void cleanup(uvgrtp::context& ctx, uvgrtp::session *local_session, uvgrtp::session *remote_session,
uvgrtp::media_stream *send, uvgrtp::media_stream *receive);
int main(void)
{
/* See sending.cc for more details */
uvgrtp::context ctx;
std::cout << "Starting uvgRTP generic RTP payload sending example" << std::endl;
/* See sending.cc for more details */
uvgrtp::session *sess = ctx.create_session("127.0.0.1");
// See sending example for more details
uvgrtp::context ctx;
uvgrtp::session *local_session = ctx.create_session(REMOTE_ADDRESS);
uvgrtp::session *remote_session = ctx.create_session(LOCAL_INTERFACE);
/* To enable interoperability between RTP libraries, uvgRTP won't fragment generic frames by default.
*
@ -22,74 +47,94 @@ int main(void)
* received, uvgRTP constructs one full RTP frame from the fragments and returns the frame to user.
*
* See sending.cc for more details about create_stream() */
uvgrtp::media_stream *send = sess->create_stream(8888, 8889, RTP_FORMAT_GENERIC, RCE_FRAGMENT_GENERIC);
uvgrtp::media_stream *recv = sess->create_stream(8889, 8888, RTP_FORMAT_GENERIC, RCE_FRAGMENT_GENERIC);
/* Notice that PAYLOAD_MAXLEN > MTU (4096 > 1500).
int flags = RCE_FRAGMENT_GENERIC;
uvgrtp::media_stream *send = local_session->create_stream(LOCAL_PORT, REMOTE_PORT,
RTP_FORMAT_GENERIC, flags);
uvgrtp::media_stream *recv = remote_session->create_stream(REMOTE_PORT, LOCAL_PORT,
RTP_FORMAT_GENERIC, flags);
if (!recv || recv->install_receive_hook(nullptr, rtp_receive_hook) != RTP_OK)
{
cleanup(ctx, local_session, remote_session, send, recv);
std::cerr << "Failed to install RTP receive hook!" << std::endl;
return EXIT_FAILURE;
}
if (send)
{
/* Notice that PAYLOAD_MAXLEN > MTU (4096 > 1500).
*
* uvgRTP fragments all generic input frames that are larger than 1500 and in the receiving end,
* it will reconstruct the full sent frame from fragments when all fragments have been received */
auto media = std::unique_ptr<uint8_t[]>(new uint8_t[PAYLOAD_MAXLEN]);
auto media = std::unique_ptr<uint8_t[]>(new uint8_t[PAYLOAD_MAXLEN]);
srand(time(NULL));
srand(time(NULL));
while (true) {
int size = (rand() % PAYLOAD_MAXLEN) + 1;
for (int i = 0; i < TEST_PACKETS; ++i)
{
int random_packet_size = (rand() % PAYLOAD_MAXLEN) + 1;
for (int i = 0; i < size; ++i)
media[i] = (i + size) % CHAR_MAX;
for (int i = 0; i < random_packet_size; ++i)
{
media[i] = (i + random_packet_size) % CHAR_MAX;
}
if (send->push_frame(media.get(), size, RTP_NO_FLAGS) != RTP_OK) {
fprintf(stderr, "Failed to send frame!\n");
return -1;
}
std::cout << "Sending RTP frame " << i + 1 << '/' << TEST_PACKETS
<< ". Payload size: " << random_packet_size << std::endl;
auto frame = recv->pull_frame();
if (memcmp(frame->payload, media.get(), size))
LOG_ERROR("frame is corrupted!");
fprintf(stderr, "received frame of size %u, rand %d\n", frame->payload_len, size);
uvgrtp::frame::dealloc_frame(frame);
if (send->push_frame(media.get(), random_packet_size, RTP_NO_FLAGS) != RTP_OK)
{
cleanup(ctx, local_session, remote_session, send, recv);
std::cerr << "Failed to send frame!" << std::endl;
return EXIT_FAILURE;
}
}
}
#if 0
for (int i = 0; i < PAYLOAD_MAXLEN; ++i)
custom_media[i] = i % CHAR_MAX;
if (send->push_frame(std::move(custom_media), PAYLOAD_MAXLEN, RTP_NO_FLAGS) != RTP_OK) {
fprintf(stderr, "Failed to send frame!\n");
return -1;
}
auto frame = recv->pull_frame();
/* Verify that all packets were received without corruption */
for (int i = 0; i < PAYLOAD_MAXLEN; ++i) {
if (frame->payload[i] != (i % CHAR_MAX))
fprintf(stderr, "frame was corrupted during transfer!\n");
}
/* the frame must be destroyed manually */
(void)uvgrtp::frame::dealloc_frame(frame);
/* The input data size doesn't always have to be larger than MTU,
* i.e., you can still send small packets */
uint8_t data[5] = { 0x1, 0x5, 0xa, 0x77, 0xff };
send->push_frame(data, sizeof(data), 0);
frame = recv->pull_frame();
if (memcmp(data, frame->payload, 5))
fprintf(stderr, "frame was corrupted during transfer!\n");
/* the frame must be destroyed manually */
(void)uvgrtp::frame::dealloc_frame(frame);
#endif
/* Session must be destroyed manually */
ctx.destroy_session(sess);
ctx.destroy_session(local_session);
ctx.destroy_session(remote_session);
return 0;
return EXIT_SUCCESS;
}
void rtp_receive_hook(void *arg, uvgrtp::frame::rtp_frame *frame)
{
std::cout << "Received RTP frame. Payload size: " << frame->payload_len << std::endl;
/* Now we own the frame. Here you could give the frame to the application
* if f.ex "arg" was some application-specific pointer
*
* arg->copy_frame(frame) or whatever
*
* When we're done with the frame, it must be deallocated manually */
(void)uvgrtp::frame::dealloc_frame(frame);
}
void cleanup(uvgrtp::context &ctx, uvgrtp::session *local_session, uvgrtp::session *remote_session,
uvgrtp::media_stream *send, uvgrtp::media_stream *receive)
{
if (send)
{
local_session->destroy_stream(send);
}
if (receive)
{
remote_session->destroy_stream(receive);
}
if (local_session)
{
// Session must be destroyed manually
ctx.destroy_session(local_session);
}
if (remote_session)
{
// Session must be destroyed manually
ctx.destroy_session(remote_session);
}
}

View File

@ -1,21 +1,132 @@
#include <uvgrtp/lib.hh>
#include <climits>
#define PAYLOAD_MAXLEN 256
#define KEY_SIZE 16
#define SALT_SIZE 14
/* Key and salt for the SRTP session of sender and receiver
/* Encryption is also supported by uvgRTP. Encryption is facilitated
* by Secure RTP (SRTP) protocol. In order to use SRTP, the encryption
* context must be exchanged in some way. uvgRTP offers two main methods
* for exchanging the encryption contexts.
*
* NOTE: uvgRTP only supports 128 bit keys and 112 bit salts */
uint8_t key[KEY_SIZE] = { 0 };
uint8_t salt[SALT_SIZE] = { 0 };
* This example presents the user implemented encryption key
* negotiation. In this scenario the encryption keys are exchanged
* by the application and handed over to uvgRTP. */
void thread_func(void)
// network parameters of example
constexpr char SENDER_ADDRESS[] = "127.0.0.1";
constexpr uint16_t LOCAL_PORT = 8888;
constexpr char RECEIVER_ADDRESS[] = "127.0.0.1";
constexpr uint16_t REMOTE_PORT = 8890;
// encryption parameters of example
enum Key_length{SRTP_128 = 128, SRTP_196 = 196, SRTP_256 = 256};
constexpr Key_length KEY_SIZE = SRTP_256;
constexpr int KEY_SIZE_BYTES = KEY_SIZE/8;
constexpr int SALT_SIZE = 112;
constexpr int SALT_SIZE_BYTES = SALT_SIZE/8;
// demonstration parameters
constexpr auto EXAMPLE_DURATION = std::chrono::seconds(5);
constexpr int FRAME_RATE = 30; // fps
constexpr int SEND_TEST_PACKETS = (EXAMPLE_DURATION.count() - 1)*FRAME_RATE;
constexpr int PACKET_INTERVAL_MS = 1000/FRAME_RATE;
constexpr int RECEIVER_WAIT_TIME_MS = 100;
void process_frame(uvgrtp::frame::rtp_frame *frame);
void receive_func(uint8_t key[KEY_SIZE_BYTES], uint8_t salt[SALT_SIZE_BYTES]);
void wait_until_next_frame(std::chrono::steady_clock::time_point& start, int frame_index);
int main(void)
{
uvgrtp::context ctx;
// first we check if crypto has been included before attempting to use it
if (!ctx.crypto_enabled())
{
std::cerr << "Cannot run SRTP example if crypto is not included in uvgRTP!"
<< std::endl;
return EXIT_FAILURE;
}
/* Key and salt for the SRTP session of sender and receiver
*
* NOTE: uvgRTP supports 128, 196 and 256 bit keys and 112 bit salts */
uint8_t key[KEY_SIZE_BYTES] = { 0 };
uint8_t salt[SALT_SIZE_BYTES] = { 0 };
// initialize SRTP key and salt with dummy values
for (int i = 0; i < KEY_SIZE_BYTES; ++i)
key[i] = i;
for (int i = 0; i < SALT_SIZE_BYTES; ++i)
salt[i] = i * 2;
std::cout << "Starting uvgRTP SRTP user provided encryption key example. Using key:"
<< key << " and salt: " << salt << std::endl;
// Create separate thread for the receiver
std::thread receiver(receive_func, key, salt);
// Enable SRTP and let user manage the keys
unsigned flags = RCE_SRTP | RCE_SRTP_KMNGMNT_USER | RCE_SRTP_KEYSIZE_256;
uvgrtp::session *sender_session = ctx.create_session(RECEIVER_ADDRESS);
uvgrtp::media_stream *send = sender_session->create_stream(LOCAL_PORT, REMOTE_PORT,
RTP_FORMAT_GENERIC, flags);
if (send)
{
/* When using user managed keys, before anything else can be done
* the add_srtp_ctx() must be called with the user SRTP key and salt.
*
* All calls to "send" that try to modify and or/use the newly
* created media stream before calling add_srtp_ctx() will fail
* if SRTP is enabled */
send->add_srtp_ctx(key, salt);
// All media is now encrypted/decrypted automatically
char *message = (char *)"Hello, world!";
size_t msg_len = strlen(message);
auto start = std::chrono::steady_clock::now();
for (unsigned int i = 0; i < SEND_TEST_PACKETS; ++i)
{
if ((i+1)%10 == 0 || i == 0) // print every 10 frames and first
std::cout << "Sending frame # " << i + 1 << '/' << SEND_TEST_PACKETS << std::endl;
if (send->push_frame((uint8_t *)message, msg_len, RTP_NO_FLAGS) != RTP_OK)
{
std::cerr << "Failed to send frame" << std::endl;
}
// wait until it is time to send the next frame. Included only for
// demostration purposes since you can use uvgRTP to send packets as fast as desired
wait_until_next_frame(start, i);
}
sender_session->destroy_stream(send);
}
else
{
std::cerr << "Failed to create SRTP sender" << std::endl;
}
if (receiver.joinable())
{
receiver.join();
}
if (sender_session)
ctx.destroy_session(sender_session);
return EXIT_SUCCESS;
}
void receive_func(uint8_t key[KEY_SIZE_BYTES], uint8_t salt[SALT_SIZE_BYTES])
{
/* See sending.cc for more details */
uvgrtp::context ctx;
uvgrtp::session *sess = ctx.create_session("127.0.0.1");
uvgrtp::session *receiver_session = ctx.create_session(SENDER_ADDRESS);
/* Enable SRTP and let user manage keys */
unsigned flags = RCE_SRTP | RCE_SRTP_KMNGMNT_USER;
@ -23,64 +134,70 @@ void thread_func(void)
/* With user-managed keys, you have the option to use 192- and 256-bit keys.
*
* If 192- or 256-bit key size is specified in the flags, add_srtp_ctx() expects
* the key paramter to be 24 or 32 bytes long, respectively. */
if (0)
flags |= RCE_SRTP_KEYSIZE_192;
* the key parameter to be 24 or 32 bytes long, respectively. */
flags |= RCE_SRTP_KEYSIZE_256;
/* See sending.cc for more details about create_stream() */
uvgrtp::media_stream *recv = sess->create_stream(8889, 8888, RTP_FORMAT_GENERIC, flags);
uvgrtp::media_stream *recv = receiver_session->create_stream(REMOTE_PORT, LOCAL_PORT,
RTP_FORMAT_GENERIC, flags);
/* Before anything else can be done,
* add_srtp_ctx() must be called with the SRTP key and salt.
*
* All calls to "recv" that try to modify and or/use the newly
* created media stream before calling add_srtp_ctx() will fail */
recv->add_srtp_ctx(key, salt);
// Receive frames by pulling for EXAMPLE_DURATION milliseconds
if (recv)
{
/* Before anything else can be done,
* add_srtp_ctx() must be called with the SRTP key and salt.
*
* All calls to "recv" that try to modify and or/use the newly
* created media stream before calling add_srtp_ctx() will fail */
recv->add_srtp_ctx(key, salt);
for (;;) {
auto frame = recv->pull_frame();
fprintf(stderr, "Message: '%s'\n", frame->payload);
std::cout << "Start receiving frames for " << EXAMPLE_DURATION.count() << " s" << std::endl;
auto start = std::chrono::steady_clock::now();
/* the frame must be destroyed manually */
(void)uvgrtp::frame::dealloc_frame(frame);
uvgrtp::frame::rtp_frame *frame = nullptr;
while (std::chrono::steady_clock::now() - start < EXAMPLE_DURATION)
{
/* You can specify a timeout for the operation and if the a frame is not received
* within that time limit, pull_frame() returns a nullptr
*
* The parameter tells how long time a frame is waited in milliseconds */
frame = recv->pull_frame(RECEIVER_WAIT_TIME_MS);
if (frame)
{
process_frame(frame);
}
}
receiver_session->destroy_stream(recv);
}
if (receiver_session)
{
ctx.destroy_session(receiver_session);
}
}
int main(void)
void process_frame(uvgrtp::frame::rtp_frame *frame)
{
/* initialize SRTP key and salt */
for (int i = 0; i < KEY_SIZE; ++i)
key[i] = i;
std::string payload = std::string((char*)frame->payload, frame->payload_len);
for (int i = 0; i < SALT_SIZE; ++i)
salt[i] = i * 2;
std::cout << "Received SRTP frame. Payload: " << payload << std::endl;
/* Create separate thread for the receiver */
new std::thread(thread_func);
/* See sending.cc for more details */
uvgrtp::context ctx;
uvgrtp::session *sess = ctx.create_session("127.0.0.1");
/* Enable SRTP and let user manage keys */
unsigned flags = RCE_SRTP | RCE_SRTP_KMNGMNT_USER;
/* See sending.cc for more details about create_stream() */
uvgrtp::media_stream *send = sess->create_stream(8888, 8889, RTP_FORMAT_GENERIC, flags);
/* Before anything else can be done,
* add_srtp_ctx() must be called with the SRTP key and salt.
*
* All calls to "send" that try to modify and or/use the newly
* created media stream before calling add_srtp_ctx() will fail */
send->add_srtp_ctx(key, salt);
/* All media is now encrypted/decrypted automatically */
char *message = (char *)"Hello, world!";
size_t msg_len = strlen(message);
for (;;) {
send->push_frame((uint8_t *)message, msg_len, RTP_NO_FLAGS);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
/* When we receive a frame, the ownership of the frame belongs to us and
* when we're done with it, we need to deallocate the frame */
(void)uvgrtp::frame::dealloc_frame(frame);
}
void wait_until_next_frame(std::chrono::steady_clock::time_point &start, int frame_index)
{
// wait until it is time to send the next frame. Simulates a steady sending pace
// and included only for demostration purposes since you can use uvgRTP to send
// packets as fast as desired
auto time_since_start = std::chrono::steady_clock::now() - start;
auto next_frame_time = (frame_index + 1)*std::chrono::milliseconds(PACKET_INTERVAL_MS);
if (next_frame_time > time_since_start)
{
std::this_thread::sleep_for(next_frame_time - time_since_start);
}
}

View File

@ -0,0 +1,113 @@
TEMPLATE = app
CONFIG += console c++11
CONFIG -= app_bundle
CONFIG -= qt
# define here which example you want to build. Remember to rerun qmake
CONFIG += selectBinding
selectBinding {
DEFINES += __RTP_NO_CRYPTO__
message("Building binding example.")
TARGET = Binding
SOURCES += \
binding.cc \
}
selectConfiguration {
message("Building configuration example.")
TARGET = Configuration
SOURCES += \
configuration.cc \
}
selectCustomTimestamps {
message("Building timestamp example.")
TARGET = Timestamp
SOURCES += \
custom_timestamps.cc \
}
selectReceiveHook {
message("Building receive hook example.")
TARGET = ReceiveHook
SOURCES += \
receiving_hook.cc \
}
selectReceivePoll {
message("Building receive poll example.")
TARGET = ReceivePoll
SOURCES += \
receiving_poll.cc \
}
selectRTCPhook {
message("Building RTCP hook example.")
TARGET = RTCPHook
SOURCES += \
rtcp_hook.cc \
}
configSending {
message("Building sending example.")
TARGET = Sending
SOURCES += \
sending.cc \
}
selectSendingGeneric {
message("Building generic sending example.")
TARGET = SendingGeneric
SOURCES += \
sending_generic.cc \
}
selectSRTPUser {
message("Building user managed SRTP example.")
TARGET = SRTPUser
SOURCES += \
srtp_user.cc \
}
selectZRTPMultistream {
message("Building ZRTP multistream example.")
TARGET = ZRTPMultistream
SOURCES += \
zrtp_multistream.cc \
}
DISTFILES += \
README.md
# uvgrtp include folder
INCLUDEPATH += ../../include
LIBS += -luvgrtp
# uvgRTP links agains Crypto++ if it is available and excluded.
# You can safely remove its linking if you did not include Crypto++ in uvgRTP.
# Please note that the SRTP and ZRTP examples need Crypto++ to succeed
win32-msvc{
LIBS += -lcryptlib
} else {
LIBS += -lcryptopp
}
win32-msvc{
message("Detected MSVC compiler")
# find the libraries if uvgrtp has been built using CMake instructions
CONFIG(debug, debug|release) {
UVGRTP_LIB_FOLDER = -L$$PWD/../../build/Debug
} else:CONFIG(release, debug|release) {
UVGRTP_LIB_FOLDER = -L$$PWD/../../build/Release
}
LIBS += -lws2_32
LIBS += -ladvapi32
LIBS += $${UVGRTP_LIB_FOLDER}
message("Using the following folder for uvgRTP library:" $${UVGRTP_LIB_FOLDER})
}

View File

@ -2,62 +2,211 @@
#include <climits>
#include <cstring>
void thread_func(void)
{
/* See sending.cc for more details */
uvgrtp::context ctx;
uvgrtp::session *sess = ctx.create_session("127.0.0.1");
/* Zimmermann RTP (ZRTP) is a key management protocol for SRTP. Compared
* to most approaches, using ZRTP can facilitate end-to-end encryption
* of media traffic since the keys are exchanged peer-to-peer.
*
* Using ZRTP in uvgRTP requires only setting it on with RCE_SRTP_KMNGMNT_ZRTP
* flag. Then when creating the media streams, you will encounter a small additional
* wait until the ZRTP negotiation has been completed. ZRTP has to only be negotiatiated
* once per session, since the following media_streams can use the key context from
* the first media_stream.
*
* This example demonstrates usign the ZRTP to negotiate SRTP encryption context
* for multiple media_streams. There are two senders and two receivers representing
* video and audio streams.
*/
/* Enable SRTP and use ZRTP to manage keys */
unsigned flags = RCE_SRTP | RCE_SRTP_KMNGMNT_ZRTP;
// Network parameters of this example
constexpr char SENDER_ADDRESS[] = "127.0.0.1";
constexpr uint16_t SENDER_VIDEO_PORT = 8888;
constexpr uint16_t SENDER_AUDIO_PORT = 8890;
/* Keys creates using Diffie-Hellman mode */
uvgrtp::media_stream *video = sess->create_stream(8889, 8888, RTP_FORMAT_GENERIC, flags);
constexpr char RECEIVER_ADDRESS[] = "127.0.0.1";
constexpr uint16_t RECEIVER_VIDEO_PORT = 7776;
constexpr uint16_t RECEIVER_AUDIO_PORT = 7778;
/* Keys created using Multistream mode */
uvgrtp::media_stream *audio = sess->create_stream(7778, 7777, RTP_FORMAT_GENERIC, flags);
// demonstration parameters of this example
constexpr int VIDEO_PAYLOAD_SIZE = 4000;
constexpr int AUDIO_PAYLOAD_SIZE = 100;
for (;;) {
auto frame = video->pull_frame();
fprintf(stderr, "Message: '%s'\n", frame->payload);
(void)uvgrtp::frame::dealloc_frame(frame);
constexpr auto EXAMPLE_RUN_TIME_S = std::chrono::seconds(5);
constexpr auto RECEIVER_WAIT_TIME_MS = std::chrono::milliseconds(50);
frame = audio->pull_frame();
fprintf(stderr, "Message: '%s'\n", frame->payload);
(void)uvgrtp::frame::dealloc_frame(frame);
}
}
constexpr auto AUDIO_FRAME_INTERVAL_MS = std::chrono::milliseconds(20);
constexpr auto VIDEO_FRAME_INTERVAL_MS = std::chrono::milliseconds(1000/60); // 60 fps video
void receive_function(uvgrtp::session* receiver_session, int flags, std::shared_ptr<std::mutex> print_mutex,
RTP_FORMAT format, uint16_t receiver_port, uint16_t sender_port);
void sender_function(uvgrtp::session* sender_session, int flags, std::shared_ptr<std::mutex> print_mutex,
RTP_FORMAT format, uint16_t sender_port, uint16_t receiver_port, size_t payload_size,
std::chrono::milliseconds frame_interval);
void wait_until_next_frame(std::chrono::steady_clock::time_point& start, std::chrono::milliseconds interval, int frame_index);
int main(void)
{
std::cout << "Starting uvgRTP SRTP together with ZRTP example" << std::endl;
uvgrtp::context receiver_ctx;
// check that Crypto++ has been compiled into uvgRTP, otherwise encryption wont work.
if (!receiver_ctx.crypto_enabled())
{
std::cerr << "Cannot run SRTP example if crypto++ is not included in uvgRTP!"
<< std::endl;
return EXIT_FAILURE;
}
uvgrtp::session *receiver_session = receiver_ctx.create_session(SENDER_ADDRESS);
std::shared_ptr<std::mutex> print_mutex = std::shared_ptr<std::mutex> (new std::mutex);
/* Create separate thread for the receiver
*
* Because we're using ZRTP for SRTP key management,
* the receiver and sender must communicate with each other
* before the actual media communication starts */
new std::thread(thread_func);
/* See sending.cc for more details */
uvgrtp::context ctx;
uvgrtp::session *sess = ctx.create_session("127.0.0.1");
// Enable SRTP and use ZRTP to manage keys for both sender and receiver*/
unsigned rce_flags = RCE_SRTP | RCE_SRTP_KMNGMNT_ZRTP;
/* Enable SRTP and use ZRTP to manage keys */
unsigned flags = RCE_SRTP | RCE_SRTP_KMNGMNT_ZRTP;
// start the receivers in a separate thread
std::thread a_receiver(receive_function, receiver_session, rce_flags, print_mutex,
RTP_FORMAT_OPUS, RECEIVER_AUDIO_PORT, SENDER_AUDIO_PORT);
/* Initialize ZRTP and negotiate the keys used to encrypt the media */
uvgrtp::media_stream *video = sess->create_stream(8888, 8889, RTP_FORMAT_GENERIC, flags);
std::thread v_receiver(receive_function, receiver_session, rce_flags, print_mutex,
RTP_FORMAT_H266, RECEIVER_VIDEO_PORT, SENDER_VIDEO_PORT);
uvgrtp::context sender_ctx;
uvgrtp::session *sender_session = sender_ctx.create_session(RECEIVER_ADDRESS);
// start the senders in their own threads
std::thread a_sender(sender_function, sender_session, rce_flags, print_mutex,
RTP_FORMAT_OPUS, SENDER_AUDIO_PORT, RECEIVER_AUDIO_PORT,
AUDIO_PAYLOAD_SIZE, AUDIO_FRAME_INTERVAL_MS);
std::thread v_sender(sender_function, sender_session, rce_flags, print_mutex,
RTP_FORMAT_H266, SENDER_VIDEO_PORT, RECEIVER_VIDEO_PORT,
VIDEO_PAYLOAD_SIZE, VIDEO_FRAME_INTERVAL_MS);
// wait until all threads have ended
if (a_receiver.joinable())
{
a_receiver.join();
}
if (v_receiver.joinable())
{
v_receiver.join();
}
if (a_sender.joinable())
{
a_sender.join();
}
if (v_sender.joinable())
{
v_sender.join();
}
if (sender_session)
sender_ctx.destroy_session(sender_session);
if (receiver_session)
sender_ctx.destroy_session(receiver_session);
std::cout << "ZRTP example finished" << std::endl;
return EXIT_SUCCESS;
}
void receive_function(uvgrtp::session* receiver_session, int flags,
std::shared_ptr<std::mutex> print_mutex,
RTP_FORMAT format, uint16_t receiver_port, uint16_t sender_port)
{
/* Keys created using Multistream mode */
uvgrtp::media_stream *receiver_stream =
receiver_session->create_stream(receiver_port, sender_port, format, flags);
if (receiver_stream)
{
uvgrtp::frame::rtp_frame *frame = nullptr;
std::cout << "Start receiving frames for " << EXAMPLE_RUN_TIME_S.count()
<< " seconds" << std::endl;
auto start = std::chrono::steady_clock::now();
while (std::chrono::steady_clock::now() - start < EXAMPLE_RUN_TIME_S)
{
/* You can specify a timeout for the operation and if the a frame is not received
* within that time limit, pull_frame() returns a nullptr
*
* The parameter tells how long time a frame is waited in milliseconds */
frame = receiver_stream->pull_frame(RECEIVER_WAIT_TIME_MS.count());
if (frame)
{
print_mutex->lock();
std::cout << "Received a frame. Payload size: " << frame->payload_len << std::endl;
print_mutex->unlock();
// Process the frame here
(void)uvgrtp::frame::dealloc_frame(frame);
}
}
receiver_session->destroy_stream(receiver_stream);
}
}
void sender_function(uvgrtp::session* sender_session, int flags, std::shared_ptr<std::mutex> print_mutex,
RTP_FORMAT format, uint16_t sender_port, uint16_t receiver_port, size_t payload_size,
std::chrono::milliseconds frame_interval)
{
/* The first call to create_stream() creates keys for the session using Diffie-Hellman
* key exchange and all subsequent calls to create_stream() initialize keys for the
* stream using Multistream mode */
uvgrtp::media_stream *audio = sess->create_stream(7777, 7778, RTP_FORMAT_GENERIC, flags);
uvgrtp::media_stream *sender_audio_strm = sender_session->create_stream(sender_port,
receiver_port,
format, flags);
char *message = (char *)"Hello, world!";
size_t msg_len = strlen(message);
if (sender_audio_strm)
{
auto start = std::chrono::steady_clock::now();
for (;;) {
video->push_frame((uint8_t *)message, msg_len, RTP_NO_FLAGS);
audio->push_frame((uint8_t *)message, msg_len, RTP_NO_FLAGS);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
for (int i = 0; std::chrono::steady_clock::now() < (start + EXAMPLE_RUN_TIME_S); ++i)
{
/*
print_mutex->lock();
std::cout << "Sending frame" << std::endl;
print_mutex->unlock();
*/
std::unique_ptr<uint8_t[]> dummy_audio_frame = std::unique_ptr<uint8_t[]>(new uint8_t[payload_size]);
if (sender_audio_strm->push_frame(std::move(dummy_audio_frame), payload_size, RTP_NO_FLAGS) != RTP_OK)
{
std::cerr << "Failed to send frame" << std::endl;
}
// wait until it is time to send the next frame. Included only for
// demostration purposes since you can use uvgRTP to send packets as fast as desired
wait_until_next_frame(start, frame_interval, i);
}
}
}
void wait_until_next_frame(std::chrono::steady_clock::time_point& start,
std::chrono::milliseconds interval, int frame_index)
{
// wait until it is time to send the next frame. Simulates a steady sending pace
// and included only for demostration purposes since you can use uvgRTP to send
// packets as fast as desired
auto time_since_start = std::chrono::steady_clock::now() - start;
auto next_frame_time = (frame_index + 1)*interval;
if (next_frame_time > time_since_start)
{
std::this_thread::sleep_for(next_frame_time - time_since_start);
}
}

View File

@ -82,6 +82,8 @@ namespace uvgrtp {
std::string& get_cname();
/// \endcond
bool crypto_enabled() const;
private:
/* Generate CNAME for participant using host and login names */
std::string generate_cname();

View File

@ -1,6 +1,6 @@
#include "clock.hh"
#include "uvgrtp/clock.hh"
#include "debug.hh"
#include "uvgrtp/debug.hh"
#include <stdio.h>

View File

@ -1,6 +1,6 @@
#include "crypto.hh"
#include "uvgrtp/crypto.hh"
#include "debug.hh"
#include "uvgrtp/debug.hh"

View File

@ -1,8 +1,8 @@
#include "dispatch.hh"
#include "queue.hh"
#include "socket.hh"
#include "debug.hh"
#include "uvgrtp/socket.hh"
#include "uvgrtp/debug.hh"
#ifndef _WIN32

View File

@ -1,8 +1,8 @@
#pragma once
#include "runner.hh"
#include "uvgrtp/runner.hh"
#include "util.hh"
#include "uvgrtp/util.hh"
#include <condition_variable>
#include <queue>

View File

@ -2,7 +2,8 @@
#include "../queue.hh"
#include "../rtp.hh"
#include "debug.hh"
#include "uvgrtp/debug.hh"
#include <cstdint>
#include <cstring>

View File

@ -1,9 +1,11 @@
#pragma once
#include "h26x.hh"
#include "util.hh"
#include "frame.hh"
#include "socket.hh"
#include "uvgrtp/clock.hh"
#include "uvgrtp/util.hh"
#include "uvgrtp/frame.hh"
#include "uvgrtp/socket.hh"
#include <deque>

View File

@ -3,7 +3,8 @@
#include "../srtp/srtcp.hh"
#include "../rtp.hh"
#include "../queue.hh"
#include "debug.hh"
#include "uvgrtp/debug.hh"
#include <cstdint>
#include <cstring>

View File

@ -1,9 +1,11 @@
#pragma once
#include "h26x.hh"
#include "util.hh"
#include "frame.hh"
#include "socket.hh"
#include "uvgrtp/clock.hh"
#include "uvgrtp/util.hh"
#include "uvgrtp/frame.hh"
#include "uvgrtp/socket.hh"
#include <deque>
#include <map>

View File

@ -2,8 +2,9 @@
#include "../rtp.hh"
#include "../queue.hh"
#include "frame.hh"
#include "debug.hh"
#include "uvgrtp/frame.hh"
#include "uvgrtp/debug.hh"
#include <cstdint>
#include <cstring>

View File

@ -2,10 +2,10 @@
#include "h26x.hh"
#include "util.hh"
#include "socket.hh"
#include "frame.hh"
#include "uvgrtp/util.hh"
#include "uvgrtp/socket.hh"
#include "uvgrtp/clock.hh"
#include "uvgrtp/frame.hh"
#include <deque>

View File

@ -2,8 +2,9 @@
#include "../rtp.hh"
#include "../queue.hh"
#include "socket.hh"
#include "debug.hh"
#include "uvgrtp/socket.hh"
#include "uvgrtp/debug.hh"
#include <cstdint>

View File

@ -1,9 +1,9 @@
#pragma once
#include "media.hh"
#include "util.hh"
#include "socket.hh"
#include "clock.hh"
#include "uvgrtp/util.hh"
#include "uvgrtp/socket.hh"
#include "uvgrtp/clock.hh"
#include <deque>

View File

@ -1,9 +1,10 @@
#include "media.hh"
#include "../rtp.hh"
#include "socket.hh"
#include "../queue.hh"
#include "debug.hh"
#include "uvgrtp/socket.hh"
#include "uvgrtp/debug.hh"
#include <map>
#include <unordered_map>

View File

@ -1,6 +1,6 @@
#pragma once
#include "util.hh"
#include "uvgrtp/util.hh"
#include <map>
#include <memory>

View File

@ -1,7 +1,7 @@
#include "frame.hh"
#include "uvgrtp/frame.hh"
#include "util.hh"
#include "debug.hh"
#include "uvgrtp/util.hh"
#include "uvgrtp/debug.hh"
#include <cstring>

View File

@ -1,8 +1,8 @@
#include "holepuncher.hh"
#include "clock.hh"
#include "socket.hh"
#include "debug.hh"
#include "uvgrtp/clock.hh"
#include "uvgrtp/socket.hh"
#include "uvgrtp/debug.hh"
#define THRESHOLD 2000

View File

@ -1,7 +1,7 @@
#pragma once
#include "runner.hh"
#include "util.hh"
#include "uvgrtp/runner.hh"
#include "uvgrtp/util.hh"
#include <atomic>

View File

@ -1,6 +1,6 @@
#include "hostname.hh"
#include "debug.hh"
#include "uvgrtp/debug.hh"
#ifdef _WIN32
//#include <windows.h>

View File

@ -1,10 +1,11 @@
#include "lib.hh"
#include "uvgrtp/lib.hh"
#include "uvgrtp/version.hh"
#include "uvgrtp/session.hh"
#include "uvgrtp/debug.hh"
#include "debug.hh"
#include "hostname.hh"
#include "random.hh"
#include "session.hh"
#include "version.hh"
#include <cstdlib>
#include <cstring>
@ -80,3 +81,8 @@ std::string& uvgrtp::context::get_cname()
{
return cname_;
}
bool uvgrtp::context::crypto_enabled() const
{
return uvgrtp::crypto::enabled();
}

View File

@ -1,17 +1,17 @@
#include "media_stream.hh"
#include "uvgrtp/media_stream.hh"
#include "formats/h264.hh"
#include "formats/h265.hh"
#include "formats/h266.hh"
#include "debug.hh"
#include "uvgrtp/debug.hh"
#include "random.hh"
#include "rtp.hh"
#include "zrtp.hh"
#include "holepuncher.hh"
#include "pkt_dispatch.hh"
#include "rtcp.hh"
#include "socket.hh"
#include "uvgrtp/rtcp.hh"
#include "uvgrtp/socket.hh"
#include "srtp/srtcp.hh"
#include "srtp/srtp.hh"
#include "formats/media.hh"

View File

@ -1,6 +1,6 @@
#include "multicast.hh"
#include "frame.hh"
#include "uvgrtp/frame.hh"
uvgrtp::multicast::multicast()
{

View File

@ -1,6 +1,6 @@
#pragma once
#include "util.hh"
#include "uvgrtp/util.hh"
namespace uvgrtp {
class connection;

View File

@ -1,10 +1,11 @@
#include "pkt_dispatch.hh"
#include "frame.hh"
#include "socket.hh"
#include "debug.hh"
#include "random.hh"
#include "util.hh"
#include "uvgrtp/util.hh"
#include "uvgrtp/frame.hh"
#include "uvgrtp/socket.hh"
#include "uvgrtp/debug.hh"
#include <chrono>

View File

@ -1,6 +1,6 @@
#pragma once
#include "util.hh"
#include "uvgrtp/util.hh"
#include <mutex>
#include <unordered_map>

View File

@ -1,8 +1,8 @@
#include "poll.hh"
#include "multicast.hh"
#include "socket.hh"
#include "debug.hh"
#include "uvgrtp/socket.hh"
#include "uvgrtp/debug.hh"
#ifdef _WIN32
#include <winsock2.h>

View File

@ -1,6 +1,6 @@
#pragma once
#include "util.hh"
#include "uvgrtp/util.hh"
#include <vector>

View File

@ -6,9 +6,11 @@
#include "rtp.hh"
#include "srtp/base.hh"
#include "debug.hh"
#include "random.hh"
#include "uvgrtp/debug.hh"
#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>

View File

@ -1,9 +1,9 @@
#pragma once
#include "frame.hh"
#include "socket.hh"
#include "util.hh"
#include "uvgrtp/frame.hh"
#include "uvgrtp/socket.hh"
#include "uvgrtp/util.hh"
#include <atomic>
#include <memory>

View File

@ -1,6 +1,6 @@
#include "random.hh"
#include "debug.hh"
#include "uvgrtp/debug.hh"
#ifdef _WIN32
#include <winsock2.h>

View File

@ -1,5 +1,5 @@
#include "util.hh"
#include "uvgrtp/util.hh"
namespace uvgrtp {
namespace random {

View File

@ -1,13 +1,14 @@
#include "rtcp.hh"
#include "uvgrtp/rtcp.hh"
#include "hostname.hh"
#include "poll.hh"
#include "debug.hh"
#include "util.hh"
#include "rtp.hh"
#include "frame.hh"
#include "srtp/srtcp.hh"
#include "uvgrtp/debug.hh"
#include "uvgrtp/util.hh"
#include "uvgrtp/frame.hh"
#ifndef _WIN32
#include <sys/time.h>
#endif

View File

@ -1,9 +1,10 @@
#include "rtp.hh"
#include "frame.hh"
#include "debug.hh"
#include "random.hh"
#include "uvgrtp/frame.hh"
#include "uvgrtp/debug.hh"
#ifndef _WIN32
#include <arpa/inet.h>
#include <unistd.h>

View File

@ -1,7 +1,7 @@
#pragma once
#include "clock.hh"
#include "util.hh"
#include "uvgrtp/clock.hh"
#include "uvgrtp/util.hh"
namespace uvgrtp {

View File

@ -1,4 +1,4 @@
#include "runner.hh"
#include "uvgrtp/runner.hh"
uvgrtp::runner::runner():
active_(false), runner_(nullptr)

View File

@ -1,9 +1,9 @@
#include "session.hh"
#include "uvgrtp/session.hh"
#include "media_stream.hh"
#include "uvgrtp/media_stream.hh"
#include "zrtp.hh"
#include "crypto.hh"
#include "debug.hh"
#include "uvgrtp/crypto.hh"
#include "uvgrtp/debug.hh"
uvgrtp::session::session(std::string addr):

View File

@ -1,7 +1,7 @@
#include "socket.hh"
#include "uvgrtp/socket.hh"
#include "debug.hh"
#include "util.hh"
#include "uvgrtp/debug.hh"
#include "uvgrtp/util.hh"
#ifdef _WIN32
#include <winsock2.h>

View File

@ -1,7 +1,7 @@
#include "base.hh"
#include "crypto.hh"
#include "debug.hh"
#include "uvgrtp/crypto.hh"
#include "uvgrtp/debug.hh"
#include <cstring>
#include <iostream>

View File

@ -1,6 +1,6 @@
#pragma once
#include "util.hh"
#include "uvgrtp/util.hh"
#ifdef _WIN32
#include <winsock2.h>

View File

@ -1,7 +1,7 @@
#include "srtcp.hh"
#include "crypto.hh"
#include "debug.hh"
#include "uvgrtp/crypto.hh"
#include "uvgrtp/debug.hh"
#include <cstring>
#include <iostream>

View File

@ -1,9 +1,10 @@
#include "srtp.hh"
#include "base.hh"
#include "crypto.hh"
#include "debug.hh"
#include "frame.hh"
#include "uvgrtp/crypto.hh"
#include "uvgrtp/debug.hh"
#include "uvgrtp/frame.hh"
#include <cstring>
#include <iostream>

View File

@ -6,12 +6,15 @@
#include "zrtp/dh_kxchng.hh"
#include "zrtp/hello.hh"
#include "zrtp/hello_ack.hh"
#include "socket.hh"
#include "debug.hh"
#include "crypto.hh"
#include "random.hh"
#include "uvgrtp/socket.hh"
#include "uvgrtp/debug.hh"
#include "uvgrtp/crypto.hh"
#include <cstring>
#include <thread>

View File

@ -1,10 +1,11 @@
#include "commit.hh"
#include "../zrtp.hh"
#include "crypto.hh"
#include "debug.hh"
#include "frame.hh"
#include "socket.hh"
#include "uvgrtp/crypto.hh"
#include "uvgrtp/debug.hh"
#include "uvgrtp/frame.hh"
#include "uvgrtp/socket.hh"
#include <cassert>
#include <cstring>

View File

@ -3,7 +3,7 @@
#include "defines.hh"
#include "zrtp_message.hh"
#include "util.hh"
#include "uvgrtp/util.hh"
#ifndef _WIN32
#include <netinet/in.h>

View File

@ -1,10 +1,11 @@
#include "confack.hh"
#include "../zrtp.hh"
#include "crypto.hh"
#include "frame.hh"
#include "socket.hh"
#include "debug.hh"
#include "uvgrtp/crypto.hh"
#include "uvgrtp/frame.hh"
#include "uvgrtp/socket.hh"
#include "uvgrtp/debug.hh"
#include <cstring>

View File

@ -4,7 +4,7 @@
#include "zrtp_receiver.hh"
#include "zrtp_message.hh"
#include "util.hh"
#include "uvgrtp/util.hh"
#ifndef _WIN32
#include <netinet/in.h>

View File

@ -1,10 +1,11 @@
#include "confirm.hh"
#include "../zrtp.hh"
#include "crypto.hh"
#include "frame.hh"
#include "socket.hh"
#include "debug.hh"
#include "uvgrtp/crypto.hh"
#include "uvgrtp/frame.hh"
#include "uvgrtp/socket.hh"
#include "uvgrtp/debug.hh"
#include <cstring>

View File

@ -4,7 +4,7 @@
#include "defines.hh"
#include "zrtp_message.hh"
#include "util.hh"
#include "uvgrtp/util.hh"
#ifndef _WIN32
#include <netinet/in.h>

View File

@ -1,6 +1,6 @@
#pragma once
#include "util.hh"
#include "uvgrtp/util.hh"
namespace uvgrtp {

View File

@ -3,10 +3,11 @@
#include "zrtp_receiver.hh"
#include "../zrtp.hh"
#include "crypto.hh"
#include "frame.hh"
#include "socket.hh"
#include "debug.hh"
#include "uvgrtp/crypto.hh"
#include "uvgrtp/frame.hh"
#include "uvgrtp/socket.hh"
#include "uvgrtp/debug.hh"
#include <cstring>

View File

@ -3,7 +3,7 @@
#include "defines.hh"
#include "zrtp_message.hh"
#include "util.hh"
#include "uvgrtp/util.hh"
#ifndef _WIN32
#include <netinet/in.h>

View File

@ -3,11 +3,11 @@
#include "zrtp_receiver.hh"
#include "../zrtp.hh"
#include "crypto.hh"
#include "socket.hh"
#include "frame.hh"
#include "debug.hh"
#include "uvgrtp/crypto.hh"
#include "uvgrtp/socket.hh"
#include "uvgrtp/frame.hh"
#include "uvgrtp/debug.hh"
#include <cstring>

View File

@ -3,7 +3,7 @@
#include "defines.hh"
#include "zrtp_message.hh"
#include "util.hh"
#include "uvgrtp/util.hh"
#ifndef _WIN32
#include <netinet/in.h>

View File

@ -3,12 +3,11 @@
#include "zrtp_receiver.hh"
#include "../zrtp.hh"
#include "crypto.hh"
#include "frame.hh"
#include "socket.hh"
#include "debug.hh"
#include "uvgrtp/crypto.hh"
#include "uvgrtp/frame.hh"
#include "uvgrtp/socket.hh"
#include "uvgrtp/debug.hh"
#include <cstring>

View File

@ -3,7 +3,7 @@
#include "defines.hh"
#include "zrtp_message.hh"
#include "util.hh"
#include "uvgrtp/util.hh"
#ifndef _WIN32
#include <netinet/in.h>

View File

@ -3,10 +3,11 @@
#include "zrtp_receiver.hh"
#include "../zrtp.hh"
#include "crypto.hh"
#include "frame.hh"
#include "socket.hh"
#include "debug.hh"
#include "uvgrtp/crypto.hh"
#include "uvgrtp/frame.hh"
#include "uvgrtp/socket.hh"
#include "uvgrtp/debug.hh"
#include <cstring>

View File

@ -3,7 +3,7 @@
#include "defines.hh"
#include "zrtp_message.hh"
#include "util.hh"
#include "uvgrtp/util.hh"
#ifndef _WIN32
#include <netinet/in.h>

View File

@ -1,9 +1,9 @@
#include "zrtp_message.hh"
#include "frame.hh"
#include "socket.hh"
#include "uvgrtp/frame.hh"
#include "uvgrtp/socket.hh"
#include "debug.hh"
#include "uvgrtp/debug.hh"
#include <string>

View File

@ -1,9 +1,10 @@
#pragma once
#include "defines.hh"
#include "util.hh"
#include "../zrtp.hh"
#include "frame.hh"
#include "uvgrtp/frame.hh"
#include "uvgrtp/util.hh"
namespace uvgrtp {
@ -40,4 +41,4 @@ namespace uvgrtp {
};
namespace uvg_rtp = uvgrtp;
namespace uvg_rtp = uvgrtp;

View File

@ -8,11 +8,13 @@
#include "hello.hh"
#include "hello_ack.hh"
#include "socket.hh"
#include "crypto.hh"
#include "../poll.hh"
#include "debug.hh"
#include "util.hh"
#include "uvgrtp/socket.hh"
#include "uvgrtp/crypto.hh"
#include "uvgrtp/debug.hh"
#include "uvgrtp/util.hh"
#ifdef _WIN32
#include <winsock2.h>

View File

@ -1,6 +1,6 @@
#pragma once
#include "util.hh"
#include "uvgrtp/util.hh"
#ifndef _WIN32
#include <netinet/in.h>

View File

@ -1,4 +1,4 @@
#include "lib.hh"
#include "uvgrtp/lib.hh"
#include <gtest/gtest.h>
TEST(VersionTests, version) {

View File

@ -1,4 +1,4 @@
#include "lib.hh"
#include "uvgrtp/lib.hh"
#include <gtest/gtest.h>

View File

@ -1,4 +1,4 @@
#include "lib.hh"
#include "uvgrtp/lib.hh"
#include <gtest/gtest.h>
constexpr char LOCAL_INTERFACE[] = "127.0.0.1";

View File

@ -1,4 +1,4 @@
#include "lib.hh"
#include "uvgrtp/lib.hh"
#include <gtest/gtest.h>
constexpr uint16_t SEND_PORT = 9100;

View File

@ -1,4 +1,4 @@
#include "lib.hh"
#include "uvgrtp/lib.hh"
#include <gtest/gtest.h>

View File

@ -79,17 +79,17 @@ SOURCES += \
src/srtp/srtcp.cc \
HEADERS += \
include/clock.hh \
include/crypto.hh \
include/debug.hh \
include/frame.hh \
include/lib.hh \
include/media_stream.hh \
include/rtcp.hh \
include/runner.hh \
include/session.hh \
include/socket.hh \
include/util.hh \
include/uvgrtp/clock.hh \
include/uvgrtp/crypto.hh \
include/uvgrtp/debug.hh \
include/uvgrtp/frame.hh \
include/uvgrtp/lib.hh \
include/uvgrtp/media_stream.hh \
include/uvgrtp/rtcp.hh \
include/uvgrtp/runner.hh \
include/uvgrtp/session.hh \
include/uvgrtp/socket.hh \
include/uvgrtp/util.hh \
include/version.hh \
src/dispatch.hh \
src/holepuncher.hh \