我正在尝试开发自定义摄像机录像机。当我的设备在 Activity 的 beginRecording() 中执行 start MediaRecorder 方法时,应用程序崩溃。我不知道出了什么问题,因为我遵循Google API Guide。我的设备使用 Android 2.3 或更高版本。在我的 Nexus S 4.1.1 中执行正常,但在我的 Xperia 2.3.3 中没有运行,显示此跟踪。
我的代码是:
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;
}
跟踪错误是:
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
更新:
我已经修复了这个错误。主要代码是:
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);
}
但现在,我还有其他问题。当我在 2.2 和 2.3.3 版本的设备中安装应用程序时,因为在此版本中不支持以下行: mRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)); 如果我更改代码:CamcorderProfile.QUALITY_LOW,则设备以最低质量记录。
我必须更改 2.2 和 2.3 版本中的代码:
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);
}
为什么我必须这样做,当在 Android API 中,setProfile() 方法在 2.2 中可用并且设备应该以更高的质量记录可以支持?
更新2:
在 initCamera 方法中,我改变了:
Camera.Parameters camParams = mCamera.getParameters();
camParams.set( "cam_mode", 1 );
mCamera.setParameters( camParams );
在 2.3 版本的设备中,我可以使用该行来检测视频配置的分辨率: mRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)); 但在某些设备(如 HTC Desire)中,不支持此配置。
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);
对于 2.2 版本,可以接受 AAC 音频编码器吗?因为我的 HTC Legent 不支持这个音频编码器?UPDATE3:Android 2.2 版本不支持 ACC 音频编码器。