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 "content/renderer/media/audio_device.h" | |
6 | |
7 #include "base/debug/trace_event.h" | |
8 #include "base/message_loop.h" | |
9 #include "base/threading/thread_restrictions.h" | |
10 #include "base/time.h" | |
11 #include "media/audio/audio_output_controller.h" | |
12 #include "media/audio/audio_util.h" | |
13 | |
14 using media::AudioRendererSink; | |
15 | |
16 // Takes care of invoking the render callback on the audio thread. | |
17 // An instance of this class is created for each capture stream in | |
18 // OnStreamCreated(). | |
19 class AudioDevice::AudioThreadCallback | |
20 : public AudioDeviceThread::Callback { | |
21 public: | |
22 AudioThreadCallback(const media::AudioParameters& audio_parameters, | |
23 base::SharedMemoryHandle memory, | |
24 int memory_length, | |
25 AudioRendererSink::RenderCallback* render_callback); | |
26 virtual ~AudioThreadCallback(); | |
27 | |
28 virtual void MapSharedMemory() OVERRIDE; | |
29 | |
30 // Called whenever we receive notifications about pending data. | |
31 virtual void Process(int pending_data) OVERRIDE; | |
32 | |
33 private: | |
34 AudioRendererSink::RenderCallback* render_callback_; | |
35 DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); | |
36 }; | |
37 | |
38 AudioDevice::AudioDevice( | |
39 media::AudioOutputIPC* ipc, | |
40 const scoped_refptr<base::MessageLoopProxy>& io_loop) | |
41 : ScopedLoopObserver(io_loop), | |
42 callback_(NULL), | |
43 ipc_(ipc), | |
44 stream_id_(0), | |
45 play_on_start_(true), | |
46 is_started_(false) { | |
47 CHECK(ipc_); | |
48 } | |
49 | |
50 void AudioDevice::Initialize(const media::AudioParameters& params, | |
51 RenderCallback* callback) { | |
52 CHECK_EQ(0, stream_id_) << | |
53 "AudioDevice::Initialize() must be called before Start()"; | |
54 | |
55 CHECK(!callback_); // Calling Initialize() twice? | |
56 | |
57 audio_parameters_ = params; | |
58 callback_ = callback; | |
59 } | |
60 | |
61 AudioDevice::~AudioDevice() { | |
62 // The current design requires that the user calls Stop() before deleting | |
63 // this class. | |
64 CHECK_EQ(0, stream_id_); | |
65 } | |
66 | |
67 void AudioDevice::Start() { | |
68 DCHECK(callback_) << "Initialize hasn't been called"; | |
69 message_loop()->PostTask(FROM_HERE, | |
70 base::Bind(&AudioDevice::CreateStreamOnIOThread, this, | |
71 audio_parameters_)); | |
72 } | |
73 | |
74 void AudioDevice::Stop() { | |
75 { | |
76 base::AutoLock auto_lock(audio_thread_lock_); | |
77 audio_thread_.Stop(MessageLoop::current()); | |
78 } | |
79 | |
80 message_loop()->PostTask(FROM_HERE, | |
81 base::Bind(&AudioDevice::ShutDownOnIOThread, this)); | |
82 } | |
83 | |
84 void AudioDevice::Play() { | |
85 message_loop()->PostTask(FROM_HERE, | |
86 base::Bind(&AudioDevice::PlayOnIOThread, this)); | |
87 } | |
88 | |
89 void AudioDevice::Pause(bool flush) { | |
90 message_loop()->PostTask(FROM_HERE, | |
91 base::Bind(&AudioDevice::PauseOnIOThread, this, flush)); | |
92 } | |
93 | |
94 bool AudioDevice::SetVolume(double volume) { | |
95 if (volume < 0 || volume > 1.0) | |
96 return false; | |
97 | |
98 if (!message_loop()->PostTask(FROM_HERE, | |
99 base::Bind(&AudioDevice::SetVolumeOnIOThread, this, volume))) { | |
100 return false; | |
101 } | |
102 | |
103 return true; | |
104 } | |
105 | |
106 void AudioDevice::CreateStreamOnIOThread(const media::AudioParameters& params) { | |
107 DCHECK(message_loop()->BelongsToCurrentThread()); | |
108 // Make sure we don't create the stream more than once. | |
109 DCHECK_EQ(0, stream_id_); | |
110 if (stream_id_) | |
111 return; | |
112 | |
113 stream_id_ = ipc_->AddDelegate(this); | |
114 ipc_->CreateStream(stream_id_, params); | |
115 } | |
116 | |
117 void AudioDevice::PlayOnIOThread() { | |
118 DCHECK(message_loop()->BelongsToCurrentThread()); | |
119 if (stream_id_ && is_started_) | |
120 ipc_->PlayStream(stream_id_); | |
121 else | |
122 play_on_start_ = true; | |
123 } | |
124 | |
125 void AudioDevice::PauseOnIOThread(bool flush) { | |
126 DCHECK(message_loop()->BelongsToCurrentThread()); | |
127 if (stream_id_ && is_started_) { | |
128 ipc_->PauseStream(stream_id_); | |
129 if (flush) | |
130 ipc_->FlushStream(stream_id_); | |
131 } else { | |
132 // Note that |flush| isn't relevant here since this is the case where | |
133 // the stream is first starting. | |
134 play_on_start_ = false; | |
135 } | |
136 } | |
137 | |
138 void AudioDevice::ShutDownOnIOThread() { | |
139 DCHECK(message_loop()->BelongsToCurrentThread()); | |
140 | |
141 // Make sure we don't call shutdown more than once. | |
142 if (stream_id_) { | |
143 is_started_ = false; | |
144 | |
145 ipc_->CloseStream(stream_id_); | |
146 ipc_->RemoveDelegate(stream_id_); | |
147 stream_id_ = 0; | |
148 } | |
149 | |
150 // We can run into an issue where ShutDownOnIOThread is called right after | |
151 // OnStreamCreated is called in cases where Start/Stop are called before we | |
152 // get the OnStreamCreated callback. To handle that corner case, we call | |
153 // Stop(). In most cases, the thread will already be stopped. | |
154 // Another situation is when the IO thread goes away before Stop() is called | |
155 // in which case, we cannot use the message loop to close the thread handle | |
156 // and can't not rely on the main thread existing either. | |
157 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
158 audio_thread_.Stop(NULL); | |
159 audio_callback_.reset(); | |
160 } | |
161 | |
162 void AudioDevice::SetVolumeOnIOThread(double volume) { | |
163 DCHECK(message_loop()->BelongsToCurrentThread()); | |
164 if (stream_id_) | |
165 ipc_->SetVolume(stream_id_, volume); | |
166 } | |
167 | |
168 void AudioDevice::OnStateChanged(media::AudioOutputIPCDelegate::State state) { | |
169 DCHECK(message_loop()->BelongsToCurrentThread()); | |
170 | |
171 // Do nothing if the stream has been closed. | |
172 if (!stream_id_) | |
173 return; | |
174 | |
175 if (state == media::AudioOutputIPCDelegate::kError) { | |
176 DLOG(WARNING) << "AudioDevice::OnStateChanged(kError)"; | |
177 // Don't dereference the callback object if the audio thread | |
178 // is stopped or stopping. That could mean that the callback | |
179 // object has been deleted. | |
180 // TODO(tommi): Add an explicit contract for clearing the callback | |
181 // object. Possibly require calling Initialize again or provide | |
182 // a callback object via Start() and clear it in Stop(). | |
183 if (!audio_thread_.IsStopped()) | |
184 callback_->OnRenderError(); | |
185 } | |
186 } | |
187 | |
188 void AudioDevice::OnStreamCreated( | |
189 base::SharedMemoryHandle handle, | |
190 base::SyncSocket::Handle socket_handle, | |
191 int length) { | |
192 DCHECK(message_loop()->BelongsToCurrentThread()); | |
193 DCHECK_GE(length, audio_parameters_.GetBytesPerBuffer()); | |
194 #if defined(OS_WIN) | |
195 DCHECK(handle); | |
196 DCHECK(socket_handle); | |
197 #else | |
198 DCHECK_GE(handle.fd, 0); | |
199 DCHECK_GE(socket_handle, 0); | |
200 #endif | |
201 | |
202 // We should only get this callback if stream_id_ is valid. If it is not, | |
203 // the IPC layer should have closed the shared memory and socket handles | |
204 // for us and not invoked the callback. The basic assertion is that when | |
205 // stream_id_ is 0 the AudioDevice instance is not registered as a delegate | |
206 // and hence it should not receive callbacks. | |
207 DCHECK(stream_id_); | |
208 | |
209 base::AutoLock auto_lock(audio_thread_lock_); | |
210 | |
211 DCHECK(audio_thread_.IsStopped()); | |
212 audio_callback_.reset(new AudioDevice::AudioThreadCallback(audio_parameters_, | |
213 handle, length, callback_)); | |
214 audio_thread_.Start(audio_callback_.get(), socket_handle, "AudioDevice"); | |
215 | |
216 // We handle the case where Play() and/or Pause() may have been called | |
217 // multiple times before OnStreamCreated() gets called. | |
218 is_started_ = true; | |
219 if (play_on_start_) | |
220 PlayOnIOThread(); | |
221 } | |
222 | |
223 void AudioDevice::OnIPCClosed() { | |
224 ipc_ = NULL; | |
225 } | |
226 | |
227 void AudioDevice::WillDestroyCurrentMessageLoop() { | |
228 LOG(ERROR) << "IO loop going away before the audio device has been stopped"; | |
229 ShutDownOnIOThread(); | |
230 } | |
231 | |
232 // AudioDevice::AudioThreadCallback | |
233 | |
234 AudioDevice::AudioThreadCallback::AudioThreadCallback( | |
235 const media::AudioParameters& audio_parameters, | |
236 base::SharedMemoryHandle memory, | |
237 int memory_length, | |
238 media::AudioRendererSink::RenderCallback* render_callback) | |
239 : AudioDeviceThread::Callback(audio_parameters, memory, memory_length), | |
240 render_callback_(render_callback) { | |
241 } | |
242 | |
243 AudioDevice::AudioThreadCallback::~AudioThreadCallback() { | |
244 } | |
245 | |
246 void AudioDevice::AudioThreadCallback::MapSharedMemory() { | |
247 shared_memory_.Map(media::TotalSharedMemorySizeInBytes(memory_length_)); | |
248 } | |
249 | |
250 // Called whenever we receive notifications about pending data. | |
251 void AudioDevice::AudioThreadCallback::Process(int pending_data) { | |
252 if (pending_data == media::AudioOutputController::kPauseMark) { | |
253 memset(shared_memory_.memory(), 0, memory_length_); | |
254 media::SetActualDataSizeInBytes(&shared_memory_, memory_length_, 0); | |
255 return; | |
256 } | |
257 | |
258 // Convert the number of pending bytes in the render buffer | |
259 // into milliseconds. | |
260 int audio_delay_milliseconds = pending_data / bytes_per_ms_; | |
261 | |
262 TRACE_EVENT0("audio", "AudioDevice::FireRenderCallback"); | |
263 | |
264 // Update the audio-delay measurement then ask client to render audio. | |
265 size_t num_frames = render_callback_->Render(audio_data_, | |
266 audio_parameters_.frames_per_buffer(), audio_delay_milliseconds); | |
267 | |
268 // Interleave, scale, and clip to int. | |
269 // TODO(crogers/vrk): Figure out a way to avoid the float -> int -> float | |
270 // conversions that happen in the <audio> and WebRTC scenarios. | |
271 media::InterleaveFloatToInt(audio_data_, shared_memory_.memory(), | |
272 num_frames, audio_parameters_.bits_per_sample() / 8); | |
273 | |
274 // Let the host know we are done. | |
275 media::SetActualDataSizeInBytes(&shared_memory_, memory_length_, | |
276 num_frames * audio_parameters_.GetBytesPerFrame()); | |
277 } | |
OLD | NEW |