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