arcore-android-sdk icon indicating copy to clipboard operation
arcore-android-sdk copied to clipboard

ARCore without GLSurface.Renderer and in Foreground Services

Open pablovela5620 opened this issue 3 years ago • 1 comments

SPECIFIC ISSUE ENCOUNTERED

Following up on #1259, #360 #31 #259, it seems that there's a few use cases for getting the motion tracked position of the phone without needing to render the camera image. Currently it seems that a GLContext is required but there's no good documentations or examples as to how this would be done without the use of GLSurface.Renderer (I'm guessing this is not a typical use).

I'm in the process of building an app that would relay the position of a phone to a VR headset to allow of phone tracking after a fixed point calibration. I need the ability to generate the pose from a Foreground service meaning I won't have access to the GLSurface in order to render the image and have the relevant GL context.

I've seen a few recommendations to just rip out the GLThread from GLSurfaceView. But I'm not fully sure what that means or how to do that. When following #360 I get a similar com.google.ar.core.exceptions.MissingGlContextException

Any guidance as to how this could be done would be greatly appreciated (Documentation or examples as so far I haven't found too much)

This is currently what my code looks like


import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.opengl.EGL14;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.opengl.GLSurfaceView;
import android.opengl.EGLConfig;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

import androidx.annotation.Nullable;

import com.google.ar.core.Session;
import com.google.ar.core.exceptions.CameraNotAvailableException;


public class TrackingForegroundService extends Service {

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Thread mGLThread = new CustomGLThread();
        mGLThread.start();

        // setup notification
        Log.d("Foreground Service", "Starting");
        final String CHANNELID = "Foreground Service ID";
        NotificationChannel channel = new NotificationChannel(
                CHANNELID,
                CHANNELID,
                NotificationManager.IMPORTANCE_LOW
        );

        getSystemService(NotificationManager.class).createNotificationChannel(channel);
        Notification.Builder notification = new Notification.Builder(this, CHANNELID)
                .setContentText("Service is running")
                .setContentTitle("Service enabled")
                .setSmallIcon(R.drawable.ic_launcher_background);

        startForeground(1001, notification.build());
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    static class CustomGLThread extends Thread {

        private static EGLDisplay mEGLDisplay;
        private static boolean mGLContextCreated;
        private static android.opengl.EGLConfig[] mEGLConfig;
        private static EGLSurface mEGLSurface;
        private static EGLContext mEGLContext;

        Session session;

        @Override
        public void run() {
            setName("CustomGLThread" + getId());
            try {
                guardedRun();
            } catch (InterruptedException e) {
                // fall thru and exit normally
            } finally {
                Log.e("Thread", "Thread");
//                sGLThreadManager.threadExiting(this);
            }

        }

        private void guardedRun() throws InterruptedException {
            while (true) {
                Log.e("Service", "Running Thread");
                try {
                    InitGLContext();
                    Frame frame = session.update();
                    Camera camera = frame.getCamera();
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (CameraNotAvailableException e){
                    //done
                }
            }
        }

        private static boolean InitGLContext() {
            if (mGLContextCreated) {
                Log.e("GL", "Created");
                return true;
            }
            Log.v("GL", "init GL context");
            mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
            if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
                Log.e("GL", "gl context init fail! unable to open connection to local window system");
                return false;
            }
            int[] eglMajVersion = new int[10];
            int[] eglMinVersion = new int[10];
            if (!EGL14.eglInitialize(mEGLDisplay, eglMajVersion, 0, eglMinVersion, 0)) {
                Log.e("GL", "unable to initialize EGL");
                return false;
            }

            int[] confAttr = {
                    EGL14.EGL_RENDERABLE_TYPE,
                    EGL14.EGL_OPENGL_ES2_BIT,// very important!
                    EGL14.EGL_SURFACE_TYPE,
                    EGL14.EGL_PBUFFER_BIT,//EGL_WINDOW_BIT EGL_PBUFFER_BIT we will create a pixelbuffer surface
                    EGL14.EGL_RED_SIZE, 8,
                    EGL14.EGL_GREEN_SIZE, 8,
                    EGL14.EGL_BLUE_SIZE, 8,
                    EGL14.EGL_ALPHA_SIZE, 8,// if you need the alpha channel
                    EGL14.EGL_DEPTH_SIZE, 8,// if you need the depth buffer
                    EGL14.EGL_STENCIL_SIZE, 8,
                    EGL14.EGL_NONE};

            EGLConfig[] mEGLConfig = new EGLConfig[1];
            int[] numConfig = new int[1];
            if (!EGL14.eglChooseConfig(mEGLDisplay, confAttr, 0, mEGLConfig, 0, 1, numConfig, 0)) {
                Log.e("GL", "some config is wrong");
                return false;
            } else {
                Log.v("GL", "all config is ok");
            }

            int[] surfaceAttr = {
                    EGL14.EGL_WIDTH, 640,
                    EGL14.EGL_HEIGHT, 480,
                    EGL14.EGL_NONE
            };
            mEGLSurface = EGL14.eglCreatePbufferSurface(mEGLDisplay, mEGLConfig[0], surfaceAttr, 0);
            if (mEGLSurface == EGL14.EGL_NO_SURFACE) {
                switch (EGL14.eglGetError()) {
                    case EGL14.EGL_BAD_ALLOC:
                        // Not enough resources available. Handle and recover
                        Log.e("GL", "Not enough resources available");
                        break;
                    case EGL14.EGL_BAD_CONFIG:
                        // Verify that provided EGLConfig is valid
                        Log.e("GL", "provided EGLConfig is invalid");
                        break;
                    case EGL14.EGL_BAD_PARAMETER:
                        // Verify that the EGL_WIDTH and EGL_HEIGHT are
                        // non-negative values
                        Log.e("GL", "provided EGL_WIDTH and EGL_HEIGHT is invalid");
                        break;
                    case EGL14.EGL_BAD_MATCH:
                        // Check window and EGLConfig attributes to determine
                        // compatibility and pbuffer-texture parameters
                        Log.e("GL", "Check window and EGLConfig attributes");
                        break;
                }
                return false;
            }

            int[] contextAttr = {
                    EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,// very important!
                    EGL14.EGL_NONE
            };
            mEGLContext = EGL14.eglCreateContext(mEGLDisplay, mEGLConfig[0], EGL14.EGL_NO_CONTEXT, contextAttr, 0);
            if (mEGLContext == EGL14.EGL_NO_CONTEXT) {
                if (EGL14.EGL_BAD_CONFIG == EGL14.eglGetError()) {
                    Log.e("GL", "EGL_BAD_CONFIG");
                }
                return false;
            }

            if (!EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
                Log.e("GL", "make current fail");
                return false;
            }
            Log.v("GL", "GL initialize succeed");
            mGLContextCreated = true;
            return true;
        }
    }
}

VERSIONS USED

  • Android Studio: Bumblebee 2021.1.1 Patch 3
  • ARCore SDK for Android: 33-rc3
  • Device manufacturer, model, and O/S: Google Pixel 4a

pablovela5620 avatar Apr 28 '22 22:04 pablovela5620

@pablovela5620 Hey, did you find a solution to your issue? I am facing a similar situation.

laxnpander avatar Apr 30 '24 10:04 laxnpander