/src/rtpproxy/src/rtp_analyze.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2009 Sippy Software, Inc., http://www.sippysoft.com |
3 | | * All rights reserved. |
4 | | * |
5 | | * Redistribution and use in source and binary forms, with or without |
6 | | * modification, are permitted provided that the following conditions |
7 | | * are met: |
8 | | * 1. Redistributions of source code must retain the above copyright |
9 | | * notice, this list of conditions and the following disclaimer. |
10 | | * 2. Redistributions in binary form must reproduce the above copyright |
11 | | * notice, this list of conditions and the following disclaimer in the |
12 | | * documentation and/or other materials provided with the distribution. |
13 | | * |
14 | | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
15 | | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
16 | | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
17 | | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
18 | | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
19 | | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
20 | | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
21 | | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
22 | | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
23 | | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
24 | | * SUCH DAMAGE. |
25 | | * |
26 | | */ |
27 | | |
28 | | #include <sys/types.h> |
29 | | #include <stdint.h> |
30 | | #include <stdio.h> |
31 | | #include <stdlib.h> |
32 | | #include <string.h> |
33 | | |
34 | | #include "config.h" |
35 | | |
36 | | #include "rtpp_types.h" |
37 | | #include "rtpp_ssrc.h" |
38 | | #include "rtpa_stats.h" |
39 | | #include "rtpp_debug.h" |
40 | | #include "rtpp_mallocs.h" |
41 | | #include "rtpp_log.h" |
42 | | #include "rtpp_log_obj.h" |
43 | | #include "rtp_info.h" |
44 | | #include "rtp.h" |
45 | | #include "rtp_analyze.h" |
46 | | #include "rtpp_math.h" |
47 | | #include "rtpp_codeptr.h" |
48 | | #include "rtpp_refcnt.h" |
49 | | #include "rtpp_ringbuf.h" |
50 | | |
51 | | #define FIX_TIMESTAMP_RESET 1 |
52 | | #define DEBUG_TIMESTAMP_RESET 1 |
53 | | |
54 | | struct rtp_analyze_jdata; |
55 | | |
56 | | struct rtp_analyze_jitter { |
57 | | int jdlen; |
58 | | double jmax_acum; |
59 | | double jtotal_acum; |
60 | | long long jvcount_acum; |
61 | | long long pcount_acum; |
62 | | struct rtp_analyze_jdata *first; |
63 | | }; |
64 | | |
65 | | struct rtp_analyze_jdata_ssrc { |
66 | | uint64_t prev_rtime_ts; |
67 | | uint32_t prev_ts; |
68 | | #if FIX_TIMESTAMP_RESET |
69 | | long long ts_rcount; |
70 | | long long ts_jcount; |
71 | | #endif |
72 | | long long ts_dcount; |
73 | | long long seq_rcount; |
74 | | double jlast; |
75 | | double jmax; |
76 | | double jtotal; |
77 | | long long pcount; |
78 | | }; |
79 | | |
80 | | struct rtp_analyze_jdata { |
81 | | struct rtp_analyze_jdata_ssrc jss; |
82 | | struct rtpp_ringbuf *ts_dedup; |
83 | | struct rtpp_ssrc ssrc; |
84 | | struct rtp_analyze_jdata *next; |
85 | | }; |
86 | | |
87 | | static double |
88 | | rtp_ts2dtime(int ts_rate, uint32_t ts) |
89 | 0 | { |
90 | |
|
91 | 0 | return ((double)ts) / ((double)ts_rate); |
92 | 0 | } |
93 | | |
94 | | static uint64_t |
95 | | rtp_dtime2time_ts64(int ts_rate, double dtime) |
96 | 0 | { |
97 | |
|
98 | 0 | return (uint64_t)(dtime * (double)ts_rate); |
99 | 0 | } |
100 | | |
101 | | /* rlog can be null in this context, when compiled for the extractaudio context */ |
102 | | #define LOGD_IF_NOT_NULL(log, args...) \ |
103 | 0 | if ((log) != NULL) { \ |
104 | 0 | RTPP_LOG((log), RTPP_LOG_DBUG, ## args); \ |
105 | 0 | } |
106 | | #define LOGI_IF_NOT_NULL(log, args...) \ |
107 | | if ((log) != NULL) { \ |
108 | | RTPP_LOG((log), RTPP_LOG_INFO, ## args); \ |
109 | | } |
110 | | |
111 | 0 | #define RTP_NORMAL 0 |
112 | 0 | #define RTP_SEQ_RESET 1 |
113 | 0 | #define RTP_SSRC_RESET 2 |
114 | | |
115 | 0 | #define RTPC_JDATA_MAX 10 |
116 | | |
117 | | static void |
118 | | update_jitter_stats(struct rtp_analyze_jdata *jdp, struct rtp_info *rinfo, |
119 | | double rtime, int hint, struct rtpp_log *rlog) |
120 | 0 | { |
121 | 0 | int64_t dval; |
122 | 0 | uint64_t rtime_ts, wrcorr; |
123 | 0 | #if FIX_TIMESTAMP_RESET |
124 | 0 | int64_t rtime_ts_delta; |
125 | 0 | #endif |
126 | |
|
127 | 0 | rtime_ts = rtp_dtime2time_ts64(rinfo->rtp_profile->ts_rate, rtime); |
128 | 0 | if (rinfo->rtp_profile->pt_kind == RTP_PTK_AUDIO && |
129 | 0 | CALL_SMETHOD(jdp->ts_dedup, locate, &rinfo->ts) >= 0) { |
130 | 0 | jdp->jss.ts_dcount++; |
131 | 0 | if (jdp->jss.pcount == 1) { |
132 | 0 | jdp->jss.prev_rtime_ts = rtime_ts; |
133 | 0 | jdp->jss.prev_ts = rinfo->ts; |
134 | 0 | } |
135 | 0 | return; |
136 | 0 | } |
137 | 0 | if (jdp->jss.prev_rtime_ts != 0) { |
138 | 0 | if (hint == RTP_SEQ_RESET) { |
139 | 0 | jdp->jss.seq_rcount++; |
140 | 0 | goto saveandexit; |
141 | 0 | } |
142 | 0 | #if FIX_TIMESTAMP_RESET |
143 | 0 | rtime_ts_delta = jdp->jss.prev_rtime_ts - rtime_ts; |
144 | 0 | #endif |
145 | 0 | if (jdp->jss.prev_ts > rinfo->ts) { |
146 | 0 | if (((uint64_t)jdp->jss.prev_ts - (uint64_t)rinfo->ts) > ((uint32_t)1 << 31)) { |
147 | | /* Normal case, timestamp wrap */ |
148 | 0 | wrcorr = (uint64_t)1 << 32; |
149 | 0 | #if FIX_TIMESTAMP_RESET |
150 | 0 | } else if (rtime_ts_delta != 0 && ((uint64_t)jdp->jss.prev_ts - (uint64_t)rinfo->ts) > |
151 | 0 | ABS(rtime_ts_delta) * 50) { |
152 | | /* Timestamp reset */ |
153 | 0 | #if DEBUG_TIMESTAMP_RESET |
154 | 0 | LOGD_IF_NOT_NULL(rlog, "update_jitter_stats() : timestamp reset : " SSRC_FMT ", %lld, %llu", |
155 | 0 | rinfo->ssrc, T_printf(ABS(rtime_ts_delta)), |
156 | 0 | T_printf((uint64_t)jdp->jss.prev_ts - (uint64_t)rinfo->ts)); |
157 | 0 | #endif |
158 | 0 | jdp->jss.ts_rcount++; |
159 | 0 | goto saveandexit; |
160 | 0 | #endif |
161 | 0 | } else { |
162 | 0 | wrcorr = 0; |
163 | 0 | } |
164 | 0 | } else { |
165 | 0 | # if FIX_TIMESTAMP_RESET |
166 | 0 | if (rtime_ts_delta != 0 && ((uint64_t)rinfo->ts - (uint64_t)jdp->jss.prev_ts) > |
167 | 0 | ABS(rtime_ts_delta) * 1024) { |
168 | | /* Timestamp jump */ |
169 | 0 | #if DEBUG_TIMESTAMP_RESET |
170 | 0 | LOGD_IF_NOT_NULL(rlog,"update_jitter_stats() : timestamp jump : " SSRC_FMT ", %lld, %lld", |
171 | 0 | rinfo->ssrc, T_printf(ABS(rtime_ts_delta)), |
172 | 0 | T_printf((uint64_t)rinfo->ts - (uint64_t)jdp->jss.prev_ts)); |
173 | 0 | #endif |
174 | 0 | jdp->jss.ts_jcount++; |
175 | 0 | goto saveandexit; |
176 | 0 | } |
177 | 0 | #endif |
178 | 0 | wrcorr = 0; |
179 | 0 | } |
180 | 0 | dval = (rtime_ts - ((uint64_t)rinfo->ts + wrcorr)) - |
181 | 0 | (jdp->jss.prev_rtime_ts - (uint64_t)jdp->jss.prev_ts); |
182 | 0 | #if DEBUG_TIMESTAMP_RESET |
183 | 0 | if (dval > 10000) |
184 | 0 | LOGD_IF_NOT_NULL(rlog, "##### LARGE VALUE #####" SSRC_FMT ",%lld,%llu,%u,%llu,%u,%llu,%lld", |
185 | 0 | rinfo->ssrc, jdp->jss.pcount, T_printf(rtime_ts), rinfo->ts, |
186 | 0 | T_printf(jdp->jss.prev_rtime_ts), jdp->jss.prev_ts, |
187 | 0 | T_printf(wrcorr), T_printf(dval)); |
188 | 0 | #endif |
189 | 0 | jdp->jss.jlast = jdp->jss.jlast + (double)(ABS(dval) - jdp->jss.jlast) / 16.0; |
190 | 0 | if (jdp->jss.jlast > jdp->jss.jmax) { |
191 | 0 | jdp->jss.jmax = jdp->jss.jlast; |
192 | 0 | } |
193 | 0 | jdp->jss.jtotal += jdp->jss.jlast; |
194 | 0 | } |
195 | 0 | RTPP_DBGCODE(analyze > 1) { |
196 | 0 | LOGD_IF_NOT_NULL(rlog, SSRC_FMT ",%lld,%llu,%u,%f", rinfo->ssrc, jdp->jss.pcount, |
197 | 0 | T_printf(rtime_ts), rinfo->ts, jdp->jss.jlast); |
198 | 0 | } |
199 | 0 | jdp->jss.pcount++; |
200 | 0 | saveandexit: |
201 | 0 | if (rinfo->rtp_profile->pt_kind == RTP_PTK_AUDIO) { |
202 | 0 | CALL_SMETHOD(jdp->ts_dedup, push, &rinfo->ts); |
203 | 0 | } |
204 | 0 | jdp->jss.prev_rtime_ts = rtime_ts; |
205 | 0 | jdp->jss.prev_ts = rinfo->ts; |
206 | 0 | } |
207 | | |
208 | | static struct rtp_analyze_jitter *rtp_analyze_jt_ctor(void); |
209 | | |
210 | | int |
211 | | rtpp_stats_init(struct rtpp_session_stat *stat) |
212 | 0 | { |
213 | |
|
214 | 0 | memset(stat, '\0', sizeof(struct rtpp_session_stat)); |
215 | 0 | stat->jdata = rtp_analyze_jt_ctor(); |
216 | 0 | if (stat->jdata == NULL) { |
217 | 0 | return (-1); |
218 | 0 | } |
219 | 0 | stat->last.pt = PT_UNKN; |
220 | 0 | return (0); |
221 | 0 | } |
222 | | |
223 | | static struct rtp_analyze_jdata * |
224 | | rtp_analyze_jdata_ctor() |
225 | 0 | { |
226 | 0 | struct rtp_analyze_jdata *jdp; |
227 | |
|
228 | 0 | jdp = rtpp_zmalloc(sizeof(*jdp)); |
229 | 0 | if (jdp == NULL) { |
230 | 0 | goto e0; |
231 | 0 | } |
232 | 0 | jdp->ts_dedup = rtpp_ringbuf_ctor(sizeof(jdp->jss.prev_ts), 10); |
233 | 0 | if (jdp->ts_dedup == NULL) { |
234 | 0 | goto e1; |
235 | 0 | } |
236 | 0 | return (jdp); |
237 | | |
238 | 0 | e1: |
239 | 0 | free(jdp); |
240 | 0 | e0: |
241 | 0 | return (NULL); |
242 | 0 | } |
243 | | |
244 | | static struct rtp_analyze_jitter * |
245 | | rtp_analyze_jt_ctor() |
246 | 0 | { |
247 | 0 | struct rtp_analyze_jitter *jp; |
248 | |
|
249 | 0 | jp = rtpp_zmalloc(sizeof(*jp)); |
250 | 0 | if (jp == NULL) { |
251 | 0 | goto e0; |
252 | 0 | } |
253 | 0 | jp->first = rtp_analyze_jdata_ctor(); |
254 | 0 | if (jp->first == NULL) { |
255 | 0 | goto e1; |
256 | 0 | } |
257 | 0 | jp->jdlen = 1; |
258 | 0 | return (jp); |
259 | | |
260 | 0 | e1: |
261 | 0 | free(jp); |
262 | 0 | e0: |
263 | 0 | return (NULL); |
264 | 0 | } |
265 | | |
266 | | |
267 | | static void rtp_analyze_jt_destroy(struct rtp_analyze_jitter *); |
268 | | |
269 | | void |
270 | | rtpp_stats_destroy(struct rtpp_session_stat *stat) |
271 | 0 | { |
272 | |
|
273 | 0 | rtp_analyze_jt_destroy(stat->jdata); |
274 | 0 | } |
275 | | |
276 | | static void |
277 | | rtp_analyze_jt_destroy(struct rtp_analyze_jitter *jp) |
278 | 0 | { |
279 | 0 | struct rtp_analyze_jdata *jdp, *jdp_next; |
280 | |
|
281 | 0 | for (jdp = jp->first; jdp != NULL; jdp = jdp_next) { |
282 | 0 | jdp_next = jdp->next; |
283 | 0 | RTPP_OBJ_DECREF(jdp->ts_dedup); |
284 | 0 | free(jdp); |
285 | 0 | jp->jdlen -= 1; |
286 | 0 | } |
287 | 0 | RTPP_DBG_ASSERT(jp->jdlen == 0); |
288 | 0 | free(jp); |
289 | 0 | } |
290 | | |
291 | | static struct rtp_analyze_jdata * |
292 | | jdata_by_ssrc(struct rtp_analyze_jitter *jp, uint32_t ssrc) |
293 | 0 | { |
294 | 0 | struct rtp_analyze_jdata *rjdp, *jdp_last, *jdp_prelast; |
295 | |
|
296 | 0 | if (jp->first->ssrc.inited == 0) { |
297 | 0 | jp->first->ssrc.val = ssrc; |
298 | 0 | jp->first->ssrc.inited = 1; |
299 | 0 | return (jp->first); |
300 | 0 | } |
301 | | |
302 | 0 | jdp_last = jdp_prelast = NULL; |
303 | 0 | for (rjdp = jp->first; rjdp != NULL; rjdp = rjdp->next) { |
304 | 0 | if (rjdp->ssrc.val == ssrc) { |
305 | 0 | return (rjdp); |
306 | 0 | } |
307 | 0 | jdp_prelast = jdp_last; |
308 | 0 | jdp_last = rjdp; |
309 | 0 | } |
310 | | |
311 | 0 | if (jp->jdlen == RTPC_JDATA_MAX) { |
312 | | /* Re-use the last per-ssrc data */ |
313 | 0 | rjdp = jdp_last; |
314 | 0 | if (jdp_prelast != NULL) { |
315 | 0 | RTPP_DBG_ASSERT(jdp_prelast->next == jdp_last); |
316 | 0 | jdp_prelast->next = NULL; |
317 | 0 | } else { |
318 | 0 | jp->first = NULL; |
319 | 0 | } |
320 | 0 | CALL_SMETHOD(rjdp->ts_dedup, flush); |
321 | 0 | if (rjdp->jss.pcount >= 2) { |
322 | 0 | if (jp->jmax_acum < rjdp->jss.jmax) { |
323 | 0 | jp->jmax_acum = rjdp->jss.jmax; |
324 | 0 | } |
325 | 0 | jp->jtotal_acum += rjdp->jss.jtotal; |
326 | 0 | jp->jvcount_acum += rjdp->jss.pcount - 1; |
327 | 0 | jp->pcount_acum += rjdp->jss.pcount; |
328 | 0 | } |
329 | 0 | memset(&rjdp->jss, '\0', sizeof(rjdp->jss)); |
330 | 0 | RTPP_DBG_ASSERT(rjdp->ssrc.inited == 1); |
331 | 0 | } else { |
332 | | /* Allocate per-ssrc data */ |
333 | 0 | rjdp = rtp_analyze_jdata_ctor(); |
334 | 0 | if (rjdp == NULL) { |
335 | 0 | return (NULL); |
336 | 0 | } |
337 | 0 | rjdp->ssrc.inited = 1; |
338 | 0 | jp->jdlen += 1; |
339 | 0 | } |
340 | 0 | rjdp->ssrc.val = ssrc; |
341 | 0 | rjdp->next = jp->first; |
342 | 0 | jp->first = rjdp; |
343 | 0 | return (rjdp); |
344 | 0 | } |
345 | | |
346 | | enum update_rtpp_stats_rval |
347 | | update_rtpp_stats(struct rtpp_log *rlog, struct rtpp_session_stat *stat, rtp_hdr_t *header, |
348 | | struct rtp_info *rinfo, double rtime) |
349 | 0 | { |
350 | 0 | uint32_t seq; |
351 | 0 | uint16_t idx; |
352 | 0 | uint32_t mask; |
353 | 0 | const struct rtp_profile *rpp; |
354 | 0 | struct rtp_analyze_jdata *jdp; |
355 | |
|
356 | 0 | rpp = rinfo->rtp_profile; |
357 | 0 | jdp = jdata_by_ssrc(stat->jdata, rinfo->ssrc); |
358 | 0 | if (jdp == NULL) |
359 | 0 | return (UPDATE_ERR); |
360 | 0 | if (stat->ssrc_changes == 0) { |
361 | 0 | RTPP_DBG_ASSERT(stat->last.pcount == 0); |
362 | 0 | RTPP_DBG_ASSERT(stat->psent == 0); |
363 | 0 | RTPP_DBG_ASSERT(stat->precvd == 0); |
364 | 0 | stat->last.ssrc.val = rinfo->ssrc; |
365 | 0 | stat->last.ssrc.inited = 1; |
366 | 0 | stat->last.max_seq = stat->last.min_seq = rinfo->seq; |
367 | 0 | stat->last.base_ts = rinfo->ts; |
368 | 0 | stat->last.base_rtime = rtime; |
369 | 0 | stat->last.pcount = 1; |
370 | 0 | stat->ssrc_changes = 1; |
371 | 0 | idx = (rinfo->seq % 131072) >> 5; |
372 | 0 | stat->last.seen[idx] |= (uint32_t)1 << (rinfo->seq & 31); |
373 | 0 | stat->last.seq = rinfo->seq; |
374 | 0 | if (rpp->ts_rate > 0) { |
375 | 0 | update_jitter_stats(jdp, rinfo, rtime, RTP_NORMAL, rlog); |
376 | 0 | } |
377 | 0 | return (UPDATE_OK); |
378 | 0 | } |
379 | 0 | RTPP_DBG_ASSERT(stat->last.ssrc.inited == 1); |
380 | 0 | if (stat->last.ssrc.val != rinfo->ssrc) { |
381 | 0 | update_rtpp_totals(stat, stat); |
382 | 0 | stat->last.duplicates = 0; |
383 | 0 | memset(stat->last.seen, '\0', sizeof(stat->last.seen)); |
384 | | #if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) |
385 | | LOGI_IF_NOT_NULL(rlog, "SSRC changed from " SSRC_FMT "/%d to " |
386 | | SSRC_FMT "/%d", stat->last.ssrc.val, stat->last.seq, rinfo->ssrc, |
387 | | rinfo->seq); |
388 | | #endif |
389 | 0 | stat->last.ssrc.val = rinfo->ssrc; |
390 | 0 | stat->last.max_seq = stat->last.min_seq = rinfo->seq; |
391 | 0 | stat->last.base_ts = rinfo->ts; |
392 | 0 | stat->last.base_rtime = rtime; |
393 | 0 | stat->last.pcount = 1; |
394 | 0 | stat->ssrc_changes += 1; |
395 | 0 | if ((stat->psent > 0 || stat->precvd > 0) && rlog != NULL) { |
396 | 0 | LOGD_IF_NOT_NULL(rlog, SSRC_FMT "/%d: ssrc_changes=%u, psent=%u, precvd=%u", |
397 | 0 | rinfo->ssrc, rinfo->seq, stat->ssrc_changes, stat->psent, stat->precvd); |
398 | 0 | } |
399 | 0 | idx = (rinfo->seq % 131072) >> 5; |
400 | 0 | stat->last.seen[idx] |= (uint32_t)1 << (rinfo->seq & 31); |
401 | 0 | stat->last.seq = rinfo->seq; |
402 | 0 | if (rpp->ts_rate > 0) { |
403 | 0 | update_jitter_stats(jdp, rinfo, rtime, RTP_SSRC_RESET, rlog); |
404 | 0 | } |
405 | 0 | return (UPDATE_SSRC_CHG); |
406 | 0 | } |
407 | 0 | seq = rinfo->seq + stat->last.seq_offset; |
408 | 0 | if (header->mbt && (seq < stat->last.max_seq && (stat->last.max_seq & 0xffff) != 65535)) { |
409 | 0 | LOGD_IF_NOT_NULL(rlog, SSRC_FMT "/%d: seq reset last->max_seq=%u, seq=%u, m=%u", |
410 | 0 | rinfo->ssrc, rinfo->seq, stat->last.max_seq, seq, header->mbt); |
411 | | /* Seq reset has happened. Treat it as a ssrc change */ |
412 | 0 | update_rtpp_totals(stat, stat); |
413 | 0 | stat->last.duplicates = 0; |
414 | 0 | memset(stat->last.seen, '\0', sizeof(stat->last.seen)); |
415 | 0 | stat->last.max_seq = stat->last.min_seq = seq; |
416 | 0 | stat->last.base_ts = rinfo->ts; |
417 | 0 | stat->last.base_rtime = rtime; |
418 | 0 | stat->last.pcount = 1; |
419 | 0 | stat->seq_res_count += 1; |
420 | 0 | idx = (seq % 131072) >> 5; |
421 | 0 | stat->last.seen[idx] |= (uint32_t)1 << (rinfo->seq & 31); |
422 | 0 | stat->last.seq = rinfo->seq; |
423 | 0 | if (rpp->ts_rate > 0) { |
424 | 0 | update_jitter_stats(jdp, rinfo, rtime, RTP_SEQ_RESET, rlog); |
425 | 0 | } |
426 | 0 | return (UPDATE_OK); |
427 | 0 | } else { |
428 | 0 | if (rpp->ts_rate > 0) { |
429 | 0 | if (seq == 0 && (stat->last.max_seq & 0xffff) < 65500) { |
430 | 0 | update_jitter_stats(jdp, rinfo, rtime, RTP_SEQ_RESET, rlog); |
431 | 0 | } else { |
432 | 0 | update_jitter_stats(jdp, rinfo, rtime, RTP_NORMAL, rlog); |
433 | 0 | } |
434 | 0 | } |
435 | 0 | } |
436 | 0 | if (rpp->ts_rate != 0 && ABS(rtime - stat->last.base_rtime - |
437 | 0 | rtp_ts2dtime(rpp->ts_rate, rinfo->ts - stat->last.base_ts)) > 0.1) { |
438 | 0 | LOGD_IF_NOT_NULL(rlog, SSRC_FMT "/%d: delta rtime=%f, delta ts=%f", |
439 | 0 | rinfo->ssrc, rinfo->seq, rtime - stat->last.base_rtime, |
440 | 0 | rtp_ts2dtime(rpp->ts_rate, rinfo->ts - stat->last.base_ts)); |
441 | 0 | stat->last.base_rtime = rtime; |
442 | 0 | } |
443 | 0 | if (stat->last.max_seq % 65536 < 536 && rinfo->seq > 65000) { |
444 | | /* Pre-wrap packet received after a wrap */ |
445 | 0 | seq -= 65536; |
446 | 0 | } else if (stat->last.max_seq > 65000 && seq < stat->last.max_seq - 65000) { |
447 | 0 | LOGD_IF_NOT_NULL(rlog, SSRC_FMT "/%d: wrap last->max_seq=%u, seq=%u", |
448 | 0 | rinfo->ssrc, rinfo->seq, stat->last.max_seq, seq); |
449 | | /* Wrap up has happened */ |
450 | 0 | stat->last.seq_offset += 65536; |
451 | 0 | seq += 65536; |
452 | 0 | if (stat->last.seq_offset % 131072 == 65536) { |
453 | 0 | memset(stat->last.seen + 2048, '\0', sizeof(stat->last.seen) / 2); |
454 | 0 | } else { |
455 | 0 | memset(stat->last.seen, '\0', sizeof(stat->last.seen) / 2); |
456 | 0 | } |
457 | 0 | } else if (seq + 536 < stat->last.max_seq || seq > stat->last.max_seq + 536) { |
458 | 0 | LOGD_IF_NOT_NULL(rlog, SSRC_FMT "/%d: desync last->max_seq=%u, seq=%u, m=%u", |
459 | 0 | rinfo->ssrc, rinfo->seq, stat->last.max_seq, seq, header->mbt); |
460 | | /* Desynchronization has happened. Treat it as a ssrc change */ |
461 | 0 | update_rtpp_totals(stat, stat); |
462 | 0 | stat->last.duplicates = 0; |
463 | 0 | memset(stat->last.seen, '\0', sizeof(stat->last.seen)); |
464 | 0 | stat->last.max_seq = stat->last.min_seq = seq; |
465 | 0 | stat->last.pcount = 1; |
466 | 0 | stat->desync_count += 1; |
467 | 0 | idx = (seq % 131072) >> 5; |
468 | 0 | stat->last.seen[idx] |= (uint32_t)1 << (rinfo->seq & 31); |
469 | 0 | stat->last.seq = rinfo->seq; |
470 | 0 | return (UPDATE_OK); |
471 | 0 | } |
472 | | /* printf("last->max_seq=%u, seq=%u, m=%u\n", stat->last.max_seq, seq, header->mbt);*/ |
473 | 0 | idx = (seq % 131072) >> 5; |
474 | 0 | mask = stat->last.seen[idx]; |
475 | 0 | if (((mask >> (seq & 31)) & 1) != 0) { |
476 | 0 | LOGD_IF_NOT_NULL(rlog, SSRC_FMT "/%d: DUP", |
477 | 0 | rinfo->ssrc, rinfo->seq); |
478 | 0 | stat->last.duplicates += 1; |
479 | 0 | stat->last.seq = rinfo->seq; |
480 | 0 | return (UPDATE_OK); |
481 | 0 | } |
482 | 0 | stat->last.seen[idx] |= (uint32_t)1 << (rinfo->seq & 31); |
483 | 0 | if (seq - stat->last.max_seq != 1) |
484 | 0 | LOGD_IF_NOT_NULL(rlog, SSRC_FMT "/%d: delta = %d", |
485 | 0 | rinfo->ssrc, rinfo->seq, seq - stat->last.max_seq); |
486 | 0 | if (seq >= stat->last.max_seq) { |
487 | 0 | stat->last.max_seq = seq; |
488 | 0 | stat->last.pcount += 1; |
489 | 0 | stat->last.seq = rinfo->seq; |
490 | 0 | return (UPDATE_OK); |
491 | 0 | } |
492 | 0 | if (seq >= stat->last.min_seq) { |
493 | 0 | stat->last.pcount += 1; |
494 | 0 | stat->last.seq = rinfo->seq; |
495 | 0 | return (UPDATE_OK); |
496 | 0 | } |
497 | 0 | if (stat->last.seq_offset == 0 && seq < stat->last.min_seq) { |
498 | 0 | stat->last.min_seq = seq; |
499 | 0 | stat->last.pcount += 1; |
500 | 0 | LOGD_IF_NOT_NULL(rlog, SSRC_FMT "/%d: last->min_seq=%u", |
501 | 0 | rinfo->ssrc, rinfo->seq, stat->last.min_seq); |
502 | 0 | stat->last.seq = rinfo->seq; |
503 | 0 | return (UPDATE_OK); |
504 | 0 | } |
505 | | /* XXX something wrong with the stream */ |
506 | 0 | stat->last.seq = rinfo->seq; |
507 | 0 | return (UPDATE_ERR); |
508 | 0 | } |
509 | | |
510 | | void |
511 | | update_rtpp_totals(struct rtpp_session_stat *wstat, struct rtpp_session_stat *ostat) |
512 | 0 | { |
513 | |
|
514 | 0 | if (ostat != wstat) { |
515 | 0 | ostat->psent = wstat->psent; |
516 | 0 | ostat->precvd = wstat->precvd; |
517 | 0 | ostat->duplicates = wstat->duplicates; |
518 | 0 | } |
519 | 0 | if (wstat->last.pcount == 0) |
520 | 0 | return; |
521 | 0 | ostat->psent += wstat->last.max_seq - wstat->last.min_seq + 1; |
522 | 0 | ostat->precvd += wstat->last.pcount; |
523 | 0 | ostat->duplicates += wstat->last.duplicates; |
524 | 0 | } |
525 | | |
526 | | int |
527 | | get_jitter_stats(struct rtp_analyze_jitter *jp, struct rtpa_stats_jitter *jst, |
528 | | struct rtpp_log *rlog) |
529 | 0 | { |
530 | 0 | int i; |
531 | 0 | struct rtp_analyze_jdata *rjdp; |
532 | 0 | double jtotal; |
533 | |
|
534 | 0 | i = 0; |
535 | 0 | for (rjdp = jp->first; rjdp != NULL && rjdp->ssrc.inited == 1; rjdp = rjdp->next) { |
536 | 0 | if (rjdp->jss.pcount < 2) { |
537 | 0 | continue; |
538 | 0 | } |
539 | 0 | #if DEBUG_TIMESTAMP_RESET |
540 | 0 | LOGD_IF_NOT_NULL(rlog, "get_jitter_stats() : " SSRC_FMT ", jss.jmax=%f", |
541 | 0 | rjdp->ssrc.val, rjdp->jss.jmax); |
542 | 0 | #endif |
543 | 0 | if (i == 0) { |
544 | 0 | jst->jlast = rjdp->jss.jlast; |
545 | 0 | jst->jmax = MAX(jp->jmax_acum, rjdp->jss.jmax); |
546 | 0 | jtotal = jp->jtotal_acum + rjdp->jss.jtotal; |
547 | 0 | jst->jvcount = jp->jvcount_acum + rjdp->jss.pcount - 1; |
548 | 0 | jst->pcount = jp->pcount_acum + rjdp->jss.pcount; |
549 | 0 | } else { |
550 | 0 | if (jst->jmax < rjdp->jss.jmax) { |
551 | 0 | jst->jmax = rjdp->jss.jmax; |
552 | 0 | } |
553 | 0 | jtotal += rjdp->jss.jtotal; |
554 | 0 | jst->jvcount += rjdp->jss.pcount - 1; |
555 | 0 | jst->pcount += rjdp->jss.pcount; |
556 | 0 | } |
557 | 0 | i += 1; |
558 | 0 | } |
559 | 0 | if (i > 0) { |
560 | 0 | jst->javg = jtotal / (double)(jst->jvcount); |
561 | 0 | } |
562 | 0 | return (i); |
563 | 0 | } |