davidh5
davidh5

Reputation: 1

Android - taking pictures every minute with Camera

I have a problem to solve - I was trying everything and nothing is working. I am writing an application which should take picture every minute (period between pictures might be longer in future, but that isn't important) and upload it to ftp server. I am trying to open camera only for a while before taking photo and after taking photo release it and stop preview. But that doesn't work - there isn't even one picture taken. Opening camera etc. is doing by handler. If I put in handler only takePicture method - app works fine (and open camera once and keep it open in onCreate method), but I want to release camera and open it before taking picture to save battery. I attach my source code and logcat.

MainActivity.java

public class MainActivity extends ActionBarActivity {

private Camera mCamera;
private CameraPreview mPreview;
private File pictureFile;
private File mediaStorageDir;
private Context context;
private Handler handler;

public Camera.PictureCallback mPicture = new Camera.PictureCallback() {

    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        pictureFile = getOutputMediaFile();
        if (pictureFile == null) {
            Log.d("TAG", "Error creating media file, check storage permissions: ");
            return;
        }
        try {
            FileOutputStream fos = new FileOutputStream(pictureFile);
            fos.write(data);
            fos.close();
        } catch (FileNotFoundException e) {
            Log.d("TAG", "File not found: " + e.getMessage());
        } catch (IOException e) {
            Log.d("TAG", "Error accessing file: " + e.getMessage());
        }
        Log.d("ftp", "photo taken: " + pictureFile);
        mCamera.release();
        startFTP();
    }
};

private Runnable runnable = new Runnable() {
    @Override
    public void run() {
        try {
            mCamera = Camera.open();
        } catch (Exception e) {
        }
        Camera.Parameters params = mCamera.getParameters();
        params.setPictureSize(1920, 1080);
        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
            params.set("orientation", "portrait");
            params.set("rotation", 90);
        }
        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
            params.set("orientation", "landscape");
        }
        mCamera.setParameters(params);
        mPreview = new CameraPreview(context, mCamera);
        FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
        preview.addView(mPreview);
        mCamera.startPreview();
        mCamera.takePicture(null, null, mPicture);
        try {
            this.wait(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        handler.postDelayed(this, 60000);
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    context = getApplicationContext();
    handler = new Handler();
    Button captureButton = (Button) findViewById(R.id.button_capture);
    captureButton.setOnClickListener(
            new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    handler.post(runnable);
                }
            }
    );
}

public void startFTP()  {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d("ftp", "photo uploading started: " + pictureFile);
                String sendingPictureName = pictureFile.getName();
                String serverAddress = "1111111";
                String userId = "111111";
                String password = "111111";
                FTPClient ftp = new FTPClient();
                try {
                    ftp.connect(serverAddress);
                    ftp.login(userId, password);
                    int reply = ftp.getReplyCode();
                    if (!FTPReply.isPositiveCompletion(reply)) {
                        ftp.disconnect();
                    }
                    ftp.setFileType(FTP.BINARY_FILE_TYPE);
                    ftp.enterLocalPassiveMode();
                    ftp.changeWorkingDirectory("/111111");
                    InputStream input;
                    input = new FileInputStream(pictureFile);
                    if (ftp.storeFile(pictureFile.getName(), input)) {
                        File fileToDelete = new File(mediaStorageDir.getPath() +"/"+ sendingPictureName);
                        //Log.d("ftp", "sciezka do usuwanego pliku: " + fileToDelete.getAbsolutePath());
                        fileToDelete.delete();
                    }
                    input.close();
                    Log.d("ftp", "photo uploading ended: " + sendingPictureName);
                    ftp.logout();
                    ftp.disconnect();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();
    //noinspection SimplifiableIfStatement
    if (id == R.id.action_settings) {
        return true;
    }
    return super.onOptionsItemSelected(item);
}

private File getOutputMediaFile() {
    mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), "MyCameraApp");
    if (!mediaStorageDir.exists()) {
        if (!mediaStorageDir.mkdirs()) {
            Log.d("MyCameraApp", "failed to create directory");
            return null;
        }
    }
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    File mediaFile;
    mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_"+ timeStamp + ".jpg");
    return mediaFile;
}


public void cancelTimer(View view) {
    handler.removeCallbacks(runnable);
}
}

CameraPreview.java

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;

