Home | History | Annotate | Download | only in android
      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