rtcp: Calculate RTP timestamp in SR correctly
Also add the ability to manually set RTP and NTP timestamps in RTP packets.
This commit is contained in:
parent
627fd7e602
commit
63175f8a04
|
@ -11,6 +11,8 @@ add_executable(sending_generic)
|
|||
add_executable(srtp_user) # SRTP user keys example
|
||||
add_executable(srtp_zrtp) # SRTP + ZRTP example
|
||||
add_executable(zrtp_multistream) # ZRTP Multistream example
|
||||
add_executable(sync_sender) # Syncing streams example, sender
|
||||
add_executable(sync_receiver) # Syncing streams example, receiver
|
||||
|
||||
target_sources(binding PRIVATE binding.cc)
|
||||
target_sources(configuration PRIVATE configuration.cc)
|
||||
|
@ -23,7 +25,8 @@ target_sources(sending_generic PRIVATE sending_generic.cc)
|
|||
target_sources(srtp_user PRIVATE srtp_user.cc)
|
||||
target_sources(srtp_zrtp PRIVATE srtp_zrtp.cc)
|
||||
target_sources(zrtp_multistream PRIVATE zrtp_multistream.cc)
|
||||
|
||||
target_sources(sync_sender PRIVATE sync_sender.cc)
|
||||
target_sources(sync_receiver PRIVATE sync_receiver.cc)
|
||||
|
||||
if(MSVC)
|
||||
set(CRYPTOPP_LIB_NAME "cryptlib")
|
||||
|
@ -41,6 +44,8 @@ if(CRYPTOPP_FOUND AND NOT DISABLE_CRYPTO)
|
|||
target_link_libraries(rtcp_hook PRIVATE uvgrtp ${CRYPTOPP_LIB_NAME})
|
||||
target_link_libraries(sending PRIVATE uvgrtp ${CRYPTOPP_LIB_NAME})
|
||||
target_link_libraries(sending_generic PRIVATE uvgrtp ${CRYPTOPP_LIB_NAME})
|
||||
target_link_libraries(sync_sender PRIVATE uvgrtp ${CRYPTOPP_LIB_NAME})
|
||||
target_link_libraries(sync_receiver PRIVATE uvgrtp ${CRYPTOPP_LIB_NAME})
|
||||
else()
|
||||
target_link_libraries(binding PRIVATE uvgrtp)
|
||||
target_link_libraries(configuration PRIVATE uvgrtp)
|
||||
|
@ -50,6 +55,8 @@ else()
|
|||
target_link_libraries(rtcp_hook PRIVATE uvgrtp)
|
||||
target_link_libraries(sending PRIVATE uvgrtp)
|
||||
target_link_libraries(sending_generic PRIVATE uvgrtp)
|
||||
target_link_libraries(sync_sender PRIVATE uvgrtp)
|
||||
target_link_libraries(sync_receiver PRIVATE uvgrtp)
|
||||
endif()
|
||||
|
||||
target_link_libraries(srtp_user PRIVATE uvgrtp ${CRYPTOPP_LIB_NAME})
|
||||
|
|
|
@ -0,0 +1,279 @@
|
|||
#include <uvgrtp/lib.hh>
|
||||
#include <climits>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
|
||||
#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(60);
|
||||
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
|
||||
|
||||
constexpr auto END_WAIT = std::chrono::seconds(1200);
|
||||
|
||||
|
||||
uint32_t latest_audio_rtp_ts = 0;
|
||||
uint32_t latest_video_rtp_ts = 0;
|
||||
|
||||
uint32_t video_ssrc = 0;
|
||||
uint32_t audio_ssrc = 0;
|
||||
uint32_t count = 0;
|
||||
std::vector<int> v_latencies = {};
|
||||
std::vector<int> v_rtp_diffs = {};
|
||||
|
||||
std::vector<int> a_latencies = {};
|
||||
std::vector<int> a_rtp_diffs = {};
|
||||
|
||||
|
||||
|
||||
void sender_hook(uvgrtp::frame::rtcp_sender_report* frame);
|
||||
void receiver_hook(uvgrtp::frame::rtcp_receiver_report* frame);
|
||||
void rtp_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame);
|
||||
|
||||
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
uvgrtp::session* receiver_session = receiver_ctx.create_session(SENDER_ADDRESS, RECEIVER_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 */
|
||||
|
||||
// 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 | RCE_RTCP;
|
||||
//unsigned rce_multistream_flags = RCE_SRTP | RCE_SRTP_KMNGMNT_ZRTP | RCE_ZRTP_MULTISTREAM_MODE | RCE_RTCP;
|
||||
|
||||
unsigned rce_dh_flags = RCE_RTCP;
|
||||
unsigned rce_multistream_flags = RCE_RTCP;
|
||||
|
||||
// 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);
|
||||
|
||||
|
||||
// wait until all threads have ended
|
||||
if (a_receiver.joinable())
|
||||
{
|
||||
a_receiver.join();
|
||||
}
|
||||
if (v_receiver.joinable())
|
||||
{
|
||||
v_receiver.join();
|
||||
}
|
||||
|
||||
|
||||
if (receiver_session)
|
||||
receiver_ctx.destroy_session(receiver_session);
|
||||
|
||||
std::cout << "ZRTP example finished" << std::endl;
|
||||
|
||||
std::ofstream file;
|
||||
file.open("latency_output.txt");
|
||||
file << "Latencies in video stream (ms): Current NTP time - NTP time in SR" << std::endl;
|
||||
for (auto i : v_latencies) {
|
||||
file << "* " << i << std::endl;
|
||||
}
|
||||
|
||||
file << "RTP timestamp differences in video stream: SR RTP ts - latest RTP packet RTP ts" << std::endl;
|
||||
for (auto i : v_rtp_diffs) {
|
||||
file << "* " << i << std::endl;
|
||||
}
|
||||
|
||||
file << "Latencies in audio stream (ms): Current NTP time - NTP time in SR" << std::endl;
|
||||
for (auto i : a_latencies) {
|
||||
file << "* " << i << std::endl;
|
||||
}
|
||||
|
||||
file << "RTP timestamp differences in audio stream: SR RTP ts - latest RTP packet RTP ts" << std::endl;
|
||||
for (auto i : a_rtp_diffs) {
|
||||
file << "* " << i << std::endl;
|
||||
}
|
||||
file.close();
|
||||
|
||||
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 || receiver_stream->get_rtcp()->install_receiver_hook(receiver_hook) != RTP_OK)
|
||||
{
|
||||
std::cerr << "Failed to install RTCP receiver report hook" << std::endl;
|
||||
}
|
||||
|
||||
if (!receiver_stream || receiver_stream->get_rtcp()->install_sender_hook(sender_hook) != RTP_OK)
|
||||
{
|
||||
std::cerr << "Failed to install RTCP sender report hook" << std::endl;
|
||||
}
|
||||
|
||||
if (!receiver_stream || receiver_stream->install_receive_hook(nullptr, rtp_receive_hook) != RTP_OK)
|
||||
{
|
||||
std::cerr << "Failed to install RTP reception hook";
|
||||
return;
|
||||
}
|
||||
std::this_thread::sleep_for(EXAMPLE_RUN_TIME_S); // lets this example run for some time
|
||||
|
||||
}
|
||||
|
||||
void receiver_hook(uvgrtp::frame::rtcp_receiver_report* frame)
|
||||
{
|
||||
std::cout << "RTCP receiver report! ----------" << std::endl;
|
||||
|
||||
for (auto& block : frame->report_blocks)
|
||||
{
|
||||
std::cout << "ssrc: " << block.ssrc << std::endl;
|
||||
std::cout << "fraction field value: " << uint32_t(block.fraction) << std::endl;
|
||||
std::cout << "fraction: " << float(block.fraction) / 256 << 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 (jiffies): " << uvgrtp::clock::jiffies_to_ms(block.dlsr)
|
||||
<< std::endl << std::endl;
|
||||
}
|
||||
|
||||
/* RTCP frames can be deallocated using delete */
|
||||
delete frame;
|
||||
}
|
||||
|
||||
void sender_hook(uvgrtp::frame::rtcp_sender_report* frame)
|
||||
{
|
||||
uint64_t current_ntp = uvgrtp::clock::ntp::now();
|
||||
uint32_t msw = frame->sender_info.ntp_msw;
|
||||
uint32_t lsw = frame->sender_info.ntp_lsw;
|
||||
uint64_t ntp_ts = (uint64_t(msw) << 32) | lsw;
|
||||
uint64_t diff_ms = uvgrtp::clock::ntp::diff(ntp_ts, current_ntp);
|
||||
int diff = -10000; // "impossible" value, when real value is not found
|
||||
|
||||
/*std::cout << "RTCP sender report! ----------" << std::endl;
|
||||
|
||||
std::cout << "---Senders ssrc: " << frame->ssrc << 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;*/
|
||||
//std::cout << "---Difference between SR generation and current NTP time (ms): " << diff_ms << std::endl;
|
||||
|
||||
if (frame->ssrc == video_ssrc) {
|
||||
//std::cout << "---Latest RTP ts: Video: " << latest_video_rtp_ts << std::endl;
|
||||
diff = frame->sender_info.rtp_ts - latest_video_rtp_ts;
|
||||
//std::cout << "---Video stream: SR RTP ts - latest ts from RTP packet: " << diff << std::endl;
|
||||
v_latencies.push_back(diff_ms);
|
||||
v_rtp_diffs.push_back(diff);
|
||||
}
|
||||
|
||||
if (frame->ssrc == audio_ssrc) {
|
||||
//std::cout << "---Latest RTP ts: Audio: " << latest_audio_rtp_ts << std::endl;
|
||||
diff = frame->sender_info.rtp_ts - latest_audio_rtp_ts;
|
||||
a_latencies.push_back(diff_ms);
|
||||
a_rtp_diffs.push_back(diff);
|
||||
//std::cout << "----------------------------------------------------Audio stream: SR RTP ts - latest ts from RTP packet: " << diff << std::endl;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
for (auto& block : frame->report_blocks)
|
||||
{
|
||||
std::cout << "ssrc: " << block.ssrc << std::endl;
|
||||
std::cout << "fraction: " << uint32_t(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 (jiffies): " << uvgrtp::clock::jiffies_to_ms(block.dlsr)
|
||||
<< std::endl << std::endl;
|
||||
}
|
||||
|
||||
/* RTCP frames can be deallocated using delete */
|
||||
delete frame;
|
||||
}
|
||||
|
||||
void rtp_receive_hook(void* arg, uvgrtp::frame::rtp_frame* frame)
|
||||
{
|
||||
|
||||
if (frame->header.payload == RTP_FORMAT_OPUS) {
|
||||
latest_audio_rtp_ts = frame->header.timestamp;
|
||||
if (audio_ssrc == 0) {
|
||||
audio_ssrc = frame->header.ssrc;
|
||||
}
|
||||
}
|
||||
else if (frame->header.payload == RTP_FORMAT_H265) {
|
||||
latest_video_rtp_ts = frame->header.timestamp;
|
||||
if (video_ssrc == 0) {
|
||||
video_ssrc = frame->header.ssrc;
|
||||
}
|
||||
}
|
||||
(void)uvgrtp::frame::dealloc_frame(frame);
|
||||
}
|
|
@ -0,0 +1,230 @@
|
|||
#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(60);
|
||||
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
|
||||
|
||||
constexpr auto END_WAIT = std::chrono::seconds(1200);
|
||||
// RTP timestamps, audio and video
|
||||
uint32_t a_ts = 400000;
|
||||
uint32_t v_ts = 200000;
|
||||
|
||||
void receiver_hook(uvgrtp::frame::rtcp_receiver_report* frame);
|
||||
void sender_hook(uvgrtp::frame::rtcp_sender_report* frame);
|
||||
|
||||
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;
|
||||
|
||||
|
||||
std::shared_ptr<std::mutex> print_mutex = std::shared_ptr<std::mutex>(new std::mutex);
|
||||
|
||||
|
||||
// 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 | RCE_RTCP;
|
||||
//unsigned rce_multistream_flags = RCE_SRTP | RCE_SRTP_KMNGMNT_ZRTP | RCE_ZRTP_MULTISTREAM_MODE | RCE_RTCP;
|
||||
|
||||
unsigned rce_dh_flags = RCE_RTCP;
|
||||
unsigned rce_multistream_flags = RCE_RTCP;
|
||||
|
||||
|
||||
std::cout << "Initializing senders" << std::endl;
|
||||
uvgrtp::context sender_ctx;
|
||||
uvgrtp::session* sender_session = sender_ctx.create_session(RECEIVER_ADDRESS, SENDER_ADDRESS);
|
||||
|
||||
// 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_sender.joinable())
|
||||
{
|
||||
a_sender.join();
|
||||
}
|
||||
if (v_sender.joinable())
|
||||
{
|
||||
v_sender.join();
|
||||
}
|
||||
|
||||
if (sender_session)
|
||||
sender_ctx.destroy_session(sender_session);
|
||||
|
||||
std::cout << "ZRTP example finished" << std::endl;
|
||||
std::this_thread::sleep_for(END_WAIT);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
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 || sender_audio_strm->get_rtcp()->install_sender_hook(sender_hook) != RTP_OK) {
|
||||
std::cerr << "Failed to install RTCP sender report hook" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sender_audio_strm || sender_audio_strm->get_rtcp()->install_receiver_hook(receiver_hook) != RTP_OK)
|
||||
{
|
||||
std::cerr << "Failed to install RTCP receiver report hook" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t ts;
|
||||
if (format == RTP_FORMAT_OPUS) {
|
||||
ts = a_ts;
|
||||
}
|
||||
else {
|
||||
ts = v_ts;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
uint64_t clock_ntp = uvgrtp::clock::ntp::now();// -UINT32_MAX / 100;
|
||||
if (sender_audio_strm->push_frame(std::move(dummy_frame), payload_size, ts, clock_ntp, RTP_NO_FLAGS) != RTP_OK)
|
||||
{
|
||||
std::cerr << "Failed to send frame" << std::endl;
|
||||
}
|
||||
ts += 1;
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
void sender_hook(uvgrtp::frame::rtcp_sender_report* frame)
|
||||
{
|
||||
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;
|
||||
|
||||
for (auto& block : frame->report_blocks)
|
||||
{
|
||||
std::cout << "ssrc: " << block.ssrc << std::endl;
|
||||
std::cout << "fraction: " << uint32_t(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 (jiffies): " << uvgrtp::clock::jiffies_to_ms(block.dlsr)
|
||||
<< std::endl << std::endl;
|
||||
}
|
||||
|
||||
/* RTCP frames can be deallocated using delete */
|
||||
delete frame;
|
||||
}
|
||||
|
||||
void receiver_hook(uvgrtp::frame::rtcp_receiver_report* frame)
|
||||
{
|
||||
/*std::cout << "RTCP receiver report! ----------" << std::endl;
|
||||
std::cout << "---Receivers own ssrc: " << frame->ssrc << std::endl;
|
||||
|
||||
for (auto& block : frame->report_blocks)
|
||||
{
|
||||
std::cout << "---ssrc: " << block.ssrc << std::endl;
|
||||
std::cout << "---fraction field value: " << uint32_t(block.fraction) << std::endl;
|
||||
std::cout << "---fraction: " << float(block.fraction) / 256 << 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 (jiffies): " << uvgrtp::clock::jiffies_to_ms(block.dlsr)
|
||||
<< std::endl << std::endl;
|
||||
}*/
|
||||
|
||||
/* RTCP frames can be deallocated using delete */
|
||||
delete frame;
|
||||
}
|
|
@ -171,6 +171,39 @@ namespace uvgrtp {
|
|||
*/
|
||||
rtp_error_t push_frame(uint8_t *data, size_t data_len, uint32_t ts, int rtp_flags);
|
||||
|
||||
/**
|
||||
* \brief Send data to remote participant with a custom timestamp and sampling timestamp
|
||||
*
|
||||
* \details If so specified either by the selected media format and/or given
|
||||
* ::RTP_CTX_ENABLE_FLAGS, uvgRTP fragments the input data into RTP packets of 1492 bytes,
|
||||
* or to any other size defined by the application using ::RCC_MTU_SIZE
|
||||
*
|
||||
* The frame is automatically reconstructed by the receiver if all fragments have been
|
||||
* received successfully.
|
||||
*
|
||||
* If application so wishes, it may override uvgRTP's own timestamp
|
||||
* calculations and provide timestamping information for the stream itself.
|
||||
* This requires that the application provides a sensible value for the ts
|
||||
* parameter. If RTCP has been enabled, uvgrtp::rtcp::set_ts_info() should have
|
||||
* been called.
|
||||
*
|
||||
* \param data Pointer to data the that should be sent, uvgRTP does not take ownership of the memory
|
||||
* \param data_len Length of data
|
||||
* \param ts 32-bit timestamp value for the data, RTP timestamp
|
||||
* \param s_ts 64-bit timestamp value of when the packets data was sampled
|
||||
|
||||
* \param rtp_flags Optional flags, see ::RTP_FLAGS for more details
|
||||
*
|
||||
* \return RTP error code
|
||||
*
|
||||
* \retval RTP_OK On success
|
||||
* \retval RTP_INVALID_VALUE If one of the parameters are invalid
|
||||
* \retval RTP_MEMORY_ERROR If the data chunk is too large to be processed
|
||||
* \retval RTP_SEND_ERROR If uvgRTP failed to send the data to remote
|
||||
* \retval RTP_GENERIC_ERROR If an unspecified error occurred
|
||||
*/
|
||||
rtp_error_t push_frame(uint8_t* data, size_t data_len, uint32_t ts, uint64_t s_ts, int rtp_flags);
|
||||
|
||||
/**
|
||||
* \brief Send data to remote participant with a custom timestamp
|
||||
*
|
||||
|
@ -202,6 +235,38 @@ namespace uvgrtp {
|
|||
*/
|
||||
rtp_error_t push_frame(std::unique_ptr<uint8_t[]> data, size_t data_len, uint32_t ts, int rtp_flags);
|
||||
|
||||
/**
|
||||
* \brief Send data to remote participant with a custom timestamp and sampling timestamp
|
||||
*
|
||||
* \details If so specified either by the selected media format and/or given
|
||||
* ::RTP_CTX_ENABLE_FLAGS, uvgRTP fragments the input data into RTP packets of 1492 bytes,
|
||||
* or to any other size defined by the application using ::RCC_MTU_SIZE
|
||||
*
|
||||
* The frame is automatically reconstructed by the receiver if all fragments have been
|
||||
* received successfully.
|
||||
*
|
||||
* If application so wishes, it may override uvgRTP's own timestamp
|
||||
* calculations and provide timestamping information for the stream itself.
|
||||
* This requires that the application provides a sensible value for the ts
|
||||
* parameter. If RTCP has been enabled, uvgrtp::rtcp::set_ts_info() should have
|
||||
* been called.
|
||||
*
|
||||
* \param data Smart pointer to data the that should be sent
|
||||
* \param data_len Length of data
|
||||
* \param ts 32-bit timestamp value for the data
|
||||
* \param s_ts 64-bit timestamp value of when the packets data was sampled
|
||||
* \param rtp_flags Optional flags, see ::RTP_FLAGS for more details
|
||||
*
|
||||
* \return RTP error code
|
||||
*
|
||||
* \retval RTP_OK On success
|
||||
* \retval RTP_INVALID_VALUE If one of the parameters are invalid
|
||||
* \retval RTP_MEMORY_ERROR If the data chunk is too large to be processed
|
||||
* \retval RTP_SEND_ERROR If uvgRTP failed to send the data to remote
|
||||
* \retval RTP_GENERIC_ERROR If an unspecified error occurred
|
||||
*/
|
||||
rtp_error_t push_frame(std::unique_ptr<uint8_t[]> data, size_t data_len, uint32_t ts, uint64_t s_ts, int rtp_flags);
|
||||
|
||||
/**
|
||||
* \brief Poll a frame indefinitely from the media stream object
|
||||
*
|
||||
|
|
|
@ -630,6 +630,8 @@ namespace uvgrtp {
|
|||
|
||||
std::atomic<uint32_t> interval_ms_;
|
||||
|
||||
std::shared_ptr<uvgrtp::rtp> rtp_ptr_;
|
||||
|
||||
std::mutex packet_mutex_;
|
||||
|
||||
// messages waiting to be sent
|
||||
|
|
|
@ -472,6 +472,32 @@ rtp_error_t uvgrtp::media_stream::push_frame(uint8_t *data, size_t data_len, uin
|
|||
return ret;
|
||||
}
|
||||
|
||||
rtp_error_t uvgrtp::media_stream::push_frame(uint8_t* data, size_t data_len, uint32_t ts, uint64_t s_ts, int rtp_flags)
|
||||
{
|
||||
rtp_error_t ret = check_push_preconditions(rtp_flags, false);
|
||||
if (ret == RTP_OK)
|
||||
{
|
||||
if (rce_flags_ & RCE_HOLEPUNCH_KEEPALIVE)
|
||||
holepuncher_->notify();
|
||||
|
||||
rtp_->set_timestamp(ts);
|
||||
rtp_->set_sampling_ntp(s_ts);
|
||||
if (rtp_flags & RTP_COPY)
|
||||
{
|
||||
data = copy_frame(data, data_len);
|
||||
std::unique_ptr<uint8_t[]> data_copy(data);
|
||||
ret = media_->push_frame(std::move(data_copy), data_len, rtp_flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = media_->push_frame(data, data_len, rtp_flags);
|
||||
}
|
||||
rtp_->set_timestamp(INVALID_TS);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
rtp_error_t uvgrtp::media_stream::push_frame(std::unique_ptr<uint8_t[]> data, size_t data_len, uint32_t ts, int rtp_flags)
|
||||
{
|
||||
rtp_error_t ret = check_push_preconditions(rtp_flags, true);
|
||||
|
@ -489,6 +515,24 @@ rtp_error_t uvgrtp::media_stream::push_frame(std::unique_ptr<uint8_t[]> data, si
|
|||
return ret;
|
||||
}
|
||||
|
||||
rtp_error_t uvgrtp::media_stream::push_frame(std::unique_ptr<uint8_t[]> data, size_t data_len, uint32_t ts, uint64_t s_ts, int rtp_flags)
|
||||
{
|
||||
rtp_error_t ret = check_push_preconditions(rtp_flags, true);
|
||||
if (ret == RTP_OK)
|
||||
{
|
||||
if (rce_flags_ & RCE_HOLEPUNCH_KEEPALIVE)
|
||||
holepuncher_->notify();
|
||||
|
||||
// making a copy of a smart pointer does not make sense
|
||||
rtp_->set_timestamp(ts);
|
||||
rtp_->set_sampling_ntp(s_ts);
|
||||
ret = media_->push_frame(std::move(data), data_len, rtp_flags);
|
||||
rtp_->set_timestamp(INVALID_TS);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uvgrtp::frame::rtp_frame *uvgrtp::media_stream::pull_frame()
|
||||
{
|
||||
if (!check_pull_preconditions()) {
|
||||
|
|
25
src/rtcp.cc
25
src/rtcp.cc
|
@ -61,6 +61,7 @@ uvgrtp::rtcp::rtcp(std::shared_ptr<uvgrtp::rtp> rtp, std::string cname, int rce_
|
|||
app_hook_u_(nullptr),
|
||||
active_(false),
|
||||
interval_ms_(DEFAULT_RTCP_INTERVAL_MS),
|
||||
rtp_ptr_(rtp),
|
||||
ourItems_(),
|
||||
bye_ssrcs_(false),
|
||||
mtu_size_(MAX_IPV4_PAYLOAD)
|
||||
|
@ -1187,7 +1188,6 @@ rtp_error_t uvgrtp::rtcp::handle_incoming_packet(uint8_t *buffer, size_t size)
|
|||
}
|
||||
|
||||
/* Update the timeout map */
|
||||
std::cout << "incoming ssrc: " << sender_ssrc << std::endl;
|
||||
if (ms_since_last_rep_.find(sender_ssrc) != ms_since_last_rep_.end()) {
|
||||
ms_since_last_rep_.at(sender_ssrc) = 0;
|
||||
}
|
||||
|
@ -1723,18 +1723,28 @@ rtp_error_t uvgrtp::rtcp::generate_report()
|
|||
clock_start_ = uvgrtp::clock::ntp::now();
|
||||
}
|
||||
|
||||
/* TODO: The RTP timestamp should be from an actual RTP packet and NTP timestamp should be the one
|
||||
corresponding to it. */
|
||||
uint64_t ntp_ts = uvgrtp::clock::ntp::now();
|
||||
|
||||
uint64_t expanded_ts_start = rtp_ts_start_;
|
||||
uint64_t ts_since_start = (uint64_t)(uvgrtp::clock::ntp::diff(clock_start_, ntp_ts) * double(clock_rate_ / 1000));
|
||||
// TODO: user gives this timestamp of the moment when the LAST rtp frame was sampled
|
||||
uint64_t sampling_ntp_ts = rtp_ptr_->get_sampling_ntp();
|
||||
|
||||
uint64_t diff_us = uvgrtp::clock::ntp::diff(sampling_ntp_ts, ntp_ts) * 1000;
|
||||
|
||||
//std::cout << "-----diff microseconds: " << diff_us << " - clock rate/1000: " << clock_rate_/1000 << std::endl;
|
||||
|
||||
uint32_t rtp_ts = rtp_ptr_->get_rtp_ts();
|
||||
//std::cout << "-----last rtp packet timestamp: " << rtp_ts << " - plus this num: " <<
|
||||
// (diff_us * double(clock_rate_) / 1000000)<< std::endl;
|
||||
|
||||
|
||||
uint32_t reporting_rtp_ts = rtp_ts + (diff_us * double(clock_rate_) / 1000000);
|
||||
//std::cout << "---- Final rtp ts to be sent: " << reporting_rtp_ts << std::endl;
|
||||
|
||||
|
||||
uint64_t rtp_ts = expanded_ts_start + ts_since_start;
|
||||
|
||||
if (!construct_rtcp_header(frame, write_ptr, sender_report_size, reports, uvgrtp::frame::RTCP_FT_SR) ||
|
||||
!construct_ssrc(frame, write_ptr, ssrc_) ||
|
||||
!construct_sender_info(frame, write_ptr, ntp_ts, rtp_ts, our_stats.sent_pkts, our_stats.sent_bytes))
|
||||
!construct_sender_info(frame, write_ptr, ntp_ts, reporting_rtp_ts, our_stats.sent_pkts, our_stats.sent_bytes))
|
||||
{
|
||||
UVG_LOG_ERROR("Failed to construct SR");
|
||||
return RTP_GENERIC_ERROR;
|
||||
|
@ -1877,7 +1887,6 @@ rtp_error_t uvgrtp::rtcp::generate_report()
|
|||
|
||||
UVG_LOG_DEBUG("Sending RTCP report compound packet, Total size: %lli",
|
||||
compound_packet_size);
|
||||
|
||||
return send_rtcp_packet_to_participants(frame, compound_packet_size, true);
|
||||
}
|
||||
|
||||
|
|
21
src/rtp.cc
21
src/rtp.cc
|
@ -14,6 +14,7 @@
|
|||
#endif
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
||||
#define INVALID_TS UINT64_MAX
|
||||
|
||||
|
@ -28,6 +29,8 @@ uvgrtp::rtp::rtp(rtp_format_t fmt):
|
|||
wc_start_2(),
|
||||
sent_pkts_(0),
|
||||
timestamp_(INVALID_TS),
|
||||
sampling_ntp_(0),
|
||||
rtp_ts_(0),
|
||||
payload_size_(MAX_IPV4_MEDIA_PAYLOAD),
|
||||
delay_(PKT_MAX_DELAY_MS)
|
||||
{
|
||||
|
@ -156,10 +159,14 @@ void uvgrtp::rtp::fill_header(uint8_t *buffer)
|
|||
uint64_t u_seconds = time_since_start.count() * clock_rate_;
|
||||
|
||||
uint32_t rtp_timestamp = ts_ + uint32_t(u_seconds / 1000000);
|
||||
rtp_ts_ = rtp_timestamp;
|
||||
sampling_ntp_ = uvgrtp::clock::ntp::now();
|
||||
|
||||
*(uint32_t *)&buffer[4] = htonl((u_long)rtp_timestamp);
|
||||
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
rtp_ts_ = timestamp_;
|
||||
*(uint32_t *)&buffer[4] = htonl((u_long)timestamp_);
|
||||
}
|
||||
}
|
||||
|
@ -204,6 +211,18 @@ size_t uvgrtp::rtp::get_pkt_max_delay() const
|
|||
return delay_;
|
||||
}
|
||||
|
||||
void uvgrtp::rtp::set_sampling_ntp(uint64_t ntp_ts) {
|
||||
sampling_ntp_ = ntp_ts;
|
||||
}
|
||||
|
||||
uint64_t uvgrtp::rtp::get_sampling_ntp() const {
|
||||
return sampling_ntp_;
|
||||
}
|
||||
|
||||
uint32_t uvgrtp::rtp::get_rtp_ts() const {
|
||||
return rtp_ts_;
|
||||
}
|
||||
|
||||
rtp_error_t uvgrtp::rtp::packet_handler(ssize_t size, void *packet, int rce_flags, uvgrtp::frame::rtp_frame **out)
|
||||
{
|
||||
(void)rce_flags;
|
||||
|
|
10
src/rtp.hh
10
src/rtp.hh
|
@ -23,6 +23,8 @@ namespace uvgrtp {
|
|||
size_t get_payload_size() const;
|
||||
size_t get_pkt_max_delay() const;
|
||||
rtp_format_t get_payload() const;
|
||||
uint64_t get_sampling_ntp() const;
|
||||
uint32_t get_rtp_ts() const;
|
||||
|
||||
void inc_sent_pkts();
|
||||
void inc_sequence();
|
||||
|
@ -33,6 +35,7 @@ namespace uvgrtp {
|
|||
void set_timestamp(uint64_t timestamp);
|
||||
void set_payload_size(size_t payload_size);
|
||||
void set_pkt_max_delay(size_t delay);
|
||||
void set_sampling_ntp(uint64_t ntp_ts);
|
||||
|
||||
void fill_header(uint8_t *buffer);
|
||||
void update_sequence(uint8_t *buffer);
|
||||
|
@ -59,6 +62,13 @@ namespace uvgrtp {
|
|||
/* Use custom timestamp for the outgoing RTP packets */
|
||||
uint64_t timestamp_;
|
||||
|
||||
/* custom NTP timestamp of when the RTP packet was SAMPLED */
|
||||
uint64_t sampling_ntp_;
|
||||
|
||||
/* Last RTP timestamp. The 2 timestamps above are initial timestamps, this is the
|
||||
* one that gets updated */
|
||||
uint32_t rtp_ts_;
|
||||
|
||||
/* What is the maximum size of the payload available for this RTP instance */
|
||||
size_t payload_size_;
|
||||
|
||||
|
|
Loading…
Reference in New Issue