• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Extracts media info metrics from media info data points."""
2
3import collections
4import enum
5
6
7# Direction and type numbers map to constants in the DataPoint group in
8# callstats.proto
9class Direction(enum.Enum):
10    """
11    Possible directions for media entries of a data point.
12    """
13    SENDER = 0
14    RECEIVER = 1
15    BANDWIDTH_ESTIMATION = 2
16
17
18class MediaType(enum.Enum):
19    """
20    Possible media types for media entries of a data point.
21    """
22    AUDIO = 1
23    VIDEO = 2
24    DATA = 3
25
26
27TimestampedValues = collections.namedtuple('TimestampedValues',
28                                           ['TimestampEpochSeconds',
29                                            'ValueList'])
30
31
32class MediaInfoMetricsExtractor(object):
33    """
34    Extracts media metrics from a list of raw media info data points.
35
36    Media info datapoints are expected to be dictionaries in the format returned
37    by cfm_facade.get_media_info_data_points.
38    """
39
40    def __init__(self, data_points):
41        """
42        Initializes with a set of data points.
43
44        @param data_points Data points as a list of dictionaries. Dictionaries
45            should be in the format returned by
46            cfm_facade.get_media_info_data_points.  I.e., as returned when
47            querying the Browser Window for datapoints when the ExportMediaInfo
48            mod is active.
49        """
50        self._data_points = data_points
51
52    def get_top_level_metric(self, name):
53        """
54        Gets top level metrics.
55
56        Gets metrics that are global for one datapoint. I.e., metrics that
57        are not in the media list, such as CPU usage.
58
59        @param name Name of the metric. Names map to the names in the DataPoint
60            group in callstats.proto.
61
62        @returns A list with TimestampedValues. The ValueList is guaranteed to
63            not contain any None values.
64        """
65        metrics = []
66        for data_point in self._data_points:
67            value = data_point.get(name)
68            if value is not None:
69                timestamp = data_point['timestamp']
70                metrics.append(TimestampedValues(timestamp, [value]))
71        return metrics
72
73    def get_media_metric(self,
74                         name,
75                         direction=None,
76                         media_type=None,
77                         post_process_func=lambda x: x):
78        """
79        Gets media metrics.
80
81        Gets metrics that are in the media part of the datapoint. A DataPoint
82        often contains many media entries, why there are multiple values for a
83        specific name.
84
85        @param name Name of the metric. Names map to the names in the DataPoint
86            group in callstats.proto.
87        @param direction: Only include metrics with this direction in the media
88            stream. See the Direction constants in this module. If None, all
89            directions are included.
90        @param media_type: Only include metrics with this media type in the
91            media stream.  See the MediaType constants in this module. If None,
92            all media types are included.
93        @param post_process_func: Function that takes a list of values and
94            optionally transforms it, returning the updated list or a scalar
95            value.  Default is to return the unmodified list. This method is
96            called for the list of values in the same datapoint. Example usage
97            is to sum the received audio bytes for all streams for one logged
98            line.
99
100        @returns A list with TimestampedValues. The ValueList is guaranteed to
101            not contain any None values.
102        """
103        metrics = []
104        for data_point in self._data_points:
105            timestamp = data_point['timestamp']
106            values = [
107                media[name] for media in data_point['media']
108                if name in media
109                    and _media_matches(media, direction, media_type)
110            ]
111            # Filter None values and only add the metric if there is at least
112            # one value left.
113            values = [x for x in values if x is not None]
114            if values:
115                values = post_process_func(values)
116                values = values if isinstance(values, list) else [values]
117                metrics.append(TimestampedValues(timestamp, values))
118        return metrics
119
120def _media_matches(media, direction, media_type):
121    direction_match = (True if direction is None
122                       else media['direction'] == direction.value)
123    type_match = (True if media_type is None
124                  else media['mediatype'] == media_type.value)
125    return direction_match and type_match
126