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