]> git.0d.be Git - empathy.git/blobdiff - src/empathy-call-window.c
Port EmpathyCallWindow to new API
[empathy.git] / src / empathy-call-window.c
index 0c10c8d5cc72c22ab65d161186798613c95002b9..48b6b664705f42e9cb8e98fdf05bf9e65516fed5 100644 (file)
@@ -22,6 +22,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <math.h>
+
 #include <gst/gst.h>
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
@@ -73,7 +75,14 @@ struct _EmpathyCallWindowPriv
   GtkWidget *sidebar_button;
   GtkWidget *statusbar;
   GtkWidget *volume_button;
+  GtkWidget *mic_button;
   GtkWidget *camera_button;
+  GtkWidget *toolbar;
+  GtkWidget *hangup;
+  GtkWidget *pane;
+
+  gdouble volume;
+  GtkAdjustment *audio_input_adj;
 
   GtkWidget *dtmf_panel;
 
@@ -86,13 +95,17 @@ struct _EmpathyCallWindowPriv
   GstElement *funnel;
   GstElement *liveadder;
 
-  GladeXML *glade;
   guint context_id;
 
   GTimer *timer;
   guint timer_id;
 
+  GtkWidget *video_contrast;
+  GtkWidget *video_brightness;
+  GtkWidget *video_gamma;
+
   GMutex *lock;
+  gboolean call_started;
 };
 
 #define GET_PRIV(o) \
@@ -111,6 +124,9 @@ static void empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
 static void empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
   EmpathyCallWindow *window);
 
+static void empathy_call_window_mic_toggled_cb (
+  GtkToggleToolButton *toggle, EmpathyCallWindow *window);
+
 static void empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
   EmpathyCallWindow *window);
 
@@ -119,6 +135,9 @@ static void empathy_call_window_hangup (EmpathyCallWindow *window);
 static void empathy_call_window_status_message (EmpathyCallWindow *window,
   gchar *message);
 
+static gboolean empathy_call_window_bus_message (GstBus *bus,
+  GstMessage *message, gpointer user_data);
+
 static void
 empathy_call_window_volume_changed_cb (GtkScaleButton *button,
   gdouble value, EmpathyCallWindow *window);
