基于SurfaceTexture的静默/无预览拍照方案
来源:互联网 发布:mac project软件 编辑:程序博客网 时间:2024/06/16 02:03
公司业务需要做一个静默拍照的功能,了解了一下常见的解决方案,基本上都是基于SurfaceView做的,弊病颇多。研究了一下,决定以SurfaceTexture为切入点,做一个真正的静默拍照功能。废话不多说,上代码:
package com.xxx.service;import android.app.Service;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.Handler;import android.os.IBinder;import android.os.Message;import android.os.RemoteException;import android.text.TextUtils;import android.util.Log;import com.xxx.view.SlientCamera;import org.json.JSONException;import org.json.JSONObject;import java.io.File;import java.lang.ref.WeakReference;/** * * Created by jxl on 2017/8/29. */public class SlientTackPicService extends Service { private static final String TAG = "slient_tack_pic"; private static final int SLIENT_TACK_CAMERA = 1; private SlientCamera slientCamera; private int cameraStatus; private int replySN; private String replySEID; public MyHandler handler = new MyHandler(this); private static class MyHandler extends Handler { WeakReference<SlientTackPicService> mContext; MyHandler(SlientTackPicService mContext) { this.mContext = new WeakReference<SlientTackPicService>(mContext); } @Override public void handleMessage(Message msg) { SlientTackPicService service = mContext.get(); if (null == service) { Log.e(TAG, "mContext.get() is null!"); return; } switch (msg.what) { case SLIENT_TACK_CAMERA: Log.i(TAG, "camera ready, tackPicture!"); service.slientCamera.tackPicture(); break; default: break; } } } @Override public void onCreate() { super.onCreate(); Log.i(TAG, "SlientTackPicService, onCreate()"); slientCamera = new SlientCamera(this); slientCamera.onCreate(); cameraStatus = slientCamera.openCamere() ? 1 : -1; Log.i(TAG, "cameraStatus = " + cameraStatus); if (cameraStatus == 1) { handler.sendEmptyMessageDelayed(SLIENT_TACK_CAMERA, 3000); } } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "onStartCommand"); return super.onStartCommand(intent, flags, startId); } @Override public IBinder onBind(Intent intent) { return null; } private void releaseSource() { Log.i(TAG, "releaseSource"); if (handler.hasMessages(SLIENT_TACK_CAMERA)) { handler.removeMessages(SLIENT_TACK_CAMERA); } stopSelf(); } @Override public void onDestroy() { super.onDestroy(); Log.i(TAG, "SlientTackPicService, onDestroy()"); slientCamera.onDestroy();}
SlientTackPicService这个服务在后台运行,主要的拍照操作在 SlientCamera 类中执行。在SlientTackPicService的onCreate()方法中执行对SlientCamera 的初始化,并判断能否打开相机:cameraStatus 为1时表示后台打开相机成功,-1表示打开相机失败。失败原因可能是相机当前正在使用,或者其他未知原因等等。相机打开成功后,延时三秒进行拍照操作。下面上SlientCamera 的代码:
package com.xxx.view;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.ImageFormat;import android.graphics.SurfaceTexture;import android.hardware.Camera;import android.text.TextUtils;import android.util.Log;import com.xxx.service.SlientTackPicService;import com.xxx.tools.BitmapUtil;import com.xxx.tools.CameraUtil;import com.xxx.tools.Constants;import java.io.File;import java.io.IOException;/** * * Created by jxl on 2017/9/1. */public class SlientCamera implements SurfaceTexture.OnFrameAvailableListener{ private static final String TAG = "slient_camera"; private Context context; private Camera mCamera; private SurfaceTexture surfaceTexture; public SlientCamera(Context context) { this.context = context; } public void onCreate() { Log.i(TAG, "SlientCamera, onCreate()"); surfaceTexture = new SurfaceTexture(10); surfaceTexture.setOnFrameAvailableListener(this); //openCamere(); } public boolean openCamere() { try { mCamera = Camera.open(0); } catch (Exception e) { e.printStackTrace(); return false; } mCamera.setDisplayOrientation(180); /*List<Camera.Size> preList = mCamera.getParameters().getSupportedPreviewSizes(); List<Camera.Size> picList = mCamera.getParameters().getSupportedPictureSizes(); for (Camera.Size size:preList) { Log.i(TAG, "PreviewSize, size.width = " + size.width + ", size.height = " + size.height); } for (Camera.Size size:picList) { Log.i(TAG, "PictureSize, size.width = " + size.width + ", size.height = " + size.height); }*/ Camera.Parameters params = mCamera.getParameters(); params.setPreviewFormat(ImageFormat.NV21); //params.setRotation(); /*boolean flag = params.isZoomSupported(); int maxZoom = params.getMaxZoom();*/ params.setZoom(0);//设置焦距为0 params.setPreviewSize(Constants.VIDEO_WIDTH, Constants.VIDEO_HEIGHT); params.setPictureSize(Constants.PICTURE_WIDTH, Constants.PICTURE_HEIGHT); mCamera.setParameters(params); if (mCamera == null) { // Seeing this on Nexus 7 2012 -- I guess it wants a rear-facing camera, but // there isn't one. TODO: fix //throw new RuntimeException("Default camera not available"); Log.e(TAG, "openCamere, mCamera == null!"); return false; } try { //这一步是最关键的,使用surfaceTexture来承载相机的预览,而不需要设置一个可见的view mCamera.setPreviewTexture(surfaceTexture); mCamera.startPreview(); } catch (IOException ioe) { ioe.printStackTrace(); return false; } return true; } public void tackPicture() { //Log.w(TAG, "tackPicture()"); File picFile = CameraUtil.getOutputMediaFile(); if (picFile == null) { Log.e(TAG, "tackPicture, getOutputMediaFile is null!"); return; } if (null == mCamera) { Log.e(TAG, "tackPicture(), null == mCamera"); return; } mCamera.takePicture(mShutterCallback, null, mPictureCallback); //saveData = true; //Log.i(TAG, "takePicture after time = " + System.currentTimeMillis()); } private Camera.ShutterCallback mShutterCallback = new Camera.ShutterCallback() { @Override public void onShutter() { //playContinuousSound(); } }; private Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() { @Override public void onPictureTaken(final byte[] data, Camera camera) { Log.i(TAG, "data time = " + System.currentTimeMillis()); //bitmap就是拍出来的照片,可以进行需要做的操作 Bitmap bitmap = BitmapUtil.rotaingImageView(0, BitmapFactory.decodeByteArray(data, 0, data.length)); //Log.i(TAG, "save time = " + System.currentTimeMillis()); closeCamera(); } }; @Override public void onFrameAvailable(SurfaceTexture surfaceTexture) { Log.i(TAG, "onFrameAvailable, surfaceTexture.getTimestamp() = " + surfaceTexture.getTimestamp()); //surfaceTexture.updateTexImage(); } private void closeCamera() { Log.i(TAG, "closeCamera"); if (null != mCamera) { mCamera.stopPreview(); mCamera.release(); mCamera = null; } } public void onDestroy() { closeCamera(); }}
主要代码都在上面了,大概的解释一下:在SlientCamera 的初始化的时候,创建一个SurfaceTexture,这个SurfaceTexture承载了相机的预览。但是由于我们没有设置可见的TextureView,所以不会有预览的界面。关于openCamere这一段,其实是有很多坑的,比如相机前后摄像头、预览角度、预览界面之类的设置。我们公司做的业务是智能硬件,所以硬件参数是固定的,我就把这些参数写死了,实际的应用中是很麻烦的,这里不再赘述了。拍照时调用tackPicture方法,mPictureCallback 方法内的那个byte数组就是最终的照片,将其转化为bitmap即可。具体的实现很简单,就不再贴出来了。
总结一下这个思路,好处在于不需要设置一个可见的view去承载相机的预览,把这个事情交给了surfaceTexture去做,可以全程在后台静默完成,想想还是挺猥琐的。限制在于surfaceTexture只能在API11之上才能调用,而在更高等级的API21中,调用相机需要弹出权限提示框,无法再静默打开摄像头。说白了,这是一个只能在API11~API21中间使用的思路。
- 基于SurfaceTexture的静默/无预览拍照方案
- 基于Android TextureView与SurfaceTexture实现相机Camera拍照预览与保存照片
- Android静默拍照(无感知拍照)
- 使用TextureView和SurfaceTexture预览Camera 基础拍照demo
- Android4.1 无预览拍照
- android camera无预览拍照 后台拍照
- 玩转Android Camera开发(二):使用TextureView和SurfaceTexture预览Camera 基础拍照demo
- 玩转Android Camera开发(二):使用TextureView和SurfaceTexture预览Camera 基础拍照demo
- 玩转Android Camera开发(二):使用TextureView和SurfaceTexture预览Camera 基础拍照demo
- 玩转Android Camera开发(二):使用TextureView和SurfaceTexture预览Camera 基础拍照demo
- 玩转Android Camera开发(二):使用TextureView和SurfaceTexture预览Camera 基础拍照demo
- android---------SurfaceTexture----------通过SurfaceTexture来对预览的图像进行处理
- 使用Camera进行拍照 & 后台静默拍照的思路
- 趁热打铁之---VS2010基于VFW的视频控件编写,实现预览、拍照功能
- Android 基于SurfaceTexture shader的类动漫+波浪形图像处理
- Android 修改源码达到Camera 无预览拍照效果
- Android SurfaceTexture和GLSurfaceView做Camera预览
- Android SurfaceTexture和GLSurfaceView做Camera预览
- linux(十三)之磁盘分区、创建文件系统、挂载
- Python学习笔记(1)
- WOJ1171-Recursive Function
- 邝斌的ACM模板(Baby-Step Giant-Step)
- HighCharts常见使用问题总结(个人笔记)
- 基于SurfaceTexture的静默/无预览拍照方案
- 二叉树
- C++ std::vector指定位置插入
- Law of Commutation HDU
- 视频播放问题总结
- mysql索引优缺点以及创建的时机
- WOJ1187-Difference Minimizing
- kshon学习笔记之android相对布局
- 美团前端一面