Log in or register to post comments

Unsatisfied Link Error

January 16, 2013 - 11:22am #1

I have created a new project (since the samples play right) and wanted to build up the app semi from scratch (i used the ImageTargets files as a base). Now I have everything compiling but I have no idea why i get the Unsatisfied link Error for getOpenGleEsVersionNAtive.

Below is my LLScanner code

 

/************************************************************************
* Author: Piotr Leja
* Purpose: QCAR scanner for Lulu and Lala,
* Native Renderer Implementation
* Date Created Wednesday January 16, 2013
*
* Based On: QCAR Sample Image Targets by QualComm
************************************************************************/
 
#include <jni.h>
#include <android/log.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
 
/*
//Dont think I need OpenGL but i will keep it anyway
#ifdef USE_OPENGL_ES_1_1
#include <GLES/gl.h>
#include <GLES/glext.h>
#else
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#endif
*/
 
#include <QCAR/QCAR.h>
#include <QCAR/CameraDevice.h>
#include <QCAR/Renderer.h>
#include <QCAR/VideoBackgroundConfig.h>
#include <QCAR/Trackable.h>
#include <QCAR/TrackableResult.h>
#include <QCAR/Tool.h>
#include <QCAR/Tracker.h>
#include <QCAR/TrackerManager.h>
#include <QCAR/ImageTracker.h>
#include <QCAR/CameraCalibration.h>
#include <QCAR/UpdateCallback.h>
#include <QCAR/DataSet.h>
 
#include "SampleUtils.h"
 
