OLD | NEW |
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/string_util.h" | 7 #include "base/string_util.h" |
8 #include "media/audio/audio_util.h" | 8 #include "media/audio/audio_util.h" |
9 | 9 |
10 // TODO(henrika): come up with suitable value(s) for all platforms. | 10 // TODO(henrika): come up with suitable value(s) for all platforms. |
11 // Max supported size for input and output buffers. | 11 // Max supported size for input and output buffers. |
12 // Unit is in #(audio frames), hence 1440 <=> 30ms @ 48kHz. | 12 // Unit is in #(audio frames), hence 1440 <=> 30ms @ 48kHz. |
13 static const size_t kMaxBufferSize = 1440; | 13 static const size_t kMaxBufferSize = 1440; |
14 static const int kMaxChannels = 2; | 14 static const int kMaxChannels = 2; |
15 static const int64 kMillisecondsBetweenProcessCalls = 5000; | 15 static const int64 kMillisecondsBetweenProcessCalls = 5000; |
16 static const char kVersion[] = "WebRTC AudioDevice 1.0.0.Chrome"; | 16 static const char kVersion[] = "WebRTC AudioDevice 1.0.0.Chrome"; |
17 | 17 |
18 WebRtcAudioDeviceImpl::WebRtcAudioDeviceImpl( | 18 WebRtcAudioDeviceImpl::WebRtcAudioDeviceImpl( |
19 size_t input_buffer_size, size_t output_buffer_size, | 19 size_t input_buffer_size, size_t output_buffer_size, |
20 int input_channels, int output_channels, | 20 int input_channels, int output_channels, |
21 double input_sample_rate, double output_sample_rate) | 21 double input_sample_rate, double output_sample_rate) |
22 : audio_transport_callback_(NULL), | 22 : audio_transport_callback_(NULL), |
23 last_error_(AudioDeviceModule::kAdmErrNone), | 23 last_error_(AudioDeviceModule::kAdmErrNone), |
24 input_buffer_size_(input_buffer_size), | 24 input_buffer_size_(input_buffer_size), |
25 output_buffer_size_(output_buffer_size), | 25 output_buffer_size_(output_buffer_size), |
26 input_channels_(input_channels), | 26 input_channels_(input_channels), |
27 output_channels_(output_channels), | 27 output_channels_(output_channels), |
28 input_sample_rate_(input_sample_rate), | 28 input_sample_rate_(input_sample_rate), |
29 output_sample_rate_(output_sample_rate), | 29 output_sample_rate_(output_sample_rate), |
| 30 adm_thread_("WebRtcAudioDeviceImpl"), |
| 31 recording_stop_event_(false, false), |
30 initialized_(false), | 32 initialized_(false), |
31 playing_(false), | 33 playing_(false), |
32 recording_(false), | 34 recording_state_(kStopped), |
33 input_delay_ms_(0), | 35 input_delay_ms_(0), |
34 output_delay_ms_(0), | 36 output_delay_ms_(0), |
35 last_process_time_(base::TimeTicks::Now()) { | 37 last_process_time_(base::TimeTicks::Now()) { |
36 VLOG(1) << "WebRtcAudioDeviceImpl::WebRtcAudioDeviceImpl()"; | 38 VLOG(1) << "WebRtcAudioDeviceImpl::WebRtcAudioDeviceImpl()"; |
37 | 39 |
| 40 adm_thread_.Start(); |
| 41 adm_message_loop_ = adm_thread_.message_loop_proxy(); |
| 42 |
38 // Create an AudioInputDevice client if the requested buffer size | 43 // Create an AudioInputDevice client if the requested buffer size |
39 // is an even multiple of 10 milliseconds. | 44 // is an even multiple of 10 milliseconds. |
40 if (BufferSizeIsValid(input_buffer_size, input_sample_rate)) { | 45 if (BufferSizeIsValid(input_buffer_size, input_sample_rate)) { |
41 audio_input_device_ = new AudioInputDevice( | 46 audio_input_device_ = new AudioInputDevice( |
42 input_buffer_size, | 47 input_buffer_size, |
43 input_channels, | 48 input_channels, |
44 input_sample_rate, | 49 input_sample_rate, |
| 50 adm_message_loop_.get(), |
| 51 this, |
45 this); | 52 this); |
46 } | 53 } |
47 | 54 |
48 // Create an AudioDevice client if the requested buffer size | 55 // Create an AudioDevice client if the requested buffer size |
49 // is an even multiple of 10 milliseconds. | 56 // is an even multiple of 10 milliseconds. |
50 if (BufferSizeIsValid(output_buffer_size, output_sample_rate)) { | 57 if (BufferSizeIsValid(output_buffer_size, output_sample_rate)) { |
51 audio_output_device_ = new AudioDevice( | 58 audio_output_device_ = new AudioDevice( |
52 output_buffer_size, | 59 output_buffer_size, |
53 output_channels, | 60 output_channels, |
54 output_sample_rate, | 61 output_sample_rate, |
55 this); | 62 this); |
56 } | 63 } |
57 DCHECK(audio_input_device_); | 64 DCHECK(audio_input_device_); |
58 DCHECK(audio_output_device_); | 65 DCHECK(audio_output_device_); |
59 | 66 |
60 input_buffer_.reset(new int16[kMaxBufferSize * kMaxChannels]); | 67 input_buffer_.reset(new int16[kMaxBufferSize * kMaxChannels]); |
61 output_buffer_.reset(new int16[kMaxBufferSize * kMaxChannels]); | 68 output_buffer_.reset(new int16[kMaxBufferSize * kMaxChannels]); |
62 | 69 |
63 bytes_per_sample_ = sizeof(*input_buffer_.get()); | 70 bytes_per_sample_ = sizeof(*input_buffer_.get()); |
64 } | 71 } |
65 | 72 |
66 WebRtcAudioDeviceImpl::~WebRtcAudioDeviceImpl() { | 73 WebRtcAudioDeviceImpl::~WebRtcAudioDeviceImpl() { |
67 VLOG(1) << "WebRtcAudioDeviceImpl::~WebRtcAudioDeviceImpl()"; | 74 VLOG(1) << "WebRtcAudioDeviceImpl::~WebRtcAudioDeviceImpl()"; |
68 if (playing_) | 75 if (playing_) |
69 StopPlayout(); | 76 StopPlayout(); |
70 if (recording_) | 77 StopRecording(); |
71 StopRecording(); | |
72 if (initialized_) | 78 if (initialized_) |
73 Terminate(); | 79 Terminate(); |
| 80 adm_thread_.Stop(); |
74 } | 81 } |
75 | 82 |
76 void WebRtcAudioDeviceImpl::Render( | 83 void WebRtcAudioDeviceImpl::Render( |
77 const std::vector<float*>& audio_data, | 84 const std::vector<float*>& audio_data, |
78 size_t number_of_frames, | 85 size_t number_of_frames, |
79 size_t audio_delay_milliseconds) { | 86 size_t audio_delay_milliseconds) { |
80 DCHECK_LE(number_of_frames, kMaxBufferSize); | 87 DCHECK_LE(number_of_frames, kMaxBufferSize); |
81 | 88 |
82 // Store the reported audio delay locally. | 89 // Store the reported audio delay locally. |
| 90 lock_.Acquire(); |
83 output_delay_ms_ = audio_delay_milliseconds; | 91 output_delay_ms_ = audio_delay_milliseconds; |
| 92 lock_.Release(); |
84 | 93 |
85 const int channels = audio_data.size(); | 94 const int channels = audio_data.size(); |
86 DCHECK_LE(channels, kMaxChannels); | 95 DCHECK_LE(channels, kMaxChannels); |
87 | 96 |
88 const int samples_per_sec = static_cast<int>(input_sample_rate_); | 97 const int samples_per_sec = static_cast<int>(input_sample_rate_); |
89 uint32_t samples_per_10_msec = (samples_per_sec / 100); | 98 uint32_t samples_per_10_msec = (samples_per_sec / 100); |
90 const int bytes_per_10_msec = | 99 const int bytes_per_10_msec = |
91 channels * samples_per_10_msec * bytes_per_sample_; | 100 channels * samples_per_10_msec * bytes_per_sample_; |
92 | 101 |
93 uint32_t num_audio_samples = 0; | 102 uint32_t num_audio_samples = 0; |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
147 const int bytes_per_10_msec = | 156 const int bytes_per_10_msec = |
148 channels * samples_per_10_msec * bytes_per_sample_; | 157 channels * samples_per_10_msec * bytes_per_sample_; |
149 size_t accumulated_audio_samples = 0; | 158 size_t accumulated_audio_samples = 0; |
150 | 159 |
151 char* audio_byte_buffer = reinterpret_cast<char*>(input_buffer_.get()); | 160 char* audio_byte_buffer = reinterpret_cast<char*>(input_buffer_.get()); |
152 | 161 |
153 // Write audio samples in blocks of 10 milliseconds to the registered | 162 // Write audio samples in blocks of 10 milliseconds to the registered |
154 // webrtc::AudioTransport sink. Keep writing until our internal byte | 163 // webrtc::AudioTransport sink. Keep writing until our internal byte |
155 // buffer is empty. | 164 // buffer is empty. |
156 while (accumulated_audio_samples < number_of_frames) { | 165 while (accumulated_audio_samples < number_of_frames) { |
| 166 lock_.Acquire(); |
| 167 int output_delay_ms = output_delay_ms_; |
| 168 lock_.Release(); |
157 // Deliver 10ms of recorded PCM audio. | 169 // Deliver 10ms of recorded PCM audio. |
158 // TODO(henrika): add support for analog AGC? | 170 // TODO(henrika): add support for analog AGC? |
159 audio_transport_callback_->RecordedDataIsAvailable( | 171 audio_transport_callback_->RecordedDataIsAvailable( |
160 audio_byte_buffer, | 172 audio_byte_buffer, |
161 samples_per_10_msec, | 173 samples_per_10_msec, |
162 bytes_per_sample_, | 174 bytes_per_sample_, |
163 channels, | 175 channels, |
164 samples_per_sec, | 176 samples_per_sec, |
165 input_delay_ms_ + output_delay_ms_, | 177 input_delay_ms_ + output_delay_ms, |
166 0, // clock_drift | 178 0, // clock_drift |
167 0, // current_mic_level | 179 0, // current_mic_level |
168 new_mic_level); // not used | 180 new_mic_level); // not used |
169 accumulated_audio_samples += samples_per_10_msec; | 181 accumulated_audio_samples += samples_per_10_msec; |
170 audio_byte_buffer += bytes_per_10_msec; | 182 audio_byte_buffer += bytes_per_10_msec; |
171 } | 183 } |
172 } | 184 } |
173 | 185 |
174 int32_t WebRtcAudioDeviceImpl::Version(char* version, | 186 int32_t WebRtcAudioDeviceImpl::Version(char* version, |
175 uint32_t& remaining_buffer_in_bytes, | 187 uint32_t& remaining_buffer_in_bytes, |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
225 int32_t WebRtcAudioDeviceImpl::RegisterEventObserver( | 237 int32_t WebRtcAudioDeviceImpl::RegisterEventObserver( |
226 webrtc::AudioDeviceObserver* event_callback) { | 238 webrtc::AudioDeviceObserver* event_callback) { |
227 VLOG(1) << "RegisterEventObserver()"; | 239 VLOG(1) << "RegisterEventObserver()"; |
228 NOTIMPLEMENTED(); | 240 NOTIMPLEMENTED(); |
229 return -1; | 241 return -1; |
230 } | 242 } |
231 | 243 |
232 int32_t WebRtcAudioDeviceImpl::RegisterAudioCallback( | 244 int32_t WebRtcAudioDeviceImpl::RegisterAudioCallback( |
233 webrtc::AudioTransport* audio_callback) { | 245 webrtc::AudioTransport* audio_callback) { |
234 VLOG(1) << "RegisterAudioCallback()"; | 246 VLOG(1) << "RegisterAudioCallback()"; |
235 if (playing_ || recording_) { | 247 int32_t error = 0; |
| 248 base::WaitableEvent event(false, false); |
| 249 adm_message_loop_->PostTask(FROM_HERE, |
| 250 NewRunnableMethod( |
| 251 this, |
| 252 &WebRtcAudioDeviceImpl::RegisterAudioCallbackOnAdmThread, |
| 253 audio_callback, &error, &event)); |
| 254 event.Wait(); |
| 255 return error; |
| 256 } |
| 257 |
| 258 void WebRtcAudioDeviceImpl::RegisterAudioCallbackOnAdmThread( |
| 259 webrtc::AudioTransport* audio_callback, |
| 260 int32_t* error, |
| 261 base::WaitableEvent* event) { |
| 262 VLOG(1) << "RegisterAudioCallbackOnAdmThread()"; |
| 263 if (playing_ || recording_state_ != kStopped) { |
236 LOG(ERROR) << "Unable to (de)register transport during active media"; | 264 LOG(ERROR) << "Unable to (de)register transport during active media"; |
237 return -1; | 265 *error = -1; |
| 266 return; |
238 } | 267 } |
239 audio_transport_callback_ = audio_callback; | 268 audio_transport_callback_ = audio_callback; |
240 return 0; | 269 *error = 0; |
| 270 event->Signal(); |
241 } | 271 } |
242 | 272 |
243 int32_t WebRtcAudioDeviceImpl::Init() { | 273 int32_t WebRtcAudioDeviceImpl::Init() { |
244 VLOG(1) << "Init()"; | 274 VLOG(1) << "Init()"; |
245 if (initialized_) | 275 if (initialized_) |
246 return 0; | 276 return 0; |
247 initialized_ = true; | 277 initialized_ = true; |
248 return 0; | 278 return 0; |
249 } | 279 } |
250 | 280 |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
371 playing_ = !audio_output_device_->Stop(); | 401 playing_ = !audio_output_device_->Stop(); |
372 return (!playing_ ? 0 : -1); | 402 return (!playing_ ? 0 : -1); |
373 } | 403 } |
374 | 404 |
375 bool WebRtcAudioDeviceImpl::Playing() const { | 405 bool WebRtcAudioDeviceImpl::Playing() const { |
376 return playing_; | 406 return playing_; |
377 } | 407 } |
378 | 408 |
379 int32_t WebRtcAudioDeviceImpl::StartRecording() { | 409 int32_t WebRtcAudioDeviceImpl::StartRecording() { |
380 VLOG(1) << "StartRecording()"; | 410 VLOG(1) << "StartRecording()"; |
381 LOG_IF(ERROR, !audio_transport_callback_) << "Audio transport is missing"; | 411 adm_message_loop_->PostTask(FROM_HERE, |
382 if (!audio_transport_callback_) { | 412 NewRunnableMethod(this, |
383 LOG(ERROR) << "Audio transport is missing"; | 413 &WebRtcAudioDeviceImpl::StartRecordingOnAdmThread)); |
384 return -1; | 414 return 0; |
385 } | 415 } |
386 if (recording_) { | 416 |
| 417 void WebRtcAudioDeviceImpl::StartRecordingOnAdmThread() { |
| 418 VLOG(1) << "StartRecordingOnAdmThread()"; |
| 419 // Required to set audio_transport_callback_ before starting recording. |
| 420 DCHECK(audio_transport_callback_); |
| 421 if (recording_state_ != kStopped) { |
387 // webrtc::VoiceEngine assumes that it is OK to call Start() twice and | 422 // webrtc::VoiceEngine assumes that it is OK to call Start() twice and |
388 // that the call is ignored the second time. | 423 // that the call is ignored the second time. |
389 LOG(WARNING) << "Recording is already active"; | 424 LOG(WARNING) << "Recording is already active"; |
390 return 0; | 425 return; |
391 } | 426 } |
392 recording_ = audio_input_device_->Start(); | 427 audio_input_device_->Start(); |
393 return (recording_ ? 0 : -1); | 428 recording_state_ = kStarting; |
394 } | 429 } |
395 | 430 |
396 int32_t WebRtcAudioDeviceImpl::StopRecording() { | 431 int32_t WebRtcAudioDeviceImpl::StopRecording() { |
397 VLOG(1) << "StopRecording()"; | 432 VLOG(1) << "StopRecording()"; |
398 DCHECK(audio_input_device_); | 433 adm_message_loop_->PostTask(FROM_HERE, |
399 if (!recording_) { | 434 NewRunnableMethod(this, |
| 435 &WebRtcAudioDeviceImpl::StopRecordingOnAdmThread)); |
| 436 recording_stop_event_.Wait(); |
| 437 return 0; |
| 438 } |
| 439 |
| 440 void WebRtcAudioDeviceImpl::StopRecordingOnAdmThread() { |
| 441 VLOG(1) << "StopRecordingOnAdmThread()"; |
| 442 // Client never sees kStopping state since it's always blocked on |
| 443 // StopRecording() call. |
| 444 DCHECK_NE(recording_state_, kStopping); |
| 445 if (recording_state_ == kStopped) { |
400 // webrtc::VoiceEngine assumes that it is OK to call Stop() just in case. | 446 // webrtc::VoiceEngine assumes that it is OK to call Stop() just in case. |
401 LOG(WARNING) << "Recording was already stopped"; | 447 LOG(WARNING) << "Recording was already stopped"; |
402 return 0; | 448 recording_stop_event_.Signal(); |
| 449 return; |
403 } | 450 } |
404 recording_ = !audio_input_device_->Stop(); | 451 audio_input_device_->Stop(); |
405 return (!recording_ ? 0 : -1); | 452 recording_state_ = kStopping; |
| 453 } |
| 454 |
| 455 void WebRtcAudioDeviceImpl::OnRecordingStarted() { |
| 456 VLOG(1) << "OnRecordingStarted()"; |
| 457 adm_message_loop_->PostTask(FROM_HERE, |
| 458 NewRunnableMethod(this, |
| 459 &WebRtcAudioDeviceImpl::OnRecordingStartedOnAdmThread)); |
| 460 } |
| 461 |
| 462 void WebRtcAudioDeviceImpl::OnRecordingStartedOnAdmThread() { |
| 463 VLOG(1) << "OnRecordingStartedOnAdmThread()"; |
| 464 recording_state_ = kStarted; |
| 465 } |
| 466 |
| 467 void WebRtcAudioDeviceImpl::OnRecordingStopped() { |
| 468 VLOG(1) << "OnRecordingStopped()"; |
| 469 adm_message_loop_->PostTask(FROM_HERE, |
| 470 NewRunnableMethod(this, |
| 471 &WebRtcAudioDeviceImpl::OnRecordingStoppedOnAdmThread)); |
| 472 } |
| 473 |
| 474 void WebRtcAudioDeviceImpl::OnRecordingStoppedOnAdmThread() { |
| 475 VLOG(1) << "OnRecordingStoppedOnAdmThread()"; |
| 476 recording_state_ = kStopped; |
| 477 // Always signal "stopped" since client must be waiting for it. |
| 478 recording_stop_event_.Signal(); |
406 } | 479 } |
407 | 480 |
408 bool WebRtcAudioDeviceImpl::Recording() const { | 481 bool WebRtcAudioDeviceImpl::Recording() const { |
409 return recording_; | 482 bool recording = false; |
| 483 base::WaitableEvent event(false, false); |
| 484 adm_message_loop_->PostTask(FROM_HERE, |
| 485 NewRunnableMethod(this, |
| 486 &WebRtcAudioDeviceImpl::GetRecordingOnAdmThread, |
| 487 &recording, &event)); |
| 488 event.Wait(); |
| 489 return recording; |
| 490 } |
| 491 |
| 492 void WebRtcAudioDeviceImpl::GetRecordingOnAdmThread( |
| 493 bool* recording, |
| 494 base::WaitableEvent* event) const { |
| 495 *recording = recording_state_ == kStarting || |
| 496 recording_state_ == kStarted || |
| 497 recording_state_ == kStopping; |
| 498 event->Signal(); |
410 } | 499 } |
411 | 500 |
412 int32_t WebRtcAudioDeviceImpl::SetAGC(bool enable) { | 501 int32_t WebRtcAudioDeviceImpl::SetAGC(bool enable) { |
413 NOTIMPLEMENTED(); | 502 NOTIMPLEMENTED(); |
414 return -1; | 503 return -1; |
415 } | 504 } |
416 | 505 |
417 bool WebRtcAudioDeviceImpl::AGC() const { | 506 bool WebRtcAudioDeviceImpl::AGC() const { |
418 NOTIMPLEMENTED(); | 507 NOTIMPLEMENTED(); |
419 return false; | 508 return false; |
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
637 return -1; | 726 return -1; |
638 } | 727 } |
639 | 728 |
640 int32_t WebRtcAudioDeviceImpl::PlayoutDelay(uint16_t* delay_ms) const { | 729 int32_t WebRtcAudioDeviceImpl::PlayoutDelay(uint16_t* delay_ms) const { |
641 // Report the cached output delay value. | 730 // Report the cached output delay value. |
642 *delay_ms = static_cast<uint16_t>(output_delay_ms_); | 731 *delay_ms = static_cast<uint16_t>(output_delay_ms_); |
643 return 0; | 732 return 0; |
644 } | 733 } |
645 | 734 |
646 int32_t WebRtcAudioDeviceImpl::RecordingDelay(uint16_t* delay_ms) const { | 735 int32_t WebRtcAudioDeviceImpl::RecordingDelay(uint16_t* delay_ms) const { |
647 // Report the cached output delay value. | 736 base::WaitableEvent event(false, false); |
648 *delay_ms = static_cast<uint16_t>(input_delay_ms_); | 737 adm_message_loop_->PostTask(FROM_HERE, |
| 738 NewRunnableMethod(this, |
| 739 &WebRtcAudioDeviceImpl::GetRecordingDelayOnAdmThread, |
| 740 delay_ms, &event)); |
| 741 event.Wait(); |
649 return 0; | 742 return 0; |
650 } | 743 } |
651 | 744 |
| 745 void WebRtcAudioDeviceImpl::GetRecordingDelayOnAdmThread( |
| 746 uint16_t* delay_ms, |
| 747 base::WaitableEvent* event) const { |
| 748 // Report the cached output delay value. |
| 749 *delay_ms = static_cast<uint16_t>(input_delay_ms_); |
| 750 event->Signal(); |
| 751 } |
| 752 |
652 int32_t WebRtcAudioDeviceImpl::CPULoad(uint16_t* load) const { | 753 int32_t WebRtcAudioDeviceImpl::CPULoad(uint16_t* load) const { |
653 NOTIMPLEMENTED(); | 754 NOTIMPLEMENTED(); |
654 return -1; | 755 return -1; |
655 } | 756 } |
656 | 757 |
657 int32_t WebRtcAudioDeviceImpl::StartRawOutputFileRecording( | 758 int32_t WebRtcAudioDeviceImpl::StartRawOutputFileRecording( |
658 const char pcm_file_name_utf8[webrtc::kAdmMaxFileNameSize]) { | 759 const char pcm_file_name_utf8[webrtc::kAdmMaxFileNameSize]) { |
659 NOTIMPLEMENTED(); | 760 NOTIMPLEMENTED(); |
660 return -1; | 761 return -1; |
661 } | 762 } |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
723 size_t buffer_size, float sample_rate) const { | 824 size_t buffer_size, float sample_rate) const { |
724 const int samples_per_sec = static_cast<int>(sample_rate); | 825 const int samples_per_sec = static_cast<int>(sample_rate); |
725 const int samples_per_10_msec = (samples_per_sec / 100); | 826 const int samples_per_10_msec = (samples_per_sec / 100); |
726 bool size_is_valid = (((buffer_size % samples_per_10_msec) == 0) && | 827 bool size_is_valid = (((buffer_size % samples_per_10_msec) == 0) && |
727 (buffer_size <= kMaxBufferSize)); | 828 (buffer_size <= kMaxBufferSize)); |
728 DLOG_IF(WARNING, !size_is_valid) << "Size of buffer must be and even " | 829 DLOG_IF(WARNING, !size_is_valid) << "Size of buffer must be and even " |
729 << "multiple of 10 ms and less than " | 830 << "multiple of 10 ms and less than " |
730 << kMaxBufferSize; | 831 << kMaxBufferSize; |
731 return size_is_valid; | 832 return size_is_valid; |
732 } | 833 } |
OLD | NEW |