1*b7c941bbSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*b7c941bbSAndroid Build Coastguard Worker# Copyright (C) 2015 The Android Open Source Project 3*b7c941bbSAndroid Build Coastguard Worker# 4*b7c941bbSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 5*b7c941bbSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 6*b7c941bbSAndroid Build Coastguard Worker# You may obtain a copy of the License at 7*b7c941bbSAndroid Build Coastguard Worker# 8*b7c941bbSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 9*b7c941bbSAndroid Build Coastguard Worker# 10*b7c941bbSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 11*b7c941bbSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 12*b7c941bbSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*b7c941bbSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 14*b7c941bbSAndroid Build Coastguard Worker# limitations under the License. 15*b7c941bbSAndroid Build Coastguard Worker# 16*b7c941bbSAndroid Build Coastguard Worker 17*b7c941bbSAndroid Build Coastguard Workerimport argparse, json, math, re, sys, zipfile 18*b7c941bbSAndroid Build Coastguard Workerimport xml.etree.ElementTree as ET 19*b7c941bbSAndroid Build Coastguard Workerfrom collections import defaultdict, namedtuple 20*b7c941bbSAndroid Build Coastguard Worker 21*b7c941bbSAndroid Build Coastguard Worker 22*b7c941bbSAndroid Build Coastguard Workerclass Size(namedtuple('Size', ['width', 'height'])): 23*b7c941bbSAndroid Build Coastguard Worker """A namedtuple with width and height fields.""" 24*b7c941bbSAndroid Build Coastguard Worker def __str__(self): 25*b7c941bbSAndroid Build Coastguard Worker return '%dx%d' % (self.width, self.height) 26*b7c941bbSAndroid Build Coastguard Worker 27*b7c941bbSAndroid Build Coastguard Workerdef nicekey(v): 28*b7c941bbSAndroid Build Coastguard Worker """Returns a nicer sort key for sorting strings. 29*b7c941bbSAndroid Build Coastguard Worker 30*b7c941bbSAndroid Build Coastguard Worker This sorts using lower case, with numbers in numerical order first.""" 31*b7c941bbSAndroid Build Coastguard Worker key = [] 32*b7c941bbSAndroid Build Coastguard Worker num = False 33*b7c941bbSAndroid Build Coastguard Worker for p in re.split('(\d+)', v.lower()): 34*b7c941bbSAndroid Build Coastguard Worker if num: 35*b7c941bbSAndroid Build Coastguard Worker key.append(('0', int(p))) 36*b7c941bbSAndroid Build Coastguard Worker elif p: 37*b7c941bbSAndroid Build Coastguard Worker key.append((p, 0)) 38*b7c941bbSAndroid Build Coastguard Worker num = not num 39*b7c941bbSAndroid Build Coastguard Worker return key + [(v, 0)] 40*b7c941bbSAndroid Build Coastguard Worker 41*b7c941bbSAndroid Build Coastguard Workerdef nice(v): 42*b7c941bbSAndroid Build Coastguard Worker """Returns a nicer representation for objects in debug messages. 43*b7c941bbSAndroid Build Coastguard Worker 44*b7c941bbSAndroid Build Coastguard Worker Dictionaries are sorted, size is WxH, unicode removed, and floats have 1 digit precision.""" 45*b7c941bbSAndroid Build Coastguard Worker if isinstance(v, dict): 46*b7c941bbSAndroid Build Coastguard Worker return 'dict(' + ', '.join(k + '=' + nice(v) for k, v in sorted(v.items(), key=lambda i: nicekey(i[0]))) + ')' 47*b7c941bbSAndroid Build Coastguard Worker if isinstance(v, str): 48*b7c941bbSAndroid Build Coastguard Worker return repr(v) 49*b7c941bbSAndroid Build Coastguard Worker if isinstance(v, int): 50*b7c941bbSAndroid Build Coastguard Worker return str(v) 51*b7c941bbSAndroid Build Coastguard Worker if isinstance(v, Size): 52*b7c941bbSAndroid Build Coastguard Worker return repr(str(v)) 53*b7c941bbSAndroid Build Coastguard Worker if isinstance(v, float): 54*b7c941bbSAndroid Build Coastguard Worker return '%.1f' % v 55*b7c941bbSAndroid Build Coastguard Worker if isinstance(v, type(u'')): 56*b7c941bbSAndroid Build Coastguard Worker return repr(str(v)) 57*b7c941bbSAndroid Build Coastguard Worker raise ValueError(v) 58*b7c941bbSAndroid Build Coastguard Worker 59*b7c941bbSAndroid Build Coastguard Workerclass ResultParser: 60*b7c941bbSAndroid Build Coastguard Worker @staticmethod 61*b7c941bbSAndroid Build Coastguard Worker def _intify(value): 62*b7c941bbSAndroid Build Coastguard Worker """Returns a value converted to int if possible, else the original value.""" 63*b7c941bbSAndroid Build Coastguard Worker try: 64*b7c941bbSAndroid Build Coastguard Worker return int(value) 65*b7c941bbSAndroid Build Coastguard Worker except ValueError: 66*b7c941bbSAndroid Build Coastguard Worker return value 67*b7c941bbSAndroid Build Coastguard Worker 68*b7c941bbSAndroid Build Coastguard Worker def _parseDict(self, value): 69*b7c941bbSAndroid Build Coastguard Worker """Parses a MediaFormat from its string representation sans brackets.""" 70*b7c941bbSAndroid Build Coastguard Worker return dict((k, self._intify(v)) 71*b7c941bbSAndroid Build Coastguard Worker for k, v in re.findall(r'([^ =]+)=([^ [=]+(?:|\[[^\]]+\]))(?:, |$)', value)) 72*b7c941bbSAndroid Build Coastguard Worker 73*b7c941bbSAndroid Build Coastguard Worker def _cleanFormat(self, format): 74*b7c941bbSAndroid Build Coastguard Worker """Removes internal fields from a parsed MediaFormat.""" 75*b7c941bbSAndroid Build Coastguard Worker format.pop('what', None) 76*b7c941bbSAndroid Build Coastguard Worker format.pop('image-data', None) 77*b7c941bbSAndroid Build Coastguard Worker 78*b7c941bbSAndroid Build Coastguard Worker MESSAGE_PATTERN = r'(?P<key>\w+)=(?P<value>\{[^}]*\}|[^ ,{}]+)' 79*b7c941bbSAndroid Build Coastguard Worker 80*b7c941bbSAndroid Build Coastguard Worker def _parsePartialResult(self, message_match): 81*b7c941bbSAndroid Build Coastguard Worker """Parses a partial test result conforming to the message pattern. 82*b7c941bbSAndroid Build Coastguard Worker 83*b7c941bbSAndroid Build Coastguard Worker Returns: 84*b7c941bbSAndroid Build Coastguard Worker A tuple of string key and int, string or dict value, where dict has 85*b7c941bbSAndroid Build Coastguard Worker string keys mapping to int or string values. 86*b7c941bbSAndroid Build Coastguard Worker """ 87*b7c941bbSAndroid Build Coastguard Worker key, value = message_match.group('key', 'value') 88*b7c941bbSAndroid Build Coastguard Worker if value.startswith('{'): 89*b7c941bbSAndroid Build Coastguard Worker value = self._parseDict(value[1:-1]) 90*b7c941bbSAndroid Build Coastguard Worker if key.endswith('Format'): 91*b7c941bbSAndroid Build Coastguard Worker self._cleanFormat(value) 92*b7c941bbSAndroid Build Coastguard Worker else: 93*b7c941bbSAndroid Build Coastguard Worker value = self._intify(value) 94*b7c941bbSAndroid Build Coastguard Worker return key, value 95*b7c941bbSAndroid Build Coastguard Worker 96*b7c941bbSAndroid Build Coastguard Worker 97*b7c941bbSAndroid Build Coastguard Workerdef perc(data, p, fn=round): 98*b7c941bbSAndroid Build Coastguard Worker """Returns a percentile value from a sorted array. 99*b7c941bbSAndroid Build Coastguard Worker 100*b7c941bbSAndroid Build Coastguard Worker Arguments: 101*b7c941bbSAndroid Build Coastguard Worker data: sorted data 102*b7c941bbSAndroid Build Coastguard Worker p: percentile value (0-100) 103*b7c941bbSAndroid Build Coastguard Worker fn: method used for rounding the percentile to an integer index in data 104*b7c941bbSAndroid Build Coastguard Worker """ 105*b7c941bbSAndroid Build Coastguard Worker return data[int(fn((len(data) - 1) * p / 100))] 106*b7c941bbSAndroid Build Coastguard Worker 107*b7c941bbSAndroid Build Coastguard Worker 108*b7c941bbSAndroid Build Coastguard Workerdef genXml(data, A=None): 109*b7c941bbSAndroid Build Coastguard Worker yield '<?xml version="1.0" encoding="utf-8" ?>' 110*b7c941bbSAndroid Build Coastguard Worker yield '<!-- Copyright 2016 The Android Open Source Project' 111*b7c941bbSAndroid Build Coastguard Worker yield '' 112*b7c941bbSAndroid Build Coastguard Worker yield ' Licensed under the Apache License, Version 2.0 (the "License");' 113*b7c941bbSAndroid Build Coastguard Worker yield ' you may not use this file except in compliance with the License.' 114*b7c941bbSAndroid Build Coastguard Worker yield ' You may obtain a copy of the License at' 115*b7c941bbSAndroid Build Coastguard Worker yield '' 116*b7c941bbSAndroid Build Coastguard Worker yield ' http://www.apache.org/licenses/LICENSE-2.0' 117*b7c941bbSAndroid Build Coastguard Worker yield '' 118*b7c941bbSAndroid Build Coastguard Worker yield ' Unless required by applicable law or agreed to in writing, software' 119*b7c941bbSAndroid Build Coastguard Worker yield ' distributed under the License is distributed on an "AS IS" BASIS,' 120*b7c941bbSAndroid Build Coastguard Worker yield ' WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.' 121*b7c941bbSAndroid Build Coastguard Worker yield ' See the License for the specific language governing permissions and' 122*b7c941bbSAndroid Build Coastguard Worker yield ' limitations under the License.' 123*b7c941bbSAndroid Build Coastguard Worker yield '-->' 124*b7c941bbSAndroid Build Coastguard Worker yield '' 125*b7c941bbSAndroid Build Coastguard Worker yield '<MediaCodecs>' 126*b7c941bbSAndroid Build Coastguard Worker last_section = None 127*b7c941bbSAndroid Build Coastguard Worker from collections import namedtuple 128*b7c941bbSAndroid Build Coastguard Worker Comp = namedtuple('Comp', 'is_decoder google mime name') 129*b7c941bbSAndroid Build Coastguard Worker Result = namedtuple('Result', 'mn mx p95 med geo p5') 130*b7c941bbSAndroid Build Coastguard Worker for comp_, cdata in sorted(data.items()): 131*b7c941bbSAndroid Build Coastguard Worker comp = Comp(*comp_) 132*b7c941bbSAndroid Build Coastguard Worker section = 'Decoders' if comp.is_decoder else 'Encoders' 133*b7c941bbSAndroid Build Coastguard Worker if section != last_section: 134*b7c941bbSAndroid Build Coastguard Worker if last_section: 135*b7c941bbSAndroid Build Coastguard Worker yield ' </%s>' % last_section 136*b7c941bbSAndroid Build Coastguard Worker yield ' <%s>' % section 137*b7c941bbSAndroid Build Coastguard Worker last_section = section 138*b7c941bbSAndroid Build Coastguard Worker yield ' <MediaCodec name="%s" type="%s" update="true">' % (comp.name, comp.mime) 139*b7c941bbSAndroid Build Coastguard Worker for size, sdata in sorted(cdata.items()): 140*b7c941bbSAndroid Build Coastguard Worker data = sorted(sdata) 141*b7c941bbSAndroid Build Coastguard Worker N = len(data) 142*b7c941bbSAndroid Build Coastguard Worker mn, mx = data[0], data[-1] 143*b7c941bbSAndroid Build Coastguard Worker 144*b7c941bbSAndroid Build Coastguard Worker if N < 20 and not A.ignore: 145*b7c941bbSAndroid Build Coastguard Worker raise ValueError("need at least 20 data points for %s size %s; have %s" % 146*b7c941bbSAndroid Build Coastguard Worker (comp.name, size, N)) 147*b7c941bbSAndroid Build Coastguard Worker 148*b7c941bbSAndroid Build Coastguard Worker TO = 2.2 # tolerance with margin 149*b7c941bbSAndroid Build Coastguard Worker T = TO / 1.1 # tolerance without margin 150*b7c941bbSAndroid Build Coastguard Worker 151*b7c941bbSAndroid Build Coastguard Worker Final = namedtuple('Final', 'comment c2 var qual') 152*b7c941bbSAndroid Build Coastguard Worker lastFinal = None 153*b7c941bbSAndroid Build Coastguard Worker for RG in (10, 15, 20, 25, 30, 40, 50): 154*b7c941bbSAndroid Build Coastguard Worker P = 50./RG 155*b7c941bbSAndroid Build Coastguard Worker quality = 0 156*b7c941bbSAndroid Build Coastguard Worker p95, med, p5 = perc(data, P, math.floor), perc(data, 50, round), perc(data, 100 - P, math.ceil) 157*b7c941bbSAndroid Build Coastguard Worker geo = math.sqrt(p5 * p95) 158*b7c941bbSAndroid Build Coastguard Worker comment = '' 159*b7c941bbSAndroid Build Coastguard Worker pub_lo, pub_hi = min(int(p95 * T), round(geo)), max(math.ceil(p5 / T), round(geo)) 160*b7c941bbSAndroid Build Coastguard Worker if pub_lo > med: 161*b7c941bbSAndroid Build Coastguard Worker if pub_lo > med * 1.1: 162*b7c941bbSAndroid Build Coastguard Worker quality += 0.5 163*b7c941bbSAndroid Build Coastguard Worker comment += ' SLOW' 164*b7c941bbSAndroid Build Coastguard Worker pub_lo = int(med) 165*b7c941bbSAndroid Build Coastguard Worker if N < 2 * RG: 166*b7c941bbSAndroid Build Coastguard Worker comment += ' N=%d' % N 167*b7c941bbSAndroid Build Coastguard Worker quality += 2 168*b7c941bbSAndroid Build Coastguard Worker RGVAR = False 169*b7c941bbSAndroid Build Coastguard Worker if p5 / p95 > T ** 3: 170*b7c941bbSAndroid Build Coastguard Worker quality += 3 171*b7c941bbSAndroid Build Coastguard Worker RGVAR = True 172*b7c941bbSAndroid Build Coastguard Worker if pub_hi > pub_lo * TO: 173*b7c941bbSAndroid Build Coastguard Worker quality += 1 174*b7c941bbSAndroid Build Coastguard Worker if RG == 10: 175*b7c941bbSAndroid Build Coastguard Worker # find best pub_lo and pub_hi 176*b7c941bbSAndroid Build Coastguard Worker for i in range(N // 2): 177*b7c941bbSAndroid Build Coastguard Worker pub_lo_, pub_hi_ = min(int(data[N // 2 - i - 1] * T), round(geo), int(med)), max(math.ceil(data[N // 2 + i] / T), round(geo)) 178*b7c941bbSAndroid Build Coastguard Worker if pub_hi_ > pub_lo_ * TO: 179*b7c941bbSAndroid Build Coastguard Worker # ??? 180*b7c941bbSAndroid Build Coastguard Worker pub_lo = min(pub_lo, math.ceil(pub_hi_ / TO)) 181*b7c941bbSAndroid Build Coastguard Worker break 182*b7c941bbSAndroid Build Coastguard Worker pub_lo, pub_hi = pub_lo_, pub_hi_ 183*b7c941bbSAndroid Build Coastguard Worker if mn < pub_lo / T or mx > pub_hi * T or pub_lo <= pub_hi / T: 184*b7c941bbSAndroid Build Coastguard Worker quality += 1 185*b7c941bbSAndroid Build Coastguard Worker comment += ' FLAKY(' 186*b7c941bbSAndroid Build Coastguard Worker if round(mn, 1) < pub_lo / T: 187*b7c941bbSAndroid Build Coastguard Worker comment += 'mn=%.1f < ' % mn 188*b7c941bbSAndroid Build Coastguard Worker comment += 'RANGE' 189*b7c941bbSAndroid Build Coastguard Worker if round(mx, 1) > pub_hi * T: 190*b7c941bbSAndroid Build Coastguard Worker comment += ' < mx=%.1f' % mx 191*b7c941bbSAndroid Build Coastguard Worker comment += ')' 192*b7c941bbSAndroid Build Coastguard Worker if False: 193*b7c941bbSAndroid Build Coastguard Worker comment += ' DATA(mn=%1.f p%d=%1.f accept=%1.f-%1.f p50=%1.f p%d=%1.f mx=%1.f)' % ( 194*b7c941bbSAndroid Build Coastguard Worker mn, 100-P, p95, pub_lo / T, pub_hi * T, med, P, p5, mx) 195*b7c941bbSAndroid Build Coastguard Worker var = math.sqrt(p5/p95) 196*b7c941bbSAndroid Build Coastguard Worker if p95 < geo / T or p5 > geo * T: 197*b7c941bbSAndroid Build Coastguard Worker if RGVAR: 198*b7c941bbSAndroid Build Coastguard Worker comment += ' RG.VARIANCE:%.1f' % ((p5/p95) ** (1./3)) 199*b7c941bbSAndroid Build Coastguard Worker else: 200*b7c941bbSAndroid Build Coastguard Worker comment += ' variance:%.1f' % var 201*b7c941bbSAndroid Build Coastguard Worker comment = comment.replace('RANGE', '%d - %d' % (math.ceil(pub_lo / T), int(pub_hi * T))) 202*b7c941bbSAndroid Build Coastguard Worker c2 = '' 203*b7c941bbSAndroid Build Coastguard Worker if N >= 2 * RG: 204*b7c941bbSAndroid Build Coastguard Worker c2 += ' N=%d' % N 205*b7c941bbSAndroid Build Coastguard Worker if var <= T or p5 / p95 > T ** 3: 206*b7c941bbSAndroid Build Coastguard Worker c2 += ' v%d%%=%.1f' % (round(100 - 2 * P), var) 207*b7c941bbSAndroid Build Coastguard Worker if A and A.dbg: 208*b7c941bbSAndroid Build Coastguard Worker c2 += ' E=%s' % (str(quality)) 209*b7c941bbSAndroid Build Coastguard Worker if c2: 210*b7c941bbSAndroid Build Coastguard Worker c2 = ' <!--%s -->' % c2 211*b7c941bbSAndroid Build Coastguard Worker 212*b7c941bbSAndroid Build Coastguard Worker if comment: 213*b7c941bbSAndroid Build Coastguard Worker comment = ' <!-- measured %d%%:%d-%d med:%d%s -->' % (round(100 - 2 * P), int(p95), math.ceil(p5), int(round(med)), comment) 214*b7c941bbSAndroid Build Coastguard Worker if A and A.dbg: yield '<!-- --> %s%s' % (comment, c2) 215*b7c941bbSAndroid Build Coastguard Worker c2 = ' <Limit name="measured-frame-rate-%s" range="%d-%d" />%s' % (size, pub_lo, pub_hi, c2) 216*b7c941bbSAndroid Build Coastguard Worker final = Final(comment, c2, var, quality) 217*b7c941bbSAndroid Build Coastguard Worker if lastFinal and final.var > lastFinal.var * math.sqrt(1.3): 218*b7c941bbSAndroid Build Coastguard Worker if A and A.dbg: yield '<!-- RANGE JUMP -->' 219*b7c941bbSAndroid Build Coastguard Worker break 220*b7c941bbSAndroid Build Coastguard Worker elif not lastFinal or quality <= lastFinal.qual: 221*b7c941bbSAndroid Build Coastguard Worker lastFinal = final 222*b7c941bbSAndroid Build Coastguard Worker if N < 2 * RG or quality >= 4: 223*b7c941bbSAndroid Build Coastguard Worker break 224*b7c941bbSAndroid Build Coastguard Worker comment, c2, var, quality = lastFinal 225*b7c941bbSAndroid Build Coastguard Worker 226*b7c941bbSAndroid Build Coastguard Worker if comment: 227*b7c941bbSAndroid Build Coastguard Worker yield comment 228*b7c941bbSAndroid Build Coastguard Worker yield c2 229*b7c941bbSAndroid Build Coastguard Worker yield ' </MediaCodec>' 230*b7c941bbSAndroid Build Coastguard Worker if last_section: 231*b7c941bbSAndroid Build Coastguard Worker yield ' </%s>' % last_section 232*b7c941bbSAndroid Build Coastguard Worker yield '</MediaCodecs>' 233*b7c941bbSAndroid Build Coastguard Worker 234*b7c941bbSAndroid Build Coastguard Worker 235*b7c941bbSAndroid Build Coastguard Workerclass Data: 236*b7c941bbSAndroid Build Coastguard Worker def __init__(self): 237*b7c941bbSAndroid Build Coastguard Worker self.data = set() 238*b7c941bbSAndroid Build Coastguard Worker self.kind = {} 239*b7c941bbSAndroid Build Coastguard Worker self.devices = set() 240*b7c941bbSAndroid Build Coastguard Worker self.parser = ResultParser() 241*b7c941bbSAndroid Build Coastguard Worker 242*b7c941bbSAndroid Build Coastguard Worker def summarize(self, A=None): 243*b7c941bbSAndroid Build Coastguard Worker devs = sorted(self.devices) 244*b7c941bbSAndroid Build Coastguard Worker # device > (not encoder,goog,mime,codec) > size > fps 245*b7c941bbSAndroid Build Coastguard Worker xmlInfo = defaultdict(lambda: defaultdict(lambda: defaultdict(list))) 246*b7c941bbSAndroid Build Coastguard Worker 247*b7c941bbSAndroid Build Coastguard Worker for mime, encoder, goog in sorted(set(self.kind.values())): 248*b7c941bbSAndroid Build Coastguard Worker for dev, build, codec, size, num, std, avg, p0, p5, p10, p20, p30, p40, p50, p60, p70, p80, p90, p95, p100 in self.data: 249*b7c941bbSAndroid Build Coastguard Worker if self.kind[codec] != (mime, encoder, goog): 250*b7c941bbSAndroid Build Coastguard Worker continue 251*b7c941bbSAndroid Build Coastguard Worker 252*b7c941bbSAndroid Build Coastguard Worker if p95 > 2: # ignore measurements at or below 2fps 253*b7c941bbSAndroid Build Coastguard Worker xmlInfo[dev][(not encoder, goog, mime, codec)][size].append(p95) 254*b7c941bbSAndroid Build Coastguard Worker else: 255*b7c941bbSAndroid Build Coastguard Worker print("warning: p95 value is suspiciously low: {}".format( 256*b7c941bbSAndroid Build Coastguard Worker nice(dict(config=dict(dev=dev, codec=codec, size=str(size), N=num), 257*b7c941bbSAndroid Build Coastguard Worker data=dict(std=std, avg=avg, p0=p0, p5=p5, p10=p10, p20=p20, p30=p30, p40=p40, 258*b7c941bbSAndroid Build Coastguard Worker p50=p50, p60=p60, p70=p70, p80=p80, p90=p90, p95=p95, p100=p100)))), 259*b7c941bbSAndroid Build Coastguard Worker file=sys.stderr) 260*b7c941bbSAndroid Build Coastguard Worker for dev, ddata in xmlInfo.items(): 261*b7c941bbSAndroid Build Coastguard Worker outFile = '{}.media_codecs_performance.xml'.format(dev) 262*b7c941bbSAndroid Build Coastguard Worker print(f"generating {outFile}", file=sys.stderr) 263*b7c941bbSAndroid Build Coastguard Worker with open(outFile, "wt") as out: 264*b7c941bbSAndroid Build Coastguard Worker for l in genXml(ddata, A=A): 265*b7c941bbSAndroid Build Coastguard Worker out.write(l + '\n') 266*b7c941bbSAndroid Build Coastguard Worker print(l) 267*b7c941bbSAndroid Build Coastguard Worker print(f"generated {outFile}", file=sys.stderr) 268*b7c941bbSAndroid Build Coastguard Worker 269*b7c941bbSAndroid Build Coastguard Worker def parse_fmt(self, fmt): 270*b7c941bbSAndroid Build Coastguard Worker return self.parser._parseDict(fmt) 271*b7c941bbSAndroid Build Coastguard Worker 272*b7c941bbSAndroid Build Coastguard Worker def parse_perf(self, a, device, build): 273*b7c941bbSAndroid Build Coastguard Worker def rateFn(i): 274*b7c941bbSAndroid Build Coastguard Worker if i is None: 275*b7c941bbSAndroid Build Coastguard Worker return i 276*b7c941bbSAndroid Build Coastguard Worker elif i == 0: 277*b7c941bbSAndroid Build Coastguard Worker return 1e6 278*b7c941bbSAndroid Build Coastguard Worker return 1000. / i 279*b7c941bbSAndroid Build Coastguard Worker 280*b7c941bbSAndroid Build Coastguard Worker points = ('avg', 'min', 'p5', 'p10', 'p20', 'p30', 'p40', 'p50', 'p60', 'p70', 'p80', 'p90', 'p95', 'max') 281*b7c941bbSAndroid Build Coastguard Worker a = dict(a) 282*b7c941bbSAndroid Build Coastguard Worker codec = a['codec_name'] + '' 283*b7c941bbSAndroid Build Coastguard Worker mime = a['mime_type'] 284*b7c941bbSAndroid Build Coastguard Worker size = Size(a['width'], a['height']) 285*b7c941bbSAndroid Build Coastguard Worker if 'decode_to' in a: 286*b7c941bbSAndroid Build Coastguard Worker fmt = self.parse_fmt(a['output_format']) 287*b7c941bbSAndroid Build Coastguard Worker ofmt = self.parse_fmt(a['input_format']) 288*b7c941bbSAndroid Build Coastguard Worker else: 289*b7c941bbSAndroid Build Coastguard Worker fmt = self.parse_fmt(a['input_format']) 290*b7c941bbSAndroid Build Coastguard Worker ofmt = self.parse_fmt(a['output_format']) 291*b7c941bbSAndroid Build Coastguard Worker size = Size(max(fmt['width'], ofmt['width']), max(fmt['height'], ofmt['height'])) 292*b7c941bbSAndroid Build Coastguard Worker 293*b7c941bbSAndroid Build Coastguard Worker try: 294*b7c941bbSAndroid Build Coastguard Worker prefix = 'time_avg_stats_' 295*b7c941bbSAndroid Build Coastguard Worker if prefix + 'stdev' in a and a[prefix + 'avg']: 296*b7c941bbSAndroid Build Coastguard Worker stdev = (a[prefix + 'stdev'] * 1e3 / a[prefix + 'avg'] ** 2) 297*b7c941bbSAndroid Build Coastguard Worker data = ((device, build, codec, size, a[prefix + 'num'], stdev) + 298*b7c941bbSAndroid Build Coastguard Worker tuple(rateFn(a.get(prefix + i)) for i in points)) 299*b7c941bbSAndroid Build Coastguard Worker self.data.add(data) 300*b7c941bbSAndroid Build Coastguard Worker self.kind[codec] = (mime, 'decode_to' not in a, codec.lower().startswith('omx.google.')) 301*b7c941bbSAndroid Build Coastguard Worker self.devices.add(data[0]) 302*b7c941bbSAndroid Build Coastguard Worker except (KeyError, ZeroDivisionError): 303*b7c941bbSAndroid Build Coastguard Worker print(a, file=sys.stderr) 304*b7c941bbSAndroid Build Coastguard Worker raise 305*b7c941bbSAndroid Build Coastguard Worker 306*b7c941bbSAndroid Build Coastguard Worker def parse_json(self, json, device, build): 307*b7c941bbSAndroid Build Coastguard Worker for test, results in json: 308*b7c941bbSAndroid Build Coastguard Worker if test in ("video_encoder_performance", "video_decoder_performance"): 309*b7c941bbSAndroid Build Coastguard Worker try: 310*b7c941bbSAndroid Build Coastguard Worker if isinstance(results, list) and len(results[0]) and len(results[0][0]) == 2 and len(results[0][0][0]): 311*b7c941bbSAndroid Build Coastguard Worker for result in results: 312*b7c941bbSAndroid Build Coastguard Worker self.parse_perf(result, device, build) 313*b7c941bbSAndroid Build Coastguard Worker else: 314*b7c941bbSAndroid Build Coastguard Worker self.parse_perf(results, device, build) 315*b7c941bbSAndroid Build Coastguard Worker except KeyboardInterrupt: 316*b7c941bbSAndroid Build Coastguard Worker raise 317*b7c941bbSAndroid Build Coastguard Worker 318*b7c941bbSAndroid Build Coastguard Worker def parse_result(self, result): 319*b7c941bbSAndroid Build Coastguard Worker device, build = '', '' 320*b7c941bbSAndroid Build Coastguard Worker if not result.endswith('.zip'): 321*b7c941bbSAndroid Build Coastguard Worker print(f"cannot parse %{result}", file=sys.stderr) 322*b7c941bbSAndroid Build Coastguard Worker return 323*b7c941bbSAndroid Build Coastguard Worker 324*b7c941bbSAndroid Build Coastguard Worker try: 325*b7c941bbSAndroid Build Coastguard Worker with zipfile.ZipFile(result) as zip: 326*b7c941bbSAndroid Build Coastguard Worker resultInfo, testInfos = None, [] 327*b7c941bbSAndroid Build Coastguard Worker for info in zip.infolist(): 328*b7c941bbSAndroid Build Coastguard Worker if re.search(r'/GenericDeviceInfo.deviceinfo.json$', info.filename): 329*b7c941bbSAndroid Build Coastguard Worker resultInfo = info 330*b7c941bbSAndroid Build Coastguard Worker elif re.search(r'/Cts(Media|Video)(Decoder)?TestCases\.reportlog\.json$', info.filename): 331*b7c941bbSAndroid Build Coastguard Worker testInfos.append(info) 332*b7c941bbSAndroid Build Coastguard Worker if resultInfo: 333*b7c941bbSAndroid Build Coastguard Worker try: 334*b7c941bbSAndroid Build Coastguard Worker jsonFile = zip.open(resultInfo) 335*b7c941bbSAndroid Build Coastguard Worker jsonData = json.load(jsonFile) 336*b7c941bbSAndroid Build Coastguard Worker device, build = jsonData['build_device'], jsonData['build_id'] 337*b7c941bbSAndroid Build Coastguard Worker except ValueError: 338*b7c941bbSAndroid Build Coastguard Worker print(f"could not parse %{resultInfo.filename}", file=sys.stderr) 339*b7c941bbSAndroid Build Coastguard Worker for info in testInfos: 340*b7c941bbSAndroid Build Coastguard Worker jsonFile = zip.open(info) 341*b7c941bbSAndroid Build Coastguard Worker try: 342*b7c941bbSAndroid Build Coastguard Worker jsonData = json.load(jsonFile, object_pairs_hook=lambda items: items) 343*b7c941bbSAndroid Build Coastguard Worker except ValueError: 344*b7c941bbSAndroid Build Coastguard Worker print(f"cannot parse JSON in {info.filename}", file=sys.stderr) 345*b7c941bbSAndroid Build Coastguard Worker self.parse_json(jsonData, device, build) 346*b7c941bbSAndroid Build Coastguard Worker 347*b7c941bbSAndroid Build Coastguard Worker except zipfile.BadZipfile: 348*b7c941bbSAndroid Build Coastguard Worker raise ValueError('bad zipfile') 349*b7c941bbSAndroid Build Coastguard Worker 350*b7c941bbSAndroid Build Coastguard Worker 351*b7c941bbSAndroid Build Coastguard WorkerP = argparse.ArgumentParser("gar_v2") 352*b7c941bbSAndroid Build Coastguard WorkerP.add_argument("--dbg", "-v", action='store_true', help="dump debug info into xml") 353*b7c941bbSAndroid Build Coastguard WorkerP.add_argument("--ignore", "-I", action='store_true', help="ignore minimum sample count") 354*b7c941bbSAndroid Build Coastguard WorkerP.add_argument("result_zip", nargs="*") 355*b7c941bbSAndroid Build Coastguard WorkerA = P.parse_args() 356*b7c941bbSAndroid Build Coastguard Worker 357*b7c941bbSAndroid Build Coastguard WorkerD = Data() 358*b7c941bbSAndroid Build Coastguard Workerfor res in A.result_zip: 359*b7c941bbSAndroid Build Coastguard Worker D.parse_result(res) 360*b7c941bbSAndroid Build Coastguard WorkerD.summarize(A=A) 361