Mercurial > pidgin.yaz
view pidgin/gtkmedia.c @ 26437:29bc149cbd07
Send candidates more like GMail.
author | Mike Ruprecht <maiku@soc.pidgin.im> |
---|---|
date | Mon, 23 Mar 2009 07:37:40 +0000 |
parents | 0dfd05966e09 |
children | c3ea7e628e58 |
line wrap: on
line source
/** * @file media.c Account API * @ingroup core * * Pidgin * * Pidgin is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * source distribution. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <string.h> #include "debug.h" #include "internal.h" #include "connection.h" #include "media.h" #include "media-gst.h" #include "mediamanager.h" #include "pidgin.h" #include "request.h" #include "gtkmedia.h" #include "gtkutils.h" #ifdef USE_VV #include <gst/interfaces/xoverlay.h> typedef enum { /* Waiting for response */ PIDGIN_MEDIA_WAITING = 1, /* Got request */ PIDGIN_MEDIA_REQUESTED, /* Accepted call */ PIDGIN_MEDIA_ACCEPTED, /* Rejected call */ PIDGIN_MEDIA_REJECTED, } PidginMediaState; struct _PidginMediaPrivate { PurpleMedia *media; gchar *screenname; GstElement *send_level; GstElement *recv_level; GtkItemFactory *item_factory; GtkWidget *menubar; GtkWidget *statusbar; GtkWidget *mute; GtkWidget *send_progress; GtkWidget *recv_progress; PidginMediaState state; GtkWidget *display; GtkWidget *send_widget; GtkWidget *recv_widget; GtkWidget *local_video; GtkWidget *remote_video; PurpleConnection *pc; }; #define PIDGIN_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PIDGIN_TYPE_MEDIA, PidginMediaPrivate)) static void pidgin_media_class_init (PidginMediaClass *klass); static void pidgin_media_init (PidginMedia *media); static void pidgin_media_dispose (GObject *object); static void pidgin_media_finalize (GObject *object); static void pidgin_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void pidgin_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void pidgin_media_set_state(PidginMedia *gtkmedia, PidginMediaState state); static GtkWindowClass *parent_class = NULL; #if 0 enum { LAST_SIGNAL }; static guint pidgin_media_signals[LAST_SIGNAL] = {0}; #endif enum { PROP_0, PROP_MEDIA, PROP_SCREENNAME, PROP_SEND_LEVEL, PROP_RECV_LEVEL }; GType pidgin_media_get_type(void) { static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof(PidginMediaClass), NULL, NULL, (GClassInitFunc) pidgin_media_class_init, NULL, NULL, sizeof(PidginMedia), 0, (GInstanceInitFunc) pidgin_media_init, NULL }; type = g_type_register_static(GTK_TYPE_WINDOW, "PidginMedia", &info, 0); } return type; } static void pidgin_media_class_init (PidginMediaClass *klass) { GObjectClass *gobject_class = (GObjectClass*)klass; /* GtkContainerClass *container_class = (GtkContainerClass*)klass; */ parent_class = g_type_class_peek_parent(klass); gobject_class->dispose = pidgin_media_dispose; gobject_class->finalize = pidgin_media_finalize; gobject_class->set_property = pidgin_media_set_property; gobject_class->get_property = pidgin_media_get_property; g_object_class_install_property(gobject_class, PROP_MEDIA, g_param_spec_object("media", "PurpleMedia", "The PurpleMedia associated with this media.", PURPLE_TYPE_MEDIA, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_SCREENNAME, g_param_spec_string("screenname", "Screenname", "The screenname of the user this session is with.", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_SEND_LEVEL, g_param_spec_object("send-level", "Send level", "The GstElement of this media's send 'level'", GST_TYPE_ELEMENT, G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_RECV_LEVEL, g_param_spec_object("recv-level", "Receive level", "The GstElement of this media's recv 'level'", GST_TYPE_ELEMENT, G_PARAM_READWRITE)); g_type_class_add_private(klass, sizeof(PidginMediaPrivate)); } static void pidgin_media_mute_toggled(GtkToggleButton *toggle, PidginMedia *media) { purple_media_mute(media->priv->media, gtk_toggle_button_get_active(toggle)); } static gboolean pidgin_media_delete_event_cb(GtkWidget *widget, GdkEvent *event, PidginMedia *media) { if (media->priv->media) purple_media_hangup(media->priv->media); return FALSE; } static int pidgin_x_error_handler(Display *display, XErrorEvent *event) { const gchar *error_type; switch (event->error_code) { #define XERRORCASE(type) case type: error_type = #type; break XERRORCASE(BadAccess); XERRORCASE(BadAlloc); XERRORCASE(BadAtom); XERRORCASE(BadColor); XERRORCASE(BadCursor); XERRORCASE(BadDrawable); XERRORCASE(BadFont); XERRORCASE(BadGC); XERRORCASE(BadIDChoice); XERRORCASE(BadImplementation); XERRORCASE(BadLength); XERRORCASE(BadMatch); XERRORCASE(BadName); XERRORCASE(BadPixmap); XERRORCASE(BadRequest); XERRORCASE(BadValue); XERRORCASE(BadWindow); #undef XERRORCASE default: error_type = "unknown"; break; } purple_debug_error("media", "A %s Xlib error has occurred. " "The program would normally crash now.\n", error_type); return 0; } static void menu_hangup(gpointer data, guint action, GtkWidget *item) { PidginMedia *gtkmedia = PIDGIN_MEDIA(data); purple_media_hangup(gtkmedia->priv->media); } static GtkItemFactoryEntry menu_items[] = { { N_("/_Media"), NULL, NULL, 0, "<Branch>", NULL }, { N_("/Media/_Hangup"), NULL, menu_hangup, 0, "<Item>", NULL }, }; static gint menu_item_count = sizeof(menu_items) / sizeof(menu_items[0]); static const char * item_factory_translate_func (const char *path, gpointer func_data) { return _(path); } static GtkWidget * setup_menubar(PidginMedia *window) { GtkAccelGroup *accel_group; GtkWidget *menu; accel_group = gtk_accel_group_new (); gtk_window_add_accel_group(GTK_WINDOW(window), accel_group); g_object_unref(accel_group); window->priv->item_factory = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<main>", accel_group); gtk_item_factory_set_translate_func(window->priv->item_factory, (GtkTranslateFunc)item_factory_translate_func, NULL, NULL); gtk_item_factory_create_items(window->priv->item_factory, menu_item_count, menu_items, window); g_signal_connect(G_OBJECT(accel_group), "accel-changed", G_CALLBACK(pidgin_save_accels_cb), NULL); menu = gtk_item_factory_get_widget( window->priv->item_factory, "<main>"); gtk_widget_show(menu); return menu; } static void pidgin_media_init (PidginMedia *media) { GtkWidget *vbox, *hbox; media->priv = PIDGIN_MEDIA_GET_PRIVATE(media); XSetErrorHandler(pidgin_x_error_handler); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(media), vbox); media->priv->statusbar = gtk_statusbar_new(); gtk_box_pack_end(GTK_BOX(vbox), media->priv->statusbar, FALSE, FALSE, 0); gtk_statusbar_push(GTK_STATUSBAR(media->priv->statusbar), 0, _("Calling...")); gtk_widget_show(media->priv->statusbar); media->priv->menubar = setup_menubar(media); gtk_box_pack_start(GTK_BOX(vbox), media->priv->menubar, FALSE, TRUE, 0); hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); gtk_widget_show(GTK_WIDGET(hbox)); media->priv->mute = gtk_toggle_button_new_with_mnemonic("_Mute"); g_signal_connect(media->priv->mute, "toggled", G_CALLBACK(pidgin_media_mute_toggled), media); gtk_box_pack_end(GTK_BOX(hbox), media->priv->mute, FALSE, FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(media->priv->mute), PIDGIN_HIG_BOX_SPACE); media->priv->display = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_container_set_border_width(GTK_CONTAINER(media->priv->display), PIDGIN_HIG_BOX_SPACE); gtk_box_pack_start(GTK_BOX(vbox), media->priv->display, TRUE, TRUE, PIDGIN_HIG_BOX_SPACE); gtk_widget_show(vbox); g_signal_connect(G_OBJECT(media), "delete-event", G_CALLBACK(pidgin_media_delete_event_cb), media); } static gboolean level_message_cb(GstBus *bus, GstMessage *message, PidginMedia *gtkmedia) { gdouble rms_db; gdouble percent; const GValue *list; const GValue *value; GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(message)); GtkWidget *progress; if (message->type != GST_MESSAGE_ELEMENT) return TRUE; if (!gst_structure_has_name( gst_message_get_structure(message), "level")) return TRUE; if (src == gtkmedia->priv->send_level) progress = gtkmedia->priv->send_progress; else if (src == gtkmedia->priv->recv_level) progress = gtkmedia->priv->recv_progress; else return TRUE; list = gst_structure_get_value( gst_message_get_structure(message), "rms"); /* Only bother with the first channel. */ value = gst_value_list_get_value(list, 0); rms_db = g_value_get_double(value); percent = pow(10, rms_db / 20) * 5; if(percent > 1.0) percent = 1.0; gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), percent); return TRUE; } static void pidgin_media_disconnect_levels(PurpleMedia *media, PidginMedia *gtkmedia) { GstElement *element = purple_media_get_pipeline(media); gulong handler_id = g_signal_handler_find(G_OBJECT(gst_pipeline_get_bus(GST_PIPELINE(element))), G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, G_CALLBACK(level_message_cb), gtkmedia); if (handler_id) g_signal_handler_disconnect(G_OBJECT(gst_pipeline_get_bus(GST_PIPELINE(element))), handler_id); } static void pidgin_media_dispose(GObject *media) { PidginMedia *gtkmedia = PIDGIN_MEDIA(media); purple_debug_info("gtkmedia", "pidgin_media_dispose\n"); if (gtkmedia->priv->media) { purple_request_close_with_handle(gtkmedia); purple_media_remove_output_windows(gtkmedia->priv->media); pidgin_media_disconnect_levels(gtkmedia->priv->media, gtkmedia); g_object_unref(gtkmedia->priv->media); gtkmedia->priv->media = NULL; } if (gtkmedia->priv->item_factory) { g_object_unref(gtkmedia->priv->item_factory); gtkmedia->priv->item_factory = NULL; } if (gtkmedia->priv->send_level) { gst_object_unref(gtkmedia->priv->send_level); gtkmedia->priv->send_level = NULL; } if (gtkmedia->priv->recv_level) { gst_object_unref(gtkmedia->priv->recv_level); gtkmedia->priv->recv_level = NULL; } G_OBJECT_CLASS(parent_class)->dispose(media); } static void pidgin_media_finalize(GObject *media) { /* PidginMedia *gtkmedia = PIDGIN_MEDIA(media); */ purple_debug_info("gtkmedia", "pidgin_media_finalize\n"); G_OBJECT_CLASS(parent_class)->finalize(media); } static void pidgin_media_emit_message(PidginMedia *gtkmedia, const char *msg) { PurpleConversation *conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_ANY, gtkmedia->priv->screenname, purple_connection_get_account(gtkmedia->priv->pc)); if (conv != NULL) purple_conversation_write(conv, NULL, msg, PURPLE_MESSAGE_SYSTEM, time(NULL)); } typedef struct { PidginMedia *gtkmedia; gchar *session_id; gchar *participant; } PidginMediaRealizeData; static gboolean realize_cb_cb(PidginMediaRealizeData *data) { PidginMediaPrivate *priv = data->gtkmedia->priv; gulong window_id; if (data->participant == NULL) window_id = GDK_WINDOW_XWINDOW(priv->local_video->window); else window_id = GDK_WINDOW_XWINDOW(priv->remote_video->window); purple_media_set_output_window(priv->media, data->session_id, data->participant, window_id); g_free(data->session_id); g_free(data->participant); g_free(data); return FALSE; } static void realize_cb(GtkWidget *widget, PidginMediaRealizeData *data) { g_timeout_add(0, (GSourceFunc)realize_cb_cb, data); } static void pidgin_media_error_cb(PidginMedia *media, const char *error, PidginMedia *gtkmedia) { PurpleConversation *conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_ANY, gtkmedia->priv->screenname, purple_connection_get_account(gtkmedia->priv->pc)); if (conv != NULL) purple_conversation_write(conv, NULL, error, PURPLE_MESSAGE_ERROR, time(NULL)); gtk_statusbar_push(GTK_STATUSBAR(gtkmedia->priv->statusbar), 0, error); } static void pidgin_media_accepted_cb(PurpleMedia *media, const gchar *session_id, const gchar *participant, PidginMedia *gtkmedia) { pidgin_media_set_state(gtkmedia, PIDGIN_MEDIA_ACCEPTED); pidgin_media_emit_message(gtkmedia, _("Call in progress.")); gtk_statusbar_push(GTK_STATUSBAR(gtkmedia->priv->statusbar), 0, _("Call in progress.")); gtk_widget_show(GTK_WIDGET(gtkmedia)); } static gboolean plug_delete_event_cb(GtkWidget *widget, gpointer data) { return TRUE; } static gboolean plug_removed_cb(GtkWidget *widget, gpointer data) { return TRUE; } static void socket_realize_cb(GtkWidget *widget, gpointer data) { gtk_socket_add_id(GTK_SOCKET(widget), gtk_plug_get_id(GTK_PLUG(data))); } static void pidgin_media_ready_cb(PurpleMedia *media, PidginMedia *gtkmedia, const gchar *sid) { GstElement *pipeline = purple_media_get_pipeline(media); GtkWidget *send_widget = NULL, *recv_widget = NULL; gboolean is_initiator; PurpleMediaSessionType type = purple_media_get_session_type(media, sid); if (gtkmedia->priv->recv_widget == NULL && type & (PURPLE_MEDIA_RECV_VIDEO | PURPLE_MEDIA_RECV_AUDIO)) { recv_widget = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_box_pack_start(GTK_BOX(gtkmedia->priv->display), recv_widget, TRUE, TRUE, 0); gtk_widget_show(recv_widget); } else recv_widget = gtkmedia->priv->recv_widget; if (gtkmedia->priv->send_widget == NULL && type & (PURPLE_MEDIA_SEND_VIDEO | PURPLE_MEDIA_SEND_AUDIO)) { send_widget = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_box_pack_start(GTK_BOX(gtkmedia->priv->display), send_widget, TRUE, TRUE, 0); gtk_widget_show(send_widget); } else send_widget = gtkmedia->priv->send_widget; if (type & PURPLE_MEDIA_RECV_VIDEO) { PidginMediaRealizeData *data; GtkWidget *aspect; GtkWidget *remote_video; GtkWidget *plug; GtkWidget *socket; GdkColor color = {0, 0, 0, 0}; aspect = gtk_aspect_frame_new(NULL, 0.5, 0.5, 4.0/3.0, FALSE); gtk_frame_set_shadow_type(GTK_FRAME(aspect), GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(recv_widget), aspect, TRUE, TRUE, 0); plug = gtk_plug_new(0); g_signal_connect(G_OBJECT(plug), "delete-event", G_CALLBACK(plug_delete_event_cb), plug); gtk_widget_show(plug); socket = gtk_socket_new(); g_signal_connect(G_OBJECT(socket), "realize", G_CALLBACK(socket_realize_cb), plug); g_signal_connect(G_OBJECT(socket), "plug-removed", G_CALLBACK(plug_removed_cb), NULL); gtk_container_add(GTK_CONTAINER(aspect), socket); gtk_widget_show(socket); data = g_new0(PidginMediaRealizeData, 1); data->gtkmedia = gtkmedia; data->session_id = g_strdup(sid); data->participant = g_strdup(gtkmedia->priv->screenname); remote_video = gtk_drawing_area_new(); gtk_widget_modify_bg(remote_video, GTK_STATE_NORMAL, &color); g_signal_connect(G_OBJECT(remote_video), "realize", G_CALLBACK(realize_cb), data); gtk_container_add(GTK_CONTAINER(plug), remote_video); gtk_widget_set_size_request (GTK_WIDGET(remote_video), 320, 240); gtk_widget_show(remote_video); gtk_widget_show(aspect); gtkmedia->priv->remote_video = remote_video; } if (type & PURPLE_MEDIA_SEND_VIDEO) { PidginMediaRealizeData *data; GtkWidget *aspect; GtkWidget *local_video; GtkWidget *plug; GtkWidget *socket; GdkColor color = {0, 0, 0, 0}; aspect = gtk_aspect_frame_new(NULL, 0.5, 0.5, 4.0/3.0, FALSE); gtk_frame_set_shadow_type(GTK_FRAME(aspect), GTK_SHADOW_IN); gtk_box_pack_start(GTK_BOX(send_widget), aspect, TRUE, TRUE, 0); plug = gtk_plug_new(0); g_signal_connect(G_OBJECT(plug), "delete-event", G_CALLBACK(plug_delete_event_cb), plug); gtk_widget_show(plug); socket = gtk_socket_new(); g_signal_connect(G_OBJECT(socket), "realize", G_CALLBACK(socket_realize_cb), plug); g_signal_connect(G_OBJECT(socket), "plug-removed", G_CALLBACK(plug_removed_cb), NULL); gtk_container_add(GTK_CONTAINER(aspect), socket); gtk_widget_show(socket); data = g_new0(PidginMediaRealizeData, 1); data->gtkmedia = gtkmedia; data->session_id = g_strdup(sid); data->participant = NULL; local_video = gtk_drawing_area_new(); gtk_widget_modify_bg(local_video, GTK_STATE_NORMAL, &color); g_signal_connect(G_OBJECT(local_video), "realize", G_CALLBACK(realize_cb), data); gtk_container_add(GTK_CONTAINER(plug), local_video); gtk_widget_set_size_request (GTK_WIDGET(local_video), 160, 120); gtk_widget_show(local_video); gtk_widget_show(aspect); gtkmedia->priv->local_video = local_video; } if (type & PURPLE_MEDIA_RECV_AUDIO) { gtkmedia->priv->recv_progress = gtk_progress_bar_new(); gtk_widget_set_size_request(gtkmedia->priv->recv_progress, 320, 10); gtk_box_pack_end(GTK_BOX(recv_widget), gtkmedia->priv->recv_progress, FALSE, FALSE, 0); gtk_widget_show(gtkmedia->priv->recv_progress); } if (type & PURPLE_MEDIA_SEND_AUDIO) { GstElement *media_src = purple_media_get_src(media, sid); gtkmedia->priv->send_level = gst_bin_get_by_name( GST_BIN(media_src), "sendlevel"); gtkmedia->priv->send_progress = gtk_progress_bar_new(); gtk_widget_set_size_request(gtkmedia->priv->send_progress, 320, 10); gtk_box_pack_end(GTK_BOX(send_widget), gtkmedia->priv->send_progress, FALSE, FALSE, 0); gtk_widget_show(gtkmedia->priv->send_progress); gtk_widget_show(gtkmedia->priv->mute); } if (type & PURPLE_MEDIA_AUDIO) { GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)); g_signal_connect(G_OBJECT(bus), "message::element", G_CALLBACK(level_message_cb), gtkmedia); gst_object_unref(bus); } if (send_widget != NULL) gtkmedia->priv->send_widget = send_widget; if (recv_widget != NULL) gtkmedia->priv->recv_widget = recv_widget; g_object_get(G_OBJECT(media), "initiator", &is_initiator, NULL); if (is_initiator == FALSE) { gchar *message; if (type & PURPLE_MEDIA_AUDIO && type & PURPLE_MEDIA_VIDEO) { message = g_strdup_printf(_("%s wishes to start an audio/video session with you."), gtkmedia->priv->screenname); } else if (type & PURPLE_MEDIA_AUDIO) { message = g_strdup_printf(_("%s wishes to start an audio session with you."), gtkmedia->priv->screenname); } else if (type & PURPLE_MEDIA_VIDEO) { message = g_strdup_printf(_("%s wishes to start a video session with you."), gtkmedia->priv->screenname); } pidgin_media_emit_message(gtkmedia, message); g_free(message); } gtk_widget_show(gtkmedia->priv->display); } static void pidgin_media_state_changed_cb(PurpleMedia *media, PurpleMediaState state, gchar *sid, gchar *name, PidginMedia *gtkmedia) { purple_debug_info("gtkmedia", "state: %d sid: %s name: %s\n", state, sid, name); if (sid == NULL && name == NULL) { if (state == PURPLE_MEDIA_STATE_END) { pidgin_media_emit_message(gtkmedia, _("The call has been terminated.")); gtk_widget_destroy(GTK_WIDGET(gtkmedia)); } } else if (state == PURPLE_MEDIA_STATE_NEW && sid != NULL && name != NULL) { pidgin_media_ready_cb(media, gtkmedia, sid); } else if (state == PURPLE_MEDIA_STATE_CONNECTED && purple_media_get_session_type(media, sid) & PURPLE_MEDIA_RECV_AUDIO) { GstElement *tee = purple_media_get_tee(media, sid, name); GstIterator *iter = gst_element_iterate_src_pads(tee); GstPad *sinkpad; if (gst_iterator_next(iter, (gpointer)&sinkpad) == GST_ITERATOR_OK) { GstPad *peer = gst_pad_get_peer(sinkpad); if (peer != NULL) { gtkmedia->priv->recv_level = gst_bin_get_by_name( GST_BIN(GST_OBJECT_PARENT( peer)), "recvlevel"); gst_object_unref(peer); } gst_object_unref(sinkpad); } gst_iterator_free(iter); } } static void pidgin_media_stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type, gchar *sid, gchar *name, PidginMedia *gtkmedia) { if (type == PURPLE_MEDIA_INFO_REJECT) { pidgin_media_emit_message(gtkmedia, _("You have rejected the call.")); } } static void pidgin_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { PidginMedia *media; g_return_if_fail(PIDGIN_IS_MEDIA(object)); media = PIDGIN_MEDIA(object); switch (prop_id) { case PROP_MEDIA: { gboolean initiator; if (media->priv->media) g_object_unref(media->priv->media); media->priv->media = g_value_get_object(value); g_object_ref(media->priv->media); g_object_get(G_OBJECT(media->priv->media), "initiator", &initiator, NULL); if (initiator == TRUE) pidgin_media_set_state(media, PIDGIN_MEDIA_WAITING); else pidgin_media_set_state(media, PIDGIN_MEDIA_REQUESTED); g_signal_connect(G_OBJECT(media->priv->media), "error", G_CALLBACK(pidgin_media_error_cb), media); g_signal_connect(G_OBJECT(media->priv->media), "accepted", G_CALLBACK(pidgin_media_accepted_cb), media); g_signal_connect(G_OBJECT(media->priv->media), "state-changed", G_CALLBACK(pidgin_media_state_changed_cb), media); g_signal_connect(G_OBJECT(media->priv->media), "stream-info", G_CALLBACK(pidgin_media_stream_info_cb), media); break; } case PROP_SCREENNAME: if (media->priv->screenname) g_free(media->priv->screenname); media->priv->screenname = g_value_dup_string(value); break; case PROP_SEND_LEVEL: if (media->priv->send_level) gst_object_unref(media->priv->send_level); media->priv->send_level = g_value_get_object(value); g_object_ref(media->priv->send_level); break; case PROP_RECV_LEVEL: if (media->priv->recv_level) gst_object_unref(media->priv->recv_level); media->priv->recv_level = g_value_get_object(value); g_object_ref(media->priv->recv_level); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void pidgin_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { PidginMedia *media; g_return_if_fail(PIDGIN_IS_MEDIA(object)); media = PIDGIN_MEDIA(object); switch (prop_id) { case PROP_MEDIA: g_value_set_object(value, media->priv->media); break; case PROP_SCREENNAME: g_value_set_string(value, media->priv->screenname); break; case PROP_SEND_LEVEL: g_value_set_object(value, media->priv->send_level); break; case PROP_RECV_LEVEL: g_value_set_object(value, media->priv->recv_level); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } GtkWidget * pidgin_media_new(PurpleMedia *media, const gchar *screenname) { PidginMedia *gtkmedia = g_object_new(pidgin_media_get_type(), "media", media, "screenname", screenname, NULL); return GTK_WIDGET(gtkmedia); } static void pidgin_media_set_state(PidginMedia *gtkmedia, PidginMediaState state) { gtkmedia->priv->state = state; } static gboolean pidgin_media_new_cb(PurpleMediaManager *manager, PurpleMedia *media, PurpleConnection *pc, gchar *screenname, gpointer nul) { PidginMedia *gtkmedia = PIDGIN_MEDIA( pidgin_media_new(media, screenname)); gboolean initiator; PurpleBuddy *buddy = purple_find_buddy( purple_connection_get_account(pc), screenname); const gchar *alias = buddy ? purple_buddy_get_contact_alias(buddy) : screenname; gtkmedia->priv->pc = pc; gtk_window_set_title(GTK_WINDOW(gtkmedia), alias); g_object_get(G_OBJECT(media), "initiator", &initiator, NULL); if (initiator == FALSE) { gchar *message = g_strdup_printf("%s wishes to start a " "media session with you\n", alias); purple_request_accept_cancel(gtkmedia, "Media invitation", message, NULL, PURPLE_DEFAULT_ACTION_NONE, (void*)pc, screenname, NULL, media, purple_media_accept, purple_media_reject); g_free(message); } else gtk_widget_show(GTK_WIDGET(gtkmedia)); return TRUE; } static GstElement * create_default_video_src(void) { GstElement *sendbin, *src, *videoscale, *capsfilter; GstPad *pad; GstPad *ghost; GstCaps *caps; const gchar *video_plugin = purple_prefs_get_string( "/purple/media/video/plugin"); const gchar *video_device = purple_prefs_get_string( "/purple/media/video/device"); sendbin = gst_bin_new("purplesendvideobin"); src = gst_element_factory_make(video_plugin, "purplevideosource"); videoscale = gst_element_factory_make("videoscale", NULL); capsfilter = gst_element_factory_make("capsfilter", NULL); /* It was recommended to set the size < 352x288 and framerate < 20 */ caps = gst_caps_from_string("video/x-raw-yuv , width=[250,350] , " "height=[200,275] , framerate=[10/1,20/1]"); g_object_set(G_OBJECT(capsfilter), "caps", caps, NULL); gst_bin_add_many(GST_BIN(sendbin), src, videoscale, capsfilter, NULL); gst_element_link_many(src, videoscale, capsfilter, NULL); if (!strcmp(video_plugin, "videotestsrc")) { /* Set is-live to true to throttle videotestsrc */ g_object_set (G_OBJECT(src), "is-live", TRUE, NULL); } pad = gst_element_get_static_pad(capsfilter, "src"); ghost = gst_ghost_pad_new("ghostsrc", pad); gst_object_unref(pad); gst_element_add_pad(sendbin, ghost); if (video_device != NULL && strcmp(video_device, "")) g_object_set(G_OBJECT(src), "device", video_device, NULL); return sendbin; } static GstElement * create_default_video_sink(void) { return gst_element_factory_make("autovideosink", NULL); } static GstElement * create_default_audio_src(void) { GstElement *bin, *src, *volume, *level; GstPad *pad, *ghost; const gchar *audio_device = purple_prefs_get_string( "/purple/media/audio/device"); double input_volume = purple_prefs_get_int( "/purple/media/audio/volume/input")/10.0; bin = gst_bin_new("purplesendaudiobin"); src = gst_element_factory_make("alsasrc", "asrc"); volume = gst_element_factory_make("volume", "purpleaudioinputvolume"); g_object_set(volume, "volume", input_volume, NULL); level = gst_element_factory_make("level", "sendlevel"); gst_bin_add_many(GST_BIN(bin), src, volume, level, NULL); gst_element_link(src, volume); gst_element_link(volume, level); pad = gst_element_get_pad(level, "src"); ghost = gst_ghost_pad_new("ghostsrc", pad); gst_element_add_pad(bin, ghost); g_object_set(G_OBJECT(level), "message", TRUE, NULL); if (audio_device != NULL && strcmp(audio_device, "")) g_object_set(G_OBJECT(src), "device", audio_device, NULL); return bin; } static GstElement * create_default_audio_sink(void) { GstElement *bin, *sink, *volume, *level, *queue; GstPad *pad, *ghost; double output_volume = purple_prefs_get_int( "/purple/media/audio/volume/output")/10.0; bin = gst_bin_new("pidginrecvaudiobin"); sink = gst_element_factory_make("alsasink", "asink"); g_object_set(G_OBJECT(sink), "async", FALSE, "sync", FALSE, NULL); volume = gst_element_factory_make("volume", "purpleaudiooutputvolume"); g_object_set(volume, "volume", output_volume, NULL); level = gst_element_factory_make("level", "recvlevel"); queue = gst_element_factory_make("queue", NULL); gst_bin_add_many(GST_BIN(bin), sink, volume, level, queue, NULL); gst_element_link(level, sink); gst_element_link(volume, level); gst_element_link(queue, volume); pad = gst_element_get_pad(queue, "sink"); ghost = gst_ghost_pad_new("ghostsink", pad); gst_element_add_pad(bin, ghost); g_object_set(G_OBJECT(level), "message", TRUE, NULL); return bin; } static PurpleMediaElementInfo default_video_src = { "pidgindefaultvideosrc", /* id */ PURPLE_MEDIA_ELEMENT_VIDEO /* type */ | PURPLE_MEDIA_ELEMENT_SRC | PURPLE_MEDIA_ELEMENT_ONE_SRC | PURPLE_MEDIA_ELEMENT_UNIQUE, create_default_video_src, /* create */ }; static PurpleMediaElementInfo default_video_sink = { "pidgindefaultvideosink", /* id */ PURPLE_MEDIA_ELEMENT_VIDEO /* type */ | PURPLE_MEDIA_ELEMENT_SINK | PURPLE_MEDIA_ELEMENT_ONE_SINK, create_default_video_sink, /* create */ }; static PurpleMediaElementInfo default_audio_src = { "pidgindefaultaudiosrc", /* id */ PURPLE_MEDIA_ELEMENT_AUDIO /* type */ | PURPLE_MEDIA_ELEMENT_SRC | PURPLE_MEDIA_ELEMENT_ONE_SRC | PURPLE_MEDIA_ELEMENT_UNIQUE, create_default_audio_src, /* create */ }; static PurpleMediaElementInfo default_audio_sink = { "pidgindefaultaudiosink", /* id */ PURPLE_MEDIA_ELEMENT_AUDIO /* type */ | PURPLE_MEDIA_ELEMENT_SINK | PURPLE_MEDIA_ELEMENT_ONE_SINK, create_default_audio_sink, /* create */ }; void pidgin_medias_init(void) { PurpleMediaManager *manager = purple_media_manager_get(); g_signal_connect(G_OBJECT(manager), "init-media", G_CALLBACK(pidgin_media_new_cb), NULL); purple_debug_info("gtkmedia", "Registering media element types\n"); purple_media_manager_set_active_element(manager, &default_video_src); purple_media_manager_set_active_element(manager, &default_video_sink); purple_media_manager_set_active_element(manager, &default_audio_src); purple_media_manager_set_active_element(manager, &default_audio_sink); } #endif /* USE_VV */