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, &param_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