Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(218)

Side by Side Diff: services/media/audio/audio_output.cc

Issue 1424933002: Add an initial revision of an audio server. (Closed) Base URL: https://github.com/domokit/mojo.git@change4
Patch Set: refactor MixerKernel into a class to prepare for the addition of a linear interpolation sampler Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698