| /* |
| * Copyright (c) 2013 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. |
| */ |
| |
| // This file contains JNI for the voice engine interfaces. |
| // The native functions are found using jni's auto discovery. |
| |
| #include "webrtc/examples/android/media_demo/jni/voice_engine_jni.h" |
| |
| #include <map> |
| #include <string> |
| |
| #include "webrtc/base/arraysize.h" |
| #include "webrtc/examples/android/media_demo/jni/jni_helpers.h" |
| #include "webrtc/modules/utility/interface/helpers_android.h" |
| #include "webrtc/test/channel_transport/include/channel_transport.h" |
| #include "webrtc/voice_engine/include/voe_audio_processing.h" |
| #include "webrtc/voice_engine/include/voe_base.h" |
| #include "webrtc/voice_engine/include/voe_codec.h" |
| #include "webrtc/voice_engine/include/voe_file.h" |
| #include "webrtc/voice_engine/include/voe_hardware.h" |
| #include "webrtc/voice_engine/include/voe_network.h" |
| #include "webrtc/voice_engine/include/voe_rtp_rtcp.h" |
| #include "webrtc/voice_engine/include/voe_volume_control.h" |
| |
| // Macro for native functions that can be found by way of jni-auto discovery. |
| // Note extern "C" is needed for "discovery" of native methods to work. |
| #define JOWW(rettype, name) \ |
| extern "C" rettype JNIEXPORT JNICALL Java_org_webrtc_webrtcdemo_##name |
| |
| namespace { |
| |
| static JavaVM* g_vm = NULL; |
| static ClassReferenceHolder* g_class_reference_holder = NULL; |
| |
| jclass GetClass(JNIEnv* jni, const char* name) { |
| CHECK(g_class_reference_holder, "Class reference holder NULL"); |
| return g_class_reference_holder->GetClass(name); |
| } |
| |
| static const char* g_classes[] = {"org/webrtc/webrtcdemo/CodecInst"}; |
| |
| template<typename T> |
| void ReleaseSubApi(T instance) { |
| CHECK(instance->Release() >= 0, "failed to release instance") |
| } |
| |
| class VoiceEngineData { |
| public: |
| VoiceEngineData() |
| : ve(webrtc::VoiceEngine::Create()), |
| base(webrtc::VoEBase::GetInterface(ve)), |
| codec(webrtc::VoECodec::GetInterface(ve)), |
| file(webrtc::VoEFile::GetInterface(ve)), |
| netw(webrtc::VoENetwork::GetInterface(ve)), |
| apm(webrtc::VoEAudioProcessing::GetInterface(ve)), |
| volume(webrtc::VoEVolumeControl::GetInterface(ve)), |
| hardware(webrtc::VoEHardware::GetInterface(ve)), |
| rtp(webrtc::VoERTP_RTCP::GetInterface(ve)) { |
| CHECK(ve != NULL, "Voice engine instance failed to be created"); |
| CHECK(base != NULL, "Failed to acquire base interface"); |
| CHECK(codec != NULL, "Failed to acquire codec interface"); |
| CHECK(file != NULL, "Failed to acquire file interface"); |
| CHECK(netw != NULL, "Failed to acquire netw interface"); |
| CHECK(apm != NULL, "Failed to acquire apm interface"); |
| CHECK(volume != NULL, "Failed to acquire volume interface"); |
| CHECK(hardware != NULL, "Failed to acquire hardware interface"); |
| CHECK(rtp != NULL, "Failed to acquire rtp interface"); |
| } |
| |
| ~VoiceEngineData() { |
| CHECK(channel_transports_.empty(), |
| "VoE transports must be deleted before terminating"); |
| CHECK(base->Terminate() == 0, "VoE failed to terminate"); |
| ReleaseSubApi(base); |
| ReleaseSubApi(codec); |
| ReleaseSubApi(file); |
| ReleaseSubApi(netw); |
| ReleaseSubApi(apm); |
| ReleaseSubApi(volume); |
| ReleaseSubApi(hardware); |
| ReleaseSubApi(rtp); |
| webrtc::VoiceEngine* ve_instance = ve; |
| CHECK(webrtc::VoiceEngine::Delete(ve_instance), "VoE failed to be deleted"); |
| } |
| |
| int CreateChannel() { |
| int channel = base->CreateChannel(); |
| if (channel == -1) { |
| return -1; |
| } |
| CreateTransport(channel); |
| return channel; |
| } |
| |
| int DeleteChannel(int channel) { |
| if (base->DeleteChannel(channel) != 0) { |
| return -1; |
| } |
| DeleteTransport(channel); |
| return 0; |
| } |
| |
| webrtc::test::VoiceChannelTransport* GetTransport(int channel) { |
| ChannelTransports::iterator found = channel_transports_.find(channel); |
| if (found == channel_transports_.end()) { |
| return NULL; |
| } |
| return found->second; |
| } |
| |
| webrtc::VoiceEngine* const ve; |
| webrtc::VoEBase* const base; |
| webrtc::VoECodec* const codec; |
| webrtc::VoEFile* const file; |
| webrtc::VoENetwork* const netw; |
| webrtc::VoEAudioProcessing* const apm; |
| webrtc::VoEVolumeControl* const volume; |
| webrtc::VoEHardware* const hardware; |
| webrtc::VoERTP_RTCP* const rtp; |
| |
| private: |
| // Voice engine no longer provides a socket implementation. There is, |
| // however, a socket implementation in webrtc::test. |
| typedef std::map<int, webrtc::test::VoiceChannelTransport*> |
| ChannelTransports; |
| |
| void CreateTransport(int channel) { |
| CHECK(GetTransport(channel) == NULL, |
| "Transport already created for VoE channel, inconsistent state"); |
| channel_transports_[channel] = |
| new webrtc::test::VoiceChannelTransport(netw, channel); |
| } |
| void DeleteTransport(int channel) { |
| CHECK(GetTransport(channel) != NULL, |
| "VoE channel missing transport, inconsistent state"); |
| delete channel_transports_[channel]; |
| channel_transports_.erase(channel); |
| } |
| |
| ChannelTransports channel_transports_; |
| }; |
| |
| webrtc::CodecInst* GetCodecInst(JNIEnv* jni, jobject j_codec) { |
| jclass j_codec_class = jni->GetObjectClass(j_codec); |
| jfieldID native_codec_id = |
| jni->GetFieldID(j_codec_class, "nativeCodecInst", "J"); |
| jlong j_p = jni->GetLongField(j_codec, native_codec_id); |
| return reinterpret_cast<webrtc::CodecInst*>(j_p); |
| } |
| |
| } // namespace |
| |
| namespace webrtc_examples { |
| |
| void SetVoeDeviceObjects(JavaVM* vm) { |
| CHECK(vm, "Trying to register NULL vm"); |
| g_vm = vm; |
| webrtc::AttachThreadScoped ats(g_vm); |
| JNIEnv* jni = ats.env(); |
| g_class_reference_holder = new ClassReferenceHolder( |
| jni, g_classes, arraysize(g_classes)); |
| } |
| |
| void ClearVoeDeviceObjects() { |
| CHECK(g_vm, "Clearing vm without it being set"); |
| { |
| webrtc::AttachThreadScoped ats(g_vm); |
| g_class_reference_holder->FreeReferences(ats.env()); |
| } |
| g_vm = NULL; |
| delete g_class_reference_holder; |
| g_class_reference_holder = NULL; |
| } |
| |
| } // namespace webrtc_examples |
| |
| VoiceEngineData* GetVoiceEngineData(JNIEnv* jni, jobject j_voe) { |
| jclass j_voe_class = jni->GetObjectClass(j_voe); |
| jfieldID native_voe_id = |
| jni->GetFieldID(j_voe_class, "nativeVoiceEngine", "J"); |
| jlong j_p = jni->GetLongField(j_voe, native_voe_id); |
| return reinterpret_cast<VoiceEngineData*>(j_p); |
| } |
| |
| webrtc::VoiceEngine* GetVoiceEngine(JNIEnv* jni, jobject j_voe) { |
| return GetVoiceEngineData(jni, j_voe)->ve; |
| } |
| |
| JOWW(jlong, VoiceEngine_create)(JNIEnv* jni, jclass) { |
| VoiceEngineData* voe_data = new VoiceEngineData(); |
| return jlongFromPointer(voe_data); |
| } |
| |
| JOWW(void, VoiceEngine_dispose)(JNIEnv* jni, jobject j_voe) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| delete voe_data; |
| } |
| |
| JOWW(jint, VoiceEngine_init)(JNIEnv* jni, jobject j_voe) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| return voe_data->base->Init(); |
| } |
| |
| JOWW(jint, VoiceEngine_createChannel)(JNIEnv* jni, jobject j_voe) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| return voe_data->CreateChannel(); |
| } |
| |
| JOWW(jint, VoiceEngine_deleteChannel)(JNIEnv* jni, jobject j_voe, |
| jint channel) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| return voe_data->DeleteChannel(channel); |
| } |
| |
| JOWW(jint, VoiceEngine_setLocalReceiver)(JNIEnv* jni, jobject j_voe, |
| jint channel, jint port) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| webrtc::test::VoiceChannelTransport* transport = |
| voe_data->GetTransport(channel); |
| return transport->SetLocalReceiver(port); |
| } |
| |
| JOWW(jint, VoiceEngine_setSendDestination)(JNIEnv* jni, jobject j_voe, |
| jint channel, jint port, |
| jstring j_addr) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| std::string addr = JavaToStdString(jni, j_addr); |
| webrtc::test::VoiceChannelTransport* transport = |
| voe_data->GetTransport(channel); |
| return transport->SetSendDestination(addr.c_str(), port); |
| } |
| |
| JOWW(jint, VoiceEngine_startListen)(JNIEnv* jni, jobject j_voe, jint channel) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| return voe_data->base->StartReceive(channel); |
| } |
| |
| JOWW(jint, VoiceEngine_startPlayout)(JNIEnv* jni, jobject j_voe, jint channel) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| return voe_data->base->StartPlayout(channel); |
| } |
| |
| JOWW(jint, VoiceEngine_startSend)(JNIEnv* jni, jobject j_voe, jint channel) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| return voe_data->base->StartSend(channel); |
| } |
| |
| JOWW(jint, VoiceEngine_stopListen)(JNIEnv* jni, jobject j_voe, jint channel) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| return voe_data->base->StartReceive(channel); |
| } |
| |
| JOWW(jint, VoiceEngine_stopPlayout)(JNIEnv* jni, jobject j_voe, jint channel) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| return voe_data->base->StopPlayout(channel); |
| } |
| |
| JOWW(jint, VoiceEngine_stopSend)(JNIEnv* jni, jobject j_voe, jint channel) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| return voe_data->base->StopSend(channel); |
| } |
| |
| JOWW(jint, VoiceEngine_setSpeakerVolume)(JNIEnv* jni, jobject j_voe, |
| jint level) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| return voe_data->volume->SetSpeakerVolume(level); |
| } |
| |
| JOWW(jint, VoiceEngine_startPlayingFileLocally)(JNIEnv* jni, jobject j_voe, |
| jint channel, |
| jstring j_filename, |
| jboolean loop) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| std::string filename = JavaToStdString(jni, j_filename); |
| return voe_data->file->StartPlayingFileLocally(channel, |
| filename.c_str(), |
| loop); |
| } |
| |
| JOWW(jint, VoiceEngine_stopPlayingFileLocally)(JNIEnv* jni, jobject j_voe, |
| jint channel) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| return voe_data->file->StopPlayingFileLocally(channel); |
| } |
| |
| JOWW(jint, VoiceEngine_startPlayingFileAsMicrophone)(JNIEnv* jni, jobject j_voe, |
| jint channel, |
| jstring j_filename, |
| jboolean loop) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| std::string filename = JavaToStdString(jni, j_filename); |
| return voe_data->file->StartPlayingFileAsMicrophone(channel, |
| filename.c_str(), |
| loop); |
| } |
| |
| JOWW(jint, VoiceEngine_stopPlayingFileAsMicrophone)(JNIEnv* jni, jobject j_voe, |
| jint channel) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| return voe_data->file->StopPlayingFileAsMicrophone(channel); |
| } |
| |
| JOWW(jint, VoiceEngine_numOfCodecs)(JNIEnv* jni, jobject j_voe) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| return voe_data->codec->NumOfCodecs(); |
| } |
| |
| JOWW(jobject, VoiceEngine_getCodec)(JNIEnv* jni, jobject j_voe, jint index) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| webrtc::CodecInst* codec = new webrtc::CodecInst(); |
| CHECK(voe_data->codec->GetCodec(index, *codec) == 0, |
| "getCodec must be called with valid index"); |
| jclass j_codec_class = GetClass(jni, "org/webrtc/webrtcdemo/CodecInst"); |
| jmethodID j_codec_ctor = GetMethodID(jni, j_codec_class, "<init>", "(J)V"); |
| jobject j_codec = |
| jni->NewObject(j_codec_class, j_codec_ctor, jlongFromPointer(codec)); |
| CHECK_JNI_EXCEPTION(jni, "error during NewObject"); |
| return j_codec; |
| } |
| |
| JOWW(jint, VoiceEngine_setSendCodec)(JNIEnv* jni, jobject j_voe, jint channel, |
| jobject j_codec) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| webrtc::CodecInst* inst = GetCodecInst(jni, j_codec); |
| return voe_data->codec->SetSendCodec(channel, *inst); |
| } |
| |
| JOWW(jint, VoiceEngine_setEcStatus)(JNIEnv* jni, jobject j_voe, jboolean enable, |
| jint ec_mode) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| return voe_data->apm->SetEcStatus(enable, |
| static_cast<webrtc::EcModes>(ec_mode)); |
| } |
| |
| JOWW(jint, VoiceEngine_setAecmMode)(JNIEnv* jni, jobject j_voe, jint aecm_mode, |
| jboolean cng) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| return voe_data->apm->SetAecmMode(static_cast<webrtc::AecmModes>(aecm_mode), |
| cng); |
| } |
| |
| JOWW(jint, VoiceEngine_setAgcStatus)(JNIEnv* jni, jobject j_voe, |
| jboolean enable, jint agc_mode) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| return voe_data->apm->SetAgcStatus(enable, |
| static_cast<webrtc::AgcModes>(agc_mode)); |
| } |
| |
| // Returns the native AgcConfig object associated with the Java object |
| // |j_codec|. |
| void GetNativeAgcConfig(JNIEnv* jni, jobject j_codec, |
| webrtc::AgcConfig* agc_config) { |
| jclass j_codec_class = jni->GetObjectClass(j_codec); |
| jfieldID dBOv_id = jni->GetFieldID(j_codec_class, "targetLevelDbOv", "I"); |
| agc_config->targetLeveldBOv = jni->GetIntField(j_codec, dBOv_id); |
| jfieldID gain_id = |
| jni->GetFieldID(j_codec_class, "digitalCompressionGaindB", "I"); |
| agc_config->digitalCompressionGaindB = jni->GetIntField(j_codec, gain_id); |
| jfieldID limiter_id = jni->GetFieldID(j_codec_class, "limiterEnable", "Z"); |
| agc_config->limiterEnable = jni->GetBooleanField(j_codec, limiter_id); |
| } |
| |
| JOWW(jint, VoiceEngine_setAgcConfig)(JNIEnv* jni, jobject j_voe, |
| jobject j_config) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| webrtc::AgcConfig config; |
| GetNativeAgcConfig(jni, j_config, &config); |
| return voe_data->apm->SetAgcConfig(config); |
| } |
| |
| JOWW(jint, VoiceEngine_setNsStatus)(JNIEnv* jni, jobject j_voe, jboolean enable, |
| jint ns_mode) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| return voe_data->apm->SetNsStatus(enable, |
| static_cast<webrtc::NsModes>(ns_mode)); |
| } |
| |
| JOWW(jint, VoiceEngine_startDebugRecording)(JNIEnv* jni, jobject j_voe, |
| jstring j_filename) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| std::string filename = JavaToStdString(jni, j_filename); |
| return voe_data->apm->StartDebugRecording(filename.c_str()); |
| } |
| |
| JOWW(jint, VoiceEngine_stopDebugRecording)(JNIEnv* jni, jobject j_voe) { |
| VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); |
| return voe_data->apm->StopDebugRecording(); |
| } |
| |
| JOWW(void, CodecInst_dispose)(JNIEnv* jni, jobject j_codec) { |
| delete GetCodecInst(jni, j_codec); |
| } |
| |
| JOWW(jint, CodecInst_plType)(JNIEnv* jni, jobject j_codec) { |
| return GetCodecInst(jni, j_codec)->pltype; |
| } |
| |
| JOWW(jstring, CodecInst_name)(JNIEnv* jni, jobject j_codec) { |
| return jni->NewStringUTF(GetCodecInst(jni, j_codec)->plname); |
| } |
| |
| JOWW(jint, CodecInst_plFrequency)(JNIEnv* jni, jobject j_codec) { |
| return GetCodecInst(jni, j_codec)->plfreq; |
| } |
| |
| JOWW(jint, CodecInst_pacSize)(JNIEnv* jni, jobject j_codec) { |
| return GetCodecInst(jni, j_codec)->pacsize; |
| } |
| |
| JOWW(jint, CodecInst_channels)(JNIEnv* jni, jobject j_codec) { |
| return GetCodecInst(jni, j_codec)->channels; |
| } |
| |
| JOWW(jint, CodecInst_rate)(JNIEnv* jni, jobject j_codec) { |
| return GetCodecInst(jni, j_codec)->rate; |
| } |