]> git.0d.be Git - empathy.git/blobdiff - src/empathy-call-window.c
Merge branch 'sasl'
[empathy.git] / src / empathy-call-window.c
index 0d6cf1ce489ed514c8ae009fefcb0283c2f99503..6a0c24aadd08b622c55c3d177e2bb9d9eba56217 100644 (file)
@@ -29,7 +29,9 @@
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
 
+#include <telepathy-glib/util.h>
 #include <telepathy-farsight/channel.h>
+#include <telepathy-glib/util.h>
 
 #include <gst/farsight/fs-element-added-notifier.h>
 
 #include <libempathy-gtk/empathy-audio-sink.h>
 #include <libempathy-gtk/empathy-video-src.h>
 #include <libempathy-gtk/empathy-ui-utils.h>
-#include <libempathy-gtk/empathy-sound.h>
+#include <libempathy-gtk/empathy-sound-manager.h>
 #include <libempathy-gtk/empathy-geometry.h>
+#include <libempathy-gtk/empathy-images.h>
 
 #define DEBUG_FLAG EMPATHY_DEBUG_VOIP
 #include <libempathy/empathy-debug.h>
 
 #include "empathy-call-window.h"
 #include "empathy-call-window-fullscreen.h"
-#include "empathy-sidebar.h"
+#include "ev-sidebar.h"
 
 #define BUTTON_ID "empathy-call-dtmf-button-id"
 
@@ -116,6 +119,8 @@ struct _EmpathyCallWindowPriv
 
   GtkUIManager *ui_manager;
   GtkWidget *errors_vbox;
+  /* widget displays the video received from the remote user. This widget is
+   * alive only during call. */
   GtkWidget *video_output;
   GtkWidget *video_preview;
   GtkWidget *remote_user_avatar_widget;
@@ -162,6 +167,22 @@ struct _EmpathyCallWindowPriv
 
   GtkWidget *dtmf_panel;
 
+  /* Details vbox */
+  GtkWidget *details_vbox;
+  GtkWidget *vcodec_encoding_label;
+  GtkWidget *acodec_encoding_label;
+  GtkWidget *vcodec_decoding_label;
+  GtkWidget *acodec_decoding_label;
+
+  GtkWidget *audio_remote_candidate_label;
+  GtkWidget *audio_local_candidate_label;
+  GtkWidget *video_remote_candidate_label;
+  GtkWidget *video_local_candidate_label;
+  GtkWidget *video_remote_candidate_info_img;
+  GtkWidget *video_local_candidate_info_img;
+  GtkWidget *audio_remote_candidate_info_img;
+  GtkWidget *audio_local_candidate_info_img;
+
   GstElement *video_input;
   GstElement *audio_input;
   GstElement *audio_output;
@@ -195,6 +216,13 @@ struct _EmpathyCallWindowPriv
   gboolean sidebar_was_visible_before_fs;
   gint original_width_before_fs;
   gint original_height_before_fs;
+
+  /* TRUE if the call should be started when the pipeline is playing */
+  gboolean start_call_when_playing;
+  /* TRUE if we requested to set the pipeline in the playing state */
+  gboolean pipeline_playing;
+
+  EmpathySoundManager *sound_mgr;
 };
 
 #define GET_PRIV(o) \
@@ -214,15 +242,15 @@ static void empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
   EmpathyCallWindow *window);
 
 static void empathy_call_window_set_send_video (EmpathyCallWindow *window,
-  gboolean send);
+  CameraState state);
 
 static void empathy_call_window_mic_toggled_cb (
   GtkToggleToolButton *toggle, EmpathyCallWindow *window);
 
-static void empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
+static void empathy_call_window_sidebar_hidden_cb (EvSidebar *sidebar,
   EmpathyCallWindow *window);
 
-static void empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
+static void empathy_call_window_sidebar_shown_cb (EvSidebar *sidebar,
   EmpathyCallWindow *window);
 
 static void empathy_call_window_hangup_cb (gpointer object,
@@ -350,7 +378,7 @@ empathy_call_window_create_dtmf (EmpathyCallWindow *self)
   int i;
   GQuark button_quark;
   struct {
-    gchar *label;
+    const gchar *label;
     TpDTMFEvent event;
   } dtmfbuttons[] = { { "1", TP_DTMF_EVENT_DIGIT_1 },
                       { "2", TP_DTMF_EVENT_DIGIT_2 },
@@ -518,9 +546,6 @@ empathy_call_window_mic_volume_changed_cb (GtkAdjustment *adj,
   EmpathyCallWindowPriv *priv = GET_PRIV (self);
   gdouble volume;
 
-  if (priv->audio_input == NULL)
-    return;
-
   volume = gtk_adjustment_get_value (adj)/100.0;
 
   /* Don't store the volume because of muting */
@@ -579,9 +604,10 @@ empathy_call_window_create_audio_input (EmpathyCallWindow *self)
   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 3);
 
   priv->volume_progress_bar = gtk_progress_bar_new ();
-  gtk_progress_bar_set_orientation (
-      GTK_PROGRESS_BAR (priv->volume_progress_bar),
-      GTK_PROGRESS_BOTTOM_TO_TOP);
+
+  gtk_orientable_set_orientation (GTK_ORIENTABLE (priv->volume_progress_bar),
+      GTK_ORIENTATION_VERTICAL);
+
   gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->volume_progress_bar),
       0);
 
@@ -592,19 +618,17 @@ empathy_call_window_create_audio_input (EmpathyCallWindow *self)
 }
 
 static void
-empathy_call_window_setup_remote_frame (GstBus *bus, EmpathyCallWindow *self)
+create_video_output_widget (EmpathyCallWindow *self)
 {
   EmpathyCallWindowPriv *priv = GET_PRIV (self);
+  GstBus *bus;
 
-  /* Initializing all the content (UI and output gst elements) related to the
-     remote contact */
-  priv->remote_user_output_hbox = gtk_hbox_new (FALSE, 0);
-
-  priv->remote_user_avatar_widget = gtk_image_new ();
-  gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
-      priv->remote_user_avatar_widget, TRUE, TRUE, 0);
+  g_assert (priv->video_output == NULL);
+  g_assert (priv->pipeline != NULL);
 
+  bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
   priv->video_output = empathy_video_widget_new (bus);
+
   gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
       priv->video_output, TRUE, TRUE, 0);
 
@@ -613,85 +637,141 @@ empathy_call_window_setup_remote_frame (GstBus *bus, EmpathyCallWindow *self)
   g_signal_connect (G_OBJECT (priv->video_output), "button-press-event",
       G_CALLBACK (empathy_call_window_video_button_press_cb), self);
 
-  gtk_container_add (GTK_CONTAINER (priv->remote_user_output_frame),
-      priv->remote_user_output_hbox);
+  g_object_unref (bus);
+}
+
+static void
+create_audio_output (EmpathyCallWindow *self)
+{
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
 
+  g_assert (priv->audio_output == NULL);
   priv->audio_output = empathy_audio_sink_new ();
   gst_object_ref (priv->audio_output);
   gst_object_sink (priv->audio_output);
 }
 
 static void
