Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
|
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#include "AudioInputAndroid.h"
|
||||
#include <stdio.h>
|
||||
#include "../../logging.h"
|
||||
#include "JNIUtilities.h"
|
||||
#include "tgnet/FileLog.h"
|
||||
|
||||
extern JavaVM* sharedJVM;
|
||||
|
||||
using namespace tgvoip;
|
||||
using namespace tgvoip::audio;
|
||||
|
||||
jmethodID AudioInputAndroid::initMethod=NULL;
|
||||
jmethodID AudioInputAndroid::releaseMethod=NULL;
|
||||
jmethodID AudioInputAndroid::startMethod=NULL;
|
||||
jmethodID AudioInputAndroid::stopMethod=NULL;
|
||||
jmethodID AudioInputAndroid::getEnabledEffectsMaskMethod=NULL;
|
||||
jclass AudioInputAndroid::jniClass=NULL;
|
||||
|
||||
AudioInputAndroid::AudioInputAndroid(){
|
||||
jni::DoWithJNI([this](JNIEnv* env){
|
||||
jmethodID ctor=env->GetMethodID(jniClass, "<init>", "(J)V");
|
||||
jobject obj=env->NewObject(jniClass, ctor, (jlong)(intptr_t)this);
|
||||
DEBUG_REF("AudioInputAndroid");
|
||||
javaObject=env->NewGlobalRef(obj);
|
||||
|
||||
env->CallVoidMethod(javaObject, initMethod, 48000, 16, 1, 960*2);
|
||||
enabledEffects=(unsigned int)env->CallIntMethod(javaObject, getEnabledEffectsMaskMethod);
|
||||
});
|
||||
running=false;
|
||||
}
|
||||
|
||||
AudioInputAndroid::~AudioInputAndroid(){
|
||||
{
|
||||
MutexGuard guard(mutex);
|
||||
jni::DoWithJNI([this](JNIEnv* env){
|
||||
env->CallVoidMethod(javaObject, releaseMethod);
|
||||
DEBUG_DELREF("AudioInputAndroid");
|
||||
env->DeleteGlobalRef(javaObject);
|
||||
javaObject=NULL;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void AudioInputAndroid::Start(){
|
||||
MutexGuard guard(mutex);
|
||||
jni::DoWithJNI([this](JNIEnv* env){
|
||||
failed=!env->CallBooleanMethod(javaObject, startMethod);
|
||||
});
|
||||
running=true;
|
||||
}
|
||||
|
||||
void AudioInputAndroid::Stop(){
|
||||
MutexGuard guard(mutex);
|
||||
running=false;
|
||||
jni::DoWithJNI([this](JNIEnv* env){
|
||||
env->CallVoidMethod(javaObject, stopMethod);
|
||||
});
|
||||
}
|
||||
|
||||
void AudioInputAndroid::HandleCallback(JNIEnv* env, jobject buffer){
|
||||
if(!running)
|
||||
return;
|
||||
unsigned char* buf=(unsigned char*) env->GetDirectBufferAddress(buffer);
|
||||
size_t len=(size_t) env->GetDirectBufferCapacity(buffer);
|
||||
InvokeCallback(buf, len);
|
||||
}
|
||||
|
||||
unsigned int AudioInputAndroid::GetEnabledEffects(){
|
||||
return enabledEffects;
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#ifndef LIBTGVOIP_AUDIOINPUTANDROID_H
|
||||
#define LIBTGVOIP_AUDIOINPUTANDROID_H
|
||||
|
||||
#include <jni.h>
|
||||
#include "../../audio/AudioInput.h"
|
||||
#include "../../threading.h"
|
||||
|
||||
namespace tgvoip{ namespace audio{
|
||||
class AudioInputAndroid : public AudioInput{
|
||||
|
||||
public:
|
||||
AudioInputAndroid();
|
||||
virtual ~AudioInputAndroid();
|
||||
virtual void Start();
|
||||
virtual void Stop();
|
||||
void HandleCallback(JNIEnv* env, jobject buffer);
|
||||
unsigned int GetEnabledEffects();
|
||||
static jmethodID initMethod;
|
||||
static jmethodID releaseMethod;
|
||||
static jmethodID startMethod;
|
||||
static jmethodID stopMethod;
|
||||
static jmethodID getEnabledEffectsMaskMethod;
|
||||
static jclass jniClass;
|
||||
|
||||
static constexpr unsigned int EFFECT_AEC=1;
|
||||
static constexpr unsigned int EFFECT_NS=2;
|
||||
|
||||
private:
|
||||
jobject javaObject;
|
||||
bool running;
|
||||
Mutex mutex;
|
||||
unsigned int enabledEffects=0;
|
||||
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LIBTGVOIP_AUDIOINPUTANDROID_H
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "AudioInputOpenSLES.h"
|
||||
#include "../../logging.h"
|
||||
#include "OpenSLEngineWrapper.h"
|
||||
|
||||
#define CHECK_SL_ERROR(res, msg) if(res!=SL_RESULT_SUCCESS){ LOGE(msg); return; }
|
||||
#define BUFFER_SIZE 960 // 20 ms
|
||||
|
||||
using namespace tgvoip;
|
||||
using namespace tgvoip::audio;
|
||||
|
||||
unsigned int AudioInputOpenSLES::nativeBufferSize;
|
||||
|
||||
AudioInputOpenSLES::AudioInputOpenSLES(){
|
||||
slEngine=OpenSLEngineWrapper::CreateEngine();
|
||||
|
||||
LOGI("Native buffer size is %u samples", nativeBufferSize);
|
||||
if(nativeBufferSize<BUFFER_SIZE && BUFFER_SIZE % nativeBufferSize!=0){
|
||||
LOGE("20ms is not divisible by native buffer size!!");
|
||||
}else if(nativeBufferSize>BUFFER_SIZE && nativeBufferSize%BUFFER_SIZE!=0){
|
||||
LOGE("native buffer size is not multiple of 20ms!!");
|
||||
nativeBufferSize+=nativeBufferSize%BUFFER_SIZE;
|
||||
}
|
||||
if(nativeBufferSize==BUFFER_SIZE)
|
||||
nativeBufferSize*=2;
|
||||
LOGI("Adjusted native buffer size is %u", nativeBufferSize);
|
||||
|
||||
buffer=(int16_t*)calloc(BUFFER_SIZE, sizeof(int16_t));
|
||||
nativeBuffer=(int16_t*)calloc((size_t) nativeBufferSize, sizeof(int16_t));
|
||||
slRecorderObj=NULL;
|
||||
}
|
||||
|
||||
AudioInputOpenSLES::~AudioInputOpenSLES(){
|
||||
//Stop();
|
||||
(*slBufferQueue)->Clear(slBufferQueue);
|
||||
(*slRecorderObj)->Destroy(slRecorderObj);
|
||||
slRecorderObj=NULL;
|
||||
slRecorder=NULL;
|
||||
slBufferQueue=NULL;
|
||||
slEngine=NULL;
|
||||
OpenSLEngineWrapper::DestroyEngine();
|
||||
free(buffer);
|
||||
buffer=NULL;
|
||||
free(nativeBuffer);
|
||||
nativeBuffer=NULL;
|
||||
}
|
||||
|
||||
void AudioInputOpenSLES::BufferCallback(SLAndroidSimpleBufferQueueItf bq, void *context){
|
||||
((AudioInputOpenSLES*)context)->HandleSLCallback();
|
||||
}
|
||||
|
||||
void AudioInputOpenSLES::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
|
||||
assert(slRecorderObj==NULL);
|
||||
SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE,
|
||||
SL_IODEVICE_AUDIOINPUT,
|
||||
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
|
||||
SLDataSource audioSrc = {&loc_dev, NULL};
|
||||
SLDataLocator_AndroidSimpleBufferQueue loc_bq =
|
||||
{SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1};
|
||||
SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, channels, sampleRate*1000,
|
||||
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||
channels==2 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER, SL_BYTEORDER_LITTLEENDIAN};
|
||||
SLDataSink audioSnk = {&loc_bq, &format_pcm};
|
||||
|
||||
const SLInterfaceID id[2] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION};
|
||||
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
|
||||
SLresult result = (*slEngine)->CreateAudioRecorder(slEngine, &slRecorderObj, &audioSrc, &audioSnk, 2, id, req);
|
||||
CHECK_SL_ERROR(result, "Error creating recorder");
|
||||
|
||||
SLAndroidConfigurationItf recorderConfig;
|
||||
result = (*slRecorderObj)->GetInterface(slRecorderObj, SL_IID_ANDROIDCONFIGURATION, &recorderConfig);
|
||||
SLint32 streamType = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
|
||||
result = (*recorderConfig)->SetConfiguration(recorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &streamType, sizeof(SLint32));
|
||||
|
||||
result=(*slRecorderObj)->Realize(slRecorderObj, SL_BOOLEAN_FALSE);
|
||||
CHECK_SL_ERROR(result, "Error realizing recorder");
|
||||
|
||||
result=(*slRecorderObj)->GetInterface(slRecorderObj, SL_IID_RECORD, &slRecorder);
|
||||
CHECK_SL_ERROR(result, "Error getting recorder interface");
|
||||
|
||||
result=(*slRecorderObj)->GetInterface(slRecorderObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &slBufferQueue);
|
||||
CHECK_SL_ERROR(result, "Error getting buffer queue");
|
||||
|
||||
result=(*slBufferQueue)->RegisterCallback(slBufferQueue, AudioInputOpenSLES::BufferCallback, this);
|
||||
CHECK_SL_ERROR(result, "Error setting buffer queue callback");
|
||||
|
||||
(*slBufferQueue)->Enqueue(slBufferQueue, nativeBuffer, nativeBufferSize*sizeof(int16_t));
|
||||
}
|
||||
|
||||
void AudioInputOpenSLES::Start(){
|
||||
SLresult result=(*slRecorder)->SetRecordState(slRecorder, SL_RECORDSTATE_RECORDING);
|
||||
CHECK_SL_ERROR(result, "Error starting record");
|
||||
}
|
||||
|
||||
void AudioInputOpenSLES::Stop(){
|
||||
SLresult result=(*slRecorder)->SetRecordState(slRecorder, SL_RECORDSTATE_STOPPED);
|
||||
CHECK_SL_ERROR(result, "Error stopping record");
|
||||
}
|
||||
|
||||
|
||||
void AudioInputOpenSLES::HandleSLCallback(){
|
||||
//SLmillisecond pMsec = 0;
|
||||
//(*slRecorder)->GetPosition(slRecorder, &pMsec);
|
||||
//LOGI("Callback! pos=%lu", pMsec);
|
||||
//InvokeCallback((unsigned char*)buffer, BUFFER_SIZE*sizeof(int16_t));
|
||||
//fwrite(nativeBuffer, 1, nativeBufferSize*2, test);
|
||||
|
||||
if(nativeBufferSize==BUFFER_SIZE){
|
||||
//LOGV("nativeBufferSize==BUFFER_SIZE");
|
||||
InvokeCallback((unsigned char *) nativeBuffer, BUFFER_SIZE*sizeof(int16_t));
|
||||
}else if(nativeBufferSize<BUFFER_SIZE){
|
||||
//LOGV("nativeBufferSize<BUFFER_SIZE");
|
||||
if(positionInBuffer>=BUFFER_SIZE){
|
||||
InvokeCallback((unsigned char *) buffer, BUFFER_SIZE*sizeof(int16_t));
|
||||
positionInBuffer=0;
|
||||
}
|
||||
memcpy(((unsigned char*)buffer)+positionInBuffer*2, nativeBuffer, (size_t)nativeBufferSize*2);
|
||||
positionInBuffer+=nativeBufferSize;
|
||||
}else if(nativeBufferSize>BUFFER_SIZE){
|
||||
//LOGV("nativeBufferSize>BUFFER_SIZE");
|
||||
for(unsigned int offset=0;offset<nativeBufferSize;offset+=BUFFER_SIZE){
|
||||
InvokeCallback(((unsigned char *) nativeBuffer)+offset*2, BUFFER_SIZE*sizeof(int16_t));
|
||||
}
|
||||
}
|
||||
|
||||
(*slBufferQueue)->Enqueue(slBufferQueue, nativeBuffer, nativeBufferSize*sizeof(int16_t));
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#ifndef LIBTGVOIP_AUDIOINPUTOPENSLES_H
|
||||
#define LIBTGVOIP_AUDIOINPUTOPENSLES_H
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
||||
#include "../../audio/AudioInput.h"
|
||||
|
||||
namespace tgvoip{ namespace audio{
|
||||
class AudioInputOpenSLES : public AudioInput{
|
||||
|
||||
public:
|
||||
AudioInputOpenSLES();
|
||||
virtual ~AudioInputOpenSLES();
|
||||
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
|
||||
virtual void Start();
|
||||
virtual void Stop();
|
||||
|
||||
static unsigned int nativeBufferSize;
|
||||
|
||||
private:
|
||||
static void BufferCallback(SLAndroidSimpleBufferQueueItf bq, void *context);
|
||||
void HandleSLCallback();
|
||||
SLEngineItf slEngine;
|
||||
SLObjectItf slRecorderObj;
|
||||
SLRecordItf slRecorder;
|
||||
SLAndroidSimpleBufferQueueItf slBufferQueue;
|
||||
int16_t* buffer;
|
||||
int16_t* nativeBuffer;
|
||||
size_t positionInBuffer;
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LIBTGVOIP_AUDIOINPUTOPENSLES_H
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#include "AudioOutputAndroid.h"
|
||||
#include <stdio.h>
|
||||
#include "../../logging.h"
|
||||
#include "tgnet/FileLog.h"
|
||||
|
||||
extern JavaVM* sharedJVM;
|
||||
|
||||
using namespace tgvoip;
|
||||
using namespace tgvoip::audio;
|
||||
|
||||
jmethodID AudioOutputAndroid::initMethod=NULL;
|
||||
jmethodID AudioOutputAndroid::releaseMethod=NULL;
|
||||
jmethodID AudioOutputAndroid::startMethod=NULL;
|
||||
jmethodID AudioOutputAndroid::stopMethod=NULL;
|
||||
jclass AudioOutputAndroid::jniClass=NULL;
|
||||
|
||||
AudioOutputAndroid::AudioOutputAndroid(){
|
||||
JNIEnv* env=NULL;
|
||||
bool didAttach=false;
|
||||
sharedJVM->GetEnv((void**) &env, JNI_VERSION_1_6);
|
||||
if(!env){
|
||||
sharedJVM->AttachCurrentThread(&env, NULL);
|
||||
didAttach=true;
|
||||
}
|
||||
|
||||
jmethodID ctor=env->GetMethodID(jniClass, "<init>", "(J)V");
|
||||
jobject obj=env->NewObject(jniClass, ctor, (jlong)(intptr_t)this);
|
||||
DEBUG_REF("AudioOutputAndroid");
|
||||
javaObject=env->NewGlobalRef(obj);
|
||||
|
||||
env->CallVoidMethod(javaObject, initMethod, 48000, 16, 1, 960*2);
|
||||
|
||||
if(didAttach){
|
||||
sharedJVM->DetachCurrentThread();
|
||||
}
|
||||
running=false;
|
||||
}
|
||||
|
||||
AudioOutputAndroid::~AudioOutputAndroid(){
|
||||
JNIEnv* env=NULL;
|
||||
bool didAttach=false;
|
||||
sharedJVM->GetEnv((void**) &env, JNI_VERSION_1_6);
|
||||
if(!env){
|
||||
sharedJVM->AttachCurrentThread(&env, NULL);
|
||||
didAttach=true;
|
||||
}
|
||||
|
||||
env->CallVoidMethod(javaObject, releaseMethod);
|
||||
DEBUG_DELREF("AudioOutputAndroid");
|
||||
env->DeleteGlobalRef(javaObject);
|
||||
javaObject=NULL;
|
||||
|
||||
if(didAttach){
|
||||
sharedJVM->DetachCurrentThread();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioOutputAndroid::Start(){
|
||||
JNIEnv* env=NULL;
|
||||
bool didAttach=false;
|
||||
sharedJVM->GetEnv((void**) &env, JNI_VERSION_1_6);
|
||||
if(!env){
|
||||
sharedJVM->AttachCurrentThread(&env, NULL);
|
||||
didAttach=true;
|
||||
}
|
||||
|
||||
env->CallVoidMethod(javaObject, startMethod);
|
||||
|
||||
if(didAttach){
|
||||
sharedJVM->DetachCurrentThread();
|
||||
}
|
||||
running=true;
|
||||
}
|
||||
|
||||
void AudioOutputAndroid::Stop(){
|
||||
running=false;
|
||||
JNIEnv* env=NULL;
|
||||
bool didAttach=false;
|
||||
sharedJVM->GetEnv((void**) &env, JNI_VERSION_1_6);
|
||||
if(!env){
|
||||
sharedJVM->AttachCurrentThread(&env, NULL);
|
||||
didAttach=true;
|
||||
}
|
||||
|
||||
env->CallVoidMethod(javaObject, stopMethod);
|
||||
|
||||
if(didAttach){
|
||||
sharedJVM->DetachCurrentThread();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioOutputAndroid::HandleCallback(JNIEnv* env, jbyteArray buffer){
|
||||
if(!running)
|
||||
return;
|
||||
unsigned char* buf=(unsigned char*) env->GetByteArrayElements(buffer, NULL);
|
||||
size_t len=(size_t) env->GetArrayLength(buffer);
|
||||
InvokeCallback(buf, len);
|
||||
env->ReleaseByteArrayElements(buffer, (jbyte *) buf, 0);
|
||||
}
|
||||
|
||||
|
||||
bool AudioOutputAndroid::IsPlaying(){
|
||||
return running;
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#ifndef LIBTGVOIP_AUDIOOUTPUTANDROID_H
|
||||
#define LIBTGVOIP_AUDIOOUTPUTANDROID_H
|
||||
|
||||
#include <jni.h>
|
||||
#include "../../audio/AudioOutput.h"
|
||||
|
||||
namespace tgvoip{ namespace audio{
|
||||
class AudioOutputAndroid : public AudioOutput{
|
||||
|
||||
public:
|
||||
|
||||
AudioOutputAndroid();
|
||||
virtual ~AudioOutputAndroid();
|
||||
virtual void Start();
|
||||
virtual void Stop();
|
||||
virtual bool IsPlaying() override;
|
||||
void HandleCallback(JNIEnv* env, jbyteArray buffer);
|
||||
static jmethodID initMethod;
|
||||
static jmethodID releaseMethod;
|
||||
static jmethodID startMethod;
|
||||
static jmethodID stopMethod;
|
||||
static jclass jniClass;
|
||||
|
||||
private:
|
||||
jobject javaObject;
|
||||
bool running;
|
||||
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LIBTGVOIP_AUDIOOUTPUTANDROID_H
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include "AudioOutputOpenSLES.h"
|
||||
#include "../../logging.h"
|
||||
#include "../../VoIPController.h"
|
||||
#include "OpenSLEngineWrapper.h"
|
||||
#include "AudioInputAndroid.h"
|
||||
|
||||
#define CHECK_SL_ERROR(res, msg) if(res!=SL_RESULT_SUCCESS){ LOGE(msg); failed=true; return; }
|
||||
#define BUFFER_SIZE 960 // 20 ms
|
||||
|
||||
using namespace tgvoip;
|
||||
using namespace tgvoip::audio;
|
||||
|
||||
unsigned int AudioOutputOpenSLES::nativeBufferSize;
|
||||
|
||||
AudioOutputOpenSLES::AudioOutputOpenSLES(){
|
||||
SLresult result;
|
||||
slEngine=OpenSLEngineWrapper::CreateEngine();
|
||||
|
||||
const SLInterfaceID pOutputMixIDs[] = {};
|
||||
const SLboolean pOutputMixRequired[] = {};
|
||||
result = (*slEngine)->CreateOutputMix(slEngine, &slOutputMixObj, 0, pOutputMixIDs, pOutputMixRequired);
|
||||
CHECK_SL_ERROR(result, "Error creating output mix");
|
||||
|
||||
result = (*slOutputMixObj)->Realize(slOutputMixObj, SL_BOOLEAN_FALSE);
|
||||
CHECK_SL_ERROR(result, "Error realizing output mix");
|
||||
|
||||
LOGI("Native buffer size is %u samples", nativeBufferSize);
|
||||
/*if(nativeBufferSize<BUFFER_SIZE && BUFFER_SIZE % nativeBufferSize!=0){
|
||||
LOGE("20ms is not divisible by native buffer size!!");
|
||||
nativeBufferSize=BUFFER_SIZE;
|
||||
}else if(nativeBufferSize>BUFFER_SIZE && nativeBufferSize%BUFFER_SIZE!=0){
|
||||
LOGE("native buffer size is not multiple of 20ms!!");
|
||||
nativeBufferSize+=nativeBufferSize%BUFFER_SIZE;
|
||||
}
|
||||
LOGI("Adjusted native buffer size is %u", nativeBufferSize);*/
|
||||
|
||||
buffer=(int16_t*)calloc(BUFFER_SIZE, sizeof(int16_t));
|
||||
nativeBuffer=(int16_t*)calloc((size_t) nativeBufferSize, sizeof(int16_t));
|
||||
slPlayerObj=NULL;
|
||||
remainingDataSize=0;
|
||||
}
|
||||
|
||||
AudioOutputOpenSLES::~AudioOutputOpenSLES(){
|
||||
if(!stopped)
|
||||
Stop();
|
||||
(*slBufferQueue)->Clear(slBufferQueue);
|
||||
LOGV("destroy slPlayerObj");
|
||||
(*slPlayerObj)->Destroy(slPlayerObj);
|
||||
LOGV("destroy slOutputMixObj");
|
||||
(*slOutputMixObj)->Destroy(slOutputMixObj);
|
||||
OpenSLEngineWrapper::DestroyEngine();
|
||||
free(buffer);
|
||||
free(nativeBuffer);
|
||||
}
|
||||
|
||||
|
||||
void AudioOutputOpenSLES::SetNativeBufferSize(unsigned int size){
|
||||
AudioOutputOpenSLES::nativeBufferSize=size;
|
||||
}
|
||||
|
||||
void AudioOutputOpenSLES::BufferCallback(SLAndroidSimpleBufferQueueItf bq, void *context){
|
||||
((AudioOutputOpenSLES*)context)->HandleSLCallback();
|
||||
}
|
||||
|
||||
void AudioOutputOpenSLES::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels){
|
||||
assert(slPlayerObj==NULL);
|
||||
SLDataLocator_AndroidSimpleBufferQueue locatorBufferQueue =
|
||||
{SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1};
|
||||
SLDataFormat_PCM formatPCM = {SL_DATAFORMAT_PCM, channels, sampleRate*1000,
|
||||
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
|
||||
channels==2 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER, SL_BYTEORDER_LITTLEENDIAN};
|
||||
SLDataSource audioSrc = {&locatorBufferQueue, &formatPCM};
|
||||
SLDataLocator_OutputMix locatorOutMix = {SL_DATALOCATOR_OUTPUTMIX, slOutputMixObj};
|
||||
SLDataSink audioSnk = {&locatorOutMix, NULL};
|
||||
|
||||
const SLInterfaceID id[2] = {SL_IID_BUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION};
|
||||
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
|
||||
SLresult result = (*slEngine)->CreateAudioPlayer(slEngine, &slPlayerObj, &audioSrc, &audioSnk, 2, id, req);
|
||||
CHECK_SL_ERROR(result, "Error creating player");
|
||||
|
||||
|
||||
SLAndroidConfigurationItf playerConfig;
|
||||
result = (*slPlayerObj)->GetInterface(slPlayerObj, SL_IID_ANDROIDCONFIGURATION, &playerConfig);
|
||||
SLint32 streamType = SL_ANDROID_STREAM_VOICE;
|
||||
result = (*playerConfig)->SetConfiguration(playerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
|
||||
|
||||
|
||||
result=(*slPlayerObj)->Realize(slPlayerObj, SL_BOOLEAN_FALSE);
|
||||
CHECK_SL_ERROR(result, "Error realizing player");
|
||||
|
||||
result=(*slPlayerObj)->GetInterface(slPlayerObj, SL_IID_PLAY, &slPlayer);
|
||||
CHECK_SL_ERROR(result, "Error getting player interface");
|
||||
|
||||
result=(*slPlayerObj)->GetInterface(slPlayerObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &slBufferQueue);
|
||||
CHECK_SL_ERROR(result, "Error getting buffer queue");
|
||||
|
||||
result=(*slBufferQueue)->RegisterCallback(slBufferQueue, AudioOutputOpenSLES::BufferCallback, this);
|
||||
CHECK_SL_ERROR(result, "Error setting buffer queue callback");
|
||||
|
||||
(*slBufferQueue)->Enqueue(slBufferQueue, nativeBuffer, nativeBufferSize*sizeof(int16_t));
|
||||
}
|
||||
|
||||
bool AudioOutputOpenSLES::IsPhone(){
|
||||
return false;
|
||||
}
|
||||
|
||||
void AudioOutputOpenSLES::EnableLoudspeaker(bool enabled){
|
||||
|
||||
}
|
||||
|
||||
void AudioOutputOpenSLES::Start(){
|
||||
stopped=false;
|
||||
SLresult result=(*slPlayer)->SetPlayState(slPlayer, SL_PLAYSTATE_PLAYING);
|
||||
CHECK_SL_ERROR(result, "Error starting player");
|
||||
}
|
||||
|
||||
void AudioOutputOpenSLES::Stop(){
|
||||
stopped=true;
|
||||
LOGV("Stopping OpenSL output");
|
||||
SLresult result=(*slPlayer)->SetPlayState(slPlayer, SL_PLAYSTATE_PAUSED);
|
||||
CHECK_SL_ERROR(result, "Error starting player");
|
||||
}
|
||||
|
||||
void AudioOutputOpenSLES::HandleSLCallback(){
|
||||
/*if(stopped){
|
||||
//LOGV("left HandleSLCallback early");
|
||||
return;
|
||||
}*/
|
||||
//LOGV("before InvokeCallback");
|
||||
if(!stopped){
|
||||
while(remainingDataSize<nativeBufferSize*2){
|
||||
assert(remainingDataSize+BUFFER_SIZE*2<10240);
|
||||
InvokeCallback(remainingData+remainingDataSize, BUFFER_SIZE*2);
|
||||
remainingDataSize+=BUFFER_SIZE*2;
|
||||
}
|
||||
memcpy(nativeBuffer, remainingData, nativeBufferSize*2);
|
||||
remainingDataSize-=nativeBufferSize*2;
|
||||
if(remainingDataSize>0)
|
||||
memmove(remainingData, remainingData+nativeBufferSize*2, remainingDataSize);
|
||||
//InvokeCallback((unsigned char *) nativeBuffer, nativeBufferSize*sizeof(int16_t));
|
||||
}else{
|
||||
memset(nativeBuffer, 0, nativeBufferSize*2);
|
||||
}
|
||||
|
||||
(*slBufferQueue)->Enqueue(slBufferQueue, nativeBuffer, nativeBufferSize*sizeof(int16_t));
|
||||
//LOGV("left HandleSLCallback");
|
||||
}
|
||||
|
||||
|
||||
bool AudioOutputOpenSLES::IsPlaying(){
|
||||
if(slPlayer){
|
||||
uint32_t state;
|
||||
(*slPlayer)->GetPlayState(slPlayer, &state);
|
||||
return state==SL_PLAYSTATE_PLAYING;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
float AudioOutputOpenSLES::GetLevel(){
|
||||
return 0; // we don't use this anyway
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#ifndef LIBTGVOIP_AUDIOOUTPUTOPENSLES_H
|
||||
#define LIBTGVOIP_AUDIOOUTPUTOPENSLES_H
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
||||
#include "../../audio/AudioOutput.h"
|
||||
|
||||
namespace tgvoip{ namespace audio{
|
||||
class AudioOutputOpenSLES : public AudioOutput{
|
||||
public:
|
||||
AudioOutputOpenSLES();
|
||||
virtual ~AudioOutputOpenSLES();
|
||||
virtual void Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels);
|
||||
virtual bool IsPhone();
|
||||
virtual void EnableLoudspeaker(bool enabled);
|
||||
virtual void Start();
|
||||
virtual void Stop();
|
||||
virtual bool IsPlaying();
|
||||
virtual float GetLevel();
|
||||
|
||||
static void SetNativeBufferSize(unsigned int size);
|
||||
static unsigned int nativeBufferSize;
|
||||
|
||||
private:
|
||||
static void BufferCallback(SLAndroidSimpleBufferQueueItf bq, void *context);
|
||||
void HandleSLCallback();
|
||||
SLEngineItf slEngine;
|
||||
SLObjectItf slPlayerObj;
|
||||
SLObjectItf slOutputMixObj;
|
||||
SLPlayItf slPlayer;
|
||||
SLAndroidSimpleBufferQueueItf slBufferQueue;
|
||||
int16_t* buffer;
|
||||
int16_t* nativeBuffer;
|
||||
bool stopped;
|
||||
unsigned char remainingData[10240];
|
||||
size_t remainingDataSize;
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LIBTGVOIP_AUDIOOUTPUTANDROID_H
|
||||
70
TMessagesProj/jni/voip/libtgvoip/os/android/JNIUtilities.h
Normal file
70
TMessagesProj/jni/voip/libtgvoip/os/android/JNIUtilities.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
//
|
||||
// Created by Grishka on 15.08.2018.
|
||||
//
|
||||
|
||||
#ifndef LIBTGVOIP_JNIUTILITIES_H
|
||||
#define LIBTGVOIP_JNIUTILITIES_H
|
||||
|
||||
#include <functional>
|
||||
#include <jni.h>
|
||||
#include <stdarg.h>
|
||||
#include <string>
|
||||
#include "../../Buffers.h"
|
||||
|
||||
extern JavaVM* sharedJVM;
|
||||
|
||||
namespace tgvoip{
|
||||
namespace jni{
|
||||
|
||||
inline JNIEnv *GetEnv() {
|
||||
JNIEnv *env = nullptr;
|
||||
sharedJVM->GetEnv((void **) &env, JNI_VERSION_1_6);
|
||||
return env;
|
||||
}
|
||||
|
||||
inline void DoWithJNI(std::function<void(JNIEnv*)> f){
|
||||
JNIEnv *env=GetEnv();
|
||||
bool didAttach=false;
|
||||
if(!env){
|
||||
sharedJVM->AttachCurrentThread(&env, NULL);
|
||||
didAttach=true;
|
||||
}
|
||||
|
||||
f(env);
|
||||
|
||||
if(didAttach){
|
||||
sharedJVM->DetachCurrentThread();
|
||||
}
|
||||
}
|
||||
|
||||
inline void AttachAndCallVoidMethod(jmethodID method, jobject obj, ...){
|
||||
if(!method || !obj)
|
||||
return;
|
||||
va_list va;
|
||||
va_start(va, obj);
|
||||
DoWithJNI([&va, method, obj](JNIEnv* env){
|
||||
env->CallVoidMethodV(obj, method, va);
|
||||
});
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
inline std::string JavaStringToStdString(JNIEnv* env, jstring jstr){
|
||||
if(!jstr)
|
||||
return "";
|
||||
const char* jchars=env->GetStringUTFChars(jstr, NULL);
|
||||
std::string str(jchars);
|
||||
env->ReleaseStringUTFChars(jstr, jchars);
|
||||
return str;
|
||||
}
|
||||
|
||||
inline jbyteArray BufferToByteArray(JNIEnv* env, Buffer& buf){
|
||||
jbyteArray arr=env->NewByteArray((jsize)buf.Length());
|
||||
jbyte* elements=env->GetByteArrayElements(arr, NULL);
|
||||
memcpy(elements, *buf, buf.Length());
|
||||
env->ReleaseByteArrayElements(arr, elements, 0);
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif //LIBTGVOIP_JNIUTILITIES_H
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#include <cstddef>
|
||||
#include "OpenSLEngineWrapper.h"
|
||||
#include "../../logging.h"
|
||||
|
||||
#define CHECK_SL_ERROR(res, msg) if(res!=SL_RESULT_SUCCESS){ LOGE(msg); return NULL; }
|
||||
|
||||
using namespace tgvoip;
|
||||
using namespace tgvoip::audio;
|
||||
|
||||
|
||||
SLObjectItf OpenSLEngineWrapper::sharedEngineObj=NULL;
|
||||
SLEngineItf OpenSLEngineWrapper::sharedEngine=NULL;
|
||||
int OpenSLEngineWrapper::count=0;
|
||||
|
||||
void OpenSLEngineWrapper::DestroyEngine(){
|
||||
count--;
|
||||
LOGI("release: engine instance count %d", count);
|
||||
if(count==0){
|
||||
(*sharedEngineObj)->Destroy(sharedEngineObj);
|
||||
sharedEngineObj=NULL;
|
||||
sharedEngine=NULL;
|
||||
}
|
||||
LOGI("after release");
|
||||
}
|
||||
|
||||
SLEngineItf OpenSLEngineWrapper::CreateEngine(){
|
||||
count++;
|
||||
if(sharedEngine)
|
||||
return sharedEngine;
|
||||
const SLInterfaceID pIDs[1] = {SL_IID_ENGINE};
|
||||
const SLboolean pIDsRequired[1] = {SL_BOOLEAN_TRUE};
|
||||
SLresult result = slCreateEngine(&sharedEngineObj, 0, NULL, 1, pIDs, pIDsRequired);
|
||||
CHECK_SL_ERROR(result, "Error creating engine");
|
||||
|
||||
result=(*sharedEngineObj)->Realize(sharedEngineObj, SL_BOOLEAN_FALSE);
|
||||
CHECK_SL_ERROR(result, "Error realizing engine");
|
||||
|
||||
result = (*sharedEngineObj)->GetInterface(sharedEngineObj, SL_IID_ENGINE, &sharedEngine);
|
||||
CHECK_SL_ERROR(result, "Error getting engine interface");
|
||||
return sharedEngine;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// libtgvoip is free and unencumbered public domain software.
|
||||
// For more information, see http://unlicense.org or the UNLICENSE file
|
||||
// you should have received with this source code distribution.
|
||||
//
|
||||
|
||||
#ifndef LIBTGVOIP_OPENSLENGINEWRAPPER_H
|
||||
#define LIBTGVOIP_OPENSLENGINEWRAPPER_H
|
||||
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
||||
namespace tgvoip{ namespace audio{
|
||||
class OpenSLEngineWrapper{
|
||||
public:
|
||||
static SLEngineItf CreateEngine();
|
||||
static void DestroyEngine();
|
||||
|
||||
private:
|
||||
static SLObjectItf sharedEngineObj;
|
||||
static SLEngineItf sharedEngine;
|
||||
static int count;
|
||||
};
|
||||
}}
|
||||
|
||||
#endif //LIBTGVOIP_OPENSLENGINEWRAPPER_H
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
//
|
||||
// Created by Grishka on 12.08.2018.
|
||||
//
|
||||
|
||||
#include "VideoRendererAndroid.h"
|
||||
#include "JNIUtilities.h"
|
||||
#include "../../PrivateDefines.h"
|
||||
#include "../../logging.h"
|
||||
|
||||
using namespace tgvoip;
|
||||
using namespace tgvoip::video;
|
||||
|
||||
jmethodID VideoRendererAndroid::resetMethod=NULL;
|
||||
jmethodID VideoRendererAndroid::decodeAndDisplayMethod=NULL;
|
||||
jmethodID VideoRendererAndroid::setStreamEnabledMethod=NULL;
|
||||
jmethodID VideoRendererAndroid::setRotationMethod=NULL;
|
||||
std::vector<uint32_t> VideoRendererAndroid::availableDecoders;
|
||||
int VideoRendererAndroid::maxResolution;
|
||||
|
||||
extern JavaVM* sharedJVM;
|
||||
|
||||
VideoRendererAndroid::VideoRendererAndroid(jobject jobj) : queue(50){
|
||||
this->jobj=jobj;
|
||||
}
|
||||
|
||||
VideoRendererAndroid::~VideoRendererAndroid(){
|
||||
running=false;
|
||||
Request req{
|
||||
Buffer(0),
|
||||
Request::Type::Shutdown
|
||||
};
|
||||
queue.Put(std::move(req));
|
||||
thread->Join();
|
||||
delete thread;
|
||||
/*decoderThread.Post([this]{
|
||||
decoderEnv->DeleteGlobalRef(jobj);
|
||||
});*/
|
||||
}
|
||||
|
||||
void VideoRendererAndroid::Reset(uint32_t codec, unsigned int width, unsigned int height, std::vector<Buffer> &_csd){
|
||||
csd.clear();
|
||||
for(Buffer& b:_csd){
|
||||
csd.push_back(Buffer::CopyOf(b));
|
||||
}
|
||||
this->codec=codec;
|
||||
this->width=width;
|
||||
this->height=height;
|
||||
Request req{
|
||||
Buffer(0),
|
||||
Request::Type::ResetDecoder
|
||||
};
|
||||
queue.Put(std::move(req));
|
||||
Request req2{
|
||||
Buffer(0),
|
||||
Request::Type::UpdateStreamState
|
||||
};
|
||||
queue.Put(std::move(req2));
|
||||
|
||||
if(!thread){
|
||||
thread=new Thread(std::bind(&VideoRendererAndroid::RunThread, this));
|
||||
thread->Start();
|
||||
}
|
||||
}
|
||||
|
||||
void VideoRendererAndroid::DecodeAndDisplay(Buffer frame, uint32_t pts){
|
||||
/*decoderThread.Post(std::bind([this](Buffer frame){
|
||||
}, std::move(frame)));*/
|
||||
//LOGV("2 before decode %u", (unsigned int)frame.Length());
|
||||
Request req{
|
||||
std::move(frame),
|
||||
Request::Type::DecodeFrame
|
||||
};
|
||||
queue.Put(std::move(req));
|
||||
}
|
||||
|
||||
void VideoRendererAndroid::RunThread(){
|
||||
JNIEnv* env;
|
||||
sharedJVM->AttachCurrentThread(&env, NULL);
|
||||
|
||||
constexpr size_t bufferSize=200*1024;
|
||||
unsigned char* buf=reinterpret_cast<unsigned char*>(malloc(bufferSize));
|
||||
jobject jbuf=env->NewDirectByteBuffer(buf, bufferSize);
|
||||
uint16_t lastSetRotation=0;
|
||||
|
||||
while(running){
|
||||
//LOGV("before get from queue");
|
||||
Request request=std::move(queue.GetBlocking());
|
||||
//LOGV("1 before decode %u", (unsigned int)request.Length());
|
||||
if(request.type==Request::Type::Shutdown){
|
||||
LOGI("Shutting down video decoder thread");
|
||||
break;
|
||||
}else if(request.type==Request::Type::DecodeFrame){
|
||||
if(request.buffer.Length()>bufferSize){
|
||||
LOGE("Frame data is too long (%u, max %u)", (int) request.buffer.Length(), (int) bufferSize);
|
||||
}else{
|
||||
if(lastSetRotation!=rotation){
|
||||
lastSetRotation=rotation;
|
||||
env->CallVoidMethod(jobj, setRotationMethod, (jint)rotation);
|
||||
}
|
||||
memcpy(buf, *request.buffer, request.buffer.Length());
|
||||
env->CallVoidMethod(jobj, decodeAndDisplayMethod, jbuf, (jint) request.buffer.Length(), 0);
|
||||
}
|
||||
}else if(request.type==Request::Type::ResetDecoder){
|
||||
jobjectArray jcsd=NULL;
|
||||
if(!csd.empty()){
|
||||
jcsd=env->NewObjectArray((jsize)csd.size(), env->FindClass("[B"), NULL);
|
||||
jsize i=0;
|
||||
for(Buffer& b:csd){
|
||||
env->SetObjectArrayElement(jcsd, i, jni::BufferToByteArray(env, b));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
std::string codecStr="";
|
||||
switch(codec){
|
||||
case CODEC_AVC:
|
||||
codecStr="video/avc";
|
||||
break;
|
||||
case CODEC_HEVC:
|
||||
codecStr="video/hevc";
|
||||
break;
|
||||
case CODEC_VP8:
|
||||
codecStr="video/x-vnd.on2.vp8";
|
||||
break;
|
||||
case CODEC_VP9:
|
||||
codecStr="video/x-vnd.on2.vp9";
|
||||
break;
|
||||
}
|
||||
env->CallVoidMethod(jobj, resetMethod, env->NewStringUTF(codecStr.c_str()), (jint)width, (jint)height, jcsd);
|
||||
}else if(request.type==Request::Type::UpdateStreamState){
|
||||
env->CallVoidMethod(jobj, setStreamEnabledMethod, streamEnabled);
|
||||
}
|
||||
}
|
||||
free(buf);
|
||||
sharedJVM->DetachCurrentThread();
|
||||
LOGI("==== decoder thread exiting ====");
|
||||
}
|
||||
|
||||
void VideoRendererAndroid::SetStreamEnabled(bool enabled){
|
||||
LOGI("Video stream state: %d", enabled);
|
||||
streamEnabled=enabled;
|
||||
Request req{
|
||||
Buffer(0),
|
||||
Request::Type::UpdateStreamState
|
||||
};
|
||||
queue.Put(std::move(req));
|
||||
}
|
||||
|
||||
void VideoRendererAndroid::SetRotation(uint16_t rotation){
|
||||
this->rotation=rotation;
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
//
|
||||
// Created by Grishka on 12.08.2018.
|
||||
//
|
||||
|
||||
#ifndef LIBTGVOIP_VIDEORENDERERANDROID_H
|
||||
#define LIBTGVOIP_VIDEORENDERERANDROID_H
|
||||
|
||||
#include "../../video/VideoRenderer.h"
|
||||
#include "../../MessageThread.h"
|
||||
|
||||
#include <jni.h>
|
||||
#include "../../BlockingQueue.h"
|
||||
|
||||
namespace tgvoip{
|
||||
namespace video{
|
||||
class VideoRendererAndroid : public VideoRenderer{
|
||||
public:
|
||||
VideoRendererAndroid(jobject jobj);
|
||||
virtual ~VideoRendererAndroid();
|
||||
virtual void Reset(uint32_t codec, unsigned int width, unsigned int height, std::vector<Buffer>& csd) override;
|
||||
virtual void DecodeAndDisplay(Buffer frame, uint32_t pts) override;
|
||||
virtual void SetStreamEnabled(bool enabled) override;
|
||||
virtual void SetRotation(uint16_t rotation) override;
|
||||
|
||||
static jmethodID resetMethod;
|
||||
static jmethodID decodeAndDisplayMethod;
|
||||
static jmethodID setStreamEnabledMethod;
|
||||
static jmethodID setRotationMethod;
|
||||
static std::vector<uint32_t> availableDecoders;
|
||||
static int maxResolution;
|
||||
private:
|
||||
struct Request{
|
||||
enum Type{
|
||||
DecodeFrame,
|
||||
ResetDecoder,
|
||||
UpdateStreamState,
|
||||
Shutdown
|
||||
};
|
||||
|
||||
Buffer buffer;
|
||||
Type type;
|
||||
};
|
||||
void RunThread();
|
||||
Thread* thread=NULL;
|
||||
bool running=true;
|
||||
BlockingQueue<Request> queue;
|
||||
std::vector<Buffer> csd;
|
||||
int width;
|
||||
int height;
|
||||
bool streamEnabled=true;
|
||||
uint32_t codec;
|
||||
uint16_t rotation=0;
|
||||
jobject jobj;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif //LIBTGVOIP_VIDEORENDERERANDROID_H
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
//
|
||||
// Created by Grishka on 12.08.2018.
|
||||
//
|
||||
|
||||
#include "VideoSourceAndroid.h"
|
||||
#include "JNIUtilities.h"
|
||||
#include "../../logging.h"
|
||||
#include "../../PrivateDefines.h"
|
||||
#include "tgnet/FileLog.h"
|
||||
|
||||
using namespace tgvoip;
|
||||
using namespace tgvoip::video;
|
||||
|
||||
extern JavaVM* sharedJVM;
|
||||
|
||||
std::vector<uint32_t> VideoSourceAndroid::availableEncoders;
|
||||
|
||||
VideoSourceAndroid::VideoSourceAndroid(jobject jobj) : javaObject(jobj){
|
||||
jni::DoWithJNI([&](JNIEnv* env){
|
||||
jclass cls=env->GetObjectClass(javaObject);
|
||||
startMethod=env->GetMethodID(cls, "start", "()V");
|
||||
stopMethod=env->GetMethodID(cls, "stop", "()V");
|
||||
prepareEncoderMethod=env->GetMethodID(cls, "prepareEncoder", "(Ljava/lang/String;I)V");
|
||||
requestKeyFrameMethod=env->GetMethodID(cls, "requestKeyFrame", "()V");
|
||||
setBitrateMethod=env->GetMethodID(cls, "setBitrate", "(I)V");
|
||||
});
|
||||
}
|
||||
|
||||
VideoSourceAndroid::~VideoSourceAndroid(){
|
||||
jni::DoWithJNI([this](JNIEnv* env){
|
||||
DEBUG_DELREF("VideoSourceAndroid");
|
||||
env->DeleteGlobalRef(javaObject);
|
||||
});
|
||||
}
|
||||
|
||||
void VideoSourceAndroid::Start(){
|
||||
jni::DoWithJNI([this](JNIEnv* env){
|
||||
env->CallVoidMethod(javaObject, startMethod);
|
||||
});
|
||||
}
|
||||
|
||||
void VideoSourceAndroid::Stop(){
|
||||
jni::DoWithJNI([this](JNIEnv* env){
|
||||
env->CallVoidMethod(javaObject, stopMethod);
|
||||
});
|
||||
}
|
||||
|
||||
void VideoSourceAndroid::SendFrame(Buffer frame, uint32_t flags){
|
||||
callback(frame, flags, rotation);
|
||||
}
|
||||
|
||||
void VideoSourceAndroid::SetStreamParameters(std::vector<Buffer> csd, unsigned int width, unsigned int height){
|
||||
LOGD("Video stream parameters: %d x %d", width, height);
|
||||
this->width=width;
|
||||
this->height=height;
|
||||
this->csd=std::move(csd);
|
||||
}
|
||||
|
||||
void VideoSourceAndroid::Reset(uint32_t codec, int maxResolution){
|
||||
jni::DoWithJNI([&](JNIEnv* env){
|
||||
std::string codecStr="";
|
||||
switch(codec){
|
||||
case CODEC_AVC:
|
||||
codecStr="video/avc";
|
||||
break;
|
||||
case CODEC_HEVC:
|
||||
codecStr="video/hevc";
|
||||
break;
|
||||
case CODEC_VP8:
|
||||
codecStr="video/x-vnd.on2.vp8";
|
||||
break;
|
||||
case CODEC_VP9:
|
||||
codecStr="video/x-vnd.on2.vp9";
|
||||
break;
|
||||
}
|
||||
env->CallVoidMethod(javaObject, prepareEncoderMethod, env->NewStringUTF(codecStr.c_str()), maxResolution);
|
||||
});
|
||||
}
|
||||
|
||||
void VideoSourceAndroid::RequestKeyFrame(){
|
||||
jni::DoWithJNI([this](JNIEnv* env){
|
||||
env->CallVoidMethod(javaObject, requestKeyFrameMethod);
|
||||
});
|
||||
}
|
||||
|
||||
void VideoSourceAndroid::SetBitrate(uint32_t bitrate){
|
||||
jni::DoWithJNI([&](JNIEnv* env){
|
||||
env->CallVoidMethod(javaObject, setBitrateMethod, (jint)bitrate);
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// Created by Grishka on 12.08.2018.
|
||||
//
|
||||
|
||||
#ifndef LIBTGVOIP_VIDEOSOURCEANDROID_H
|
||||
#define LIBTGVOIP_VIDEOSOURCEANDROID_H
|
||||
|
||||
#include "../../video/VideoSource.h"
|
||||
#include "../../Buffers.h"
|
||||
#include <jni.h>
|
||||
#include <vector>
|
||||
|
||||
namespace tgvoip{
|
||||
namespace video{
|
||||
class VideoSourceAndroid : public VideoSource{
|
||||
public:
|
||||
VideoSourceAndroid(jobject jobj);
|
||||
virtual ~VideoSourceAndroid();
|
||||
virtual void Start() override;
|
||||
virtual void Stop() override;
|
||||
virtual void Reset(uint32_t codec, int maxResolution) override;
|
||||
void SendFrame(Buffer frame, uint32_t flags);
|
||||
void SetStreamParameters(std::vector<Buffer> csd, unsigned int width, unsigned int height);
|
||||
virtual void RequestKeyFrame() override;
|
||||
virtual void SetBitrate(uint32_t bitrate) override;
|
||||
|
||||
static std::vector<uint32_t> availableEncoders;
|
||||
private:
|
||||
jobject javaObject;
|
||||
jmethodID prepareEncoderMethod;
|
||||
jmethodID startMethod;
|
||||
jmethodID stopMethod;
|
||||
jmethodID requestKeyFrameMethod;
|
||||
jmethodID setBitrateMethod;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif //LIBTGVOIP_VIDEOSOURCEANDROID_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue