static gboolean add_member (GObject *object, TpHandle member, const gchar *message, GError **error) { ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (object); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (self->priv->conn, TP_HANDLE_TYPE_CONTACT); /* In connection managers that supported the RequestChannel method for * streamed media channels, it would be necessary to support adding the * called contact to the members of an outgoing call. However, in this * legacy-free example, we don't support that usage, so the only use for * AddMembers is to accept an incoming call. */ if (member == self->group.self_handle && tp_handle_set_is_member (self->group.local_pending, member)) { /* We're in local-pending, move to members to accept. */ TpIntSet *set = tp_intset_new_containing (member); GHashTableIter iter; gpointer v; g_assert (self->priv->progress == PROGRESS_CALLING); g_message ("SIGNALLING: send: Accepting incoming call from %s", tp_handle_inspect (contact_repo, self->priv->handle)); self->priv->progress = PROGRESS_ACTIVE; tp_group_mixin_change_members (object, "", set /* added */, NULL /* nobody removed */, NULL /* nobody added to local pending */, NULL /* nobody added to remote pending */, member /* actor */, TP_CHANNEL_GROUP_CHANGE_REASON_NONE); tp_intset_destroy (set); g_hash_table_iter_init (&iter, self->priv->streams); while (g_hash_table_iter_next (&iter, NULL, &v)) { /* we accept the proposed stream direction... */ example_callable_media_stream_accept_proposed_direction (v); /* ... and the stream tries to connect */ example_callable_media_stream_connect (v); } return TRUE; } /* Otherwise it's a meaningless request, so reject it. */ g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, "Cannot add handle %u to channel", member); return FALSE; }
static void example_callable_media_channel_close (ExampleCallableMediaChannel *self, TpHandle actor, TpChannelGroupChangeReason reason) { if (self->priv->progress != PROGRESS_ENDED) { TpIntSet *everyone; self->priv->progress = PROGRESS_ENDED; if (actor == self->group.self_handle) { const gchar *send_reason; /* In a real protocol these would be some sort of real protocol * construct, like an XMPP error stanza or a SIP error code */ switch (reason) { case TP_CHANNEL_GROUP_CHANGE_REASON_BUSY: send_reason = "<user-is-busy/>"; break; case TP_CHANNEL_GROUP_CHANGE_REASON_NO_ANSWER: send_reason = "<no-answer/>"; break; default: send_reason = "<call-terminated/>"; } g_message ("SIGNALLING: send: Terminating call: %s", send_reason); } everyone = tp_intset_new_containing (self->priv->handle); tp_intset_add (everyone, self->group.self_handle); tp_group_mixin_change_members ((GObject *) self, "", NULL /* nobody added */, everyone /* removed */, NULL /* nobody locally pending */, NULL /* nobody remotely pending */, actor, reason); tp_intset_destroy (everyone); g_signal_emit (self, signals[SIGNAL_CALL_TERMINATED], 0); tp_svc_channel_emit_closed (self); } }
static gboolean add_member (GObject *obj, TpHandle handle, const gchar *message, GError **error) { TpTestsTextChannelGroup *self = TP_TESTS_TEXT_CHANNEL_GROUP (obj); TpIntSet *add = tp_intset_new (); tp_intset_add (add, handle); tp_group_mixin_change_members (obj, message, add, NULL, NULL, NULL, self->conn->self_handle, TP_CHANNEL_GROUP_CHANGE_REASON_NONE); tp_intset_destroy (add); return TRUE; }
static void constructed (GObject *object) { void (*chain_up) (GObject *) = ((GObjectClass *) example_callable_media_channel_parent_class)->constructed; ExampleCallableMediaChannel *self = EXAMPLE_CALLABLE_MEDIA_CHANNEL (object); TpHandleRepoIface *contact_repo = tp_base_connection_get_handles (self->priv->conn, TP_HANDLE_TYPE_CONTACT); TpIntSet *members; TpIntSet *local_pending; if (chain_up != NULL) chain_up (object); tp_handle_ref (contact_repo, self->priv->handle); tp_handle_ref (contact_repo, self->priv->initiator); tp_dbus_daemon_register_object ( tp_base_connection_get_dbus_daemon (self->priv->conn), self->priv->object_path, self); tp_group_mixin_init (object, G_STRUCT_OFFSET (ExampleCallableMediaChannel, group), contact_repo, self->priv->conn->self_handle); /* Initially, the channel contains the initiator as a member; they are also * the actor for the change that adds any initial members. */ members = tp_intset_new_containing (self->priv->initiator); if (self->priv->locally_requested) { /* Nobody is locally pending. The remote peer will turn up in * remote-pending state when we actually contact them, which is done * in RequestStreams */ self->priv->progress = PROGRESS_NONE; local_pending = NULL; } else { /* This is an incoming call, so the self-handle is locally * pending, to indicate that we need to answer. */ self->priv->progress = PROGRESS_CALLING; local_pending = tp_intset_new_containing (self->priv->conn->self_handle); } tp_group_mixin_change_members (object, "", members /* added */, NULL /* nobody removed */, local_pending, /* added to local-pending */ NULL /* nobody added to remote-pending */, self->priv->initiator /* actor */, TP_CHANNEL_GROUP_CHANGE_REASON_NONE); tp_intset_destroy (members); if (local_pending != NULL) tp_intset_destroy (local_pending); /* We don't need to allow adding or removing members to this Group in ways * that need flags set, so the only flag we set is to say we support the * Properties interface to the Group. * * It doesn't make sense to add anyone to the Group, since we already know * who we're going to call (or were called by). The only call to AddMembers * we need to support is to move ourselves from local-pending to member in * the incoming call case, and that's always allowed anyway. * * (Connection managers that support the various backwards-compatible * ways to make an outgoing StreamedMedia channel have to support adding the * peer to remote-pending, but that has no actual effect other than to * obscure what's going on; in this one, there's no need to support that * usage.) * * Similarly, it doesn't make sense to remove anyone from this Group apart * from ourselves (to hang up), and removing the SelfHandle is always * allowed anyway. */ tp_group_mixin_change_flags (object, TP_CHANNEL_GROUP_FLAG_PROPERTIES, 0); /* Future versions of telepathy-spec will allow a channel request to * say "initially include an audio stream" and/or "initially include a video * stream", which would be represented like this; we don't support this * usage yet, though, so ExampleCallableMediaManager will never invoke * our constructor in this way. */ g_assert (!(self->priv->locally_requested && self->priv->initial_audio)); g_assert (!(self->priv->locally_requested && self->priv->initial_video)); if (!self->priv->locally_requested) { /* the caller has almost certainly asked us for some streams - there's * not much point in having a call otherwise */ if (self->priv->initial_audio) { g_message ("Channel initially has an audio stream"); example_callable_media_channel_add_stream (self, TP_MEDIA_STREAM_TYPE_AUDIO, FALSE); } if (self->priv->initial_video) { g_message ("Channel initially has a video stream"); example_callable_media_channel_add_stream (self, TP_MEDIA_STREAM_TYPE_VIDEO, FALSE); } } }