-empathy_call_window_setup_self_frame (GstBus *bus, EmpathyCallWindow *self)
+create_video_input (EmpathyCallWindow *self)
 {
   EmpathyCallWindowPriv *priv = GET_PRIV (self);
 
-  /* Initializing all the content (UI and input gst elements) related to the
-     self contact, except for the video preview widget. This widget is only
-     initialized when the "show video preview" option is activated */
-  priv->self_user_output_hbox = gtk_hbox_new (FALSE, 0);
-
-  priv->self_user_avatar_widget = gtk_image_new ();
-  gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
-      priv->self_user_avatar_widget, TRUE, TRUE, 0);
-
-  gtk_container_add (GTK_CONTAINER (priv->self_user_output_frame),
-      priv->self_user_output_hbox);
-
+  g_assert (priv->video_input == NULL);
   priv->video_input = empathy_video_src_new ();
   gst_object_ref (priv->video_input);
   gst_object_sink (priv->video_input);
+}
 
+static void
+create_audio_input (EmpathyCallWindow *self)
+{
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
+
+  g_assert (priv->audio_input == NULL);
   priv->audio_input = empathy_audio_src_new ();
   gst_object_ref (priv->audio_input);
   gst_object_sink (priv->audio_input);
 
-  empathy_signal_connect_weak (priv->audio_input, "peak-level-changed",
+  tp_g_signal_connect_object (priv->audio_input, "peak-level-changed",
     G_CALLBACK (empathy_call_window_audio_input_level_changed_cb),
-    G_OBJECT (self));
+    self, 0);
 }
 
 static void
-empathy_call_window_setup_video_preview (EmpathyCallWindow *window)
+add_video_preview_to_pipeline (EmpathyCallWindow *self)
 {
-  EmpathyCallWindowPriv *priv = GET_PRIV (window);
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
   GstElement *preview;
-  GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
 
-  if (priv->video_preview != NULL)
+  g_assert (priv->video_preview != NULL);
+  g_assert (priv->pipeline != NULL);
+  g_assert (priv->video_input != NULL);
+  g_assert (priv->video_tee != NULL);
+
+  preview = empathy_video_widget_get_element (
+      EMPATHY_VIDEO_WIDGET (priv->video_preview));
+
+  if (!gst_bin_add (GST_BIN (priv->pipeline), priv->video_input))
+    {
+      g_warning ("Could not add video input to pipeline");
+      return;
+    }
+
+  if (!gst_bin_add (GST_BIN (priv->pipeline), priv->video_tee))
     {
-      /* Since the video preview and the video tee are initialized and freed
-         at the same time, if one is initialized, then the other one should
-         be too. */
-      g_assert (priv->video_tee != NULL);
+      g_warning ("Could not add video tee to pipeline");
       return;
     }
 
-  DEBUG ("Create video preview");
+  if (!gst_bin_add (GST_BIN (priv->pipeline), preview))
+    {
+      g_warning ("Could not add video preview to pipeline");
+      return;
+    }
+
+  if (!gst_element_link (priv->video_input, priv->video_tee))
+    {
+      g_warning ("Could not link video input to video tee");
+      return;
+    }
+
+  if (!gst_element_link (priv->video_tee, preview))
+    {
+      g_warning ("Could not link video tee to video preview");
+      return;
+    }
+}
+
+static void
+create_video_preview (EmpathyCallWindow *self)
+{
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
+  GstBus *bus;
+
+  g_assert (priv->video_preview == NULL);
   g_assert (priv->video_tee == NULL);
 
-  priv->video_tee = gst_element_factory_make ("tee", NULL);
-  gst_object_ref (priv->video_tee);
-  gst_object_sink (priv->video_tee);
+  bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
 
   priv->video_preview = empathy_video_widget_new_with_size (bus,
       SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
   g_object_set (priv->video_preview, "sync", FALSE, "async", TRUE, NULL);
+
   gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
       priv->video_preview, TRUE, TRUE, 0);
 
-  preview = empathy_video_widget_get_element (
-      EMPATHY_VIDEO_WIDGET (priv->video_preview));
-  gst_bin_add_many (GST_BIN (priv->pipeline), priv->video_input,
-      priv->video_tee, preview, NULL);
-  gst_element_link_many (priv->video_input, priv->video_tee,
-      preview, NULL);
+  priv->video_tee = gst_element_factory_make ("tee", NULL);
+  gst_object_ref (priv->video_tee);
+  gst_object_sink (priv->video_tee);
 
   g_object_unref (bus);
+}
 
-  gst_element_set_state (preview, GST_STATE_PLAYING);
-  gst_element_set_state (priv->video_input, GST_STATE_PLAYING);
-  gst_element_set_state (priv->video_tee, GST_STATE_PLAYING);
+static void
+play_camera (EmpathyCallWindow *window,
+    gboolean play)
+{
+  EmpathyCallWindowPriv *priv = GET_PRIV (window);
+  GstElement *preview;
+  GstState state;
+
+  if (priv->video_preview == NULL)
+    {
+      create_video_preview (window);
+      add_video_preview_to_pipeline (window);
+    }
+
+  if (play)
+    state = GST_STATE_PLAYING;
+  else
+    state = GST_STATE_NULL;
+
+  preview = empathy_video_widget_get_element (
+      EMPATHY_VIDEO_WIDGET (priv->video_preview));
+
+  gst_element_set_state (preview, state);
+  gst_element_set_state (priv->video_input, state);
+  gst_element_set_state (priv->video_tee, state);
 }
 
 static void
@@ -705,8 +785,7 @@ display_video_preview (EmpathyCallWindow *self,
       /* Display the preview and hide the self avatar */
       DEBUG ("Show video preview");
 
-      if (priv->video_preview == NULL)
-        empathy_call_window_setup_video_preview (self);
+      play_camera (self, TRUE);
       gtk_widget_show (priv->video_preview);
       gtk_widget_hide (priv->self_user_avatar_widget);
     }
@@ -716,7 +795,10 @@ display_video_preview (EmpathyCallWindow *self,
       DEBUG ("Show self avatar");
 
       if (priv->video_preview != NULL)
-        gtk_widget_hide (priv->video_preview);
+        {
+          gtk_widget_hide (priv->video_preview);
+          play_camera (self, FALSE);
+        }
       gtk_widget_show (priv->self_user_avatar_widget);
     }
 }
@@ -730,7 +812,7 @@ empathy_call_window_set_state_connecting (EmpathyCallWindow *window)
   priv->call_state = CONNECTING;
 
   if (priv->outgoing)
-    empathy_sound_start_playing (GTK_WIDGET (window),
+    empathy_sound_manager_start_playing (priv->sound_mgr, GTK_WIDGET (window),
         EMPATHY_SOUND_PHONE_OUTGOING, MS_BETWEEN_RING);
 }
 
@@ -747,7 +829,7 @@ disable_camera (EmpathyCallWindow *self)
   display_video_preview (self, FALSE);
 
   if (priv->camera_state == CAMERA_STATE_ON)
-    empathy_call_window_set_send_video (self, FALSE);
+    empathy_call_window_set_send_video (self, CAMERA_STATE_OFF);
 
   block_camera_control_signals (self);
   gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
@@ -797,10 +879,14 @@ enable_preview (EmpathyCallWindow *self)
   DEBUG ("Enable preview");
 
   if (priv->camera_state == CAMERA_STATE_ON)
