diff --git a/src/modules/audio_device/main/source/android/audio_device_android_jni.cc b/src/modules/audio_device/main/source/android/audio_device_android_jni.cc index 85691cdabe..4cea767f24 100644 --- a/src/modules/audio_device/main/source/android/audio_device_android_jni.cc +++ b/src/modules/audio_device/main/source/android/audio_device_android_jni.cc @@ -29,7 +29,7 @@ namespace webrtc JavaVM* AudioDeviceAndroidJni::globalJvm = NULL; JNIEnv* AudioDeviceAndroidJni::globalJNIEnv = NULL; -jobject AudioDeviceAndroidJni::globalSndContext = NULL; +jobject AudioDeviceAndroidJni::globalContext = NULL; jclass AudioDeviceAndroidJni::globalScClass = NULL; // ---------------------------------------------------------------------------- @@ -46,7 +46,6 @@ WebRtc_Word32 AudioDeviceAndroidJni::SetAndroidAudioDeviceObjects( void* context) { // TODO(leozwang): Make this function thread-safe. globalJvm = reinterpret_cast(javaVM); - globalSndContext = reinterpret_cast(context); if (env) { globalJNIEnv = reinterpret_cast(env); @@ -54,16 +53,26 @@ WebRtc_Word32 AudioDeviceAndroidJni::SetAndroidAudioDeviceObjects( jclass javaScClassLocal = globalJNIEnv->FindClass( "org/webrtc/voiceengine/AudioDeviceAndroid"); if (!javaScClassLocal) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, + "%s: could not find java class", __FUNCTION__); return -1; // exception thrown } - else { - } // Create a global reference to the class (to tell JNI that we are // referencing it after this function has returned). globalScClass = reinterpret_cast ( globalJNIEnv->NewGlobalRef(javaScClassLocal)); if (!globalScClass) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, + "%s: could not create reference", __FUNCTION__); + return -1; + } + + globalContext = globalJNIEnv->NewGlobalRef( + reinterpret_cast(context)); + if (!globalContext) { + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, + "%s: could not create context reference", __FUNCTION__); return -1; } @@ -71,12 +80,17 @@ WebRtc_Word32 AudioDeviceAndroidJni::SetAndroidAudioDeviceObjects( globalJNIEnv->DeleteLocalRef(javaScClassLocal); } else { // User is resetting the env variable + WEBRTC_TRACE(kTraceStateInfo, kTraceAudioDevice, -1, + "%s: env is NULL, assuming deinit", __FUNCTION__); + if (!globalJNIEnv) { + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, -1, + "%s: saved env already NULL", __FUNCTION__); return 0; } globalJNIEnv->DeleteGlobalRef(globalScClass); - globalJNIEnv = (JNIEnv *) NULL; + globalJNIEnv = reinterpret_cast(NULL); } return 0; @@ -406,6 +420,9 @@ WebRtc_Word32 AudioDeviceAndroidJni::Terminate() _javaDirectPlayBuffer = NULL; _javaDirectRecBuffer = NULL; + env->DeleteGlobalRef(_javaContext); + _javaContext = NULL; + // Delete the references to the java buffers, this allows the // garbage collector to delete them env->DeleteGlobalRef(_javaPlayBuffer); @@ -2144,7 +2161,7 @@ WebRtc_Word32 AudioDeviceAndroidJni::InitJavaResources() { // todo: Check if we already have created the java object _javaVM = globalJvm; - _javaContext = globalSndContext; + _javaContext = globalContext; _javaScClass = globalScClass; // use the jvm that has been set @@ -2199,7 +2216,7 @@ WebRtc_Word32 AudioDeviceAndroidJni::InitJavaResources() return -1; } - // create a reference to the object (to tell JNI that we are referencing it + // Create a reference to the object (to tell JNI that we are referencing it // after this function has returned). _javaScObj = env->NewGlobalRef(javaScObjLocal); if (!_javaScObj) @@ -2215,29 +2232,25 @@ WebRtc_Word32 AudioDeviceAndroidJni::InitJavaResources() ////////////////////// // AUDIO MANAGEMENT - // This is not mandatory functionality. + + // This is not mandatory functionality if (_javaContext) { - // Get Context field ID - jfieldID fidContext = env->GetFieldID(_javaScClass, "_context", + jfieldID context_id = env->GetFieldID(globalScClass, + "_context", "Landroid/content/Context;"); - if (!fidContext) { + if (!context_id) { WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: could not get Context fid", __FUNCTION__); + "%s: could not get _context id", __FUNCTION__); return -1; } - // Set the Java application Context so we can use AudioManager - // Get Context object and check it. - jobject javaContext = (jobject) _javaContext; - env->SetObjectField(_javaScObj, fidContext, javaContext); - javaContext = env->GetObjectField(_javaScObj, fidContext); + env->SetObjectField(_javaScObj, context_id, globalContext); + jobject javaContext = env->GetObjectField(_javaScObj, context_id); if (!javaContext) { WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, - "%s: could not set Context", __FUNCTION__); + "%s: could not set or get _context", __FUNCTION__); return -1; } - // Delete local object ref. - env->DeleteLocalRef(javaContext); } else { WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, diff --git a/src/modules/audio_device/main/source/android/audio_device_android_jni.h b/src/modules/audio_device/main/source/android/audio_device_android_jni.h index e35cdaac01..1e292e3035 100644 --- a/src/modules/audio_device/main/source/android/audio_device_android_jni.h +++ b/src/modules/audio_device/main/source/android/audio_device_android_jni.h @@ -24,8 +24,8 @@ namespace webrtc { class EventWrapper; -const WebRtc_UWord32 N_REC_SAMPLES_PER_SEC = 44000; // Default is 44.1 kHz -const WebRtc_UWord32 N_PLAY_SAMPLES_PER_SEC = 44000; // Default is 44.1 kHz +const WebRtc_UWord32 N_REC_SAMPLES_PER_SEC = 16000; // Default is 16 kHz +const WebRtc_UWord32 N_PLAY_SAMPLES_PER_SEC = 16000; // Default is 16 kHz const WebRtc_UWord32 N_REC_CHANNELS = 1; // default is mono recording const WebRtc_UWord32 N_PLAY_CHANNELS = 1; // default is mono playout @@ -264,7 +264,7 @@ class AudioDeviceAndroidJni : public AudioDeviceGeneric { // reliable. Chromium has a good example at base/android. static JavaVM* globalJvm; static JNIEnv* globalJNIEnv; - static jobject globalSndContext; + static jobject globalContext; static jclass globalScClass; }; diff --git a/src/modules/audio_device/main/source/android/org/webrtc/voiceengine/AudioDeviceAndroid.java b/src/modules/audio_device/main/source/android/org/webrtc/voiceengine/AudioDeviceAndroid.java index b56085bd3c..2a31a2da98 100644 --- a/src/modules/audio_device/main/source/android/org/webrtc/voiceengine/AudioDeviceAndroid.java +++ b/src/modules/audio_device/main/source/android/org/webrtc/voiceengine/AudioDeviceAndroid.java @@ -1,509 +1,504 @@ -/* - * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -/* - * Android audio device test app - */ - -package org.webrtc.voiceengine; - -import java.nio.ByteBuffer; -import java.util.concurrent.locks.ReentrantLock; - -import android.content.Context; -import android.media.AudioFormat; -import android.media.AudioManager; -import android.media.AudioRecord; -import android.media.AudioTrack; -import android.util.Log; - - -class AudioDeviceAndroid { - private AudioTrack _audioTrack = null; - private AudioRecord _audioRecord = null; - - private Context _context; - private AudioManager _audioManager; - - private ByteBuffer _playBuffer; - private ByteBuffer _recBuffer; - private byte[] _tempBufPlay; - private byte[] _tempBufRec; - - private final ReentrantLock _playLock = new ReentrantLock(); - private final ReentrantLock _recLock = new ReentrantLock(); - - private boolean _doPlayInit = true; - private boolean _doRecInit = true; - private boolean _isRecording = false; - private boolean _isPlaying = false; - - private int _bufferedRecSamples = 0; - private int _bufferedPlaySamples = 0; - private int _playPosition = 0; - - AudioDeviceAndroid() { - try { - _playBuffer = ByteBuffer.allocateDirect(2 * 480); // Max 10 ms @ 48 - // kHz - _recBuffer = ByteBuffer.allocateDirect(2 * 480); // Max 10 ms @ 48 - // kHz - } catch (Exception e) { - DoLog(e.getMessage()); - } - - _tempBufPlay = new byte[2 * 480]; - _tempBufRec = new byte[2 * 480]; - } - - @SuppressWarnings("unused") - private int InitRecording(int audioSource, int sampleRate) { - // get the minimum buffer size that can be used - int minRecBufSize = - AudioRecord.getMinBufferSize(sampleRate, - AudioFormat.CHANNEL_CONFIGURATION_MONO, - AudioFormat.ENCODING_PCM_16BIT); - - // DoLog("min rec buf size is " + minRecBufSize); - - // double size to be more safe - int recBufSize = minRecBufSize * 2; - _bufferedRecSamples = (5 * sampleRate) / 200; - // DoLog("rough rec delay set to " + _bufferedRecSamples); - - // release the object - if (_audioRecord != null) { - _audioRecord.release(); - _audioRecord = null; - } - - try { - _audioRecord = new AudioRecord( - audioSource, - sampleRate, - AudioFormat.CHANNEL_CONFIGURATION_MONO, - AudioFormat.ENCODING_PCM_16BIT, - recBufSize); - - } catch (Exception e) { - DoLog(e.getMessage()); - return -1; - } - - // check that the audioRecord is ready to be used - if (_audioRecord.getState() != AudioRecord.STATE_INITIALIZED) { - // DoLog("rec not initialized " + sampleRate); - return -1; - } - - // DoLog("rec sample rate set to " + sampleRate); - - return _bufferedRecSamples; - } - - @SuppressWarnings("unused") - private int StartRecording() { - if (_isPlaying == false) { - SetAudioMode(true); - } - - // start recording - try { - _audioRecord.startRecording(); - - } catch (IllegalStateException e) { - e.printStackTrace(); - return -1; - } - - _isRecording = true; - return 0; - } - - @SuppressWarnings("unused") - private int InitPlayback(int sampleRate) { - // get the minimum buffer size that can be used - int minPlayBufSize = - AudioTrack.getMinBufferSize(sampleRate, - AudioFormat.CHANNEL_CONFIGURATION_MONO, - AudioFormat.ENCODING_PCM_16BIT); - - // DoLog("min play buf size is " + minPlayBufSize); - - int playBufSize = minPlayBufSize; - if (playBufSize < 6000) { - playBufSize *= 2; - } - _bufferedPlaySamples = 0; - // DoLog("play buf size is " + playBufSize); - - // release the object - if (_audioTrack != null) { - _audioTrack.release(); - _audioTrack = null; - } - - try { - _audioTrack = new AudioTrack( - AudioManager.STREAM_VOICE_CALL, - sampleRate, - AudioFormat.CHANNEL_CONFIGURATION_MONO, - AudioFormat.ENCODING_PCM_16BIT, - playBufSize, AudioTrack.MODE_STREAM); - } catch (Exception e) { - DoLog(e.getMessage()); - return -1; - } - - // check that the audioRecord is ready to be used - if (_audioTrack.getState() != AudioTrack.STATE_INITIALIZED) { - // DoLog("play not initialized " + sampleRate); - return -1; - } - - // DoLog("play sample rate set to " + sampleRate); - - if (_audioManager == null && _context != null) { - _audioManager = (AudioManager) - _context.getSystemService(Context.AUDIO_SERVICE); - } - - // Return max playout volume - if (_audioManager == null) { - // Don't know the max volume but still init is OK for playout, - // so we should not return error. - return 0; - } - return _audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL); - } - - @SuppressWarnings("unused") - private int StartPlayback() { - if (_isRecording == false) { - SetAudioMode(true); - } - - // start playout - try { - _audioTrack.play(); - - } catch (IllegalStateException e) { - e.printStackTrace(); - return -1; - } - - _isPlaying = true; - return 0; - } - - @SuppressWarnings("unused") - private int StopRecording() { - _recLock.lock(); - try { - // only stop if we are recording - if (_audioRecord.getRecordingState() == - AudioRecord.RECORDSTATE_RECORDING) { - // stop recording - try { - _audioRecord.stop(); - } catch (IllegalStateException e) { - e.printStackTrace(); - return -1; - } - } - - // release the object - _audioRecord.release(); - _audioRecord = null; - - } finally { - // Ensure we always unlock, both for success, exception or error - // return. - _doRecInit = true; - _recLock.unlock(); - } - - if (_isPlaying == false) { - SetAudioMode(false); - } - - _isRecording = false; - return 0; - } - - @SuppressWarnings("unused") - private int StopPlayback() { - _playLock.lock(); - try { - // only stop if we are playing - if (_audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { - // stop playout - try { - _audioTrack.stop(); - } catch (IllegalStateException e) { - e.printStackTrace(); - return -1; - } - - // flush the buffers - _audioTrack.flush(); - } - - // release the object - _audioTrack.release(); - _audioTrack = null; - - } finally { - // Ensure we always unlock, both for success, exception or error - // return. - _doPlayInit = true; - _playLock.unlock(); - } - - if (_isRecording == false) { - SetAudioMode(false); - } - - _isPlaying = false; - return 0; - } - - @SuppressWarnings("unused") - private int PlayAudio(int lengthInBytes) { - - int bufferedSamples = 0; - - _playLock.lock(); - try { - if (_audioTrack == null) { - return -2; // We have probably closed down while waiting for - // play lock - } - - // Set priority, only do once - if (_doPlayInit == true) { - try { - android.os.Process.setThreadPriority( - android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); - } catch (Exception e) { - DoLog("Set play thread priority failed: " + e.getMessage()); - } - _doPlayInit = false; - } - - int written = 0; - _playBuffer.get(_tempBufPlay); - written = _audioTrack.write(_tempBufPlay, 0, lengthInBytes); - _playBuffer.rewind(); // Reset the position to start of buffer - - // DoLog("Wrote data to sndCard"); - - // increase by number of written samples - _bufferedPlaySamples += (written >> 1); - - // decrease by number of played samples - int pos = _audioTrack.getPlaybackHeadPosition(); - if (pos < _playPosition) { // wrap or reset by driver - _playPosition = 0; // reset - } - _bufferedPlaySamples -= (pos - _playPosition); - _playPosition = pos; - - if (!_isRecording) { - bufferedSamples = _bufferedPlaySamples; - } - - if (written != lengthInBytes) { - // DoLog("Could not write all data to sc (written = " + written - // + ", length = " + lengthInBytes + ")"); - return -1; - } - - } finally { - // Ensure we always unlock, both for success, exception or error - // return. - _playLock.unlock(); - } - - return bufferedSamples; - } - - @SuppressWarnings("unused") - private int RecordAudio(int lengthInBytes) { - _recLock.lock(); - - try { - if (_audioRecord == null) { - return -2; // We have probably closed down while waiting for rec - // lock - } - - // Set priority, only do once - if (_doRecInit == true) { - try { - android.os.Process.setThreadPriority( - android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); - } catch (Exception e) { - DoLog("Set rec thread priority failed: " + e.getMessage()); - } - _doRecInit = false; - } - - int readBytes = 0; - _recBuffer.rewind(); // Reset the position to start of buffer - readBytes = _audioRecord.read(_tempBufRec, 0, lengthInBytes); - // DoLog("read " + readBytes + "from SC"); - _recBuffer.put(_tempBufRec); - - if (readBytes != lengthInBytes) { - // DoLog("Could not read all data from sc (read = " + readBytes - // + ", length = " + lengthInBytes + ")"); - return -1; - } - - } catch (Exception e) { - DoLogErr("RecordAudio try failed: " + e.getMessage()); - - } finally { - // Ensure we always unlock, both for success, exception or error - // return. - _recLock.unlock(); - } - - return (_bufferedPlaySamples); - } - - @SuppressWarnings("unused") - private int SetPlayoutSpeaker(boolean loudspeakerOn) { - // create audio manager if needed - if (_audioManager == null && _context != null) { - _audioManager = (AudioManager) - _context.getSystemService(Context.AUDIO_SERVICE); - } - - if (_audioManager == null) { - DoLogErr("Could not change audio routing - no audio manager"); - return -1; - } - - int apiLevel = Integer.parseInt(android.os.Build.VERSION.SDK); - - if ((3 == apiLevel) || (4 == apiLevel)) { - // 1.5 and 1.6 devices - if (loudspeakerOn) { - // route audio to back speaker - _audioManager.setMode(AudioManager.MODE_NORMAL); - } else { - // route audio to earpiece - _audioManager.setMode(AudioManager.MODE_IN_CALL); - } - } else { - // 2.x devices - if ((android.os.Build.BRAND.equals("Samsung") || - android.os.Build.BRAND.equals("samsung")) && - ((5 == apiLevel) || (6 == apiLevel) || - (7 == apiLevel))) { - // Samsung 2.0, 2.0.1 and 2.1 devices - if (loudspeakerOn) { - // route audio to back speaker - _audioManager.setMode(AudioManager.MODE_IN_CALL); - _audioManager.setSpeakerphoneOn(loudspeakerOn); - } else { - // route audio to earpiece - _audioManager.setSpeakerphoneOn(loudspeakerOn); - _audioManager.setMode(AudioManager.MODE_NORMAL); - } - } else { - // Non-Samsung and Samsung 2.2 and up devices - _audioManager.setSpeakerphoneOn(loudspeakerOn); - } - } - - return 0; - } - - @SuppressWarnings("unused") - private int SetPlayoutVolume(int level) { - - // create audio manager if needed - if (_audioManager == null && _context != null) { - _audioManager = (AudioManager) - _context.getSystemService(Context.AUDIO_SERVICE); - } - - int retVal = -1; - - if (_audioManager != null) { - _audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, - level, 0); - retVal = 0; - } - - return retVal; - } - - @SuppressWarnings("unused") - private int GetPlayoutVolume() { - - // create audio manager if needed - if (_audioManager == null && _context != null) { - _audioManager = (AudioManager) - _context.getSystemService(Context.AUDIO_SERVICE); - } - - int level = -1; - - if (_audioManager != null) { - level = _audioManager.getStreamVolume( - AudioManager.STREAM_VOICE_CALL); - } - - return level; - } - - private void SetAudioMode(boolean startCall) { - int apiLevel = Integer.parseInt(android.os.Build.VERSION.SDK); - - if (_audioManager == null && _context != null) { - _audioManager = (AudioManager) - _context.getSystemService(Context.AUDIO_SERVICE); - } - - if (_audioManager == null) { - DoLogErr("Could not set audio mode - no audio manager"); - return; - } - - // ***IMPORTANT*** When the API level for honeycomb (H) has been - // decided, - // the condition should be changed to include API level 8 to H-1. - if ((android.os.Build.BRAND.equals("Samsung") || android.os.Build.BRAND - .equals("samsung")) && (8 == apiLevel)) { - // Set Samsung specific VoIP mode for 2.2 devices - int mode = - (startCall ? 4 /* VoIP mode */ - : AudioManager.MODE_NORMAL); - _audioManager.setMode(mode); - if (_audioManager.getMode() != mode) { - DoLogErr("Could not set audio mode for Samsung device"); - } - } - } - - final String logTag = "WebRTC AD java"; - - private void DoLog(String msg) { - Log.d(logTag, msg); - } - - private void DoLogErr(String msg) { - Log.e(logTag, msg); - } -} +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +package org.webrtc.voiceengine; + +import java.nio.ByteBuffer; +import java.util.concurrent.locks.ReentrantLock; + +import android.content.Context; +import android.media.AudioFormat; +import android.media.AudioManager; +import android.media.AudioRecord; +import android.media.AudioTrack; +import android.util.Log; + +class AudioDeviceAndroid { + private AudioTrack _audioTrack = null; + private AudioRecord _audioRecord = null; + + private Context _context; + private AudioManager _audioManager; + + private ByteBuffer _playBuffer; + private ByteBuffer _recBuffer; + private byte[] _tempBufPlay; + private byte[] _tempBufRec; + + private final ReentrantLock _playLock = new ReentrantLock(); + private final ReentrantLock _recLock = new ReentrantLock(); + + private boolean _doPlayInit = true; + private boolean _doRecInit = true; + private boolean _isRecording = false; + private boolean _isPlaying = false; + + private int _bufferedRecSamples = 0; + private int _bufferedPlaySamples = 0; + private int _playPosition = 0; + + AudioDeviceAndroid() { + try { + _playBuffer = ByteBuffer.allocateDirect(2 * 480); // Max 10 ms @ 48 + // kHz + _recBuffer = ByteBuffer.allocateDirect(2 * 480); // Max 10 ms @ 48 + // kHz + } catch (Exception e) { + DoLog(e.getMessage()); + } + + _tempBufPlay = new byte[2 * 480]; + _tempBufRec = new byte[2 * 480]; + } + + @SuppressWarnings("unused") + private int InitRecording(int audioSource, int sampleRate) { + // get the minimum buffer size that can be used + int minRecBufSize = + AudioRecord.getMinBufferSize(sampleRate, + AudioFormat.CHANNEL_CONFIGURATION_MONO, + AudioFormat.ENCODING_PCM_16BIT); + + // DoLog("min rec buf size is " + minRecBufSize); + + // double size to be more safe + int recBufSize = minRecBufSize * 2; + _bufferedRecSamples = (5 * sampleRate) / 200; + // DoLog("rough rec delay set to " + _bufferedRecSamples); + + // release the object + if (_audioRecord != null) { + _audioRecord.release(); + _audioRecord = null; + } + + try { + _audioRecord = new AudioRecord( + audioSource, + sampleRate, + AudioFormat.CHANNEL_CONFIGURATION_MONO, + AudioFormat.ENCODING_PCM_16BIT, + recBufSize); + + } catch (Exception e) { + DoLog(e.getMessage()); + return -1; + } + + // check that the audioRecord is ready to be used + if (_audioRecord.getState() != AudioRecord.STATE_INITIALIZED) { + // DoLog("rec not initialized " + sampleRate); + return -1; + } + + // DoLog("rec sample rate set to " + sampleRate); + + return _bufferedRecSamples; + } + + @SuppressWarnings("unused") + private int StartRecording() { + if (_isPlaying == false) { + SetAudioMode(true); + } + + // start recording + try { + _audioRecord.startRecording(); + + } catch (IllegalStateException e) { + e.printStackTrace(); + return -1; + } + + _isRecording = true; + return 0; + } + + @SuppressWarnings("unused") + private int InitPlayback(int sampleRate) { + // get the minimum buffer size that can be used + int minPlayBufSize = + AudioTrack.getMinBufferSize(sampleRate, + AudioFormat.CHANNEL_CONFIGURATION_MONO, + AudioFormat.ENCODING_PCM_16BIT); + + // DoLog("min play buf size is " + minPlayBufSize); + + int playBufSize = minPlayBufSize; + if (playBufSize < 6000) { + playBufSize *= 2; + } + _bufferedPlaySamples = 0; + // DoLog("play buf size is " + playBufSize); + + // release the object + if (_audioTrack != null) { + _audioTrack.release(); + _audioTrack = null; + } + + try { + _audioTrack = new AudioTrack( + AudioManager.STREAM_VOICE_CALL, + sampleRate, + AudioFormat.CHANNEL_CONFIGURATION_MONO, + AudioFormat.ENCODING_PCM_16BIT, + playBufSize, AudioTrack.MODE_STREAM); + } catch (Exception e) { + DoLog(e.getMessage()); + return -1; + } + + // check that the audioRecord is ready to be used + if (_audioTrack.getState() != AudioTrack.STATE_INITIALIZED) { + // DoLog("play not initialized " + sampleRate); + return -1; + } + + // DoLog("play sample rate set to " + sampleRate); + + if (_audioManager == null && _context != null) { + _audioManager = (AudioManager) + _context.getSystemService(Context.AUDIO_SERVICE); + } + + // Return max playout volume + if (_audioManager == null) { + // Don't know the max volume but still init is OK for playout, + // so we should not return error. + return 0; + } + return _audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL); + } + + @SuppressWarnings("unused") + private int StartPlayback() { + if (_isRecording == false) { + SetAudioMode(true); + } + + // start playout + try { + _audioTrack.play(); + + } catch (IllegalStateException e) { + e.printStackTrace(); + return -1; + } + + _isPlaying = true; + return 0; + } + + @SuppressWarnings("unused") + private int StopRecording() { + _recLock.lock(); + try { + // only stop if we are recording + if (_audioRecord.getRecordingState() == + AudioRecord.RECORDSTATE_RECORDING) { + // stop recording + try { + _audioRecord.stop(); + } catch (IllegalStateException e) { + e.printStackTrace(); + return -1; + } + } + + // release the object + _audioRecord.release(); + _audioRecord = null; + + } finally { + // Ensure we always unlock, both for success, exception or error + // return. + _doRecInit = true; + _recLock.unlock(); + } + + if (_isPlaying == false) { + SetAudioMode(false); + } + + _isRecording = false; + return 0; + } + + @SuppressWarnings("unused") + private int StopPlayback() { + _playLock.lock(); + try { + // only stop if we are playing + if (_audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { + // stop playout + try { + _audioTrack.stop(); + } catch (IllegalStateException e) { + e.printStackTrace(); + return -1; + } + + // flush the buffers + _audioTrack.flush(); + } + + // release the object + _audioTrack.release(); + _audioTrack = null; + + } finally { + // Ensure we always unlock, both for success, exception or error + // return. + _doPlayInit = true; + _playLock.unlock(); + } + + if (_isRecording == false) { + SetAudioMode(false); + } + + _isPlaying = false; + return 0; + } + + @SuppressWarnings("unused") + private int PlayAudio(int lengthInBytes) { + + int bufferedSamples = 0; + + _playLock.lock(); + try { + if (_audioTrack == null) { + return -2; // We have probably closed down while waiting for + // play lock + } + + // Set priority, only do once + if (_doPlayInit == true) { + try { + android.os.Process.setThreadPriority( + android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); + } catch (Exception e) { + DoLog("Set play thread priority failed: " + e.getMessage()); + } + _doPlayInit = false; + } + + int written = 0; + _playBuffer.get(_tempBufPlay); + written = _audioTrack.write(_tempBufPlay, 0, lengthInBytes); + _playBuffer.rewind(); // Reset the position to start of buffer + + // DoLog("Wrote data to sndCard"); + + // increase by number of written samples + _bufferedPlaySamples += (written >> 1); + + // decrease by number of played samples + int pos = _audioTrack.getPlaybackHeadPosition(); + if (pos < _playPosition) { // wrap or reset by driver + _playPosition = 0; // reset + } + _bufferedPlaySamples -= (pos - _playPosition); + _playPosition = pos; + + if (!_isRecording) { + bufferedSamples = _bufferedPlaySamples; + } + + if (written != lengthInBytes) { + // DoLog("Could not write all data to sc (written = " + written + // + ", length = " + lengthInBytes + ")"); + return -1; + } + + } finally { + // Ensure we always unlock, both for success, exception or error + // return. + _playLock.unlock(); + } + + return bufferedSamples; + } + + @SuppressWarnings("unused") + private int RecordAudio(int lengthInBytes) { + _recLock.lock(); + + try { + if (_audioRecord == null) { + return -2; // We have probably closed down while waiting for rec + // lock + } + + // Set priority, only do once + if (_doRecInit == true) { + try { + android.os.Process.setThreadPriority( + android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); + } catch (Exception e) { + DoLog("Set rec thread priority failed: " + e.getMessage()); + } + _doRecInit = false; + } + + int readBytes = 0; + _recBuffer.rewind(); // Reset the position to start of buffer + readBytes = _audioRecord.read(_tempBufRec, 0, lengthInBytes); + // DoLog("read " + readBytes + "from SC"); + _recBuffer.put(_tempBufRec); + + if (readBytes != lengthInBytes) { + // DoLog("Could not read all data from sc (read = " + readBytes + // + ", length = " + lengthInBytes + ")"); + return -1; + } + + } catch (Exception e) { + DoLogErr("RecordAudio try failed: " + e.getMessage()); + + } finally { + // Ensure we always unlock, both for success, exception or error + // return. + _recLock.unlock(); + } + + return (_bufferedPlaySamples); + } + + @SuppressWarnings("unused") + private int SetPlayoutSpeaker(boolean loudspeakerOn) { + // create audio manager if needed + if (_audioManager == null && _context != null) { + _audioManager = (AudioManager) + _context.getSystemService(Context.AUDIO_SERVICE); + } + + if (_audioManager == null) { + DoLogErr("Could not change audio routing - no audio manager"); + return -1; + } + + int apiLevel = Integer.parseInt(android.os.Build.VERSION.SDK); + + if ((3 == apiLevel) || (4 == apiLevel)) { + // 1.5 and 1.6 devices + if (loudspeakerOn) { + // route audio to back speaker + _audioManager.setMode(AudioManager.MODE_NORMAL); + } else { + // route audio to earpiece + _audioManager.setMode(AudioManager.MODE_IN_CALL); + } + } else { + // 2.x devices + if ((android.os.Build.BRAND.equals("Samsung") || + android.os.Build.BRAND.equals("samsung")) && + ((5 == apiLevel) || (6 == apiLevel) || + (7 == apiLevel))) { + // Samsung 2.0, 2.0.1 and 2.1 devices + if (loudspeakerOn) { + // route audio to back speaker + _audioManager.setMode(AudioManager.MODE_IN_CALL); + _audioManager.setSpeakerphoneOn(loudspeakerOn); + } else { + // route audio to earpiece + _audioManager.setSpeakerphoneOn(loudspeakerOn); + _audioManager.setMode(AudioManager.MODE_NORMAL); + } + } else { + // Non-Samsung and Samsung 2.2 and up devices + _audioManager.setSpeakerphoneOn(loudspeakerOn); + } + } + + return 0; + } + + @SuppressWarnings("unused") + private int SetPlayoutVolume(int level) { + + // create audio manager if needed + if (_audioManager == null && _context != null) { + _audioManager = (AudioManager) + _context.getSystemService(Context.AUDIO_SERVICE); + } + + int retVal = -1; + + if (_audioManager != null) { + _audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, + level, 0); + retVal = 0; + } + + return retVal; + } + + @SuppressWarnings("unused") + private int GetPlayoutVolume() { + + // create audio manager if needed + if (_audioManager == null && _context != null) { + _audioManager = (AudioManager) + _context.getSystemService(Context.AUDIO_SERVICE); + } + + int level = -1; + + if (_audioManager != null) { + level = _audioManager.getStreamVolume( + AudioManager.STREAM_VOICE_CALL); + } + + return level; + } + + private void SetAudioMode(boolean startCall) { + int apiLevel = Integer.parseInt(android.os.Build.VERSION.SDK); + + if (_audioManager == null && _context != null) { + _audioManager = (AudioManager) + _context.getSystemService(Context.AUDIO_SERVICE); + } + + if (_audioManager == null) { + DoLogErr("Could not set audio mode - no audio manager"); + return; + } + + // ***IMPORTANT*** When the API level for honeycomb (H) has been + // decided, + // the condition should be changed to include API level 8 to H-1. + if ((android.os.Build.BRAND.equals("Samsung") || android.os.Build.BRAND + .equals("samsung")) && (8 == apiLevel)) { + // Set Samsung specific VoIP mode for 2.2 devices + int mode = + (startCall ? 4 /* VoIP mode */ + : AudioManager.MODE_NORMAL); + _audioManager.setMode(mode); + if (_audioManager.getMode() != mode) { + DoLogErr("Could not set audio mode for Samsung device"); + } + } + } + + final String logTag = "WebRTC AD java"; + + private void DoLog(String msg) { + Log.d(logTag, msg); + } + + private void DoLogErr(String msg) { + Log.e(logTag, msg); + } +} diff --git a/src/video_engine/main/test/android_test/jni/org_webrtc_videoengineapp_vie_android_java_api.h b/src/video_engine/main/test/android_test/jni/org_webrtc_videoengineapp_vie_android_java_api.h index f2beb409ae..935f4c1cd6 100644 --- a/src/video_engine/main/test/android_test/jni/org_webrtc_videoengineapp_vie_android_java_api.h +++ b/src/video_engine/main/test/android_test/jni/org_webrtc_videoengineapp_vie_android_java_api.h @@ -238,10 +238,10 @@ JNIEXPORT jint JNICALL Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_StopInco /* * Class: org_webrtc_videoengineapp_ViEAndroidJavaAPI * Method: VoE_Create - * Signature: ()Z + * Signature: (Landroid/content/Context)Z */ JNIEXPORT jboolean JNICALL Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_VoE_1Create - (JNIEnv *, jobject); + (JNIEnv *, jobject, jobject); /* * Class: org_webrtc_videoengineapp_ViEAndroidJavaAPI diff --git a/src/video_engine/main/test/android_test/jni/vie_android_java_api.cc b/src/video_engine/main/test/android_test/jni/vie_android_java_api.cc index ed06e54d4c..82884cc876 100644 --- a/src/video_engine/main/test/android_test/jni/vie_android_java_api.cc +++ b/src/video_engine/main/test/android_test/jni/vie_android_java_api.cc @@ -225,7 +225,7 @@ public: }; // JNI_OnLoad -jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) { +jint JNI_OnLoad(JavaVM* vm, void* reserved) { webrtcGlobalVM = vm; if (!webrtcGlobalVM) { @@ -1099,15 +1099,15 @@ JNIEXPORT jint JNICALL Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_StopInco /* * Class: org_webrtc_videoengineapp_ViEAndroidJavaAPI * Method: VoE_Create - * Signature: ()Z + * Signature: (Landroid/content/Context)Z */ JNIEXPORT jboolean JNICALL Java_org_webrtc_videoengineapp_ViEAndroidJavaAPI_VoE_1Create( JNIEnv *env, - jobject context) -{ + jobject context, + jobject ctx) { __android_log_write(ANDROID_LOG_DEBUG, WEBRTC_LOG_TAG, "Create VoiceEngine"); - VoiceEngine::SetAndroidObjects(webrtcGlobalVM, env, context); + VoiceEngine::SetAndroidObjects(webrtcGlobalVM, env, ctx); // Check if already created if (voeData.ve) { diff --git a/src/video_engine/main/test/android_test/src/org/webrtc/videoengineapp/ViEAndroidJavaAPI.java b/src/video_engine/main/test/android_test/src/org/webrtc/videoengineapp/ViEAndroidJavaAPI.java index 9fd060eeb7..4b5cfbb971 100644 --- a/src/video_engine/main/test/android_test/src/org/webrtc/videoengineapp/ViEAndroidJavaAPI.java +++ b/src/video_engine/main/test/android_test/src/org/webrtc/videoengineapp/ViEAndroidJavaAPI.java @@ -86,7 +86,7 @@ public class ViEAndroidJavaAPI { // Voice Engine API // Create and Delete functions - public native boolean VoE_Create(); + public native boolean VoE_Create(Context context); public native boolean VoE_Delete(); // Initialization and Termination functions diff --git a/src/video_engine/main/test/android_test/src/org/webrtc/videoengineapp/WebRTCDemo.java b/src/video_engine/main/test/android_test/src/org/webrtc/videoengineapp/WebRTCDemo.java index 2d1331c260..56745bc0da 100644 --- a/src/video_engine/main/test/android_test/src/org/webrtc/videoengineapp/WebRTCDemo.java +++ b/src/video_engine/main/test/android_test/src/org/webrtc/videoengineapp/WebRTCDemo.java @@ -671,7 +671,7 @@ public class WebRTCDemo extends TabActivity implements IViEAndroidCallback, private int SetupVoE() { // Create VoiceEngine // Error logging is done in native API wrapper - ViEAndroidAPI.VoE_Create(); + ViEAndroidAPI.VoE_Create(getApplicationContext()); // Initialize if (0 != ViEAndroidAPI.VoE_Init(enableTrace)) {