2 * empathy-streamed-media-window.c - Source for EmpathyStreamedMediaWindow
3 * Copyright (C) 2008-2009 Collabora Ltd.
4 * @author Sjoerd Simons <sjoerd.simons@collabora.co.uk>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27 #include <gdk/gdkkeysyms.h>
30 #include <glib/gi18n.h>
32 #include <telepathy-glib/util.h>
33 #include <telepathy-farsight/channel.h>
34 #include <telepathy-glib/util.h>
36 #include <gst/farsight/fs-element-added-notifier.h>
38 #include <libempathy/empathy-tp-contact-factory.h>
39 #include <libempathy/empathy-utils.h>
40 #include <libempathy-gtk/empathy-avatar-image.h>
41 #include <libempathy-gtk/empathy-dialpad-widget.h>
42 #include <libempathy-gtk/empathy-ui-utils.h>
43 #include <libempathy-gtk/empathy-sound-manager.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-streamed-media-window.h"
51 #include "empathy-streamed-media-window-fullscreen.h"
52 #include "empathy-video-widget.h"
53 #include "empathy-audio-src.h"
54 #include "empathy-audio-sink.h"
55 #include "empathy-video-src.h"
56 #include "ev-sidebar.h"
58 #define CONTENT_HBOX_BORDER_WIDTH 6
59 #define CONTENT_HBOX_SPACING 3
60 #define CONTENT_HBOX_CHILDREN_PACKING_PADDING 3
62 #define SELF_VIDEO_SECTION_WIDTH 160
63 #define SELF_VIDEO_SECTION_HEIGTH 120
65 /* The avatar's default width and height are set to the same value because we
66 want a square icon. */
67 #define REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
68 #define REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT \
69 EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
71 /* If an video input error occurs, the error message will start with "v4l" */
72 #define VIDEO_INPUT_ERROR_PREFIX "v4l"
74 /* The time interval in milliseconds between 2 outgoing rings */
75 #define MS_BETWEEN_RING 500
77 G_DEFINE_TYPE(EmpathyStreamedMediaWindow, empathy_streamed_media_window, GTK_TYPE_WINDOW)
86 static guint signals[LAST_SIGNAL] = {0};
90 PROP_STREAMED_MEDIA_HANDLER = 1,
101 CAMERA_STATE_OFF = 0,
102 CAMERA_STATE_PREVIEW,
106 /* private structure */
107 typedef struct _EmpathyStreamedMediaWindowPriv EmpathyStreamedMediaWindowPriv;
109 struct _EmpathyStreamedMediaWindowPriv
111 gboolean dispose_has_run;
112 EmpathyStreamedMediaHandler *handler;
113 EmpathyContact *contact;
118 GtkUIManager *ui_manager;
119 GtkWidget *errors_vbox;
120 /* widget displays the video received from the remote user. This widget is
121 * alive only during call. */
122 GtkWidget *video_output;
123 GtkWidget *video_preview;
124 GtkWidget *remote_user_avatar_widget;
125 GtkWidget *self_user_avatar_widget;
127 GtkWidget *sidebar_button;
128 GtkWidget *statusbar;
129 GtkWidget *volume_button;
130 GtkWidget *redial_button;
131 GtkWidget *mic_button;
135 GtkAction *menu_fullscreen;
136 GtkAction *action_camera_on;
137 GtkWidget *tool_button_camera_off;
138 GtkWidget *tool_button_camera_preview;
139 GtkWidget *tool_button_camera_on;
141 /* The frames and boxes that contain self and remote avatar and video
142 input/output. When we redial, we destroy and re-create the boxes */
143 GtkWidget *remote_user_output_frame;
144 GtkWidget *self_user_output_frame;
145 GtkWidget *remote_user_output_hbox;
146 GtkWidget *self_user_output_hbox;
148 /* We keep a reference on the hbox which contains the main content so we can
149 easilly repack everything when toggling fullscreen */
150 GtkWidget *content_hbox;
152 /* This vbox is contained in the content_hbox and it contains the
153 self_user_output_frame and the sidebar button. When toggling fullscreen,
154 it needs to be repacked. We keep a reference on it for easier access. */
157 gulong video_output_motion_handler_id;
158 guint bus_message_source_id;
161 GtkWidget *volume_scale;
162 GtkWidget *volume_progress_bar;
163 GtkAdjustment *audio_input_adj;
165 GtkWidget *dtmf_panel;
168 GtkWidget *details_vbox;
169 GtkWidget *vcodec_encoding_label;
170 GtkWidget *acodec_encoding_label;
171 GtkWidget *vcodec_decoding_label;
172 GtkWidget *acodec_decoding_label;
174 GtkWidget *audio_remote_candidate_label;
175 GtkWidget *audio_local_candidate_label;
176 GtkWidget *video_remote_candidate_label;
177 GtkWidget *video_local_candidate_label;
178 GtkWidget *video_remote_candidate_info_img;
179 GtkWidget *video_local_candidate_info_img;
180 GtkWidget *audio_remote_candidate_info_img;
181 GtkWidget *audio_local_candidate_info_img;
183 GstElement *video_input;
184 GstElement *audio_input;
185 GstElement *audio_output;
186 GstElement *pipeline;
187 GstElement *video_tee;
191 FsElementAddedNotifier *fsnotifier;
198 GtkWidget *video_contrast;
199 GtkWidget *video_brightness;
200 GtkWidget *video_gamma;
203 gboolean call_started;
204 gboolean sending_video;
205 CameraState camera_state;
207 EmpathyStreamedMediaWindowFullscreen *fullscreen;
208 gboolean is_fullscreen;
210 /* Those fields represent the state of the window before it actually was in
212 gboolean sidebar_was_visible_before_fs;
213 gint original_width_before_fs;
214 gint original_height_before_fs;
216 /* TRUE if the call should be started when the pipeline is playing */
217 gboolean start_call_when_playing;
218 /* TRUE if we requested to set the pipeline in the playing state */
219 gboolean pipeline_playing;
221 EmpathySoundManager *sound_mgr;
224 #define GET_PRIV(o) \
225 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_STREAMED_MEDIA_WINDOW, \
226 EmpathyStreamedMediaWindowPriv))
228 static void empathy_streamed_media_window_realized_cb (GtkWidget *widget,
229 EmpathyStreamedMediaWindow *window);
231 static gboolean empathy_streamed_media_window_delete_cb (GtkWidget *widget,
232 GdkEvent *event, EmpathyStreamedMediaWindow *window);
234 static gboolean empathy_streamed_media_window_state_event_cb (GtkWidget *widget,
235 GdkEventWindowState *event, EmpathyStreamedMediaWindow *window);
237 static void empathy_streamed_media_window_sidebar_toggled_cb (GtkToggleButton *toggle,
238 EmpathyStreamedMediaWindow *window);
240 static void empathy_streamed_media_window_set_send_video (EmpathyStreamedMediaWindow *window,
243 static void empathy_streamed_media_window_mic_toggled_cb (
244 GtkToggleToolButton *toggle, EmpathyStreamedMediaWindow *window);
246 static void empathy_streamed_media_window_sidebar_hidden_cb (EvSidebar *sidebar,
247 EmpathyStreamedMediaWindow *window);
249 static void empathy_streamed_media_window_sidebar_shown_cb (EvSidebar *sidebar,
250 EmpathyStreamedMediaWindow *window);
252 static void empathy_streamed_media_window_hangup_cb (gpointer object,
253 EmpathyStreamedMediaWindow *window);
255 static void empathy_streamed_media_window_fullscreen_cb (gpointer object,
256 EmpathyStreamedMediaWindow *window);
258 static void empathy_streamed_media_window_fullscreen_toggle (EmpathyStreamedMediaWindow *window);
260 static gboolean empathy_streamed_media_window_video_button_press_cb (
261 GtkWidget *video_output, GdkEventButton *event, EmpathyStreamedMediaWindow *window);
263 static gboolean empathy_streamed_media_window_key_press_cb (GtkWidget *video_output,
264 GdkEventKey *event, EmpathyStreamedMediaWindow *window);
266 static gboolean empathy_streamed_media_window_video_output_motion_notify (
267 GtkWidget *widget, GdkEventMotion *event, EmpathyStreamedMediaWindow *window);
269 static void empathy_streamed_media_window_video_menu_popup (EmpathyStreamedMediaWindow *window,
272 static void empathy_streamed_media_window_redial_cb (gpointer object,
273 EmpathyStreamedMediaWindow *window);
275 static void empathy_streamed_media_window_restart_call (EmpathyStreamedMediaWindow *window);
277 static void empathy_streamed_media_window_status_message (EmpathyStreamedMediaWindow *window,
280 static void empathy_streamed_media_window_update_avatars_visibility (EmpathyTpStreamedMedia *call,
281 EmpathyStreamedMediaWindow *window);
283 static gboolean empathy_streamed_media_window_bus_message (GstBus *bus,
284 GstMessage *message, gpointer user_data);
287 empathy_streamed_media_window_volume_changed_cb (GtkScaleButton *button,
288 gdouble value, EmpathyStreamedMediaWindow *window);
290 static void block_camera_control_signals (EmpathyStreamedMediaWindow *self);
291 static void unblock_camera_control_signals (EmpathyStreamedMediaWindow *self);
294 empathy_streamed_media_window_setup_toolbar (EmpathyStreamedMediaWindow *self)
296 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
297 GtkToolItem *tool_item;
298 GtkWidget *camera_off_icon;
299 GdkPixbuf *pixbuf, *modded_pixbuf;
301 /* set the icon of the 'camera off' button by greying off the webcam icon */
302 pixbuf = empathy_pixbuf_from_icon_name ("camera-web",
303 GTK_ICON_SIZE_SMALL_TOOLBAR);
305 modded_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
306 gdk_pixbuf_get_width (pixbuf),
307 gdk_pixbuf_get_height (pixbuf));
309 gdk_pixbuf_saturate_and_pixelate (pixbuf, modded_pixbuf, 1.0, TRUE);
310 g_object_unref (pixbuf);
312 camera_off_icon = gtk_image_new_from_pixbuf (modded_pixbuf);
313 g_object_unref (modded_pixbuf);
314 gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (
315 priv->tool_button_camera_off), camera_off_icon);
317 /* Add an empty expanded GtkToolItem so the volume button is at the end of
319 tool_item = gtk_tool_item_new ();
320 gtk_tool_item_set_expand (tool_item, TRUE);
321 gtk_widget_show (GTK_WIDGET (tool_item));
322 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
324 priv->volume_button = gtk_volume_button_new ();
325 /* FIXME listen to the audiosinks signals and update the button according to
326 * that, for now starting out at 1.0 and assuming only the app changes the
328 gtk_scale_button_set_value (GTK_SCALE_BUTTON (priv->volume_button), 1.0);
329 g_signal_connect (G_OBJECT (priv->volume_button), "value-changed",
330 G_CALLBACK (empathy_streamed_media_window_volume_changed_cb), self);
332 tool_item = gtk_tool_item_new ();
333 gtk_container_add (GTK_CONTAINER (tool_item), priv->volume_button);
334 gtk_widget_show_all (GTK_WIDGET (tool_item));
335 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
339 dtmf_start_tone_cb (EmpathyDialpadWidget *dialpad,
341 EmpathyStreamedMediaWindow *window)
343 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
344 EmpathyTpStreamedMedia *call;
346 g_object_get (priv->handler, "tp-call", &call, NULL);
348 empathy_tp_streamed_media_start_tone (call, event);
350 g_object_unref (call);
354 dtmf_stop_tone_cb (EmpathyDialpadWidget *self,
356 EmpathyStreamedMediaWindow *window)
358 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
359 EmpathyTpStreamedMedia *call;
361 g_object_get (priv->handler, "tp-call", &call, NULL);
363 empathy_tp_streamed_media_stop_tone (call);
365 g_object_unref (call);
369 empathy_streamed_media_window_create_video_input_add_slider (EmpathyStreamedMediaWindow *self,
370 gchar *label_text, GtkWidget *bin)
372 GtkWidget *vbox = gtk_vbox_new (FALSE, 2);
373 GtkWidget *scale = gtk_vscale_new_with_range (0, 100, 10);
374 GtkWidget *label = gtk_label_new (label_text);
376 gtk_widget_set_sensitive (scale, FALSE);
378 gtk_container_add (GTK_CONTAINER (bin), vbox);
380 gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
381 gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 0);
382 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
388 empathy_streamed_media_window_video_contrast_changed_cb (GtkAdjustment *adj,
389 EmpathyStreamedMediaWindow *self)
392 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
394 empathy_video_src_set_channel (priv->video_input,
395 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST, gtk_adjustment_get_value (adj));
399 empathy_streamed_media_window_video_brightness_changed_cb (GtkAdjustment *adj,
400 EmpathyStreamedMediaWindow *self)
403 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
405 empathy_video_src_set_channel (priv->video_input,
406 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS, gtk_adjustment_get_value (adj));
410 empathy_streamed_media_window_video_gamma_changed_cb (GtkAdjustment *adj,
411 EmpathyStreamedMediaWindow *self)
414 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
416 empathy_video_src_set_channel (priv->video_input,
417 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA, gtk_adjustment_get_value (adj));
422 empathy_streamed_media_window_create_video_input (EmpathyStreamedMediaWindow *self)
424 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
427 hbox = gtk_hbox_new (TRUE, 3);
429 priv->video_contrast = empathy_streamed_media_window_create_video_input_add_slider (
430 self, _("Contrast"), hbox);
432 priv->video_brightness = empathy_streamed_media_window_create_video_input_add_slider (
433 self, _("Brightness"), hbox);
435 priv->video_gamma = empathy_streamed_media_window_create_video_input_add_slider (
436 self, _("Gamma"), hbox);
442 empathy_streamed_media_window_setup_video_input (EmpathyStreamedMediaWindow *self)
444 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
448 supported = empathy_video_src_get_supported_channels (priv->video_input);
450 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_CONTRAST)
452 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_contrast));
454 gtk_adjustment_set_value (adj,
455 empathy_video_src_get_channel (priv->video_input,
456 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST));
458 g_signal_connect (G_OBJECT (adj), "value-changed",
459 G_CALLBACK (empathy_streamed_media_window_video_contrast_changed_cb), self);
461 gtk_widget_set_sensitive (priv->video_contrast, TRUE);
464 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_BRIGHTNESS)
466 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_brightness));
468 gtk_adjustment_set_value (adj,
469 empathy_video_src_get_channel (priv->video_input,
470 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS));
472 g_signal_connect (G_OBJECT (adj), "value-changed",
473 G_CALLBACK (empathy_streamed_media_window_video_brightness_changed_cb), self);
474 gtk_widget_set_sensitive (priv->video_brightness, TRUE);
477 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_GAMMA)
479 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_gamma));
481 gtk_adjustment_set_value (adj,
482 empathy_video_src_get_channel (priv->video_input,
483 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA));
485 g_signal_connect (G_OBJECT (adj), "value-changed",
486 G_CALLBACK (empathy_streamed_media_window_video_gamma_changed_cb), self);
487 gtk_widget_set_sensitive (priv->video_gamma, TRUE);
492 empathy_streamed_media_window_mic_volume_changed_cb (GtkAdjustment *adj,
493 EmpathyStreamedMediaWindow *self)
495 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
498 volume = gtk_adjustment_get_value (adj)/100.0;
500 /* Don't store the volume because of muting */
501 if (volume > 0 || gtk_toggle_tool_button_get_active (
502 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
503 priv->volume = volume;
505 /* Ensure that the toggle button is active if the volume is > 0 and inactive
506 * if it's smaller than 0 */
507 if ((volume > 0) != gtk_toggle_tool_button_get_active (
508 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
509 gtk_toggle_tool_button_set_active (
510 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), volume > 0);
512 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
517 empathy_streamed_media_window_audio_input_level_changed_cb (EmpathyGstAudioSrc *src,
518 gdouble level, EmpathyStreamedMediaWindow *window)
521 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
523 value = CLAMP (pow (10, level / 20), 0.0, 1.0);
524 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
529 empathy_streamed_media_window_create_audio_input (EmpathyStreamedMediaWindow *self)
531 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
532 GtkWidget *hbox, *vbox, *label;
534 hbox = gtk_hbox_new (TRUE, 3);
536 vbox = gtk_vbox_new (FALSE, 3);
537 gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 3);
539 priv->volume_scale = gtk_vscale_new_with_range (0, 150, 100);
540 gtk_range_set_inverted (GTK_RANGE (priv->volume_scale), TRUE);
541 label = gtk_label_new (_("Volume"));
543 priv->audio_input_adj = gtk_range_get_adjustment (
544 GTK_RANGE (priv->volume_scale));
545 priv->volume = empathy_audio_src_get_volume (EMPATHY_GST_AUDIO_SRC
546 (priv->audio_input));
547 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
549 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
550 G_CALLBACK (empathy_streamed_media_window_mic_volume_changed_cb), self);
552 gtk_box_pack_start (GTK_BOX (vbox), priv->volume_scale, TRUE, TRUE, 3);
553 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 3);
555 priv->volume_progress_bar = gtk_progress_bar_new ();
557 gtk_orientable_set_orientation (GTK_ORIENTABLE (priv->volume_progress_bar),
558 GTK_ORIENTATION_VERTICAL);
560 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
563 gtk_box_pack_start (GTK_BOX (hbox), priv->volume_progress_bar, FALSE, FALSE,
570 create_video_output_widget (EmpathyStreamedMediaWindow *self)
572 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
575 g_assert (priv->video_output == NULL);
576 g_assert (priv->pipeline != NULL);
578 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
579 priv->video_output = empathy_video_widget_new (bus);
581 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
582 priv->video_output, TRUE, TRUE, 0);
584 gtk_widget_add_events (priv->video_output,
585 GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
586 g_signal_connect (G_OBJECT (priv->video_output), "button-press-event",
587 G_CALLBACK (empathy_streamed_media_window_video_button_press_cb), self);
589 g_object_unref (bus);
593 create_video_input (EmpathyStreamedMediaWindow *self)
595 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
597 g_assert (priv->video_input == NULL);
598 priv->video_input = empathy_video_src_new ();
599 gst_object_ref (priv->video_input);
600 gst_object_sink (priv->video_input);
604 create_audio_input (EmpathyStreamedMediaWindow *self)
606 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
608 g_assert (priv->audio_input == NULL);
609 priv->audio_input = empathy_audio_src_new ();
610 gst_object_ref (priv->audio_input);
611 gst_object_sink (priv->audio_input);
613 tp_g_signal_connect_object (priv->audio_input, "peak-level-changed",
614 G_CALLBACK (empathy_streamed_media_window_audio_input_level_changed_cb),
619 add_video_preview_to_pipeline (EmpathyStreamedMediaWindow *self)
621 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
624 g_assert (priv->video_preview != NULL);
625 g_assert (priv->pipeline != NULL);
626 g_assert (priv->video_input != NULL);
627 g_assert (priv->video_tee != NULL);
629 preview = empathy_video_widget_get_element (
630 EMPATHY_VIDEO_WIDGET (priv->video_preview));
632 if (!gst_bin_add (GST_BIN (priv->pipeline), priv->video_input))
634 g_warning ("Could not add video input to pipeline");
638 if (!gst_bin_add (GST_BIN (priv->pipeline), priv->video_tee))
640 g_warning ("Could not add video tee to pipeline");
644 if (!gst_bin_add (GST_BIN (priv->pipeline), preview))
646 g_warning ("Could not add video preview to pipeline");
650 if (!gst_element_link (priv->video_input, priv->video_tee))
652 g_warning ("Could not link video input to video tee");
656 if (!gst_element_link (priv->video_tee, preview))
658 g_warning ("Could not link video tee to video preview");
664 create_video_preview (EmpathyStreamedMediaWindow *self)
666 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
669 g_assert (priv->video_preview == NULL);
670 g_assert (priv->video_tee == NULL);
672 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
674 priv->video_preview = empathy_video_widget_new_with_size (bus,
675 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
676 g_object_set (priv->video_preview,
682 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
683 priv->video_preview, TRUE, TRUE, 0);
685 priv->video_tee = gst_element_factory_make ("tee", NULL);
686 gst_object_ref (priv->video_tee);
687 gst_object_sink (priv->video_tee);
689 g_object_unref (bus);
693 play_camera (EmpathyStreamedMediaWindow *window,
696 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
700 if (priv->video_preview == NULL)
702 create_video_preview (window);
703 add_video_preview_to_pipeline (window);
707 state = GST_STATE_PLAYING;
709 state = GST_STATE_NULL;
711 preview = empathy_video_widget_get_element (
712 EMPATHY_VIDEO_WIDGET (priv->video_preview));
714 gst_element_set_state (preview, state);
715 gst_element_set_state (priv->video_input, state);
716 gst_element_set_state (priv->video_tee, state);
720 display_video_preview (EmpathyStreamedMediaWindow *self,
723 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
727 /* Display the preview and hide the self avatar */
728 DEBUG ("Show video preview");
730 play_camera (self, TRUE);
731 gtk_widget_show (priv->video_preview);
732 gtk_widget_hide (priv->self_user_avatar_widget);
736 /* Display the self avatar and hide the preview */
737 DEBUG ("Show self avatar");
739 if (priv->video_preview != NULL)
741 gtk_widget_hide (priv->video_preview);
742 play_camera (self, FALSE);
744 gtk_widget_show (priv->self_user_avatar_widget);
749 empathy_streamed_media_window_set_state_connecting (EmpathyStreamedMediaWindow *window)
751 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
753 empathy_streamed_media_window_status_message (window, _("Connecting…"));
754 priv->call_state = CONNECTING;
757 empathy_sound_manager_start_playing (priv->sound_mgr, GTK_WIDGET (window),
758 EMPATHY_SOUND_PHONE_OUTGOING, MS_BETWEEN_RING);
762 disable_camera (EmpathyStreamedMediaWindow *self)
764 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
766 if (priv->camera_state == CAMERA_STATE_OFF)
769 DEBUG ("Disable camera");
771 display_video_preview (self, FALSE);
773 if (priv->camera_state == CAMERA_STATE_ON)
774 empathy_streamed_media_window_set_send_video (self, CAMERA_STATE_OFF);
776 block_camera_control_signals (self);
777 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
778 priv->tool_button_camera_on), FALSE);
779 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
780 priv->tool_button_camera_preview), FALSE);
782 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
783 priv->tool_button_camera_off), TRUE);
784 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (priv->action_camera_on),
786 unblock_camera_control_signals (self);
788 priv->camera_state = CAMERA_STATE_OFF;
792 tool_button_camera_off_toggled_cb (GtkToggleToolButton *toggle,
793 EmpathyStreamedMediaWindow *self)
795 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
797 if (!gtk_toggle_tool_button_get_active (toggle))
799 if (priv->camera_state == CAMERA_STATE_OFF)
801 /* We can't change the state by disabling the button */
802 block_camera_control_signals (self);
803 gtk_toggle_tool_button_set_active (toggle, TRUE);
804 unblock_camera_control_signals (self);
810 disable_camera (self);
814 enable_preview (EmpathyStreamedMediaWindow *self)
816 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
818 if (priv->camera_state == CAMERA_STATE_PREVIEW)
821 DEBUG ("Enable preview");
823 if (priv->camera_state == CAMERA_STATE_ON)
825 /* preview is already displayed so we just have to stop sending */
826 empathy_streamed_media_window_set_send_video (self, CAMERA_STATE_PREVIEW);
830 display_video_preview (self, TRUE);
833 block_camera_control_signals (self);
834 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
835 priv->tool_button_camera_off), FALSE);
836 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
837 priv->tool_button_camera_on), FALSE);
839 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
840 priv->tool_button_camera_preview), TRUE);
841 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (priv->action_camera_on),
842 CAMERA_STATE_PREVIEW);
843 unblock_camera_control_signals (self);
845 priv->camera_state = CAMERA_STATE_PREVIEW;
849 tool_button_camera_preview_toggled_cb (GtkToggleToolButton *toggle,
850 EmpathyStreamedMediaWindow *self)
852 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
854 if (!gtk_toggle_tool_button_get_active (toggle))
856 if (priv->camera_state == CAMERA_STATE_PREVIEW)
858 /* We can't change the state by disabling the button */
859 block_camera_control_signals (self);
860 gtk_toggle_tool_button_set_active (toggle, TRUE);
861 unblock_camera_control_signals (self);
867 enable_preview (self);
871 enable_camera (EmpathyStreamedMediaWindow *self)
873 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
875 if (priv->camera_state == CAMERA_STATE_ON)
878 if (priv->video_input == NULL)
880 DEBUG ("Can't enable camera, no input");
885 DEBUG ("Enable camera");
887 empathy_streamed_media_window_set_send_video (self, CAMERA_STATE_ON);
889 block_camera_control_signals (self);
890 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
891 priv->tool_button_camera_off), FALSE);
892 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
893 priv->tool_button_camera_preview), FALSE);
895 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
896 priv->tool_button_camera_on), TRUE);
897 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (priv->action_camera_on),
899 unblock_camera_control_signals (self);
901 priv->camera_state = CAMERA_STATE_ON;
905 tool_button_camera_on_toggled_cb (GtkToggleToolButton *toggle,
906 EmpathyStreamedMediaWindow *self)
908 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
910 if (!gtk_toggle_tool_button_get_active (toggle))
912 if (priv->camera_state == CAMERA_STATE_ON)
914 /* We can't change the state by disabling the button */
915 block_camera_control_signals (self);
916 gtk_toggle_tool_button_set_active (toggle, TRUE);
917 unblock_camera_control_signals (self);
923 enable_camera (self);
927 action_camera_change_cb (GtkRadioAction *action,
928 GtkRadioAction *current,
929 EmpathyStreamedMediaWindow *self)
933 state = gtk_radio_action_get_current_value (current);
937 case CAMERA_STATE_OFF:
938 disable_camera (self);
941 case CAMERA_STATE_PREVIEW:
942 enable_preview (self);
945 case CAMERA_STATE_ON:
946 enable_camera (self);
950 g_assert_not_reached ();
955 create_pipeline (EmpathyStreamedMediaWindow *self)
957 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
960 g_assert (priv->pipeline == NULL);
962 priv->pipeline = gst_pipeline_new (NULL);
963 priv->pipeline_playing = FALSE;
965 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
966 priv->bus_message_source_id = gst_bus_add_watch (bus,
967 empathy_streamed_media_window_bus_message, self);
969 g_object_unref (bus);
974 empathy_streamed_media_window_init (EmpathyStreamedMediaWindow *self)
976 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
984 GError *error = NULL;
987 filename = empathy_file_lookup ("empathy-streamed-media-window.ui", "src");
988 gui = empathy_builder_get_file (filename,
989 "call_window_vbox", &top_vbox,
990 "errors_vbox", &priv->errors_vbox,
992 "statusbar", &priv->statusbar,
993 "redial", &priv->redial_button,
994 "microphone", &priv->mic_button,
995 "toolbar", &priv->toolbar,
996 "menuredial", &priv->redial,
997 "ui_manager", &priv->ui_manager,
998 "menufullscreen", &priv->menu_fullscreen,
999 "camera_off", &priv->tool_button_camera_off,
1000 "camera_preview", &priv->tool_button_camera_preview,
1001 "camera_on", &priv->tool_button_camera_on,
1002 "action_camera_on", &priv->action_camera_on,
1003 "details_vbox", &priv->details_vbox,
1004 "vcodec_encoding_label", &priv->vcodec_encoding_label,
1005 "acodec_encoding_label", &priv->acodec_encoding_label,
1006 "acodec_decoding_label", &priv->acodec_decoding_label,
1007 "vcodec_decoding_label", &priv->vcodec_decoding_label,
1008 "audio_remote_candidate_label", &priv->audio_remote_candidate_label,
1009 "audio_local_candidate_label", &priv->audio_local_candidate_label,
1010 "video_remote_candidate_label", &priv->video_remote_candidate_label,
1011 "video_local_candidate_label", &priv->video_local_candidate_label,
1012 "video_remote_candidate_info_img", &priv->video_remote_candidate_info_img,
1013 "video_local_candidate_info_img", &priv->video_local_candidate_info_img,
1014 "audio_remote_candidate_info_img", &priv->audio_remote_candidate_info_img,
1015 "audio_local_candidate_info_img", &priv->audio_local_candidate_info_img,
1019 empathy_builder_connect (gui, self,
1020 "menuhangup", "activate", empathy_streamed_media_window_hangup_cb,
1021 "hangup", "clicked", empathy_streamed_media_window_hangup_cb,
1022 "menuredial", "activate", empathy_streamed_media_window_redial_cb,
1023 "redial", "clicked", empathy_streamed_media_window_redial_cb,
1024 "microphone", "toggled", empathy_streamed_media_window_mic_toggled_cb,
1025 "menufullscreen", "activate", empathy_streamed_media_window_fullscreen_cb,
1026 "camera_off", "toggled", tool_button_camera_off_toggled_cb,
1027 "camera_preview", "toggled", tool_button_camera_preview_toggled_cb,
1028 "camera_on", "toggled", tool_button_camera_on_toggled_cb,
1029 "action_camera_on", "changed", action_camera_change_cb,
1032 gtk_action_set_sensitive (priv->menu_fullscreen, FALSE);
1034 priv->lock = g_mutex_new ();
1036 gtk_container_add (GTK_CONTAINER (self), top_vbox);
1038 priv->content_hbox = gtk_hbox_new (FALSE, CONTENT_HBOX_SPACING);
1039 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
1040 CONTENT_HBOX_BORDER_WIDTH);
1041 gtk_paned_pack1 (GTK_PANED (priv->pane), priv->content_hbox, TRUE, FALSE);
1043 /* remote user output frame */
1044 priv->remote_user_output_frame = gtk_frame_new (NULL);
1045 gtk_widget_set_size_request (priv->remote_user_output_frame,
1046 EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT);
1047 gtk_box_pack_start (GTK_BOX (priv->content_hbox),
1048 priv->remote_user_output_frame, TRUE, TRUE,
1049 CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1051 priv->remote_user_output_hbox = gtk_hbox_new (FALSE, 0);
1053 priv->remote_user_avatar_widget = gtk_image_new ();
1055 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
1056 priv->remote_user_avatar_widget, TRUE, TRUE, 0);
1058 gtk_container_add (GTK_CONTAINER (priv->remote_user_output_frame),
1059 priv->remote_user_output_hbox);
1061 /* self user output frame */
1062 priv->self_user_output_frame = gtk_frame_new (NULL);
1063 gtk_widget_set_size_request (priv->self_user_output_frame,
1064 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
1066 priv->self_user_output_hbox = gtk_hbox_new (FALSE, 0);
1068 priv->self_user_avatar_widget = gtk_image_new ();
1069 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
1070 priv->self_user_avatar_widget, TRUE, TRUE, 0);
1072 gtk_container_add (GTK_CONTAINER (priv->self_user_output_frame),
1073 priv->self_user_output_hbox);
1075 create_pipeline (self);
1076 create_video_output_widget (self);
1077 create_audio_input (self);
1078 create_video_input (self);
1080 priv->fsnotifier = fs_element_added_notifier_new ();
1081 fs_element_added_notifier_add (priv->fsnotifier, GST_BIN (priv->pipeline));
1083 /* The call will be started as soon the pipeline is playing */
1084 priv->start_call_when_playing = TRUE;
1086 keyfile = g_key_file_new ();
1087 filename = empathy_file_lookup ("element-properties", "data");
1088 if (g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error))
1090 fs_element_added_notifier_set_properties_from_keyfile (priv->fsnotifier,
1095 g_warning ("Could not load element-properties file: %s", error->message);
1096 g_key_file_free (keyfile);
1097 g_clear_error (&error);
1101 priv->vbox = gtk_vbox_new (FALSE, 3);
1102 gtk_box_pack_start (GTK_BOX (priv->content_hbox), priv->vbox,
1103 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1104 gtk_box_pack_start (GTK_BOX (priv->vbox), priv->self_user_output_frame,
1107 empathy_streamed_media_window_setup_toolbar (self);
1109 priv->sidebar_button = gtk_toggle_button_new_with_mnemonic (_("_Sidebar"));
1110 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
1111 g_signal_connect (G_OBJECT (priv->sidebar_button), "toggled",
1112 G_CALLBACK (empathy_streamed_media_window_sidebar_toggled_cb), self);
1114 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
1116 h = gtk_hbox_new (FALSE, 3);
1117 gtk_box_pack_end (GTK_BOX (priv->vbox), h, FALSE, FALSE, 3);
1118 gtk_box_pack_end (GTK_BOX (h), priv->sidebar_button, FALSE, FALSE, 3);
1120 priv->sidebar = ev_sidebar_new ();
1121 g_signal_connect (G_OBJECT (priv->sidebar),
1122 "hide", G_CALLBACK (empathy_streamed_media_window_sidebar_hidden_cb), self);
1123 g_signal_connect (G_OBJECT (priv->sidebar),
1124 "show", G_CALLBACK (empathy_streamed_media_window_sidebar_shown_cb), self);
1125 gtk_paned_pack2 (GTK_PANED (priv->pane), priv->sidebar, FALSE, FALSE);
1127 page = empathy_streamed_media_window_create_audio_input (self);
1128 ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "audio-input",
1129 _("Audio input"), page);
1131 page = empathy_streamed_media_window_create_video_input (self);
1132 ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "video-input",
1133 _("Video input"), page);
1135 priv->dtmf_panel = empathy_dialpad_widget_new ();
1136 g_signal_connect (priv->dtmf_panel, "start-tone",
1137 G_CALLBACK (dtmf_start_tone_cb), self);
1138 g_signal_connect (priv->dtmf_panel, "stop-tone",
1139 G_CALLBACK (dtmf_stop_tone_cb), self);
1140 ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "dialpad",
1141 _("Dialpad"), priv->dtmf_panel);
1143 gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
1145 /* Put the details vbox in a scroll window as it can require a lot of
1146 * horizontal space. */
1147 scroll = gtk_scrolled_window_new (NULL, NULL);
1148 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scroll),
1149 priv->details_vbox);
1151 ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "details",
1152 _("Details"), scroll);
1154 gtk_widget_show_all (top_vbox);
1156 gtk_widget_hide (priv->sidebar);
1158 priv->fullscreen = empathy_streamed_media_window_fullscreen_new (self);
1159 empathy_streamed_media_window_fullscreen_set_video_widget (priv->fullscreen,
1160 priv->video_output);
1161 g_signal_connect (G_OBJECT (priv->fullscreen->leave_fullscreen_button),
1162 "clicked", G_CALLBACK (empathy_streamed_media_window_fullscreen_cb), self);
1164 g_signal_connect (G_OBJECT (self), "realize",
1165 G_CALLBACK (empathy_streamed_media_window_realized_cb), self);
1167 g_signal_connect (G_OBJECT (self), "delete-event",
1168 G_CALLBACK (empathy_streamed_media_window_delete_cb), self);
1170 g_signal_connect (G_OBJECT (self), "window-state-event",
1171 G_CALLBACK (empathy_streamed_media_window_state_event_cb), self);
1173 g_signal_connect (G_OBJECT (self), "key-press-event",
1174 G_CALLBACK (empathy_streamed_media_window_key_press_cb), self);
1176 priv->timer = g_timer_new ();
1178 g_object_ref (priv->ui_manager);
1179 g_object_unref (gui);
1181 priv->sound_mgr = empathy_sound_manager_dup_singleton ();
1183 empathy_geometry_bind (GTK_WINDOW (self), "av-window");
1186 /* Instead of specifying a width and a height, we specify only one size. That's
1187 because we want a square avatar icon. */
1189 init_contact_avatar_with_size (EmpathyContact *contact,
1190 GtkWidget *image_widget,
1193 GdkPixbuf *pixbuf_avatar = NULL;
1195 if (contact != NULL)
1197 pixbuf_avatar = empathy_pixbuf_avatar_from_contact_scaled (contact,
1201 if (pixbuf_avatar == NULL)
1203 pixbuf_avatar = empathy_pixbuf_from_icon_name_sized (
1204 EMPATHY_IMAGE_AVATAR_DEFAULT, size);
1207 gtk_image_set_from_pixbuf (GTK_IMAGE (image_widget), pixbuf_avatar);
1209 if (pixbuf_avatar != NULL)
1210 g_object_unref (pixbuf_avatar);
1214 set_window_title (EmpathyStreamedMediaWindow *self)
1216 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1219 /* translators: Call is a noun and %s is the contact name. This string
1220 * is used in the window title */
1221 tmp = g_strdup_printf (_("Call with %s"),
1222 empathy_contact_get_alias (priv->contact));
1223 gtk_window_set_title (GTK_WINDOW (self), tmp);
1228 contact_name_changed_cb (EmpathyContact *contact,
1229 GParamSpec *pspec, EmpathyStreamedMediaWindow *self)
1231 set_window_title (self);
1235 contact_avatar_changed_cb (EmpathyContact *contact,
1236 GParamSpec *pspec, GtkWidget *avatar_widget)
1239 GtkAllocation allocation;
1241 gtk_widget_get_allocation (avatar_widget, &allocation);
1242 size = allocation.height;
1246 /* the widget is not allocated yet, set a default size */
1247 size = MIN (REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT,
1248 REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH);
1251 init_contact_avatar_with_size (contact, avatar_widget, size);
1255 empathy_streamed_media_window_got_self_contact_cb (TpConnection *connection,
1256 EmpathyContact *contact, const GError *error, gpointer user_data,
1257 GObject *weak_object)
1259 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
1260 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1262 init_contact_avatar_with_size (contact, priv->self_user_avatar_widget,
1263 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
1265 g_signal_connect (contact, "notify::avatar",
1266 G_CALLBACK (contact_avatar_changed_cb), priv->self_user_avatar_widget);
1270 empathy_streamed_media_window_setup_avatars (EmpathyStreamedMediaWindow *self,
1271 EmpathyStreamedMediaHandler *handler)
1273 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1275 g_object_get (handler, "contact", &(priv->contact), NULL);
1277 if (priv->contact != NULL)
1279 TpConnection *connection;
1281 set_window_title (self);
1283 g_signal_connect (priv->contact, "notify::name",
1284 G_CALLBACK (contact_name_changed_cb), self);
1285 g_signal_connect (priv->contact, "notify::avatar",
1286 G_CALLBACK (contact_avatar_changed_cb),
1287 priv->remote_user_avatar_widget);
1289 /* Retreiving the self avatar */
1290 connection = empathy_contact_get_connection (priv->contact);
1291 empathy_tp_contact_factory_get_from_handle (connection,
1292 tp_connection_get_self_handle (connection),
1293 empathy_streamed_media_window_got_self_contact_cb, self, NULL, G_OBJECT (self));
1297 g_warning ("call handler doesn't have a contact");
1298 /* translators: Call is a noun. This string is used in the window
1300 gtk_window_set_title (GTK_WINDOW (self), _("Call"));
1302 /* Since we can't access the remote contact, we can't get a connection
1303 to it and can't get the self contact (and its avatar). This means
1304 that we have to manually set the self avatar. */
1305 init_contact_avatar_with_size (NULL, priv->self_user_avatar_widget,
1306 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
1309 init_contact_avatar_with_size (priv->contact,
1310 priv->remote_user_avatar_widget,
1311 MIN (REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH,
1312 REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT));
1314 /* The remote avatar is shown by default and will be hidden when we receive
1315 video from the remote side. */
1316 gtk_widget_hide (priv->video_output);
1317 gtk_widget_show (priv->remote_user_avatar_widget);
1321 update_send_codec (EmpathyStreamedMediaWindow *self,
1324 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1331 codec = empathy_streamed_media_handler_get_send_audio_codec (priv->handler);
1332 widget = priv->acodec_encoding_label;
1336 codec = empathy_streamed_media_handler_get_send_video_codec (priv->handler);
1337 widget = priv->vcodec_encoding_label;
1343 tmp = g_strdup_printf ("%s/%u", codec->encoding_name, codec->clock_rate);
1344 gtk_label_set_text (GTK_LABEL (widget), tmp);
1349 send_audio_codec_notify_cb (GObject *object,
1353 EmpathyStreamedMediaWindow *self = user_data;
1355 update_send_codec (self, TRUE);
1359 send_video_codec_notify_cb (GObject *object,
1363 EmpathyStreamedMediaWindow *self = user_data;
1365 update_send_codec (self, FALSE);
1369 update_recv_codec (EmpathyStreamedMediaWindow *self,
1372 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1375 GString *str = NULL;
1379 codecs = empathy_streamed_media_handler_get_recv_audio_codecs (priv->handler);
1380 widget = priv->acodec_decoding_label;
1384 codecs = empathy_streamed_media_handler_get_recv_video_codecs (priv->handler);
1385 widget = priv->vcodec_decoding_label;
1391 for (l = codecs; l != NULL; l = g_list_next (l))
1393 FsCodec *codec = l->data;
1396 str = g_string_new (NULL);
1398 g_string_append (str, ", ");
1400 g_string_append_printf (str, "%s/%u", codec->encoding_name,
1404 gtk_label_set_text (GTK_LABEL (widget), str->str);
1405 g_string_free (str, TRUE);
1409 recv_audio_codecs_notify_cb (GObject *object,
1413 EmpathyStreamedMediaWindow *self = user_data;
1415 update_recv_codec (self, TRUE);
1419 recv_video_codecs_notify_cb (GObject *object,
1423 EmpathyStreamedMediaWindow *self = user_data;
1425 update_recv_codec (self, FALSE);
1428 static const gchar *
1429 candidate_type_to_str (FsCandidate *candidate)
1431 switch (candidate->type)
1433 case FS_CANDIDATE_TYPE_HOST:
1435 case FS_CANDIDATE_TYPE_SRFLX:
1436 return "server reflexive";
1437 case FS_CANDIDATE_TYPE_PRFLX:
1438 return "peer reflexive";
1439 case FS_CANDIDATE_TYPE_RELAY:
1441 case FS_CANDIDATE_TYPE_MULTICAST:
1448 static const gchar *
1449 candidate_type_to_desc (FsCandidate *candidate)
1451 switch (candidate->type)
1453 case FS_CANDIDATE_TYPE_HOST:
1454 return _("The IP address as seen by the machine");
1455 case FS_CANDIDATE_TYPE_SRFLX:
1456 return _("The IP address as seen by a server on the Internet");
1457 case FS_CANDIDATE_TYPE_PRFLX:
1458 return _("The IP address of the peer as seen by the other side");
1459 case FS_CANDIDATE_TYPE_RELAY:
1460 return _("The IP address of a relay server");
1461 case FS_CANDIDATE_TYPE_MULTICAST:
1462 return _("The IP address of the multicast group");
1469 update_candidat_widget (EmpathyStreamedMediaWindow *self,
1472 FsCandidate *candidate)
1476 g_assert (candidate != NULL);
1477 str = g_strdup_printf ("%s %u (%s)", candidate->ip,
1478 candidate->port, candidate_type_to_str (candidate));
1480 gtk_label_set_text (GTK_LABEL (label), str);
1481 gtk_widget_set_tooltip_text (img, candidate_type_to_desc (candidate));
1487 candidates_changed_cb (GObject *object,
1489 EmpathyStreamedMediaWindow *self)
1491 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1492 FsCandidate *candidate = NULL;
1494 if (type == FS_MEDIA_TYPE_VIDEO)
1496 /* Update remote candidate */
1497 candidate = empathy_streamed_media_handler_get_video_remote_candidate (
1500 update_candidat_widget (self, priv->video_remote_candidate_label,
1501 priv->video_remote_candidate_info_img, candidate);
1503 /* Update local candidate */
1504 candidate = empathy_streamed_media_handler_get_video_local_candidate (
1507 update_candidat_widget (self, priv->video_local_candidate_label,
1508 priv->video_local_candidate_info_img, candidate);
1512 /* Update remote candidate */
1513 candidate = empathy_streamed_media_handler_get_audio_remote_candidate (
1516 update_candidat_widget (self, priv->audio_remote_candidate_label,
1517 priv->audio_remote_candidate_info_img, candidate);
1519 /* Update local candidate */
1520 candidate = empathy_streamed_media_handler_get_audio_local_candidate (
1523 update_candidat_widget (self, priv->audio_local_candidate_label,
1524 priv->audio_local_candidate_info_img, candidate);
1529 empathy_streamed_media_window_constructed (GObject *object)
1531 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (object);
1532 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1533 EmpathyTpStreamedMedia *call;
1535 g_assert (priv->handler != NULL);
1537 g_object_get (priv->handler, "tp-call", &call, NULL);
1538 priv->outgoing = (call == NULL);
1540 g_object_unref (call);
1542 empathy_streamed_media_window_setup_avatars (self, priv->handler);
1543 empathy_streamed_media_window_set_state_connecting (self);
1545 if (!empathy_streamed_media_handler_has_initial_video (priv->handler))
1547 gtk_toggle_tool_button_set_active (
1548 GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_off), TRUE);
1550 /* If call has InitialVideo, the preview will be started once the call has
1551 * been started (start_call()). */
1553 update_send_codec (self, TRUE);
1554 update_send_codec (self, FALSE);
1555 update_recv_codec (self, TRUE);
1556 update_recv_codec (self, FALSE);
1558 tp_g_signal_connect_object (priv->handler, "notify::send-audio-codec",
1559 G_CALLBACK (send_audio_codec_notify_cb), self, 0);
1560 tp_g_signal_connect_object (priv->handler, "notify::send-video-codec",
1561 G_CALLBACK (send_video_codec_notify_cb), self, 0);
1562 tp_g_signal_connect_object (priv->handler, "notify::recv-audio-codecs",
1563 G_CALLBACK (recv_audio_codecs_notify_cb), self, 0);
1564 tp_g_signal_connect_object (priv->handler, "notify::recv-video-codecs",
1565 G_CALLBACK (recv_video_codecs_notify_cb), self, 0);
1567 tp_g_signal_connect_object (priv->handler, "candidates-changed",
1568 G_CALLBACK (candidates_changed_cb), self, 0);
1571 static void empathy_streamed_media_window_dispose (GObject *object);
1572 static void empathy_streamed_media_window_finalize (GObject *object);
1575 empathy_streamed_media_window_set_property (GObject *object,
1576 guint property_id, const GValue *value, GParamSpec *pspec)
1578 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (object);
1580 switch (property_id)
1582 case PROP_STREAMED_MEDIA_HANDLER:
1583 priv->handler = g_value_dup_object (value);
1586 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1591 empathy_streamed_media_window_get_property (GObject *object,
1592 guint property_id, GValue *value, GParamSpec *pspec)
1594 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (object);
1596 switch (property_id)
1598 case PROP_STREAMED_MEDIA_HANDLER:
1599 g_value_set_object (value, priv->handler);
1602 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1607 empathy_streamed_media_window_class_init (
1608 EmpathyStreamedMediaWindowClass *empathy_streamed_media_window_class)
1610 GObjectClass *object_class = G_OBJECT_CLASS (empathy_streamed_media_window_class);
1611 GParamSpec *param_spec;
1613 g_type_class_add_private (empathy_streamed_media_window_class,
1614 sizeof (EmpathyStreamedMediaWindowPriv));
1616 object_class->constructed = empathy_streamed_media_window_constructed;
1617 object_class->set_property = empathy_streamed_media_window_set_property;
1618 object_class->get_property = empathy_streamed_media_window_get_property;
1620 object_class->dispose = empathy_streamed_media_window_dispose;
1621 object_class->finalize = empathy_streamed_media_window_finalize;
1623 param_spec = g_param_spec_object ("handler",
1624 "handler", "The call handler",
1625 EMPATHY_TYPE_STREAMED_MEDIA_HANDLER,
1626 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
1627 g_object_class_install_property (object_class,
1628 PROP_STREAMED_MEDIA_HANDLER, param_spec);
1632 empathy_streamed_media_window_video_stream_changed_cb (EmpathyTpStreamedMedia *call,
1633 GParamSpec *property, EmpathyStreamedMediaWindow *self)
1635 DEBUG ("video stream changed");
1636 empathy_streamed_media_window_update_avatars_visibility (call, self);
1640 empathy_streamed_media_window_dispose (GObject *object)
1642 EmpathyTpStreamedMedia *call;
1643 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (object);
1644 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1646 if (priv->dispose_has_run)
1649 priv->dispose_has_run = TRUE;
1651 g_object_get (priv->handler, "tp-call", &call, NULL);
1655 g_object_unref (call);
1658 if (priv->handler != NULL)
1660 empathy_streamed_media_handler_stop_call (priv->handler);
1661 g_object_unref (priv->handler);
1663 priv->handler = NULL;
1665 if (priv->bus_message_source_id != 0)
1667 g_source_remove (priv->bus_message_source_id);
1668 priv->bus_message_source_id = 0;
1671 if (priv->pipeline != NULL)
1672 g_object_unref (priv->pipeline);
1673 priv->pipeline = NULL;
1675 if (priv->video_input != NULL)
1676 g_object_unref (priv->video_input);
1677 priv->video_input = NULL;
1679 if (priv->audio_input != NULL)
1680 g_object_unref (priv->audio_input);
1681 priv->audio_input = NULL;
1683 if (priv->video_tee != NULL)
1684 g_object_unref (priv->video_tee);
1685 priv->video_tee = NULL;
1687 if (priv->fsnotifier != NULL)
1688 g_object_unref (priv->fsnotifier);
1689 priv->fsnotifier = NULL;
1691 if (priv->timer_id != 0)
1692 g_source_remove (priv->timer_id);
1695 if (priv->ui_manager != NULL)
1696 g_object_unref (priv->ui_manager);
1697 priv->ui_manager = NULL;
1699 if (priv->fullscreen != NULL)
1700 g_object_unref (priv->fullscreen);
1701 priv->fullscreen = NULL;
1703 if (priv->contact != NULL)
1705 g_signal_handlers_disconnect_by_func (priv->contact,
1706 contact_name_changed_cb, self);
1707 g_object_unref (priv->contact);
1708 priv->contact = NULL;
1711 tp_clear_object (&priv->sound_mgr);
1713 /* release any references held by the object here */
1714 if (G_OBJECT_CLASS (empathy_streamed_media_window_parent_class)->dispose)
1715 G_OBJECT_CLASS (empathy_streamed_media_window_parent_class)->dispose (object);
1719 disconnect_video_output_motion_handler (EmpathyStreamedMediaWindow *self)
1721 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1723 if (priv->video_output_motion_handler_id != 0)
1725 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1726 priv->video_output_motion_handler_id);
1727 priv->video_output_motion_handler_id = 0;
1732 empathy_streamed_media_window_finalize (GObject *object)
1734 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (object);
1735 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1737 disconnect_video_output_motion_handler (self);
1739 /* free any data held directly by the object here */
1740 g_mutex_free (priv->lock);
1742 g_timer_destroy (priv->timer);
1744 G_OBJECT_CLASS (empathy_streamed_media_window_parent_class)->finalize (object);
1748 EmpathyStreamedMediaWindow *
1749 empathy_streamed_media_window_new (EmpathyStreamedMediaHandler *handler)
1751 return EMPATHY_STREAMED_MEDIA_WINDOW (
1752 g_object_new (EMPATHY_TYPE_STREAMED_MEDIA_WINDOW, "handler", handler, NULL));
1756 empathy_streamed_media_window_conference_added_cb (EmpathyStreamedMediaHandler *handler,
1757 GstElement *conference, gpointer user_data)
1759 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
1760 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1762 gst_bin_add (GST_BIN (priv->pipeline), conference);
1764 gst_element_set_state (conference, GST_STATE_PLAYING);
1768 empathy_streamed_media_window_request_resource_cb (EmpathyStreamedMediaHandler *handler,
1769 FsMediaType type, FsStreamDirection direction, gpointer user_data)
1771 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
1772 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1774 if (type != FS_MEDIA_TYPE_VIDEO)
1777 if (direction == FS_DIRECTION_RECV)
1780 /* video and direction is send */
1781 return priv->video_input != NULL;
1785 empathy_streamed_media_window_reset_pipeline (EmpathyStreamedMediaWindow *self)
1787 GstStateChangeReturn state_change_return;
1788 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1790 if (priv->pipeline == NULL)
1793 if (priv->bus_message_source_id != 0)
1795 g_source_remove (priv->bus_message_source_id);
1796 priv->bus_message_source_id = 0;
1799 state_change_return = gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1801 if (state_change_return == GST_STATE_CHANGE_SUCCESS ||
1802 state_change_return == GST_STATE_CHANGE_NO_PREROLL)
1804 if (priv->pipeline != NULL)
1805 g_object_unref (priv->pipeline);
1806 priv->pipeline = NULL;
1808 g_signal_handlers_disconnect_by_func (priv->audio_input_adj,
1809 empathy_streamed_media_window_mic_volume_changed_cb, self);
1811 if (priv->video_tee != NULL)
1812 g_object_unref (priv->video_tee);
1813 priv->video_tee = NULL;
1815 if (priv->video_preview != NULL)
1816 gtk_widget_destroy (priv->video_preview);
1817 priv->video_preview = NULL;
1819 priv->funnel = NULL;
1821 create_pipeline (self);
1822 /* Call will be started when user will hit the 'redial' button */
1823 priv->start_call_when_playing = FALSE;
1824 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1830 g_message ("Error: could not destroy pipeline. Closing call window");
1831 gtk_widget_destroy (GTK_WIDGET (self));
1838 reset_details_pane (EmpathyStreamedMediaWindow *self)
1840 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1842 /* translators: encoding video codec is unknown */
1843 gtk_label_set_text (GTK_LABEL (priv->vcodec_encoding_label),
1844 C_("codec", "Unknown"));
1845 /* translators: encoding audio codec is unknown */
1846 gtk_label_set_text (GTK_LABEL (priv->acodec_encoding_label),
1847 C_("codec", "Unknown"));
1848 /* translators: decoding video codec is unknown */
1849 gtk_label_set_text (GTK_LABEL (priv->vcodec_decoding_label),
1850 C_("codec", "Unknown"));
1851 /* translators: decoding audio codec is unknown */
1852 gtk_label_set_text (GTK_LABEL (priv->acodec_decoding_label),
1853 C_("codec", "Unknown"));
1857 empathy_streamed_media_window_disconnected (EmpathyStreamedMediaWindow *self,
1860 gboolean could_disconnect = FALSE;
1861 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1862 gboolean could_reset_pipeline;
1864 /* Leave full screen mode if needed */
1865 gtk_window_unfullscreen (GTK_WINDOW (self));
1867 gtk_action_set_sensitive (priv->menu_fullscreen, FALSE);
1869 could_reset_pipeline = empathy_streamed_media_window_reset_pipeline (self);
1871 if (priv->call_state == CONNECTING)
1872 empathy_sound_manager_stop (priv->sound_mgr,
1873 EMPATHY_SOUND_PHONE_OUTGOING);
1875 if (priv->call_state != REDIALING)
1876 priv->call_state = DISCONNECTED;
1878 if (could_reset_pipeline)
1880 g_mutex_lock (priv->lock);
1882 g_timer_stop (priv->timer);
1884 if (priv->timer_id != 0)
1885 g_source_remove (priv->timer_id);
1888 g_mutex_unlock (priv->lock);
1891 /* We are about to destroy the window, no need to update it or create
1892 * a video preview */
1895 empathy_streamed_media_window_status_message (self, _("Disconnected"));
1897 gtk_action_set_sensitive (priv->redial, TRUE);
1898 gtk_widget_set_sensitive (priv->redial_button, TRUE);
1900 /* Unsensitive the camera and mic button */
1901 gtk_widget_set_sensitive (priv->tool_button_camera_on, FALSE);
1902 gtk_action_set_sensitive (priv->action_camera_on, FALSE);
1903 gtk_widget_set_sensitive (priv->mic_button, FALSE);
1905 /* Be sure that the mic button is enabled */
1906 gtk_toggle_tool_button_set_active (
1907 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), TRUE);
1909 if (priv->camera_state == CAMERA_STATE_ON)
1911 /* Enable the 'preview' button as we are not sending atm. */
1912 gtk_toggle_tool_button_set_active (
1913 GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_preview), TRUE);
1915 else if (priv->camera_state == CAMERA_STATE_PREVIEW)
1917 /* Restart the preview with the new pipeline. */
1918 display_video_preview (self, TRUE);
1921 gtk_progress_bar_set_fraction (
1922 GTK_PROGRESS_BAR (priv->volume_progress_bar), 0);
1924 /* destroy the video output; it will be recreated when we'll redial */
1925 disconnect_video_output_motion_handler (self);
1926 gtk_widget_destroy (priv->video_output);
1927 priv->video_output = NULL;
1929 gtk_widget_show (priv->remote_user_avatar_widget);
1931 reset_details_pane (self);
1933 priv->sending_video = FALSE;
1934 priv->call_started = FALSE;
1936 could_disconnect = TRUE;
1938 /* TODO: display the self avatar of the preview (depends if the "Always
1939 * Show Video Preview" is enabled or not) */
1942 return could_disconnect;
1947 empathy_streamed_media_window_channel_closed_cb (EmpathyStreamedMediaHandler *handler,
1950 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
1951 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1953 if (empathy_streamed_media_window_disconnected (self, TRUE) &&
1954 priv->call_state == REDIALING)
1955 empathy_streamed_media_window_restart_call (self);
1960 empathy_streamed_media_window_channel_stream_closed_cb (EmpathyStreamedMediaHandler *handler,
1961 TfStream *stream, gpointer user_data)
1963 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
1964 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1967 g_object_get (stream, "media-type", &media_type, NULL);
1970 * This assumes that there is only one video stream per channel...
1973 if (media_type == TP_MEDIA_STREAM_TYPE_VIDEO)
1975 if (priv->funnel != NULL)
1979 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1980 (priv->video_output));
1982 gst_element_set_state (output, GST_STATE_NULL);
1983 gst_element_set_state (priv->funnel, GST_STATE_NULL);
1985 gst_bin_remove (GST_BIN (priv->pipeline), output);
1986 gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
1987 priv->funnel = NULL;
1990 else if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO)
1992 if (priv->audio_output != NULL)
1994 gst_element_set_state (priv->audio_output, GST_STATE_NULL);
1996 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
1997 priv->audio_output = NULL;
2002 /* Called with global lock held */
2004 empathy_streamed_media_window_get_video_sink_pad (EmpathyStreamedMediaWindow *self)
2006 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2010 if (priv->funnel == NULL)
2012 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
2013 (priv->video_output));
2015 priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
2019 g_warning ("Could not create fsfunnel");
2023 if (!gst_bin_add (GST_BIN (priv->pipeline), priv->funnel))
2025 gst_object_unref (priv->funnel);
2026 priv->funnel = NULL;
2027 g_warning ("Could not add funnel to pipeline");
2031 if (!gst_bin_add (GST_BIN (priv->pipeline), output))
2033 g_warning ("Could not add the video output widget to the pipeline");
2037 if (!gst_element_link (priv->funnel, output))
2039 g_warning ("Could not link output sink to funnel");
2040 goto error_output_added;
2043 if (gst_element_set_state (output, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2045 g_warning ("Could not start video sink");
2046 goto error_output_added;
2049 if (gst_element_set_state (priv->funnel, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2051 g_warning ("Could not start funnel");
2052 goto error_output_added;
2056 pad = gst_element_get_request_pad (priv->funnel, "sink%d");
2059 g_warning ("Could not get request pad from funnel");
2066 gst_element_set_locked_state (priv->funnel, TRUE);
2067 gst_element_set_locked_state (output, TRUE);
2069 gst_element_set_state (priv->funnel, GST_STATE_NULL);
2070 gst_element_set_state (output, GST_STATE_NULL);
2072 gst_bin_remove (GST_BIN (priv->pipeline), output);
2073 gst_element_set_locked_state (output, FALSE);
2077 gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
2078 priv->funnel = NULL;
2083 /* Called with global lock held */
2085 empathy_streamed_media_window_get_audio_sink_pad (EmpathyStreamedMediaWindow *self)
2087 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2089 GstPadTemplate *template;
2091 if (priv->audio_output == NULL)
2093 priv->audio_output = empathy_audio_sink_new ();
2095 if (!gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output))
2097 g_warning ("Could not add audio sink to pipeline");
2098 g_object_unref (priv->audio_output);
2099 goto error_add_output;
2102 if (gst_element_set_state (priv->audio_output, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2104 g_warning ("Could not start audio sink");
2109 template = gst_element_class_get_pad_template (
2110 GST_ELEMENT_GET_CLASS (priv->audio_output), "sink%d");
2112 pad = gst_element_request_pad (priv->audio_output,
2113 template, NULL, NULL);
2117 g_warning ("Could not get sink pad from sink");
2124 gst_element_set_locked_state (priv->audio_output, TRUE);
2125 gst_element_set_state (priv->audio_output, GST_STATE_NULL);
2126 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
2127 priv->audio_output = NULL;
2135 empathy_streamed_media_window_update_timer (gpointer user_data)
2137 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
2138 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2142 time_ = g_timer_elapsed (priv->timer, NULL);
2144 /* Translators: number of minutes:seconds the caller has been connected */
2145 str = g_strdup_printf (_("Connected — %d:%02dm"), (int) time_ / 60,
2147 empathy_streamed_media_window_status_message (self, str);
2154 display_error (EmpathyStreamedMediaWindow *self,
2155 EmpathyTpStreamedMedia *call,
2159 const gchar *details)
2161 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2162 GtkWidget *info_bar;
2163 GtkWidget *content_area;
2170 /* Create info bar */
2171 info_bar = gtk_info_bar_new_with_buttons (GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
2174 gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
2176 content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
2178 /* hbox containing the image and the messages vbox */
2179 hbox = gtk_hbox_new (FALSE, 3);
2180 gtk_container_add (GTK_CONTAINER (content_area), hbox);
2183 image = gtk_image_new_from_icon_name (img, GTK_ICON_SIZE_DIALOG);
2184 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
2186 /* vbox containing the main message and the details expander */
2187 vbox = gtk_vbox_new (FALSE, 3);
2188 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
2191 txt = g_strdup_printf ("<b>%s</b>\n%s", title, desc);
2193 label = gtk_label_new (NULL);
2194 gtk_label_set_markup (GTK_LABEL (label), txt);
2195 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
2196 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
2199 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
2202 if (details != NULL)
2204 GtkWidget *expander;
2206 expander = gtk_expander_new (_("Technical Details"));
2208 txt = g_strdup_printf ("<i>%s</i>", details);
2210 label = gtk_label_new (NULL);
2211 gtk_label_set_markup (GTK_LABEL (label), txt);
2212 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
2213 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
2216 gtk_container_add (GTK_CONTAINER (expander), label);
2217 gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
2220 g_signal_connect (info_bar, "response",
2221 G_CALLBACK (gtk_widget_destroy), NULL);
2223 gtk_box_pack_start (GTK_BOX (priv->errors_vbox), info_bar,
2224 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
2225 gtk_widget_show_all (info_bar);
2229 media_stream_error_to_txt (EmpathyStreamedMediaWindow *self,
2230 EmpathyTpStreamedMedia *call,
2232 TpMediaStreamError error)
2234 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2241 case TP_MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED:
2243 return g_strdup_printf (
2244 _("%s's software does not understand any of the audio formats "
2245 "supported by your computer"),
2246 empathy_contact_get_alias (priv->contact));
2248 return g_strdup_printf (
2249 _("%s's software does not understand any of the video formats "
2250 "supported by your computer"),
2251 empathy_contact_get_alias (priv->contact));
2253 case TP_MEDIA_STREAM_ERROR_CONNECTION_FAILED:
2254 return g_strdup_printf (
2255 _("Can't establish a connection to %s. "
2256 "One of you might be on a network that does not allow "
2257 "direct connections."),
2258 empathy_contact_get_alias (priv->contact));
2260 case TP_MEDIA_STREAM_ERROR_NETWORK_ERROR:
2261 return g_strdup (_("There was a failure on the network"));
2263 case TP_MEDIA_STREAM_ERROR_NO_CODECS:
2265 return g_strdup (_("The audio formats necessary for this call "
2266 "are not installed on your computer"));
2268 return g_strdup (_("The video formats necessary for this call "
2269 "are not installed on your computer"));
2271 case TP_MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR:
2272 cm = empathy_tp_streamed_media_get_connection_manager (call);
2274 url = g_strdup_printf ("http://bugs.freedesktop.org/enter_bug.cgi?"
2275 "product=Telepathy&component=%s", cm);
2277 result = g_strdup_printf (
2278 _("Something unexpected happened in a Telepathy component. "
2279 "Please <a href=\"%s\">report this bug</a> and attach "
2280 "logs gathered from the 'Debug' window in the Help menu."), url);
2285 case TP_MEDIA_STREAM_ERROR_MEDIA_ERROR:
2286 return g_strdup (_("There was a failure in the call engine"));
2288 case TP_MEDIA_STREAM_ERROR_EOS:
2289 return g_strdup (_("The end of the stream was reached"));
2291 case TP_MEDIA_STREAM_ERROR_UNKNOWN:
2298 empathy_streamed_media_window_stream_error (EmpathyStreamedMediaWindow *self,
2299 EmpathyTpStreamedMedia *call,
2308 desc = media_stream_error_to_txt (self, call, audio, code);
2311 /* No description, use the error message. That's not great as it's not
2312 * localized but it's better than nothing. */
2313 display_error (self, call, icon, title, msg, NULL);
2317 display_error (self, call, icon, title, desc, msg);
2323 empathy_streamed_media_window_audio_stream_error (EmpathyTpStreamedMedia *call,
2326 EmpathyStreamedMediaWindow *self)
2328 empathy_streamed_media_window_stream_error (self, call, TRUE, code, msg,
2329 "gnome-stock-mic", _("Can't establish audio stream"));
2333 empathy_streamed_media_window_video_stream_error (EmpathyTpStreamedMedia *call,
2336 EmpathyStreamedMediaWindow *self)
2338 empathy_streamed_media_window_stream_error (self, call, FALSE, code, msg,
2339 "camera-web", _("Can't establish video stream"));
2343 empathy_streamed_media_window_connected (gpointer user_data)
2345 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
2346 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2347 EmpathyTpStreamedMedia *call;
2348 gboolean can_send_video;
2350 empathy_sound_manager_stop (priv->sound_mgr, EMPATHY_SOUND_PHONE_OUTGOING);
2352 can_send_video = priv->video_input != NULL && priv->contact != NULL &&
2353 empathy_contact_can_voip_video (priv->contact);
2355 g_object_get (priv->handler, "tp-call", &call, NULL);
2357 tp_g_signal_connect_object (call, "notify::video-stream",
2358 G_CALLBACK (empathy_streamed_media_window_video_stream_changed_cb),
2361 if (empathy_tp_streamed_media_has_dtmf (call))
2362 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
2364 if (priv->video_input == NULL)
2365 empathy_streamed_media_window_set_send_video (self, CAMERA_STATE_OFF);
2367 priv->sending_video = can_send_video ?
2368 empathy_tp_streamed_media_is_sending_video (call) : FALSE;
2370 gtk_toggle_tool_button_set_active (
2371 GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_on),
2372 priv->sending_video && priv->video_input != NULL);
2373 gtk_widget_set_sensitive (priv->tool_button_camera_on, can_send_video);
2374 gtk_action_set_sensitive (priv->action_camera_on, can_send_video);
2376 gtk_action_set_sensitive (priv->redial, FALSE);
2377 gtk_widget_set_sensitive (priv->redial_button, FALSE);
2379 gtk_widget_set_sensitive (priv->mic_button, TRUE);
2381 empathy_streamed_media_window_update_avatars_visibility (call, self);
2383 g_object_unref (call);
2385 g_mutex_lock (priv->lock);
2387 priv->timer_id = g_timeout_add_seconds (1,
2388 empathy_streamed_media_window_update_timer, self);
2390 g_mutex_unlock (priv->lock);
2392 empathy_streamed_media_window_update_timer (self);
2394 gtk_action_set_sensitive (priv->menu_fullscreen, TRUE);
2400 /* Called from the streaming thread */
2402 empathy_streamed_media_window_src_added_cb (EmpathyStreamedMediaHandler *handler,
2403 GstPad *src, guint media_type, gpointer user_data)
2405 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
2406 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2407 gboolean retval = FALSE;
2411 g_mutex_lock (priv->lock);
2413 if (priv->call_state != CONNECTED)
2415 g_timer_start (priv->timer);
2416 priv->timer_id = g_idle_add (empathy_streamed_media_window_connected, self);
2417 priv->call_state = CONNECTED;
2422 case TP_MEDIA_STREAM_TYPE_AUDIO:
2423 pad = empathy_streamed_media_window_get_audio_sink_pad (self);
2425 case TP_MEDIA_STREAM_TYPE_VIDEO:
2426 gtk_widget_hide (priv->remote_user_avatar_widget);
2427 gtk_widget_show (priv->video_output);
2428 pad = empathy_streamed_media_window_get_video_sink_pad (self);
2431 g_assert_not_reached ();
2437 if (GST_PAD_LINK_FAILED (gst_pad_link (src, pad)))
2438 g_warning ("Could not link %s sink pad",
2439 media_type == TP_MEDIA_STREAM_TYPE_AUDIO ? "audio" : "video");
2443 gst_object_unref (pad);
2447 /* If no sink could be linked, try to add fakesink to prevent the whole call
2452 GstElement *fakesink = gst_element_factory_make ("fakesink", NULL);
2454 if (gst_bin_add (GST_BIN (priv->pipeline), fakesink))
2456 GstPad *sinkpad = gst_element_get_static_pad (fakesink, "sink");
2457 if (gst_element_set_state (fakesink, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE ||
2458 GST_PAD_LINK_FAILED (gst_pad_link (src, sinkpad)))
2460 gst_element_set_locked_state (fakesink, TRUE);
2461 gst_element_set_state (fakesink, GST_STATE_NULL);
2462 gst_bin_remove (GST_BIN (priv->pipeline), fakesink);
2466 DEBUG ("Could not link real sink, linked fakesink instead");
2468 gst_object_unref (sinkpad);
2472 gst_object_unref (fakesink);
2477 g_mutex_unlock (priv->lock);
2483 empathy_streamed_media_window_sink_added_cb (EmpathyStreamedMediaHandler *handler,
2484 GstPad *sink, guint media_type, gpointer user_data)
2486 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
2487 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2489 gboolean retval = FALSE;
2493 case TP_MEDIA_STREAM_TYPE_AUDIO:
2494 if (!gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input))
2496 g_warning ("Could not add audio source to pipeline");
2500 pad = gst_element_get_static_pad (priv->audio_input, "src");
2503 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
2504 g_warning ("Could not get source pad from audio source");
2508 if (GST_PAD_LINK_FAILED (gst_pad_link (pad, sink)))
2510 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
2511 g_warning ("Could not link audio source to farsight");
2515 if (gst_element_set_state (priv->audio_input, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2517 g_warning ("Could not start audio source");
2518 gst_element_set_state (priv->audio_input, GST_STATE_NULL);
2519 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
2525 case TP_MEDIA_STREAM_TYPE_VIDEO:
2526 if (priv->video_input != NULL)
2528 if (priv->video_tee != NULL)
2530 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
2531 if (GST_PAD_LINK_FAILED (gst_pad_link (pad, sink)))
2533 g_warning ("Could not link videp soure input pipeline");
2536 gst_object_unref (pad);
2543 g_assert_not_reached ();
2550 empathy_streamed_media_window_remove_video_input (EmpathyStreamedMediaWindow *self)
2552 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2553 GstElement *preview;
2555 disable_camera (self);
2557 DEBUG ("remove video input");
2558 preview = empathy_video_widget_get_element (
2559 EMPATHY_VIDEO_WIDGET (priv->video_preview));
2561 gst_element_set_state (priv->video_input, GST_STATE_NULL);
2562 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
2563 gst_element_set_state (preview, GST_STATE_NULL);
2565 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
2566 priv->video_tee, preview, NULL);
2568 g_object_unref (priv->video_input);
2569 priv->video_input = NULL;
2570 g_object_unref (priv->video_tee);
2571 priv->video_tee = NULL;
2572 gtk_widget_destroy (priv->video_preview);
2573 priv->video_preview = NULL;
2575 gtk_widget_set_sensitive (priv->tool_button_camera_on, FALSE);
2576 gtk_action_set_sensitive (priv->action_camera_on, FALSE);
2577 gtk_widget_set_sensitive (priv->tool_button_camera_preview, FALSE);
2581 start_call (EmpathyStreamedMediaWindow *self)
2583 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2585 priv->call_started = TRUE;
2586 empathy_streamed_media_handler_start_call (priv->handler,
2587 empathy_get_current_action_time ());
2589 if (empathy_streamed_media_handler_has_initial_video (priv->handler))
2591 /* Enable 'send video' buttons and display the preview */
2592 gtk_toggle_tool_button_set_active (
2593 GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_on), TRUE);
2598 empathy_streamed_media_window_bus_message (GstBus *bus, GstMessage *message,
2601 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
2602 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2605 empathy_streamed_media_handler_bus_message (priv->handler, bus, message);
2607 switch (GST_MESSAGE_TYPE (message))
2609 case GST_MESSAGE_STATE_CHANGED:
2610 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
2612 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
2613 if (newstate == GST_STATE_PAUSED)
2614 empathy_streamed_media_window_setup_video_input (self);
2616 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
2617 !priv->call_started)
2619 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
2620 if (newstate == GST_STATE_PAUSED)
2622 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2623 priv->pipeline_playing = TRUE;
2625 if (priv->start_call_when_playing)
2630 case GST_MESSAGE_ERROR:
2632 GError *error = NULL;
2633 GstElement *gst_error;
2636 gst_message_parse_error (message, &error, &debug);
2637 gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
2639 g_message ("Element error: %s -- %s\n", error->message, debug);
2641 if (g_str_has_prefix (gst_element_get_name (gst_error),
2642 VIDEO_INPUT_ERROR_PREFIX))
2644 /* Remove the video input and continue */
2645 if (priv->video_input != NULL)
2646 empathy_streamed_media_window_remove_video_input (self);
2647 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2651 empathy_streamed_media_window_disconnected (self, TRUE);
2653 g_error_free (error);
2656 case GST_MESSAGE_UNKNOWN:
2657 case GST_MESSAGE_EOS:
2658 case GST_MESSAGE_WARNING:
2659 case GST_MESSAGE_INFO:
2660 case GST_MESSAGE_TAG:
2661 case GST_MESSAGE_BUFFERING:
2662 case GST_MESSAGE_STATE_DIRTY:
2663 case GST_MESSAGE_STEP_DONE:
2664 case GST_MESSAGE_CLOCK_PROVIDE:
2665 case GST_MESSAGE_CLOCK_LOST:
2666 case GST_MESSAGE_NEW_CLOCK:
2667 case GST_MESSAGE_STRUCTURE_CHANGE:
2668 case GST_MESSAGE_STREAM_STATUS:
2669 case GST_MESSAGE_APPLICATION:
2670 case GST_MESSAGE_ELEMENT:
2671 case GST_MESSAGE_SEGMENT_START:
2672 case GST_MESSAGE_SEGMENT_DONE:
2673 case GST_MESSAGE_DURATION:
2674 case GST_MESSAGE_ANY:
2683 empathy_streamed_media_window_update_avatars_visibility (EmpathyTpStreamedMedia *call,
2684 EmpathyStreamedMediaWindow *window)
2686 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2688 if (empathy_tp_streamed_media_is_receiving_video (call))
2690 gtk_widget_hide (priv->remote_user_avatar_widget);
2691 gtk_widget_show (priv->video_output);
2695 gtk_widget_hide (priv->video_output);
2696 gtk_widget_show (priv->remote_user_avatar_widget);
2701 call_handler_notify_tp_streamed_media_cb (EmpathyStreamedMediaHandler *handler,
2703 EmpathyStreamedMediaWindow *self)
2705 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2706 EmpathyTpStreamedMedia *call;
2708 g_object_get (priv->handler, "tp-call", &call, NULL);
2712 tp_g_signal_connect_object (call, "audio-stream-error",
2713 G_CALLBACK (empathy_streamed_media_window_audio_stream_error), self, 0);
2714 tp_g_signal_connect_object (call, "video-stream-error",
2715 G_CALLBACK (empathy_streamed_media_window_video_stream_error), self, 0);
2717 g_object_unref (call);
2721 empathy_streamed_media_window_realized_cb (GtkWidget *widget, EmpathyStreamedMediaWindow *window)
2723 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2724 EmpathyTpStreamedMedia *call;
2726 g_signal_connect (priv->handler, "conference-added",
2727 G_CALLBACK (empathy_streamed_media_window_conference_added_cb), window);
2728 g_signal_connect (priv->handler, "request-resource",
2729 G_CALLBACK (empathy_streamed_media_window_request_resource_cb), window);
2730 g_signal_connect (priv->handler, "closed",
2731 G_CALLBACK (empathy_streamed_media_window_channel_closed_cb), window);
2732 g_signal_connect (priv->handler, "src-pad-added",
2733 G_CALLBACK (empathy_streamed_media_window_src_added_cb), window);
2734 g_signal_connect (priv->handler, "sink-pad-added",
2735 G_CALLBACK (empathy_streamed_media_window_sink_added_cb), window);
2736 g_signal_connect (priv->handler, "stream-closed",
2737 G_CALLBACK (empathy_streamed_media_window_channel_stream_closed_cb), window);
2739 g_object_get (priv->handler, "tp-call", &call, NULL);
2742 tp_g_signal_connect_object (call, "audio-stream-error",
2743 G_CALLBACK (empathy_streamed_media_window_audio_stream_error), window,
2745 tp_g_signal_connect_object (call, "video-stream-error",
2746 G_CALLBACK (empathy_streamed_media_window_video_stream_error), window,
2749 g_object_unref (call);
2753 /* tp-call doesn't exist yet, we'll connect signals once it has been
2755 g_signal_connect (priv->handler, "notify::tp-call",
2756 G_CALLBACK (call_handler_notify_tp_streamed_media_cb), window);
2759 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
2763 empathy_streamed_media_window_delete_cb (GtkWidget *widget, GdkEvent*event,
2764 EmpathyStreamedMediaWindow *window)
2766 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2768 if (priv->pipeline != NULL)
2770 if (priv->bus_message_source_id != 0)
2772 g_source_remove (priv->bus_message_source_id);
2773 priv->bus_message_source_id = 0;
2776 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
2779 if (priv->call_state == CONNECTING)
2780 empathy_sound_manager_stop (priv->sound_mgr, EMPATHY_SOUND_PHONE_OUTGOING);
2786 show_controls (EmpathyStreamedMediaWindow *window, gboolean set_fullscreen)
2789 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2791 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2796 gtk_widget_hide (priv->sidebar);
2797 gtk_widget_hide (menu);
2798 gtk_widget_hide (priv->vbox);
2799 gtk_widget_hide (priv->statusbar);
2800 gtk_widget_hide (priv->toolbar);
2804 if (priv->sidebar_was_visible_before_fs)
2805 gtk_widget_show (priv->sidebar);
2807 gtk_widget_show (menu);
2808 gtk_widget_show (priv->vbox);
2809 gtk_widget_show (priv->statusbar);
2810 gtk_widget_show (priv->toolbar);
2812 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
2813 priv->original_height_before_fs);
2818 show_borders (EmpathyStreamedMediaWindow *window, gboolean set_fullscreen)
2820 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2822 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
2823 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
2824 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
2825 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
2827 if (priv->video_output != NULL)
2829 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2830 priv->video_output, TRUE, TRUE,
2831 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2835 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2836 priv->vbox, TRUE, TRUE,
2837 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2842 empathy_streamed_media_window_state_event_cb (GtkWidget *widget,
2843 GdkEventWindowState *event, EmpathyStreamedMediaWindow *window)
2845 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
2847 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2848 gboolean set_fullscreen = event->new_window_state &
2849 GDK_WINDOW_STATE_FULLSCREEN;
2853 gboolean sidebar_was_visible;
2854 GtkAllocation allocation;
2855 gint original_width, original_height;
2857 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2858 original_width = allocation.width;
2859 original_height = allocation.height;
2861 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
2863 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
2864 priv->original_width_before_fs = original_width;
2865 priv->original_height_before_fs = original_height;
2867 if (priv->video_output_motion_handler_id == 0 &&
2868 priv->video_output != NULL)
2870 priv->video_output_motion_handler_id = g_signal_connect (
2871 G_OBJECT (priv->video_output), "motion-notify-event",
2872 G_CALLBACK (empathy_streamed_media_window_video_output_motion_notify),
2878 disconnect_video_output_motion_handler (window);
2881 empathy_streamed_media_window_fullscreen_set_fullscreen (priv->fullscreen,
2883 show_controls (window, set_fullscreen);
2884 show_borders (window, set_fullscreen);
2885 gtk_action_set_stock_id (priv->menu_fullscreen,
2886 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
2887 priv->is_fullscreen = set_fullscreen;
2894 empathy_streamed_media_window_sidebar_toggled_cb (GtkToggleButton *toggle,
2895 EmpathyStreamedMediaWindow *window)
2897 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2899 int w, h, handle_size;
2900 GtkAllocation allocation, sidebar_allocation;
2902 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2903 w = allocation.width;
2904 h = allocation.height;
2906 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
2908 gtk_widget_get_allocation (priv->sidebar, &sidebar_allocation);
2909 if (gtk_toggle_button_get_active (toggle))
2911 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
2912 gtk_widget_show (priv->sidebar);
2913 w += sidebar_allocation.width + handle_size;
2917 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
2918 w -= sidebar_allocation.width + handle_size;
2919 gtk_widget_hide (priv->sidebar);
2922 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
2925 gtk_window_resize (GTK_WINDOW (window), w, h);
2929 empathy_streamed_media_window_set_send_video (EmpathyStreamedMediaWindow *window,
2932 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2933 EmpathyTpStreamedMedia *call;
2935 priv->sending_video = (state == CAMERA_STATE_ON);
2937 if (state == CAMERA_STATE_PREVIEW ||
2938 state == CAMERA_STATE_ON)
2940 /* When we start sending video, we want to show the video preview by
2942 display_video_preview (window, TRUE);
2946 display_video_preview (window, FALSE);
2949 if (priv->call_state != CONNECTED)
2952 g_object_get (priv->handler, "tp-call", &call, NULL);
2953 DEBUG ("%s sending video", priv->sending_video ? "start": "stop");
2954 empathy_tp_streamed_media_request_video_stream_direction (call, priv->sending_video);
2955 g_object_unref (call);
2959 empathy_streamed_media_window_mic_toggled_cb (GtkToggleToolButton *toggle,
2960 EmpathyStreamedMediaWindow *window)
2962 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2965 active = (gtk_toggle_tool_button_get_active (toggle));
2969 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2971 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
2975 /* TODO, Instead of setting the input volume to 0 we should probably
2976 * stop sending but this would cause the audio call to drop if both
2977 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
2978 * in the future. GNOME #574574
2980 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
2982 gtk_adjustment_set_value (priv->audio_input_adj, 0);
2987 empathy_streamed_media_window_sidebar_hidden_cb (EvSidebar *sidebar,
2988 EmpathyStreamedMediaWindow *window)
2990 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2992 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
2997 empathy_streamed_media_window_sidebar_shown_cb (EvSidebar *sidebar,
2998 EmpathyStreamedMediaWindow *window)
3000 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3002 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
3007 empathy_streamed_media_window_hangup_cb (gpointer object,
3008 EmpathyStreamedMediaWindow *window)
3010 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3012 empathy_streamed_media_handler_stop_call (priv->handler);
3014 if (empathy_streamed_media_window_disconnected (window, FALSE))
3015 gtk_widget_destroy (GTK_WIDGET (window));
3019 empathy_streamed_media_window_restart_call (EmpathyStreamedMediaWindow *window)
3021 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3023 /* Remove error info bars */
3024 gtk_container_forall (GTK_CONTAINER (priv->errors_vbox),
3025 (GtkCallback) gtk_widget_destroy, NULL);
3027 create_video_output_widget (window);
3029 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
3030 G_CALLBACK (empathy_streamed_media_window_mic_volume_changed_cb), window);
3032 /* While the call was disconnected, the input volume might have changed.
3033 * However, since the audio_input source was destroyed, its volume has not
3034 * been updated during that time. That's why we manually update it here */
3035 empathy_streamed_media_window_mic_volume_changed_cb (priv->audio_input_adj, window);
3037 priv->outgoing = TRUE;
3038 empathy_streamed_media_window_set_state_connecting (window);
3040 if (priv->pipeline_playing)
3041 start_call (window);
3043 /* call will be started when the pipeline is ready */
3044 priv->start_call_when_playing = TRUE;
3047 empathy_streamed_media_window_setup_avatars (window, priv->handler);
3049 gtk_action_set_sensitive (priv->redial, FALSE);
3050 gtk_widget_set_sensitive (priv->redial_button, FALSE);
3054 empathy_streamed_media_window_redial_cb (gpointer object,
3055 EmpathyStreamedMediaWindow *window)
3057 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3059 if (priv->call_state == CONNECTED)
3060 priv->call_state = REDIALING;
3062 empathy_streamed_media_handler_stop_call (priv->handler);
3064 if (priv->call_state != CONNECTED)
3065 empathy_streamed_media_window_restart_call (window);
3069 empathy_streamed_media_window_fullscreen_cb (gpointer object,
3070 EmpathyStreamedMediaWindow *window)
3072 empathy_streamed_media_window_fullscreen_toggle (window);
3076 empathy_streamed_media_window_fullscreen_toggle (EmpathyStreamedMediaWindow *window)
3078 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3080 if (priv->is_fullscreen)
3081 gtk_window_unfullscreen (GTK_WINDOW (window));
3083 gtk_window_fullscreen (GTK_WINDOW (window));
3087 empathy_streamed_media_window_video_button_press_cb (GtkWidget *video_output,
3088 GdkEventButton *event, EmpathyStreamedMediaWindow *window)
3090 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
3092 empathy_streamed_media_window_video_menu_popup (window, event->button);
3100 empathy_streamed_media_window_key_press_cb (GtkWidget *video_output,
3101 GdkEventKey *event, EmpathyStreamedMediaWindow *window)
3103 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3105 if (priv->is_fullscreen && event->keyval == GDK_KEY_Escape)
3107 /* Since we are in fullscreen mode, toggling will bring us back to
3109 empathy_streamed_media_window_fullscreen_toggle (window);
3117 empathy_streamed_media_window_video_output_motion_notify (GtkWidget *widget,
3118 GdkEventMotion *event, EmpathyStreamedMediaWindow *window)
3120 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3122 if (priv->is_fullscreen)
3124 empathy_streamed_media_window_fullscreen_show_popup (priv->fullscreen);
3131 empathy_streamed_media_window_video_menu_popup (EmpathyStreamedMediaWindow *window,
3135 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3137 menu = gtk_ui_manager_get_widget (priv->ui_manager,
3139 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
3140 button, gtk_get_current_event_time ());
3141 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
3145 empathy_streamed_media_window_status_message (EmpathyStreamedMediaWindow *window,
3148 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3150 if (priv->context_id == 0)
3152 priv->context_id = gtk_statusbar_get_context_id (
3153 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
3157 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
3160 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
3165 empathy_streamed_media_window_volume_changed_cb (GtkScaleButton *button,
3166 gdouble value, EmpathyStreamedMediaWindow *window)
3168 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3170 if (priv->audio_output == NULL)
3173 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),
3177 /* block all the signals related to camera control widgets. This is useful
3178 * when we are manually updating the UI and so don't want to fire the
3181 block_camera_control_signals (EmpathyStreamedMediaWindow *self)
3183 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
3185 g_signal_handlers_block_by_func (priv->tool_button_camera_off,
3186 tool_button_camera_off_toggled_cb, self);
3187 g_signal_handlers_block_by_func (priv->tool_button_camera_preview,
3188 tool_button_camera_preview_toggled_cb, self);
3189 g_signal_handlers_block_by_func (priv->tool_button_camera_on,
3190 tool_button_camera_on_toggled_cb, self);
3191 g_signal_handlers_block_by_func (priv->action_camera_on,
3192 action_camera_change_cb, self);
3196 unblock_camera_control_signals (EmpathyStreamedMediaWindow *self)
3198 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
3200 g_signal_handlers_unblock_by_func (priv->tool_button_camera_off,
3201 tool_button_camera_off_toggled_cb, self);
3202 g_signal_handlers_unblock_by_func (priv->tool_button_camera_preview,
3203 tool_button_camera_preview_toggled_cb, self);
3204 g_signal_handlers_unblock_by_func (priv->tool_button_camera_on,
3205 tool_button_camera_on_toggled_cb, self);
3206 g_signal_handlers_unblock_by_func (priv->action_camera_on,
3207 action_camera_change_cb, self);