1 // Copyright 2020 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.
4 
5 #include "cast/streaming/sender.h"
6 
7 #include <algorithm>
8 #include <chrono>
9 #include <ratio>
10 
11 #include "cast/streaming/session_config.h"
12 #include "util/chrono_helpers.h"
13 #include "util/osp_logging.h"
14 #include "util/std_util.h"
15 
16 namespace openscreen {
17 namespace cast {
18 
19 using openscreen::operator<<;  // For std::chrono::duration logging.
20 
Sender(Environment * environment,SenderPacketRouter * packet_router,SessionConfig config,RtpPayloadType rtp_payload_type)21 Sender::Sender(Environment* environment,
22                SenderPacketRouter* packet_router,
23                SessionConfig config,
24                RtpPayloadType rtp_payload_type)
25     : config_(config),
26       packet_router_(packet_router),
27       rtcp_session_(config.sender_ssrc,
28                     config.receiver_ssrc,
29                     environment->now()),
30       rtcp_parser_(&rtcp_session_, this),
31       sender_report_builder_(&rtcp_session_),
32       rtp_packetizer_(rtp_payload_type,
33                       config.sender_ssrc,
34                       packet_router_->max_packet_size()),
35       rtp_timebase_(config.rtp_timebase),
36       crypto_(config.aes_secret_key, config.aes_iv_mask),
37       target_playout_delay_(config.target_playout_delay) {
38   OSP_DCHECK(packet_router_);
39   OSP_DCHECK_NE(rtcp_session_.sender_ssrc(), rtcp_session_.receiver_ssrc());
40   OSP_DCHECK_GT(rtp_timebase_, 0);
41   OSP_DCHECK(target_playout_delay_ > milliseconds::zero());
42 
43   pending_sender_report_.reference_time = SenderPacketRouter::kNever;
44 
45   packet_router_->OnSenderCreated(rtcp_session_.receiver_ssrc(), this);
46 }
47 
~Sender()48 Sender::~Sender() {
49   packet_router_->OnSenderDestroyed(rtcp_session_.receiver_ssrc());
50 }
51 
SetObserver(Sender::Observer * observer)52 void Sender::SetObserver(Sender::Observer* observer) {
53   observer_ = observer;
54 }
55 
GetInFlightFrameCount() const56 int Sender::GetInFlightFrameCount() const {
57   return num_frames_in_flight_;
58 }
59 
GetInFlightMediaDuration(RtpTimeTicks next_frame_rtp_timestamp) const60 Clock::duration Sender::GetInFlightMediaDuration(
61     RtpTimeTicks next_frame_rtp_timestamp) const {
62   if (num_frames_in_flight_ == 0) {
63     return Clock::duration::zero();  // No frames are currently in-flight.
64   }
65 
66   const PendingFrameSlot& oldest_slot = *get_slot_for(checkpoint_frame_id_ + 1);
67   // Note: The oldest slot's frame cannot have been canceled because the
68   // protocol does not allow ACK'ing this particular frame without also moving
69   // the checkpoint forward. See "CST2 feedback" discussion in rtp_defines.h.
70   OSP_DCHECK(oldest_slot.is_active_for_frame(checkpoint_frame_id_ + 1));
71 
72   return (next_frame_rtp_timestamp - oldest_slot.frame->rtp_timestamp)
73       .ToDuration<Clock::duration>(rtp_timebase_);
74 }
75 
GetMaxInFlightMediaDuration() const76 Clock::duration Sender::GetMaxInFlightMediaDuration() const {
77   // Assumption: The total amount of allowed in-flight media should equal the
78   // half of the playout delay window, plus the amount of time it takes to
79   // receive an ACK from the Receiver.
80   //
81   // Why half of the playout delay window? It's assumed here that capture and
82   // media encoding, which occur before EnqueueFrame() is called, are executing
83   // within the first half of the playout delay window. This leaves the second
84   // half for executing all network transmits/re-transmits, plus decoding and
85   // play-out at the Receiver.
86   return (target_playout_delay_ / 2) + (round_trip_time_ / 2);
87 }
88 
NeedsKeyFrame() const89 bool Sender::NeedsKeyFrame() const {
90   return last_enqueued_key_frame_id_ <= picture_lost_at_frame_id_;
91 }
92 
GetNextFrameId() const93 FrameId Sender::GetNextFrameId() const {
94   return last_enqueued_frame_id_ + 1;
95 }
96 
EnqueueFrame(const EncodedFrame & frame)97 Sender::EnqueueFrameResult Sender::EnqueueFrame(const EncodedFrame& frame) {
98   // Assume the fields of the |frame| have all been set correctly, with
99   // monotonically increasing timestamps and a valid pointer to the data.
100   OSP_DCHECK_EQ(frame.frame_id, GetNextFrameId());
101   OSP_DCHECK_GE(frame.referenced_frame_id, FrameId::first());
102   if (frame.frame_id != FrameId::first()) {
103     OSP_DCHECK_GT(frame.rtp_timestamp, pending_sender_report_.rtp_timestamp);
104     OSP_DCHECK_GT(frame.reference_time, pending_sender_report_.reference_time);
105   }
106   OSP_DCHECK(frame.data.data());
107 
108   // Check whether enqueuing the frame would exceed the design limit for the
109   // span of FrameIds. Even if |num_frames_in_flight_| is less than
110   // kMaxUnackedFrames, it's the span of FrameIds that is restricted.
111   if ((frame.frame_id - checkpoint_frame_id_) > kMaxUnackedFrames) {
112     return REACHED_ID_SPAN_LIMIT;
113   }
114 
115   // Check whether enqueuing the frame would exceed the current maximum media
116   // duration limit.
117   if (GetInFlightMediaDuration(frame.rtp_timestamp) >
118       GetMaxInFlightMediaDuration()) {
119     return MAX_DURATION_IN_FLIGHT;
120   }
121 
122   // Encrypt the frame and initialize the slot tracking its sending.
123   PendingFrameSlot* const slot = get_slot_for(frame.frame_id);
124   OSP_DCHECK(!slot->frame);
125   slot->frame = crypto_.Encrypt(frame);
126   const int packet_count = rtp_packetizer_.ComputeNumberOfPackets(*slot->frame);
127   if (packet_count <= 0) {
128     slot->frame.reset();
129     return PAYLOAD_TOO_LARGE;
130   }
131   slot->send_flags.Resize(packet_count, YetAnotherBitVector::SET);
132   slot->packet_sent_times.assign(packet_count, SenderPacketRouter::kNever);
133 
134   // Officially record the "enqueue."
135   ++num_frames_in_flight_;
136   last_enqueued_frame_id_ = slot->frame->frame_id;
137   OSP_DCHECK_LE(num_frames_in_flight_,
138                 last_enqueued_frame_id_ - checkpoint_frame_id_);
139   if (slot->frame->dependency == EncodedFrame::KEY_FRAME) {
140     last_enqueued_key_frame_id_ = slot->frame->frame_id;
141   }
142 
143   // Update the target playout delay, if necessary.
144   if (slot->frame->new_playout_delay > milliseconds::zero()) {
145     target_playout_delay_ = slot->frame->new_playout_delay;
146     playout_delay_change_at_frame_id_ = slot->frame->frame_id;
147   }
148 
149   // Update the lip-sync information for the next Sender Report.
150   pending_sender_report_.reference_time = slot->frame->reference_time;
151   pending_sender_report_.rtp_timestamp = slot->frame->rtp_timestamp;
152 
153   // If the round trip time hasn't been computed yet, immediately send a RTCP
154   // packet (i.e., before the RTP packets are sent). The RTCP packet will
155   // provide a Sender Report which contains the required lip-sync information
156   // the Receiver needs for timing the media playout.
157   //
158   // Detail: Working backwards, if the round trip time is not known, then this
159   // Sender has never processed a Receiver Report. Thus, the Receiver has never
160   // provided a Receiver Report, which it can only do after having processed a
161   // Sender Report from this Sender. Thus, this Sender really needs to send
162   // that, right now!
163   if (round_trip_time_ == Clock::duration::zero()) {
164     packet_router_->RequestRtcpSend(rtcp_session_.receiver_ssrc());
165   }
166 
167   // Re-activate RTP sending if it was suspended.
168   packet_router_->RequestRtpSend(rtcp_session_.receiver_ssrc());
169 
170   return OK;
171 }
172 
CancelInFlightData()173 void Sender::CancelInFlightData() {
174   while (checkpoint_frame_id_ <= last_enqueued_frame_id_) {
175     ++checkpoint_frame_id_;
176     CancelPendingFrame(checkpoint_frame_id_);
177   }
178 }
179 
OnReceivedRtcpPacket(Clock::time_point arrival_time,absl::Span<const uint8_t> packet)180 void Sender::OnReceivedRtcpPacket(Clock::time_point arrival_time,
181                                   absl::Span<const uint8_t> packet) {
182   rtcp_packet_arrival_time_ = arrival_time;
183   // This call to Parse() invoke zero or more of the OnReceiverXYZ() methods in
184   // the current call stack:
185   if (rtcp_parser_.Parse(packet, last_enqueued_frame_id_)) {
186     packet_router_->OnRtcpReceived(arrival_time, round_trip_time_);
187   }
188 }
189 
GetRtcpPacketForImmediateSend(Clock::time_point send_time,absl::Span<uint8_t> buffer)190 absl::Span<uint8_t> Sender::GetRtcpPacketForImmediateSend(
191     Clock::time_point send_time,
192     absl::Span<uint8_t> buffer) {
193   if (pending_sender_report_.reference_time == SenderPacketRouter::kNever) {
194     // Cannot send a report if one is not available (i.e., a frame has never
195     // been enqueued).
196     return buffer.subspan(0, 0);
197   }
198 
199   // The Sender Report to be sent is a snapshot of the "pending Sender Report,"
200   // but with its timestamp fields modified. First, the reference time is set to
201   // the RTCP packet's send time. Then, the corresponding RTP timestamp is
202   // translated to match (for lip-sync).
203   RtcpSenderReport sender_report = pending_sender_report_;
204   sender_report.reference_time = send_time;
205   sender_report.rtp_timestamp += RtpTimeDelta::FromDuration(
206       sender_report.reference_time - pending_sender_report_.reference_time,
207       rtp_timebase_);
208 
209   return sender_report_builder_.BuildPacket(sender_report, buffer).first;
210 }
211 
GetRtpPacketForImmediateSend(Clock::time_point send_time,absl::Span<uint8_t> buffer)212 absl::Span<uint8_t> Sender::GetRtpPacketForImmediateSend(
213     Clock::time_point send_time,
214     absl::Span<uint8_t> buffer) {
215   ChosenPacket chosen = ChooseNextRtpPacketNeedingSend();
216 
217   // If no packets need sending (i.e., all packets have been sent at least once
218   // and do not need to be re-sent yet), check whether a Kickstart packet should
219   // be sent. It's possible that there has been complete packet loss of some
220   // frames, and the Receiver may not be aware of the existence of the latest
221   // frame(s). Kickstarting is the only way the Receiver can discover the newer
222   // frames it doesn't know about.
223   if (!chosen) {
224     const ChosenPacketAndWhen kickstart = ChooseKickstartPacket();
225     if (kickstart.when > send_time) {
226       // Nothing to send, so return "empty" signal to the packet router. The
227       // packet router will suspend RTP sending until this Sender explicitly
228       // resumes it.
229       return buffer.subspan(0, 0);
230     }
231     chosen = kickstart;
232     OSP_DCHECK(chosen);
233   }
234 
235   const absl::Span<uint8_t> result = rtp_packetizer_.GeneratePacket(
236       *chosen.slot->frame, chosen.packet_id, buffer);
237   chosen.slot->send_flags.Clear(chosen.packet_id);
238   chosen.slot->packet_sent_times[chosen.packet_id] = send_time;
239 
240   ++pending_sender_report_.send_packet_count;
241   // According to RFC3550, the octet count does not include the RTP header. The
242   // following is just a good approximation, however, because the header size
243   // will very infrequently be 4 bytes greater (see
244   // RtpPacketizer::kAdaptiveLatencyHeaderSize). No known Cast Streaming
245   // Receiver implementations use this for anything, and so this should be fine.
246   const int approximate_octet_count =
247       static_cast<int>(result.size()) - RtpPacketizer::kBaseRtpHeaderSize;
248   OSP_DCHECK_GE(approximate_octet_count, 0);
249   pending_sender_report_.send_octet_count += approximate_octet_count;
250 
251   return result;
252 }
253 
GetRtpResumeTime()254 Clock::time_point Sender::GetRtpResumeTime() {
255   if (ChooseNextRtpPacketNeedingSend()) {
256     return Alarm::kImmediately;
257   }
258   return ChooseKickstartPacket().when;
259 }
260 
OnReceiverReferenceTimeAdvanced(Clock::time_point reference_time)261 void Sender::OnReceiverReferenceTimeAdvanced(Clock::time_point reference_time) {
262   // Not used.
263 }
264 
OnReceiverReport(const RtcpReportBlock & receiver_report)265 void Sender::OnReceiverReport(const RtcpReportBlock& receiver_report) {
266   OSP_DCHECK_NE(rtcp_packet_arrival_time_, SenderPacketRouter::kNever);
267 
268   const Clock::duration total_delay =
269       rtcp_packet_arrival_time_ -
270       sender_report_builder_.GetRecentReportTime(
271           receiver_report.last_status_report_id, rtcp_packet_arrival_time_);
272   const auto non_network_delay =
273       Clock::to_duration(receiver_report.delay_since_last_report);
274 
275   // Round trip time measurement: This is the time elapsed since the Sender
276   // Report was sent, minus the time the Receiver did other stuff before sending
277   // the Receiver Report back.
278   //
279   // If the round trip time seems to be less than or equal to zero, assume clock
280   // imprecision by one or both peers caused a bad value to be calculated. The
281   // true value is likely very close to zero (i.e., this is ideal network
282   // behavior); and so just represent this as 75 µs, an optimistic
283   // wired-Ethernet LAN ping time.
284   constexpr auto kNearZeroRoundTripTime = Clock::to_duration(microseconds(75));
285   static_assert(kNearZeroRoundTripTime > Clock::duration::zero(),
286                 "More precision in Clock::duration needed!");
287   const Clock::duration measurement =
288       std::max(total_delay - non_network_delay, kNearZeroRoundTripTime);
289 
290   // Validate the measurement by using the current target playout delay as a
291   // "reasonable upper-bound." It's certainly possible that the actual network
292   // round-trip time could exceed the target playout delay, but that would mean
293   // the current network performance is totally inadequate for streaming anyway.
294   if (measurement > target_playout_delay_) {
295     OSP_LOG_WARN << "Invalidating a round-trip time measurement ("
296                  << measurement
297                  << ") since it exceeds the current target playout delay ("
298                  << target_playout_delay_ << ").";
299     return;
300   }
301 
302   // Measurements will typically have high variance. Use a simple smoothing
303   // filter to track a short-term average that changes less drastically.
304   if (round_trip_time_ == Clock::duration::zero()) {
305     round_trip_time_ = measurement;
306   } else {
307     // Arbitrary constant, to provide 1/8 weight to the new measurement, and 7/8
308     // weight to the old estimate, which seems to work well for de-noising the
309     // estimate.
310     constexpr int kInertia = 7;
311     round_trip_time_ =
312         (kInertia * round_trip_time_ + measurement) / (kInertia + 1);
313   }
314   // TODO(miu): Add tracing event here to note the updated RTT.
315 }
316 
OnReceiverIndicatesPictureLoss()317 void Sender::OnReceiverIndicatesPictureLoss() {
318   // The Receiver will continue the PLI notifications until it has received a
319   // key frame. Thus, if a key frame is already in-flight, don't make a state
320   // change that would cause this Sender to force another expensive key frame.
321   if (checkpoint_frame_id_ < last_enqueued_key_frame_id_) {
322     return;
323   }
324 
325   picture_lost_at_frame_id_ = checkpoint_frame_id_;
326 
327   if (observer_) {
328     observer_->OnPictureLost();
329   }
330 
331   // Note: It may seem that all pending frames should be canceled until
332   // EnqueueFrame() is called with a key frame. However:
333   //
334   //   1. The Receiver should still be the main authority on what frames/packets
335   //      are being ACK'ed and NACK'ed.
336   //
337   //   2. It may be desirable for the Receiver to be "limping along" in the
338   //      meantime. For example, video may be corrupted but mostly watchable,
339   //      and so it's best for the Sender to continue sending the non-key frames
340   //      until the Receiver indicates otherwise.
341 }
342 
OnReceiverCheckpoint(FrameId frame_id,milliseconds playout_delay)343 void Sender::OnReceiverCheckpoint(FrameId frame_id,
344                                   milliseconds playout_delay) {
345   if (frame_id > last_enqueued_frame_id_) {
346     OSP_LOG_ERROR
347         << "Ignoring checkpoint for " << latest_expected_frame_id_
348         << " because this Sender could not have sent any frames after "
349         << last_enqueued_frame_id_ << '.';
350     return;
351   }
352   // CompoundRtcpParser should guarantee this:
353   OSP_DCHECK(playout_delay >= milliseconds::zero());
354 
355   while (checkpoint_frame_id_ < frame_id) {
356     ++checkpoint_frame_id_;
357     CancelPendingFrame(checkpoint_frame_id_);
358   }
359   latest_expected_frame_id_ = std::max(latest_expected_frame_id_, frame_id);
360 
361   if (playout_delay != target_playout_delay_ &&
362       frame_id >= playout_delay_change_at_frame_id_) {
363     OSP_LOG_WARN << "Sender's target playout delay (" << target_playout_delay_
364                  << ") disagrees with the Receiver's (" << playout_delay << ")";
365   }
366 }
367 
OnReceiverHasFrames(std::vector<FrameId> acks)368 void Sender::OnReceiverHasFrames(std::vector<FrameId> acks) {
369   OSP_DCHECK(!acks.empty() && AreElementsSortedAndUnique(acks));
370 
371   if (acks.back() > last_enqueued_frame_id_) {
372     OSP_LOG_ERROR << "Ignoring individual frame ACKs: ACKing frame "
373                   << latest_expected_frame_id_
374                   << " is invalid because this Sender could not have sent any "
375                      "frames after "
376                   << last_enqueued_frame_id_ << '.';
377     return;
378   }
379 
380   for (FrameId id : acks) {
381     CancelPendingFrame(id);
382   }
383   latest_expected_frame_id_ = std::max(latest_expected_frame_id_, acks.back());
384 }
385 
OnReceiverIsMissingPackets(std::vector<PacketNack> nacks)386 void Sender::OnReceiverIsMissingPackets(std::vector<PacketNack> nacks) {
387   OSP_DCHECK(!nacks.empty() && AreElementsSortedAndUnique(nacks));
388   OSP_DCHECK_NE(rtcp_packet_arrival_time_, SenderPacketRouter::kNever);
389 
390   // This is a point-in-time threshold that indicates whether each NACK will
391   // trigger a packet retransmit. The threshold is based on the network round
392   // trip time because a Receiver's NACK may have been issued while the needed
393   // packet was in-flight from the Sender. In such cases, the Receiver's NACK is
394   // likely stale and this Sender should not redundantly re-transmit the packet
395   // again.
396   const Clock::time_point too_recent_a_send_time =
397       rtcp_packet_arrival_time_ - round_trip_time_;
398 
399   // Iterate over all the NACKs...
400   bool need_to_send = false;
401   for (auto nack_it = nacks.begin(); nack_it != nacks.end();) {
402     // Find the slot associated with the NACK's frame ID.
403     const FrameId frame_id = nack_it->frame_id;
404     PendingFrameSlot* slot = nullptr;
405     if (frame_id <= last_enqueued_frame_id_) {
406       PendingFrameSlot* const candidate_slot = get_slot_for(frame_id);
407       if (candidate_slot->is_active_for_frame(frame_id)) {
408         slot = candidate_slot;
409       }
410     }
411 
412     // If no slot was found (i.e., the NACK is invalid) for the frame, skip-over
413     // all other NACKs for the same frame. While it seems to be a bug that the
414     // Receiver would attempt to NACK a frame that does not yet exist, this can
415     // happen in rare cases where RTCP packets arrive out-of-order (i.e., the
416     // network shuffled them).
417     if (!slot) {
418       // TODO(miu): Add tracing event here to record this.
419       for (++nack_it; nack_it != nacks.end() && nack_it->frame_id == frame_id;
420            ++nack_it) {
421       }
422       continue;
423     }
424 
425     // NOLINTNEXTLINE
426     latest_expected_frame_id_ = std::max(latest_expected_frame_id_, frame_id);
427 
428     const auto HandleIndividualNack = [&](FramePacketId packet_id) {
429       if (slot->packet_sent_times[packet_id] <= too_recent_a_send_time) {
430         slot->send_flags.Set(packet_id);
431         need_to_send = true;
432       }
433     };
434     const FramePacketId range_end = slot->packet_sent_times.size();
435     if (nack_it->packet_id == kAllPacketsLost) {
436       for (FramePacketId packet_id = 0; packet_id < range_end; ++packet_id) {
437         HandleIndividualNack(packet_id);
438       }
439       ++nack_it;
440     } else {
441       do {
442         if (nack_it->packet_id < range_end) {
443           HandleIndividualNack(nack_it->packet_id);
444         } else {
445           OSP_LOG_WARN
446               << "Ignoring NACK for packet that doesn't exist in frame "
447               << frame_id << ": " << static_cast<int>(nack_it->packet_id);
448         }
449         ++nack_it;
450       } while (nack_it != nacks.end() && nack_it->frame_id == frame_id);
451     }
452   }
453 
454   if (need_to_send) {
455     packet_router_->RequestRtpSend(rtcp_session_.receiver_ssrc());
456   }
457 }
458 
ChooseNextRtpPacketNeedingSend()459 Sender::ChosenPacket Sender::ChooseNextRtpPacketNeedingSend() {
460   // Find the oldest packet needing to be sent (or re-sent).
461   for (FrameId frame_id = checkpoint_frame_id_ + 1;
462        frame_id <= last_enqueued_frame_id_; ++frame_id) {
463     PendingFrameSlot* const slot = get_slot_for(frame_id);
464     if (!slot->is_active_for_frame(frame_id)) {
465       continue;  // Frame was canceled. None of its packets need to be sent.
466     }
467     const FramePacketId packet_id = slot->send_flags.FindFirstSet();
468     if (packet_id < slot->send_flags.size()) {
469       return {slot, packet_id};
470     }
471   }
472 
473   return {};  // Nothing needs to be sent.
474 }
475 
ChooseKickstartPacket()476 Sender::ChosenPacketAndWhen Sender::ChooseKickstartPacket() {
477   if (latest_expected_frame_id_ >= last_enqueued_frame_id_) {
478     // Since the Receiver must know about all of the frames currently queued, no
479     // Kickstart packet is necessary.
480     return {};
481   }
482 
483   // The Kickstart packet is always in the last-enqueued frame, so that the
484   // Receiver will know about every frame the Sender has. However, which packet
485   // should be chosen? Any would do, since all packets contain the frame's total
486   // packet count. For historical reasons, all sender implementations have
487   // always just sent the last packet; and so that tradition is continued here.
488   ChosenPacketAndWhen chosen;
489   chosen.slot = get_slot_for(last_enqueued_frame_id_);
490   // Note: This frame cannot have been canceled since
491   // |latest_expected_frame_id_| hasn't yet reached this point.
492   OSP_DCHECK(chosen.slot->is_active_for_frame(last_enqueued_frame_id_));
493   chosen.packet_id = chosen.slot->send_flags.size() - 1;
494 
495   const Clock::time_point time_last_sent =
496       chosen.slot->packet_sent_times[chosen.packet_id];
497   // Sanity-check: This method should not be called to choose a packet while
498   // there are still unsent packets.
499   OSP_DCHECK_NE(time_last_sent, SenderPacketRouter::kNever);
500 
501   // The desired Kickstart interval is a fraction of the total
502   // |target_playout_delay_|. The reason for the specific ratio here is based on
503   // lost knowledge (from legacy implementations); but it makes sense (i.e., to
504   // be a good "network citizen") to be less aggressive for larger playout delay
505   // windows, and more aggressive for shorter ones to avoid too-late packet
506   // arrivals.
507   using kWaitFraction = std::ratio<1, 20>;
508   const Clock::duration desired_kickstart_interval =
509       Clock::to_duration(target_playout_delay_) * kWaitFraction::num /
510       kWaitFraction::den;
511   // The actual interval used is increased, if current network performance
512   // warrants waiting longer. Don't send a Kickstart packet until no NACKs
513   // have been received for two network round-trip periods.
514   constexpr int kLowerBoundRoundTrips = 2;
515   const Clock::duration kickstart_interval = std::max(
516       desired_kickstart_interval, round_trip_time_ * kLowerBoundRoundTrips);
517   chosen.when = time_last_sent + kickstart_interval;
518 
519   return chosen;
520 }
521 
CancelPendingFrame(FrameId frame_id)522 void Sender::CancelPendingFrame(FrameId frame_id) {
523   PendingFrameSlot* const slot = get_slot_for(frame_id);
524   if (!slot->is_active_for_frame(frame_id)) {
525     return;  // Frame was already canceled.
526   }
527 
528   packet_router_->OnPayloadReceived(
529       slot->frame->data.size(), rtcp_packet_arrival_time_, round_trip_time_);
530 
531   slot->frame.reset();
532   OSP_DCHECK_GT(num_frames_in_flight_, 0);
533   --num_frames_in_flight_;
534   if (observer_) {
535     observer_->OnFrameCanceled(frame_id);
536   }
537 }
538 
OnFrameCanceled(FrameId frame_id)539 void Sender::Observer::OnFrameCanceled(FrameId frame_id) {}
OnPictureLost()540 void Sender::Observer::OnPictureLost() {}
541 Sender::Observer::~Observer() = default;
542 
543 Sender::PendingFrameSlot::PendingFrameSlot() = default;
544 Sender::PendingFrameSlot::~PendingFrameSlot() = default;
545 
546 }  // namespace cast
547 }  // namespace openscreen
548