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 BaseAudioContext::trace(visitor); | 147 BaseAudioContext::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 BaseAudioContext. | 208 // the pure virtual interface from BaseAudioContext. |
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_EQ(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); |
Raymond Toy
2016/07/25 16:56:53
DCHECK_EQ(..., 0u)
HyungwookLee
2016/07/26 00:42:40
Done.
| |
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 |