Revision 0351dafc sdp.c

View differences:

sdp.c
2 2
 * \author   Lorenzo Miniero <lorenzo@meetecho.com>
3 3
 * \copyright GNU General Public License v3
4 4
 * \brief    SDP processing
5
 * \details  Implementation (based on the Sofia-SDP stack) of the SDP
5
 * \details  Implementation of an SDP
6 6
 * parser/merger/generator in the gateway. Each SDP coming from peers is
7 7
 * stripped/anonymized before it is passed to the plugins: all
8 8
 * DTLS/ICE/transport related information is removed, only leaving the
9 9
 * relevant information in place. SDP coming from plugins is stripped/anonymized
10 10
 * as well, and merged with the proper DTLS/ICE/transport information before
11
 * it is sent to the peers.
11
 * it is sent to the peers. The actual SDP processing (parsing SDP strings,
12
 * representation of SDP as an internal format, and so on) is done via
13
 * the tools provided in sdp-utils.h.
12 14
 * 
13 15
 * \ingroup protocols
14 16
 * \ref protocols
......
22 24
#include "debug.h"
23 25

  
24 26

  
25
static su_home_t *home = NULL;
26

  
27

  
28 27
/* SDP Initialization */
29 28
int janus_sdp_init(void) {
30
	home = su_home_new(sizeof(su_home_t));
31
	if(su_home_init(home) < 0) {
32
		JANUS_LOG(LOG_FATAL, "Ops, error setting up sofia-sdp?\n");
33
		return -1;
34
	}
29
	/* Nothing to do here, maybe in the future */
35 30
	return 0;
36 31
}
37 32

  
38 33
void janus_sdp_deinit(void) {
39
	su_home_deinit(home);
40
	su_home_unref(home);
41
	home = NULL;
34
	/* Nothing to do here, maybe in the future */
42 35
}
43 36

  
44 37

  
45
/* Fake attribute we use for the sendrecv hack */
46
static sdp_attribute_t fakedir = {
47
	.a_size = sizeof(sdp_attribute_t),
48
	.a_name = "jfmod",
49
	.a_value = "sr"
50
};
51

  
52

  
53
/* SDP parser */
54
void janus_sdp_free(janus_sdp *sdp) {
55
	if(!sdp)
56
		return;
57
	sdp_parser_t *parser = (sdp_parser_t *)sdp->parser;
58
	if(parser)
59
		sdp_parser_free(parser);
60
	sdp->parser = NULL;
61
	sdp->sdp = NULL;
62
	g_free(sdp);
63
	sdp = NULL;
64
}
65

  
66 38
/* Pre-parse SDP: is this SDP valid? how many audio/video lines? any features to take into account? */
67 39
janus_sdp *janus_sdp_preparse(const char *jsep_sdp, int *audio, int *video, int *data, int *bundle, int *rtcpmux, int *trickle) {
68 40
	if(!jsep_sdp || !audio || !video || !data || !bundle || !rtcpmux || !trickle) {
69 41
		JANUS_LOG(LOG_ERR, "  Can't preparse, invalid arduments\n");
70 42
		return NULL;
71 43
	}
72
	sdp_parser_t *parser = sdp_parse(home, jsep_sdp, strlen(jsep_sdp), 0);
73
	sdp_session_t *parsed_sdp = sdp_session(parser);
44
	janus_sdp *parsed_sdp = janus_sdp_import(jsep_sdp);
74 45
	if(!parsed_sdp) {
75
		JANUS_LOG(LOG_ERR, "  Error parsing SDP? %s\n", sdp_parsing_error(parser));
76
		sdp_parser_free(parser);
46
		JANUS_LOG(LOG_ERR, "  Error parsing SDP?\n");
77 47
		/* Invalid SDP */
78 48
		return NULL;
79 49
	}
80
	sdp_media_t *m = parsed_sdp->sdp_media;
81
	while(m) {
82
		if(m->m_type == sdp_media_audio && m->m_port > 0) {
50
	/* Look for m-lines */
51
	GList *temp = parsed_sdp->m_lines;
52
	while(temp) {
53
		janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
54
		if(m->type == JANUS_SDP_AUDIO && m->port > 0) {
83 55
			*audio = *audio + 1;
84
		} else if(m->m_type == sdp_media_video && m->m_port > 0) {
56
		} else if(m->type == JANUS_SDP_VIDEO && m->port > 0) {
85 57
			*video = *video + 1;
86 58
		}
87
		m = m->m_next;
59
		temp = temp->next;
88 60
	}
89 61
#ifdef HAVE_SCTP
90 62
	*data = (strstr(jsep_sdp, "DTLS/SCTP") && !strstr(jsep_sdp, " 0 DTLS/SCTP")) ? 1 : 0;	/* FIXME This is a really hacky way of checking... */
......
96 68
	//~ *trickle = (strstr(jsep_sdp, "trickle") || strstr(jsep_sdp, "google-ice") || strstr(jsep_sdp, "Mozilla")) ? 1 : 0;	/* FIXME This is a really hacky way of checking... */
97 69
	/* FIXME We're assuming trickle is always supported, see https://github.com/meetecho/janus-gateway/issues/83 */
98 70
	*trickle = 1;
99
	janus_sdp *sdp = (janus_sdp *)g_malloc0(sizeof(janus_sdp));
100
	if(sdp == NULL) {
101
		JANUS_LOG(LOG_FATAL, "Memory error!\n");
102
		return NULL;
103
	}
104 71

  
105
	sdp->parser = parser;
106
	sdp->sdp = parsed_sdp;
107
	return sdp;
72
	return parsed_sdp;
108 73
}
109 74

  
110 75
/* Parse SDP */
111
int janus_sdp_parse(janus_ice_handle *handle, janus_sdp *sdp) {
112
	if(!handle || !sdp)
113
		return -1;
114
	sdp_session_t *remote_sdp = (sdp_session_t *)sdp->sdp;
115
	if(!remote_sdp)
76
int janus_sdp_parse(void *ice_handle, janus_sdp *remote_sdp) {
77
	if(!ice_handle || !remote_sdp)
116 78
		return -1;
79
	janus_ice_handle *handle = (janus_ice_handle *)ice_handle;
117 80
	janus_ice_stream *stream = NULL;
118 81
	gchar *ruser = NULL, *rpass = NULL, *rhashing = NULL, *rfingerprint = NULL;
119 82
	int audio = 0, video = 0;
120 83
#ifdef HAVE_SCTP
121 84
	int data = 0;
122 85
#endif
123
	/* Ok, let's start */
124
	sdp_attribute_t *a = remote_sdp->sdp_attributes;
125
	while(a) {
126
		if(a->a_name) {
127
			if(!strcasecmp(a->a_name, "fingerprint")) {
128
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Fingerprint (global) : %s\n", handle->handle_id, a->a_value);
129
				if(strcasestr(a->a_value, "sha-256 ") == a->a_value) {
86
	/* Ok, let's start with global attributes */
87
	GList *temp = remote_sdp->attributes;
88
	while(temp) {
89
		janus_sdp_attribute *a = (janus_sdp_attribute *)temp->data;
90
		if(a && a->name) {
91
			if(!strcasecmp(a->name, "fingerprint")) {
92
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Fingerprint (global) : %s\n", handle->handle_id, a->value);
93
				if(strcasestr(a->value, "sha-256 ") == a->value) {
130 94
					rhashing = g_strdup("sha-256");
131
					rfingerprint = g_strdup(a->a_value + strlen("sha-256 "));
132
				} else if(strcasestr(a->a_value, "sha-1 ") == a->a_value) {
95
					rfingerprint = g_strdup(a->value + strlen("sha-256 "));
96
				} else if(strcasestr(a->value, "sha-1 ") == a->value) {
133 97
					JANUS_LOG(LOG_WARN, "[%"SCNu64"]  Hashing algorithm not the one we expected (sha-1 instead of sha-256), but that's ok\n", handle->handle_id);
134 98
					rhashing = g_strdup("sha-1");
135
					rfingerprint = g_strdup(a->a_value + strlen("sha-1 "));
99
					rfingerprint = g_strdup(a->value + strlen("sha-1 "));
136 100
				} else {
137 101
					/* FIXME We should handle this somehow anyway... OpenSSL supports them all */
138 102
					JANUS_LOG(LOG_WARN, "[%"SCNu64"]  Hashing algorithm not the one we expected (sha-256/sha-1), *NOT* cool\n", handle->handle_id);
139 103
				}
140
			} else if(!strcasecmp(a->a_name, "ice-ufrag")) {
141
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE ufrag (global):   %s\n", handle->handle_id, a->a_value);
142
				ruser = g_strdup(a->a_value);
143
			} else if(!strcasecmp(a->a_name, "ice-pwd")) {
144
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE pwd (global):     %s\n", handle->handle_id, a->a_value);
145
				rpass = g_strdup(a->a_value);
104
			} else if(!strcasecmp(a->name, "ice-ufrag")) {
105
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE ufrag (global):   %s\n", handle->handle_id, a->value);
106
				ruser = g_strdup(a->value);
107
			} else if(!strcasecmp(a->name, "ice-pwd")) {
108
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE pwd (global):     %s\n", handle->handle_id, a->value);
109
				rpass = g_strdup(a->value);
146 110
			}
147 111
		}
148
		a = a->a_next;
112
		temp = temp->next;
149 113
	}
150
	sdp_media_t *m = remote_sdp->sdp_media;
151
	while(m) {
152
		/* What media type is this? */
153
		if(m->m_type == sdp_media_audio) {
154
			if(handle->rtp_profile == NULL && m->m_proto_name != NULL)
155
				handle->rtp_profile = g_strdup(m->m_proto_name);
156
			if(m->m_port > 0) {
114
	/* Now go on with m-line and their attributes */
115
	temp = remote_sdp->m_lines;
116
	while(temp) {
117
		janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
118
		if(m->type == JANUS_SDP_AUDIO) {
119
			if(handle->rtp_profile == NULL)
120
				handle->rtp_profile = g_strdup(m->proto);
121
			if(m->port > 0) {
157 122
				audio++;
158 123
				if(audio > 1) {
159
					m = m->m_next;
124
					temp = temp->next;
160 125
					continue;
161 126
				}
162 127
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing audio candidates (stream=%d)...\n", handle->handle_id, handle->audio_id);
......
166 131
				janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_AUDIO);
167 132
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio rejected by peer...\n", handle->handle_id);
168 133
			}
169
		} else if(m->m_type == sdp_media_video) {
170
			if(handle->rtp_profile == NULL && m->m_proto_name != NULL)
171
				handle->rtp_profile = g_strdup(m->m_proto_name);
172
			if(m->m_port > 0) {
134
		} else if(m->type == JANUS_SDP_VIDEO) {
135
			if(handle->rtp_profile == NULL)
136
				handle->rtp_profile = g_strdup(m->proto);
137
			if(m->port > 0) {
173 138
				video++;
174 139
				if(video > 1) {
175
					m = m->m_next;
140
					temp = temp->next;
176 141
					continue;
177 142
				}
178 143
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing video candidates (stream=%d)...\n", handle->handle_id, handle->video_id);
......
188 153
				janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_HAS_VIDEO);
189 154
			}
190 155
#ifdef HAVE_SCTP
191
		} else if(m->m_type == sdp_media_application) {
156
		} else if(m->type == JANUS_SDP_APPLICATION) {
192 157
			/* Is this SCTP for DataChannels? */
193
			if(m->m_proto_name != NULL && !strcasecmp(m->m_proto_name, "DTLS/SCTP")) {
194
				if(m->m_port > 0) {
158
			if(!strcasecmp(m->proto, "DTLS/SCTP")) {
159
				if(m->port > 0) {
195 160
					/* Yep */
196 161
					data++;
197 162
					if(data > 1) {
198
						m = m->m_next;
163
						temp = temp->next;
199 164
						continue;
200 165
					}
201 166
					JANUS_LOG(LOG_VERB, "[%"SCNu64"] Parsing SCTP candidates (stream=%d)...\n", handle->handle_id, handle->video_id);
......
207 172
					}
208 173
					if(stream == NULL) {
209 174
						JANUS_LOG(LOG_WARN, "No valid stream for data??\n");
175
						temp = temp->next;
210 176
						continue;
211 177
					}
212 178
				}
......
218 184
#endif
219 185
		} else {
220 186
			JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping disabled/unsupported media line...\n", handle->handle_id);
221
			m = m->m_next;
187
			temp = temp->next;
222 188
			continue;
223 189
		}
224 190
		/* Look for mid, ICE credentials and fingerprint first: check media attributes */
225
		a = m->m_attributes;
226
		while(a) {
227
			if(a->a_name) {
228
				if(!strcasecmp(a->a_name, "mid")) {
191
		GList *tempA = m->attributes;
192
		while(tempA) {
193
			janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
194
			if(a->name) {
195
				if(!strcasecmp(a->name, "mid")) {
229 196
					/* Found mid attribute */
230
					if(m->m_type == sdp_media_audio && m->m_port > 0) {
231
						JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio mid: %s\n", handle->handle_id, a->a_value);
232
						handle->audio_mid = g_strdup(a->a_value);
233
					} else if(m->m_type == sdp_media_video && m->m_port > 0) {
234
						JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video mid: %s\n", handle->handle_id, a->a_value);
235
						handle->video_mid = g_strdup(a->a_value);
197
					if(m->type == JANUS_SDP_AUDIO && m->port > 0) {
198
						JANUS_LOG(LOG_VERB, "[%"SCNu64"] Audio mid: %s\n", handle->handle_id, a->value);
199
						handle->audio_mid = g_strdup(a->value);
200
					} else if(m->type == JANUS_SDP_VIDEO && m->port > 0) {
201
						JANUS_LOG(LOG_VERB, "[%"SCNu64"] Video mid: %s\n", handle->handle_id, a->value);
202
						handle->video_mid = g_strdup(a->value);
236 203
#ifdef HAVE_SCTP
237
					} else if(m->m_type == sdp_media_application) {
238
						JANUS_LOG(LOG_VERB, "[%"SCNu64"] Data Channel mid: %s\n", handle->handle_id, a->a_value);
239
						handle->data_mid = g_strdup(a->a_value);
204
					} else if(m->type == JANUS_SDP_APPLICATION) {
205
						JANUS_LOG(LOG_VERB, "[%"SCNu64"] Data Channel mid: %s\n", handle->handle_id, a->value);
206
						handle->data_mid = g_strdup(a->value);
240 207
#endif
241 208
					}
242
				} else if(!strcasecmp(a->a_name, "fingerprint")) {
243
					JANUS_LOG(LOG_VERB, "[%"SCNu64"] Fingerprint (local) : %s\n", handle->handle_id, a->a_value);
244
					if(strcasestr(a->a_value, "sha-256 ") == a->a_value) {
209
				} else if(!strcasecmp(a->name, "fingerprint")) {
210
					JANUS_LOG(LOG_VERB, "[%"SCNu64"] Fingerprint (local) : %s\n", handle->handle_id, a->value);
211
					if(strcasestr(a->value, "sha-256 ") == a->value) {
245 212
						if(rhashing)
246 213
							g_free(rhashing);	/* FIXME We're overwriting the global one, if any */
247 214
						rhashing = g_strdup("sha-256");
248 215
						if(rfingerprint)
249 216
							g_free(rfingerprint);	/* FIXME We're overwriting the global one, if any */
250
						rfingerprint = g_strdup(a->a_value + strlen("sha-256 "));
251
					} else if(strcasestr(a->a_value, "sha-1 ") == a->a_value) {
217
						rfingerprint = g_strdup(a->value + strlen("sha-256 "));
218
					} else if(strcasestr(a->value, "sha-1 ") == a->value) {
252 219
						JANUS_LOG(LOG_WARN, "[%"SCNu64"]  Hashing algorithm not the one we expected (sha-1 instead of sha-256), but that's ok\n", handle->handle_id);
253 220
						if(rhashing)
254 221
							g_free(rhashing);	/* FIXME We're overwriting the global one, if any */
255 222
						rhashing = g_strdup("sha-1");
256 223
						if(rfingerprint)
257 224
							g_free(rfingerprint);	/* FIXME We're overwriting the global one, if any */
258
						rfingerprint = g_strdup(a->a_value + strlen("sha-1 "));
225
						rfingerprint = g_strdup(a->value + strlen("sha-1 "));
259 226
					} else {
260 227
						/* FIXME We should handle this somehow anyway... OpenSSL supports them all */
261 228
						JANUS_LOG(LOG_WARN, "[%"SCNu64"]  Hashing algorithm not the one we expected (sha-256), *NOT* cool\n", handle->handle_id);
262 229
					}
263
				} else if(!strcasecmp(a->a_name, "setup")) {
264
					JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS setup (local):  %s\n", handle->handle_id, a->a_value);
265
					if(!strcasecmp(a->a_value, "actpass") || !strcasecmp(a->a_value, "passive"))
230
				} else if(!strcasecmp(a->name, "setup")) {
231
					JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS setup (local):  %s\n", handle->handle_id, a->value);
232
					if(!strcasecmp(a->value, "actpass") || !strcasecmp(a->value, "passive"))
266 233
						stream->dtls_role = JANUS_DTLS_ROLE_CLIENT;
267
					else if(!strcasecmp(a->a_value, "active"))
234
					else if(!strcasecmp(a->value, "active"))
268 235
						stream->dtls_role = JANUS_DTLS_ROLE_SERVER;
269 236
					/* TODO Handle holdconn... */
270
				} else if(!strcasecmp(a->a_name, "ice-ufrag")) {
271
					JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE ufrag (local):   %s\n", handle->handle_id, a->a_value);
237
				} else if(!strcasecmp(a->name, "ice-ufrag")) {
238
					JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE ufrag (local):   %s\n", handle->handle_id, a->value);
272 239
					if(ruser)
273 240
						g_free(ruser);	/* FIXME We're overwriting the global one, if any */
274
					ruser = g_strdup(a->a_value);
275
				} else if(!strcasecmp(a->a_name, "ice-pwd")) {
276
					JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE pwd (local):     %s\n", handle->handle_id, a->a_value);
241
					ruser = g_strdup(a->value);
242
				} else if(!strcasecmp(a->name, "ice-pwd")) {
243
					JANUS_LOG(LOG_VERB, "[%"SCNu64"] ICE pwd (local):     %s\n", handle->handle_id, a->value);
277 244
					if(rpass)
278 245
						g_free(rpass);	/* FIXME We're overwriting the global one, if any */
279
					rpass = g_strdup(a->a_value);
246
					rpass = g_strdup(a->value);
280 247
				}
281 248
			}
282
			a = a->a_next;
249
			tempA = tempA->next;
283 250
		}
284 251
		if(!ruser || !rpass || !rfingerprint || !rhashing) {
285 252
			/* Missing mandatory information, failure... */
......
312 279
			g_free(stream->rpass);
313 280
		stream->rpass = g_strdup(rpass);
314 281
		/* Now look for candidates and other info */
315
		a = m->m_attributes;
316
		while(a) {
317
			if(a->a_name) {
318
				if(!strcasecmp(a->a_name, "candidate")) {
319
					if(m->m_type == sdp_media_video && handle->audio_id > 0 && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
282
		tempA = m->attributes;
283
		while(tempA) {
284
			janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
285
			if(a->name) {
286
				if(!strcasecmp(a->name, "candidate")) {
287
					if(m->type == JANUS_SDP_VIDEO && handle->audio_id > 0 && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
320 288
						JANUS_LOG(LOG_VERB, "[%"SCNu64"] This is a video candidate but we're bundling, ignoring...\n", handle->handle_id);
321 289
#ifdef HAVE_SCTP
322
					} else if(m->m_type == sdp_media_application && (handle->audio_id > 0 || handle->video_id > 0) && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
290
					} else if(m->type == JANUS_SDP_APPLICATION && (handle->audio_id > 0 || handle->video_id > 0) && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE)) {
323 291
						JANUS_LOG(LOG_VERB, "[%"SCNu64"] This is a SCTP candidate but we're bundling, ignoring...\n", handle->handle_id);
324 292
#endif
325 293
					} else {
326
						int res = janus_sdp_parse_candidate(stream, (const char *)a->a_value, 0);
294
						int res = janus_sdp_parse_candidate(stream, (const char *)a->value, 0);
327 295
						if(res != 0) {
328 296
							JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse candidate... (%d)\n", handle->handle_id, res);
329 297
						}
330 298
					}
331 299
				}
332
				if(!strcasecmp(a->a_name, "ssrc")) {
333
					int res = janus_sdp_parse_ssrc(stream, (const char *)a->a_value, m->m_type == sdp_media_video);
300
				if(!strcasecmp(a->name, "ssrc")) {
301
					int res = janus_sdp_parse_ssrc(stream, (const char *)a->value, m->type == JANUS_SDP_VIDEO);
334 302
					if(res != 0) {
335 303
						JANUS_LOG(LOG_ERR, "[%"SCNu64"] Failed to parse SSRC attribute... (%d)\n", handle->handle_id, res);
336 304
					}
337 305
				}
338 306
#ifdef HAVE_SCTP
339
				if(!strcasecmp(a->a_name, "sctpmap")) {
307
				if(!strcasecmp(a->name, "sctpmap")) {
340 308
					/* TODO Parse sctpmap line to get the UDP-port value and the number of channels */
341
					JANUS_LOG(LOG_VERB, "Got a sctpmap attribute: %s\n", a->a_value);
309
					JANUS_LOG(LOG_VERB, "Got a sctpmap attribute: %s\n", a->value);
342 310
				}
343 311
#endif
344 312
			}
345
			a = a->a_next;
313
			tempA = tempA->next;
346 314
		}
347
		m = m->m_next;
315
		temp = temp->next;
348 316
	}
349 317
	if(ruser)
350 318
		g_free(ruser);
......
361 329
	return 0;	/* FIXME Handle errors better */
362 330
}
363 331

  
364
int janus_sdp_parse_candidate(janus_ice_stream *stream, const char *candidate, int trickle) {
365
	if(stream == NULL || candidate == NULL)
332
int janus_sdp_parse_candidate(void *ice_stream, const char *candidate, int trickle) {
333
	if(ice_stream == NULL || candidate == NULL)
366 334
		return -1;
335
	janus_ice_stream *stream = (janus_ice_stream *)ice_stream;
367 336
	janus_ice_handle *handle = stream->handle;
368 337
	if(handle == NULL)
369 338
		return -2;
......
561 530
	return 0;
562 531
}
563 532

  
564
int janus_sdp_parse_ssrc(janus_ice_stream *stream, const char *ssrc_attr, int video) {
565
	if(stream == NULL || ssrc_attr == NULL)
533
int janus_sdp_parse_ssrc(void *ice_stream, const char *ssrc_attr, int video) {
534
	if(ice_stream == NULL || ssrc_attr == NULL)
566 535
		return -1;
536
	janus_ice_stream *stream = (janus_ice_stream *)ice_stream;
567 537
	janus_ice_handle *handle = stream->handle;
568 538
	if(handle == NULL)
569 539
		return -2;
......
593 563
char *janus_sdp_anonymize(const char *sdp) {
594 564
	if(sdp == NULL)
595 565
		return NULL;
596
	sdp_session_t *anon = NULL;
597
	sdp_parser_t *parser = sdp_parse(home, sdp, strlen(sdp), 0);
598
	if(!(anon = sdp_session(parser))) {
599
		JANUS_LOG(LOG_ERR, "Error parsing/merging SDP: %s\n", sdp_parsing_error(parser));
600
		sdp_parser_free(parser);
566
	janus_sdp *anon = janus_sdp_import(sdp);
567
	if(anon == NULL) {
568
		JANUS_LOG(LOG_ERR, "Error parsing/anonymizing SDP\n");
601 569
		return NULL;
602 570
	}
603
	/* c= */
604
	if(anon->sdp_connection && anon->sdp_connection->c_address) {
605
		anon->sdp_connection->c_address = "1.1.1.1";
571
	int audio = 0, video = 0;
572
#ifdef HAVE_SCTP
573
	int data = 0;
574
#endif
575
		/* o= */
576
	if(anon->o_addr != NULL) {
577
		g_free(anon->o_addr);
578
		anon->o_ipv4 = TRUE;
579
		anon->o_addr = g_strdup("1.1.1.1");
606 580
	}
607
	/* a= */
608
	if(anon->sdp_attributes) {
581
		/* a= */
582
	GList *temp = anon->attributes;
583
	while(temp) {
584
		janus_sdp_attribute *a = (janus_sdp_attribute *)temp->data;
609 585
		/* These are attributes we handle ourselves, the plugins don't need them */
610
		while(sdp_attribute_find(anon->sdp_attributes, "ice-ufrag"))
611
			sdp_attribute_remove(&anon->sdp_attributes, "ice-ufrag");
612
		while(sdp_attribute_find(anon->sdp_attributes, "ice-pwd"))
613
			sdp_attribute_remove(&anon->sdp_attributes, "ice-pwd");
614
		while(sdp_attribute_find(anon->sdp_attributes, "ice-options"))
615
			sdp_attribute_remove(&anon->sdp_attributes, "ice-options");
616
		while(sdp_attribute_find(anon->sdp_attributes, "fingerprint"))
617
			sdp_attribute_remove(&anon->sdp_attributes, "fingerprint");
618
		while(sdp_attribute_find(anon->sdp_attributes, "group"))
619
			sdp_attribute_remove(&anon->sdp_attributes, "group");
620
		while(sdp_attribute_find(anon->sdp_attributes, "msid-semantic"))
621
			sdp_attribute_remove(&anon->sdp_attributes, "msid-semantic");
622
		while(sdp_attribute_find(anon->sdp_attributes, "rtcp-rsize"))
623
			sdp_attribute_remove(&anon->sdp_attributes, "rtcp-rsize");
586
		if(!strcasecmp(a->name, "ice-ufrag")
587
				|| !strcasecmp(a->name, "ice-pwd")
588
				|| !strcasecmp(a->name, "ice-options")
589
				|| !strcasecmp(a->name, "fingerprint")
590
				|| !strcasecmp(a->name, "group")
591
				|| !strcasecmp(a->name, "msid-semantic")
592
				|| !strcasecmp(a->name, "rtcp-rsize")) {
593
			anon->attributes = g_list_remove(anon->attributes, a);
594
			temp = anon->attributes;
595
			g_free(a->name);
596
			g_free(a->value);
597
			g_free(a);
598
			continue;
599
		}
600
		temp = temp->next;
601
		continue;
624 602
	}
625 603
		/* m= */
626
	if(anon->sdp_media) {
627
		int audio = 0, video = 0;
604
	temp = anon->m_lines;
605
	while(temp) {
606
		janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
607
		if(m->type == JANUS_SDP_AUDIO && m->port > 0) {
608
			audio++;
609
			m->port = audio == 1 ? 9 : 0;
610
		} else if(m->type == JANUS_SDP_VIDEO && m->port > 0) {
611
			video++;
612
			m->port = video == 1 ? 9 : 0;
628 613
#ifdef HAVE_SCTP
629
		int data = 0;
614
		} else if(m->type == JANUS_SDP_APPLICATION && m->port > 0) {
615
			data++;
616
			m->port = data == 1 ? 9 : 0;
630 617
#endif
631
		sdp_media_t *m = anon->sdp_media;
632
		while(m) {
633
			if(m->m_type == sdp_media_audio && m->m_port > 0) {
634
				audio++;
635
				m->m_port = audio == 1 ? 1 : 0;
636
			} else if(m->m_type == sdp_media_video && m->m_port > 0) {
637
				video++;
638
				m->m_port = video == 1 ? 1 : 0;
639
#ifdef HAVE_SCTP
640
			} else if(m->m_type == sdp_media_application) {
641
				if(m->m_proto_name != NULL && !strcasecmp(m->m_proto_name, "DTLS/SCTP") && m->m_port != 0) {
642
					data++;
643
					m->m_port = data == 1 ? 1 : 0;
644
				} else {
645
					m->m_port = 0;
646
				}
647
#endif
648
			} else {
649
				m->m_port = 0;
650
			}
651
				/* c= */
652
			if(m->m_connections) {
653
				sdp_connection_t *c = m->m_connections;
654
				while(c) {
655
					if(c->c_address) {
656
						c->c_address = "1.1.1.1";
657
					}
658
					c = c->c_next;
659
				}
618
		} else {
619
			m->port = 0;
620
		}
621
			/* c= */
622
		if(m->c_addr != NULL) {
623
			g_free(m->c_addr);
624
			m->c_ipv4 = TRUE;
625
			m->c_addr = g_strdup("1.1.1.1");
626
		}
627
			/* a= */
628
		GList *tempA = m->attributes;
629
		while(tempA) {
630
			janus_sdp_attribute *a = (janus_sdp_attribute *)tempA->data;
631
			/* These are attributes we handle ourselves, the plugins don't need them */
632
			if(!strcasecmp(a->name, "ice-ufrag")
633
					|| !strcasecmp(a->name, "ice-pwd")
634
					|| !strcasecmp(a->name, "ice-options")
635
					|| !strcasecmp(a->name, "crypto")
636
					|| !strcasecmp(a->name, "fingerprint")
637
					|| !strcasecmp(a->name, "setup")
638
					|| !strcasecmp(a->name, "connection")
639
					|| !strcasecmp(a->name, "group")
640
					|| !strcasecmp(a->name, "mid")
641
					|| !strcasecmp(a->name, "msid")
642
					|| !strcasecmp(a->name, "msid-semantic")
643
					|| !strcasecmp(a->name, "rtcp")
644
					|| !strcasecmp(a->name, "rtcp-mux")
645
					|| !strcasecmp(a->name, "rtcp-rsize")
646
					|| !strcasecmp(a->name, "candidate")
647
					|| !strcasecmp(a->name, "ssrc")
648
					|| !strcasecmp(a->name, "ssrc-group")
649
					|| !strcasecmp(a->name, "extmap")	/* TODO Actually implement RTP extensions */
650
					|| !strcasecmp(a->name, "sctpmap")) {
651
				m->attributes = g_list_remove(m->attributes, a);
652
				tempA = m->attributes;
653
				g_free(a->name);
654
				g_free(a->value);
655
				g_free(a);
656
				continue;
660 657
			}
661
				/* a= */
662
			if(m->m_attributes) {
663
				/* These are attributes we handle ourselves, the plugins don't need them */
664
				while(sdp_attribute_find(m->m_attributes, "ice-ufrag"))
665
					sdp_attribute_remove(&m->m_attributes, "ice-ufrag");
666
				while(sdp_attribute_find(m->m_attributes, "ice-pwd"))
667
					sdp_attribute_remove(&m->m_attributes, "ice-pwd");
668
				while(sdp_attribute_find(m->m_attributes, "ice-options"))
669
					sdp_attribute_remove(&m->m_attributes, "ice-options");
670
				while(sdp_attribute_find(m->m_attributes, "crypto"))
671
					sdp_attribute_remove(&m->m_attributes, "crypto");
672
				while(sdp_attribute_find(m->m_attributes, "fingerprint"))
673
					sdp_attribute_remove(&m->m_attributes, "fingerprint");
674
				while(sdp_attribute_find(m->m_attributes, "setup"))
675
					sdp_attribute_remove(&m->m_attributes, "setup");
676
				while(sdp_attribute_find(m->m_attributes, "connection"))
677
					sdp_attribute_remove(&m->m_attributes, "connection");
678
				while(sdp_attribute_find(m->m_attributes, "group"))
679
					sdp_attribute_remove(&m->m_attributes, "group");
680
				while(sdp_attribute_find(m->m_attributes, "mid"))
681
					sdp_attribute_remove(&m->m_attributes, "mid");
682
				while(sdp_attribute_find(m->m_attributes, "msid"))
683
					sdp_attribute_remove(&m->m_attributes, "msid");
684
				while(sdp_attribute_find(m->m_attributes, "msid-semantic"))
685
					sdp_attribute_remove(&m->m_attributes, "msid-semantic");
686
				while(sdp_attribute_find(m->m_attributes, "rtcp"))
687
					sdp_attribute_remove(&m->m_attributes, "rtcp");
688
				while(sdp_attribute_find(m->m_attributes, "rtcp-mux"))
689
					sdp_attribute_remove(&m->m_attributes, "rtcp-mux");
690
				while(sdp_attribute_find(m->m_attributes, "rtcp-rsize"))
691
					sdp_attribute_remove(&m->m_attributes, "rtcp-rsize");
692
				while(sdp_attribute_find(m->m_attributes, "candidate"))
693
					sdp_attribute_remove(&m->m_attributes, "candidate");
694
				while(sdp_attribute_find(m->m_attributes, "ssrc"))
695
					sdp_attribute_remove(&m->m_attributes, "ssrc");
696
				while(sdp_attribute_find(m->m_attributes, "ssrc-group"))
697
					sdp_attribute_remove(&m->m_attributes, "ssrc-group");
698
				while(sdp_attribute_find(m->m_attributes, "extmap"))	/* TODO Actually implement RTP extensions */
699
					sdp_attribute_remove(&m->m_attributes, "extmap");
700
				while(sdp_attribute_find(m->m_attributes, "sctpmap"))
701
					sdp_attribute_remove(&m->m_attributes, "sctpmap");
702
				/* Also remove attributes/formats we know we don't support now */
703
				GList *ptypes = NULL;
704
				sdp_attribute_t *a = m->m_attributes;
705
				while(a) {
706
					if(strstr(a->a_value, "red/90000") || strstr(a->a_value, "ulpfec/90000") || strstr(a->a_value, "rtx/90000")) {
707
						int ptype = atoi(a->a_value);
708
						ptypes = g_list_append(ptypes, GINT_TO_POINTER(ptype));
709
						JANUS_LOG(LOG_VERB, "Will remove payload type %d\n", ptype);
710
					}
711
					a = a->a_next;
712
				}
713
				if(ptypes) {
714
					GList *p = ptypes;
715
					while(p) {
716
						int ptype = GPOINTER_TO_INT(p->data);
717
						if(m->m_format) {
718
							sdp_list_t *fmt = m->m_format, *old = NULL;
719
							while(fmt) {
720
								int fmt_pt = atoi(fmt->l_text);
721
								if(fmt_pt == ptype) {
722
									if(!old) {
723
										m->m_format = fmt->l_next;
724
										fmt = m->m_format;
725
										continue;
726
									} else {
727
										old->l_next = fmt->l_next;
728
										fmt = fmt->l_next;
729
										continue;
730
									}
731
								}
732
								old = fmt;
733
								fmt = fmt->l_next;
734
							}
735
						}
736
						a = m->m_attributes;
737
						sdp_attribute_t *old = NULL;
738
						while(a) {
739
							int a_pt = atoi(a->a_value);
740
							if(a_pt == ptype) {
741
								if(!old) {
742
									m->m_attributes = a->a_next;
743
									a = m->m_attributes;
744
									continue;
745
								} else {
746
									old->a_next = a->a_next;
747
									a = a->a_next;
748
									continue;
749
								}
750
							}
751
							old = a;
752
							a = a->a_next;
753
						}
754
						p = g_list_remove(p, p->data);
658
			/* Also remove attributes/formats we know we don't support now */
659
			if(strstr(a->value, "red/90000") || strstr(a->value, "ulpfec/90000") || strstr(a->value, "rtx/90000")) {
660
				int ptype = atoi(a->value);
661
				JANUS_LOG(LOG_VERB, "Will remove payload type %d\n", ptype);
662
				/* Remove this attribute */
663
				m->attributes = g_list_remove(m->attributes, a);
664
				tempA = m->attributes;
665
				g_free(a->name);
666
				g_free(a->value);
667
				g_free(a);
668
				/* Also remove other attributes with the same payload type, and any reference from the m-line */
669
				m->ptypes = g_list_remove(m->ptypes, GINT_TO_POINTER(ptype));
670
				GList *tempAA = m->attributes;
671
				while(tempAA) {
672
					janus_sdp_attribute *aa = (janus_sdp_attribute *)tempAA->data;
673
					if(atoi(aa->value) == ptype) {
674
						if(tempAA == tempA)
675
							tempA = tempA->next;
676
						m->attributes = g_list_remove(m->attributes, aa);
677
						tempAA = m->attributes;
678
						g_free(aa->name);
679
						g_free(aa->value);
680
						g_free(aa);
681
						continue;
755 682
					}
683
					tempAA = tempAA->next;
756 684
				}
757 685
			}
758
			if(m->m_type != sdp_media_application && m->m_mode == sdp_sendrecv) {
759
				/* FIXME sendrecv hack: sofia-sdp doesn't print sendrecv, but we want it to */
760
				sdp_attribute_append(&m->m_attributes, &fakedir);
761
			}
762
			m = m->m_next;
686
			tempA = tempA->next;
763 687
		}
688
		temp = temp->next;
764 689
	}
765
	char buf[JANUS_BUFSIZE];
766
	sdp_printer_t *printer = sdp_print(home, anon, buf, JANUS_BUFSIZE, 0);
767
	if(sdp_message(printer)) {
768
		int retval = sdp_message_size(printer);
769
		sdp_printer_free(printer);
770
		sdp_parser_free(parser);
771
		/* FIXME Take care of the sendrecv hack, if needed */
772
		char *replace = strstr(buf, "a=jfmod:sr");
773
		while(replace != NULL) {
774
			memcpy(replace, "a=sendrecv", strlen("a=sendrecv"));
775
			replace++;
776
			replace = strstr(replace, "a=jfmod:sr");
777
		}
690
	char *buf = janus_sdp_export(anon);
691
	janus_sdp_free(anon);
692
	if(buf != NULL) {
778 693
		JANUS_LOG(LOG_VERB, " -------------------------------------------\n");
779
		JANUS_LOG(LOG_VERB, "  >> Anonymized (%zu --> %d bytes)\n", strlen(sdp), retval);
694
		JANUS_LOG(LOG_VERB, "  >> Anonymized (%zu --> %zu bytes)\n", strlen(sdp), strlen(buf));
780 695
		JANUS_LOG(LOG_VERB, " -------------------------------------------\n");
781 696
		JANUS_LOG(LOG_VERB, "%s\n", buf);
782
		return g_strdup(buf);
697
		return buf;
783 698
	} else {
784
		JANUS_LOG(LOG_ERR, "Error anonymizing SDP: %s\n", sdp_printing_error(printer));
785
		sdp_printer_free(printer);
786
		sdp_parser_free(parser);
699
		JANUS_LOG(LOG_ERR, "Error anonymizing SDP\n");
787 700
		return NULL;
788 701
	}
789 702
}
790 703

  
791
char *janus_sdp_merge(janus_ice_handle *handle, const char *origsdp) {
792
	if(handle == NULL || origsdp == NULL)
793
		return NULL;
794
	sdp_session_t *anon = NULL;
795
	sdp_parser_t *parser = sdp_parse(home, origsdp, strlen(origsdp), 0);
796
	if(!(anon = sdp_session(parser))) {
797
		JANUS_LOG(LOG_ERR, "[%"SCNu64"] Error parsing/merging SDP: %s\n", handle->handle_id, sdp_parsing_error(parser));
798
		sdp_parser_free(parser);
704
char *janus_sdp_merge(void *ice_handle, const char *origsdp) {
705
	if(ice_handle == NULL || origsdp == NULL)
799 706
		return NULL;
800
	}
801
	/* Prepare SDP to merge */
802
	gchar buffer[512];
803
	memset(buffer, 0, 512);
804
	char *sdp = (char*)g_malloc0(JANUS_BUFSIZE);
805
	if(sdp == NULL) {
806
		JANUS_LOG(LOG_FATAL, "Memory error!\n");
807
		sdp_parser_free(parser);
707
	janus_ice_handle *handle = (janus_ice_handle *)ice_handle;
708
	janus_ice_stream *stream = NULL;
709
	janus_sdp *anon = janus_sdp_import(origsdp);
710
	if(anon == NULL) {
711
		JANUS_LOG(LOG_ERR, "Error parsing/merging SDP\n");
808 712
		return NULL;
809 713
	}
810
	sdp[0] = '\0';
811 714
	char *rtp_profile = handle->rtp_profile ? handle->rtp_profile : (char *)"RTP/SAVPF";
812 715
	/* FIXME Any Plan B to take into account? */
813 716
	int planb = strstr(origsdp, "a=planb:") ? 1 : 0;
......
816 719
	} else {
817 720
		janus_flags_clear(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_PLAN_B);
818 721
	}
819
	/* Version v= */
820
	g_strlcat(sdp,
821
		"v=0\r\n", JANUS_BUFSIZE);
722
	gboolean ipv4 = !strstr(janus_get_public_ip(), ":");
822 723
	/* Origin o= */
823
	if(anon->sdp_origin) {
824
		g_snprintf(buffer, 512,
825
			"o=%s %"SCNu64" %"SCNu64" IN IP4 %s\r\n",
826
				anon->sdp_origin->o_username ? anon->sdp_origin->o_username : "-",
827
				anon->sdp_origin->o_id, anon->sdp_origin->o_version,
828
				janus_get_public_ip());
829
		g_strlcat(sdp, buffer, JANUS_BUFSIZE);
830
	} else {
831
		gint64 sessid = janus_get_real_time();
832
		gint64 version = sessid;	/* FIXME This needs to be increased when it changes, so time should be ok */
833
		g_snprintf(buffer, 512,
834
			"o=%s %"SCNi64" %"SCNi64" IN IP4 %s\r\n",
835
				"-", sessid, version, janus_get_public_ip());
836
		g_strlcat(sdp, buffer, JANUS_BUFSIZE);
724
	gint64 sessid = janus_get_real_time();
725
	if(anon->o_name == NULL)
726
		anon->o_name = g_strdup("-");
727
	if(anon->o_sessid == 0 || anon->o_version == 0) {
728
		anon->o_sessid = sessid;
729
		anon->o_version = 1;
837 730
	}
731
	anon->o_ipv4 = ipv4;
732
	g_free(anon->o_addr);
733
	anon->o_addr = g_strdup(janus_get_public_ip());
838 734
	/* Session name s= */
839
	if(anon->sdp_subject && strlen(anon->sdp_subject) > 0) {
840
		g_snprintf(buffer, 512, "s=%s\r\n", anon->sdp_subject);
841
	} else {
842
		g_snprintf(buffer, 512, "s=%s\r\n", "Meetecho Janus");
843
	}
844
	g_strlcat(sdp, buffer, JANUS_BUFSIZE);
845
	/* Timing t= */
846
	g_snprintf(buffer, 512,
847
		"t=%lu %lu\r\n", anon->sdp_time ? anon->sdp_time->t_start : 0, anon->sdp_time ? anon->sdp_time->t_stop : 0);
848
	g_strlcat(sdp, buffer, JANUS_BUFSIZE);
849
	/* ICE Full or Lite? */
850
	if(janus_ice_is_ice_lite_enabled()) {
851
		/* Janus is acting in ICE Lite mode, advertize this */
852
		g_strlcat(sdp, "a=ice-lite\r\n", JANUS_BUFSIZE);
853
	}
735
	if(anon->s_name == NULL)
736
		anon->s_name = g_strdup("Meetecho Janus");
854 737
	/* bundle: add new global attribute */
855 738
	int audio = (strstr(origsdp, "m=audio") != NULL);
856 739
	int video = (strstr(origsdp, "m=video") != NULL);
......
859 742
#else
860 743
	int data = 0;
861 744
#endif
862
	g_strlcat(sdp, "a=group:BUNDLE", JANUS_BUFSIZE);
745
	char buffer[2048], buffer_part[512];
746
	buffer[0] = '\0';
747
	buffer_part[0] = '\0';
748
	g_snprintf(buffer, sizeof(buffer), "BUNDLE");
863 749
	if(audio) {
864
		g_snprintf(buffer, 512,
750
		g_snprintf(buffer_part, sizeof(buffer_part),
865 751
			" %s", handle->audio_mid ? handle->audio_mid : "audio");
866
		g_strlcat(sdp, buffer, JANUS_BUFSIZE);
752
		g_strlcat(buffer, buffer_part, JANUS_BUFSIZE);
867 753
	}
868 754
	if(video) {
869
		g_snprintf(buffer, 512,
755
		g_snprintf(buffer_part, sizeof(buffer_part),
870 756
			" %s", handle->video_mid ? handle->video_mid : "video");
871
		g_strlcat(sdp, buffer, JANUS_BUFSIZE);
757
		g_strlcat(buffer, buffer_part, JANUS_BUFSIZE);
872 758
	}
873 759
	if(data) {
874
		g_snprintf(buffer, 512,
760
		g_snprintf(buffer_part, sizeof(buffer_part),
875 761
			" %s", handle->data_mid ? handle->data_mid : "data");
876
		g_strlcat(sdp, buffer, JANUS_BUFSIZE);
762
		g_strlcat(buffer, buffer_part, JANUS_BUFSIZE);
877 763
	}
878
	g_strlcat(sdp, "\r\n", JANUS_BUFSIZE);
764
	/* Global attributes: start with group */
765
	GList *first = anon->attributes;
766
#pragma GCC diagnostic ignored "-Wformat-security"
767
	janus_sdp_attribute *a = janus_sdp_attribute_create("group", buffer);
768
#pragma GCC diagnostic warning "-Wformat-security"
769
	anon->attributes = g_list_insert_before(anon->attributes, first, a);
879 770
	/* msid-semantic: add new global attribute */
880
	g_strlcat(sdp,
881
		"a=msid-semantic: WMS janus\r\n",
882
		JANUS_BUFSIZE);
883
	char wms[JANUS_BUFSIZE];
884
	memset(wms, 0, JANUS_BUFSIZE);
885
	g_strlcat(wms, "WMS", JANUS_BUFSIZE);
886
	/* Copy other global attributes, if any */
887
	if(anon->sdp_attributes) {
888
		sdp_attribute_t *a = anon->sdp_attributes;
889
		while(a) {
890
			if(a->a_value == NULL) {
891
				g_snprintf(buffer, 512,
892
					"a=%s\r\n", a->a_name);
893
				g_strlcat(sdp, buffer, JANUS_BUFSIZE);
894
			} else {
895
				g_snprintf(buffer, 512,
896
					"a=%s:%s\r\n", a->a_name, a->a_value);
897
				g_strlcat(sdp, buffer, JANUS_BUFSIZE);
898
			}
899
			a = a->a_next;
900
		}
771
	a = janus_sdp_attribute_create("msid-semantic", " WMS janus");
772
	anon->attributes = g_list_insert_before(anon->attributes, first, a);
773
	/* ICE Full or Lite? */
774
	if(janus_ice_is_ice_lite_enabled()) {
775
		/* Janus is acting in ICE Lite mode, advertize this */
776
		a = janus_sdp_attribute_create("ice-lite", NULL);
777
		anon->attributes = g_list_insert_before(anon->attributes, first, a);
901 778
	}
902
	gboolean ipv6 = strstr(janus_get_public_ip(), ":") != NULL;
903 779
	/* Media lines now */
904
	if(anon->sdp_media) {
905
		int audio = 0, video = 0;
780
	audio = 0;
781
	video = 0;
906 782
#ifdef HAVE_SCTP
907
		int data = 0;
783
	data = 0;
908 784
#endif
909
		sdp_media_t *m = anon->sdp_media;
910
		janus_ice_stream *stream = NULL;
911
		while(m) {
912
			if(m->m_type == sdp_media_audio && m->m_port > 0) {
913
				audio++;
914
				if(audio > 1 || !handle->audio_id) {
915
					JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping audio line (we have %d audio lines, and the id is %d)\n", handle->handle_id, audio, handle->audio_id);
916
					g_snprintf(buffer, 512,
917
						"m=audio 0 %s 0\r\n", rtp_profile);
918
					g_strlcat(sdp, buffer, JANUS_BUFSIZE);
919
					/* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */
920
					g_snprintf(buffer, 512,
921
						"c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip());
922
					g_strlcat(sdp, buffer, JANUS_BUFSIZE);
923
					g_strlcat(sdp, "a=inactive\r\n", JANUS_BUFSIZE);
924
					m = m->m_next;
925
					continue;
926
				}
927
				/* Audio */
928
				stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->audio_id));
929
				if(stream == NULL) {
930
					JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping audio line (invalid stream %d)\n", handle->handle_id, handle->audio_id);
931
					g_snprintf(buffer, 512,
932
						"m=audio 0 %s 0\r\n", rtp_profile);
933
					g_strlcat(sdp, buffer, JANUS_BUFSIZE);
934
					/* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */
935
					g_snprintf(buffer, 512,
936
						"c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip());
937
					g_strlcat(sdp, buffer, JANUS_BUFSIZE);
938
					g_strlcat(sdp, "a=inactive\r\n", JANUS_BUFSIZE);
939
					m = m->m_next;
940
					continue;
941
				}
942
				g_snprintf(buffer, 512,
943
					"m=audio 1 %s", rtp_profile);
944
				g_strlcat(sdp, buffer, JANUS_BUFSIZE);
945
			} else if(m->m_type == sdp_media_video && m->m_port > 0) {
946
				video++;
947
				gint id = handle->video_id;
785
	GList *temp = anon->m_lines;
786
	while(temp) {
787
		janus_sdp_mline *m = (janus_sdp_mline *)temp->data;
788
		first = m->attributes;
789
		/* Overwrite RTP profile for audio and video */
790
		if(m->type != JANUS_SDP_APPLICATION) {
791
			g_free(m->proto);
792
			m->proto = g_strdup(rtp_profile);
793
		}
794
		/* Media connection c= */
795
		g_free(m->c_addr);
796
		m->c_ipv4 = ipv4;
797
		m->c_addr = g_strdup(janus_get_public_ip());
798
		/* Check if we need to refuse the media or not */
799
		if(m->type == JANUS_SDP_AUDIO && m->port > 0) {
800
			audio++;
801
			if(audio > 1 || !handle->audio_id) {
802
				JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping audio line (we have %d audio lines, and the id is %d)\n", handle->handle_id, audio, handle->audio_id);
803
				m->port = 0;
804
				g_free(m->direction);
805
				m->direction = g_strdup("inactive");
806
				temp = temp->next;
807
				continue;
808
			}
809
			/* Audio */
810
			stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(handle->audio_id));
811
			if(stream == NULL) {
812
				JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping audio line (invalid stream %d)\n", handle->handle_id, handle->audio_id);
813
				m->port = 0;
814
				g_free(m->direction);
815
				m->direction = g_strdup("inactive");
816
				temp = temp->next;
817
				continue;
818
			}
819
		} else if(m->type == JANUS_SDP_VIDEO && m->port > 0) {
820
			video++;
821
			gint id = handle->video_id;
822
			if(id == 0 && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE))
823
				id = handle->audio_id > 0 ? handle->audio_id : handle->video_id;
824
			if(video > 1 || !id) {
825
				JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping video line (we have %d video lines, and the id is %d)\n", handle->handle_id, video,
826
					janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE) ? handle->audio_id : handle->video_id);
827
				m->port = 0;
828
				g_free(m->direction);
829
				m->direction = g_strdup("inactive");
830
				temp = temp->next;
831
				continue;
832
			}
833
			/* Video */
834
			stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(id));
835
			if(stream == NULL) {
836
				JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping video line (invalid stream %d)\n", handle->handle_id, id);
837
				m->port = 0;
838
				g_free(m->direction);
839
				m->direction = g_strdup("inactive");
840
				temp = temp->next;
841
				continue;
842
			}
843
#ifdef HAVE_SCTP
844
		} else if(m->type == JANUS_SDP_APPLICATION) {
845
			/* Is this SCTP for DataChannels? */
846
			if(!strcasecmp(m->proto, "DTLS/SCTP") && m->port > 0) {
847
				/* Yep */
848
				data++;
849
				gint id = handle->data_id;
948 850
				if(id == 0 && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE))
949 851
					id = handle->audio_id > 0 ? handle->audio_id : handle->video_id;
950
				if(video > 1 || !id) {
951
					JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping video line (we have %d video lines, and the id is %d)\n", handle->handle_id, video,
952
						janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE) ? handle->audio_id : handle->video_id);
953
					g_snprintf(buffer, 512,
954
						"m=video 0 %s 0\r\n", rtp_profile);
955
					g_strlcat(sdp, buffer, JANUS_BUFSIZE);
956
					/* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */
957
					g_snprintf(buffer, 512,
958
						"c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip());
959
					g_strlcat(sdp, buffer, JANUS_BUFSIZE);
960
					g_strlcat(sdp, "a=inactive\r\n", JANUS_BUFSIZE);
961
					m = m->m_next;
852
				if(data > 1 || !id) {
853
					JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping SCTP line (we have %d SCTP lines, and the id is %d)\n", handle->handle_id, data, id);
854
					m->port = 0;
855
					g_free(m->direction);
856
					m->direction = g_strdup("inactive");
857
					temp = temp->next;
962 858
					continue;
963 859
				}
964
				/* Video */
860
				/* SCTP */
965 861
				stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(id));
966 862
				if(stream == NULL) {
967
					JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping video line (invalid stream %d)\n", handle->handle_id, id);
968
					g_snprintf(buffer, 512,
969
						"m=video 0 %s 0\r\n", rtp_profile);
970
					g_strlcat(sdp, buffer, JANUS_BUFSIZE);
971
					/* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */
972
					g_snprintf(buffer, 512,
973
						"c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip());
974
					g_strlcat(sdp, buffer, JANUS_BUFSIZE);
975
					g_strlcat(sdp, "a=inactive\r\n", JANUS_BUFSIZE);
976
					m = m->m_next;
977
					continue;
978
				}
979
				g_snprintf(buffer, 512,
980
					"m=video 1 %s", rtp_profile);
981
				g_strlcat(sdp, buffer, JANUS_BUFSIZE);
982
#ifdef HAVE_SCTP
983
			} else if(m->m_type == sdp_media_application) {
984
				/* Is this SCTP for DataChannels? */
985
				if(m->m_port > 0 && m->m_proto_name != NULL && !strcasecmp(m->m_proto_name, "DTLS/SCTP") && m->m_port > 0) {
986
					/* Yep */
987
					data++;
988
					gint id = handle->data_id;
989
					if(id == 0 && janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_BUNDLE))
990
						id = handle->audio_id > 0 ? handle->audio_id : handle->video_id;
991
					if(data > 1 || !id) {
992
						JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping SCTP line (we have %d SCTP lines, and the id is %d)\n", handle->handle_id, data, id);
993
						g_snprintf(buffer, 512,
994
							"m=%s 0 %s 0\r\n",
995
							m->m_type_name, m->m_proto_name);
996
						g_strlcat(sdp, buffer, JANUS_BUFSIZE);
997
						/* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */
998
						g_snprintf(buffer, 512,
999
							"c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip());
1000
						g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1001
						m = m->m_next;
1002
						continue;
1003
					}
1004
					/* SCTP */
1005
					stream = g_hash_table_lookup(handle->streams, GUINT_TO_POINTER(id));
1006
					if(stream == NULL) {
1007
						JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping SCTP line (invalid stream %d)\n", handle->handle_id, id);
1008
						g_snprintf(buffer, 512,
1009
							"m=%s 0 %s 0\r\n",
1010
							m->m_type_name, m->m_proto_name);
1011
						g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1012
						/* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */
1013
						g_snprintf(buffer, 512,
1014
							"c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip());
1015
						g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1016
						m = m->m_next;
1017
						continue;
1018
					}
1019
					g_strlcat(sdp, "m=application 1 DTLS/SCTP", JANUS_BUFSIZE);
1020
				} else {
1021
					JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping unsupported application media line...\n", handle->handle_id);
1022
					g_snprintf(buffer, 512,
1023
						"m=%s 0 %s 0\r\n",
1024
						m->m_type_name, m->m_proto_name);
1025
					g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1026
					m = m->m_next;
863
					JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping SCTP line (invalid stream %d)\n", handle->handle_id, id);
864
					m->port = 0;
865
					g_free(m->direction);
866
					m->direction = g_strdup("inactive");
867
					temp = temp->next;
1027 868
					continue;
1028 869
				}
1029
#endif
1030 870
			} else {
1031
				JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping disabled/unsupported media line...\n", handle->handle_id);
1032
				g_snprintf(buffer, 512,
1033
					"m=%s 0 %s 0\r\n",
1034
					m->m_type_name, m->m_type == sdp_media_application ? m->m_proto_name : rtp_profile);
1035
				g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1036
				/* FIXME Adding a c-line anyway because otherwise Firefox complains? ("c= connection line not specified for every media level, validation failed") */
1037
				g_snprintf(buffer, 512,
1038
					"c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip());
1039
				g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1040
				if(m->m_type == sdp_media_audio || m->m_type == sdp_media_video)
1041
					g_strlcat(sdp, "a=inactive\r\n", JANUS_BUFSIZE);
1042
				m = m->m_next;
871
				JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping unsupported application media line...\n", handle->handle_id);
872
				m->port = 0;
873
				g_free(m->direction);
874
				m->direction = g_strdup("inactive");
875
				temp = temp->next;
1043 876
				continue;
1044 877
			}
1045
			/* Add formats now */
1046
			if(!m->m_rtpmaps) {
1047
				JANUS_LOG(LOG_VERB, "[%"SCNu64"] No RTP maps?? trying formats...\n", handle->handle_id);
1048
				if(!m->m_format) {
1049
					JANUS_LOG(LOG_ERR, "[%"SCNu64"] No formats either?? this sucks!\n", handle->handle_id);
1050
					g_strlcat(sdp, " 0", JANUS_BUFSIZE);	/* FIXME Won't work apparently */
1051
				} else {
1052
					sdp_list_t *fmt = m->m_format;
1053
					while(fmt) {
1054
						g_snprintf(buffer, 512, " %s", fmt->l_text);
1055
						g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1056
						guint16 pt = atoi(fmt->l_text);
1057
						if(m->m_type == sdp_media_audio) {
1058
							stream->audio_payload_types = g_list_append(stream->audio_payload_types, GUINT_TO_POINTER(pt));
1059
						} else if(m->m_type == sdp_media_video) {
1060
							stream->video_payload_types = g_list_append(stream->video_payload_types, GUINT_TO_POINTER(pt));
1061
						}
1062
						fmt = fmt->l_next;
1063
					}
1064
				}
1065
			} else {
1066
				sdp_rtpmap_t *r = m->m_rtpmaps;
1067
				while(r) {
1068
					g_snprintf(buffer, 512, " %d", r->rm_pt);
1069
					g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1070
					guint16 pt = r->rm_pt;
1071
					if(m->m_type == sdp_media_audio) {
1072
						stream->audio_payload_types = g_list_append(stream->audio_payload_types, GUINT_TO_POINTER(pt));
1073
					} else if(m->m_type == sdp_media_video) {
1074
						stream->video_payload_types = g_list_append(stream->video_payload_types, GUINT_TO_POINTER(pt));
1075
					}
1076
					r = r->rm_next;
1077
				}
1078
			}
1079
			g_strlcat(sdp, "\r\n", JANUS_BUFSIZE);
1080
			/* Media connection c= */
1081
			g_snprintf(buffer, 512,
1082
				"c=IN %s %s\r\n", ipv6 ? "IP6" : "IP4", janus_get_public_ip());
1083
			g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1084
			/* Any bandwidth? */
1085
			if(m->m_bandwidths) {
1086
				g_snprintf(buffer, 512,
1087
					"b=%s:%lu\r\n",	/* FIXME Are we doing this correctly? */
1088
						m->m_bandwidths->b_modifier_name ? m->m_bandwidths->b_modifier_name : "AS",
1089
						m->m_bandwidths->b_value);
1090
				g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1091
			}
1092
			/* a=mid:(audio|video|data) */
1093
			switch(m->m_type) {
1094
				case sdp_media_audio:
1095
					g_snprintf(buffer, 512, "a=mid:%s\r\n", handle->audio_mid ? handle->audio_mid : "audio");
1096
					break;
1097
				case sdp_media_video:
1098
					g_snprintf(buffer, 512, "a=mid:%s\r\n", handle->video_mid ? handle->video_mid : "video");
1099
					break;
878
#endif
879
		} else {
880
			JANUS_LOG(LOG_WARN, "[%"SCNu64"] Skipping disabled/unsupported media line...\n", handle->handle_id);
881
			m->port = 0;
882
			g_free(m->direction);
883
			m->direction = g_strdup("inactive");
884
			temp = temp->next;
885
			continue;
886
		}
887
		/* a=mid:(audio|video|data) */
888
		if(m->type == JANUS_SDP_AUDIO) {
889
#pragma GCC diagnostic ignored "-Wformat-security"
890
			a = janus_sdp_attribute_create("mid", handle->audio_mid ? handle->audio_mid : "audio");
891
#pragma GCC diagnostic warning "-Wformat-security"
892
			m->attributes = g_list_insert_before(m->attributes, first, a);
893
		} else if(m->type == JANUS_SDP_VIDEO) {
894
#pragma GCC diagnostic ignored "-Wformat-security"
895
			a = janus_sdp_attribute_create("mid", handle->video_mid ? handle->video_mid : "video");
896
#pragma GCC diagnostic warning "-Wformat-security"
897
			m->attributes = g_list_insert_before(m->attributes, first, a);
1100 898
#ifdef HAVE_SCTP
1101
				case sdp_media_application:
1102
					/* FIXME sctpmap and webrtc-datachannel should be dynamic */
1103
					g_snprintf(buffer, 512, "a=mid:%s\r\na=sctpmap:5000 webrtc-datachannel 16\r\n",
1104
						handle->data_mid ? handle->data_mid : "data");
1105
					break;
899
		} else if(m->type == JANUS_SDP_APPLICATION) {
900
			/* FIXME sctpmap and webrtc-datachannel should be dynamic */
901
			a = janus_sdp_attribute_create("sctpmap", "5000 webrtc-datachannel 16");
902
			m->attributes = g_list_insert_before(m->attributes, first, a);
903
#pragma GCC diagnostic ignored "-Wformat-security"
904
			a = janus_sdp_attribute_create("mid", handle->data_mid ? handle->data_mid : "data");
905
#pragma GCC diagnostic warning "-Wformat-security"
906
			m->attributes = g_list_insert_before(m->attributes, first, a);
1106 907
#endif
1107
				default:
1108
					break;
1109
			}
1110
			g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1111
			if(m->m_type != sdp_media_application) {
1112
				/* What is the direction? */
1113
				switch(m->m_mode) {
1114
					case sdp_sendonly:
1115
						g_strlcat(sdp, "a=sendonly\r\n", JANUS_BUFSIZE);
1116
						break;
1117
					case sdp_recvonly:
1118
						g_strlcat(sdp, "a=recvonly\r\n", JANUS_BUFSIZE);
1119
						break;
1120
					case sdp_inactive:
1121
						g_strlcat(sdp, "a=inactive\r\n", JANUS_BUFSIZE);
1122
						break;
1123
					case sdp_sendrecv:
1124
					default:
1125
						g_strlcat(sdp, "a=sendrecv\r\n", JANUS_BUFSIZE);
1126
						break;
1127
				}
1128
				/* rtcp-mux */
1129
				g_snprintf(buffer, 512, "a=rtcp-mux\r\n");
1130
				g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1131
				/* RTP maps */
1132
				if(m->m_rtpmaps) {
1133
					sdp_rtpmap_t *rm = NULL;
1134
					for(rm = m->m_rtpmaps; rm; rm = rm->rm_next) {
1135
						g_snprintf(buffer, 512, "a=rtpmap:%u %s/%lu%s%s\r\n",
1136
							rm->rm_pt, rm->rm_encoding, rm->rm_rate,
1137
							rm->rm_params ? "/" : "", 
1138
							rm->rm_params ? rm->rm_params : "");
1139
						g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1140
					}
1141
					for(rm = m->m_rtpmaps; rm; rm = rm->rm_next) {
1142
						if(rm->rm_fmtp) {
1143
							g_snprintf(buffer, 512, "a=fmtp:%u %s\r\n", rm->rm_pt, rm->rm_fmtp);
1144
							g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1145
						}
1146
					}
1147
				}
1148
			}
1149
			/* ICE ufrag and pwd, DTLS fingerprint setup and connection a= */
1150
			gchar *ufrag = NULL;
1151
			gchar *password = NULL;
1152
			nice_agent_get_local_credentials(handle->agent, stream->stream_id, &ufrag, &password);
1153
			memset(buffer, 0, 100);
1154
			g_snprintf(buffer, 512,
1155
				"a=ice-ufrag:%s\r\n"
1156
				"a=ice-pwd:%s\r\n"
1157
				"a=ice-options:trickle\r\n"
1158
				"a=fingerprint:sha-256 %s\r\n"
1159
				"a=setup:%s\r\n"
1160
				"a=connection:new\r\n",
1161
					ufrag, password,
1162
					janus_dtls_get_local_fingerprint(),
1163
					janus_get_dtls_srtp_role(stream->dtls_role));
1164
			if(ufrag != NULL)
1165
				g_free(ufrag);
1166
			ufrag = NULL;
1167
			if(password != NULL)
1168
				g_free(password);
1169
			password = NULL;
1170
			g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1171
			/* Copy existing media attributes, if any */
1172
			if(m->m_attributes) {
1173
				sdp_attribute_t *a = m->m_attributes;
1174
				while(a) {
1175
					if(!strcmp(a->a_name, "planb")) {
1176
						/* Skip the fake planb attribute, it's for internal use only */
1177
						a = a->a_next;
1178
						continue;
1179
					}
1180
					if(a->a_value == NULL) {
1181
						g_snprintf(buffer, 512,
1182
							"a=%s\r\n", a->a_name);
1183
						g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1184
					} else {
1185
						/* Make sure this attribute is related to a payload type we know of */
1186
						if(m->m_type == sdp_media_application) {
1187
							g_snprintf(buffer, 512,
1188
								"a=%s:%s\r\n", a->a_name, a->a_value);
1189
							g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1190
						} else {
1191
							int pt = atoi(a->a_value);
1192
							GList *pts = m->m_type == sdp_media_audio ? stream->audio_payload_types : stream->video_payload_types;
1193
							while(pts) {
1194
								guint16 media_pt = GPOINTER_TO_UINT(pts->data);
1195
								if(pt == media_pt) {
1196
									g_snprintf(buffer, 512,
1197
										"a=%s:%s\r\n", a->a_name, a->a_value);
1198
									g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1199
									break;
1200
								}
1201
								pts = pts->next;
1202
							}
1203
						}
1204
					}
1205
					a = a->a_next;
1206
				}
1207
			}
1208
			/* Add last attributes, rtcp and ssrc (msid) */
1209
			if(!planb) {
1210
				/* Single SSRC */
1211
				if(m->m_type == sdp_media_audio && m->m_mode != sdp_inactive && m->m_mode != sdp_recvonly) {
1212
					g_snprintf(buffer, 512,
1213
						"a=ssrc:%"SCNu32" cname:janusaudio\r\n"
1214
						"a=ssrc:%"SCNu32" msid:janus janusa0\r\n"
1215
						"a=ssrc:%"SCNu32" mslabel:janus\r\n"
1216
						"a=ssrc:%"SCNu32" label:janusa0\r\n",
1217
							stream->audio_ssrc, stream->audio_ssrc, stream->audio_ssrc, stream->audio_ssrc);
1218
					g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1219
				} else if(m->m_type == sdp_media_video && m->m_mode != sdp_inactive && m->m_mode != sdp_recvonly) {
1220
					g_snprintf(buffer, 512,
1221
						"a=ssrc:%"SCNu32" cname:janusvideo\r\n"
1222
						"a=ssrc:%"SCNu32" msid:janus janusv0\r\n"
1223
						"a=ssrc:%"SCNu32" mslabel:janus\r\n"
1224
						"a=ssrc:%"SCNu32" label:janusv0\r\n",
1225
							stream->video_ssrc, stream->video_ssrc, stream->video_ssrc, stream->video_ssrc);
1226
					g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1227
				}
1228
			} else {
1229
				/* Multiple SSRCs */
1230
				char mslabel[255];
1231
				memset(mslabel, 0, 255);
1232
				if(m->m_attributes) {
1233
					char id[256];
1234
					uint32_t ssrc = 0;
1235
					sdp_attribute_t *a = m->m_attributes;
1236
					while(a) {
1237
						if(a->a_name == NULL || a->a_value == NULL || strcmp(a->a_name, "planb")) {
1238
							a = a->a_next;
1239
							continue;
1240
						}
1241
						if(sscanf(a->a_value, "%255s %"SCNu32"", id, &ssrc) != 2) {
1242
							JANUS_LOG(LOG_ERR, "Error parsing 'planb' attribute, skipping it...\n");
1243
							a = a->a_next;
1244
							continue;
1245
						}
1246
						JANUS_LOG(LOG_VERB, "Parsing 'planb' attribute: %s\n", a->a_value);
1247
						/* Add proper SSRC attributes */
1248
						if(m->m_type == sdp_media_audio) {
1249
							g_snprintf(buffer, 512,
1250
								"a=ssrc:%"SCNu32" cname:%saudio\r\n"
1251
								"a=ssrc:%"SCNu32" msid:%s %sa0\r\n"
1252
								"a=ssrc:%"SCNu32" mslabel:%s\r\n"
1253
								"a=ssrc:%"SCNu32" label:%sa0\r\n",
1254
									ssrc, id, ssrc, id, id, ssrc, id, ssrc, id);
1255
						} else if(m->m_type == sdp_media_video) {
1256
							g_snprintf(buffer, 512,
1257
								"a=ssrc:%"SCNu32" cname:%svideo\r\n"
1258
								"a=ssrc:%"SCNu32" msid:%s %sv0\r\n"
1259
								"a=ssrc:%"SCNu32" mslabel:%s\r\n"
1260
								"a=ssrc:%"SCNu32" label:%sv0\r\n",
1261
									ssrc, id, ssrc, id, id, ssrc, id, ssrc, id);
1262
						}
1263
						g_strlcat(sdp, buffer, JANUS_BUFSIZE);
1264
						/* Add to msid-semantic, if needed */
1265
						if(!strstr(wms, id)) {
1266
							g_snprintf(mslabel, 255, " %s", id);
1267
							g_strlcat(wms, mslabel, JANUS_BUFSIZE);
1268
						}
1269
						/* Go on */
1270
						a = a->a_next;
1271
					}
1272
				}
1273
			}
1274
			/* And now the candidates */
1275
			janus_ice_candidates_to_sdp(handle, sdp, stream->stream_id, 1);
1276
			if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX) &&
1277
					m->m_type != sdp_media_application)
1278
				janus_ice_candidates_to_sdp(handle, sdp, stream->stream_id, 2);
1279
			/* Next */
1280
			m = m->m_next;
1281 908
		}
909
		if(m->type != JANUS_SDP_APPLICATION) {
910
			a = janus_sdp_attribute_create("rtcp-mux", NULL);
911
			m->attributes = g_list_insert_before(m->attributes, first, a);
912
		}
913
		/* ICE ufrag and pwd, DTLS fingerprint setup and connection a= */
914
		gchar *ufrag = NULL;
915
		gchar *password = NULL;
916
		nice_agent_get_local_credentials(handle->agent, stream->stream_id, &ufrag, &password);
917
		a = janus_sdp_attribute_create("ice-ufrag", "%s", ufrag);
918
		m->attributes = g_list_insert_before(m->attributes, first, a);
919
		a = janus_sdp_attribute_create("ice-pwd", "%s", password);
920
		m->attributes = g_list_insert_before(m->attributes, first, a);
921
		g_free(ufrag);
922
		g_free(password);
923
		a = janus_sdp_attribute_create("ice-options", "trickle");
924
		m->attributes = g_list_insert_before(m->attributes, first, a);
925
		a = janus_sdp_attribute_create("fingerprint", "sha-256 %s", janus_dtls_get_local_fingerprint());
926
		m->attributes = g_list_insert_before(m->attributes, first, a);
927
		a = janus_sdp_attribute_create("setup", "%s", janus_get_dtls_srtp_role(stream->dtls_role));
928
		m->attributes = g_list_insert_before(m->attributes, first, a);
929
		/* Add last attributes, rtcp and ssrc (msid) */
930
		if(m->type == JANUS_SDP_AUDIO &&
931
				(m->direction == NULL || !strcasecmp(m->direction, "sendrecv") || !strcasecmp(m->direction, "sendonly"))) {
932
			a = janus_sdp_attribute_create("ssrc", "%"SCNu32" cname:janusaudio", stream->audio_ssrc);
933
			m->attributes = g_list_append(m->attributes, a);
934
			a = janus_sdp_attribute_create("ssrc", "%"SCNu32" msid:janus janusa0", stream->audio_ssrc);
935
			m->attributes = g_list_append(m->attributes, a);
936
			a = janus_sdp_attribute_create("ssrc", "%"SCNu32" mslabel:janus", stream->audio_ssrc);
937
			m->attributes = g_list_append(m->attributes, a);
938
			a = janus_sdp_attribute_create("ssrc", "%"SCNu32" label:janusa0", stream->audio_ssrc);
939
			m->attributes = g_list_append(m->attributes, a);
940
		} else if(m->type == JANUS_SDP_VIDEO &&
941
				(m->direction == NULL || !strcasecmp(m->direction, "sendrecv") || !strcasecmp(m->direction, "sendonly"))) {
942
			a = janus_sdp_attribute_create("ssrc", "%"SCNu32" cname:janusvideo", stream->video_ssrc);
943
			m->attributes = g_list_append(m->attributes, a);
944
			a = janus_sdp_attribute_create("ssrc", "%"SCNu32" msid:janus janusv0", stream->video_ssrc);
945
			m->attributes = g_list_append(m->attributes, a);
946
			a = janus_sdp_attribute_create("ssrc", "%"SCNu32" mslabel:janus", stream->video_ssrc);
947
			m->attributes = g_list_append(m->attributes, a);
948
			a = janus_sdp_attribute_create("ssrc", "%"SCNu32" label:janusv0", stream->video_ssrc);
949
			m->attributes = g_list_append(m->attributes, a);
950
		}
951
		/* And now the candidates */
952
		janus_ice_candidates_to_sdp(handle, m, stream->stream_id, 1);
953
		if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_RTCPMUX) &&
954
				m->type != JANUS_SDP_APPLICATION)
955
			janus_ice_candidates_to_sdp(handle, m, stream->stream_id, 2);
956
		/* Next */
957
		temp = temp->next;
1282 958
	}
1283 959

  
1284
	/* Do we need to update the msid-semantic attribute? */
1285
	if(planb) {
1286
		sdp = janus_string_replace(sdp, "WMS janus", wms);
1287
	}
1288
	
1289
	sdp_parser_free(parser);
960
	char *sdp = janus_sdp_export(anon);
961
	janus_sdp_free(anon);
1290 962

  
1291 963
	JANUS_LOG(LOG_VERB, " -------------------------------------------\n");
1292 964
	JANUS_LOG(LOG_VERB, "  >> Merged (%zu --> %zu bytes)\n", strlen(origsdp), strlen(sdp));

Also available in: Unified diff