1

I am trying to use the camera's image as part of live wallpaper.

In the Engine that I declared, I have this code:

public class Class extends WallpaperService
{
    Camera cam;

    @Override
    public void onCreate()
    {
        super.onCreate();
        cam = Camera.open();
    }

    //...

    @Override
    public Engine onCreateEngine()
    {
        return new CubeEngine(cam);
    }

    class CubeEngine extends Engine
    {
        Camera cam;

        CubeEngine(Camera cam)
        {
            this.cam=cam;
        }

        //...

        @Override
        public void onDestroy()
        {
            if (cam != null)
            {
                cam.stopPreview();
                cam.setPreviewCallback(null);
                cam.release();
                cam = null;
            }
            super.onDestroy();
        }

        @Override
        public void onSurfaceChanged(SurfaceHolder holder, int format,
                int width, int height)
        {
            try
            {
                cam.setPreviewDisplay(holder);
                cam.startPreview();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
            super.onSurfaceChanged(holder, format, width, height);
        }

        @Override
        public void onSurfaceCreated(SurfaceHolder holder)
        {
            super.onSurfaceCreated(holder);
        }

        @Override
        public void onSurfaceDestroyed(SurfaceHolder holder)
        {
            if (cam != null)
            {
                cam.stopPreview();
                cam.setPreviewCallback(null);
                cam.release();
                cam = null;
            }
            super.onSurfaceDestroyed(holder);
        }

        //...
    }
}

cam is a Camera that was declared as Camera.open();

When I run this I get: java.io.IOException: setPreviewDisplay failed

I am now getting this exception:

    07-26 00:12:18.399: WARN/CameraService(1357): Overlay create failed - retrying
07-26 00:12:18.419: WARN/CameraService(1357): Overlay create failed - retrying
07-26 00:12:18.439: WARN/CameraService(1357): Overlay create failed - retrying
07-26 00:12:18.459: WARN/CameraService(1357): Overlay create failed - retrying
07-26 00:12:18.479: WARN/CameraService(1357): Overlay create failed - retrying
07-26 00:12:18.509: WARN/CameraService(1357): Overlay create failed - retrying
07-26 00:12:18.529: WARN/CameraService(1357): Overlay create failed - retrying
07-26 00:12:18.549: WARN/CameraService(1357): Overlay create failed - retrying
07-26 00:12:18.569: ERROR/CameraService(1357): Overlay Creation Failed!


07-26 00:12:18.609: WARN/System.err(4104): java.lang.RuntimeException: startPreview failed
07-26 00:12:18.609: WARN/System.err(4104):     at android.hardware.Camera.startPreview(Native Method)
07-26 00:12:18.609: WARN/System.err(4104):     at com.petrifiednightmares.transparentphone.main.GenericaCamera.surfaceChanged(GenericaCamera.java:29)
07-26 00:12:18.609: WARN/System.err(4104):     at android.service.wallpaper.WallpaperService$Engine.updateSurface(WallpaperService.java:687)
07-26 00:12:18.609: WARN/System.err(4104):     at android.service.wallpaper.WallpaperService$Engine.attach(WallpaperService.java:749)
07-26 00:12:18.619: WARN/System.err(4104):     at android.service.wallpaper.WallpaperService$IWallpaperEngineWrapper.executeMessage(WallpaperService.java:984)
07-26 00:12:18.619: WARN/System.err(4104):     at com.android.internal.os.HandlerCaller$MyHandler.handleMessage(HandlerCaller.java:61)
07-26 00:12:18.619: WARN/System.err(4104):     at android.os.Handler.dispatchMessage(Handler.java:99)
07-26 00:12:18.619: WARN/System.err(4104):     at android.os.Looper.loop(Looper.java:143)
07-26 00:12:18.619: WARN/System.err(4104):     at android.app.ActivityThread.main(ActivityThread.java:4293)
07-26 00:12:18.629: WARN/System.err(4104):     at java.lang.reflect.Method.invokeNative(Native Method)
07-26 00:12:18.629: WARN/System.err(4104):     at java.lang.reflect.Method.invoke(Method.java:507)
07-26 00:12:18.629: WARN/System.err(4104):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
07-26 00:12:18.629: WARN/System.err(4104):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
07-26 00:12:18.629: WARN/System.err(4104):     at dalvik.system.NativeStart.main(Native Method)
Razor Storm
  • 12,167
  • 20
  • 88
  • 148
  • I think you are going to have to add a bit more code so that we can see the whole setup for the camera in class you are using this in. – Idistic Jul 26 '11 at 05:14
  • Yep, you'll need to show more code. For example, how did you create the surface? – Lior Ohana Jul 26 '11 at 05:16
  • I put more code up. This is my first foray into android development, so please let me know if I'm making some really stupid errors. :) – Razor Storm Jul 26 '11 at 05:32
  • Although I would do the setup events a little differently the only thing that I see that is missing is setting the camera's preview size. I will be interested to see if the surfaceview of the wallpaper can handle camera previews. Don't think this is a good idea, but sounds like an interesting project. – Idistic Jul 26 '11 at 06:08
  • Yeah that's the thing I wasn't sure about. I haven't seen any live wallpapers that take advantage of the camera and decided to write up a quick app as a proof of concept (could evolve into a full fledged AR app). A big possibility is that wall papers simply cannot handle camera previews (could be a deliberate move by android developers to prevent excessive battery usage?) – Razor Storm Jul 26 '11 at 06:15
  • Too tired to try and run this down, I created a live wallpaper and added in the camera code, the camera was failing with a buffer error, so I forced it to use push buffers and then it failed with a null pointer exception in a private onUpdateSurface call. Have to think about this a bit. – Idistic Jul 26 '11 at 08:39

2 Answers2

5

It more or less follows your code with a few changes.

import java.io.IOException;
import android.hardware.Camera;
import android.view.SurfaceHolder;

public class GenericaCamera implements SurfaceHolder.Callback {

