1 /**
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <MediaQualityAnalyzer.h>
18 #include <ImsMediaTimer.h>
19 #include <ImsMediaTrace.h>
20 #include <ImsMediaAudioUtil.h>
21 #include <RtcpXrEncoder.h>
22 #include <RtpReceptionStats.h>
23 #include <AudioConfig.h>
24 #include <stdlib.h>
25 #include <algorithm>
26 #include <numeric>
27
28 using namespace android::telephony::imsmedia;
29
30 #define DEFAULT_PARAM (-1)
31 #define DEFAULT_INACTIVITY_TIME_FOR_CALL_QUALITY (4)
32 #define CALL_QUALITY_MONITORING_TIME (5)
33 #define MAX_NUM_PACKET_STORED (500)
34 #define DELETE_ALL (65536)
35 #define TIMER_INTERVAL (1000) // 1 sec
36 #define STOP_TIMEOUT (1000) // 1 sec
37 #define MESSAGE_PROCESSING_INTERVAL (20000) // 20 msec
38 #define MEDIA_DIRECTION_CONTAINS_RECEIVE(a) \
39 ((a) == RtpConfig::MEDIA_DIRECTION_SEND_RECEIVE || \
40 (a) == RtpConfig::MEDIA_DIRECTION_RECEIVE_ONLY)
41 #define MAX_TIME_FACTOR (1000)
42
MediaQualityAnalyzer()43 MediaQualityAnalyzer::MediaQualityAnalyzer()
44 {
45 mTimeStarted = 0;
46 mCodecType = 0;
47 mCodecAttribute = 0;
48 mIsRxRtpEnabled = false;
49 mIsRtcpEnabled = false;
50 mCallback = nullptr;
51 std::unique_ptr<RtcpXrEncoder> analyzer(new RtcpXrEncoder());
52 mRtcpXrEncoder = std::move(analyzer);
53 mBaseRtpInactivityTimes.clear();
54 mCurrentRtpInactivityTimes.clear();
55 mRtcpInactivityTime = 0;
56 mRtpHysteresisTime = 0;
57 mPacketLossDuration = 0;
58 mPacketLossThreshold.clear();
59 mJitterThreshold.clear();
60 mNotifyStatus = false;
61 mCountRtpInactivity = 0;
62 mCountRtcpInactivity = 0;
63 mNumRtcpPacketReceived = 0;
64 mReceptionInterval = 0;
65 mLatestRtcpSrTimestamp = 0;
66 mLatestRtcpSrNtpTimestamp = 0;
67 mLatestRoundTripDelayMs = 0;
68 mTimeFactor = 1;
69 reset();
70 }
71
~MediaQualityAnalyzer()72 MediaQualityAnalyzer::~MediaQualityAnalyzer()
73 {
74 if (!IsThreadStopped())
75 {
76 stop();
77 }
78 }
79
setConfig(AudioConfig * config)80 void MediaQualityAnalyzer::setConfig(AudioConfig* config)
81 {
82 if (!isSameConfig(config))
83 {
84 reset();
85 }
86
87 mIsRxRtpEnabled = MEDIA_DIRECTION_CONTAINS_RECEIVE(config->getMediaDirection());
88 mCodecType = config->getCodecType();
89 mCodecAttribute = config->getEvsParams().getEvsBandwidth();
90
91 mCallQuality.setCodecType(convertAudioCodecType(
92 mCodecType, ImsMediaAudioUtil::FindMaxEvsBandwidthFromRange(mCodecAttribute)));
93
94 if (mCodecType == AudioConfig::CODEC_AMR)
95 {
96 mRtcpXrEncoder->setSamplingRate(8);
97 }
98 else
99 {
100 mRtcpXrEncoder->setSamplingRate(16);
101 }
102
103 // Enable RTCP if both interval and direction is valid
104 bool isRtcpEnabled = (config->getRtcpConfig().getIntervalSec() > 0 &&
105 config->getMediaDirection() != RtpConfig::MEDIA_DIRECTION_NO_FLOW);
106
107 if (mIsRtcpEnabled != isRtcpEnabled)
108 {
109 mIsRtcpEnabled = isRtcpEnabled;
110 mCountRtcpInactivity = 0;
111 mNumRtcpPacketReceived = 0;
112 }
113
114 IMLOGI4("[setConfig] codec type[%d], bandwidth[%d], rxRtp[%d], rtcp[%d]", mCodecType,
115 mCodecAttribute, mIsRxRtpEnabled, mIsRtcpEnabled);
116 }
117
setCallback(BaseSessionCallback * callback)118 void MediaQualityAnalyzer::setCallback(BaseSessionCallback* callback)
119 {
120 mCallback = callback;
121 }
122
setMediaQualityThreshold(const MediaQualityThreshold & threshold)123 void MediaQualityAnalyzer::setMediaQualityThreshold(const MediaQualityThreshold& threshold)
124 {
125 mBaseRtpInactivityTimes = threshold.getRtpInactivityTimerMillis();
126 mCurrentRtpInactivityTimes = mBaseRtpInactivityTimes;
127 mRtcpInactivityTime = threshold.getRtcpInactivityTimerMillis();
128 mRtpHysteresisTime = threshold.getRtpHysteresisTimeInMillis();
129 mPacketLossDuration = threshold.getRtpPacketLossDurationMillis();
130 mPacketLossThreshold = threshold.getRtpPacketLossRate();
131 mJitterThreshold = threshold.getRtpJitterMillis();
132 mNotifyStatus = threshold.getNotifyCurrentStatus();
133
134 mCountRtpInactivity = 0;
135 mCountRtcpInactivity = 0;
136 mNumRtcpPacketReceived = 0;
137
138 // reset the status
139 mQualityStatus = MediaQualityStatus();
140
141 mPacketLossChecker.initialize(mRtpHysteresisTime);
142 mJitterChecker.initialize(mRtpHysteresisTime);
143 }
144
setNotifyRtpReceptionStatsInterval(const int32_t intervalMs)145 void MediaQualityAnalyzer::setNotifyRtpReceptionStatsInterval(const int32_t intervalMs)
146 {
147 IMLOGD1("[setNotifyRtpReceptionStatsInterval] interval[%d]", intervalMs);
148 mReceptionInterval = intervalMs;
149 }
150
isSameConfig(AudioConfig * config)151 bool MediaQualityAnalyzer::isSameConfig(AudioConfig* config)
152 {
153 return (mCodecType == config->getCodecType() &&
154 mCodecAttribute == config->getEvsParams().getEvsBandwidth() &&
155 mIsRxRtpEnabled == MEDIA_DIRECTION_CONTAINS_RECEIVE(config->getMediaDirection()));
156 }
157
start()158 void MediaQualityAnalyzer::start()
159 {
160 if (IsThreadStopped())
161 {
162 IMLOGD0("[start]");
163 // reset the report
164 mCallQuality = CallQuality();
165 mTimeStarted = ImsMediaTimer::GetTimeInMilliSeconds();
166 StartThread("MediaQualityAnalyzer");
167 }
168 }
169
stop()170 void MediaQualityAnalyzer::stop()
171 {
172 IMLOGD0("[stop]");
173
174 if (!IsThreadStopped())
175 {
176 StopThread();
177 mConditionExit.wait_timeout(STOP_TIMEOUT);
178 notifyCallQuality();
179 }
180
181 reset();
182 }
183
collectInfo(const int32_t streamType,uint64_t param)184 void MediaQualityAnalyzer::collectInfo(const int32_t streamType, uint64_t param)
185 {
186 if (streamType == kStreamRtpTx && param != 0)
187 {
188 RtpPacket* packet = reinterpret_cast<RtpPacket*>(param);
189 mListTxPacket.push_back(packet);
190
191 if (mListTxPacket.size() > MAX_NUM_PACKET_STORED)
192 {
193 RtpPacket* pPacket = mListTxPacket.front();
194 mListTxPacket.pop_front();
195 delete pPacket;
196 }
197
198 mCallQuality.setNumRtpPacketsTransmitted(mCallQuality.getNumRtpPacketsTransmitted() + 1);
199 }
200 else if (streamType == kStreamRtpRx && param != 0)
201 {
202 RtpPacket* packet = reinterpret_cast<RtpPacket*>(param);
203 // for call quality report
204 mCallQuality.setNumRtpPacketsReceived(mCallQuality.getNumRtpPacketsReceived() + 1);
205
206 switch (packet->rtpDataType)
207 {
208 case kRtpDataTypeSid:
209 mCallQuality.setNumRtpSidPacketsReceived(
210 mCallQuality.getNumRtpSidPacketsReceived() + 1);
211 break;
212 default:
213 break;
214 }
215
216 // for jitter check
217 if (mSSRC != packet->ssrc) // stream is reset
218 {
219 mJitterRxPacket = std::abs(packet->jitter);
220 // update rtcp-xr params
221 mRtcpXrEncoder->setSsrc(packet->ssrc);
222 }
223 else
224 {
225 mJitterRxPacket =
226 mJitterRxPacket + (double)(std::abs(packet->jitter) - mJitterRxPacket) * 0.0625;
227 }
228
229 mCallQualitySumRelativeJitter += mJitterRxPacket;
230
231 if (mCallQuality.getMaxRelativeJitter() < mJitterRxPacket)
232 {
233 mCallQuality.setMaxRelativeJitter(mJitterRxPacket);
234 }
235
236 mCallQuality.setAverageRelativeJitter(
237 mCallQualitySumRelativeJitter / mCallQuality.getNumRtpPacketsReceived());
238
239 mSSRC = packet->ssrc;
240 mNumRxPacket++;
241 mListRxPacket.push_back(packet);
242
243 if (mListRxPacket.size() > MAX_NUM_PACKET_STORED)
244 {
245 RtpPacket* pPacket = mListRxPacket.front();
246 mListRxPacket.pop_front();
247 delete pPacket;
248 }
249
250 IMLOGD_PACKET3(IM_PACKET_LOG_RTP, "[collectInfo] seq[%d], jitter[%d], rx list size[%d]",
251 packet->seqNum, packet->jitter, mListRxPacket.size());
252 }
253 else if (streamType == kStreamRtcp)
254 {
255 mNumRtcpPacketReceived++;
256 IMLOGD_PACKET1(
257 IM_PACKET_LOG_RTP, "[collectInfo] rtcp received[%d]", mNumRtcpPacketReceived);
258
259 if (param != 0)
260 {
261 RtcpSr* rtcpSr = reinterpret_cast<RtcpSr*>(param);
262 uint64_t ntpTs = (static_cast<uint64_t>(rtcpSr->ntpTimestampMsw) << 32) |
263 rtcpSr->ntpTimestampLsw;
264 IMLOGD_PACKET2(IM_PACKET_LOG_RTP, "[collectInfo] rtcp-sr : ts=%d, ntp=%ld",
265 rtcpSr->rtpTimestamp, ntpTs);
266 mLatestRtcpSrTimestamp = rtcpSr->rtpTimestamp;
267 mLatestRtcpSrNtpTimestamp = ntpTs;
268 delete rtcpSr;
269 }
270 }
271 }
272
collectOptionalInfo(const int32_t optionType,const int32_t seq,const int32_t value)273 void MediaQualityAnalyzer::collectOptionalInfo(
274 const int32_t optionType, const int32_t seq, const int32_t value)
275 {
276 if (optionType == kTimeToLive)
277 {
278 // TODO : pass data to rtcp-xr
279 }
280 else if (optionType == kRoundTripDelay)
281 {
282 IMLOGD_PACKET1(IM_PACKET_LOG_RTP, "[collectOptionalInfo] round trip time[%d]", value);
283 mLatestRoundTripDelayMs = value;
284 mSumRoundTripTime += value;
285 mCountRoundTripTime++;
286 mCallQuality.setAverageRoundTripTime(mSumRoundTripTime / mCountRoundTripTime);
287
288 mRtcpXrEncoder->setRoundTripDelay(value);
289 }
290 else if (optionType == kReportPacketLossGap)
291 {
292 if (mListLostPacket.size() > MAX_NUM_PACKET_STORED)
293 {
294 LostPacket* entry = mListLostPacket.front();
295 mListLostPacket.pop_front();
296 delete entry;
297 }
298
299 LostPacket* entry = new LostPacket(seq, value, ImsMediaTimer::GetTimeInMilliSeconds());
300 mListLostPacket.push_back(entry);
301
302 for (int32_t i = 0; i < value; i++)
303 {
304 // for rtcp xr
305 mRtcpXrEncoder->stackRxRtpStatus(kRtpStatusLost, 0);
306
307 // for call quality report
308 mCallQuality.setNumRtpPacketsNotReceived(
309 mCallQuality.getNumRtpPacketsNotReceived() + 1);
310 mCallQualityNumLostPacket++;
311 // for loss checking
312 mNumLostPacket++;
313 }
314
315 IMLOGD_PACKET3(IM_PACKET_LOG_RTP,
316 "[collectOptionalInfo] lost packet seq[%d], value[%d], list size[%d]", seq, value,
317 mListLostPacket.size());
318 }
319 else if (optionType == kAudioPlayingStatus)
320 {
321 switch (value)
322 {
323 case kAudioTypeNoData:
324 mCallQuality.setNumNoDataFrames(mCallQuality.getNumNoDataFrames() + 1);
325 break;
326 case kAudioTypeVoice:
327 mCallQuality.setNumVoiceFrames(mCallQuality.getNumVoiceFrames() + 1);
328 break;
329 }
330 }
331 }
332
collectRxRtpStatus(const int32_t seq,const kRtpPacketStatus status,const uint32_t time)333 void MediaQualityAnalyzer::collectRxRtpStatus(
334 const int32_t seq, const kRtpPacketStatus status, const uint32_t time)
335 {
336 if (mListRxPacket.empty())
337 {
338 return;
339 }
340
341 bool found = false;
342
343 for (std::list<RtpPacket*>::reverse_iterator rit = mListRxPacket.rbegin();
344 rit != mListRxPacket.rend(); ++rit)
345 {
346 RtpPacket* packet = *rit;
347
348 if (packet->seqNum == seq)
349 {
350 packet->status = status;
351 uint32_t delay = time - packet->arrival;
352 mRtcpXrEncoder->stackRxRtpStatus(packet->status, delay);
353 IMLOGD_PACKET3(IM_PACKET_LOG_RTP, "[collectRxRtpStatus] seq[%d], status[%d], delay[%u]",
354 seq, packet->status, delay);
355
356 // set the max playout delay
357 if (delay > mCallQuality.getMaxPlayoutDelayMillis())
358 {
359 mCallQuality.setMaxPlayoutDelayMillis(delay);
360 }
361
362 // set the min playout delay
363 if (delay < mCallQuality.getMinPlayoutDelayMillis() ||
364 mCallQuality.getMinPlayoutDelayMillis() == 0)
365 {
366 mCallQuality.setMinPlayoutDelayMillis(delay);
367 }
368
369 mListPlayoutDelay.push_back(delay);
370 found = true;
371 break;
372 }
373 }
374
375 if (!found)
376 {
377 IMLOGW1("[collectRxRtpStatus] no rtp packet found seq[%d]", seq);
378 return;
379 }
380
381 switch (status)
382 {
383 case kRtpStatusNormal:
384 mCallQualityNumRxPacket++;
385 mCallQualityInactNumRxPacket++;
386 break;
387 case kRtpStatusLate:
388 case kRtpStatusDiscarded:
389 mCallQuality.setNumDroppedRtpPackets(mCallQuality.getNumDroppedRtpPackets() + 1);
390 mCallQualityNumDroppedPacket++;
391 mCallQualityNumRxPacket++;
392 IMLOGD_PACKET1(IM_PACKET_LOG_RTP, "[collectRxRtpStatus] num late arrival[%d]",
393 mCallQuality.getNumDroppedRtpPackets());
394 break;
395 case kRtpStatusDuplicated:
396 mCallQuality.setNumRtpDuplicatePackets(mCallQuality.getNumRtpDuplicatePackets() + 1);
397 mCallQualityNumRxPacket++;
398 break;
399 default:
400 break;
401 }
402
403 if (mBeginSeq == -1)
404 {
405 mBeginSeq = seq;
406 mEndSeq = seq;
407 }
408 else
409 {
410 if (USHORT_SEQ_ROUND_COMPARE(seq, mEndSeq))
411 {
412 mEndSeq = seq;
413 }
414 }
415 }
416
collectJitterBufferSize(const int32_t currSize,const int32_t maxSize)417 void MediaQualityAnalyzer::collectJitterBufferSize(const int32_t currSize, const int32_t maxSize)
418 {
419 IMLOGD_PACKET2(IM_PACKET_LOG_RTP, "[collectJitterBufferSize] current size[%d], max size[%d]",
420 currSize, maxSize);
421
422 mCurrentBufferSize = currSize;
423 mMaxBufferSize = maxSize;
424 mRtcpXrEncoder->setJitterBufferStatus(currSize, maxSize);
425 }
426
processData(const int32_t timeCount)427 void MediaQualityAnalyzer::processData(const int32_t timeCount)
428 {
429 IMLOGD_PACKET1(IM_PACKET_LOG_RTP, "[processData] count[%d]", timeCount);
430
431 bool reportCallQuality = false;
432
433 // call quality inactivity
434 if (mCallQualityInactNumRxPacket == 0)
435 {
436 mCallQualityNumInactPeriods++;
437
438 if ((mCallQualityNumInactPeriods >= DEFAULT_INACTIVITY_TIME_FOR_CALL_QUALITY) &&
439 (!mCallQuality.getRtpInactivityDetected()))
440 {
441 mCallQuality.setRtpInactivityDetected(true);
442 reportCallQuality = true;
443 }
444 }
445 else
446 {
447 mCallQualityNumInactPeriods = 0;
448
449 if (mCallQuality.getRtpInactivityDetected())
450 {
451 mCallQuality.setRtpInactivityDetected(false);
452 reportCallQuality = true;
453 }
454 }
455
456 mCallQualityInactNumRxPacket = 0;
457
458 // call quality packet loss
459 if (timeCount % CALL_QUALITY_MONITORING_TIME == 0)
460 {
461 double lossRate = 0;
462 int32_t quality;
463
464 // Calculate loss rate based on lost packets only
465 /* b/314203053 Dropped packets will also be included in the calculation,
466 but that will only happen once Lassen also includes it in their calculation,
467 so that the two implementations match */
468 if (mCallQualityNumRxPacket == 0)
469 {
470 lossRate = 0;
471 // Set quality to bad, based on no packets received in 5 seconds
472 quality = CallQuality::kCallQualityBad;
473 }
474 else
475 {
476 // mCallQualityNumRxPacket includes dropped packets, so mCallQualityNumDroppedPacket
477 // is not included separately in the denominator for this calculation
478 uint32_t totalLostPackets = mCallQualityNumLostPacket; //+mCallQualityNumDroppedPacket;
479 lossRate = (double)totalLostPackets /
480 (mCallQualityNumLostPacket + mCallQualityNumRxPacket) * 100;
481
482 // Set quality based on lossRate
483 quality = getCallQuality(lossRate);
484 }
485
486 mCallQuality.setDownlinkCallQualityLevel(quality);
487
488 IMLOGD6("[processData] CallQuality stats [last %d seconds]: lost=%d, received=%d, "
489 "dropped=%d, lossRate=%.1f, quality=%d",
490 CALL_QUALITY_MONITORING_TIME, mCallQualityNumLostPacket, mCallQualityNumRxPacket,
491 mCallQualityNumDroppedPacket, lossRate, quality);
492
493 mCallQualityNumLostPacket = 0;
494 mCallQualityNumRxPacket = 0;
495 mCallQualityNumDroppedPacket = 0;
496
497 // Notify every period
498 reportCallQuality = true;
499 }
500
501 if (reportCallQuality)
502 {
503 notifyCallQuality();
504 }
505
506 processMediaQuality();
507 processRtpReceptionStats(timeCount);
508 }
509
processMediaQuality()510 void MediaQualityAnalyzer::processMediaQuality()
511 {
512 // media quality rtp inactivity
513 if (mNumRxPacket == 0 && mIsRxRtpEnabled)
514 {
515 mCountRtpInactivity += 1000;
516 }
517 else
518 {
519 mCountRtpInactivity = 0;
520 mNumRxPacket = 0;
521 mCurrentRtpInactivityTimes = mBaseRtpInactivityTimes;
522 }
523
524 // media quality rtcp inactivity
525 if (mNumRtcpPacketReceived == 0 && mIsRtcpEnabled)
526 {
527 mCountRtcpInactivity += 1000;
528 }
529 else
530 {
531 mCountRtcpInactivity = 0;
532 mNumRtcpPacketReceived = 0;
533 }
534
535 mQualityStatus.setRtpInactivityTimeMillis(mCountRtpInactivity);
536 mQualityStatus.setRtcpInactivityTimeMillis(mCountRtcpInactivity);
537 mQualityStatus.setRtpJitterMillis(mJitterRxPacket);
538
539 if (mPacketLossDuration != 0)
540 {
541 // counts received packets for the duration
542 int32_t numReceivedPacketsInDuration =
543 std::count_if(mListRxPacket.begin(), mListRxPacket.end(),
544 [=, this](RtpPacket* packet)
545 {
546 return (ImsMediaTimer::GetTimeInMilliSeconds() - packet->arrival <=
547 mPacketLossDuration);
548 });
549
550 int32_t numLostPacketsInDuration = 0;
551
552 if (!mListLostPacket.empty())
553 {
554 // cumulates the number of lost packets for the duration
555 std::list<LostPacket*> listLostPacketInDuration;
556 std::copy_if(mListLostPacket.begin(), mListLostPacket.end(),
557 std::back_inserter(listLostPacketInDuration),
558 [=, this](LostPacket* packet)
559 {
560 return (ImsMediaTimer::GetTimeInMilliSeconds() - packet->markedTime <=
561 mPacketLossDuration);
562 });
563
564 numLostPacketsInDuration = std::accumulate(begin(listLostPacketInDuration),
565 end(listLostPacketInDuration), 0,
566 [](int i, const LostPacket* packet)
567 {
568 return packet->numLoss + i;
569 });
570 }
571
572 if (numLostPacketsInDuration == 0 || numReceivedPacketsInDuration == 0)
573 {
574 mQualityStatus.setRtpPacketLossRate(0);
575 }
576 else
577 {
578 int32_t lossRate = numLostPacketsInDuration * 100 /
579 (numReceivedPacketsInDuration + numLostPacketsInDuration);
580
581 IMLOGD4("[processMediaQuality] MediaQuality stats [last %d msec]: lossRate=%d, "
582 "received=%d, lost=%d",
583 mPacketLossDuration, lossRate, numReceivedPacketsInDuration,
584 numLostPacketsInDuration);
585 mQualityStatus.setRtpPacketLossRate(lossRate);
586 }
587 }
588 else
589 {
590 mQualityStatus.setRtpPacketLossRate(0);
591 }
592
593 bool shouldNotify = false;
594
595 // check jitter notification, this notification should be triggered when the RTPs are receiving
596 if (!mJitterThreshold.empty() && mIsRxRtpEnabled && mCountRtpInactivity == 0)
597 {
598 if (mJitterChecker.checkNotifiable(mJitterThreshold, mQualityStatus.getRtpJitterMillis()))
599 {
600 shouldNotify = true;
601 }
602 }
603
604 // check packet loss notification, this notification should be triggered when the RTPs are
605 // receiving
606 if (!mPacketLossThreshold.empty() && mIsRxRtpEnabled && mCountRtpInactivity == 0)
607 {
608 if (mPacketLossChecker.checkNotifiable(
609 mPacketLossThreshold, mQualityStatus.getRtpPacketLossRate()))
610 {
611 shouldNotify = true;
612 }
613 }
614
615 IMLOGD_PACKET4(IM_PACKET_LOG_RTP,
616 "[processMediaQuality] rtpInactivity=%d, rtcpInactivity=%d, lossRate=%d, "
617 "jitter=%d",
618 mQualityStatus.getRtpInactivityTimeMillis(),
619 mQualityStatus.getRtcpInactivityTimeMillis(), mQualityStatus.getRtpPacketLossRate(),
620 mQualityStatus.getRtpJitterMillis());
621
622 if (mNotifyStatus)
623 {
624 notifyMediaQualityStatus();
625 mNotifyStatus = false;
626 return;
627 }
628
629 if (!mCurrentRtpInactivityTimes.empty() && mIsRxRtpEnabled)
630 {
631 std::vector<int32_t>::iterator rtpIter = std::find_if(mCurrentRtpInactivityTimes.begin(),
632 mCurrentRtpInactivityTimes.end(),
633 [=, this](int32_t inactivityTime)
634 {
635 return (inactivityTime != 0 &&
636 mCountRtpInactivity >= inactivityTime); // check cross the threshold
637 });
638
639 if (rtpIter != mCurrentRtpInactivityTimes.end()) // found
640 {
641 mCurrentRtpInactivityTimes.erase(rtpIter);
642 notifyMediaQualityStatus();
643 return;
644 }
645 }
646
647 if (mRtcpInactivityTime != 0 && mCountRtcpInactivity == mRtcpInactivityTime && mIsRtcpEnabled)
648 {
649 notifyMediaQualityStatus();
650 mCountRtcpInactivity = 0;
651 return;
652 }
653
654 if (shouldNotify)
655 {
656 notifyMediaQualityStatus();
657 }
658 }
659
processRtpReceptionStats(const int32_t timeCount)660 void MediaQualityAnalyzer::processRtpReceptionStats(const int32_t timeCount)
661 {
662 if (mReceptionInterval != 0 && timeCount % (mReceptionInterval / 1000) == 0)
663 {
664 RtpReceptionStats* stats = new RtpReceptionStats();
665
666 if (!mListRxPacket.empty())
667 {
668 stats->setRtpTimestamp(mListRxPacket.back()->timestamp);
669 stats->setRtcpSrTimestamp(mLatestRtcpSrTimestamp);
670 stats->setRtcpSrNtpTimestamp(mLatestRtcpSrNtpTimestamp);
671 stats->setRoundTripTimeMs(mLatestRoundTripDelayMs);
672 uint32_t meanDelay = mListPlayoutDelay.empty()
673 ? 0
674 : std::accumulate(mListPlayoutDelay.begin(), mListPlayoutDelay.end(), 0.0f) /
675 mListPlayoutDelay.size();
676 stats->setJitterBufferMs(meanDelay);
677 mListPlayoutDelay.clear();
678 }
679
680 mCallback->SendEvent(kAudioNotifyRtpReceptionStats, reinterpret_cast<uint64_t>(stats));
681 }
682 }
683
notifyCallQuality()684 void MediaQualityAnalyzer::notifyCallQuality()
685 {
686 if (mCallback != nullptr)
687 {
688 mCallQuality.setCallDuration(ImsMediaTimer::GetTimeInMilliSeconds() - mTimeStarted);
689 mCallQuality.setCodecType(convertAudioCodecType(
690 mCodecType, ImsMediaAudioUtil::FindMaxEvsBandwidthFromRange(mCodecAttribute)));
691
692 IMLOGD1("[notifyCallQuality] duration[%d]", mCallQuality.getCallDuration());
693 CallQuality* callQuality = new CallQuality(mCallQuality);
694 mCallback->SendEvent(kAudioCallQualityChangedInd, reinterpret_cast<uint64_t>(callQuality));
695
696 // reset the items to keep in reporting interval
697 mCallQuality.setMinPlayoutDelayMillis(0);
698 mCallQuality.setMaxPlayoutDelayMillis(0);
699 }
700 }
701
notifyMediaQualityStatus()702 void MediaQualityAnalyzer::notifyMediaQualityStatus()
703 {
704 IMLOGD0("[notifyMediaQualityStatus]");
705 MediaQualityStatus* status = new MediaQualityStatus(mQualityStatus);
706 mCallback->SendEvent(kImsMediaEventMediaQualityStatus, reinterpret_cast<uint64_t>(status));
707 }
708
getRtcpXrReportBlock(const uint32_t rtcpXrReport,uint8_t * data,uint32_t & size)709 bool MediaQualityAnalyzer::getRtcpXrReportBlock(
710 const uint32_t rtcpXrReport, uint8_t* data, uint32_t& size)
711 {
712 IMLOGD1("[getRtcpXrReportBlock] rtcpXrReport[%d]", rtcpXrReport);
713
714 if (rtcpXrReport == 0)
715 {
716 return false;
717 }
718
719 if (!mRtcpXrEncoder->createRtcpXrReport(
720 rtcpXrReport, &mListRxPacket, &mListLostPacket, mBeginSeq, mEndSeq, data, size))
721 {
722 IMLOGW0("[getRtcpXrReportBlock] fail to createRtcpXrReport");
723 return false;
724 }
725
726 mBeginSeq = mEndSeq + 1;
727 clearPacketList(mListRxPacket, mEndSeq);
728 clearPacketList(mListTxPacket, mEndSeq);
729 clearLostPacketList(mEndSeq);
730 return true;
731 }
732
getCallQuality()733 CallQuality MediaQualityAnalyzer::getCallQuality()
734 {
735 return mCallQuality;
736 }
737
getRxPacketSize()738 uint32_t MediaQualityAnalyzer::getRxPacketSize()
739 {
740 return mListRxPacket.size();
741 }
742
getTxPacketSize()743 uint32_t MediaQualityAnalyzer::getTxPacketSize()
744 {
745 return mListTxPacket.size();
746 }
747
getLostPacketSize()748 uint32_t MediaQualityAnalyzer::getLostPacketSize()
749 {
750 return std::accumulate(begin(mListLostPacket), end(mListLostPacket), 0,
751 [](int i, const LostPacket* packet)
752 {
753 return packet->numLoss + i;
754 });
755 }
756
setEventTimeFactor(const uint32_t timeFactor)757 void MediaQualityAnalyzer::setEventTimeFactor(const uint32_t timeFactor)
758 {
759 if (timeFactor > MAX_TIME_FACTOR)
760 {
761 IMLOGW1("[setEventTimeFactor] event time factor is over the maximum[%d]", MAX_TIME_FACTOR);
762 mTimeFactor = MAX_TIME_FACTOR;
763 }
764 else
765 {
766 mTimeFactor = timeFactor == 0 ? 1 : timeFactor;
767 }
768 }
769
SendEvent(uint32_t event,uint64_t paramA,uint64_t paramB)770 void MediaQualityAnalyzer::SendEvent(uint32_t event, uint64_t paramA, uint64_t paramB)
771 {
772 AddEvent(event, paramA, paramB);
773 }
774
AddEvent(uint32_t event,uint64_t paramA,uint64_t paramB)775 void MediaQualityAnalyzer::AddEvent(uint32_t event, uint64_t paramA, uint64_t paramB)
776 {
777 IMLOGD_PACKET2(IM_PACKET_LOG_RTP, "[AddEvent] event[%d], size[%d]", event, mListevent.size());
778 std::lock_guard<std::mutex> guard(mEventMutex);
779 mListevent.push_back(event);
780 mListParamA.push_back(paramA);
781 mListParamB.push_back(paramB);
782 }
783
processEvent(uint32_t event,uint64_t paramA,uint64_t paramB)784 void MediaQualityAnalyzer::processEvent(uint32_t event, uint64_t paramA, uint64_t paramB)
785 {
786 switch (event)
787 {
788 case kRequestRoundTripTimeDelayUpdate:
789 collectOptionalInfo(kRoundTripDelay, 0, paramA);
790 break;
791 case kRequestAudioPlayingStatus:
792 collectOptionalInfo(kAudioPlayingStatus, 0, paramA);
793 break;
794 case kCollectPacketInfo:
795 collectInfo(static_cast<ImsMediaStreamType>(paramA), paramB);
796 break;
797 case kCollectOptionalInfo:
798 if (paramA != 0)
799 {
800 SessionCallbackParameter* param =
801 reinterpret_cast<SessionCallbackParameter*>(paramA);
802 collectOptionalInfo(param->type, param->param1, param->param2);
803 delete param;
804 }
805 break;
806 case kCollectRxRtpStatus:
807 if (paramA != 0)
808 {
809 SessionCallbackParameter* param =
810 reinterpret_cast<SessionCallbackParameter*>(paramA);
811 collectRxRtpStatus(
812 param->type, static_cast<kRtpPacketStatus>(param->param1), param->param2);
813 delete param;
814 }
815 break;
816 case kCollectJitterBufferSize:
817 collectJitterBufferSize(static_cast<int32_t>(paramA), static_cast<int32_t>(paramB));
818 break;
819 case kGetRtcpXrReportBlock:
820 {
821 uint32_t size = 0;
822 uint8_t* reportBlock = new uint8_t[MAX_BLOCK_LENGTH]{};
823
824 if (getRtcpXrReportBlock(static_cast<int32_t>(paramA), reportBlock, size))
825 {
826 mCallback->SendEvent(
827 kRequestSendRtcpXrReport, reinterpret_cast<uint64_t>(reportBlock), size);
828 }
829 else
830 {
831 delete[] reportBlock;
832 }
833 }
834 break;
835 default:
836 break;
837 }
838 }
839
run()840 void* MediaQualityAnalyzer::run()
841 {
842 IMLOGD1("[run] enter, %p", this);
843 uint64_t nextTime = ImsMediaTimer::GetTimeInMicroSeconds();
844 int32_t timeCount = 0;
845 uint32_t prevTimeInMsec = ImsMediaTimer::GetTimeInMilliSeconds();
846
847 while (true)
848 {
849 if (IsThreadStopped())
850 {
851 IMLOGD0("[run] terminated");
852 break;
853 }
854
855 nextTime += MESSAGE_PROCESSING_INTERVAL / mTimeFactor;
856 uint64_t nCurrTime = ImsMediaTimer::GetTimeInMicroSeconds();
857 int64_t nTime = nextTime - nCurrTime;
858
859 if (nTime > 0)
860 {
861 ImsMediaTimer::USleep(nTime);
862 }
863
864 // process event in the list
865 for (;;)
866 {
867 mEventMutex.lock();
868
869 if (IsThreadStopped() || mListevent.size() == 0)
870 {
871 mEventMutex.unlock();
872 break;
873 }
874
875 processEvent(mListevent.front(), mListParamA.front(), mListParamB.front());
876
877 mListevent.pop_front();
878 mListParamA.pop_front();
879 mListParamB.pop_front();
880 mEventMutex.unlock();
881 }
882
883 if (IsThreadStopped())
884 {
885 IMLOGD0("[run] terminated");
886 break;
887 }
888
889 uint32_t currTimeInMsec = ImsMediaTimer::GetTimeInMilliSeconds();
890
891 // process every TIMER_INTERVAL
892 if (currTimeInMsec - prevTimeInMsec >= TIMER_INTERVAL / mTimeFactor)
893 {
894 processData(++timeCount);
895 prevTimeInMsec += TIMER_INTERVAL / mTimeFactor;
896 }
897 }
898
899 IMLOGD1("[run] exit %p", this);
900 mConditionExit.signal();
901 return nullptr;
902 }
903
reset()904 void MediaQualityAnalyzer::reset()
905 {
906 mSSRC = DEFAULT_PARAM;
907 mBeginSeq = -1;
908 mEndSeq = -1;
909
910 mCallQualitySumRelativeJitter = 0;
911 mSumRoundTripTime = 0;
912 mCountRoundTripTime = 0;
913 mCurrentBufferSize = 0;
914 mMaxBufferSize = 0;
915 mCallQualityNumRxPacket = 0;
916 mCallQualityNumLostPacket = 0;
917 mCallQualityNumDroppedPacket = 0;
918 mCallQualityInactNumRxPacket = 0;
919 mCallQualityNumInactPeriods = 0;
920 clearPacketList(mListRxPacket, DELETE_ALL);
921 clearPacketList(mListTxPacket, DELETE_ALL);
922 clearLostPacketList(DELETE_ALL);
923 mNumRxPacket = 0;
924 mNumLostPacket = 0;
925 mJitterRxPacket = 0.0;
926
927 // rtp and rtcp inactivity
928 mCountRtpInactivity = 0;
929 mCountRtcpInactivity = 0;
930 mNumRtcpPacketReceived = 0;
931
932 // reset the status
933 mQualityStatus = MediaQualityStatus();
934
935 mPacketLossChecker.initialize(mRtpHysteresisTime);
936 mJitterChecker.initialize(mRtpHysteresisTime);
937 }
938
clearPacketList(std::list<RtpPacket * > & list,const int32_t seq)939 void MediaQualityAnalyzer::clearPacketList(std::list<RtpPacket*>& list, const int32_t seq)
940 {
941 if (list.empty())
942 {
943 return;
944 }
945
946 for (std::list<RtpPacket*>::iterator iter = list.begin(); iter != list.end();)
947 {
948 RtpPacket* packet = *iter;
949 // do not remove the packet seq is larger than target seq
950 if (packet->seqNum > seq)
951 {
952 iter++;
953 continue;
954 }
955
956 iter = list.erase(iter);
957 delete packet;
958 }
959 }
960
clearLostPacketList(const int32_t seq)961 void MediaQualityAnalyzer::clearLostPacketList(const int32_t seq)
962 {
963 if (mListLostPacket.empty())
964 {
965 return;
966 }
967
968 for (std::list<LostPacket*>::iterator iter = mListLostPacket.begin();
969 iter != mListLostPacket.end();)
970 {
971 LostPacket* packet = *iter;
972 // do not remove the lost packet entry seq is larger than target seq
973 if (packet->seqNum > seq)
974 {
975 iter++;
976 continue;
977 }
978
979 iter = mListLostPacket.erase(iter);
980 delete packet;
981 }
982 }
983
getCallQuality(const double lossRate)984 uint32_t MediaQualityAnalyzer::getCallQuality(const double lossRate)
985 {
986 if (lossRate < 1.0f)
987 {
988 return CallQuality::kCallQualityExcellent;
989 }
990 else if (lossRate < 3.0f)
991 {
992 return CallQuality::kCallQualityGood;
993 }
994 else if (lossRate < 5.0f)
995 {
996 return CallQuality::kCallQualityFair;
997 }
998 else if (lossRate < 8.0f)
999 {
1000 return CallQuality::kCallQualityPoor;
1001 }
1002 else
1003 {
1004 return CallQuality::kCallQualityBad;
1005 }
1006 }
1007
convertAudioCodecType(const int32_t codec,const int32_t bandwidth)1008 int32_t MediaQualityAnalyzer::convertAudioCodecType(const int32_t codec, const int32_t bandwidth)
1009 {
1010 switch (codec)
1011 {
1012 default:
1013 return CallQuality::AUDIO_QUALITY_NONE;
1014 case AudioConfig::CODEC_AMR:
1015 return CallQuality::AUDIO_QUALITY_AMR;
1016 case AudioConfig::CODEC_AMR_WB:
1017 return CallQuality::AUDIO_QUALITY_AMR_WB;
1018 case AudioConfig::CODEC_EVS:
1019 {
1020 switch (bandwidth)
1021 {
1022 default:
1023 case EvsParams::EVS_BAND_NONE:
1024 break;
1025 case EvsParams::EVS_NARROW_BAND:
1026 return CallQuality::AUDIO_QUALITY_EVS_NB;
1027 case EvsParams::EVS_WIDE_BAND:
1028 return CallQuality::AUDIO_QUALITY_EVS_WB;
1029 case EvsParams::EVS_SUPER_WIDE_BAND:
1030 return CallQuality::AUDIO_QUALITY_EVS_SWB;
1031 case EvsParams::EVS_FULL_BAND:
1032 return CallQuality::AUDIO_QUALITY_EVS_FB;
1033 }
1034 }
1035 }
1036
1037 return CallQuality::AUDIO_QUALITY_NONE;
1038 }