summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikhail Zabaluev <mikhail.zabaluev@nokia.com>2011-09-05 11:01:37 +0300
committerMikhail Zabaluev <mikhail.zabaluev@nokia.com>2011-09-05 11:01:37 +0300
commit6acdc05cf49cd03a1ec8058eae5a4f2d043323e9 (patch)
treec50d90b0d852fd3686fe0944d20bef9dd338fbab
parentcf94024c49bc64ca31961cb5895249ce0dc57048 (diff)
parentcb9453be492f28cf7b12c53f10b639ee9c56ed0f (diff)
Merge branch 'master' into debug-lite
-rw-r--r--rakia/media-channel.c249
-rw-r--r--rakia/media-channel.h2
-rw-r--r--rakia/media-manager.c42
-rw-r--r--rakia/media-session.c214
-rw-r--r--rakia/media-session.h15
-rw-r--r--rakia/media-stream.c286
-rw-r--r--rakia/media-stream.h2
-rw-r--r--src/Makefile.am2
-rw-r--r--src/sip-connection.c2
-rw-r--r--src/telepathy-rakia.c2
-rw-r--r--tests/twisted/Makefile.am1
-rw-r--r--tests/twisted/constants.py3
-rw-r--r--tests/twisted/voip/dtmf.py160
-rw-r--r--tests/twisted/voip/outgoing-basics.py34
-rw-r--r--tests/twisted/voip/voip_test.py30
15 files changed, 722 insertions, 322 deletions
diff --git a/rakia/media-channel.c b/rakia/media-channel.c
index 57b1823..c463b9f 100644
--- a/rakia/media-channel.c
+++ b/rakia/media-channel.c
@@ -32,6 +32,7 @@
#include <telepathy-glib/channel-iface.h>
#include <telepathy-glib/dbus.h>
+#include <telepathy-glib/dtmf.h>
#include <telepathy-glib/errors.h>
#include <telepathy-glib/exportable-channel.h>
#include <telepathy-glib/interfaces.h>
@@ -50,6 +51,11 @@
TP_CHANNEL_CALL_STATE_QUEUED | \
TP_CHANNEL_CALL_STATE_IN_PROGRESS)
+/* DTMF dialstring playback durations in milliseconds */
+#define RAKIA_DTMF_TONE_DURATION 250
+#define RAKIA_DTMF_GAP_DURATION 100
+#define RAKIA_DTMF_PAUSE_DURATION 3000
+
static void event_target_init (gpointer, gpointer);
static void channel_iface_init (gpointer, gpointer);
static void media_signalling_iface_init (gpointer, gpointer);
@@ -58,6 +64,9 @@ static void dtmf_iface_init (gpointer, gpointer);
static void call_state_iface_init (gpointer, gpointer);
static void hold_iface_init (gpointer, gpointer);
+static void priv_session_dtmf_ready_cb (RakiaMediaSession *session,
+ RakiaMediaChannel *channel);
+
G_DEFINE_TYPE_WITH_CODE (RakiaMediaChannel, rakia_media_channel,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (RAKIA_TYPE_EVENT_TARGET, event_target_init);
@@ -109,6 +118,9 @@ enum
PROP_INITIAL_AUDIO,
PROP_INITIAL_VIDEO,
PROP_IMMUTABLE_STREAMS,
+ PROP_CURRENTLY_SENDING_TONES,
+ PROP_INITIAL_TONES,
+ PROP_DEFERRED_TONES,
/* Telepathy properties (see below too) */
PROP_NAT_TRAVERSAL,
PROP_STUN_SERVER,
@@ -143,8 +155,6 @@ static guint signals[NUM_SIGNALS] = { 0 };
/* private structure */
-typedef struct _RakiaMediaChannelPrivate RakiaMediaChannelPrivate;
-
struct _RakiaMediaChannelPrivate
{
RakiaBaseConnection *conn;
@@ -155,6 +165,9 @@ struct _RakiaMediaChannelPrivate
GHashTable *call_states;
gchar *stun_server;
guint stun_port;
+ TpDTMFPlayer *dtmf_player;
+ gchar *initial_tones;
+ gchar *deferred_tones;
gboolean initial_audio;
gboolean initial_video;
@@ -163,7 +176,7 @@ struct _RakiaMediaChannelPrivate
gboolean dispose_has_run;
};
-#define RAKIA_MEDIA_CHANNEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RAKIA_TYPE_MEDIA_CHANNEL, RakiaMediaChannelPrivate))
+#define RAKIA_MEDIA_CHANNEL_GET_PRIVATE(chan) ((chan)->priv)
/***********************************************************************
* Set: Gobject interface
@@ -172,7 +185,11 @@ struct _RakiaMediaChannelPrivate
static void
rakia_media_channel_init (RakiaMediaChannel *self)
{
- RakiaMediaChannelPrivate *priv = RAKIA_MEDIA_CHANNEL_GET_PRIVATE (self);
+ RakiaMediaChannelPrivate *priv =
+ G_TYPE_INSTANCE_GET_PRIVATE (self,
+ RAKIA_TYPE_MEDIA_CHANNEL, RakiaMediaChannelPrivate);
+
+ self->priv = priv;
/* allocate any data required by the object here */
priv->call_states = g_hash_table_new (NULL, NULL);
@@ -280,6 +297,12 @@ rakia_media_channel_class_init (RakiaMediaChannelClass *klass)
{ "ImmutableStreams", "immutable-streams", NULL },
{ NULL }
};
+ static TpDBusPropertiesMixinPropImpl dtmf_props[] = {
+ { "CurrentlySendingTones", "currently-sending-tones", NULL },
+ { "InitialTones", "initial-tones", NULL },
+ { "DeferredTones", "deferred-tones", NULL },
+ { NULL }
+ };
static TpDBusPropertiesMixinIfaceImpl prop_interfaces[] = {
{ TP_IFACE_CHANNEL,
@@ -292,6 +315,11 @@ rakia_media_channel_class_init (RakiaMediaChannelClass *klass)
NULL,
streamed_media_props,
},
+ { TP_IFACE_CHANNEL_INTERFACE_DTMF,
+ tp_dbus_properties_mixin_getter_gobject_properties,
+ NULL,
+ dtmf_props,
+ },
{ NULL }
};
@@ -395,6 +423,28 @@ rakia_media_channel_class_init (RakiaMediaChannelClass *klass)
g_object_class_install_property (object_class, PROP_IMMUTABLE_STREAMS,
param_spec);
+ param_spec = g_param_spec_boolean ("currently-sending-tones",
+ "Currently sending tones",
+ "True if the channel is currently sending DTMF tones",
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_CURRENTLY_SENDING_TONES,
+ param_spec);
+
+ param_spec = g_param_spec_string ("initial-tones", "Initial tones",
+ "The initial DTMF tones to send after audio stream(s) are established.",
+ NULL,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_INITIAL_TONES,
+ param_spec);
+
+ param_spec = g_param_spec_string ("deferred-tones", "Deferred tones",
+ "The DTMF tones deferred waiting for user input.",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property (object_class, PROP_DEFERRED_TONES,
+ param_spec);
+
signals[SIG_INCOMING_CALL] =
g_signal_new ("incoming-call",
G_OBJECT_CLASS_TYPE (klass),
@@ -513,6 +563,23 @@ rakia_media_channel_get_property (GObject *object,
case PROP_STUN_PORT:
g_value_set_uint (value, priv->stun_port);
break;
+ case PROP_CURRENTLY_SENDING_TONES:
+ g_value_set_boolean (value,
+ priv->dtmf_player != NULL
+ && tp_dtmf_player_is_active (priv->dtmf_player));
+ break;
+ case PROP_INITIAL_TONES:
+ if (priv->initial_tones == NULL)
+ g_value_set_static_string (value, "");
+ else
+ g_value_set_string (value, priv->initial_tones);
+ break;
+ case PROP_DEFERRED_TONES:
+ if (priv->deferred_tones == NULL)
+ g_value_set_static_string (value, "");
+ else
+ g_value_set_string (value, priv->deferred_tones);
+ break;
default:
/* Some properties live in the mixin */
{
@@ -618,6 +685,9 @@ rakia_media_channel_set_property (GObject *object,
/* Also expose as a legacy Telepathy property */
rakia_media_channel_set_tp_property (chan, value, pspec);
break;
+ case PROP_INITIAL_TONES:
+ priv->initial_tones = g_value_dup_string (value);
+ break;
default:
/* some properties live in the mixin */
if (rakia_media_channel_set_tp_property (chan, value, pspec))
@@ -645,6 +715,9 @@ rakia_media_channel_dispose (GObject *object)
if (!priv->closed)
rakia_media_channel_close (self);
+ if (priv->dtmf_player != NULL)
+ g_object_unref (priv->dtmf_player);
+
contact_handles = tp_base_connection_get_handles (
TP_BASE_CONNECTION (priv->conn), TP_HANDLE_TYPE_CONTACT);
@@ -667,6 +740,9 @@ rakia_media_channel_finalize (GObject *object)
g_hash_table_destroy (priv->call_states);
+ g_free (priv->initial_tones);
+ g_free (priv->deferred_tones);
+
g_free (priv->stun_server);
g_free (priv->object_path);
@@ -1655,6 +1731,11 @@ priv_create_session (RakiaMediaChannel *channel,
G_CALLBACK(priv_session_state_changed_cb),
channel,
0);
+ g_signal_connect_object (session,
+ "dtmf-ready",
+ G_CALLBACK (priv_session_dtmf_ready_cb),
+ channel,
+ 0);
priv->session = session;
@@ -1950,19 +2031,120 @@ rakia_media_channel_request_hold (TpSvcChannelInterfaceHold *iface,
}
static void
+dtmf_player_started_tone_cb (TpDTMFPlayer *dtmf_player,
+ guint event,
+ gpointer user_data)
+{
+ RakiaMediaChannel *self = user_data;
+ RakiaMediaChannelPrivate *priv = self->priv;
+
+ if (priv->session != NULL)
+ rakia_media_session_start_telephony_event (priv->session, event);
+}
+
+static void
+dtmf_player_stopped_tone_cb (TpDTMFPlayer *dtmf_player,
+ gpointer user_data)
+{
+ RakiaMediaChannel *self = user_data;
+ RakiaMediaChannelPrivate *priv = self->priv;
+
+ if (priv->session != NULL)
+ rakia_media_session_stop_telephony_event (priv->session);
+}
+
+static void
+dtmf_player_finished_cb (TpDTMFPlayer *dtmf_player,
+ gboolean cancelled,
+ gpointer user_data)
+{
+ RakiaMediaChannel *self = user_data;
+
+ tp_svc_channel_interface_dtmf_emit_stopped_tones (self, cancelled);
+}
+
+static void
+dtmf_player_tones_deferred_cb (TpDTMFPlayer *dtmf_player,
+ gchar *tones,
+ gpointer user_data)
+{
+ RakiaMediaChannel *self = user_data;
+ RakiaMediaChannelPrivate *priv = self->priv;
+
+ g_free (priv->deferred_tones);
+ priv->deferred_tones = g_strdup (tones);
+
+ tp_svc_channel_interface_dtmf_emit_tones_deferred (self, tones);
+}
+
+static void
+priv_ensure_dtmf_player (RakiaMediaChannel *self)
+{
+ RakiaMediaChannelPrivate *priv = self->priv;
+ if (priv->dtmf_player != NULL)
+ return;
+
+ priv->dtmf_player = tp_dtmf_player_new ();
+
+ g_signal_connect (priv->dtmf_player, "started-tone",
+ G_CALLBACK (dtmf_player_started_tone_cb), self);
+ g_signal_connect (priv->dtmf_player, "stopped-tone",
+ G_CALLBACK (dtmf_player_stopped_tone_cb), self);
+ g_signal_connect (priv->dtmf_player, "finished",
+ G_CALLBACK (dtmf_player_finished_cb), self);
+ g_signal_connect (priv->dtmf_player, "tones-deferred",
+ G_CALLBACK (dtmf_player_tones_deferred_cb), self);
+}
+
+static gboolean
+rakia_media_channel_send_dtmf_tones (RakiaMediaChannel *self,
+ const gchar *tones,
+ guint tone_duration,
+ GError **error)
+{
+ RakiaMediaChannelPrivate *priv = self->priv;
+
+ /* Perform sanity checks on the session */
+ if (priv->session == NULL)
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "the media session is not available, has the channel been closed?");
+ return FALSE;
+ }
+ if (!rakia_media_session_has_media (priv->session,
+ TP_MEDIA_STREAM_TYPE_AUDIO))
+ {
+ g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
+ "no audio streams are available");
+ return FALSE;
+ }
+
+ priv_ensure_dtmf_player (self);
+
+ if (!tp_dtmf_player_play (priv->dtmf_player, tones, tone_duration,
+ RAKIA_DTMF_GAP_DURATION, RAKIA_DTMF_PAUSE_DURATION, error))
+ return FALSE;
+
+ g_free (priv->deferred_tones);
+ priv->deferred_tones = NULL;
+
+ tp_svc_channel_interface_dtmf_emit_sending_tones (self, tones);
+
+ return TRUE;
+}
+
+static void
rakia_media_channel_start_tone (TpSvcChannelInterfaceDTMF *iface,
- guint stream_id,
- guchar event,
- DBusGMethodInvocation *context)
+ guint stream_id,
+ guchar event,
+ DBusGMethodInvocation *context)
{
RakiaMediaChannel *self = RAKIA_MEDIA_CHANNEL (iface);
- RakiaMediaChannelPrivate *priv;
GError *error = NULL;
+ gchar tone[2];
DEBUG("enter");
- g_assert (RAKIA_IS_MEDIA_CHANNEL (self));
-
if (event >= NUM_TP_DTMF_EVENTS)
{
g_set_error (&error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT,
@@ -1972,12 +2154,10 @@ rakia_media_channel_start_tone (TpSvcChannelInterfaceDTMF *iface,
return;
}
- priv = RAKIA_MEDIA_CHANNEL_GET_PRIVATE (self);
+ tone[0] = tp_dtmf_event_to_char (event);
+ tone[1] = '\0';
- if (!rakia_media_session_start_telephony_event (priv->session,
- stream_id,
- event,
- &error))
+ if (!rakia_media_channel_send_dtmf_tones (self, tone, G_MAXUINT, &error))
{
dbus_g_method_return_error (context, error);
g_error_free (error);
@@ -1989,29 +2169,49 @@ rakia_media_channel_start_tone (TpSvcChannelInterfaceDTMF *iface,
static void
rakia_media_channel_stop_tone (TpSvcChannelInterfaceDTMF *iface,
- guint stream_id,
- DBusGMethodInvocation *context)
+ guint stream_id,
+ DBusGMethodInvocation *context)
{
RakiaMediaChannel *self = RAKIA_MEDIA_CHANNEL (iface);
RakiaMediaChannelPrivate *priv;
- GError *error = NULL;
DEBUG("enter");
- g_assert (RAKIA_IS_MEDIA_CHANNEL (self));
-
priv = RAKIA_MEDIA_CHANNEL_GET_PRIVATE (self);
- if (!rakia_media_session_stop_telephony_event (priv->session,
- stream_id,
- &error))
+ if (priv->dtmf_player != NULL)
+ tp_dtmf_player_cancel (priv->dtmf_player);
+
+ tp_svc_channel_interface_dtmf_return_from_stop_tone (context);
+}
+
+static void
+rakia_media_channel_multiple_tones (TpSvcChannelInterfaceDTMF *iface,
+ const gchar *tones,
+ DBusGMethodInvocation *context)
+{
+ RakiaMediaChannel *self = (RakiaMediaChannel *) iface;
+ GError *error = NULL;
+
+ if (!rakia_media_channel_send_dtmf_tones (self, tones,
+ RAKIA_DTMF_TONE_DURATION, &error))
{
dbus_g_method_return_error (context, error);
g_error_free (error);
return;
}
- tp_svc_channel_interface_dtmf_return_from_stop_tone (context);
+ tp_svc_channel_interface_dtmf_return_from_multiple_tones (context);
+}
+
+static void
+priv_session_dtmf_ready_cb (RakiaMediaSession *session,
+ RakiaMediaChannel *channel)
+{
+ RakiaMediaChannelPrivate *priv = channel->priv;
+ if (!tp_str_empty (priv->initial_tones))
+ rakia_media_channel_send_dtmf_tones (channel, priv->initial_tones,
+ RAKIA_DTMF_TONE_DURATION, NULL);
}
static void
@@ -2068,6 +2268,7 @@ dtmf_iface_init (gpointer g_iface, gpointer iface_data)
klass, rakia_media_channel_##x)
IMPLEMENT(start_tone);
IMPLEMENT(stop_tone);
+ IMPLEMENT(multiple_tones);
#undef IMPLEMENT
}
diff --git a/rakia/media-channel.h b/rakia/media-channel.h
index 6407832..cbd42ec 100644
--- a/rakia/media-channel.h
+++ b/rakia/media-channel.h
@@ -35,6 +35,7 @@ G_BEGIN_DECLS
typedef struct _RakiaMediaChannel RakiaMediaChannel;
typedef struct _RakiaMediaChannelClass RakiaMediaChannelClass;
+typedef struct _RakiaMediaChannelPrivate RakiaMediaChannelPrivate;
struct _RakiaMediaChannelClass {
GObjectClass parent_class;
@@ -47,6 +48,7 @@ struct _RakiaMediaChannel {
GObject parent;
TpGroupMixin group;
TpPropertiesMixin properties;
+ RakiaMediaChannelPrivate *priv;
};
GType rakia_media_channel_get_type(void);
diff --git a/rakia/media-manager.c b/rakia/media-manager.c
index 230ba3a..92fb738 100644
--- a/rakia/media-manager.c
+++ b/rakia/media-manager.c
@@ -37,11 +37,6 @@
#define DEBUG_FLAG RAKIA_DEBUG_CONNECTION
#include "rakia/debug.h"
-typedef enum {
- RAKIA_MEDIA_CHANNEL_CREATE_WITH_AUDIO = 1 << 0,
- RAKIA_MEDIA_CHANNEL_CREATE_WITH_VIDEO = 1 << 1,
-} RakiaMediaChannelCreationFlags;
-
static void channel_manager_iface_init (gpointer, gpointer);
static void rakia_media_manager_constructed (GObject *object);
static void rakia_media_manager_close_all (RakiaMediaManager *fac);
@@ -264,15 +259,16 @@ static RakiaMediaChannel *
new_media_channel (RakiaMediaManager *fac,
TpHandle initiator,
TpHandle maybe_peer,
- RakiaMediaChannelCreationFlags flags)
+ GHashTable *request_properties)
{
RakiaMediaManagerPrivate *priv;
RakiaMediaChannel *chan = NULL;
gchar *object_path;
const gchar *nat_traversal = "none";
- gboolean initial_audio;
- gboolean initial_video;
+ gboolean initial_audio = FALSE;
+ gboolean initial_video = FALSE;
gboolean immutable_streams = FALSE;
+ const gchar *dtmf_initial_tones = NULL;
g_assert (initiator != 0);
@@ -283,8 +279,15 @@ new_media_channel (RakiaMediaManager *fac,
DEBUG("channel object path %s", object_path);
- initial_audio = ((flags & RAKIA_MEDIA_CHANNEL_CREATE_WITH_AUDIO) != 0);
- initial_video = ((flags & RAKIA_MEDIA_CHANNEL_CREATE_WITH_VIDEO) != 0);
+ if (request_properties != NULL)
+ {
+ initial_audio = tp_asv_get_boolean (request_properties,
+ TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio", NULL);
+ initial_video = tp_asv_get_boolean (request_properties,
+ TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialVideo", NULL);
+ dtmf_initial_tones = tp_asv_get_string (request_properties,
+ TP_IFACE_CHANNEL_INTERFACE_DTMF ".InitialTones");
+ }
g_object_get (priv->conn,
"immutable-streams", &immutable_streams,
@@ -304,6 +307,7 @@ new_media_channel (RakiaMediaManager *fac,
"initial-video", initial_video,
"immutable-streams", immutable_streams,
"nat-traversal", nat_traversal,
+ "initial-tones", dtmf_initial_tones,
NULL);
g_free (object_path);
@@ -340,7 +344,6 @@ rakia_nua_i_invite_cb (TpBaseConnection *conn,
{
RakiaMediaChannel *channel;
TpHandle handle;
- guint channel_flags = 0;
/* figure out a handle for the identity */
@@ -362,7 +365,7 @@ rakia_nua_i_invite_cb (TpBaseConnection *conn,
return TRUE;
}
- channel = new_media_channel (fac, handle, handle, channel_flags);
+ channel = new_media_channel (fac, handle, handle, NULL);
rakia_handle_unref (conn, handle);
@@ -453,6 +456,7 @@ static const gchar * const named_channel_allowed_properties[] = {
TP_IFACE_CHANNEL ".TargetID",
TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio",
TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialVideo",
+ TP_IFACE_CHANNEL_INTERFACE_DTMF ".InitialTones",
NULL
};
@@ -507,7 +511,6 @@ rakia_media_manager_requestotron (TpChannelManager *manager,
RakiaMediaChannel *channel = NULL;
GError *error = NULL;
GSList *request_tokens;
- guint chan_flags = 0;
gboolean require_target_handle;
gboolean add_peer_to_remote_pending;
@@ -572,7 +575,7 @@ rakia_media_manager_requestotron (TpChannelManager *manager,
&error))
goto error;
- channel = new_media_channel (self, conn->self_handle, 0, 0);
+ channel = new_media_channel (self, conn->self_handle, 0, NULL);
break;
case TP_HANDLE_TYPE_CONTACT:
@@ -613,15 +616,8 @@ rakia_media_manager_requestotron (TpChannelManager *manager,
}
}
- if (tp_asv_get_boolean (request_properties,
- TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio", NULL))
- chan_flags |= RAKIA_MEDIA_CHANNEL_CREATE_WITH_AUDIO;
-
- if (tp_asv_get_boolean (request_properties,
- TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialVideo", NULL))
- chan_flags |= RAKIA_MEDIA_CHANNEL_CREATE_WITH_VIDEO;
-
- channel = new_media_channel (self, conn->self_handle, handle, chan_flags);
+ channel = new_media_channel (self, conn->self_handle, handle,
+ request_properties);
if (add_peer_to_remote_pending)
{
diff --git a/rakia/media-session.c b/rakia/media-session.c
index 10e5e44..a78b3da 100644
--- a/rakia/media-session.c
+++ b/rakia/media-session.c
@@ -70,6 +70,7 @@ G_DEFINE_TYPE_WITH_CODE(RakiaMediaSession,
enum
{
SIG_STATE_CHANGED,
+ SIG_DTMF_READY,
SIG_LAST_SIGNAL
};
@@ -154,10 +155,12 @@ struct _RakiaMediaSessionPrivate
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;
};
@@ -188,7 +191,7 @@ static void priv_zap_event (RakiaMediaSession *self);
static void rakia_media_session_init (RakiaMediaSession *self)
{
- RakiaMediaSessionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
+ RakiaMediaSessionPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
RAKIA_TYPE_MEDIA_SESSION, RakiaMediaSessionPrivate);
self->priv = priv;
@@ -453,6 +456,14 @@ rakia_media_session_class_init (RakiaMediaSessionClass *klass)
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
@@ -508,6 +519,7 @@ rakia_media_session_finalize (GObject *object)
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);
@@ -1105,6 +1117,10 @@ rakia_media_session_accept (RakiaMediaSession *self)
/* 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
@@ -1367,54 +1383,68 @@ rakia_media_session_request_hold (RakiaMediaSession *self,
}
gboolean
-rakia_media_session_start_telephony_event (RakiaMediaSession *self,
- guint stream_id,
- guchar event,
- GError **error)
+rakia_media_session_has_media (RakiaMediaSession *self,
+ TpMediaStreamType type)
{
+ RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self);
RakiaMediaStream *stream;
+ guint i;
- stream = rakia_media_session_get_stream (self, stream_id, error);
- if (stream == NULL)
- return FALSE;
-
- if (rakia_media_stream_get_media_type (stream) != TP_MEDIA_STREAM_TYPE_AUDIO)
+ for (i = 0; i < priv->streams->len; i++)
{
- g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
- "non-audio stream %u does not support telephony events", stream_id);
- return FALSE;
+ stream = g_ptr_array_index(priv->streams, i);
+ if (stream == NULL)
+ continue;
+ if (rakia_media_stream_get_media_type (stream) == type)
+ return TRUE;
}
- DEBUG("starting telephony event %u on stream %u", (guint) event, stream_id);
-
- rakia_media_stream_start_telephony_event (stream, event);
-
- return TRUE;
+ return FALSE;
}
-gboolean
-rakia_media_session_stop_telephony_event (RakiaMediaSession *self,
- guint stream_id,
- GError **error)
+void
+rakia_media_session_start_telephony_event (RakiaMediaSession *self,
+ guchar event)
{
+ RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (self);
RakiaMediaStream *stream;
+ guint i;
- stream = rakia_media_session_get_stream (self, stream_id, error);
- if (stream == NULL)
- return FALSE;
-
- if (rakia_media_stream_get_media_type (stream) != TP_MEDIA_STREAM_TYPE_AUDIO)
+ for (i = 0; i < priv->streams->len; i++)
{
- g_set_error (error, TP_ERRORS, TP_ERROR_NOT_AVAILABLE,
- "non-audio stream %u does not support telephony events; spurious use of the stop event?", stream_id);
- return FALSE;
+ 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;
+
+ DEBUG("starting telephony event %u on stream %u", (guint) event, i);
+
+ rakia_media_stream_start_telephony_event (stream, event);
}
+}
- DEBUG("stopping the telephony event on stream %u", stream_id);
+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;
- rakia_media_stream_stop_telephony_event (stream);
+ DEBUG("stopping the telephony event on stream %u", i);
- return TRUE;
+ rakia_media_stream_stop_telephony_event (stream);
+ }
}
gint
@@ -1742,17 +1772,18 @@ priv_session_rollback (RakiaMediaSession *session)
rakia_media_session_change_state (session, RAKIA_MEDIA_SESSION_STATE_ACTIVE);
}
-static gboolean
-priv_session_local_sdp (RakiaMediaSession *session,
- GString *user_sdp,
- gboolean authoritative)
+static GString *
+priv_session_generate_sdp (RakiaMediaSession *session,
+ gboolean authoritative)
{
RakiaMediaSessionPrivate *priv = RAKIA_MEDIA_SESSION_GET_PRIVATE (session);
- gboolean has_supported_media = FALSE;
+ GString *user_sdp;
guint len;
guint i;
- g_return_val_if_fail (priv->local_non_ready == 0, FALSE);
+ 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)
@@ -1761,24 +1792,16 @@ priv_session_local_sdp (RakiaMediaSession *session,
DEBUG("clamped response to %u streams seen in the offer", len);
}
- g_string_append (user_sdp, "v=0\r\n");
-
for (i = 0; i < len; i++)
{
RakiaMediaStream *stream = g_ptr_array_index (priv->streams, i);
if (stream)
- {
- user_sdp = g_string_append (user_sdp,
- rakia_media_stream_local_sdp (stream));
- has_supported_media = TRUE;
- }
+ rakia_media_stream_generate_sdp (stream, user_sdp);
else
- {
- user_sdp = g_string_append (user_sdp, "m=audio 0 RTP/AVP 0\r\n");
- }
+ g_string_append (user_sdp, "m=audio 0 RTP/AVP 0\r\n");
}
- return has_supported_media;
+ return user_sdp;
}
static void
@@ -1791,16 +1814,23 @@ priv_session_invite (RakiaMediaSession *session, gboolean reinvite)
g_return_if_fail (priv->nua_op != NULL);
- user_sdp = g_string_new (NULL);
+ user_sdp = priv_session_generate_sdp (session, TRUE);
+
+ g_return_if_fail (user_sdp != NULL);
- if (priv_session_local_sdp (session, user_sdp, TRUE))
+ 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(user_sdp->str),
+ SOATAG_USER_SDP_STR(priv->local_sdp),
SOATAG_RTP_SORT(SOA_RTP_SORT_REMOTE),
SOATAG_RTP_SELECT(SOA_RTP_SELECT_ALL),
NUTAG_AUTOANSWER(0),
@@ -1815,53 +1845,46 @@ priv_session_invite (RakiaMediaSession *session, gboolean reinvite)
: RAKIA_MEDIA_SESSION_STATE_INVITE_SENT);
}
else
- WARNING ("cannot send a valid SDP offer, are there no streams?");
-
- g_string_free (user_sdp, TRUE);
+ {
+ DEBUG("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);
- GString *user_sdp;
+ msg_t *msg;
g_return_if_fail (priv->nua_op != NULL);
- user_sdp = g_string_new (NULL);
-
- if (priv_session_local_sdp (session, user_sdp, FALSE))
- {
- msg_t *msg;
+ {
+ GString *user_sdp = priv_session_generate_sdp (session, 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);
+ g_free (priv->local_sdp);
+ priv->local_sdp = g_string_free (user_sdp, FALSE);
+ }
- msg = (priv->saved_event[0])
- ? nua_saved_event_request (priv->saved_event) : NULL;
+ /* 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);
- nua_respond (priv->nua_op, SIP_200_OK,
- TAG_IF(msg, NUTAG_WITH(msg)),
- SOATAG_USER_SDP_STR (user_sdp->str),
- SOATAG_RTP_SORT(SOA_RTP_SORT_REMOTE),
- SOATAG_RTP_SELECT(SOA_RTP_SELECT_ALL),
- NUTAG_AUTOANSWER(0),
- TAG_END());
+ msg = (priv->saved_event[0])
+ ? nua_saved_event_request (priv->saved_event) : NULL;
- if (priv->saved_event[0])
- nua_destroy_event (priv->saved_event);
+ 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());
- rakia_media_session_change_state (session, RAKIA_MEDIA_SESSION_STATE_ACTIVE);
- }
- else
- {
- WARNING ("cannot respond with a valid SDP answer, were all streams closed?");
-
- priv_session_rollback (session);
- }
+ if (priv->saved_event[0])
+ nua_destroy_event (priv->saved_event);
- g_string_free (user_sdp, TRUE);
+ rakia_media_session_change_state (session, RAKIA_MEDIA_SESSION_STATE_ACTIVE);
}
static gboolean
@@ -2021,12 +2044,25 @@ static void priv_stream_supported_codecs_cb (RakiaMediaStream *stream,
static void
priv_stream_state_changed_cb (RakiaMediaStream *stream,
guint state,
- RakiaMediaChannel *channel)
+ RakiaMediaSession *session)
{
- g_assert (RAKIA_IS_MEDIA_CHANNEL (channel));
+ RakiaMediaSessionPrivate *priv = session->priv;
+
tp_svc_channel_type_streamed_media_emit_stream_state_changed(
- channel,
+ 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
@@ -2149,7 +2185,7 @@ rakia_media_session_add_stream (RakiaMediaSession *self,
self);
g_signal_connect (stream, "state-changed",
G_CALLBACK (priv_stream_state_changed_cb),
- priv->channel);
+ self);
g_signal_connect (stream, "direction-changed",
G_CALLBACK (priv_stream_direction_changed_cb),
priv->channel);
diff --git a/rakia/media-session.h b/rakia/media-session.h
index 030887f..6fc52a1 100644
--- a/rakia/media-session.h
+++ b/rakia/media-session.h
@@ -113,13 +113,12 @@ TpLocalHoldState rakia_media_session_get_hold_state (RakiaMediaSession *session)
void rakia_media_session_request_hold (RakiaMediaSession *session,
gboolean hold);
-gboolean rakia_media_session_start_telephony_event (RakiaMediaSession *self,
- guint stream_id,
- guchar event,
- GError **error);
-gboolean rakia_media_session_stop_telephony_event (RakiaMediaSession *self,
- guint stream_id,
- GError **error);
+gboolean rakia_media_session_has_media (RakiaMediaSession *self,
+ TpMediaStreamType type);
+
+void rakia_media_session_start_telephony_event (RakiaMediaSession *self,
+ guchar event);
+void rakia_media_session_stop_telephony_event (RakiaMediaSession *self);
gint rakia_media_session_rate_native_transport (RakiaMediaSession *session,
const GValue *transport);
@@ -129,4 +128,6 @@ gboolean rakia_sdp_rtcp_bandwidth_throttled (const sdp_bandwidth_t *b);
gchar * rakia_sdp_get_string_attribute (const sdp_attribute_t *attrs,
const char *name);
+G_END_DECLS
+
#endif /* #ifndef __RAKIA_MEDIA_SESSION_H__*/
diff --git a/rakia/media-stream.c b/rakia/media-stream.c
index 002c265..a8cbf2c 100644
--- a/rakia/media-stream.c
+++ b/rakia/media-stream.c
@@ -135,8 +135,6 @@ struct _RakiaMediaStreamPrivate
gboolean hold_state; /* see gobj. prop. 'hold-state' */
gboolean created_locally; /* see gobj. prop. 'created-locally' */
- gchar *stream_sdp; /* SDP description of the stream */
-
GValue native_codecs; /* intersected codec list */
GValue native_candidates;
@@ -168,8 +166,10 @@ static void push_remote_candidates (RakiaMediaStream *stream);
static void push_active_candidate_pair (RakiaMediaStream *stream);
static void priv_update_sending (RakiaMediaStream *stream,
TpMediaStreamDirection direction);
-static void priv_update_local_sdp(RakiaMediaStream *stream);
-static void priv_generate_sdp (RakiaMediaStream *stream);
+static void priv_emit_local_ready (RakiaMediaStream *stream);
+static const char *priv_get_preferred_native_candidate (
+ RakiaMediaStreamPrivate *priv,
+ const GPtrArray **transports);
/***********************************************************************
* Set: Gobject interface
@@ -178,15 +178,11 @@ static void priv_generate_sdp (RakiaMediaStream *stream);
static void
rakia_media_stream_init (RakiaMediaStream *self)
{
- RakiaMediaStreamPrivate *priv =
- G_TYPE_INSTANCE_GET_PRIVATE ((self),
- RAKIA_TYPE_MEDIA_STREAM, RakiaMediaStreamPrivate);
+ RakiaMediaStreamPrivate *priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
+ RAKIA_TYPE_MEDIA_STREAM, RakiaMediaStreamPrivate);
self->priv = priv;
- priv->playing = FALSE;
- priv->sending = FALSE;
-
g_value_init (&priv->native_codecs, TP_ARRAY_TYPE_MEDIA_STREAM_HANDLER_CODEC_LIST);
g_value_take_boxed (&priv->native_codecs,
dbus_g_type_specialized_construct (TP_ARRAY_TYPE_MEDIA_STREAM_HANDLER_CODEC_LIST));
@@ -194,12 +190,6 @@ rakia_media_stream_init (RakiaMediaStream *self)
g_value_init (&priv->native_candidates, TP_ARRAY_TYPE_MEDIA_STREAM_HANDLER_CANDIDATE_LIST);
g_value_take_boxed (&priv->native_candidates,
dbus_g_type_specialized_construct (TP_ARRAY_TYPE_MEDIA_STREAM_HANDLER_CANDIDATE_LIST));
-
- priv->native_cands_prepared = FALSE;
- priv->native_codecs_prepared = FALSE;
-
- priv->push_remote_cands_pending = FALSE;
- priv->push_remote_codecs_pending = FALSE;
}
static void
@@ -552,7 +542,6 @@ rakia_media_stream_finalize (GObject *object)
/* free any data held directly by the object here */
g_free (priv->object_path);
- g_free (priv->stream_sdp);
g_value_unset (&priv->native_codecs);
g_value_unset (&priv->native_candidates);
@@ -607,7 +596,6 @@ rakia_media_stream_error (TpSvcMediaStreamHandler *iface,
STREAM_DEBUG (self, "StreamHandler.Error called: %u %s", errno, message);
-
rakia_media_stream_close (self);
tp_svc_media_stream_handler_return_from_error (context);
@@ -637,7 +625,7 @@ rakia_media_stream_native_candidates_prepared (TpSvcMediaStreamHandler *iface,
priv->native_cands_prepared = TRUE;
if (priv->native_codecs_prepared)
- priv_generate_sdp (obj);
+ priv_emit_local_ready (obj);
push_active_candidate_pair (obj);
@@ -653,9 +641,9 @@ rakia_media_stream_native_candidates_prepared (TpSvcMediaStreamHandler *iface,
*/
static void
rakia_media_stream_new_active_candidate_pair (TpSvcMediaStreamHandler *iface,
- const gchar *native_candidate_id,
- const gchar *remote_candidate_id,
- DBusGMethodInvocation *context)
+ const gchar *native_candidate_id,
+ const gchar *remote_candidate_id,
+ DBusGMethodInvocation *context)
{
RakiaMediaStream *self = RAKIA_MEDIA_STREAM (iface);
RakiaMediaStreamPrivate *priv = self->priv;
@@ -701,13 +689,6 @@ rakia_media_stream_new_native_candidate (TpSvcMediaStreamHandler *iface,
priv = RAKIA_MEDIA_STREAM_GET_PRIVATE (obj);
- if (priv->stream_sdp != NULL)
- {
- MESSAGE ("Stream %u: SDP already generated, ignoring native candidate '%s'", priv->id, candidate_id);
- tp_svc_media_stream_handler_return_from_new_native_candidate (context);
- return;
- }
-
g_return_if_fail (transports->len >= 1);
/* Rate the preferability of the address */
@@ -752,17 +733,14 @@ priv_set_local_codecs (RakiaMediaStream *self,
const GPtrArray *codecs)
{
RakiaMediaStreamPrivate *priv = RAKIA_MEDIA_STREAM_GET_PRIVATE (self);
- GValue val = { 0, };
STREAM_DEBUG(self, "putting list of %d locally supported codecs into cache",
codecs->len);
- g_value_init (&val, TP_ARRAY_TYPE_MEDIA_STREAM_HANDLER_CODEC_LIST);
- g_value_set_static_boxed (&val, codecs);
- g_value_copy (&val, &priv->native_codecs);
+ g_value_set_boxed (&priv->native_codecs, codecs);
priv->native_codecs_prepared = TRUE;
if (priv->native_cands_prepared)
- priv_generate_sdp (self);
+ priv_emit_local_ready (self);
}
static void
@@ -786,19 +764,12 @@ rakia_media_stream_codecs_updated (TpSvcMediaStreamHandler *iface,
}
else
{
- GValue val = { 0, };
-
STREAM_DEBUG (self, "putting list of %d locally supported "
"codecs from CodecsUpdated into cache", codecs->len);
- g_value_init (&val, TP_ARRAY_TYPE_MEDIA_STREAM_HANDLER_CODEC_LIST);
- g_value_set_static_boxed (&val, codecs);
- g_value_copy (&val, &priv->native_codecs);
+ g_value_set_boxed (&priv->native_codecs, codecs);
- /* This doesn't use priv_generate_sdp because it short-circuits if
- * priv->stream_sdp is already set. We want to update it.
- */
if (priv->native_cands_prepared)
- priv_update_local_sdp (self);
+ g_signal_emit (self, signals[SIG_LOCAL_MEDIA_UPDATED], 0);
tp_svc_media_stream_handler_return_from_codecs_updated (context);
}
@@ -848,10 +819,6 @@ rakia_media_stream_ready (TpSvcMediaStreamHandler *iface,
tp_svc_media_stream_handler_emit_set_stream_sending (
iface, priv->sending);
- priv->native_codecs_prepared = TRUE;
- if (priv->native_cands_prepared)
- priv_generate_sdp (obj);
-
if (priv->push_remote_cands_pending)
{
priv->push_remote_cands_pending = FALSE;
@@ -938,10 +905,10 @@ rakia_media_stream_supported_codecs (TpSvcMediaStreamHandler *iface,
"got codec intersection containing %u codecs from stream-engine",
codecs->len);
- /* Uncomment the line below if there's need to limit the local codec list
- * with the intersection for later SDP negotiations.
- * TODO: Make sure to update the SDP for the stream as well. */
- /* g_value_set_boxed (&priv->native_codecs, codecs); */
+ /* Save the local codecs, but avoid triggering a new
+ * session update at this point. If the stream engine have changed any codec
+ * parameters, it is supposed to follow up with CodecsUpdated. */
+ g_value_set_boxed (&priv->native_codecs, codecs);
if (priv->codec_intersect_pending)
{
@@ -1009,17 +976,6 @@ rakia_media_stream_close (RakiaMediaStream *self)
tp_svc_media_stream_handler_emit_close (self);
}
-/**
- * Described the local stream configuration in SDP (RFC2327),
- * or NULL if stream not configured yet.
- */
-const char *rakia_media_stream_local_sdp (RakiaMediaStream *obj)
-{
- RakiaMediaStreamPrivate *priv;
- priv = RAKIA_MEDIA_STREAM_GET_PRIVATE (obj);
- return priv->stream_sdp;
-}
-
TpMediaStreamDirection
rakia_media_stream_direction_from_remote_media (const sdp_media_t *media)
{
@@ -1342,7 +1298,7 @@ rakia_media_stream_set_direction (RakiaMediaStream *stream,
&& priv->native_codecs_prepared
&& priv_get_requested_direction (priv)
!= old_sdp_direction)
- priv_update_local_sdp (stream);
+ g_signal_emit (stream, signals[SIG_LOCAL_MEDIA_UPDATED], 0);
}
/*
@@ -1401,10 +1357,9 @@ rakia_media_stream_get_requested_direction (RakiaMediaStream *self)
*/
gboolean rakia_media_stream_is_local_ready (RakiaMediaStream *self)
{
- RakiaMediaStreamPrivate *priv;
- priv = RAKIA_MEDIA_STREAM_GET_PRIVATE (self);
- g_assert (priv->stream_sdp == NULL || priv->ready_received);
- return (priv->stream_sdp != NULL);
+ RakiaMediaStreamPrivate *priv = self->priv;
+ return (priv->ready_received && priv->native_cands_prepared
+ && priv->native_codecs_prepared);
}
gboolean
@@ -1443,17 +1398,10 @@ rakia_media_stream_request_hold_state (RakiaMediaStream *self, gboolean hold)
}
static void
-priv_generate_sdp (RakiaMediaStream *self)
+priv_emit_local_ready (RakiaMediaStream *self)
{
- RakiaMediaStreamPrivate *priv = RAKIA_MEDIA_STREAM_GET_PRIVATE (self);
-
- if (priv->stream_sdp != NULL)
- return;
-
- priv_update_local_sdp (self);
-
- g_assert (priv->stream_sdp != NULL);
-
+ /* Trigger any session updates that are due in the current session state */
+ g_signal_emit (self, signals[SIG_LOCAL_MEDIA_UPDATED], 0);
g_signal_emit (self, signals[SIG_READY], 0);
}
@@ -1720,20 +1668,18 @@ static void push_remote_candidates (RakiaMediaStream *stream)
static void
push_active_candidate_pair (RakiaMediaStream *stream)
{
- RakiaMediaStreamPrivate *priv;
-
- DEBUG("enter");
-
- priv = RAKIA_MEDIA_STREAM_GET_PRIVATE (stream);
+ RakiaMediaStreamPrivate *priv = stream->priv;
- if (priv->ready_received
- && priv->native_candidate_id != NULL
+ if (priv->ready_received && priv->native_cands_prepared
&& priv->remote_candidate_id != NULL)
{
+ const char *native_candidate_id;
+
+ native_candidate_id = priv_get_preferred_native_candidate (priv, NULL);
STREAM_DEBUG (stream, "emitting SetActiveCandidatePair for %s-%s",
- priv->native_candidate_id, priv->remote_candidate_id);
- tp_svc_media_stream_handler_emit_set_active_candidate_pair (
- stream, priv->native_candidate_id, priv->remote_candidate_id);
+ native_candidate_id, priv->remote_candidate_id);
+ tp_svc_media_stream_handler_emit_set_active_candidate_pair (stream,
+ native_candidate_id, priv->remote_candidate_id);
}
}
@@ -1815,31 +1761,16 @@ priv_append_rtpmaps (const GPtrArray *codecs, GString *mline, GString *alines)
* Refreshes the local SDP based on Farsight stream, and current
* object, state.
*/
-static void
-priv_update_local_sdp(RakiaMediaStream *stream)
+static const char *
+priv_get_preferred_native_candidate (RakiaMediaStreamPrivate *priv,
+ const GPtrArray **transports)
{
- RakiaMediaStreamPrivate *priv;
- GString *mline;
- GString *alines;
- gchar *cline;
- GValue transport = { 0 };
const GPtrArray *candidates;
- gchar *tr_addr = NULL;
- /* gchar *tr_user = NULL; */
- /* gchar *tr_pass = NULL; */
- gchar *tr_subtype = NULL;
- gchar *tr_profile = NULL;
- guint tr_port;
- guint tr_component;
- /* guint tr_type; */
- /* gdouble tr_pref; */
- guint rtcp_port = 0;
- gchar *rtcp_address = NULL;
- const gchar *dirline;
+ const gchar *candidate_id = NULL;
+ const GPtrArray *ca_tports = NULL;
+ GValue transport = { 0 };
int i;
- priv = RAKIA_MEDIA_STREAM_GET_PRIVATE (stream);
-
candidates = g_value_get_boxed (&priv->native_candidates);
g_value_init (&transport, TP_STRUCT_TYPE_MEDIA_STREAM_HANDLER_TRANSPORT);
@@ -1850,9 +1781,7 @@ priv_update_local_sdp(RakiaMediaStream *stream)
for (i = candidates->len - 1; i >= 0; --i)
{
GValueArray *candidate;
- const gchar *candidate_id;
- const GPtrArray *ca_tports;
- guint tr_proto = TP_MEDIA_STREAM_BASE_PROTO_UDP;
+ guint tr_proto = (guint) -1;
guint j;
candidate = g_ptr_array_index (candidates, i);
@@ -1868,33 +1797,18 @@ priv_update_local_sdp(RakiaMediaStream *stream)
for (j = 0; j < ca_tports->len; j++)
{
+ guint tr_component = 0;
+
g_value_set_static_boxed (&transport,
g_ptr_array_index (ca_tports, j));
+
+ /* Find the RTP component */
dbus_g_type_struct_get (&transport,
0, &tr_component,
+ 3, &tr_proto,
G_MAXUINT);
- switch (tr_component)
- {
- case 1: /* RTP */
- dbus_g_type_struct_get (&transport,
- 1, &tr_addr,
- 2, &tr_port,
- 3, &tr_proto,
- 4, &tr_subtype,
- 5, &tr_profile,
- /* 6, &tr_pref, */
- /* 7, &tr_type, */
- /* 8, &tr_user, */
- /* 9, &tr_pass, */
- G_MAXUINT);
- break;
- case 2: /* RTCP */
- dbus_g_type_struct_get (&transport,
- 1, &rtcp_address,
- 2, &rtcp_port,
- G_MAXUINT);
- break;
- }
+ if (tr_component == 1)
+ break;
}
if (priv->native_candidate_id != NULL)
@@ -1904,28 +1818,100 @@ priv_update_local_sdp(RakiaMediaStream *stream)
}
else if (tr_proto == TP_MEDIA_STREAM_BASE_PROTO_UDP)
{
- g_free (priv->native_candidate_id);
- priv->native_candidate_id = g_strdup (candidate_id);
break;
}
}
- g_return_if_fail (i >= 0);
+
+ if (i < 0)
+ {
+ WARNING ("preferred candidate not found");
+ return NULL;
+ }
+
+ if (transports != NULL)
+ *transports = ca_tports;
+
+ return candidate_id;
+}
+
+/**
+ * Produces the SDP description of the stream based on Farsight state and
+ * current object state.
+ *
+ * @param stream The stream object
+ * @param signal_update If true, emit the signal "local-media-updated".
+ */
+void
+rakia_media_stream_generate_sdp (RakiaMediaStream *stream, GString *out)
+{
+ RakiaMediaStreamPrivate *priv = stream->priv;
+ GString *alines;
+ GValue transport = { 0 };
+ const GPtrArray *transports = NULL;
+ gchar *tr_addr = NULL;
+ /* gchar *tr_user = NULL; */
+ /* gchar *tr_pass = NULL; */
+ gchar *tr_subtype = NULL;
+ gchar *tr_profile = NULL;
+ guint tr_port;
+ /* guint tr_type; */
+ /* gdouble tr_pref; */
+ guint rtcp_port = 0;
+ gchar *rtcp_address = NULL;
+ const gchar *dirline;
+ guint j;
+
+ priv_get_preferred_native_candidate (priv, &transports);
+
+ g_return_if_fail (transports != NULL);
+
+ g_value_init (&transport, TP_STRUCT_TYPE_MEDIA_STREAM_HANDLER_TRANSPORT);
+
+ for (j = 0; j != transports->len; j++)
+ {
+ guint tr_component;
+
+ g_value_set_static_boxed (&transport,
+ g_ptr_array_index (transports, j));
+
+ dbus_g_type_struct_get (&transport,
+ 0, &tr_component,
+ G_MAXUINT);
+ switch (tr_component)
+ {
+ case 1: /* RTP */
+ dbus_g_type_struct_get (&transport,
+ 1, &tr_addr,
+ 2, &tr_port,
+ 4, &tr_subtype,
+ 5, &tr_profile,
+ /* 6, &tr_pref, */
+ /* 7, &tr_type, */
+ /* 8, &tr_user, */
+ /* 9, &tr_pass, */
+ G_MAXUINT);
+ break;
+ case 2: /* RTCP */
+ dbus_g_type_struct_get (&transport,
+ 1, &rtcp_address,
+ 2, &rtcp_port,
+ G_MAXUINT);
+ break;
+ }
+ }
+
g_return_if_fail (tr_addr != NULL);
g_return_if_fail (tr_subtype != NULL);
g_return_if_fail (tr_profile != NULL);
- mline = g_string_new ("m=");
- g_string_append_printf (mline,
+ g_string_append (out, "m=");
+ g_string_append_printf (out,
"%s %u %s/%s",
priv_media_type_to_str (priv->media_type),
tr_port,
tr_subtype,
tr_profile);
- cline = g_strdup_printf ("c=IN %s %s\r\n",
- (strchr (tr_addr, ':') == NULL)? "IP4" : "IP6",
- tr_addr);
-
switch (priv_get_requested_direction (priv))
{
case TP_MEDIA_STREAM_DIRECTION_BIDIRECTIONAL:
@@ -1967,13 +1953,13 @@ priv_update_local_sdp(RakiaMediaStream *stream)
}
priv_append_rtpmaps (g_value_get_boxed (&priv->native_codecs),
- mline, alines);
+ out, alines);
+
+ g_string_append_printf (out, "\r\nc=IN %s %s\r\n",
+ (strchr (tr_addr, ':') == NULL)? "IP4" : "IP6",
+ tr_addr);
- g_free(priv->stream_sdp);
- priv->stream_sdp = g_strconcat(mline->str, "\r\n",
- cline,
- alines->str,
- NULL);
+ g_string_append (out, alines->str);
g_free (tr_addr);
g_free (tr_profile);
@@ -1982,11 +1968,7 @@ priv_update_local_sdp(RakiaMediaStream *stream)
/* g_free (tr_pass); */
g_free (rtcp_address);
- g_string_free (mline, TRUE);
- g_free (cline);
g_string_free (alines, TRUE);
-
- g_signal_emit (stream, signals[SIG_LOCAL_MEDIA_UPDATED], 0);
}
static void
diff --git a/rakia/media-stream.h b/rakia/media-stream.h
index aabbb62..7f1ab87 100644
--- a/rakia/media-stream.h
+++ b/rakia/media-stream.h
@@ -65,7 +65,7 @@ GType rakia_media_stream_get_type(void);
void rakia_media_stream_close (RakiaMediaStream *self);
guint rakia_media_stream_get_id (RakiaMediaStream *self);
guint rakia_media_stream_get_media_type (RakiaMediaStream *self);
-const char *rakia_media_stream_local_sdp (RakiaMediaStream *self);
+void rakia_media_stream_generate_sdp (RakiaMediaStream *self, GString *out);
gboolean rakia_media_stream_set_remote_media (RakiaMediaStream *self,
const sdp_media_t *media,
guint direction_up_mask,
diff --git a/src/Makefile.am b/src/Makefile.am
index 7b37144..cf22c22 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -41,7 +41,7 @@ noinst_LTLIBRARIES = librakia-convenience.la
glib-mkenums \
--fhead "#ifndef __$(shell echo $* | tr [:lower:]- [:upper:]_)_ENUM_TYPES_H__\n#define __$(shell echo $* | tr [:lower:]- [:upper:]_)_ENUM_TYPES_H__\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \
--fprod "/* enumerations from \"@filename@\" */\n" \
- --vhead "GType @enum_name@_get_type (void);\n#define $(shell echo $* | tr [:lower:]- [:upper:]_ | sed 's/_.*//')_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \
+ --vhead "GType @enum_name@_get_type (void);\n#define RAKIA_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \
--ftail "G_END_DECLS\n\n#endif /* __$(shell echo $* | tr [:lower:]- [:upper:]_)_ENUM_TYPES_H__ */" \
$< > $@ || rm -f $@
diff --git a/src/sip-connection.c b/src/sip-connection.c
index c611512..68a75f7 100644
--- a/src/sip-connection.c
+++ b/src/sip-connection.c
@@ -509,7 +509,7 @@ rakia_connection_class_init (RakiaConnectionClass *klass)
param_spec = g_param_spec_enum ("keepalive-mechanism", "Keepalive mechanism",
"Keepalive mechanism for SIP registration",
- rakia_connection_keepalive_mechanism_get_type (),
+ RAKIA_TYPE_CONNECTION_KEEPALIVE_MECHANISM,
RAKIA_CONNECTION_KEEPALIVE_AUTO, /*default value*/
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
INST_PROP(PROP_KEEPALIVE_MECHANISM);
diff --git a/src/telepathy-rakia.c b/src/telepathy-rakia.c
index f482562..45c952c 100644
--- a/src/telepathy-rakia.c
+++ b/src/telepathy-rakia.c
@@ -66,6 +66,8 @@ main (int argc, char** argv)
tp_debug_divert_messages (logfile_string);
+ tp_debug_divert_messages (logfile_string);
+
status = tp_run_connection_manager ("telepathy-rakia", VERSION,
construct_cm, argc, argv);
diff --git a/tests/twisted/Makefile.am b/tests/twisted/Makefile.am
index 816c77d..74645c2 100644
--- a/tests/twisted/Makefile.am
+++ b/tests/twisted/Makefile.am
@@ -12,6 +12,7 @@ TWISTED_TESTS = \
text/initiate-requestotron.py \
voip/incoming-basics.py \
voip/outgoing-basics.py \
+ voip/dtmf.py \
$(NULL)
TESTS =
diff --git a/tests/twisted/constants.py b/tests/twisted/constants.py
index 1ffa4d8..c590b3d 100644
--- a/tests/twisted/constants.py
+++ b/tests/twisted/constants.py
@@ -17,6 +17,7 @@ CHANNEL = "org.freedesktop.Telepathy.Channel"
CHANNEL_IFACE_CALL_STATE = CHANNEL + ".Interface.CallState"
CHANNEL_IFACE_CHAT_STATE = CHANNEL + '.Interface.ChatState'
CHANNEL_IFACE_DESTROYABLE = CHANNEL + ".Interface.Destroyable"
+CHANNEL_IFACE_DTMF = CHANNEL + ".Interface.DTMF"
CHANNEL_IFACE_GROUP = CHANNEL + ".Interface.Group"
CHANNEL_IFACE_HOLD = CHANNEL + ".Interface.Hold"
CHANNEL_IFACE_MEDIA_SIGNALLING = CHANNEL + ".Interface.MediaSignalling"
@@ -63,6 +64,8 @@ CALL_INITIAL_AUDIO = CHANNEL_TYPE_CALL + '.InitialAudio'
CALL_INITIAL_VIDEO = CHANNEL_TYPE_CALL + '.InitialVideo'
CALL_MUTABLE_CONTENTS = CHANNEL_TYPE_CALL + '.MutableContents'
+DTMF_INITIAL_TONES = CHANNEL_IFACE_DTMF + '.InitialTones'
+
CALL_CONTENT = 'org.freedesktop.Telepathy.Call.Content.DRAFT'
CALL_CONTENT_IFACE_MEDIA = \
'org.freedesktop.Telepathy.Call.Content.Interface.Media.DRAFT'
diff --git a/tests/twisted/voip/dtmf.py b/tests/twisted/voip/dtmf.py
new file mode 100644
index 0000000..4369bc3
--- /dev/null
+++ b/tests/twisted/voip/dtmf.py
@@ -0,0 +1,160 @@
+"""
+Test DTMF dialstring playback and signalling.
+"""
+
+from sofiatest import exec_test
+from servicetest import (
+ call_async, wrap_channel, EventPattern,
+ assertEquals, assertContains, assertLength, assertSameSets
+ )
+from voip_test import VoipTestContext
+import constants as cs
+
+def setup_dtmf_channel(context, initial_tones=None):
+ q = context.q
+ bus = context.bus
+ conn = context.conn
+
+ conn.Connect()
+ q.expect('dbus-signal', signal='StatusChanged', args=[0, 1])
+
+ request_params = {
+ cs.CHANNEL_TYPE: cs.CHANNEL_TYPE_STREAMED_MEDIA,
+ cs.TARGET_HANDLE_TYPE: cs.HT_CONTACT,
+ cs.TARGET_ID: context.peer,
+ cs.INITIAL_AUDIO: True,
+ }
+ if initial_tones:
+ request_params[cs.DTMF_INITIAL_TONES] = initial_tones
+
+ path = conn.Requests.CreateChannel(request_params)[0]
+
+ chan = wrap_channel(bus.get_object(conn.bus_name, path), 'StreamedMedia',
+ ['MediaSignalling', 'DTMF'])
+
+ channel_props = chan.Properties.GetAll(cs.CHANNEL)
+
+ assertContains(cs.CHANNEL_IFACE_DTMF, channel_props['Interfaces'])
+
+ dtmf_props = chan.Properties.GetAll(cs.CHANNEL_IFACE_DTMF)
+
+ if initial_tones:
+ assertEquals(initial_tones, dtmf_props['InitialTones'])
+ else:
+ assertEquals('', dtmf_props['InitialTones'])
+ assertEquals(False, dtmf_props['CurrentlySendingTones'])
+
+ stream_handler = context.handle_audio_session(chan)
+
+ invite_event = q.expect('sip-invite')
+
+ context.accept(invite_event.sip_message)
+
+ q.expect('dbus-signal', signal='SetRemoteCodecs')
+
+ stream_handler.SupportedCodecs(context.get_audio_codecs_dbus())
+ stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED)
+
+ return chan
+
+def request_initial_tones(q, bus, conn, sip_proxy, peer='foo@bar.com'):
+ context = VoipTestContext(q, conn, bus, sip_proxy, 'sip:testacc@127.0.0.1', peer)
+
+ tones = '123'
+
+ chan = setup_dtmf_channel(context, tones)
+
+ q.expect_many(
+ EventPattern('dbus-signal', signal='SendingTones', args=[tones]),
+ EventPattern('dbus-signal', signal='StartTelephonyEvent', args=[int(tones[0])]))
+
+ assertEquals(True, chan.Properties.Get(cs.CHANNEL_IFACE_DTMF, 'CurrentlySendingTones'))
+
+ q.expect('dbus-signal', signal='StopTelephonyEvent')
+
+ for i in range(1, len(tones) - 1):
+ q.expect('dbus-signal', signal='StartTelephonyEvent', args=[int(tones[i])])
+ q.expect('dbus-signal', signal='StopTelephonyEvent')
+
+ q.expect('dbus-signal', signal='StoppedTones', args=[False])
+
+ assertEquals(False, chan.Properties.Get(cs.CHANNEL_IFACE_DTMF, 'CurrentlySendingTones'))
+
+def multiple_tones(q, bus, conn, sip_proxy, peer='foo@bar.com'):
+
+ context = VoipTestContext(q, conn, bus, sip_proxy, 'sip:testacc@127.0.0.1', peer)
+
+ chan = setup_dtmf_channel(context)
+
+ tones_deferred = '78'
+ tones = '56w' + tones_deferred
+
+ chan.DTMF.MultipleTones(tones)
+
+ q.expect_many(
+ EventPattern('dbus-signal', signal='SendingTones', args=[tones]),
+ EventPattern('dbus-signal', signal='StartTelephonyEvent', args=[int(tones[0])]))
+
+ dtmf_props = chan.Properties.GetAll(cs.CHANNEL_IFACE_DTMF)
+ assertEquals(True, dtmf_props['CurrentlySendingTones'])
+ assertEquals('', dtmf_props['DeferredTones'])
+
+ q.expect('dbus-signal', signal='StopTelephonyEvent')
+
+ q.expect('dbus-signal', signal='StartTelephonyEvent', args=[int(tones[1])])
+ q.expect('dbus-signal', signal='StopTelephonyEvent')
+
+ q.expect('dbus-signal', signal='TonesDeferred', args=[tones_deferred])
+
+ dtmf_props = chan.Properties.GetAll(cs.CHANNEL_IFACE_DTMF)
+
+ assertEquals(False, dtmf_props['CurrentlySendingTones'])
+ assertEquals(tones_deferred, dtmf_props['DeferredTones'])
+
+ chan.DTMF.MultipleTones(tones_deferred)
+
+ q.expect_many(
+ EventPattern('dbus-signal', signal='SendingTones', args=[tones_deferred]),
+ EventPattern('dbus-signal', signal='StartTelephonyEvent', args=[int(tones_deferred[0])]))
+
+ dtmf_props = chan.Properties.GetAll(cs.CHANNEL_IFACE_DTMF)
+
+ assertEquals(True, dtmf_props['CurrentlySendingTones'])
+ assertEquals('', dtmf_props['DeferredTones'])
+
+ q.expect('dbus-signal', signal='StopTelephonyEvent')
+
+ for i in range(1, len(tones_deferred) - 1):
+ q.expect('dbus-signal', signal='StartTelephonyEvent', args=[int(tones_deferred[i])])
+ q.expect('dbus-signal', signal='StopTelephonyEvent')
+
+ q.expect('dbus-signal', signal='StoppedTones', args=[False])
+
+def bleep_bloop(q, bus, conn, sip_proxy, peer='foo@bar.com'):
+
+ context = VoipTestContext(q, conn, bus, sip_proxy, 'sip:testacc@127.0.0.1', peer)
+
+ chan = setup_dtmf_channel(context)
+
+ call_async(q, chan.DTMF, 'StartTone', 666, 3)
+ q.expect_many(
+ EventPattern('dbus-signal', signal='StartTelephonyEvent'),
+ EventPattern('dbus-signal', signal='SendingTones', args=['3']),
+ EventPattern('dbus-return', method='StartTone'),
+ )
+
+ assertEquals(True, chan.Properties.Get(cs.CHANNEL_IFACE_DTMF, 'CurrentlySendingTones'))
+
+ call_async(q, chan.DTMF, 'StopTone', 666)
+ q.expect_many(
+ EventPattern('dbus-signal', signal='StopTelephonyEvent'),
+ EventPattern('dbus-signal', signal='StoppedTones', args=[True]),
+ EventPattern('dbus-return', method='StopTone'),
+ )
+
+ assertEquals(False, chan.Properties.Get(cs.CHANNEL_IFACE_DTMF, 'CurrentlySendingTones'))
+
+if __name__ == '__main__':
+ exec_test(request_initial_tones)
+ exec_test(multiple_tones)
+ exec_test(bleep_bloop)
diff --git a/tests/twisted/voip/outgoing-basics.py b/tests/twisted/voip/outgoing-basics.py
index f09a59b..d840c18 100644
--- a/tests/twisted/voip/outgoing-basics.py
+++ b/tests/twisted/voip/outgoing-basics.py
@@ -7,9 +7,8 @@ import dbus
from sofiatest import exec_test
from servicetest import (
- make_channel_proxy, wrap_channel,
- EventPattern, call_async,
- assertEquals, assertContains, assertLength,
+ wrap_channel, EventPattern, call_async,
+ assertEquals, assertContains, assertLength, assertSameSets
)
import constants as cs
from voip_test import VoipTestContext
@@ -175,23 +174,7 @@ def worker(q, bus, conn, sip_proxy, variant, peer):
cs.MEDIA_STREAM_PENDING_REMOTE_SEND),
streams[0][1:])
- # S-E does state recovery to get the session handler, and calls Ready on it
- session_handlers = chan.MediaSignalling.GetSessionHandlers()
- sh_path, sh_type = session_handlers[0]
-
- assert sh_type == 'rtp'
-
- session_handler = make_channel_proxy(conn, sh_path, 'Media.SessionHandler')
- session_handler.Ready()
-
- e = q.expect('dbus-signal', signal='NewStreamHandler')
-
- stream_handler = make_channel_proxy(conn, e.args[0], 'Media.StreamHandler')
-
- stream_handler.NewNativeCandidate("fake", context.get_remote_transports_dbus())
- stream_handler.NativeCandidatesPrepared()
- stream_handler.Ready(context.get_audio_codecs_dbus())
- stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED)
+ stream_handler = context.handle_audio_session(chan)
sh_props = stream_handler.GetAll(
cs.STREAM_HANDLER, dbus_interface=dbus.PROPERTIES_IFACE)
@@ -228,7 +211,11 @@ def worker(q, bus, conn, sip_proxy, variant, peer):
EventPattern('dbus-signal', signal='MembersChanged',
args=['', [remote_handle], [], [], [], remote_handle,
cs.GC_REASON_NONE]),
- )
+ EventPattern('dbus-signal', signal='SetRemoteCodecs'),
+ ),
+
+ stream_handler.SupportedCodecs(context.get_audio_codecs_dbus())
+ stream_handler.StreamState(cs.MEDIA_STREAM_STATE_CONNECTED)
# Time passes ... afterwards we close the chan
@@ -272,12 +259,13 @@ def rccs(q, bus, conn, stream):
expected_allowed = [
cs.TARGET_ID, cs.TARGET_HANDLE,
- cs.INITIAL_VIDEO, cs.INITIAL_AUDIO
+ cs.INITIAL_VIDEO, cs.INITIAL_AUDIO,
+ cs.DTMF_INITIAL_TONES
]
allowed.sort()
expected_allowed.sort()
- assertEquals(expected_allowed, allowed)
+ assertSameSets(expected_allowed, allowed)
if __name__ == '__main__':
diff --git a/tests/twisted/voip/voip_test.py b/tests/twisted/voip/voip_test.py
index b31928f..c8724af 100644
--- a/tests/twisted/voip/voip_test.py
+++ b/tests/twisted/voip/voip_test.py
@@ -4,7 +4,10 @@ import uuid
import twisted.protocols.sip
-from servicetest import assertContains
+from servicetest import (
+ make_channel_proxy,
+ assertContains,
+ )
class VoipTestContext(object):
# Default audio codecs for the remote end
@@ -178,3 +181,28 @@ class VoipTestContext(object):
def terminate(self):
return self.send_message('BYE', call_id=self.call_id)
+
+ def handle_audio_session(self, chan):
+ """
+ Serves a SessionHandler and a StreamHandler for the MediaSignalling
+ channel. Returns the interface proxy for StreamHandler.
+ """
+ session_handlers = chan.MediaSignalling.GetSessionHandlers()
+ sh_path, sh_type = session_handlers[0]
+
+ assert sh_type == 'rtp'
+
+ session_handler = make_channel_proxy(self.conn, sh_path,
+ 'Media.SessionHandler')
+ session_handler.Ready()
+
+ e = self.q.expect('dbus-signal', signal='NewStreamHandler')
+
+ stream_handler = make_channel_proxy(self.conn, e.args[0],
+ 'Media.StreamHandler')
+
+ stream_handler.NewNativeCandidate("fake", self.get_remote_transports_dbus())
+ stream_handler.NativeCandidatesPrepared()
+ stream_handler.Ready(self.get_audio_codecs_dbus())
+
+ return stream_handler