turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license |
| 5 | * that can be found in the LICENSE file in the root of the source |
| 6 | * tree. An additional intellectual property rights grant can be found |
| 7 | * in the file PATENTS. All contributing project authors may |
| 8 | * be found in the AUTHORS file in the root of the source tree. |
| 9 | */ |
| 10 | |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 11 | #ifndef MODULES_AUDIO_CODING_NETEQ_NACK_TRACKER_H_ |
| 12 | #define MODULES_AUDIO_CODING_NETEQ_NACK_TRACKER_H_ |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 13 | |
Yves Gerey | 988cc08 | 2018-10-23 12:03:01 +0200 | [diff] [blame] | 14 | #include <stddef.h> |
| 15 | #include <stdint.h> |
Jonas Olsson | a4d8737 | 2019-07-05 19:08:33 +0200 | [diff] [blame] | 16 | |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 17 | #include <map> |
Yves Gerey | 665174f | 2018-06-19 15:03:05 +0200 | [diff] [blame] | 18 | #include <vector> |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 19 | |
Jakob Ivarsson | 018cd3d | 2021-09-13 16:06:28 +0200 | [diff] [blame] | 20 | #include "absl/types/optional.h" |
Yves Gerey | 988cc08 | 2018-10-23 12:03:01 +0200 | [diff] [blame] | 21 | #include "modules/include/module_common_types_public.h" |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 22 | #include "rtc_base/gtest_prod_util.h" |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 23 | |
| 24 | // |
henrik.lundin | 9195186 | 2016-06-08 06:43:41 -0700 | [diff] [blame] | 25 | // The NackTracker class keeps track of the lost packets, an estimate of |
| 26 | // time-to-play for each packet is also given. |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 27 | // |
| 28 | // Every time a packet is pushed into NetEq, LastReceivedPacket() has to be |
| 29 | // called to update the NACK list. |
| 30 | // |
| 31 | // Every time 10ms audio is pulled from NetEq LastDecodedPacket() should be |
| 32 | // called, and time-to-play is updated at that moment. |
| 33 | // |
Ivo Creusen | bc503c9 | 2021-09-14 15:17:23 +0000 | [diff] [blame] | 34 | // If packet N is received, any packet prior to N which has not arrived is |
| 35 | // considered lost, and should be labeled as "missing" (the size of |
| 36 | // the list might be limited and older packet eliminated from the list). |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 37 | // |
henrik.lundin | 9195186 | 2016-06-08 06:43:41 -0700 | [diff] [blame] | 38 | // The NackTracker class has to know about the sample rate of the packets to |
| 39 | // compute time-to-play. So sample rate should be set as soon as the first |
| 40 | // packet is received. If there is a change in the receive codec (sender changes |
| 41 | // codec) then NackTracker should be reset. This is because NetEQ would flush |
| 42 | // its buffer and re-transmission is meaning less for old packet. Therefore, in |
| 43 | // that case, after reset the sampling rate has to be updated. |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 44 | // |
| 45 | // Thread Safety |
| 46 | // ============= |
| 47 | // Please note that this class in not thread safe. The class must be protected |
| 48 | // if different APIs are called from different threads. |
| 49 | // |
| 50 | namespace webrtc { |
| 51 | |
henrik.lundin | 9195186 | 2016-06-08 06:43:41 -0700 | [diff] [blame] | 52 | class NackTracker { |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 53 | public: |
| 54 | // A limit for the size of the NACK list. |
| 55 | static const size_t kNackListSizeLimit = 500; // 10 seconds for 20 ms frame |
| 56 | // packets. |
Ivo Creusen | bc503c9 | 2021-09-14 15:17:23 +0000 | [diff] [blame] | 57 | NackTracker(); |
henrik.lundin | 9195186 | 2016-06-08 06:43:41 -0700 | [diff] [blame] | 58 | ~NackTracker(); |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 59 | |
| 60 | // Set a maximum for the size of the NACK list. If the last received packet |
| 61 | // has sequence number of N, then NACK list will not contain any element |
Artem Titov | d00ce74 | 2021-07-28 20:00:17 +0200 | [diff] [blame] | 62 | // with sequence number earlier than N - `max_nack_list_size`. |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 63 | // |
Artem Titov | d00ce74 | 2021-07-28 20:00:17 +0200 | [diff] [blame] | 64 | // The largest maximum size is defined by `kNackListSizeLimit` |
henrik.lundin | 48ed930 | 2015-10-29 05:36:24 -0700 | [diff] [blame] | 65 | void SetMaxNackListSize(size_t max_nack_list_size); |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 66 | |
| 67 | // Set the sampling rate. |
| 68 | // |
| 69 | // If associated sampling rate of the received packets is changed, call this |
| 70 | // function to update sampling rate. Note that if there is any change in |
| 71 | // received codec then NetEq will flush its buffer and NACK has to be reset. |
| 72 | // After Reset() is called sampling rate has to be set. |
| 73 | void UpdateSampleRate(int sample_rate_hz); |
| 74 | |
| 75 | // Update the sequence number and the timestamp of the last decoded RTP. This |
| 76 | // API should be called every time 10 ms audio is pulled from NetEq. |
| 77 | void UpdateLastDecodedPacket(uint16_t sequence_number, uint32_t timestamp); |
| 78 | |
| 79 | // Update the sequence number and the timestamp of the last received RTP. This |
| 80 | // API should be called every time a packet pushed into ACM. |
| 81 | void UpdateLastReceivedPacket(uint16_t sequence_number, uint32_t timestamp); |
| 82 | |
| 83 | // Get a list of "missing" packets which have expected time-to-play larger |
| 84 | // than the given round-trip-time (in milliseconds). |
| 85 | // Note: Late packets are not included. |
Ivo Creusen | 3a9640a | 2021-09-10 11:45:47 +0000 | [diff] [blame] | 86 | // Calling this method multiple times may give different results, since the |
| 87 | // internal nack list may get flushed if never_nack_multiple_times_ is true. |
| 88 | std::vector<uint16_t> GetNackList(int64_t round_trip_time_ms); |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 89 | |
| 90 | // Reset to default values. The NACK list is cleared. |
Ivo Creusen | bc503c9 | 2021-09-14 15:17:23 +0000 | [diff] [blame] | 91 | // `max_nack_list_size_` preserves its value. |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 92 | void Reset(); |
| 93 | |
Ivo Creusen | 3a9640a | 2021-09-10 11:45:47 +0000 | [diff] [blame] | 94 | // Returns the estimated packet loss rate in Q30, for testing only. |
| 95 | uint32_t GetPacketLossRateForTest() { return packet_loss_rate_; } |
| 96 | |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 97 | private: |
| 98 | // This test need to access the private method GetNackList(). |
henrik.lundin | 9195186 | 2016-06-08 06:43:41 -0700 | [diff] [blame] | 99 | FRIEND_TEST_ALL_PREFIXES(NackTrackerTest, EstimateTimestampAndTimeToPlay); |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 100 | |
Ivo Creusen | 3a9640a | 2021-09-10 11:45:47 +0000 | [diff] [blame] | 101 | // Options that can be configured via field trial. |
| 102 | struct Config { |
| 103 | Config(); |
| 104 | |
| 105 | // The exponential decay factor used to estimate the packet loss rate. |
| 106 | double packet_loss_forget_factor = 0.996; |
| 107 | // How many additional ms we are willing to wait (at most) for nacked |
| 108 | // packets for each additional percentage of packet loss. |
| 109 | int ms_per_loss_percent = 20; |
| 110 | // If true, never nack packets more than once. |
| 111 | bool never_nack_multiple_times = false; |
Jakob Ivarsson | e1bbef1 | 2021-11-25 15:34:57 +0100 | [diff] [blame] | 112 | // Only nack if the RTT is valid. |
| 113 | bool require_valid_rtt = false; |
Jakob Ivarsson | 4d5eb65 | 2022-03-30 16:26:19 +0200 | [diff] [blame] | 114 | // Default RTT to use unless `require_valid_rtt` is set. |
| 115 | int default_rtt_ms = 100; |
Jakob Ivarsson | e1bbef1 | 2021-11-25 15:34:57 +0100 | [diff] [blame] | 116 | // Do not nack if the loss rate is above this value. |
| 117 | double max_loss_rate = 1.0; |
Ivo Creusen | 3a9640a | 2021-09-10 11:45:47 +0000 | [diff] [blame] | 118 | }; |
| 119 | |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 120 | struct NackElement { |
Ivo Creusen | bc503c9 | 2021-09-14 15:17:23 +0000 | [diff] [blame] | 121 | NackElement(int64_t initial_time_to_play_ms, uint32_t initial_timestamp) |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 122 | : time_to_play_ms(initial_time_to_play_ms), |
Ivo Creusen | bc503c9 | 2021-09-14 15:17:23 +0000 | [diff] [blame] | 123 | estimated_timestamp(initial_timestamp) {} |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 124 | |
| 125 | // Estimated time (ms) left for this packet to be decoded. This estimate is |
| 126 | // updated every time jitter buffer decodes a packet. |
pkasting@chromium.org | 16825b1 | 2015-01-12 21:51:21 +0000 | [diff] [blame] | 127 | int64_t time_to_play_ms; |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 128 | |
| 129 | // A guess about the timestamp of the missing packet, it is used for |
Artem Titov | d00ce74 | 2021-07-28 20:00:17 +0200 | [diff] [blame] | 130 | // estimation of `time_to_play_ms`. The estimate might be slightly wrong if |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 131 | // there has been frame-size change since the last received packet and the |
| 132 | // missing packet. However, the risk of this is low, and in case of such |
| 133 | // errors, there will be a minor misestimation in time-to-play of missing |
| 134 | // packets. This will have a very minor effect on NACK performance. |
| 135 | uint32_t estimated_timestamp; |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 136 | }; |
| 137 | |
| 138 | class NackListCompare { |
| 139 | public: |
henrik.lundin | 48ed930 | 2015-10-29 05:36:24 -0700 | [diff] [blame] | 140 | bool operator()(uint16_t sequence_number_old, |
| 141 | uint16_t sequence_number_new) const { |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 142 | return IsNewerSequenceNumber(sequence_number_new, sequence_number_old); |
| 143 | } |
| 144 | }; |
| 145 | |
| 146 | typedef std::map<uint16_t, NackElement, NackListCompare> NackList; |
| 147 | |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 148 | // This API is used only for testing to assess whether time-to-play is |
| 149 | // computed correctly. |
| 150 | NackList GetNackList() const; |
| 151 | |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 152 | // This function subtracts 10 ms of time-to-play for all packets in NACK list. |
| 153 | // This is called when 10 ms elapsed with no new RTP packet decoded. |
| 154 | void UpdateEstimatedPlayoutTimeBy10ms(); |
| 155 | |
Jakob Ivarsson | 018cd3d | 2021-09-13 16:06:28 +0200 | [diff] [blame] | 156 | // Returns a valid number of samples per packet given the current received |
| 157 | // sequence number and timestamp or nullopt of none could be computed. |
| 158 | absl::optional<int> GetSamplesPerPacket( |
| 159 | uint16_t sequence_number_current_received_rtp, |
| 160 | uint32_t timestamp_current_received_rtp) const; |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 161 | |
Artem Titov | d00ce74 | 2021-07-28 20:00:17 +0200 | [diff] [blame] | 162 | // Given the `sequence_number_current_received_rtp` of currently received RTP |
Ivo Creusen | bc503c9 | 2021-09-14 15:17:23 +0000 | [diff] [blame] | 163 | // update the list. Packets that are older than the received packet are added |
| 164 | // to the nack list. |
Jakob Ivarsson | 018cd3d | 2021-09-13 16:06:28 +0200 | [diff] [blame] | 165 | void UpdateList(uint16_t sequence_number_current_received_rtp, |
| 166 | uint32_t timestamp_current_received_rtp); |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 167 | |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 168 | // Packets which have sequence number older that |
Artem Titov | d00ce74 | 2021-07-28 20:00:17 +0200 | [diff] [blame] | 169 | // `sequence_num_last_received_rtp_` - `max_nack_list_size_` are removed |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 170 | // from the NACK list. |
| 171 | void LimitNackListSize(); |
| 172 | |
| 173 | // Estimate timestamp of a missing packet given its sequence number. |
Jakob Ivarsson | 018cd3d | 2021-09-13 16:06:28 +0200 | [diff] [blame] | 174 | uint32_t EstimateTimestamp(uint16_t sequence_number, int samples_per_packet); |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 175 | |
| 176 | // Compute time-to-play given a timestamp. |
pkasting@chromium.org | 16825b1 | 2015-01-12 21:51:21 +0000 | [diff] [blame] | 177 | int64_t TimeToPlay(uint32_t timestamp) const; |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 178 | |
Ivo Creusen | 3a9640a | 2021-09-10 11:45:47 +0000 | [diff] [blame] | 179 | // Updates the estimated packet lost rate. |
| 180 | void UpdatePacketLossRate(int packets_lost); |
| 181 | |
| 182 | const Config config_; |
| 183 | |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 184 | // Valid if a packet is received. |
| 185 | uint16_t sequence_num_last_received_rtp_; |
| 186 | uint32_t timestamp_last_received_rtp_; |
| 187 | bool any_rtp_received_; // If any packet received. |
| 188 | |
| 189 | // Valid if a packet is decoded. |
| 190 | uint16_t sequence_num_last_decoded_rtp_; |
| 191 | uint32_t timestamp_last_decoded_rtp_; |
| 192 | bool any_rtp_decoded_; // If any packet decoded. |
| 193 | |
| 194 | int sample_rate_khz_; // Sample rate in kHz. |
| 195 | |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 196 | // A list of missing packets to be retransmitted. Components of the list |
| 197 | // contain the sequence number of missing packets and the estimated time that |
| 198 | // each pack is going to be played out. |
| 199 | NackList nack_list_; |
| 200 | |
| 201 | // NACK list will not keep track of missing packets prior to |
Artem Titov | d00ce74 | 2021-07-28 20:00:17 +0200 | [diff] [blame] | 202 | // `sequence_num_last_received_rtp_` - `max_nack_list_size_`. |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 203 | size_t max_nack_list_size_; |
Ivo Creusen | 3a9640a | 2021-09-10 11:45:47 +0000 | [diff] [blame] | 204 | |
| 205 | // Current estimate of the packet loss rate in Q30. |
| 206 | uint32_t packet_loss_rate_ = 0; |
turaj@webrtc.org | 7959e16 | 2013-09-12 18:30:26 +0000 | [diff] [blame] | 207 | }; |
| 208 | |
| 209 | } // namespace webrtc |
| 210 | |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 211 | #endif // MODULES_AUDIO_CODING_NETEQ_NACK_TRACKER_H_ |