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

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

Issue 8440002: Low-latency AudioOutputStream implementation based on WASAPI for Windows. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: rebased Created 9 years, 1 month 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 | « no previous file | media/audio/audio_util.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 "content/renderer/media/webrtc_audio_device_impl.h" 5 #include "content/renderer/media/webrtc_audio_device_impl.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/string_util.h" 8 #include "base/string_util.h"
9 #include "base/win/windows_version.h"
9 #include "content/common/view_messages.h" 10 #include "content/common/view_messages.h"
10 #include "content/renderer/render_thread_impl.h" 11 #include "content/renderer/render_thread_impl.h"
11 #include "media/audio/audio_util.h" 12 #include "media/audio/audio_util.h"
12 13
13 static const int64 kMillisecondsBetweenProcessCalls = 5000; 14 static const int64 kMillisecondsBetweenProcessCalls = 5000;
14 static const char kVersion[] = "WebRTC AudioDevice 1.0.0.Chrome"; 15 static const char kVersion[] = "WebRTC AudioDevice 1.0.0.Chrome";
15 16
16 static int GetAudioInputHardwareSampleRate() { 17 static int GetAudioInputHardwareSampleRate() {
17 static double input_sample_rate = 0; 18 static double input_sample_rate = 0;
18 if (!input_sample_rate) { 19 if (!input_sample_rate) {
(...skipping 14 matching lines...) Expand all
33 input_sample_rate_(0), 34 input_sample_rate_(0),
34 output_sample_rate_(0), 35 output_sample_rate_(0),
35 input_delay_ms_(0), 36 input_delay_ms_(0),
36 output_delay_ms_(0), 37 output_delay_ms_(0),
37 last_error_(AudioDeviceModule::kAdmErrNone), 38 last_error_(AudioDeviceModule::kAdmErrNone),
38 last_process_time_(base::TimeTicks::Now()), 39 last_process_time_(base::TimeTicks::Now()),
39 session_id_(0), 40 session_id_(0),
40 initialized_(false), 41 initialized_(false),
41 playing_(false), 42 playing_(false),
42 recording_(false) { 43 recording_(false) {
43 VLOG(1) << "WebRtcAudioDeviceImpl::WebRtcAudioDeviceImpl()"; 44 DVLOG(1) << "WebRtcAudioDeviceImpl::WebRtcAudioDeviceImpl()";
44 DCHECK(RenderThreadImpl::current()) << 45 DCHECK(RenderThreadImpl::current()) <<
45 "WebRtcAudioDeviceImpl must be constructed on the render thread"; 46 "WebRtcAudioDeviceImpl must be constructed on the render thread";
46 } 47 }
47 48
48 WebRtcAudioDeviceImpl::~WebRtcAudioDeviceImpl() { 49 WebRtcAudioDeviceImpl::~WebRtcAudioDeviceImpl() {
49 VLOG(1) << "WebRtcAudioDeviceImpl::~WebRtcAudioDeviceImpl()"; 50 DVLOG(1) << "WebRtcAudioDeviceImpl::~WebRtcAudioDeviceImpl()";
50 if (playing_) 51 if (playing_)
51 StopPlayout(); 52 StopPlayout();
52 if (recording_) 53 if (recording_)
53 StopRecording(); 54 StopRecording();
54 if (initialized_) 55 if (initialized_)
55 Terminate(); 56 Terminate();
56 } 57 }
57 58
58 int32_t WebRtcAudioDeviceImpl::AddRef() { 59 int32_t WebRtcAudioDeviceImpl::AddRef() {
59 return base::subtle::Barrier_AtomicIncrement(&ref_count_, 1); 60 return base::subtle::Barrier_AtomicIncrement(&ref_count_, 1);
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
167 input_delay_ms_ + output_delay_ms_, 168 input_delay_ms_ + output_delay_ms_,
168 0, // clock_drift 169 0, // clock_drift
169 0, // current_mic_level 170 0, // current_mic_level
170 new_mic_level); // not used 171 new_mic_level); // not used
171 accumulated_audio_samples += samples_per_10_msec; 172 accumulated_audio_samples += samples_per_10_msec;
172 audio_byte_buffer += bytes_per_10_msec; 173 audio_byte_buffer += bytes_per_10_msec;
173 } 174 }
174 } 175 }
175 176
176 void WebRtcAudioDeviceImpl::OnDeviceStarted(int device_index) { 177 void WebRtcAudioDeviceImpl::OnDeviceStarted(int device_index) {
177 VLOG(1) << "OnDeviceStarted (device_index=" << device_index << ")"; 178 DVLOG(1) << "OnDeviceStarted (device_index=" << device_index << ")";
178 // -1 is an invalid device index. Do nothing if a valid device has 179 // -1 is an invalid device index. Do nothing if a valid device has
179 // been started. Otherwise update the |recording_| state to false. 180 // been started. Otherwise update the |recording_| state to false.
180 if (device_index != -1) 181 if (device_index != -1)
181 return; 182 return;
182 183
183 base::AutoLock auto_lock(lock_); 184 base::AutoLock auto_lock(lock_);
184 if (recording_) 185 if (recording_)
185 recording_ = false; 186 recording_ = false;
186 } 187 }
187 188
188 void WebRtcAudioDeviceImpl::OnDeviceStopped() { 189 void WebRtcAudioDeviceImpl::OnDeviceStopped() {
189 VLOG(1) << "OnDeviceStopped"; 190 DVLOG(1) << "OnDeviceStopped";
190 base::AutoLock auto_lock(lock_); 191 base::AutoLock auto_lock(lock_);
191 if (recording_) 192 if (recording_)
192 recording_ = false; 193 recording_ = false;
193 } 194 }
194 195
195 int32_t WebRtcAudioDeviceImpl::Version(char* version, 196 int32_t WebRtcAudioDeviceImpl::Version(char* version,
196 uint32_t& remaining_buffer_in_bytes, 197 uint32_t& remaining_buffer_in_bytes,
197 uint32_t& position) const { 198 uint32_t& position) const {
198 VLOG(1) << "Version()"; 199 DVLOG(1) << "Version()";
199 DCHECK(version); 200 DCHECK(version);
200 if (version == NULL) 201 if (version == NULL)
201 return -1; 202 return -1;
202 size_t arr_size = arraysize(kVersion); 203 size_t arr_size = arraysize(kVersion);
203 if (remaining_buffer_in_bytes < arr_size) { 204 if (remaining_buffer_in_bytes < arr_size) {
204 DLOG(WARNING) << "version string requires " << arr_size << " bytes"; 205 DLOG(WARNING) << "version string requires " << arr_size << " bytes";
205 return -1; 206 return -1;
206 } 207 }
207 base::strlcpy(&version[position], kVersion, arr_size - 1); 208 base::strlcpy(&version[position], kVersion, arr_size - 1);
208 remaining_buffer_in_bytes -= arr_size; 209 remaining_buffer_in_bytes -= arr_size;
209 position += arr_size; 210 position += arr_size;
210 VLOG(1) << "version: " << version; 211 DVLOG(1) << "version: " << version;
211 return 0; 212 return 0;
212 } 213 }
213 214
214 int32_t WebRtcAudioDeviceImpl::ChangeUniqueId(const int32_t id) { 215 int32_t WebRtcAudioDeviceImpl::ChangeUniqueId(const int32_t id) {
215 NOTIMPLEMENTED(); 216 NOTIMPLEMENTED();
216 return -1; 217 return -1;
217 } 218 }
218 219
219 int32_t WebRtcAudioDeviceImpl::TimeUntilNextProcess() { 220 int32_t WebRtcAudioDeviceImpl::TimeUntilNextProcess() {
220 // Calculate the number of milliseconds until this module wants its 221 // Calculate the number of milliseconds until this module wants its
(...skipping 17 matching lines...) Expand all
238 NOTIMPLEMENTED(); 239 NOTIMPLEMENTED();
239 return -1; 240 return -1;
240 } 241 }
241 242
242 webrtc::AudioDeviceModule::ErrorCode WebRtcAudioDeviceImpl::LastError() const { 243 webrtc::AudioDeviceModule::ErrorCode WebRtcAudioDeviceImpl::LastError() const {
243 return last_error_; 244 return last_error_;
244 } 245 }
245 246
246 int32_t WebRtcAudioDeviceImpl::RegisterEventObserver( 247 int32_t WebRtcAudioDeviceImpl::RegisterEventObserver(
247 webrtc::AudioDeviceObserver* event_callback) { 248 webrtc::AudioDeviceObserver* event_callback) {
248 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::RegisterEventObserver() " 249 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::RegisterEventObserver() "
249 << "NOT IMPLEMENTED"; 250 << "NOT IMPLEMENTED";
250 return -1; 251 return -1;
251 } 252 }
252 253
253 int32_t WebRtcAudioDeviceImpl::RegisterAudioCallback( 254 int32_t WebRtcAudioDeviceImpl::RegisterAudioCallback(
254 webrtc::AudioTransport* audio_callback) { 255 webrtc::AudioTransport* audio_callback) {
255 VLOG(1) << "RegisterAudioCallback()"; 256 DVLOG(1) << "RegisterAudioCallback()";
256 if (playing_ || recording_) { 257 if (playing_ || recording_) {
257 LOG(ERROR) << "Unable to (de)register transport during active media"; 258 LOG(ERROR) << "Unable to (de)register transport during active media";
258 return -1; 259 return -1;
259 } 260 }
260 audio_transport_callback_ = audio_callback; 261 audio_transport_callback_ = audio_callback;
261 return 0; 262 return 0;
262 } 263 }
263 264
264 int32_t WebRtcAudioDeviceImpl::Init() { 265 int32_t WebRtcAudioDeviceImpl::Init() {
265 VLOG(1) << "Init()"; 266 DVLOG(1) << "Init()";
266 267
267 if (!render_loop_->BelongsToCurrentThread()) { 268 if (!render_loop_->BelongsToCurrentThread()) {
268 int32_t error = 0; 269 int32_t error = 0;
269 base::WaitableEvent event(false, false); 270 base::WaitableEvent event(false, false);
270 // Ensure that we call Init() from the main render thread since 271 // Ensure that we call Init() from the main render thread since
271 // the audio clients can only be created on this thread. 272 // the audio clients can only be created on this thread.
272 render_loop_->PostTask( 273 render_loop_->PostTask(
273 FROM_HERE, 274 FROM_HERE,
274 base::Bind(&WebRtcAudioDeviceImpl::InitOnRenderThread, 275 base::Bind(&WebRtcAudioDeviceImpl::InitOnRenderThread,
275 this, &error, &event)); 276 this, &error, &event));
276 event.Wait(); 277 event.Wait();
277 return error; 278 return error;
278 } 279 }
279 280
280 // Calling Init() multiple times in a row is OK. 281 // Calling Init() multiple times in a row is OK.
281 if (initialized_) 282 if (initialized_)
282 return 0; 283 return 0;
283 284
284 DCHECK(!audio_input_device_); 285 DCHECK(!audio_input_device_);
285 DCHECK(!audio_output_device_); 286 DCHECK(!audio_output_device_);
286 DCHECK(!input_buffer_.get()); 287 DCHECK(!input_buffer_.get());
287 DCHECK(!output_buffer_.get()); 288 DCHECK(!output_buffer_.get());
288 289
289 // Ask the browser for the default audio output hardware sample-rate. 290 // Ask the browser for the default audio output hardware sample-rate.
290 // This request is based on a synchronous IPC message. 291 // This request is based on a synchronous IPC message.
291 int output_sample_rate = 292 int output_sample_rate =
292 static_cast<int>(AudioDevice::GetAudioHardwareSampleRate()); 293 static_cast<int>(AudioDevice::GetAudioHardwareSampleRate());
293 VLOG(1) << "Audio output hardware sample rate: " << output_sample_rate; 294 DVLOG(1) << "Audio output hardware sample rate: " << output_sample_rate;
294 295
295 // Ask the browser for the default audio input hardware sample-rate. 296 // Ask the browser for the default audio input hardware sample-rate.
296 // This request is based on a synchronous IPC message. 297 // This request is based on a synchronous IPC message.
297 int input_sample_rate = GetAudioInputHardwareSampleRate(); 298 int input_sample_rate = GetAudioInputHardwareSampleRate();
298 VLOG(1) << "Audio input hardware sample rate: " << input_sample_rate; 299 DVLOG(1) << "Audio input hardware sample rate: " << input_sample_rate;
299 300
300 int input_channels = 0; 301 int input_channels = 0;
301 int output_channels = 0; 302 int output_channels = 0;
302 303
303 size_t input_buffer_size = 0; 304 size_t input_buffer_size = 0;
304 size_t output_buffer_size = 0; 305 size_t output_buffer_size = 0;
305 306
306 // For real-time audio (in combination with the webrtc::VoiceEngine) it 307 // Windows
307 // is convenient to use audio buffers of size N*10ms.
308
309 #if defined(OS_WIN) 308 #if defined(OS_WIN)
310 if (output_sample_rate != 48000) { 309 if (input_sample_rate != 48000 && input_sample_rate != 44100) {
311 DLOG(ERROR) << "Only 48kHz sample rate is supported on Windows."; 310 DLOG(ERROR) << "Only 48 and 44.1kHz input rates are supported on Windows.";
311 return -1;
312 }
313 if (output_sample_rate != 48000 && output_sample_rate != 44100) {
314 DLOG(ERROR) << "Only 48 and 44.1kHz output rates are supported on Windows.";
312 return -1; 315 return -1;
313 } 316 }
314 317
315 // Use stereo recording on Windows since low-latency Core Audio (WASAPI) 318 // Use stereo recording on Windows since low-latency Core Audio (WASAPI)
316 // does not support mono. 319 // does not support mono.
317 input_channels = 2; 320 input_channels = 2;
318 output_channels = 1; 321
322 // Use stereo rendering on Windows to make input and output sides
323 // symmetric. WASAPI supports both stereo and mono.
324 output_channels = 2;
319 325
320 // Capture side: AUDIO_PCM_LOW_LATENCY is based on the Core Audio (WASAPI) 326 // Capture side: AUDIO_PCM_LOW_LATENCY is based on the Core Audio (WASAPI)
321 // API which was introduced in Windows Vista. For lower Windows versions, 327 // API which was introduced in Windows Vista. For lower Windows versions,
322 // a callback-driven Wave implementation is used instead. An input buffer 328 // a callback-driven Wave implementation is used instead. An input buffer
323 // size of 10ms works well for both these implementations. 329 // size of 10ms works well for both these implementations.
324 330
325 // Use different buffer sizes depending on the current hardware sample rate. 331 // Use different buffer sizes depending on the current hardware sample rate.
326 if (input_sample_rate == 48000) { 332 if (input_sample_rate == 48000) {
327 input_buffer_size = 480; 333 input_buffer_size = 480;
328 } else { 334 } else {
329 // We do run at 44.1kHz at the actual audio layer, but ask for frames 335 // We do run at 44.1kHz at the actual audio layer, but ask for frames
330 // at 44.0kHz to ensure that we can feed them to the webrtc::VoiceEngine. 336 // at 44.0kHz to ensure that we can feed them to the webrtc::VoiceEngine.
331 input_buffer_size = 440; 337 input_buffer_size = 440;
332 } 338 }
333 339
334 // Rendering side: AUDIO_PCM_LOW_LATENCY on Windows is based on a callback- 340 // Render side: AUDIO_PCM_LOW_LATENCY is based on the Core Audio (WASAPI)
335 // driven Wave implementation where 2 buffers are fed to the audio driver 341 // API which was introduced in Windows Vista. For lower Windows versions,
336 // before actual rendering starts. Initial real-time tests have shown that 342 // a callback-driven Wave implementation is used instead. An output buffer
337 // 20ms buffer size (corresponds to ~40ms total delay) is not enough but 343 // size of 10ms works well for WASAPI but 30ms is needed for Wave.
338 // can lead to buffer underruns. The next even multiple of 10ms is 30ms
339 // (<=> ~60ms total delay) and it works fine also under high load.
340 output_buffer_size = 3 * 480;
341 #elif defined(OS_MACOSX)
342 if (output_sample_rate != 48000 && output_sample_rate != 44100) {
343 DLOG(ERROR) << "Only 48 and 44.1kHz sample rates are supported on Mac OSX.";
344 return -1;
345 }
346 input_channels = 1;
347 output_channels = 1;
348
349 // Rendering side: AUDIO_PCM_LOW_LATENCY on Mac OS X is based on a callback-
350 // driven Core Audio implementation. Tests have shown that 10ms is a suitable
351 // frame size to use, both for 48kHz and 44.1kHz.
352 // Capturing side: AUDIO_PCM_LINEAR on Mac OS X uses the Audio Queue Services
353 // API which is not well suited for real-time applications since the delay
354 // is very high. We set buffer sizes to 10ms for the input side here as well
355 // but none of them will work.
356 // TODO(henrika): add support for AUDIO_PCM_LOW_LATENCY on the capture side
357 // based on the Mac OS X Core Audio API.
358 344
359 // Use different buffer sizes depending on the current hardware sample rate. 345 // Use different buffer sizes depending on the current hardware sample rate.
360 if (output_sample_rate == 48000) { 346 if (output_sample_rate == 48000) {
361 input_buffer_size = 480;
362 output_buffer_size = 480; 347 output_buffer_size = 480;
363 } else { 348 } else {
364 // We do run at 44.1kHz at the actual audio layer, but ask for frames 349 // We do run at 44.1kHz at the actual audio layer, but ask for frames
365 // at 44.0kHz to ensure that we can feed them to the webrtc::VoiceEngine. 350 // at 44.0kHz to ensure that we can feed them to the webrtc::VoiceEngine.
351 // TODO(henrika): figure out why we seem to need 20ms here for glitch-
352 // free audio.
353 output_buffer_size = 2 * 440;
354 }
355
356 // Windows XP and lower can't cope with 10 ms output buffer size.
357 // It must be extended to 30 ms (60 ms will be used internally by WaveOut).
358 if (base::win::GetVersion() <= base::win::VERSION_XP) {
359 output_buffer_size = 3 * output_buffer_size;
360 DLOG(WARNING) << "Extending the output buffer size by a factor of three "
361 << "since Windows XP has been detected.";
362 }
363
364 // Mac OS X
365 #elif defined(OS_MACOSX)
366 if (input_sample_rate != 48000 && input_sample_rate != 44100) {
367 DLOG(ERROR) << "Only 48 and 44.1kHz input rates are supported on Mac OSX.";
368 return -1;
369 }
370 if (output_sample_rate != 48000 && output_sample_rate != 44100) {
371 DLOG(ERROR) << "Only 48 and 44.1kHz output rates are supported on Mac OSX.";
372 return -1;
373 }
374
375 input_channels = 1;
376 output_channels = 1;
377
378 // Capture side: AUDIO_PCM_LOW_LATENCY on Mac OS X is based on a callback-
379 // driven Core Audio implementation. Tests have shown that 10ms is a suitable
380 // frame size to use, both for 48kHz and 44.1kHz.
381
382 // Use different buffer sizes depending on the current hardware sample rate.
383 if (input_sample_rate == 48000) {
384 input_buffer_size = 480;
385 } else {
386 // We do run at 44.1kHz at the actual audio layer, but ask for frames
387 // at 44.0kHz to ensure that we can feed them to the webrtc::VoiceEngine.
366 input_buffer_size = 440; 388 input_buffer_size = 440;
389 }
390
391 // Render side: AUDIO_PCM_LOW_LATENCY on Mac OS X is based on a callback-
392 // driven Core Audio implementation. Tests have shown that 10ms is a suitable
393 // frame size to use, both for 48kHz and 44.1kHz.
394
395 // Use different buffer sizes depending on the current hardware sample rate.
396 if (output_sample_rate == 48000) {
397 output_buffer_size = 480;
398 } else {
399 // We do run at 44.1kHz at the actual audio layer, but ask for frames
400 // at 44.0kHz to ensure that we can feed them to the webrtc::VoiceEngine.
367 output_buffer_size = 440; 401 output_buffer_size = 440;
368 } 402 }
403 // Linux
369 #elif defined(OS_LINUX) || defined(OS_OPENBSD) 404 #elif defined(OS_LINUX) || defined(OS_OPENBSD)
370 if (output_sample_rate != 48000) { 405 if (output_sample_rate != 48000) {
371 DLOG(ERROR) << "Only 48kHz sample rate is supported on Linux."; 406 DLOG(ERROR) << "Only 48kHz sample rate is supported on Linux.";
372 return -1; 407 return -1;
373 } 408 }
374 input_channels = 1; 409 input_channels = 1;
375 output_channels = 1; 410 output_channels = 1;
376 411
377 // Based on tests using the current ALSA implementation in Chrome, we have 412 // Based on tests using the current ALSA implementation in Chrome, we have
378 // found that the best combination is 20ms on the input side and 10ms on the 413 // found that the best combination is 20ms on the input side and 10ms on the
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
414 input_buffer_.reset(new int16[input_buffer_size * input_channels]); 449 input_buffer_.reset(new int16[input_buffer_size * input_channels]);
415 output_buffer_.reset(new int16[output_buffer_size * output_channels]); 450 output_buffer_.reset(new int16[output_buffer_size * output_channels]);
416 451
417 DCHECK(input_buffer_.get()); 452 DCHECK(input_buffer_.get());
418 DCHECK(output_buffer_.get()); 453 DCHECK(output_buffer_.get());
419 454
420 bytes_per_sample_ = sizeof(*input_buffer_.get()); 455 bytes_per_sample_ = sizeof(*input_buffer_.get());
421 456
422 initialized_ = true; 457 initialized_ = true;
423 458
424 VLOG(1) << "Capture parameters (size/channels/rate): (" 459 DVLOG(1) << "Capture parameters (size/channels/rate): ("
425 << input_buffer_size_ << "/" << input_channels_ << "/" 460 << input_buffer_size_ << "/" << input_channels_ << "/"
426 << input_sample_rate_ << ")"; 461 << input_sample_rate_ << ")";
427 VLOG(1) << "Render parameters (size/channels/rate): (" 462 DVLOG(1) << "Render parameters (size/channels/rate): ("
428 << output_buffer_size_ << "/" << output_channels_ << "/" 463 << output_buffer_size_ << "/" << output_channels_ << "/"
429 << output_sample_rate_ << ")"; 464 << output_sample_rate_ << ")";
430 return 0; 465 return 0;
431 } 466 }
432 467
433 void WebRtcAudioDeviceImpl::InitOnRenderThread(int32_t* error, 468 void WebRtcAudioDeviceImpl::InitOnRenderThread(int32_t* error,
434 base::WaitableEvent* event) { 469 base::WaitableEvent* event) {
435 DCHECK(render_loop_->BelongsToCurrentThread()); 470 DCHECK(render_loop_->BelongsToCurrentThread());
436 *error = Init(); 471 *error = Init();
437 event->Signal(); 472 event->Signal();
438 } 473 }
439 474
440 int32_t WebRtcAudioDeviceImpl::Terminate() { 475 int32_t WebRtcAudioDeviceImpl::Terminate() {
441 VLOG(1) << "Terminate()"; 476 DVLOG(1) << "Terminate()";
442 477
443 // Calling Terminate() multiple times in a row is OK. 478 // Calling Terminate() multiple times in a row is OK.
444 if (!initialized_) 479 if (!initialized_)
445 return 0; 480 return 0;
446 481
447 DCHECK(audio_input_device_); 482 DCHECK(audio_input_device_);
448 DCHECK(audio_output_device_); 483 DCHECK(audio_output_device_);
449 DCHECK(input_buffer_.get()); 484 DCHECK(input_buffer_.get());
450 DCHECK(output_buffer_.get()); 485 DCHECK(output_buffer_.get());
451 486
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
483 518
484 int32_t WebRtcAudioDeviceImpl::RecordingDeviceName( 519 int32_t WebRtcAudioDeviceImpl::RecordingDeviceName(
485 uint16_t index, 520 uint16_t index,
486 char name[webrtc::kAdmMaxDeviceNameSize], 521 char name[webrtc::kAdmMaxDeviceNameSize],
487 char guid[webrtc::kAdmMaxGuidSize]) { 522 char guid[webrtc::kAdmMaxGuidSize]) {
488 NOTIMPLEMENTED(); 523 NOTIMPLEMENTED();
489 return -1; 524 return -1;
490 } 525 }
491 526
492 int32_t WebRtcAudioDeviceImpl::SetPlayoutDevice(uint16_t index) { 527 int32_t WebRtcAudioDeviceImpl::SetPlayoutDevice(uint16_t index) {
493 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetPlayoutDevice() " 528 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetPlayoutDevice() "
494 << "NOT IMPLEMENTED"; 529 << "NOT IMPLEMENTED";
495 return 0; 530 return 0;
496 } 531 }
497 532
498 int32_t WebRtcAudioDeviceImpl::SetPlayoutDevice(WindowsDeviceType device) { 533 int32_t WebRtcAudioDeviceImpl::SetPlayoutDevice(WindowsDeviceType device) {
499 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetPlayoutDevice() " 534 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetPlayoutDevice() "
500 << "NOT IMPLEMENTED"; 535 << "NOT IMPLEMENTED";
501 return 0; 536 return 0;
502 } 537 }
503 538
504 int32_t WebRtcAudioDeviceImpl::SetRecordingDevice(uint16_t index) { 539 int32_t WebRtcAudioDeviceImpl::SetRecordingDevice(uint16_t index) {
505 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetRecordingDevice() " 540 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetRecordingDevice() "
506 << "NOT IMPLEMENTED"; 541 << "NOT IMPLEMENTED";
507 return 0; 542 return 0;
508 } 543 }
509 544
510 int32_t WebRtcAudioDeviceImpl::SetRecordingDevice(WindowsDeviceType device) { 545 int32_t WebRtcAudioDeviceImpl::SetRecordingDevice(WindowsDeviceType device) {
511 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetRecordingDevice() " 546 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetRecordingDevice() "
512 << "NOT IMPLEMENTED"; 547 << "NOT IMPLEMENTED";
513 return 0; 548 return 0;
514 } 549 }
515 550
516 int32_t WebRtcAudioDeviceImpl::PlayoutIsAvailable(bool* available) { 551 int32_t WebRtcAudioDeviceImpl::PlayoutIsAvailable(bool* available) {
517 VLOG(1) << "PlayoutIsAvailable()"; 552 DVLOG(1) << "PlayoutIsAvailable()";
518 *available = (audio_output_device_ != NULL); 553 *available = (audio_output_device_ != NULL);
519 return 0; 554 return 0;
520 } 555 }
521 556
522 int32_t WebRtcAudioDeviceImpl::InitPlayout() { 557 int32_t WebRtcAudioDeviceImpl::InitPlayout() {
523 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::InitPlayout() " 558 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::InitPlayout() "
524 << "NOT IMPLEMENTED"; 559 << "NOT IMPLEMENTED";
525 return 0; 560 return 0;
526 } 561 }
527 562
528 bool WebRtcAudioDeviceImpl::PlayoutIsInitialized() const { 563 bool WebRtcAudioDeviceImpl::PlayoutIsInitialized() const {
529 VLOG(1) << "PlayoutIsInitialized()"; 564 DVLOG(1) << "PlayoutIsInitialized()";
530 return (audio_output_device_ != NULL); 565 return (audio_output_device_ != NULL);
531 } 566 }
532 567
533 int32_t WebRtcAudioDeviceImpl::RecordingIsAvailable(bool* available) { 568 int32_t WebRtcAudioDeviceImpl::RecordingIsAvailable(bool* available) {
534 VLOG(1) << "RecordingIsAvailable()"; 569 DVLOG(1) << "RecordingIsAvailable()";
535 *available = (audio_input_device_ != NULL); 570 *available = (audio_input_device_ != NULL);
536 return 0; 571 return 0;
537 } 572 }
538 573
539 int32_t WebRtcAudioDeviceImpl::InitRecording() { 574 int32_t WebRtcAudioDeviceImpl::InitRecording() {
540 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::InitRecording() " 575 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::InitRecording() "
541 << "NOT IMPLEMENTED"; 576 << "NOT IMPLEMENTED";
542 return 0; 577 return 0;
543 } 578 }
544 579
545 bool WebRtcAudioDeviceImpl::RecordingIsInitialized() const { 580 bool WebRtcAudioDeviceImpl::RecordingIsInitialized() const {
546 VLOG(1) << "RecordingIsInitialized()"; 581 DVLOG(1) << "RecordingIsInitialized()";
547 return (audio_input_device_ != NULL); 582 return (audio_input_device_ != NULL);
548 } 583 }
549 584
550 int32_t WebRtcAudioDeviceImpl::StartPlayout() { 585 int32_t WebRtcAudioDeviceImpl::StartPlayout() {
551 VLOG(1) << "StartPlayout()"; 586 DVLOG(1) << "StartPlayout()";
552 if (!audio_transport_callback_) { 587 if (!audio_transport_callback_) {
553 LOG(ERROR) << "Audio transport is missing"; 588 LOG(ERROR) << "Audio transport is missing";
554 return -1; 589 return -1;
555 } 590 }
556 if (playing_) { 591 if (playing_) {
557 // webrtc::VoiceEngine assumes that it is OK to call Start() twice and 592 // webrtc::VoiceEngine assumes that it is OK to call Start() twice and
558 // that the call is ignored the second time. 593 // that the call is ignored the second time.
559 LOG(WARNING) << "Playout is already active";
560 return 0; 594 return 0;
561 } 595 }
562 audio_output_device_->Start(); 596 audio_output_device_->Start();
563 playing_ = true; 597 playing_ = true;
564 return 0; 598 return 0;
565 } 599 }
566 600
567 int32_t WebRtcAudioDeviceImpl::StopPlayout() { 601 int32_t WebRtcAudioDeviceImpl::StopPlayout() {
568 VLOG(1) << "StopPlayout()"; 602 DVLOG(1) << "StopPlayout()";
569 DCHECK(audio_output_device_); 603 DCHECK(audio_output_device_);
570 if (!playing_) { 604 if (!playing_) {
571 // webrtc::VoiceEngine assumes that it is OK to call Stop() just in case. 605 // webrtc::VoiceEngine assumes that it is OK to call Stop() just in case.
572 LOG(WARNING) << "Playout was already stopped";
573 return 0; 606 return 0;
574 } 607 }
575 playing_ = !audio_output_device_->Stop(); 608 playing_ = !audio_output_device_->Stop();
576 return (!playing_ ? 0 : -1); 609 return (!playing_ ? 0 : -1);
577 } 610 }
578 611
579 bool WebRtcAudioDeviceImpl::Playing() const { 612 bool WebRtcAudioDeviceImpl::Playing() const {
580 return playing_; 613 return playing_;
581 } 614 }
582 615
583 int32_t WebRtcAudioDeviceImpl::StartRecording() { 616 int32_t WebRtcAudioDeviceImpl::StartRecording() {
584 VLOG(1) << "StartRecording()"; 617 DVLOG(1) << "StartRecording()";
585 #if defined(OS_MACOSX) 618 #if defined(OS_MACOSX)
586 DLOG(WARNING) << "Real-time recording is not yet fully supported on Mac OS X"; 619 DLOG(WARNING) << "Real-time recording is not yet fully supported on Mac OS X";
587 #endif 620 #endif
588 LOG_IF(ERROR, !audio_transport_callback_) << "Audio transport is missing"; 621 LOG_IF(ERROR, !audio_transport_callback_) << "Audio transport is missing";
589 if (!audio_transport_callback_) { 622 if (!audio_transport_callback_) {
590 LOG(ERROR) << "Audio transport is missing"; 623 LOG(ERROR) << "Audio transport is missing";
591 return -1; 624 return -1;
592 } 625 }
593 626
594 if (session_id_ <= 0) { 627 if (session_id_ <= 0) {
595 LOG(WARNING) << session_id_ << " is an invalid session id."; 628 LOG(WARNING) << session_id_ << " is an invalid session id.";
596 // TODO(xians): enable the return -1 when MediaStreamManager can handle 629 // TODO(xians): enable the return -1 when MediaStreamManager can handle
597 // AudioInputDeviceManager. 630 // AudioInputDeviceManager.
598 // return -1; 631 // return -1;
599 } 632 }
600 633
601 base::AutoLock auto_lock(lock_); 634 base::AutoLock auto_lock(lock_);
602 if (recording_) { 635 if (recording_) {
603 // webrtc::VoiceEngine assumes that it is OK to call Start() twice and 636 // webrtc::VoiceEngine assumes that it is OK to call Start() twice and
604 // that the call is ignored the second time. 637 // that the call is ignored the second time.
605 LOG(WARNING) << "Recording is already active";
606 return 0; 638 return 0;
607 } 639 }
608 640
609 // Specify the session_id which is mapped to a certain device. 641 // Specify the session_id which is mapped to a certain device.
610 audio_input_device_->SetDevice(session_id_); 642 audio_input_device_->SetDevice(session_id_);
611 audio_input_device_->Start(); 643 audio_input_device_->Start();
612 recording_ = true; 644 recording_ = true;
613 return 0; 645 return 0;
614 } 646 }
615 647
616 int32_t WebRtcAudioDeviceImpl::StopRecording() { 648 int32_t WebRtcAudioDeviceImpl::StopRecording() {
617 VLOG(1) << "StopRecording()"; 649 DVLOG(1) << "StopRecording()";
618 DCHECK(audio_input_device_); 650 DCHECK(audio_input_device_);
619 651
620 base::AutoLock auto_lock(lock_); 652 base::AutoLock auto_lock(lock_);
621 if (!recording_) { 653 if (!recording_) {
622 // webrtc::VoiceEngine assumes that it is OK to call Stop() just in case. 654 // webrtc::VoiceEngine assumes that it is OK to call Stop() just in case.
623 LOG(WARNING) << "Recording was already stopped";
624 return 0; 655 return 0;
625 } 656 }
626 recording_ = !audio_input_device_->Stop(); 657 recording_ = !audio_input_device_->Stop();
627 return (!recording_ ? 0 : -1); 658 return (!recording_ ? 0 : -1);
628 } 659 }
629 660
630 bool WebRtcAudioDeviceImpl::Recording() const { 661 bool WebRtcAudioDeviceImpl::Recording() const {
631 return recording_; 662 return recording_;
632 } 663 }
633 664
634 int32_t WebRtcAudioDeviceImpl::SetAGC(bool enable) { 665 int32_t WebRtcAudioDeviceImpl::SetAGC(bool enable) {
635 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetAGC() " 666 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetAGC() " << "NOT IMPLEMENTED";
636 << "NOT IMPLEMENTED";
637 return -1; 667 return -1;
638 } 668 }
639 669
640 bool WebRtcAudioDeviceImpl::AGC() const { 670 bool WebRtcAudioDeviceImpl::AGC() const {
641 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::AGC() " 671 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::AGC() " << "NOT IMPLEMENTED";
642 << "NOT IMPLEMENTED";
643 return false; 672 return false;
644 } 673 }
645 674
646 int32_t WebRtcAudioDeviceImpl::SetWaveOutVolume(uint16_t volume_left, 675 int32_t WebRtcAudioDeviceImpl::SetWaveOutVolume(uint16_t volume_left,
647 uint16_t volume_right) { 676 uint16_t volume_right) {
648 NOTIMPLEMENTED(); 677 NOTIMPLEMENTED();
649 return -1; 678 return -1;
650 } 679 }
651 int32_t WebRtcAudioDeviceImpl::WaveOutVolume( 680 int32_t WebRtcAudioDeviceImpl::WaveOutVolume(
652 uint16_t* volume_left, 681 uint16_t* volume_left,
653 uint16_t* volume_right) const { 682 uint16_t* volume_right) const {
654 NOTIMPLEMENTED(); 683 NOTIMPLEMENTED();
655 return -1; 684 return -1;
656 } 685 }
657 686
658 int32_t WebRtcAudioDeviceImpl::SpeakerIsAvailable(bool* available) { 687 int32_t WebRtcAudioDeviceImpl::SpeakerIsAvailable(bool* available) {
659 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SpeakerIsAvailable() " 688 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SpeakerIsAvailable() "
660 << "NOT IMPLEMENTED"; 689 << "NOT IMPLEMENTED";
661 *available = true; 690 *available = true;
662 return 0; 691 return 0;
663 } 692 }
664 693
665 int32_t WebRtcAudioDeviceImpl::InitSpeaker() { 694 int32_t WebRtcAudioDeviceImpl::InitSpeaker() {
666 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::InitSpeaker() " 695 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::InitSpeaker() "
667 << "NOT IMPLEMENTED"; 696 << "NOT IMPLEMENTED";
668 return 0; 697 return 0;
669 } 698 }
670 699
671 bool WebRtcAudioDeviceImpl::SpeakerIsInitialized() const { 700 bool WebRtcAudioDeviceImpl::SpeakerIsInitialized() const {
672 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SpeakerIsInitialized() " 701 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SpeakerIsInitialized() "
673 << "NOT IMPLEMENTED"; 702 << "NOT IMPLEMENTED";
674 return true; 703 return true;
675 } 704 }
676 705
677 int32_t WebRtcAudioDeviceImpl::MicrophoneIsAvailable(bool* available) { 706 int32_t WebRtcAudioDeviceImpl::MicrophoneIsAvailable(bool* available) {
678 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::MicrophoneIsAvailable() " 707 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::MicrophoneIsAvailable() "
679 << "NOT IMPLEMENTED"; 708 << "NOT IMPLEMENTED";
680 *available = true; 709 *available = true;
681 return 0; 710 return 0;
682 } 711 }
683 712
684 int32_t WebRtcAudioDeviceImpl::InitMicrophone() { 713 int32_t WebRtcAudioDeviceImpl::InitMicrophone() {
685 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::InitMicrophone() " 714 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::InitMicrophone() "
686 << "NOT IMPLEMENTED"; 715 << "NOT IMPLEMENTED";
687 return 0; 716 return 0;
688 } 717 }
689 718
690 bool WebRtcAudioDeviceImpl::MicrophoneIsInitialized() const { 719 bool WebRtcAudioDeviceImpl::MicrophoneIsInitialized() const {
691 NOTIMPLEMENTED(); 720 NOTIMPLEMENTED();
692 return true; 721 return true;
693 } 722 }
694 723
695 int32_t WebRtcAudioDeviceImpl::SpeakerVolumeIsAvailable( 724 int32_t WebRtcAudioDeviceImpl::SpeakerVolumeIsAvailable(
696 bool* available) { 725 bool* available) {
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
735 return -1; 764 return -1;
736 } 765 }
737 766
738 int32_t WebRtcAudioDeviceImpl::MicrophoneVolume(uint32_t* volume) const { 767 int32_t WebRtcAudioDeviceImpl::MicrophoneVolume(uint32_t* volume) const {
739 NOTIMPLEMENTED(); 768 NOTIMPLEMENTED();
740 return -1; 769 return -1;
741 } 770 }
742 771
743 int32_t WebRtcAudioDeviceImpl::MaxMicrophoneVolume( 772 int32_t WebRtcAudioDeviceImpl::MaxMicrophoneVolume(
744 uint32_t* max_volume) const { 773 uint32_t* max_volume) const {
745 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::MaxMicrophoneVolume() " 774 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::MaxMicrophoneVolume() "
746 << "NOT IMPLEMENTED"; 775 << "NOT IMPLEMENTED";
747 return -1; 776 return -1;
748 } 777 }
749 778
750 int32_t WebRtcAudioDeviceImpl::MinMicrophoneVolume( 779 int32_t WebRtcAudioDeviceImpl::MinMicrophoneVolume(
751 uint32_t* min_volume) const { 780 uint32_t* min_volume) const {
752 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::MinMicrophoneVolume() " 781 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::MinMicrophoneVolume() "
753 << "NOT IMPLEMENTED"; 782 << "NOT IMPLEMENTED";
754 return -1; 783 return -1;
755 } 784 }
756 785
757 int32_t WebRtcAudioDeviceImpl::MicrophoneVolumeStepSize( 786 int32_t WebRtcAudioDeviceImpl::MicrophoneVolumeStepSize(
758 uint16_t* step_size) const { 787 uint16_t* step_size) const {
759 NOTIMPLEMENTED(); 788 NOTIMPLEMENTED();
760 return -1; 789 return -1;
761 } 790 }
762 791
763 int32_t WebRtcAudioDeviceImpl::SpeakerMuteIsAvailable(bool* available) { 792 int32_t WebRtcAudioDeviceImpl::SpeakerMuteIsAvailable(bool* available) {
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
800 NOTIMPLEMENTED(); 829 NOTIMPLEMENTED();
801 return -1; 830 return -1;
802 } 831 }
803 832
804 int32_t WebRtcAudioDeviceImpl::MicrophoneBoost(bool* enabled) const { 833 int32_t WebRtcAudioDeviceImpl::MicrophoneBoost(bool* enabled) const {
805 NOTIMPLEMENTED(); 834 NOTIMPLEMENTED();
806 return -1; 835 return -1;
807 } 836 }
808 837
809 int32_t WebRtcAudioDeviceImpl::StereoPlayoutIsAvailable(bool* available) const { 838 int32_t WebRtcAudioDeviceImpl::StereoPlayoutIsAvailable(bool* available) const {
810 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::StereoPlayoutIsAvailable() " 839 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::StereoPlayoutIsAvailable() "
811 << "NOT IMPLEMENTED"; 840 << "NOT IMPLEMENTED";
812 *available = false; 841 *available = false;
813 return 0; 842 return 0;
814 } 843 }
815 844
816 int32_t WebRtcAudioDeviceImpl::SetStereoPlayout(bool enable) { 845 int32_t WebRtcAudioDeviceImpl::SetStereoPlayout(bool enable) {
817 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetStereoPlayout() " 846 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetStereoPlayout() "
818 << "NOT IMPLEMENTED"; 847 << "NOT IMPLEMENTED";
819 return 0; 848 return 0;
820 } 849 }
821 850
822 int32_t WebRtcAudioDeviceImpl::StereoPlayout(bool* enabled) const { 851 int32_t WebRtcAudioDeviceImpl::StereoPlayout(bool* enabled) const {
823 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::StereoPlayout() " 852 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::StereoPlayout() "
824 << "NOT IMPLEMENTED"; 853 << "NOT IMPLEMENTED";
825 return 0; 854 return 0;
826 } 855 }
827 856
828 int32_t WebRtcAudioDeviceImpl::StereoRecordingIsAvailable( 857 int32_t WebRtcAudioDeviceImpl::StereoRecordingIsAvailable(
829 bool* available) const { 858 bool* available) const {
830 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::StereoRecordingIsAvailable() " 859 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::StereoRecordingIsAvailable() "
831 << "NOT IMPLEMENTED"; 860 << "NOT IMPLEMENTED";
832 return 0; 861 return 0;
833 } 862 }
834 863
835 int32_t WebRtcAudioDeviceImpl::SetStereoRecording(bool enable) { 864 int32_t WebRtcAudioDeviceImpl::SetStereoRecording(bool enable) {
836 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetStereoRecording() " 865 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::SetStereoRecording() "
837 << "NOT IMPLEMENTED"; 866 << "NOT IMPLEMENTED";
838 return -1; 867 return -1;
839 } 868 }
840 869
841 int32_t WebRtcAudioDeviceImpl::StereoRecording(bool* enabled) const { 870 int32_t WebRtcAudioDeviceImpl::StereoRecording(bool* enabled) const {
842 VLOG(2) << "WARNING: WebRtcAudioDeviceImpl::StereoRecording() " 871 DVLOG(2) << "WARNING: WebRtcAudioDeviceImpl::StereoRecording() "
843 << "NOT IMPLEMENTED"; 872 << "NOT IMPLEMENTED";
844 return -1; 873 return -1;
845 } 874 }
846 875
847 int32_t WebRtcAudioDeviceImpl::SetRecordingChannel(const ChannelType channel) { 876 int32_t WebRtcAudioDeviceImpl::SetRecordingChannel(const ChannelType channel) {
848 NOTIMPLEMENTED(); 877 NOTIMPLEMENTED();
849 return -1; 878 return -1;
850 } 879 }
851 880
852 int32_t WebRtcAudioDeviceImpl::RecordingChannel(ChannelType* channel) const { 881 int32_t WebRtcAudioDeviceImpl::RecordingChannel(ChannelType* channel) const {
853 NOTIMPLEMENTED(); 882 NOTIMPLEMENTED();
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
944 } 973 }
945 974
946 int32_t WebRtcAudioDeviceImpl::GetLoudspeakerStatus(bool* enabled) const { 975 int32_t WebRtcAudioDeviceImpl::GetLoudspeakerStatus(bool* enabled) const {
947 NOTIMPLEMENTED(); 976 NOTIMPLEMENTED();
948 return -1; 977 return -1;
949 } 978 }
950 979
951 void WebRtcAudioDeviceImpl::SetSessionId(int session_id) { 980 void WebRtcAudioDeviceImpl::SetSessionId(int session_id) {
952 session_id_ = session_id; 981 session_id_ = session_id;
953 } 982 }
OLDNEW
« no previous file with comments | « no previous file | media/audio/audio_util.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698