[telepathy-gabble/master] Set individual ringing/hold/mute flags.

Will Thompson will.thompson at collabora.co.uk
Thu May 21 03:38:23 PDT 2009


This embeds some knowledge of RTP into jingle-session.c, but actually
there was already some RTP-specific stuff in there, and we already know
that needs refactoring out.
---
 src/jingle-session.c         |  197 ++++++++++++++++++++++++++++++++++++------
 src/jingle-session.h         |    4 +
 src/media-channel-hold.c     |  109 ++++++-----------------
 src/media-channel-internal.h |    7 +-
 src/media-channel.c          |    2 +
 src/media-stream.c           |   14 ++--
 6 files changed, 214 insertions(+), 119 deletions(-)

diff --git a/src/jingle-session.c b/src/jingle-session.c
index e06f6ef..4130974 100644
--- a/src/jingle-session.c
+++ b/src/jingle-session.c
@@ -33,6 +33,10 @@
 #include "gabble-signals-marshal.h"
 #include "jingle-content.h"
 #include "jingle-factory.h"
+/* FIXME: the RTP-specific bits of this file should be separated from the
+ *        generic Jingle code.
+ */
+#include "jingle-media-rtp.h"
 #include "namespaces.h"
 #include "util.h"
 
@@ -42,6 +46,7 @@ G_DEFINE_TYPE(GabbleJingleSession, gabble_jingle_session, G_TYPE_OBJECT);
 enum
 {
   NEW_CONTENT,
+  REMOTE_STATE_CHANGED,
   TERMINATED,
   LAST_SIGNAL
 };
@@ -399,6 +404,11 @@ gabble_jingle_session_class_init (GabbleJingleSessionClass *cls)
         G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST,
         0, NULL, NULL, gabble_marshal_VOID__BOOLEAN_UINT,
         G_TYPE_NONE, 2, G_TYPE_BOOLEAN, G_TYPE_UINT);
+
+  signals[REMOTE_STATE_CHANGED] = g_signal_new ("remote-state-changed",
+        G_TYPE_FROM_CLASS (cls), G_SIGNAL_RUN_LAST,
+        0, NULL, NULL, gabble_marshal_VOID__VOID,
+        G_TYPE_NONE, 0);
 }
 
 typedef void (*HandlerFunc)(GabbleJingleSession *sess,
@@ -901,11 +911,133 @@ on_session_accept (GabbleJingleSession *sess, LmMessageNode *node,
 }
 
 static void
+mute_all (GabbleJingleSession *sess,
+    gboolean mute)
+{
+  GHashTableIter iter;
+  gpointer value;
+
+  g_hash_table_iter_init (&iter, sess->priv->contents);
+
+  while (g_hash_table_iter_next (&iter, NULL, &value))
+    {
+      if (G_OBJECT_TYPE (value) == GABBLE_TYPE_JINGLE_MEDIA_RTP)
+        g_object_set (value, "remote-mute", mute, NULL);
+    }
+}
+
+static gboolean
+set_mute (GabbleJingleSession *sess,
+    const gchar *name,
+    gboolean mute,
+    GError **error)
+{
+  GabbleJingleContent *c;
+
+  if (name == NULL)
+    {
+      mute_all (sess, mute);
+      return TRUE;
+    }
+
+  c = g_hash_table_lookup (sess->priv->contents, name);
+
+  if (c == NULL)
+    {
+      g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_ITEM_NOT_FOUND,
+          "content '%s' does not exist", name);
+      return FALSE;
+    }
+
+  if (G_OBJECT_TYPE (c) != GABBLE_TYPE_JINGLE_MEDIA_RTP)
+    {
+      g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_BAD_REQUEST,
+          "content '%s' isn't an RTP session", name);
+      return FALSE;
+    }
+
+  g_object_set (c, "remote-mute", mute, NULL);
+  return TRUE;
+}
+
+static void
+set_hold (GabbleJingleSession *sess,
+    gboolean hold)
+{
+  sess->priv->remote_hold = hold;
+}
+
+static void
+set_ringing (GabbleJingleSession *sess,
+    gboolean ringing)
+{
+  sess->priv->remote_ringing = ringing;
+}
+
+static gboolean
+handle_payload (GabbleJingleSession *sess,
+    LmMessageNode *payload,
+    gboolean *handled,
+    GError **error)
+{
+  const gchar *ns = lm_message_node_get_namespace (payload);
+  const gchar *elt = lm_message_node_get_name (payload);
+  const gchar *name_attr = lm_message_node_get_attribute (payload, "name");
+
+  if (tp_strdiff (ns, NS_JINGLE_RTP_INFO))
+    {
+      *handled = FALSE;
+      return TRUE;
+    }
+
+  *handled = TRUE;
+
+  if (!tp_strdiff (elt, "active"))
+    {
+      /* Clear all states, we're active */
+      mute_all (sess, FALSE);
+      set_ringing (sess, FALSE);
+      set_hold (sess, FALSE);
+    }
+  else if (!tp_strdiff (elt, "ringing"))
+    {
+      set_ringing (sess, TRUE);
+    }
+  else if (!tp_strdiff (elt, "hold"))
+    {
+      set_hold (sess, TRUE);
+    }
+  else if (!tp_strdiff (elt, "unhold"))
+    {
+      set_hold (sess, FALSE);
+    }
+  /* XEP-0178 says that only <mute/> and <unmute/> can have a name=''
+   * attribute.
+   */
+  else if (!tp_strdiff (elt, "mute"))
+    {
+      return set_mute (sess, name_attr, TRUE, error);
+    }
+  else if (!tp_strdiff (elt, "unmute"))
+    {
+      return set_mute (sess, name_attr, FALSE, error);
+    }
+  else
+    {
+      g_set_error (error, GABBLE_XMPP_ERROR,
+          XMPP_ERROR_JINGLE_UNSUPPORTED_INFO,
+          "<%s> is not known in namespace %s", elt, ns);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
 on_session_info (GabbleJingleSession *sess,
     LmMessageNode *node,
     GError **error)
 {
-  GList *contents, *l;
   gboolean understood_a_payload = FALSE;
   gboolean hit_an_error = FALSE;
   LmMessageNode *n = node->children;
@@ -914,39 +1046,34 @@ on_session_info (GabbleJingleSession *sess,
   if (n == NULL)
     return;
 
-  contents = g_hash_table_get_values (sess->priv->contents);
-
   for (n = node->children; n != NULL; n = n->next)
     {
-      for (l = contents; l != NULL; l = g_list_next (l))
-        {
-          gboolean handled;
-          GError *e = NULL;
+      gboolean handled;
+      GError *e = NULL;
 
-          if (gabble_jingle_content_handle_info (l->data, n, &handled, &e))
-            {
-              understood_a_payload = understood_a_payload || handled;
-            }
-          else
-            {
-              if (hit_an_error)
-                {
-                  DEBUG ("already got another error; ignoring %s", e->message);
-                  g_error_free (e);
-                }
-              else
-                {
-                  DEBUG ("hit an error: %s", e->message);
-                  hit_an_error = TRUE;
-                  g_propagate_error (error, e);
-                }
-            }
+      if (handle_payload (sess, n, &handled, &e))
+        {
+          understood_a_payload = understood_a_payload || handled;
+        }
+      else if (hit_an_error)
+        {
+          DEBUG ("already got another error; ignoring %s", e->message);
+          g_error_free (e);
+        }
+      else
+        {
+          DEBUG ("hit an error: %s", e->message);
+          hit_an_error = TRUE;
+          g_propagate_error (error, e);
         }
     }
 
-  /* If we didn't understand any of the payloads, tell the other end.
+  /* If we understood something, the remote state (may have) changed. Else,
+   * return an error to the peer.
    */
-  if (!understood_a_payload)
+  if (understood_a_payload)
+    g_signal_emit (sess, signals[REMOTE_STATE_CHANGED], 0);
+  else
     g_set_error (error, GABBLE_XMPP_ERROR, XMPP_ERROR_JINGLE_UNSUPPORTED_INFO,
         "no recognized session-info payloads");
 }
@@ -1907,3 +2034,19 @@ gabble_jingle_session_send_held (GabbleJingleSession *sess,
   /* This is just informational, so ignoring the reply. */
   gabble_jingle_session_send (sess, message, NULL, NULL);
 }
+
+gboolean
+gabble_jingle_session_get_remote_hold (GabbleJingleSession *sess)
+{
+  g_assert (GABBLE_IS_JINGLE_SESSION (sess));
+
+  return sess->priv->remote_hold;
+}
+
+gboolean
+gabble_jingle_session_get_remote_ringing (GabbleJingleSession *sess)
+{
+  g_assert (GABBLE_IS_JINGLE_SESSION (sess));
+
+  return sess->priv->remote_ringing;
+}
diff --git a/src/jingle-session.h b/src/jingle-session.h
index a5379ba..870b716 100644
--- a/src/jingle-session.h
+++ b/src/jingle-session.h
@@ -112,6 +112,10 @@ void gabble_jingle_session_send (GabbleJingleSession *sess,
 
 void gabble_jingle_session_send_held (GabbleJingleSession *sess, gboolean held);
 
+gboolean gabble_jingle_session_get_remote_hold (GabbleJingleSession *sess);
+
+gboolean gabble_jingle_session_get_remote_ringing (GabbleJingleSession *sess);
+
 /* Only to be used for the test suite */
 void gabble_set_jingle_session_timeout (guint seconds);
 
diff --git a/src/media-channel-hold.c b/src/media-channel-hold.c
index 2a9215b..e7ee621 100644
--- a/src/media-channel-hold.c
+++ b/src/media-channel-hold.c
@@ -290,84 +290,29 @@ gabble_media_channel_hold_iface_init (gpointer g_iface,
  * is ringing.
  */
 
-static TpChannelCallStateFlags
-jingle_remote_state_to_csf (JingleRtpRemoteState state)
-{
-  TpChannelCallStateFlags ret = 0;
-
-  if (state & JINGLE_RTP_REMOTE_STATE_MUTE)
-    DEBUG ("FIXME: we should be able to expose <mute/> through CallState");
-
-  if (state & JINGLE_RTP_REMOTE_STATE_RINGING)
-    ret |= TP_CHANNEL_CALL_STATE_RINGING;
-
-  if (state & JINGLE_RTP_REMOTE_STATE_HOLD)
-    ret |= TP_CHANNEL_CALL_STATE_HELD;
-
-  return ret;
-}
-
-
 static void
-remote_state_changed_cb (GabbleJingleMediaRtp *rtp,
-    GParamSpec *pspec G_GNUC_UNUSED,
+remote_state_changed_cb (GabbleJingleSession *session,
     GabbleMediaChannel *self)
 {
   GabbleMediaChannelPrivate *priv = self->priv;
-  JingleRtpRemoteState state = gabble_jingle_media_rtp_get_remote_state (rtp);
-  TpChannelCallStateFlags csf = 0;
-
-  DEBUG ("Content %p's state changed to %u (current channel state: %u)", rtp,
-      state, priv->remote_state);
-
-  if (state == priv->remote_state)
-    {
-      DEBUG ("already in that state");
-      return;
-    }
-
-  if ((state & priv->remote_state) == priv->remote_state)
-    {
-      /* state has a superset of the flags in the current aggregated level, add
-       * the new flags.
-       */
-      DEBUG ("%u is a superset of %u", state, priv->remote_state);
-      priv->remote_state = state;
-    }
-  else
-    {
-      /* This content is now less held than the current aggregated level; we
-       * need to recalculate the combined hold level and see if it's changed.
-       */
-      guint i = 0;
+  TpChannelCallStateFlags call_state = 0;
 
-      DEBUG ("%u less held than %u; recalculating", state, priv->remote_state);
-      state = JINGLE_RTP_REMOTE_STATE_ACTIVE;
+  if (gabble_jingle_session_get_remote_hold (session))
+    call_state |= TP_CHANNEL_CALL_STATE_HELD;
 
-      for (i = 0; i < priv->streams->len; i++)
-        {
-          GabbleJingleMediaRtp *c = gabble_media_stream_get_content (
-                g_ptr_array_index (priv->streams, i));
-          JingleRtpRemoteState s = gabble_jingle_media_rtp_get_remote_state (c);
+  if (gabble_jingle_session_get_remote_ringing (session))
+    call_state |= TP_CHANNEL_CALL_STATE_RINGING;
 
-          state |= s;
-          DEBUG ("%p in state %u; combined flags so far are %u", c, s, state);
-        }
+  DEBUG ("Call state changed to %u (current state %u)", call_state,
+      priv->call_state);
 
-      if (priv->remote_state == state)
-        {
-          DEBUG ("no change");
-          return;
-        }
+  if (call_state == priv->call_state)
+    return;
 
-      priv->remote_state = state;
-    }
+  priv->call_state = call_state;
 
-  csf = jingle_remote_state_to_csf (priv->remote_state);
-  DEBUG ("emitting CallStateChanged(%u, %u) (JingleRtpRemoteState %u)",
-      priv->session->peer, csf, priv->remote_state);
   tp_svc_channel_interface_call_state_emit_call_state_changed (self,
-      priv->session->peer, csf);
+      priv->session->peer, call_state);
 }
 
 
@@ -377,13 +322,10 @@ gabble_media_channel_get_call_states (TpSvcChannelInterfaceCallState *iface,
     DBusGMethodInvocation *context)
 {
   GabbleMediaChannel *self = (GabbleMediaChannel *) iface;
-  GabbleMediaChannelPrivate *priv = self->priv;
-  JingleRtpRemoteState state = priv->remote_state;
   GHashTable *states = g_hash_table_new (g_direct_hash, g_direct_equal);
 
-  if (state != JINGLE_RTP_REMOTE_STATE_ACTIVE)
-    g_hash_table_insert (states, GUINT_TO_POINTER (priv->session->peer),
-        GUINT_TO_POINTER (jingle_remote_state_to_csf (state)));
+  g_hash_table_insert (states, GUINT_TO_POINTER (self->priv->session->peer),
+      GUINT_TO_POINTER (self->priv->call_state));
 
   tp_svc_channel_interface_call_state_return_from_get_call_states (context,
       states);
@@ -405,8 +347,8 @@ gabble_media_channel_call_state_iface_init (gpointer g_iface,
 }
 
 
-/* Called by construct_stream to allow the Hold and CallState code to hook
- * itself up to a new stream.
+/* Called by construct_stream to allow the Hold code to hook itself up to a new
+ * stream.
  */
 void
 gabble_media_channel_hold_new_stream (GabbleMediaChannel *chan,
@@ -422,12 +364,19 @@ gabble_media_channel_hold_new_stream (GabbleMediaChannel *chan,
 
   /* A stream being added might cause the "total" hold state to change */
   stream_hold_state_changed (stream, NULL, chan);
+}
+
+/* Called by _latch_to_session to allow the CallState code to hook itself up to
+ * a new session.
+ */
+void
+gabble_media_channel_hold_latch_to_session (GabbleMediaChannel *chan)
+{
+  g_assert (chan->priv->session != NULL);
 
-  /* Watch the active/mute/held state of the corresponding content so we can
-   * keep the call state up to date, and call the callback once to pick up the
-   * current state of this content.
+  /* Watch the active/ringing/held state of the session so we can keep the call
+   * state up to date.
    */
-  gabble_signal_connect_weak (content, "notify::remote-state",
-      (GCallback) remote_state_changed_cb, chan_o);
-  remote_state_changed_cb (content, NULL, chan);
+  gabble_signal_connect_weak (chan->priv->session, "remote-state-changed",
+      (GCallback) remote_state_changed_cb, (GObject *) chan);
 }
diff --git a/src/media-channel-internal.h b/src/media-channel-internal.h
index 6138f66..8e4003b 100644
--- a/src/media-channel-internal.h
+++ b/src/media-channel-internal.h
@@ -55,10 +55,7 @@ struct _GabbleMediaChannelPrivate
   TpLocalHoldState hold_state;
   TpLocalHoldStateReason hold_state_reason;
 
-  /* The "most held" of all associated contents' current states, which is what
-   * we present on CallState.
-   */
-  JingleRtpRemoteState remote_state;
+  TpChannelCallStateFlags call_state;
 
   GPtrArray *delayed_request_streams;
 
@@ -71,6 +68,8 @@ struct _GabbleMediaChannelPrivate
   unsigned dispose_has_run:1;
 };
 
+void gabble_media_channel_hold_latch_to_session (GabbleMediaChannel *chan);
+
 void gabble_media_channel_hold_new_stream (GabbleMediaChannel *chan,
     GabbleMediaStream *stream,
     GabbleJingleMediaRtp *content);
diff --git a/src/media-channel.c b/src/media-channel.c
index 0731f0c..c3c66ee 100644
--- a/src/media-channel.c
+++ b/src/media-channel.c
@@ -244,6 +244,8 @@ _latch_to_session (GabbleMediaChannel *chan)
   g_signal_connect (priv->session, "terminated",
                     (GCallback) session_terminated_cb, chan);
 
+  gabble_media_channel_hold_latch_to_session (chan);
+
   g_assert (priv->streams->len == 0);
 
   tp_svc_channel_interface_media_signalling_emit_new_session_handler (
diff --git a/src/media-stream.c b/src/media-stream.c
index 2a2c0ef..b2446a9 100644
--- a/src/media-stream.c
+++ b/src/media-stream.c
@@ -148,8 +148,8 @@ static void content_state_changed_cb (GabbleJingleContent *c,
      GParamSpec *pspec, GabbleMediaStream *stream);
 static void content_senders_changed_cb (GabbleJingleContent *c,
      GParamSpec *pspec, GabbleMediaStream *stream);
-static void remote_state_changed_cb (GabbleJingleMediaRtp *rtp,
-    GParamSpec *pspec, GabbleMediaStream *stream);
+static void remote_state_changed_cb (GabbleJingleSession *session,
+    GabbleMediaStream *stream);
 static void content_removed_cb (GabbleJingleContent *content,
       GabbleMediaStream *stream);
 static void update_direction (GabbleMediaStream *stream, GabbleJingleContent *c);
@@ -459,8 +459,8 @@ gabble_media_stream_set_property (GObject      *object,
       gabble_signal_connect_weak (priv->content, "notify::senders",
           (GCallback) content_senders_changed_cb, object);
 
-      gabble_signal_connect_weak (priv->content, "notify::remote-state",
-          (GCallback) remote_state_changed_cb, object);
+      gabble_signal_connect_weak (priv->content->session,
+          "remote-state-changed", (GCallback) remote_state_changed_cb, object);
 
       gabble_signal_connect_weak (priv->content, "removed",
           (GCallback) content_removed_cb, object);
@@ -1595,15 +1595,13 @@ content_senders_changed_cb (GabbleJingleContent *c,
 }
 
 static void
-remote_state_changed_cb (GabbleJingleMediaRtp *rtp,
-    GParamSpec *pspec,
+remote_state_changed_cb (GabbleJingleSession *session,
     GabbleMediaStream *stream)
 {
   GabbleMediaStreamPrivate *priv = stream->priv;
-  JingleRtpRemoteState state = gabble_jingle_media_rtp_get_remote_state (rtp);
   gboolean old_hold = priv->on_hold;
 
-  priv->on_hold = (state & JINGLE_RTP_REMOTE_STATE_HOLD);
+  priv->on_hold = gabble_jingle_session_get_remote_hold (session);
 
   if (old_hold != priv->on_hold)
     push_sending (stream);
-- 
1.5.6.5




More information about the telepathy-commits mailing list