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

Side by Side Diff: media/audio/audio_output_mixer.cc

Issue 9691001: Audio software mixer. (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: Created 8 years, 8 months 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 | Annotate | Revision Log
« no previous file with comments | « media/audio/audio_output_mixer.h ('k') | media/audio/audio_output_proxy.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2012 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 "media/audio/audio_output_mixer.h"
6
7 #include <algorithm>
8
9 #include "base/bind.h"
10 #include "base/compiler_specific.h"
11 #include "base/message_loop.h"
12 #include "base/time.h"
13 #include "media/audio/audio_io.h"
14 #include "media/audio/audio_output_proxy.h"
15 #include "media/audio/audio_util.h"
16
17 namespace media {
18
19 AudioOutputMixer::AudioOutputMixer(AudioManager* audio_manager,
20 const AudioParameters& params,
21 const base::TimeDelta& close_delay)
22 : AudioOutputDispatcher(audio_manager, params),
23 ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)),
24 close_timer_(FROM_HERE,
25 close_delay,
26 weak_this_.GetWeakPtr(),
27 &AudioOutputMixer::ClosePhysicalStream) {
28 // TODO(enal): align data.
29 mixer_data_.reset(new uint8[params_.GetBytesPerBuffer()]);
30 }
31
32 AudioOutputMixer::~AudioOutputMixer() {
33 }
34
35 bool AudioOutputMixer::OpenStream() {
36 DCHECK_EQ(MessageLoop::current(), message_loop_);
37
38 if (physical_stream_.get())
39 return true;
40 AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(params_);
41 if (!stream)
42 return false;
43 if (!stream->Open()) {
44 stream->Close();
45 return false;
46 }
47 physical_stream_.reset(stream);
48 close_timer_.Reset();
49 return true;
50 }
51
52 bool AudioOutputMixer::StartStream(
53 AudioOutputStream::AudioSourceCallback* callback,
54 AudioOutputProxy* stream_proxy) {
55 DCHECK_EQ(MessageLoop::current(), message_loop_);
56
57 // May need to re-open the physical stream if no active proxies and
58 // enough time had pass.
59 OpenStream();
60 if (!physical_stream_.get())
61 return false;
62
63 double volume = 0.0;
64 stream_proxy->GetVolume(&volume);
65 bool should_start = proxies_.empty();
66 {
67 base::AutoLock lock(lock_);
68 ProxyData* proxy_data = &proxies_[stream_proxy];
69 proxy_data->audio_source_callback = callback;
70 proxy_data->volume = volume;
71 proxy_data->pending_bytes = 0;
72 }
73 // We cannot start physical stream under the lock,
74 // OnMoreData() would try acquiring it...
75 if (should_start) {
76 physical_stream_->SetVolume(1.0);
77 physical_stream_->Start(this);
78 }
79 return true;
80 }
81
82 void AudioOutputMixer::StopStream(AudioOutputProxy* stream_proxy) {
83 DCHECK_EQ(MessageLoop::current(), message_loop_);
84
85 // Because of possible deadlock we cannot stop physical stream under the lock
86 // (physical_stream_->Stop() can call OnError(), and it acquires the lock to
87 // iterate through proxies), so acquire the lock, update proxy list, release
88 // the lock, and only then stop physical stream if necessary.
89 bool stop_physical_stream = false;
90 {
91 base::AutoLock lock(lock_);
92 ProxyMap::iterator it = proxies_.find(stream_proxy);
93 if (it != proxies_.end()) {
94 proxies_.erase(it);
95 stop_physical_stream = proxies_.empty();
96 }
97 }
98 if (physical_stream_.get()) {
99 if (stop_physical_stream)
100 physical_stream_->Stop();
101 close_timer_.Reset();
102 }
103 }
104
105 void AudioOutputMixer::StreamVolumeSet(AudioOutputProxy* stream_proxy,
106 double volume) {
107 DCHECK_EQ(MessageLoop::current(), message_loop_);
108
109 ProxyMap::iterator it = proxies_.find(stream_proxy);
110
111 // Do nothing if stream is not currently playing.
112 if (it != proxies_.end()) {
113 base::AutoLock lock(lock_);
114 it->second.volume = volume;
115 }
116 }
117
118 void AudioOutputMixer::CloseStream(AudioOutputProxy* stream_proxy) {
119 DCHECK_EQ(MessageLoop::current(), message_loop_);
120
121 StopStream(stream_proxy);
122 }
123
124 void AudioOutputMixer::Shutdown() {
125 DCHECK_EQ(MessageLoop::current(), message_loop_);
126
127 // Cancel any pending tasks to close physical stream.
128 weak_this_.InvalidateWeakPtrs();
129
130 while (!proxies_.empty()) {
131 CloseStream(proxies_.begin()->first);
132 }
133 ClosePhysicalStream();
134
135 // No AudioOutputProxy objects should hold a reference to us when we get
136 // to this stage.
137 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference";
138 }
139
140 void AudioOutputMixer::ClosePhysicalStream() {
141 DCHECK_EQ(MessageLoop::current(), message_loop_);
142
143 if (proxies_.empty() && physical_stream_.get() != NULL)
144 physical_stream_.release()->Close();
145 }
146
147 // AudioSourceCallback implementation.
148 uint32 AudioOutputMixer::OnMoreData(AudioOutputStream* stream,
149 uint8* dest,
150 uint32 max_size,
151 AudioBuffersState buffers_state) {
152 max_size = std::min(max_size,
153 static_cast<uint32>(params_.GetBytesPerBuffer()));
154 // TODO(enal): consider getting rid of lock as it is in time-critical code.
155 // E.g. swap |proxies_| with local variable, and merge 2 lists
156 // at the end. That would speed things up but complicate stopping
157 // the stream.
158 base::AutoLock lock(lock_);
159 if (proxies_.empty())
160 return 0;
161 uint32 actual_total_size = 0;
162 uint32 bytes_per_sample = params_.bits_per_sample() >> 3;
163
164 // Go through all the streams, getting data for every one of them
165 // and mixing it into destination.
166 // Minor optimization: for the first stream we are writing data directly into
167 // destination. This way we don't have to mix the data when there is only one
168 // active stream, and net win in other cases, too.
169 bool first_stream = true;
170 uint8* actual_dest = dest;
171 for (ProxyMap::iterator it = proxies_.begin(); it != proxies_.end(); ++it) {
172 AudioOutputProxy* stream_proxy = it->first;
173 ProxyData* proxy_data = &it->second;
174 // TODO(enal): We don't know |pending _bytes| for individual stream, and we
175 // should give that value to individual stream's OnMoreData(). I believe it
176 // can be used there to evaluate exact position of data it should return.
177 // Current code "sorta works" if everything works perfectly, but would have
178 // problems if some of the buffers are only partially filled -- we don't
179 // know how how much data was in the buffer OS returned to us, so we cannot
180 // correctly calculate new value. If we know number of buffers we can solve
181 // the problem by storing not one value but vector of them.
182 int pending_bytes = std::min(proxy_data->pending_bytes,
183 buffers_state.pending_bytes);
184 // Note: there is no way we can deduce hardware_delay_bytes for the
185 // particular proxy stream. Use zero instead.
186 uint32 actual_size = proxy_data->audio_source_callback->OnMoreData(
187 stream_proxy,
188 actual_dest,
189 max_size,
190 AudioBuffersState(pending_bytes, 0));
191
192 // Should update pending_bytes for each proxy.
193 // If stream ended, pending_bytes goes down by max_size.
194 if (actual_size == 0) {
195 pending_bytes -= max_size;
196 proxy_data->pending_bytes = std::max(pending_bytes, 0);
197 continue;
198 }
199
200 // Otherwise, it goes up by amount of data. It cannot exceed max amount of
201 // data we can buffer, but we don't know that value. So we increment
202 // pending_bytes unconditionally but adjust it before actual use (which
203 // would be on a next OnMoreData() call).
204 proxy_data->pending_bytes = pending_bytes + actual_size;
205
206 // No need to mix muted stream.
207 double volume = proxy_data->volume;
208 if (volume == 0.0)
209 continue;
210
211 // Different handling for first and all subsequent streams.
212 if (first_stream) {
213 if (volume != 1.0) {
214 media::AdjustVolume(actual_dest,
215 actual_size,
216 params_.channels(),
217 bytes_per_sample,
218 volume);
219 }
220 if (actual_size < max_size)
221 memset(dest + actual_size, 0, max_size - actual_size);
222 first_stream = false;
223 actual_dest = mixer_data_.get();
224 actual_total_size = actual_size;
225 } else {
226 media::MixStreams(dest,
227 actual_dest,
228 actual_size,
229 bytes_per_sample,
230 volume);
231 actual_total_size = std::max(actual_size, actual_total_size);
232 }
233 }
234 return actual_total_size;
235 }
236
237 void AudioOutputMixer::OnError(AudioOutputStream* stream, int code) {
238 base::AutoLock lock(lock_);
239 for (ProxyMap::iterator it = proxies_.begin(); it != proxies_.end(); ++it) {
240 it->second.audio_source_callback->OnError(it->first, code);
241 }
242 }
243
244 void AudioOutputMixer::WaitTillDataReady() {
245 base::AutoLock lock(lock_);
246 for (ProxyMap::iterator it = proxies_.begin(); it != proxies_.end(); ++it) {
247 it->second.audio_source_callback->WaitTillDataReady();
248 }
249 }
250
251 } // namespace media
OLDNEW
« no previous file with comments | « media/audio/audio_output_mixer.h ('k') | media/audio/audio_output_proxy.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698