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
This commit is contained in:
Aaro Altonen 2020-09-08 08:19:38 +03:00
parent 7d683b1c17
commit d60b240341
23 changed files with 301 additions and 218 deletions

View File

@ -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<uint8_t[]> buffer = std::unique_ptr<uint8_t[]>(new uint8_t[PAYLOAD_MAXLEN]);

View File

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

View File

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

View File

@ -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<uint8_t[]> buffer = std::unique_ptr<uint8_t[]>(new uint8_t[PAYLOAD_MAXLEN]);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<uint8_t[]>(new uint8_t[PAYLOAD_MAXLEN]);

View File

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

43
include/formats/h26x.hh Normal file
View File

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

View File

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

View File

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

View File

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

165
src/formats/h265.cc Normal file
View File

@ -0,0 +1,165 @@
#ifdef _WIN32
#else
#include <sys/socket.h>
#endif
#include <cstdint>
#include <cstring>
#include <iostream>
#include <unordered_map>
#include <queue>
#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()
{
}

View File

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

View File

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

View File

@ -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<uint8_t[]> data, size_t data_len, int flags)
@ -33,10 +33,10 @@ rtp_error_t uvg_rtp::formats::media::push_frame(std::unique_ptr<uint8_t[]> 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;

View File

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

View File

@ -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<uvg_rtp::formats::hevc *>(media_)->packet_handler
dynamic_cast<uvg_rtp::formats::h265 *>(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<uvg_rtp::formats::hevc *>(media_)->packet_handler
dynamic_cast<uvg_rtp::formats::h265 *>(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<uvg_rtp::formats::hevc *>(media_)->packet_handler
dynamic_cast<uvg_rtp::formats::h265 *>(media_)->packet_handler
);
break;

View File

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

View File

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