2
* @file media.c Account API
7
* Pidgin is the legal property of its developers, whose names are too numerous
8
* to list here. Please refer to the COPYRIGHT file distributed with this
11
* This program is free software; you can redistribute it and/or modify
12
* it under the terms of the GNU General Public License as published by
13
* the Free Software Foundation; either version 2 of the License, or
14
* (at your option) any later version.
16
* This program is distributed in the hope that it will be useful,
17
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
* GNU General Public License for more details.
21
* You should have received a copy of the GNU General Public License
22
* along with this program; if not, write to the Free Software
23
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
29
#include "connection.h"
31
#include "mediamanager.h"
37
#include "pidginstock.h"
40
#include "media-gst.h"
43
#include <gdk/gdkwin32.h>
46
#include <gst/interfaces/xoverlay.h>
48
#define PIDGIN_TYPE_MEDIA (pidgin_media_get_type())
49
#define PIDGIN_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PIDGIN_TYPE_MEDIA, PidginMedia))
50
#define PIDGIN_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PIDGIN_TYPE_MEDIA, PidginMediaClass))
51
#define PIDGIN_IS_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PIDGIN_TYPE_MEDIA))
52
#define PIDGIN_IS_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PIDGIN_TYPE_MEDIA))
53
#define PIDGIN_MEDIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PIDGIN_TYPE_MEDIA, PidginMediaClass))
55
typedef struct _PidginMedia PidginMedia;
56
typedef struct _PidginMediaClass PidginMediaClass;
57
typedef struct _PidginMediaPrivate PidginMediaPrivate;
61
/* Waiting for response */
62
PIDGIN_MEDIA_WAITING = 1,
64
PIDGIN_MEDIA_REQUESTED,
66
PIDGIN_MEDIA_ACCEPTED,
68
PIDGIN_MEDIA_REJECTED,
71
struct _PidginMediaClass
73
GtkWindowClass parent_class;
79
PidginMediaPrivate *priv;
82
struct _PidginMediaPrivate
86
gulong level_handler_id;
88
GtkItemFactory *item_factory;
95
GtkWidget *send_progress;
96
GtkWidget *recv_progress;
98
PidginMediaState state;
101
GtkWidget *send_widget;
102
GtkWidget *recv_widget;
103
GtkWidget *button_widget;
104
GtkWidget *local_video;
105
GtkWidget *remote_video;
108
PurpleMediaSessionType request_type;
111
#define PIDGIN_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PIDGIN_TYPE_MEDIA, PidginMediaPrivate))
113
static void pidgin_media_class_init (PidginMediaClass *klass);
114
static void pidgin_media_init (PidginMedia *media);
115
static void pidgin_media_dispose (GObject *object);
116
static void pidgin_media_finalize (GObject *object);
117
static void pidgin_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
118
static void pidgin_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
119
static void pidgin_media_set_state(PidginMedia *gtkmedia, PidginMediaState state);
121
static GtkWindowClass *parent_class = NULL;
128
static guint pidgin_media_signals[LAST_SIGNAL] = {0};
138
pidgin_media_get_type(void)
140
static GType type = 0;
143
static const GTypeInfo info = {
144
sizeof(PidginMediaClass),
147
(GClassInitFunc) pidgin_media_class_init,
152
(GInstanceInitFunc) pidgin_media_init,
155
type = g_type_register_static(GTK_TYPE_WINDOW, "PidginMedia", &info, 0);
162
pidgin_media_class_init (PidginMediaClass *klass)
164
GObjectClass *gobject_class = (GObjectClass*)klass;
165
/* GtkContainerClass *container_class = (GtkContainerClass*)klass; */
166
parent_class = g_type_class_peek_parent(klass);
168
gobject_class->dispose = pidgin_media_dispose;
169
gobject_class->finalize = pidgin_media_finalize;
170
gobject_class->set_property = pidgin_media_set_property;
171
gobject_class->get_property = pidgin_media_get_property;
173
g_object_class_install_property(gobject_class, PROP_MEDIA,
174
g_param_spec_object("media",
176
"The PurpleMedia associated with this media.",
178
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
179
g_object_class_install_property(gobject_class, PROP_SCREENNAME,
180
g_param_spec_string("screenname",
182
"The screenname of the user this session is with.",
184
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
186
g_type_class_add_private(klass, sizeof(PidginMediaPrivate));
190
pidgin_media_mute_toggled(GtkToggleButton *toggle, PidginMedia *media)
192
purple_media_stream_info(media->priv->media,
193
gtk_toggle_button_get_active(toggle) ?
194
PURPLE_MEDIA_INFO_MUTE : PURPLE_MEDIA_INFO_UNMUTE,
199
pidgin_media_pause_toggled(GtkToggleButton *toggle, PidginMedia *media)
201
purple_media_stream_info(media->priv->media,
202
gtk_toggle_button_get_active(toggle) ?
203
PURPLE_MEDIA_INFO_PAUSE : PURPLE_MEDIA_INFO_UNPAUSE,
208
pidgin_media_delete_event_cb(GtkWidget *widget,
209
GdkEvent *event, PidginMedia *media)
211
if (media->priv->media)
212
purple_media_stream_info(media->priv->media,
213
PURPLE_MEDIA_INFO_HANGUP, NULL, NULL, TRUE);
219
pidgin_x_error_handler(Display *display, XErrorEvent *event)
221
const gchar *error_type;
222
switch (event->error_code) {
223
#define XERRORCASE(type) case type: error_type = #type; break
224
XERRORCASE(BadAccess);
225
XERRORCASE(BadAlloc);
227
XERRORCASE(BadColor);
228
XERRORCASE(BadCursor);
229
XERRORCASE(BadDrawable);
232
XERRORCASE(BadIDChoice);
233
XERRORCASE(BadImplementation);
234
XERRORCASE(BadLength);
235
XERRORCASE(BadMatch);
237
XERRORCASE(BadPixmap);
238
XERRORCASE(BadRequest);
239
XERRORCASE(BadValue);
240
XERRORCASE(BadWindow);
243
error_type = "unknown";
246
purple_debug_error("media", "A %s Xlib error has occurred. "
247
"The program would normally crash now.\n",
254
menu_hangup(gpointer data, guint action, GtkWidget *item)
256
PidginMedia *gtkmedia = PIDGIN_MEDIA(data);
257
purple_media_stream_info(gtkmedia->priv->media,
258
PURPLE_MEDIA_INFO_HANGUP, NULL, NULL, TRUE);
261
static GtkItemFactoryEntry menu_items[] = {
262
{ N_("/_Media"), NULL, NULL, 0, "<Branch>", NULL },
263
{ N_("/Media/_Hangup"), NULL, menu_hangup, 0, "<Item>", NULL },
266
static gint menu_item_count = sizeof(menu_items) / sizeof(menu_items[0]);
269
item_factory_translate_func (const char *path, gpointer func_data)
275
setup_menubar(PidginMedia *window)
277
GtkAccelGroup *accel_group;
280
accel_group = gtk_accel_group_new ();
281
gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
282
g_object_unref(accel_group);
284
window->priv->item_factory = gtk_item_factory_new(GTK_TYPE_MENU_BAR,
285
"<main>", accel_group);
287
gtk_item_factory_set_translate_func(window->priv->item_factory,
288
(GtkTranslateFunc)item_factory_translate_func,
291
gtk_item_factory_create_items(window->priv->item_factory,
292
menu_item_count, menu_items, window);
293
g_signal_connect(G_OBJECT(accel_group), "accel-changed",
294
G_CALLBACK(pidgin_save_accels_cb), NULL);
296
menu = gtk_item_factory_get_widget(
297
window->priv->item_factory, "<main>");
299
gtk_widget_show(menu);
304
pidgin_media_init (PidginMedia *media)
307
media->priv = PIDGIN_MEDIA_GET_PRIVATE(media);
310
XSetErrorHandler(pidgin_x_error_handler);
313
vbox = gtk_vbox_new(FALSE, 0);
314
gtk_container_add(GTK_CONTAINER(media), vbox);
316
media->priv->statusbar = gtk_statusbar_new();
317
gtk_box_pack_end(GTK_BOX(vbox), media->priv->statusbar,
319
gtk_statusbar_push(GTK_STATUSBAR(media->priv->statusbar),
321
gtk_widget_show(media->priv->statusbar);
323
media->priv->menubar = setup_menubar(media);
324
gtk_box_pack_start(GTK_BOX(vbox), media->priv->menubar,
327
media->priv->display = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
328
gtk_container_set_border_width(GTK_CONTAINER(media->priv->display),
329
PIDGIN_HIG_BOX_SPACE);
330
gtk_box_pack_start(GTK_BOX(vbox), media->priv->display,
331
TRUE, TRUE, PIDGIN_HIG_BOX_SPACE);
332
gtk_widget_show(vbox);
334
g_signal_connect(G_OBJECT(media), "delete-event",
335
G_CALLBACK(pidgin_media_delete_event_cb), media);
339
level_message_cb(PurpleMedia *media, gchar *session_id, gchar *participant,
340
double level, PidginMedia *gtkmedia)
343
if (participant == NULL)
344
progress = gtkmedia->priv->send_progress;
346
progress = gtkmedia->priv->recv_progress;
347
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), level);
352
pidgin_media_disconnect_levels(PurpleMedia *media, PidginMedia *gtkmedia)
354
PurpleMediaManager *manager = purple_media_get_manager(media);
355
GstElement *element = purple_media_manager_get_pipeline(manager);
356
gulong handler_id = g_signal_handler_find(G_OBJECT(gst_pipeline_get_bus(GST_PIPELINE(element))),
357
G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0,
358
NULL, G_CALLBACK(level_message_cb), gtkmedia);
360
g_signal_handler_disconnect(G_OBJECT(gst_pipeline_get_bus(GST_PIPELINE(element))),
365
pidgin_media_dispose(GObject *media)
367
PidginMedia *gtkmedia = PIDGIN_MEDIA(media);
368
purple_debug_info("gtkmedia", "pidgin_media_dispose\n");
370
if (gtkmedia->priv->media) {
371
purple_request_close_with_handle(gtkmedia);
372
purple_media_remove_output_windows(gtkmedia->priv->media);
373
pidgin_media_disconnect_levels(gtkmedia->priv->media, gtkmedia);
374
g_object_unref(gtkmedia->priv->media);
375
gtkmedia->priv->media = NULL;
378
if (gtkmedia->priv->item_factory) {
379
g_object_unref(gtkmedia->priv->item_factory);
380
gtkmedia->priv->item_factory = NULL;
383
G_OBJECT_CLASS(parent_class)->dispose(media);
387
pidgin_media_finalize(GObject *media)
389
/* PidginMedia *gtkmedia = PIDGIN_MEDIA(media); */
390
purple_debug_info("gtkmedia", "pidgin_media_finalize\n");
392
G_OBJECT_CLASS(parent_class)->finalize(media);
396
pidgin_media_emit_message(PidginMedia *gtkmedia, const char *msg)
398
PurpleConversation *conv = purple_find_conversation_with_account(
399
PURPLE_CONV_TYPE_ANY, gtkmedia->priv->screenname,
400
purple_media_get_account(gtkmedia->priv->media));
402
purple_conversation_write(conv, NULL, msg,
403
PURPLE_MESSAGE_SYSTEM, time(NULL));
408
PidginMedia *gtkmedia;
411
} PidginMediaRealizeData;
414
realize_cb_cb(PidginMediaRealizeData *data)
416
PidginMediaPrivate *priv = data->gtkmedia->priv;
420
if (data->participant == NULL)
421
window_id = GDK_WINDOW_HWND(priv->local_video->window);
423
window_id = GDK_WINDOW_HWND(priv->remote_video->window);
424
#elif defined(HAVE_X11)
425
if (data->participant == NULL)
426
window_id = GDK_WINDOW_XWINDOW(priv->local_video->window);
428
window_id = GDK_WINDOW_XWINDOW(priv->remote_video->window);
430
# error "Unsupported windowing system"
433
purple_media_set_output_window(priv->media, data->session_id,
434
data->participant, window_id);
436
g_free(data->session_id);
437
g_free(data->participant);
443
realize_cb(GtkWidget *widget, PidginMediaRealizeData *data)
445
g_timeout_add(0, (GSourceFunc)realize_cb_cb, data);
449
pidgin_media_error_cb(PidginMedia *media, const char *error, PidginMedia *gtkmedia)
451
PurpleConversation *conv = purple_find_conversation_with_account(
452
PURPLE_CONV_TYPE_ANY, gtkmedia->priv->screenname,
453
purple_media_get_account(gtkmedia->priv->media));
455
purple_conversation_write(conv, NULL, error,
456
PURPLE_MESSAGE_ERROR, time(NULL));
457
gtk_statusbar_push(GTK_STATUSBAR(gtkmedia->priv->statusbar),
462
pidgin_media_accept_cb(PurpleMedia *media, int index)
464
purple_media_stream_info(media, PURPLE_MEDIA_INFO_ACCEPT,
469
pidgin_media_reject_cb(PurpleMedia *media, int index)
471
purple_media_stream_info(media, PURPLE_MEDIA_INFO_REJECT,
476
pidgin_request_timeout_cb(PidginMedia *gtkmedia)
478
PurpleAccount *account;
481
PurpleMediaSessionType type;
482
gchar *message = NULL;
484
account = purple_media_get_account(gtkmedia->priv->media);
485
buddy = purple_find_buddy(account, gtkmedia->priv->screenname);
486
alias = buddy ? purple_buddy_get_contact_alias(buddy) :
487
gtkmedia->priv->screenname;
488
type = gtkmedia->priv->request_type;
489
gtkmedia->priv->timeout_id = 0;
491
if (type & PURPLE_MEDIA_AUDIO && type & PURPLE_MEDIA_VIDEO) {
492
message = g_strdup_printf(_("%s wishes to start an audio/video session with you."),
494
} else if (type & PURPLE_MEDIA_AUDIO) {
495
message = g_strdup_printf(_("%s wishes to start an audio session with you."),
497
} else if (type & PURPLE_MEDIA_VIDEO) {
498
message = g_strdup_printf(_("%s wishes to start a video session with you."),
502
gtkmedia->priv->request_type = PURPLE_MEDIA_NONE;
503
if (!purple_media_accepted(gtkmedia->priv->media, NULL, NULL)) {
504
purple_request_accept_cancel(gtkmedia, _("Incoming Call"),
505
message, NULL, PURPLE_DEFAULT_ACTION_NONE,
506
(void*)account, gtkmedia->priv->screenname,
507
NULL, gtkmedia->priv->media,
508
pidgin_media_accept_cb,
509
pidgin_media_reject_cb);
511
pidgin_media_emit_message(gtkmedia, message);
517
#if GTK_CHECK_VERSION(2,12,0)
518
pidgin_media_input_volume_changed(GtkScaleButton *range, double value,
521
double val = (double)value * 100.0;
523
pidgin_media_input_volume_changed(GtkRange *range, PurpleMedia *media)
525
double val = (double)gtk_range_get_value(GTK_RANGE(range));
527
purple_media_set_input_volume(media, NULL, val);
531
#if GTK_CHECK_VERSION(2,12,0)
532
pidgin_media_output_volume_changed(GtkScaleButton *range, double value,
535
double val = (double)value * 100.0;
537
pidgin_media_output_volume_changed(GtkRange *range, PurpleMedia *media)
539
double val = (double)gtk_range_get_value(GTK_RANGE(range));
541
purple_media_set_output_volume(media, NULL, NULL, val);
545
pidgin_media_add_audio_widget(PidginMedia *gtkmedia,
546
PurpleMediaSessionType type)
548
GtkWidget *volume_widget, *progress_parent, *volume, *progress;
551
if (type & PURPLE_MEDIA_SEND_AUDIO) {
552
value = purple_prefs_get_int(
553
"/purple/media/audio/volume/input");
554
} else if (type & PURPLE_MEDIA_RECV_AUDIO) {
555
value = purple_prefs_get_int(
556
"/purple/media/audio/volume/output");
558
g_return_val_if_reached(NULL);
560
#if GTK_CHECK_VERSION(2,12,0)
561
/* Setup widget structure */
562
volume_widget = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
563
progress_parent = gtk_vbox_new(FALSE, 0);
564
gtk_box_pack_start(GTK_BOX(volume_widget),
565
progress_parent, TRUE, TRUE, 0);
568
volume = gtk_volume_button_new();
569
gtk_scale_button_set_value(GTK_SCALE_BUTTON(volume), value/100.0);
570
gtk_box_pack_end(GTK_BOX(volume_widget),
571
volume, FALSE, FALSE, 0);
573
/* Setup widget structure */
574
volume_widget = gtk_vbox_new(FALSE, 0);
575
progress_parent = volume_widget;
578
volume = gtk_hscale_new_with_range(0.0, 100.0, 5.0);
579
gtk_range_set_increments(GTK_RANGE(volume), 5.0, 25.0);
580
gtk_range_set_value(GTK_RANGE(volume), value);
581
gtk_scale_set_draw_value(GTK_SCALE(volume), FALSE);
582
gtk_box_pack_end(GTK_BOX(volume_widget),
583
volume, TRUE, FALSE, 0);
586
/* Volume level indicator */
587
progress = gtk_progress_bar_new();
588
gtk_widget_set_size_request(progress, 250, 10);
589
gtk_box_pack_end(GTK_BOX(progress_parent), progress, TRUE, FALSE, 0);
591
if (type & PURPLE_MEDIA_SEND_AUDIO) {
592
g_signal_connect (G_OBJECT(volume), "value-changed",
593
G_CALLBACK(pidgin_media_input_volume_changed),
594
gtkmedia->priv->media);
595
gtkmedia->priv->send_progress = progress;
596
} else if (type & PURPLE_MEDIA_RECV_AUDIO) {
597
g_signal_connect (G_OBJECT(volume), "value-changed",
598
G_CALLBACK(pidgin_media_output_volume_changed),
599
gtkmedia->priv->media);
600
gtkmedia->priv->recv_progress = progress;
603
gtk_widget_show_all(volume_widget);
605
return volume_widget;
609
pidgin_media_ready_cb(PurpleMedia *media, PidginMedia *gtkmedia, const gchar *sid)
611
GtkWidget *send_widget = NULL, *recv_widget = NULL, *button_widget = NULL;
612
PurpleMediaSessionType type =
613
purple_media_get_session_type(media, sid);
614
GdkPixbuf *icon = NULL;
616
if (gtkmedia->priv->recv_widget == NULL
617
&& type & (PURPLE_MEDIA_RECV_VIDEO |
618
PURPLE_MEDIA_RECV_AUDIO)) {
619
recv_widget = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
620
gtk_box_pack_start(GTK_BOX(gtkmedia->priv->display),
621
recv_widget, TRUE, TRUE, 0);
622
gtk_widget_show(recv_widget);
624
recv_widget = gtkmedia->priv->recv_widget;
625
if (gtkmedia->priv->send_widget == NULL
626
&& type & (PURPLE_MEDIA_SEND_VIDEO |
627
PURPLE_MEDIA_SEND_AUDIO)) {
628
send_widget = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
629
gtk_box_pack_start(GTK_BOX(gtkmedia->priv->display),
630
send_widget, TRUE, TRUE, 0);
631
button_widget = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE);
632
gtk_box_pack_end(GTK_BOX(send_widget), button_widget,
634
gtk_widget_show(GTK_WIDGET(button_widget));
635
gtk_widget_show(send_widget);
637
send_widget = gtkmedia->priv->send_widget;
638
button_widget = gtkmedia->priv->button_widget;
641
if (type & PURPLE_MEDIA_RECV_VIDEO) {
642
PidginMediaRealizeData *data;
644
GtkWidget *remote_video;
645
GdkColor color = {0, 0, 0, 0};
647
aspect = gtk_aspect_frame_new(NULL, 0.5, 0.5, 4.0/3.0, FALSE);
648
gtk_frame_set_shadow_type(GTK_FRAME(aspect), GTK_SHADOW_IN);
649
gtk_box_pack_start(GTK_BOX(recv_widget), aspect, TRUE, TRUE, 0);
651
data = g_new0(PidginMediaRealizeData, 1);
652
data->gtkmedia = gtkmedia;
653
data->session_id = g_strdup(sid);
654
data->participant = g_strdup(gtkmedia->priv->screenname);
656
remote_video = gtk_drawing_area_new();
657
gtk_widget_modify_bg(remote_video, GTK_STATE_NORMAL, &color);
658
g_signal_connect(G_OBJECT(remote_video), "realize",
659
G_CALLBACK(realize_cb), data);
660
gtk_container_add(GTK_CONTAINER(aspect), remote_video);
661
gtk_widget_set_size_request (GTK_WIDGET(remote_video), 320, 240);
662
gtk_widget_show(remote_video);
663
gtk_widget_show(aspect);
665
gtkmedia->priv->remote_video = remote_video;
667
if (type & PURPLE_MEDIA_SEND_VIDEO) {
668
PidginMediaRealizeData *data;
670
GtkWidget *local_video;
671
GdkColor color = {0, 0, 0, 0};
673
aspect = gtk_aspect_frame_new(NULL, 0.5, 0.5, 4.0/3.0, FALSE);
674
gtk_frame_set_shadow_type(GTK_FRAME(aspect), GTK_SHADOW_IN);
675
gtk_box_pack_start(GTK_BOX(send_widget), aspect, TRUE, TRUE, 0);
677
data = g_new0(PidginMediaRealizeData, 1);
678
data->gtkmedia = gtkmedia;
679
data->session_id = g_strdup(sid);
680
data->participant = NULL;
682
local_video = gtk_drawing_area_new();
683
gtk_widget_modify_bg(local_video, GTK_STATE_NORMAL, &color);
684
g_signal_connect(G_OBJECT(local_video), "realize",
685
G_CALLBACK(realize_cb), data);
686
gtk_container_add(GTK_CONTAINER(aspect), local_video);
687
gtk_widget_set_size_request (GTK_WIDGET(local_video), 160, 120);
689
gtk_widget_show(local_video);
690
gtk_widget_show(aspect);
692
gtkmedia->priv->pause =
693
gtk_toggle_button_new_with_mnemonic(_("_Pause"));
694
g_signal_connect(gtkmedia->priv->pause, "toggled",
695
G_CALLBACK(pidgin_media_pause_toggled),
697
gtk_box_pack_end(GTK_BOX(button_widget), gtkmedia->priv->pause,
699
gtk_widget_show(gtkmedia->priv->pause);
701
gtkmedia->priv->local_video = local_video;
704
if (type & PURPLE_MEDIA_RECV_AUDIO) {
705
gtk_box_pack_end(GTK_BOX(recv_widget),
706
pidgin_media_add_audio_widget(gtkmedia,
707
PURPLE_MEDIA_RECV_AUDIO), FALSE, FALSE, 0);
709
if (type & PURPLE_MEDIA_SEND_AUDIO) {
710
gtkmedia->priv->mute =
711
gtk_toggle_button_new_with_mnemonic("_Mute");
712
g_signal_connect(gtkmedia->priv->mute, "toggled",
713
G_CALLBACK(pidgin_media_mute_toggled),
715
gtk_box_pack_end(GTK_BOX(button_widget), gtkmedia->priv->mute,
717
gtk_widget_show(gtkmedia->priv->mute);
719
gtk_box_pack_end(GTK_BOX(send_widget),
720
pidgin_media_add_audio_widget(gtkmedia,
721
PURPLE_MEDIA_SEND_AUDIO), FALSE, FALSE, 0);
725
if (type & PURPLE_MEDIA_AUDIO &&
726
gtkmedia->priv->level_handler_id == 0) {
727
gtkmedia->priv->level_handler_id = g_signal_connect(
728
media, "level", G_CALLBACK(level_message_cb),
732
if (send_widget != NULL)
733
gtkmedia->priv->send_widget = send_widget;
734
if (recv_widget != NULL)
735
gtkmedia->priv->recv_widget = recv_widget;
736
if (button_widget != NULL)
737
gtkmedia->priv->button_widget = button_widget;
739
if (purple_media_is_initiator(media, sid, NULL) == FALSE) {
740
if (gtkmedia->priv->timeout_id != 0)
741
g_source_remove(gtkmedia->priv->timeout_id);
742
gtkmedia->priv->request_type |= type;
743
gtkmedia->priv->timeout_id = g_timeout_add(500,
744
(GSourceFunc)pidgin_request_timeout_cb,
748
/* set the window icon according to the type */
749
if (type & PURPLE_MEDIA_VIDEO) {
750
icon = gtk_widget_render_icon(GTK_WIDGET(gtkmedia),
751
PIDGIN_STOCK_TOOLBAR_VIDEO_CALL,
752
gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_LARGE), NULL);
753
} else if (type & PURPLE_MEDIA_AUDIO) {
754
icon = gtk_widget_render_icon(GTK_WIDGET(gtkmedia),
755
PIDGIN_STOCK_TOOLBAR_AUDIO_CALL,
756
gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_LARGE), NULL);
760
gtk_window_set_icon(GTK_WINDOW(gtkmedia), icon);
761
g_object_unref(icon);
764
gtk_widget_show(gtkmedia->priv->display);
768
pidgin_media_state_changed_cb(PurpleMedia *media, PurpleMediaState state,
769
gchar *sid, gchar *name, PidginMedia *gtkmedia)
771
purple_debug_info("gtkmedia", "state: %d sid: %s name: %s\n",
772
state, sid ? sid : "(null)", name ? name : "(null)");
773
if (sid == NULL && name == NULL) {
774
if (state == PURPLE_MEDIA_STATE_END) {
775
pidgin_media_emit_message(gtkmedia,
776
_("The call has been terminated."));
777
gtk_widget_destroy(GTK_WIDGET(gtkmedia));
779
} else if (state == PURPLE_MEDIA_STATE_NEW &&
780
sid != NULL && name != NULL) {
781
pidgin_media_ready_cb(media, gtkmedia, sid);
786
pidgin_media_stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type,
787
gchar *sid, gchar *name, gboolean local,
788
PidginMedia *gtkmedia)
790
if (type == PURPLE_MEDIA_INFO_REJECT) {
791
pidgin_media_emit_message(gtkmedia,
792
_("You have rejected the call."));
793
} else if (type == PURPLE_MEDIA_INFO_ACCEPT) {
795
purple_request_close_with_handle(gtkmedia);
796
pidgin_media_set_state(gtkmedia, PIDGIN_MEDIA_ACCEPTED);
797
pidgin_media_emit_message(gtkmedia, _("Call in progress."));
798
gtk_statusbar_push(GTK_STATUSBAR(gtkmedia->priv->statusbar),
799
0, _("Call in progress."));
800
gtk_widget_show(GTK_WIDGET(gtkmedia));
805
pidgin_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
808
g_return_if_fail(PIDGIN_IS_MEDIA(object));
810
media = PIDGIN_MEDIA(object);
814
if (media->priv->media)
815
g_object_unref(media->priv->media);
816
media->priv->media = g_value_get_object(value);
817
g_object_ref(media->priv->media);
819
if (purple_media_is_initiator(media->priv->media,
821
pidgin_media_set_state(media, PIDGIN_MEDIA_WAITING);
823
pidgin_media_set_state(media, PIDGIN_MEDIA_REQUESTED);
825
g_signal_connect(G_OBJECT(media->priv->media), "error",
826
G_CALLBACK(pidgin_media_error_cb), media);
827
g_signal_connect(G_OBJECT(media->priv->media), "state-changed",
828
G_CALLBACK(pidgin_media_state_changed_cb), media);
829
g_signal_connect(G_OBJECT(media->priv->media), "stream-info",
830
G_CALLBACK(pidgin_media_stream_info_cb), media);
833
case PROP_SCREENNAME:
834
if (media->priv->screenname)
835
g_free(media->priv->screenname);
836
media->priv->screenname = g_value_dup_string(value);
839
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
845
pidgin_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
848
g_return_if_fail(PIDGIN_IS_MEDIA(object));
850
media = PIDGIN_MEDIA(object);
854
g_value_set_object(value, media->priv->media);
856
case PROP_SCREENNAME:
857
g_value_set_string(value, media->priv->screenname);
860
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
866
pidgin_media_new(PurpleMedia *media, const gchar *screenname)
868
PidginMedia *gtkmedia = g_object_new(pidgin_media_get_type(),
870
"screenname", screenname, NULL);
871
return GTK_WIDGET(gtkmedia);
875
pidgin_media_set_state(PidginMedia *gtkmedia, PidginMediaState state)
877
gtkmedia->priv->state = state;
881
pidgin_media_new_cb(PurpleMediaManager *manager, PurpleMedia *media,
882
PurpleAccount *account, gchar *screenname, gpointer nul)
884
PidginMedia *gtkmedia = PIDGIN_MEDIA(
885
pidgin_media_new(media, screenname));
886
PurpleBuddy *buddy = purple_find_buddy(account, screenname);
887
const gchar *alias = buddy ?
888
purple_buddy_get_contact_alias(buddy) : screenname;
889
gtk_window_set_title(GTK_WINDOW(gtkmedia), alias);
891
if (purple_media_is_initiator(media, NULL, NULL) == TRUE)
892
gtk_widget_show(GTK_WIDGET(gtkmedia));
898
create_default_video_src(PurpleMedia *media,
899
const gchar *session_id, const gchar *participant)
901
GstElement *sendbin, *src, *videoscale, *capsfilter;
907
/* autovideosrc doesn't pick ksvideosrc for some reason */
908
src = gst_element_factory_make("ksvideosrc", NULL);
910
src = gst_element_factory_make("dshowvideosrc", NULL);
912
src = gst_element_factory_make("autovideosrc", NULL);
914
src = gst_element_factory_make("gconfvideosrc", NULL);
916
src = gst_element_factory_make("autovideosrc", NULL);
918
src = gst_element_factory_make("v4l2src", NULL);
920
src = gst_element_factory_make("v4lsrc", NULL);
923
purple_debug_error("gtkmedia", "Unable to find a suitable "
924
"element for the default video source.\n");
928
sendbin = gst_bin_new("pidgindefaultvideosrc");
929
videoscale = gst_element_factory_make("videoscale", NULL);
930
capsfilter = gst_element_factory_make("capsfilter", NULL);
932
/* It was recommended to set the size <= 352x288 and framerate <= 20 */
933
caps = gst_caps_from_string("video/x-raw-yuv , width=[250,352] , "
934
"height=[200,288] , framerate=[1/1,20/1]");
935
g_object_set(G_OBJECT(capsfilter), "caps", caps, NULL);
937
gst_bin_add_many(GST_BIN(sendbin), src,
938
videoscale, capsfilter, NULL);
939
gst_element_link_many(src, videoscale, capsfilter, NULL);
941
pad = gst_element_get_static_pad(capsfilter, "src");
942
ghost = gst_ghost_pad_new("ghostsrc", pad);
943
gst_object_unref(pad);
944
gst_element_add_pad(sendbin, ghost);
950
create_default_video_sink(PurpleMedia *media,
951
const gchar *session_id, const gchar *participant)
953
GstElement *sink = gst_element_factory_make("gconfvideosink", NULL);
955
sink = gst_element_factory_make("autovideosink", NULL);
957
purple_debug_error("gtkmedia", "Unable to find a suitable "
958
"element for the default video sink.\n");
963
create_default_audio_src(PurpleMedia *media,
964
const gchar *session_id, const gchar *participant)
967
src = gst_element_factory_make("gconfaudiosrc", NULL);
969
src = gst_element_factory_make("autoaudiosrc", NULL);
971
src = gst_element_factory_make("alsasrc", NULL);
973
src = gst_element_factory_make("osssrc", NULL);
975
src = gst_element_factory_make("dshowaudiosrc", NULL);
977
purple_debug_error("gtkmedia", "Unable to find a suitable "
978
"element for the default audio source.\n");
981
gst_element_set_name(src, "pidgindefaultaudiosrc");
986
create_default_audio_sink(PurpleMedia *media,
987
const gchar *session_id, const gchar *participant)
990
sink = gst_element_factory_make("gconfaudiosink", NULL);
992
sink = gst_element_factory_make("autoaudiosink",NULL);
994
purple_debug_error("gtkmedia", "Unable to find a suitable "
995
"element for the default audio sink.\n");
1003
pidgin_medias_init(void)
1006
PurpleMediaManager *manager = purple_media_manager_get();
1007
PurpleMediaElementInfo *default_video_src =
1008
g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO,
1009
"id", "pidgindefaultvideosrc",
1010
"name", "Pidgin Default Video Source",
1011
"type", PURPLE_MEDIA_ELEMENT_VIDEO
1012
| PURPLE_MEDIA_ELEMENT_SRC
1013
| PURPLE_MEDIA_ELEMENT_ONE_SRC
1014
| PURPLE_MEDIA_ELEMENT_UNIQUE,
1015
"create-cb", create_default_video_src, NULL);
1016
PurpleMediaElementInfo *default_video_sink =
1017
g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO,
1018
"id", "pidgindefaultvideosink",
1019
"name", "Pidgin Default Video Sink",
1020
"type", PURPLE_MEDIA_ELEMENT_VIDEO
1021
| PURPLE_MEDIA_ELEMENT_SINK
1022
| PURPLE_MEDIA_ELEMENT_ONE_SINK,
1023
"create-cb", create_default_video_sink, NULL);
1024
PurpleMediaElementInfo *default_audio_src =
1025
g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO,
1026
"id", "pidgindefaultaudiosrc",
1027
"name", "Pidgin Default Audio Source",
1028
"type", PURPLE_MEDIA_ELEMENT_AUDIO
1029
| PURPLE_MEDIA_ELEMENT_SRC
1030
| PURPLE_MEDIA_ELEMENT_ONE_SRC
1031
| PURPLE_MEDIA_ELEMENT_UNIQUE,
1032
"create-cb", create_default_audio_src, NULL);
1033
PurpleMediaElementInfo *default_audio_sink =
1034
g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO,
1035
"id", "pidgindefaultaudiosink",
1036
"name", "Pidgin Default Audio Sink",
1037
"type", PURPLE_MEDIA_ELEMENT_AUDIO
1038
| PURPLE_MEDIA_ELEMENT_SINK
1039
| PURPLE_MEDIA_ELEMENT_ONE_SINK,
1040
"create-cb", create_default_audio_sink, NULL);
1042
g_signal_connect(G_OBJECT(manager), "init-media",
1043
G_CALLBACK(pidgin_media_new_cb), NULL);
1045
purple_media_manager_set_ui_caps(manager,
1046
PURPLE_MEDIA_CAPS_AUDIO |
1047
PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION |
1048
PURPLE_MEDIA_CAPS_VIDEO |
1049
PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION |
1050
PURPLE_MEDIA_CAPS_AUDIO_VIDEO);
1052
purple_debug_info("gtkmedia", "Registering media element types\n");
1053
purple_media_manager_set_active_element(manager, default_video_src);
1054
purple_media_manager_set_active_element(manager, default_video_sink);
1055
purple_media_manager_set_active_element(manager, default_audio_src);
1056
purple_media_manager_set_active_element(manager, default_audio_sink);