CameraSurfaceView——Android Camera使用详解
来源:互联网 发布:剑三捏脸数据不合法 编辑:程序博客网 时间:2024/06/08 09:38
本篇文章将围绕自定义控件——CameraSurfaceView来阐述Android Camera的使用详解,这里的Camera不包含Camera2,因为Camera2只适应5.0+的安卓系统,而目前市面上5.0以下的安卓设备还是有相当大的一部分的,所以当前的主流还是使用Camera来开发相机。首先先介绍下CameraSurfaceView,这是一个集拍照,录像,闪光灯,摄像头切换,前后台切换,自适应横竖屏于一体的SurfaceView,而我们平常使用Camera无非也就这几样功能(当然还有例如直播所会使用的Camera预览帧编解码,这个控件暂时没有,后期会补上来),
而CameraSurfaceView使用方法也很简单,如下:
1.赋予程序必要的权限;2.布局文件中添加<com.yph.camerasurfaceview.CameraSurfaceView android:id="@+id/cameraSurfaceView" android:layout_width="match_parent" android:layout_height="match_parent" />或者 new CameraSurfaceView(context),即可。3.拍照:cameraSurfaceView.capture(); 录像:cameraSurfaceView.startRecord();//开始录制 cameraSurfaceView.stopRecord(); //结束录制 //设置录制时长为10秒视频 cameraSurfaceView.startRecord(10000, new MediaRecorder.OnInfoListener() { @Override public void onInfo(MediaRecorder mr, int what, int extra) { cameraSurfaceView.stopRecord(); } }); 闪光灯:cameraSurfaceView.switchLight(boolean open); 前后摄像头的切换:cameraSurfaceView.setDefaultCamera(boolean backCamera); 前后台切换:cameraSurfaceView.setRunBack(boolean b)4.释放Camera: @Override protected void onDestroy() { super.onDestroy(); cameraSurfaceView.closeCamera(); }
主体类如下(已经包含了上述所有功能代码的实现):
public class CameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Camera.AutoFocusCallback, View.OnClickListener { protected static final int[] VIDEO_320 = {320, 240}; protected static final int[] VIDEO_480 = {640, 480}; protected static final int[] VIDEO_720 = {1280, 720}; protected static final int[] VIDEO_1080 = {1920, 1080}; private int screenOritation = Configuration.ORIENTATION_PORTRAIT; private boolean mOpenBackCamera = true; private SurfaceHolder mSurfaceHolder; private SurfaceTexture mSurfaceTexture; private boolean mRunInBackground = false; boolean isAttachedWindow = false; private Camera mCamera; private Camera.Parameters mParam; private byte[] previewBuffer; private int mCameraId; protected int previewformat = ImageFormat.NV21; Context context; public CameraSurfaceView(Context context) { super(context); init(context); } public CameraSurfaceView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public CameraSurfaceView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } private void init(Context context) { this.context = context; cameraState = CameraState.START; if (cameraStateListener != null) { cameraStateListener.onCameraStateChange(cameraState); } openCamera(); if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { screenOritation = Configuration.ORIENTATION_LANDSCAPE; } mSurfaceHolder = getHolder(); mSurfaceHolder.addCallback(this); mSurfaceTexture = new SurfaceTexture(10); setOnClickListener(this); post(new Runnable() { @Override public void run() { if(!isAttachedWindow){ mRunInBackground = true; startPreview(); } } }); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); isAttachedWindow = true; } private void openCamera() { if (mOpenBackCamera) { mCameraId = findCamera(false); } else { mCameraId = findCamera(true); } if (mCameraId == -1) { mCameraId = 0; } try { mCamera = Camera.open(mCameraId); } catch (Exception ee) { mCamera = null; cameraState = CameraState.ERROR; if (cameraStateListener != null) { cameraStateListener.onCameraStateChange(cameraState); } } if (mCamera == null) { Toast.makeText(context, "打开摄像头失败", Toast.LENGTH_SHORT).show(); return; } } private int findCamera(boolean front) { int cameraCount; try { Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); cameraCount = Camera.getNumberOfCameras(); for (int camIdx = 0; camIdx < cameraCount; camIdx++) { Camera.getCameraInfo(camIdx, cameraInfo); int facing = front ? 1 : 0; if (cameraInfo.facing == facing) { return camIdx; } } } catch (Exception e) { e.printStackTrace(); } return -1; } public boolean setDefaultCamera(boolean backCamera) { if (mOpenBackCamera == backCamera) return false; if (isRecording) { Toast.makeText(context, "请先结束录像", Toast.LENGTH_SHORT).show(); return false; } mOpenBackCamera = backCamera; if (mCamera != null) { closeCamera(); openCamera(); startPreview(); } return true; } public void closeCamera() { stopRecord(); stopPreview(); releaseCamera(); } private void releaseCamera() { try { if (mCamera != null) { mCamera.setPreviewCallback(null); mCamera.setPreviewCallbackWithBuffer(null); mCamera.stopPreview(); mCamera.release(); mCamera = null; } } catch (Exception ee) { } } private boolean isSupportCameraLight() { boolean mIsSupportCameraLight = false; try { if (mCamera != null) { Camera.Parameters parameter = mCamera.getParameters(); Object a = parameter.getSupportedFlashModes(); if (a == null) { mIsSupportCameraLight = false; } else { mIsSupportCameraLight = true; } } } catch (Exception e) { mIsSupportCameraLight = false; e.printStackTrace(); } return mIsSupportCameraLight; } private Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() { public synchronized void onPreviewFrame(byte[] data, Camera camera) { if (data == null) { releaseCamera(); return; } //you can code media here if (cameraState != CameraState.PREVIEW) { cameraState = CameraState.PREVIEW; if (cameraStateListener != null) { cameraStateListener.onCameraStateChange(cameraState); } } mCamera.addCallbackBuffer(previewBuffer); } }; //设置Camera各项参数 private void startPreview() { if (mCamera == null) return; try { mParam = mCamera.getParameters(); mParam.setPreviewFormat(previewformat); mParam.setRotation(0); Camera.Size previewSize = CamParaUtil.getSize(mParam.getSupportedPreviewSizes(), 1000, mCamera.new Size(VIDEO_720[0], VIDEO_720[1])); mParam.setPreviewSize(previewSize.width, previewSize.height); int yuv_buffersize = previewSize.width * previewSize.height * ImageFormat.getBitsPerPixel(previewformat) / 8; previewBuffer = new byte[yuv_buffersize]; Camera.Size pictureSize = CamParaUtil.getSize(mParam.getSupportedPictureSizes(), 1500, mCamera.new Size(VIDEO_1080[0], VIDEO_1080[1])); mParam.setPictureSize(pictureSize.width, pictureSize.height); if (CamParaUtil.isSupportedFormats(mParam.getSupportedPictureFormats(), ImageFormat.JPEG)) { mParam.setPictureFormat(ImageFormat.JPEG); mParam.setJpegQuality(100); } if (CamParaUtil.isSupportedFocusMode(mParam.getSupportedFocusModes(), FOCUS_MODE_AUTO)) { mParam.setFocusMode(FOCUS_MODE_AUTO); } if (screenOritation != Configuration.ORIENTATION_LANDSCAPE) { mParam.set("orientation", "portrait"); mCamera.setDisplayOrientation(90); } else { mParam.set("orientation", "landscape"); mCamera.setDisplayOrientation(0); } if (mRunInBackground) { mCamera.setPreviewTexture(mSurfaceTexture); mCamera.addCallbackBuffer(previewBuffer);// mCamera.setPreviewCallbackWithBuffer(previewCallback);//设置摄像头预览帧回调 } else { mCamera.setPreviewDisplay(mSurfaceHolder);// mCamera.setPreviewCallback(previewCallback);//设置摄像头预览帧回调 } mCamera.setParameters(mParam); mCamera.startPreview(); if (cameraState != CameraState.START) { cameraState = CameraState.START; if (cameraStateListener != null) { cameraStateListener.onCameraStateChange(cameraState); } } } catch (Exception e) { releaseCamera(); return; } try { String mode = mCamera.getParameters().getFocusMode(); if (("auto".equals(mode)) || ("macro".equals(mode))) { mCamera.autoFocus(null); } } catch (Exception e) { } } private void stopPreview() { if (mCamera == null) return; try { if (mRunInBackground) { mCamera.setPreviewCallbackWithBuffer(null); mCamera.stopPreview(); } else { mCamera.setPreviewCallback(null); mCamera.stopPreview(); } if (cameraState != CameraState.STOP) { cameraState = CameraState.STOP; if (cameraStateListener != null) { cameraStateListener.onCameraStateChange(cameraState); } } } catch (Exception ee) { } } @Override public void onClick(View v) { if (mCamera != null) { mCamera.autoFocus(null); } } @Override public void surfaceCreated(SurfaceHolder holder) { stopPreview(); startPreview(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { stopPreview(); if (mRunInBackground) startPreview(); } protected CameraState cameraState; private CameraStateListener cameraStateListener; public enum CameraState { START, PREVIEW, STOP, ERROR } public void setOnCameraStateListener(CameraStateListener listener) { this.cameraStateListener = listener; } public interface CameraStateListener { void onCameraStateChange(CameraState paramCameraState); } /** * ___________________________________前/后台运行______________________________________ **/ public void setRunBack(boolean b) { if (mCamera == null) return; if (b == mRunInBackground) return; if(!b && !isAttachedWindow){ Toast.makeText(context, "Vew未依附在Window,无法显示", Toast.LENGTH_SHORT).show(); return; } mRunInBackground = b; if (b) setVisibility(View.GONE); else setVisibility(View.VISIBLE); } /** * ___________________________________开关闪光灯______________________________________ **/ public void switchLight(boolean open) { if (mCamera == null) return; try { if (mCamera != null) { if (open) { Camera.Parameters parameter = mCamera.getParameters(); if (parameter.getFlashMode().equals("off")) { parameter.setFlashMode("torch"); mCamera.setParameters(parameter); } else { parameter.setFlashMode("off"); mCamera.setParameters(parameter); } } else { Camera.Parameters parameter = mCamera.getParameters(); if ((parameter.getFlashMode() != null) && (parameter.getFlashMode().equals("torch"))) { parameter.setFlashMode("off"); mCamera.setParameters(parameter); } } } } catch (Exception e) { e.printStackTrace(); } } /** * ___________________________________以下为拍照模块______________________________________ **/ public void capture() { if (mCamera == null) return; if (!FileUtil.isExternalStorageWritable()) { Toast.makeText(context, "请插入存储卡", Toast.LENGTH_SHORT).show(); return; } mCamera.autoFocus(this); } @Override public void onAutoFocus(boolean success, Camera camera) { if (success) { try { mCamera.takePicture(null, null, new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] data, Camera camera) { Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); Matrix matrix = new Matrix(); if (mOpenBackCamera) { matrix.setRotate(90); } else { matrix.setRotate(270); matrix.postScale(-1, 1); } bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); FileUtil.saveBitmap(bitmap); Toast.makeText(context, "拍照成功", Toast.LENGTH_SHORT).show(); startPreview(); } }); } catch (Exception e) { if (isRecording) { Toast.makeText(context, "请先结束录像", Toast.LENGTH_SHORT).show(); } } } } /** * ___________________________________以下为视频录制模块______________________________________ **/ MediaRecorder mediaRecorder = new MediaRecorder(); private boolean isRecording = false; public boolean isRecording() { return isRecording; } public boolean startRecord() { return startRecord(-1, null); } public boolean startRecord(int maxDurationMs, MediaRecorder.OnInfoListener onInfoListener) { if (mCamera == null) return false; if (!FileUtil.isExternalStorageWritable()) { Toast.makeText(context, "请插入存储卡", Toast.LENGTH_SHORT).show(); return false; } mCamera.unlock(); mediaRecorder.reset(); mediaRecorder.setCamera(mCamera); mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); Camera.Size videoSize = CamParaUtil.getSize(mParam.getSupportedVideoSizes(), 1200, mCamera.new Size(VIDEO_1080[0], VIDEO_1080[1])); mediaRecorder.setVideoSize(videoSize.width, videoSize.height); mediaRecorder.setVideoEncodingBitRate(5 * 1024 * 1024);// mediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());//设置录制预览surface if (mOpenBackCamera) { mediaRecorder.setOrientationHint(90); } else { if (screenOritation == Configuration.ORIENTATION_LANDSCAPE) mediaRecorder.setOrientationHint(90); else mediaRecorder.setOrientationHint(270); } if (maxDurationMs != -1) { mediaRecorder.setMaxDuration(maxDurationMs); mediaRecorder.setOnInfoListener(onInfoListener); } mediaRecorder.setOutputFile(FileUtil.getMediaOutputPath()); try { mediaRecorder.prepare(); mediaRecorder.start(); isRecording = true; } catch (IOException e) { e.printStackTrace(); return false; } return true; } public void stopRecord() { if (!isRecording) return; mediaRecorder.setPreviewDisplay(null); try { mediaRecorder.stop(); isRecording = false; Toast.makeText(context, "视频已保存在根目录", Toast.LENGTH_SHORT).show(); } catch (IllegalStateException e) { e.printStackTrace(); } } /**_________________________________________________________________________________________**/}
① 需要相机和录音的权限,如果你的target>=23的话,那么你应该判断系统版本,如果大于23则动态申请权限
② 获取到SurfaceView的SurfaceHolder并添加Callback监听,主要为了监听SurfaceView的创建和销毁
在创建的回调方法中设置Camera的各项参数和启动Camera的预览,在销毁的回调中关闭Camera预览;
③ 设置Camera的各项参数是需要其支持的,所以先获取supprot的参数,再从中选择,若非Camera支持的参数
会造成预览黑屏或者崩溃;
④ SurfaceTexture和SurfaceView的区别:
SurfaceTexture是从Android3.0(API 11)加入的一个新类。跟SurfaceView很像,可以从camera preview
或者video decode里面获取图像流(image stream)。SurfaceView从camera读取到的预览(preview)图像流
一定要输出到一个可见的(Visible)SurfaceView上,而SurfaceTexture在接收图像流后,不需要显示出来。
所以在有些需求上,比如在Service后台里面获取camera预览帧进行处理就必须使用SurfaceTexture才能实现。
所以CameraSurfaceView控件的后台运行也是采用SurfaceTexture的,但是这里我要补充一点的是,当有可见的
SurfaceTexture,通过Camera.setPreviewTexture(surfaceTexture)也是可以实现显示Camera预览的。
⑤ 拍照是先调用聚焦,回调成功后在调用Camera的takePicture方法实现的,大部分机型都能做到在录制视频的同
时进行拍照,部分机型不行,所以得先关闭录制,这里已经做了相应的处理。
⑥ 录制视频这块请严格按照格式进行初始化,因为这个初始化环节每个步骤基本是必须的,而且顺序也不能改变
否则会崩溃。
⑦ 前、后台切换时,如果是通过代码里实例CameraSurfaceView对象并且没添加到window是无法切换为前台的,这
种情况适用于在service里面实时获取Camera预览帧的情况,注意添加
mCamera.addCallbackBuffer(previewBuffer);
mCamera.setPreviewCallbackWithBuffer(previewCallback);
在Camera.PreviewCallback previewCallback的onPreviewFrame方法里也要添加
mCamera.addCallbackBuffer(previewBuffer);
⑧ 前后置摄像头切换比较简单,关闭当前Camenra,通过findCamera方法找到摄像头ID并打开,然后开启预览即可。
源码传送门:https://github.com/qq542391099/CameraSurfaceView-master
- CameraSurfaceView——Android Camera使用详解
- Android Camera 使用详解
- Android Camera 使用详解
- Android Camera 使用详解
- Android Camera使用详解
- Android Camera、Camera2详解
- Android Camera API详解
- Android Camera详解
- Android开发指南——摄像头camera使用(转)
- Android基础入门教程——9.3 使用Camera拍照
- Android Camera——拍照
- Android学习——Camera
- Android——camera摄像头
- Android多媒体—拍照Camera
- Android Camera 使用小结
- Android Camera 使用小结
- Android Camera 使用小结
- Android Camera 使用小结
- 了解AJAX以及其封装编程
- Andrid_scrollTo/scrollBy方法的区别
- 一篇SSM框架整合友好的文章(一)
- Java反射机制
- WAVE格式
- CameraSurfaceView——Android Camera使用详解
- js实验2.(4)AJAX的GET
- 简单常用滤波算法C语言实现
- 编程之法--字符串包含
- poj 1948 Triangular Pastures
- 动态规划练习一之采药
- 世界坐标转换为NGUI坐标办法举例
- 克隆二叉树
- 数论 欧几里得