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