public CameraPreview(Context context, Camera camera) {
    super(context);
    mCamera = camera;
    mHolder = getHolder();
    mHolder.addCallback(this);
    mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

public void surfaceCreated(SurfaceHolder holder) {
    try {
        mCamera.setPreviewDisplay(holder);
        mCamera.setDisplayOrientation(90);
        mCamera.startPreview();
    } catch (IOException e) {
        Log.d("TAG", "Error setting camera preview: " + e.getMessage());
    }
}

public void surfaceDestroyed(SurfaceHolder holder) {
}

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    if (mHolder.getSurface() == null){
        // preview surface does not exist
        return;
    }
    try {
        mCamera.stopPreview();
    } catch (Exception e){
    }
    try {
        mCamera.setPreviewDisplay(mHolder);
        mCamera.startPreview();
    } catch (Exception e){
        Log.d("TAG", "Error starting camera preview: " + e.getMessage());
    }
}
}

activity_main.xml - it's layout only for testing and it isn't perfect

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<FrameLayout
    android:id="@+id/camera_preview"
    android:layout_width="500px"
    android:layout_height="500px"
    android:layout_weight="0.1"
    android:layout_above="@+id/button_capture" />

<Button
    android:id="@+id/button_capture"
    android:text="start timer"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:layout_weight="1"
    android:layout_alignParentEnd="false"
    android:layout_alignParentStart="false"
    android:layout_centerVertical="true" />

<Button
    android:id="@+id/timer_button"
    android:text="stop timer"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:layout_weight="1"
    android:layout_below="@+id/button_capture"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="false"
    android:onClick="cancelTimer" />

Logcat:

08-24 09:42:42.828    8935-8935/? E/Zygote﹕ MountEmulatedStorage()
08-24 09:42:42.828    8935-8935/? I/libpersona﹕ KNOX_SDCARD checking this for 10185
08-24 09:42:42.828    8935-8935/? E/Zygote﹕ v2
08-24 09:42:42.828    8935-8935/? I/libpersona﹕ KNOX_SDCARD not a persona
08-24 09:42:42.828    8935-8935/? I/SELinux﹕ Function: selinux_compare_spd_ram , priority [2] , priority version is VE=SEPF_SM-A500FU_5.0.2_0023
08-24 09:42:42.838    8935-8935/? E/SELinux﹕ [DEBUG] get_category: variable seinfo: default sensitivity: NULL, cateogry: NULL
08-24 09:42:42.838    8935-8935/? I/art﹕ Late-enabling -Xcheck:jni
08-24 09:42:42.878    8935-8935/? D/TimaKeyStoreProvider﹕ TimaSignature is unavailable
08-24 09:42:42.878    8935-8935/? D/ActivityThread﹕ Added TimaKeyStore provider
08-24 09:42:42.988    8935-8935/com.example.dawid.camerabezpodgladu D/PhoneWindow﹕ *FMB* installDecor mIsFloating : false
08-24 09:42:42.988    8935-8935/com.example.dawid.camerabezpodgladu D/PhoneWindow﹕ *FMB* installDecor flags : -2139029248
08-24 09:42:43.108    8935-8951/com.example.dawid.camerabezpodgladu D/OpenGLRenderer﹕ Render dirty regions requested: true
08-24 09:42:43.118    8935-8935/com.example.dawid.camerabezpodgladu D/PhoneWindow﹕ *FMB* isFloatingMenuEnabled mFloatingMenuBtn : null
08-24 09:42:43.118    8935-8935/com.example.dawid.camerabezpodgladu D/PhoneWindow﹕ *FMB* isFloatingMenuEnabled return false
08-24 09:42:43.138    8935-8935/com.example.dawid.camerabezpodgladu D/SRIB_DCS﹕ log_dcs ThreadedRenderer::initialize entered!
08-24 09:42:43.138    8935-8951/com.example.dawid.camerabezpodgladu I/Adreno-EGL﹕ <qeglDrvAPI_eglInitialize:379>: EGL 1.4 QUALCOMM build: SKARAJGA_AU_LINUX_ANDROID_LA.BR.1.1.2_RB1.05.00.02.031.018+PATCH[ES]_msm8916_32_refs/tags/AU_LINUX_ANDROID_LA.BR.1.1.2_RB1.05.00.02.031.018__release_ENGG (I856e09677e)
OpenGL ES Shader Compiler Version: E031.25.03.02
Build Date: 04/06/15 Mon
Local Branch:
Remote Branch: refs/tags/AU_LINUX_ANDROID_LA.BR.1.1.2_RB1.05.00.02.031.018
Local Patches: 112c106f3772623daa7b4181c6cf23491044ead1 Revert "Disable ASTC on A405"
58a118cb818fdc906095a49a90977c15f9d3b223 Remove ASTC
08-24 09:42:43.138    8935-8951/com.example.dawid.camerabezpodgladu I/OpenGLRenderer﹕ Initialized EGL, version 1.4
08-24 09:42:43.158    8935-8951/com.example.dawid.camerabezpodgladu D/OpenGLRenderer﹕ Get maximum texture size. GL_MAX_TEXTURE_SIZE is 4096
08-24 09:42:43.158    8935-8951/com.example.dawid.camerabezpodgladu D/OpenGLRenderer﹕ Enabling debug mode 0
08-24 09:42:43.258    8935-8935/com.example.dawid.camerabezpodgladu I/Timeline﹕ Timeline: Activity_idle id: android.os.BinderProxy@3d659231 time:4110726
08-24 09:42:44.648    8935-8935/com.example.dawid.camerabezpodgladu D/ViewRootImpl﹕ ViewPostImeInputStage ACTION_DOWN
08-24 09:42:45.678    8935-8935/com.example.dawid.camerabezpodgladu I/Choreographer﹕ Skipped 34 frames!  The application may be doing too much work on its main thread.
08-24 09:43:45.138    8935-8935/com.example.dawid.camerabezpodgladu W/CameraBase﹕ An error occurred while connecting to camera: 0
08-24 09:43:45.498    8935-8935/com.example.dawid.camerabezpodgladu D/AndroidRuntime﹕ Shutting down VM
08-24 09:43:45.498    8935-8935/com.example.dawid.camerabezpodgladu E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.example.dawid.camerabezpodgladu, PID: 8935
java.lang.RuntimeException: startPreview failed
        at android.hardware.Camera.startPreview(Native Method)
        at com.example.dawid.camerabezpodgladu.CameraPreview.surfaceCreated(CameraPreview.java:31)
        at android.view.SurfaceView.updateWindow(SurfaceView.java:682)
        at android.view.SurfaceView$3.onPreDraw(SurfaceView.java:200)
        at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:921)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2226)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1239)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6752)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:777)
        at android.view.Choreographer.doCallbacks(Choreographer.java:590)
        at android.view.Choreographer.doFrame(Choreographer.java:560)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:763)
        at android.os.Handler.handleCallback(Handler.java:739)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:145)
        at android.app.ActivityThread.main(ActivityThread.java:6145)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)
