0

Following code is tested on HTC Desire S, Galaxy S II and emulator. It is working fine, but surprisingly it doesn't work on Galaxy S Duos (GT-S7562). What happens is that all calls are successful with no exception but callbacks are not called.

public class CameraManager implements PictureCallback  {
    private final static String DEBUG_TAG = "CameraManager";

public void TakePicture() {
        try {
            _camera = Camera.open(cameraId);
            Log.d(DEBUG_TAG, "Camera.TakePicture.open");
            SurfaceView view = new SurfaceView(CameraManager.this.getContext());
            _camera.setPreviewDisplay(view.getHolder());
            Log.d(DEBUG_TAG, "Camera.TakePicture.setPreviewDisplay");
            _camera.startPreview();
            Log.d(DEBUG_TAG, "Camera.TakePicture.startPreview");

            AudioManager manager = (AudioManager) CameraManager.super.getContext().getSystemService(Context.AUDIO_SERVICE);
            Log.d(DEBUG_TAG, "Camera.TakePicture.AudioManager.ctor()");
            manager.setStreamVolume(AudioManager.STREAM_SYSTEM, 0 , AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
            Log.d(DEBUG_TAG, "Camera.TakePicture.setStreamVolume");

            Camera.ShutterCallback shutter = new Camera.ShutterCallback() {
                @Override
                public void onShutter() {
                    AudioManager manager = (AudioManager) CameraManager.super.getContext().getSystemService(Context.AUDIO_SERVICE);
                    Log.d(DEBUG_TAG, "Camera.TakePicture.Shutter.AudioManager.ctor()");
                    manager.setStreamVolume(AudioManager.STREAM_SYSTEM, manager.getStreamMaxVolume(AudioManager.STREAM_SYSTEM) , AudioManager.FLAG_ALLOW_RINGER_MODES);
                    Log.d(DEBUG_TAG, "Camera.TakePicture.Shutter.setStreamVolume");
                }
            };

            Camera.PictureCallback rawCallback = new Camera.PictureCallback() {
                @Override
                public void onPictureTaken(byte[] data, Camera camera) {
                    if (data != null) {
                        Log.i(DEBUG_TAG, "Picture taken::RAW");
                        _camera.stopPreview();
                        _camera.release();
                    } else {
                        Log.wtf(DEBUG_TAG, "Picture NOT taken::RAW");
                    }
                }
            };
            _camera.takePicture(shutter, rawCallback, CameraManager.this);
            Log.d(DEBUG_TAG, "Camera.TakePicture.taken");
        } catch (Exception err) {
            err.printStackTrace();
            Log.d(DEBUG_TAG, "Camera.TakePicture.Exception:: %s" + err.getMessage());
        }
    }


    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        if (data != null) {
            Log.i(DEBUG_TAG, "Picture taken::JPG");
            _camera.stopPreview();
            _camera.release();
        } else {
            Log.wtf(DEBUG_TAG, "Picture NOT taken::JPG");
        }
    }
}

Here's the output log of logcat for execution of above code, As you can see, callbacks are not called.:

[ 10-16 01:39:18.711  3873:0xf21 D/CameraManager ]
Camera.TakePicture.open

[ 10-16 01:39:18.891  3873:0xf21 D/CameraManager ]
Camera.TakePicture.setFrontCamera

[ 10-16 01:39:18.901  3873:0xf21 D/CameraManager ]
Camera.TakePicture.setPreviewDisplay

[ 10-16 01:39:18.901  3873:0xf21 D/CameraManager ]
Camera.TakePicture.startPreview

[ 10-16 01:39:18.901  3873:0xf21 D/CameraManager ]
Camera.TakePicture.AudioManager.ctor()

[ 10-16 01:39:19.001  3873:0xf21 D/CameraManager ]
Camera.TakePicture.setStreamVolume

[ 10-16 01:39:19.041  3873:0xf21 D/CameraManager ]
Camera.TakePicture.taken

I have also checked SO for similar problems with Galaxy S and found following code, I used it with no success:

Camera.Parameters parameters = camera.getParameters();
parameters.set("camera-id", 2);
// (800, 480) is also supported front camera preview size at Samsung Galaxy S.
parameters.setPreviewSize(640, 480); 
camera.setParameters(parameters);

I was wondering if anyone could tell me what's wrong with my code? or maybe there's some limitations with this model that doesn't allow taking pictures without showing a preview surface. If so, then could you please let me know of any possible workaround? Note that this code is executed from an android service.

Farzan
  • 745
  • 10
  • 25

2 Answers2

0

Documentation is explicit: you must start preview if you want to take a picture. From your code, it is not clear why the preview surface is not showing. IIRC, in Honeycomb and later, you cannot play with the preview surface coordinates to move it off screen. But you can usually hide the preview surface behind some image view.

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • The code is running from android service and as you can see a dummy SurfaceView is passed to setPreviewDisplay method, so there will be no Preview Surface to be shown. Following the documentation is not what I am looking for, the code works on some devices so what I am trying to do is not restricted by android sdk itself instead it's restricted by device manufacturers. In other word, it's more about compatibility issue rather than sdk restriction. – Farzan Oct 16 '13 at 22:38
  • I noticed that you try to silence the shutter. This may be against regulations in some markets. If the device under test complies with such regulations, it will silently ignore an attempt to capture a picture without a shutter sound. – Alex Cohn Oct 16 '13 at 22:52
  • So are you suggesting that if I avoid to mute the shutter sound then the problem might be solved? – Farzan Oct 17 '13 at 00:17
  • worth trying… I would also try to put the SurfaceView on the screen - even if that could only prove what specifically is wrong with this device. – Alex Cohn Oct 17 '13 at 06:42
  • thanks for your suggestion. Not muting shutter didn't help but couldn't find the time to show the SurfaceView yet. In the meantime I'll be happy to know any other suggestions. – Farzan Oct 29 '13 at 21:04
0

Camera.takePicture with a rawCallback requires calling addRawImageCallbackBuffer

(I ran into the problem too and had to go to the source code to figure is out) When Camera.takePicture is called with second argument (Callback raw) non-null, the user must call Camera.addRawImageCallbackBuffer() at least once before takePicture() to start the supply of buffers for data to be returned in. If this is not done, the image is discarded (and apparently the callbacks are not called.

This is a block comment from android.hardware.Camera.java for addRawImageCallbackBuffer():

Adds a pre-allocated buffer to the raw image callback buffer queue.
Applications can add one or more buffers to the queue. When a raw image
frame arrives and there is still at least one available buffer, the
buffer will be used to hold the raw image data and removed from the
queue. Then raw image callback is invoked with the buffer. If a raw
image frame arrives but there is no buffer left, the frame is
discarded. Applications should add buffers back when they finish
processing the data in them by calling this method again in order
to avoid running out of raw image callback buffers.

The size of the buffer is determined by multiplying the raw image
width, height, and bytes per pixel. The width and height can be
read from {@link Camera.Parameters#getPictureSize()}. Bytes per pixel
can be computed from
{@link android.graphics.ImageFormat#getBitsPerPixel(int)} / 8,
using the image format from {@link Camera.Parameters#getPreviewFormat()}.

This method is only necessary when the PictureCallbck for raw image
is used while calling {@link #takePicture(Camera.ShutterCallback,
Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)}.

Please note that by calling this method, the mode for
application-managed callback buffers is triggered. If this method has
never been called, null will be returned by the raw image callback since
there is no image callback buffer available. Furthermore, When a supplied
buffer is too small to hold the raw image data, raw image callback will
return null and the buffer will be removed from the buffer queue.

@param callbackBuffer the buffer to add to the raw image callback buffer
queue. The size should be width * height * (bits per pixel) / 8. An
null callbackBuffer will be ignored and won't be added to the queue.

@see #takePicture(Camera.ShutterCallback,
Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)}.

Try your code with the 'raw' callback argument to takePicture() set to null.

Ribo
  • 3,363
  • 1
  • 29
  • 35