package com.whyc.widget; import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.graphics.ImageFormat; import android.graphics.SurfaceTexture; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CaptureRequest; import android.media.Image; import android.media.ImageReader; import android.media.MediaRecorder; import android.media.MediaScannerConnection; import android.net.Uri; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.support.annotation.NonNull; import android.util.AttributeSet; import android.util.Log; import android.util.Size; import android.view.Surface; import android.view.TextureView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import static android.content.ContentValues.TAG; import static android.os.Environment.DIRECTORY_DOCUMENTS; /** * 这个采用TEMPLATE_RECORD和TEMPLATE_VIDEO_SNAPSHOT模板,TEMPLATE_RECORD模板是录制视频 * 改为: * 副摄 ->录像 * 主摄-> 拍照 * */ public class Camera2TextureView2 extends TextureView { private Context mContext; //主摄,后摄 private CameraDevice mainCameraDevice; //副摄,前摄 private CameraDevice subCameraDevice; private CaptureRequest.Builder captureCaptureBuilder = null; private CaptureRequest.Builder videoCaptureBuilder = null; private CaptureRequest.Builder previewCaptureBuilder = null; private CameraCaptureSession mainCaptureSession; private CameraCaptureSession subCaptureSession; private MediaRecorder mMediaRecorder; private Surface previewSurface; private ImageReader mImageReader; private Surface surfaceForStream; /**线程处理器 分别处理 录像,预览,拍照*/ private Handler mainHandler = new Handler(Looper.getMainLooper()); private HandlerThread videoThread; private HandlerThread previewThread; private HandlerThread captureThread; private HandlerThread imageThread; private Handler videoThreadHandler; private Handler previewThreadHandler; private Handler captureThreadHandler; private Handler imageThreadHandler; ScheduledExecutorService scheduledExecutorService; //主页面的视图 private LinearLayout llUpText; private TextView tvDevice; private int wight = 3840; private int height = 2160; // private int wight = 1920; // private int height = 1080; private int videoWidth = 1920; private int videoHeight = 1080; Size outputSizes = new Size(wight,height); public Camera2TextureView2(Context context) { super(context,null); } public Camera2TextureView2(Context context, AttributeSet attrs) { super(context, attrs); // executorService = Executors.newScheduledThreadPool(4); /*String url = "http://192.168.10.79:8919/fg/server/timestamp"; executorService.execute(()-> { HttpReqData httpReqData = new HttpReqData(url); HttpRespData data = HttpRequestUtil.getData(httpReqData); System.out.println("-------------------------------------------heirong wei---"+data.content); });*/ mContext = context; } public void init(LinearLayout llUpText, TextView tvDevice) { this.llUpText = llUpText; this.tvDevice = tvDevice; setSurfaceTextureListener(mSurfaceTextureListener); } private SurfaceTextureListener mSurfaceTextureListener = new SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { //预览surface SurfaceTexture surfaceTexture = getSurfaceTexture(); surfaceTexture.setDefaultBufferSize(outputSizes.getWidth(), outputSizes.getHeight()); previewSurface = new Surface(surfaceTexture); //页面加载后初始化线程处理器 initPreviewThreadHandler(); initCamera2(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { return false; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } }; @SuppressLint("MissingPermission") private void initCamera2() { //获取权限 // ActivityCompat.requestPermissions(Camera2TextureViewActivity.this, permissionArray, 1); CameraManager cm = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE); try { //获取前摄像头 String[] cameraIds = cm.getCameraIdList(); for (int i = 0; i < cameraIds.length; i++) { String cameraId = cameraIds[i]; CameraCharacteristics cameraCharacteristics = cm.getCameraCharacteristics(cameraId); if(cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT){ cm.openCamera(cameraId,subDeviceStateCallback,null); }else if(cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_BACK){ cm.openCamera(cameraId,mainDeviceStateCallback,null); } } }catch (Exception e){ e.printStackTrace(); } } private CameraDevice.StateCallback subDeviceStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice cameraDevice) { subCameraDevice = cameraDevice; } @Override public void onDisconnected(@NonNull CameraDevice camera) { } @Override public void onError(@NonNull CameraDevice camera, int error) { } }; private CameraDevice.StateCallback mainDeviceStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice cameraDevice) { mainCameraDevice = cameraDevice; //拍照的接收器 mImageReader = ImageReader.newInstance(outputSizes.getWidth(), outputSizes.getHeight(), ImageFormat.JPEG, 2); // mImageReader = ImageReader.newInstance(outputSizes.getWidth(), outputSizes.getHeight(), ImageFormat.JPEG, 4); //创建主摄像头会话并预览 createPreviewSession(); } @Override public void onDisconnected(@NonNull CameraDevice camera) { } @Override public void onError(@NonNull CameraDevice camera, int error) { } }; private void createPreviewSession() { try { mainCameraDevice.createCaptureSession(Arrays.asList(previewSurface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { mainCaptureSession = session; try { previewCaptureBuilder = mainCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); } catch (CameraAccessException e) { e.printStackTrace(); } previewCaptureBuilder.addTarget(previewSurface); startPreview(); } @Override public void onConfigureFailed(@NonNull CameraCaptureSession session) { } }, null); }catch (Exception e){ e.printStackTrace(); } } private void startPreview() { //启动预览 try { mainCaptureSession.setRepeatingRequest(previewCaptureBuilder.build(), null, previewThreadHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } private File recorderFile; private String recorderPath; private boolean isRecording = false; private void initRecorder() { // recorderFile = new File(Camera2Activity.this.getExternalFilesDir(null).getAbsolutePath() + "/video/"); // recorderFile = new File(mContext.getExternalFilesDir(null).getAbsolutePath() + "/video/"); // recorderFile = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM); recorderFile = new File(Environment.getExternalStoragePublicDirectory(DIRECTORY_DOCUMENTS).getAbsolutePath() + "/yc_test/"); if (!recorderFile.exists()) { recorderFile.mkdirs(); } recorderPath = recorderFile.getAbsolutePath()+File.separator + System.currentTimeMillis() + ".mp4"; Log.e(TAG, "视频路径:" + recorderPath); mMediaRecorder = new MediaRecorder(); mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); mMediaRecorder.setOutputFile(recorderPath); mMediaRecorder.setVideoEncodingBitRate(10000000); mMediaRecorder.setVideoFrameRate(20); mMediaRecorder.setVideoSize(videoWidth, videoHeight); mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); } public void createRecorderSession(){ if(mMediaRecorder!=null) { mMediaRecorder.reset(); } initRecorder(); //线程开启 initThreadHandlerForRecording(); mImageReader.setOnImageAvailableListener(mImageReaderListener, imageThreadHandler); try{ mMediaRecorder.prepare(); mMediaRecorder.start(); }catch (Exception e){ e.printStackTrace(); } try { subCameraDevice.createCaptureSession(Arrays.asList(mMediaRecorder.getSurface()), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { subCaptureSession = session; try { //camera2 视频录制模式 videoCaptureBuilder = subCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD); videoCaptureBuilder.addTarget(mMediaRecorder.getSurface()); // 开始录制视频 subCaptureSession.setRepeatingRequest(videoCaptureBuilder.build(), null, videoThreadHandler); } catch (Exception e) { e.printStackTrace(); } } @Override public void onConfigureFailed(@NonNull CameraCaptureSession session) { } }, null); //camera2 3秒钟1次的定时任务 //对焦并拍照 scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); scheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { takePhoto(); } }, 1, 2, TimeUnit.SECONDS); // focus(); } catch (CameraAccessException e) { e.printStackTrace(); } } private void takePhoto() { try { // 对焦完成,开始拍照 /*==========摄像头的模式设置==========*/ // 设置自动白平衡 // captureCaptureBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_AUTO); // 设置对焦模式 // captureCaptureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); try { if(captureCaptureBuilder == null) { captureCaptureBuilder = mainCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureCaptureBuilder.addTarget(mImageReader.getSurface()); captureCaptureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); } mainCaptureSession.capture(captureCaptureBuilder.build(),null, captureThreadHandler); // Log.d("Camera2", "拍照成功:"); } catch (CameraAccessException e) { e.printStackTrace(); } }catch (Exception e){ e.printStackTrace(); } } private ImageReader.OnImageAvailableListener mImageReaderListener = reader -> { Image image = null; try { // Log.d("Camera2", "获取到图像数据:"); image = reader.acquireNextImage(); // 获取到拍照的图像数据 //这里只是演示test // llUpText.setVisibility(View.VISIBLE); // tvDevice.setText("这是测试的动态赋值"); // 保存图片到相册-这种方式是JEPG格式 saveImageToGallery(image); //YUV420 // int I420size = image.getWidth()*image.getHeight()*3/2; // byte[] nv21 = new byte[I420size]; // YUVUtil.YUVToNV21_NV12(image,nv21,outputSizes.getWidth(),outputSizes.getHeight(),"NV21"); /*List planetBytes = getPlanetBytes(image); byte[] nv21 = byteMergerAll(planetBytes.get(0), planetBytes.get(1)); Bitmap bitmap = BitmapUtil.nv21ToBitmap(nv21, outputSizes.getWidth(), outputSizes.getHeight()); BitmapUtil.saveBitmapToFile(bitmap);*/ }catch (Exception e){ e.printStackTrace(); }finally { if(image!=null) { image.close(); } } }; private List getPlanetBytes(Image image) { Image.Plane[] planes = image.getPlanes(); int remaining0 = planes[0].getBuffer().remaining(); // int remaining1 = planes[1].getBuffer().remaining(); int remaining2 = planes[2].getBuffer().remaining(); //分别准备三个数组接收YUV分量。 byte[] yRawSrcBytes = new byte[remaining0]; // byte[] uRawSrcBytes = new byte[remaining1]; byte[] vRawSrcBytes = new byte[remaining2]; planes[0].getBuffer().get(yRawSrcBytes); // planes[1].getBuffer().get(uRawSrcBytes); planes[2].getBuffer().get(vRawSrcBytes); List yuvBytes = new ArrayList<>(); yuvBytes.add(yRawSrcBytes); yuvBytes.add(vRawSrcBytes); return yuvBytes; } private static byte[] byteMergerAll(byte[]... values) { int length_byte = 0; for (int i = 0; i < values.length; i++) { length_byte += values[i].length; } byte[] all_byte = new byte[length_byte]; int countLength = 0; for (int i = 0; i < values.length; i++) { byte[] b = values[i]; System.arraycopy(b, 0, all_byte, countLength, b.length); countLength += b.length; } return all_byte; } private void saveImageToGallery(Image image) { // 定义图片的保存路径和文件名 String fileName = "IMG_" + System.currentTimeMillis() + ".jpg"; String filePath = Environment.getExternalStoragePublicDirectory(DIRECTORY_DOCUMENTS).getAbsolutePath() + "/yc_test"+ File.separator + fileName; // 获取图片的字节数组 ByteBuffer buffer = image.getPlanes()[0].getBuffer(); byte[] data = new byte[buffer.remaining()]; buffer.get(data); // 创建文件输出流 // FileOutputStream fos = null; /*try { fos = new FileOutputStream(filePath); fos.write(data);*/ try ( FileOutputStream fos = new FileOutputStream(filePath); BufferedOutputStream bos = new BufferedOutputStream(fos)){ bos.write(data); } catch (FileNotFoundException fileNotFoundException) { fileNotFoundException.printStackTrace(); } catch (IOException ioException) { ioException.printStackTrace(); } try { // 通知图库更新 MediaScannerConnection.scanFile(mContext, new String[]{filePath}, null, null); // 在某些设备上,可能需要发送广播通知才能使图片立即出现在相册中 mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(new File(filePath)))); // 显示保存成功的提示 // Toast.makeText(this, "图片保存成功", Toast.LENGTH_SHORT).show(); } catch (Exception e) { e.printStackTrace(); // 显示保存失败的提示 // Toast.makeText(this, "图片保存失败", Toast.LENGTH_SHORT).show(); }finally { image.close(); } } public void stopRecording() { try { // 停止录制视频,关闭录像请求 mMediaRecorder.stop(); //关闭录制视频会话 subCaptureSession.stopRepeating(); //关闭拍照会话 scheduledExecutorService.shutdownNow(); //停止画面图片监听 mImageReader.setOnImageAvailableListener(null, null); //终止线程 stopThreadHandlerForRecording(); //这里只是演示test // llUpText.setVisibility(View.GONE); } catch (Exception e) { e.printStackTrace(); } finally { mMediaRecorder.reset(); // mMediaRecorder.release(); // mMediaRecorder = null; //图库更新 addToGallery(recorderPath); isRecording = false; } } private void addToGallery(String videoFilePath) { // 发送广播通知图库更新 MediaScannerConnection.scanFile(mContext, new String[]{videoFilePath}, null, (path, uri) -> { // 添加到相册成功的回调 Toast.makeText(mContext, "视频已保存至相册", Toast.LENGTH_SHORT).show(); }); } private void initPreviewThreadHandler(){ //预览的处理线程 previewThread = new HandlerThread("camera2Preview"); previewThread.start(); previewThreadHandler = new Handler(previewThread.getLooper()); //图像监测的处理线程 imageThread = new HandlerThread("camera2Image"); imageThread.start(); imageThreadHandler = new Handler(imageThread.getLooper()); } /** * 录像 一个线程处理器 * 拍照 一个线程处理器 * 图像监测 一个线程处理器 */ private void initThreadHandlerForRecording() { //录像的处理线程 videoThread = new HandlerThread("Camera2Video"); videoThread.start(); videoThreadHandler = new Handler(videoThread.getLooper()); //拍照的处理线程 captureThread = new HandlerThread("camera2Capture"); captureThread.start(); captureThreadHandler = new Handler(captureThread.getLooper()); //图像监测的处理线程 if(imageThreadHandler == null) { imageThread = new HandlerThread("camera2Image"); imageThread.start(); imageThreadHandler = new Handler(imageThread.getLooper()); } } private void stopThreadHandlerForRecording() { try { videoThread.quitSafely(); videoThread.join(); videoThread = null; videoThreadHandler = null; captureThread.quitSafely(); captureThread.join(); captureThread = null; captureThreadHandler = null; imageThread.quitSafely(); imageThread.join(); imageThread = null; imageThreadHandler = null; } catch (InterruptedException e) { e.printStackTrace(); } } }