How to build a custom camera recorder app?

混江龙づ霸主 提交于 2019-12-22 13:06:14

问题


I am trying to develop a custom camera video recorder. When my devices execute the start MediaRecorder method in beginRecording() in the Activity, the app crashes. I don't know what is wrong, because I follow the Google API Guide. My devices use Android 2.3 or higher version. In my Nexus S 4.1.1 executes ok, but in my Xperia 2.3.3 doesn't go, displaying this trace.

My code is:

private static final String TAG = "RecordVideo";
private MediaRecorder mRecorder = null;
private Camera mCamera = null;
private String OUTPUT_FILE;
private VideoView mVideoView = null;
private Button mStartBtn = null;
private SurfaceHolder mHolder;
private boolean isRecording = false;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    if (checkCameraHardware(this)) {
        mStartBtn = (Button) findViewById(R.id.beginBtn);

        mVideoView = (VideoView)this.findViewById(R.id.videoView);

     // Create our Preview view and set it as the content of our activity.
        mHolder = mVideoView.getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }
}

/** Check if this device has a camera */
private boolean checkCameraHardware(Context context) {
    if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
        // this device has a camera
        return true;
    } else {
        // no camera on this device
        return false;
    }
}

public void doClick(View view) {
    switch(view.getId()) {
    case R.id.beginBtn:
        beginRecording();
        break;
    case R.id.stopBtn:
        stopRecording();
        break;
    case R.id.playRecordingBtn:
        playRecording();
        break;
    case R.id.stopPlayingRecordingBtn:
        stopPlayingRecording();
        break;
    }
}

