OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/audio/audio_output_dispatcher_impl.h" | 5 #include "media/audio/audio_output_dispatcher_impl.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/compiler_specific.h" | 10 #include "base/compiler_specific.h" |
11 #include "base/message_loop/message_loop.h" | 11 #include "base/message_loop/message_loop.h" |
12 #include "base/time/time.h" | 12 #include "base/time/time.h" |
13 #include "media/audio/audio_io.h" | 13 #include "media/audio/audio_io.h" |
14 #include "media/audio/audio_output_proxy.h" | 14 #include "media/audio/audio_output_proxy.h" |
15 | 15 |
16 namespace media { | 16 namespace media { |
17 | 17 |
18 AudioOutputDispatcherImpl::AudioOutputDispatcherImpl( | 18 AudioOutputDispatcherImpl::AudioOutputDispatcherImpl( |
19 AudioManager* audio_manager, | 19 AudioManager* audio_manager, |
20 const AudioParameters& params, | 20 const AudioParameters& params, |
21 const std::string& output_device_id, | 21 const std::string& output_device_id, |
22 const std::string& input_device_id, | 22 const std::string& input_device_id, |
23 const base::TimeDelta& close_delay) | 23 const base::TimeDelta& close_delay) |
24 : AudioOutputDispatcher(audio_manager, | 24 : AudioOutputDispatcher(audio_manager, params, output_device_id, |
25 params, | 25 input_device_id), |
26 output_device_id, | 26 pause_delay_(base::TimeDelta::FromMicroseconds( |
27 input_device_id), | 27 2 * params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond / |
28 idle_proxies_(0), | 28 static_cast<float>(params.sample_rate()))), |
| 29 paused_proxies_(0), |
| 30 weak_this_(this), |
29 close_timer_(FROM_HERE, | 31 close_timer_(FROM_HERE, |
30 close_delay, | 32 close_delay, |
31 this, | 33 this, |
32 &AudioOutputDispatcherImpl::CloseIdleStreams) {} | 34 &AudioOutputDispatcherImpl::ClosePendingStreams) { |
| 35 } |
33 | 36 |
34 AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() { | 37 AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() { |
35 DCHECK_EQ(idle_proxies_, 0u); | |
36 DCHECK(proxy_to_physical_map_.empty()); | 38 DCHECK(proxy_to_physical_map_.empty()); |
37 DCHECK(idle_streams_.empty()); | 39 DCHECK(idle_streams_.empty()); |
| 40 DCHECK(pausing_streams_.empty()); |
38 } | 41 } |
39 | 42 |
40 bool AudioOutputDispatcherImpl::OpenStream() { | 43 bool AudioOutputDispatcherImpl::OpenStream() { |
41 DCHECK(message_loop_->BelongsToCurrentThread()); | 44 DCHECK(message_loop_->BelongsToCurrentThread()); |
42 | 45 |
| 46 paused_proxies_++; |
| 47 |
43 // Ensure that there is at least one open stream. | 48 // Ensure that there is at least one open stream. |
44 if (idle_streams_.empty() && !CreateAndOpenStream()) | 49 if (idle_streams_.empty() && !CreateAndOpenStream()) { |
| 50 paused_proxies_--; |
45 return false; | 51 return false; |
| 52 } |
46 | 53 |
47 ++idle_proxies_; | |
48 close_timer_.Reset(); | 54 close_timer_.Reset(); |
49 return true; | 55 return true; |
50 } | 56 } |
51 | 57 |
52 bool AudioOutputDispatcherImpl::StartStream( | 58 bool AudioOutputDispatcherImpl::StartStream( |
53 AudioOutputStream::AudioSourceCallback* callback, | 59 AudioOutputStream::AudioSourceCallback* callback, |
54 AudioOutputProxy* stream_proxy) { | 60 AudioOutputProxy* stream_proxy) { |
55 DCHECK(message_loop_->BelongsToCurrentThread()); | 61 DCHECK(message_loop_->BelongsToCurrentThread()); |
56 | 62 |
57 if (idle_streams_.empty() && !CreateAndOpenStream()) | 63 if (idle_streams_.empty() && !CreateAndOpenStream()) |
58 return false; | 64 return false; |
59 | 65 |
60 AudioOutputStream* physical_stream = idle_streams_.back(); | 66 AudioOutputStream* physical_stream = idle_streams_.back(); |
| 67 DCHECK(physical_stream); |
61 idle_streams_.pop_back(); | 68 idle_streams_.pop_back(); |
62 | 69 |
63 DCHECK_GT(idle_proxies_, 0u); | 70 DCHECK_GT(paused_proxies_, 0u); |
64 --idle_proxies_; | 71 --paused_proxies_; |
| 72 |
| 73 close_timer_.Reset(); |
| 74 |
| 75 // Schedule task to allocate streams for other proxies if we need to. |
| 76 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 77 &AudioOutputDispatcherImpl::OpenTask, weak_this_.GetWeakPtr())); |
65 | 78 |
66 double volume = 0; | 79 double volume = 0; |
67 stream_proxy->GetVolume(&volume); | 80 stream_proxy->GetVolume(&volume); |
68 physical_stream->SetVolume(volume); | 81 physical_stream->SetVolume(volume); |
69 physical_stream->Start(callback); | 82 physical_stream->Start(callback); |
70 proxy_to_physical_map_[stream_proxy] = physical_stream; | 83 proxy_to_physical_map_[stream_proxy] = physical_stream; |
71 | |
72 close_timer_.Reset(); | |
73 return true; | 84 return true; |
74 } | 85 } |
75 | 86 |
76 void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) { | 87 void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) { |
77 DCHECK(message_loop_->BelongsToCurrentThread()); | 88 DCHECK(message_loop_->BelongsToCurrentThread()); |
78 | 89 |
79 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); | 90 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); |
80 DCHECK(it != proxy_to_physical_map_.end()); | 91 DCHECK(it != proxy_to_physical_map_.end()); |
81 AudioOutputStream* physical_stream = it->second; | 92 AudioOutputStream* physical_stream = it->second; |
82 proxy_to_physical_map_.erase(it); | 93 proxy_to_physical_map_.erase(it); |
83 | 94 |
84 physical_stream->Stop(); | 95 physical_stream->Stop(); |
85 ++idle_proxies_; | |
86 idle_streams_.push_back(physical_stream); | |
87 | 96 |
88 close_timer_.Reset(); | 97 ++paused_proxies_; |
| 98 |
| 99 pausing_streams_.push_front(physical_stream); |
| 100 |
| 101 // Don't recycle stream until two buffers worth of time has elapsed. |
| 102 message_loop_->PostDelayedTask( |
| 103 FROM_HERE, |
| 104 base::Bind(&AudioOutputDispatcherImpl::StopStreamTask, |
| 105 weak_this_.GetWeakPtr()), |
| 106 pause_delay_); |
89 } | 107 } |
90 | 108 |
91 void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy, | 109 void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy, |
92 double volume) { | 110 double volume) { |
93 DCHECK(message_loop_->BelongsToCurrentThread()); | 111 DCHECK(message_loop_->BelongsToCurrentThread()); |
94 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); | 112 AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy); |
95 if (it != proxy_to_physical_map_.end()) { | 113 if (it != proxy_to_physical_map_.end()) { |
96 AudioOutputStream* physical_stream = it->second; | 114 AudioOutputStream* physical_stream = it->second; |
97 physical_stream->SetVolume(volume); | 115 physical_stream->SetVolume(volume); |
98 } | 116 } |
99 } | 117 } |
100 | 118 |
| 119 void AudioOutputDispatcherImpl::StopStreamTask() { |
| 120 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 121 |
| 122 if (pausing_streams_.empty()) |
| 123 return; |
| 124 |
| 125 AudioOutputStream* stream = pausing_streams_.back(); |
| 126 pausing_streams_.pop_back(); |
| 127 idle_streams_.push_back(stream); |
| 128 close_timer_.Reset(); |
| 129 } |
| 130 |
101 void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) { | 131 void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) { |
102 DCHECK(message_loop_->BelongsToCurrentThread()); | 132 DCHECK(message_loop_->BelongsToCurrentThread()); |
103 | 133 |
104 DCHECK_GT(idle_proxies_, 0u); | 134 while (!pausing_streams_.empty()) { |
105 --idle_proxies_; | 135 idle_streams_.push_back(pausing_streams_.back()); |
| 136 pausing_streams_.pop_back(); |
| 137 } |
106 | 138 |
107 // Leave a single stream running until the close timer fires to help cycle | 139 DCHECK_GT(paused_proxies_, 0u); |
108 // time when streams are opened and closed repeatedly. | 140 paused_proxies_--; |
109 static const size_t kStreamKeepAliveCount = 1; | 141 |
110 if (idle_streams_.size() > kStreamKeepAliveCount) { | 142 while (idle_streams_.size() > paused_proxies_) { |
111 for (size_t i = kStreamKeepAliveCount; i < idle_streams_.size(); ++i) | 143 idle_streams_.back()->Close(); |
112 idle_streams_[i]->Close(); | 144 idle_streams_.pop_back(); |
113 idle_streams_.erase(idle_streams_.begin() + kStreamKeepAliveCount, | |
114 idle_streams_.end()); | |
115 } | 145 } |
116 close_timer_.Reset(); | |
117 } | 146 } |
118 | 147 |
119 void AudioOutputDispatcherImpl::Shutdown() { | 148 void AudioOutputDispatcherImpl::Shutdown() { |
120 DCHECK(message_loop_->BelongsToCurrentThread()); | 149 DCHECK(message_loop_->BelongsToCurrentThread()); |
121 | 150 |
122 // Close all idle streams immediately. The |close_timer_| will handle | 151 // Cancel any pending tasks to close paused streams or create new ones. |
123 // invalidating any outstanding tasks upon its destruction. | 152 weak_this_.InvalidateWeakPtrs(); |
124 CloseIdleStreams(); | 153 |
| 154 // No AudioOutputProxy objects should hold a reference to us when we get |
| 155 // to this stage. |
| 156 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference"; |
| 157 |
| 158 AudioOutputStreamList::iterator it = idle_streams_.begin(); |
| 159 for (; it != idle_streams_.end(); ++it) |
| 160 (*it)->Close(); |
| 161 idle_streams_.clear(); |
| 162 |
| 163 it = pausing_streams_.begin(); |
| 164 for (; it != pausing_streams_.end(); ++it) |
| 165 (*it)->Close(); |
| 166 pausing_streams_.clear(); |
125 } | 167 } |
126 | 168 |
127 bool AudioOutputDispatcherImpl::CreateAndOpenStream() { | 169 bool AudioOutputDispatcherImpl::CreateAndOpenStream() { |
128 DCHECK(message_loop_->BelongsToCurrentThread()); | 170 DCHECK(message_loop_->BelongsToCurrentThread()); |
129 AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream( | 171 AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream( |
130 params_, output_device_id_, input_device_id_); | 172 params_, output_device_id_, input_device_id_); |
131 if (!stream) | 173 if (!stream) |
132 return false; | 174 return false; |
133 | 175 |
134 if (!stream->Open()) { | 176 if (!stream->Open()) { |
135 stream->Close(); | 177 stream->Close(); |
136 return false; | 178 return false; |
137 } | 179 } |
138 | |
139 idle_streams_.push_back(stream); | 180 idle_streams_.push_back(stream); |
140 return true; | 181 return true; |
141 } | 182 } |
142 | 183 |
143 void AudioOutputDispatcherImpl::CloseIdleStreams() { | 184 void AudioOutputDispatcherImpl::OpenTask() { |
144 DCHECK(message_loop_->BelongsToCurrentThread()); | 185 DCHECK(message_loop_->BelongsToCurrentThread()); |
145 for (size_t i = 0; i < idle_streams_.size(); ++i) | 186 // Make sure that we have at least one stream allocated if there |
146 idle_streams_[i]->Close(); | 187 // are paused streams. |
147 idle_streams_.clear(); | 188 if (paused_proxies_ > 0 && idle_streams_.empty() && |
| 189 pausing_streams_.empty()) { |
| 190 CreateAndOpenStream(); |
| 191 } |
| 192 |
| 193 close_timer_.Reset(); |
| 194 } |
| 195 |
| 196 // This method is called by |close_timer_|. |
| 197 void AudioOutputDispatcherImpl::ClosePendingStreams() { |
| 198 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 199 while (!idle_streams_.empty()) { |
| 200 idle_streams_.back()->Close(); |
| 201 idle_streams_.pop_back(); |
| 202 } |
148 } | 203 } |
149 | 204 |
150 } // namespace media | 205 } // namespace media |
OLD | NEW |