1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker *
4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker */
10*d9f75844SAndroid Build Coastguard Worker
11*d9f75844SAndroid Build Coastguard Worker #include "audio/voip/audio_ingress.h"
12*d9f75844SAndroid Build Coastguard Worker
13*d9f75844SAndroid Build Coastguard Worker #include <algorithm>
14*d9f75844SAndroid Build Coastguard Worker #include <utility>
15*d9f75844SAndroid Build Coastguard Worker #include <vector>
16*d9f75844SAndroid Build Coastguard Worker
17*d9f75844SAndroid Build Coastguard Worker #include "api/audio_codecs/audio_format.h"
18*d9f75844SAndroid Build Coastguard Worker #include "audio/utility/audio_frame_operations.h"
19*d9f75844SAndroid Build Coastguard Worker #include "modules/audio_coding/include/audio_coding_module.h"
20*d9f75844SAndroid Build Coastguard Worker #include "modules/rtp_rtcp/source/byte_io.h"
21*d9f75844SAndroid Build Coastguard Worker #include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
22*d9f75844SAndroid Build Coastguard Worker #include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h"
23*d9f75844SAndroid Build Coastguard Worker #include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h"
24*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/logging.h"
25*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/numerics/safe_minmax.h"
26*d9f75844SAndroid Build Coastguard Worker
27*d9f75844SAndroid Build Coastguard Worker namespace webrtc {
28*d9f75844SAndroid Build Coastguard Worker
29*d9f75844SAndroid Build Coastguard Worker namespace {
30*d9f75844SAndroid Build Coastguard Worker
CreateAcmConfig(rtc::scoped_refptr<AudioDecoderFactory> decoder_factory)31*d9f75844SAndroid Build Coastguard Worker AudioCodingModule::Config CreateAcmConfig(
32*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<AudioDecoderFactory> decoder_factory) {
33*d9f75844SAndroid Build Coastguard Worker AudioCodingModule::Config acm_config;
34*d9f75844SAndroid Build Coastguard Worker acm_config.neteq_config.enable_muted_state = true;
35*d9f75844SAndroid Build Coastguard Worker acm_config.decoder_factory = decoder_factory;
36*d9f75844SAndroid Build Coastguard Worker return acm_config;
37*d9f75844SAndroid Build Coastguard Worker }
38*d9f75844SAndroid Build Coastguard Worker
39*d9f75844SAndroid Build Coastguard Worker } // namespace
40*d9f75844SAndroid Build Coastguard Worker
AudioIngress(RtpRtcpInterface * rtp_rtcp,Clock * clock,ReceiveStatistics * receive_statistics,rtc::scoped_refptr<AudioDecoderFactory> decoder_factory)41*d9f75844SAndroid Build Coastguard Worker AudioIngress::AudioIngress(
42*d9f75844SAndroid Build Coastguard Worker RtpRtcpInterface* rtp_rtcp,
43*d9f75844SAndroid Build Coastguard Worker Clock* clock,
44*d9f75844SAndroid Build Coastguard Worker ReceiveStatistics* receive_statistics,
45*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<AudioDecoderFactory> decoder_factory)
46*d9f75844SAndroid Build Coastguard Worker : playing_(false),
47*d9f75844SAndroid Build Coastguard Worker remote_ssrc_(0),
48*d9f75844SAndroid Build Coastguard Worker first_rtp_timestamp_(-1),
49*d9f75844SAndroid Build Coastguard Worker rtp_receive_statistics_(receive_statistics),
50*d9f75844SAndroid Build Coastguard Worker rtp_rtcp_(rtp_rtcp),
51*d9f75844SAndroid Build Coastguard Worker acm_receiver_(CreateAcmConfig(decoder_factory)),
52*d9f75844SAndroid Build Coastguard Worker ntp_estimator_(clock) {}
53*d9f75844SAndroid Build Coastguard Worker
54*d9f75844SAndroid Build Coastguard Worker AudioIngress::~AudioIngress() = default;
55*d9f75844SAndroid Build Coastguard Worker
GetAudioFrameWithInfo(int sampling_rate,AudioFrame * audio_frame)56*d9f75844SAndroid Build Coastguard Worker AudioMixer::Source::AudioFrameInfo AudioIngress::GetAudioFrameWithInfo(
57*d9f75844SAndroid Build Coastguard Worker int sampling_rate,
58*d9f75844SAndroid Build Coastguard Worker AudioFrame* audio_frame) {
59*d9f75844SAndroid Build Coastguard Worker audio_frame->sample_rate_hz_ = sampling_rate;
60*d9f75844SAndroid Build Coastguard Worker
61*d9f75844SAndroid Build Coastguard Worker // Get 10ms raw PCM data from the ACM.
62*d9f75844SAndroid Build Coastguard Worker bool muted = false;
63*d9f75844SAndroid Build Coastguard Worker if (acm_receiver_.GetAudio(sampling_rate, audio_frame, &muted) == -1) {
64*d9f75844SAndroid Build Coastguard Worker RTC_DLOG(LS_ERROR) << "GetAudio() failed!";
65*d9f75844SAndroid Build Coastguard Worker // In all likelihood, the audio in this frame is garbage. We return an
66*d9f75844SAndroid Build Coastguard Worker // error so that the audio mixer module doesn't add it to the mix. As
67*d9f75844SAndroid Build Coastguard Worker // a result, it won't be played out and the actions skipped here are
68*d9f75844SAndroid Build Coastguard Worker // irrelevant.
69*d9f75844SAndroid Build Coastguard Worker return AudioMixer::Source::AudioFrameInfo::kError;
70*d9f75844SAndroid Build Coastguard Worker }
71*d9f75844SAndroid Build Coastguard Worker
72*d9f75844SAndroid Build Coastguard Worker if (muted) {
73*d9f75844SAndroid Build Coastguard Worker AudioFrameOperations::Mute(audio_frame);
74*d9f75844SAndroid Build Coastguard Worker }
75*d9f75844SAndroid Build Coastguard Worker
76*d9f75844SAndroid Build Coastguard Worker // Measure audio level.
77*d9f75844SAndroid Build Coastguard Worker constexpr double kAudioSampleDurationSeconds = 0.01;
78*d9f75844SAndroid Build Coastguard Worker output_audio_level_.ComputeLevel(*audio_frame, kAudioSampleDurationSeconds);
79*d9f75844SAndroid Build Coastguard Worker
80*d9f75844SAndroid Build Coastguard Worker // If caller invoked StopPlay(), then mute the frame.
81*d9f75844SAndroid Build Coastguard Worker if (!playing_) {
82*d9f75844SAndroid Build Coastguard Worker AudioFrameOperations::Mute(audio_frame);
83*d9f75844SAndroid Build Coastguard Worker muted = true;
84*d9f75844SAndroid Build Coastguard Worker }
85*d9f75844SAndroid Build Coastguard Worker
86*d9f75844SAndroid Build Coastguard Worker // Set first rtp timestamp with first audio frame with valid timestamp.
87*d9f75844SAndroid Build Coastguard Worker if (first_rtp_timestamp_ < 0 && audio_frame->timestamp_ != 0) {
88*d9f75844SAndroid Build Coastguard Worker first_rtp_timestamp_ = audio_frame->timestamp_;
89*d9f75844SAndroid Build Coastguard Worker }
90*d9f75844SAndroid Build Coastguard Worker
91*d9f75844SAndroid Build Coastguard Worker if (first_rtp_timestamp_ >= 0) {
92*d9f75844SAndroid Build Coastguard Worker // Compute elapsed and NTP times.
93*d9f75844SAndroid Build Coastguard Worker int64_t unwrap_timestamp;
94*d9f75844SAndroid Build Coastguard Worker {
95*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&lock_);
96*d9f75844SAndroid Build Coastguard Worker unwrap_timestamp =
97*d9f75844SAndroid Build Coastguard Worker timestamp_wrap_handler_.Unwrap(audio_frame->timestamp_);
98*d9f75844SAndroid Build Coastguard Worker audio_frame->ntp_time_ms_ =
99*d9f75844SAndroid Build Coastguard Worker ntp_estimator_.Estimate(audio_frame->timestamp_);
100*d9f75844SAndroid Build Coastguard Worker }
101*d9f75844SAndroid Build Coastguard Worker // For clock rate, default to the playout sampling rate if we haven't
102*d9f75844SAndroid Build Coastguard Worker // received any packets yet.
103*d9f75844SAndroid Build Coastguard Worker absl::optional<std::pair<int, SdpAudioFormat>> decoder =
104*d9f75844SAndroid Build Coastguard Worker acm_receiver_.LastDecoder();
105*d9f75844SAndroid Build Coastguard Worker int clock_rate = decoder ? decoder->second.clockrate_hz
106*d9f75844SAndroid Build Coastguard Worker : acm_receiver_.last_output_sample_rate_hz();
107*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(clock_rate, 0);
108*d9f75844SAndroid Build Coastguard Worker audio_frame->elapsed_time_ms_ =
109*d9f75844SAndroid Build Coastguard Worker (unwrap_timestamp - first_rtp_timestamp_) / (clock_rate / 1000);
110*d9f75844SAndroid Build Coastguard Worker }
111*d9f75844SAndroid Build Coastguard Worker
112*d9f75844SAndroid Build Coastguard Worker return muted ? AudioMixer::Source::AudioFrameInfo::kMuted
113*d9f75844SAndroid Build Coastguard Worker : AudioMixer::Source::AudioFrameInfo::kNormal;
114*d9f75844SAndroid Build Coastguard Worker }
115*d9f75844SAndroid Build Coastguard Worker
StartPlay()116*d9f75844SAndroid Build Coastguard Worker bool AudioIngress::StartPlay() {
117*d9f75844SAndroid Build Coastguard Worker {
118*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&lock_);
119*d9f75844SAndroid Build Coastguard Worker if (receive_codec_info_.empty()) {
120*d9f75844SAndroid Build Coastguard Worker RTC_DLOG(LS_WARNING) << "Receive codecs have not been set yet";
121*d9f75844SAndroid Build Coastguard Worker return false;
122*d9f75844SAndroid Build Coastguard Worker }
123*d9f75844SAndroid Build Coastguard Worker }
124*d9f75844SAndroid Build Coastguard Worker playing_ = true;
125*d9f75844SAndroid Build Coastguard Worker return true;
126*d9f75844SAndroid Build Coastguard Worker }
127*d9f75844SAndroid Build Coastguard Worker
SetReceiveCodecs(const std::map<int,SdpAudioFormat> & codecs)128*d9f75844SAndroid Build Coastguard Worker void AudioIngress::SetReceiveCodecs(
129*d9f75844SAndroid Build Coastguard Worker const std::map<int, SdpAudioFormat>& codecs) {
130*d9f75844SAndroid Build Coastguard Worker {
131*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&lock_);
132*d9f75844SAndroid Build Coastguard Worker for (const auto& kv : codecs) {
133*d9f75844SAndroid Build Coastguard Worker receive_codec_info_[kv.first] = kv.second.clockrate_hz;
134*d9f75844SAndroid Build Coastguard Worker }
135*d9f75844SAndroid Build Coastguard Worker }
136*d9f75844SAndroid Build Coastguard Worker acm_receiver_.SetCodecs(codecs);
137*d9f75844SAndroid Build Coastguard Worker }
138*d9f75844SAndroid Build Coastguard Worker
ReceivedRTPPacket(rtc::ArrayView<const uint8_t> rtp_packet)139*d9f75844SAndroid Build Coastguard Worker void AudioIngress::ReceivedRTPPacket(rtc::ArrayView<const uint8_t> rtp_packet) {
140*d9f75844SAndroid Build Coastguard Worker RtpPacketReceived rtp_packet_received;
141*d9f75844SAndroid Build Coastguard Worker rtp_packet_received.Parse(rtp_packet.data(), rtp_packet.size());
142*d9f75844SAndroid Build Coastguard Worker
143*d9f75844SAndroid Build Coastguard Worker // Set payload type's sampling rate before we feed it into ReceiveStatistics.
144*d9f75844SAndroid Build Coastguard Worker {
145*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&lock_);
146*d9f75844SAndroid Build Coastguard Worker const auto& it =
147*d9f75844SAndroid Build Coastguard Worker receive_codec_info_.find(rtp_packet_received.PayloadType());
148*d9f75844SAndroid Build Coastguard Worker // If sampling rate info is not available in our received codec set, it
149*d9f75844SAndroid Build Coastguard Worker // would mean that remote media endpoint is sending incorrect payload id
150*d9f75844SAndroid Build Coastguard Worker // which can't be processed correctly especially on payload type id in
151*d9f75844SAndroid Build Coastguard Worker // dynamic range.
152*d9f75844SAndroid Build Coastguard Worker if (it == receive_codec_info_.end()) {
153*d9f75844SAndroid Build Coastguard Worker RTC_DLOG(LS_WARNING) << "Unexpected payload id received: "
154*d9f75844SAndroid Build Coastguard Worker << rtp_packet_received.PayloadType();
155*d9f75844SAndroid Build Coastguard Worker return;
156*d9f75844SAndroid Build Coastguard Worker }
157*d9f75844SAndroid Build Coastguard Worker rtp_packet_received.set_payload_type_frequency(it->second);
158*d9f75844SAndroid Build Coastguard Worker }
159*d9f75844SAndroid Build Coastguard Worker
160*d9f75844SAndroid Build Coastguard Worker // Track current remote SSRC.
161*d9f75844SAndroid Build Coastguard Worker if (rtp_packet_received.Ssrc() != remote_ssrc_) {
162*d9f75844SAndroid Build Coastguard Worker rtp_rtcp_->SetRemoteSSRC(rtp_packet_received.Ssrc());
163*d9f75844SAndroid Build Coastguard Worker remote_ssrc_.store(rtp_packet_received.Ssrc());
164*d9f75844SAndroid Build Coastguard Worker }
165*d9f75844SAndroid Build Coastguard Worker
166*d9f75844SAndroid Build Coastguard Worker rtp_receive_statistics_->OnRtpPacket(rtp_packet_received);
167*d9f75844SAndroid Build Coastguard Worker
168*d9f75844SAndroid Build Coastguard Worker RTPHeader header;
169*d9f75844SAndroid Build Coastguard Worker rtp_packet_received.GetHeader(&header);
170*d9f75844SAndroid Build Coastguard Worker
171*d9f75844SAndroid Build Coastguard Worker size_t packet_length = rtp_packet_received.size();
172*d9f75844SAndroid Build Coastguard Worker if (packet_length < header.headerLength ||
173*d9f75844SAndroid Build Coastguard Worker (packet_length - header.headerLength) < header.paddingLength) {
174*d9f75844SAndroid Build Coastguard Worker RTC_DLOG(LS_ERROR) << "Packet length(" << packet_length << ") header("
175*d9f75844SAndroid Build Coastguard Worker << header.headerLength << ") padding("
176*d9f75844SAndroid Build Coastguard Worker << header.paddingLength << ")";
177*d9f75844SAndroid Build Coastguard Worker return;
178*d9f75844SAndroid Build Coastguard Worker }
179*d9f75844SAndroid Build Coastguard Worker
180*d9f75844SAndroid Build Coastguard Worker const uint8_t* payload = rtp_packet_received.data() + header.headerLength;
181*d9f75844SAndroid Build Coastguard Worker size_t payload_length = packet_length - header.headerLength;
182*d9f75844SAndroid Build Coastguard Worker size_t payload_data_length = payload_length - header.paddingLength;
183*d9f75844SAndroid Build Coastguard Worker auto data_view = rtc::ArrayView<const uint8_t>(payload, payload_data_length);
184*d9f75844SAndroid Build Coastguard Worker
185*d9f75844SAndroid Build Coastguard Worker // Push the incoming payload (parsed and ready for decoding) into the ACM.
186*d9f75844SAndroid Build Coastguard Worker if (acm_receiver_.InsertPacket(header, data_view) != 0) {
187*d9f75844SAndroid Build Coastguard Worker RTC_DLOG(LS_ERROR) << "AudioIngress::ReceivedRTPPacket() unable to "
188*d9f75844SAndroid Build Coastguard Worker "push data to the ACM";
189*d9f75844SAndroid Build Coastguard Worker }
190*d9f75844SAndroid Build Coastguard Worker }
191*d9f75844SAndroid Build Coastguard Worker
ReceivedRTCPPacket(rtc::ArrayView<const uint8_t> rtcp_packet)192*d9f75844SAndroid Build Coastguard Worker void AudioIngress::ReceivedRTCPPacket(
193*d9f75844SAndroid Build Coastguard Worker rtc::ArrayView<const uint8_t> rtcp_packet) {
194*d9f75844SAndroid Build Coastguard Worker rtcp::CommonHeader rtcp_header;
195*d9f75844SAndroid Build Coastguard Worker if (rtcp_header.Parse(rtcp_packet.data(), rtcp_packet.size()) &&
196*d9f75844SAndroid Build Coastguard Worker (rtcp_header.type() == rtcp::SenderReport::kPacketType ||
197*d9f75844SAndroid Build Coastguard Worker rtcp_header.type() == rtcp::ReceiverReport::kPacketType)) {
198*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GE(rtcp_packet.size(), 8);
199*d9f75844SAndroid Build Coastguard Worker
200*d9f75844SAndroid Build Coastguard Worker uint32_t sender_ssrc =
201*d9f75844SAndroid Build Coastguard Worker ByteReader<uint32_t>::ReadBigEndian(rtcp_packet.data() + 4);
202*d9f75844SAndroid Build Coastguard Worker
203*d9f75844SAndroid Build Coastguard Worker // If we don't have remote ssrc at this point, it's likely that remote
204*d9f75844SAndroid Build Coastguard Worker // endpoint is receive-only or it could have restarted the media.
205*d9f75844SAndroid Build Coastguard Worker if (sender_ssrc != remote_ssrc_) {
206*d9f75844SAndroid Build Coastguard Worker rtp_rtcp_->SetRemoteSSRC(sender_ssrc);
207*d9f75844SAndroid Build Coastguard Worker remote_ssrc_.store(sender_ssrc);
208*d9f75844SAndroid Build Coastguard Worker }
209*d9f75844SAndroid Build Coastguard Worker }
210*d9f75844SAndroid Build Coastguard Worker
211*d9f75844SAndroid Build Coastguard Worker // Deliver RTCP packet to RTP/RTCP module for parsing and processing.
212*d9f75844SAndroid Build Coastguard Worker rtp_rtcp_->IncomingRtcpPacket(rtcp_packet.data(), rtcp_packet.size());
213*d9f75844SAndroid Build Coastguard Worker
214*d9f75844SAndroid Build Coastguard Worker int64_t rtt = 0;
215*d9f75844SAndroid Build Coastguard Worker if (rtp_rtcp_->RTT(remote_ssrc_, &rtt, nullptr, nullptr, nullptr) != 0) {
216*d9f75844SAndroid Build Coastguard Worker // Waiting for valid RTT.
217*d9f75844SAndroid Build Coastguard Worker return;
218*d9f75844SAndroid Build Coastguard Worker }
219*d9f75844SAndroid Build Coastguard Worker
220*d9f75844SAndroid Build Coastguard Worker uint32_t ntp_secs = 0, ntp_frac = 0, rtp_timestamp = 0;
221*d9f75844SAndroid Build Coastguard Worker if (rtp_rtcp_->RemoteNTP(&ntp_secs, &ntp_frac, nullptr, nullptr,
222*d9f75844SAndroid Build Coastguard Worker &rtp_timestamp) != 0) {
223*d9f75844SAndroid Build Coastguard Worker // Waiting for RTCP.
224*d9f75844SAndroid Build Coastguard Worker return;
225*d9f75844SAndroid Build Coastguard Worker }
226*d9f75844SAndroid Build Coastguard Worker
227*d9f75844SAndroid Build Coastguard Worker {
228*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&lock_);
229*d9f75844SAndroid Build Coastguard Worker ntp_estimator_.UpdateRtcpTimestamp(
230*d9f75844SAndroid Build Coastguard Worker TimeDelta::Millis(rtt), NtpTime(ntp_secs, ntp_frac), rtp_timestamp);
231*d9f75844SAndroid Build Coastguard Worker }
232*d9f75844SAndroid Build Coastguard Worker }
233*d9f75844SAndroid Build Coastguard Worker
GetChannelStatistics()234*d9f75844SAndroid Build Coastguard Worker ChannelStatistics AudioIngress::GetChannelStatistics() {
235*d9f75844SAndroid Build Coastguard Worker ChannelStatistics channel_stats;
236*d9f75844SAndroid Build Coastguard Worker
237*d9f75844SAndroid Build Coastguard Worker // Get clockrate for current decoder ahead of jitter calculation.
238*d9f75844SAndroid Build Coastguard Worker uint32_t clockrate_hz = 0;
239*d9f75844SAndroid Build Coastguard Worker absl::optional<std::pair<int, SdpAudioFormat>> decoder =
240*d9f75844SAndroid Build Coastguard Worker acm_receiver_.LastDecoder();
241*d9f75844SAndroid Build Coastguard Worker if (decoder) {
242*d9f75844SAndroid Build Coastguard Worker clockrate_hz = decoder->second.clockrate_hz;
243*d9f75844SAndroid Build Coastguard Worker }
244*d9f75844SAndroid Build Coastguard Worker
245*d9f75844SAndroid Build Coastguard Worker StreamStatistician* statistician =
246*d9f75844SAndroid Build Coastguard Worker rtp_receive_statistics_->GetStatistician(remote_ssrc_);
247*d9f75844SAndroid Build Coastguard Worker if (statistician) {
248*d9f75844SAndroid Build Coastguard Worker RtpReceiveStats stats = statistician->GetStats();
249*d9f75844SAndroid Build Coastguard Worker channel_stats.packets_lost = stats.packets_lost;
250*d9f75844SAndroid Build Coastguard Worker channel_stats.packets_received = stats.packet_counter.packets;
251*d9f75844SAndroid Build Coastguard Worker channel_stats.bytes_received = stats.packet_counter.payload_bytes;
252*d9f75844SAndroid Build Coastguard Worker channel_stats.remote_ssrc = remote_ssrc_;
253*d9f75844SAndroid Build Coastguard Worker if (clockrate_hz > 0) {
254*d9f75844SAndroid Build Coastguard Worker channel_stats.jitter = static_cast<double>(stats.jitter) / clockrate_hz;
255*d9f75844SAndroid Build Coastguard Worker }
256*d9f75844SAndroid Build Coastguard Worker }
257*d9f75844SAndroid Build Coastguard Worker
258*d9f75844SAndroid Build Coastguard Worker // Get RTCP report using remote SSRC.
259*d9f75844SAndroid Build Coastguard Worker const std::vector<ReportBlockData>& report_data =
260*d9f75844SAndroid Build Coastguard Worker rtp_rtcp_->GetLatestReportBlockData();
261*d9f75844SAndroid Build Coastguard Worker for (const ReportBlockData& block_data : report_data) {
262*d9f75844SAndroid Build Coastguard Worker const RTCPReportBlock& rtcp_report = block_data.report_block();
263*d9f75844SAndroid Build Coastguard Worker if (rtp_rtcp_->SSRC() != rtcp_report.source_ssrc ||
264*d9f75844SAndroid Build Coastguard Worker remote_ssrc_ != rtcp_report.sender_ssrc) {
265*d9f75844SAndroid Build Coastguard Worker continue;
266*d9f75844SAndroid Build Coastguard Worker }
267*d9f75844SAndroid Build Coastguard Worker RemoteRtcpStatistics remote_stat;
268*d9f75844SAndroid Build Coastguard Worker remote_stat.packets_lost = rtcp_report.packets_lost;
269*d9f75844SAndroid Build Coastguard Worker remote_stat.fraction_lost =
270*d9f75844SAndroid Build Coastguard Worker static_cast<double>(rtcp_report.fraction_lost) / (1 << 8);
271*d9f75844SAndroid Build Coastguard Worker if (clockrate_hz > 0) {
272*d9f75844SAndroid Build Coastguard Worker remote_stat.jitter =
273*d9f75844SAndroid Build Coastguard Worker static_cast<double>(rtcp_report.jitter) / clockrate_hz;
274*d9f75844SAndroid Build Coastguard Worker }
275*d9f75844SAndroid Build Coastguard Worker if (block_data.has_rtt()) {
276*d9f75844SAndroid Build Coastguard Worker remote_stat.round_trip_time =
277*d9f75844SAndroid Build Coastguard Worker static_cast<double>(block_data.last_rtt_ms()) /
278*d9f75844SAndroid Build Coastguard Worker rtc::kNumMillisecsPerSec;
279*d9f75844SAndroid Build Coastguard Worker }
280*d9f75844SAndroid Build Coastguard Worker remote_stat.last_report_received_timestamp_ms =
281*d9f75844SAndroid Build Coastguard Worker block_data.report_block_timestamp_utc_us() /
282*d9f75844SAndroid Build Coastguard Worker rtc::kNumMicrosecsPerMillisec;
283*d9f75844SAndroid Build Coastguard Worker channel_stats.remote_rtcp = remote_stat;
284*d9f75844SAndroid Build Coastguard Worker
285*d9f75844SAndroid Build Coastguard Worker // Receive only channel won't send any RTP packets.
286*d9f75844SAndroid Build Coastguard Worker if (!channel_stats.remote_ssrc.has_value()) {
287*d9f75844SAndroid Build Coastguard Worker channel_stats.remote_ssrc = remote_ssrc_;
288*d9f75844SAndroid Build Coastguard Worker }
289*d9f75844SAndroid Build Coastguard Worker break;
290*d9f75844SAndroid Build Coastguard Worker }
291*d9f75844SAndroid Build Coastguard Worker
292*d9f75844SAndroid Build Coastguard Worker return channel_stats;
293*d9f75844SAndroid Build Coastguard Worker }
294*d9f75844SAndroid Build Coastguard Worker
295*d9f75844SAndroid Build Coastguard Worker } // namespace webrtc
296