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/AudioTrack 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_track_jni.h"
     19 
     20 #include <android/log.h>
     21 #include <stdlib.h>
     22 
     23 #include "webrtc/modules/audio_device/audio_device_config.h"
     24 #include "webrtc/modules/audio_device/audio_device_utility.h"
     25 
     26 #include "webrtc/system_wrappers/interface/event_wrapper.h"
     27 #include "webrtc/system_wrappers/interface/thread_wrapper.h"
     28 #include "webrtc/system_wrappers/interface/trace.h"
     29 
     30 namespace webrtc {
     31 
     32 JavaVM* AudioTrackJni::globalJvm = NULL;
     33 JNIEnv* AudioTrackJni::globalJNIEnv = NULL;
     34 jobject AudioTrackJni::globalContext = NULL;
     35 jclass AudioTrackJni::globalScClass = NULL;
     36 
     37 int32_t AudioTrackJni::SetAndroidAudioDeviceObjects(void* javaVM, void* env,
     38                                                     void* context) {
     39   assert(env);
     40   globalJvm = reinterpret_cast<JavaVM*>(javaVM);
     41   globalJNIEnv = reinterpret_cast<JNIEnv*>(env);
     42   // Get java class type (note path to class packet).
     43   jclass javaScClassLocal = globalJNIEnv->FindClass(
     44       "org/webrtc/voiceengine/WebRtcAudioTrack");
     45   if (!javaScClassLocal) {
     46     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1,
     47                  "%s: could not find java class", __FUNCTION__);
     48     return -1; // exception thrown
     49   }
     50 
     51   // Create a global reference to the class (to tell JNI that we are
     52   // referencing it after this function has returned).
     53   globalScClass = reinterpret_cast<jclass> (
     54       globalJNIEnv->NewGlobalRef(javaScClassLocal));
     55   if (!globalScClass) {
     56     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1,
     57                  "%s: could not create reference", __FUNCTION__);
     58     return -1;
     59   }
     60 
     61   globalContext = globalJNIEnv->NewGlobalRef(
     62       reinterpret_cast<jobject>(context));
     63   if (!globalContext) {
     64     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1,
     65                  "%s: could not create context reference", __FUNCTION__);
     66     return -1;
     67   }
     68 
     69   // Delete local class ref, we only use the global ref
     70   globalJNIEnv->DeleteLocalRef(javaScClassLocal);
     71   return 0;
     72 }
     73 
     74 void AudioTrackJni::ClearAndroidAudioDeviceObjects() {
     75   WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, -1,
     76                "%s: env is NULL, assuming deinit", __FUNCTION__);
     77 
     78   globalJvm = NULL;
     79   if (!globalJNIEnv) {
     80     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, -1,
     81                  "%s: saved env already NULL", __FUNCTION__);
     82     return;
     83   }
     84 
     85   globalJNIEnv->DeleteGlobalRef(globalContext);
     86   globalContext = reinterpret_cast<jobject>(NULL);
     87 
     88   globalJNIEnv->DeleteGlobalRef(globalScClass);
     89   globalScClass = reinterpret_cast<jclass>(NULL);
     90 
     91   globalJNIEnv = reinterpret_cast<JNIEnv*>(NULL);
     92 }
     93 
     94 AudioTrackJni::AudioTrackJni(const int32_t id)
     95     : _javaVM(NULL),
     96       _jniEnvPlay(NULL),
     97       _javaScClass(0),
     98       _javaScObj(0),
     99       _javaPlayBuffer(0),
    100       _javaDirectPlayBuffer(NULL),
    101       _javaMidPlayAudio(0),
    102       _ptrAudioBuffer(NULL),
    103       _critSect(*CriticalSectionWrapper::CreateCriticalSection()),
    104       _id(id),
    105       _initialized(false),
    106       _timeEventPlay(*EventWrapper::Create()),
    107       _playStartStopEvent(*EventWrapper::Create()),
    108       _ptrThreadPlay(NULL),
    109       _playThreadID(0),
    110       _playThreadIsInitialized(false),
    111       _shutdownPlayThread(false),
    112       _playoutDeviceIsSpecified(false),
    113       _playing(false),
    114       _playIsInitialized(false),
    115       _speakerIsInitialized(false),
    116       _startPlay(false),
    117       _playWarning(0),
    118       _playError(0),
    119       _delayPlayout(0),
    120       _samplingFreqOut((N_PLAY_SAMPLES_PER_SEC/1000)),
    121       _maxSpeakerVolume(0) {
    122 }
    123 
    124 AudioTrackJni::~AudioTrackJni() {
    125   WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id,
    126                "%s destroyed", __FUNCTION__);
    127 
    128   Terminate();
    129 
    130   delete &_playStartStopEvent;
    131   delete &_timeEventPlay;
    132   delete &_critSect;
    133 }
    134 
    135 int32_t AudioTrackJni::Init() {
    136   CriticalSectionScoped lock(&_critSect);
    137   if (_initialized)
    138   {
    139     return 0;
    140   }
    141 
    142   _playWarning = 0;
    143   _playError = 0;
    144 
    145   // Init Java member variables
    146   // and set up JNI interface to
    147   // AudioDeviceAndroid java class
    148   if (InitJavaResources() != 0)
    149   {
    150     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    151                  "%s: Failed to init Java resources", __FUNCTION__);
    152     return -1;
    153   }
    154 
    155   // Check the sample rate to be used for playback and recording
    156   // and the max playout volume
    157   if (InitSampleRate() != 0)
    158   {
    159     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    160                  "%s: Failed to init samplerate", __FUNCTION__);
    161     return -1;
    162   }
    163 
    164   const char* threadName = "jni_audio_render_thread";
    165   _ptrThreadPlay = ThreadWrapper::CreateThread(PlayThreadFunc, this,
    166                                                kRealtimePriority, threadName);
    167   if (_ptrThreadPlay == NULL)
    168   {
    169     WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id,
    170                  "  failed to create the play audio thread");
    171     return -1;
    172   }
    173 
    174   unsigned int threadID = 0;
    175   if (!_ptrThreadPlay->Start(threadID))
    176   {
    177     WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id,
    178                  "  failed to start the play audio thread");
    179     delete _ptrThreadPlay;
    180     _ptrThreadPlay = NULL;
    181     return -1;
    182   }
    183   _playThreadID = threadID;
    184 
    185   _initialized = true;
    186 
    187   return 0;
    188 }
    189 
    190 int32_t AudioTrackJni::Terminate() {
    191   CriticalSectionScoped lock(&_critSect);
    192   if (!_initialized)
    193   {
    194     return 0;
    195   }
    196 
    197   StopPlayout();
    198   _shutdownPlayThread = true;
    199   _timeEventPlay.Set(); // Release rec thread from waiting state
    200   if (_ptrThreadPlay)
    201   {
    202     // First, the thread must detach itself from Java VM
    203     _critSect.Leave();
    204     if (kEventSignaled != _playStartStopEvent.Wait(5000))
    205     {
    206       WEBRTC_TRACE(
    207           kTraceError,
    208           kTraceAudioDevice,
    209           _id,
    210           "%s: Playout thread shutdown timed out, cannot "
    211           "terminate thread",
    212           __FUNCTION__);
    213       // If we close thread anyway, the app will crash
    214       return -1;
    215     }
    216     _playStartStopEvent.Reset();
    217     _critSect.Enter();
    218 
    219     // Close down play thread
    220     ThreadWrapper* tmpThread = _ptrThreadPlay;
    221     _ptrThreadPlay = NULL;
    222     _critSect.Leave();
    223     tmpThread->SetNotAlive();
    224     _timeEventPlay.Set();
    225     if (tmpThread->Stop())
    226     {
    227       delete tmpThread;
    228       _jniEnvPlay = NULL;
    229     }
    230     else
    231     {
    232       WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
    233                    "  failed to close down the play audio thread");
    234     }
    235     _critSect.Enter();
    236 
    237     _playThreadIsInitialized = false;
    238   }
    239   _speakerIsInitialized = false;
    240   _playoutDeviceIsSpecified = false;
    241 
    242   // get the JNI env for this thread
    243   JNIEnv *env;
    244   bool isAttached = false;
    245 
    246   // get the JNI env for this thread
    247   if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
    248   {
    249     // try to attach the thread and get the env
    250     // Attach this thread to JVM
    251     jint res = _javaVM->AttachCurrentThread(&env, NULL);
    252     if ((res < 0) || !env)
    253     {
    254       WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    255                    "%s: Could not attach thread to JVM (%d, %p)",
    256                    __FUNCTION__, res, env);
    257       return -1;
    258     }
    259     isAttached = true;
    260   }
    261 
    262   // Make method IDs and buffer pointers unusable
    263   _javaMidPlayAudio = 0;
    264   _javaDirectPlayBuffer = NULL;
    265 
    266   // Delete the references to the java buffers, this allows the
    267   // garbage collector to delete them
    268   env->DeleteGlobalRef(_javaPlayBuffer);
    269   _javaPlayBuffer = 0;
    270 
    271   // Delete the references to the java object and class, this allows the
    272   // garbage collector to delete them
    273   env->DeleteGlobalRef(_javaScObj);
    274   _javaScObj = 0;
    275   _javaScClass = 0;
    276 
    277   // Detach this thread if it was attached
    278   if (isAttached)
    279   {
    280     if (_javaVM->DetachCurrentThread() < 0)
    281     {
    282       WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
    283                    "%s: Could not detach thread from JVM", __FUNCTION__);
    284     }
    285   }
    286 
    287   _initialized = false;
    288 
    289   return 0;
    290 }
    291 
    292 int32_t AudioTrackJni::PlayoutDeviceName(uint16_t index,
    293                                          char name[kAdmMaxDeviceNameSize],
    294                                          char guid[kAdmMaxGuidSize]) {
    295   if (0 != index)
    296   {
    297         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    298                      "  Device index is out of range [0,0]");
    299         return -1;
    300   }
    301 
    302   // Return empty string
    303   memset(name, 0, kAdmMaxDeviceNameSize);
    304 
    305   if (guid)
    306   {
    307     memset(guid, 0, kAdmMaxGuidSize);
    308     }
    309 
    310   return 0;
    311 }
    312 
    313 int32_t AudioTrackJni::SetPlayoutDevice(uint16_t index) {
    314   if (_playIsInitialized)
    315   {
    316     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    317                  "  Playout already initialized");
    318     return -1;
    319     }
    320 
    321   if (0 != index)
    322   {
    323     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    324                  "  Device index is out of range [0,0]");
    325     return -1;
    326   }
    327 
    328   // Do nothing but set a flag, this is to have consistent behavior
    329   // with other platforms
    330     _playoutDeviceIsSpecified = true;
    331 
    332     return 0;
    333 }
    334 
    335 int32_t AudioTrackJni::SetPlayoutDevice(
    336     AudioDeviceModule::WindowsDeviceType device) {
    337   WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
    338                "  API call not supported on this platform");
    339   return -1;
    340 }
    341 
    342 
    343 int32_t AudioTrackJni::PlayoutIsAvailable(bool& available) {  // NOLINT
    344   available = false;
    345 
    346   // Try to initialize the playout side
    347   int32_t res = InitPlayout();
    348 
    349   // Cancel effect of initialization
    350   StopPlayout();
    351 
    352     if (res != -1)
    353     {
    354       available = true;
    355     }
    356 
    357     return res;
    358 }
    359 
    360 int32_t AudioTrackJni::InitPlayout() {
    361   CriticalSectionScoped lock(&_critSect);
    362 
    363     if (!_initialized)
    364     {
    365       WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    366                    "  Not initialized");
    367       return -1;
    368     }
    369 
    370     if (_playing)
    371     {
    372       WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
    373                    "  Playout already started");
    374         return -1;
    375     }
    376 
    377     if (!_playoutDeviceIsSpecified)
    378     {
    379       WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    380                    "  Playout device is not specified");
    381       return -1;
    382     }
    383 
    384     if (_playIsInitialized)
    385     {
    386       WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
    387                    "  Playout already initialized");
    388       return 0;
    389     }
    390 
    391     // Initialize the speaker
    392     if (InitSpeaker() == -1)
    393     {
    394       WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
    395                    "  InitSpeaker() failed");
    396     }
    397 
    398     // get the JNI env for this thread
    399     JNIEnv *env;
    400     bool isAttached = false;
    401 
    402     // get the JNI env for this thread
    403     if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
    404     {
    405       WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
    406                    "attaching");
    407 
    408       // try to attach the thread and get the env
    409       // Attach this thread to JVM
    410       jint res = _javaVM->AttachCurrentThread(&env, NULL);
    411       if ((res < 0) || !env)
    412       {
    413         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    414                      "  Could not attach thread to JVM (%d, %p)", res, env);
    415         return -1;
    416       }
    417       isAttached = true;
    418     }
    419 
    420     // get the method ID
    421     jmethodID initPlaybackID = env->GetMethodID(_javaScClass, "InitPlayback",
    422                                                 "(I)I");
    423 
    424     int samplingFreq = 44100;
    425     if (_samplingFreqOut != 44)
    426     {
    427       samplingFreq = _samplingFreqOut * 1000;
    428     }
    429 
    430     int retVal = -1;
    431 
    432     // Call java sc object method
    433     jint res = env->CallIntMethod(_javaScObj, initPlaybackID, samplingFreq);
    434     if (res < 0)
    435     {
    436       WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    437                    "InitPlayback failed (%d)", res);
    438     }
    439     else
    440     {
    441       // Set the audio device buffer sampling rate
    442       _ptrAudioBuffer->SetPlayoutSampleRate(_samplingFreqOut * 1000);
    443       _playIsInitialized = true;
    444       retVal = 0;
    445     }
    446 
    447     // Detach this thread if it was attached
    448     if (isAttached)
    449     {
    450       WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
    451                      "detaching");
    452       if (_javaVM->DetachCurrentThread() < 0)
    453       {
    454         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
    455                      "  Could not detach thread from JVM");
    456       }
    457     }
    458 
    459     return retVal;
    460 }
    461 
    462 int32_t AudioTrackJni::StartPlayout() {
    463   CriticalSectionScoped lock(&_critSect);
    464 
    465   if (!_playIsInitialized)
    466   {
    467     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    468                  "  Playout not initialized");
    469     return -1;
    470   }
    471 
    472   if (_playing)
    473     {
    474       WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
    475                    "  Playout already started");
    476       return 0;
    477     }
    478 
    479   // get the JNI env for this thread
    480   JNIEnv *env;
    481   bool isAttached = false;
    482 
    483   // get the JNI env for this thread
    484     if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
    485     {
    486       // try to attach the thread and get the env
    487       // Attach this thread to JVM
    488       jint res = _javaVM->AttachCurrentThread(&env, NULL);
    489       if ((res < 0) || !env)
    490       {
    491         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    492                      "  Could not attach thread to JVM (%d, %p)", res, env);
    493         return -1;
    494       }
    495         isAttached = true;
    496     }
    497 
    498     // get the method ID
    499     jmethodID startPlaybackID = env->GetMethodID(_javaScClass, "StartPlayback",
    500                                                  "()I");
    501 
    502     // Call java sc object method
    503     jint res = env->CallIntMethod(_javaScObj, startPlaybackID);
    504     if (res < 0)
    505     {
    506         WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    507                      "StartPlayback failed (%d)", res);
    508         return -1;
    509     }
    510 
    511     _playWarning = 0;
    512     _playError = 0;
    513 
    514     // Signal to playout thread that we want to start
    515     _startPlay = true;
    516     _timeEventPlay.Set(); // Release thread from waiting state
    517     _critSect.Leave();
    518     // Wait for thread to init
    519     if (kEventSignaled != _playStartStopEvent.Wait(5000))
    520     {
    521       WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    522                    "  Timeout or error starting");
    523     }
    524     _playStartStopEvent.Reset();
    525     _critSect.Enter();
    526 
    527     // Detach this thread if it was attached
    528     if (isAttached)
    529     {
    530       if (_javaVM->DetachCurrentThread() < 0)
    531       {
    532         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
    533                      "  Could not detach thread from JVM");
    534       }
    535     }
    536 
    537     return 0;
    538 }
    539 
    540 int32_t AudioTrackJni::StopPlayout() {
    541   CriticalSectionScoped lock(&_critSect);
    542 
    543   if (!_playIsInitialized)
    544   {
    545     WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
    546                  "  Playout is not initialized");
    547     return 0;
    548   }
    549 
    550     // get the JNI env for this thread
    551   JNIEnv *env;
    552   bool isAttached = false;
    553 
    554   // get the JNI env for this thread
    555   if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
    556   {
    557     // try to attach the thread and get the env
    558     // Attach this thread to JVM
    559     jint res = _javaVM->AttachCurrentThread(&env, NULL);
    560     if ((res < 0) || !env)
    561         {
    562           WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    563                        "  Could not attach thread to JVM (%d, %p)", res, env);
    564           return -1;
    565         }
    566     isAttached = true;
    567   }
    568 
    569   // get the method ID
    570   jmethodID stopPlaybackID = env->GetMethodID(_javaScClass, "StopPlayback",
    571                                               "()I");
    572 
    573   // Call java sc object method
    574   jint res = env->CallIntMethod(_javaScObj, stopPlaybackID);
    575   if (res < 0)
    576   {
    577     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    578                  "StopPlayback failed (%d)", res);
    579   }
    580 
    581   _playIsInitialized = false;
    582   _playing = false;
    583     _playWarning = 0;
    584     _playError = 0;
    585 
    586     // Detach this thread if it was attached
    587     if (isAttached)
    588     {
    589       if (_javaVM->DetachCurrentThread() < 0)
    590       {
    591         WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
    592                      "  Could not detach thread from JVM");
    593       }
    594     }
    595 
    596     return 0;
    597 
    598 }
    599 
    600 int32_t AudioTrackJni::InitSpeaker() {
    601   CriticalSectionScoped lock(&_critSect);
    602 
    603   if (_playing)
    604   {
    605     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
    606                  "  Playout already started");
    607     return -1;
    608   }
    609 
    610     if (!_playoutDeviceIsSpecified)
    611     {
    612       WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    613                    "  Playout device is not specified");
    614       return -1;
    615     }
    616 
    617     // Nothing needs to be done here, we use a flag to have consistent
    618     // behavior with other platforms
    619     _speakerIsInitialized = true;
    620 
    621     return 0;
    622 }
    623 
    624 int32_t AudioTrackJni::SpeakerVolumeIsAvailable(bool& available) {  // NOLINT
    625   available = true; // We assume we are always be able to set/get volume
    626   return 0;
    627 }
    628 
    629 int32_t AudioTrackJni::SetSpeakerVolume(uint32_t volume) {
    630   if (!_speakerIsInitialized)
    631   {
    632     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    633                  "  Speaker not initialized");
    634     return -1;
    635   }
    636   if (!globalContext)
    637   {
    638     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    639                  "  Context is not set");
    640     return -1;
    641   }
    642 
    643   // get the JNI env for this thread
    644   JNIEnv *env;
    645   bool isAttached = false;
    646 
    647   if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
    648   {
    649     // try to attach the thread and get the env
    650     // Attach this thread to JVM
    651     jint res = _javaVM->AttachCurrentThread(&env, NULL);
    652     if ((res < 0) || !env)
    653     {
    654       WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    655                    "  Could not attach thread to JVM (%d, %p)", res, env);
    656       return -1;
    657     }
    658     isAttached = true;
    659   }
    660 
    661   // get the method ID
    662   jmethodID setPlayoutVolumeID = env->GetMethodID(_javaScClass,
    663                                                   "SetPlayoutVolume", "(I)I");
    664 
    665   // call java sc object method
    666   jint res = env->CallIntMethod(_javaScObj, setPlayoutVolumeID,
    667                                 static_cast<int> (volume));
    668   if (res < 0)
    669   {
    670     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    671                  "SetPlayoutVolume failed (%d)", res);
    672     return -1;
    673   }
    674 
    675   // Detach this thread if it was attached
    676   if (isAttached)
    677   {
    678     if (_javaVM->DetachCurrentThread() < 0)
    679     {
    680       WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
    681                    "  Could not detach thread from JVM");
    682     }
    683   }
    684 
    685   return 0;
    686 }
    687 
    688 int32_t AudioTrackJni::SpeakerVolume(uint32_t& volume) const {  // NOLINT
    689   if (!_speakerIsInitialized)
    690   {
    691     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    692                  "  Speaker not initialized");
    693     return -1;
    694   }
    695   if (!globalContext)
    696   {
    697     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    698                  "  Context is not set");
    699     return -1;
    700   }
    701 
    702   // get the JNI env for this thread
    703   JNIEnv *env;
    704   bool isAttached = false;
    705 
    706   if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
    707   {
    708     // try to attach the thread and get the env
    709     // Attach this thread to JVM
    710     jint res = _javaVM->AttachCurrentThread(&env, NULL);
    711     if ((res < 0) || !env)
    712     {
    713       WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    714                    "  Could not attach thread to JVM (%d, %p)", res, env);
    715       return -1;
    716     }
    717     isAttached = true;
    718   }
    719 
    720   // get the method ID
    721   jmethodID getPlayoutVolumeID = env->GetMethodID(_javaScClass,
    722                                                   "GetPlayoutVolume", "()I");
    723 
    724   // call java sc object method
    725   jint level = env->CallIntMethod(_javaScObj, getPlayoutVolumeID);
    726   if (level < 0)
    727   {
    728     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    729                  "GetPlayoutVolume failed (%d)", level);
    730     return -1;
    731   }
    732 
    733   // Detach this thread if it was attached
    734   if (isAttached)
    735   {
    736     if (_javaVM->DetachCurrentThread() < 0)
    737     {
    738       WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
    739                    "  Could not detach thread from JVM");
    740     }
    741   }
    742 
    743   volume = static_cast<uint32_t> (level);
    744 
    745   return 0;
    746 }
    747 
    748 
    749 int32_t AudioTrackJni::MaxSpeakerVolume(uint32_t& maxVolume) const {  // NOLINT
    750   if (!_speakerIsInitialized)
    751   {
    752     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    753                  "  Speaker not initialized");
    754     return -1;
    755   }
    756 
    757   maxVolume = _maxSpeakerVolume;
    758 
    759   return 0;
    760 }
    761 
    762 int32_t AudioTrackJni::MinSpeakerVolume(uint32_t& minVolume) const {  // NOLINT
    763   if (!_speakerIsInitialized)
    764   {
    765     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    766                  "  Speaker not initialized");
    767     return -1;
    768   }
    769   minVolume = 0;
    770   return 0;
    771 }
    772 
    773 int32_t AudioTrackJni::SpeakerVolumeStepSize(
    774     uint16_t& stepSize) const {  // NOLINT
    775   if (!_speakerIsInitialized)
    776   {
    777     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    778                  "  Speaker not initialized");
    779     return -1;
    780   }
    781 
    782   stepSize = 1;
    783 
    784   return 0;
    785 }
    786 
    787 int32_t AudioTrackJni::SpeakerMuteIsAvailable(bool& available) {  // NOLINT
    788   available = false; // Speaker mute not supported on Android
    789   return 0;
    790 }
    791 
    792 int32_t AudioTrackJni::SetSpeakerMute(bool enable) {
    793   WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
    794                "  API call not supported on this platform");
    795   return -1;
    796 }
    797 
    798 int32_t AudioTrackJni::SpeakerMute(bool& /*enabled*/) const {
    799 
    800   WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
    801                "  API call not supported on this platform");
    802   return -1;
    803 }
    804 
    805 int32_t AudioTrackJni::StereoPlayoutIsAvailable(bool& available) {  // NOLINT
    806   available = false; // Stereo playout not supported on Android
    807   return 0;
    808 }
    809 
    810 int32_t AudioTrackJni::SetStereoPlayout(bool enable) {
    811   if (enable)
    812   {
    813     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    814                  "  Enabling not available");
    815     return -1;
    816   }
    817 
    818   return 0;
    819 }
    820 
    821 int32_t AudioTrackJni::StereoPlayout(bool& enabled) const {  // NOLINT
    822   enabled = false;
    823   return 0;
    824 }
    825 
    826 int32_t AudioTrackJni::SetPlayoutBuffer(
    827     const AudioDeviceModule::BufferType type,
    828     uint16_t sizeMS) {
    829   WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
    830                  "  API call not supported on this platform");
    831   return -1;
    832 }
    833 
    834 
    835 int32_t AudioTrackJni::PlayoutBuffer(
    836     AudioDeviceModule::BufferType& type,  // NOLINT
    837     uint16_t& sizeMS) const {  // NOLINT
    838   type = AudioDeviceModule::kAdaptiveBufferSize;
    839   sizeMS = _delayPlayout; // Set to current playout delay
    840 
    841     return 0;
    842 }
    843 
    844 int32_t AudioTrackJni::PlayoutDelay(uint16_t& delayMS) const {  // NOLINT
    845   delayMS = _delayPlayout;
    846   return 0;
    847 }
    848 
    849 void AudioTrackJni::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
    850   CriticalSectionScoped lock(&_critSect);
    851   _ptrAudioBuffer = audioBuffer;
    852   // inform the AudioBuffer about default settings for this implementation
    853   _ptrAudioBuffer->SetPlayoutSampleRate(N_PLAY_SAMPLES_PER_SEC);
    854   _ptrAudioBuffer->SetPlayoutChannels(N_PLAY_CHANNELS);
    855 }
    856 
    857 int32_t AudioTrackJni::SetPlayoutSampleRate(const uint32_t samplesPerSec) {
    858   if (samplesPerSec > 48000 || samplesPerSec < 8000)
    859   {
    860     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    861                  "  Invalid sample rate");
    862     return -1;
    863     }
    864 
    865   // set the playout sample rate to use
    866   if (samplesPerSec == 44100)
    867   {
    868     _samplingFreqOut = 44;
    869   }
    870   else
    871   {
    872     _samplingFreqOut = samplesPerSec / 1000;
    873   }
    874 
    875   // Update the AudioDeviceBuffer
    876   _ptrAudioBuffer->SetPlayoutSampleRate(samplesPerSec);
    877 
    878   return 0;
    879 }
    880 
    881 bool AudioTrackJni::PlayoutWarning() const {
    882   return (_playWarning > 0);
    883 }
    884 
    885 bool AudioTrackJni::PlayoutError() const {
    886   return (_playError > 0);
    887 }
    888 
    889 void AudioTrackJni::ClearPlayoutWarning() {
    890   _playWarning = 0;
    891 }
    892 
    893 void AudioTrackJni::ClearPlayoutError() {
    894   _playError = 0;
    895 }
    896 
    897 int32_t AudioTrackJni::SetLoudspeakerStatus(bool enable) {
    898   if (!globalContext)
    899   {
    900     WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
    901                  "  Context is not set");
    902     return -1;
    903   }
    904 
    905   // get the JNI env for this thread
    906   JNIEnv *env;
    907     bool isAttached = false;
    908 
    909     if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
    910     {
    911       // try to attach the thread and get the env
    912       // Attach this thread to JVM
    913       jint res = _javaVM->AttachCurrentThread(&env, NULL);
    914 
    915       // Get the JNI env for this thread
    916       if ((res < 0) || !env)
    917       {
    918             WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
    919                          "  Could not attach thread to JVM (%d, %p)", res, env);
    920             return -1;
    921       }
    922       isAttached = true;
    923     }
    924 
    925     // get the method ID
    926     jmethodID setPlayoutSpeakerID = env->GetMethodID(_javaScClass,
    927                                                      "SetPlayoutSpeaker",
    928                                                      "(Z)I");
    929 
    930     // call java sc object method
    931     jint res = env->CallIntMethod(_javaScObj, setPlayoutSpeakerID, enable);
    932     if (res < 0)
    933     {
    934       WEBRTC_TRACE(kTraceError, kTraceUtility, -1,
    935                    "  SetPlayoutSpeaker failed (%d)", res);
    936       return -1;
    937     }
    938 
    939     _loudSpeakerOn = enable;
    940 
    941     // Detach this thread if it was attached
    942     if (isAttached)
    943     {
    944       if (_javaVM->DetachCurrentThread() < 0)
    945       {
    946         WEBRTC_TRACE(kTraceWarning, kTraceUtility, -1,
    947                      "  Could not detach thread from JVM");
    948       }
    949     }
    950 
    951     return 0;
    952 }
    953 
    954 int32_t AudioTrackJni::GetLoudspeakerStatus(bool& enabled) const {  // NOLINT
    955   enabled = _loudSpeakerOn;
    956   return 0;
    957 }
    958 
    959 int32_t AudioTrackJni::InitJavaResources() {
    960   // todo: Check if we already have created the java object
    961   _javaVM = globalJvm;
    962   _javaScClass = globalScClass;
    963 
    964   // use the jvm that has been set
    965   if (!_javaVM)
    966   {
    967     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    968                  "%s: Not a valid Java VM pointer", __FUNCTION__);
    969     return -1;
    970   }
    971 
    972   // get the JNI env for this thread
    973   JNIEnv *env;
    974   bool isAttached = false;
    975 
    976   // get the JNI env for this thread
    977   if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
    978   {
    979     // try to attach the thread and get the env
    980     // Attach this thread to JVM
    981     jint res = _javaVM->AttachCurrentThread(&env, NULL);
    982     if ((res < 0) || !env)
    983     {
    984       WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
    985                    "%s: Could not attach thread to JVM (%d, %p)",
    986                    __FUNCTION__, res, env);
    987       return -1;
    988     }
    989     isAttached = true;
    990   }
    991 
    992   WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
    993                "get method id");
    994 
    995   // get the method ID for the void(void) constructor
    996   jmethodID cid = env->GetMethodID(_javaScClass, "<init>", "()V");
    997   if (cid == NULL)
    998   {
    999     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
   1000                  "%s: could not get constructor ID", __FUNCTION__);
   1001     return -1; /* exception thrown */
   1002   }
   1003 
   1004   WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
   1005                "construct object", __FUNCTION__);
   1006 
   1007   // construct the object
   1008   jobject javaScObjLocal = env->NewObject(_javaScClass, cid);
   1009   if (!javaScObjLocal)
   1010   {
   1011     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
   1012                  "%s: could not create Java sc object", __FUNCTION__);
   1013     return -1;
   1014   }
   1015 
   1016   // Create a reference to the object (to tell JNI that we are referencing it
   1017   // after this function has returned).
   1018   _javaScObj = env->NewGlobalRef(javaScObjLocal);
   1019   if (!_javaScObj)
   1020   {
   1021     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
   1022                  "%s: could not create Java sc object reference",
   1023                  __FUNCTION__);
   1024     return -1;
   1025   }
   1026 
   1027   // Delete local object ref, we only use the global ref.
   1028   env->DeleteLocalRef(javaScObjLocal);
   1029 
   1030   //////////////////////
   1031   // AUDIO MANAGEMENT
   1032 
   1033   // This is not mandatory functionality
   1034   if (globalContext) {
   1035     jfieldID context_id = env->GetFieldID(globalScClass,
   1036                                           "_context",
   1037                                           "Landroid/content/Context;");
   1038     if (!context_id) {
   1039       WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
   1040                    "%s: could not get _context id", __FUNCTION__);
   1041       return -1;
   1042     }
   1043 
   1044     env->SetObjectField(_javaScObj, context_id, globalContext);
   1045     jobject javaContext = env->GetObjectField(_javaScObj, context_id);
   1046     if (!javaContext) {
   1047       WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
   1048                    "%s: could not set or get _context", __FUNCTION__);
   1049       return -1;
   1050     }
   1051   }
   1052   else {
   1053     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
   1054                  "%s: did not set Context - some functionality is not "
   1055                  "supported",
   1056                  __FUNCTION__);
   1057   }
   1058 
   1059   /////////////
   1060   // PLAYOUT
   1061 
   1062   // Get play buffer field ID.
   1063   jfieldID fidPlayBuffer = env->GetFieldID(_javaScClass, "_playBuffer",
   1064                                            "Ljava/nio/ByteBuffer;");
   1065   if (!fidPlayBuffer)
   1066   {
   1067     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
   1068                  "%s: could not get play buffer fid", __FUNCTION__);
   1069     return -1;
   1070   }
   1071 
   1072   // Get play buffer object.
   1073   jobject javaPlayBufferLocal =
   1074       env->GetObjectField(_javaScObj, fidPlayBuffer);
   1075   if (!javaPlayBufferLocal)
   1076   {
   1077     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
   1078                  "%s: could not get play buffer", __FUNCTION__);
   1079     return -1;
   1080   }
   1081 
   1082   // Create a global reference to the object (to tell JNI that we are
   1083   // referencing it after this function has returned)
   1084   // NOTE: we are referencing it only through the direct buffer (see below).
   1085   _javaPlayBuffer = env->NewGlobalRef(javaPlayBufferLocal);
   1086   if (!_javaPlayBuffer)
   1087   {
   1088     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
   1089                  "%s: could not get play buffer reference", __FUNCTION__);
   1090     return -1;
   1091   }
   1092 
   1093   // Delete local object ref, we only use the global ref.
   1094   env->DeleteLocalRef(javaPlayBufferLocal);
   1095 
   1096   // Get direct buffer.
   1097   _javaDirectPlayBuffer = env->GetDirectBufferAddress(_javaPlayBuffer);
   1098   if (!_javaDirectPlayBuffer)
   1099   {
   1100     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
   1101                  "%s: could not get direct play buffer", __FUNCTION__);
   1102     return -1;
   1103   }
   1104 
   1105   // Get the play audio method ID.
   1106   _javaMidPlayAudio = env->GetMethodID(_javaScClass, "PlayAudio", "(I)I");
   1107   if (!_javaMidPlayAudio)
   1108   {
   1109     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
   1110                  "%s: could not get play audio mid", __FUNCTION__);
   1111     return -1;
   1112   }
   1113 
   1114   // Detach this thread if it was attached.
   1115   if (isAttached)
   1116   {
   1117     if (_javaVM->DetachCurrentThread() < 0)
   1118     {
   1119       WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
   1120                    "%s: Could not detach thread from JVM", __FUNCTION__);
   1121     }
   1122   }
   1123 
   1124   return 0;
   1125 
   1126 }
   1127 
   1128 int32_t AudioTrackJni::InitSampleRate() {
   1129   int samplingFreq = 44100;
   1130   jint res = 0;
   1131 
   1132   // get the JNI env for this thread
   1133   JNIEnv *env;
   1134   bool isAttached = false;
   1135 
   1136   // get the JNI env for this thread
   1137   if (_javaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
   1138   {
   1139     // try to attach the thread and get the env
   1140     // Attach this thread to JVM
   1141     jint res = _javaVM->AttachCurrentThread(&env, NULL);
   1142     if ((res < 0) || !env)
   1143     {
   1144       WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
   1145                    "%s: Could not attach thread to JVM (%d, %p)",
   1146                    __FUNCTION__, res, env);
   1147       return -1;
   1148     }
   1149     isAttached = true;
   1150   }
   1151 
   1152   // get the method ID
   1153   jmethodID initPlaybackID = env->GetMethodID(_javaScClass, "InitPlayback",
   1154                                               "(I)I");
   1155 
   1156   if (_samplingFreqOut > 0)
   1157   {
   1158     // read the configured sampling rate
   1159     samplingFreq = 44100;
   1160     if (_samplingFreqOut != 44)
   1161     {
   1162       samplingFreq = _samplingFreqOut * 1000;
   1163     }
   1164     WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id,
   1165                  "  Trying configured playback sampling rate %d",
   1166                  samplingFreq);
   1167   }
   1168   else
   1169   {
   1170     // set the preferred sampling frequency
   1171     if (samplingFreq == 8000)
   1172     {
   1173       // try 16000
   1174       samplingFreq = 16000;
   1175     }
   1176     // else use same as recording
   1177   }
   1178 
   1179   bool keepTrying = true;
   1180   while (keepTrying)
   1181   {
   1182     // call java sc object method
   1183     res = env->CallIntMethod(_javaScObj, initPlaybackID, samplingFreq);
   1184     if (res < 0)
   1185     {
   1186       switch (samplingFreq)
   1187       {
   1188         case 44100:
   1189           samplingFreq = 16000;
   1190           break;
   1191         case 16000:
   1192           samplingFreq = 8000;
   1193           break;
   1194         default: // error
   1195           WEBRTC_TRACE(kTraceError,
   1196                        kTraceAudioDevice, _id,
   1197                        "InitPlayback failed (%d)", res);
   1198           return -1;
   1199       }
   1200     }
   1201     else
   1202     {
   1203       keepTrying = false;
   1204     }
   1205   }
   1206 
   1207   // Store max playout volume
   1208   _maxSpeakerVolume = static_cast<uint32_t> (res);
   1209   if (_maxSpeakerVolume < 1)
   1210   {
   1211     WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
   1212                  "  Did not get valid max speaker volume value (%d)",
   1213                  _maxSpeakerVolume);
   1214   }
   1215 
   1216   // set the playback sample rate to use
   1217   if (samplingFreq == 44100)
   1218   {
   1219     _samplingFreqOut = 44;
   1220   }
   1221   else
   1222   {
   1223     _samplingFreqOut = samplingFreq / 1000;
   1224   }
   1225 
   1226   WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, _id,
   1227                "Playback sample rate set to (%d)", _samplingFreqOut);
   1228 
   1229   // get the method ID
   1230   jmethodID stopPlaybackID = env->GetMethodID(_javaScClass, "StopPlayback",
   1231                                               "()I");
   1232 
   1233   // Call java sc object method
   1234   res = env->CallIntMethod(_javaScObj, stopPlaybackID);
   1235   if (res < 0)
   1236   {
   1237     WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
   1238                  "StopPlayback failed (%d)", res);
   1239   }
   1240 
   1241   // Detach this thread if it was attached
   1242   if (isAttached)
   1243   {
   1244     if (_javaVM->DetachCurrentThread() < 0)
   1245     {
   1246       WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
   1247                    "%s: Could not detach thread from JVM", __FUNCTION__);
   1248     }
   1249   }
   1250 
   1251   return 0;
   1252 
   1253 }
   1254 
   1255 bool AudioTrackJni::PlayThreadFunc(void* pThis)
   1256 {
   1257   return (static_cast<AudioTrackJni*> (pThis)->PlayThreadProcess());
   1258 }
   1259 
   1260 bool AudioTrackJni::PlayThreadProcess()
   1261 {
   1262   if (!_playThreadIsInitialized)
   1263   {
   1264     // Do once when thread is started
   1265 
   1266     // Attach this thread to JVM and get the JNI env for this thread
   1267     jint res = _javaVM->AttachCurrentThread(&_jniEnvPlay, NULL);
   1268     if ((res < 0) || !_jniEnvPlay)
   1269     {
   1270             WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice,
   1271                          _id,
   1272                          "Could not attach playout thread to JVM (%d, %p)",
   1273                          res, _jniEnvPlay);
   1274             return false; // Close down thread
   1275     }
   1276 
   1277     _playThreadIsInitialized = true;
   1278   }
   1279 
   1280   if (!_playing)
   1281     {
   1282       switch (_timeEventPlay.Wait(1000))
   1283       {
   1284         case kEventSignaled:
   1285           WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice,
   1286                        _id, "Playout thread event signal");
   1287           _timeEventPlay.Reset();
   1288           break;
   1289         case kEventError:
   1290           WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice,
   1291                        _id, "Playout thread event error");
   1292                 return true;
   1293         case kEventTimeout:
   1294           WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice,
   1295                        _id, "Playout thread event timeout");
   1296           return true;
   1297       }
   1298     }
   1299 
   1300   Lock();
   1301 
   1302   if (_startPlay)
   1303     {
   1304       WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
   1305                    "_startPlay true, performing initial actions");
   1306       _startPlay = false;
   1307       _playing = true;
   1308       _playWarning = 0;
   1309       _playError = 0;
   1310       _playStartStopEvent.Set();
   1311       WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
   1312                    "Sent signal");
   1313     }
   1314 
   1315   if (_playing)
   1316   {
   1317     int8_t playBuffer[2 * 480]; // Max 10 ms @ 48 kHz / 16 bit
   1318     uint32_t samplesToPlay = _samplingFreqOut * 10;
   1319 
   1320     // ask for new PCM data to be played out using the AudioDeviceBuffer
   1321     // ensure that this callback is executed without taking the
   1322     // audio-thread lock
   1323     UnLock();
   1324     uint32_t nSamples =
   1325                 _ptrAudioBuffer->RequestPlayoutData(samplesToPlay);
   1326     Lock();
   1327 
   1328     // Check again since play may have stopped during unlocked period
   1329     if (!_playing)
   1330     {
   1331       UnLock();
   1332       return true;
   1333     }
   1334 
   1335     nSamples = _ptrAudioBuffer->GetPlayoutData(playBuffer);
   1336         if (nSamples != samplesToPlay)
   1337         {
   1338           WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
   1339                        "  invalid number of output samples(%d)", nSamples);
   1340           _playWarning = 1;
   1341         }
   1342 
   1343         // Copy data to our direct buffer (held by java sc object)
   1344         // todo: Give _javaDirectPlayBuffer directly to VoE?
   1345         memcpy(_javaDirectPlayBuffer, playBuffer, nSamples * 2);
   1346 
   1347         UnLock();
   1348 
   1349         // Call java sc object method to process data in direct buffer
   1350         // Will block until data has been put in OS playout buffer
   1351         // (see java sc class)
   1352         jint res = _jniEnvPlay->CallIntMethod(_javaScObj, _javaMidPlayAudio,
   1353                                               2 * nSamples);
   1354         if (res < 0)
   1355         {
   1356           WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
   1357                        "PlayAudio failed (%d)", res);
   1358             _playWarning = 1;
   1359         }
   1360         else if (res > 0)
   1361         {
   1362           // we are not recording and have got a delay value from playback
   1363           _delayPlayout = res / _samplingFreqOut;
   1364         }
   1365         Lock();
   1366 
   1367   }  // _playing
   1368 
   1369   if (_shutdownPlayThread)
   1370   {
   1371     WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
   1372                  "Detaching thread from Java VM");
   1373 
   1374     // Detach thread from Java VM
   1375     if (_javaVM->DetachCurrentThread() < 0)
   1376     {
   1377             WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice,
   1378                          _id, "Could not detach playout thread from JVM");
   1379             _shutdownPlayThread = false;
   1380             // If we say OK (i.e. set event) and close thread anyway,
   1381             // app will crash
   1382     }
   1383     else
   1384     {
   1385       _jniEnvPlay = NULL;
   1386       _shutdownPlayThread = false;
   1387       _playStartStopEvent.Set(); // Signal to Terminate() that we are done
   1388             WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
   1389                          "Sent signal");
   1390     }
   1391   }
   1392 
   1393   UnLock();
   1394   return true;
   1395 }
   1396 
   1397 }  // namespace webrtc
   1398