OLD | NEW |
---|---|
(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 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::StreamOpened() { | |
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 AudioOutputStream* AudioOutputMixer::StreamStarted( | |
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 StreamOpened(); | |
58 if (!physical_stream_.get()) | |
59 return NULL; | |
60 | |
61 bool should_start = proxies_.empty(); | |
62 { | |
63 base::AutoLock lock(lock_); | |
64 proxies_.push_back(stream_proxy); | |
65 } | |
66 // We cannot start physical stream under the lock, | |
67 // OnMoreData() would try acquiring it... | |
68 if (should_start) { | |
69 physical_stream_->SetVolume(1.0); | |
70 physical_stream_->Start(this); | |
71 } | |
72 return stream_proxy; | |
73 } | |
74 | |
75 void AudioOutputMixer::StreamStopped(AudioOutputStream* physical_stream, | |
76 AudioOutputProxy* stream_proxy) { | |
77 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
78 DCHECK_EQ(stream_proxy, physical_stream); | |
79 | |
80 // Because of possible deadlock we cannot stop physical stream | |
81 // under the lock, so acquire the lock, update proxy list, release the lock, | |
82 // and only then stop physical stream if necessary. | |
83 bool stop_physical_stream = false; | |
84 { | |
85 base::AutoLock lock(lock_); | |
86 ProxyList::iterator it = std::find(proxies_.begin(), | |
87 proxies_.end(), | |
88 stream_proxy); | |
89 if (it != proxies_.end()) { | |
90 proxies_.erase(it); | |
91 stop_physical_stream = proxies_.empty(); | |
92 } | |
93 } | |
94 if (physical_stream_.get()) { | |
95 if (stop_physical_stream) | |
96 physical_stream_->Stop(); | |
97 close_timer_.Reset(); | |
98 } | |
99 } | |
100 | |
101 void AudioOutputMixer::StreamVolumeSet(AudioOutputStream* physical_stream, | |
102 double volume) { | |
103 } | |
104 | |
105 void AudioOutputMixer::StreamClosed(AudioOutputProxy* stream_proxy) { | |
106 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
107 StreamStopped(stream_proxy, stream_proxy); | |
108 } | |
109 | |
110 void AudioOutputMixer::Shutdown() { | |
111 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
112 | |
113 // Cancel any pending tasks to close physical stream. | |
114 weak_this_.InvalidateWeakPtrs(); | |
115 | |
116 while (!proxies_.empty()) { | |
117 StreamStopped(proxies_[0], proxies_[0]); | |
118 } | |
119 ClosePhysicalStream(); | |
120 | |
121 // No AudioOutputProxy objects should hold a reference to us when we get | |
122 // to this stage. | |
123 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference"; | |
124 } | |
125 | |
126 void AudioOutputMixer::ClosePhysicalStream() { | |
127 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
128 if (proxies_.empty() && physical_stream_.get() != NULL) | |
129 physical_stream_.release()->Close(); | |
130 } | |
131 | |
132 // AudioSourceCallback implementation. | |
133 uint32 AudioOutputMixer::OnMoreData(AudioOutputStream* stream, | |
134 uint8* dest, | |
135 uint32 max_size, | |
136 AudioBuffersState buffers_state) { | |
137 max_size = std::min(max_size, | |
138 static_cast<uint32>(params_.GetBytesPerBuffer())); | |
139 base::AutoLock lock(lock_); | |
vrk (LEFT CHROMIUM)
2012/03/30 22:25:58
Hmm, I'm a little worried about lock contention. T
enal1
2012/04/04 18:46:55
Done.
| |
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 (ProxyList::iterator it = proxies_.begin(); it != proxies_.end(); ++it) { | |
153 AudioOutputProxy* stream_proxy = *it; | |
154 int pending_bytes = std::min(stream_proxy->pending_bytes(), | |
vrk (LEFT CHROMIUM)
2012/03/30 22:25:58
|pending_bytes| isn't actually used for anything u
enal1
2012/04/04 18:46:55
Done.
| |
155 buffers_state.pending_bytes); | |
156 // Note: there is no way we can deduce hardware_delay_bytes for the | |
157 // particular proxy stream. Use zero instead. | |
158 uint32 actual_size = stream_proxy->audio_source_callback()->OnMoreData( | |
vrk (LEFT CHROMIUM)
2012/03/30 22:25:58
Exposing audio_source_callback() is leaking implem
enal1
2012/04/04 18:46:55
Done.
| |
159 stream, | |
160 actual_dest, | |
161 max_size, | |
162 AudioBuffersState(buffers_state.pending_bytes, 0)); | |
vrk (LEFT CHROMIUM)
2012/03/30 22:25:58
You're right that there's no way to get the exact
enal1
2012/04/04 18:46:55
There are 2 different problems here:
(1) We don't
| |
163 | |
164 // Should update pending_bytes for each proxy. | |
165 // If stream ended, pending_bytes goes down by max_size. | |
166 if (actual_size == 0) { | |
167 pending_bytes -= max_size; | |
168 stream_proxy->set_pending_bytes(std::max(pending_bytes, 0)); | |
169 continue; | |
170 } | |
171 | |
172 // Otherwise, it goes up by amount of data. It cannot exceed max amount of | |
173 // data we can buffer, but we don't know that value. So we increment | |
174 // pending_bytes unconditionally but adjust it before actual use (which | |
175 // would be on a next OnMoreData() call). | |
176 stream_proxy->set_pending_bytes(pending_bytes + actual_size); | |
177 | |
178 // No need to mix muted stream. | |
179 double volume = 0.0; | |
180 stream_proxy->GetVolume(&volume); | |
181 if (volume == 0.0) | |
182 continue; | |
183 | |
184 // Different handling for first and all subsequent streams. | |
185 if (first_stream) { | |
vrk (LEFT CHROMIUM)
2012/03/30 22:25:58
Instead of the "first_stream" bool, please handle
enal1
2012/04/04 18:46:55
I thought about it, but decided against it -- (1)
| |
186 if (volume != 1.0) { | |
187 media::AdjustVolume(static_cast<void*>(actual_dest), | |
tommi (sloooow) - chröme
2012/04/03 13:47:51
cast not necessary
enal1
2012/04/04 18:46:55
Done.
| |
188 actual_size, | |
189 params_.channels(), | |
190 bytes_per_sample, | |
191 volume); | |
192 } | |
193 if (actual_size < max_size) | |
194 memset(dest + actual_size, 0, max_size - actual_size); | |
195 first_stream = false; | |
196 actual_dest = mixer_data_.get(); | |
197 actual_total_size = actual_size; | |
198 } else { | |
199 media::MixStreams(static_cast<void*>(dest), | |
tommi (sloooow) - chröme
2012/04/03 13:47:51
casts not necessary and static_cast to void* seems
enal1
2012/04/04 18:46:55
Done.
| |
200 static_cast<void*>(actual_dest), | |
201 actual_size, | |
202 bytes_per_sample, | |
203 volume); | |
204 actual_total_size = std::max(actual_size, actual_total_size); | |
205 } | |
206 } | |
207 return actual_total_size; | |
208 } | |
209 | |
210 void AudioOutputMixer::OnError(AudioOutputStream* stream, int code) { | |
211 base::AutoLock lock(lock_); | |
212 for (ProxyList::iterator it = proxies_.begin(); it != proxies_.end(); ++it) { | |
213 (*it)->audio_source_callback()->OnError(stream, code); | |
vrk (LEFT CHROMIUM)
2012/03/30 22:25:58
In the same vein as line 158, do not expose audio_
enal1
2012/04/04 18:46:55
Done.
| |
214 } | |
215 } | |
216 | |
217 void AudioOutputMixer::WaitTillDataReady() { | |
218 base::AutoLock lock(lock_); | |
219 for (ProxyList::iterator it = proxies_.begin(); it != proxies_.end(); ++it) { | |
220 (*it)->audio_source_callback()->WaitTillDataReady(); | |
vrk (LEFT CHROMIUM)
2012/03/30 22:25:58
In the same vein as line 158, do not expose audio_
enal1
2012/04/04 18:46:55
Done.
| |
221 } | |
222 } | |
OLD | NEW |