1 /*
2 * sip-media-stream.c - Source for SIPMediaStream
3 * Copyright (C) 2006 Collabora Ltd.
4 * Copyright (C) 2006,2007 Nokia Corporation
5 * @author Kai Vehmanen <first.surname@nokia.com>
6 * @author Mikhail Zabaluev <first.surname@nokia.com>
7 *
8 * Based on telepathy-gabble implementation (gabble-media-stream).
9 * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk>
10 *
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * version 2.1 as published by the Free Software Foundation.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include <dbus/dbus-glib.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <telepathy-glib/dbus.h>
30 #include <telepathy-glib/enums.h>
31 #include <telepathy-glib/errors.h>
32 #include <telepathy-glib/svc-media-interfaces.h>
33
34 #include "config.h"
35
36 #include "sip-media-stream.h"
37 #include "sip-media-session.h"
38
39 #include <sofia-sip/msg_parser.h>
40
41 #include "signals-marshal.h"
42 #include "telepathy-helpers.h"
43
44 #define DEBUG_FLAG SIP_DEBUG_MEDIA
45 #include "debug.h"
46
47
48 #define same_boolean(old, new) ((!(old)) == (!(new)))
49
50
51 static void stream_handler_iface_init (gpointer, gpointer);
52
53 G_DEFINE_TYPE_WITH_CODE(SIPMediaStream,
54 sip_media_stream,
55 G_TYPE_OBJECT,
56 G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_MEDIA_STREAM_HANDLER,
57 stream_handler_iface_init)
58 )
59
60 /* signal enum */
61 enum
62 {
63 SIG_READY,
64 SIG_SUPPORTED_CODECS,
65 SIG_STATE_CHANGED,
66 SIG_DIRECTION_CHANGED,
67
68 SIG_LAST_SIGNAL
69 };
70
71 static guint signals[SIG_LAST_SIGNAL] = {0};
72
73 /* properties */
74 enum
75 {
76 PROP_MEDIA_SESSION = 1,
77 PROP_OBJECT_PATH,
78 PROP_ID,
79 PROP_MEDIA_TYPE,
80 PROP_STATE,
81 PROP_DIRECTION,
82 PROP_PENDING_SEND_FLAGS,
83 LAST_PROPERTY
84 };
85
86 /* private structure */
87 typedef struct _SIPMediaStreamPrivate SIPMediaStreamPrivate;
88
89 struct _SIPMediaStreamPrivate
90 {
91 SIPMediaSession *session; /** see gobj. prop. 'media-session' */
92 gchar *object_path; /** see gobj. prop. 'object-path' */
93 guint id; /** see gobj. prop. 'id' */
94 guint media_type; /** see gobj. prop. 'media-type' */
95 guint state; /** see gobj. prop. 'state' */
96 guint direction; /** see gobj. prop. 'direction' */
97 guint pending_send_flags; /** see gobj. prop. 'pending-send-flags' */
98
99 gchar *stream_sdp; /** SDP description of the stream */
100
101
102 GValue native_codecs; /** intersected codec list */
103 GValue native_candidates;
104
105 const sdp_media_t *remote_media; /** pointer to the SDP media structure
106 * owned by the session object */
107
108 guint remote_candidate_counter;
109 gchar *remote_candidate_id;
110
111 gchar *native_candidate_id;
112
113 gboolean ready_received; /** our ready method has been called */
114 gboolean playing; /** stream set to playing */
115 gboolean sending; /** stream set to sending */
116 gboolean native_cands_prepared; /** all candidates discovered */
117 gboolean native_codecs_prepared; /** all codecs discovered */
118 gboolean push_remote_requested; /** remote info signals are pending */
119 gboolean codec_intersect_pending; /** codec intersection is pending */
120 gboolean dispose_has_run;
121 };
122
123 #define SIP_MEDIA_STREAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SIP_TYPE_MEDIA_STREAM, SIPMediaStreamPrivate))
124
125 static void push_remote_codecs (SIPMediaStream *stream);
126 static void push_remote_candidates (SIPMediaStream *stream);
127 static void push_active_candidate_pair (SIPMediaStream *stream);
128 static void priv_update_sending (SIPMediaStream *stream,
129 TpMediaStreamDirection direction,
130 guint pending_send_flags);
131 static void priv_update_local_sdp(SIPMediaStream *stream);
132 static void priv_generate_sdp (SIPMediaStream *stream);
133
134 #if 0
135 #ifdef ENABLE_DEBUG
136 static const char *debug_tp_protocols[] = {
137 "TP_MEDIA_STREAM_PROTO_UDP (0)",
138 "TP_MEDIA_STREAM_PROTO_TCP (1)"
139 };
140
141 static const char *debug_tp_transports[] = {
142 "TP_MEDIA_STREAM_TRANSPORT_TYPE_LOCAL (0)",
143 "TP_MEDIA_STREAM_TRANSPORT_TYPE_DERIVED (1)",
144 "TP_MEDIA_STREAM_TRANSPORT_TYPE_RELAY (2)"
145 };
146 #endif /* ENABLE_DEBUG */
147 #endif /* 0 */
148
149 /***********************************************************************
150 * Set: DBus type utilities
151 ***********************************************************************/
152
DEFINE_TP_STRUCT_TYPE(sip_tp_transport_struct_type,G_TYPE_UINT,G_TYPE_STRING,G_TYPE_UINT,G_TYPE_UINT,G_TYPE_STRING,G_TYPE_STRING,G_TYPE_DOUBLE,G_TYPE_UINT,G_TYPE_STRING,G_TYPE_STRING)153 DEFINE_TP_STRUCT_TYPE(sip_tp_transport_struct_type,
154 G_TYPE_UINT,
155 G_TYPE_STRING,
156 G_TYPE_UINT,
157 G_TYPE_UINT,
158 G_TYPE_STRING,
159 G_TYPE_STRING,
160 G_TYPE_DOUBLE,
161 G_TYPE_UINT,
162 G_TYPE_STRING,
163 G_TYPE_STRING)
164
165 DEFINE_TP_LIST_TYPE(sip_tp_transport_list_type,
166 sip_tp_transport_struct_type())
167
168 DEFINE_TP_STRUCT_TYPE(sip_tp_candidate_struct_type,
169 G_TYPE_STRING,
170 sip_tp_transport_list_type ())
171
172 DEFINE_TP_LIST_TYPE(sip_tp_candidate_list_type,
173 sip_tp_candidate_struct_type ())
174
175 DEFINE_TP_STRUCT_TYPE(sip_tp_codec_struct_type,
176 G_TYPE_UINT,
177 G_TYPE_STRING,
178 G_TYPE_UINT,
179 G_TYPE_UINT,
180 G_TYPE_UINT,
181 DBUS_TYPE_G_STRING_STRING_HASHTABLE)
182
183 DEFINE_TP_LIST_TYPE(sip_tp_codec_list_type,
184 sip_tp_codec_struct_type ())
185
186 /***********************************************************************
187 * Set: Gobject interface
188 ***********************************************************************/
189
190 static void
191 sip_media_stream_init (SIPMediaStream *self)
192 {
193 SIPMediaStreamPrivate *priv = SIP_MEDIA_STREAM_GET_PRIVATE (self);
194
195 priv->playing = FALSE;
196 priv->sending = FALSE;
197
198 g_value_init (&priv->native_codecs, sip_tp_codec_list_type ());
199 g_value_take_boxed (&priv->native_codecs,
200 dbus_g_type_specialized_construct (sip_tp_codec_list_type ()));
201
202 g_value_init (&priv->native_candidates, sip_tp_candidate_list_type ());
203 g_value_take_boxed (&priv->native_candidates,
204 dbus_g_type_specialized_construct (sip_tp_candidate_list_type ()));
205
206 priv->native_cands_prepared = FALSE;
207 priv->native_codecs_prepared = FALSE;
208
209 priv->push_remote_requested = FALSE;
210 }
211
212 static GObject *
sip_media_stream_constructor(GType type,guint n_props,GObjectConstructParam * props)213 sip_media_stream_constructor (GType type, guint n_props,
214 GObjectConstructParam *props)
215 {
216 GObject *obj;
217 SIPMediaStreamPrivate *priv;
218 DBusGConnection *bus;
219
220 /* call base class constructor */
221 obj = G_OBJECT_CLASS (sip_media_stream_parent_class)->
222 constructor (type, n_props, props);
223 priv = SIP_MEDIA_STREAM_GET_PRIVATE (SIP_MEDIA_STREAM (obj));
224
225 /* go for the bus */
226 bus = tp_get_bus ();
227 dbus_g_connection_register_g_object (bus, priv->object_path, obj);
228
229 return obj;
230 }
231
232 static void
sip_media_stream_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)233 sip_media_stream_get_property (GObject *object,
234 guint property_id,
235 GValue *value,
236 GParamSpec *pspec)
237 {
238 SIPMediaStream *stream = SIP_MEDIA_STREAM (object);
239 SIPMediaStreamPrivate *priv = SIP_MEDIA_STREAM_GET_PRIVATE (stream);
240
241 switch (property_id)
242 {
243 case PROP_MEDIA_SESSION:
244 g_value_set_object (value, priv->session);
245 break;
246 case PROP_OBJECT_PATH:
247 g_value_set_string (value, priv->object_path);
248 break;
249 case PROP_ID:
250 g_value_set_uint (value, priv->id);
251 break;
252 case PROP_MEDIA_TYPE:
253 g_value_set_uint (value, priv->media_type);
254 break;
255 case PROP_STATE:
256 g_value_set_uint (value, priv->state);
257 break;
258 case PROP_DIRECTION:
259 g_value_set_uint (value, priv->direction);
260 break;
261 case PROP_PENDING_SEND_FLAGS:
262 g_value_set_uint (value, priv->pending_send_flags);
263 break;
264 default:
265 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
266 }
267 }
268
269 static void
sip_media_stream_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)270 sip_media_stream_set_property (GObject *object,
271 guint property_id,
272 const GValue *value,
273 GParamSpec *pspec)
274 {
275 SIPMediaStream *stream = SIP_MEDIA_STREAM (object);
276 SIPMediaStreamPrivate *priv = SIP_MEDIA_STREAM_GET_PRIVATE (stream);
277
278 switch (property_id)
279 {
280 case PROP_MEDIA_SESSION:
281 priv->session = g_value_get_object (value);
282 break;
283 case PROP_OBJECT_PATH:
284 g_free (priv->object_path);
285 priv->object_path = g_value_dup_string (value);
286 break;
287 case PROP_ID:
288 priv->id = g_value_get_uint (value);
289 break;
290 case PROP_MEDIA_TYPE:
291 priv->media_type = g_value_get_uint (value);
292 break;
293 case PROP_STATE:
294 priv->state = g_value_get_uint (value);
295 break;
296 case PROP_DIRECTION:
297 priv->direction = g_value_get_uint (value);
298 break;
299 case PROP_PENDING_SEND_FLAGS:
300 priv->pending_send_flags = g_value_get_uint (value);
301 break;
302 default:
303 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
304 }
305 }
306
307 static void sip_media_stream_dispose (GObject *object);
308 static void sip_media_stream_finalize (GObject *object);
309
310 static void
sip_media_stream_class_init(SIPMediaStreamClass * sip_media_stream_class)311 sip_media_stream_class_init (SIPMediaStreamClass *sip_media_stream_class)
312 {
313 GObjectClass *object_class = G_OBJECT_CLASS (sip_media_stream_class);
314 GType stream_type = G_OBJECT_CLASS_TYPE (sip_media_stream_class);
315 GParamSpec *param_spec;
316
317 g_type_class_add_private (sip_media_stream_class, sizeof (SIPMediaStreamPrivate));
318
319 object_class->constructor = sip_media_stream_constructor;
320
321 object_class->get_property = sip_media_stream_get_property;
322 object_class->set_property = sip_media_stream_set_property;
323
324 object_class->dispose = sip_media_stream_dispose;
325 object_class->finalize = sip_media_stream_finalize;
326
327 param_spec = g_param_spec_object ("media-session", "GabbleMediaSession object",
328 "SIP media session object that owns this "
329 "media stream object.",
330 SIP_TYPE_MEDIA_SESSION,
331 G_PARAM_CONSTRUCT_ONLY |
332 G_PARAM_READWRITE |
333 G_PARAM_STATIC_NICK |
334 G_PARAM_STATIC_BLURB);
335 g_object_class_install_property (object_class, PROP_MEDIA_SESSION, param_spec);
336
337 param_spec = g_param_spec_string ("object-path", "D-Bus object path",
338 "The D-Bus object path used for this "
339 "object on the bus.",
340 NULL,
341 G_PARAM_CONSTRUCT_ONLY |
342 G_PARAM_READWRITE |
343 G_PARAM_STATIC_NAME |
344 G_PARAM_STATIC_BLURB);
345 g_object_class_install_property (object_class, PROP_OBJECT_PATH, param_spec);
346
347 param_spec = g_param_spec_uint ("id", "Stream ID",
348 "A stream number for the stream used in the "
349 "D-Bus API.",
350 0, G_MAXUINT, 0,
351 G_PARAM_CONSTRUCT_ONLY |
352 G_PARAM_READWRITE |
353 G_PARAM_STATIC_NAME |
354 G_PARAM_STATIC_BLURB);
355 g_object_class_install_property (object_class, PROP_ID, param_spec);
356
357 param_spec = g_param_spec_uint ("media-type", "Stream media type",
358 "A constant indicating which media type the "
359 "stream carries.",
360 TP_MEDIA_STREAM_TYPE_AUDIO,
361 TP_MEDIA_STREAM_TYPE_VIDEO,
362 TP_MEDIA_STREAM_TYPE_AUDIO,
363 G_PARAM_CONSTRUCT_ONLY |
364 G_PARAM_READWRITE |
365 G_PARAM_STATIC_NAME |
366 G_PARAM_STATIC_BLURB);
367 g_object_class_install_property (object_class, PROP_MEDIA_TYPE, param_spec);
368
369 param_spec = g_param_spec_uint ("state", "Connection state",
370 "Connection state of the media stream",
371 TP_MEDIA_STREAM_STATE_DISCONNECTED,
372 TP_MEDIA_STREAM_STATE_CONNECTED,
373 TP_MEDIA_STREAM_STATE_DISCONNECTED,
374 G_PARAM_READWRITE |
375 G_PARAM_STATIC_NAME |
376 G_PARAM_STATIC_BLURB);
377 g_object_class_install_property (object_class,
378 PROP_STATE,
379 param_spec);
380
381 /* We don't change the following two as individual properties
382 * after construction, use sip_media_stream_set_direction() */
383
384 param_spec = g_param_spec_uint ("direction", "Stream direction",
385 "A value indicating the current "
386 "direction of the stream",
387 TP_MEDIA_STREAM_DIRECTION_NONE,
388 TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL,
389 TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL,
390 G_PARAM_CONSTRUCT_ONLY |
391 G_PARAM_READWRITE |
392 G_PARAM_STATIC_NAME |
393 G_PARAM_STATIC_BLURB);
394 g_object_class_install_property (object_class, PROP_DIRECTION, param_spec);
395
396 param_spec = g_param_spec_uint ("pending-send-flags", "Pending send flags",
397 "Flags indicating the current "
398 "pending send state of the stream",
399 0,
400 TP_MEDIA_STREAM_PENDING_LOCAL_SEND
401 | TP_MEDIA_STREAM_PENDING_REMOTE_SEND,
402 0,
403 G_PARAM_CONSTRUCT_ONLY |
404 G_PARAM_READWRITE |
405 G_PARAM_STATIC_NAME |
406 G_PARAM_STATIC_BLURB);
407 g_object_class_install_property (object_class,
408 PROP_PENDING_SEND_FLAGS,
409 param_spec);
410
411 /* signals not exported by DBus interface */
412 signals[SIG_READY] =
413 g_signal_new ("ready",
414 stream_type,
415 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
416 0,
417 NULL, NULL,
418 g_cclosure_marshal_VOID__VOID,
419 G_TYPE_NONE, 0);
420
421 signals[SIG_SUPPORTED_CODECS] =
422 g_signal_new ("supported-codecs",
423 stream_type,
424 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
425 0,
426 NULL, NULL,
427 g_cclosure_marshal_VOID__UINT,
428 G_TYPE_NONE, 1, G_TYPE_UINT);
429
430 signals[SIG_STATE_CHANGED] =
431 g_signal_new ("state-changed",
432 stream_type,
433 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
434 0,
435 NULL, NULL,
436 g_cclosure_marshal_VOID__UINT,
437 G_TYPE_NONE, 1, G_TYPE_UINT);
438
439 signals[SIG_DIRECTION_CHANGED] =
440 g_signal_new ("direction-changed",
441 stream_type,
442 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
443 0,
444 NULL, NULL,
445 _tpsip_marshal_VOID__UINT_UINT,
446 G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
447 }
448
449 void
sip_media_stream_dispose(GObject * object)450 sip_media_stream_dispose (GObject *object)
451 {
452 SIPMediaStream *self = SIP_MEDIA_STREAM (object);
453 SIPMediaStreamPrivate *priv = SIP_MEDIA_STREAM_GET_PRIVATE (self);
454
455 if (priv->dispose_has_run)
456 return;
457
458 priv->dispose_has_run = TRUE;
459
460 /* release any references held by the object here */
461
462 if (G_OBJECT_CLASS (sip_media_stream_parent_class)->dispose)
463 G_OBJECT_CLASS (sip_media_stream_parent_class)->dispose (object);
464
465 DEBUG ("exit");
466 }
467
468 void
sip_media_stream_finalize(GObject * object)469 sip_media_stream_finalize (GObject *object)
470 {
471 SIPMediaStream *self = SIP_MEDIA_STREAM (object);
472 SIPMediaStreamPrivate *priv = SIP_MEDIA_STREAM_GET_PRIVATE (self);
473
474 /* free any data held directly by the object here */
475 g_free (priv->object_path);
476 g_free (priv->stream_sdp);
477
478 g_value_unset (&priv->native_codecs);
479 g_value_unset (&priv->native_candidates);
480
481 g_free (priv->native_candidate_id);
482 g_free (priv->remote_candidate_id);
483
484 G_OBJECT_CLASS (sip_media_stream_parent_class)->finalize (object);
485
486 DEBUG ("exit");
487 }
488
489 /***********************************************************************
490 * Set: Media.StreamHandler interface implementation (same for 0.12/0.13???)
491 ***********************************************************************/
492
493 /**
494 * sip_media_stream_codec_choice
495 *
496 * Implements DBus method CodecChoice
497 * on interface org.freedesktop.Telepathy.Media.StreamHandler
498 */
499 static void
sip_media_stream_codec_choice(TpSvcMediaStreamHandler * iface,guint codec_id,DBusGMethodInvocation * context)500 sip_media_stream_codec_choice (TpSvcMediaStreamHandler *iface,
501 guint codec_id,
502 DBusGMethodInvocation *context)
503 {
504 /* Inform the connection manager of the current codec choice.
505 * -> note: not implemented by tp-gabble either (2006/May) */
506
507 g_debug ("%s: not implemented (ignoring)", G_STRFUNC);
508
509 tp_svc_media_stream_handler_return_from_codec_choice (context);
510 }
511
512 /**
513 * sip_media_stream_error
514 *
515 * Implements DBus method Error
516 * on interface org.freedesktop.Telepathy.Media.StreamHandler
517 */
518 static void
sip_media_stream_error(TpSvcMediaStreamHandler * iface,guint errno,const gchar * message,DBusGMethodInvocation * context)519 sip_media_stream_error (TpSvcMediaStreamHandler *iface,
520 guint errno,
521 const gchar *message,
522 DBusGMethodInvocation *context)
523 {
524 /* Note: Inform the connection manager that an error occured in this stream. */
525
526 SIPMediaStream *obj = SIP_MEDIA_STREAM (iface);
527 SIPMediaStreamPrivate *priv;
528
529 g_assert (SIP_IS_MEDIA_STREAM (obj));
530
531 priv = SIP_MEDIA_STREAM_GET_PRIVATE (obj);
532
533 SESSION_DEBUG(priv->session, "Media.StreamHandler::Error called -- terminating session");
534
535 sip_media_session_terminate (priv->session);
536
537 tp_svc_media_stream_handler_return_from_error (context);
538 }
539
540
541 /**
542 * sip_media_stream_native_candidates_prepared
543 *
544 * Implements DBus method NativeCandidatesPrepared
545 * on interface org.freedesktop.Telepathy.Media.StreamHandler
546 */
547 static void
sip_media_stream_native_candidates_prepared(TpSvcMediaStreamHandler * iface,DBusGMethodInvocation * context)548 sip_media_stream_native_candidates_prepared (TpSvcMediaStreamHandler *iface,
549 DBusGMethodInvocation *context)
550 {
551 /* purpose: "Informs the connection manager that all possible native candisates
552 * have been discovered for the moment."
553 */
554
555 SIPMediaStream *obj = SIP_MEDIA_STREAM (iface);
556 SIPMediaStreamPrivate *priv;
557
558 g_assert (SIP_IS_MEDIA_STREAM (obj));
559 priv = SIP_MEDIA_STREAM_GET_PRIVATE (obj);
560
561 DEBUG("enter");
562
563 priv->native_cands_prepared = TRUE;
564
565 if (priv->native_codecs_prepared)
566 priv_generate_sdp (obj);
567
568 push_active_candidate_pair (obj);
569
570 tp_svc_media_stream_handler_return_from_native_candidates_prepared (context);
571 }
572
573
574 /**
575 * sip_media_stream_new_active_candidate_pair
576 *
577 * Implements DBus method NewActiveCandidatePair
578 * on interface org.freedesktop.Telepathy.Media.StreamHandler
579 */
580 static void
sip_media_stream_new_active_candidate_pair(TpSvcMediaStreamHandler * iface,const gchar * native_candidate_id,const gchar * remote_candidate_id,DBusGMethodInvocation * context)581 sip_media_stream_new_active_candidate_pair (TpSvcMediaStreamHandler *iface,
582 const gchar *native_candidate_id,
583 const gchar *remote_candidate_id,
584 DBusGMethodInvocation *context)
585 {
586 SIPMediaStream *obj = SIP_MEDIA_STREAM (iface);
587 SIPMediaStreamPrivate *priv;
588
589 priv = SIP_MEDIA_STREAM_GET_PRIVATE (obj);
590
591 DEBUG("stream engine reported new active candidate pair %s-%s",
592 native_candidate_id, remote_candidate_id);
593
594 if (priv->remote_candidate_id == NULL
595 || strcmp (priv->remote_candidate_id, remote_candidate_id))
596 {
597 GError *err;
598 err = g_error_new (TP_ERRORS,
599 TP_ERROR_INVALID_ARGUMENT,
600 "Remote candidate ID does not match the locally "
601 "stored data");
602 dbus_g_method_return_error (context, err);
603 g_error_free (err);
604 return;
605 }
606
607 tp_svc_media_stream_handler_return_from_new_active_candidate_pair (context);
608 }
609
610
611 /**
612 * sip_media_stream_new_native_candidate
613 *
614 * Implements DBus method NewNativeCandidate
615 * on interface org.freedesktop.Telepathy.Media.StreamHandler
616 */
617 static void
sip_media_stream_new_native_candidate(TpSvcMediaStreamHandler * iface,const gchar * candidate_id,const GPtrArray * transports,DBusGMethodInvocation * context)618 sip_media_stream_new_native_candidate (TpSvcMediaStreamHandler *iface,
619 const gchar *candidate_id,
620 const GPtrArray *transports,
621 DBusGMethodInvocation *context)
622 {
623 SIPMediaStream *obj = SIP_MEDIA_STREAM (iface);
624 SIPMediaStreamPrivate *priv;
625 GPtrArray *candidates;
626 GValue candidate = { 0, };
627 GValue transport = { 0, };
628 gint tr_goodness;
629
630 g_assert (SIP_IS_MEDIA_STREAM (obj));
631 priv = SIP_MEDIA_STREAM_GET_PRIVATE (obj);
632
633 if (priv->stream_sdp != NULL)
634 {
635 g_message ("Stream %u: SDP already generated, ignoring native candidate '%s'", priv->id, candidate_id);
636 tp_svc_media_stream_handler_return_from_new_native_candidate (context);
637 return;
638 }
639
640 g_return_if_fail (transports->len >= 1);
641
642 /* Rate the preferability of the address */
643 g_value_init (&transport, sip_tp_transport_struct_type ());
644 g_value_set_static_boxed (&transport, g_ptr_array_index (transports, 0));
645 tr_goodness = sip_media_session_rate_native_transport (priv->session,
646 &transport);
647
648 candidates = g_value_get_boxed (&priv->native_candidates);
649
650 if (tr_goodness > 0)
651 {
652 DEBUG("native candidate '%s' is rated as preferable", candidate_id);
653 g_free (priv->native_candidate_id);
654 priv->native_candidate_id = g_strdup (candidate_id);
655
656 /* Drop the candidates received previously */
657 g_value_reset (&priv->native_candidates);
658 candidates = dbus_g_type_specialized_construct (
659 sip_tp_candidate_list_type ());
660 g_value_take_boxed (&priv->native_candidates, candidates);
661 }
662
663 g_value_init (&candidate, sip_tp_candidate_struct_type ());
664 g_value_take_boxed (&candidate,
665 dbus_g_type_specialized_construct (sip_tp_candidate_struct_type ()));
666
667 dbus_g_type_struct_set (&candidate,
668 0, candidate_id,
669 1, transports,
670 G_MAXUINT);
671
672 g_ptr_array_add (candidates, g_value_get_boxed (&candidate));
673
674 SESSION_DEBUG(priv->session, "put native candidate '%s' from stream-engine into cache", candidate_id);
675
676 tp_svc_media_stream_handler_return_from_new_native_candidate (context);
677 }
678
679
680 /**
681 * sip_media_stream_ready
682 *
683 * Implements DBus method Ready
684 * on interface org.freedesktop.Telepathy.Media.StreamHandler
685 */
686 static void
sip_media_stream_ready(TpSvcMediaStreamHandler * iface,const GPtrArray * codecs,DBusGMethodInvocation * context)687 sip_media_stream_ready (TpSvcMediaStreamHandler *iface,
688 const GPtrArray *codecs,
689 DBusGMethodInvocation *context)
690 {
691 /* purpose: "Inform the connection manager that a client is ready to handle
692 * this StreamHandler. Also provide it with info about all supported
693 * codecs."
694 *
695 * - note, with SIP we don't send the invite just yet (we need
696 * candidates first
697 */
698
699 SIPMediaStream *obj = SIP_MEDIA_STREAM (iface);
700 SIPMediaStreamPrivate *priv;
701 GValue val = { 0, };
702
703 DEBUG ("enter");
704
705 g_assert (SIP_IS_MEDIA_STREAM (obj));
706
707 priv = SIP_MEDIA_STREAM_GET_PRIVATE (obj);
708
709 priv->ready_received = TRUE;
710
711 SESSION_DEBUG(priv->session, "putting list of %d locally supported "
712 "codecs from stream-engine into cache", codecs->len);
713 g_value_init (&val, sip_tp_codec_list_type ());
714 g_value_set_static_boxed (&val, codecs);
715 g_value_copy (&val, &priv->native_codecs);
716
717 priv->native_codecs_prepared = TRUE;
718 if (priv->native_cands_prepared)
719 priv_generate_sdp (obj);
720
721 /* Push the initial sending/playing state */
722 tp_svc_media_stream_handler_emit_set_stream_sending (
723 iface, priv->sending);
724 tp_svc_media_stream_handler_emit_set_stream_playing (
725 iface, priv->playing);
726
727 if (priv->push_remote_requested) {
728 push_remote_candidates (obj);
729 push_remote_codecs (obj);
730 priv->push_remote_requested = FALSE;
731 }
732
733 /* note: for inbound sessions, emit active candidate pair once
734 remote info is set */
735 push_active_candidate_pair (obj);
736
737 tp_svc_media_stream_handler_return_from_ready (context);
738 }
739
740 /* FIXME: set_local_codecs not implemented */
741
742 /**
743 * sip_media_stream_stream_state
744 *
745 * Implements DBus method StreamState
746 * on interface org.freedesktop.Telepathy.Media.StreamHandler
747 */
748 static void
sip_media_stream_stream_state(TpSvcMediaStreamHandler * iface,guint state,DBusGMethodInvocation * context)749 sip_media_stream_stream_state (TpSvcMediaStreamHandler *iface,
750 guint state,
751 DBusGMethodInvocation *context)
752 {
753 /* purpose: "Informs the connection manager of the stream's current state
754 * State is as specified in *ChannelTypeStreamedMedia::GetStreams."
755 *
756 * - set the stream state for session
757 */
758
759 SIPMediaStream *obj = SIP_MEDIA_STREAM (iface);
760 SIPMediaStreamPrivate *priv;
761 priv = SIP_MEDIA_STREAM_GET_PRIVATE (obj);
762
763 if (priv->state != state)
764 {
765 DEBUG("changing stream state from %u to %u", priv->state, state);
766 priv->state = state;
767 g_signal_emit (obj, signals[SIG_STATE_CHANGED], 0, state);
768 }
769
770 tp_svc_media_stream_handler_return_from_stream_state (context);
771 }
772
773 /**
774 * sip_media_stream_supported_codecs
775 *
776 * Implements DBus method SupportedCodecs
777 * on interface org.freedesktop.Telepathy.Media.StreamHandler
778 */
779 static void
sip_media_stream_supported_codecs(TpSvcMediaStreamHandler * iface,const GPtrArray * codecs,DBusGMethodInvocation * context)780 sip_media_stream_supported_codecs (TpSvcMediaStreamHandler *iface,
781 const GPtrArray *codecs,
782 DBusGMethodInvocation *context)
783 {
784 /* purpose: "Inform the connection manager of the supported codecs for this session.
785 * This is called after the connection manager has emitted SetRemoteCodecs
786 * to notify what codecs are supported by the peer, and will thus be an
787 * intersection of all locally supported codecs (passed to Ready)
788 * and those supported by the peer."
789 *
790 * - emit SupportedCodecs
791 */
792
793 SIPMediaStream *self = SIP_MEDIA_STREAM (iface);
794 SIPMediaStreamPrivate *priv;
795 priv = SIP_MEDIA_STREAM_GET_PRIVATE (self);
796
797 SESSION_DEBUG(priv->session, "got codec intersection containing %u "
798 "codecs from stream-engine", codecs->len);
799
800 g_assert (priv->codec_intersect_pending);
801 priv->codec_intersect_pending = FALSE;
802
803 /* store the intersection for later on */
804 g_value_set_boxed (&priv->native_codecs, codecs);
805
806 g_signal_emit (self, signals[SIG_SUPPORTED_CODECS], 0, codecs->len);
807
808 tp_svc_media_stream_handler_return_from_supported_codecs (context);
809 }
810
811 /***********************************************************************
812 * Helper functions follow (not based on generated templates)
813 ***********************************************************************/
814
815 guint
sip_media_stream_get_id(SIPMediaStream * self)816 sip_media_stream_get_id (SIPMediaStream *self)
817 {
818 SIPMediaStreamPrivate *priv = SIP_MEDIA_STREAM_GET_PRIVATE (self);
819 return priv->id;
820 }
821
822 guint
sip_media_stream_get_media_type(SIPMediaStream * self)823 sip_media_stream_get_media_type (SIPMediaStream *self)
824 {
825 SIPMediaStreamPrivate *priv = SIP_MEDIA_STREAM_GET_PRIVATE (self);
826 return priv->media_type;
827 }
828
829 void
sip_media_stream_close(SIPMediaStream * self)830 sip_media_stream_close (SIPMediaStream *self)
831 {
832 tp_svc_media_stream_handler_emit_close (self);
833 }
834
835 /**
836 * Described the local stream configuration in SDP (RFC2327),
837 * or NULL if stream not configured yet.
838 */
sip_media_stream_local_sdp(SIPMediaStream * obj)839 const char *sip_media_stream_local_sdp (SIPMediaStream *obj)
840 {
841 SIPMediaStreamPrivate *priv;
842 priv = SIP_MEDIA_STREAM_GET_PRIVATE (obj);
843 return priv->stream_sdp;
844 }
845
846 static inline guint
sip_tp_stream_direction_from_remote(sdp_mode_t mode)847 sip_tp_stream_direction_from_remote (sdp_mode_t mode)
848 {
849 return ((mode & sdp_recvonly)? TP_MEDIA_STREAM_DIRECTION_SEND : 0)
850 | ((mode & sdp_sendonly)? TP_MEDIA_STREAM_DIRECTION_RECEIVE : 0);
851 }
852
853 /**
854 * Sets the remote candidates and codecs for this stream, as
855 * received via signaling.
856 *
857 * Parses the SDP information, updates TP remote candidates and
858 * codecs if the client is ready.
859 *
860 * Note that the pointer to the media description structure is saved,
861 * implying that the structure shall not go away for the lifetime of
862 * the stream, preferably kept in the memory home attached to
863 * the session object.
864 *
865 * @return TRUE if the remote information has been accepted,
866 * FALSE if the update is not acceptable.
867 */
868 gboolean
sip_media_stream_set_remote_media(SIPMediaStream * stream,const sdp_media_t * new_media,gboolean authoritative)869 sip_media_stream_set_remote_media (SIPMediaStream *stream,
870 const sdp_media_t *new_media,
871 gboolean authoritative)
872 {
873 SIPMediaStreamPrivate *priv;
874 sdp_connection_t *sdp_conn;
875 const sdp_media_t *old_media;
876 gboolean transport_changed = TRUE;
877 guint new_direction;
878
879 DEBUG ("enter");
880
881 priv = SIP_MEDIA_STREAM_GET_PRIVATE (stream);
882
883 /* Do sanity checks */
884
885 g_return_val_if_fail (new_media != NULL, -1);
886
887 if (new_media->m_rejected || new_media->m_port == 0)
888 {
889 DEBUG("the stream is rejected remotely");
890 return FALSE;
891 }
892
893 if (new_media->m_proto != sdp_proto_rtp)
894 {
895 g_warning ("Stream %u: the remote protocol is not RTP/AVP", priv->id);
896 return FALSE;
897 }
898
899 sdp_conn = sdp_media_connections (new_media);
900 if (sdp_conn == NULL)
901 {
902 g_warning ("Stream %u: no valid remote connections", priv->id);
903 return FALSE;
904 }
905
906 /* Note: always update the pointer to the current media structure
907 * because of memory management done in the session object */
908 old_media = priv->remote_media;
909 priv->remote_media = new_media;
910
911 /* Check if there was any media update at all */
912
913 if (sdp_media_cmp (old_media, new_media) == 0)
914 {
915 DEBUG("no media changes detected for the stream");
916 return TRUE;
917 }
918
919 new_direction = sip_tp_stream_direction_from_remote (new_media->m_mode);
920
921 /* Make sure the answer can only remove sending or receiving bits
922 * of the offer */
923 if (!authoritative)
924 new_direction &= priv->direction;
925
926 /* Check if the transport candidate needs to be changed */
927 if (old_media != NULL)
928 {
929 if (!sdp_connection_cmp (sdp_media_connections (old_media), sdp_conn))
930 transport_changed = FALSE;
931
932 /* Disable sending at this point if it will be disabled
933 * accordingly to the new direction */
934 priv_update_sending (stream,
935 priv->direction & new_direction,
936 priv->pending_send_flags);
937 }
938
939 /* First add the new candidate, then update the codec set.
940 * The offerer isn't supposed to send us anything from the new transport
941 * until we accept; if it's the answer, both orderings have problems. */
942
943 if (transport_changed)
944 {
945 /* Make sure we stop sending before we use the new set of codecs
946 * intended for the new connection */
947 sip_media_stream_set_sending (stream, FALSE);
948
949 push_remote_candidates (stream);
950 }
951
952 push_remote_codecs (stream);
953
954 priv->codec_intersect_pending = TRUE;
955
956 /* TODO: this will go to session change commit code */
957
958 /* note: for outbound sessions (for which remote cands become
959 * available at a later stage), emit active candidate pair
960 * (and playing status?) once remote info set */
961 push_active_candidate_pair (stream);
962
963 /* Set the final direction and sending status */
964 sip_media_stream_set_direction (stream,
965 new_direction,
966 priv->pending_send_flags);
967
968 return TRUE;
969 }
970
971 /**
972 * Converts a sofia-sip media type enum to Telepathy media type.
973 * See <sofia-sip/sdp.h> and <telepathy-constants.h>.
974 *
975 * @return G_MAXUINT if the media type cannot be mapped
976 */
977 guint
sip_tp_media_type(sdp_media_e sip_mtype)978 sip_tp_media_type (sdp_media_e sip_mtype)
979 {
980 switch (sip_mtype)
981 {
982 case sdp_media_audio: return TP_MEDIA_STREAM_TYPE_AUDIO;
983 case sdp_media_video: return TP_MEDIA_STREAM_TYPE_VIDEO;
984 default: return G_MAXUINT;
985 }
986
987 g_assert_not_reached();
988 }
989
990 /**
991 * Sets the media state to playing or non-playing. When not playing,
992 * received RTP packets may not be played locally.
993 */
sip_media_stream_set_playing(SIPMediaStream * stream,gboolean playing)994 void sip_media_stream_set_playing (SIPMediaStream *stream, gboolean playing)
995 {
996 SIPMediaStreamPrivate *priv;
997 priv = SIP_MEDIA_STREAM_GET_PRIVATE (stream);
998
999 if (same_boolean (priv->playing, playing))
1000 return;
1001
1002 DEBUG("set playing to %s", playing? "TRUE" : "FALSE");
1003
1004 priv->playing = playing;
1005
1006 if (priv->ready_received)
1007 tp_svc_media_stream_handler_emit_set_stream_playing (
1008 (TpSvcMediaStreamHandler *)stream, playing);
1009 }
1010
1011 /**
1012 * Sets the media state to sending or non-sending. When not sending,
1013 * captured media are not sent over the network.
1014 */
1015 void
sip_media_stream_set_sending(SIPMediaStream * stream,gboolean sending)1016 sip_media_stream_set_sending (SIPMediaStream *stream, gboolean sending)
1017 {
1018 SIPMediaStreamPrivate *priv;
1019 priv = SIP_MEDIA_STREAM_GET_PRIVATE (stream);
1020
1021 if (same_boolean(priv->sending, sending))
1022 return;
1023
1024 DEBUG("set sending to %s", sending? "TRUE" : "FALSE");
1025
1026 priv->sending = sending;
1027
1028 if (priv->ready_received)
1029 tp_svc_media_stream_handler_emit_set_stream_sending (
1030 (TpSvcMediaStreamHandler *)stream, sending);
1031 }
1032
1033 static void
priv_update_sending(SIPMediaStream * stream,TpMediaStreamDirection direction,guint pending_send_flags)1034 priv_update_sending (SIPMediaStream *stream,
1035 TpMediaStreamDirection direction,
1036 guint pending_send_flags)
1037 {
1038 sip_media_stream_set_sending (stream,
1039 (direction & TP_MEDIA_STREAM_DIRECTION_SEND) != 0
1040 && !(pending_send_flags
1041 & (TP_MEDIA_STREAM_PENDING_REMOTE_SEND
1042 | TP_MEDIA_STREAM_PENDING_LOCAL_SEND)));
1043 }
1044
1045 void
sip_media_stream_set_direction(SIPMediaStream * stream,TpMediaStreamDirection direction,guint pending_send_flags)1046 sip_media_stream_set_direction (SIPMediaStream *stream,
1047 TpMediaStreamDirection direction,
1048 guint pending_send_flags)
1049 {
1050 SIPMediaStreamPrivate *priv;
1051 priv = SIP_MEDIA_STREAM_GET_PRIVATE (stream);
1052
1053 if (priv->direction != direction
1054 || priv->pending_send_flags != pending_send_flags)
1055 {
1056 DEBUG("setting direction %u, pending send flags %u", direction, pending_send_flags);
1057
1058 priv->direction = direction;
1059 priv->pending_send_flags = pending_send_flags;
1060
1061 /* TODO: SDP should not be cached, but created on demand */
1062 if (priv->native_cands_prepared && priv->native_codecs_prepared)
1063 priv_update_local_sdp (stream);
1064
1065 g_signal_emit (stream, signals[SIG_DIRECTION_CHANGED], 0,
1066 direction, pending_send_flags);
1067 }
1068
1069 if (priv->remote_media != NULL)
1070 priv_update_sending (stream, direction, pending_send_flags);
1071 }
1072
1073 /**
1074 * Returns true if the stream has a valid SDP description and
1075 * connection has been established with the stream engine.
1076 */
sip_media_stream_is_ready(SIPMediaStream * self)1077 gboolean sip_media_stream_is_ready (SIPMediaStream *self)
1078 {
1079 SIPMediaStreamPrivate *priv;
1080 priv = SIP_MEDIA_STREAM_GET_PRIVATE (self);
1081 g_assert (priv->stream_sdp == NULL || priv->ready_received);
1082 return (priv->stream_sdp != NULL);
1083 }
1084
1085 gboolean
sip_media_stream_is_codec_intersect_pending(SIPMediaStream * self)1086 sip_media_stream_is_codec_intersect_pending (SIPMediaStream *self)
1087 {
1088 SIPMediaStreamPrivate *priv = SIP_MEDIA_STREAM_GET_PRIVATE (self);
1089 return priv->codec_intersect_pending;
1090 }
1091
1092 void
sip_media_stream_start_telephony_event(SIPMediaStream * self,guchar event)1093 sip_media_stream_start_telephony_event (SIPMediaStream *self, guchar event)
1094 {
1095 tp_svc_media_stream_handler_emit_start_telephony_event (
1096 (TpSvcMediaStreamHandler *)self, event);
1097 }
1098
1099 void
sip_media_stream_stop_telephony_event(SIPMediaStream * self)1100 sip_media_stream_stop_telephony_event (SIPMediaStream *self)
1101 {
1102 tp_svc_media_stream_handler_emit_stop_telephony_event (
1103 (TpSvcMediaStreamHandler *)self);
1104 }
1105
1106 static void
priv_generate_sdp(SIPMediaStream * self)1107 priv_generate_sdp (SIPMediaStream *self)
1108 {
1109 SIPMediaStreamPrivate *priv = SIP_MEDIA_STREAM_GET_PRIVATE (self);
1110
1111 if (priv->stream_sdp != NULL)
1112 return;
1113
1114 priv_update_local_sdp (self);
1115
1116 g_assert (priv->stream_sdp != NULL);
1117
1118 g_signal_emit (self, signals[SIG_READY], 0);
1119 }
1120
1121 static void
priv_parse_fmtp(su_home_t * home,const char * fmtp,GHashTable * param_hash)1122 priv_parse_fmtp (su_home_t *home, const char *fmtp, GHashTable *param_hash)
1123 {
1124 const msg_param_t *param_list = NULL;
1125 char *s;
1126 const char *avpair;
1127 const char *pv;
1128 char *attr;
1129 int i;
1130
1131 if (!fmtp)
1132 return;
1133
1134 s = su_strdup (home, fmtp);
1135 if (msg_avlist_d (home, &s, ¶m_list) < 0)
1136 {
1137 DEBUG("format-specific parameters can't be parsed as an attribute list: %s", fmtp);
1138 goto pass_as_is;
1139 }
1140 if (*s)
1141 {
1142 DEBUG("unexpected parser exit (whitespace?) while parsing format-specific parameters: %s", fmtp);
1143 goto pass_as_is;
1144 }
1145
1146 g_assert (param_list != NULL);
1147
1148 if (param_list[0] != NULL
1149 && param_list[1] == NULL
1150 && !strchr (param_list[0], '='))
1151 {
1152 DEBUG("passing freeform parameter string verbatim: %s", fmtp);
1153 goto pass_as_is;
1154 }
1155
1156 for (i = 0; (avpair = param_list[i]) != NULL; i++)
1157 {
1158 pv = strchr (avpair, '=');
1159 if (pv)
1160 {
1161 attr = su_strndup (home, avpair, pv - avpair);
1162 DEBUG("parsed attribute-value pair %s=%s", attr, pv + 1);
1163 g_hash_table_insert (param_hash, attr, (gpointer) (pv + 1));
1164 }
1165 else
1166 {
1167 DEBUG("parsed freeform parameter %s", avpair);
1168 g_hash_table_insert (param_hash, (gpointer) avpair, NULL);
1169 }
1170 }
1171
1172 return;
1173
1174 pass_as_is:
1175 g_hash_table_insert (param_hash, "", (gpointer) fmtp);
1176 }
1177
1178 /**
1179 * Notify StreamEngine of remote codecs.
1180 *
1181 * @pre Ready signal must be receiveid (priv->ready_received)
1182 */
push_remote_codecs(SIPMediaStream * stream)1183 static void push_remote_codecs (SIPMediaStream *stream)
1184 {
1185 SIPMediaStreamPrivate *priv;
1186 GPtrArray *codecs;
1187 GType codecs_type;
1188 GType codec_type;
1189 const sdp_media_t *sdpmedia;
1190 const sdp_rtpmap_t *rtpmap;
1191 su_home_t temphome[1] = { SU_HOME_INIT(temphome) };
1192
1193 DEBUG ("enter");
1194
1195 priv = SIP_MEDIA_STREAM_GET_PRIVATE (stream);
1196
1197 sdpmedia = priv->remote_media;
1198 if (sdpmedia == NULL)
1199 {
1200 DEBUG("remote media description is not received yet");
1201 return;
1202 }
1203
1204 if (!priv->ready_received)
1205 {
1206 DEBUG("the stream engine is not ready, SetRemoteCodecs is pending");
1207 priv->push_remote_requested = TRUE;
1208 return;
1209 }
1210
1211 codec_type = sip_tp_codec_struct_type ();
1212 codecs_type = sip_tp_codec_list_type ();
1213
1214 codecs = dbus_g_type_specialized_construct (codecs_type);
1215
1216 rtpmap = sdpmedia->m_rtpmaps;
1217 while (rtpmap)
1218 {
1219 GValue codec = { 0, };
1220 GHashTable *opt_params;
1221
1222 g_value_init (&codec, codec_type);
1223 g_value_take_boxed (&codec,
1224 dbus_g_type_specialized_construct (codec_type));
1225
1226 /* Note: all strings in opt_params hash are either allocated at temphome
1227 * or referenced as is from the rtpmap structure */
1228 opt_params = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
1229 priv_parse_fmtp (temphome, rtpmap->rm_fmtp, opt_params);
1230
1231 /* RFC2327: see "m=" line definition
1232 * - note, 'encoding_params' is assumed to be channel
1233 * count (i.e. channels in farsight) */
1234
1235 dbus_g_type_struct_set (&codec,
1236 /* payload type: */
1237 0, rtpmap->rm_pt,
1238 /* encoding name: */
1239 1, rtpmap->rm_encoding,
1240 /* media type */
1241 2, (guint)priv->media_type,
1242 /* clock-rate */
1243 3, rtpmap->rm_rate,
1244 /* number of supported channels: */
1245 4, rtpmap->rm_params ? atoi(rtpmap->rm_params) : 0,
1246 /* optional params: */
1247 5, opt_params,
1248 G_MAXUINT);
1249
1250 g_ptr_array_add (codecs, g_value_get_boxed (&codec));
1251
1252 g_hash_table_destroy (opt_params);
1253
1254 rtpmap = rtpmap->rm_next;
1255 }
1256
1257 su_home_deinit (temphome);
1258
1259 SESSION_DEBUG(priv->session, "passing %d remote codecs to stream engine",
1260 codecs->len);
1261
1262 if (codecs->len > 0) {
1263 tp_svc_media_stream_handler_emit_set_remote_codecs (
1264 (TpSvcMediaStreamHandler *)stream, codecs);
1265 }
1266
1267 g_boxed_free (codecs_type, codecs);
1268 }
1269
push_remote_candidates(SIPMediaStream * stream)1270 static void push_remote_candidates (SIPMediaStream *stream)
1271 {
1272 SIPMediaStreamPrivate *priv;
1273 GValue candidate = { 0 };
1274 GValue transport = { 0 };
1275 GPtrArray *candidates;
1276 GPtrArray *transports;
1277 GType candidate_type;
1278 GType candidates_type;
1279 GType transport_type;
1280 GType transports_type;
1281 const sdp_media_t *media;
1282 const sdp_connection_t *sdp_conn;
1283 gchar *candidate_id;
1284 guint port;
1285
1286 DEBUG("enter");
1287
1288 priv = SIP_MEDIA_STREAM_GET_PRIVATE (stream);
1289
1290 media = priv->remote_media;
1291 if (media == NULL)
1292 {
1293 DEBUG("remote media description is not received yet");
1294 return;
1295 }
1296
1297 if (!priv->ready_received)
1298 {
1299 DEBUG("the stream engine is not ready, SetRemoteCandidateList is pending");
1300 priv->push_remote_requested = TRUE;
1301 return;
1302 }
1303
1304 /* use the address from SDP c-line as the only remote candidate */
1305
1306 sdp_conn = sdp_media_connections (media);
1307 g_return_if_fail (sdp_conn != NULL);
1308
1309 port = (guint) media->m_port;
1310
1311 transport_type = sip_tp_transport_struct_type ();
1312 g_value_init (&transport, transport_type);
1313 g_value_take_boxed (&transport,
1314 dbus_g_type_specialized_construct (transport_type));
1315 dbus_g_type_struct_set (&transport,
1316 0, 0, /* component number */
1317 1, sdp_conn->c_address,
1318 2, port,
1319 3, TP_MEDIA_STREAM_BASE_PROTO_UDP,
1320 4, "RTP",
1321 5, "AVP",
1322 /* 6, 0.0f, */
1323 7, TP_MEDIA_STREAM_TRANSPORT_TYPE_LOCAL,
1324 /* 8, "", */
1325 /* 9, "", */
1326 G_MAXUINT);
1327
1328 DEBUG("remote address=<%s>, port=<%u>", sdp_conn->c_address, port);
1329
1330 transports_type = sip_tp_transport_list_type ();
1331 transports = dbus_g_type_specialized_construct (transports_type);
1332 g_ptr_array_add (transports, g_value_get_boxed (&transport));
1333
1334 g_free (priv->remote_candidate_id);
1335 candidate_id = g_strdup_printf ("L%u", ++priv->remote_candidate_counter);
1336 priv->remote_candidate_id = candidate_id;
1337
1338 candidate_type = sip_tp_candidate_struct_type ();
1339 g_value_init (&candidate, candidate_type);
1340 g_value_take_boxed (&candidate,
1341 dbus_g_type_specialized_construct (candidate_type));
1342 dbus_g_type_struct_set (&candidate,
1343 0, candidate_id,
1344 1, transports,
1345 G_MAXUINT);
1346
1347 candidates_type = sip_tp_candidate_list_type ();
1348 candidates = dbus_g_type_specialized_construct (candidates_type);
1349 g_ptr_array_add (candidates, g_value_get_boxed (&candidate));
1350
1351 DEBUG("emitting SetRemoteCandidateList with %s", candidate_id);
1352
1353 tp_svc_media_stream_handler_emit_set_remote_candidate_list (
1354 (TpSvcMediaStreamHandler *)stream, candidates);
1355
1356 g_boxed_free (candidates_type, candidates);
1357 g_boxed_free (transports_type, transports);
1358
1359 #if 0
1360 if (priv->playing) {
1361 g_debug ("%s: emitting SetStreamPlaying TRUE", G_STRFUNC);
1362 tp_svc_media_stream_handler_emit_set_stream_playing (
1363 (TpSvcMediaStreamHandler *)stream, TRUE);
1364 }
1365 #endif
1366 }
1367
1368 static void
push_active_candidate_pair(SIPMediaStream * stream)1369 push_active_candidate_pair (SIPMediaStream *stream)
1370 {
1371 SIPMediaStreamPrivate *priv;
1372
1373 DEBUG("enter");
1374
1375 priv = SIP_MEDIA_STREAM_GET_PRIVATE (stream);
1376
1377 if (priv->ready_received
1378 && priv->native_candidate_id != NULL
1379 && priv->remote_candidate_id != NULL)
1380 {
1381 DEBUG("emitting SetActiveCandidatePair for %s-%s",
1382 priv->native_candidate_id, priv->remote_candidate_id);
1383 tp_svc_media_stream_handler_emit_set_active_candidate_pair (
1384 stream, priv->native_candidate_id, priv->remote_candidate_id);
1385 }
1386 }
1387
priv_media_type_to_str(guint media_type)1388 static const char* priv_media_type_to_str(guint media_type)
1389 {
1390 switch (media_type)
1391 {
1392 case TP_MEDIA_STREAM_TYPE_AUDIO: return "audio";
1393 case TP_MEDIA_STREAM_TYPE_VIDEO: return "video";
1394 default: g_assert_not_reached ();
1395 ;
1396 }
1397 return "-";
1398 }
1399
1400 static void
priv_marshal_param(gpointer key,gpointer val,gpointer user_data)1401 priv_marshal_param (gpointer key,
1402 gpointer val,
1403 gpointer user_data)
1404 {
1405 GString *params = (GString *) user_data;
1406 const char *name = (const char *) key;
1407 const char *value = (const char *) val;
1408
1409 if (params->len != 0)
1410 g_string_append_c (params, ';');
1411
1412 if (name == NULL || !*name)
1413 g_string_append_printf (params, "%s", value);
1414 else if (value == NULL)
1415 g_string_append_printf (params, "%s", name);
1416 else
1417 g_string_append_printf (params, "%s=%s", name, value);
1418 }
1419
1420 /**
1421 * Refreshes the local SDP based on Farsight stream, and current
1422 * object, state.
1423 */
1424 static void
priv_update_local_sdp(SIPMediaStream * stream)1425 priv_update_local_sdp(SIPMediaStream *stream)
1426 {
1427 SIPMediaStreamPrivate *priv;
1428 GString *mline;
1429 GString *alines;
1430 gchar *cline;
1431 GValue transport = { 0 };
1432 GValue codec = { 0, };
1433 const GPtrArray *codecs, *candidates;
1434 gchar *tr_addr = NULL;
1435 /* gchar *tr_user = NULL; */
1436 /* gchar *tr_pass = NULL; */
1437 gchar *tr_subtype = NULL;
1438 gchar *tr_profile = NULL;
1439 guint tr_port;
1440 /* guint tr_component; */
1441 guint tr_proto;
1442 /* guint tr_type; */
1443 /* gdouble tr_pref; */
1444 const gchar *dirline;
1445 const gchar *iproto;
1446 int i;
1447
1448 /* Note: impl limits ...
1449 * - no multi-stream support
1450 * - no IPv6 support (missing from the Farsight API?)
1451 */
1452
1453 g_assert (SIP_IS_MEDIA_STREAM (stream));
1454 priv = SIP_MEDIA_STREAM_GET_PRIVATE (stream);
1455
1456 candidates = g_value_get_boxed (&priv->native_candidates);
1457 codecs = g_value_get_boxed (&priv->native_codecs);
1458
1459 g_value_init (&transport, sip_tp_transport_struct_type ());
1460 g_value_init (&codec, sip_tp_codec_struct_type ());
1461
1462 /* Find the preferred candidate, if defined,
1463 * else the last acceptable candidate */
1464
1465 for (i = candidates->len - 1; i >= 0; --i)
1466 {
1467 GValueArray *candidate;
1468 const gchar *candidate_id;
1469 const GPtrArray *ca_tports;
1470
1471 candidate = g_ptr_array_index (candidates, i);
1472 candidate_id =
1473 g_value_get_string (g_value_array_get_nth (candidate, 0));
1474 ca_tports = g_value_get_boxed (g_value_array_get_nth (candidate, 1));
1475
1476 g_return_if_fail (ca_tports->len >= 1);
1477
1478 g_value_set_static_boxed (&transport, g_ptr_array_index (ca_tports, 0));
1479
1480 dbus_g_type_struct_get (&transport,
1481 /* 0, &tr_component, */
1482 1, &tr_addr,
1483 2, &tr_port,
1484 3, &tr_proto,
1485 4, &tr_subtype,
1486 5, &tr_profile,
1487 /* 6, &tr_pref, */
1488 /* 7, &tr_type, */
1489 /* 8, &tr_user, */
1490 /* 9, &tr_pass, */
1491 G_MAXUINT);
1492
1493 if (priv->native_candidate_id != NULL)
1494 {
1495 if (!strcmp (candidate_id, priv->native_candidate_id))
1496 break;
1497 }
1498 else if (tr_proto == TP_MEDIA_STREAM_BASE_PROTO_UDP)
1499 {
1500 g_free (priv->native_candidate_id);
1501 priv->native_candidate_id = g_strdup (candidate_id);
1502 break;
1503 }
1504 }
1505 g_return_if_fail (i >= 0);
1506 g_return_if_fail (tr_addr != NULL);
1507 g_return_if_fail (tr_subtype != NULL);
1508 g_return_if_fail (tr_profile != NULL);
1509
1510 mline = g_string_new ("m=");
1511 g_string_append_printf (mline,
1512 "%s %u %s/%s",
1513 priv_media_type_to_str (priv->media_type),
1514 tr_port,
1515 tr_subtype,
1516 tr_profile);
1517
1518 iproto = (strchr (tr_addr, ':') == NULL)? "IP4" : "IP6";
1519 cline = g_strdup_printf ("c=IN %s %s\r\n", iproto, tr_addr);
1520
1521 switch (priv->direction)
1522 {
1523 case TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL:
1524 dirline = "";
1525 break;
1526 case TP_MEDIA_STREAM_DIRECTION_SEND:
1527 dirline = "a=sendonly\r\n";
1528 break;
1529 case TP_MEDIA_STREAM_DIRECTION_RECEIVE:
1530 dirline = "a=recvonly\r\n";
1531 break;
1532 case TP_MEDIA_STREAM_DIRECTION_NONE:
1533 dirline = "a=inactive\r\n";
1534 break;
1535 default:
1536 g_assert_not_reached();
1537 }
1538
1539 alines = g_string_new (dirline);
1540
1541 for (i = 0; i < codecs->len; i++) {
1542 guint co_id, co_type, co_clockrate, co_channels;
1543 gchar *co_name;
1544 GHashTable *co_params;
1545
1546 g_value_set_static_boxed (&codec, g_ptr_array_index (codecs, i));
1547
1548 dbus_g_type_struct_get (&codec,
1549 0, &co_id,
1550 1, &co_name,
1551 2, &co_type,
1552 3, &co_clockrate,
1553 4, &co_channels,
1554 5, &co_params,
1555 G_MAXUINT);
1556
1557 g_return_if_fail (co_type == priv->media_type);
1558
1559 /* Add rtpmap entry to media a-lines */
1560 g_string_append_printf (alines,
1561 "a=rtpmap:%u %s/%u",
1562 co_id,
1563 co_name,
1564 co_clockrate);
1565 if (co_channels > 1)
1566 g_string_append_printf (alines, "/%u", co_channels);
1567 g_string_append (alines, "\r\n");
1568
1569 /* Marshal parameters into the fmtp attribute */
1570 if (g_hash_table_size (co_params) != 0)
1571 {
1572 GString *fmtp_value;
1573 g_string_append_printf (alines, "a=fmtp:%u ", co_id);
1574 fmtp_value = g_string_new (NULL);
1575 g_hash_table_foreach (co_params, priv_marshal_param, fmtp_value);
1576 g_string_append (alines, fmtp_value->str);
1577 g_string_free (fmtp_value, TRUE);
1578 g_string_append (alines, "\r\n");
1579 }
1580
1581 /* Add PT id to mline */
1582 g_string_append_printf (mline, " %u", co_id);
1583
1584 g_free (co_name);
1585 g_hash_table_destroy (co_params);
1586 }
1587
1588 g_free(priv->stream_sdp);
1589 priv->stream_sdp = g_strconcat(mline->str, "\r\n",
1590 cline,
1591 #if 0 /* Some Cisco GWs do not like these */
1592 "b=RS:0\r\nb=RR:0\r\n",
1593 #endif
1594 alines->str,
1595 NULL);
1596
1597 g_free (tr_addr);
1598 g_free (tr_profile);
1599 g_free (tr_subtype);
1600 /* g_free (tr_user); */
1601 /* g_free (tr_pass); */
1602
1603 g_string_free (mline, TRUE);
1604 g_free (cline);
1605 g_string_free (alines, TRUE);
1606 }
1607
1608 static void
stream_handler_iface_init(gpointer g_iface,gpointer iface_data)1609 stream_handler_iface_init (gpointer g_iface, gpointer iface_data)
1610 {
1611 TpSvcMediaStreamHandlerClass *klass = (TpSvcMediaStreamHandlerClass *)g_iface;
1612
1613 #define IMPLEMENT(x) tp_svc_media_stream_handler_implement_##x (\
1614 klass, (tp_svc_media_stream_handler_##x##_impl) sip_media_stream_##x)
1615 IMPLEMENT(codec_choice);
1616 IMPLEMENT(error);
1617 IMPLEMENT(native_candidates_prepared);
1618 IMPLEMENT(new_active_candidate_pair);
1619 IMPLEMENT(new_native_candidate);
1620 IMPLEMENT(ready);
1621 /* IMPLEMENT(set_local_codecs); */
1622 IMPLEMENT(stream_state);
1623 IMPLEMENT(supported_codecs);
1624 #undef IMPLEMENT
1625 }
1626