3

I am trying to do a code in an asynctask that takes a picture from the camera and send it to a server over UDP 100 times. However, the PictureCallback isn't called. Can someone please help me?

this is what i tried:

public class MainAsyncTask extends AsyncTask<Void, String, Void> {

protected static final String TAG = null;
public MainActivity mainAct;

public MainAsyncTask(MainActivity mainActivity)
{
    super();
    this.mainAct = mainActivity;
}

@Override
protected Void doInBackground(Void... params) {
    DatagramSocket clientSocket = null;
    InetAddress IPAddress = null;
    try {
        clientSocket = new DatagramSocket();
        IPAddress = InetAddress.getByName("192.168.1.15");
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    byte [] data;
    DatagramPacket sendPacket;

    try {
        for (int i=0; i < 100; i++)
        {
            publishProgress("");
            File file = new File(Environment.getExternalStorageDirectory()+ File.separator +"img.jpg");
            while (!file.exists() || file.length() == 0);
            Bitmap screen = BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()+ File.separator +"img.jpg");
            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
            screen.compress(Bitmap.CompressFormat.JPEG, 15, bytes);
            data = bytes.toByteArray();
            sendPacket = new DatagramPacket(data, data.length, IPAddress, 3107);
            clientSocket.send(sendPacket);
            file.delete();
        }

        clientSocket.close();


    } catch (Exception e) {
        // TODO Auto-generated catch block
        publishProgress(e.getMessage());
    }
    return null;
}

public static void takeSnapShots(MainActivity mainAct)
{
    PictureCallback jpegCallback = new PictureCallback() {
        public void onPictureTaken(byte[] data, Camera camera) 
        {
            FileOutputStream outStream = null;
            try {
                outStream = new FileOutputStream(Environment.getExternalStorageDirectory()+File.separator+"img"+".jpg");    
                outStream.write(data);
                outStream.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally 
            {
                camera.stopPreview();
                camera.release();
                camera = null;
            }
            Log.d(TAG, "onPictureTaken - jpeg");
        }
        };
    SurfaceView surface = new SurfaceView(mainAct.getApplicationContext());
    Camera camera = Camera.open();
    try {
        camera.setPreviewDisplay(surface.getHolder());
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    camera.startPreview();
    camera.takePicture(null,null,jpegCallback);
}


protected void onProgressUpdate(String... progress) {
    takeSnapShots(mainAct);
}

@Override
protected void onPostExecute(Void result)
{
}

}

Yoav Feuerstein
  • 1,925
  • 2
  • 22
  • 53
eyal
  • 31
  • 2

3 Answers3

1

I don't think that AsyncTask is the most convenient tool to do the job.

You need a SurfaceView that is not simply created out of nowhere, but connected to the screen. You should initialize your camera only once, and you cannot call camera.takePicture() in a loop. You can call takePicture() from onPictureTaken() callback, but you should also remember that you cannot work with sockets from the UI thread. Luckily, you can follow the Google recommendations.

the recommended way to access the camera is to open Camera on a separate thread.

and

Callbacks will be invoked on the event thread open(int) was called from.

If you open camera in a new HandlerThread, as shown here, the picture callbacks will arrive on that beckground thread, which may be used also for networking.

Also, I recommend you to send directly the JPEG buffer that you receive from the camera. I believe that overhead of saving image to file, reading the file to bitmap, and compressing the latter to another JPEG may be way too much. To control the image size, choose appropriate picture size. Note that the size should be selected from the list of sizes supported by the specific camera.

public class CameraView extends SurfaceView 
    implements SurfaceHolder.Callback, Camera.PictureCallback {

    private static final String TAG = "CameraView";

    private Camera camera;
    private HandlerThread cameraThread;
    private Handler handler;
    private boolean bCameraInitialized = false;
    private int picturesToTake = 0;

    public CameraView(Context context, AttributeSet attr) {
        super(context, attr);

        // install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        getHolder().addCallback(this);
    }

    @Override public void surfaceCreated(SurfaceHolder holder) {
        cameraThread = new HandlerThread("CameraHandlerThread");
        cameraThread.start();
        handler = new Handler(cameraThread.getLooper());
        hanlder.post(new Runnable() {
            @Override public void run() {
                openRearCamera();
                bCameraInitialized = false;
            }
        });
    }

    @Override public void surfaceDestroyed(SurfaceHolder holder) {
        if (camera != null) {
            Log.d(TAG, "Camera release");
            camera.release();
            camera = null;
            bCameraInitialized = false;
        }
    }

    // finalize the camera init now that we know preview size
    @Override public void surfaceChanged(SurfaceHolder holder, int format, final int w, final int h) {
        Log.w(TAG, "surfaceChanged(" + w + ", " + h + ")");
        if (!bCameraInitialized) {
            cameraSetup(w, h);
            bCameraInitialized = true;
        }
    }

    private void openRearCamera() {
        if (camera != null) {
            Log.e(TAG, "openRearCamera(): camera is not null");
            return;
        }
        try {
            camera = Camera.open(0);
            Log.d(TAG, "Camera ready " + String.valueOf(camera));
        }
        catch (Throwable e) {
            Log.e(TAG, "openRearCamera(): Camera.open() failed", e);
        }
    }

    private void cameraSetup(int w, int h) {

        if (camera == null) {
            Log.e(TAG, "cameraSetup(): camera is null");
            return;
        }

        Log.d(TAG, "Camera setup");
        try {
            Camera.Parameters params = camera.getParameters();

            // still picture settings - be close to preview size
            Camera.Size pictureSize = params.getSupportedPictureSizes()[0];
            params.setPictureSize(pictureSize.width, optimalPictureSize.height);

            camera.setParameters(params);
            camera.setPreviewDisplay(getHolder());
            camera.startPreview();
        }
        catch (Throwable e) {
            Log.e(TAG, "Failed to finalize camera setup", e);
        }
    }

    private void sendJpeg(byte[] data) {
        DatagramSocket clientSocket = null;
        InetAddress IPAddress = null;
        try {
            clientSocket = new DatagramSocket();
            IPAddress = InetAddress.getByName("192.168.1.15");
        }
        catch (Exception e) {
            Log.e(TAG, "failed to initialize client socket", e);
        }
        DatagramPacket sendPacket;
        sendPacket = new DatagramPacket(data, data.length, IPAddress, 3107);
        clientSocket.send(sendPacket);
        Log.d(TAG, "sent image");
    }

    @Override public void onPictureTaken(byte[] data, Camera camera) {
        sendJpeg(data);
        camera.startPreview();
        takePictures(picturesToTake-1);
    }

    public void takePictures(int n) {
        if (n > 0) {
            picturesToTake = n;
            Log.d(TAG, "take " + n + " images");
            camera.takePicture(null, null, this);
        }
        else {
            Log.d(TAG, "all images captured");
        }
    }
}

The class above is a compilation from several projects, with error checking reduced to minimum for brevity. It may require some fixes to compile. You simply add a <CameraView /> to your activity layout, and call its takePictures when the user clicks a button or something.

Community
  • 1
  • 1
Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
0

Do you call to your AsyncTask like this? Just to create the AsyncTask is not enouge.

new MainAsyncTask(ActivityContext).execute();
yshahak
  • 4,996
  • 1
  • 31
  • 37
0

You can't do this

camera.setPreviewDisplay(surface.getHolder());

From the docs:

http://developer.android.com/reference/android/hardware/Camera.html#setPreviewDisplay(android.view.SurfaceHolder)

"The SurfaceHolder must already contain a surface when this method is called. If you are using SurfaceView, you will need to register a SurfaceHolder.Callback with addCallback(SurfaceHolder.Callback) and wait for surfaceCreated(SurfaceHolder) before calling setPreviewDisplay() or starting preview."

You'd have to do something like this:

SurfaceHolder surfaceHolder = surface.getHolder();
surfaceHolder.addCallback(new Callback() {
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            try {
                camera.setPreviewDisplay(holder);
                camera.startPreview();
                camera.takePicture(null,null,jpegCallback);
            } catch (IOException e) {
            }
        }
        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {}
    }
);
ci_
  • 8,594
  • 10
  • 39
  • 63