summaryrefslogtreecommitdiff
path: root/rakia
diff options
context:
space:
mode:
Diffstat (limited to 'rakia')
-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
7 files changed, 514 insertions, 296 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,