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 <RtcpXrEncoder.h>
18 #include <RtcpConfig.h>
19 #include <ImsMediaTrace.h>
20 #include <limits.h>
21 #include <cmath>
22 
RtcpXrEncoder()23 RtcpXrEncoder::RtcpXrEncoder()
24 {
25     mSsrc = 0;
26     mSamplingRate = 16;
27     mRoundTripDelay = 0;
28     mVoipLossCount = 0;
29     mVoipDiscardedCount = 0;
30     mVoipPktCount = 0;
31     mVoipLostCountInBurst = 0;
32     mJitterBufferNominal = 0;
33     mJitterBufferMax = 0;
34     mJitterBufferAbsMax = 0;
35     mVoipC11 = 0;
36     mVoipC13 = 0;
37     mVoipC14 = 0;
38     mVoipC22 = 0;
39     mVoipC23 = 0;
40     mVoipC33 = 0;
41     mVoipC31 = 0;
42     mVoipC32 = 0;
43 }
44 
~RtcpXrEncoder()45 RtcpXrEncoder::~RtcpXrEncoder() {}
46 
setSsrc(const uint32_t ssrc)47 void RtcpXrEncoder::setSsrc(const uint32_t ssrc)
48 {
49     mSsrc = ssrc;
50 }
51 
setSamplingRate(const uint32_t rate)52 void RtcpXrEncoder::setSamplingRate(const uint32_t rate)
53 {
54     IMLOGD1("[setSamplingRate] rate[%d]", rate);
55     mSamplingRate = rate;
56 }
57 
setRoundTripDelay(const uint32_t delay)58 void RtcpXrEncoder::setRoundTripDelay(const uint32_t delay)
59 {
60     IMLOGD1("[setRoundTripDelay] delay[%d]", delay);
61     mRoundTripDelay = delay;
62 }
63 
stackRxRtpStatus(const int32_t status,const uint32_t delay)64 void RtcpXrEncoder::stackRxRtpStatus(const int32_t status, const uint32_t delay)
65 {
66     bool packetLost = false;
67     bool packetDiscarded = false;
68 
69     if (status == kRtpStatusLost)
70     {
71         mVoipLossCount++;
72         packetLost = true;
73     }
74     else if (status == kRtpStatusLate || status == kRtpStatusDiscarded ||
75             status == kRtpStatusDuplicated)
76     {
77         mVoipDiscardedCount++;
78         packetDiscarded = true;
79     }
80 
81     if (!packetLost && !packetDiscarded)
82     {
83         mVoipPktCount++;
84     }
85     else
86     {
87         if (mVoipPktCount >= G_MIN_THRESHOLD)
88         {
89             if (mVoipLostCountInBurst == 1)
90             {
91                 mVoipC14++;
92             }
93             else
94             {
95                 mVoipC13++;
96             }
97 
98             mVoipLostCountInBurst = 1;
99             mVoipC11 += mVoipPktCount;
100         }
101         else
102         {
103             mVoipLostCountInBurst++;
104 
105             if (mVoipPktCount == 0)
106             {
107                 mVoipC33++;
108             }
109             else
110             {
111                 mVoipC23++;
112                 mVoipC22 += (mVoipPktCount - 1);
113             }
114         }
115 
116         mVoipPktCount = 0;
117     }
118 
119     if (status == kRtpStatusNormal && delay > mJitterBufferMax)
120     {
121         mJitterBufferMax = delay;
122     }
123 
124     IMLOGD_PACKET8(IM_PACKET_LOG_RTCP,
125             "[stackRxRtpStatus] lost[%d], discarded[%d], C11[%d], C13[%d], C14[%d], C22[%d], "
126             "C23[%d], C33[%d]",
127             packetLost, packetDiscarded, mVoipC11, mVoipC13, mVoipC14, mVoipC22, mVoipC23,
128             mVoipC33);
129 }
130 
setJitterBufferStatus(const uint32_t current,const uint32_t max)131 void RtcpXrEncoder::setJitterBufferStatus(const uint32_t current, const uint32_t max)
132 {
133     IMLOGD_PACKET2(
134             IM_PACKET_LOG_RTCP, "[setJitterBufferStatus] current[%d], max[%d]", current, max);
135     mJitterBufferNominal = current;
136     mJitterBufferAbsMax = max;
137 }
138 
createRtcpXrReport(const uint32_t rtcpXrReport,std::list<RtpPacket * > * packets,std::list<LostPacket * > * lostPackets,uint16_t beginSeq,uint16_t endSeq,uint8_t * data,uint32_t & size)139 bool RtcpXrEncoder::createRtcpXrReport(const uint32_t rtcpXrReport, std::list<RtpPacket*>* packets,
140         std::list<LostPacket*>* lostPackets, uint16_t beginSeq, uint16_t endSeq, uint8_t* data,
141         uint32_t& size)
142 {
143     size = 0;
144 
145     if (data == nullptr || rtcpXrReport == RtcpConfig::FLAG_RTCPXR_NONE)
146     {
147         return false;
148     }
149 
150     uint8_t* buffer = data;
151 
152     if (rtcpXrReport & RtcpConfig::FLAG_RTCPXR_STATISTICS_SUMMARY_REPORT_BLOCK)
153     {
154         tLossReport* lossReport = createLossAnalysisReport(packets, lostPackets, beginSeq, endSeq);
155         tJitterReport* jitterReport = createJitterAnalysisReport(packets, beginSeq, endSeq);
156         tTTLReport* ttlReport = createTTLAnalysisReport(packets, beginSeq, endSeq);
157         tDuplicateReport* duplicateReport =
158                 createDuplicateAnalysisReport(packets, beginSeq, endSeq);
159 
160         encodeStatisticSummeryReport(lossReport, jitterReport, ttlReport, duplicateReport, buffer);
161 
162         if (lossReport != nullptr)
163         {
164             delete lossReport;
165         }
166 
167         if (jitterReport != nullptr)
168         {
169             delete jitterReport;
170         }
171 
172         if (ttlReport != nullptr)
173         {
174             delete ttlReport;
175         }
176 
177         if (duplicateReport != nullptr)
178         {
179             delete duplicateReport;
180         }
181 
182         size += BLOCK_LENGTH_STATISTICS;
183     }
184 
185     if (rtcpXrReport & RtcpConfig::FLAG_RTCPXR_VOIP_METRICS_REPORT_BLOCK)
186     {
187         tVoIPMatricReport* voipReport = createVoIPMatricReport();
188         encodeVoipMetricReport(voipReport, buffer + size);
189         size += BLOCK_LENGTH_STATISTICS;
190 
191         if (voipReport != nullptr)
192         {
193             delete voipReport;
194         }
195     }
196 
197     IMLOGD_PACKET2(IM_PACKET_LOG_RTCP, "[createRtcpXrReport] rtcpXrReport[%d], size[%d]",
198             rtcpXrReport, size);
199 
200     return (size > 0);
201 }
202 
createLossAnalysisReport(std::list<RtpPacket * > * packets,std::list<LostPacket * > * lostPackets,uint16_t beginSeq,uint16_t endSeq)203 tLossReport* RtcpXrEncoder::createLossAnalysisReport(std::list<RtpPacket*>* packets,
204         std::list<LostPacket*>* lostPackets, uint16_t beginSeq, uint16_t endSeq)
205 {
206     tLossReport* report = new tLossReport();
207     report->beginSeq = beginSeq;
208     report->endSeq = endSeq;
209     report->numLostPackets = 0;
210     report->numPacketsReceived = 0;
211 
212     for (const auto& packet : *packets)
213     {
214         if (packet->seqNum >= beginSeq && packet->seqNum <= endSeq)
215         {
216             report->numPacketsReceived++;
217         }
218     }
219 
220     for (const auto& packet : *lostPackets)
221     {
222         if (packet->seqNum >= beginSeq && packet->seqNum <= endSeq)
223         {
224             for (int32_t i = 0; i < packet->numLoss; i++)
225             {
226                 if (packet->seqNum + i > endSeq)
227                 {
228                     break;
229                 }
230 
231                 report->numLostPackets++;
232             }
233         }
234     }
235 
236     IMLOGD_PACKET4(IM_PACKET_LOG_RTCP,
237             "[createLossAnalysisReport] begin[%d], end[%d], lost[%d], received[%d]", beginSeq,
238             endSeq, report->numLostPackets, report->numPacketsReceived);
239 
240     return report;
241 }
242 
createJitterAnalysisReport(std::list<RtpPacket * > * packets,uint16_t beginSeq,uint16_t endSeq)243 tJitterReport* RtcpXrEncoder::createJitterAnalysisReport(
244         std::list<RtpPacket*>* packets, uint16_t beginSeq, uint16_t endSeq)
245 {
246     tJitterReport* report = new tJitterReport();
247     report->beginSeq = beginSeq;
248     report->endSeq = endSeq;
249 
250     report->minJitter = INT_MAX;
251     report->maxJitter = INT_MIN;
252     int64_t sumJitter = 0;
253     int64_t sumJitterSqr = 0;
254     uint32_t count = 0;
255 
256     for (const auto& packet : *packets)
257     {
258         if (packet->seqNum >= beginSeq && packet->seqNum <= endSeq)
259         {
260             // change units from ms to timestamp
261             int32_t jitter = packet->jitter * mSamplingRate;
262             // min
263             if (jitter < report->minJitter)
264             {
265                 report->minJitter = jitter;
266             }
267 
268             // max
269             if (jitter > report->maxJitter)
270             {
271                 report->maxJitter = jitter;
272             }
273 
274             sumJitter += jitter;
275             sumJitterSqr += jitter * jitter;
276             count++;
277         }
278     }
279 
280     count == 0 ? report->meanJitter = 0 : report->meanJitter = (double)sumJitter / count;
281 
282     report->devJitter = (int32_t)sqrt((double)(sumJitterSqr) / count -
283             (double)(sumJitter) / count * (double)(sumJitter) / count);
284 
285     IMLOGD6("[createJitterAnalysisReport] begin[%d], end[%d], min[%d], max[%d], mean[%d], dev[%d]",
286             beginSeq, endSeq, report->minJitter, report->maxJitter, report->meanJitter,
287             report->devJitter);
288 
289     return report;
290 }
291 
createTTLAnalysisReport(std::list<RtpPacket * > * packets,uint16_t beginSeq,uint16_t endSeq)292 tTTLReport* RtcpXrEncoder::createTTLAnalysisReport(
293         std::list<RtpPacket*>* packets, uint16_t beginSeq, uint16_t endSeq)
294 {
295     (void)packets;
296     tTTLReport* report = new tTTLReport();
297     report->beginSeq = beginSeq;
298     report->endSeq = endSeq;
299 
300     // TODO: add implementation
301     report->minTTL = 0;
302     report->meanTTL = 0;
303     report->maxTTL = 0;
304     report->devTTL = 0;
305 
306     IMLOGD6("[createTTLAnalysisReport] begin[%d], end[%d], min[%d], max[%d], mean[%d], dev[%d]",
307             beginSeq, endSeq, report->minTTL, report->maxTTL, report->meanTTL, report->devTTL);
308 
309     return report;
310 }
311 
createDuplicateAnalysisReport(std::list<RtpPacket * > * packets,uint16_t beginSeq,uint16_t endSeq)312 tDuplicateReport* RtcpXrEncoder::createDuplicateAnalysisReport(
313         std::list<RtpPacket*>* packets, uint16_t beginSeq, uint16_t endSeq)
314 {
315     tDuplicateReport* report = new tDuplicateReport();
316     report->beginSeq = beginSeq;
317     report->endSeq = endSeq;
318     report->numDuplicatedPackets = 0;
319     report->numPacketsReceived = 0;
320 
321     for (const auto& packet : *packets)
322     {
323         if (packet->seqNum >= beginSeq && packet->seqNum <= endSeq)
324         {
325             if (packet->status == kRtpStatusDuplicated)
326             {
327                 report->numDuplicatedPackets++;
328             }
329 
330             report->numPacketsReceived++;
331         }
332     }
333 
334     IMLOGD4("[createDuplicateAnalysisReport] begin[%d], end[%d], dup[%d], received[%d]", beginSeq,
335             endSeq, report->numDuplicatedPackets, report->numPacketsReceived);
336 
337     return report;
338 }
339 
createVoIPMatricReport()340 tVoIPMatricReport* RtcpXrEncoder::createVoIPMatricReport()
341 {
342     double p32 = 0;
343     double p23 = 0;
344 
345     /** consider it is gap in case of (b) the period from the end of the last burst to either the
346      time of the report in RFC3611 4.7.2. */
347     if (mVoipPktCount != 0)
348     {
349         mVoipC11 += mVoipPktCount;
350         mVoipPktCount = 0;
351     }
352 
353     // Calculate additional transition counts.
354     mVoipC31 = mVoipC13;
355     mVoipC32 = mVoipC23;
356     uint32_t cTotal =
357             mVoipC11 + mVoipC14 + mVoipC13 + mVoipC22 + mVoipC23 + mVoipC31 + mVoipC32 + mVoipC33;
358 
359     // Calculate burst and densities.
360     if (mVoipC32 == 0 || (mVoipC31 + mVoipC32 + mVoipC33) == 0)
361     {
362         p32 = 0;
363     }
364     else
365     {
366         p32 = static_cast<double>(mVoipC32) / (mVoipC31 + mVoipC32 + mVoipC33);
367     }
368 
369     if ((mVoipC22 + mVoipC23) == 0)
370     {
371         p23 = 0;
372     }
373     else
374     {
375         p23 = 1 - static_cast<double>(mVoipC22) / (mVoipC22 + mVoipC23);
376     }
377 
378     IMLOGD3("[createVoIPMatricReport] cTotal[%d], P23[%lf], P32[%lf]", cTotal, p23, p32);
379 
380     tVoIPMatricReport* report = new tVoIPMatricReport();
381     report->ssrc = mSsrc;
382     /* calculate loss and discard rates */
383     report->lossRate = 255 * (double)mVoipLossCount / cTotal;
384     report->discardRate = 255 * (double)mVoipDiscardedCount / cTotal;
385     report->burstDensity = 255 * (double)p23 / (p23 + p32);
386     report->gapDensity = 255 * (double)mVoipC14 / (mVoipC11 + mVoipC14);
387     // Calculate burst and gap durations in ms
388     uint32_t denum = 0;
389     mVoipC13 == 0 ? denum = 1 : denum = mVoipC13;
390     report->gapDuration = (mVoipC11 + mVoipC14 + mVoipC13) * 20 / denum;
391     report->burstDuration = cTotal * 20 / denum - report->gapDuration;
392     // get it from the rtp stack
393     report->roundTripDelay = mRoundTripDelay;
394     // not implemented yet
395     report->endSystemDelay = 0;
396     // sound signal quality - not support
397     report->signalLevel = 0;
398     report->noiseLevel = 0;
399     report->rerl = 0;
400     report->gMin = G_MIN_THRESHOLD;
401     // call quaility - not support
402     report->rFactor = 0;
403     report->extRFactor = 0;
404     report->rxConfig = 127;
405     report->jitterBufferNominal = mJitterBufferNominal;
406     report->jitterBufferMaximum = mJitterBufferMax;
407     report->jitterBufferAbsMaximum = mJitterBufferAbsMax;
408 
409     IMLOGD6("[createVoIPMatricReport] lossRate[%d], discardRate[%d], burstDensity[%d], "
410             "gapDensity[%d], gapDuration[%d], burstDuration[%d]",
411             report->lossRate, report->discardRate, report->burstDensity, report->gapDensity,
412             report->gapDuration, report->burstDuration);
413     IMLOGD3("[createVoIPMatricReport] JBNominal[%d], JBMax[%d], JBAbsMaximum[%d]",
414             report->jitterBufferNominal, report->jitterBufferMaximum,
415             report->jitterBufferAbsMaximum);
416     return report;
417 }
418 
encodeStatisticSummeryReport(tLossReport * lossReport,tJitterReport * jitterReport,tTTLReport * ttlReport,tDuplicateReport * duplicateReport,uint8_t * data)419 void RtcpXrEncoder::encodeStatisticSummeryReport(tLossReport* lossReport,
420         tJitterReport* jitterReport, tTTLReport* ttlReport, tDuplicateReport* duplicateReport,
421         uint8_t* data)
422 {
423     /** The Statistics Summary Report* block format
424      0                   1                   2                   3
425      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
426     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
427     | Block Type = 6|L|D|J|ToH|rsvd.|      block length = 9         |
428     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
429     | SSRC of source                                                |
430     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
431     | begin_seq                     | end_seq                       |
432     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
433     | lost_packets                                                  |
434     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
435     | dup_packets                                                   |
436     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
437     | min_jitter                                                    |
438     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
439     | max_jitter                                                    |
440     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
441     | mean_jitter                                                   |
442     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
443     | dev_jitter                                                    |
444     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
445     | min_ttl_or_hl | max_ttl_or_hl |mean_ttl_or_hl | dev_ttl_or_hl |
446     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
447 */
448     if (data == nullptr || lossReport == nullptr || jitterReport == nullptr ||
449             ttlReport == nullptr || duplicateReport == nullptr)
450     {
451         return;
452     }
453 
454     memset(data, 0, BLOCK_LENGTH_STATISTICS);
455     mBitWriter.SetBuffer(data, BLOCK_LENGTH_STATISTICS);
456     mBitWriter.Write(6, 8);
457     // flag of lost and duplicated packets --> always set
458     mBitWriter.Write(1, 1);
459     mBitWriter.Write(1, 1);
460     // flag of jitter
461     mBitWriter.Write(1, 1);
462 
463     // TTL and HL : 0 - not using, 1 - IPv4, 2 - IPv6, 3 must not used
464     if (ttlReport->ipVersion == -1)
465     {
466         mBitWriter.Write(0, 2);
467     }
468     else if (ttlReport->ipVersion == IPV4)
469     {
470         mBitWriter.Write(1, 2);
471     }
472     else
473     {
474         mBitWriter.Write(2, 2);
475     }
476 
477     // padding
478     mBitWriter.Write(0, 3);
479     // block length
480     mBitWriter.Write(9, 16);
481     // ssrc of source
482     mBitWriter.WriteByteBuffer(mSsrc);
483     // sequence number
484     mBitWriter.Write(lossReport->beginSeq, 16);
485     mBitWriter.Write(lossReport->endSeq, 16);
486     // lost packets
487     mBitWriter.WriteByteBuffer(lossReport->numLostPackets);
488     // dup packets
489     mBitWriter.WriteByteBuffer(duplicateReport->numDuplicatedPackets);
490     IMLOGD6("[encodeStatisticSummeryReport] beginSeq[%hu], endSeq[%hu], nMinJitter[%d], "
491             "nMaxJitter[%d], nMeanJitter[%d], nDevJitter[%d]",
492             lossReport->beginSeq, lossReport->endSeq, jitterReport->minJitter,
493             jitterReport->maxJitter, jitterReport->meanJitter, jitterReport->devJitter);
494     // min, max, mean, dev jitter
495     mBitWriter.WriteByteBuffer(jitterReport->minJitter);
496     mBitWriter.WriteByteBuffer(jitterReport->maxJitter);
497     mBitWriter.WriteByteBuffer(jitterReport->meanJitter);
498     mBitWriter.WriteByteBuffer(jitterReport->devJitter);
499     IMLOGD4("[encodeStatisticSummeryReport] nMinTTL[%d], nMaxTTL[%d], "
500             "nMeanTTL[%d], nDevTTL[%d]",
501             ttlReport->minTTL, ttlReport->maxTTL, ttlReport->meanTTL, ttlReport->devTTL);
502     // min, max, mean, dev ttl/hl
503     mBitWriter.Write(ttlReport->minTTL, 8);
504     mBitWriter.Write(ttlReport->maxTTL, 8);
505     mBitWriter.Write(ttlReport->meanTTL, 8);
506     mBitWriter.Write(ttlReport->devTTL, 8);
507 }
508 
encodeVoipMetricReport(tVoIPMatricReport * report,uint8_t * data)509 void RtcpXrEncoder::encodeVoipMetricReport(tVoIPMatricReport* report, uint8_t* data)
510 {
511     if (report == nullptr || data == nullptr)
512     {
513         return;
514     }
515 
516     /** The VoIP Matircs Report block format
517      0                   1                   2                   3
518      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
519     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
520     |      BT=7     |   reserved    |      block length = 8         |
521     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
522     | SSRC of source                                                |
523     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
524     | loss rate     | discard rate  | burst density | gap density   |
525     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
526     | burst duration                | gap duration                  |
527     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
528     | round trip delay              | end system delay              |
529     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
530     | signal level  | noise level   | RERL          | Gmin          |
531     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
532     | R factor      | ext. R factor | MOS-LQ        | MOS-CQ        |
533     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
534     | RX config     | reserved      | JB nominal                    |
535     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
536     | JB maximum                    | JB abs max                    |
537     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
538 */
539     IMLOGD0("[encodeVoipMetricReport]");
540 
541     memset(data, 0, BLOCK_LENGTH_VOIP_METRICS);
542     mBitWriter.SetBuffer(data, BLOCK_LENGTH_VOIP_METRICS);
543     mBitWriter.Write(7, 8);
544     mBitWriter.Write(0, 8);
545     // block length
546     mBitWriter.Write(8, 16);
547     // ssrc of source
548     mBitWriter.WriteByteBuffer(report->ssrc);
549     mBitWriter.Write(report->lossRate, 8);
550     mBitWriter.Write(report->discardRate, 8);
551     mBitWriter.Write(report->burstDensity, 8);
552     mBitWriter.Write(report->gapDensity, 8);
553     mBitWriter.Write(report->burstDuration, 16);
554     mBitWriter.Write(report->gapDuration, 16);
555     mBitWriter.Write(report->roundTripDelay, 16);
556     mBitWriter.Write(report->endSystemDelay, 16);
557     // signal level - 127 unavailable
558     mBitWriter.Write(127, 8);
559     mBitWriter.Write(127, 8);
560     mBitWriter.Write(127, 8);
561     mBitWriter.Write(report->gMin, 8);
562     // R factor - 127 unavailable
563     mBitWriter.Write(127, 8);
564     mBitWriter.Write(127, 8);
565     // MOS - 127 unavailable
566     mBitWriter.Write(127, 8);
567     mBitWriter.Write(127, 8);
568     // receiver configuration byte(Rx Config)
569     mBitWriter.Write(report->rxConfig, 8);
570     mBitWriter.Write(0, 8);
571     // Jitter Buffer - milliseconds
572     mBitWriter.Write(report->jitterBufferNominal, 16);
573     mBitWriter.Write(report->jitterBufferMaximum, 16);
574     mBitWriter.Write(report->jitterBufferAbsMaximum, 16);
575 }
576