08-24 09:43:48.818    8935-8935/com.example.dawid.camerabezpodgladu I/Process﹕ Sending signal. PID: 8935 SIG: 9

Even once method takePicture isn't called. Error is getting from bad realesing camera and reopening it after minute (next handler action).

I will be very thankful for even one tip Dawid

Upvotes: 0

Views: 1588

Answers (2)

jdosser
jdosser

Reputation: 163

Finally figured out how to set the phone to take timed pics without any screen display. There were 2 main issues I struggled with. First, I wanted to take the pics without displaying to screen. Along those lines, I found an example where they used : mCamera.setPreviewTexture(new SurfaceTexture(10)); and nothing showed on the screen when using this preview method. It appears that some sort of preview is required. Another method was to set the preview to 1 pixel. This method had examples online which appeared to work as well, but I did not try it.

The second and bigger problem had to do with 'onPictureTaken' method. This method processes your picture after the 'takePicture' call. This is where the first answer (above) was tripping me up. It seemed that no matter what looping method I used, or where in the code the call to 'takePicture' was located, all of the 'onPictureTaken' methods were queued up and called one after another once the parent of the 'takePicture' caller ended.

Although the picture data processed by onPictureTaken were in a proper time sequence, I could see that having several hundred pics stored and waiting to process could cause problems, and a method needed to be found where on pic was processed and stored before the next pic was taken.

Along those lines, I stumbled upon the AlarmManager and coupled that with the BroadcastReceiver and Future classes to solve the problem.

What I've done is set the alarmManger to go off at a set time or time frequency. The BroadcaseReceiver captures this call & in turn calls a method which creates a thread where a 'Future' object makes the call to take a picture.

'Future' object is nice, because it will wait for the physical camera to take the picture (takePicture) and then process it (onPictureTaken). This all occurs in one thread, then terminates. So no queuing of pics to process and each picture sequence is handled separately.

Code is contained below. Note that some of the default 'Overrides' have been left out to save space. Also, the visible screen was basically a button which captured the click event...very basic. MainActivity.java: package myTest.com.test;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class MainActivity extends Activity {
CameraHandler cameraHandler;
public BroadcastReceiver br;
public PendingIntent pi;
public AlarmManager am;

final static private long LOOPTIME = 20000;
private static final ExecutorService threadpool = Executors.newFixedThreadPool(3);

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    setup();
}

