/pidgin/main: e4c122196b08: Add out-of-band DTMF support and dia...
David Woodhouse
David.Woodhouse at intel.com
Fri Mar 6 17:19:16 EST 2015
Changeset: e4c122196b08fa0596351ac000f5440128a13449
Author: David Woodhouse <David.Woodhouse at intel.com>
Date: 2014-10-08 23:10 +0100
Branch: default
URL: https://hg.pidgin.im/pidgin/main/rev/e4c122196b08
Description:
Add out-of-band DTMF support and dialpad to use it
This resolves the non-Jabber-specific part of ticket 12617, by adding
purple_media_send_dtmf(), and a corresponding method to the
PurpleMediaBackendIface and the farstream implementation thereof.
Fixes #15575
Refs #12617
diffstat:
libpurple/media.c | 43 ++++++++++++
libpurple/media.h | 20 +++++-
libpurple/media/backend-fs2.c | 63 ++++++++++++++++++
libpurple/media/backend-iface.h | 3 +
pidgin/gtkmedia.c | 137 +++++++++++++++++++++++++++++++++++++++-
5 files changed, 263 insertions(+), 3 deletions(-)
diffs (truncated from 355 to 300 lines):
diff --git a/libpurple/media.c b/libpurple/media.c
--- a/libpurple/media.c
+++ b/libpurple/media.c
@@ -1420,3 +1420,46 @@ purple_media_get_tee(PurpleMedia *media,
}
#endif /* USE_GSTREAMER */
+gboolean
+purple_media_send_dtmf(PurpleMedia *media, const gchar *session_id,
+ gchar dtmf, guint8 volume, guint16 duration)
+{
+#ifdef USE_VV
+ PurpleAccount *account = NULL;
+ PurpleConnection *gc = NULL;
+ PurplePlugin *prpl = NULL;
+ PurplePluginProtocolInfo *prpl_info = NULL;
+ PurpleMediaBackendIface *backend_iface = NULL;
+
+ if (media)
+ {
+ account = purple_media_get_account(media);
+ backend_iface = PURPLE_MEDIA_BACKEND_GET_INTERFACE(media->priv->backend);
+ }
+ if (account)
+ gc = purple_account_get_connection(account);
+ if (gc)
+ prpl = purple_connection_get_prpl(gc);
+ if (prpl)
+ prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl);
+
+ if (dtmf == 'a')
+ dtmf = 'A';
+ else if (dtmf == 'b')
+ dtmf = 'B';
+ else if (dtmf == 'c')
+ dtmf = 'C';
+ else if (dtmf == 'd')
+ dtmf = 'D';
+
+ g_return_val_if_fail(strchr("0123456789ABCD#*", dtmf), FALSE);
+
+ if (backend_iface && backend_iface->send_dtmf
+ && backend_iface->send_dtmf(media->priv->backend,
+ session_id, dtmf, volume, duration))
+ {
+ return TRUE;
+ }
+#endif
+ return FALSE;
+}
diff --git a/libpurple/media.h b/libpurple/media.h
--- a/libpurple/media.h
+++ b/libpurple/media.h
@@ -31,6 +31,8 @@
#include <glib.h>
#include <glib-object.h>
+typedef struct _PurpleMedia PurpleMedia;
+
#include "media/candidate.h"
#include "media/codec.h"
#include "media/enum-types.h"
@@ -42,8 +44,6 @@
#define PURPLE_IS_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA))
#define PURPLE_MEDIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA, PurpleMediaClass))
-typedef struct _PurpleMedia PurpleMedia;
-
#include "signals.h"
#include "util.h"
@@ -441,6 +441,22 @@ gulong purple_media_set_output_window(Pu
*/
void purple_media_remove_output_windows(PurpleMedia *media);
+/**
+ * purple_media_send_dtmf:
+ * @media: The media instance to send a DTMF signal to.
+ * @sess_id: The session id of the session to send the DTMF signal on.
+ * @dtmf: The character representing the DTMF in the range [0-9#*A-D].
+ * @volume: The power level expressed in dBm0 after dropping the sign in the
+ * range of 0 to 63. A larger value represents a lower volume.
+ * @duration: The duration of the tone in milliseconds.
+ *
+ * Sends a DTMF signal out-of-band.
+ *
+ * Returns: %TRUE DTMF sent successfully, or %FALSE otherwise.
+ */
+gboolean purple_media_send_dtmf(PurpleMedia *media, const gchar *session_id,
+ gchar dtmf, guint8 volume, guint16 duration);
+
G_END_DECLS
#endif /* _PURPLE_MEDIA_H_ */
diff --git a/libpurple/media/backend-fs2.c b/libpurple/media/backend-fs2.c
--- a/libpurple/media/backend-fs2.c
+++ b/libpurple/media/backend-fs2.c
@@ -91,6 +91,9 @@ static gboolean purple_media_backend_fs2
static void purple_media_backend_fs2_set_params(PurpleMediaBackend *self,
guint num_params, GParameter *params);
static const gchar **purple_media_backend_fs2_get_available_params(void);
+static gboolean purple_media_backend_fs2_send_dtmf(
+ PurpleMediaBackend *self, const gchar *sess_id,
+ gchar dtmf, guint8 volume, guint16 duration);
static void free_stream(PurpleMediaBackendFs2Stream *stream);
static void free_session(PurpleMediaBackendFs2Session *session);
@@ -531,6 +534,7 @@ purple_media_backend_iface_init(PurpleMe
iface->set_send_codec = purple_media_backend_fs2_set_send_codec;
iface->set_params = purple_media_backend_fs2_set_params;
iface->get_available_params = purple_media_backend_fs2_get_available_params;
+ iface->send_dtmf = purple_media_backend_fs2_send_dtmf;
}
static FsMediaType
@@ -2557,6 +2561,65 @@ purple_media_backend_fs2_set_params(Purp
gst_structure_free(sdes);
#endif /* HAVE_FARSIGHT */
}
+static gboolean
+send_dtmf_callback(gpointer userdata)
+{
+ FsSession *session = userdata;
+
+ fs_session_stop_telephony_event(session);
+
+ return FALSE;
+}
+static gboolean
+purple_media_backend_fs2_send_dtmf(PurpleMediaBackend *self,
+ const gchar *sess_id, gchar dtmf, guint8 volume,
+ guint16 duration)
+{
+ PurpleMediaBackendFs2Session *session;
+ FsDTMFEvent event;
+
+ g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), FALSE);
+
+ session = get_session(PURPLE_MEDIA_BACKEND_FS2(self), sess_id);
+ if (session == NULL)
+ return FALSE;
+
+ /* Convert DTMF char into FsDTMFEvent enum */
+ switch(dtmf) {
+ case '0': event = FS_DTMF_EVENT_0; break;
+ case '1': event = FS_DTMF_EVENT_1; break;
+ case '2': event = FS_DTMF_EVENT_2; break;
+ case '3': event = FS_DTMF_EVENT_3; break;
+ case '4': event = FS_DTMF_EVENT_4; break;
+ case '5': event = FS_DTMF_EVENT_5; break;
+ case '6': event = FS_DTMF_EVENT_6; break;
+ case '7': event = FS_DTMF_EVENT_7; break;
+ case '8': event = FS_DTMF_EVENT_8; break;
+ case '9': event = FS_DTMF_EVENT_9; break;
+ case '*': event = FS_DTMF_EVENT_STAR; break;
+ case '#': event = FS_DTMF_EVENT_POUND; break;
+ case 'A': event = FS_DTMF_EVENT_A; break;
+ case 'B': event = FS_DTMF_EVENT_B; break;
+ case 'C': event = FS_DTMF_EVENT_C; break;
+ case 'D': event = FS_DTMF_EVENT_D; break;
+ default:
+ return FALSE;
+ }
+
+ if (!fs_session_start_telephony_event(session->session,
+ event, volume)) {
+ return FALSE;
+ }
+
+ if (duration <= 50) {
+ fs_session_stop_telephony_event(session->session);
+ } else {
+ purple_timeout_add(duration, send_dtmf_callback,
+ session->session);
+ }
+
+ return TRUE;
+}
#else
GType
purple_media_backend_fs2_get_type(void)
diff --git a/libpurple/media/backend-iface.h b/libpurple/media/backend-iface.h
--- a/libpurple/media/backend-iface.h
+++ b/libpurple/media/backend-iface.h
@@ -83,6 +83,9 @@ struct _PurpleMediaBackendIface
void (*set_params) (PurpleMediaBackend *self,
guint num_params, GParameter *params);
const gchar **(*get_available_params) (void);
+ gboolean (*send_dtmf) (PurpleMediaBackend *self,
+ const gchar *sess_id, gchar dtmf, guint8 volume,
+ guint16 duration);
};
/**
diff --git a/pidgin/gtkmedia.c b/pidgin/gtkmedia.c
--- a/pidgin/gtkmedia.c
+++ b/pidgin/gtkmedia.c
@@ -43,6 +43,7 @@
#ifdef GDK_WINDOWING_QUARTZ
#include <gdk/gdkquartz.h>
#endif
+#include <gdk/gdkkeysyms.h>
#include "gtk3compat.h"
@@ -744,6 +745,136 @@ pidgin_media_add_audio_widget(PidginMedi
}
static void
+phone_dtmf_pressed_cb(GtkButton *button, gpointer user_data)
+{
+ PidginMedia *gtkmedia = user_data;
+ gint num;
+ gchar *sid;
+
+ num = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "dtmf-digit"));
+ sid = g_object_get_data(G_OBJECT(button), "session-id");
+
+ purple_media_send_dtmf(gtkmedia->priv->media, sid, num, 25, 50);
+}
+
+static inline GtkWidget *
+phone_create_button(const gchar *text_hi, const gchar *text_lo)
+{
+ GtkWidget *button;
+ GtkWidget *label_hi;
+ GtkWidget *label_lo;
+ GtkWidget *grid;
+ gchar *text_hi_local;
+
+ if (text_hi)
+ text_hi_local = _(text_hi);
+ else
+ text_hi_local = "";
+
+ grid = gtk_vbox_new(TRUE, 0);
+
+ button = gtk_button_new();
+ label_hi = gtk_label_new(text_hi_local);
+ gtk_misc_set_alignment(GTK_MISC(label_hi), 0.5, 0.5);
+ gtk_box_pack_end(GTK_BOX(grid), label_hi, FALSE, TRUE, 0);
+ label_lo = gtk_label_new(text_lo);
+ gtk_misc_set_alignment(GTK_MISC(label_lo), 0.5, 0.5);
+ gtk_label_set_use_markup(GTK_LABEL(label_lo), TRUE);
+ gtk_box_pack_end(GTK_BOX(grid), label_lo, FALSE, TRUE, 0);
+ gtk_container_add(GTK_CONTAINER(button), grid);
+
+ return button;
+}
+
+static struct phone_label {
+ gchar *subtext;
+ gchar *text;
+ gchar chr;
+} phone_labels[] = {
+ {"<b>1</b>", NULL, '1'},
+ /* Translators note: These are the letters on the keys of a numeric
+ keypad; translate according to §7.2.4 of
+ http://www.etsi.org/deliver/etsi_es/202100_202199/202130/01.01.01_60/es_20213 */
+ /* Letters on the '2' key of a numeric keypad */
+ {"<b>2</b>", N_("ABC"), '2'},
+ /* Letters on the '3' key of a numeric keypad */
+ {"<b>3</b>", N_("DEF"), '3'},
+ /* Letters on the '4' key of a numeric keypad */
+ {"<b>4</b>", N_("GHI"), '4'},
+ /* Letters on the '5' key of a numeric keypad */
+ {"<b>5</b>", N_("JKL"), '5'},
+ /* Letters on the '6' key of a numeric keypad */
+ {"<b>6</b>", N_("MNO"), '6'},
+ /* Letters on the '7' key of a numeric keypad */
+ {"<b>7</b>", N_("PQRS"), '7'},
+ /* Letters on the '8' key of a numeric keypad */
+ {"<b>8</b>", N_("TUV"), '8'},
+ /* Letters on the '9' key of a numeric keypad */
+ {"<b>9</b>", N_("WXYZ"), '9'},
+ {"<b>*</b>", NULL, '*'},
+ {"<b>0</b>", NULL, '0'},
+ {"<b>#</b>", NULL, '#'},
+ {NULL, NULL, 0}
+};
+
+static gboolean
+pidgin_media_dtmf_key_press_event_cb(GtkWidget *widget,
+ GdkEvent *event, gpointer user_data)
+{
+ PidginMedia *gtkmedia = user_data;
+ GdkEventKey *key = (GdkEventKey *) event;
+
+ if (event->type != GDK_KEY_PRESS) {
+ return FALSE;
+ }
+
+ if ((key->keyval >= GDK_KEY_0 && key->keyval <= GDK_KEY_9) ||
+ key->keyval == GDK_KEY_asterisk ||
+ key->keyval == GDK_KEY_numbersign) {
+ gchar *sid = g_object_get_data(G_OBJECT(widget), "session-id");
+
+ purple_media_send_dtmf(gtkmedia->priv->media, sid, key->keyval, 25, 50);
+ }
+
More information about the Commits
mailing list