250 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			250 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			C++
		
	
	
	
| #include "rtp.hh"
 | |
| 
 | |
| #include "frame.hh"
 | |
| #include "debug.hh"
 | |
| #include "random.hh"
 | |
| 
 | |
| #ifdef __linux__
 | |
| #include <arpa/inet.h>
 | |
| #include <unistd.h>
 | |
| #endif
 | |
| 
 | |
| #include <chrono>
 | |
| 
 | |
| 
 | |
| 
 | |
| #define INVALID_TS UINT64_MAX
 | |
| 
 | |
| uvgrtp::rtp::rtp(rtp_format_t fmt):
 | |
|     wc_start_(0),
 | |
|     sent_pkts_(0),
 | |
|     timestamp_(INVALID_TS),
 | |
|     delay_(PKT_MAX_DELAY)
 | |
| {
 | |
|     seq_  = uvgrtp::random::generate_32() & 0xffff;
 | |
|     ts_   = uvgrtp::random::generate_32();
 | |
|     ssrc_ = uvgrtp::random::generate_32();
 | |
| 
 | |
|     set_payload(fmt);
 | |
|     set_payload_size(MAX_PAYLOAD);
 | |
| }
 | |
| 
 | |
| uvgrtp::rtp::~rtp()
 | |
| {
 | |
| }
 | |
| 
 | |
| uint32_t uvgrtp::rtp::get_ssrc()
 | |
| {
 | |
|     return ssrc_;
 | |
| }
 | |
| 
 | |
| uint16_t uvgrtp::rtp::get_sequence()
 | |
| {
 | |
|     return seq_;
 | |
| }
 | |
| 
 | |
| void uvgrtp::rtp::set_payload(rtp_format_t fmt)
 | |
