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

Side by Side Diff: webrtc/tools/run_video_analysis.py

Issue 2746413002: Improve error handling for ffmpeg operations (Closed)
Patch Set: remove debug prints Created 3 years, 9 months 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. 2 # Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
3 # 3 #
4 # Use of this source code is governed by a BSD-style license 4 # Use of this source code is governed by a BSD-style license
5 # that can be found in the LICENSE file in the root of the source 5 # that can be found in the LICENSE file in the root of the source
6 # tree. An additional intellectual property rights grant can be found 6 # tree. An additional intellectual property rights grant can be found
7 # in the file PATENTS. All contributing project authors may 7 # in the file PATENTS. All contributing project authors may
8 # be found in the AUTHORS file in the root of the source tree. 8 # be found in the AUTHORS file in the root of the source tree.
9 9
10 import optparse 10 import optparse
11 import os 11 import os
12 import subprocess 12 import subprocess
13 import sys 13 import sys
14 import time 14 import time
15 import glob 15 import glob
16 import re 16 import re
17 import shutil
17 18
18 # Used to time-stamp output files and directories 19 # Used to time-stamp output files and directories
19 CURRENT_TIME = time.strftime("%d_%m_%Y-%H:%M:%S") 20 CURRENT_TIME = time.strftime("%d_%m_%Y-%H:%M:%S")
20 21
21 def _ParseArgs(): 22 def _ParseArgs():
22 """Registers the command-line options.""" 23 """Registers the command-line options."""
23 usage = 'usage: %prog [options]' 24 usage = 'usage: %prog [options]'
24 parser = optparse.OptionParser(usage=usage) 25 parser = optparse.OptionParser(usage=usage)
25 26
26 parser.add_option('--frame_width', type='string', default='1280', 27 parser.add_option('--frame_width', type='string', default='1280',
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after
173 magewell_usb_ports.append(directory) 174 magewell_usb_ports.append(directory)
174 175
175 test_magewell_path = str(test_magewell_device).split('/') 176 test_magewell_path = str(test_magewell_device).split('/')
176 for directory in test_magewell_path: 177 for directory in test_magewell_path:
177 178
178 # Find the folder with pattern "N-N", e.g. "4-3" or \ 179 # Find the folder with pattern "N-N", e.g. "4-3" or \
179 # "[USB bus ID]-[USB port]" 180 # "[USB bus ID]-[USB port]"
180 if re.match(r'^\d-\d$', directory): 181 if re.match(r'^\d-\d$', directory):
181 magewell_usb_ports.append(directory) 182 magewell_usb_ports.append(directory)
182 183
183 print '\nResetting USB ports where magewell devices are connected...' 184 try:
184 185
kjellander_webrtc 2017/03/14 12:45:26 remove line 185. I think the blank-line-before-com
janssonWebRTC 2017/03/14 13:57:19 Done.
185 # Use the USB bus and port ID (e.g. 4-3) to unbind and bind the USB devices 186 # Abort early if no devices are found.
186 # (i.e. soft eject and insert). 187 if len(magewell_usb_ports) == 0:
187 try: 188 raise Exception('No magewell devices found.')
189
190 print '\nResetting USB ports where magewell devices are connected...'
191
192 # Use the USB bus and port ID (e.g. 4-3) to unbind and bind the USB devices
193 # (i.e. soft eject and insert).
188 for usb_port in magewell_usb_ports: 194 for usb_port in magewell_usb_ports:
189 echo_cmd = ['echo', usb_port] 195 echo_cmd = ['echo', usb_port]
190 unbind_cmd = ['sudo', 'tee', '/sys/bus/usb/drivers/usb/unbind'] 196 unbind_cmd = ['sudo', 'tee', '/sys/bus/usb/drivers/usb/unbind']
191 bind_cmd = ['sudo', 'tee', '/sys/bus/usb/drivers/usb/bind'] 197 bind_cmd = ['sudo', 'tee', '/sys/bus/usb/drivers/usb/bind']
192 198
193 # TODO(jansson) Figure out a way to call on echo once for bind & unbind 199 # TODO(jansson) Figure out a way to call on echo once for bind & unbind
194 # if possible. 200 # if possible.
195 echo_unbind = subprocess.Popen(echo_cmd, stdout=subprocess.PIPE) 201 echo_unbind = subprocess.Popen(echo_cmd, stdout=subprocess.PIPE)
196 unbind = subprocess.Popen(unbind_cmd, stdin=echo_unbind.stdout) 202 unbind = subprocess.Popen(unbind_cmd, stdin=echo_unbind.stdout)
197 echo_unbind.stdout.close() 203 echo_unbind.stdout.close()
198 unbind.communicate() 204 unbind.communicate()
199 unbind.wait() 205 unbind.wait()
200 206
201 echo_bind = subprocess.Popen(echo_cmd, stdout=subprocess.PIPE) 207 echo_bind = subprocess.Popen(echo_cmd, stdout=subprocess.PIPE)
202 bind = subprocess.Popen(bind_cmd, stdin=echo_bind.stdout) 208 bind = subprocess.Popen(bind_cmd, stdin=echo_bind.stdout)
203 echo_bind.stdout.close() 209 echo_bind.stdout.close()
204 bind.communicate() 210 bind.communicate()
205 bind.wait() 211 bind.wait()
206 except OSError as e: 212 except Exception:
kjellander_webrtc 2017/03/14 12:45:26 From http://google.github.io/styleguide/pyguide.ht
janssonWebRTC 2017/03/14 13:57:19 Done.
207 print 'Error while resetting magewell devices: ' + e
208 raise 213 raise
209 214 else:
210 print 'Reset done!\n' 215 print 'Reset done!\n'
211 216
212 217
213 def StartRecording(options, record_paths): 218 def StartRecording(options, ref_file_location, test_file_location):
214 """Starts recording from the two specified video devices. 219 """Starts recording from the two specified video devices.
215 220
216 Args: 221 Args:
217 options(object): Contains all the provided command line options. 222 options(object): Contains all the provided command line options.
218 record_paths(dict): key: value pair with reference and test file 223 record_paths(dict): key: value pair with reference and test file
219 absolute paths. 224 absolute paths.
220 """ 225 """
221 ref_file_name = '%s_%s_ref.%s' % (options.app_name, CURRENT_TIME, 226 ref_file_name = '%s_%s_ref.%s' % (options.app_name, CURRENT_TIME,
222 options.video_container) 227 options.video_container)
223 ref_file_location = os.path.join(record_paths['ref_rec_location'], 228 ref_file = os.path.join(ref_file_location, ref_file_name)
224 ref_file_name)
225 229
226 test_file_name = '%s_%s_test.%s' % (options.app_name, CURRENT_TIME, 230 test_file_name = '%s_%s_test.%s' % (options.app_name, CURRENT_TIME,
227 options.video_container) 231 options.video_container)
228 test_file_location = os.path.join(record_paths['test_rec_location'], 232 test_file = os.path.join(test_file_location, test_file_name)
229 test_file_name)
230 233
231 # Reference video recorder command line. 234 # Reference video recorder command line.
232 ref_cmd = [ 235 ref_cmd = [
233 options.ffmpeg, 236 options.ffmpeg,
234 '-v', 'error', 237 '-v', 'error',
235 '-s', options.frame_width + 'x' + options.frame_height, 238 '-s', options.frame_width + 'x' + options.frame_height,
236 '-framerate', options.framerate, 239 '-framerate', options.framerate,
237 '-f', options.recording_api, 240 '-f', options.recording_api,
238 '-i', options.ref_video_device, 241 '-i', options.ref_video_device,
239 '-pix_fmt', options.pixel_format, 242 '-pix_fmt', options.pixel_format,
240 '-s', options.frame_width + 'x' + options.frame_height, 243 '-s', options.frame_width + 'x' + options.frame_height,
241 '-t', options.ref_duration, 244 '-t', options.ref_duration,
242 '-framerate', options.framerate, 245 '-framerate', options.framerate,
243 ref_file_location 246 ref_file
244 ] 247 ]
245 248
246 # Test video recorder command line. 249 # Test video recorder command line.
247 test_cmd = [ 250 test_cmd = [
248 options.ffmpeg, 251 options.ffmpeg,
249 '-v', 'error', 252 '-v', 'error',
250 '-s', options.frame_width + 'x' + options.frame_height, 253 '-s', options.frame_width + 'x' + options.frame_height,
251 '-framerate', options.framerate, 254 '-framerate', options.framerate,
252 '-f', options.recording_api, 255 '-f', options.recording_api,
253 '-i', options.test_video_device, 256 '-i', options.test_video_device,
254 '-pix_fmt', options.pixel_format, 257 '-pix_fmt', options.pixel_format,
255 '-s', options.frame_width + 'x' + options.frame_height, 258 '-s', options.frame_width + 'x' + options.frame_height,
256 '-t', options.test_duration, 259 '-t', options.test_duration,
257 '-framerate', options.framerate, 260 '-framerate', options.framerate,
258 test_file_location 261 test_file
259 ] 262 ]
260 print 'Trying to record from reference recorder...' 263 try:
261 ref_recorder = subprocess.Popen(ref_cmd, stderr=sys.stderr) 264 print 'Trying to record from reference recorder...'
265 ref_recorder = subprocess.Popen(ref_cmd, stderr=sys.stderr)
262 266
263 # Start the 2nd recording a little later to ensure the 1st one has started. 267 # Start the 2nd recording a little later to ensure the 1st one has started.
264 # TODO(jansson) Check that the ref_recorder output file exists rather than 268 # TODO(jansson) Check that the ref_recorder output file exists rather than
265 # using sleep. 269 # using sleep.
266 time.sleep(options.time_between_recordings) 270 time.sleep(options.time_between_recordings)
267 print 'Trying to record from test recorder...' 271 print 'Trying to record from test recorder...'
268 test_recorder = subprocess.Popen(test_cmd, stderr=sys.stderr) 272 test_recorder = subprocess.Popen(test_cmd, stderr=sys.stderr)
269 test_recorder.wait() 273 test_recorder.wait()
270 ref_recorder.wait() 274 ref_recorder.wait()
271 275
272 # ffmpeg does not abort when it fails, need to check return code. 276 # ffmpeg does not abort when it fails, need to check return code.
273 assert ref_recorder.returncode == 0, ( 277 if ref_recorder.returncode != 0 or test_recorder.returncode != 0:
274 'Ref recording failed, check ffmpeg output and device: %s'
275 % options.ref_video_device)
276 assert test_recorder.returncode == 0, (
277 'Test recording failed, check ffmpeg output and device: %s'
278 % options.test_video_device)
279 278
280 print 'Ref file recorded to: ' + os.path.abspath(ref_file_location) 279 # Cleanup recording directories.
281 print 'Test file recorded to: ' + os.path.abspath(test_file_location) 280 shutil.rmtree(ref_file_location)
282 print 'Recording done!\n' 281 shutil.rmtree(test_file_location)
283 return FlipAndCropRecordings(options, test_file_name, 282 raise Exception('Recording failed, check ffmpeg output')
kjellander_webrtc 2017/03/14 12:45:26 Introduce your own exception in this module. Fro
janssonWebRTC 2017/03/14 13:57:19 Done.
284 record_paths['test_rec_location'], ref_file_name, 283 except Exception:
kjellander_webrtc 2017/03/14 12:45:26 Same here as above.
janssonWebRTC 2017/03/14 13:57:19 Done.
285 record_paths['ref_rec_location']) 284 raise
285 else:
286 print 'Ref file recorded to: ' + os.path.abspath(ref_file)
287 print 'Test file recorded to: ' + os.path.abspath(test_file)
288 print 'Recording done!\n'
289 return FlipAndCropRecordings(options, test_file_name,
290 test_file_location, ref_file_name,
291 ref_file_location)
kjellander_webrtc 2017/03/14 12:45:26 Fix indentation/wrapping.
janssonWebRTC 2017/03/14 13:57:19 Done.
292
286 293
287 294
288 def FlipAndCropRecordings(options, test_file_name, test_file_location, 295 def FlipAndCropRecordings(options, test_file_name, test_file_location,
289 ref_file_name, ref_file_location): 296 ref_file_name, ref_file_location):
290 """Performs a horizontal flip of the reference video to match the test video. 297 """Performs a horizontal flip of the reference video to match the test video.
291 298
292 This is done to the match orientation and then crops the ref and test videos 299 This is done to the match orientation and then crops the ref and test videos
293 using the options.test_crop_parameters and options.ref_crop_parameters. 300 using the options.test_crop_parameters and options.ref_crop_parameters.
294 301
295 Args: 302 Args:
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
327 test_video_crop_cmd = [ 334 test_video_crop_cmd = [
328 options.ffmpeg, 335 options.ffmpeg,
329 '-v', 'error', 336 '-v', 'error',
330 '-s', options.frame_width + 'x' + options.frame_height, 337 '-s', options.frame_width + 'x' + options.frame_height,
331 '-i', os.path.join(test_file_location, test_file_name), 338 '-i', os.path.join(test_file_location, test_file_name),
332 '-vf', options.test_crop_parameters, 339 '-vf', options.test_crop_parameters,
333 '-c:a', 'copy', 340 '-c:a', 'copy',
334 cropped_test_file 341 cropped_test_file
335 ] 342 ]
336 343
337 ref_crop = subprocess.Popen(ref_video_crop_cmd)
338 ref_crop.wait()
339 print 'Ref file cropped to: ' + cropped_ref_file
340
341 try: 344 try:
345 ref_crop = subprocess.Popen(ref_video_crop_cmd)
346 ref_crop.wait()
342 test_crop = subprocess.Popen(test_video_crop_cmd) 347 test_crop = subprocess.Popen(test_video_crop_cmd)
343 test_crop.wait() 348 test_crop.wait()
349
350 # ffmpeg does not abort when it fails, need to check return code.
351 if ref_crop.returncode != 0 or test_crop.returncode != 0:
352
353 # Cleanup recording directories.
354 shutil.rmtree(ref_file_location)
355 shutil.rmtree(test_file_location)
356 raise Exception('Cropping failed, check ffmpeg output')
kjellander_webrtc 2017/03/14 12:45:26 raise a module-specific exception instead of the g
janssonWebRTC 2017/03/14 13:57:19 Done.
357 except Exception:
kjellander_webrtc 2017/03/14 12:45:26 Never catch Exception (see above)
janssonWebRTC 2017/03/14 13:57:19 Done.
358 raise
359 else:
360 print 'Ref file cropped to: ' + cropped_ref_file
344 print 'Test file cropped to: ' + cropped_test_file 361 print 'Test file cropped to: ' + cropped_test_file
345 print 'Cropping done!\n' 362 print 'Cropping done!\n'
346 363
347 # Need to return these so they can be used by other parts. 364 # Need to return these so they can be used by other parts.
348 cropped_recordings = { 365 cropped_recordings = {
349 'cropped_test_file' : cropped_test_file, 366 'cropped_test_file' : cropped_test_file,
350 'cropped_ref_file' : cropped_ref_file 367 'cropped_ref_file' : cropped_ref_file
351 } 368 }
352
353 return cropped_recordings 369 return cropped_recordings
354 except subprocess.CalledProcessError as e:
355 print 'Something went wrong during cropping: ' + e
356 raise
357 370
358 371
359 def CompareVideos(options, recording_result): 372 def CompareVideos(options, recording_result):
360 """Runs the compare_video.py script from src/webrtc/tools using the file path. 373 """Runs the compare_video.py script from src/webrtc/tools using the file path.
361 374
362 Uses the path from recording_result and writes the output to a file named 375 Uses the path from recording_result and writes the output to a file named
363 <options.app_name + '_' + CURRENT_TIME + '_result.txt> in the reference video 376 <options.app_name + '_' + CURRENT_TIME + '_result.txt> in the reference video
364 recording folder taken from recording_result. 377 recording folder taken from recording_result.
365 378
366 Args: 379 Args:
367 options(object): Contains all the provided command line options. 380 options(object): Contains all the provided command line options.
368 recording_files_and_time(dict): key: value pair with the path to cropped 381 recording_files_and_time(dict): key: value pair with the path to cropped
369 test and reference video files 382 test and reference video files
370 """ 383 """
371 print 'Starting comparison...' 384 print 'Starting comparison...'
372 print 'Grab a coffee, this might take a few minutes...' 385 print 'Grab a coffee, this might take a few minutes...'
373 cropped_ref_file = recording_result['cropped_ref_file'] 386 cropped_ref_file = recording_result['cropped_ref_file']
374 cropped_test_file = recording_result['cropped_test_file'] 387 cropped_test_file = recording_result['cropped_test_file']
375 compare_videos_script = os.path.abspath(options.compare_videos_script) 388 compare_videos_script = os.path.abspath(options.compare_videos_script)
376 rec_path = os.path.abspath(os.path.join( 389 rec_path = os.path.abspath(os.path.join(
377 os.path.dirname(recording_result['cropped_ref_file']))) 390 os.path.dirname(recording_result['cropped_ref_file'])))
378 result_file_name = os.path.join(rec_path, '%s_%s_result.txt') % ( 391 result_file_name = os.path.join(rec_path, '%s_%s_result.txt') % (
379 options.app_name, CURRENT_TIME) 392 options.app_name, CURRENT_TIME)
380 393
381 # Find the crop dimensions (950 and 420) in the ref crop parameter string: 394 # Find the crop dimensions (e.g. 950 and 420) in the ref crop parameter
382 # 'hflip, crop=950:420:130:56' 395 # string: 'hflip, crop=950:420:130:56'
383 for param in options.ref_crop_parameters.split('crop'): 396 for param in options.ref_crop_parameters.split('crop'):
384 if param[0] == '=': 397 if param[0] == '=':
385 crop_width = param.split(':')[0].split('=')[1] 398 crop_width = param.split(':')[0].split('=')[1]
386 crop_height = param.split(':')[1] 399 crop_height = param.split(':')[1]
387 400
388 compare_cmd = [ 401 compare_cmd = [
389 sys.executable, 402 sys.executable,
390 compare_videos_script, 403 compare_videos_script,
391 '--ref_video', cropped_ref_file, 404 '--ref_video', cropped_ref_file,
392 '--test_video', cropped_test_file, 405 '--test_video', cropped_test_file,
393 '--frame_analyzer', os.path.abspath(options.frame_analyzer), 406 '--frame_analyzer', os.path.abspath(options.frame_analyzer),
394 '--zxing_path', options.zxing_path, 407 '--zxing_path', options.zxing_path,
395 '--ffmpeg_path', options.ffmpeg, 408 '--ffmpeg_path', options.ffmpeg,
396 '--stats_file_ref', os.path.join(os.path.dirname(cropped_ref_file), 409 '--stats_file_ref', os.path.join(os.path.dirname(cropped_ref_file),
397 cropped_ref_file + '_stats.txt'), 410 cropped_ref_file + '_stats.txt'),
398 '--stats_file_test', os.path.join(os.path.dirname(cropped_test_file), 411 '--stats_file_test', os.path.join(os.path.dirname(cropped_test_file),
399 cropped_test_file + '_stats.txt'), 412 cropped_test_file + '_stats.txt'),
400 '--yuv_frame_height', crop_height, 413 '--yuv_frame_height', crop_height,
401 '--yuv_frame_width', crop_width 414 '--yuv_frame_width', crop_width
402 ] 415 ]
403 416
404 try: 417 try:
405 with open(result_file_name, 'w') as f: 418 with open(result_file_name, 'w') as f:
406 compare_video_recordings = subprocess.Popen(compare_cmd, stdout=f) 419 compare_video_recordings = subprocess.Popen(compare_cmd, stdout=f)
407 compare_video_recordings.wait() 420 compare_video_recordings.wait()
408 print 'Result recorded to: ' + os.path.abspath(result_file_name) 421 except Exception as e:
kjellander_webrtc 2017/03/14 12:45:26 Don't catch Exception here.
janssonWebRTC 2017/03/14 13:57:19 Done.
409 print 'Comparison done!'
410 except subprocess.CalledProcessError as e:
411 print 'Something went wrong when trying to compare videos: ' + e 422 print 'Something went wrong when trying to compare videos: ' + e
412 raise 423 raise
424 else:
425 print 'Result recorded to: ' + os.path.abspath(result_file_name)
426 print 'Comparison done!'
413 427
414 428
415 def main(): 429 def main():
416 """The main function. 430 """The main function.
417 431
418 A simple invocation is: 432 A simple invocation is:
419 ./run_video_analysis.py \ 433 ./run_video_analysis.py \
420 --app_name AppRTCMobile \ 434 --app_name AppRTCMobile \
421 --ffmpeg ./ffmpeg --ref_video_device=/dev/video0 \ 435 --ffmpeg ./ffmpeg --ref_video_device=/dev/video0 \
422 --test_video_device=/dev/video1 \ 436 --test_video_device=/dev/video1 \
(...skipping 12 matching lines...) Expand all
435 /tmp/ref/cropped_AppRTCMobile_<recording date and time>_ref.yuv 449 /tmp/ref/cropped_AppRTCMobile_<recording date and time>_ref.yuv
436 /tmp/test/cropped_AppRTCMobile_<recording date and time>_ref.yuv 450 /tmp/test/cropped_AppRTCMobile_<recording date and time>_ref.yuv
437 451
438 # Comparison metrics from cropped test and ref videos. 452 # Comparison metrics from cropped test and ref videos.
439 /tmp/test/AppRTCMobile_<recording date and time>_result.text 453 /tmp/test/AppRTCMobile_<recording date and time>_result.text
440 454
441 """ 455 """
442 options = _ParseArgs() 456 options = _ParseArgs()
443 RestartMagewellDevices(options.ref_video_device, options.test_video_device) 457 RestartMagewellDevices(options.ref_video_device, options.test_video_device)
444 record_paths = CreateRecordingDirs(options) 458 record_paths = CreateRecordingDirs(options)
445 recording_result = StartRecording(options, record_paths) 459 recording_result = StartRecording(options, record_paths['ref_rec_location'],
460 record_paths['test_rec_location'])
446 461
447 # Do not require compare_video.py script to run, no metrics will be generated. 462 # Do not require compare_video.py script to run, no metrics will be generated.
448 if options.compare_videos_script: 463 if options.compare_videos_script:
449 CompareVideos(options, recording_result) 464 CompareVideos(options, recording_result)
450 else: 465 else:
451 print ('Skipping compare videos step due to compare_videos flag were not ' 466 print ('Skipping compare videos step due to compare_videos flag were not '
452 'passed.') 467 'passed.')
453 468
454 469
455 if __name__ == '__main__': 470 if __name__ == '__main__':
456 sys.exit(main()) 471 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698