blob: 411fea2cd5a46c5e86bb79281812beee0f11c096 [file] [log] [blame]
kjellanderd2b63cf2017-06-30 03:04:59 -07001#!/usr/bin/env python
2# Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3#
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
6# tree. An additional intellectual property rights grant can be found
7# in the file PATENTS. All contributing project authors may
8# be found in the AUTHORS file in the root of the source tree.
9
Paulina Hensmanede87962018-10-10 15:48:30 +020010import json
kjellanderd2b63cf2017-06-30 03:04:59 -070011import optparse
12import os
Magnus Jedvert3e169ac2018-08-24 12:44:59 +000013import shutil
kjellanderd2b63cf2017-06-30 03:04:59 -070014import subprocess
15import sys
Magnus Jedvert3e169ac2018-08-24 12:44:59 +000016import tempfile
kjellanderd2b63cf2017-06-30 03:04:59 -070017
18
19SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
20
21# Chrome browsertests will throw away stderr; avoid that output gets lost.
22sys.stderr = sys.stdout
23
24
25def _ParseArgs():
26 """Registers the command-line options."""
27 usage = 'usage: %prog [options]'
28 parser = optparse.OptionParser(usage=usage)
29
30 parser.add_option('--label', type='string', default='MY_TEST',
31 help=('Label of the test, used to identify different '
32 'tests. Default: %default'))
33 parser.add_option('--ref_video', type='string',
34 help='Reference video to compare with (YUV).')
35 parser.add_option('--test_video', type='string',
36 help=('Test video to be compared with the reference '
37 'video (YUV).'))
38 parser.add_option('--frame_analyzer', type='string',
39 help='Path to the frame analyzer executable.')
Paulina Hensmanb671d462018-09-14 11:32:00 +020040 parser.add_option('--aligned_output_file', type='string',
41 help='Path for output aligned YUV or Y4M file.')
Paulina Hensman12c62b92018-09-28 15:14:07 +020042 parser.add_option('--vmaf', type='string',
43 help='Path to VMAF executable.')
44 parser.add_option('--vmaf_model', type='string',
45 help='Path to VMAF model.')
46 parser.add_option('--vmaf_phone_model', action='store_true',
47 help='Whether to use phone model in VMAF.')
kjellanderd2b63cf2017-06-30 03:04:59 -070048 parser.add_option('--yuv_frame_width', type='int', default=640,
Magnus Jedvert3e169ac2018-08-24 12:44:59 +000049 help='Width of the YUV file\'s frames. Default: %default')
kjellanderd2b63cf2017-06-30 03:04:59 -070050 parser.add_option('--yuv_frame_height', type='int', default=480,
Magnus Jedvert3e169ac2018-08-24 12:44:59 +000051 help='Height of the YUV file\'s frames. Default: %default')
Edward Lemur2e5966b2018-01-30 15:33:02 +010052 parser.add_option('--chartjson_result_file', type='str', default=None,
53 help='Where to store perf results in chartjson format.')
kjellanderd2b63cf2017-06-30 03:04:59 -070054 options, _ = parser.parse_args()
55
kjellanderd2b63cf2017-06-30 03:04:59 -070056 if not options.ref_video:
57 parser.error('You must provide a path to the reference video!')
58 if not os.path.exists(options.ref_video):
59 parser.error('Cannot find the reference video at %s' % options.ref_video)
60
61 if not options.test_video:
62 parser.error('You must provide a path to the test video!')
63 if not os.path.exists(options.test_video):
64 parser.error('Cannot find the test video at %s' % options.test_video)
65
66 if not options.frame_analyzer:
67 parser.error('You must provide the path to the frame analyzer executable!')
68 if not os.path.exists(options.frame_analyzer):
69 parser.error('Cannot find frame analyzer executable at %s!' %
70 options.frame_analyzer)
Paulina Hensman12c62b92018-09-28 15:14:07 +020071
72 if options.vmaf and not options.vmaf_model:
73 parser.error('You must provide a path to a VMAF model to use VMAF.')
74
kjellanderd2b63cf2017-06-30 03:04:59 -070075 return options
76
77def _DevNull():
78 """On Windows, sometimes the inherited stdin handle from the parent process
79 fails. Workaround this by passing null to stdin to the subprocesses commands.
80 This function can be used to create the null file handler.
81 """
82 return open(os.devnull, 'r')
83
Paulina Hensman12c62b92018-09-28 15:14:07 +020084
Paulina Hensmana6471eb2018-10-05 14:34:33 +020085def _RunFrameAnalyzer(options, yuv_directory=None):
Paulina Hensman12c62b92018-09-28 15:14:07 +020086 """Run frame analyzer to compare the videos and print output."""
87 cmd = [
88 options.frame_analyzer,
89 '--label=%s' % options.label,
90 '--reference_file=%s' % options.ref_video,
91 '--test_file=%s' % options.test_video,
Paulina Hensman12c62b92018-09-28 15:14:07 +020092 '--width=%d' % options.yuv_frame_width,
93 '--height=%d' % options.yuv_frame_height,
94 ]
95 if options.chartjson_result_file:
96 cmd.append('--chartjson_result_file=%s' % options.chartjson_result_file)
97 if options.aligned_output_file:
98 cmd.append('--aligned_output_file=%s' % options.aligned_output_file)
Paulina Hensmana6471eb2018-10-05 14:34:33 +020099 if yuv_directory:
100 cmd.append('--yuv_directory=%s' % yuv_directory)
Paulina Hensman12c62b92018-09-28 15:14:07 +0200101 frame_analyzer = subprocess.Popen(cmd, stdin=_DevNull(),
102 stdout=sys.stdout, stderr=sys.stderr)
103 frame_analyzer.wait()
Paulina Hensmana6471eb2018-10-05 14:34:33 +0200104 if frame_analyzer.returncode != 0:
105 print 'Failed to run frame analyzer.'
Paulina Hensman12c62b92018-09-28 15:14:07 +0200106 return frame_analyzer.returncode
107
108
Paulina Hensmanede87962018-10-10 15:48:30 +0200109def _RunVmaf(options, yuv_directory, logfile):
Paulina Hensman12c62b92018-09-28 15:14:07 +0200110 """ Run VMAF to compare videos and print output.
111
Paulina Hensman12c62b92018-09-28 15:14:07 +0200112 The yuv_directory is assumed to have been populated with a reference and test
113 video in .yuv format, with names according to the label.
114 """
115 cmd = [
116 options.vmaf,
117 'yuv420p',
118 str(options.yuv_frame_width),
119 str(options.yuv_frame_height),
Paulina Hensmana6471eb2018-10-05 14:34:33 +0200120 os.path.join(yuv_directory, "ref.yuv"),
121 os.path.join(yuv_directory, "test.yuv"),
Paulina Hensman12c62b92018-09-28 15:14:07 +0200122 options.vmaf_model,
Paulina Hensmanede87962018-10-10 15:48:30 +0200123 '--log',
124 logfile,
125 '--log-fmt',
126 'json',
Paulina Hensman12c62b92018-09-28 15:14:07 +0200127 ]
128 if options.vmaf_phone_model:
129 cmd.append('--phone-model')
130
131 vmaf = subprocess.Popen(cmd, stdin=_DevNull(),
Paulina Hensmanede87962018-10-10 15:48:30 +0200132 stdout=sys.stdout, stderr=sys.stderr)
Paulina Hensman12c62b92018-09-28 15:14:07 +0200133 vmaf.wait()
134 if vmaf.returncode != 0:
135 print 'Failed to run VMAF.'
136 return 1
Paulina Hensmana6471eb2018-10-05 14:34:33 +0200137
Paulina Hensmanede87962018-10-10 15:48:30 +0200138 # Read per-frame scores from VMAF output and print.
139 with open(logfile) as f:
140 vmaf_data = json.load(f)
141 vmaf_scores = []
142 for frame in vmaf_data['frames']:
143 vmaf_scores.append(frame['metrics']['vmaf'])
144 print 'RESULT VMAF: %s=' % options.label, vmaf_scores
145
Paulina Hensman12c62b92018-09-28 15:14:07 +0200146 return 0
147
148
kjellanderd2b63cf2017-06-30 03:04:59 -0700149def main():
150 """The main function.
151
152 A simple invocation is:
Paulina Hensmanb671d462018-09-14 11:32:00 +0200153 ./webrtc/rtc_tools/compare_videos.py
kjellanderd2b63cf2017-06-30 03:04:59 -0700154 --ref_video=<path_and_name_of_reference_video>
155 --test_video=<path_and_name_of_test_video>
156 --frame_analyzer=<path_and_name_of_the_frame_analyzer_executable>
Magnus Jedvert3e169ac2018-08-24 12:44:59 +0000157
Paulina Hensman12c62b92018-09-28 15:14:07 +0200158 Running vmaf requires the following arguments:
159 --vmaf, --vmaf_model, --yuv_frame_width, --yuv_frame_height
kjellanderd2b63cf2017-06-30 03:04:59 -0700160 """
161 options = _ParseArgs()
162
Paulina Hensmana6471eb2018-10-05 14:34:33 +0200163 if options.vmaf:
164 try:
165 # Directory to save temporary YUV files for VMAF in frame_analyzer.
166 yuv_directory = tempfile.mkdtemp()
Paulina Hensmanede87962018-10-10 15:48:30 +0200167 _, vmaf_logfile = tempfile.mkstemp()
Paulina Hensman12c62b92018-09-28 15:14:07 +0200168
Paulina Hensmana6471eb2018-10-05 14:34:33 +0200169 # Run frame analyzer to compare the videos and print output.
170 if _RunFrameAnalyzer(options, yuv_directory=yuv_directory) != 0:
171 return 1
Paulina Hensman12c62b92018-09-28 15:14:07 +0200172
Paulina Hensmana6471eb2018-10-05 14:34:33 +0200173 # Run VMAF for further video comparison and print output.
Paulina Hensmanede87962018-10-10 15:48:30 +0200174 return _RunVmaf(options, yuv_directory, vmaf_logfile)
Paulina Hensmana6471eb2018-10-05 14:34:33 +0200175 finally:
176 shutil.rmtree(yuv_directory)
Paulina Hensmanede87962018-10-10 15:48:30 +0200177 os.remove(vmaf_logfile)
Paulina Hensmana6471eb2018-10-05 14:34:33 +0200178 else:
179 return _RunFrameAnalyzer(options)
kjellanderd2b63cf2017-06-30 03:04:59 -0700180
kjellanderd2b63cf2017-06-30 03:04:59 -0700181 return 0
182
183if __name__ == '__main__':
184 sys.exit(main())