    private Camera         cameraDevice        = null;
    private SurfaceHolder  cameraSurfaceHolder = null; 

    // MODIFIED FROM ORIGINAL SEE UPDATE AT BOTTOM
    public GenericaCamera(SurfaceHolder holder)
    {
        cameraSurfaceHolder = holder;
        cameraSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        cameraSurfaceHolder.addCallback(this);
    }

     // Required camera surface holder interface Callback's
     public void surfaceChanged(SurfaceHolder holder, int format, int w, int h)
     {
         Camera.Parameters params = cameraDevice.getParameters();
         Camera.Size       size   = getBestPreviewSize(params,w,h);

         if (size != null)
            params.setPreviewSize(size.width, size.height);
         cameraDevice.startPreview();
     }

     // When the surface is ready then we can build the camera and attach 
     // the camera preview output to the UI holder 
     public void surfaceCreated(SurfaceHolder holder)
     {
        try {

            cameraDevice = Camera.open();  
            cameraDevice.setPreviewDisplay(cameraSurfaceHolder);

        } catch (IOException e) { }

    }

    // Stop the camera preview and dispose of the camera object 
    public void surfaceDestroyed(SurfaceHolder holder)
    {
        if (null == cameraDevice)
            return; 
        cameraDevice.stopPreview(); 
        cameraDevice.release(); 
        cameraDevice = null; 
    }

    public Camera.Size getBestPreviewSize(Camera.Parameters parameters, int w, int h)
    {
        Camera.Size result = null; 

        for (Camera.Size size : parameters.getSupportedPreviewSizes())
        {
            if (size.width <= w && size.height <= h)
            {
                if (null == result)
                result = size; 
            else
            {
                int resultDelta = w - result.width + h - result.height;
                int newDelta    = w - size.width   + h - size.height;

                    if (newDelta < resultDelta)
                result = size; 
            }
            } 
        }
        return result; 
    }

}

Update

Went into the wallpaper code and figured it out, still problems but at least I got it to basically work, order of when is important because of the push buffers setup

Problems

  1. Not in landscape mode, this causes a distorted image to be previewed, known issue of the camera just havent tried to lock into landscape yet

  2. In preview if you use back key everything is destroyed properly (camera released) but if you set the wallpaper the class does not call the onDestroy method so this means that you can't get the camera because the preview instance of the class has not released it

