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"
7 #include "base/debug/trace_event.h"
13 const int kMinSchedulingDelayMs
= 1;
14 const int kNumAggressiveReportsSentAtStart
= 100;
16 // The additional number of frames that can be in-flight when input exceeds the
17 // maximum frame rate.
18 const int kMaxFrameBurst
= 5;
22 // Convenience macro used in logging statements throughout this file.
23 #define SENDER_SSRC (is_audio_ ? "AUDIO[" : "VIDEO[") << ssrc_ << "] "
25 FrameSender::FrameSender(scoped_refptr
<CastEnvironment
> cast_environment
,
27 CastTransportSender
* const transport_sender
,
28 base::TimeDelta rtcp_interval
,
31 double max_frame_rate
,
32 base::TimeDelta min_playout_delay
,
33 base::TimeDelta max_playout_delay
,
34 CongestionControl
* congestion_control
)
35 : cast_environment_(cast_environment
),
36 transport_sender_(transport_sender
),
38 rtcp_interval_(rtcp_interval
),
39 min_playout_delay_(min_playout_delay
== base::TimeDelta() ?
40 max_playout_delay
: min_playout_delay
),
41 max_playout_delay_(max_playout_delay
),
42 send_target_playout_delay_(false),
43 max_frame_rate_(max_frame_rate
),
44 num_aggressive_rtcp_reports_sent_(0),
45 last_sent_frame_id_(0),
46 latest_acked_frame_id_(0),
47 duplicate_ack_counter_(0),
48 congestion_control_(congestion_control
),
49 rtp_timebase_(rtp_timebase
),
52 DCHECK(transport_sender_
);
53 DCHECK_GT(rtp_timebase_
, 0);
54 DCHECK(congestion_control_
);
55 SetTargetPlayoutDelay(min_playout_delay_
);
56 send_target_playout_delay_
= false;
57 memset(frame_rtp_timestamps_
, 0, sizeof(frame_rtp_timestamps_
));
60 FrameSender::~FrameSender() {
63 void FrameSender::ScheduleNextRtcpReport() {
64 DCHECK(cast_environment_
->CurrentlyOn(CastEnvironment::MAIN
));
65 base::TimeDelta time_to_next
= rtcp_interval_
;
67 time_to_next
= std::max(
68 time_to_next
, base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs
));
70 cast_environment_
->PostDelayedTask(
71 CastEnvironment::MAIN
,
73 base::Bind(&FrameSender::SendRtcpReport
, weak_factory_
.GetWeakPtr(),
78 void FrameSender::SendRtcpReport(bool schedule_future_reports
) {
79 DCHECK(cast_environment_
->CurrentlyOn(CastEnvironment::MAIN
));
81 // Sanity-check: We should have sent at least the first frame by this point.
82 DCHECK(!last_send_time_
.is_null());
84 // Create lip-sync info for the sender report. The last sent frame's
85 // reference time and RTP timestamp are used to estimate an RTP timestamp in
86 // terms of "now." Note that |now| is never likely to be precise to an exact
87 // frame boundary; and so the computation here will result in a
88 // |now_as_rtp_timestamp| value that is rarely equal to any one emitted by the
90 const base::TimeTicks now
= cast_environment_
->Clock()->NowTicks();
91 const base::TimeDelta time_delta
=
92 now
- GetRecordedReferenceTime(last_sent_frame_id_
);
93 const int64 rtp_delta
= TimeDeltaToRtpDelta(time_delta
, rtp_timebase_
);
94 const uint32 now_as_rtp_timestamp
=
95 GetRecordedRtpTimestamp(last_sent_frame_id_
) +
96 static_cast<uint32
>(rtp_delta
);
97 transport_sender_
->SendSenderReport(ssrc_
, now
, now_as_rtp_timestamp
);
99 if (schedule_future_reports
)
100 ScheduleNextRtcpReport();
103 void FrameSender::OnMeasuredRoundTripTime(base::TimeDelta rtt
) {
104 DCHECK(rtt
> base::TimeDelta());
105 current_round_trip_time_
= rtt
;
108 void FrameSender::SetTargetPlayoutDelay(
109 base::TimeDelta new_target_playout_delay
) {
110 if (send_target_playout_delay_
&&
111 target_playout_delay_
== new_target_playout_delay
) {
114 new_target_playout_delay
= std::max(new_target_playout_delay
,
116 new_target_playout_delay
= std::min(new_target_playout_delay
,
118 VLOG(2) << SENDER_SSRC
<< "Target playout delay changing from "
119 << target_playout_delay_
.InMilliseconds() << " ms to "
120 << new_target_playout_delay
.InMilliseconds() << " ms.";
121 target_playout_delay_
= new_target_playout_delay
;
122 send_target_playout_delay_
= true;
123 congestion_control_
->UpdateTargetPlayoutDelay(target_playout_delay_
);
126 void FrameSender::ResendCheck() {
127 DCHECK(cast_environment_
->CurrentlyOn(CastEnvironment::MAIN
));
128 DCHECK(!last_send_time_
.is_null());
129 const base::TimeDelta time_since_last_send
=
130 cast_environment_
->Clock()->NowTicks() - last_send_time_
;
131 if (time_since_last_send
> target_playout_delay_
) {
132 if (latest_acked_frame_id_
== last_sent_frame_id_
) {
133 // Last frame acked, no point in doing anything
135 VLOG(1) << SENDER_SSRC
<< "ACK timeout; last acked frame: "
136 << latest_acked_frame_id_
;
137 ResendForKickstart();
140 ScheduleNextResendCheck();
143 void FrameSender::ScheduleNextResendCheck() {
144 DCHECK(cast_environment_
->CurrentlyOn(CastEnvironment::MAIN
));
145 DCHECK(!last_send_time_
.is_null());
146 base::TimeDelta time_to_next
=
147 last_send_time_
- cast_environment_
->Clock()->NowTicks() +
148 target_playout_delay_
;
149 time_to_next
= std::max(
150 time_to_next
, base::TimeDelta::FromMilliseconds(kMinSchedulingDelayMs
));
151 cast_environment_
->PostDelayedTask(
152 CastEnvironment::MAIN
,
154 base::Bind(&FrameSender::ResendCheck
, weak_factory_
.GetWeakPtr()),
158 void FrameSender::ResendForKickstart() {
159 DCHECK(cast_environment_
->CurrentlyOn(CastEnvironment::MAIN
));
160 DCHECK(!last_send_time_
.is_null());
161 VLOG(1) << SENDER_SSRC
<< "Resending last packet of frame "
162 << last_sent_frame_id_
<< " to kick-start.";
163 last_send_time_
= cast_environment_
->Clock()->NowTicks();
164 transport_sender_
->ResendFrameForKickstart(ssrc_
, last_sent_frame_id_
);
167 void FrameSender::RecordLatestFrameTimestamps(uint32 frame_id
,
168 base::TimeTicks reference_time
,
169 RtpTimestamp rtp_timestamp
) {
170 DCHECK(!reference_time
.is_null());
171 frame_reference_times_
[frame_id
% arraysize(frame_reference_times_
)] =
173 frame_rtp_timestamps_
[frame_id
% arraysize(frame_rtp_timestamps_
)] =
177 base::TimeTicks
FrameSender::GetRecordedReferenceTime(uint32 frame_id
) const {
178 return frame_reference_times_
[frame_id
% arraysize(frame_reference_times_
)];
181 RtpTimestamp
FrameSender::GetRecordedRtpTimestamp(uint32 frame_id
) const {
182 return frame_rtp_timestamps_
[frame_id
% arraysize(frame_rtp_timestamps_
)];
185 int FrameSender::GetUnacknowledgedFrameCount() const {
187 static_cast<int32
>(last_sent_frame_id_
- latest_acked_frame_id_
);
192 base::TimeDelta
FrameSender::GetAllowedInFlightMediaDuration() const {
193 // The total amount allowed in-flight media should equal the amount that fits
194 // within the entire playout delay window, plus the amount of time it takes to
195 // receive an ACK from the receiver.
196 // TODO(miu): Research is needed, but there is likely a better formula.
197 return target_playout_delay_
+ (current_round_trip_time_
/ 2);
200 void FrameSender::SendEncodedFrame(
201 int requested_bitrate_before_encode
,
202 scoped_ptr
<EncodedFrame
> encoded_frame
) {
203 DCHECK(cast_environment_
->CurrentlyOn(CastEnvironment::MAIN
));
205 VLOG(2) << SENDER_SSRC
<< "About to send another frame: last_sent="
206 << last_sent_frame_id_
<< ", latest_acked=" << latest_acked_frame_id_
;
208 const uint32 frame_id
= encoded_frame
->frame_id
;
210 const bool is_first_frame_to_be_sent
= last_send_time_
.is_null();
211 last_send_time_
= cast_environment_
->Clock()->NowTicks();
212 last_sent_frame_id_
= frame_id
;
213 // If this is the first frame about to be sent, fake the value of
214 // |latest_acked_frame_id_| to indicate the receiver starts out all caught up.
215 // Also, schedule the periodic frame re-send checks.
216 if (is_first_frame_to_be_sent
) {
217 latest_acked_frame_id_
= frame_id
- 1;
218 ScheduleNextResendCheck();
221 VLOG_IF(1, !is_audio_
&& encoded_frame
->dependency
== EncodedFrame::KEY
)
222 << SENDER_SSRC
<< "Sending encoded key frame, id=" << frame_id
;
224 cast_environment_
->Logging()->InsertEncodedFrameEvent(
225 last_send_time_
, FRAME_ENCODED
,
226 is_audio_
? AUDIO_EVENT
: VIDEO_EVENT
,
227 encoded_frame
->rtp_timestamp
,
228 frame_id
, static_cast<int>(encoded_frame
->data
.size()),
229 encoded_frame
->dependency
== EncodedFrame::KEY
,
230 requested_bitrate_before_encode
);
232 RecordLatestFrameTimestamps(frame_id
,
233 encoded_frame
->reference_time
,
234 encoded_frame
->rtp_timestamp
);
237 // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc
238 TRACE_EVENT_INSTANT1(
239 "cast_perf_test", "VideoFrameEncoded",
240 TRACE_EVENT_SCOPE_THREAD
,
241 "rtp_timestamp", encoded_frame
->rtp_timestamp
);
244 // At the start of the session, it's important to send reports before each
245 // frame so that the receiver can properly compute playout times. The reason
246 // more than one report is sent is because transmission is not guaranteed,
247 // only best effort, so send enough that one should almost certainly get
249 if (num_aggressive_rtcp_reports_sent_
< kNumAggressiveReportsSentAtStart
) {
250 // SendRtcpReport() will schedule future reports to be made if this is the
251 // last "aggressive report."
252 ++num_aggressive_rtcp_reports_sent_
;
253 const bool is_last_aggressive_report
=
254 (num_aggressive_rtcp_reports_sent_
== kNumAggressiveReportsSentAtStart
);
255 VLOG_IF(1, is_last_aggressive_report
)
256 << SENDER_SSRC
<< "Sending last aggressive report.";
257 SendRtcpReport(is_last_aggressive_report
);
260 congestion_control_
->SendFrameToTransport(
261 frame_id
, encoded_frame
->data
.size() * 8, last_send_time_
);
263 if (send_target_playout_delay_
) {
264 encoded_frame
->new_playout_delay_ms
=
265 target_playout_delay_
.InMilliseconds();
267 transport_sender_
->InsertFrame(ssrc_
, *encoded_frame
);
270 void FrameSender::OnReceivedCastFeedback(const RtcpCastMessage
& cast_feedback
) {
271 DCHECK(cast_environment_
->CurrentlyOn(CastEnvironment::MAIN
));
273 const bool have_valid_rtt
= current_round_trip_time_
> base::TimeDelta();
274 if (have_valid_rtt
) {
275 congestion_control_
->UpdateRtt(current_round_trip_time_
);
277 // Having the RTT value implies the receiver sent back a receiver report
278 // based on it having received a report from here. Therefore, ensure this
279 // sender stops aggressively sending reports.
280 if (num_aggressive_rtcp_reports_sent_
< kNumAggressiveReportsSentAtStart
) {
281 VLOG(1) << SENDER_SSRC
282 << "No longer a need to send reports aggressively (sent "
283 << num_aggressive_rtcp_reports_sent_
<< ").";
284 num_aggressive_rtcp_reports_sent_
= kNumAggressiveReportsSentAtStart
;
285 ScheduleNextRtcpReport();
289 if (last_send_time_
.is_null())
290 return; // Cannot get an ACK without having first sent a frame.
292 if (cast_feedback
.missing_frames_and_packets
.empty()) {
293 OnAck(cast_feedback
.ack_frame_id
);
295 // We only count duplicate ACKs when we have sent newer frames.
296 if (latest_acked_frame_id_
== cast_feedback
.ack_frame_id
&&
297 latest_acked_frame_id_
!= last_sent_frame_id_
) {
298 duplicate_ack_counter_
++;
300 duplicate_ack_counter_
= 0;
302 // TODO(miu): The values "2" and "3" should be derived from configuration.
303 if (duplicate_ack_counter_
>= 2 && duplicate_ack_counter_
% 3 == 2) {
304 VLOG(1) << SENDER_SSRC
<< "Received duplicate ACK for frame "
305 << latest_acked_frame_id_
;
306 ResendForKickstart();
309 // Only count duplicated ACKs if there is no NACK request in between.
310 // This is to avoid aggresive resend.
311 duplicate_ack_counter_
= 0;
314 base::TimeTicks now
= cast_environment_
->Clock()->NowTicks();
315 congestion_control_
->AckFrame(cast_feedback
.ack_frame_id
, now
);
317 cast_environment_
->Logging()->InsertFrameEvent(
320 is_audio_
? AUDIO_EVENT
: VIDEO_EVENT
,
321 GetRecordedRtpTimestamp(cast_feedback
.ack_frame_id
),
322 cast_feedback
.ack_frame_id
);
324 const bool is_acked_out_of_order
=
325 static_cast<int32
>(cast_feedback
.ack_frame_id
-
326 latest_acked_frame_id_
) < 0;
327 VLOG(2) << SENDER_SSRC
328 << "Received ACK" << (is_acked_out_of_order
? " out-of-order" : "")
329 << " for frame " << cast_feedback
.ack_frame_id
;
330 if (!is_acked_out_of_order
) {
331 // Cancel resends of acked frames.
332 std::vector
<uint32
> cancel_sending_frames
;
333 while (latest_acked_frame_id_
!= cast_feedback
.ack_frame_id
) {
334 latest_acked_frame_id_
++;
335 cancel_sending_frames
.push_back(latest_acked_frame_id_
);
337 transport_sender_
->CancelSendingFrames(ssrc_
, cancel_sending_frames
);
338 latest_acked_frame_id_
= cast_feedback
.ack_frame_id
;
342 bool FrameSender::ShouldDropNextFrame(base::TimeDelta frame_duration
) const {
343 // Check that accepting the next frame won't cause more frames to become
344 // in-flight than the system's design limit.
345 const int count_frames_in_flight
=
346 GetUnacknowledgedFrameCount() + GetNumberOfFramesInEncoder();
347 if (count_frames_in_flight
>= kMaxUnackedFrames
) {
348 VLOG(1) << SENDER_SSRC
<< "Dropping: Too many frames would be in-flight.";
352 // Check that accepting the next frame won't exceed the configured maximum
353 // frame rate, allowing for short-term bursts.
354 base::TimeDelta duration_in_flight
= GetInFlightMediaDuration();
355 const double max_frames_in_flight
=
356 max_frame_rate_
* duration_in_flight
.InSecondsF();
357 if (count_frames_in_flight
>= max_frames_in_flight
+ kMaxFrameBurst
) {
358 VLOG(1) << SENDER_SSRC
<< "Dropping: Burst threshold would be exceeded.";
362 // Check that accepting the next frame won't exceed the allowed in-flight
364 const base::TimeDelta duration_would_be_in_flight
=
365 duration_in_flight
+ frame_duration
;
366 const base::TimeDelta allowed_in_flight
= GetAllowedInFlightMediaDuration();
368 const int64 percent
= allowed_in_flight
> base::TimeDelta() ?
369 100 * duration_would_be_in_flight
/ allowed_in_flight
: kint64max
;
370 VLOG_IF(1, percent
> 50)
372 << duration_in_flight
.InMicroseconds() << " usec in-flight + "
373 << frame_duration
.InMicroseconds() << " usec for next frame --> "
374 << percent
<< "% of allowed in-flight.";
376 if (duration_would_be_in_flight
> allowed_in_flight
) {
377 VLOG(1) << SENDER_SSRC
<< "Dropping: In-flight duration would be too high.";
381 // Next frame is accepted.