[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