近期做了一段时间的Camera的开发,虽然是应用层的开发但是也粗略的接触了一些framework相关的东西,在此写出来分享下。
先说下应用开发的目的和实用环境,这次开发的主要目的是实现android设备上外挂USB摄像头来实现录像拍照的功能,类似行车记录仪的功能,但是灵活性更大因为可随时在android设备上回看录像和回看照片,并可查看GPS的运行轨迹,当然主要功能还是录像和拍照功能了。</p><p> 首先还是要做一个简单的demo功能出来,后面再逐渐的去丰富各个功能模块和完善BUG。
第一步肯定是建工程了,一个主Activity用来显示录像预览的图像,并且设计简单的按钮分别来实现开始关闭录像,拍照,浏览录像和照片等功能。 当然首先还是要在AndroidManifest.xml中加入
<uses-permission android:name = "android.permission.CAMERA" />
<uses-feature android:name = "android.hardware.camera" />
<uses-feature android:name = "android.hardware.camera.autofocus" />
<uses-permission android:name = "android.permission.RECORD_AUDIO"></span>
之所以加入RECORD_AUDIO是因为后期可能会加入声音录入,因此提前加进来。
主Activity需要实现OnClickListener,SurfaceHolder.Callback,OnCheckChangeListener等接口,因为所设计的按钮分别需要监听Click,录像模式选择会监听CheckBox的动作。
核心代码
下面的函数主要是实现Camera取景预览的界面,并且初始化了应用的初始界面,包括按钮和预览界面。
取景的容器为SurfaceView,使用它必须用到SurfaceHolder,它可以随时监听Surface的变化。并且需要将SurfaceHolder的类型设置为SURFACE_TYPE_PUSH_BUFFERS.这样画面缓存会由Camera类来管理。
private void initView() {
display = (SurfaceView) findViewById(R.id.display);
surfaceHolder = display.getHolder();
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
camera = (ImageButton) findViewById(R.id.camera);
video_record = (CheckBox) findViewById(R.id.video);
camera_shoot = (ImageButton) findViewById(R.id.shoot);
explorer = (Button) findViewById(R.id.explorer);
settings = (Button) findViewById(R.id.settings);
surfaceHolder.addCallback(this);
surfaceHolder.setFormat(PixelFormat.TRANSPARENT);
surfaceHolder.setFixedSize(1280,720);
camera.setOnClickListener(this);
camera_shoot.setOnClickListener(this);
explorer.setOnClickListener(this);
settings.setOnClickListener(this);
timer = (TextView)findViewById(R.id.timer);
video_record.setOnClickListener(this);
}
Camera录像拍照等功能我实现均在另一个Service中,目的也是可以实现后台录像的功能,并随时监听操作。
核心代码
此处代码主要是完成录像画面的预览,其中会进行USB设备的热插拔监听,拍照Camera属性设置已经Camera对象的初始化,打开预览等操作。
public void surfaceCreate() throws RemoteException {
Log.e(TAG, "surfaceCreate");
mSurfaceHolder = application.getSurfaceHolder();
handler.removeCallbacks(USBtask);
handler.postDelayed(USBtask, 5000);
try {
if(!screen_on){
screen_on = true;
handler.sendEmptyMessage(GPS_INTERVAL);
handler.sendEmptyMessageDelayed(RESUME, 3000);
return;
}
if(mCamera == null){
mCamera = Camera.open(cameraId);
cameraPictureParamsSet();
mCamera.setPreviewDisplay(mSurfaceHolder);
startPreview();
Log.e(TAG, "surfaceCreate mCamera is null, startPreview!");
if(isRecording){
cameraListener.autoStartRecord();
startRecord();
}
}else{
if(isRecording)
cameraListener.autoStartRecord();
mCamera.setPreviewDisplay(mSurfaceHolder);
startPreview();
Log.e(TAG, "surfaceCreate mCamera is not null, startPreview!");
}
} catch (IOException e) {
e.printStackTrace();
}catch(RuntimeException e){
cameraListener.noCamera();
}
}
这部分代码就是录像的主要代码了,其中主要涉及到了MediaRecorder的部分操作,包括它输出视频流保存的格式和压缩方式,码率和码流,分辨率等参数的设置。因此处较易出现异常因此建议多catch异常以免程序崩溃。
private void recordstart(boolean tts){
hour = 0;
minute = 0;
second = 0;
if (mMediaRecorder == null){
mMediaRecorder = new MediaRecorder();
}else{
mMediaRecorder.reset();
try {
mCamera.reconnect();
} catch (IOException e1) {
e1.printStackTrace();
}
}
cameraPictureParamsSet();
mCamera.unlock();
if(mSurfaceHolder == null)
mCamera.stopPreview();
mMediaRecorder.setCamera(mCamera);
boolean record_toggle = preference.getBoolean("record", true);
if(record_toggle){
mMediaRecorder
.setAudioSource(MediaRecorder.AudioSource.MIC);
}
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setVideoFrameRate(mVideoFrameRate);
mMediaRecorder.setVideoSize(1280, 720);
mMediaRecorder.setVideoEncodingBitRate(4000000);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
if(record_toggle){
mMediaRecorder
.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mMediaRecorder.setAudioChannels(1);
mMediaRecorder.setAudioSamplingRate(8000);
}
mMediaRecorder.setMaxDuration(max_duration_ms);
mMediaRecorder.setOnErrorListener(CameraService.this);
mMediaRecorder.setOnInfoListener(CameraService.this);
getVideoName();
if(isPathValid){
try {
recordingPath = getVideoName().getAbsolutePath();
mMediaRecorder.setOutputFile(recordingPath);
fileUtils.setRecordingfile(recordingPath);
mMediaRecorder.prepare();
mMediaRecorder.start();
handler.removeCallbacks(task);
if(isPathValid){
handler.postDelayed(task, 1000);
}
cameraListener.autoStartRecord();
if(tts)
tts(getString(R.string.isrecodering));
isRecording = true;
} catch (IllegalStateException e) {