Getting error as libOpenSLES Leaving BufferQueue::Enqueue (SL_RESULT_BUFFER_INSUFFICIENT) - android

Hi I am working on Android JNI Audio part, below jni code I am calling from android 12 times as per my logic. As soon as 1st clip played I send callback event to android and I am again calling selectClip() to play audio clip. I am able to play 2 times but on third call application throwing error as libOpenSLES Leaving BufferQueue : SL_RESULT_BUFFER_INSUFFICIENT.
Am I missing something in this?
Any pointer on the same to resolve this?
jboolean flag=JNI_TRUE;
jint clipNote = 0;
// select the desired clip and play count, and enqueue the first buffer if idle
JNIEXPORT jboolean JNICALL Java_com_example_nativeaudio_NativeAudio_selectClip(JNIEnv * env, jobject obj,
jint count)
{
// sleep logic
while((clock() - t)/1000 < 2000) {
usleep(10000); // sleep for 10ms
}
if(clipNote < 12) {
if(flag == JNI_TRUE) {
__android_log_print(ANDROID_LOG_DEBUG , "CustomTag", " flag = true : ClipNote : %d",clipNote);
clipNote = clipNote + 1;
nextBuffer = (short *) audio1;
nextSize = sizeof(audio1);
nextCount = count/2000;
if (nextSize > 0) {
// here we only enqueue one buffer because it is a long clip,
// but for streaming playback we would typically enqueue at least 2 buffers to start
SLresult result;
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, nextBuffer, nextSize);
if (SL_RESULT_SUCCESS != result) {
return JNI_FALSE;
}
}
// callback to android
jclass cls = (*env)->GetObjectClass(env, obj);
jmethodID mid = (*env)->GetMethodID(env, cls, "callBackStart", "(I)V");
if (mid == 0) {
return;
}
flag=JNI_FALSE;
(*env)->CallVoidMethod(env, obj, mid, clipNote);
} else {
// callback to android
__android_log_print(ANDROID_LOG_DEBUG , "CustomTag", " flag = false");
jclass cls = (*env)->GetObjectClass(env, obj);
jmethodID mid = (*env)->GetMethodID(env, cls, "callBackRelease", "(I)V");
if (mid == 0) {
return;
}
flag = JNI_TRUE;
(*env)->CallVoidMethod(env, obj, mid, count);
}
t = clock();
} else {
SLresult result;
// make sure the asset audio player was created
if (NULL != fdPlayerPlay) {
result = (*fdPlayerPlay)>SetPlayState(fdPlayerPlay,SL_PLAYSTATE_PAUSED);
assert(SL_RESULT_SUCCESS == result);
(void)result;
}
}
return JNI_TRUE;
}

SL_RESULT_BUFFER_INSUFFICIENT error was occurring due to the issue in timing.
Second thread gets started before the first is complete.
The third thread gets started when the first two are in progress. This cannot be handled so it was throwing an error.
The timing was increased sufficient enough to complete the first thread, the issue was solved.

Set numbuffers
Chances are your numbuffers on your SLDataLocator_AndroidSimpleBufferQueue is inappropriately low.
SLDataLocator_AndroidSimpleBufferQueue in_loc;
in_loc.numBuffers = 5; // what number do you have here?
From the OpenSL ES spec:
If the maximum number of buffers specified in the SLDataLocator_BufferQueue structure used as the data source when creating the media object using the CreateAudioPlayer or CreateMidiPlayer method has been reached, the buffer is not added to the buffer queue and SL_RESULT_BUFFER_INSUFFICIENT is returned. At this point the client should wait until it receives a callback notification for a buffer completion at which time it can enqueue the buffer.
Alternatives
If changing your numbuffers to even a very high number doesn't work, ensure that you set your state to playing:
(*player)->SetPlayState( player, SL_PLAYSTATE_PLAYING ); // begin playback

You can call (*bqPlayerBufferQueue)->Clear(bqPlayerBufferQueue); when you stop the player, that will allow the previous thread to shutdown without having to wait on a timer.

Related

How to properly and simply use callbacks in JNI NDK Android

I am trying to have Java call a C function in Android and some caveats that I am met with is the documentation for callbacks in general seem very unclear and most answers on SO are just talking about theory and posting broken links with no actual code. So please post code in your answer.
I am trying to create a joystick in java and have it send input to the NDK to perform actions.
if I can just have Java call a C function and pass a int to it ,
that would be good. but I think that wont work because C in running
it's own main loop.
perhaps I need to use C to call a java
function to get controller state and execute code from there.
I did manage to find some code Here but it seems the code while it does compile by itself, but doesn't appear to work when moved to modern code .
How should I implement call backs in a Java C relationship on Android? It seems that from the code example above that it is C that is calling Java functions and these need to be used like getters functions.
Here is some code that I have and I appear to be missing somethings because it is unclear to me how I should have these call backs performed. I will mark this code with function name and where is it located, if java or C++ (CPP) .
I think there are two ways that I would like this to work.
My Javafunction that I want to call
public int Cat(){
Log.e("JniHandler", "CAT WAS CALLED!!!");
return 333;
}
Structure information CPP . Not sure if it is needed.
#include <string.h>
#include <inttypes.h>
#include <pthread.h>
#include <jni.h>
#include <android/log.h>
#include <assert.h>
typedef struct tick_context {
JavaVM *javaVM;
jclass jniHelperClz;
jobject jniHelperObj;
jclass mainActivityClz;
jobject mainActivityObj;
pthread_mutex_t lock;
int done;
} TickContext;
TickContext g_ctx;
This section works. It looks that as soon as the JVM is launched, this will initialize.
JNI_OnLoad CPP
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
memset(&g_ctx, 0, sizeof(g_ctx));
SDL_Log(" DEBUG -- JNI INIT!!!");
g_ctx.javaVM = vm;
if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR; // JNI version not supported.
}
jclass clz = env->FindClass("org/foo/bar/mainactivity");
g_ctx.jniHelperClz = (jclass)env->NewGlobalRef(clz); // hack
jmethodID jniHelperCtor = env->GetMethodID(g_ctx.jniHelperClz,"<init>", "()V");
jobject handler = env->NewObject(g_ctx.jniHelperClz, jniHelperCtor);
g_ctx.jniHelperObj = env->NewGlobalRef(handler);
//queryRuntimeInfo(env, g_ctx.jniHelperObj); // This when getting version returns null.
g_ctx.done = 0;
g_ctx.mainActivityObj = NULL;
return JNI_VERSION_1_6;
}
queryRuntimeInfo CPP
void queryRuntimeInfo(JNIEnv *env, jobject instance) {
SDL_Log("DEBUG QUERYRUNTIME INTO");
// Find out which OS we are running on. It does not matter for this app
// just to demo how to call static functions.
// Our java JniHelper class id and instance are initialized when this
// shared lib got loaded, we just directly use them
// static function does not need instance, so we just need to feed
// class and method id to JNI
jmethodID versionFunc = env->GetStaticMethodID(g_ctx.jniHelperClz, "getBuildVersion", "()Ljava/lang/String;"); // This returns null.
if (!versionFunc) {
SDL_Log("DEBUG versionFunc INTO");
//LOGE("Failed to retrieve getBuildVersion() methodID # line %d",__LINE__);
return;
}
SDL_Log("DEBUG BUILD VERSION INTO");
jstring buildVersion = (jstring)env->CallStaticObjectMethod(g_ctx.jniHelperClz, versionFunc); // hack
const char *version = env->GetStringUTFChars(buildVersion, NULL);
if (!version) {
SDL_Log("DEBUG buildVersion INTO");
//LOGE("Unable to get version string # line %d", __LINE__);
return;
}
//LOGI("Android Version - %s", version);
SDL_Log("DEBUG ANDROID VERSION %s", version);
env->ReleaseStringUTFChars(buildVersion, version);
// we are called from JNI_OnLoad, so got to release LocalRef to avoid leaking
env->DeleteLocalRef(buildVersion);
// Query available memory size from a non-static public function
// we need use an instance of JniHelper class to call JNI
jmethodID memFunc = env->GetMethodID(g_ctx.jniHelperClz, "getRuntimeMemorySize", "()J");
if (!memFunc) {
//LOGE("Failed to retrieve getRuntimeMemorySize() methodID # line %d",__LINE__);
SDL_Log("DEBUG Failed to retrieve memory size");
return;
}
jlong result = env->CallLongMethod(instance, memFunc);
//LOGI("Runtime free memory size: %" PRId64, result);
SDL_Log("DEBUG RUN TIME FREE MEMORY SIZE");
(void)result; // silence the compiler warning
}
This function isn't ever called and I am not sure who is the caller. CPP
extern "C" JNIEXPORT void JNICALL InvokeFunction(JNIEnv *env, jobject instance) {
SDL_Log("JNI CALL START TICKET");
pthread_t threadInfo_;
pthread_attr_t threadAttr_;
pthread_attr_init(&threadAttr_);
pthread_attr_setdetachstate(&threadAttr_, PTHREAD_CREATE_DETACHED);
pthread_mutex_init(&g_ctx.lock, NULL);
jclass clz = env->GetObjectClass(instance);
g_ctx.mainActivityClz = (jclass)env->NewGlobalRef(clz); // hack
g_ctx.mainActivityObj = env->NewGlobalRef(instance);
int result = pthread_create( &threadInfo_, &threadAttr_, UpdateTicks, &g_ctx);
assert(result == 0);
pthread_attr_destroy(&threadAttr_);
(void)result;
}
updateticks CPP // This code is never actually called.
void* UpdateTicks(void* context) {
SDL_Log("DEBUG -- UPDATE TICKS IS CALLED");
TickContext *pctx = (TickContext*) context;
JavaVM *javaVM = pctx->javaVM;
JNIEnv *env;
jint res = javaVM->GetEnv((void**)&env, JNI_VERSION_1_6);
if (res != JNI_OK) {
res = javaVM->AttachCurrentThread(&env, NULL);
if (JNI_OK != res) {
//LOGE("Failed to AttachCurrentThread, ErrorCode = %d", res);
SDL_Log("FAILED TO ATTACH THREAD");
return NULL;
}
}
jmethodID statusId = env->GetMethodID(pctx->jniHelperClz, "updateStatus", "(Ljava/lang/String;)V");
//sendJavaMsg(env, pctx->jniHelperObj, statusId, "TickerThread status: initializing...");
// get mainActivity updateTimer function
jmethodID timerId = env->GetMethodID(pctx->mainActivityClz, "updateTimer", "()V");
struct timeval beginTime, curTime, usedTime, leftTime;
const struct timeval kOneSecond = {
(__kernel_time_t)1,
(__kernel_suseconds_t) 0
};
//sendJavaMsg(env, pctx->jniHelperObj, statusId, "TickerThread status: start ticking ...");
while(1) {
gettimeofday(&beginTime, NULL);
pthread_mutex_lock(&pctx->lock);
int done = pctx->done;
if (pctx->done) {
pctx->done = 0;
}
pthread_mutex_unlock(&pctx->lock);
if (done) {
break;
}
env->CallVoidMethod(pctx->mainActivityObj, timerId);
gettimeofday(&curTime, NULL);
timersub(&curTime, &beginTime, &usedTime);
timersub(&kOneSecond, &usedTime, &leftTime);
struct timespec sleepTime;
sleepTime.tv_sec = leftTime.tv_sec;
sleepTime.tv_nsec = leftTime.tv_usec * 1000;
if (sleepTime.tv_sec <= 1) {
nanosleep(&sleepTime, NULL);
} else {
//sendJavaMsg(env, pctx->jniHelperObj, statusId, "TickerThread error: processing too long!");
}
}
//sendJavaMsg(env, pctx->jniHelperObj, statusId, "TickerThread status: ticking stopped");
javaVM->DetachCurrentThread();
return context;
}

