diff --git a/build/android/liblinphone_tester.mk b/build/android/liblinphone_tester.mk
index b23a381bd..e4387132f 100644
--- a/build/android/liblinphone_tester.mk
+++ b/build/android/liblinphone_tester.mk
@@ -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) \
diff --git a/build/wp8/LibLinphoneTester-native/LibLinphoneTester-native.vcxproj b/build/wp8/LibLinphoneTester-native/LibLinphoneTester-native.vcxproj
index 021ab6328..6bc245f9f 100644
--- a/build/wp8/LibLinphoneTester-native/LibLinphoneTester-native.vcxproj
+++ b/build/wp8/LibLinphoneTester-native/LibLinphoneTester-native.vcxproj
@@ -107,6 +107,8 @@
+
+
true
diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c
index c0afd17a5..8d5d82c5f 100644
--- a/coreapi/linphonecall.c
+++ b/coreapi/linphonecall.c
@@ -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;
+ }
+}
diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c
index 0832fc947..122657a14 100644
--- a/coreapi/linphonecore.c
+++ b/coreapi/linphonecore.c
@@ -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){
diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h
index c95adbc4f..00cd466a3 100644
--- a/coreapi/linphonecore.h
+++ b/coreapi/linphonecore.h
@@ -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);
diff --git a/coreapi/private.h b/coreapi/private.h
index e42d6c177..ac274545a 100644
--- a/coreapi/private.h
+++ b/coreapi/private.h
@@ -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;
diff --git a/oRTP b/oRTP
index 1bfd94cea..486604095 160000
--- a/oRTP
+++ b/oRTP
@@ -1 +1 @@
-Subproject commit 1bfd94cea8e62972162f926db3c6ffedca68ae22
+Subproject commit 486604095db3f2ed4cb3fa8d30c45baf191d79e0
diff --git a/tester/Makefile.am b/tester/Makefile.am
index 7fb08bb0f..ec5366726 100644
--- a/tester/Makefile.am
+++ b/tester/Makefile.am
@@ -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)
diff --git a/tester/call_tester.c b/tester/call_tester.c
index f67ee74ab..a881e709d 100644
--- a/tester/call_tester.c
+++ b/tester/call_tester.c
@@ -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);
diff --git a/tester/dtmf_tester.c b/tester/dtmf_tester.c
new file mode 100644
index 000000000..fa627cfd4
--- /dev/null
+++ b/tester/dtmf_tester.c
@@ -0,0 +1,148 @@
+/*
+ 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 .
+*/
+
+#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
+};
diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h
index 464926707..4bbe1fa3a 100644
--- a/tester/liblinphone_tester.h
+++ b/tester/liblinphone_tester.h
@@ -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);
diff --git a/tester/quality_reporting_tester.c b/tester/quality_reporting_tester.c
index 505949a74..1190f188f 100644
--- a/tester/quality_reporting_tester.c
+++ b/tester/quality_reporting_tester.c
@@ -17,7 +17,6 @@
*/
#include
-#include "CUnit/Basic.h"
#include "linphonecore.h"
#include "private.h"
#include "liblinphone_tester.h"
diff --git a/tester/tester.c b/tester/tester.c
index 994dc67c4..aa0bd3e8b 100644
--- a/tester/tester.c
+++ b/tester/tester.c
@@ -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) {