summaryrefslogtreecommitdiff
path: root/pjsip-apps
diff options
context:
space:
mode:
authorNanang Izzuddin <nanang@teluu.com>2008-06-20 21:45:50 +0000
committerNanang Izzuddin <nanang@teluu.com>2008-06-20 21:45:50 +0000
commit28789d42d78ebffb91dac00facc6bee334d8dd8f (patch)
tree0997bd66640798a115e32b6d6b0ecd7bf9b9c377 /pjsip-apps
parentdcadef7fc04bb899333fb0ab472679c8ac280e09 (diff)
More ticket #543: added PESQ test
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2038 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip-apps')
-rw-r--r--pjsip-apps/src/pjsua/pjsua_app.c77
-rw-r--r--pjsip-apps/src/test-pjsua/mod_pesq.py130
-rw-r--r--pjsip-apps/src/test-pjsua/scripts-pesq/100_defaults.py12
3 files changed, 215 insertions, 4 deletions
diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c
index 16bc9dee..aef6b535 100644
--- a/pjsip-apps/src/pjsua/pjsua_app.c
+++ b/pjsip-apps/src/pjsua/pjsua_app.c
@@ -86,6 +86,8 @@ static struct app_config
pjsua_player_id wav_id;
pjsua_conf_port_id wav_port;
pj_bool_t auto_play;
+ pj_bool_t auto_play_hangup;
+ pj_timer_entry auto_hangup_timer;
pj_bool_t auto_loop;
pj_bool_t auto_conf;
pj_str_t rec_file;
@@ -444,7 +446,7 @@ static pj_status_t parse_args(int argc, char *argv[],
OPT_100REL, OPT_USE_IMS, OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
OPT_NAMESERVER, OPT_STUN_DOMAIN, OPT_STUN_SRV,
OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
- OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP,
+ OPT_AUTO_ANSWER, OPT_AUTO_PLAY, OPT_AUTO_PLAY_HANGUP, OPT_AUTO_LOOP,
OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_SND_CLOCK_RATE, OPT_STEREO,
OPT_USE_ICE, OPT_USE_SRTP, OPT_SRTP_SECURE,
OPT_USE_TURN,OPT_ICE_NO_HOST, OPT_TURN_SRV, OPT_TURN_TCP,
@@ -501,8 +503,8 @@ static pj_status_t parse_args(int argc, char *argv[],
{ "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG},
{ "no-presence", 0, 0, OPT_NO_PRESENCE},
{ "auto-answer",1, 0, OPT_AUTO_ANSWER},
- { "auto-hangup",1, 0, OPT_AUTO_HANGUP},
{ "auto-play", 0, 0, OPT_AUTO_PLAY},
+ { "auto-play-hangup",0, 0, OPT_AUTO_PLAY_HANGUP},
{ "auto-rec", 0, 0, OPT_AUTO_REC},
{ "auto-loop", 0, 0, OPT_AUTO_LOOP},
{ "auto-conf", 0, 0, OPT_AUTO_CONF},
@@ -855,6 +857,10 @@ static pj_status_t parse_args(int argc, char *argv[],
cfg->auto_play = 1;
break;
+ case OPT_AUTO_PLAY_HANGUP:
+ cfg->auto_play_hangup = 1;
+ break;
+
case OPT_AUTO_REC:
cfg->auto_rec = 1;
break;
@@ -1913,6 +1919,13 @@ static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
pjsip_endpt_cancel_timer(endpt, &cd->timer);
}
+ /* Rewind play file when hangup automatically,
+ * since file is not looped
+ */
+ if (app_config.auto_play_hangup)
+ pjsua_player_set_pos(app_config.wav_id, 0);
+
+
PJ_LOG(3,(THIS_FILE, "Call %d is DISCONNECTED [reason=%d (%s)]",
call_id,
call_info.last_status,
@@ -2116,7 +2129,9 @@ static void on_call_media_state(pjsua_call_id call_id)
}
/* Stream a file, if desired */
- if (app_config.auto_play && app_config.wav_port != PJSUA_INVALID_ID) {
+ if ((app_config.auto_play || app_config.auto_play_hangup) &&
+ app_config.wav_port != PJSUA_INVALID_ID)
+ {
pjsua_conf_connect(app_config.wav_port, call_info.conf_slot);
connect_sound = PJ_FALSE;
}
@@ -2387,6 +2402,44 @@ static void print_acc_status(int acc_id)
info.online_status_text.ptr);
}
+/* Playfile done notification, set timer to hangup calls */
+pj_status_t on_playfile_done(pjmedia_port *port, void *usr_data)
+{
+ pj_time_val delay;
+
+ PJ_UNUSED_ARG(port);
+ PJ_UNUSED_ARG(usr_data);
+
+ /* Just rewind WAV when it is played outside of call */
+ if (pjsua_call_get_count() == 0) {
+ pjsua_player_set_pos(app_config.wav_id, 0);
+ return PJ_SUCCESS;
+ }
+
+ /* Timer is already active */
+ if (app_config.auto_hangup_timer.id == 1)
+ return PJ_SUCCESS;
+
+ app_config.auto_hangup_timer.id = 1;
+ delay.sec = 0;
+ delay.msec = 200; /* Give 200 ms before hangup */
+ pjsip_endpt_schedule_timer(pjsua_get_pjsip_endpt(),
+ &app_config.auto_hangup_timer,
+ &delay);
+
+ return PJ_SUCCESS;
+}
+
+/* Auto hangup timer callback */
+static void hangup_timeout_callback(pj_timer_heap_t *timer_heap,
+ struct pj_timer_entry *entry)
+{
+ PJ_UNUSED_ARG(timer_heap);
+ PJ_UNUSED_ARG(entry);
+
+ app_config.auto_hangup_timer.id = 0;
+ pjsua_call_hangup_all();
+}
/*
* Show a bit of help.
@@ -3760,8 +3813,12 @@ pj_status_t app_init(int argc, char *argv[])
/* Optionally registers WAV file */
for (i=0; i<app_config.wav_count; ++i) {
pjsua_player_id wav_id;
+ unsigned play_options = 0;
+
+ if (app_config.auto_play_hangup)
+ play_options |= PJMEDIA_FILE_NO_LOOP;
- status = pjsua_player_create(&app_config.wav_files[i], 0,
+ status = pjsua_player_create(&app_config.wav_files[i], play_options,
&wav_id);
if (status != PJ_SUCCESS)
goto on_error;
@@ -3769,6 +3826,18 @@ pj_status_t app_init(int argc, char *argv[])
if (app_config.wav_id == PJSUA_INVALID_ID) {
app_config.wav_id = wav_id;
app_config.wav_port = pjsua_player_get_conf_port(app_config.wav_id);
+ if (app_config.auto_play_hangup) {
+ pjmedia_port *port;
+
+ pjsua_player_get_port(app_config.wav_id, &port);
+ status = pjmedia_wav_player_set_eof_cb(port, NULL,
+ &on_playfile_done);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ pj_timer_entry_init(&app_config.auto_hangup_timer, 0, NULL,
+ &hangup_timeout_callback);
+ }
}
}
diff --git a/pjsip-apps/src/test-pjsua/mod_pesq.py b/pjsip-apps/src/test-pjsua/mod_pesq.py
new file mode 100644
index 00000000..3527dce9
--- /dev/null
+++ b/pjsip-apps/src/test-pjsua/mod_pesq.py
@@ -0,0 +1,130 @@
+# $Id$
+
+# Quality test of media calls.
+# - UA1 calls UA2
+# - UA1 plays a file until finished to be streamed to UA2
+# - UA2 records from stream
+# - Apply PESQ to played file (reference) and recorded file (degraded)
+#
+# File should be:
+# - naming: xxxxxx.CLOCK_RATE.wav, e.g: test1.8.wav
+# - clock-rate of those files can only be 8khz or 16khz
+
+import time
+import imp
+import sys
+import re
+import subprocess
+import inc_const as const
+
+from inc_cfg import *
+
+# Load configuration
+cfg_file = imp.load_source("cfg_file", sys.argv[2])
+
+# PESQ configs
+# PESQ_THRESHOLD specifies the minimum acceptable PESQ MOS value, so test can be declared successful
+PESQ = "tools/pesq.exe"
+PESQ_THRESHOLD = 1.0
+
+# UserData
+class mod_pesq_user_data:
+ # Sample rate option for PESQ
+ pesq_sample_rate_opt = ""
+ # Input/Reference filename
+ input_filename = ""
+ # Output/Degraded filename
+ output_filename = ""
+
+# Test body function
+def test_func(t, user_data):
+ # module debugging purpose
+ #user_data.pesq_sample_rate_opt = "+16000"
+ #user_data.input_filename = "wavs/input.16.wav"
+ #user_data.output_filename = "wavs/tmp.16.wav"
+ #return
+
+ ua1 = t.process[0]
+ ua2 = t.process[1]
+
+ # Get conference clock rate of UA2 for PESQ sample rate option
+ ua2.send("cl")
+ clock_rate_line = ua2.expect("Port \#00\[\d+KHz")
+ if (clock_rate_line == None):
+ raise TestError("Failed getting")
+ clock_rate = re.match("Port \#00\[(\d+)KHz", clock_rate_line).group(1)
+ user_data.pesq_sample_rate_opt = "+" + clock_rate + "000"
+
+ # Get input file name
+ ua1.sync_stdout()
+ ua1.send("dc")
+ line = ua1.expect(const.MEDIA_PLAY_FILE)
+ user_data.input_filename = re.compile(const.MEDIA_PLAY_FILE).match(line).group(1)
+
+ # Get output file name
+ ua2.sync_stdout()
+ ua2.send("dc")
+ line = ua2.expect(const.MEDIA_REC_FILE)
+ user_data.output_filename = re.compile(const.MEDIA_REC_FILE).match(line).group(1)
+
+ # Find appropriate clock rate for the input file
+ clock_rate = re.compile(".+(\.\d+\.wav)$").match(user_data.output_filename)
+ if (clock_rate==None):
+ raise TestError("Cannot compare input & output, incorrect output filename format")
+ user_data.input_filename = re.sub("\.\d+\.wav$", clock_rate.group(1), user_data.input_filename)
+
+ time.sleep(1)
+ ua1.sync_stdout()
+ ua2.sync_stdout()
+
+ # UA1 making call
+ ua1.send("m")
+ ua1.send(t.inst_params[1].uri)
+ ua1.expect(const.STATE_CALLING)
+
+ # Auto answer, auto play, auto hangup
+ # Just wait for call disconnected
+
+ if ua1.expect(const.STATE_CONFIRMED, False)==None:
+ raise TestError("Call failed")
+ ua2.expect(const.STATE_CONFIRMED)
+
+ while True:
+ line = ua2.proc.stdout.readline()
+ if line == "":
+ raise TestError(ua2.name + ": Premature EOF")
+ # Search for disconnected text
+ if re.search(const.STATE_DISCONNECTED, line) != None:
+ break
+
+
+# Post body function
+def post_func(t, user_data):
+ endpt = t.process[0]
+
+ # Execute PESQ
+ fullcmd = PESQ + " " + user_data.pesq_sample_rate_opt + " " + user_data.input_filename + " " + user_data.output_filename
+ endpt.trace("Popen " + fullcmd)
+ pesq_proc = subprocess.Popen(fullcmd, stdout=subprocess.PIPE, universal_newlines=True)
+ pesq_out = pesq_proc.communicate()
+
+ # Parse ouput
+ mo_pesq_out = re.compile("Prediction\s+:\s+PESQ_MOS\s+=\s+(.+)\s*").search(pesq_out[0])
+ if (mo_pesq_out == None):
+ raise TestError("Failed to fetch PESQ result")
+
+ # Evaluate the similarity value
+ pesq_res = mo_pesq_out.group(1)
+ if (pesq_res >= PESQ_THRESHOLD):
+ endpt.trace("Success, PESQ result=" + pesq_res)
+ else:
+ endpt.trace("Failed, PESQ result=" + pesq_res)
+ raise TestError("WAV seems to be degraded badly")
+
+
+# Here where it all comes together
+test = cfg_file.test_param
+test.test_func = test_func
+test.post_func = post_func
+test.user_data = mod_pesq_user_data()
+
diff --git a/pjsip-apps/src/test-pjsua/scripts-pesq/100_defaults.py b/pjsip-apps/src/test-pjsua/scripts-pesq/100_defaults.py
new file mode 100644
index 00000000..c8c1aa5e
--- /dev/null
+++ b/pjsip-apps/src/test-pjsua/scripts-pesq/100_defaults.py
@@ -0,0 +1,12 @@
+# $Id$
+#
+from inc_cfg import *
+
+# Simple call
+test_param = TestParam(
+ "PESQ",
+ [
+ InstanceParam("UA1", "--null-audio --max-calls=1 --play-file wavs/input.16.wav --auto-play-hangup"),
+ InstanceParam("UA2", "--null-audio --max-calls=1 --rec-file wavs/tmp.16.wav --clock-rate 16000 --auto-answer 200 --auto-rec")
+ ]
+ )