Commit 8f0f3673 authored by hclam@chromium.org's avatar hclam@chromium.org

Cast: Move retransmission to the transport

This change is to move the logic of retransmission from AudioSender/
VideoSender to CastTransportSender.

With this change AudioSender and VideoSender will not handle packets any
more. They will simply see frames. Retransmission is now handled by
CastTransportSender.

Added two more public methods for CastTransportSender:
* CancelSendingFrames
  Cancel sending certain frames.
* SendFrameForKickstart
  Sends the last packet of a frame to kick start the session.

cast_simulator shows no regression.
BUG=393042

Review URL: https://codereview.chromium.org/445933002

Cr-Commit-Position: refs/heads/master@{#290094}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@290094 0039d316-1c4b-4281-b951-d872f2087c98
parent 5b9a89a2
...@@ -37,8 +37,10 @@ bool CastTransportHostFilter::OnMessageReceived(const IPC::Message& message) { ...@@ -37,8 +37,10 @@ bool CastTransportHostFilter::OnMessageReceived(const IPC::Message& message) {
OnInsertCodedVideoFrame) OnInsertCodedVideoFrame)
IPC_MESSAGE_HANDLER(CastHostMsg_SendSenderReport, IPC_MESSAGE_HANDLER(CastHostMsg_SendSenderReport,
OnSendSenderReport) OnSendSenderReport)
IPC_MESSAGE_HANDLER(CastHostMsg_ResendPackets, IPC_MESSAGE_HANDLER(CastHostMsg_ResendFrameForKickstart,
OnResendPackets) OnResendFrameForKickstart)
IPC_MESSAGE_HANDLER(CastHostMsg_CancelSendingFrames,
OnCancelSendingFrames)
IPC_MESSAGE_UNHANDLED(handled = false); IPC_MESSAGE_UNHANDLED(handled = false);
IPC_END_MESSAGE_MAP(); IPC_END_MESSAGE_MAP();
return handled; return handled;
...@@ -198,38 +200,48 @@ void CastTransportHostFilter::OnInsertCodedVideoFrame( ...@@ -198,38 +200,48 @@ void CastTransportHostFilter::OnInsertCodedVideoFrame(
} }
} }
void CastTransportHostFilter::OnSendSenderReport( void CastTransportHostFilter::OnCancelSendingFrames(
int32 channel_id, int32 channel_id, uint32 ssrc,
uint32 ssrc, const std::vector<uint32>& frame_ids) {
base::TimeTicks current_time,
uint32 current_time_as_rtp_timestamp) {
media::cast::CastTransportSender* sender = media::cast::CastTransportSender* sender =
id_map_.Lookup(channel_id); id_map_.Lookup(channel_id);
if (sender) { if (sender) {
sender->SendSenderReport(ssrc, sender->CancelSendingFrames(ssrc, frame_ids);
current_time,
current_time_as_rtp_timestamp);
} else { } else {
DVLOG(1) DVLOG(1)
<< "CastTransportHostFilter::OnSendSenderReport " << "CastTransportHostFilter::OnCancelSendingFrames "
<< "on non-existing channel"; << "on non-existing channel";
} }
} }
void CastTransportHostFilter::OnResendPackets( void CastTransportHostFilter::OnResendFrameForKickstart(
int32 channel_id, uint32 ssrc, uint32 frame_id) {
media::cast::CastTransportSender* sender =
id_map_.Lookup(channel_id);
if (sender) {
sender->ResendFrameForKickstart(ssrc, frame_id);
} else {
DVLOG(1)
<< "CastTransportHostFilter::OnResendFrameForKickstart "
<< "on non-existing channel";
}
}
void CastTransportHostFilter::OnSendSenderReport(
int32 channel_id, int32 channel_id,
bool is_audio, uint32 ssrc,
const media::cast::MissingFramesAndPacketsMap& missing_packets, base::TimeTicks current_time,
bool cancel_rtx_if_not_in_list, uint32 current_time_as_rtp_timestamp) {
base::TimeDelta dedupe_window) {
media::cast::CastTransportSender* sender = media::cast::CastTransportSender* sender =
id_map_.Lookup(channel_id); id_map_.Lookup(channel_id);
if (sender) { if (sender) {
sender->ResendPackets( sender->SendSenderReport(ssrc,
is_audio, missing_packets, cancel_rtx_if_not_in_list, dedupe_window); current_time,
current_time_as_rtp_timestamp);
} else { } else {
DVLOG(1) DVLOG(1)
<< "CastTransportHostFilter::OnResendPackets on non-existing channel"; << "CastTransportHostFilter::OnSendSenderReport "
<< "on non-existing channel";
} }
} }
......
...@@ -65,12 +65,10 @@ class CastTransportHostFilter : public content::BrowserMessageFilter { ...@@ -65,12 +65,10 @@ class CastTransportHostFilter : public content::BrowserMessageFilter {
uint32 ssrc, uint32 ssrc,
base::TimeTicks current_time, base::TimeTicks current_time,
uint32 current_time_as_rtp_timestamp); uint32 current_time_as_rtp_timestamp);
void OnResendPackets( void OnCancelSendingFrames(int32 channel_id, uint32 ssrc,
int32 channel_id, const std::vector<uint32>& frame_ids);
bool is_audio, void OnResendFrameForKickstart(int32 channel_id, uint32 ssrc,
const media::cast::MissingFramesAndPacketsMap& missing_packets, uint32 frame_id);
bool cancel_rtx_if_not_in_list,
base::TimeDelta dedupe_window);
void OnNew( void OnNew(
int32 channel_id, int32 channel_id,
const net::IPEndPoint& remote_end_point); const net::IPEndPoint& remote_end_point);
......
...@@ -125,12 +125,13 @@ TEST_F(CastTransportHostFilterTest, SimpleMessages) { ...@@ -125,12 +125,13 @@ TEST_F(CastTransportHostFilterTest, SimpleMessages) {
kChannelId, 1, base::TimeTicks(), 2); kChannelId, 1, base::TimeTicks(), 2);
FakeSend(rtcp_msg); FakeSend(rtcp_msg);
media::cast::MissingFramesAndPacketsMap missing_packets; std::vector<uint32> frame_ids;
missing_packets[1].insert(4); frame_ids.push_back(1);
missing_packets[1].insert(7); CastHostMsg_CancelSendingFrames cancel_msg(kChannelId, 1, frame_ids);
CastHostMsg_ResendPackets resend_msg( FakeSend(cancel_msg);
kChannelId, false, missing_packets, true, base::TimeDelta());
FakeSend(resend_msg); CastHostMsg_ResendFrameForKickstart kickstart_msg(kChannelId, 1, 1);
FakeSend(kickstart_msg);
CastHostMsg_Delete delete_msg(kChannelId); CastHostMsg_Delete delete_msg(kChannelId);
FakeSend(delete_msg); FakeSend(delete_msg);
......
...@@ -139,13 +139,17 @@ IPC_MESSAGE_CONTROL4( ...@@ -139,13 +139,17 @@ IPC_MESSAGE_CONTROL4(
base::TimeTicks /* current_time */, base::TimeTicks /* current_time */,
uint32 /* current_time_as_rtp_timestamp */) uint32 /* current_time_as_rtp_timestamp */)
IPC_MESSAGE_CONTROL5( IPC_MESSAGE_CONTROL3(
CastHostMsg_ResendPackets, CastHostMsg_CancelSendingFrames,
int32 /* channel_id */, int32 /* channel_id */,
bool /* is_audio */, uint32 /* ssrc */,
media::cast::MissingFramesAndPacketsMap /* missing_packets */, std::vector<uint32> /* frame_ids */)
bool /* cancel_rtx_if_not_in_list */,
base::TimeDelta /* dedupe_window */) IPC_MESSAGE_CONTROL3(
CastHostMsg_ResendFrameForKickstart,
int32 /* channel_id */,
uint32 /* ssrc */,
uint32 /* frame_id */)
IPC_MESSAGE_CONTROL2( IPC_MESSAGE_CONTROL2(
CastHostMsg_New, CastHostMsg_New,
......
...@@ -70,16 +70,18 @@ void CastTransportSenderIPC::SendSenderReport( ...@@ -70,16 +70,18 @@ void CastTransportSenderIPC::SendSenderReport(
current_time_as_rtp_timestamp)); current_time_as_rtp_timestamp));
} }
void CastTransportSenderIPC::ResendPackets( void CastTransportSenderIPC::CancelSendingFrames(
bool is_audio, uint32 ssrc, const std::vector<uint32>& frame_ids) {
const media::cast::MissingFramesAndPacketsMap& missing_packets, Send(new CastHostMsg_CancelSendingFrames(channel_id_,
bool cancel_rtx_if_not_in_list, ssrc,
base::TimeDelta dedupe_window) { frame_ids));
Send(new CastHostMsg_ResendPackets(channel_id_, }
is_audio,
missing_packets, void CastTransportSenderIPC::ResendFrameForKickstart(
cancel_rtx_if_not_in_list, uint32 ssrc, uint32 frame_id) {
dedupe_window)); Send(new CastHostMsg_ResendFrameForKickstart(channel_id_,
ssrc,
frame_id));
} }
void CastTransportSenderIPC::OnNotifyStatusChange( void CastTransportSenderIPC::OnNotifyStatusChange(
......
...@@ -44,12 +44,10 @@ class CastTransportSenderIPC ...@@ -44,12 +44,10 @@ class CastTransportSenderIPC
uint32 ssrc, uint32 ssrc,
base::TimeTicks current_time, base::TimeTicks current_time,
uint32 current_time_as_rtp_timestamp) OVERRIDE; uint32 current_time_as_rtp_timestamp) OVERRIDE;
virtual void ResendPackets( virtual void CancelSendingFrames(
bool is_audio, uint32 ssrc,
const media::cast::MissingFramesAndPacketsMap& missing_packets, const std::vector<uint32>& frame_ids) OVERRIDE;
bool cancel_rtx_if_not_in_list, virtual void ResendFrameForKickstart(uint32 ssrc, uint32 frame_id) OVERRIDE;
base::TimeDelta dedupe_window)
OVERRIDE;
void OnNotifyStatusChange( void OnNotifyStatusChange(
media::cast::CastTransportStatus status); media::cast::CastTransportStatus status);
......
...@@ -85,19 +85,15 @@ class CastTransportSender : public base::NonThreadSafe { ...@@ -85,19 +85,15 @@ class CastTransportSender : public base::NonThreadSafe {
base::TimeTicks current_time, base::TimeTicks current_time,
uint32 current_time_as_rtp_timestamp) = 0; uint32 current_time_as_rtp_timestamp) = 0;
// Retransmission request. // Cancels sending packets for the frames in the set.
// |missing_packets| includes the list of frames and packets in each // |ssrc| is the SSRC for the stream.
// frame to be re-transmitted. // |frame_ids| contains the IDs of the frames that will be cancelled.
// If |cancel_rtx_if_not_in_list| is used as an optimization to cancel virtual void CancelSendingFrames(uint32 ssrc,
// pending re-transmission requests of packets not listed in const std::vector<uint32>& frame_ids) = 0;
// |missing_packets|. If the requested packet(s) were sent recently
// (how long is specified by |dedupe_window|) then this re-transmit // Resends a frame or part of a frame to kickstart. This is used when the
// will be ignored. // stream appears to be stalled.
virtual void ResendPackets( virtual void ResendFrameForKickstart(uint32 ssrc, uint32 frame_id) = 0;
bool is_audio,
const MissingFramesAndPacketsMap& missing_packets,
bool cancel_rtx_if_not_in_list,
base::TimeDelta dedupe_window) = 0;
// Returns a callback for receiving packets for testing purposes. // Returns a callback for receiving packets for testing purposes.
virtual PacketReceiverCallback PacketReceiverForTesting(); virtual PacketReceiverCallback PacketReceiverForTesting();
......
...@@ -111,7 +111,9 @@ void CastTransportSenderImpl::InitializeAudio( ...@@ -111,7 +111,9 @@ void CastTransportSenderImpl::InitializeAudio(
} }
audio_rtcp_session_.reset( audio_rtcp_session_.reset(
new Rtcp(cast_message_cb, new Rtcp(base::Bind(&CastTransportSenderImpl::OnReceivedCastMessage,
weak_factory_.GetWeakPtr(), config.ssrc,
cast_message_cb),
rtt_cb, rtt_cb,
base::Bind(&CastTransportSenderImpl::OnReceivedLogMessage, base::Bind(&CastTransportSenderImpl::OnReceivedLogMessage,
weak_factory_.GetWeakPtr(), AUDIO_EVENT), weak_factory_.GetWeakPtr(), AUDIO_EVENT),
...@@ -142,7 +144,9 @@ void CastTransportSenderImpl::InitializeVideo( ...@@ -142,7 +144,9 @@ void CastTransportSenderImpl::InitializeVideo(
} }
video_rtcp_session_.reset( video_rtcp_session_.reset(
new Rtcp(cast_message_cb, new Rtcp(base::Bind(&CastTransportSenderImpl::OnReceivedCastMessage,
weak_factory_.GetWeakPtr(), config.ssrc,
cast_message_cb),
rtt_cb, rtt_cb,
base::Bind(&CastTransportSenderImpl::OnReceivedLogMessage, base::Bind(&CastTransportSenderImpl::OnReceivedLogMessage,
weak_factory_.GetWeakPtr(), VIDEO_EVENT), weak_factory_.GetWeakPtr(), VIDEO_EVENT),
...@@ -202,21 +206,48 @@ void CastTransportSenderImpl::SendSenderReport( ...@@ -202,21 +206,48 @@ void CastTransportSenderImpl::SendSenderReport(
} }
} }
void CastTransportSenderImpl::CancelSendingFrames(
uint32 ssrc,
const std::vector<uint32>& frame_ids) {
if (audio_sender_ && ssrc == audio_sender_->ssrc()) {
audio_sender_->CancelSendingFrames(frame_ids);
} else if (video_sender_ && ssrc == video_sender_->ssrc()) {
video_sender_->CancelSendingFrames(frame_ids);
} else {
NOTREACHED() << "Invalid request for cancel sending.";
}
}
void CastTransportSenderImpl::ResendFrameForKickstart(uint32 ssrc,
uint32 frame_id) {
if (audio_sender_ && ssrc == audio_sender_->ssrc()) {
DCHECK(audio_rtcp_session_);
audio_sender_->ResendFrameForKickstart(frame_id,
audio_rtcp_session_->rtt());
} else if (video_sender_ && ssrc == video_sender_->ssrc()) {
DCHECK(video_rtcp_session_);
video_sender_->ResendFrameForKickstart(frame_id,
video_rtcp_session_->rtt());
} else {
NOTREACHED() << "Invalid request for kickstart.";
}
}
void CastTransportSenderImpl::ResendPackets( void CastTransportSenderImpl::ResendPackets(
bool is_audio, uint32 ssrc,
const MissingFramesAndPacketsMap& missing_packets, const MissingFramesAndPacketsMap& missing_packets,
bool cancel_rtx_if_not_in_list, bool cancel_rtx_if_not_in_list,
base::TimeDelta dedupe_window) { base::TimeDelta dedupe_window) {
if (is_audio) { if (audio_sender_ && ssrc == audio_sender_->ssrc()) {
DCHECK(audio_sender_) << "Audio sender uninitialized";
audio_sender_->ResendPackets(missing_packets, audio_sender_->ResendPackets(missing_packets,
cancel_rtx_if_not_in_list, cancel_rtx_if_not_in_list,
dedupe_window); dedupe_window);
} else { } else if (video_sender_ && ssrc == video_sender_->ssrc()) {
DCHECK(video_sender_) << "Video sender uninitialized";
video_sender_->ResendPackets(missing_packets, video_sender_->ResendPackets(missing_packets,
cancel_rtx_if_not_in_list, cancel_rtx_if_not_in_list,
dedupe_window); dedupe_window);
} else {
NOTREACHED() << "Invalid request for retransmission.";
} }
} }
...@@ -293,5 +324,31 @@ void CastTransportSenderImpl::OnReceivedLogMessage( ...@@ -293,5 +324,31 @@ void CastTransportSenderImpl::OnReceivedLogMessage(
} }
} }
void CastTransportSenderImpl::OnReceivedCastMessage(
uint32 ssrc,
const RtcpCastMessageCallback& cast_message_cb,
const RtcpCastMessage& cast_message) {
if (!cast_message_cb.is_null())
cast_message_cb.Run(cast_message);
if (cast_message.missing_frames_and_packets.empty())
return;
base::TimeDelta rtt;
if (video_sender_ && video_sender_->ssrc() == ssrc) {
rtt = video_rtcp_session_->rtt();
}
// This call does two things.
// 1. Specifies that retransmissions for packets not listed in the set are
// cancelled.
// 2. Specifies a deduplication window. For video this would be the most
// recent RTT. For audio there is no deduplication.
ResendPackets(ssrc,
cast_message.missing_frames_and_packets,
true,
rtt);
}
} // namespace cast } // namespace cast
} // namespace media } // namespace media
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#define MEDIA_CAST_NET_CAST_TRANSPORT_IMPL_H_ #define MEDIA_CAST_NET_CAST_TRANSPORT_IMPL_H_
#include "base/callback.h" #include "base/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
...@@ -79,15 +80,28 @@ class CastTransportSenderImpl : public CastTransportSender { ...@@ -79,15 +80,28 @@ class CastTransportSenderImpl : public CastTransportSender {
base::TimeTicks current_time, base::TimeTicks current_time,
uint32 current_time_as_rtp_timestamp) OVERRIDE; uint32 current_time_as_rtp_timestamp) OVERRIDE;
virtual void ResendPackets(bool is_audio, virtual void CancelSendingFrames(
const MissingFramesAndPacketsMap& missing_packets, uint32 ssrc,
bool cancel_rtx_if_not_in_list, const std::vector<uint32>& frame_ids) OVERRIDE;
base::TimeDelta dedupe_window)
OVERRIDE; virtual void ResendFrameForKickstart(uint32 ssrc, uint32 frame_id) OVERRIDE;
virtual PacketReceiverCallback PacketReceiverForTesting() OVERRIDE; virtual PacketReceiverCallback PacketReceiverForTesting() OVERRIDE;
private: private:
FRIEND_TEST_ALL_PREFIXES(CastTransportSenderImplTest, NacksCancelRetransmits);
FRIEND_TEST_ALL_PREFIXES(CastTransportSenderImplTest, CancelRetransmits);
FRIEND_TEST_ALL_PREFIXES(CastTransportSenderImplTest, Kickstart);
// Resend packets for the stream identified by |ssrc|.
// If |cancel_rtx_if_not_in_list| is true then transmission of packets for the
// frames but not in the list will be dropped.
// If packet was sent after |now - dedupe_window| then it will not be sent.
void ResendPackets(uint32 ssrc,
const MissingFramesAndPacketsMap& missing_packets,
bool cancel_rtx_if_not_in_list,
base::TimeDelta dedupe_window);
// If |raw_events_callback_| is non-null, calls it with events collected // If |raw_events_callback_| is non-null, calls it with events collected
// by |event_subscriber_| since last call. // by |event_subscriber_| since last call.
void SendRawEvents(); void SendRawEvents();
...@@ -99,6 +113,11 @@ class CastTransportSenderImpl : public CastTransportSender { ...@@ -99,6 +113,11 @@ class CastTransportSenderImpl : public CastTransportSender {
void OnReceivedLogMessage(EventMediaType media_type, void OnReceivedLogMessage(EventMediaType media_type,
const RtcpReceiverLogMessage& log); const RtcpReceiverLogMessage& log);
// Called when a RTCP Cast message is received.
void OnReceivedCastMessage(uint32 ssrc,
const RtcpCastMessageCallback& cast_message_cb,
const RtcpCastMessage& cast_message);
base::TickClock* clock_; // Not owned by this class. base::TickClock* clock_; // Not owned by this class.
CastTransportStatusCallback status_callback_; CastTransportStatusCallback status_callback_;
scoped_refptr<base::SingleThreadTaskRunner> transport_task_runner_; scoped_refptr<base::SingleThreadTaskRunner> transport_task_runner_;
......
...@@ -19,15 +19,43 @@ ...@@ -19,15 +19,43 @@
namespace media { namespace media {
namespace cast { namespace cast {
static const int64 kStartMillisecond = INT64_C(12345678900000); namespace {
const int64 kStartMillisecond = INT64_C(12345678900000);
const uint32 kVideoSsrc = 1;
} // namespace
class FakePacketSender : public PacketSender { class FakePacketSender : public PacketSender {
public: public:
FakePacketSender() {} FakePacketSender()
: paused_(false), packets_sent_(0) {}
virtual bool SendPacket(PacketRef packet, const base::Closure& cb) OVERRIDE { virtual bool SendPacket(PacketRef packet, const base::Closure& cb) OVERRIDE {
if (paused_) {
stored_packet_ = packet;
callback_ = cb;
return false;
}
++packets_sent_;
return true; return true;
} }
void SetPaused(bool paused) {
paused_ = paused;
if (!paused && stored_packet_) {
SendPacket(stored_packet_, callback_);
callback_.Run();
}
}
int packets_sent() const { return packets_sent_; }
private:
bool paused_;
base::Closure callback_;
PacketRef stored_packet_;
int packets_sent_;
DISALLOW_COPY_AND_ASSIGN(FakePacketSender);
}; };
class CastTransportSenderImplTest : public ::testing::Test { class CastTransportSenderImplTest : public ::testing::Test {
...@@ -68,6 +96,18 @@ class CastTransportSenderImplTest : public ::testing::Test { ...@@ -68,6 +96,18 @@ class CastTransportSenderImplTest : public ::testing::Test {
task_runner_->RunTasks(); task_runner_->RunTasks();
} }
void InitializeVideo() {
CastTransportRtpConfig rtp_config;
rtp_config.ssrc = kVideoSsrc;
rtp_config.feedback_ssrc = 2;
rtp_config.rtp_payload_type = 3;
rtp_config.stored_frames = 10;
transport_sender_->InitializeVideo(rtp_config,
RtcpCastMessageCallback(),
RtcpRttCallback());
}
void LogRawEvents(const std::vector<PacketEvent>& packet_events, void LogRawEvents(const std::vector<PacketEvent>& packet_events,
const std::vector<FrameEvent>& frame_events) { const std::vector<FrameEvent>& frame_events) {
num_times_callback_called_++; num_times_callback_called_++;
...@@ -95,5 +135,123 @@ TEST_F(CastTransportSenderImplTest, InitWithLogging) { ...@@ -95,5 +135,123 @@ TEST_F(CastTransportSenderImplTest, InitWithLogging) {
EXPECT_EQ(5, num_times_callback_called_); EXPECT_EQ(5, num_times_callback_called_);
} }
TEST_F(CastTransportSenderImplTest, NacksCancelRetransmits) {
InitWithoutLogging();
InitializeVideo();
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50));
// A fake frame that will be decomposed into 4 packets.
EncodedFrame fake_frame;
fake_frame.frame_id = 1;
fake_frame.rtp_timestamp = 1;
fake_frame.dependency = EncodedFrame::KEY;
fake_frame.data.resize(5000, ' ');
transport_sender_->InsertCodedVideoFrame(fake_frame);
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10));
EXPECT_EQ(4, transport_.packets_sent());
// Resend packet 0.
MissingFramesAndPacketsMap missing_packets;
missing_packets[1].insert(0);
missing_packets[1].insert(1);
missing_packets[1].insert(2);
transport_.SetPaused(true);
transport_sender_->ResendPackets(
kVideoSsrc, missing_packets, true,
base::TimeDelta::FromMilliseconds(10));
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10));
RtcpCastMessage cast_message;
cast_message.media_ssrc = kVideoSsrc;
cast_message.ack_frame_id = 1;
cast_message.missing_frames_and_packets[1].insert(3);
transport_sender_->OnReceivedCastMessage(kVideoSsrc,
RtcpCastMessageCallback(),
cast_message);
transport_.SetPaused(false);
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10));
// Resend one packet in the socket when unpaused.
// Resend one more packet from NACK.
EXPECT_EQ(6, transport_.packets_sent());
}
TEST_F(CastTransportSenderImplTest, CancelRetransmits) {
InitWithoutLogging();
InitializeVideo();
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50));
// A fake frame that will be decomposed into 4 packets.
EncodedFrame fake_frame;
fake_frame.frame_id = 1;
fake_frame.rtp_timestamp = 1;
fake_frame.dependency = EncodedFrame::KEY;
fake_frame.data.resize(5000, ' ');
transport_sender_->InsertCodedVideoFrame(fake_frame);
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10));
EXPECT_EQ(4, transport_.packets_sent());
// Resend all packets for frame 1.
MissingFramesAndPacketsMap missing_packets;
missing_packets[1].insert(kRtcpCastAllPacketsLost);
transport_.SetPaused(true);
transport_sender_->ResendPackets(
kVideoSsrc, missing_packets, true,
base::TimeDelta::FromMilliseconds(10));
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10));
std::vector<uint32> cancel_sending_frames;
cancel_sending_frames.push_back(1);
transport_sender_->CancelSendingFrames(kVideoSsrc,
cancel_sending_frames);
transport_.SetPaused(false);
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10));
// Resend one packet in the socket when unpaused.
EXPECT_EQ(5, transport_.packets_sent());
}
TEST_F(CastTransportSenderImplTest, Kickstart) {
InitWithoutLogging();
InitializeVideo();
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(50));
// A fake frame that will be decomposed into 4 packets.
EncodedFrame fake_frame;
fake_frame.frame_id = 1;
fake_frame.rtp_timestamp = 1;
fake_frame.dependency = EncodedFrame::KEY;
fake_frame.data.resize(5000, ' ');
transport_.SetPaused(true);
transport_sender_->InsertCodedVideoFrame(fake_frame);
transport_sender_->ResendFrameForKickstart(kVideoSsrc, 1);
transport_.SetPaused(false);
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10));
EXPECT_EQ(4, transport_.packets_sent());
// Resend 2 packets for frame 1.
MissingFramesAndPacketsMap missing_packets;
missing_packets[1].insert(0);
missing_packets[1].insert(1);
transport_.SetPaused(true);
transport_sender_->ResendPackets(
kVideoSsrc, missing_packets, true,
base::TimeDelta::FromMilliseconds(10));
transport_sender_->ResendFrameForKickstart(kVideoSsrc, 1);
transport_.SetPaused(false);
task_runner_->Sleep(base::TimeDelta::FromMilliseconds(10));
// Resend one packet in the socket when unpaused.
// Two more retransmission packets sent.
EXPECT_EQ(7, transport_.packets_sent());
}
} // namespace cast } // namespace cast
} // namespace media } // namespace media
...@@ -107,6 +107,8 @@ class Rtcp { ...@@ -107,6 +107,8 @@ class Rtcp {
void OnReceivedReceiverLog(const RtcpReceiverLogMessage& receiver_log); void OnReceivedReceiverLog(const RtcpReceiverLogMessage& receiver_log);
const base::TimeDelta& rtt() const { return rtt_; }
protected: protected:
void OnReceivedNtp(uint32 ntp_seconds, uint32 ntp_fraction); void OnReceivedNtp(uint32 ntp_seconds, uint32 ntp_fraction);
void OnReceivedLipSyncInfo(uint32 rtp_timestamp, void OnReceivedLipSyncInfo(uint32 rtp_timestamp,
......
...@@ -117,6 +117,34 @@ void RtpSender::ResendPackets( ...@@ -117,6 +117,34 @@ void RtpSender::ResendPackets(
} }
} }
void RtpSender::CancelSendingFrames(const std::vector<uint32>& frame_ids) {
for (std::vector<uint32>::const_iterator i = frame_ids.begin();
i != frame_ids.end(); ++i) {
const SendPacketVector* stored_packets = storage_->GetFrame8(*i & 0xFF);
if (!stored_packets)
continue;
for (SendPacketVector::const_iterator j = stored_packets->begin();
j != stored_packets->end(); ++j) {
transport_->CancelSendingPacket(j->first);
}
}
}
void RtpSender::ResendFrameForKickstart(uint32 frame_id,
base::TimeDelta dedupe_window) {
// Send the last packet of the encoded frame to kick start
// retransmission. This gives enough information to the receiver what
// packets and frames are missing.
MissingFramesAndPacketsMap missing_frames_and_packets;
PacketIdSet missing;
missing.insert(kRtcpCastLastPacket);
missing_frames_and_packets.insert(std::make_pair(frame_id, missing));
// Sending this extra packet is to kick-start the session. There is
// no need to optimize re-transmission for this case.
ResendPackets(missing_frames_and_packets, false, dedupe_window);
}
void RtpSender::UpdateSequenceNumber(Packet* packet) { void RtpSender::UpdateSequenceNumber(Packet* packet) {
// TODO(miu): This is an abstraction violation. This needs to be a part of // TODO(miu): This is an abstraction violation. This needs to be a part of
// the overall packet (de)serialization consolidation. // the overall packet (de)serialization consolidation.
......
...@@ -48,6 +48,10 @@ class RtpSender { ...@@ -48,6 +48,10 @@ class RtpSender {
bool cancel_rtx_if_not_in_list, bool cancel_rtx_if_not_in_list,
base::TimeDelta dedupe_window); base::TimeDelta dedupe_window);
void CancelSendingFrames(const std::vector<uint32>& frame_ids);
void ResendFrameForKickstart(uint32 frame_id, base::TimeDelta dedupe_window);
size_t send_packet_count() const { size_t send_packet_count() const {
return packetizer_ ? packetizer_->send_packet_count() : 0; return packetizer_ ? packetizer_->send_packet_count() : 0;
} }
......
...@@ -221,10 +221,6 @@ void AudioSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) { ...@@ -221,10 +221,6 @@ void AudioSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
// Only count duplicated ACKs if there is no NACK request in between. // Only count duplicated ACKs if there is no NACK request in between.
// This is to avoid aggresive resend. // This is to avoid aggresive resend.
duplicate_ack_counter_ = 0; duplicate_ack_counter_ = 0;
// A NACK is also used to cancel pending re-transmissions.
transport_sender_->ResendPackets(
true, cast_feedback.missing_frames_and_packets, false, min_rtt_);
} }
const base::TimeTicks now = cast_environment_->Clock()->NowTicks(); const base::TimeTicks now = cast_environment_->Clock()->NowTicks();
...@@ -244,14 +240,12 @@ void AudioSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) { ...@@ -244,14 +240,12 @@ void AudioSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
<< " for frame " << cast_feedback.ack_frame_id; << " for frame " << cast_feedback.ack_frame_id;
if (!is_acked_out_of_order) { if (!is_acked_out_of_order) {
// Cancel resends of acked frames. // Cancel resends of acked frames.
MissingFramesAndPacketsMap missing_frames_and_packets; std::vector<uint32> cancel_sending_frames;
PacketIdSet missing;
while (latest_acked_frame_id_ != cast_feedback.ack_frame_id) { while (latest_acked_frame_id_ != cast_feedback.ack_frame_id) {
latest_acked_frame_id_++; latest_acked_frame_id_++;
missing_frames_and_packets[latest_acked_frame_id_] = missing; cancel_sending_frames.push_back(latest_acked_frame_id_);
} }
transport_sender_->ResendPackets( transport_sender_->CancelSendingFrames(ssrc_, cancel_sending_frames);
true, missing_frames_and_packets, true, base::TimeDelta());
latest_acked_frame_id_ = cast_feedback.ack_frame_id; latest_acked_frame_id_ = cast_feedback.ack_frame_id;
} }
} }
...@@ -274,20 +268,8 @@ void AudioSender::ResendForKickstart() { ...@@ -274,20 +268,8 @@ void AudioSender::ResendForKickstart() {
DCHECK(!last_send_time_.is_null()); DCHECK(!last_send_time_.is_null());
VLOG(1) << "Resending last packet of frame " << last_sent_frame_id_ VLOG(1) << "Resending last packet of frame " << last_sent_frame_id_
<< " to kick-start."; << " to kick-start.";
// Send the first packet of the last encoded frame to kick start
// retransmission. This gives enough information to the receiver what
// packets and frames are missing.
MissingFramesAndPacketsMap missing_frames_and_packets;
PacketIdSet missing;
missing.insert(kRtcpCastLastPacket);
missing_frames_and_packets.insert(
std::make_pair(last_sent_frame_id_, missing));
last_send_time_ = cast_environment_->Clock()->NowTicks(); last_send_time_ = cast_environment_->Clock()->NowTicks();
transport_sender_->ResendFrameForKickstart(ssrc_, last_sent_frame_id_);
// Sending this extra packet is to kick-start the session. There is
// no need to optimize re-transmission for this case.
transport_sender_->ResendPackets(
true, missing_frames_and_packets, false, min_rtt_);
} }
} // namespace cast } // namespace cast
......
...@@ -17,10 +17,10 @@ FrameSender::FrameSender(scoped_refptr<CastEnvironment> cast_environment, ...@@ -17,10 +17,10 @@ FrameSender::FrameSender(scoped_refptr<CastEnvironment> cast_environment,
uint32 ssrc) uint32 ssrc)
: cast_environment_(cast_environment), : cast_environment_(cast_environment),
transport_sender_(transport_sender), transport_sender_(transport_sender),
ssrc_(ssrc),
rtp_timestamp_helper_(frequency), rtp_timestamp_helper_(frequency),
rtt_available_(false), rtt_available_(false),
rtcp_interval_(rtcp_interval), rtcp_interval_(rtcp_interval),
ssrc_(ssrc),
weak_factory_(this) { weak_factory_(this) {
} }
......
...@@ -50,6 +50,8 @@ class FrameSender { ...@@ -50,6 +50,8 @@ class FrameSender {
// network layer. // network layer.
CastTransportSender* const transport_sender_; CastTransportSender* const transport_sender_;
const uint32 ssrc_;
// Records lip-sync (i.e., mapping of RTP <--> NTP timestamps), and // Records lip-sync (i.e., mapping of RTP <--> NTP timestamps), and
// extrapolates this mapping to any other point in time. // extrapolates this mapping to any other point in time.
RtpTimestampHelper rtp_timestamp_helper_; RtpTimestampHelper rtp_timestamp_helper_;
...@@ -63,7 +65,6 @@ class FrameSender { ...@@ -63,7 +65,6 @@ class FrameSender {
private: private:
const base::TimeDelta rtcp_interval_; const base::TimeDelta rtcp_interval_;
const uint32 ssrc_;
// NOTE: Weak pointers must be invalidated before all other member variables. // NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<FrameSender> weak_factory_; base::WeakPtrFactory<FrameSender> weak_factory_;
......
...@@ -308,10 +308,6 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) { ...@@ -308,10 +308,6 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
// Only count duplicated ACKs if there is no NACK request in between. // Only count duplicated ACKs if there is no NACK request in between.
// This is to avoid aggresive resend. // This is to avoid aggresive resend.
duplicate_ack_counter_ = 0; duplicate_ack_counter_ = 0;
// A NACK is also used to cancel pending re-transmissions.
transport_sender_->ResendPackets(
false, cast_feedback.missing_frames_and_packets, true, rtt);
} }
base::TimeTicks now = cast_environment_->Clock()->NowTicks(); base::TimeTicks now = cast_environment_->Clock()->NowTicks();
...@@ -332,14 +328,12 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) { ...@@ -332,14 +328,12 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
<< " for frame " << cast_feedback.ack_frame_id; << " for frame " << cast_feedback.ack_frame_id;
if (!is_acked_out_of_order) { if (!is_acked_out_of_order) {
// Cancel resends of acked frames. // Cancel resends of acked frames.
MissingFramesAndPacketsMap missing_frames_and_packets; std::vector<uint32> cancel_sending_frames;
PacketIdSet missing;
while (latest_acked_frame_id_ != cast_feedback.ack_frame_id) { while (latest_acked_frame_id_ != cast_feedback.ack_frame_id) {
latest_acked_frame_id_++; latest_acked_frame_id_++;
missing_frames_and_packets[latest_acked_frame_id_] = missing; cancel_sending_frames.push_back(latest_acked_frame_id_);
} }
transport_sender_->ResendPackets( transport_sender_->CancelSendingFrames(ssrc_, cancel_sending_frames);
false, missing_frames_and_packets, true, rtt);
latest_acked_frame_id_ = cast_feedback.ack_frame_id; latest_acked_frame_id_ = cast_feedback.ack_frame_id;
} }
} }
...@@ -363,20 +357,8 @@ void VideoSender::ResendForKickstart() { ...@@ -363,20 +357,8 @@ void VideoSender::ResendForKickstart() {
DCHECK(!last_send_time_.is_null()); DCHECK(!last_send_time_.is_null());
VLOG(1) << "Resending last packet of frame " << last_sent_frame_id_ VLOG(1) << "Resending last packet of frame " << last_sent_frame_id_
<< " to kick-start."; << " to kick-start.";
// Send the first packet of the last encoded frame to kick start
// retransmission. This gives enough information to the receiver what
// packets and frames are missing.
MissingFramesAndPacketsMap missing_frames_and_packets;
PacketIdSet missing;
missing.insert(kRtcpCastLastPacket);
missing_frames_and_packets.insert(
std::make_pair(last_sent_frame_id_, missing));
last_send_time_ = cast_environment_->Clock()->NowTicks(); last_send_time_ = cast_environment_->Clock()->NowTicks();
transport_sender_->ResendFrameForKickstart(ssrc_, last_sent_frame_id_);
// Sending this extra packet is to kick-start the session. There is
// no need to optimize re-transmission for this case.
transport_sender_->ResendPackets(false, missing_frames_and_packets,
false, rtt_);
} }
} // namespace cast } // namespace cast
......
...@@ -503,32 +503,5 @@ TEST_F(VideoSenderTest, AcksCancelRetransmits) { ...@@ -503,32 +503,5 @@ TEST_F(VideoSenderTest, AcksCancelRetransmits) {
EXPECT_EQ(0, transport_.number_of_rtp_packets()); EXPECT_EQ(0, transport_.number_of_rtp_packets());
} }
TEST_F(VideoSenderTest, NAcksCancelRetransmits) {
InitEncoder(false);
transport_.SetPause(true);
// Send two video frames.
scoped_refptr<media::VideoFrame> video_frame = GetLargeNewVideoFrame();
video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
RunTasks(33);
video_frame = GetLargeNewVideoFrame();
video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
RunTasks(33);
// Frames should be in buffer, waiting. Now let's ack the first one and nack
// one packet in the second one.
RtcpCastMessage cast_feedback(1);
cast_feedback.media_ssrc = 2;
cast_feedback.ack_frame_id = 0;
PacketIdSet missing_packets;
missing_packets.insert(0);
cast_feedback.missing_frames_and_packets[1] = missing_packets;
video_sender_->OnReceivedCastFeedback(cast_feedback);
transport_.SetPause(false);
RunTasks(33);
// Only one packet should be retransmitted.
EXPECT_EQ(1, transport_.number_of_rtp_packets());
}
} // namespace cast } // namespace cast
} // namespace media } // namespace media
...@@ -143,14 +143,15 @@ class CastTransportSenderWrapper : public CastTransportSender { ...@@ -143,14 +143,15 @@ class CastTransportSenderWrapper : public CastTransportSender {
current_time_as_rtp_timestamp); current_time_as_rtp_timestamp);
} }
// Retransmission request. virtual void CancelSendingFrames(
virtual void ResendPackets( uint32 ssrc,
bool is_audio, const std::vector<uint32>& frame_ids) OVERRIDE {
const MissingFramesAndPacketsMap& missing_packets, transport_->CancelSendingFrames(ssrc, frame_ids);
bool cancel_rtx_if_not_in_list, }
base::TimeDelta dedupe_window) OVERRIDE {
transport_->ResendPackets( virtual void ResendFrameForKickstart(uint32 ssrc,
is_audio, missing_packets, cancel_rtx_if_not_in_list, dedupe_window); uint32 frame_id) OVERRIDE {
transport_->ResendFrameForKickstart(ssrc, frame_id);
} }
virtual PacketReceiverCallback PacketReceiverForTesting() OVERRIDE { virtual PacketReceiverCallback PacketReceiverForTesting() OVERRIDE {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment