1from __future__ import division 2 3from autotest_lib.client.common_lib.cros.cfm.metrics import ( 4 media_info_metrics_extractor) 5 6MEDIA_TYPE = media_info_metrics_extractor.MediaType 7DIRECTION = media_info_metrics_extractor.Direction 8 9# Delta used to determine if floating point values are equal. 10FLOATING_POINT_COMPARISON_DELTA = 0.00001 11 12 13def _avg(l): 14 """ 15 Returns the average of the list or 0 if the list is empty. 16 """ 17 return sum(l)/len(l) if l else 0 18 19 20def _get_number_of_incoming_active_video_streams(extractor): 21 """ 22 Calculates the number of incoming video streams. 23 24 @param extractor media_info_metrics_extractor.MediaInfoMetricsExtractor. 25 26 @returns List with (timestamp, number of incoming video streams) tuples. 27 """ 28 # Get metrics of a kind we know exists and count the nuber of values 29 # for each data point. 30 fps_metrics = extractor.get_media_metric( 31 'fps', direction=DIRECTION.RECEIVER, media_type=MEDIA_TYPE.VIDEO) 32 return [(x, [len(y)]) for x, y in fps_metrics] 33 34 35ADAPTATION_CHANGES = 'adaptation_changes' 36ADAPTATION_REASON = 'adaptation_reason' 37AVG_ENCODE_MS = 'avg_encode_ms' 38BROWSER_CPU_PERCENT_OF_TOTAL = 'browser_cpu_percent' 39CPU_PERCENT_OF_TOTAL = 'cpu_percent' 40FRAMERATE_CAPTURED = 'framerate_catured' 41FRAMERATE_DECODED = 'framerate_decoded' 42FRAMERATE_ENCODED = 'framerate_encoded' 43FRAMERATE_TO_RENDERER = 'framerate_to_renderer' 44FRAMERATE_NETWORK_RECEIVED = 'framerate_network_received' 45GPU_PERCENT_OF_TOTAL = 'gpu_cpu_percent' 46NUMBER_OF_ACTIVE_INCOMING_VIDEO_STREAMS = 'num_active_vid_in_streams' 47PROCESS_JS_MEMORY_USED = 'process_js_memory_used' 48RENDERER_CPU_PERCENT_OF_TOTAL = 'renderer_cpu_percent' 49VIDEO_RECEIVED_FRAME_HEIGHT = 'video_received_frame_height' 50VIDEO_RECEIVED_FRAME_WIDTH = 'video_received_frame_width' 51VIDEO_SENT_FRAME_HEIGHT = 'video_sent_frame_height' 52VIDEO_SENT_FRAME_WIDTH = 'video_sent_frame_width' 53VIDEO_SENT_PACKETS = 'video_sent_packets' 54 55 56# Mapping between metric names and how to extract the named metric using the 57# MediaInfoMetricsExtractor. 58METRIC_NAME_TO_EXTRACTOR_FUNC_DICT = { 59 ADAPTATION_CHANGES: 60 lambda x: x.get_media_metric('adaptationChanges', 61 direction=DIRECTION.SENDER, 62 media_type=MEDIA_TYPE.VIDEO), 63 ADAPTATION_REASON: 64 lambda x: x.get_media_metric('adaptationReason', 65 direction=DIRECTION.SENDER, 66 media_type=MEDIA_TYPE.VIDEO), 67 AVG_ENCODE_MS: 68 lambda x: x.get_media_metric('avgEncodeMs', 69 direction=DIRECTION.SENDER, 70 media_type=MEDIA_TYPE.VIDEO), 71 BROWSER_CPU_PERCENT_OF_TOTAL: 72 lambda x: x.get_top_level_metric('browserProcessCpuUsage'), 73 CPU_PERCENT_OF_TOTAL: 74 lambda x: x.get_top_level_metric('systemcpuusage'), 75 FRAMERATE_CAPTURED: 76 lambda x: x.get_media_metric('fps', 77 direction=DIRECTION.SENDER, 78 media_type=MEDIA_TYPE.VIDEO), 79 FRAMERATE_DECODED: 80 lambda x: x.get_media_metric('fpsdecoded', 81 direction=DIRECTION.RECEIVER, 82 media_type=MEDIA_TYPE.VIDEO, 83 post_process_func=_avg), 84 FRAMERATE_ENCODED: 85 lambda x: x.get_media_metric('fpsnetwork', 86 direction=DIRECTION.SENDER, 87 media_type=MEDIA_TYPE.VIDEO), 88 FRAMERATE_NETWORK_RECEIVED: 89 lambda x: x.get_media_metric('fpsnetwork', 90 direction=DIRECTION.RECEIVER, 91 media_type=MEDIA_TYPE.VIDEO), 92 FRAMERATE_TO_RENDERER: 93 lambda x: x.get_media_metric('fps', 94 direction=DIRECTION.RECEIVER, 95 media_type=MEDIA_TYPE.VIDEO, 96 post_process_func=_avg), 97 GPU_PERCENT_OF_TOTAL: 98 lambda x: x.get_top_level_metric('gpuProcessCpuUsage'), 99 NUMBER_OF_ACTIVE_INCOMING_VIDEO_STREAMS: 100 _get_number_of_incoming_active_video_streams, 101 PROCESS_JS_MEMORY_USED: 102 lambda x: x.get_top_level_metric('processJsMemoryUsed'), 103 RENDERER_CPU_PERCENT_OF_TOTAL: 104 lambda x: x.get_top_level_metric('processcpuusage'), 105 VIDEO_RECEIVED_FRAME_WIDTH: 106 lambda x: x.get_media_metric('width', 107 direction=DIRECTION.RECEIVER, 108 media_type=MEDIA_TYPE.VIDEO, 109 post_process_func=_avg), 110 VIDEO_RECEIVED_FRAME_HEIGHT: 111 lambda x: x.get_media_metric('height', 112 direction=DIRECTION.RECEIVER, 113 media_type=MEDIA_TYPE.VIDEO, 114 post_process_func=_avg), 115 VIDEO_SENT_FRAME_HEIGHT: 116 lambda x: x.get_media_metric('height', 117 direction=DIRECTION.SENDER, 118 media_type=MEDIA_TYPE.VIDEO), 119 VIDEO_SENT_FRAME_WIDTH: 120 lambda x: x.get_media_metric('width', 121 direction=DIRECTION.SENDER, 122 media_type=MEDIA_TYPE.VIDEO), 123 VIDEO_SENT_PACKETS: 124 lambda x: x.get_media_metric('packetssent', 125 direction=DIRECTION.SENDER, 126 media_type=MEDIA_TYPE.VIDEO), 127} 128 129class MetricsCollector(object): 130 """Collects metrics for a test run.""" 131 132 def __init__(self, data_point_collector): 133 """ 134 Initializes. 135 136 @param data_point_collector 137 """ 138 self._data_point_collector = data_point_collector 139 self._extractor = None 140 141 def collect_snapshot(self): 142 """ 143 Stores new merics since the last call. 144 145 Metrics can then be retrieved by calling get_metric. 146 """ 147 self._data_point_collector.collect_snapshot() 148 data_points = self._data_point_collector.get_data_points() 149 # Replace the extractor on each snapshot to always support 150 # getting metrics 151 self._extractor = (media_info_metrics_extractor 152 .MediaInfoMetricsExtractor(data_points)) 153 154 def get_metric(self, name): 155 """ 156 Gets the metric with the specified name. 157 158 @param name The name of the metric 159 @returns a list with (timestamp, value_list) tuples 160 """ 161 if self._extractor is None: 162 raise RuntimeError( 163 'collect_snapshot() must be called at least once.') 164 return METRIC_NAME_TO_EXTRACTOR_FUNC_DICT[name]( 165 self._extractor) 166 167 168class DataPointCollector(object): 169 """Collects data points.""" 170 171 def __init__(self, cfm_facade): 172 """ 173 Initializes. 174 175 @param cfm_facade The cfm facade to use for getting data points. 176 """ 177 self._cfm_facade = cfm_facade 178 self._data_points = [] 179 180 def collect_snapshot(self): 181 """ 182 Collects any new datapoints since the last collection. 183 """ 184 data_points = self._cfm_facade.get_media_info_data_points() 185 last_timestamp = (self._data_points[-1]['timestamp'] 186 if self._data_points else 0) 187 for data_point in data_points: 188 if (data_point['timestamp'] > 189 last_timestamp + FLOATING_POINT_COMPARISON_DELTA): 190 self._data_points.append(data_point) 191 192 def get_data_points(self): 193 """ 194 Gets all collected data points. 195 """ 196 return self._data_points 197