Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(333)

Side by Side Diff: content/renderer/media/audio_device.cc

Issue 10834033: Move AudioDevice and AudioInputDevice to media. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed comments and fixed a few lint issues Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « content/renderer/media/audio_device.h ('k') | content/renderer/media/audio_device_factory.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 }
OLDNEW
« no previous file with comments | « content/renderer/media/audio_device.h ('k') | content/renderer/media/audio_device_factory.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698