Question

I am trying to build an Android camera & camcorder, and have successful with getting the camera to work.

However, I am having issues with the camcorder, that when I initiate a recording, the app crashes and the phone needs to be rebooted via removing the battery (Samsung Galaxy S II). I've used numerous pieces of sample code, and been searching for a solution to a potentially simple problem!

The aim of this app is to record video for 60 seconds, and then capture 5 images. This process should be initiated by a single button, and loop until the button is pressed again, or the phone runs out of power. I am yet to fully implement this functionality!

Any help would be appreciated!

--- Manifest ---

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.metadev.spacecamx"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
    android:minSdkVersion="10"
    android:targetSdkVersion="18" />

<!-- Include Camera Properties -->
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

<!-- Include Camera Permissions -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity
        android:name="com.metadev.spacecamx.MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

</manifest>

--- Layout ---

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/camera_preview" >

<ImageButton
    android:id="@+id/button_capture"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_centerHorizontal="true"
    android:contentDescription="@null"
    android:background="@drawable/ic_action_camera" />

</RelativeLayout>

--- CameraPreview Class ---

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

private static final String TAG = "CameraPreview";

private SurfaceHolder mHolder;
private Camera mCamera;

Parameters paras;

public CameraPreview(Context context, Camera camera) {
    super(context);
    mCamera = camera;

    //SurfaceHolder.Callback is implemented to provide notification 
    //of creation and destruction of underlying surface.
    mHolder = getHolder();
    mHolder.addCallback(this);
}
//Method to draw camera preview surface.
public void surfaceCreated(SurfaceHolder holder) {
    try {
        mCamera.setPreviewDisplay(holder);
        mCamera.startPreview();
    } catch (IOException e) {
        Log.d(TAG, "Camera Preview Creation Failed: " + e.getMessage());
    }
}

//Method to release camera preview surface.
public void surfaceDestroyed(SurfaceHolder holder) {
    //destroyed
}

//Method to handle changes in orientation of view.
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) {
        //Attempt to kill non-existent preview.
    }

    try {
        mCamera.setPreviewDisplay(mHolder);
        mCamera.startPreview();
    } catch (Exception e) {
        Log.d(TAG, "Camera Preview Creation Failed: " + e.getMessage());
    }
}

}

--- MainActivity ---

public class MainActivity extends Activity {

private static final String TAG = "CameraRecorderActivity";
public static final int MEDIA_TYPE_IMAGE = 1;
public static final int MEDIA_TYPE_VIDEO = 2;
private int pictureCount = 0;
private boolean isRecording = false;

private Camera mCamera;
private CameraPreview mPreview;
private ImageButton mSnapButton;
private MediaRecorder mMediaRecorder;

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

    View decorView = getWindow().getDecorView();
    //Hide the status bar.
    int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
    decorView.setSystemUiVisibility(uiOptions);
    //Hide the action bar.
    ActionBar actionBar = getActionBar();
    actionBar.hide();

    Boolean hasCamera = checkCameraHardware(getApplicationContext());
    if(!hasCamera) {
        Toast.makeText(getApplicationContext(), "Camera is unavailable", Toast.LENGTH_LONG).show();
    } else {
        //Create camera instance
        mCamera = getCameraInstance();

        mPreview = new CameraPreview(this, mCamera);
        RelativeLayout preview = (RelativeLayout) findViewById(R.id.camera_preview);
        preview.addView(mPreview);

        mSnapButton = (ImageButton) findViewById(R.id.button_capture);
        mSnapButton.bringToFront();

        //Image capture button
        mSnapButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick (View v) {
                if (isRecording) {
                    // stop recording and release camera
                    mMediaRecorder.stop();  // stop the recording
                    releaseMediaRecorder(); // release the MediaRecorder object
                    mCamera.lock();         // take camera access back from MediaRecorder

                    // inform the user that recording has stopped
                    isRecording = false;
                } else {
                    // initialize video camera
                    if (prepareVideoRecorder()) {
                        // Camera is available and unlocked, MediaRecorder is prepared,
                        // now you can start recording
                        mMediaRecorder.start();

                        // inform the user that recording has started
                        isRecording = true;
                    } else {
                        // prepare didn't work, release the camera
                        releaseMediaRecorder();
                        // inform user
                    }
                }
                //mCamera.takePicture(null, null, mPicture);
            }
        });
    }
}

//Method to check if device has a camera
private boolean checkCameraHardware(Context context) {
    if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
        //Device has a camera
        return true;
    } else {
    //Device does not have a camera.
        return false;
    }
}

private Camera getCameraInstance() {
    Camera c = null;
    try {
        //Attempt to get camera
        c = Camera.open();
    } catch (Exception e) {
        //Camera unavailable
    }
    return c;
}

