From d60b2403411fca9348e2e50afdb28bc33a9f7ffe Mon Sep 17 00:00:00 2001 From: Aaro Altonen Date: Tue, 8 Sep 2020 08:19:38 +0300 Subject: [PATCH] Separate HEVC code into H265 and H26x H26x shall contain all code common to H26{4,5,6} and H26{4,5,6} files shall contain format-specific code which is mainly just NAL unit fragmentation. This commit also changes the HEVC RTP format name from RTP_FORMAT_HEVC to RTP_FORMAT_H265 to unify naming with upcoming AVC/VVC support --- examples/binding.cc | 2 +- examples/configuration.cc | 2 +- examples/custom_timestamps.cc | 2 +- examples/deallocation_1.cc | 2 +- examples/deallocation_2.cc | 2 +- examples/deallocation_3.cc | 2 +- examples/receiving_hook.cc | 2 +- examples/receiving_poll.cc | 2 +- examples/sending.cc | 2 +- examples/sending_fragmented.cc | 2 +- include/formats/{hevc.hh => h265.hh} | 33 ++-- include/formats/h26x.hh | 43 +++++ include/formats/media.hh | 4 +- include/frame.hh | 8 +- include/util.hh | 4 +- src/formats/h265.cc | 165 ++++++++++++++++ ...evc_pkt_handler.cc => h265_pkt_handler.cc} | 14 +- src/formats/{hevc.cc => h26x.cc} | 181 +++--------------- src/formats/media.cc | 6 +- src/formats/module.mk | 5 +- src/media_stream.cc | 20 +- src/queue.cc | 14 +- src/rtp.cc | 2 +- 23 files changed, 301 insertions(+), 218 deletions(-) rename include/formats/{hevc.hh => h265.hh} (50%) create mode 100644 include/formats/h26x.hh create mode 100644 src/formats/h265.cc rename src/formats/{hevc_pkt_handler.cc => h265_pkt_handler.cc} (95%) rename src/formats/{hevc.cc => h26x.cc} (58%) diff --git a/examples/binding.cc b/examples/binding.cc index 98df64c..dffada9 100644 --- a/examples/binding.cc +++ b/examples/binding.cc @@ -13,7 +13,7 @@ int main(void) /* 8888 is source port or the port for the interface where data is received (ie. 10.21.25.200:8888) * 8889 is remote port or the port for the interface where the data is sent (ie. 10.21.25.2:8889) */ - uvg_rtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_HEVC, 0); + uvg_rtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_H265, 0); while (true) { std::unique_ptr buffer = std::unique_ptr(new uint8_t[PAYLOAD_MAXLEN]); diff --git a/examples/configuration.cc b/examples/configuration.cc index 1769520..9e3687c 100644 --- a/examples/configuration.cc +++ b/examples/configuration.cc @@ -14,7 +14,7 @@ int main(void) /* Enable system call dispatcher for the sender to minimize delay experienced by the application * * See sending.cc for more details about media stream initialization */ - uvg_rtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_HEVC, RCE_SYSTEM_CALL_DISPATCHER); + uvg_rtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_H265, RCE_SYSTEM_CALL_DISPATCHER); /* Increase UDP send/recv buffers to 40 MB */ hevc->configure_ctx(RCC_UDP_RCV_BUF_SIZE, 40 * 1000 * 1000); diff --git a/examples/custom_timestamps.cc b/examples/custom_timestamps.cc index 7aa077c..feb9878 100644 --- a/examples/custom_timestamps.cc +++ b/examples/custom_timestamps.cc @@ -12,7 +12,7 @@ int main(void) uvg_rtp::session *sess = ctx.create_session("127.0.0.1"); /* Create MediaStream and RTCP for the session */ - uvg_rtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_HEVC, RCE_RTCP); + uvg_rtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_H265, RCE_RTCP); uint8_t *buffer = new uint8_t[PAYLOAD_MAXLEN]; uint32_t clock_rate = 90000 / 30; diff --git a/examples/deallocation_1.cc b/examples/deallocation_1.cc index 66d9a09..dbbe66e 100644 --- a/examples/deallocation_1.cc +++ b/examples/deallocation_1.cc @@ -14,7 +14,7 @@ int main(void) * SCD requires that we provide some deallocation mechanism, this example is about the smart pointer approach * * See sending.cc for more details about media stream initialization */ - uvg_rtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_HEVC, RCE_SYSTEM_CALL_DISPATCHER); + uvg_rtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_H265, RCE_SYSTEM_CALL_DISPATCHER); for (int i = 0; i < 10; ++i) { std::unique_ptr buffer = std::unique_ptr(new uint8_t[PAYLOAD_MAXLEN]); diff --git a/examples/deallocation_2.cc b/examples/deallocation_2.cc index 936645b..237b0e5 100644 --- a/examples/deallocation_2.cc +++ b/examples/deallocation_2.cc @@ -16,7 +16,7 @@ int main(void) * This example is about the copy approach * * See sending.cc for more details about media stream initialization */ - uvg_rtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_HEVC, RCE_SYSTEM_CALL_DISPATCHER); + uvg_rtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_H265, RCE_SYSTEM_CALL_DISPATCHER); uint8_t *buffer = new uint8_t[PAYLOAD_MAXLEN]; diff --git a/examples/deallocation_3.cc b/examples/deallocation_3.cc index a395a05..d38e708 100644 --- a/examples/deallocation_3.cc +++ b/examples/deallocation_3.cc @@ -16,7 +16,7 @@ int main(void) * This example is about the deallocation hook approach * * See sending.cc for more details about media stream initialization */ - uvg_rtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_HEVC, RCE_SYSTEM_CALL_DISPATCHER); + uvg_rtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_H265, RCE_SYSTEM_CALL_DISPATCHER); /* When SCD has processed this memory chunk, it will call dealloc_hook() * which will do all the necessary deallocation steps required by the application diff --git a/examples/receiving_hook.cc b/examples/receiving_hook.cc index ed2dfd3..5f33c45 100644 --- a/examples/receiving_hook.cc +++ b/examples/receiving_hook.cc @@ -40,7 +40,7 @@ int main(void) * This same object is used for both sending and receiving media * * 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); + uvg_rtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_H265, 0); /* Receive hook can be installed and the receiver will call this hook when an RTP frame is received * diff --git a/examples/receiving_poll.cc b/examples/receiving_poll.cc index 005ec75..c032c5c 100644 --- a/examples/receiving_poll.cc +++ b/examples/receiving_poll.cc @@ -24,7 +24,7 @@ int main(void) * This same object is used for both sending and receiving media * * 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); + uvg_rtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_H265, 0); /* pull_frame() will block until a frame is received. * diff --git a/examples/sending.cc b/examples/sending.cc index 51c1bea..9f0cca7 100644 --- a/examples/sending.cc +++ b/examples/sending.cc @@ -25,7 +25,7 @@ int main(void) * This same object is used for both sending and receiving media * * 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); + uvg_rtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_H265, 0); uint8_t *buffer = new uint8_t[PAYLOAD_MAXLEN]; uint32_t timestamp = 0; diff --git a/examples/sending_fragmented.cc b/examples/sending_fragmented.cc index 8bc1a73..377762c 100644 --- a/examples/sending_fragmented.cc +++ b/examples/sending_fragmented.cc @@ -11,7 +11,7 @@ int main(void) uvg_rtp::session *sess = ctx.create_session("127.0.0.1"); /* See sending.cc for more details */ - uvg_rtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_HEVC, 0); + uvg_rtp::media_stream *hevc = sess->create_stream(8888, 8889, RTP_FORMAT_H265, 0); /* Three buffers that create one discrete frame (perhaps three NAL units) that all should have the same timestamp */ auto buffer1 = std::unique_ptr(new uint8_t[PAYLOAD_MAXLEN]); diff --git a/include/formats/hevc.hh b/include/formats/h265.hh similarity index 50% rename from include/formats/hevc.hh rename to include/formats/h265.hh index b256808..6e3458a 100644 --- a/include/formats/hevc.hh +++ b/include/formats/h265.hh @@ -1,46 +1,49 @@ #pragma once #include "frame.hh" -#include "media.hh" #include "queue.hh" +#include "formats/h26x.hh" namespace uvg_rtp { namespace formats { - struct hevc_headers { - uint8_t nal_header[uvg_rtp::frame::HEADER_SIZE_HEVC_NAL]; + struct h265_headers { + uint8_t nal_header[uvg_rtp::frame::HEADER_SIZE_H265_NAL]; /* there are three types of Fragmentation Unit headers: * - header for the first fragment * - header for all middle fragments * - header for the last fragment */ - uint8_t fu_headers[3 * uvg_rtp::frame::HEADER_SIZE_HEVC_FU]; + uint8_t fu_headers[3 * uvg_rtp::frame::HEADER_SIZE_H265_FU]; }; - class hevc : public media { + class h265 : public h26x { public: - hevc(uvg_rtp::socket *socket, uvg_rtp::rtp *rtp, int flags); - ~hevc(); + h265(uvg_rtp::socket *socket, uvg_rtp::rtp *rtp, int flags); + ~h265(); /* Packet handler for RTP frames that transport HEVC bitstream * - * Depending on what is received, packet handler will either merge - * TODO explain what it does on a high level + * If "frame" is not a fragmentation unit, packet handler checks + * if "frame" is SPS/VPS/PPS packet and if so, returns the packet + * to user immediately. + * + * If "frame" is a fragmentation unit, packet handler checks if + * it has received all fragments of a complete HEVC NAL unit and if + * so, it merges all fragments into a complete NAL unit and returns + * the NAL unit to user. If the NAL unit is not complete, packet + * handler holds onto the frame and waits for other fragments to arrive. * * Return RTP_OK if the packet was successfully handled - * Return RTP_PKT_READY if "out" contains an RTP that can be returned to user + * Return RTP_PKT_READY if "frame" contains an RTP that can be returned to user * Return RTP_PKT_NOT_HANDLED if the packet is not handled by this handler * Return RTP_PKT_MODIFIED if the packet was modified but should be forwarded to other handlers * Return RTP_GENERIC_ERROR if the packet was corrupted in some way */ static rtp_error_t packet_handler(void *arg, int flags, frame::rtp_frame **frame); protected: - rtp_error_t __push_frame(uint8_t *data, size_t data_len, int flags); - - private: - rtp_error_t push_hevc_frame(uint8_t *data, size_t data_len); - rtp_error_t push_hevc_nal(uint8_t *data, size_t data_len, bool more); + rtp_error_t push_nal_unit(uint8_t *data, size_t data_len, bool more); }; }; }; diff --git a/include/formats/h26x.hh b/include/formats/h26x.hh new file mode 100644 index 0000000..790e22a --- /dev/null +++ b/include/formats/h26x.hh @@ -0,0 +1,43 @@ +#pragma once + +#include "frame.hh" +#include "media.hh" +#include "queue.hh" + +namespace uvg_rtp { + + namespace formats { + + class h26x : public media { + public: + h26x(uvg_rtp::socket *socket, uvg_rtp::rtp *rtp, int flags); + virtual ~h26x(); + + /* Find H26x start code from "data" + * This process is the same for H26{4,5,6} + * + * Return the offset of the start code on success + * Return -1 if no start code was found */ + ssize_t find_h26x_start_code(uint8_t *data, size_t len, size_t offset, uint8_t& start_len); + + /* Top-level push_frame() called by the Media class + * Sets up the frame queue for the send operation + * + * Return RTP_OK on success + * Return RTP_INVALID_VALUE if one of the parameters is invalid */ + rtp_error_t push_media_frame(uint8_t *data, size_t data_len, int flags); + + /* Last push_frame() on the call stack which splits the input frame ("data") + * into NAL units using find_h26x_start_code() and fragments the NAL unit + * into Fragmentation Units (FUs) which are pushed to frame queue + * + * Return RTP_OK on success + * Return RTP_INVALID_VALUE if one the parameters is invalid */ + rtp_error_t push_h26x_frame(uint8_t *data, size_t data_len); + + protected: + /* Each H26x class overrides this function with their custom NAL pushing function */ + virtual rtp_error_t push_nal_unit(uint8_t *data, size_t data_len, bool more); + }; + }; +}; diff --git a/include/formats/media.hh b/include/formats/media.hh index 2a8582e..acdbe2a 100644 --- a/include/formats/media.hh +++ b/include/formats/media.hh @@ -17,7 +17,7 @@ namespace uvg_rtp { virtual ~media(); /* These two functions are called by media_stream which is self is called by the application. - * They act as thunks and forward the call to __push_frame() which every media should + * They act as thunks and forward the call to push_media_frame() which every media should * implement if they require more processing than what the default implementation offers * * Return RTP_OK on success */ @@ -37,7 +37,7 @@ namespace uvg_rtp { static rtp_error_t packet_handler(void *arg, int flags, frame::rtp_frame **frame); protected: - virtual rtp_error_t __push_frame(uint8_t *data, size_t data_len, int flags); + virtual rtp_error_t push_media_frame(uint8_t *data, size_t data_len, int flags); uvg_rtp::socket *socket_; uvg_rtp::rtp *rtp_ctx_; diff --git a/include/frame.hh b/include/frame.hh index accb67b..56ff016 100644 --- a/include/frame.hh +++ b/include/frame.hh @@ -13,8 +13,6 @@ #include "util.hh" -#define INVALID_FRAME_TYPE(ft) (ft < RTP_FT_GENERIC|| ft > RTP_FT_HEVC_FU) - #define RTP_HEADER_LENGTH 12 #define RTCP_HEADER_LENGTH 12 @@ -23,14 +21,14 @@ namespace uvg_rtp { enum HEADER_SIZES { HEADER_SIZE_RTP = 12, HEADER_SIZE_OPUS = 1, - HEADER_SIZE_HEVC_NAL = 2, - HEADER_SIZE_HEVC_FU = 1, + HEADER_SIZE_H265_NAL = 2, + HEADER_SIZE_H265_FU = 1, }; enum RTP_FRAME_TYPE { RTP_FT_GENERIC = 0, /* payload length + RTP Header size (N + 12) */ RTP_FT_OPUS = 1, /* payload length + RTP Header size + Opus header (N + 12 + 0 [for now]) */ - RTP_FT_HEVC_FU = 2, /* payload length + RTP Header size + HEVC NAL Header + FU Header (N + 12 + 2 + 1) */ + RTP_FT_H265_FU = 2, /* payload length + RTP Header size + HEVC NAL Header + FU Header (N + 12 + 2 + 1) */ }; enum RTCP_FRAME_TYPE { diff --git a/include/util.hh b/include/util.hh index a74e332..384003b 100644 --- a/include/util.hh +++ b/include/util.hh @@ -61,7 +61,7 @@ typedef enum RTP_ERROR { typedef enum RTP_FORMAT { RTP_FORMAT_GENERIC = 0, - RTP_FORMAT_HEVC = 96, + RTP_FORMAT_H265 = 96, RTP_FORMAT_OPUS = 97, } rtp_format_t; @@ -143,7 +143,7 @@ enum RTP_CTX_ENABLE_FLAGS { * This behavior can be disabled with RCE_HEVC_NO_INTRA_DELAY * If this flag is given, uvgRTP treats all frame types * equally and drops all frames that are late */ - RCE_HEVC_NO_INTRA_DELAY = 1 << 5, + RCE_H265_NO_INTRA_DELAY = 1 << 5, /* Fragment generic frames into RTP packets of 1500 bytes. * diff --git a/src/formats/h265.cc b/src/formats/h265.cc new file mode 100644 index 0000000..3c57817 --- /dev/null +++ b/src/formats/h265.cc @@ -0,0 +1,165 @@ +#ifdef _WIN32 +#else +#include +#endif + +#include +#include +#include +#include +#include + +#include "debug.hh" +#include "queue.hh" + +#include "formats/h265.hh" + +rtp_error_t uvg_rtp::formats::h265::push_nal_unit(uint8_t *data, size_t data_len, bool more) +{ + if (data_len <= 3) + return RTP_INVALID_VALUE; + + uint8_t nal_type = (data[0] >> 1) & 0x3F; + rtp_error_t ret = RTP_OK; + size_t data_left = data_len; + size_t data_pos = 0; + size_t payload_size = rtp_ctx_->get_payload_size(); + +#ifdef __linux__ + if (data_len - 3 <= payload_size) { + if ((ret = fqueue_->enqueue_message(data, data_len)) != RTP_OK) { + LOG_ERROR("enqeueu failed for small packet"); + return ret; + } + + if (more) + return RTP_NOT_READY; + return fqueue_->flush_queue(); + } + + /* The payload is larger than MTU (1500 bytes) so we must split it into smaller RTP frames + * Because we don't if the SCD is enabled and thus cannot make any assumptions about the life time + * of current stack, we need to store NAL and FU headers to the frame queue transaction. + * + * This can be done by asking a handle to current transaction's buffer vectors. + * + * During Connection initialization, the frame queue was given HEVC as the payload format so the + * transaction also contains our media-specifi headers [get_media_headers()]. */ + auto buffers = fqueue_->get_buffer_vector(); + auto headers = (uvg_rtp::formats::h265_headers *)fqueue_->get_media_headers(); + + headers->nal_header[0] = 49 << 1; /* fragmentation unit */ + headers->nal_header[1] = 1; /* temporal id */ + + headers->fu_headers[0] = (uint8_t)((1 << 7) | nal_type); + headers->fu_headers[1] = nal_type; + headers->fu_headers[2] = (uint8_t)((1 << 6) | nal_type); + + buffers.push_back(std::make_pair(sizeof(headers->nal_header), headers->nal_header)); + buffers.push_back(std::make_pair(sizeof(uint8_t), &headers->fu_headers[0])); + buffers.push_back(std::make_pair(payload_size, nullptr)); + + data_pos = uvg_rtp::frame::HEADER_SIZE_H265_NAL; + data_left -= uvg_rtp::frame::HEADER_SIZE_H265_NAL; + + while (data_left > payload_size) { + buffers.at(2).first = payload_size; + buffers.at(2).second = &data[data_pos]; + + if ((ret = fqueue_->enqueue_message(buffers)) != RTP_OK) { + LOG_ERROR("Queueing the message failed!"); + fqueue_->deinit_transaction(); + return ret; + } + + data_pos += payload_size; + data_left -= payload_size; + + /* from now on, use the FU header meant for middle fragments */ + buffers.at(1).second = &headers->fu_headers[1]; + } + + /* use the FU header meant for the last fragment */ + buffers.at(1).second = &headers->fu_headers[2]; + + buffers.at(2).first = data_left; + buffers.at(2).second = &data[data_pos]; + + if ((ret = fqueue_->enqueue_message(buffers)) != RTP_OK) { + LOG_ERROR("Failed to send HEVC frame!"); + fqueue_->deinit_transaction(); + return ret; + } + + if (more) + return RTP_NOT_READY; + return fqueue_->flush_queue(); +#else + if (data_len - 3 <= payload_size) { + LOG_DEBUG("send unfrag size %zu, type %u", data_len, nal_type); + + if ((ret = uvg_rtp::generic::push_frame(sender, data, data_len, 0)) != RTP_OK) { + LOG_ERROR("Failed to send small packet! %s", strerror(errno)); + return ret; + } + + if (more) + return RTP_NOT_READY; + return RTP_OK; + } + + const size_t HEADER_SIZE = + uvg_rtp::frame::HEADER_SIZE_RTP + + uvg_rtp::frame::HEADER_SIZE_H265_NAL + + uvg_rtp::frame::HEADER_SIZE_H265_FU; + + uint8_t buffer[HEADER_SIZE + payload_size] = { 0 }; + + rtp_ctx_->fill_header(buffer); + + buffer[uvg_rtp::frame::HEADER_SIZE_RTP + 0] = 49 << 1; /* fragmentation unit */ + buffer[uvg_rtp::frame::HEADER_SIZE_RTP + 1] = 1; /* TID */ + buffer[uvg_rtp::frame::HEADER_SIZE_RTP + + uvg_rtp::frame::HEADER_SIZE_H265_NAL] = (1 << 7) | nal_type; /* Start bit + NAL type */ + + data_pos = uvg_rtp::frame::HEADER_SIZE_H265_NAL; + data_left -= uvg_rtp::frame::HEADER_SIZE_H265_NAL; + + while (data_left > payload_size) { + memcpy(&buffer[HEADER_SIZE], &data[data_pos], payload_size); + + if ((ret = socket_->send(buffer, sizeof(buffer), 0)) != RTP_OK) + return ret; + + rtp_ctx_->update_sequence(buffer); + + data_pos += payload_size; + data_left -= payload_size; + + /* Clear extra bits */ + buffer[uvg_rtp::frame::HEADER_SIZE_RTP + + uvg_rtp::frame::HEADER_SIZE_H265_NAL] = nal_type; + } + + buffer[uvg_rtp::frame::HEADER_SIZE_RTP + + uvg_rtp::frame::HEADER_SIZE_H265_NAL] = nal_type | (1 << 6); /* set E bit to signal end of data */ + + memcpy(&buffer[HEADER_SIZE], &data[data_pos], data_left); + + if ((ret = socket_->sendto(buffer, HEADER_SIZE + data_left, 0)) != RTP_OK) + return ret; + + if (more) + return RTP_NOT_READY; + return RTP_OK; +#endif +} + +uvg_rtp::formats::h265::h265(uvg_rtp::socket *socket, uvg_rtp::rtp *rtp, int flags): + h26x(socket, rtp, flags) +{ +} + +uvg_rtp::formats::h265::~h265() +{ +} diff --git a/src/formats/hevc_pkt_handler.cc b/src/formats/h265_pkt_handler.cc similarity index 95% rename from src/formats/hevc_pkt_handler.cc rename to src/formats/h265_pkt_handler.cc index 4123c53..d9ad9fc 100644 --- a/src/formats/hevc_pkt_handler.cc +++ b/src/formats/h265_pkt_handler.cc @@ -7,7 +7,7 @@ #include "debug.hh" #include "queue.hh" -#include "formats/hevc.hh" +#include "formats/h265.hh" #define RTP_FRAME_MAX_DELAY 100 #define INVALID_SEQ 0x13371338 @@ -102,7 +102,7 @@ static void __drop_frame(frame_info_t& finfo, uint32_t ts) finfo.erase(ts); } -rtp_error_t uvg_rtp::formats::hevc::packet_handler(void *arg, int flags, uvg_rtp::frame::rtp_frame **out) +rtp_error_t uvg_rtp::formats::h265::packet_handler(void *arg, int flags, uvg_rtp::frame::rtp_frame **out) { (void)arg; @@ -110,7 +110,7 @@ rtp_error_t uvg_rtp::formats::hevc::packet_handler(void *arg, int flags, uvg_rtp static std::unordered_set dropped; uvg_rtp::frame::rtp_frame *frame; - bool enable_idelay = !(flags & RCE_HEVC_NO_INTRA_DELAY); + bool enable_idelay = !(flags & RCE_H265_NO_INTRA_DELAY); /* Use "intra" to keep track of intra frames * @@ -127,8 +127,8 @@ rtp_error_t uvg_rtp::formats::hevc::packet_handler(void *arg, int flags, uvg_rtp uint32_t intra = INVALID_TS; const size_t HEVC_HDR_SIZE = - uvg_rtp::frame::HEADER_SIZE_HEVC_NAL + - uvg_rtp::frame::HEADER_SIZE_HEVC_FU; + uvg_rtp::frame::HEADER_SIZE_H265_NAL + + uvg_rtp::frame::HEADER_SIZE_H265_FU; frame = *out; @@ -217,13 +217,13 @@ rtp_error_t uvg_rtp::formats::hevc::packet_handler(void *arg, int flags, uvg_rtp /* uvg_rtp::frame::rtp_frame *out = uvg_rtp::frame::alloc_rtp_frame(); */ uvg_rtp::frame::rtp_frame *complete = uvg_rtp::frame::alloc_rtp_frame(); - complete->payload_len = finfo[c_ts].total_size + uvg_rtp::frame::HEADER_SIZE_HEVC_NAL; + complete->payload_len = finfo[c_ts].total_size + uvg_rtp::frame::HEADER_SIZE_H265_NAL; complete->payload = new uint8_t[complete->payload_len]; std::memcpy(&complete->header, &(*out)->header, RTP_HDR_SIZE); std::memcpy(complete->payload, nal_header, NAL_HDR_SIZE); - fptr += uvg_rtp::frame::HEADER_SIZE_HEVC_NAL; + fptr += uvg_rtp::frame::HEADER_SIZE_H265_NAL; for (auto& fragment : finfo.at(c_ts).fragments) { std::memcpy( diff --git a/src/formats/hevc.cc b/src/formats/h26x.cc similarity index 58% rename from src/formats/hevc.cc rename to src/formats/h26x.cc index 82125f7..b428826 100644 --- a/src/formats/hevc.cc +++ b/src/formats/h26x.cc @@ -12,7 +12,7 @@ #include "debug.hh" #include "queue.hh" -#include "formats/hevc.hh" +#include "formats/h26x.hh" #define PTR_DIFF(a, b) ((ptrdiff_t)((char *)(a) - (char *)(b))) @@ -72,7 +72,12 @@ static inline unsigned __find_hevc_start(uint32_t value) /* NOTE: the area 0 - len (ie data[0] - data[len - 1]) must be addressable * Do not add offset to "data" ptr before passing it to __get_hevc_start()! */ -static ssize_t __get_hevc_start(uint8_t *data, size_t len, size_t offset, uint8_t& start_len) +ssize_t uvg_rtp::formats::h26x::find_h26x_start_code( + uint8_t *data, + size_t len, + size_t offset, + uint8_t& start_len +) { bool prev_z = false; bool cur_z = false; @@ -225,153 +230,12 @@ end: return -1; } -rtp_error_t uvg_rtp::formats::hevc::push_hevc_nal(uint8_t *data, size_t data_len, bool more) -{ - if (data_len <= 3) - return RTP_INVALID_VALUE; - - uint8_t nal_type = (data[0] >> 1) & 0x3F; - rtp_error_t ret = RTP_OK; - size_t data_left = data_len; - size_t data_pos = 0; - size_t payload_size = rtp_ctx_->get_payload_size(); - -#ifdef __linux__ - if (data_len - 3 <= payload_size) { - if ((ret = fqueue_->enqueue_message(data, data_len)) != RTP_OK) { - LOG_ERROR("enqeueu failed for small packet"); - return ret; - } - - if (more) - return RTP_NOT_READY; - return fqueue_->flush_queue(); - } - - /* The payload is larger than MTU (1500 bytes) so we must split it into smaller RTP frames - * Because we don't if the SCD is enabled and thus cannot make any assumptions about the life time - * of current stack, we need to store NAL and FU headers to the frame queue transaction. - * - * This can be done by asking a handle to current transaction's buffer vectors. - * - * During Connection initialization, the frame queue was given HEVC as the payload format so the - * transaction also contains our media-specifi headers [get_media_headers()]. */ - auto buffers = fqueue_->get_buffer_vector(); - auto headers = (uvg_rtp::formats::hevc_headers *)fqueue_->get_media_headers(); - - headers->nal_header[0] = 49 << 1; /* fragmentation unit */ - headers->nal_header[1] = 1; /* temporal id */ - - headers->fu_headers[0] = (uint8_t)((1 << 7) | nal_type); - headers->fu_headers[1] = nal_type; - headers->fu_headers[2] = (uint8_t)((1 << 6) | nal_type); - - buffers.push_back(std::make_pair(sizeof(headers->nal_header), headers->nal_header)); - buffers.push_back(std::make_pair(sizeof(uint8_t), &headers->fu_headers[0])); - buffers.push_back(std::make_pair(payload_size, nullptr)); - - data_pos = uvg_rtp::frame::HEADER_SIZE_HEVC_NAL; - data_left -= uvg_rtp::frame::HEADER_SIZE_HEVC_NAL; - - while (data_left > payload_size) { - buffers.at(2).first = payload_size; - buffers.at(2).second = &data[data_pos]; - - if ((ret = fqueue_->enqueue_message(buffers)) != RTP_OK) { - LOG_ERROR("Queueing the message failed!"); - fqueue_->deinit_transaction(); - return ret; - } - - data_pos += payload_size; - data_left -= payload_size; - - /* from now on, use the FU header meant for middle fragments */ - buffers.at(1).second = &headers->fu_headers[1]; - } - - /* use the FU header meant for the last fragment */ - buffers.at(1).second = &headers->fu_headers[2]; - - buffers.at(2).first = data_left; - buffers.at(2).second = &data[data_pos]; - - if ((ret = fqueue_->enqueue_message(buffers)) != RTP_OK) { - LOG_ERROR("Failed to send HEVC frame!"); - fqueue_->deinit_transaction(); - return ret; - } - - if (more) - return RTP_NOT_READY; - return fqueue_->flush_queue(); -#else - if (data_len - 3 <= payload_size) { - LOG_DEBUG("send unfrag size %zu, type %u", data_len, nal_type); - - if ((ret = uvg_rtp::generic::push_frame(sender, data, data_len, 0)) != RTP_OK) { - LOG_ERROR("Failed to send small packet! %s", strerror(errno)); - return ret; - } - - if (more) - return RTP_NOT_READY; - return RTP_OK; - } - - const size_t HEADER_SIZE = - uvg_rtp::frame::HEADER_SIZE_RTP + - uvg_rtp::frame::HEADER_SIZE_HEVC_NAL + - uvg_rtp::frame::HEADER_SIZE_HEVC_FU; - - uint8_t buffer[HEADER_SIZE + payload_size] = { 0 }; - - rtp_ctx_->fill_header(buffer); - - buffer[uvg_rtp::frame::HEADER_SIZE_RTP + 0] = 49 << 1; /* fragmentation unit */ - buffer[uvg_rtp::frame::HEADER_SIZE_RTP + 1] = 1; /* TID */ - buffer[uvg_rtp::frame::HEADER_SIZE_RTP + - uvg_rtp::frame::HEADER_SIZE_HEVC_NAL] = (1 << 7) | nal_type; /* Start bit + NAL type */ - - data_pos = uvg_rtp::frame::HEADER_SIZE_HEVC_NAL; - data_left -= uvg_rtp::frame::HEADER_SIZE_HEVC_NAL; - - while (data_left > payload_size) { - memcpy(&buffer[HEADER_SIZE], &data[data_pos], payload_size); - - if ((ret = socket_->send(buffer, sizeof(buffer), 0)) != RTP_OK) - return ret; - - rtp_ctx_->update_sequence(buffer); - - data_pos += payload_size; - data_left -= payload_size; - - /* Clear extra bits */ - buffer[uvg_rtp::frame::HEADER_SIZE_RTP + - uvg_rtp::frame::HEADER_SIZE_HEVC_NAL] = nal_type; - } - - buffer[uvg_rtp::frame::HEADER_SIZE_RTP + - uvg_rtp::frame::HEADER_SIZE_HEVC_NAL] = nal_type | (1 << 6); /* set E bit to signal end of data */ - - memcpy(&buffer[HEADER_SIZE], &data[data_pos], data_left); - - if ((ret = socket_->sendto(buffer, HEADER_SIZE + data_left, 0)) != RTP_OK) - return ret; - - if (more) - return RTP_NOT_READY; - return RTP_OK; -#endif -} - -rtp_error_t uvg_rtp::formats::hevc::push_hevc_frame(uint8_t *data, size_t data_len) +rtp_error_t uvg_rtp::formats::h26x::push_h26x_frame(uint8_t *data, size_t data_len) { #ifdef __linux__ /* find first start code */ uint8_t start_len = 0; - int offset = __get_hevc_start(data, data_len, 0, start_len); + int offset = find_h26x_start_code(data, data_len, 0, start_len); int prev_offset = offset; size_t r_off = 0; rtp_error_t ret = RTP_GENERIC_ERROR; @@ -384,10 +248,10 @@ rtp_error_t uvg_rtp::formats::hevc::push_hevc_frame(uint8_t *data, size_t data_l } while (offset != -1) { - offset = __get_hevc_start(data, data_len, offset, start_len); + offset = find_h26x_start_code(data, data_len, offset, start_len); if (offset != -1) { - ret = push_hevc_nal(&data[prev_offset], offset - prev_offset - start_len, true); + ret = push_nal_unit(&data[prev_offset], offset - prev_offset - start_len, true); if (ret != RTP_NOT_READY) goto error; @@ -399,7 +263,7 @@ rtp_error_t uvg_rtp::formats::hevc::push_hevc_frame(uint8_t *data, size_t data_l if (prev_offset == -1) prev_offset = 0; - if ((ret = push_hevc_nal(&data[prev_offset], data_len - prev_offset, false)) == RTP_OK) + if ((ret = push_nal_unit(&data[prev_offset], data_len - prev_offset, false)) == RTP_OK) return RTP_OK; error: @@ -416,7 +280,7 @@ error: offset = __get_hevc_start(data, data_len, offset, start_len); if (offset > 4 && offset != -1) { - if ((ret = __push_hevc_nal(&data[prev_offset], offset - prev_offset - start_len, false)) == -1) + if ((ret = push_nal_unit(&data[prev_offset], offset - prev_offset - start_len, false)) == -1) goto end; prev_offset = offset; @@ -426,24 +290,33 @@ error: if (prev_offset == -1) prev_offset = 0; - ret = __push_hevc_nal(&data[prev_offset], data_len - prev_offset, false); + ret = push_nal_unit(&data[prev_offset], data_len - prev_offset, false); end: fqueue_->deinit_transaction(); return ret; #endif } -uvg_rtp::formats::hevc::hevc(uvg_rtp::socket *socket, uvg_rtp::rtp *rtp, int flags): +rtp_error_t uvg_rtp::formats::h26x::push_nal_unit(uint8_t *data, size_t data_len, bool more) +{ + (void)data, (void)data_len, (void)more; + + LOG_ERROR("Each H26x class must implement this function!"); + + return RTP_NOT_SUPPORTED; +} + +uvg_rtp::formats::h26x::h26x(uvg_rtp::socket *socket, uvg_rtp::rtp *rtp, int flags): media(socket, rtp, flags) { } -uvg_rtp::formats::hevc::~hevc() +uvg_rtp::formats::h26x::~h26x() { delete fqueue_; } -rtp_error_t uvg_rtp::formats::hevc::__push_frame(uint8_t *data, size_t data_len, int flags) +rtp_error_t uvg_rtp::formats::h26x::push_media_frame(uint8_t *data, size_t data_len, int flags) { (void)flags; @@ -457,5 +330,5 @@ rtp_error_t uvg_rtp::formats::hevc::__push_frame(uint8_t *data, size_t data_len, return ret; } - return push_hevc_frame(data, data_len); + return push_h26x_frame(data, data_len); } diff --git a/src/formats/media.cc b/src/formats/media.cc index 1131658..e3c4116 100644 --- a/src/formats/media.cc +++ b/src/formats/media.cc @@ -25,7 +25,7 @@ rtp_error_t uvg_rtp::formats::media::push_frame(uint8_t *data, size_t data_len, if (!data || !data_len) return RTP_INVALID_VALUE; - return __push_frame(data, data_len, flags); + return push_media_frame(data, data_len, flags); } rtp_error_t uvg_rtp::formats::media::push_frame(std::unique_ptr data, size_t data_len, int flags) @@ -33,10 +33,10 @@ rtp_error_t uvg_rtp::formats::media::push_frame(std::unique_ptr data, if (!data || !data_len) return RTP_INVALID_VALUE; - return __push_frame(data.get(), data_len, flags); + return push_media_frame(data.get(), data_len, flags); } -rtp_error_t uvg_rtp::formats::media::__push_frame(uint8_t *data, size_t data_len, int flags) +rtp_error_t uvg_rtp::formats::media::push_media_frame(uint8_t *data, size_t data_len, int flags) { (void)flags; diff --git a/src/formats/module.mk b/src/formats/module.mk index 7a4802d..7bd1391 100644 --- a/src/formats/module.mk +++ b/src/formats/module.mk @@ -1,5 +1,6 @@ SOURCES += \ src/formats/media.cc \ - src/formats/hevc.cc \ - src/formats/hevc_pkt_handler.cc \ + src/formats/h26x.cc \ + src/formats/h265.cc \ + src/formats/h265_pkt_handler.cc \ src/formats/hevc_recv_optimistic.cc diff --git a/src/media_stream.cc b/src/media_stream.cc index c729eb8..ea19207 100644 --- a/src/media_stream.cc +++ b/src/media_stream.cc @@ -5,7 +5,7 @@ #include "media_stream.hh" #include "random.hh" -#include "formats/hevc.hh" +#include "formats/h265.hh" #define INVALID_TS UINT64_MAX @@ -131,12 +131,12 @@ rtp_error_t uvg_rtp::media_stream::init() pkt_dispatcher_->install_aux_handler(rtp_handler_key_, rtcp_, rtcp_->recv_packet_handler); switch (fmt_) { - case RTP_FORMAT_HEVC: - media_ = new uvg_rtp::formats::hevc(socket_, rtp_, ctx_config_.flags); + case RTP_FORMAT_H265: + media_ = new uvg_rtp::formats::h265(socket_, rtp_, ctx_config_.flags); pkt_dispatcher_->install_aux_handler( rtp_handler_key_, nullptr, - dynamic_cast(media_)->packet_handler + dynamic_cast(media_)->packet_handler ); break; @@ -239,12 +239,12 @@ rtp_error_t uvg_rtp::media_stream::init(uvg_rtp::zrtp *zrtp) pkt_dispatcher_->install_aux_handler(rtp_handler_key_, srtp_, srtp_->recv_packet_handler); switch (fmt_) { - case RTP_FORMAT_HEVC: - media_ = new uvg_rtp::formats::hevc(socket_, rtp_, ctx_config_.flags); + case RTP_FORMAT_H265: + media_ = new uvg_rtp::formats::h265(socket_, rtp_, ctx_config_.flags); pkt_dispatcher_->install_aux_handler( rtp_handler_key_, nullptr, - dynamic_cast(media_)->packet_handler + dynamic_cast(media_)->packet_handler ); break; @@ -338,12 +338,12 @@ rtp_error_t uvg_rtp::media_stream::add_srtp_ctx(uint8_t *key, uint8_t *salt) pkt_dispatcher_->install_aux_handler(rtp_handler_key_, srtp_, srtp_->recv_packet_handler); switch (fmt_) { - case RTP_FORMAT_HEVC: - media_ = new uvg_rtp::formats::hevc(socket_, rtp_, ctx_config_.flags); + case RTP_FORMAT_H265: + media_ = new uvg_rtp::formats::h265(socket_, rtp_, ctx_config_.flags); pkt_dispatcher_->install_aux_handler( rtp_handler_key_, nullptr, - dynamic_cast(media_)->packet_handler + dynamic_cast(media_)->packet_handler ); break; diff --git a/src/queue.cc b/src/queue.cc index e6f3b80..c7cdb2d 100644 --- a/src/queue.cc +++ b/src/queue.cc @@ -12,7 +12,7 @@ #include "queue.hh" #include "random.hh" -#include "formats/hevc.hh" +#include "formats/h265.hh" uvg_rtp::frame_queue::frame_queue(uvg_rtp::socket *socket, uvg_rtp::rtp *rtp, int flags): rtp_(rtp), socket_(socket), flags_(flags) @@ -57,8 +57,8 @@ rtp_error_t uvg_rtp::frame_queue::init_transaction() active_->rtp_headers = new uvg_rtp::frame::rtp_header[max_mcount_]; switch (rtp_->get_payload()) { - case RTP_FORMAT_HEVC: - active_->media_headers = new uvg_rtp::formats::hevc_headers; + case RTP_FORMAT_H265: + active_->media_headers = new uvg_rtp::formats::h265_headers; break; default: @@ -136,8 +136,8 @@ rtp_error_t uvg_rtp::frame_queue::destroy_transaction(uvg_rtp::transaction_t *t) t->rtp_headers = nullptr; switch (rtp_->get_payload()) { - case RTP_FORMAT_HEVC: - delete (uvg_rtp::formats::hevc_headers *)t->media_headers; + case RTP_FORMAT_H265: + delete (uvg_rtp::formats::h265_headers *)t->media_headers; t->media_headers = nullptr; break; @@ -192,8 +192,8 @@ rtp_error_t uvg_rtp::frame_queue::deinit_transaction(uint32_t key) if (free_.size() >= (size_t)max_queued_) { switch (rtp_->get_payload()) { - case RTP_FORMAT_HEVC: - delete (uvg_rtp::formats::hevc_headers *)transaction_it->second->media_headers; + case RTP_FORMAT_H265: + delete (uvg_rtp::formats::h265_headers *)transaction_it->second->media_headers; break; default: diff --git a/src/rtp.cc b/src/rtp.cc index fc07f40..c465002 100644 --- a/src/rtp.cc +++ b/src/rtp.cc @@ -44,7 +44,7 @@ void uvg_rtp::rtp::set_payload(rtp_format_t fmt) payload_ = fmt_ = fmt; switch (fmt_) { - case RTP_FORMAT_HEVC: + case RTP_FORMAT_H265: clock_rate_ = 90000; break;