1 /* 2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 /* 12 * Android audio device implementation (JNI/AudioRecord usage) 13 */ 14 15 // TODO(xians): Break out attach and detach current thread to JVM to 16 // separate functions. 17 18 #include "webrtc/modules/audio_device/android/audio_record_jni.h" 19 20 #include <android/log.h> 21 #include <stdlib.h> 22 23 #include "webrtc/modules/audio_device/android/audio_common.h" 24 #include "webrtc/modules/audio_device/audio_device_config.h" 25 #include "webrtc/modules/audio_device/audio_device_utility.h" 26 27 #include "webrtc/system_wrappers/interface/event_wrapper.h" 28 #include "webrtc/system_wrappers/interface/thread_wrapper.h" 29 #include "webrtc/system_wrappers/interface/trace.h" 30 31 namespace webrtc { 32 33 JavaVM* AudioRecordJni::globalJvm = NULL; 34 JNIEnv* AudioRecordJni::globalJNIEnv = NULL; 35 jobject AudioRecordJni::globalContext = NULL; 36 jclass AudioRecordJni::globalScClass = NULL; 37 38 int32_t AudioRecordJni::SetAndroidAudioDeviceObjects(void* javaVM, void* env, 39 void* context) { 40 assert(env); 41 globalJvm = reinterpret_cast<JavaVM*>(javaVM); 42 globalJNIEnv = reinterpret_cast<JNIEnv*>(env); 43 // Get java class type (note path to class packet). 44 jclass javaScClassLocal = globalJNIEnv->FindClass( 45 "org/webrtc/voiceengine/WebRtcAudioRecord"); 46 if (!javaScClassLocal) { 47 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, 48 "%s: could not find java class", __FUNCTION__); 49 return -1; // exception thrown 50 } 51 52 // Create a global reference to the class (to tell JNI that we are 53 // referencing it after this function has returned). 54 globalScClass = reinterpret_cast<jclass> ( 55 globalJNIEnv->NewGlobalRef(javaScClassLocal)); 56 if (!globalScClass) { 57 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, 58 "%s: could not create reference", __FUNCTION__); 59 return -1; 60 } 61 62 globalContext = globalJNIEnv->NewGlobalRef( 63 reinterpret_cast<jobject>(context)); 64 if (!globalContext) { 65 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, 66 "%s: could not create context reference", __FUNCTION__); 67 return -1; 68 } 69 70 // Delete local class ref, we only use the global ref 71 globalJNIEnv->DeleteLocalRef(javaScClassLocal); 72 73 return 0; 74 } 75 76 void AudioRecordJni::ClearAndroidAudioDeviceObjects() { 77 WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, -1, 78 "%s: env is NULL, assuming deinit", __FUNCTION__); 79 80 globalJvm = NULL;; 81 if (!globalJNIEnv) { 82 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, -1, 83 "%s: saved env already NULL", __FUNCTION__); 84 return; 85 } 86 87 globalJNIEnv->DeleteGlobalRef(globalContext); 88 globalContext = reinterpret_cast<jobject>(NULL); 89 90 globalJNIEnv->DeleteGlobalRef(globalScClass); 91 globalScClass = reinterpret_cast<jclass>(NULL); 92 93 globalJNIEnv = reinterpret_cast<JNIEnv*>(NULL); 94 } 95 96 AudioRecordJni::AudioRecordJni( 97 const int32_t id, PlayoutDelayProvider* delay_provider) 98 : _javaVM(NULL), 99 _jniEnvRec(NULL), 100 _javaScClass(0), 101 _javaScObj(0), 102 _javaRecBuffer(0), 103 _javaDirectRecBuffer(NULL), 104 _javaMidRecAudio(0), 105 _ptrAudioBuffer(NULL), 106 _critSect(*CriticalSectionWrapper::CreateCriticalSection()), 107 _id(id), 108 _delay_provider(delay_provider), 109 _initialized(false), 110 _timeEventRec(*EventWrapper::Create()), 111 _recStartStopEvent(*EventWrapper::Create()), 112 _ptrThreadRec(NULL), 113 _recThreadID(0), 114 _recThreadIsInitialized(false), 115 _shutdownRecThread(false), 116 _recordingDeviceIsSpecified(false), 117 _recording(false), 118 _recIsInitialized(false), 119 _micIsInitialized(false), 120 _startRec(false), 121 _recWarning(0), 122 _recError(0), 123 _delayRecording(0), 124 _AGC(false), 125 _samplingFreqIn((N_REC_SAMPLES_PER_SEC/1000)), 126 _recAudioSource(1) { // 1 is AudioSource.MIC which is our default 127 memset(_recBuffer, 0, sizeof(_recBuffer)); 128 } 129 130 AudioRecordJni::~AudioRecordJni() { 131 WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, 132 "%s destroyed", __FUNCTION__); 133 134 Terminate(); 135 136 delete &_recStartStopEvent; 137 delete &_timeEventRec; 138 delete &_critSect; 139 } 140 141 int32_t AudioRecordJni::Init() { 142 CriticalSectionScoped lock(&_critSect); 143 144 if (_initialized) 145 { 146 return 0; 147 } 148 149 _recWarning = 0; 150 _recError = 0; 151 152 // Init Java member variables 153 // and set up JNI interface to 154 // AudioDeviceAndroid java class 155 if (InitJavaResources() != 0) 156 { 157 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 158 "%s: Failed to init Java resources", __FUNCTION__); 159 return -1; 160 } 161 162 // Check the sample rate to be used for playback and recording 163 // and the max playout volume 164 if (InitSampleRate() != 0) 165 { 166 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 167 "%s: Failed to init samplerate", __FUNCTION__); 168 return -1; 169 } 170 171 const char* threadName = "jni_audio_capture_thread"; 172 _ptrThreadRec = ThreadWrapper::CreateThread(RecThreadFunc, this, 173 kRealtimePriority, threadName); 174 if (_ptrThreadRec == NULL) 175 { 176 WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, 177 " failed to create the rec audio thread"); 178 return -1; 179 } 180 181 unsigned int threadID(0); 182 if (!_ptrThreadRec->Start(threadID)) 183 { 184 WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id, 185 " failed to start the rec audio thread"); 186 delete _ptrThreadRec; 187 _ptrThreadRec = NULL; 188 return -1; 189 } 190 _recThreadID = threadID; 191 _initialized = true; 192 193 return 0; 194 } 195 196 int32_t AudioRecordJni::Terminate() { 197 CriticalSectionScoped lock(&_critSect); 198 199 if (!_initialized) 200 { 201 return 0; 202 } 203 204 StopRecording(); 205 _shutdownRecThread = true; 206 _timeEventRec.Set(); // Release rec thread from waiting state 207 if (_ptrThreadRec) 208 { 209 // First, the thread must detach itself from Java VM 210 _critSect.Leave(); 211 if (kEventSignaled != _recStartStopEvent.Wait(5000)) 212 { 213 WEBRTC_TRACE( 214 kTraceError, 215 kTraceAudioDevice, 216 _id, 217 "%s: Recording thread shutdown timed out, cannot " 218 "terminate thread", 219 __FUNCTION__); 220 // If we close thread anyway, the app will crash 221 return -1; 222 } 223 _recStartStopEvent.Reset(); 224 _critSect.Enter(); 225 226 // Close down rec thread 227 ThreadWrapper* tmpThread = _ptrThreadRec; 228 _ptrThreadRec = NULL; 229 _critSect.Leave(); 230 tmpThread->SetNotAlive(); 231 // Release again, we might have returned to waiting state 232 _timeEventRec.Set(); 233 if (tmpThread->Stop()) 234 { 235 delete tmpThread; 236 _jniEnvRec = NULL; 237 } 238 else 239 { 240 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 241 " failed to close down the rec audio thread"); 242 } 243 _critSect.Enter(); 244 245 _recThreadIsInitialized = false; 246 } 247 _micIsInitialized = false; 248 _recordingDeviceIsSpecified = false; 249 250 // get the JNI env for this thread 251 JNIEnv *env; 252 bool isAttached = false; 253 254 // get the JNI env for this thread 255 if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) 256 { 257 // try to attach the thread and get the env 258 // Attach this thread to JVM 259 jint res = _javaVM->AttachCurrentThread(&env, NULL); 260 if ((res < 0) || !env) 261 { 262 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 263 "%s: Could not attach thread to JVM (%d, %p)", 264 __FUNCTION__, res, env); 265 return -1; 266 } 267 isAttached = true; 268 } 269 270 // Make method IDs and buffer pointers unusable 271 _javaMidRecAudio = 0; 272 _javaDirectRecBuffer = NULL; 273 274 // Delete the references to the java buffers, this allows the 275 // garbage collector to delete them 276 env->DeleteGlobalRef(_javaRecBuffer); 277 _javaRecBuffer = 0; 278 279 // Delete the references to the java object and class, this allows the 280 // garbage collector to delete them 281 env->DeleteGlobalRef(_javaScObj); 282 _javaScObj = 0; 283 _javaScClass = 0; 284 285 // Detach this thread if it was attached 286 if (isAttached) 287 { 288 if (_javaVM->DetachCurrentThread() < 0) 289 { 290 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 291 "%s: Could not detach thread from JVM", __FUNCTION__); 292 } 293 } 294 295 _initialized = false; 296 297 return 0; 298 } 299 300 int32_t AudioRecordJni::RecordingDeviceName(uint16_t index, 301 char name[kAdmMaxDeviceNameSize], 302 char guid[kAdmMaxGuidSize]) { 303 if (0 != index) 304 { 305 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 306 " Device index is out of range [0,0]"); 307 return -1; 308 } 309 310 // Return empty string 311 memset(name, 0, kAdmMaxDeviceNameSize); 312 313 if (guid) 314 { 315 memset(guid, 0, kAdmMaxGuidSize); 316 } 317 318 return 0; 319 } 320 321 int32_t AudioRecordJni::SetRecordingDevice(uint16_t index) { 322 if (_recIsInitialized) 323 { 324 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 325 " Recording already initialized"); 326 return -1; 327 } 328 329 // Recording device index will be used for specifying recording 330 // audio source, allow any value 331 _recAudioSource = index; 332 _recordingDeviceIsSpecified = true; 333 334 return 0; 335 } 336 337 int32_t AudioRecordJni::SetRecordingDevice( 338 AudioDeviceModule::WindowsDeviceType device) { 339 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 340 " API call not supported on this platform"); 341 return -1; 342 } 343 344 int32_t AudioRecordJni::RecordingIsAvailable(bool& available) { // NOLINT 345 available = false; 346 347 // Try to initialize the playout side 348 int32_t res = InitRecording(); 349 350 // Cancel effect of initialization 351 StopRecording(); 352 353 if (res != -1) 354 { 355 available = true; 356 } 357 358 return res; 359 } 360 361 int32_t AudioRecordJni::InitRecording() { 362 CriticalSectionScoped lock(&_critSect); 363 364 if (!_initialized) 365 { 366 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 367 " Not initialized"); 368 return -1; 369 } 370 371 if (_recording) 372 { 373 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 374 " Recording already started"); 375 return -1; 376 } 377 378 if (!_recordingDeviceIsSpecified) 379 { 380 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 381 " Recording device is not specified"); 382 return -1; 383 } 384 385 if (_recIsInitialized) 386 { 387 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 388 " Recording already initialized"); 389 return 0; 390 } 391 392 // Initialize the microphone 393 if (InitMicrophone() == -1) 394 { 395 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 396 " InitMicrophone() failed"); 397 } 398 399 // get the JNI env for this thread 400 JNIEnv *env; 401 bool isAttached = false; 402 403 // get the JNI env for this thread 404 if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) 405 { 406 // try to attach the thread and get the env 407 // Attach this thread to JVM 408 jint res = _javaVM->AttachCurrentThread(&env, NULL); 409 if ((res < 0) || !env) 410 { 411 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 412 " Could not attach thread to JVM (%d, %p)", res, env); 413 return -1; 414 } 415 isAttached = true; 416 } 417 418 // get the method ID 419 jmethodID initRecordingID = env->GetMethodID(_javaScClass, "InitRecording", 420 "(II)I"); 421 422 int samplingFreq = 44100; 423 if (_samplingFreqIn != 44) 424 { 425 samplingFreq = _samplingFreqIn * 1000; 426 } 427 428 int retVal = -1; 429 430 // call java sc object method 431 jint res = env->CallIntMethod(_javaScObj, initRecordingID, _recAudioSource, 432 samplingFreq); 433 if (res < 0) 434 { 435 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 436 "InitRecording failed (%d)", res); 437 } 438 else 439 { 440 // Set the audio device buffer sampling rate 441 _ptrAudioBuffer->SetRecordingSampleRate(_samplingFreqIn * 1000); 442 443 // the init rec function returns a fixed delay 444 _delayRecording = res / _samplingFreqIn; 445 446 _recIsInitialized = true; 447 retVal = 0; 448 } 449 450 // Detach this thread if it was attached 451 if (isAttached) 452 { 453 if (_javaVM->DetachCurrentThread() < 0) 454 { 455 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 456 " Could not detach thread from JVM"); 457 } 458 } 459 460 return retVal; 461 } 462 463 int32_t AudioRecordJni::StartRecording() { 464 CriticalSectionScoped lock(&_critSect); 465 466 if (!_recIsInitialized) 467 { 468 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 469 " Recording not initialized"); 470 return -1; 471 } 472 473 if (_recording) 474 { 475 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 476 " Recording already started"); 477 return 0; 478 } 479 480 // get the JNI env for this thread 481 JNIEnv *env; 482 bool isAttached = false; 483 484 // get the JNI env for this thread 485 if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) 486 { 487 // try to attach the thread and get the env 488 // Attach this thread to JVM 489 jint res = _javaVM->AttachCurrentThread(&env, NULL); 490 if ((res < 0) || !env) 491 { 492 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 493 " Could not attach thread to JVM (%d, %p)", res, env); 494 return -1; 495 } 496 isAttached = true; 497 } 498 499 // get the method ID 500 jmethodID startRecordingID = env->GetMethodID(_javaScClass, 501 "StartRecording", "()I"); 502 503 // Call java sc object method 504 jint res = env->CallIntMethod(_javaScObj, startRecordingID); 505 if (res < 0) 506 { 507 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 508 "StartRecording failed (%d)", res); 509 return -1; 510 } 511 512 _recWarning = 0; 513 _recError = 0; 514 515 // Signal to recording thread that we want to start 516 _startRec = true; 517 _timeEventRec.Set(); // Release thread from waiting state 518 _critSect.Leave(); 519 // Wait for thread to init 520 if (kEventSignaled != _recStartStopEvent.Wait(5000)) 521 { 522 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 523 " Timeout or error starting"); 524 } 525 _recStartStopEvent.Reset(); 526 _critSect.Enter(); 527 528 // Detach this thread if it was attached 529 if (isAttached) 530 { 531 if (_javaVM->DetachCurrentThread() < 0) 532 { 533 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 534 " Could not detach thread from JVM"); 535 } 536 } 537 538 return 0; 539 540 } 541 542 int32_t AudioRecordJni::StopRecording() { 543 CriticalSectionScoped lock(&_critSect); 544 545 if (!_recIsInitialized) 546 { 547 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 548 " Recording is not initialized"); 549 return 0; 550 } 551 552 // make sure we don't start recording (it's asynchronous), 553 // assuming that we are under lock 554 _startRec = false; 555 556 // get the JNI env for this thread 557 JNIEnv *env; 558 bool isAttached = false; 559 560 // get the JNI env for this thread 561 if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) 562 { 563 // try to attach the thread and get the env 564 // Attach this thread to JVM 565 jint res = _javaVM->AttachCurrentThread(&env, NULL); 566 if ((res < 0) || !env) 567 { 568 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 569 " Could not attach thread to JVM (%d, %p)", res, env); 570 return -1; 571 } 572 isAttached = true; 573 } 574 575 // get the method ID 576 jmethodID stopRecordingID = env->GetMethodID(_javaScClass, "StopRecording", 577 "()I"); 578 579 // Call java sc object method 580 jint res = env->CallIntMethod(_javaScObj, stopRecordingID); 581 if (res < 0) 582 { 583 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 584 "StopRecording failed (%d)", res); 585 } 586 587 _recIsInitialized = false; 588 _recording = false; 589 _recWarning = 0; 590 _recError = 0; 591 592 // Detach this thread if it was attached 593 if (isAttached) 594 { 595 if (_javaVM->DetachCurrentThread() < 0) 596 { 597 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 598 " Could not detach thread from JVM"); 599 } 600 } 601 602 return 0; 603 604 } 605 606 int32_t AudioRecordJni::SetAGC(bool enable) { 607 _AGC = enable; 608 return 0; 609 } 610 611 int32_t AudioRecordJni::InitMicrophone() { 612 CriticalSectionScoped lock(&_critSect); 613 614 if (_recording) 615 { 616 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 617 " Recording already started"); 618 return -1; 619 } 620 621 if (!_recordingDeviceIsSpecified) 622 { 623 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 624 " Recording device is not specified"); 625 return -1; 626 } 627 628 // Nothing needs to be done here, we use a flag to have consistent 629 // behavior with other platforms 630 _micIsInitialized = true; 631 632 return 0; 633 } 634 635 int32_t AudioRecordJni::MicrophoneVolumeIsAvailable( 636 bool& available) { // NOLINT 637 available = false; // Mic volume not supported on Android 638 return 0; 639 } 640 641 int32_t AudioRecordJni::SetMicrophoneVolume( uint32_t /*volume*/) { 642 643 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 644 " API call not supported on this platform"); 645 return -1; 646 } 647 648 int32_t AudioRecordJni::MicrophoneVolume(uint32_t& volume) const { // NOLINT 649 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 650 " API call not supported on this platform"); 651 return -1; 652 } 653 654 int32_t AudioRecordJni::MaxMicrophoneVolume( 655 uint32_t& maxVolume) const { // NOLINT 656 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 657 " API call not supported on this platform"); 658 return -1; 659 } 660 661 int32_t AudioRecordJni::MinMicrophoneVolume( 662 uint32_t& minVolume) const { // NOLINT 663 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 664 " API call not supported on this platform"); 665 return -1; 666 } 667 668 int32_t AudioRecordJni::MicrophoneVolumeStepSize( 669 uint16_t& stepSize) const { 670 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 671 " API call not supported on this platform"); 672 return -1; 673 } 674 675 int32_t AudioRecordJni::MicrophoneMuteIsAvailable(bool& available) { // NOLINT 676 available = false; // Mic mute not supported on Android 677 return 0; 678 } 679 680 int32_t AudioRecordJni::SetMicrophoneMute(bool enable) { 681 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 682 " API call not supported on this platform"); 683 return -1; 684 } 685 686 int32_t AudioRecordJni::MicrophoneMute(bool& enabled) const { // NOLINT 687 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 688 " API call not supported on this platform"); 689 return -1; 690 } 691 692 int32_t AudioRecordJni::MicrophoneBoostIsAvailable(bool& available) { // NOLINT 693 available = false; // Mic boost not supported on Android 694 return 0; 695 } 696 697 int32_t AudioRecordJni::SetMicrophoneBoost(bool enable) { 698 if (!_micIsInitialized) 699 { 700 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 701 " Microphone not initialized"); 702 return -1; 703 } 704 705 if (enable) 706 { 707 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 708 " Enabling not available"); 709 return -1; 710 } 711 712 return 0; 713 } 714 715 int32_t AudioRecordJni::MicrophoneBoost(bool& enabled) const { // NOLINT 716 if (!_micIsInitialized) 717 { 718 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 719 " Microphone not initialized"); 720 return -1; 721 } 722 723 enabled = false; 724 725 return 0; 726 } 727 728 int32_t AudioRecordJni::StereoRecordingIsAvailable(bool& available) { // NOLINT 729 available = false; // Stereo recording not supported on Android 730 return 0; 731 } 732 733 int32_t AudioRecordJni::SetStereoRecording(bool enable) { 734 if (enable) 735 { 736 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 737 " Enabling not available"); 738 return -1; 739 } 740 741 return 0; 742 } 743 744 int32_t AudioRecordJni::StereoRecording(bool& enabled) const { // NOLINT 745 enabled = false; 746 return 0; 747 } 748 749 int32_t AudioRecordJni::RecordingDelay(uint16_t& delayMS) const { // NOLINT 750 delayMS = _delayRecording; 751 return 0; 752 } 753 754 bool AudioRecordJni::RecordingWarning() const { 755 return (_recWarning > 0); 756 } 757 758 bool AudioRecordJni::RecordingError() const { 759 return (_recError > 0); 760 } 761 762 void AudioRecordJni::ClearRecordingWarning() { 763 _recWarning = 0; 764 } 765 766 void AudioRecordJni::ClearRecordingError() { 767 _recError = 0; 768 } 769 770 void AudioRecordJni::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) { 771 CriticalSectionScoped lock(&_critSect); 772 _ptrAudioBuffer = audioBuffer; 773 // inform the AudioBuffer about default settings for this implementation 774 _ptrAudioBuffer->SetRecordingSampleRate(N_REC_SAMPLES_PER_SEC); 775 _ptrAudioBuffer->SetRecordingChannels(N_REC_CHANNELS); 776 } 777 778 int32_t AudioRecordJni::SetRecordingSampleRate(const uint32_t samplesPerSec) { 779 if (samplesPerSec > 48000 || samplesPerSec < 8000) 780 { 781 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 782 " Invalid sample rate"); 783 return -1; 784 } 785 786 // set the recording sample rate to use 787 if (samplesPerSec == 44100) 788 { 789 _samplingFreqIn = 44; 790 } 791 else 792 { 793 _samplingFreqIn = samplesPerSec / 1000; 794 } 795 796 // Update the AudioDeviceBuffer 797 _ptrAudioBuffer->SetRecordingSampleRate(samplesPerSec); 798 799 return 0; 800 } 801 802 int32_t AudioRecordJni::InitJavaResources() { 803 // todo: Check if we already have created the java object 804 _javaVM = globalJvm; 805 _javaScClass = globalScClass; 806 807 // use the jvm that has been set 808 if (!_javaVM) 809 { 810 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 811 "%s: Not a valid Java VM pointer", __FUNCTION__); 812 return -1; 813 } 814 815 // get the JNI env for this thread 816 JNIEnv *env; 817 bool isAttached = false; 818 819 // get the JNI env for this thread 820 if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) 821 { 822 // try to attach the thread and get the env 823 // Attach this thread to JVM 824 jint res = _javaVM->AttachCurrentThread(&env, NULL); 825 if ((res < 0) || !env) 826 { 827 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 828 "%s: Could not attach thread to JVM (%d, %p)", 829 __FUNCTION__, res, env); 830 return -1; 831 } 832 isAttached = true; 833 } 834 835 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 836 "get method id"); 837 838 // get the method ID for the void(void) constructor 839 jmethodID cid = env->GetMethodID(_javaScClass, "<init>", "()V"); 840 if (cid == NULL) 841 { 842 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 843 "%s: could not get constructor ID", __FUNCTION__); 844 return -1; /* exception thrown */ 845 } 846 847 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 848 "construct object", __FUNCTION__); 849 850 // construct the object 851 jobject javaScObjLocal = env->NewObject(_javaScClass, cid); 852 if (!javaScObjLocal) 853 { 854 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 855 "%s: could not create Java sc object", __FUNCTION__); 856 return -1; 857 } 858 859 // Create a reference to the object (to tell JNI that we are referencing it 860 // after this function has returned). 861 _javaScObj = env->NewGlobalRef(javaScObjLocal); 862 if (!_javaScObj) 863 { 864 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 865 "%s: could not create Java sc object reference", 866 __FUNCTION__); 867 return -1; 868 } 869 870 // Delete local object ref, we only use the global ref. 871 env->DeleteLocalRef(javaScObjLocal); 872 873 ////////////////////// 874 // AUDIO MANAGEMENT 875 876 // This is not mandatory functionality 877 if (globalContext) { 878 jfieldID context_id = env->GetFieldID(globalScClass, 879 "_context", 880 "Landroid/content/Context;"); 881 if (!context_id) { 882 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 883 "%s: could not get _context id", __FUNCTION__); 884 return -1; 885 } 886 887 env->SetObjectField(_javaScObj, context_id, globalContext); 888 jobject javaContext = env->GetObjectField(_javaScObj, context_id); 889 if (!javaContext) { 890 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 891 "%s: could not set or get _context", __FUNCTION__); 892 return -1; 893 } 894 } 895 else { 896 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 897 "%s: did not set Context - some functionality is not " 898 "supported", 899 __FUNCTION__); 900 } 901 902 // Get rec buffer field ID. 903 jfieldID fidRecBuffer = env->GetFieldID(_javaScClass, "_recBuffer", 904 "Ljava/nio/ByteBuffer;"); 905 if (!fidRecBuffer) 906 { 907 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 908 "%s: could not get rec buffer fid", __FUNCTION__); 909 return -1; 910 } 911 912 // Get rec buffer object. 913 jobject javaRecBufferLocal = env->GetObjectField(_javaScObj, fidRecBuffer); 914 if (!javaRecBufferLocal) 915 { 916 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 917 "%s: could not get rec buffer", __FUNCTION__); 918 return -1; 919 } 920 921 // Create a global reference to the object (to tell JNI that we are 922 // referencing it after this function has returned) 923 // NOTE: we are referencing it only through the direct buffer (see below). 924 _javaRecBuffer = env->NewGlobalRef(javaRecBufferLocal); 925 if (!_javaRecBuffer) 926 { 927 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 928 "%s: could not get rec buffer reference", __FUNCTION__); 929 return -1; 930 } 931 932 // Delete local object ref, we only use the global ref. 933 env->DeleteLocalRef(javaRecBufferLocal); 934 935 // Get direct buffer. 936 _javaDirectRecBuffer = env->GetDirectBufferAddress(_javaRecBuffer); 937 if (!_javaDirectRecBuffer) 938 { 939 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 940 "%s: could not get direct rec buffer", __FUNCTION__); 941 return -1; 942 } 943 944 // Get the rec audio method ID. 945 _javaMidRecAudio = env->GetMethodID(_javaScClass, "RecordAudio", "(I)I"); 946 if (!_javaMidRecAudio) 947 { 948 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 949 "%s: could not get rec audio mid", __FUNCTION__); 950 return -1; 951 } 952 953 // Detach this thread if it was attached. 954 if (isAttached) 955 { 956 if (_javaVM->DetachCurrentThread() < 0) 957 { 958 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 959 "%s: Could not detach thread from JVM", __FUNCTION__); 960 } 961 } 962 963 return 0; 964 965 } 966 967 int32_t AudioRecordJni::InitSampleRate() { 968 int samplingFreq = 44100; 969 jint res = 0; 970 971 // get the JNI env for this thread 972 JNIEnv *env; 973 bool isAttached = false; 974 975 // get the JNI env for this thread 976 if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) 977 { 978 // try to attach the thread and get the env 979 // Attach this thread to JVM 980 jint res = _javaVM->AttachCurrentThread(&env, NULL); 981 if ((res < 0) || !env) 982 { 983 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 984 "%s: Could not attach thread to JVM (%d, %p)", 985 __FUNCTION__, res, env); 986 return -1; 987 } 988 isAttached = true; 989 } 990 991 if (_samplingFreqIn > 0) 992 { 993 // read the configured sampling rate 994 samplingFreq = 44100; 995 if (_samplingFreqIn != 44) 996 { 997 samplingFreq = _samplingFreqIn * 1000; 998 } 999 WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, 1000 " Trying configured recording sampling rate %d", 1001 samplingFreq); 1002 } 1003 1004 // get the method ID 1005 jmethodID initRecordingID = env->GetMethodID(_javaScClass, "InitRecording", 1006 "(II)I"); 1007 1008 bool keepTrying = true; 1009 while (keepTrying) 1010 { 1011 // call java sc object method 1012 res = env->CallIntMethod(_javaScObj, initRecordingID, _recAudioSource, 1013 samplingFreq); 1014 if (res < 0) 1015 { 1016 switch (samplingFreq) 1017 { 1018 case 44100: 1019 samplingFreq = 16000; 1020 break; 1021 case 16000: 1022 samplingFreq = 8000; 1023 break; 1024 default: // error 1025 WEBRTC_TRACE(kTraceError, 1026 kTraceAudioDevice, _id, 1027 "%s: InitRecording failed (%d)", __FUNCTION__, 1028 res); 1029 return -1; 1030 } 1031 } 1032 else 1033 { 1034 keepTrying = false; 1035 } 1036 } 1037 1038 // set the recording sample rate to use 1039 if (samplingFreq == 44100) 1040 { 1041 _samplingFreqIn = 44; 1042 } 1043 else 1044 { 1045 _samplingFreqIn = samplingFreq / 1000; 1046 } 1047 1048 WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id, 1049 "Recording sample rate set to (%d)", _samplingFreqIn); 1050 1051 // get the method ID 1052 jmethodID stopRecordingID = env->GetMethodID(_javaScClass, "StopRecording", 1053 "()I"); 1054 1055 // Call java sc object method 1056 res = env->CallIntMethod(_javaScObj, stopRecordingID); 1057 if (res < 0) 1058 { 1059 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 1060 "StopRecording failed (%d)", res); 1061 } 1062 1063 // Detach this thread if it was attached 1064 if (isAttached) 1065 { 1066 if (_javaVM->DetachCurrentThread() < 0) 1067 { 1068 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, 1069 "%s: Could not detach thread from JVM", __FUNCTION__); 1070 } 1071 } 1072 1073 return 0; 1074 } 1075 1076 bool AudioRecordJni::RecThreadFunc(void* pThis) 1077 { 1078 return (static_cast<AudioRecordJni*> (pThis)->RecThreadProcess()); 1079 } 1080 1081 bool AudioRecordJni::RecThreadProcess() 1082 { 1083 if (!_recThreadIsInitialized) 1084 { 1085 // Do once when thread is started 1086 1087 // Attach this thread to JVM 1088 jint res = _javaVM->AttachCurrentThread(&_jniEnvRec, NULL); 1089 1090 // Get the JNI env for this thread 1091 if ((res < 0) || !_jniEnvRec) 1092 { 1093 WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, 1094 _id, "Could not attach rec thread to JVM (%d, %p)", 1095 res, _jniEnvRec); 1096 return false; // Close down thread 1097 } 1098 1099 _recThreadIsInitialized = true; 1100 } 1101 1102 // just sleep if rec has not started 1103 if (!_recording) 1104 { 1105 switch (_timeEventRec.Wait(1000)) 1106 { 1107 case kEventSignaled: 1108 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, 1109 _id, "Recording thread event signal"); 1110 _timeEventRec.Reset(); 1111 break; 1112 case kEventError: 1113 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, 1114 _id, "Recording thread event error"); 1115 return true; 1116 case kEventTimeout: 1117 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, 1118 _id, "Recording thread event timeout"); 1119 return true; 1120 } 1121 } 1122 1123 Lock(); 1124 1125 if (_startRec) 1126 { 1127 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, 1128 "_startRec true, performing initial actions"); 1129 _startRec = false; 1130 _recording = true; 1131 _recWarning = 0; 1132 _recError = 0; 1133 _recStartStopEvent.Set(); 1134 } 1135 1136 if (_recording) 1137 { 1138 uint32_t samplesToRec = _samplingFreqIn * 10; 1139 1140 // Call java sc object method to record data to direct buffer 1141 // Will block until data has been recorded (see java sc class), 1142 // therefore we must release the lock 1143 UnLock(); 1144 jint recDelayInSamples = _jniEnvRec->CallIntMethod(_javaScObj, 1145 _javaMidRecAudio, 1146 2 * samplesToRec); 1147 if (recDelayInSamples < 0) 1148 { 1149 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, 1150 "RecordAudio failed"); 1151 _recWarning = 1; 1152 } 1153 else 1154 { 1155 _delayRecording = recDelayInSamples / _samplingFreqIn; 1156 } 1157 Lock(); 1158 1159 // Check again since recording may have stopped during Java call 1160 if (_recording) 1161 { 1162 // WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1163 // "total delay is %d", msPlayDelay + _delayRecording); 1164 1165 // Copy data to our direct buffer (held by java sc object) 1166 // todo: Give _javaDirectRecBuffer directly to VoE? 1167 // todo: Check count <= 480 ? 1168 memcpy(_recBuffer, _javaDirectRecBuffer, 2 * samplesToRec); 1169 1170 // store the recorded buffer (no action will be taken if the 1171 // #recorded samples is not a full buffer) 1172 _ptrAudioBuffer->SetRecordedBuffer(_recBuffer, samplesToRec); 1173 1174 // store vqe delay values 1175 _ptrAudioBuffer->SetVQEData(_delay_provider->PlayoutDelayMs(), 1176 _delayRecording, 0); 1177 1178 // deliver recorded samples at specified sample rate, mic level 1179 // etc. to the observer using callback 1180 UnLock(); 1181 _ptrAudioBuffer->DeliverRecordedData(); 1182 Lock(); 1183 } 1184 1185 } // _recording 1186 1187 if (_shutdownRecThread) 1188 { 1189 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1190 "Detaching rec thread from Java VM"); 1191 1192 // Detach thread from Java VM 1193 if (_javaVM->DetachCurrentThread() < 0) 1194 { 1195 WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, 1196 _id, "Could not detach recording thread from JVM"); 1197 _shutdownRecThread = false; 1198 // If we say OK (i.e. set event) and close thread anyway, 1199 // app will crash 1200 } 1201 else 1202 { 1203 _jniEnvRec = NULL; 1204 _shutdownRecThread = false; 1205 _recStartStopEvent.Set(); // Signal to Terminate() that we are done 1206 1207 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, 1208 "Sent signal rec"); 1209 } 1210 } 1211 1212 UnLock(); 1213 return true; 1214 } 1215 1216 } // namespace webrtc 1217