зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1188321 - remove media_tests code from talos. r=parkouss
--HG-- extra : rebase_source : 4d20fc4cdecb0b78b9c0d4218fd272dc1a213d9a
This commit is contained in:
Родитель
0f595763a7
Коммит
cb03e54f2d
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
vim:se?t ts=2 sw=2 sts=2 et cindent:
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
// audioPlayback Test plays an input file from a given
|
||||
// <audio> and records the same to compute PESQ scores
|
||||
var audioPlayback = function() {
|
||||
var test = this;
|
||||
var cleanupTimeout = 5000;
|
||||
var audio = document.createElement('audio');
|
||||
|
||||
// start audio recorder
|
||||
initiateAudioRecording(test);
|
||||
if (test.failed) {
|
||||
test.finished = true;
|
||||
runNextTest();
|
||||
return;
|
||||
}
|
||||
|
||||
audio.addEventListener('ended', function(evt) {
|
||||
// stop the recorder
|
||||
cleanupAudioRecording(test);
|
||||
if (!test.failed) {
|
||||
// Compute SNR and Delay between the reference
|
||||
// and the degreated files.
|
||||
getSNRAndDelay(test);
|
||||
// SNR_DELAY=5.4916,0
|
||||
// We update results as 2 separate results
|
||||
var res = JSON.parse(test.http_response);
|
||||
if(res["SNR-DELAY"]) {
|
||||
// fix the test.name
|
||||
var testName = test.name;
|
||||
var snr_delay = res["SNR-DELAY"].split(",");
|
||||
test.name = testName+"_snr_in_db";
|
||||
test.results = snr_delay[0];
|
||||
updateResults(test);
|
||||
test.name = testName+"_delay_in_ms";
|
||||
test.results = snr_delay[1];
|
||||
updateResults(test);
|
||||
// restore test.name
|
||||
test.name = testName;
|
||||
}
|
||||
}
|
||||
test.finished = true;
|
||||
runNextTest();
|
||||
});
|
||||
|
||||
audio.addEventListener('error', function(evt) {
|
||||
// cleanup any started processes and run the next
|
||||
// test
|
||||
cleanupAudioRecording(test);
|
||||
test.finished = true;
|
||||
runNextTest();
|
||||
});
|
||||
|
||||
audio.volume = 0.9;
|
||||
audio.src = test.src;
|
||||
audio.play();
|
||||
};
|
Двоичные данные
testing/talos/talos/startup_test/media/html/input16.wav
Двоичные данные
testing/talos/talos/startup_test/media/html/input16.wav
Двоичный файл не отображается.
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
vim:se?t ts=2 sw=2 sts=2 et cindent:
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
// This file hosts the logic for performing Media Operations
|
||||
// on our test server and handling responses.
|
||||
// Media Operations are in the form of GET requests/
|
||||
|
||||
// Global call back for all the HTTP Requests.
|
||||
// Since we use synchronous Ajax requests, we should
|
||||
// be good with this design.
|
||||
var httpRequestCb = function(test, req_status, response) {
|
||||
// update the test of its http request status
|
||||
if (req_status != 200) {
|
||||
test.failed = true;
|
||||
}
|
||||
test.http_response = response;
|
||||
log(test.http_response);
|
||||
}
|
||||
|
||||
// Perform the GET Request, invoke appropriate call backs
|
||||
var sendGetRequest = function(test, async) {
|
||||
var request = new XMLHttpRequest();
|
||||
request.open('GET', test.url, async);
|
||||
request.onreadystatechange = function() {
|
||||
if (request.readyState == 4) {
|
||||
httpRequestCb(test,request.status, request.response);
|
||||
}
|
||||
};
|
||||
request.send();
|
||||
};
|
||||
|
||||
// Stop our server and exit the browser
|
||||
var cleanupTests = function() {
|
||||
var url = baseUrl + '/server/config/stop'
|
||||
test.url = url;
|
||||
sendGetRequest(test, true);
|
||||
};
|
||||
|
||||
|
||||
// Perform audio/recorder/start command
|
||||
var initiateAudioRecording = function(test) {
|
||||
var url = baseUrl + '/audio/recorder/start/?timeout=' + test.timeout;
|
||||
test.url = url;
|
||||
test.failed = false;
|
||||
sendGetRequest(test, false);
|
||||
};
|
||||
|
||||
|
||||
// Perform audio/recorder/stop command
|
||||
var cleanupAudioRecording = function(test) {
|
||||
var url = baseUrl + '/audio/recorder/stop';
|
||||
test.url = url;
|
||||
test.failed = false;
|
||||
sendGetRequest(test, false);
|
||||
};
|
||||
|
||||
// Perform audio/snr/compute command
|
||||
var getSNRAndDelay = function(test) {
|
||||
var url = baseUrl + '/audio/snr/compute';
|
||||
test.url = url;
|
||||
test.failed = false;
|
||||
sendGetRequest(test, false);
|
||||
};
|
|
@ -1,26 +0,0 @@
|
|||
<!--
|
||||
vim:set ts=2 sw=2 sts=2 et cindent:
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<title> Media Performance Tests </title>
|
||||
</head>
|
||||
<body>
|
||||
<h1> Media End to End Peformance Tests </h1>
|
||||
<br/>
|
||||
<h2> Test Progress: </h2>
|
||||
<table id="tests"></div>
|
||||
<div id="log"></div>
|
||||
<script language="Javascript" src="/scripts/MozillaFileLogger.js"></script>
|
||||
<script language="Javascript" src="/scripts/Profiler.js"></script>
|
||||
<script language="Javascript" src="/tests/quit.js"></script>
|
||||
<script language="Javascript" src="/startup_test/media/html/media_api.js"></script>
|
||||
<script language="Javascript" src="/startup_test/media/html/audio_playback.js"></script>
|
||||
<script language="Javascript" src="/startup_test/media/html/pc_audio_quality.js"></script>
|
||||
<script language="Javascript" src="/startup_test/media/html/media_tests.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
vim:se?t ts=2 sw=2 sts=2 et cindent:
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
/* Part of this framework has be inspired by the latency-benchmark.js
|
||||
* framework
|
||||
* This file has the framework for running media tests. Individual
|
||||
* test logic are placed in their own Javascript files.
|
||||
* See audio_plyback.js for an example.
|
||||
*/
|
||||
|
||||
|
||||
// Global variable to hold results of all the tests
|
||||
// Entry in this object will be of the form
|
||||
// ==> results{'Test-Name'} : 'Results'
|
||||
var results = {};
|
||||
// Our test server
|
||||
var baseUrl = 'http://localhost:16932';
|
||||
// Handy area to dump the results of the tests
|
||||
var testsTable = document.getElementById('tests');
|
||||
var dontLog = true;
|
||||
|
||||
function log(msg) {
|
||||
if (!dontLog) {
|
||||
var div = document.getElementById("log");
|
||||
div.innerHTML = "<p>" + msg + "</p>";
|
||||
}
|
||||
}
|
||||
|
||||
// Updates results to be used for Talos eventually
|
||||
var updateResults = function(test) {
|
||||
// check if test.results has any data, if so, consider
|
||||
if(test.results) {
|
||||
results[test.name] = test.results
|
||||
}
|
||||
//update tests table to report on the UI
|
||||
var row = document.createElement('tr');
|
||||
var nameCell = document.createElement('td');
|
||||
nameCell.textContent = test.name;
|
||||
var outcomeCell = document.createElement('td');
|
||||
outcomeCell.textContent = test.results;
|
||||
row.appendChild(nameCell);
|
||||
row.appendChild(outcomeCell);
|
||||
testsTable.appendChild(row);
|
||||
};
|
||||
|
||||
// Array of Media Tests.
|
||||
// To add a new test, add an entry in this array and
|
||||
// provide logic for the test in its own .js file
|
||||
// See audio_playback.js & pc_audio_quality.js for examples.
|
||||
var tests = [
|
||||
{
|
||||
name: 'audio_peer_connection_quality',
|
||||
src: 'input16.wav',
|
||||
timeout: 10, //record timeout in seconds
|
||||
forceTimeout: 15000, //test forced timeout in ms
|
||||
test: audioPCQuality
|
||||
},
|
||||
{
|
||||
name: 'audio_playback_quality',
|
||||
src: 'input16.wav',
|
||||
timeout: 10, //record timeout in seconds
|
||||
forceTimeout: 15000, //test forced timeout in ms
|
||||
test: audioPlayback
|
||||
},
|
||||
];
|
||||
|
||||
var nextTestIndex = 0;
|
||||
|
||||
var runNextTest = function() {
|
||||
var testIndex = nextTestIndex++;
|
||||
if (testIndex >= tests.length ) {
|
||||
//Ideally we would post to a server via xmlhttprequest, but that doesn't seem reliable
|
||||
dump("__start_report\n");
|
||||
dump("audio_playback_quality_snr_in_db," + results["audio_playback_quality_snr_in_db"] + "\n");
|
||||
dump("audio_playback_quality_delay_in_ms," + results["audio_playback_quality_delay_in_ms"] + "\n");
|
||||
dump("__end_report\n");
|
||||
dump("__startTimestamp" + results["BEGIN_TIME_STAMP"] + "__endTimestamp\n");
|
||||
|
||||
// let's clean up the test framework
|
||||
var request = new XMLHttpRequest();
|
||||
var url = baseUrl + '/server/config/stop';
|
||||
request.open('GET', url, false);
|
||||
request.send();
|
||||
|
||||
goQuitApplication();
|
||||
window.close();
|
||||
} else {
|
||||
var test = tests[testIndex];
|
||||
// Occasionally <audio> tag doesn't play media properly
|
||||
// and this results in test case to hang. Since it
|
||||
// never error's out, test.forceTimeout guards against this.
|
||||
setTimeout(function() { checkTimeout(test); }, test.forceTimeout);
|
||||
test.test();
|
||||
}
|
||||
};
|
||||
|
||||
// Test timed out, go through the next test.
|
||||
var checkTimeout = function(test) {
|
||||
if(!test.finished) {
|
||||
test.finished = true;
|
||||
log("test " + test.name + "timed out");
|
||||
runNextTest();
|
||||
}
|
||||
};
|
||||
|
||||
results['BEGIN_TIME_STAMP'] = new Date().getTime();
|
||||
setTimeout(runNextTest, 100);
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
/*
|
||||
vim:se?t ts=2 sw=2 sts=2 et cindent:
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
// Peer Connection Audio Quality Test
|
||||
// This test performs one-way audio call using WebRT Peer Connection
|
||||
// mozCaptureStreamUntilEnded() is used to replace GUM to feed
|
||||
// audio from an input file into the Peer Connection.
|
||||
// Once played out, the audio at the speaker is recorded to compute
|
||||
// SNR scores
|
||||
|
||||
var pc1;
|
||||
var pc2;
|
||||
var pc1_offer;
|
||||
var pc2_answer;
|
||||
|
||||
function failed(code) {
|
||||
log("Failure callback: " + code);
|
||||
}
|
||||
|
||||
// pc1.createOffer finished, call pc1.setLocal
|
||||
function step1(offer) {
|
||||
pc1_offer = offer;
|
||||
pc1.setLocalDescription(offer, step2, failed);
|
||||
}
|
||||
|
||||
// pc1.setLocal finished, call pc2.setRemote
|
||||
function step2() {
|
||||
pc2.setRemoteDescription(pc1_offer, step3, failed);
|
||||
};
|
||||
|
||||
// pc2.setRemote finished, call pc2.createAnswer
|
||||
function step3() {
|
||||
pc2.createAnswer(step4, failed);
|
||||
}
|
||||
|
||||
// pc2.createAnswer finished, call pc2.setLocal
|
||||
function step4(answer) {
|
||||
pc2_answer = answer;
|
||||
pc2.setLocalDescription(answer, step5, failed);
|
||||
}
|
||||
|
||||
// pc2.setLocal finished, call pc1.setRemote
|
||||
function step5() {
|
||||
pc1.setRemoteDescription(pc2_answer, step6, failed);
|
||||
}
|
||||
|
||||
// pc1.setRemote finished, media should be running!
|
||||
function step6() {
|
||||
log("Peer Connection Signaling Completed Successfully !");
|
||||
}
|
||||
|
||||
function end(status) {
|
||||
}
|
||||
|
||||
// Test Function
|
||||
var audioPCQuality = function() {
|
||||
var test = this
|
||||
var localaudio = document.createElement('audio');
|
||||
var pc1audio = document.createElement('audio');
|
||||
var pc2audio = document.createElement('audio');
|
||||
pc1 = new mozRTCPeerConnection();
|
||||
pc2 = new mozRTCPeerConnection();
|
||||
|
||||
pc1.onaddstream = function(obj) {
|
||||
log("pc1 got remote stream from pc2 " + obj.type);
|
||||
}
|
||||
|
||||
pc2.onaddstream = function(obj) {
|
||||
log("pc2 got remote stream from pc1 " + obj.type);
|
||||
pc1audio.mozSrcObject = obj.stream;
|
||||
pc1audio.play();
|
||||
}
|
||||
|
||||
localaudio.src = test.src
|
||||
|
||||
initiateAudioRecording(test);
|
||||
if (test.failed) {
|
||||
test.finished = true;
|
||||
runNextTest();
|
||||
return;
|
||||
}
|
||||
|
||||
localaudio.addEventListener('ended', function(evt) {
|
||||
// stop the recorder
|
||||
cleanupAudioRecording(test);
|
||||
if (!test.failed) {
|
||||
// Compute SNR and Delay between the reference
|
||||
// and the degraded files.
|
||||
getSNRAndDelay(test);
|
||||
// SNR_DELAY=5.4916,0
|
||||
// We update results as 2 separate results
|
||||
var res = JSON.parse(test.http_response);
|
||||
if(res["SNR-DELAY"]) {
|
||||
// fix the test.name
|
||||
var testName = test.name;
|
||||
var snr_delay = res["SNR-DELAY"].split(",");
|
||||
test.name = testName+"_snr_in_db";
|
||||
test.results = snr_delay[0];
|
||||
updateResults(test);
|
||||
test.name = testName+"_delay_in_ms";
|
||||
test.results = snr_delay[1];
|
||||
updateResults(test);
|
||||
// restore test.name
|
||||
test.name = testName;
|
||||
}
|
||||
}
|
||||
test.finished = true;
|
||||
runNextTest();
|
||||
});
|
||||
|
||||
localaudio.addEventListener('error', function(evt) {
|
||||
cleanupAudioRecording(test);
|
||||
test.failed = true;
|
||||
test.finished = true;
|
||||
runNextTest();
|
||||
});
|
||||
|
||||
localaudio.vol = 0.9;
|
||||
localAudioStream = localaudio.mozCaptureStreamUntilEnded();
|
||||
localaudio.play();
|
||||
pc1.addStream(localAudioStream);
|
||||
navigator.mozGetUserMedia({audio:true,fake:true}, function(audio2) {
|
||||
pc2.addStream(audio2);
|
||||
pc1.createOffer(step1, failed);
|
||||
}, failed);
|
||||
};
|
|
@ -1,171 +0,0 @@
|
|||
# vim:se?t ts=2 sw=2 sts=2 et cindent:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import sys
|
||||
import platform
|
||||
import optparse
|
||||
import mozhttpd
|
||||
# media test utilities
|
||||
import media_utils
|
||||
|
||||
from mozlog import get_proxy_logger
|
||||
|
||||
LOG = get_proxy_logger()
|
||||
|
||||
"""
|
||||
MediaManager serves as entry point for running media performance tests.
|
||||
It is responsible for the following
|
||||
1. Manage life-cycle of the mozHttpdServer and browser instance
|
||||
2. Provide RESTP API handlers for the Media tests
|
||||
3. REST API design follows the pattern
|
||||
<resource>/<component>/<operation>
|
||||
Example: audio recorder functionality
|
||||
==> audio/recorder/start, audio/recorder/stop
|
||||
For audio pesq functionality
|
||||
==> audio/pesq/compute
|
||||
For Stopping the server and browser
|
||||
==> server/config/stop
|
||||
|
||||
The idea here is to allow easy specification of resources
|
||||
(audio, video, server) , components (tools such as PESQ, SOX)
|
||||
and the operations on them (start, stop, computue..)
|
||||
|
||||
NOTE: Using PESQ seems to have licensing issues. Hence for time being
|
||||
usage of PESQ is replaced by the SNR computation.
|
||||
"""
|
||||
|
||||
# This path is based on the ${talos}
|
||||
# This page gets loaded as default home page for running the media tests.
|
||||
__TEST_HTML_PAGE__ = \
|
||||
"http://localhost:16932/startup_test/media/html/media_tests.html"
|
||||
|
||||
|
||||
class ObjectDb(object):
|
||||
"""
|
||||
ObjectDb serves as a global storage to hold object handles needed
|
||||
during manager's operation. It holds browser process handle, httpd
|
||||
server handle and reference to the Audio Utils object.
|
||||
"""
|
||||
|
||||
httpd_server = None
|
||||
audio_utils = None
|
||||
|
||||
|
||||
# URI Handlers and Parsers
|
||||
def errorMessage(message):
|
||||
return (500, {'ERROR': message})
|
||||
|
||||
|
||||
@mozhttpd.handlers.json_response
|
||||
def parseGETUrl(request):
|
||||
# Parse the url and invoke appropriate handlers
|
||||
url_path = request.path.lower()
|
||||
if url_path.find('audio') != -1:
|
||||
return (handleAudioRequest(request))
|
||||
elif url_path.find('server') != -1:
|
||||
return (handleServerConfigRequest(request))
|
||||
else:
|
||||
return errorMessage(request.path)
|
||||
|
||||
|
||||
# Handler for Server Configuration Commands
|
||||
def handleServerConfigRequest(request):
|
||||
# Stopping server is the only operation supported
|
||||
if request.path == '/server/config/stop':
|
||||
ObjectDb.httpd_server.stop()
|
||||
return (200, {'ok': 'OK'})
|
||||
else:
|
||||
return errorMessage(request.path)
|
||||
|
||||
|
||||
# Handler Audio Resource Command
|
||||
def handleAudioRequest(request):
|
||||
# is this a recorder API
|
||||
if request.path.find('recorder') != -1:
|
||||
return (parseAudioRecorderRequest(request))
|
||||
elif request.path.find('snr') != -1:
|
||||
return(parseSNRRequest(request))
|
||||
else:
|
||||
return errorMessage(request.path)
|
||||
|
||||
|
||||
# Handle all the audio recorder operations
|
||||
def parseAudioRecorderRequest(request):
|
||||
# check if there are params
|
||||
params = request.query.split(',')
|
||||
if request.path.find('start') != -1:
|
||||
for items in params:
|
||||
(name, value) = items.split('=')
|
||||
if name.startswith('timeout'):
|
||||
status, message = ObjectDb.audio_utils.startRecording(value)
|
||||
if status is True:
|
||||
return (200, {'Start-Recording': message})
|
||||
else:
|
||||
return errorMessage(message)
|
||||
elif request.path.find('stop') != -1:
|
||||
ObjectDb.audio_utils.stopRecording()
|
||||
return (200, {'Stop-Recording': 'Success'})
|
||||
else:
|
||||
return errorMessage(request.path)
|
||||
|
||||
|
||||
# Parse SNR Request
|
||||
def parseSNRRequest(request):
|
||||
if request.path.find('compute') != -1:
|
||||
status, message = ObjectDb.audio_utils.computeSNRAndDelay()
|
||||
if status is True:
|
||||
return (200, {'SNR-DELAY': message})
|
||||
else:
|
||||
return errorMessage(message)
|
||||
else:
|
||||
return errorMessage(request.path)
|
||||
|
||||
|
||||
# Run HTTPD server and setup URL path handlers
|
||||
# doc_root is set to ${talos} when invoked from the talos
|
||||
def run_server(doc_root):
|
||||
ObjectDb.audio_utils = media_utils.AudioUtils()
|
||||
|
||||
httpd_server = mozhttpd.MozHttpd(
|
||||
port=16932,
|
||||
docroot=doc_root,
|
||||
urlhandlers=[
|
||||
{'method': 'GET', 'path': '/audio/', 'function': parseGETUrl},
|
||||
{'method': 'GET', 'path': '/server/?', 'function': parseGETUrl}
|
||||
]
|
||||
)
|
||||
|
||||
LOG.info("Server %s at %s:%s" % (httpd_server.docroot,
|
||||
httpd_server.host,
|
||||
httpd_server.port))
|
||||
ObjectDb.httpd_server = httpd_server
|
||||
httpd_server.start()
|
||||
return httpd_server
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Linux platform is supported
|
||||
if not platform.system() == "Linux":
|
||||
print "This version of the tool supports only Linux"
|
||||
sys.exit(0)
|
||||
|
||||
parser = optparse.OptionParser()
|
||||
"""
|
||||
No validation of options are done since we control
|
||||
this program from within the Talos.
|
||||
TODO: provide validation once stand-alone usage
|
||||
is supported
|
||||
"""
|
||||
parser.add_option("-t", "--talos", dest="talos_path",
|
||||
help="Talos Path to serves as docroot")
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if options.stop:
|
||||
ObjectDb.httpd_server.stop()
|
||||
|
||||
# 1. Create handle to the AudioUtils
|
||||
ObjectDb.audio_utils = media_utils.AudioUtils()
|
||||
|
||||
# 3. Start the httpd server
|
||||
run_server(options.talos_path)
|
|
@ -1,213 +0,0 @@
|
|||
# vim:set ts=2 sw=2 sts=2 et cindent:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"""
|
||||
Utilities needed for performing media tests. At present, we do
|
||||
0. Audio Recording from the monitor of default sink device.
|
||||
1. Silence Removal.
|
||||
2. PESQ based quality measurement.
|
||||
4. Linux platform support
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import threading
|
||||
import mozfile
|
||||
|
||||
here = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
"""
|
||||
Constants for audio tools, input and processed audio files
|
||||
For Linux platform, PulseAudio and LibSox are assumed to be
|
||||
installed.
|
||||
"""
|
||||
_TOOLS_PATH_ = os.path.join(here, 'tools')
|
||||
_TEST_PATH_ = os.path.join(here, 'html')
|
||||
_PESQ_ = os.path.join(_TOOLS_PATH_, 'PESQ')
|
||||
_MEDIA_TOOLS_ = os.path.join(_TOOLS_PATH_, 'MediaUtils')
|
||||
_INPUT_FILE_ = os.path.join(_TEST_PATH_, 'input16.wav')
|
||||
# These files are created and removed as part of test run
|
||||
_RECORDED_FILE_ = os.path.join(_TEST_PATH_, 'record.wav')
|
||||
_RECORDED_NO_SILENCE_ = os.path.join(_TEST_PATH_, 'record_no_silence.wav')
|
||||
|
||||
# Constants used as parameters to Sox recorder and silence trimmer
|
||||
# TODO: Make these dynamically configurable
|
||||
_SAMPLE_RATE_ = '48000' # 48khz - seems to work with pacat better than 16khz
|
||||
_NUM_CHANNELS_ = '1' # mono channel
|
||||
_SOX_ABOVE_PERIODS_ = '1' # One period of silence in the beginning
|
||||
_SOX_ABOVE_DURATION_ = '2' # Duration to trim till proper audio
|
||||
_SOX_ABOVE_THRESHOLD_ = '5%' # Audio level to trim at the beginning
|
||||
_SOX_BELOW_PERIODS_ = '1' # One period of silence in the beginning
|
||||
_SOX_BELOW_DURATION_ = '2' # Duration to trim till proper audio
|
||||
_SOX_BELOW_THRESHOLD_ = '5%' # Audio level to trim at the beginning
|
||||
_PESQ_SAMPLE_RATE_ = '+16000' # 16Khz
|
||||
_VOLUME_100_PERCENT_ = '65536' # Max volume
|
||||
_DEFAULT_REC_DURATION_ = 10 # Defaults to 10 seconds worth of recording
|
||||
|
||||
|
||||
class AudioRecorder(threading.Thread):
|
||||
"""
|
||||
Thread to record audio
|
||||
"""
|
||||
|
||||
def __init__(self, parent, output_file):
|
||||
self.output_file = output_file
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
# Set record duration, typically set to length
|
||||
# of the audio test being run.
|
||||
def setDuration(self, duration):
|
||||
self.rec_duration = duration
|
||||
|
||||
# Set source monitor for recording
|
||||
# We pick the first monitor for the sink available
|
||||
def setRecordingDevice(self, device):
|
||||
self.rec_device = device
|
||||
# Adjust volume of the sink to 100%, since quality was
|
||||
# bit degraded when this was not done.
|
||||
cmd = ['pacmd', 'set-source-volume', self.rec_device,
|
||||
_VOLUME_100_PERCENT_]
|
||||
cmd = [str(s) for s in cmd]
|
||||
try:
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
p.communicate()
|
||||
except:
|
||||
return False, "Audio Recorder : pacmd set-source-volume failed"
|
||||
|
||||
# no need to set the success message, since this function
|
||||
# is used internally
|
||||
return True
|
||||
|
||||
# Start recording.
|
||||
def run(self):
|
||||
|
||||
if not self.rec_device:
|
||||
return
|
||||
|
||||
if not self.rec_duration:
|
||||
self.rec_duration = _DEFAULT_REC_DURATION_
|
||||
|
||||
# PACAT command to record 16 bit singned little endian mono channel
|
||||
# audio from the sink self.rec_device
|
||||
pa_command = ['pacat', '-r', '-d', self.rec_device, '--format=s16le',
|
||||
'--fix-rate', '--channels=1']
|
||||
pa_command = [str(s) for s in pa_command]
|
||||
|
||||
# Sox command to convert raw audio from PACAT output to .wav format"
|
||||
sox_command = ['sox', '-t', 'raw', '-r', _SAMPLE_RATE_,
|
||||
'--encoding=signed-integer', '-Lb', 16, '-c',
|
||||
_NUM_CHANNELS_, '-', self.output_file, 'rate',
|
||||
'16000', 'trim', 0, self.rec_duration]
|
||||
|
||||
sox_command = [str(s) for s in sox_command]
|
||||
|
||||
# Run the commands the PIPE them together
|
||||
p1 = subprocess.Popen(pa_command, stdout=subprocess.PIPE)
|
||||
p2 = subprocess.Popen(sox_command, stdin=p1.stdout,
|
||||
stdout=subprocess.PIPE)
|
||||
p2.communicate()[0]
|
||||
# No need to kill p2 since it is terminated as part of trim duration
|
||||
if p1:
|
||||
p1.kill()
|
||||
|
||||
|
||||
class AudioUtils(object):
|
||||
'''
|
||||
Utility class for managing pre and post recording operations
|
||||
It includes operations to
|
||||
- start/stop audio recorder based on PusleAudio and SOX
|
||||
- Trim the silence off the recorded audio based on SOX
|
||||
- Compute PESQ scores
|
||||
'''
|
||||
|
||||
# Reference to the recorder thread.
|
||||
recorder = None
|
||||
|
||||
# Function to find the monitor for sink available
|
||||
# on the platform.
|
||||
def setupAudioDeviceForRecording(self):
|
||||
# Use pactl to find the monitor for our Sink
|
||||
# This picks the first sink available
|
||||
cmd = ['pactl', 'list']
|
||||
output = subprocess.check_output(cmd)
|
||||
result = re.search('\s*Name: (\S*\.monitor)', output)
|
||||
if result:
|
||||
self.recorder.setRecordingDevice(result.group(1))
|
||||
return True, "Recording Device: %s Set" % result.group(1)
|
||||
else:
|
||||
return False, "Unable to Set Recording Device"
|
||||
|
||||
# Run SNR on the audio reference file and recorded file
|
||||
def computeSNRAndDelay(self):
|
||||
snr_delay = "-1.000,-1"
|
||||
|
||||
if not os.path.exists(_MEDIA_TOOLS_):
|
||||
return False, "SNR Tool not found"
|
||||
|
||||
cmd = [_MEDIA_TOOLS_, '-c', 'snr', '-r', _INPUT_FILE_, '-t',
|
||||
_RECORDED_NO_SILENCE_]
|
||||
cmd = [str(s) for s in cmd]
|
||||
|
||||
output = subprocess.check_output(cmd)
|
||||
# SNR_Delay=1.063,5
|
||||
result = re.search('SNR_DELAY=(\d+\.\d+),(\d+)', output)
|
||||
|
||||
# delete the recorded file with no silence
|
||||
mozfile.remove(_RECORDED_NO_SILENCE_)
|
||||
|
||||
if result:
|
||||
snr_delay = str(result.group(1)) + ',' + str(result.group(2))
|
||||
return True, snr_delay
|
||||
else:
|
||||
"""
|
||||
We return status as True since SNR computation went through
|
||||
successfully but scores computation failed due to severly
|
||||
degraded audio quality.
|
||||
"""
|
||||
return True, snr_delay
|
||||
|
||||
# Kick-off Audio Recording Thread
|
||||
def startRecording(self, duration):
|
||||
|
||||
if self.recorder and self.recorder.is_alive():
|
||||
return False, "An Running Instance Of Recorder Found"
|
||||
|
||||
self.recorder = AudioRecorder(self, _RECORDED_FILE_)
|
||||
if not self.recorder:
|
||||
return False, "Audio Recorder Setup Failed"
|
||||
|
||||
status, message = self.setupAudioDeviceForRecording()
|
||||
if status is True:
|
||||
self.recorder.setDuration(duration)
|
||||
self.recorder.start()
|
||||
# check if the thread did start
|
||||
if self.recorder.is_alive():
|
||||
return True, message
|
||||
else:
|
||||
return False, "Audio Recorder Setup Failed"
|
||||
else:
|
||||
return False, message
|
||||
|
||||
# Let complete Audio Recording and remove silence at the
|
||||
# beginning and towards the end of recorded audio.
|
||||
def stopRecording(self):
|
||||
self.recorder.join()
|
||||
# clean up silence and delete the recorded file
|
||||
"""
|
||||
http://digitalcardboard.com/blog/2009/08/25/the-sox-of-silence/
|
||||
./sox record.wav out1.wav silence 1 2 5% 1 2 5% reverse silence
|
||||
1 2 5%
|
||||
"""
|
||||
cmd = ['sox', _RECORDED_FILE_, _RECORDED_NO_SILENCE_, 'silence',
|
||||
_SOX_ABOVE_PERIODS_, _SOX_ABOVE_DURATION_,
|
||||
_SOX_ABOVE_THRESHOLD_, 'reverse', 'silence',
|
||||
_SOX_BELOW_PERIODS_, _SOX_BELOW_DURATION_,
|
||||
_SOX_BELOW_THRESHOLD_, 'reverse']
|
||||
cmd = [str(s) for s in cmd]
|
||||
subprocess.call(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
# Delete the recorded file
|
||||
mozfile.remove(_RECORDED_FILE_)
|
Двоичные данные
testing/talos/talos/startup_test/media/tools/MediaUtils
Двоичные данные
testing/talos/talos/startup_test/media/tools/MediaUtils
Двоичный файл не отображается.
Двоичные данные
testing/talos/talos/startup_test/media/tools/PESQ
Двоичные данные
testing/talos/talos/startup_test/media/tools/PESQ
Двоичный файл не отображается.
|
@ -211,18 +211,6 @@ class tresize(TsBase):
|
|||
unit = 'ms'
|
||||
|
||||
|
||||
# Media Test
|
||||
@register_test()
|
||||
class media_tests(TsBase):
|
||||
"""
|
||||
Media Performance Tests
|
||||
"""
|
||||
cycles = 5
|
||||
desktop = True
|
||||
url = 'http://localhost:16932/startup_test/media/html/media_tests.html'
|
||||
timeout = 360
|
||||
unit = 'score'
|
||||
|
||||
# pageloader tests(tp5, etc)
|
||||
|
||||
# The overall test number is determined by first calculating the median
|
||||
|
|
|
@ -149,14 +149,6 @@ class TTest(object):
|
|||
['python'] + test_config['setup'].split(),
|
||||
)
|
||||
|
||||
mm_httpd = None
|
||||
|
||||
if test_config['name'] == 'media_tests':
|
||||
from startup_test.media import media_manager
|
||||
mm_httpd = media_manager.run_server(
|
||||
os.path.dirname(os.path.realpath(__file__))
|
||||
)
|
||||
|
||||
counter_management = None
|
||||
if counters:
|
||||
counter_management = CounterManagement(
|
||||
|
@ -178,8 +170,6 @@ class TTest(object):
|
|||
finally:
|
||||
if counter_management:
|
||||
counter_management.stop()
|
||||
if mm_httpd:
|
||||
mm_httpd.stop()
|
||||
|
||||
if test_config['mainthread']:
|
||||
rawlog = os.path.join(here, "mainthread_io.log")
|
||||
|
|
|
@ -170,10 +170,6 @@ def GenerateBrowserCommandLine(browser_path, extra_args, profile_dir,
|
|||
|
||||
command_args.extend(url.split(' '))
|
||||
|
||||
# Handle media performance tests
|
||||
if url.find('media_manager.py') != -1:
|
||||
command_args = url.split(' ')
|
||||
|
||||
return command_args
|
||||
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче