| importScripts("mediasource-worker-util.js"); |
| |
| // Note, we do not use testharness.js utilities within the worker context |
| // because it also communicates using postMessage to the main HTML document's |
| // harness, and would confuse the test case message parsing there. |
| |
| let util = new MediaSourceWorkerUtil(); |
| let sourceBuffer; |
| |
| // Phases of this test case, in sequence: |
| const testPhase = { |
| // Main thread verifies initial unattached HTMLMediaElement duration is NaN |
| // and readyState is HAVE_NOTHING, then starts this worker. |
| // This worker creates a MediaSource, verifies its initial duration |
| // is NaN, creates an object URL for the MediaSource and sends the URL to the |
| // main thread. |
| kInitial: "Initial", |
| |
| // Main thread receives object URL, re-verifies that the media element |
| // duration is still NaN and readyState is still HAVE_NOTHING, and then sets |
| // the URL as the src of the media element, eventually causing worker |
| // mediaSource 'onsourceopen' event dispatch. |
| kAttaching: "Awaiting sourceopen event that signals attachment is setup", |
| |
| kRequestNaNDurationCheck: |
| "Sending request to main thread to verify expected duration of the freshly setup attachment", |
| kConfirmNaNDurationResult: |
| "Checking that main thread correctly ACK'ed the freshly setup attachment's duration verification request", |
| |
| kRequestHaveNothingReadyStateCheck: |
| "Sending request to main thread to verify expected readyState of HAVE_NOTHING of the freshly setup attachment", |
| kConfirmHaveNothingReadyStateResult: |
| "Checking that main thread correctly ACK'ed the freshly setup attachment's readyState HAVE_NOTHING verification request", |
| |
| kRequestSetDurationCheck: |
| "Sending request to main thread to verify explicitly set duration before any media data has been appended", |
| kConfirmSetDurationResult: |
| "Checking that main thread correctly ACK'ed the duration verification request of explicitly set duration before any media data has been appended", |
| |
| kRequestHaveNothingReadyStateRecheck: |
| "Sending request to main thread to recheck that the readyState is still HAVE_NOTHING", |
| kConfirmHaveNothingReadyStateRecheckResult: |
| "Checking that main thread correctly ACK'ed the request to recheck readyState of HAVE_NOTHING", |
| |
| kRequestAwaitNewDurationCheck: |
| "Buffering media and then sending request to main thread to await duration reaching the expected value due to buffering", |
| kConfirmAwaitNewDurationResult: |
| "Checking that main thread correctly ACK'ed the request to await duration reaching the expected value due to buffering", |
| |
| kRequestAtLeastHaveMetadataReadyStateCheck: |
| "Sending request to main thread to verify expected readyState of at least HAVE_METADATA due to buffering", |
| kConfirmAtLeastHaveMetadataReadyStateResult: |
| "Checking that main thread correctly ACK'ed the request to verify expected readyState of at least HAVE_METADATA due to buffering", |
| |
| }; |
| |
| let phase = testPhase.kInitial; |
| |
| // Setup handler for receipt of attachment completion. |
| util.mediaSource.addEventListener("sourceopen", () => { |
| URL.revokeObjectURL(util.mediaSourceObjectUrl); |
| assert(phase === testPhase.kAttaching, "Unexpected sourceopen received by Worker mediaSource."); |
| phase = testPhase.kRequestNaNDurationCheck; |
| processPhase(); |
| }, { once : true }); |
| |
| // Setup handler for receipt of acknowledgement of successful verifications from |
| // main thread. |ackVerificationData| contains the round-tripped verification |
| // request that the main thread just sent, and is used in processPhase to ensure |
| // the ACK for this phase matched the request for verification. |
| let ackVerificationData; |
| onmessage = e => { |
| if (e.data === undefined || e.data.subject !== messageSubject.ACK_VERIFIED || e.data.info === undefined) { |
| postMessage({ |
| subject: messageSubject.ERROR, |
| info: "Invalid message received by Worker" |
| }); |
| return; |
| } |
| |
| ackVerificationData = e.data.info; |
| processPhase(/* isResponseToAck */ true); |
| }; |
| |
| processPhase(); |
| |
| |
| // Returns true if checks succeed, false otherwise. |
| function checkAckVerificationData(expectedRequest) { |
| |
| // Compares only subject and info fields. Uses logic similar to testharness.js's |
| // same_value(x,y) to correctly handle NaN, but doesn't distinguish +0 from -0. |
| function messageValuesEqual(m1, m2) { |
| if (m1.subject !== m1.subject) { |
| // NaN case |
| if (m2.subject === m2.subject) |
| return false; |
| } else if (m1.subject !== m2.subject) { |
| return false; |
| } |
| |
| if (m1.info !== m1.info) { |
| // NaN case |
| return (m2.info !== m2.info); |
| } |
| |
| return m1.info === m2.info; |
| } |
| |
| if (messageValuesEqual(expectedRequest, ackVerificationData)) { |
| ackVerificationData = undefined; |
| return true; |
| } |
| |
| postMessage({ |
| subject: messageSubject.ERROR, |
| info: "ACK_VERIFIED message from main thread was for a mismatching request for this phase. phase=[" + phase + |
| "], expected request that would produce ACK in this phase=[" + JSON.stringify(expectedRequest) + |
| "], actual request reported with the ACK=[" + JSON.stringify(ackVerificationData) + "]" |
| }); |
| |
| ackVerificationData = undefined; |
| return false; |
| } |
| |
| function bufferMediaAndSendDurationVerificationRequest() { |
| sourceBuffer = util.mediaSource.addSourceBuffer(util.mediaMetadata.type); |
| sourceBuffer.onerror = (err) => { |
| postMessage({ subject: messageSubject.ERROR, info: err }); |
| }; |
| sourceBuffer.onupdateend = () => { |
| // Sanity check the duration. |
| // Unnecessary for this buffering, except helps with test coverage. |
| var duration = util.mediaSource.duration; |
| if (isNaN(duration) || duration <= 0.0) { |
| postMessage({ |
| subject: messageSubject.ERROR, |
| info: "mediaSource.duration " + duration + " is not within expected range (0,1)" |
| }); |
| return; |
| } |
| |
| // Await the main thread media element duration matching the worker |
| // mediaSource duration. |
| postMessage(getAwaitCurrentDurationRequest()); |
| }; |
| |
| util.mediaLoadPromise.then(mediaData => { sourceBuffer.appendBuffer(mediaData); }, |
| err => { postMessage({ subject: messageSubject.ERROR, info: err }) }); |
| } |
| |
| |
| function getAwaitCurrentDurationRequest() { |
| // Sanity check that we have a numeric duration value now. |
| const dur = util.mediaSource.duration; |
| assert(!Number.isNaN(dur), "Unexpected NaN duration in worker"); |
| return { subject: messageSubject.AWAIT_DURATION, info: dur }; |
| } |
| |
| function assert(conditionBool, description) { |
| if (conditionBool !== true) { |
| postMessage({ |
| subject: messageSubject.ERROR, |
| info: "Current test phase [" + phase + "] failed worker assertion. " + description |
| }); |
| } |
| } |
| |
| function processPhase(isResponseToAck = false) { |
| assert(!isResponseToAck || (phase !== testPhase.kInitial && phase !== testPhase.kAttaching), |
| "Phase does not expect verification ack receipt from main thread"); |
| |
| // Some static request messages useful in transmission and ACK verification. |
| const nanDurationCheckRequest = { subject: messageSubject.VERIFY_DURATION, info: NaN }; |
| const haveNothingReadyStateCheckRequest = { subject: messageSubject.VERIFY_HAVE_NOTHING }; |
| const setDurationCheckRequest = { subject: messageSubject.AWAIT_DURATION, info: 0.1 }; |
| const atLeastHaveMetadataReadyStateCheckRequest = { subject: messageSubject.VERIFY_AT_LEAST_HAVE_METADATA }; |
| |
| switch (phase) { |
| |
| case testPhase.kInitial: |
| assert(Number.isNaN(util.mediaSource.duration), "Initial unattached MediaSource duration must be NaN, but instead is " + util.mediaSource.duration); |
| phase = testPhase.kAttaching; |
| postMessage({ subject: messageSubject.OBJECT_URL, info: util.mediaSourceObjectUrl }); |
| break; |
| |
| case testPhase.kAttaching: |
| postMessage({ |
| subject: messageSubject.ERROR, |
| info: "kAttaching phase is handled by main thread and by worker onsourceopen, not this switch case." |
| }); |
| break; |
| |
| case testPhase.kRequestNaNDurationCheck: |
| assert(!isResponseToAck); |
| postMessage(nanDurationCheckRequest); |
| phase = testPhase.kConfirmNaNDurationResult; |
| break; |
| |
| case testPhase.kConfirmNaNDurationResult: |
| assert(isResponseToAck); |
| if (checkAckVerificationData(nanDurationCheckRequest)) { |
| phase = testPhase.kRequestHaveNothingReadyStateCheck; |
| processPhase(); |
| } |
| break; |
| |
| case testPhase.kRequestHaveNothingReadyStateCheck: |
| assert(!isResponseToAck); |
| postMessage(haveNothingReadyStateCheckRequest); |
| phase = testPhase.kConfirmHaveNothingReadyStateResult; |
| break; |
| |
| case testPhase.kConfirmHaveNothingReadyStateResult: |
| assert(isResponseToAck); |
| if (checkAckVerificationData(haveNothingReadyStateCheckRequest)) { |
| phase = testPhase.kRequestSetDurationCheck; |
| processPhase(); |
| } |
| break; |
| |
| case testPhase.kRequestSetDurationCheck: |
| assert(!isResponseToAck); |
| const newDuration = setDurationCheckRequest.info; |
| assert(!Number.isNaN(newDuration) && newDuration > 0); |
| |
| // Set the duration, then request verification. |
| util.mediaSource.duration = newDuration; |
| postMessage(setDurationCheckRequest); |
| phase = testPhase.kConfirmSetDurationResult; |
| break; |
| |
| case testPhase.kConfirmSetDurationResult: |
| assert(isResponseToAck); |
| if (checkAckVerificationData(setDurationCheckRequest)) { |
| phase = testPhase.kRequestHaveNothingReadyStateRecheck; |
| processPhase(); |
| } |
| break; |
| |
| case testPhase.kRequestHaveNothingReadyStateRecheck: |
| assert(!isResponseToAck); |
| postMessage(haveNothingReadyStateCheckRequest); |
| phase = testPhase.kConfirmHaveNothingReadyStateRecheckResult; |
| break; |
| |
| case testPhase.kConfirmHaveNothingReadyStateRecheckResult: |
| assert(isResponseToAck); |
| if (checkAckVerificationData(haveNothingReadyStateCheckRequest)) { |
| phase = testPhase.kRequestAwaitNewDurationCheck; |
| processPhase(); |
| } |
| break; |
| |
| case testPhase.kRequestAwaitNewDurationCheck: |
| assert(!isResponseToAck); |
| bufferMediaAndSendDurationVerificationRequest(); |
| phase = testPhase.kConfirmAwaitNewDurationResult; |
| break; |
| |
| case testPhase.kConfirmAwaitNewDurationResult: |
| assert(isResponseToAck); |
| if (checkAckVerificationData(getAwaitCurrentDurationRequest())) { |
| phase = testPhase.kRequestAtLeastHaveMetadataReadyStateCheck; |
| processPhase(); |
| } |
| break; |
| |
| case testPhase.kRequestAtLeastHaveMetadataReadyStateCheck: |
| assert(!isResponseToAck); |
| postMessage(atLeastHaveMetadataReadyStateCheckRequest); |
| phase = testPhase.kConfirmAtLeastHaveMetadataReadyStateResult; |
| break; |
| |
| case testPhase.kConfirmAtLeastHaveMetadataReadyStateResult: |
| assert(isResponseToAck); |
| if (checkAckVerificationData(atLeastHaveMetadataReadyStateCheckRequest)) { |
| postMessage({ subject: messageSubject.WORKER_DONE }); |
| } |
| phase = "No further phase processing should occur once WORKER_DONE message has been sent"; |
| break; |
| |
| default: |
| postMessage({ |
| subject: messageSubject.ERROR, |
| info: "Unexpected test phase in worker:" + phase, |
| }); |
| } |
| |
| } |