12

I am trying to build a small photo app with a burst mode for camera. The main idea is to shoot a picture every 0,3sec and to store the pictures in an Array until the last picture is taken. The number of pictures to shoot should specified.

I can only shoot a picture every 2sec. as shortest interval. Then, when the pictures should be written to storage, the applications chrashes.

Does someone know how to implement this? Here is my code:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.View.OnClickListener;

public class CamaeaView extends Activity implements SurfaceHolder.Callback, OnClickListener {

    static final int FOTO_MODE = 1;
    private static final String TAG = "Test";
    Camera mCamera;
    boolean mPreviewRunning = false;
    private Context mContext = this;
    ArrayList<Object> listImage = null;
    public static ArrayList<Object> List1[];

    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        Log.e(TAG, "onCreate");

        @SuppressWarnings("unused")
        Bundle extras = getIntent().getExtras();

        getWindow().setFormat(PixelFormat.TRANSLUCENT);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.main);
        mSurfaceView = (SurfaceView) findViewById(R.id.surface_camera);
        mSurfaceView.setOnClickListener(this);
        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.addCallback(this);
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
    }

    Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
        public void onPictureTaken(byte[] imageData, Camera c) {
            Log.d("Start: ","Imagelist");

            Intent mIntent = new Intent();
            listImage.add(StoreByteImage(imageData));
            SaveImage(listImage);
            mCamera.startPreview();

            setResult(FOTO_MODE, mIntent);
            finish();

        }
    };

    protected void onResume() {
        Log.d(TAG, "onResume");
        super.onResume();
    }

    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
    }

    protected void onStop() {
        Log.e(TAG, "onStop");
        super.onStop();
    }

    public void surfaceCreated(SurfaceHolder holder) {
        Log.d(TAG, "surfaceCreated");
        mCamera = Camera.open();

    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        Log.d(TAG, "surfaceChanged");

        // XXX stopPreview() will crash if preview is not running
        if (mPreviewRunning) {
            mCamera.stopPreview();
        }

        Camera.Parameters p = mCamera.getParameters();
        p.setPreviewSize(w, h);
        p.setPictureSize(1024, 768);
        p.getSupportedPictureFormats();
        p.setPictureFormat(PixelFormat.JPEG);
        mCamera.setParameters(p);
        try {
            mCamera.setPreviewDisplay(holder);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        mCamera.startPreview();
        mPreviewRunning = true;
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        Log.e(TAG, "surfaceDestroyed");
        mCamera.stopPreview();
        mPreviewRunning = false;
        mCamera.release();
    }

    private SurfaceView mSurfaceView;
    private SurfaceHolder mSurfaceHolder;

    public void onClick(View arg0) {
        int i = 0;
        while (i < 4) {

            mCamera.takePicture(null, mPictureCallback, mPictureCallback);
            i = i + 1;
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    public static Object StoreByteImage(byte[] imageData) {

        Object obj = null;
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(imageData);
            ObjectInputStream objImageData = new ObjectInputStream(bis);
            obj = objImageData.readObject();

        } catch (IOException ex) {
            // TODO: Handle the exception
        } catch (ClassNotFoundException ex) {
            // TODO: Handle the exception
        }
        return obj;

    }

    public static Object createImagesArray(byte[] imageData) {

        Object obj = null;
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(imageData);
            ObjectInputStream objImageData = new ObjectInputStream(bis);
            obj = objImageData.readObject();

        } catch (IOException ex) {
            // TODO: Handle the exception
        } catch (ClassNotFoundException ex) {
            // TODO: Handle the exception
        }
        return obj;

    }

    public static boolean SaveImage(List<Object> listImage) {

        FileOutputStream outStream = null;
        Log.d("ListSize: ", Integer.toString(listImage.size()));
        Iterator<Object> itList = listImage.iterator();
        byte[] imageBytes = null;
        while (itList.hasNext()) {

            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            try {
                ObjectOutputStream oos = new ObjectOutputStream(bos);
                oos.writeObject(listImage);
                oos.flush();
                oos.close();
                bos.close();
                imageBytes = bos.toByteArray();
            } catch (IOException ex) {
                // TODO: Handle the exception
            }

            try {
                // Write to SD Card
                outStream = new FileOutputStream(String.format(
                        "/sdcard/DCIM/%d.jpg", System.currentTimeMillis())); // <9>
                outStream.write(imageBytes);
                outStream.close();

            } catch (FileNotFoundException e) { // <10>
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
            }
        }
        Log.d(TAG, "onPictureTaken - jpeg");

        return true;
    }

}