private void setup() {
    try{
        cameraHandler = new CameraHandler();
        br = new BroadcastReceiver() {
            @Override
            public void onReceive(Context c, Intent i) {
                //Toast.makeText(c, "Taking a pic!",   Toast.LENGTH_LONG).show();
                TryAThread();
            }
        };
        registerReceiver(br, new IntentFilter("com.timedActivity.activity") );
        pi = PendingIntent.getBroadcast(this, 0, new Intent("com.timedActivity.activity"), 0);
        am = (AlarmManager)(this.getSystemService( Context.ALARM_SERVICE ));
    }
    catch (Exception e){        }
}

private void TryAThread()  {
    try{
        CameraCaller cameraCaller = new CameraCaller(cameraHandler);
        Future future = threadpool.submit(cameraCaller);

        while (!future.isDone()) {
            try {
                Thread.sleep(5000);
            } catch (Exception ex) { }
        }
    }
    catch (Exception e){        }
}

@Override
protected void onDestroy() {
    am.cancel(pi);
    unregisterReceiver(br);
    super.onDestroy();
}

public void onClickListener(View view){
    try{
        am.setRepeating(am.ELAPSED_REALTIME,SystemClock.elapsedRealtime(), LOOPTIME, pi);
    }
    catch (Exception e){        }
}

}

CameraCaller.java:.

package myTest.com.test;
import java.util.concurrent.Callable;

public class CameraCaller implements Callable {
private CameraHandler cameraHandler;
public CameraCaller(CameraHandler ch){
    cameraHandler = ch;
}

@Override
public Object call() throws Exception {
    cameraHandler.takeAPic();
    return true;
}

}

CameraHandler.java:.

package myTest.com.test;

import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.os.Environment;
import android.util.Log;
import junit.runner.Version;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class CameraHandler implements Camera.PictureCallback{
private Camera mCamera;
public  CameraHandler(){
}

  public Boolean takeAPic(){
    try{
        if (mCamera == null){
            mCamera = Camera.open();
            mCamera.enableShutterSound(false);

            try {
                mCamera.setPreviewTexture(new SurfaceTexture(10));
            }
            catch (IOException e1) {Log.e(Version.id(), e1.getMessage()); 
            }
        }
        mCamera.startPreview();
        mCamera.takePicture(null, null, this);
    }
    catch (Exception ex){        }
      return  true;
}

    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        File pictureFile = getOutputMediaFile();
        if (pictureFile == null) {
            return;
        }
        try {
            FileOutputStream fos = new FileOutputStream(pictureFile);
            fos.write(data);
            fos.close();
        } catch (FileNotFoundException e) {

        } catch (IOException e) {            }
        try {
            Thread.sleep(2000);
        }catch (Exception ex){}
    }

public static File getOutputMediaFile() {
    File file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
    File mediaStorageDir = new File(file, "MyCameraApp");
    if (!mediaStorageDir.exists()) {
        if (!mediaStorageDir.mkdirs()) {
            Log.d("MyCameraApp", "failed to create directory");
            return null;
        }
    }
    // Create a media file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    File mediaFile;
    mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");
    return mediaFile;
}

}

Upvotes: 1

jdosser
jdosser

Reputation: 163

Well David, you want 1 tip? I'm working on a similar problem and this may get you a little further down the road...to where I'm currently stuck.

I can take a picture without preview using the following.

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    cameraHandler = new CameraHandler();
    tryThis();
}

private void tryThis(){
    cameraHandler.takeAPic();
}

And the class is: public class CameraHandler implements Camera.PictureCallback{ private Camera mCamera;

public  CameraHandler(){    }

  public void takeAPic(){
    try{
        if (mCamera == null){
            mCamera = Camera.open();
            mCamera.enableShutterSound(false);

            try {
                mCamera.setPreviewTexture(new SurfaceTexture(10));
            }
            catch (IOException e1) {                }
        }
        mCamera.startPreview();
        mCamera.takePicture(null, null, this);
    }
    catch (Exception ex){        }
}

    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
        File pictureFile = getOutputMediaFile();
        if (pictureFile == null) {
            return;
        }
        try {
            FileOutputStream fos = new FileOutputStream(pictureFile);
            fos.write(data);
            fos.close();
        } 
        catch (FileNotFoundException e) {            } 
        catch (IOException e) {            }
    }

The problem I'm running into now is that any code I put in following the 'takePicture' is not causing the 'onPictureTaken' to be called.

However, this will at least get you to a spot where you can take a picture without a preview. Maybe you can figure out how to make multiple calls to snap the pictures

Upvotes: 0

Related Questions