// Once we have a surface, we can start the previewing
// Once we're previewing with Camera, we can setup the
// MediaRecorder
@Override
public void surfaceCreated(SurfaceHolder holder) {
    Log.v(TAG, "surface created");
    mStartBtn.setEnabled(true);
    try {
        mCamera.setPreviewDisplay(holder);
        mCamera.startPreview();
        prepareForRecording(holder);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
        int height) {
    Log.v(TAG, "Surface changed: width x Height = " + width + "x" + height);
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    Log.v(TAG, "surface destroyed");
}

private void beginRecording() {
    mRecorder.setOnInfoListener(this);
    mRecorder.setOnErrorListener(this);
    mRecorder.start();
    isRecording = true;
}

private void stopRecording() {
    Log.v(TAG, "stop recording");
    if (mRecorder != null) {
        mRecorder.setOnInfoListener(null);
        mRecorder.setOnErrorListener(null);
        mRecorder.stop();
        mRecorder.reset();   // clear recorder configuration
        mRecorder.release(); // release the recorder object
        mRecorder = null;
        mCamera.lock();
    }
    mCamera.release();
    mCamera = null;
    isRecording = false;
}

private void playRecording() {
    MediaController mc = new MediaController(this);
    mVideoView.setMediaController(mc);
    mVideoView.setVideoPath(OUTPUT_FILE);
    mVideoView.start();
}

private void stopPlayingRecording() {
    mVideoView.stopPlayback();
}

@Override
protected void onResume() {
    Log.v(TAG, "resuming");
    mCamera = Camera.open();
    Parameters camParams = mCamera.getParameters();
    //camera.setDisplayOrientation(90);
    // We could set other parameters in camParams then:
    // camera.setParameters(camParams);

    super.onResume();
}

@Override
protected void onPause() {
    Log.v(TAG, "pausing");
    if (mRecorder != null) {
        mRecorder.release();
        mRecorder = null;
    }
    if(mCamera != null) {
        try {
            mCamera.reconnect();  // this also does a lock()
            mCamera.release();
            mCamera = null;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    super.onPause();
}

@Override
protected void onDestroy() {
    Log.v(TAG, "destroying");
    super.onDestroy();
}

private void prepareForRecording(SurfaceHolder holder) {
    if(mRecorder != null) {
        mRecorder.reset();
    }

    try {
        mRecorder = new MediaRecorder();

        // Step 1: Unlock and set camera to MediaRecorder
        mCamera.unlock();
        mRecorder.setCamera(mCamera);  // Must be done while MediaRecorder is idle
    // mCamera.lock();

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

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

        String fileExtension = ".mp4";

        // Step 4: Set output file
        OUTPUT_FILE = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "/videooutput" + fileExtension;
        File outFile = new File(OUTPUT_FILE);
        if(outFile.exists()) {
            outFile.delete();
        }
        mRecorder.setOutputFile(OUTPUT_FILE);

        // Step 5: Set the preview output
        mRecorder.setPreviewDisplay(holder.getSurface());

        //            mRecorder.setMaxDuration(30000); // limit to 30 seconds
                                        // Must implement onInfoListener

        // Step 6: Prepare configured MediaRecorder
        mRecorder.prepare();

    } catch (IllegalStateException e) {
        Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage());
        e.printStackTrace();
    } catch(Exception e) {
        Log.e(TAG, e.toString());
        e.printStackTrace();
    }
}

@Override
public void onInfo(MediaRecorder mediaRecorder, int what, int extra) {
    if(what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
        Log.i(TAG, "got a recording event");
        mCamera.stopPreview();
        Toast.makeText(this, "Recording limit has been reached. Stopping the recording", Toast.LENGTH_SHORT).show();
        isRecording = false;
    }
}

@Override
public void onError(MediaRecorder mr, int what, int extra) {
    Log.e(TAG, "got a recording error");
    mCamera.stopPreview();
    Toast.makeText(this, "Recording error has occurred. Stopping the recording", Toast.LENGTH_SHORT).show();
    isRecording = false;
}

Trace error is:

 V/RecordVideo(7427): resuming
 V/RecordVideo(7427): surface created
 I/MediaRecorderJNI(7427): prepare: surface=0x2b8ac8 (identity=2136)
 V/RecordVideo(7427): Surface changed: width x Height = 600x375
 E/MediaRecorder(7427): start failed: -2147483648
 D/AndroidRuntime(7427): Shutting down VM
 W/dalvikvm(7427): threadid=1: thread exiting with uncaught exception (group=0x2aac8578)
 E/AndroidRuntime(7427): FATAL EXCEPTION: main
 E/AndroidRuntime(7427): java.lang.IllegalStateException: Could not execute method of the activity
 E/AndroidRuntime(7427):    at android.view.View$1.onClick(View.java:2168)
 E/AndroidRuntime(7427):    at android.view.View.performClick(View.java:2552)
 E/AndroidRuntime(7427):    at android.view.View$PerformClick.run(View.java:9229)
 E/AndroidRuntime(7427):    at android.os.Handler.handleCallback(Handler.java:587)
 E/AndroidRuntime(7427):    at android.os.Handler.dispatchMessage(Handler.java:92)
 E/AndroidRuntime(7427):    at android.os.Looper.loop(Looper.java:130)
 E/AndroidRuntime(7427):    at android.app.ActivityThread.main(ActivityThread.java:3701)
 E/AndroidRuntime(7427):    at java.lang.reflect.Method.invokeNative(Native Method)
 E/AndroidRuntime(7427):    at java.lang.reflect.Method.invoke(Method.java:507)
 E/AndroidRuntime(7427):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
 E/AndroidRuntime(7427):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:624)
 E/AndroidRuntime(7427):    at dalvik.system.NativeStart.main(Native Method)
 E/AndroidRuntime(7427): Caused by: java.lang.reflect.InvocationTargetException
 E/AndroidRuntime(7427):    at java.lang.reflect.Method.invokeNative(Native Method)
 E/AndroidRuntime(7427):    at java.lang.reflect.Method.invoke(Method.java:507)
 E/AndroidRuntime(7427):    at android.view.View$1.onClick(View.java:2163)
 E/AndroidRuntime(7427):    ... 11 more
 E/AndroidRuntime(7427): Caused by: java.lang.RuntimeException: start failed.
 E/AndroidRuntime(7427):    at android.media.MediaRecorder.start(Native Method)
 E/AndroidRuntime(7427):    at com.androidbook.record.video.MainActivity.beginRecording(MainActivity.java:124)
 E/AndroidRuntime(7427):    at com.androidbook.record.video.MainActivity.doClick(MainActivity.java:80)
 E/AndroidRuntime(7427):    ... 14 more

UPDATE:

I've fixed this error. The main code is:

 private boolean initCamera() {
    try {
        mCamera  = Camera.open();
        Camera.Parameters camParams = mCamera.getParameters();
        List<Size> a = camParams.getSupportedPreviewSizes();

        for (int i = 0; i < a.size(); i++) {
            if (width_video < a.get(i).width) {
                width_video = a.get(i).width;
                height_video = a.get(i).height;
            }
        }


        mCamera.lock();
        //mCamera.setDisplayOrientation(90);
        // Could also set other parameters here and apply using:
        //mCamera.setParameters(camParams);

        mHolder = mVideoView.getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }
    catch(RuntimeException re) {
        Log.v(TAG, "Could not initialize the Camera");
        re.printStackTrace();
        return false;
    }
    return true;
}

private void initRecorder() {

    if(mRecorder != null) return;

    mOutputFileName = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) +
               "/videooutput" + ".mp4";

    File outFile = new File(mOutputFileName);
    if(outFile.exists()) {
        outFile.delete();
    }

    try {
        mCamera.stopPreview();
        mCamera.unlock();
        mRecorder = new MediaRecorder();
        mRecorder.setCamera(mCamera);

        mRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
        mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);

        if (Build.VERSION_CODES.GINGERBREAD >= Build.VERSION.SDK_INT) {
            mRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
        } else {
            mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
            mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
            mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
            mRecorder.setVideoSize(width_video, height_video);
            mRecorder.setVideoFrameRate(30);
        }



        mRecorder.setMaxDuration(21000); // limit to 30 seconds
        mRecorder.setPreviewDisplay(mHolder.getSurface());
        mRecorder.setOutputFile(mOutputFileName);

        mRecorder.prepare();
        Log.v(TAG, "MediaRecorder initialized");

    }
    catch(Exception e) {
        Log.v(TAG, "MediaRecorder failed to initialize");
        e.printStackTrace();

    }
}

