request-video-frame-callback-webrtc.https.html (5621B)
1 <!doctype html> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 5 <title>WebRTC video.requestVideoFrameCallback() test</title> 6 <script src="/webrtc/RTCPeerConnection-helper.js"></script> 7 </head> 8 <body> 9 <div id="log"></div> 10 <div> 11 <video id="local-view" muted autoplay="autoplay"></video> 12 <video id="remote-view" muted autoplay="autoplay"></video> 13 </div> 14 15 <!-- These files are in place when executing on W3C. --> 16 <script src="/resources/testharness.js"></script> 17 <script src="/resources/testharnessreport.js"></script> 18 <script type="text/javascript"> 19 var test = async_test('Test video.requestVideoFrameCallback() parameters for WebRTC applications.'); 20 21 // 22 // This test is based on /webrtc/simplecall.https.html, but it calls to 23 // video.requestVideoFrameCallback() before ending, to verify WebRTC required 24 // and optional parameters. 25 // 26 27 var gFirstConnection = null; 28 var gSecondConnection = null; 29 var verify_params = (now, metadata) => { 30 assert_greater_than(now, 0); 31 32 // Verify all required fields 33 assert_greater_than(metadata.presentationTime, 0); 34 assert_greater_than(metadata.expectedDisplayTime, 0); 35 assert_greater_than(metadata.presentedFrames, 0); 36 assert_greater_than(metadata.width, 0); 37 assert_greater_than(metadata.height, 0); 38 assert_true("mediaTime" in metadata, "mediaTime should be present"); 39 40 // Verify WebRTC only fields. 41 assert_true("rtpTimestamp" in metadata, "rtpTimestamp should be present"); 42 assert_true("receiveTime" in metadata, "receiveTime should be present"); 43 // Verifying that captureTime is too unreliable to be included in tests. 44 } 45 46 var verify_local_metadata = (now, metadata) => { 47 assert_greater_than(metadata.expectedDisplayTime, 0); 48 assert_greater_than(metadata.presentedFrames, 0); 49 assert_greater_than(metadata.width, 0); 50 assert_greater_than(metadata.height, 0); 51 assert_true("captureTime" in metadata, "captureTime should always be present for local sources."); 52 assert_greater_than(metadata.captureTime, 0); 53 } 54 55 // If the remote video gets video data that implies the negotiation 56 // as well as the ICE and DTLS connection are up. 57 document.getElementById('remote-view') 58 .addEventListener('loadedmetadata', function() { 59 document.getElementById('remote-view').requestVideoFrameCallback(test.step_func_done(verify_params)); 60 }); 61 62 document.getElementById('local-view') 63 .addEventListener('loadmetadata', function() { 64 document.getElementById('local-view').requestVideoFrameCallback(test.step_func_done(verify_local_metadata)); 65 }); 66 67 68 function getNoiseStreamOkCallback(localStream) { 69 gFirstConnection = new RTCPeerConnection(null); 70 test.add_cleanup(() => gFirstConnection.close()); 71 gFirstConnection.onicecandidate = onIceCandidateToFirst; 72 73 gSecondConnection = new RTCPeerConnection(null); 74 test.add_cleanup(() => gSecondConnection.close()); 75 gSecondConnection.onicecandidate = onIceCandidateToSecond; 76 gSecondConnection.ontrack = onRemoteTrack; 77 78 localStream.getTracks().forEach(function(track) { 79 // Bidirectional streams are needed in order for captureTime to be 80 // populated. Use the same source in both directions. 81 gFirstConnection.addTrack(track, localStream); 82 gSecondConnection.addTrack(track, localStream); 83 }); 84 85 gFirstConnection.createOffer().then(onOfferCreated, failed('createOffer')); 86 87 var videoTag = document.getElementById('local-view'); 88 videoTag.srcObject = localStream; 89 }; 90 91 var onOfferCreated = test.step_func(function(offer) { 92 gFirstConnection.setLocalDescription(offer); 93 94 // This would normally go across the application's signaling solution. 95 // In our case, the "signaling" is to call this function. 96 receiveCall(offer.sdp); 97 }); 98 99 function receiveCall(offerSdp) { 100 var parsedOffer = new RTCSessionDescription({ type: 'offer', 101 sdp: offerSdp }); 102 gSecondConnection.setRemoteDescription(parsedOffer); 103 104 gSecondConnection.createAnswer().then(onAnswerCreated, 105 failed('createAnswer')); 106 }; 107 108 var onAnswerCreated = test.step_func(function(answer) { 109 gSecondConnection.setLocalDescription(answer); 110 111 // Similarly, this would go over the application's signaling solution. 112 handleAnswer(answer.sdp); 113 }); 114 115 function handleAnswer(answerSdp) { 116 var parsedAnswer = new RTCSessionDescription({ type: 'answer', 117 sdp: answerSdp }); 118 gFirstConnection.setRemoteDescription(parsedAnswer); 119 }; 120 121 var onIceCandidateToFirst = test.step_func(function(event) { 122 // If event.candidate is null = no more candidates. 123 if (event.candidate) { 124 gSecondConnection.addIceCandidate(event.candidate); 125 } 126 }); 127 128 var onIceCandidateToSecond = test.step_func(function(event) { 129 if (event.candidate) { 130 gFirstConnection.addIceCandidate(event.candidate); 131 } 132 }); 133 134 var onRemoteTrack = test.step_func(function(event) { 135 var videoTag = document.getElementById('remote-view'); 136 if (!videoTag.srcObject) { 137 videoTag.srcObject = event.streams[0]; 138 } 139 }); 140 141 // Returns a suitable error callback. 142 function failed(function_name) { 143 return test.unreached_func('WebRTC called error callback for ' + function_name); 144 } 145 146 // This function starts the test. 147 test.step(function() { 148 getNoiseStream({ video: true, audio: true }) 149 .then(test.step_func(getNoiseStreamOkCallback), failed('getNoiseStream')); 150 }); 151 </script> 152 153 </body> 154 </html>