# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from page_sets import press_story
from telemetry import story
class WebrtcPage(press_story.PressStory):
def __init__(self, url, page_set, name, tags, extra_browser_args=None):
assert url.startswith('file://webrtc_cases/')
self.URL = url
self.NAME = name
super(WebrtcPage, self).__init__(page_set,
tags=tags,
extra_browser_args=extra_browser_args)
class GetUserMedia(WebrtcPage):
"""Why: Acquires a high definition (720p) local stream."""
def __init__(self, page_set, tags):
super(GetUserMedia, self).__init__(
url='file://webrtc_cases/resolution.html',
name='hd_local_stream_10s',
page_set=page_set, tags=tags)
def ExecuteTest(self, action_runner):
action_runner.ClickElement('button[id="hd"]')
action_runner.Wait(10)
class DataChannel(WebrtcPage):
"""Why: Transfer as much data as possible through a data channel in 10s."""
def __init__(self, page_set, tags):
super(DataChannel, self).__init__(
url='file://webrtc_cases/datatransfer.html',
name='10s_datachannel_transfer',
page_set=page_set, tags=tags)
def ExecuteTest(self, action_runner):
action_runner.ExecuteJavaScript('megsToSend.value = 100;')
action_runner.ClickElement('button[id="sendTheData"]')
action_runner.Wait(10)
def ParseTestResults(self, action_runner):
self.AddJavaScriptMeasurement(
'data_transferred',
'sizeInBytes_biggerIsBetter',
'receiveProgress.value',
description='Amount of data transferred by data channel in 10 seconds')
self.AddJavaScriptMeasurement(
'data_throughput',
'bytesPerSecond_biggerIsBetter',
'currentThroughput',
description='Throughput of the data transfer.')
class CanvasCapturePeerConnection(WebrtcPage):
"""Why: Sets up a canvas capture stream connection to a peer connection."""
def __init__(self, page_set, tags):
super(CanvasCapturePeerConnection, self).__init__(
url='file://webrtc_cases/canvas-capture.html',
name='canvas_capture_peer_connection',
page_set=page_set, tags=tags)
def ExecuteTest(self, action_runner):
with action_runner.CreateInteraction('Action_Canvas_PeerConnection',
repeatable=False):
action_runner.ClickElement('button[id="startButton"]')
action_runner.Wait(10)
class VideoCodecConstraints(WebrtcPage):
"""Why: Sets up a video codec to a peer connection."""
def __init__(self, page_set, video_codec, tags):
super(VideoCodecConstraints, self).__init__(
url='file://webrtc_cases/codec_constraints.html',
name='codec_constraints_%s' % video_codec.lower(),
page_set=page_set, tags=tags)
self.video_codec = video_codec
def ExecuteTest(self, action_runner):
with action_runner.CreateInteraction('Action_Codec_Constraints',
repeatable=False):
action_runner.ClickElement('input[id="%s"]' % self.video_codec)
action_runner.ClickElement('button[id="startButton"]')
action_runner.WaitForElement('button[id="callButton"]:enabled')
action_runner.ClickElement('button[id="callButton"]')
action_runner.Wait(20)
action_runner.ClickElement('button[id="hangupButton"]')
class MultiplePeerConnections(WebrtcPage):
"""Why: Sets up several peer connections in the same page."""
def __init__(self, page_set, tags):
super(MultiplePeerConnections, self).__init__(
url='file://webrtc_cases/multiple-peerconnections.html',
name='multiple_peerconnections',
page_set=page_set, tags=tags)
def ExecuteTest(self, action_runner):
with action_runner.CreateInteraction('Action_Create_PeerConnection',
repeatable=False):
# Set the number of peer connections to create to 10.
action_runner.ExecuteJavaScript(
'document.getElementById("num-peerconnections").value=10')
action_runner.ExecuteJavaScript(
'document.getElementById("cpuoveruse-detection").checked=false')
action_runner.ClickElement('button[id="start-test"]')
action_runner.Wait(20)
class PausePlayPeerConnections(WebrtcPage):
"""Why: Ensures frequent pause and plays of peer connection streams work."""
def __init__(self, page_set, tags):
super(PausePlayPeerConnections, self).__init__(
url='file://webrtc_cases/pause-play.html',
name='pause_play_peerconnections',
page_set=page_set, tags=tags)
def ExecuteTest(self, action_runner):
action_runner.ExecuteJavaScript(
'startTest({test_runtime_s}, {num_peerconnections},'
'{iteration_delay_ms}, "video");'.format(
test_runtime_s=20, num_peerconnections=10, iteration_delay_ms=20))
action_runner.Wait(20)
class InsertableStreamsAudioProcessing(WebrtcPage):
"""Why: processes/transforms audio using insertable streams."""
def __init__(self, page_set, tags):
super(InsertableStreamsAudioProcessing, self).__init__(
url='file://webrtc_cases/audio-processing.html',
name='insertable_streams_audio_processing',
page_set=page_set,
tags=tags,
extra_browser_args=(
'--enable-blink-features=WebCodecs,MediaStreamInsertableStreams'))
self.supported = None
def RunNavigateSteps(self, action_runner):
self.supported = action_runner.EvaluateJavaScript('''(function () {
try {
new MediaStreamTrackGenerator('audio');
return true;
} catch (e) {
return false;
}
})()''')
if self.supported:
super(InsertableStreamsAudioProcessing,
self).RunNavigateSteps(action_runner)
def ExecuteTest(self, action_runner):
self.AddMeasurement(
'supported', 'count_biggerIsBetter', 1 if self.supported else 0,
'Boolean flag indicating if this benchmark is supported by the browser.'
)
if not self.supported:
return
action_runner.WaitForJavaScriptCondition('!!audio')
action_runner.ExecuteJavaScript('start()')
action_runner.Wait(10)
class InsertableStreamsVideoProcessing(WebrtcPage):
"""Why: processes/transforms video in various ways."""
def __init__(self, page_set, source, transform, sink, tags):
super(InsertableStreamsVideoProcessing, self).__init__(
url='file://webrtc_cases/video-processing.html',
name=('insertable_streams_video_processing_%s_%s_%s' %
(source, transform, sink)),
page_set=page_set,
tags=tags,
extra_browser_args=(
'--enable-blink-features=WebCodecs,MediaStreamInsertableStreams'))
self.source = source
self.transform = transform
self.sink = sink
self.supported = None
def RunNavigateSteps(self, action_runner):
self.supported = action_runner.EvaluateJavaScript(
"typeof MediaStreamTrackProcessor !== 'undefined' &&"
"typeof MediaStreamTrackGenerator !== 'undefined'")
if self.supported:
super(InsertableStreamsVideoProcessing,
self).RunNavigateSteps(action_runner)
def ExecuteTest(self, action_runner):
self.AddMeasurement(
'supported', 'count_biggerIsBetter', 1 if self.supported else 0,
'Boolean flag indicating if this benchmark is supported by the browser.'
)
if not self.supported:
return
with action_runner.CreateInteraction('Start_Pipeline', repeatable=True):
action_runner.WaitForElement('select[id="sourceSelector"]:enabled')
action_runner.ExecuteJavaScript(
'document.getElementById("sourceSelector").value="%s";' % self.source)
action_runner.WaitForElement('select[id="transformSelector"]:enabled')
action_runner.ExecuteJavaScript(
'document.getElementById("transformSelector").value="%s";' %
self.transform)
action_runner.WaitForElement('select[id="sinkSelector"]:enabled')
action_runner.ExecuteJavaScript(
'document.getElementById("sinkSelector").value="%s";' % self.sink)
action_runner.ExecuteJavaScript(
'document.getElementById("sourceSelector").dispatchEvent('
' new InputEvent("input", {}));')
action_runner.WaitForElement('.sinkVideo')
action_runner.Wait(10)
self.AddJavaScriptMeasurement(
'sink_decoded_frames',
'count_biggerIsBetter',
'document.querySelector(".sinkVideo").webkitDecodedFrameCount',
description='Number of frames received at the sink video.')
class NegotiateTiming(WebrtcPage):
"""Why: Measure how long renegotiation takes with large SDP blobs."""
def __init__(self, page_set, tags):
super(NegotiateTiming,
self).__init__(url='file://webrtc_cases/negotiate-timing.html',
name='negotiate-timing',
page_set=page_set,
tags=tags)
def ExecuteTest(self, action_runner):
action_runner.ExecuteJavaScript('start()')
action_runner.WaitForJavaScriptCondition('!callButton.disabled')
action_runner.ExecuteJavaScript('call()')
action_runner.WaitForJavaScriptCondition('!renegotiateButton.disabled')
# Due to suspicion of renegotiate activating too early:
action_runner.Wait(1)
# Negotiate 50 transceivers, then negotiate back to 1, simulating Meet "pin"
action_runner.ExecuteJavaScript('videoSectionsField.value = 50')
action_runner.ExecuteJavaScript('renegotiate()')
action_runner.WaitForJavaScriptCondition('!renegotiateButton.disabled')
action_runner.ExecuteJavaScript('videoSectionsField.value = 1')
action_runner.ExecuteJavaScript('renegotiate()')
action_runner.WaitForJavaScriptCondition('!renegotiateButton.disabled')
# Negotiate back up to 50, simulating Meet "unpin". This is what gets measured.
action_runner.ExecuteJavaScript('videoSectionsField.value = 50')
action_runner.ExecuteJavaScript('renegotiate()')
action_runner.WaitForJavaScriptCondition('!renegotiateButton.disabled')
result = action_runner.EvaluateJavaScript('result')
self.AddMeasurement('callerSetLocalDescription',
'ms',
result['callerSetLocalDescription'],
description='Time for caller SetLocalDescription')
self.AddMeasurement('calleeSetLocalDescription',
'ms',
result['calleeSetLocalDescription'],
description='Time for callee SetLocalDescription')
self.AddMeasurement('callerSetRemoteDescription',
'ms',
result['callerSetRemoteDescription'],
description='Time for caller SetRemoteDescription')
self.AddMeasurement('calleeSetRemoteDescription',
'ms',
result['calleeSetRemoteDescription'],
description='Time for callee SetRemoteDescription')
self.AddMeasurement('callerCreateOffer',
'ms',
result['callerCreateOffer'],
description='Time for overall offer/answer handshake')
self.AddMeasurement('calleeCreateAnswer',
'ms',
result['calleeCreateAnswer'],
description='Time for overall offer/answer handshake')
self.AddMeasurement('elapsedTime',
'ms',
result['elapsedTime'],
description='Time for overall offer/answer handshake')
self.AddMeasurement(
'audioImpairment',
'count',
result['audioImpairment'],
description='Number of late audio samples concealed during negotiation')
class EncodedInsertableStreams(WebrtcPage):
"""Why: Performs encoded insertable streams."""
def __init__(self, page_set, tags):
super(EncodedInsertableStreams, self).__init__(
url='file://webrtc_cases/encoded-insertable-streams.html',
name='encoded_insertable_streams',
page_set=page_set,
tags=tags)
def ExecuteTest(self, action_runner):
with action_runner.CreateInteraction('Action_Create_PeerConnection',
repeatable=False):
# Set the number of peer connections to create to 10.
action_runner.ExecuteJavaScript(
'document.getElementById("num-peerconnections").value=10')
action_runner.ClickElement('button[id="start-test"]')
action_runner.Wait(20)
class WebrtcPageSet(story.StorySet):
def __init__(self):
super(WebrtcPageSet, self).__init__(
cloud_storage_bucket=story.PUBLIC_BUCKET)
self.AddStory(PausePlayPeerConnections(self, tags=['pauseplay']))
self.AddStory(MultiplePeerConnections(self, tags=['stress']))
self.AddStory(DataChannel(self, tags=['datachannel']))
self.AddStory(GetUserMedia(self, tags=['getusermedia']))
self.AddStory(CanvasCapturePeerConnection(self, tags=['smoothness']))
self.AddStory(VideoCodecConstraints(self, 'H264', tags=['videoConstraints']))
self.AddStory(VideoCodecConstraints(self, 'VP8', tags=['videoConstraints']))
self.AddStory(VideoCodecConstraints(self, 'VP9', tags=['videoConstraints']))
self.AddStory(
InsertableStreamsAudioProcessing(self, tags=['insertableStreams']))
self.AddStory(
InsertableStreamsVideoProcessing(self,
'camera',
'webgl',
'video',
tags=['insertableStreams']))
self.AddStory(
InsertableStreamsVideoProcessing(self,
'video',
'webgl',
'video',
tags=['insertableStreams']))
self.AddStory(
InsertableStreamsVideoProcessing(self,
'pc',
'webgl',
'video',
tags=['insertableStreams']))
self.AddStory(
InsertableStreamsVideoProcessing(self,
'camera',
'canvas2d',
'video',
tags=['insertableStreams']))
self.AddStory(
InsertableStreamsVideoProcessing(self,
'camera',
'noop',
'video',
tags=['insertableStreams']))
self.AddStory(
InsertableStreamsVideoProcessing(self,
'camera',
'webgl',
'pc',
tags=['insertableStreams']))
self.AddStory(NegotiateTiming(self, tags=['sdp']))
self.AddStory(EncodedInsertableStreams(self, tags=['stress']))