| 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 |