private void beginRecording() {
    mRecorder.setOnInfoListener(this);
    mRecorder.setOnErrorListener(this);
    mRecorder.start();
    mRecordingMsg.setText("RECORDING");
    mStartBtn.setEnabled(false);
    mStopBtn.setEnabled(true);
}

But now, I've other problem. When I installed the app in devices with 2.2 and 2.3.3 version because in this versions can't support the line: mRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)); and if I change the code: CamcorderProfile.QUALITY_LOW, the devices record in the lowest level of quality.

And I have to change the code in 2.2 and 2.3 versions doing this:

if (Build.VERSION_CODES.GINGERBREAD >= Build.VERSION.SDK_INT) {
    mRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
} else {
    Recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
    mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
    mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
    mRecorder.setVideoSize(width_video, height_video);
    mRecorder.setVideoFrameRate(30);
}

Why I have to do this, when in the Android API the setProfile() method is available in 2.2 and devices should record in their higher quality the can be supported?

UPDATE2:

In initCamera method, I've changed:

 Camera.Parameters camParams = mCamera.getParameters();
 camParams.set( "cam_mode", 1 );     
 mCamera.setParameters( camParams );

in the 2.3 version devices I can use the line to detect the resolution video configuration: mRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)); but in some devices like HTC Desire, don't support this configuration.

 mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
 mRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
 mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
 mRecorder.setVideoSize(width_video, height_video);
 mRecorder.setVideoFrameRate(30);

For 2.2 version, can accept the AAC Audio Encoder?, because my HTC Legent don't support this Audio Encoder?? UPDATE3: Android 2.2 version can't support ACC Audio Encoder.

来源:https://stackoverflow.com/questions/12154694/how-to-build-a-custom-camera-recorder-app

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!