[Telepathy-commits] [telepathy-gabble/master] Simplified readiness-checking logic for contents, added support for content-add/accept, thus making the incoming/outgoing twisted tests work.

Senko Rasic senko at phyrexia.lan
Tue Dec 2 04:33:57 PST 2008


---
 src/jingle-content.c                       |  140 ++++++++++++++++++++++------
 src/jingle-content.h                       |    5 +-
 src/jingle-media-rtp.c                     |    6 +-
 src/jingle-session.c                       |   69 ++++++++++----
 src/jingle-transport-google.c              |   15 ++-
 src/media-channel.c                        |   10 ++-
 src/media-stream.c                         |    2 +
 tests/twisted/jingle/test-incoming-call.py |    2 +-
 8 files changed, 188 insertions(+), 61 deletions(-)

diff --git a/src/jingle-content.c b/src/jingle-content.c
index 9648e8d..7444f5c 100644
--- a/src/jingle-content.c
+++ b/src/jingle-content.c
@@ -66,6 +66,7 @@ struct _GabbleJingleContentPrivate
   gchar *name;
   gchar *creator;
   gboolean created_by_initiator;
+  gboolean created_by_us;
   JingleContentState state;
   JingleContentSenders senders;
   gboolean ready;
@@ -75,6 +76,9 @@ struct _GabbleJingleContentPrivate
 
   GabbleJingleTransportIface *transport;
 
+  gboolean media_ready;
+  gboolean transport_ready;
+
   gboolean dispose_has_run;
 };
 
@@ -105,6 +109,9 @@ gabble_jingle_content_init (GabbleJingleContent *obj)
 
   priv->state = JINGLE_CONTENT_STATE_EMPTY;
   priv->created_by_initiator = TRUE;
+  priv->created_by_us = TRUE;
+  priv->media_ready = FALSE;
+  priv->transport_ready = FALSE;
   priv->dispose_has_run = FALSE;
 
   obj->conn = NULL;
@@ -216,7 +223,6 @@ gabble_jingle_content_set_property (GObject *object,
 
           g_signal_connect (priv->transport, "new-candidates",
               (GCallback) new_transport_candidates_cb, self);
-
       }
       break;
     case PROP_NAME:
@@ -320,15 +326,15 @@ gabble_jingle_content_class_init (GabbleJingleContentClass *cls)
                                   G_PARAM_READWRITE |
                                   G_PARAM_STATIC_NAME |
                                   G_PARAM_STATIC_BLURB);
-  g_object_class_install_property (object_class, PROP_STATE, param_spec);
+  g_object_class_install_property (object_class, PROP_SENDERS, param_spec);
 
   param_spec = g_param_spec_uint ("state", "Content state",
                                   "The current state that the content is in.",
-                                  0, G_MAXUINT32, JINGLE_CONTENT_STATE_NEW,
+                                  0, G_MAXUINT32, JINGLE_CONTENT_STATE_EMPTY,
                                   G_PARAM_READWRITE |
                                   G_PARAM_STATIC_NAME |
                                   G_PARAM_STATIC_BLURB);
