]> git.0d.be Git - empathy.git/blob - src/empathy-streamed-media-handler.c
Merge remote-tracking branch 'jonny/ft'
[empathy.git] / src / empathy-streamed-media-handler.c
1 /*
2  * empathy-streamed-media-handler.c - Source for EmpathyStreamedMediaHandler
3  * Copyright (C) 2008-2011 Collabora Ltd.
4  * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk>
5  *
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.
10  *
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.
15  *
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
19  */
20
21
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include <telepathy-glib/account-channel-request.h>
26 #include <telepathy-glib/util.h>
27 #include <telepathy-glib/interfaces.h>
28
29 #include <telepathy-farsight/channel.h>
30 #include <telepathy-farsight/stream.h>
31
32 #include <libempathy/empathy-utils.h>
33
34 #include <libempathy-gtk/empathy-call-utils.h>
35
36 #include "empathy-streamed-media-handler.h"
37
38 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
39 #include <libempathy/empathy-debug.h>
40
41 G_DEFINE_TYPE(EmpathyStreamedMediaHandler, empathy_streamed_media_handler, G_TYPE_OBJECT)
42
43 /* signal enum */
44 enum {
45   CONFERENCE_ADDED,
46   SRC_PAD_ADDED,
47   SINK_PAD_ADDED,
48   REQUEST_RESOURCE,
49   CLOSED,
50   STREAM_CLOSED,
51   CANDIDATES_CHANGED,
52   LAST_SIGNAL
53 };
54
55 static guint signals[LAST_SIGNAL] = {0};
56
57 enum {
58   PROP_TP_STREAMED_MEDIA = 1,
59   PROP_GST_BUS,
60   PROP_CONTACT,
61   PROP_INITIAL_AUDIO,
62   PROP_INITIAL_VIDEO,
63   PROP_SEND_AUDIO_CODEC,
64   PROP_SEND_VIDEO_CODEC,
65   PROP_RECV_AUDIO_CODECS,
66   PROP_RECV_VIDEO_CODECS,
67   PROP_AUDIO_REMOTE_CANDIDATE,
68   PROP_VIDEO_REMOTE_CANDIDATE,
69   PROP_AUDIO_LOCAL_CANDIDATE,
70   PROP_VIDEO_LOCAL_CANDIDATE,
71 };
72
73 /* private structure */
74
75 typedef struct {
76   gboolean dispose_has_run;
77   EmpathyTpStreamedMedia *call;
78   EmpathyContact *contact;
79   TfChannel *tfchannel;
80   gboolean initial_audio;
81   gboolean initial_video;
82
83   FsCodec *send_audio_codec;
84   FsCodec *send_video_codec;
85   GList *recv_audio_codecs;
86   GList *recv_video_codecs;
87   FsCandidate *audio_remote_candidate;
88   FsCandidate *video_remote_candidate;
89   FsCandidate *audio_local_candidate;
90   FsCandidate *video_local_candidate;
91 } EmpathyStreamedMediaHandlerPriv;
92
93 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyStreamedMediaHandler)
94
95 static void
96 empathy_streamed_media_handler_dispose (GObject *object)
97 {
98   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (object);
99
100   if (priv->dispose_has_run)
101     return;
102
103   priv->dispose_has_run = TRUE;
104
105   if (priv->contact != NULL)
106     g_object_unref (priv->contact);
107
108   priv->contact = NULL;
109
110   if (priv->tfchannel != NULL)
111     g_object_unref (priv->tfchannel);
112
113   priv->tfchannel = NULL;
114
115   if (priv->call != NULL)
116     {
117       empathy_tp_streamed_media_close (priv->call);
118       g_object_unref (priv->call);
119     }
120
121   priv->call = NULL;
122
123   /* release any references held by the object here */
124   if (G_OBJECT_CLASS (empathy_streamed_media_handler_parent_class)->dispose)
125     G_OBJECT_CLASS (empathy_streamed_media_handler_parent_class)->dispose (object);
126 }
127
128 static void
129 empathy_streamed_media_handler_finalize (GObject *object)
130 {
131   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (object);
132
133   fs_codec_destroy (priv->send_audio_codec);
134   fs_codec_destroy (priv->send_video_codec);
135   fs_codec_list_destroy (priv->recv_audio_codecs);
136   fs_codec_list_destroy (priv->recv_video_codecs);
137   fs_candidate_destroy (priv->audio_remote_candidate);
138   fs_candidate_destroy (priv->video_remote_candidate);
139   fs_candidate_destroy (priv->audio_local_candidate);
140   fs_candidate_destroy (priv->video_local_candidate);
141
142   if (G_OBJECT_CLASS (empathy_streamed_media_handler_parent_class)->finalize)
143     G_OBJECT_CLASS (empathy_streamed_media_handler_parent_class)->finalize (object);
144 }
145
146 static void
147 empathy_streamed_media_handler_init (EmpathyStreamedMediaHandler *obj)
148 {
149   EmpathyStreamedMediaHandlerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (obj,
150     EMPATHY_TYPE_STREAMED_MEDIA_HANDLER, EmpathyStreamedMediaHandlerPriv);
151
152   obj->priv = priv;
153 }
154
155 static void
156 empathy_streamed_media_handler_constructed (GObject *object)
157 {
158   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (object);
159
160   if (priv->contact == NULL)
161     {
162       g_object_get (priv->call, "contact", &(priv->contact), NULL);
163     }
164 }
165
166 static void
167 empathy_streamed_media_handler_set_property (GObject *object,
168   guint property_id, const GValue *value, GParamSpec *pspec)
169 {
170   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (object);
171
172   switch (property_id)
173     {
174       case PROP_CONTACT:
175         priv->contact = g_value_dup_object (value);
176         break;
177       case PROP_TP_STREAMED_MEDIA:
178         priv->call = g_value_dup_object (value);
179         break;
180       case PROP_INITIAL_AUDIO:
181         priv->initial_audio = g_value_get_boolean (value);
182         break;
183       case PROP_INITIAL_VIDEO:
184         priv->initial_video = g_value_get_boolean (value);
185         break;
186       default:
187         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
188     }
189 }
190
191 static void
192 empathy_streamed_media_handler_get_property (GObject *object,
193   guint property_id, GValue *value, GParamSpec *pspec)
194 {
195   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (object);
196
197   switch (property_id)
198     {
199       case PROP_CONTACT:
200         g_value_set_object (value, priv->contact);
201         break;
202       case PROP_TP_STREAMED_MEDIA:
203         g_value_set_object (value, priv->call);
204         break;
205       case PROP_INITIAL_AUDIO:
206         g_value_set_boolean (value, priv->initial_audio);
207         break;
208       case PROP_INITIAL_VIDEO:
209         g_value_set_boolean (value, priv->initial_video);
210         break;
211       case PROP_SEND_AUDIO_CODEC:
212         g_value_set_boxed (value, priv->send_audio_codec);
213         break;
214       case PROP_SEND_VIDEO_CODEC:
215         g_value_set_boxed (value, priv->send_video_codec);
216         break;
217       case PROP_RECV_AUDIO_CODECS:
218         g_value_set_boxed (value, priv->recv_audio_codecs);
219         break;
220       case PROP_RECV_VIDEO_CODECS:
221         g_value_set_boxed (value, priv->recv_video_codecs);
222         break;
223       case PROP_AUDIO_REMOTE_CANDIDATE:
224         g_value_set_boxed (value, priv->audio_remote_candidate);
225         break;
226       case PROP_VIDEO_REMOTE_CANDIDATE:
227         g_value_set_boxed (value, priv->video_remote_candidate);
228         break;
229       case PROP_AUDIO_LOCAL_CANDIDATE:
230         g_value_set_boxed (value, priv->audio_local_candidate);
231         break;
232       case PROP_VIDEO_LOCAL_CANDIDATE:
233         g_value_set_boxed (value, priv->video_local_candidate);
234         break;
235       default:
236         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
237     }
238 }
239
240
241 static void
242 empathy_streamed_media_handler_class_init (EmpathyStreamedMediaHandlerClass *klass)
243 {
244   GObjectClass *object_class = G_OBJECT_CLASS (klass);
245   GParamSpec *param_spec;
246
247   g_type_class_add_private (klass, sizeof (EmpathyStreamedMediaHandlerPriv));
248
249   object_class->constructed = empathy_streamed_media_handler_constructed;
250   object_class->set_property = empathy_streamed_media_handler_set_property;
251   object_class->get_property = empathy_streamed_media_handler_get_property;
252   object_class->dispose = empathy_streamed_media_handler_dispose;
253   object_class->finalize = empathy_streamed_media_handler_finalize;
254
255   param_spec = g_param_spec_object ("contact",
256     "contact", "The remote contact",
257     EMPATHY_TYPE_CONTACT,
258     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
259   g_object_class_install_property (object_class, PROP_CONTACT, param_spec);
260
261   param_spec = g_param_spec_object ("tp-call",
262     "tp-call", "The calls channel wrapper",
263     EMPATHY_TYPE_TP_STREAMED_MEDIA,
264     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
265   g_object_class_install_property (object_class, PROP_TP_STREAMED_MEDIA, param_spec);
266
267   param_spec = g_param_spec_boolean ("initial-audio",
268     "initial-audio", "Whether the call should start with audio",
269     TRUE,
270     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
271   g_object_class_install_property (object_class, PROP_INITIAL_AUDIO,
272       param_spec);
273
274   param_spec = g_param_spec_boolean ("initial-video",
275     "initial-video", "Whether the call should start with video",
276     FALSE,
277     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
278   g_object_class_install_property (object_class, PROP_INITIAL_VIDEO,
279     param_spec);
280
281   param_spec = g_param_spec_boxed ("send-audio-codec",
282     "send audio codec", "Codec used to encode the outgoing video stream",
283     FS_TYPE_CODEC,
284     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
285   g_object_class_install_property (object_class, PROP_SEND_AUDIO_CODEC,
286     param_spec);
287
288   param_spec = g_param_spec_boxed ("send-video-codec",
289     "send video codec", "Codec used to encode the outgoing video stream",
290     FS_TYPE_CODEC,
291     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
292   g_object_class_install_property (object_class, PROP_SEND_VIDEO_CODEC,
293     param_spec);
294
295   param_spec = g_param_spec_boxed ("recv-audio-codecs",
296     "recvs audio codec", "Codecs used to decode the incoming audio stream",
297     FS_TYPE_CODEC_LIST,
298     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
299   g_object_class_install_property (object_class, PROP_RECV_AUDIO_CODECS,
300     param_spec);
301
302   param_spec = g_param_spec_boxed ("recv-video-codecs",
303     "recvs video codec", "Codecs used to decode the incoming video stream",
304     FS_TYPE_CODEC_LIST,
305     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
306   g_object_class_install_property (object_class, PROP_RECV_VIDEO_CODECS,
307     param_spec);
308
309   param_spec = g_param_spec_boxed ("audio-remote-candidate",
310     "audio remote candidate",
311     "Remote candidate used for the audio stream",
312     FS_TYPE_CANDIDATE,
313     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
314   g_object_class_install_property (object_class,
315       PROP_AUDIO_REMOTE_CANDIDATE, param_spec);
316
317   param_spec = g_param_spec_boxed ("video-remote-candidate",
318     "video remote candidate",
319     "Remote candidate used for the video stream",
320     FS_TYPE_CANDIDATE,
321     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
322   g_object_class_install_property (object_class,
323       PROP_VIDEO_REMOTE_CANDIDATE, param_spec);
324
325   param_spec = g_param_spec_boxed ("audio-local-candidate",
326     "audio local candidate",
327     "Local candidate used for the audio stream",
328     FS_TYPE_CANDIDATE,
329     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
330   g_object_class_install_property (object_class,
331       PROP_AUDIO_REMOTE_CANDIDATE, param_spec);
332
333   param_spec = g_param_spec_boxed ("video-local-candidate",
334     "video local candidate",
335     "Local candidate used for the video stream",
336     FS_TYPE_CANDIDATE,
337     G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
338   g_object_class_install_property (object_class,
339       PROP_VIDEO_REMOTE_CANDIDATE, param_spec);
340
341   signals[CONFERENCE_ADDED] =
342     g_signal_new ("conference-added", G_TYPE_FROM_CLASS (klass),
343       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
344       g_cclosure_marshal_generic,
345       G_TYPE_NONE,
346       1, FS_TYPE_CONFERENCE);
347
348   signals[SRC_PAD_ADDED] =
349     g_signal_new ("src-pad-added", G_TYPE_FROM_CLASS (klass),
350       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
351       g_cclosure_marshal_generic,
352       G_TYPE_BOOLEAN,
353       2, GST_TYPE_PAD, G_TYPE_UINT);
354
355   signals[SINK_PAD_ADDED] =
356     g_signal_new ("sink-pad-added", G_TYPE_FROM_CLASS (klass),
357       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
358       g_cclosure_marshal_generic,
359       G_TYPE_BOOLEAN,
360       2, GST_TYPE_PAD, G_TYPE_UINT);
361
362   signals[REQUEST_RESOURCE] =
363     g_signal_new ("request-resource", G_TYPE_FROM_CLASS (klass),
364       G_SIGNAL_RUN_LAST, 0,
365       g_signal_accumulator_true_handled, NULL,
366       g_cclosure_marshal_generic,
367       G_TYPE_BOOLEAN, 2, G_TYPE_UINT, G_TYPE_UINT);
368
369   signals[CLOSED] =
370     g_signal_new ("closed", G_TYPE_FROM_CLASS (klass),
371       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
372       g_cclosure_marshal_generic,
373       G_TYPE_NONE,
374       0);
375
376   signals[STREAM_CLOSED] =
377     g_signal_new ("stream-closed", G_TYPE_FROM_CLASS (klass),
378       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
379       g_cclosure_marshal_generic,
380       G_TYPE_NONE, 1, TF_TYPE_STREAM);
381
382   signals[CANDIDATES_CHANGED] =
383     g_signal_new ("candidates-changed", G_TYPE_FROM_CLASS (klass),
384       G_SIGNAL_RUN_LAST, 0, NULL, NULL,
385       g_cclosure_marshal_generic,
386       G_TYPE_NONE, 1, G_TYPE_UINT);
387 }
388
389 /**
390  * empathy_streamed_media_handler_new_for_contact:
391  * @contact: an #EmpathyContact
392  *
393  * Creates a new #EmpathyStreamedMediaHandler with contact @contact.
394  *
395  * Return value: a new #EmpathyStreamedMediaHandler
396  */
397 EmpathyStreamedMediaHandler *
398 empathy_streamed_media_handler_new_for_contact (EmpathyContact *contact)
399 {
400   return EMPATHY_STREAMED_MEDIA_HANDLER (g_object_new (EMPATHY_TYPE_STREAMED_MEDIA_HANDLER,
401     "contact", contact, NULL));
402 }
403
404 EmpathyStreamedMediaHandler *
405 empathy_streamed_media_handler_new_for_channel (EmpathyTpStreamedMedia *call)
406 {
407   return EMPATHY_STREAMED_MEDIA_HANDLER (g_object_new (EMPATHY_TYPE_STREAMED_MEDIA_HANDLER,
408     "tp-call", call,
409     "initial-video", empathy_tp_streamed_media_is_receiving_video (call),
410     NULL));
411 }
412
413 static void
414 update_sending_codec (EmpathyStreamedMediaHandler *self,
415     FsCodec *codec,
416     FsSession *session)
417 {
418   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
419   FsMediaType type;
420
421   if (codec == NULL || session == NULL)
422     return;
423
424   g_object_get (session, "media-type", &type, NULL);
425
426   if (type == FS_MEDIA_TYPE_AUDIO)
427     {
428       priv->send_audio_codec = fs_codec_copy (codec);
429       g_object_notify (G_OBJECT (self), "send-audio-codec");
430     }
431   else if (type == FS_MEDIA_TYPE_VIDEO)
432     {
433       priv->send_video_codec = fs_codec_copy (codec);
434       g_object_notify (G_OBJECT (self), "send-video-codec");
435     }
436 }
437
438 static void
439 update_receiving_codec (EmpathyStreamedMediaHandler *self,
440     GList *codecs,
441     FsStream *stream)
442 {
443   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
444   FsSession *session;
445   FsMediaType type;
446
447   if (codecs == NULL || stream == NULL)
448     return;
449
450   g_object_get (stream, "session", &session, NULL);
451   if (session == NULL)
452     return;
453
454   g_object_get (session, "media-type", &type, NULL);
455
456   if (type == FS_MEDIA_TYPE_AUDIO)
457     {
458       priv->recv_audio_codecs = fs_codec_list_copy (codecs);
459       g_object_notify (G_OBJECT (self), "recv-audio-codecs");
460     }
461   else if (type == FS_MEDIA_TYPE_VIDEO)
462     {
463       priv->recv_video_codecs = fs_codec_list_copy (codecs);
464       g_object_notify (G_OBJECT (self), "recv-video-codecs");
465     }
466
467   g_object_unref (session);
468 }
469
470 static void
471 update_candidates (EmpathyStreamedMediaHandler *self,
472     FsCandidate *remote_candidate,
473     FsCandidate *local_candidate,
474     FsStream *stream)
475 {
476   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
477   FsSession *session;
478   FsMediaType type;
479
480   if (stream == NULL)
481     return;
482
483   g_object_get (stream, "session", &session, NULL);
484   if (session == NULL)
485     return;
486
487   g_object_get (session, "media-type", &type, NULL);
488
489   if (type == FS_MEDIA_TYPE_AUDIO)
490     {
491       if (remote_candidate != NULL)
492         {
493           fs_candidate_destroy (priv->audio_remote_candidate);
494           priv->audio_remote_candidate = fs_candidate_copy (remote_candidate);
495           g_object_notify (G_OBJECT (self), "audio-remote-candidate");
496         }
497
498       if (local_candidate != NULL)
499         {
500           fs_candidate_destroy (priv->audio_local_candidate);
501           priv->audio_local_candidate = fs_candidate_copy (local_candidate);
502           g_object_notify (G_OBJECT (self), "audio-local-candidate");
503         }
504
505       g_signal_emit (G_OBJECT (self), signals[CANDIDATES_CHANGED], 0,
506           FS_MEDIA_TYPE_AUDIO);
507     }
508   else if (type == FS_MEDIA_TYPE_VIDEO)
509     {
510       if (remote_candidate != NULL)
511         {
512           fs_candidate_destroy (priv->video_remote_candidate);
513           priv->video_remote_candidate = fs_candidate_copy (remote_candidate);
514           g_object_notify (G_OBJECT (self), "video-remote-candidate");
515         }
516
517       if (local_candidate != NULL)
518         {
519           fs_candidate_destroy (priv->video_local_candidate);
520           priv->video_local_candidate = fs_candidate_copy (local_candidate);
521           g_object_notify (G_OBJECT (self), "video-local-candidate");
522         }
523
524       g_signal_emit (G_OBJECT (self), signals[CANDIDATES_CHANGED], 0,
525           FS_MEDIA_TYPE_VIDEO);
526     }
527
528   g_object_unref (session);
529 }
530
531 void
532 empathy_streamed_media_handler_bus_message (EmpathyStreamedMediaHandler *handler,
533   GstBus *bus, GstMessage *message)
534 {
535   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (handler);
536   const GstStructure *s = gst_message_get_structure (message);
537
538   if (priv->tfchannel == NULL)
539     return;
540
541   if (s != NULL &&
542       gst_structure_has_name (s, "farsight-send-codec-changed"))
543     {
544       const GValue *val;
545       FsCodec *codec;
546       FsSession *session;
547
548       val = gst_structure_get_value (s, "codec");
549       codec = g_value_get_boxed (val);
550
551       val = gst_structure_get_value (s, "session");
552       session = g_value_get_object (val);
553
554       update_sending_codec (handler, codec, session);
555     }
556   else if (s != NULL &&
557       gst_structure_has_name (s, "farsight-recv-codecs-changed"))
558     {
559       const GValue *val;
560       GList *codecs;
561       FsStream *stream;
562
563       val = gst_structure_get_value (s, "codecs");
564       codecs = g_value_get_boxed (val);
565
566       val = gst_structure_get_value (s, "stream");
567       stream = g_value_get_object (val);
568
569       update_receiving_codec (handler, codecs, stream);
570     }
571   else if (s != NULL &&
572       gst_structure_has_name (s, "farsight-new-active-candidate-pair"))
573     {
574       const GValue *val;
575       FsCandidate *remote_candidate, *local_candidate;
576       FsStream *stream;
577
578       val = gst_structure_get_value (s, "remote-candidate");
579       remote_candidate = g_value_get_boxed (val);
580
581       val = gst_structure_get_value (s, "local-candidate");
582       local_candidate = g_value_get_boxed (val);
583
584       val = gst_structure_get_value (s, "stream");
585       stream = g_value_get_object (val);
586
587       update_candidates (handler, remote_candidate, local_candidate, stream);
588     }
589
590   tf_channel_bus_message (priv->tfchannel, message);
591 }
592
593 static void
594 empathy_streamed_media_handler_tf_channel_session_created_cb (TfChannel *tfchannel,
595   FsConference *conference, FsParticipant *participant,
596   EmpathyStreamedMediaHandler *self)
597 {
598   g_signal_emit (G_OBJECT (self), signals[CONFERENCE_ADDED], 0,
599     GST_ELEMENT (conference));
600 }
601
602 static gboolean
603 src_pad_added_error_idle (gpointer data)
604 {
605   TfStream *stream = data;
606
607   tf_stream_error (stream, TP_MEDIA_STREAM_ERROR_MEDIA_ERROR,
608       "Could not link sink");
609   g_object_unref (stream);
610
611   return FALSE;
612 }
613
614 static void
615 empathy_streamed_media_handler_tf_stream_src_pad_added_cb (TfStream *stream,
616   GstPad *pad, FsCodec *codec, EmpathyStreamedMediaHandler  *handler)
617 {
618   guint media_type;
619   gboolean retval;
620
621   g_object_get (stream, "media-type", &media_type, NULL);
622
623   g_signal_emit (G_OBJECT (handler), signals[SRC_PAD_ADDED], 0,
624       pad, media_type, &retval);
625
626   if (!retval)
627     g_idle_add (src_pad_added_error_idle, g_object_ref (stream));
628 }
629
630
631 static gboolean
632 empathy_streamed_media_handler_tf_stream_request_resource_cb (TfStream *stream,
633   guint direction, EmpathyTpStreamedMedia *call)
634 {
635   gboolean ret;
636   guint media_type;
637
638   g_object_get (G_OBJECT (stream), "media-type", &media_type, NULL);
639
640   g_signal_emit (G_OBJECT (call),
641     signals[REQUEST_RESOURCE], 0, media_type, direction, &ret);
642
643   return ret;
644 }
645
646 static void
647 empathy_streamed_media_handler_tf_stream_closed_cb (TfStream *stream,
648   EmpathyStreamedMediaHandler *handler)
649 {
650   g_signal_emit (handler, signals[STREAM_CLOSED], 0, stream);
651 }
652
653 static void
654 empathy_streamed_media_handler_tf_channel_stream_created_cb (TfChannel *tfchannel,
655   TfStream *stream, EmpathyStreamedMediaHandler *handler)
656 {
657   guint media_type;
658   GstPad *spad;
659   gboolean retval;
660   FsStream *fs_stream;
661   GList *codecs;
662   FsSession *session;
663   FsCodec *codec;
664
665   g_signal_connect (stream, "src-pad-added",
666       G_CALLBACK (empathy_streamed_media_handler_tf_stream_src_pad_added_cb), handler);
667   g_signal_connect (stream, "request-resource",
668       G_CALLBACK (empathy_streamed_media_handler_tf_stream_request_resource_cb),
669         handler);
670   g_signal_connect (stream, "closed",
671       G_CALLBACK (empathy_streamed_media_handler_tf_stream_closed_cb), handler);
672
673   g_object_get (stream, "media-type", &media_type,
674     "sink-pad", &spad, NULL);
675
676   g_signal_emit (G_OBJECT (handler), signals[SINK_PAD_ADDED], 0,
677       spad, media_type, &retval);
678
679  if (!retval)
680       tf_stream_error (stream, TP_MEDIA_STREAM_ERROR_MEDIA_ERROR,
681           "Could not link source");
682
683  /* Get sending codec */
684  g_object_get (stream, "farsight-session", &session, NULL);
685  g_object_get (session, "current-send-codec", &codec, NULL);
686
687  update_sending_codec (handler, codec, session);
688
689  tp_clear_object (&session);
690  tp_clear_object (&codec);
691
692  /* Get receiving codec */
693  g_object_get (stream, "farsight-stream", &fs_stream, NULL);
694  g_object_get (fs_stream, "current-recv-codecs", &codecs, NULL);
695
696  update_receiving_codec (handler, codecs, fs_stream);
697
698  fs_codec_list_destroy (codecs);
699  tp_clear_object (&fs_stream);
700
701  gst_object_unref (spad);
702 }
703
704 static void
705 empathy_streamed_media_handler_tf_channel_closed_cb (TfChannel *tfchannel,
706   EmpathyStreamedMediaHandler *handler)
707 {
708   g_signal_emit (G_OBJECT (handler), signals[CLOSED], 0);
709 }
710
711 static void
712 empathy_streamed_media_handler_start_tpfs (EmpathyStreamedMediaHandler *self)
713 {
714   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
715   TpChannel *channel;
716
717   g_object_get (priv->call, "channel", &channel, NULL);
718
719   g_assert (channel != NULL);
720
721   priv->tfchannel = tf_channel_new (channel);
722
723   /* Set up the telepathy farsight channel */
724   g_signal_connect (priv->tfchannel, "session-created",
725       G_CALLBACK (empathy_streamed_media_handler_tf_channel_session_created_cb), self);
726   g_signal_connect (priv->tfchannel, "stream-created",
727       G_CALLBACK (empathy_streamed_media_handler_tf_channel_stream_created_cb), self);
728   g_signal_connect (priv->tfchannel, "closed",
729       G_CALLBACK (empathy_streamed_media_handler_tf_channel_closed_cb), self);
730
731   g_object_unref (channel);
732 }
733
734 static void
735 empathy_streamed_media_handler_request_cb (GObject *source,
736     GAsyncResult *result,
737     gpointer user_data)
738 {
739   EmpathyStreamedMediaHandler *self = EMPATHY_STREAMED_MEDIA_HANDLER (user_data);
740   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
741   TpChannel *channel;
742   GError *error = NULL;
743   TpAccountChannelRequest *req = TP_ACCOUNT_CHANNEL_REQUEST (source);
744   TpAccount *account;
745
746   channel = tp_account_channel_request_create_and_handle_channel_finish (req,
747       result, NULL, &error);
748   if (channel == NULL)
749     {
750       DEBUG ("Failed to create the channel: %s", error->message);
751       g_error_free (error);
752       return;
753     }
754
755   account = tp_account_channel_request_get_account (req);
756
757   priv->call = empathy_tp_streamed_media_new (account, channel);
758
759   g_object_notify (G_OBJECT (self), "tp-call");
760
761   empathy_streamed_media_handler_start_tpfs (self);
762
763   g_object_unref (channel);
764 }
765
766 void
767 empathy_streamed_media_handler_start_call (EmpathyStreamedMediaHandler *handler,
768     gint64 timestamp)
769 {
770   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (handler);
771   TpAccountChannelRequest *req;
772   TpAccount *account;
773   GHashTable *request;
774
775   if (priv->call != NULL)
776     {
777       empathy_streamed_media_handler_start_tpfs (handler);
778       empathy_tp_streamed_media_accept_incoming_call (priv->call);
779       return;
780     }
781
782   /* No TpStreamedMedia object (we are redialing). Request a new media channel that
783    * will be used to create a new EmpathyTpStreamedMedia. */
784   g_assert (priv->contact != NULL);
785
786   account = empathy_contact_get_account (priv->contact);
787   request = empathy_call_create_streamed_media_request (
788       empathy_contact_get_id (priv->contact),
789       priv->initial_audio, priv->initial_video);
790
791   req = tp_account_channel_request_new (account, request, timestamp);
792
793   tp_account_channel_request_create_and_handle_channel_async (req, NULL,
794       empathy_streamed_media_handler_request_cb, handler);
795
796   g_object_unref (req);
797   g_hash_table_unref (request);
798 }
799
800 /**
801  * empathy_streamed_media_handler_stop_call:
802  * @handler: an #EmpathyStreamedMediaHandler
803  *
804  * Closes the #EmpathyStreamedMediaHandler's call and frees its resources.
805  */
806 void
807 empathy_streamed_media_handler_stop_call (EmpathyStreamedMediaHandler *handler)
808 {
809   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (handler);
810
811   if (priv->call != NULL)
812     {
813       empathy_tp_streamed_media_leave (priv->call);
814       g_object_unref (priv->call);
815     }
816
817   priv->call = NULL;
818 }
819
820 /**
821  * empathy_streamed_media_handler_has_initial_video:
822  * @handler: an #EmpathyStreamedMediaHandler
823  *
824  * Return %TRUE if the call managed by this #EmpathyStreamedMediaHandler was
825  * created with video enabled
826  *
827  * Return value: %TRUE if the call was created as a video conversation.
828  */
829 gboolean
830 empathy_streamed_media_handler_has_initial_video (EmpathyStreamedMediaHandler *handler)
831 {
832   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (handler);
833
834   return priv->initial_video;
835 }
836
837 FsCodec *
838 empathy_streamed_media_handler_get_send_audio_codec (EmpathyStreamedMediaHandler *self)
839 {
840   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
841
842   return priv->send_audio_codec;
843 }
844
845 FsCodec *
846 empathy_streamed_media_handler_get_send_video_codec (EmpathyStreamedMediaHandler *self)
847 {
848   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
849
850   return priv->send_video_codec;
851 }
852
853 GList *
854 empathy_streamed_media_handler_get_recv_audio_codecs (EmpathyStreamedMediaHandler *self)
855 {
856   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
857
858   return priv->recv_audio_codecs;
859 }
860
861 GList *
862 empathy_streamed_media_handler_get_recv_video_codecs (EmpathyStreamedMediaHandler *self)
863 {
864   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
865
866   return priv->recv_video_codecs;
867 }
868
869 FsCandidate *
870 empathy_streamed_media_handler_get_audio_remote_candidate (
871     EmpathyStreamedMediaHandler *self)
872 {
873   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
874
875   return priv->audio_remote_candidate;
876 }
877
878 FsCandidate *
879 empathy_streamed_media_handler_get_audio_local_candidate (
880     EmpathyStreamedMediaHandler *self)
881 {
882   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
883
884   return priv->audio_local_candidate;
885 }
886
887 FsCandidate *
888 empathy_streamed_media_handler_get_video_remote_candidate (
889     EmpathyStreamedMediaHandler *self)
890 {
891   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
892
893   return priv->video_remote_candidate;
894 }
895
896 FsCandidate *
897 empathy_streamed_media_handler_get_video_local_candidate (
898     EmpathyStreamedMediaHandler *self)
899 {
900   EmpathyStreamedMediaHandlerPriv *priv = GET_PRIV (self);
901
902   return priv->video_local_candidate;
903 }