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 // This file contains JNI for the voice engine interfaces. 12 // The native functions are found using jni's auto discovery. 13 14 #include "webrtc/examples/android/media_demo/jni/voice_engine_jni.h" 15 16 #include <map> 17 #include <string> 18 19 #include "webrtc/examples/android/media_demo/jni/jni_helpers.h" 20 #include "webrtc/modules/utility/interface/helpers_android.h" 21 #include "webrtc/test/channel_transport/include/channel_transport.h" 22 #include "webrtc/voice_engine/include/voe_audio_processing.h" 23 #include "webrtc/voice_engine/include/voe_base.h" 24 #include "webrtc/voice_engine/include/voe_codec.h" 25 #include "webrtc/voice_engine/include/voe_file.h" 26 #include "webrtc/voice_engine/include/voe_hardware.h" 27 #include "webrtc/voice_engine/include/voe_network.h" 28 #include "webrtc/voice_engine/include/voe_rtp_rtcp.h" 29 #include "webrtc/voice_engine/include/voe_volume_control.h" 30 31 // Macro for native functions that can be found by way of jni-auto discovery. 32 // Note extern "C" is needed for "discovery" of native methods to work. 33 #define JOWW(rettype, name) \ 34 extern "C" rettype JNIEXPORT JNICALL Java_org_webrtc_webrtcdemo_##name 35 36 namespace { 37 38 static JavaVM* g_vm = NULL; 39 static ClassReferenceHolder* g_class_reference_holder = NULL; 40 41 jclass GetClass(JNIEnv* jni, const char* name) { 42 CHECK(g_class_reference_holder, "Class reference holder NULL"); 43 return g_class_reference_holder->GetClass(name); 44 } 45 46 static const char* g_classes[] = {"org/webrtc/webrtcdemo/CodecInst"}; 47 48 template<typename T> 49 void ReleaseSubApi(T instance) { 50 CHECK(instance->Release() >= 0, "failed to release instance") 51 } 52 53 class VoiceEngineData { 54 public: 55 VoiceEngineData() 56 : ve(webrtc::VoiceEngine::Create()), 57 base(webrtc::VoEBase::GetInterface(ve)), 58 codec(webrtc::VoECodec::GetInterface(ve)), 59 file(webrtc::VoEFile::GetInterface(ve)), 60 netw(webrtc::VoENetwork::GetInterface(ve)), 61 apm(webrtc::VoEAudioProcessing::GetInterface(ve)), 62 volume(webrtc::VoEVolumeControl::GetInterface(ve)), 63 hardware(webrtc::VoEHardware::GetInterface(ve)), 64 rtp(webrtc::VoERTP_RTCP::GetInterface(ve)) { 65 CHECK(ve != NULL, "Voice engine instance failed to be created"); 66 CHECK(base != NULL, "Failed to acquire base interface"); 67 CHECK(codec != NULL, "Failed to acquire codec interface"); 68 CHECK(file != NULL, "Failed to acquire file interface"); 69 CHECK(netw != NULL, "Failed to acquire netw interface"); 70 CHECK(apm != NULL, "Failed to acquire apm interface"); 71 CHECK(volume != NULL, "Failed to acquire volume interface"); 72 CHECK(hardware != NULL, "Failed to acquire hardware interface"); 73 CHECK(rtp != NULL, "Failed to acquire rtp interface"); 74 } 75 76 ~VoiceEngineData() { 77 CHECK(channel_transports_.empty(), 78 "VoE transports must be deleted before terminating"); 79 CHECK(base->Terminate() == 0, "VoE failed to terminate"); 80 ReleaseSubApi(base); 81 ReleaseSubApi(codec); 82 ReleaseSubApi(file); 83 ReleaseSubApi(netw); 84 ReleaseSubApi(apm); 85 ReleaseSubApi(volume); 86 ReleaseSubApi(hardware); 87 ReleaseSubApi(rtp); 88 webrtc::VoiceEngine* ve_instance = ve; 89 CHECK(webrtc::VoiceEngine::Delete(ve_instance), "VoE failed to be deleted"); 90 } 91 92 int CreateChannel() { 93 int channel = base->CreateChannel(); 94 if (channel == -1) { 95 return -1; 96 } 97 CreateTransport(channel); 98 return channel; 99 } 100 101 int DeleteChannel(int channel) { 102 if (base->DeleteChannel(channel) != 0) { 103 return -1; 104 } 105 DeleteTransport(channel); 106 return 0; 107 } 108 109 webrtc::test::VoiceChannelTransport* GetTransport(int channel) { 110 ChannelTransports::iterator found = channel_transports_.find(channel); 111 if (found == channel_transports_.end()) { 112 return NULL; 113 } 114 return found->second; 115 } 116 117 webrtc::VoiceEngine* const ve; 118 webrtc::VoEBase* const base; 119 webrtc::VoECodec* const codec; 120 webrtc::VoEFile* const file; 121 webrtc::VoENetwork* const netw; 122 webrtc::VoEAudioProcessing* const apm; 123 webrtc::VoEVolumeControl* const volume; 124 webrtc::VoEHardware* const hardware; 125 webrtc::VoERTP_RTCP* const rtp; 126 127 private: 128 // Voice engine no longer provides a socket implementation. There is, 129 // however, a socket implementation in webrtc::test. 130 typedef std::map<int, webrtc::test::VoiceChannelTransport*> 131 ChannelTransports; 132 133 void CreateTransport(int channel) { 134 CHECK(GetTransport(channel) == NULL, 135 "Transport already created for VoE channel, inconsistent state"); 136 channel_transports_[channel] = 137 new webrtc::test::VoiceChannelTransport(netw, channel); 138 } 139 void DeleteTransport(int channel) { 140 CHECK(GetTransport(channel) != NULL, 141 "VoE channel missing transport, inconsistent state"); 142 delete channel_transports_[channel]; 143 channel_transports_.erase(channel); 144 } 145 146 ChannelTransports channel_transports_; 147 }; 148 149 webrtc::CodecInst* GetCodecInst(JNIEnv* jni, jobject j_codec) { 150 jclass j_codec_class = jni->GetObjectClass(j_codec); 151 jfieldID native_codec_id = 152 jni->GetFieldID(j_codec_class, "nativeCodecInst", "J"); 153 jlong j_p = jni->GetLongField(j_codec, native_codec_id); 154 return reinterpret_cast<webrtc::CodecInst*>(j_p); 155 } 156 157 } // namespace 158 159 namespace webrtc_examples { 160 161 void SetVoeDeviceObjects(JavaVM* vm) { 162 CHECK(vm, "Trying to register NULL vm"); 163 g_vm = vm; 164 webrtc::AttachThreadScoped ats(g_vm); 165 JNIEnv* jni = ats.env(); 166 g_class_reference_holder = new ClassReferenceHolder( 167 jni, g_classes, ARRAYSIZE(g_classes)); 168 } 169 170 void ClearVoeDeviceObjects() { 171 CHECK(g_vm, "Clearing vm without it being set"); 172 { 173 webrtc::AttachThreadScoped ats(g_vm); 174 g_class_reference_holder->FreeReferences(ats.env()); 175 } 176 g_vm = NULL; 177 delete g_class_reference_holder; 178 g_class_reference_holder = NULL; 179 } 180 181 } // namespace webrtc_examples 182 183 VoiceEngineData* GetVoiceEngineData(JNIEnv* jni, jobject j_voe) { 184 jclass j_voe_class = jni->GetObjectClass(j_voe); 185 jfieldID native_voe_id = 186 jni->GetFieldID(j_voe_class, "nativeVoiceEngine", "J"); 187 jlong j_p = jni->GetLongField(j_voe, native_voe_id); 188 return reinterpret_cast<VoiceEngineData*>(j_p); 189 } 190 191 webrtc::VoiceEngine* GetVoiceEngine(JNIEnv* jni, jobject j_voe) { 192 return GetVoiceEngineData(jni, j_voe)->ve; 193 } 194 195 JOWW(jlong, VoiceEngine_create)(JNIEnv* jni, jclass) { 196 VoiceEngineData* voe_data = new VoiceEngineData(); 197 return jlongFromPointer(voe_data); 198 } 199 200 JOWW(void, VoiceEngine_dispose)(JNIEnv* jni, jobject j_voe) { 201 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 202 delete voe_data; 203 } 204 205 JOWW(jint, VoiceEngine_init)(JNIEnv* jni, jobject j_voe) { 206 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 207 return voe_data->base->Init(); 208 } 209 210 JOWW(jint, VoiceEngine_createChannel)(JNIEnv* jni, jobject j_voe) { 211 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 212 return voe_data->CreateChannel(); 213 } 214 215 JOWW(jint, VoiceEngine_deleteChannel)(JNIEnv* jni, jobject j_voe, 216 jint channel) { 217 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 218 return voe_data->DeleteChannel(channel); 219 } 220 221 JOWW(jint, VoiceEngine_setLocalReceiver)(JNIEnv* jni, jobject j_voe, 222 jint channel, jint port) { 223 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 224 webrtc::test::VoiceChannelTransport* transport = 225 voe_data->GetTransport(channel); 226 return transport->SetLocalReceiver(port); 227 } 228 229 JOWW(jint, VoiceEngine_setSendDestination)(JNIEnv* jni, jobject j_voe, 230 jint channel, jint port, 231 jstring j_addr) { 232 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 233 std::string addr = JavaToStdString(jni, j_addr); 234 webrtc::test::VoiceChannelTransport* transport = 235 voe_data->GetTransport(channel); 236 return transport->SetSendDestination(addr.c_str(), port); 237 } 238 239 JOWW(jint, VoiceEngine_startListen)(JNIEnv* jni, jobject j_voe, jint channel) { 240 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 241 return voe_data->base->StartReceive(channel); 242 } 243 244 JOWW(jint, VoiceEngine_startPlayout)(JNIEnv* jni, jobject j_voe, jint channel) { 245 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 246 return voe_data->base->StartPlayout(channel); 247 } 248 249 JOWW(jint, VoiceEngine_startSend)(JNIEnv* jni, jobject j_voe, jint channel) { 250 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 251 return voe_data->base->StartSend(channel); 252 } 253 254 JOWW(jint, VoiceEngine_stopListen)(JNIEnv* jni, jobject j_voe, jint channel) { 255 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 256 return voe_data->base->StartReceive(channel); 257 } 258 259 JOWW(jint, VoiceEngine_stopPlayout)(JNIEnv* jni, jobject j_voe, jint channel) { 260 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 261 return voe_data->base->StopPlayout(channel); 262 } 263 264 JOWW(jint, VoiceEngine_stopSend)(JNIEnv* jni, jobject j_voe, jint channel) { 265 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 266 return voe_data->base->StopSend(channel); 267 } 268 269 JOWW(jint, VoiceEngine_setSpeakerVolume)(JNIEnv* jni, jobject j_voe, 270 jint level) { 271 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 272 return voe_data->volume->SetSpeakerVolume(level); 273 } 274 275 JOWW(jint, VoiceEngine_setLoudspeakerStatus)(JNIEnv* jni, jobject j_voe, 276 jboolean enable) { 277 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 278 return voe_data->hardware->SetLoudspeakerStatus(enable); 279 } 280 281 JOWW(jint, VoiceEngine_startPlayingFileLocally)(JNIEnv* jni, jobject j_voe, 282 jint channel, 283 jstring j_filename, 284 jboolean loop) { 285 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 286 std::string filename = JavaToStdString(jni, j_filename); 287 return voe_data->file->StartPlayingFileLocally(channel, 288 filename.c_str(), 289 loop); 290 } 291 292 JOWW(jint, VoiceEngine_stopPlayingFileLocally)(JNIEnv* jni, jobject j_voe, 293 jint channel) { 294 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 295 return voe_data->file->StopPlayingFileLocally(channel); 296 } 297 298 JOWW(jint, VoiceEngine_startPlayingFileAsMicrophone)(JNIEnv* jni, jobject j_voe, 299 jint channel, 300 jstring j_filename, 301 jboolean loop) { 302 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 303 std::string filename = JavaToStdString(jni, j_filename); 304 return voe_data->file->StartPlayingFileAsMicrophone(channel, 305 filename.c_str(), 306 loop); 307 } 308 309 JOWW(jint, VoiceEngine_stopPlayingFileAsMicrophone)(JNIEnv* jni, jobject j_voe, 310 jint channel) { 311 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 312 return voe_data->file->StopPlayingFileAsMicrophone(channel); 313 } 314 315 JOWW(jint, VoiceEngine_numOfCodecs)(JNIEnv* jni, jobject j_voe) { 316 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 317 return voe_data->codec->NumOfCodecs(); 318 } 319 320 JOWW(jobject, VoiceEngine_getCodec)(JNIEnv* jni, jobject j_voe, jint index) { 321 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 322 webrtc::CodecInst* codec = new webrtc::CodecInst(); 323 CHECK(voe_data->codec->GetCodec(index, *codec) == 0, 324 "getCodec must be called with valid index"); 325 jclass j_codec_class = GetClass(jni, "org/webrtc/webrtcdemo/CodecInst"); 326 jmethodID j_codec_ctor = GetMethodID(jni, j_codec_class, "<init>", "(J)V"); 327 jobject j_codec = 328 jni->NewObject(j_codec_class, j_codec_ctor, jlongFromPointer(codec)); 329 CHECK_EXCEPTION(jni, "error during NewObject"); 330 return j_codec; 331 } 332 333 JOWW(jint, VoiceEngine_setSendCodec)(JNIEnv* jni, jobject j_voe, jint channel, 334 jobject j_codec) { 335 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 336 webrtc::CodecInst* inst = GetCodecInst(jni, j_codec); 337 return voe_data->codec->SetSendCodec(channel, *inst); 338 } 339 340 JOWW(jint, VoiceEngine_setEcStatus)(JNIEnv* jni, jobject j_voe, jboolean enable, 341 jint ec_mode) { 342 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 343 return voe_data->apm->SetEcStatus(enable, 344 static_cast<webrtc::EcModes>(ec_mode)); 345 } 346 347 JOWW(jint, VoiceEngine_setAecmMode)(JNIEnv* jni, jobject j_voe, jint aecm_mode, 348 jboolean cng) { 349 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 350 return voe_data->apm->SetAecmMode(static_cast<webrtc::AecmModes>(aecm_mode), 351 cng); 352 } 353 354 JOWW(jint, VoiceEngine_setAgcStatus)(JNIEnv* jni, jobject j_voe, 355 jboolean enable, jint agc_mode) { 356 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 357 return voe_data->apm->SetAgcStatus(enable, 358 static_cast<webrtc::AgcModes>(agc_mode)); 359 } 360 361 // Returns the native AgcConfig object associated with the Java object 362 // |j_codec|. 363 void GetNativeAgcConfig(JNIEnv* jni, jobject j_codec, 364 webrtc::AgcConfig* agc_config) { 365 jclass j_codec_class = jni->GetObjectClass(j_codec); 366 jfieldID dBOv_id = jni->GetFieldID(j_codec_class, "targetLevelDbOv", "I"); 367 agc_config->targetLeveldBOv = jni->GetIntField(j_codec, dBOv_id); 368 jfieldID gain_id = 369 jni->GetFieldID(j_codec_class, "digitalCompressionGaindB", "I"); 370 agc_config->digitalCompressionGaindB = jni->GetIntField(j_codec, gain_id); 371 jfieldID limiter_id = jni->GetFieldID(j_codec_class, "limiterEnable", "Z"); 372 agc_config->limiterEnable = jni->GetBooleanField(j_codec, limiter_id); 373 } 374 375 JOWW(jint, VoiceEngine_setAgcConfig)(JNIEnv* jni, jobject j_voe, 376 jobject j_config) { 377 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 378 webrtc::AgcConfig config; 379 GetNativeAgcConfig(jni, j_config, &config); 380 return voe_data->apm->SetAgcConfig(config); 381 } 382 383 JOWW(jint, VoiceEngine_setNsStatus)(JNIEnv* jni, jobject j_voe, jboolean enable, 384 jint ns_mode) { 385 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 386 return voe_data->apm->SetNsStatus(enable, 387 static_cast<webrtc::NsModes>(ns_mode)); 388 } 389 390 JOWW(jint, VoiceEngine_startDebugRecording)(JNIEnv* jni, jobject j_voe, 391 jstring j_filename) { 392 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 393 std::string filename = JavaToStdString(jni, j_filename); 394 return voe_data->apm->StartDebugRecording(filename.c_str()); 395 } 396 397 JOWW(jint, VoiceEngine_stopDebugRecording)(JNIEnv* jni, jobject j_voe) { 398 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 399 return voe_data->apm->StopDebugRecording(); 400 } 401 402 JOWW(jint, VoiceEngine_startRtpDump)(JNIEnv* jni, jobject j_voe, jint channel, 403 jstring j_filename, jint direction) { 404 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 405 std::string filename = JavaToStdString(jni, j_filename); 406 return voe_data->rtp->StartRTPDump( 407 channel, filename.c_str(), 408 static_cast<webrtc::RTPDirections>(direction)); 409 } 410 411 JOWW(jint, VoiceEngine_stopRtpDump)(JNIEnv* jni, jobject j_voe, jint channel, 412 jint direction) { 413 VoiceEngineData* voe_data = GetVoiceEngineData(jni, j_voe); 414 return voe_data->rtp->StopRTPDump( 415 channel, static_cast<webrtc::RTPDirections>(direction)); 416 } 417 418 JOWW(void, CodecInst_dispose)(JNIEnv* jni, jobject j_codec) { 419 delete GetCodecInst(jni, j_codec); 420 } 421 422 JOWW(jint, CodecInst_plType)(JNIEnv* jni, jobject j_codec) { 423 return GetCodecInst(jni, j_codec)->pltype; 424 } 425 426 JOWW(jstring, CodecInst_name)(JNIEnv* jni, jobject j_codec) { 427 return jni->NewStringUTF(GetCodecInst(jni, j_codec)->plname); 428 } 429 430 JOWW(jint, CodecInst_plFrequency)(JNIEnv* jni, jobject j_codec) { 431 return GetCodecInst(jni, j_codec)->plfreq; 432 } 433 434 JOWW(jint, CodecInst_pacSize)(JNIEnv* jni, jobject j_codec) { 435 return GetCodecInst(jni, j_codec)->pacsize; 436 } 437 438 JOWW(jint, CodecInst_channels)(JNIEnv* jni, jobject j_codec) { 439 return GetCodecInst(jni, j_codec)->channels; 440 } 441 442 JOWW(jint, CodecInst_rate)(JNIEnv* jni, jobject j_codec) { 443 return GetCodecInst(jni, j_codec)->rate; 444 } 445