@@ -127,10 +146,8 @@ static void
 empathy_call_window_setup_menubar (EmpathyCallWindow *self)
 {
   EmpathyCallWindowPriv *priv = GET_PRIV (self);
-  GtkWidget *hangup;
 
-  hangup = glade_xml_get_widget (priv->glade, "menuhangup");
-  g_signal_connect_swapped (G_OBJECT (hangup), "activate",
+  g_signal_connect_swapped (priv->hangup, "activate",
     G_CALLBACK (empathy_call_window_hangup), self);
 }
 
@@ -138,28 +155,23 @@ static void
 empathy_call_window_setup_toolbar (EmpathyCallWindow *self)
 {
   EmpathyCallWindowPriv *priv = GET_PRIV (self);
-  GtkWidget *hangup;
-  GtkWidget *mic;
-  GtkWidget *camera;
-  GtkWidget *toolbar;
   GtkToolItem *tool_item;
 
-  hangup = glade_xml_get_widget (priv->glade, "hangup");
-
-  g_signal_connect_swapped (G_OBJECT (hangup), "clicked",
+  g_signal_connect_swapped (priv->hangup, "clicked",
     G_CALLBACK (empathy_call_window_hangup), self);
 
-  mic = glade_xml_get_widget (priv->glade, "microphone");
-  gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (mic), TRUE);
+  gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (priv->mic_button),
+      TRUE);
 
-  toolbar = glade_xml_get_widget (priv->glade, "toolbar");
+  g_signal_connect (G_OBJECT (priv->mic_button), "toggled",
+    G_CALLBACK (empathy_call_window_mic_toggled_cb), self);
 
   /* Add an empty expanded GtkToolItem so the volume button is at the end of
    * the toolbar. */
   tool_item = gtk_tool_item_new ();
   gtk_tool_item_set_expand (tool_item, TRUE);
   gtk_widget_show (GTK_WIDGET (tool_item));
-  gtk_toolbar_insert (GTK_TOOLBAR (toolbar), tool_item, -1);
+  gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
 
   priv->volume_button = gtk_volume_button_new ();
   /* FIXME listen to the audiosinks signals and update the button according to
@@ -172,14 +184,13 @@ empathy_call_window_setup_toolbar (EmpathyCallWindow *self)
   tool_item = gtk_tool_item_new ();
   gtk_container_add (GTK_CONTAINER (tool_item), priv->volume_button);
   gtk_widget_show_all (GTK_WIDGET (tool_item));
-  gtk_toolbar_insert (GTK_TOOLBAR (toolbar), tool_item, -1);
+  gtk_toolbar_insert (GTK_TOOLBAR (priv->toolbar), tool_item, -1);
 
+  gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (priv->camera_button),
+      FALSE);
+  gtk_widget_set_sensitive (priv->camera_button, FALSE);
 
-  camera = glade_xml_get_widget (priv->glade, "camera");
-  priv->camera_button = camera;
-  gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (camera), FALSE);
-
-  g_signal_connect (G_OBJECT (camera), "toggled",
+  g_signal_connect (priv->camera_button, "toggled",
     G_CALLBACK (empathy_call_window_camera_toggled_cb), self);
 }
 
@@ -260,53 +271,198 @@ empathy_call_window_create_dtmf (EmpathyCallWindow *self)
   return table;
 }
 
+static GtkWidget *
+empathy_call_window_create_video_input_add_slider (EmpathyCallWindow *self,
+  gchar *label_text, GtkWidget *bin)
+{
+   GtkWidget *vbox = gtk_vbox_new (FALSE, 2);
+   GtkWidget *scale = gtk_vscale_new_with_range (0, 100, 10);
+   GtkWidget *label = gtk_label_new (label_text);
+
+   gtk_widget_set_sensitive (scale, FALSE);
+
+   gtk_container_add (GTK_CONTAINER (bin), vbox);
+
+   gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
+   gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 0);
+   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+   return scale;
+}
+
+static void
+empathy_call_window_video_contrast_changed_cb (GtkAdjustment *adj,
+  EmpathyCallWindow *self)
+
+{
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
+
+  empathy_video_src_set_channel (priv->video_input,
+    EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST, gtk_adjustment_get_value (adj));
+}
+
+static void
+empathy_call_window_video_brightness_changed_cb (GtkAdjustment *adj,
+  EmpathyCallWindow *self)
+
+{
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
+
+  empathy_video_src_set_channel (priv->video_input,
+    EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS, gtk_adjustment_get_value (adj));
+}
+
+static void
+empathy_call_window_video_gamma_changed_cb (GtkAdjustment *adj,
+  EmpathyCallWindow *self)
+
+{
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
+
+  empathy_video_src_set_channel (priv->video_input,
+    EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA, gtk_adjustment_get_value (adj));
+}
+
+
 static GtkWidget *
 empathy_call_window_create_video_input (EmpathyCallWindow *self)
 {
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
   GtkWidget *hbox;
-  int i;
-  gchar *controls[] = { _("Contrast"), _("Brightness"), _("Gamma"), NULL };
 
   hbox = gtk_hbox_new (TRUE, 3);
 
-  for (i = 0; controls[i] != NULL; i++)
+  priv->video_contrast = empathy_call_window_create_video_input_add_slider (
+    self,  _("Contrast"), hbox);
+
+  priv->video_brightness = empathy_call_window_create_video_input_add_slider (
+    self,  _("Brightness"), hbox);
+
+  priv->video_gamma = empathy_call_window_create_video_input_add_slider (
+    self,  _("Gamma"), hbox);
+
+  return hbox;
+}
+
+static void
+empathy_call_window_setup_video_input (EmpathyCallWindow *self)
+{
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
+  guint supported;
+  GtkAdjustment *adj;
+
+  supported = empathy_video_src_get_supported_channels (priv->video_input);
+
+  if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_CONTRAST)
     {
-      GtkWidget *vbox = gtk_vbox_new (FALSE, 2);
-      GtkWidget *scale = gtk_vscale_new_with_range (0, 100, 10);
-      GtkWidget *label = gtk_label_new (controls[i]);
+      adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_contrast));
+
+      gtk_adjustment_set_value (adj,
+        empathy_video_src_get_channel (priv->video_input,
+          EMPATHY_GST_VIDEO_SRC_CHANNEL_CONTRAST));
 
-      gtk_container_add (GTK_CONTAINER (hbox), vbox);
+      g_signal_connect (G_OBJECT (adj), "value-changed",
+        G_CALLBACK (empathy_call_window_video_contrast_changed_cb), self);
 
-      gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
-      gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 0);
-      gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+      gtk_widget_set_sensitive (priv->video_contrast, TRUE);
     }
 
-  return hbox;
+  if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_BRIGHTNESS)
+    {
+      adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_brightness));
+
+      gtk_adjustment_set_value (adj,
+        empathy_video_src_get_channel (priv->video_input,
+          EMPATHY_GST_VIDEO_SRC_CHANNEL_BRIGHTNESS));
+
+      g_signal_connect (G_OBJECT (adj), "value-changed",
+        G_CALLBACK (empathy_call_window_video_brightness_changed_cb), self);
+      gtk_widget_set_sensitive (priv->video_brightness, TRUE);
+    }
+
+  if (supported & EMPATHY_GST_VIDEO_SRC_SUPPORTS_GAMMA)
+    {
+      adj = gtk_range_get_adjustment (GTK_RANGE (priv->video_gamma));
+
+      gtk_adjustment_set_value (adj,
+        empathy_video_src_get_channel (priv->video_input,
+          EMPATHY_GST_VIDEO_SRC_CHANNEL_GAMMA));
+
+      g_signal_connect (G_OBJECT (adj), "value-changed",
+        G_CALLBACK (empathy_call_window_video_gamma_changed_cb), self);
+      gtk_widget_set_sensitive (priv->video_gamma, TRUE);
+    }
+}
+
+static void
+empathy_call_window_mic_volume_changed_cb (GtkAdjustment *adj,
+  EmpathyCallWindow *self)
+{
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
+  gdouble volume;
+
+  volume =  gtk_adjustment_get_value (adj)/100.0;
+
+  /* Don't store the volume because of muting */
+  if (volume > 0 || gtk_toggle_tool_button_get_active (
+        GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
+    priv->volume = volume;
+
+  /* Ensure that the toggle button is active if the volume is > 0 and inactive
+   * if it's smaller then 0 */
+  if ((volume > 0) != gtk_toggle_tool_button_get_active (
+        GTK_TOGGLE_TOOL_BUTTON (priv->mic_button)))
+    gtk_toggle_tool_button_set_active (
+      GTK_TOGGLE_TOOL_BUTTON (priv->mic_button), volume > 0);
+
+  empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
+    volume);
+}
+
+static void
+empathy_call_window_audio_input_level_changed_cb (EmpathyGstAudioSrc *src,
+  gdouble level, GtkProgressBar *bar)
+{
+  gdouble value;
+
+  value = CLAMP (pow (10, level / 20), 0.0, 1.0);
+  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (bar), value);
 }
 
 static GtkWidget *
 empathy_call_window_create_audio_input (EmpathyCallWindow *self)
 {
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
   GtkWidget *hbox, *vbox, *scale, *progress, *label;
+  GtkAdjustment *adj;
 
   hbox = gtk_hbox_new (TRUE, 3);
 
   vbox = gtk_vbox_new (FALSE, 3);
   gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 3);
 
-  scale = gtk_vscale_new_with_range (0, 100, 10);
+  scale = gtk_vscale_new_with_range (0, 150, 100);
   gtk_range_set_inverted (GTK_RANGE (scale), TRUE);
   label = gtk_label_new (_("Volume"));
 
+  priv->audio_input_adj = adj = gtk_range_get_adjustment (GTK_RANGE (scale));
+  priv->volume =  empathy_audio_src_get_volume (EMPATHY_GST_AUDIO_SRC
+    (priv->audio_input));
+  gtk_adjustment_set_value (adj, priv->volume * 100);
+
+  g_signal_connect (G_OBJECT (adj), "value-changed",
+    G_CALLBACK (empathy_call_window_mic_volume_changed_cb), self);
+
   gtk_box_pack_start (GTK_BOX (vbox), scale, TRUE, TRUE, 3);
   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 3);
 
-
   progress = gtk_progress_bar_new ();
   gtk_progress_bar_set_orientation (GTK_PROGRESS_BAR (progress),
     GTK_PROGRESS_BOTTOM_TO_TOP);
-  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress), 0.5);
+  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (progress), 0);
+
+  g_signal_connect (priv->audio_input, "peak-level-changed",
+    G_CALLBACK (empathy_call_window_audio_input_level_changed_cb), progress);
 
   gtk_box_pack_start (GTK_BOX (hbox), progress, FALSE, FALSE, 3);
 
