基于CameraX完成拍照和拍视频

背景:最近在做一个APP视频上传的项目,由于使用的是自家的服务器,内存有一定的限制,而目前的手机大多分辨率较大,拍出来的视频几秒钟就好几兆,如何让压缩录制的视频,跟微信一样,因此查阅了一些资料,特此记下,防止自己遗忘!

我之前大多使用Camera1和Camera2开发相机功能的时候需要调用非常复杂的API,而且由于Android手机的碎片化严重,不同手机对相机功能的支持度也不一样,因此很多做相机相关应用的公司都会封装自己的相机库来简化相机的使用步骤和处理兼容性问题。

之后谷歌为简化相机API的使用,推出了CameraX。最多兼容到Android 5.0,底层调用的也是Camera2,不过比Camera2用起来更简单,而且可以绑定生命周期,从而可以自动的处理相机的开启释放等工作。

dependencies {
   // CameraX 核心库使用 camera2 实现
    implementation "androidx.camera:camera-camera2:1.0.0-beta03"
    // 可以使用CameraView
    implementation "androidx.camera:camera-view:1.0.0-alpha10"
    // 可以使用供应商扩展
    implementation "androidx.camera:camera-extensions:1.0.0-alpha10"
    //camerax的生命周期库
    implementation "androidx.camera:camera-lifecycle:1.0.0-beta03"
    }

配置状态
预览配置:Preview用于相机预览的时候显示预览画面。

Preview preview = new Preview.Builder()
                //设置宽高比
                .setTargetAspectRatio(screenAspectRatio)
                //设置当前屏幕的旋转
                .setTargetRotation(rotation)
                .build();

照相配置:ImageCapture 用于拍照,并将图片保存

ImageCapture imageCapture = new ImageCapture.Builder()
                //优化捕获速度,可能降低图片质量
                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                //设置宽高比
                .setTargetAspectRatio(screenAspectRatio)
                //设置初始的旋转角度
                .setTargetRotation(rotation)
                .build();

录制视频设置:VideoCapture 用来录制视频和保存视频,宽高比和分辨率设置一个就可以了,不要同时设置否则报错。根据实际的需求来设置,如果对宽高比要求高就设置宽高比,反之就设置分辨率

这个地方是重点,录制视频的时候可以设置分辨率、帧率和bit率进行修改录制视频的大小

VideoCapture videoCapture = new VideoCaptureConfig.Builder()
                //设置当前旋转
                .setTargetRotation(rotation)
                //设置宽高比
                .setTargetAspectRatio(screenAspectRatio)
                //分辨率
                //.setTargetResolution(resolution)
                //视频帧率  越高视频体积越大
                .setVideoFrameRate(25)
                //bit率  越大视频体积越大
                .setBitRate(3 * 1024 * 1024)
                .build();

绑定到生命周期:ProcessCameraProvider 是一个单例类,可以把相机的生命周期绑定到任何LifecycleOwner类中。AppCompatActivity和Fragment都是LifecycleOwner

//Future表示一个异步的任务,ListenableFuture可以监听这个任务,当任务完成的时候执行回调
 ListenableFuture cameraProviderFuture =
                ProcessCameraProvider.getInstance(this);
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();

//重新绑定之前必须先取消绑定
cameraProvider.unbindAll();

Camera camera = cameraProvider.bindToLifecycle(CameraActivity.this,
                cameraSelector,preview,imageCapture,videoCapture);

OK预览,照相,录视频的配置和绑定到生命周期的工作就完成了

预览的时候需要显示到一个View控件上吧,CameraX中提供了一个PreviewView用来显示预览画面。其内部封装了TextureView和SurfaceView,可以根据不同的模式来选择其内部使用TextureView还是SurfaceView来显示。

xml中添加PreviewView,并在代码中将其附加到前面创建出来的Preview这个实例上

preview.setSurfaceProvider(mPreviewView.createSurfaceProvider(camera .getCameraInfo()));
这样当我们进入该页面的时候就可以看到相机的预览效果呢,接下来就是执行拍照和录制的功能了

现在我们就一起看看如何实现拍照

//创建图片保存的文件地址
  File file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath(),
          System.currentTimeMillis() + ".jpeg");
  ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(file).build();
  mImageCapture.takePicture(outputFileOptions,mExecutorService , new ImageCapture.OnImageSavedCallback() {
      @Override
      public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
          Uri savedUri = outputFileResults.getSavedUri();
          if(savedUri == null){
              savedUri = Uri.fromFile(file);
          }
          outputFilePath = file.getAbsolutePath();
          onFileSaved(savedUri);
      }

      @Override
      public void onError(@NonNull ImageCaptureException exception) {
          Log.e(TAG, "Photo capture failed: "+exception.getMessage(), exception);
      }
  });
//将前面保存的文件添加到媒体中
private void onFileSaved(Uri savedUri) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            sendBroadcast(new Intent(android.hardware.Camera.ACTION_NEW_PICTURE, savedUri));
        }
        String mimeTypeFromExtension = MimeTypeMap.getSingleton().getMimeTypeFromExtension(MimeTypeMap
                .getFileExtensionFromUrl(savedUri.getPath()));
        MediaScannerConnection.scanFile(getApplicationContext(),
                new String[]{new File(savedUri.getPath()).getAbsolutePath()},
                new String[]{mimeTypeFromExtension}, new MediaScannerConnection.OnScanCompletedListener() {
                    @Override
                    public void onScanCompleted(String path, Uri uri) {
                        Log.d(TAG, "Image capture scanned into media store: $uri"+uri);
                    }
                });
        PreviewActivity.start(this, outputFilePath, !takingPicture);
    }

1、调用ImageCapture的takePicture方法来拍照
2、传入一个文件地址用来保存拍好的照片
3、onImageSaved方法是照片已经拍好并存好之后的回调
4、onFileSaved方法中将前面保存的文件添加到媒体中,最后跳转到预览界面。

录制视频
和拍照类似

//创建视频保存的文件地址
File file = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath(),
        System.currentTimeMillis() + ".mp4");
mVideoCapture.startRecording(file, Executors.newSingleThreadExecutor(), new VideoCapture.OnVideoSavedCallback() {
    @Override
    public void onVideoSaved(@NonNull File file) {
        outputFilePath = file.getAbsolutePath();
        onFileSaved(Uri.fromFile(file));
    }

    @Override
    public void onError(int videoCaptureError, @NonNull String message, @Nullable Throwable cause) {
        Log.i(TAG,message);
    }
});

1、使用VideoCapture的startRecording方法来录视频
2、传入一个File文件用来保存视频,
3、录制完成之后回调onVideoSaved方法,并返回该文件的实例。
4、调用onFileSaved方法将前面保存的文件添加到媒体中,最后跳转到预览界面。
5、到达录制时间的时候,需要调用videoCapture.stopRecording();方法来停止录像。

现在基本就实现了拍照和录制视频的功能了,下一篇再记录一下CameraView的使用知识!

注:本文章知识大部分都采集于大佬们的博客,我只是在此做知识笔记,防止自己遗忘!不用于任何商业用途!
原著地址:https://zhuanlan.zhihu.com/p/...

你可能感兴趣的:(android)