common: FPS API for sending

This commit is contained in:
Joni Räsänen 2022-08-19 15:22:43 +03:00
parent 0d795f5f8a
commit 2abf0bc4ef
8 changed files with 137 additions and 5 deletions

View File

@ -358,6 +358,9 @@ namespace uvgrtp {
std::unique_ptr<uvgrtp::holepuncher> holepuncher_;
std::string cname_;
int fps_enumerator_ = 30;
int fps_denominator_ = 1;
};
}

View File

@ -278,6 +278,10 @@ enum RTP_CTX_CONFIGURATION_FLAGS {
* to use jumbo frames, it can set the MTU size to 9000 bytes */
RCC_MTU_SIZE = 5,
RCC_FPS_ENUMERATOR = 6,
RCC_FPS_DENOMINATOR = 7,
RCC_LAST
};

View File

@ -164,3 +164,8 @@ rtp_error_t uvgrtp::formats::media::packet_handler(void *arg, int flags, uvgrtp:
return RTP_OK;
}
void uvgrtp::formats::media::set_fps(int enumerator, int denominator)
{
fqueue_->set_fps(enumerator, denominator);
}

View File

@ -62,6 +62,8 @@ namespace uvgrtp {
/* Return pointer to the internal frame info structure which is relayed to packet handler */
media_frame_info_t *get_media_frame_info();
void set_fps(int enumarator, int denominator);
protected:
virtual rtp_error_t push_media_frame(uint8_t *data, size_t data_len, int flags);

View File

@ -9,6 +9,7 @@
#include "random.hh"
#include "debug.hh"
#include <thread>
#ifdef _WIN32
#include <winsock2.h>
@ -27,7 +28,10 @@ uvgrtp::frame_queue::frame_queue(std::shared_ptr<uvgrtp::socket> socket, std::sh
max_ccount_(MAX_CHUNK_COUNT* max_mcount_),
rtp_(rtp),
socket_(socket),
flags_(flags)
flags_(flags),
frame_interval_(),
fps_sync_point_(),
frames_since_sync_(0)
{}
uvgrtp::frame_queue::~frame_queue()
@ -285,7 +289,37 @@ rtp_error_t uvgrtp::frame_queue::flush_queue()
if (active_->packets.size() > 1)
((uint8_t *)&active_->rtp_headers[active_->rtphdr_ptr - 1])[1] |= (1 << 7);
if (socket_->sendto(active_->packets, 0) != RTP_OK) {
if (fps)
{
std::chrono::high_resolution_clock::time_point this_moment = std::chrono::high_resolution_clock::now();
std::chrono::high_resolution_clock::time_point next_frame = next_frame_time();
if (next_frame > this_moment)
{
UVG_LOG_DEBUG("Updating fps sync point");
fps_sync_point_ = this_moment;
next_frame = next_frame_time();
}
std::chrono::nanoseconds packet_interval = frame_interval_/ active_->packets.size();
for (int i = 0; i < active_->packets.size(); ++i)
{
std::chrono::high_resolution_clock::time_point next_packet = this_moment + i * packet_interval;
// sleep until next packet time
std::this_thread::sleep_for(next_packet - std::chrono::high_resolution_clock::now());
// send pkt vects
if (socket_->sendto(active_->packets[i], 0) != RTP_OK) {
UVG_LOG_ERROR("Failed to send packet: %s", strerror(errno));
(void)deinit_transaction();
return RTP_SEND_ERROR;
}
}
}
else if (socket_->sendto(active_->packets, 0) != RTP_OK) {
UVG_LOG_ERROR("Failed to flush the message queue: %s", strerror(errno));
(void)deinit_transaction();
return RTP_SEND_ERROR;
@ -295,6 +329,11 @@ rtp_error_t uvgrtp::frame_queue::flush_queue()
return deinit_transaction();
}
inline std::chrono::high_resolution_clock::time_point uvgrtp::frame_queue::next_frame_time()
{
return fps_sync_point_ + frame_interval_* (frames_since_sync_ + 1);
}
void uvgrtp::frame_queue::update_rtp_header()
{
memcpy(&active_->rtp_headers[active_->rtphdr_ptr], &active_->rtp_common, sizeof(active_->rtp_common));

View File

@ -149,10 +149,22 @@ namespace uvgrtp {
* significant memory leaks */
void install_dealloc_hook(void (*dealloc_hook)(void *));
void set_fps(int enumerator, int denominator)
{
fps = enumerator > 0 && denominator > 0;
if (denominator > 0)
{
frame_interval_ = std::chrono::nanoseconds(uint64_t(1.0 / double(enumerator / denominator) * 1000*1000*1000));
}
fps_sync_point_ = std::chrono::high_resolution_clock::now();
}
private:
void enqueue_finalize(uvgrtp::buf_vec& tmp);
inline std::chrono::high_resolution_clock::time_point next_frame_time();
transaction_t *active_;
/* Deallocation hook is stored here and copied to transaction upon initialization */
@ -166,6 +178,12 @@ namespace uvgrtp {
/* RTP context flags */
int flags_;
bool fps = false;
std::chrono::nanoseconds frame_interval_;
std::chrono::high_resolution_clock::time_point fps_sync_point_;
uint64_t frames_since_sync_ = 0;
};
}

View File

@ -34,7 +34,9 @@ uvgrtp::media_stream::media_stream(std::string cname, std::string addr,
reception_flow_(nullptr),
media_(nullptr),
holepuncher_(nullptr),
cname_(cname)
cname_(cname),
fps_enumerator_(30),
fps_denominator_(1)
{
fmt_ = fmt;
addr_ = addr;
@ -572,6 +574,20 @@ rtp_error_t uvgrtp::media_stream::configure_ctx(int flag, ssize_t value)
}
break;
case RCC_FPS_ENUMERATOR: {
fps_enumerator_ = value;
media_->set_fps(fps_enumerator_, fps_denominator_);
break;
}
case RCC_FPS_DENOMINATOR: {
fps_denominator_ = value;
media_->set_fps(fps_enumerator_, fps_denominator_);
break;
}
default:
return RTP_INVALID_VALUE;
}

View File

@ -133,8 +133,6 @@ TEST(FormatTests, h265)
cleanup_sess(ctx, sess);
}
TEST(FormatTests, h265_large)
{
std::cout << "Starting h265 test" << std::endl;
@ -148,6 +146,11 @@ TEST(FormatTests, h265_large)
{
sender = sess->create_stream(SEND_PORT, RECEIVE_PORT, RTP_FORMAT_H265, RCE_NO_FLAGS);
receiver = sess->create_stream(RECEIVE_PORT, SEND_PORT, RTP_FORMAT_H265, RCE_H26X_PREPEND_SC);
if (receiver)
{
receiver->configure_ctx(RCC_UDP_RCV_BUF_SIZE, 40 * 1000 * 1000);
}
}
std::vector<size_t> test_sizes = {100000, 200000, 300000, 400000, 500000, 750000, 1000000};
@ -169,6 +172,48 @@ TEST(FormatTests, h265_large)
cleanup_sess(ctx, sess);
}
TEST(FormatTests, h265_large_fps)
{
std::cout << "Starting h265 test" << std::endl;
uvgrtp::context ctx;
uvgrtp::session* sess = ctx.create_session(LOCAL_ADDRESS);
uvgrtp::media_stream* sender = nullptr;
uvgrtp::media_stream* receiver = nullptr;
if (sess)
{
sender = sess->create_stream(SEND_PORT, RECEIVE_PORT, RTP_FORMAT_H265, RCE_NO_FLAGS);
receiver = sess->create_stream(RECEIVE_PORT, SEND_PORT, RTP_FORMAT_H265, RCE_H26X_PREPEND_SC);
if (receiver)
{
sender->configure_ctx(RCC_FPS_ENUM, 100);
sender->configure_ctx(RCC_FPS_DENUM, 1);
receiver->configure_ctx(RCC_UDP_RCV_BUF_SIZE, 40 * 1000 * 1000);
}
}
std::vector<size_t> test_sizes = { 100000, 200000, 300000, 400000, 500000, 750000, 1000000 };
// the default packet limit for RTP is 1458 where 12 bytes are dedicated to RTP header
int rtp_flags = RTP_NO_FLAGS;
int nal_type = 5;
rtp_format_t format = RTP_FORMAT_H265;
int test_runs = 10;
for (auto& size : test_sizes)
{
std::unique_ptr<uint8_t[]> intra_frame = create_test_packet(format, nal_type, true, size, rtp_flags);
test_packet_size(std::move(intra_frame), test_runs, size, sess, sender, receiver, rtp_flags);
}
cleanup_ms(sess, sender);
cleanup_ms(sess, receiver);
cleanup_sess(ctx, sess);
}
TEST(FormatTests, h266)
{
std::cout << "Starting h266 test" << std::endl;