@@ -317,25 +473,28 @@ static void
 empathy_call_window_init (EmpathyCallWindow *self)
 {
   EmpathyCallWindowPriv *priv = GET_PRIV (self);
+  GtkBuilder *gui;
   GtkWidget *vbox, *top_vbox;
   GtkWidget *hbox, *h;
   GtkWidget *arrow;
   GtkWidget *page;
   GstBus *bus;
   gchar *filename;
-  GtkWidget *pane;
-
-  filename = empathy_file_lookup ("empathy-call-window.glade", "src");
 
-  priv->glade = empathy_glade_get_file (filename, "call_window", NULL,
+  filename = empathy_file_lookup ("empathy-call-window.ui", "src");
+  gui = empathy_builder_get_file (filename,
     "call_window_vbox", &top_vbox,
-    "pane", &pane,
+    "pane", &priv->pane,
     "statusbar", &priv->statusbar,
+    "microphone", &priv->mic_button,
+    "camera", &priv->camera_button,
+    "toolbar", &priv->toolbar,
+    "hangup", &priv->hangup,
     NULL);
 
   priv->lock = g_mutex_new ();
 
-  gtk_widget_reparent (top_vbox, GTK_WIDGET (self));
+  gtk_container_add (GTK_CONTAINER (self), top_vbox);
 
   empathy_call_window_setup_menubar (self);
   empathy_call_window_setup_toolbar (self);
@@ -344,10 +503,12 @@ empathy_call_window_init (EmpathyCallWindow *self)
 
   hbox = gtk_hbox_new (FALSE, 3);
   gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
-  gtk_paned_pack1 (GTK_PANED(pane), hbox, TRUE, FALSE);
+  gtk_paned_pack1 (GTK_PANED (priv->pane), hbox, TRUE, FALSE);
 
   bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
 
+  gst_bus_add_watch (bus, empathy_call_window_bus_message, self);
+
   priv->video_output = empathy_video_widget_new (bus);
   gtk_box_pack_start (GTK_BOX (hbox), priv->video_output, TRUE, TRUE, 3);
 
@@ -359,7 +520,7 @@ empathy_call_window_init (EmpathyCallWindow *self)
   gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 3);
 
   priv->video_preview = empathy_video_widget_new_with_size (bus, 160, 120);
-  g_object_set (priv->video_preview, "sync", FALSE, "async", FALSE, NULL);
+  g_object_set (priv->video_preview, "sync", FALSE, "async", TRUE, NULL);
   gtk_box_pack_start (GTK_BOX (vbox), priv->video_preview, FALSE, FALSE, 0);
 
   priv->video_input = empathy_video_src_new ();
@@ -391,7 +552,7 @@ empathy_call_window_init (EmpathyCallWindow *self)
   g_signal_connect (G_OBJECT (priv->sidebar),
     "hide", G_CALLBACK (empathy_call_window_sidebar_hidden_cb),
     self);
-  gtk_paned_pack2 (GTK_PANED(pane), priv->sidebar, FALSE, FALSE);
+  gtk_paned_pack2 (GTK_PANED (priv->pane), priv->sidebar, FALSE, FALSE);
 
   priv->dtmf_panel = empathy_call_window_create_dtmf (self);
   empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Dialpad"),
@@ -420,6 +581,8 @@ empathy_call_window_init (EmpathyCallWindow *self)
   empathy_call_window_status_message (self, _("Connecting..."));
 
   priv->timer = g_timer_new ();
+
+  g_object_unref (gui);
 }
 
 static void empathy_call_window_dispose (GObject *object);