| {
 | |
|     payload_ = fmt_ = fmt;
 | |
| 
 | |
|     switch (fmt_) {
 | |
|         case RTP_FORMAT_H264:
 | |
|         case RTP_FORMAT_H265:
 | |
|         case RTP_FORMAT_H266:
 | |
|             clock_rate_ = 90000;
 | |
|             break;
 | |
| 
 | |
|         case RTP_FORMAT_OPUS:
 | |
|             clock_rate_ = 48000;
 | |
|             break;
 | |
| 
 | |
|         default:
 | |
|             LOG_WARN("Unknown RTP format, setting clock rate to 10000");
 | |
|             clock_rate_ = 10000;
 | |
|             break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void uvgrtp::rtp::set_dynamic_payload(uint8_t payload)
 | |
| {
 | |
|     payload_ = payload;
 | |
| }
 | |
| 
 | |
| void uvgrtp::rtp::inc_sequence()
 | |
| {
 | |
|     seq_++;
 | |
| }
 | |
| 
 | |
| void uvgrtp::rtp::inc_sent_pkts()
 | |
| {
 | |
|     sent_pkts_++;
 | |
| }
 | |
| 
 | |
| void uvgrtp::rtp::update_sequence(uint8_t *buffer)
 | |
| {
 | |
|     if (!buffer)
 | |
|         return;
 | |
| 
 | |
|     *(uint16_t *)&buffer[2] = htons(seq_);
 | |
| }
 | |
| 
 | |
| void uvgrtp::rtp::fill_header(uint8_t *buffer)
 | |
| {
 | |
|     if (!buffer)
 | |
|         return;
 | |
| 
 | |
|     /* This is the first RTP message, get wall clock reading (t = 0)
 | |
|      * and generate random RTP timestamp for this reading */
 | |
|     if (!wc_start_) {
 | |
|         ts_        = uvgrtp::random::generate_32();
 | |
|         wc_start_  = uvgrtp::clock::ntp::now();
 | |
|     }
 | |
| 
 | |
|     buffer[0] = 2 << 6; // RTP version
 | |
|     buffer[1] = (payload_ & 0x7f) | (0 << 7);
 | |
| 
 | |
|     *(uint16_t *)&buffer[2] = htons(seq_);
 | |
|     *(uint32_t *)&buffer[8] = htonl(ssrc_);
 | |
| 
 | |
|     if (timestamp_ == INVALID_TS) {
 | |
|         *(uint32_t *)&buffer[4] = htonl(
 | |
|             ts_
 | |
|             + uvgrtp::clock::ntp::diff_now(wc_start_)
 | |
|             * clock_rate_
 | |
|             / 1000
 | |
|         );
 | |
| 
 | |
|     } else {
 | |
|         *(uint32_t *)&buffer[4] = htonl(timestamp_);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void uvgrtp::rtp::set_timestamp(uint64_t timestamp)
 | |
| {
 | |
|     timestamp_= timestamp;
 | |
| }
 | |
| 
 | |
| uint32_t uvgrtp::rtp::get_clock_rate(void)
 | |
| {
 | |
|     return clock_rate_;
 | |
| }
 | |
| 
 | |
| void uvgrtp::rtp::set_payload_size(size_t payload_size)
 | |
| {
 | |
|     switch (fmt_) {
 | |
|         case RTP_FORMAT_H264:
 | |
|         case RTP_FORMAT_H265:
 | |
|         case RTP_FORMAT_H266:
 | |
|             if (payload_size > 3)
 | |
|                 payload_size -= 3;
 | |
|             break;
 | |
|     }
 | |
| 
 | |
|     payload_size_ = payload_size;
 | |
| }
 | |
| 
 | |
| size_t uvgrtp::rtp::get_payload_size()
 | |
| {
 | |
|     return payload_size_;
 | |
| }
 | |
| 
 | |
| rtp_format_t uvgrtp::rtp::get_payload()
 | |
| {
 | |
|     return (rtp_format_t)fmt_;
 | |
| }
 | |
| 
 | |
| void uvgrtp::rtp::set_pkt_max_delay(size_t delay)
 | |
| {
 | |
|     delay_ = delay;
 | |
| }
 | |
| 
 | |
| size_t uvgrtp::rtp::get_pkt_max_delay()
 | |
| {
 | |
|     return delay_;
 | |
| }
 | |
| 
 | |
| rtp_error_t uvgrtp::rtp::packet_handler(ssize_t size, void *packet, int flags, uvgrtp::frame::rtp_frame **out)
 | |
| {
 | |
|     (void)flags;
 | |
| 
 | |
|     /* not an RTP frame */
 | |
|     if (size < 12)
 | |
|         return RTP_PKT_NOT_HANDLED;
 | |
| 
 | |
|     uint8_t *ptr = (uint8_t *)packet;
 | |
| 
 | |
|     /* invalid version */
 | |
|     if (((ptr[0] >> 6) & 0x03) != 0x2)
 | |
|         return RTP_PKT_NOT_HANDLED;
 | |
| 
 | |
|     if (!(*out = uvgrtp::frame::alloc_rtp_frame()))
 | |
|         return RTP_GENERIC_ERROR;
 | |
| 
 | |
|     (*out)->header.version   = (ptr[0] >> 6) & 0x03;
 | |
|     (*out)->header.padding   = (ptr[0] >> 5) & 0x01;
 | |
|     (*out)->header.ext       = (ptr[0] >> 4) & 0x01;
 | |
|     (*out)->header.cc        = (ptr[0] >> 0) & 0x0f;
 | |
|     (*out)->header.marker    = (ptr[1] & 0x80) ? 1 : 0;
 | |
|     (*out)->header.payload   = (ptr[1] & 0x7f);
 | |
|     (*out)->header.seq       = ntohs(*(uint16_t *)&ptr[2]);
 | |
|     (*out)->header.timestamp = ntohl(*(uint32_t *)&ptr[4]);
 | |
|     (*out)->header.ssrc      = ntohl(*(uint32_t *)&ptr[8]);
 | |
| 
 | |
|     (*out)->payload_len = (size_t)size - sizeof(uvgrtp::frame::rtp_header);
 | |
| 
 | |
|     /* Skip the generic RTP header
 | |
|      * There may be 0..N CSRC entries after the header, so check those */
 | |
|     ptr += sizeof(uvgrtp::frame::rtp_header);
 | |
| 
 | |
|     if ((*out)->header.cc > 0) {
 | |
|         LOG_DEBUG("frame contains csrc entries");
 | |
| 
 | |
|         if ((ssize_t)((*out)->payload_len - (*out)->header.cc * sizeof(uint32_t)) < 0) {
 | |
|             LOG_DEBUG("Invalid frame length, %d CSRC entries, total length %zu", (*out)->header.cc, (*out)->payload_len);
 | |
|             (void)uvgrtp::frame::dealloc_frame(*out);
 | |
|             return RTP_GENERIC_ERROR;
 | |
|         }
 | |
|         LOG_DEBUG("Allocating %u CSRC entries", (*out)->header.cc);
 | |
| 
 | |
|         (*out)->csrc         = new uint32_t[(*out)->header.cc];
 | |
|         (*out)->payload_len -= (*out)->header.cc * sizeof(uint32_t);
 | |
| 
 | |
|         for (size_t i = 0; i < (*out)->header.cc; ++i) {
 | |
|             (*out)->csrc[i]  = *(uint32_t *)ptr;
 | |
|             ptr             += sizeof(uint32_t);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if ((*out)->header.ext) {
 | |
|         LOG_DEBUG("Frame contains extension information");
 | |
|         (*out)->ext = new uvgrtp::frame::ext_header;
 | |
| 
 | |
|         (*out)->ext->type  = ntohs(*(uint16_t *)&ptr[0]);
 | |
|         (*out)->ext->len   = ntohs(*(uint32_t *)&ptr[1]);
 | |
|         (*out)->ext->data  = (uint8_t *)memdup(ptr + 2 * sizeof(uint16_t), (*out)->ext->len);
 | |
|         ptr               += 2 * sizeof(uint16_t) + (*out)->ext->len;
 | |
|     }
 | |
| 
 | |
|     /* If padding is set to 1, the last byte of the payload indicates
 | |
|      * how many padding bytes was used. Make sure the padding length is
 | |
|      * valid and subtract the amount of padding bytes from payload length */
 | |
|     if ((*out)->header.padding) {
 | |
|         LOG_DEBUG("Frame contains padding");
 | |
|         uint8_t padding_len = (*out)->payload[(*out)->payload_len - 1];
 | |
| 
 | |
|         if (!padding_len || (*out)->payload_len <= padding_len) {
 | |
|             uvgrtp::frame::dealloc_frame(*out);
 | |
|             return RTP_GENERIC_ERROR;
 | |
|         }
 | |
| 
 | |
|         (*out)->payload_len -= padding_len;
 | |
|         (*out)->padding_len  = padding_len;
 | |
|     }
 | |
| 
 | |
|     (*out)->payload    = (uint8_t *)memdup(ptr, (*out)->payload_len);
 | |
|     (*out)->dgram      = (uint8_t *)packet;
 | |
|     (*out)->dgram_size = size;
 | |
| 
 | |
|     return RTP_PKT_MODIFIED;
 | |
| }
 |