2 * empathy-call-handler.c - Source for EmpathyCallHandler
3 * Copyright (C) 2008-2009 Collabora Ltd.
4 * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "empathy-call-handler.h"
24 #include <telepathy-farstream/telepathy-farstream.h>
26 #include "empathy-call-utils.h"
27 #include "empathy-utils.h"
29 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
30 #include "empathy-debug.h"
32 G_DEFINE_TYPE(EmpathyCallHandler
, empathy_call_handler
, G_TYPE_OBJECT
)
49 static guint signals
[LAST_SIGNAL
] = {0};
52 PROP_CALL_CHANNEL
= 1,
56 PROP_SEND_AUDIO_CODEC
,
57 PROP_SEND_VIDEO_CODEC
,
58 PROP_RECV_AUDIO_CODECS
,
59 PROP_RECV_VIDEO_CODECS
,
60 PROP_AUDIO_REMOTE_CANDIDATE
,
61 PROP_VIDEO_REMOTE_CANDIDATE
,
62 PROP_AUDIO_LOCAL_CANDIDATE
,
63 PROP_VIDEO_LOCAL_CANDIDATE
,
66 /* private structure */
68 struct _EmpathyCallHandlerPriv
{
71 EmpathyContact
*contact
;
73 gboolean initial_video
;
75 FsCodec
*send_audio_codec
;
76 FsCodec
*send_video_codec
;
77 GList
*recv_audio_codecs
;
78 GList
*recv_video_codecs
;
79 FsCandidate
*audio_remote_candidate
;
80 FsCandidate
*video_remote_candidate
;
81 FsCandidate
*audio_local_candidate
;
82 FsCandidate
*video_local_candidate
;
83 gboolean accept_when_initialised
;
86 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyCallHandler)
89 empathy_call_handler_dispose (GObject
*object
)
91 EmpathyCallHandlerPriv
*priv
= GET_PRIV (object
);
93 tp_clear_object (&priv
->tfchannel
);
94 tp_clear_object (&priv
->call
);
95 tp_clear_object (&priv
->contact
);
97 G_OBJECT_CLASS (empathy_call_handler_parent_class
)->dispose (object
);
101 empathy_call_handler_finalize (GObject
*object
)
103 EmpathyCallHandlerPriv
*priv
= GET_PRIV (object
);
105 fs_codec_destroy (priv
->send_audio_codec
);
106 fs_codec_destroy (priv
->send_video_codec
);
107 fs_codec_list_destroy (priv
->recv_audio_codecs
);
108 fs_codec_list_destroy (priv
->recv_video_codecs
);
109 fs_candidate_destroy (priv
->audio_remote_candidate
);
110 fs_candidate_destroy (priv
->video_remote_candidate
);
111 fs_candidate_destroy (priv
->audio_local_candidate
);
112 fs_candidate_destroy (priv
->video_local_candidate
);
114 G_OBJECT_CLASS (empathy_call_handler_parent_class
)->finalize (object
);
118 empathy_call_handler_init (EmpathyCallHandler
*obj
)
120 EmpathyCallHandlerPriv
*priv
= G_TYPE_INSTANCE_GET_PRIVATE (obj
,
121 EMPATHY_TYPE_CALL_HANDLER
, EmpathyCallHandlerPriv
);
127 on_call_accepted_cb (GObject
*source_object
,
131 TpCallChannel
*call
= TP_CALL_CHANNEL (source_object
);
132 GError
*error
= NULL
;
134 if (!tp_call_channel_accept_finish (call
, res
, &error
))
136 g_warning ("could not accept Call: %s", error
->message
);
137 g_error_free (error
);
142 on_call_invalidated_cb (TpCallChannel
*call
,
146 EmpathyCallHandler
*self
)
148 EmpathyCallHandlerPriv
*priv
= self
->priv
;
150 if (priv
->call
== call
)
152 /* Invalidated unexpectedly? Fake call ending */
153 g_signal_emit (self
, signals
[STATE_CHANGED
], 0,
154 TP_CALL_STATE_ENDED
, NULL
);
155 priv
->accept_when_initialised
= FALSE
;
156 tp_clear_object (&priv
->call
);
157 tp_clear_object (&priv
->tfchannel
);
162 on_call_state_changed_cb (TpCallChannel
*call
,
165 TpCallStateReason
*reason
,
167 EmpathyCallHandler
*handler
)
169 EmpathyCallHandlerPriv
*priv
= handler
->priv
;
171 /* Clean up the TfChannel before bubbling the state-change signal
172 * further up. This ensures that the conference-removed signal is
173 * emitted before state-changed so that the client gets a chance
174 * to remove the conference from the pipeline before resetting the
177 if (state
== TP_CALL_STATE_ENDED
)
179 tp_channel_close_async (TP_CHANNEL (call
), NULL
, NULL
);
180 priv
->accept_when_initialised
= FALSE
;
181 tp_clear_object (&priv
->call
);
182 tp_clear_object (&priv
->tfchannel
);
185 g_signal_emit (handler
, signals
[STATE_CHANGED
], 0, state
,
186 reason
->dbus_reason
);
188 if (state
== TP_CALL_STATE_INITIALISED
&&
189 priv
->accept_when_initialised
)
191 tp_call_channel_accept_async (priv
->call
, on_call_accepted_cb
, NULL
);
192 priv
->accept_when_initialised
= FALSE
;
197 empathy_call_handler_set_property (GObject
*object
,
198 guint property_id
, const GValue
*value
, GParamSpec
*pspec
)
200 EmpathyCallHandlerPriv
*priv
= GET_PRIV (object
);
205 priv
->contact
= g_value_dup_object (value
);
207 case PROP_CALL_CHANNEL
:
208 g_return_if_fail (priv
->call
== NULL
);
210 priv
->call
= g_value_dup_object (value
);
212 tp_g_signal_connect_object (priv
->call
, "state-changed",
213 G_CALLBACK (on_call_state_changed_cb
), object
, 0);
214 tp_g_signal_connect_object (priv
->call
, "invalidated",
215 G_CALLBACK (on_call_invalidated_cb
), object
, 0);
217 case PROP_INITIAL_VIDEO
:
218 priv
->initial_video
= g_value_get_boolean (value
);
221 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
226 empathy_call_handler_get_property (GObject
*object
,
227 guint property_id
, GValue
*value
, GParamSpec
*pspec
)
229 EmpathyCallHandlerPriv
*priv
= GET_PRIV (object
);
234 g_value_set_object (value
, priv
->contact
);
236 case PROP_CALL_CHANNEL
:
237 g_value_set_object (value
, priv
->call
);
239 case PROP_INITIAL_VIDEO
:
240 g_value_set_boolean (value
, priv
->initial_video
);
242 case PROP_SEND_AUDIO_CODEC
:
243 g_value_set_boxed (value
, priv
->send_audio_codec
);
245 case PROP_SEND_VIDEO_CODEC
:
246 g_value_set_boxed (value
, priv
->send_video_codec
);
248 case PROP_RECV_AUDIO_CODECS
:
249 g_value_set_boxed (value
, priv
->recv_audio_codecs
);
251 case PROP_RECV_VIDEO_CODECS
:
252 g_value_set_boxed (value
, priv
->recv_video_codecs
);
254 case PROP_AUDIO_REMOTE_CANDIDATE
:
255 g_value_set_boxed (value
, priv
->audio_remote_candidate
);
257 case PROP_VIDEO_REMOTE_CANDIDATE
:
258 g_value_set_boxed (value
, priv
->video_remote_candidate
);
260 case PROP_AUDIO_LOCAL_CANDIDATE
:
261 g_value_set_boxed (value
, priv
->audio_local_candidate
);
263 case PROP_VIDEO_LOCAL_CANDIDATE
:
264 g_value_set_boxed (value
, priv
->video_local_candidate
);
267 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
273 empathy_call_handler_class_init (EmpathyCallHandlerClass
*klass
)
275 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
276 GParamSpec
*param_spec
;
278 g_type_class_add_private (klass
, sizeof (EmpathyCallHandlerPriv
));
280 object_class
->set_property
= empathy_call_handler_set_property
;
281 object_class
->get_property
= empathy_call_handler_get_property
;
282 object_class
->dispose
= empathy_call_handler_dispose
;
283 object_class
->finalize
= empathy_call_handler_finalize
;
285 param_spec
= g_param_spec_object ("target-contact",
286 "TargetContact", "The contact",
287 EMPATHY_TYPE_CONTACT
,
288 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
| G_PARAM_STATIC_STRINGS
);
289 g_object_class_install_property (object_class
, PROP_CONTACT
, param_spec
);
291 param_spec
= g_param_spec_object ("call-channel",
292 "call channel", "The call channel",
293 TP_TYPE_CALL_CHANNEL
,
294 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
| G_PARAM_STATIC_STRINGS
);
295 g_object_class_install_property (object_class
, PROP_CALL_CHANNEL
, param_spec
);
297 param_spec
= g_param_spec_boolean ("initial-video",
298 "initial-video", "Whether the call should start with video",
300 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT
| G_PARAM_STATIC_STRINGS
);
301 g_object_class_install_property (object_class
, PROP_INITIAL_VIDEO
,
304 param_spec
= g_param_spec_boxed ("send-audio-codec",
305 "send audio codec", "Codec used to encode the outgoing video stream",
307 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
308 g_object_class_install_property (object_class
, PROP_SEND_AUDIO_CODEC
,
311 param_spec
= g_param_spec_boxed ("send-video-codec",
312 "send video codec", "Codec used to encode the outgoing video stream",
314 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
315 g_object_class_install_property (object_class
, PROP_SEND_VIDEO_CODEC
,
318 param_spec
= g_param_spec_boxed ("recv-audio-codecs",
319 "recvs audio codec", "Codecs used to decode the incoming audio stream",
321 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
322 g_object_class_install_property (object_class
, PROP_RECV_AUDIO_CODECS
,
325 param_spec
= g_param_spec_boxed ("recv-video-codecs",
326 "recvs video codec", "Codecs used to decode the incoming video stream",
328 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
329 g_object_class_install_property (object_class
, PROP_RECV_VIDEO_CODECS
,
332 param_spec
= g_param_spec_boxed ("audio-remote-candidate",
333 "audio remote candidate",
334 "Remote candidate used for the audio stream",
336 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
337 g_object_class_install_property (object_class
,
338 PROP_AUDIO_REMOTE_CANDIDATE
, param_spec
);
340 param_spec
= g_param_spec_boxed ("video-remote-candidate",
341 "video remote candidate",
342 "Remote candidate used for the video stream",
344 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
345 g_object_class_install_property (object_class
,
346 PROP_VIDEO_REMOTE_CANDIDATE
, param_spec
);
348 param_spec
= g_param_spec_boxed ("audio-local-candidate",
349 "audio local candidate",
350 "Local candidate used for the audio stream",
352 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
353 g_object_class_install_property (object_class
,
354 PROP_AUDIO_REMOTE_CANDIDATE
, param_spec
);
356 param_spec
= g_param_spec_boxed ("video-local-candidate",
357 "video local candidate",
358 "Local candidate used for the video stream",
360 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
);
361 g_object_class_install_property (object_class
,
362 PROP_VIDEO_REMOTE_CANDIDATE
, param_spec
);
364 signals
[CONFERENCE_ADDED
] =
365 g_signal_new ("conference-added", G_TYPE_FROM_CLASS (klass
),
366 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
367 g_cclosure_marshal_generic
,
369 1, FS_TYPE_CONFERENCE
);
371 signals
[CONFERENCE_REMOVED
] =
372 g_signal_new ("conference-removed", G_TYPE_FROM_CLASS (klass
),
373 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
374 g_cclosure_marshal_generic
,
376 1, FS_TYPE_CONFERENCE
);
378 signals
[SRC_PAD_ADDED
] =
379 g_signal_new ("src-pad-added", G_TYPE_FROM_CLASS (klass
),
380 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
381 g_cclosure_marshal_generic
,
383 2, TF_TYPE_CONTENT
, GST_TYPE_PAD
);
385 signals
[CONTENT_ADDED
] =
386 g_signal_new ("content-added", G_TYPE_FROM_CLASS (klass
),
387 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
388 g_cclosure_marshal_generic
,
392 signals
[CONTENT_REMOVED
] =
393 g_signal_new ("content-removed", G_TYPE_FROM_CLASS (klass
),
394 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
395 g_cclosure_marshal_generic
,
400 g_signal_new ("closed", G_TYPE_FROM_CLASS (klass
),
401 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
402 g_cclosure_marshal_generic
,
406 signals
[CANDIDATES_CHANGED
] =
407 g_signal_new ("candidates-changed", G_TYPE_FROM_CLASS (klass
),
408 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
409 g_cclosure_marshal_generic
,
410 G_TYPE_NONE
, 1, G_TYPE_UINT
);
412 signals
[STATE_CHANGED
] =
413 g_signal_new ("state-changed", G_TYPE_FROM_CLASS (klass
),
414 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
415 g_cclosure_marshal_generic
,
416 G_TYPE_NONE
, 2, G_TYPE_UINT
, G_TYPE_STRING
);
418 signals
[FRAMERATE_CHANGED
] =
419 g_signal_new ("framerate-changed", G_TYPE_FROM_CLASS (klass
),
420 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
421 g_cclosure_marshal_generic
,
422 G_TYPE_NONE
, 1, G_TYPE_UINT
);
424 signals
[RESOLUTION_CHANGED
] =
425 g_signal_new ("resolution-changed", G_TYPE_FROM_CLASS (klass
),
426 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
427 g_cclosure_marshal_generic
,
429 2, G_TYPE_UINT
, G_TYPE_UINT
);
433 empathy_call_handler_new_for_channel (TpCallChannel
*call
,
434 EmpathyContact
*contact
)
436 return EMPATHY_CALL_HANDLER (g_object_new (EMPATHY_TYPE_CALL_HANDLER
,
437 "call-channel", call
,
438 "initial-video", tp_call_channel_has_initial_video (call
, NULL
),
439 "target-contact", contact
,
444 update_sending_codec (EmpathyCallHandler
*self
,
448 EmpathyCallHandlerPriv
*priv
= GET_PRIV (self
);
451 if (codec
== NULL
|| session
== NULL
)
454 g_object_get (session
, "media-type", &type
, NULL
);
456 if (type
== FS_MEDIA_TYPE_AUDIO
)
458 priv
->send_audio_codec
= fs_codec_copy (codec
);
459 g_object_notify (G_OBJECT (self
), "send-audio-codec");
461 else if (type
== FS_MEDIA_TYPE_VIDEO
)
463 priv
->send_video_codec
= fs_codec_copy (codec
);
464 g_object_notify (G_OBJECT (self
), "send-video-codec");
469 update_receiving_codec (EmpathyCallHandler
*self
,
473 EmpathyCallHandlerPriv
*priv
= GET_PRIV (self
);
477 if (codecs
== NULL
|| stream
== NULL
)
480 g_object_get (stream
, "session", &session
, NULL
);
484 g_object_get (session
, "media-type", &type
, NULL
);
486 if (type
== FS_MEDIA_TYPE_AUDIO
)
488 priv
->recv_audio_codecs
= fs_codec_list_copy (codecs
);
489 g_object_notify (G_OBJECT (self
), "recv-audio-codecs");
491 else if (type
== FS_MEDIA_TYPE_VIDEO
)
493 priv
->recv_video_codecs
= fs_codec_list_copy (codecs
);
494 g_object_notify (G_OBJECT (self
), "recv-video-codecs");
497 g_object_unref (session
);
501 update_candidates (EmpathyCallHandler
*self
,
502 FsCandidate
*remote_candidate
,
503 FsCandidate
*local_candidate
,
506 EmpathyCallHandlerPriv
*priv
= GET_PRIV (self
);
513 g_object_get (stream
, "session", &session
, NULL
);
517 g_object_get (session
, "media-type", &type
, NULL
);
519 if (type
== FS_MEDIA_TYPE_AUDIO
)
521 if (remote_candidate
!= NULL
)
523 fs_candidate_destroy (priv
->audio_remote_candidate
);
524 priv
->audio_remote_candidate
= fs_candidate_copy (remote_candidate
);
525 g_object_notify (G_OBJECT (self
), "audio-remote-candidate");
528 if (local_candidate
!= NULL
)
530 fs_candidate_destroy (priv
->audio_local_candidate
);
531 priv
->audio_local_candidate
= fs_candidate_copy (local_candidate
);
532 g_object_notify (G_OBJECT (self
), "audio-local-candidate");
535 g_signal_emit (G_OBJECT (self
), signals
[CANDIDATES_CHANGED
], 0,
536 FS_MEDIA_TYPE_AUDIO
);
538 else if (type
== FS_MEDIA_TYPE_VIDEO
)
540 if (remote_candidate
!= NULL
)
542 fs_candidate_destroy (priv
->video_remote_candidate
);
543 priv
->video_remote_candidate
= fs_candidate_copy (remote_candidate
);
544 g_object_notify (G_OBJECT (self
), "video-remote-candidate");
547 if (local_candidate
!= NULL
)
549 fs_candidate_destroy (priv
->video_local_candidate
);
550 priv
->video_local_candidate
= fs_candidate_copy (local_candidate
);
551 g_object_notify (G_OBJECT (self
), "video-local-candidate");
554 g_signal_emit (G_OBJECT (self
), signals
[CANDIDATES_CHANGED
], 0,
555 FS_MEDIA_TYPE_VIDEO
);
558 g_object_unref (session
);
562 empathy_call_handler_bus_message (EmpathyCallHandler
*handler
,
563 GstBus
*bus
, GstMessage
*message
)
565 EmpathyCallHandlerPriv
*priv
= GET_PRIV (handler
);
566 const GstStructure
*s
= gst_message_get_structure (message
);
568 if (priv
->tfchannel
== NULL
)
572 gst_structure_has_name (s
, "farsight-send-codec-changed"))
578 DEBUG ("farsight-send-codec-changed");
580 val
= gst_structure_get_value (s
, "codec");
581 codec
= g_value_get_boxed (val
);
583 val
= gst_structure_get_value (s
, "session");
584 session
= g_value_get_object (val
);
586 update_sending_codec (handler
, codec
, session
);
588 else if (s
!= NULL
&&
589 gst_structure_has_name (s
, "farsight-recv-codecs-changed"))
595 DEBUG ("farsight-recv-codecs-changed");
597 val
= gst_structure_get_value (s
, "codecs");
598 codecs
= g_value_get_boxed (val
);
600 val
= gst_structure_get_value (s
, "stream");
601 stream
= g_value_get_object (val
);
603 update_receiving_codec (handler
, codecs
, stream
);
605 else if (s
!= NULL
&&
606 gst_structure_has_name (s
, "farsight-new-active-candidate-pair"))
609 FsCandidate
*remote_candidate
, *local_candidate
;
612 DEBUG ("farsight-new-active-candidate-pair");
614 val
= gst_structure_get_value (s
, "remote-candidate");
615 remote_candidate
= g_value_get_boxed (val
);
617 val
= gst_structure_get_value (s
, "local-candidate");
618 local_candidate
= g_value_get_boxed (val
);
620 val
= gst_structure_get_value (s
, "stream");
621 stream
= g_value_get_object (val
);
623 update_candidates (handler
, remote_candidate
, local_candidate
, stream
);
626 tf_channel_bus_message (priv
->tfchannel
, message
);
630 on_tf_channel_conference_added_cb (TfChannel
*tfchannel
,
631 GstElement
*conference
,
632 EmpathyCallHandler
*self
)
634 g_signal_emit (G_OBJECT (self
), signals
[CONFERENCE_ADDED
], 0,
639 on_tf_channel_conference_removed_cb (TfChannel
*tfchannel
,
640 FsConference
*conference
,
641 EmpathyCallHandler
*self
)
643 g_signal_emit (G_OBJECT (self
), signals
[CONFERENCE_REMOVED
], 0,
644 GST_ELEMENT (conference
));
648 src_pad_added_error_idle (gpointer data
)
650 TfContent
*content
= data
;
652 tf_content_error_literal (content
, "Could not link sink");
653 g_object_unref (content
);
659 on_tf_content_src_pad_added_cb (TfContent
*content
,
664 EmpathyCallHandler
*handler
)
668 g_signal_emit (G_OBJECT (handler
), signals
[SRC_PAD_ADDED
], 0,
669 content
, pad
, &retval
);
672 g_idle_add (src_pad_added_error_idle
, g_object_ref (content
));
676 on_tf_content_framerate_changed (TfContent
*content
,
678 EmpathyCallHandler
*handler
)
682 g_object_get (content
, "framerate", &framerate
, NULL
);
685 g_signal_emit (G_OBJECT (handler
), signals
[FRAMERATE_CHANGED
], 0,
690 on_tf_content_resolution_changed (TfContent
*content
,
693 EmpathyCallHandler
*handler
)
695 if (width
> 0 && height
> 0)
696 g_signal_emit (G_OBJECT (handler
), signals
[RESOLUTION_CHANGED
], 0,
701 on_tf_channel_content_added_cb (TfChannel
*tfchannel
,
703 EmpathyCallHandler
*handler
)
707 // FsStream *fs_stream;
712 g_signal_connect (content
, "src-pad-added",
713 G_CALLBACK (on_tf_content_src_pad_added_cb
), handler
);
715 g_signal_connect (content
, "start-sending",
716 G_CALLBACK (on_tf_content_start_sending_cb
), handler
);
717 g_signal_connect (content
, "stop-sending",
718 G_CALLBACK (on_tf_content_stop_sending_cb
), handler
);
721 g_signal_emit (G_OBJECT (handler
), signals
[CONTENT_ADDED
], 0,
725 tf_content_error_literal (content
, "Could not link source");
727 /* Get sending codec */
728 g_object_get (content
, "fs-session", &session
, NULL
);
729 g_object_get (session
, "current-send-codec", &codec
, NULL
);
731 update_sending_codec (handler
, codec
, session
);
733 tp_clear_object (&session
);
734 tp_clear_object (&codec
);
736 /* Get receiving codec */
738 g_object_get (content, "fs-stream", &fs_stream, NULL);
739 g_object_get (fs_stream, "current-recv-codecs", &codecs, NULL);
741 update_receiving_codec (handler, codecs, fs_stream);
743 fs_codec_list_destroy (codecs);
744 tp_clear_object (&fs_stream);
747 g_object_get (content
, "media-type", &mtype
, NULL
);
749 if (mtype
== FS_MEDIA_TYPE_VIDEO
)
751 guint framerate
, width
, height
;
753 g_signal_connect (content
, "notify::framerate",
754 G_CALLBACK (on_tf_content_framerate_changed
),
757 g_signal_connect (content
, "resolution-changed",
758 G_CALLBACK (on_tf_content_resolution_changed
),
761 g_object_get (content
,
762 "framerate", &framerate
,
768 g_signal_emit (G_OBJECT (handler
), signals
[FRAMERATE_CHANGED
], 0,
771 if (width
> 0 && height
> 0)
772 g_signal_emit (G_OBJECT (handler
), signals
[RESOLUTION_CHANGED
], 0,
778 on_tf_channel_content_removed_cb (TfChannel
*tfchannel
,
780 EmpathyCallHandler
*handler
)
784 DEBUG ("removing content");
786 g_signal_emit (G_OBJECT (handler
), signals
[CONTENT_REMOVED
], 0,
791 g_warning ("Could not remove content!");
793 tf_content_error_literal (content
, "Could not link source");
798 on_tf_channel_closed_cb (TfChannel
*tfchannel
,
799 EmpathyCallHandler
*handler
)
801 g_signal_emit (G_OBJECT (handler
), signals
[CLOSED
], 0);
805 on_tf_channel_ready (GObject
*source
,
806 GAsyncResult
*result
,
809 EmpathyCallHandler
*self
= EMPATHY_CALL_HANDLER (user_data
);
810 EmpathyCallHandlerPriv
*priv
= GET_PRIV (self
);
811 GError
*error
= NULL
;
813 priv
->tfchannel
= TF_CHANNEL (g_async_initable_new_finish (
814 G_ASYNC_INITABLE (source
), result
, NULL
));
816 if (priv
->tfchannel
== NULL
)
818 g_warning ("Failed to create Farstream channel: %s", error
->message
);
819 g_error_free (error
);
823 /* Set up the telepathy farstream channel */
824 g_signal_connect (priv
->tfchannel
, "closed",
825 G_CALLBACK (on_tf_channel_closed_cb
), self
);
826 g_signal_connect (priv
->tfchannel
, "fs-conference-added",
827 G_CALLBACK (on_tf_channel_conference_added_cb
), self
);
828 g_signal_connect (priv
->tfchannel
, "fs-conference-removed",
829 G_CALLBACK (on_tf_channel_conference_removed_cb
), self
);
830 g_signal_connect (priv
->tfchannel
, "content-added",
831 G_CALLBACK (on_tf_channel_content_added_cb
), self
);
832 g_signal_connect (priv
->tfchannel
, "content-removed",
833 G_CALLBACK (on_tf_channel_content_removed_cb
), self
);
837 empathy_call_handler_start_tpfs (EmpathyCallHandler
*self
)
839 EmpathyCallHandlerPriv
*priv
= GET_PRIV (self
);
841 tf_channel_new_async (TP_CHANNEL (priv
->call
),
842 on_tf_channel_ready
, self
);
846 empathy_call_handler_request_cb (GObject
*source
,
847 GAsyncResult
*result
,
850 EmpathyCallHandler
*self
= EMPATHY_CALL_HANDLER (user_data
);
851 EmpathyCallHandlerPriv
*priv
= GET_PRIV (self
);
853 GError
*error
= NULL
;
854 TpAccountChannelRequest
*req
= TP_ACCOUNT_CHANNEL_REQUEST (source
);
856 channel
= tp_account_channel_request_create_and_handle_channel_finish (req
,
857 result
, NULL
, &error
);
860 DEBUG ("Failed to create the channel: %s", error
->message
);
861 g_error_free (error
);
865 if (!TP_IS_CALL_CHANNEL (channel
))
867 DEBUG ("The channel is not a Call channel!");
871 priv
->call
= TP_CALL_CHANNEL (channel
);
872 tp_g_signal_connect_object (priv
->call
, "state-changed",
873 G_CALLBACK (on_call_state_changed_cb
), self
, 0);
874 tp_g_signal_connect_object (priv
->call
, "invalidated",
875 G_CALLBACK (on_call_invalidated_cb
), self
, 0);
877 g_object_notify (G_OBJECT (self
), "call-channel");
879 empathy_call_handler_start_tpfs (self
);
880 tp_call_channel_accept_async (priv
->call
, on_call_accepted_cb
, NULL
);
884 empathy_call_handler_start_call (EmpathyCallHandler
*handler
,
887 EmpathyCallHandlerPriv
*priv
= GET_PRIV (handler
);
888 TpAccountChannelRequest
*req
;
891 if (priv
->call
!= NULL
)
893 empathy_call_handler_start_tpfs (handler
);
895 if (tp_channel_get_requested (TP_CHANNEL (priv
->call
)))
897 /* accept outgoing channels immediately */
898 tp_call_channel_accept_async (priv
->call
,
899 on_call_accepted_cb
, NULL
);
903 /* accepting incoming channels when they are INITIALISED */
904 if (tp_call_channel_get_state (priv
->call
, NULL
, NULL
, NULL
) ==
905 TP_CALL_STATE_INITIALISED
)
906 tp_call_channel_accept_async (priv
->call
,
907 on_call_accepted_cb
, NULL
);
909 priv
->accept_when_initialised
= TRUE
;
915 /* No TpCallChannel (we are redialing). Request a new call channel */
916 g_assert (priv
->contact
!= NULL
);
918 account
= empathy_contact_get_account (priv
->contact
);
920 req
= empathy_call_create_call_request (account
,
921 empathy_contact_get_id (priv
->contact
), priv
->initial_video
, timestamp
);
923 tp_account_channel_request_create_and_handle_channel_async (req
, NULL
,
924 empathy_call_handler_request_cb
, handler
);
926 g_object_unref (req
);
930 * empathy_call_handler_stop_call:
931 * @handler: an #EmpathyCallHandler
933 * Closes the #EmpathyCallHandler's call and frees its resources.
936 empathy_call_handler_stop_call (EmpathyCallHandler
*handler
)
938 EmpathyCallHandlerPriv
*priv
= GET_PRIV (handler
);
940 if (priv
->call
!= NULL
)
942 tp_call_channel_hangup_async (priv
->call
,
943 TP_CALL_STATE_CHANGE_REASON_USER_REQUESTED
,
949 * empathy_call_handler_has_initial_video:
950 * @handler: an #EmpathyCallHandler
952 * Return %TRUE if the call managed by this #EmpathyCallHandler was
953 * created with video enabled
955 * Return value: %TRUE if the call was created as a video conversation.
958 empathy_call_handler_has_initial_video (EmpathyCallHandler
*handler
)
960 EmpathyCallHandlerPriv
*priv
= GET_PRIV (handler
);
962 return priv
->initial_video
;
966 empathy_call_handler_get_send_audio_codec (EmpathyCallHandler
*self
)
968 EmpathyCallHandlerPriv
*priv
= GET_PRIV (self
);
970 return priv
->send_audio_codec
;
974 empathy_call_handler_get_send_video_codec (EmpathyCallHandler
*self
)
976 EmpathyCallHandlerPriv
*priv
= GET_PRIV (self
);
978 return priv
->send_video_codec
;
982 empathy_call_handler_get_recv_audio_codecs (EmpathyCallHandler
*self
)
984 EmpathyCallHandlerPriv
*priv
= GET_PRIV (self
);
986 return priv
->recv_audio_codecs
;
990 empathy_call_handler_get_recv_video_codecs (EmpathyCallHandler
*self
)
992 EmpathyCallHandlerPriv
*priv
= GET_PRIV (self
);
994 return priv
->recv_video_codecs
;
998 empathy_call_handler_get_audio_remote_candidate (
999 EmpathyCallHandler
*self
)
1001 EmpathyCallHandlerPriv
*priv
= GET_PRIV (self
);
1003 return priv
->audio_remote_candidate
;
1007 empathy_call_handler_get_audio_local_candidate (
1008 EmpathyCallHandler
*self
)
1010 EmpathyCallHandlerPriv
*priv
= GET_PRIV (self
);
1012 return priv
->audio_local_candidate
;
1016 empathy_call_handler_get_video_remote_candidate (
1017 EmpathyCallHandler
*self
)
1019 EmpathyCallHandlerPriv
*priv
= GET_PRIV (self
);
1021 return priv
->video_remote_candidate
;
1025 empathy_call_handler_get_video_local_candidate (
1026 EmpathyCallHandler
*self
)
1028 EmpathyCallHandlerPriv
*priv
= GET_PRIV (self
);
1030 return priv
->video_local_candidate
;
1034 empathy_call_handler_get_contact (EmpathyCallHandler
*self
)
1036 return self
->priv
->contact
;