#ifdef __cplusplus
extern "C"
{
#endif
 
// Screen dimensions:
unsigned int screenWidth        = 0;
unsigned int screenHeight       = 0;
 
bool switchDataSetAsap = false;
 
// Indicates whether screen is in portrait (true) or landscape (false) mode
bool isActivityInPortraitMode   = false;
 
// Indicate whether Phone supports continus autofocus
bool isContinuousAutoOn = false;
 
// Indicate whether Phone supports continus autofocus
bool isInfintiyFocusOn = false;
 
//Dataset for current Image Targets
//There might be more datasets when we update app at another time
QCAR::DataSet* dataSetLALBookOne    = 0;
 
class ImageTargets_UpdateCallback : public QCAR::UpdateCallback
{   
    virtual void QCAR_onUpdate(QCAR::State& /*state*/)
    {
        if (switchDataSetAsap)
        {
/*
Keeping as reference, for now there will be no update callbacks used to change Datasets
            switchDataSetAsap = false;
 
            // Get the image tracker:
            QCAR::TrackerManager& trackerManager = QCAR::TrackerManager::getInstance();
            QCAR::ImageTracker* imageTracker = static_cast<QCAR::ImageTracker*>(
                trackerManager.getTracker(QCAR::Tracker::IMAGE_TRACKER));
            if (imageTracker == 0 || dataSetStonesAndChips == 0 || dataSetTarmac == 0 ||
                imageTracker->getActiveDataSet() == 0)
            {
                LOG("Failed to switch data set.");
                return;
            }
            
            if (imageTracker->getActiveDataSet() == dataSetStonesAndChips)
            {
                imageTracker->deactivateDataSet(dataSetStonesAndChips);
                imageTracker->activateDataSet(dataSetTarmac);
            }
            else
            {
                imageTracker->deactivateDataSet(dataSetTarmac);
                imageTracker->activateDataSet(dataSetStonesAndChips);
            }
*/
        }
    }
};
 
UpdateCallback updateCallback;
 
JNIEXPORT int JNICALL
extern "C" Java_net_vproductions_lal_ImageTargets_getOpenGlEsVersionNative(JNIEnv *, jobject)
{
#ifdef USE_OPENGL_ES_1_1        
    return 1;
#else
    return 2;
#endif
}
 
JNIEXPORT void JNICALL
Java_net_vproductions_lal_ImageTargets_setActivityPortraitMode(JNIEnv *, jobject, jboolean isPortrait)
{
    isActivityInPortraitMode = isPortrait;
}
 
JNIEXPORT void JNICALL
Java_net_vproductions_lal_ImageTargets_switchDatasetAsap(JNIEnv *, jobject)
{
    switchDataSetAsap = true;
}
 
JNIEXPORT int JNICALL
Java_net_vproductions_lal_ImageTargets_initTracker(JNIEnv *, jobject)
{
    ///LOG("Java_net_vproductions_lal_ImageTargets_initTracker");
    
    // Initialize the image tracker:
    QCAR::TrackerManager& trackerManager = QCAR::TrackerManager::getInstance();
    QCAR::Tracker* tracker = trackerManager.initTracker(QCAR::Tracker::IMAGE_TRACKER);
    if (tracker == NULL)
    {
        ///LOG("Failed to initialize Tracker.");
        return 0;
    }
 
    ///LOG("Successfully initialized Tracker.");
    return 1;
}
 
JNIEXPORT void JNICALL
Java_net_vproductions_lal_ImageTargets_deinitTracker(JNIEnv *, jobject)
{
    ///LOG("Java_net_vproductions_lal_ImageTargets_deinitTracker");
 
    // Deinit the image tracker:
    QCAR::TrackerManager& trackerManager = QCAR::TrackerManager::getInstance();
    trackerManager.deinitTracker(QCAR::Tracker::IMAGE_TRACKER);
}
 
JNIEXPORT int JNICALL
Java_net_vproductions_lal_ImageTargets_loadTrackerData(JNIEnv *, jobject)
{
    ///LOG("Java_net_vproductions_lal_ImageTargets_loadTrackerData");
    
    // Get the image tracker:
    QCAR::TrackerManager& trackerManager = QCAR::TrackerManager::getInstance();
    QCAR::ImageTracker* imageTracker = static_cast<QCAR::ImageTracker*>(
                    trackerManager.getTracker(QCAR::Tracker::IMAGE_TRACKER));
    if (imageTracker == NULL)
    {
        ///LOG("Failed to load tracking data set because the ImageTracker has not"
        //    " been initialized.");
        return 0;
    }
 
    dataSetLALBookOne = imageTracker->createDataSet();
    if (dataSetLALBookOne == 0)
    {
        //LOG("Failed to create a new tracking data.");
        return 0;
    }
 
    // Load the data sets:
    if (!dataSetLALBookOne->load("LuluAndLala.xml", QCAR::DataSet::STORAGE_APPRESOURCE))
    {
        //LOG("Failed to load data set for book one.");
        return 0;
    }
 
    // Activate the data set:
    if (!imageTracker->activateDataSet(dataSetLALBookOne))
    {
        //LOG("Failed to activate book one dataset.");
        return 0;
    }
 
    //LOG("Successfully loaded and activated data set.");
    return 1;
}
 
JNIEXPORT int JNICALL
Java_net_vproductions_lal_ImageTargets_destroyTrackerData(JNIEnv *, jobject)
{
    //LOG("net_vproductions_lal_ImageTargets_destroyTrackerData");
 
    // Get the image tracker:
    QCAR::TrackerManager& trackerManager = QCAR::TrackerManager::getInstance();
    QCAR::ImageTracker* imageTracker = static_cast<QCAR::ImageTracker*>(
        trackerManager.getTracker(QCAR::Tracker::IMAGE_TRACKER));
    if (imageTracker == NULL)
    {
        //LOG("Failed to destroy the tracking data set because the ImageTracker has not"
        //    " been initialized.");
        return 0;
    }
    
    if (dataSetLALBookOne != 0)
    {
        if (imageTracker->getActiveDataSet() == dataSetLALBookOne &&
            !imageTracker->deactivateDataSet(dataSetLALBookOne))
        {
            //LOG("Failed to destroy the tracking data set Lulu and Lala Book One because the data set "
                "could not be deactivated.");
            return 0;
        }
 
        if (!imageTracker->destroyDataSet(dataSetLALBookOne))
        {
            //LOG("Failed to destroy the tracking data set Lulu and Lala Book One.");
            return 0;
        }
 
        //LOG("Successfully destroyed the data set Lulu and Lala Book One.");
        dataSetLALBookOne = 0;
    }
 
    return 1;
}
 
