2
* empathy-call-window.c - Source for EmpathyCallWindow
3
* Copyright (C) 2008-2011 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
27
#include <gdk/gdkkeysyms.h>
30
#include <glib/gi18n.h>
32
#include <telepathy-glib/util.h>
33
#include <telepathy-farstream/telepathy-farstream.h>
34
#include <telepathy-glib/util.h>
36
#include <gst/farsight/fs-element-added-notifier.h>
37
#include <gst/farsight/fs-utils.h>
39
#include <libempathy/empathy-tp-contact-factory.h>
40
#include <libempathy/empathy-utils.h>
41
#include <libempathy-gtk/empathy-avatar-image.h>
42
#include <libempathy-gtk/empathy-ui-utils.h>
43
#include <libempathy-gtk/empathy-sound.h>
44
#include <libempathy-gtk/empathy-geometry.h>
45
#include <libempathy-gtk/empathy-images.h>
47
#define DEBUG_FLAG EMPATHY_DEBUG_VOIP
48
#include <libempathy/empathy-debug.h>
50
#include "empathy-call-window.h"
51
#include "empathy-call-window-fullscreen.h"
52
#include "empathy-call-factory.h"
53
#include "empathy-video-widget.h"
54
#include "empathy-audio-src.h"
55
#include "empathy-audio-sink.h"
56
#include "empathy-video-src.h"
57
#include "empathy-sidebar.h"
59
#define BUTTON_ID "empathy-call-dtmf-button-id"
61
#define CONTENT_HBOX_BORDER_WIDTH 6
62
#define CONTENT_HBOX_SPACING 3
63
#define CONTENT_HBOX_CHILDREN_PACKING_PADDING 3
65
#define SELF_VIDEO_SECTION_WIDTH 160
66
#define SELF_VIDEO_SECTION_HEIGTH 120
68
/* The avatar's default width and height are set to the same value because we
69
want a square icon. */
70
#define REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
71
#define REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT \
72
EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
74
/* If an video input error occurs, the error message will start with "v4l" */
75
#define VIDEO_INPUT_ERROR_PREFIX "v4l"
77
/* The time interval in milliseconds between 2 outgoing rings */
78
#define MS_BETWEEN_RING 500
80
G_DEFINE_TYPE(EmpathyCallWindow, empathy_call_window, GTK_TYPE_WINDOW)
83
PROP_CALL_HANDLER = 1,
99
struct _EmpathyCallWindowPriv
101
gboolean dispose_has_run;
102
EmpathyCallHandler *handler;
104
EmpathyContact *contact;
109
GtkUIManager *ui_manager;
110
GtkWidget *errors_vbox;
111
/* widget displays the video received from the remote user. This widget is
112
* alive only during call. */
113
GtkWidget *video_output;
114
GtkWidget *video_preview;
115
GtkWidget *remote_user_avatar_widget;
116
GtkWidget *self_user_avatar_widget;
118
GtkWidget *sidebar_button;
119
GtkWidget *statusbar;
120
GtkWidget *volume_button;
121
GtkWidget *redial_button;
122
GtkWidget *mic_button;
126
GtkAction *menu_fullscreen;
127
GtkAction *action_camera_on;
128
GtkWidget *tool_button_camera_off;
129
GtkWidget *tool_button_camera_preview;
130
GtkWidget *tool_button_camera_on;
132
/* The frames and boxes that contain self and remote avatar and video
133
input/output. When we redial, we destroy and re-create the boxes */
134
GtkWidget *remote_user_output_frame;
135
GtkWidget *self_user_output_frame;
136
GtkWidget *remote_user_output_hbox;
137
GtkWidget *self_user_output_hbox;
139
/* We keep a reference on the hbox which contains the main content so we can
140
easilly repack everything when toggling fullscreen */
141
GtkWidget *content_hbox;
143
/* This vbox is contained in the content_hbox and it contains the
144
self_user_output_frame and the sidebar button. When toggling fullscreen,
145
it needs to be repacked. We keep a reference on it for easier access. */
148
gulong video_output_motion_handler_id;
149
guint bus_message_source_id;
152
GtkWidget *volume_scale;
153
GtkWidget *volume_progress_bar;
154
GtkAdjustment *audio_input_adj;
156
GtkWidget *dtmf_panel;
159
GtkWidget *details_vbox;
160
GtkWidget *vcodec_encoding_label;
161
GtkWidget *acodec_encoding_label;
162
GtkWidget *vcodec_decoding_label;
163
GtkWidget *acodec_decoding_label;
165
GtkWidget *audio_remote_candidate_label;
166
GtkWidget *audio_local_candidate_label;
167
GtkWidget *video_remote_candidate_label;
168
GtkWidget *video_local_candidate_label;
169
GtkWidget *video_remote_candidate_info_img;
170
GtkWidget *video_local_candidate_info_img;
171
GtkWidget *audio_remote_candidate_info_img;
172
GtkWidget *audio_local_candidate_info_img;
174
GstElement *video_input;
175
GstElement *audio_input;
176
GstElement *audio_output;
177
GstElement *pipeline;
178
GstElement *video_tee;
189
GtkWidget *video_contrast;
190
GtkWidget *video_brightness;
191
GtkWidget *video_gamma;
194
gboolean call_started;
195
gboolean sending_video;
196
CameraState camera_state;
198
EmpathyCallWindowFullscreen *fullscreen;
199
gboolean is_fullscreen;
201
/* Those fields represent the state of the window before it actually was in
203
gboolean sidebar_was_visible_before_fs;
204
gint original_width_before_fs;
205
gint original_height_before_fs;
207
/* TRUE if the call should be started when the pipeline is playing */
208
gboolean start_call_when_playing;
209
/* TRUE if we requested to set the pipeline in the playing state */
210
gboolean pipeline_playing;
213
#define GET_PRIV(o) (EMPATHY_CALL_WINDOW (o)->priv)
215
static void empathy_call_window_realized_cb (GtkWidget *widget,
216
EmpathyCallWindow *window);
218
static gboolean empathy_call_window_delete_cb (GtkWidget *widget,
219
GdkEvent *event, EmpathyCallWindow *window);
221
static gboolean empathy_call_window_state_event_cb (GtkWidget *widget,
222
GdkEventWindowState *event, EmpathyCallWindow *window);
224
static void empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
225
EmpathyCallWindow *window);
227
static void empathy_call_window_set_send_video (EmpathyCallWindow *window,
230
static void empathy_call_window_mic_toggled_cb (
231
GtkToggleToolButton *toggle, EmpathyCallWindow *window);
233
static void empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
234
EmpathyCallWindow *window);
236
static void empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
237
EmpathyCallWindow *window);
239
static void empathy_call_window_hangup_cb (gpointer object,
240
EmpathyCallWindow *window);
242
static void empathy_call_window_fullscreen_cb (gpointer object,
243
EmpathyCallWindow *window);
245
static void empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window);
247
static gboolean empathy_call_window_video_button_press_cb (
248
GtkWidget *video_output, GdkEventButton *event, EmpathyCallWindow *window);
250
static gboolean empathy_call_window_key_press_cb (GtkWidget *video_output,
251
GdkEventKey *event, EmpathyCallWindow *window);
253
static gboolean empathy_call_window_video_output_motion_notify (
254
GtkWidget *widget, GdkEventMotion *event, EmpathyCallWindow *window);
256
static void empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
259
static void empathy_call_window_redial_cb (gpointer object,
260
EmpathyCallWindow *window);
262
static void empathy_call_window_restart_call (EmpathyCallWindow *window);
264
static void empathy_call_window_status_message (EmpathyCallWindow *window,
267
static gboolean empathy_call_window_bus_message (GstBus *bus,
268
GstMessage *message, gpointer user_data);
271
empathy_call_window_volume_changed_cb (GtkScaleButton *button,
272
gdouble value, EmpathyCallWindow *window);
274
static void block_camera_control_signals (EmpathyCallWindow *self);
275
static void unblock_camera_control_signals (EmpathyCallWindow *self);
278
empathy_call_window_setup_toolbar (EmpathyCallWindow *self)
280
EmpathyCallWindowPriv *priv = GET_PRIV (self);
281
GtkToolItem *tool_item;
282
GtkWidget *camera_off_icon;
283
GdkPixbuf *pixbuf, *modded_pixbuf;
285
/* set the icon of the 'camera off' button by greying off the webcam icon */
286
pixbuf = empathy_pixbuf_from_icon_name ("camera-web",
287
GTK_ICON_SIZE_SMALL_TOOLBAR);
289
modded_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
290
gdk_pixbuf_get_width (pixbuf),
291
gdk_pixbuf_get_height (pixbuf));
293
gdk_pixbuf_saturate_and_pixelate (pixbuf, modded_pixbuf, 1.0, TRUE);
294
g_object_unref (pixbuf);
296
camera_off_icon = gtk_image_new_from_pixbuf (modded_pixbuf);
297
g_object_unref (modded_pixbuf);
298
gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (
299
priv->tool_button_camera_off), camera_off_icon);
301
/* Add an empty expanded GtkToolItem so the volume button is at the end of
303
tool_item = gtk_tool_item_new ();
304
gtk_tool_item_set_expand (tool_item, TRUE);
305
gtk_widget_show (GTK_WIDGET (tool_item));
306
gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
308
priv->volume_button = gtk_volume_button_new ();
309
/* FIXME listen to the audiosinks signals and update the button according to
310
* that, for now starting out at 1.0 and assuming only the app changes the
312
gtk_scale_button_set_value (GTK_SCALE_BUTTON (priv->volume_button), 1.0);
313
g_signal_connect (G_OBJECT (priv->volume_button), "value-changed",
314
G_CALLBACK (empathy_call_window_volume_changed_cb), self);
316
tool_item = gtk_tool_item_new ();
317
gtk_container_add (GTK_CONTAINER (tool_item), priv->volume_button);
318
gtk_widget_show_all (GTK_WIDGET (tool_item));
319
gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
323
dtmf_button_pressed_cb (GtkButton *button, EmpathyCallWindow *window)
325
EmpathyCallWindowPriv *priv = GET_PRIV (window);
326
TpyCallChannel *call;
330
g_object_get (priv->handler, "call-channel", &call, NULL);
332
button_quark = g_quark_from_static_string (BUTTON_ID);
333
event = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (button),
336
tpy_call_channel_dtmf_start_tone (call, event);
338
g_object_unref (call);
342
dtmf_button_released_cb (GtkButton *button, EmpathyCallWindow *window)
344
EmpathyCallWindowPriv *priv = GET_PRIV (window);
345
TpyCallChannel *call;
347
g_object_get (priv->handler, "call-channel", &call, NULL);
349
tpy_call_channel_dtmf_stop_tone (call);
351
g_object_unref (call);
355
empathy_call_window_create_dtmf (EmpathyCallWindow *self)
363
} dtmfbuttons[] = { { "1", TP_DTMF_EVENT_DIGIT_1 },
364
{ "2", TP_DTMF_EVENT_DIGIT_2 },
365
{ "3", TP_DTMF_EVENT_DIGIT_3 },
366
{ "4", TP_DTMF_EVENT_DIGIT_4 },
367
{ "5", TP_DTMF_EVENT_DIGIT_5 },
368
{ "6", TP_DTMF_EVENT_DIGIT_6 },
369
{ "7", TP_DTMF_EVENT_DIGIT_7 },
370
{ "8", TP_DTMF_EVENT_DIGIT_8 },
371
{ "9", TP_DTMF_EVENT_DIGIT_9 },
372
{ "#", TP_DTMF_EVENT_HASH },
373
{ "0", TP_DTMF_EVENT_DIGIT_0 },
374
{ "*", TP_DTMF_EVENT_ASTERISK },
377
button_quark = g_quark_from_static_string (BUTTON_ID);
379
table = gtk_table_new (4, 3, TRUE);
381
for (i = 0; dtmfbuttons[i].label != NULL; i++)
383
GtkWidget *button = gtk_button_new_with_label (dtmfbuttons[i].label);
384
gtk_table_attach (GTK_TABLE (table), button, i % 3, i % 3 + 1,
385
i/3, i/3 + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 1, 1);
387
g_object_set_qdata (G_OBJECT (button), button_quark,
388
GUINT_TO_POINTER (dtmfbuttons[i].event));
390
g_signal_connect (G_OBJECT (button), "pressed",
391
G_CALLBACK (dtmf_button_pressed_cb), self);
392
g_signal_connect (G_OBJECT (button), "released",
393
G_CALLBACK (dtmf_button_released_cb), self);
400
empathy_call_window_create_video_input_add_slider (EmpathyCallWindow *self,
401
gchar *label_text, GtkWidget *bin)
403
GtkWidget *vbox = gtk_vbox_new (FALSE, 2);
404
GtkWidget *scale = gtk_vscale_new_with_range (0, 100, 10);
405
GtkWidget *label = gtk_label_new (label_text);
407
gtk_widget_set_sensitive (scale, FALSE);
409
gtk_container_add (GTK_CONTAINER (bin), vbox);
411
gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
412
gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 0);
413
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
419
empathy_call_window_video_contrast_changed_cb (GtkAdjustment *adj,
420
EmpathyCallWindow *self)
423
EmpathyCallWindowPriv *priv = GET_PRIV (self);
425
empathy_video_src_set_channel (priv->video_input,
426
EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST, gtk_adjustment_get_value (adj));
430
empathy_call_window_video_brightness_changed_cb (GtkAdjustment *adj,
431
EmpathyCallWindow *self)
434
EmpathyCallWindowPriv *priv = GET_PRIV (self);
436
empathy_video_src_set_channel (priv->video_input,
437
EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS, gtk_adjustment_get_value (adj));
441
empathy_call_window_video_gamma_changed_cb (GtkAdjustment *adj,
442
EmpathyCallWindow *self)
445
EmpathyCallWindowPriv *priv = GET_PRIV (self);
447
empathy_video_src_set_channel (priv->video_input,
448
EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA, gtk_adjustment_get_value (adj));
453
empathy_call_window_create_video_input (EmpathyCallWindow *self)
455
EmpathyCallWindowPriv *priv = GET_PRIV (self);
458
hbox = gtk_hbox_new (TRUE, 3);
460
priv->video_contrast = empathy_call_window_create_video_input_add_slider (
461
self, _("Contrast"), hbox);
463
priv->video_brightness = empathy_call_window_create_video_input_add_slider (
464
self, _("Brightness"), hbox);
466
priv->video_gamma = empathy_call_window_create_video_input_add_slider (
467
self, _("Gamma"), hbox);
473
empathy_call_window_setup_video_input (EmpathyCallWindow *self)
475
EmpathyCallWindowPriv *priv = GET_PRIV (self);
479
supported = empathy_video_src_get_supported_channels (priv->video_input);
481
if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_CONTRAST)
483
adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_contrast));
485
gtk_adjustment_set_value (adj,
486
empathy_video_src_get_channel (priv->video_input,
487
EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST));
489
g_signal_connect (G_OBJECT (adj), "value-changed",
490
G_CALLBACK (empathy_call_window_video_contrast_changed_cb), self);
492
gtk_widget_set_sensitive (priv->video_contrast, TRUE);
495
if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_BRIGHTNESS)
497
adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_brightness));
499
gtk_adjustment_set_value (adj,
500
empathy_video_src_get_channel (priv->video_input,
501
EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS));
503
g_signal_connect (G_OBJECT (adj), "value-changed",
504
G_CALLBACK (empathy_call_window_video_brightness_changed_cb), self);
505
gtk_widget_set_sensitive (priv->video_brightness, TRUE);
508
if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_GAMMA)
510
adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_gamma));
512
gtk_adjustment_set_value (adj,
513
empathy_video_src_get_channel (priv->video_input,
514
EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA));
516
g_signal_connect (G_OBJECT (adj), "value-changed",
517
G_CALLBACK (empathy_call_window_video_gamma_changed_cb), self);
518
gtk_widget_set_sensitive (priv->video_gamma, TRUE);
523
empathy_call_window_mic_volume_changed_cb (GtkAdjustment *adj,
524
EmpathyCallWindow *self)
526
EmpathyCallWindowPriv *priv = GET_PRIV (self);
529
volume = gtk_adjustment_get_value (adj)/100.0;
531
/* Don't store the volume because of muting */
532
if (volume > 0 || gtk_toggle_tool_button_get_active (
533
GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
534
priv->volume = volume;
536
/* Ensure that the toggle button is active if the volume is > 0 and inactive
537
* if it's smaller than 0 */
538
if ((volume > 0) != gtk_toggle_tool_button_get_active (
539
GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
540
gtk_toggle_tool_button_set_active (
541
GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), volume > 0);
543
empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
548
empathy_call_window_audio_input_level_changed_cb (EmpathyGstAudioSrc *src,
549
gdouble level, EmpathyCallWindow *window)
552
EmpathyCallWindowPriv *priv = GET_PRIV (window);
554
value = CLAMP (pow (10, level / 20), 0.0, 1.0);
555
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
560
empathy_call_window_create_audio_input (EmpathyCallWindow *self)
562
EmpathyCallWindowPriv *priv = GET_PRIV (self);
563
GtkWidget *hbox, *vbox, *label;
565
hbox = gtk_hbox_new (TRUE, 3);
567
vbox = gtk_vbox_new (FALSE, 3);
568
gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 3);
570
priv->volume_scale = gtk_vscale_new_with_range (0, 150, 100);
571
gtk_range_set_inverted (GTK_RANGE (priv->volume_scale), TRUE);
572
label = gtk_label_new (_("Volume"));
574
priv->audio_input_adj = gtk_range_get_adjustment (
575
GTK_RANGE (priv->volume_scale));
576
priv->volume = empathy_audio_src_get_volume (EMPATHY_GST_AUDIO_SRC
577
(priv->audio_input));
578
gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
580
g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
581
G_CALLBACK (empathy_call_window_mic_volume_changed_cb), self);
583
gtk_box_pack_start (GTK_BOX (vbox), priv->volume_scale, TRUE, TRUE, 3);
584
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 3);
586
priv->volume_progress_bar = gtk_progress_bar_new ();
588
gtk_progress_bar_set_orientation (
589
GTK_PROGRESS_BAR (priv->volume_progress_bar),
590
GTK_PROGRESS_BOTTOM_TO_TOP);
592
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
595
gtk_box_pack_start (GTK_BOX (hbox), priv->volume_progress_bar, FALSE, FALSE,
602
create_video_output_widget (EmpathyCallWindow *self)
604
EmpathyCallWindowPriv *priv = GET_PRIV (self);
607
g_assert (priv->video_output == NULL);
608
g_assert (priv->pipeline != NULL);
610
bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
611
priv->video_output = empathy_video_widget_new (bus);
613
gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
614
priv->video_output, TRUE, TRUE, 0);
616
gtk_widget_add_events (priv->video_output,
617
GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
618
g_signal_connect (G_OBJECT (priv->video_output), "button-press-event",
619
G_CALLBACK (empathy_call_window_video_button_press_cb), self);
621
g_object_unref (bus);
625
create_video_input (EmpathyCallWindow *self)
627
EmpathyCallWindowPriv *priv = GET_PRIV (self);
629
g_assert (priv->video_input == NULL);
630
priv->video_input = empathy_video_src_new ();
631
gst_object_ref (priv->video_input);
632
gst_object_sink (priv->video_input);
636
create_audio_input (EmpathyCallWindow *self)
638
EmpathyCallWindowPriv *priv = GET_PRIV (self);
640
g_assert (priv->audio_input == NULL);
641
priv->audio_input = empathy_audio_src_new ();
642
gst_object_ref (priv->audio_input);
643
gst_object_sink (priv->audio_input);
645
tp_g_signal_connect_object (priv->audio_input, "peak-level-changed",
646
G_CALLBACK (empathy_call_window_audio_input_level_changed_cb),
651
add_video_preview_to_pipeline (EmpathyCallWindow *self)
653
EmpathyCallWindowPriv *priv = GET_PRIV (self);
656
g_assert (priv->video_preview != NULL);
657
g_assert (priv->pipeline != NULL);
658
g_assert (priv->video_input != NULL);
659
g_assert (priv->video_tee != NULL);
661
preview = empathy_video_widget_get_element (
662
EMPATHY_VIDEO_WIDGET (priv->video_preview));
664
if (!gst_bin_add (GST_BIN (priv->pipeline), priv->video_input))
666
g_warning ("Could not add video input to pipeline");
670
if (!gst_bin_add (GST_BIN (priv->pipeline), priv->video_tee))
672
g_warning ("Could not add video tee to pipeline");
676
if (!gst_bin_add (GST_BIN (priv->pipeline), preview))
678
g_warning ("Could not add video preview to pipeline");
682
if (!gst_element_link (priv->video_input, priv->video_tee))
684
g_warning ("Could not link video input to video tee");
688
if (!gst_element_link (priv->video_tee, preview))
690
g_warning ("Could not link video tee to video preview");
696
create_video_preview (EmpathyCallWindow *self)
698
EmpathyCallWindowPriv *priv = GET_PRIV (self);
701
g_assert (priv->video_preview == NULL);
702
g_assert (priv->video_tee == NULL);
704
bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
706
priv->video_preview = empathy_video_widget_new_with_size (bus,
707
SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
708
g_object_set (priv->video_preview, "sync", FALSE, "async", TRUE, NULL);
710
gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
711
priv->video_preview, TRUE, TRUE, 0);
713
priv->video_tee = gst_element_factory_make ("tee", NULL);
714
gst_object_ref (priv->video_tee);
715
gst_object_sink (priv->video_tee);
717
g_object_unref (bus);
721
play_camera (EmpathyCallWindow *window,
724
EmpathyCallWindowPriv *priv = GET_PRIV (window);
728
if (priv->video_preview == NULL)
730
create_video_preview (window);
731
add_video_preview_to_pipeline (window);
735
state = GST_STATE_PLAYING;
737
state = GST_STATE_NULL;
739
preview = empathy_video_widget_get_element (
740
EMPATHY_VIDEO_WIDGET (priv->video_preview));
742
gst_element_set_state (preview, state);
743
gst_element_set_state (priv->video_input, state);
744
gst_element_set_state (priv->video_tee, state);
748
display_video_preview (EmpathyCallWindow *self,
751
EmpathyCallWindowPriv *priv = GET_PRIV (self);
755
/* Display the preview and hide the self avatar */
756
DEBUG ("Show video preview");
758
play_camera (self, TRUE);
759
gtk_widget_show (priv->video_preview);
760
gtk_widget_hide (priv->self_user_avatar_widget);
764
/* Display the self avatar and hide the preview */
765
DEBUG ("Show self avatar");
767
if (priv->video_preview != NULL)
769
gtk_widget_hide (priv->video_preview);
770
play_camera (self, FALSE);
772
gtk_widget_show (priv->self_user_avatar_widget);
777
empathy_call_window_set_state_connecting (EmpathyCallWindow *window)
779
EmpathyCallWindowPriv *priv = GET_PRIV (window);
781
empathy_call_window_status_message (window, _("Connecting…"));
782
priv->call_state = CONNECTING;
785
empathy_sound_start_playing (GTK_WIDGET (window),
786
EMPATHY_SOUND_PHONE_OUTGOING, MS_BETWEEN_RING);
790
disable_camera (EmpathyCallWindow *self)
792
EmpathyCallWindowPriv *priv = GET_PRIV (self);
794
if (priv->camera_state == CAMERA_STATE_OFF)
797
DEBUG ("Disable camera");
799
display_video_preview (self, FALSE);
801
if (priv->camera_state == CAMERA_STATE_ON)
802
empathy_call_window_set_send_video (self, CAMERA_STATE_OFF);
804
block_camera_control_signals (self);
805
gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
806
priv->tool_button_camera_on), FALSE);
807
gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
808
priv->tool_button_camera_preview), FALSE);
810
gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
811
priv->tool_button_camera_off), TRUE);
812
gtk_radio_action_set_current_value (GTK_RADIO_ACTION (priv->action_camera_on),
814
unblock_camera_control_signals (self);
816
priv->camera_state = CAMERA_STATE_OFF;
820
tool_button_camera_off_toggled_cb (GtkToggleToolButton *toggle,
821
EmpathyCallWindow *self)
823
EmpathyCallWindowPriv *priv = GET_PRIV (self);
825
if (!gtk_toggle_tool_button_get_active (toggle))
827
if (priv->camera_state == CAMERA_STATE_OFF)
829
/* We can't change the state by disabling the button */
830
block_camera_control_signals (self);
831
gtk_toggle_tool_button_set_active (toggle, TRUE);
832
unblock_camera_control_signals (self);
838
disable_camera (self);
842
enable_preview (EmpathyCallWindow *self)
844
EmpathyCallWindowPriv *priv = GET_PRIV (self);
846
if (priv->camera_state == CAMERA_STATE_PREVIEW)
849
DEBUG ("Enable preview");
851
if (priv->camera_state == CAMERA_STATE_ON)
853
/* preview is already displayed so we just have to stop sending */
854
empathy_call_window_set_send_video (self, CAMERA_STATE_PREVIEW);
858
display_video_preview (self, TRUE);
861
block_camera_control_signals (self);
862
gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
863
priv->tool_button_camera_off), FALSE);
864
gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
865
priv->tool_button_camera_on), FALSE);
867
gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
868
priv->tool_button_camera_preview), TRUE);
869
gtk_radio_action_set_current_value (GTK_RADIO_ACTION (priv->action_camera_on),
870
CAMERA_STATE_PREVIEW);
871
unblock_camera_control_signals (self);
873
priv->camera_state = CAMERA_STATE_PREVIEW;
877
tool_button_camera_preview_toggled_cb (GtkToggleToolButton *toggle,
878
EmpathyCallWindow *self)
880
EmpathyCallWindowPriv *priv = GET_PRIV (self);
882
if (!gtk_toggle_tool_button_get_active (toggle))
884
if (priv->camera_state == CAMERA_STATE_PREVIEW)
886
/* We can't change the state by disabling the button */
887
block_camera_control_signals (self);
888
gtk_toggle_tool_button_set_active (toggle, TRUE);
889
unblock_camera_control_signals (self);
895
enable_preview (self);
899
enable_camera (EmpathyCallWindow *self)
901
EmpathyCallWindowPriv *priv = GET_PRIV (self);
903
if (priv->camera_state == CAMERA_STATE_ON)
906
if (priv->video_input == NULL)
908
DEBUG ("Can't enable camera, no input");
913
DEBUG ("Enable camera");
915
empathy_call_window_set_send_video (self, CAMERA_STATE_ON);
917
block_camera_control_signals (self);
918
gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
919
priv->tool_button_camera_off), FALSE);
920
gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
921
priv->tool_button_camera_preview), FALSE);
923
gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
924
priv->tool_button_camera_on), TRUE);
925
gtk_radio_action_set_current_value (GTK_RADIO_ACTION (priv->action_camera_on),
927
unblock_camera_control_signals (self);
929
priv->camera_state = CAMERA_STATE_ON;
933
tool_button_camera_on_toggled_cb (GtkToggleToolButton *toggle,
934
EmpathyCallWindow *self)
936
EmpathyCallWindowPriv *priv = GET_PRIV (self);
938
if (!gtk_toggle_tool_button_get_active (toggle))
940
if (priv->camera_state == CAMERA_STATE_ON)
942
/* We can't change the state by disabling the button */
943
block_camera_control_signals (self);
944
gtk_toggle_tool_button_set_active (toggle, TRUE);
945
unblock_camera_control_signals (self);
951
enable_camera (self);
955
action_camera_change_cb (GtkRadioAction *action,
956
GtkRadioAction *current,
957
EmpathyCallWindow *self)
961
state = gtk_radio_action_get_current_value (current);
965
case CAMERA_STATE_OFF:
966
disable_camera (self);
969
case CAMERA_STATE_PREVIEW:
970
enable_preview (self);
973
case CAMERA_STATE_ON:
974
enable_camera (self);
978
g_assert_not_reached ();
983
create_pipeline (EmpathyCallWindow *self)
985
EmpathyCallWindowPriv *priv = GET_PRIV (self);
988
g_assert (priv->pipeline == NULL);
990
priv->pipeline = gst_pipeline_new (NULL);
991
priv->pipeline_playing = FALSE;
993
bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
994
priv->bus_message_source_id = gst_bus_add_watch (bus,
995
empathy_call_window_bus_message, self);
997
g_object_unref (bus);
1002
empathy_call_window_init (EmpathyCallWindow *self)
1004
EmpathyCallWindowPriv *priv;
1006
GtkWidget *top_vbox;
1013
priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
1014
EMPATHY_TYPE_CALL_WINDOW, EmpathyCallWindowPriv);
1016
filename = empathy_file_lookup ("empathy-call-window.ui", "src");
1017
gui = empathy_builder_get_file (filename,
1018
"call_window_vbox", &top_vbox,
1019
"errors_vbox", &priv->errors_vbox,
1020
"pane", &priv->pane,
1021
"statusbar", &priv->statusbar,
1022
"redial", &priv->redial_button,
1023
"microphone", &priv->mic_button,
1024
"toolbar", &priv->toolbar,
1025
"menuredial", &priv->redial,
1026
"ui_manager", &priv->ui_manager,
1027
"menufullscreen", &priv->menu_fullscreen,
1028
"camera_off", &priv->tool_button_camera_off,
1029
"camera_preview", &priv->tool_button_camera_preview,
1030
"camera_on", &priv->tool_button_camera_on,
1031
"action_camera_on", &priv->action_camera_on,
1032
"details_vbox", &priv->details_vbox,
1033
"vcodec_encoding_label", &priv->vcodec_encoding_label,
1034
"acodec_encoding_label", &priv->acodec_encoding_label,
1035
"acodec_decoding_label", &priv->acodec_decoding_label,
1036
"vcodec_decoding_label", &priv->vcodec_decoding_label,
1037
"audio_remote_candidate_label", &priv->audio_remote_candidate_label,
1038
"audio_local_candidate_label", &priv->audio_local_candidate_label,
1039
"video_remote_candidate_label", &priv->video_remote_candidate_label,
1040
"video_local_candidate_label", &priv->video_local_candidate_label,
1041
"video_remote_candidate_info_img", &priv->video_remote_candidate_info_img,
1042
"video_local_candidate_info_img", &priv->video_local_candidate_info_img,
1043
"audio_remote_candidate_info_img", &priv->audio_remote_candidate_info_img,
1044
"audio_local_candidate_info_img", &priv->audio_local_candidate_info_img,
1048
empathy_builder_connect (gui, self,
1049
"menuhangup", "activate", empathy_call_window_hangup_cb,
1050
"hangup", "clicked", empathy_call_window_hangup_cb,
1051
"menuredial", "activate", empathy_call_window_redial_cb,
1052
"redial", "clicked", empathy_call_window_redial_cb,
1053
"microphone", "toggled", empathy_call_window_mic_toggled_cb,
1054
"menufullscreen", "activate", empathy_call_window_fullscreen_cb,
1055
"camera_off", "toggled", tool_button_camera_off_toggled_cb,
1056
"camera_preview", "toggled", tool_button_camera_preview_toggled_cb,
1057
"camera_on", "toggled", tool_button_camera_on_toggled_cb,
1058
"action_camera_on", "changed", action_camera_change_cb,
1061
gtk_action_set_sensitive (priv->menu_fullscreen, FALSE);
1063
priv->lock = g_mutex_new ();
1065
gtk_container_add (GTK_CONTAINER (self), top_vbox);
1067
priv->content_hbox = gtk_hbox_new (FALSE, CONTENT_HBOX_SPACING);
1068
gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
1069
CONTENT_HBOX_BORDER_WIDTH);
1070
gtk_paned_pack1 (GTK_PANED (priv->pane), priv->content_hbox, TRUE, FALSE);
1072
/* remote user output frame */
1073
priv->remote_user_output_frame = gtk_frame_new (NULL);
1074
gtk_widget_set_size_request (priv->remote_user_output_frame,
1075
EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT);
1076
gtk_box_pack_start (GTK_BOX (priv->content_hbox),
1077
priv->remote_user_output_frame, TRUE, TRUE,
1078
CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1080
priv->remote_user_output_hbox = gtk_hbox_new (FALSE, 0);
1082
priv->remote_user_avatar_widget = gtk_image_new ();
1084
gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
1085
priv->remote_user_avatar_widget, TRUE, TRUE, 0);
1087
gtk_container_add (GTK_CONTAINER (priv->remote_user_output_frame),
1088
priv->remote_user_output_hbox);
1090
/* self user output frame */
1091
priv->self_user_output_frame = gtk_frame_new (NULL);
1092
gtk_widget_set_size_request (priv->self_user_output_frame,
1093
SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
1095
priv->self_user_output_hbox = gtk_hbox_new (FALSE, 0);
1097
priv->self_user_avatar_widget = gtk_image_new ();
1098
gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
1099
priv->self_user_avatar_widget, TRUE, TRUE, 0);
1101
gtk_container_add (GTK_CONTAINER (priv->self_user_output_frame),
1102
priv->self_user_output_hbox);
1104
create_pipeline (self);
1105
create_video_output_widget (self);
1106
create_audio_input (self);
1107
create_video_input (self);
1109
/* The call will be started as soon the pipeline is playing */
1110
priv->start_call_when_playing = TRUE;
1112
priv->vbox = gtk_vbox_new (FALSE, 3);
1113
gtk_box_pack_start (GTK_BOX (priv->content_hbox), priv->vbox,
1114
FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1115
gtk_box_pack_start (GTK_BOX (priv->vbox), priv->self_user_output_frame,
1118
empathy_call_window_setup_toolbar (self);
1120
priv->sidebar_button = gtk_toggle_button_new_with_mnemonic (_("_Sidebar"));
1121
arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
1122
g_signal_connect (G_OBJECT (priv->sidebar_button), "toggled",
1123
G_CALLBACK (empathy_call_window_sidebar_toggled_cb), self);
1125
gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
1127
h = gtk_hbox_new (FALSE, 3);
1128
gtk_box_pack_end (GTK_BOX (priv->vbox), h, FALSE, FALSE, 3);
1129
gtk_box_pack_end (GTK_BOX (h), priv->sidebar_button, FALSE, FALSE, 3);
1131
priv->sidebar = empathy_sidebar_new ();
1132
g_signal_connect (G_OBJECT (priv->sidebar),
1133
"hide", G_CALLBACK (empathy_call_window_sidebar_hidden_cb), self);
1134
g_signal_connect (G_OBJECT (priv->sidebar),
1135
"show", G_CALLBACK (empathy_call_window_sidebar_shown_cb), self);
1136
gtk_paned_pack2 (GTK_PANED (priv->pane), priv->sidebar, FALSE, FALSE);
1138
page = empathy_call_window_create_audio_input (self);
1139
empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar),
1140
_("Audio input"), page);
1142
page = empathy_call_window_create_video_input (self);
1143
empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar),
1144
_("Video input"), page);
1146
priv->dtmf_panel = empathy_call_window_create_dtmf (self);
1147
empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar),
1148
_("Dialpad"), priv->dtmf_panel);
1150
gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
1152
/* Put the details vbox in a scroll window as it can require a lot of
1153
* horizontal space. */
1154
scroll = gtk_scrolled_window_new (NULL, NULL);
1155
gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scroll),
1156
priv->details_vbox);
1158
empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Details"),
1161
gtk_widget_show_all (top_vbox);
1163
gtk_widget_hide (priv->sidebar);
1165
priv->fullscreen = empathy_call_window_fullscreen_new (self);
1166
empathy_call_window_fullscreen_set_video_widget (priv->fullscreen,
1167
priv->video_output);
1168
g_signal_connect (G_OBJECT (priv->fullscreen->leave_fullscreen_button),
1169
"clicked", G_CALLBACK (empathy_call_window_fullscreen_cb), self);
1171
g_signal_connect (G_OBJECT (self), "realize",
1172
G_CALLBACK (empathy_call_window_realized_cb), self);
1174
g_signal_connect (G_OBJECT (self), "delete-event",
1175
G_CALLBACK (empathy_call_window_delete_cb), self);
1177
g_signal_connect (G_OBJECT (self), "window-state-event",
1178
G_CALLBACK (empathy_call_window_state_event_cb), self);
1180
g_signal_connect (G_OBJECT (self), "key-press-event",
1181
G_CALLBACK (empathy_call_window_key_press_cb), self);
1183
priv->timer = g_timer_new ();
1185
g_object_ref (priv->ui_manager);
1186
g_object_unref (gui);
1188
empathy_geometry_bind (GTK_WINDOW (self), "call-window");
1191
/* Instead of specifying a width and a height, we specify only one size. That's
1192
because we want a square avatar icon. */
1194
init_contact_avatar_with_size (EmpathyContact *contact,
1195
GtkWidget *image_widget,
1198
GdkPixbuf *pixbuf_avatar = NULL;
1200
if (contact != NULL)
1202
pixbuf_avatar = empathy_pixbuf_avatar_from_contact_scaled (contact,
1206
if (pixbuf_avatar == NULL)
1208
pixbuf_avatar = empathy_pixbuf_from_icon_name_sized (
1209
EMPATHY_IMAGE_AVATAR_DEFAULT, size);
1212
gtk_image_set_from_pixbuf (GTK_IMAGE (image_widget), pixbuf_avatar);
1214
if (pixbuf_avatar != NULL)
1215
g_object_unref (pixbuf_avatar);
1219
set_window_title (EmpathyCallWindow *self)
1221
EmpathyCallWindowPriv *priv = GET_PRIV (self);
1224
if (priv->contact != NULL)
1226
/* translators: Call is a noun and %s is the contact name. This string
1227
* is used in the window title */
1228
tmp = g_strdup_printf (_("Call with %s"),
1229
empathy_contact_get_alias (priv->contact));
1230
gtk_window_set_title (GTK_WINDOW (self), tmp);
1235
gtk_window_set_title (GTK_WINDOW (self), _("Call with %d participants"));
1240
contact_name_changed_cb (EmpathyContact *contact,
1241
GParamSpec *pspec, EmpathyCallWindow *self)
1243
set_window_title (self);
1247
contact_avatar_changed_cb (EmpathyContact *contact,
1248
GParamSpec *pspec, GtkWidget *avatar_widget)
1251
GtkAllocation allocation;
1253
gtk_widget_get_allocation (avatar_widget, &allocation);
1254
size = allocation.height;
1258
/* the widget is not allocated yet, set a default size */
1259
size = MIN (REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT,
1260
REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH);
1263
init_contact_avatar_with_size (contact, avatar_widget, size);
1267
empathy_call_window_got_self_contact_cb (TpConnection *connection,
1268
EmpathyContact *contact, const GError *error, gpointer user_data,
1269
GObject *weak_object)
1271
EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1272
EmpathyCallWindowPriv *priv = GET_PRIV (self);
1274
init_contact_avatar_with_size (contact, priv->self_user_avatar_widget,
1275
MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
1277
g_signal_connect (contact, "notify::avatar",
1278
G_CALLBACK (contact_avatar_changed_cb), priv->self_user_avatar_widget);
1282
empathy_call_window_setup_avatars (EmpathyCallWindow *self,
1283
EmpathyCallHandler *handler)
1285
EmpathyCallWindowPriv *priv = GET_PRIV (self);
1286
TpConnection *connection;
1288
g_signal_connect (priv->contact, "notify::name",
1289
G_CALLBACK (contact_name_changed_cb), self);
1290
g_signal_connect (priv->contact, "notify::avatar",
1291
G_CALLBACK (contact_avatar_changed_cb),
1292
priv->remote_user_avatar_widget);
1294
/* Retrieving the self avatar */
1295
connection = empathy_contact_get_connection (priv->contact);
1296
empathy_tp_contact_factory_get_from_handle (connection,
1297
tp_connection_get_self_handle (connection),
1298
empathy_call_window_got_self_contact_cb, self, NULL,
1301
set_window_title (self);
1303
init_contact_avatar_with_size (priv->contact,
1304
priv->remote_user_avatar_widget,
1305
MIN (REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH,
1306
REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT));
1308
/* The remote avatar is shown by default and will be hidden when we receive
1309
video from the remote side. */
1310
gtk_widget_hide (priv->video_output);
1311
gtk_widget_show (priv->remote_user_avatar_widget);
1315
update_send_codec (EmpathyCallWindow *self,
1318
EmpathyCallWindowPriv *priv = GET_PRIV (self);
1325
codec = empathy_call_handler_get_send_audio_codec (priv->handler);
1326
widget = priv->acodec_encoding_label;
1330
codec = empathy_call_handler_get_send_video_codec (priv->handler);
1331
widget = priv->vcodec_encoding_label;
1337
tmp = g_strdup_printf ("%s/%u", codec->encoding_name, codec->clock_rate);
1338
gtk_label_set_text (GTK_LABEL (widget), tmp);
1343
send_audio_codec_notify_cb (GObject *object,
1347
EmpathyCallWindow *self = user_data;
1349
update_send_codec (self, TRUE);
1353
send_video_codec_notify_cb (GObject *object,
1357
EmpathyCallWindow *self = user_data;
1359
update_send_codec (self, FALSE);
1363
update_recv_codec (EmpathyCallWindow *self,
1366
EmpathyCallWindowPriv *priv = GET_PRIV (self);
1369
GString *str = NULL;
1373
codecs = empathy_call_handler_get_recv_audio_codecs (priv->handler);
1374
widget = priv->acodec_decoding_label;
1378
codecs = empathy_call_handler_get_recv_video_codecs (priv->handler);
1379
widget = priv->vcodec_decoding_label;
1385
for (l = codecs; l != NULL; l = g_list_next (l))
1387
FsCodec *codec = l->data;
1390
str = g_string_new (NULL);
1392
g_string_append (str, ", ");
1394
g_string_append_printf (str, "%s/%u", codec->encoding_name,
1398
gtk_label_set_text (GTK_LABEL (widget), str->str);
1399
g_string_free (str, TRUE);
1403
recv_audio_codecs_notify_cb (GObject *object,
1407
EmpathyCallWindow *self = user_data;
1409
update_recv_codec (self, TRUE);
1413
recv_video_codecs_notify_cb (GObject *object,
1417
EmpathyCallWindow *self = user_data;
1419
update_recv_codec (self, FALSE);
1422
static const gchar *
1423
candidate_type_to_str (FsCandidate *candidate)
1425
switch (candidate->type)
1427
case FS_CANDIDATE_TYPE_HOST:
1429
case FS_CANDIDATE_TYPE_SRFLX:
1430
return "server reflexive";
1431
case FS_CANDIDATE_TYPE_PRFLX:
1432
return "peer reflexive";
1433
case FS_CANDIDATE_TYPE_RELAY:
1435
case FS_CANDIDATE_TYPE_MULTICAST:
1442
static const gchar *
1443
candidate_type_to_desc (FsCandidate *candidate)
1445
switch (candidate->type)
1447
case FS_CANDIDATE_TYPE_HOST:
1448
return _("The IP address as seen by the machine");
1449
case FS_CANDIDATE_TYPE_SRFLX:
1450
return _("The IP address as seen by a server on the Internet");
1451
case FS_CANDIDATE_TYPE_PRFLX:
1452
return _("The IP address of the peer as seen by the other side");
1453
case FS_CANDIDATE_TYPE_RELAY:
1454
return _("The IP address of a relay server");
1455
case FS_CANDIDATE_TYPE_MULTICAST:
1456
return _("The IP address of the multicast group");
1463
update_candidat_widget (EmpathyCallWindow *self,
1466
FsCandidate *candidate)
1470
g_assert (candidate != NULL);
1471
str = g_strdup_printf ("%s %u (%s)", candidate->ip,
1472
candidate->port, candidate_type_to_str (candidate));
1474
gtk_label_set_text (GTK_LABEL (label), str);
1475
gtk_widget_set_tooltip_text (img, candidate_type_to_desc (candidate));
1481
candidates_changed_cb (GObject *object,
1483
EmpathyCallWindow *self)
1485
EmpathyCallWindowPriv *priv = GET_PRIV (self);
1486
FsCandidate *candidate = NULL;
1488
if (type == FS_MEDIA_TYPE_VIDEO)
1490
/* Update remote candidate */
1491
candidate = empathy_call_handler_get_video_remote_candidate (
1494
update_candidat_widget (self, priv->video_remote_candidate_label,
1495
priv->video_remote_candidate_info_img, candidate);
1497
/* Update local candidate */
1498
candidate = empathy_call_handler_get_video_local_candidate (
1501
update_candidat_widget (self, priv->video_local_candidate_label,
1502
priv->video_local_candidate_info_img, candidate);
1506
/* Update remote candidate */
1507
candidate = empathy_call_handler_get_audio_remote_candidate (
1510
update_candidat_widget (self, priv->audio_remote_candidate_label,
1511
priv->audio_remote_candidate_info_img, candidate);
1513
/* Update local candidate */
1514
candidate = empathy_call_handler_get_audio_local_candidate (
1517
update_candidat_widget (self, priv->audio_local_candidate_label,
1518
priv->audio_local_candidate_info_img, candidate);
1523
empathy_call_window_constructed (GObject *object)
1525
EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1526
EmpathyCallWindowPriv *priv = GET_PRIV (self);
1527
TpyCallChannel *call;
1529
g_assert (priv->handler != NULL);
1531
g_object_get (priv->handler, "call-channel", &call, NULL);
1532
priv->outgoing = (call == NULL);
1534
g_object_unref (call);
1536
g_object_get (priv->handler, "target-contact", &priv->contact, NULL);
1537
g_assert (priv->contact != NULL);
1539
empathy_call_window_setup_avatars (self, priv->handler);
1540
empathy_call_window_set_state_connecting (self);
1542
if (!empathy_call_handler_has_initial_video (priv->handler))
1544
gtk_toggle_tool_button_set_active (
1545
GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_off), TRUE);
1547
/* If call has InitialVideo, the preview will be started once the call has
1548
* been started (start_call()). */
1550
update_send_codec (self, TRUE);
1551
update_send_codec (self, FALSE);
1552
update_recv_codec (self, TRUE);
1553
update_recv_codec (self, FALSE);
1555
tp_g_signal_connect_object (priv->handler, "notify::send-audio-codec",
1556
G_CALLBACK (send_audio_codec_notify_cb), self, 0);
1557
tp_g_signal_connect_object (priv->handler, "notify::send-video-codec",
1558
G_CALLBACK (send_video_codec_notify_cb), self, 0);
1559
tp_g_signal_connect_object (priv->handler, "notify::recv-audio-codecs",
1560
G_CALLBACK (recv_audio_codecs_notify_cb), self, 0);
1561
tp_g_signal_connect_object (priv->handler, "notify::recv-video-codecs",
1562
G_CALLBACK (recv_video_codecs_notify_cb), self, 0);
1564
tp_g_signal_connect_object (priv->handler, "candidates-changed",
1565
G_CALLBACK (candidates_changed_cb), self, 0);
1568
static void empathy_call_window_dispose (GObject *object);
1569
static void empathy_call_window_finalize (GObject *object);
1572
empathy_call_window_set_property (GObject *object,
1573
guint property_id, const GValue *value, GParamSpec *pspec)
1575
EmpathyCallWindowPriv *priv = GET_PRIV (object);
1577
switch (property_id)
1579
case PROP_CALL_HANDLER:
1580
priv->handler = g_value_dup_object (value);
1583
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1588
empathy_call_window_get_property (GObject *object,
1589
guint property_id, GValue *value, GParamSpec *pspec)
1591
EmpathyCallWindowPriv *priv = GET_PRIV (object);
1593
switch (property_id)
1595
case PROP_CALL_HANDLER:
1596
g_value_set_object (value, priv->handler);
1599
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1604
empathy_call_window_class_init (
1605
EmpathyCallWindowClass *empathy_call_window_class)
1607
GObjectClass *object_class = G_OBJECT_CLASS (empathy_call_window_class);
1608
GParamSpec *param_spec;
1610
g_type_class_add_private (empathy_call_window_class,
1611
sizeof (EmpathyCallWindowPriv));
1613
object_class->constructed = empathy_call_window_constructed;
1614
object_class->set_property = empathy_call_window_set_property;
1615
object_class->get_property = empathy_call_window_get_property;
1617
object_class->dispose = empathy_call_window_dispose;
1618
object_class->finalize = empathy_call_window_finalize;
1620
param_spec = g_param_spec_object ("handler",
1621
"handler", "The call handler",
1622
EMPATHY_TYPE_CALL_HANDLER,
1623
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
1624
g_object_class_install_property (object_class,
1625
PROP_CALL_HANDLER, param_spec);
1629
empathy_call_window_dispose (GObject *object)
1631
EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1632
EmpathyCallWindowPriv *priv = GET_PRIV (self);
1634
if (priv->dispose_has_run)
1637
priv->dispose_has_run = TRUE;
1639
if (priv->handler != NULL)
1641
empathy_call_handler_stop_call (priv->handler);
1642
tp_clear_object (&priv->handler);
1645
if (priv->bus_message_source_id != 0)
1647
g_source_remove (priv->bus_message_source_id);
1648
priv->bus_message_source_id = 0;
1651
tp_clear_object (&priv->pipeline);
1652
tp_clear_object (&priv->video_input);
1653
tp_clear_object (&priv->audio_input);
1654
tp_clear_object (&priv->video_tee);
1655
tp_clear_object (&priv->ui_manager);
1656
tp_clear_object (&priv->fullscreen);
1658
g_list_free_full (priv->notifiers, g_object_unref);
1660
if (priv->timer_id != 0)
1661
g_source_remove (priv->timer_id);
1664
if (priv->contact != NULL)
1666
g_signal_handlers_disconnect_by_func (priv->contact,
1667
contact_name_changed_cb, self);
1668
priv->contact = NULL;
1672
G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose (object);
1676
disconnect_video_output_motion_handler (EmpathyCallWindow *self)
1678
EmpathyCallWindowPriv *priv = GET_PRIV (self);
1680
if (priv->video_output_motion_handler_id != 0)
1682
g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1683
priv->video_output_motion_handler_id);
1684
priv->video_output_motion_handler_id = 0;
1689
empathy_call_window_finalize (GObject *object)
1691
EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
1692
EmpathyCallWindowPriv *priv = GET_PRIV (self);
1694
disconnect_video_output_motion_handler (self);
1696
/* free any data held directly by the object here */
1697
g_mutex_free (priv->lock);
1699
g_timer_destroy (priv->timer);
1701
G_OBJECT_CLASS (empathy_call_window_parent_class)->finalize (object);
1706
empathy_call_window_new (EmpathyCallHandler *handler)
1708
return EMPATHY_CALL_WINDOW (
1709
g_object_new (EMPATHY_TYPE_CALL_WINDOW, "handler", handler, NULL));
1713
empathy_call_window_conference_added_cb (EmpathyCallHandler *handler,
1714
GstElement *conference, gpointer user_data)
1716
EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1717
EmpathyCallWindowPriv *priv = GET_PRIV (self);
1718
FsElementAddedNotifier *notifier;
1721
g_debug ("Conference added");
1723
/* Add notifier to set the various element properties as needed */
1724
notifier = fs_element_added_notifier_new ();
1725
keyfile = fs_utils_get_default_element_properties (conference);
1727
if (keyfile != NULL)
1728
fs_element_added_notifier_set_properties_from_keyfile (notifier, keyfile);
1730
fs_element_added_notifier_add (notifier, GST_BIN (priv->pipeline));
1732
priv->notifiers = g_list_prepend (priv->notifiers, notifier);
1734
gst_bin_add (GST_BIN (priv->pipeline), conference);
1735
gst_element_set_state (conference, GST_STATE_PLAYING);
1739
empathy_call_window_conference_removed_cb (EmpathyCallHandler *handler,
1740
GstElement *conference, gpointer user_data)
1742
EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1743
EmpathyCallWindowPriv *priv = GET_PRIV (self);
1745
gst_bin_remove (GST_BIN (priv->pipeline), conference);
1746
gst_element_set_state (conference, GST_STATE_NULL);
1750
empathy_call_window_reset_pipeline (EmpathyCallWindow *self)
1752
GstStateChangeReturn state_change_return;
1753
EmpathyCallWindowPriv *priv = GET_PRIV (self);
1755
if (priv->pipeline == NULL)
1758
if (priv->bus_message_source_id != 0)
1760
g_source_remove (priv->bus_message_source_id);
1761
priv->bus_message_source_id = 0;
1764
state_change_return = gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1766
if (state_change_return == GST_STATE_CHANGE_SUCCESS ||
1767
state_change_return == GST_STATE_CHANGE_NO_PREROLL)
1769
if (priv->pipeline != NULL)
1770
g_object_unref (priv->pipeline);
1771
priv->pipeline = NULL;
1773
g_signal_handlers_disconnect_by_func (priv->audio_input_adj,
1774
empathy_call_window_mic_volume_changed_cb, self);
1776
if (priv->video_tee != NULL)
1777
g_object_unref (priv->video_tee);
1778
priv->video_tee = NULL;
1780
if (priv->video_preview != NULL)
1781
gtk_widget_destroy (priv->video_preview);
1782
priv->video_preview = NULL;
1784
priv->funnel = NULL;
1786
create_pipeline (self);
1787
/* Call will be started when user will hit the 'redial' button */
1788
priv->start_call_when_playing = FALSE;
1789
gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1795
g_message ("Error: could not destroy pipeline. Closing call window");
1796
gtk_widget_destroy (GTK_WIDGET (self));
1803
reset_details_pane (EmpathyCallWindow *self)
1805
EmpathyCallWindowPriv *priv = GET_PRIV (self);
1807
gtk_label_set_text (GTK_LABEL (priv->vcodec_encoding_label), _("Unknown"));
1808
gtk_label_set_text (GTK_LABEL (priv->acodec_encoding_label), _("Unknown"));
1809
gtk_label_set_text (GTK_LABEL (priv->vcodec_decoding_label), _("Unknown"));
1810
gtk_label_set_text (GTK_LABEL (priv->acodec_decoding_label), _("Unknown"));
1814
empathy_call_window_disconnected (EmpathyCallWindow *self,
1817
gboolean could_disconnect = FALSE;
1818
EmpathyCallWindowPriv *priv = GET_PRIV (self);
1819
gboolean could_reset_pipeline;
1821
/* Leave full screen mode if needed */
1822
gtk_window_unfullscreen (GTK_WINDOW (self));
1824
gtk_action_set_sensitive (priv->menu_fullscreen, FALSE);
1826
could_reset_pipeline = empathy_call_window_reset_pipeline (self);
1828
if (priv->call_state == CONNECTING)
1829
empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
1831
if (priv->call_state != REDIALING)
1832
priv->call_state = DISCONNECTED;
1834
if (could_reset_pipeline)
1836
g_mutex_lock (priv->lock);
1838
g_timer_stop (priv->timer);
1840
if (priv->timer_id != 0)
1841
g_source_remove (priv->timer_id);
1844
g_mutex_unlock (priv->lock);
1847
/* We are about to destroy the window, no need to update it or create
1848
* a video preview */
1851
empathy_call_window_status_message (self, _("Disconnected"));
1853
gtk_action_set_sensitive (priv->redial, TRUE);
1854
gtk_widget_set_sensitive (priv->redial_button, TRUE);
1856
/* Unsensitive the camera and mic button */
1857
gtk_widget_set_sensitive (priv->tool_button_camera_on, FALSE);
1858
gtk_action_set_sensitive (priv->action_camera_on, FALSE);
1859
gtk_widget_set_sensitive (priv->mic_button, FALSE);
1861
/* Be sure that the mic button is enabled */
1862
gtk_toggle_tool_button_set_active (
1863
GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), TRUE);
1865
if (priv->camera_state == CAMERA_STATE_ON)
1867
/* Enable the 'preview' button as we are not sending atm. */
1868
gtk_toggle_tool_button_set_active (
1869
GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_preview), TRUE);
1871
else if (priv->camera_state == CAMERA_STATE_PREVIEW)
1873
/* Restart the preview with the new pipeline. */
1874
display_video_preview (self, TRUE);
1877
gtk_progress_bar_set_fraction (
1878
GTK_PROGRESS_BAR (priv->volume_progress_bar), 0);
1880
/* destroy the video output; it will be recreated when we'll redial */
1881
disconnect_video_output_motion_handler (self);
1882
if (priv->video_output != NULL)
1883
gtk_widget_destroy (priv->video_output);
1884
priv->video_output = NULL;
1886
gtk_widget_show (priv->remote_user_avatar_widget);
1888
reset_details_pane (self);
1890
priv->sending_video = FALSE;
1891
priv->call_started = FALSE;
1893
could_disconnect = TRUE;
1895
/* TODO: display the self avatar of the preview (depends if the "Always
1896
* Show Video Preview" is enabled or not) */
1899
return could_disconnect;
1904
empathy_call_window_channel_closed_cb (EmpathyCallHandler *handler,
1907
EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
1908
EmpathyCallWindowPriv *priv = GET_PRIV (self);
1910
if (empathy_call_window_disconnected (self, TRUE) &&
1911
priv->call_state == REDIALING)
1912
empathy_call_window_restart_call (self);
1916
empathy_call_window_sink_removed_cb (EmpathyCallHandler *handler,
1918
FsMediaType media_type,
1919
EmpathyCallWindow *self)
1921
EmpathyCallWindowPriv *priv = GET_PRIV (self);
1923
g_print ("window: removing content\n");
1926
* This assumes that there is only one video stream per channel...
1929
if (media_type == FS_MEDIA_TYPE_VIDEO)
1931
if (priv->funnel != NULL)
1935
output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1936
(priv->video_output));
1938
gst_element_set_state (output, GST_STATE_NULL);
1939
gst_element_set_state (priv->funnel, GST_STATE_NULL);
1941
gst_bin_remove (GST_BIN (priv->pipeline), output);
1942
gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
1943
priv->funnel = NULL;
1947
else if (media_type == FS_MEDIA_TYPE_AUDIO)
1949
if (priv->audio_output != NULL)
1951
gst_element_set_state (priv->audio_output, GST_STATE_NULL);
1953
gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
1954
priv->audio_output = NULL;
1962
/* Called with global lock held */
1964
empathy_call_window_get_video_sink_pad (EmpathyCallWindow *self)
1966
EmpathyCallWindowPriv *priv = GET_PRIV (self);
1970
if (priv->funnel == NULL)
1972
output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1973
(priv->video_output));
1975
priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
1979
g_warning ("Could not create fsfunnel");
1983
if (!gst_bin_add (GST_BIN (priv->pipeline), priv->funnel))
1985
gst_object_unref (priv->funnel);
1986
priv->funnel = NULL;
1987
g_warning ("Could not add funnel to pipeline");
1991
if (!gst_bin_add (GST_BIN (priv->pipeline), output))
1993
g_warning ("Could not add the video output widget to the pipeline");
1997
if (!gst_element_link (priv->funnel, output))
1999
g_warning ("Could not link output sink to funnel");
2000
goto error_output_added;
2003
if (gst_element_set_state (output, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2005
g_warning ("Could not start video sink");
2006
goto error_output_added;
2009
if (gst_element_set_state (priv->funnel, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2011
g_warning ("Could not start funnel");
2012
goto error_output_added;
2016
pad = gst_element_get_request_pad (priv->funnel, "sink%d");
2019
g_warning ("Could not get request pad from funnel");
2026
gst_element_set_locked_state (priv->funnel, TRUE);
2027
gst_element_set_locked_state (output, TRUE);
2029
gst_element_set_state (priv->funnel, GST_STATE_NULL);
2030
gst_element_set_state (output, GST_STATE_NULL);
2032
gst_bin_remove (GST_BIN (priv->pipeline), output);
2033
gst_element_set_locked_state (output, FALSE);
2037
gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
2038
priv->funnel = NULL;
2043
/* Called with global lock held */
2045
empathy_call_window_get_audio_sink_pad (EmpathyCallWindow *self)
2047
EmpathyCallWindowPriv *priv = GET_PRIV (self);
2049
GstPadTemplate *template;
2051
if (priv->audio_output == NULL)
2053
priv->audio_output = empathy_audio_sink_new ();
2055
if (!gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output))
2057
g_warning ("Could not add audio sink to pipeline");
2058
g_object_unref (priv->audio_output);
2059
goto error_add_output;
2062
if (gst_element_set_state (priv->audio_output, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2064
g_warning ("Could not start audio sink");
2069
template = gst_element_class_get_pad_template (
2070
GST_ELEMENT_GET_CLASS (priv->audio_output), "sink%d");
2072
pad = gst_element_request_pad (priv->audio_output,
2073
template, NULL, NULL);
2077
g_warning ("Could not get sink pad from sink");
2084
gst_element_set_locked_state (priv->audio_output, TRUE);
2085
gst_element_set_state (priv->audio_output, GST_STATE_NULL);
2086
gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
2087
priv->audio_output = NULL;
2095
empathy_call_window_update_timer (gpointer user_data)
2097
EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
2098
EmpathyCallWindowPriv *priv = GET_PRIV (self);
2102
time_ = g_timer_elapsed (priv->timer, NULL);
2104
/* Translators: number of minutes:seconds the caller has been connected */
2105
str = g_strdup_printf (_("Connected — %d:%02dm"), (int) time_ / 60,
2107
empathy_call_window_status_message (self, str);
2115
display_error (EmpathyCallWindow *self,
2116
TpyCallChannel *call,
2120
const gchar *details)
2122
EmpathyCallWindowPriv *priv = GET_PRIV (self);
2123
GtkWidget *info_bar;
2124
GtkWidget *content_area;
2131
/* Create info bar */
2132
info_bar = gtk_info_bar_new_with_buttons (GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
2135
gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
2137
content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
2139
/* hbox containing the image and the messages vbox */
2140
hbox = gtk_hbox_new (FALSE, 3);
2141
gtk_container_add (GTK_CONTAINER (content_area), hbox);
2144
image = gtk_image_new_from_icon_name (img, GTK_ICON_SIZE_DIALOG);
2145
gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
2147
/* vbox containing the main message and the details expander */
2148
vbox = gtk_vbox_new (FALSE, 3);
2149
gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
2152
txt = g_strdup_printf ("<b>%s</b>\n%s", title, desc);
2154
label = gtk_label_new (NULL);
2155
gtk_label_set_markup (GTK_LABEL (label), txt);
2156
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
2157
gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
2160
gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
2163
if (details != NULL)
2165
GtkWidget *expander;
2167
expander = gtk_expander_new (_("Technical Details"));
2169
txt = g_strdup_printf ("<i>%s</i>", details);
2171
label = gtk_label_new (NULL);
2172
gtk_label_set_markup (GTK_LABEL (label), txt);
2173
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
2174
gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
2177
gtk_container_add (GTK_CONTAINER (expander), label);
2178
gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
2181
g_signal_connect (info_bar, "response",
2182
G_CALLBACK (gtk_widget_destroy), NULL);
2184
gtk_box_pack_start (GTK_BOX (priv->errors_vbox), info_bar,
2185
FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
2186
gtk_widget_show_all (info_bar);
2190
media_stream_error_to_txt (EmpathyCallWindow *self,
2191
TpyCallChannel *call,
2193
TpMediaStreamError error)
2195
EmpathyCallWindowPriv *priv = GET_PRIV (self);
2196
const gchar *cm = NULL;
2202
case TP_MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED:
2204
return g_strdup_printf (
2205
_("%s's software does not understand any of the audio formats "
2206
"supported by your computer"),
2207
empathy_contact_get_alias (priv->contact));
2209
return g_strdup_printf (
2210
_("%s's software does not understand any of the video formats "
2211
"supported by your computer"),
2212
empathy_contact_get_alias (priv->contact));
2214
case TP_MEDIA_STREAM_ERROR_CONNECTION_FAILED:
2215
return g_strdup_printf (
2216
_("Can't establish a connection to %s. "
2217
"One of you might be on a network that does not allow "
2218
"direct connections."),
2219
empathy_contact_get_alias (priv->contact));
2221
case TP_MEDIA_STREAM_ERROR_NETWORK_ERROR:
2222
return g_strdup (_("There was a failure on the network"));
2224
case TP_MEDIA_STREAM_ERROR_NO_CODECS:
2226
return g_strdup (_("The audio formats necessary for this call "
2227
"are not installed on your computer"));
2229
return g_strdup (_("The video formats necessary for this call "
2230
"are not installed on your computer"));
2232
case TP_MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR:
2233
tp_connection_parse_object_path (
2234
tp_channel_borrow_connection (TP_CHANNEL (call)),
2237
url = g_strdup_printf ("http://bugs.freedesktop.org/enter_bug.cgi?"
2238
"product=Telepathy&component=%s", cm);
2240
result = g_strdup_printf (
2241
_("Something unexpected happened in a Telepathy component. "
2242
"Please <a href=\"%s\">report this bug</a> and attach "
2243
"logs gathered from the 'Debug' window in the Help menu."), url);
2249
case TP_MEDIA_STREAM_ERROR_MEDIA_ERROR:
2250
return g_strdup (_("There was a failure in the call engine"));
2252
case TP_MEDIA_STREAM_ERROR_EOS:
2253
return g_strdup (_("The end of the stream was reached"));
2255
case TP_MEDIA_STREAM_ERROR_UNKNOWN:
2262
empathy_call_window_stream_error (EmpathyCallWindow *self,
2263
TpyCallChannel *call,
2272
desc = media_stream_error_to_txt (self, call, audio, code);
2275
/* No description, use the error message. That's not great as it's not
2276
* localized but it's better than nothing. */
2277
display_error (self, call, icon, title, msg, NULL);
2281
display_error (self, call, icon, title, desc, msg);
2287
empathy_call_window_audio_stream_error (TpyCallChannel *call,
2290
EmpathyCallWindow *self)
2292
empathy_call_window_stream_error (self, call, TRUE, code, msg,
2293
"gnome-stock-mic", _("Can't establish audio stream"));
2297
empathy_call_window_video_stream_error (TpyCallChannel *call,
2300
EmpathyCallWindow *self)
2302
empathy_call_window_stream_error (self, call, FALSE, code, msg,
2303
"camera-web", _("Can't establish video stream"));
2308
empathy_call_window_state_changed_cb (EmpathyCallHandler *handler,
2310
EmpathyCallWindow *self)
2312
EmpathyCallWindowPriv *priv = GET_PRIV (self);
2313
TpyCallChannel *call;
2314
gboolean can_send_video;
2316
if (state != TPY_CALL_STATE_ACCEPTED)
2319
if (priv->call_state == CONNECTED)
2322
g_timer_start (priv->timer);
2323
priv->call_state = CONNECTED;
2325
empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
2327
can_send_video = priv->video_input != NULL &&
2328
empathy_contact_can_voip_video (priv->contact);
2330
g_object_get (priv->handler, "call-channel", &call, NULL);
2332
if (tpy_call_channel_has_dtmf (call))
2333
gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
2335
if (priv->video_input == NULL)
2336
empathy_call_window_set_send_video (self, CAMERA_STATE_OFF);
2338
gtk_widget_set_sensitive (priv->tool_button_camera_on, can_send_video);
2339
gtk_action_set_sensitive (priv->action_camera_on, can_send_video);
2341
gtk_action_set_sensitive (priv->redial, FALSE);
2342
gtk_widget_set_sensitive (priv->redial_button, FALSE);
2344
gtk_widget_set_sensitive (priv->mic_button, TRUE);
2346
gtk_widget_hide (priv->video_output);
2347
gtk_widget_show (priv->remote_user_avatar_widget);
2349
g_object_unref (call);
2351
g_mutex_lock (priv->lock);
2353
priv->timer_id = g_timeout_add_seconds (1,
2354
empathy_call_window_update_timer, self);
2356
g_mutex_unlock (priv->lock);
2358
empathy_call_window_update_timer (self);
2360
gtk_action_set_sensitive (priv->menu_fullscreen, TRUE);
2364
emapthy_call_window_show_video_output_cb (gpointer user_data)
2366
EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
2368
gtk_widget_hide (self->priv->remote_user_avatar_widget);
2369
gtk_widget_show (self->priv->video_output);
2374
/* Called from the streaming thread */
2376
empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
2377
GstPad *src, guint media_type, gpointer user_data)
2379
EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
2380
EmpathyCallWindowPriv *priv = GET_PRIV (self);
2381
gboolean retval = FALSE;
2385
g_mutex_lock (priv->lock);
2389
case TP_MEDIA_STREAM_TYPE_AUDIO:
2390
pad = empathy_call_window_get_audio_sink_pad (self);
2392
case TP_MEDIA_STREAM_TYPE_VIDEO:
2393
g_idle_add (emapthy_call_window_show_video_output_cb, self);
2394
pad = empathy_call_window_get_video_sink_pad (self);
2397
g_assert_not_reached ();
2403
if (GST_PAD_LINK_FAILED (gst_pad_link (src, pad)))
2404
g_warning ("Could not link %s sink pad",
2405
media_type == TP_MEDIA_STREAM_TYPE_AUDIO ? "audio" : "video");
2409
gst_object_unref (pad);
2413
/* If no sink could be linked, try to add fakesink to prevent the whole call
2418
GstElement *fakesink = gst_element_factory_make ("fakesink", NULL);
2420
if (gst_bin_add (GST_BIN (priv->pipeline), fakesink))
2422
GstPad *sinkpad = gst_element_get_static_pad (fakesink, "sink");
2423
if (gst_element_set_state (fakesink, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE ||
2424
GST_PAD_LINK_FAILED (gst_pad_link (src, sinkpad)))
2426
gst_element_set_locked_state (fakesink, TRUE);
2427
gst_element_set_state (fakesink, GST_STATE_NULL);
2428
gst_bin_remove (GST_BIN (priv->pipeline), fakesink);
2432
g_debug ("Could not link real sink, linked fakesink instead");
2434
gst_object_unref (sinkpad);
2438
gst_object_unref (fakesink);
2443
g_mutex_unlock (priv->lock);
2449
empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
2450
GstPad *sink, FsMediaType media_type, gpointer user_data)
2452
EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
2453
EmpathyCallWindowPriv *priv = GET_PRIV (self);
2455
gboolean retval = FALSE;
2459
case FS_MEDIA_TYPE_AUDIO:
2460
if (!gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input))
2462
g_warning ("Could not add audio source to pipeline");
2466
pad = gst_element_get_static_pad (priv->audio_input, "src");
2469
gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
2470
g_warning ("Could not get source pad from audio source");
2474
if (GST_PAD_LINK_FAILED (gst_pad_link (pad, sink)))
2476
gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
2477
g_warning ("Could not link audio source to farsight");
2481
if (gst_element_set_state (priv->audio_input, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2483
g_warning ("Could not start audio source");
2484
gst_element_set_state (priv->audio_input, GST_STATE_NULL);
2485
gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
2491
case FS_MEDIA_TYPE_VIDEO:
2492
if (priv->video_input != NULL)
2494
if (priv->video_tee != NULL)
2496
pad = gst_element_get_request_pad (priv->video_tee, "src%d");
2497
if (GST_PAD_LINK_FAILED (gst_pad_link (pad, sink)))
2499
g_warning ("Could not link video source input pipeline");
2502
gst_object_unref (pad);
2509
g_assert_not_reached ();
2516
empathy_call_window_remove_video_input (EmpathyCallWindow *self)
2518
EmpathyCallWindowPriv *priv = GET_PRIV (self);
2519
GstElement *preview;
2521
disable_camera (self);
2523
DEBUG ("remove video input");
2524
preview = empathy_video_widget_get_element (
2525
EMPATHY_VIDEO_WIDGET (priv->video_preview));
2527
gst_element_set_state (priv->video_input, GST_STATE_NULL);
2528
gst_element_set_state (priv->video_tee, GST_STATE_NULL);
2529
gst_element_set_state (preview, GST_STATE_NULL);
2531
gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
2532
priv->video_tee, preview, NULL);
2534
g_object_unref (priv->video_input);
2535
priv->video_input = NULL;
2536
g_object_unref (priv->video_tee);
2537
priv->video_tee = NULL;
2538
gtk_widget_destroy (priv->video_preview);
2539
priv->video_preview = NULL;
2541
gtk_widget_set_sensitive (priv->tool_button_camera_on, FALSE);
2542
gtk_action_set_sensitive (priv->action_camera_on, FALSE);
2543
gtk_widget_set_sensitive (priv->tool_button_camera_preview, FALSE);
2547
start_call (EmpathyCallWindow *self)
2549
EmpathyCallWindowPriv *priv = GET_PRIV (self);
2551
priv->call_started = TRUE;
2552
empathy_call_handler_start_call (priv->handler,
2553
gtk_get_current_event_time ());
2555
if (empathy_call_handler_has_initial_video (priv->handler))
2557
TpyCallChannel *call;
2560
g_object_get (priv->handler, "call-channel", &call, NULL);
2561
s = tpy_call_channel_get_video_state (call);
2563
if (s == TPY_SENDING_STATE_PENDING_SEND ||
2564
s == TPY_SENDING_STATE_SENDING)
2566
/* Enable 'send video' buttons and display the preview */
2567
gtk_toggle_tool_button_set_active (
2568
GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_on),
2573
gtk_toggle_tool_button_set_active (
2574
GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_off),
2577
if (priv->video_preview == NULL)
2579
create_video_preview (self);
2580
add_video_preview_to_pipeline (self);
2584
g_object_unref (call);
2589
empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
2592
EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
2593
EmpathyCallWindowPriv *priv = GET_PRIV (self);
2596
empathy_call_handler_bus_message (priv->handler, bus, message);
2598
switch (GST_MESSAGE_TYPE (message))
2600
case GST_MESSAGE_STATE_CHANGED:
2601
if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
2603
gst_message_parse_state_changed (message, NULL, &newstate, NULL);
2604
if (newstate == GST_STATE_PAUSED)
2605
empathy_call_window_setup_video_input (self);
2607
if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
2608
!priv->call_started)
2610
gst_message_parse_state_changed (message, NULL, &newstate, NULL);
2611
if (newstate == GST_STATE_PAUSED)
2613
gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2614
priv->pipeline_playing = TRUE;
2616
if (priv->start_call_when_playing)
2621
case GST_MESSAGE_ERROR:
2623
GError *error = NULL;
2624
GstElement *gst_error;
2627
gst_message_parse_error (message, &error, &debug);
2628
gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
2630
g_message ("Element error: %s -- %s\n", error->message, debug);
2632
if (g_str_has_prefix (gst_element_get_name (gst_error),
2633
VIDEO_INPUT_ERROR_PREFIX))
2635
/* Remove the video input and continue */
2636
if (priv->video_input != NULL)
2637
empathy_call_window_remove_video_input (self);
2638
gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2642
empathy_call_window_disconnected (self, TRUE);
2644
g_error_free (error);
2647
case GST_MESSAGE_UNKNOWN:
2648
case GST_MESSAGE_EOS:
2649
case GST_MESSAGE_WARNING:
2650
case GST_MESSAGE_INFO:
2651
case GST_MESSAGE_TAG:
2652
case GST_MESSAGE_BUFFERING:
2653
case GST_MESSAGE_STATE_DIRTY:
2654
case GST_MESSAGE_STEP_DONE:
2655
case GST_MESSAGE_CLOCK_PROVIDE:
2656
case GST_MESSAGE_CLOCK_LOST:
2657
case GST_MESSAGE_NEW_CLOCK:
2658
case GST_MESSAGE_STRUCTURE_CHANGE:
2659
case GST_MESSAGE_STREAM_STATUS:
2660
case GST_MESSAGE_APPLICATION:
2661
case GST_MESSAGE_ELEMENT:
2662
case GST_MESSAGE_SEGMENT_START:
2663
case GST_MESSAGE_SEGMENT_DONE:
2664
case GST_MESSAGE_DURATION:
2665
case GST_MESSAGE_ANY:
2674
call_handler_notify_call_cb (EmpathyCallHandler *handler,
2676
EmpathyCallWindow *self)
2678
EmpathyCallWindowPriv *priv = GET_PRIV (self);
2679
TpyCallChannel *call;
2681
g_object_get (priv->handler, "call-channel", &call, NULL);
2686
tp_g_signal_connect_object (call, "audio-stream-error",
2687
G_CALLBACK (empathy_call_window_audio_stream_error), self, 0);
2688
tp_g_signal_connect_object (call, "video-stream-error",
2689
G_CALLBACK (empathy_call_window_video_stream_error), self, 0);
2691
g_object_unref (call);
2695
empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
2697
EmpathyCallWindowPriv *priv = GET_PRIV (window);
2698
TpyCallChannel *call;
2700
g_signal_connect (priv->handler, "state-changed",
2701
G_CALLBACK (empathy_call_window_state_changed_cb), window);
2702
g_signal_connect (priv->handler, "conference-added",
2703
G_CALLBACK (empathy_call_window_conference_added_cb), window);
2704
g_signal_connect (priv->handler, "conference-removed",
2705
G_CALLBACK (empathy_call_window_conference_removed_cb), window);
2706
g_signal_connect (priv->handler, "closed",
2707
G_CALLBACK (empathy_call_window_channel_closed_cb), window);
2708
g_signal_connect (priv->handler, "src-pad-added",
2709
G_CALLBACK (empathy_call_window_src_added_cb), window);
2710
g_signal_connect (priv->handler, "sink-pad-added",
2711
G_CALLBACK (empathy_call_window_sink_added_cb), window);
2712
g_signal_connect (priv->handler, "sink-pad-removed",
2713
G_CALLBACK (empathy_call_window_sink_removed_cb), window);
2715
g_object_get (priv->handler, "call-channel", &call, NULL);
2718
/* FIXME: part of the improvements for DRAFT2
2719
tp_g_signal_connect_object (call, "audio-stream-error",
2720
G_CALLBACK (empathy_call_window_audio_stream_error), window,
2722
tp_g_signal_connect_object (call, "video-stream-error",
2723
G_CALLBACK (empathy_call_window_video_stream_error), window,
2726
g_object_unref (call);
2730
/* call-channel doesn't exist yet, we'll connect signals once it has been
2732
g_signal_connect (priv->handler, "notify::call-channel",
2733
G_CALLBACK (call_handler_notify_call_cb), window);
2736
gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
2740
empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
2741
EmpathyCallWindow *window)
2743
EmpathyCallWindowPriv *priv = GET_PRIV (window);
2745
if (priv->pipeline != NULL)
2747
if (priv->bus_message_source_id != 0)
2749
g_source_remove (priv->bus_message_source_id);
2750
priv->bus_message_source_id = 0;
2753
gst_element_set_state (priv->pipeline, GST_STATE_NULL);
2756
if (priv->call_state == CONNECTING)
2757
empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
2763
show_controls (EmpathyCallWindow *window, gboolean set_fullscreen)
2766
EmpathyCallWindowPriv *priv = GET_PRIV (window);
2768
menu = gtk_ui_manager_get_widget (priv->ui_manager,
2773
gtk_widget_hide (priv->sidebar);
2774
gtk_widget_hide (menu);
2775
gtk_widget_hide (priv->vbox);
2776
gtk_widget_hide (priv->statusbar);
2777
gtk_widget_hide (priv->toolbar);
2781
if (priv->sidebar_was_visible_before_fs)
2782
gtk_widget_show (priv->sidebar);
2784
gtk_widget_show (menu);
2785
gtk_widget_show (priv->vbox);
2786
gtk_widget_show (priv->statusbar);
2787
gtk_widget_show (priv->toolbar);
2789
gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
2790
priv->original_height_before_fs);
2795
show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
2797
EmpathyCallWindowPriv *priv = GET_PRIV (window);
2799
gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
2800
set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
2801
gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
2802
set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
2804
if (priv->video_output != NULL)
2806
gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2807
priv->video_output, TRUE, TRUE,
2808
set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2812
gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2813
priv->vbox, TRUE, TRUE,
2814
set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2819
empathy_call_window_state_event_cb (GtkWidget *widget,
2820
GdkEventWindowState *event, EmpathyCallWindow *window)
2822
if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
2824
EmpathyCallWindowPriv *priv = GET_PRIV (window);
2825
gboolean set_fullscreen = event->new_window_state &
2826
GDK_WINDOW_STATE_FULLSCREEN;
2830
gboolean sidebar_was_visible;
2831
GtkAllocation allocation;
2832
gint original_width, original_height;
2834
gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2835
original_width = allocation.width;
2836
original_height = allocation.height;
2838
g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
2840
priv->sidebar_was_visible_before_fs = sidebar_was_visible;
2841
priv->original_width_before_fs = original_width;
2842
priv->original_height_before_fs = original_height;
2844
if (priv->video_output_motion_handler_id == 0 &&
2845
priv->video_output != NULL)
2847
priv->video_output_motion_handler_id = g_signal_connect (
2848
G_OBJECT (priv->video_output), "motion-notify-event",
2849
G_CALLBACK (empathy_call_window_video_output_motion_notify),
2855
disconnect_video_output_motion_handler (window);
2858
empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
2860
show_controls (window, set_fullscreen);
2861
show_borders (window, set_fullscreen);
2862
gtk_action_set_stock_id (priv->menu_fullscreen,
2863
(set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
2864
priv->is_fullscreen = set_fullscreen;
2871
empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
2872
EmpathyCallWindow *window)
2874
EmpathyCallWindowPriv *priv = GET_PRIV (window);
2876
int w, h, handle_size;
2877
GtkAllocation allocation, sidebar_allocation;
2879
gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2880
w = allocation.width;
2881
h = allocation.height;
2883
gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
2885
gtk_widget_get_allocation (priv->sidebar, &sidebar_allocation);
2886
if (gtk_toggle_button_get_active (toggle))
2888
arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
2889
gtk_widget_show (priv->sidebar);
2890
w += sidebar_allocation.width + handle_size;
2894
arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
2895
w -= sidebar_allocation.width + handle_size;
2896
gtk_widget_hide (priv->sidebar);
2899
gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
2902
gtk_window_resize (GTK_WINDOW (window), w, h);
2906
empathy_call_window_set_send_video (EmpathyCallWindow *window,
2909
EmpathyCallWindowPriv *priv = GET_PRIV (window);
2910
TpyCallChannel *call;
2912
priv->sending_video = (state == CAMERA_STATE_ON);
2914
if (state == CAMERA_STATE_PREVIEW ||
2915
state == CAMERA_STATE_ON)
2917
/* When we start sending video, we want to show the video preview by
2919
display_video_preview (window, TRUE);
2923
display_video_preview (window, FALSE);
2926
if (priv->call_state != CONNECTED)
2929
g_object_get (priv->handler, "call-channel", &call, NULL);
2930
DEBUG ("%s sending video", priv->sending_video ? "start": "stop");
2931
tpy_call_channel_send_video (call, priv->sending_video);
2932
g_object_unref (call);
2936
empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
2937
EmpathyCallWindow *window)
2939
EmpathyCallWindowPriv *priv = GET_PRIV (window);
2942
active = (gtk_toggle_tool_button_get_active (toggle));
2946
empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2948
gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
2952
/* TODO, Instead of setting the input volume to 0 we should probably
2953
* stop sending but this would cause the audio call to drop if both
2954
* sides mute at the same time on certain CMs AFAIK. Need to revisit this
2955
* in the future. GNOME #574574
2957
empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2959
gtk_adjustment_set_value (priv->audio_input_adj, 0);
2964
empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
2965
EmpathyCallWindow *window)
2967
EmpathyCallWindowPriv *priv = GET_PRIV (window);
2969
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2974
empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
2975
EmpathyCallWindow *window)
2977
EmpathyCallWindowPriv *priv = GET_PRIV (window);
2979
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2984
empathy_call_window_hangup_cb (gpointer object,
2985
EmpathyCallWindow *window)
2987
EmpathyCallWindowPriv *priv = GET_PRIV (window);
2989
empathy_call_handler_stop_call (priv->handler);
2991
if (empathy_call_window_disconnected (window, FALSE))
2992
gtk_widget_destroy (GTK_WIDGET (window));
2996
empathy_call_window_restart_call (EmpathyCallWindow *window)
2998
EmpathyCallWindowPriv *priv = GET_PRIV (window);
3000
/* Remove error info bars */
3001
gtk_container_forall (GTK_CONTAINER (priv->errors_vbox),
3002
(GtkCallback) gtk_widget_destroy, NULL);
3004
create_video_output_widget (window);
3006
g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
3007
G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
3009
/* While the call was disconnected, the input volume might have changed.
3010
* However, since the audio_input source was destroyed, its volume has not
3011
* been updated during that time. That's why we manually update it here */
3012
empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
3014
priv->outgoing = TRUE;
3015
empathy_call_window_set_state_connecting (window);
3017
if (priv->pipeline_playing)
3018
start_call (window);
3020
/* call will be started when the pipeline is ready */
3021
priv->start_call_when_playing = TRUE;
3024
empathy_call_window_setup_avatars (window, priv->handler);
3026
gtk_action_set_sensitive (priv->redial, FALSE);
3027
gtk_widget_set_sensitive (priv->redial_button, FALSE);
3031
empathy_call_window_redial_cb (gpointer object,
3032
EmpathyCallWindow *window)
3034
EmpathyCallWindowPriv *priv = GET_PRIV (window);
3036
if (priv->call_state == CONNECTED)
3037
priv->call_state = REDIALING;
3039
empathy_call_handler_stop_call (priv->handler);
3041
if (priv->call_state != CONNECTED)
3042
empathy_call_window_restart_call (window);
3046
empathy_call_window_fullscreen_cb (gpointer object,
3047
EmpathyCallWindow *window)
3049
empathy_call_window_fullscreen_toggle (window);
3053
empathy_call_window_fullscreen_toggle (EmpathyCallWindow *window)
3055
EmpathyCallWindowPriv *priv = GET_PRIV (window);
3057
if (priv->is_fullscreen)
3058
gtk_window_unfullscreen (GTK_WINDOW (window));
3060
gtk_window_fullscreen (GTK_WINDOW (window));
3064
empathy_call_window_video_button_press_cb (GtkWidget *video_output,
3065
GdkEventButton *event, EmpathyCallWindow *window)
3067
if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
3069
empathy_call_window_video_menu_popup (window, event->button);
3077
empathy_call_window_key_press_cb (GtkWidget *video_output,
3078
GdkEventKey *event, EmpathyCallWindow *window)
3080
EmpathyCallWindowPriv *priv = GET_PRIV (window);
3082
if (priv->is_fullscreen && event->keyval == GDK_KEY_Escape)
3084
/* Since we are in fullscreen mode, toggling will bring us back to
3086
empathy_call_window_fullscreen_toggle (window);
3094
empathy_call_window_video_output_motion_notify (GtkWidget *widget,
3095
GdkEventMotion *event, EmpathyCallWindow *window)
3097
EmpathyCallWindowPriv *priv = GET_PRIV (window);
3099
if (priv->is_fullscreen)
3101
empathy_call_window_fullscreen_show_popup (priv->fullscreen);
3108
empathy_call_window_video_menu_popup (EmpathyCallWindow *window,
3112
EmpathyCallWindowPriv *priv = GET_PRIV (window);
3114
menu = gtk_ui_manager_get_widget (priv->ui_manager,
3116
gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
3117
button, gtk_get_current_event_time ());
3118
gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
3122
empathy_call_window_status_message (EmpathyCallWindow *window,
3125
EmpathyCallWindowPriv *priv = GET_PRIV (window);
3127
if (priv->context_id == 0)
3129
priv->context_id = gtk_statusbar_get_context_id (
3130
GTK_STATUSBAR (priv->statusbar), "voip call status messages");
3134
gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
3137
gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
3142
empathy_call_window_volume_changed_cb (GtkScaleButton *button,
3143
gdouble value, EmpathyCallWindow *window)
3145
EmpathyCallWindowPriv *priv = GET_PRIV (window);
3147
if (priv->audio_output == NULL)
3150
empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),
3154
/* block all the signals related to camera control widgets. This is useful
3155
* when we are manually updating the UI and so don't want to fire the
3158
block_camera_control_signals (EmpathyCallWindow *self)
3160
EmpathyCallWindowPriv *priv = GET_PRIV (self);
3162
g_signal_handlers_block_by_func (priv->tool_button_camera_off,
3163
tool_button_camera_off_toggled_cb, self);
3164
g_signal_handlers_block_by_func (priv->tool_button_camera_preview,
3165
tool_button_camera_preview_toggled_cb, self);
3166
g_signal_handlers_block_by_func (priv->tool_button_camera_on,
3167
tool_button_camera_on_toggled_cb, self);
3168
g_signal_handlers_block_by_func (priv->action_camera_on,
3169
action_camera_change_cb, self);
3173
unblock_camera_control_signals (EmpathyCallWindow *self)
3175
EmpathyCallWindowPriv *priv = GET_PRIV (self);
3177
g_signal_handlers_unblock_by_func (priv->tool_button_camera_off,
3178
tool_button_camera_off_toggled_cb, self);
3179
g_signal_handlers_unblock_by_func (priv->tool_button_camera_preview,
3180
tool_button_camera_preview_toggled_cb, self);
3181
g_signal_handlers_unblock_by_func (priv->tool_button_camera_on,
3182
tool_button_camera_on_toggled_cb, self);
3183
g_signal_handlers_unblock_by_func (priv->action_camera_on,
3184
action_camera_change_cb, self);