Add ability to use custom timestamps for media

Instead of using uvgRTP's internal RTP timestamps, let user specify
timestamp for the media when push_frame() is called.

Instead of propagating the timestamp through the send stack, store
the timestamp value temporarily to the RTP context of the media
stream and reset it after push_frame() is done.

User should not mix and match uvgRTP's internal RTP timestamps with
custom timestamps so either all of the calls or none of the calls should
given a timestamp to push_frame().

Custom timestamps should be increased in accordance with the media clock
rate, otherwise problems may occur with media reception.
This commit is contained in:
Aaro Altonen 2020-06-01 13:18:11 +03:00
parent 05ad2ceeba
commit c997a94c2a
5 changed files with 73 additions and 9 deletions

View File

@ -27,15 +27,23 @@ int main(void)
* In this example, we have one media streams with remote participant: hevc */
uvg_rtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_HEVC, 0);
uint8_t *buffer = new uint8_t[PAYLOAD_MAXLEN];
uint8_t *buffer = new uint8_t[PAYLOAD_MAXLEN];
uint32_t timestamp = 0;
for (int i = 0; i < 10; ++i) {
#ifndef CUSTOM_TIMESTAMPS
/* Sending data is as simple as calling push_frame().
*
* push_frame() will fragment the input buffer into payloads of 1500 bytes and send them to remote */
if (hevc->push_frame(buffer, PAYLOAD_MAXLEN, RTP_NO_FLAGS) != RTP_OK)
fprintf(stderr, "Failed to send RTP frame!");
#else
/* If needed, custom timestamps can be given to push_frame().
*
* This overrides uvgRTP's own calculations and uses the given timestamp for all RTP packets of "buffer" */
if (hevc->push_frame(buffer, PAYLOAD_MAXLEN, (90000 / 30) * timestamp++, RTP_NO_FLAGS) != RTP_OK)
fprintf(stderr, "Failed to send RTP frame!");
#endif
}
/* Session must be destroyed manually */

View File

@ -70,13 +70,18 @@ namespace uvg_rtp {
* push_frame(..., RTP_MORE | RTP_SLICE); // more data coming in, do not flush queue
* push_frame(..., RTP_SLICE); // no more data coming in, flush queue
*
* If user wishes to manage RTP timestamps himself, he may pass "ts" to push_frame()
* which forces uvgRTP to use that timestamp for all RTP packets of "data".
*
* Return RTP_OK success
* Return RTP_INVALID_VALUE if one of the parameters are invalid
* Return RTP_MEMORY_ERROR if the data chunk is too large to be processed
* Return RTP_SEND_ERROR if uvgRTP failed to send the data to remote
* Return RTP_GENERIC_ERROR for any other error condition */
rtp_error_t push_frame(uint8_t *data, size_t data_len, int flags);
rtp_error_t push_frame(uint8_t *data, size_t data_len, uint32_t ts, int flags);
rtp_error_t push_frame(std::unique_ptr<uint8_t[]> data, size_t data_len, int flags);
rtp_error_t push_frame(std::unique_ptr<uint8_t[]> data, size_t data_len, uint32_t ts, int flags);
/* When a frame is received, it is put into the frame vector of the receiver
* Calling application can poll frames by calling pull_frame().

View File

@ -20,6 +20,7 @@ namespace uvg_rtp {
void set_clock_rate(size_t rate);
void set_payload(rtp_format_t fmt);
void set_dynamic_payload(uint8_t payload);
void set_timestamp(uint64_t timestamp);
void fill_header(uint8_t *buffer);
void update_sequence(uint8_t *buffer);
@ -36,6 +37,9 @@ namespace uvg_rtp {
uvg_rtp::clock::hrc::hrc_t wc_start_2;
size_t sent_pkts_;
/* Use custom timestamp for the outgoing RTP packets */
uint64_t timestamp_;
};
};

View File

@ -5,6 +5,8 @@
#include "media_stream.hh"
#include "random.hh"
#define INVALID_TS UINT64_MAX
uvg_rtp::media_stream::media_stream(std::string addr, int src_port, int dst_port, rtp_format_t fmt, int flags):
srtp_(nullptr),
socket_(flags),
@ -212,6 +214,38 @@ rtp_error_t uvg_rtp::media_stream::push_frame(std::unique_ptr<uint8_t[]> data, s
return sender_->push_frame(std::move(data), data_len, flags);
}
rtp_error_t uvg_rtp::media_stream::push_frame(uint8_t *data, size_t data_len, uint32_t ts, int flags)
{
rtp_error_t ret = RTP_GENERIC_ERROR;
if (!initialized_) {
LOG_ERROR("RTP context has not been initialized fully, cannot continue!");
return RTP_NOT_INITIALIZED;
}
rtp_->set_timestamp(ts);
ret = sender_->push_frame(data, data_len, flags);
rtp_->set_timestamp(INVALID_TS);
return ret;
}
rtp_error_t uvg_rtp::media_stream::push_frame(std::unique_ptr<uint8_t[]> data, size_t data_len, uint32_t ts, int flags)
{
rtp_error_t ret = RTP_GENERIC_ERROR;
if (!initialized_) {
LOG_ERROR("RTP context has not been initialized fully, cannot continue!");
return RTP_NOT_INITIALIZED;
}
rtp_->set_timestamp(ts);
ret = sender_->push_frame(std::move(data), data_len, flags);
rtp_->set_timestamp(INVALID_TS);
return ret;
}
uvg_rtp::frame::rtp_frame *uvg_rtp::media_stream::pull_frame()
{
if (!initialized_) {

View File

@ -10,8 +10,12 @@
#include "random.hh"
#include "rtp.hh"
#define INVALID_TS UINT64_MAX
uvg_rtp::rtp::rtp(rtp_format_t fmt):
wc_start_(0), sent_pkts_(0)
wc_start_(0),
sent_pkts_(0),
timestamp_(INVALID_TS)
{
seq_ = uvg_rtp::random::generate_32() & 0xffff;
ts_ = uvg_rtp::random::generate_32();
@ -94,12 +98,21 @@ void uvg_rtp::rtp::fill_header(uint8_t *buffer)
buffer[1] = (payload_ & 0x7f) | (0 << 7);
*(uint16_t *)&buffer[2] = htons(seq_);
*(uint32_t *)&buffer[4] = htonl(
ts_
+ uvg_rtp::clock::hrc::diff_now(wc_start_2)
* clock_rate_
/ 1000
);
*(uint32_t *)&buffer[8] = htonl(ssrc_);
if (timestamp_ == INVALID_TS) {
*(uint32_t *)&buffer[4] = htonl(
ts_
+ uvg_rtp::clock::hrc::diff_now(wc_start_2)
* clock_rate_
/ 1000
);
} else {
*(uint32_t *)&buffer[4] = htonl(timestamp_);
}
}
void uvg_rtp::rtp::set_timestamp(uint64_t timestamp)
{
timestamp_= timestamp;
}