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:
parent
7d683b1c17
commit
d60b240341
|
@ -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]);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
};
|
||||
};
|
|
@ -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);
|
||||
};
|
||||
};
|
||||
};
|
|
@ -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_;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
}
|
|
@ -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(
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
14
src/queue.cc
14
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:
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue