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 }