I am a novice JNI,Why doesn't my android jni C ++ try block catch an exception

I am a novice JNI,Why doesn't my android jni C ++ try block catch an exception,The code crashes when and the app crashes without jumping to exception handling
this is my code
Activity re code
Receive H264 data with queue at JAVA layer
Start a decoding thread and continuously take a frame of H264 data packets from the queue into the C ++ layer to decode with FFmpeg
class decode extends Thread {
#Override
public void run() {
// super.run();
while (isDecode) {
byte[] data = one.poll();
if (data != null)
if (ffmpegUtilsInSignalOne != null)
ffmpegUtilsInSignalOne.decodeH264One(data);
}
}
}
cpp File code
extern "C"
JNIEXPORT void JNICALL
Java_cn_zhihuiyun_control_utils_FFMPEGUtils_decodeH264One(JNIEnv *env, jobject thiz,
jbyteArray data) {
if (packetOne != nullptr && pCodecCtxOne != nullptr && vFrameOne != nullptr) {
if (data == nullptr)
return;;
if (isPlay == 0)
return;
jbyte *arr = env->GetByteArrayElements(data, JNI_FALSE);
packetOne->data = (uint8_t *) arr;
packetOne->size = env->GetArrayLength(data);
avcodec_send_packet(pCodecCtxOne, packetOne);
avcodec_receive_frame(pCodecCtxOne, vFrameOne);
av_packet_unref(packetOne);
env->ReleaseByteArrayElements(data, arr, 0);
}
}
At this time, a display thread is started in the C layer for screen rendering
extern "C"
JNIEXPORT void JNICALL
Java_cn_zhihuiyun_control_utils_FFMPEGUtils_initVisThread(JNIEnv *env, jobject thiz) {
isPlay = 1;
threadVister = pthread_create(&threadVister, nullptr,
reinterpret_cast<void *(*)(void *)>(&showVideo),
(void *) env);
pthread_detach(threadVister);
}
void *showVideo(JNIEnv *env) {
try {
while (isPlay == 1) {
if (vFrameOne != nullptr && pFrameRGBAOne != nullptr && pCodecCtxOne != nullptr &&
nativeWindowOne != nullptr) {
ANativeWindow_lock(nativeWindowOne, &windowBufferOne, nullptr);
av_image_fill_arrays(pFrameRGBAOne->data, pFrameRGBAOne->linesize,
(const uint8_t *) windowBufferOne.bits, AV_PIX_FMT_RGBA,
detWidth, detHeight, 1);
int re = -1;
try {
re = libyuv::I420ToARGB(vFrameOne->data[0], vFrameOne->linesize[0],
vFrameOne->data[2], vFrameOne->linesize[2],
vFrameOne->data[1], vFrameOne->linesize[1],
pFrameRGBAOne->data[0], pFrameRGBAOne->linesize[0],
pCodecCtxOne->width, pCodecCtxOne->height);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
} catch (...) {
}
if (re == -1) {
} else {
ANativeWindow_unlockAndPost(nativeWindowOne);
}
}
}
}
} catch (...) {
printf("error");
}
if (vFrameOne != nullptr) {
av_frame_free(&vFrameOne);
vFrameOne = nullptr;
}
if (packetOne != nullptr) {
av_free(packetOne);
packetOne = nullptr;
}
if (pFrameRGBAOne != nullptr) {
av_free(pFrameRGBAOne);
pFrameRGBAOne = nullptr;
}
if (pCodecCtxOne != nullptr) {
avcodec_free_context(&pCodecCtxOne);
pCodecCtxOne = nullptr;
}
if (nativeWindowOne != nullptr) {
ANativeWindow_release(nativeWindowOne);
nativeWindowOne = nullptr;
}
pthread_exit(&threadVister);
}
You should carefully read the documentation of avcodec_receive_frame:
Return decoded output data from a decoder.
Parameters
avctx codec context
frame This will be set to a reference-counted video or audio frame (depending on the decoder type) allocated by the decoder. Note that the function will always call av_frame_unref(frame) before doing anything else.
Returns
0: success, a frame was returned
AVERROR(EAGAIN): output is not available in this state - user must try to send new input
AVERROR_EOF: the decoder has been fully flushed, and there will be no more output frames
AVERROR(EINVAL): codec not opened, or it is an encoder other negative values: legitimate decoding errors
I have highlighted two key pieces of information:
First, the call to avcodec_receive_frame will invalidate vFrameOne. If your other thread is in the middle of decoding, your program will crash. You will need to establish a synchronization mechanism between receiving and displaying threads to make sure the receiver is always receiving into a frame only it has access to, and that it only passes full frames to the displaying side. (see next point)
Second, you should check the return value of avcodec_receive_frame. If you see an AVERROR(EAGAIN) you have not received enough packets to produce a full frame. Only if this function produces 0 can you take the full frame and hand it over to the displaying thread.

