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

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 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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698