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