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:
Heikki Tampio 2023-02-06 14:26:39 +02:00
parent 627fd7e602
commit 63175f8a04
9 changed files with 675 additions and 10 deletions

View File

@ -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})

279
examples/sync_receiver.cc Normal file
View File

@ -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);
}

230
examples/sync_sender.cc Normal file
View File

@ -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;
}

View File

@ -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
*

View File

@ -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

View File

@ -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()) {

View File

@ -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);
}

View File

@ -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;

View File

@ -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_;