OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "base/bind.h" | |
6 #include "base/location.h" | |
7 #include "base/logging.h" | |
8 | |
9 #include "services/media/audio/audio_output.h" | |
10 #include "services/media/audio/audio_output_manager.h" | |
11 #include "services/media/audio/audio_track_to_output_link.h" | |
12 | |
13 namespace mojo { | |
14 namespace media { | |
15 namespace audio { | |
16 | |
17 // Function used in a base::Closure to defer the final part of a shutdown task | |
18 // to the main event loop. | |
19 static void FinishShutdownSelf(AudioOutputManager* manager, | |
20 AudioOutputWeakPtr weak_output) { | |
21 auto output = weak_output.lock(); | |
22 if (output) { | |
23 manager->ShutdownOutput(output); | |
24 } | |
25 } | |
26 | |
27 AudioOutput::AudioOutput(AudioOutputManager* manager) | |
28 : manager_(manager) { | |
29 DCHECK(manager_); | |
30 } | |
31 | |
32 AudioOutput::~AudioOutput() { | |
33 DCHECK(!task_runner_ && shutting_down_); | |
34 } | |
35 | |
36 MediaResult AudioOutput::AddTrackLink(AudioTrackToOutputLinkPtr link) { | |
37 MediaResult res = InitializeLink(link); | |
38 | |
39 if (res == MediaResult::OK) { | |
40 base::AutoLock lock(processing_lock_); | |
41 | |
42 // Assert that we are the output in this link. | |
43 DCHECK(this == link->GetOutput().get()); | |
jeffbrown
2015/11/04 23:43:32
Not sure I understand why this has to happen insid
johngro
2015/11/06 02:20:24
Link initialization happens outside of the scope o
| |
44 | |
45 if (shutting_down_) { | |
46 return MediaResult::SHUTTING_DOWN; | |
47 } | |
48 | |
49 auto insert_result = links_.emplace(link); | |
50 DCHECK(insert_result.second); | |
51 } else { | |
52 // TODO(johngro): Output didn't like this track for some reason... Should | |
53 // probably log something about this. | |
54 } | |
55 | |
56 return res; | |
57 } | |
58 | |
59 MediaResult AudioOutput::RemoveTrackLink( | |
60 const AudioTrackToOutputLinkPtr& link) { | |
61 base::AutoLock lock(processing_lock_); | |
62 | |
63 if (shutting_down_) { | |
64 return MediaResult::SHUTTING_DOWN; | |
65 } | |
66 | |
67 auto iter = links_.find(link); | |
68 if (iter == links_.end()) { | |
69 return MediaResult::NOT_FOUND; | |
70 } | |
71 | |
72 links_.erase(iter); | |
73 return MediaResult::OK; | |
74 } | |
75 | |
76 MediaResult AudioOutput::Init() { | |
77 return MediaResult::OK; | |
78 } | |
79 | |
80 void AudioOutput::Cleanup() { | |
81 } | |
82 | |
83 MediaResult AudioOutput::InitializeLink(const AudioTrackToOutputLinkPtr& link) { | |
84 DCHECK(link); | |
85 return MediaResult::OK; | |
86 } | |
87 | |
88 void AudioOutput::ScheduleCallback(LocalTime when) { | |
89 base::AutoLock lock(shutdown_lock_); | |
90 | |
91 // If we are in the process of shutting down, then we are no longer permitted | |
92 // to schedule callbacks. | |
jeffbrown
2015/11/04 23:43:32
There's a race condition here given that shutdown
johngro
2015/11/06 02:20:24
No, I do not think that there is. I took care to
| |
93 if (shutting_down_) { | |
94 DCHECK(!task_runner_); | |
95 return; | |
96 } | |
97 DCHECK(task_runner_); | |
98 | |
99 // TODO(johngro): Someday, if there is ever a way to schedule delayed tasks | |
100 // with absolute time, or with resolution better than microseconds, do so. | |
101 // Until then figure out the relative time for scheduling the task and do so. | |
102 LocalTime now = LocalClock::now(); | |
103 base::TimeDelta sched_time = (now > when) | |
104 ? base::TimeDelta::FromMicroseconds(0) | |
105 : base::TimeDelta::FromMicroseconds( | |
106 local_time::to_usec<int64_t>(when - now)); | |
107 | |
108 task_runner_->PostNonNestableDelayedTask( | |
109 FROM_HERE, | |
110 base::Bind(&ProcessThunk, weak_self_), | |
111 sched_time); | |
112 } | |
113 | |
114 void AudioOutput::ShutdownSelf() { | |
115 // If we are not already in the process of shutting down, send a message to | |
116 // the main message loop telling it to complete the shutdown process. | |
117 if (!BeginShutdown()) { | |
118 DCHECK(manager_); | |
119 manager_->ScheduleMessageLoopTask( | |
120 FROM_HERE, | |
121 base::Bind(&FinishShutdownSelf, manager_, weak_self_)); | |
122 } | |
123 } | |
124 | |
125 void AudioOutput::ProcessThunk(AudioOutputWeakPtr weak_output) { | |
126 // If we are still around by the time this callback fires, enter the procesing | |
127 // lock and dispatch to our derived class's implementation. | |
128 auto output = weak_output.lock(); | |
129 if (output) { | |
130 base::AutoLock lock(output->processing_lock_); | |
131 output->Process(); | |
132 } | |
133 } | |
134 | |
135 MediaResult AudioOutput::Init( | |
136 const AudioOutputPtr& self, | |
137 scoped_refptr<base::SequencedTaskRunner> task_runner) { | |
138 DCHECK(this == self.get()); | |
139 DCHECK(task_runner); | |
140 | |
141 // If our derived class failed to initialize, don't bother to hold onto the | |
142 // state we will need drive our callback engine. Begin the process of | |
143 // shutting ourselves down, the output manager will eventually finish the job | |
144 // for us. | |
145 MediaResult res = Init(); | |
146 if (res != MediaResult::OK) { | |
147 ShutdownSelf(); | |
148 return res; | |
149 } | |
150 | |
151 // Stash our callback state and schedule an immediate callback to get things | |
152 // running. | |
153 task_runner_ = task_runner; | |
154 weak_self_ = self; | |
155 task_runner_->PostNonNestableTask(FROM_HERE, | |
156 base::Bind(&ProcessThunk, weak_self_)); | |
157 | |
158 return MediaResult::OK; | |
159 } | |
160 | |
161 bool AudioOutput::BeginShutdown() { | |
162 // Start the process of shutting down if we have not already. This method may | |
163 // be called from either a processing context, or from the audio output | |
164 // manager. After it finishes, any pending processing callbacks will have | |
165 // been nerfed, although there may still be callbacks in flight. | |
166 base::AutoLock lock(shutdown_lock_); | |
167 | |
168 if (shutting_down_) { return true; } | |
169 | |
170 shutting_down_ = true; | |
171 task_runner_ = nullptr; | |
172 | |
173 return false; | |
174 } | |
175 | |
176 void AudioOutput::Shutdown() { | |
177 if (shut_down_) { return; } | |
178 | |
179 // TODO(johngro): Assert that we are running on the audio server's main | |
180 // message loop thread. | |
181 | |
182 // Make sure no new callbacks can be generated, and that pending callbacks | |
183 // have been nerfed. | |
184 BeginShutdown(); | |
185 | |
186 // Synchronize with any callbacks in flight. By acquiring and releasing the | |
187 // processing lock, we are guaranteed that we have no callbacks which are in | |
188 // the middle of processing, and that any pending callbacks will be nerfed. | |
189 // It is safe to destroy this audio output at any point in time after this. | |
190 processing_lock_.Acquire(); | |
191 processing_lock_.Release(); | |
jeffbrown
2015/11/04 23:43:32
Obviously this blocks the main thread but I have t
johngro
2015/11/06 02:20:24
I believe that I have already address this in resp
| |
192 | |
193 // Unlink ourselves from all of our tracks. Then go ahead and clear the track | |
194 // set. | |
195 for (const auto& link : links_) { | |
196 DCHECK(link); | |
197 AudioTrackImplPtr track = link->GetTrack(); | |
198 if (track) { | |
199 track->RemoveOutput(link); | |
200 } | |
201 } | |
202 links_.clear(); | |
203 | |
204 // Give our derived class a chance to clean up its resources. | |
205 Cleanup(); | |
206 | |
207 // We are now completely shut down. The only reason we have this flag is to | |
208 // make sure that Shutdown is idempotent. | |
209 shut_down_ = true; | |
210 } | |
211 | |
212 } // namespace audio | |
213 } // namespace media | |
214 } // namespace mojo | |
215 | |
OLD | NEW |