-
Notifications
You must be signed in to change notification settings - Fork 94
/
binding.cc
142 lines (120 loc) · 6.19 KB
/
binding.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
#include <uvgrtp/lib.hh>
#include <iostream>
#include <cstring>
/* 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)
{
std::cout << "Starting uvgRTP binding example" << std::endl;
uvgrtp::context rtp_ctx;
std::pair<std::string, std::string> addresses_sender(LOCAL_INTERFACE, REMOTE_ADDRESS);
uvgrtp::session *sending_session = rtp_ctx.create_session(addresses_sender);
uvgrtp::media_stream *send = sending_session->create_stream(LOCAL_PORT, REMOTE_PORT,
RTP_FORMAT_H265, RCE_NO_FLAGS);
/* 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;
std::pair<std::string, std::string> addresses_receiver(REMOTE_ADDRESS, LOCAL_INTERFACE);
uvgrtp::session *receiving_session = rtp_ctx.create_session(addresses_receiver);
uvgrtp::media_stream *recv = receiving_session->create_stream(REMOTE_PORT, LOCAL_PORT,
RTP_FORMAT_H265, flags);
// 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;
}
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;
// uvgRTP mandates the existance of NAL units so we fake some
std::unique_ptr<uint8_t[]> dummy_frame =
std::unique_ptr<uint8_t[]>(new uint8_t[PAYLOAD_LEN]);
memset(dummy_frame.get(), 'a', PAYLOAD_LEN); // payload
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 (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);
}