Imitating the source code of Tiktok short video system to take photos and videos based on CameraX

Background: Recently, I was working on an APP video upload project. Due to the use of my own server, there are certain memory restrictions. At present, most mobile phones have a large resolution, and the video captured is several megabytes in a few seconds. How to compress the recorded video is the same as wechat. Therefore, I consulted some materials and wrote it down to prevent myself from forgetting!

I used to use Camera1 and Camera2 to develop camera functions, which need to call very complex API s. Moreover, due to the serious fragmentation of Android phones, different phones have different support for camera functions. Therefore, many companies doing camera related applications will package their own camera libraries to simplify the use steps of cameras and deal with compatibility problems.

Later, Google launched CameraX to simplify the use of camera API. Compatible with Android 5.0 at most, Camera2 is also called at the bottom, but it is simpler to use than Camera2, and it can bind the life cycle, so it can automatically handle the opening and release of the camera.

dependencies {
   // CameraX core library is implemented by camera2
    implementation "androidx.camera:camera-camera2:1.0.0-beta03"
    // You can use CameraView
    implementation "androidx.camera:camera-view:1.0.0-alpha10"
    // You can use vendor extensions
    implementation "androidx.camera:camera-extensions:1.0.0-alpha10"
    //Life cycle Library of camerax
    implementation "androidx.camera:camera-lifecycle:1.0.0-beta03"
    }

Configuration status
Preview configuration: preview is used to display the preview screen when the camera previews.

Preview preview = new Preview.Builder()
                //Set aspect ratio
                .setTargetAspectRatio(screenAspectRatio)
                //Sets the rotation of the current screen
                .setTargetRotation(rotation)
                .build();

Camera configuration: ImageCapture is used to take pictures and save them

ImageCapture imageCapture = new ImageCapture.Builder()
                //Optimizing capture speed may reduce picture quality
                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                //Set aspect ratio
                .setTargetAspectRatio(screenAspectRatio)
                //Sets the initial rotation angle
                .setTargetRotation(rotation)
                .build();

Video recording settings: VideoCapture is used to record and save video. Set one aspect ratio and resolution. Do not set it at the same time, otherwise an error will be reported. Set according to the actual requirements. If the aspect ratio is required, set the aspect ratio; otherwise, set the resolution

This place is the key point. When recording video, you can set the resolution, frame rate and bit rate to modify the size of the recorded video

VideoCapture videoCapture = new VideoCaptureConfig.Builder()
                //Sets the current rotation
                .setTargetRotation(rotation)
                //Set aspect ratio
                .setTargetAspectRatio(screenAspectRatio)
                //resolving power
                //.setTargetResolution(resolution)
                //The higher the video frame rate, the larger the video volume
                .setVideoFrameRate(25)
                //The larger the bit rate, the larger the video volume
                .setBitRate(3 * 1024 * 1024)
                .build();

Bind to lifecycle: ProcessCameraProvider is a singleton class that can bind the lifecycle of a camera to any lifecycle owner class. AppCompatActivity and Fragment are both lifecycle owners

//Future represents an asynchronous task. ListenableFuture can listen to this task and execute callback when the task is completed
 ListenableFuture<ProcessCameraProvider> cameraProviderFuture =
                ProcessCameraProvider.getInstance(this);
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();

//You must unbind before rebinding
cameraProvider.unbindAll();

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

OK, the configuration of preview, photographing, video recording and binding to the life cycle are completed

When previewing, it needs to be displayed on a View control. CameraX provides a PreviewView to display the preview screen. Its interior encapsulates TextureView and SurfaceView. You can choose whether to use TextureView or SurfaceView to display it according to different modes.

Add PreviewView to xml and attach it to the previewinstance created earlier in the code

<androidx.camera.view.PreviewView
        android:id="@+id/view_finder"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

preview.setSurfaceProvider(mPreviewView.createSurfaceProvider(camera .getCameraInfo()));
In this way, when we enter this page, we can see the preview effect of the camera. Next, we will perform the functions of photographing and recording

Now let's see how to take photos

//Create the file address where the picture is saved
  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);
      }
  });
//Add previously saved files to media
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. Call the takePicture method of ImageCapture to take pictures
2. Pass in a file address to save the photos
3. The onImageSaved method is the callback after the photo has been taken and coexisted
4. The onFileSaved method adds the previously saved file to the media, and finally jumps to the preview interface.

record video
It's similar to taking pictures

//Create the file address where the video is saved
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. Use VideoCapture's startRecording method to record video
2. Pass in a File file to save the video,
3. After recording, call back the onVideoSaved method and return an instance of the file.
4. Call the onFileSaved method to add the previously saved file to the media, and finally jump to the preview interface.
5. When the recording time is reached, you need to call videocapture stopRecording(); To stop the recording.

Tags: Java Android Vue Design Pattern

Posted by Loldongs on Mon, 23 May 2022 01:13:51 +0300