OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2012, Google Inc. All rights reserved. | 2 * Copyright (C) 2012, Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
148 DEFINE_TRACE(OfflineAudioContext) | 148 DEFINE_TRACE(OfflineAudioContext) |
149 { | 149 { |
150 visitor->trace(m_renderTarget); | 150 visitor->trace(m_renderTarget); |
151 visitor->trace(m_completeResolver); | 151 visitor->trace(m_completeResolver); |
152 visitor->trace(m_scheduledSuspends); | 152 visitor->trace(m_scheduledSuspends); |
153 BaseAudioContext::trace(visitor); | 153 BaseAudioContext::trace(visitor); |
154 } | 154 } |
155 | 155 |
156 ScriptPromise OfflineAudioContext::startOfflineRendering(ScriptState* scriptStat
e) | 156 ScriptPromise OfflineAudioContext::startOfflineRendering(ScriptState* scriptStat
e) |
157 { | 157 { |
158 ASSERT(isMainThread()); | 158 DCHECK(isMainThread()); |
159 | 159 |
160 // Calling close() on an OfflineAudioContext is not supported/allowed, | 160 // Calling close() on an OfflineAudioContext is not supported/allowed, |
161 // but it might well have been stopped by its execution context. | 161 // but it might well have been stopped by its execution context. |
162 // | 162 // |
163 // See: crbug.com/435867 | 163 // See: crbug.com/435867 |
164 if (isContextClosed()) { | 164 if (isContextClosed()) { |
165 return ScriptPromise::rejectWithDOMException( | 165 return ScriptPromise::rejectWithDOMException( |
166 scriptState, | 166 scriptState, |
167 DOMException::create( | 167 DOMException::create( |
168 InvalidStateError, | 168 InvalidStateError, |
(...skipping 11 matching lines...) Expand all Loading... |
180 | 180 |
181 // Can't call startRendering more than once. Return a rejected promise now. | 181 // Can't call startRendering more than once. Return a rejected promise now. |
182 if (m_isRenderingStarted) { | 182 if (m_isRenderingStarted) { |
183 return ScriptPromise::rejectWithDOMException( | 183 return ScriptPromise::rejectWithDOMException( |
184 scriptState, | 184 scriptState, |
185 DOMException::create( | 185 DOMException::create( |
186 InvalidStateError, | 186 InvalidStateError, |
187 "cannot call startRendering more than once")); | 187 "cannot call startRendering more than once")); |
188 } | 188 } |
189 | 189 |
190 ASSERT(!m_isRenderingStarted); | 190 DCHECK(!m_isRenderingStarted); |
191 | 191 |
192 m_completeResolver = ScriptPromiseResolver::create(scriptState); | 192 m_completeResolver = ScriptPromiseResolver::create(scriptState); |
193 | 193 |
194 // Start rendering and return the promise. | 194 // Start rendering and return the promise. |
195 m_isRenderingStarted = true; | 195 m_isRenderingStarted = true; |
196 setContextState(Running); | 196 setContextState(Running); |
197 destinationHandler().startRendering(); | 197 destinationHandler().startRendering(); |
198 | 198 |
199 return m_completeResolver->promise(); | 199 return m_completeResolver->promise(); |
200 } | 200 } |
(...skipping 11 matching lines...) Expand all Loading... |
212 { | 212 { |
213 // This CANNOT be called on OfflineAudioContext; this is only to implement | 213 // This CANNOT be called on OfflineAudioContext; this is only to implement |
214 // the pure virtual interface from BaseAudioContext. | 214 // the pure virtual interface from BaseAudioContext. |
215 RELEASE_NOTREACHED(); | 215 RELEASE_NOTREACHED(); |
216 | 216 |
217 return ScriptPromise(); | 217 return ScriptPromise(); |
218 } | 218 } |
219 | 219 |
220 ScriptPromise OfflineAudioContext::suspendContext(ScriptState* scriptState, doub
le when) | 220 ScriptPromise OfflineAudioContext::suspendContext(ScriptState* scriptState, doub
le when) |
221 { | 221 { |
222 ASSERT(isMainThread()); | 222 DCHECK(isMainThread()); |
223 | 223 |
224 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState)
; | 224 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState)
; |
225 ScriptPromise promise = resolver->promise(); | 225 ScriptPromise promise = resolver->promise(); |
226 | 226 |
227 // The render thread does not exist; reject the promise. | 227 // The render thread does not exist; reject the promise. |
228 if (!destinationHandler().offlineRenderThread()) { | 228 if (!destinationHandler().offlineRenderThread()) { |
229 resolver->reject(DOMException::create(InvalidStateError, | 229 resolver->reject(DOMException::create(InvalidStateError, |
230 "the rendering is already finished")); | 230 "the rendering is already finished")); |
231 return promise; | 231 return promise; |
232 } | 232 } |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
279 return promise; | 279 return promise; |
280 } | 280 } |
281 | 281 |
282 m_scheduledSuspends.add(frame, resolver); | 282 m_scheduledSuspends.add(frame, resolver); |
283 | 283 |
284 return promise; | 284 return promise; |
285 } | 285 } |
286 | 286 |
287 ScriptPromise OfflineAudioContext::resumeContext(ScriptState* scriptState) | 287 ScriptPromise OfflineAudioContext::resumeContext(ScriptState* scriptState) |
288 { | 288 { |
289 ASSERT(isMainThread()); | 289 DCHECK(isMainThread()); |
290 | 290 |
291 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState)
; | 291 ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState)
; |
292 ScriptPromise promise = resolver->promise(); | 292 ScriptPromise promise = resolver->promise(); |
293 | 293 |
294 // If the rendering has not started, reject the promise. | 294 // If the rendering has not started, reject the promise. |
295 if (!m_isRenderingStarted) { | 295 if (!m_isRenderingStarted) { |
296 resolver->reject(DOMException::create(InvalidStateError, | 296 resolver->reject(DOMException::create(InvalidStateError, |
297 "cannot resume an offline context that has not started")); | 297 "cannot resume an offline context that has not started")); |
298 return promise; | 298 return promise; |
299 } | 299 } |
300 | 300 |
301 // If the context is in a closed state, reject the promise. | 301 // If the context is in a closed state, reject the promise. |
302 if (contextState() == AudioContextState::Closed) { | 302 if (contextState() == AudioContextState::Closed) { |
303 resolver->reject(DOMException::create(InvalidStateError, | 303 resolver->reject(DOMException::create(InvalidStateError, |
304 "cannot resume a closed offline context")); | 304 "cannot resume a closed offline context")); |
305 return promise; | 305 return promise; |
306 } | 306 } |
307 | 307 |
308 // If the context is already running, resolve the promise without altering | 308 // If the context is already running, resolve the promise without altering |
309 // the current state or starting the rendering loop. | 309 // the current state or starting the rendering loop. |
310 if (contextState() == AudioContextState::Running) { | 310 if (contextState() == AudioContextState::Running) { |
311 resolver->resolve(); | 311 resolver->resolve(); |
312 return promise; | 312 return promise; |
313 } | 313 } |
314 | 314 |
315 ASSERT(contextState() == AudioContextState::Suspended); | 315 DCHECK_EQ(contextState(), AudioContextState::Suspended); |
316 | 316 |
317 // If the context is suspended, resume rendering by setting the state to | 317 // If the context is suspended, resume rendering by setting the state to |
318 // "Running". and calling startRendering(). Note that resuming is possible | 318 // "Running". and calling startRendering(). Note that resuming is possible |
319 // only after the rendering started. | 319 // only after the rendering started. |
320 setContextState(Running); | 320 setContextState(Running); |
321 destinationHandler().startRendering(); | 321 destinationHandler().startRendering(); |
322 | 322 |
323 // Resolve the promise immediately. | 323 // Resolve the promise immediately. |
324 resolver->resolve(); | 324 resolver->resolve(); |
325 | 325 |
326 return promise; | 326 return promise; |
327 } | 327 } |
328 | 328 |
329 void OfflineAudioContext::fireCompletionEvent() | 329 void OfflineAudioContext::fireCompletionEvent() |
330 { | 330 { |
331 ASSERT(isMainThread()); | 331 DCHECK(isMainThread()); |
332 | 332 |
333 // We set the state to closed here so that the oncomplete event handler sees | 333 // We set the state to closed here so that the oncomplete event handler sees |
334 // that the context has been closed. | 334 // that the context has been closed. |
335 setContextState(Closed); | 335 setContextState(Closed); |
336 | 336 |
337 AudioBuffer* renderedBuffer = renderTarget(); | 337 AudioBuffer* renderedBuffer = renderTarget(); |
338 | 338 |
339 ASSERT(renderedBuffer); | 339 DCHECK(renderedBuffer); |
340 if (!renderedBuffer) | 340 if (!renderedBuffer) |
341 return; | 341 return; |
342 | 342 |
343 // Avoid firing the event if the document has already gone away. | 343 // Avoid firing the event if the document has already gone away. |
344 if (getExecutionContext()) { | 344 if (getExecutionContext()) { |
345 // Call the offline rendering completion event listener and resolve the | 345 // Call the offline rendering completion event listener and resolve the |
346 // promise too. | 346 // promise too. |
347 dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer)); | 347 dispatchEvent(OfflineAudioCompletionEvent::create(renderedBuffer)); |
348 m_completeResolver->resolve(renderedBuffer); | 348 m_completeResolver->resolve(renderedBuffer); |
349 } else { | 349 } else { |
350 // The resolver should be rejected when the execution context is gone. | 350 // The resolver should be rejected when the execution context is gone. |
351 m_completeResolver->reject(DOMException::create(InvalidStateError, | 351 m_completeResolver->reject(DOMException::create(InvalidStateError, |
352 "the execution context does not exist")); | 352 "the execution context does not exist")); |
353 } | 353 } |
354 } | 354 } |
355 | 355 |
356 bool OfflineAudioContext::handlePreOfflineRenderTasks() | 356 bool OfflineAudioContext::handlePreOfflineRenderTasks() |
357 { | 357 { |
358 ASSERT(isAudioThread()); | 358 DCHECK(isAudioThread()); |
359 | 359 |
360 // OfflineGraphAutoLocker here locks the audio graph for this scope. Note | 360 // OfflineGraphAutoLocker here locks the audio graph for this scope. Note |
361 // that this locker does not use tryLock() inside because the timing of | 361 // that this locker does not use tryLock() inside because the timing of |
362 // suspension MUST NOT be delayed. | 362 // suspension MUST NOT be delayed. |
363 OfflineGraphAutoLocker locker(this); | 363 OfflineGraphAutoLocker locker(this); |
364 | 364 |
365 // Update the dirty state of the listener. | 365 // Update the dirty state of the listener. |
366 listener()->updateState(); | 366 listener()->updateState(); |
367 | 367 |
368 deferredTaskHandler().handleDeferredTasks(); | 368 deferredTaskHandler().handleDeferredTasks(); |
369 handleStoppableSourceNodes(); | 369 handleStoppableSourceNodes(); |
370 | 370 |
371 return shouldSuspend(); | 371 return shouldSuspend(); |
372 } | 372 } |
373 | 373 |
374 void OfflineAudioContext::handlePostOfflineRenderTasks() | 374 void OfflineAudioContext::handlePostOfflineRenderTasks() |
375 { | 375 { |
376 ASSERT(isAudioThread()); | 376 DCHECK(isAudioThread()); |
377 | 377 |
378 // OfflineGraphAutoLocker here locks the audio graph for the same reason | 378 // OfflineGraphAutoLocker here locks the audio graph for the same reason |
379 // above in |handlePreOfflineRenderTasks|. | 379 // above in |handlePreOfflineRenderTasks|. |
380 OfflineGraphAutoLocker locker(this); | 380 OfflineGraphAutoLocker locker(this); |
381 | 381 |
382 deferredTaskHandler().breakConnections(); | 382 deferredTaskHandler().breakConnections(); |
383 releaseFinishedSourceNodes(); | 383 releaseFinishedSourceNodes(); |
384 deferredTaskHandler().handleDeferredTasks(); | 384 deferredTaskHandler().handleDeferredTasks(); |
385 deferredTaskHandler().requestToDeleteHandlersOnMainThread(); | 385 deferredTaskHandler().requestToDeleteHandlersOnMainThread(); |
386 } | 386 } |
387 | 387 |
388 | 388 |
389 OfflineAudioDestinationHandler& OfflineAudioContext::destinationHandler() | 389 OfflineAudioDestinationHandler& OfflineAudioContext::destinationHandler() |
390 { | 390 { |
391 return static_cast<OfflineAudioDestinationHandler&>(destination()->audioDest
inationHandler()); | 391 return static_cast<OfflineAudioDestinationHandler&>(destination()->audioDest
inationHandler()); |
392 } | 392 } |
393 | 393 |
394 void OfflineAudioContext::resolveSuspendOnMainThread(size_t frame) | 394 void OfflineAudioContext::resolveSuspendOnMainThread(size_t frame) |
395 { | 395 { |
396 ASSERT(isMainThread()); | 396 DCHECK(isMainThread()); |
397 | 397 |
398 // Suspend the context first. This will fire onstatechange event. | 398 // Suspend the context first. This will fire onstatechange event. |
399 setContextState(Suspended); | 399 setContextState(Suspended); |
400 | 400 |
401 // Wait until the suspend map is available for the removal. | 401 // Wait until the suspend map is available for the removal. |
402 AutoLocker locker(this); | 402 AutoLocker locker(this); |
403 | 403 |
404 // If the context is going away, m_scheduledSuspends could have had all its
entries removed. | 404 // If the context is going away, m_scheduledSuspends could have had all its
entries removed. |
405 // Check for that here. | 405 // Check for that here. |
406 if (m_scheduledSuspends.size()) { | 406 if (m_scheduledSuspends.size()) { |
407 // |frame| must exist in the map. | 407 // |frame| must exist in the map. |
408 DCHECK(m_scheduledSuspends.contains(frame)); | 408 DCHECK(m_scheduledSuspends.contains(frame)); |
409 | 409 |
410 SuspendMap::iterator it = m_scheduledSuspends.find(frame); | 410 SuspendMap::iterator it = m_scheduledSuspends.find(frame); |
411 it->value->resolve(); | 411 it->value->resolve(); |
412 | 412 |
413 m_scheduledSuspends.remove(it); | 413 m_scheduledSuspends.remove(it); |
414 } | 414 } |
415 } | 415 } |
416 | 416 |
417 void OfflineAudioContext::rejectPendingResolvers() | 417 void OfflineAudioContext::rejectPendingResolvers() |
418 { | 418 { |
419 ASSERT(isMainThread()); | 419 DCHECK(isMainThread()); |
420 | 420 |
421 // Wait until the suspend map is available for removal. | 421 // Wait until the suspend map is available for removal. |
422 AutoLocker locker(this); | 422 AutoLocker locker(this); |
423 | 423 |
424 // Offline context is going away so reject any promises that are still pendi
ng. | 424 // Offline context is going away so reject any promises that are still pendi
ng. |
425 | 425 |
426 for (auto& pendingSuspendResolver : m_scheduledSuspends) { | 426 for (auto& pendingSuspendResolver : m_scheduledSuspends) { |
427 pendingSuspendResolver.value->reject(DOMException::create( | 427 pendingSuspendResolver.value->reject(DOMException::create( |
428 InvalidStateError, "Audio context is going away")); | 428 InvalidStateError, "Audio context is going away")); |
429 } | 429 } |
430 | 430 |
431 m_scheduledSuspends.clear(); | 431 m_scheduledSuspends.clear(); |
432 ASSERT(m_resumeResolvers.size() == 0); | 432 DCHECK_EQ(m_resumeResolvers.size(), 0u); |
433 | 433 |
434 rejectPendingDecodeAudioDataResolvers(); | 434 rejectPendingDecodeAudioDataResolvers(); |
435 } | 435 } |
436 | 436 |
437 bool OfflineAudioContext::shouldSuspend() | 437 bool OfflineAudioContext::shouldSuspend() |
438 { | 438 { |
439 ASSERT(isAudioThread()); | 439 DCHECK(isAudioThread()); |
440 | 440 |
441 // Note that the GraphLock is required before this check. Since this needs | 441 // Note that the GraphLock is required before this check. Since this needs |
442 // to run on the audio thread, OfflineGraphAutoLocker must be used. | 442 // to run on the audio thread, OfflineGraphAutoLocker must be used. |
443 if (m_scheduledSuspends.contains(currentSampleFrame())) | 443 if (m_scheduledSuspends.contains(currentSampleFrame())) |
444 return true; | 444 return true; |
445 | 445 |
446 return false; | 446 return false; |
447 } | 447 } |
448 | 448 |
449 } // namespace blink | 449 } // namespace blink |
450 | 450 |
OLD | NEW |