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
28 #include <gdk/gdkkeysyms.h>
31 #include <glib/gi18n.h>
33 #include <telepathy-glib/util.h>
35 #include <telepathy-farstream/telepathy-farstream.h>
37 #include <farstream/fs-element-added-notifier.h>
38 #include <farstream/fs-utils.h>
40 #include <libempathy/empathy-tp-contact-factory.h>
41 #include <libempathy/empathy-utils.h>
42 #include <libempathy-gtk/empathy-avatar-image.h>
43 #include <libempathy-gtk/empathy-dialpad-widget.h>
44 #include <libempathy-gtk/empathy-ui-utils.h>
45 #include <libempathy-gtk/empathy-sound-manager.h>
46 #include <libempathy-gtk/empathy-geometry.h>
47 #include <libempathy-gtk/empathy-images.h>
49 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
50 #include <libempathy/empathy-debug.h>
52 #include "empathy-streamed-media-window.h"
53 #include "empathy-streamed-media-window-fullscreen.h"
54 #include "empathy-video-widget.h"
55 #include "empathy-audio-src.h"
56 #include "empathy-audio-sink.h"
57 #include "empathy-video-src.h"
58 #include "ev-sidebar.h"
60 #define CONTENT_HBOX_BORDER_WIDTH 6
61 #define CONTENT_HBOX_SPACING 3
62 #define CONTENT_HBOX_CHILDREN_PACKING_PADDING 3
64 #define SELF_VIDEO_SECTION_WIDTH 160
65 #define SELF_VIDEO_SECTION_HEIGTH 120
67 /* The avatar's default width and height are set to the same value because we
68 want a square icon. */
69 #define REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
70 #define REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT \
71 EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
73 /* If an video input error occurs, the error message will start with "v4l" */
74 #define VIDEO_INPUT_ERROR_PREFIX "v4l"
76 /* The time interval in milliseconds between 2 outgoing rings */
77 #define MS_BETWEEN_RING 500
79 G_DEFINE_TYPE(EmpathyStreamedMediaWindow
, empathy_streamed_media_window
, GTK_TYPE_WINDOW
)
88 static guint signals
[LAST_SIGNAL
] = {0};
92 PROP_STREAMED_MEDIA_HANDLER
= 1,
103 CAMERA_STATE_OFF
= 0,
104 CAMERA_STATE_PREVIEW
,
108 /* private structure */
109 typedef struct _EmpathyStreamedMediaWindowPriv EmpathyStreamedMediaWindowPriv
;
111 struct _EmpathyStreamedMediaWindowPriv
113 gboolean dispose_has_run
;
114 EmpathyStreamedMediaHandler
*handler
;
115 EmpathyContact
*contact
;
120 GtkUIManager
*ui_manager
;
121 GtkWidget
*errors_vbox
;
122 /* widget displays the video received from the remote user. This widget is
123 * alive only during call. */
124 GtkWidget
*video_output
;
125 GtkWidget
*video_preview
;
126 GtkWidget
*remote_user_avatar_widget
;
127 GtkWidget
*self_user_avatar_widget
;
129 GtkWidget
*sidebar_button
;
130 GtkWidget
*statusbar
;
131 GtkWidget
*volume_button
;
132 GtkWidget
*redial_button
;
133 GtkWidget
*mic_button
;
137 GtkAction
*menu_fullscreen
;
138 GtkAction
*action_camera_on
;
139 GtkWidget
*tool_button_camera_off
;
140 GtkWidget
*tool_button_camera_preview
;
141 GtkWidget
*tool_button_camera_on
;
143 /* The frames and boxes that contain self and remote avatar and video
144 input/output. When we redial, we destroy and re-create the boxes */
145 GtkWidget
*remote_user_output_frame
;
146 GtkWidget
*self_user_output_frame
;
147 GtkWidget
*remote_user_output_hbox
;
148 GtkWidget
*self_user_output_hbox
;
150 /* We keep a reference on the hbox which contains the main content so we can
151 easilly repack everything when toggling fullscreen */
152 GtkWidget
*content_hbox
;
154 /* This vbox is contained in the content_hbox and it contains the
155 self_user_output_frame and the sidebar button. When toggling fullscreen,
156 it needs to be repacked. We keep a reference on it for easier access. */
159 gulong video_output_motion_handler_id
;
160 guint bus_message_source_id
;
163 GtkWidget
*volume_scale
;
164 GtkWidget
*volume_progress_bar
;
165 GtkAdjustment
*audio_input_adj
;
167 GtkWidget
*dtmf_panel
;
170 GtkWidget
*details_vbox
;
171 GtkWidget
*vcodec_encoding_label
;
172 GtkWidget
*acodec_encoding_label
;
173 GtkWidget
*vcodec_decoding_label
;
174 GtkWidget
*acodec_decoding_label
;
176 GtkWidget
*audio_remote_candidate_label
;
177 GtkWidget
*audio_local_candidate_label
;
178 GtkWidget
*video_remote_candidate_label
;
179 GtkWidget
*video_local_candidate_label
;
180 GtkWidget
*video_remote_candidate_info_img
;
181 GtkWidget
*video_local_candidate_info_img
;
182 GtkWidget
*audio_remote_candidate_info_img
;
183 GtkWidget
*audio_local_candidate_info_img
;
185 GstElement
*video_input
;
186 GstElement
*audio_input
;
187 GstElement
*audio_output
;
188 GstElement
*pipeline
;
189 GstElement
*video_tee
;
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_start_tone_cb (EmpathyDialpadWidget
*dialpad
,
343 EmpathyStreamedMediaWindow
*window
)
345 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
346 EmpathyTpStreamedMedia
*call
;
348 g_object_get (priv
->handler
, "tp-call", &call
, NULL
);
350 empathy_tp_streamed_media_start_tone (call
, event
);
352 g_object_unref (call
);
356 dtmf_stop_tone_cb (EmpathyDialpadWidget
*self
,
358 EmpathyStreamedMediaWindow
*window
)
360 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
361 EmpathyTpStreamedMedia
*call
;
363 g_object_get (priv
->handler
, "tp-call", &call
, NULL
);
365 empathy_tp_streamed_media_stop_tone (call
);
367 g_object_unref (call
);
371 empathy_streamed_media_window_create_video_input_add_slider (EmpathyStreamedMediaWindow
*self
,
372 gchar
*label_text
, GtkWidget
*bin
)
374 GtkWidget
*vbox
= gtk_box_new (GTK_ORIENTATION_VERTICAL
, 2);
375 GtkWidget
*scale
= gtk_scale_new_with_range (GTK_ORIENTATION_VERTICAL
, 0,
377 GtkWidget
*label
= gtk_label_new (label_text
);
379 gtk_widget_set_sensitive (scale
, FALSE
);
381 gtk_container_add (GTK_CONTAINER (bin
), vbox
);
383 gtk_range_set_inverted (GTK_RANGE (scale
), TRUE
);
384 gtk_box_pack_start (GTK_BOX (vbox
), scale
, TRUE
, TRUE
, 0);
385 gtk_box_pack_start (GTK_BOX (vbox
), label
, FALSE
, FALSE
, 0);
391 empathy_streamed_media_window_video_contrast_changed_cb (GtkAdjustment
*adj
,
392 EmpathyStreamedMediaWindow
*self
)
395 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
397 empathy_video_src_set_channel (priv
->video_input
,
398 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST
, gtk_adjustment_get_value (adj
));
402 empathy_streamed_media_window_video_brightness_changed_cb (GtkAdjustment
*adj
,
403 EmpathyStreamedMediaWindow
*self
)
406 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
408 empathy_video_src_set_channel (priv
->video_input
,
409 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS
, gtk_adjustment_get_value (adj
));
413 empathy_streamed_media_window_video_gamma_changed_cb (GtkAdjustment
*adj
,
414 EmpathyStreamedMediaWindow
*self
)
417 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
419 empathy_video_src_set_channel (priv
->video_input
,
420 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA
, gtk_adjustment_get_value (adj
));
425 empathy_streamed_media_window_create_video_input (EmpathyStreamedMediaWindow
*self
)
427 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
430 hbox
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 3);
431 gtk_box_set_homogeneous (GTK_BOX (hbox
), TRUE
);
433 priv
->video_contrast
= empathy_streamed_media_window_create_video_input_add_slider (
434 self
, _("Contrast"), hbox
);
436 priv
->video_brightness
= empathy_streamed_media_window_create_video_input_add_slider (
437 self
, _("Brightness"), hbox
);
439 priv
->video_gamma
= empathy_streamed_media_window_create_video_input_add_slider (
440 self
, _("Gamma"), hbox
);
446 empathy_streamed_media_window_setup_video_input (EmpathyStreamedMediaWindow
*self
)
448 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
452 supported
= empathy_video_src_get_supported_channels (priv
->video_input
);
454 if (supported
& EMPATHY_GST_VIDEO_SRC_SUPPORTS_CONTRAST
)
456 adj
= gtk_range_get_adjustment (GTK_RANGE (priv
->video_contrast
));
458 gtk_adjustment_set_value (adj
,
459 empathy_video_src_get_channel (priv
->video_input
,
460 EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST
));
462 g_signal_connect (G_OBJECT (adj
), "value-changed",
463 G_CALLBACK (empathy_streamed_media_window_video_contrast_changed_cb
), self
);
465 gtk_widget_set_sensitive (priv
->video_contrast
, TRUE
);
468 if (supported
& EMPATHY_GST_VIDEO_SRC_SUPPORTS_BRIGHTNESS
)
470 adj
= gtk_range_get_adjustment (GTK_RANGE (priv
->video_brightness
));
472 gtk_adjustment_set_value (adj
,
473 empathy_video_src_get_channel (priv
->video_input
,
474 EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS
));
476 g_signal_connect (G_OBJECT (adj
), "value-changed",
477 G_CALLBACK (empathy_streamed_media_window_video_brightness_changed_cb
), self
);
478 gtk_widget_set_sensitive (priv
->video_brightness
, TRUE
);
481 if (supported
& EMPATHY_GST_VIDEO_SRC_SUPPORTS_GAMMA
)
483 adj
= gtk_range_get_adjustment (GTK_RANGE (priv
->video_gamma
));
485 gtk_adjustment_set_value (adj
,
486 empathy_video_src_get_channel (priv
->video_input
,
487 EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA
));
489 g_signal_connect (G_OBJECT (adj
), "value-changed",
490 G_CALLBACK (empathy_streamed_media_window_video_gamma_changed_cb
), self
);
491 gtk_widget_set_sensitive (priv
->video_gamma
, TRUE
);
496 empathy_streamed_media_window_mic_volume_changed_cb (GtkAdjustment
*adj
,
497 EmpathyStreamedMediaWindow
*self
)
499 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
502 volume
= gtk_adjustment_get_value (adj
)/100.0;
504 /* Don't store the volume because of muting */
505 if (volume
> 0 || gtk_toggle_tool_button_get_active (
506 GTK_TOGGLE_TOOL_BUTTON (priv
->mic_button
)))
507 priv
->volume
= volume
;
509 /* Ensure that the toggle button is active if the volume is > 0 and inactive
510 * if it's smaller than 0 */
511 if ((volume
> 0) != gtk_toggle_tool_button_get_active (
512 GTK_TOGGLE_TOOL_BUTTON (priv
->mic_button
)))
513 gtk_toggle_tool_button_set_active (
514 GTK_TOGGLE_TOOL_BUTTON (priv
->mic_button
), volume
> 0);
516 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv
->audio_input
),
521 empathy_streamed_media_window_audio_input_level_changed_cb (EmpathyGstAudioSrc
*src
,
522 gdouble level
, EmpathyStreamedMediaWindow
*window
)
525 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
527 value
= CLAMP (pow (10, level
/ 20), 0.0, 1.0);
528 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv
->volume_progress_bar
),
533 empathy_streamed_media_window_create_audio_input (EmpathyStreamedMediaWindow
*self
)
535 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
536 GtkWidget
*hbox
, *vbox
, *label
;
538 hbox
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 3);
539 gtk_box_set_homogeneous (GTK_BOX (hbox
), TRUE
);
541 vbox
= gtk_box_new (GTK_ORIENTATION_VERTICAL
, 3);
542 gtk_box_pack_start (GTK_BOX (hbox
), vbox
, FALSE
, FALSE
, 3);
544 priv
->volume_scale
= gtk_scale_new_with_range (GTK_ORIENTATION_VERTICAL
, 0,
546 gtk_range_set_inverted (GTK_RANGE (priv
->volume_scale
), TRUE
);
547 label
= gtk_label_new (_("Volume"));
549 priv
->audio_input_adj
= gtk_range_get_adjustment (
550 GTK_RANGE (priv
->volume_scale
));
551 priv
->volume
= empathy_audio_src_get_volume (EMPATHY_GST_AUDIO_SRC
552 (priv
->audio_input
));
553 gtk_adjustment_set_value (priv
->audio_input_adj
, priv
->volume
* 100);
555 g_signal_connect (G_OBJECT (priv
->audio_input_adj
), "value-changed",
556 G_CALLBACK (empathy_streamed_media_window_mic_volume_changed_cb
), self
);
558 gtk_box_pack_start (GTK_BOX (vbox
), priv
->volume_scale
, TRUE
, TRUE
, 3);
559 gtk_box_pack_start (GTK_BOX (vbox
), label
, FALSE
, FALSE
, 3);
561 priv
->volume_progress_bar
= gtk_progress_bar_new ();
563 gtk_orientable_set_orientation (GTK_ORIENTABLE (priv
->volume_progress_bar
),
564 GTK_ORIENTATION_VERTICAL
);
566 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv
->volume_progress_bar
),
569 gtk_box_pack_start (GTK_BOX (hbox
), priv
->volume_progress_bar
, FALSE
, FALSE
,
576 create_video_output_widget (EmpathyStreamedMediaWindow
*self
)
578 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
581 g_assert (priv
->video_output
== NULL
);
582 g_assert (priv
->pipeline
!= NULL
);
584 bus
= gst_pipeline_get_bus (GST_PIPELINE (priv
->pipeline
));
585 priv
->video_output
= empathy_video_widget_new (bus
);
587 gtk_box_pack_start (GTK_BOX (priv
->remote_user_output_hbox
),
588 priv
->video_output
, TRUE
, TRUE
, 0);
590 gtk_widget_add_events (priv
->video_output
,
591 GDK_BUTTON_PRESS_MASK
| GDK_POINTER_MOTION_MASK
);
592 g_signal_connect (G_OBJECT (priv
->video_output
), "button-press-event",
593 G_CALLBACK (empathy_streamed_media_window_video_button_press_cb
), self
);
595 g_object_unref (bus
);
599 create_video_input (EmpathyStreamedMediaWindow
*self
)
601 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
603 g_assert (priv
->video_input
== NULL
);
604 priv
->video_input
= empathy_video_src_new ();
605 gst_object_ref (priv
->video_input
);
606 gst_object_sink (priv
->video_input
);
610 create_audio_input (EmpathyStreamedMediaWindow
*self
)
612 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
614 g_assert (priv
->audio_input
== NULL
);
615 priv
->audio_input
= empathy_audio_src_new ();
616 gst_object_ref (priv
->audio_input
);
617 gst_object_sink (priv
->audio_input
);
619 tp_g_signal_connect_object (priv
->audio_input
, "peak-level-changed",
620 G_CALLBACK (empathy_streamed_media_window_audio_input_level_changed_cb
),
625 add_video_preview_to_pipeline (EmpathyStreamedMediaWindow
*self
)
627 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
630 g_assert (priv
->video_preview
!= NULL
);
631 g_assert (priv
->pipeline
!= NULL
);
632 g_assert (priv
->video_input
!= NULL
);
633 g_assert (priv
->video_tee
!= NULL
);
635 preview
= empathy_video_widget_get_element (
636 EMPATHY_VIDEO_WIDGET (priv
->video_preview
));
638 if (!gst_bin_add (GST_BIN (priv
->pipeline
), priv
->video_input
))
640 g_warning ("Could not add video input to pipeline");
644 if (!gst_bin_add (GST_BIN (priv
->pipeline
), priv
->video_tee
))
646 g_warning ("Could not add video tee to pipeline");
650 if (!gst_bin_add (GST_BIN (priv
->pipeline
), preview
))
652 g_warning ("Could not add video preview to pipeline");
656 if (!gst_element_link (priv
->video_input
, priv
->video_tee
))
658 g_warning ("Could not link video input to video tee");
662 if (!gst_element_link (priv
->video_tee
, preview
))
664 g_warning ("Could not link video tee to video preview");
670 create_video_preview (EmpathyStreamedMediaWindow
*self
)
672 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
675 g_assert (priv
->video_preview
== NULL
);
676 g_assert (priv
->video_tee
== NULL
);
678 bus
= gst_pipeline_get_bus (GST_PIPELINE (priv
->pipeline
));
680 priv
->video_preview
= empathy_video_widget_new_with_size (bus
,
681 SELF_VIDEO_SECTION_WIDTH
, SELF_VIDEO_SECTION_HEIGTH
);
682 g_object_set (priv
->video_preview
,
688 gtk_box_pack_start (GTK_BOX (priv
->self_user_output_hbox
),
689 priv
->video_preview
, TRUE
, TRUE
, 0);
691 priv
->video_tee
= gst_element_factory_make ("tee", NULL
);
692 gst_object_ref (priv
->video_tee
);
693 gst_object_sink (priv
->video_tee
);
695 g_object_unref (bus
);
699 play_camera (EmpathyStreamedMediaWindow
*window
,
702 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
706 if (priv
->video_preview
== NULL
)
708 create_video_preview (window
);
709 add_video_preview_to_pipeline (window
);
713 state
= GST_STATE_PLAYING
;
715 state
= GST_STATE_NULL
;
717 preview
= empathy_video_widget_get_element (
718 EMPATHY_VIDEO_WIDGET (priv
->video_preview
));
720 gst_element_set_state (preview
, state
);
721 gst_element_set_state (priv
->video_input
, state
);
722 gst_element_set_state (priv
->video_tee
, state
);
726 display_video_preview (EmpathyStreamedMediaWindow
*self
,
729 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
733 /* Display the preview and hide the self avatar */
734 DEBUG ("Show video preview");
736 play_camera (self
, TRUE
);
737 gtk_widget_show (priv
->video_preview
);
738 gtk_widget_hide (priv
->self_user_avatar_widget
);
742 /* Display the self avatar and hide the preview */
743 DEBUG ("Show self avatar");
745 if (priv
->video_preview
!= NULL
)
747 gtk_widget_hide (priv
->video_preview
);
748 play_camera (self
, FALSE
);
750 gtk_widget_show (priv
->self_user_avatar_widget
);
755 empathy_streamed_media_window_set_state_connecting (EmpathyStreamedMediaWindow
*window
)
757 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
759 empathy_streamed_media_window_status_message (window
, _("Connecting…"));
760 priv
->call_state
= CONNECTING
;
763 empathy_sound_manager_start_playing (priv
->sound_mgr
, GTK_WIDGET (window
),
764 EMPATHY_SOUND_PHONE_OUTGOING
, MS_BETWEEN_RING
);
768 disable_camera (EmpathyStreamedMediaWindow
*self
)
770 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
772 if (priv
->camera_state
== CAMERA_STATE_OFF
)
775 DEBUG ("Disable camera");
777 display_video_preview (self
, FALSE
);
779 if (priv
->camera_state
== CAMERA_STATE_ON
)
780 empathy_streamed_media_window_set_send_video (self
, CAMERA_STATE_OFF
);
782 block_camera_control_signals (self
);
783 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
784 priv
->tool_button_camera_on
), FALSE
);
785 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
786 priv
->tool_button_camera_preview
), FALSE
);
788 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
789 priv
->tool_button_camera_off
), TRUE
);
790 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (priv
->action_camera_on
),
792 unblock_camera_control_signals (self
);
794 priv
->camera_state
= CAMERA_STATE_OFF
;
798 tool_button_camera_off_toggled_cb (GtkToggleToolButton
*toggle
,
799 EmpathyStreamedMediaWindow
*self
)
801 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
803 if (!gtk_toggle_tool_button_get_active (toggle
))
805 if (priv
->camera_state
== CAMERA_STATE_OFF
)
807 /* We can't change the state by disabling the button */
808 block_camera_control_signals (self
);
809 gtk_toggle_tool_button_set_active (toggle
, TRUE
);
810 unblock_camera_control_signals (self
);
816 disable_camera (self
);
820 enable_preview (EmpathyStreamedMediaWindow
*self
)
822 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
824 if (priv
->camera_state
== CAMERA_STATE_PREVIEW
)
827 DEBUG ("Enable preview");
829 if (priv
->camera_state
== CAMERA_STATE_ON
)
831 /* preview is already displayed so we just have to stop sending */
832 empathy_streamed_media_window_set_send_video (self
, CAMERA_STATE_PREVIEW
);
836 display_video_preview (self
, TRUE
);
839 block_camera_control_signals (self
);
840 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
841 priv
->tool_button_camera_off
), FALSE
);
842 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
843 priv
->tool_button_camera_on
), FALSE
);
845 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
846 priv
->tool_button_camera_preview
), TRUE
);
847 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (priv
->action_camera_on
),
848 CAMERA_STATE_PREVIEW
);
849 unblock_camera_control_signals (self
);
851 priv
->camera_state
= CAMERA_STATE_PREVIEW
;
855 tool_button_camera_preview_toggled_cb (GtkToggleToolButton
*toggle
,
856 EmpathyStreamedMediaWindow
*self
)
858 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
860 if (!gtk_toggle_tool_button_get_active (toggle
))
862 if (priv
->camera_state
== CAMERA_STATE_PREVIEW
)
864 /* We can't change the state by disabling the button */
865 block_camera_control_signals (self
);
866 gtk_toggle_tool_button_set_active (toggle
, TRUE
);
867 unblock_camera_control_signals (self
);
873 enable_preview (self
);
877 enable_camera (EmpathyStreamedMediaWindow
*self
)
879 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
881 if (priv
->camera_state
== CAMERA_STATE_ON
)
884 if (priv
->video_input
== NULL
)
886 DEBUG ("Can't enable camera, no input");
891 DEBUG ("Enable camera");
893 empathy_streamed_media_window_set_send_video (self
, CAMERA_STATE_ON
);
895 block_camera_control_signals (self
);
896 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
897 priv
->tool_button_camera_off
), FALSE
);
898 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
899 priv
->tool_button_camera_preview
), FALSE
);
901 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
902 priv
->tool_button_camera_on
), TRUE
);
903 gtk_radio_action_set_current_value (GTK_RADIO_ACTION (priv
->action_camera_on
),
905 unblock_camera_control_signals (self
);
907 priv
->camera_state
= CAMERA_STATE_ON
;
911 tool_button_camera_on_toggled_cb (GtkToggleToolButton
*toggle
,
912 EmpathyStreamedMediaWindow
*self
)
914 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
916 if (!gtk_toggle_tool_button_get_active (toggle
))
918 if (priv
->camera_state
== CAMERA_STATE_ON
)
920 /* We can't change the state by disabling the button */
921 block_camera_control_signals (self
);
922 gtk_toggle_tool_button_set_active (toggle
, TRUE
);
923 unblock_camera_control_signals (self
);
929 enable_camera (self
);
933 action_camera_change_cb (GtkRadioAction
*action
,
934 GtkRadioAction
*current
,
935 EmpathyStreamedMediaWindow
*self
)
939 state
= gtk_radio_action_get_current_value (current
);
943 case CAMERA_STATE_OFF
:
944 disable_camera (self
);
947 case CAMERA_STATE_PREVIEW
:
948 enable_preview (self
);
951 case CAMERA_STATE_ON
:
952 enable_camera (self
);
956 g_assert_not_reached ();
961 create_pipeline (EmpathyStreamedMediaWindow
*self
)
963 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
966 g_assert (priv
->pipeline
== NULL
);
968 priv
->pipeline
= gst_pipeline_new (NULL
);
969 priv
->pipeline_playing
= FALSE
;
971 bus
= gst_pipeline_get_bus (GST_PIPELINE (priv
->pipeline
));
972 priv
->bus_message_source_id
= gst_bus_add_watch (bus
,
973 empathy_streamed_media_window_bus_message
, self
);
975 g_object_unref (bus
);
980 empathy_streamed_media_window_init (EmpathyStreamedMediaWindow
*self
)
982 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
991 filename
= empathy_file_lookup ("empathy-streamed-media-window.ui", "src");
992 gui
= empathy_builder_get_file (filename
,
993 "call_window_vbox", &top_vbox
,
994 "errors_vbox", &priv
->errors_vbox
,
996 "statusbar", &priv
->statusbar
,
997 "redial", &priv
->redial_button
,
998 "microphone", &priv
->mic_button
,
999 "toolbar", &priv
->toolbar
,
1000 "menuredial", &priv
->redial
,
1001 "ui_manager", &priv
->ui_manager
,
1002 "menufullscreen", &priv
->menu_fullscreen
,
1003 "camera_off", &priv
->tool_button_camera_off
,
1004 "camera_preview", &priv
->tool_button_camera_preview
,
1005 "camera_on", &priv
->tool_button_camera_on
,
1006 "action_camera_on", &priv
->action_camera_on
,
1007 "details_vbox", &priv
->details_vbox
,
1008 "vcodec_encoding_label", &priv
->vcodec_encoding_label
,
1009 "acodec_encoding_label", &priv
->acodec_encoding_label
,
1010 "acodec_decoding_label", &priv
->acodec_decoding_label
,
1011 "vcodec_decoding_label", &priv
->vcodec_decoding_label
,
1012 "audio_remote_candidate_label", &priv
->audio_remote_candidate_label
,
1013 "audio_local_candidate_label", &priv
->audio_local_candidate_label
,
1014 "video_remote_candidate_label", &priv
->video_remote_candidate_label
,
1015 "video_local_candidate_label", &priv
->video_local_candidate_label
,
1016 "video_remote_candidate_info_img", &priv
->video_remote_candidate_info_img
,
1017 "video_local_candidate_info_img", &priv
->video_local_candidate_info_img
,
1018 "audio_remote_candidate_info_img", &priv
->audio_remote_candidate_info_img
,
1019 "audio_local_candidate_info_img", &priv
->audio_local_candidate_info_img
,
1023 empathy_builder_connect (gui
, self
,
1024 "menuhangup", "activate", empathy_streamed_media_window_hangup_cb
,
1025 "hangup", "clicked", empathy_streamed_media_window_hangup_cb
,
1026 "menuredial", "activate", empathy_streamed_media_window_redial_cb
,
1027 "redial", "clicked", empathy_streamed_media_window_redial_cb
,
1028 "microphone", "toggled", empathy_streamed_media_window_mic_toggled_cb
,
1029 "menufullscreen", "activate", empathy_streamed_media_window_fullscreen_cb
,
1030 "camera_off", "toggled", tool_button_camera_off_toggled_cb
,
1031 "camera_preview", "toggled", tool_button_camera_preview_toggled_cb
,
1032 "camera_on", "toggled", tool_button_camera_on_toggled_cb
,
1033 "action_camera_on", "changed", action_camera_change_cb
,
1036 gtk_action_set_sensitive (priv
->menu_fullscreen
, FALSE
);
1038 priv
->lock
= g_mutex_new ();
1040 gtk_container_add (GTK_CONTAINER (self
), top_vbox
);
1042 priv
->content_hbox
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
,
1043 CONTENT_HBOX_SPACING
);
1044 gtk_container_set_border_width (GTK_CONTAINER (priv
->content_hbox
),
1045 CONTENT_HBOX_BORDER_WIDTH
);
1046 gtk_paned_pack1 (GTK_PANED (priv
->pane
), priv
->content_hbox
, TRUE
, FALSE
);
1048 /* remote user output frame */
1049 priv
->remote_user_output_frame
= gtk_frame_new (NULL
);
1050 gtk_widget_set_size_request (priv
->remote_user_output_frame
,
1051 EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH
, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT
);
1052 gtk_box_pack_start (GTK_BOX (priv
->content_hbox
),
1053 priv
->remote_user_output_frame
, TRUE
, TRUE
,
1054 CONTENT_HBOX_CHILDREN_PACKING_PADDING
);
1056 priv
->remote_user_output_hbox
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 0);
1058 priv
->remote_user_avatar_widget
= gtk_image_new ();
1060 gtk_box_pack_start (GTK_BOX (priv
->remote_user_output_hbox
),
1061 priv
->remote_user_avatar_widget
, TRUE
, TRUE
, 0);
1063 gtk_container_add (GTK_CONTAINER (priv
->remote_user_output_frame
),
1064 priv
->remote_user_output_hbox
);
1066 /* self user output frame */
1067 priv
->self_user_output_frame
= gtk_frame_new (NULL
);
1068 gtk_widget_set_size_request (priv
->self_user_output_frame
,
1069 SELF_VIDEO_SECTION_WIDTH
, SELF_VIDEO_SECTION_HEIGTH
);
1071 priv
->self_user_output_hbox
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 0);
1073 priv
->self_user_avatar_widget
= gtk_image_new ();
1074 gtk_box_pack_start (GTK_BOX (priv
->self_user_output_hbox
),
1075 priv
->self_user_avatar_widget
, TRUE
, TRUE
, 0);
1077 gtk_container_add (GTK_CONTAINER (priv
->self_user_output_frame
),
1078 priv
->self_user_output_hbox
);
1080 create_pipeline (self
);
1081 create_video_output_widget (self
);
1082 create_audio_input (self
);
1083 create_video_input (self
);
1085 priv
->fsnotifier
= fs_element_added_notifier_new ();
1086 fs_element_added_notifier_add (priv
->fsnotifier
, GST_BIN (priv
->pipeline
));
1088 /* The call will be started as soon the pipeline is playing */
1089 priv
->start_call_when_playing
= TRUE
;
1091 priv
->vbox
= gtk_box_new (GTK_ORIENTATION_VERTICAL
, 3);
1092 gtk_box_pack_start (GTK_BOX (priv
->content_hbox
), priv
->vbox
,
1093 FALSE
, FALSE
, CONTENT_HBOX_CHILDREN_PACKING_PADDING
);
1094 gtk_box_pack_start (GTK_BOX (priv
->vbox
), priv
->self_user_output_frame
,
1097 empathy_streamed_media_window_setup_toolbar (self
);
1099 priv
->sidebar_button
= gtk_toggle_button_new_with_mnemonic (_("_Sidebar"));
1100 arrow
= gtk_arrow_new (GTK_ARROW_RIGHT
, GTK_SHADOW_NONE
);
1101 g_signal_connect (G_OBJECT (priv
->sidebar_button
), "toggled",
1102 G_CALLBACK (empathy_streamed_media_window_sidebar_toggled_cb
), self
);
1104 gtk_button_set_image (GTK_BUTTON (priv
->sidebar_button
), arrow
);
1106 h
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 3);
1107 gtk_box_pack_end (GTK_BOX (priv
->vbox
), h
, FALSE
, FALSE
, 3);
1108 gtk_box_pack_end (GTK_BOX (h
), priv
->sidebar_button
, FALSE
, FALSE
, 3);
1110 priv
->sidebar
= ev_sidebar_new ();
1111 g_signal_connect (G_OBJECT (priv
->sidebar
),
1112 "hide", G_CALLBACK (empathy_streamed_media_window_sidebar_hidden_cb
), self
);
1113 g_signal_connect (G_OBJECT (priv
->sidebar
),
1114 "show", G_CALLBACK (empathy_streamed_media_window_sidebar_shown_cb
), self
);
1115 gtk_paned_pack2 (GTK_PANED (priv
->pane
), priv
->sidebar
, FALSE
, FALSE
);
1117 page
= empathy_streamed_media_window_create_audio_input (self
);
1118 ev_sidebar_add_page (EV_SIDEBAR (priv
->sidebar
), "audio-input",
1119 _("Audio input"), page
);
1121 page
= empathy_streamed_media_window_create_video_input (self
);
1122 ev_sidebar_add_page (EV_SIDEBAR (priv
->sidebar
), "video-input",
1123 _("Video input"), page
);
1125 priv
->dtmf_panel
= empathy_dialpad_widget_new ();
1126 g_signal_connect (priv
->dtmf_panel
, "start-tone",
1127 G_CALLBACK (dtmf_start_tone_cb
), self
);
1128 g_signal_connect (priv
->dtmf_panel
, "stop-tone",
1129 G_CALLBACK (dtmf_stop_tone_cb
), self
);
1130 ev_sidebar_add_page (EV_SIDEBAR (priv
->sidebar
), "dialpad",
1131 _("Dialpad"), priv
->dtmf_panel
);
1133 gtk_widget_set_sensitive (priv
->dtmf_panel
, FALSE
);
1135 /* Put the details vbox in a scroll window as it can require a lot of
1136 * horizontal space. */
1137 scroll
= gtk_scrolled_window_new (NULL
, NULL
);
1138 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scroll
),
1139 priv
->details_vbox
);
1141 ev_sidebar_add_page (EV_SIDEBAR (priv
->sidebar
), "details",
1142 _("Details"), scroll
);
1144 gtk_widget_show_all (top_vbox
);
1146 gtk_widget_hide (priv
->sidebar
);
1148 priv
->fullscreen
= empathy_streamed_media_window_fullscreen_new (self
);
1149 empathy_streamed_media_window_fullscreen_set_video_widget (priv
->fullscreen
,
1150 priv
->video_output
);
1151 g_signal_connect (G_OBJECT (priv
->fullscreen
->leave_fullscreen_button
),
1152 "clicked", G_CALLBACK (empathy_streamed_media_window_fullscreen_cb
), self
);
1154 g_signal_connect (G_OBJECT (self
), "realize",
1155 G_CALLBACK (empathy_streamed_media_window_realized_cb
), self
);
1157 g_signal_connect (G_OBJECT (self
), "delete-event",
1158 G_CALLBACK (empathy_streamed_media_window_delete_cb
), self
);
1160 g_signal_connect (G_OBJECT (self
), "window-state-event",
1161 G_CALLBACK (empathy_streamed_media_window_state_event_cb
), self
);
1163 g_signal_connect (G_OBJECT (self
), "key-press-event",
1164 G_CALLBACK (empathy_streamed_media_window_key_press_cb
), self
);
1166 priv
->timer
= g_timer_new ();
1168 g_object_ref (priv
->ui_manager
);
1169 g_object_unref (gui
);
1171 priv
->sound_mgr
= empathy_sound_manager_dup_singleton ();
1173 empathy_geometry_bind (GTK_WINDOW (self
), "av-window");
1176 /* Instead of specifying a width and a height, we specify only one size. That's
1177 because we want a square avatar icon. */
1179 init_contact_avatar_with_size (EmpathyContact
*contact
,
1180 GtkWidget
*image_widget
,
1183 GdkPixbuf
*pixbuf_avatar
= NULL
;
1185 if (contact
!= NULL
)
1187 pixbuf_avatar
= empathy_pixbuf_avatar_from_contact_scaled (contact
,
1191 if (pixbuf_avatar
== NULL
)
1193 pixbuf_avatar
= empathy_pixbuf_from_icon_name_sized (
1194 EMPATHY_IMAGE_AVATAR_DEFAULT
, size
);
1197 gtk_image_set_from_pixbuf (GTK_IMAGE (image_widget
), pixbuf_avatar
);
1199 if (pixbuf_avatar
!= NULL
)
1200 g_object_unref (pixbuf_avatar
);
1204 set_window_title (EmpathyStreamedMediaWindow
*self
)
1206 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
1209 /* translators: Call is a noun and %s is the contact name. This string
1210 * is used in the window title */
1211 tmp
= g_strdup_printf (_("Call with %s"),
1212 empathy_contact_get_alias (priv
->contact
));
1213 gtk_window_set_title (GTK_WINDOW (self
), tmp
);
1218 contact_name_changed_cb (EmpathyContact
*contact
,
1219 GParamSpec
*pspec
, EmpathyStreamedMediaWindow
*self
)
1221 set_window_title (self
);
1225 contact_avatar_changed_cb (EmpathyContact
*contact
,
1226 GParamSpec
*pspec
, GtkWidget
*avatar_widget
)
1229 GtkAllocation allocation
;
1231 gtk_widget_get_allocation (avatar_widget
, &allocation
);
1232 size
= allocation
.height
;
1236 /* the widget is not allocated yet, set a default size */
1237 size
= MIN (REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT
,
1238 REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH
);
1241 init_contact_avatar_with_size (contact
, avatar_widget
, size
);
1245 empathy_streamed_media_window_got_self_contact_cb (TpConnection
*connection
,
1246 EmpathyContact
*contact
, const GError
*error
, gpointer user_data
,
1247 GObject
*weak_object
)
1249 EmpathyStreamedMediaWindow
*self
= EMPATHY_STREAMED_MEDIA_WINDOW (user_data
);
1250 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
1252 init_contact_avatar_with_size (contact
, priv
->self_user_avatar_widget
,
1253 MIN (SELF_VIDEO_SECTION_WIDTH
, SELF_VIDEO_SECTION_HEIGTH
));
1255 g_signal_connect (contact
, "notify::avatar",
1256 G_CALLBACK (contact_avatar_changed_cb
), priv
->self_user_avatar_widget
);
1260 empathy_streamed_media_window_setup_avatars (EmpathyStreamedMediaWindow
*self
,
1261 EmpathyStreamedMediaHandler
*handler
)
1263 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
1265 g_object_get (handler
, "contact", &(priv
->contact
), NULL
);
1267 if (priv
->contact
!= NULL
)
1269 TpConnection
*connection
;
1271 set_window_title (self
);
1273 g_signal_connect (priv
->contact
, "notify::name",
1274 G_CALLBACK (contact_name_changed_cb
), self
);
1275 g_signal_connect (priv
->contact
, "notify::avatar",
1276 G_CALLBACK (contact_avatar_changed_cb
),
1277 priv
->remote_user_avatar_widget
);
1279 /* Retreiving the self avatar */
1280 connection
= empathy_contact_get_connection (priv
->contact
);
1281 empathy_tp_contact_factory_get_from_handle (connection
,
1282 tp_connection_get_self_handle (connection
),
1283 empathy_streamed_media_window_got_self_contact_cb
, self
, NULL
, G_OBJECT (self
));
1287 g_warning ("call handler doesn't have a contact");
1288 /* translators: Call is a noun. This string is used in the window
1290 gtk_window_set_title (GTK_WINDOW (self
), _("Call"));
1292 /* Since we can't access the remote contact, we can't get a connection
1293 to it and can't get the self contact (and its avatar). This means
1294 that we have to manually set the self avatar. */
1295 init_contact_avatar_with_size (NULL
, priv
->self_user_avatar_widget
,
1296 MIN (SELF_VIDEO_SECTION_WIDTH
, SELF_VIDEO_SECTION_HEIGTH
));
1299 init_contact_avatar_with_size (priv
->contact
,
1300 priv
->remote_user_avatar_widget
,
1301 MIN (REMOTE_CONTACT_AVATAR_DEFAULT_WIDTH
,
1302 REMOTE_CONTACT_AVATAR_DEFAULT_HEIGHT
));
1304 /* The remote avatar is shown by default and will be hidden when we receive
1305 video from the remote side. */
1306 gtk_widget_hide (priv
->video_output
);
1307 gtk_widget_show (priv
->remote_user_avatar_widget
);
1311 update_send_codec (EmpathyStreamedMediaWindow
*self
,
1314 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
1321 codec
= empathy_streamed_media_handler_get_send_audio_codec (priv
->handler
);
1322 widget
= priv
->acodec_encoding_label
;
1326 codec
= empathy_streamed_media_handler_get_send_video_codec (priv
->handler
);
1327 widget
= priv
->vcodec_encoding_label
;
1333 tmp
= g_strdup_printf ("%s/%u", codec
->encoding_name
, codec
->clock_rate
);
1334 gtk_label_set_text (GTK_LABEL (widget
), tmp
);
1339 send_audio_codec_notify_cb (GObject
*object
,
1343 EmpathyStreamedMediaWindow
*self
= user_data
;
1345 update_send_codec (self
, TRUE
);
1349 send_video_codec_notify_cb (GObject
*object
,
1353 EmpathyStreamedMediaWindow
*self
= user_data
;
1355 update_send_codec (self
, FALSE
);
1359 update_recv_codec (EmpathyStreamedMediaWindow
*self
,
1362 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
1365 GString
*str
= NULL
;
1369 codecs
= empathy_streamed_media_handler_get_recv_audio_codecs (priv
->handler
);
1370 widget
= priv
->acodec_decoding_label
;
1374 codecs
= empathy_streamed_media_handler_get_recv_video_codecs (priv
->handler
);
1375 widget
= priv
->vcodec_decoding_label
;
1381 for (l
= codecs
; l
!= NULL
; l
= g_list_next (l
))
1383 FsCodec
*codec
= l
->data
;
1386 str
= g_string_new (NULL
);
1388 g_string_append (str
, ", ");
1390 g_string_append_printf (str
, "%s/%u", codec
->encoding_name
,
1394 gtk_label_set_text (GTK_LABEL (widget
), str
->str
);
1395 g_string_free (str
, TRUE
);
1399 recv_audio_codecs_notify_cb (GObject
*object
,
1403 EmpathyStreamedMediaWindow
*self
= user_data
;
1405 update_recv_codec (self
, TRUE
);
1409 recv_video_codecs_notify_cb (GObject
*object
,
1413 EmpathyStreamedMediaWindow
*self
= user_data
;
1415 update_recv_codec (self
, FALSE
);
1418 static const gchar
*
1419 candidate_type_to_str (FsCandidate
*candidate
)
1421 switch (candidate
->type
)
1423 case FS_CANDIDATE_TYPE_HOST
:
1425 case FS_CANDIDATE_TYPE_SRFLX
:
1426 return "server reflexive";
1427 case FS_CANDIDATE_TYPE_PRFLX
:
1428 return "peer reflexive";
1429 case FS_CANDIDATE_TYPE_RELAY
:
1431 case FS_CANDIDATE_TYPE_MULTICAST
:
1438 static const gchar
*
1439 candidate_type_to_desc (FsCandidate
*candidate
)
1441 switch (candidate
->type
)
1443 case FS_CANDIDATE_TYPE_HOST
:
1444 return _("The IP address as seen by the machine");
1445 case FS_CANDIDATE_TYPE_SRFLX
:
1446 return _("The IP address as seen by a server on the Internet");
1447 case FS_CANDIDATE_TYPE_PRFLX
:
1448 return _("The IP address of the peer as seen by the other side");
1449 case FS_CANDIDATE_TYPE_RELAY
:
1450 return _("The IP address of a relay server");
1451 case FS_CANDIDATE_TYPE_MULTICAST
:
1452 return _("The IP address of the multicast group");
1459 update_candidat_widget (EmpathyStreamedMediaWindow
*self
,
1462 FsCandidate
*candidate
)
1466 g_assert (candidate
!= NULL
);
1467 str
= g_strdup_printf ("%s %u (%s)", candidate
->ip
,
1468 candidate
->port
, candidate_type_to_str (candidate
));
1470 gtk_label_set_text (GTK_LABEL (label
), str
);
1471 gtk_widget_set_tooltip_text (img
, candidate_type_to_desc (candidate
));
1477 candidates_changed_cb (GObject
*object
,
1479 EmpathyStreamedMediaWindow
*self
)
1481 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
1482 FsCandidate
*candidate
= NULL
;
1484 if (type
== FS_MEDIA_TYPE_VIDEO
)
1486 /* Update remote candidate */
1487 candidate
= empathy_streamed_media_handler_get_video_remote_candidate (
1490 update_candidat_widget (self
, priv
->video_remote_candidate_label
,
1491 priv
->video_remote_candidate_info_img
, candidate
);
1493 /* Update local candidate */
1494 candidate
= empathy_streamed_media_handler_get_video_local_candidate (
1497 update_candidat_widget (self
, priv
->video_local_candidate_label
,
1498 priv
->video_local_candidate_info_img
, candidate
);
1502 /* Update remote candidate */
1503 candidate
= empathy_streamed_media_handler_get_audio_remote_candidate (
1506 update_candidat_widget (self
, priv
->audio_remote_candidate_label
,
1507 priv
->audio_remote_candidate_info_img
, candidate
);
1509 /* Update local candidate */
1510 candidate
= empathy_streamed_media_handler_get_audio_local_candidate (
1513 update_candidat_widget (self
, priv
->audio_local_candidate_label
,
1514 priv
->audio_local_candidate_info_img
, candidate
);
1519 empathy_streamed_media_window_constructed (GObject
*object
)
1521 EmpathyStreamedMediaWindow
*self
= EMPATHY_STREAMED_MEDIA_WINDOW (object
);
1522 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
1523 EmpathyTpStreamedMedia
*call
;
1525 g_assert (priv
->handler
!= NULL
);
1527 g_object_get (priv
->handler
, "tp-call", &call
, NULL
);
1528 priv
->outgoing
= (call
== NULL
);
1530 g_object_unref (call
);
1532 empathy_streamed_media_window_setup_avatars (self
, priv
->handler
);
1533 empathy_streamed_media_window_set_state_connecting (self
);
1535 if (!empathy_streamed_media_handler_has_initial_video (priv
->handler
))
1537 gtk_toggle_tool_button_set_active (
1538 GTK_TOGGLE_TOOL_BUTTON (priv
->tool_button_camera_off
), TRUE
);
1540 /* If call has InitialVideo, the preview will be started once the call has
1541 * been started (start_call()). */
1543 update_send_codec (self
, TRUE
);
1544 update_send_codec (self
, FALSE
);
1545 update_recv_codec (self
, TRUE
);
1546 update_recv_codec (self
, FALSE
);
1548 tp_g_signal_connect_object (priv
->handler
, "notify::send-audio-codec",
1549 G_CALLBACK (send_audio_codec_notify_cb
), self
, 0);
1550 tp_g_signal_connect_object (priv
->handler
, "notify::send-video-codec",
1551 G_CALLBACK (send_video_codec_notify_cb
), self
, 0);
1552 tp_g_signal_connect_object (priv
->handler
, "notify::recv-audio-codecs",
1553 G_CALLBACK (recv_audio_codecs_notify_cb
), self
, 0);
1554 tp_g_signal_connect_object (priv
->handler
, "notify::recv-video-codecs",
1555 G_CALLBACK (recv_video_codecs_notify_cb
), self
, 0);
1557 tp_g_signal_connect_object (priv
->handler
, "candidates-changed",
1558 G_CALLBACK (candidates_changed_cb
), self
, 0);
1561 static void empathy_streamed_media_window_dispose (GObject
*object
);
1562 static void empathy_streamed_media_window_finalize (GObject
*object
);
1565 empathy_streamed_media_window_set_property (GObject
*object
,
1566 guint property_id
, const GValue
*value
, GParamSpec
*pspec
)
1568 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (object
);
1570 switch (property_id
)
1572 case PROP_STREAMED_MEDIA_HANDLER
:
1573 priv
->handler
= g_value_dup_object (value
);
1576 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
1581 empathy_streamed_media_window_get_property (GObject
*object
,
1582 guint property_id
, GValue
*value
, GParamSpec
*pspec
)
1584 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (object
);
1586 switch (property_id
)
1588 case PROP_STREAMED_MEDIA_HANDLER
:
1589 g_value_set_object (value
, priv
->handler
);
1592 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
1597 empathy_streamed_media_window_class_init (
1598 EmpathyStreamedMediaWindowClass
*empathy_streamed_media_window_class
)
1600 GObjectClass
*object_class
= G_OBJECT_CLASS (empathy_streamed_media_window_class
);
1601 GParamSpec
*param_spec
;
1603 g_type_class_add_private (empathy_streamed_media_window_class
,
1604 sizeof (EmpathyStreamedMediaWindowPriv
));
1606 object_class
->constructed
= empathy_streamed_media_window_constructed
;
1607 object_class
->set_property
= empathy_streamed_media_window_set_property
;
1608 object_class
->get_property
= empathy_streamed_media_window_get_property
;
1610 object_class
->dispose
= empathy_streamed_media_window_dispose
;
1611 object_class
->finalize
= empathy_streamed_media_window_finalize
;
1613 param_spec
= g_param_spec_object ("handler",
1614 "handler", "The call handler",
1615 EMPATHY_TYPE_STREAMED_MEDIA_HANDLER
,
1616 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
| G_PARAM_STATIC_STRINGS
);
1617 g_object_class_install_property (object_class
,
1618 PROP_STREAMED_MEDIA_HANDLER
, param_spec
);
1622 empathy_streamed_media_window_video_stream_changed_cb (EmpathyTpStreamedMedia
*call
,
1623 GParamSpec
*property
, EmpathyStreamedMediaWindow
*self
)
1625 DEBUG ("video stream changed");
1626 empathy_streamed_media_window_update_avatars_visibility (call
, self
);
1630 empathy_streamed_media_window_dispose (GObject
*object
)
1632 EmpathyTpStreamedMedia
*call
;
1633 EmpathyStreamedMediaWindow
*self
= EMPATHY_STREAMED_MEDIA_WINDOW (object
);
1634 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
1636 if (priv
->dispose_has_run
)
1639 priv
->dispose_has_run
= TRUE
;
1641 g_object_get (priv
->handler
, "tp-call", &call
, NULL
);
1645 g_object_unref (call
);
1648 if (priv
->handler
!= NULL
)
1650 empathy_streamed_media_handler_stop_call (priv
->handler
);
1651 g_object_unref (priv
->handler
);
1653 priv
->handler
= NULL
;
1655 if (priv
->bus_message_source_id
!= 0)
1657 g_source_remove (priv
->bus_message_source_id
);
1658 priv
->bus_message_source_id
= 0;
1661 if (priv
->pipeline
!= NULL
)
1662 g_object_unref (priv
->pipeline
);
1663 priv
->pipeline
= NULL
;
1665 if (priv
->video_input
!= NULL
)
1666 g_object_unref (priv
->video_input
);
1667 priv
->video_input
= NULL
;
1669 if (priv
->audio_input
!= NULL
)
1670 g_object_unref (priv
->audio_input
);
1671 priv
->audio_input
= NULL
;
1673 if (priv
->video_tee
!= NULL
)
1674 g_object_unref (priv
->video_tee
);
1675 priv
->video_tee
= NULL
;
1677 if (priv
->fsnotifier
!= NULL
)
1678 g_object_unref (priv
->fsnotifier
);
1679 priv
->fsnotifier
= NULL
;
1681 if (priv
->timer_id
!= 0)
1682 g_source_remove (priv
->timer_id
);
1685 if (priv
->ui_manager
!= NULL
)
1686 g_object_unref (priv
->ui_manager
);
1687 priv
->ui_manager
= NULL
;
1689 if (priv
->fullscreen
!= NULL
)
1690 g_object_unref (priv
->fullscreen
);
1691 priv
->fullscreen
= NULL
;
1693 if (priv
->contact
!= NULL
)
1695 g_signal_handlers_disconnect_by_func (priv
->contact
,
1696 contact_name_changed_cb
, self
);
1697 g_object_unref (priv
->contact
);
1698 priv
->contact
= NULL
;
1701 tp_clear_object (&priv
->sound_mgr
);
1703 /* release any references held by the object here */
1704 if (G_OBJECT_CLASS (empathy_streamed_media_window_parent_class
)->dispose
)
1705 G_OBJECT_CLASS (empathy_streamed_media_window_parent_class
)->dispose (object
);
1709 disconnect_video_output_motion_handler (EmpathyStreamedMediaWindow
*self
)
1711 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
1713 if (priv
->video_output_motion_handler_id
!= 0)
1715 g_signal_handler_disconnect (G_OBJECT (priv
->video_output
),
1716 priv
->video_output_motion_handler_id
);
1717 priv
->video_output_motion_handler_id
= 0;
1722 empathy_streamed_media_window_finalize (GObject
*object
)
1724 EmpathyStreamedMediaWindow
*self
= EMPATHY_STREAMED_MEDIA_WINDOW (object
);
1725 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
1727 disconnect_video_output_motion_handler (self
);
1729 /* free any data held directly by the object here */
1730 g_mutex_free (priv
->lock
);
1732 g_timer_destroy (priv
->timer
);
1734 G_OBJECT_CLASS (empathy_streamed_media_window_parent_class
)->finalize (object
);
1738 EmpathyStreamedMediaWindow
*
1739 empathy_streamed_media_window_new (EmpathyStreamedMediaHandler
*handler
)
1741 return EMPATHY_STREAMED_MEDIA_WINDOW (
1742 g_object_new (EMPATHY_TYPE_STREAMED_MEDIA_WINDOW
, "handler", handler
, NULL
));
1746 empathy_streamed_media_window_conference_added_cb (EmpathyStreamedMediaHandler
*handler
,
1747 GstElement
*conference
, gpointer user_data
)
1749 EmpathyStreamedMediaWindow
*self
= EMPATHY_STREAMED_MEDIA_WINDOW (user_data
);
1750 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
1753 keyfile
= fs_utils_get_default_element_properties (conference
);
1754 if (keyfile
!= NULL
)
1755 fs_element_added_notifier_set_properties_from_keyfile (priv
->fsnotifier
,
1758 gst_bin_add (GST_BIN (priv
->pipeline
), conference
);
1760 gst_element_set_state (conference
, GST_STATE_PLAYING
);
1764 empathy_streamed_media_window_start_receiving_cb (EmpathyStreamedMediaHandler
*handler
,
1765 FsMediaType type
,gpointer user_data
)
1767 EmpathyStreamedMediaWindow
*self
= EMPATHY_STREAMED_MEDIA_WINDOW (user_data
);
1768 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
1770 if (type
!= FS_MEDIA_TYPE_VIDEO
)
1773 /* video and direction is send */
1774 return priv
->video_input
!= NULL
;
1778 empathy_streamed_media_window_reset_pipeline (EmpathyStreamedMediaWindow
*self
)
1780 GstStateChangeReturn state_change_return
;
1781 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
1783 if (priv
->pipeline
== NULL
)
1786 if (priv
->bus_message_source_id
!= 0)
1788 g_source_remove (priv
->bus_message_source_id
);
1789 priv
->bus_message_source_id
= 0;
1792 state_change_return
= gst_element_set_state (priv
->pipeline
, GST_STATE_NULL
);
1794 if (state_change_return
== GST_STATE_CHANGE_SUCCESS
||
1795 state_change_return
== GST_STATE_CHANGE_NO_PREROLL
)
1797 if (priv
->pipeline
!= NULL
)
1798 g_object_unref (priv
->pipeline
);
1799 priv
->pipeline
= NULL
;
1801 g_signal_handlers_disconnect_by_func (priv
->audio_input_adj
,
1802 empathy_streamed_media_window_mic_volume_changed_cb
, self
);
1804 if (priv
->video_tee
!= NULL
)
1805 g_object_unref (priv
->video_tee
);
1806 priv
->video_tee
= NULL
;
1808 if (priv
->video_preview
!= NULL
)
1809 gtk_widget_destroy (priv
->video_preview
);
1810 priv
->video_preview
= NULL
;
1812 priv
->funnel
= NULL
;
1814 create_pipeline (self
);
1815 /* Call will be started when user will hit the 'redial' button */
1816 priv
->start_call_when_playing
= FALSE
;
1817 gst_element_set_state (priv
->pipeline
, GST_STATE_PAUSED
);
1823 g_message ("Error: could not destroy pipeline. Closing call window");
1824 gtk_widget_destroy (GTK_WIDGET (self
));
1831 reset_details_pane (EmpathyStreamedMediaWindow
*self
)
1833 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
1835 /* translators: encoding video codec is unknown */
1836 gtk_label_set_text (GTK_LABEL (priv
->vcodec_encoding_label
),
1837 C_("codec", "Unknown"));
1838 /* translators: encoding audio codec is unknown */
1839 gtk_label_set_text (GTK_LABEL (priv
->acodec_encoding_label
),
1840 C_("codec", "Unknown"));
1841 /* translators: decoding video codec is unknown */
1842 gtk_label_set_text (GTK_LABEL (priv
->vcodec_decoding_label
),
1843 C_("codec", "Unknown"));
1844 /* translators: decoding audio codec is unknown */
1845 gtk_label_set_text (GTK_LABEL (priv
->acodec_decoding_label
),
1846 C_("codec", "Unknown"));
1850 empathy_streamed_media_window_disconnected (EmpathyStreamedMediaWindow
*self
,
1853 gboolean could_disconnect
= FALSE
;
1854 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
1855 gboolean could_reset_pipeline
;
1857 /* Leave full screen mode if needed */
1858 gtk_window_unfullscreen (GTK_WINDOW (self
));
1860 gtk_action_set_sensitive (priv
->menu_fullscreen
, FALSE
);
1862 could_reset_pipeline
= empathy_streamed_media_window_reset_pipeline (self
);
1864 if (priv
->call_state
== CONNECTING
)
1865 empathy_sound_manager_stop (priv
->sound_mgr
,
1866 EMPATHY_SOUND_PHONE_OUTGOING
);
1868 if (priv
->call_state
!= REDIALING
)
1869 priv
->call_state
= DISCONNECTED
;
1871 if (could_reset_pipeline
)
1873 g_mutex_lock (priv
->lock
);
1875 g_timer_stop (priv
->timer
);
1877 if (priv
->timer_id
!= 0)
1878 g_source_remove (priv
->timer_id
);
1881 g_mutex_unlock (priv
->lock
);
1884 /* We are about to destroy the window, no need to update it or create
1885 * a video preview */
1888 empathy_streamed_media_window_status_message (self
, _("Disconnected"));
1890 gtk_action_set_sensitive (priv
->redial
, TRUE
);
1891 gtk_widget_set_sensitive (priv
->redial_button
, TRUE
);
1893 /* Unsensitive the camera and mic button */
1894 gtk_widget_set_sensitive (priv
->tool_button_camera_on
, FALSE
);
1895 gtk_action_set_sensitive (priv
->action_camera_on
, FALSE
);
1896 gtk_widget_set_sensitive (priv
->mic_button
, FALSE
);
1898 /* Be sure that the mic button is enabled */
1899 gtk_toggle_tool_button_set_active (
1900 GTK_TOGGLE_TOOL_BUTTON (priv
->mic_button
), TRUE
);
1902 if (priv
->camera_state
== CAMERA_STATE_ON
)
1904 /* Enable the 'preview' button as we are not sending atm. */
1905 gtk_toggle_tool_button_set_active (
1906 GTK_TOGGLE_TOOL_BUTTON (priv
->tool_button_camera_preview
), TRUE
);
1908 else if (priv
->camera_state
== CAMERA_STATE_PREVIEW
)
1910 /* Restart the preview with the new pipeline. */
1911 display_video_preview (self
, TRUE
);
1914 gtk_progress_bar_set_fraction (
1915 GTK_PROGRESS_BAR (priv
->volume_progress_bar
), 0);
1917 /* destroy the video output; it will be recreated when we'll redial */
1918 disconnect_video_output_motion_handler (self
);
1919 gtk_widget_destroy (priv
->video_output
);
1920 priv
->video_output
= NULL
;
1922 gtk_widget_show (priv
->remote_user_avatar_widget
);
1924 reset_details_pane (self
);
1926 priv
->sending_video
= FALSE
;
1927 priv
->call_started
= FALSE
;
1929 could_disconnect
= TRUE
;
1931 /* TODO: display the self avatar of the preview (depends if the "Always
1932 * Show Video Preview" is enabled or not) */
1935 return could_disconnect
;
1940 empathy_streamed_media_window_channel_closed_cb (EmpathyStreamedMediaHandler
*handler
,
1943 EmpathyStreamedMediaWindow
*self
= EMPATHY_STREAMED_MEDIA_WINDOW (user_data
);
1944 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
1946 if (empathy_streamed_media_window_disconnected (self
, TRUE
) &&
1947 priv
->call_state
== REDIALING
)
1948 empathy_streamed_media_window_restart_call (self
);
1953 empathy_streamed_media_window_channel_stream_closed_cb (EmpathyStreamedMediaHandler
*handler
,
1954 TfContent
*content
, gpointer user_data
)
1956 EmpathyStreamedMediaWindow
*self
= EMPATHY_STREAMED_MEDIA_WINDOW (user_data
);
1957 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
1960 g_object_get (content
, "media-type", &media_type
, NULL
);
1963 * This assumes that there is only one video stream per channel...
1966 if (media_type
== TP_MEDIA_STREAM_TYPE_VIDEO
)
1968 if (priv
->funnel
!= NULL
)
1972 output
= empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
1973 (priv
->video_output
));
1975 gst_element_set_state (output
, GST_STATE_NULL
);
1976 gst_element_set_state (priv
->funnel
, GST_STATE_NULL
);
1978 gst_bin_remove (GST_BIN (priv
->pipeline
), output
);
1979 gst_bin_remove (GST_BIN (priv
->pipeline
), priv
->funnel
);
1980 priv
->funnel
= NULL
;
1983 else if (media_type
== TP_MEDIA_STREAM_TYPE_AUDIO
)
1985 if (priv
->audio_output
!= NULL
)
1987 gst_element_set_state (priv
->audio_output
, GST_STATE_NULL
);
1989 gst_bin_remove (GST_BIN (priv
->pipeline
), priv
->audio_output
);
1990 priv
->audio_output
= NULL
;
1995 /* Called with global lock held */
1997 empathy_streamed_media_window_get_video_sink_pad (EmpathyStreamedMediaWindow
*self
)
1999 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
2003 if (priv
->funnel
== NULL
)
2005 output
= empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
2006 (priv
->video_output
));
2008 priv
->funnel
= gst_element_factory_make ("fsfunnel", NULL
);
2012 g_warning ("Could not create fsfunnel");
2016 if (!gst_bin_add (GST_BIN (priv
->pipeline
), priv
->funnel
))
2018 gst_object_unref (priv
->funnel
);
2019 priv
->funnel
= NULL
;
2020 g_warning ("Could not add funnel to pipeline");
2024 if (!gst_bin_add (GST_BIN (priv
->pipeline
), output
))
2026 g_warning ("Could not add the video output widget to the pipeline");
2030 if (!gst_element_link (priv
->funnel
, output
))
2032 g_warning ("Could not link output sink to funnel");
2033 goto error_output_added
;
2036 if (gst_element_set_state (output
, GST_STATE_PLAYING
) == GST_STATE_CHANGE_FAILURE
)
2038 g_warning ("Could not start video sink");
2039 goto error_output_added
;
2042 if (gst_element_set_state (priv
->funnel
, GST_STATE_PLAYING
) == GST_STATE_CHANGE_FAILURE
)
2044 g_warning ("Could not start funnel");
2045 goto error_output_added
;
2049 pad
= gst_element_get_request_pad (priv
->funnel
, "sink%d");
2052 g_warning ("Could not get request pad from funnel");
2059 gst_element_set_locked_state (priv
->funnel
, TRUE
);
2060 gst_element_set_locked_state (output
, TRUE
);
2062 gst_element_set_state (priv
->funnel
, GST_STATE_NULL
);
2063 gst_element_set_state (output
, GST_STATE_NULL
);
2065 gst_bin_remove (GST_BIN (priv
->pipeline
), output
);
2066 gst_element_set_locked_state (output
, FALSE
);
2070 gst_bin_remove (GST_BIN (priv
->pipeline
), priv
->funnel
);
2071 priv
->funnel
= NULL
;
2076 /* Called with global lock held */
2078 empathy_streamed_media_window_get_audio_sink_pad (EmpathyStreamedMediaWindow
*self
)
2080 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
2082 GstPadTemplate
*template;
2084 if (priv
->audio_output
== NULL
)
2086 priv
->audio_output
= empathy_audio_sink_new ();
2088 if (!gst_bin_add (GST_BIN (priv
->pipeline
), priv
->audio_output
))
2090 g_warning ("Could not add audio sink to pipeline");
2091 g_object_unref (priv
->audio_output
);
2092 goto error_add_output
;
2095 if (gst_element_set_state (priv
->audio_output
, GST_STATE_PLAYING
) == GST_STATE_CHANGE_FAILURE
)
2097 g_warning ("Could not start audio sink");
2102 template = gst_element_class_get_pad_template (
2103 GST_ELEMENT_GET_CLASS (priv
->audio_output
), "sink%d");
2105 pad
= gst_element_request_pad (priv
->audio_output
,
2106 template, NULL
, NULL
);
2110 g_warning ("Could not get sink pad from sink");
2117 gst_element_set_locked_state (priv
->audio_output
, TRUE
);
2118 gst_element_set_state (priv
->audio_output
, GST_STATE_NULL
);
2119 gst_bin_remove (GST_BIN (priv
->pipeline
), priv
->audio_output
);
2120 priv
->audio_output
= NULL
;
2128 empathy_streamed_media_window_update_timer (gpointer user_data
)
2130 EmpathyStreamedMediaWindow
*self
= EMPATHY_STREAMED_MEDIA_WINDOW (user_data
);
2131 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
2135 time_
= g_timer_elapsed (priv
->timer
, NULL
);
2137 /* Translators: number of minutes:seconds the caller has been connected */
2138 str
= g_strdup_printf (_("Connected — %d:%02dm"), (int) time_
/ 60,
2140 empathy_streamed_media_window_status_message (self
, str
);
2147 display_error (EmpathyStreamedMediaWindow
*self
,
2148 EmpathyTpStreamedMedia
*call
,
2152 const gchar
*details
)
2154 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
2155 GtkWidget
*info_bar
;
2156 GtkWidget
*content_area
;
2163 /* Create info bar */
2164 info_bar
= gtk_info_bar_new_with_buttons (GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
,
2167 gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar
), GTK_MESSAGE_WARNING
);
2169 content_area
= gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar
));
2171 /* hbox containing the image and the messages vbox */
2172 hbox
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 3);
2173 gtk_container_add (GTK_CONTAINER (content_area
), hbox
);
2176 image
= gtk_image_new_from_icon_name (img
, GTK_ICON_SIZE_DIALOG
);
2177 gtk_box_pack_start (GTK_BOX (hbox
), image
, FALSE
, FALSE
, 0);
2179 /* vbox containing the main message and the details expander */
2180 vbox
= gtk_box_new (GTK_ORIENTATION_VERTICAL
, 3);
2181 gtk_box_pack_start (GTK_BOX (hbox
), vbox
, TRUE
, TRUE
, 0);
2184 txt
= g_strdup_printf ("<b>%s</b>\n%s", title
, desc
);
2186 label
= gtk_label_new (NULL
);
2187 gtk_label_set_markup (GTK_LABEL (label
), txt
);
2188 gtk_label_set_line_wrap (GTK_LABEL (label
), TRUE
);
2189 gtk_misc_set_alignment (GTK_MISC (label
), 0, 0);
2192 gtk_box_pack_start (GTK_BOX (vbox
), label
, TRUE
, TRUE
, 0);
2195 if (details
!= NULL
)
2197 GtkWidget
*expander
;
2199 expander
= gtk_expander_new (_("Technical Details"));
2201 txt
= g_strdup_printf ("<i>%s</i>", details
);
2203 label
= gtk_label_new (NULL
);
2204 gtk_label_set_markup (GTK_LABEL (label
), txt
);
2205 gtk_label_set_line_wrap (GTK_LABEL (label
), TRUE
);
2206 gtk_misc_set_alignment (GTK_MISC (label
), 0, 0);
2209 gtk_container_add (GTK_CONTAINER (expander
), label
);
2210 gtk_box_pack_start (GTK_BOX (vbox
), expander
, TRUE
, TRUE
, 0);
2213 g_signal_connect (info_bar
, "response",
2214 G_CALLBACK (gtk_widget_destroy
), NULL
);
2216 gtk_box_pack_start (GTK_BOX (priv
->errors_vbox
), info_bar
,
2217 FALSE
, FALSE
, CONTENT_HBOX_CHILDREN_PACKING_PADDING
);
2218 gtk_widget_show_all (info_bar
);
2222 media_stream_error_to_txt (EmpathyStreamedMediaWindow
*self
,
2223 EmpathyTpStreamedMedia
*call
,
2225 TpMediaStreamError error
)
2227 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
2234 case TP_MEDIA_STREAM_ERROR_CODEC_NEGOTIATION_FAILED
:
2236 return g_strdup_printf (
2237 _("%s's software does not understand any of the audio formats "
2238 "supported by your computer"),
2239 empathy_contact_get_alias (priv
->contact
));
2241 return g_strdup_printf (
2242 _("%s's software does not understand any of the video formats "
2243 "supported by your computer"),
2244 empathy_contact_get_alias (priv
->contact
));
2246 case TP_MEDIA_STREAM_ERROR_CONNECTION_FAILED
:
2247 return g_strdup_printf (
2248 _("Can't establish a connection to %s. "
2249 "One of you might be on a network that does not allow "
2250 "direct connections."),
2251 empathy_contact_get_alias (priv
->contact
));
2253 case TP_MEDIA_STREAM_ERROR_NETWORK_ERROR
:
2254 return g_strdup (_("There was a failure on the network"));
2256 case TP_MEDIA_STREAM_ERROR_NO_CODECS
:
2258 return g_strdup (_("The audio formats necessary for this call "
2259 "are not installed on your computer"));
2261 return g_strdup (_("The video formats necessary for this call "
2262 "are not installed on your computer"));
2264 case TP_MEDIA_STREAM_ERROR_INVALID_CM_BEHAVIOR
:
2265 cm
= empathy_tp_streamed_media_get_connection_manager (call
);
2267 url
= g_strdup_printf ("http://bugs.freedesktop.org/enter_bug.cgi?"
2268 "product=Telepathy&component=%s", cm
);
2270 result
= g_strdup_printf (
2271 _("Something unexpected happened in a Telepathy component. "
2272 "Please <a href=\"%s\">report this bug</a> and attach "
2273 "logs gathered from the 'Debug' window in the Help menu."), url
);
2278 case TP_MEDIA_STREAM_ERROR_MEDIA_ERROR
:
2279 return g_strdup (_("There was a failure in the call engine"));
2281 case TP_MEDIA_STREAM_ERROR_EOS
:
2282 return g_strdup (_("The end of the stream was reached"));
2284 case TP_MEDIA_STREAM_ERROR_UNKNOWN
:
2291 empathy_streamed_media_window_stream_error (EmpathyStreamedMediaWindow
*self
,
2292 EmpathyTpStreamedMedia
*call
,
2301 desc
= media_stream_error_to_txt (self
, call
, audio
, code
);
2304 /* No description, use the error message. That's not great as it's not
2305 * localized but it's better than nothing. */
2306 display_error (self
, call
, icon
, title
, msg
, NULL
);
2310 display_error (self
, call
, icon
, title
, desc
, msg
);
2316 empathy_streamed_media_window_audio_stream_error (EmpathyTpStreamedMedia
*call
,
2319 EmpathyStreamedMediaWindow
*self
)
2321 empathy_streamed_media_window_stream_error (self
, call
, TRUE
, code
, msg
,
2322 "gnome-stock-mic", _("Can't establish audio stream"));
2326 empathy_streamed_media_window_video_stream_error (EmpathyTpStreamedMedia
*call
,
2329 EmpathyStreamedMediaWindow
*self
)
2331 empathy_streamed_media_window_stream_error (self
, call
, FALSE
, code
, msg
,
2332 "camera-web", _("Can't establish video stream"));
2336 empathy_streamed_media_window_connected (gpointer user_data
)
2338 EmpathyStreamedMediaWindow
*self
= EMPATHY_STREAMED_MEDIA_WINDOW (user_data
);
2339 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
2340 EmpathyTpStreamedMedia
*call
;
2341 gboolean can_send_video
;
2343 empathy_sound_manager_stop (priv
->sound_mgr
, EMPATHY_SOUND_PHONE_OUTGOING
);
2345 can_send_video
= priv
->video_input
!= NULL
&& priv
->contact
!= NULL
&&
2346 empathy_contact_can_voip_video (priv
->contact
);
2348 g_object_get (priv
->handler
, "tp-call", &call
, NULL
);
2350 tp_g_signal_connect_object (call
, "notify::video-stream",
2351 G_CALLBACK (empathy_streamed_media_window_video_stream_changed_cb
),
2354 if (empathy_tp_streamed_media_has_dtmf (call
))
2355 gtk_widget_set_sensitive (priv
->dtmf_panel
, TRUE
);
2357 if (priv
->video_input
== NULL
)
2358 empathy_streamed_media_window_set_send_video (self
, CAMERA_STATE_OFF
);
2360 priv
->sending_video
= can_send_video
?
2361 empathy_tp_streamed_media_is_sending_video (call
) : FALSE
;
2363 gtk_toggle_tool_button_set_active (
2364 GTK_TOGGLE_TOOL_BUTTON (priv
->tool_button_camera_on
),
2365 priv
->sending_video
&& priv
->video_input
!= NULL
);
2366 gtk_widget_set_sensitive (priv
->tool_button_camera_on
, can_send_video
);
2367 gtk_action_set_sensitive (priv
->action_camera_on
, can_send_video
);
2369 gtk_action_set_sensitive (priv
->redial
, FALSE
);
2370 gtk_widget_set_sensitive (priv
->redial_button
, FALSE
);
2372 gtk_widget_set_sensitive (priv
->mic_button
, TRUE
);
2374 empathy_streamed_media_window_update_avatars_visibility (call
, self
);
2376 g_object_unref (call
);
2378 g_mutex_lock (priv
->lock
);
2380 priv
->timer_id
= g_timeout_add_seconds (1,
2381 empathy_streamed_media_window_update_timer
, self
);
2383 g_mutex_unlock (priv
->lock
);
2385 empathy_streamed_media_window_update_timer (self
);
2387 gtk_action_set_sensitive (priv
->menu_fullscreen
, TRUE
);
2393 /* Called from the streaming thread */
2395 empathy_streamed_media_window_src_added_cb (EmpathyStreamedMediaHandler
*handler
,
2396 GstPad
*src
, guint media_type
, gpointer user_data
)
2398 EmpathyStreamedMediaWindow
*self
= EMPATHY_STREAMED_MEDIA_WINDOW (user_data
);
2399 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
2400 gboolean retval
= FALSE
;
2404 g_mutex_lock (priv
->lock
);
2406 if (priv
->call_state
!= CONNECTED
)
2408 g_timer_start (priv
->timer
);
2409 priv
->timer_id
= g_idle_add (empathy_streamed_media_window_connected
, self
);
2410 priv
->call_state
= CONNECTED
;
2415 case TP_MEDIA_STREAM_TYPE_AUDIO
:
2416 pad
= empathy_streamed_media_window_get_audio_sink_pad (self
);
2418 case TP_MEDIA_STREAM_TYPE_VIDEO
:
2419 gtk_widget_hide (priv
->remote_user_avatar_widget
);
2420 gtk_widget_show (priv
->video_output
);
2421 pad
= empathy_streamed_media_window_get_video_sink_pad (self
);
2424 g_assert_not_reached ();
2430 if (GST_PAD_LINK_FAILED (gst_pad_link (src
, pad
)))
2431 g_warning ("Could not link %s sink pad",
2432 media_type
== TP_MEDIA_STREAM_TYPE_AUDIO
? "audio" : "video");
2436 gst_object_unref (pad
);
2440 /* If no sink could be linked, try to add fakesink to prevent the whole call
2445 GstElement
*fakesink
= gst_element_factory_make ("fakesink", NULL
);
2447 if (gst_bin_add (GST_BIN (priv
->pipeline
), fakesink
))
2449 GstPad
*sinkpad
= gst_element_get_static_pad (fakesink
, "sink");
2450 if (gst_element_set_state (fakesink
, GST_STATE_PLAYING
) == GST_STATE_CHANGE_FAILURE
||
2451 GST_PAD_LINK_FAILED (gst_pad_link (src
, sinkpad
)))
2453 gst_element_set_locked_state (fakesink
, TRUE
);
2454 gst_element_set_state (fakesink
, GST_STATE_NULL
);
2455 gst_bin_remove (GST_BIN (priv
->pipeline
), fakesink
);
2459 DEBUG ("Could not link real sink, linked fakesink instead");
2461 gst_object_unref (sinkpad
);
2465 gst_object_unref (fakesink
);
2470 g_mutex_unlock (priv
->lock
);
2476 empathy_streamed_media_window_sink_added_cb (EmpathyStreamedMediaHandler
*handler
,
2477 GstPad
*sink
, guint media_type
, gpointer user_data
)
2479 EmpathyStreamedMediaWindow
*self
= EMPATHY_STREAMED_MEDIA_WINDOW (user_data
);
2480 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
2482 gboolean retval
= FALSE
;
2486 case TP_MEDIA_STREAM_TYPE_AUDIO
:
2487 if (!gst_bin_add (GST_BIN (priv
->pipeline
), priv
->audio_input
))
2489 g_warning ("Could not add audio source to pipeline");
2493 pad
= gst_element_get_static_pad (priv
->audio_input
, "src");
2496 gst_bin_remove (GST_BIN (priv
->pipeline
), priv
->audio_input
);
2497 g_warning ("Could not get source pad from audio source");
2501 if (GST_PAD_LINK_FAILED (gst_pad_link (pad
, sink
)))
2503 gst_bin_remove (GST_BIN (priv
->pipeline
), priv
->audio_input
);
2504 g_warning ("Could not link audio source to farsight");
2508 if (gst_element_set_state (priv
->audio_input
, GST_STATE_PLAYING
) == GST_STATE_CHANGE_FAILURE
)
2510 g_warning ("Could not start audio source");
2511 gst_element_set_state (priv
->audio_input
, GST_STATE_NULL
);
2512 gst_bin_remove (GST_BIN (priv
->pipeline
), priv
->audio_input
);
2518 case TP_MEDIA_STREAM_TYPE_VIDEO
:
2519 if (priv
->video_input
!= NULL
)
2521 if (priv
->video_tee
!= NULL
)
2523 pad
= gst_element_get_request_pad (priv
->video_tee
, "src%d");
2524 if (GST_PAD_LINK_FAILED (gst_pad_link (pad
, sink
)))
2526 g_warning ("Could not link videp soure input pipeline");
2529 gst_object_unref (pad
);
2536 g_assert_not_reached ();
2543 empathy_streamed_media_window_remove_video_input (EmpathyStreamedMediaWindow
*self
)
2545 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
2546 GstElement
*preview
;
2548 disable_camera (self
);
2550 DEBUG ("remove video input");
2551 preview
= empathy_video_widget_get_element (
2552 EMPATHY_VIDEO_WIDGET (priv
->video_preview
));
2554 gst_element_set_state (priv
->video_input
, GST_STATE_NULL
);
2555 gst_element_set_state (priv
->video_tee
, GST_STATE_NULL
);
2556 gst_element_set_state (preview
, GST_STATE_NULL
);
2558 gst_bin_remove_many (GST_BIN (priv
->pipeline
), priv
->video_input
,
2559 priv
->video_tee
, preview
, NULL
);
2561 g_object_unref (priv
->video_input
);
2562 priv
->video_input
= NULL
;
2563 g_object_unref (priv
->video_tee
);
2564 priv
->video_tee
= NULL
;
2565 gtk_widget_destroy (priv
->video_preview
);
2566 priv
->video_preview
= NULL
;
2568 gtk_widget_set_sensitive (priv
->tool_button_camera_on
, FALSE
);
2569 gtk_action_set_sensitive (priv
->action_camera_on
, FALSE
);
2570 gtk_widget_set_sensitive (priv
->tool_button_camera_preview
, FALSE
);
2574 start_call (EmpathyStreamedMediaWindow
*self
)
2576 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
2578 priv
->call_started
= TRUE
;
2579 empathy_streamed_media_handler_start_call (priv
->handler
,
2580 empathy_get_current_action_time ());
2582 if (empathy_streamed_media_handler_has_initial_video (priv
->handler
))
2584 /* Enable 'send video' buttons and display the preview */
2585 gtk_toggle_tool_button_set_active (
2586 GTK_TOGGLE_TOOL_BUTTON (priv
->tool_button_camera_on
), TRUE
);
2591 empathy_streamed_media_window_bus_message (GstBus
*bus
, GstMessage
*message
,
2594 EmpathyStreamedMediaWindow
*self
= EMPATHY_STREAMED_MEDIA_WINDOW (user_data
);
2595 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
2598 empathy_streamed_media_handler_bus_message (priv
->handler
, bus
, message
);
2600 switch (GST_MESSAGE_TYPE (message
))
2602 case GST_MESSAGE_STATE_CHANGED
:
2603 if (GST_MESSAGE_SRC (message
) == GST_OBJECT (priv
->video_input
))
2605 gst_message_parse_state_changed (message
, NULL
, &newstate
, NULL
);
2606 if (newstate
== GST_STATE_PAUSED
)
2607 empathy_streamed_media_window_setup_video_input (self
);
2609 if (GST_MESSAGE_SRC (message
) == GST_OBJECT (priv
->pipeline
) &&
2610 !priv
->call_started
)
2612 gst_message_parse_state_changed (message
, NULL
, &newstate
, NULL
);
2613 if (newstate
== GST_STATE_PAUSED
)
2615 gst_element_set_state (priv
->pipeline
, GST_STATE_PLAYING
);
2616 priv
->pipeline_playing
= TRUE
;
2618 if (priv
->start_call_when_playing
)
2623 case GST_MESSAGE_ERROR
:
2625 GError
*error
= NULL
;
2626 GstElement
*gst_error
;
2629 gst_message_parse_error (message
, &error
, &debug
);
2630 gst_error
= GST_ELEMENT (GST_MESSAGE_SRC (message
));
2632 g_message ("Element error: %s -- %s\n", error
->message
, debug
);
2634 if (g_str_has_prefix (gst_element_get_name (gst_error
),
2635 VIDEO_INPUT_ERROR_PREFIX
))
2637 /* Remove the video input and continue */
2638 if (priv
->video_input
!= NULL
)
2639 empathy_streamed_media_window_remove_video_input (self
);
2640 gst_element_set_state (priv
->pipeline
, GST_STATE_PLAYING
);
2644 empathy_streamed_media_window_disconnected (self
, TRUE
);
2646 g_error_free (error
);
2649 case GST_MESSAGE_UNKNOWN
:
2650 case GST_MESSAGE_EOS
:
2651 case GST_MESSAGE_WARNING
:
2652 case GST_MESSAGE_INFO
:
2653 case GST_MESSAGE_TAG
:
2654 case GST_MESSAGE_BUFFERING
:
2655 case GST_MESSAGE_STATE_DIRTY
:
2656 case GST_MESSAGE_STEP_DONE
:
2657 case GST_MESSAGE_CLOCK_PROVIDE
:
2658 case GST_MESSAGE_CLOCK_LOST
:
2659 case GST_MESSAGE_NEW_CLOCK
:
2660 case GST_MESSAGE_STRUCTURE_CHANGE
:
2661 case GST_MESSAGE_STREAM_STATUS
:
2662 case GST_MESSAGE_APPLICATION
:
2663 case GST_MESSAGE_ELEMENT
:
2664 case GST_MESSAGE_SEGMENT_START
:
2665 case GST_MESSAGE_SEGMENT_DONE
:
2666 case GST_MESSAGE_DURATION
:
2667 case GST_MESSAGE_ANY
:
2676 empathy_streamed_media_window_update_avatars_visibility (EmpathyTpStreamedMedia
*call
,
2677 EmpathyStreamedMediaWindow
*window
)
2679 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
2681 if (empathy_tp_streamed_media_is_receiving_video (call
))
2683 gtk_widget_hide (priv
->remote_user_avatar_widget
);
2684 gtk_widget_show (priv
->video_output
);
2688 gtk_widget_hide (priv
->video_output
);
2689 gtk_widget_show (priv
->remote_user_avatar_widget
);
2694 call_handler_notify_tp_streamed_media_cb (EmpathyStreamedMediaHandler
*handler
,
2696 EmpathyStreamedMediaWindow
*self
)
2698 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
2699 EmpathyTpStreamedMedia
*call
;
2701 g_object_get (priv
->handler
, "tp-call", &call
, NULL
);
2705 tp_g_signal_connect_object (call
, "audio-stream-error",
2706 G_CALLBACK (empathy_streamed_media_window_audio_stream_error
), self
, 0);
2707 tp_g_signal_connect_object (call
, "video-stream-error",
2708 G_CALLBACK (empathy_streamed_media_window_video_stream_error
), self
, 0);
2710 g_object_unref (call
);
2714 empathy_streamed_media_window_realized_cb (GtkWidget
*widget
, EmpathyStreamedMediaWindow
*window
)
2716 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
2717 EmpathyTpStreamedMedia
*call
;
2719 g_signal_connect (priv
->handler
, "conference-added",
2720 G_CALLBACK (empathy_streamed_media_window_conference_added_cb
), window
);
2721 g_signal_connect (priv
->handler
, "start-receiving",
2722 G_CALLBACK (empathy_streamed_media_window_start_receiving_cb
), window
);
2723 g_signal_connect (priv
->handler
, "closed",
2724 G_CALLBACK (empathy_streamed_media_window_channel_closed_cb
), window
);
2725 g_signal_connect (priv
->handler
, "src-pad-added",
2726 G_CALLBACK (empathy_streamed_media_window_src_added_cb
), window
);
2727 g_signal_connect (priv
->handler
, "sink-pad-added",
2728 G_CALLBACK (empathy_streamed_media_window_sink_added_cb
), window
);
2729 g_signal_connect (priv
->handler
, "stream-closed",
2730 G_CALLBACK (empathy_streamed_media_window_channel_stream_closed_cb
), window
);
2732 g_object_get (priv
->handler
, "tp-call", &call
, NULL
);
2735 tp_g_signal_connect_object (call
, "audio-stream-error",
2736 G_CALLBACK (empathy_streamed_media_window_audio_stream_error
), window
,
2738 tp_g_signal_connect_object (call
, "video-stream-error",
2739 G_CALLBACK (empathy_streamed_media_window_video_stream_error
), window
,
2742 g_object_unref (call
);
2746 /* tp-call doesn't exist yet, we'll connect signals once it has been
2748 g_signal_connect (priv
->handler
, "notify::tp-call",
2749 G_CALLBACK (call_handler_notify_tp_streamed_media_cb
), window
);
2752 gst_element_set_state (priv
->pipeline
, GST_STATE_PAUSED
);
2756 empathy_streamed_media_window_delete_cb (GtkWidget
*widget
, GdkEvent
*event
,
2757 EmpathyStreamedMediaWindow
*window
)
2759 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
2761 if (priv
->pipeline
!= NULL
)
2763 if (priv
->bus_message_source_id
!= 0)
2765 g_source_remove (priv
->bus_message_source_id
);
2766 priv
->bus_message_source_id
= 0;
2769 gst_element_set_state (priv
->pipeline
, GST_STATE_NULL
);
2772 if (priv
->call_state
== CONNECTING
)
2773 empathy_sound_manager_stop (priv
->sound_mgr
, EMPATHY_SOUND_PHONE_OUTGOING
);
2779 show_controls (EmpathyStreamedMediaWindow
*window
, gboolean set_fullscreen
)
2782 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
2784 menu
= gtk_ui_manager_get_widget (priv
->ui_manager
,
2789 gtk_widget_hide (priv
->sidebar
);
2790 gtk_widget_hide (menu
);
2791 gtk_widget_hide (priv
->vbox
);
2792 gtk_widget_hide (priv
->statusbar
);
2793 gtk_widget_hide (priv
->toolbar
);
2797 if (priv
->sidebar_was_visible_before_fs
)
2798 gtk_widget_show (priv
->sidebar
);
2800 gtk_widget_show (menu
);
2801 gtk_widget_show (priv
->vbox
);
2802 gtk_widget_show (priv
->statusbar
);
2803 gtk_widget_show (priv
->toolbar
);
2805 gtk_window_resize (GTK_WINDOW (window
), priv
->original_width_before_fs
,
2806 priv
->original_height_before_fs
);
2811 show_borders (EmpathyStreamedMediaWindow
*window
, gboolean set_fullscreen
)
2813 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
2815 gtk_container_set_border_width (GTK_CONTAINER (priv
->content_hbox
),
2816 set_fullscreen
? 0 : CONTENT_HBOX_BORDER_WIDTH
);
2817 gtk_box_set_spacing (GTK_BOX (priv
->content_hbox
),
2818 set_fullscreen
? 0 : CONTENT_HBOX_SPACING
);
2820 if (priv
->video_output
!= NULL
)
2822 gtk_box_set_child_packing (GTK_BOX (priv
->content_hbox
),
2823 priv
->video_output
, TRUE
, TRUE
,
2824 set_fullscreen
? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING
,
2828 gtk_box_set_child_packing (GTK_BOX (priv
->content_hbox
),
2829 priv
->vbox
, TRUE
, TRUE
,
2830 set_fullscreen
? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING
,
2835 empathy_streamed_media_window_state_event_cb (GtkWidget
*widget
,
2836 GdkEventWindowState
*event
, EmpathyStreamedMediaWindow
*window
)
2838 if (event
->changed_mask
& GDK_WINDOW_STATE_FULLSCREEN
)
2840 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
2841 gboolean set_fullscreen
= event
->new_window_state
&
2842 GDK_WINDOW_STATE_FULLSCREEN
;
2846 gboolean sidebar_was_visible
;
2847 GtkAllocation allocation
;
2848 gint original_width
, original_height
;
2850 gtk_widget_get_allocation (GTK_WIDGET (window
), &allocation
);
2851 original_width
= allocation
.width
;
2852 original_height
= allocation
.height
;
2854 g_object_get (priv
->sidebar
, "visible", &sidebar_was_visible
, NULL
);
2856 priv
->sidebar_was_visible_before_fs
= sidebar_was_visible
;
2857 priv
->original_width_before_fs
= original_width
;
2858 priv
->original_height_before_fs
= original_height
;
2860 if (priv
->video_output_motion_handler_id
== 0 &&
2861 priv
->video_output
!= NULL
)
2863 priv
->video_output_motion_handler_id
= g_signal_connect (
2864 G_OBJECT (priv
->video_output
), "motion-notify-event",
2865 G_CALLBACK (empathy_streamed_media_window_video_output_motion_notify
),
2871 disconnect_video_output_motion_handler (window
);
2874 empathy_streamed_media_window_fullscreen_set_fullscreen (priv
->fullscreen
,
2876 show_controls (window
, set_fullscreen
);
2877 show_borders (window
, set_fullscreen
);
2878 gtk_action_set_stock_id (priv
->menu_fullscreen
,
2879 (set_fullscreen
? "gtk-leave-fullscreen" : "gtk-fullscreen"));
2880 priv
->is_fullscreen
= set_fullscreen
;
2887 empathy_streamed_media_window_sidebar_toggled_cb (GtkToggleButton
*toggle
,
2888 EmpathyStreamedMediaWindow
*window
)
2890 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
2892 int w
, h
, handle_size
;
2893 GtkAllocation allocation
, sidebar_allocation
;
2895 gtk_widget_get_allocation (GTK_WIDGET (window
), &allocation
);
2896 w
= allocation
.width
;
2897 h
= allocation
.height
;
2899 gtk_widget_style_get (priv
->pane
, "handle_size", &handle_size
, NULL
);
2901 gtk_widget_get_allocation (priv
->sidebar
, &sidebar_allocation
);
2902 if (gtk_toggle_button_get_active (toggle
))
2904 arrow
= gtk_arrow_new (GTK_ARROW_LEFT
, GTK_SHADOW_NONE
);
2905 gtk_widget_show (priv
->sidebar
);
2906 w
+= sidebar_allocation
.width
+ handle_size
;
2910 arrow
= gtk_arrow_new (GTK_ARROW_RIGHT
, GTK_SHADOW_NONE
);
2911 w
-= sidebar_allocation
.width
+ handle_size
;
2912 gtk_widget_hide (priv
->sidebar
);
2915 gtk_button_set_image (GTK_BUTTON (priv
->sidebar_button
), arrow
);
2918 gtk_window_resize (GTK_WINDOW (window
), w
, h
);
2922 empathy_streamed_media_window_set_send_video (EmpathyStreamedMediaWindow
*window
,
2925 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
2926 EmpathyTpStreamedMedia
*call
;
2928 priv
->sending_video
= (state
== CAMERA_STATE_ON
);
2930 if (state
== CAMERA_STATE_PREVIEW
||
2931 state
== CAMERA_STATE_ON
)
2933 /* When we start sending video, we want to show the video preview by
2935 display_video_preview (window
, TRUE
);
2939 display_video_preview (window
, FALSE
);
2942 if (priv
->call_state
!= CONNECTED
)
2945 g_object_get (priv
->handler
, "tp-call", &call
, NULL
);
2946 DEBUG ("%s sending video", priv
->sending_video
? "start": "stop");
2947 empathy_tp_streamed_media_request_video_stream_direction (call
, priv
->sending_video
);
2948 g_object_unref (call
);
2952 empathy_streamed_media_window_mic_toggled_cb (GtkToggleToolButton
*toggle
,
2953 EmpathyStreamedMediaWindow
*window
)
2955 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
2958 active
= (gtk_toggle_tool_button_get_active (toggle
));
2962 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv
->audio_input
),
2964 gtk_adjustment_set_value (priv
->audio_input_adj
, priv
->volume
* 100);
2968 /* TODO, Instead of setting the input volume to 0 we should probably
2969 * stop sending but this would cause the audio call to drop if both
2970 * sides mute at the same time on certain CMs AFAIK. Need to revisit this
2971 * in the future. GNOME #574574
2973 empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv
->audio_input
),
2975 gtk_adjustment_set_value (priv
->audio_input_adj
, 0);
2980 empathy_streamed_media_window_sidebar_hidden_cb (EvSidebar
*sidebar
,
2981 EmpathyStreamedMediaWindow
*window
)
2983 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
2985 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv
->sidebar_button
),
2990 empathy_streamed_media_window_sidebar_shown_cb (EvSidebar
*sidebar
,
2991 EmpathyStreamedMediaWindow
*window
)
2993 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
2995 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv
->sidebar_button
),
3000 empathy_streamed_media_window_hangup_cb (gpointer object
,
3001 EmpathyStreamedMediaWindow
*window
)
3003 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
3005 empathy_streamed_media_handler_stop_call (priv
->handler
);
3007 if (empathy_streamed_media_window_disconnected (window
, FALSE
))
3008 gtk_widget_destroy (GTK_WIDGET (window
));
3012 empathy_streamed_media_window_restart_call (EmpathyStreamedMediaWindow
*window
)
3014 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
3016 /* Remove error info bars */
3017 gtk_container_forall (GTK_CONTAINER (priv
->errors_vbox
),
3018 (GtkCallback
) gtk_widget_destroy
, NULL
);
3020 create_video_output_widget (window
);
3022 g_signal_connect (G_OBJECT (priv
->audio_input_adj
), "value-changed",
3023 G_CALLBACK (empathy_streamed_media_window_mic_volume_changed_cb
), window
);
3025 /* While the call was disconnected, the input volume might have changed.
3026 * However, since the audio_input source was destroyed, its volume has not
3027 * been updated during that time. That's why we manually update it here */
3028 empathy_streamed_media_window_mic_volume_changed_cb (priv
->audio_input_adj
, window
);
3030 priv
->outgoing
= TRUE
;
3031 empathy_streamed_media_window_set_state_connecting (window
);
3033 if (priv
->pipeline_playing
)
3034 start_call (window
);
3036 /* call will be started when the pipeline is ready */
3037 priv
->start_call_when_playing
= TRUE
;
3040 empathy_streamed_media_window_setup_avatars (window
, priv
->handler
);
3042 gtk_action_set_sensitive (priv
->redial
, FALSE
);
3043 gtk_widget_set_sensitive (priv
->redial_button
, FALSE
);
3047 empathy_streamed_media_window_redial_cb (gpointer object
,
3048 EmpathyStreamedMediaWindow
*window
)
3050 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
3052 if (priv
->call_state
== CONNECTED
)
3053 priv
->call_state
= REDIALING
;
3055 empathy_streamed_media_handler_stop_call (priv
->handler
);
3057 if (priv
->call_state
!= CONNECTED
)
3058 empathy_streamed_media_window_restart_call (window
);
3062 empathy_streamed_media_window_fullscreen_cb (gpointer object
,
3063 EmpathyStreamedMediaWindow
*window
)
3065 empathy_streamed_media_window_fullscreen_toggle (window
);
3069 empathy_streamed_media_window_fullscreen_toggle (EmpathyStreamedMediaWindow
*window
)
3071 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
3073 if (priv
->is_fullscreen
)
3074 gtk_window_unfullscreen (GTK_WINDOW (window
));
3076 gtk_window_fullscreen (GTK_WINDOW (window
));
3080 empathy_streamed_media_window_video_button_press_cb (GtkWidget
*video_output
,
3081 GdkEventButton
*event
, EmpathyStreamedMediaWindow
*window
)
3083 if (event
->button
== 3 && event
->type
== GDK_BUTTON_PRESS
)
3085 empathy_streamed_media_window_video_menu_popup (window
, event
->button
);
3093 empathy_streamed_media_window_key_press_cb (GtkWidget
*video_output
,
3094 GdkEventKey
*event
, EmpathyStreamedMediaWindow
*window
)
3096 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
3098 if (priv
->is_fullscreen
&& event
->keyval
== GDK_KEY_Escape
)
3100 /* Since we are in fullscreen mode, toggling will bring us back to
3102 empathy_streamed_media_window_fullscreen_toggle (window
);
3110 empathy_streamed_media_window_video_output_motion_notify (GtkWidget
*widget
,
3111 GdkEventMotion
*event
, EmpathyStreamedMediaWindow
*window
)
3113 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
3115 if (priv
->is_fullscreen
)
3117 empathy_streamed_media_window_fullscreen_show_popup (priv
->fullscreen
);
3124 empathy_streamed_media_window_video_menu_popup (EmpathyStreamedMediaWindow
*window
,
3128 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
3130 menu
= gtk_ui_manager_get_widget (priv
->ui_manager
,
3132 gtk_menu_popup (GTK_MENU (menu
), NULL
, NULL
, NULL
, NULL
,
3133 button
, gtk_get_current_event_time ());
3134 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu
), FALSE
);
3138 empathy_streamed_media_window_status_message (EmpathyStreamedMediaWindow
*window
,
3141 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
3143 if (priv
->context_id
== 0)
3145 priv
->context_id
= gtk_statusbar_get_context_id (
3146 GTK_STATUSBAR (priv
->statusbar
), "voip call status messages");
3150 gtk_statusbar_pop (GTK_STATUSBAR (priv
->statusbar
), priv
->context_id
);
3153 gtk_statusbar_push (GTK_STATUSBAR (priv
->statusbar
), priv
->context_id
,
3158 empathy_streamed_media_window_volume_changed_cb (GtkScaleButton
*button
,
3159 gdouble value
, EmpathyStreamedMediaWindow
*window
)
3161 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (window
);
3163 if (priv
->audio_output
== NULL
)
3166 empathy_audio_sink_set_volume (EMPATHY_GST_AUDIO_SINK (priv
->audio_output
),
3170 /* block all the signals related to camera control widgets. This is useful
3171 * when we are manually updating the UI and so don't want to fire the
3174 block_camera_control_signals (EmpathyStreamedMediaWindow
*self
)
3176 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
3178 g_signal_handlers_block_by_func (priv
->tool_button_camera_off
,
3179 tool_button_camera_off_toggled_cb
, self
);
3180 g_signal_handlers_block_by_func (priv
->tool_button_camera_preview
,
3181 tool_button_camera_preview_toggled_cb
, self
);
3182 g_signal_handlers_block_by_func (priv
->tool_button_camera_on
,
3183 tool_button_camera_on_toggled_cb
, self
);
3184 g_signal_handlers_block_by_func (priv
->action_camera_on
,
3185 action_camera_change_cb
, self
);
3189 unblock_camera_control_signals (EmpathyStreamedMediaWindow
*self
)
3191 EmpathyStreamedMediaWindowPriv
*priv
= GET_PRIV (self
);
3193 g_signal_handlers_unblock_by_func (priv
->tool_button_camera_off
,
3194 tool_button_camera_off_toggled_cb
, self
);
3195 g_signal_handlers_unblock_by_func (priv
->tool_button_camera_preview
,
3196 tool_button_camera_preview_toggled_cb
, self
);
3197 g_signal_handlers_unblock_by_func (priv
->tool_button_camera_on
,
3198 tool_button_camera_on_toggled_cb
, self
);
3199 g_signal_handlers_unblock_by_func (priv
->action_camera_on
,
3200 action_camera_change_cb
, self
);