base-call: convert DTMF tone handling to a state machine
Generalize the timeout/stop logic that QMI requires into the base call. We'll use this to implement the pause character (,) generically. Signed-off-by: Dan Williams <dan@ioncontrol.co>
This commit is contained in:
+263
-7
@@ -89,6 +89,9 @@ struct _MMBaseCallPrivate {
|
||||
* 'terminated' is coming asynchronously (e.g. via in-call state
|
||||
* update notifications) */
|
||||
GCancellable *start_cancellable;
|
||||
|
||||
/* DTMF support */
|
||||
GQueue *dtmf_queue;
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
@@ -728,6 +731,239 @@ handle_hangup (MMBaseCall *self,
|
||||
/*****************************************************************************/
|
||||
/* Send dtmf (DBus call handling) */
|
||||
|
||||
typedef enum {
|
||||
DTMF_STEP_FIRST,
|
||||
DTMF_STEP_START,
|
||||
DTMF_STEP_TIMEOUT,
|
||||
DTMF_STEP_STOP,
|
||||
DTMF_STEP_LAST,
|
||||
} DtmfStep;
|
||||
|
||||
typedef struct {
|
||||
DtmfStep step;
|
||||
GError *saved_error;
|
||||
guint8 call_id;
|
||||
gchar *dtmf;
|
||||
guint timeout_id;
|
||||
} SendDtmfContext;
|
||||
|
||||
static void
|
||||
send_dtmf_context_clear_timeout (SendDtmfContext *ctx)
|
||||
{
|
||||
if (ctx->timeout_id) {
|
||||
g_source_remove (ctx->timeout_id);
|
||||
ctx->timeout_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
send_dtmf_context_free (SendDtmfContext *ctx)
|
||||
{
|
||||
send_dtmf_context_clear_timeout (ctx);
|
||||
g_free (ctx->dtmf);
|
||||
g_assert (!ctx->saved_error);
|
||||
g_slice_free (SendDtmfContext, ctx);
|
||||
}
|
||||
|
||||
static void send_dtmf_task_step_next (GTask *task);
|
||||
|
||||
static void
|
||||
stop_dtmf_ignore_ready (MMBaseCall *self,
|
||||
GAsyncResult *res,
|
||||
gpointer unused)
|
||||
{
|
||||
/* Ignore the result and error */
|
||||
MM_BASE_CALL_GET_CLASS (self)->stop_dtmf_finish (self, res, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
send_dtmf_task_cancel (GTask *task)
|
||||
{
|
||||
MMBaseCall *self;
|
||||
SendDtmfContext *ctx;
|
||||
|
||||
ctx = g_task_get_task_data (task);
|
||||
self = g_task_get_source_object (task);
|
||||
|
||||
send_dtmf_context_clear_timeout (ctx);
|
||||
if (ctx->step > DTMF_STEP_FIRST && ctx->step < DTMF_STEP_STOP) {
|
||||
if (MM_BASE_CALL_GET_CLASS (self)->stop_dtmf) {
|
||||
MM_BASE_CALL_GET_CLASS (self)->stop_dtmf (self,
|
||||
(GAsyncReadyCallback)stop_dtmf_ignore_ready,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
g_assert (ctx->step != DTMF_STEP_LAST);
|
||||
ctx->step = DTMF_STEP_LAST;
|
||||
send_dtmf_task_step_next (task);
|
||||
}
|
||||
|
||||
static void
|
||||
stop_dtmf_ready (MMBaseCall *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
SendDtmfContext *ctx;
|
||||
GError *error = NULL;
|
||||
gboolean success;
|
||||
|
||||
ctx = g_task_get_task_data (task);
|
||||
|
||||
success = MM_BASE_CALL_GET_CLASS (self)->stop_dtmf_finish (self, res, &error);
|
||||
if (ctx->step == DTMF_STEP_STOP) {
|
||||
if (!success) {
|
||||
g_propagate_error (&ctx->saved_error, error);
|
||||
ctx->step = DTMF_STEP_LAST;
|
||||
} else
|
||||
ctx->step++;
|
||||
|
||||
send_dtmf_task_step_next (task);
|
||||
}
|
||||
|
||||
/* Balance stop_dtmf() */
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
dtmf_timeout (GTask *task)
|
||||
{
|
||||
SendDtmfContext *ctx;
|
||||
|
||||
ctx = g_task_get_task_data (task);
|
||||
|
||||
send_dtmf_context_clear_timeout (ctx);
|
||||
ctx->step++;
|
||||
send_dtmf_task_step_next (task);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
send_dtmf_ready (MMBaseCall *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
SendDtmfContext *ctx;
|
||||
GError *error = NULL;
|
||||
gboolean success;
|
||||
|
||||
ctx = g_task_get_task_data (task);
|
||||
|
||||
success = MM_BASE_CALL_GET_CLASS (self)->send_dtmf_finish (self, res, &error);
|
||||
if (ctx->step == DTMF_STEP_START) {
|
||||
if (!success) {
|
||||
g_propagate_error (&ctx->saved_error, error);
|
||||
ctx->step = DTMF_STEP_LAST;
|
||||
} else
|
||||
ctx->step++;
|
||||
|
||||
send_dtmf_task_step_next (task);
|
||||
}
|
||||
|
||||
/* Balance send_dtmf() */
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static void
|
||||
send_dtmf_task_step_next (GTask *task)
|
||||
{
|
||||
SendDtmfContext *ctx;
|
||||
gboolean need_stop;
|
||||
MMBaseCall *self;
|
||||
|
||||
self = g_task_get_source_object (task);
|
||||
ctx = g_task_get_task_data (task);
|
||||
|
||||
need_stop = MM_BASE_CALL_GET_CLASS (self)->stop_dtmf &&
|
||||
MM_BASE_CALL_GET_CLASS (self)->stop_dtmf_finish;
|
||||
|
||||
switch (ctx->step) {
|
||||
case DTMF_STEP_FIRST:
|
||||
ctx->step++;
|
||||
/* Fall through */
|
||||
case DTMF_STEP_START:
|
||||
MM_BASE_CALL_GET_CLASS (self)->send_dtmf (self,
|
||||
ctx->dtmf,
|
||||
(GAsyncReadyCallback)send_dtmf_ready,
|
||||
g_object_ref (task));
|
||||
break;
|
||||
case DTMF_STEP_TIMEOUT:
|
||||
if (need_stop) {
|
||||
/* Disable DTMF press after DTMF tone duration elapses */
|
||||
ctx->timeout_id = g_timeout_add (mm_base_call_get_dtmf_tone_duration (self),
|
||||
(GSourceFunc) dtmf_timeout,
|
||||
task);
|
||||
return;
|
||||
}
|
||||
/* Fall through */
|
||||
case DTMF_STEP_STOP:
|
||||
if (need_stop) {
|
||||
send_dtmf_context_clear_timeout (ctx);
|
||||
MM_BASE_CALL_GET_CLASS (self)->stop_dtmf (self,
|
||||
(GAsyncReadyCallback)stop_dtmf_ready,
|
||||
g_object_ref (task));
|
||||
return;
|
||||
}
|
||||
/* Fall through */
|
||||
case DTMF_STEP_LAST:
|
||||
send_dtmf_context_clear_timeout (ctx);
|
||||
if (ctx->saved_error)
|
||||
g_task_return_error (task, g_steal_pointer (&ctx->saved_error));
|
||||
else
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
|
||||
/* Start the next tone if any are queued */
|
||||
g_queue_remove (self->priv->dtmf_queue, task);
|
||||
task = g_queue_peek_head (self->priv->dtmf_queue);
|
||||
if (task)
|
||||
send_dtmf_task_step_next (task);
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
send_dtmf_task_finish (MMBaseCall *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static GTask *
|
||||
send_dtmf_task_new (MMBaseCall *self,
|
||||
const gchar *dtmf,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data,
|
||||
GError **error)
|
||||
{
|
||||
GTask *task;
|
||||
SendDtmfContext *ctx;
|
||||
guint8 call_id;
|
||||
|
||||
call_id = mm_base_call_get_index (self);
|
||||
if (call_id == 0) {
|
||||
g_set_error (error,
|
||||
MM_CORE_ERROR,
|
||||
MM_CORE_ERROR_INVALID_ARGS,
|
||||
"Invalid call index");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
task = g_task_new (self, NULL, callback, user_data);
|
||||
ctx = g_slice_new0 (SendDtmfContext);
|
||||
ctx->call_id = call_id;
|
||||
ctx->dtmf = g_strdup (dtmf);
|
||||
g_task_set_task_data (task, ctx, (GDestroyNotify) send_dtmf_context_free);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Send DTMF D-Bus request handling */
|
||||
|
||||
typedef struct {
|
||||
MMBaseCall *self;
|
||||
GDBusMethodInvocation *invocation;
|
||||
@@ -750,7 +986,7 @@ handle_send_dtmf_ready (MMBaseCall *self,
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!MM_BASE_CALL_GET_CLASS (self)->send_dtmf_finish (self, res, &error)) {
|
||||
if (!send_dtmf_task_finish (self, res, &error)) {
|
||||
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
||||
} else {
|
||||
mm_gdbus_call_complete_send_dtmf (MM_GDBUS_CALL (ctx->self), ctx->invocation);
|
||||
@@ -764,8 +1000,9 @@ handle_send_dtmf_auth_ready (MMAuthProvider *authp,
|
||||
GAsyncResult *res,
|
||||
HandleSendDtmfContext *ctx)
|
||||
{
|
||||
MMCallState state;
|
||||
GError *error = NULL;
|
||||
MMCallState state;
|
||||
GError *error = NULL;
|
||||
GTask *task;
|
||||
|
||||
if (!mm_auth_provider_authorize_finish (authp, res, &error)) {
|
||||
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
||||
@@ -775,7 +1012,7 @@ handle_send_dtmf_auth_ready (MMAuthProvider *authp,
|
||||
|
||||
state = mm_gdbus_call_get_state (MM_GDBUS_CALL (ctx->self));
|
||||
|
||||
/* Check if we do support doing it */
|
||||
/* Check if we do support doing DTMF at all */
|
||||
if (!MM_BASE_CALL_GET_CLASS (ctx->self)->send_dtmf ||
|
||||
!MM_BASE_CALL_GET_CLASS (ctx->self)->send_dtmf_finish) {
|
||||
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
||||
@@ -793,9 +1030,20 @@ handle_send_dtmf_auth_ready (MMAuthProvider *authp,
|
||||
}
|
||||
|
||||
mm_obj_info (ctx->self, "processing user request to send DTMF...");
|
||||
MM_BASE_CALL_GET_CLASS (ctx->self)->send_dtmf (ctx->self, ctx->dtmf,
|
||||
(GAsyncReadyCallback)handle_send_dtmf_ready,
|
||||
ctx);
|
||||
task = send_dtmf_task_new (ctx->self,
|
||||
ctx->dtmf,
|
||||
(GAsyncReadyCallback)handle_send_dtmf_ready,
|
||||
ctx,
|
||||
&error);
|
||||
if (!task) {
|
||||
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
||||
handle_send_dtmf_context_free (ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
g_queue_push_tail (ctx->self->priv->dtmf_queue, task);
|
||||
if (g_queue_get_length (ctx->self->priv->dtmf_queue) == 1)
|
||||
send_dtmf_task_step_next (task);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -1116,6 +1364,8 @@ mm_base_call_init (MMBaseCall *self)
|
||||
/* Setup authorization provider */
|
||||
self->priv->authp = mm_auth_provider_get ();
|
||||
self->priv->authp_cancellable = g_cancellable_new ();
|
||||
|
||||
self->priv->dtmf_queue = g_queue_new ();
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1123,6 +1373,9 @@ finalize (GObject *object)
|
||||
{
|
||||
MMBaseCall *self = MM_BASE_CALL (object);
|
||||
|
||||
g_assert (g_queue_get_length (self->priv->dtmf_queue) == 0);
|
||||
g_queue_free (g_steal_pointer (&self->priv->dtmf_queue));
|
||||
|
||||
g_assert (!self->priv->start_cancellable);
|
||||
g_free (self->priv->path);
|
||||
|
||||
@@ -1153,6 +1406,9 @@ dispose (GObject *object)
|
||||
g_cancellable_cancel (self->priv->authp_cancellable);
|
||||
g_clear_object (&self->priv->authp_cancellable);
|
||||
|
||||
g_queue_foreach (self->priv->dtmf_queue, (GFunc) send_dtmf_task_cancel, NULL);
|
||||
g_queue_clear (self->priv->dtmf_queue);
|
||||
|
||||
G_OBJECT_CLASS (mm_base_call_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
|
||||
+16
-8
@@ -90,14 +90,22 @@ struct _MMBaseCallClass {
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
/* Send a DTMF tone */
|
||||
void (* send_dtmf) (MMBaseCall *self,
|
||||
const gchar *dtmf,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean (* send_dtmf_finish) (MMBaseCall *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
/* DTMF tone handling */
|
||||
void (* send_dtmf) (MMBaseCall *self,
|
||||
const gchar *dtmf,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean (* send_dtmf_finish) (MMBaseCall *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
void (* stop_dtmf) (MMBaseCall *self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean (* stop_dtmf_finish) (MMBaseCall *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
};
|
||||
|
||||
GType mm_base_call_get_type (void);
|
||||
|
||||
+104
-89
@@ -331,22 +331,41 @@ call_hangup (MMBaseCall *self,
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Send DTMF character */
|
||||
/* DTMF handling */
|
||||
|
||||
typedef struct {
|
||||
QmiClient *client;
|
||||
guint8 call_id;
|
||||
} SendDtmfContext;
|
||||
|
||||
static void
|
||||
send_dtmf_context_free (SendDtmfContext *ctx)
|
||||
static gboolean
|
||||
get_client_and_call_id (MMCallQmi *self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data,
|
||||
QmiClient **client,
|
||||
guint *call_id)
|
||||
{
|
||||
g_clear_object (&ctx->client);
|
||||
g_slice_free (SendDtmfContext, ctx);
|
||||
g_return_val_if_fail (client, FALSE);
|
||||
g_return_val_if_fail (call_id, FALSE);
|
||||
|
||||
/* Ensure Voice client */
|
||||
if (!ensure_qmi_client (self,
|
||||
QMI_SERVICE_VOICE, client,
|
||||
callback, user_data))
|
||||
return FALSE;
|
||||
|
||||
*call_id = mm_base_call_get_index (MM_BASE_CALL (self));
|
||||
if (*call_id == 0) {
|
||||
g_task_report_new_error (self,
|
||||
callback,
|
||||
user_data,
|
||||
(gpointer) __func__,
|
||||
MM_CORE_ERROR,
|
||||
MM_CORE_ERROR_INVALID_ARGS,
|
||||
"Invalid call index");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
call_send_dtmf_finish (MMBaseCall *self,
|
||||
call_stop_dtmf_finish (MMBaseCall *call,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
@@ -366,35 +385,59 @@ voice_stop_continuous_dtmf_ready (QmiClientVoice *client,
|
||||
g_prefix_error (&error, "QMI operation failed: ");
|
||||
g_task_return_error (task, error);
|
||||
} else if (!qmi_message_voice_stop_continuous_dtmf_output_get_result (output, &error)) {
|
||||
g_prefix_error (&error, "Couldn't send DTMF character: ");
|
||||
g_prefix_error (&error, "Couldn't stop DTMF character: ");
|
||||
g_task_return_error (task, error);
|
||||
} else {
|
||||
g_task_return_boolean (task, TRUE);
|
||||
}
|
||||
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
voice_stop_continuous_dtmf (GTask *task)
|
||||
static void
|
||||
call_stop_dtmf (MMBaseCall *_self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
SendDtmfContext *ctx;
|
||||
GError *error = NULL;
|
||||
g_autoptr (QmiMessageVoiceStopContinuousDtmfInput) input = NULL;
|
||||
MMCallQmi *self = MM_CALL_QMI (_self);
|
||||
GTask *task;
|
||||
QmiClient *client = NULL;
|
||||
guint call_id = 0;
|
||||
GError *error = NULL;
|
||||
g_autoptr (QmiMessageVoiceStopContinuousDtmfInput) input = NULL;
|
||||
|
||||
ctx = g_task_get_task_data (task);
|
||||
if (!get_client_and_call_id (self,
|
||||
callback,
|
||||
user_data,
|
||||
&client,
|
||||
&call_id))
|
||||
return;
|
||||
|
||||
task = g_task_new (self, NULL, callback, user_data);
|
||||
|
||||
input = qmi_message_voice_stop_continuous_dtmf_input_new ();
|
||||
qmi_message_voice_stop_continuous_dtmf_input_set_data (input, ctx->call_id, &error);
|
||||
if (!qmi_message_voice_stop_continuous_dtmf_input_set_data (input,
|
||||
call_id,
|
||||
&error)) {
|
||||
g_task_return_error (task, error);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
qmi_client_voice_stop_continuous_dtmf (QMI_CLIENT_VOICE (ctx->client),
|
||||
/* Stop sending DTMF tone */
|
||||
qmi_client_voice_stop_continuous_dtmf (QMI_CLIENT_VOICE (client),
|
||||
input,
|
||||
5,
|
||||
NULL,
|
||||
(GAsyncReadyCallback) voice_stop_continuous_dtmf_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
static gboolean
|
||||
call_send_dtmf_finish (MMBaseCall *call,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -402,85 +445,55 @@ voice_start_continuous_dtmf_ready (QmiClientVoice *client,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
MMCallQmi *self;
|
||||
g_autoptr(QmiMessageVoiceStartContinuousDtmfOutput) output = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
self = g_task_get_source_object (task);
|
||||
g_autoptr (QmiMessageVoiceStartContinuousDtmfOutput) output = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
output = qmi_client_voice_start_continuous_dtmf_finish (client, res, &error);
|
||||
if (!output) {
|
||||
g_prefix_error (&error, "QMI operation failed: ");
|
||||
g_task_return_error (task, error);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!qmi_message_voice_start_continuous_dtmf_output_get_result (output, &error)) {
|
||||
} else if (!qmi_message_voice_start_continuous_dtmf_output_get_result (output, &error)) {
|
||||
g_prefix_error (&error, "Couldn't send DTMF character: ");
|
||||
g_task_return_error (task, error);
|
||||
} else {
|
||||
g_task_return_boolean (task, TRUE);
|
||||
}
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static void
|
||||
call_send_dtmf (MMBaseCall *_self,
|
||||
const gchar *dtmf,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
MMCallQmi *self = MM_CALL_QMI (_self);
|
||||
GTask *task;
|
||||
QmiClient *client = NULL;
|
||||
guint call_id = 0;
|
||||
GError *error = NULL;
|
||||
g_autoptr (QmiMessageVoiceStartContinuousDtmfInput) input = NULL;
|
||||
|
||||
if (!get_client_and_call_id (self,
|
||||
callback,
|
||||
user_data,
|
||||
&client,
|
||||
&call_id))
|
||||
return;
|
||||
|
||||
task = g_task_new (self, NULL, callback, user_data);
|
||||
|
||||
input = qmi_message_voice_start_continuous_dtmf_input_new ();
|
||||
if (!qmi_message_voice_start_continuous_dtmf_input_set_data (input,
|
||||
call_id,
|
||||
dtmf[0],
|
||||
&error)) {
|
||||
g_task_return_error (task, error);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Disable DTMF press after DTMF tone duration elapses */
|
||||
g_timeout_add (mm_base_call_get_dtmf_tone_duration (MM_BASE_CALL (self)),
|
||||
(GSourceFunc) voice_stop_continuous_dtmf,
|
||||
task);
|
||||
}
|
||||
|
||||
static void
|
||||
call_send_dtmf (MMBaseCall *self,
|
||||
const gchar *dtmf,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
GError *error = NULL;
|
||||
SendDtmfContext *ctx;
|
||||
QmiClient *client = NULL;
|
||||
guint8 call_id;
|
||||
g_autoptr (QmiMessageVoiceStartContinuousDtmfInput) input = NULL;
|
||||
|
||||
/* Ensure Voice client */
|
||||
if (!ensure_qmi_client (MM_CALL_QMI (self),
|
||||
QMI_SERVICE_VOICE, &client,
|
||||
callback, user_data))
|
||||
return;
|
||||
|
||||
task = g_task_new (self, NULL, callback, user_data);
|
||||
|
||||
call_id = mm_base_call_get_index (self);
|
||||
if (call_id == 0) {
|
||||
g_task_return_new_error (task,
|
||||
MM_CORE_ERROR,
|
||||
MM_CORE_ERROR_INVALID_ARGS,
|
||||
"Invalid call index");
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx = g_slice_new0 (SendDtmfContext);
|
||||
ctx->client = g_object_ref (client);
|
||||
ctx->call_id = call_id;
|
||||
|
||||
g_task_set_task_data (task, ctx, (GDestroyNotify) send_dtmf_context_free);
|
||||
|
||||
/* Send DTMF character as ASCII number */
|
||||
input = qmi_message_voice_start_continuous_dtmf_input_new ();
|
||||
qmi_message_voice_start_continuous_dtmf_input_set_data (input,
|
||||
call_id,
|
||||
(guint8) dtmf[0],
|
||||
&error);
|
||||
if (error) {
|
||||
g_task_return_new_error (task,
|
||||
MM_CORE_ERROR,
|
||||
MM_CORE_ERROR_INVALID_ARGS,
|
||||
"DTMF data build failed");
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
qmi_client_voice_start_continuous_dtmf (QMI_CLIENT_VOICE (client),
|
||||
input,
|
||||
5,
|
||||
@@ -548,4 +561,6 @@ mm_call_qmi_class_init (MMCallQmiClass *klass)
|
||||
base_call_class->hangup_finish = call_hangup_finish;
|
||||
base_call_class->send_dtmf = call_send_dtmf;
|
||||
base_call_class->send_dtmf_finish = call_send_dtmf_finish;
|
||||
base_call_class->stop_dtmf = call_stop_dtmf;
|
||||
base_call_class->stop_dtmf_finish = call_stop_dtmf_finish;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user