uvgrtp-base/src/formats/h265.cc

208 lines
6.4 KiB
C++

#include "formats/h265.hh"
#include "debug.hh"
#include "queue.hh"
#include <cstdint>
#include <cstring>
#include <iostream>
#include <unordered_map>
#include <queue>
#ifndef _WIN32
#include <sys/socket.h>
#endif
void uvgrtp::formats::h265::clear_aggregation_info()
{
aggr_pkt_info_.nalus.clear();
aggr_pkt_info_.aggr_pkt.clear();
}
rtp_error_t uvgrtp::formats::h265::make_aggregation_pkt()
{
rtp_error_t ret;
if (aggr_pkt_info_.nalus.empty())
return RTP_INVALID_VALUE;
/* Only one buffer in the vector -> no need to create an aggregation packet */
if (aggr_pkt_info_.nalus.size() == 1) {
if ((ret = fqueue_->enqueue_message(aggr_pkt_info_.nalus)) != RTP_OK) {
LOG_ERROR("Failed to enqueue Single NAL Unit packet!");
return ret;
}
return fqueue_->flush_queue();
}
/* create header for the packet and craft the aggregation packet
* according to the format defined in RFC 7798 */
aggr_pkt_info_.nal_header[0] = H265_PKT_AGGR << 1;
aggr_pkt_info_.nal_header[1] = 1;
aggr_pkt_info_.aggr_pkt.push_back(
std::make_pair(
uvgrtp::frame::HEADER_SIZE_H265_NAL,
aggr_pkt_info_.nal_header
)
);
for (size_t i = 0; i < aggr_pkt_info_.nalus.size(); ++i) {
auto pkt_size = aggr_pkt_info_.nalus[i].first;
aggr_pkt_info_.nalus[i].first = htons(aggr_pkt_info_.nalus[i].first);
aggr_pkt_info_.aggr_pkt.push_back(
std::make_pair(
sizeof(uint16_t),
(uint8_t *)&aggr_pkt_info_.nalus[i].first
)
);
aggr_pkt_info_.aggr_pkt.push_back(
std::make_pair(
pkt_size,
aggr_pkt_info_.nalus[i].second
)
);
}
if ((ret = fqueue_->enqueue_message(aggr_pkt_info_.aggr_pkt)) != RTP_OK) {
LOG_ERROR("Failed to enqueue buffers of an aggregation packet!");
return ret;
}
return ret;
}
rtp_error_t uvgrtp::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();
if (data_len - 3 <= payload_size) {
/* If there is more data coming in (possibly another small packet)
* create entry to "aggr_pkt_info_" to construct an aggregation packet */
if (more) {
aggr_pkt_info_.nalus.push_back(std::make_pair(data_len, data));
return RTP_NOT_READY;
} else {
if (aggr_pkt_info_.nalus.empty()) {
if ((ret = fqueue_->enqueue_message(data, data_len)) != RTP_OK) {
LOG_ERROR("Failed to enqueue Single NAL Unit packet!");
return ret;
}
if (more)
return RTP_NOT_READY;
return fqueue_->flush_queue();
} else {
(void)make_aggregation_pkt();
ret = fqueue_->flush_queue();
clear_aggregation_info();
return ret;
}
}
} else {
/* If smaller NALUs were queued before this NALU,
* send them in an aggregation packet before proceeding with fragmentation */
(void)make_aggregation_pkt();
}
/* 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 = (uvgrtp::formats::h265_headers *)fqueue_->get_media_headers();
headers->nal_header[0] = H265_PKT_FRAG << 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 = uvgrtp::frame::HEADER_SIZE_H265_NAL;
data_left -= uvgrtp::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!");
clear_aggregation_info();
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!");
clear_aggregation_info();
fqueue_->deinit_transaction();
return ret;
}
if (more)
return RTP_NOT_READY;
clear_aggregation_info();
return fqueue_->flush_queue();
}
uvgrtp::formats::h265::h265(uvgrtp::socket *socket, uvgrtp::rtp *rtp, int flags):
h26x(socket, rtp, flags), finfo_{}
{
finfo_.rtp_ctx = rtp;
}
uvgrtp::formats::h265::~h265()
{
}
uvgrtp::formats::h265_frame_info_t *uvgrtp::formats::h265::get_h265_frame_info()
{
return &finfo_;
}
rtp_error_t uvgrtp::formats::h265::frame_getter(void *arg, uvgrtp::frame::rtp_frame **frame)
{
auto finfo = (uvgrtp::formats::h265_frame_info_t *)arg;
if (finfo->queued.size()) {
*frame = finfo->queued.front();
finfo->queued.pop_front();
return RTP_PKT_READY;
}
return RTP_NOT_FOUND;
}