Add RequestKeyFrame with Pli to RtcpTransceiver

Add support for reduced size mode.

Bug: webrtc:8239
Change-Id: I1d646f0d7848af6632c9204ce5b96ae24cfc0ad3
Reviewed-on: https://webrtc-review.googlesource.com/23681
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20812}
This commit is contained in:
Danil Chapovalov
2017-11-21 11:08:53 +01:00
committed by Commit Bot
parent de939432dc
commit a7e418cd5f
7 changed files with 156 additions and 22 deletions

View File

@ -90,4 +90,18 @@ void RtcpTransceiver::UnsetRemb() {
}); });
} }
void RtcpTransceiver::RequestKeyFrame(std::vector<uint32_t> ssrcs) {
// TODO(danilchap): Replace with lambda with move capture when available.
struct RequestKeyFrameClosure {
void operator()() {
if (ptr)
ptr->RequestKeyFrame(ssrcs);
}
rtc::WeakPtr<RtcpTransceiverImpl> ptr;
std::vector<uint32_t> ssrcs;
};
task_queue_->PostTask(RequestKeyFrameClosure{ptr_, std::move(ssrcs)});
}
} // namespace webrtc } // namespace webrtc

View File

@ -44,6 +44,8 @@ class RtcpTransceiver {
// Stops sending REMB in following compound packets. // Stops sending REMB in following compound packets.
void UnsetRemb(); void UnsetRemb();
void RequestKeyFrame(std::vector<uint32_t> ssrcs);
private: private:
rtc::TaskQueue* const task_queue_; rtc::TaskQueue* const task_queue_;
std::unique_ptr<RtcpTransceiverImpl> rtcp_transceiver_; std::unique_ptr<RtcpTransceiverImpl> rtcp_transceiver_;

View File

@ -48,6 +48,11 @@ bool RtcpTransceiverConfig::Validate() const {
RTC_LOG(LS_ERROR) << debug_id << "outgoing transport must be set"; RTC_LOG(LS_ERROR) << debug_id << "outgoing transport must be set";
return false; return false;
} }
if (initial_report_delay_ms < 0) {
RTC_LOG(LS_ERROR) << debug_id << "delay " << initial_report_delay_ms
<< "ms before first report shouldn't be negative.";
return false;
}
if (report_period_ms <= 0) { if (report_period_ms <= 0) {
RTC_LOG(LS_ERROR) << debug_id << "period " << report_period_ms RTC_LOG(LS_ERROR) << debug_id << "period " << report_period_ms
<< "ms between reports should be positive."; << "ms between reports should be positive.";
@ -58,6 +63,10 @@ bool RtcpTransceiverConfig::Validate() const {
<< "missing task queue for periodic compound packets"; << "missing task queue for periodic compound packets";
return false; return false;
} }
if (rtcp_mode != RtcpMode::kCompound && rtcp_mode != RtcpMode::kReducedSize) {
RTC_LOG(LS_ERROR) << debug_id << "unsupported rtcp mode";
return false;
}
// TODO(danilchap): Remove or update the warning when RtcpTransceiver supports // TODO(danilchap): Remove or update the warning when RtcpTransceiver supports
// send-only sessions. // send-only sessions.
if (receive_statistics == nullptr) if (receive_statistics == nullptr)

View File

@ -13,6 +13,7 @@
#include <string> #include <string>
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "rtc_base/task_queue.h" #include "rtc_base/task_queue.h"
namespace webrtc { namespace webrtc {
@ -51,6 +52,11 @@ struct RtcpTransceiverConfig {
// Rtcp report block generator for outgoing receiver reports. // Rtcp report block generator for outgoing receiver reports.
ReceiveStatisticsProvider* receive_statistics = nullptr; ReceiveStatisticsProvider* receive_statistics = nullptr;
// Configures if sending should
// enforce compound packets: https://tools.ietf.org/html/rfc4585#section-3.1
// or allow reduced size packets: https://tools.ietf.org/html/rfc5506
// Receiving accepts both compound and reduced-size packets.
RtcpMode rtcp_mode = RtcpMode::kCompound;
// //
// Tuning parameters. // Tuning parameters.
// //

View File

@ -17,6 +17,7 @@
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "modules/rtp_rtcp/source/rtcp_packet.h" #include "modules/rtp_rtcp/source/rtcp_packet.h"
#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h" #include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
#include "modules/rtp_rtcp/source/rtcp_packet/pli.h"
#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" #include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h"
#include "modules/rtp_rtcp/source/rtcp_packet/report_block.h" #include "modules/rtp_rtcp/source/rtcp_packet/report_block.h"
#include "modules/rtp_rtcp/source/rtcp_packet/sdes.h" #include "modules/rtp_rtcp/source/rtcp_packet/sdes.h"
@ -28,11 +29,13 @@
#include "rtc_base/timeutils.h" #include "rtc_base/timeutils.h"
namespace webrtc { namespace webrtc {
namespace {
// Helper to put several RTCP packets into lower layer datagram composing // Helper to put several RTCP packets into lower layer datagram composing
// Compound or Reduced-Size RTCP packet, as defined by RFC 5506 section 2. // Compound or Reduced-Size RTCP packet, as defined by RFC 5506 section 2.
class PacketSender : public rtcp::RtcpPacket::PacketReadyCallback { // TODO(danilchap): When in compound mode and packets are so many that several
// compound RTCP packets need to be generated, ensure each packet is compound.
class RtcpTransceiverImpl::PacketSender
: public rtcp::RtcpPacket::PacketReadyCallback {
public: public:
PacketSender(Transport* transport, size_t max_packet_size) PacketSender(Transport* transport, size_t max_packet_size)
: transport_(transport), max_packet_size_(max_packet_size) { : transport_(transport), max_packet_size_(max_packet_size) {
@ -56,6 +59,8 @@ class PacketSender : public rtcp::RtcpPacket::PacketReadyCallback {
} }
} }
bool IsEmpty() const { return index_ == 0; }
private: private:
// Implements RtcpPacket::PacketReadyCallback // Implements RtcpPacket::PacketReadyCallback
void OnPacketReady(uint8_t* data, size_t length) override { void OnPacketReady(uint8_t* data, size_t length) override {
@ -68,8 +73,6 @@ class PacketSender : public rtcp::RtcpPacket::PacketReadyCallback {
uint8_t buffer_[IP_PACKET_SIZE]; uint8_t buffer_[IP_PACKET_SIZE];
}; };
} // namespace
RtcpTransceiverImpl::RtcpTransceiverImpl(const RtcpTransceiverConfig& config) RtcpTransceiverImpl::RtcpTransceiverImpl(const RtcpTransceiverConfig& config)
: config_(config), ptr_factory_(this) { : config_(config), ptr_factory_(this) {
RTC_CHECK(config_.Validate()); RTC_CHECK(config_.Validate());
@ -94,12 +97,8 @@ void RtcpTransceiverImpl::ReceivePacket(rtc::ArrayView<const uint8_t> packet,
} }
void RtcpTransceiverImpl::SendCompoundPacket() { void RtcpTransceiverImpl::SendCompoundPacket() {
SendPacket(); SendPeriodicCompoundPacket();
if (config_.schedule_periodic_compound_packets) { ReschedulePeriodicCompoundPackets();
// Stop existent send task.
ptr_factory_.InvalidateWeakPtrs();
SchedulePeriodicCompoundPackets(config_.report_period_ms);
}
} }
void RtcpTransceiverImpl::SetRemb(int bitrate_bps, void RtcpTransceiverImpl::SetRemb(int bitrate_bps,
@ -117,6 +116,27 @@ void RtcpTransceiverImpl::UnsetRemb() {
remb_.reset(); remb_.reset();
} }
void RtcpTransceiverImpl::RequestKeyFrame(
rtc::ArrayView<const uint32_t> ssrcs) {
RTC_DCHECK(!ssrcs.empty());
const uint32_t sender_ssrc = config_.feedback_ssrc;
PacketSender sender(config_.outgoing_transport, config_.max_packet_size);
if (config_.rtcp_mode == RtcpMode::kCompound)
CreateCompoundPacket(&sender);
for (uint32_t media_ssrc : ssrcs) {
rtcp::Pli pli;
pli.SetSenderSsrc(sender_ssrc);
pli.SetMediaSsrc(media_ssrc);
sender.AppendPacket(pli);
}
sender.Send();
if (config_.rtcp_mode == RtcpMode::kCompound)
ReschedulePeriodicCompoundPackets();
}
void RtcpTransceiverImpl::HandleReceivedPacket( void RtcpTransceiverImpl::HandleReceivedPacket(
const rtcp::CommonHeader& rtcp_packet_header, const rtcp::CommonHeader& rtcp_packet_header,
int64_t now_us) { int64_t now_us) {
@ -134,17 +154,25 @@ void RtcpTransceiverImpl::HandleReceivedPacket(
} }
} }
void RtcpTransceiverImpl::ReschedulePeriodicCompoundPackets() {
if (!config_.schedule_periodic_compound_packets)
return;
// Stop existent send task.
ptr_factory_.InvalidateWeakPtrs();
SchedulePeriodicCompoundPackets(config_.report_period_ms);
}
void RtcpTransceiverImpl::SchedulePeriodicCompoundPackets(int64_t delay_ms) { void RtcpTransceiverImpl::SchedulePeriodicCompoundPackets(int64_t delay_ms) {
class SendPeriodicCompoundPacket : public rtc::QueuedTask { class SendPeriodicCompoundPacketTask : public rtc::QueuedTask {
public: public:
SendPeriodicCompoundPacket(rtc::TaskQueue* task_queue, SendPeriodicCompoundPacketTask(rtc::TaskQueue* task_queue,
rtc::WeakPtr<RtcpTransceiverImpl> ptr) rtc::WeakPtr<RtcpTransceiverImpl> ptr)
: task_queue_(task_queue), ptr_(std::move(ptr)) {} : task_queue_(task_queue), ptr_(std::move(ptr)) {}
bool Run() override { bool Run() override {
RTC_DCHECK(task_queue_->IsCurrent()); RTC_DCHECK(task_queue_->IsCurrent());
if (!ptr_) if (!ptr_)
return true; return true;
ptr_->SendPacket(); ptr_->SendPeriodicCompoundPacket();
task_queue_->PostDelayedTask(rtc::WrapUnique(this), task_queue_->PostDelayedTask(rtc::WrapUnique(this),
ptr_->config_.report_period_ms); ptr_->config_.report_period_ms);
return false; return false;
@ -157,7 +185,7 @@ void RtcpTransceiverImpl::SchedulePeriodicCompoundPackets(int64_t delay_ms) {
RTC_DCHECK(config_.schedule_periodic_compound_packets); RTC_DCHECK(config_.schedule_periodic_compound_packets);
auto task = rtc::MakeUnique<SendPeriodicCompoundPacket>( auto task = rtc::MakeUnique<SendPeriodicCompoundPacketTask>(
config_.task_queue, ptr_factory_.GetWeakPtr()); config_.task_queue, ptr_factory_.GetWeakPtr());
if (delay_ms > 0) if (delay_ms > 0)
config_.task_queue->PostDelayedTask(std::move(task), delay_ms); config_.task_queue->PostDelayedTask(std::move(task), delay_ms);
@ -165,27 +193,30 @@ void RtcpTransceiverImpl::SchedulePeriodicCompoundPackets(int64_t delay_ms) {
config_.task_queue->PostTask(std::move(task)); config_.task_queue->PostTask(std::move(task));
} }
void RtcpTransceiverImpl::SendPacket() { void RtcpTransceiverImpl::CreateCompoundPacket(PacketSender* sender) {
PacketSender sender(config_.outgoing_transport, config_.max_packet_size); RTC_DCHECK(sender->IsEmpty());
const uint32_t sender_ssrc = config_.feedback_ssrc; const uint32_t sender_ssrc = config_.feedback_ssrc;
rtcp::ReceiverReport receiver_report; rtcp::ReceiverReport receiver_report;
receiver_report.SetSenderSsrc(sender_ssrc); receiver_report.SetSenderSsrc(sender_ssrc);
receiver_report.SetReportBlocks(CreateReportBlocks()); receiver_report.SetReportBlocks(CreateReportBlocks());
sender.AppendPacket(receiver_report); sender->AppendPacket(receiver_report);
if (!config_.cname.empty()) { if (!config_.cname.empty()) {
rtcp::Sdes sdes; rtcp::Sdes sdes;
bool added = sdes.AddCName(config_.feedback_ssrc, config_.cname); bool added = sdes.AddCName(config_.feedback_ssrc, config_.cname);
RTC_DCHECK(added) << "Failed to add cname " << config_.cname RTC_DCHECK(added) << "Failed to add cname " << config_.cname
<< " to rtcp sdes packet."; << " to rtcp sdes packet.";
sender.AppendPacket(sdes); sender->AppendPacket(sdes);
} }
if (remb_) { if (remb_) {
remb_->SetSenderSsrc(sender_ssrc); remb_->SetSenderSsrc(sender_ssrc);
sender.AppendPacket(*remb_); sender->AppendPacket(*remb_);
} }
}
void RtcpTransceiverImpl::SendPeriodicCompoundPacket() {
PacketSender sender(config_.outgoing_transport, config_.max_packet_size);
CreateCompoundPacket(&sender);
sender.Send(); sender.Send();
} }

View File

@ -48,7 +48,10 @@ class RtcpTransceiverImpl {
// Stops sending REMB in following compound packets. // Stops sending REMB in following compound packets.
void UnsetRemb(); void UnsetRemb();
void RequestKeyFrame(rtc::ArrayView<const uint32_t> ssrcs);
private: private:
class PacketSender;
struct SenderReportTimes { struct SenderReportTimes {
int64_t local_received_time_us; int64_t local_received_time_us;
NtpTime remote_sent_time; NtpTime remote_sent_time;
@ -57,9 +60,13 @@ class RtcpTransceiverImpl {
void HandleReceivedPacket(const rtcp::CommonHeader& rtcp_packet_header, void HandleReceivedPacket(const rtcp::CommonHeader& rtcp_packet_header,
int64_t now_us); int64_t now_us);
void ReschedulePeriodicCompoundPackets();
void SchedulePeriodicCompoundPackets(int64_t delay_ms); void SchedulePeriodicCompoundPackets(int64_t delay_ms);
// Creates compound RTCP packet, as defined in
// https://tools.ietf.org/html/rfc5506#section-2
void CreateCompoundPacket(PacketSender* sender);
// Sends RTCP packets. // Sends RTCP packets.
void SendPacket(); void SendPeriodicCompoundPacket();
// Generate Report Blocks to be send in Sender or Receiver Report. // Generate Report Blocks to be send in Sender or Receiver Report.
std::vector<rtcp::ReportBlock> CreateReportBlocks(); std::vector<rtcp::ReportBlock> CreateReportBlocks();

View File

@ -447,4 +447,69 @@ TEST(RtcpTransceiverImplTest,
EXPECT_EQ(CompactNtpRttToMs(report_blocks[1].delay_since_last_sr()), 100); EXPECT_EQ(CompactNtpRttToMs(report_blocks[1].delay_since_last_sr()), 100);
} }
TEST(RtcpTransceiverImplTest, RequestKeyFrameWithPictureLossIndication) {
const uint32_t kSenderSsrc = 1234;
const uint32_t kRemoteSsrcs[] = {4321, 5321};
MockTransport outgoing_transport;
RtcpTransceiverConfig config;
config.feedback_ssrc = kSenderSsrc;
config.schedule_periodic_compound_packets = false;
config.outgoing_transport = &outgoing_transport;
RtcpTransceiverImpl rtcp_transceiver(config);
RtcpPacketParser rtcp_parser;
EXPECT_CALL(outgoing_transport, SendRtcp(_, _))
.WillOnce(Invoke(&rtcp_parser, &RtcpPacketParser::Parse));
rtcp_transceiver.RequestKeyFrame(kRemoteSsrcs);
// Expect a pli packet per ssrc in the sent single compound packet.
EXPECT_EQ(rtcp_parser.pli()->num_packets(), 2);
EXPECT_EQ(rtcp_parser.pli()->sender_ssrc(), kSenderSsrc);
// test::RtcpPacketParser overwrites first pli packet with second one.
EXPECT_EQ(rtcp_parser.pli()->media_ssrc(), kRemoteSsrcs[1]);
}
TEST(RtcpTransceiverImplTest, KeyFrameRequestCreatesCompoundPacket) {
const uint32_t kRemoteSsrcs[] = {4321};
MockTransport outgoing_transport;
RtcpTransceiverConfig config;
// Turn periodic off to ensure sent rtcp packet is explicitly requested.
config.schedule_periodic_compound_packets = false;
config.outgoing_transport = &outgoing_transport;
config.rtcp_mode = webrtc::RtcpMode::kCompound;
RtcpTransceiverImpl rtcp_transceiver(config);
RtcpPacketParser rtcp_parser;
EXPECT_CALL(outgoing_transport, SendRtcp(_, _))
.WillOnce(Invoke(&rtcp_parser, &RtcpPacketParser::Parse));
rtcp_transceiver.RequestKeyFrame(kRemoteSsrcs);
// Test sent packet is compound by expecting presense of receiver report.
EXPECT_EQ(rtcp_parser.receiver_report()->num_packets(), 1);
}
TEST(RtcpTransceiverImplTest, KeyFrameRequestCreatesReducedSizePacket) {
const uint32_t kRemoteSsrcs[] = {4321};
MockTransport outgoing_transport;
RtcpTransceiverConfig config;
// Turn periodic off to ensure sent rtcp packet is explicitly requested.
config.schedule_periodic_compound_packets = false;
config.outgoing_transport = &outgoing_transport;
config.rtcp_mode = webrtc::RtcpMode::kReducedSize;
RtcpTransceiverImpl rtcp_transceiver(config);
RtcpPacketParser rtcp_parser;
// Expect some rtcp packet is triggered by the RequestKeyFrame.
EXPECT_CALL(outgoing_transport, SendRtcp(_, _))
.WillOnce(Invoke(&rtcp_parser, &RtcpPacketParser::Parse));
rtcp_transceiver.RequestKeyFrame(kRemoteSsrcs);
// Test sent packet is reduced size by expecting absense of receiver report.
EXPECT_EQ(rtcp_parser.receiver_report()->num_packets(), 0);
}
} // namespace } // namespace