1 /* 2 * libjingle 3 * Copyright 2012, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "talk/app/webrtc/test/fakeaudiocapturemodule.h" 29 30 #include "talk/base/common.h" 31 #include "talk/base/refcount.h" 32 #include "talk/base/thread.h" 33 #include "talk/base/timeutils.h" 34 35 // Audio sample value that is high enough that it doesn't occur naturally when 36 // frames are being faked. E.g. NetEq will not generate this large sample value 37 // unless it has received an audio frame containing a sample of this value. 38 // Even simpler buffers would likely just contain audio sample values of 0. 39 static const int kHighSampleValue = 10000; 40 41 // Same value as src/modules/audio_device/main/source/audio_device_config.h in 42 // https://code.google.com/p/webrtc/ 43 static const uint32 kAdmMaxIdleTimeProcess = 1000; 44 45 // Constants here are derived by running VoE using a real ADM. 46 // The constants correspond to 10ms of mono audio at 44kHz. 47 static const int kTimePerFrameMs = 10; 48 static const int kNumberOfChannels = 1; 49 static const int kSamplesPerSecond = 44000; 50 static const int kTotalDelayMs = 0; 51 static const int kClockDriftMs = 0; 52 static const uint32_t kMaxVolume = 14392; 53 54 enum { 55 MSG_RUN_PROCESS, 56 MSG_STOP_PROCESS, 57 }; 58 59 FakeAudioCaptureModule::FakeAudioCaptureModule( 60 talk_base::Thread* process_thread) 61 : last_process_time_ms_(0), 62 audio_callback_(NULL), 63 recording_(false), 64 playing_(false), 65 play_is_initialized_(false), 66 rec_is_initialized_(false), 67 current_mic_level_(kMaxVolume), 68 started_(false), 69 next_frame_time_(0), 70 process_thread_(process_thread), 71 frames_received_(0) { 72 } 73 74 FakeAudioCaptureModule::~FakeAudioCaptureModule() { 75 // Ensure that thread stops calling ProcessFrame(). 76 process_thread_->Send(this, MSG_STOP_PROCESS); 77 } 78 79 talk_base::scoped_refptr<FakeAudioCaptureModule> FakeAudioCaptureModule::Create( 80 talk_base::Thread* process_thread) { 81 if (process_thread == NULL) return NULL; 82 83 talk_base::scoped_refptr<FakeAudioCaptureModule> capture_module( 84 new talk_base::RefCountedObject<FakeAudioCaptureModule>(process_thread)); 85 if (!capture_module->Initialize()) { 86 return NULL; 87 } 88 return capture_module; 89 } 90 91 int FakeAudioCaptureModule::frames_received() const { 92 return frames_received_; 93 } 94 95 int32_t FakeAudioCaptureModule::Version(char* /*version*/, 96 uint32_t& /*remaining_buffer_in_bytes*/, 97 uint32_t& /*position*/) const { 98 ASSERT(false); 99 return 0; 100 } 101 102 int32_t FakeAudioCaptureModule::TimeUntilNextProcess() { 103 const uint32 current_time = talk_base::Time(); 104 if (current_time < last_process_time_ms_) { 105 // TODO: wraparound could be handled more gracefully. 106 return 0; 107 } 108 const uint32 elapsed_time = current_time - last_process_time_ms_; 109 if (kAdmMaxIdleTimeProcess < elapsed_time) { 110 return 0; 111 } 112 return kAdmMaxIdleTimeProcess - elapsed_time; 113 } 114 115 int32_t FakeAudioCaptureModule::Process() { 116 last_process_time_ms_ = talk_base::Time(); 117 return 0; 118 } 119 120 int32_t FakeAudioCaptureModule::ChangeUniqueId(const int32_t /*id*/) { 121 ASSERT(false); 122 return 0; 123 } 124 125 int32_t FakeAudioCaptureModule::ActiveAudioLayer( 126 AudioLayer* /*audio_layer*/) const { 127 ASSERT(false); 128 return 0; 129 } 130 131 webrtc::AudioDeviceModule::ErrorCode FakeAudioCaptureModule::LastError() const { 132 ASSERT(false); 133 return webrtc::AudioDeviceModule::kAdmErrNone; 134 } 135 136 int32_t FakeAudioCaptureModule::RegisterEventObserver( 137 webrtc::AudioDeviceObserver* /*event_callback*/) { 138 // Only used to report warnings and errors. This fake implementation won't 139 // generate any so discard this callback. 140 return 0; 141 } 142 143 int32_t FakeAudioCaptureModule::RegisterAudioCallback( 144 webrtc::AudioTransport* audio_callback) { 145 audio_callback_ = audio_callback; 146 return 0; 147 } 148 149 int32_t FakeAudioCaptureModule::Init() { 150 // Initialize is called by the factory method. Safe to ignore this Init call. 151 return 0; 152 } 153 154 int32_t FakeAudioCaptureModule::Terminate() { 155 // Clean up in the destructor. No action here, just success. 156 return 0; 157 } 158 159 bool FakeAudioCaptureModule::Initialized() const { 160 ASSERT(false); 161 return 0; 162 } 163 164 int16_t FakeAudioCaptureModule::PlayoutDevices() { 165 ASSERT(false); 166 return 0; 167 } 168 169 int16_t FakeAudioCaptureModule::RecordingDevices() { 170 ASSERT(false); 171 return 0; 172 } 173 174 int32_t FakeAudioCaptureModule::PlayoutDeviceName( 175 uint16_t /*index*/, 176 char /*name*/[webrtc::kAdmMaxDeviceNameSize], 177 char /*guid*/[webrtc::kAdmMaxGuidSize]) { 178 ASSERT(false); 179 return 0; 180 } 181 182 int32_t FakeAudioCaptureModule::RecordingDeviceName( 183 uint16_t /*index*/, 184 char /*name*/[webrtc::kAdmMaxDeviceNameSize], 185 char /*guid*/[webrtc::kAdmMaxGuidSize]) { 186 ASSERT(false); 187 return 0; 188 } 189 190 int32_t FakeAudioCaptureModule::SetPlayoutDevice(uint16_t /*index*/) { 191 // No playout device, just playing from file. Return success. 192 return 0; 193 } 194 195 int32_t FakeAudioCaptureModule::SetPlayoutDevice(WindowsDeviceType /*device*/) { 196 if (play_is_initialized_) { 197 return -1; 198 } 199 return 0; 200 } 201 202 int32_t FakeAudioCaptureModule::SetRecordingDevice(uint16_t /*index*/) { 203 // No recording device, just dropping audio. Return success. 204 return 0; 205 } 206 207 int32_t FakeAudioCaptureModule::SetRecordingDevice( 208 WindowsDeviceType /*device*/) { 209 if (rec_is_initialized_) { 210 return -1; 211 } 212 return 0; 213 } 214 215 int32_t FakeAudioCaptureModule::PlayoutIsAvailable(bool* /*available*/) { 216 ASSERT(false); 217 return 0; 218 } 219 220 int32_t FakeAudioCaptureModule::InitPlayout() { 221 play_is_initialized_ = true; 222 return 0; 223 } 224 225 bool FakeAudioCaptureModule::PlayoutIsInitialized() const { 226 return play_is_initialized_; 227 } 228 229 int32_t FakeAudioCaptureModule::RecordingIsAvailable(bool* /*available*/) { 230 ASSERT(false); 231 return 0; 232 } 233 234 int32_t FakeAudioCaptureModule::InitRecording() { 235 rec_is_initialized_ = true; 236 return 0; 237 } 238 239 bool FakeAudioCaptureModule::RecordingIsInitialized() const { 240 ASSERT(false); 241 return 0; 242 } 243 244 int32_t FakeAudioCaptureModule::StartPlayout() { 245 if (!play_is_initialized_) { 246 return -1; 247 } 248 playing_ = true; 249 UpdateProcessing(); 250 return 0; 251 } 252 253 int32_t FakeAudioCaptureModule::StopPlayout() { 254 playing_ = false; 255 UpdateProcessing(); 256 return 0; 257 } 258 259 bool FakeAudioCaptureModule::Playing() const { 260 return playing_; 261 } 262 263 int32_t FakeAudioCaptureModule::StartRecording() { 264 if (!rec_is_initialized_) { 265 return -1; 266 } 267 recording_ = true; 268 UpdateProcessing(); 269 return 0; 270 } 271 272 int32_t FakeAudioCaptureModule::StopRecording() { 273 recording_ = false; 274 UpdateProcessing(); 275 return 0; 276 } 277 278 bool FakeAudioCaptureModule::Recording() const { 279 return recording_; 280 } 281 282 int32_t FakeAudioCaptureModule::SetAGC(bool /*enable*/) { 283 // No AGC but not needed since audio is pregenerated. Return success. 284 return 0; 285 } 286 287 bool FakeAudioCaptureModule::AGC() const { 288 ASSERT(false); 289 return 0; 290 } 291 292 int32_t FakeAudioCaptureModule::SetWaveOutVolume(uint16_t /*volume_left*/, 293 uint16_t /*volume_right*/) { 294 ASSERT(false); 295 return 0; 296 } 297 298 int32_t FakeAudioCaptureModule::WaveOutVolume( 299 uint16_t* /*volume_left*/, 300 uint16_t* /*volume_right*/) const { 301 ASSERT(false); 302 return 0; 303 } 304 305 int32_t FakeAudioCaptureModule::SpeakerIsAvailable(bool* available) { 306 // No speaker, just dropping audio. Return success. 307 *available = true; 308 return 0; 309 } 310 311 int32_t FakeAudioCaptureModule::InitSpeaker() { 312 // No speaker, just playing from file. Return success. 313 return 0; 314 } 315 316 bool FakeAudioCaptureModule::SpeakerIsInitialized() const { 317 ASSERT(false); 318 return 0; 319 } 320 321 int32_t FakeAudioCaptureModule::MicrophoneIsAvailable(bool* available) { 322 // No microphone, just playing from file. Return success. 323 *available = true; 324 return 0; 325 } 326 327 int32_t FakeAudioCaptureModule::InitMicrophone() { 328 // No microphone, just playing from file. Return success. 329 return 0; 330 } 331 332 bool FakeAudioCaptureModule::MicrophoneIsInitialized() const { 333 ASSERT(false); 334 return 0; 335 } 336 337 int32_t FakeAudioCaptureModule::SpeakerVolumeIsAvailable(bool* /*available*/) { 338 ASSERT(false); 339 return 0; 340 } 341 342 int32_t FakeAudioCaptureModule::SetSpeakerVolume(uint32_t /*volume*/) { 343 ASSERT(false); 344 return 0; 345 } 346 347 int32_t FakeAudioCaptureModule::SpeakerVolume(uint32_t* /*volume*/) const { 348 ASSERT(false); 349 return 0; 350 } 351 352 int32_t FakeAudioCaptureModule::MaxSpeakerVolume( 353 uint32_t* /*max_volume*/) const { 354 ASSERT(false); 355 return 0; 356 } 357 358 int32_t FakeAudioCaptureModule::MinSpeakerVolume( 359 uint32_t* /*min_volume*/) const { 360 ASSERT(false); 361 return 0; 362 } 363 364 int32_t FakeAudioCaptureModule::SpeakerVolumeStepSize( 365 uint16_t* /*step_size*/) const { 366 ASSERT(false); 367 return 0; 368 } 369 370 int32_t FakeAudioCaptureModule::MicrophoneVolumeIsAvailable( 371 bool* /*available*/) { 372 ASSERT(false); 373 return 0; 374 } 375 376 int32_t FakeAudioCaptureModule::SetMicrophoneVolume(uint32_t /*volume*/) { 377 ASSERT(false); 378 return 0; 379 } 380 381 int32_t FakeAudioCaptureModule::MicrophoneVolume(uint32_t* volume) const { 382 *volume = current_mic_level_; 383 return 0; 384 } 385 386 int32_t FakeAudioCaptureModule::MaxMicrophoneVolume( 387 uint32_t* max_volume) const { 388 *max_volume = kMaxVolume; 389 return 0; 390 } 391 392 int32_t FakeAudioCaptureModule::MinMicrophoneVolume( 393 uint32_t* /*min_volume*/) const { 394 ASSERT(false); 395 return 0; 396 } 397 398 int32_t FakeAudioCaptureModule::MicrophoneVolumeStepSize( 399 uint16_t* /*step_size*/) const { 400 ASSERT(false); 401 return 0; 402 } 403 404 int32_t FakeAudioCaptureModule::SpeakerMuteIsAvailable(bool* /*available*/) { 405 ASSERT(false); 406 return 0; 407 } 408 409 int32_t FakeAudioCaptureModule::SetSpeakerMute(bool /*enable*/) { 410 ASSERT(false); 411 return 0; 412 } 413 414 int32_t FakeAudioCaptureModule::SpeakerMute(bool* /*enabled*/) const { 415 ASSERT(false); 416 return 0; 417 } 418 419 int32_t FakeAudioCaptureModule::MicrophoneMuteIsAvailable(bool* /*available*/) { 420 ASSERT(false); 421 return 0; 422 } 423 424 int32_t FakeAudioCaptureModule::SetMicrophoneMute(bool /*enable*/) { 425 ASSERT(false); 426 return 0; 427 } 428 429 int32_t FakeAudioCaptureModule::MicrophoneMute(bool* /*enabled*/) const { 430 ASSERT(false); 431 return 0; 432 } 433 434 int32_t FakeAudioCaptureModule::MicrophoneBoostIsAvailable( 435 bool* /*available*/) { 436 ASSERT(false); 437 return 0; 438 } 439 440 int32_t FakeAudioCaptureModule::SetMicrophoneBoost(bool /*enable*/) { 441 ASSERT(false); 442 return 0; 443 } 444 445 int32_t FakeAudioCaptureModule::MicrophoneBoost(bool* /*enabled*/) const { 446 ASSERT(false); 447 return 0; 448 } 449 450 int32_t FakeAudioCaptureModule::StereoPlayoutIsAvailable( 451 bool* available) const { 452 // No recording device, just dropping audio. Stereo can be dropped just 453 // as easily as mono. 454 *available = true; 455 return 0; 456 } 457 458 int32_t FakeAudioCaptureModule::SetStereoPlayout(bool /*enable*/) { 459 // No recording device, just dropping audio. Stereo can be dropped just 460 // as easily as mono. 461 return 0; 462 } 463 464 int32_t FakeAudioCaptureModule::StereoPlayout(bool* /*enabled*/) const { 465 ASSERT(false); 466 return 0; 467 } 468 469 int32_t FakeAudioCaptureModule::StereoRecordingIsAvailable( 470 bool* available) const { 471 // Keep thing simple. No stereo recording. 472 *available = false; 473 return 0; 474 } 475 476 int32_t FakeAudioCaptureModule::SetStereoRecording(bool enable) { 477 if (!enable) { 478 return 0; 479 } 480 return -1; 481 } 482 483 int32_t FakeAudioCaptureModule::StereoRecording(bool* /*enabled*/) const { 484 ASSERT(false); 485 return 0; 486 } 487 488 int32_t FakeAudioCaptureModule::SetRecordingChannel( 489 const ChannelType channel) { 490 if (channel != AudioDeviceModule::kChannelBoth) { 491 // There is no right or left in mono. I.e. kChannelBoth should be used for 492 // mono. 493 ASSERT(false); 494 return -1; 495 } 496 return 0; 497 } 498 499 int32_t FakeAudioCaptureModule::RecordingChannel(ChannelType* channel) const { 500 // Stereo recording not supported. However, WebRTC ADM returns kChannelBoth 501 // in that case. Do the same here. 502 *channel = AudioDeviceModule::kChannelBoth; 503 return 0; 504 } 505 506 int32_t FakeAudioCaptureModule::SetPlayoutBuffer(const BufferType /*type*/, 507 uint16_t /*size_ms*/) { 508 ASSERT(false); 509 return 0; 510 } 511 512 int32_t FakeAudioCaptureModule::PlayoutBuffer(BufferType* /*type*/, 513 uint16_t* /*size_ms*/) const { 514 ASSERT(false); 515 return 0; 516 } 517 518 int32_t FakeAudioCaptureModule::PlayoutDelay(uint16_t* delay_ms) const { 519 // No delay since audio frames are dropped. 520 *delay_ms = 0; 521 return 0; 522 } 523 524 int32_t FakeAudioCaptureModule::RecordingDelay(uint16_t* /*delay_ms*/) const { 525 ASSERT(false); 526 return 0; 527 } 528 529 int32_t FakeAudioCaptureModule::CPULoad(uint16_t* /*load*/) const { 530 ASSERT(false); 531 return 0; 532 } 533 534 int32_t FakeAudioCaptureModule::StartRawOutputFileRecording( 535 const char /*pcm_file_name_utf8*/[webrtc::kAdmMaxFileNameSize]) { 536 ASSERT(false); 537 return 0; 538 } 539 540 int32_t FakeAudioCaptureModule::StopRawOutputFileRecording() { 541 ASSERT(false); 542 return 0; 543 } 544 545 int32_t FakeAudioCaptureModule::StartRawInputFileRecording( 546 const char /*pcm_file_name_utf8*/[webrtc::kAdmMaxFileNameSize]) { 547 ASSERT(false); 548 return 0; 549 } 550 551 int32_t FakeAudioCaptureModule::StopRawInputFileRecording() { 552 ASSERT(false); 553 return 0; 554 } 555 556 int32_t FakeAudioCaptureModule::SetRecordingSampleRate( 557 const uint32_t /*samples_per_sec*/) { 558 ASSERT(false); 559 return 0; 560 } 561 562 int32_t FakeAudioCaptureModule::RecordingSampleRate( 563 uint32_t* /*samples_per_sec*/) const { 564 ASSERT(false); 565 return 0; 566 } 567 568 int32_t FakeAudioCaptureModule::SetPlayoutSampleRate( 569 const uint32_t /*samples_per_sec*/) { 570 ASSERT(false); 571 return 0; 572 } 573 574 int32_t FakeAudioCaptureModule::PlayoutSampleRate( 575 uint32_t* /*samples_per_sec*/) const { 576 ASSERT(false); 577 return 0; 578 } 579 580 int32_t FakeAudioCaptureModule::ResetAudioDevice() { 581 ASSERT(false); 582 return 0; 583 } 584 585 int32_t FakeAudioCaptureModule::SetLoudspeakerStatus(bool /*enable*/) { 586 ASSERT(false); 587 return 0; 588 } 589 590 int32_t FakeAudioCaptureModule::GetLoudspeakerStatus(bool* /*enabled*/) const { 591 ASSERT(false); 592 return 0; 593 } 594 595 void FakeAudioCaptureModule::OnMessage(talk_base::Message* msg) { 596 switch (msg->message_id) { 597 case MSG_RUN_PROCESS: 598 ProcessFrameP(); 599 break; 600 case MSG_STOP_PROCESS: 601 StopProcessP(); 602 break; 603 default: 604 // All existing messages should be caught. Getting here should never 605 // happen. 606 ASSERT(false); 607 } 608 } 609 610 bool FakeAudioCaptureModule::Initialize() { 611 // Set the send buffer samples high enough that it would not occur on the 612 // remote side unless a packet containing a sample of that magnitude has been 613 // sent to it. Note that the audio processing pipeline will likely distort the 614 // original signal. 615 SetSendBuffer(kHighSampleValue); 616 last_process_time_ms_ = talk_base::Time(); 617 return true; 618 } 619 620 void FakeAudioCaptureModule::SetSendBuffer(int value) { 621 Sample* buffer_ptr = reinterpret_cast<Sample*>(send_buffer_); 622 const int buffer_size_in_samples = sizeof(send_buffer_) / 623 kNumberBytesPerSample; 624 for (int i = 0; i < buffer_size_in_samples; ++i) { 625 buffer_ptr[i] = value; 626 } 627 } 628 629 void FakeAudioCaptureModule::ResetRecBuffer() { 630 memset(rec_buffer_, 0, sizeof(rec_buffer_)); 631 } 632 633 bool FakeAudioCaptureModule::CheckRecBuffer(int value) { 634 const Sample* buffer_ptr = reinterpret_cast<const Sample*>(rec_buffer_); 635 const int buffer_size_in_samples = sizeof(rec_buffer_) / 636 kNumberBytesPerSample; 637 for (int i = 0; i < buffer_size_in_samples; ++i) { 638 if (buffer_ptr[i] >= value) return true; 639 } 640 return false; 641 } 642 643 void FakeAudioCaptureModule::UpdateProcessing() { 644 const bool process = recording_ || playing_; 645 if (process) { 646 if (started_) { 647 // Already started. 648 return; 649 } 650 process_thread_->Post(this, MSG_RUN_PROCESS); 651 } else { 652 process_thread_->Send(this, MSG_STOP_PROCESS); 653 } 654 } 655 656 void FakeAudioCaptureModule::ProcessFrameP() { 657 ASSERT(talk_base::Thread::Current() == process_thread_); 658 if (!started_) { 659 next_frame_time_ = talk_base::Time(); 660 started_ = true; 661 } 662 // Receive and send frames every kTimePerFrameMs. 663 if (audio_callback_ != NULL) { 664 if (playing_) { 665 ReceiveFrameP(); 666 } 667 if (recording_) { 668 SendFrameP(); 669 } 670 } 671 672 next_frame_time_ += kTimePerFrameMs; 673 const uint32 current_time = talk_base::Time(); 674 const uint32 wait_time = (next_frame_time_ > current_time) ? 675 next_frame_time_ - current_time : 0; 676 process_thread_->PostDelayed(wait_time, this, MSG_RUN_PROCESS); 677 } 678 679 void FakeAudioCaptureModule::ReceiveFrameP() { 680 ASSERT(talk_base::Thread::Current() == process_thread_); 681 ResetRecBuffer(); 682 uint32_t nSamplesOut = 0; 683 if (audio_callback_->NeedMorePlayData(kNumberSamples, kNumberBytesPerSample, 684 kNumberOfChannels, kSamplesPerSecond, 685 rec_buffer_, nSamplesOut) != 0) { 686 ASSERT(false); 687 } 688 ASSERT(nSamplesOut == kNumberSamples); 689 // The SetBuffer() function ensures that after decoding, the audio buffer 690 // should contain samples of similar magnitude (there is likely to be some 691 // distortion due to the audio pipeline). If one sample is detected to 692 // have the same or greater magnitude somewhere in the frame, an actual frame 693 // has been received from the remote side (i.e. faked frames are not being 694 // pulled). 695 if (CheckRecBuffer(kHighSampleValue)) ++frames_received_; 696 } 697 698 void FakeAudioCaptureModule::SendFrameP() { 699 ASSERT(talk_base::Thread::Current() == process_thread_); 700 bool key_pressed = false; 701 if (audio_callback_->RecordedDataIsAvailable(send_buffer_, kNumberSamples, 702 kNumberBytesPerSample, 703 kNumberOfChannels, 704 kSamplesPerSecond, kTotalDelayMs, 705 kClockDriftMs, current_mic_level_, 706 key_pressed, 707 current_mic_level_) != 0) { 708 ASSERT(false); 709 } 710 } 711 712 void FakeAudioCaptureModule::StopProcessP() { 713 ASSERT(talk_base::Thread::Current() == process_thread_); 714 started_ = false; 715 process_thread_->Clear(this); 716 } 717