JNIEXPORT void JNICALL
Java_net_vproductions_lal_ImageTargets_onQCARInitializedNative(JNIEnv *, jobject)
{
    // Register the update callback where we handle the data set swap:
    QCAR::registerCallback(&updateCallback);
 
    // Comment in to enable tracking of up to 2 targets simultaneously and
    // split the work over multiple frames:
    // QCAR::setHint(QCAR::HINT_MAX_SIMULTANEOUS_IMAGE_TARGETS, 2);
}
 
JNIEXPORT void JNICALL
Java_net_vproductions_lal_Renderer_renderFrame(JNIEnv *, jobject)
{
    //LOG("Java_com_qualcomm_QCARSamples_ImageTargets_GLRenderer_renderFrame");
 
    // Clear color and depth buffer 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
    // Get the state from QCAR and mark the beginning of a rendering section
    QCAR::State state = QCAR::Renderer::getInstance().begin();
    
    // Explicitly render the Video Background
    QCAR::Renderer::getInstance().drawVideoBackground();
       
    // Did we find any trackables this frame?
    for(int tIdx = 0; tIdx < state.getNumTrackableResults(); tIdx++)
    {
        // Get the trackable:
        const QCAR::TrackableResult* result = state.getTrackableResult(tIdx);
        const QCAR::Trackable& trackable = result->getTrackable();
 
//LOG(trackable->getName());
//jstring tmpName = env->NewStringUTF(trackable->getName());
//env->CallBooleanMethod
}
    QCAR::Renderer::getInstance().end();
}
 
void
configureVideoBackground()
{
    // Get the default video mode:
    QCAR::CameraDevice& cameraDevice = QCAR::CameraDevice::getInstance();
    QCAR::VideoMode videoMode = cameraDevice.getVideoMode(QCAR::CameraDevice::MODE_DEFAULT);
 
 
    // Configure the video background
    QCAR::VideoBackgroundConfig config;
    config.mEnabled = true;
    config.mSynchronous = true;
    config.mPosition.data[0] = 0.0f;
    config.mPosition.data[1] = 0.0f;
    
    if (isActivityInPortraitMode)
    {
        ////LOG("configureVideoBackground PORTRAIT");
        config.mSize.data[0] = videoMode.mHeight
                                * (screenHeight / (float)videoMode.mWidth);
        config.mSize.data[1] = screenHeight;
 
        if(config.mSize.data[0] < screenWidth)
        {
            //LOG("Correcting rendering background size to handle missmatch between screen and video aspect ratios.");
            config.mSize.data[0] = screenWidth;
            config.mSize.data[1] = screenWidth * 
                              (videoMode.mWidth / (float)videoMode.mHeight);
        }
    }
    else
    {
        ////LOG("configureVideoBackground LANDSCAPE");
        config.mSize.data[0] = screenWidth;
        config.mSize.data[1] = videoMode.mHeight
                            * (screenWidth / (float)videoMode.mWidth);
 
        if(config.mSize.data[1] < screenHeight)
        {
            //LOG("Correcting rendering background size to handle missmatch between screen and video aspect ratios.");
            config.mSize.data[0] = screenHeight
                                * (videoMode.mWidth / (float)videoMode.mHeight);
            config.mSize.data[1] = screenHeight;
        }
    }
 
    //LOG("Configure Video Background : Video (%d,%d), Screen (%d,%d), mSize (%d,%d)", videoMode.mWidth, videoMode.mHeight, screenWidth, screenHeight, config.mSize.data[0], config.mSize.data[1]);
 
    // Set the config:
    QCAR::Renderer::getInstance().setVideoBackgroundConfig(config);
}
 
