tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

video_playback_latency.py (5092B)


      1 # This Source Code Form is subject to the terms of the Mozilla Public
      2 # License, v. 2.0. If a copy of the MPL was not distributed with this
      3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
      4 
      5 import filters
      6 from base_python_support import BasePythonSupport
      7 
      8 
      9 class VideoPlaybackLatency(BasePythonSupport):
     10    def __init__(self, **kwargs):
     11        super().__init__(**kwargs)
     12        self._is_android = False
     13        self._is_chrome = False
     14 
     15    def setup_test(self, test, args):
     16        from cmdline import CHROME_ANDROID_APPS, CHROMIUM_DISTROS, DESKTOP_APPS
     17 
     18        self._is_android = args.app not in DESKTOP_APPS
     19        self._is_chrome = (
     20            args.app in CHROMIUM_DISTROS or args.app in CHROME_ANDROID_APPS
     21        )
     22 
     23    def modify_command(self, cmd, test):
     24        # Because of the aspect ratio of Android during recording,
     25        # most pixels are white, so we need to use a lower fraction.
     26        fraction = "0.25" if self._is_android else "0.7"
     27 
     28        # Firefox/Safari configuration allows video playback but
     29        # Chrome needs an explicit switch to enable it.
     30        if self._is_chrome:
     31            cmd += [
     32                "--chrome.enableVideoAutoplay",
     33                "true",
     34            ]
     35 
     36        cmd += [
     37            "--visualMetricsKeyColor",
     38            "poster",
     39            "0",
     40            "128",
     41            "220",
     42            "255",
     43            "220",
     44            "255",
     45            fraction,
     46            "--visualMetricsKeyColor",
     47            "firstFrame",
     48            "220",
     49            "255",
     50            "0",
     51            "60",
     52            "0",
     53            "60",
     54            fraction,
     55            "--visualMetricsKeyColor",
     56            "secondFrame",
     57            "0",
     58            "60",
     59            "0",
     60            "60",
     61            "220",
     62            "255",
     63            fraction,
     64            "--visualMetricsKeyColor",
     65            "lastFrame",
     66            "220",
     67            "255",
     68            "220",
     69            "255",
     70            "0",
     71            "128",
     72            fraction,
     73        ]
     74 
     75    def handle_result(self, bt_result, raw_result, last_result=False, **kwargs):
     76        measurements = {
     77            "poster": [],
     78            "posterEnd": [],
     79            "firstFrame": [],
     80            "secondFrame": [],
     81            "lastFrame": [],
     82            "estimatedFirstFrameLatency": [],
     83            "estimatedAnyFrameLatency": [],
     84        }
     85 
     86        fps = 30.0
     87        total_duration_ms = 1000.0
     88        frame_duration_ms = total_duration_ms / fps
     89 
     90        offsets = {
     91            "firstFrame": 0.0,
     92            "posterEnd": 0.0,
     93            "secondFrame": frame_duration_ms * 3.0,
     94            "lastFrame": total_duration_ms - frame_duration_ms,
     95        }
     96 
     97        # Gather the key frame start times of each page/cycle
     98        for cycle in raw_result["visualMetrics"]:
     99            measurement = {}
    100            for key, frames in cycle["KeyColorFrames"].items():
    101                if key not in measurements or not len(frames):
    102                    continue
    103                measurement[key] = frames[0]["startTimestamp"]
    104                if key == "poster":
    105                    measurement["posterEnd"] = frames[0]["endTimestamp"]
    106 
    107            for key in ["firstFrame", "posterEnd", "secondFrame", "lastFrame"]:
    108                if key not in measurement:
    109                    continue
    110                normalized_value = measurement[key] - offsets[key]
    111                if normalized_value <= 0:
    112                    continue
    113                measurements["estimatedFirstFrameLatency"].append(normalized_value)
    114                break
    115 
    116            for key, value in measurement.items():
    117                measurements[key].append(value)
    118                if key not in offsets:
    119                    continue
    120                normalized_value = value - offsets[key]
    121                if normalized_value <= 0:
    122                    continue
    123                measurements["estimatedAnyFrameLatency"].append(normalized_value)
    124 
    125        for measurement, values in measurements.items():
    126            bt_result["measurements"].setdefault(measurement, []).extend(values)
    127 
    128    def _build_subtest(self, measurement_name, replicates, test):
    129        unit = test.get("unit", "ms")
    130        if test.get("subtest_unit"):
    131            unit = test.get("subtest_unit")
    132 
    133        return {
    134            "name": measurement_name,
    135            "lowerIsBetter": test.get("lower_is_better", True),
    136            "alertThreshold": float(test.get("alert_threshold", 2.0)),
    137            "unit": unit,
    138            "replicates": replicates,
    139            "value": round(filters.geometric_mean(replicates), 3),
    140        }
    141 
    142    def summarize_test(self, test, suite, **kwargs):
    143        suite["type"] = "pageload"
    144        if suite["subtests"] == {}:
    145            suite["subtests"] = []
    146        for measurement_name, replicates in test["measurements"].items():
    147            if not replicates:
    148                continue
    149            suite["subtests"].append(
    150                self._build_subtest(measurement_name, replicates, test)
    151            )
    152        suite["subtests"].sort(key=lambda subtest: subtest["name"])