-    /* preview is already displayed so we just have to stop sending */
-    empathy_call_window_set_send_video (self, FALSE);
-
-  display_video_preview (self, TRUE);
+    {
+      /* preview is already displayed so we just have to stop sending */
+      empathy_call_window_set_send_video (self, CAMERA_STATE_PREVIEW);
+    }
+  else
+    {
+      display_video_preview (self, TRUE);
+    }
 
   block_camera_control_signals (self);
   gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
@@ -847,9 +933,16 @@ enable_camera (EmpathyCallWindow *self)
   if (priv->camera_state == CAMERA_STATE_ON)
     return;
 
+  if (priv->video_input == NULL)
+    {
+      DEBUG ("Can't enable camera, no input");
+      return;
+    }
+
+
   DEBUG ("Enable camera");
 
-  empathy_call_window_set_send_video (self, TRUE);
+  empathy_call_window_set_send_video (self, CAMERA_STATE_ON);
 
   block_camera_control_signals (self);
   gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (
@@ -916,6 +1009,25 @@ action_camera_change_cb (GtkRadioAction *action,
     }
 }
 
+static void
+create_pipeline (EmpathyCallWindow *self)
+{
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
+  GstBus *bus;
+
+  g_assert (priv->pipeline == NULL);
+
+  priv->pipeline = gst_pipeline_new (NULL);
+  priv->pipeline_playing = FALSE;
+
+  bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
+  priv->bus_message_source_id = gst_bus_add_watch (bus,
+      empathy_call_window_bus_message, self);
+
+  g_object_unref (bus);
+}
+
+
 static void
 empathy_call_window_init (EmpathyCallWindow *self)
 {
@@ -925,7 +1037,6 @@ empathy_call_window_init (EmpathyCallWindow *self)
   GtkWidget *h;
   GtkWidget *arrow;
   GtkWidget *page;
-  GstBus *bus;
   gchar *filename;
   GKeyFile *keyfile;
   GError *error = NULL;
@@ -947,6 +1058,19 @@ empathy_call_window_init (EmpathyCallWindow *self)
     "camera_on", &priv->tool_button_camera_on,
     "action_camera_off",  &priv->action_camera,
     "action_camera_preview",  &priv->action_camera_preview,
+    "details_vbox",  &priv->details_vbox,
+    "vcodec_encoding_label", &priv->vcodec_encoding_label,
+    "acodec_encoding_label", &priv->acodec_encoding_label,
+    "acodec_decoding_label", &priv->acodec_decoding_label,
+    "vcodec_decoding_label", &priv->vcodec_decoding_label,
+    "audio_remote_candidate_label", &priv->audio_remote_candidate_label,
+    "audio_local_candidate_label", &priv->audio_local_candidate_label,
+    "video_remote_candidate_label", &priv->video_remote_candidate_label,
+    "video_local_candidate_label", &priv->video_local_candidate_label,
+    "video_remote_candidate_info_img", &priv->video_remote_candidate_info_img,
+    "video_local_candidate_info_img", &priv->video_local_candidate_info_img,
+    "audio_remote_candidate_info_img", &priv->audio_remote_candidate_info_img,
+    "audio_local_candidate_info_img", &priv->audio_local_candidate_info_img,
     NULL);
   g_free (filename);
 
@@ -963,6 +1087,8 @@ empathy_call_window_init (EmpathyCallWindow *self)
     "action_camera_off", "changed", action_camera_change_cb,
     NULL);
 
+  gtk_action_set_sensitive (priv->menu_fullscreen, FALSE);
+
   priv->lock = g_mutex_new ();
 
   gtk_container_add (GTK_CONTAINER (self), top_vbox);
@@ -972,14 +1098,50 @@ empathy_call_window_init (EmpathyCallWindow *self)
                                   CONTENT_HBOX_BORDER_WIDTH);
   gtk_paned_pack1 (GTK_PANED (priv->pane), priv->content_hbox, TRUE, FALSE);
 
-  priv->pipeline = gst_pipeline_new (NULL);
-  bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
-  priv->bus_message_source_id = gst_bus_add_watch (bus,
-      empathy_call_window_bus_message, self);
+  /* remote user output frame */
+  priv->remote_user_output_frame = gtk_frame_new (NULL);
+  gtk_widget_set_size_request (priv->remote_user_output_frame,
+      EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT);
+  gtk_box_pack_start (GTK_BOX (priv->content_hbox),
+      priv->remote_user_output_frame, TRUE, TRUE,
+      CONTENT_HBOX_CHILDREN_PACKING_PADDING);
+
+  priv->remote_user_output_hbox = gtk_hbox_new (FALSE, 0);
+
+  priv->remote_user_avatar_widget = gtk_image_new ();
+
+  gtk_box_pack_start (GTK_BOX (priv->remote_user_output_hbox),
+      priv->remote_user_avatar_widget, TRUE, TRUE, 0);
+
+  gtk_container_add (GTK_CONTAINER (priv->remote_user_output_frame),
+      priv->remote_user_output_hbox);
+
+  /* self user output frame */
+  priv->self_user_output_frame = gtk_frame_new (NULL);
+  gtk_widget_set_size_request (priv->self_user_output_frame,
+      SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
+
+  priv->self_user_output_hbox = gtk_hbox_new (FALSE, 0);
+
+  priv->self_user_avatar_widget = gtk_image_new ();
+  gtk_box_pack_start (GTK_BOX (priv->self_user_output_hbox),
+      priv->self_user_avatar_widget, TRUE, TRUE, 0);
+
+  gtk_container_add (GTK_CONTAINER (priv->self_user_output_frame),
+      priv->self_user_output_hbox);
+
+  create_pipeline (self);
+  create_video_output_widget (self);
+  create_audio_input (self);
+  create_audio_output (self);
+  create_video_input (self);
 
   priv->fsnotifier = fs_element_added_notifier_new ();
   fs_element_added_notifier_add (priv->fsnotifier, GST_BIN (priv->pipeline));
 
+  /* The call will be started as soon the pipeline is playing */
+  priv->start_call_when_playing = TRUE;
+
   keyfile = g_key_file_new ();
   filename = empathy_file_lookup ("element-properties", "data");
   if (g_key_file_load_from_file (keyfile, filename, G_KEY_FILE_NONE, &error))
@@ -995,30 +1157,14 @@ empathy_call_window_init (EmpathyCallWindow *self)
     }
   g_free (filename);
 
-
-  priv->remote_user_output_frame = gtk_frame_new (NULL);
-  gtk_widget_set_size_request (priv->remote_user_output_frame,
-      EMPATHY_VIDEO_WIDGET_DEFAULT_WIDTH, EMPATHY_VIDEO_WIDGET_DEFAULT_HEIGHT);
-  gtk_box_pack_start (GTK_BOX (priv->content_hbox),
-      priv->remote_user_output_frame, TRUE, TRUE,
-      CONTENT_HBOX_CHILDREN_PACKING_PADDING);
-  empathy_call_window_setup_remote_frame (bus, self);
-
-  priv->self_user_output_frame = gtk_frame_new (NULL);
-  gtk_widget_set_size_request (priv->self_user_output_frame,
-      SELF_VIDEO_SECTION_WIDTH, SELF_VIDEO_SECTION_HEIGTH);
-
   priv->vbox = gtk_vbox_new (FALSE, 3);
   gtk_box_pack_start (GTK_BOX (priv->content_hbox), priv->vbox,
       FALSE, FALSE, CONTENT_HBOX_CHILDREN_PACKING_PADDING);
   gtk_box_pack_start (GTK_BOX (priv->vbox), priv->self_user_output_frame,
       FALSE, FALSE, 0);
