Add linphone_call_send_dtmfs method to allow sending a DTMF sequence instead...

Add linphone_call_send_dtmfs method to allow sending a DTMF sequence instead of a single one, and add a test suite
parent 4caf4757
......@@ -15,7 +15,8 @@ common_SRC_FILES := \
remote_provisioning_tester.c \
quality_reporting_tester.c \
transport_tester.c \
player_tester.c
player_tester.c \
dtmf_tester.c
common_C_INCLUDES += \
$(LOCAL_PATH) \
......
......@@ -107,6 +107,8 @@
<ClCompile Include="..\..\..\tester\stun_tester.c" />
<ClCompile Include="..\..\..\tester\tester.c" />
<ClCompile Include="..\..\..\tester\transport_tester.c" />
<ClCompile Include="..\..\..\tester\dtmf_tester.c" />
<ClCompile Include="linphone-tester-native.cpp">
<CompileAsWinRT>true</CompileAsWinRT>
</ClCompile>
......
......@@ -979,6 +979,11 @@ void linphone_call_set_state_base(LinphoneCall *call, LinphoneCallState cstate,
linphone_reporting_call_state_updated(call);
/*cancelling DTMF sequence, if any*/
if (cstate!=LinphoneCallStreamsRunning&&call->dtmfs_timer!=NULL){
linphone_call_cancel_dtmfs(call);
}
if (cstate==LinphoneCallReleased){
if (call->op!=NULL) {
/*transfer the last error so that it can be obtained even in Released state*/
......@@ -3124,3 +3129,76 @@ void linphone_call_set_new_params(LinphoneCall *call, const LinphoneCallParams *
if (call->params) linphone_call_params_unref(call->params);
call->params=cp;
}
static int send_dtmf_handler(void *data, unsigned int revents){
LinphoneCall *call = (LinphoneCall*)data;
/*By default we send DTMF RFC2833 if we do not have enabled SIP_INFO but we can also send RFC2833 and SIP_INFO*/
if (linphone_core_get_use_rfc2833_for_dtmf(call->core)!=0 || linphone_core_get_use_info_for_dtmf(call->core)==0)
{
/* In Band DTMF */
if (call->audiostream!=NULL){
audio_stream_send_dtmf(call->audiostream,*call->dtmf_sequence);
}
else
{
ms_error("Cannot send RFC2833 DTMF when we are not in communication.");
return FALSE;
}
}
if (linphone_core_get_use_info_for_dtmf(call->core)!=0){
/* Out of Band DTMF (use INFO method) */
sal_call_send_dtmf(call->op,*call->dtmf_sequence);
}
/*this check is needed because linphone_call_send_dtmf does not set the timer since its a single character*/
if (call->dtmfs_timer!=NULL) {
memmove(call->dtmf_sequence, call->dtmf_sequence+1, strlen(call->dtmf_sequence));
}
/* continue only if the dtmf sequence is not empty*/
if (call->dtmf_sequence!=NULL&&*call->dtmf_sequence!='\0') {
return TRUE;
} else {
linphone_call_cancel_dtmfs(call);
return FALSE;
}
}
int linphone_call_send_dtmf(LinphoneCall *call,char dtmf){
if (call==NULL){
ms_warning("linphone_call_send_dtmf(): invalid call, canceling DTMF.");
return -1;
}
call->dtmf_sequence = &dtmf;
send_dtmf_handler(call,0);
call->dtmf_sequence = NULL;
return 0;
}
int linphone_call_send_dtmfs(LinphoneCall *call,char *dtmfs) {
if (call==NULL){
ms_warning("linphone_call_send_dtmfs(): invalid call, canceling DTMF sequence.");
return -1;
}
if (call->dtmfs_timer!=NULL){
ms_warning("linphone_call_send_dtmfs(): a DTMF sequence is already in place, canceling DTMF sequence.");
return -2;
}
if (dtmfs != NULL) {
int delay_ms = lp_config_get_int(call->core->config,"net","dtmf_delay_ms",200);
call->dtmf_sequence = ms_strdup(dtmfs);
call->dtmfs_timer = sal_create_timer(call->core->sal, send_dtmf_handler, call, delay_ms, "DTMF sequence timer");
}
return 0;
}
void linphone_call_cancel_dtmfs(LinphoneCall *call) {
/*nothing to do*/
if (!call || !call->dtmfs_timer) return;
sal_cancel_timer(call->core->sal, call->dtmfs_timer);
belle_sip_object_unref(call->dtmfs_timer);
call->dtmfs_timer = NULL;
if (call->dtmf_sequence != NULL) {
ms_free(call->dtmf_sequence);
call->dtmf_sequence = NULL;
}
}
......@@ -4780,38 +4780,10 @@ bool_t linphone_core_agc_enabled(const LinphoneCore *lc){
return lc->sound_conf.agc;
}
/**
* Send the specified dtmf.
*
* @ingroup media_parameters
* This function only works during calls. The dtmf is automatically played to the user.
* @param lc The LinphoneCore object
* @param dtmf The dtmf name specified as a char, such as '0', '#' etc...
*
**/
void linphone_core_send_dtmf(LinphoneCore *lc, char dtmf)
{
LinphoneCall *call=linphone_core_get_current_call(lc);
if (call==NULL){
ms_warning("linphone_core_send_dtmf(): no active call");
return;
}
/*By default we send DTMF RFC2833 if we do not have enabled SIP_INFO but we can also send RFC2833 and SIP_INFO*/
if (linphone_core_get_use_rfc2833_for_dtmf(lc)!=0 || linphone_core_get_use_info_for_dtmf(lc)==0)
{
/* In Band DTMF */
if (call->audiostream!=NULL){
audio_stream_send_dtmf(call->audiostream,dtmf);
}
else
{
ms_error("we cannot send RFC2833 dtmf when we are not in communication");
}
}
if (linphone_core_get_use_info_for_dtmf(lc)!=0){
/* Out of Band DTMF (use INFO method) */
sal_call_send_dtmf(call->op,dtmf);
}
linphone_call_send_dtmf(call, dtmf);
}
void linphone_core_set_stun_server(LinphoneCore *lc, const char *server){
......
......@@ -731,6 +731,37 @@ LINPHONE_PUBLIC void linphone_call_start_recording(LinphoneCall *call);
LINPHONE_PUBLIC void linphone_call_stop_recording(LinphoneCall *call);
LINPHONE_PUBLIC LinphonePlayer * linphone_call_get_player(LinphoneCall *call);
LINPHONE_PUBLIC bool_t linphone_call_media_in_progress(LinphoneCall *call);
/**
* Send the specified dtmf.
*
* The dtmf is automatically played to the user.
* @param call The LinphoneCall object
* @param dtmf The dtmf name specified as a char, such as '0', '#' etc...
* @returns 0 if successful, -1 on error.
**/
LINPHONE_PUBLIC int linphone_call_send_dtmf(LinphoneCall *lc,char dtmf);
/**
* Send a list of dtmf.
*
* The dtmfs are automatically sent to remote, separated by some needed customizable delay.
* Sending is canceled if the call state changes to something not LinphoneCallStreamsRunning.
* @param call The LinphoneCall object
* @param dtmfs A dtmf sequence such as '123#123123'
* @returns -2 if there is already a DTMF sequence, -1 if call is not ready, 0 otherwise.
**/
LINPHONE_PUBLIC int linphone_call_send_dtmfs(LinphoneCall *call,char *dtmfs);
/**
* Stop current DTMF sequence sending.
*
* Please note that some DTMF could be already sent,
* depending on when this function call is delayed from #linphone_call_send_dtmfs. This
* function will be automatically called if call state change to anything but LinphoneCallStreamsRunning.
*
* @param call The LinphoneCall object
**/
LINPHONE_PUBLIC void linphone_call_cancel_dtmfs(LinphoneCall *call);
/**
* Return TRUE if this call is currently part of a conference
......@@ -2049,6 +2080,16 @@ LINPHONE_PUBLIC LinphoneCallParams *linphone_core_create_call_params(LinphoneCor
LINPHONE_PUBLIC LinphoneCall *linphone_core_get_call_by_remote_address(LinphoneCore *lc, const char *remote_address);
/**
* Send the specified dtmf.
*
* @ingroup media_parameters
* @deprecated Use #linphone_call_send_dtmf instead.
* This function only works during calls. The dtmf is automatically played to the user.
* @param lc The LinphoneCore object
* @param dtmf The dtmf name specified as a char, such as '0', '#' etc...
*
**/
LINPHONE_PUBLIC void linphone_core_send_dtmf(LinphoneCore *lc,char dtmf);
LINPHONE_PUBLIC int linphone_core_set_primary_contact(LinphoneCore *lc, const char *contact);
......
......@@ -239,6 +239,9 @@ struct _LinphoneCall
int localdesc_changed;/*not a boolean, contains a mask representing changes*/
LinphonePlayer *player;
char *dtmf_sequence; /*DTMF sequence needed to be sent using #dtmfs_timer*/
belle_sip_source_t *dtmfs_timer; /*DTMF timer needed to send a DTMF sequence*/
bool_t refer_pending;
bool_t expect_media_in_ack;
bool_t audio_muted;
......
oRTP @ 48660409
Subproject commit 1bfd94cea8e62972162f926db3c6ffedca68ae22
Subproject commit 486604095db3f2ed4cb3fa8d30c45baf191d79e0
......@@ -24,7 +24,8 @@ liblinphonetester_la_SOURCES = tester.c \
quality_reporting_tester.c \
log_collection_tester.c \
transport_tester.c \
player_tester.c
player_tester.c \
dtmf_tester.c
liblinphonetester_la_LDFLAGS= -no-undefined
liblinphonetester_la_LIBADD= ../coreapi/liblinphone.la $(CUNIT_LIBS)
......
......@@ -340,8 +340,8 @@ static void simple_call(void) {
}
}
}
liblinphone_tester_check_rtcp(marie,pauline);
end_call(marie,pauline);
linphone_core_manager_destroy(marie);
......@@ -365,13 +365,13 @@ static void direct_call_over_ipv6(){
linphone_core_enable_ipv6(pauline->lc,TRUE);
linphone_core_set_default_proxy_config(marie->lc,NULL);
linphone_core_invite(marie->lc,"sip:[::1]:12002;transport=tcp");
CU_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&marie->stat.number_of_LinphoneCallOutgoingRinging,1));
CU_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&pauline->stat.number_of_LinphoneCallIncomingReceived,1));
linphone_core_accept_call(pauline->lc,linphone_core_get_current_call(pauline->lc));
CU_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&marie->stat.number_of_LinphoneCallStreamsRunning,1));
CU_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&pauline->stat.number_of_LinphoneCallStreamsRunning,1));
liblinphone_tester_check_rtcp(marie,pauline);
end_call(marie,pauline);
linphone_core_manager_destroy(marie);
......
/*
liblinphone_tester - liblinphone test suite
Copyright (C) 2013 Belledonne Communications SARL
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, see <http://www.gnu.org/licenses/>.
*/
#include "liblinphone_tester.h"
#include "private.h"
LinphoneCoreManager* marie;
LinphoneCoreManager* pauline;
LinphoneCall *marie_call;
void dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf) {
stats* counters = get_stats(lc);
char** dst = &counters->dtmf_list_received;
*dst = *dst ?
ms_strcat_printf(*dst, "%c", dtmf)
: ms_strdup_printf("%c", dtmf);
}
void send_dtmf_base(bool_t use_rfc2833, bool_t use_sipinfo, char dtmf, char* dtmf_seq) {
char* expected = NULL;
marie = linphone_core_manager_new( "marie_rc");
pauline = linphone_core_manager_new( "pauline_rc");
linphone_core_set_use_rfc2833_for_dtmf(marie->lc, use_rfc2833);
linphone_core_set_use_info_for_dtmf(marie->lc, use_sipinfo);
linphone_core_set_use_rfc2833_for_dtmf(pauline->lc, use_rfc2833);
linphone_core_set_use_info_for_dtmf(pauline->lc, use_sipinfo);
CU_ASSERT_TRUE(call(pauline,marie));
marie_call = linphone_core_get_current_call(marie->lc);
if (dtmf != '\0') {
linphone_call_send_dtmf(marie_call, dtmf);
/*wait for the DTMF to be received from pauline*/
wait_for_until(marie->lc, pauline->lc, NULL, 0, 1000);
expected = ms_strdup_printf("%c", dtmf);
}
if (dtmf_seq != NULL) {
int dtmf_delay_ms = lp_config_get_int(marie_call->core->config,"net","dtmf_delay_ms",200);
linphone_call_send_dtmfs(marie_call, dtmf_seq);
/*wait for the DTMF sequence to be received from pauline*/
wait_for_until(marie->lc, pauline->lc, NULL, 0, 1000 + dtmf_delay_ms * strlen(dtmf_seq));
expected = (dtmf!='\0')?ms_strdup_printf("%c%s",dtmf,dtmf_seq):ms_strdup(dtmf_seq);
}
if (expected != NULL) {
CU_ASSERT_PTR_NOT_NULL(pauline->stat.dtmf_list_received);
if (pauline->stat.dtmf_list_received) {
CU_ASSERT_STRING_EQUAL(pauline->stat.dtmf_list_received, expected);
}
ms_free(expected);
} else {
CU_ASSERT_PTR_NULL(pauline->stat.dtmf_list_received);
}
}
void send_dtmf_cleanup() {
CU_ASSERT_PTR_NULL(marie_call->dtmfs_timer);
CU_ASSERT_PTR_NULL(marie_call->dtmf_sequence);
/*just to sleep*/
linphone_core_terminate_all_calls(pauline->lc);
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallEnd,1));
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallEnd,1));
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
}
static void send_dtmf_rfc2833() {
send_dtmf_base(TRUE,FALSE,'1',NULL);
send_dtmf_cleanup();
}
static void send_dtmf_sip_info() {
send_dtmf_base(FALSE,TRUE,'#',NULL);
send_dtmf_cleanup();
}
static void send_dtmfs_sequence_rfc2833() {
send_dtmf_base(TRUE,FALSE,'\0',"1230#");
send_dtmf_cleanup();
}
static void send_dtmfs_sequence_sip_info() {
send_dtmf_base(FALSE,TRUE,'\0',"1230#");
send_dtmf_cleanup();
}
static void send_dtmfs_sequence_not_ready() {
marie = linphone_core_manager_new( "marie_rc");
CU_ASSERT_EQUAL(linphone_call_send_dtmfs(linphone_core_get_current_call(marie->lc), "123"), -1);
linphone_core_manager_destroy(marie);
}
static void send_dtmfs_sequence_call_state_changed() {
send_dtmf_base(FALSE,TRUE,'\0',NULL);
/*very long DTMF(around 4 sec to be sent)*/
linphone_call_send_dtmfs(marie_call, "123456789123456789");
/*just after, change call state, and expect DTMF to be canceled*/
linphone_core_pause_call(marie_call->core,marie_call);
CU_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&marie->stat.number_of_LinphoneCallPausing,1));
CU_ASSERT_TRUE(wait_for(marie->lc,pauline->lc,&marie->stat.number_of_LinphoneCallPaused,1));
/*wait a few time to ensure that no DTMF are received*/
wait_for_until(marie->lc, pauline->lc, NULL, 0, 1000);
CU_ASSERT_PTR_NULL(pauline->stat.dtmf_list_received);
send_dtmf_cleanup();
}
test_t dtmf_tests[] = {
{ "Send DTMF using RFC2833",send_dtmf_rfc2833},
{ "Send DTMF using SIP INFO",send_dtmf_sip_info},
{ "Send DTMF sequence using RFC2833",send_dtmfs_sequence_rfc2833},
{ "Send DTMF sequence using SIP INFO",send_dtmfs_sequence_sip_info},
{ "DTMF sequence not sent if invalid call",send_dtmfs_sequence_not_ready},
{ "DTMF sequence canceled if call state changed",send_dtmfs_sequence_call_state_changed},
};
test_suite_t dtmf_test_suite = {
"DTMF",
NULL,
NULL,
sizeof(dtmf_tests) / sizeof(dtmf_tests[0]),
dtmf_tests
};
......@@ -61,6 +61,7 @@ extern test_suite_t quality_reporting_test_suite;
extern test_suite_t log_collection_test_suite;
extern test_suite_t transport_test_suite;
extern test_suite_t player_test_suite;
extern test_suite_t dtmf_test_suite;
extern int liblinphone_tester_nb_test_suites(void);
......@@ -202,6 +203,8 @@ typedef struct _stats {
int number_of_NetworkReachableFalse;
int number_of_player_eof;
LinphoneChatMessage* last_received_chat_message;
char * dtmf_list_received;
}stats;
typedef struct _LinphoneCoreManager {
......@@ -242,6 +245,7 @@ void linphone_publish_state_changed(LinphoneCore *lc, LinphoneEvent *ev, Linphon
void linphone_notify_received(LinphoneCore *lc, LinphoneEvent *lev, const char *eventname, const LinphoneContent *content);
void linphone_configuration_status(LinphoneCore *lc, LinphoneConfiguringState status, const char *message);
void linphone_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t on, const char *authentication_token);
void dtmf_received(LinphoneCore *lc, LinphoneCall *call, int dtmf);
LinphoneAddress * create_linphone_address(const char * domain);
bool_t wait_for(LinphoneCore* lc_1, LinphoneCore* lc_2,int* counter,int value);
......
......@@ -17,7 +17,6 @@
*/
#include <stdio.h>
#include "CUnit/Basic.h"
#include "linphonecore.h"
#include "private.h"
#include "liblinphone_tester.h"
......
......@@ -225,6 +225,7 @@ LinphoneCoreManager* linphone_core_manager_new2(const char* rc_file, int check_f
mgr->v_table.configuring_status=linphone_configuration_status;
mgr->v_table.call_encryption_changed=linphone_call_encryption_changed;
mgr->v_table.network_reachable=network_reachable;
mgr->v_table.dtmf_received=dtmf_received;
reset_counters(&mgr->stat);
if (rc_file) rc_path = ms_strdup_printf("rcfiles/%s", rc_file);
......@@ -387,11 +388,10 @@ void liblinphone_tester_init(void) {
add_test_suite(&flexisip_test_suite);
add_test_suite(&remote_provisioning_test_suite);
add_test_suite(&quality_reporting_test_suite);
#ifndef ANDROID
add_test_suite(&log_collection_test_suite);
#endif
add_test_suite(&transport_test_suite);
add_test_suite(&player_test_suite);
add_test_suite(&dtmf_test_suite);
}
void liblinphone_tester_uninit(void) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment