#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-ui-utils.h>
#include <libempathy-gtk/empathy-sound.h>
#include <libempathy-gtk/empathy-geometry.h>
+#include <libempathy-gtk/empathy-images.h>
#define DEBUG_FLAG EMPATHY_DEBUG_VOIP
#include <libempathy/empathy-debug.h>
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;
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;
};
#define GET_PRIV(o) \
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 */
}
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);
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))
+ {
+ g_warning ("Could not add video tee to pipeline");
+ return;
+ }
+
+ 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))
{
- /* 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 link video input to video tee");
return;
}
- DEBUG ("Create video preview");
+ 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);
+}
+
+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, GST_STATE_PLAYING);
- gst_element_set_state (priv->video_input, GST_STATE_PLAYING);
- gst_element_set_state (priv->video_tee, GST_STATE_PLAYING);
+ gst_element_set_state (preview, state);
+ gst_element_set_state (priv->video_input, state);
+ gst_element_set_state (priv->video_tee, state);
}
static void
/* 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);
}
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);
}
}
{
EmpathyCallWindowPriv *priv = GET_PRIV (window);
- empathy_call_window_status_message (window, _("Connecting..."));
+ empathy_call_window_status_message (window, _("Connecting…"));
priv->call_state = CONNECTING;
if (priv->outgoing)
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);
}
}
+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)
{
GtkWidget *h;
GtkWidget *arrow;
GtkWidget *page;
- GstBus *bus;
gchar *filename;
GKeyFile *keyfile;
GError *error = NULL;
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))
}
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",
"show", G_CALLBACK (empathy_call_window_sidebar_shown_cb), self);
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"),
- priv->dtmf_panel);
-
- gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
-
page = empathy_call_window_create_audio_input (self);
empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Audio input"),
page);
empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Video input"),
page);
+ priv->dtmf_panel = empathy_call_window_create_dtmf (self);
+ empathy_sidebar_add_page (EMPATHY_SIDEBAR (priv->sidebar), _("Dialpad"),
+ priv->dtmf_panel);
+
+ gtk_widget_set_sensitive (priv->dtmf_panel, FALSE);
+
+
gtk_widget_show_all (top_vbox);
gtk_widget_hide (priv->sidebar);
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
empathy_call_window_setup_avatars (self, priv->handler);
empathy_call_window_set_state_connecting (self);
- if (empathy_call_handler_has_initial_video (priv->handler))
- {
- /* Enable 'send video' buttons and display the preview */
- gtk_toggle_tool_button_set_active (
- GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_on), TRUE);
-
- display_video_preview (self, TRUE);
- }
- else
+ if (!empathy_call_handler_has_initial_video (priv->handler))
{
gtk_toggle_tool_button_set_active (
GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_off), TRUE);
}
+ /* If call has InitialVideo, the preview will be started once the call has
+ * been started (start_call()). */
}
static void 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;
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;
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,
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;
- }
-
/* free any data held directly by the object here */
g_mutex_free (priv->lock);
EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
EmpathyCallWindowPriv *priv = GET_PRIV (self);
- if (type != TP_MEDIA_STREAM_TYPE_VIDEO)
+ if (type != FS_MEDIA_TYPE_VIDEO)
return TRUE;
if (direction == FS_DIRECTION_RECV)
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;
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
}
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;
+
+ could_reset_pipeline = empathy_call_window_reset_pipeline (self);
if (priv->call_state == CONNECTING)
empathy_sound_stop (EMPATHY_SOUND_PHONE_OUTGOING);
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 */
+ gtk_widget_destroy (priv->video_output);
+ priv->video_output = NULL;
+
gtk_widget_show (priv->remote_user_avatar_widget);
priv->sending_video = FALSE;
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);
}
{
EmpathyCallWindowPriv *priv = GET_PRIV (self);
GstPad *pad;
+ GstElement *output;
if (priv->funnel == NULL)
{
- GstElement *output;
-
output = empathy_video_widget_get_element (EMPATHY_VIDEO_WIDGET
(priv->video_output));
priv->funnel = gst_element_factory_make ("fsfunnel", NULL);
- gst_bin_add (GST_BIN (priv->pipeline), priv->funnel);
- gst_bin_add (GST_BIN (priv->pipeline), output);
+ if (!priv->funnel)
+ {
+ g_warning ("Could not create fsfunnel");
+ return NULL;
+ }
+
+ if (!gst_bin_add (GST_BIN (priv->pipeline), priv->funnel))
+ {
+ gst_object_unref (priv->funnel);
+ priv->funnel = NULL;
+ g_warning ("Could not add funnel to pipeline");
+ return NULL;
+ }
+
+ if (!gst_bin_add (GST_BIN (priv->pipeline), output))
+ {
+ g_warning ("Could not add the video output widget to the pipeline");
+ goto error;
+ }
+
+ if (!gst_element_link (priv->funnel, output))
+ {
+ g_warning ("Could not link output sink to funnel");
+ goto error_output_added;
+ }
- gst_element_link (priv->funnel, output);
+ if (gst_element_set_state (output, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
+ {
+ g_warning ("Could not start video sink");
+ goto error_output_added;
+ }
- gst_element_set_state (priv->funnel, GST_STATE_PLAYING);
- gst_element_set_state (output, GST_STATE_PLAYING);
+ if (gst_element_set_state (priv->funnel, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
+ {
+ g_warning ("Could not start funnel");
+ goto error_output_added;
+ }
}
pad = gst_element_get_request_pad (priv->funnel, "sink%d");
+ if (!pad)
+ g_warning ("Could not get request pad from funnel");
+
return pad;
+
+
+ error_output_added:
+
+ gst_element_set_locked_state (priv->funnel, TRUE);
+ gst_element_set_locked_state (output, TRUE);
+
+ gst_element_set_state (priv->funnel, GST_STATE_NULL);
+ gst_element_set_state (output, GST_STATE_NULL);
+
+ gst_bin_remove (GST_BIN (priv->pipeline), output);
+ gst_element_set_locked_state (output, FALSE);
+
+ error:
+
+ gst_bin_remove (GST_BIN (priv->pipeline), priv->funnel);
+ priv->funnel = NULL;
+
+ return NULL;
}
/* Called with global lock held */
{
EmpathyCallWindowPriv *priv = GET_PRIV (self);
GstPad *pad;
+ GstElement *filter;
+ GError *gerror = NULL;
if (priv->liveadder == NULL)
{
priv->liveadder = gst_element_factory_make ("liveadder", NULL);
- gst_bin_add (GST_BIN (priv->pipeline), priv->liveadder);
- gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output);
+ if (!gst_bin_add (GST_BIN (priv->pipeline), priv->liveadder))
+ {
+ g_warning ("Could not add liveadder to the pipeline");
+ goto error_add_liveadder;
+ }
+ if (!gst_bin_add (GST_BIN (priv->pipeline), priv->audio_output))
+ {
+ g_warning ("Could not add audio sink to pipeline");
+ goto error_add_output;
+ }
- gst_element_link (priv->liveadder, priv->audio_output);
+ if (gst_element_set_state (priv->liveadder, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
+ {
+ g_warning ("Could not start liveadder");
+ goto error;
+ }
+
+ if (gst_element_set_state (priv->audio_output, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
+ {
+ g_warning ("Could not start audio sink");
+ goto error;
+ }
- gst_element_set_state (priv->liveadder, GST_STATE_PLAYING);
- gst_element_set_state (priv->audio_output, GST_STATE_PLAYING);
+ if (GST_PAD_LINK_FAILED (
+ gst_element_link (priv->liveadder, priv->audio_output)))
+ {
+ g_warning ("Could not link liveadder to audio output");
+ goto error;
+ }
+ }
+
+ filter = gst_parse_bin_from_description (
+ "audioconvert ! audioresample ! audioconvert", TRUE, &gerror);
+ if (filter == NULL)
+ {
+ g_warning ("Could not make audio conversion filter: %s", gerror->message);
+ g_clear_error (&gerror);
+ goto error;
+ }
+
+ if (!gst_bin_add (GST_BIN (priv->pipeline), filter))
+ {
+ g_warning ("Could not add audio conversion filter to pipeline");
+ gst_object_unref (filter);
+ goto error;
+ }
+
+ if (gst_element_set_state (filter, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
+ {
+ g_warning ("Could not start audio conversion filter");
+ goto error_filter;
}
- pad = gst_element_get_request_pad (priv->liveadder, "sink%d");
+ if (!gst_element_link (filter, priv->liveadder))
+ {
+ g_warning ("Could not link audio conversion filter to liveadder");
+ goto error_filter;
+ }
+
+ pad = gst_element_get_static_pad (filter, "sink");
+
+ if (pad == NULL)
+ {
+ g_warning ("Could not get sink pad from filter");
+ goto error_filter;
+ }
return pad;
+
+ error_filter:
+
+ gst_element_set_locked_state (filter, TRUE);
+ gst_element_set_state (filter, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN (priv->pipeline), filter);
+
+ error:
+
+ gst_element_set_locked_state (priv->liveadder, TRUE);
+ gst_element_set_locked_state (priv->audio_output, TRUE);
+
+ gst_element_set_state (priv->liveadder, GST_STATE_NULL);
+ gst_element_set_state (priv->audio_output, GST_STATE_NULL);
+
+ gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_output);
+
+ error_add_output:
+
+ gst_bin_remove (GST_BIN (priv->pipeline), priv->liveadder);
+
+ gst_element_set_locked_state (priv->liveadder, FALSE);
+ gst_element_set_locked_state (priv->audio_output, FALSE);
+
+ error_add_liveadder:
+
+ if (priv->liveadder != NULL)
+ {
+ gst_object_unref (priv->liveadder);
+ priv->liveadder = NULL;
+ }
+
+ return NULL;
}
static gboolean
"product=Telepathy&component=%s", cm);
result = g_strdup_printf (
- _("Something not expected happened in a Telepathy component. "
+ _("Something unexpected happened in a Telepathy component. "
"Please <a href=\"%s\">report this bug</a> and attach "
"logs gathered from the 'Debug' window in the Help menu."), url);
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);
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);
/* Called from the streaming thread */
-static void
+static gboolean
empathy_call_window_src_added_cb (EmpathyCallHandler *handler,
GstPad *src, guint media_type, gpointer user_data)
{
EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
EmpathyCallWindowPriv *priv = GET_PRIV (self);
+ gboolean retval = FALSE;
GstPad *pad;
g_assert_not_reached ();
}
- gst_pad_link (src, pad);
+ if (pad == NULL)
+ goto out;
+
+ if (GST_PAD_LINK_FAILED (gst_pad_link (src, pad)))
+ g_warning ("Could not link %s sink pad",
+ media_type == TP_MEDIA_STREAM_TYPE_AUDIO ? "audio" : "video");
+ else
+ retval = TRUE;
+
gst_object_unref (pad);
+ out:
+
+ /* If no sink could be linked, try to add fakesink to prevent the whole call
+ * aborting */
+
+ if (!retval)
+ {
+ GstElement *fakesink = gst_element_factory_make ("fakesink", NULL);
+
+ if (gst_bin_add (GST_BIN (priv->pipeline), fakesink))
+ {
+ GstPad *sinkpad = gst_element_get_static_pad (fakesink, "sink");
+ if (gst_element_set_state (fakesink, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE ||
+ GST_PAD_LINK_FAILED (gst_pad_link (src, sinkpad)))
+ {
+ gst_element_set_locked_state (fakesink, TRUE);
+ gst_element_set_state (fakesink, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN (priv->pipeline), fakesink);
+ }
+ else
+ {
+ g_debug ("Could not link real sink, linked fakesink instead");
+ }
+ gst_object_unref (sinkpad);
+ }
+ else
+ {
+ gst_object_unref (fakesink);
+ }
+ }
+
+
g_mutex_unlock (priv->lock);
+
+ return TRUE;
}
-/* Called from the streaming thread */
-static void
+static gboolean
empathy_call_window_sink_added_cb (EmpathyCallHandler *handler,
GstPad *sink, guint media_type, gpointer user_data)
{
EmpathyCallWindow *self = EMPATHY_CALL_WINDOW (user_data);
EmpathyCallWindowPriv *priv = GET_PRIV (self);
GstPad *pad;
+ gboolean retval = FALSE;
switch (media_type)
{
case TP_MEDIA_STREAM_TYPE_AUDIO:
- gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input);
+ if (!gst_bin_add (GST_BIN (priv->pipeline), priv->audio_input))
+ {
+ g_warning ("Could not add audio source to pipeline");
+ break;
+ }
pad = gst_element_get_static_pad (priv->audio_input, "src");
- gst_pad_link (pad, sink);
+ if (!pad)
+ {
+ gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
+ g_warning ("Could not get source pad from audio source");
+ break;
+ }
+
+ if (GST_PAD_LINK_FAILED (gst_pad_link (pad, sink)))
+ {
+ gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
+ g_warning ("Could not link audio source to farsight");
+ break;
+ }
- gst_element_set_state (priv->audio_input, GST_STATE_PLAYING);
+ if (gst_element_set_state (priv->audio_input, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
+ {
+ g_warning ("Could not start audio source");
+ gst_element_set_state (priv->audio_input, GST_STATE_NULL);
+ gst_bin_remove (GST_BIN (priv->pipeline), priv->audio_input);
+ break;
+ }
+
+ retval = TRUE;
break;
case TP_MEDIA_STREAM_TYPE_VIDEO:
if (priv->video_input != NULL)
if (priv->video_tee != NULL)
{
pad = gst_element_get_request_pad (priv->video_tee, "src%d");
- gst_pad_link (pad, sink);
+ if (GST_PAD_LINK_FAILED (gst_pad_link (pad, sink)))
+ {
+ g_warning ("Could not link videp soure input pipeline");
+ break;
+ }
+ gst_object_unref (pad);
}
+
+ retval = TRUE;
}
break;
default:
g_assert_not_reached ();
}
+ return retval;
}
static void
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));
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
+start_call (EmpathyCallWindow *self)
+{
+ EmpathyCallWindowPriv *priv = GET_PRIV (self);
+
+ priv->call_started = TRUE;
+ empathy_call_handler_start_call (priv->handler,
+ gtk_get_current_event_time ());
+
+ if (empathy_call_handler_has_initial_video (priv->handler))
+ {
+ /* Enable 'send video' buttons and display the preview */
+ gtk_toggle_tool_button_set_active (
+ GTK_TOGGLE_TOOL_BUTTON (priv->tool_button_camera_on), TRUE);
+ }
+}
static gboolean
empathy_call_window_bus_message (GstBus *bus, GstMessage *message,
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);
+ priv->pipeline_playing = TRUE;
+
+ if (priv->start_call_when_playing)
+ start_call (self);
}
}
break;
}
else
{
- empathy_call_window_disconnected (self);
+ empathy_call_window_disconnected (self, TRUE);
}
g_error_free (error);
g_free (debug);
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);
}
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);
}
EmpathyCallWindowPriv *priv = GET_PRIV (window);
gboolean active;
- if (priv->audio_input == NULL)
- return;
-
active = (gtk_toggle_tool_button_get_active (toggle));
if (active)
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);
-
- 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);
* 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);
- priv->call_started = TRUE;
- empathy_call_handler_start_call (priv->handler);
+ 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);
- gst_element_set_state (priv->pipeline, GST_STATE_PLAYING);
gtk_action_set_sensitive (priv->redial, FALSE);
gtk_widget_set_sensitive (priv->redial_button, FALSE);
g_signal_handlers_block_by_func (priv->tool_button_camera_on,
tool_button_camera_on_toggled_cb, self);
g_signal_handlers_block_by_func (priv->action_camera,
- tool_button_camera_on_toggled_cb, self);
+ action_camera_change_cb, self);
}
static void
g_signal_handlers_unblock_by_func (priv->tool_button_camera_on,
tool_button_camera_on_toggled_cb, self);
g_signal_handlers_unblock_by_func (priv->action_camera,
- tool_button_camera_on_toggled_cb, self);
+ action_camera_change_cb, self);
}