What is the best way to save JNIEnv*

I have an Android project with JNI. In the CPP file which implements a listener class, there is a callback x() . When x() function is called, I want to call another function in a java class. However, in order to invoke that java function, I need to access JNIEnv*.
I know that in the same cpp file of the callback, there is a function:
static jboolean init (JNIEnv* env, jobject obj) {...}
Should I save in the cpp file JNIEnv* as member variable when init(..) is called? and use it later when the callback happens?
Sorry but I am a beginner in JNI.
Caching a JNIEnv* is not a particularly good idea, since you can't use the same JNIEnv* across multiple threads, and might not even be able to use it for multiple native calls on the same thread (see http://android-developers.blogspot.se/2011/11/jni-local-reference-changes-in-ics.html)
Writing a function that gets the JNIEnv* and attaches the current thread to the VM if necessary isn't too difficult:
bool GetJniEnv(JavaVM *vm, JNIEnv **env) {
bool did_attach_thread = false;
*env = nullptr;
// Check if the current thread is attached to the VM
auto get_env_result = vm->GetEnv((void**)env, JNI_VERSION_1_6);
if (get_env_result == JNI_EDETACHED) {
if (vm->AttachCurrentThread(env, NULL) == JNI_OK) {
did_attach_thread = true;
} else {
// Failed to attach thread. Throw an exception if you want to.
}
} else if (get_env_result == JNI_EVERSION) {
// Unsupported JNI version. Throw an exception if you want to.
}
return did_attach_thread;
}
The way you'd use it is:
JNIEnv *env;
bool did_attach = GetJniEnv(vm, &env);
// Use env...
// ...
if (did_attach) {
vm->DetachCurrentThread();
}
You could wrap this in a class that attaches upon construction and detaches upon destruction, RAII-style:
class ScopedEnv {
public:
ScopedEnv() : attached_to_vm_(false) {
attached_to_vm_ = GetJniEnv(g_vm, &env_); // g_vm is a global
}
ScopedEnv(const ScopedEnv&) = delete;
ScopedEnv& operator=(const ScopedEnv&) = delete;
virtual ~ScopedEnv() {
if (attached_to_vm_) {
g_vm->DetachCurrentThread();
attached_to_vm_ = false;
}
}
JNIEnv *GetEnv() const { return env_; }
private:
bool attached_to_env_;
JNIEnv *env_;
};
// Usage:
{
ScopedEnv scoped_env;
scoped_env.GetEnv()->SomeJniFunction();
}
// scoped_env falls out of scope, the thread is automatically detached if necessary
Edit: Sometimes you might have a long-ish running native thread that will need a JNIEnv* on multiple occasions. In such situations you may want to avoid constantly attaching and detaching the thread to/from the JVM, but you still need to make sure that you detach the thread upon thread destruction.
You can accomplish this by attaching the thread only once and then leaving it attached, and by setting up a thread destruction callback using pthread_key_create and pthread_setspecific that will take care of calling DetachCurrentThread.
/**
* Get a JNIEnv* valid for this thread, regardless of whether
* we're on a native thread or a Java thread.
* If the calling thread is not currently attached to the JVM
* it will be attached, and then automatically detached when the
* thread is destroyed.
*/
JNIEnv *GetJniEnv() {
JNIEnv *env = nullptr;
// We still call GetEnv first to detect if the thread already
// is attached. This is done to avoid setting up a DetachCurrentThread
// call on a Java thread.
// g_vm is a global.
auto get_env_result = g_vm->GetEnv((void**)&env, JNI_VERSION_1_6);
if (get_env_result == JNI_EDETACHED) {
if (g_vm->AttachCurrentThread(&env, NULL) == JNI_OK) {
DeferThreadDetach(env);
} else {
// Failed to attach thread. Throw an exception if you want to.
}
} else if (get_env_result == JNI_EVERSION) {
// Unsupported JNI version. Throw an exception if you want to.
}
return env;
}
void DeferThreadDetach(JNIEnv *env) {
static pthread_key_t thread_key;
// Set up a Thread Specific Data key, and a callback that
// will be executed when a thread is destroyed.
// This is only done once, across all threads, and the value
// associated with the key for any given thread will initially
// be NULL.
static auto run_once = [] {
const auto err = pthread_key_create(&thread_key, [] (void *ts_env) {
if (ts_env) {
g_vm->DetachCurrentThread();
}
});
if (err) {
// Failed to create TSD key. Throw an exception if you want to.
}
return 0;
}();
// For the callback to actually be executed when a thread exits
// we need to associate a non-NULL value with the key on that thread.
// We can use the JNIEnv* as that value.
const auto ts_env = pthread_getspecific(thread_key);
if (!ts_env) {
if (pthread_setspecific(thread_key, env)) {
// Failed to set thread-specific value for key. Throw an exception if you want to.
}
}
}
If __cxa_thread_atexit is available to you, you might be able to accomplish the same thing with some thread_local object that calls DetachCurrentThread in its destructor.
#Michael, gives a good overview of how best to retrieve the JNI by caching the JVM.
For those that dont want to use pthread (or cant' because you are on Windows system), and you are using c++ 11 or highter, then thread_local storage is the way to go.
Bellow is rough example on how to implement a wrapper method that properly attaches to a thread and automatically cleans-up when the thread exits
JNIEnv* JNIThreadHelper::GetJniEnv() {
// This method might have been called from a different thread than the one that created
// this handler. Check to make sure that the JNI is attached and if not attach it to the
// new thread.
// double check it's all ok
int nEnvStat = m_pJvm->GetEnv(reinterpret_cast<void**>(&m_pJniEnv), JNI_VERSION_1_6);
if (nEnvStat == JNI_EDETACHED) {
std::cout << "GetEnv: not attached. Attempting to attach" << std::endl;
JavaVMAttachArgs args;
args.version = JNI_VERSION_1_6; // choose your JNI version
args.name = NULL; // you might want to give the java thread a name
args.group = NULL; // you might want to assign the java thread to a ThreadGroup
if (m_pJvm->AttachCurrentThread(&m_pJniEnv, &args) != 0) {
std::cout << "Failed to attach" << std::endl;
return nullptr;
}
thread_local struct DetachJniOnExit {
~DetachJniOnExit() {
m_pJvm->DetachCurrentThread();
}
};
m_bIsAttachedOnAThread = true;
}
else if (nEnvStat == JNI_OK) {
//
}
else if (nEnvStat == JNI_EVERSION) {
std::cout << "GetEnv: version not supported" << std::endl;
return nullptr;
}
return m_pJniEnv;
}

Enabling MediaPlayerBuffering event to be fired in android-vlc

I would like to subscribe for the MediaPlayer buffering event in the android vlc app.
I edited the EventHandler class and uncommented the event constant.
public static final int MediaPlayerBuffering = 0x103; // ** uncommented this**
public static final int MediaPlayerPlaying = 0x104;
I then added the the variable in libvlcjni.c
libvlc_event_manager_t *ev = libvlc_media_player_event_manager(mp);
static const libvlc_event_type_t mp_events[] = {
libvlc_MediaPlayerPlaying,
libvlc_MediaPlayerPaused,
libvlc_MediaPlayerEndReached,
libvlc_MediaPlayerStopped,
libvlc_MediaPlayerVout,
libvlc_MediaPlayerPositionChanged,
libvlc_MediaPlayerEncounteredError,
libvlc_MediaPlayerBuffering // **added this here**
};
recompiled the jni to get the so file and then built the vlc app but the event never seems to fire off.
Where else do I have to link to get the event fired when there is a buffering event due to lack of bandwidth.
I can see in logcat that it prints 1001 ms buffered in 6ms. But that is coming from the lower layer and not the java layer
had to add this in the libvlcjni.c file
else if(ev->type == libvlc_MediaPlayeBuffering) {
/* For determining the vout/ES track change */
jstring sData = (*env)->NewStringUTF(env, "data");
(*env)->CallVoidMethod(env, bundle, putFloat, sData, ev->u.media_player_buffer.new_cache);
(*env)->DeleteLocalRef(env, sData);
}
hope this helps someone
else if(ev->type == libvlc_MediaPlayerBuffering) {
/* For determining the vout/ES track change */
jstring sData = (*env)->NewStringUTF(env, "data");
(*env)->CallVoidMethod(env, bundle, putFloat, sData, ev->u.media_player_buffering.new_cache);
(*env)->DeleteLocalRef(env, sData);
}
The answer is ev->u.media_player_buffering.new_cache
In the VLC file mediaPlayer.c
I found this code:
`else if( newval.i_int == INPUT_EVENT_CACHE )
{
event.type = libvlc_MediaPlayerBuffering;
event.u.media_player_buffering.new_cache = (int)(100 * var_GetFloat( p_input, "cache" ));
libvlc_event_send( p_mi->p_event_manager, &event );
}
`
and in libvlc_events.h
/* media instance */
struct
{
float new_cache;
} media_player_buffering;
Then, I compiled it and it worked. Special thanks to my boy Tracy on the coast!

Android 4.1 virtual keyboard freeze

I have the following strange issue that only occurs on Android 4.1: When the virtual keyboard is opened and the user presses the BACK button, my app just freezes and doesn't react any longer. After a while a system dialog box pops up informing me that the app has died and will be closed. I can hardly imagine that this is a bug in Android because hiding the keyboard by pressing the BACK button is elementary functionality that should just work. Still, my code is so small that I can almost completely rule out an error in my code.
Here is my code for you to review:
static int quit = 0;
static void engine_handle_cmd(struct android_app *app, int32_t cmd)
{
switch(cmd) {
case APP_CMD_TERM_WINDOW:
quit = 1;
break;
}
}
static int32_t engine_handle_input(struct android_app *app, AInputEvent *event)
{
switch(AInputEvent_getType(event)) {
case AINPUT_EVENT_TYPE_MOTION: {
int action = AMotionEvent_getAction(event);
if((action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) showkeyboard(app);
break;
}
}
return 0;
}
void android_main(struct android_app* state)
{
int events, fd;
struct android_poll_source *source;
app_dummy();
state->onAppCmd = engine_handle_cmd;
state->onInputEvent = engine_handle_input;
while(!quit) {
if(ALooper_pollOnce(-1, &fd, &events, (void **) &source) >= 0) {
if(source) source->process(state, source);
}
}
exit(0);
}
The showkeyboard() function is implemented in JNI like this:
static void showkeyboard(struct android_app* state)
{
// Attaches the current thread to the JVM.
jint lResult;
jint lFlags = 0;
JavaVM *lJavaVM = state->activity->vm;
JNIEnv *lJNIEnv = state->activity->env;
JavaVMAttachArgs lJavaVMAttachArgs;
jobject lNativeActivity, INPUT_METHOD_SERVICE;
jclass ClassNativeActivity, ClassContext;
jfieldID FieldINPUT_METHOD_SERVICE;
JNIEnv *env;
int attached = 0;
// must check if we're already attached!
switch((*lJavaVM)->GetEnv(lJavaVM, (void**) &env, JNI_VERSION_1_6)) {
case JNI_OK:
break;
case JNI_EDETACHED:
lJavaVMAttachArgs.version = JNI_VERSION_1_6;
lJavaVMAttachArgs.name = "NativeThread";
lJavaVMAttachArgs.group = NULL;
lResult = (*lJavaVM)->AttachCurrentThread(lJavaVM, &lJNIEnv, &lJavaVMAttachArgs);
if(lResult == JNI_ERR) return;
attached = 1;
break;
case JNI_EVERSION:
return; // Invalid Java version
}
// Retrieves NativeActivity.
lNativeActivity = state->activity->clazz;
ClassNativeActivity = (*lJNIEnv)->GetObjectClass(lJNIEnv, lNativeActivity);
// Retrieves Context.INPUT_METHOD_SERVICE.
ClassContext = (*lJNIEnv)->FindClass(lJNIEnv, "android/content/Context");
FieldINPUT_METHOD_SERVICE = (*lJNIEnv)->GetStaticFieldID(lJNIEnv, ClassContext, "INPUT_METHOD_SERVICE", "Ljava/lang/String;");
INPUT_METHOD_SERVICE = (*lJNIEnv)->GetStaticObjectField(lJNIEnv, ClassContext, FieldINPUT_METHOD_SERVICE);
{
// Runs getSystemService(Context.INPUT_METHOD_SERVICE).
jclass ClassInputMethodManager = (*lJNIEnv)->FindClass(lJNIEnv, "android/view/inputmethod/InputMethodManager");
jmethodID MethodGetSystemService = (*lJNIEnv)->GetMethodID(lJNIEnv, ClassNativeActivity, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
jobject lInputMethodManager = (*lJNIEnv)->CallObjectMethod(lJNIEnv, lNativeActivity, MethodGetSystemService, INPUT_METHOD_SERVICE);
// Runs getWindow().getDecorView().
jmethodID MethodGetWindow = (*lJNIEnv)->GetMethodID(lJNIEnv, ClassNativeActivity, "getWindow", "()Landroid/view/Window;");
jobject lWindow = (*lJNIEnv)->CallObjectMethod(lJNIEnv, lNativeActivity, MethodGetWindow);
jclass ClassWindow = (*lJNIEnv)->FindClass(lJNIEnv, "android/view/Window");
jmethodID MethodGetDecorView = (*lJNIEnv)->GetMethodID(lJNIEnv, ClassWindow, "getDecorView", "()Landroid/view/View;");
jobject lDecorView = (*lJNIEnv)->CallObjectMethod(lJNIEnv, lWindow, MethodGetDecorView);
// Runs lInputMethodManager.showSoftInput(...).
jmethodID MethodShowSoftInput = (*lJNIEnv)->GetMethodID(lJNIEnv, ClassInputMethodManager, "showSoftInput", "(Landroid/view/View;I)Z");
(*lJNIEnv)->CallBooleanMethod(lJNIEnv, lInputMethodManager, MethodShowSoftInput, lDecorView, lFlags);
if(attached) {
// Finished with the JVM.
(*lJavaVM)->DetachCurrentThread(lJavaVM);
}
}
}
To reproduce, you just have to compile the app, put it onto the emulator with an Android 4.1 image, then click somewhere and the virtual keyboard will be shown. Now try to close the keyboard using the BACK button. The app will freeze now.
Note that this error only occurs on Android 4.1. It works fine on 2.3, 3.0 and 4.0. Could this be a major bug in Android itself? I really have no other explanation because my code is really just a primitive main loop that doesn't do anything at all. But still, trying to close the virtual keyboard will crash the app.
Thanks a lot for help on this issue! I have been grappling with this for hours now :(
UPDATE 1:
The issue also appears on Android 4.2. A ticket has been opened on the Android issues site. Everybody who experiences the same problem should comment on the ticket so that the Android developers become aware of it. Here is the link:
http://code.google.com/p/android/issues/detail?id=43817&thanks=43817&ts=1359632204
Thanks for asking this, it helped me.
I was seeing the freezes too. It seems to be coming from AInputQueue_preDispatchEvent in process_input in android_native_app_glue.c. To work around it, I changed:
LOGI("New input event: type=%d\n", AInputEvent_getType(event));
if (AInputQueue_preDispathEvent(app->inputQueue, event)) {
return;
}
int32_t handled = 0;
to:
int32_t type = AInputEvent_getType(event);
LOGI("New input event: type=%d\n", type);
if (!g_iShowingSoftKeyboard || (type != AINPUT_EVENT_TYPE_KEY)
|| (AKeyEvent_getKeyCode(event) != AKEYCODE_BACK))
{
if (AInputQueue_preDispathEvent(app->inputQueue, event)) {
return;
}
}
int32_t handled = 0;
of course g_iShowingSoftKeyboard is the global that I am using that is true if the soft keyboard is showing.
Edit: and I close the soft keyboard where I handle the back press in my input handler.

Categories

Resources