# Copyright 2014 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import json import logging import re from telemetry.core import camel_case from telemetry.value import list_of_scalar_values from metrics import Metric INTERESTING_METRICS = { 'packetsReceived': { 'units': 'packets', 'description': 'Packets received by the peer connection', }, 'packetsSent': { 'units': 'packets', 'description': 'Packets sent by the peer connection', }, 'googDecodeMs': { 'units': 'ms', 'description': 'Time spent decoding.', }, 'googMaxDecodeMs': { 'units': 'ms', 'description': 'Maximum time spent decoding one frame.', }, 'googRtt': { 'units': 'ms', 'description': 'Measured round-trip time.', }, 'googJitterReceived': { 'units': 'ms', 'description': 'Receive-side jitter in milliseconds.', }, 'googCaptureJitterMs': { 'units': 'ms', 'description': 'Capture device (audio/video) jitter.', }, 'googTargetDelayMs': { 'units': 'ms', 'description': 'The delay we are targeting.', }, 'googExpandRate': { 'units': '%', 'description': 'How much we have NetEQ-expanded the audio (0-100%)', }, 'googFrameRateReceived': { 'units': 'fps', 'description': 'Receive-side frames per second (video)', }, 'googFrameRateSent': { 'units': 'fps', 'description': 'Send-side frames per second (video)', }, # Bandwidth estimation stats. 'googAvailableSendBandwidth': { 'units': 'bit/s', 'description': 'How much send bandwidth we estimate we have.' }, 'googAvailableReceiveBandwidth': { 'units': 'bit/s', 'description': 'How much receive bandwidth we estimate we have.' }, 'googTargetEncBitrate': { 'units': 'bit/s', 'description': ('The target encoding bitrate we estimate is good to ' 'aim for given our bandwidth estimates.') }, 'googTransmitBitrate': { 'units': 'bit/s', 'description': 'The actual transmit bitrate.' }, } def GetReportKind(report): if 'audioInputLevel' in report or 'audioOutputLevel' in report: return 'audio' if 'googFrameRateSent' in report or 'googFrameRateReceived' in report: return 'video' if 'googAvailableSendBandwidth' in report: return 'bwe' logging.debug('Did not recognize report batch: %s.', report.keys()) # There are other kinds of reports, such as transport types, which we don't # care about here. For these cases just return 'unknown' which will ignore the # report. return 'unknown' def DistinguishAudioVideoOrBwe(report, stat_name): return GetReportKind(report) + '_' + stat_name def StripAudioVideoBweDistinction(stat_name): return re.sub('^(audio|video|bwe)_', '', stat_name) def SortStatsIntoTimeSeries(report_batches): time_series = {} for report_batch in report_batches: for report in report_batch: for stat_name, value in report.iteritems(): if stat_name not in INTERESTING_METRICS: continue if GetReportKind(report) == 'unknown': continue full_stat_name = DistinguishAudioVideoOrBwe(report, stat_name) time_series.setdefault(full_stat_name, []).append(float(value)) return time_series class WebRtcStatisticsMetric(Metric): """Makes it possible to measure stats from peer connections.""" def __init__(self): super(WebRtcStatisticsMetric, self).__init__() self._all_reports = None def Start(self, page, tab): pass def Stop(self, page, tab): """Digs out stats from data populated by the javascript in webrtc_cases.""" self._all_reports = tab.EvaluateJavaScript( 'JSON.stringify(window.peerConnectionReports)') def AddResults(self, tab, results): if not self._all_reports: return reports = json.loads(self._all_reports) for i, report in enumerate(reports): time_series = SortStatsIntoTimeSeries(report) for stat_name, values in time_series.iteritems(): stat_name_underscored = camel_case.ToUnderscore(stat_name) trace_name = 'peer_connection_%d_%s' % (i, stat_name_underscored) general_name = StripAudioVideoBweDistinction(stat_name) results.AddValue(list_of_scalar_values.ListOfScalarValues( results.current_page, trace_name, INTERESTING_METRICS[general_name]['units'], values, description=INTERESTING_METRICS[general_name]['description'], important=False))