diff options
Diffstat (limited to 'rakia')
-rw-r--r-- | rakia/media-channel.c | 249 | ||||
-rw-r--r-- | rakia/media-channel.h | 2 | ||||
-rw-r--r-- | rakia/media-manager.c | 42 | ||||
-rw-r--r-- | rakia/media-session.c | 214 | ||||
-rw-r--r-- | rakia/media-session.h | 15 | ||||
-rw-r--r-- | rakia/media-stream.c | 286 | ||||
-rw-r--r-- | rakia/media-stream.h | 2 |
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, |