Error from the comments:

ERROR/MemoryHeapBase(1382): error opening /dev/pmem_camera: No such file or directory 
ERROR/QualcommCameraHardware(1382): failed to construct master heap for pmem pool /dev/pmem_camera 
ERROR/QualcommCameraHardware(1382): initRaw X failed with pmem_camera, trying with pmem_adsp
Nanne
  • 64,065
  • 16
  • 119
  • 163
droidfish
  • 301
  • 4
  • 15
  • Please specify "crashes" with an actual description and a stacktrace? Should be usefull... – Nanne Jul 31 '11 at 11:50
  • Yes of course:) actually these lines are red 07-31 14:12:32.801: ERROR/MemoryHeapBase(1382): error opening /dev/pmem_camera: No such file or directory 07-31 14:12:32.801: ERROR/QualcommCameraHardware(1382): failed to construct master heap for pmem pool /dev/pmem_camera 07-31 14:12:32.801: ERROR/QualcommCameraHardware(1382): initRaw X failed with pmem_camera, trying with pmem_adsp – droidfish Jul 31 '11 at 12:15
  • Could it be that you are running out of mem? You have to take into account the limited heapspace you have.... – Nanne Jul 31 '11 at 12:19
  • Well chrash means it just hang up my phone, so i have to remove the battery. – droidfish Jul 31 '11 at 12:19
  • My phone has 200MB Ram free out of 512. So 4 Pics in the size of 1024x768 should be a problem – droidfish Jul 31 '11 at 12:21
  • No, heapspace. You only have 16 mb of that available. see http://jindroid.com/2010/10/11/max-heap-size-for-an-android-application/ for a random link. You say you have them in an array first, and save them later, right? then you should take into account you have bitmaps (if they're not compressed yet) and they are in memory (not RAM, but HEAP). – Nanne Jul 31 '11 at 12:23
  • ok. So i should compress the picture straight after its taken and then write it to the array? – droidfish Jul 31 '11 at 12:27
  • Can you please post finally how did this work? – COD3BOY Apr 19 '14 at 06:35
  • How did you do that? – Anton Shkurenko Dec 08 '15 at 09:39

2 Answers2

17

AFAIK, you cannot take another picture until the first one is complete. Eliminate your for loop and Thread.sleep(). Take the next picture in mPictureCallback.

The main idea is to shoot a picture every 0,3sec and to store the pictures in an Array until the last picture is taken.

The "0,3sec" objective is faster than most devices can process an image, and you may not have enough heap space for your array of images. I suspect that you will have to write each image out to disk as it comes in via an AsyncTask, so you can free up the heap space while also not tying up the main application thread.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Here is this app (https://play.google.com/store/apps/details?id=com.spritefish.fastburstcameralite), that can take a picture every .1 or less time without any delay. I am curious if you can guess what method this app might be using to achieve this burst mode. I already know about the preview callback. But am also aware that it produces low quality images. – Salman Khakwani Mar 22 '14 at 12:05
  • 5
    @MrSMAK: Since they make the statement "due to the high speed, pictures are not taken in the highest resolution possible", it would not surprise me in the least if they are using the preview callback. In fact, the statement "Zero shutter lag - pictures are taken as soon as shutter button is pressed" almost assures that they are using the preview callback, as they have no control over any hardware-imposed lag when you call `takePicture()`. – CommonsWare Mar 22 '14 at 12:14
  • Thanks for your response :-) Now i am clear about the Burst Camera approach. – Salman Khakwani Mar 24 '14 at 04:23
  • @CommonsWare can you give an example, how to burst images? – Anton Shkurenko Dec 01 '15 at 12:42
1

You can try capturing subsequent preview frames in preallocated byte arrays. See the following methods:

http://developer.android.com/reference/android/hardware/Camera.html#addCallbackBuffer(byte[]) http://developer.android.com/reference/android/hardware/Camera.html#setPreviewCallbackWithBuffer(android.hardware.Camera.PreviewCallback)

...or http://developer.android.com/reference/android/hardware/Camera.html#setOneShotPreviewCallback(android.hardware.Camera.PreviewCallback) for better control over timing.

LXE
  • 11
  • 3