Zamar
Zamar

Reputation: 529

SurfaceView - > Surface had no valid native window | Android Camera API | hardware.camera2

I'm having troubles with the SurfaceView class of Android Developing using it on the latest Camera API. I've double checked that my min API level is 21.

I've approached this in two ways, one is creating the SurfaceView in the XML and then giving the value of it to my private SurfaceView or creating it with the public constructor.

With that SurfaceView I can obtain the SurfaceHolder with getHolder() and then the Surface with getSurface().

In the code I'm posting I'm just creating it with the public constructor.

This SurfaceView is supposed to put the preview of the Camera with createCaptureSession()

Here's my code, its a quick class implementation I've tried after trying it yesterday with another project. check onCreate() of MainActivity and onOpened(), there's where SurfaceView is used.

package com.example.universitywork.canyouwork;
/* IMPORTS */ 

public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback {
// CREAMOS CLASES
private CameraManager mManager;
private CameraDevice cDevice;
private CaptureRequest cRequest;
private Handler handle = new Handler();
private SurfaceView surface;
private SurfaceHolder holder;
private Surface under;
private final CameraDevice.StateCallback mCallbackDevice = new CameraDevice.StateCallback() {

    @Override
    public void onOpened(CameraDevice camera) {
        cDevice = camera;
        try {
            camera.createCaptureSession(Arrays.asList(under), mCallbackSession, handle);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void onDisconnected(CameraDevice camera) {
        System.out.println("ONDC");
    }

    @Override
    public void onError(CameraDevice camera, int error) {
        System.out.println("ONerror");
    }
};

private final CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {

};

private final CameraCaptureSession.StateCallback mCallbackSession = new CameraCaptureSession.StateCallback() {

    @Override
    public void onConfigured(CameraCaptureSession session) {
        try {
            System.out.println("LLEGO AQUI");

            cRequest = cDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).build();
          //  session.capture(cRequest, mCaptureCallback, handle);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onConfigureFailed(CameraCaptureSession session) {
        System.out.println("ONCONFIGUREFAILED");
    }
};

public MainActivity() {

}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    surface= new SurfaceView(this);
    holder = surface.getHolder();
    holder.addCallback(this);
    holder.setFixedSize(500,500);
    under= holder.getSurface();
    mManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    String id = getId(mManager);
    try {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {

            // TODO: Consider calling
            //    ActivityCompat#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for ActivityCompat#requestPermissions for more details.
            return;
        }
        mManager.openCamera(id, mCallbackDevice, handle);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}


@Override
public void surfaceCreated(SurfaceHolder holder) {
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    System.out.println("ONCHANGED");
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    System.out.println("ONDESTROYED");
}
public String getId (CameraManager manager) {
    try {
        for (String cameraId : manager.getCameraIdList()) {
            CameraCharacteristics characteristics =         manager.getCameraCharacteristics(cameraId);
            int orientation =     characteristics.get(CameraCharacteristics.LENS_FACING);
            if (orientation == CameraCharacteristics.LENS_FACING_FRONT) return cameraId;
        }
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
    return "NULL";
}}

Fast forward to the error:

    E/Legacy-CameraDevice-JNI: getNativeWindow: Surface had no valid native window.
    E/Legacy-CameraDevice-JNI: LegacyCameraDevice_nativeDetectSurfaceDimens: Could not retrieve native window from surface.
    D/AndroidRuntime: Shutting down VM
    E/AndroidRuntime: FATAL EXCEPTION: main
                  Process: com.example.universitywork.canyouwork, PID: 4885
                  java.lang.IllegalArgumentException: Surface was abandoned
                      at android.hardware.camera2.utils.SurfaceUtils.getSurfaceSize(SurfaceUtils.java:70)
                      at android.hardware.camera2.params.OutputConfiguration.<init>(OutputConfiguration.java:97)
                      at android.hardware.camera2.params.OutputConfiguration.<init>(OutputConfiguration.java:71)
                      at android.hardware.camera2.impl.CameraDeviceImpl.createCaptureSession(CameraDeviceImpl.java:474)
                      at com.example.universitywork.canyouwork.MainActivity$1.onOpened(MainActivity.java:40)
                      at android.hardware.camera2.impl.CameraDeviceImpl$1.run(CameraDeviceImpl.java:134)
                      at android.os.Handler.handleCallback(Handler.java:739)
                      at android.os.Handler.dispatchMessage(Handler.java:95)
                      at android.os.Looper.loop(Looper.java:148)
                      at android.app.ActivityThread.main(ActivityThread.java:5417)
                      at java.lang.reflect.Method.invoke(Native Method)
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
                   Caused by: android.hardware.camera2.legacy.LegacyExceptionUtils$BufferQueueAbandonedException
                      at android.hardware.camera2.legacy.LegacyExceptionUtils.throwOnError(LegacyExceptionUtils.java:64)
                      at android.hardware.camera2.legacy.LegacyCameraDevice.getSurfaceSize(LegacyCameraDevice.java:540)
                      at android.hardware.camera2.utils.SurfaceUtils.getSurfaceSize(SurfaceUtils.java:68)
                      at android.hardware.camera2.params.OutputConfiguration.<init>(OutputConfiguration.java:97) 
                      at android.hardware.camera2.params.OutputConfiguration.<init>(OutputConfiguration.java:71) 
                      at android.hardware.camera2.impl.CameraDeviceImpl.createCaptureSession(CameraDeviceImpl.java:474) 
                      at com.example.universitywork.canyouwork.MainActivity$1.onOpened(MainActivity.java:40) 
                      at android.hardware.camera2.impl.CameraDeviceImpl$1.run(CameraDeviceImpl.java:134) 
                      at android.os.Handler.handleCallback(Handler.java:739) 
                      at android.os.Handler.dispatchMessage(Handler.java:95) 
                      at android.os.Looper.loop(Looper.java:148) 
                      at android.app.ActivityThread.main(ActivityThread.java:5417) 
                      at java.lang.reflect.Method.invoke(Native Method) 
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 

I think it has to do with the size or the underlying surface of the SurfaceView. Maybe im not using it correctly but the Android documentation says it can be made with SurfaceView.

Upvotes: 3

Views: 8365

Answers (4)

A.Hosein
A.Hosein

Reputation: 182

You just need to call "openCamera" function after your surfaceView Created
Put the below code to onCreate ( #kotlin )

 mySurfaceView!!.holder!!.addCallback(object: SurfaceHolder.Callback {
        override fun surfaceChanged(holder: SurfaceHolder, format: Int,
                                    width: Int, height: Int) {

        }

        override fun surfaceCreated(holder: SurfaceHolder) {
          
            openCamera()

        }

        override fun surfaceDestroyed(holder: SurfaceHolder) {

        }
    })

Upvotes: 3

Rakesh
Rakesh

Reputation: 199

i found solution use this method to choose your size

private static Size chooseOptimalSize(Size[] choices, int width, int height) {
        Size bigEnough = null;
        int minAreaDiff = Integer.MAX_VALUE;
        for (Size option : choices) {
            int diff = (width*height)-(option.getWidth()*option.getHeight()) ;
            if (diff >=0 && diff < minAreaDiff &&
                    option.getWidth() <= width &&
                    option.getHeight() <= height) {
                minAreaDiff = diff;
                bigEnough = option;
            }
        }
        if (bigEnough != null) {
            return bigEnough;
        } else {
            Arrays.sort(choices,new CompareSizeByArea());
            return choices[0];
        }

    }

Upvotes: 2

Gustav
Gustav

Reputation: 149

After many days and sleepless nights of battling with the camera2 api, I came to the conclusion that the documentation and even the samples are not crystal clear, or, I am just slow on the uptake. Consolation for me came when I saw lots of people are battling with the api and replies to issues raised does not necessary provide solutions and simple answers.

My attempt was to take two pictures - one after the other. Even closing the Intent with finish() and reopening it, did not provide the solution - I simply could not take a second picture without relaunching the entire application. On the second picture, the API kept on failing with the following issue:-

Fatal Exception: java.lang.IllegalArgumentException Surface had no valid native Surface.

android.hardware.camera2.legacy.LegacyCameraDevice.nativeGetSurfaceId (LegacyCameraDevice.java)
android.hardware.camera2.legacy.LegacyCameraDevice.getSurfaceId (LegacyCameraDevice.java:658)
android.hardware.camera2.legacy.LegacyCameraDevice.containsSurfaceId (LegacyCameraDevice.java:678)

These lines of code provided the clue and my breakthrough:-

ImageReader reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1);
outputSurfaces.add(reader.getSurface());
outputSurfaces.add(new Surface(textureView.getSurfaceTexture()));

We retrieve the bitmap and save it to a file more or less as follows:-

image = reader.acquireLatestImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.capacity()];
buffer.get(bytes);

Then finally:-

if (image != null) {
    image.close();
}

Overlooked by many:-

ImageReader reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1);

For some reason, this remains running as an instance and will continue to do so until it is closed. It does not recycle with the GC and it does not get destroyed when the class is closed. You should manually close the reader as well:-

if (image != null) {
    image.close();
}
if(reader != null) {
    reader.close();
}

It solved all my camera2 issues I am sure only until next time. Hope this helped somebody else as well!

Upvotes: 7

Zamar
Zamar

Reputation: 529

After lots of hours I realised I just needed to reach SurfaceCreated() and I didn't reach it, solution was to not openCamera in onCreate().

Upvotes: 4

Related Questions