blob: 787a6e5231fe8f9f0a26e369edfc11953ddcbec9 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <chrono>
#include <utility>
#include <vector>
#include "absl/types/optional.h"
#include "absl/types/span.h"
#include "cast/streaming/constants.h"
#include "cast/streaming/frame_id.h"
#include "cast/streaming/rtcp_common.h"
#include "cast/streaming/rtp_defines.h"
namespace openscreen {
namespace cast {
class RtcpSession;
// Collects current status and feedback messages from the Receiver in the
// current process, and builds compound RTCP packets to be transmitted to a
// Sender.
// Usage:
// 1. Call the various SetXYZ/IncludeXYZInNextPacket() methods as the
// receiver's state changes. The SetXYZ() methods provide values that will
// be included in every RTCP packet until they are changed, while the
// IncludeXYZInNextPacket() methods provide values for only the next-built
// RTCP packet. The latter case is part of the overall protocol design, to
// help prevent the Sender from acting on stale Receiver state.
// 2. At certain times, call BuildPacket() and transmit it to the sender:
// a. By default, every 1/2 sec, to provide the sender with a "keep alive"
// ping that it can also use to monitor network round-trip times.
// b. When there is new feedback, the collected information should be
// immediately conveyed to the sender.
class CompoundRtcpBuilder {
explicit CompoundRtcpBuilder(RtcpSession* session);
// Gets/Sets the checkpoint |frame_id| that will be included in built RTCP
// packets. This value indicates to the sender that all of the packets for all
// frames up to and including the given frame have been successfully received.
FrameId checkpoint_frame() const { return checkpoint_frame_id_; }
void SetCheckpointFrame(FrameId frame_id);
// Gets/Sets the current end-to-end target playout delay setting for the Cast
// RTP receiver, to be included in built RTCP packets. This reflect any
// changes the sender has made by using the "Cast Adaptive Latency Extension"
// in received RTP packets.
std::chrono::milliseconds playout_delay() const { return playout_delay_; }
void SetPlayoutDelay(std::chrono::milliseconds delay);
// Gets/Sets the picture loss indicator flag. While this is set, built RTCP
// packets will include a PLI message that indicates to the sender that there
// has been an unrecoverable decoding error. This asks the sender to provide a
// key frame as soon as possible. The client must explicitly clear this flag
// when decoding will recover.
bool is_picture_loss_indicator_set() const { return picture_loss_indicator_; }
void SetPictureLossIndicator(bool picture_is_lost);
// Include a receiver report about recent packet receive activity in ONLY the
// next built RTCP packet. This replaces a prior receiver report if
// BuildPacket() was not called in the meantime (since only the most
// up-to-date version of the Receiver's state is relevant to the Sender).
void IncludeReceiverReportInNextPacket(
const RtcpReportBlock& receiver_report);
// Include detailed feedback about wholly-received frames, whole missing
// frames, and partially-received frames (specific missing packets) in ONLY
// the next built RTCP packet. The data will be included in a best-effort
// fashion, depending on the size of the |buffer| passed to the next call to
// BuildPacket(). This replaces prior feedback data if BuildPacket() was not
// called in the meantime (since only the most up-to-date version of the
// Receiver's state is relevant to the Sender).
// The elements in the lists are assumed to be monotonically increasing:
// |packet_nacks| indicates specific packets that have not yet been received,
// or may use kAllPacketsLost to indicate that no packets have been received
// for a frame. |frame_acks| indicates which frames after the checkpoint frame
// have been fully received.
void IncludeFeedbackInNextPacket(std::vector<PacketNack> packet_nacks,
std::vector<FrameId> frame_acks);
// Builds a compound RTCP packet and returns the portion of the |buffer| that
// was used. The buffer's size must be at least kRequiredBufferSize, but
// should generally be the maximum packet size (see discussion in
// rtp_defines.h), to avoid dropping any ACK/NACK feedback.
// |send_time| specifies the when the resulting packet will be sent. This
// should be monotonically increasing so the consuming side (the Sender) can
// determine the chronological ordering of RTCP packets. The Sender might also
// use this to estimate round-trip times over the network.
absl::Span<uint8_t> BuildPacket(Clock::time_point send_time,
absl::Span<uint8_t> buffer);
// The required buffer size to be provided to BuildPacket(). This accounts for
// all the possible headers and report structures that might be included,
// along with a reasonable amount of space for the feedback's ACK/NACKs bit
// vectors.
static constexpr int kRequiredBufferSize = 256;
// Helper methods called by BuildPacket() to append one RTCP packet to the
// |buffer| that will ultimately contain a "compound RTCP packet."
void AppendReceiverReportPacket(absl::Span<uint8_t>* buffer);
void AppendReceiverReferenceTimeReportPacket(Clock::time_point send_time,
absl::Span<uint8_t>* buffer);
void AppendPictureLossIndicatorPacket(absl::Span<uint8_t>* buffer);
void AppendCastFeedbackPacket(absl::Span<uint8_t>* buffer);
int AppendCastFeedbackLossFields(absl::Span<uint8_t>* buffer);
void AppendCastFeedbackAckFields(absl::Span<uint8_t>* buffer);
RtcpSession* const session_;
// Data to include in the next built RTCP packet.
FrameId checkpoint_frame_id_ = FrameId::leader();
std::chrono::milliseconds playout_delay_ = kDefaultTargetPlayoutDelay;
absl::optional<RtcpReportBlock> receiver_report_for_next_packet_;
std::vector<PacketNack> nacks_for_next_packet_;
std::vector<FrameId> acks_for_next_packet_;
bool picture_loss_indicator_ = false;
// An 8-bit wrap-around counter that tracks how many times Cast Feedback has
// been included in the built RTCP packets.
uint8_t feedback_count_ = 0;
} // namespace cast
} // namespace openscreen