-  empathy_call_window_setup_self_frame (bus, self);
 
   empathy_call_window_setup_toolbar (self);
 
-  g_object_unref (bus);
-
   priv->sidebar_button = gtk_toggle_button_new_with_mnemonic (_("_Sidebar"));
   arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
   g_signal_connect (G_OBJECT (priv->sidebar_button), "toggled",
@@ -1030,7 +1176,7 @@ empathy_call_window_init (EmpathyCallWindow *self)
   gtk_box_pack_end (GTK_BOX (priv->vbox), h, FALSE, FALSE, 3);
   gtk_box_pack_end (GTK_BOX (h), priv->sidebar_button, FALSE, FALSE, 3);
 
-  priv->sidebar = empathy_sidebar_new ();
+  priv->sidebar = ev_sidebar_new ();
   g_signal_connect (G_OBJECT (priv->sidebar),
     "hide", G_CALLBACK (empathy_call_window_sidebar_hidden_cb), self);
   g_signal_connect (G_OBJECT (priv->sidebar),
@@ -1038,19 +1184,21 @@ empathy_call_window_init (EmpathyCallWindow *self)
   gtk_paned_pack2 (GTK_PANED (priv->pane), priv->sidebar, FALSE, FALSE);
 
   page = empathy_call_window_create_audio_input (self);
-  empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Audio input"),
-    page);
+  ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "audio-input",
+      _("Audio input"), page);
 
   page = empathy_call_window_create_video_input (self);
-  empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Video input"),
-    page);
+  ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "video-input",
+      _("Video input"), page);
 
   priv->dtmf_panel = empathy_call_window_create_dtmf (self);
-  empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Dialpad"),
-    priv->dtmf_panel);
+  ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "dialpad",
+      _("Dialpad"), priv->dtmf_panel);
 
   gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
 
+  ev_sidebar_add_page (EV_SIDEBAR (priv->sidebar), "details",
+      _("Details"), priv->details_vbox);
 
   gtk_widget_show_all (top_vbox);
 
@@ -1079,6 +1227,8 @@ empathy_call_window_init (EmpathyCallWindow *self)
   g_object_ref (priv->ui_manager);
   g_object_unref (gui);
 
+  priv->sound_mgr = empathy_sound_manager_dup_singleton ();
+
   empathy_geometry_bind (GTK_WINDOW (self), "call-window");
 }
 
@@ -1099,11 +1249,14 @@ init_contact_avatar_with_size (EmpathyContact *contact,
 
   if (pixbuf_avatar == NULL)
     {
-      pixbuf_avatar = empathy_pixbuf_from_icon_name_sized ("stock_person",
-          size);
+      pixbuf_avatar = empathy_pixbuf_from_icon_name_sized (
+          EMPATHY_IMAGE_AVATAR_DEFAULT, size);
     }
 
   gtk_image_set_from_pixbuf (GTK_IMAGE (image_widget), pixbuf_avatar);
+
+  if (pixbuf_avatar != NULL)
+    g_object_unref (pixbuf_avatar);
 }
 
 static void
@@ -1115,7 +1268,7 @@ set_window_title (EmpathyCallWindow *self)
   /* translators: Call is a noun and %s is the contact name. This string
    * is used in the window title */
   tmp = g_strdup_printf (_("Call with %s"),
-      empathy_contact_get_name (priv->contact));
+      empathy_contact_get_alias (priv->contact));
   gtk_window_set_title (GTK_WINDOW (self), tmp);
   g_free (tmp);
 }
