1 // Copyright 2019 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 #ifndef CAST_STREAMING_COMPOUND_RTCP_BUILDER_H_
6 #define CAST_STREAMING_COMPOUND_RTCP_BUILDER_H_
7 
8 #include <chrono>
9 #include <utility>
10 #include <vector>
11 
12 #include "absl/types/optional.h"
13 #include "absl/types/span.h"
14 #include "cast/streaming/constants.h"
15 #include "cast/streaming/frame_id.h"
16 #include "cast/streaming/rtcp_common.h"
17 #include "cast/streaming/rtp_defines.h"
18 
19 namespace openscreen {
20 namespace cast {
21 
22 class RtcpSession;
23 
24 // Collects current status and feedback messages from the Receiver in the
25 // current process, and builds compound RTCP packets to be transmitted to a
26 // Sender.
27 //
28 // Usage:
29 //
30 //   1. Call the various SetXYZ/IncludeXYZInNextPacket() methods as the
31 //      receiver's state changes. The SetXYZ() methods provide values that will
32 //      be included in every RTCP packet until they are changed, while the
33 //      IncludeXYZInNextPacket() methods provide values for only the next-built
34 //      RTCP packet. The latter case is part of the overall protocol design, to
35 //      help prevent the Sender from acting on stale Receiver state.
36 //
37 //   2. At certain times, call BuildPacket() and transmit it to the sender:
38 //      a. By default, every 1/2 sec, to provide the sender with a "keep alive"
39 //         ping that it can also use to monitor network round-trip times.
40 //      b. When there is new feedback, the collected information should be
41 //         immediately conveyed to the sender.
42 class CompoundRtcpBuilder {
43  public:
44   explicit CompoundRtcpBuilder(RtcpSession* session);
45   ~CompoundRtcpBuilder();
46 
47   // Gets/Sets the checkpoint |frame_id| that will be included in built RTCP
48   // packets. This value indicates to the sender that all of the packets for all
49   // frames up to and including the given frame have been successfully received.
checkpoint_frame()50   FrameId checkpoint_frame() const { return checkpoint_frame_id_; }
51   void SetCheckpointFrame(FrameId frame_id);
52 
53   // Gets/Sets the current end-to-end target playout delay setting for the Cast
54   // RTP receiver, to be included in built RTCP packets. This reflect any
55   // changes the sender has made by using the "Cast Adaptive Latency Extension"
56   // in received RTP packets.
playout_delay()57   std::chrono::milliseconds playout_delay() const { return playout_delay_; }
58   void SetPlayoutDelay(std::chrono::milliseconds delay);
59 
60   // Gets/Sets the picture loss indicator flag. While this is set, built RTCP
61   // packets will include a PLI message that indicates to the sender that there
62   // has been an unrecoverable decoding error. This asks the sender to provide a
63   // key frame as soon as possible. The client must explicitly clear this flag
64   // when decoding will recover.
is_picture_loss_indicator_set()65   bool is_picture_loss_indicator_set() const { return picture_loss_indicator_; }
66   void SetPictureLossIndicator(bool picture_is_lost);
67 
68   // Include a receiver report about recent packet receive activity in ONLY the
69   // next built RTCP packet. This replaces a prior receiver report if
70   // BuildPacket() was not called in the meantime (since only the most
71   // up-to-date version of the Receiver's state is relevant to the Sender).
72   void IncludeReceiverReportInNextPacket(
73       const RtcpReportBlock& receiver_report);
74 
75   // Include detailed feedback about wholly-received frames, whole missing
76   // frames, and partially-received frames (specific missing packets) in ONLY
77   // the next built RTCP packet. The data will be included in a best-effort
78   // fashion, depending on the size of the |buffer| passed to the next call to
79   // BuildPacket(). This replaces prior feedback data if BuildPacket() was not
80   // called in the meantime (since only the most up-to-date version of the
81   // Receiver's state is relevant to the Sender).
82   //
83   // The elements in the lists are assumed to be monotonically increasing:
84   // |packet_nacks| indicates specific packets that have not yet been received,
85   // or may use kAllPacketsLost to indicate that no packets have been received
86   // for a frame. |frame_acks| indicates which frames after the checkpoint frame
87   // have been fully received.
88   void IncludeFeedbackInNextPacket(std::vector<PacketNack> packet_nacks,
89                                    std::vector<FrameId> frame_acks);
90 
91   // Builds a compound RTCP packet and returns the portion of the |buffer| that
92   // was used. The buffer's size must be at least kRequiredBufferSize, but
93   // should generally be the maximum packet size (see discussion in
94   // rtp_defines.h), to avoid dropping any ACK/NACK feedback.
95   //
96   // |send_time| specifies the when the resulting packet will be sent. This
97   // should be monotonically increasing so the consuming side (the Sender) can
98   // determine the chronological ordering of RTCP packets. The Sender might also
99   // use this to estimate round-trip times over the network.
100   absl::Span<uint8_t> BuildPacket(Clock::time_point send_time,
101                                   absl::Span<uint8_t> buffer);
102 
103   // The required buffer size to be provided to BuildPacket(). This accounts for
104   // all the possible headers and report structures that might be included,
105   // along with a reasonable amount of space for the feedback's ACK/NACKs bit
106   // vectors.
107   static constexpr int kRequiredBufferSize = 256;
108 
109  private:
110   // Helper methods called by BuildPacket() to append one RTCP packet to the
111   // |buffer| that will ultimately contain a "compound RTCP packet."
112   void AppendReceiverReportPacket(absl::Span<uint8_t>* buffer);
113   void AppendReceiverReferenceTimeReportPacket(Clock::time_point send_time,
114                                                absl::Span<uint8_t>* buffer);
115   void AppendPictureLossIndicatorPacket(absl::Span<uint8_t>* buffer);
116   void AppendCastFeedbackPacket(absl::Span<uint8_t>* buffer);
117   int AppendCastFeedbackLossFields(absl::Span<uint8_t>* buffer);
118   void AppendCastFeedbackAckFields(absl::Span<uint8_t>* buffer);
119 
120   RtcpSession* const session_;
121 
122   // Data to include in the next built RTCP packet.
123   FrameId checkpoint_frame_id_ = FrameId::leader();
124   std::chrono::milliseconds playout_delay_ = kDefaultTargetPlayoutDelay;
125   absl::optional<RtcpReportBlock> receiver_report_for_next_packet_;
126   std::vector<PacketNack> nacks_for_next_packet_;
127   std::vector<FrameId> acks_for_next_packet_;
128   bool picture_loss_indicator_ = false;
129 
130   // An 8-bit wrap-around counter that tracks how many times Cast Feedback has
131   // been included in the built RTCP packets.
132   uint8_t feedback_count_ = 0;
133 };
134 
135 }  // namespace cast
136 }  // namespace openscreen
137 
138 #endif  // CAST_STREAMING_COMPOUND_RTCP_BUILDER_H_
139