Add support for ICE/STUN/TURN in res_rtp_asterisk and chan_sip.
[asterisk/asterisk.git] / res / pjproject / tests / pjsua / mod_pesq.py
1 # $Id$
2
3 # Quality test of media calls.
4 # - UA1 calls UA2
5 # - UA1 plays a file until finished to be streamed to UA2
6 # - UA2 records from stream
7 # - Apply PESQ to played file (reference) and recorded file (degraded)
8 #
9 # File should be:
10 # - naming: xxxxxx.CLOCK_RATE.wav, e.g: test1.8.wav
11 # - clock-rate of those files can only be 8khz or 16khz
12
13 import time
14 import imp
15 import os
16 import sys
17 import re
18 import subprocess
19 import wave
20 import shutil
21 import inc_const as const
22
23 from inc_cfg import *
24
25 # Load configuration
26 cfg_file = imp.load_source("cfg_file", ARGS[1])
27
28 # PESQ configs
29 PESQ = "tools/pesq"                     # PESQ executable path
30 PESQ_DEFAULT_THRESHOLD = 3.4            # Default minimum acceptable PESQ MOS value
31
32 # PESQ params
33 pesq_sample_rate_opt = ""               # Sample rate option for PESQ
34 input_filename  = ""                    # Input/Reference filename
35 output_filename = ""                    # Output/Degraded filename
36
37
38 # Test body function
39 def test_func(t):
40         global pesq_sample_rate_opt
41         global input_filename
42         global output_filename
43
44         ua1 = t.process[0]
45         ua2 = t.process[1]
46
47         # Get input file name
48         input_filename = re.compile(const.MEDIA_PLAY_FILE).search(ua1.inst_param.arg).group(1)
49
50         # Get output file name
51         output_filename = re.compile(const.MEDIA_REC_FILE).search(ua2.inst_param.arg).group(1)
52
53         # Get WAV input length, in seconds
54         fin = wave.open(input_filename, "r")
55         if fin == None:
56                 raise TestError("Failed opening input WAV file")
57         inwavlen = fin.getnframes() * 1.0 / fin.getframerate()
58         inwavlen += 0.2
59         fin.close()
60         print "WAV input len = " + str(inwavlen) + "s"
61
62         # Get clock rate of the output
63         mo_clock_rate = re.compile("\.(\d+)\.wav").search(output_filename)
64         if (mo_clock_rate==None):
65                 raise TestError("Cannot compare input & output, incorrect output filename format")
66         clock_rate = mo_clock_rate.group(1)
67         
68         # Get channel count of the output
69         channel_count = 1
70         if re.search("--stereo", ua2.inst_param.arg) != None:
71                 channel_count = 2
72         
73         # Get matched input file from output file
74         # (PESQ evaluates only files whose same clock rate & channel count)
75         if channel_count == 2:
76             if re.search("\.\d+\.\d+\.wav", input_filename) != None:
77                     input_filename = re.sub("\.\d+\.\d+\.wav", "." + str(channel_count) + "."+clock_rate+".wav", input_filename)
78             else:
79                     input_filename = re.sub("\.\d+\.wav", "." + str(channel_count) + "."+clock_rate+".wav", input_filename)
80
81         if (clock_rate != "8") & (clock_rate != "16"):
82                 raise TestError("PESQ only works on clock rate 8kHz or 16kHz, clock rate used = "+clock_rate+ "kHz")
83
84         # Get conference clock rate of UA2 for PESQ sample rate option
85         pesq_sample_rate_opt = "+" + clock_rate + "000"
86
87         # UA1 making call
88         ua1.send("m")
89         ua1.send(t.inst_params[1].uri)
90         ua1.expect(const.STATE_CALLING)
91
92         # UA2 wait until call established
93         ua2.expect(const.STATE_CONFIRMED)
94
95         ua1.sync_stdout()
96         ua2.sync_stdout()
97         time.sleep(2)
98
99         # Disconnect mic -> rec file, to avoid echo recorded when using sound device
100         # Disconnect stream -> spk, make it silent
101         # Connect stream -> rec file, start recording
102         ua2.send("cd 0 1\ncd 4 0\ncc 4 1")
103
104         # Disconnect mic -> stream, make stream purely sending from file
105         # Disconnect stream -> spk, make it silent
106         # Connect file -> stream, start sending
107         ua1.send("cd 0 4\ncd 4 0\ncc 1 4")
108
109         time.sleep(inwavlen)
110
111         # Disconnect files from bridge
112         ua2.send("cd 4 1")
113         ua2.expect(const.MEDIA_DISCONN_PORT_SUCCESS)
114         ua1.send("cd 1 4")
115         ua1.expect(const.MEDIA_DISCONN_PORT_SUCCESS)
116
117
118 # Post body function
119 def post_func(t):
120         global pesq_sample_rate_opt
121         global input_filename
122         global output_filename
123
124         endpt = t.process[0]
125
126         # Execute PESQ
127         fullcmd = os.path.normpath(PESQ) + " " + pesq_sample_rate_opt + " " + input_filename + " " + output_filename
128         endpt.trace("Popen " + fullcmd)
129         pesq_proc = subprocess.Popen(fullcmd, shell=True, stdout=subprocess.PIPE, universal_newlines=True)
130         pesq_out  = pesq_proc.communicate()
131
132         # Parse ouput
133         mo_pesq_out = re.compile("Prediction[^=]+=\s+([\-\d\.]+)\s*").search(pesq_out[0])
134         if (mo_pesq_out == None):
135                 raise TestError("Failed to fetch PESQ result")
136
137         # Get threshold
138         if (cfg_file.pesq_threshold != None) | (cfg_file.pesq_threshold > -0.5 ):
139                 threshold = cfg_file.pesq_threshold
140         else:
141                 threshold = PESQ_DEFAULT_THRESHOLD
142
143         # Evaluate the PESQ MOS value
144         pesq_res = mo_pesq_out.group(1)
145         if (float(pesq_res) >= threshold):
146                 endpt.trace("Success, PESQ result = " + pesq_res + " (target=" + str(threshold) + ").")
147         else:
148                 endpt.trace("Failed, PESQ result = " + pesq_res + " (target=" + str(threshold) + ").")
149                 # Save the wav file
150                 wavoutname = ARGS[1]
151                 wavoutname = re.sub("[\\\/]", "_", wavoutname)
152                 wavoutname = re.sub("\.py$", ".wav", wavoutname)
153                 wavoutname = "logs/" + wavoutname
154                 try:
155                         shutil.copyfile(output_filename, wavoutname)
156                         print "Output WAV is copied to " + wavoutname
157                 except:
158                         print "Couldn't copy output WAV, please check if 'logs' directory exists."
159
160                 raise TestError("WAV seems to be degraded badly, PESQ = "+ pesq_res + " (target=" + str(threshold) + ").")
161
162
163 # Here where it all comes together
164 test = cfg_file.test_param
165 test.test_func = test_func
166 test.post_func = post_func
167