1 /*
2  *  Copyright (c) 2012 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 
11 #include "webrtc/video_engine/vie_receiver.h"
12 
13 #include <vector>
14 
15 #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
16 #include "webrtc/modules/rtp_rtcp/interface/fec_receiver.h"
17 #include "webrtc/modules/rtp_rtcp/interface/receive_statistics.h"
18 #include "webrtc/modules/rtp_rtcp/interface/remote_ntp_time_estimator.h"
19 #include "webrtc/modules/rtp_rtcp/interface/rtp_cvo.h"
20 #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
21 #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h"
22 #include "webrtc/modules/rtp_rtcp/interface/rtp_receiver.h"
23 #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
24 #include "webrtc/modules/utility/interface/rtp_dump.h"
25 #include "webrtc/modules/video_coding/main/interface/video_coding.h"
26 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
27 #include "webrtc/system_wrappers/interface/logging.h"
28 #include "webrtc/system_wrappers/interface/metrics.h"
29 #include "webrtc/system_wrappers/interface/tick_util.h"
30 #include "webrtc/system_wrappers/interface/timestamp_extrapolator.h"
31 #include "webrtc/system_wrappers/interface/trace.h"
32 
33 namespace webrtc {
34 
35 static const int kPacketLogIntervalMs = 10000;
36 
ViEReceiver(const int32_t channel_id,VideoCodingModule * module_vcm,RemoteBitrateEstimator * remote_bitrate_estimator,RtpFeedback * rtp_feedback)37 ViEReceiver::ViEReceiver(const int32_t channel_id,
38                          VideoCodingModule* module_vcm,
39                          RemoteBitrateEstimator* remote_bitrate_estimator,
40                          RtpFeedback* rtp_feedback)
41     : receive_cs_(CriticalSectionWrapper::CreateCriticalSection()),
42       clock_(Clock::GetRealTimeClock()),
43       rtp_header_parser_(RtpHeaderParser::Create()),
44       rtp_payload_registry_(
45           new RTPPayloadRegistry(RTPPayloadStrategy::CreateStrategy(false))),
46       rtp_receiver_(
47           RtpReceiver::CreateVideoReceiver(channel_id,
48                                            clock_,
49                                            this,
50                                            rtp_feedback,
51                                            rtp_payload_registry_.get())),
52       rtp_receive_statistics_(ReceiveStatistics::Create(clock_)),
53       fec_receiver_(FecReceiver::Create(this)),
54       rtp_rtcp_(NULL),
55       vcm_(module_vcm),
56       remote_bitrate_estimator_(remote_bitrate_estimator),
57       ntp_estimator_(new RemoteNtpTimeEstimator(clock_)),
58       rtp_dump_(NULL),
59       receiving_(false),
60       receiving_rtcp_(false),
61       restored_packet_in_use_(false),
62       receiving_ast_enabled_(false),
63       receiving_cvo_enabled_(false),
64       receiving_rid_enabled_(false),
65       last_packet_log_ms_(-1) {
66   assert(remote_bitrate_estimator);
67 }
68 
~ViEReceiver()69 ViEReceiver::~ViEReceiver() {
70   UpdateHistograms();
71   if (rtp_dump_) {
72     rtp_dump_->Stop();
73     RtpDump::DestroyRtpDump(rtp_dump_);
74     rtp_dump_ = NULL;
75   }
76 }
77 
UpdateHistograms()78 void ViEReceiver::UpdateHistograms() {
79   FecPacketCounter counter = fec_receiver_->GetPacketCounter();
80   if (counter.num_packets > 0) {
81     RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.ReceivedFecPacketsInPercent",
82         counter.num_fec_packets * 100 / counter.num_packets);
83   }
84   if (counter.num_fec_packets > 0) {
85     RTC_HISTOGRAM_PERCENTAGE(
86         "WebRTC.Video.RecoveredMediaPacketsInPercentOfFec",
87             counter.num_recovered_packets * 100 / counter.num_fec_packets);
88   }
89 }
90 
SetReceiveCodec(const VideoCodec & video_codec)91 bool ViEReceiver::SetReceiveCodec(const VideoCodec& video_codec) {
92   int8_t old_pltype = -1;
93   if (rtp_payload_registry_->ReceivePayloadType(video_codec.plName,
94                                                 kVideoPayloadTypeFrequency,
95                                                 0,
96                                                 video_codec.maxBitrate,
97                                                 &old_pltype) != -1) {
98     rtp_payload_registry_->DeRegisterReceivePayload(old_pltype);
99   }
100 
101   return RegisterPayload(video_codec);
102 }
103 
RegisterPayload(const VideoCodec & video_codec)104 bool ViEReceiver::RegisterPayload(const VideoCodec& video_codec) {
105   return rtp_receiver_->RegisterReceivePayload(video_codec.plName,
106                                                video_codec.plType,
107                                                kVideoPayloadTypeFrequency,
108                                                0,
109                                                video_codec.maxBitrate) == 0;
110 }
111 
SetNackStatus(bool enable,int max_nack_reordering_threshold)112 void ViEReceiver::SetNackStatus(bool enable,
113                                 int max_nack_reordering_threshold) {
114   if (!enable) {
115     // Reset the threshold back to the lower default threshold when NACK is
116     // disabled since we no longer will be receiving retransmissions.
117     max_nack_reordering_threshold = kDefaultMaxReorderingThreshold;
118   }
119   rtp_receive_statistics_->SetMaxReorderingThreshold(
120       max_nack_reordering_threshold);
121   rtp_receiver_->SetNACKStatus(enable ? kNackRtcp : kNackOff);
122 }
123 
SetRtxPayloadType(int payload_type)124 void ViEReceiver::SetRtxPayloadType(int payload_type) {
125   rtp_payload_registry_->SetRtxPayloadType(payload_type);
126 }
127 
SetRtxSsrc(uint32_t ssrc)128 void ViEReceiver::SetRtxSsrc(uint32_t ssrc) {
129   rtp_payload_registry_->SetRtxSsrc(ssrc);
130 }
131 
GetRtxSsrc(uint32_t * ssrc) const132 bool ViEReceiver::GetRtxSsrc(uint32_t* ssrc) const {
133   return rtp_payload_registry_->GetRtxSsrc(ssrc);
134 }
135 
IsFecEnabled() const136 bool ViEReceiver::IsFecEnabled() const {
137   return rtp_payload_registry_->ulpfec_payload_type() > -1;
138 }
139 
GetRemoteSsrc() const140 uint32_t ViEReceiver::GetRemoteSsrc() const {
141   return rtp_receiver_->SSRC();
142 }
143 
GetCsrcs(uint32_t * csrcs) const144 int ViEReceiver::GetCsrcs(uint32_t* csrcs) const {
145   return rtp_receiver_->CSRCs(csrcs);
146 }
147 
GetRID(char rid[256]) const148 void ViEReceiver::GetRID(char rid[256]) const {
149   rtp_receiver_->GetRID(rid);
150 }
151 
SetRtpRtcpModule(RtpRtcp * module)152 void ViEReceiver::SetRtpRtcpModule(RtpRtcp* module) {
153   rtp_rtcp_ = module;
154 }
155 
GetRtpReceiver() const156 RtpReceiver* ViEReceiver::GetRtpReceiver() const {
157   return rtp_receiver_.get();
158 }
159 
RegisterSimulcastRtpRtcpModules(const std::list<RtpRtcp * > & rtp_modules)160 void ViEReceiver::RegisterSimulcastRtpRtcpModules(
161     const std::list<RtpRtcp*>& rtp_modules) {
162   CriticalSectionScoped cs(receive_cs_.get());
163   rtp_rtcp_simulcast_.clear();
164 
165   if (!rtp_modules.empty()) {
166     rtp_rtcp_simulcast_.insert(rtp_rtcp_simulcast_.begin(),
167                                rtp_modules.begin(),
168                                rtp_modules.end());
169   }
170 }
171 
SetReceiveTimestampOffsetStatus(bool enable,int id)172 bool ViEReceiver::SetReceiveTimestampOffsetStatus(bool enable, int id) {
173   if (enable) {
174     return rtp_header_parser_->RegisterRtpHeaderExtension(
175         kRtpExtensionTransmissionTimeOffset, id);
176   } else {
177     return rtp_header_parser_->DeregisterRtpHeaderExtension(
178         kRtpExtensionTransmissionTimeOffset);
179   }
180 }
181 
SetReceiveAbsoluteSendTimeStatus(bool enable,int id)182 bool ViEReceiver::SetReceiveAbsoluteSendTimeStatus(bool enable, int id) {
183   if (enable) {
184     if (rtp_header_parser_->RegisterRtpHeaderExtension(
185         kRtpExtensionAbsoluteSendTime, id)) {
186       receiving_ast_enabled_ = true;
187       return true;
188     } else {
189       return false;
190     }
191   } else {
192     receiving_ast_enabled_ = false;
193     return rtp_header_parser_->DeregisterRtpHeaderExtension(
194         kRtpExtensionAbsoluteSendTime);
195   }
196 }
197 
SetReceiveVideoRotationStatus(bool enable,int id)198 bool ViEReceiver::SetReceiveVideoRotationStatus(bool enable, int id) {
199   if (enable) {
200     if (rtp_header_parser_->RegisterRtpHeaderExtension(
201             kRtpExtensionVideoRotation, id)) {
202       receiving_cvo_enabled_ = true;
203       return true;
204     } else {
205       return false;
206     }
207   } else {
208     receiving_cvo_enabled_ = false;
209     return rtp_header_parser_->DeregisterRtpHeaderExtension(
210         kRtpExtensionVideoRotation);
211   }
212 }
213 
SetReceiveRIDStatus(bool enable,int id)214 bool ViEReceiver::SetReceiveRIDStatus(bool enable, int id) {
215   if (enable) {
216     if (rtp_header_parser_->RegisterRtpHeaderExtension(
217             kRtpExtensionRtpStreamId, id)) {
218       receiving_rid_enabled_ = true;
219       return true;
220     } else {
221       return false;
222     }
223   } else {
224     receiving_rid_enabled_ = false;
225     return rtp_header_parser_->DeregisterRtpHeaderExtension(
226         kRtpExtensionRtpStreamId);
227   }
228 }
229 
ReceivedRTPPacket(const void * rtp_packet,size_t rtp_packet_length,const PacketTime & packet_time)230 int ViEReceiver::ReceivedRTPPacket(const void* rtp_packet,
231                                    size_t rtp_packet_length,
232                                    const PacketTime& packet_time) {
233   return InsertRTPPacket(static_cast<const uint8_t*>(rtp_packet),
234                          rtp_packet_length, packet_time);
235 }
236 
ReceivedRTCPPacket(const void * rtcp_packet,size_t rtcp_packet_length)237 int ViEReceiver::ReceivedRTCPPacket(const void* rtcp_packet,
238                                     size_t rtcp_packet_length) {
239   return InsertRTCPPacket(static_cast<const uint8_t*>(rtcp_packet),
240                           rtcp_packet_length);
241 }
242 
OnReceivedPayloadData(const uint8_t * payload_data,const size_t payload_size,const WebRtcRTPHeader * rtp_header)243 int32_t ViEReceiver::OnReceivedPayloadData(const uint8_t* payload_data,
244                                            const size_t payload_size,
245                                            const WebRtcRTPHeader* rtp_header) {
246   WebRtcRTPHeader rtp_header_with_ntp = *rtp_header;
247   rtp_header_with_ntp.ntp_time_ms =
248       ntp_estimator_->Estimate(rtp_header->header.timestamp);
249   if (vcm_->IncomingPacket(payload_data,
250                            payload_size,
251                            rtp_header_with_ntp) != 0) {
252     // Check this...
253     return -1;
254   }
255   return 0;
256 }
257 
OnRecoveredPacket(const uint8_t * rtp_packet,size_t rtp_packet_length)258 bool ViEReceiver::OnRecoveredPacket(const uint8_t* rtp_packet,
259                                     size_t rtp_packet_length) {
260   RTPHeader header;
261   if (!rtp_header_parser_->Parse(rtp_packet, rtp_packet_length, &header)) {
262     return false;
263   }
264   header.payload_type_frequency = kVideoPayloadTypeFrequency;
265   bool in_order = IsPacketInOrder(header);
266   return ReceivePacket(rtp_packet, rtp_packet_length, header, in_order);
267 }
268 
ReceivedBWEPacket(int64_t arrival_time_ms,size_t payload_size,const RTPHeader & header)269 void ViEReceiver::ReceivedBWEPacket(
270     int64_t arrival_time_ms, size_t payload_size, const RTPHeader& header) {
271   // Only forward if the incoming packet *and* the channel are both configured
272   // to receive absolute sender time. RTP time stamps may have different rates
273   // for audio and video and shouldn't be mixed.
274   if (header.extension.hasAbsoluteSendTime && receiving_ast_enabled_) {
275     remote_bitrate_estimator_->IncomingPacket(arrival_time_ms, payload_size,
276                                               header);
277   }
278 }
279 
InsertRTPPacket(const uint8_t * rtp_packet,size_t rtp_packet_length,const PacketTime & packet_time)280 int ViEReceiver::InsertRTPPacket(const uint8_t* rtp_packet,
281                                  size_t rtp_packet_length,
282                                  const PacketTime& packet_time) {
283   {
284     CriticalSectionScoped cs(receive_cs_.get());
285     if (!receiving_) {
286       return -1;
287     }
288     if (rtp_dump_) {
289       rtp_dump_->DumpPacket(rtp_packet, rtp_packet_length);
290     }
291   }
292 
293   RTPHeader header;
294   if (!rtp_header_parser_->Parse(rtp_packet, rtp_packet_length,
295                                  &header)) {
296     return -1;
297   }
298   size_t payload_length = rtp_packet_length - header.headerLength;
299   int64_t arrival_time_ms;
300   int64_t now_ms = clock_->TimeInMilliseconds();
301   if (packet_time.timestamp != -1)
302     arrival_time_ms = (packet_time.timestamp + 500) / 1000;
303   else
304     arrival_time_ms = now_ms;
305 
306   {
307     // Periodically log the RTP header of incoming packets.
308     CriticalSectionScoped cs(receive_cs_.get());
309     if (now_ms - last_packet_log_ms_ > kPacketLogIntervalMs) {
310       std::stringstream ss;
311       ss << "Packet received on SSRC: " << header.ssrc << " with payload type: "
312          << static_cast<int>(header.payloadType) << ", timestamp: "
313          << header.timestamp << ", sequence number: " << header.sequenceNumber
314          << ", arrival time: " << arrival_time_ms;
315       if (header.extension.hasTransmissionTimeOffset)
316         ss << ", toffset: " << header.extension.transmissionTimeOffset;
317       if (header.extension.hasAbsoluteSendTime)
318         ss << ", abs send time: " << header.extension.absoluteSendTime;
319       if (header.extension.hasRID)
320         ss << ", rid: " << header.extension.rid;
321       LOG(LS_INFO) << ss.str();
322       last_packet_log_ms_ = now_ms;
323     }
324   }
325 
326   remote_bitrate_estimator_->IncomingPacket(arrival_time_ms,
327                                             payload_length, header);
328   header.payload_type_frequency = kVideoPayloadTypeFrequency;
329 
330   bool in_order = IsPacketInOrder(header);
331   rtp_payload_registry_->SetIncomingPayloadType(header);
332   int ret = ReceivePacket(rtp_packet, rtp_packet_length, header, in_order)
333       ? 0
334       : -1;
335   // Update receive statistics after ReceivePacket.
336   // Receive statistics will be reset if the payload type changes (make sure
337   // that the first packet is included in the stats).
338   rtp_receive_statistics_->IncomingPacket(
339       header, rtp_packet_length, IsPacketRetransmitted(header, in_order));
340   return ret;
341 }
342 
ReceivePacket(const uint8_t * packet,size_t packet_length,const RTPHeader & header,bool in_order)343 bool ViEReceiver::ReceivePacket(const uint8_t* packet,
344                                 size_t packet_length,
345                                 const RTPHeader& header,
346                                 bool in_order) {
347   if (rtp_payload_registry_->IsEncapsulated(header)) {
348     return ParseAndHandleEncapsulatingHeader(packet, packet_length, header);
349   }
350   const uint8_t* payload = packet + header.headerLength;
351   assert(packet_length >= header.headerLength);
352   size_t payload_length = packet_length - header.headerLength;
353   PayloadUnion payload_specific;
354   if (!rtp_payload_registry_->GetPayloadSpecifics(header.payloadType,
355                                                   &payload_specific)) {
356     return false;
357   }
358   return rtp_receiver_->IncomingRtpPacket(header, payload, payload_length,
359                                           payload_specific, in_order);
360 }
361 
ParseAndHandleEncapsulatingHeader(const uint8_t * packet,size_t packet_length,const RTPHeader & header)362 bool ViEReceiver::ParseAndHandleEncapsulatingHeader(const uint8_t* packet,
363                                                     size_t packet_length,
364                                                     const RTPHeader& header) {
365   if (rtp_payload_registry_->IsRed(header)) {
366     int8_t ulpfec_pt = rtp_payload_registry_->ulpfec_payload_type();
367     if (packet[header.headerLength] == ulpfec_pt) {
368       rtp_receive_statistics_->FecPacketReceived(header, packet_length);
369       // Notify vcm about received FEC packets to avoid NACKing these packets.
370       NotifyReceiverOfFecPacket(header);
371     }
372     if (fec_receiver_->AddReceivedRedPacket(
373             header, packet, packet_length, ulpfec_pt) != 0) {
374       return false;
375     }
376     return fec_receiver_->ProcessReceivedFec() == 0;
377   } else if (rtp_payload_registry_->IsRtx(header)) {
378     if (header.headerLength + header.paddingLength == packet_length) {
379       // This is an empty packet and should be silently dropped before trying to
380       // parse the RTX header.
381       return true;
382     }
383     // Remove the RTX header and parse the original RTP header.
384     if (packet_length < header.headerLength)
385       return false;
386     if (packet_length > sizeof(restored_packet_))
387       return false;
388     CriticalSectionScoped cs(receive_cs_.get());
389     if (restored_packet_in_use_) {
390       LOG(LS_WARNING) << "Multiple RTX headers detected, dropping packet.";
391       return false;
392     }
393     uint8_t* restored_packet_ptr = restored_packet_;
394     if (!rtp_payload_registry_->RestoreOriginalPacket(
395         &restored_packet_ptr, packet, &packet_length, rtp_receiver_->SSRC(),
396         header)) {
397       LOG(LS_WARNING) << "Incoming RTX packet: Invalid RTP header";
398       return false;
399     }
400     restored_packet_in_use_ = true;
401     bool ret = OnRecoveredPacket(restored_packet_ptr, packet_length);
402     restored_packet_in_use_ = false;
403     return ret;
404   }
405   return false;
406 }
407 
NotifyReceiverOfFecPacket(const RTPHeader & header)408 void ViEReceiver::NotifyReceiverOfFecPacket(const RTPHeader& header) {
409   int8_t last_media_payload_type =
410       rtp_payload_registry_->last_received_media_payload_type();
411   if (last_media_payload_type < 0) {
412     LOG(LS_WARNING) << "Failed to get last media payload type.";
413     return;
414   }
415   // Fake an empty media packet.
416   WebRtcRTPHeader rtp_header = {};
417   rtp_header.header = header;
418   rtp_header.header.payloadType = last_media_payload_type;
419   rtp_header.header.paddingLength = 0;
420   PayloadUnion payload_specific;
421   if (!rtp_payload_registry_->GetPayloadSpecifics(last_media_payload_type,
422                                                   &payload_specific)) {
423     LOG(LS_WARNING) << "Failed to get payload specifics.";
424     return;
425   }
426   rtp_header.type.Video.codec = payload_specific.Video.videoCodecType;
427   rtp_header.type.Video.rotation = kVideoRotation_0;
428   if (header.extension.hasVideoRotation) {
429     rtp_header.type.Video.rotation =
430         ConvertCVOByteToVideoRotation(header.extension.videoRotation);
431   }
432   OnReceivedPayloadData(NULL, 0, &rtp_header);
433 }
434 
InsertRTCPPacket(const uint8_t * rtcp_packet,size_t rtcp_packet_length)435 int ViEReceiver::InsertRTCPPacket(const uint8_t* rtcp_packet,
436                                   size_t rtcp_packet_length) {
437   {
438     CriticalSectionScoped cs(receive_cs_.get());
439     if (!receiving_rtcp_) {
440       return -1;
441     }
442 
443     if (rtp_dump_) {
444       rtp_dump_->DumpPacket(rtcp_packet, rtcp_packet_length);
445     }
446 
447     std::list<RtpRtcp*>::iterator it = rtp_rtcp_simulcast_.begin();
448     while (it != rtp_rtcp_simulcast_.end()) {
449       RtpRtcp* rtp_rtcp = *it++;
450       rtp_rtcp->IncomingRtcpPacket(rtcp_packet, rtcp_packet_length);
451     }
452   }
453   assert(rtp_rtcp_);  // Should be set by owner at construction time.
454   int ret = rtp_rtcp_->IncomingRtcpPacket(rtcp_packet, rtcp_packet_length);
455   if (ret != 0) {
456     return ret;
457   }
458 
459   int64_t rtt = 0;
460   rtp_rtcp_->RTT(rtp_receiver_->SSRC(), &rtt, NULL, NULL, NULL);
461   if (rtt == 0) {
462     // Waiting for valid rtt.
463     return 0;
464   }
465   uint32_t ntp_secs = 0;
466   uint32_t ntp_frac = 0;
467   uint32_t rtp_timestamp = 0;
468   if (0 != rtp_rtcp_->RemoteNTP(&ntp_secs, &ntp_frac, NULL, NULL,
469                                 &rtp_timestamp)) {
470     // Waiting for RTCP.
471     return 0;
472   }
473   ntp_estimator_->UpdateRtcpTimestamp(rtt, ntp_secs, ntp_frac, rtp_timestamp);
474 
475   return 0;
476 }
477 
StartReceive()478 void ViEReceiver::StartReceive() {
479   CriticalSectionScoped cs(receive_cs_.get());
480   receiving_ = true;
481 }
482 
StopReceive()483 void ViEReceiver::StopReceive() {
484   CriticalSectionScoped cs(receive_cs_.get());
485   receiving_ = false;
486 }
487 
StartRTCPReceive()488 void ViEReceiver::StartRTCPReceive() {
489   CriticalSectionScoped cs(receive_cs_.get());
490   receiving_rtcp_ = true;
491 }
492 
StopRTCPReceive()493 void ViEReceiver::StopRTCPReceive() {
494   CriticalSectionScoped cs(receive_cs_.get());
495   receiving_rtcp_ = false;
496 }
497 
StartRTPDump(const char file_nameUTF8[1024])498 int ViEReceiver::StartRTPDump(const char file_nameUTF8[1024]) {
499   CriticalSectionScoped cs(receive_cs_.get());
500   if (rtp_dump_) {
501     // Restart it if it already exists and is started
502     rtp_dump_->Stop();
503   } else {
504     rtp_dump_ = RtpDump::CreateRtpDump();
505     if (rtp_dump_ == NULL) {
506       return -1;
507     }
508   }
509   if (rtp_dump_->Start(file_nameUTF8) != 0) {
510     RtpDump::DestroyRtpDump(rtp_dump_);
511     rtp_dump_ = NULL;
512     return -1;
513   }
514   return 0;
515 }
516 
StopRTPDump()517 int ViEReceiver::StopRTPDump() {
518   CriticalSectionScoped cs(receive_cs_.get());
519   if (rtp_dump_) {
520     if (rtp_dump_->IsActive()) {
521       rtp_dump_->Stop();
522     }
523     RtpDump::DestroyRtpDump(rtp_dump_);
524     rtp_dump_ = NULL;
525   } else {
526     return -1;
527   }
528   return 0;
529 }
530 
GetReceiveStatistics() const531 ReceiveStatistics* ViEReceiver::GetReceiveStatistics() const {
532   return rtp_receive_statistics_.get();
533 }
534 
IsPacketInOrder(const RTPHeader & header) const535 bool ViEReceiver::IsPacketInOrder(const RTPHeader& header) const {
536   StreamStatistician* statistician =
537       rtp_receive_statistics_->GetStatistician(header.ssrc);
538   if (!statistician)
539     return false;
540   return statistician->IsPacketInOrder(header.sequenceNumber);
541 }
542 
IsPacketRetransmitted(const RTPHeader & header,bool in_order) const543 bool ViEReceiver::IsPacketRetransmitted(const RTPHeader& header,
544                                         bool in_order) const {
545   // Retransmissions are handled separately if RTX is enabled.
546   if (rtp_payload_registry_->RtxEnabled())
547     return false;
548   StreamStatistician* statistician =
549       rtp_receive_statistics_->GetStatistician(header.ssrc);
550   if (!statistician)
551     return false;
552   // Check if this is a retransmission.
553   int64_t min_rtt = 0;
554   rtp_rtcp_->RTT(rtp_receiver_->SSRC(), NULL, NULL, &min_rtt, NULL);
555   return !in_order &&
556       statistician->IsRetransmitOfOldPacket(header, min_rtt);
557 }
558 }  // namespace webrtc
559