@@ -1132,8 +1285,10 @@ contact_avatar_changed_cb (EmpathyContact *contact,
     GParamSpec *pspec, GtkWidget *avatar_widget)
 {
   int size;
+  GtkAllocation allocation;
 
-  size = avatar_widget->allocation.height;
+  gtk_widget_get_allocation (avatar_widget, &allocation);
+  size = allocation.height;
 
   if (size == 0)
     {
@@ -1146,7 +1301,7 @@ contact_avatar_changed_cb (EmpathyContact *contact,
 }
 
 static void
-empathy_call_window_got_self_contact_cb (EmpathyTpContactFactory *factory,
+empathy_call_window_got_self_contact_cb (TpConnection *connection,
     EmpathyContact *contact, const GError *error, gpointer user_data,
     GObject *weak_object)
 {
@@ -1171,7 +1326,6 @@ empathy_call_window_setup_avatars (EmpathyCallWindow *self,
   if (priv->contact != NULL)
     {
       TpConnection *connection;
-      EmpathyTpContactFactory *factory;
 
       set_window_title (self);
 
@@ -1183,12 +1337,9 @@ empathy_call_window_setup_avatars (EmpathyCallWindow *self,
 
       /* Retreiving the self avatar */
       connection = empathy_contact_get_connection (priv->contact);
-      factory = empathy_tp_contact_factory_dup_singleton (connection);
-      empathy_tp_contact_factory_get_from_handle (factory,
+      empathy_tp_contact_factory_get_from_handle (connection,
           tp_connection_get_self_handle (connection),
           empathy_call_window_got_self_contact_cb, self, NULL, G_OBJECT (self));
-
-      g_object_unref (factory);
     }
   else
     {
@@ -1215,6 +1366,214 @@ empathy_call_window_setup_avatars (EmpathyCallWindow *self,
   gtk_widget_show (priv->remote_user_avatar_widget);
 }
 
+static void
+update_send_codec (EmpathyCallWindow *self,
+    gboolean audio)
+{
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
+  FsCodec *codec;
+  GtkWidget *widget;
+  gchar *tmp;
+
+  if (audio)
+    {
+      codec = empathy_call_handler_get_send_audio_codec (priv->handler);
+      widget = priv->acodec_encoding_label;
+    }
+  else
+    {
+      codec = empathy_call_handler_get_send_video_codec (priv->handler);
+      widget = priv->vcodec_encoding_label;
+    }
+
+  if (codec == NULL)
+    return;
+
+  tmp = g_strdup_printf ("%s/%u", codec->encoding_name, codec->clock_rate);
+  gtk_label_set_text (GTK_LABEL (widget), tmp);
+  g_free (tmp);
+}
+
+static void
+send_audio_codec_notify_cb (GObject *object,
+    GParamSpec *pspec,
+    gpointer user_data)
+{
+  EmpathyCallWindow *self = user_data;
+
+  update_send_codec (self, TRUE);
+}
+
+static void
+send_video_codec_notify_cb (GObject *object,
+    GParamSpec *pspec,
+    gpointer user_data)
+{
+  EmpathyCallWindow *self = user_data;
+
+  update_send_codec (self, FALSE);
+}
+
+static void
+update_recv_codec (EmpathyCallWindow *self,
+    gboolean audio)
+{
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
+  GList *codecs, *l;
+  GtkWidget *widget;
+  GString *str = NULL;
+
+  if (audio)
+    {
+      codecs = empathy_call_handler_get_recv_audio_codecs (priv->handler);
+      widget = priv->acodec_decoding_label;
+    }
+  else
+    {
+      codecs = empathy_call_handler_get_recv_video_codecs (priv->handler);
+      widget = priv->vcodec_decoding_label;
+    }
+
+  if (codecs == NULL)
+    return;
+
+  for (l = codecs; l != NULL; l = g_list_next (l))
+    {
+      FsCodec *codec = l->data;
+
+      if (str == NULL)
+        str = g_string_new (NULL);
+      else
+        g_string_append (str, ", ");
+
+      g_string_append_printf (str, "%s/%u", codec->encoding_name,
+          codec->clock_rate);
+    }
+
+  gtk_label_set_text (GTK_LABEL (widget), str->str);
+  g_string_free (str, TRUE);
+}
+
+static void
+recv_audio_codecs_notify_cb (GObject *object,
+    GParamSpec *pspec,
+    gpointer user_data)
+{
+  EmpathyCallWindow *self = user_data;
+
+  update_recv_codec (self, TRUE);
+}
+
+static void
+recv_video_codecs_notify_cb (GObject *object,
+    GParamSpec *pspec,
+    gpointer user_data)
+{
+  EmpathyCallWindow *self = user_data;
+
+  update_recv_codec (self, FALSE);
+}
+
+static const gchar *
+candidate_type_to_str (FsCandidate *candidate)
+{
+  switch (candidate->type)
+    {
+      case FS_CANDIDATE_TYPE_HOST:
+        return "host";
+      case FS_CANDIDATE_TYPE_SRFLX:
+        return "server reflexive";
+      case FS_CANDIDATE_TYPE_PRFLX:
+        return "peer reflexive";
+      case FS_CANDIDATE_TYPE_RELAY:
+        return "relay";
+      case FS_CANDIDATE_TYPE_MULTICAST:
+        return "multicast";
+    }
+
+  return NULL;
+}
+
+static const gchar *
+candidate_type_to_desc (FsCandidate *candidate)
+{
+  switch (candidate->type)
+    {
+      case FS_CANDIDATE_TYPE_HOST:
+        return _("The IP address as seen by the machine");
+      case FS_CANDIDATE_TYPE_SRFLX:
+        return _("The IP address as seen by a server on the Internet");
+      case FS_CANDIDATE_TYPE_PRFLX:
+        return _("The IP address of the peer as seen by the other side");
+      case FS_CANDIDATE_TYPE_RELAY:
+        return _("The IP address of a relay server");
+      case FS_CANDIDATE_TYPE_MULTICAST:
+        return _("The IP address of the multicast group");
+    }
+
+  return NULL;
+}
+
+static void
+update_candidat_widget (EmpathyCallWindow *self,
+    GtkWidget *label,
+    GtkWidget *img,
+    FsCandidate *candidate)
+{
+  gchar *str;
+
+  g_assert (candidate != NULL);
+  str = g_strdup_printf ("%s %u (%s)", candidate->ip,
+      candidate->port, candidate_type_to_str (candidate));
+
+  gtk_label_set_text (GTK_LABEL (label), str);
+  gtk_widget_set_tooltip_text (img, candidate_type_to_desc (candidate));
+
+  g_free (str);
+}
+
+static void
+candidates_changed_cb (GObject *object,
+    FsMediaType type,
+    EmpathyCallWindow *self)
+{
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
+  FsCandidate *candidate = NULL;
+
+  if (type == FS_MEDIA_TYPE_VIDEO)
+    {
+      /* Update remote candidate */
+      candidate = empathy_call_handler_get_video_remote_candidate (
+          priv->handler);
+
+      update_candidat_widget (self, priv->video_remote_candidate_label,
+          priv->video_remote_candidate_info_img, candidate);
+
+      /* Update local candidate */
+      candidate = empathy_call_handler_get_video_local_candidate (
+          priv->handler);
+
+      update_candidat_widget (self, priv->video_local_candidate_label,
+          priv->video_local_candidate_info_img, candidate);
+    }
+  else
+    {
+      /* Update remote candidate */
+      candidate = empathy_call_handler_get_audio_remote_candidate (
+          priv->handler);
+
+      update_candidat_widget (self, priv->audio_remote_candidate_label,
+          priv->audio_remote_candidate_info_img, candidate);
+
+      /* Update local candidate */
+      candidate = empathy_call_handler_get_audio_local_candidate (
+          priv->handler);
+
+      update_candidat_widget (self, priv->audio_local_candidate_label,
+          priv->audio_local_candidate_info_img, candidate);
+    }
+}
+
 static void
 empathy_call_window_constructed (GObject *object)
 {
@@ -1239,6 +1598,23 @@ empathy_call_window_constructed (GObject *object)
     }
   /* If call has InitialVideo, the preview will be started once the call has
    * been started (start_call()). */
+
+  update_send_codec (self, TRUE);
+  update_send_codec (self, FALSE);
+  update_recv_codec (self, TRUE);
+  update_recv_codec (self, FALSE);
+
+  tp_g_signal_connect_object (priv->handler, "notify::send-audio-codec",
+      G_CALLBACK (send_audio_codec_notify_cb), self, 0);
+  tp_g_signal_connect_object (priv->handler, "notify::send-video-codec",
+      G_CALLBACK (send_video_codec_notify_cb), self, 0);
+  tp_g_signal_connect_object (priv->handler, "notify::recv-audio-codecs",
+      G_CALLBACK (recv_audio_codecs_notify_cb), self, 0);
+  tp_g_signal_connect_object (priv->handler, "notify::recv-video-codecs",
+      G_CALLBACK (recv_video_codecs_notify_cb), self, 0);
+
+  tp_g_signal_connect_object (priv->handler, "candidates-changed",
+      G_CALLBACK (candidates_changed_cb), self, 0);
 }
 
 static void empathy_call_window_dispose (GObject *object);
@@ -1325,15 +1701,22 @@ empathy_call_window_dispose (GObject *object)
 
   if (call != NULL)
     {
-      g_signal_handlers_disconnect_by_func (call,
-        empathy_call_window_video_stream_changed_cb, object);
       g_object_unref (call);
     }
 
   if (priv->handler != NULL)
-    g_object_unref (priv->handler);
+    {
+      empathy_call_handler_stop_call (priv->handler);
+      g_object_unref (priv->handler);
+    }
   priv->handler = NULL;
 
+  if (priv->bus_message_source_id != 0)
+    {
+      g_source_remove (priv->bus_message_source_id);
+      priv->bus_message_source_id = 0;
+    }
+
   if (priv->pipeline != NULL)
     g_object_unref (priv->pipeline);
   priv->pipeline = NULL;
@@ -1354,6 +1737,10 @@ empathy_call_window_dispose (GObject *object)
     g_object_unref (priv->video_tee);
   priv->video_tee = NULL;
 
+  if (priv->liveadder != NULL)
+    gst_object_unref (priv->liveadder);
+  priv->liveadder = NULL;
+
   if (priv->fsnotifier != NULL)
     g_object_unref (priv->fsnotifier);
   priv->fsnotifier = NULL;
@@ -1366,6 +1753,10 @@ empathy_call_window_dispose (GObject *object)
     g_object_unref (priv->ui_manager);
   priv->ui_manager = NULL;
 
+  if (priv->fullscreen != NULL)
+    g_object_unref (priv->fullscreen);
+  priv->fullscreen = NULL;
+
   if (priv->contact != NULL)
     {
       g_signal_handlers_disconnect_by_func (priv->contact,
@@ -1374,15 +1765,16 @@ empathy_call_window_dispose (GObject *object)
       priv->contact = NULL;
     }
 
+  tp_clear_object (&priv->sound_mgr);
+
   /* release any references held by the object here */
   if (G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose)
     G_OBJECT_CLASS (empathy_call_window_parent_class)->dispose (object);
 }
 
-void
-empathy_call_window_finalize (GObject *object)
+static void
+disconnect_video_output_motion_handler (EmpathyCallWindow *self)
 {
-  EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
   EmpathyCallWindowPriv *priv = GET_PRIV (self);
 
   if (priv->video_output_motion_handler_id != 0)
@@ -1391,12 +1783,15 @@ empathy_call_window_finalize (GObject *object)
           priv->video_output_motion_handler_id);
       priv->video_output_motion_handler_id = 0;
     }
+}
 
-  if (priv->bus_message_source_id != 0)
-    {
-      g_source_remove (priv->bus_message_source_id);
-      priv->bus_message_source_id = 0;
-    }
+void
+empathy_call_window_finalize (GObject *object)
+{
+  EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (object);
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
+
+  disconnect_video_output_motion_handler (self);
 
   /* free any data held directly by the object here */
   g_mutex_free (priv->lock);
@@ -1467,21 +1862,9 @@ empathy_call_window_reset_pipeline (EmpathyCallWindow *self)
         g_object_unref (priv->pipeline);
       priv->pipeline = NULL;
 
-      if (priv->video_input != NULL)
-        g_object_unref (priv->video_input);
-      priv->video_input = NULL;
-
-      if (priv->audio_input != NULL)
-        g_object_unref (priv->audio_input);
-      priv->audio_input = NULL;
-
       g_signal_handlers_disconnect_by_func (priv->audio_input_adj,
           empathy_call_window_mic_volume_changed_cb, self);
 
-      if (priv->audio_output != NULL)
-        g_object_unref (priv->audio_output);
-      priv->audio_output = NULL;
-
       if (priv->video_tee != NULL)
         g_object_unref (priv->video_tee);
       priv->video_tee = NULL;
@@ -1493,6 +1876,11 @@ empathy_call_window_reset_pipeline (EmpathyCallWindow *self)
       priv->liveadder = NULL;
       priv->funnel = NULL;
 
+      create_pipeline (self);
+      /* Call will be started when user will hit the 'redial' button */
+      priv->start_call_when_playing = FALSE;
+      gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
+
       return TRUE;
     }
   else
@@ -1504,15 +1892,35 @@ empathy_call_window_reset_pipeline (EmpathyCallWindow *self)
     }
 }
 
+static void
+reset_details_pane (EmpathyCallWindow *self)
+{
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
+
+  gtk_label_set_text (GTK_LABEL (priv->vcodec_encoding_label), _("Unknown"));
+  gtk_label_set_text (GTK_LABEL (priv->acodec_encoding_label), _("Unknown"));
+  gtk_label_set_text (GTK_LABEL (priv->vcodec_decoding_label), _("Unknown"));
+  gtk_label_set_text (GTK_LABEL (priv->acodec_decoding_label), _("Unknown"));
+}
+
 static gboolean
-empathy_call_window_disconnected (EmpathyCallWindow *self)
+empathy_call_window_disconnected (EmpathyCallWindow *self,
+    gboolean restart)
 {
   gboolean could_disconnect = FALSE;
   EmpathyCallWindowPriv *priv = GET_PRIV (self);
-  gboolean could_reset_pipeline = empathy_call_window_reset_pipeline (self);
+  gboolean could_reset_pipeline;
+
+  /* Leave full screen mode if needed */
+  gtk_window_unfullscreen (GTK_WINDOW (self));
+
+  gtk_action_set_sensitive (priv->menu_fullscreen, FALSE);
+
+  could_reset_pipeline = empathy_call_window_reset_pipeline (self);
 
   if (priv->call_state == CONNECTING)
-      empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
+      empathy_sound_manager_stop (priv->sound_mgr,
+          EMPATHY_SOUND_PHONE_OUTGOING);
 
   if (priv->call_state != REDIALING)
     priv->call_state = DISCONNECTED;
@@ -1529,33 +1937,48 @@ empathy_call_window_disconnected (EmpathyCallWindow *self)
 
       g_mutex_unlock (priv->lock);
 
+      if (!restart)
+        /* We are about to destroy the window, no need to update it or create
+         * a video preview */
+        return TRUE;
+
       empathy_call_window_status_message (self, _("Disconnected"));
 
       gtk_action_set_sensitive (priv->redial, TRUE);
       gtk_widget_set_sensitive (priv->redial_button, TRUE);
 
-      /* Reseting the send_video, camera_buton and mic_button to their
-         initial state */
+      /* Unsensitive the camera and mic button */
       gtk_widget_set_sensitive (priv->tool_button_camera_on, FALSE);
       gtk_widget_set_sensitive (priv->mic_button, FALSE);
-      gtk_toggle_tool_button_set_active (
-          GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_off), TRUE);
+
+      /* Be sure that the mic button is enabled */
       gtk_toggle_tool_button_set_active (
           GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), TRUE);
 
-      /* FIXME: This is to workaround the fact that the pipeline has been
-       * destroyed and so we can't display preview until a new call (and so a
-       * new pipeline) is created. We should fix this properly by refactoring
-       * the code managing the pipeline. This is bug #602937 */
-      gtk_widget_set_sensitive (priv->tool_button_camera_preview, FALSE);
-      gtk_action_set_sensitive (priv->action_camera_preview, FALSE);
+      if (priv->camera_state == CAMERA_STATE_ON)
+        {
+          /* Enable the 'preview' button as we are not sending atm. */
+          gtk_toggle_tool_button_set_active (
+              GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_preview), TRUE);
+        }
+      else if (priv->camera_state == CAMERA_STATE_PREVIEW)
+        {
+          /* Restart the preview with the new pipeline. */
+          display_video_preview (self, TRUE);
+        }
 
       gtk_progress_bar_set_fraction (
           GTK_PROGRESS_BAR (priv->volume_progress_bar), 0);
 
-      gtk_widget_hide (priv->video_output);
+      /* destroy the video output; it will be recreated when we'll redial */
+      disconnect_video_output_motion_handler (self);
+      gtk_widget_destroy (priv->video_output);
+      priv->video_output = NULL;
+
       gtk_widget_show (priv->remote_user_avatar_widget);
 
+      reset_details_pane (self);
+
       priv->sending_video = FALSE;
       priv->call_started = FALSE;
 
@@ -1576,7 +1999,8 @@ empathy_call_window_channel_closed_cb (EmpathyCallHandler *handler,
   EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
   EmpathyCallWindowPriv *priv = GET_PRIV (self);
 
-  if (empathy_call_window_disconnected (self) && priv->call_state == REDIALING)
+  if (empathy_call_window_disconnected (self, TRUE) &&
+      priv->call_state == REDIALING)
       empathy_call_window_restart_call (self);
 }
 
@@ -1935,19 +2359,19 @@ media_stream_error_to_txt (EmpathyCallWindow *self,
           return g_strdup_printf (
               _("%s's software does not understand any of the audio formats "
                 "supported by your computer"),
-            empathy_contact_get_name (priv->contact));
+            empathy_contact_get_alias (priv->contact));
         else
           return g_strdup_printf (
               _("%s's software does not understand any of the video formats "
                 "supported by your computer"),
-            empathy_contact_get_name (priv->contact));
+            empathy_contact_get_alias (priv->contact));
 
       case TP_MEDIA_STREAM_ERROR_CONNECTION_FAILED:
         return g_strdup_printf (
             _("Can't establish a connection to %s. "
               "One of you might be on a network that does not allow "
               "direct connections."),
-          empathy_contact_get_name (priv->contact));
+          empathy_contact_get_alias (priv->contact));
 
       case TP_MEDIA_STREAM_ERROR_NETWORK_ERROR:
           return g_strdup (_("There was a failure on the network"));