-  g_object_class_install_property (object_class, PROP_SENDERS, param_spec);
+  g_object_class_install_property (object_class, PROP_STATE, param_spec);
 
   param_spec = g_param_spec_boolean ("ready", "Ready?",
                                      "A boolean signifying whether media for "
@@ -341,14 +347,13 @@ gabble_jingle_content_class_init (GabbleJingleContentClass *cls)
 
   /* signal definitions */
 
-  signals[READY] =
-    g_signal_new ("ready",
-                  G_OBJECT_CLASS_TYPE (cls),
-                  G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
-                  0,
-                  NULL, NULL,
-                  g_cclosure_marshal_VOID__VOID,
-                  G_TYPE_NONE, 0);
+  signals[READY] = g_signal_new ("ready",
+    G_OBJECT_CLASS_TYPE (cls),
+    G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+    0,
+    NULL, NULL,
+    g_cclosure_marshal_VOID__VOID,
+    G_TYPE_NONE, 0);
 
   signals[NEW_CANDIDATES] = g_signal_new (
     "new-candidates",
@@ -463,6 +468,7 @@ gabble_jingle_content_parse_add (GabbleJingleContent *c,
       priv->transport_ns = g_strdup (ns);
     }
 
+  priv->created_by_us = FALSE;
   priv->created_by_initiator = (!tp_strdiff (creator, "initiator"));
   priv->senders = _string_to_enum (content_senders_table, senders);
   if (priv->senders == JINGLE_CONTENT_SENDERS_NONE)
@@ -640,28 +646,79 @@ gabble_jingle_content_add_candidates (GabbleJingleContent *self, GList *li)
   gabble_jingle_transport_iface_add_candidates (priv->transport, li);
 }
 
+/* Returns whether the content is ready to be signalled (initiated, for local
+ * streams, or acknowledged, for remote streams. */
 gboolean
-gabble_jingle_content_is_ready (GabbleJingleContent *self, gboolean for_acceptance)
+gabble_jingle_content_is_ready (GabbleJingleContent *self)
 {
   GabbleJingleContentPrivate *priv = GABBLE_JINGLE_CONTENT_GET_PRIVATE (self);
-  JingleTransportState state;
 
-  /* Content is ready for initiatiation the moment the media using it is ready
-   * (ie. has local codecs). But in order to accept it, it must also be
-   * connected. */
+  /* If it's created by us, media ready and not signalled,
+   * it's ready to be added. */
+  if (priv->created_by_us && priv->media_ready &&
+      (priv->state == JINGLE_CONTENT_STATE_EMPTY))
+          return TRUE;
+
+  /* If it's created by peer, media and transports ready,
+   * and not acknowledged yet, it's ready for acceptance. */
+  if (!priv->created_by_us && priv->media_ready && priv->transport_ready &&
+      (priv->state == JINGLE_CONTENT_STATE_NEW))
+          return TRUE;
+
+  return FALSE;
+}
+
+static void
+try_content_add_or_accept (GabbleJingleContent *self)
+{
+  GabbleJingleContentPrivate *priv = GABBLE_JINGLE_CONTENT_GET_PRIVATE (self);
+  LmMessage *msg;
+  LmMessageNode *sess_node;
+  JingleAction action = JINGLE_ACTION_UNKNOWN;
+  JingleContentState new_state = JINGLE_CONTENT_STATE_EMPTY;
+
+  /* FIXME: we should only do this if the content-disposition != "session"
+   * so we ignore it for now */
+  DEBUG ("called, but doing nothing for now");
+  return;
+
+  if (!gabble_jingle_content_is_ready (self))
+      return;
+
+  if (priv->created_by_us)
+    {
+      /* TODO: set a timer for acknowledgement */
+      action = JINGLE_ACTION_CONTENT_ADD;
+      new_state = JINGLE_CONTENT_STATE_SENT;
+    }
+  else
+    {
+      action = JINGLE_ACTION_CONTENT_ACCEPT;
+      new_state = JINGLE_CONTENT_STATE_ACKNOWLEDGED;
+    }
+
+  msg = gabble_jingle_session_new_message (self->session,
+      action, &sess_node);
+  gabble_jingle_content_produce_node (self, sess_node, TRUE);
+  _gabble_connection_send (self->conn, msg, NULL);
+  lm_message_unref (msg);
 
-  /* if local codecs are not set, we're definitely not ready */
-  if (!priv->ready)
-      return FALSE;
+  priv->state = new_state;
+  g_object_notify (G_OBJECT (self), "state");
+}
 
-  /* we are okay for signalling content-add/session-initiate */
-  if (!for_acceptance)
-      return TRUE;
+/* Called by a subclass when the media is ready (e.g. we got local codecs) */
+void
+_gabble_jingle_content_set_media_ready (GabbleJingleContent *self)
+{
+  GabbleJingleContentPrivate *priv = GABBLE_JINGLE_CONTENT_GET_PRIVATE (self);
+
+  priv->media_ready = TRUE;
+  try_content_add_or_accept (self);
 
-  g_object_get (priv->transport, "state", &state, NULL);
+  /* FIXME we abuse this to signal to session that we might be ready */
+  g_object_notify (G_OBJECT (self), "ready");
 
-  /* we are even okay for accepting content/session, hooray */
-  return (state == JINGLE_TRANSPORT_STATE_CONNECTED);
 }
 
 void
@@ -672,8 +729,33 @@ gabble_jingle_content_set_transport_state (GabbleJingleContent *self,
 
   g_object_set (priv->transport, "state", state, NULL);
 
-  /* FIXME: refactor this to use _is_ready, if neccessary 
-  if ((state == JINGLE_TRANSPORT_STATE_CONNECTED) && (priv->ready))
-      g_object_notify (G_OBJECT (self), "ready"); */
+  if (state == JINGLE_TRANSPORT_STATE_CONNECTED)
+    {
+      priv->transport_ready = TRUE;
+      try_content_add_or_accept (self);
+    }
+
+  /* FIXME we abuse this to signal to session that we might be ready */
+  g_object_notify (G_OBJECT (self), "ready");
+
 }
 
+void
+gabble_jingle_content_accept (GabbleJingleContent *c)
+{
+  GabbleJingleContentPrivate *priv = GABBLE_JINGLE_CONTENT_GET_PRIVATE (c);
+  LmMessage *msg;
+  LmMessageNode *sess_node;
+
+  g_assert (!priv->created_by_us);
+  g_assert (gabble_jingle_content_is_ready (c));
+
+  msg = gabble_jingle_session_new_message (c->session,
+      JINGLE_ACTION_CONTENT_ACCEPT, &sess_node);
+
+  gabble_jingle_content_produce_node (c, sess_node, TRUE);
+  _gabble_connection_send (c->conn, msg, NULL);
+  lm_message_unref (msg);
+
+  g_object_set (c, "state", JINGLE_CONTENT_STATE_ACKNOWLEDGED, NULL);
+}
diff --git a/src/jingle-content.h b/src/jingle-content.h
index d65bade..999f309 100644
--- a/src/jingle-content.h
+++ b/src/jingle-content.h
@@ -104,10 +104,11 @@ void gabble_jingle_content_parse_accept (GabbleJingleContent *c,
 void gabble_jingle_content_parse_transport_info (GabbleJingleContent *self,
   LmMessageNode *trans_node, GError **error);
 void gabble_jingle_content_add_candidates (GabbleJingleContent *self, GList *li);
-gboolean gabble_jingle_content_is_ready (GabbleJingleContent *self, gboolean for_acceptance);
-void gabble_jingle_content_set_local_codecs (GabbleJingleContent *content, GList *li);
+void _gabble_jingle_content_set_media_ready (GabbleJingleContent *self);
+gboolean gabble_jingle_content_is_ready (GabbleJingleContent *self);
 void gabble_jingle_content_set_transport_state (GabbleJingleContent *content,
     JingleTransportState state);
+void gabble_jingle_content_accept (GabbleJingleContent *c);
 
 #endif /* __JINGLE_CONTENT_H__ */
 
diff --git a/src/jingle-media-rtp.c b/src/jingle-media-rtp.c
index 1986ea6..d35243c 100644
--- a/src/jingle-media-rtp.c
+++ b/src/jingle-media-rtp.c
@@ -256,6 +256,8 @@ parse_description (GabbleJingleContent *content,
       g_assert_not_reached ();
     }
 
+  DEBUG ("detected media type %u", mtype);
+
   /* FIXME: we ignore "profile" attribute */
 
   for (node = desc_node->children; node; node = node->next)
@@ -435,11 +437,11 @@ jingle_media_rtp_set_local_codecs (GabbleJingleMediaRtp *self, GList *codecs)
   GabbleJingleMediaRtpPrivate *priv =
     GABBLE_JINGLE_MEDIA_RTP_GET_PRIVATE (self);
 
-  DEBUG ("adding new local codecs, yippie");
+  DEBUG ("adding new local codecs");
 
   priv->local_codecs = g_list_concat (priv->local_codecs, codecs);
 
-  g_object_set (self, "ready", TRUE, NULL);
+  _gabble_jingle_content_set_media_ready (GABBLE_JINGLE_CONTENT (self));
 }
 
 void
diff --git a/src/jingle-session.c b/src/jingle-session.c
index 38ba9fe..aac77b5 100644
--- a/src/jingle-session.c
+++ b/src/jingle-session.c
@@ -69,7 +69,14 @@ struct _GabbleJingleSessionPrivate
   gchar *peer_jid;
   gchar *initiator;
   gboolean local_initiator;
+
+  /* GabbleJingleContent objects keyed by content name.
+   * Table owns references to these objects. */
   GHashTable *contents;
+
+  /* Table of initial content objects keyed by name.
+   * Any object in this table has to appear in contents
+   * also. This table just borrows the references. */
   GHashTable *initial_contents;
   JingleDialect dialect;
   JingleState state;
@@ -639,7 +646,7 @@ _mark_each_initial_content (gpointer key, gpointer data, gpointer user_data)
   GabbleJingleSession *sess = GABBLE_JINGLE_SESSION (user_data);
   GabbleJingleSessionPrivate *priv = GABBLE_JINGLE_SESSION_GET_PRIVATE (sess);
 
-  g_hash_table_insert (priv->initial_contents, key, GUINT_TO_POINTER (TRUE));
+  g_hash_table_insert (priv->initial_contents, key, data);
 }
 
 static void
