-
Notifications
You must be signed in to change notification settings - Fork 93
/
zrtp_multistream.cc
236 lines (191 loc) · 9.46 KB
/
zrtp_multistream.cc
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
#include <uvgrtp/lib.hh>
#include <climits>
#include <cstring>
#include <iostream>
#include <cstring>
/* 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.
*/
// 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;
constexpr char RECEIVER_ADDRESS[] = "127.0.0.1";
constexpr uint16_t RECEIVER_VIDEO_PORT = 7776;
constexpr uint16_t RECEIVER_AUDIO_PORT = 7778;
// demonstration parameters of this example
constexpr int VIDEO_PAYLOAD_SIZE = 4000;
constexpr int AUDIO_PAYLOAD_SIZE = 100;
constexpr auto EXAMPLE_RUN_TIME_S = std::chrono::seconds(2);
constexpr auto RECEIVER_WAIT_TIME_MS = std::chrono::milliseconds(50);
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;
}
std::cout << "Initializing receivers" << std::endl;
std::pair<std::string, std::string> addresses_receiver(RECEIVER_ADDRESS, SENDER_ADDRESS);
uvgrtp::session *receiver_session = receiver_ctx.create_session(addresses_receiver);
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 */
// Enable SRTP and use ZRTP to manage keys for both sender and receiver*/
unsigned rce_dh_flags = RCE_SRTP | RCE_SRTP_KMNGMNT_ZRTP | RCE_ZRTP_DIFFIE_HELLMAN_MODE;
unsigned rce_multistream_flags = RCE_SRTP | RCE_SRTP_KMNGMNT_ZRTP | RCE_ZRTP_MULTISTREAM_MODE;
// start the receivers in a separate thread
std::thread a_receiver(receive_function, receiver_session, rce_dh_flags, print_mutex,
RTP_FORMAT_OPUS, RECEIVER_AUDIO_PORT, SENDER_AUDIO_PORT);
std::thread v_receiver(receive_function, receiver_session, rce_multistream_flags, print_mutex,
RTP_FORMAT_H265, RECEIVER_VIDEO_PORT, SENDER_VIDEO_PORT);
std::cout << "Initializing senders" << std::endl;
uvgrtp::context sender_ctx;
std::pair<std::string, std::string> addresses_sender(SENDER_ADDRESS, RECEIVER_ADDRESS);
uvgrtp::session *sender_session = sender_ctx.create_session(addresses_sender);
// start the senders in their own threads
std::thread a_sender(sender_function, sender_session, rce_dh_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_multistream_flags, print_mutex,
RTP_FORMAT_H265, 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)
{
print_mutex->lock();
std::cout << "Receiver thread port: " << receiver_port << "<-" << sender_port << std::endl;
print_mutex->unlock();
/* 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. Sequence number: " << frame->header.seq << 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)
{
print_mutex->lock();
std::cout << "Sender thread port: " << sender_port << "->" << receiver_port << std::endl;
print_mutex->unlock();
/* 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 *sender_audio_strm = sender_session->create_stream(sender_port,
receiver_port,
format, flags);
if (sender_audio_strm)
{
auto start = std::chrono::steady_clock::now();
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_frame = std::unique_ptr<uint8_t[]>(new uint8_t[payload_size]);
if (format == RTP_FORMAT_H265 && payload_size >= 5)
{
memset(dummy_frame.get(), 'a', payload_size); // data
memset(dummy_frame.get(), 0, 3);
memset(dummy_frame.get() + 3, 1, 1);
memset(dummy_frame.get() + 4, 1, (19 << 1)); // Intra frame NAL type
}
if (sender_audio_strm->push_frame(std::move(dummy_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);
}
}