blob: bdb0bea01464b10eee9333248ba1d4425fb645f9 [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
10import optparse
11import os
Magnus Jedvert3e169ac2018-08-24 12:44:59 +000012import shutil
kjellanderd2b63cf2017-06-30 03:04:59 -070013import subprocess
14import sys
Magnus Jedvert3e169ac2018-08-24 12:44:59 +000015import tempfile
kjellanderd2b63cf2017-06-30 03:04:59 -070016
17
18SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
19
20# Chrome browsertests will throw away stderr; avoid that output gets lost.
21sys.stderr = sys.stdout
22
23
24def _ParseArgs():
25 """Registers the command-line options."""
26 usage = 'usage: %prog [options]'
27 parser = optparse.OptionParser(usage=usage)
28
29 parser.add_option('--label', type='string', default='MY_TEST',
30 help=('Label of the test, used to identify different '
31 'tests. Default: %default'))
32 parser.add_option('--ref_video', type='string',
33 help='Reference video to compare with (YUV).')
34 parser.add_option('--test_video', type='string',
35 help=('Test video to be compared with the reference '
36 'video (YUV).'))
37 parser.add_option('--frame_analyzer', type='string',
38 help='Path to the frame analyzer executable.')
Paulina Hensmanb671d462018-09-14 11:32:00 +020039 parser.add_option('--aligned_output_file', type='string',
40 help='Path for output aligned YUV or Y4M file.')
Paulina Hensmane307d562018-09-28 08:24:57 +020041 parser.add_option('--vmaf', type='string',
42 help='Path to VMAF executable.')
43 parser.add_option('--vmaf_model', type='string',
44 help='Path to VMAF model.')
45 parser.add_option('--vmaf_phone_model', action='store_true',
46 help='Whether to use phone model in VMAF.')
kjellanderd2b63cf2017-06-30 03:04:59 -070047 parser.add_option('--barcode_decoder', type='string',
Magnus Jedvert3e169ac2018-08-24 12:44:59 +000048 help=('Path to the barcode decoder script. By default, we '
49 'will assume we can find it in barcode_tools/'
50 'relative to this directory.'))
kjellanderd2b63cf2017-06-30 03:04:59 -070051 parser.add_option('--ffmpeg_path', type='string',
Magnus Jedvert3e169ac2018-08-24 12:44:59 +000052 help=('The path to where the ffmpeg executable is located. '
53 'If omitted, it will be assumed to be present in the '
54 'PATH with the name ffmpeg[.exe].'))
kjellanderd2b63cf2017-06-30 03:04:59 -070055 parser.add_option('--zxing_path', type='string',
Magnus Jedvert3e169ac2018-08-24 12:44:59 +000056 help=('The path to where the zxing executable is located. '
57 'If omitted, it will be assumed to be present in the '
58 'PATH with the name zxing[.exe].'))
kjellanderd2b63cf2017-06-30 03:04:59 -070059 parser.add_option('--stats_file_ref', type='string', default='stats_ref.txt',
Magnus Jedvert3e169ac2018-08-24 12:44:59 +000060 help=('Path to the temporary stats file to be created and '
61 'used for the reference video file. '
62 'Default: %default'))
kjellanderd2b63cf2017-06-30 03:04:59 -070063 parser.add_option('--stats_file_test', type='string',
Magnus Jedvert3e169ac2018-08-24 12:44:59 +000064 default='stats_test.txt',
65 help=('Path to the temporary stats file to be created and '
66 'used for the test video file. Default: %default'))
kjellanderd2b63cf2017-06-30 03:04:59 -070067 parser.add_option('--stats_file', type='string',
68 help=('DEPRECATED'))
69 parser.add_option('--yuv_frame_width', type='int', default=640,
Magnus Jedvert3e169ac2018-08-24 12:44:59 +000070 help='Width of the YUV file\'s frames. Default: %default')
kjellanderd2b63cf2017-06-30 03:04:59 -070071 parser.add_option('--yuv_frame_height', type='int', default=480,
Magnus Jedvert3e169ac2018-08-24 12:44:59 +000072 help='Height of the YUV file\'s frames. Default: %default')
Edward Lemur2e5966b2018-01-30 15:33:02 +010073 parser.add_option('--chartjson_result_file', type='str', default=None,
74 help='Where to store perf results in chartjson format.')
kjellanderd2b63cf2017-06-30 03:04:59 -070075 options, _ = parser.parse_args()
76
Magnus Jedvert3e169ac2018-08-24 12:44:59 +000077 if options.stats_file:
78 options.stats_file_test = options.stats_file
79 print ('WARNING: Using deprecated switch --stats_file. '
80 'The new flag is --stats_file_test.')
81
kjellanderd2b63cf2017-06-30 03:04:59 -070082 if not options.ref_video:
83 parser.error('You must provide a path to the reference video!')
84 if not os.path.exists(options.ref_video):
85 parser.error('Cannot find the reference video at %s' % options.ref_video)
86
87 if not options.test_video:
88 parser.error('You must provide a path to the test video!')
89 if not os.path.exists(options.test_video):
90 parser.error('Cannot find the test video at %s' % options.test_video)
91
92 if not options.frame_analyzer:
93 parser.error('You must provide the path to the frame analyzer executable!')
94 if not os.path.exists(options.frame_analyzer):
95 parser.error('Cannot find frame analyzer executable at %s!' %
96 options.frame_analyzer)
Paulina Hensmane307d562018-09-28 08:24:57 +020097
98 if options.vmaf and not options.vmaf_model:
99 parser.error('You must provide a path to a VMAF model to use VMAF.')
100
kjellanderd2b63cf2017-06-30 03:04:59 -0700101 return options
102
103def _DevNull():
104 """On Windows, sometimes the inherited stdin handle from the parent process
105 fails. Workaround this by passing null to stdin to the subprocesses commands.
106 This function can be used to create the null file handler.
107 """
108 return open(os.devnull, 'r')
109
Magnus Jedvert3e169ac2018-08-24 12:44:59 +0000110def DecodeBarcodesInVideo(options, path_to_decoder, video, stat_file):
111 # Run barcode decoder on the test video to identify frame numbers.
112 png_working_directory = tempfile.mkdtemp()
113 cmd = [
114 sys.executable,
115 path_to_decoder,
116 '--yuv_file=%s' % video,
117 '--yuv_frame_width=%d' % options.yuv_frame_width,
118 '--yuv_frame_height=%d' % options.yuv_frame_height,
119 '--stats_file=%s' % stat_file,
120 '--png_working_dir=%s' % png_working_directory,
121 ]
122 if options.zxing_path:
123 cmd.append('--zxing_path=%s' % options.zxing_path)
124 if options.ffmpeg_path:
125 cmd.append('--ffmpeg_path=%s' % options.ffmpeg_path)
126
127
128 barcode_decoder = subprocess.Popen(cmd, stdin=_DevNull(),
129 stdout=sys.stdout, stderr=sys.stderr)
130 barcode_decoder.wait()
131
132 shutil.rmtree(png_working_directory)
133 if barcode_decoder.returncode != 0:
134 print 'Failed to run barcode decoder script.'
135 return 1
136 return 0
137
Paulina Hensmane307d562018-09-28 08:24:57 +0200138
139def _RunFrameAnalyzer(options):
140 """Run frame analyzer to compare the videos and print output."""
141 cmd = [
142 options.frame_analyzer,
143 '--label=%s' % options.label,
144 '--reference_file=%s' % options.ref_video,
145 '--test_file=%s' % options.test_video,
146 '--stats_file_ref=%s' % options.stats_file_ref,
147 '--stats_file_test=%s' % options.stats_file_test,
148 '--width=%d' % options.yuv_frame_width,
149 '--height=%d' % options.yuv_frame_height,
150 ]
151 if options.chartjson_result_file:
152 cmd.append('--chartjson_result_file=%s' % options.chartjson_result_file)
153 if options.aligned_output_file:
154 cmd.append('--aligned_output_file=%s' % options.aligned_output_file)
155 if options.yuv_directory:
156 cmd.append('--yuv_directory=%s' % options.yuv_directory)
157 frame_analyzer = subprocess.Popen(cmd, stdin=_DevNull(),
158 stdout=sys.stdout, stderr=sys.stderr)
159 frame_analyzer.wait()
160 return frame_analyzer.returncode
161
162
163def _RunVmaf(options):
164 """ Run VMAF to compare videos and print output.
165
166 The provided vmaf directory is assumed to contain a c++ wrapper executable
167 and a model.
168
169 The yuv_directory is assumed to have been populated with a reference and test
170 video in .yuv format, with names according to the label.
171 """
172 cmd = [
173 options.vmaf,
174 'yuv420p',
175 str(options.yuv_frame_width),
176 str(options.yuv_frame_height),
177 os.path.join(options.yuv_directory, "ref.yuv"),
178 os.path.join(options.yuv_directory, "test.yuv"),
179 options.vmaf_model,
180 ]
181 if options.vmaf_phone_model:
182 cmd.append('--phone-model')
183
184 vmaf = subprocess.Popen(cmd, stdin=_DevNull(),
185 stdout=subprocess.PIPE, stderr=sys.stderr)
186 vmaf.wait()
187 if vmaf.returncode != 0:
188 print 'Failed to run VMAF.'
189 return 1
190 output = vmaf.stdout.read()
191 # Extract score from VMAF output.
192 score = float(output.split('\n')[2].split()[3])
193 print 'RESULT Vmaf: %s= %f' % (options.label, score)
194 return 0
195
196
kjellanderd2b63cf2017-06-30 03:04:59 -0700197def main():
198 """The main function.
199
200 A simple invocation is:
Paulina Hensmanb671d462018-09-14 11:32:00 +0200201 ./webrtc/rtc_tools/compare_videos.py
kjellanderd2b63cf2017-06-30 03:04:59 -0700202 --ref_video=<path_and_name_of_reference_video>
203 --test_video=<path_and_name_of_test_video>
204 --frame_analyzer=<path_and_name_of_the_frame_analyzer_executable>
Magnus Jedvert3e169ac2018-08-24 12:44:59 +0000205
Paulina Hensmane307d562018-09-28 08:24:57 +0200206 Running vmaf requires the following arguments:
207 --vmaf, --vmaf_model, --yuv_frame_width, --yuv_frame_height
208
Magnus Jedvert3e169ac2018-08-24 12:44:59 +0000209 Notice that the prerequisites for barcode_decoder.py also applies to this
210 script. The means the following executables have to be available in the PATH:
211 * zxing
212 * ffmpeg
kjellanderd2b63cf2017-06-30 03:04:59 -0700213 """
214 options = _ParseArgs()
215
Magnus Jedvert3e169ac2018-08-24 12:44:59 +0000216 if options.barcode_decoder:
217 path_to_decoder = options.barcode_decoder
218 else:
219 path_to_decoder = os.path.join(SCRIPT_DIR, 'barcode_tools',
220 'barcode_decoder.py')
221
222 if DecodeBarcodesInVideo(options, path_to_decoder,
223 options.ref_video, options.stats_file_ref) != 0:
224 return 1
225 if DecodeBarcodesInVideo(options, path_to_decoder,
226 options.test_video, options.stats_file_test) != 0:
227 return 1
228
Paulina Hensmane307d562018-09-28 08:24:57 +0200229 # Create a directory to save temporary YUV files for VMAF in frame_analyzer.
230 if options.vmaf:
231 options.yuv_directory = tempfile.mkdtemp() + '/'
232
233 # Run frame_analyzer to compare the videos and print output.
234 if _RunFrameAnalyzer(options) != 0:
kjellanderd2b63cf2017-06-30 03:04:59 -0700235 print 'Failed to run frame analyzer.'
236 return 1
237
Paulina Hensmane307d562018-09-28 08:24:57 +0200238 # Run VMAF for further video comparison and print output.
239 if options.vmaf:
240 return_code = _RunVmaf(options)
241 shutil.rmtree(options.yuv_directory)
242 return return_code
243
kjellanderd2b63cf2017-06-30 03:04:59 -0700244 return 0
245
246if __name__ == '__main__':
247 sys.exit(main())