mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-13 07:24:47 +00:00
bug 815002 - allow using loopback devices in WebRTC mochitests on Linux when available. r=jsmith,jmaher
This commit is contained in:
parent
57682362bb
commit
59841f10b0
@ -8,6 +8,16 @@ var Cr = SpecialPowers.Cr;
|
||||
|
||||
// Specifies whether we are using fake streams to run this automation
|
||||
var FAKE_ENABLED = true;
|
||||
try {
|
||||
var audioDevice = SpecialPowers.getCharPref('media.audio_loopback_dev');
|
||||
var videoDevice = SpecialPowers.getCharPref('media.video_loopback_dev');
|
||||
dump('TEST DEVICES: Using media devices:\n');
|
||||
dump('audio: ' + audioDevice + '\nvideo: ' + videoDevice + '\n');
|
||||
FAKE_ENABLED = false;
|
||||
} catch (e) {
|
||||
dump('TEST DEVICES: No test devices found (in media.{audio,video}_loopback_dev, using fake streams.\n');
|
||||
FAKE_ENABLED = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@ -97,7 +107,7 @@ function createMediaElement(type, label) {
|
||||
* The error callback if the stream fails to be retrieved
|
||||
*/
|
||||
function getUserMedia(constraints, onSuccess, onError) {
|
||||
if (!("fake" in constraints)) {
|
||||
if (!("fake" in constraints) && FAKE_ENABLED) {
|
||||
constraints["fake"] = FAKE_ENABLED;
|
||||
}
|
||||
|
||||
|
@ -190,7 +190,8 @@ class MochitestRunner(MozbuildObject):
|
||||
jsdebugger=False, debug_on_failure=False, start_at=None, end_at=None,
|
||||
e10s=False, dmd=False, dump_output_directory=None,
|
||||
dump_about_memory_after_test=False, dump_dmd_after_test=False,
|
||||
install_extension=None, quiet=False, environment=[], app_override=None, **kwargs):
|
||||
install_extension=None, quiet=False, environment=[], app_override=None,
|
||||
useTestMediaDevices=False, **kwargs):
|
||||
"""Runs a mochitest.
|
||||
|
||||
test_paths are path to tests. They can be a relative path from the
|
||||
@ -315,6 +316,7 @@ class MochitestRunner(MozbuildObject):
|
||||
options.dumpOutputDirectory = dump_output_directory
|
||||
options.quiet = quiet
|
||||
options.environment = environment
|
||||
options.useTestMediaDevices = useTestMediaDevices
|
||||
|
||||
options.failureFile = failure_file_path
|
||||
if install_extension != None:
|
||||
@ -526,6 +528,12 @@ def MochitestCommand(func):
|
||||
help="Sets the given variable in the application's environment")
|
||||
func = setenv(func)
|
||||
|
||||
test_media = CommandArgument('--use-test-media-devices', default=False,
|
||||
action='store_true',
|
||||
dest='useTestMediaDevices',
|
||||
help='Use test media device drivers for media testing.')
|
||||
func = test_media(func)
|
||||
|
||||
app_override = CommandArgument('--app-override', default=None, action='store',
|
||||
help="Override the default binary used to run tests with the path you provide, e.g. " \
|
||||
" --app-override /usr/bin/firefox . " \
|
||||
|
@ -416,6 +416,12 @@ class MochitestOptions(optparse.OptionParser):
|
||||
"help": "name of the pidfile to generate",
|
||||
"default": "",
|
||||
}],
|
||||
[["--use-test-media-devices"],
|
||||
{ "action": "store_true",
|
||||
"default": False,
|
||||
"dest": "useTestMediaDevices",
|
||||
"help": "Use test media device drivers for media testing.",
|
||||
}],
|
||||
]
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
@ -574,6 +580,13 @@ class MochitestOptions(optparse.OptionParser):
|
||||
self.error('--dump-output-directory not a directory: %s' %
|
||||
options.dumpOutputDirectory)
|
||||
|
||||
if options.useTestMediaDevices:
|
||||
if not mozinfo.isLinux:
|
||||
self.error('--use-test-media-devices is only supported on Linux currently')
|
||||
for f in ['/usr/bin/gst-launch-0.10', '/usr/bin/pactl']:
|
||||
if not os.path.isfile(f):
|
||||
self.error('Missing binary %s required for --use-test-media-devices')
|
||||
|
||||
return options
|
||||
|
||||
|
||||
|
@ -12,6 +12,7 @@ import sys
|
||||
SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
|
||||
sys.path.insert(0, SCRIPT_DIR);
|
||||
|
||||
import ctypes
|
||||
import glob
|
||||
import json
|
||||
import mozcrash
|
||||
@ -754,11 +755,123 @@ class SSLTunnel:
|
||||
if os.path.exists(self.configFile):
|
||||
os.remove(self.configFile)
|
||||
|
||||
def checkAndConfigureV4l2loopback(device):
|
||||
'''
|
||||
Determine if a given device path is a v4l2loopback device, and if so
|
||||
toggle a few settings on it via fcntl. Very linux-specific.
|
||||
|
||||
Returns (status, device name) where status is a boolean.
|
||||
'''
|
||||
if not mozinfo.isLinux:
|
||||
return False, ''
|
||||
|
||||
libc = ctypes.cdll.LoadLibrary('libc.so.6')
|
||||
O_RDWR = 2
|
||||
# These are from linux/videodev2.h
|
||||
class v4l2_capability(ctypes.Structure):
|
||||
_fields_ = [
|
||||
('driver', ctypes.c_char * 16),
|
||||
('card', ctypes.c_char * 32),
|
||||
('bus_info', ctypes.c_char * 32),
|
||||
('version', ctypes.c_uint32),
|
||||
('capabilities', ctypes.c_uint32),
|
||||
('device_caps', ctypes.c_uint32),
|
||||
('reserved', ctypes.c_uint32 * 3)
|
||||
]
|
||||
VIDIOC_QUERYCAP = 0x80685600
|
||||
|
||||
fd = libc.open(device, O_RDWR)
|
||||
if fd < 0:
|
||||
return False, ''
|
||||
|
||||
vcap = v4l2_capability()
|
||||
if libc.ioctl(fd, VIDIOC_QUERYCAP, ctypes.byref(vcap)) != 0:
|
||||
return False, ''
|
||||
|
||||
if vcap.driver != 'v4l2 loopback':
|
||||
return False, ''
|
||||
|
||||
class v4l2_control(ctypes.Structure):
|
||||
_fields_ = [
|
||||
('id', ctypes.c_uint32),
|
||||
('value', ctypes.c_int32)
|
||||
]
|
||||
|
||||
# These are private v4l2 control IDs, see:
|
||||
# https://github.com/umlaeute/v4l2loopback/blob/fd822cf0faaccdf5f548cddd9a5a3dcebb6d584d/v4l2loopback.c#L131
|
||||
KEEP_FORMAT = 0x8000000
|
||||
SUSTAIN_FRAMERATE = 0x8000001
|
||||
VIDIOC_S_CTRL = 0xc008561c
|
||||
|
||||
control = v4l2_control()
|
||||
control.id = KEEP_FORMAT
|
||||
control.value = 1
|
||||
libc.ioctl(fd, VIDIOC_S_CTRL, ctypes.byref(control))
|
||||
|
||||
control.id = SUSTAIN_FRAMERATE
|
||||
control.value = 1
|
||||
libc.ioctl(fd, VIDIOC_S_CTRL, ctypes.byref(control))
|
||||
libc.close(fd)
|
||||
|
||||
return True, vcap.card
|
||||
|
||||
def findTestMediaDevices():
|
||||
'''
|
||||
Find the test media devices configured on this system, and return a dict
|
||||
containing information about them. The dict will have keys for 'audio'
|
||||
and 'video', each containing the name of the media device to use.
|
||||
|
||||
If audio and video devices could not be found, return None.
|
||||
|
||||
This method is only currently implemented for Linux.
|
||||
'''
|
||||
if not mozinfo.isLinux:
|
||||
return None
|
||||
|
||||
info = {}
|
||||
# Look for a v4l2loopback device.
|
||||
name = None
|
||||
device = None
|
||||
for dev in sorted(glob.glob('/dev/video*')):
|
||||
result, name_ = checkAndConfigureV4l2loopback(dev)
|
||||
if result:
|
||||
name = name_
|
||||
device = dev
|
||||
break
|
||||
|
||||
if not (name and device):
|
||||
log.error('Couldn\'t find a v4l2loopback video device')
|
||||
return None
|
||||
|
||||
# Feed it a frame of output so it has something to display
|
||||
subprocess.check_call(['/usr/bin/gst-launch-0.10', 'videotestsrc',
|
||||
'pattern=green', 'num-buffers=1', '!',
|
||||
'v4l2sink', 'device=%s' % device])
|
||||
info['video'] = name
|
||||
|
||||
# Use pactl to see if the PulseAudio module-sine-source module is loaded.
|
||||
def sine_source_loaded():
|
||||
o = subprocess.check_output(['/usr/bin/pactl', 'list', 'short', 'modules'])
|
||||
return filter(lambda x: 'module-sine-source' in x, o.splitlines())
|
||||
|
||||
if not sine_source_loaded():
|
||||
# Load module-sine-source
|
||||
subprocess.check_call(['/usr/bin/pactl', 'load-module',
|
||||
'module-sine-source'])
|
||||
if not sine_source_loaded():
|
||||
log.error('Couldn\'t load module-sine-source')
|
||||
return None
|
||||
|
||||
# Hardcode the name since it's always the same.
|
||||
info['audio'] = 'Sine source at 440 Hz'
|
||||
return info
|
||||
|
||||
class Mochitest(MochitestUtilsMixin):
|
||||
certdbNew = False
|
||||
sslTunnel = None
|
||||
vmwareHelper = None
|
||||
DEFAULT_TIMEOUT = 60.0
|
||||
mediaDevices = None
|
||||
|
||||
# XXX use automation.py for test name to avoid breaking legacy
|
||||
# TODO: replace this with 'runtests.py' or 'mochitest' or the like
|
||||
@ -877,6 +990,11 @@ class Mochitest(MochitestUtilsMixin):
|
||||
'ws': options.sslPort
|
||||
}
|
||||
|
||||
# See if we should use fake media devices.
|
||||
if options.useTestMediaDevices:
|
||||
prefs['media.audio_loopback_dev'] = self.mediaDevices['audio']
|
||||
prefs['media.video_loopback_dev'] = self.mediaDevices['video']
|
||||
|
||||
|
||||
# create a profile
|
||||
self.profile = Profile(profile=options.profilePath,
|
||||
@ -1225,6 +1343,13 @@ class Mochitest(MochitestUtilsMixin):
|
||||
options.debuggerArgs,
|
||||
options.debuggerInteractive)
|
||||
|
||||
if options.useTestMediaDevices:
|
||||
devices = findTestMediaDevices()
|
||||
if not devices:
|
||||
log.error("Could not find test media devices to use")
|
||||
return 1
|
||||
self.mediaDevices = devices
|
||||
|
||||
self.leak_report_file = os.path.join(options.profilePath, "runtests_leaks.log")
|
||||
|
||||
browserEnv = self.buildBrowserEnv(options, debuggerInfo is not None)
|
||||
|
Loading…
x
Reference in New Issue
Block a user