mediasource-worker-duration.js (11249B)
1 importScripts("mediasource-worker-util.js"); 2 3 // Note, we do not use testharness.js utilities within the worker context 4 // because it also communicates using postMessage to the main HTML document's 5 // harness, and would confuse the test case message parsing there. 6 7 let util = new MediaSourceWorkerUtil(); 8 let sourceBuffer; 9 10 // Phases of this test case, in sequence: 11 const testPhase = { 12 // Main thread verifies initial unattached HTMLMediaElement duration is NaN 13 // and readyState is HAVE_NOTHING, then starts this worker. 14 // This worker creates a MediaSource, verifies its initial duration 15 // is NaN, creates an object URL for the MediaSource and sends the URL to the 16 // main thread. 17 kInitial: "Initial", 18 19 // Main thread receives MediaSourceHandle, re-verifies that the media element 20 // duration is still NaN and readyState is still HAVE_NOTHING, and then sets 21 // the handle as the srcObject of the media element, eventually causing worker 22 // mediaSource 'onsourceopen' event dispatch. 23 kAttaching: "Awaiting sourceopen event that signals attachment is setup", 24 25 kRequestNaNDurationCheck: 26 "Sending request to main thread to verify expected duration of the freshly setup attachment", 27 kConfirmNaNDurationResult: 28 "Checking that main thread correctly ACK'ed the freshly setup attachment's duration verification request", 29 30 kRequestHaveNothingReadyStateCheck: 31 "Sending request to main thread to verify expected readyState of HAVE_NOTHING of the freshly setup attachment", 32 kConfirmHaveNothingReadyStateResult: 33 "Checking that main thread correctly ACK'ed the freshly setup attachment's readyState HAVE_NOTHING verification request", 34 35 kRequestSetDurationCheck: 36 "Sending request to main thread to verify explicitly set duration before any media data has been appended", 37 kConfirmSetDurationResult: 38 "Checking that main thread correctly ACK'ed the duration verification request of explicitly set duration before any media data has been appended", 39 40 kRequestHaveNothingReadyStateRecheck: 41 "Sending request to main thread to recheck that the readyState is still HAVE_NOTHING", 42 kConfirmHaveNothingReadyStateRecheckResult: 43 "Checking that main thread correctly ACK'ed the request to recheck readyState of HAVE_NOTHING", 44 45 kRequestAwaitNewDurationCheck: 46 "Buffering media and then sending request to main thread to await duration reaching the expected value due to buffering", 47 kConfirmAwaitNewDurationResult: 48 "Checking that main thread correctly ACK'ed the request to await duration reaching the expected value due to buffering", 49 50 kRequestAtLeastHaveMetadataReadyStateCheck: 51 "Sending request to main thread to verify expected readyState of at least HAVE_METADATA due to buffering", 52 kConfirmAtLeastHaveMetadataReadyStateResult: 53 "Checking that main thread correctly ACK'ed the request to verify expected readyState of at least HAVE_METADATA due to buffering", 54 55 }; 56 57 let phase = testPhase.kInitial; 58 59 // Setup handler for receipt of attachment completion. 60 util.mediaSource.addEventListener("sourceopen", () => { 61 assert(phase === testPhase.kAttaching, "Unexpected sourceopen received by Worker mediaSource."); 62 phase = testPhase.kRequestNaNDurationCheck; 63 processPhase(); 64 }, { once : true }); 65 66 // Setup handler for receipt of acknowledgement of successful verifications from 67 // main thread. |ackVerificationData| contains the round-tripped verification 68 // request that the main thread just sent, and is used in processPhase to ensure 69 // the ACK for this phase matched the request for verification. 70 let ackVerificationData; 71 onmessage = e => { 72 if (e.data === undefined || e.data.subject !== messageSubject.ACK_VERIFIED || e.data.info === undefined) { 73 postMessage({ 74 subject: messageSubject.ERROR, 75 info: "Invalid message received by Worker" 76 }); 77 return; 78 } 79 80 ackVerificationData = e.data.info; 81 processPhase(/* isResponseToAck */ true); 82 }; 83 84 processPhase(); 85 86 87 // Returns true if checks succeed, false otherwise. 88 function checkAckVerificationData(expectedRequest) { 89 90 // Compares only subject and info fields. Uses logic similar to testharness.js's 91 // same_value(x,y) to correctly handle NaN, but doesn't distinguish +0 from -0. 92 function messageValuesEqual(m1, m2) { 93 if (m1.subject !== m1.subject) { 94 // NaN case 95 if (m2.subject === m2.subject) 96 return false; 97 } else if (m1.subject !== m2.subject) { 98 return false; 99 } 100 101 if (m1.info !== m1.info) { 102 // NaN case 103 return (m2.info !== m2.info); 104 } 105 106 return m1.info === m2.info; 107 } 108 109 if (messageValuesEqual(expectedRequest, ackVerificationData)) { 110 ackVerificationData = undefined; 111 return true; 112 } 113 114 postMessage({ 115 subject: messageSubject.ERROR, 116 info: "ACK_VERIFIED message from main thread was for a mismatching request for this phase. phase=[" + phase + 117 "], expected request that would produce ACK in this phase=[" + JSON.stringify(expectedRequest) + 118 "], actual request reported with the ACK=[" + JSON.stringify(ackVerificationData) + "]" 119 }); 120 121 ackVerificationData = undefined; 122 return false; 123 } 124 125 function bufferMediaAndSendDurationVerificationRequest() { 126 sourceBuffer = util.mediaSource.addSourceBuffer(util.mediaMetadata.type); 127 sourceBuffer.onerror = (err) => { 128 postMessage({ subject: messageSubject.ERROR, info: err }); 129 }; 130 sourceBuffer.onupdateend = () => { 131 // Sanity check the duration. 132 // Unnecessary for this buffering, except helps with test coverage. 133 var duration = util.mediaSource.duration; 134 if (isNaN(duration) || duration <= 0.0) { 135 postMessage({ 136 subject: messageSubject.ERROR, 137 info: "mediaSource.duration " + duration + " is not within expected range (0,1)" 138 }); 139 return; 140 } 141 142 // Await the main thread media element duration matching the worker 143 // mediaSource duration. 144 postMessage(getAwaitCurrentDurationRequest()); 145 }; 146 147 util.mediaLoadPromise.then(mediaData => { sourceBuffer.appendBuffer(mediaData); }, 148 err => { postMessage({ subject: messageSubject.ERROR, info: err }) }); 149 } 150 151 152 function getAwaitCurrentDurationRequest() { 153 // Sanity check that we have a numeric duration value now. 154 const dur = util.mediaSource.duration; 155 assert(!Number.isNaN(dur), "Unexpected NaN duration in worker"); 156 return { subject: messageSubject.AWAIT_DURATION, info: dur }; 157 } 158 159 function assert(conditionBool, description) { 160 if (conditionBool !== true) { 161 postMessage({ 162 subject: messageSubject.ERROR, 163 info: "Current test phase [" + phase + "] failed worker assertion. " + description 164 }); 165 } 166 } 167 168 function processPhase(isResponseToAck = false) { 169 assert(!isResponseToAck || (phase !== testPhase.kInitial && phase !== testPhase.kAttaching), 170 "Phase does not expect verification ack receipt from main thread"); 171 172 // Some static request messages useful in transmission and ACK verification. 173 const nanDurationCheckRequest = { subject: messageSubject.VERIFY_DURATION, info: NaN }; 174 const haveNothingReadyStateCheckRequest = { subject: messageSubject.VERIFY_HAVE_NOTHING }; 175 const setDurationCheckRequest = { subject: messageSubject.AWAIT_DURATION, info: 0.1 }; 176 const atLeastHaveMetadataReadyStateCheckRequest = { subject: messageSubject.VERIFY_AT_LEAST_HAVE_METADATA }; 177 178 switch (phase) { 179 180 case testPhase.kInitial: 181 assert(Number.isNaN(util.mediaSource.duration), "Initial unattached MediaSource duration must be NaN, but instead is " + util.mediaSource.duration); 182 phase = testPhase.kAttaching; 183 let handle = util.mediaSource.handle; 184 postMessage({ subject: messageSubject.HANDLE, info: handle }, { transfer: [handle] } ); 185 break; 186 187 case testPhase.kAttaching: 188 postMessage({ 189 subject: messageSubject.ERROR, 190 info: "kAttaching phase is handled by main thread and by worker onsourceopen, not this switch case." 191 }); 192 break; 193 194 case testPhase.kRequestNaNDurationCheck: 195 assert(!isResponseToAck); 196 postMessage(nanDurationCheckRequest); 197 phase = testPhase.kConfirmNaNDurationResult; 198 break; 199 200 case testPhase.kConfirmNaNDurationResult: 201 assert(isResponseToAck); 202 if (checkAckVerificationData(nanDurationCheckRequest)) { 203 phase = testPhase.kRequestHaveNothingReadyStateCheck; 204 processPhase(); 205 } 206 break; 207 208 case testPhase.kRequestHaveNothingReadyStateCheck: 209 assert(!isResponseToAck); 210 postMessage(haveNothingReadyStateCheckRequest); 211 phase = testPhase.kConfirmHaveNothingReadyStateResult; 212 break; 213 214 case testPhase.kConfirmHaveNothingReadyStateResult: 215 assert(isResponseToAck); 216 if (checkAckVerificationData(haveNothingReadyStateCheckRequest)) { 217 phase = testPhase.kRequestSetDurationCheck; 218 processPhase(); 219 } 220 break; 221 222 case testPhase.kRequestSetDurationCheck: 223 assert(!isResponseToAck); 224 const newDuration = setDurationCheckRequest.info; 225 assert(!Number.isNaN(newDuration) && newDuration > 0); 226 227 // Set the duration, then request verification. 228 util.mediaSource.duration = newDuration; 229 postMessage(setDurationCheckRequest); 230 phase = testPhase.kConfirmSetDurationResult; 231 break; 232 233 case testPhase.kConfirmSetDurationResult: 234 assert(isResponseToAck); 235 if (checkAckVerificationData(setDurationCheckRequest)) { 236 phase = testPhase.kRequestHaveNothingReadyStateRecheck; 237 processPhase(); 238 } 239 break; 240 241 case testPhase.kRequestHaveNothingReadyStateRecheck: 242 assert(!isResponseToAck); 243 postMessage(haveNothingReadyStateCheckRequest); 244 phase = testPhase.kConfirmHaveNothingReadyStateRecheckResult; 245 break; 246 247 case testPhase.kConfirmHaveNothingReadyStateRecheckResult: 248 assert(isResponseToAck); 249 if (checkAckVerificationData(haveNothingReadyStateCheckRequest)) { 250 phase = testPhase.kRequestAwaitNewDurationCheck; 251 processPhase(); 252 } 253 break; 254 255 case testPhase.kRequestAwaitNewDurationCheck: 256 assert(!isResponseToAck); 257 bufferMediaAndSendDurationVerificationRequest(); 258 phase = testPhase.kConfirmAwaitNewDurationResult; 259 break; 260 261 case testPhase.kConfirmAwaitNewDurationResult: 262 assert(isResponseToAck); 263 if (checkAckVerificationData(getAwaitCurrentDurationRequest())) { 264 phase = testPhase.kRequestAtLeastHaveMetadataReadyStateCheck; 265 processPhase(); 266 } 267 break; 268 269 case testPhase.kRequestAtLeastHaveMetadataReadyStateCheck: 270 assert(!isResponseToAck); 271 postMessage(atLeastHaveMetadataReadyStateCheckRequest); 272 phase = testPhase.kConfirmAtLeastHaveMetadataReadyStateResult; 273 break; 274 275 case testPhase.kConfirmAtLeastHaveMetadataReadyStateResult: 276 assert(isResponseToAck); 277 if (checkAckVerificationData(atLeastHaveMetadataReadyStateCheckRequest)) { 278 postMessage({ subject: messageSubject.WORKER_DONE }); 279 } 280 phase = "No further phase processing should occur once WORKER_DONE message has been sent"; 281 break; 282 283 default: 284 postMessage({ 285 subject: messageSubject.ERROR, 286 info: "Unexpected test phase in worker:" + phase, 287 }); 288 } 289 290 }