@@ -561,12 +724,28 @@ empathy_call_window_conference_added_cb (EmpathyCallHandler *handler,
   gst_element_set_state (conference, GST_STATE_PLAYING);
 }
 
-static void
-empathy_call_window_channel_closed_cb (TfChannel *channel, gpointer user_data)
+static gboolean
+empathy_call_window_request_resource_cb (EmpathyCallHandler *handler,
+  FsMediaType type, FsStreamDirection direction, gpointer user_data)
 {
   EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
   EmpathyCallWindowPriv *priv = GET_PRIV (self);
 
+  if (type != TP_MEDIA_STREAM_TYPE_VIDEO)
+    return TRUE;
+
+  if (direction == FS_DIRECTION_RECV)
+    return TRUE;
+
+  /* video and direction is send */
+  return priv->video_input != NULL;
+}
+
+static void
+empathy_call_window_disconnected (EmpathyCallWindow *self)
+{
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
+
   g_mutex_lock (priv->lock);
 
   g_timer_stop (priv->timer);
@@ -582,6 +761,15 @@ empathy_call_window_channel_closed_cb (TfChannel *channel, gpointer user_data)
   gtk_widget_set_sensitive (priv->camera_button, FALSE);
 }
 
+
+static void
+empathy_call_window_channel_closed_cb (TfChannel *channel, gpointer user_data)
+{
+  EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
+
+  empathy_call_window_disconnected (self);
+}
+
 /* Called with global lock held */
 static GstPad *
 empathy_call_window_get_video_sink_pad (EmpathyCallWindow *self)
@@ -648,7 +836,7 @@ empathy_call_window_update_timer (gpointer user_data)
   time = g_timer_elapsed (priv->timer, NULL);
 
   /* Translators: number of minutes:seconds the caller has been connected */
-  str = g_strdup_printf (_("Connected -- %d:%02dm"), (int) time / 60,
+  str = g_strdup_printf (_("Connected  %d:%02dm"), (int) time / 60,
     (int) time % 60);
   empathy_call_window_status_message (self, str);
   g_free (str);
@@ -668,6 +856,9 @@ empathy_call_window_connected (gpointer user_data)
   if (empathy_tp_call_has_dtmf (call))
     gtk_widget_set_sensitive (priv->dtmf_panel, TRUE);
 
+  if (priv->video_input != NULL)
+    gtk_widget_set_sensitive (priv->camera_button, TRUE);
+
   g_object_unref (call);
 
   g_mutex_lock (priv->lock);
@@ -733,14 +924,18 @@ empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
     {
       case TP_MEDIA_STREAM_TYPE_AUDIO:
         gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input);
-        gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
 
         pad = gst_element_get_static_pad (priv->audio_input, "src");
         gst_pad_link (pad, sink);
+
+        gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
         break;
       case TP_MEDIA_STREAM_TYPE_VIDEO:
-        pad =  gst_element_get_request_pad (priv->video_tee, "src%d");
-        gst_pad_link (pad, sink);
+        if (priv->video_input != NULL)
+          {
+            pad =  gst_element_get_request_pad (priv->video_tee, "src%d");
+            gst_pad_link (pad, sink);
+          }
         break;
       default:
         g_assert_not_reached ();
@@ -748,15 +943,141 @@ empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
 
 }
 