@@ -1977,6 +2401,10 @@ media_stream_error_to_txt (EmpathyCallWindow *self,
       case TP_MEDIA_STREAM_ERROR_MEDIA_ERROR:
         return g_strdup (_("There was a failure in the call engine"));
 
+      case TP_MEDIA_STREAM_ERROR_EOS:
+        return g_strdup (_("The end of the stream was reached"));
+
+      case TP_MEDIA_STREAM_ERROR_UNKNOWN:
       default:
         return NULL;
     }
@@ -2035,21 +2463,22 @@ empathy_call_window_connected (gpointer user_data)
   EmpathyTpCall *call;
   gboolean can_send_video;
 
-  empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
+  empathy_sound_manager_stop (priv->sound_mgr, EMPATHY_SOUND_PHONE_OUTGOING);
 
   can_send_video = priv->video_input != NULL && priv->contact != NULL &&
     empathy_contact_can_voip_video (priv->contact);
 
   g_object_get (priv->handler, "tp-call", &call, NULL);
 
-  g_signal_connect (call, "notify::video-stream",
-    G_CALLBACK (empathy_call_window_video_stream_changed_cb), self);
+  tp_g_signal_connect_object (call, "notify::video-stream",
+    G_CALLBACK (empathy_call_window_video_stream_changed_cb),
+    self, 0);
 
   if (empathy_tp_call_has_dtmf (call))
     gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
 
   if (priv->video_input == NULL)
