1# Copyright (c) 2017 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import json
6
7from autotest_lib.server.cros import cfm_jmidata_helper_base
8
9# Start index in the JSON object that contains Audio/Video streams related info.
10AV_INDEX = 4
11
12SSRC = u'ssrc'
13GLOBAL = u'global'
14
15AUDIO_INPUT = u'audioInputLevel'
16AUDIO_OUTPUT = u'audioOutputLevel'
17BYTES_RECEIVED = u'bytesReceived'
18BYTES_SENT = u'bytesSent'
19ADAPTATION_CHANGES = u'googAdaptationChanges'
20AVERAGE_ENCODE_TIME = u'googAvgEncodeMs'
21BANDWIDTH_LIMITED_RESOLUTION = u'googBandwidthLimitedResolution'
22CPU_LIMITED_RESOLUTION = u'googCpuLimitedResolution'
23VIDEO_ENCODE_CPU_USAGE = u'googEncodeUsagePercent'
24VIDEO_RECEIVED_FRAME_HEIGHT = u'googFrameHeightReceived'
25VIDEO_RECEIVED_FRAME_WIDTH = u'googFrameWidthReceived'
26FRAMERATE_OUTGOING = u'googFrameRateInput'
27FRAMERATE_RECEIVED = u'googFrameRateReceived'
28FRAMERATE_SENT = u'googFrameRateSent'
29FRAMERATE_DECODED = u'googFrameRateDecoded'
30FRAMERATE_OUTPUT = u'googFrameRateOutput'
31FRAME_WIDTH_SENT = u'googFrameWidthSent'
32FRAME_HEIGHT_SENT = u'googFrameHeightSent'
33FRAMES_DECODED = u'framesDecoded'
34FRAMES_ENCODED = u'framesEncoded'
35VIDEO_PACKETS_LOST = u'packetsLost'
36VIDEO_PACKETS_SENT = u'packetsSent'
37
38BROWSER_CPU = u'browserCpuUsage'
39GPU_CPU = u'gpuCpuUsage'
40NUM_PROCESSORS = u'numOfProcessors'
41NACL_EFFECTS_CPU = u'pluginCpuUsage'
42RENDERER_CPU = u'tabCpuUsage'
43TOTAL_CPU = u'totalCpuUsage'
44
45
46class JMIDataV3Helper(cfm_jmidata_helper_base.JMIDataHelperBase):
47    """Helper class for JMI data v3 parsing. This class helps in extracting
48    relevant JMI data from javascript log.
49
50    The class takes javascript file as input and extracts jmidata elements from
51    the file that is internally stored as a list. Whenever we need to extract
52    data i.e. audio received bytes we call a method such as
53    getAudioReceivedDataList. This method converts each string element in the
54    internal list to a json object and retrieves the relevant info from it which
55    is then stored and returned as a list.
56    """
57
58    def __init__(self, log_file_content):
59        super(JMIDataV3Helper, self).__init__(log_file_content, 'jmidatav3')
60
61    def _ExtractAllJMIDataPointsWithKey(self, jmi_type, is_audio, key):
62        """Extracts all values from the data points with the given key."""
63        data_list = []
64        for jmi_data_point in self._jmi_list:
65            json_arr = json.loads(jmi_data_point)
66            for i in range(AV_INDEX, len(json_arr)):
67                if json_arr[i] and jmi_type in json_arr[i]:
68                    jmi_obj = json_arr[i][jmi_type]
69                    this_is_audio = (AUDIO_INPUT in jmi_obj or
70                                     AUDIO_OUTPUT in jmi_obj)
71                    if this_is_audio == is_audio and key in jmi_obj:
72                        if (not isinstance(jmi_obj[key], int) and
73                                (jmi_obj[key] == 'false' or
74                                 jmi_obj[key] == 'true')):
75                            jmi_obj[key] = 1 if jmi_obj[key] == 'true' else 0
76                        data_list.append(jmi_obj[key])
77        if not data_list:
78            data_list = [0]
79        return data_list
80
81    def GetAudioReceivedBytesList(self):
82        return self._ExtractAllJMIDataPointsWithKey(
83                jmi_type=SSRC, is_audio=True, key=BYTES_RECEIVED)
84
85    def GetAudioSentBytesList(self):
86        return self._ExtractAllJMIDataPointsWithKey(
87                jmi_type=SSRC, is_audio=True, key=BYTES_SENT)
88
89    def GetAudioReceivedEnergyList(self):
90        return self._ExtractAllJMIDataPointsWithKey(
91                jmi_type=SSRC, is_audio=True, key=AUDIO_OUTPUT)
92
93    def GetAudioSentEnergyList(self):
94        return self._ExtractAllJMIDataPointsWithKey(
95                jmi_type=SSRC, is_audio=True, key=AUDIO_INPUT)
96
97    def GetVideoSentBytesList(self):
98        return self._ExtractAllJMIDataPointsWithKey(
99                jmi_type=SSRC, is_audio=False, key=BYTES_SENT)
100
101    def GetVideoReceivedBytesList(self):
102        return self._ExtractAllJMIDataPointsWithKey(
103                jmi_type=SSRC, is_audio=False, key=BYTES_RECEIVED)
104
105    def GetVideoIncomingFramerateReceivedList(self):
106        return self._ExtractAllJMIDataPointsWithKey(
107                jmi_type=SSRC, is_audio=False, key=FRAMERATE_RECEIVED)
108
109    def GetVideoOutgoingFramerateSentList(self):
110        return self._ExtractAllJMIDataPointsWithKey(
111                jmi_type=SSRC, is_audio=False, key=FRAMERATE_SENT)
112
113    def GetVideoIncomingFramerateDecodedList(self):
114        return self._ExtractAllJMIDataPointsWithKey(
115                jmi_type=SSRC, is_audio=False, key=FRAMERATE_DECODED)
116
117    def GetVideoIncomingFramerateList(self):
118        return self._ExtractAllJMIDataPointsWithKey(
119                jmi_type=SSRC, is_audio=False, key=FRAMERATE_OUTPUT)
120
121    def GetVideoSentFrameWidthList(self):
122        return self._ExtractAllJMIDataPointsWithKey(
123                jmi_type=SSRC, is_audio=False, key=FRAME_WIDTH_SENT)
124
125    def GetVideoSentFrameHeightList(self):
126        return self._ExtractAllJMIDataPointsWithKey(
127                jmi_type=SSRC, is_audio=False, key=FRAME_HEIGHT_SENT)
128
129    def GetCPULimitedResolutionList(self):
130        return self._ExtractAllJMIDataPointsWithKey(
131                jmi_type=SSRC, is_audio=False, key=CPU_LIMITED_RESOLUTION)
132
133    def GetVideoPacketsSentList(self):
134        return self._ExtractAllJMIDataPointsWithKey(
135                jmi_type=SSRC, is_audio=False, key=VIDEO_PACKETS_SENT)
136
137    def GetVideoPacketsLostList(self):
138        return self._ExtractAllJMIDataPointsWithKey(
139                jmi_type=SSRC, is_audio=False, key=VIDEO_PACKETS_LOST)
140
141    def GetVideoIncomingFramesDecodedList(self):
142        return self._ExtractAllJMIDataPointsWithKey(
143                jmi_type=SSRC, is_audio=False, key=FRAMES_DECODED)
144
145    def GetVideoOutgoingFramesEncodedList(self):
146        return self._ExtractAllJMIDataPointsWithKey(
147                jmi_type=SSRC, is_audio=False, key=FRAMES_ENCODED)
148
149    def GetVideoAdaptationChangeList(self):
150        return self._ExtractAllJMIDataPointsWithKey(
151                jmi_type=SSRC, is_audio=False, key=ADAPTATION_CHANGES)
152
153    def GetVideoEncodeTimeList(self):
154        return self._ExtractAllJMIDataPointsWithKey(
155                jmi_type=SSRC, is_audio=False, key=AVERAGE_ENCODE_TIME)
156
157    def GetBandwidthLimitedResolutionList(self):
158        return self._ExtractAllJMIDataPointsWithKey(
159                jmi_type=SSRC, is_audio=False, key=BANDWIDTH_LIMITED_RESOLUTION)
160
161    def GetVideoReceivedFrameHeightList(self):
162        return self._ExtractAllJMIDataPointsWithKey(
163                jmi_type=SSRC, is_audio=False, key=VIDEO_RECEIVED_FRAME_HEIGHT)
164
165    def GetVideoOutgoingFramerateInputList(self):
166        return self._ExtractAllJMIDataPointsWithKey(
167                jmi_type=SSRC, is_audio=False, key=FRAMERATE_OUTGOING)
168
169    def GetVideoReceivedFrameWidthList(self):
170        return self._ExtractAllJMIDataPointsWithKey(
171                jmi_type=SSRC, is_audio=False, key=VIDEO_RECEIVED_FRAME_WIDTH)
172
173    def GetVideoEncodeCpuUsagePercentList(self):
174        return self._ExtractAllJMIDataPointsWithKey(
175                jmi_type=SSRC, is_audio=False, key=VIDEO_ENCODE_CPU_USAGE)
176
177    def GetNumberOfActiveIncomingVideoStreams(self):
178        """Retrieve number of active incoming video streams."""
179        if not self._jmi_list:
180            # JMI data hasn't started populating yet.
181            return 0
182
183        num_video_streams = []
184
185        # If JMI data has started getting populated and has video stream data.
186        for jmi_data_point in self._jmi_list:
187            json_arr = json.loads(jmi_data_point)
188            video_streams = 0
189            for i in range(AV_INDEX, len(json_arr)):
190                if json_arr[i] and SSRC in json_arr[i]:
191                    ssrc_obj = json_arr[i][SSRC]
192                    is_audio = ('audioInputLevel' in ssrc_obj or
193                            'audioOutputLevel' in ssrc_obj)
194                    is_incoming = 'bytesReceived' in ssrc_obj
195                    frame_rate_received = 'googFrameRateReceived' in ssrc_obj
196                    if ssrc_obj['mediaType'] == 'video' and \
197                            frame_rate_received:
198                        frame_rate = ssrc_obj['googFrameRateReceived']
199                        if (is_incoming and not is_audio) and \
200                                frame_rate != 0:
201                            video_streams += 1
202            num_video_streams.append(video_streams)
203        if not num_video_streams:
204            num_video_streams = [0]
205        return num_video_streams
206
207    def GetCpuUsageList(self, cpu_type):
208        """Retrieves cpu usage data from JMI data.
209
210        @param cpu_type: Cpu usage type.
211        @returns List containing CPU usage data.
212        """
213        data_list = []
214        for jmi_data_point in self._jmi_list:
215            json_arr = json.loads(jmi_data_point)
216            for i in range(AV_INDEX, len(json_arr)):
217                if json_arr[i] and GLOBAL in json_arr[i]:
218                    global_obj = json_arr[i][GLOBAL]
219                    # Some values in JMIDataV3 are set to 'null'.
220                    if cpu_type == u'numOfProcessors':
221                        return global_obj[cpu_type]
222                    elif (cpu_type in global_obj and
223                            self.IsFloat(global_obj[cpu_type])):
224                        data_list.append(float(global_obj[cpu_type]))
225        if not data_list:
226            data_list = [0]
227        return data_list
228
229    def GetNumOfProcessors(self):
230        return self.GetCpuUsageList(NUM_PROCESSORS)
231
232    def GetTotalCpuPercentage(self):
233        return self.GetCpuUsageList(TOTAL_CPU)
234
235    def GetBrowserCpuPercentage(self):
236        return self.GetCpuUsageList(BROWSER_CPU)
237
238    def GetGpuCpuPercentage(self):
239        return self.GetCpuUsageList(GPU_CPU)
240
241    def GetNaclEffectsCpuPercentage(self):
242        return self.GetCpuUsageList(NACL_EFFECTS_CPU)
243
244    def GetRendererCpuPercentage(self):
245        return self.GetCpuUsageList(RENDERER_CPU)
246
247    def IsFloat(self, value):
248        try:
249            float(value)
250            return True
251        except TypeError:
252            return False
253