+static gboolean
+empathy_gst_bin_has_child (GstBin *bin, GstElement *element)
+{
+  GstIterator *it;
+  gboolean ret = FALSE;
+  GstElement *item;
+
+  it = gst_bin_iterate_recurse (bin);
+
+  for (;;)
+    {
+      switch (gst_iterator_next (it, (gpointer *)&item))
+       {
+         case GST_ITERATOR_OK:
+           if (item == element)
+            {
+              gst_object_unref (GST_OBJECT (item));
+              ret = TRUE;
+              goto out;
+            }
+           gst_object_unref (GST_OBJECT (item));
+           break;
+         case GST_ITERATOR_RESYNC:
+           gst_iterator_resync (it);
+           break;
+        case GST_ITERATOR_ERROR:
+           g_assert_not_reached ();
+           /* fallthrough */
+        case GST_ITERATOR_DONE:
+           goto out;
+           break;
+      }
+    }
+    gst_iterator_free (it);
+
+out:
+  return ret;
+}
+
+static void
+empathy_call_window_remove_video_input (EmpathyCallWindow *self)
+{
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
+  GstElement *preview;
+
+  preview = empathy_video_widget_get_element (
+    EMPATHY_VIDEO_WIDGET (priv->video_preview));
+
+  gst_element_set_state (priv->video_input, GST_STATE_NULL);
+  gst_element_set_state (priv->video_tee, GST_STATE_NULL);
+  gst_element_set_state (preview, GST_STATE_NULL);
+
+  gst_bin_remove_many (GST_BIN (priv->pipeline), priv->video_input,
+    priv->video_tee, preview, NULL);
+
+  g_object_unref (priv->video_input);
+  priv->video_input = NULL;
+  g_object_unref (priv->video_tee);
+  priv->video_tee = NULL;
+}
+
+
+static gboolean
+empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
+  gpointer user_data)
+{
+  EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
+  EmpathyCallWindowPriv *priv = GET_PRIV (self);
+  GstState newstate;
+
+  empathy_call_handler_bus_message (priv->handler, bus, message);
+
+  switch (GST_MESSAGE_TYPE (message))
+    {
+      case GST_MESSAGE_STATE_CHANGED:
+        if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->video_input))
+          {
+            gst_message_parse_state_changed (message, NULL, &newstate, NULL);
+            if (newstate == GST_STATE_PAUSED)
+                empathy_call_window_setup_video_input (self);
+          }
+        if (GST_MESSAGE_SRC (message) == GST_OBJECT (priv->pipeline) &&
+            !priv->call_started)
+          {
+            gst_message_parse_state_changed (message, NULL, &newstate, NULL);
+            if (newstate == GST_STATE_PAUSED)
+              {
+                priv->call_started = TRUE;
+                empathy_call_handler_start_call (priv->handler);
+                gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
+              }
+          }
+        break;
+      case GST_MESSAGE_ERROR:
+        {
+          GError *error;
+          gchar *debug;
+
+          gst_message_parse_error (message, &error, &debug);
+
+          g_message ("Element error: %s -- %s\n", error->message, debug);
+
+          if (priv->video_input != NULL &&
+              empathy_gst_bin_has_child (GST_BIN (priv->video_input),
+                GST_ELEMENT (GST_MESSAGE_SRC (message))))
+            {
+              /* Remove the video input and continue */
+              empathy_call_window_remove_video_input (self);
+              gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
+            }
+          else
+            {
+              gst_element_set_state (priv->pipeline, GST_STATE_NULL);
+              empathy_call_window_disconnected (self);
+            }
+          g_error_free (error);
+          g_free (debug);
+        }
+      default:
+        break;
+    }
+
+  return TRUE;
+}
+
 static void
 empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
 {
   EmpathyCallWindowPriv *priv = GET_PRIV (window);
   GstElement *preview;
-  GstBus *bus;
 
   g_signal_connect (priv->handler, "conference-added",
     G_CALLBACK (empathy_call_window_conference_added_cb), window);
+  g_signal_connect (priv->handler, "request-resource",
+    G_CALLBACK (empathy_call_window_request_resource_cb), window);
   g_signal_connect (priv->handler, "closed",
     G_CALLBACK (empathy_call_window_channel_closed_cb), window);
   g_signal_connect (priv->handler, "src-pad-added",
@@ -764,9 +1085,6 @@ empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
   g_signal_connect (priv->handler, "sink-pad-added",
     G_CALLBACK (empathy_call_window_sink_added_cb), window);
 
-  bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
-  empathy_call_handler_set_bus (priv->handler, bus);
-  empathy_call_handler_start_call (priv->handler);
 
   preview = empathy_video_widget_get_element (
     EMPATHY_VIDEO_WIDGET (priv->video_preview));
@@ -776,9 +1094,7 @@ empathy_call_window_realized_cb (GtkWidget *widget, EmpathyCallWindow *window)
   gst_element_link_many (priv->video_input, priv->video_tee,
     preview, NULL);
 
-  gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
-
-  g_object_unref (bus);
+  gst_element_set_state (priv->pipeline, GST_STATE_PAUSED);
 }
 
 static gboolean
@@ -798,19 +1114,30 @@ empathy_call_window_sidebar_toggled_cb (GtkToggleButton *toggle,
 {
   EmpathyCallWindowPriv *priv = GET_PRIV (window);
   GtkWidget *arrow;
+  int w,h, handle_size;
+
+  w = GTK_WIDGET (window)->allocation.width;
+  h = GTK_WIDGET (window)->allocation.height;
+
+  gtk_widget_style_get (priv->pane, "handle_size", &handle_size, NULL);
 
   if (gtk_toggle_button_get_active (toggle))
     {
       arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
       gtk_widget_show (priv->sidebar);
+      w += priv->sidebar->allocation.width + handle_size;
     }
   else
     {
       arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
+      w -= priv->sidebar->allocation.width + handle_size;
       gtk_widget_hide (priv->sidebar);
     }
 
   gtk_button_set_image (GTK_BUTTON (priv->sidebar_button), arrow);
+
+  if (w > 0 && h > 0)
+    gtk_window_resize (GTK_WINDOW (window), w, h);
 }
 
 static void
@@ -830,6 +1157,34 @@ empathy_call_window_camera_toggled_cb (GtkToggleToolButton *toggle,
   g_object_unref (call);
 }
 
+static void
+empathy_call_window_mic_toggled_cb (GtkToggleToolButton *toggle,
+  EmpathyCallWindow *window)
+{
+  EmpathyCallWindowPriv *priv = GET_PRIV (window);
+  gboolean active;
+
+  active = (gtk_toggle_tool_button_get_active (toggle));
+
+  if (active)
+    {
+      empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
+        priv->volume);
+      gtk_adjustment_set_value (priv->audio_input_adj, priv->volume * 100);
+    }
+  else
+    {
+      /* TODO, Instead of setting the input volume to 0 we should probably
+       * stop sending but this would cause the audio call to drop if both
+       * sides mute at the same time on certain CMs AFAIK. Need to revisit this
+       * in the future. GNOME #574574
+       */
+      empathy_audio_src_set_volume (EMPATHY_GST_AUDIO_SRC (priv->audio_input),
+        0);
+      gtk_adjustment_set_value (priv->audio_input_adj, 0);
+    }
+}
+
 static void
 empathy_call_window_sidebar_hidden_cb (EmpathySidebar *sidebar,
   EmpathyCallWindow *window)