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-ui-utils.h>
42 #include <libempathy-gtk/empathy-sound-manager.h>
43 #include <libempathy-gtk/empathy-geometry.h>
44 #include <libempathy-gtk/empathy-images.h>
46 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
47 #include <libempathy/empathy-debug.h>
49 #include "empathy-streamed-media-window.h"
50 #include "empathy-streamed-media-window-fullscreen.h"
51 #include "empathy-video-widget.h"
52 #include "empathy-audio-src.h"
53 #include "empathy-audio-sink.h"
54 #include "empathy-video-src.h"
55 #include "ev-sidebar.h"
57 #define BUTTON_ID "empathy-call-dtmf-button-id"
59 #define CONTENT_HBOX_BORDER_WIDTH 6
60 #define CONTENT_HBOX_SPACING 3
61 #define CONTENT_HBOX_CHILDREN_PACKING_PADDING 3
63 #define SELF_VIDEO_SECTION_WIDTH 160
64 #define SELF_VIDEO_SECTION_HEIGTH 120
66 /* The avatar's default width and height are set to the same value because we
67 want a square icon. */
68 #define REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
69 #define REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT \
70 EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
72 /* If an video input error occurs, the error message will start with "v4l" */
73 #define VIDEO_INPUT_ERROR_PREFIX "v4l"
75 /* The time interval in milliseconds between 2 outgoing rings */
76 #define MS_BETWEEN_RING 500
78 G_DEFINE_TYPE(EmpathyStreamedMediaWindow, empathy_streamed_media_window, GTK_TYPE_WINDOW)
87 static guint signals[LAST_SIGNAL] = {0};
91 PROP_STREAMED_MEDIA_HANDLER = 1,
102 CAMERA_STATE_OFF = 0,
103 CAMERA_STATE_PREVIEW,
107 /* private structure */
108 typedef struct _EmpathyStreamedMediaWindowPriv EmpathyStreamedMediaWindowPriv;
110 struct _EmpathyStreamedMediaWindowPriv
112 gboolean dispose_has_run;
113 EmpathyStreamedMediaHandler *handler;
114 EmpathyContact *contact;
119 GtkUIManager *ui_manager;
120 GtkWidget *errors_vbox;
121 /* widget displays the video received from the remote user. This widget is
122 * alive only during call. */
123 GtkWidget *video_output;
124 GtkWidget *video_preview;
125 GtkWidget *remote_user_avatar_widget;
126 GtkWidget *self_user_avatar_widget;
128 GtkWidget *sidebar_button;
129 GtkWidget *statusbar;
130 GtkWidget *volume_button;
131 GtkWidget *redial_button;
132 GtkWidget *mic_button;
136 GtkAction *menu_fullscreen;
137 GtkAction *action_camera_on;
138 GtkWidget *tool_button_camera_off;
139 GtkWidget *tool_button_camera_preview;
140 GtkWidget *tool_button_camera_on;
142 /* The frames and boxes that contain self and remote avatar and video
143 input/output. When we redial, we destroy and re-create the boxes */
144 GtkWidget *remote_user_output_frame;
145 GtkWidget *self_user_output_frame;
146 GtkWidget *remote_user_output_hbox;
147 GtkWidget *self_user_output_hbox;
149 /* We keep a reference on the hbox which contains the main content so we can
150 easilly repack everything when toggling fullscreen */
151 GtkWidget *content_hbox;
153 /* This vbox is contained in the content_hbox and it contains the
154 self_user_output_frame and the sidebar button. When toggling fullscreen,
155 it needs to be repacked. We keep a reference on it for easier access. */
158 gulong video_output_motion_handler_id;
159 guint bus_message_source_id;
162 GtkWidget *volume_scale;
163 GtkWidget *volume_progress_bar;
164 GtkAdjustment *audio_input_adj;
166 GtkWidget *dtmf_panel;
169 GtkWidget *details_vbox;
170 GtkWidget *vcodec_encoding_label;
171 GtkWidget *acodec_encoding_label;
172 GtkWidget *vcodec_decoding_label;
173 GtkWidget *acodec_decoding_label;
175 GtkWidget *audio_remote_candidate_label;
176 GtkWidget *audio_local_candidate_label;
177 GtkWidget *video_remote_candidate_label;
178 GtkWidget *video_local_candidate_label;
179 GtkWidget *video_remote_candidate_info_img;
180 GtkWidget *video_local_candidate_info_img;
181 GtkWidget *audio_remote_candidate_info_img;
182 GtkWidget *audio_local_candidate_info_img;
184 GstElement *video_input;
185 GstElement *audio_input;
186 GstElement *audio_output;
187 GstElement *pipeline;
188 GstElement *video_tee;
191 GstElement *liveadder;
193 FsElementAddedNotifier *fsnotifier;
200 GtkWidget *video_contrast;
201 GtkWidget *video_brightness;
202 GtkWidget *video_gamma;
205 gboolean call_started;
206 gboolean sending_video;
207 CameraState camera_state;
209 EmpathyStreamedMediaWindowFullscreen *fullscreen;
210 gboolean is_fullscreen;
212 /* Those fields represent the state of the window before it actually was in
214 gboolean sidebar_was_visible_before_fs;
215 gint original_width_before_fs;
216 gint original_height_before_fs;
218 /* TRUE if the call should be started when the pipeline is playing */
219 gboolean start_call_when_playing;
220 /* TRUE if we requested to set the pipeline in the playing state */
221 gboolean pipeline_playing;
223 EmpathySoundManager *sound_mgr;
226 #define GET_PRIV(o) \
227 (G_TYPE_INSTANCE_GET_PRIVATE ((o), EMPATHY_TYPE_STREAMED_MEDIA_WINDOW, \
228 EmpathyStreamedMediaWindowPriv))
230 static void empathy_streamed_media_window_realized_cb (GtkWidget *widget,
231 EmpathyStreamedMediaWindow *window);
233 static gboolean empathy_streamed_media_window_delete_cb (GtkWidget *widget,
234 GdkEvent *event, EmpathyStreamedMediaWindow *window);
236 static gboolean empathy_streamed_media_window_state_event_cb (GtkWidget *widget,
237 GdkEventWindowState *event, EmpathyStreamedMediaWindow *window);
239 static void empathy_streamed_media_window_sidebar_toggled_cb (GtkToggleButton *toggle,
240 EmpathyStreamedMediaWindow *window);
242 static void empathy_streamed_media_window_set_send_video (EmpathyStreamedMediaWindow *window,
245 static void empathy_streamed_media_window_mic_toggled_cb (
246 GtkToggleToolButton *toggle, EmpathyStreamedMediaWindow *window);
248 static void empathy_streamed_media_window_sidebar_hidden_cb (EvSidebar *sidebar,
249 EmpathyStreamedMediaWindow *window);
251 static void empathy_streamed_media_window_sidebar_shown_cb (EvSidebar *sidebar,
252 EmpathyStreamedMediaWindow *window);
254 static void empathy_streamed_media_window_hangup_cb (gpointer object,
255 EmpathyStreamedMediaWindow *window);
257 static void empathy_streamed_media_window_fullscreen_cb (gpointer object,
258 EmpathyStreamedMediaWindow *window);
260 static void empathy_streamed_media_window_fullscreen_toggle (EmpathyStreamedMediaWindow *window);
262 static gboolean empathy_streamed_media_window_video_button_press_cb (
263 GtkWidget *video_output, GdkEventButton *event, EmpathyStreamedMediaWindow *window);
265 static gboolean empathy_streamed_media_window_key_press_cb (GtkWidget *video_output,
266 GdkEventKey *event, EmpathyStreamedMediaWindow *window);
268 static gboolean empathy_streamed_media_window_video_output_motion_notify (
269 GtkWidget *widget, GdkEventMotion *event, EmpathyStreamedMediaWindow *window);
271 static void empathy_streamed_media_window_video_menu_popup (EmpathyStreamedMediaWindow *window,
274 static void empathy_streamed_media_window_redial_cb (gpointer object,
275 EmpathyStreamedMediaWindow *window);
277 static void empathy_streamed_media_window_restart_call (EmpathyStreamedMediaWindow *window);
279 static void empathy_streamed_media_window_status_message (EmpathyStreamedMediaWindow *window,
282 static void empathy_streamed_media_window_update_avatars_visibility (EmpathyTpStreamedMedia *call,
283 EmpathyStreamedMediaWindow *window);
285 static gboolean empathy_streamed_media_window_bus_message (GstBus *bus,
286 GstMessage *message, gpointer user_data);
289 empathy_streamed_media_window_volume_changed_cb (GtkScaleButton *button,
290 gdouble value, EmpathyStreamedMediaWindow *window);
292 static void block_camera_control_signals (EmpathyStreamedMediaWindow *self);
293 static void unblock_camera_control_signals (EmpathyStreamedMediaWindow *self);
296 empathy_streamed_media_window_setup_toolbar (EmpathyStreamedMediaWindow *self)
298 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
299 GtkToolItem *tool_item;
300 GtkWidget *camera_off_icon;
301 GdkPixbuf *pixbuf, *modded_pixbuf;
303 /* set the icon of the 'camera off' button by greying off the webcam icon */
304 pixbuf = empathy_pixbuf_from_icon_name ("camera-web",
305 GTK_ICON_SIZE_SMALL_TOOLBAR);
307 modded_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8,
308 gdk_pixbuf_get_width (pixbuf),
309 gdk_pixbuf_get_height (pixbuf));
311 gdk_pixbuf_saturate_and_pixelate (pixbuf, modded_pixbuf, 1.0, TRUE);
312 g_object_unref (pixbuf);
314 camera_off_icon = gtk_image_new_from_pixbuf (modded_pixbuf);
315 g_object_unref (modded_pixbuf);
316 gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (
317 priv->tool_button_camera_off), camera_off_icon);
319 /* Add an empty expanded GtkToolItem so the volume button is at the end of
321 tool_item = gtk_tool_item_new ();
322 gtk_tool_item_set_expand (tool_item, TRUE);
323 gtk_widget_show (GTK_WIDGET (tool_item));
324 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
326 priv->volume_button = gtk_volume_button_new ();
327 /* FIXME listen to the audiosinks signals and update the button according to
328 * that, for now starting out at 1.0 and assuming only the app changes the
330 gtk_scale_button_set_value (GTK_SCALE_BUTTON (priv->volume_button), 1.0);
331 g_signal_connect (G_OBJECT (priv->volume_button), "value-changed",
332 G_CALLBACK (empathy_streamed_media_window_volume_changed_cb), self);
334 tool_item = gtk_tool_item_new ();
335 gtk_container_add (GTK_CONTAINER (tool_item), priv->volume_button);
336 gtk_widget_show_all (GTK_WIDGET (tool_item));
337 gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
341 dtmf_button_pressed_cb (GtkButton *button, EmpathyStreamedMediaWindow *window)
343 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
344 EmpathyTpStreamedMedia *call;
348 g_object_get (priv->handler, "tp-call", &call, NULL);
350 button_quark = g_quark_from_static_string (BUTTON_ID);
351 event = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (button),
354 empathy_tp_streamed_media_start_tone (call, event);
356 g_object_unref (call);
360 dtmf_button_released_cb (GtkButton *button, EmpathyStreamedMediaWindow *window)
362 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
363 EmpathyTpStreamedMedia *call;
365 g_object_get (priv->handler, "tp-call", &call, NULL);
367 empathy_tp_streamed_media_stop_tone (call);
369 g_object_unref (call);
373 empathy_streamed_media_window_create_dtmf (EmpathyStreamedMediaWindow *self)
381 } dtmfbuttons[] = { { "1", TP_DTMF_EVENT_DIGIT_1 },
382 { "2", TP_DTMF_EVENT_DIGIT_2 },
383 { "3", TP_DTMF_EVENT_DIGIT_3 },
384 { "4", TP_DTMF_EVENT_DIGIT_4 },
385 { "5", TP_DTMF_EVENT_DIGIT_5 },
386 { "6", TP_DTMF_EVENT_DIGIT_6 },
387 { "7", TP_DTMF_EVENT_DIGIT_7 },
388 { "8", TP_DTMF_EVENT_DIGIT_8 },
389 { "9", TP_DTMF_EVENT_DIGIT_9 },
390 { "#", TP_DTMF_EVENT_HASH },
391 { "0", TP_DTMF_EVENT_DIGIT_0 },
392 { "*", TP_DTMF_EVENT_ASTERISK },
395 button_quark = g_quark_from_static_string (BUTTON_ID);
397 table = gtk_table_new (4, 3, TRUE);
399 for (i = 0; dtmfbuttons[i].label != NULL; i++)
401 GtkWidget *button = gtk_button_new_with_label (dtmfbuttons[i].label);
402 gtk_table_attach (GTK_TABLE (table), button, i % 3, i % 3 + 1,
403 i/3, i/3 + 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 1, 1);
405 g_object_set_qdata (G_OBJECT (button), button_quark,
406 GUINT_TO_POINTER (dtmfbuttons[i].event));
408 g_signal_connect (G_OBJECT (button), "pressed",
409 G_CALLBACK (dtmf_button_pressed_cb), self);
410 g_signal_connect (G_OBJECT (button), "released",
411 G_CALLBACK (dtmf_button_released_cb), self);
418 empathy_streamed_media_window_create_video_input_add_slider (EmpathyStreamedMediaWindow *self,
419 gchar *label_text, GtkWidget *bin)
421 GtkWidget *vbox = gtk_vbox_new (FALSE, 2);
422 GtkWidget *scale = gtk_vscale_new_with_range (0, 100, 10);
423 GtkWidget *label = gtk_label_new (label_text);
425 gtk_widget_set_sensitive (scale, FALSE);
427 gtk_container_add (GTK_CONTAINER (bin), vbox);
429 gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
430 gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 0);
431 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
437 empathy_streamed_media_window_video_contrast_changed_cb (GtkAdjustment *adj,
438 EmpathyStreamedMediaWindow *self)
441 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
443 empathy_video_src_set_channel (priv->video_input,
444 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST, gtk_adjustment_get_value (adj));
448 empathy_streamed_media_window_video_brightness_changed_cb (GtkAdjustment *adj,
449 EmpathyStreamedMediaWindow *self)
452 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
454 empathy_video_src_set_channel (priv->video_input,
455 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS, gtk_adjustment_get_value (adj));
459 empathy_streamed_media_window_video_gamma_changed_cb (GtkAdjustment *adj,
460 EmpathyStreamedMediaWindow *self)
463 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
465 empathy_video_src_set_channel (priv->video_input,
466 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA, gtk_adjustment_get_value (adj));
471 empathy_streamed_media_window_create_video_input (EmpathyStreamedMediaWindow *self)
473 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
476 hbox = gtk_hbox_new (TRUE, 3);
478 priv->video_contrast = empathy_streamed_media_window_create_video_input_add_slider (
479 self, _("Contrast"), hbox);
481 priv->video_brightness = empathy_streamed_media_window_create_video_input_add_slider (
482 self, _("Brightness"), hbox);
484 priv->video_gamma = empathy_streamed_media_window_create_video_input_add_slider (
485 self, _("Gamma"), hbox);
491 empathy_streamed_media_window_setup_video_input (EmpathyStreamedMediaWindow *self)
493 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
497 supported = empathy_video_src_get_supported_channels (priv->video_input);
499 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_CONTRAST)
501 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_contrast));
503 gtk_adjustment_set_value (adj,
504 empathy_video_src_get_channel (priv->video_input,
505 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST));
507 g_signal_connect (G_OBJECT (adj), "value-changed",
508 G_CALLBACK (empathy_streamed_media_window_video_contrast_changed_cb), self);
510 gtk_widget_set_sensitive (priv->video_contrast, TRUE);
513 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_BRIGHTNESS)
515 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_brightness));
517 gtk_adjustment_set_value (adj,
518 empathy_video_src_get_channel (priv->video_input,
519 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS));
521 g_signal_connect (G_OBJECT (adj), "value-changed",
522 G_CALLBACK (empathy_streamed_media_window_video_brightness_changed_cb), self);
523 gtk_widget_set_sensitive (priv->video_brightness, TRUE);
526 if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_GAMMA)
528 adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_gamma));
530 gtk_adjustment_set_value (adj,
531 empathy_video_src_get_channel (priv->video_input,
532 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA));
534 g_signal_connect (G_OBJECT (adj), "value-changed",
535 G_CALLBACK (empathy_streamed_media_window_video_gamma_changed_cb), self);
536 gtk_widget_set_sensitive (priv->video_gamma, TRUE);
541 empathy_streamed_media_window_mic_volume_changed_cb (GtkAdjustment *adj,
542 EmpathyStreamedMediaWindow *self)
544 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
547 volume = gtk_adjustment_get_value (adj)/100.0;
549 /* Don't store the volume because of muting */
550 if (volume > 0 || gtk_toggle_tool_button_get_active (
551 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
552 priv->volume = volume;
554 /* Ensure that the toggle button is active if the volume is > 0 and inactive
555 * if it's smaller than 0 */
556 if ((volume > 0) != gtk_toggle_tool_button_get_active (
557 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
558 gtk_toggle_tool_button_set_active (
559 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), volume > 0);
561 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
566 empathy_streamed_media_window_audio_input_level_changed_cb (EmpathyGstAudioSrc *src,
567 gdouble level, EmpathyStreamedMediaWindow *window)
570 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
572 value = CLAMP (pow (10, level / 20), 0.0, 1.0);
573 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
578 empathy_streamed_media_window_create_audio_input (EmpathyStreamedMediaWindow *self)
580 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
581 GtkWidget *hbox, *vbox, *label;
583 hbox = gtk_hbox_new (TRUE, 3);
585 vbox = gtk_vbox_new (FALSE, 3);
586 gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 3);
588 priv->volume_scale = gtk_vscale_new_with_range (0, 150, 100);
589 gtk_range_set_inverted (GTK_RANGE (priv->volume_scale), TRUE);
590 label = gtk_label_new (_("Volume"));
592 priv->audio_input_adj = gtk_range_get_adjustment (
593 GTK_RANGE (priv->volume_scale));
594 priv->volume = empathy_audio_src_get_volume (EMPATHY_GST_AUDIO_SRC
595 (priv->audio_input));
596 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
598 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
599 G_CALLBACK (empathy_streamed_media_window_mic_volume_changed_cb), self);
601 gtk_box_pack_start (GTK_BOX (vbox), priv->volume_scale, TRUE, TRUE, 3);
602 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 3);
604 priv->volume_progress_bar = gtk_progress_bar_new ();
606 gtk_orientable_set_orientation (GTK_ORIENTABLE (priv->volume_progress_bar),
607 GTK_ORIENTATION_VERTICAL);
609 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
612 gtk_box_pack_start (GTK_BOX (hbox), priv->volume_progress_bar, FALSE, FALSE,
619 create_video_output_widget (EmpathyStreamedMediaWindow *self)
621 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
624 g_assert (priv->video_output == NULL);
625 g_assert (priv->pipeline != NULL);
627 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
628 priv->video_output = empathy_video_widget_new (bus);
630 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
631 priv->video_output, TRUE, TRUE, 0);
633 gtk_widget_add_events (priv->video_output,
634 GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
635 g_signal_connect (G_OBJECT (priv->video_output), "button-press-event",
636 G_CALLBACK (empathy_streamed_media_window_video_button_press_cb), self);
638 g_object_unref (bus);
642 create_audio_output (EmpathyStreamedMediaWindow *self)
644 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
646 g_assert (priv->audio_output == NULL);
647 priv->audio_output = empathy_audio_sink_new ();
648 gst_object_ref (priv->audio_output);
649 gst_object_sink (priv->audio_output);
653 create_video_input (EmpathyStreamedMediaWindow *self)
655 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
657 g_assert (priv->video_input == NULL);
658 priv->video_input = empathy_video_src_new ();
659 gst_object_ref (priv->video_input);
660 gst_object_sink (priv->video_input);
664 create_audio_input (EmpathyStreamedMediaWindow *self)
666 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
668 g_assert (priv->audio_input == NULL);
669 priv->audio_input = empathy_audio_src_new ();
670 gst_object_ref (priv->audio_input);
671 gst_object_sink (priv->audio_input);
673 tp_g_signal_connect_object (priv->audio_input, "peak-level-changed",
674 G_CALLBACK (empathy_streamed_media_window_audio_input_level_changed_cb),
679 add_video_preview_to_pipeline (EmpathyStreamedMediaWindow *self)
681 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
684 g_assert (priv->video_preview != NULL);
685 g_assert (priv->pipeline != NULL);
686 g_assert (priv->video_input != NULL);
687 g_assert (priv->video_tee != NULL);
689 preview = empathy_video_widget_get_element (
690 EMPATHY_VIDEO_WIDGET (priv->video_preview));
692 if (!gst_bin_add (GST_BIN (priv->pipeline), priv->video_input))
694 g_warning ("Could not add video input to pipeline");
698 if (!gst_bin_add (GST_BIN (priv->pipeline), priv->video_tee))
700 g_warning ("Could not add video tee to pipeline");
704 if (!gst_bin_add (GST_BIN (priv->pipeline), preview))
706 g_warning ("Could not add video preview to pipeline");
710 if (!gst_element_link (priv->video_input, priv->video_tee))
712 g_warning ("Could not link video input to video tee");
716 if (!gst_element_link (priv->video_tee, preview))
718 g_warning ("Could not link video tee to video preview");
724 create_video_preview (EmpathyStreamedMediaWindow *self)
726 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
729 g_assert (priv->video_preview == NULL);
730 g_assert (priv->video_tee == NULL);
732 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
734 priv->video_preview = empathy_video_widget_new_with_size (bus,
735 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
736 g_object_set (priv->video_preview, "sync", FALSE, "async", TRUE, NULL);
738 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
739 priv->video_preview, TRUE, TRUE, 0);
741 priv->video_tee = gst_element_factory_make ("tee", NULL);
742 gst_object_ref (priv->video_tee);
743 gst_object_sink (priv->video_tee);
745 g_object_unref (bus);
749 play_camera (EmpathyStreamedMediaWindow *window,
752 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
756 if (priv->video_preview == NULL)
758 create_video_preview (window);
759 add_video_preview_to_pipeline (window);
763 state = GST_STATE_PLAYING;
765 state = GST_STATE_NULL;
767 preview = empathy_video_widget_get_element (
768 EMPATHY_VIDEO_WIDGET (priv->video_preview));
770 gst_element_set_state (preview, state);
771 gst_element_set_state (priv->video_input, state);
772 gst_element_set_state (priv->video_tee, state);
776 display_video_preview (EmpathyStreamedMediaWindow *self,
779 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
783 /* Display the preview and hide the self avatar */
784 DEBUG ("Show video preview");
786 play_camera (self, TRUE);
787 gtk_widget_show (priv->video_preview);
788 gtk_widget_hide (priv->self_user_avatar_widget);
792 /* Display the self avatar and hide the preview */
793 DEBUG ("Show self avatar");
795 if (priv->video_preview != NULL)
797 gtk_widget_hide (priv->video_preview);
798 play_camera (self, FALSE);
800 gtk_widget_show (priv->self_user_avatar_widget);
805 empathy_streamed_media_window_set_state_connecting (EmpathyStreamedMediaWindow *window)
807 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
809 empathy_streamed_media_window_status_message (window, _("Connecting…"));
810 priv->call_state = CONNECTING;
813 empathy_sound_manager_start_playing (priv->sound_mgr, GTK_WIDGET (window),
814 EMPATHY_SOUND_PHONE_OUTGOING, MS_BETWEEN_RING);
818 disable_camera (EmpathyStreamedMediaWindow *self)
820 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
822 if (priv->camera_state == CAMERA_STATE_OFF)
825 DEBUG ("Disable camera");
827 display_video_preview (self, FALSE);
829 if (priv->camera_state == CAMERA_STATE_ON)
830 empathy_streamed_media_window_set_send_video (self, CAMERA_STATE_OFF);
832 block_camera_control_signals (self);
833 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
834 priv->tool_button_camera_on), FALSE);
835 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
836 priv->tool_button_camera_preview), FALSE);
838 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
839 priv->tool_button_camera_off), TRUE);
840 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (priv->action_camera_on),
842 unblock_camera_control_signals (self);
844 priv->camera_state = CAMERA_STATE_OFF;
848 tool_button_camera_off_toggled_cb (GtkToggleToolButton *toggle,
849 EmpathyStreamedMediaWindow *self)
851 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
853 if (!gtk_toggle_tool_button_get_active (toggle))
855 if (priv->camera_state == CAMERA_STATE_OFF)
857 /* We can't change the state by disabling the button */
858 block_camera_control_signals (self);
859 gtk_toggle_tool_button_set_active (toggle, TRUE);
860 unblock_camera_control_signals (self);
866 disable_camera (self);
870 enable_preview (EmpathyStreamedMediaWindow *self)
872 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
874 if (priv->camera_state == CAMERA_STATE_PREVIEW)
877 DEBUG ("Enable preview");
879 if (priv->camera_state == CAMERA_STATE_ON)
881 /* preview is already displayed so we just have to stop sending */
882 empathy_streamed_media_window_set_send_video (self, CAMERA_STATE_PREVIEW);
886 display_video_preview (self, TRUE);
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_on), FALSE);
895 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
896 priv->tool_button_camera_preview), TRUE);
897 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (priv->action_camera_on),
898 CAMERA_STATE_PREVIEW);
899 unblock_camera_control_signals (self);
901 priv->camera_state = CAMERA_STATE_PREVIEW;
905 tool_button_camera_preview_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_PREVIEW)
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_preview (self);
927 enable_camera (EmpathyStreamedMediaWindow *self)
929 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
931 if (priv->camera_state == CAMERA_STATE_ON)
934 if (priv->video_input == NULL)
936 DEBUG ("Can't enable camera, no input");
941 DEBUG ("Enable camera");
943 empathy_streamed_media_window_set_send_video (self, CAMERA_STATE_ON);
945 block_camera_control_signals (self);
946 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
947 priv->tool_button_camera_off), FALSE);
948 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
949 priv->tool_button_camera_preview), FALSE);
951 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
952 priv->tool_button_camera_on), TRUE);
953 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (priv->action_camera_on),
955 unblock_camera_control_signals (self);
957 priv->camera_state = CAMERA_STATE_ON;
961 tool_button_camera_on_toggled_cb (GtkToggleToolButton *toggle,
962 EmpathyStreamedMediaWindow *self)
964 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
966 if (!gtk_toggle_tool_button_get_active (toggle))
968 if (priv->camera_state == CAMERA_STATE_ON)
970 /* We can't change the state by disabling the button */
971 block_camera_control_signals (self);
972 gtk_toggle_tool_button_set_active (toggle, TRUE);
973 unblock_camera_control_signals (self);
979 enable_camera (self);
983 action_camera_change_cb (GtkRadioAction *action,
984 GtkRadioAction *current,
985 EmpathyStreamedMediaWindow *self)
989 state = gtk_radio_action_get_current_value (current);
993 case CAMERA_STATE_OFF:
994 disable_camera (self);
997 case CAMERA_STATE_PREVIEW:
998 enable_preview (self);
1001 case CAMERA_STATE_ON:
1002 enable_camera (self);
1006 g_assert_not_reached ();
1011 create_pipeline (EmpathyStreamedMediaWindow *self)
1013 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1016 g_assert (priv->pipeline == NULL);
1018 priv->pipeline = gst_pipeline_new (NULL);
1019 priv->pipeline_playing = FALSE;
1021 bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
1022 priv->bus_message_source_id = gst_bus_add_watch (bus,
1023 empathy_streamed_media_window_bus_message, self);
1025 g_object_unref (bus);
1030 empathy_streamed_media_window_init (EmpathyStreamedMediaWindow *self)
1032 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1034 GtkWidget *top_vbox;
1040 GError *error = NULL;
1042 filename = empathy_file_lookup ("empathy-call-window.ui", "src");
1043 gui = empathy_builder_get_file (filename,
1044 "call_window_vbox", &top_vbox,
1045 "errors_vbox", &priv->errors_vbox,
1046 "pane", &priv->pane,
1047 "statusbar", &priv->statusbar,
1048 "redial", &priv->redial_button,
1049 "microphone", &priv->mic_button,
1050 "toolbar", &priv->toolbar,
1051 "menuredial", &priv->redial,
1052 "ui_manager", &priv->ui_manager,
1053 "menufullscreen", &priv->menu_fullscreen,
1054 "camera_off", &priv->tool_button_camera_off,
1055 "camera_preview", &priv->tool_button_camera_preview,
1056 "camera_on", &priv->tool_button_camera_on,
1057 "action_camera_on", &priv->action_camera_on,
1058 "details_vbox", &priv->details_vbox,
1059 "vcodec_encoding_label", &priv->vcodec_encoding_label,
1060 "acodec_encoding_label", &priv->acodec_encoding_label,
1061 "acodec_decoding_label", &priv->acodec_decoding_label,
1062 "vcodec_decoding_label", &priv->vcodec_decoding_label,
1063 "audio_remote_candidate_label", &priv->audio_remote_candidate_label,
1064 "audio_local_candidate_label", &priv->audio_local_candidate_label,
1065 "video_remote_candidate_label", &priv->video_remote_candidate_label,
1066 "video_local_candidate_label", &priv->video_local_candidate_label,
1067 "video_remote_candidate_info_img", &priv->video_remote_candidate_info_img,
1068 "video_local_candidate_info_img", &priv->video_local_candidate_info_img,
1069 "audio_remote_candidate_info_img", &priv->audio_remote_candidate_info_img,
1070 "audio_local_candidate_info_img", &priv->audio_local_candidate_info_img,
1074 empathy_builder_connect (gui, self,
1075 "menuhangup", "activate", empathy_streamed_media_window_hangup_cb,
1076 "hangup", "clicked", empathy_streamed_media_window_hangup_cb,
1077 "menuredial", "activate", empathy_streamed_media_window_redial_cb,
1078 "redial", "clicked", empathy_streamed_media_window_redial_cb,
1079 "microphone", "toggled", empathy_streamed_media_window_mic_toggled_cb,
1080 "menufullscreen", "activate", empathy_streamed_media_window_fullscreen_cb,
1081 "camera_off", "toggled", tool_button_camera_off_toggled_cb,
1082 "camera_preview", "toggled", tool_button_camera_preview_toggled_cb,
1083 "camera_on", "toggled", tool_button_camera_on_toggled_cb,
1084 "action_camera_on", "changed", action_camera_change_cb,
1087 gtk_action_set_sensitive (priv->menu_fullscreen, FALSE);
1089 priv->lock = g_mutex_new ();
1091 gtk_container_add (GTK_CONTAINER (self), top_vbox);
1093 priv->content_hbox = gtk_hbox_new (FALSE, CONTENT_HBOX_SPACING);
1094 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
1095 CONTENT_HBOX_BORDER_WIDTH);
1096 gtk_paned_pack1 (GTK_PANED (priv->pane), priv->content_hbox, TRUE, FALSE);
1098 /* remote user output frame */
1099 priv->remote_user_output_frame = gtk_frame_new (NULL);
1100 gtk_widget_set_size_request (priv->remote_user_output_frame,
1101 EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT);
1102 gtk_box_pack_start (GTK_BOX (priv->content_hbox),
1103 priv->remote_user_output_frame, TRUE, TRUE,
1104 CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1106 priv->remote_user_output_hbox = gtk_hbox_new (FALSE, 0);
1108 priv->remote_user_avatar_widget = gtk_image_new ();
1110 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
1111 priv->remote_user_avatar_widget, TRUE, TRUE, 0);
1113 gtk_container_add (GTK_CONTAINER (priv->remote_user_output_frame),
1114 priv->remote_user_output_hbox);
1116 /* self user output frame */
1117 priv->self_user_output_frame = gtk_frame_new (NULL);
1118 gtk_widget_set_size_request (priv->self_user_output_frame,
1119 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
1121 priv->self_user_output_hbox = gtk_hbox_new (FALSE, 0);
1123 priv->self_user_avatar_widget = gtk_image_new ();
1124 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
1125 priv->self_user_avatar_widget, TRUE, TRUE, 0);
1127 gtk_container_add (GTK_CONTAINER (priv->self_user_output_frame),
1128 priv->self_user_output_hbox);
1130 create_pipeline (self);
1131 create_video_output_widget (self);
1132 create_audio_input (self);
1133 create_audio_output (self);
1134 create_video_input (self);
1136 priv->fsnotifier = fs_element_added_notifier_new ();
1137 fs_element_added_notifier_add (priv->fsnotifier, GST_BIN (priv->pipeline));
1139 /* The call will be started as soon the pipeline is playing */
1140 priv->start_call_when_playing = TRUE;
1142 keyfile = g_key_file_new ();
1143 filename = empathy_file_lookup ("element-properties", "data");
1144 if (g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error))
1146 fs_element_added_notifier_set_properties_from_keyfile (priv->fsnotifier,
1151 g_warning ("Could not load element-properties file: %s", error->message);
1152 g_key_file_free (keyfile);
1153 g_clear_error (&error);
1157 priv->vbox = gtk_vbox_new (FALSE, 3);
1158 gtk_box_pack_start (GTK_BOX (priv->content_hbox), priv->vbox,
1159 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1160 gtk_box_pack_start (GTK_BOX (priv->vbox), priv->self_user_output_frame,
1163 empathy_streamed_media_window_setup_toolbar (self);
1165 priv->sidebar_button = gtk_toggle_button_new_with_mnemonic (_("_Sidebar"));
1166 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
1167 g_signal_connect (G_OBJECT (priv->sidebar_button), "toggled",
1168 G_CALLBACK (empathy_streamed_media_window_sidebar_toggled_cb), self);
1170 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
1172 h = gtk_hbox_new (FALSE, 3);
1173 gtk_box_pack_end (GTK_BOX (priv->vbox), h, FALSE, FALSE, 3);
1174 gtk_box_pack_end (GTK_BOX (h), priv->sidebar_button, FALSE, FALSE, 3);
1176 priv->sidebar = ev_sidebar_new ();
1177 g_signal_connect (G_OBJECT (priv->sidebar),
1178 "hide", G_CALLBACK (empathy_streamed_media_window_sidebar_hidden_cb), self);
1179 g_signal_connect (G_OBJECT (priv->sidebar),
1180 "show", G_CALLBACK (empathy_streamed_media_window_sidebar_shown_cb), self);
1181 gtk_paned_pack2 (GTK_PANED (priv->pane), priv->sidebar, FALSE, FALSE);
1183 page = empathy_streamed_media_window_create_audio_input (self);
1184 ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "audio-input",
1185 _("Audio input"), page);
1187 page = empathy_streamed_media_window_create_video_input (self);
1188 ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "video-input",
1189 _("Video input"), page);
1191 priv->dtmf_panel = empathy_streamed_media_window_create_dtmf (self);
1192 ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "dialpad",
1193 _("Dialpad"), priv->dtmf_panel);
1195 gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
1197 ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "details",
1198 _("Details"), priv->details_vbox);
1200 gtk_widget_show_all (top_vbox);
1202 gtk_widget_hide (priv->sidebar);
1204 priv->fullscreen = empathy_streamed_media_window_fullscreen_new (self);
1205 empathy_streamed_media_window_fullscreen_set_video_widget (priv->fullscreen,
1206 priv->video_output);
1207 g_signal_connect (G_OBJECT (priv->fullscreen->leave_fullscreen_button),
1208 "clicked", G_CALLBACK (empathy_streamed_media_window_fullscreen_cb), self);
1210 g_signal_connect (G_OBJECT (self), "realize",
1211 G_CALLBACK (empathy_streamed_media_window_realized_cb), self);
1213 g_signal_connect (G_OBJECT (self), "delete-event",
1214 G_CALLBACK (empathy_streamed_media_window_delete_cb), self);
1216 g_signal_connect (G_OBJECT (self), "window-state-event",
1217 G_CALLBACK (empathy_streamed_media_window_state_event_cb), self);
1219 g_signal_connect (G_OBJECT (self), "key-press-event",
1220 G_CALLBACK (empathy_streamed_media_window_key_press_cb), self);
1222 priv->timer = g_timer_new ();
1224 g_object_ref (priv->ui_manager);
1225 g_object_unref (gui);
1227 priv->sound_mgr = empathy_sound_manager_dup_singleton ();
1229 empathy_geometry_bind (GTK_WINDOW (self), "call-window");
1232 /* Instead of specifying a width and a height, we specify only one size. That's
1233 because we want a square avatar icon. */
1235 init_contact_avatar_with_size (EmpathyContact *contact,
1236 GtkWidget *image_widget,
1239 GdkPixbuf *pixbuf_avatar = NULL;
1241 if (contact != NULL)
1243 pixbuf_avatar = empathy_pixbuf_avatar_from_contact_scaled (contact,
1247 if (pixbuf_avatar == NULL)
1249 pixbuf_avatar = empathy_pixbuf_from_icon_name_sized (
1250 EMPATHY_IMAGE_AVATAR_DEFAULT, size);
1253 gtk_image_set_from_pixbuf (GTK_IMAGE (image_widget), pixbuf_avatar);
1255 if (pixbuf_avatar != NULL)
1256 g_object_unref (pixbuf_avatar);
1260 set_window_title (EmpathyStreamedMediaWindow *self)
1262 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1265 /* translators: Call is a noun and %s is the contact name. This string
1266 * is used in the window title */
1267 tmp = g_strdup_printf (_("Call with %s"),
1268 empathy_contact_get_alias (priv->contact));
1269 gtk_window_set_title (GTK_WINDOW (self), tmp);
1274 contact_name_changed_cb (EmpathyContact *contact,
1275 GParamSpec *pspec, EmpathyStreamedMediaWindow *self)
1277 set_window_title (self);
1281 contact_avatar_changed_cb (EmpathyContact *contact,
1282 GParamSpec *pspec, GtkWidget *avatar_widget)
1285 GtkAllocation allocation;
1287 gtk_widget_get_allocation (avatar_widget, &allocation);
1288 size = allocation.height;
1292 /* the widget is not allocated yet, set a default size */
1293 size = MIN (REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT,
1294 REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH);
1297 init_contact_avatar_with_size (contact, avatar_widget, size);
1301 empathy_streamed_media_window_got_self_contact_cb (TpConnection *connection,
1302 EmpathyContact *contact, const GError *error, gpointer user_data,
1303 GObject *weak_object)
1305 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
1306 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1308 init_contact_avatar_with_size (contact, priv->self_user_avatar_widget,
1309 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
1311 g_signal_connect (contact, "notify::avatar",
1312 G_CALLBACK (contact_avatar_changed_cb), priv->self_user_avatar_widget);
1316 empathy_streamed_media_window_setup_avatars (EmpathyStreamedMediaWindow *self,
1317 EmpathyStreamedMediaHandler *handler)
1319 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1321 g_object_get (handler, "contact", &(priv->contact), NULL);
1323 if (priv->contact != NULL)
1325 TpConnection *connection;
1327 set_window_title (self);
1329 g_signal_connect (priv->contact, "notify::name",
1330 G_CALLBACK (contact_name_changed_cb), self);
1331 g_signal_connect (priv->contact, "notify::avatar",
1332 G_CALLBACK (contact_avatar_changed_cb),
1333 priv->remote_user_avatar_widget);
1335 /* Retreiving the self avatar */
1336 connection = empathy_contact_get_connection (priv->contact);
1337 empathy_tp_contact_factory_get_from_handle (connection,
1338 tp_connection_get_self_handle (connection),
1339 empathy_streamed_media_window_got_self_contact_cb, self, NULL, G_OBJECT (self));
1343 g_warning ("call handler doesn't have a contact");
1344 /* translators: Call is a noun. This string is used in the window
1346 gtk_window_set_title (GTK_WINDOW (self), _("Call"));
1348 /* Since we can't access the remote contact, we can't get a connection
1349 to it and can't get the self contact (and its avatar). This means
1350 that we have to manually set the self avatar. */
1351 init_contact_avatar_with_size (NULL, priv->self_user_avatar_widget,
1352 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
1355 init_contact_avatar_with_size (priv->contact,
1356 priv->remote_user_avatar_widget,
1357 MIN (REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH,
1358 REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT));
1360 /* The remote avatar is shown by default and will be hidden when we receive
1361 video from the remote side. */
1362 gtk_widget_hide (priv->video_output);
1363 gtk_widget_show (priv->remote_user_avatar_widget);
1367 update_send_codec (EmpathyStreamedMediaWindow *self,
1370 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1377 codec = empathy_streamed_media_handler_get_send_audio_codec (priv->handler);
1378 widget = priv->acodec_encoding_label;
1382 codec = empathy_streamed_media_handler_get_send_video_codec (priv->handler);
1383 widget = priv->vcodec_encoding_label;
1389 tmp = g_strdup_printf ("%s/%u", codec->encoding_name, codec->clock_rate);
1390 gtk_label_set_text (GTK_LABEL (widget), tmp);
1395 send_audio_codec_notify_cb (GObject *object,
1399 EmpathyStreamedMediaWindow *self = user_data;
1401 update_send_codec (self, TRUE);
1405 send_video_codec_notify_cb (GObject *object,
1409 EmpathyStreamedMediaWindow *self = user_data;
1411 update_send_codec (self, FALSE);
1415 update_recv_codec (EmpathyStreamedMediaWindow *self,
1418 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1421 GString *str = NULL;
1425 codecs = empathy_streamed_media_handler_get_recv_audio_codecs (priv->handler);
1426 widget = priv->acodec_decoding_label;
1430 codecs = empathy_streamed_media_handler_get_recv_video_codecs (priv->handler);
1431 widget = priv->vcodec_decoding_label;
1437 for (l = codecs; l != NULL; l = g_list_next (l))
1439 FsCodec *codec = l->data;
1442 str = g_string_new (NULL);
1444 g_string_append (str, ", ");
1446 g_string_append_printf (str, "%s/%u", codec->encoding_name,
1450 gtk_label_set_text (GTK_LABEL (widget), str->str);
1451 g_string_free (str, TRUE);
1455 recv_audio_codecs_notify_cb (GObject *object,
1459 EmpathyStreamedMediaWindow *self = user_data;
1461 update_recv_codec (self, TRUE);
1465 recv_video_codecs_notify_cb (GObject *object,
1469 EmpathyStreamedMediaWindow *self = user_data;
1471 update_recv_codec (self, FALSE);
1474 static const gchar *
1475 candidate_type_to_str (FsCandidate *candidate)
1477 switch (candidate->type)
1479 case FS_CANDIDATE_TYPE_HOST:
1481 case FS_CANDIDATE_TYPE_SRFLX:
1482 return "server reflexive";
1483 case FS_CANDIDATE_TYPE_PRFLX:
1484 return "peer reflexive";
1485 case FS_CANDIDATE_TYPE_RELAY:
1487 case FS_CANDIDATE_TYPE_MULTICAST:
1494 static const gchar *
1495 candidate_type_to_desc (FsCandidate *candidate)
1497 switch (candidate->type)
1499 case FS_CANDIDATE_TYPE_HOST:
1500 return _("The IP address as seen by the machine");
1501 case FS_CANDIDATE_TYPE_SRFLX:
1502 return _("The IP address as seen by a server on the Internet");
1503 case FS_CANDIDATE_TYPE_PRFLX:
1504 return _("The IP address of the peer as seen by the other side");
1505 case FS_CANDIDATE_TYPE_RELAY:
1506 return _("The IP address of a relay server");
1507 case FS_CANDIDATE_TYPE_MULTICAST:
1508 return _("The IP address of the multicast group");
1515 update_candidat_widget (EmpathyStreamedMediaWindow *self,
1518 FsCandidate *candidate)
1522 g_assert (candidate != NULL);
1523 str = g_strdup_printf ("%s %u (%s)", candidate->ip,
1524 candidate->port, candidate_type_to_str (candidate));
1526 gtk_label_set_text (GTK_LABEL (label), str);
1527 gtk_widget_set_tooltip_text (img, candidate_type_to_desc (candidate));
1533 candidates_changed_cb (GObject *object,
1535 EmpathyStreamedMediaWindow *self)
1537 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1538 FsCandidate *candidate = NULL;
1540 if (type == FS_MEDIA_TYPE_VIDEO)
1542 /* Update remote candidate */
1543 candidate = empathy_streamed_media_handler_get_video_remote_candidate (
1546 update_candidat_widget (self, priv->video_remote_candidate_label,
1547 priv->video_remote_candidate_info_img, candidate);
1549 /* Update local candidate */
1550 candidate = empathy_streamed_media_handler_get_video_local_candidate (
1553 update_candidat_widget (self, priv->video_local_candidate_label,
1554 priv->video_local_candidate_info_img, candidate);
1558 /* Update remote candidate */
1559 candidate = empathy_streamed_media_handler_get_audio_remote_candidate (
1562 update_candidat_widget (self, priv->audio_remote_candidate_label,
1563 priv->audio_remote_candidate_info_img, candidate);
1565 /* Update local candidate */
1566 candidate = empathy_streamed_media_handler_get_audio_local_candidate (
1569 update_candidat_widget (self, priv->audio_local_candidate_label,
1570 priv->audio_local_candidate_info_img, candidate);
1575 empathy_streamed_media_window_constructed (GObject *object)
1577 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (object);
1578 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1579 EmpathyTpStreamedMedia *call;
1581 g_assert (priv->handler != NULL);
1583 g_object_get (priv->handler, "tp-call", &call, NULL);
1584 priv->outgoing = (call == NULL);
1586 g_object_unref (call);
1588 empathy_streamed_media_window_setup_avatars (self, priv->handler);
1589 empathy_streamed_media_window_set_state_connecting (self);
1591 if (!empathy_streamed_media_handler_has_initial_video (priv->handler))
1593 gtk_toggle_tool_button_set_active (
1594 GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_off), TRUE);
1596 /* If call has InitialVideo, the preview will be started once the call has
1597 * been started (start_call()). */
1599 update_send_codec (self, TRUE);
1600 update_send_codec (self, FALSE);
1601 update_recv_codec (self, TRUE);
1602 update_recv_codec (self, FALSE);
1604 tp_g_signal_connect_object (priv->handler, "notify::send-audio-codec",
1605 G_CALLBACK (send_audio_codec_notify_cb), self, 0);
1606 tp_g_signal_connect_object (priv->handler, "notify::send-video-codec",
1607 G_CALLBACK (send_video_codec_notify_cb), self, 0);
1608 tp_g_signal_connect_object (priv->handler, "notify::recv-audio-codecs",
1609 G_CALLBACK (recv_audio_codecs_notify_cb), self, 0);
1610 tp_g_signal_connect_object (priv->handler, "notify::recv-video-codecs",
1611 G_CALLBACK (recv_video_codecs_notify_cb), self, 0);
1613 tp_g_signal_connect_object (priv->handler, "candidates-changed",
1614 G_CALLBACK (candidates_changed_cb), self, 0);
1617 static void empathy_streamed_media_window_dispose (GObject *object);
1618 static void empathy_streamed_media_window_finalize (GObject *object);
1621 empathy_streamed_media_window_set_property (GObject *object,
1622 guint property_id, const GValue *value, GParamSpec *pspec)
1624 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (object);
1626 switch (property_id)
1628 case PROP_STREAMED_MEDIA_HANDLER:
1629 priv->handler = g_value_dup_object (value);
1632 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1637 empathy_streamed_media_window_get_property (GObject *object,
1638 guint property_id, GValue *value, GParamSpec *pspec)
1640 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (object);
1642 switch (property_id)
1644 case PROP_STREAMED_MEDIA_HANDLER:
1645 g_value_set_object (value, priv->handler);
1648 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1653 empathy_streamed_media_window_class_init (
1654 EmpathyStreamedMediaWindowClass *empathy_streamed_media_window_class)
1656 GObjectClass *object_class = G_OBJECT_CLASS (empathy_streamed_media_window_class);
1657 GParamSpec *param_spec;
1659 g_type_class_add_private (empathy_streamed_media_window_class,
1660 sizeof (EmpathyStreamedMediaWindowPriv));
1662 object_class->constructed = empathy_streamed_media_window_constructed;
1663 object_class->set_property = empathy_streamed_media_window_set_property;
1664 object_class->get_property = empathy_streamed_media_window_get_property;
1666 object_class->dispose = empathy_streamed_media_window_dispose;
1667 object_class->finalize = empathy_streamed_media_window_finalize;
1669 param_spec = g_param_spec_object ("handler",
1670 "handler", "The call handler",
1671 EMPATHY_TYPE_STREAMED_MEDIA_HANDLER,
1672 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
1673 g_object_class_install_property (object_class,
1674 PROP_STREAMED_MEDIA_HANDLER, param_spec);
1678 empathy_streamed_media_window_video_stream_changed_cb (EmpathyTpStreamedMedia *call,
1679 GParamSpec *property, EmpathyStreamedMediaWindow *self)
1681 DEBUG ("video stream changed");
1682 empathy_streamed_media_window_update_avatars_visibility (call, self);
1686 empathy_streamed_media_window_dispose (GObject *object)
1688 EmpathyTpStreamedMedia *call;
1689 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (object);
1690 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1692 if (priv->dispose_has_run)
1695 priv->dispose_has_run = TRUE;
1697 g_object_get (priv->handler, "tp-call", &call, NULL);
1701 g_object_unref (call);
1704 if (priv->handler != NULL)
1706 empathy_streamed_media_handler_stop_call (priv->handler);
1707 g_object_unref (priv->handler);
1709 priv->handler = NULL;
1711 if (priv->bus_message_source_id != 0)
1713 g_source_remove (priv->bus_message_source_id);
1714 priv->bus_message_source_id = 0;
1717 if (priv->pipeline != NULL)
1718 g_object_unref (priv->pipeline);
1719 priv->pipeline = NULL;
1721 if (priv->video_input != NULL)
1722 g_object_unref (priv->video_input);
1723 priv->video_input = NULL;
1725 if (priv->audio_input != NULL)
1726 g_object_unref (priv->audio_input);
1727 priv->audio_input = NULL;
1729 if (priv->audio_output != NULL)
1730 g_object_unref (priv->audio_output);
1731 priv->audio_output = NULL;
1733 if (priv->video_tee != NULL)
1734 g_object_unref (priv->video_tee);
1735 priv->video_tee = NULL;
1737 if (priv->liveadder != NULL)
1738 gst_object_unref (priv->liveadder);
1739 priv->liveadder = NULL;
1741 if (priv->fsnotifier != NULL)
1742 g_object_unref (priv->fsnotifier);
1743 priv->fsnotifier = NULL;
1745 if (priv->timer_id != 0)
1746 g_source_remove (priv->timer_id);
1749 if (priv->ui_manager != NULL)
1750 g_object_unref (priv->ui_manager);
1751 priv->ui_manager = NULL;
1753 if (priv->fullscreen != NULL)
1754 g_object_unref (priv->fullscreen);
1755 priv->fullscreen = NULL;
1757 if (priv->contact != NULL)
1759 g_signal_handlers_disconnect_by_func (priv->contact,
1760 contact_name_changed_cb, self);
1761 g_object_unref (priv->contact);
1762 priv->contact = NULL;
1765 tp_clear_object (&priv->sound_mgr);
1767 /* release any references held by the object here */
1768 if (G_OBJECT_CLASS (empathy_streamed_media_window_parent_class)->dispose)
1769 G_OBJECT_CLASS (empathy_streamed_media_window_parent_class)->dispose (object);
1773 disconnect_video_output_motion_handler (EmpathyStreamedMediaWindow *self)
1775 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1777 if (priv->video_output_motion_handler_id != 0)
1779 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1780 priv->video_output_motion_handler_id);
1781 priv->video_output_motion_handler_id = 0;
1786 empathy_streamed_media_window_finalize (GObject *object)
1788 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (object);
1789 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1791 disconnect_video_output_motion_handler (self);
1793 /* free any data held directly by the object here */
1794 g_mutex_free (priv->lock);
1796 g_timer_destroy (priv->timer);
1798 G_OBJECT_CLASS (empathy_streamed_media_window_parent_class)->finalize (object);
1802 EmpathyStreamedMediaWindow *
1803 empathy_streamed_media_window_new (EmpathyStreamedMediaHandler *handler)
1805 return EMPATHY_STREAMED_MEDIA_WINDOW (
1806 g_object_new (EMPATHY_TYPE_STREAMED_MEDIA_WINDOW, "handler", handler, NULL));
1810 empathy_streamed_media_window_conference_added_cb (EmpathyStreamedMediaHandler *handler,
1811 GstElement *conference, gpointer user_data)
1813 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
1814 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1816 gst_bin_add (GST_BIN (priv->pipeline), conference);
1818 gst_element_set_state (conference, GST_STATE_PLAYING);
1822 empathy_streamed_media_window_request_resource_cb (EmpathyStreamedMediaHandler *handler,
1823 FsMediaType type, FsStreamDirection direction, gpointer user_data)
1825 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
1826 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1828 if (type != FS_MEDIA_TYPE_VIDEO)
1831 if (direction == FS_DIRECTION_RECV)
1834 /* video and direction is send */
1835 return priv->video_input != NULL;
1839 empathy_streamed_media_window_reset_pipeline (EmpathyStreamedMediaWindow *self)
1841 GstStateChangeReturn state_change_return;
1842 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1844 if (priv->pipeline == NULL)
1847 if (priv->bus_message_source_id != 0)
1849 g_source_remove (priv->bus_message_source_id);
1850 priv->bus_message_source_id = 0;
1853 state_change_return = gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1855 if (state_change_return == GST_STATE_CHANGE_SUCCESS ||
1856 state_change_return == GST_STATE_CHANGE_NO_PREROLL)
1858 if (priv->pipeline != NULL)
1859 g_object_unref (priv->pipeline);
1860 priv->pipeline = NULL;
1862 g_signal_handlers_disconnect_by_func (priv->audio_input_adj,
1863 empathy_streamed_media_window_mic_volume_changed_cb, self);
1865 if (priv->video_tee != NULL)
1866 g_object_unref (priv->video_tee);
1867 priv->video_tee = NULL;
1869 if (priv->video_preview != NULL)
1870 gtk_widget_destroy (priv->video_preview);
1871 priv->video_preview = NULL;
1873 priv->liveadder = NULL;
1874 priv->funnel = NULL;
1876 create_pipeline (self);
1877 /* Call will be started when user will hit the 'redial' button */
1878 priv->start_call_when_playing = FALSE;
1879 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1885 g_message ("Error: could not destroy pipeline. Closing call window");
1886 gtk_widget_destroy (GTK_WIDGET (self));
1893 reset_details_pane (EmpathyStreamedMediaWindow *self)
1895 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1897 gtk_label_set_text (GTK_LABEL (priv->vcodec_encoding_label), _("Unknown"));
1898 gtk_label_set_text (GTK_LABEL (priv->acodec_encoding_label), _("Unknown"));
1899 gtk_label_set_text (GTK_LABEL (priv->vcodec_decoding_label), _("Unknown"));
1900 gtk_label_set_text (GTK_LABEL (priv->acodec_decoding_label), _("Unknown"));
1904 empathy_streamed_media_window_disconnected (EmpathyStreamedMediaWindow *self,
1907 gboolean could_disconnect = FALSE;
1908 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1909 gboolean could_reset_pipeline;
1911 /* Leave full screen mode if needed */
1912 gtk_window_unfullscreen (GTK_WINDOW (self));
1914 gtk_action_set_sensitive (priv->menu_fullscreen, FALSE);
1916 could_reset_pipeline = empathy_streamed_media_window_reset_pipeline (self);
1918 if (priv->call_state == CONNECTING)
1919 empathy_sound_manager_stop (priv->sound_mgr,
1920 EMPATHY_SOUND_PHONE_OUTGOING);
1922 if (priv->call_state != REDIALING)
1923 priv->call_state = DISCONNECTED;
1925 if (could_reset_pipeline)
1927 g_mutex_lock (priv->lock);
1929 g_timer_stop (priv->timer);
1931 if (priv->timer_id != 0)
1932 g_source_remove (priv->timer_id);
1935 g_mutex_unlock (priv->lock);
1938 /* We are about to destroy the window, no need to update it or create
1939 * a video preview */
1942 empathy_streamed_media_window_status_message (self, _("Disconnected"));
1944 gtk_action_set_sensitive (priv->redial, TRUE);
1945 gtk_widget_set_sensitive (priv->redial_button, TRUE);
1947 /* Unsensitive the camera and mic button */
1948 gtk_widget_set_sensitive (priv->tool_button_camera_on, FALSE);
1949 gtk_action_set_sensitive (priv->action_camera_on, FALSE);
1950 gtk_widget_set_sensitive (priv->mic_button, FALSE);
1952 /* Be sure that the mic button is enabled */
1953 gtk_toggle_tool_button_set_active (
1954 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), TRUE);
1956 if (priv->camera_state == CAMERA_STATE_ON)
1958 /* Enable the 'preview' button as we are not sending atm. */
1959 gtk_toggle_tool_button_set_active (
1960 GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_preview), TRUE);
1962 else if (priv->camera_state == CAMERA_STATE_PREVIEW)
1964 /* Restart the preview with the new pipeline. */
1965 display_video_preview (self, TRUE);
1968 gtk_progress_bar_set_fraction (
1969 GTK_PROGRESS_BAR (priv->volume_progress_bar), 0);
1971 /* destroy the video output; it will be recreated when we'll redial */
1972 disconnect_video_output_motion_handler (self);
1973 gtk_widget_destroy (priv->video_output);
1974 priv->video_output = NULL;
1976 gtk_widget_show (priv->remote_user_avatar_widget);
1978 reset_details_pane (self);
1980 priv->sending_video = FALSE;
1981 priv->call_started = FALSE;
1983 could_disconnect = TRUE;
1985 /* TODO: display the self avatar of the preview (depends if the "Always
1986 * Show Video Preview" is enabled or not) */
1989 return could_disconnect;
1994 empathy_streamed_media_window_channel_closed_cb (EmpathyStreamedMediaHandler *handler,
1997 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
1998 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2000 if (empathy_streamed_media_window_disconnected (self, TRUE) &&
2001 priv->call_state == REDIALING)
2002 empathy_streamed_media_window_restart_call (self);
2007 empathy_streamed_media_window_channel_stream_closed_cb (EmpathyStreamedMediaHandler *handler,
2008 TfStream *stream, gpointer user_data)
2010 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
2011 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2014 g_object_get (stream, "media-type", &media_type, NULL);
2017 * This assumes that there is only one video stream per channel...
2020 if (media_type == TP_MEDIA_STREAM_TYPE_VIDEO)
2022 if (priv->funnel != NULL)
2026 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
2027 (priv->video_output));
2029 gst_element_set_state (output, GST_STATE_NULL);
2030 gst_element_set_state (priv->funnel, GST_STATE_NULL);
2032 gst_bin_remove (GST_BIN (priv->pipeline), output);
2033 gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
2034 priv->funnel = NULL;
2037 else if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO)
2039 if (priv->liveadder != NULL)
2041 gst_element_set_state (priv->audio_output, GST_STATE_NULL);
2042 gst_element_set_state (priv->liveadder, GST_STATE_NULL);
2044 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
2045 gst_bin_remove (GST_BIN (priv->pipeline), priv->liveadder);
2046 priv->liveadder = NULL;
2051 /* Called with global lock held */
2053 empathy_streamed_media_window_get_video_sink_pad (EmpathyStreamedMediaWindow *self)
2055 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2059 if (priv->funnel == NULL)
2061 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
2062 (priv->video_output));
2064 priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
2068 g_warning ("Could not create fsfunnel");
2072 if (!gst_bin_add (GST_BIN (priv->pipeline), priv->funnel))
2074 gst_object_unref (priv->funnel);
2075 priv->funnel = NULL;
2076 g_warning ("Could not add funnel to pipeline");
2080 if (!gst_bin_add (GST_BIN (priv->pipeline), output))
2082 g_warning ("Could not add the video output widget to the pipeline");
2086 if (!gst_element_link (priv->funnel, output))
2088 g_warning ("Could not link output sink to funnel");
2089 goto error_output_added;
2092 if (gst_element_set_state (output, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2094 g_warning ("Could not start video sink");
2095 goto error_output_added;
2098 if (gst_element_set_state (priv->funnel, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2100 g_warning ("Could not start funnel");
2101 goto error_output_added;
2105 pad = gst_element_get_request_pad (priv->funnel, "sink%d");
2108 g_warning ("Could not get request pad from funnel");
2115 gst_element_set_locked_state (priv->funnel, TRUE);
2116 gst_element_set_locked_state (output, TRUE);
2118 gst_element_set_state (priv->funnel, GST_STATE_NULL);
2119 gst_element_set_state (output, GST_STATE_NULL);
2121 gst_bin_remove (GST_BIN (priv->pipeline), output);
2122 gst_element_set_locked_state (output, FALSE);
2126 gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
2127 priv->funnel = NULL;
2132 /* Called with global lock held */
2134 empathy_streamed_media_window_get_audio_sink_pad (EmpathyStreamedMediaWindow *self)
2136 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2139 GError *gerror = NULL;
2141 if (priv->liveadder == NULL)
2143 priv->liveadder = gst_element_factory_make ("liveadder", NULL);
2145 if (!gst_bin_add (GST_BIN (priv->pipeline), priv->liveadder))
2147 g_warning ("Could not add liveadder to the pipeline");
2148 goto error_add_liveadder;
2150 if (!gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output))
2152 g_warning ("Could not add audio sink to pipeline");
2153 goto error_add_output;
2156 if (gst_element_set_state (priv->liveadder, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2158 g_warning ("Could not start liveadder");
2162 if (gst_element_set_state (priv->audio_output, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2164 g_warning ("Could not start audio sink");
2168 if (GST_PAD_LINK_FAILED (
2169 gst_element_link (priv->liveadder, priv->audio_output)))
2171 g_warning ("Could not link liveadder to audio output");
2176 filter = gst_parse_bin_from_description (
2177 "audioconvert ! audioresample ! audioconvert", TRUE, &gerror);
2180 g_warning ("Could not make audio conversion filter: %s", gerror->message);
2181 g_clear_error (&gerror);
2185 if (!gst_bin_add (GST_BIN (priv->pipeline), filter))
2187 g_warning ("Could not add audio conversion filter to pipeline");
2188 gst_object_unref (filter);
2192 if (gst_element_set_state (filter, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2194 g_warning ("Could not start audio conversion filter");
2198 if (!gst_element_link (filter, priv->liveadder))
2200 g_warning ("Could not link audio conversion filter to liveadder");
2204 pad = gst_element_get_static_pad (filter, "sink");
2208 g_warning ("Could not get sink pad from filter");
2216 gst_element_set_locked_state (filter, TRUE);
2217 gst_element_set_state (filter, GST_STATE_NULL);
2218 gst_bin_remove (GST_BIN (priv->pipeline), filter);
2222 gst_element_set_locked_state (priv->liveadder, TRUE);
2223 gst_element_set_locked_state (priv->audio_output, TRUE);
2225 gst_element_set_state (priv->liveadder, GST_STATE_NULL);
2226 gst_element_set_state (priv->audio_output, GST_STATE_NULL);
2228 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
2232 gst_bin_remove (GST_BIN (priv->pipeline), priv->liveadder);
2234 gst_element_set_locked_state (priv->liveadder, FALSE);
2235 gst_element_set_locked_state (priv->audio_output, FALSE);
2237 error_add_liveadder:
2239 if (priv->liveadder != NULL)
2241 gst_object_unref (priv->liveadder);
2242 priv->liveadder = NULL;
2249 empathy_streamed_media_window_update_timer (gpointer user_data)
2251 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
2252 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2256 time_ = g_timer_elapsed (priv->timer, NULL);
2258 /* Translators: number of minutes:seconds the caller has been connected */
2259 str = g_strdup_printf (_("Connected — %d:%02dm"), (int) time_ / 60,
2261 empathy_streamed_media_window_status_message (self, str);
2268 display_error (EmpathyStreamedMediaWindow *self,
2269 EmpathyTpStreamedMedia *call,
2273 const gchar *details)
2275 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2276 GtkWidget *info_bar;
2277 GtkWidget *content_area;
2284 /* Create info bar */
2285 info_bar = gtk_info_bar_new_with_buttons (GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
2288 gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
2290 content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
2292 /* hbox containing the image and the messages vbox */
2293 hbox = gtk_hbox_new (FALSE, 3);
2294 gtk_container_add (GTK_CONTAINER (content_area), hbox);
2297 image = gtk_image_new_from_icon_name (img, GTK_ICON_SIZE_DIALOG);
2298 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
2300 /* vbox containing the main message and the details expander */
2301 vbox = gtk_vbox_new (FALSE, 3);
2302 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
2305 txt = g_strdup_printf ("<b>%s</b>\n%s", title, desc);
2307 label = gtk_label_new (NULL);
2308 gtk_label_set_markup (GTK_LABEL (label), txt);
2309 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
2310 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
2313 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
2316 if (details != NULL)
2318 GtkWidget *expander;
2320 expander = gtk_expander_new (_("Technical Details"));
2322 txt = g_strdup_printf ("<i>%s</i>", details);
2324 label = gtk_label_new (NULL);
2325 gtk_label_set_markup (GTK_LABEL (label), txt);
2326 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
2327 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
2330 gtk_container_add (GTK_CONTAINER (expander), label);
2331 gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
2334 g_signal_connect (info_bar, "response",
2335 G_CALLBACK (gtk_widget_destroy), NULL);
2337 gtk_box_pack_start (GTK_BOX (priv->errors_vbox), info_bar,
2338 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
2339 gtk_widget_show_all (info_bar);
2343 media_stream_error_to_txt (EmpathyStreamedMediaWindow *self,
2344 EmpathyTpStreamedMedia *call,
2346 TpMediaStreamError error)
2348 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2355 case TP_MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED:
2357 return g_strdup_printf (
2358 _("%s's software does not understand any of the audio formats "
2359 "supported by your computer"),
2360 empathy_contact_get_alias (priv->contact));
2362 return g_strdup_printf (
2363 _("%s's software does not understand any of the video formats "
2364 "supported by your computer"),
2365 empathy_contact_get_alias (priv->contact));
2367 case TP_MEDIA_STREAM_ERROR_CONNECTION_FAILED:
2368 return g_strdup_printf (
2369 _("Can't establish a connection to %s. "
2370 "One of you might be on a network that does not allow "
2371 "direct connections."),
2372 empathy_contact_get_alias (priv->contact));
2374 case TP_MEDIA_STREAM_ERROR_NETWORK_ERROR:
2375 return g_strdup (_("There was a failure on the network"));
2377 case TP_MEDIA_STREAM_ERROR_NO_CODECS:
2379 return g_strdup (_("The audio formats necessary for this call "
2380 "are not installed on your computer"));
2382 return g_strdup (_("The video formats necessary for this call "
2383 "are not installed on your computer"));
2385 case TP_MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR:
2386 cm = empathy_tp_streamed_media_get_connection_manager (call);
2388 url = g_strdup_printf ("http://bugs.freedesktop.org/enter_bug.cgi?"
2389 "product=Telepathy&component=%s", cm);
2391 result = g_strdup_printf (
2392 _("Something unexpected happened in a Telepathy component. "
2393 "Please <a href=\"%s\">report this bug</a> and attach "
2394 "logs gathered from the 'Debug' window in the Help menu."), url);
2399 case TP_MEDIA_STREAM_ERROR_MEDIA_ERROR:
2400 return g_strdup (_("There was a failure in the call engine"));
2402 case TP_MEDIA_STREAM_ERROR_EOS:
2403 return g_strdup (_("The end of the stream was reached"));
2405 case TP_MEDIA_STREAM_ERROR_UNKNOWN:
2412 empathy_streamed_media_window_stream_error (EmpathyStreamedMediaWindow *self,
2413 EmpathyTpStreamedMedia *call,
2422 desc = media_stream_error_to_txt (self, call, audio, code);
2425 /* No description, use the error message. That's not great as it's not
2426 * localized but it's better than nothing. */
2427 display_error (self, call, icon, title, msg, NULL);
2431 display_error (self, call, icon, title, desc, msg);
2437 empathy_streamed_media_window_audio_stream_error (EmpathyTpStreamedMedia *call,
2440 EmpathyStreamedMediaWindow *self)
2442 empathy_streamed_media_window_stream_error (self, call, TRUE, code, msg,
2443 "gnome-stock-mic", _("Can't establish audio stream"));
2447 empathy_streamed_media_window_video_stream_error (EmpathyTpStreamedMedia *call,
2450 EmpathyStreamedMediaWindow *self)
2452 empathy_streamed_media_window_stream_error (self, call, FALSE, code, msg,
2453 "camera-web", _("Can't establish video stream"));
2457 empathy_streamed_media_window_connected (gpointer user_data)
2459 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
2460 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2461 EmpathyTpStreamedMedia *call;
2462 gboolean can_send_video;
2464 empathy_sound_manager_stop (priv->sound_mgr, EMPATHY_SOUND_PHONE_OUTGOING);
2466 can_send_video = priv->video_input != NULL && priv->contact != NULL &&
2467 empathy_contact_can_voip_video (priv->contact);
2469 g_object_get (priv->handler, "tp-call", &call, NULL);
2471 tp_g_signal_connect_object (call, "notify::video-stream",
2472 G_CALLBACK (empathy_streamed_media_window_video_stream_changed_cb),
2475 if (empathy_tp_streamed_media_has_dtmf (call))
2476 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
2478 if (priv->video_input == NULL)
2479 empathy_streamed_media_window_set_send_video (self, CAMERA_STATE_OFF);
2481 priv->sending_video = can_send_video ?
2482 empathy_tp_streamed_media_is_sending_video (call) : FALSE;
2484 gtk_toggle_tool_button_set_active (
2485 GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_on),
2486 priv->sending_video && priv->video_input != NULL);
2487 gtk_widget_set_sensitive (priv->tool_button_camera_on, can_send_video);
2488 gtk_action_set_sensitive (priv->action_camera_on, can_send_video);
2490 gtk_action_set_sensitive (priv->redial, FALSE);
2491 gtk_widget_set_sensitive (priv->redial_button, FALSE);
2493 gtk_widget_set_sensitive (priv->mic_button, TRUE);
2495 empathy_streamed_media_window_update_avatars_visibility (call, self);
2497 g_object_unref (call);
2499 g_mutex_lock (priv->lock);
2501 priv->timer_id = g_timeout_add_seconds (1,
2502 empathy_streamed_media_window_update_timer, self);
2504 g_mutex_unlock (priv->lock);
2506 empathy_streamed_media_window_update_timer (self);
2508 gtk_action_set_sensitive (priv->menu_fullscreen, TRUE);
2514 /* Called from the streaming thread */
2516 empathy_streamed_media_window_src_added_cb (EmpathyStreamedMediaHandler *handler,
2517 GstPad *src, guint media_type, gpointer user_data)
2519 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
2520 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2521 gboolean retval = FALSE;
2525 g_mutex_lock (priv->lock);
2527 if (priv->call_state != CONNECTED)
2529 g_timer_start (priv->timer);
2530 priv->timer_id = g_idle_add (empathy_streamed_media_window_connected, self);
2531 priv->call_state = CONNECTED;
2536 case TP_MEDIA_STREAM_TYPE_AUDIO:
2537 pad = empathy_streamed_media_window_get_audio_sink_pad (self);
2539 case TP_MEDIA_STREAM_TYPE_VIDEO:
2540 gtk_widget_hide (priv->remote_user_avatar_widget);
2541 gtk_widget_show (priv->video_output);
2542 pad = empathy_streamed_media_window_get_video_sink_pad (self);
2545 g_assert_not_reached ();
2551 if (GST_PAD_LINK_FAILED (gst_pad_link (src, pad)))
2552 g_warning ("Could not link %s sink pad",
2553 media_type == TP_MEDIA_STREAM_TYPE_AUDIO ? "audio" : "video");
2557 gst_object_unref (pad);
2561 /* If no sink could be linked, try to add fakesink to prevent the whole call
2566 GstElement *fakesink = gst_element_factory_make ("fakesink", NULL);
2568 if (gst_bin_add (GST_BIN (priv->pipeline), fakesink))
2570 GstPad *sinkpad = gst_element_get_static_pad (fakesink, "sink");
2571 if (gst_element_set_state (fakesink, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE ||
2572 GST_PAD_LINK_FAILED (gst_pad_link (src, sinkpad)))
2574 gst_element_set_locked_state (fakesink, TRUE);
2575 gst_element_set_state (fakesink, GST_STATE_NULL);
2576 gst_bin_remove (GST_BIN (priv->pipeline), fakesink);
2580 g_debug ("Could not link real sink, linked fakesink instead");
2582 gst_object_unref (sinkpad);
2586 gst_object_unref (fakesink);
2591 g_mutex_unlock (priv->lock);
2597 empathy_streamed_media_window_sink_added_cb (EmpathyStreamedMediaHandler *handler,
2598 GstPad *sink, guint media_type, gpointer user_data)
2600 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
2601 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2603 gboolean retval = FALSE;
2607 case TP_MEDIA_STREAM_TYPE_AUDIO:
2608 if (!gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input))
2610 g_warning ("Could not add audio source to pipeline");
2614 pad = gst_element_get_static_pad (priv->audio_input, "src");
2617 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
2618 g_warning ("Could not get source pad from audio source");
2622 if (GST_PAD_LINK_FAILED (gst_pad_link (pad, sink)))
2624 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
2625 g_warning ("Could not link audio source to farsight");
2629 if (gst_element_set_state (priv->audio_input, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2631 g_warning ("Could not start audio source");
2632 gst_element_set_state (priv->audio_input, GST_STATE_NULL);
2633 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
2639 case TP_MEDIA_STREAM_TYPE_VIDEO:
2640 if (priv->video_input != NULL)
2642 if (priv->video_tee != NULL)
2644 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
2645 if (GST_PAD_LINK_FAILED (gst_pad_link (pad, sink)))
2647 g_warning ("Could not link videp soure input pipeline");
2650 gst_object_unref (pad);
2657 g_assert_not_reached ();
2664 empathy_streamed_media_window_remove_video_input (EmpathyStreamedMediaWindow *self)
2666 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2667 GstElement *preview;
2669 disable_camera (self);
2671 DEBUG ("remove video input");
2672 preview = empathy_video_widget_get_element (
2673 EMPATHY_VIDEO_WIDGET (priv->video_preview));
2675 gst_element_set_state (priv->video_input, GST_STATE_NULL);
2676 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
2677 gst_element_set_state (preview, GST_STATE_NULL);
2679 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
2680 priv->video_tee, preview, NULL);
2682 g_object_unref (priv->video_input);
2683 priv->video_input = NULL;
2684 g_object_unref (priv->video_tee);
2685 priv->video_tee = NULL;
2686 gtk_widget_destroy (priv->video_preview);
2687 priv->video_preview = NULL;
2689 gtk_widget_set_sensitive (priv->tool_button_camera_on, FALSE);
2690 gtk_action_set_sensitive (priv->action_camera_on, FALSE);
2691 gtk_widget_set_sensitive (priv->tool_button_camera_preview, FALSE);
2695 start_call (EmpathyStreamedMediaWindow *self)
2697 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2699 priv->call_started = TRUE;
2700 empathy_streamed_media_handler_start_call (priv->handler,
2701 gtk_get_current_event_time ());
2703 if (empathy_streamed_media_handler_has_initial_video (priv->handler))
2705 /* Enable 'send video' buttons and display the preview */
2706 gtk_toggle_tool_button_set_active (
2707 GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_on), TRUE);
2712 empathy_streamed_media_window_bus_message (GstBus *bus, GstMessage *message,
2715 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
2716 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2719 empathy_streamed_media_handler_bus_message (priv->handler, bus, message);
2721 switch (GST_MESSAGE_TYPE (message))
2723 case GST_MESSAGE_STATE_CHANGED:
2724 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
2726 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
2727 if (newstate == GST_STATE_PAUSED)
2728 empathy_streamed_media_window_setup_video_input (self);
2730 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
2731 !priv->call_started)
2733 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
2734 if (newstate == GST_STATE_PAUSED)
2736 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2737 priv->pipeline_playing = TRUE;
2739 if (priv->start_call_when_playing)
2744 case GST_MESSAGE_ERROR:
2746 GError *error = NULL;
2747 GstElement *gst_error;
2750 gst_message_parse_error (message, &error, &debug);
2751 gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
2753 g_message ("Element error: %s -- %s\n", error->message, debug);
2755 if (g_str_has_prefix (gst_element_get_name (gst_error),
2756 VIDEO_INPUT_ERROR_PREFIX))
2758 /* Remove the video input and continue */
2759 if (priv->video_input != NULL)
2760 empathy_streamed_media_window_remove_video_input (self);
2761 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2765 empathy_streamed_media_window_disconnected (self, TRUE);
2767 g_error_free (error);
2770 case GST_MESSAGE_UNKNOWN:
2771 case GST_MESSAGE_EOS:
2772 case GST_MESSAGE_WARNING:
2773 case GST_MESSAGE_INFO:
2774 case GST_MESSAGE_TAG:
2775 case GST_MESSAGE_BUFFERING:
2776 case GST_MESSAGE_STATE_DIRTY:
2777 case GST_MESSAGE_STEP_DONE:
2778 case GST_MESSAGE_CLOCK_PROVIDE:
2779 case GST_MESSAGE_CLOCK_LOST:
2780 case GST_MESSAGE_NEW_CLOCK:
2781 case GST_MESSAGE_STRUCTURE_CHANGE:
2782 case GST_MESSAGE_STREAM_STATUS:
2783 case GST_MESSAGE_APPLICATION:
2784 case GST_MESSAGE_ELEMENT:
2785 case GST_MESSAGE_SEGMENT_START:
2786 case GST_MESSAGE_SEGMENT_DONE:
2787 case GST_MESSAGE_DURATION:
2788 case GST_MESSAGE_ANY:
2797 empathy_streamed_media_window_update_avatars_visibility (EmpathyTpStreamedMedia *call,
2798 EmpathyStreamedMediaWindow *window)
2800 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2802 if (empathy_tp_streamed_media_is_receiving_video (call))
2804 gtk_widget_hide (priv->remote_user_avatar_widget);
2805 gtk_widget_show (priv->video_output);
2809 gtk_widget_hide (priv->video_output);
2810 gtk_widget_show (priv->remote_user_avatar_widget);
2815 call_handler_notify_tp_streamed_media_cb (EmpathyStreamedMediaHandler *handler,
2817 EmpathyStreamedMediaWindow *self)
2819 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2820 EmpathyTpStreamedMedia *call;
2822 g_object_get (priv->handler, "tp-call", &call, NULL);
2826 tp_g_signal_connect_object (call, "audio-stream-error",
2827 G_CALLBACK (empathy_streamed_media_window_audio_stream_error), self, 0);
2828 tp_g_signal_connect_object (call, "video-stream-error",
2829 G_CALLBACK (empathy_streamed_media_window_video_stream_error), self, 0);
2831 g_object_unref (call);
2835 empathy_streamed_media_window_realized_cb (GtkWidget *widget, EmpathyStreamedMediaWindow *window)
2837 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2838 EmpathyTpStreamedMedia *call;
2840 g_signal_connect (priv->handler, "conference-added",
2841 G_CALLBACK (empathy_streamed_media_window_conference_added_cb), window);
2842 g_signal_connect (priv->handler, "request-resource",
2843 G_CALLBACK (empathy_streamed_media_window_request_resource_cb), window);
2844 g_signal_connect (priv->handler, "closed",
2845 G_CALLBACK (empathy_streamed_media_window_channel_closed_cb), window);
2846 g_signal_connect (priv->handler, "src-pad-added",
2847 G_CALLBACK (empathy_streamed_media_window_src_added_cb), window);
2848 g_signal_connect (priv->handler, "sink-pad-added",
2849 G_CALLBACK (empathy_streamed_media_window_sink_added_cb), window);
2850 g_signal_connect (priv->handler, "stream-closed",
2851 G_CALLBACK (empathy_streamed_media_window_channel_stream_closed_cb), window);
2853 g_object_get (priv->handler, "tp-call", &call, NULL);
2856 tp_g_signal_connect_object (call, "audio-stream-error",
2857 G_CALLBACK (empathy_streamed_media_window_audio_stream_error), window,
2859 tp_g_signal_connect_object (call, "video-stream-error",
2860 G_CALLBACK (empathy_streamed_media_window_video_stream_error), window,
2863 g_object_unref (call);
2867 /* tp-call doesn't exist yet, we'll connect signals once it has been
2869 g_signal_connect (priv->handler, "notify::tp-call",
2870 G_CALLBACK (call_handler_notify_tp_streamed_media_cb), window);
2873 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
2877 empathy_streamed_media_window_delete_cb (GtkWidget *widget, GdkEvent*event,
2878 EmpathyStreamedMediaWindow *window)
2880 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2882 if (priv->pipeline != NULL)
2884 if (priv->bus_message_source_id != 0)
2886 g_source_remove (priv->bus_message_source_id);
2887 priv->bus_message_source_id = 0;
2890 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
2893 if (priv->call_state == CONNECTING)
2894 empathy_sound_manager_stop (priv->sound_mgr, EMPATHY_SOUND_PHONE_OUTGOING);
2900 show_controls (EmpathyStreamedMediaWindow *window, gboolean set_fullscreen)
2903 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2905 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2910 gtk_widget_hide (priv->sidebar);
2911 gtk_widget_hide (menu);
2912 gtk_widget_hide (priv->vbox);
2913 gtk_widget_hide (priv->statusbar);
2914 gtk_widget_hide (priv->toolbar);
2918 if (priv->sidebar_was_visible_before_fs)
2919 gtk_widget_show (priv->sidebar);
2921 gtk_widget_show (menu);
2922 gtk_widget_show (priv->vbox);
2923 gtk_widget_show (priv->statusbar);
2924 gtk_widget_show (priv->toolbar);
2926 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
2927 priv->original_height_before_fs);
2932 show_borders (EmpathyStreamedMediaWindow *window, gboolean set_fullscreen)
2934 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2936 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
2937 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
2938 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
2939 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
2941 if (priv->video_output != NULL)
2943 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2944 priv->video_output, TRUE, TRUE,
2945 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2949 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2950 priv->vbox, TRUE, TRUE,
2951 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2956 empathy_streamed_media_window_state_event_cb (GtkWidget *widget,
2957 GdkEventWindowState *event, EmpathyStreamedMediaWindow *window)
2959 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
2961 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2962 gboolean set_fullscreen = event->new_window_state &
2963 GDK_WINDOW_STATE_FULLSCREEN;
2967 gboolean sidebar_was_visible;
2968 GtkAllocation allocation;
2969 gint original_width, original_height;
2971 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2972 original_width = allocation.width;
2973 original_height = allocation.height;
2975 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
2977 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
2978 priv->original_width_before_fs = original_width;
2979 priv->original_height_before_fs = original_height;
2981 if (priv->video_output_motion_handler_id == 0 &&
2982 priv->video_output != NULL)
2984 priv->video_output_motion_handler_id = g_signal_connect (
2985 G_OBJECT (priv->video_output), "motion-notify-event",
2986 G_CALLBACK (empathy_streamed_media_window_video_output_motion_notify),
2992 disconnect_video_output_motion_handler (window);
2995 empathy_streamed_media_window_fullscreen_set_fullscreen (priv->fullscreen,
2997 show_controls (window, set_fullscreen);
2998 show_borders (window, set_fullscreen);
2999 gtk_action_set_stock_id (priv->menu_fullscreen,
3000 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
3001 priv->is_fullscreen = set_fullscreen;
3008 empathy_streamed_media_window_sidebar_toggled_cb (GtkToggleButton *toggle,
3009 EmpathyStreamedMediaWindow *window)
3011 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3013 int w, h, handle_size;
3014 GtkAllocation allocation, sidebar_allocation;
3016 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
3017 w = allocation.width;
3018 h = allocation.height;
3020 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
3022 gtk_widget_get_allocation (priv->sidebar, &sidebar_allocation);
3023 if (gtk_toggle_button_get_active (toggle))
3025 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
3026 gtk_widget_show (priv->sidebar);
3027 w += sidebar_allocation.width + handle_size;
3031 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
3032 w -= sidebar_allocation.width + handle_size;
3033 gtk_widget_hide (priv->sidebar);
3036 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
3039 gtk_window_resize (GTK_WINDOW (window), w, h);
3043 empathy_streamed_media_window_set_send_video (EmpathyStreamedMediaWindow *window,
3046 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3047 EmpathyTpStreamedMedia *call;
3049 priv->sending_video = (state == CAMERA_STATE_ON);
3051 if (state == CAMERA_STATE_PREVIEW ||
3052 state == CAMERA_STATE_ON)
3054 /* When we start sending video, we want to show the video preview by
3056 display_video_preview (window, TRUE);
3060 display_video_preview (window, FALSE);
3063 if (priv->call_state != CONNECTED)
3066 g_object_get (priv->handler, "tp-call", &call, NULL);
3067 DEBUG ("%s sending video", priv->sending_video ? "start": "stop");
3068 empathy_tp_streamed_media_request_video_stream_direction (call, priv->sending_video);
3069 g_object_unref (call);
3073 empathy_streamed_media_window_mic_toggled_cb (GtkToggleToolButton *toggle,
3074 EmpathyStreamedMediaWindow *window)
3076 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3079 active = (gtk_toggle_tool_button_get_active (toggle));
3083 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
3085 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
3089 /* TODO, Instead of setting the input volume to 0 we should probably
3090 * stop sending but this would cause the audio call to drop if both
3091 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
3092 * in the future. GNOME #574574
3094 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
3096 gtk_adjustment_set_value (priv->audio_input_adj, 0);
3101 empathy_streamed_media_window_sidebar_hidden_cb (EvSidebar *sidebar,
3102 EmpathyStreamedMediaWindow *window)
3104 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3106 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
3111 empathy_streamed_media_window_sidebar_shown_cb (EvSidebar *sidebar,
3112 EmpathyStreamedMediaWindow *window)
3114 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3116 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
3121 empathy_streamed_media_window_hangup_cb (gpointer object,
3122 EmpathyStreamedMediaWindow *window)
3124 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3126 empathy_streamed_media_handler_stop_call (priv->handler);
3128 if (empathy_streamed_media_window_disconnected (window, FALSE))
3129 gtk_widget_destroy (GTK_WIDGET (window));
3133 empathy_streamed_media_window_restart_call (EmpathyStreamedMediaWindow *window)
3135 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3137 /* Remove error info bars */
3138 gtk_container_forall (GTK_CONTAINER (priv->errors_vbox),
3139 (GtkCallback) gtk_widget_destroy, NULL);
3141 create_video_output_widget (window);
3143 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
3144 G_CALLBACK (empathy_streamed_media_window_mic_volume_changed_cb), window);
3146 /* While the call was disconnected, the input volume might have changed.
3147 * However, since the audio_input source was destroyed, its volume has not
3148 * been updated during that time. That's why we manually update it here */
3149 empathy_streamed_media_window_mic_volume_changed_cb (priv->audio_input_adj, window);
3151 priv->outgoing = TRUE;
3152 empathy_streamed_media_window_set_state_connecting (window);
3154 if (priv->pipeline_playing)
3155 start_call (window);
3157 /* call will be started when the pipeline is ready */
3158 priv->start_call_when_playing = TRUE;
3161 empathy_streamed_media_window_setup_avatars (window, priv->handler);
3163 gtk_action_set_sensitive (priv->redial, FALSE);
3164 gtk_widget_set_sensitive (priv->redial_button, FALSE);
3168 empathy_streamed_media_window_redial_cb (gpointer object,
3169 EmpathyStreamedMediaWindow *window)
3171 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3173 if (priv->call_state == CONNECTED)
3174 priv->call_state = REDIALING;
3176 empathy_streamed_media_handler_stop_call (priv->handler);
3178 if (priv->call_state != CONNECTED)
3179 empathy_streamed_media_window_restart_call (window);
3183 empathy_streamed_media_window_fullscreen_cb (gpointer object,
3184 EmpathyStreamedMediaWindow *window)
3186 empathy_streamed_media_window_fullscreen_toggle (window);
3190 empathy_streamed_media_window_fullscreen_toggle (EmpathyStreamedMediaWindow *window)
3192 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3194 if (priv->is_fullscreen)
3195 gtk_window_unfullscreen (GTK_WINDOW (window));
3197 gtk_window_fullscreen (GTK_WINDOW (window));
3201 empathy_streamed_media_window_video_button_press_cb (GtkWidget *video_output,
3202 GdkEventButton *event, EmpathyStreamedMediaWindow *window)
3204 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
3206 empathy_streamed_media_window_video_menu_popup (window, event->button);
3214 empathy_streamed_media_window_key_press_cb (GtkWidget *video_output,
3215 GdkEventKey *event, EmpathyStreamedMediaWindow *window)
3217 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3219 if (priv->is_fullscreen && event->keyval == GDK_KEY_Escape)
3221 /* Since we are in fullscreen mode, toggling will bring us back to
3223 empathy_streamed_media_window_fullscreen_toggle (window);
3231 empathy_streamed_media_window_video_output_motion_notify (GtkWidget *widget,
3232 GdkEventMotion *event, EmpathyStreamedMediaWindow *window)
3234 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3236 if (priv->is_fullscreen)
3238 empathy_streamed_media_window_fullscreen_show_popup (priv->fullscreen);
3245 empathy_streamed_media_window_video_menu_popup (EmpathyStreamedMediaWindow *window,
3249 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3251 menu = gtk_ui_manager_get_widget (priv->ui_manager,
3253 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
3254 button, gtk_get_current_event_time ());
3255 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
3259 empathy_streamed_media_window_status_message (EmpathyStreamedMediaWindow *window,
3262 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3264 if (priv->context_id == 0)
3266 priv->context_id = gtk_statusbar_get_context_id (
3267 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
3271 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
3274 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
3279 empathy_streamed_media_window_volume_changed_cb (GtkScaleButton *button,
3280 gdouble value, EmpathyStreamedMediaWindow *window)
3282 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3284 if (priv->audio_output == NULL)
3287 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),
3291 /* block all the signals related to camera control widgets. This is useful
3292 * when we are manually updating the UI and so don't want to fire the
3295 block_camera_control_signals (EmpathyStreamedMediaWindow *self)
3297 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
3299 g_signal_handlers_block_by_func (priv->tool_button_camera_off,
3300 tool_button_camera_off_toggled_cb, self);
3301 g_signal_handlers_block_by_func (priv->tool_button_camera_preview,
3302 tool_button_camera_preview_toggled_cb, self);
3303 g_signal_handlers_block_by_func (priv->tool_button_camera_on,
3304 tool_button_camera_on_toggled_cb, self);
3305 g_signal_handlers_block_by_func (priv->action_camera_on,
3306 action_camera_change_cb, self);
3310 unblock_camera_control_signals (EmpathyStreamedMediaWindow *self)
3312 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
3314 g_signal_handlers_unblock_by_func (priv->tool_button_camera_off,
3315 tool_button_camera_off_toggled_cb, self);
3316 g_signal_handlers_unblock_by_func (priv->tool_button_camera_preview,
3317 tool_button_camera_preview_toggled_cb, self);
3318 g_signal_handlers_unblock_by_func (priv->tool_button_camera_on,
3319 tool_button_camera_on_toggled_cb, self);
3320 g_signal_handlers_unblock_by_func (priv->action_camera_on,
3321 action_camera_change_cb, self);