JNIEXPORT void JNICALL
Java_net_vproductions_lal_ImageTargets_initApplicationNative(JNIEnv* env, jobject obj, jint width, jint height)
{
    //LOG("Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargets_initApplicationNative");
    
    // Store screen dimensions
    screenWidth = width;
    screenHeight = height;
        
    // Handle to the activity class:
    //jclass activityClass = env->GetObjectClass(obj);
    //LOG("Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargets_initApplicationNative finished");
}
 
 
JNIEXPORT void JNICALL
Java_net_vproductions_lal_ImageTargets_deinitApplicationNative(JNIEnv* env, jobject obj)
{
    //LOG("Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargets_deinitApplicationNative");
//Deinit is not needed since no textures are used.
}
 
 
JNIEXPORT void JNICALL
Java_net_vproductions_lal_ImageTargets_startCamera(JNIEnv *,
                                                                         jobject)
{
    //LOG("Java_com_qualcomm_QCARSamples_ImageTargets_ImageTargets_startCamera");
    
    // Select the camera to open, set this to QCAR::CameraDevice::CAMERA_FRONT 
    // to activate the front camera instead.
    QCAR::CameraDevice::CAMERA camera = QCAR::CameraDevice::CAMERA_DEFAULT;
 
    // Initialize the camera:
    if (!QCAR::CameraDevice::getInstance().init(camera))
        return;
 
    // Configure the video background
    configureVideoBackground();
 
    // Select the default mode:
    if (!QCAR::CameraDevice::getInstance().selectVideoMode(
                                QCAR::CameraDevice::MODE_DEFAULT))
        return;
 
    // Start the camera:
    if (!QCAR::CameraDevice::getInstance().start())
        return;
 
    // Uncomment to enable flash
    //if(QCAR::CameraDevice::getInstance().setFlashTorchMode(true))
    // //LOG("IMAGE TARGETS : enabled torch");
 
    // Uncomment to enable infinity focus mode, or any other supported focus mode
    // See CameraDevice.h for supported focus modes
if(QCAR::CameraDevice::getInstance().setFocusMode(QCAR::CameraDevice::FOCUS_MODE_CONTINUOUSAUTO)
{
isContinuousAutoOn = true;
    //LOG("AR : enabled infinity focus");
 
}
    if(!isContinuousAutoOn && QCAR::CameraDevice::getInstance().setFocusMode(QCAR::CameraDevice::FOCUS_MODE_INFINITY))
{
isInfintiyFocusOn = true;
    //LOG("AR : enabled infinity focus");
}
 
 
    // Start the tracker:
    QCAR::TrackerManager& trackerManager = QCAR::TrackerManager::getInstance();
    QCAR::Tracker* imageTracker = trackerManager.getTracker(QCAR::Tracker::IMAGE_TRACKER);
    if(imageTracker != 0)
        imageTracker->start();
}
 
 
JNIEXPORT void JNICALL
Java_net_vproductions_lal_ImageTargets_stopCamera(JNIEnv *, jobject)
{
    //LOG("Java_net_vproductions_lal_ImageTargets_stopCamera");
 
    // Stop the tracker:
    QCAR::TrackerManager& trackerManager = QCAR::TrackerManager::getInstance();
    QCAR::Tracker* imageTracker = trackerManager.getTracker(QCAR::Tracker::IMAGE_TRACKER);
    if(imageTracker != 0)
        imageTracker->stop();
    
    QCAR::CameraDevice::getInstance().stop();
    QCAR::CameraDevice::getInstance().deinit();
}
 
JNIEXPORT jboolean JNICALL
Java_net_vproductions_lal_ImageTargets_triggerFocus(JNIEnv*, jobject)
{
if(!isInfintiyFocusOn && !isContinuousAutoOn)
return QCAR::CameraDevice::getInstance().setFocusMode(QCAR::CameraDevice::FOCUS_MODE_TRIGGERAUTO) ? JNI_TRUE : JNI_FALSE;
return JNI_FALSE;
}
 
JNIEXPORT void JNICALL
Java_net_vproductions_lal_Renderer_initRendering(
                                                    JNIEnv* env, jobject obj)
{
    //LOG("Java_net_vproductions_lal_Renderer_initRendering");
 
    // Define clear color
    glClearColor(0.0f, 0.0f, 0.0f, QCAR::requiresAlpha() ? 0.0f : 1.0f);
    /*
    // Now generate the OpenGL texture objects and add settings
    for (int i = 0; i < textureCount; ++i)
    {
        glGenTextures(1, &(textures[i]->mTextureID));
        glBindTexture(GL_TEXTURE_2D, textures[i]->mTextureID);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textures[i]->mWidth,
                textures[i]->mHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                (GLvoid*)  textures[i]->mData);
    }
ImageTargetsifndef USE_OPENGL_ES_1_1
  
    shaderProgramID     = SampleUtils::createProgramFromBuffer(cubeMeshVertexShader,
                                                            cubeFragmentShader);
 
    vertexHandle        = glGetAttribLocation(shaderProgramID,
                                                "vertexPosition");
    normalHandle        = glGetAttribLocation(shaderProgramID,
                                                "vertexNormal");
    textureCoordHandle  = glGetAttribLocation(shaderProgramID,
                                                "vertexTexCoord");
    mvpMatrixHandle     = glGetUniformLocation(shaderProgramID,
                                                "modelViewProjectionMatrix");
    texSampler2DHandle  = glGetUniformLocation(shaderProgramID, 
                                                "texSampler2D");
                                                
ImageTargetsendif
*/
}
 
 
JNIEXPORT void JNICALL
Java_net_vproductions_lal_Renderer_updateRendering(
                        JNIEnv* env, jobject obj, jint width, jint height)
{
    //LOG("Java_net_vproductions_lal_Renderer_updateRendering");
 
    // Update screen dimensions
    screenWidth = width;
    screenHeight = height;
 
    // Reconfigure the video background
    configureVideoBackground();
}
 
#ifdef __cplusplus
}
#endif
 
