session-android/jni/redphone/CallAudioManager.cpp

295 lines
9.1 KiB
C++
Raw Normal View History

#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 <string.h>
#include <stdint.h>
#include <unistd.h>
#include <jni.h>
#include <android/log.h>
#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<CallAudioManager *>(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<CallAudioManager *>(handle);
manager->setMute(muteEnabled);
}
void JNICALL Java_org_thoughtcrime_redphone_audio_CallAudioManager_stop
(JNIEnv *env, jobject obj, jlong handle)
{
CallAudioManager *manager = reinterpret_cast<CallAudioManager*>(handle);
manager->stop();
}
void JNICALL Java_org_thoughtcrime_redphone_audio_CallAudioManager_dispose
(JNIEnv *env, jobject obj, jlong handle)
{
CallAudioManager *manager = reinterpret_cast<CallAudioManager*>(handle);
delete manager;
}