  3. It also shows up as the lock screen image, not sure how to override that, probably receive and respond via intents and turn it off when you are in the guard/lock screen

  4. Have not handled visibility event, so other camera classes won't work, potential unnecessary battery drain etc.

Anyway with all of that said the modifications are as follows to get this to at least start working

In the engine class create the camera from the above class in the onCreate, any later and problems occur because of the push buffers setup, and even though this is deprecated in later 2.3+ it is still required in older versions. Matter of fact have not tested this above 2.2 yet.

@Override
    public void onCreate(SurfaceHolder holder) {
        Log.d("CameraWallpaper","onCreate(SurfaceHolder holder)");
        if (null == GC)
        GC = new GenericaCamera(holder);
    super.onCreate(holder);
}

Then in the GenericCamera class change to the following constructor pattern

public GenericaCamera(SurfaceHolder holder)
{
     cameraSurfaceHolder = holder;
     cameraSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
     cameraSurfaceHolder.addCallback(this);
}

That's basically it, at least this should get you started, and if you find any solutions to 1-4 let the community know!

Update 2

It seems that Android 3+ breaks this, not sure why since the source for those platforms is still closed.

So final answer for upto 2.3 is that above works, but on 3+ it will not.

Idistic
  • 6,281
  • 2
  • 28
  • 38
  • Ok I tried simply putting your code in wholesale and instantiating it at surfaceCreated() of my code and passing it the SurfaceHandler. (I modified your class a bit to take in SurfaceHandlers instead of SurfaceViews. The same problem persists, I am beginning to think that the wallpaper surface is not suitable for camera. – Razor Storm Jul 26 '11 at 07:01
  • Actually, the error changed, it is now breaking startPreview() instead of setPreviewSize() – Razor Storm Jul 26 '11 at 07:14
  • I'll modify my question with a copy paste of the exception. – Razor Storm Jul 26 '11 at 07:23
  • I still haven't gotten it to work yet, what did you change to get it working? – Razor Storm Jul 31 '11 at 23:43
  • Ok I tried the code, there's no more errors, but the screen is just black instead of the camera feed. A possibility is that since I have gingerbread on my phone the code is not compatible. hmm – Razor Storm Aug 01 '11 at 02:14
  • 1
    Possibly, make take out the push buffer call in the camera code, for gingerbread that is supposed to be deprecated, but I have not test it. I will test it later using 3.1 which do have on tablet. You are picking the phone up right ? (laugh, but I was leaving mine on the table and the lens was covered) – Idistic Aug 01 '11 at 02:18
  • HAHAHA Yeah I was thinking that for a while too. I slapped myself after realizing that I should pick up the phone so the lens wouldn't be covered. Sadly, after uncovering the lens, it was still black. haha – Razor Storm Aug 01 '11 at 07:22
  • 3+ as in android 3.0 and above? Hmm it's interesting that it's not working on my 2.3.3 either. Unless HTC Sense somehow messes it up? I doubt that sense modified the wallpaper code though. – Razor Storm Aug 02 '11 at 22:07
  • That's a possibility, too bad I don't have a single camera device running 2.3+ to test. Hmmm, I tried looking at the android source to debug, but it hasn't been too helpful (all the important stuff are native C++ libraries that I can't find sources for). One obvious thing to try is to force it to explicitly grab the main back camera by doing Camera.open(0), but this didn't help. :( – Razor Storm Aug 02 '11 at 22:30
  • Well that's progress, haha what did you change? By the way, how are you even debugging this? I'm having trouble figuring out a way to systematically test the problem, the fact that there arent even any error messages just compounds the problem. – Razor Storm Aug 03 '11 at 00:17
  • Deep sigh ... well not what I thought I was doing, it's just randomly working and then not working. I suspect this has to do with the way they are now using updateWindow() after the setType call ... at least I can now log a defect and see what they say ... – Idistic Aug 03 '11 at 00:27
0

I have seen the following gotcha giving me java.io.IOException: setPreviewDisplay failed:

If you do both video and photo, there are two functions, camera.unlock() and camera.reconnect(). You must camera.unlock() before recording a video and camera.reconnect() before taking a photo.

18446744073709551615
  • 16,368
  • 4
  • 94
  • 127