1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/cast/sender/frame_sender.h"
10 const int kMinSchedulingDelayMs
= 1;
13 FrameSender::FrameSender(scoped_refptr
<CastEnvironment
> cast_environment
,
14 CastTransportSender
* const transport_sender
,
15 base::TimeDelta rtcp_interval
,
18 double max_frame_rate
,
19 base::TimeDelta playout_delay
)
20 : cast_environment_(cast_environment
),
21 transport_sender_(transport_sender
),
23 rtt_available_(false),
24 rtcp_interval_(rtcp_interval
),
25 max_frame_rate_(max_frame_rate
),
26 num_aggressive_rtcp_reports_sent_(0),
27 last_sent_frame_id_(0),
28 latest_acked_frame_id_(0),
29 duplicate_ack_counter_(0),
30 rtp_timebase_(rtp_timebase
),
32 DCHECK_GT(rtp_timebase_
, 0);
33 SetTargetPlayoutDelay(playout_delay
);
34 send_target_playout_delay_
= false;
35 memset(frame_rtp_timestamps_
, 0, sizeof(frame_rtp_timestamps_
));
38 FrameSender::~FrameSender() {
41 void FrameSender::ScheduleNextRtcpReport() {
42 DCHECK(cast_environment_
->CurrentlyOn(CastEnvironment::MAIN
));
43 base::TimeDelta time_to_next
= rtcp_interval_
;
45 time_to_next
= std::max(
46 time_to_next
, base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs
));
48 cast_environment_
->PostDelayedTask(
49 CastEnvironment::MAIN
,
51 base::Bind(&FrameSender::SendRtcpReport
, weak_factory_
.GetWeakPtr(),
56 void FrameSender::SendRtcpReport(bool schedule_future_reports
) {
57 DCHECK(cast_environment_
->CurrentlyOn(CastEnvironment::MAIN
));
59 // Sanity-check: We should have sent at least the first frame by this point.
60 DCHECK(!last_send_time_
.is_null());
62 // Create lip-sync info for the sender report. The last sent frame's
63 // reference time and RTP timestamp are used to estimate an RTP timestamp in
64 // terms of "now." Note that |now| is never likely to be precise to an exact
65 // frame boundary; and so the computation here will result in a
66 // |now_as_rtp_timestamp| value that is rarely equal to any one emitted by the
68 const base::TimeTicks now
= cast_environment_
->Clock()->NowTicks();
69 const base::TimeDelta time_delta
=
70 now
- GetRecordedReferenceTime(last_sent_frame_id_
);
71 const int64 rtp_delta
= TimeDeltaToRtpDelta(time_delta
, rtp_timebase_
);
72 const uint32 now_as_rtp_timestamp
=
73 GetRecordedRtpTimestamp(last_sent_frame_id_
) +
74 static_cast<uint32
>(rtp_delta
);
75 transport_sender_
->SendSenderReport(ssrc_
, now
, now_as_rtp_timestamp
);
77 if (schedule_future_reports
)
78 ScheduleNextRtcpReport();
81 void FrameSender::OnReceivedRtt(base::TimeDelta rtt
,
82 base::TimeDelta avg_rtt
,
83 base::TimeDelta min_rtt
,
84 base::TimeDelta max_rtt
) {
85 rtt_available_
= true;
92 void FrameSender::SetTargetPlayoutDelay(
93 base::TimeDelta new_target_playout_delay
) {
94 target_playout_delay_
= new_target_playout_delay
;
96 std::min(kMaxUnackedFrames
,
97 1 + static_cast<int>(target_playout_delay_
*
99 base::TimeDelta::FromSeconds(1)));
100 send_target_playout_delay_
= true;
103 void FrameSender::ResendCheck() {
104 DCHECK(cast_environment_
->CurrentlyOn(CastEnvironment::MAIN
));
105 DCHECK(!last_send_time_
.is_null());
106 const base::TimeDelta time_since_last_send
=
107 cast_environment_
->Clock()->NowTicks() - last_send_time_
;
108 if (time_since_last_send
> target_playout_delay_
) {
109 if (latest_acked_frame_id_
== last_sent_frame_id_
) {
110 // Last frame acked, no point in doing anything
112 VLOG(1) << "ACK timeout; last acked frame: " << latest_acked_frame_id_
;
113 ResendForKickstart();
116 ScheduleNextResendCheck();
119 void FrameSender::ScheduleNextResendCheck() {
120 DCHECK(cast_environment_
->CurrentlyOn(CastEnvironment::MAIN
));
121 DCHECK(!last_send_time_
.is_null());
122 base::TimeDelta time_to_next
=
123 last_send_time_
- cast_environment_
->Clock()->NowTicks() +
124 target_playout_delay_
;
125 time_to_next
= std::max(
126 time_to_next
, base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs
));
127 cast_environment_
->PostDelayedTask(
128 CastEnvironment::MAIN
,
130 base::Bind(&FrameSender::ResendCheck
, weak_factory_
.GetWeakPtr()),
134 void FrameSender::ResendForKickstart() {
135 DCHECK(cast_environment_
->CurrentlyOn(CastEnvironment::MAIN
));
136 DCHECK(!last_send_time_
.is_null());
137 VLOG(1) << "Resending last packet of frame " << last_sent_frame_id_
138 << " to kick-start.";
139 last_send_time_
= cast_environment_
->Clock()->NowTicks();
140 transport_sender_
->ResendFrameForKickstart(ssrc_
, last_sent_frame_id_
);
143 void FrameSender::RecordLatestFrameTimestamps(uint32 frame_id
,
144 base::TimeTicks reference_time
,
145 RtpTimestamp rtp_timestamp
) {
146 DCHECK(!reference_time
.is_null());
147 frame_reference_times_
[frame_id
% arraysize(frame_reference_times_
)] =
149 frame_rtp_timestamps_
[frame_id
% arraysize(frame_rtp_timestamps_
)] =
153 base::TimeTicks
FrameSender::GetRecordedReferenceTime(uint32 frame_id
) const {
154 return frame_reference_times_
[frame_id
% arraysize(frame_reference_times_
)];
157 RtpTimestamp
FrameSender::GetRecordedRtpTimestamp(uint32 frame_id
) const {
158 return frame_rtp_timestamps_
[frame_id
% arraysize(frame_rtp_timestamps_
)];