[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