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;
1043 filename = empathy_file_lookup ("empathy-call-window.ui", "src");
1044 gui = empathy_builder_get_file (filename,
1045 "call_window_vbox", &top_vbox,
1046 "errors_vbox", &priv->errors_vbox,
1047 "pane", &priv->pane,
1048 "statusbar", &priv->statusbar,
1049 "redial", &priv->redial_button,
1050 "microphone", &priv->mic_button,
1051 "toolbar", &priv->toolbar,
1052 "menuredial", &priv->redial,
1053 "ui_manager", &priv->ui_manager,
1054 "menufullscreen", &priv->menu_fullscreen,
1055 "camera_off", &priv->tool_button_camera_off,
1056 "camera_preview", &priv->tool_button_camera_preview,
1057 "camera_on", &priv->tool_button_camera_on,
1058 "action_camera_on", &priv->action_camera_on,
1059 "details_vbox", &priv->details_vbox,
1060 "vcodec_encoding_label", &priv->vcodec_encoding_label,
1061 "acodec_encoding_label", &priv->acodec_encoding_label,
1062 "acodec_decoding_label", &priv->acodec_decoding_label,
1063 "vcodec_decoding_label", &priv->vcodec_decoding_label,
1064 "audio_remote_candidate_label", &priv->audio_remote_candidate_label,
1065 "audio_local_candidate_label", &priv->audio_local_candidate_label,
1066 "video_remote_candidate_label", &priv->video_remote_candidate_label,
1067 "video_local_candidate_label", &priv->video_local_candidate_label,
1068 "video_remote_candidate_info_img", &priv->video_remote_candidate_info_img,
1069 "video_local_candidate_info_img", &priv->video_local_candidate_info_img,
1070 "audio_remote_candidate_info_img", &priv->audio_remote_candidate_info_img,
1071 "audio_local_candidate_info_img", &priv->audio_local_candidate_info_img,
1075 empathy_builder_connect (gui, self,
1076 "menuhangup", "activate", empathy_streamed_media_window_hangup_cb,
1077 "hangup", "clicked", empathy_streamed_media_window_hangup_cb,
1078 "menuredial", "activate", empathy_streamed_media_window_redial_cb,
1079 "redial", "clicked", empathy_streamed_media_window_redial_cb,
1080 "microphone", "toggled", empathy_streamed_media_window_mic_toggled_cb,
1081 "menufullscreen", "activate", empathy_streamed_media_window_fullscreen_cb,
1082 "camera_off", "toggled", tool_button_camera_off_toggled_cb,
1083 "camera_preview", "toggled", tool_button_camera_preview_toggled_cb,
1084 "camera_on", "toggled", tool_button_camera_on_toggled_cb,
1085 "action_camera_on", "changed", action_camera_change_cb,
1088 gtk_action_set_sensitive (priv->menu_fullscreen, FALSE);
1090 priv->lock = g_mutex_new ();
1092 gtk_container_add (GTK_CONTAINER (self), top_vbox);
1094 priv->content_hbox = gtk_hbox_new (FALSE, CONTENT_HBOX_SPACING);
1095 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
1096 CONTENT_HBOX_BORDER_WIDTH);
1097 gtk_paned_pack1 (GTK_PANED (priv->pane), priv->content_hbox, TRUE, FALSE);
1099 /* remote user output frame */
1100 priv->remote_user_output_frame = gtk_frame_new (NULL);
1101 gtk_widget_set_size_request (priv->remote_user_output_frame,
1102 EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT);
1103 gtk_box_pack_start (GTK_BOX (priv->content_hbox),
1104 priv->remote_user_output_frame, TRUE, TRUE,
1105 CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1107 priv->remote_user_output_hbox = gtk_hbox_new (FALSE, 0);
1109 priv->remote_user_avatar_widget = gtk_image_new ();
1111 gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
1112 priv->remote_user_avatar_widget, TRUE, TRUE, 0);
1114 gtk_container_add (GTK_CONTAINER (priv->remote_user_output_frame),
1115 priv->remote_user_output_hbox);
1117 /* self user output frame */
1118 priv->self_user_output_frame = gtk_frame_new (NULL);
1119 gtk_widget_set_size_request (priv->self_user_output_frame,
1120 SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
1122 priv->self_user_output_hbox = gtk_hbox_new (FALSE, 0);
1124 priv->self_user_avatar_widget = gtk_image_new ();
1125 gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
1126 priv->self_user_avatar_widget, TRUE, TRUE, 0);
1128 gtk_container_add (GTK_CONTAINER (priv->self_user_output_frame),
1129 priv->self_user_output_hbox);
1131 create_pipeline (self);
1132 create_video_output_widget (self);
1133 create_audio_input (self);
1134 create_audio_output (self);
1135 create_video_input (self);
1137 priv->fsnotifier = fs_element_added_notifier_new ();
1138 fs_element_added_notifier_add (priv->fsnotifier, GST_BIN (priv->pipeline));
1140 /* The call will be started as soon the pipeline is playing */
1141 priv->start_call_when_playing = TRUE;
1143 keyfile = g_key_file_new ();
1144 filename = empathy_file_lookup ("element-properties", "data");
1145 if (g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error))
1147 fs_element_added_notifier_set_properties_from_keyfile (priv->fsnotifier,
1152 g_warning ("Could not load element-properties file: %s", error->message);
1153 g_key_file_free (keyfile);
1154 g_clear_error (&error);
1158 priv->vbox = gtk_vbox_new (FALSE, 3);
1159 gtk_box_pack_start (GTK_BOX (priv->content_hbox), priv->vbox,
1160 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
1161 gtk_box_pack_start (GTK_BOX (priv->vbox), priv->self_user_output_frame,
1164 empathy_streamed_media_window_setup_toolbar (self);
1166 priv->sidebar_button = gtk_toggle_button_new_with_mnemonic (_("_Sidebar"));
1167 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
1168 g_signal_connect (G_OBJECT (priv->sidebar_button), "toggled",
1169 G_CALLBACK (empathy_streamed_media_window_sidebar_toggled_cb), self);
1171 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
1173 h = gtk_hbox_new (FALSE, 3);
1174 gtk_box_pack_end (GTK_BOX (priv->vbox), h, FALSE, FALSE, 3);
1175 gtk_box_pack_end (GTK_BOX (h), priv->sidebar_button, FALSE, FALSE, 3);
1177 priv->sidebar = ev_sidebar_new ();
1178 g_signal_connect (G_OBJECT (priv->sidebar),
1179 "hide", G_CALLBACK (empathy_streamed_media_window_sidebar_hidden_cb), self);
1180 g_signal_connect (G_OBJECT (priv->sidebar),
1181 "show", G_CALLBACK (empathy_streamed_media_window_sidebar_shown_cb), self);
1182 gtk_paned_pack2 (GTK_PANED (priv->pane), priv->sidebar, FALSE, FALSE);
1184 page = empathy_streamed_media_window_create_audio_input (self);
1185 ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "audio-input",
1186 _("Audio input"), page);
1188 page = empathy_streamed_media_window_create_video_input (self);
1189 ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "video-input",
1190 _("Video input"), page);
1192 priv->dtmf_panel = empathy_streamed_media_window_create_dtmf (self);
1193 ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "dialpad",
1194 _("Dialpad"), priv->dtmf_panel);
1196 gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
1198 /* Put the details vbox in a scroll window as it can require a lot of
1199 * horizontal space. */
1200 scroll = gtk_scrolled_window_new (NULL, NULL);
1201 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scroll),
1202 priv->details_vbox);
1204 ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "details",
1205 _("Details"), scroll);
1207 gtk_widget_show_all (top_vbox);
1209 gtk_widget_hide (priv->sidebar);
1211 priv->fullscreen = empathy_streamed_media_window_fullscreen_new (self);
1212 empathy_streamed_media_window_fullscreen_set_video_widget (priv->fullscreen,
1213 priv->video_output);
1214 g_signal_connect (G_OBJECT (priv->fullscreen->leave_fullscreen_button),
1215 "clicked", G_CALLBACK (empathy_streamed_media_window_fullscreen_cb), self);
1217 g_signal_connect (G_OBJECT (self), "realize",
1218 G_CALLBACK (empathy_streamed_media_window_realized_cb), self);
1220 g_signal_connect (G_OBJECT (self), "delete-event",
1221 G_CALLBACK (empathy_streamed_media_window_delete_cb), self);
1223 g_signal_connect (G_OBJECT (self), "window-state-event",
1224 G_CALLBACK (empathy_streamed_media_window_state_event_cb), self);
1226 g_signal_connect (G_OBJECT (self), "key-press-event",
1227 G_CALLBACK (empathy_streamed_media_window_key_press_cb), self);
1229 priv->timer = g_timer_new ();
1231 g_object_ref (priv->ui_manager);
1232 g_object_unref (gui);
1234 priv->sound_mgr = empathy_sound_manager_dup_singleton ();
1236 empathy_geometry_bind (GTK_WINDOW (self), "call-window");
1239 /* Instead of specifying a width and a height, we specify only one size. That's
1240 because we want a square avatar icon. */
1242 init_contact_avatar_with_size (EmpathyContact *contact,
1243 GtkWidget *image_widget,
1246 GdkPixbuf *pixbuf_avatar = NULL;
1248 if (contact != NULL)
1250 pixbuf_avatar = empathy_pixbuf_avatar_from_contact_scaled (contact,
1254 if (pixbuf_avatar == NULL)
1256 pixbuf_avatar = empathy_pixbuf_from_icon_name_sized (
1257 EMPATHY_IMAGE_AVATAR_DEFAULT, size);
1260 gtk_image_set_from_pixbuf (GTK_IMAGE (image_widget), pixbuf_avatar);
1262 if (pixbuf_avatar != NULL)
1263 g_object_unref (pixbuf_avatar);
1267 set_window_title (EmpathyStreamedMediaWindow *self)
1269 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1272 /* translators: Call is a noun and %s is the contact name. This string
1273 * is used in the window title */
1274 tmp = g_strdup_printf (_("Call with %s"),
1275 empathy_contact_get_alias (priv->contact));
1276 gtk_window_set_title (GTK_WINDOW (self), tmp);
1281 contact_name_changed_cb (EmpathyContact *contact,
1282 GParamSpec *pspec, EmpathyStreamedMediaWindow *self)
1284 set_window_title (self);
1288 contact_avatar_changed_cb (EmpathyContact *contact,
1289 GParamSpec *pspec, GtkWidget *avatar_widget)
1292 GtkAllocation allocation;
1294 gtk_widget_get_allocation (avatar_widget, &allocation);
1295 size = allocation.height;
1299 /* the widget is not allocated yet, set a default size */
1300 size = MIN (REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT,
1301 REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH);
1304 init_contact_avatar_with_size (contact, avatar_widget, size);
1308 empathy_streamed_media_window_got_self_contact_cb (TpConnection *connection,
1309 EmpathyContact *contact, const GError *error, gpointer user_data,
1310 GObject *weak_object)
1312 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
1313 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1315 init_contact_avatar_with_size (contact, priv->self_user_avatar_widget,
1316 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
1318 g_signal_connect (contact, "notify::avatar",
1319 G_CALLBACK (contact_avatar_changed_cb), priv->self_user_avatar_widget);
1323 empathy_streamed_media_window_setup_avatars (EmpathyStreamedMediaWindow *self,
1324 EmpathyStreamedMediaHandler *handler)
1326 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1328 g_object_get (handler, "contact", &(priv->contact), NULL);
1330 if (priv->contact != NULL)
1332 TpConnection *connection;
1334 set_window_title (self);
1336 g_signal_connect (priv->contact, "notify::name",
1337 G_CALLBACK (contact_name_changed_cb), self);
1338 g_signal_connect (priv->contact, "notify::avatar",
1339 G_CALLBACK (contact_avatar_changed_cb),
1340 priv->remote_user_avatar_widget);
1342 /* Retreiving the self avatar */
1343 connection = empathy_contact_get_connection (priv->contact);
1344 empathy_tp_contact_factory_get_from_handle (connection,
1345 tp_connection_get_self_handle (connection),
1346 empathy_streamed_media_window_got_self_contact_cb, self, NULL, G_OBJECT (self));
1350 g_warning ("call handler doesn't have a contact");
1351 /* translators: Call is a noun. This string is used in the window
1353 gtk_window_set_title (GTK_WINDOW (self), _("Call"));
1355 /* Since we can't access the remote contact, we can't get a connection
1356 to it and can't get the self contact (and its avatar). This means
1357 that we have to manually set the self avatar. */
1358 init_contact_avatar_with_size (NULL, priv->self_user_avatar_widget,
1359 MIN (SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH));
1362 init_contact_avatar_with_size (priv->contact,
1363 priv->remote_user_avatar_widget,
1364 MIN (REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH,
1365 REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT));
1367 /* The remote avatar is shown by default and will be hidden when we receive
1368 video from the remote side. */
1369 gtk_widget_hide (priv->video_output);
1370 gtk_widget_show (priv->remote_user_avatar_widget);
1374 update_send_codec (EmpathyStreamedMediaWindow *self,
1377 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1384 codec = empathy_streamed_media_handler_get_send_audio_codec (priv->handler);
1385 widget = priv->acodec_encoding_label;
1389 codec = empathy_streamed_media_handler_get_send_video_codec (priv->handler);
1390 widget = priv->vcodec_encoding_label;
1396 tmp = g_strdup_printf ("%s/%u", codec->encoding_name, codec->clock_rate);
1397 gtk_label_set_text (GTK_LABEL (widget), tmp);
1402 send_audio_codec_notify_cb (GObject *object,
1406 EmpathyStreamedMediaWindow *self = user_data;
1408 update_send_codec (self, TRUE);
1412 send_video_codec_notify_cb (GObject *object,
1416 EmpathyStreamedMediaWindow *self = user_data;
1418 update_send_codec (self, FALSE);
1422 update_recv_codec (EmpathyStreamedMediaWindow *self,
1425 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1428 GString *str = NULL;
1432 codecs = empathy_streamed_media_handler_get_recv_audio_codecs (priv->handler);
1433 widget = priv->acodec_decoding_label;
1437 codecs = empathy_streamed_media_handler_get_recv_video_codecs (priv->handler);
1438 widget = priv->vcodec_decoding_label;
1444 for (l = codecs; l != NULL; l = g_list_next (l))
1446 FsCodec *codec = l->data;
1449 str = g_string_new (NULL);
1451 g_string_append (str, ", ");
1453 g_string_append_printf (str, "%s/%u", codec->encoding_name,
1457 gtk_label_set_text (GTK_LABEL (widget), str->str);
1458 g_string_free (str, TRUE);
1462 recv_audio_codecs_notify_cb (GObject *object,
1466 EmpathyStreamedMediaWindow *self = user_data;
1468 update_recv_codec (self, TRUE);
1472 recv_video_codecs_notify_cb (GObject *object,
1476 EmpathyStreamedMediaWindow *self = user_data;
1478 update_recv_codec (self, FALSE);
1481 static const gchar *
1482 candidate_type_to_str (FsCandidate *candidate)
1484 switch (candidate->type)
1486 case FS_CANDIDATE_TYPE_HOST:
1488 case FS_CANDIDATE_TYPE_SRFLX:
1489 return "server reflexive";
1490 case FS_CANDIDATE_TYPE_PRFLX:
1491 return "peer reflexive";
1492 case FS_CANDIDATE_TYPE_RELAY:
1494 case FS_CANDIDATE_TYPE_MULTICAST:
1501 static const gchar *
1502 candidate_type_to_desc (FsCandidate *candidate)
1504 switch (candidate->type)
1506 case FS_CANDIDATE_TYPE_HOST:
1507 return _("The IP address as seen by the machine");
1508 case FS_CANDIDATE_TYPE_SRFLX:
1509 return _("The IP address as seen by a server on the Internet");
1510 case FS_CANDIDATE_TYPE_PRFLX:
1511 return _("The IP address of the peer as seen by the other side");
1512 case FS_CANDIDATE_TYPE_RELAY:
1513 return _("The IP address of a relay server");
1514 case FS_CANDIDATE_TYPE_MULTICAST:
1515 return _("The IP address of the multicast group");
1522 update_candidat_widget (EmpathyStreamedMediaWindow *self,
1525 FsCandidate *candidate)
1529 g_assert (candidate != NULL);
1530 str = g_strdup_printf ("%s %u (%s)", candidate->ip,
1531 candidate->port, candidate_type_to_str (candidate));
1533 gtk_label_set_text (GTK_LABEL (label), str);
1534 gtk_widget_set_tooltip_text (img, candidate_type_to_desc (candidate));
1540 candidates_changed_cb (GObject *object,
1542 EmpathyStreamedMediaWindow *self)
1544 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1545 FsCandidate *candidate = NULL;
1547 if (type == FS_MEDIA_TYPE_VIDEO)
1549 /* Update remote candidate */
1550 candidate = empathy_streamed_media_handler_get_video_remote_candidate (
1553 update_candidat_widget (self, priv->video_remote_candidate_label,
1554 priv->video_remote_candidate_info_img, candidate);
1556 /* Update local candidate */
1557 candidate = empathy_streamed_media_handler_get_video_local_candidate (
1560 update_candidat_widget (self, priv->video_local_candidate_label,
1561 priv->video_local_candidate_info_img, candidate);
1565 /* Update remote candidate */
1566 candidate = empathy_streamed_media_handler_get_audio_remote_candidate (
1569 update_candidat_widget (self, priv->audio_remote_candidate_label,
1570 priv->audio_remote_candidate_info_img, candidate);
1572 /* Update local candidate */
1573 candidate = empathy_streamed_media_handler_get_audio_local_candidate (
1576 update_candidat_widget (self, priv->audio_local_candidate_label,
1577 priv->audio_local_candidate_info_img, candidate);
1582 empathy_streamed_media_window_constructed (GObject *object)
1584 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (object);
1585 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1586 EmpathyTpStreamedMedia *call;
1588 g_assert (priv->handler != NULL);
1590 g_object_get (priv->handler, "tp-call", &call, NULL);
1591 priv->outgoing = (call == NULL);
1593 g_object_unref (call);
1595 empathy_streamed_media_window_setup_avatars (self, priv->handler);
1596 empathy_streamed_media_window_set_state_connecting (self);
1598 if (!empathy_streamed_media_handler_has_initial_video (priv->handler))
1600 gtk_toggle_tool_button_set_active (
1601 GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_off), TRUE);
1603 /* If call has InitialVideo, the preview will be started once the call has
1604 * been started (start_call()). */
1606 update_send_codec (self, TRUE);
1607 update_send_codec (self, FALSE);
1608 update_recv_codec (self, TRUE);
1609 update_recv_codec (self, FALSE);
1611 tp_g_signal_connect_object (priv->handler, "notify::send-audio-codec",
1612 G_CALLBACK (send_audio_codec_notify_cb), self, 0);
1613 tp_g_signal_connect_object (priv->handler, "notify::send-video-codec",
1614 G_CALLBACK (send_video_codec_notify_cb), self, 0);
1615 tp_g_signal_connect_object (priv->handler, "notify::recv-audio-codecs",
1616 G_CALLBACK (recv_audio_codecs_notify_cb), self, 0);
1617 tp_g_signal_connect_object (priv->handler, "notify::recv-video-codecs",
1618 G_CALLBACK (recv_video_codecs_notify_cb), self, 0);
1620 tp_g_signal_connect_object (priv->handler, "candidates-changed",
1621 G_CALLBACK (candidates_changed_cb), self, 0);
1624 static void empathy_streamed_media_window_dispose (GObject *object);
1625 static void empathy_streamed_media_window_finalize (GObject *object);
1628 empathy_streamed_media_window_set_property (GObject *object,
1629 guint property_id, const GValue *value, GParamSpec *pspec)
1631 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (object);
1633 switch (property_id)
1635 case PROP_STREAMED_MEDIA_HANDLER:
1636 priv->handler = g_value_dup_object (value);
1639 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1644 empathy_streamed_media_window_get_property (GObject *object,
1645 guint property_id, GValue *value, GParamSpec *pspec)
1647 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (object);
1649 switch (property_id)
1651 case PROP_STREAMED_MEDIA_HANDLER:
1652 g_value_set_object (value, priv->handler);
1655 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1660 empathy_streamed_media_window_class_init (
1661 EmpathyStreamedMediaWindowClass *empathy_streamed_media_window_class)
1663 GObjectClass *object_class = G_OBJECT_CLASS (empathy_streamed_media_window_class);
1664 GParamSpec *param_spec;
1666 g_type_class_add_private (empathy_streamed_media_window_class,
1667 sizeof (EmpathyStreamedMediaWindowPriv));
1669 object_class->constructed = empathy_streamed_media_window_constructed;
1670 object_class->set_property = empathy_streamed_media_window_set_property;
1671 object_class->get_property = empathy_streamed_media_window_get_property;
1673 object_class->dispose = empathy_streamed_media_window_dispose;
1674 object_class->finalize = empathy_streamed_media_window_finalize;
1676 param_spec = g_param_spec_object ("handler",
1677 "handler", "The call handler",
1678 EMPATHY_TYPE_STREAMED_MEDIA_HANDLER,
1679 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
1680 g_object_class_install_property (object_class,
1681 PROP_STREAMED_MEDIA_HANDLER, param_spec);
1685 empathy_streamed_media_window_video_stream_changed_cb (EmpathyTpStreamedMedia *call,
1686 GParamSpec *property, EmpathyStreamedMediaWindow *self)
1688 DEBUG ("video stream changed");
1689 empathy_streamed_media_window_update_avatars_visibility (call, self);
1693 empathy_streamed_media_window_dispose (GObject *object)
1695 EmpathyTpStreamedMedia *call;
1696 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (object);
1697 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1699 if (priv->dispose_has_run)
1702 priv->dispose_has_run = TRUE;
1704 g_object_get (priv->handler, "tp-call", &call, NULL);
1708 g_object_unref (call);
1711 if (priv->handler != NULL)
1713 empathy_streamed_media_handler_stop_call (priv->handler);
1714 g_object_unref (priv->handler);
1716 priv->handler = NULL;
1718 if (priv->bus_message_source_id != 0)
1720 g_source_remove (priv->bus_message_source_id);
1721 priv->bus_message_source_id = 0;
1724 if (priv->pipeline != NULL)
1725 g_object_unref (priv->pipeline);
1726 priv->pipeline = NULL;
1728 if (priv->video_input != NULL)
1729 g_object_unref (priv->video_input);
1730 priv->video_input = NULL;
1732 if (priv->audio_input != NULL)
1733 g_object_unref (priv->audio_input);
1734 priv->audio_input = NULL;
1736 if (priv->audio_output != NULL)
1737 g_object_unref (priv->audio_output);
1738 priv->audio_output = NULL;
1740 if (priv->video_tee != NULL)
1741 g_object_unref (priv->video_tee);
1742 priv->video_tee = NULL;
1744 if (priv->liveadder != NULL)
1745 gst_object_unref (priv->liveadder);
1746 priv->liveadder = NULL;
1748 if (priv->fsnotifier != NULL)
1749 g_object_unref (priv->fsnotifier);
1750 priv->fsnotifier = NULL;
1752 if (priv->timer_id != 0)
1753 g_source_remove (priv->timer_id);
1756 if (priv->ui_manager != NULL)
1757 g_object_unref (priv->ui_manager);
1758 priv->ui_manager = NULL;
1760 if (priv->fullscreen != NULL)
1761 g_object_unref (priv->fullscreen);
1762 priv->fullscreen = NULL;
1764 if (priv->contact != NULL)
1766 g_signal_handlers_disconnect_by_func (priv->contact,
1767 contact_name_changed_cb, self);
1768 g_object_unref (priv->contact);
1769 priv->contact = NULL;
1772 tp_clear_object (&priv->sound_mgr);
1774 /* release any references held by the object here */
1775 if (G_OBJECT_CLASS (empathy_streamed_media_window_parent_class)->dispose)
1776 G_OBJECT_CLASS (empathy_streamed_media_window_parent_class)->dispose (object);
1780 disconnect_video_output_motion_handler (EmpathyStreamedMediaWindow *self)
1782 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1784 if (priv->video_output_motion_handler_id != 0)
1786 g_signal_handler_disconnect (G_OBJECT (priv->video_output),
1787 priv->video_output_motion_handler_id);
1788 priv->video_output_motion_handler_id = 0;
1793 empathy_streamed_media_window_finalize (GObject *object)
1795 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (object);
1796 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1798 disconnect_video_output_motion_handler (self);
1800 /* free any data held directly by the object here */
1801 g_mutex_free (priv->lock);
1803 g_timer_destroy (priv->timer);
1805 G_OBJECT_CLASS (empathy_streamed_media_window_parent_class)->finalize (object);
1809 EmpathyStreamedMediaWindow *
1810 empathy_streamed_media_window_new (EmpathyStreamedMediaHandler *handler)
1812 return EMPATHY_STREAMED_MEDIA_WINDOW (
1813 g_object_new (EMPATHY_TYPE_STREAMED_MEDIA_WINDOW, "handler", handler, NULL));
1817 empathy_streamed_media_window_conference_added_cb (EmpathyStreamedMediaHandler *handler,
1818 GstElement *conference, gpointer user_data)
1820 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
1821 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1823 gst_bin_add (GST_BIN (priv->pipeline), conference);
1825 gst_element_set_state (conference, GST_STATE_PLAYING);
1829 empathy_streamed_media_window_request_resource_cb (EmpathyStreamedMediaHandler *handler,
1830 FsMediaType type, FsStreamDirection direction, gpointer user_data)
1832 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
1833 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1835 if (type != FS_MEDIA_TYPE_VIDEO)
1838 if (direction == FS_DIRECTION_RECV)
1841 /* video and direction is send */
1842 return priv->video_input != NULL;
1846 empathy_streamed_media_window_reset_pipeline (EmpathyStreamedMediaWindow *self)
1848 GstStateChangeReturn state_change_return;
1849 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1851 if (priv->pipeline == NULL)
1854 if (priv->bus_message_source_id != 0)
1856 g_source_remove (priv->bus_message_source_id);
1857 priv->bus_message_source_id = 0;
1860 state_change_return = gst_element_set_state (priv->pipeline, GST_STATE_NULL);
1862 if (state_change_return == GST_STATE_CHANGE_SUCCESS ||
1863 state_change_return == GST_STATE_CHANGE_NO_PREROLL)
1865 if (priv->pipeline != NULL)
1866 g_object_unref (priv->pipeline);
1867 priv->pipeline = NULL;
1869 g_signal_handlers_disconnect_by_func (priv->audio_input_adj,
1870 empathy_streamed_media_window_mic_volume_changed_cb, self);
1872 if (priv->video_tee != NULL)
1873 g_object_unref (priv->video_tee);
1874 priv->video_tee = NULL;
1876 if (priv->video_preview != NULL)
1877 gtk_widget_destroy (priv->video_preview);
1878 priv->video_preview = NULL;
1880 priv->liveadder = NULL;
1881 priv->funnel = NULL;
1883 create_pipeline (self);
1884 /* Call will be started when user will hit the 'redial' button */
1885 priv->start_call_when_playing = FALSE;
1886 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
1892 g_message ("Error: could not destroy pipeline. Closing call window");
1893 gtk_widget_destroy (GTK_WIDGET (self));
1900 reset_details_pane (EmpathyStreamedMediaWindow *self)
1902 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1904 /* translators: encoding video codec is unknown */
1905 gtk_label_set_text (GTK_LABEL (priv->vcodec_encoding_label), _("Unknown"));
1906 /* translators: encoding audio codec is unknown */
1907 gtk_label_set_text (GTK_LABEL (priv->acodec_encoding_label), _("Unknown"));
1908 /* translators: decoding video codec is unknown */
1909 gtk_label_set_text (GTK_LABEL (priv->vcodec_decoding_label), _("Unknown"));
1910 /* translators: decoding audio codec is unknown */
1911 gtk_label_set_text (GTK_LABEL (priv->acodec_decoding_label), _("Unknown"));
1915 empathy_streamed_media_window_disconnected (EmpathyStreamedMediaWindow *self,
1918 gboolean could_disconnect = FALSE;
1919 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
1920 gboolean could_reset_pipeline;
1922 /* Leave full screen mode if needed */
1923 gtk_window_unfullscreen (GTK_WINDOW (self));
1925 gtk_action_set_sensitive (priv->menu_fullscreen, FALSE);
1927 could_reset_pipeline = empathy_streamed_media_window_reset_pipeline (self);
1929 if (priv->call_state == CONNECTING)
1930 empathy_sound_manager_stop (priv->sound_mgr,
1931 EMPATHY_SOUND_PHONE_OUTGOING);
1933 if (priv->call_state != REDIALING)
1934 priv->call_state = DISCONNECTED;
1936 if (could_reset_pipeline)
1938 g_mutex_lock (priv->lock);
1940 g_timer_stop (priv->timer);
1942 if (priv->timer_id != 0)
1943 g_source_remove (priv->timer_id);
1946 g_mutex_unlock (priv->lock);
1949 /* We are about to destroy the window, no need to update it or create
1950 * a video preview */
1953 empathy_streamed_media_window_status_message (self, _("Disconnected"));
1955 gtk_action_set_sensitive (priv->redial, TRUE);
1956 gtk_widget_set_sensitive (priv->redial_button, TRUE);
1958 /* Unsensitive the camera and mic button */
1959 gtk_widget_set_sensitive (priv->tool_button_camera_on, FALSE);
1960 gtk_action_set_sensitive (priv->action_camera_on, FALSE);
1961 gtk_widget_set_sensitive (priv->mic_button, FALSE);
1963 /* Be sure that the mic button is enabled */
1964 gtk_toggle_tool_button_set_active (
1965 GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), TRUE);
1967 if (priv->camera_state == CAMERA_STATE_ON)
1969 /* Enable the 'preview' button as we are not sending atm. */
1970 gtk_toggle_tool_button_set_active (
1971 GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_preview), TRUE);
1973 else if (priv->camera_state == CAMERA_STATE_PREVIEW)
1975 /* Restart the preview with the new pipeline. */
1976 display_video_preview (self, TRUE);
1979 gtk_progress_bar_set_fraction (
1980 GTK_PROGRESS_BAR (priv->volume_progress_bar), 0);
1982 /* destroy the video output; it will be recreated when we'll redial */
1983 disconnect_video_output_motion_handler (self);
1984 gtk_widget_destroy (priv->video_output);
1985 priv->video_output = NULL;
1987 gtk_widget_show (priv->remote_user_avatar_widget);
1989 reset_details_pane (self);
1991 priv->sending_video = FALSE;
1992 priv->call_started = FALSE;
1994 could_disconnect = TRUE;
1996 /* TODO: display the self avatar of the preview (depends if the "Always
1997 * Show Video Preview" is enabled or not) */
2000 return could_disconnect;
2005 empathy_streamed_media_window_channel_closed_cb (EmpathyStreamedMediaHandler *handler,
2008 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
2009 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2011 if (empathy_streamed_media_window_disconnected (self, TRUE) &&
2012 priv->call_state == REDIALING)
2013 empathy_streamed_media_window_restart_call (self);
2018 empathy_streamed_media_window_channel_stream_closed_cb (EmpathyStreamedMediaHandler *handler,
2019 TfStream *stream, gpointer user_data)
2021 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
2022 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2025 g_object_get (stream, "media-type", &media_type, NULL);
2028 * This assumes that there is only one video stream per channel...
2031 if (media_type == TP_MEDIA_STREAM_TYPE_VIDEO)
2033 if (priv->funnel != NULL)
2037 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
2038 (priv->video_output));
2040 gst_element_set_state (output, GST_STATE_NULL);
2041 gst_element_set_state (priv->funnel, GST_STATE_NULL);
2043 gst_bin_remove (GST_BIN (priv->pipeline), output);
2044 gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
2045 priv->funnel = NULL;
2048 else if (media_type == TP_MEDIA_STREAM_TYPE_AUDIO)
2050 if (priv->liveadder != NULL)
2052 gst_element_set_state (priv->audio_output, GST_STATE_NULL);
2053 gst_element_set_state (priv->liveadder, GST_STATE_NULL);
2055 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
2056 gst_bin_remove (GST_BIN (priv->pipeline), priv->liveadder);
2057 priv->liveadder = NULL;
2062 /* Called with global lock held */
2064 empathy_streamed_media_window_get_video_sink_pad (EmpathyStreamedMediaWindow *self)
2066 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2070 if (priv->funnel == NULL)
2072 output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
2073 (priv->video_output));
2075 priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
2079 g_warning ("Could not create fsfunnel");
2083 if (!gst_bin_add (GST_BIN (priv->pipeline), priv->funnel))
2085 gst_object_unref (priv->funnel);
2086 priv->funnel = NULL;
2087 g_warning ("Could not add funnel to pipeline");
2091 if (!gst_bin_add (GST_BIN (priv->pipeline), output))
2093 g_warning ("Could not add the video output widget to the pipeline");
2097 if (!gst_element_link (priv->funnel, output))
2099 g_warning ("Could not link output sink to funnel");
2100 goto error_output_added;
2103 if (gst_element_set_state (output, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2105 g_warning ("Could not start video sink");
2106 goto error_output_added;
2109 if (gst_element_set_state (priv->funnel, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2111 g_warning ("Could not start funnel");
2112 goto error_output_added;
2116 pad = gst_element_get_request_pad (priv->funnel, "sink%d");
2119 g_warning ("Could not get request pad from funnel");
2126 gst_element_set_locked_state (priv->funnel, TRUE);
2127 gst_element_set_locked_state (output, TRUE);
2129 gst_element_set_state (priv->funnel, GST_STATE_NULL);
2130 gst_element_set_state (output, GST_STATE_NULL);
2132 gst_bin_remove (GST_BIN (priv->pipeline), output);
2133 gst_element_set_locked_state (output, FALSE);
2137 gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
2138 priv->funnel = NULL;
2143 /* Called with global lock held */
2145 empathy_streamed_media_window_get_audio_sink_pad (EmpathyStreamedMediaWindow *self)
2147 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2150 GError *gerror = NULL;
2152 if (priv->liveadder == NULL)
2154 priv->liveadder = gst_element_factory_make ("liveadder", NULL);
2156 if (!gst_bin_add (GST_BIN (priv->pipeline), priv->liveadder))
2158 g_warning ("Could not add liveadder to the pipeline");
2159 goto error_add_liveadder;
2161 if (!gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output))
2163 g_warning ("Could not add audio sink to pipeline");
2164 goto error_add_output;
2167 if (gst_element_set_state (priv->liveadder, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2169 g_warning ("Could not start liveadder");
2173 if (gst_element_set_state (priv->audio_output, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2175 g_warning ("Could not start audio sink");
2179 if (GST_PAD_LINK_FAILED (
2180 gst_element_link (priv->liveadder, priv->audio_output)))
2182 g_warning ("Could not link liveadder to audio output");
2187 filter = gst_parse_bin_from_description (
2188 "audioconvert ! audioresample ! audioconvert", TRUE, &gerror);
2191 g_warning ("Could not make audio conversion filter: %s", gerror->message);
2192 g_clear_error (&gerror);
2196 if (!gst_bin_add (GST_BIN (priv->pipeline), filter))
2198 g_warning ("Could not add audio conversion filter to pipeline");
2199 gst_object_unref (filter);
2203 if (gst_element_set_state (filter, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2205 g_warning ("Could not start audio conversion filter");
2209 if (!gst_element_link (filter, priv->liveadder))
2211 g_warning ("Could not link audio conversion filter to liveadder");
2215 pad = gst_element_get_static_pad (filter, "sink");
2219 g_warning ("Could not get sink pad from filter");
2227 gst_element_set_locked_state (filter, TRUE);
2228 gst_element_set_state (filter, GST_STATE_NULL);
2229 gst_bin_remove (GST_BIN (priv->pipeline), filter);
2233 gst_element_set_locked_state (priv->liveadder, TRUE);
2234 gst_element_set_locked_state (priv->audio_output, TRUE);
2236 gst_element_set_state (priv->liveadder, GST_STATE_NULL);
2237 gst_element_set_state (priv->audio_output, GST_STATE_NULL);
2239 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
2243 gst_bin_remove (GST_BIN (priv->pipeline), priv->liveadder);
2245 gst_element_set_locked_state (priv->liveadder, FALSE);
2246 gst_element_set_locked_state (priv->audio_output, FALSE);
2248 error_add_liveadder:
2250 if (priv->liveadder != NULL)
2252 gst_object_unref (priv->liveadder);
2253 priv->liveadder = NULL;
2260 empathy_streamed_media_window_update_timer (gpointer user_data)
2262 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
2263 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2267 time_ = g_timer_elapsed (priv->timer, NULL);
2269 /* Translators: number of minutes:seconds the caller has been connected */
2270 str = g_strdup_printf (_("Connected — %d:%02dm"), (int) time_ / 60,
2272 empathy_streamed_media_window_status_message (self, str);
2279 display_error (EmpathyStreamedMediaWindow *self,
2280 EmpathyTpStreamedMedia *call,
2284 const gchar *details)
2286 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2287 GtkWidget *info_bar;
2288 GtkWidget *content_area;
2295 /* Create info bar */
2296 info_bar = gtk_info_bar_new_with_buttons (GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
2299 gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING);
2301 content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
2303 /* hbox containing the image and the messages vbox */
2304 hbox = gtk_hbox_new (FALSE, 3);
2305 gtk_container_add (GTK_CONTAINER (content_area), hbox);
2308 image = gtk_image_new_from_icon_name (img, GTK_ICON_SIZE_DIALOG);
2309 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
2311 /* vbox containing the main message and the details expander */
2312 vbox = gtk_vbox_new (FALSE, 3);
2313 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
2316 txt = g_strdup_printf ("<b>%s</b>\n%s", title, desc);
2318 label = gtk_label_new (NULL);
2319 gtk_label_set_markup (GTK_LABEL (label), txt);
2320 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
2321 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
2324 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
2327 if (details != NULL)
2329 GtkWidget *expander;
2331 expander = gtk_expander_new (_("Technical Details"));
2333 txt = g_strdup_printf ("<i>%s</i>", details);
2335 label = gtk_label_new (NULL);
2336 gtk_label_set_markup (GTK_LABEL (label), txt);
2337 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
2338 gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
2341 gtk_container_add (GTK_CONTAINER (expander), label);
2342 gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
2345 g_signal_connect (info_bar, "response",
2346 G_CALLBACK (gtk_widget_destroy), NULL);
2348 gtk_box_pack_start (GTK_BOX (priv->errors_vbox), info_bar,
2349 FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
2350 gtk_widget_show_all (info_bar);
2354 media_stream_error_to_txt (EmpathyStreamedMediaWindow *self,
2355 EmpathyTpStreamedMedia *call,
2357 TpMediaStreamError error)
2359 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2366 case TP_MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED:
2368 return g_strdup_printf (
2369 _("%s's software does not understand any of the audio formats "
2370 "supported by your computer"),
2371 empathy_contact_get_alias (priv->contact));
2373 return g_strdup_printf (
2374 _("%s's software does not understand any of the video formats "
2375 "supported by your computer"),
2376 empathy_contact_get_alias (priv->contact));
2378 case TP_MEDIA_STREAM_ERROR_CONNECTION_FAILED:
2379 return g_strdup_printf (
2380 _("Can't establish a connection to %s. "
2381 "One of you might be on a network that does not allow "
2382 "direct connections."),
2383 empathy_contact_get_alias (priv->contact));
2385 case TP_MEDIA_STREAM_ERROR_NETWORK_ERROR:
2386 return g_strdup (_("There was a failure on the network"));
2388 case TP_MEDIA_STREAM_ERROR_NO_CODECS:
2390 return g_strdup (_("The audio formats necessary for this call "
2391 "are not installed on your computer"));
2393 return g_strdup (_("The video formats necessary for this call "
2394 "are not installed on your computer"));
2396 case TP_MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR:
2397 cm = empathy_tp_streamed_media_get_connection_manager (call);
2399 url = g_strdup_printf ("http://bugs.freedesktop.org/enter_bug.cgi?"
2400 "product=Telepathy&component=%s", cm);
2402 result = g_strdup_printf (
2403 _("Something unexpected happened in a Telepathy component. "
2404 "Please <a href=\"%s\">report this bug</a> and attach "
2405 "logs gathered from the 'Debug' window in the Help menu."), url);
2410 case TP_MEDIA_STREAM_ERROR_MEDIA_ERROR:
2411 return g_strdup (_("There was a failure in the call engine"));
2413 case TP_MEDIA_STREAM_ERROR_EOS:
2414 return g_strdup (_("The end of the stream was reached"));
2416 case TP_MEDIA_STREAM_ERROR_UNKNOWN:
2423 empathy_streamed_media_window_stream_error (EmpathyStreamedMediaWindow *self,
2424 EmpathyTpStreamedMedia *call,
2433 desc = media_stream_error_to_txt (self, call, audio, code);
2436 /* No description, use the error message. That's not great as it's not
2437 * localized but it's better than nothing. */
2438 display_error (self, call, icon, title, msg, NULL);
2442 display_error (self, call, icon, title, desc, msg);
2448 empathy_streamed_media_window_audio_stream_error (EmpathyTpStreamedMedia *call,
2451 EmpathyStreamedMediaWindow *self)
2453 empathy_streamed_media_window_stream_error (self, call, TRUE, code, msg,
2454 "gnome-stock-mic", _("Can't establish audio stream"));
2458 empathy_streamed_media_window_video_stream_error (EmpathyTpStreamedMedia *call,
2461 EmpathyStreamedMediaWindow *self)
2463 empathy_streamed_media_window_stream_error (self, call, FALSE, code, msg,
2464 "camera-web", _("Can't establish video stream"));
2468 empathy_streamed_media_window_connected (gpointer user_data)
2470 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
2471 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2472 EmpathyTpStreamedMedia *call;
2473 gboolean can_send_video;
2475 empathy_sound_manager_stop (priv->sound_mgr, EMPATHY_SOUND_PHONE_OUTGOING);
2477 can_send_video = priv->video_input != NULL && priv->contact != NULL &&
2478 empathy_contact_can_voip_video (priv->contact);
2480 g_object_get (priv->handler, "tp-call", &call, NULL);
2482 tp_g_signal_connect_object (call, "notify::video-stream",
2483 G_CALLBACK (empathy_streamed_media_window_video_stream_changed_cb),
2486 if (empathy_tp_streamed_media_has_dtmf (call))
2487 gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
2489 if (priv->video_input == NULL)
2490 empathy_streamed_media_window_set_send_video (self, CAMERA_STATE_OFF);
2492 priv->sending_video = can_send_video ?
2493 empathy_tp_streamed_media_is_sending_video (call) : FALSE;
2495 gtk_toggle_tool_button_set_active (
2496 GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_on),
2497 priv->sending_video && priv->video_input != NULL);
2498 gtk_widget_set_sensitive (priv->tool_button_camera_on, can_send_video);
2499 gtk_action_set_sensitive (priv->action_camera_on, can_send_video);
2501 gtk_action_set_sensitive (priv->redial, FALSE);
2502 gtk_widget_set_sensitive (priv->redial_button, FALSE);
2504 gtk_widget_set_sensitive (priv->mic_button, TRUE);
2506 empathy_streamed_media_window_update_avatars_visibility (call, self);
2508 g_object_unref (call);
2510 g_mutex_lock (priv->lock);
2512 priv->timer_id = g_timeout_add_seconds (1,
2513 empathy_streamed_media_window_update_timer, self);
2515 g_mutex_unlock (priv->lock);
2517 empathy_streamed_media_window_update_timer (self);
2519 gtk_action_set_sensitive (priv->menu_fullscreen, TRUE);
2525 /* Called from the streaming thread */
2527 empathy_streamed_media_window_src_added_cb (EmpathyStreamedMediaHandler *handler,
2528 GstPad *src, guint media_type, gpointer user_data)
2530 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
2531 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2532 gboolean retval = FALSE;
2536 g_mutex_lock (priv->lock);
2538 if (priv->call_state != CONNECTED)
2540 g_timer_start (priv->timer);
2541 priv->timer_id = g_idle_add (empathy_streamed_media_window_connected, self);
2542 priv->call_state = CONNECTED;
2547 case TP_MEDIA_STREAM_TYPE_AUDIO:
2548 pad = empathy_streamed_media_window_get_audio_sink_pad (self);
2550 case TP_MEDIA_STREAM_TYPE_VIDEO:
2551 gtk_widget_hide (priv->remote_user_avatar_widget);
2552 gtk_widget_show (priv->video_output);
2553 pad = empathy_streamed_media_window_get_video_sink_pad (self);
2556 g_assert_not_reached ();
2562 if (GST_PAD_LINK_FAILED (gst_pad_link (src, pad)))
2563 g_warning ("Could not link %s sink pad",
2564 media_type == TP_MEDIA_STREAM_TYPE_AUDIO ? "audio" : "video");
2568 gst_object_unref (pad);
2572 /* If no sink could be linked, try to add fakesink to prevent the whole call
2577 GstElement *fakesink = gst_element_factory_make ("fakesink", NULL);
2579 if (gst_bin_add (GST_BIN (priv->pipeline), fakesink))
2581 GstPad *sinkpad = gst_element_get_static_pad (fakesink, "sink");
2582 if (gst_element_set_state (fakesink, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE ||
2583 GST_PAD_LINK_FAILED (gst_pad_link (src, sinkpad)))
2585 gst_element_set_locked_state (fakesink, TRUE);
2586 gst_element_set_state (fakesink, GST_STATE_NULL);
2587 gst_bin_remove (GST_BIN (priv->pipeline), fakesink);
2591 g_debug ("Could not link real sink, linked fakesink instead");
2593 gst_object_unref (sinkpad);
2597 gst_object_unref (fakesink);
2602 g_mutex_unlock (priv->lock);
2608 empathy_streamed_media_window_sink_added_cb (EmpathyStreamedMediaHandler *handler,
2609 GstPad *sink, guint media_type, gpointer user_data)
2611 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
2612 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2614 gboolean retval = FALSE;
2618 case TP_MEDIA_STREAM_TYPE_AUDIO:
2619 if (!gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input))
2621 g_warning ("Could not add audio source to pipeline");
2625 pad = gst_element_get_static_pad (priv->audio_input, "src");
2628 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
2629 g_warning ("Could not get source pad from audio source");
2633 if (GST_PAD_LINK_FAILED (gst_pad_link (pad, sink)))
2635 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
2636 g_warning ("Could not link audio source to farsight");
2640 if (gst_element_set_state (priv->audio_input, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
2642 g_warning ("Could not start audio source");
2643 gst_element_set_state (priv->audio_input, GST_STATE_NULL);
2644 gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
2650 case TP_MEDIA_STREAM_TYPE_VIDEO:
2651 if (priv->video_input != NULL)
2653 if (priv->video_tee != NULL)
2655 pad = gst_element_get_request_pad (priv->video_tee, "src%d");
2656 if (GST_PAD_LINK_FAILED (gst_pad_link (pad, sink)))
2658 g_warning ("Could not link videp soure input pipeline");
2661 gst_object_unref (pad);
2668 g_assert_not_reached ();
2675 empathy_streamed_media_window_remove_video_input (EmpathyStreamedMediaWindow *self)
2677 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2678 GstElement *preview;
2680 disable_camera (self);
2682 DEBUG ("remove video input");
2683 preview = empathy_video_widget_get_element (
2684 EMPATHY_VIDEO_WIDGET (priv->video_preview));
2686 gst_element_set_state (priv->video_input, GST_STATE_NULL);
2687 gst_element_set_state (priv->video_tee, GST_STATE_NULL);
2688 gst_element_set_state (preview, GST_STATE_NULL);
2690 gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
2691 priv->video_tee, preview, NULL);
2693 g_object_unref (priv->video_input);
2694 priv->video_input = NULL;
2695 g_object_unref (priv->video_tee);
2696 priv->video_tee = NULL;
2697 gtk_widget_destroy (priv->video_preview);
2698 priv->video_preview = NULL;
2700 gtk_widget_set_sensitive (priv->tool_button_camera_on, FALSE);
2701 gtk_action_set_sensitive (priv->action_camera_on, FALSE);
2702 gtk_widget_set_sensitive (priv->tool_button_camera_preview, FALSE);
2706 start_call (EmpathyStreamedMediaWindow *self)
2708 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2710 priv->call_started = TRUE;
2711 empathy_streamed_media_handler_start_call (priv->handler,
2712 gtk_get_current_event_time ());
2714 if (empathy_streamed_media_handler_has_initial_video (priv->handler))
2716 /* Enable 'send video' buttons and display the preview */
2717 gtk_toggle_tool_button_set_active (
2718 GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_on), TRUE);
2723 empathy_streamed_media_window_bus_message (GstBus *bus, GstMessage *message,
2726 EmpathyStreamedMediaWindow *self = EMPATHY_STREAMED_MEDIA_WINDOW (user_data);
2727 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2730 empathy_streamed_media_handler_bus_message (priv->handler, bus, message);
2732 switch (GST_MESSAGE_TYPE (message))
2734 case GST_MESSAGE_STATE_CHANGED:
2735 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
2737 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
2738 if (newstate == GST_STATE_PAUSED)
2739 empathy_streamed_media_window_setup_video_input (self);
2741 if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
2742 !priv->call_started)
2744 gst_message_parse_state_changed (message, NULL, &newstate, NULL);
2745 if (newstate == GST_STATE_PAUSED)
2747 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2748 priv->pipeline_playing = TRUE;
2750 if (priv->start_call_when_playing)
2755 case GST_MESSAGE_ERROR:
2757 GError *error = NULL;
2758 GstElement *gst_error;
2761 gst_message_parse_error (message, &error, &debug);
2762 gst_error = GST_ELEMENT (GST_MESSAGE_SRC (message));
2764 g_message ("Element error: %s -- %s\n", error->message, debug);
2766 if (g_str_has_prefix (gst_element_get_name (gst_error),
2767 VIDEO_INPUT_ERROR_PREFIX))
2769 /* Remove the video input and continue */
2770 if (priv->video_input != NULL)
2771 empathy_streamed_media_window_remove_video_input (self);
2772 gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
2776 empathy_streamed_media_window_disconnected (self, TRUE);
2778 g_error_free (error);
2781 case GST_MESSAGE_UNKNOWN:
2782 case GST_MESSAGE_EOS:
2783 case GST_MESSAGE_WARNING:
2784 case GST_MESSAGE_INFO:
2785 case GST_MESSAGE_TAG:
2786 case GST_MESSAGE_BUFFERING:
2787 case GST_MESSAGE_STATE_DIRTY:
2788 case GST_MESSAGE_STEP_DONE:
2789 case GST_MESSAGE_CLOCK_PROVIDE:
2790 case GST_MESSAGE_CLOCK_LOST:
2791 case GST_MESSAGE_NEW_CLOCK:
2792 case GST_MESSAGE_STRUCTURE_CHANGE:
2793 case GST_MESSAGE_STREAM_STATUS:
2794 case GST_MESSAGE_APPLICATION:
2795 case GST_MESSAGE_ELEMENT:
2796 case GST_MESSAGE_SEGMENT_START:
2797 case GST_MESSAGE_SEGMENT_DONE:
2798 case GST_MESSAGE_DURATION:
2799 case GST_MESSAGE_ANY:
2808 empathy_streamed_media_window_update_avatars_visibility (EmpathyTpStreamedMedia *call,
2809 EmpathyStreamedMediaWindow *window)
2811 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2813 if (empathy_tp_streamed_media_is_receiving_video (call))
2815 gtk_widget_hide (priv->remote_user_avatar_widget);
2816 gtk_widget_show (priv->video_output);
2820 gtk_widget_hide (priv->video_output);
2821 gtk_widget_show (priv->remote_user_avatar_widget);
2826 call_handler_notify_tp_streamed_media_cb (EmpathyStreamedMediaHandler *handler,
2828 EmpathyStreamedMediaWindow *self)
2830 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
2831 EmpathyTpStreamedMedia *call;
2833 g_object_get (priv->handler, "tp-call", &call, NULL);
2837 tp_g_signal_connect_object (call, "audio-stream-error",
2838 G_CALLBACK (empathy_streamed_media_window_audio_stream_error), self, 0);
2839 tp_g_signal_connect_object (call, "video-stream-error",
2840 G_CALLBACK (empathy_streamed_media_window_video_stream_error), self, 0);
2842 g_object_unref (call);
2846 empathy_streamed_media_window_realized_cb (GtkWidget *widget, EmpathyStreamedMediaWindow *window)
2848 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2849 EmpathyTpStreamedMedia *call;
2851 g_signal_connect (priv->handler, "conference-added",
2852 G_CALLBACK (empathy_streamed_media_window_conference_added_cb), window);
2853 g_signal_connect (priv->handler, "request-resource",
2854 G_CALLBACK (empathy_streamed_media_window_request_resource_cb), window);
2855 g_signal_connect (priv->handler, "closed",
2856 G_CALLBACK (empathy_streamed_media_window_channel_closed_cb), window);
2857 g_signal_connect (priv->handler, "src-pad-added",
2858 G_CALLBACK (empathy_streamed_media_window_src_added_cb), window);
2859 g_signal_connect (priv->handler, "sink-pad-added",
2860 G_CALLBACK (empathy_streamed_media_window_sink_added_cb), window);
2861 g_signal_connect (priv->handler, "stream-closed",
2862 G_CALLBACK (empathy_streamed_media_window_channel_stream_closed_cb), window);
2864 g_object_get (priv->handler, "tp-call", &call, NULL);
2867 tp_g_signal_connect_object (call, "audio-stream-error",
2868 G_CALLBACK (empathy_streamed_media_window_audio_stream_error), window,
2870 tp_g_signal_connect_object (call, "video-stream-error",
2871 G_CALLBACK (empathy_streamed_media_window_video_stream_error), window,
2874 g_object_unref (call);
2878 /* tp-call doesn't exist yet, we'll connect signals once it has been
2880 g_signal_connect (priv->handler, "notify::tp-call",
2881 G_CALLBACK (call_handler_notify_tp_streamed_media_cb), window);
2884 gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
2888 empathy_streamed_media_window_delete_cb (GtkWidget *widget, GdkEvent*event,
2889 EmpathyStreamedMediaWindow *window)
2891 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2893 if (priv->pipeline != NULL)
2895 if (priv->bus_message_source_id != 0)
2897 g_source_remove (priv->bus_message_source_id);
2898 priv->bus_message_source_id = 0;
2901 gst_element_set_state (priv->pipeline, GST_STATE_NULL);
2904 if (priv->call_state == CONNECTING)
2905 empathy_sound_manager_stop (priv->sound_mgr, EMPATHY_SOUND_PHONE_OUTGOING);
2911 show_controls (EmpathyStreamedMediaWindow *window, gboolean set_fullscreen)
2914 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2916 menu = gtk_ui_manager_get_widget (priv->ui_manager,
2921 gtk_widget_hide (priv->sidebar);
2922 gtk_widget_hide (menu);
2923 gtk_widget_hide (priv->vbox);
2924 gtk_widget_hide (priv->statusbar);
2925 gtk_widget_hide (priv->toolbar);
2929 if (priv->sidebar_was_visible_before_fs)
2930 gtk_widget_show (priv->sidebar);
2932 gtk_widget_show (menu);
2933 gtk_widget_show (priv->vbox);
2934 gtk_widget_show (priv->statusbar);
2935 gtk_widget_show (priv->toolbar);
2937 gtk_window_resize (GTK_WINDOW (window), priv->original_width_before_fs,
2938 priv->original_height_before_fs);
2943 show_borders (EmpathyStreamedMediaWindow *window, gboolean set_fullscreen)
2945 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2947 gtk_container_set_border_width (GTK_CONTAINER (priv->content_hbox),
2948 set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
2949 gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
2950 set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
2952 if (priv->video_output != NULL)
2954 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2955 priv->video_output, TRUE, TRUE,
2956 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2960 gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
2961 priv->vbox, TRUE, TRUE,
2962 set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
2967 empathy_streamed_media_window_state_event_cb (GtkWidget *widget,
2968 GdkEventWindowState *event, EmpathyStreamedMediaWindow *window)
2970 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
2972 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
2973 gboolean set_fullscreen = event->new_window_state &
2974 GDK_WINDOW_STATE_FULLSCREEN;
2978 gboolean sidebar_was_visible;
2979 GtkAllocation allocation;
2980 gint original_width, original_height;
2982 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
2983 original_width = allocation.width;
2984 original_height = allocation.height;
2986 g_object_get (priv->sidebar, "visible", &sidebar_was_visible, NULL);
2988 priv->sidebar_was_visible_before_fs = sidebar_was_visible;
2989 priv->original_width_before_fs = original_width;
2990 priv->original_height_before_fs = original_height;
2992 if (priv->video_output_motion_handler_id == 0 &&
2993 priv->video_output != NULL)
2995 priv->video_output_motion_handler_id = g_signal_connect (
2996 G_OBJECT (priv->video_output), "motion-notify-event",
2997 G_CALLBACK (empathy_streamed_media_window_video_output_motion_notify),
3003 disconnect_video_output_motion_handler (window);
3006 empathy_streamed_media_window_fullscreen_set_fullscreen (priv->fullscreen,
3008 show_controls (window, set_fullscreen);
3009 show_borders (window, set_fullscreen);
3010 gtk_action_set_stock_id (priv->menu_fullscreen,
3011 (set_fullscreen ? "gtk-leave-fullscreen" : "gtk-fullscreen"));
3012 priv->is_fullscreen = set_fullscreen;
3019 empathy_streamed_media_window_sidebar_toggled_cb (GtkToggleButton *toggle,
3020 EmpathyStreamedMediaWindow *window)
3022 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3024 int w, h, handle_size;
3025 GtkAllocation allocation, sidebar_allocation;
3027 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
3028 w = allocation.width;
3029 h = allocation.height;
3031 gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
3033 gtk_widget_get_allocation (priv->sidebar, &sidebar_allocation);
3034 if (gtk_toggle_button_get_active (toggle))
3036 arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
3037 gtk_widget_show (priv->sidebar);
3038 w += sidebar_allocation.width + handle_size;
3042 arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
3043 w -= sidebar_allocation.width + handle_size;
3044 gtk_widget_hide (priv->sidebar);
3047 gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
3050 gtk_window_resize (GTK_WINDOW (window), w, h);
3054 empathy_streamed_media_window_set_send_video (EmpathyStreamedMediaWindow *window,
3057 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3058 EmpathyTpStreamedMedia *call;
3060 priv->sending_video = (state == CAMERA_STATE_ON);
3062 if (state == CAMERA_STATE_PREVIEW ||
3063 state == CAMERA_STATE_ON)
3065 /* When we start sending video, we want to show the video preview by
3067 display_video_preview (window, TRUE);
3071 display_video_preview (window, FALSE);
3074 if (priv->call_state != CONNECTED)
3077 g_object_get (priv->handler, "tp-call", &call, NULL);
3078 DEBUG ("%s sending video", priv->sending_video ? "start": "stop");
3079 empathy_tp_streamed_media_request_video_stream_direction (call, priv->sending_video);
3080 g_object_unref (call);
3084 empathy_streamed_media_window_mic_toggled_cb (GtkToggleToolButton *toggle,
3085 EmpathyStreamedMediaWindow *window)
3087 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3090 active = (gtk_toggle_tool_button_get_active (toggle));
3094 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
3096 gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
3100 /* TODO, Instead of setting the input volume to 0 we should probably
3101 * stop sending but this would cause the audio call to drop if both
3102 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
3103 * in the future. GNOME #574574
3105 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
3107 gtk_adjustment_set_value (priv->audio_input_adj, 0);
3112 empathy_streamed_media_window_sidebar_hidden_cb (EvSidebar *sidebar,
3113 EmpathyStreamedMediaWindow *window)
3115 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3117 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
3122 empathy_streamed_media_window_sidebar_shown_cb (EvSidebar *sidebar,
3123 EmpathyStreamedMediaWindow *window)
3125 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3127 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->sidebar_button),
3132 empathy_streamed_media_window_hangup_cb (gpointer object,
3133 EmpathyStreamedMediaWindow *window)
3135 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3137 empathy_streamed_media_handler_stop_call (priv->handler);
3139 if (empathy_streamed_media_window_disconnected (window, FALSE))
3140 gtk_widget_destroy (GTK_WIDGET (window));
3144 empathy_streamed_media_window_restart_call (EmpathyStreamedMediaWindow *window)
3146 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3148 /* Remove error info bars */
3149 gtk_container_forall (GTK_CONTAINER (priv->errors_vbox),
3150 (GtkCallback) gtk_widget_destroy, NULL);
3152 create_video_output_widget (window);
3154 g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
3155 G_CALLBACK (empathy_streamed_media_window_mic_volume_changed_cb), window);
3157 /* While the call was disconnected, the input volume might have changed.
3158 * However, since the audio_input source was destroyed, its volume has not
3159 * been updated during that time. That's why we manually update it here */
3160 empathy_streamed_media_window_mic_volume_changed_cb (priv->audio_input_adj, window);
3162 priv->outgoing = TRUE;
3163 empathy_streamed_media_window_set_state_connecting (window);
3165 if (priv->pipeline_playing)
3166 start_call (window);
3168 /* call will be started when the pipeline is ready */
3169 priv->start_call_when_playing = TRUE;
3172 empathy_streamed_media_window_setup_avatars (window, priv->handler);
3174 gtk_action_set_sensitive (priv->redial, FALSE);
3175 gtk_widget_set_sensitive (priv->redial_button, FALSE);
3179 empathy_streamed_media_window_redial_cb (gpointer object,
3180 EmpathyStreamedMediaWindow *window)
3182 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3184 if (priv->call_state == CONNECTED)
3185 priv->call_state = REDIALING;
3187 empathy_streamed_media_handler_stop_call (priv->handler);
3189 if (priv->call_state != CONNECTED)
3190 empathy_streamed_media_window_restart_call (window);
3194 empathy_streamed_media_window_fullscreen_cb (gpointer object,
3195 EmpathyStreamedMediaWindow *window)
3197 empathy_streamed_media_window_fullscreen_toggle (window);
3201 empathy_streamed_media_window_fullscreen_toggle (EmpathyStreamedMediaWindow *window)
3203 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3205 if (priv->is_fullscreen)
3206 gtk_window_unfullscreen (GTK_WINDOW (window));
3208 gtk_window_fullscreen (GTK_WINDOW (window));
3212 empathy_streamed_media_window_video_button_press_cb (GtkWidget *video_output,
3213 GdkEventButton *event, EmpathyStreamedMediaWindow *window)
3215 if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
3217 empathy_streamed_media_window_video_menu_popup (window, event->button);
3225 empathy_streamed_media_window_key_press_cb (GtkWidget *video_output,
3226 GdkEventKey *event, EmpathyStreamedMediaWindow *window)
3228 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3230 if (priv->is_fullscreen && event->keyval == GDK_KEY_Escape)
3232 /* Since we are in fullscreen mode, toggling will bring us back to
3234 empathy_streamed_media_window_fullscreen_toggle (window);
3242 empathy_streamed_media_window_video_output_motion_notify (GtkWidget *widget,
3243 GdkEventMotion *event, EmpathyStreamedMediaWindow *window)
3245 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3247 if (priv->is_fullscreen)
3249 empathy_streamed_media_window_fullscreen_show_popup (priv->fullscreen);
3256 empathy_streamed_media_window_video_menu_popup (EmpathyStreamedMediaWindow *window,
3260 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3262 menu = gtk_ui_manager_get_widget (priv->ui_manager,
3264 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
3265 button, gtk_get_current_event_time ());
3266 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
3270 empathy_streamed_media_window_status_message (EmpathyStreamedMediaWindow *window,
3273 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3275 if (priv->context_id == 0)
3277 priv->context_id = gtk_statusbar_get_context_id (
3278 GTK_STATUSBAR (priv->statusbar), "voip call status messages");
3282 gtk_statusbar_pop (GTK_STATUSBAR (priv->statusbar), priv->context_id);
3285 gtk_statusbar_push (GTK_STATUSBAR (priv->statusbar), priv->context_id,
3290 empathy_streamed_media_window_volume_changed_cb (GtkScaleButton *button,
3291 gdouble value, EmpathyStreamedMediaWindow *window)
3293 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (window);
3295 if (priv->audio_output == NULL)
3298 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv->audio_output),
3302 /* block all the signals related to camera control widgets. This is useful
3303 * when we are manually updating the UI and so don't want to fire the
3306 block_camera_control_signals (EmpathyStreamedMediaWindow *self)
3308 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
3310 g_signal_handlers_block_by_func (priv->tool_button_camera_off,
3311 tool_button_camera_off_toggled_cb, self);
3312 g_signal_handlers_block_by_func (priv->tool_button_camera_preview,
3313 tool_button_camera_preview_toggled_cb, self);
3314 g_signal_handlers_block_by_func (priv->tool_button_camera_on,
3315 tool_button_camera_on_toggled_cb, self);
3316 g_signal_handlers_block_by_func (priv->action_camera_on,
3317 action_camera_change_cb, self);
3321 unblock_camera_control_signals (EmpathyStreamedMediaWindow *self)
3323 EmpathyStreamedMediaWindowPriv *priv = GET_PRIV (self);
3325 g_signal_handlers_unblock_by_func (priv->tool_button_camera_off,
3326 tool_button_camera_off_toggled_cb, self);
3327 g_signal_handlers_unblock_by_func (priv->tool_button_camera_preview,
3328 tool_button_camera_preview_toggled_cb, self);
3329 g_signal_handlers_unblock_by_func (priv->tool_button_camera_on,
3330 tool_button_camera_on_toggled_cb, self);
3331 g_signal_handlers_unblock_by_func (priv->action_camera_on,
3332 action_camera_change_cb, self);