Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(244)

Side by Side Diff: build/android/devil/android/tools/video_recorder.py

Issue 1522313002: [Android] Move screenshot and video_recorder into devil. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix video_recorder timing Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2015 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Captures a video from an Android device."""
7
8 import argparse
9 import logging
10 import os
11 import threading
12 import time
13 import sys
14
15 if __name__ == '__main__':
16 sys.path.append(os.path.abspath(os.path.join(
17 os.path.dirname(__file__), os.pardir, os.pardir, os.pardir)))
18 from devil.android import device_signal
19 from devil.android import device_utils
20 from devil.android.tools import script_common
21 from devil.utils import cmd_helper
22 from devil.utils import reraiser_thread
23 from devil.utils import timeout_retry
24
25
26 class VideoRecorder(object):
27 """Records a screen capture video from an Android Device (KitKat or newer)."""
28
29 def __init__(self, device, megabits_per_second=4, size=None,
30 rotate=False):
31 """Creates a VideoRecorder instance.
32
33 Args:
34 device: DeviceUtils instance.
35 host_file: Path to the video file to store on the host.
36 megabits_per_second: Video bitrate in megabits per second. Allowed range
37 from 0.1 to 100 mbps.
38 size: Video frame size tuple (width, height) or None to use the device
39 default.
40 rotate: If True, the video will be rotated 90 degrees.
41 """
42 self._bit_rate = megabits_per_second * 1000 * 1000
43 self._device = device
44 self._device_file = (
45 '%s/screen-recording.mp4' % device.GetExternalStoragePath())
46 self._recorder_thread = None
47 self._rotate = rotate
48 self._size = size
49 self._started = threading.Event()
50
51 def __enter__(self):
52 self.Start()
53
54 def Start(self, timeout=None):
55 """Start recording video."""
56 def screenrecord_started():
57 return bool(self._device.GetPids('screenrecord'))
58
59 if screenrecord_started():
60 raise Exception("Can't run multiple concurrent video captures.")
61
62 self._started.clear()
63 self._recorder_thread = reraiser_thread.ReraiserThread(self._Record)
64 self._recorder_thread.start()
65 timeout_retry.WaitFor(
66 screenrecord_started, wait_period=1, max_tries=timeout)
67 self._started.wait(timeout)
68
69 def _Record(self):
70 cmd = ['screenrecord', '--verbose', '--bit-rate', str(self._bit_rate)]
71 if self._rotate:
72 cmd += ['--rotate']
73 if self._size:
74 cmd += ['--size', '%dx%d' % self._size]
75 cmd += [self._device_file]
76 for line in self._device.adb.IterShell(
77 ' '.join(cmd_helper.SingleQuote(i) for i in cmd), None):
78 if line.startswith('Content area is '):
79 self._started.set()
80
81 def __exit__(self, _exc_type, _exc_value, _traceback):
82 self.Stop()
83
84 def Stop(self):
85 """Stop recording video."""
86 if not self._device.KillAll('screenrecord', signum=device_signal.SIGINT,
87 quiet=True):
88 logging.warning('Nothing to kill: screenrecord was not running')
89 self._recorder_thread.join()
90
91 def Pull(self, host_file=None):
92 """Pull resulting video file from the device.
93
94 Args:
95 host_file: Path to the video file to store on the host.
96 Returns:
97 Output video file name on the host.
98 """
99 # TODO(jbudorick): Merge filename generation with the logic for doing so in
100 # DeviceUtils.
101 host_file_name = (
102 host_file
103 or 'screen-recording-%s-%s.mp4' % (
104 str(self._device),
105 time.strftime('%Y%m%dT%H%M%S', time.localtime())))
106 host_file_name = os.path.abspath(host_file_name)
107 self._device.PullFile(self._device_file, host_file_name)
108 self._device.RunShellCommand('rm -f "%s"' % self._device_file)
109 return host_file_name
110
111
112 def main():
113 # Parse options.
114 parser = argparse.ArgumentParser(description=__doc__)
115 parser.add_argument('-d', '--device', dest='devices', action='append',
116 help='Serial number of Android device to use.')
117 parser.add_argument('--blacklist-file', help='Device blacklist JSON file.')
118 parser.add_argument('-f', '--file', metavar='FILE',
119 help='Save result to file instead of generating a '
120 'timestamped file name.')
121 parser.add_argument('-v', '--verbose', action='store_true',
122 help='Verbose logging.')
123 parser.add_argument('-b', '--bitrate', default=4, type=float,
124 help='Bitrate in megabits/s, from 0.1 to 100 mbps, '
125 '%default mbps by default.')
126 parser.add_argument('-r', '--rotate', action='store_true',
127 help='Rotate video by 90 degrees.')
128 parser.add_argument('-s', '--size', metavar='WIDTHxHEIGHT',
129 help='Frame size to use instead of the device '
130 'screen size.')
131 parser.add_argument('host_file', nargs='?',
132 help='File to which the video capture will be written.')
133
134 args = parser.parse_args()
135
136 host_file = args.host_file or args.file
137
138 if args.verbose:
139 logging.getLogger().setLevel(logging.DEBUG)
140
141 size = (tuple(int(i) for i in args.size.split('x'))
142 if args.size
143 else None)
144
145 def record_video(device, stop_recording):
146 recorder = VideoRecorder(
147 device, megabits_per_second=args.bitrate, size=size, rotate=args.rotate)
148 with recorder:
149 stop_recording.wait()
150
151 f = None
152 if host_file:
153 root, ext = os.path.splitext(host_file)
154 f = '%s_%s%s' % (root, str(device), ext)
155 f = recorder.Pull(f)
156 print 'Video written to %s' % os.path.abspath(f)
157
158 parallel_devices = device_utils.DeviceUtils.parallel(
159 script_common.GetDevices(args.devices, args.blacklist_file),
160 async=True)
161 stop_recording = threading.Event()
162 running_recording = parallel_devices.pMap(record_video, stop_recording)
163 print 'Recording. Press Enter to stop.',
164 sys.stdout.flush()
165 raw_input()
166 stop_recording.set()
167
168 running_recording.pGet(None)
169 return 0
170
171
172 if __name__ == '__main__':
173 sys.exit(main())
OLDNEW
« no previous file with comments | « build/android/devil/android/tools/script_common_test.py ('k') | build/android/pylib/screenshot.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698