-    empathy_call_window_set_send_video (self, FALSE);
+    empathy_call_window_set_send_video (self, CAMERA_STATE_OFF);
 
   priv->sending_video = can_send_video ?
     empathy_tp_call_is_sending_video (call) : FALSE;
@@ -2064,11 +2493,6 @@ empathy_call_window_connected (gpointer user_data)
 
   gtk_widget_set_sensitive (priv->mic_button, TRUE);
 
-  /* FIXME: this should won't be needed once bug #602937 is fixed
-   * (see empathy_call_window_disconnected for details) */
-  gtk_widget_set_sensitive (priv->tool_button_camera_preview, TRUE);
-  gtk_action_set_sensitive (priv->action_camera_preview, TRUE);
-
   empathy_call_window_update_avatars_visibility (call, self);
 
   g_object_unref (call);
@@ -2082,6 +2506,8 @@ empathy_call_window_connected (gpointer user_data)
 
   empathy_call_window_update_timer (self);
 
+  gtk_action_set_sensitive (priv->menu_fullscreen, TRUE);
+
   return FALSE;
 }
 
@@ -2222,6 +2648,7 @@ empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
                     g_warning ("Could not link videp soure input pipeline");
                     break;
                   }
+                gst_object_unref (pad);
               }
 
             retval = TRUE;
@@ -2240,6 +2667,8 @@ empathy_call_window_remove_video_input (EmpathyCallWindow *self)
   EmpathyCallWindowPriv *priv = GET_PRIV (self);
   GstElement *preview;
 
+  disable_camera (self);
+
   DEBUG ("remove video input");
   preview = empathy_video_widget_get_element (
     EMPATHY_VIDEO_WIDGET (priv->video_preview));
@@ -2258,11 +2687,8 @@ empathy_call_window_remove_video_input (EmpathyCallWindow *self)
   gtk_widget_destroy (priv->video_preview);
   priv->video_preview = NULL;
 
-  gtk_toggle_tool_button_set_active (
-      GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_on), FALSE);
   gtk_widget_set_sensitive (priv->tool_button_camera_on, FALSE);
-
-  gtk_widget_show (priv->self_user_avatar_widget);
+  gtk_widget_set_sensitive (priv->tool_button_camera_preview, FALSE);
 }
 
 static void
@@ -2271,8 +2697,8 @@ start_call (EmpathyCallWindow *self)
   EmpathyCallWindowPriv *priv = GET_PRIV (self);
 
   priv->call_started = TRUE;
-  empathy_call_handler_start_call (priv->handler);
-  gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
+  empathy_call_handler_start_call (priv->handler,
+      gtk_get_current_event_time ());
 
   if (empathy_call_handler_has_initial_video (priv->handler))
     {
@@ -2307,7 +2733,11 @@ empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
             gst_message_parse_state_changed (message, NULL, &newstate, NULL);
             if (newstate == GST_STATE_PAUSED)
               {
-                start_call (self);
+                gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
+                priv->pipeline_playing = TRUE;
+
+                if (priv->start_call_when_playing)
+                  start_call (self);
               }
           }
         break;
@@ -2332,11 +2762,30 @@ empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
             }
           else
             {
-              empathy_call_window_disconnected (self);
+              empathy_call_window_disconnected (self, TRUE);
             }
           g_error_free (error);
           g_free (debug);
         }
+      case GST_MESSAGE_UNKNOWN:
+      case GST_MESSAGE_EOS:
+      case GST_MESSAGE_WARNING:
+      case GST_MESSAGE_INFO:
+      case GST_MESSAGE_TAG:
+      case GST_MESSAGE_BUFFERING:
+      case GST_MESSAGE_STATE_DIRTY:
+      case GST_MESSAGE_STEP_DONE:
+      case GST_MESSAGE_CLOCK_PROVIDE:
+      case GST_MESSAGE_CLOCK_LOST:
+      case GST_MESSAGE_NEW_CLOCK:
+      case GST_MESSAGE_STRUCTURE_CHANGE:
+      case GST_MESSAGE_STREAM_STATUS:
+      case GST_MESSAGE_APPLICATION:
+      case GST_MESSAGE_ELEMENT:
+      case GST_MESSAGE_SEGMENT_START:
+      case GST_MESSAGE_SEGMENT_DONE:
+      case GST_MESSAGE_DURATION:
+      case GST_MESSAGE_ANY:
       default:
         break;
     }
@@ -2374,10 +2823,10 @@ call_handler_notify_tp_call_cb (EmpathyCallHandler *handler,
   if (call == NULL)
     return;
 
-  empathy_signal_connect_weak (call, "audio-stream-error",
-      G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (self));
-  empathy_signal_connect_weak (call, "video-stream-error",
-      G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (self));
+  tp_g_signal_connect_object (call, "audio-stream-error",
+      G_CALLBACK (empathy_call_window_audio_stream_error), self, 0);
+  tp_g_signal_connect_object (call, "video-stream-error",
+      G_CALLBACK (empathy_call_window_video_stream_error), self, 0);
 
   g_object_unref (call);
 }
@@ -2404,10 +2853,12 @@ empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
   g_object_get (priv->handler, "tp-call", &call, NULL);
   if (call != NULL)
     {
-      empathy_signal_connect_weak (call, "audio-stream-error",
-        G_CALLBACK (empathy_call_window_audio_stream_error), G_OBJECT (window));
-      empathy_signal_connect_weak (call, "video-stream-error",
-        G_CALLBACK (empathy_call_window_video_stream_error), G_OBJECT (window));
+      tp_g_signal_connect_object (call, "audio-stream-error",
+        G_CALLBACK (empathy_call_window_audio_stream_error), window,
+        0);
+      tp_g_signal_connect_object (call, "video-stream-error",
+        G_CALLBACK (empathy_call_window_video_stream_error), window,
+        0);
 
       g_object_unref (call);
     }
@@ -2440,7 +2891,7 @@ empathy_call_window_delete_cb (GtkWidget *widget, GdkEvent*event,
     }
 
   if (priv->call_state == CONNECTING)
-    empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
+    empathy_sound_manager_stop (priv->sound_mgr, EMPATHY_SOUND_PHONE_OUTGOING);
 
   return FALSE;
 }
@@ -2486,10 +2937,15 @@ show_borders (EmpathyCallWindow *window, gboolean set_fullscreen)
       set_fullscreen ? 0 : CONTENT_HBOX_BORDER_WIDTH);
   gtk_box_set_spacing (GTK_BOX (priv->content_hbox),
       set_fullscreen ? 0 : CONTENT_HBOX_SPACING);
-  gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
-      priv->video_output, TRUE, TRUE,
-      set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
-      GTK_PACK_START);
+
+  if (priv->video_output != NULL)
+    {
+      gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
+          priv->video_output, TRUE, TRUE,
+          set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
+          GTK_PACK_START);
+    }
+
   gtk_box_set_child_packing (GTK_BOX (priv->content_hbox),
       priv->vbox, TRUE, TRUE,
       set_fullscreen ? 0 : CONTENT_HBOX_CHILDREN_PACKING_PADDING,
@@ -2533,12 +2989,7 @@ empathy_call_window_state_event_cb (GtkWidget *widget,
         }
       else
         {
-          if (priv->video_output_motion_handler_id != 0)
-            {
-              g_signal_handler_disconnect (G_OBJECT (priv->video_output),
-                  priv->video_output_motion_handler_id);
-              priv->video_output_motion_handler_id = 0;
-            }
+          disconnect_video_output_motion_handler (window);
         }
 
       empathy_call_window_fullscreen_set_fullscreen (priv->fullscreen,
@@ -2590,23 +3041,31 @@ empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
 
 static void
 empathy_call_window_set_send_video (EmpathyCallWindow *window,
-  gboolean send)
+  CameraState state)
 {
   EmpathyCallWindowPriv *priv = GET_PRIV (window);
   EmpathyTpCall *call;
 
-  priv->sending_video = send;
+  priv->sending_video = (state == CAMERA_STATE_ON);
 
-  /* When we start sending video, we want to show the video preview by
-     default. */
-  display_video_preview (window, send);
+  if (state == CAMERA_STATE_PREVIEW ||
+      state == CAMERA_STATE_ON)
+    {
+      /* When we start sending video, we want to show the video preview by
+         default. */
+      display_video_preview (window, TRUE);
+    }
+  else
+    {
+      display_video_preview (window, FALSE);
+    }
 
   if (priv->call_state != CONNECTED)
     return;
 
   g_object_get (priv->handler, "tp-call", &call, NULL);
-  DEBUG ("%s sending video", send ? "start": "stop");
-  empathy_tp_call_request_video_stream_direction (call, send);
+  DEBUG ("%s sending video", priv->sending_video ? "start": "stop");
+  empathy_tp_call_request_video_stream_direction (call, priv->sending_video);
   g_object_unref (call);
 }
 
@@ -2617,9 +3076,6 @@ empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
   EmpathyCallWindowPriv *priv = GET_PRIV (window);
   gboolean active;
 
-  if (priv->audio_input == NULL)
-    return;
-
   active = (gtk_toggle_tool_button_get_active (toggle));
 
   if (active)
@@ -2642,7 +3098,7 @@ empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
 }
 
 static void
-empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
+empathy_call_window_sidebar_hidden_cb (EvSidebar *sidebar,
   EmpathyCallWindow *window)
 {
   EmpathyCallWindowPriv *priv = GET_PRIV (window);
@@ -2652,7 +3108,7 @@ empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
 }
 
 static void
-empathy_call_window_sidebar_shown_cb (EmpathySidebar *sidebar,
+empathy_call_window_sidebar_shown_cb (EvSidebar *sidebar,
   EmpathyCallWindow *window)
 {
   EmpathyCallWindowPriv *priv = GET_PRIV (window);
@@ -2665,26 +3121,24 @@ static void
 empathy_call_window_hangup_cb (gpointer object,
                                EmpathyCallWindow *window)
 {
-  if (empathy_call_window_disconnected (window))
+  EmpathyCallWindowPriv *priv = GET_PRIV (window);
+
+  empathy_call_handler_stop_call (priv->handler);
+
+  if (empathy_call_window_disconnected (window, FALSE))
     gtk_widget_destroy (GTK_WIDGET (window));
 }
 
 static void
 empathy_call_window_restart_call (EmpathyCallWindow *window)
 {
-  GstBus *bus;
   EmpathyCallWindowPriv *priv = GET_PRIV (window);
 
-  gtk_widget_destroy (priv->remote_user_output_hbox);
-  gtk_widget_destroy (priv->self_user_output_hbox);
-
-  priv->pipeline = gst_pipeline_new (NULL);
-  bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
-  priv->bus_message_source_id = gst_bus_add_watch (bus,
-      empathy_call_window_bus_message, window);
+  /* Remove error info bars */
+  gtk_container_forall (GTK_CONTAINER (priv->errors_vbox),
+      (GtkCallback) gtk_widget_destroy, NULL);
 
-  empathy_call_window_setup_remote_frame (bus, window);
-  empathy_call_window_setup_self_frame (bus, window);
+  create_video_output_widget (window);
 
   g_signal_connect (G_OBJECT (priv->audio_input_adj), "value-changed",
       G_CALLBACK (empathy_call_window_mic_volume_changed_cb), window);
@@ -2694,14 +3148,16 @@ empathy_call_window_restart_call (EmpathyCallWindow *window)
    * been updated during that time. That's why we manually update it here */
   empathy_call_window_mic_volume_changed_cb (priv->audio_input_adj, window);
 
-  g_object_unref (bus);
-
-  gtk_widget_show_all (priv->content_hbox);
-
   priv->outgoing = TRUE;
   empathy_call_window_set_state_connecting (window);
 
-  start_call (window);
+  if (priv->pipeline_playing)
+    start_call (window);
+  else
+    /* call will be started when the pipeline is ready */
+    priv->start_call_when_playing = TRUE;
+
+
   empathy_call_window_setup_avatars (window, priv->handler);
 
   gtk_action_set_sensitive (priv->redial, FALSE);
@@ -2760,7 +3216,7 @@ empathy_call_window_key_press_cb (GtkWidget *video_output,
 {
   EmpathyCallWindowPriv *priv = GET_PRIV (window);
 
-  if (priv->is_fullscreen && event->keyval == GDK_Escape)
+  if (priv->is_fullscreen && event->keyval == GDK_KEY_Escape)
     {
       /* Since we are in fullscreen mode, toggling will bring us back to
          normal mode. */