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