pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Start filling out the ChangeLog for 2.14.6
release-2.x.y
2021-06-21, Gary Kramlich
a8e48ef3fc45
Start filling out the ChangeLog for 2.14.6
Testing Done:
None, it's a text file.
Reviewed at https://reviews.imfreedom.org/r/769/
/*
* Configures microphones and webcams for voice and video
* Copyright (C) 2009 Mike Ruprecht <cmaiku@gmail.com>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
*/
#include
"internal.h"
#include
"debug.h"
#include
"mediamanager.h"
#include
"media-gst.h"
#include
"version.h"
#include
"gtkplugin.h"
#include
"gtkutils.h"
#include
"gtkprefs.h"
#if GST_CHECK_VERSION(1,0,0)
#include
<gst/video/videooverlay.h>
#else
#include
<gst/interfaces/propertyprobe.h>
#endif
/* container window for showing a stand-alone configurator */
static
GtkWidget
*
window
=
NULL
;
static
PurpleMediaElementInfo
*
old_video_src
=
NULL
,
*
old_video_sink
=
NULL
,
*
old_audio_src
=
NULL
,
*
old_audio_sink
=
NULL
;
static
const
gchar
*
AUDIO_SRC_PLUGINS
[]
=
{
"alsasrc"
,
N_
(
"ALSA"
),
/* "esdmon", "ESD", ? */
"osssrc"
,
N_
(
"OSS"
),
"pulsesrc"
,
N_
(
"PulseAudio"
),
"sndiosrc"
,
N_
(
"sndio"
),
/* "audiotestsrc wave=silence", "Silence", */
"audiotestsrc"
,
N_
(
"Test Sound"
),
NULL
};
static
const
gchar
*
AUDIO_SINK_PLUGINS
[]
=
{
"alsasink"
,
N_
(
"ALSA"
),
"artsdsink"
,
N_
(
"aRts"
),
"esdsink"
,
N_
(
"ESD"
),
"osssink"
,
N_
(
"OSS"
),
"pulsesink"
,
N_
(
"PulseAudio"
),
"sndiosink"
,
N_
(
"sndio"
),
NULL
};
static
const
gchar
*
VIDEO_SRC_PLUGINS
[]
=
{
"videotestsrc"
,
N_
(
"Test Input"
),
"dshowvideosrc"
,
N_
(
"DirectDraw"
),
"ksvideosrc"
,
N_
(
"KS Video"
),
"qcamsrc"
,
N_
(
"Quickcam"
),
"v4lsrc"
,
N_
(
"Video4Linux"
),
"v4l2src"
,
N_
(
"Video4Linux2"
),
"v4lmjpegsrc"
,
N_
(
"Video4Linux MJPEG"
),
NULL
};
static
const
gchar
*
VIDEO_SINK_PLUGINS
[]
=
{
/* "aasink", "AALib", Didn't work for me */
"directdrawsink"
,
N_
(
"DirectDraw"
),
"glimagesink"
,
N_
(
"OpenGL"
),
"ximagesink"
,
N_
(
"X Window System"
),
"xvimagesink"
,
N_
(
"X Window System (Xv)"
),
NULL
};
static
GList
*
get_element_devices
(
const
gchar
*
element_name
)
{
GList
*
ret
=
NULL
;
GstElement
*
element
;
GObjectClass
*
klass
;
#if !GST_CHECK_VERSION(1,0,0)
GstPropertyProbe
*
probe
;
const
GParamSpec
*
pspec
;
#endif
ret
=
g_list_prepend
(
ret
,
(
gpointer
)
_
(
"Default"
));
ret
=
g_list_prepend
(
ret
,
""
);
if
(
purple_strequal
(
element_name
,
"<custom>"
)
||
(
*
element_name
==
'\0'
))
{
return
g_list_reverse
(
ret
);
}
element
=
gst_element_factory_make
(
element_name
,
"test"
);
if
(
!
element
)
{
purple_debug_info
(
"vvconfig"
,
"'%s' - unable to find element
\n
"
,
element_name
);
return
g_list_reverse
(
ret
);
}
klass
=
G_OBJECT_GET_CLASS
(
element
);
if
(
!
klass
)
{
purple_debug_info
(
"vvconfig"
,
"'%s' - unable to find G_Object Class
\n
"
,
element_name
);
return
g_list_reverse
(
ret
);
}
#if GST_CHECK_VERSION(1,0,0)
purple_debug_info
(
"vvconfig"
,
"'%s' - gstreamer-1.0 doesn't support "
"property probing
\n
"
,
element_name
);
#else
if
(
!
g_object_class_find_property
(
klass
,
"device"
)
||
!
GST_IS_PROPERTY_PROBE
(
element
)
||
!
(
probe
=
GST_PROPERTY_PROBE
(
element
))
||
!
(
pspec
=
gst_property_probe_get_property
(
probe
,
"device"
)))
{
purple_debug_info
(
"vvconfig"
,
"'%s' - no device
\n
"
,
element_name
);
}
else
{
gsize
n
;
GValueArray
*
array
;
/* Set autoprobe[-fps] to FALSE to avoid delays when probing. */
if
(
g_object_class_find_property
(
klass
,
"autoprobe"
))
{
g_object_set
(
G_OBJECT
(
element
),
"autoprobe"
,
FALSE
,
NULL
);
if
(
g_object_class_find_property
(
klass
,
"autoprobe-fps"
))
{
g_object_set
(
G_OBJECT
(
element
),
"autoprobe-fps"
,
FALSE
,
NULL
);
}
}
array
=
gst_property_probe_probe_and_get_values
(
probe
,
pspec
);
if
(
array
==
NULL
)
{
purple_debug_info
(
"vvconfig"
,
"'%s' has no devices
\n
"
,
element_name
);
return
g_list_reverse
(
ret
);
}
for
(
n
=
0
;
n
<
array
->
n_values
;
++
n
)
{
GValue
*
device
;
const
gchar
*
name
;
const
gchar
*
device_name
;
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
/* GValueArray is in gstreamer-0.10 API */
device
=
g_value_array_get_nth
(
array
,
n
);
G_GNUC_END_IGNORE_DEPRECATIONS
g_object_set_property
(
G_OBJECT
(
element
),
"device"
,
device
);
if
(
gst_element_set_state
(
element
,
GST_STATE_READY
)
!=
GST_STATE_CHANGE_SUCCESS
)
{
purple_debug_warning
(
"vvconfig"
,
"Error changing state of %s
\n
"
,
element_name
);
continue
;
}
g_object_get
(
G_OBJECT
(
element
),
"device-name"
,
&
name
,
NULL
);
device_name
=
g_value_get_string
(
device
);
if
(
name
==
NULL
)
name
=
_
(
"Unknown"
);
purple_debug_info
(
"vvconfig"
,
"Found device %s : %s for %s
\n
"
,
device_name
,
name
,
element_name
);
ret
=
g_list_prepend
(
ret
,
(
gpointer
)
name
);
ret
=
g_list_prepend
(
ret
,
(
gpointer
)
device_name
);
gst_element_set_state
(
element
,
GST_STATE_NULL
);
}
}
#endif
gst_object_unref
(
element
);
return
g_list_reverse
(
ret
);
}
static
GList
*
get_element_plugins
(
const
gchar
**
plugins
)
{
GList
*
ret
=
NULL
;
ret
=
g_list_prepend
(
ret
,
"Default"
);
ret
=
g_list_prepend
(
ret
,
""
);
for
(;
plugins
[
0
]
&&
plugins
[
1
];
plugins
+=
2
)
{
#if GST_CHECK_VERSION(1,0,0)
if
(
gst_registry_check_feature_version
(
gst_registry_get
(),
plugins
[
0
],
0
,
0
,
0
))
{
#else
if
(
gst_default_registry_check_feature_version
(
plugins
[
0
],
0
,
0
,
0
))
{
#endif
ret
=
g_list_prepend
(
ret
,
(
gpointer
)
plugins
[
1
]);
ret
=
g_list_prepend
(
ret
,
(
gpointer
)
plugins
[
0
]);
}
}
ret
=
g_list_reverse
(
ret
);
return
ret
;
}
static
void
device_changed_cb
(
const
gchar
*
name
,
PurplePrefType
type
,
gconstpointer
value
,
gpointer
data
)
{
GtkSizeGroup
*
sg
=
data
;
GtkWidget
*
parent
,
*
widget
;
GSList
*
widgets
;
GList
*
devices
;
GValue
gvalue
;
gint
position
;
gchar
*
label
,
*
pref
;
widgets
=
gtk_size_group_get_widgets
(
GTK_SIZE_GROUP
(
sg
));
for
(;
widgets
;
widgets
=
g_slist_next
(
widgets
))
{
const
gchar
*
widget_name
=
gtk_widget_get_name
(
GTK_WIDGET
(
widgets
->
data
));
if
(
purple_strequal
(
widget_name
,
name
))
{
gchar
*
temp_str
;
gchar
delimiters
[
3
]
=
{
0
,
0
,
0
};
const
gchar
*
text
;
gint
keyval
,
pos
;
widget
=
widgets
->
data
;
/* Get label with _ from the GtkLabel */
text
=
gtk_label_get_text
(
GTK_LABEL
(
widget
));
keyval
=
gtk_label_get_mnemonic_keyval
(
GTK_LABEL
(
widget
));
delimiters
[
0
]
=
g_ascii_tolower
(
keyval
);
delimiters
[
1
]
=
g_ascii_toupper
(
keyval
);
pos
=
strcspn
(
text
,
delimiters
);
if
(
pos
!=
-1
)
{
temp_str
=
g_strndup
(
text
,
pos
);
label
=
g_strconcat
(
temp_str
,
"_"
,
text
+
pos
,
NULL
);
g_free
(
temp_str
);
}
else
{
label
=
g_strdup
(
text
);
}
break
;
}
}
if
(
widgets
==
NULL
)
return
;
parent
=
gtk_widget_get_parent
(
widget
);
widget
=
parent
;
parent
=
gtk_widget_get_parent
(
GTK_WIDGET
(
widget
));
gvalue
.
g_type
=
0
;
g_value_init
(
&
gvalue
,
G_TYPE_INT
);
gtk_container_child_get_property
(
GTK_CONTAINER
(
parent
),
GTK_WIDGET
(
widget
),
"position"
,
&
gvalue
);
position
=
g_value_get_int
(
&
gvalue
);
g_value_unset
(
&
gvalue
);
gtk_widget_destroy
(
widget
);
pref
=
g_strdup
(
name
);
strcpy
(
pref
+
strlen
(
pref
)
-
strlen
(
"plugin"
),
"device"
);
devices
=
get_element_devices
(
value
);
if
(
g_list_find_custom
(
devices
,
purple_prefs_get_string
(
pref
),
(
GCompareFunc
)
strcmp
)
==
NULL
)
{
GList
*
next
=
g_list_next
(
devices
);
if
(
next
!=
NULL
)
{
purple_prefs_set_string
(
pref
,
next
->
data
);
}
}
widget
=
pidgin_prefs_dropdown_from_list
(
parent
,
label
,
PURPLE_PREF_STRING
,
pref
,
devices
);
g_list_free
(
devices
);
g_signal_connect_swapped
(
widget
,
"destroy"
,
G_CALLBACK
(
g_free
),
pref
);
g_free
(
label
);
gtk_misc_set_alignment
(
GTK_MISC
(
widget
),
0
,
0.5
);
gtk_widget_set_name
(
widget
,
name
);
gtk_size_group_add_widget
(
sg
,
widget
);
gtk_box_reorder_child
(
GTK_BOX
(
parent
),
gtk_widget_get_parent
(
GTK_WIDGET
(
widget
)),
position
);
}
static
void
get_plugin_frame
(
GtkWidget
*
parent
,
GtkSizeGroup
*
sg
,
const
gchar
*
name
,
const
gchar
*
plugin_label
,
const
gchar
**
plugin_strs
,
const
gchar
*
plugin_pref
,
const
gchar
*
device_label
,
const
gchar
*
device_pref
)
{
GtkWidget
*
vbox
,
*
widget
;
GList
*
plugins
,
*
devices
;
vbox
=
pidgin_make_frame
(
parent
,
name
);
/* Setup plugin preference */
plugins
=
get_element_plugins
(
plugin_strs
);
widget
=
pidgin_prefs_dropdown_from_list
(
vbox
,
plugin_label
,
PURPLE_PREF_STRING
,
plugin_pref
,
plugins
);
g_list_free
(
plugins
);
gtk_size_group_add_widget
(
sg
,
widget
);
gtk_misc_set_alignment
(
GTK_MISC
(
widget
),
0
,
0.5
);
/* Setup device preference */
devices
=
get_element_devices
(
purple_prefs_get_string
(
plugin_pref
));
if
(
g_list_find_custom
(
devices
,
purple_prefs_get_string
(
device_pref
),
(
GCompareFunc
)
strcmp
)
==
NULL
)
{
GList
*
next
=
g_list_next
(
devices
);
if
(
next
!=
NULL
)
{
purple_prefs_set_string
(
device_pref
,
next
->
data
);
}
}
widget
=
pidgin_prefs_dropdown_from_list
(
vbox
,
device_label
,
PURPLE_PREF_STRING
,
device_pref
,
devices
);
g_list_free
(
devices
);
gtk_widget_set_name
(
widget
,
plugin_pref
);
gtk_size_group_add_widget
(
sg
,
widget
);
gtk_misc_set_alignment
(
GTK_MISC
(
widget
),
0
,
0.5
);
purple_prefs_connect_callback
(
vbox
,
plugin_pref
,
device_changed_cb
,
sg
);
g_signal_connect_swapped
(
vbox
,
"destroy"
,
G_CALLBACK
(
purple_prefs_disconnect_by_handle
),
vbox
);
}
static
GtkWidget
*
get_plugin_config_frame
(
PurplePlugin
*
plugin
)
{
GtkWidget
*
notebook
,
*
vbox_audio
,
*
vbox_video
;
GtkSizeGroup
*
sg
;
notebook
=
gtk_notebook_new
();
gtk_container_set_border_width
(
GTK_CONTAINER
(
notebook
),
PIDGIN_HIG_BORDER
);
gtk_widget_show
(
notebook
);
vbox_audio
=
gtk_vbox_new
(
FALSE
,
PIDGIN_HIG_CAT_SPACE
);
vbox_video
=
gtk_vbox_new
(
FALSE
,
PIDGIN_HIG_CAT_SPACE
);
gtk_notebook_append_page
(
GTK_NOTEBOOK
(
notebook
),
vbox_audio
,
gtk_label_new
(
_
(
"Audio"
)));
gtk_notebook_append_page
(
GTK_NOTEBOOK
(
notebook
),
vbox_video
,
gtk_label_new
(
_
(
"Video"
)));
gtk_container_set_border_width
(
GTK_CONTAINER
(
vbox_audio
),
PIDGIN_HIG_BORDER
);
gtk_container_set_border_width
(
GTK_CONTAINER
(
vbox_video
),
PIDGIN_HIG_BORDER
);
gtk_widget_show
(
vbox_audio
);
gtk_widget_show
(
vbox_video
);
sg
=
gtk_size_group_new
(
GTK_SIZE_GROUP_HORIZONTAL
);
get_plugin_frame
(
vbox_audio
,
sg
,
_
(
"Output"
),
_
(
"_Plugin"
),
AUDIO_SINK_PLUGINS
,
"/plugins/core/vvconfig/audio/sink/plugin"
,
_
(
"_Device"
),
"/plugins/core/vvconfig/audio/sink/device"
);
get_plugin_frame
(
vbox_audio
,
sg
,
_
(
"Input"
),
_
(
"P_lugin"
),
AUDIO_SRC_PLUGINS
,
"/plugins/core/vvconfig/audio/src/plugin"
,
_
(
"D_evice"
),
"/plugins/core/vvconfig/audio/src/device"
);
get_plugin_frame
(
vbox_video
,
sg
,
_
(
"Output"
),
_
(
"_Plugin"
),
VIDEO_SINK_PLUGINS
,
"/plugins/gtk/vvconfig/video/sink/plugin"
,
_
(
"_Device"
),
"/plugins/gtk/vvconfig/video/sink/device"
);
get_plugin_frame
(
vbox_video
,
sg
,
_
(
"Input"
),
_
(
"P_lugin"
),
VIDEO_SRC_PLUGINS
,
"/plugins/core/vvconfig/video/src/plugin"
,
_
(
"D_evice"
),
"/plugins/core/vvconfig/video/src/device"
);
return
notebook
;
}
static
GstElement
*
create_video_src
(
PurpleMedia
*
media
,
const
gchar
*
session_id
,
const
gchar
*
participant
)
{
const
gchar
*
plugin
=
purple_prefs_get_string
(
"/plugins/core/vvconfig/video/src/plugin"
);
const
gchar
*
device
=
purple_prefs_get_string
(
"/plugins/core/vvconfig/video/src/device"
);
GstElement
*
ret
;
if
(
plugin
[
0
]
==
'\0'
)
return
purple_media_element_info_call_create
(
old_video_src
,
media
,
session_id
,
participant
);
ret
=
gst_element_factory_make
(
plugin
,
"vvconfig-videosrc"
);
if
(
device
[
0
]
!=
'\0'
)
g_object_set
(
G_OBJECT
(
ret
),
"device"
,
device
,
NULL
);
if
(
purple_strequal
(
plugin
,
"videotestsrc"
))
g_object_set
(
G_OBJECT
(
ret
),
"is-live"
,
1
,
NULL
);
return
ret
;
}
static
void
videosink_disable_last_sample
(
GstElement
*
sink
)
{
GObjectClass
*
klass
=
G_OBJECT_GET_CLASS
(
sink
);
if
(
g_object_class_find_property
(
klass
,
"enable-last-sample"
))
{
g_object_set
(
sink
,
"enable-last-sample"
,
FALSE
,
NULL
);
}
}
static
void
autovideosink_child_added_cb
(
GstChildProxy
*
child_proxy
,
GObject
*
object
,
#if GST_CHECK_VERSION(1,0,0)
gchar
*
name
,
#endif
gpointer
user_data
)
{
videosink_disable_last_sample
(
GST_ELEMENT
(
object
));
}
static
GstElement
*
create_video_sink
(
PurpleMedia
*
media
,
const
gchar
*
session_id
,
const
gchar
*
participant
)
{
const
gchar
*
plugin
=
purple_prefs_get_string
(
"/plugins/gtk/vvconfig/video/sink/plugin"
);
const
gchar
*
device
=
purple_prefs_get_string
(
"/plugins/gtk/vvconfig/video/sink/device"
);
GstElement
*
ret
;
if
(
plugin
[
0
]
==
'\0'
)
return
purple_media_element_info_call_create
(
old_video_sink
,
media
,
session_id
,
participant
);
ret
=
gst_element_factory_make
(
plugin
,
NULL
);
if
(
device
[
0
]
!=
'\0'
)
g_object_set
(
G_OBJECT
(
ret
),
"device"
,
device
,
NULL
);
if
(
purple_strequal
(
plugin
,
"autovideosink"
))
{
g_signal_connect
(
ret
,
"child-added"
,
G_CALLBACK
(
autovideosink_child_added_cb
),
NULL
);
}
else
{
videosink_disable_last_sample
(
ret
);
}
return
ret
;
}
static
GstElement
*
create_audio_src
(
PurpleMedia
*
media
,
const
gchar
*
session_id
,
const
gchar
*
participant
)
{
const
gchar
*
plugin
=
purple_prefs_get_string
(
"/plugins/core/vvconfig/audio/src/plugin"
);
const
gchar
*
device
=
purple_prefs_get_string
(
"/plugins/core/vvconfig/audio/src/device"
);
GstElement
*
ret
;
if
(
plugin
[
0
]
==
'\0'
)
return
purple_media_element_info_call_create
(
old_audio_src
,
media
,
session_id
,
participant
);
ret
=
gst_element_factory_make
(
plugin
,
NULL
);
if
(
device
[
0
]
!=
'\0'
)
g_object_set
(
G_OBJECT
(
ret
),
"device"
,
device
,
NULL
);
return
ret
;
}
static
GstElement
*
create_audio_sink
(
PurpleMedia
*
media
,
const
gchar
*
session_id
,
const
gchar
*
participant
)
{
const
gchar
*
plugin
=
purple_prefs_get_string
(
"/plugins/core/vvconfig/audio/sink/plugin"
);
const
gchar
*
device
=
purple_prefs_get_string
(
"/plugins/core/vvconfig/audio/sink/device"
);
GstElement
*
ret
;
if
(
plugin
[
0
]
==
'\0'
)
return
purple_media_element_info_call_create
(
old_audio_sink
,
media
,
session_id
,
participant
);
ret
=
gst_element_factory_make
(
plugin
,
NULL
);
if
(
device
[
0
]
!=
'\0'
)
g_object_set
(
G_OBJECT
(
ret
),
"device"
,
device
,
NULL
);
return
ret
;
}
static
void
set_element_info_cond
(
PurpleMediaElementInfo
*
old_info
,
PurpleMediaElementInfo
*
new_info
,
const
gchar
*
id
)
{
gchar
*
element_id
=
purple_media_element_info_get_id
(
old_info
);
if
(
purple_strequal
(
element_id
,
id
))
purple_media_manager_set_active_element
(
purple_media_manager_get
(),
new_info
);
g_free
(
element_id
);
}
static
gboolean
plugin_load
(
PurplePlugin
*
plugin
)
{
PurpleMediaManager
*
manager
;
PurpleMediaElementInfo
*
video_src
,
*
video_sink
,
*
audio_src
,
*
audio_sink
;
/* Disable the plugin if the UI doesn't support VV */
if
(
purple_media_manager_get_ui_caps
(
purple_media_manager_get
())
==
PURPLE_MEDIA_CAPS_NONE
)
return
FALSE
;
purple_prefs_add_none
(
"/plugins/core/vvconfig"
);
purple_prefs_add_none
(
"/plugins/core/vvconfig/audio"
);
purple_prefs_add_none
(
"/plugins/core/vvconfig/audio/src"
);
purple_prefs_add_string
(
"/plugins/core/vvconfig/audio/src/plugin"
,
""
);
purple_prefs_add_string
(
"/plugins/core/vvconfig/audio/src/device"
,
""
);
purple_prefs_add_none
(
"/plugins/core/vvconfig/audio/sink"
);
purple_prefs_add_string
(
"/plugins/core/vvconfig/audio/sink/plugin"
,
""
);
purple_prefs_add_string
(
"/plugins/core/vvconfig/audio/sink/device"
,
""
);
purple_prefs_add_none
(
"/plugins/core/vvconfig/video"
);
purple_prefs_add_none
(
"/plugins/core/vvconfig/video/src"
);
purple_prefs_add_string
(
"/plugins/core/vvconfig/video/src/plugin"
,
""
);
purple_prefs_add_string
(
"/plugins/core/vvconfig/video/src/device"
,
""
);
purple_prefs_add_none
(
"/plugins/gtk/vvconfig"
);
purple_prefs_add_none
(
"/plugins/gtk/vvconfig/video"
);
purple_prefs_add_none
(
"/plugins/gtk/vvconfig/video/sink"
);
purple_prefs_add_string
(
"/plugins/gtk/vvconfig/video/sink/plugin"
,
""
);
purple_prefs_add_string
(
"/plugins/gtk/vvconfig/video/sink/device"
,
""
);
video_src
=
g_object_new
(
PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
"id"
,
"vvconfig-videosrc"
,
"name"
,
"VV Conf Plugin Video Source"
,
"type"
,
PURPLE_MEDIA_ELEMENT_VIDEO
|
PURPLE_MEDIA_ELEMENT_SRC
|
PURPLE_MEDIA_ELEMENT_ONE_SRC
|
PURPLE_MEDIA_ELEMENT_UNIQUE
,
"create-cb"
,
create_video_src
,
NULL
);
video_sink
=
g_object_new
(
PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
"id"
,
"vvconfig-videosink"
,
"name"
,
"VV Conf Plugin Video Sink"
,
"type"
,
PURPLE_MEDIA_ELEMENT_VIDEO
|
PURPLE_MEDIA_ELEMENT_SINK
|
PURPLE_MEDIA_ELEMENT_ONE_SINK
,
"create-cb"
,
create_video_sink
,
NULL
);
audio_src
=
g_object_new
(
PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
"id"
,
"vvconfig-audiosrc"
,
"name"
,
"VV Conf Plugin Audio Source"
,
"type"
,
PURPLE_MEDIA_ELEMENT_AUDIO
|
PURPLE_MEDIA_ELEMENT_SRC
|
PURPLE_MEDIA_ELEMENT_ONE_SRC
|
PURPLE_MEDIA_ELEMENT_UNIQUE
,
"create-cb"
,
create_audio_src
,
NULL
);
audio_sink
=
g_object_new
(
PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
"id"
,
"vvconfig-audiosink"
,
"name"
,
"VV Conf Plugin Audio Sink"
,
"type"
,
PURPLE_MEDIA_ELEMENT_AUDIO
|
PURPLE_MEDIA_ELEMENT_SINK
|
PURPLE_MEDIA_ELEMENT_ONE_SINK
,
"create-cb"
,
create_audio_sink
,
NULL
);
purple_debug_info
(
"gtkmedia"
,
"Registering media element types
\n
"
);
manager
=
purple_media_manager_get
();
old_video_src
=
purple_media_manager_get_active_element
(
manager
,
PURPLE_MEDIA_ELEMENT_VIDEO
|
PURPLE_MEDIA_ELEMENT_SRC
);
old_video_sink
=
purple_media_manager_get_active_element
(
manager
,
PURPLE_MEDIA_ELEMENT_VIDEO
|
PURPLE_MEDIA_ELEMENT_SINK
);
old_audio_src
=
purple_media_manager_get_active_element
(
manager
,
PURPLE_MEDIA_ELEMENT_AUDIO
|
PURPLE_MEDIA_ELEMENT_SRC
);
old_audio_sink
=
purple_media_manager_get_active_element
(
manager
,
PURPLE_MEDIA_ELEMENT_AUDIO
|
PURPLE_MEDIA_ELEMENT_SINK
);
set_element_info_cond
(
old_video_src
,
video_src
,
"pidgindefaultvideosrc"
);
set_element_info_cond
(
old_video_sink
,
video_sink
,
"pidgindefaultvideosink"
);
set_element_info_cond
(
old_audio_src
,
audio_src
,
"pidgindefaultaudiosrc"
);
set_element_info_cond
(
old_audio_sink
,
audio_sink
,
"pidgindefaultaudiosink"
);
return
TRUE
;
}
static
void
config_destroy
(
GtkObject
*
w
,
gpointer
nul
)
{
purple_debug_info
(
"vvconfig"
,
"closing vv configuration window
\n
"
);
window
=
NULL
;
}
static
void
config_close
(
GtkObject
*
w
,
gpointer
nul
)
{
gtk_widget_destroy
(
GTK_WIDGET
(
window
));
}
typedef
GtkWidget
*
(
*
FrameCreateCb
)(
PurplePlugin
*
plugin
);
static
void
show_config
(
PurplePluginAction
*
action
)
{
if
(
!
window
)
{
FrameCreateCb
create_frame
=
action
->
user_data
;
GtkWidget
*
vbox
=
gtk_vbox_new
(
FALSE
,
PIDGIN_HIG_BORDER
);
GtkWidget
*
hbox
=
gtk_hbox_new
(
FALSE
,
PIDGIN_HIG_BORDER
);
GtkWidget
*
config_frame
=
create_frame
(
NULL
);
GtkWidget
*
close
=
gtk_button_new_from_stock
(
GTK_STOCK_CLOSE
);
gtk_container_add
(
GTK_CONTAINER
(
vbox
),
config_frame
);
gtk_container_add
(
GTK_CONTAINER
(
vbox
),
hbox
);
window
=
pidgin_create_window
(
action
->
label
,
PIDGIN_HIG_BORDER
,
NULL
,
FALSE
);
g_signal_connect
(
G_OBJECT
(
window
),
"destroy"
,
G_CALLBACK
(
config_destroy
),
NULL
);
g_signal_connect
(
G_OBJECT
(
close
),
"clicked"
,
G_CALLBACK
(
config_close
),
NULL
);
gtk_box_pack_end
(
GTK_BOX
(
hbox
),
close
,
FALSE
,
FALSE
,
PIDGIN_HIG_BORDER
);
gtk_container_add
(
GTK_CONTAINER
(
window
),
vbox
);
gtk_widget_show
(
GTK_WIDGET
(
close
));
gtk_widget_show
(
GTK_WIDGET
(
vbox
));
gtk_widget_show
(
GTK_WIDGET
(
hbox
));
}
gtk_window_present
(
GTK_WINDOW
(
window
));
}
static
GstElement
*
create_pipeline
()
{
GstElement
*
pipeline
=
gst_pipeline_new
(
"voicetest"
);
GstElement
*
src
=
create_audio_src
(
NULL
,
NULL
,
NULL
);
GstElement
*
sink
=
create_audio_sink
(
NULL
,
NULL
,
NULL
);
GstElement
*
volume
=
gst_element_factory_make
(
"volume"
,
"volume"
);
GstElement
*
level
=
gst_element_factory_make
(
"level"
,
"level"
);
GstElement
*
valve
=
gst_element_factory_make
(
"valve"
,
"valve"
);
gst_bin_add_many
(
GST_BIN
(
pipeline
),
src
,
volume
,
level
,
valve
,
sink
,
NULL
);
gst_element_link_many
(
src
,
volume
,
level
,
valve
,
sink
,
NULL
);
gst_element_set_state
(
GST_ELEMENT
(
pipeline
),
GST_STATE_PLAYING
);
return
pipeline
;
}
static
void
on_volume_change_cb
(
GtkRange
*
range
,
GstBin
*
pipeline
)
{
GstElement
*
volume
;
g_return_if_fail
(
pipeline
!=
NULL
);
volume
=
gst_bin_get_by_name
(
pipeline
,
"volume"
);
g_object_set
(
volume
,
"volume"
,
gtk_range_get_value
(
range
)
/
10.0
,
NULL
);
}
static
gdouble
gst_msg_db_to_percent
(
GstMessage
*
msg
,
gchar
*
value_name
)
{
const
GValue
*
list
;
const
GValue
*
value
;
gdouble
value_db
;
gdouble
percent
;
list
=
gst_structure_get_value
(
gst_message_get_structure
(
msg
),
value_name
);
#if GST_CHECK_VERSION(1,0,0)
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
value
=
g_value_array_get_nth
(
g_value_get_boxed
(
list
),
0
);
G_GNUC_END_IGNORE_DEPRECATIONS
#else
value
=
gst_value_list_get_value
(
list
,
0
);
#endif
value_db
=
g_value_get_double
(
value
);
percent
=
pow
(
10
,
value_db
/
20
);
return
(
percent
>
1.0
)
?
1.0
:
percent
;
}
typedef
struct
{
GtkProgressBar
*
level
;
GtkRange
*
threshold
;
}
BusCbCtx
;
static
gboolean
gst_bus_cb
(
GstBus
*
bus
,
GstMessage
*
msg
,
BusCbCtx
*
ctx
)
{
if
(
GST_MESSAGE_TYPE
(
msg
)
==
GST_MESSAGE_ELEMENT
&&
gst_structure_has_name
(
gst_message_get_structure
(
msg
),
"level"
))
{
GstElement
*
src
=
GST_ELEMENT
(
GST_MESSAGE_SRC
(
msg
));
gchar
*
name
=
gst_element_get_name
(
src
);
if
(
purple_strequal
(
name
,
"level"
))
{
gdouble
percent
;
gdouble
threshold
;
GstElement
*
valve
;
percent
=
gst_msg_db_to_percent
(
msg
,
"rms"
);
percent
*=
5
;
if
(
percent
>
1.0
)
percent
=
1.0
;
gtk_progress_bar_set_fraction
(
ctx
->
level
,
percent
);
percent
=
gst_msg_db_to_percent
(
msg
,
"decay"
);
threshold
=
gtk_range_get_value
(
ctx
->
threshold
)
/
100.0
;
valve
=
gst_bin_get_by_name
(
GST_BIN
(
GST_ELEMENT_PARENT
(
src
)),
"valve"
);
g_object_set
(
valve
,
"drop"
,
(
percent
<
threshold
),
NULL
);
g_object_set
(
ctx
->
level
,
"text"
,
(
percent
<
threshold
)
?
_
(
"DROP"
)
:
" "
,
NULL
);
}
g_free
(
name
);
}
return
TRUE
;
}
static
void
voice_test_frame_destroy_cb
(
GtkObject
*
w
,
GstElement
*
pipeline
)
{
g_return_if_fail
(
GST_IS_ELEMENT
(
pipeline
));
gst_element_set_state
(
pipeline
,
GST_STATE_NULL
);
gst_object_unref
(
pipeline
);
}
static
void
volume_scale_destroy_cb
(
GtkRange
*
volume
,
gpointer
nul
)
{
purple_prefs_set_int
(
"/purple/media/audio/volume/input"
,
gtk_range_get_value
(
volume
));
}
static
gchar
*
threshold_value_format_cb
(
GtkScale
*
scale
,
gdouble
value
)
{
return
g_strdup_printf
(
"%.*f%%"
,
gtk_scale_get_digits
(
scale
),
value
);
}
static
void
threshold_scale_destroy_cb
(
GtkRange
*
threshold
,
gpointer
nul
)
{
purple_prefs_set_int
(
"/purple/media/audio/silence_threshold"
,
gtk_range_get_value
(
threshold
));
}
static
GtkWidget
*
get_voice_test_frame
(
PurplePlugin
*
plugin
)
{
GtkWidget
*
vbox
=
gtk_vbox_new
(
FALSE
,
PIDGIN_HIG_BORDER
);
GtkWidget
*
level
=
gtk_progress_bar_new
();
GtkWidget
*
volume
=
gtk_hscale_new_with_range
(
0
,
100
,
1
);
GtkWidget
*
threshold
=
gtk_hscale_new_with_range
(
0
,
100
,
1
);
GtkWidget
*
label
;
GtkTable
*
table
=
GTK_TABLE
(
gtk_table_new
(
2
,
2
,
FALSE
));
GstElement
*
pipeline
;
GstBus
*
bus
;
BusCbCtx
*
ctx
;
g_object_set
(
vbox
,
"width-request"
,
500
,
NULL
);
gtk_table_set_row_spacings
(
table
,
PIDGIN_HIG_BOX_SPACE
);
gtk_table_set_col_spacings
(
table
,
PIDGIN_HIG_BOX_SPACE
);
label
=
gtk_label_new
(
_
(
"Volume:"
));
g_object_set
(
label
,
"xalign"
,
0.0
,
NULL
);
gtk_table_attach
(
table
,
label
,
0
,
1
,
0
,
1
,
GTK_FILL
,
0
,
0
,
0
);
gtk_table_attach_defaults
(
table
,
volume
,
1
,
2
,
0
,
1
);
label
=
gtk_label_new
(
_
(
"Silence threshold:"
));
g_object_set
(
label
,
"xalign"
,
0.0
,
"yalign"
,
1.0
,
NULL
);
gtk_table_attach
(
table
,
label
,
0
,
1
,
1
,
2
,
GTK_FILL
,
GTK_FILL
,
0
,
0
);
gtk_table_attach_defaults
(
table
,
threshold
,
1
,
2
,
1
,
2
);
gtk_container_add
(
GTK_CONTAINER
(
vbox
),
level
);
gtk_container_add
(
GTK_CONTAINER
(
vbox
),
GTK_WIDGET
(
table
));
gtk_widget_show_all
(
vbox
);
pipeline
=
create_pipeline
();
bus
=
gst_pipeline_get_bus
(
GST_PIPELINE
(
pipeline
));
gst_bus_add_signal_watch
(
bus
);
ctx
=
g_new
(
BusCbCtx
,
1
);
ctx
->
level
=
GTK_PROGRESS_BAR
(
level
);
ctx
->
threshold
=
GTK_RANGE
(
threshold
);
g_signal_connect_data
(
bus
,
"message"
,
G_CALLBACK
(
gst_bus_cb
),
ctx
,
(
GClosureNotify
)
g_free
,
0
);
gst_object_unref
(
bus
);
g_signal_connect
(
volume
,
"value-changed"
,
(
GCallback
)
on_volume_change_cb
,
pipeline
);
gtk_range_set_value
(
GTK_RANGE
(
volume
),
purple_prefs_get_int
(
"/purple/media/audio/volume/input"
));
gtk_widget_set
(
volume
,
"draw-value"
,
FALSE
,
NULL
);
gtk_range_set_value
(
GTK_RANGE
(
threshold
),
purple_prefs_get_int
(
"/purple/media/audio/silence_threshold"
));
g_signal_connect
(
vbox
,
"destroy"
,
G_CALLBACK
(
voice_test_frame_destroy_cb
),
pipeline
);
g_signal_connect
(
volume
,
"destroy"
,
G_CALLBACK
(
volume_scale_destroy_cb
),
NULL
);
g_signal_connect
(
threshold
,
"format-value"
,
G_CALLBACK
(
threshold_value_format_cb
),
NULL
);
g_signal_connect
(
threshold
,
"destroy"
,
G_CALLBACK
(
threshold_scale_destroy_cb
),
NULL
);
return
vbox
;
}
static
GList
*
actions
(
PurplePlugin
*
plugin
,
gpointer
context
)
{
GList
*
l
=
NULL
;
PurplePluginAction
*
act
=
NULL
;
act
=
purple_plugin_action_new
(
_
(
"Input and Output Settings"
),
show_config
);
act
->
user_data
=
get_plugin_config_frame
;
l
=
g_list_append
(
l
,
act
);
act
=
purple_plugin_action_new
(
_
(
"Microphone Test"
),
show_config
);
act
->
user_data
=
get_voice_test_frame
;
l
=
g_list_append
(
l
,
act
);
return
l
;
}
static
gboolean
plugin_unload
(
PurplePlugin
*
plugin
)
{
PurpleMediaManager
*
manager
=
purple_media_manager_get
();
purple_media_manager_set_active_element
(
manager
,
old_video_src
);
purple_media_manager_set_active_element
(
manager
,
old_video_sink
);
purple_media_manager_set_active_element
(
manager
,
old_audio_src
);
purple_media_manager_set_active_element
(
manager
,
old_audio_sink
);
return
TRUE
;
}
static
PidginPluginUiInfo
ui_info
=
{
get_plugin_config_frame
,
0
,
/* page_num (Reserved) */
/* Padding */
NULL
,
NULL
,
NULL
,
NULL
};
static
PurplePluginInfo
info
=
{
PURPLE_PLUGIN_MAGIC
,
/**< magic */
PURPLE_MAJOR_VERSION
,
/**< major version */
PURPLE_MINOR_VERSION
,
/**< minor version */
PURPLE_PLUGIN_STANDARD
,
/**< type */
PIDGIN_PLUGIN_TYPE
,
/**< ui_requirement */
0
,
/**< flags */
NULL
,
/**< dependencies */
PURPLE_PRIORITY_DEFAULT
,
/**< priority */
"gtk-maiku-vvconfig"
,
/**< id */
N_
(
"Voice/Video Settings"
),
/**< name */
DISPLAY_VERSION
,
/**< version */
N_
(
"Configure your microphone and webcam."
),
/**< summary */
N_
(
"Configure microphone and webcam "
"settings for voice/video calls."
),
/**< description */
"Mike Ruprecht <cmaiku@gmail.com>"
,
/**< author */
PURPLE_WEBSITE
,
/**< homepage */
plugin_load
,
/**< load */
plugin_unload
,
/**< unload */
NULL
,
/**< destroy */
&
ui_info
,
/**< ui_info */
NULL
,
/**< extra_info */
NULL
,
/**< prefs_info */
actions
,
/**< actions */
/* padding */
NULL
,
NULL
,
NULL
,
NULL
};
static
void
init_plugin
(
PurplePlugin
*
plugin
)
{
}
PURPLE_INIT_PLUGIN
(
vvconfig
,
init_plugin
,
info
)