From cd469a4ce5315fee62bb77b5e781a18a99da506d Mon Sep 17 00:00:00 2001 From: Oleh Prypin Date: Thu, 17 May 2018 12:15:25 +0200 Subject: [PATCH] Add support for launching webcam software for use in recipes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Copy ensure_webcam_is_running.py from recipes repo * Turn it into a wrapper that can launch another script (fix_python_path is copied from test_env.py as _ForcePythonInterpreter) * Support it in mb.py * Add it to video_capture_unittests No-Try: True Bug: chromium:755660 Change-Id: I376724a77e443620724add7818592e9368d02079 Reviewed-on: https://webrtc-review.googlesource.com/77320 Commit-Queue: Oleh Prypin Reviewed-by: Patrik Höglund Cr-Commit-Position: refs/heads/master@{#23275} --- tools_webrtc/ensure_webcam_is_running.py | 123 +++++++++++++++++++++++ tools_webrtc/mb/gn_isolate_map.pyl | 1 + tools_webrtc/mb/mb.py | 14 +-- tools_webrtc/mb/mb_unittest.py | 51 ++++++++++ 4 files changed, 183 insertions(+), 6 deletions(-) create mode 100755 tools_webrtc/ensure_webcam_is_running.py diff --git a/tools_webrtc/ensure_webcam_is_running.py b/tools_webrtc/ensure_webcam_is_running.py new file mode 100755 index 0000000000..952ebd61b6 --- /dev/null +++ b/tools_webrtc/ensure_webcam_is_running.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python +# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. +# +# Use of this source code is governed by a BSD-style license +# that can be found in the LICENSE file in the root of the source +# tree. An additional intellectual property rights grant can be found +# in the file PATENTS. All contributing project authors may +# be found in the AUTHORS file in the root of the source tree. + +"""Checks if a virtual webcam is running and starts it if not. + +Returns a non-zero return code if the webcam could not be started. + +Prerequisites: +* The Python interpreter must have the psutil package installed. +* Windows: a scheduled task named 'ManyCam' must exist and be configured to + launch ManyCam preconfigured to auto-play the test clip. +* Mac: ManyCam must be installed in the default location and be preconfigured + to auto-play the test clip. +* Linux: The v4l2loopback kernel module must be compiled and loaded to the + kernel already and the v4l2_file_player application must be compiled and put + in the location specified below. + +NOTICE: When running this script as a buildbot step, make sure to set +usePTY=False for the build step when adding it, or the subprocess will die as +soon the step has executed. + +If any command line arguments are passed to the script, it is executed as a +command in a subprocess. +""" + +import os +# psutil is not installed on non-Linux machines by default. +import psutil # pylint: disable=F0401 +import subprocess +import sys +import time + + +WEBCAM_WIN = ('schtasks', '/run', '/tn', 'ManyCam') +WEBCAM_MAC = ('open', '/Applications/ManyCam/ManyCam.app') +E = os.path.expandvars +WEBCAM_LINUX = ( + E('$HOME/fake-webcam-driver/linux/v4l2_file_player/v4l2_file_player'), + E('$HOME/webrtc_video_quality/reference_video.yuv'), + '640', '480', '/dev/video0', +) + + +def IsWebCamRunning(): + if sys.platform == 'win32': + process_name = 'ManyCam.exe' + elif sys.platform.startswith('darwin'): + process_name = 'ManyCam' + elif sys.platform.startswith('linux'): + process_name = 'v4l2_file_player' + else: + raise Exception('Unsupported platform: %s' % sys.platform) + for p in psutil.process_iter(): + try: + if process_name == p.name: + print 'Found a running virtual webcam (%s with PID %s)' % (p.name, + p.pid) + return True + except psutil.AccessDenied: + pass # This is normal if we query sys processes, etc. + return False + + +def StartWebCam(): + try: + if sys.platform == 'win32': + subprocess.check_call(WEBCAM_WIN) + print 'Successfully launched virtual webcam.' + elif sys.platform.startswith('darwin'): + subprocess.check_call(WEBCAM_MAC) + print 'Successfully launched virtual webcam.' + elif sys.platform.startswith('linux'): + + # Must redirect stdout/stderr/stdin to avoid having the subprocess + # being killed when the parent shell dies (happens on the bots). + process = subprocess.Popen(WEBCAM_LINUX, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=subprocess.PIPE) + # If the v4l2loopback module is not loaded or incorrectly configured, + # the process will still launch but will die immediately. + # Wait for a second and then check for aliveness to catch such errors. + time.sleep(1) + if process.poll() is None: + print 'Successfully launched virtual webcam with PID %s' % process.pid + else: + print 'Failed to launch virtual webcam.' + return False + + except Exception as e: + print 'Failed to launch virtual webcam: %s' % e + return False + + return True + + +def _ForcePythonInterpreter(cmd): + """Returns the fixed command line to call the right python executable.""" + out = cmd[:] + if out[0] == 'python': + out[0] = sys.executable + elif out[0].endswith('.py'): + out.insert(0, sys.executable) + return out + + +def Main(argv): + if IsWebCamRunning(): + return 0 + if not StartWebCam(): + return 1 + + if argv: + return subprocess.call(_ForcePythonInterpreter(argv)) + + +if __name__ == '__main__': + sys.exit(Main(sys.argv[1:])) diff --git a/tools_webrtc/mb/gn_isolate_map.pyl b/tools_webrtc/mb/gn_isolate_map.pyl index ef18910340..848a9b38e4 100644 --- a/tools_webrtc/mb/gn_isolate_map.pyl +++ b/tools_webrtc/mb/gn_isolate_map.pyl @@ -121,6 +121,7 @@ "video_capture_tests": { "label": "//modules/video_capture:video_capture_tests", "type": "console_test_launcher", + "use_webcam": True, }, "video_engine_tests": { "label": "//:video_engine_tests", diff --git a/tools_webrtc/mb/mb.py b/tools_webrtc/mb/mb.py index 02910662db..e8883673f8 100755 --- a/tools_webrtc/mb/mb.py +++ b/tools_webrtc/mb/mb.py @@ -844,18 +844,20 @@ class MetaBuildWrapper(object): else: extra_files = ['../../testing/test_env.py'] + if isolate_map[target].get('use_webcam', False): + cmdline.append('../../tools_webrtc/ensure_webcam_is_running.py') + extra_files.append('../../tools_webrtc/ensure_webcam_is_running.py') + # This needs to mirror the settings in //build/config/ui.gni: # use_x11 = is_linux && !use_ozone. use_x11 = is_linux and not 'use_ozone=true' in vals['gn_args'] xvfb = use_x11 and test_type == 'windowed_test_launcher' if xvfb: - extra_files += [ - '../../testing/xvfb.py', - ] - - cmdline = (['../../testing/xvfb.py'] if xvfb else - ['../../testing/test_env.py']) + cmdline.append('../../testing/xvfb.py') + extra_files.append('../../testing/xvfb.py') + else: + cmdline.append('../../testing/test_env.py') # Memcheck is only supported for linux. Ignore in other platforms. if is_linux and 'rtc_use_memcheck=true' in vals['gn_args']: diff --git a/tools_webrtc/mb/mb_unittest.py b/tools_webrtc/mb/mb_unittest.py index 6dea9b8f18..379a56d128 100755 --- a/tools_webrtc/mb/mb_unittest.py +++ b/tools_webrtc/mb/mb_unittest.py @@ -711,6 +711,57 @@ class UnitTest(unittest.TestCase): '--tsan=0', ]) + def test_isolate_test_launcher_with_webcam(self): + test_files = { + '/tmp/swarming_targets': 'base_unittests\n', + '/fake_src/testing/buildbot/gn_isolate_map.pyl': ( + "{'base_unittests': {" + " 'label': '//base:base_unittests'," + " 'type': 'console_test_launcher'," + " 'use_webcam': True," + "}}\n" + ), + '/fake_src/out/Default/base_unittests.runtime_deps': ( + "base_unittests\n" + "some_resource_file\n" + ), + } + mbw = self.check(['gen', '-c', 'debug_goma', '//out/Default', + '--swarming-targets-file', '/tmp/swarming_targets', + '--isolate-map-file', + '/fake_src/testing/buildbot/gn_isolate_map.pyl'], + files=test_files, ret=0) + + isolate_file = mbw.files['/fake_src/out/Default/base_unittests.isolate'] + isolate_file_contents = ast.literal_eval(isolate_file) + files = isolate_file_contents['variables']['files'] + command = isolate_file_contents['variables']['command'] + + self.assertEqual(files, [ + '../../testing/test_env.py', + '../../third_party/gtest-parallel/gtest-parallel', + '../../third_party/gtest-parallel/gtest_parallel.py', + '../../tools_webrtc/ensure_webcam_is_running.py', + '../../tools_webrtc/gtest-parallel-wrapper.py', + 'base_unittests', + 'some_resource_file', + ]) + self.assertEqual(command, [ + '../../tools_webrtc/ensure_webcam_is_running.py', + '../../testing/test_env.py', + '../../tools_webrtc/gtest-parallel-wrapper.py', + '--output_dir=${ISOLATED_OUTDIR}/test_logs', + '--gtest_color=no', + '--timeout=900', + '--retry_failed=3', + './base_unittests', + '--', + '--asan=0', + '--lsan=0', + '--msan=0', + '--tsan=0', + ]) + def test_isolate(self): files = { '/fake_src/out/Default/toolchain.ninja': "",