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_device.h"
6
7 #include "base/basictypes.h"
8 #include "base/debug/trace_event.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/threading/thread_restrictions.h"
11 #include "base/time/time.h"
12 #include "media/audio/audio_output_controller.h"
13 #include "media/base/limits.h"
14
15 namespace media {
16
17 // Takes care of invoking the render callback on the audio thread.
18 // An instance of this class is created for each capture stream in
19 // OnStreamCreated().
20 class AudioOutputDevice::AudioThreadCallback
21 : public AudioDeviceThread::Callback {
22 public:
23 AudioThreadCallback(const AudioParameters& audio_parameters,
24 base::SharedMemoryHandle memory,
25 int memory_length,
26 AudioRendererSink::RenderCallback* render_callback);
27 virtual ~AudioThreadCallback();
28
29 virtual void MapSharedMemory() OVERRIDE;
30
31 // Called whenever we receive notifications about pending data.
32 virtual void Process(int pending_data) OVERRIDE;
33
34 private:
35 AudioRendererSink::RenderCallback* render_callback_;
36 scoped_ptr<AudioBus> input_bus_;
37 scoped_ptr<AudioBus> output_bus_;
38 DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback);
39 };
40
AudioOutputDevice(scoped_ptr<AudioOutputIPC> ipc,const scoped_refptr<base::MessageLoopProxy> & io_loop)41 AudioOutputDevice::AudioOutputDevice(
42 scoped_ptr<AudioOutputIPC> ipc,
43 const scoped_refptr<base::MessageLoopProxy>& io_loop)
44 : ScopedLoopObserver(io_loop),
45 callback_(NULL),
46 ipc_(ipc.Pass()),
47 state_(IDLE),
48 play_on_start_(true),
49 session_id_(-1),
50 stopping_hack_(false) {
51 CHECK(ipc_);
52
53 // The correctness of the code depends on the relative values assigned in the
54 // State enum.
55 COMPILE_ASSERT(IPC_CLOSED < IDLE, invalid_enum_value_assignment_0);
56 COMPILE_ASSERT(IDLE < CREATING_STREAM, invalid_enum_value_assignment_1);
57 COMPILE_ASSERT(CREATING_STREAM < PAUSED, invalid_enum_value_assignment_2);
58 COMPILE_ASSERT(PAUSED < PLAYING, invalid_enum_value_assignment_3);
59 }
60
InitializeUnifiedStream(const AudioParameters & params,RenderCallback * callback,int session_id)61 void AudioOutputDevice::InitializeUnifiedStream(const AudioParameters& params,
62 RenderCallback* callback,
63 int session_id) {
64 DCHECK(!callback_) << "Calling InitializeUnifiedStream() twice?";
65 DCHECK(params.IsValid());
66 audio_parameters_ = params;
67 callback_ = callback;
68 session_id_ = session_id;
69 }
70
Initialize(const AudioParameters & params,RenderCallback * callback)71 void AudioOutputDevice::Initialize(const AudioParameters& params,
72 RenderCallback* callback) {
73 InitializeUnifiedStream(params, callback, 0);
74 }
75
~AudioOutputDevice()76 AudioOutputDevice::~AudioOutputDevice() {
77 // The current design requires that the user calls Stop() before deleting
78 // this class.
79 DCHECK(audio_thread_.IsStopped());
80 }
81
Start()82 void AudioOutputDevice::Start() {
83 DCHECK(callback_) << "Initialize hasn't been called";
84 message_loop()->PostTask(FROM_HERE,
85 base::Bind(&AudioOutputDevice::CreateStreamOnIOThread, this,
86 audio_parameters_));
87 }
88
Stop()89 void AudioOutputDevice::Stop() {
90 {
91 base::AutoLock auto_lock(audio_thread_lock_);
92 audio_thread_.Stop(base::MessageLoop::current());
93 stopping_hack_ = true;
94 }
95
96 message_loop()->PostTask(FROM_HERE,
97 base::Bind(&AudioOutputDevice::ShutDownOnIOThread, this));
98 }
99
Play()100 void AudioOutputDevice::Play() {
101 message_loop()->PostTask(FROM_HERE,
102 base::Bind(&AudioOutputDevice::PlayOnIOThread, this));
103 }
104
Pause()105 void AudioOutputDevice::Pause() {
106 message_loop()->PostTask(FROM_HERE,
107 base::Bind(&AudioOutputDevice::PauseOnIOThread, this));
108 }
109
SetVolume(double volume)110 bool AudioOutputDevice::SetVolume(double volume) {
111 if (volume < 0 || volume > 1.0)
112 return false;
113
114 if (!message_loop()->PostTask(FROM_HERE,
115 base::Bind(&AudioOutputDevice::SetVolumeOnIOThread, this, volume))) {
116 return false;
117 }
118
119 return true;
120 }
121
CreateStreamOnIOThread(const AudioParameters & params)122 void AudioOutputDevice::CreateStreamOnIOThread(const AudioParameters& params) {
123 DCHECK(message_loop()->BelongsToCurrentThread());
124 if (state_ == IDLE) {
125 state_ = CREATING_STREAM;
126 ipc_->CreateStream(this, params, session_id_);
127 }
128 }
129
PlayOnIOThread()130 void AudioOutputDevice::PlayOnIOThread() {
131 DCHECK(message_loop()->BelongsToCurrentThread());
132 if (state_ == PAUSED) {
133 ipc_->PlayStream();
134 state_ = PLAYING;
135 play_on_start_ = false;
136 } else {
137 play_on_start_ = true;
138 }
139 }
140
PauseOnIOThread()141 void AudioOutputDevice::PauseOnIOThread() {
142 DCHECK(message_loop()->BelongsToCurrentThread());
143 if (state_ == PLAYING) {
144 ipc_->PauseStream();
145 state_ = PAUSED;
146 }
147 play_on_start_ = false;
148 }
149
ShutDownOnIOThread()150 void AudioOutputDevice::ShutDownOnIOThread() {
151 DCHECK(message_loop()->BelongsToCurrentThread());
152
153 // Close the stream, if we haven't already.
154 if (state_ >= CREATING_STREAM) {
155 ipc_->CloseStream();
156 state_ = IDLE;
157 }
158
159 // We can run into an issue where ShutDownOnIOThread is called right after
160 // OnStreamCreated is called in cases where Start/Stop are called before we
161 // get the OnStreamCreated callback. To handle that corner case, we call
162 // Stop(). In most cases, the thread will already be stopped.
163 //
164 // Another situation is when the IO thread goes away before Stop() is called
165 // in which case, we cannot use the message loop to close the thread handle
166 // and can't rely on the main thread existing either.
167 base::AutoLock auto_lock_(audio_thread_lock_);
168 base::ThreadRestrictions::ScopedAllowIO allow_io;
169 audio_thread_.Stop(NULL);
170 audio_callback_.reset();
171 stopping_hack_ = false;
172 }
173
SetVolumeOnIOThread(double volume)174 void AudioOutputDevice::SetVolumeOnIOThread(double volume) {
175 DCHECK(message_loop()->BelongsToCurrentThread());
176 if (state_ >= CREATING_STREAM)
177 ipc_->SetVolume(volume);
178 }
179
OnStateChanged(AudioOutputIPCDelegate::State state)180 void AudioOutputDevice::OnStateChanged(AudioOutputIPCDelegate::State state) {
181 DCHECK(message_loop()->BelongsToCurrentThread());
182
183 // Do nothing if the stream has been closed.
184 if (state_ < CREATING_STREAM)
185 return;
186
187 // TODO(miu): Clean-up inconsistent and incomplete handling here.
188 // http://crbug.com/180640
189 switch (state) {
190 case AudioOutputIPCDelegate::kPlaying:
191 case AudioOutputIPCDelegate::kPaused:
192 break;
193 case AudioOutputIPCDelegate::kError:
194 DLOG(WARNING) << "AudioOutputDevice::OnStateChanged(kError)";
195 // Don't dereference the callback object if the audio thread
196 // is stopped or stopping. That could mean that the callback
197 // object has been deleted.
198 // TODO(tommi): Add an explicit contract for clearing the callback
199 // object. Possibly require calling Initialize again or provide
200 // a callback object via Start() and clear it in Stop().
201 if (!audio_thread_.IsStopped())
202 callback_->OnRenderError();
203 break;
204 default:
205 NOTREACHED();
206 break;
207 }
208 }
209
OnStreamCreated(base::SharedMemoryHandle handle,base::SyncSocket::Handle socket_handle,int length)210 void AudioOutputDevice::OnStreamCreated(
211 base::SharedMemoryHandle handle,
212 base::SyncSocket::Handle socket_handle,
213 int length) {
214 DCHECK(message_loop()->BelongsToCurrentThread());
215 #if defined(OS_WIN)
216 DCHECK(handle);
217 DCHECK(socket_handle);
218 #else
219 DCHECK_GE(handle.fd, 0);
220 DCHECK_GE(socket_handle, 0);
221 #endif
222 DCHECK_GT(length, 0);
223
224 if (state_ != CREATING_STREAM)
225 return;
226
227 // We can receive OnStreamCreated() on the IO thread after the client has
228 // called Stop() but before ShutDownOnIOThread() is processed. In such a
229 // situation |callback_| might point to freed memory. Instead of starting
230 // |audio_thread_| do nothing and wait for ShutDownOnIOThread() to get called.
231 //
232 // TODO(scherkus): The real fix is to have sane ownership semantics. The fact
233 // that |callback_| (which should own and outlive this object!) can point to
234 // freed memory is a mess. AudioRendererSink should be non-refcounted so that
235 // owners (WebRtcAudioDeviceImpl, AudioRendererImpl, etc...) can Stop() and
236 // delete as they see fit. AudioOutputDevice should internally use WeakPtr
237 // to handle teardown and thread hopping. See http://crbug.com/151051 for
238 // details.
239 base::AutoLock auto_lock(audio_thread_lock_);
240 if (stopping_hack_)
241 return;
242
243 DCHECK(audio_thread_.IsStopped());
244 audio_callback_.reset(new AudioOutputDevice::AudioThreadCallback(
245 audio_parameters_, handle, length, callback_));
246 audio_thread_.Start(
247 audio_callback_.get(), socket_handle, "AudioOutputDevice", true);
248 state_ = PAUSED;
249
250 // We handle the case where Play() and/or Pause() may have been called
251 // multiple times before OnStreamCreated() gets called.
252 if (play_on_start_)
253 PlayOnIOThread();
254 }
255
OnIPCClosed()256 void AudioOutputDevice::OnIPCClosed() {
257 DCHECK(message_loop()->BelongsToCurrentThread());
258 state_ = IPC_CLOSED;
259 ipc_.reset();
260 }
261
WillDestroyCurrentMessageLoop()262 void AudioOutputDevice::WillDestroyCurrentMessageLoop() {
263 LOG(ERROR) << "IO loop going away before the audio device has been stopped";
264 ShutDownOnIOThread();
265 }
266
267 // AudioOutputDevice::AudioThreadCallback
268
AudioThreadCallback(const AudioParameters & audio_parameters,base::SharedMemoryHandle memory,int memory_length,AudioRendererSink::RenderCallback * render_callback)269 AudioOutputDevice::AudioThreadCallback::AudioThreadCallback(
270 const AudioParameters& audio_parameters,
271 base::SharedMemoryHandle memory,
272 int memory_length,
273 AudioRendererSink::RenderCallback* render_callback)
274 : AudioDeviceThread::Callback(audio_parameters, memory, memory_length, 1),
275 render_callback_(render_callback) {}
276
~AudioThreadCallback()277 AudioOutputDevice::AudioThreadCallback::~AudioThreadCallback() {
278 }
279
MapSharedMemory()280 void AudioOutputDevice::AudioThreadCallback::MapSharedMemory() {
281 CHECK_EQ(total_segments_, 1);
282 CHECK(shared_memory_.Map(memory_length_));
283
284 // Calculate output and input memory size.
285 int output_memory_size = AudioBus::CalculateMemorySize(audio_parameters_);
286 int input_channels = audio_parameters_.input_channels();
287 int frames = audio_parameters_.frames_per_buffer();
288 int input_memory_size = AudioBus::CalculateMemorySize(input_channels, frames);
289
290 int io_size = output_memory_size + input_memory_size;
291
292 DCHECK_EQ(memory_length_, io_size);
293
294 output_bus_ =
295 AudioBus::WrapMemory(audio_parameters_, shared_memory_.memory());
296
297 if (input_channels > 0) {
298 // The input data is after the output data.
299 char* input_data =
300 static_cast<char*>(shared_memory_.memory()) + output_memory_size;
301 input_bus_ = AudioBus::WrapMemory(input_channels, frames, input_data);
302 }
303 }
304
305 // Called whenever we receive notifications about pending data.
Process(int pending_data)306 void AudioOutputDevice::AudioThreadCallback::Process(int pending_data) {
307 // Negative |pending_data| indicates the browser side stream has stopped.
308 if (pending_data < 0)
309 return;
310
311 // Convert the number of pending bytes in the render buffer into milliseconds.
312 int audio_delay_milliseconds = pending_data / bytes_per_ms_;
313
314 TRACE_EVENT0("audio", "AudioOutputDevice::FireRenderCallback");
315
316 // Update the audio-delay measurement then ask client to render audio. Since
317 // |output_bus_| is wrapping the shared memory the Render() call is writing
318 // directly into the shared memory.
319 int input_channels = audio_parameters_.input_channels();
320 if (input_bus_ && input_channels > 0) {
321 render_callback_->RenderIO(
322 input_bus_.get(), output_bus_.get(), audio_delay_milliseconds);
323 } else {
324 render_callback_->Render(output_bus_.get(), audio_delay_milliseconds);
325 }
326 }
327
328 } // namespace media.
329