近期有个项目要起用Android开发版,驱动四颗摄像头,进行拍照,录像并上传的功能。第一想法是不就是驱动摄像头拍照吗?之后就陷入了各种bug和功能不满足的漩涡中。好了不多说下面上代码。不好勿喷
项目中引用了GitHub上的一个开源项目:https://github.com/saki4510t/UVCCamera
该项目中写了很多例子有兴趣的可以了解一下,我这便主要用了项目中的libuvccamera和usbCameraCommon两包。后面会把这两个包单独上传。直接在Android studio中引用这两个包。
首先在布局文件中这样写:
android:id="@+id/camera1"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
宽/高写自己需要拍出来的照片的对应像素。如果此处不写具体值后面的照片尺寸会是Android设备的默认比例大小
获取权限在Manifest中添加
在Activity中添加。应为6.0以上的权限中文件权限被设置成了隐私权限所以此处需要动态获取。可以加入版本判断。android:name="android.permission.CAMERA"/> android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
//检查权限(NEED_PERMISSION)是否被授权 PackageManager.PERMISSION_GRANTED表示同意授权 if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { //用户已经拒绝过一次,再次弹出权限申请对话框需要给用户一个解释 if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission .WRITE_EXTERNAL_STORAGE)) { Toast.makeText(this, "请开通相关权限,否则无法正常使用本应用!", Toast.LENGTH_SHORT).show(); } //申请权限 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1); } else { //Toast.makeText(this, "授权成功!", Toast.LENGTH_SHORT).show(); Log.e("Application", "checkPermission: 已经授权!"); }
好了下面是重点
package com.yao.camera.activity; import android.app.Activity; import android.app.PendingIntent; import android.content.Context; import android.graphics.SurfaceTexture; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.support.annotation.Nullable; import android.util.Log; import android.view.Surface; import android.view.View; import android.widget.Button; import com.serenegiant.common.BaseActivity; import com.serenegiant.usb.USBMonitor; import com.serenegiant.usb.UVCCamera; import com.serenegiant.usbcameracommon.UVCCameraHandler; import com.serenegiant.widget.UVCCameraTextureView; import com.yao.camera.R; import com.yao.camera.http.ImageUpHttps; import com.yao.camera.model.HttpModel; import com.yao.camera.util.CameraUtil; import com.yao.camera.util.FileUtil; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import butterknife.BindView; import butterknife.ButterKnife; /** * Created by yao on 2018/2/6. * USB相机拍照Activity */ public class CameraActivity extends BaseActivity { /** * 调试显示 */ @BindView(R.id.camera1) UVCCameraTextureView cameraOne; /** * 调试拍照按钮 */ @BindView(R.id.d_photograph) Button dPhotograph; public static Boolean DEBUG = true; /** * preview resolution(width) * if your camera does not support specific resolution and mode, * {@link UVCCamera#setPreviewSize(int, int, int)} throw exception * 像素 */ private static final int PREVIEW_WIDTH = 640; /** * preview resolution(height) * if your camera does not support specific resolution and mode, * {@link UVCCamera#setPreviewSize(int, int, int)} throw exception * 像素 */ private static final int PREVIEW_HEIGHT = 480; /** * 摄像头属性类 */ private USBMonitor usbMonitor; /** * 摄像头编号 1开始 */ private int type = 1; /**摄像头操作类*/ private UVCCameraHandler camera1; /** * 日志标志 */ private static final String TAG = "CameraActivity"; /** * 作为线程锁加载实体类 */ private final CameraUtil sy = new CameraUtil(); /**usb信息*/ private UsbManager mUsbManager; private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 1) { //finish(); } } }; private Message msg = new Message(); private PendingIntent mPermissionIntent = null; /** * activity onCreate方法 * * @param savedInstanceState */ @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); //加载布局文件 引用第三方类库实例化布局文件中的实体 setContentView(R.layout.camera_layout); ButterKnife.bind(this); init(); } /** * activity onStart方法 */ @Override protected void onStart() { //启动USB检测 usbMonitor.register(); super.onStart(); } @Override protected void onResume() { super.onResume(); } @Override protected void onDestroy() { //释放资源 releaseCamera(0); if (usbMonitor != null) { try { usbMonitor.destroy(); } catch (Exception e) { e.printStackTrace(); } usbMonitor = null; } super.onDestroy(); } @Override protected void onStop() { super.onStop(); } /** * 实例化对象 */ private void init() { //设置展示图像的比例 cameraOne.setAspectRatio(PREVIEW_WIDTH / (float) PREVIEW_HEIGHT); //实例化USB检测类 usbMonitor = new USBMonitor(this, deviceConnectListener); //实例化摄像头属性 camera1 = getHandler(CameraActivity.this, cameraOne); //设置拍照按钮点击事件 dPhotograph.setOnClickListener(onClickListener); mUsbManager = (UsbManager)this.getSystemService(Context.USB_SERVICE); //依次启动摄像头展示 synchronized (sy) { if (cameraOne != null) { cameraOne.onResume(); } } } /** * 按钮点击事件 */ private View.OnClickListener onClickListener = new View.OnClickListener() { @Override public void onClick(View v) { switch (v.getId()) { //拍照按钮 case R.id.d_photograph: Log.d("test", "拍照"); cameraS(); break; } } }; /** * USB检测类中的内部类,必传参数 */ private USBMonitor.OnDeviceConnectListener deviceConnectListener = new USBMonitor.OnDeviceConnectListener() { //实例化时执行 @Override public void onAttach(UsbDevice device) { if (DEBUG) Log.d(TAG, "onAttach:" + device); } //推出时执行 @Override public void onDettach(UsbDevice device) { if (DEBUG) Log.d(TAG, "onDettach:" + device); } @Override public void onConnect(UsbDevice device, final USBMonitor.UsbControlBlock ctrlBlock, boolean createNew) { if (DEBUG) { Log.d(TAG, "name:" + device.getDeviceName()); } //BaseActivity中的方法 queueEvent(new Runnable() { @Override public void run() { //检测启动那个摄像头 switch (type) { case 1: camera1.open(ctrlBlock); startPreview(cameraOne, camera1); File cameraF = FileUtil.getCaptureFile(Environment.DIRECTORY_DCIM, "5.jpg"); camera1.captureStill(cameraF.getPath()); files.add(cameraF); break; default: //打开摄像头 UCameraLiftUp.open(ctrlBlock); //页面展示 startPreview(cameraLiftUp, UCameraLiftUp); //图片文件 File fileLift = FileUtil.getCaptureFile(Environment.DIRECTORY_DCIM, ".jpg"); //拍照 UCameraLiftUp.captureStill(fileLift.getPath()); //添加到文件列表中 files.add(fileLift); break; } //Log.d(TAG, "run: 拍照完成--------------------------"+files.get(type-1).getName()); type++; } }, 0); } @Override public void onDisconnect(UsbDevice device, USBMonitor.UsbControlBlock ctrlBlock) { if (DEBUG) Log.v(TAG, "onDisconnect:" + device.getDeviceName()); } @Override public void onCancel(UsbDevice device) { if (DEBUG) Log.v(TAG, "onCancel:"); } }; /** * type 1-n 对应每个摄像头 0 表示所有 * 释放摄像头资源 */ private synchronized void releaseCamera(int type) { synchronized (sy) { switch (type) { case 1: releaseCameraTemp(cameraone, camera1); break; default: break; } } } /** * 释放资源 */ private void releaseCameraTemp(UVCCameraHandler camera, UVCCameraTextureView view) { try { if (camera != null) { camera.close(); //camera.release(); camera = null; } } catch (Exception e) { e.printStackTrace(); } } //=========================================================================================== /** * 将摄像头拍到的画面进行展示 * * @param cameraTextureView * @param cameraHandler */ private void startPreview(UVCCameraTextureView cameraTextureView, UVCCameraHandler cameraHandler) { final SurfaceTexture st = cameraTextureView.getSurfaceTexture(); if (null == st) { return; } cameraHandler.startPreview(new Surface(st)); } private UVCCameraHandler getHandler(Activity activity, UVCCameraTextureView cameraTextureView) { return UVCCameraHandler.createHandler(activity, cameraTextureView, 0, PREVIEW_WIDTH, PREVIEW_HEIGHT, 1); } //拍照 public boolean cameraS() { final Listlist = usbMonitor.getDeviceList(); type = 1; Log.d("CameraActivity", "共有" + list.size() + "有USB设备"); new Thread() { @Override public void run() { try { //依次启动摄像头 for (UsbDevice device : list) { if(device.getProductName().indexOf("Camera")==-1 && !device.getProductName().equals("YHU3343CP205")) continue; //拍照 //Log.d("---", device.toString()); usbMonitor.requestPermission(device); } } catch (Exception e) { e.printStackTrace(); } } }.start(); return false; } }
下面开始解释首先用USBmessage获取USB数量将camera和其他USB设备区分开
设置要拍出来图片的比例最终尺寸有布局文件决定将出来一个该比例下布局能支持的最大图。
设置UVCCameraHander将实现布局中的view和USB图片展示的绑定。
调用USBMonitor将回调USBMonitor.OnDeviceConnectlistener中的onConnect()
每次有USB设备进来是都会调用onAttach()
传入的device是对应的摄像头驱动首先打开该驱动:camera.open(ctrBlock)该参数在大神的sdk中会回调最终调用device
启动 UVCCameraHander和view的绑定开始预览。
captuieStill()无参数也是可以的将会在dicm生成图片,由参数请注意此处需要传入空的图片文件路径,加文件名,后面程序将写入对应的数据
如果是拍摄视频请在UVCCameraHandle中回调一下startRecording和stopRecording,开始录制和结束录制。
关键代码在AbstractUVCCameraHandler中
public void handleCaptureStill(final String path) { if (DEBUG) Log.v(TAG_THREAD, "handleCaptureStill:"); final Activity parent = mWeakParent.get(); if (parent == null) return; mSoundPool.play(mSoundId, 0.2f, 0.2f, 0, 0, 1.0f); // play shutter sound try { final Bitmap bitmap = mWeakCameraView.get().captureStillImage(); // get buffered output stream for saving a captured still image as a file on external storage. // the file name is came from current time. // You should use extension name as same as CompressFormat when calling Bitmap#compress. final File outputFile = TextUtils.isEmpty(path) ? MediaMuxerWrapper.getCaptureFile(Environment.DIRECTORY_DCIM, ".jpg") : new File(path); final BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile)); try { try { bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os); os.flush(); mHandler.sendMessage(mHandler.obtainMessage(MSG_MEDIA_UPDATE, outputFile.getPath())); } catch (final IOException e) { } } finally { os.close(); } } catch (final Exception e) { callOnError(e); } } public void handleStartRecording() { if (DEBUG) Log.v(TAG_THREAD, "handleStartRecording:"); try { if ((mUVCCamera == null) || (mMuxer != null)) return; final MediaMuxerWrapper muxer = new MediaMuxerWrapper(".mp4"); // if you record audio only, ".m4a" is also OK. MediaVideoBufferEncoder videoEncoder = null; switch (mEncoderType) { case 1: // for video capturing using MediaVideoEncoder new MediaVideoEncoder(muxer, getWidth(), getHeight(), mMediaEncoderListener); break; case 2: // for video capturing using MediaVideoBufferEncoder videoEncoder = new MediaVideoBufferEncoder(muxer, getWidth(), getHeight(), mMediaEncoderListener); break; // case 0: // for video capturing using MediaSurfaceEncoder default: new MediaSurfaceEncoder(muxer, getWidth(), getHeight(), mMediaEncoderListener); break; } if (true) { // for audio capturing new MediaAudioEncoder(muxer, mMediaEncoderListener); } muxer.prepare(); muxer.startRecording(); if (videoEncoder != null) { mUVCCamera.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_NV21); } synchronized (mSync) { mMuxer = muxer; mVideoEncoder = videoEncoder; } callOnStartRecording(); } catch (final IOException e) { callOnError(e); Log.e(TAG, "startCapture:", e); } } public void handleStopRecording() { if (DEBUG) Log.v(TAG_THREAD, "handleStopRecording:mMuxer=" + mMuxer); final MediaMuxerWrapper muxer; synchronized (mSync) { muxer = mMuxer; mMuxer = null; mVideoEncoder = null; if (mUVCCamera != null) { mUVCCamera.stopCapture(); } } try { mWeakCameraView.get().setVideoEncoder(null); } catch (final Exception e) { // ignore } if (muxer != null) { muxer.stopRecording(); mUVCCamera.setFrameCallback(null, 0); // you should not wait here callOnStopRecording(); } }
最后说明如果需要多个摄像头只需要为每个摄像头建立一个view和一个UVCCameraHander即可。
可以参考这些写自己需要实现的摄像头功能。也可以参考UVCCamera源码中的例子
资源连接:https://download.csdn.net/download/qq_33543638/10535630