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.h" | 5 #include "media/audio/audio_output_dispatcher.h" |
6 | 6 |
7 #include "base/bind.h" | |
8 #include "base/compiler_specific.h" | |
9 #include "base/message_loop.h" | 7 #include "base/message_loop.h" |
10 #include "base/time.h" | |
11 #include "media/audio/audio_io.h" | |
12 | 8 |
13 namespace media { | 9 namespace media { |
14 | 10 |
15 AudioOutputDispatcher::AudioOutputDispatcher( | 11 AudioOutputDispatcher::AudioOutputDispatcher( |
16 AudioManager* audio_manager, const AudioParameters& params, | 12 AudioManager* audio_manager, |
17 base::TimeDelta close_delay) | 13 const AudioParameters& params) |
18 : audio_manager_(audio_manager), | 14 : audio_manager_(audio_manager), |
19 message_loop_(MessageLoop::current()), | 15 message_loop_(MessageLoop::current()), |
20 params_(params), | 16 params_(params) { |
21 pause_delay_(base::TimeDelta::FromMilliseconds( | |
22 2 * params.frames_per_buffer() * | |
23 base::Time::kMillisecondsPerSecond / params.sample_rate())), | |
24 paused_proxies_(0), | |
25 ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)), | |
26 close_timer_(FROM_HERE, | |
27 close_delay, | |
28 weak_this_.GetWeakPtr(), | |
29 &AudioOutputDispatcher::ClosePendingStreams) { | |
30 // We expect to be instantiated on the audio thread. Otherwise the | 17 // We expect to be instantiated on the audio thread. Otherwise the |
31 // message_loop_ member will point to the wrong message loop! | 18 // message_loop_ member will point to the wrong message loop! |
32 DCHECK(audio_manager->GetMessageLoop()->BelongsToCurrentThread()); | 19 DCHECK(audio_manager->GetMessageLoop()->BelongsToCurrentThread()); |
33 } | 20 } |
34 | 21 |
35 AudioOutputDispatcher::~AudioOutputDispatcher() { | 22 AudioOutputDispatcher::~AudioOutputDispatcher() { |
36 DCHECK_EQ(MessageLoop::current(), message_loop_); | 23 DCHECK_EQ(MessageLoop::current(), message_loop_); |
37 } | 24 } |
38 | 25 |
39 bool AudioOutputDispatcher::StreamOpened() { | |
40 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
41 paused_proxies_++; | |
42 | |
43 // Ensure that there is at least one open stream. | |
44 if (idle_streams_.empty() && !CreateAndOpenStream()) { | |
45 return false; | |
46 } | |
47 | |
48 close_timer_.Reset(); | |
49 | |
50 return true; | |
51 } | |
52 | |
53 AudioOutputStream* AudioOutputDispatcher::StreamStarted() { | |
54 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
55 | |
56 if (idle_streams_.empty() && !CreateAndOpenStream()) { | |
57 return NULL; | |
58 } | |
59 | |
60 AudioOutputStream* stream = idle_streams_.back(); | |
61 idle_streams_.pop_back(); | |
62 | |
63 DCHECK_GT(paused_proxies_, 0u); | |
64 paused_proxies_--; | |
65 | |
66 close_timer_.Reset(); | |
67 | |
68 // Schedule task to allocate streams for other proxies if we need to. | |
69 message_loop_->PostTask(FROM_HERE, base::Bind( | |
70 &AudioOutputDispatcher::OpenTask, weak_this_.GetWeakPtr())); | |
71 | |
72 return stream; | |
73 } | |
74 | |
75 void AudioOutputDispatcher::StreamStopped(AudioOutputStream* stream) { | |
76 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
77 | |
78 paused_proxies_++; | |
79 | |
80 pausing_streams_.push_front(stream); | |
81 | |
82 // Don't recycle stream until two buffers worth of time has elapsed. | |
83 message_loop_->PostDelayedTask( | |
84 FROM_HERE, | |
85 base::Bind(&AudioOutputDispatcher::StopStreamTask, | |
86 weak_this_.GetWeakPtr()), | |
87 pause_delay_); | |
88 } | |
89 | |
90 void AudioOutputDispatcher::StopStreamTask() { | |
91 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
92 | |
93 if (pausing_streams_.empty()) | |
94 return; | |
95 | |
96 AudioOutputStream* stream = pausing_streams_.back(); | |
97 pausing_streams_.pop_back(); | |
98 idle_streams_.push_back(stream); | |
99 close_timer_.Reset(); | |
100 } | |
101 | |
102 void AudioOutputDispatcher::StreamClosed() { | |
103 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
104 | |
105 while (!pausing_streams_.empty()) { | |
106 idle_streams_.push_back(pausing_streams_.back()); | |
107 pausing_streams_.pop_back(); | |
108 } | |
109 | |
110 DCHECK_GT(paused_proxies_, 0u); | |
111 paused_proxies_--; | |
112 | |
113 while (idle_streams_.size() > paused_proxies_) { | |
114 idle_streams_.back()->Close(); | |
115 idle_streams_.pop_back(); | |
116 } | |
117 } | |
118 | |
119 void AudioOutputDispatcher::Shutdown() { | |
120 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
121 | |
122 // Cancel any pending tasks to close paused streams or create new ones. | |
123 weak_this_.InvalidateWeakPtrs(); | |
124 | |
125 // No AudioOutputProxy objects should hold a reference to us when we get | |
126 // to this stage. | |
127 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference"; | |
128 | |
129 AudioOutputStreamList::iterator it = idle_streams_.begin(); | |
130 for (; it != idle_streams_.end(); ++it) | |
131 (*it)->Close(); | |
132 idle_streams_.clear(); | |
133 | |
134 it = pausing_streams_.begin(); | |
135 for (; it != pausing_streams_.end(); ++it) | |
136 (*it)->Close(); | |
137 pausing_streams_.clear(); | |
138 } | |
139 | |
140 bool AudioOutputDispatcher::CreateAndOpenStream() { | |
141 AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(params_); | |
142 if (!stream) | |
143 return false; | |
144 | |
145 if (!stream->Open()) { | |
146 stream->Close(); | |
147 return false; | |
148 } | |
149 idle_streams_.push_back(stream); | |
150 return true; | |
151 } | |
152 | |
153 void AudioOutputDispatcher::OpenTask() { | |
154 // Make sure that we have at least one stream allocated if there | |
155 // are paused streams. | |
156 if (paused_proxies_ > 0 && idle_streams_.empty() && | |
157 pausing_streams_.empty()) { | |
158 CreateAndOpenStream(); | |
159 } | |
160 | |
161 close_timer_.Reset(); | |
162 } | |
163 | |
164 // This method is called by |close_timer_|. | |
165 void AudioOutputDispatcher::ClosePendingStreams() { | |
166 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
167 | |
168 while (!idle_streams_.empty()) { | |
169 idle_streams_.back()->Close(); | |
170 idle_streams_.pop_back(); | |
171 } | |
172 } | |
173 | |
174 } // namespace media | 26 } // namespace media |
OLD | NEW |