kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # Copyright (c) 2011 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 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 10 | import os |
| 11 | import gviz_api |
| 12 | import webrtc.data_helper |
| 13 | |
| 14 | def main(): |
| 15 | """ |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 16 | This Python script displays a web page with test created with the |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 17 | video_quality_measurement program, which is a tool in WebRTC. |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 18 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 19 | The script requires on two external files and one Python library: |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 20 | - A HTML template file with layout and references to the json variables |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 21 | defined in this script |
| 22 | - A data file in Python format, containing the following: |
| 23 | - test_configuration - a dictionary of test configuration names and values. |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 24 | - frame_data_types - a dictionary that maps the different metrics to their |
kjellander@webrtc.org | 418bce5 | 2011-12-05 16:29:21 +0000 | [diff] [blame] | 25 | data types. |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 26 | - frame_data - a list of dictionaries where each dictionary maps a metric to |
| 27 | it's value. |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 28 | - The gviz_api.py of the Google Visualization Python API, available at |
| 29 | http://code.google.com/p/google-visualization-python/ |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 30 | |
| 31 | The HTML file is shipped with the script, while the data file must be |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 32 | generated by running video_quality_measurement with the --python flag |
| 33 | specified. |
| 34 | """ |
| 35 | print 'Content-type: text/html\n' # the newline is required! |
| 36 | |
| 37 | page_template_filename = '../templates/chart_page_template.html' |
| 38 | # The data files must be located in the project tree for app engine being |
| 39 | # able to access them. |
| 40 | data_filenames = [ '../data/vp8_sw.py', '../data/vp8_hw.py' ] |
| 41 | # Will contain info/error messages to be displayed on the resulting page. |
| 42 | messages = [] |
| 43 | # Load the page HTML template. |
| 44 | try: |
| 45 | f = open(page_template_filename) |
| 46 | page_template = f.read() |
| 47 | f.close() |
| 48 | except IOError as e: |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 49 | ShowErrorPage('Cannot open page template file: %s<br>Details: %s' % |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 50 | (page_template_filename, e)) |
| 51 | return |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 52 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 53 | # Read data from external Python script files. First check that they exist. |
| 54 | for filename in data_filenames: |
| 55 | if not os.path.exists(filename): |
| 56 | messages.append('Cannot open data file: %s' % filename) |
| 57 | data_filenames.remove(filename) |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 58 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 59 | # Read data from all existing input files. |
| 60 | data_list = [] |
kjellander@webrtc.org | 418bce5 | 2011-12-05 16:29:21 +0000 | [diff] [blame] | 61 | test_configurations = [] |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 62 | names = [] |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 63 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 64 | for filename in data_filenames: |
| 65 | read_vars = {} # empty dictionary to load the data into. |
| 66 | execfile(filename, read_vars, read_vars) |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 67 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 68 | test_configuration = read_vars['test_configuration'] |
| 69 | table_description = read_vars['frame_data_types'] |
| 70 | table_data = read_vars['frame_data'] |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 71 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 72 | # Verify the data in the file loaded properly. |
| 73 | if not table_description or not table_data: |
| 74 | messages.append('Invalid input file: %s. Missing description list or ' |
kjellander@webrtc.org | 418bce5 | 2011-12-05 16:29:21 +0000 | [diff] [blame] | 75 | 'data dictionary variables.' % filename) |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 76 | continue |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 77 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 78 | # Frame numbers appear as number type in the data, but Chart API requires |
| 79 | # values of the X-axis to be of string type. |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 80 | # Change the frame_number column data type: |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 81 | table_description['frame_number'] = ('string', 'Frame number') |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 82 | # Convert all the values to string types: |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 83 | for row in table_data: |
| 84 | row['frame_number'] = str(row['frame_number']) |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 85 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 86 | # Store the unique data from this file in the high level lists. |
kjellander@webrtc.org | 418bce5 | 2011-12-05 16:29:21 +0000 | [diff] [blame] | 87 | test_configurations.append(test_configuration) |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 88 | data_list.append(table_data) |
kjellander@webrtc.org | 418bce5 | 2011-12-05 16:29:21 +0000 | [diff] [blame] | 89 | # Name of the test run must be present. |
| 90 | test_name = FindConfiguration(test_configuration, 'name') |
| 91 | if not test_name: |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 92 | messages.append('Invalid input file: %s. Missing configuration key ' |
kjellander@webrtc.org | 418bce5 | 2011-12-05 16:29:21 +0000 | [diff] [blame] | 93 | '"name"', filename) |
| 94 | continue |
| 95 | names.append(test_name) |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 96 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 97 | # Create data helper and build data tables for each graph. |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 98 | helper = webrtc.data_helper.DataHelper(data_list, table_description, |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 99 | names, messages) |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 100 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 101 | # Loading it into gviz_api.DataTable objects and create JSON strings. |
kjellander@webrtc.org | 418bce5 | 2011-12-05 16:29:21 +0000 | [diff] [blame] | 102 | description, data = helper.CreateConfigurationTable(test_configurations) |
| 103 | configurations = gviz_api.DataTable(description, data) |
kjellander@webrtc.org | e415864 | 2014-08-06 09:11:18 +0000 | [diff] [blame^] | 104 | json_configurations = configurations.ToJSon() # pylint: disable=W0612 |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 105 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 106 | description, data = helper.CreateData('ssim') |
| 107 | ssim = gviz_api.DataTable(description, data) |
kjellander@webrtc.org | e415864 | 2014-08-06 09:11:18 +0000 | [diff] [blame^] | 108 | # pylint: disable=W0612 |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 109 | json_ssim_data = ssim.ToJSon(helper.GetOrdering(description)) |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 110 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 111 | description, data = helper.CreateData('psnr') |
| 112 | psnr = gviz_api.DataTable(description, data) |
kjellander@webrtc.org | e415864 | 2014-08-06 09:11:18 +0000 | [diff] [blame^] | 113 | # pylint: disable=W0612 |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 114 | json_psnr_data = psnr.ToJSon(helper.GetOrdering(description)) |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 115 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 116 | description, data = helper.CreateData('packets_dropped') |
| 117 | packet_loss = gviz_api.DataTable(description, data) |
kjellander@webrtc.org | e415864 | 2014-08-06 09:11:18 +0000 | [diff] [blame^] | 118 | # pylint: disable=W0612 |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 119 | json_packet_loss_data = packet_loss.ToJSon(helper.GetOrdering(description)) |
| 120 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 121 | description, data = helper.CreateData('bit_rate') |
| 122 | # Add a column of data points for the desired bit rate to be plotted. |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 123 | # (uses test configuration from the last data set, assuming it is the same |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 124 | # for all of them) |
kjellander@webrtc.org | 418bce5 | 2011-12-05 16:29:21 +0000 | [diff] [blame] | 125 | desired_bit_rate = FindConfiguration(test_configuration, 'bit_rate_in_kbps') |
| 126 | if not desired_bit_rate: |
| 127 | ShowErrorPage('Cannot configuration field named "bit_rate_in_kbps"') |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 128 | return |
kjellander@webrtc.org | 418bce5 | 2011-12-05 16:29:21 +0000 | [diff] [blame] | 129 | desired_bit_rate = int(desired_bit_rate) |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 130 | # Add new column data type description. |
| 131 | description['desired_bit_rate'] = ('number', 'Desired bit rate (kbps)') |
| 132 | for row in data: |
| 133 | row['desired_bit_rate'] = desired_bit_rate |
| 134 | bit_rate = gviz_api.DataTable(description, data) |
kjellander@webrtc.org | e415864 | 2014-08-06 09:11:18 +0000 | [diff] [blame^] | 135 | # pylint: disable=W0612 |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 136 | json_bit_rate_data = bit_rate.ToJSon(helper.GetOrdering(description)) |
| 137 | |
| 138 | # Format the messages list with newlines. |
| 139 | messages = '\n'.join(messages) |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 140 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 141 | # Put the variables as JSon strings into the template. |
| 142 | print page_template % vars() |
| 143 | |
kjellander@webrtc.org | 418bce5 | 2011-12-05 16:29:21 +0000 | [diff] [blame] | 144 | def FindConfiguration(configuration, name): |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 145 | """ Finds a configuration value using it's name. |
kjellander@webrtc.org | 418bce5 | 2011-12-05 16:29:21 +0000 | [diff] [blame] | 146 | Returns the first configuration with a matching name. Returns None if no |
| 147 | matching configuration is found. """ |
| 148 | return_value = None |
| 149 | for row in configuration: |
| 150 | if row['name'] == name: |
| 151 | return_value = row['value'] |
| 152 | break |
| 153 | return return_value |
| 154 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 155 | def ShowErrorPage(error_message): |
| 156 | print '<html><body>%s</body></html>' % error_message |
phoglund@webrtc.org | 5d371393 | 2013-03-07 09:59:43 +0000 | [diff] [blame] | 157 | |
kjellander@webrtc.org | 689cb30 | 2011-11-07 15:25:47 +0000 | [diff] [blame] | 158 | if __name__ == '__main__': |
| 159 | main() |