mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-26 20:25:34 +08:00
[feat] add rtcp packet module
This commit is contained in:
44
src/common/sequence_number_compare.h
Normal file
44
src/common/sequence_number_compare.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
template <typename U>
|
||||
inline bool IsNewer(U value, U prev_value) {
|
||||
static_assert(!std::numeric_limits<U>::is_signed, "U must be unsigned");
|
||||
// kBreakpoint is the half-way mark for the type U. For instance, for a
|
||||
// uint16_t it will be 0x8000, and for a uint32_t, it will be 0x8000000.
|
||||
constexpr U kBreakpoint = (std::numeric_limits<U>::max() >> 1) + 1;
|
||||
// Distinguish between elements that are exactly kBreakpoint apart.
|
||||
// If t1>t2 and |t1-t2| = kBreakpoint: IsNewer(t1,t2)=true,
|
||||
// IsNewer(t2,t1)=false
|
||||
// rather than having IsNewer(t1,t2) = IsNewer(t2,t1) = false.
|
||||
if (value - prev_value == kBreakpoint) {
|
||||
return value > prev_value;
|
||||
}
|
||||
return value != prev_value &&
|
||||
static_cast<U>(value - prev_value) < kBreakpoint;
|
||||
}
|
||||
|
||||
// NB: Doesn't fulfill strict weak ordering requirements.
|
||||
// Mustn't be used as std::map Compare function.
|
||||
inline bool IsNewerSequenceNumber(uint16_t sequence_number,
|
||||
uint16_t prev_sequence_number) {
|
||||
return IsNewer(sequence_number, prev_sequence_number);
|
||||
}
|
||||
|
||||
// NB: Doesn't fulfill strict weak ordering requirements.
|
||||
// Mustn't be used as std::map Compare function.
|
||||
inline bool IsNewerTimestamp(uint32_t timestamp, uint32_t prev_timestamp) {
|
||||
return IsNewer(timestamp, prev_timestamp);
|
||||
}
|
||||
|
||||
inline uint16_t LatestSequenceNumber(uint16_t sequence_number1,
|
||||
uint16_t sequence_number2) {
|
||||
return IsNewerSequenceNumber(sequence_number1, sequence_number2)
|
||||
? sequence_number1
|
||||
: sequence_number2;
|
||||
}
|
||||
|
||||
inline uint32_t LatestTimestamp(uint32_t timestamp1, uint32_t timestamp2) {
|
||||
return IsNewerTimestamp(timestamp1, timestamp2) ? timestamp1 : timestamp2;
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-08-21
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SEQUENCE_NUMBER_UNWRAPPER_H_
|
||||
#define _SEQUENCE_NUMBER_UNWRAPPER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
// A sequence number unwrapper where the first unwrapped value equals the
|
||||
// first value being unwrapped.
|
||||
template <typename T, T M = 0>
|
||||
class SeqNumUnwrapper {
|
||||
static_assert(
|
||||
std::is_unsigned<T>::value &&
|
||||
std::numeric_limits<T>::max() < std::numeric_limits<int64_t>::max(),
|
||||
"Type unwrapped must be an unsigned integer smaller than int64_t.");
|
||||
|
||||
public:
|
||||
// Unwraps `value` and updates the internal state of the unwrapper.
|
||||
int64_t Unwrap(T value) {
|
||||
if (!last_value_) {
|
||||
last_unwrapped_ = {value};
|
||||
} else {
|
||||
last_unwrapped_ += Delta(*last_value_, value);
|
||||
}
|
||||
|
||||
last_value_ = value;
|
||||
return last_unwrapped_;
|
||||
}
|
||||
|
||||
// Returns the `value` without updating the internal state of the unwrapper.
|
||||
int64_t PeekUnwrap(T value) const {
|
||||
if (!last_value_) {
|
||||
return value;
|
||||
}
|
||||
return last_unwrapped_ + Delta(*last_value_, value);
|
||||
}
|
||||
|
||||
// Resets the unwrapper to its initial state. Unwrapped sequence numbers will
|
||||
// being at 0 after resetting.
|
||||
void Reset() {
|
||||
last_unwrapped_ = 0;
|
||||
last_value_.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
static int64_t Delta(T last_value, T new_value) {
|
||||
constexpr int64_t kBackwardAdjustment =
|
||||
M == 0 ? int64_t{std::numeric_limits<T>::max()} + 1 : M;
|
||||
int64_t result = ForwardDiff<T, M>(last_value, new_value);
|
||||
if (!AheadOrAt<T, M>(new_value, last_value)) {
|
||||
result -= kBackwardAdjustment;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int64_t last_unwrapped_ = 0;
|
||||
std::unique_ptr<T> last_value_;
|
||||
};
|
||||
|
||||
using RtpTimestampUnwrapper = SeqNumUnwrapper<uint32_t>;
|
||||
using RtpSeqNumUnwrapper = SeqNumUnwrapper<uint16_t>;
|
||||
|
||||
#endif
|
||||
66
src/rtcp/rtcp_packet.cpp
Normal file
66
src/rtcp/rtcp_packet.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "rtcp_packet.h"
|
||||
|
||||
#include "log.h"
|
||||
|
||||
#define IP_PACKET_SIZE 1500 // we assume ethernet
|
||||
|
||||
bool RtcpPacket::OnBufferFull(std::vector<uint8_t>& packet,
|
||||
PacketReadyCallback callback) const {
|
||||
if (packet.empty()) {
|
||||
return false;
|
||||
}
|
||||
callback(packet);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t RtcpPacket::HeaderLength() const {
|
||||
size_t length_in_bytes = BlockLength();
|
||||
|
||||
if (length_in_bytes <= 0) {
|
||||
LOG_FATAL("length_in_bytes must be a positive value");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (length_in_bytes % 4 != 0) {
|
||||
LOG_FATAL("Padding must be handled by each subclass, length_in_bytes [{}]",
|
||||
length_in_bytes);
|
||||
return -1;
|
||||
}
|
||||
// Length in 32-bit words without common header.
|
||||
return (length_in_bytes - kHeaderLength) / 4;
|
||||
}
|
||||
|
||||
// From RFC 3550, RTP: A Transport Protocol for Real-Time Applications.
|
||||
//
|
||||
// RTP header format.
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |V=2|P| RC/FMT | PT | length |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
void RtcpPacket::CreateHeader(
|
||||
size_t count_or_format, // Depends on packet type.
|
||||
uint8_t packet_type, size_t length, uint8_t* buffer, size_t* pos) {
|
||||
CreateHeader(count_or_format, packet_type, length, /*padding=*/false, buffer,
|
||||
pos);
|
||||
}
|
||||
|
||||
void RtcpPacket::CreateHeader(
|
||||
size_t count_or_format, // Depends on packet type.
|
||||
uint8_t packet_type, size_t length, bool padding, uint8_t* buffer,
|
||||
size_t* pos) {
|
||||
if (length >= 0xffffU) {
|
||||
LOG_FATAL("length must be less than 0xffffU");
|
||||
}
|
||||
if (count_or_format >= 0x1f) {
|
||||
LOG_FATAL("count_or_format must be less than 0x1f");
|
||||
}
|
||||
constexpr uint8_t kVersionBits = 2 << 6;
|
||||
uint8_t padding_bit = padding ? 1 << 5 : 0;
|
||||
buffer[*pos + 0] =
|
||||
kVersionBits | padding_bit | static_cast<uint8_t>(count_or_format);
|
||||
buffer[*pos + 1] = packet_type;
|
||||
buffer[*pos + 2] = (length >> 8) & 0xff;
|
||||
buffer[*pos + 3] = length & 0xff;
|
||||
*pos += kHeaderLength;
|
||||
}
|
||||
61
src/rtcp/rtcp_packet.h
Normal file
61
src/rtcp/rtcp_packet.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-12-11
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _RTCP_PACKET_H_
|
||||
#define _RTCP_PACKET_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
class RtcpPacket {
|
||||
public:
|
||||
// Callback used to signal that an RTCP packet is ready. Note that this may
|
||||
// not contain all data in this RtcpPacket; if a packet cannot fit in
|
||||
// max_length bytes, it will be fragmented and multiple calls to this
|
||||
// callback will be made.
|
||||
using PacketReadyCallback = std::function<void(std::vector<uint8_t> packet)>;
|
||||
|
||||
virtual ~RtcpPacket() = default;
|
||||
|
||||
void SetSenderSsrc(uint32_t ssrc) { sender_ssrc_ = ssrc; }
|
||||
uint32_t sender_ssrc() const { return sender_ssrc_; }
|
||||
|
||||
// Size of this packet in bytes (including headers).
|
||||
virtual size_t BlockLength() const = 0;
|
||||
|
||||
// Creates packet in the given buffer at the given position.
|
||||
// Calls PacketReadyCallback::OnPacketReady if remaining buffer is too small
|
||||
// and assume buffer can be reused after OnPacketReady returns.
|
||||
virtual bool Create(std::vector<uint8_t>& packet, size_t max_length,
|
||||
PacketReadyCallback callback) const = 0;
|
||||
|
||||
protected:
|
||||
// Size of the rtcp common header.
|
||||
static constexpr size_t kHeaderLength = 4;
|
||||
RtcpPacket() {}
|
||||
|
||||
static void CreateHeader(size_t count_or_format, uint8_t packet_type,
|
||||
size_t block_length, // Payload size in 32bit words.
|
||||
std::vector<uint8_t>& buffer);
|
||||
|
||||
static void CreateHeader(size_t count_or_format, uint8_t packet_type,
|
||||
size_t block_length, // Payload size in 32bit words.
|
||||
bool padding, // True if there are padding bytes.
|
||||
std::vector<uint8_t>& buffer);
|
||||
|
||||
bool OnBufferFull(std::vector<uint8_t>& packet,
|
||||
PacketReadyCallback callback) const;
|
||||
// Size of the rtcp packet as written in header.
|
||||
size_t HeaderLength() const;
|
||||
|
||||
private:
|
||||
uint32_t sender_ssrc_ = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,30 +0,0 @@
|
||||
#include "rtcp_tcc.h"
|
||||
|
||||
RtcpTcc::RtcpTcc() {
|
||||
buffer_ = new uint8_t[DEFAULT_RR_SIZE];
|
||||
size_ = DEFAULT_RR_SIZE;
|
||||
}
|
||||
|
||||
RtcpTcc::~RtcpTcc() {
|
||||
if (buffer_) {
|
||||
delete buffer_;
|
||||
buffer_ = nullptr;
|
||||
}
|
||||
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
void RtcpTcc::SetReportBlock(RtcpReportBlock &rtcp_report_block) {
|
||||
reports_.push_back(rtcp_report_block);
|
||||
}
|
||||
|
||||
void RtcpTcc::SetReportBlock(std::vector<RtcpReportBlock> &rtcp_report_blocks) {
|
||||
reports_ = rtcp_report_blocks;
|
||||
}
|
||||
|
||||
const uint8_t *RtcpTcc::Encode() {
|
||||
rtcp_header_.Encode(DEFAULT_RTCP_VERSION, 0, DEFAULT_RR_BLOCK_NUM,
|
||||
RTCP_TYPE::RR, DEFAULT_RR_SIZE, buffer_);
|
||||
|
||||
return buffer_;
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-12-06
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _RTCP_TCC_H_
|
||||
#define _RTCP_TCC_H_
|
||||
|
||||
// RR
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |V=2|P| FMT=15 | PT=205 | length |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | SSRC of packet sender |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | SSRC of media source |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | base sequence number | packet status count |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | reference time | fb pkt. count |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | packet chunk | packet chunk |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// . .
|
||||
// . .
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | packet chunk | recv delta | recv delta |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// . .
|
||||
// . .
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | recv delta | recv delta | zero padding |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
// RTP transport sequence number
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | 0xBE | 0xDE | length=1 |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ID | L=1 |transport-wide sequence number | zero padding |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "rtcp_header.h"
|
||||
#include "rtcp_typedef.h"
|
||||
|
||||
class RtcpTcc {
|
||||
public:
|
||||
RtcpTcc();
|
||||
~RtcpTcc();
|
||||
|
||||
public:
|
||||
void SetReportBlock(RtcpReportBlock &rtcp_report_block);
|
||||
void SetReportBlock(std::vector<RtcpReportBlock> &rtcp_report_blocks);
|
||||
|
||||
public:
|
||||
const uint8_t *Encode();
|
||||
size_t Decode();
|
||||
|
||||
// Entire RTP buffer
|
||||
const uint8_t *Buffer() const { return buffer_; }
|
||||
size_t Size() const { return size_; }
|
||||
|
||||
private:
|
||||
RtcpHeader rtcp_header_;
|
||||
std::vector<RtcpReportBlock> reports_;
|
||||
|
||||
// Entire RTCP buffer
|
||||
uint8_t *buffer_ = nullptr;
|
||||
size_t size_ = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
229
src/rtcp/transport_feedback.cpp
Normal file
229
src/rtcp/transport_feedback.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
#include "transport_feedback.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "sequence_number_compare.h"
|
||||
|
||||
// Header size:
|
||||
// * 4 bytes Common RTCP Packet Header
|
||||
// * 8 bytes Common Packet Format for RTCP Feedback Messages
|
||||
// * 8 bytes FeedbackPacket header
|
||||
constexpr size_t kTransportFeedbackHeaderSizeBytes = 4 + 8 + 8;
|
||||
constexpr size_t kChunkSizeBytes = 2;
|
||||
// TODO(sprang): Add support for dynamic max size for easier fragmentation,
|
||||
// eg. set it to what's left in the buffer or IP_PACKET_SIZE.
|
||||
// Size constraint imposed by RTCP common header: 16bit size field interpreted
|
||||
// as number of four byte words minus the first header word.
|
||||
constexpr size_t kMaxSizeBytes = (1 << 16) * 4;
|
||||
// Payload size:
|
||||
// * 8 bytes Common Packet Format for RTCP Feedback Messages
|
||||
// * 8 bytes FeedbackPacket header.
|
||||
// * 2 bytes for one chunk.
|
||||
constexpr size_t kMinPayloadSizeBytes = 8 + 8 + 2;
|
||||
constexpr int64_t kBaseTimeTick =
|
||||
TransportFeedback::kDeltaTick * (1 << 8); // 64ms
|
||||
constexpr int64_t kTimeWrapPeriod = kBaseTimeTick * (1 << 24); // 12.43days
|
||||
|
||||
TransportFeedback::TransportFeedback()
|
||||
: base_seq_no_(0), pkt_stat_cnt_(0), ref_time_(0), feedback_pkt_cnt_(0) {}
|
||||
|
||||
TransportFeedback::~TransportFeedback() {}
|
||||
|
||||
bool TransportFeedback::AddReceivedPacket(uint16_t sequence_number,
|
||||
int64_t timestamp) {
|
||||
int16_t delta = 0;
|
||||
// Convert to ticks and round.
|
||||
if (last_ts_ > timestamp) {
|
||||
timestamp += (last_ts_ - timestamp) + kTimeWrapPeriod;
|
||||
}
|
||||
|
||||
int64_t delta_full = timestamp - last_ts_ % kTimeWrapPeriod;
|
||||
if (delta_full > kTimeWrapPeriod / 2) {
|
||||
delta_full -= kTimeWrapPeriod;
|
||||
delta_full -= kDeltaTick / 2;
|
||||
} else {
|
||||
delta_full += kDeltaTick / 2;
|
||||
}
|
||||
delta_full /= kDeltaTick;
|
||||
|
||||
delta = static_cast<int16_t>(delta_full);
|
||||
// If larger than 16bit signed, we can't represent it - need new fb packet.
|
||||
if (delta != delta_full) {
|
||||
LOG_WARN("Delta value too large ( >= 2^16 ticks )");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t next_seq_no = base_seq_no_ + pkt_stat_cnt_;
|
||||
if (sequence_number != next_seq_no) {
|
||||
uint16_t last_seq_no = next_seq_no - 1;
|
||||
if (!IsNewerSequenceNumber(sequence_number, last_seq_no)) return false;
|
||||
uint16_t num_missing_packets = sequence_number - next_seq_no;
|
||||
if (!AddMissingPackets(num_missing_packets)) return false;
|
||||
}
|
||||
|
||||
DeltaSize delta_size = (delta >= 0 && delta <= 0xff) ? 1 : 2;
|
||||
if (!AddDeltaSize(delta_size)) return false;
|
||||
|
||||
received_packets_.emplace_back(sequence_number, delta);
|
||||
last_ts_ += delta * kDeltaTick;
|
||||
size_bytes_ += delta_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TransportFeedback::AddMissingPackets(size_t num_missing_packets) {
|
||||
size_t new_num_seq_no = pkt_stat_cnt_ + num_missing_packets;
|
||||
if (new_num_seq_no > kMaxReportedPackets) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!last_chunk_.Empty()) {
|
||||
while (num_missing_packets > 0 && last_chunk_.CanAdd(0)) {
|
||||
last_chunk_.Add(0);
|
||||
--num_missing_packets;
|
||||
}
|
||||
if (num_missing_packets == 0) {
|
||||
pkt_stat_cnt_ = new_num_seq_no;
|
||||
return true;
|
||||
}
|
||||
encoded_chunks_.push_back(last_chunk_.Emit());
|
||||
}
|
||||
|
||||
size_t full_chunks = num_missing_packets / LastChunk::kMaxRunLengthCapacity;
|
||||
size_t partial_chunk = num_missing_packets % LastChunk::kMaxRunLengthCapacity;
|
||||
size_t num_chunks = full_chunks + (partial_chunk > 0 ? 1 : 0);
|
||||
if (size_bytes_ + kChunkSizeBytes * num_chunks > kMaxSizeBytes) {
|
||||
pkt_stat_cnt_ = (new_num_seq_no - num_missing_packets);
|
||||
return false;
|
||||
}
|
||||
size_bytes_ += kChunkSizeBytes * num_chunks;
|
||||
// T = 0, S = 0, run length = kMaxRunLengthCapacity, see EncodeRunLength().
|
||||
encoded_chunks_.insert(encoded_chunks_.end(), full_chunks,
|
||||
LastChunk::kMaxRunLengthCapacity);
|
||||
last_chunk_.AddMissingPackets(partial_chunk);
|
||||
pkt_stat_cnt_ = new_num_seq_no;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TransportFeedback::AddDeltaSize(DeltaSize delta_size) {
|
||||
if (pkt_stat_cnt_ == kMaxReportedPackets) return false;
|
||||
size_t add_chunk_size = last_chunk_.Empty() ? kChunkSizeBytes : 0;
|
||||
if (size_bytes_ + delta_size + add_chunk_size > kMaxSizeBytes) return false;
|
||||
|
||||
if (last_chunk_.CanAdd(delta_size)) {
|
||||
size_bytes_ += add_chunk_size;
|
||||
last_chunk_.Add(delta_size);
|
||||
++pkt_stat_cnt_;
|
||||
return true;
|
||||
}
|
||||
if (size_bytes_ + delta_size + kChunkSizeBytes > kMaxSizeBytes) return false;
|
||||
|
||||
encoded_chunks_.push_back(last_chunk_.Emit());
|
||||
size_bytes_ += kChunkSizeBytes;
|
||||
last_chunk_.Add(delta_size);
|
||||
++pkt_stat_cnt_;
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransportFeedback::Clear() {
|
||||
pkt_stat_cnt_ = 0;
|
||||
last_ts_ = BaseTime();
|
||||
received_packets_.clear();
|
||||
all_packets_.clear();
|
||||
encoded_chunks_.clear();
|
||||
last_chunk_.Clear();
|
||||
size_bytes_ = kTransportFeedbackHeaderSizeBytes;
|
||||
}
|
||||
|
||||
// Serialize packet.
|
||||
bool TransportFeedback::Create(std::vector<uint8_t>& packet, size_t max_length,
|
||||
PacketReadyCallback callback) const {
|
||||
if (pkt_stat_cnt_ == 0) return false;
|
||||
|
||||
size_t position = packet.size();
|
||||
while (position + BlockLength() > max_length) {
|
||||
if (!OnBufferFull(packet, callback)) return false;
|
||||
}
|
||||
const size_t position_end = position + BlockLength();
|
||||
const size_t padding_length = PaddingLength();
|
||||
bool has_padding = padding_length > 0;
|
||||
CreateHeader(kFeedbackMessageType, kPacketType, HeaderLength(), has_padding,
|
||||
packet);
|
||||
CreateCommonFeedback(packet + position);
|
||||
position += kCommonFeedbackLength;
|
||||
|
||||
ByteWriter<uint16_t>::WriteBigEndian(&packet[position], base_seq_no_);
|
||||
position += 2;
|
||||
|
||||
ByteWriter<uint16_t>::WriteBigEndian(&packet[position], pkt_stat_cnt_);
|
||||
position += 2;
|
||||
|
||||
ByteWriter<uint32_t, 3>::WriteBigEndian(&packet[position], base_time_ticks_);
|
||||
position += 3;
|
||||
|
||||
packet[(position)++] = feedback_seq_;
|
||||
|
||||
for (uint16_t chunk : encoded_chunks_) {
|
||||
ByteWriter<uint16_t>::WriteBigEndian(&packet[position], chunk);
|
||||
position += 2;
|
||||
}
|
||||
if (!last_chunk_.Empty()) {
|
||||
uint16_t chunk = last_chunk_.EncodeLast();
|
||||
ByteWriter<uint16_t>::WriteBigEndian(&packet[position], chunk);
|
||||
position += 2;
|
||||
}
|
||||
|
||||
if (include_timestamps_) {
|
||||
for (const auto& received_packet : received_packets_) {
|
||||
int16_t delta = received_packet.delta_ticks();
|
||||
if (delta >= 0 && delta <= 0xFF) {
|
||||
packet[(position)++] = delta;
|
||||
} else {
|
||||
ByteWriter<int16_t>::WriteBigEndian(&packet[position], delta);
|
||||
position += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (padding_length > 0) {
|
||||
for (size_t i = 0; i < padding_length - 1; ++i) {
|
||||
packet[(position)++] = 0;
|
||||
}
|
||||
packet[(position)++] = padding_length;
|
||||
}
|
||||
RTC_DCHECK_EQ(position, position_end);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransportFeedback::SetBaseSequenceNumber(uint16_t base_sequence) {
|
||||
base_seq_no_ = base_sequence;
|
||||
}
|
||||
|
||||
void TransportFeedback::SetPacketStatusCount(uint16_t packet_status_count) {
|
||||
pkt_stat_cnt_ = packet_status_count;
|
||||
}
|
||||
|
||||
void TransportFeedback::SetReferenceTime(uint32_t reference_time) {
|
||||
ref_time_ = reference_time;
|
||||
}
|
||||
|
||||
void TransportFeedback::SetFeedbackPacketCount(uint8_t feedback_packet_count) {
|
||||
feedback_pkt_cnt_ = feedback_packet_count;
|
||||
}
|
||||
|
||||
int64_t TransportFeedback::BaseTime() const {
|
||||
// Add an extra kTimeWrapPeriod to allow add received packets arrived earlier
|
||||
// than the first added packet (and thus allow to record negative deltas)
|
||||
// even when base_time_ticks_ == 0.
|
||||
return 0 + kTimeWrapPeriod + int64_t{base_time_ticks_} * kBaseTimeTick;
|
||||
}
|
||||
|
||||
int64_t TransportFeedback::GetBaseDelta(int64_t prev_timestamp) const {
|
||||
int64_t delta = BaseTime() - prev_timestamp;
|
||||
// Compensate for wrap around.
|
||||
if (std::abs(delta - kTimeWrapPeriod) < std::abs(delta) {
|
||||
delta -= kTimeWrapPeriod; // Wrap backwards.
|
||||
} else if (std::abs(delta + kTimeWrapPeriod) < std::abs(delta)) {
|
||||
delta += kTimeWrapPeriod; // Wrap forwards.
|
||||
}
|
||||
return delta;
|
||||
}
|
||||
168
src/rtcp/transport_feedback.h
Normal file
168
src/rtcp/transport_feedback.h
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-12-11
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _TRANSPORT_FEEDBACK_H_
|
||||
#define _TRANSPORT_FEEDBACK_H_
|
||||
|
||||
// RR
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |V=2|P| FMT=15 | PT=205 | length |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | SSRC of packet sender |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | SSRC of media source |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | base sequence number | packet status count |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | reference time | fb pkt. count |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | packet chunk | packet chunk |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// . .
|
||||
// . .
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | packet chunk | recv delta | recv delta |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// . .
|
||||
// . .
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | recv delta | recv delta | zero padding |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
// RTP transport sequence number
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | 0xBE | 0xDE | length=1 |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | ID | L=1 |transport-wide sequence number | zero padding |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#include "rtcp_header.h"
|
||||
#include "rtcp_packet.h"
|
||||
#include "rtcp_typedef.h"
|
||||
|
||||
class TransportFeedback : public RtcpPacket {
|
||||
public:
|
||||
class ReceivedPacket {
|
||||
public:
|
||||
ReceivedPacket(uint16_t sequence_number, int16_t delta_ticks)
|
||||
: sequence_number_(sequence_number), delta_ticks_(delta_ticks) {}
|
||||
ReceivedPacket(const ReceivedPacket&) = default;
|
||||
ReceivedPacket& operator=(const ReceivedPacket&) = default;
|
||||
|
||||
uint16_t sequence_number() const { return sequence_number_; }
|
||||
int16_t delta_ticks() const { return delta_ticks_; }
|
||||
int64_t delta() const { return delta_ticks_ * kDeltaTick; }
|
||||
|
||||
private:
|
||||
uint16_t sequence_number_;
|
||||
int16_t delta_ticks_;
|
||||
};
|
||||
|
||||
static constexpr uint8_t kFeedbackMessageType = 15;
|
||||
static constexpr int64_t kDeltaTick = 250; // 0.25ms
|
||||
// Maximum number of packets (including missing) TransportFeedback can report.
|
||||
static constexpr size_t kMaxReportedPackets = 0xffff;
|
||||
|
||||
private:
|
||||
// Size in bytes of a delta time in rtcp packet.
|
||||
// Valid values are 0 (packet wasn't received), 1 or 2.
|
||||
using DeltaSize = uint8_t;
|
||||
// Keeps DeltaSizes that can be encoded into single chunk if it is last chunk.
|
||||
class LastChunk {
|
||||
public:
|
||||
using DeltaSize = TransportFeedback::DeltaSize;
|
||||
static constexpr size_t kMaxRunLengthCapacity = 0x1fff;
|
||||
|
||||
LastChunk();
|
||||
|
||||
bool Empty() const;
|
||||
void Clear();
|
||||
// Return if delta sizes still can be encoded into single chunk with added
|
||||
// `delta_size`.
|
||||
bool CanAdd(DeltaSize delta_size) const;
|
||||
// Add `delta_size`, assumes `CanAdd(delta_size)`,
|
||||
void Add(DeltaSize delta_size);
|
||||
// Equivalent to calling Add(0) `num_missing` times. Assumes `Empty()`.
|
||||
void AddMissingPackets(size_t num_missing);
|
||||
|
||||
// Encode chunk as large as possible removing encoded delta sizes.
|
||||
// Assume CanAdd() == false for some valid delta_size.
|
||||
uint16_t Emit();
|
||||
// Encode all stored delta_sizes into single chunk, pad with 0s if needed.
|
||||
uint16_t EncodeLast() const;
|
||||
|
||||
// Decode up to `max_size` delta sizes from `chunk`.
|
||||
void Decode(uint16_t chunk, size_t max_size);
|
||||
// Appends content of the Lastchunk to `deltas`.
|
||||
void AppendTo(std::vector<DeltaSize>* deltas) const;
|
||||
|
||||
private:
|
||||
static constexpr size_t kMaxOneBitCapacity = 14;
|
||||
static constexpr size_t kMaxTwoBitCapacity = 7;
|
||||
static constexpr size_t kMaxVectorCapacity = kMaxOneBitCapacity;
|
||||
static constexpr DeltaSize kLarge = 2;
|
||||
|
||||
uint16_t EncodeOneBit() const;
|
||||
void DecodeOneBit(uint16_t chunk, size_t max_size);
|
||||
|
||||
uint16_t EncodeTwoBit(size_t size) const;
|
||||
void DecodeTwoBit(uint16_t chunk, size_t max_size);
|
||||
|
||||
uint16_t EncodeRunLength() const;
|
||||
void DecodeRunLength(uint16_t chunk, size_t max_size);
|
||||
|
||||
std::array<DeltaSize, kMaxVectorCapacity> delta_sizes_;
|
||||
size_t size_;
|
||||
bool all_same_;
|
||||
bool has_large_delta_;
|
||||
};
|
||||
|
||||
public:
|
||||
TransportFeedback();
|
||||
~TransportFeedback();
|
||||
|
||||
public:
|
||||
bool AddReceivedPacket(uint16_t sequence_number, int64_t timestamp);
|
||||
bool AddMissingPackets(size_t num_missing_packets);
|
||||
bool AddDeltaSize(DeltaSize delta_size);
|
||||
void Clear();
|
||||
|
||||
bool Create(std::vector<uint8_t>& packet, size_t max_length,
|
||||
PacketReadyCallback callback) const override;
|
||||
|
||||
public:
|
||||
void SetBaseSequenceNumber(uint16_t base_sequence_number);
|
||||
void SetPacketStatusCount(uint16_t packet_status_count);
|
||||
void SetReferenceTime(uint32_t reference_time);
|
||||
void SetFeedbackPacketCount(uint8_t feedback_packet_count);
|
||||
|
||||
int64_t BaseTime() const;
|
||||
int64_t GetBaseDelta(int64_t prev_timestamp) const;
|
||||
|
||||
private:
|
||||
uint16_t base_seq_no_;
|
||||
uint16_t pkt_stat_cnt_;
|
||||
uint32_t ref_time_;
|
||||
uint8_t feedback_pkt_cnt_;
|
||||
|
||||
int64_t last_ts_;
|
||||
|
||||
std::vector<ReceivedPacket> received_packets_;
|
||||
std::vector<ReceivedPacket> all_packets_;
|
||||
// All but last encoded packet chunks.
|
||||
std::vector<uint16_t> encoded_chunks_;
|
||||
LastChunk last_chunk_;
|
||||
size_t size_bytes_;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user