@@ -669,8 +676,10 @@ on_session_initiate (GabbleJingleSession *sess, LmMessageNode *node,
 
   if (*error == NULL)
     {
+      DEBUG ("marking all newly defined contents as initial contents");
+
       /* mark all newly defined contents as initial contents */
-      g_hash_table_foreach (priv->initial_contents,
+      g_hash_table_foreach (priv->contents,
           _mark_each_initial_content, sess);
 
       set_state (sess, JS_STATE_PENDING_INITIATED);
@@ -1096,7 +1105,9 @@ _check_content_for_acceptance (gpointer key, gpointer data, gpointer user_data)
   GabbleJingleContent *c = GABBLE_JINGLE_CONTENT (data);
   gboolean *is_ready = (gboolean *) user_data;
 
-  *is_ready = gabble_jingle_content_is_ready (c, TRUE);
+  /* TODO: content knows whether we are the creator (different
+   * rules for readiness for initiate/accept) */
+  *is_ready = gabble_jingle_content_is_ready (c);
 }
 
 static void
@@ -1105,7 +1116,9 @@ _check_content_for_initiation (gpointer key, gpointer data, gpointer user_data)
   GabbleJingleContent *c = GABBLE_JINGLE_CONTENT (data);
   gboolean *is_ready = (gboolean *) user_data;
 
-  *is_ready = gabble_jingle_content_is_ready (c, FALSE);
+  *is_ready = gabble_jingle_content_is_ready (c);
+  DEBUG ("content %p reported readiness: %u", c, *is_ready);
+
 }
 
 static void
@@ -1113,15 +1126,10 @@ _try_session_accept_fill (gpointer key, gpointer data, gpointer user_data)
 {
   GabbleJingleContent *c = GABBLE_JINGLE_CONTENT (data);
   LmMessageNode *sess_node = user_data;
-  JingleContentState state;
-
-  g_object_get (c, "state", &state, NULL);
 
-  DEBUG ("considering whether to add content node, state = %u", state);
-
-  /* we only want to acknowledge newly added contents */
-  if (state == JINGLE_CONTENT_STATE_EMPTY)
-    gabble_jingle_content_produce_node (c, sess_node, TRUE);
+  /* We can safely add every content, because we're only
+   * considering the initial ones anyways. */
+  gabble_jingle_content_produce_node (c, sess_node, TRUE);
 }
 
 static void
@@ -1132,7 +1140,7 @@ try_session_accept (GabbleJingleSession *sess)
   LmMessageNode *sess_node;
   gboolean content_ready = TRUE;
 
-  g_assert (g_hash_table_size (priv->contents) > 0);
+  g_assert (g_hash_table_size (priv->initial_contents) > 0);
 
   if (priv->state != JS_STATE_PENDING_INITIATED)
     {
@@ -1194,6 +1202,20 @@ try_session_initiate (GabbleJingleSession *sess)
 }
 
 static void
+content_accept (GabbleJingleSession *sess, GabbleJingleContent *c)
+{
+  GabbleJingleSessionPrivate *priv = GABBLE_JINGLE_SESSION_GET_PRIVATE (sess);
+
+  if (priv->state < JS_STATE_PENDING_INITIATE_SENT)
+    {
+      DEBUG ("session is in state %u, won't try to accept content", priv->state);
+      return;
+    }
+
+  gabble_jingle_content_accept (c);
+}
+
+static void
 set_state (GabbleJingleSession *sess, JingleState state)
 {
   GabbleJingleSessionPrivate *priv = GABBLE_JINGLE_SESSION_GET_PRIVATE (sess);
@@ -1293,6 +1315,8 @@ gabble_jingle_session_add_content (GabbleJingleSession *sess, JingleMediaType mt
                     "senders", JINGLE_CONTENT_SENDERS_BOTH,
                     NULL);
 
+  DEBUG ("here");
+
   g_signal_connect (c, "notify::ready",
       (GCallback) content_ready_cb, sess);
 
@@ -1305,7 +1329,7 @@ gabble_jingle_session_add_content (GabbleJingleSession *sess, JingleMediaType mt
    * when we come to it. Here we assume all the contents added before
    * an initiation message has been sent are initial contents. */
   if (priv->state == JS_STATE_PENDING_CREATED)
-      g_hash_table_insert (priv->initial_contents, name, GUINT_TO_POINTER (TRUE));
+      g_hash_table_insert (priv->initial_contents, name, c);
 
   /* Try to signal content-add if needed. */
   if ((priv->state == JS_STATE_ACTIVE) ||
@@ -1321,6 +1345,7 @@ gabble_jingle_session_add_content (GabbleJingleSession *sess, JingleMediaType mt
       _gabble_connection_send (priv->conn, msg, NULL);
       lm_message_unref (msg);
 
+      DEBUG ("setting jingle cotnent state to sent");
       g_object_set (c, "state", JINGLE_CONTENT_STATE_SENT, NULL);
     }
 
@@ -1405,22 +1430,30 @@ content_ready_cb (GabbleJingleContent *c,
 
   if (g_hash_table_lookup (priv->initial_contents, name))
     {
+      DEBUG ("it's one of the initial contents");
       /* it's one of the contents from session-initiate */
       if (priv->local_initiator)
         {
+          DEBUG ("local initiator, i'm going to try to initiate");
           try_session_initiate (sess);
         }
       else
         {
+          DEBUG ("remote initiator, i'm going to try to accept");
           try_session_accept (sess);
         }
     }
   else
     {
-      /* FIXME: should do analogous thing; if we are the content
-       * initiator (hm, maybe we should really remember that),
-       * and content is ready for acceptance, then accept it
-       * ... */
+      /* FIXME: we're assuming the content wasn't
+       * created by us (we should invent content property
+       * that says this, instead of assuming anything) */
+      if (gabble_jingle_content_is_ready (c))
+        {
+          /* FIXME: content can figure out it needs to accept itself,
+           * it should be handled automatically */
+          content_accept (sess, c);
+        }
     }
 }
 
diff --git a/src/jingle-transport-google.c b/src/jingle-transport-google.c
index 0b90470..70b7a6d 100644
--- a/src/jingle-transport-google.c
+++ b/src/jingle-transport-google.c
@@ -537,16 +537,19 @@ add_candidates (GabbleJingleTransportIface *obj, GList *new_candidates)
     GABBLE_JINGLE_TRANSPORT_GOOGLE (obj);
   GabbleJingleTransportGooglePrivate *priv =
     GABBLE_JINGLE_TRANSPORT_GOOGLE_GET_PRIVATE (transport);
-  gboolean ready;
+  JingleContentState state;
 
-  g_object_get (priv->content, "ready", &ready, NULL);
+  g_object_get (priv->content, "state", &state, NULL);
 
-  if (ready) {
-      DEBUG ("content ready, transmitting new candidates");
+  if (state > JINGLE_CONTENT_STATE_EMPTY)
+    {
+      DEBUG ("content already signalled, transmitting candidates");
       transmit_candidates (transport, new_candidates);
       priv->pending_candidates = NULL;
-  } else {
-      DEBUG ("content not ready, not transmitting candidates");
+    }
+  else
+    {
+      DEBUG ("content not signalled yet, waiting with candidates");
 
       /* if we already have pending candidates, the new ones will
        * be in the local_candidates list after them. but these
diff --git a/src/media-channel.c b/src/media-channel.c
index c4e54cd..273ac66 100644
--- a/src/media-channel.c
+++ b/src/media-channel.c
@@ -1670,6 +1670,8 @@ session_terminated_cb (GabbleJingleSession *session,
     {
       GPtrArray *tmp = priv->streams;
 
+      DEBUG ("unreffing streams");
+
       /* move priv->streams aside so that the stream_close_cb
        * doesn't double unref */
       priv->streams = NULL;
@@ -2098,8 +2100,7 @@ create_stream_from_content (GabbleMediaChannel *chan, GabbleJingleContent *c)
                     session);
   */
 
-  /* keep track of the stream */
-  g_object_ref (stream);
+  /* we will own the only reference to this stream */
   g_ptr_array_add (priv->streams, stream);
 
   g_signal_connect (stream, "close",
@@ -2131,7 +2132,10 @@ create_stream_from_content (GabbleMediaChannel *chan, GabbleJingleContent *c)
       /* all of the streams are bidirectional from farsight's point of view, it's
        * just in the signalling they change */
       tp_svc_media_session_handler_emit_new_stream_handler (chan,
-        object_path, id, TP_MEDIA_STREAM_TYPE_AUDIO, TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL);
+        object_path, id,
+        type == JINGLE_MEDIA_TYPE_AUDIO ?
+          TP_MEDIA_STREAM_TYPE_AUDIO : TP_MEDIA_STREAM_TYPE_VIDEO,
+        TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL);
     }
 
   g_free (object_path);
diff --git a/src/media-stream.c b/src/media-stream.c
index 2f39458..4afb65c 100644
--- a/src/media-stream.c
+++ b/src/media-stream.c
@@ -603,6 +603,8 @@ gabble_media_stream_dispose (GObject *object)
   GabbleMediaStream *self = GABBLE_MEDIA_STREAM (object);
   GabbleMediaStreamPrivate *priv = GABBLE_MEDIA_STREAM_GET_PRIVATE (self);
 
+  DEBUG ("called");
+
   if (priv->dispose_has_run)
     return;
 
diff --git a/tests/twisted/jingle/test-incoming-call.py b/tests/twisted/jingle/test-incoming-call.py
index 393f58f..242a90d 100644
--- a/tests/twisted/jingle/test-incoming-call.py
+++ b/tests/twisted/jingle/test-incoming-call.py
@@ -126,7 +126,7 @@ def test(q, bus, conn, stream):
 
     # Tests completed, close the connection
 
-    e = q.expect('dbus-signal', signal='Close') #XXX - match against the path
+    e = q.expect('dbus-signal', signal='Closed') #XXX - match against the path
 
     conn.Disconnect()
     q.expect('dbus-signal', signal='StatusChanged', args=[2, 1])
-- 
1.5.6.5




More information about the Telepathy-commits mailing list