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 }