#include "AudioCodec.h" #include "MicrophoneReader.h" #include "SequenceCounter.h" #include "JitterBuffer.h" #include "RtpAudioReceiver.h" #include "RtpAudioSender.h" #include "AudioPlayer.h" #include "NetworkUtil.h" #include "CallAudioManager.h" #include #include #include #include #include #define TAG "CallAudioManager" CallAudioManager::CallAudioManager(int androidSdkVersion, int socketFd, struct sockaddr *sockAddr, int sockAddrLen, SrtpStreamParameters *senderParameters, SrtpStreamParameters *receiverParameters) : running(0), finished(1), engineObject(NULL), engineEngine(NULL), audioCodec(), audioSender(socketFd, sockAddr, sockAddrLen, senderParameters), audioReceiver(socketFd, receiverParameters), webRtcJitterBuffer(audioCodec), clock(), microphoneReader(androidSdkVersion, audioCodec, audioSender, clock), audioPlayer(webRtcJitterBuffer, audioCodec), sockAddr(sockAddr) { } int CallAudioManager::init() { if (pthread_mutex_init(&mutex, NULL) != 0) { __android_log_print(ANDROID_LOG_WARN, TAG, "Failed to create mutex!"); return -1; } if (pthread_cond_init(&condition, NULL) != 0) { __android_log_print(ANDROID_LOG_WARN, TAG, "Failed to create condition!"); return -1; } return 0; } CallAudioManager::~CallAudioManager() { __android_log_print(ANDROID_LOG_WARN, TAG, "Shutting down..."); microphoneReader.stop(); audioPlayer.stop(); webRtcJitterBuffer.stop(); if (sockAddr != NULL) { free(sockAddr); } if (engineObject != NULL) { (*engineObject)->Destroy(engineObject); } __android_log_print(ANDROID_LOG_WARN, TAG, "Shutdown complete...."); } int CallAudioManager::start() { running = 1; finished = 0; if (slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL) != SL_RESULT_SUCCESS) { __android_log_print(ANDROID_LOG_WARN, TAG, "Failed to create engineObject!"); return -1; } if ((*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE) != SL_RESULT_SUCCESS) { __android_log_print(ANDROID_LOG_WARN, TAG, "Failed to realize engineObject!"); return -1; } if ((*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine) != SL_RESULT_SUCCESS) { __android_log_print(ANDROID_LOG_WARN, TAG, "Failed to get engine interface!"); return -1; } if (audioCodec.init() != 0) { __android_log_print(ANDROID_LOG_WARN, TAG, "Failed to initialize codec!"); return -1; } if (audioSender.init() != 0) { __android_log_print(ANDROID_LOG_WARN, TAG, "Failed to initialize RTP sender!"); return -1; } if (audioReceiver.init() != 0) { __android_log_print(ANDROID_LOG_WARN, TAG, "Failed to initialize RTP receiver!"); return -1; } if (webRtcJitterBuffer.init() != 0) { __android_log_print(ANDROID_LOG_WARN, TAG, "Failed to initialize jitter buffer!"); return -1; } __android_log_print(ANDROID_LOG_WARN, TAG, "Starting MicrophoneReader..."); if (microphoneReader.start(&engineEngine) == -1) { __android_log_print(ANDROID_LOG_WARN, TAG, "ERROR -- MicrophoneReader::start() returned -1!"); return -1; } __android_log_print(ANDROID_LOG_WARN, TAG, "Starting AudioPlayer..."); if (audioPlayer.start(&engineEngine) == -1) { __android_log_print(ANDROID_LOG_WARN, TAG, "AudioPlayer::start() returned -1!"); return -1; } char buffer[4096]; while(running) { RtpPacket *packet = audioReceiver.receive(buffer, sizeof(buffer)); if (packet != NULL) { if (packet->getTimestamp() == 0) { packet->setTimestamp(clock.getImprovisedTimestamp(packet->getPayloadLen())); } webRtcJitterBuffer.addAudio(packet, clock.getTickCount()); delete packet; } } if (pthread_mutex_lock(&mutex) != 0) { __android_log_print(ANDROID_LOG_WARN, TAG, "Failed to acquire mutex!"); return 0; } finished = 1; pthread_cond_signal(&condition); pthread_mutex_unlock(&mutex); return 0; } void CallAudioManager::stop() { running = 0; microphoneReader.stop(); audioPlayer.stop(); webRtcJitterBuffer.stop(); pthread_mutex_lock(&mutex); while (finished == 0) { pthread_cond_wait(&condition, &mutex); } pthread_mutex_unlock(&mutex); usleep(40000); // Duration of microphone frame. } void CallAudioManager::setMute(int muteEnabled) { microphoneReader.setMute(muteEnabled); } static void constructSockAddr(JNIEnv *env, jstring serverIpString, jint serverPort, struct sockaddr** result, int *resultLen) { const char* serverIp = env->GetStringUTFChars(serverIpString, 0); int addressType = NetworkUtil::getAddressType(serverIp); if (addressType == 1) { struct sockaddr_in *sockAddr = (struct sockaddr_in*)malloc(sizeof(struct sockaddr_in)); memset(sockAddr, 0, sizeof(struct sockaddr_in)); sockAddr->sin_family = AF_INET; sockAddr->sin_port = htons(serverPort); if (inet_aton(serverIp, &(sockAddr->sin_addr)) == 0) { __android_log_print(ANDROID_LOG_WARN, TAG, "Invalid address: %s", serverIp); free(sockAddr); sockAddr = NULL; } *result = (struct sockaddr*)sockAddr; *resultLen = sizeof(struct sockaddr_in); } else if (addressType == 0) { struct sockaddr_in6 *sockAddr = (struct sockaddr_in6*)malloc(sizeof(struct sockaddr_in6)); memset(sockAddr, 0, sizeof(struct sockaddr_in6)); sockAddr->sin6_family = AF_INET6; sockAddr->sin6_port = htons(serverPort); if (inet_pton(AF_INET6, serverIp, &(sockAddr->sin6_addr)) != 1) { __android_log_print(ANDROID_LOG_WARN, TAG, "Invalid IPv6 address: %s", serverIp); free(sockAddr); sockAddr = NULL; } *result = (struct sockaddr*)sockAddr; *resultLen = sizeof(struct sockaddr_in6); } else { __android_log_print(ANDROID_LOG_WARN, TAG, "Unknown address type: %d", addressType); *result = NULL; *resultLen = 0; } env->ReleaseStringUTFChars(serverIpString, serverIp); } static SrtpStreamParameters* constructSrtpStreamParameters(JNIEnv *env, jbyteArray cipherKey, jbyteArray macKey, jbyteArray salt) { uint8_t* cipherKeyBytes = (uint8_t*)env->GetByteArrayElements(cipherKey, 0); uint8_t* macKeyBytes = (uint8_t*)env->GetByteArrayElements(macKey, 0); uint8_t* saltBytes = (uint8_t*)env->GetByteArrayElements(salt, 0); SrtpStreamParameters *parameters = new SrtpStreamParameters(cipherKeyBytes, macKeyBytes, saltBytes); env->ReleaseByteArrayElements(cipherKey, (jbyte*)cipherKeyBytes, 0); env->ReleaseByteArrayElements(macKey, (jbyte*)macKeyBytes, 0); env->ReleaseByteArrayElements(salt, (jbyte*)saltBytes, 0); return parameters; } jlong JNICALL Java_org_thoughtcrime_redphone_audio_CallAudioManager_create (JNIEnv *env, jobject obj, jint androidSdkVersion, jint socketFd, jstring serverIpString, jint serverPort, jbyteArray senderCipherKey, jbyteArray senderMacKey, jbyteArray senderSalt, jbyteArray receiverCipherKey, jbyteArray receiverMacKey, jbyteArray receiverSalt) { struct sockaddr *sockAddr; int sockAddrLen; constructSockAddr(env, serverIpString, serverPort, &sockAddr, &sockAddrLen); if (sockAddr == NULL) { __android_log_print(ANDROID_LOG_WARN, TAG, "Failed to construct sockAddr!"); env->ThrowNew(env->FindClass("org/thoughtcrime/redphone/audio/NativeAudioException"), "Failed to initialize native audio"); return -1; } SrtpStreamParameters *senderParameters = constructSrtpStreamParameters(env, senderCipherKey, senderMacKey, senderSalt); SrtpStreamParameters *receiverParameters = constructSrtpStreamParameters(env, receiverCipherKey, receiverMacKey, receiverSalt); CallAudioManager *manager = new CallAudioManager(androidSdkVersion, socketFd, sockAddr, sockAddrLen, senderParameters, receiverParameters); if (manager->init() != 0) { delete manager; env->ThrowNew(env->FindClass("org/thoughtcrime/redphone/audio/NativeAudioException"), "Failed to initialize native audio"); return -1; } return (jlong)manager; } void JNICALL Java_org_thoughtcrime_redphone_audio_CallAudioManager_start (JNIEnv *env, jobject obj, jlong handle) { CallAudioManager *manager = reinterpret_cast(handle); int result = manager->start(); if (result == -1) { env->ThrowNew(env->FindClass("org/thoughtcrime/redphone/audio/NativeAudioException"), "Failed to start native audio"); } } void JNICALL Java_org_thoughtcrime_redphone_audio_CallAudioManager_setMute (JNIEnv *env, jobject obj, jlong handle, jboolean muteEnabled) { CallAudioManager *manager = reinterpret_cast(handle); manager->setMute(muteEnabled); } void JNICALL Java_org_thoughtcrime_redphone_audio_CallAudioManager_stop (JNIEnv *env, jobject obj, jlong handle) { CallAudioManager *manager = reinterpret_cast(handle); manager->stop(); } void JNICALL Java_org_thoughtcrime_redphone_audio_CallAudioManager_dispose (JNIEnv *env, jobject obj, jlong handle) { CallAudioManager *manager = reinterpret_cast(handle); delete manager; }