and below is the error I get. 
01-16 13:57:02.797: W/dalvikvm(13765): No implementation found for native Lnet/vproductions/lal/ImageTargets;.getOpenGlEsVersionNative ()I
...
...
01-16 13:57:02.801: E/AndroidRuntime(13765): FATAL EXCEPTION: main
01-16 13:57:02.801: E/AndroidRuntime(13765): java.lang.UnsatisfiedLinkError: getOpenGlEsVersionNative
01-16 13:57:02.801: E/AndroidRuntime(13765): at net.vproductions.lal.ImageTargets.getOpenGlEsVersionNative(Native Method)
01-16 13:57:02.801: E/AndroidRuntime(13765): at net.vproductions.lal.ImageTargets.getInitializationFlags(ImageTargets.java:364)
01-16 13:57:02.801: E/AndroidRuntime(13765): at net.vproductions.lal.ImageTargets.onCreate(ImageTargets.java:347)
01-16 13:57:02.801: E/AndroidRuntime(13765): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
01-16 13:57:02.801: E/AndroidRuntime(13765): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611)
01-16 13:57:02.801: E/AndroidRuntime(13765): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)
01-16 13:57:02.801: E/AndroidRuntime(13765): at android.app.ActivityThread.access$1500(ActivityThread.java:117)
01-16 13:57:02.801: E/AndroidRuntime(13765): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)
01-16 13:57:02.801: E/AndroidRuntime(13765): at android.os.Handler.dispatchMessage(Handler.java:99)
01-16 13:57:02.801: E/AndroidRuntime(13765): at android.os.Looper.loop(Looper.java:130)
01-16 13:57:02.801: E/AndroidRuntime(13765): at android.app.ActivityThread.main(ActivityThread.java:3683)
01-16 13:57:02.801: E/AndroidRuntime(13765): at java.lang.reflect.Method.invokeNative(Native Method)
01-16 13:57:02.801: E/AndroidRuntime(13765): at java.lang.reflect.Method.invoke(Method.java:507)
01-16 13:57:02.801: E/AndroidRuntime(13765): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
01-16 13:57:02.801: E/AndroidRuntime(13765): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
01-16 13:57:02.801: E/AndroidRuntime(13765): at dalvik.system.NativeStart.main(Native Method)

I use ImageTargets.Java as a base (but i have changed so it loads the proper libraries).

OK, glad that you figured

January 17, 2013 - 6:00am #4

OK, glad that you figured that out.

Yes i took those steps, but i

January 17, 2013 - 5:58am #3

Yes i took those steps, but i figured out the problem. I had commented out the second LOCAL_SRC_FILES to see if my paths were properly created (../../etc) and when i added my c++ code i never commented them back in. So I noticed that after compiling the sample jni I had some lines saying "arm++ comipling >" or somthing similar but in mine I didnt.

 

SO thank you for the help.

Hi raigex, the unsatisfied

January 16, 2013 - 2:16pm #2

Hi raigex,

the unsatisfied link error usually means that you did not rebuilt the C++ code (using ndk-build) or perhaps you did not refresh the project in Eclipse after compiling the native code.

Are you sure you took those steps ?

 

Log in or register to post comments