mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-26 20:25:34 +08:00
[feat] add rtp packet history module
This commit is contained in:
@@ -47,8 +47,8 @@ void AudioChannelSend::Destroy() {
|
||||
|
||||
int AudioChannelSend::SendAudio(char *data, size_t size) {
|
||||
if (rtp_audio_sender_ && rtp_packetizer_) {
|
||||
std::vector<RtpPacket> rtp_packets =
|
||||
rtp_packetizer_->Build((uint8_t *)data, (uint32_t)size);
|
||||
std::vector<std::shared_ptr<RtpPacket>> rtp_packets =
|
||||
rtp_packetizer_->Build((uint8_t *)data, (uint32_t)size, true);
|
||||
rtp_audio_sender_->Enqueue(rtp_packets);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,8 +47,8 @@ void DataChannelSend::Destroy() {
|
||||
|
||||
int DataChannelSend::SendData(const char *data, size_t size) {
|
||||
if (rtp_data_sender_ && rtp_packetizer_) {
|
||||
std::vector<RtpPacket> rtp_packets =
|
||||
rtp_packetizer_->Build((uint8_t *)data, (uint32_t)size);
|
||||
std::vector<std::shared_ptr<RtpPacket>> rtp_packets =
|
||||
rtp_packetizer_->Build((uint8_t *)data, (uint32_t)size, true);
|
||||
rtp_data_sender_->Enqueue(rtp_packets);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,8 @@ RtpAudioSender::~RtpAudioSender() {
|
||||
SSRCManager::Instance().DeleteSsrc(ssrc_);
|
||||
}
|
||||
|
||||
void RtpAudioSender::Enqueue(std::vector<RtpPacket>& rtp_packets) {
|
||||
void RtpAudioSender::Enqueue(
|
||||
std::vector<std::shared_ptr<RtpPacket>> rtp_packets) {
|
||||
if (!rtp_statistics_) {
|
||||
rtp_statistics_ = std::make_unique<RtpStatistics>();
|
||||
rtp_statistics_->Start();
|
||||
@@ -38,20 +39,20 @@ void RtpAudioSender::SetSendDataFunc(
|
||||
data_send_func_ = data_send_func;
|
||||
}
|
||||
|
||||
int RtpAudioSender::SendRtpPacket(RtpPacket& rtp_packet) {
|
||||
int RtpAudioSender::SendRtpPacket(std::shared_ptr<RtpPacket> rtp_packet) {
|
||||
if (!data_send_func_) {
|
||||
LOG_ERROR("data_send_func_ is nullptr");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (0 != data_send_func_((const char*)rtp_packet.Buffer().data(),
|
||||
rtp_packet.Size())) {
|
||||
if (0 != data_send_func_((const char*)rtp_packet->Buffer().data(),
|
||||
rtp_packet->Size())) {
|
||||
LOG_ERROR("Send rtp packet failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
last_send_bytes_ += (uint32_t)rtp_packet.Size();
|
||||
total_rtp_payload_sent_ += (uint32_t)rtp_packet.PayloadSize();
|
||||
last_send_bytes_ += (uint32_t)rtp_packet->Size();
|
||||
total_rtp_payload_sent_ += (uint32_t)rtp_packet->PayloadSize();
|
||||
total_rtp_packets_sent_++;
|
||||
|
||||
if (io_statistics_) {
|
||||
@@ -136,7 +137,7 @@ bool RtpAudioSender::Process() {
|
||||
|
||||
for (size_t i = 0; i < 10; i++)
|
||||
if (!rtp_packe_queue_.isEmpty()) {
|
||||
RtpPacket rtp_packet;
|
||||
std::shared_ptr<RtpPacket> rtp_packet;
|
||||
rtp_packe_queue_.pop(rtp_packet);
|
||||
SendRtpPacket(rtp_packet);
|
||||
}
|
||||
|
||||
@@ -23,12 +23,12 @@ class RtpAudioSender : public ThreadBase {
|
||||
virtual ~RtpAudioSender();
|
||||
|
||||
public:
|
||||
void Enqueue(std::vector<RtpPacket> &rtp_packets);
|
||||
void Enqueue(std::vector<std::shared_ptr<RtpPacket>> rtp_packets);
|
||||
void SetSendDataFunc(std::function<int(const char *, size_t)> data_send_func);
|
||||
uint32_t GetSsrc() { return ssrc_; }
|
||||
|
||||
private:
|
||||
int SendRtpPacket(RtpPacket &rtp_packet);
|
||||
int SendRtpPacket(std::shared_ptr<RtpPacket> rtp_packet);
|
||||
int SendRtcpSR(RtcpSenderReport &rtcp_sr);
|
||||
|
||||
bool CheckIsTimeSendSR();
|
||||
@@ -38,7 +38,7 @@ class RtpAudioSender : public ThreadBase {
|
||||
|
||||
private:
|
||||
std::function<int(const char *, size_t)> data_send_func_ = nullptr;
|
||||
RingBuffer<RtpPacket> rtp_packe_queue_;
|
||||
RingBuffer<std::shared_ptr<RtpPacket>> rtp_packe_queue_;
|
||||
|
||||
private:
|
||||
uint32_t ssrc_ = 0;
|
||||
|
||||
@@ -22,7 +22,8 @@ RtpDataSender::~RtpDataSender() {
|
||||
SSRCManager::Instance().DeleteSsrc(ssrc_);
|
||||
}
|
||||
|
||||
void RtpDataSender::Enqueue(std::vector<RtpPacket>& rtp_packets) {
|
||||
void RtpDataSender::Enqueue(
|
||||
std::vector<std::shared_ptr<RtpPacket>> rtp_packets) {
|
||||
if (!rtp_statistics_) {
|
||||
rtp_statistics_ = std::make_unique<RtpStatistics>();
|
||||
rtp_statistics_->Start();
|
||||
@@ -38,20 +39,20 @@ void RtpDataSender::SetSendDataFunc(
|
||||
data_send_func_ = data_send_func;
|
||||
}
|
||||
|
||||
int RtpDataSender::SendRtpPacket(RtpPacket& rtp_packet) {
|
||||
int RtpDataSender::SendRtpPacket(std::shared_ptr<RtpPacket> rtp_packet) {
|
||||
if (!data_send_func_) {
|
||||
LOG_ERROR("data_send_func_ is nullptr");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (0 != data_send_func_((const char*)rtp_packet.Buffer().data(),
|
||||
rtp_packet.Size())) {
|
||||
if (0 != data_send_func_((const char*)rtp_packet->Buffer().data(),
|
||||
rtp_packet->Size())) {
|
||||
LOG_ERROR("Send rtp packet failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
last_send_bytes_ += (uint32_t)rtp_packet.Size();
|
||||
total_rtp_payload_sent_ += (uint32_t)rtp_packet.PayloadSize();
|
||||
last_send_bytes_ += (uint32_t)rtp_packet->Size();
|
||||
total_rtp_payload_sent_ += (uint32_t)rtp_packet->PayloadSize();
|
||||
total_rtp_packets_sent_++;
|
||||
|
||||
if (io_statistics_) {
|
||||
@@ -136,7 +137,7 @@ bool RtpDataSender::Process() {
|
||||
|
||||
for (size_t i = 0; i < 10; i++)
|
||||
if (!rtp_packe_queue_.isEmpty()) {
|
||||
RtpPacket rtp_packet;
|
||||
std::shared_ptr<RtpPacket> rtp_packet;
|
||||
rtp_packe_queue_.pop(rtp_packet);
|
||||
SendRtpPacket(rtp_packet);
|
||||
}
|
||||
|
||||
@@ -23,13 +23,13 @@ class RtpDataSender : public ThreadBase {
|
||||
virtual ~RtpDataSender();
|
||||
|
||||
public:
|
||||
void Enqueue(std::vector<RtpPacket> &rtp_packets);
|
||||
void Enqueue(std::vector<std::shared_ptr<RtpPacket>> rtp_packets);
|
||||
void SetSendDataFunc(std::function<int(const char *, size_t)> data_send_func);
|
||||
uint32_t GetSsrc() { return ssrc_; }
|
||||
|
||||
private:
|
||||
private:
|
||||
int SendRtpPacket(RtpPacket &rtp_packet);
|
||||
int SendRtpPacket(std::shared_ptr<RtpPacket> rtp_packet);
|
||||
int SendRtcpSR(RtcpSenderReport &rtcp_sr);
|
||||
|
||||
bool CheckIsTimeSendSR();
|
||||
@@ -39,7 +39,7 @@ class RtpDataSender : public ThreadBase {
|
||||
|
||||
private:
|
||||
std::function<int(const char *, size_t)> data_send_func_ = nullptr;
|
||||
RingBuffer<RtpPacket> rtp_packe_queue_;
|
||||
RingBuffer<std::shared_ptr<RtpPacket>> rtp_packe_queue_;
|
||||
|
||||
private:
|
||||
uint32_t ssrc_ = 0;
|
||||
|
||||
@@ -38,14 +38,15 @@ RtpVideoSender::~RtpVideoSender() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void RtpVideoSender::Enqueue(std::vector<RtpPacket>& rtp_packets) {
|
||||
void RtpVideoSender::Enqueue(
|
||||
std::vector<std::shared_ptr<RtpPacket>>& rtp_packets) {
|
||||
if (!rtp_statistics_) {
|
||||
rtp_statistics_ = std::make_unique<RtpStatistics>();
|
||||
rtp_statistics_->Start();
|
||||
}
|
||||
|
||||
for (auto& rtp_packet : rtp_packets) {
|
||||
rtp_packe_queue_.push(rtp_packet);
|
||||
rtp_packe_queue_.push(std::move(rtp_packet));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,35 +60,33 @@ void RtpVideoSender::SetOnSentPacketFunc(
|
||||
on_sent_packet_func_ = on_sent_packet_func;
|
||||
}
|
||||
|
||||
int RtpVideoSender::SendRtpPacket(RtpPacket& rtp_packet) {
|
||||
int RtpVideoSender::SendRtpPacket(std::shared_ptr<RtpPacket> rtp_packet) {
|
||||
if (!data_send_func_) {
|
||||
LOG_ERROR("data_send_func_ is nullptr");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (on_sent_packet_func_) {
|
||||
webrtc::RtpPacketToSend rtp_packet_to_send;
|
||||
rtp_packet_to_send.SetSequenceNumber(rtp_packet.SequenceNumber());
|
||||
rtp_packet_to_send.SetSsrc(rtp_packet.Ssrc());
|
||||
rtp_packet_to_send.SetSize(rtp_packet.Size());
|
||||
rtp_packet_to_send.set_transport_sequence_number(transport_seq_++);
|
||||
rtp_packet_to_send.set_packet_type(webrtc::RtpPacketMediaType::kVideo);
|
||||
on_sent_packet_func_(rtp_packet_to_send);
|
||||
webrtc::RtpPacketToSend* rtp_packet_to_send =
|
||||
dynamic_cast<webrtc::RtpPacketToSend*>(rtp_packet.get());
|
||||
rtp_packet_to_send->set_transport_sequence_number(transport_seq_++);
|
||||
rtp_packet_to_send->set_packet_type(webrtc::RtpPacketMediaType::kVideo);
|
||||
on_sent_packet_func_(*rtp_packet_to_send);
|
||||
}
|
||||
|
||||
if (0 != data_send_func_((const char*)rtp_packet.Buffer().data(),
|
||||
rtp_packet.Size())) {
|
||||
if (0 != data_send_func_((const char*)rtp_packet->Buffer().data(),
|
||||
rtp_packet->Size())) {
|
||||
// LOG_ERROR("Send rtp packet failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef SAVE_RTP_SENT_STREAM
|
||||
fwrite((unsigned char*)rtp_packet.Payload(), 1, rtp_packet.PayloadSize(),
|
||||
fwrite((unsigned char*)rtp_packet->Payload(), 1, rtp_packet->PayloadSize(),
|
||||
file_rtp_sent_);
|
||||
#endif
|
||||
|
||||
last_send_bytes_ += (uint32_t)rtp_packet.Size();
|
||||
total_rtp_payload_sent_ += (uint32_t)rtp_packet.PayloadSize();
|
||||
last_send_bytes_ += (uint32_t)rtp_packet->Size();
|
||||
total_rtp_payload_sent_ += (uint32_t)rtp_packet->PayloadSize();
|
||||
total_rtp_packets_sent_++;
|
||||
|
||||
if (io_statistics_) {
|
||||
@@ -131,7 +130,7 @@ int RtpVideoSender::SendRtpPacket(RtpPacket& rtp_packet) {
|
||||
|
||||
rtcp_sr.Encode();
|
||||
|
||||
// SendRtcpSR(rtcp_sr);
|
||||
SendRtcpSR(rtcp_sr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -168,14 +167,17 @@ bool RtpVideoSender::CheckIsTimeSendSR() {
|
||||
}
|
||||
|
||||
bool RtpVideoSender::Process() {
|
||||
bool pop_success = false;
|
||||
last_send_bytes_ = 0;
|
||||
|
||||
for (size_t i = 0; i < 10; i++)
|
||||
if (!rtp_packe_queue_.isEmpty()) {
|
||||
RtpPacket rtp_packet;
|
||||
rtp_packe_queue_.pop(rtp_packet);
|
||||
std::shared_ptr<RtpPacket> rtp_packet;
|
||||
pop_success = rtp_packe_queue_.pop(rtp_packet);
|
||||
if (pop_success) {
|
||||
SendRtpPacket(rtp_packet);
|
||||
}
|
||||
}
|
||||
|
||||
if (rtp_statistics_) {
|
||||
rtp_statistics_->UpdateSentBytes(last_send_bytes_);
|
||||
|
||||
@@ -18,14 +18,14 @@ class RtpVideoSender : public ThreadBase {
|
||||
virtual ~RtpVideoSender();
|
||||
|
||||
public:
|
||||
void Enqueue(std::vector<RtpPacket> &rtp_packets);
|
||||
void Enqueue(std::vector<std::shared_ptr<RtpPacket>> &rtp_packets);
|
||||
void SetSendDataFunc(std::function<int(const char *, size_t)> data_send_func);
|
||||
void SetOnSentPacketFunc(
|
||||
std::function<void(const webrtc::RtpPacketToSend &)> on_sent_packet_func);
|
||||
uint32_t GetSsrc() { return ssrc_; }
|
||||
|
||||
private:
|
||||
int SendRtpPacket(RtpPacket &rtp_packet);
|
||||
int SendRtpPacket(std::shared_ptr<RtpPacket> rtp_packet);
|
||||
int SendRtcpSR(RtcpSenderReport &rtcp_sr);
|
||||
|
||||
bool CheckIsTimeSendSR();
|
||||
@@ -37,7 +37,7 @@ class RtpVideoSender : public ThreadBase {
|
||||
std::function<int(const char *, size_t)> data_send_func_ = nullptr;
|
||||
std::function<void(const webrtc::RtpPacketToSend &)> on_sent_packet_func_ =
|
||||
nullptr;
|
||||
RingBuffer<RtpPacket> rtp_packe_queue_;
|
||||
RingBuffer<std::shared_ptr<RtpPacket>> rtp_packe_queue_;
|
||||
|
||||
private:
|
||||
uint32_t ssrc_ = 0;
|
||||
|
||||
@@ -58,8 +58,8 @@ void VideoChannelSend::Destroy() {
|
||||
|
||||
int VideoChannelSend::SendVideo(char* data, size_t size) {
|
||||
if (rtp_video_sender_ && rtp_packetizer_) {
|
||||
std::vector<RtpPacket> rtp_packets =
|
||||
rtp_packetizer_->Build((uint8_t*)data, (uint32_t)size);
|
||||
std::vector<std::shared_ptr<RtpPacket>> rtp_packets =
|
||||
rtp_packetizer_->Build((uint8_t*)data, (uint32_t)size, true);
|
||||
rtp_video_sender_->Enqueue(rtp_packets);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "ice_agent.h"
|
||||
#include "rtp_packetizer.h"
|
||||
#include "rtp_video_sender.h"
|
||||
#include "transport_feedback.h"
|
||||
#include "transport_feedback_adapter.h"
|
||||
|
||||
class VideoChannelSend {
|
||||
@@ -38,9 +37,6 @@ class VideoChannelSend {
|
||||
Timestamp recv_ts,
|
||||
const webrtc::rtcp::CongestionControlFeedback& feedback);
|
||||
|
||||
void HandleTransportPacketsFeedback(
|
||||
const webrtc::TransportPacketsFeedback& feedback);
|
||||
|
||||
private:
|
||||
void PostUpdates(webrtc::NetworkControlUpdate update);
|
||||
void UpdateControlState();
|
||||
|
||||
129
src/common/api/rtp_rtcp/rtp_rtcp_typedef.h
Normal file
129
src/common/api/rtp_rtcp/rtp_rtcp_typedef.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef _RTP_RTCP_TYPEDEF_H_
|
||||
#define _RTP_RTCP_TYPEDEF_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "api/transport/network_types.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
|
||||
#define RTCP_CNAME_SIZE 256 // RFC 3550 page 44, including null termination
|
||||
#define IP_PACKET_SIZE 1500 // we assume ethernet
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
const int kVideoPayloadTypeFrequency = 90000;
|
||||
|
||||
// TODO(bugs.webrtc.org/6458): Remove this when all the depending projects are
|
||||
// updated to correctly set rtp rate for RtcpSender.
|
||||
const int kBogusRtpRateForAudioRtcp = 8000;
|
||||
|
||||
// Minimum RTP header size in bytes.
|
||||
const uint8_t kRtpHeaderSize = 12;
|
||||
|
||||
// This enum must not have any gaps, i.e., all integers between
|
||||
// kRtpExtensionNone and kRtpExtensionNumberOfExtensions must be valid enum
|
||||
// entries.
|
||||
enum RTPExtensionType : int {
|
||||
kRtpExtensionNone,
|
||||
kRtpExtensionTransmissionTimeOffset,
|
||||
kRtpExtensionAudioLevel,
|
||||
kRtpExtensionCsrcAudioLevel,
|
||||
kRtpExtensionInbandComfortNoise,
|
||||
kRtpExtensionAbsoluteSendTime,
|
||||
kRtpExtensionAbsoluteCaptureTime,
|
||||
kRtpExtensionVideoRotation,
|
||||
kRtpExtensionTransportSequenceNumber,
|
||||
kRtpExtensionTransportSequenceNumber02,
|
||||
kRtpExtensionPlayoutDelay,
|
||||
kRtpExtensionVideoContentType,
|
||||
kRtpExtensionVideoLayersAllocation,
|
||||
kRtpExtensionVideoTiming,
|
||||
kRtpExtensionRtpStreamId,
|
||||
kRtpExtensionRepairedRtpStreamId,
|
||||
kRtpExtensionMid,
|
||||
kRtpExtensionGenericFrameDescriptor,
|
||||
kRtpExtensionGenericFrameDescriptor00 [[deprecated]] =
|
||||
kRtpExtensionGenericFrameDescriptor,
|
||||
kRtpExtensionDependencyDescriptor,
|
||||
kRtpExtensionGenericFrameDescriptor02 [[deprecated]] =
|
||||
kRtpExtensionDependencyDescriptor,
|
||||
kRtpExtensionColorSpace,
|
||||
kRtpExtensionVideoFrameTrackingId,
|
||||
kRtpExtensionCorruptionDetection,
|
||||
kRtpExtensionNumberOfExtensions // Must be the last entity in the enum.
|
||||
};
|
||||
|
||||
enum RTCPAppSubTypes { kAppSubtypeBwe = 0x00 };
|
||||
|
||||
// TODO(sprang): Make this an enum class once rtcp_receiver has been cleaned up.
|
||||
enum RTCPPacketType : uint32_t {
|
||||
kRtcpReport = 0x0001,
|
||||
kRtcpSr = 0x0002,
|
||||
kRtcpRr = 0x0004,
|
||||
kRtcpSdes = 0x0008,
|
||||
kRtcpBye = 0x0010,
|
||||
kRtcpPli = 0x0020,
|
||||
kRtcpNack = 0x0040,
|
||||
kRtcpFir = 0x0080,
|
||||
kRtcpTmmbr = 0x0100,
|
||||
kRtcpTmmbn = 0x0200,
|
||||
kRtcpSrReq = 0x0400,
|
||||
kRtcpLossNotification = 0x2000,
|
||||
kRtcpRemb = 0x10000,
|
||||
kRtcpTransmissionTimeOffset = 0x20000,
|
||||
kRtcpXrReceiverReferenceTime = 0x40000,
|
||||
kRtcpXrDlrrReportBlock = 0x80000,
|
||||
kRtcpTransportFeedback = 0x100000,
|
||||
kRtcpXrTargetBitrate = 0x200000,
|
||||
};
|
||||
|
||||
enum class KeyFrameReqMethod : uint8_t {
|
||||
kNone, // Don't request keyframes.
|
||||
kPliRtcp, // Request keyframes through Picture Loss Indication.
|
||||
kFirRtcp // Request keyframes through Full Intra-frame Request.
|
||||
};
|
||||
|
||||
enum RtxMode {
|
||||
kRtxOff = 0x0,
|
||||
kRtxRetransmitted = 0x1, // Only send retransmissions over RTX.
|
||||
kRtxRedundantPayloads = 0x2 // Preventively send redundant payloads
|
||||
// instead of padding.
|
||||
};
|
||||
|
||||
const size_t kRtxHeaderSize = 2;
|
||||
|
||||
// NOTE! `kNumMediaTypes` must be kept in sync with RtpPacketMediaType!
|
||||
static constexpr size_t kNumMediaTypes = 5;
|
||||
enum class RtpPacketMediaType : size_t {
|
||||
kAudio, // Audio media packets.
|
||||
kVideo, // Video media packets.
|
||||
kRetransmission, // Retransmisions, sent as response to NACK.
|
||||
kForwardErrorCorrection, // FEC packets.
|
||||
kPadding = kNumMediaTypes - 1, // RTX or plain padding sent to maintain BWE.
|
||||
// Again, don't forget to update `kNumMediaTypes` if you add another value!
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
#endif // _RTP_RTCP_TYPEDEF_H_
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "api/rtp_rtcp/rtp_rtcp_typedef.h"
|
||||
#include "api/transport/network_types.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "api/units/time_delta.h"
|
||||
@@ -30,97 +31,10 @@
|
||||
#include "congestion_control_feedback.h"
|
||||
#include "report_block_data.h"
|
||||
|
||||
#define RTCP_CNAME_SIZE 256 // RFC 3550 page 44, including null termination
|
||||
#define IP_PACKET_SIZE 1500 // we assume ethernet
|
||||
|
||||
class RtpPacket;
|
||||
|
||||
namespace webrtc {
|
||||
class RtpPacketToSend;
|
||||
namespace rtcp {
|
||||
class TransportFeedback;
|
||||
}
|
||||
|
||||
const int kVideoPayloadTypeFrequency = 90000;
|
||||
|
||||
// TODO(bugs.webrtc.org/6458): Remove this when all the depending projects are
|
||||
// updated to correctly set rtp rate for RtcpSender.
|
||||
const int kBogusRtpRateForAudioRtcp = 8000;
|
||||
|
||||
// Minimum RTP header size in bytes.
|
||||
const uint8_t kRtpHeaderSize = 12;
|
||||
|
||||
// This enum must not have any gaps, i.e., all integers between
|
||||
// kRtpExtensionNone and kRtpExtensionNumberOfExtensions must be valid enum
|
||||
// entries.
|
||||
enum RTPExtensionType : int {
|
||||
kRtpExtensionNone,
|
||||
kRtpExtensionTransmissionTimeOffset,
|
||||
kRtpExtensionAudioLevel,
|
||||
kRtpExtensionCsrcAudioLevel,
|
||||
kRtpExtensionInbandComfortNoise,
|
||||
kRtpExtensionAbsoluteSendTime,
|
||||
kRtpExtensionAbsoluteCaptureTime,
|
||||
kRtpExtensionVideoRotation,
|
||||
kRtpExtensionTransportSequenceNumber,
|
||||
kRtpExtensionTransportSequenceNumber02,
|
||||
kRtpExtensionPlayoutDelay,
|
||||
kRtpExtensionVideoContentType,
|
||||
kRtpExtensionVideoLayersAllocation,
|
||||
kRtpExtensionVideoTiming,
|
||||
kRtpExtensionRtpStreamId,
|
||||
kRtpExtensionRepairedRtpStreamId,
|
||||
kRtpExtensionMid,
|
||||
kRtpExtensionGenericFrameDescriptor,
|
||||
kRtpExtensionGenericFrameDescriptor00 [[deprecated]] =
|
||||
kRtpExtensionGenericFrameDescriptor,
|
||||
kRtpExtensionDependencyDescriptor,
|
||||
kRtpExtensionGenericFrameDescriptor02 [[deprecated]] =
|
||||
kRtpExtensionDependencyDescriptor,
|
||||
kRtpExtensionColorSpace,
|
||||
kRtpExtensionVideoFrameTrackingId,
|
||||
kRtpExtensionCorruptionDetection,
|
||||
kRtpExtensionNumberOfExtensions // Must be the last entity in the enum.
|
||||
};
|
||||
|
||||
enum RTCPAppSubTypes { kAppSubtypeBwe = 0x00 };
|
||||
|
||||
// TODO(sprang): Make this an enum class once rtcp_receiver has been cleaned up.
|
||||
enum RTCPPacketType : uint32_t {
|
||||
kRtcpReport = 0x0001,
|
||||
kRtcpSr = 0x0002,
|
||||
kRtcpRr = 0x0004,
|
||||
kRtcpSdes = 0x0008,
|
||||
kRtcpBye = 0x0010,
|
||||
kRtcpPli = 0x0020,
|
||||
kRtcpNack = 0x0040,
|
||||
kRtcpFir = 0x0080,
|
||||
kRtcpTmmbr = 0x0100,
|
||||
kRtcpTmmbn = 0x0200,
|
||||
kRtcpSrReq = 0x0400,
|
||||
kRtcpLossNotification = 0x2000,
|
||||
kRtcpRemb = 0x10000,
|
||||
kRtcpTransmissionTimeOffset = 0x20000,
|
||||
kRtcpXrReceiverReferenceTime = 0x40000,
|
||||
kRtcpXrDlrrReportBlock = 0x80000,
|
||||
kRtcpTransportFeedback = 0x100000,
|
||||
kRtcpXrTargetBitrate = 0x200000,
|
||||
};
|
||||
|
||||
enum class KeyFrameReqMethod : uint8_t {
|
||||
kNone, // Don't request keyframes.
|
||||
kPliRtcp, // Request keyframes through Picture Loss Indication.
|
||||
kFirRtcp // Request keyframes through Full Intra-frame Request.
|
||||
};
|
||||
|
||||
enum RtxMode {
|
||||
kRtxOff = 0x0,
|
||||
kRtxRetransmitted = 0x1, // Only send retransmissions over RTX.
|
||||
kRtxRedundantPayloads = 0x2 // Preventively send redundant payloads
|
||||
// instead of padding.
|
||||
};
|
||||
|
||||
const size_t kRtxHeaderSize = 2;
|
||||
|
||||
struct RtpState {
|
||||
uint16_t sequence_number = 0;
|
||||
@@ -159,9 +73,6 @@ class NetworkLinkRtcpObserver {
|
||||
public:
|
||||
virtual ~NetworkLinkRtcpObserver() = default;
|
||||
|
||||
virtual void OnTransportFeedback(
|
||||
Timestamp /* receive_time */,
|
||||
const rtcp::TransportFeedback& /* feedback */) {}
|
||||
// RFC 8888 congestion control feedback.
|
||||
virtual void OnCongestionControlFeedback(
|
||||
Timestamp /* receive_time */,
|
||||
@@ -177,17 +88,6 @@ class NetworkLinkRtcpObserver {
|
||||
virtual void OnRttUpdate(Timestamp /* receive_time */, TimeDelta /* rtt */) {}
|
||||
};
|
||||
|
||||
// NOTE! `kNumMediaTypes` must be kept in sync with RtpPacketMediaType!
|
||||
static constexpr size_t kNumMediaTypes = 5;
|
||||
enum class RtpPacketMediaType : size_t {
|
||||
kAudio, // Audio media packets.
|
||||
kVideo, // Video media packets.
|
||||
kRetransmission, // Retransmisions, sent as response to NACK.
|
||||
kForwardErrorCorrection, // FEC packets.
|
||||
kPadding = kNumMediaTypes - 1, // RTX or plain padding sent to maintain BWE.
|
||||
// Again, don't forget to update `kNumMediaTypes` if you add another value!
|
||||
};
|
||||
|
||||
struct RtpPacketSendInfo {
|
||||
static RtpPacketSendInfo From(const RtpPacketToSend& rtp_packet_to_send,
|
||||
const PacedPacketInfo& pacing_info);
|
||||
|
||||
@@ -1,672 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "transport_feedback.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <numeric>
|
||||
#include <utility>
|
||||
|
||||
#include "byte_io.h"
|
||||
#include "common_header.h"
|
||||
#include "log.h"
|
||||
#include "module_common_types_public.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace rtcp {
|
||||
namespace {
|
||||
// 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 TimeDelta kBaseTimeTick = TransportFeedback::kDeltaTick * (1 << 8);
|
||||
constexpr TimeDelta kTimeWrapPeriod = kBaseTimeTick * (1 << 24);
|
||||
|
||||
// Message 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| FMT=15 | PT=205 | length |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// 0 | SSRC of packet sender |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// 4 | SSRC of media source |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// 8 | base sequence number | packet status count |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// 12 | reference time | fb pkt. count |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// 16 | packet chunk | packet chunk |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// . .
|
||||
// . .
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | packet chunk | recv delta | recv delta |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// . .
|
||||
// . .
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | recv delta | recv delta | zero padding |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
} // namespace
|
||||
|
||||
TransportFeedback::LastChunk::LastChunk() { Clear(); }
|
||||
|
||||
bool TransportFeedback::LastChunk::Empty() const { return size_ == 0; }
|
||||
|
||||
void TransportFeedback::LastChunk::Clear() {
|
||||
size_ = 0;
|
||||
all_same_ = true;
|
||||
has_large_delta_ = false;
|
||||
}
|
||||
|
||||
bool TransportFeedback::LastChunk::CanAdd(DeltaSize delta_size) const {
|
||||
if (size_ < kMaxTwoBitCapacity) return true;
|
||||
if (size_ < kMaxOneBitCapacity && !has_large_delta_ && delta_size != kLarge)
|
||||
return true;
|
||||
if (size_ < kMaxRunLengthCapacity && all_same_ &&
|
||||
delta_sizes_[0] == delta_size)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void TransportFeedback::LastChunk::Add(DeltaSize delta_size) {
|
||||
if (size_ < kMaxVectorCapacity) delta_sizes_[size_] = delta_size;
|
||||
size_++;
|
||||
all_same_ = all_same_ && delta_size == delta_sizes_[0];
|
||||
has_large_delta_ = has_large_delta_ || delta_size == kLarge;
|
||||
}
|
||||
|
||||
void TransportFeedback::LastChunk::AddMissingPackets(size_t num_missing) {
|
||||
std::fill(delta_sizes_.begin(), delta_sizes_.end(), DeltaSize(0));
|
||||
size_ = num_missing;
|
||||
}
|
||||
|
||||
uint16_t TransportFeedback::LastChunk::Emit() {
|
||||
if (all_same_) {
|
||||
uint16_t chunk = EncodeRunLength();
|
||||
Clear();
|
||||
return chunk;
|
||||
}
|
||||
if (size_ == kMaxOneBitCapacity) {
|
||||
uint16_t chunk = EncodeOneBit();
|
||||
Clear();
|
||||
return chunk;
|
||||
}
|
||||
uint16_t chunk = EncodeTwoBit(kMaxTwoBitCapacity);
|
||||
// Remove `kMaxTwoBitCapacity` encoded delta sizes:
|
||||
// Shift remaining delta sizes and recalculate all_same_ && has_large_delta_.
|
||||
size_ -= kMaxTwoBitCapacity;
|
||||
all_same_ = true;
|
||||
has_large_delta_ = false;
|
||||
for (size_t i = 0; i < size_; ++i) {
|
||||
DeltaSize delta_size = delta_sizes_[kMaxTwoBitCapacity + i];
|
||||
delta_sizes_[i] = delta_size;
|
||||
all_same_ = all_same_ && delta_size == delta_sizes_[0];
|
||||
has_large_delta_ = has_large_delta_ || delta_size == kLarge;
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
uint16_t TransportFeedback::LastChunk::EncodeLast() const {
|
||||
if (all_same_) return EncodeRunLength();
|
||||
if (size_ <= kMaxTwoBitCapacity) return EncodeTwoBit(size_);
|
||||
return EncodeOneBit();
|
||||
}
|
||||
|
||||
// Appends content of the Lastchunk to `deltas`.
|
||||
void TransportFeedback::LastChunk::AppendTo(
|
||||
std::vector<DeltaSize>* deltas) const {
|
||||
if (all_same_) {
|
||||
deltas->insert(deltas->end(), size_, delta_sizes_[0]);
|
||||
} else {
|
||||
deltas->insert(deltas->end(), delta_sizes_.begin(),
|
||||
delta_sizes_.begin() + size_);
|
||||
}
|
||||
}
|
||||
|
||||
void TransportFeedback::LastChunk::Decode(uint16_t chunk, size_t max_size) {
|
||||
if ((chunk & 0x8000) == 0) {
|
||||
DecodeRunLength(chunk, max_size);
|
||||
} else if ((chunk & 0x4000) == 0) {
|
||||
DecodeOneBit(chunk, max_size);
|
||||
} else {
|
||||
DecodeTwoBit(chunk, max_size);
|
||||
}
|
||||
}
|
||||
|
||||
// One Bit Status Vector Chunk
|
||||
//
|
||||
// 0 1
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |T|S| symbol list |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// T = 1
|
||||
// S = 0
|
||||
// Symbol list = 14 entries where 0 = not received, 1 = received 1-byte delta.
|
||||
uint16_t TransportFeedback::LastChunk::EncodeOneBit() const {
|
||||
uint16_t chunk = 0x8000;
|
||||
for (size_t i = 0; i < size_; ++i)
|
||||
chunk |= delta_sizes_[i] << (kMaxOneBitCapacity - 1 - i);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
void TransportFeedback::LastChunk::DecodeOneBit(uint16_t chunk,
|
||||
size_t max_size) {
|
||||
size_ = std::min(kMaxOneBitCapacity, max_size);
|
||||
has_large_delta_ = false;
|
||||
all_same_ = false;
|
||||
for (size_t i = 0; i < size_; ++i)
|
||||
delta_sizes_[i] = (chunk >> (kMaxOneBitCapacity - 1 - i)) & 0x01;
|
||||
}
|
||||
|
||||
// Two Bit Status Vector Chunk
|
||||
//
|
||||
// 0 1
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |T|S| symbol list |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// T = 1
|
||||
// S = 1
|
||||
// symbol list = 7 entries of two bits each.
|
||||
uint16_t TransportFeedback::LastChunk::EncodeTwoBit(size_t size) const {
|
||||
uint16_t chunk = 0xc000;
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
chunk |= delta_sizes_[i] << 2 * (kMaxTwoBitCapacity - 1 - i);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
void TransportFeedback::LastChunk::DecodeTwoBit(uint16_t chunk,
|
||||
size_t max_size) {
|
||||
size_ = std::min(kMaxTwoBitCapacity, max_size);
|
||||
has_large_delta_ = true;
|
||||
all_same_ = false;
|
||||
for (size_t i = 0; i < size_; ++i)
|
||||
delta_sizes_[i] = (chunk >> 2 * (kMaxTwoBitCapacity - 1 - i)) & 0x03;
|
||||
}
|
||||
|
||||
// Run Length Status Vector Chunk
|
||||
//
|
||||
// 0 1
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |T| S | Run Length |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// T = 0
|
||||
// S = symbol
|
||||
// Run Length = Unsigned integer denoting the run length of the symbol
|
||||
uint16_t TransportFeedback::LastChunk::EncodeRunLength() const {
|
||||
return (delta_sizes_[0] << 13) | static_cast<uint16_t>(size_);
|
||||
}
|
||||
|
||||
void TransportFeedback::LastChunk::DecodeRunLength(uint16_t chunk,
|
||||
size_t max_count) {
|
||||
size_ = std::min<size_t>(chunk & 0x1fff, max_count);
|
||||
DeltaSize delta_size = (chunk >> 13) & 0x03;
|
||||
has_large_delta_ = delta_size >= kLarge;
|
||||
all_same_ = true;
|
||||
// To make it consistent with Add function, populate delta_sizes_ beyond 1st.
|
||||
for (size_t i = 0; i < std::min<size_t>(size_, kMaxVectorCapacity); ++i)
|
||||
delta_sizes_[i] = delta_size;
|
||||
}
|
||||
|
||||
TransportFeedback::TransportFeedback()
|
||||
: TransportFeedback(/*include_timestamps=*/true) {}
|
||||
|
||||
TransportFeedback::TransportFeedback(bool include_timestamps)
|
||||
: base_seq_no_(0),
|
||||
num_seq_no_(0),
|
||||
base_time_ticks_(0),
|
||||
feedback_seq_(0),
|
||||
include_timestamps_(include_timestamps),
|
||||
last_timestamp_(Timestamp::Zero()),
|
||||
size_bytes_(kTransportFeedbackHeaderSizeBytes) {}
|
||||
|
||||
TransportFeedback::TransportFeedback(const TransportFeedback&) = default;
|
||||
|
||||
TransportFeedback::TransportFeedback(TransportFeedback&& other)
|
||||
: base_seq_no_(other.base_seq_no_),
|
||||
num_seq_no_(other.num_seq_no_),
|
||||
base_time_ticks_(other.base_time_ticks_),
|
||||
feedback_seq_(other.feedback_seq_),
|
||||
include_timestamps_(other.include_timestamps_),
|
||||
last_timestamp_(other.last_timestamp_),
|
||||
received_packets_(std::move(other.received_packets_)),
|
||||
all_packets_(std::move(other.all_packets_)),
|
||||
encoded_chunks_(std::move(other.encoded_chunks_)),
|
||||
last_chunk_(other.last_chunk_),
|
||||
size_bytes_(other.size_bytes_) {
|
||||
other.Clear();
|
||||
}
|
||||
|
||||
TransportFeedback::~TransportFeedback() {}
|
||||
|
||||
void TransportFeedback::SetBase(uint16_t base_sequence,
|
||||
Timestamp ref_timestamp) {
|
||||
base_seq_no_ = base_sequence;
|
||||
base_time_ticks_ =
|
||||
(ref_timestamp.us() % kTimeWrapPeriod.us()) / kBaseTimeTick.us();
|
||||
last_timestamp_ = BaseTime();
|
||||
}
|
||||
|
||||
void TransportFeedback::SetFeedbackSequenceNumber(uint8_t feedback_sequence) {
|
||||
feedback_seq_ = feedback_sequence;
|
||||
}
|
||||
|
||||
bool TransportFeedback::AddReceivedPacket(uint16_t sequence_number,
|
||||
Timestamp timestamp) {
|
||||
// Set delta to zero if timestamps are not included, this will simplify the
|
||||
// encoding process.
|
||||
int16_t delta = 0;
|
||||
if (include_timestamps_) {
|
||||
// Convert to ticks and round.
|
||||
if (last_timestamp_ > timestamp) {
|
||||
timestamp += (last_timestamp_ - timestamp).RoundUpTo(kTimeWrapPeriod);
|
||||
}
|
||||
int64_t delta_full =
|
||||
(timestamp - last_timestamp_).us() % kTimeWrapPeriod.us();
|
||||
if (delta_full > kTimeWrapPeriod.us() / 2) {
|
||||
delta_full -= kTimeWrapPeriod.us();
|
||||
delta_full -= kDeltaTick.us() / 2;
|
||||
} else {
|
||||
delta_full += kDeltaTick.us() / 2;
|
||||
}
|
||||
delta_full /= kDeltaTick.us();
|
||||
|
||||
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_ + num_seq_no_;
|
||||
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_timestamp_ += delta * kDeltaTick;
|
||||
if (include_timestamps_) {
|
||||
size_bytes_ += delta_size;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<TransportFeedback::ReceivedPacket>&
|
||||
TransportFeedback::GetReceivedPackets() const {
|
||||
return received_packets_;
|
||||
}
|
||||
|
||||
void TransportFeedback::ForAllPackets(
|
||||
rtc::FunctionView<void(uint16_t, TimeDelta)> handler) const {
|
||||
TimeDelta delta_since_base = TimeDelta::Zero();
|
||||
auto received_it = received_packets_.begin();
|
||||
const uint16_t last_seq_num = base_seq_no_ + num_seq_no_;
|
||||
for (uint16_t seq_num = base_seq_no_; seq_num != last_seq_num; ++seq_num) {
|
||||
if (received_it != received_packets_.end() &&
|
||||
received_it->sequence_number() == seq_num) {
|
||||
delta_since_base += received_it->delta();
|
||||
handler(seq_num, delta_since_base);
|
||||
++received_it;
|
||||
} else {
|
||||
handler(seq_num, TimeDelta::PlusInfinity());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t TransportFeedback::GetBaseSequence() const { return base_seq_no_; }
|
||||
|
||||
Timestamp 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 Timestamp::Zero() + kTimeWrapPeriod +
|
||||
int64_t{base_time_ticks_} * kBaseTimeTick;
|
||||
}
|
||||
|
||||
TimeDelta TransportFeedback::GetBaseDelta(Timestamp prev_timestamp) const {
|
||||
TimeDelta delta = BaseTime() - prev_timestamp;
|
||||
// Compensate for wrap around.
|
||||
if ((delta - kTimeWrapPeriod).Abs() < delta.Abs()) {
|
||||
delta -= kTimeWrapPeriod; // Wrap backwards.
|
||||
} else if ((delta + kTimeWrapPeriod).Abs() < delta.Abs()) {
|
||||
delta += kTimeWrapPeriod; // Wrap forwards.
|
||||
}
|
||||
return delta;
|
||||
}
|
||||
|
||||
// De-serialize packet.
|
||||
bool TransportFeedback::Parse(const CommonHeader& packet) {
|
||||
if (packet.payload_size_bytes() < kMinPayloadSizeBytes) {
|
||||
LOG_WARN(
|
||||
"Buffer too small ({} bytes) to fit a FeedbackPacket. Minimum size = "
|
||||
"{}",
|
||||
packet.payload_size_bytes(), kMinPayloadSizeBytes);
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t* const payload = packet.payload();
|
||||
ParseCommonFeedback(payload);
|
||||
|
||||
base_seq_no_ = ByteReader<uint16_t>::ReadBigEndian(&payload[8]);
|
||||
uint16_t status_count = ByteReader<uint16_t>::ReadBigEndian(&payload[10]);
|
||||
base_time_ticks_ = ByteReader<uint32_t, 3>::ReadBigEndian(&payload[12]);
|
||||
feedback_seq_ = payload[15];
|
||||
Clear();
|
||||
size_t index = 16;
|
||||
const size_t end_index = packet.payload_size_bytes();
|
||||
|
||||
if (status_count == 0) {
|
||||
LOG_WARN("Empty feedback messages not allowed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> delta_sizes;
|
||||
delta_sizes.reserve(status_count);
|
||||
while (delta_sizes.size() < status_count) {
|
||||
if (index + kChunkSizeBytes > end_index) {
|
||||
LOG_WARN("Buffer overflow while parsing packet.");
|
||||
Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t chunk = ByteReader<uint16_t>::ReadBigEndian(&payload[index]);
|
||||
index += kChunkSizeBytes;
|
||||
encoded_chunks_.push_back(chunk);
|
||||
last_chunk_.Decode(chunk, status_count - delta_sizes.size());
|
||||
last_chunk_.AppendTo(&delta_sizes);
|
||||
}
|
||||
// Last chunk is stored in the `last_chunk_`.
|
||||
encoded_chunks_.pop_back();
|
||||
num_seq_no_ = status_count;
|
||||
|
||||
uint16_t seq_no = base_seq_no_;
|
||||
size_t recv_delta_size =
|
||||
std::accumulate(delta_sizes.begin(), delta_sizes.end(), 0);
|
||||
|
||||
// Determine if timestamps, that is, recv_delta are included in the packet.
|
||||
if (end_index >= index + recv_delta_size) {
|
||||
for (size_t delta_size : delta_sizes) {
|
||||
switch (delta_size) {
|
||||
case 0:
|
||||
break;
|
||||
case 1: {
|
||||
int16_t delta = payload[index];
|
||||
received_packets_.emplace_back(seq_no, delta);
|
||||
last_timestamp_ += delta * kDeltaTick;
|
||||
index += delta_size;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
int16_t delta = ByteReader<int16_t>::ReadBigEndian(&payload[index]);
|
||||
received_packets_.emplace_back(seq_no, delta);
|
||||
last_timestamp_ += delta * kDeltaTick;
|
||||
index += delta_size;
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
Clear();
|
||||
LOG_WARN("Invalid delta_size for seq_no {}", seq_no);
|
||||
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
++seq_no;
|
||||
}
|
||||
} else {
|
||||
// The packet does not contain receive deltas.
|
||||
include_timestamps_ = false;
|
||||
for (size_t delta_size : delta_sizes) {
|
||||
// Use delta sizes to detect if packet was received.
|
||||
if (delta_size > 0) {
|
||||
received_packets_.emplace_back(seq_no, 0);
|
||||
}
|
||||
++seq_no;
|
||||
}
|
||||
}
|
||||
size_bytes_ = RtcpPacket::kHeaderLength + index;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<TransportFeedback> TransportFeedback::ParseFrom(
|
||||
const uint8_t* buffer, size_t length) {
|
||||
CommonHeader header;
|
||||
if (!header.Parse(buffer, length)) return nullptr;
|
||||
if (header.type() != kPacketType || header.fmt() != kFeedbackMessageType)
|
||||
return nullptr;
|
||||
std::unique_ptr<TransportFeedback> parsed(new TransportFeedback);
|
||||
if (!parsed->Parse(header)) return nullptr;
|
||||
return parsed;
|
||||
}
|
||||
|
||||
bool TransportFeedback::IsConsistent() const {
|
||||
size_t packet_size = kTransportFeedbackHeaderSizeBytes;
|
||||
std::vector<DeltaSize> delta_sizes;
|
||||
LastChunk chunk_decoder;
|
||||
for (uint16_t chunk : encoded_chunks_) {
|
||||
chunk_decoder.Decode(chunk, kMaxReportedPackets);
|
||||
chunk_decoder.AppendTo(&delta_sizes);
|
||||
packet_size += kChunkSizeBytes;
|
||||
}
|
||||
if (!last_chunk_.Empty()) {
|
||||
last_chunk_.AppendTo(&delta_sizes);
|
||||
packet_size += kChunkSizeBytes;
|
||||
}
|
||||
if (num_seq_no_ != delta_sizes.size()) {
|
||||
LOG_ERROR("{} packets encoded. Expected {}", delta_sizes.size(),
|
||||
num_seq_no_);
|
||||
return false;
|
||||
}
|
||||
Timestamp timestamp = BaseTime();
|
||||
auto packet_it = received_packets_.begin();
|
||||
uint16_t seq_no = base_seq_no_;
|
||||
for (DeltaSize delta_size : delta_sizes) {
|
||||
if (delta_size > 0) {
|
||||
if (packet_it == received_packets_.end()) {
|
||||
LOG_ERROR("Failed to find delta for seq_no {}", seq_no);
|
||||
return false;
|
||||
}
|
||||
if (packet_it->sequence_number() != seq_no) {
|
||||
LOG_ERROR("Expected to find delta for seq_no {}. Next delta is for {}",
|
||||
seq_no, packet_it->sequence_number());
|
||||
return false;
|
||||
}
|
||||
if (delta_size == 1 &&
|
||||
(packet_it->delta_ticks() < 0 || packet_it->delta_ticks() > 0xff)) {
|
||||
LOG_ERROR("Delta {} for seq_no {} doesn't fit into one byte",
|
||||
packet_it->delta_ticks(), seq_no);
|
||||
return false;
|
||||
}
|
||||
timestamp += packet_it->delta();
|
||||
++packet_it;
|
||||
}
|
||||
if (include_timestamps_) {
|
||||
packet_size += delta_size;
|
||||
}
|
||||
++seq_no;
|
||||
}
|
||||
if (packet_it != received_packets_.end()) {
|
||||
LOG_ERROR("Unencoded delta for seq_no {}", packet_it->sequence_number());
|
||||
return false;
|
||||
}
|
||||
if (timestamp != last_timestamp_) {
|
||||
LOG_ERROR("Last timestamp mismatch. Calculated: {}. Saved: {}",
|
||||
ToString(timestamp), ToString(last_timestamp_));
|
||||
return false;
|
||||
}
|
||||
if (size_bytes_ != packet_size) {
|
||||
LOG_ERROR("Rtcp packet size mismatch. Calculated: {}. Saved: {}",
|
||||
packet_size, size_bytes_);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t TransportFeedback::BlockLength() const {
|
||||
// Round size_bytes_ up to multiple of 32bits.
|
||||
return (size_bytes_ + 3) & (~static_cast<size_t>(3));
|
||||
}
|
||||
|
||||
size_t TransportFeedback::PaddingLength() const {
|
||||
return BlockLength() - size_bytes_;
|
||||
}
|
||||
|
||||
// Serialize packet.
|
||||
bool TransportFeedback::Create(uint8_t* packet, size_t* position,
|
||||
size_t max_length,
|
||||
PacketReadyCallback callback) const {
|
||||
if (num_seq_no_ == 0) return false;
|
||||
|
||||
while (*position + BlockLength() > max_length) {
|
||||
if (!OnBufferFull(packet, position, 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, position);
|
||||
CreateCommonFeedback(packet + *position);
|
||||
*position += kCommonFeedbackLength;
|
||||
|
||||
ByteWriter<uint16_t>::WriteBigEndian(&packet[*position], base_seq_no_);
|
||||
*position += 2;
|
||||
|
||||
ByteWriter<uint16_t>::WriteBigEndian(&packet[*position], num_seq_no_);
|
||||
*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;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransportFeedback::Clear() {
|
||||
num_seq_no_ = 0;
|
||||
last_timestamp_ = BaseTime();
|
||||
received_packets_.clear();
|
||||
all_packets_.clear();
|
||||
encoded_chunks_.clear();
|
||||
last_chunk_.Clear();
|
||||
size_bytes_ = kTransportFeedbackHeaderSizeBytes;
|
||||
}
|
||||
|
||||
bool TransportFeedback::AddDeltaSize(DeltaSize delta_size) {
|
||||
if (num_seq_no_ == 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);
|
||||
++num_seq_no_;
|
||||
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);
|
||||
++num_seq_no_;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TransportFeedback::AddMissingPackets(size_t num_missing_packets) {
|
||||
size_t new_num_seq_no = num_seq_no_ + 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) {
|
||||
num_seq_no_ = 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) {
|
||||
num_seq_no_ = (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);
|
||||
num_seq_no_ = new_num_seq_no;
|
||||
return true;
|
||||
}
|
||||
} // namespace rtcp
|
||||
} // namespace webrtc
|
||||
@@ -1,182 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TRANSPORT_FEEDBACK_H_
|
||||
#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TRANSPORT_FEEDBACK_H_
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "api/function_view.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "rtpfb.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace rtcp {
|
||||
class CommonHeader;
|
||||
|
||||
class TransportFeedback : public Rtpfb {
|
||||
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_; }
|
||||
TimeDelta delta() const { return delta_ticks_ * kDeltaTick; }
|
||||
|
||||
private:
|
||||
uint16_t sequence_number_;
|
||||
int16_t delta_ticks_;
|
||||
};
|
||||
// TODO(sprang): IANA reg?
|
||||
static constexpr uint8_t kFeedbackMessageType = 15;
|
||||
// Convert to multiples of 0.25ms.
|
||||
static constexpr TimeDelta kDeltaTick = TimeDelta::Micros(250);
|
||||
// Maximum number of packets (including missing) TransportFeedback can report.
|
||||
static constexpr size_t kMaxReportedPackets = 0xffff;
|
||||
|
||||
TransportFeedback();
|
||||
|
||||
// If `include_timestamps` is set to false, the created packet will not
|
||||
// contain the receive delta block.
|
||||
explicit TransportFeedback(bool include_timestamps);
|
||||
TransportFeedback(const TransportFeedback&);
|
||||
TransportFeedback(TransportFeedback&&);
|
||||
|
||||
~TransportFeedback() override;
|
||||
|
||||
void SetBase(uint16_t base_sequence, // Seq# of first packet in this msg.
|
||||
Timestamp ref_timestamp); // Reference timestamp for this msg.
|
||||
|
||||
void SetFeedbackSequenceNumber(uint8_t feedback_sequence);
|
||||
// NOTE: This method requires increasing sequence numbers (excepting wraps).
|
||||
bool AddReceivedPacket(uint16_t sequence_number, Timestamp timestamp);
|
||||
const std::vector<ReceivedPacket>& GetReceivedPackets() const;
|
||||
|
||||
// Calls `handler` for all packets this feedback describes.
|
||||
// For received packets pass receieve time as `delta_since_base` since the
|
||||
// `BaseTime()`. For missed packets calls `handler` with `delta_since_base =
|
||||
// PlusInfinity()`.
|
||||
void ForAllPackets(rtc::FunctionView<void(uint16_t sequence_number,
|
||||
TimeDelta delta_since_base)>
|
||||
handler) const;
|
||||
|
||||
uint16_t GetBaseSequence() const;
|
||||
|
||||
// Returns number of packets (including missing) this feedback describes.
|
||||
size_t GetPacketStatusCount() const { return num_seq_no_; }
|
||||
|
||||
// Get the reference time including any precision loss.
|
||||
Timestamp BaseTime() const;
|
||||
|
||||
// Get the unwrapped delta between current base time and `prev_timestamp`.
|
||||
TimeDelta GetBaseDelta(Timestamp prev_timestamp) const;
|
||||
|
||||
// Does the feedback packet contain timestamp information?
|
||||
bool IncludeTimestamps() const { return include_timestamps_; }
|
||||
|
||||
bool Parse(const CommonHeader& packet);
|
||||
static std::unique_ptr<TransportFeedback> ParseFrom(const uint8_t* buffer,
|
||||
size_t length);
|
||||
// Pre and postcondition for all public methods. Should always return true.
|
||||
// This function is for tests.
|
||||
bool IsConsistent() const;
|
||||
|
||||
size_t BlockLength() const override;
|
||||
size_t PaddingLength() const;
|
||||
|
||||
bool Create(uint8_t* packet, size_t* position, size_t max_length,
|
||||
PacketReadyCallback callback) const override;
|
||||
|
||||
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_;
|
||||
};
|
||||
|
||||
// Reset packet to consistent empty state.
|
||||
void Clear();
|
||||
|
||||
bool AddDeltaSize(DeltaSize delta_size);
|
||||
// Adds `num_missing_packets` deltas of size 0.
|
||||
bool AddMissingPackets(size_t num_missing_packets);
|
||||
|
||||
uint16_t base_seq_no_;
|
||||
uint16_t num_seq_no_;
|
||||
uint32_t base_time_ticks_;
|
||||
uint8_t feedback_seq_;
|
||||
bool include_timestamps_;
|
||||
|
||||
Timestamp last_timestamp_;
|
||||
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_;
|
||||
};
|
||||
|
||||
} // namespace rtcp
|
||||
} // namespace webrtc
|
||||
#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TRANSPORT_FEEDBACK_H_
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "log.h"
|
||||
#include "ntp_time_util.h"
|
||||
#include "rtp_packet_to_send.h"
|
||||
#include "transport_feedback.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@@ -163,77 +162,6 @@ std::optional<SentPacket> TransportFeedbackAdapter::ProcessSentPacket(
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<TransportPacketsFeedback>
|
||||
TransportFeedbackAdapter::ProcessTransportFeedback(
|
||||
const rtcp::TransportFeedback& feedback, Timestamp feedback_receive_time) {
|
||||
if (feedback.GetPacketStatusCount() == 0) {
|
||||
LOG_INFO("Empty transport feedback packet received.");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Add timestamp deltas to a local time base selected on first packet arrival.
|
||||
// This won't be the true time base, but makes it easier to manually inspect
|
||||
// time stamps.
|
||||
if (last_transport_feedback_base_time_.IsInfinite()) {
|
||||
current_offset_ = feedback_receive_time;
|
||||
} else {
|
||||
// TODO(srte): We shouldn't need to do rounding here.
|
||||
const TimeDelta delta =
|
||||
feedback.GetBaseDelta(last_transport_feedback_base_time_)
|
||||
.RoundDownTo(TimeDelta::Millis(1));
|
||||
// Protect against assigning current_offset_ negative value.
|
||||
if (delta < Timestamp::Zero() - current_offset_) {
|
||||
LOG_WARN("Unexpected feedback timestamp received.");
|
||||
current_offset_ = feedback_receive_time;
|
||||
} else {
|
||||
current_offset_ += delta;
|
||||
}
|
||||
}
|
||||
last_transport_feedback_base_time_ = feedback.BaseTime();
|
||||
|
||||
std::vector<PacketResult> packet_result_vector;
|
||||
packet_result_vector.reserve(feedback.GetPacketStatusCount());
|
||||
|
||||
size_t failed_lookups = 0;
|
||||
size_t ignored = 0;
|
||||
|
||||
feedback.ForAllPackets([&](uint16_t sequence_number,
|
||||
TimeDelta delta_since_base) {
|
||||
int64_t seq_num = seq_num_unwrapper_.Unwrap(sequence_number);
|
||||
std::optional<PacketFeedback> packet_feedback = RetrievePacketFeedback(
|
||||
seq_num, /*received=*/delta_since_base.IsFinite());
|
||||
if (!packet_feedback) {
|
||||
++failed_lookups;
|
||||
return;
|
||||
}
|
||||
if (delta_since_base.IsFinite()) {
|
||||
packet_feedback->receive_time =
|
||||
current_offset_ + delta_since_base.RoundDownTo(TimeDelta::Millis(1));
|
||||
}
|
||||
if (packet_feedback->network_route == network_route_) {
|
||||
PacketResult result;
|
||||
result.sent_packet = packet_feedback->sent;
|
||||
result.receive_time = packet_feedback->receive_time;
|
||||
packet_result_vector.push_back(result);
|
||||
} else {
|
||||
++ignored;
|
||||
}
|
||||
});
|
||||
|
||||
if (failed_lookups > 0) {
|
||||
LOG_WARN(
|
||||
"Failed to lookup send time for {} packet {}. Packets reordered or "
|
||||
"send time history too small?",
|
||||
failed_lookups, (failed_lookups > 1 ? "s" : ""));
|
||||
}
|
||||
if (ignored > 0) {
|
||||
LOG_INFO("Ignoring packets because they were sent on a different route.",
|
||||
ignored);
|
||||
}
|
||||
return ToTransportFeedback(std::move(packet_result_vector),
|
||||
feedback_receive_time, /*suports_ecn=*/false);
|
||||
}
|
||||
|
||||
std::optional<TransportPacketsFeedback>
|
||||
TransportFeedbackAdapter::ProcessCongestionControlFeedback(
|
||||
const rtcp::CongestionControlFeedback& feedback,
|
||||
|
||||
@@ -76,9 +76,6 @@ class TransportFeedbackAdapter {
|
||||
std::optional<SentPacket> ProcessSentPacket(
|
||||
const rtc::SentPacket& sent_packet);
|
||||
|
||||
std::optional<TransportPacketsFeedback> ProcessTransportFeedback(
|
||||
const rtcp::TransportFeedback& feedback, Timestamp feedback_receive_time);
|
||||
|
||||
std::optional<TransportPacketsFeedback> ProcessCongestionControlFeedback(
|
||||
const rtcp::CongestionControlFeedback& feedback,
|
||||
Timestamp feedback_receive_time);
|
||||
|
||||
39
src/rtp/rtp_packet/rtp_packet_history.cpp
Normal file
39
src/rtp/rtp_packet/rtp_packet_history.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "rtp_packet_history.h"
|
||||
|
||||
#include "sequence_number_compare.h"
|
||||
|
||||
RtpPacketHistory::RtpPacketHistory() {}
|
||||
|
||||
RtpPacketHistory::~RtpPacketHistory() {}
|
||||
|
||||
void RtpPacketHistory::AddPacket(std::shared_ptr<RtpPacketToSend> rtp_packet,
|
||||
Timestamp send_time) {
|
||||
rtp_packet_history_.push_back(
|
||||
{rtp_packet, send_time, GetPacketIndex(rtp_packet->SequenceNumber())});
|
||||
}
|
||||
|
||||
int RtpPacketHistory::GetPacketIndex(uint16_t sequence_number) const {
|
||||
if (packet_history_.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int first_seq = packet_history_.front().packet_->SequenceNumber();
|
||||
if (first_seq == sequence_number) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int packet_index = sequence_number - first_seq;
|
||||
constexpr int kSeqNumSpan = std::numeric_limits<uint16_t>::max() + 1;
|
||||
|
||||
if (IsNewerSequenceNumber(sequence_number, first_seq)) {
|
||||
if (sequence_number < first_seq) {
|
||||
// Forward wrap.
|
||||
packet_index += kSeqNumSpan;
|
||||
}
|
||||
} else if (sequence_number > first_seq) {
|
||||
// Backwards wrap.
|
||||
packet_index -= kSeqNumSpan;
|
||||
}
|
||||
|
||||
return packet_index;
|
||||
}
|
||||
38
src/rtp/rtp_packet/rtp_packet_history.h
Normal file
38
src/rtp/rtp_packet/rtp_packet_history.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2025-02-14
|
||||
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _RTP_PACKET_HISTORY_H_
|
||||
#define _RTP_PACKET_HISTORY_H_
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "rtp_packet_to_send.h"
|
||||
|
||||
class RtpPacketHistory {
|
||||
public:
|
||||
RtpPacketHistory();
|
||||
~RtpPacketHistory();
|
||||
|
||||
void AddPacket(std::shared_ptr<RtpPacketToSend> rtp_packet,
|
||||
Timestamp send_time);
|
||||
|
||||
private:
|
||||
int GetPacketIndex(uint16_t sequence_number) const;
|
||||
|
||||
return packet_index;
|
||||
}
|
||||
|
||||
private : struct RtpPacketToSendInfo {
|
||||
std::shared_ptr<RtpPacketToSend> rtp_packet;
|
||||
Timestamp send_time;
|
||||
uint64_t index;
|
||||
};
|
||||
|
||||
private:
|
||||
std::deque<std::shared_ptr<RtpPacketToSend>> rtp_packet_history_;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -18,12 +18,12 @@
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "api/ref_counted_base.h"
|
||||
#include "api/rtp_rtcp/rtp_rtcp_typedef.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/units/time_delta.h"
|
||||
#include "api/units/timestamp.h"
|
||||
#include "api/video/video_timing.h"
|
||||
#include "rtp_packet.h"
|
||||
#include "rtp_rtcp_defines.h"
|
||||
|
||||
// Forward declare the RtpPacket class since it is not in the webrtc namespace.
|
||||
class RtpPacket;
|
||||
@@ -34,9 +34,6 @@ namespace webrtc {
|
||||
// create rtp header extensions or other data that is sent over the wire.
|
||||
class RtpPacketToSend : public ::RtpPacket {
|
||||
public:
|
||||
// RtpPacketToSend::Type is deprecated. Use RtpPacketMediaType directly.
|
||||
using Type = RtpPacketMediaType;
|
||||
|
||||
explicit RtpPacketToSend();
|
||||
RtpPacketToSend(size_t capacity);
|
||||
RtpPacketToSend(const RtpPacketToSend& packet);
|
||||
@@ -51,9 +48,11 @@ class RtpPacketToSend : public ::RtpPacket {
|
||||
webrtc::Timestamp capture_time() const { return capture_time_; }
|
||||
void set_capture_time(webrtc::Timestamp time) { capture_time_ = time; }
|
||||
|
||||
void set_packet_type(RtpPacketMediaType type);
|
||||
void set_packet_type(webrtc::RtpPacketMediaType type);
|
||||
|
||||
std::optional<RtpPacketMediaType> packet_type() const { return packet_type_; }
|
||||
std::optional<webrtc::RtpPacketMediaType> packet_type() const {
|
||||
return packet_type_;
|
||||
}
|
||||
|
||||
enum class OriginalType { kAudio, kVideo };
|
||||
// Original type does not change if packet type is changed to kRetransmission.
|
||||
@@ -157,7 +156,7 @@ class RtpPacketToSend : public ::RtpPacket {
|
||||
|
||||
private:
|
||||
webrtc::Timestamp capture_time_ = webrtc::Timestamp::Zero();
|
||||
std::optional<RtpPacketMediaType> packet_type_;
|
||||
std::optional<webrtc::RtpPacketMediaType> packet_type_;
|
||||
std::optional<OriginalType> original_packet_type_;
|
||||
std::optional<uint32_t> original_ssrc_;
|
||||
std::optional<int64_t> transport_sequence_number_;
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "rtp_packet.h"
|
||||
#include "rtp_packet_to_send.h"
|
||||
|
||||
class RtpPacketizer {
|
||||
public:
|
||||
@@ -20,8 +21,8 @@ class RtpPacketizer {
|
||||
|
||||
virtual ~RtpPacketizer() = default;
|
||||
|
||||
virtual std::vector<RtpPacket> Build(uint8_t* payload,
|
||||
uint32_t payload_size) = 0;
|
||||
virtual std::vector<std::shared_ptr<RtpPacket>> Build(
|
||||
uint8_t* payload, uint32_t payload_size, bool use_rtp_packet_to_send) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -4,8 +4,9 @@ RtpPacketizerAv1::RtpPacketizerAv1(uint32_t ssrc) {}
|
||||
|
||||
RtpPacketizerAv1::~RtpPacketizerAv1() {}
|
||||
|
||||
std::vector<RtpPacket> RtpPacketizerAv1::Build(uint8_t* payload,
|
||||
uint32_t payload_size) {
|
||||
std::vector<RtpPacket> rtp_packets;
|
||||
std::vector<std::shared_ptr<RtpPacket>> RtpPacketizerAv1::Build(
|
||||
uint8_t* payload, uint32_t payload_size, bool use_rtp_packet_to_send) {
|
||||
std::vector<std::shared_ptr<RtpPacket>> rtp_packets;
|
||||
|
||||
return rtp_packets;
|
||||
}
|
||||
@@ -15,8 +15,9 @@ class RtpPacketizerAv1 : public RtpPacketizer {
|
||||
|
||||
virtual ~RtpPacketizerAv1();
|
||||
|
||||
std::vector<RtpPacket> Build(uint8_t* payload,
|
||||
uint32_t payload_size) override;
|
||||
std::vector<std::shared_ptr<RtpPacket>> Build(
|
||||
uint8_t* payload, uint32_t payload_size,
|
||||
bool use_rtp_packet_to_send) override;
|
||||
|
||||
private:
|
||||
uint8_t version_;
|
||||
|
||||
@@ -17,8 +17,37 @@ RtpPacketizerGeneric::RtpPacketizerGeneric(uint32_t ssrc)
|
||||
|
||||
RtpPacketizerGeneric::~RtpPacketizerGeneric() {}
|
||||
|
||||
std::vector<RtpPacket> RtpPacketizerGeneric::Build(uint8_t* payload,
|
||||
uint32_t payload_size) {
|
||||
void RtpPacketizerGeneric::AddAbsSendTimeExtension(
|
||||
std::vector<uint8_t>& rtp_packet_frame) {
|
||||
uint16_t extension_profile = 0xBEDE; // One-byte header extension
|
||||
uint8_t sub_extension_id = 3; // ID for Absolute Send Time
|
||||
uint8_t sub_extension_length =
|
||||
2; // Length of the extension data in bytes minus 1
|
||||
|
||||
uint32_t abs_send_time =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
abs_send_time &= 0x00FFFFFF; // Absolute Send Time is 24 bits
|
||||
|
||||
// Add extension profile
|
||||
rtp_packet_frame.push_back((extension_profile >> 8) & 0xFF);
|
||||
rtp_packet_frame.push_back(extension_profile & 0xFF);
|
||||
|
||||
// Add extension length (in 32-bit words, minus one)
|
||||
rtp_packet_frame.push_back(
|
||||
0x00); // Placeholder for length, will be updated later
|
||||
rtp_packet_frame.push_back(0x01); // One 32-bit word
|
||||
|
||||
// Add Absolute Send Time extension
|
||||
rtp_packet_frame.push_back((sub_extension_id << 4) | sub_extension_length);
|
||||
rtp_packet_frame.push_back((abs_send_time >> 16) & 0xFF);
|
||||
rtp_packet_frame.push_back((abs_send_time >> 8) & 0xFF);
|
||||
rtp_packet_frame.push_back(abs_send_time & 0xFF);
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<RtpPacket>> RtpPacketizerGeneric::Build(
|
||||
uint8_t* payload, uint32_t payload_size, bool use_rtp_packet_to_send) {
|
||||
uint32_t last_packet_size = payload_size % MAX_NALU_LEN;
|
||||
uint32_t packet_num =
|
||||
payload_size / MAX_NALU_LEN + (last_packet_size ? 1 : 0);
|
||||
@@ -28,7 +57,8 @@ std::vector<RtpPacket> RtpPacketizerGeneric::Build(uint8_t* payload,
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
|
||||
std::vector<RtpPacket> rtp_packets;
|
||||
std::vector<std::shared_ptr<RtpPacket>> rtp_packets;
|
||||
|
||||
for (uint32_t index = 0; index < packet_num; index++) {
|
||||
version_ = kRtpVersion;
|
||||
has_padding_ = false;
|
||||
@@ -77,40 +107,17 @@ std::vector<RtpPacket> RtpPacketizerGeneric::Build(uint8_t* payload,
|
||||
payload + MAX_NALU_LEN);
|
||||
}
|
||||
|
||||
RtpPacket rtp_packet;
|
||||
rtp_packet.Build(rtp_packet_frame_.data(), rtp_packet_frame_.size());
|
||||
|
||||
rtp_packets.emplace_back(rtp_packet);
|
||||
if (use_rtp_packet_to_send) {
|
||||
std::shared_ptr<webrtc::RtpPacketToSend> rtp_packet =
|
||||
std::make_unique<webrtc::RtpPacketToSend>();
|
||||
rtp_packet->Build(rtp_packet_frame_.data(), rtp_packet_frame_.size());
|
||||
rtp_packets.emplace_back(std::move(rtp_packet));
|
||||
} else {
|
||||
std::shared_ptr<RtpPacket> rtp_packet = std::make_unique<RtpPacket>();
|
||||
rtp_packet->Build(rtp_packet_frame_.data(), rtp_packet_frame_.size());
|
||||
rtp_packets.emplace_back(std::move(rtp_packet));
|
||||
}
|
||||
}
|
||||
|
||||
return rtp_packets;
|
||||
}
|
||||
|
||||
void RtpPacketizerGeneric::AddAbsSendTimeExtension(
|
||||
std::vector<uint8_t>& rtp_packet_frame) {
|
||||
uint16_t extension_profile = 0xBEDE; // One-byte header extension
|
||||
uint8_t sub_extension_id = 3; // ID for Absolute Send Time
|
||||
uint8_t sub_extension_length =
|
||||
2; // Length of the extension data in bytes minus 1
|
||||
|
||||
uint32_t abs_send_time =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
abs_send_time &= 0x00FFFFFF; // Absolute Send Time is 24 bits
|
||||
|
||||
// Add extension profile
|
||||
rtp_packet_frame.push_back((extension_profile >> 8) & 0xFF);
|
||||
rtp_packet_frame.push_back(extension_profile & 0xFF);
|
||||
|
||||
// Add extension length (in 32-bit words, minus one)
|
||||
rtp_packet_frame.push_back(
|
||||
0x00); // Placeholder for length, will be updated later
|
||||
rtp_packet_frame.push_back(0x01); // One 32-bit word
|
||||
|
||||
// Add Absolute Send Time extension
|
||||
rtp_packet_frame.push_back((sub_extension_id << 4) | sub_extension_length);
|
||||
rtp_packet_frame.push_back((abs_send_time >> 16) & 0xFF);
|
||||
rtp_packet_frame.push_back((abs_send_time >> 8) & 0xFF);
|
||||
rtp_packet_frame.push_back(abs_send_time & 0xFF);
|
||||
}
|
||||
@@ -15,8 +15,9 @@ class RtpPacketizerGeneric : public RtpPacketizer {
|
||||
|
||||
virtual ~RtpPacketizerGeneric();
|
||||
|
||||
std::vector<RtpPacket> Build(uint8_t* payload,
|
||||
uint32_t payload_size) override;
|
||||
std::vector<std::shared_ptr<RtpPacket>> Build(
|
||||
uint8_t* payload, uint32_t payload_size,
|
||||
bool use_rtp_packet_to_send) override;
|
||||
|
||||
private:
|
||||
void AddAbsSendTimeExtension(std::vector<uint8_t>& rtp_packet_frame);
|
||||
|
||||
@@ -17,15 +17,6 @@ RtpPacketizerH264::RtpPacketizerH264(uint32_t ssrc)
|
||||
|
||||
RtpPacketizerH264::~RtpPacketizerH264() {}
|
||||
|
||||
std::vector<RtpPacket> RtpPacketizerH264::Build(uint8_t* payload,
|
||||
uint32_t payload_size) {
|
||||
if (payload_size <= MAX_NALU_LEN) {
|
||||
return BuildNalu(payload, payload_size);
|
||||
} else {
|
||||
return BuildFua(payload, payload_size);
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
@@ -67,9 +58,18 @@ void RtpPacketizerH264::AddAbsSendTimeExtension(
|
||||
rtp_packet_frame.push_back(abs_send_time & 0xFF);
|
||||
}
|
||||
|
||||
std::vector<RtpPacket> RtpPacketizerH264::BuildNalu(uint8_t* payload,
|
||||
uint32_t payload_size) {
|
||||
std::vector<RtpPacket> rtp_packets;
|
||||
std::vector<std::shared_ptr<RtpPacket>> RtpPacketizerH264::Build(
|
||||
uint8_t* payload, uint32_t payload_size, bool use_rtp_packet_to_send) {
|
||||
if (payload_size <= MAX_NALU_LEN) {
|
||||
return BuildNalu(payload, payload_size, use_rtp_packet_to_send);
|
||||
} else {
|
||||
return BuildFua(payload, payload_size, use_rtp_packet_to_send);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<RtpPacket>> RtpPacketizerH264::BuildNalu(
|
||||
uint8_t* payload, uint32_t payload_size, bool use_rtp_packet_to_send) {
|
||||
std::vector<std::shared_ptr<RtpPacket>> rtp_packets;
|
||||
|
||||
version_ = kRtpVersion;
|
||||
has_padding_ = false;
|
||||
@@ -123,16 +123,23 @@ std::vector<RtpPacket> RtpPacketizerH264::BuildNalu(uint8_t* payload,
|
||||
rtp_packet_frame_.insert(rtp_packet_frame_.end(), payload,
|
||||
payload + payload_size);
|
||||
|
||||
RtpPacket rtp_packet;
|
||||
rtp_packet.Build(rtp_packet_frame_.data(), rtp_packet_frame_.size());
|
||||
rtp_packets.emplace_back(rtp_packet);
|
||||
if (use_rtp_packet_to_send) {
|
||||
std::shared_ptr<webrtc::RtpPacketToSend> rtp_packet =
|
||||
std::make_unique<webrtc::RtpPacketToSend>();
|
||||
rtp_packet->Build(rtp_packet_frame_.data(), rtp_packet_frame_.size());
|
||||
rtp_packets.emplace_back(std::move(rtp_packet));
|
||||
} else {
|
||||
std::shared_ptr<RtpPacket> rtp_packet = std::make_unique<RtpPacket>();
|
||||
rtp_packet->Build(rtp_packet_frame_.data(), rtp_packet_frame_.size());
|
||||
rtp_packets.emplace_back(std::move(rtp_packet));
|
||||
}
|
||||
|
||||
return rtp_packets;
|
||||
}
|
||||
|
||||
std::vector<RtpPacket> RtpPacketizerH264::BuildFua(uint8_t* payload,
|
||||
uint32_t payload_size) {
|
||||
std::vector<RtpPacket> rtp_packets;
|
||||
std::vector<std::shared_ptr<RtpPacket>> RtpPacketizerH264::BuildFua(
|
||||
uint8_t* payload, uint32_t payload_size, bool use_rtp_packet_to_send) {
|
||||
std::vector<std::shared_ptr<RtpPacket>> rtp_packets;
|
||||
|
||||
uint32_t last_packet_size = payload_size % MAX_NALU_LEN;
|
||||
uint32_t packet_num =
|
||||
@@ -214,10 +221,16 @@ std::vector<RtpPacket> RtpPacketizerH264::BuildFua(uint8_t* payload,
|
||||
payload + index * MAX_NALU_LEN + MAX_NALU_LEN);
|
||||
}
|
||||
|
||||
RtpPacket rtp_packet;
|
||||
rtp_packet.Build(rtp_packet_frame_.data(), rtp_packet_frame_.size());
|
||||
|
||||
rtp_packets.emplace_back(rtp_packet);
|
||||
if (use_rtp_packet_to_send) {
|
||||
std::shared_ptr<webrtc::RtpPacketToSend> rtp_packet =
|
||||
std::make_unique<webrtc::RtpPacketToSend>();
|
||||
rtp_packet->Build(rtp_packet_frame_.data(), rtp_packet_frame_.size());
|
||||
rtp_packets.emplace_back(std::move(rtp_packet));
|
||||
} else {
|
||||
std::shared_ptr<RtpPacket> rtp_packet = std::make_unique<RtpPacket>();
|
||||
rtp_packet->Build(rtp_packet_frame_.data(), rtp_packet_frame_.size());
|
||||
rtp_packets.emplace_back(std::move(rtp_packet));
|
||||
}
|
||||
}
|
||||
|
||||
return rtp_packets;
|
||||
|
||||
@@ -15,12 +15,16 @@ class RtpPacketizerH264 : public RtpPacketizer {
|
||||
|
||||
virtual ~RtpPacketizerH264();
|
||||
|
||||
std::vector<RtpPacket> Build(uint8_t* payload,
|
||||
uint32_t payload_size) override;
|
||||
std::vector<std::shared_ptr<RtpPacket>> Build(
|
||||
uint8_t* payload, uint32_t payload_size,
|
||||
bool use_rtp_packet_to_send) override;
|
||||
|
||||
std::vector<RtpPacket> BuildNalu(uint8_t* payload, uint32_t payload_size);
|
||||
std::vector<std::shared_ptr<RtpPacket>> BuildNalu(
|
||||
uint8_t* payload, uint32_t payload_size, bool use_rtp_packet_to_send);
|
||||
|
||||
std::vector<RtpPacket> BuildFua(uint8_t* payload, uint32_t payload_size);
|
||||
std::vector<std::shared_ptr<RtpPacket>> BuildFua(uint8_t* payload,
|
||||
uint32_t payload_size,
|
||||
bool use_rtp_packet_to_send);
|
||||
|
||||
private:
|
||||
bool EncodeH264Fua(RtpPacket& rtp_packet, uint8_t* payload,
|
||||
|
||||
Reference in New Issue
Block a user