|  | <!DOCTYPE html> | 
|  | <html> | 
|  | <head> | 
|  | <title>RTCRtpScriptTransform Insertable Streams - Worker</title> | 
|  | <script src="/resources/testharness.js"></script> | 
|  | <script src="/resources/testharnessreport.js"></script> | 
|  | <script src=/resources/testdriver.js></script> | 
|  | <script src=/resources/testdriver-vendor.js></script> | 
|  | <script src='../../mediacapture-streams/permission-helper.js'></script> | 
|  | <script src="../../webrtc/RTCPeerConnection-helper.js"></script> | 
|  | </head> | 
|  | <body> | 
|  | <script> | 
|  |  | 
|  | function areArrayBuffersEqual(buffer1, buffer2) | 
|  | { | 
|  | if (buffer1.byteLength !== buffer2.byteLength) { | 
|  | return false; | 
|  | } | 
|  | let array1 = new Int8Array(buffer1); | 
|  | var array2 = new Int8Array(buffer2); | 
|  | for (let i = 0 ; i < buffer1.byteLength ; ++i) { | 
|  | if (array1[i] !== array2[i]) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  |  | 
|  | promise_test(async t => { | 
|  | const caller = new RTCPeerConnection(); | 
|  | t.add_cleanup(() => caller.close()); | 
|  | const callee = new RTCPeerConnection(); | 
|  | t.add_cleanup(() => callee.close()); | 
|  |  | 
|  | // Video is used in a later test, so we ask for both permissions | 
|  | await setMediaPermission(); | 
|  | const stream = await navigator.mediaDevices.getUserMedia({audio:true}); | 
|  | const track = stream.getTracks()[0]; | 
|  | t.add_cleanup(() => track.stop()); | 
|  |  | 
|  | const audioSender = caller.addTrack(track); | 
|  |  | 
|  | const senderWorker = new Worker('RTCRtpScriptTransform-sender-worker-single-frame.js'); | 
|  | audioSender.transform = new RTCRtpScriptTransform(senderWorker); | 
|  | t.add_cleanup(() => senderWorker.terminate()); | 
|  | let expectedFrameInfo = null; | 
|  | let numVerifiedFrames = 0; | 
|  | const onmessagePromise = new Promise(resolve => { | 
|  | senderWorker.onmessage = t.step_func(message => { | 
|  | if (!(message.data instanceof RTCEncodedAudioFrame)) { | 
|  | // This is the first message sent from the Worker to the test. | 
|  | // It contains an object (not an RTCEncodedAudioFrame) with the same | 
|  | // fields as the RTCEncodedAudioFrame to be sent in follow-up messages. | 
|  | // These serve as expected values to validate that the | 
|  | // RTCEncodedAudioFrame is sent correctly back to the test in the next | 
|  | // message. | 
|  | expectedFrameInfo = message.data; | 
|  | } else { | 
|  | // This is the frame sent by the Worker after reading it from the | 
|  | // readable stream. The Worker sends it twice after sending the | 
|  | // verification message. | 
|  | const frame = message.data; | 
|  | assert_equals(frame.type, expectedFrameInfo.type); | 
|  | assert_true(areArrayBuffersEqual(frame.data, expectedFrameInfo.data)); | 
|  | assert_equals(frame.getMetadata().synchronizationSource, expectedFrameInfo.metadata.synchronizationSource); | 
|  | assert_equals(frame.getMetadata().rtpTimestamp, expectedFrameInfo.metadata.rtpTimestamp); | 
|  |  | 
|  | if (++numVerifiedFrames == 2) | 
|  | resolve(); | 
|  | } | 
|  | }); | 
|  | }); | 
|  |  | 
|  | exchangeIceCandidates(caller, callee); | 
|  | await exchangeOfferAnswer(caller, callee); | 
|  |  | 
|  | return onmessagePromise; | 
|  | }, 'RTCRtpSender initializes its transform attribute and the Worker sends an RTCEncodedAudioFrame back'); | 
|  |  | 
|  | promise_test(async t => { | 
|  | const caller = new RTCPeerConnection(); | 
|  | t.add_cleanup(() => caller.close()); | 
|  | const callee = new RTCPeerConnection(); | 
|  | t.add_cleanup(() => callee.close()); | 
|  |  | 
|  | const stream = await navigator.mediaDevices.getUserMedia({video:true}); | 
|  | const videoTrack = stream.getVideoTracks()[0]; | 
|  | t.add_cleanup(() => videoTrack.stop()); | 
|  |  | 
|  | const videoSender = caller.addTrack(videoTrack) | 
|  |  | 
|  | const senderWorker = new Worker('RTCRtpScriptTransform-sender-worker-single-frame.js'); | 
|  | videoSender.transform = new RTCRtpScriptTransform(senderWorker); | 
|  | t.add_cleanup(() => senderWorker.terminate()); | 
|  |  | 
|  | let expectedFrameInfo = null; | 
|  | let numVerifiedFrames = 0; | 
|  | const onmessagePromise = new Promise(resolve => { | 
|  | senderWorker.onmessage = t.step_func(message => { | 
|  | if (!(message.data instanceof RTCEncodedVideoFrame)) { | 
|  | // This is the first message sent from the Worker to the test. | 
|  | // It contains an object (not an RTCEncodedVideoFrame) with the same | 
|  | // fields as the RTCEncodedVideoFrame to be sent in follow-up messages. | 
|  | // These serve as expected values to validate that the | 
|  | // RTCEncodedVideoFrame is sent correctly back to the test in the next | 
|  | // message. | 
|  | expectedFrameInfo = message.data; | 
|  | } else { | 
|  | // This is the frame sent by the Worker after reading it from the | 
|  | // readable stream. The Worker sends it twice after sending the | 
|  | // verification message. | 
|  | const frame = message.data; | 
|  | assert_equals(frame.type, expectedFrameInfo.type); | 
|  | assert_true(areArrayBuffersEqual(frame.data, expectedFrameInfo.data)); | 
|  | assert_equals(frame.getMetadata().synchronizationSource, expectedFrameInfo.metadata.synchronizationSource); | 
|  | assert_equals(frame.getMetadata().rtpTimestamp, expectedFrameInfo.metadata.rtpTimestamp); | 
|  | if (++numVerifiedFrames == 2) | 
|  | resolve(); | 
|  | } | 
|  | }); | 
|  | }); | 
|  |  | 
|  | exchangeIceCandidates(caller, callee); | 
|  | await exchangeOfferAnswer(caller, callee); | 
|  |  | 
|  | return onmessagePromise; | 
|  | }, 'RTCRtpSender initializes its transform attribute and the Worker sends an RTCEncodedVideoFrame back'); | 
|  |  | 
|  | </script> | 
|  | </body> | 
|  | </html> |