| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2010, Google Inc. All rights reserved. | 2 * Copyright (C) 2010, 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 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 88 | 88 |
| 89 // FIXME(dominicc): Devolve these constructors to AudioContext | 89 // FIXME(dominicc): Devolve these constructors to AudioContext |
| 90 // and OfflineAudioContext respectively. | 90 // and OfflineAudioContext respectively. |
| 91 | 91 |
| 92 // Constructor for rendering to the audio hardware. | 92 // Constructor for rendering to the audio hardware. |
| 93 BaseAudioContext::BaseAudioContext(Document* document) | 93 BaseAudioContext::BaseAudioContext(Document* document) |
| 94 : SuspendableObject(document), | 94 : SuspendableObject(document), |
| 95 destination_node_(nullptr), | 95 destination_node_(nullptr), |
| 96 is_cleared_(false), | 96 is_cleared_(false), |
| 97 is_resolving_resume_promises_(false), | 97 is_resolving_resume_promises_(false), |
| 98 has_posted_cleanup_task_(false), |
| 98 user_gesture_required_(false), | 99 user_gesture_required_(false), |
| 99 connection_count_(0), | 100 connection_count_(0), |
| 100 deferred_task_handler_(DeferredTaskHandler::Create()), | 101 deferred_task_handler_(DeferredTaskHandler::Create()), |
| 101 context_state_(kSuspended), | 102 context_state_(kSuspended), |
| 102 closed_context_sample_rate_(-1), | 103 closed_context_sample_rate_(-1), |
| 103 periodic_wave_sine_(nullptr), | 104 periodic_wave_sine_(nullptr), |
| 104 periodic_wave_square_(nullptr), | 105 periodic_wave_square_(nullptr), |
| 105 periodic_wave_sawtooth_(nullptr), | 106 periodic_wave_sawtooth_(nullptr), |
| 106 periodic_wave_triangle_(nullptr), | 107 periodic_wave_triangle_(nullptr), |
| 107 output_position_() { | 108 output_position_() { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 142 periodic_wave_sawtooth_(nullptr), | 143 periodic_wave_sawtooth_(nullptr), |
| 143 periodic_wave_triangle_(nullptr), | 144 periodic_wave_triangle_(nullptr), |
| 144 output_position_() {} | 145 output_position_() {} |
| 145 | 146 |
| 146 BaseAudioContext::~BaseAudioContext() { | 147 BaseAudioContext::~BaseAudioContext() { |
| 147 GetDeferredTaskHandler().ContextWillBeDestroyed(); | 148 GetDeferredTaskHandler().ContextWillBeDestroyed(); |
| 148 // AudioNodes keep a reference to their context, so there should be no way to | 149 // AudioNodes keep a reference to their context, so there should be no way to |
| 149 // be in the destructor if there are still AudioNodes around. | 150 // be in the destructor if there are still AudioNodes around. |
| 150 DCHECK(!IsDestinationInitialized()); | 151 DCHECK(!IsDestinationInitialized()); |
| 151 DCHECK(!active_source_nodes_.size()); | 152 DCHECK(!active_source_nodes_.size()); |
| 152 DCHECK(!finished_source_handlers_.size()); | |
| 153 DCHECK(!is_resolving_resume_promises_); | 153 DCHECK(!is_resolving_resume_promises_); |
| 154 DCHECK(!resume_resolvers_.size()); | 154 DCHECK(!resume_resolvers_.size()); |
| 155 DCHECK(!autoplay_status_.has_value()); | 155 DCHECK(!autoplay_status_.has_value()); |
| 156 } | 156 } |
| 157 | 157 |
| 158 void BaseAudioContext::Initialize() { | 158 void BaseAudioContext::Initialize() { |
| 159 if (IsDestinationInitialized()) | 159 if (IsDestinationInitialized()) |
| 160 return; | 160 return; |
| 161 | 161 |
| 162 FFTFrame::Initialize(); | 162 FFTFrame::Initialize(); |
| (...skipping 530 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 693 WrapPersistent(this))); | 693 WrapPersistent(this))); |
| 694 } | 694 } |
| 695 | 695 |
| 696 void BaseAudioContext::NotifyStateChange() { | 696 void BaseAudioContext::NotifyStateChange() { |
| 697 DispatchEvent(Event::Create(EventTypeNames::statechange)); | 697 DispatchEvent(Event::Create(EventTypeNames::statechange)); |
| 698 } | 698 } |
| 699 | 699 |
| 700 void BaseAudioContext::NotifySourceNodeFinishedProcessing( | 700 void BaseAudioContext::NotifySourceNodeFinishedProcessing( |
| 701 AudioHandler* handler) { | 701 AudioHandler* handler) { |
| 702 DCHECK(IsAudioThread()); | 702 DCHECK(IsAudioThread()); |
| 703 MutexLocker lock(finished_source_handlers_mutex_); |
| 703 finished_source_handlers_.push_back(handler); | 704 finished_source_handlers_.push_back(handler); |
| 704 } | 705 } |
| 705 | 706 |
| 706 void BaseAudioContext::RemoveFinishedSourceNodes(bool needs_removal) { | |
| 707 DCHECK(IsAudioThread()); | |
| 708 | |
| 709 if (needs_removal) { | |
| 710 Platform::Current()->MainThread()->GetWebTaskRunner()->PostTask( | |
| 711 BLINK_FROM_HERE, | |
| 712 CrossThreadBind( | |
| 713 &BaseAudioContext::RemoveFinishedSourceNodesOnMainThread, | |
| 714 WrapCrossThreadPersistent(this))); | |
| 715 } | |
| 716 } | |
| 717 | |
| 718 void BaseAudioContext::RemoveFinishedSourceNodesOnMainThread() { | |
| 719 DCHECK(IsMainThread()); | |
| 720 AutoLocker locker(this); | |
| 721 // Quadratic worst case, but sizes of both vectors are considered | |
| 722 // manageable, especially |m_finishedSourceNodes| is likely to be short. | |
| 723 for (AudioNode* node : finished_source_nodes_) { | |
| 724 size_t i = active_source_nodes_.Find(node); | |
| 725 if (i != kNotFound) | |
| 726 active_source_nodes_.erase(i); | |
| 727 } | |
| 728 finished_source_nodes_.clear(); | |
| 729 } | |
| 730 | |
| 731 Document* BaseAudioContext::GetDocument() const { | 707 Document* BaseAudioContext::GetDocument() const { |
| 732 return ToDocument(GetExecutionContext()); | 708 return ToDocument(GetExecutionContext()); |
| 733 } | 709 } |
| 734 | 710 |
| 735 AutoplayPolicy::Type BaseAudioContext::GetAutoplayPolicy() const { | 711 AutoplayPolicy::Type BaseAudioContext::GetAutoplayPolicy() const { |
| 736 Document* document = GetDocument(); | 712 Document* document = GetDocument(); |
| 737 DCHECK(document); | 713 DCHECK(document); |
| 738 return AutoplayPolicy::GetAutoplayPolicyForDocument(*document); | 714 return AutoplayPolicy::GetAutoplayPolicyForDocument(*document); |
| 739 } | 715 } |
| 740 | 716 |
| 741 bool BaseAudioContext::AreAutoplayRequirementsFulfilled() const { | 717 bool BaseAudioContext::AreAutoplayRequirementsFulfilled() const { |
| 742 switch (GetAutoplayPolicy()) { | 718 switch (GetAutoplayPolicy()) { |
| 743 case AutoplayPolicy::Type::kNoUserGestureRequired: | 719 case AutoplayPolicy::Type::kNoUserGestureRequired: |
| 744 return true; | 720 return true; |
| 745 case AutoplayPolicy::Type::kUserGestureRequired: | 721 case AutoplayPolicy::Type::kUserGestureRequired: |
| 746 case AutoplayPolicy::Type::kUserGestureRequiredForCrossOrigin: | 722 case AutoplayPolicy::Type::kUserGestureRequiredForCrossOrigin: |
| 747 return UserGestureIndicator::ProcessingUserGesture(); | 723 return UserGestureIndicator::ProcessingUserGesture(); |
| 748 case AutoplayPolicy::Type::kDocumentUserActivationRequired: | 724 case AutoplayPolicy::Type::kDocumentUserActivationRequired: |
| 749 return GetDocument()->GetFrame() && | 725 return GetDocument()->GetFrame() && |
| 750 GetDocument()->GetFrame()->HasReceivedUserGesture(); | 726 GetDocument()->GetFrame()->HasReceivedUserGesture(); |
| 751 } | 727 } |
| 752 | 728 |
| 753 NOTREACHED(); | 729 NOTREACHED(); |
| 754 return false; | 730 return false; |
| 755 } | 731 } |
| 756 | 732 |
| 757 bool BaseAudioContext::ReleaseFinishedSourceNodes() { | |
| 758 DCHECK(IsGraphOwner()); | |
| 759 DCHECK(IsAudioThread()); | |
| 760 bool did_remove = false; | |
| 761 for (AudioHandler* handler : finished_source_handlers_) { | |
| 762 for (AudioNode* node : active_source_nodes_) { | |
| 763 if (finished_source_nodes_.Contains(node)) | |
| 764 continue; | |
| 765 if (handler == &node->Handler()) { | |
| 766 handler->BreakConnection(); | |
| 767 finished_source_nodes_.insert(node); | |
| 768 did_remove = true; | |
| 769 break; | |
| 770 } | |
| 771 } | |
| 772 } | |
| 773 finished_source_handlers_.clear(); | |
| 774 return did_remove; | |
| 775 } | |
| 776 | |
| 777 void BaseAudioContext::NotifySourceNodeStartedProcessing(AudioNode* node) { | 733 void BaseAudioContext::NotifySourceNodeStartedProcessing(AudioNode* node) { |
| 778 DCHECK(IsMainThread()); | 734 DCHECK(IsMainThread()); |
| 779 AutoLocker locker(this); | 735 AutoLocker locker(this); |
| 780 | 736 |
| 781 active_source_nodes_.push_back(node); | 737 active_source_nodes_.push_back(node); |
| 782 node->Handler().MakeConnection(); | 738 node->Handler().MakeConnection(); |
| 783 } | 739 } |
| 784 | 740 |
| 785 void BaseAudioContext::ReleaseActiveSourceNodes() { | 741 void BaseAudioContext::ReleaseActiveSourceNodes() { |
| 786 DCHECK(IsMainThread()); | 742 DCHECK(IsMainThread()); |
| 787 for (auto& source_node : active_source_nodes_) | 743 for (auto& source_node : active_source_nodes_) |
| 788 source_node->Handler().BreakConnection(); | 744 source_node->Handler().BreakConnection(); |
| 789 | 745 |
| 790 active_source_nodes_.clear(); | 746 active_source_nodes_.clear(); |
| 791 } | 747 } |
| 792 | 748 |
| 793 void BaseAudioContext::HandleStoppableSourceNodes() { | 749 void BaseAudioContext::HandleStoppableSourceNodes() { |
| 750 DCHECK(IsAudioThread()); |
| 794 DCHECK(IsGraphOwner()); | 751 DCHECK(IsGraphOwner()); |
| 795 | 752 |
| 796 // Find AudioBufferSourceNodes to see if we can stop playing them. | 753 if (active_source_nodes_.size()) |
| 797 for (AudioNode* node : active_source_nodes_) { | 754 ScheduleMainThreadCleanup(); |
| 798 // If the AudioNode has been marked as finished and released by | |
| 799 // the audio thread, but not yet removed by the main thread | |
| 800 // (see releaseActiveSourceNodes() above), |node| must not be | |
| 801 // touched as its handler may have been released already. | |
| 802 if (finished_source_nodes_.Contains(node)) | |
| 803 continue; | |
| 804 if (node->Handler().GetNodeType() == | |
| 805 AudioHandler::kNodeTypeAudioBufferSource) { | |
| 806 AudioBufferSourceNode* source_node = | |
| 807 static_cast<AudioBufferSourceNode*>(node); | |
| 808 source_node->GetAudioBufferSourceHandler().HandleStoppableSourceNode(); | |
| 809 } | |
| 810 } | |
| 811 } | 755 } |
| 812 | 756 |
| 813 void BaseAudioContext::HandlePreRenderTasks( | 757 void BaseAudioContext::HandlePreRenderTasks( |
| 814 const AudioIOPosition& output_position) { | 758 const AudioIOPosition& output_position) { |
| 815 DCHECK(IsAudioThread()); | 759 DCHECK(IsAudioThread()); |
| 816 | 760 |
| 817 // At the beginning of every render quantum, try to update the internal | 761 // At the beginning of every render quantum, try to update the internal |
| 818 // rendering graph state (from main thread changes). It's OK if the tryLock() | 762 // rendering graph state (from main thread changes). It's OK if the tryLock() |
| 819 // fails, we'll just take slightly longer to pick up the changes. | 763 // fails, we'll just take slightly longer to pick up the changes. |
| 820 if (TryLock()) { | 764 if (TryLock()) { |
| (...skipping 16 matching lines...) Expand all Loading... |
| 837 } | 781 } |
| 838 | 782 |
| 839 void BaseAudioContext::HandlePostRenderTasks() { | 783 void BaseAudioContext::HandlePostRenderTasks() { |
| 840 DCHECK(IsAudioThread()); | 784 DCHECK(IsAudioThread()); |
| 841 | 785 |
| 842 // Must use a tryLock() here too. Don't worry, the lock will very rarely be | 786 // Must use a tryLock() here too. Don't worry, the lock will very rarely be |
| 843 // contended and this method is called frequently. The worst that can happen | 787 // contended and this method is called frequently. The worst that can happen |
| 844 // is that there will be some nodes which will take slightly longer than usual | 788 // is that there will be some nodes which will take slightly longer than usual |
| 845 // to be deleted or removed from the render graph (in which case they'll | 789 // to be deleted or removed from the render graph (in which case they'll |
| 846 // render silence). | 790 // render silence). |
| 847 bool did_remove = false; | |
| 848 if (TryLock()) { | 791 if (TryLock()) { |
| 849 // Take care of AudioNode tasks where the tryLock() failed previously. | 792 // Take care of AudioNode tasks where the tryLock() failed previously. |
| 850 GetDeferredTaskHandler().BreakConnections(); | 793 GetDeferredTaskHandler().BreakConnections(); |
| 851 | 794 |
| 852 // Dynamically clean up nodes which are no longer needed. | |
| 853 did_remove = ReleaseFinishedSourceNodes(); | |
| 854 | |
| 855 GetDeferredTaskHandler().HandleDeferredTasks(); | 795 GetDeferredTaskHandler().HandleDeferredTasks(); |
| 856 GetDeferredTaskHandler().RequestToDeleteHandlersOnMainThread(); | 796 GetDeferredTaskHandler().RequestToDeleteHandlersOnMainThread(); |
| 857 | 797 |
| 858 unlock(); | 798 unlock(); |
| 859 } | 799 } |
| 860 | |
| 861 RemoveFinishedSourceNodes(did_remove); | |
| 862 } | 800 } |
| 863 | 801 |
| 864 void BaseAudioContext::ResolvePromisesForResumeOnMainThread() { | 802 void BaseAudioContext::PerformCleanupOnMainThread() { |
| 865 DCHECK(IsMainThread()); | 803 DCHECK(IsMainThread()); |
| 866 AutoLocker locker(this); | 804 AutoLocker locker(this); |
| 867 | 805 |
| 868 for (auto& resolver : resume_resolvers_) { | 806 if (is_resolving_resume_promises_) { |
| 869 if (context_state_ == kClosed) { | 807 for (auto& resolver : resume_resolvers_) { |
| 870 resolver->Reject(DOMException::Create( | 808 if (context_state_ == kClosed) { |
| 871 kInvalidStateError, "Cannot resume a context that has been closed")); | 809 resolver->Reject(DOMException::Create( |
| 872 } else { | 810 kInvalidStateError, |
| 873 SetContextState(kRunning); | 811 "Cannot resume a context that has been closed")); |
| 874 resolver->Resolve(); | 812 } else { |
| 813 SetContextState(kRunning); |
| 814 resolver->Resolve(); |
| 815 } |
| 875 } | 816 } |
| 817 resume_resolvers_.clear(); |
| 818 is_resolving_resume_promises_ = false; |
| 876 } | 819 } |
| 877 | 820 |
| 878 resume_resolvers_.clear(); | 821 if (active_source_nodes_.size()) { |
| 879 is_resolving_resume_promises_ = false; | 822 // Find AudioBufferSourceNodes to see if we can stop playing them. |
| 823 for (AudioNode* node : active_source_nodes_) { |
| 824 if (node->Handler().GetNodeType() == |
| 825 AudioHandler::kNodeTypeAudioBufferSource) { |
| 826 AudioBufferSourceNode* source_node = |
| 827 static_cast<AudioBufferSourceNode*>(node); |
| 828 source_node->GetAudioBufferSourceHandler().HandleStoppableSourceNode(); |
| 829 } |
| 830 } |
| 831 |
| 832 Vector<AudioHandler*> finished_handlers; |
| 833 { |
| 834 MutexLocker lock(finished_source_handlers_mutex_); |
| 835 finished_source_handlers_.swap(finished_handlers); |
| 836 } |
| 837 // Break the connection and release active nodes that have finished |
| 838 // playing. |
| 839 unsigned remove_count = 0; |
| 840 Vector<bool> removables; |
| 841 removables.resize(active_source_nodes_.size()); |
| 842 for (AudioHandler* handler : finished_handlers) { |
| 843 for (unsigned i = 0; i < active_source_nodes_.size(); ++i) { |
| 844 if (handler == &active_source_nodes_[i]->Handler()) { |
| 845 handler->BreakConnection(); |
| 846 removables[i] = true; |
| 847 remove_count++; |
| 848 break; |
| 849 } |
| 850 } |
| 851 } |
| 852 |
| 853 // Copy over the surviving active nodes. |
| 854 HeapVector<Member<AudioNode>> actives; |
| 855 actives.ReserveInitialCapacity(active_source_nodes_.size() - remove_count); |
| 856 for (unsigned i = 0; i < removables.size(); ++i) { |
| 857 if (!removables[i]) |
| 858 actives.push_back(active_source_nodes_[i]); |
| 859 } |
| 860 active_source_nodes_.swap(actives); |
| 861 } |
| 862 has_posted_cleanup_task_ = false; |
| 863 } |
| 864 |
| 865 void BaseAudioContext::ScheduleMainThreadCleanup() { |
| 866 if (has_posted_cleanup_task_) |
| 867 return; |
| 868 Platform::Current()->MainThread()->GetWebTaskRunner()->PostTask( |
| 869 BLINK_FROM_HERE, |
| 870 CrossThreadBind(&BaseAudioContext::PerformCleanupOnMainThread, |
| 871 WrapCrossThreadPersistent(this))); |
| 872 has_posted_cleanup_task_ = true; |
| 880 } | 873 } |
| 881 | 874 |
| 882 void BaseAudioContext::ResolvePromisesForResume() { | 875 void BaseAudioContext::ResolvePromisesForResume() { |
| 883 // This runs inside the BaseAudioContext's lock when handling pre-render | 876 // This runs inside the BaseAudioContext's lock when handling pre-render |
| 884 // tasks. | 877 // tasks. |
| 885 DCHECK(IsAudioThread()); | 878 DCHECK(IsAudioThread()); |
| 886 DCHECK(IsGraphOwner()); | 879 DCHECK(IsGraphOwner()); |
| 887 | 880 |
| 888 // Resolve any pending promises created by resume(). Only do this if we | 881 // Resolve any pending promises created by resume(). Only do this if we |
| 889 // haven't already started resolving these promises. This gets called very | 882 // haven't already started resolving these promises. This gets called very |
| 890 // often and it takes some time to resolve the promises in the main thread. | 883 // often and it takes some time to resolve the promises in the main thread. |
| 891 if (!is_resolving_resume_promises_ && resume_resolvers_.size() > 0) { | 884 if (!is_resolving_resume_promises_ && resume_resolvers_.size() > 0) { |
| 892 is_resolving_resume_promises_ = true; | 885 is_resolving_resume_promises_ = true; |
| 893 Platform::Current()->MainThread()->GetWebTaskRunner()->PostTask( | 886 ScheduleMainThreadCleanup(); |
| 894 BLINK_FROM_HERE, | |
| 895 CrossThreadBind(&BaseAudioContext::ResolvePromisesForResumeOnMainThread, | |
| 896 WrapCrossThreadPersistent(this))); | |
| 897 } | 887 } |
| 898 } | 888 } |
| 899 | 889 |
| 900 void BaseAudioContext::RejectPendingDecodeAudioDataResolvers() { | 890 void BaseAudioContext::RejectPendingDecodeAudioDataResolvers() { |
| 901 // Now reject any pending decodeAudioData resolvers | 891 // Now reject any pending decodeAudioData resolvers |
| 902 for (auto& resolver : decode_audio_resolvers_) | 892 for (auto& resolver : decode_audio_resolvers_) |
| 903 resolver->Reject(DOMException::Create(kInvalidStateError, | 893 resolver->Reject(DOMException::Create(kInvalidStateError, |
| 904 "Audio context is going away")); | 894 "Audio context is going away")); |
| 905 decode_audio_resolvers_.clear(); | 895 decode_audio_resolvers_.clear(); |
| 906 } | 896 } |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1022 } | 1012 } |
| 1023 | 1013 |
| 1024 SecurityOrigin* BaseAudioContext::GetSecurityOrigin() const { | 1014 SecurityOrigin* BaseAudioContext::GetSecurityOrigin() const { |
| 1025 if (GetExecutionContext()) | 1015 if (GetExecutionContext()) |
| 1026 return GetExecutionContext()->GetSecurityOrigin(); | 1016 return GetExecutionContext()->GetSecurityOrigin(); |
| 1027 | 1017 |
| 1028 return nullptr; | 1018 return nullptr; |
| 1029 } | 1019 } |
| 1030 | 1020 |
| 1031 } // namespace blink | 1021 } // namespace blink |
| OLD | NEW |