private PictureCallback mPicture = new PictureCallback () {

    @Override
    public void onPictureTaken(byte[] data, Camera camera) {
            File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
            if (pictureFile == null) {
                Log.d(TAG, "Media Creation Error. 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, "Cannot Access File: " + e.getMessage());
            } finally {
                if (pictureCount < 4) {
                    mCamera.startPreview();
                    mCamera.takePicture(null, null, mPicture);
                    pictureCount++; 
                } else {
                    mCamera.startPreview();
                }
            }
    }
};

private boolean prepareVideoRecorder(){

    mMediaRecorder = new MediaRecorder();
    // Step 1: Unlock and set camera to MediaRecorder
    mCamera.unlock();
    mMediaRecorder.setCamera(mCamera);

    // Step 2: Set sources
    mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
    mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

    // Step 3: Set a CamcorderProfile (requires API Level 8 or higher)
    mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));

    // Step 4: Set output file
    mMediaRecorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString());

    // Step 5: Set the preview output
    mMediaRecorder.setPreviewDisplay(mPreview.getHolder().getSurface());

    // Step 6: Prepare configured MediaRecorder
    try {
        mMediaRecorder.prepare();
    } catch (IllegalStateException e) {
        Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage());
        releaseMediaRecorder();
        return false;
    } catch (IOException e) {
        Log.d(TAG, "IOException preparing MediaRecorder: " + e.getMessage());
        releaseMediaRecorder();
        return false;
    }
    return true;
}

private static File getOutputMediaFile(int type) {
    File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "SpaceCam");

    if(!mediaStorageDir.exists()) {
        if(!mediaStorageDir.mkdirs()) {
            Log.d("SpaceCam", "Failed to create directory.");
            return null;
        }
    }

    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.UK).format(new Date());
    File mediaFile;
    if (type == MEDIA_TYPE_IMAGE) {
        mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");
    } else  if (type == MEDIA_TYPE_VIDEO) {
        mediaFile = new File(mediaStorageDir.getPath() + File.separator + "VID_" + timeStamp + ".mp4");
    } else {
        return null;
        }
    return mediaFile;
}

@Override
protected void onPause() {
    super.onPause();
    if (mCamera != null) {
        mCamera.setPreviewCallback(null);
        mPreview.getHolder().removeCallback(mPreview);
        releaseCamera();
        RelativeLayout preview = (RelativeLayout) findViewById(R.id.camera_preview);
        preview.removeView(mPreview);
        mPreview = null;
    }
}

@Override
protected void onResume() {
    super.onResume();
    try {
        mCamera = Camera.open(0);
        mCamera.setPreviewCallback(null);
        mPreview = new CameraPreview(MainActivity.this, mCamera);
        RelativeLayout preview = (RelativeLayout) findViewById(R.id.camera_preview);
        preview.addView(mPreview);
        mSnapButton = (ImageButton) findViewById(R.id.button_capture);
        mSnapButton.bringToFront();
    } catch (Exception e) {
        Log.d(TAG, "Error initiating preview: " + e.getMessage());
    }
}

private void releaseMediaRecorder(){
    if (mMediaRecorder != null) {
        mMediaRecorder.reset();   // clear recorder configuration
        mMediaRecorder.release(); // release the recorder object
        mMediaRecorder = null;
        mCamera.lock();           // lock camera for later use
    }
}

private void releaseCamera () {
    if (mCamera != null) {
        mCamera.release();
        mCamera = null;
    }
}
}

--- LogCat Output ---

