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

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
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 AudioOutputMixer::AudioOutputMixer(AudioManager* audio_manager,
18 const AudioParameters& params,
19 const base::TimeDelta& close_delay)
20 : AudioOutputDispatcher(audio_manager, params),
21 ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)),
22 close_timer_(FROM_HERE,
23 close_delay,
24 weak_this_.GetWeakPtr(),
25 &AudioOutputMixer::ClosePhysicalStream) {
26 // TODO(enal): align data.
27 mixer_data_.reset(new uint8[params_.GetBytesPerBuffer()]);
28 }
29
30 AudioOutputMixer::~AudioOutputMixer() {
31 }
32
33 bool AudioOutputMixer::OpenStream() {
34 DCHECK_EQ(MessageLoop::current(), message_loop_);
35
36 if (physical_stream_.get())
37 return true;
38 AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(params_);
39 if (!stream)
40 return false;
41 if (!stream->Open()) {
42 stream->Close();
43 return false;
44 }
45 physical_stream_.reset(stream);
46 close_timer_.Reset();
47 return true;
48 }
49
50 bool AudioOutputMixer::StartStream(
51 AudioOutputStream::AudioSourceCallback* callback,
52 AudioOutputProxy* stream_proxy) {
53 DCHECK_EQ(MessageLoop::current(), message_loop_);
54
55 // May need to re-open the physical stream if no active proxies and
56 // enough time had pass.
57 OpenStream();
58 if (!physical_stream_.get())
59 return false;
60
61 bool should_start = proxies_.empty();
62 {
63 base::AutoLock lock(lock_);
64 proxies_[stream_proxy].pending_bytes = 0;
65 }
66 // We cannot start physical stream under the lock,
vrk (LEFT CHROMIUM) 2012/04/06 23:37:05 I don't understand this problem. Start() should no
enal1 2012/04/16 22:01:35 Not right. Starting stream will call OnMoreData()
vrk (LEFT CHROMIUM) 2012/04/18 14:57:36 Ah, my mistake! I was only looking at the linux im
67 // OnMoreData() would try acquiring it...
68 if (should_start) {
69 physical_stream_->SetVolume(1.0);
70 physical_stream_->Start(this);
71 }
72 return true;
73 }
74
75 void AudioOutputMixer::StopStream(AudioOutputProxy* stream_proxy) {
76 DCHECK_EQ(MessageLoop::current(), message_loop_);
77
78 // Because of possible deadlock we cannot stop physical stream
vrk (LEFT CHROMIUM) 2012/04/06 23:37:05 Again, what is the deadlock you're worried about h
enal1 2012/04/16 22:01:35 Updated comment.
79 // under the lock, so acquire the lock, update proxy list, release the lock,
80 // and only then stop physical stream if necessary.
81 bool stop_physical_stream = false;
82 {
83 base::AutoLock lock(lock_);
84 ProxyMap::iterator it = proxies_.find(stream_proxy);
85 if (it != proxies_.end()) {
86 proxies_.erase(it);
87 stop_physical_stream = proxies_.empty();
88 }
89 }
90 if (physical_stream_.get()) {
91 if (stop_physical_stream)
92 physical_stream_->Stop();
93 close_timer_.Reset();
94 }
95 }
96
97 void AudioOutputMixer::StreamVolumeSet(AudioOutputProxy* stream_proxy,
98 double volume) {
99 }
100
101 void AudioOutputMixer::CloseStream(AudioOutputProxy* stream_proxy) {
102 DCHECK_EQ(MessageLoop::current(), message_loop_);
103 StopStream(stream_proxy);
104 }
105
106 void AudioOutputMixer::Shutdown() {
107 DCHECK_EQ(MessageLoop::current(), message_loop_);
108
109 // Cancel any pending tasks to close physical stream.
110 weak_this_.InvalidateWeakPtrs();
111
112 while (!proxies_.empty()) {
113 StopStream(proxies_.begin()->first);
vrk (LEFT CHROMIUM) 2012/04/06 23:37:05 nit: I don't like this because it assumes StopStre
enal1 2012/04/16 22:01:35 Updated comment in the header file, now it says th
114 }
115 ClosePhysicalStream();
116
117 // No AudioOutputProxy objects should hold a reference to us when we get
118 // to this stage.
119 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference";
120 }
121
122 void AudioOutputMixer::ClosePhysicalStream() {
123 DCHECK_EQ(MessageLoop::current(), message_loop_);
124 if (proxies_.empty() && physical_stream_.get() != NULL)
125 physical_stream_.release()->Close();
126 }
127
128 // AudioSourceCallback implementation.
129 uint32 AudioOutputMixer::OnMoreData(AudioOutputStream* stream,
vrk (LEFT CHROMIUM) 2012/04/06 23:37:05 What is the first parameter of OnMoreData() used f
enal1 2012/04/16 22:01:35 It is not used, and I am planning to get rid of it
130 uint8* dest,
131 uint32 max_size,
132 AudioBuffersState buffers_state) {
133 max_size = std::min(max_size,
134 static_cast<uint32>(params_.GetBytesPerBuffer()));
135 // TODO(enal): consider getting rid of lock as it is in time-critical code.
136 // E.g. swap |proxies_| with local variable, and merge 2 lists
137 // at the end. That would speed things up but complicate stopping
138 // the stream.
139 base::AutoLock lock(lock_);
140 if (proxies_.empty())
141 return 0;
142 uint32 actual_total_size = 0;
143 uint32 bytes_per_sample = params_.bits_per_sample() >> 3;
144
145 // Go through all the streams, getting data for every one of them
146 // and mixing it into destination.
147 // Minor optimization: for the first stream we are writing data directly into
148 // destination. This way we don't have to mix the data when there is only one
149 // active stream, and net win in other cases, too.
150 bool first_stream = true;
151 uint8* actual_dest = dest;
152 for (ProxyMap::iterator it = proxies_.begin(); it != proxies_.end(); ++it) {
153 AudioOutputProxy* stream_proxy = it->first;
154 ProxyData* proxy_data = &it->second;
155 // TODO(enal): We don't know |pending _bytes| for individual stream, and we
vrk (LEFT CHROMIUM) 2012/04/06 23:37:05 The pending bytes problem is a tricky one indeed..
enal1 2012/04/16 22:01:35 Yes, I checked that.
156 // should givw that value to individual stream's OnMoreData(). I believe it
157 // can be used there to evaluate exact position of data it should return.
158 // Current code "sorta works" if everything works perfectly, but would have
vrk (LEFT CHROMIUM) 2012/04/06 23:37:05 (sorry in advance for this huge comment, but I can
enal1 2012/04/16 22:01:35 We always feed entire buffer to the OS, so numbers
vrk (LEFT CHROMIUM) 2012/04/18 14:57:36 Thanks for the explanation! Yeah, sounds like some
159 // problems if some of the buffers are only partially filled -- we don't
160 // know how how much data was in the buffer OS returned to us, so we cannot
161 // correctly calculate new value. If we know number of buffers we can solve
162 // the problem by storing not one value but vector of them.
163 int pending_bytes = std::min(proxy_data->pending_bytes,
164 buffers_state.pending_bytes);
165 // Note: there is no way we can deduce hardware_delay_bytes for the
166 // particular proxy stream. Use zero instead.
167 uint32 actual_size = stream_proxy->OnMoreData(
168 stream_proxy,
169 actual_dest,
170 max_size,
171 AudioBuffersState(pending_bytes, 0));
172
173 // Should update pending_bytes for each proxy.
174 // If stream ended, pending_bytes goes down by max_size.
vrk (LEFT CHROMIUM) 2012/04/06 23:37:05 After a stream is ended, when does it get removed
enal1 2012/04/16 22:01:35 StopStream() does that.
175 if (actual_size == 0) {
176 pending_bytes -= max_size;
177 proxy_data->pending_bytes = std::max(pending_bytes, 0);
178 continue;
179 }
180
181 // Otherwise, it goes up by amount of data. It cannot exceed max amount of
182 // data we can buffer, but we don't know that value. So we increment
183 // pending_bytes unconditionally but adjust it before actual use (which
184 // would be on a next OnMoreData() call).
185 proxy_data->pending_bytes = pending_bytes + actual_size;
186
187 // No need to mix muted stream.
188 double volume = 0.0;
189 stream_proxy->GetVolume(&volume);
190 if (volume == 0.0)
191 continue;
192
193 // Different handling for first and all subsequent streams.
194 if (first_stream) {
195 if (volume != 1.0) {
196 media::AdjustVolume(actual_dest,
197 actual_size,
198 params_.channels(),
199 bytes_per_sample,
200 volume);
201 }
202 if (actual_size < max_size)
203 memset(dest + actual_size, 0, max_size - actual_size);
204 first_stream = false;
205 actual_dest = mixer_data_.get();
206 actual_total_size = actual_size;
207 } else {
208 media::MixStreams(dest,
209 actual_dest,
210 actual_size,
211 bytes_per_sample,
212 volume);
213 actual_total_size = std::max(actual_size, actual_total_size);
214 }
215 }
216 return actual_total_size;
217 }
218
219 void AudioOutputMixer::OnError(AudioOutputStream* stream, int code) {
220 base::AutoLock lock(lock_);
221 for (ProxyMap::iterator it = proxies_.begin(); it != proxies_.end(); ++it) {
222 it->first->OnError(it->first, code);
223 }
224 }
225
226 void AudioOutputMixer::WaitTillDataReady() {
227 base::AutoLock lock(lock_);
228 for (ProxyMap::iterator it = proxies_.begin(); it != proxies_.end(); ++it) {
229 it->first->WaitTillDataReady();
230 }
231 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698