1 /*! \file rtp.c
2 * \author Lorenzo Miniero <lorenzo@meetecho.com>
3 * \copyright GNU General Public License v3
4 * \brief RTP processing
5 * \details Implementation of the RTP header. Since the server does not
6 * much more than relaying frames around, the only thing we're interested
7 * in is the RTP header and how to get its payload, and parsing extensions.
8 *
9 * \ingroup protocols
10 * \ref protocols
11 */
12
13 #include <string.h>
14 #include "rtp.h"
15 #include "rtpsrtp.h"
16 #include "debug.h"
17 #include "utils.h"
18
janus_is_rtp(char * buf,guint len)19 gboolean janus_is_rtp(char *buf, guint len) {
20 if (len < 12)
21 return FALSE;
22 janus_rtp_header *header = (janus_rtp_header *)buf;
23 return ((header->type < 64) || (header->type >= 96));
24 }
25
janus_rtp_payload(char * buf,int len,int * plen)26 char *janus_rtp_payload(char *buf, int len, int *plen) {
27 if(!buf || len < 12)
28 return NULL;
29 janus_rtp_header *rtp = (janus_rtp_header *)buf;
30 if (rtp->version != 2) {
31 return NULL;
32 }
33 int hlen = 12;
34 if(rtp->csrccount) /* Skip CSRC if needed */
35 hlen += rtp->csrccount*4;
36
37 if(rtp->extension) {
38 janus_rtp_header_extension *ext = (janus_rtp_header_extension*)(buf+hlen);
39 int extlen = ntohs(ext->length)*4;
40 hlen += 4;
41 if(len > (hlen + extlen))
42 hlen += extlen;
43 }
44 if (len-hlen <= 0) {
45 return NULL;
46 }
47 if(plen)
48 *plen = len-hlen;
49 return buf+hlen;
50 }
51
janus_rtp_header_extension_get_id(const char * sdp,const char * extension)52 int janus_rtp_header_extension_get_id(const char *sdp, const char *extension) {
53 if(!sdp || !extension)
54 return -1;
55 char extmap[100];
56 g_snprintf(extmap, 100, "a=extmap:%%d %s", extension);
57 /* Look for the extmap */
58 const char *line = strstr(sdp, "m=");
59 while(line) {
60 char *next = strchr(line, '\n');
61 if(next) {
62 *next = '\0';
63 if(strstr(line, "a=extmap") && strstr(line, extension)) {
64 /* Gotcha! */
65 int id = 0;
66 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
67 if(sscanf(line, extmap, &id) == 1) {
68 #pragma GCC diagnostic warning "-Wformat-nonliteral"
69 *next = '\n';
70 return id;
71 }
72 }
73 *next = '\n';
74 }
75 line = next ? (next+1) : NULL;
76 }
77 return -2;
78 }
79
janus_rtp_header_extension_get_from_id(const char * sdp,int id)80 const char *janus_rtp_header_extension_get_from_id(const char *sdp, int id) {
81 if(!sdp || id < 0)
82 return NULL;
83 /* Look for the mapping */
84 char extmap[100];
85 g_snprintf(extmap, 100, "a=extmap:%d ", id);
86 const char *line = strstr(sdp, "m=");
87 while(line) {
88 char *next = strchr(line, '\n');
89 if(next) {
90 *next = '\0';
91 if(strstr(line, extmap)) {
92 /* Gotcha! */
93 char extension[100];
94 if(sscanf(line, "a=extmap:%d %99s", &id, extension) == 2) {
95 *next = '\n';
96 if(strstr(extension, JANUS_RTP_EXTMAP_AUDIO_LEVEL))
97 return JANUS_RTP_EXTMAP_AUDIO_LEVEL;
98 if(strstr(extension, JANUS_RTP_EXTMAP_VIDEO_ORIENTATION))
99 return JANUS_RTP_EXTMAP_VIDEO_ORIENTATION;
100 if(strstr(extension, JANUS_RTP_EXTMAP_PLAYOUT_DELAY))
101 return JANUS_RTP_EXTMAP_PLAYOUT_DELAY;
102 if(strstr(extension, JANUS_RTP_EXTMAP_TOFFSET))
103 return JANUS_RTP_EXTMAP_TOFFSET;
104 if(strstr(extension, JANUS_RTP_EXTMAP_ABS_SEND_TIME))
105 return JANUS_RTP_EXTMAP_ABS_SEND_TIME;
106 if(strstr(extension, JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC))
107 return JANUS_RTP_EXTMAP_TRANSPORT_WIDE_CC;
108 if(strstr(extension, JANUS_RTP_EXTMAP_MID))
109 return JANUS_RTP_EXTMAP_MID;
110 if(strstr(extension, JANUS_RTP_EXTMAP_RID))
111 return JANUS_RTP_EXTMAP_RID;
112 if(strstr(extension, JANUS_RTP_EXTMAP_REPAIRED_RID))
113 return JANUS_RTP_EXTMAP_REPAIRED_RID;
114 JANUS_LOG(LOG_ERR, "Unsupported extension '%s'\n", extension);
115 return NULL;
116 }
117 }
118 *next = '\n';
119 }
120 line = next ? (next+1) : NULL;
121 }
122 return NULL;
123 }
124
125 /* Static helper to quickly find the extension data */
janus_rtp_header_extension_find(char * buf,int len,int id,uint8_t * byte,uint32_t * word,char ** ref)126 static int janus_rtp_header_extension_find(char *buf, int len, int id,
127 uint8_t *byte, uint32_t *word, char **ref) {
128 if(!buf || len < 12)
129 return -1;
130 janus_rtp_header *rtp = (janus_rtp_header *)buf;
131 if (rtp->version != 2) {
132 return -1;
133 }
134 int hlen = 12;
135 if(rtp->csrccount) /* Skip CSRC if needed */
136 hlen += rtp->csrccount*4;
137 if(rtp->extension) {
138 janus_rtp_header_extension *ext = (janus_rtp_header_extension *)(buf+hlen);
139 int extlen = ntohs(ext->length)*4;
140 hlen += 4;
141 if(len > (hlen + extlen)) {
142 /* 1-Byte extension */
143 if(ntohs(ext->type) == 0xBEDE) {
144 const uint8_t padding = 0x00, reserved = 0xF;
145 uint8_t extid = 0, idlen;
146 int i = 0;
147 while(i < extlen) {
148 extid = (uint8_t)buf[hlen+i] >> 4;
149 if(extid == reserved) {
150 break;
151 } else if(extid == padding) {
152 i++;
153 continue;
154 }
155 idlen = ((uint8_t)buf[hlen+i] & 0xF)+1;
156 if(extid == id) {
157 /* Found! */
158 if(byte)
159 *byte = (uint8_t)buf[hlen+i+1];
160 if(word && idlen >= 3 && (i+3) < extlen) {
161 memcpy(word, buf+hlen+i, sizeof(uint32_t));
162 *word = ntohl(*word);
163 }
164 if(ref)
165 *ref = &buf[hlen+i];
166 return 0;
167 }
168 i += 1 + idlen;
169 }
170 }
171 hlen += extlen;
172 }
173 }
174 return -1;
175 }
176
janus_rtp_header_extension_parse_audio_level(char * buf,int len,int id,gboolean * vad,int * level)177 int janus_rtp_header_extension_parse_audio_level(char *buf, int len, int id, gboolean *vad, int *level) {
178 uint8_t byte = 0;
179 if(janus_rtp_header_extension_find(buf, len, id, &byte, NULL, NULL) < 0)
180 return -1;
181 /* a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level */
182 gboolean v = (byte & 0x80) >> 7;
183 int value = byte & 0x7F;
184 JANUS_LOG(LOG_DBG, "%02x --> v=%d, level=%d\n", byte, v, value);
185 if(vad)
186 *vad = v;
187 if(level)
188 *level = value;
189 return 0;
190 }
191
janus_rtp_header_extension_parse_video_orientation(char * buf,int len,int id,gboolean * c,gboolean * f,gboolean * r1,gboolean * r0)192 int janus_rtp_header_extension_parse_video_orientation(char *buf, int len, int id,
193 gboolean *c, gboolean *f, gboolean *r1, gboolean *r0) {
194 uint8_t byte = 0;
195 if(janus_rtp_header_extension_find(buf, len, id, &byte, NULL, NULL) < 0)
196 return -1;
197 /* a=extmap:4 urn:3gpp:video-orientation */
198 gboolean cbit = (byte & 0x08) >> 3;
199 gboolean fbit = (byte & 0x04) >> 2;
200 gboolean r1bit = (byte & 0x02) >> 1;
201 gboolean r0bit = byte & 0x01;
202 JANUS_LOG(LOG_DBG, "%02x --> c=%d, f=%d, r1=%d, r0=%d\n", byte, cbit, fbit, r1bit, r0bit);
203 if(c)
204 *c = cbit;
205 if(f)
206 *f = fbit;
207 if(r1)
208 *r1 = r1bit;
209 if(r0)
210 *r0 = r0bit;
211 return 0;
212 }
213
janus_rtp_header_extension_parse_playout_delay(char * buf,int len,int id,uint16_t * min_delay,uint16_t * max_delay)214 int janus_rtp_header_extension_parse_playout_delay(char *buf, int len, int id,
215 uint16_t *min_delay, uint16_t *max_delay) {
216 uint32_t bytes = 0;
217 if(janus_rtp_header_extension_find(buf, len, id, NULL, &bytes, NULL) < 0)
218 return -1;
219 /* a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay */
220 uint16_t min = (bytes & 0x00FFF000) >> 12;
221 uint16_t max = bytes & 0x00000FFF;
222 JANUS_LOG(LOG_DBG, "%"SCNu32"x --> min=%"SCNu16", max=%"SCNu16"\n", bytes, min, max);
223 if(min_delay)
224 *min_delay = min;
225 if(max_delay)
226 *max_delay = max;
227 return 0;
228 }
229
janus_rtp_header_extension_parse_mid(char * buf,int len,int id,char * sdes_item,int sdes_len)230 int janus_rtp_header_extension_parse_mid(char *buf, int len, int id,
231 char *sdes_item, int sdes_len) {
232 char *ext = NULL;
233 if(janus_rtp_header_extension_find(buf, len, id, NULL, NULL, &ext) < 0)
234 return -1;
235 /* a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid */
236 if(ext == NULL)
237 return -2;
238 int val_len = (*ext & 0x0F) + 1;
239 if(val_len > (sdes_len-1)) {
240 JANUS_LOG(LOG_WARN, "SDES buffer is too small (%d < %d), MID will be cut\n", val_len, sdes_len);
241 val_len = sdes_len-1;
242 }
243 if (val_len > len-(ext-buf)-1 ) {
244 return -3;
245 }
246 memcpy(sdes_item, ext+1, val_len);
247 *(sdes_item+val_len) = '\0';
248 return 0;
249 }
250
janus_rtp_header_extension_parse_rid(char * buf,int len,int id,char * sdes_item,int sdes_len)251 int janus_rtp_header_extension_parse_rid(char *buf, int len, int id,
252 char *sdes_item, int sdes_len) {
253 char *ext = NULL;
254 if(janus_rtp_header_extension_find(buf, len, id, NULL, NULL, &ext) < 0)
255 return -1;
256 /* a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id */
257 /* a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id */
258 if(ext == NULL)
259 return -2;
260 int val_len = (*ext & 0x0F) + 1;
261 if(val_len > (sdes_len-1)) {
262 JANUS_LOG(LOG_WARN, "SDES buffer is too small (%d < %d), RTP stream ID will be cut\n", val_len, sdes_len);
263 val_len = sdes_len-1;
264 }
265 if (val_len > len-(ext-buf)-1 ) {
266 return -3;
267 }
268 memcpy(sdes_item, ext+1, val_len);
269 *(sdes_item+val_len) = '\0';
270 return 0;
271 }
272
janus_rtp_header_extension_parse_abs_sent_time(char * buf,int len,int id,uint32_t * abs_ts)273 int janus_rtp_header_extension_parse_abs_sent_time(char *buf, int len, int id, uint32_t *abs_ts) {
274 char *ext = NULL;
275 if(janus_rtp_header_extension_find(buf, len, id, NULL, NULL, &ext) < 0)
276 return -1;
277 /* a=extmap:4 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time */
278 if(ext == NULL)
279 return -2;
280 int val_len = (*ext & 0x0F) + 1;
281 if(val_len < 3 || val_len > len-(ext-buf)-1)
282 return -3;
283 uint32_t abs24 = 0;
284 memcpy(&abs24, ext+1, 3);
285 if(abs_ts)
286 *abs_ts = ntohl(abs24 << 8);
287 return 0;
288 }
289
janus_rtp_header_extension_set_abs_send_time(char * buf,int len,int id,uint32_t abs_ts)290 int janus_rtp_header_extension_set_abs_send_time(char *buf, int len, int id, uint32_t abs_ts) {
291 char *ext = NULL;
292 if(janus_rtp_header_extension_find(buf, len, id, NULL, NULL, &ext) < 0)
293 return -1;
294 if(ext == NULL)
295 return -2;
296 int val_len = (*ext & 0x0F) + 1;
297 if(val_len < 3 || val_len > len-(ext-buf)-1)
298 return -3;
299 uint32_t abs24 = htonl(abs_ts) >> 8;
300 memcpy(ext+1, &abs24, 3);
301 return 0;
302 }
303
janus_rtp_header_extension_parse_transport_wide_cc(char * buf,int len,int id,uint16_t * transSeqNum)304 int janus_rtp_header_extension_parse_transport_wide_cc(char *buf, int len, int id, uint16_t *transSeqNum) {
305 char *ext = NULL;
306 if(janus_rtp_header_extension_find(buf, len, id, NULL, NULL, &ext) < 0)
307 return -1;
308 /* 0 1 2 3
309 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
310 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
311 | ID | L=1 |transport-wide sequence number | zero padding |
312 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
313 */
314 if(ext == NULL)
315 return -2;
316 int val_len = (*ext & 0x0F) + 1;
317 if (val_len < 2 || val_len > len-(ext-buf)-1)
318 return -3;
319 memcpy(transSeqNum, ext+1, sizeof(uint16_t));
320 *transSeqNum = ntohs(*transSeqNum);
321 return 0;
322 }
323
janus_rtp_header_extension_set_transport_wide_cc(char * buf,int len,int id,uint16_t transSeqNum)324 int janus_rtp_header_extension_set_transport_wide_cc(char *buf, int len, int id, uint16_t transSeqNum) {
325 char *ext = NULL;
326 if(janus_rtp_header_extension_find(buf, len, id, NULL, NULL, &ext) < 0)
327 return -1;
328 if(ext == NULL)
329 return -2;
330 int val_len = (*ext & 0x0F) + 1;
331 if (val_len < 2 || val_len > len-(ext-buf)-1)
332 return -3;
333 transSeqNum = htons(transSeqNum);
334 memcpy(ext+1, &transSeqNum, sizeof(uint16_t));
335 return 0;
336 }
337
janus_rtp_header_extension_replace_id(char * buf,int len,int id,int new_id)338 int janus_rtp_header_extension_replace_id(char *buf, int len, int id, int new_id) {
339 if(!buf || len < 12)
340 return -1;
341 janus_rtp_header *rtp = (janus_rtp_header *)buf;
342 if (rtp->version != 2) {
343 return -2;
344 }
345 int hlen = 12;
346 if(rtp->csrccount) /* Skip CSRC if needed */
347 hlen += rtp->csrccount*4;
348 if(rtp->extension) {
349 janus_rtp_header_extension *ext = (janus_rtp_header_extension *)(buf+hlen);
350 int extlen = ntohs(ext->length)*4;
351 hlen += 4;
352 if(len > (hlen + extlen)) {
353 /* 1-Byte extension */
354 if(ntohs(ext->type) == 0xBEDE) {
355 const uint8_t padding = 0x00, reserved = 0xF;
356 uint8_t extid = 0, idlen = 0;
357 int i = 0;
358 while(i < extlen) {
359 extid = buf[hlen+i] >> 4;
360 if(extid == reserved) {
361 break;
362 } else if(extid == padding) {
363 i++;
364 continue;
365 }
366 idlen = (buf[hlen+i] & 0xF)+1;
367 if(extid == id) {
368 /* Found! */
369 buf[hlen+i] = (new_id << 4) + (idlen - 1);
370 return 0;
371 }
372 i += 1 + idlen;
373 }
374 }
375 hlen += extlen;
376 }
377 }
378 return -3;
379 }
380
381 /* RTP context related methods */
janus_rtp_switching_context_reset(janus_rtp_switching_context * context)382 void janus_rtp_switching_context_reset(janus_rtp_switching_context *context) {
383 if(context == NULL)
384 return;
385 /* Reset the context values */
386 memset(context, 0, sizeof(*context));
387 }
388
janus_rtp_skew_compensate_audio(janus_rtp_header * header,janus_rtp_switching_context * context,gint64 now)389 int janus_rtp_skew_compensate_audio(janus_rtp_header *header, janus_rtp_switching_context *context, gint64 now) {
390 /* Reset values if a new ssrc has been detected */
391 if (context->a_new_ssrc) {
392 JANUS_LOG(LOG_VERB, "audio skew SSRC=%"SCNu32" resetting status\n", context->a_last_ssrc);
393 context->a_reference_time = now;
394 context->a_start_time = 0;
395 context->a_evaluating_start_time = 0;
396 context->a_start_ts = 0;
397 context->a_active_delay = 0;
398 context->a_prev_delay = 0;
399 context->a_seq_offset = 0;
400 context->a_ts_offset = 0;
401 context->a_target_ts = 0;
402 context->a_new_ssrc = FALSE;
403 }
404
405 /* N : a N sequence number jump has been performed */
406 /* 0 : any new skew compensation has been applied */
407 /* -N : a N packet drop must be performed */
408 int exit_status = 0;
409
410 /* Do not execute skew analysis in the first seconds */
411 if (now-context->a_reference_time < SKEW_DETECTION_WAIT_TIME_SECS/2 * G_USEC_PER_SEC) {
412 return 0;
413 } else if (!context->a_start_time) {
414 JANUS_LOG(LOG_VERB, "audio skew SSRC=%"SCNu32" evaluation phase start\n", context->a_last_ssrc);
415 context->a_start_time = now;
416 context->a_evaluating_start_time = now;
417 context->a_start_ts = context->a_last_ts;
418 }
419
420 /* Skew analysis */
421 /* Are we waiting for a target timestamp? (a negative skew has been evaluated in a previous iteration) */
422 if (context->a_target_ts > 0 && (gint32)(context->a_target_ts - context->a_last_ts) > 0) {
423 context->a_seq_offset--;
424 exit_status = -1;
425 } else {
426 context->a_target_ts = 0;
427 /* Do not execute analysis for out of order packets or multi-packets frame */
428 if (context->a_last_seq == context->a_prev_seq + 1 && context->a_last_ts != context->a_prev_ts) {
429 /* Set the sample rate according to the header */
430 guint32 akhz = 48; /* 48khz for Opus */
431 if(header->type == 0 || header->type == 8 || header->type == 9)
432 akhz = 8;
433 /* Evaluate the local RTP timestamp according to the local clock */
434 guint32 expected_ts = ((now - context->a_start_time)*akhz)/1000 + context->a_start_ts;
435 /* Evaluate current delay */
436 gint32 delay_now = context->a_last_ts - expected_ts;
437 /* Exponentially weighted moving average estimation */
438 gint32 delay_estimate = (63*context->a_prev_delay + delay_now)/64;
439 /* Save previous delay for the next iteration*/
440 context->a_prev_delay = delay_estimate;
441 /* Evaluate the distance between active delay and current delay estimate */
442 gint32 offset = context->a_active_delay - delay_estimate;
443 JANUS_LOG(LOG_HUGE, "audio skew status SSRC=%"SCNu32" RECVD_TS=%"SCNu32" EXPTD_TS=%"SCNu32" OFFSET=%"SCNi32" TS_OFFSET=%"SCNi32" SEQ_OFFSET=%"SCNi16"\n", context->a_last_ssrc, context->a_last_ts, expected_ts, offset, context->a_ts_offset, context->a_seq_offset);
444 gint32 skew_th = RTP_AUDIO_SKEW_TH_MS*akhz;
445 /* Evaluation phase */
446 if (context->a_evaluating_start_time > 0) {
447 /* Check if the offset has surpassed half the threshold during the evaluating phase */
448 if (now-context->a_evaluating_start_time <= SKEW_DETECTION_WAIT_TIME_SECS/2 * G_USEC_PER_SEC) {
449 if (abs(offset) <= skew_th/2) {
450 JANUS_LOG(LOG_HUGE, "audio skew SSRC=%"SCNu32" evaluation phase continue\n", context->a_last_ssrc);
451 } else {
452 JANUS_LOG(LOG_VERB, "audio skew SSRC=%"SCNu32" evaluation phase reset\n", context->a_last_ssrc);
453 context->a_start_time = now;
454 context->a_evaluating_start_time = now;
455 context->a_start_ts = context->a_last_ts;
456 }
457 } else {
458 JANUS_LOG(LOG_VERB, "audio skew SSRC=%"SCNu32" evaluation phase stop\n", context->a_last_ssrc);
459 context->a_evaluating_start_time = 0;
460 }
461 return 0;
462 }
463 /* Check if the offset has surpassed the threshold */
464 if (offset >= skew_th) {
465 /* The source is slowing down */
466 /* Update active delay */
467 context->a_active_delay = delay_estimate;
468 /* Adjust ts offset */
469 context->a_ts_offset += skew_th;
470 /* Calculate last ts increase */
471 guint32 ts_incr = context->a_last_ts-context->a_prev_ts;
472 /* Evaluate sequence number jump */
473 guint16 jump = (skew_th+ts_incr-1)/ts_incr;
474 /* Adjust seq num offset */
475 context->a_seq_offset += jump;
476 exit_status = jump;
477 } else if (offset <= -skew_th) {
478 /* The source is speeding up*/
479 /* Update active delay */
480 context->a_active_delay = delay_estimate;
481 /* Adjust ts offset */
482 context->a_ts_offset -= skew_th;
483 /* Set target ts */
484 context->a_target_ts = context->a_last_ts + skew_th;
485 if (context->a_target_ts == 0)
486 context->a_target_ts = 1;
487 /* Adjust seq num offset */
488 context->a_seq_offset--;
489 exit_status = -1;
490 }
491 }
492 }
493
494 /* Skew compensation */
495 /* Fix header timestamp considering the active offset */
496 guint32 fixed_rtp_ts = context->a_last_ts + context->a_ts_offset;
497 header->timestamp = htonl(fixed_rtp_ts);
498 /* Fix header sequence number considering the total offset */
499 guint16 fixed_rtp_seq = context->a_last_seq + context->a_seq_offset;
500 header->seq_number = htons(fixed_rtp_seq);
501
502 return exit_status;
503 }
504
janus_rtp_skew_compensate_video(janus_rtp_header * header,janus_rtp_switching_context * context,gint64 now)505 int janus_rtp_skew_compensate_video(janus_rtp_header *header, janus_rtp_switching_context *context, gint64 now) {
506 /* Reset values if a new ssrc has been detected */
507 if (context->v_new_ssrc) {
508 JANUS_LOG(LOG_VERB, "video skew SSRC=%"SCNu32" resetting status\n", context->v_last_ssrc);
509 context->v_reference_time = now;
510 context->v_start_time = 0;
511 context->v_evaluating_start_time = 0;
512 context->v_start_ts = 0;
513 context->v_active_delay = 0;
514 context->v_prev_delay = 0;
515 context->v_seq_offset = 0;
516 context->v_ts_offset = 0;
517 context->v_target_ts = 0;
518 context->v_new_ssrc = FALSE;
519 }
520
521 /* N : a N sequence numbers jump has been performed */
522 /* 0 : any new skew compensation has been applied */
523 /* -N : a N packets drop must be performed */
524 int exit_status = 0;
525
526 /* Do not execute skew analysis in the first seconds */
527 if (now-context->v_reference_time < SKEW_DETECTION_WAIT_TIME_SECS/2 *G_USEC_PER_SEC) {
528 return 0;
529 } else if (!context->v_start_time) {
530 JANUS_LOG(LOG_VERB, "video skew SSRC=%"SCNu32" evaluation phase start\n", context->v_last_ssrc);
531 context->v_start_time = now;
532 context->v_evaluating_start_time = now;
533 context->v_start_ts = context->v_last_ts;
534 }
535
536 /* Skew analysis */
537 /* Are we waiting for a target timestamp? (a negative skew has been evaluated in a previous iteration) */
538 if (context->v_target_ts > 0 && (gint32)(context->v_target_ts - context->v_last_ts) > 0) {
539 context->v_seq_offset--;
540 exit_status = -1;
541 } else {
542 context->v_target_ts = 0;
543 /* Do not execute analysis for out of order packets or multi-packets frame */
544 if (context->v_last_seq == context->v_prev_seq + 1 && context->v_last_ts != context->v_prev_ts) {
545 /* Set the sample rate */
546 guint32 vkhz = 90; /* 90khz */
547 /* Evaluate the local RTP timestamp according to the local clock */
548 guint32 expected_ts = ((now - context->v_start_time)*vkhz)/1000 + context->v_start_ts;
549 /* Evaluate current delay */
550 gint32 delay_now = context->v_last_ts - expected_ts;
551 /* Exponentially weighted moving average estimation */
552 gint32 delay_estimate = (63*context->v_prev_delay + delay_now)/64;
553 /* Save previous delay for the next iteration*/
554 context->v_prev_delay = delay_estimate;
555 /* Evaluate the distance between active delay and current delay estimate */
556 gint32 offset = context->v_active_delay - delay_estimate;
557 JANUS_LOG(LOG_HUGE, "video skew status SSRC=%"SCNu32" RECVD_TS=%"SCNu32" EXPTD_TS=%"SCNu32" OFFSET=%"SCNi32" TS_OFFSET=%"SCNi32" SEQ_OFFSET=%"SCNi16"\n", context->v_last_ssrc, context->v_last_ts, expected_ts, offset, context->v_ts_offset, context->v_seq_offset);
558 gint32 skew_th = RTP_VIDEO_SKEW_TH_MS*vkhz;
559 /* Evaluation phase */
560 if (context->v_evaluating_start_time > 0) {
561 /* Check if the offset has surpassed half the threshold during the evaluating phase */
562 if (now-context->v_evaluating_start_time <= SKEW_DETECTION_WAIT_TIME_SECS/2 * G_USEC_PER_SEC) {
563 if (abs(offset) <= skew_th/2) {
564 JANUS_LOG(LOG_HUGE, "video skew SSRC=%"SCNu32" evaluation phase continue\n", context->v_last_ssrc);
565 } else {
566 JANUS_LOG(LOG_VERB, "video skew SSRC=%"SCNu32" evaluation phase reset\n", context->v_last_ssrc);
567 context->v_start_time = now;
568 context->v_evaluating_start_time = now;
569 context->v_start_ts = context->v_last_ts;
570 }
571 } else {
572 JANUS_LOG(LOG_VERB, "video skew SSRC=%"SCNu32" evaluation phase stop\n", context->v_last_ssrc);
573 context->v_evaluating_start_time = 0;
574 }
575 return 0;
576 }
577 /* Check if the offset has surpassed the threshold */
578 if (offset >= skew_th) {
579 /* The source is slowing down */
580 /* Update active delay */
581 context->v_active_delay = delay_estimate;
582 /* Adjust ts offset */
583 context->v_ts_offset += skew_th;
584 /* Calculate last ts increase */
585 guint32 ts_incr = context->v_last_ts-context->v_prev_ts;
586 /* Evaluate sequence number jump */
587 guint16 jump = (skew_th+ts_incr-1)/ts_incr;
588 /* Adjust seq num offset */
589 context->v_seq_offset += jump;
590 exit_status = jump;
591 } else if (offset <= -skew_th) {
592 /* The source is speeding up*/
593 /* Update active delay */
594 context->v_active_delay = delay_estimate;
595 /* Adjust ts offset */
596 context->v_ts_offset -= skew_th;
597 /* Set target ts */
598 context->v_target_ts = context->v_last_ts + skew_th;
599 if (context->v_target_ts == 0)
600 context->v_target_ts = 1;
601 /* Adjust seq num offset */
602 context->v_seq_offset--;
603 exit_status = -1;
604 }
605 }
606 }
607
608 /* Skew compensation */
609 /* Fix header timestamp considering the active offset */
610 guint32 fixed_rtp_ts = context->v_last_ts + context->v_ts_offset;
611 header->timestamp = htonl(fixed_rtp_ts);
612 /* Fix header sequence number considering the total offset */
613 guint16 fixed_rtp_seq = context->v_last_seq + context->v_seq_offset;
614 header->seq_number = htons(fixed_rtp_seq);
615
616 return exit_status;
617 }
618
janus_rtp_header_update(janus_rtp_header * header,janus_rtp_switching_context * context,gboolean video,int step)619 void janus_rtp_header_update(janus_rtp_header *header, janus_rtp_switching_context *context, gboolean video, int step) {
620 if(header == NULL || context == NULL)
621 return;
622 /* Note: while the step property is still there for compatibility reasons, to
623 * keep the signature as it was before, it's ignored: whenever there's a switch
624 * to take into account, we compute how much time passed between the last RTP
625 * packet with the old SSRC and this new one, and prepare a timestamp accordingly */
626 uint32_t ssrc = ntohl(header->ssrc);
627 uint32_t timestamp = ntohl(header->timestamp);
628 uint16_t seq = ntohs(header->seq_number);
629 if(video) {
630 if(ssrc != context->v_last_ssrc) {
631 /* Video SSRC changed: update both sequence number and timestamp */
632 JANUS_LOG(LOG_VERB, "Video SSRC changed, %"SCNu32" --> %"SCNu32"\n",
633 context->v_last_ssrc, ssrc);
634 context->v_last_ssrc = ssrc;
635 context->v_ts_reset = TRUE;
636 context->v_seq_reset = TRUE;
637 /* Reset skew compensation data */
638 context->v_new_ssrc = TRUE;
639 }
640 if(context->v_ts_reset) {
641 /* Video timestamp was paused for a while */
642 JANUS_LOG(LOG_HUGE, "Video RTP timestamp reset requested");
643 context->v_ts_reset = FALSE;
644 context->v_base_ts_prev = context->v_last_ts;
645 context->v_base_ts = timestamp;
646 /* How much time since the last video RTP packet? We compute an offset accordingly */
647 if(context->v_last_time > 0) {
648 gint64 time_diff = janus_get_monotonic_time() - context->v_last_time;
649 time_diff = (time_diff*90)/1000; /* We're assuming 90khz here */
650 if(time_diff == 0)
651 time_diff = 1;
652 context->v_base_ts_prev += (guint32)time_diff;
653 context->v_last_ts += (guint32)time_diff;
654 JANUS_LOG(LOG_HUGE, "Computed offset for video RTP timestamp: %"SCNu32"\n", (guint32)time_diff);
655 }
656 }
657 if(context->v_seq_reset) {
658 /* Video sequence number was paused for a while */
659 JANUS_LOG(LOG_HUGE, "Video RTP sequence number reset requested");
660 context->v_seq_reset = FALSE;
661 context->v_base_seq_prev = context->v_last_seq;
662 context->v_base_seq = seq;
663 }
664 /* Compute a coherent timestamp and sequence number */
665 context->v_prev_ts = context->v_last_ts;
666 context->v_last_ts = (timestamp-context->v_base_ts) + context->v_base_ts_prev;
667 context->v_prev_seq = context->v_last_seq;
668 context->v_last_seq = (seq-context->v_base_seq)+context->v_base_seq_prev+1;
669 /* Update the timestamp and sequence number in the RTP packet */
670 header->timestamp = htonl(context->v_last_ts);
671 header->seq_number = htons(context->v_last_seq);
672 /* Take note of when we last handled this RTP packet */
673 context->v_last_time = janus_get_monotonic_time();
674 } else {
675 if(ssrc != context->a_last_ssrc) {
676 /* Audio SSRC changed: update both sequence number and timestamp */
677 JANUS_LOG(LOG_VERB, "Audio SSRC changed, %"SCNu32" --> %"SCNu32"\n",
678 context->a_last_ssrc, ssrc);
679 context->a_last_ssrc = ssrc;
680 context->a_ts_reset = TRUE;
681 context->a_seq_reset = TRUE;
682 /* Reset skew compensation data */
683 context->a_new_ssrc = TRUE;
684 }
685 if(context->a_ts_reset) {
686 /* Audio timestamp was paused for a while */
687 JANUS_LOG(LOG_HUGE, "Audio RTP timestamp reset requested");
688 context->a_ts_reset = FALSE;
689 context->a_base_ts_prev = context->a_last_ts;
690 context->a_base_ts = timestamp;
691 /* How much time since the last audio RTP packet? We compute an offset accordingly */
692 if(context->a_last_time > 0) {
693 gint64 time_diff = janus_get_monotonic_time() - context->a_last_time;
694 int akhz = 48;
695 if(header->type == 0 || header->type == 8 || header->type == 9)
696 akhz = 8; /* We're assuming 48khz here (Opus), unless it's G.711/G.722 (8khz) */
697 time_diff = (time_diff*akhz)/1000;
698 if(time_diff == 0)
699 time_diff = 1;
700 context->a_base_ts_prev += (guint32)time_diff;
701 context->a_prev_ts += (guint32)time_diff;
702 context->a_last_ts += (guint32)time_diff;
703 JANUS_LOG(LOG_HUGE, "Computed offset for audio RTP timestamp: %"SCNu32"\n", (guint32)time_diff);
704 }
705 }
706 if(context->a_seq_reset) {
707 /* Audio sequence number was paused for a while */
708 JANUS_LOG(LOG_HUGE, "Audio RTP sequence number reset requested");
709 context->a_seq_reset = FALSE;
710 context->a_base_seq_prev = context->a_last_seq;
711 context->a_base_seq = seq;
712 }
713 /* Compute a coherent timestamp and sequence number */
714 context->a_prev_ts = context->a_last_ts;
715 context->a_last_ts = (timestamp-context->a_base_ts) + context->a_base_ts_prev;
716 context->a_prev_seq = context->a_last_seq;
717 context->a_last_seq = (seq-context->a_base_seq)+context->a_base_seq_prev+1;
718 /* Update the timestamp and sequence number in the RTP packet */
719 header->timestamp = htonl(context->a_last_ts);
720 header->seq_number = htons(context->a_last_seq);
721 /* Take note of when we last handled this RTP packet */
722 context->a_last_time = janus_get_monotonic_time();
723 }
724 }
725
726
727 /* SRTP stuff: we may need our own randomizer */
728 #ifdef HAVE_SRTP_2
srtp_crypto_get_random(uint8_t * key,int len)729 int srtp_crypto_get_random(uint8_t *key, int len) {
730 #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
731 /* libsrtp 2.0 doesn't have crypto_get_random, we use OpenSSL's RAND_* to replace it:
732 * https://wiki.openssl.org/index.php/Random_Numbers */
733 int rc = RAND_bytes(key, len);
734 if(rc != 1) {
735 /* Error generating */
736 return -1;
737 }
738 #endif
739 return 0;
740 }
741 #endif
742 /* SRTP error codes as a string array */
743 static const char *janus_srtp_error[] =
744 {
745 #ifdef HAVE_SRTP_2
746 "srtp_err_status_ok",
747 "srtp_err_status_fail",
748 "srtp_err_status_bad_param",
749 "srtp_err_status_alloc_fail",
750 "srtp_err_status_dealloc_fail",
751 "srtp_err_status_init_fail",
752 "srtp_err_status_terminus",
753 "srtp_err_status_auth_fail",
754 "srtp_err_status_cipher_fail",
755 "srtp_err_status_replay_fail",
756 "srtp_err_status_replay_old",
757 "srtp_err_status_algo_fail",
758 "srtp_err_status_no_such_op",
759 "srtp_err_status_no_ctx",
760 "srtp_err_status_cant_check",
761 "srtp_err_status_key_expired",
762 "srtp_err_status_socket_err",
763 "srtp_err_status_signal_err",
764 "srtp_err_status_nonce_bad",
765 "srtp_err_status_read_fail",
766 "srtp_err_status_write_fail",
767 "srtp_err_status_parse_err",
768 "srtp_err_status_encode_err",
769 "srtp_err_status_semaphore_err",
770 "srtp_err_status_pfkey_err",
771 #else
772 "err_status_ok",
773 "err_status_fail",
774 "err_status_bad_param",
775 "err_status_alloc_fail",
776 "err_status_dealloc_fail",
777 "err_status_init_fail",
778 "err_status_terminus",
779 "err_status_auth_fail",
780 "err_status_cipher_fail",
781 "err_status_replay_fail",
782 "err_status_replay_old",
783 "err_status_algo_fail",
784 "err_status_no_such_op",
785 "err_status_no_ctx",
786 "err_status_cant_check",
787 "err_status_key_expired",
788 "err_status_socket_err",
789 "err_status_signal_err",
790 "err_status_nonce_bad",
791 "err_status_read_fail",
792 "err_status_write_fail",
793 "err_status_parse_err",
794 "err_status_encode_err",
795 "err_status_semaphore_err",
796 "err_status_pfkey_err",
797 #endif
798 };
janus_srtp_error_str(int error)799 const char *janus_srtp_error_str(int error) {
800 if(error < 0 || error > 24)
801 return NULL;
802 return janus_srtp_error[error];
803 }
804
805 /* Payload types we'll offer internally */
806 #define OPUS_PT 111
807 #define MULTIOPUS_PT OPUS_PT
808 #define ISAC32_PT 104
809 #define ISAC16_PT 103
810 #define PCMU_PT 0
811 #define PCMA_PT 8
812 #define G722_PT 9
813 #define VP8_PT 96
814 #define VP9_PT 101
815 #define H264_PT 107
816 #define AV1_PT 98
817 #define H265_PT 100
janus_audiocodec_name(janus_audiocodec acodec)818 const char *janus_audiocodec_name(janus_audiocodec acodec) {
819 switch(acodec) {
820 case JANUS_AUDIOCODEC_NONE:
821 return "none";
822 case JANUS_AUDIOCODEC_OPUS:
823 return "opus";
824 case JANUS_AUDIOCODEC_MULTIOPUS:
825 return "multiopus";
826 case JANUS_AUDIOCODEC_PCMU:
827 return "pcmu";
828 case JANUS_AUDIOCODEC_PCMA:
829 return "pcma";
830 case JANUS_AUDIOCODEC_G722:
831 return "g722";
832 case JANUS_AUDIOCODEC_ISAC_32K:
833 return "isac32";
834 case JANUS_AUDIOCODEC_ISAC_16K:
835 return "isac16";
836 default:
837 /* Shouldn't happen */
838 return "opus";
839 }
840 }
janus_audiocodec_from_name(const char * name)841 janus_audiocodec janus_audiocodec_from_name(const char *name) {
842 if(name == NULL)
843 return JANUS_AUDIOCODEC_NONE;
844 else if(!strcasecmp(name, "opus"))
845 return JANUS_AUDIOCODEC_OPUS;
846 else if(!strcasecmp(name, "multiopus"))
847 return JANUS_AUDIOCODEC_MULTIOPUS;
848 else if(!strcasecmp(name, "isac32"))
849 return JANUS_AUDIOCODEC_ISAC_32K;
850 else if(!strcasecmp(name, "isac16"))
851 return JANUS_AUDIOCODEC_ISAC_16K;
852 else if(!strcasecmp(name, "pcmu"))
853 return JANUS_AUDIOCODEC_PCMU;
854 else if(!strcasecmp(name, "pcma"))
855 return JANUS_AUDIOCODEC_PCMA;
856 else if(!strcasecmp(name, "g722"))
857 return JANUS_AUDIOCODEC_G722;
858 JANUS_LOG(LOG_WARN, "Unsupported audio codec '%s'\n", name);
859 return JANUS_AUDIOCODEC_NONE;
860 }
janus_audiocodec_pt(janus_audiocodec acodec)861 int janus_audiocodec_pt(janus_audiocodec acodec) {
862 switch(acodec) {
863 case JANUS_AUDIOCODEC_NONE:
864 return -1;
865 case JANUS_AUDIOCODEC_OPUS:
866 return OPUS_PT;
867 case JANUS_AUDIOCODEC_MULTIOPUS:
868 return MULTIOPUS_PT;
869 case JANUS_AUDIOCODEC_ISAC_32K:
870 return ISAC32_PT;
871 case JANUS_AUDIOCODEC_ISAC_16K:
872 return ISAC16_PT;
873 case JANUS_AUDIOCODEC_PCMU:
874 return PCMU_PT;
875 case JANUS_AUDIOCODEC_PCMA:
876 return PCMA_PT;
877 case JANUS_AUDIOCODEC_G722:
878 return G722_PT;
879 default:
880 /* Shouldn't happen */
881 return OPUS_PT;
882 }
883 }
884
janus_videocodec_name(janus_videocodec vcodec)885 const char *janus_videocodec_name(janus_videocodec vcodec) {
886 switch(vcodec) {
887 case JANUS_VIDEOCODEC_NONE:
888 return "none";
889 case JANUS_VIDEOCODEC_VP8:
890 return "vp8";
891 case JANUS_VIDEOCODEC_VP9:
892 return "vp9";
893 case JANUS_VIDEOCODEC_H264:
894 return "h264";
895 case JANUS_VIDEOCODEC_AV1:
896 return "av1";
897 case JANUS_VIDEOCODEC_H265:
898 return "h265";
899 default:
900 /* Shouldn't happen */
901 return "vp8";
902 }
903 }
janus_videocodec_from_name(const char * name)904 janus_videocodec janus_videocodec_from_name(const char *name) {
905 if(name == NULL)
906 return JANUS_VIDEOCODEC_NONE;
907 else if(!strcasecmp(name, "vp8"))
908 return JANUS_VIDEOCODEC_VP8;
909 else if(!strcasecmp(name, "vp9"))
910 return JANUS_VIDEOCODEC_VP9;
911 else if(!strcasecmp(name, "h264"))
912 return JANUS_VIDEOCODEC_H264;
913 else if(!strcasecmp(name, "av1"))
914 return JANUS_VIDEOCODEC_AV1;
915 else if(!strcasecmp(name, "h265"))
916 return JANUS_VIDEOCODEC_H265;
917 JANUS_LOG(LOG_WARN, "Unsupported video codec '%s'\n", name);
918 return JANUS_VIDEOCODEC_NONE;
919 }
janus_videocodec_pt(janus_videocodec vcodec)920 int janus_videocodec_pt(janus_videocodec vcodec) {
921 switch(vcodec) {
922 case JANUS_VIDEOCODEC_NONE:
923 return -1;
924 case JANUS_VIDEOCODEC_VP8:
925 return VP8_PT;
926 case JANUS_VIDEOCODEC_VP9:
927 return VP9_PT;
928 case JANUS_VIDEOCODEC_H264:
929 return H264_PT;
930 case JANUS_VIDEOCODEC_AV1:
931 return AV1_PT;
932 case JANUS_VIDEOCODEC_H265:
933 return H265_PT;
934 default:
935 /* Shouldn't happen */
936 return VP8_PT;
937 }
938 }
939
janus_rtp_simulcasting_context_reset(janus_rtp_simulcasting_context * context)940 void janus_rtp_simulcasting_context_reset(janus_rtp_simulcasting_context *context) {
941 if(context == NULL)
942 return;
943 /* Reset the context values */
944 memset(context, 0, sizeof(*context));
945 context->rid_ext_id = -1;
946 context->substream = -1;
947 context->substream_target_temp = -1;
948 context->templayer = -1;
949 }
950
janus_rtp_simulcasting_prepare(json_t * simulcast,int * rid_ext_id,uint32_t * ssrcs,char ** rids)951 void janus_rtp_simulcasting_prepare(json_t *simulcast, int *rid_ext_id, uint32_t *ssrcs, char **rids) {
952 if(simulcast == NULL)
953 return;
954 json_t *r = json_object_get(simulcast, "rids");
955 json_t *s = json_object_get(simulcast, "ssrcs");
956 if(r && json_array_size(r) > 0) {
957 JANUS_LOG(LOG_VERB, " -- Simulcasting is rid based\n");
958 size_t i = 0;
959 int count = json_array_size(r);
960 for(i=count; i > 0; i--) {
961 json_t *rid = json_array_get(r, i-1);
962 if(rid && json_is_string(rid) && rids)
963 rids[count-i] = g_strdup(json_string_value(rid));
964 }
965 json_t *rid_ext = json_object_get(simulcast, "rid-ext");
966 if(rid_ext_id != NULL)
967 *rid_ext_id = json_integer_value(rid_ext);
968 } else if(s && json_array_size(s) > 0) {
969 JANUS_LOG(LOG_VERB, " -- Simulcasting is SSRC based\n");
970 size_t i = 0;
971 for(i=0; i<json_array_size(s); i++) {
972 if(i == 3)
973 break;
974 json_t *ssrc = json_array_get(s, i);
975 if(ssrc && json_is_integer(ssrc) && ssrcs)
976 ssrcs[i] = json_integer_value(ssrc);
977 }
978 }
979 }
980
janus_rtp_simulcasting_context_process_rtp(janus_rtp_simulcasting_context * context,char * buf,int len,uint32_t * ssrcs,char ** rids,janus_videocodec vcodec,janus_rtp_switching_context * sc)981 gboolean janus_rtp_simulcasting_context_process_rtp(janus_rtp_simulcasting_context *context,
982 char *buf, int len, uint32_t *ssrcs, char **rids,
983 janus_videocodec vcodec, janus_rtp_switching_context *sc) {
984 if(!context || !buf || len < 1)
985 return FALSE;
986 janus_rtp_header *header = (janus_rtp_header *)buf;
987 uint32_t ssrc = ntohl(header->ssrc);
988 int substream = -1;
989 if(ssrc == *(ssrcs)) {
990 substream = 0;
991 } else if(ssrc == *(ssrcs+1)) {
992 substream = 1;
993 } else if(ssrc == *(ssrcs+2)) {
994 substream = 2;
995 } else {
996 /* We don't recognize this SSRC, check if rid can help us */
997 if(context->rid_ext_id < 1 || rids == NULL)
998 return FALSE;
999 char sdes_item[16];
1000 if(janus_rtp_header_extension_parse_rid(buf, len, context->rid_ext_id, sdes_item, sizeof(sdes_item)) != 0)
1001 return FALSE;
1002 if(rids[0] != NULL && !strcmp(rids[0], sdes_item)) {
1003 JANUS_LOG(LOG_VERB, "Simulcasting: rid=%s --> ssrc=%"SCNu32"\n", sdes_item, ssrc);
1004 *(ssrcs) = ssrc;
1005 substream = 0;
1006 } else if(rids[1] != NULL && !strcmp(rids[1], sdes_item)) {
1007 JANUS_LOG(LOG_VERB, "Simulcasting: rid=%s --> ssrc=%"SCNu32"\n", sdes_item, ssrc);
1008 *(ssrcs+1) = ssrc;
1009 substream = 1;
1010 } else if(rids[2] != NULL && !strcmp(rids[2], sdes_item)) {
1011 JANUS_LOG(LOG_VERB, "Simulcasting: rid=%s --> ssrc=%"SCNu32"\n", sdes_item, ssrc);
1012 *(ssrcs+2) = ssrc;
1013 substream = 2;
1014 } else {
1015 JANUS_LOG(LOG_WARN, "Simulcasting: unknown rid '%s'...\n", sdes_item);
1016 return FALSE;
1017 }
1018 }
1019 /* Reset the flags */
1020 context->changed_substream = FALSE;
1021 context->changed_temporal = FALSE;
1022 context->need_pli = FALSE;
1023 gint64 now = janus_get_monotonic_time();
1024 /* Access the packet payload */
1025 int plen = 0;
1026 char *payload = janus_rtp_payload(buf, len, &plen);
1027 if(payload == NULL)
1028 return FALSE;
1029 /* Check what's our target */
1030 if(context->substream_target_temp != -1 && (substream > context->substream_target_temp ||
1031 context->substream_target <= context->substream_target_temp)) {
1032 /* We either just received media on a substream that is higher than
1033 * the target we dropped to (which means the one we want is now flowing
1034 * again) or we've been requested a lower substream target instead */
1035 context->substream_target_temp = -1;
1036 }
1037 int target = (context->substream_target_temp == -1) ? context->substream_target : context->substream_target_temp;
1038 /* Check what we need to do with the packet */
1039 if(context->substream == -1) {
1040 if((vcodec == JANUS_VIDEOCODEC_VP8 && janus_vp8_is_keyframe(payload, plen)) ||
1041 (vcodec == JANUS_VIDEOCODEC_H264 && janus_h264_is_keyframe(payload, plen))) {
1042 context->substream = substream;
1043 /* Notify the caller that the substream changed */
1044 context->changed_substream = TRUE;
1045 context->last_relayed = now;
1046 } else {
1047 /* Don't relay anything until we get a keyframe */
1048 return FALSE;
1049 }
1050 } else if(context->substream != target) {
1051 /* We're not on the substream we'd like: let's wait for a keyframe on the target */
1052 if(((context->substream < target && substream > context->substream) ||
1053 (context->substream > target && substream < context->substream)) &&
1054 ((vcodec == JANUS_VIDEOCODEC_VP8 && janus_vp8_is_keyframe(payload, plen)) ||
1055 (vcodec == JANUS_VIDEOCODEC_H264 && janus_h264_is_keyframe(payload, plen)))) {
1056 JANUS_LOG(LOG_VERB, "Received keyframe on #%d (SSRC %"SCNu32"), switching (was #%d/%"SCNu32")\n",
1057 substream, ssrc, context->substream, *(ssrcs + context->substream));
1058 context->substream = substream;
1059 /* Notify the caller that the substream changed */
1060 context->changed_substream = TRUE;
1061 context->last_relayed = now;
1062 }
1063 }
1064 /* If we haven't received our desired substream yet, let's drop temporarily */
1065 if(context->last_relayed == 0) {
1066 /* Let's start slow */
1067 context->last_relayed = now;
1068 } else if(context->substream > 0) {
1069 /* Check if too much time went by with no packet relayed */
1070 if((now - context->last_relayed) > (context->drop_trigger ? context->drop_trigger : 250000)) {
1071 context->last_relayed = now;
1072 if(context->substream != substream && context->substream_target_temp != 0) {
1073 if(context->substream_target > substream) {
1074 int prev_target = context->substream_target_temp;
1075 if(context->substream_target_temp == -1)
1076 context->substream_target_temp = context->substream_target - 1;
1077 else
1078 context->substream_target_temp--;
1079 if(context->substream_target_temp < 0)
1080 context->substream_target_temp = 0;
1081 if(context->substream_target_temp != prev_target) {
1082 JANUS_LOG(LOG_WARN, "No packet received on substream %d for a while, falling back to %d\n",
1083 context->substream, context->substream_target_temp);
1084 /* Notify the caller that we (still) need a PLI */
1085 context->need_pli = TRUE;
1086 }
1087 }
1088 }
1089 }
1090 }
1091 /* Do we need to drop this? */
1092 if(context->substream < 0)
1093 return FALSE;
1094 if(substream != context->substream) {
1095 JANUS_LOG(LOG_HUGE, "Dropping packet (it's from SSRC %"SCNu32", but we're only relaying SSRC %"SCNu32" now\n",
1096 ssrc, *(ssrcs + context->substream));
1097 return FALSE;
1098 }
1099 context->last_relayed = janus_get_monotonic_time();
1100 /* Temporal layers are only available for VP8, so don't do anything else for other codecs */
1101 if(vcodec == JANUS_VIDEOCODEC_VP8) {
1102 /* Check if there's any temporal scalability to take into account */
1103 uint16_t picid = 0;
1104 uint8_t tlzi = 0;
1105 uint8_t tid = 0;
1106 uint8_t ybit = 0;
1107 uint8_t keyidx = 0;
1108 if(janus_vp8_parse_descriptor(payload, plen, &picid, &tlzi, &tid, &ybit, &keyidx) == 0) {
1109 //~ JANUS_LOG(LOG_WARN, "%"SCNu16", %u, %u, %u, %u\n", picid, tlzi, tid, ybit, keyidx);
1110 if(context->templayer != context->templayer_target && tid == context->templayer_target) {
1111 /* FIXME We should be smarter in deciding when to switch */
1112 context->templayer = context->templayer_target;
1113 /* Notify the caller that the temporal layer changed */
1114 context->changed_temporal = TRUE;
1115 }
1116 if(context->templayer != -1 && tid > context->templayer) {
1117 JANUS_LOG(LOG_HUGE, "Dropping packet (it's temporal layer %d, but we're capping at %d)\n",
1118 tid, context->templayer);
1119 /* We increase the base sequence number, or there will be gaps when delivering later */
1120 if(sc)
1121 sc->v_base_seq++;
1122 return FALSE;
1123 }
1124 }
1125 }
1126 /* If we got here, the packet can be relayed */
1127 return TRUE;
1128 }
1129