02-09 17:59:21.345: D/dalvikvm(6773): Late-enabling CheckJNI
02-09 17:59:21.720: I/Camera(6773): sendBroadcast intent.stop.app-in-app
02-09 17:59:22.020: I/Camera(6773): sendBroadcast intent.stop.app-in-app
02-09 17:59:22.020: D/CameraRecorderActivity(6773): Error initiating preview: Fail to connect to camera service
02-09 17:59:22.110: D/libEGL(6773): loaded /system/lib/egl/libEGL_mali.so
02-09 17:59:22.135: D/libEGL(6773): loaded /system/lib/egl/libGLESv1_CM_mali.so
02-09 17:59:22.150: D/libEGL(6773): loaded /system/lib/egl/libGLESv2_mali.so
02-09 17:59:22.160: D/(6773): Device driver API match
02-09 17:59:22.160: D/(6773): Device driver API version: 10
02-09 17:59:22.160: D/(6773): User space API version: 10 
02-09 17:59:22.160: D/(6773): mali: REVISION=Linux-r2p4-02rel0 BUILD_DATE=Thu Oct 25 08:43:05 KST 2012 
02-09 17:59:22.195: D/OpenGLRenderer(6773): Enabling debug mode 0
02-09 17:59:22.880: I/Choreographer(6773): Skipped 49 frames!  The application may be doing too much work on its main thread.
02-09 17:59:24.030: W/SurfaceView(6773): CHECK surface infomation creating=false formatChanged=false sizeChanged=false visible=false visibleChanged=true surfaceChanged=true realSizeChanged=false redrawNeeded=false left=false top=false
02-09 17:59:24.150: I/Camera(6773): sendBroadcast intent.stop.app-in-app
02-09 17:59:33.925: W/SurfaceView(6773): CHECK surface infomation creating=false formatChanged=false sizeChanged=false visible=false visibleChanged=true surfaceChanged=true realSizeChanged=false redrawNeeded=true left=false top=false
02-09 17:59:33.995: I/Camera(6773): sendBroadcast intent.stop.app-in-app
02-09 17:59:34.205: I/Camera(6773): sendBroadcast intent.stop.app-in-app
02-09 17:59:34.210: D/CameraRecorderActivity(6773): Error initiating preview: Fail to connect to camera service
02-09 17:59:34.985: I/Choreographer(6773): Skipped 45 frames!  The application may be doing too much work on its main thread.
02-09 17:59:35.125: E/SpannableStringBuilder(6773): SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length
02-09 17:59:35.125: E/SpannableStringBuilder(6773): SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length
02-09 17:59:40.625: I/MediaRecorderJNI(6773): prepare: surface=0x4f8104f0 (identity=37) 

Thank you ever so much for any help!!

--- Updated LogCat with mCamera.lock() removed ---

02-09 22:46:53.493: D/dalvikvm(6686): Late-enabling CheckJNI
02-09 22:46:53.513: E/jdwp(6686): Failed sending reply to debugger: Broken pipe
02-09 22:46:53.513: D/dalvikvm(6686): Debugger has detached; object registry had 1 entries
02-09 22:46:53.738: I/Camera(6686): sendBroadcast intent.stop.app-in-app
02-09 22:46:54.113: I/Camera(6686): sendBroadcast intent.stop.app-in-app
02-09 22:46:54.118: D/CameraRecorderActivity(6686): Error initiating preview: Fail to connect to camera service
02-09 22:46:54.218: D/libEGL(6686): loaded /system/lib/egl/libEGL_mali.so
02-09 22:46:54.218: D/libEGL(6686): loaded /system/lib/egl/libGLESv1_CM_mali.so
02-09 22:46:54.238: D/libEGL(6686): loaded /system/lib/egl/libGLESv2_mali.so
02-09 22:46:54.243: D/(6686): Device driver API match
02-09 22:46:54.243: D/(6686): Device driver API version: 10
02-09 22:46:54.243: D/(6686): User space API version: 10 
02-09 22:46:54.243: D/(6686): mali: REVISION=Linux-r2p4-02rel0 BUILD_DATE=Thu Oct 25 08:43:05 KST 2012 
02-09 22:46:54.278: D/OpenGLRenderer(6686): Enabling debug mode 0
02-09 22:46:54.943: I/Choreographer(6686): Skipped 47 frames!  The application may be doing too much work on its main thread.
02-09 22:46:56.318: W/SurfaceView(6686): CHECK surface infomation creating=false formatChanged=false sizeChanged=false visible=false visibleChanged=true surfaceChanged=true realSizeChanged=false redrawNeeded=false left=false top=false
02-09 22:46:56.403: I/Camera(6686): sendBroadcast intent.stop.app-in-app
02-09 22:46:59.818: W/SurfaceView(6686): CHECK surface infomation creating=false formatChanged=false sizeChanged=false visible=false visibleChanged=true surfaceChanged=true realSizeChanged=false redrawNeeded=true left=false top=false
02-09 22:47:00.063: I/Camera(6686): sendBroadcast intent.stop.app-in-app
02-09 22:47:04.833: I/MediaRecorderJNI(6686): prepare: surface=0x4d6db008 (identity=16)
Was it helpful?

Solution 2

Ahhhh simple fix in the end! I had to release the camera before starting the MediaRecorder!! Works smoothly now! Time to make it look pretty :)

Thanks for all the help with this!

OTHER TIPS

I have a feeling the problem is when you are calling mCamera.lock();

Try removing is here:

private void releaseMediaRecorder(){
if (mMediaRecorder != null) {
    mMediaRecorder.reset();   // clear recorder configuration
    mMediaRecorder.release(); // release the recorder object
    mMediaRecorder = null;
    mCamera.lock();           // ***REMOVE THIS LINE***
}

and here too:

 //Image capture button
    mSnapButton.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick (View v) {
            if (isRecording) {
                // stop recording and release camera
                mMediaRecorder.stop();  // stop the recording
                releaseMediaRecorder(); // release the MediaRecorder object
                mCamera.lock();         // ***REMOVE THIS LINE***

Let me know if this helped.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top