2 * empathy-call-window.c - Source for EmpathyCallWindow
3 * Copyright (C) 2008 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
29 #include <glib/gi18n.h>
31 #include <telepathy-farsight/channel.h>
33 #include <libempathy/empathy-utils.h>
34 #include <libempathy-gtk/empathy-video-widget.h>
35 #include <libempathy-gtk/empathy-audio-src.h>
36 #include <libempathy-gtk/empathy-audio-sink.h>
37 #include <libempathy-gtk/empathy-video-src.h>
38 #include <libempathy-gtk/empathy-ui-utils.h>
40 #include "empathy-call-window.h"
42 #include "empathy-sidebar.h"
44 #define BUTTON_ID "empathy-call-dtmf-button-id"
46 G_DEFINE_TYPE(EmpathyCallWindow
, empathy_call_window
, GTK_TYPE_WINDOW
)
55 static guint signals
[LAST_SIGNAL
] = {0};
59 PROP_CALL_HANDLER
= 1,
62 /* private structure */
63 typedef struct _EmpathyCallWindowPriv EmpathyCallWindowPriv
;
65 struct _EmpathyCallWindowPriv
67 gboolean dispose_has_run
;
68 EmpathyCallHandler
*handler
;
72 GtkUIManager
*ui_manager
;
73 GtkWidget
*video_output
;
74 GtkWidget
*video_preview
;
76 GtkWidget
*sidebar_button
;
78 GtkWidget
*volume_button
;
79 GtkWidget
*mic_button
;
80 GtkWidget
*camera_button
;
83 GtkAction
*send_video
;
86 GtkAdjustment
*audio_input_adj
;
88 GtkWidget
*dtmf_panel
;
90 GstElement
*video_input
;
91 GstElement
*audio_input
;
92 GstElement
*audio_output
;
94 GstElement
*video_tee
;
97 GstElement
*liveadder
;
104 GtkWidget
*video_contrast
;
105 GtkWidget
*video_brightness
;
106 GtkWidget
*video_gamma
;
109 gboolean call_started
;
112 #define GET_PRIV(o) \
113 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_CALL_WINDOW, \
114 EmpathyCallWindowPriv))
116 static void empathy_call_window_realized_cb (GtkWidget
*widget
,
117 EmpathyCallWindow
*window
);
119 static gboolean
empathy_call_window_delete_cb (GtkWidget
*widget
,
120 GdkEvent
*event
, EmpathyCallWindow
*window
);
122 static void empathy_call_window_sidebar_toggled_cb (GtkToggleButton
*toggle
,
123 EmpathyCallWindow
*window
);
125 static void empathy_call_window_camera_toggled_cb (GtkToggleToolButton
*toggle
,
126 EmpathyCallWindow
*window
);
128 static void empathy_call_window_send_video_toggled_cb (GtkToggleAction
*toggle
,
129 EmpathyCallWindow
*window
);
131 static void empathy_call_window_show_preview_toggled_cb (GtkToggleAction
*toggle
,
132 EmpathyCallWindow
*window
);
134 static void empathy_call_window_mic_toggled_cb (
135 GtkToggleToolButton
*toggle
, EmpathyCallWindow
*window
);
137 static void empathy_call_window_sidebar_hidden_cb (EmpathySidebar
*sidebar
,
138 EmpathyCallWindow
*window
);
140 static void empathy_call_window_hangup_cb (gpointer object
,
141 EmpathyCallWindow
*window
);
143 static void empathy_call_window_status_message (EmpathyCallWindow
*window
,
146 static gboolean
empathy_call_window_bus_message (GstBus
*bus
,
147 GstMessage
*message
, gpointer user_data
);
150 empathy_call_window_volume_changed_cb (GtkScaleButton
*button
,
151 gdouble value
, EmpathyCallWindow
*window
);
154 empathy_call_window_setup_toolbar (EmpathyCallWindow
*self
)
156 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
157 GtkToolItem
*tool_item
;
159 /* Add an empty expanded GtkToolItem so the volume button is at the end of
161 tool_item
= gtk_tool_item_new ();
162 gtk_tool_item_set_expand (tool_item
, TRUE
);
163 gtk_widget_show (GTK_WIDGET (tool_item
));
164 gtk_toolbar_insert (GTK_TOOLBAR (priv
->toolbar
), tool_item
, -1);
166 priv
->volume_button
= gtk_volume_button_new ();
167 /* FIXME listen to the audiosinks signals and update the button according to
168 * that, for now starting out at 1.0 and assuming only the app changes the
170 gtk_scale_button_set_value (GTK_SCALE_BUTTON (priv
->volume_button
), 1.0);
171 g_signal_connect (G_OBJECT (priv
->volume_button
), "value-changed",
172 G_CALLBACK (empathy_call_window_volume_changed_cb
), self
);
174 tool_item
= gtk_tool_item_new ();
175 gtk_container_add (GTK_CONTAINER (tool_item
), priv
->volume_button
);
176 gtk_widget_show_all (GTK_WIDGET (tool_item
));
177 gtk_toolbar_insert (GTK_TOOLBAR (priv
->toolbar
), tool_item
, -1);
181 dtmf_button_pressed_cb (GtkButton
*button
, EmpathyCallWindow
*window
)
183 EmpathyCallWindowPriv
*priv
= GET_PRIV (window
);
188 g_object_get (priv
->handler
, "tp-call", &call
, NULL
);
190 button_quark
= g_quark_from_static_string (BUTTON_ID
);
191 event
= GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (button
),
194 empathy_tp_call_start_tone (call
, event
);
196 g_object_unref (call
);
200 dtmf_button_released_cb (GtkButton
*button
, EmpathyCallWindow
*window
)
202 EmpathyCallWindowPriv
*priv
= GET_PRIV (window
);
205 g_object_get (priv
->handler
, "tp-call", &call
, NULL
);
207 empathy_tp_call_stop_tone (call
);
209 g_object_unref (call
);
213 empathy_call_window_create_dtmf (EmpathyCallWindow
*self
)
221 } dtmfbuttons
[] = { { "1", TP_DTMF_EVENT_DIGIT_1
},
222 { "2", TP_DTMF_EVENT_DIGIT_2
},
223 { "3", TP_DTMF_EVENT_DIGIT_3
},
224 { "4", TP_DTMF_EVENT_DIGIT_4
},
225 { "5", TP_DTMF_EVENT_DIGIT_5
},
226 { "6", TP_DTMF_EVENT_DIGIT_6
},
227 { "7", TP_DTMF_EVENT_DIGIT_7
},
228 { "8", TP_DTMF_EVENT_DIGIT_8
},
229 { "9", TP_DTMF_EVENT_DIGIT_9
},
230 { "#", TP_DTMF_EVENT_HASH
},
231 { "0", TP_DTMF_EVENT_DIGIT_0
},
232 { "*", TP_DTMF_EVENT_ASTERISK
},
235 button_quark
= g_quark_from_static_string (BUTTON_ID
);
237 table
= gtk_table_new (4, 3, TRUE
);
239 for (i
= 0; dtmfbuttons
[i
].label
!= NULL
; i
++)
241 GtkWidget
*button
= gtk_button_new_with_label (dtmfbuttons
[i
].label
);
242 gtk_table_attach (GTK_TABLE (table
), button
, i
% 3, i
% 3 + 1,
243 i
/3, i
/3 + 1, GTK_EXPAND
| GTK_FILL
, GTK_EXPAND
| GTK_FILL
, 1, 1);
245 g_object_set_qdata (G_OBJECT (button
), button_quark
,
246 GUINT_TO_POINTER (dtmfbuttons
[i
].event
));
248 g_signal_connect (G_OBJECT (button
), "pressed",
249 G_CALLBACK (dtmf_button_pressed_cb
), self
);
250 g_signal_connect (G_OBJECT (button
), "released",
251 G_CALLBACK (dtmf_button_released_cb
), self
);
258 empathy_call_window_create_video_input_add_slider (EmpathyCallWindow
*self
,
259 gchar
*label_text
, GtkWidget
*bin
)
261 GtkWidget
*vbox
= gtk_vbox_new (FALSE
, 2);
262 GtkWidget
*scale
= gtk_vscale_new_with_range (0, 100, 10);
263 GtkWidget
*label
= gtk_label_new (label_text
);
265 gtk_widget_set_sensitive (scale
, FALSE
);
267 gtk_container_add (GTK_CONTAINER (bin
), vbox
);
269 gtk_range_set_inverted (GTK_RANGE (scale
), TRUE
);
270 gtk_box_pack_start (GTK_BOX (vbox
), scale
, TRUE
, TRUE
, 0);
271 gtk_box_pack_start (GTK_BOX (vbox
), label
, FALSE
, FALSE
, 0);
277 empathy_call_window_video_contrast_changed_cb (GtkAdjustment
*adj
,
278 EmpathyCallWindow
*self
)
281 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
283 empathy_video_src_set_channel (priv
->video_input
,
284 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST
, gtk_adjustment_get_value (adj
));
288 empathy_call_window_video_brightness_changed_cb (GtkAdjustment
*adj
,
289 EmpathyCallWindow
*self
)
292 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
294 empathy_video_src_set_channel (priv
->video_input
,
295 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS
, gtk_adjustment_get_value (adj
));
299 empathy_call_window_video_gamma_changed_cb (GtkAdjustment
*adj
,
300 EmpathyCallWindow
*self
)
303 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
305 empathy_video_src_set_channel (priv
->video_input
,
306 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA
, gtk_adjustment_get_value (adj
));
311 empathy_call_window_create_video_input (EmpathyCallWindow
*self
)
313 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
316 hbox
= gtk_hbox_new (TRUE
, 3);
318 priv
->video_contrast
= empathy_call_window_create_video_input_add_slider (
319 self
, _("Contrast"), hbox
);
321 priv
->video_brightness
= empathy_call_window_create_video_input_add_slider (
322 self
, _("Brightness"), hbox
);
324 priv
->video_gamma
= empathy_call_window_create_video_input_add_slider (
325 self
, _("Gamma"), hbox
);
331 empathy_call_window_setup_video_input (EmpathyCallWindow
*self
)
333 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
337 supported
= empathy_video_src_get_supported_channels (priv
->video_input
);
339 if (supported
& EMPATHY_GST_VIDEO_SRC_SUPPORTS_CONTRAST
)
341 adj
= gtk_range_get_adjustment (GTK_RANGE (priv
->video_contrast
));
343 gtk_adjustment_set_value (adj
,
344 empathy_video_src_get_channel (priv
->video_input
,
345 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST
));
347 g_signal_connect (G_OBJECT (adj
), "value-changed",
348 G_CALLBACK (empathy_call_window_video_contrast_changed_cb
), self
);
350 gtk_widget_set_sensitive (priv
->video_contrast
, TRUE
);
353 if (supported
& EMPATHY_GST_VIDEO_SRC_SUPPORTS_BRIGHTNESS
)
355 adj
= gtk_range_get_adjustment (GTK_RANGE (priv
->video_brightness
));
357 gtk_adjustment_set_value (adj
,
358 empathy_video_src_get_channel (priv
->video_input
,
359 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS
));
361 g_signal_connect (G_OBJECT (adj
), "value-changed",
362 G_CALLBACK (empathy_call_window_video_brightness_changed_cb
), self
);
363 gtk_widget_set_sensitive (priv
->video_brightness
, TRUE
);
366 if (supported
& EMPATHY_GST_VIDEO_SRC_SUPPORTS_GAMMA
)
368 adj
= gtk_range_get_adjustment (GTK_RANGE (priv
->video_gamma
));
370 gtk_adjustment_set_value (adj
,
371 empathy_video_src_get_channel (priv
->video_input
,
372 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA
));
374 g_signal_connect (G_OBJECT (adj
), "value-changed",
375 G_CALLBACK (empathy_call_window_video_gamma_changed_cb
), self
);
376 gtk_widget_set_sensitive (priv
->video_gamma
, TRUE
);
381 empathy_call_window_mic_volume_changed_cb (GtkAdjustment
*adj
,
382 EmpathyCallWindow
*self
)
384 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
387 volume
= gtk_adjustment_get_value (adj
)/100.0;
389 /* Don't store the volume because of muting */
390 if (volume
> 0 || gtk_toggle_tool_button_get_active (
391 GTK_TOGGLE_TOOL_BUTTON (priv
->mic_button
)))
392 priv
->volume
= volume
;
394 /* Ensure that the toggle button is active if the volume is > 0 and inactive
395 * if it's smaller then 0 */
396 if ((volume
> 0) != gtk_toggle_tool_button_get_active (
397 GTK_TOGGLE_TOOL_BUTTON (priv
->mic_button
)))
398 gtk_toggle_tool_button_set_active (
399 GTK_TOGGLE_TOOL_BUTTON (priv
->mic_button
), volume
> 0);
401 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv
->audio_input
),
406 empathy_call_window_audio_input_level_changed_cb (EmpathyGstAudioSrc
*src
,
407 gdouble level
, GtkProgressBar
*bar
)
411 value
= CLAMP (pow (10, level
/ 20), 0.0, 1.0);
412 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (bar
), value
);
416 empathy_call_window_create_audio_input (EmpathyCallWindow
*self
)
418 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
419 GtkWidget
*hbox
, *vbox
, *scale
, *progress
, *label
;
422 hbox
= gtk_hbox_new (TRUE
, 3);
424 vbox
= gtk_vbox_new (FALSE
, 3);
425 gtk_box_pack_start (GTK_BOX (hbox
), vbox
, FALSE
, FALSE
, 3);
427 scale
= gtk_vscale_new_with_range (0, 150, 100);
428 gtk_range_set_inverted (GTK_RANGE (scale
), TRUE
);
429 label
= gtk_label_new (_("Volume"));
431 priv
->audio_input_adj
= adj
= gtk_range_get_adjustment (GTK_RANGE (scale
));
432 priv
->volume
= empathy_audio_src_get_volume (EMPATHY_GST_AUDIO_SRC
433 (priv
->audio_input
));
434 gtk_adjustment_set_value (adj
, priv
->volume
* 100);
436 g_signal_connect (G_OBJECT (adj
), "value-changed",
437 G_CALLBACK (empathy_call_window_mic_volume_changed_cb
), self
);
439 gtk_box_pack_start (GTK_BOX (vbox
), scale
, TRUE
, TRUE
, 3);
440 gtk_box_pack_start (GTK_BOX (vbox
), label
, FALSE
, FALSE
, 3);
442 progress
= gtk_progress_bar_new ();
443 gtk_progress_bar_set_orientation (GTK_PROGRESS_BAR (progress
),
444 GTK_PROGRESS_BOTTOM_TO_TOP
);
445 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress
), 0);
447 g_signal_connect (priv
->audio_input
, "peak-level-changed",
448 G_CALLBACK (empathy_call_window_audio_input_level_changed_cb
), progress
);
450 gtk_box_pack_start (GTK_BOX (hbox
), progress
, FALSE
, FALSE
, 3);
456 empathy_call_window_init (EmpathyCallWindow
*self
)
458 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
460 GtkWidget
*vbox
, *top_vbox
;
467 filename
= empathy_file_lookup ("empathy-call-window.ui", "src");
468 gui
= empathy_builder_get_file (filename
,
469 "call_window_vbox", &top_vbox
,
471 "statusbar", &priv
->statusbar
,
472 "microphone", &priv
->mic_button
,
473 "camera", &priv
->camera_button
,
474 "toolbar", &priv
->toolbar
,
475 "send_video", &priv
->send_video
,
476 "ui_manager", &priv
->ui_manager
,
479 empathy_builder_connect (gui
, self
,
480 "menuhangup", "activate", empathy_call_window_hangup_cb
,
481 "hangup", "clicked", empathy_call_window_hangup_cb
,
482 "microphone", "toggled", empathy_call_window_mic_toggled_cb
,
483 "camera", "toggled", empathy_call_window_camera_toggled_cb
,
484 "send_video", "toggled", empathy_call_window_send_video_toggled_cb
,
485 "show_preview", "toggled", empathy_call_window_show_preview_toggled_cb
,
488 priv
->lock
= g_mutex_new ();
490 gtk_container_add (GTK_CONTAINER (self
), top_vbox
);
492 empathy_call_window_setup_toolbar (self
);
494 priv
->pipeline
= gst_pipeline_new (NULL
);
496 hbox
= gtk_hbox_new (FALSE
, 3);
497 gtk_container_set_border_width (GTK_CONTAINER (hbox
), 6);
498 gtk_paned_pack1 (GTK_PANED (priv
->pane
), hbox
, TRUE
, FALSE
);
500 bus
= gst_pipeline_get_bus (GST_PIPELINE (priv
->pipeline
));
502 gst_bus_add_watch (bus
, empathy_call_window_bus_message
, self
);
504 priv
->video_output
= empathy_video_widget_new (bus
);
505 gtk_box_pack_start (GTK_BOX (hbox
), priv
->video_output
, TRUE
, TRUE
, 3);
507 priv
->video_tee
= gst_element_factory_make ("tee", NULL
);
508 gst_object_ref (priv
->video_tee
);
509 gst_object_sink (priv
->video_tee
);
511 vbox
= gtk_vbox_new (FALSE
, 3);
512 gtk_box_pack_start (GTK_BOX (hbox
), vbox
, FALSE
, FALSE
, 3);
514 priv
->video_preview
= empathy_video_widget_new_with_size (bus
, 160, 120);
515 g_object_set (priv
->video_preview
, "sync", FALSE
, "async", TRUE
, NULL
);
516 gtk_box_pack_start (GTK_BOX (vbox
), priv
->video_preview
, FALSE
, FALSE
, 0);
518 priv
->video_input
= empathy_video_src_new ();
519 gst_object_ref (priv
->video_input
);
520 gst_object_sink (priv
->video_input
);
522 priv
->audio_input
= empathy_audio_src_new ();
523 gst_object_ref (priv
->audio_input
);
524 gst_object_sink (priv
->audio_input
);
526 priv
->audio_output
= empathy_audio_sink_new ();
527 gst_object_ref (priv
->audio_output
);
528 gst_object_sink (priv
->audio_output
);
530 g_object_unref (bus
);
532 priv
->sidebar_button
= gtk_toggle_button_new_with_mnemonic (_("_Sidebar"));
533 arrow
= gtk_arrow_new (GTK_ARROW_RIGHT
, GTK_SHADOW_NONE
);
534 g_signal_connect (G_OBJECT (priv
->sidebar_button
), "toggled",
535 G_CALLBACK (empathy_call_window_sidebar_toggled_cb
), self
);
537 gtk_button_set_image (GTK_BUTTON (priv
->sidebar_button
), arrow
);
539 h
= gtk_hbox_new (FALSE
, 3);
540 gtk_box_pack_end (GTK_BOX (vbox
), h
, FALSE
, FALSE
, 3);
541 gtk_box_pack_end (GTK_BOX (h
), priv
->sidebar_button
, FALSE
, FALSE
, 3);
543 priv
->sidebar
= empathy_sidebar_new ();
544 g_signal_connect (G_OBJECT (priv
->sidebar
),
545 "hide", G_CALLBACK (empathy_call_window_sidebar_hidden_cb
),
547 gtk_paned_pack2 (GTK_PANED (priv
->pane
), priv
->sidebar
, FALSE
, FALSE
);
549 priv
->dtmf_panel
= empathy_call_window_create_dtmf (self
);
550 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv
->sidebar
), _("Dialpad"),
553 gtk_widget_set_sensitive (priv
->dtmf_panel
, FALSE
);
555 page
= empathy_call_window_create_audio_input (self
);
556 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv
->sidebar
), _("Audio input"),
559 page
= empathy_call_window_create_video_input (self
);
560 empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv
->sidebar
), _("Video input"),
563 gtk_widget_show_all (top_vbox
);
565 gtk_widget_hide (priv
->sidebar
);
567 g_signal_connect (G_OBJECT (self
), "realize",
568 G_CALLBACK (empathy_call_window_realized_cb
), self
);
570 g_signal_connect (G_OBJECT (self
), "delete-event",
571 G_CALLBACK (empathy_call_window_delete_cb
), self
);
573 empathy_call_window_status_message (self
, _("Connecting..."));
575 priv
->timer
= g_timer_new ();
577 g_object_ref (priv
->ui_manager
);
578 g_object_unref (gui
);
581 static void empathy_call_window_dispose (GObject
*object
);
582 static void empathy_call_window_finalize (GObject
*object
);
585 empathy_call_window_set_property (GObject
*object
,
586 guint property_id
, const GValue
*value
, GParamSpec
*pspec
)
588 EmpathyCallWindowPriv
*priv
= GET_PRIV (object
);
592 case PROP_CALL_HANDLER
:
593 priv
->handler
= g_value_dup_object (value
);
596 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
601 empathy_call_window_get_property (GObject
*object
,
602 guint property_id
, GValue
*value
, GParamSpec
*pspec
)
604 EmpathyCallWindowPriv
*priv
= GET_PRIV (object
);
608 case PROP_CALL_HANDLER
:
609 g_value_set_object (value
, priv
->handler
);
612 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
617 empathy_call_window_class_init (
618 EmpathyCallWindowClass
*empathy_call_window_class
)
620 GObjectClass
*object_class
= G_OBJECT_CLASS (empathy_call_window_class
);
621 GParamSpec
*param_spec
;
623 g_type_class_add_private (empathy_call_window_class
,
624 sizeof (EmpathyCallWindowPriv
));
626 object_class
->set_property
= empathy_call_window_set_property
;
627 object_class
->get_property
= empathy_call_window_get_property
;
629 object_class
->dispose
= empathy_call_window_dispose
;
630 object_class
->finalize
= empathy_call_window_finalize
;
632 param_spec
= g_param_spec_object ("handler",
633 "handler", "The call handler",
634 EMPATHY_TYPE_CALL_HANDLER
,
635 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
);
636 g_object_class_install_property (object_class
,
637 PROP_CALL_HANDLER
, param_spec
);
642 empathy_call_window_dispose (GObject
*object
)
644 EmpathyCallWindow
*self
= EMPATHY_CALL_WINDOW (object
);
645 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
647 if (priv
->dispose_has_run
)
650 priv
->dispose_has_run
= TRUE
;
652 if (priv
->handler
!= NULL
)
653 g_object_unref (priv
->handler
);
655 priv
->handler
= NULL
;
657 if (priv
->pipeline
!= NULL
)
658 g_object_unref (priv
->pipeline
);
659 priv
->pipeline
= NULL
;
661 if (priv
->video_input
!= NULL
)
662 g_object_unref (priv
->video_input
);
663 priv
->video_input
= NULL
;
665 if (priv
->audio_input
!= NULL
)
666 g_object_unref (priv
->audio_input
);
667 priv
->audio_input
= NULL
;
669 if (priv
->audio_output
!= NULL
)
670 g_object_unref (priv
->audio_output
);
671 priv
->audio_output
= NULL
;
673 if (priv
->video_tee
!= NULL
)
674 g_object_unref (priv
->video_tee
);
675 priv
->video_tee
= NULL
;
677 if (priv
->timer_id
!= 0)
678 g_source_remove (priv
->timer_id
);
681 if (priv
->ui_manager
!= NULL
)
682 g_object_unref (priv
->ui_manager
);
683 priv
->ui_manager
= NULL
;
685 /* release any references held by the object here */
686 if (G_OBJECT_CLASS (empathy_call_window_parent_class
)->dispose
)
687 G_OBJECT_CLASS (empathy_call_window_parent_class
)->dispose (object
);
691 empathy_call_window_finalize (GObject
*object
)
693 EmpathyCallWindow
*self
= EMPATHY_CALL_WINDOW (object
);
694 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
696 /* free any data held directly by the object here */
697 g_mutex_free (priv
->lock
);
699 g_timer_destroy (priv
->timer
);
701 G_OBJECT_CLASS (empathy_call_window_parent_class
)->finalize (object
);
706 empathy_call_window_new (EmpathyCallHandler
*handler
)
708 return EMPATHY_CALL_WINDOW (
709 g_object_new (EMPATHY_TYPE_CALL_WINDOW
, "handler", handler
, NULL
));
713 empathy_call_window_conference_added_cb (EmpathyCallHandler
*handler
,
714 GstElement
*conference
, gpointer user_data
)
716 EmpathyCallWindow
*self
= EMPATHY_CALL_WINDOW (user_data
);
717 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
719 gst_bin_add (GST_BIN (priv
->pipeline
), conference
);
721 gst_element_set_state (conference
, GST_STATE_PLAYING
);
725 empathy_call_window_request_resource_cb (EmpathyCallHandler
*handler
,
726 FsMediaType type
, FsStreamDirection direction
, gpointer user_data
)
728 EmpathyCallWindow
*self
= EMPATHY_CALL_WINDOW (user_data
);
729 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
731 if (type
!= TP_MEDIA_STREAM_TYPE_VIDEO
)
734 if (direction
== FS_DIRECTION_RECV
)
737 /* video and direction is send */
738 return priv
->video_input
!= NULL
;
742 empathy_call_window_disconnected (EmpathyCallWindow
*self
)
744 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
746 g_mutex_lock (priv
->lock
);
748 g_timer_stop (priv
->timer
);
750 if (priv
->timer_id
!= 0)
751 g_source_remove (priv
->timer_id
);
754 g_mutex_unlock (priv
->lock
);
756 empathy_call_window_status_message (self
, _("Disconnected"));
758 gtk_widget_set_sensitive (priv
->camera_button
, FALSE
);
759 gtk_action_set_sensitive (priv
->send_video
, FALSE
);
764 empathy_call_window_channel_closed_cb (TfChannel
*channel
, gpointer user_data
)
766 EmpathyCallWindow
*self
= EMPATHY_CALL_WINDOW (user_data
);
768 empathy_call_window_disconnected (self
);
771 /* Called with global lock held */
773 empathy_call_window_get_video_sink_pad (EmpathyCallWindow
*self
)
775 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
778 if (priv
->funnel
== NULL
)
782 output
= empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
783 (priv
->video_output
));
785 priv
->funnel
= gst_element_factory_make ("fsfunnel", NULL
);
787 gst_bin_add (GST_BIN (priv
->pipeline
), priv
->funnel
);
788 gst_bin_add (GST_BIN (priv
->pipeline
), output
);
790 gst_element_link (priv
->funnel
, output
);
792 gst_element_set_state (priv
->funnel
, GST_STATE_PLAYING
);
793 gst_element_set_state (output
, GST_STATE_PLAYING
);
796 pad
= gst_element_get_request_pad (priv
->funnel
, "sink%d");
801 /* Called with global lock held */
803 empathy_call_window_get_audio_sink_pad (EmpathyCallWindow
*self
)
805 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
808 if (priv
->liveadder
== NULL
)
810 priv
->liveadder
= gst_element_factory_make ("liveadder", NULL
);
812 gst_bin_add (GST_BIN (priv
->pipeline
), priv
->liveadder
);
813 gst_bin_add (GST_BIN (priv
->pipeline
), priv
->audio_output
);
815 gst_element_link (priv
->liveadder
, priv
->audio_output
);
817 gst_element_set_state (priv
->liveadder
, GST_STATE_PLAYING
);
818 gst_element_set_state (priv
->audio_output
, GST_STATE_PLAYING
);
821 pad
= gst_element_get_request_pad (priv
->liveadder
, "sink%d");
827 empathy_call_window_update_timer (gpointer user_data
)
829 EmpathyCallWindow
*self
= EMPATHY_CALL_WINDOW (user_data
);
830 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
834 time
= g_timer_elapsed (priv
->timer
, NULL
);
836 /* Translators: number of minutes:seconds the caller has been connected */
837 str
= g_strdup_printf (_("Connected — %d:%02dm"), (int) time
/ 60,
839 empathy_call_window_status_message (self
, str
);
846 empathy_call_window_connected (gpointer user_data
)
848 EmpathyCallWindow
*self
= EMPATHY_CALL_WINDOW (user_data
);
849 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
852 g_object_get (priv
->handler
, "tp-call", &call
, NULL
);
854 if (empathy_tp_call_has_dtmf (call
))
855 gtk_widget_set_sensitive (priv
->dtmf_panel
, TRUE
);
857 if (priv
->video_input
!= NULL
)
859 gtk_widget_set_sensitive (priv
->camera_button
, TRUE
);
860 gtk_action_set_sensitive (priv
->send_video
, TRUE
);
863 g_object_unref (call
);
865 g_mutex_lock (priv
->lock
);
867 priv
->timer_id
= g_timeout_add_seconds (1,
868 empathy_call_window_update_timer
, self
);
870 g_mutex_unlock (priv
->lock
);
872 empathy_call_window_update_timer (self
);
878 /* Called from the streaming thread */
880 empathy_call_window_src_added_cb (EmpathyCallHandler
*handler
,
881 GstPad
*src
, guint media_type
, gpointer user_data
)
883 EmpathyCallWindow
*self
= EMPATHY_CALL_WINDOW (user_data
);
884 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
888 g_mutex_lock (priv
->lock
);
890 if (priv
->connected
== FALSE
)
892 g_timer_start (priv
->timer
);
893 priv
->timer_id
= g_idle_add (empathy_call_window_connected
, self
);
894 priv
->connected
= TRUE
;
899 case TP_MEDIA_STREAM_TYPE_AUDIO
:
900 pad
= empathy_call_window_get_audio_sink_pad (self
);
902 case TP_MEDIA_STREAM_TYPE_VIDEO
:
903 pad
= empathy_call_window_get_video_sink_pad (self
);
906 g_assert_not_reached ();
909 gst_pad_link (src
, pad
);
910 gst_object_unref (pad
);
912 g_mutex_unlock (priv
->lock
);
915 /* Called from the streaming thread */
917 empathy_call_window_sink_added_cb (EmpathyCallHandler
*handler
,
918 GstPad
*sink
, guint media_type
, gpointer user_data
)
920 EmpathyCallWindow
*self
= EMPATHY_CALL_WINDOW (user_data
);
921 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
926 case TP_MEDIA_STREAM_TYPE_AUDIO
:
927 gst_bin_add (GST_BIN (priv
->pipeline
), priv
->audio_input
);
929 pad
= gst_element_get_static_pad (priv
->audio_input
, "src");
930 gst_pad_link (pad
, sink
);
932 gst_element_set_state (priv
->audio_input
, GST_STATE_PLAYING
);
934 case TP_MEDIA_STREAM_TYPE_VIDEO
:
935 if (priv
->video_input
!= NULL
)
937 pad
= gst_element_get_request_pad (priv
->video_tee
, "src%d");
938 gst_pad_link (pad
, sink
);
942 g_assert_not_reached ();
948 empathy_gst_bin_has_child (GstBin
*bin
, GstElement
*element
)
951 gboolean ret
= FALSE
;
954 it
= gst_bin_iterate_recurse (bin
);
958 switch (gst_iterator_next (it
, (gpointer
*)&item
))
960 case GST_ITERATOR_OK
:
963 gst_object_unref (GST_OBJECT (item
));
967 gst_object_unref (GST_OBJECT (item
));
969 case GST_ITERATOR_RESYNC
:
970 gst_iterator_resync (it
);
972 case GST_ITERATOR_ERROR
:
973 g_assert_not_reached ();
975 case GST_ITERATOR_DONE
:
980 gst_iterator_free (it
);
987 empathy_call_window_remove_video_input (EmpathyCallWindow
*self
)
989 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
992 preview
= empathy_video_widget_get_element (
993 EMPATHY_VIDEO_WIDGET (priv
->video_preview
));
995 gst_element_set_state (priv
->video_input
, GST_STATE_NULL
);
996 gst_element_set_state (priv
->video_tee
, GST_STATE_NULL
);
997 gst_element_set_state (preview
, GST_STATE_NULL
);
999 gst_bin_remove_many (GST_BIN (priv
->pipeline
), priv
->video_input
,
1000 priv
->video_tee
, preview
, NULL
);
1002 g_object_unref (priv
->video_input
);
1003 priv
->video_input
= NULL
;
1004 g_object_unref (priv
->video_tee
);
1005 priv
->video_tee
= NULL
;
1010 empathy_call_window_bus_message (GstBus
*bus
, GstMessage
*message
,
1013 EmpathyCallWindow
*self
= EMPATHY_CALL_WINDOW (user_data
);
1014 EmpathyCallWindowPriv
*priv
= GET_PRIV (self
);
1017 empathy_call_handler_bus_message (priv
->handler
, bus
, message
);
1019 switch (GST_MESSAGE_TYPE (message
))
1021 case GST_MESSAGE_STATE_CHANGED
:
1022 if (GST_MESSAGE_SRC (message
) == GST_OBJECT (priv
->video_input
))
1024 gst_message_parse_state_changed (message
, NULL
, &newstate
, NULL
);
1025 if (newstate
== GST_STATE_PAUSED
)
1026 empathy_call_window_setup_video_input (self
);
1028 if (GST_MESSAGE_SRC (message
) == GST_OBJECT (priv
->pipeline
) &&
1029 !priv
->call_started
)
1031 gst_message_parse_state_changed (message
, NULL
, &newstate
, NULL
);
1032 if (newstate
== GST_STATE_PAUSED
)
1034 priv
->call_started
= TRUE
;
1035 empathy_call_handler_start_call (priv
->handler
);
1036 gst_element_set_state (priv
->pipeline
, GST_STATE_PLAYING
);
1040 case GST_MESSAGE_ERROR
:
1045 gst_message_parse_error (message
, &error
, &debug
);
1047 g_message ("Element error: %s -- %s\n", error
->message
, debug
);
1049 if (priv
->video_input
!= NULL
&&
1050 empathy_gst_bin_has_child (GST_BIN (priv
->video_input
),
1051 GST_ELEMENT (GST_MESSAGE_SRC (message
))))
1053 /* Remove the video input and continue */
1054 empathy_call_window_remove_video_input (self
);
1055 gst_element_set_state (priv
->pipeline
, GST_STATE_PAUSED
);
1059 gst_element_set_state (priv
->pipeline
, GST_STATE_NULL
);
1060 empathy_call_window_disconnected (self
);
1062 g_error_free (error
);
1073 empathy_call_window_realized_cb (GtkWidget
*widget
, EmpathyCallWindow
*window
)
1075 EmpathyCallWindowPriv
*priv
= GET_PRIV (window
);
1076 GstElement
*preview
;
1078 g_signal_connect (priv
->handler
, "conference-added",
1079 G_CALLBACK (empathy_call_window_conference_added_cb
), window
);
1080 g_signal_connect (priv
->handler
, "request-resource",
1081 G_CALLBACK (empathy_call_window_request_resource_cb
), window
);
1082 g_signal_connect (priv
->handler
, "closed",
1083 G_CALLBACK (empathy_call_window_channel_closed_cb
), window
);
1084 g_signal_connect (priv
->handler
, "src-pad-added",
1085 G_CALLBACK (empathy_call_window_src_added_cb
), window
);
1086 g_signal_connect (priv
->handler
, "sink-pad-added",
1087 G_CALLBACK (empathy_call_window_sink_added_cb
), window
);
1090 preview
= empathy_video_widget_get_element (
1091 EMPATHY_VIDEO_WIDGET (priv
->video_preview
));
1093 gst_bin_add_many (GST_BIN (priv
->pipeline
), priv
->video_input
,
1094 priv
->video_tee
, preview
, NULL
);
1095 gst_element_link_many (priv
->video_input
, priv
->video_tee
,
1098 gst_element_set_state (priv
->pipeline
, GST_STATE_PAUSED
);
1102 empathy_call_window_delete_cb (GtkWidget
*widget
, GdkEvent
*event
,
1103 EmpathyCallWindow
*window
)
1105 EmpathyCallWindowPriv
*priv
= GET_PRIV (window
);
1107 gst_element_set_state (priv
->pipeline
, GST_STATE_NULL
);
1113 empathy_call_window_sidebar_toggled_cb (GtkToggleButton
*toggle
,
1114 EmpathyCallWindow
*window
)
1116 EmpathyCallWindowPriv
*priv
= GET_PRIV (window
);
1118 int w
,h
, handle_size
;
1120 w
= GTK_WIDGET (window
)->allocation
.width
;
1121 h
= GTK_WIDGET (window
)->allocation
.height
;
1123 gtk_widget_style_get (priv
->pane
, "handle_size", &handle_size
, NULL
);
1125 if (gtk_toggle_button_get_active (toggle
))
1127 arrow
= gtk_arrow_new (GTK_ARROW_LEFT
, GTK_SHADOW_NONE
);
1128 gtk_widget_show (priv
->sidebar
);
1129 w
+= priv
->sidebar
->allocation
.width
+ handle_size
;
1133 arrow
= gtk_arrow_new (GTK_ARROW_RIGHT
, GTK_SHADOW_NONE
);
1134 w
-= priv
->sidebar
->allocation
.width
+ handle_size
;
1135 gtk_widget_hide (priv
->sidebar
);
1138 gtk_button_set_image (GTK_BUTTON (priv
->sidebar_button
), arrow
);
1141 gtk_window_resize (GTK_WINDOW (window
), w
, h
);
1145 empathy_call_window_set_send_video (EmpathyCallWindow
*window
,
1148 EmpathyCallWindowPriv
*priv
= GET_PRIV (window
);
1149 EmpathyTpCall
*call
;
1151 g_object_get (priv
->handler
, "tp-call", &call
, NULL
);
1152 empathy_tp_call_request_video_stream_direction (call
, send
);
1153 g_object_unref (call
);
1157 empathy_call_window_camera_toggled_cb (GtkToggleToolButton
*toggle
,
1158 EmpathyCallWindow
*window
)
1162 active
= (gtk_toggle_tool_button_get_active (toggle
));
1163 empathy_call_window_set_send_video (window
, active
);
1167 empathy_call_window_send_video_toggled_cb (GtkToggleAction
*toggle
,
1168 EmpathyCallWindow
*window
)
1172 active
= (gtk_toggle_action_get_active (toggle
));
1173 empathy_call_window_set_send_video (window
, active
);
1177 empathy_call_window_show_preview_toggled_cb (GtkToggleAction
*toggle
,
1178 EmpathyCallWindow
*window
)
1180 /* FIXME: Not implemented */
1184 empathy_call_window_mic_toggled_cb (GtkToggleToolButton
*toggle
,
1185 EmpathyCallWindow
*window
)
1187 EmpathyCallWindowPriv
*priv
= GET_PRIV (window
);
1190 active
= (gtk_toggle_tool_button_get_active (toggle
));
1194 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv
->audio_input
),
1196 gtk_adjustment_set_value (priv
->audio_input_adj
, priv
->volume
* 100);
1200 /* TODO, Instead of setting the input volume to 0 we should probably
1201 * stop sending but this would cause the audio call to drop if both
1202 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
1203 * in the future. GNOME #574574
1205 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv
->audio_input
),
1207 gtk_adjustment_set_value (priv
->audio_input_adj
, 0);
1212 empathy_call_window_sidebar_hidden_cb (EmpathySidebar
*sidebar
,
1213 EmpathyCallWindow
*window
)
1215 EmpathyCallWindowPriv
*priv
= GET_PRIV (window
);
1217 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv
->sidebar_button
),
1222 empathy_call_window_hangup_cb (gpointer object
,
1223 EmpathyCallWindow
*window
)
1225 EmpathyCallWindowPriv
*priv
= GET_PRIV (window
);
1227 gst_element_set_state (priv
->pipeline
, GST_STATE_NULL
);
1228 gtk_widget_destroy (GTK_WIDGET (window
));
1232 empathy_call_window_status_message (EmpathyCallWindow
*window
,
1235 EmpathyCallWindowPriv
*priv
= GET_PRIV (window
);
1237 if (priv
->context_id
== 0)
1239 priv
->context_id
= gtk_statusbar_get_context_id (
1240 GTK_STATUSBAR (priv
->statusbar
), "voip call status messages");
1244 gtk_statusbar_pop (GTK_STATUSBAR (priv
->statusbar
), priv
->context_id
);
1247 gtk_statusbar_push (GTK_STATUSBAR (priv
->statusbar
), priv
->context_id
,
1252 empathy_call_window_volume_changed_cb (GtkScaleButton
*button
,
1253 gdouble value
, EmpathyCallWindow
*window
)
1255 EmpathyCallWindowPriv
*priv
= GET_PRIV (window
);
1257 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv
->audio_output
),