diff options
Diffstat (limited to 'rakia/media-session.c')
-rw-r--r-- | rakia/media-session.c | 2265 |
1 files changed, 0 insertions, 2265 deletions
diff --git a/rakia/media-session.c b/rakia/media-session.c deleted file mode 100644 index 8bc1588..0000000 --- a/rakia/media-session.c +++ /dev/null @@ -1,2265 +0,0 @@ -/* - * sip-media-session.c - Source for RakiaMediaSession - * Copyright (C) 2005 Collabora Ltd. - * Copyright (C) 2005-2010 Nokia Corporation - * @author Kai Vehmanen <first.surname@nokia.com> - * @author Mikhail Zabaluev <mikhail.zabaluev@nokia.com> - * - * Based on telepathy-gabble implementation (gabble-media-session). - * @author Ole Andre Vadla Ravnaas <ole.andre.ravnaas@collabora.co.uk> - * - * This work is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This work is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this work; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "config.h" - -#include "rakia/media-session.h" - -#include <dbus/dbus-glib.h> -#include <stdlib.h> -#include <time.h> -#include <string.h> - -#include <sofia-sip/sip_status.h> - -#include <telepathy-glib/dbus.h> -#include <telepathy-glib/errors.h> -#include <telepathy-glib/gtypes.h> -#include <telepathy-glib/interfaces.h> -#include <telepathy-glib/svc-media-interfaces.h> - -#include "config.h" - -#include <rakia/base-connection.h> - -#include "rakia/media-channel.h" -#include "rakia/media-stream.h" - -#include "signals-marshal.h" - -#define DEBUG_FLAG RAKIA_DEBUG_MEDIA -#include "rakia/debug.h" - -/* The timeout for outstanding re-INVITE transactions in seconds. - * Chosen to match the allowed cancellation timeout for proxies - * described in RFC 3261 Section 13.3.1.1 */ -#define RAKIA_REINVITE_TIMEOUT 180 - -static void session_handler_iface_init (gpointer, gpointer); - -G_DEFINE_TYPE_WITH_CODE(RakiaMediaSession, - rakia_media_session, - G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (TP_TYPE_SVC_MEDIA_SESSION_HANDLER, - session_handler_iface_init) - ) - -/* signal enum */ -enum -{ - SIG_STATE_CHANGED, - SIG_DTMF_READY, - SIG_LAST_SIGNAL -}; - -/* properties */ -enum -{ - PROP_MEDIA_CHANNEL = 1, - PROP_DBUS_DAEMON, - PROP_OBJECT_PATH, - PROP_NUA_OP, - PROP_PEER, - PROP_HOLD_STATE, - PROP_HOLD_STATE_REASON, - PROP_REMOTE_PTIME, - PROP_REMOTE_MAX_PTIME, - PROP_RTCP_ENABLED, - PROP_LOCAL_IP_ADDRESS, - PROP_STUN_SERVERS, - LAST_PROPERTY -}; - -static guint signals[SIG_LAST_SIGNAL] = {0}; - -#ifdef ENABLE_DEBUG - -/** - * Media session states: - * - created, objects created, local cand/codec query ongoing - * - invite-sent, an INVITE with local SDP sent, awaiting response - * - invite-received, a remote INVITE received, response is pending - * - response-received, a 200 OK received, codec intersection is in progress - * - active, codecs and candidate pairs have been negotiated (note, - * SteamEngine might still fail to verify connectivity and report - * an error) - * - reinvite-sent, a local re-INVITE sent, response is pending - * - reinvite-received, a remote re-INVITE received, response is pending - * - ended, session has ended - */ -static const char *const session_states[NUM_RAKIA_MEDIA_SESSION_STATES] = -{ - "created", - "invite-sent", - "invite-received", - "response-received", - "active", - "reinvite-sent", - "reinvite-received", - "reinvite-pending", - "ended" -}; - -#define SESSION_DEBUG(session, format, ...) \ - rakia_log (DEBUG_FLAG, G_LOG_LEVEL_DEBUG, "session [%-17s]: " format, \ - session_states[(session)->priv->state],##__VA_ARGS__) - -#define SESSION_MESSAGE(session, format, ...) \ - rakia_log (DEBUG_FLAG, G_LOG_LEVEL_MESSAGE, "session [%-17s]: " format, \ - session_states[(session)->priv->state],##__VA_ARGS__) - -#else /* !ENABLE_DEBUG */ - -#define SESSION_DEBUG(session, format, ...) G_STMT_START { } G_STMT_END -#define SESSION_MESSAGE(session, format, ...) G_STMT_START { } G_STMT_END - -#endif /* ENABLE_DEBUG */ - -/* private structure */ -struct _RakiaMediaSessionPrivate -{ - TpDBusDaemon *dbus_daemon; - RakiaMediaChannel *channel; /* see gobj. prop. 'media-channel' */ - gchar *object_path; /* see gobj. prop. 'object-path' */ - nua_handle_t *nua_op; /* see gobj. prop. 'nua-handle' */ - TpHandle peer; /* see gobj. prop. 'peer' */ - gchar *local_ip_address; /* see gobj. prop. 'local-ip-address' */ - gchar *remote_ptime; /* see gobj. prop. 'remote-ptime' */ - gchar *remote_max_ptime; /* see gobj. prop. 'remote-max-ptime' */ - gboolean rtcp_enabled; /* see gobj. prop. 'rtcp-enabled' */ - RakiaMediaSessionState state; /* session state */ - TpLocalHoldState hold_state; /* local hold state aggregated from stream directions */ - TpLocalHoldStateReason hold_reason; /* last used hold state change reason */ - nua_saved_event_t saved_event[1]; /* Saved incoming request event */ - gint local_non_ready; /* number of streams with local information update pending */ - guint remote_stream_count; /* number of streams last seen in a remote offer */ - guint glare_timer_id; - su_home_t *home; /* Sofia memory home for remote SDP session structure */ - su_home_t *backup_home; /* Sofia memory home for previous generation remote SDP session*/ - sdp_session_t *remote_sdp; /* last received remote session */ - sdp_session_t *backup_remote_sdp; /* previous remote session */ - gchar *local_sdp; /* local session as SDP string */ - GPtrArray *streams; - gboolean remote_initiated; /*< session is remotely intiated */ - gboolean accepted; /*< session has been locally accepted for use */ - gboolean se_ready; /*< connection established with stream-engine */ - gboolean audio_connected; /*< an audio stream has reached connected state */ - gboolean pending_offer; /*< local media have been changed, but a re-INVITE is pending */ - gboolean dispose_has_run; -}; - -#define RAKIA_MEDIA_SESSION_GET_PRIVATE(session) ((session)->priv) - -static void rakia_media_session_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec); -static void rakia_media_session_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec); - -static RakiaMediaStream * -rakia_media_session_get_stream (RakiaMediaSession *self, - guint stream_id, - GError **error); - -static void priv_request_response_step (RakiaMediaSession *session); -static void priv_session_invite (RakiaMediaSession *session, gboolean reinvite); -static void priv_local_media_changed (RakiaMediaSession *session); -static gboolean priv_update_remote_media (RakiaMediaSession *session, - gboolean authoritative); -static void priv_save_event (RakiaMediaSession *self); -static void priv_zap_event (RakiaMediaSession *self); - -static void rakia_media_session_init (RakiaMediaSession *self) -{ - RakiaMediaSessionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - RAKIA_TYPE_MEDIA_SESSION, RakiaMediaSessionPrivate); - - self->priv = priv; - - priv->state = RAKIA_MEDIA_SESSION_STATE_CREATED; - priv->hold_state = TP_LOCAL_HOLD_STATE_UNHELD; - priv->hold_reason = TP_LOCAL_HOLD_STATE_REASON_NONE; - priv->rtcp_enabled = TRUE; - - /* allocate any data required by the object here */ - priv->streams = g_ptr_array_new (); -} - -static GObject * -rakia_media_session_constructor (GType type, guint n_props, - GObjectConstructParam *props) -{ - GObject *obj; - RakiaMediaSessionPrivate *priv; - - obj = G_OBJECT_CLASS (rakia_media_session_parent_class)-> - constructor (type, n_props, props); - priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (RAKIA_MEDIA_SESSION (obj)); - - g_assert (TP_IS_DBUS_DAEMON (priv->dbus_daemon)); - tp_dbus_daemon_register_object (priv->dbus_daemon, priv->object_path, obj); - - return obj; -} - -static void rakia_media_session_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - RakiaMediaSession *session = RAKIA_MEDIA_SESSION (object); - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - - switch (property_id) - { - case PROP_DBUS_DAEMON: - g_value_set_object (value, priv->dbus_daemon); - break; - case PROP_MEDIA_CHANNEL: - g_value_set_object (value, priv->channel); - break; - case PROP_OBJECT_PATH: - g_value_set_string (value, priv->object_path); - break; - case PROP_NUA_OP: - g_value_set_pointer (value, priv->nua_op); - break; - case PROP_PEER: - g_value_set_uint (value, priv->peer); - break; - case PROP_HOLD_STATE: - g_value_set_uint (value, priv->hold_state); - break; - case PROP_HOLD_STATE_REASON: - g_value_set_uint (value, priv->hold_reason); - break; - case PROP_REMOTE_PTIME: - g_value_set_string (value, priv->remote_ptime); - break; - case PROP_REMOTE_MAX_PTIME: - g_value_set_string (value, priv->remote_max_ptime); - break; - case PROP_LOCAL_IP_ADDRESS: - g_value_set_string (value, priv->local_ip_address); - break; - case PROP_RTCP_ENABLED: - g_value_set_boolean (value, priv->rtcp_enabled); - break; - case PROP_STUN_SERVERS: - { - /* TODO: should be able to get all entries from the DNS lookup(s). - * At the moment, rawudp ignores all servers except the first one. */ - GPtrArray *servers; - gchar *stun_server = NULL; - guint stun_port = RAKIA_DEFAULT_STUN_PORT; - - g_return_if_fail (priv->channel != NULL); - - g_object_get (priv->channel, - "stun-server", &stun_server, - "stun-port", &stun_port, - NULL); - - servers = g_ptr_array_new (); - - if (stun_server != NULL) - { - GValue addr = { 0 }; - const GType addr_type = TP_STRUCT_TYPE_SOCKET_ADDRESS_IP; - - g_value_init (&addr, addr_type); - g_value_take_boxed (&addr, - dbus_g_type_specialized_construct (addr_type)); - - dbus_g_type_struct_set (&addr, - 0, stun_server, - 1, (guint16) stun_port, - G_MAXUINT); - - g_ptr_array_add (servers, g_value_get_boxed (&addr)); - - g_free (stun_server); - } - - g_value_take_boxed (value, servers); - } - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void rakia_media_session_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - RakiaMediaSession *session = RAKIA_MEDIA_SESSION (object); - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - - switch (property_id) - { - case PROP_DBUS_DAEMON: - g_assert (priv->dbus_daemon == NULL); /* construct-only */ - priv->dbus_daemon = g_value_dup_object (value); - break; - case PROP_MEDIA_CHANNEL: - priv->channel = RAKIA_MEDIA_CHANNEL (g_value_get_object (value)); - break; - case PROP_OBJECT_PATH: - g_assert (priv->object_path == NULL); - priv->object_path = g_value_dup_string (value); - break; - case PROP_NUA_OP: - /* you can only set the NUA handle once - migrating a media session - * between two NUA handles makes no sense */ - g_return_if_fail (priv->nua_op == NULL); - priv->nua_op = g_value_get_pointer (value); - nua_handle_ref (priv->nua_op); - break; - case PROP_PEER: - priv->peer = g_value_get_uint (value); - break; - case PROP_LOCAL_IP_ADDRESS: - g_assert (priv->local_ip_address == NULL); - priv->local_ip_address = g_value_dup_string (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void rakia_media_session_dispose (GObject *object); -static void rakia_media_session_finalize (GObject *object); - -static void -rakia_media_session_class_init (RakiaMediaSessionClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GParamSpec *param_spec; - - g_type_class_add_private (klass, sizeof (RakiaMediaSessionPrivate)); - - object_class->constructor = rakia_media_session_constructor; - - object_class->get_property = rakia_media_session_get_property; - object_class->set_property = rakia_media_session_set_property; - - object_class->dispose = rakia_media_session_dispose; - object_class->finalize = rakia_media_session_finalize; - - param_spec = g_param_spec_object ("dbus-daemon", "TpDBusDaemon", - "Connection to D-Bus.", TP_TYPE_DBUS_DAEMON, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_DBUS_DAEMON, param_spec); - - param_spec = g_param_spec_object ("media-channel", "RakiaMediaChannel object", - "SIP media channel object that owns this media session object" - " (not reference counted).", - RAKIA_TYPE_MEDIA_CHANNEL, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_MEDIA_CHANNEL, param_spec); - - param_spec = g_param_spec_string ("object-path", "D-Bus object path", - "The D-Bus object path used for this object on the bus.", - NULL, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_OBJECT_PATH, param_spec); - - param_spec = g_param_spec_pointer ("nua-handle", "NUA handle", - "NUA stack operation handle associated with this media session.", - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_NUA_OP, param_spec); - - param_spec = g_param_spec_uint ("peer", "Session peer", - "The TpHandle representing the contact with whom this session communicates.", - 0, G_MAXUINT32, - 0, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_PEER, param_spec); - - param_spec = g_param_spec_uint ("hold-state", "Local hold state", - "The current Local_Hold_State value as reported by the Hold interface", - TP_LOCAL_HOLD_STATE_UNHELD, TP_LOCAL_HOLD_STATE_PENDING_UNHOLD, - TP_LOCAL_HOLD_STATE_UNHELD, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_HOLD_STATE, param_spec); - - param_spec = g_param_spec_uint ("hold-state-reason", - "Local hold state change reason", - "The last Local_Hold_State_Reason value as reported by the Hold interface", - TP_LOCAL_HOLD_STATE_REASON_NONE, - TP_LOCAL_HOLD_STATE_REASON_RESOURCE_NOT_AVAILABLE, - TP_LOCAL_HOLD_STATE_REASON_NONE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_HOLD_STATE_REASON, param_spec); - - param_spec = g_param_spec_string ("remote-ptime", - "a=ptime value of remote media session", - "Value of the a=ptime attribute of the remote media session, or NULL", - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_REMOTE_PTIME, param_spec); - - param_spec = g_param_spec_string ("remote-max-ptime", - "a=maxptime value of remote media session", - "Value of the a=maxptime attribute of the remote media session, or NULL", - NULL, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_REMOTE_MAX_PTIME, param_spec); - - param_spec = g_param_spec_string ("local-ip-address", "Local IP address", - "The local IP address preferred for media streams", - NULL, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_LOCAL_IP_ADDRESS, param_spec); - - param_spec = g_param_spec_boolean ("rtcp-enabled", "RTCP enabled", - "Is RTCP enabled session-wide", - TRUE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_RTCP_ENABLED, param_spec); - - param_spec = g_param_spec_boxed ("stun-servers", "STUN servers", - "Array of IP address-port pairs for available STUN servers", - TP_ARRAY_TYPE_SOCKET_ADDRESS_IP_LIST, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - g_object_class_install_property (object_class, PROP_STUN_SERVERS, param_spec); - - signals[SIG_STATE_CHANGED] = - g_signal_new ("state-changed", - G_OBJECT_CLASS_TYPE (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, - 0, - NULL, NULL, - _rakia_marshal_VOID__UINT_UINT, - G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); - - signals[SIG_DTMF_READY] = - g_signal_new ("dtmf-ready", - G_OBJECT_CLASS_TYPE (klass), - G_SIGNAL_RUN_LAST, - 0, NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); -} - -static void -rakia_media_session_dispose (GObject *object) -{ - RakiaMediaSession *self = RAKIA_MEDIA_SESSION (object); - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - - if (priv->dispose_has_run) - return; - - DEBUG("enter"); - - priv->dispose_has_run = TRUE; - - if (priv->glare_timer_id) - g_source_remove (priv->glare_timer_id); - - tp_clear_object (&priv->dbus_daemon); - - if (G_OBJECT_CLASS (rakia_media_session_parent_class)->dispose) - G_OBJECT_CLASS (rakia_media_session_parent_class)->dispose (object); - - DEBUG("exit"); -} - -static void -rakia_media_session_finalize (GObject *object) -{ - RakiaMediaSession *self = RAKIA_MEDIA_SESSION (object); - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - guint i; - - /* terminating the session should have discarded the NUA handle */ - g_assert (priv->nua_op == NULL); - - /* free any data held directly by the object here */ - - for (i = 0; i < priv->streams->len; i++) { - RakiaMediaStream *stream = g_ptr_array_index (priv->streams, i); - if (stream != NULL) - { - WARNING ("stream %u (%p) left over, reaping", i, stream); - g_object_unref (stream); - } - } - g_ptr_array_free(priv->streams, TRUE); - - priv_zap_event (self); - - if (priv->home != NULL) - su_home_unref (priv->home); - if (priv->backup_home != NULL) - su_home_unref (priv->backup_home); - - g_free (priv->local_sdp); - g_free (priv->remote_ptime); - g_free (priv->remote_max_ptime); - g_free (priv->local_ip_address); - g_free (priv->object_path); - - G_OBJECT_CLASS (rakia_media_session_parent_class)->finalize (object); - - DEBUG("exit"); -} - - - -/** - * rakia_media_session_error - * - * Implements DBus method Error - * on interface org.freedesktop.Telepathy.Media.SessionHandler - */ -static void -rakia_media_session_error (TpSvcMediaSessionHandler *iface, - guint errno, - const gchar *message, - DBusGMethodInvocation *context) -{ - RakiaMediaSession *obj = RAKIA_MEDIA_SESSION (iface); - - SESSION_DEBUG (obj, "Media.SessionHandler::Error called (%s), terminating session", message); - - rakia_media_session_terminate (obj); - - tp_svc_media_session_handler_return_from_error (context); -} - -static void priv_emit_new_stream (RakiaMediaSession *self, - RakiaMediaStream *stream) -{ - gchar *object_path; - guint id; - guint media_type; - guint direction; - - g_object_get (stream, - "object-path", &object_path, - "id", &id, - "media-type", &media_type, - "direction", &direction, - NULL); - - /* note: 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 ( - (TpSvcMediaSessionHandler *)self, object_path, id, media_type, - direction); - - g_free (object_path); -} - - -/** - * rakia_media_session_ready - * - * Implements DBus method Ready - * on interface org.freedesktop.Telepathy.Media.SessionHandler - */ -static void -rakia_media_session_ready (TpSvcMediaSessionHandler *iface, - DBusGMethodInvocation *context) -{ - RakiaMediaSession *self = RAKIA_MEDIA_SESSION (iface); - RakiaMediaSessionPrivate *priv = self->priv; - guint i; - - SESSION_DEBUG (self, "Media.SessionHandler.Ready called"); - - if (!priv->se_ready) - { - priv->se_ready = TRUE; - - for (i = 0; i < priv->streams->len; i++) - { - RakiaMediaStream *stream = g_ptr_array_index (priv->streams, i); - if (stream) - priv_emit_new_stream (self, stream); - } - } - - tp_svc_media_session_handler_return_from_ready (context); -} - -/*********************************************************************** - * Helper functions follow (not based on generated templates) - ***********************************************************************/ - -TpHandle -rakia_media_session_get_peer (RakiaMediaSession *session) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - return priv->peer; -} - -RakiaMediaSessionState -rakia_media_session_get_state (RakiaMediaSession *session) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - return priv->state; -} - -static gboolean -rakia_media_session_supports_media_type (guint media_type) -{ - switch (media_type) - { - case TP_MEDIA_STREAM_TYPE_AUDIO: - case TP_MEDIA_STREAM_TYPE_VIDEO: - return TRUE; - } - return FALSE; -} - -static void -priv_close_all_streams (RakiaMediaSession *session) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - guint i; - for (i = 0; i < priv->streams->len; i++) - { - RakiaMediaStream *stream; - stream = g_ptr_array_index (priv->streams, i); - if (stream != NULL) - rakia_media_stream_close (stream); - g_assert (g_ptr_array_index (priv->streams, i) == NULL); - } -} - -static void -priv_apply_streams_pending_direction (RakiaMediaSession *session, - guint pending_send_mask) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - RakiaMediaStream *stream; - guint i; - - /* If there has been a local change pending a re-INVITE, - * suspend remote approval until the next transaction */ - if (priv->pending_offer) - pending_send_mask &= ~(guint)TP_MEDIA_STREAM_PENDING_REMOTE_SEND; - - /* Apply the pending direction changes */ - for (i = 0; i < priv->streams->len; i++) - { - stream = g_ptr_array_index(priv->streams, i); - if (stream != NULL) - rakia_media_stream_apply_pending_direction (stream, pending_send_mask); - } -} - -void -rakia_media_session_change_state (RakiaMediaSession *session, - RakiaMediaSessionState new_state) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - guint old_state; - - if (priv->state == new_state) - return; - - SESSION_DEBUG (session, "changing state to %s", session_states[new_state]); - - old_state = priv->state; - priv->state = new_state; - - switch (new_state) - { - case RAKIA_MEDIA_SESSION_STATE_CREATED: - case RAKIA_MEDIA_SESSION_STATE_INVITE_RECEIVED: - case RAKIA_MEDIA_SESSION_STATE_REINVITE_RECEIVED: - case RAKIA_MEDIA_SESSION_STATE_INVITE_SENT: - case RAKIA_MEDIA_SESSION_STATE_REINVITE_SENT: - case RAKIA_MEDIA_SESSION_STATE_RESPONSE_RECEIVED: - case RAKIA_MEDIA_SESSION_STATE_REINVITE_PENDING: - break; - case RAKIA_MEDIA_SESSION_STATE_ACTIVE: - /* Apply any pending remote send after outgoing INVITEs. - * We don't want automatic removal of pending local send after - * responding to incoming re-INVITEs, however */ - priv_apply_streams_pending_direction (session, - TP_MEDIA_STREAM_PENDING_REMOTE_SEND); - break; - case RAKIA_MEDIA_SESSION_STATE_ENDED: - priv_close_all_streams (session); - SESSION_DEBUG (session, "destroying the NUA handle %p", priv->nua_op); - if (priv->nua_op != NULL) - { - nua_handle_destroy (priv->nua_op); - priv->nua_op = NULL; - } - break; - case NUM_RAKIA_MEDIA_SESSION_STATES: - g_assert_not_reached(); - - /* Don't add default because we want to be warned by the compiler - * about unhandled states */ - } - - g_signal_emit (session, signals[SIG_STATE_CHANGED], 0, old_state, new_state); - - if (new_state == RAKIA_MEDIA_SESSION_STATE_ACTIVE && priv->pending_offer) - priv_session_invite (session, TRUE); -} - -void rakia_media_session_terminate (RakiaMediaSession *session) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - - DEBUG ("enter"); - - if (priv->state == RAKIA_MEDIA_SESSION_STATE_ENDED) - return; - - /* XXX: taken care of by the state change? */ - priv_close_all_streams (session); - - if (priv->nua_op != NULL) - { - /* XXX: should the stack do pretty much the same - * (except freeing the saved event) upon nua_handle_destroy()? */ - switch (priv->state) - { - case RAKIA_MEDIA_SESSION_STATE_ACTIVE: - case RAKIA_MEDIA_SESSION_STATE_RESPONSE_RECEIVED: - case RAKIA_MEDIA_SESSION_STATE_REINVITE_SENT: - case RAKIA_MEDIA_SESSION_STATE_REINVITE_PENDING: - SESSION_DEBUG (session, "sending BYE"); - nua_bye (priv->nua_op, TAG_END()); - break; - case RAKIA_MEDIA_SESSION_STATE_INVITE_SENT: - SESSION_DEBUG (session, "sending CANCEL"); - nua_cancel (priv->nua_op, TAG_END()); - break; - case RAKIA_MEDIA_SESSION_STATE_INVITE_RECEIVED: - SESSION_DEBUG (session, "sending the 480 response to an incoming INVITE"); - nua_respond (priv->nua_op, 480, "Terminated", TAG_END()); - break; - case RAKIA_MEDIA_SESSION_STATE_REINVITE_RECEIVED: - if (priv->saved_event[0]) - { - SESSION_DEBUG (session, "sending the 480 response to an incoming re-INVITE"); - nua_respond (priv->nua_op, 480, "Terminated", - NUTAG_WITH(nua_saved_event_request (priv->saved_event)), - TAG_END()); - nua_destroy_event (priv->saved_event); - } - SESSION_DEBUG (session, "sending BYE to terminate the call itself"); - nua_bye (priv->nua_op, TAG_END()); - break; - default: - /* let the Sofia stack decide what do to */; - } - } - - rakia_media_session_change_state (session, RAKIA_MEDIA_SESSION_STATE_ENDED); -} - -gboolean -rakia_media_session_set_remote_media (RakiaMediaSession *session, - const sdp_session_t* sdp) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - gboolean authoritative; - - DEBUG ("enter"); - - if (priv->state == RAKIA_MEDIA_SESSION_STATE_INVITE_SENT - || priv->state == RAKIA_MEDIA_SESSION_STATE_REINVITE_SENT) - { - rakia_media_session_change_state ( - session, - RAKIA_MEDIA_SESSION_STATE_RESPONSE_RECEIVED); - } - else - { - /* Remember the m= line count in the remote offer, - * to match it with exactly this number of answer lines */ - sdp_media_t *media; - guint count = 0; - - for (media = sdp->sdp_media; media != NULL; media = media->m_next) - ++count; - - priv->remote_stream_count = count; - } - - /* Shortcut session non-updates */ - if (!sdp_session_cmp (priv->remote_sdp, sdp)) - goto finally; - - /* Delete a backup session structure, if any */ - if (priv->backup_remote_sdp != NULL) - { - priv->backup_remote_sdp = NULL; - g_assert (priv->backup_home != NULL); - su_home_unref (priv->backup_home); - priv->backup_home = NULL; - } - /* Back up the old session. - * The streams still need the old media descriptions */ - if (priv->remote_sdp != NULL) - { - g_assert (priv->home != NULL); - g_assert (priv->backup_home == NULL); - priv->backup_home = priv->home; - priv->backup_remote_sdp = priv->remote_sdp; - } - - /* Store the session description structure */ - priv->home = su_home_create (); - priv->remote_sdp = sdp_session_dup (priv->home, sdp); - g_return_val_if_fail (priv->remote_sdp != NULL, FALSE); - - authoritative = (priv->state == RAKIA_MEDIA_SESSION_STATE_INVITE_RECEIVED - || priv->state == RAKIA_MEDIA_SESSION_STATE_REINVITE_RECEIVED); - if (!priv_update_remote_media (session, authoritative)) - return FALSE; - -finally: - /* Make sure to always transition states and send out the response, - * even if no stream-engine roundtrips were initiated */ - priv_request_response_step (session); - return TRUE; -} - -void -priv_add_stream_list_entry (GPtrArray *list, - RakiaMediaStream *stream, - RakiaMediaSession *session) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - GValue entry = { 0 }; - GType stream_type; - guint id; - TpMediaStreamType type = TP_MEDIA_STREAM_TYPE_AUDIO; - TpMediaStreamState connection_state = TP_MEDIA_STREAM_STATE_CONNECTED; - TpMediaStreamDirection direction = TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL; - guint pending_send_flags = 0; - - g_assert(stream != NULL); - - g_object_get (stream, - "id", &id, - "media-type", &type, - "state", &connection_state, - "direction", &direction, - "pending-send-flags", &pending_send_flags, - NULL); - - stream_type = TP_STRUCT_TYPE_MEDIA_STREAM_INFO; - - g_value_init (&entry, stream_type); - g_value_take_boxed (&entry, - dbus_g_type_specialized_construct (stream_type)); - - dbus_g_type_struct_set (&entry, - 0, id, - 1, priv->peer, - 2, type, - 3, connection_state, - 4, direction, - 5, pending_send_flags, - G_MAXUINT); - - g_ptr_array_add (list, g_value_get_boxed (&entry)); -} - -gboolean rakia_media_session_request_streams (RakiaMediaSession *session, - const GArray *media_types, - GPtrArray *ret, - GError **error) -{ - guint i; - - DEBUG ("enter"); - - /* Validate the media types before creating any streams */ - for (i = 0; i < media_types->len; i++) { - guint media_type = g_array_index (media_types, guint, i); - if (!rakia_media_session_supports_media_type (media_type)) - { - g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "media type #%u is not supported", i); - return FALSE; - } - } - - for (i = 0; i < media_types->len; i++) { - guint media_type = g_array_index (media_types, guint, i); - RakiaMediaStream *stream; - - stream = rakia_media_session_add_stream (session, - media_type, - TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL, - TRUE); - - if (stream == NULL) - { - g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE, - "creation of stream %u failed", i); - /* XXX: should we close the streams already created as part of - * this request, despite having emitted signals about them? */ - return FALSE; - } - - priv_add_stream_list_entry (ret, stream, session); - } - - priv_local_media_changed (session); - - return TRUE; -} - -gboolean -rakia_media_session_remove_streams (RakiaMediaSession *self, - const GArray *stream_ids, - GError **error) -{ - RakiaMediaStream *stream; - guint stream_id; - guint i; - - DEBUG ("enter"); - - for (i = 0; i < stream_ids->len; i++) - { - stream_id = g_array_index (stream_ids, guint, i); - stream = rakia_media_session_get_stream (self, stream_id, error); - if (stream == NULL) - return FALSE; - rakia_media_stream_close (stream); - } - - priv_local_media_changed (self); - - return TRUE; -} - -void rakia_media_session_list_streams (RakiaMediaSession *session, - GPtrArray *ret) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - RakiaMediaStream *stream; - guint i; - - for (i = 0; i < priv->streams->len; i++) - { - stream = g_ptr_array_index(priv->streams, i); - if (stream) - priv_add_stream_list_entry (ret, stream, session); - } -} - -gboolean -rakia_media_session_request_stream_direction (RakiaMediaSession *self, - guint stream_id, - guint direction, - GError **error) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - RakiaMediaStream *stream; - - stream = rakia_media_session_get_stream (self, stream_id, error); - if (stream == NULL) - { - g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "stream %u does not exist", stream_id); - return FALSE; - } - - SESSION_DEBUG (self, "direction %u requested for stream %u", - direction, stream_id); - - if (priv->state == RAKIA_MEDIA_SESSION_STATE_INVITE_RECEIVED - || priv->state == RAKIA_MEDIA_SESSION_STATE_REINVITE_RECEIVED) - { - /* While processing a session offer, we can only mask out direction - * requested by the remote peer */ - direction &= rakia_media_stream_get_requested_direction (stream); - } - - rakia_media_stream_set_direction (stream, - direction, - TP_MEDIA_STREAM_PENDING_REMOTE_SEND); - - return TRUE; -} - -static void -priv_save_event (RakiaMediaSession *self) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - RakiaBaseConnection *conn = NULL; - - priv_zap_event (self); - - g_object_get (priv->channel, "connection", &conn, NULL); - - g_return_if_fail (conn != NULL); - - rakia_base_connection_save_event (conn, priv->saved_event); - - g_object_unref (conn); - -#ifdef ENABLE_DEBUG - { - nua_event_data_t const *ev_data = nua_event_data (priv->saved_event); - g_assert (ev_data != NULL); - DEBUG("saved the last event: %s %hd %s", nua_event_name (ev_data->e_event), ev_data->e_status, ev_data->e_phrase); - } -#endif -} - -static void -priv_zap_event (RakiaMediaSession *self) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - - if (priv->saved_event[0]) - { - nua_event_data_t const *ev_data = nua_event_data (priv->saved_event); - g_assert (ev_data != NULL); - WARNING ("zapping unhandled saved event '%s'", nua_event_name (ev_data->e_event)); - nua_destroy_event (priv->saved_event); - } -} - -void -rakia_media_session_receive_invite (RakiaMediaSession *self) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - - g_return_if_fail (priv->state == RAKIA_MEDIA_SESSION_STATE_CREATED); - g_return_if_fail (priv->nua_op != NULL); - - priv->remote_initiated = TRUE; - - nua_respond (priv->nua_op, SIP_180_RINGING, TAG_END()); - - rakia_media_session_change_state (self, RAKIA_MEDIA_SESSION_STATE_INVITE_RECEIVED); -} - -void -rakia_media_session_receive_reinvite (RakiaMediaSession *self) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - - /* Check for permitted state transitions */ - switch (priv->state) - { - case RAKIA_MEDIA_SESSION_STATE_ACTIVE: - case RAKIA_MEDIA_SESSION_STATE_RESPONSE_RECEIVED: - break; - case RAKIA_MEDIA_SESSION_STATE_REINVITE_PENDING: - g_source_remove (priv->glare_timer_id); - break; - default: - g_return_if_reached (); - } - - priv_save_event (self); - - rakia_media_session_change_state (self, RAKIA_MEDIA_SESSION_STATE_REINVITE_RECEIVED); -} - -void -rakia_media_session_accept (RakiaMediaSession *self) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - - if (priv->accepted) - return; - - SESSION_DEBUG (self, "accepting the session"); - - priv->accepted = TRUE; - - /* Apply the pending send flags */ - priv_apply_streams_pending_direction (self, - TP_MEDIA_STREAM_PENDING_LOCAL_SEND | - TP_MEDIA_STREAM_PENDING_REMOTE_SEND); - - /* Will change session state to active when streams are ready */ - priv_request_response_step (self); - - /* Can play the DTMF dialstring if an audio stream is connected */ - if (priv->audio_connected) - g_signal_emit (self, signals[SIG_DTMF_READY], 0); -} - -void -rakia_media_session_respond (RakiaMediaSession *self, - gint status, - const char *message) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - - SESSION_DEBUG (self, "responding: %03d %s", status, message ? message : ""); - - if (message != NULL && !message[0]) - message = NULL; - - if (priv->nua_op) - nua_respond (priv->nua_op, status, message, TAG_END()); -} - -gboolean rakia_media_session_is_accepted (RakiaMediaSession *self) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - return priv->accepted; -} - -static gboolean -priv_glare_retry (gpointer session) -{ - RakiaMediaSession *self = session; - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - - SESSION_DEBUG (self, "glare resolution interval is over"); - - if (priv->state == RAKIA_MEDIA_SESSION_STATE_REINVITE_PENDING) - priv_session_invite (self, TRUE); - - /* Reap the timer */ - priv->glare_timer_id = 0; - return FALSE; -} - -void -rakia_media_session_resolve_glare (RakiaMediaSession *self) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - guint interval; - - if (priv->state != RAKIA_MEDIA_SESSION_STATE_REINVITE_SENT) - { - SESSION_DEBUG (self, "glare resolution triggered in unexpected state"); - return; - } - - /* - * Set the grace interval accordinlgly to RFC 3261 section 14.1: - * - * 1. If the UAC is the owner of the Call-ID of the dialog ID - * (meaning it generated the value), T has a randomly chosen value - * between 2.1 and 4 seconds in units of 10 ms. - * 2. If the UAC is not the owner of the Call-ID of the dialog ID, T - * has a randomly chosen value of between 0 and 2 seconds in units - * of 10 ms. - */ - if (priv->pending_offer) - interval = 0; /* cut short, we have new things to negotiate */ - else if (priv->remote_initiated) - interval = g_random_int_range (0, 200) * 10; - else - interval = g_random_int_range (210, 400) * 10; - - if (priv->glare_timer_id != 0) - g_source_remove (priv->glare_timer_id); - - priv->glare_timer_id = g_timeout_add (interval, priv_glare_retry, self); - - SESSION_DEBUG (self, "glare resolution interval %u msec", interval); - - rakia_media_session_change_state ( - self, RAKIA_MEDIA_SESSION_STATE_REINVITE_PENDING); -} - -static RakiaMediaStream * -rakia_media_session_get_stream (RakiaMediaSession *self, - guint stream_id, - GError **error) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - RakiaMediaStream *stream; - - g_assert (priv->streams != NULL); - - if (stream_id >= priv->streams->len) - { - g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "stream ID %u is invalid", stream_id); - return NULL; - } - - stream = g_ptr_array_index (priv->streams, stream_id); - - if (stream == NULL) - { - g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, - "stream %u does not exist", stream_id); - return NULL; - } - - return stream; -} - -TpLocalHoldState -rakia_media_session_get_hold_state (RakiaMediaSession *self) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - return priv->hold_state; -} - -static gboolean -rakia_media_session_is_local_hold_ongoing (RakiaMediaSession *self) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - return (priv->hold_state == TP_LOCAL_HOLD_STATE_HELD - || priv->hold_state == TP_LOCAL_HOLD_STATE_PENDING_HOLD); -} - -static void -priv_initiate_hold (RakiaMediaSession *self, - gboolean hold, - TpLocalHoldStateReason reason) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - gboolean stream_hold_requested = FALSE; - RakiaMediaStream *stream; - guint i; - - DEBUG("enter"); - - if (hold) - { - if (priv->hold_state == TP_LOCAL_HOLD_STATE_HELD - || priv->hold_state == TP_LOCAL_HOLD_STATE_PENDING_HOLD) - { - MESSAGE ("redundant hold request"); - return; - } - } - else - { - if (priv->hold_state == TP_LOCAL_HOLD_STATE_UNHELD - || priv->hold_state == TP_LOCAL_HOLD_STATE_PENDING_UNHOLD) - { - MESSAGE ("redundant unhold request"); - return; - } - } - - /* Emit the hold notification for every stream that needs it */ - for (i = 0; i < priv->streams->len; i++) - { - stream = g_ptr_array_index(priv->streams, i); - if (stream != NULL - && rakia_media_stream_request_hold_state (stream, hold)) - stream_hold_requested = TRUE; - } - - if (stream_hold_requested) - { - priv->hold_state = hold? TP_LOCAL_HOLD_STATE_PENDING_HOLD - : TP_LOCAL_HOLD_STATE_PENDING_UNHOLD; - } - else - { - /* There were no streams to flip, short cut to the final state */ - priv->hold_state = hold? TP_LOCAL_HOLD_STATE_HELD - : TP_LOCAL_HOLD_STATE_UNHELD; - } - priv->hold_reason = reason; - - tp_svc_channel_interface_hold_emit_hold_state_changed (priv->channel, - priv->hold_state, reason); -} - -static void -priv_finalize_hold (RakiaMediaSession *self) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - RakiaMediaStream *stream; - TpLocalHoldState final_hold_state; - guint hold_mask; - guint unhold_mask; - guint i; - gboolean held = FALSE; - - DEBUG("enter"); - - switch (priv->hold_state) - { - case TP_LOCAL_HOLD_STATE_PENDING_HOLD: - held = TRUE; - break; - case TP_LOCAL_HOLD_STATE_PENDING_UNHOLD: - held = FALSE; - break; - default: - /* Streams changed state without request, signal this to the client. - * All streams should have the same hold state at this point, - * so just query one of them for the current hold state */ - stream = NULL; - for (i = 0; i < priv->streams->len; i++) - { - stream = g_ptr_array_index(priv->streams, i); - if (stream != NULL) - break; - } - g_return_if_fail (stream != NULL); - - g_object_get (stream, "hold-state", &held, NULL); - } - - if (held) - { - final_hold_state = TP_LOCAL_HOLD_STATE_HELD; - hold_mask = TP_MEDIA_STREAM_DIRECTION_SEND; - unhold_mask = 0; - } - else - { - final_hold_state = TP_LOCAL_HOLD_STATE_UNHELD; - hold_mask = TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL; - unhold_mask = TP_MEDIA_STREAM_DIRECTION_RECEIVE; - } - - priv->hold_state = final_hold_state; - tp_svc_channel_interface_hold_emit_hold_state_changed (priv->channel, - final_hold_state, priv->hold_reason); - - /* Set stream directions accordingly to the achieved hold state */ - for (i = 0; i < priv->streams->len; i++) - { - stream = g_ptr_array_index(priv->streams, i); - if (stream != NULL) - { - guint direction = rakia_media_stream_get_requested_direction (stream); - direction &= hold_mask; - direction |= unhold_mask; - rakia_media_stream_set_direction (stream, - direction, - TP_MEDIA_STREAM_PENDING_REMOTE_SEND - | TP_MEDIA_STREAM_PENDING_LOCAL_SEND); - } - } -} - -void -rakia_media_session_request_hold (RakiaMediaSession *self, - gboolean hold) -{ - priv_initiate_hold (self, - hold, - TP_LOCAL_HOLD_STATE_REASON_REQUESTED); -} - -gboolean -rakia_media_session_has_media (RakiaMediaSession *self, - TpMediaStreamType type) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - RakiaMediaStream *stream; - guint i; - - for (i = 0; i < priv->streams->len; i++) - { - stream = g_ptr_array_index(priv->streams, i); - if (stream == NULL) - continue; - if (rakia_media_stream_get_media_type (stream) == type) - return TRUE; - } - - return FALSE; -} - -void -rakia_media_session_start_telephony_event (RakiaMediaSession *self, - guchar event) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - RakiaMediaStream *stream; - guint i; - - for (i = 0; i < priv->streams->len; i++) - { - stream = g_ptr_array_index(priv->streams, i); - if (stream == NULL) - continue; - if (rakia_media_stream_get_media_type (stream) - != TP_MEDIA_STREAM_TYPE_AUDIO) - continue; - - SESSION_DEBUG (self, "starting telephony event %u on stream %u", - (guint) event, i); - - rakia_media_stream_start_telephony_event (stream, event); - } -} - -void -rakia_media_session_stop_telephony_event (RakiaMediaSession *self) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - RakiaMediaStream *stream; - guint i; - - for (i = 0; i < priv->streams->len; i++) - { - stream = g_ptr_array_index(priv->streams, i); - if (stream == NULL) - continue; - if (rakia_media_stream_get_media_type (stream) - != TP_MEDIA_STREAM_TYPE_AUDIO) - continue; - - SESSION_DEBUG (self, "stopping the telephony event on stream %u", i); - - rakia_media_stream_stop_telephony_event (stream); - } -} - -gint -rakia_media_session_rate_native_transport (RakiaMediaSession *session, - const GValue *transport) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - gint result = 0; - gchar *address = NULL; - guint proto = TP_MEDIA_STREAM_BASE_PROTO_UDP; - - dbus_g_type_struct_get (transport, - 1, &address, - 3, &proto, - G_MAXUINT); - - g_assert (address != NULL); - - if (proto != TP_MEDIA_STREAM_BASE_PROTO_UDP) - result = -1; - /* XXX: this will not work properly when IPv6 support comes */ - else if (priv->local_ip_address != NULL - && strcmp (address, priv->local_ip_address) == 0) - result = 1; - - g_free (address); - - return result; -} - -static void -priv_session_set_streams_playing (RakiaMediaSession *session, gboolean playing) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - RakiaMediaStream *stream; - guint i; - - for (i = 0; i < priv->streams->len; i++) - { - stream = g_ptr_array_index(priv->streams, i); - if (stream != NULL) - rakia_media_stream_set_playing (stream, playing); - } -} - -static void -priv_local_media_changed (RakiaMediaSession *session) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - - switch (priv->state) - { - case RAKIA_MEDIA_SESSION_STATE_CREATED: - /* If all streams are ready, send an offer now */ - priv_request_response_step (session); - break; - case RAKIA_MEDIA_SESSION_STATE_INVITE_RECEIVED: - case RAKIA_MEDIA_SESSION_STATE_REINVITE_RECEIVED: - /* The changes to existing streams will be included in the - * eventual answer (FIXME: implement postponed direction changes, - * which are applied after the remote offer has been processed). - * Check, however, if there are new streams not present in the - * remote offer, that will need another offer-answer round */ - if (priv->remote_stream_count < priv->streams->len) - priv->pending_offer = TRUE; - break; - case RAKIA_MEDIA_SESSION_STATE_INVITE_SENT: - case RAKIA_MEDIA_SESSION_STATE_REINVITE_SENT: - case RAKIA_MEDIA_SESSION_STATE_RESPONSE_RECEIVED: - /* Cannot send another offer right now */ - priv->pending_offer = TRUE; - break; - case RAKIA_MEDIA_SESSION_STATE_ACTIVE: - /* Check if we are allowed to send re-INVITES */ - { - gboolean immutable_streams = FALSE; - g_object_get (priv->channel, - "immutable-streams", &immutable_streams, - NULL); - if (immutable_streams) { - SESSION_MESSAGE (session, "sending of a local media update disabled by parameter 'immutable-streams'"); - break; - } - } - /* Fall through to the next case */ - case RAKIA_MEDIA_SESSION_STATE_REINVITE_PENDING: - if (priv->local_non_ready == 0) - priv_session_invite (session, TRUE); - else - priv->pending_offer = TRUE; - break; - default: - g_assert_not_reached(); - } -} - -static void -priv_update_remote_hold (RakiaMediaSession *session) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - RakiaMediaStream *stream; - gboolean has_streams = FALSE; - gboolean remote_held = TRUE; - guint direction; - guint i; - - /* The call is remotely unheld if there's at least one sending stream */ - for (i = 0; i < priv->streams->len; i++) - { - stream = g_ptr_array_index(priv->streams, i); - if (stream != NULL) - { - direction = rakia_media_stream_get_requested_direction (stream); - - if ((direction & TP_MEDIA_STREAM_DIRECTION_SEND) != 0) - remote_held = FALSE; - - has_streams = TRUE; - } - } - - if (!has_streams) - return; - - SESSION_DEBUG (session, "is remotely %s", remote_held? "held" : "unheld"); - - if (remote_held) - rakia_media_channel_change_call_state (priv->channel, - priv->peer, - TP_CHANNEL_CALL_STATE_HELD, - 0); - else - rakia_media_channel_change_call_state (priv->channel, - priv->peer, - 0, - TP_CHANNEL_CALL_STATE_HELD); -} - -gchar * -rakia_sdp_get_string_attribute (const sdp_attribute_t *attrs, const char *name) -{ - sdp_attribute_t *attr; - - attr = sdp_attribute_find (attrs, name); - if (attr == NULL) - return NULL; - - return g_strdup (attr->a_value); -} - -static gboolean -priv_update_remote_media (RakiaMediaSession *session, gboolean authoritative) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - const sdp_session_t *sdp = priv->remote_sdp; - const sdp_media_t *media; - gboolean has_supported_media = FALSE; - guint direction_up_mask; - guint pending_send_mask; - guint i; - - g_return_val_if_fail (sdp != NULL, FALSE); - - /* Update the session-wide parameters - * before updating streams' media */ - - priv->remote_ptime = rakia_sdp_get_string_attribute ( - sdp->sdp_attributes, "ptime"); - priv->remote_max_ptime = rakia_sdp_get_string_attribute ( - sdp->sdp_attributes, "maxptime"); - - priv->rtcp_enabled = !rakia_sdp_rtcp_bandwidth_throttled ( - sdp->sdp_bandwidths); - - /* - * Do not allow: - * 1) an answer to bump up directions beyond what's been offered; - * 2) an offer to remove the local hold. - */ - if (authoritative) - direction_up_mask - = rakia_media_session_is_local_hold_ongoing (session) - ? TP_MEDIA_STREAM_DIRECTION_SEND - : TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL; - else - direction_up_mask = 0; - - /* A remote media requesting to enable sending would need local approval. - * Also, if there have been any local media updates pending a re-INVITE, - * keep or bump the pending remote send flag on the streams: it will - * be resolved in the next re-INVITE transaction */ - pending_send_mask = TP_MEDIA_STREAM_PENDING_LOCAL_SEND; - if (priv->pending_offer) - pending_send_mask |= TP_MEDIA_STREAM_PENDING_REMOTE_SEND; - - media = sdp->sdp_media; - - /* note: for each session, we maintain an ordered list of - * streams (SDP m-lines) which are matched 1:1 to - * the streams of the remote SDP */ - - for (i = 0; media != NULL; media = media->m_next, i++) - { - RakiaMediaStream *stream = NULL; - guint media_type; - - media_type = rakia_tp_media_type (media->m_type); - - if (i >= priv->streams->len) - stream = rakia_media_session_add_stream ( - session, - media_type, - rakia_media_stream_direction_from_remote_media (media), - FALSE); - else - stream = g_ptr_array_index(priv->streams, i); - - /* note: it is ok for the stream to be NULL (unsupported media type) */ - if (stream == NULL) - continue; - - SESSION_DEBUG (session, "setting remote SDP for stream %u", i); - - if (media->m_rejected) - { - SESSION_DEBUG (session, "the stream has been rejected, closing"); - } - else if (rakia_media_stream_get_media_type (stream) != media_type) - { - /* XXX: close this stream and create a new one in its place? */ - WARNING ("The peer has changed the media type, don't know what to do"); - } - else if (rakia_media_stream_set_remote_media (stream, - media, - direction_up_mask, - pending_send_mask)) - { - has_supported_media = TRUE; - continue; - } - - /* There have been problems with the stream update, kill the stream */ - rakia_media_stream_close (stream); - } - g_assert(media == NULL); - g_assert(i <= priv->streams->len); - g_assert(!authoritative || i == priv->remote_stream_count); - - if (i < priv->streams->len && !priv->pending_offer) - { - /* - * It's not defined what we should do if there are previously offered - * streams not accounted in the remote SDP, in violation of RFC 3264. - * Closing them off serves resource preservation and gives better - * clue to the client as to the real state of the session. - * Note that this situation is masked if any local media updates - * have been requested and are pending until the present remote session - * answer is received and applied. In such a case, we'll issue a new offer - * at the closest available time, with the "overhanging" stream entries - * intact. - */ - do - { - RakiaMediaStream *stream; - stream = g_ptr_array_index(priv->streams, i); - if (stream != NULL) - { - SESSION_MESSAGE (session, "closing a mismatched stream %u", i); - rakia_media_stream_close (stream); - } - } - while (++i < priv->streams->len); - } - - if (has_supported_media) - priv_update_remote_hold (session); - - DEBUG("exit"); - - return has_supported_media; -} - -static void -priv_session_rollback (RakiaMediaSession *session) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - - DEBUG("enter"); - - if (priv->remote_sdp != NULL) - { - priv->remote_sdp = NULL; - g_assert (priv->home != NULL); - su_home_unref (priv->home); - priv->home = NULL; - } - if (priv->backup_remote_sdp == NULL) - { - rakia_media_session_terminate (session); - return; - } - - /* restore remote SDP from the backup */ - priv->remote_sdp = priv->backup_remote_sdp; - g_assert (priv->backup_home != NULL); - priv->home = priv->backup_home; - priv->backup_remote_sdp = NULL; - priv->backup_home = NULL; - - priv_update_remote_media (session, FALSE); - - if (priv->saved_event[0]) - { - nua_respond (priv->nua_op, SIP_488_NOT_ACCEPTABLE, - NUTAG_WITH(nua_saved_event_request (priv->saved_event)), - TAG_END()); - nua_destroy_event (priv->saved_event); - } - else - { - nua_respond (priv->nua_op, SIP_488_NOT_ACCEPTABLE, - TAG_END()); - } - - rakia_media_session_change_state (session, RAKIA_MEDIA_SESSION_STATE_ACTIVE); -} - -static GString * -priv_session_generate_sdp (RakiaMediaSession *session, - gboolean authoritative) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - GString *user_sdp; - guint len; - guint i; - - g_return_val_if_fail (priv->local_non_ready == 0, NULL); - - user_sdp = g_string_new ("v=0\r\n"); - - len = priv->streams->len; - if (!authoritative && len > priv->remote_stream_count) - { - len = priv->remote_stream_count; - SESSION_DEBUG (session, "clamped response to %u streams seen in the offer", len); - } - - for (i = 0; i < len; i++) - { - RakiaMediaStream *stream = g_ptr_array_index (priv->streams, i); - if (stream) - rakia_media_stream_generate_sdp (stream, user_sdp); - else - g_string_append (user_sdp, "m=audio 0 RTP/AVP 0\r\n"); - } - - return user_sdp; -} - -static void -priv_session_invite (RakiaMediaSession *session, gboolean reinvite) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - GString *user_sdp; - - DEBUG("enter"); - - g_return_if_fail (priv->nua_op != NULL); - - user_sdp = priv_session_generate_sdp (session, TRUE); - - g_return_if_fail (user_sdp != NULL); - - if (!reinvite - || priv->state == RAKIA_MEDIA_SESSION_STATE_REINVITE_PENDING - || tp_strdiff (priv->local_sdp, user_sdp->str)) - { - g_free (priv->local_sdp); - priv->local_sdp = g_string_free (user_sdp, FALSE); - - /* We need to be prepared to receive media right after the - * offer is sent, so we must set the streams to playing */ - priv_session_set_streams_playing (session, TRUE); - - nua_invite (priv->nua_op, - SOATAG_USER_SDP_STR(priv->local_sdp), - SOATAG_RTP_SORT(SOA_RTP_SORT_REMOTE), - SOATAG_RTP_SELECT(SOA_RTP_SELECT_ALL), - NUTAG_AUTOANSWER(0), - TAG_IF(reinvite, - NUTAG_INVITE_TIMER (RAKIA_REINVITE_TIMEOUT)), - TAG_END()); - priv->pending_offer = FALSE; - - rakia_media_session_change_state ( - session, - reinvite? RAKIA_MEDIA_SESSION_STATE_REINVITE_SENT - : RAKIA_MEDIA_SESSION_STATE_INVITE_SENT); - } - else - { - SESSION_DEBUG (session, "SDP unchanged, not sending a re-INVITE"); - g_string_free (user_sdp, TRUE); - } -} - -static void -priv_session_respond (RakiaMediaSession *session) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - msg_t *msg; - - g_return_if_fail (priv->nua_op != NULL); - - { - GString *user_sdp = priv_session_generate_sdp (session, FALSE); - - g_free (priv->local_sdp); - priv->local_sdp = g_string_free (user_sdp, FALSE); - } - - /* We need to be prepared to receive media right after the - * answer is sent, so we must set the streams to playing */ - priv_session_set_streams_playing (session, TRUE); - - msg = (priv->saved_event[0]) - ? nua_saved_event_request (priv->saved_event) : NULL; - - nua_respond (priv->nua_op, SIP_200_OK, - TAG_IF(msg, NUTAG_WITH(msg)), - SOATAG_USER_SDP_STR(priv->local_sdp), - SOATAG_RTP_SORT(SOA_RTP_SORT_REMOTE), - SOATAG_RTP_SELECT(SOA_RTP_SELECT_ALL), - NUTAG_AUTOANSWER(0), - TAG_END()); - - if (priv->saved_event[0]) - nua_destroy_event (priv->saved_event); - - rakia_media_session_change_state (session, RAKIA_MEDIA_SESSION_STATE_ACTIVE); -} - -static gboolean -priv_is_codec_intersect_pending (RakiaMediaSession *session) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - guint i; - - for (i = 0; i < priv->streams->len; i++) - { - RakiaMediaStream *stream = g_ptr_array_index (priv->streams, i); - if (stream != NULL - && rakia_media_stream_is_codec_intersect_pending (stream)) - return TRUE; - } - - return FALSE; -} - -/** - * Sends requests and responses with an outbound offer/answer - * if all streams of the session are prepared. - * - * Following inputs are considered in decision making: - * - state of the session (is remote INVITE being handled) - * - status of local streams (set up with stream-engine) - * - whether session is locally accepted - */ -static void -priv_request_response_step (RakiaMediaSession *session) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - - if (priv->local_non_ready != 0) - { - SESSION_DEBUG (session, "there are local streams not ready, postponed"); - return; - } - - switch (priv->state) - { - case RAKIA_MEDIA_SESSION_STATE_CREATED: - priv_session_invite (session, FALSE); - break; - case RAKIA_MEDIA_SESSION_STATE_RESPONSE_RECEIVED: - if (priv->accepted - && !priv_is_codec_intersect_pending (session)) - rakia_media_session_change_state (session, - RAKIA_MEDIA_SESSION_STATE_ACTIVE); - break; - case RAKIA_MEDIA_SESSION_STATE_INVITE_RECEIVED: - /* TODO: if the call has not yet been accepted locally - * and the remote endpoint supports 100rel, send them - * an early session answer in a reliable 183 response */ - if (priv->accepted - && !priv_is_codec_intersect_pending (session)) - priv_session_respond (session); - break; - case RAKIA_MEDIA_SESSION_STATE_REINVITE_RECEIVED: - if (!priv_is_codec_intersect_pending (session)) - priv_session_respond (session); - break; - case RAKIA_MEDIA_SESSION_STATE_ACTIVE: - case RAKIA_MEDIA_SESSION_STATE_REINVITE_PENDING: - if (priv->pending_offer) - priv_session_invite (session, TRUE); - break; - default: - SESSION_DEBUG (session, "no action taken in the current state"); - } -} - -static void -priv_stream_close_cb (RakiaMediaStream *stream, - RakiaMediaSession *session) -{ - RakiaMediaSessionPrivate *priv; - guint id; - - DEBUG("enter"); - - priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - - id = rakia_media_stream_get_id (stream); - g_return_if_fail (g_ptr_array_index(priv->streams, id) == stream); - - if (!rakia_media_stream_is_local_ready (stream)) - { - g_assert (priv->local_non_ready > 0); - --priv->local_non_ready; - SESSION_DEBUG(session, "stream wasn't ready, decrement the local non ready counter to %d", priv->local_non_ready); - } - - g_object_unref (stream); - - g_ptr_array_index(priv->streams, id) = NULL; - - tp_svc_channel_type_streamed_media_emit_stream_removed (priv->channel, id); -} - -static void priv_stream_ready_cb (RakiaMediaStream *stream, - RakiaMediaSession *session) -{ - RakiaMediaSessionPrivate *priv; - - DEBUG ("enter"); - - priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - - g_assert (priv->local_non_ready > 0); - --priv->local_non_ready; - - priv_request_response_step (session); -} - -static void priv_stream_supported_codecs_cb (RakiaMediaStream *stream, - guint num_codecs, - RakiaMediaSession *session) -{ - RakiaMediaSessionPrivate *priv; - - priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - - g_assert (!rakia_media_stream_is_codec_intersect_pending (stream)); - - if (num_codecs == 0) - { - /* This remote media description got no codec intersection. */ - switch (priv->state) - { - case RAKIA_MEDIA_SESSION_STATE_RESPONSE_RECEIVED: - case RAKIA_MEDIA_SESSION_STATE_INVITE_RECEIVED: - SESSION_DEBUG (session, "no codec intersection, closing the stream"); - rakia_media_stream_close (stream); - break; - case RAKIA_MEDIA_SESSION_STATE_REINVITE_RECEIVED: - /* In this case, we have the stream negotiated already, - * and we don't want to close it just because the remote party - * offers a different set of codecs. - * Roll back the whole session to the previously negotiated state. */ - priv_session_rollback (session); - return; - case RAKIA_MEDIA_SESSION_STATE_ACTIVE: - /* We've most likely rolled back from - * RAKIA_MEDIA_SESSION_STATE_REINVITE_RECEIVED, - * but we may receive more than one empty codec intersection - * in the session, so we ignore the rest */ - return; - default: - g_assert_not_reached(); - } - } - - priv_request_response_step (session); -} - -static void -priv_stream_state_changed_cb (RakiaMediaStream *stream, - guint state, - RakiaMediaSession *session) -{ - RakiaMediaSessionPrivate *priv = session->priv; - - tp_svc_channel_type_streamed_media_emit_stream_state_changed( - priv->channel, - rakia_media_stream_get_id (stream), state); - - /* Check if DTMF can now be played */ - if (!priv->audio_connected - && state == TP_MEDIA_STREAM_STATE_CONNECTED - && rakia_media_stream_get_media_type (stream) - == TP_MEDIA_STREAM_TYPE_AUDIO) - { - priv->audio_connected = TRUE; - - if (priv->accepted) - g_signal_emit (session, signals[SIG_DTMF_READY], 0); - } -} - -static void -priv_stream_direction_changed_cb (RakiaMediaStream *stream, - guint direction, - guint pending_send_flags, - RakiaMediaChannel *channel) -{ - g_assert (RAKIA_IS_MEDIA_CHANNEL (channel)); - tp_svc_channel_type_streamed_media_emit_stream_direction_changed ( - channel, - rakia_media_stream_get_id (stream), direction, pending_send_flags); -} - -static void -priv_stream_hold_state_cb (RakiaMediaStream *stream, - GParamSpec *pspec, - RakiaMediaSession *session) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session); - gboolean hold; - guint i; - - /* Determine the hold state all streams shall come to */ - switch (priv->hold_state) - { - case TP_LOCAL_HOLD_STATE_PENDING_HOLD: - hold = TRUE; - break; - case TP_LOCAL_HOLD_STATE_PENDING_UNHOLD: - hold = FALSE; - break; - default: - SESSION_MESSAGE (session, "unexpected hold state change from a stream"); - - /* Try to follow the changes and report the resulting hold state */ - g_object_get (stream, "hold-state", &hold, NULL); - priv->hold_reason = TP_LOCAL_HOLD_STATE_REASON_NONE; - } - - /* Check if all streams have reached the desired hold state */ - for (i = 0; i < priv->streams->len; i++) - { - stream = g_ptr_array_index (priv->streams, i); - if (stream != NULL) - { - gboolean stream_held = FALSE; - g_object_get (stream, "hold-state", &stream_held, NULL); - if ((!stream_held) != (!hold)) - { - SESSION_DEBUG (session, "hold/unhold not complete yet"); - return; - } - } - } - - priv_finalize_hold (session); -} - -static void -priv_stream_unhold_failure_cb (RakiaMediaStream *stream, - RakiaMediaSession *session) -{ - priv_initiate_hold (session, - TRUE, - TP_LOCAL_HOLD_STATE_REASON_RESOURCE_NOT_AVAILABLE); -} - -RakiaMediaStream* -rakia_media_session_add_stream (RakiaMediaSession *self, - guint media_type, - TpMediaStreamDirection direction, - gboolean created_locally) -{ - RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self); - RakiaMediaStream *stream = NULL; - - DEBUG ("enter"); - - if (rakia_media_session_supports_media_type (media_type)) { - guint stream_id; - gchar *object_path; - guint pending_send_flags; - - stream_id = priv->streams->len; - object_path = g_strdup_printf ("%s/MediaStream%u", - priv->object_path, - stream_id); - pending_send_flags = created_locally - ? TP_MEDIA_STREAM_PENDING_REMOTE_SEND - : TP_MEDIA_STREAM_PENDING_LOCAL_SEND; - - if (!created_locally) - direction &= ~TP_MEDIA_STREAM_DIRECTION_SEND; - - if (rakia_media_session_is_local_hold_ongoing (self)) - direction &= ~TP_MEDIA_STREAM_DIRECTION_RECEIVE; - - stream = g_object_new (RAKIA_TYPE_MEDIA_STREAM, - "dbus-daemon", priv->dbus_daemon, - "media-session", self, - "media-type", media_type, - "object-path", object_path, - "id", stream_id, - "direction", direction, - "pending-send-flags", pending_send_flags, - "created-locally", created_locally, - NULL); - - g_free (object_path); - - g_signal_connect (stream, "close", - G_CALLBACK (priv_stream_close_cb), - self); - g_signal_connect (stream, "ready", - G_CALLBACK (priv_stream_ready_cb), - self); - g_signal_connect (stream, "supported-codecs", - G_CALLBACK (priv_stream_supported_codecs_cb), - self); - g_signal_connect (stream, "state-changed", - G_CALLBACK (priv_stream_state_changed_cb), - self); - g_signal_connect (stream, "direction-changed", - G_CALLBACK (priv_stream_direction_changed_cb), - priv->channel); - g_signal_connect_swapped (stream, "local-media-updated", - G_CALLBACK (priv_local_media_changed), - self); - g_signal_connect (stream, "notify::hold-state", - G_CALLBACK (priv_stream_hold_state_cb), - self); - g_signal_connect (stream, "unhold-failure", - G_CALLBACK (priv_stream_unhold_failure_cb), - self); - - g_assert (priv->local_non_ready >= 0); - ++priv->local_non_ready; - - if (priv->se_ready) - priv_emit_new_stream (self, stream); - - tp_svc_channel_type_streamed_media_emit_stream_added (priv->channel, - stream_id, - priv->peer, - media_type); - if (direction != TP_MEDIA_STREAM_DIRECTION_RECEIVE - || pending_send_flags != TP_MEDIA_STREAM_PENDING_LOCAL_SEND) - { - tp_svc_channel_type_streamed_media_emit_stream_direction_changed ( - priv->channel, - stream_id, - direction, - pending_send_flags); - } - } - - /* note: we add an entry even for unsupported media types */ - g_ptr_array_add (priv->streams, stream); - - DEBUG ("exit"); - - return stream; -} - -static void -session_handler_iface_init (gpointer g_iface, gpointer iface_data) -{ - TpSvcMediaSessionHandlerClass *klass = (TpSvcMediaSessionHandlerClass *)g_iface; - -#define IMPLEMENT(x) tp_svc_media_session_handler_implement_##x (\ - klass, (tp_svc_media_session_handler_##x##_impl) rakia_media_session_##x) - IMPLEMENT(error); - IMPLEMENT(ready); -#undef IMPLEMENT -} - -/* Checks if RTCP is not disabled with bandwidth modifiers - * as described in RFC 3556 */ -gboolean -rakia_sdp_rtcp_bandwidth_throttled (const sdp_bandwidth_t *b) -{ - const sdp_bandwidth_t *b_RS = NULL; - const sdp_bandwidth_t *b_RR = NULL; - - while (b != NULL) - { - if (b->b_modifier_name != NULL) - { - if (strcmp (b->b_modifier_name, "RS") == 0) - b_RS = b; - else if (strcmp (b->b_modifier_name, "RR") == 0) - b_RR = b; - } - b = b->b_next; - } - - return (b_RS != NULL && b_RS->b_value == 0 - && b_RR != NULL && b_RR->b_value == 0); -} |