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.
5 #include "media/audio/audio_output_dispatcher_impl.h"
10 #include "base/compiler_specific.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/time/time.h"
13 #include "media/audio/audio_io.h"
14 #include "media/audio/audio_output_proxy.h"
15 #include "media/audio/audio_util.h"
19 AudioOutputDispatcherImpl::AudioOutputDispatcherImpl(
20 AudioManager
* audio_manager
,
21 const AudioParameters
& params
,
22 const std::string
& input_device_id
,
23 const base::TimeDelta
& close_delay
)
24 : AudioOutputDispatcher(audio_manager
, params
, input_device_id
),
25 pause_delay_(base::TimeDelta::FromMicroseconds(
26 2 * params
.frames_per_buffer() * base::Time::kMicrosecondsPerSecond
/
27 static_cast<float>(params
.sample_rate()))),
30 close_timer_(FROM_HERE
,
33 &AudioOutputDispatcherImpl::ClosePendingStreams
) {
36 AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() {
37 DCHECK(proxy_to_physical_map_
.empty());
38 DCHECK(idle_streams_
.empty());
39 DCHECK(pausing_streams_
.empty());
42 bool AudioOutputDispatcherImpl::OpenStream() {
43 DCHECK_EQ(base::MessageLoop::current(), message_loop_
);
47 // Ensure that there is at least one open stream.
48 if (idle_streams_
.empty() && !CreateAndOpenStream()) {
57 bool AudioOutputDispatcherImpl::StartStream(
58 AudioOutputStream::AudioSourceCallback
* callback
,
59 AudioOutputProxy
* stream_proxy
) {
60 DCHECK_EQ(base::MessageLoop::current(), message_loop_
);
62 if (idle_streams_
.empty() && !CreateAndOpenStream())
65 AudioOutputStream
* physical_stream
= idle_streams_
.back();
66 DCHECK(physical_stream
);
67 idle_streams_
.pop_back();
69 DCHECK_GT(paused_proxies_
, 0u);
74 // Schedule task to allocate streams for other proxies if we need to.
75 message_loop_
->PostTask(FROM_HERE
, base::Bind(
76 &AudioOutputDispatcherImpl::OpenTask
, weak_this_
.GetWeakPtr()));
79 stream_proxy
->GetVolume(&volume
);
80 physical_stream
->SetVolume(volume
);
81 physical_stream
->Start(callback
);
82 proxy_to_physical_map_
[stream_proxy
] = physical_stream
;
86 void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy
* stream_proxy
) {
87 DCHECK_EQ(base::MessageLoop::current(), message_loop_
);
89 AudioStreamMap::iterator it
= proxy_to_physical_map_
.find(stream_proxy
);
90 DCHECK(it
!= proxy_to_physical_map_
.end());
91 AudioOutputStream
* physical_stream
= it
->second
;
92 proxy_to_physical_map_
.erase(it
);
94 physical_stream
->Stop();
98 pausing_streams_
.push_front(physical_stream
);
100 // Don't recycle stream until two buffers worth of time has elapsed.
101 message_loop_
->PostDelayedTask(
103 base::Bind(&AudioOutputDispatcherImpl::StopStreamTask
,
104 weak_this_
.GetWeakPtr()),
108 void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy
* stream_proxy
,
110 DCHECK_EQ(base::MessageLoop::current(), message_loop_
);
111 AudioStreamMap::iterator it
= proxy_to_physical_map_
.find(stream_proxy
);
112 if (it
!= proxy_to_physical_map_
.end()) {
113 AudioOutputStream
* physical_stream
= it
->second
;
114 physical_stream
->SetVolume(volume
);
118 void AudioOutputDispatcherImpl::StopStreamTask() {
119 DCHECK_EQ(base::MessageLoop::current(), message_loop_
);
121 if (pausing_streams_
.empty())
124 AudioOutputStream
* stream
= pausing_streams_
.back();
125 pausing_streams_
.pop_back();
126 idle_streams_
.push_back(stream
);
127 close_timer_
.Reset();
130 void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy
* stream_proxy
) {
131 DCHECK_EQ(base::MessageLoop::current(), message_loop_
);
133 while (!pausing_streams_
.empty()) {
134 idle_streams_
.push_back(pausing_streams_
.back());
135 pausing_streams_
.pop_back();
138 DCHECK_GT(paused_proxies_
, 0u);
141 while (idle_streams_
.size() > paused_proxies_
) {
142 idle_streams_
.back()->Close();
143 idle_streams_
.pop_back();
147 void AudioOutputDispatcherImpl::Shutdown() {
148 DCHECK_EQ(base::MessageLoop::current(), message_loop_
);
150 // Cancel any pending tasks to close paused streams or create new ones.
151 weak_this_
.InvalidateWeakPtrs();
153 // No AudioOutputProxy objects should hold a reference to us when we get
155 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference";
157 AudioOutputStreamList::iterator it
= idle_streams_
.begin();
158 for (; it
!= idle_streams_
.end(); ++it
)
160 idle_streams_
.clear();
162 it
= pausing_streams_
.begin();
163 for (; it
!= pausing_streams_
.end(); ++it
)
165 pausing_streams_
.clear();
168 bool AudioOutputDispatcherImpl::CreateAndOpenStream() {
169 DCHECK_EQ(base::MessageLoop::current(), message_loop_
);
170 AudioOutputStream
* stream
= audio_manager_
->MakeAudioOutputStream(
171 params_
, input_device_id_
);
175 if (!stream
->Open()) {
179 idle_streams_
.push_back(stream
);
183 void AudioOutputDispatcherImpl::OpenTask() {
184 DCHECK_EQ(base::MessageLoop::current(), message_loop_
);
185 // Make sure that we have at least one stream allocated if there
186 // are paused streams.
187 if (paused_proxies_
> 0 && idle_streams_
.empty() &&
188 pausing_streams_
.empty()) {
189 CreateAndOpenStream();
192 close_timer_
.Reset();
195 // This method is called by |close_timer_|.
196 void AudioOutputDispatcherImpl::ClosePendingStreams() {
197 DCHECK_EQ(base::MessageLoop::current(), message_loop_
);
198 while (!idle_streams_
.empty()) {
199 idle_streams_
.back()->Close();
200 idle_streams_
.pop_back();