mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-27 02:07:42 +00:00
56a3c99289
Fixes #4409 // FREEBIE
301 lines
9.4 KiB
C++
301 lines
9.4 KiB
C++
|
|
#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();
|
|
|
|
__android_log_print(ANDROID_LOG_WARN, TAG, "Stopping audio player...");
|
|
audioPlayer.stop();
|
|
|
|
__android_log_print(ANDROID_LOG_WARN, TAG, "Stopping jitter buffer...");
|
|
webRtcJitterBuffer.stop();
|
|
|
|
__android_log_print(ANDROID_LOG_WARN, TAG, "Freeing resources...");
|
|
|
|
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;
|
|
}
|
|
|
|
|