aboutsummaryrefslogtreecommitdiff
path: root/cast/streaming/rtcp_common.h
blob: 25e2c2ed7e5b72c6c439c05448887f5840ad6769 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// 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.

#ifndef CAST_STREAMING_RTCP_COMMON_H_
#define CAST_STREAMING_RTCP_COMMON_H_

#include <stdint.h>

#include <tuple>
#include <vector>

#include "absl/types/optional.h"
#include "absl/types/span.h"
#include "cast/streaming/frame_id.h"
#include "cast/streaming/ntp_time.h"
#include "cast/streaming/rtp_defines.h"
#include "cast/streaming/rtp_time.h"
#include "cast/streaming/ssrc.h"

namespace openscreen {
namespace cast {

struct RtcpCommonHeader {
  RtcpCommonHeader();
  ~RtcpCommonHeader();

  RtcpPacketType packet_type = RtcpPacketType::kNull;

  union {
    // The number of report blocks if |packet_type| is kSenderReport or
    // kReceiverReport.
    int report_count;

    // Indicates the type of an application-defined message if |packet_type| is
    // kApplicationDefined or kPayloadSpecific.
    RtcpSubtype subtype;

    // Otherwise, not used.
  } with{0};

  // The size (in bytes) of the RTCP packet, not including the header.
  int payload_size = 0;

  // Serializes this header into the first |kRtcpCommonHeaderSize| bytes of the
  // given |buffer| and adjusts |buffer| to point to the first byte after it.
  void AppendFields(absl::Span<uint8_t>* buffer) const;

  // Parse from the 4-byte wire format in |buffer|. Returns nullopt if the data
  // is corrupt.
  static absl::optional<RtcpCommonHeader> Parse(
      absl::Span<const uint8_t> buffer);
};

// The middle 32-bits of the 64-bit NtpTimestamp field from the Sender Reports.
// This is used as an opaque identifier that the Receiver will use in its
// reports to refer to specific previous Sender Reports.
using StatusReportId = uint32_t;
constexpr StatusReportId ToStatusReportId(NtpTimestamp ntp_timestamp) {
  return static_cast<uint32_t>(ntp_timestamp >> 16);
}

// One of these is optionally included with a Sender Report or a Receiver
// Report. See: https://tools.ietf.org/html/rfc3550#section-6.4.1
struct RtcpReportBlock {
  RtcpReportBlock();
  ~RtcpReportBlock();

  // The intended recipient of this report block.
  Ssrc ssrc = 0;

  // The fraction of RTP packets lost since the last report, specified as a
  // variable numerator and fixed denominator. The numerator will always be in
  // the range [0,255] since, semantically:
  //
  //   a. Negative values are impossible.
  //   b. Values greater than 255 would indicate 100% packet loss, and so a
  //      report block would not be generated in the first place.
  int packet_fraction_lost_numerator = 0;
  static constexpr int kPacketFractionLostDenominator = 256;

  // The total number of RTP packets lost since the start of the session. This
  // value will always be in the range [0,2^24-1], as the wire format only
  // provides 24 bits; so, wrap-around is possible.
  int cumulative_packets_lost = 0;

  // The highest sequence number received in any RTP packet. Wrap-around is
  // possible.
  uint32_t extended_high_sequence_number = 0;

  // An estimate of the recent variance in RTP packet arrival times.
  RtpTimeDelta jitter;

  // The last Status Report received.
  StatusReportId last_status_report_id{};

  // The delay between when the peer received the most-recent Status Report and
  // when this report was sent. The timebase is 65536 ticks per second and,
  // because of the wire format, this value will always be in the range
  // [0,65536) seconds.
  using Delay = std::chrono::duration<int64_t, std::ratio<1, 65536>>;
  Delay delay_since_last_report{};

  // Convenience helper to compute/assign the |packet_fraction_lost_numerator|,
  // based on the |num_apparently_sent| and |num_received| packet counts since
  // the last report was sent.
  void SetPacketFractionLostNumerator(int64_t num_apparently_sent,
                                      int64_t num_received);

  // Convenience helper to compute/assign the |cumulative_packets_lost|, based
  // on the |num_apparently_sent| and |num_received| packet counts since the
  // start of the entire session.
  void SetCumulativePacketsLost(int64_t num_apparently_sent,
                                int64_t num_received);

  // Convenience helper to convert the given |local_clock_delay| to the
  // RtcpReportBlock::Delay timebase, then clamp and assign it to
  // |delay_since_last_report|.
  void SetDelaySinceLastReport(Clock::duration local_clock_delay);

  // Serializes this report block in the first |kRtcpReportBlockSize| bytes of
  // the given |buffer| and adjusts |buffer| to point to the first byte after
  // it.
  void AppendFields(absl::Span<uint8_t>* buffer) const;

  // Scans the wire-format report blocks in |buffer|, searching for one with the
  // matching |ssrc| and, if found, returns the parse result. Returns nullopt if
  // the data is corrupt or no report block with the matching SSRC was found.
  static absl::optional<RtcpReportBlock>
  ParseOne(absl::Span<const uint8_t> buffer, int report_count, Ssrc ssrc);
};

struct RtcpSenderReport {
  RtcpSenderReport();
  ~RtcpSenderReport();

  // The point-in-time at which this report was sent, according to both: 1) the
  // common reference clock shared by all RTP streams; 2) the RTP timestamp on
  // the media capture/playout timeline. Together, these are used by a Receiver
  // to achieve A/V synchronization across RTP streams for playout.
  Clock::time_point reference_time{};
  RtpTimeTicks rtp_timestamp;

  // The total number of RTP packets transmitted since the start of the session
  // (wrap-around is possible).
  uint32_t send_packet_count = 0;

  // The total number of payload bytes transmitted in RTP packets since the
  // start of the session (wrap-around is possible).
  uint32_t send_octet_count = 0;

  // The report block, if present. While the RTCP spec allows for zero or
  // multiple reports, Cast Streaming only uses zero or one.
  absl::optional<RtcpReportBlock> report_block;
};

// A pair of IDs that refers to a specific missing packet within a frame. If
// |packet_id| is kAllPacketsLost, then it represents all the packets of a
// frame.
struct PacketNack {
  FrameId frame_id;
  FramePacketId packet_id;

  // Comparison operators. Define more when you need them!
  // TODO(miu): In C++20, just
  // replace all of this with one operator<=>() definition to get them all for
  // free.
  constexpr bool operator==(const PacketNack& other) const {
    return frame_id == other.frame_id && packet_id == other.packet_id;
  }
  constexpr bool operator!=(const PacketNack& other) const {
    return frame_id != other.frame_id || packet_id != other.packet_id;
  }
  constexpr bool operator<(const PacketNack& other) const {
    return (frame_id < other.frame_id) ||
           (frame_id == other.frame_id && packet_id < other.packet_id);
  }
};

}  // namespace cast
}  // namespace openscreen

#endif  // CAST_STREAMING_RTCP_COMMON_H_