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