210 lines
7.5 KiB
C++
210 lines
7.5 KiB
C++
#include <uvgrtp/lib.hh>
|
|
|
|
#include <climits>
|
|
#include <iostream>
|
|
#include <cstring>
|
|
|
|
/* 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.
|
|
*
|
|
* 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. */
|
|
|
|
|
|
// 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;
|
|
|
|
uint8_t* message_data = new uint8_t[msg_len];
|
|
memcpy(message_data, message, msg_len);
|
|
|
|
if (send->push_frame((uint8_t *)message_data, 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 *receiver_session = ctx.create_session(SENDER_ADDRESS);
|
|
|
|
/* Enable SRTP and let user manage keys */
|
|
unsigned flags = RCE_SRTP | RCE_SRTP_KMNGMNT_USER;
|
|
|
|
/* 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 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 = receiver_session->create_stream(REMOTE_PORT, LOCAL_PORT,
|
|
RTP_FORMAT_GENERIC, flags);
|
|
|
|
// 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);
|
|
|
|
std::cout << "Start receiving frames for " << EXAMPLE_DURATION.count() << " s" << std::endl;
|
|
auto start = std::chrono::steady_clock::now();
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
void process_frame(uvgrtp::frame::rtp_frame *frame)
|
|
{
|
|
std::string payload = std::string((char*)frame->payload, frame->payload_len);
|
|
|
|
std::cout << "Received SRTP frame. Payload: " << payload << 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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|