Propagate Video CSRCs modified by an insertable streams frame transform
Allow CSRCs to be modified per-frame in an Encoded Insertable Streams transform, to support a web API which allows per-frame CSRC modifications to signal when a JS application has changed the source of the video which is written into an encoded frame. Initially only for Video, with Audio support likely to follow later. Bug: webrtc:14709 Change-Id: Ib34f35faa9cee56216b30eaae42d7e65c78bb9f2 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/291324 Reviewed-by: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Tove Petersson <tovep@google.com> Reviewed-by: Henrik Boström <hbos@webrtc.org> Commit-Queue: Tony Herre <herre@google.com> Cr-Commit-Position: refs/heads/main@{#39214}
This commit is contained in:
parent
db208317eb
commit
64ce699f4b
@ -136,4 +136,12 @@ void VideoFrameMetadata::SetRTPVideoHeaderCodecSpecifics(
|
|||||||
codec_specifics_ = std::move(codec_specifics);
|
codec_specifics_ = std::move(codec_specifics);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> VideoFrameMetadata::GetCsrcs() const {
|
||||||
|
return csrcs_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoFrameMetadata::SetCsrcs(std::vector<uint32_t> csrcs) {
|
||||||
|
csrcs_ = std::move(csrcs);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
#define API_VIDEO_VIDEO_FRAME_METADATA_H_
|
#define API_VIDEO_VIDEO_FRAME_METADATA_H_
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "absl/container/inlined_vector.h"
|
#include "absl/container/inlined_vector.h"
|
||||||
#include "absl/types/optional.h"
|
#include "absl/types/optional.h"
|
||||||
@ -88,6 +89,9 @@ class RTC_EXPORT VideoFrameMetadata {
|
|||||||
void SetRTPVideoHeaderCodecSpecifics(
|
void SetRTPVideoHeaderCodecSpecifics(
|
||||||
RTPVideoHeaderCodecSpecifics codec_specifics);
|
RTPVideoHeaderCodecSpecifics codec_specifics);
|
||||||
|
|
||||||
|
std::vector<uint32_t> GetCsrcs() const;
|
||||||
|
void SetCsrcs(std::vector<uint32_t> csrcs);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VideoFrameType frame_type_ = VideoFrameType::kEmptyFrame;
|
VideoFrameType frame_type_ = VideoFrameType::kEmptyFrame;
|
||||||
int16_t width_ = 0;
|
int16_t width_ = 0;
|
||||||
@ -106,6 +110,8 @@ class RTC_EXPORT VideoFrameMetadata {
|
|||||||
uint8_t simulcast_idx_ = 0;
|
uint8_t simulcast_idx_ = 0;
|
||||||
VideoCodecType codec_ = VideoCodecType::kVideoCodecGeneric;
|
VideoCodecType codec_ = VideoCodecType::kVideoCodecGeneric;
|
||||||
RTPVideoHeaderCodecSpecifics codec_specifics_;
|
RTPVideoHeaderCodecSpecifics codec_specifics_;
|
||||||
|
|
||||||
|
std::vector<uint32_t> csrcs_;
|
||||||
};
|
};
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|
||||||
|
|||||||
@ -29,6 +29,7 @@ namespace {
|
|||||||
|
|
||||||
using testing::NiceMock;
|
using testing::NiceMock;
|
||||||
using testing::Return;
|
using testing::Return;
|
||||||
|
using testing::ReturnRef;
|
||||||
|
|
||||||
class MockTransformableVideoFrame
|
class MockTransformableVideoFrame
|
||||||
: public webrtc::TransformableVideoFrameInterface {
|
: public webrtc::TransformableVideoFrameInterface {
|
||||||
@ -60,9 +61,18 @@ TEST(FrameTransformerFactory, CloneVideoFrame) {
|
|||||||
std::fill_n(data, 10, 5);
|
std::fill_n(data, 10, 5);
|
||||||
rtc::ArrayView<uint8_t> data_view(data);
|
rtc::ArrayView<uint8_t> data_view(data);
|
||||||
EXPECT_CALL(original_frame, GetData()).WillRepeatedly(Return(data_view));
|
EXPECT_CALL(original_frame, GetData()).WillRepeatedly(Return(data_view));
|
||||||
|
webrtc::VideoFrameMetadata metadata;
|
||||||
|
std::vector<uint32_t> csrcs{123, 321};
|
||||||
|
// Copy csrcs rather than moving so we can compare in an EXPECT_EQ later.
|
||||||
|
metadata.SetCsrcs(csrcs);
|
||||||
|
|
||||||
|
EXPECT_CALL(original_frame, GetMetadata())
|
||||||
|
.WillRepeatedly(ReturnRef(metadata));
|
||||||
auto cloned_frame = CloneVideoFrame(&original_frame);
|
auto cloned_frame = CloneVideoFrame(&original_frame);
|
||||||
|
|
||||||
EXPECT_EQ(cloned_frame->GetData().size(), 10u);
|
EXPECT_EQ(cloned_frame->GetData().size(), 10u);
|
||||||
EXPECT_THAT(cloned_frame->GetData(), testing::Each(5u));
|
EXPECT_THAT(cloned_frame->GetData(), testing::Each(5u));
|
||||||
|
EXPECT_EQ(cloned_frame->GetMetadata().GetCsrcs(), csrcs);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@ -210,7 +210,7 @@ class RtpRtcpRtxNackTest : public ::testing::Test {
|
|||||||
video_header.frame_type = VideoFrameType::kVideoFrameDelta;
|
video_header.frame_type = VideoFrameType::kVideoFrameDelta;
|
||||||
EXPECT_TRUE(rtp_sender_video_->SendVideo(
|
EXPECT_TRUE(rtp_sender_video_->SendVideo(
|
||||||
kPayloadType, VideoCodecType::kVideoCodecGeneric, timestamp,
|
kPayloadType, VideoCodecType::kVideoCodecGeneric, timestamp,
|
||||||
timestamp / 90, payload_data, video_header, 0));
|
timestamp / 90, payload_data, video_header, 0, {}));
|
||||||
// Min required delay until retransmit = 5 + RTT ms (RTT = 0).
|
// Min required delay until retransmit = 5 + RTT ms (RTT = 0).
|
||||||
fake_clock.AdvanceTimeMilliseconds(5);
|
fake_clock.AdvanceTimeMilliseconds(5);
|
||||||
int length = BuildNackList(nack_list);
|
int length = BuildNackList(nack_list);
|
||||||
@ -260,7 +260,7 @@ TEST_F(RtpRtcpRtxNackTest, LongNackList) {
|
|||||||
video_header.frame_type = VideoFrameType::kVideoFrameDelta;
|
video_header.frame_type = VideoFrameType::kVideoFrameDelta;
|
||||||
EXPECT_TRUE(rtp_sender_video_->SendVideo(
|
EXPECT_TRUE(rtp_sender_video_->SendVideo(
|
||||||
kPayloadType, VideoCodecType::kVideoCodecGeneric, timestamp,
|
kPayloadType, VideoCodecType::kVideoCodecGeneric, timestamp,
|
||||||
timestamp / 90, payload_data, video_header, 0));
|
timestamp / 90, payload_data, video_header, 0, {}));
|
||||||
// Prepare next frame.
|
// Prepare next frame.
|
||||||
timestamp += 3000;
|
timestamp += 3000;
|
||||||
fake_clock.AdvanceTimeMilliseconds(33);
|
fake_clock.AdvanceTimeMilliseconds(33);
|
||||||
|
|||||||
@ -358,7 +358,7 @@ class RtpRtcpImpl2Test : public ::testing::Test {
|
|||||||
|
|
||||||
success &= sender->SendVideo(kPayloadType, VideoCodecType::kVideoCodecVP8,
|
success &= sender->SendVideo(kPayloadType, VideoCodecType::kVideoCodecVP8,
|
||||||
rtp_timestamp, capture_time_ms, payload,
|
rtp_timestamp, capture_time_ms, payload,
|
||||||
rtp_video_header, 0);
|
rtp_video_header, 0, {});
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -228,7 +228,7 @@ class RtpRtcpImplTest : public ::testing::Test {
|
|||||||
const uint8_t payload[100] = {0};
|
const uint8_t payload[100] = {0};
|
||||||
EXPECT_TRUE(module->impl_->OnSendingRtpFrame(0, 0, kPayloadType, true));
|
EXPECT_TRUE(module->impl_->OnSendingRtpFrame(0, 0, kPayloadType, true));
|
||||||
EXPECT_TRUE(sender->SendVideo(kPayloadType, VideoCodecType::kVideoCodecVP8,
|
EXPECT_TRUE(sender->SendVideo(kPayloadType, VideoCodecType::kVideoCodecVP8,
|
||||||
0, 0, payload, rtp_video_header, 0));
|
0, 0, payload, rtp_video_header, 0, {}));
|
||||||
}
|
}
|
||||||
|
|
||||||
void IncomingRtcpNack(const RtpRtcpModule* module, uint16_t sequence_number) {
|
void IncomingRtcpNack(const RtpRtcpModule* module, uint16_t sequence_number) {
|
||||||
|
|||||||
@ -521,6 +521,7 @@ std::unique_ptr<RtpPacketToSend> RTPSender::AllocatePacket() const {
|
|||||||
&rtp_header_extension_map_, max_packet_size_ + kExtraCapacity);
|
&rtp_header_extension_map_, max_packet_size_ + kExtraCapacity);
|
||||||
packet->SetSsrc(ssrc_);
|
packet->SetSsrc(ssrc_);
|
||||||
packet->SetCsrcs(csrcs_);
|
packet->SetCsrcs(csrcs_);
|
||||||
|
|
||||||
// Reserve extensions, if registered, RtpSender set in SendToNetwork.
|
// Reserve extensions, if registered, RtpSender set in SendToNetwork.
|
||||||
packet->ReserveExtension<AbsoluteSendTime>();
|
packet->ReserveExtension<AbsoluteSendTime>();
|
||||||
packet->ReserveExtension<TransmissionOffset>();
|
packet->ReserveExtension<TransmissionOffset>();
|
||||||
@ -582,6 +583,11 @@ void RTPSender::SetMid(absl::string_view mid) {
|
|||||||
UpdateHeaderSizes();
|
UpdateHeaderSizes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> RTPSender::Csrcs() const {
|
||||||
|
MutexLock lock(&send_mutex_);
|
||||||
|
return csrcs_;
|
||||||
|
}
|
||||||
|
|
||||||
void RTPSender::SetCsrcs(const std::vector<uint32_t>& csrcs) {
|
void RTPSender::SetCsrcs(const std::vector<uint32_t>& csrcs) {
|
||||||
RTC_DCHECK_LE(csrcs.size(), kRtpCsrcSize);
|
RTC_DCHECK_LE(csrcs.size(), kRtpCsrcSize);
|
||||||
MutexLock lock(&send_mutex_);
|
MutexLock lock(&send_mutex_);
|
||||||
|
|||||||
@ -63,6 +63,7 @@ class RTPSender {
|
|||||||
uint16_t SequenceNumber() const RTC_LOCKS_EXCLUDED(send_mutex_);
|
uint16_t SequenceNumber() const RTC_LOCKS_EXCLUDED(send_mutex_);
|
||||||
void SetSequenceNumber(uint16_t seq) RTC_LOCKS_EXCLUDED(send_mutex_);
|
void SetSequenceNumber(uint16_t seq) RTC_LOCKS_EXCLUDED(send_mutex_);
|
||||||
|
|
||||||
|
std::vector<uint32_t> Csrcs() const;
|
||||||
void SetCsrcs(const std::vector<uint32_t>& csrcs)
|
void SetCsrcs(const std::vector<uint32_t>& csrcs)
|
||||||
RTC_LOCKS_EXCLUDED(send_mutex_);
|
RTC_LOCKS_EXCLUDED(send_mutex_);
|
||||||
|
|
||||||
|
|||||||
@ -1348,7 +1348,7 @@ TEST_F(RtpSenderTest, MarksPacketsWithKeyframeStatus) {
|
|||||||
EXPECT_TRUE(rtp_sender_video.SendVideo(
|
EXPECT_TRUE(rtp_sender_video.SendVideo(
|
||||||
kPayloadType, kCodecType,
|
kPayloadType, kCodecType,
|
||||||
capture_time_ms * kCaptureTimeMsToRtpTimestamp, capture_time_ms,
|
capture_time_ms * kCaptureTimeMsToRtpTimestamp, capture_time_ms,
|
||||||
kPayloadData, video_header, kDefaultExpectedRetransmissionTimeMs));
|
kPayloadData, video_header, kDefaultExpectedRetransmissionTimeMs, {}));
|
||||||
|
|
||||||
time_controller_.AdvanceTime(TimeDelta::Millis(33));
|
time_controller_.AdvanceTime(TimeDelta::Millis(33));
|
||||||
}
|
}
|
||||||
@ -1364,7 +1364,7 @@ TEST_F(RtpSenderTest, MarksPacketsWithKeyframeStatus) {
|
|||||||
EXPECT_TRUE(rtp_sender_video.SendVideo(
|
EXPECT_TRUE(rtp_sender_video.SendVideo(
|
||||||
kPayloadType, kCodecType,
|
kPayloadType, kCodecType,
|
||||||
capture_time_ms * kCaptureTimeMsToRtpTimestamp, capture_time_ms,
|
capture_time_ms * kCaptureTimeMsToRtpTimestamp, capture_time_ms,
|
||||||
kPayloadData, video_header, kDefaultExpectedRetransmissionTimeMs));
|
kPayloadData, video_header, kDefaultExpectedRetransmissionTimeMs, {}));
|
||||||
|
|
||||||
time_controller_.AdvanceTime(TimeDelta::Millis(33));
|
time_controller_.AdvanceTime(TimeDelta::Millis(33));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -171,6 +171,7 @@ RTPSenderVideo::RTPSenderVideo(const Config& config)
|
|||||||
this,
|
this,
|
||||||
config.frame_transformer,
|
config.frame_transformer,
|
||||||
rtp_sender_->SSRC(),
|
rtp_sender_->SSRC(),
|
||||||
|
rtp_sender_->Csrcs(),
|
||||||
config.task_queue_factory)
|
config.task_queue_factory)
|
||||||
: nullptr),
|
: nullptr),
|
||||||
include_capture_clock_offset_(!absl::StartsWith(
|
include_capture_clock_offset_(!absl::StartsWith(
|
||||||
@ -474,7 +475,8 @@ bool RTPSenderVideo::SendVideo(
|
|||||||
int64_t capture_time_ms,
|
int64_t capture_time_ms,
|
||||||
rtc::ArrayView<const uint8_t> payload,
|
rtc::ArrayView<const uint8_t> payload,
|
||||||
RTPVideoHeader video_header,
|
RTPVideoHeader video_header,
|
||||||
absl::optional<int64_t> expected_retransmission_time_ms) {
|
absl::optional<int64_t> expected_retransmission_time_ms,
|
||||||
|
std::vector<uint32_t> csrcs) {
|
||||||
TRACE_EVENT_ASYNC_STEP1("webrtc", "Video", capture_time_ms, "Send", "type",
|
TRACE_EVENT_ASYNC_STEP1("webrtc", "Video", capture_time_ms, "Send", "type",
|
||||||
FrameTypeToString(video_header.frame_type));
|
FrameTypeToString(video_header.frame_type));
|
||||||
RTC_CHECK_RUNS_SERIALIZED(&send_checker_);
|
RTC_CHECK_RUNS_SERIALIZED(&send_checker_);
|
||||||
@ -484,6 +486,7 @@ bool RTPSenderVideo::SendVideo(
|
|||||||
|
|
||||||
if (payload.empty())
|
if (payload.empty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!rtp_sender_->SendingMedia()) {
|
if (!rtp_sender_->SendingMedia()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -530,6 +533,8 @@ bool RTPSenderVideo::SendVideo(
|
|||||||
capture_time = Timestamp::Millis(capture_time_ms);
|
capture_time = Timestamp::Millis(capture_time_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rtp_sender_->SetCsrcs(std::move(csrcs));
|
||||||
|
|
||||||
std::unique_ptr<RtpPacketToSend> single_packet =
|
std::unique_ptr<RtpPacketToSend> single_packet =
|
||||||
rtp_sender_->AllocatePacket();
|
rtp_sender_->AllocatePacket();
|
||||||
RTC_DCHECK_LE(packet_capacity, single_packet->capacity());
|
RTC_DCHECK_LE(packet_capacity, single_packet->capacity());
|
||||||
@ -778,7 +783,7 @@ bool RTPSenderVideo::SendEncodedImage(
|
|||||||
}
|
}
|
||||||
return SendVideo(payload_type, codec_type, rtp_timestamp,
|
return SendVideo(payload_type, codec_type, rtp_timestamp,
|
||||||
encoded_image.capture_time_ms_, encoded_image, video_header,
|
encoded_image.capture_time_ms_, encoded_image, video_header,
|
||||||
expected_retransmission_time_ms);
|
expected_retransmission_time_ms, rtp_sender_->Csrcs());
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t RTPSenderVideo::PacketizationOverheadBps() const {
|
uint32_t RTPSenderVideo::PacketizationOverheadBps() const {
|
||||||
|
|||||||
@ -98,7 +98,8 @@ class RTPSenderVideo {
|
|||||||
int64_t capture_time_ms,
|
int64_t capture_time_ms,
|
||||||
rtc::ArrayView<const uint8_t> payload,
|
rtc::ArrayView<const uint8_t> payload,
|
||||||
RTPVideoHeader video_header,
|
RTPVideoHeader video_header,
|
||||||
absl::optional<int64_t> expected_retransmission_time_ms);
|
absl::optional<int64_t> expected_retransmission_time_ms,
|
||||||
|
std::vector<uint32_t> csrcs = {});
|
||||||
|
|
||||||
bool SendEncodedImage(
|
bool SendEncodedImage(
|
||||||
int payload_type,
|
int payload_type,
|
||||||
|
|||||||
@ -31,7 +31,8 @@ class TransformableVideoSenderFrame : public TransformableVideoFrameInterface {
|
|||||||
absl::optional<VideoCodecType> codec_type,
|
absl::optional<VideoCodecType> codec_type,
|
||||||
uint32_t rtp_timestamp,
|
uint32_t rtp_timestamp,
|
||||||
absl::optional<int64_t> expected_retransmission_time_ms,
|
absl::optional<int64_t> expected_retransmission_time_ms,
|
||||||
uint32_t ssrc)
|
uint32_t ssrc,
|
||||||
|
std::vector<uint32_t> csrcs)
|
||||||
: encoded_data_(encoded_image.GetEncodedData()),
|
: encoded_data_(encoded_image.GetEncodedData()),
|
||||||
header_(video_header),
|
header_(video_header),
|
||||||
metadata_(header_.GetAsMetadata()),
|
metadata_(header_.GetAsMetadata()),
|
||||||
@ -44,6 +45,7 @@ class TransformableVideoSenderFrame : public TransformableVideoFrameInterface {
|
|||||||
ssrc_(ssrc) {
|
ssrc_(ssrc) {
|
||||||
RTC_DCHECK_GE(payload_type_, 0);
|
RTC_DCHECK_GE(payload_type_, 0);
|
||||||
RTC_DCHECK_LE(payload_type_, 127);
|
RTC_DCHECK_LE(payload_type_, 127);
|
||||||
|
metadata_.SetCsrcs(std::move(csrcs));
|
||||||
}
|
}
|
||||||
|
|
||||||
~TransformableVideoSenderFrame() override = default;
|
~TransformableVideoSenderFrame() override = default;
|
||||||
@ -71,9 +73,12 @@ class TransformableVideoSenderFrame : public TransformableVideoFrameInterface {
|
|||||||
const VideoFrameMetadata& GetMetadata() const override { return metadata_; }
|
const VideoFrameMetadata& GetMetadata() const override { return metadata_; }
|
||||||
void SetMetadata(const VideoFrameMetadata& metadata) override {
|
void SetMetadata(const VideoFrameMetadata& metadata) override {
|
||||||
header_.SetFromMetadata(metadata);
|
header_.SetFromMetadata(metadata);
|
||||||
|
std::vector<uint32_t> csrcs = metadata.GetCsrcs();
|
||||||
|
|
||||||
// We have to keep a local copy because GetMetadata() has to return a
|
// We have to keep a local copy because GetMetadata() has to return a
|
||||||
// reference.
|
// reference.
|
||||||
metadata_ = header_.GetAsMetadata();
|
metadata_ = header_.GetAsMetadata();
|
||||||
|
metadata_.SetCsrcs(std::move(csrcs));
|
||||||
}
|
}
|
||||||
|
|
||||||
const RTPVideoHeader& GetHeader() const { return header_; }
|
const RTPVideoHeader& GetHeader() const { return header_; }
|
||||||
@ -109,10 +114,12 @@ RTPSenderVideoFrameTransformerDelegate::RTPSenderVideoFrameTransformerDelegate(
|
|||||||
RTPSenderVideo* sender,
|
RTPSenderVideo* sender,
|
||||||
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
|
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
|
||||||
uint32_t ssrc,
|
uint32_t ssrc,
|
||||||
|
std::vector<uint32_t> csrcs,
|
||||||
TaskQueueFactory* task_queue_factory)
|
TaskQueueFactory* task_queue_factory)
|
||||||
: sender_(sender),
|
: sender_(sender),
|
||||||
frame_transformer_(std::move(frame_transformer)),
|
frame_transformer_(std::move(frame_transformer)),
|
||||||
ssrc_(ssrc),
|
ssrc_(ssrc),
|
||||||
|
csrcs_(csrcs),
|
||||||
transformation_queue_(task_queue_factory->CreateTaskQueue(
|
transformation_queue_(task_queue_factory->CreateTaskQueue(
|
||||||
"video_frame_transformer",
|
"video_frame_transformer",
|
||||||
TaskQueueFactory::Priority::NORMAL)) {}
|
TaskQueueFactory::Priority::NORMAL)) {}
|
||||||
@ -131,7 +138,7 @@ bool RTPSenderVideoFrameTransformerDelegate::TransformFrame(
|
|||||||
absl::optional<int64_t> expected_retransmission_time_ms) {
|
absl::optional<int64_t> expected_retransmission_time_ms) {
|
||||||
frame_transformer_->Transform(std::make_unique<TransformableVideoSenderFrame>(
|
frame_transformer_->Transform(std::make_unique<TransformableVideoSenderFrame>(
|
||||||
encoded_image, video_header, payload_type, codec_type, rtp_timestamp,
|
encoded_image, video_header, payload_type, codec_type, rtp_timestamp,
|
||||||
expected_retransmission_time_ms, ssrc_));
|
expected_retransmission_time_ms, ssrc_, csrcs_));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,13 +167,14 @@ void RTPSenderVideoFrameTransformerDelegate::SendVideo(
|
|||||||
return;
|
return;
|
||||||
auto* transformed_video_frame =
|
auto* transformed_video_frame =
|
||||||
static_cast<TransformableVideoSenderFrame*>(transformed_frame.get());
|
static_cast<TransformableVideoSenderFrame*>(transformed_frame.get());
|
||||||
sender_->SendVideo(
|
sender_->SendVideo(transformed_video_frame->GetPayloadType(),
|
||||||
transformed_video_frame->GetPayloadType(),
|
transformed_video_frame->GetCodecType(),
|
||||||
transformed_video_frame->GetCodecType(),
|
transformed_video_frame->GetTimestamp(),
|
||||||
transformed_video_frame->GetTimestamp(),
|
transformed_video_frame->GetCaptureTimeMs(),
|
||||||
transformed_video_frame->GetCaptureTimeMs(),
|
transformed_video_frame->GetData(),
|
||||||
transformed_video_frame->GetData(), transformed_video_frame->GetHeader(),
|
transformed_video_frame->GetHeader(),
|
||||||
transformed_video_frame->GetExpectedRetransmissionTimeMs());
|
transformed_video_frame->GetExpectedRetransmissionTimeMs(),
|
||||||
|
transformed_video_frame->GetMetadata().GetCsrcs());
|
||||||
}
|
}
|
||||||
|
|
||||||
void RTPSenderVideoFrameTransformerDelegate::SetVideoStructureUnderLock(
|
void RTPSenderVideoFrameTransformerDelegate::SetVideoStructureUnderLock(
|
||||||
@ -221,7 +229,7 @@ std::unique_ptr<TransformableVideoFrameInterface> CloneSenderVideoFrame(
|
|||||||
encoded_image, new_header, original->GetPayloadType(), new_codec_type,
|
encoded_image, new_header, original->GetPayloadType(), new_codec_type,
|
||||||
original->GetTimestamp(),
|
original->GetTimestamp(),
|
||||||
absl::nullopt, // expected_retransmission_time_ms
|
absl::nullopt, // expected_retransmission_time_ms
|
||||||
original->GetSsrc());
|
original->GetSsrc(), original->GetMetadata().GetCsrcs());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
#define MODULES_RTP_RTCP_SOURCE_RTP_SENDER_VIDEO_FRAME_TRANSFORMER_DELEGATE_H_
|
#define MODULES_RTP_RTCP_SOURCE_RTP_SENDER_VIDEO_FRAME_TRANSFORMER_DELEGATE_H_
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "api/frame_transformer_interface.h"
|
#include "api/frame_transformer_interface.h"
|
||||||
#include "api/scoped_refptr.h"
|
#include "api/scoped_refptr.h"
|
||||||
@ -34,6 +35,7 @@ class RTPSenderVideoFrameTransformerDelegate : public TransformedFrameCallback {
|
|||||||
RTPSenderVideo* sender,
|
RTPSenderVideo* sender,
|
||||||
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
|
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer,
|
||||||
uint32_t ssrc,
|
uint32_t ssrc,
|
||||||
|
std::vector<uint32_t> csrcs,
|
||||||
TaskQueueFactory* send_transport_queue);
|
TaskQueueFactory* send_transport_queue);
|
||||||
|
|
||||||
void Init();
|
void Init();
|
||||||
@ -80,6 +82,7 @@ class RTPSenderVideoFrameTransformerDelegate : public TransformedFrameCallback {
|
|||||||
RTPSenderVideo* sender_ RTC_GUARDED_BY(sender_lock_);
|
RTPSenderVideo* sender_ RTC_GUARDED_BY(sender_lock_);
|
||||||
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer_;
|
rtc::scoped_refptr<FrameTransformerInterface> frame_transformer_;
|
||||||
const uint32_t ssrc_;
|
const uint32_t ssrc_;
|
||||||
|
std::vector<uint32_t> csrcs_;
|
||||||
// Used when the encoded frames arrives without a current task queue. This can
|
// Used when the encoded frames arrives without a current task queue. This can
|
||||||
// happen if a hardware encoder was used.
|
// happen if a hardware encoder was used.
|
||||||
std::unique_ptr<TaskQueueBase, TaskQueueDeleter> transformation_queue_;
|
std::unique_ptr<TaskQueueBase, TaskQueueDeleter> transformation_queue_;
|
||||||
|
|||||||
@ -215,7 +215,7 @@ TEST_F(RtpSenderVideoTest, KeyFrameHasCVO) {
|
|||||||
hdr.rotation = kVideoRotation_0;
|
hdr.rotation = kVideoRotation_0;
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
|
|
||||||
VideoRotation rotation;
|
VideoRotation rotation;
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
@ -242,7 +242,7 @@ TEST_F(RtpSenderVideoTest, TimingFrameHasPacketizationTimstampSet) {
|
|||||||
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, kCaptureTimestamp,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, kCaptureTimestamp,
|
||||||
kFrame, hdr,
|
kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
VideoSendTiming timing;
|
VideoSendTiming timing;
|
||||||
EXPECT_TRUE(transport_.last_sent_packet().GetExtension<VideoTimingExtension>(
|
EXPECT_TRUE(transport_.last_sent_packet().GetExtension<VideoTimingExtension>(
|
||||||
&timing));
|
&timing));
|
||||||
@ -261,13 +261,13 @@ TEST_F(RtpSenderVideoTest, DeltaFrameHasCVOWhenChanged) {
|
|||||||
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs));
|
kDefaultExpectedRetransmissionTimeMs, {}));
|
||||||
|
|
||||||
hdr.rotation = kVideoRotation_0;
|
hdr.rotation = kVideoRotation_0;
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(rtp_sender_video_->SendVideo(
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp + 1, 0, kFrame,
|
kPayload, kType, kTimestamp + 1, 0, kFrame, hdr,
|
||||||
hdr, kDefaultExpectedRetransmissionTimeMs));
|
kDefaultExpectedRetransmissionTimeMs, {}));
|
||||||
|
|
||||||
VideoRotation rotation;
|
VideoRotation rotation;
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
@ -285,12 +285,12 @@ TEST_F(RtpSenderVideoTest, DeltaFrameHasCVOWhenNonZero) {
|
|||||||
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs));
|
kDefaultExpectedRetransmissionTimeMs, {}));
|
||||||
|
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(rtp_sender_video_->SendVideo(
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp + 1, 0, kFrame,
|
kPayload, kType, kTimestamp + 1, 0, kFrame, hdr,
|
||||||
hdr, kDefaultExpectedRetransmissionTimeMs));
|
kDefaultExpectedRetransmissionTimeMs, {}));
|
||||||
|
|
||||||
VideoRotation rotation;
|
VideoRotation rotation;
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
@ -529,7 +529,7 @@ TEST_F(RtpSenderVideoTest, SendsDependencyDescriptorWhenVideoStructureIsSet) {
|
|||||||
DecodeTargetIndication::kSwitch};
|
DecodeTargetIndication::kSwitch};
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
|
|
||||||
ASSERT_EQ(transport_.packets_sent(), 1);
|
ASSERT_EQ(transport_.packets_sent(), 1);
|
||||||
DependencyDescriptor descriptor_key;
|
DependencyDescriptor descriptor_key;
|
||||||
@ -555,7 +555,7 @@ TEST_F(RtpSenderVideoTest, SendsDependencyDescriptorWhenVideoStructureIsSet) {
|
|||||||
DecodeTargetIndication::kRequired};
|
DecodeTargetIndication::kRequired};
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
|
|
||||||
EXPECT_EQ(transport_.packets_sent(), 2);
|
EXPECT_EQ(transport_.packets_sent(), 2);
|
||||||
DependencyDescriptor descriptor_delta;
|
DependencyDescriptor descriptor_delta;
|
||||||
@ -604,7 +604,7 @@ TEST_F(RtpSenderVideoTest,
|
|||||||
DecodeTargetIndication::kSwitch};
|
DecodeTargetIndication::kSwitch};
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
|
|
||||||
ASSERT_EQ(transport_.packets_sent(), 1);
|
ASSERT_EQ(transport_.packets_sent(), 1);
|
||||||
DependencyDescriptor descriptor_key;
|
DependencyDescriptor descriptor_key;
|
||||||
@ -620,7 +620,7 @@ TEST_F(RtpSenderVideoTest,
|
|||||||
DecodeTargetIndication::kRequired};
|
DecodeTargetIndication::kRequired};
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
|
|
||||||
EXPECT_EQ(transport_.packets_sent(), 2);
|
EXPECT_EQ(transport_.packets_sent(), 2);
|
||||||
EXPECT_FALSE(transport_.last_sent_packet()
|
EXPECT_FALSE(transport_.last_sent_packet()
|
||||||
@ -649,7 +649,7 @@ TEST_F(RtpSenderVideoTest, PropagatesChainDiffsIntoDependencyDescriptor) {
|
|||||||
generic.chain_diffs = {2};
|
generic.chain_diffs = {2};
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
|
|
||||||
ASSERT_EQ(transport_.packets_sent(), 1);
|
ASSERT_EQ(transport_.packets_sent(), 1);
|
||||||
DependencyDescriptor descriptor_key;
|
DependencyDescriptor descriptor_key;
|
||||||
@ -684,7 +684,7 @@ TEST_F(RtpSenderVideoTest,
|
|||||||
generic.chain_diffs = {1};
|
generic.chain_diffs = {1};
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
|
|
||||||
ASSERT_EQ(transport_.packets_sent(), 1);
|
ASSERT_EQ(transport_.packets_sent(), 1);
|
||||||
DependencyDescriptor descriptor_key;
|
DependencyDescriptor descriptor_key;
|
||||||
@ -722,7 +722,7 @@ TEST_F(RtpSenderVideoTest,
|
|||||||
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
rtp_sender_video_->SetVideoStructure(&video_structure1);
|
rtp_sender_video_->SetVideoStructure(&video_structure1);
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
// Parse 1st extension.
|
// Parse 1st extension.
|
||||||
ASSERT_EQ(transport_.packets_sent(), 1);
|
ASSERT_EQ(transport_.packets_sent(), 1);
|
||||||
DependencyDescriptor descriptor_key1;
|
DependencyDescriptor descriptor_key1;
|
||||||
@ -738,7 +738,7 @@ TEST_F(RtpSenderVideoTest,
|
|||||||
DecodeTargetIndication::kNotPresent};
|
DecodeTargetIndication::kNotPresent};
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
|
|
||||||
ASSERT_EQ(transport_.packets_sent(), 2);
|
ASSERT_EQ(transport_.packets_sent(), 2);
|
||||||
RtpPacket delta_packet = transport_.last_sent_packet();
|
RtpPacket delta_packet = transport_.last_sent_packet();
|
||||||
@ -750,7 +750,7 @@ TEST_F(RtpSenderVideoTest,
|
|||||||
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
rtp_sender_video_->SetVideoStructure(&video_structure2);
|
rtp_sender_video_->SetVideoStructure(&video_structure2);
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
// Parse the 2nd key frame.
|
// Parse the 2nd key frame.
|
||||||
ASSERT_EQ(transport_.packets_sent(), 3);
|
ASSERT_EQ(transport_.packets_sent(), 3);
|
||||||
DependencyDescriptor descriptor_key2;
|
DependencyDescriptor descriptor_key2;
|
||||||
@ -804,7 +804,7 @@ TEST_F(RtpSenderVideoTest,
|
|||||||
EXPECT_CALL(*encryptor,
|
EXPECT_CALL(*encryptor,
|
||||||
Encrypt(_, _, Not(IsEmpty()), ElementsAreArray(kFrame), _, _));
|
Encrypt(_, _, Not(IsEmpty()), ElementsAreArray(kFrame), _, _));
|
||||||
rtp_sender_video.SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video.SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
// Double check packet with the dependency descriptor is sent.
|
// Double check packet with the dependency descriptor is sent.
|
||||||
ASSERT_EQ(transport_.packets_sent(), 1);
|
ASSERT_EQ(transport_.packets_sent(), 1);
|
||||||
EXPECT_TRUE(transport_.last_sent_packet()
|
EXPECT_TRUE(transport_.last_sent_packet()
|
||||||
@ -826,7 +826,7 @@ TEST_F(RtpSenderVideoTest, PopulateGenericFrameDescriptor) {
|
|||||||
generic.dependencies.push_back(kFrameId - 500);
|
generic.dependencies.push_back(kFrameId - 500);
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
|
|
||||||
RtpGenericFrameDescriptor descriptor_wire;
|
RtpGenericFrameDescriptor descriptor_wire;
|
||||||
EXPECT_EQ(1, transport_.packets_sent());
|
EXPECT_EQ(1, transport_.packets_sent());
|
||||||
@ -861,7 +861,7 @@ void RtpSenderVideoTest::
|
|||||||
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
||||||
rtp_sender_video_->SendVideo(kPayload, VideoCodecType::kVideoCodecVP8,
|
rtp_sender_video_->SendVideo(kPayload, VideoCodecType::kVideoCodecVP8,
|
||||||
kTimestamp, 0, kFrame, hdr,
|
kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
|
|
||||||
ASSERT_EQ(transport_.packets_sent(), 1);
|
ASSERT_EQ(transport_.packets_sent(), 1);
|
||||||
// Expect only minimal 1-byte vp8 descriptor was generated.
|
// Expect only minimal 1-byte vp8 descriptor was generated.
|
||||||
@ -898,7 +898,7 @@ TEST_F(RtpSenderVideoTest, VideoLayersAllocationWithResolutionSentOnKeyFrames) {
|
|||||||
RTPVideoHeader hdr;
|
RTPVideoHeader hdr;
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
|
|
||||||
VideoLayersAllocation sent_allocation;
|
VideoLayersAllocation sent_allocation;
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
@ -908,7 +908,7 @@ TEST_F(RtpSenderVideoTest, VideoLayersAllocationWithResolutionSentOnKeyFrames) {
|
|||||||
|
|
||||||
// Next key frame also have the allocation.
|
// Next key frame also have the allocation.
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
transport_.last_sent_packet()
|
transport_.last_sent_packet()
|
||||||
.GetExtension<RtpVideoLayersAllocationExtension>(&sent_allocation));
|
.GetExtension<RtpVideoLayersAllocationExtension>(&sent_allocation));
|
||||||
@ -935,21 +935,21 @@ TEST_F(RtpSenderVideoTest,
|
|||||||
RTPVideoHeader hdr;
|
RTPVideoHeader hdr;
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
EXPECT_TRUE(transport_.last_sent_packet()
|
EXPECT_TRUE(transport_.last_sent_packet()
|
||||||
.HasExtension<RtpVideoLayersAllocationExtension>());
|
.HasExtension<RtpVideoLayersAllocationExtension>());
|
||||||
|
|
||||||
// No allocation sent on delta frame unless it has been updated.
|
// No allocation sent on delta frame unless it has been updated.
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
EXPECT_FALSE(transport_.last_sent_packet()
|
EXPECT_FALSE(transport_.last_sent_packet()
|
||||||
.HasExtension<RtpVideoLayersAllocationExtension>());
|
.HasExtension<RtpVideoLayersAllocationExtension>());
|
||||||
|
|
||||||
// Update the allocation.
|
// Update the allocation.
|
||||||
rtp_sender_video_->SetVideoLayersAllocation(allocation);
|
rtp_sender_video_->SetVideoLayersAllocation(allocation);
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
|
|
||||||
VideoLayersAllocation sent_allocation;
|
VideoLayersAllocation sent_allocation;
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
@ -984,7 +984,7 @@ TEST_F(RtpSenderVideoTest,
|
|||||||
RTPVideoHeader hdr;
|
RTPVideoHeader hdr;
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
ASSERT_TRUE(transport_.last_sent_packet()
|
ASSERT_TRUE(transport_.last_sent_packet()
|
||||||
.HasExtension<RtpVideoLayersAllocationExtension>());
|
.HasExtension<RtpVideoLayersAllocationExtension>());
|
||||||
|
|
||||||
@ -998,7 +998,7 @@ TEST_F(RtpSenderVideoTest,
|
|||||||
rtp_sender_video_->SetVideoLayersAllocation(allocation);
|
rtp_sender_video_->SetVideoLayersAllocation(allocation);
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
|
|
||||||
VideoLayersAllocation sent_allocation;
|
VideoLayersAllocation sent_allocation;
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
@ -1031,7 +1031,7 @@ TEST_F(RtpSenderVideoTest,
|
|||||||
RTPVideoHeader hdr;
|
RTPVideoHeader hdr;
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
ASSERT_TRUE(transport_.last_sent_packet()
|
ASSERT_TRUE(transport_.last_sent_packet()
|
||||||
.HasExtension<RtpVideoLayersAllocationExtension>());
|
.HasExtension<RtpVideoLayersAllocationExtension>());
|
||||||
|
|
||||||
@ -1040,7 +1040,7 @@ TEST_F(RtpSenderVideoTest,
|
|||||||
rtp_sender_video_->SetVideoLayersAllocation(allocation);
|
rtp_sender_video_->SetVideoLayersAllocation(allocation);
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
|
|
||||||
VideoLayersAllocation sent_allocation;
|
VideoLayersAllocation sent_allocation;
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
@ -1073,7 +1073,7 @@ TEST_F(RtpSenderVideoTest,
|
|||||||
RTPVideoHeader hdr;
|
RTPVideoHeader hdr;
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
ASSERT_TRUE(transport_.last_sent_packet()
|
ASSERT_TRUE(transport_.last_sent_packet()
|
||||||
.HasExtension<RtpVideoLayersAllocationExtension>());
|
.HasExtension<RtpVideoLayersAllocationExtension>());
|
||||||
|
|
||||||
@ -1082,7 +1082,7 @@ TEST_F(RtpSenderVideoTest,
|
|||||||
rtp_sender_video_->SetVideoLayersAllocation(allocation);
|
rtp_sender_video_->SetVideoLayersAllocation(allocation);
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
|
|
||||||
VideoLayersAllocation sent_allocation;
|
VideoLayersAllocation sent_allocation;
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
@ -1110,7 +1110,7 @@ TEST_F(RtpSenderVideoTest, VideoLayersAllocationSentOnDeltaFramesOnlyOnUpdate) {
|
|||||||
RTPVideoHeader hdr;
|
RTPVideoHeader hdr;
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
|
|
||||||
VideoLayersAllocation sent_allocation;
|
VideoLayersAllocation sent_allocation;
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
@ -1120,14 +1120,14 @@ TEST_F(RtpSenderVideoTest, VideoLayersAllocationSentOnDeltaFramesOnlyOnUpdate) {
|
|||||||
|
|
||||||
// VideoLayersAllocation not sent on the next delta frame.
|
// VideoLayersAllocation not sent on the next delta frame.
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
EXPECT_FALSE(transport_.last_sent_packet()
|
EXPECT_FALSE(transport_.last_sent_packet()
|
||||||
.HasExtension<RtpVideoLayersAllocationExtension>());
|
.HasExtension<RtpVideoLayersAllocationExtension>());
|
||||||
|
|
||||||
// Update allocation. VideoLayesAllocation should be sent on the next frame.
|
// Update allocation. VideoLayesAllocation should be sent on the next frame.
|
||||||
rtp_sender_video_->SetVideoLayersAllocation(allocation);
|
rtp_sender_video_->SetVideoLayersAllocation(allocation);
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
EXPECT_TRUE(
|
EXPECT_TRUE(
|
||||||
transport_.last_sent_packet()
|
transport_.last_sent_packet()
|
||||||
.GetExtension<RtpVideoLayersAllocationExtension>(&sent_allocation));
|
.GetExtension<RtpVideoLayersAllocationExtension>(&sent_allocation));
|
||||||
@ -1157,14 +1157,14 @@ TEST_F(RtpSenderVideoTest, VideoLayersAllocationNotSentOnHigherTemporalLayers) {
|
|||||||
vp8_header.temporalIdx = 1;
|
vp8_header.temporalIdx = 1;
|
||||||
|
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
EXPECT_FALSE(transport_.last_sent_packet()
|
EXPECT_FALSE(transport_.last_sent_packet()
|
||||||
.HasExtension<RtpVideoLayersAllocationExtension>());
|
.HasExtension<RtpVideoLayersAllocationExtension>());
|
||||||
|
|
||||||
// Send a delta frame on tl0.
|
// Send a delta frame on tl0.
|
||||||
vp8_header.temporalIdx = 0;
|
vp8_header.temporalIdx = 0;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
EXPECT_TRUE(transport_.last_sent_packet()
|
EXPECT_TRUE(transport_.last_sent_packet()
|
||||||
.HasExtension<RtpVideoLayersAllocationExtension>());
|
.HasExtension<RtpVideoLayersAllocationExtension>());
|
||||||
}
|
}
|
||||||
@ -1179,7 +1179,7 @@ TEST_F(RtpSenderVideoTest, AbsoluteCaptureTime) {
|
|||||||
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp,
|
||||||
kAbsoluteCaptureTimestampMs, kFrame, hdr,
|
kAbsoluteCaptureTimestampMs, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
|
|
||||||
absl::optional<AbsoluteCaptureTime> absolute_capture_time;
|
absl::optional<AbsoluteCaptureTime> absolute_capture_time;
|
||||||
|
|
||||||
@ -1214,7 +1214,7 @@ TEST_F(RtpSenderVideoTest,
|
|||||||
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp,
|
||||||
/*capture_time_ms=*/0, kFrame, hdr,
|
/*capture_time_ms=*/0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
// No absolute capture time should be set as the capture_time_ms was the
|
// No absolute capture time should be set as the capture_time_ms was the
|
||||||
// default value.
|
// default value.
|
||||||
for (const RtpPacketReceived& packet : transport_.sent_packets()) {
|
for (const RtpPacketReceived& packet : transport_.sent_packets()) {
|
||||||
@ -1238,7 +1238,7 @@ TEST_F(RtpSenderVideoTest, AbsoluteCaptureTimeWithCaptureClockOffset) {
|
|||||||
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp,
|
||||||
kAbsoluteCaptureTimestampMs, kFrame, hdr,
|
kAbsoluteCaptureTimestampMs, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
|
|
||||||
absl::optional<AbsoluteCaptureTime> absolute_capture_time;
|
absl::optional<AbsoluteCaptureTime> absolute_capture_time;
|
||||||
|
|
||||||
@ -1276,7 +1276,7 @@ TEST_F(RtpSenderVideoTest, AbsoluteCaptureTimeWithExtensionProvided) {
|
|||||||
hdr.absolute_capture_time = kAbsoluteCaptureTime;
|
hdr.absolute_capture_time = kAbsoluteCaptureTime;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp,
|
||||||
/*capture_time_ms=*/789, kFrame, hdr,
|
/*capture_time_ms=*/789, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
|
|
||||||
absl::optional<AbsoluteCaptureTime> absolute_capture_time;
|
absl::optional<AbsoluteCaptureTime> absolute_capture_time;
|
||||||
|
|
||||||
@ -1311,7 +1311,7 @@ TEST_F(RtpSenderVideoTest, PopulatesPlayoutDelay) {
|
|||||||
vp8_header.temporalIdx = 0;
|
vp8_header.temporalIdx = 0;
|
||||||
|
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
EXPECT_FALSE(
|
EXPECT_FALSE(
|
||||||
transport_.last_sent_packet().HasExtension<PlayoutDelayLimits>());
|
transport_.last_sent_packet().HasExtension<PlayoutDelayLimits>());
|
||||||
|
|
||||||
@ -1320,7 +1320,7 @@ TEST_F(RtpSenderVideoTest, PopulatesPlayoutDelay) {
|
|||||||
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
hdr.frame_type = VideoFrameType::kVideoFrameDelta;
|
||||||
vp8_header.temporalIdx = 1;
|
vp8_header.temporalIdx = 1;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
VideoPlayoutDelay received_delay = VideoPlayoutDelay();
|
VideoPlayoutDelay received_delay = VideoPlayoutDelay();
|
||||||
ASSERT_TRUE(transport_.last_sent_packet().GetExtension<PlayoutDelayLimits>(
|
ASSERT_TRUE(transport_.last_sent_packet().GetExtension<PlayoutDelayLimits>(
|
||||||
&received_delay));
|
&received_delay));
|
||||||
@ -1331,7 +1331,7 @@ TEST_F(RtpSenderVideoTest, PopulatesPlayoutDelay) {
|
|||||||
hdr.playout_delay = VideoPlayoutDelay(); // Indicates "no change".
|
hdr.playout_delay = VideoPlayoutDelay(); // Indicates "no change".
|
||||||
vp8_header.temporalIdx = 0;
|
vp8_header.temporalIdx = 0;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
ASSERT_TRUE(transport_.last_sent_packet().GetExtension<PlayoutDelayLimits>(
|
ASSERT_TRUE(transport_.last_sent_packet().GetExtension<PlayoutDelayLimits>(
|
||||||
&received_delay));
|
&received_delay));
|
||||||
EXPECT_EQ(received_delay, kExpectedDelay);
|
EXPECT_EQ(received_delay, kExpectedDelay);
|
||||||
@ -1339,14 +1339,14 @@ TEST_F(RtpSenderVideoTest, PopulatesPlayoutDelay) {
|
|||||||
// The next frame does not need the extensions since it's delivery has
|
// The next frame does not need the extensions since it's delivery has
|
||||||
// already been guaranteed.
|
// already been guaranteed.
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
EXPECT_FALSE(
|
EXPECT_FALSE(
|
||||||
transport_.last_sent_packet().HasExtension<PlayoutDelayLimits>());
|
transport_.last_sent_packet().HasExtension<PlayoutDelayLimits>());
|
||||||
|
|
||||||
// Insert key-frame, we need to refresh the state here.
|
// Insert key-frame, we need to refresh the state here.
|
||||||
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
hdr.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
rtp_sender_video_->SendVideo(kPayload, kType, kTimestamp, 0, kFrame, hdr,
|
||||||
kDefaultExpectedRetransmissionTimeMs);
|
kDefaultExpectedRetransmissionTimeMs, {});
|
||||||
ASSERT_TRUE(transport_.last_sent_packet().GetExtension<PlayoutDelayLimits>(
|
ASSERT_TRUE(transport_.last_sent_packet().GetExtension<PlayoutDelayLimits>(
|
||||||
&received_delay));
|
&received_delay));
|
||||||
EXPECT_EQ(received_delay, kExpectedDelay);
|
EXPECT_EQ(received_delay, kExpectedDelay);
|
||||||
@ -1362,7 +1362,7 @@ TEST_F(RtpSenderVideoTest, SendGenericVideo) {
|
|||||||
video_header.frame_type = VideoFrameType::kVideoFrameKey;
|
video_header.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
ASSERT_TRUE(rtp_sender_video_->SendVideo(kPayloadType, kCodecType, 1234, 4321,
|
ASSERT_TRUE(rtp_sender_video_->SendVideo(kPayloadType, kCodecType, 1234, 4321,
|
||||||
kPayload, video_header,
|
kPayload, video_header,
|
||||||
absl::nullopt));
|
absl::nullopt, {}));
|
||||||
|
|
||||||
rtc::ArrayView<const uint8_t> sent_payload =
|
rtc::ArrayView<const uint8_t> sent_payload =
|
||||||
transport_.last_sent_packet().payload();
|
transport_.last_sent_packet().payload();
|
||||||
@ -1376,7 +1376,7 @@ TEST_F(RtpSenderVideoTest, SendGenericVideo) {
|
|||||||
video_header.frame_type = VideoFrameType::kVideoFrameDelta;
|
video_header.frame_type = VideoFrameType::kVideoFrameDelta;
|
||||||
ASSERT_TRUE(rtp_sender_video_->SendVideo(kPayloadType, kCodecType, 1234, 4321,
|
ASSERT_TRUE(rtp_sender_video_->SendVideo(kPayloadType, kCodecType, 1234, 4321,
|
||||||
kDeltaPayload, video_header,
|
kDeltaPayload, video_header,
|
||||||
absl::nullopt));
|
absl::nullopt, {}));
|
||||||
|
|
||||||
sent_payload = sent_payload = transport_.last_sent_packet().payload();
|
sent_payload = sent_payload = transport_.last_sent_packet().payload();
|
||||||
generic_header = sent_payload[0];
|
generic_header = sent_payload[0];
|
||||||
@ -1394,7 +1394,7 @@ TEST_F(RtpSenderVideoTest, SendRawVideo) {
|
|||||||
video_header.frame_type = VideoFrameType::kVideoFrameKey;
|
video_header.frame_type = VideoFrameType::kVideoFrameKey;
|
||||||
ASSERT_TRUE(rtp_sender_video_->SendVideo(kPayloadType, absl::nullopt, 1234,
|
ASSERT_TRUE(rtp_sender_video_->SendVideo(kPayloadType, absl::nullopt, 1234,
|
||||||
4321, kPayload, video_header,
|
4321, kPayload, video_header,
|
||||||
absl::nullopt));
|
absl::nullopt, {}));
|
||||||
|
|
||||||
rtc::ArrayView<const uint8_t> sent_payload =
|
rtc::ArrayView<const uint8_t> sent_payload =
|
||||||
transport_.last_sent_packet().payload();
|
transport_.last_sent_packet().payload();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user