package com.example.lxb.camerademo; import android.Manifest; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.ImageFormat; import android.graphics.Matrix; import android.graphics.RectF; 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.CameraMetadata; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.params.StreamConfigurationMap; import android.media.Image; import android.media.ImageReader; import android.media.MediaRecorder; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.os.PersistableBundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.ActivityCompat; import android.util.Size; import android.util.SparseIntArray; import android.view.Surface; import android.view.TextureView; import android.view.View; import android.widget.Button; import android.widget.Toast; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; /** * Created by lxb on 2017/5/3. */ public class VideoActivity extends Activity { private MediaRecorder mMediaRecorder; private ImageReader mImageReader; private boolean isOpened; private TextureView textureView; private Size imageDimension; private CameraDevice mCameraDevice; private Semaphore mCameraOpenCloseLock = new Semaphore(1); private Handler mBackgroundHandler; private HandlerThread mBackgroundThread; private CameraCaptureSession mCaptureSession; private CaptureRequest mPreviewRequest; private boolean mFlashSupported; private String cameraId; private static final int STATE_PREVIEW = 0; private static final int STATE_WAITING_LOCK = 1; private static final int STATE_WAITING_PRECAPTURE = 2; private static final int STATE_WAITING_NON_PRECAPTURE = 3; private static final int STATE_PICTURE_TAKEN = 4; private int mState = STATE_PREVIEW; private int mSensorOrientation; private CaptureRequest.Builder mPreviewRequestBuilder; static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 90); ORIENTATIONS.append(Surface.ROTATION_90, 0); ORIENTATIONS.append(Surface.ROTATION_180, 270); ORIENTATIONS.append(Surface.ROTATION_270, 180); } private int nOperation = -1; //1 拍照 2:录像 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video_layout); initView(); nOperation = getIntent().getIntExtra("type",1); } private void initView() { textureView = (TextureView) findViewById(R.id.texture_video_preview); Button photo = (Button) findViewById(R.id.photo); photo.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { takePicture(); } }); } /** * 实时预览监听器 */ TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() { public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) { openCamera(); } public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) { configureTransform(width, height); } public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) { return true; } public void onSurfaceTextureUpdated(SurfaceTexture texture) { } }; /** * 当texture size发生改变时重新配置 * * @param viewWidth * @param viewHeight */ private void configureTransform(int viewWidth, int viewHeight) { if (null == textureView || null == imageDimension) return; int rotation = getWindowManager().getDefaultDisplay().getRotation(); Matrix matrix = new Matrix(); RectF viewRect = new RectF(0, 0, viewWidth, viewHeight); RectF bufferRect = new RectF(0, 0, imageDimension.getHeight(), imageDimension.getWidth()); float centerX = viewRect.centerX(); float centerY = viewRect.centerY(); if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) { bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY()); matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL); float scale = Math.max((float) viewHeight / imageDimension.getHeight(), (float) viewWidth / imageDimension.getWidth()); matrix.postScale(scale, scale, centerX, centerY); matrix.postRotate(90 * (rotation - 2), centerX, centerY); } else if (Surface.ROTATION_180 == rotation) { matrix.postRotate(180, centerX, centerY); } textureView.setTransform(matrix); } private void startBackgroundThread() { mBackgroundThread = new HandlerThread("CameraBackground"); mBackgroundThread.start(); mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); } private void stopBackgroundThread() { mBackgroundThread.quitSafely(); try { mBackgroundThread.join(); mBackgroundThread = null; mBackgroundHandler = null; } catch (Exception e) { e.printStackTrace(); } } private void openCamera() { if (isOpened) { return; } try { if (!mCameraOpenCloseLock.tryAcquire(4, TimeUnit.SECONDS)) throw new RuntimeException("Time out waiting to lock camera opening."); } catch (InterruptedException e) { e.printStackTrace(); } isOpened = true; CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { cameraId = manager.getCameraIdList()[0];//这个可能会有很多个,但是通常都是两个,第一个是后置,第二个是前置; CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); assert map != null; imageDimension = map.getOutputSizes(SurfaceTexture.class)[0]; if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { // TODO: Consider calling // ActivityCompat#requestPermissions // here to request the missing permissions, and then overriding // public void onRequestPermissionsResult(int requestCode, String[] permissions, // int[] grantResults) // to handle the case where the user grants the permission. See the documentation // for ActivityCompat#requestPermissions for more details. return; } manager.openCamera(cameraId, new CameraDevice.StateCallback() { @Override public void onOpened(final CameraDevice camera) { mCameraDevice = camera; createCameraPreview(camera); } @Override public void onDisconnected(CameraDevice camera) { camera.close(); mCameraDevice = null; } @Override public void onError(CameraDevice camera, int error) { camera.close(); mCameraDevice = null; } }, mBackgroundHandler); //这个指定其后台运行,如果直接UI线程也可以,直接填null; } catch (CameraAccessException e) { e.printStackTrace(); } } protected void createCameraPreview(final CameraDevice cameraDevice) { try { if (null == cameraDevice) { return; } final CaptureRequest.Builder captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD); SurfaceTexture texture = textureView.getSurfaceTexture(); texture.setDefaultBufferSize(imageDimension.getWidth(), imageDimension.getHeight()); Surface textureSurface = new Surface(texture); captureRequestBuilder.addTarget(textureSurface); switch(nOperation){ case 1: setUpImageReader(); Surface imageSurface = mImageReader.getSurface(); captureRequestBuilder.addTarget(imageSurface); List<Surface> surfaceList = Arrays.asList(textureSurface, imageSurface); createPhotoView(cameraDevice, surfaceList, captureRequestBuilder); break; case 2: setUpMediaRecorder(); Surface recorderSurface = mMediaRecorder.getSurface(); captureRequestBuilder.addTarget(recorderSurface); List<Surface> surfaceList2 = Arrays.asList(textureSurface, recorderSurface); createVideoView(cameraDevice,surfaceList2,captureRequestBuilder); break; } /*List<Surface> surfaceList = Arrays.asList(textureSurface, recorderSurface); cameraDevice.createCaptureSession(surfaceList, new CameraCaptureSession.StateCallback() {//配置要接受图像的surface @Override public void onConfigured(CameraCaptureSession cameraCaptureSession) { captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); try { cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, mBackgroundHandler);//成功配置后,便开始进行相机图像的监听 } catch (CameraAccessException e) { e.printStackTrace(); } mMediaRecorder.start(); } @Override public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { } }, null);*/ } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 创建录像视图 * * @param cameraDevice * @param surfaceList * @param captureRequestBuilder */ private void createVideoView(final CameraDevice cameraDevice, List<Surface> surfaceList, final CaptureRequest.Builder captureRequestBuilder) { try { cameraDevice.createCaptureSession(surfaceList, new CameraCaptureSession.StateCallback() {//配置要接受图像的surface @Override public void onConfigured(CameraCaptureSession cameraCaptureSession) { captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); try { cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, mBackgroundHandler);//成功配置后,便开始进行相机图像的监听 } catch (CameraAccessException e) { e.printStackTrace(); } mMediaRecorder.start(); } @Override public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { } }, null); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 初始化媒体类 */ private void setUpMediaRecorder() { mMediaRecorder = new MediaRecorder(); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP); mMediaRecorder.setMaxFileSize(0); mMediaRecorder.setOrientationHint(0); mMediaRecorder.setOutputFile(CommonUtils.createFile(CommonUtils.getCurTime() + ".mp4").getAbsolutePath()); try { mMediaRecorder.prepare(); } catch (IOException e) { e.printStackTrace(); } } /** * 创建拍照视图 * * @param cameraDevice * @param surface * @param captureRequestBuilder */ private void createPhotoView(final CameraDevice cameraDevice, List<Surface> surface, final CaptureRequest.Builder captureRequestBuilder) { try { mPreviewRequestBuilder = captureRequestBuilder; mCameraDevice.createCaptureSession(surface, new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { if (null == mCameraDevice) return; mCaptureSession = cameraCaptureSession; try { captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); setAutoFlash(captureRequestBuilder); mPreviewRequest = captureRequestBuilder.build(); mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler); } catch (Exception e) { e.printStackTrace(); Toast.makeText(AppContext.sApp, "开启相机预览失败,再试一次吧", Toast.LENGTH_LONG).show(); // mFinishCalled = true; finish(); } } @Override public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { Toast.makeText(AppContext.sApp, "开启相机预览失败,再试一次吧", Toast.LENGTH_LONG).show(); //mFinishCalled = true; finish(); } }, null); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 拍照的所需的对象资源 */ private void setUpImageReader() { mImageReader = ImageReader.newInstance(imageDimension.getWidth(), imageDimension.getHeight(), ImageFormat.YUV_420_888, 10); mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) { Image image = reader.acquireLatestImage(); if (image != null) { image.close(); } } }, mBackgroundHandler); } /** * 设置闪光灯 * * @param requestBuilder */ private void setAutoFlash(CaptureRequest.Builder requestBuilder) { try { CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); mFlashSupported = available == null ? false : available; /** * 若相机支持自动开启/关闭闪光灯,则使用. 否则闪光灯总是关闭的. */ if (mFlashSupported) requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * 拍照的回调 */ CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) { process(partialResult); } @Override public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { process(result); } }; /** * 处理拍照的过程 * * @param result */ private void process(CaptureResult result) { switch (mState) { case STATE_PREVIEW: { // We have nothing to do when the camera preview is working normally. break; } case STATE_WAITING_LOCK: { Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); /** * 判断可以立即拍摄的autoFocusState增加到4种. */ if (afState == null) { captureStillPicture(); } else if (CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED == afState || CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState || CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) { // CONTROL_AE_STATE can be null on some devices Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); /** * 判断可以立即拍摄的autoExposureState增加到4种. */ if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED || aeState == CaptureResult.CONTROL_AE_STATE_LOCKED || aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED) { mState = STATE_PICTURE_TAKEN; captureStillPicture(); } else { runPrecaptureSequence(); } } break; } case STATE_WAITING_PRECAPTURE: { // CONTROL_AE_STATE can be null on some devices Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE || aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) { mState = STATE_WAITING_NON_PRECAPTURE; } break; } case STATE_WAITING_NON_PRECAPTURE: { // CONTROL_AE_STATE can be null on some devices Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) { mState = STATE_PICTURE_TAKEN; captureStillPicture(); } break; } } } /** * 捕获图片 */ private void captureStillPicture() { try { if (null == mCameraDevice) return; // This is the CaptureRequest.Builder that we use to take a picture. final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureBuilder.addTarget(mImageReader.getSurface()); // Use the same AE and AF modes as the preview. captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); setAutoFlash(captureBuilder); // Orientation int rotation = getWindowManager().getDefaultDisplay().getRotation(); captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation)); CameraCaptureSession.CaptureCallback CaptureCallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { System.out.println("757----------------filepath:"); unlockFocus(); } }; mCaptureSession.stopRepeating(); mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null); } catch (Exception e) { e.printStackTrace(); } } private void runPrecaptureSequence() { try { mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); mState = STATE_WAITING_PRECAPTURE; mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler); } catch (Exception e) { e.printStackTrace(); } } private int getOrientation(int rotation) { return (ORIENTATIONS.get(rotation) + mSensorOrientation + 270) % 360; } /** * 开始拍照 */ private void takePicture() { try { // This is how to tell the camera to lock focus. mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START); // Tell #mCaptureCallback to wait for the lock. mState = STATE_WAITING_LOCK; mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler); } catch (Exception e) { e.printStackTrace(); } } /** * 释放照相机 */ private void unlockFocus() { try { // Reset the auto-focus trigger mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL); setAutoFlash(mPreviewRequestBuilder); mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler); // After this, the camera will go back to the normal state of preview. mState = STATE_PREVIEW; mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler); } catch (Exception e) { e.printStackTrace(); } } @Override public void onResume() { super.onResume(); startBackgroundThread(); if (textureView.isAvailable()) { openCamera(); } else { textureView.setSurfaceTextureListener(mSurfaceTextureListener); } } @Override protected void onPause() { super.onPause(); releaseRes(); stopBackgroundThread(); finish(); } @Override protected void onDestroy() { super.onDestroy(); } private void releaseRes() { try { if (mCameraDevice != null) { mCameraDevice.close(); mCameraDevice = null; } if (mMediaRecorder != null) { mMediaRecorder.stop(); mMediaRecorder.reset(); mMediaRecorder.release(); mMediaRecorder = null; } } catch (Exception e) { e.printStackTrace(); } } }
布局文件:
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextureView android:id="@+id/texture_video_preview" android:layout_width="match_parent" android:layout_height="match_parent" /> <!--<ImageView android:id="@+id/iv_video_button" android:layout_width="50dp" android:layout_height="50dp" android:layout_gravity="center_horizontal|bottom" android:layout_marginBottom="40dp" android:src="@drawable/selector" />--> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="trigger" android:layout_gravity="center_horizontal|bottom" android:layout_marginBottom="40dp" android:text="stop"/> <Button android:id="@+id/photo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="trigger" android:layout_gravity="left|bottom" android:layout_marginBottom="40dp" android:text="photo"/> </FrameLayout>
测试方法:
private void photo(){ /* Intent intent; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { intent = new Intent(MainActivity.this, PhotoActivity.class); intent.putExtra("type",1); } else { System.out.println("权限不够"); return; } //文件保存的路径和名称 //intent.putExtra("file", mFile.toString()); //拍照时的提示文本 // intent.putExtra("hint", "请将证件放入框内。将裁剪图片,只保留框内区域的图像"); //是否使用整个画面作为取景区域(全部为亮色区域) // intent.putExtra("hideBounds", true); //最大允许的拍照尺寸(像素数) //intent.putExtra("maxPicturePixels", 3840 * 2160); startActivityForResult(intent, AppContext.TAKE_PHOTO_CUSTOM);*/ Intent intent = new Intent(MainActivity.this, VideoActivity.class); intent.putExtra("type",1); startActivity(intent); } private void vedioTape(){ Intent intent = new Intent(MainActivity.this, VideoActivity.class); intent.putExtra("type",2); startActivity(intent); }