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_dispatcher_impl.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 AudioOutputDispatcherImpl::AudioOutputDispatcherImpl( | |
18 AudioManager* audio_manager, | |
19 const AudioParameters& params, | |
20 const base::TimeDelta& close_delay) | |
21 : AudioOutputDispatcher(audio_manager, params), | |
22 pause_delay_(base::TimeDelta::FromMilliseconds( | |
23 2 * params.frames_per_buffer() * | |
24 base::Time::kMillisecondsPerSecond / params.sample_rate())), | |
25 paused_proxies_(0), | |
26 ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)), | |
27 close_timer_(FROM_HERE, | |
28 close_delay, | |
29 weak_this_.GetWeakPtr(), | |
30 &AudioOutputDispatcherImpl::ClosePendingStreams) { | |
31 } | |
32 | |
33 AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() { | |
34 DCHECK(proxy_to_physical_map_.empty()); | |
35 } | |
36 | |
37 bool AudioOutputDispatcherImpl::OpenStream() { | |
38 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
39 | |
40 paused_proxies_++; | |
41 | |
42 // Ensure that there is at least one open stream. | |
43 if (idle_streams_.empty() && !CreateAndOpenStream()) | |
44 return false; | |
45 | |
46 close_timer_.Reset(); | |
47 return true; | |
48 } | |
49 | |
50 bool AudioOutputDispatcherImpl::StartStream( | |
51 AudioOutputStream::AudioSourceCallback* callback, | |
52 AudioOutputProxy* stream_proxy) { | |
53 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
54 | |
55 if (idle_streams_.empty() && !CreateAndOpenStream()) | |
56 return false; | |
57 | |
58 AudioOutputStream* physical_stream = idle_streams_.back(); | |
59 DCHECK_NE(static_cast<AudioOutputStream*>(NULL), physical_stream); | |
vrk (LEFT CHROMIUM)
2012/04/06 23:37:05
DCHECK(physical_stream)?
enal1
2012/04/16 22:01:35
Done.
| |
60 idle_streams_.pop_back(); | |
61 | |
62 DCHECK_GT(paused_proxies_, 0u); | |
63 --paused_proxies_; | |
64 | |
65 close_timer_.Reset(); | |
66 | |
67 // Schedule task to allocate streams for other proxies if we need to. | |
68 message_loop_->PostTask(FROM_HERE, base::Bind( | |
69 &AudioOutputDispatcherImpl::OpenTask, weak_this_.GetWeakPtr())); | |
70 | |
71 double volume = 0; | |
72 stream_proxy->GetVolume(&volume); | |
73 physical_stream->SetVolume(volume); | |
74 physical_stream->Start(callback); | |
75 proxy_to_physical_map_[stream_proxy] = physical_stream; | |
76 return true; | |
77 } | |
78 | |
79 void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) { | |
80 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
81 | |
82 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); | |
83 DCHECK(it != proxy_to_physical_map_.end()); | |
84 AudioOutputStream* physical_stream = it->second; | |
85 proxy_to_physical_map_.erase(it); | |
86 | |
87 DCHECK_NE(static_cast<AudioOutputStream*>(NULL), physical_stream); | |
vrk (LEFT CHROMIUM)
2012/04/06 23:37:05
Remove DCHECK: doing a null DCHECK before usage is
enal1
2012/04/16 22:01:35
Done.
| |
88 physical_stream->Stop(); | |
89 | |
90 ++paused_proxies_; | |
91 | |
92 pausing_streams_.push_front(physical_stream); | |
93 | |
94 // Don't recycle stream until two buffers worth of time has elapsed. | |
95 message_loop_->PostDelayedTask( | |
96 FROM_HERE, | |
97 base::Bind(&AudioOutputDispatcherImpl::StopStreamTask, | |
98 weak_this_.GetWeakPtr()), | |
99 pause_delay_); | |
100 } | |
101 | |
102 void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy, | |
103 double volume) { | |
104 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
105 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); | |
106 if (it != proxy_to_physical_map_.end()) { | |
107 AudioOutputStream* physical_stream = it->second; | |
108 physical_stream->SetVolume(volume); | |
109 } | |
110 } | |
111 | |
112 void AudioOutputDispatcherImpl::StopStreamTask() { | |
113 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
114 | |
115 if (pausing_streams_.empty()) | |
116 return; | |
117 | |
118 AudioOutputStream* stream = pausing_streams_.back(); | |
119 pausing_streams_.pop_back(); | |
120 idle_streams_.push_back(stream); | |
121 close_timer_.Reset(); | |
122 } | |
123 | |
124 void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) { | |
125 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
126 | |
127 while (!pausing_streams_.empty()) { | |
128 idle_streams_.push_back(pausing_streams_.back()); | |
129 pausing_streams_.pop_back(); | |
130 } | |
131 | |
132 DCHECK_GT(paused_proxies_, 0u); | |
133 paused_proxies_--; | |
134 | |
135 while (idle_streams_.size() > paused_proxies_) { | |
136 idle_streams_.back()->Close(); | |
137 idle_streams_.pop_back(); | |
138 } | |
139 } | |
140 | |
141 void AudioOutputDispatcherImpl::Shutdown() { | |
142 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
143 | |
144 // Cancel any pending tasks to close paused streams or create new ones. | |
145 weak_this_.InvalidateWeakPtrs(); | |
146 | |
147 // No AudioOutputProxy objects should hold a reference to us when we get | |
148 // to this stage. | |
149 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference"; | |
150 | |
151 AudioOutputStreamList::iterator it = idle_streams_.begin(); | |
152 for (; it != idle_streams_.end(); ++it) | |
153 (*it)->Close(); | |
154 idle_streams_.clear(); | |
155 | |
156 it = pausing_streams_.begin(); | |
157 for (; it != pausing_streams_.end(); ++it) | |
158 (*it)->Close(); | |
159 pausing_streams_.clear(); | |
vrk (LEFT CHROMIUM)
2012/04/06 23:37:05
Do you need to clear the proxy_to_physical_map_ so
enal1
2012/04/16 22:01:35
StopStream() removes physical stream from the map.
| |
160 } | |
161 | |
162 bool AudioOutputDispatcherImpl::CreateAndOpenStream() { | |
163 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
164 AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(params_); | |
165 if (!stream) | |
166 return false; | |
167 | |
168 if (!stream->Open()) { | |
169 stream->Close(); | |
170 return false; | |
171 } | |
172 idle_streams_.push_back(stream); | |
173 return true; | |
174 } | |
175 | |
176 void AudioOutputDispatcherImpl::OpenTask() { | |
177 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
178 // Make sure that we have at least one stream allocated if there | |
179 // are paused streams. | |
180 if (paused_proxies_ > 0 && idle_streams_.empty() && | |
181 pausing_streams_.empty()) { | |
182 CreateAndOpenStream(); | |
183 } | |
184 | |
185 close_timer_.Reset(); | |
186 } | |
187 | |
188 // This method is called by |close_timer_|. | |
189 void AudioOutputDispatcherImpl::ClosePendingStreams() { | |
190 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
191 while (!idle_streams_.empty()) { | |
192 idle_streams_.back()->Close(); | |
193 idle_streams_.pop_back(); | |
194 } | |
195 } | |
OLD | NEW |