telepathygabble/src/gabble-media-stream.c
changeset 10 59927b2d3b75
parent 0 d0f3a028347a
equal deleted inserted replaced
0:d0f3a028347a 10:59927b2d3b75
     1 /*
       
     2  * gabble-media-stream.c - Source for GabbleMediaStream
       
     3  * Copyright (C) 2006 Collabora Ltd.
       
     4  * 
       
     5  *   @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk>
       
     6  *
       
     7  * This library is free software; you can redistribute it and/or
       
     8  * modify it under the terms of the GNU Lesser General Public
       
     9  * License as published by the Free Software Foundation; either
       
    10  * version 2.1 of the License, or (at your option) any later version.
       
    11  *
       
    12  * This library is distributed in the hope that it will be useful,
       
    13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    15  * Lesser General Public License for more details.
       
    16  *
       
    17  * You should have received a copy of the GNU Lesser General Public
       
    18  * License along with this library; if not, write to the Free Software
       
    19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
       
    20  */
       
    21 
       
    22 #include <dbus/dbus-glib.h>
       
    23 #include <stdio.h>
       
    24 #include <stdlib.h>
       
    25 #include <string.h>
       
    26 
       
    27 
       
    28 #include "ansi.h"
       
    29 #include "debug.h"
       
    30 #include "handles.h"
       
    31 #include "namespaces.h"
       
    32 
       
    33 #include "gabble-connection.h"
       
    34 #include "gabble-media-channel.h"
       
    35 #include "gabble-media-session.h"
       
    36 #include "gabble-media-session-enumtypes.h"
       
    37 
       
    38 #include "telepathy-helpers.h"
       
    39 #include "telepathy-constants.h"
       
    40 
       
    41 #include "gabble-media-stream.h"
       
    42 #include "gabble-media-stream-signals-marshal.h"
       
    43 #include "gabble-media-stream-glue.h"
       
    44 
       
    45 #include "gabble_enums.h"
       
    46 
       
    47 #ifndef EMULATOR
       
    48 G_DEFINE_TYPE(GabbleMediaStream, gabble_media_stream, G_TYPE_OBJECT)
       
    49 #endif
       
    50 
       
    51 #define DEBUG_FLAG GABBLE_DEBUG_MEDIA
       
    52 
       
    53 #ifdef DEBUG_FLAG
       
    54 //#define DEBUG(format, ...)
       
    55 #define DEBUGGING 0
       
    56 //#define NODE_DEBUG(n, s) ;
       
    57 #endif /* DEBUG_FLAG */
       
    58 
       
    59 /* signal enum */
       
    60 enum
       
    61 {
       
    62     DESTROY,
       
    63 
       
    64     ADD_REMOTE_CANDIDATE,
       
    65     CLOSE,
       
    66     REMOVE_REMOTE_CANDIDATE,
       
    67     SET_ACTIVE_CANDIDATE_PAIR,
       
    68     SET_REMOTE_CANDIDATE_LIST,
       
    69     SET_REMOTE_CODECS,
       
    70     SET_STREAM_PLAYING,
       
    71     SET_STREAM_SENDING,
       
    72 
       
    73     NEW_ACTIVE_CANDIDATE_PAIR,
       
    74     NEW_NATIVE_CANDIDATE,
       
    75     SUPPORTED_CODECS,
       
    76     ERROR,
       
    77 
       
    78     LAST_SIGNAL 
       
    79 #ifdef EMULATOR    
       
    80     = LAST_SIGNAL_MED_STREAM
       
    81 #endif
       
    82     
       
    83 };
       
    84 
       
    85 #ifdef EMULATOR
       
    86 #include "libgabble_wsd_solution.h"
       
    87 
       
    88 	GET_STATIC_ARRAY_FROM_TLS(signals,gabble_med_stream,guint)
       
    89 	#define signals (GET_WSD_VAR_NAME(signals,gabble_med_stream, s)())	
       
    90 	
       
    91 	GET_STATIC_VAR_FROM_TLS(gabble_media_stream_parent_class,gabble_med_stream,gpointer)
       
    92 	#define gabble_media_stream_parent_class (*GET_WSD_VAR_NAME(gabble_media_stream_parent_class,gabble_med_stream,s)())
       
    93 	
       
    94 	GET_STATIC_VAR_FROM_TLS(g_define_type_id,gabble_med_stream,GType)
       
    95 	#define g_define_type_id (*GET_WSD_VAR_NAME(g_define_type_id,gabble_med_stream,s)())
       
    96 
       
    97 	/*gchar** _s_gabble_med_stream_video_codec_params() { return (gchar**)((libgabble_ImpurePtr()->_s_gabble_med_stream_video_codec_params)); }
       
    98 
       
    99 	#define video_codec_params (GET_WSD_VAR_NAME(video_codec_params,gabble_med_stream, s)())*/	
       
   100 
       
   101 	
       
   102 static void gabble_media_stream_init (GabbleMediaStream *self); 
       
   103 static void gabble_media_stream_class_init (GabbleMediaStreamClass *klass); 
       
   104 static void gabble_media_stream_class_intern_init (gpointer klass) 
       
   105 { 
       
   106 gabble_media_stream_parent_class = g_type_class_peek_parent (klass);
       
   107  gabble_media_stream_class_init ((GabbleMediaStreamClass*) klass);
       
   108 }
       
   109  EXPORT_C GType gabble_media_stream_get_type (void) 
       
   110  {
       
   111   if ((g_define_type_id == 0)) 
       
   112   {
       
   113    static const GTypeInfo g_define_type_info = { sizeof (GabbleMediaStreamClass), (GBaseInitFunc) ((void *)0), (GBaseFinalizeFunc) ((void *)0), (GClassInitFunc) gabble_media_stream_class_intern_init, (GClassFinalizeFunc) ((void *)0), ((void *)0), sizeof (GabbleMediaStream), 0, (GInstanceInitFunc) gabble_media_stream_init, ((void *)0) }; g_define_type_id = g_type_register_static ( ((GType) ((20) << (2))), g_intern_static_string ("GabbleMediaStream"), &g_define_type_info, (GTypeFlags) 0); { {} ; } } return g_define_type_id; 
       
   114   };
       
   115 
       
   116 		
       
   117 #else
       
   118 
       
   119 	static guint signals[LAST_SIGNAL] = {0};
       
   120 
       
   121 #endif
       
   122 
       
   123 
       
   124 /* properties */
       
   125 enum
       
   126 {
       
   127   PROP_CONNECTION = 1,
       
   128   PROP_MEDIA_SESSION,
       
   129   PROP_OBJECT_PATH,
       
   130   PROP_MODE,
       
   131   PROP_NAME,
       
   132   PROP_ID,
       
   133   PROP_INITIATOR,
       
   134   PROP_MEDIA_TYPE,
       
   135   PROP_CONNECTION_STATE,
       
   136   PROP_READY,
       
   137   PROP_GOT_LOCAL_CODECS,
       
   138   PROP_SIGNALLING_STATE,
       
   139   PROP_PLAYING,
       
   140   PROP_COMBINED_DIRECTION,
       
   141   LAST_PROPERTY
       
   142 };
       
   143 
       
   144 /* private structure */
       
   145 typedef struct _GabbleMediaStreamPrivate GabbleMediaStreamPrivate;
       
   146 
       
   147 struct _GabbleMediaStreamPrivate
       
   148 {
       
   149   GabbleConnection *conn;
       
   150   GabbleMediaSession *session;
       
   151   GabbleMediaSessionMode mode;
       
   152   gchar *object_path;
       
   153   guint id;
       
   154   guint media_type;
       
   155 
       
   156   gboolean ready;
       
   157   gboolean sending;
       
   158 
       
   159   GValue native_codecs;     /* intersected codec list */
       
   160   GValue native_candidates;
       
   161 
       
   162   GValue remote_codecs;
       
   163   GValue remote_candidates;
       
   164 
       
   165   guint remote_candidate_count;
       
   166 
       
   167   gboolean closed;
       
   168   gboolean dispose_has_run;
       
   169 };
       
   170 
       
   171 #define GABBLE_MEDIA_STREAM_GET_PRIVATE(obj) \
       
   172     ((GabbleMediaStreamPrivate *)obj->priv)
       
   173 //Vinod: add below definition
       
   174 #define ENABLE_DEBUG
       
   175 
       
   176 #ifdef ENABLE_DEBUG
       
   177 #if _GMS_DEBUG_LEVEL > 1
       
   178 static const char *tp_protocols[] = {
       
   179   "TP_MEDIA_STREAM_PROTO_UDP (0)",
       
   180   "TP_MEDIA_STREAM_PROTO_TCP (1)"
       
   181 };
       
   182 
       
   183 static const char *tp_transports[] = {
       
   184   "TP_MEDIA_STREAM_TRANSPORT_TYPE_LOCAL (0)",
       
   185   "TP_MEDIA_STREAM_TRANSPORT_TYPE_DERIVED (1)",
       
   186   "TP_MEDIA_STREAM_TRANSPORT_TYPE_RELAY (2)"
       
   187 };
       
   188 #endif
       
   189 #endif
       
   190 
       
   191 static void push_native_candidates (GabbleMediaStream *stream);
       
   192 static void push_remote_codecs (GabbleMediaStream *stream);
       
   193 static void push_remote_candidates (GabbleMediaStream *stream);
       
   194 static void push_playing (GabbleMediaStream *stream);
       
   195 static void push_sending (GabbleMediaStream *stream);
       
   196 
       
   197 static void
       
   198 gabble_media_stream_init (GabbleMediaStream *self)
       
   199 {
       
   200   GabbleMediaStreamPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
       
   201       GABBLE_TYPE_MEDIA_STREAM, GabbleMediaStreamPrivate);
       
   202 
       
   203   self->priv = priv;
       
   204 
       
   205   g_value_init (&priv->native_codecs, TP_TYPE_CODEC_LIST);
       
   206   g_value_take_boxed (&priv->native_codecs,
       
   207       dbus_g_type_specialized_construct (TP_TYPE_CODEC_LIST));
       
   208 
       
   209   g_value_init (&priv->native_candidates, TP_TYPE_CANDIDATE_LIST);
       
   210   g_value_take_boxed (&priv->native_candidates,
       
   211       dbus_g_type_specialized_construct (TP_TYPE_CANDIDATE_LIST));
       
   212 
       
   213   g_value_init (&priv->remote_codecs, TP_TYPE_CODEC_LIST);
       
   214   g_value_take_boxed (&priv->remote_codecs,
       
   215       dbus_g_type_specialized_construct (TP_TYPE_CODEC_LIST));
       
   216 
       
   217   g_value_init (&priv->remote_candidates, TP_TYPE_CANDIDATE_LIST);
       
   218   g_value_take_boxed (&priv->remote_candidates,
       
   219       dbus_g_type_specialized_construct (TP_TYPE_CANDIDATE_LIST));
       
   220 }
       
   221 
       
   222 static GObject *
       
   223 gabble_media_stream_constructor (GType type, guint n_props,
       
   224                                  GObjectConstructParam *props)
       
   225 {
       
   226   GObject *obj;
       
   227   GabbleMediaStreamPrivate *priv;
       
   228   DBusGConnection *bus;
       
   229 
       
   230   /* call base class constructor */
       
   231   obj = G_OBJECT_CLASS (gabble_media_stream_parent_class)->
       
   232            constructor (type, n_props, props);
       
   233   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (GABBLE_MEDIA_STREAM (obj));
       
   234 
       
   235   /* go for the bus */
       
   236   bus = tp_get_bus ();
       
   237   dbus_g_connection_register_g_object (bus, priv->object_path, obj);
       
   238 
       
   239   return obj;
       
   240 }
       
   241 
       
   242 static void
       
   243 gabble_media_stream_get_property (GObject    *object,
       
   244                                   guint       property_id,
       
   245                                   GValue     *value,
       
   246                                   GParamSpec *pspec)
       
   247 {
       
   248   GabbleMediaStream *stream = GABBLE_MEDIA_STREAM (object);
       
   249   GabbleMediaStreamPrivate *priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream);
       
   250 
       
   251   switch (property_id) {
       
   252     case PROP_CONNECTION:
       
   253       g_value_set_object (value, priv->conn);
       
   254       break;
       
   255     case PROP_MEDIA_SESSION:
       
   256       g_value_set_object (value, priv->session);
       
   257       break;
       
   258     case PROP_OBJECT_PATH:
       
   259       g_value_set_string (value, priv->object_path);
       
   260       break;
       
   261     case PROP_MODE:
       
   262       g_value_set_enum (value, priv->mode);
       
   263       break;
       
   264     case PROP_NAME:
       
   265       g_value_set_string (value, stream->name);
       
   266       break;
       
   267     case PROP_ID:
       
   268       g_value_set_uint (value, priv->id);
       
   269       break;
       
   270     case PROP_INITIATOR:
       
   271       g_value_set_uint (value, stream->initiator);
       
   272       break;
       
   273     case PROP_MEDIA_TYPE:
       
   274       g_value_set_uint (value, priv->media_type);
       
   275       break;
       
   276     case PROP_CONNECTION_STATE:
       
   277       g_value_set_uint (value, stream->connection_state);
       
   278       break;
       
   279     case PROP_READY:
       
   280       g_value_set_boolean (value, priv->ready);
       
   281       break;
       
   282     case PROP_GOT_LOCAL_CODECS:
       
   283       g_value_set_boolean (value, stream->got_local_codecs);
       
   284       break;
       
   285     case PROP_SIGNALLING_STATE:
       
   286       g_value_set_uint (value, stream->signalling_state);
       
   287       break;
       
   288     case PROP_PLAYING:
       
   289       g_value_set_boolean (value, stream->playing);
       
   290       break;
       
   291     case PROP_COMBINED_DIRECTION:
       
   292       g_value_set_uint (value, stream->combined_direction);
       
   293       break;
       
   294     default:
       
   295       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       
   296       break;
       
   297   }
       
   298 }
       
   299 
       
   300 static void
       
   301 gabble_media_stream_set_property (GObject      *object,
       
   302                                   guint         property_id,
       
   303                                   const GValue *value,
       
   304                                   GParamSpec   *pspec)
       
   305 {
       
   306   GabbleMediaStream *stream = GABBLE_MEDIA_STREAM (object);
       
   307   GabbleMediaStreamPrivate *priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream);
       
   308 
       
   309   switch (property_id) {
       
   310     case PROP_CONNECTION:
       
   311       priv->conn = g_value_get_object (value);
       
   312       break;
       
   313     case PROP_MEDIA_SESSION:
       
   314       priv->session = g_value_get_object (value);
       
   315       break;
       
   316     case PROP_OBJECT_PATH:
       
   317       g_free (priv->object_path);
       
   318       priv->object_path = g_value_dup_string (value);
       
   319       break;
       
   320     case PROP_MODE:
       
   321       priv->mode = g_value_get_enum (value);
       
   322       break;
       
   323     case PROP_NAME:
       
   324       g_free (stream->name);
       
   325       stream->name = g_value_dup_string (value);
       
   326       break;
       
   327     case PROP_ID:
       
   328       priv->id = g_value_get_uint (value);
       
   329       break;
       
   330     case PROP_INITIATOR:
       
   331       stream->initiator = g_value_get_uint (value);
       
   332       break;
       
   333     case PROP_MEDIA_TYPE:
       
   334       priv->media_type = g_value_get_uint (value);
       
   335       break;
       
   336     case PROP_CONNECTION_STATE:
       
   337       _gabble_media_session_debug (priv->session, DEBUG_MSG_INFO, DEBUG_MSG_INFO, "stream %s connection state %d",
       
   338           stream->name, stream->connection_state);
       
   339       stream->connection_state = g_value_get_uint (value);
       
   340       break;
       
   341     case PROP_READY:
       
   342       priv->ready = g_value_get_boolean (value);
       
   343       break;
       
   344     case PROP_GOT_LOCAL_CODECS:
       
   345       stream->got_local_codecs = g_value_get_boolean (value);
       
   346       break;
       
   347     case PROP_SIGNALLING_STATE:
       
   348         {
       
   349           StreamSignallingState old = stream->signalling_state;
       
   350           stream->signalling_state = g_value_get_uint (value);
       
   351           _gabble_media_session_debug (priv->session, DEBUG_MSG_INFO, DEBUG_MSG_INFO, "stream %s sig_state %d->%d",
       
   352               stream->name, old, stream->signalling_state);
       
   353           if (stream->signalling_state != old)
       
   354             push_native_candidates (stream);
       
   355         }
       
   356       break;
       
   357     case PROP_PLAYING:
       
   358         {
       
   359           gboolean old = stream->playing;
       
   360           stream->playing = g_value_get_boolean (value);
       
   361           if (stream->playing != old)
       
   362             push_playing (stream);
       
   363         }
       
   364       break;
       
   365     case PROP_COMBINED_DIRECTION:
       
   366       stream->combined_direction = g_value_get_uint (value);
       
   367       break;
       
   368     default:
       
   369       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
       
   370       break;
       
   371   }
       
   372 }
       
   373 
       
   374 static void gabble_media_stream_dispose (GObject *object);
       
   375 static void gabble_media_stream_finalize (GObject *object);
       
   376 
       
   377 static void
       
   378 gabble_media_stream_class_init (GabbleMediaStreamClass *gabble_media_stream_class)
       
   379 {
       
   380   GObjectClass *object_class = G_OBJECT_CLASS (gabble_media_stream_class);
       
   381   GParamSpec *param_spec;
       
   382 
       
   383   g_type_class_add_private (gabble_media_stream_class, sizeof (GabbleMediaStreamPrivate));
       
   384 
       
   385   object_class->constructor = gabble_media_stream_constructor;
       
   386 
       
   387   object_class->get_property = gabble_media_stream_get_property;
       
   388   object_class->set_property = gabble_media_stream_set_property;
       
   389 
       
   390   object_class->dispose = gabble_media_stream_dispose;
       
   391   object_class->finalize = gabble_media_stream_finalize;
       
   392 
       
   393   param_spec = g_param_spec_object ("connection", "GabbleConnection object",
       
   394                                     "Gabble connection object that owns this "
       
   395                                     "media stream's channel.",
       
   396                                     GABBLE_TYPE_CONNECTION,
       
   397                                     G_PARAM_CONSTRUCT_ONLY |
       
   398                                     G_PARAM_READWRITE |
       
   399                                     G_PARAM_STATIC_NICK |
       
   400                                     G_PARAM_STATIC_BLURB);
       
   401   g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
       
   402 
       
   403   param_spec = g_param_spec_object ("media-session", "GabbleMediaSession object",
       
   404                                     "Gabble media session object that owns this "
       
   405                                     "media stream object.",
       
   406                                     GABBLE_TYPE_MEDIA_SESSION,
       
   407                                     G_PARAM_CONSTRUCT_ONLY |
       
   408                                     G_PARAM_READWRITE |
       
   409                                     G_PARAM_STATIC_NICK |
       
   410                                     G_PARAM_STATIC_BLURB);
       
   411   g_object_class_install_property (object_class, PROP_MEDIA_SESSION, param_spec);
       
   412 
       
   413   param_spec = g_param_spec_string ("object-path", "D-Bus object path",
       
   414                                     "The D-Bus object path used for this "
       
   415                                     "object on the bus.",
       
   416                                     NULL,
       
   417                                     G_PARAM_CONSTRUCT_ONLY |
       
   418                                     G_PARAM_READWRITE |
       
   419                                     G_PARAM_STATIC_NAME |
       
   420                                     G_PARAM_STATIC_BLURB);
       
   421   g_object_class_install_property (object_class, PROP_OBJECT_PATH, param_spec);
       
   422 
       
   423   param_spec = g_param_spec_enum ("mode", "Signalling mode",
       
   424                                   "Which signalling mode used to control the "
       
   425                                   "stream.",
       
   426                                   gabble_media_session_mode_get_type(),
       
   427                                   MODE_JINGLE,
       
   428                                   G_PARAM_CONSTRUCT_ONLY |
       
   429                                   G_PARAM_READWRITE |
       
   430                                   G_PARAM_STATIC_NAME |
       
   431                                   G_PARAM_STATIC_BLURB);
       
   432   g_object_class_install_property (object_class, PROP_MODE, param_spec);
       
   433 
       
   434   param_spec = g_param_spec_string ("name", "Stream name",
       
   435                                     "An opaque name for the stream used in the "
       
   436                                     "signalling.",
       
   437                                     NULL,
       
   438                                     G_PARAM_CONSTRUCT_ONLY |
       
   439                                     G_PARAM_READWRITE |
       
   440                                     G_PARAM_STATIC_NAME |
       
   441                                     G_PARAM_STATIC_BLURB);
       
   442   g_object_class_install_property (object_class, PROP_NAME, param_spec);
       
   443 
       
   444   param_spec = g_param_spec_uint ("id", "Stream ID",
       
   445                                   "A stream number for the stream used in the "
       
   446                                   "D-Bus API.",
       
   447                                   0, G_MAXUINT, 0,
       
   448                                   G_PARAM_CONSTRUCT_ONLY |
       
   449                                   G_PARAM_READWRITE |
       
   450                                   G_PARAM_STATIC_NAME |
       
   451                                   G_PARAM_STATIC_BLURB);
       
   452   g_object_class_install_property (object_class, PROP_ID, param_spec);
       
   453 
       
   454   param_spec = g_param_spec_uint ("initiator", "Stream initiator",
       
   455                                   "An enum signifying which end initiated "
       
   456                                   "the stream.",
       
   457                                   INITIATOR_LOCAL,
       
   458                                   INITIATOR_REMOTE,
       
   459                                   INITIATOR_LOCAL,
       
   460                                   G_PARAM_CONSTRUCT_ONLY |
       
   461                                   G_PARAM_READWRITE |
       
   462                                   G_PARAM_STATIC_NAME |
       
   463                                   G_PARAM_STATIC_BLURB);
       
   464   g_object_class_install_property (object_class, PROP_INITIATOR, param_spec);
       
   465 
       
   466   param_spec = g_param_spec_uint ("media-type", "Stream media type",
       
   467                                   "A constant indicating which media type the "
       
   468                                   "stream carries.",
       
   469                                   TP_MEDIA_STREAM_TYPE_AUDIO,
       
   470                                   TP_MEDIA_STREAM_TYPE_VIDEO,
       
   471                                   TP_MEDIA_STREAM_TYPE_AUDIO,
       
   472                                   G_PARAM_CONSTRUCT_ONLY |
       
   473                                   G_PARAM_READWRITE |
       
   474                                   G_PARAM_STATIC_NAME |
       
   475                                   G_PARAM_STATIC_BLURB);
       
   476   g_object_class_install_property (object_class, PROP_MEDIA_TYPE, param_spec);
       
   477 
       
   478   param_spec = g_param_spec_uint ("connection-state", "Stream connection state",
       
   479                                   "An integer indicating the state of the"
       
   480                                   "stream's connection.",
       
   481                                   TP_MEDIA_STREAM_STATE_DISCONNECTED,
       
   482                                   TP_MEDIA_STREAM_STATE_CONNECTED,
       
   483                                   TP_MEDIA_STREAM_STATE_DISCONNECTED,
       
   484                                   G_PARAM_CONSTRUCT |
       
   485                                   G_PARAM_READWRITE |
       
   486                                   G_PARAM_STATIC_NAME |
       
   487                                   G_PARAM_STATIC_BLURB);
       
   488   g_object_class_install_property (object_class, PROP_CONNECTION_STATE, param_spec);
       
   489 
       
   490   param_spec = g_param_spec_boolean ("ready", "Ready?",
       
   491                                      "A boolean signifying whether the user "
       
   492                                      "is ready to handle signals from this "
       
   493                                      "object.",
       
   494                                      FALSE,
       
   495                                      G_PARAM_CONSTRUCT |
       
   496                                      G_PARAM_READWRITE |
       
   497                                      G_PARAM_STATIC_NAME |
       
   498                                      G_PARAM_STATIC_BLURB);
       
   499   g_object_class_install_property (object_class, PROP_READY, param_spec);
       
   500 
       
   501   param_spec = g_param_spec_boolean ("got-local-codecs", "Got local codecs?",
       
   502                                      "A boolean signifying whether we've got "
       
   503                                      "the locally supported codecs from the user.",
       
   504                                      FALSE,
       
   505                                      G_PARAM_CONSTRUCT |
       
   506                                      G_PARAM_READWRITE |
       
   507                                      G_PARAM_STATIC_NAME |
       
   508                                      G_PARAM_STATIC_BLURB);
       
   509   g_object_class_install_property (object_class, PROP_GOT_LOCAL_CODECS, param_spec);
       
   510 
       
   511   param_spec = g_param_spec_uint ("signalling-state", "Signalling state",
       
   512                                   "Whether the stream is newly created, "
       
   513                                   "sent to the peer, or acknowledged.",
       
   514                                   STREAM_SIG_STATE_NEW,
       
   515                                   STREAM_SIG_STATE_REMOVING,
       
   516                                   STREAM_SIG_STATE_NEW,
       
   517                                   G_PARAM_CONSTRUCT |
       
   518                                   G_PARAM_READWRITE |
       
   519                                   G_PARAM_STATIC_NAME |
       
   520                                   G_PARAM_STATIC_BLURB);
       
   521   g_object_class_install_property (object_class, PROP_SIGNALLING_STATE, param_spec);
       
   522 
       
   523   param_spec = g_param_spec_boolean ("playing", "Set playing",
       
   524                                      "A boolean signifying whether the stream "
       
   525                                      "has been set playing yet.",
       
   526                                      FALSE,
       
   527                                      G_PARAM_CONSTRUCT |
       
   528                                      G_PARAM_READWRITE |
       
   529                                      G_PARAM_STATIC_NAME |
       
   530                                      G_PARAM_STATIC_BLURB);
       
   531   g_object_class_install_property (object_class, PROP_PLAYING, param_spec);
       
   532 
       
   533   param_spec = g_param_spec_uint ("combined-direction",
       
   534       "Combined direction",
       
   535       "An integer indicating the directions the stream currently sends in, "
       
   536       "and the peers who have been asked to send.",
       
   537       TP_MEDIA_STREAM_DIRECTION_NONE,
       
   538       MAKE_COMBINED_DIRECTION (TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL,
       
   539         TP_MEDIA_STREAM_PENDING_LOCAL_SEND |
       
   540         TP_MEDIA_STREAM_PENDING_REMOTE_SEND),
       
   541       TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL,
       
   542       G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_NAME |
       
   543       G_PARAM_STATIC_BLURB);
       
   544   g_object_class_install_property (object_class, PROP_COMBINED_DIRECTION,
       
   545       param_spec);
       
   546 
       
   547   /* signals exported by D-Bus interface */
       
   548   signals[DESTROY] =
       
   549     g_signal_new ("destroy",
       
   550                   G_OBJECT_CLASS_TYPE (gabble_media_stream_class),
       
   551                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   552                   0,
       
   553                   NULL, NULL,
       
   554                   g_cclosure_marshal_VOID__VOID,
       
   555                   G_TYPE_NONE, 0);
       
   556 
       
   557   signals[ADD_REMOTE_CANDIDATE] =
       
   558     g_signal_new ("add-remote-candidate",
       
   559                   G_OBJECT_CLASS_TYPE (gabble_media_stream_class),
       
   560                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   561                   0,
       
   562                   NULL, NULL,
       
   563                   gabble_media_stream_marshal_VOID__STRING_BOXED,
       
   564                   G_TYPE_NONE, 2, G_TYPE_STRING, TP_TYPE_TRANSPORT_LIST);
       
   565 
       
   566   signals[CLOSE] =
       
   567     g_signal_new ("close",
       
   568                   G_OBJECT_CLASS_TYPE (gabble_media_stream_class),
       
   569                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   570                   0,
       
   571                   NULL, NULL,
       
   572                   g_cclosure_marshal_VOID__VOID,
       
   573                   G_TYPE_NONE, 0);
       
   574 
       
   575   signals[REMOVE_REMOTE_CANDIDATE] =
       
   576     g_signal_new ("remove-remote-candidate",
       
   577                   G_OBJECT_CLASS_TYPE (gabble_media_stream_class),
       
   578                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   579                   0,
       
   580                   NULL, NULL,
       
   581                   g_cclosure_marshal_VOID__STRING,
       
   582                   G_TYPE_NONE, 1, G_TYPE_STRING);
       
   583 
       
   584   signals[SET_ACTIVE_CANDIDATE_PAIR] =
       
   585     g_signal_new ("set-active-candidate-pair",
       
   586                   G_OBJECT_CLASS_TYPE (gabble_media_stream_class),
       
   587                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   588                   0,
       
   589                   NULL, NULL,
       
   590                   gabble_media_stream_marshal_VOID__STRING_STRING,
       
   591                   G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
       
   592 
       
   593   signals[SET_REMOTE_CANDIDATE_LIST] =
       
   594     g_signal_new ("set-remote-candidate-list",
       
   595                   G_OBJECT_CLASS_TYPE (gabble_media_stream_class),
       
   596                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   597                   0,
       
   598                   NULL, NULL,
       
   599                   g_cclosure_marshal_VOID__BOXED,
       
   600                   G_TYPE_NONE, 1, TP_TYPE_CANDIDATE_LIST);
       
   601 
       
   602   signals[SET_REMOTE_CODECS] =
       
   603     g_signal_new ("set-remote-codecs",
       
   604                   G_OBJECT_CLASS_TYPE (gabble_media_stream_class),
       
   605                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   606                   0,
       
   607                   NULL, NULL,
       
   608                   g_cclosure_marshal_VOID__BOXED,
       
   609                   G_TYPE_NONE, 1, TP_TYPE_CODEC_LIST);
       
   610 
       
   611   /* signals not exported by D-Bus interface */
       
   612   signals[NEW_ACTIVE_CANDIDATE_PAIR] =
       
   613     g_signal_new ("new-active-candidate-pair",
       
   614                   G_OBJECT_CLASS_TYPE (gabble_media_stream_class),
       
   615                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   616                   0,
       
   617                   NULL, NULL,
       
   618                   gabble_media_stream_marshal_VOID__STRING_STRING,
       
   619                   G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
       
   620 
       
   621   signals[NEW_NATIVE_CANDIDATE] =
       
   622     g_signal_new ("new-native-candidate",
       
   623                   G_OBJECT_CLASS_TYPE (gabble_media_stream_class),
       
   624                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   625                   0,
       
   626                   NULL, NULL,
       
   627                   gabble_media_stream_marshal_VOID__STRING_BOXED,
       
   628                   G_TYPE_NONE, 2, G_TYPE_STRING, TP_TYPE_TRANSPORT_LIST);
       
   629 
       
   630   signals[SUPPORTED_CODECS] =
       
   631     g_signal_new ("supported-codecs",
       
   632                   G_OBJECT_CLASS_TYPE (gabble_media_stream_class),
       
   633                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   634                   0,
       
   635                   NULL, NULL,
       
   636                   g_cclosure_marshal_VOID__BOXED,
       
   637                   G_TYPE_NONE, 1, TP_TYPE_CODEC_LIST);
       
   638 
       
   639   signals[SET_STREAM_PLAYING] =
       
   640     g_signal_new ("set-stream-playing",
       
   641                   G_OBJECT_CLASS_TYPE (gabble_media_stream_class),
       
   642                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   643                   0,
       
   644                   NULL, NULL,
       
   645                   g_cclosure_marshal_VOID__BOOLEAN,
       
   646                   G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
       
   647 
       
   648   signals[SET_STREAM_SENDING] =
       
   649     g_signal_new ("set-stream-sending",
       
   650                   G_OBJECT_CLASS_TYPE (gabble_media_stream_class),
       
   651                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   652                   0,
       
   653                   NULL, NULL,
       
   654                   g_cclosure_marshal_VOID__BOOLEAN,
       
   655                   G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
       
   656 
       
   657   signals[ERROR] =
       
   658     g_signal_new ("error",
       
   659                   G_OBJECT_CLASS_TYPE (gabble_media_stream_class),
       
   660                   G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
       
   661                   0,
       
   662                   NULL, NULL,
       
   663                   gabble_media_stream_marshal_VOID__UINT_STRING,
       
   664                   G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
       
   665 
       
   666   dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (gabble_media_stream_class), &dbus_glib_gabble_media_stream_object_info);
       
   667 }
       
   668 
       
   669 void
       
   670 gabble_media_stream_dispose (GObject *object)
       
   671 {
       
   672   GabbleMediaStream *self = GABBLE_MEDIA_STREAM (object);
       
   673   GabbleMediaStreamPrivate *priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self);
       
   674 
       
   675   if (priv->dispose_has_run)
       
   676     return;
       
   677 
       
   678   _gabble_media_stream_close (self);
       
   679 
       
   680   g_signal_emit (self, signals[DESTROY], 0);
       
   681 
       
   682   priv->dispose_has_run = TRUE;
       
   683 
       
   684   if (G_OBJECT_CLASS (gabble_media_stream_parent_class)->dispose)
       
   685     G_OBJECT_CLASS (gabble_media_stream_parent_class)->dispose (object);
       
   686 }
       
   687 
       
   688 void
       
   689 gabble_media_stream_finalize (GObject *object)
       
   690 {
       
   691   GabbleMediaStream *self = GABBLE_MEDIA_STREAM (object);
       
   692   GabbleMediaStreamPrivate *priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self);
       
   693 
       
   694   g_free (priv->object_path);
       
   695 
       
   696   g_value_unset (&priv->native_codecs);
       
   697   g_value_unset (&priv->native_candidates);
       
   698 
       
   699   g_value_unset (&priv->remote_codecs);
       
   700   g_value_unset (&priv->remote_candidates);
       
   701 
       
   702   G_OBJECT_CLASS (gabble_media_stream_parent_class)->finalize (object);
       
   703 }
       
   704 
       
   705 /**
       
   706  * gabble_media_stream_codec_choice
       
   707  *
       
   708  * Implements D-Bus method CodecChoice
       
   709  * on interface org.freedesktop.Telepathy.Media.StreamHandler
       
   710  *
       
   711  * @error: Used to return a pointer to a GError detailing any error
       
   712  *         that occurred, D-Bus will throw the error only if this
       
   713  *         function returns FALSE.
       
   714  *
       
   715  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   716  */
       
   717 gboolean
       
   718 gabble_media_stream_codec_choice (GabbleMediaStream *self,
       
   719                                   guint codec_id,
       
   720                                   GError **error)
       
   721 {
       
   722   GabbleMediaStreamPrivate *priv;
       
   723 
       
   724   g_assert (GABBLE_IS_MEDIA_STREAM (self));
       
   725 
       
   726   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self);
       
   727 
       
   728   return TRUE;
       
   729 }
       
   730 
       
   731 
       
   732 /**
       
   733  * gabble_media_stream_error
       
   734  *
       
   735  * Implements D-Bus method Error
       
   736  * on interface org.freedesktop.Telepathy.Media.StreamHandler
       
   737  *
       
   738  * @error: Used to return a pointer to a GError detailing any error
       
   739  *         that occurred, D-Bus will throw the error only if this
       
   740  *         function returns FALSE.
       
   741  *
       
   742  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   743  */
       
   744 gboolean
       
   745 gabble_media_stream_error (GabbleMediaStream *self,
       
   746                            guint errno,
       
   747                            const gchar *message,
       
   748                            GError **error)
       
   749 {
       
   750   GabbleMediaStreamPrivate *priv;
       
   751 
       
   752   g_assert (GABBLE_IS_MEDIA_STREAM (self));
       
   753 
       
   754   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self);
       
   755 
       
   756  _gabble_media_session_debug (priv->session, DEBUG_MSG_WARNING, "Media.StreamHandler::Error called, error %u (%s) -- emitting signal", errno, message);
       
   757 
       
   758   g_signal_emit (self, signals[ERROR], 0, errno, message);
       
   759 
       
   760   return TRUE;
       
   761 }
       
   762 
       
   763 
       
   764 /**
       
   765  * gabble_media_stream_native_candidates_prepared
       
   766  *
       
   767  * Implements D-Bus method NativeCandidatesPrepared
       
   768  * on interface org.freedesktop.Telepathy.Media.StreamHandler
       
   769  *
       
   770  * @error: Used to return a pointer to a GError detailing any error
       
   771  *         that occurred, D-Bus will throw the error only if this
       
   772  *         function returns FALSE.
       
   773  *
       
   774  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   775  */
       
   776 gboolean
       
   777 gabble_media_stream_native_candidates_prepared (GabbleMediaStream *self,
       
   778                                                 GError **error)
       
   779 {
       
   780   GabbleMediaStreamPrivate *priv;
       
   781 
       
   782   g_assert (GABBLE_IS_MEDIA_STREAM (self));
       
   783 
       
   784   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self);
       
   785 
       
   786   return TRUE;
       
   787 }
       
   788 
       
   789 
       
   790 /**
       
   791  * gabble_media_stream_new_active_candidate_pair
       
   792  *
       
   793  * Implements D-Bus method NewActiveCandidatePair
       
   794  * on interface org.freedesktop.Telepathy.Media.StreamHandler
       
   795  *
       
   796  * @error: Used to return a pointer to a GError detailing any error
       
   797  *         that occurred, D-Bus will throw the error only if this
       
   798  *         function returns FALSE.
       
   799  *
       
   800  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   801  */
       
   802 gboolean
       
   803 gabble_media_stream_new_active_candidate_pair (GabbleMediaStream *self,
       
   804                                                const gchar *native_candidate_id,
       
   805                                                const gchar *remote_candidate_id,
       
   806                                                GError **error)
       
   807 {
       
   808   GabbleMediaStreamPrivate *priv;
       
   809 
       
   810   g_assert (GABBLE_IS_MEDIA_STREAM (self));
       
   811 
       
   812   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self);
       
   813 
       
   814   g_signal_emit (self, signals[NEW_ACTIVE_CANDIDATE_PAIR], 0,
       
   815                  native_candidate_id, remote_candidate_id);
       
   816 
       
   817   return TRUE;
       
   818 }
       
   819 
       
   820 
       
   821 /**
       
   822  * gabble_media_stream_new_native_candidate
       
   823  *
       
   824  * Implements D-Bus method NewNativeCandidate
       
   825  * on interface org.freedesktop.Telepathy.Media.StreamHandler
       
   826  *
       
   827  * @error: Used to return a pointer to a GError detailing any error
       
   828  *         that occurred, D-Bus will throw the error only if this
       
   829  *         function returns FALSE.
       
   830  *
       
   831  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   832  */
       
   833 gboolean
       
   834 gabble_media_stream_new_native_candidate (GabbleMediaStream *self,
       
   835                                           const gchar *candidate_id,
       
   836                                           const GPtrArray *transports,
       
   837                                           GError **error)
       
   838 {
       
   839   GabbleMediaStreamPrivate *priv;
       
   840   JingleSessionState state;
       
   841   GPtrArray *candidates;
       
   842   GValue candidate = { 0, };
       
   843   GValueArray *transport;
       
   844   const gchar *addr;
       
   845 
       
   846   g_assert (GABBLE_IS_MEDIA_STREAM (self));
       
   847 
       
   848   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self);
       
   849 
       
   850   g_object_get (priv->session, "state", &state, NULL);
       
   851 
       
   852   /* FIXME: maybe this should be an assertion in case the channel
       
   853    * isn't closed early enough right now? */
       
   854   if (state > JS_STATE_ACTIVE)
       
   855     {
       
   856       gabble_debug (DEBUG_FLAG, "state > JS_STATE_ACTIVE, doing nothing");
       
   857       return TRUE;
       
   858     }
       
   859 
       
   860   candidates = g_value_get_boxed (&priv->native_candidates);
       
   861 
       
   862   g_value_init (&candidate, TP_TYPE_CANDIDATE_STRUCT);
       
   863   g_value_take_boxed (&candidate,
       
   864       dbus_g_type_specialized_construct (TP_TYPE_CANDIDATE_STRUCT));
       
   865 
       
   866   dbus_g_type_struct_set (&candidate,
       
   867       0, candidate_id,
       
   868       1, transports,
       
   869       G_MAXUINT);
       
   870 
       
   871   transport = g_ptr_array_index (transports, 0);
       
   872   addr = g_value_get_string (g_value_array_get_nth (transport, 1));
       
   873   if (!strcmp (addr, "127.0.0.1"))
       
   874     {
       
   875      _gabble_media_session_debug (priv->session, DEBUG_MSG_WARNING, "%s: ignoring native localhost candidate",
       
   876                          G_STRFUNC);
       
   877       return TRUE;
       
   878     }
       
   879 
       
   880   g_ptr_array_add (candidates, g_value_get_boxed (&candidate));
       
   881 
       
   882   _gabble_media_session_debug (priv->session, DEBUG_MSG_INFO, DEBUG_MSG_INFO, "put 1 native candidate from stream-engine into cache");
       
   883 
       
   884   push_native_candidates (self);
       
   885 
       
   886   g_signal_emit (self, signals[NEW_NATIVE_CANDIDATE], 0,
       
   887                  candidate_id, transports);
       
   888 
       
   889   return TRUE;
       
   890 }
       
   891 
       
   892 
       
   893 /**
       
   894  * gabble_media_stream_ready
       
   895  *
       
   896  * Implements D-Bus method Ready
       
   897  * on interface org.freedesktop.Telepathy.Media.StreamHandler
       
   898  *
       
   899  * @error: Used to return a pointer to a GError detailing any error
       
   900  *         that occurred, D-Bus will throw the error only if this
       
   901  *         function returns FALSE.
       
   902  *
       
   903  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   904  */
       
   905 gboolean
       
   906 gabble_media_stream_ready (GabbleMediaStream *self,
       
   907                            const GPtrArray *codecs,
       
   908                            GError **error)
       
   909 {
       
   910   GabbleMediaStreamPrivate *priv;
       
   911 
       
   912   g_assert (GABBLE_IS_MEDIA_STREAM (self));
       
   913 
       
   914   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self);
       
   915 
       
   916   _gabble_media_session_debug (priv->session, DEBUG_MSG_INFO, DEBUG_MSG_INFO, "ready called");
       
   917 
       
   918   g_object_set (self, "ready", TRUE, NULL);
       
   919 
       
   920   push_remote_codecs (self);
       
   921   push_remote_candidates (self);
       
   922   push_playing (self);
       
   923   push_sending (self);
       
   924 
       
   925   return gabble_media_stream_set_local_codecs (self, codecs, error);
       
   926 }
       
   927 
       
   928 
       
   929 /**
       
   930  * gabble_media_stream_set_local_codecs
       
   931  *
       
   932  * Implements D-Bus method SetLocalCodecs
       
   933  * on interface org.freedesktop.Telepathy.Media.StreamHandler
       
   934  *
       
   935  * @error: Used to return a pointer to a GError detailing any error
       
   936  *         that occurred, D-Bus will throw the error only if this
       
   937  *         function returns FALSE.
       
   938  *
       
   939  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   940  */
       
   941 gboolean
       
   942 gabble_media_stream_set_local_codecs (GabbleMediaStream *self,
       
   943                                       const GPtrArray *codecs,
       
   944                                       GError **error)
       
   945 {
       
   946   GabbleMediaStreamPrivate *priv;
       
   947 
       
   948   g_assert (GABBLE_IS_MEDIA_STREAM (self));
       
   949 
       
   950   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self);
       
   951 
       
   952   _gabble_media_session_debug (priv->session, DEBUG_MSG_INFO, DEBUG_MSG_INFO, "putting list of all %d locally supported "
       
   953                   "codecs from stream-engine into cache", codecs->len);
       
   954 
       
   955   g_value_set_boxed (&priv->native_codecs, codecs);
       
   956 
       
   957   g_object_set (self, "got-local-codecs", TRUE, NULL);
       
   958 
       
   959   return TRUE;
       
   960 }
       
   961 
       
   962 
       
   963 /**
       
   964  * gabble_media_stream_stream_state
       
   965  *
       
   966  * Implements D-Bus method StreamState
       
   967  * on interface org.freedesktop.Telepathy.Media.StreamHandler
       
   968  *
       
   969  * @error: Used to return a pointer to a GError detailing any error
       
   970  *         that occurred, D-Bus will throw the error only if this
       
   971  *         function returns FALSE.
       
   972  *
       
   973  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   974  */
       
   975 gboolean
       
   976 gabble_media_stream_stream_state (GabbleMediaStream *self,
       
   977                                   guint connection_state,
       
   978                                   GError **error)
       
   979 {
       
   980   g_assert (GABBLE_IS_MEDIA_STREAM (self));
       
   981 
       
   982   g_object_set (self, "connection-state", connection_state, NULL);
       
   983 
       
   984   return TRUE;
       
   985 }
       
   986 
       
   987 
       
   988 /**
       
   989  * gabble_media_stream_supported_codecs
       
   990  *
       
   991  * Implements D-Bus method SupportedCodecs
       
   992  * on interface org.freedesktop.Telepathy.Media.StreamHandler
       
   993  *
       
   994  * @error: Used to return a pointer to a GError detailing any error
       
   995  *         that occurred, D-Bus will throw the error only if this
       
   996  *         function returns FALSE.
       
   997  *
       
   998  * Returns: TRUE if successful, FALSE if an error was thrown.
       
   999  */
       
  1000 gboolean
       
  1001 gabble_media_stream_supported_codecs (GabbleMediaStream *self,
       
  1002                                       const GPtrArray *codecs,
       
  1003                                       GError **error)
       
  1004 {
       
  1005   GabbleMediaStreamPrivate *priv;
       
  1006 
       
  1007   g_assert (GABBLE_IS_MEDIA_STREAM (self));
       
  1008 
       
  1009   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self);
       
  1010 
       
  1011   _gabble_media_session_debug (priv->session, DEBUG_MSG_INFO, DEBUG_MSG_INFO, "got codec intersection containing %d "
       
  1012                   "codecs from stream-engine", codecs->len);
       
  1013 
       
  1014   /* store the intersection for later on */
       
  1015   g_value_set_boxed (&priv->native_codecs, codecs);
       
  1016 
       
  1017   g_signal_emit (self, signals[SUPPORTED_CODECS], 0, codecs);
       
  1018 
       
  1019   return TRUE;
       
  1020 }
       
  1021 
       
  1022 static LmHandlerResult
       
  1023 candidates_msg_reply_cb (GabbleConnection *conn,
       
  1024                          LmMessage *sent_msg,
       
  1025                          LmMessage *reply_msg,
       
  1026                          GObject *object,
       
  1027                          gpointer user_data)
       
  1028 {
       
  1029   GabbleMediaStream *stream = GABBLE_MEDIA_STREAM (object);
       
  1030   GabbleMediaStreamPrivate *priv;
       
  1031 
       
  1032   g_assert (GABBLE_IS_MEDIA_STREAM (stream));
       
  1033 
       
  1034   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream);
       
  1035 
       
  1036   MSG_REPLY_CB_END_SESSION_IF_NOT_SUCCESSFUL (priv->session, "candidates failed");
       
  1037 
       
  1038   return LM_HANDLER_RESULT_REMOVE_MESSAGE;
       
  1039 }
       
  1040 
       
  1041 static void
       
  1042 _add_rtp_candidate_node (GabbleMediaSession *session, LmMessageNode *parent,
       
  1043                          GValueArray *candidate)
       
  1044 {
       
  1045   gchar *addr;
       
  1046   gchar *user;
       
  1047   gchar *pass;
       
  1048   gchar *port_str;
       
  1049   gchar *pref_str;
       
  1050   gchar *xml;
       
  1051   const gchar *type_str;
       
  1052   const gchar *candidate_id;
       
  1053   guint port;
       
  1054   gdouble pref;
       
  1055   TpMediaStreamProto proto;
       
  1056   TpMediaStreamTransportType type;
       
  1057   const GPtrArray *transports;
       
  1058   GValue transport = { 0, };
       
  1059   LmMessageNode *cand_node;
       
  1060 
       
  1061   candidate_id = g_value_get_string (g_value_array_get_nth (candidate, 0));
       
  1062   transports = g_value_get_boxed (g_value_array_get_nth (candidate, 1));
       
  1063 
       
  1064   /* jingle audio only supports the concept of one transport per candidate */
       
  1065   g_assert (transports->len == 1);
       
  1066 
       
  1067   g_value_init (&transport, TP_TYPE_TRANSPORT_STRUCT);
       
  1068   g_value_set_static_boxed (&transport, g_ptr_array_index (transports, 0));
       
  1069 
       
  1070   dbus_g_type_struct_get (&transport,
       
  1071       1, &addr,
       
  1072       2, &port,
       
  1073       3, &proto,
       
  1074       6, &pref,
       
  1075       7, &type,
       
  1076       8, &user,
       
  1077       9, &pass,
       
  1078       G_MAXUINT);
       
  1079 
       
  1080   port_str = g_strdup_printf ("%d", port);
       
  1081   pref_str = g_strdup_printf ("%f", pref);
       
  1082 
       
  1083   switch (type) {
       
  1084     case TP_MEDIA_STREAM_TRANSPORT_TYPE_LOCAL:
       
  1085       type_str = "local";
       
  1086       break;
       
  1087     case TP_MEDIA_STREAM_TRANSPORT_TYPE_DERIVED:
       
  1088       type_str = "stun";
       
  1089       break;
       
  1090     case TP_MEDIA_STREAM_TRANSPORT_TYPE_RELAY:
       
  1091       type_str = "relay";
       
  1092       break;
       
  1093     default:
       
  1094       g_error ("%s: TpMediaStreamTransportType has an invalid value",
       
  1095         G_STRFUNC);
       
  1096       return;
       
  1097   }
       
  1098 
       
  1099   cand_node = lm_message_node_add_child (parent, "candidate", NULL);
       
  1100   lm_message_node_set_attributes (cand_node,
       
  1101       "name", "rtp",
       
  1102       "address", addr,
       
  1103       "port", port_str,
       
  1104       "username", user,
       
  1105       "password", pass,
       
  1106       "preference", pref_str,
       
  1107       "protocol", (proto == TP_MEDIA_STREAM_PROTO_UDP) ? "udp" : "tcp",
       
  1108       "type", type_str,
       
  1109       "network", "0",
       
  1110       "generation", "0",
       
  1111       NULL);
       
  1112 
       
  1113   xml = lm_message_node_to_string (cand_node);
       
  1114   _gabble_media_session_debug (session, DEBUG_MSG_DUMP,
       
  1115     "  from Telepathy D-Bus struct: [%s\"%s\", %s[%s1, \"%s\", %d, %s, "
       
  1116     "\"%s\", \"%s\", %f, %s, \"%s\", \"%s\"%s]]",
       
  1117     ANSI_BOLD_OFF, candidate_id, ANSI_BOLD_ON, ANSI_BOLD_OFF, addr, port,
       
  1118     tp_protocols[proto], "RTP", "AVP", pref, tp_transports[type], user, pass,
       
  1119     ANSI_BOLD_ON);
       
  1120   _gabble_media_session_debug (session, DEBUG_MSG_DUMP,
       
  1121     "  to Jingle XML: [%s%s%s]", ANSI_BOLD_OFF, xml, ANSI_BOLD_ON);
       
  1122   g_free (xml);
       
  1123 
       
  1124   g_free (addr);
       
  1125   g_free (user);
       
  1126   g_free (pass);
       
  1127   g_free (port_str);
       
  1128   g_free (pref_str);
       
  1129 }
       
  1130 
       
  1131 static LmMessage *
       
  1132 _gabble_media_stream_message_new (GabbleMediaStream *stream,
       
  1133                                   const gchar *action,
       
  1134                                   LmMessageNode **content_node)
       
  1135 {
       
  1136   GabbleMediaStreamPrivate *priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream);
       
  1137   LmMessage *msg;
       
  1138   LmMessageNode *session_node = NULL;
       
  1139 
       
  1140   /* construct a session message */
       
  1141   msg = _gabble_media_session_message_new (priv->session, action,
       
  1142       &session_node);
       
  1143 
       
  1144   /* add our content node to it if necessary */
       
  1145   *content_node = _gabble_media_stream_add_content_node (stream, session_node);
       
  1146 
       
  1147   return msg;
       
  1148 }
       
  1149 
       
  1150 
       
  1151 static void
       
  1152 push_candidate (GabbleMediaStream *stream, GValueArray *candidate)
       
  1153 {
       
  1154   GabbleMediaStreamPrivate *priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream);
       
  1155   LmMessage *msg;
       
  1156   LmMessageNode *content_node, *transport_node;
       
  1157   const gchar *action;
       
  1158 
       
  1159   if (priv->mode == MODE_GOOGLE)
       
  1160     action = "candidates";
       
  1161   else
       
  1162     action = "transport-info";
       
  1163 
       
  1164   /* construct a base message */
       
  1165   msg = _gabble_media_stream_message_new (stream, action, &content_node);
       
  1166 
       
  1167   /* for jingle, add a transport */
       
  1168   transport_node = _gabble_media_stream_content_node_add_transport (stream,
       
  1169       content_node);
       
  1170 
       
  1171   /* add transport info to it */
       
  1172   _add_rtp_candidate_node (priv->session, transport_node, candidate);
       
  1173 
       
  1174   _gabble_media_session_debug (priv->session, DEBUG_MSG_INFO, DEBUG_MSG_INFO, "sending jingle session action \"%s\" to "
       
  1175       "peer", action);
       
  1176 
       
  1177   /* send it */
       
  1178   _gabble_connection_send_with_reply (priv->conn, msg, candidates_msg_reply_cb,
       
  1179       G_OBJECT (stream), NULL, NULL);
       
  1180 
       
  1181   /* clean up */
       
  1182   lm_message_unref (msg);
       
  1183 }
       
  1184 
       
  1185 static void
       
  1186 push_native_candidates (GabbleMediaStream *stream)
       
  1187 {
       
  1188   GabbleMediaStreamPrivate *priv;
       
  1189   GPtrArray *candidates;
       
  1190   guint i;
       
  1191 
       
  1192   g_assert (GABBLE_IS_MEDIA_STREAM (stream));
       
  1193 
       
  1194   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream);
       
  1195 
       
  1196   if (stream->signalling_state == STREAM_SIG_STATE_NEW ||
       
  1197       stream->signalling_state == STREAM_SIG_STATE_REMOVING)
       
  1198     return;
       
  1199 
       
  1200   candidates = g_value_get_boxed (&priv->native_candidates);
       
  1201 
       
  1202   for (i = 0; i < candidates->len; i++)
       
  1203     push_candidate (stream, g_ptr_array_index (candidates, i));
       
  1204 
       
  1205   g_value_take_boxed (&priv->native_candidates,
       
  1206     dbus_g_type_specialized_construct (TP_TYPE_CANDIDATE_LIST));
       
  1207 }
       
  1208 
       
  1209 void
       
  1210 _gabble_media_stream_close (GabbleMediaStream *stream)
       
  1211 {
       
  1212   GabbleMediaStreamPrivate *priv;
       
  1213 
       
  1214   g_assert (GABBLE_IS_MEDIA_STREAM (stream));
       
  1215 
       
  1216   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream);
       
  1217 
       
  1218   if (!priv->closed)
       
  1219     {
       
  1220       priv->closed = TRUE;
       
  1221       g_signal_emit (stream, signals[CLOSE], 0);
       
  1222     }
       
  1223 }
       
  1224 
       
  1225 gboolean
       
  1226 _gabble_media_stream_post_remote_codecs (GabbleMediaStream *stream,
       
  1227                                          LmMessage *message,
       
  1228                                          LmMessageNode *desc_node,
       
  1229                                          GError **error)
       
  1230 {
       
  1231   GabbleMediaStreamPrivate *priv;
       
  1232   LmMessageNode *node;
       
  1233   GPtrArray *codecs;
       
  1234 
       
  1235   g_assert (GABBLE_IS_MEDIA_STREAM (stream));
       
  1236 
       
  1237   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream);
       
  1238 
       
  1239   codecs = g_value_get_boxed (&priv->remote_codecs);
       
  1240 
       
  1241   g_assert (codecs->len == 0);
       
  1242 
       
  1243   for (node = desc_node->children; node; node = node->next)
       
  1244     {
       
  1245       guchar id;
       
  1246       const gchar *name, *str;
       
  1247       guint clockrate, channels;
       
  1248       GHashTable *params;
       
  1249       GValue codec = { 0, };
       
  1250 
       
  1251       if (g_strdiff (node->name, "payload-type"))
       
  1252         continue;
       
  1253 
       
  1254       /* id of codec */
       
  1255       str = lm_message_node_get_attribute (node, "id");
       
  1256       if (str == NULL)
       
  1257         {
       
  1258           g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST,
       
  1259               "description has no ID");
       
  1260           return FALSE;
       
  1261         }
       
  1262 
       
  1263       id = atoi(str);
       
  1264 
       
  1265       /* codec name */
       
  1266       name = lm_message_node_get_attribute (node, "name");
       
  1267       if (name == NULL)
       
  1268         {
       
  1269           name = "";
       
  1270         }
       
  1271 
       
  1272       /* clock rate: jingle and newer GTalk */
       
  1273       str = lm_message_node_get_attribute (node, "clockrate"); /* google */
       
  1274       if (str == NULL)
       
  1275         str = lm_message_node_get_attribute (node, "rate"); /* jingle */
       
  1276 
       
  1277       if (str != NULL)
       
  1278         {
       
  1279           clockrate = atoi (str);
       
  1280         }
       
  1281       else
       
  1282         {
       
  1283           clockrate = 0;
       
  1284         }
       
  1285 
       
  1286       /* number of channels: jingle only */
       
  1287       str = lm_message_node_get_attribute (node, "channels");
       
  1288       if (str != NULL)
       
  1289         {
       
  1290           channels = atoi (str);
       
  1291         }
       
  1292       else
       
  1293         {
       
  1294           channels = 1;
       
  1295         }
       
  1296 
       
  1297       params = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_free);
       
  1298 
       
  1299       /* bitrate: newer GTalk only */
       
  1300       str = lm_message_node_get_attribute (node, "bitrate");
       
  1301       if (str != NULL)
       
  1302         {
       
  1303           g_hash_table_insert (params, "bitrate", g_strdup (str));
       
  1304         }
       
  1305 
       
  1306       g_value_init (&codec, TP_TYPE_CODEC_STRUCT);
       
  1307       g_value_take_boxed (&codec,
       
  1308           dbus_g_type_specialized_construct (TP_TYPE_CODEC_STRUCT));
       
  1309 
       
  1310       dbus_g_type_struct_set (&codec,
       
  1311           0, id,
       
  1312           1, name,
       
  1313           2, TP_CODEC_MEDIA_TYPE_AUDIO,
       
  1314           3, clockrate,
       
  1315           4, channels,
       
  1316           5, params,
       
  1317           G_MAXUINT);
       
  1318 
       
  1319       g_ptr_array_add (codecs, g_value_get_boxed (&codec));
       
  1320     }
       
  1321 
       
  1322   _gabble_media_session_debug (priv->session, DEBUG_MSG_INFO, DEBUG_MSG_INFO, "put %d remote codecs from peer into cache",
       
  1323                   codecs->len);
       
  1324 
       
  1325   push_remote_codecs (stream);
       
  1326 
       
  1327   return TRUE;
       
  1328 }
       
  1329 
       
  1330 static void
       
  1331 push_remote_codecs (GabbleMediaStream *stream)
       
  1332 {
       
  1333   GabbleMediaStreamPrivate *priv;
       
  1334   GPtrArray *codecs;
       
  1335 
       
  1336   g_assert (GABBLE_IS_MEDIA_STREAM (stream));
       
  1337 
       
  1338   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream);
       
  1339 
       
  1340   if (!priv->ready)
       
  1341     return;
       
  1342 
       
  1343   codecs = g_value_get_boxed (&priv->remote_codecs);
       
  1344   if (codecs->len == 0)
       
  1345     return;
       
  1346 
       
  1347   _gabble_media_session_debug (priv->session, DEBUG_MSG_EVENT, "passing %d remote codecs to stream-engine",
       
  1348                    codecs->len);
       
  1349 
       
  1350   g_signal_emit (stream, signals[SET_REMOTE_CODECS], 0, codecs);
       
  1351 
       
  1352   g_value_take_boxed (&priv->remote_codecs,
       
  1353       dbus_g_type_specialized_construct (TP_TYPE_CODEC_LIST));
       
  1354 }
       
  1355 
       
  1356 gboolean
       
  1357 _gabble_media_stream_post_remote_candidates (GabbleMediaStream *stream,
       
  1358                                              LmMessage *message,
       
  1359                                              LmMessageNode *transport_node,
       
  1360                                              GError **error)
       
  1361 {
       
  1362   GabbleMediaStreamPrivate *priv;
       
  1363   LmMessageNode *node;
       
  1364   const gchar *str;
       
  1365   GPtrArray *candidates;
       
  1366 
       
  1367   g_assert (GABBLE_IS_MEDIA_STREAM (stream));
       
  1368 
       
  1369   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream);
       
  1370 
       
  1371   candidates = g_value_get_boxed (&priv->remote_candidates);
       
  1372 
       
  1373   for (node = transport_node->children; node; node = node->next)
       
  1374     {
       
  1375       gchar *candidate_id;
       
  1376       const gchar *name, *addr;
       
  1377       guint16 port;
       
  1378       TpMediaStreamProto proto;
       
  1379       gdouble pref;
       
  1380       TpMediaStreamTransportType type;
       
  1381       const gchar *user, *pass;
       
  1382       guchar net, gen;
       
  1383       GValue candidate = { 0, };
       
  1384       GPtrArray *transports;
       
  1385       GValue transport = { 0, };
       
  1386       gchar *xml;
       
  1387 
       
  1388       if (g_strdiff (node->name, "candidate"))
       
  1389         continue;
       
  1390 
       
  1391       /*
       
  1392        * Candidate
       
  1393        */
       
  1394 
       
  1395       /* stream name */
       
  1396       name = lm_message_node_get_attribute (node, "name");
       
  1397       if (name == NULL || strcmp (name, "rtp") != 0)
       
  1398         goto FAILURE;
       
  1399 
       
  1400 
       
  1401       /*
       
  1402        * Transport
       
  1403        */
       
  1404 
       
  1405       /* ip address */
       
  1406       addr = lm_message_node_get_attribute (node, "address");
       
  1407       if (addr == NULL)
       
  1408         goto FAILURE;
       
  1409 
       
  1410       /* port */
       
  1411       str = lm_message_node_get_attribute (node, "port");
       
  1412       if (str == NULL)
       
  1413         goto FAILURE;
       
  1414       port = atoi (str);
       
  1415 
       
  1416       /* protocol */
       
  1417       str = lm_message_node_get_attribute (node, "protocol");
       
  1418       if (str == NULL)
       
  1419         goto FAILURE;
       
  1420 
       
  1421       if (strcmp (str, "udp") == 0)
       
  1422         {
       
  1423           proto = TP_MEDIA_STREAM_PROTO_UDP;
       
  1424         }
       
  1425       else if (strcmp (str, "tcp") == 0)
       
  1426         {
       
  1427           proto = TP_MEDIA_STREAM_PROTO_TCP;
       
  1428         }
       
  1429       else if (strcmp (str, "ssltcp") == 0)
       
  1430         {
       
  1431          _gabble_media_session_debug (priv->session, DEBUG_MSG_WARNING, "%s: ssltcp candidates "
       
  1432                              "not yet supported", G_STRFUNC);
       
  1433           continue;
       
  1434         }
       
  1435       else
       
  1436         goto FAILURE;
       
  1437 
       
  1438       /* protocol profile: hardcoded to "AVP" for now */
       
  1439 
       
  1440       /* preference */
       
  1441       str = lm_message_node_get_attribute (node, "preference");
       
  1442       if (str == NULL)
       
  1443         goto FAILURE;
       
  1444       pref = g_ascii_strtod (str, NULL);
       
  1445 
       
  1446       /* type */
       
  1447       str = lm_message_node_get_attribute (node, "type");
       
  1448       if (str == NULL)
       
  1449         goto FAILURE;
       
  1450 
       
  1451       if (strcmp (str, "local") == 0)
       
  1452         {
       
  1453           type = TP_MEDIA_STREAM_TRANSPORT_TYPE_LOCAL;
       
  1454         }
       
  1455       else if (strcmp (str, "stun") == 0)
       
  1456         {
       
  1457           type = TP_MEDIA_STREAM_TRANSPORT_TYPE_DERIVED;
       
  1458         }
       
  1459       else if (strcmp (str, "relay") == 0)
       
  1460         {
       
  1461           type = TP_MEDIA_STREAM_TRANSPORT_TYPE_RELAY;
       
  1462         }
       
  1463       else
       
  1464         goto FAILURE;
       
  1465 
       
  1466       /* username */
       
  1467       user = lm_message_node_get_attribute (node, "username");
       
  1468       if (user == NULL)
       
  1469         goto FAILURE;
       
  1470 
       
  1471       /* password */
       
  1472       pass = lm_message_node_get_attribute (node, "password");
       
  1473       if (pass == NULL)
       
  1474         goto FAILURE;
       
  1475 
       
  1476       /* unknown */
       
  1477       str = lm_message_node_get_attribute (node, "network");
       
  1478       if (str == NULL)
       
  1479         goto FAILURE;
       
  1480       net = atoi (str);
       
  1481 
       
  1482       /* unknown */
       
  1483       str = lm_message_node_get_attribute (node, "generation");
       
  1484       if (str == NULL)
       
  1485         goto FAILURE;
       
  1486       gen = atoi (str);
       
  1487 
       
  1488 
       
  1489       g_value_init (&transport, TP_TYPE_TRANSPORT_STRUCT);
       
  1490       g_value_take_boxed (&transport,
       
  1491           dbus_g_type_specialized_construct (TP_TYPE_TRANSPORT_STRUCT));
       
  1492 
       
  1493       dbus_g_type_struct_set (&transport,
       
  1494           0, 1,         /* component number */
       
  1495           1, addr,
       
  1496           2, port,
       
  1497           3, proto,
       
  1498           4, "RTP",
       
  1499           5, "AVP",
       
  1500           6, pref,
       
  1501           7, type,
       
  1502           8, user,
       
  1503           9, pass,
       
  1504           G_MAXUINT);
       
  1505 
       
  1506       transports = g_ptr_array_sized_new (1);
       
  1507       g_ptr_array_add (transports, g_value_get_boxed (&transport));
       
  1508 
       
  1509 
       
  1510       g_value_init (&candidate, TP_TYPE_CANDIDATE_STRUCT);
       
  1511       g_value_take_boxed (&candidate,
       
  1512           dbus_g_type_specialized_construct (TP_TYPE_CANDIDATE_STRUCT));
       
  1513 
       
  1514       /* FIXME: is this naming scheme sensible? */
       
  1515       candidate_id = g_strdup_printf ("R%d", ++priv->remote_candidate_count);
       
  1516 
       
  1517       dbus_g_type_struct_set (&candidate,
       
  1518           0, candidate_id,
       
  1519           1, transports,
       
  1520           G_MAXUINT);
       
  1521 
       
  1522       g_ptr_array_add (candidates, g_value_get_boxed (&candidate));
       
  1523 
       
  1524       xml = lm_message_node_to_string (node);
       
  1525       _gabble_media_session_debug (priv->session, DEBUG_MSG_INFO, DEBUG_MSG_INFO, "put 1 remote candidate from peer into cache");
       
  1526       _gabble_media_session_debug (priv->session, DEBUG_MSG_DUMP, "  from Jingle XML: [%s%s%s]",
       
  1527                       ANSI_BOLD_OFF, xml, ANSI_BOLD_ON);
       
  1528       _gabble_media_session_debug (priv->session, DEBUG_MSG_DUMP, "  to Telepathy D-Bus struct: [%s\"%s\", %s[%s1, \"%s\", %d, %s, \"%s\", \"%s\", %f, %s, \"%s\", \"%s\"%s]]",
       
  1529                       ANSI_BOLD_OFF, candidate_id, ANSI_BOLD_ON,
       
  1530                       ANSI_BOLD_OFF, addr, port, tp_protocols[proto], "RTP", "AVP", pref, tp_transports[type], user, pass, ANSI_BOLD_ON);
       
  1531       g_free (xml);
       
  1532 
       
  1533       g_free (candidate_id);
       
  1534     }
       
  1535 
       
  1536 /*SUCCESS:*/
       
  1537   push_remote_candidates (stream);
       
  1538 
       
  1539   return TRUE;
       
  1540 
       
  1541 FAILURE:
       
  1542   g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST,
       
  1543       "unable to parse candidate");
       
  1544 
       
  1545   return FALSE;
       
  1546 }
       
  1547 
       
  1548 static void
       
  1549 push_remote_candidates (GabbleMediaStream *stream)
       
  1550 {
       
  1551   GabbleMediaStreamPrivate *priv;
       
  1552   GPtrArray *candidates;
       
  1553   guint i;
       
  1554 
       
  1555   g_assert (GABBLE_IS_MEDIA_STREAM (stream));
       
  1556 
       
  1557   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream);
       
  1558 
       
  1559   candidates = g_value_get_boxed (&priv->remote_candidates);
       
  1560 
       
  1561   if (candidates->len == 0)
       
  1562     return;
       
  1563 
       
  1564   if (!priv->ready)
       
  1565     return;
       
  1566 
       
  1567   for (i = 0; i < candidates->len; i++)
       
  1568     {
       
  1569       GValueArray *candidate = g_ptr_array_index (candidates, i);
       
  1570       const gchar *candidate_id;
       
  1571       const GPtrArray *transports;
       
  1572 
       
  1573       candidate_id = g_value_get_string (g_value_array_get_nth (candidate, 0));
       
  1574       transports = g_value_get_boxed (g_value_array_get_nth (candidate, 1));
       
  1575 
       
  1576       _gabble_media_session_debug (priv->session, DEBUG_MSG_EVENT, "passing 1 remote candidate "
       
  1577                        "to stream-engine");
       
  1578 
       
  1579       g_signal_emit (stream, signals[ADD_REMOTE_CANDIDATE], 0,
       
  1580                      candidate_id, transports);
       
  1581     }
       
  1582 
       
  1583   g_value_take_boxed (&priv->remote_candidates,
       
  1584       dbus_g_type_specialized_construct (TP_TYPE_CANDIDATE_LIST));
       
  1585 }
       
  1586 
       
  1587 static void
       
  1588 push_playing (GabbleMediaStream *stream)
       
  1589 {
       
  1590   GabbleMediaStreamPrivate *priv;
       
  1591 
       
  1592   g_assert (GABBLE_IS_MEDIA_STREAM (stream));
       
  1593 
       
  1594   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream);
       
  1595 
       
  1596   if (!priv->ready)
       
  1597     return;
       
  1598 
       
  1599   _gabble_media_session_debug (priv->session, DEBUG_MSG_INFO, DEBUG_MSG_INFO, "stream %s emitting SetStreamPlaying(%s)",
       
  1600       stream->name, stream->playing ? "true" : "false");
       
  1601 
       
  1602   g_signal_emit (stream, signals[SET_STREAM_PLAYING], 0, stream->playing);
       
  1603 }
       
  1604 
       
  1605 static void
       
  1606 push_sending (GabbleMediaStream *stream)
       
  1607 {
       
  1608   GabbleMediaStreamPrivate *priv;
       
  1609 
       
  1610   g_assert (GABBLE_IS_MEDIA_STREAM (stream));
       
  1611 
       
  1612   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream);
       
  1613 
       
  1614   if (!priv->ready)
       
  1615     return;
       
  1616 
       
  1617   _gabble_media_session_debug (priv->session, DEBUG_MSG_INFO, DEBUG_MSG_INFO, "stream %s emitting SetStreamSending(%s)",
       
  1618       stream->name, priv->sending ? "true" : "false");
       
  1619 
       
  1620   g_signal_emit (stream, signals[SET_STREAM_SENDING], 0, priv->sending);
       
  1621 }
       
  1622 
       
  1623 /*
       
  1624  * oh sweet g_hash_table_foreach how beautiful thou be'st
       
  1625  *
       
  1626  *    _\ / ^/
       
  1627  *  \/ \// 7_   __
       
  1628  *  ( 7 ) (__) (__)
       
  1629  *  ^\\ |/__/___/
       
  1630  *   \\/_/     | <-- TP-cable kindly provided by Mika N.
       
  1631  *    \ /      O
       
  1632  *     ||     /|\
       
  1633  *     ||     / \
       
  1634  *     ||
       
  1635  * ____||_____________
       
  1636  */
       
  1637 
       
  1638 typedef struct {
       
  1639     GabbleMediaStreamPrivate *priv;
       
  1640     LmMessageNode *pt_node;
       
  1641 } CodecParamsFromTpContext;
       
  1642 
       
  1643 //#ifndef EMULATOR
       
  1644 static const gchar *video_codec_params[] = {
       
  1645   "x", "y", "width", "height", "layer", "transparent",
       
  1646 };
       
  1647 //#endif
       
  1648 
       
  1649 static void
       
  1650 codec_params_from_tp_foreach (gpointer key, gpointer value, gpointer user_data)
       
  1651 {
       
  1652   CodecParamsFromTpContext *ctx = user_data;
       
  1653   GabbleMediaStreamPrivate *priv = ctx->priv;
       
  1654   const gchar *pname = key, *pvalue = value;
       
  1655 
       
  1656   if (priv->media_type == TP_CODEC_MEDIA_TYPE_AUDIO)
       
  1657     {
       
  1658       if (priv->mode == MODE_GOOGLE && strcmp (pname, "bitrate") == 0)
       
  1659         {
       
  1660           lm_message_node_set_attribute (ctx->pt_node, pname, pvalue);
       
  1661           return;
       
  1662         }
       
  1663     }
       
  1664   else if (priv->mode == MODE_JINGLE)
       
  1665     {
       
  1666       gint i;
       
  1667 
       
  1668       for (i = 0; video_codec_params[i] != NULL; i++)
       
  1669         {
       
  1670           if (strcmp (pname, video_codec_params[i]) == 0)
       
  1671             {
       
  1672               lm_message_node_set_attribute (ctx->pt_node, pname, pvalue);
       
  1673               return;
       
  1674             }
       
  1675         }
       
  1676     }
       
  1677 
       
  1678   gabble_debug (DEBUG_FLAG, "ignoring %s=%s for %s %s stream", pname, pvalue,
       
  1679       (priv->mode == MODE_JINGLE) ? "jingle" : "google",
       
  1680       (priv->media_type == TP_CODEC_MEDIA_TYPE_AUDIO) ? "audio" : "video");
       
  1681 }
       
  1682 
       
  1683 LmMessageNode *
       
  1684 _gabble_media_stream_add_content_node (GabbleMediaStream *stream,
       
  1685                                        LmMessageNode *session_node)
       
  1686 {
       
  1687   GabbleMediaStreamPrivate *priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream);
       
  1688   LmMessageNode *node = session_node;
       
  1689 
       
  1690   /* add our content node to it if in jingle mode */
       
  1691   if (priv->mode == MODE_JINGLE)
       
  1692     {
       
  1693       node = lm_message_node_add_child (session_node, "content", NULL);
       
  1694       lm_message_node_set_attribute (node, "name", stream->name);
       
  1695 
       
  1696       if (priv->session->initiator == stream->initiator)
       
  1697         lm_message_node_set_attribute (node, "creator", "initiator");
       
  1698       else
       
  1699         lm_message_node_set_attribute (node, "creator", "responder");
       
  1700     }
       
  1701 
       
  1702   return node;
       
  1703 }
       
  1704 
       
  1705 void
       
  1706 _gabble_media_stream_content_node_add_description (GabbleMediaStream *stream,
       
  1707                                                    LmMessageNode *content_node)
       
  1708 {
       
  1709   GabbleMediaStreamPrivate *priv;
       
  1710   const GPtrArray *codecs;
       
  1711   LmMessageNode *desc_node;
       
  1712   guint i;
       
  1713   const gchar *xmlns;
       
  1714 
       
  1715   g_assert (GABBLE_IS_MEDIA_STREAM (stream));
       
  1716 
       
  1717   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream);
       
  1718 
       
  1719   codecs = g_value_get_boxed (&priv->native_codecs);
       
  1720 
       
  1721   desc_node = lm_message_node_add_child (content_node, "description", NULL);
       
  1722 
       
  1723   if (priv->mode == MODE_GOOGLE)
       
  1724     xmlns = NS_GOOGLE_SESSION_PHONE;
       
  1725   else if (priv->media_type == TP_CODEC_MEDIA_TYPE_VIDEO)
       
  1726     xmlns = NS_JINGLE_DESCRIPTION_VIDEO;
       
  1727   else
       
  1728     xmlns = NS_JINGLE_DESCRIPTION_AUDIO;
       
  1729 
       
  1730   lm_message_node_set_attribute (desc_node, "xmlns", xmlns);
       
  1731 
       
  1732   for (i = 0; i < codecs->len; i++)
       
  1733     {
       
  1734       GValue codec = { 0, };
       
  1735       guint id, clock_rate, channels;
       
  1736       gchar *name, buf[16];
       
  1737       GHashTable *params;
       
  1738       LmMessageNode *pt_node;
       
  1739       CodecParamsFromTpContext ctx;
       
  1740 
       
  1741       g_value_init (&codec, TP_TYPE_CODEC_STRUCT);
       
  1742       g_value_set_static_boxed (&codec, g_ptr_array_index (codecs, i));
       
  1743 
       
  1744       dbus_g_type_struct_get (&codec,
       
  1745           0, &id,
       
  1746           1, &name,
       
  1747           3, &clock_rate,
       
  1748           4, &channels,
       
  1749           5, &params,
       
  1750           G_MAXUINT);
       
  1751 
       
  1752       /* create a sub-node called "payload-type" and fill it */
       
  1753       pt_node = lm_message_node_add_child (desc_node, "payload-type", NULL);
       
  1754 
       
  1755       /* id: required */
       
  1756       sprintf (buf, "%u", id);
       
  1757       lm_message_node_set_attribute (pt_node, "id", buf);
       
  1758 
       
  1759       /* name: optional */
       
  1760       if (*name != '\0')
       
  1761         {
       
  1762           lm_message_node_set_attribute (pt_node, "name", name);
       
  1763         }
       
  1764 
       
  1765       /* clock rate: optional */
       
  1766       if (clock_rate != 0)
       
  1767         {
       
  1768           sprintf (buf, "%u", clock_rate);
       
  1769           lm_message_node_set_attribute (pt_node,
       
  1770               (priv->mode == MODE_GOOGLE) ? "clockrate" : "rate", buf);
       
  1771         }
       
  1772 
       
  1773       /* number of channels: optional, jingle only */
       
  1774       /* FIXME: is it? */
       
  1775       if (channels != 0 && priv->mode == MODE_JINGLE)
       
  1776         {
       
  1777           sprintf (buf, "%u", channels);
       
  1778           lm_message_node_set_attribute (pt_node, "channels", buf);
       
  1779         }
       
  1780 
       
  1781       /* parse the optional params */
       
  1782       ctx.priv = priv;
       
  1783       ctx.pt_node = pt_node;
       
  1784       g_hash_table_foreach (params, codec_params_from_tp_foreach, &ctx);
       
  1785 
       
  1786       /* clean up */
       
  1787       g_free (name);
       
  1788       g_hash_table_destroy (params);
       
  1789     }
       
  1790 }
       
  1791 
       
  1792 LmMessageNode *
       
  1793 _gabble_media_stream_content_node_add_transport (GabbleMediaStream *stream,
       
  1794                                                  LmMessageNode *content_node)
       
  1795 {
       
  1796   GabbleMediaStreamPrivate *priv;
       
  1797   LmMessageNode *node;
       
  1798 
       
  1799   g_assert (GABBLE_IS_MEDIA_STREAM (stream));
       
  1800 
       
  1801   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream);
       
  1802 
       
  1803   if (priv->mode != MODE_JINGLE)
       
  1804     return content_node;
       
  1805 
       
  1806   node = lm_message_node_add_child (content_node, "transport", NULL);
       
  1807 
       
  1808   lm_message_node_set_attribute (node, "xmlns", NS_GOOGLE_TRANSPORT_P2P);
       
  1809 
       
  1810   return node;
       
  1811 }
       
  1812 
       
  1813 void
       
  1814 _gabble_media_stream_update_sending (GabbleMediaStream *stream,
       
  1815                                      gboolean start_sending)
       
  1816 {
       
  1817   GabbleMediaStreamPrivate *priv;
       
  1818   gboolean new_sending;
       
  1819 
       
  1820   g_assert (GABBLE_IS_MEDIA_STREAM (stream));
       
  1821 
       
  1822   priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (stream);
       
  1823 
       
  1824   new_sending =
       
  1825     ((stream->combined_direction & TP_MEDIA_STREAM_DIRECTION_SEND) != 0);
       
  1826 
       
  1827   if (priv->sending == new_sending)
       
  1828     return;
       
  1829 
       
  1830   if (new_sending && !start_sending)
       
  1831     return;
       
  1832 
       
  1833   priv->sending = new_sending;
       
  1834   push_sending (stream);
       
  1835 }
       
  1836