简体   繁体   中英

What is the best way to switch between Image Capture and Video Capture on Android?

I'm trying to create an activity that will record video and take pictures at the same time. I'm currently using the CameraX API for its simplicity and would like to stick with it instead of the Camera2 or anything else. For the layout, I have two buttons, one for pictures, another for video and a TextureView. My question is what is the best way of switching between the ImageCapture and VideoCapture objects?

I have to bind them to the lifecycle of the activity in order for them to work, but only one will work at a time. Otherwise, I get this problem. I was trying to make the buttons do it for me, but then that requires me to change what the buttons do after I switch the Capture objects. I was also thinking of using fragments, but I feel like there is an easier way. I would also prefer the buttons to be combined into one if possible.

   private void startCamera() {
        CameraX.unbindAll();
        Rational aspectRatio = new Rational(textureView.getWidth(), textureView.getHeight());
        Size screen = new Size(textureView.getWidth(), textureView.getHeight());
        PreviewConfig pConfig = new PreviewConfig.Builder().setTargetAspectRatio(aspectRatio).setTargetResolution(screen).build();
        Preview preview = new Preview(pConfig);
        preview.setOnPreviewOutputUpdateListener(new Preview.OnPreviewOutputUpdateListener() {
            @Override
            public void onUpdated(Preview.PreviewOutput output) {
                ViewGroup parent = (ViewGroup) textureView.getParent();
                parent.removeView(textureView);
                parent.addView(textureView);

                textureView.setSurfaceTexture(output.getSurfaceTexture());
                updateTransform();
            }
        });

        setupPictures(preview);
        setupVideos(preview);

    }
    
    private void setupPictures(Preview preview) {
        ImageCaptureConfig imageCaptureConfig = new ImageCaptureConfig.Builder().setCaptureMode(ImageCapture.CaptureMode.MIN_LATENCY).
                setTargetRotation(getWindowManager().getDefaultDisplay().getRotation()).build();

        final ImageCapture imgCap = new ImageCapture(imageCaptureConfig);
        
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
                String imageFileName = "JPEG_" + timeStamp + "_";
                File storageDir = new File("/storage/emulated/0/DCIM/Camera/"); //getExternalFilesDir(Environment.DIRECTORY_PICTURES);
                File image;
                try {
                    image = File.createTempFile(
                            imageFileName,  //
                            ".jpg",         //
                            storageDir      // directory
                    );
                    imgCap.takePicture(image, new ImageCapture.OnImageSavedListener() {
                        @Override
                        public void onImageSaved(@NonNull File file) {
                            String msg = "Pic capture at " + file.getAbsolutePath();
                            Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show();
                        }

                        @Override
                        public void onError(@NonNull ImageCapture.UseCaseError useCaseError, @NonNull String message, @Nullable Throwable cause) {
                            String msg = "Pic Capture failed: " + message;
                            Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show();
                            if (cause != null) {
                                cause.printStackTrace();
                            }
                        }
                    });

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        CameraX.bindToLifecycle(this, preview, imgCap);  // I want this to be triggered by one of the
                                                         // two buttons and same with the other 
                                                         // but buttons also need to take pic or vid
    }

    private void setupVideos(Preview preview) {
        VideoCaptureConfig videoCaptureConfig = new VideoCaptureConfig.Builder().
                setLensFacing(CameraX.LensFacing.BACK).
                setTargetRotation(getWindowManager().getDefaultDisplay().getRotation()).build();
        
        final boolean[] recording = new boolean[]{false};
        
        @SuppressLint("RestrictedApi")
        final VideoCapture vidCap = new VideoCapture(videoCaptureConfig);

        CameraX.bindToLifecycle(this, preview, vidCap);

        findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
            @SuppressLint("RestrictedApi")
            @Override
            public void onClick(View v) {
                if (!recording[0]) {
                    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
                    String imageFileName = "MP4_" + timeStamp + "_";
                    File storageDir = new File("/storage/emulated/0/DCIM/Camera/"); //getExternalFilesDir(Environment.DIRECTORY_PICTURES);
                    File video;
                    try {
                        video = File.createTempFile(
                                imageFileName,  /* prefix */
                                ".mp4",         /* suffix */
                                storageDir      /* directory */
                        );
                        vidCap.startRecording(video, new VideoCapture.OnVideoSavedListener() {
                            @Override
                            public void onVideoSaved(File file) {
                                String msg = "Vid capture at " + file.getAbsolutePath();
                                Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show();
                            }

                            @Override
                            public void onError(VideoCapture.UseCaseError useCaseError, String message, @Nullable Throwable cause) {
                                String msg = "Vid Capture failed: " + message;
                                Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show();
                                if (cause != null) {
                                    cause.printStackTrace();
                                }
                            }
                        });
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    recording[0] = true;
                } else {
                    vidCap.stopRecording();
                    recording[0] = false;
                }
            }
        });
        CameraX.bindToLifecycle(this, preview, vidCap);
    }

Again, my layout is just two buttons at the top and the TextureView. You will also need the audio, camera, and external storage write permission in both the manifest file then check for them within the activity.

So I decided to go with using fragments to try and solve my problem, but now the issue that I'm running into I believe is that when I switch back and forth between the video and picture fragments is that the thread that takes care of connecting to the PictureCapture or VideoCapture to the actual "camera" and texture, can't disconnect and reconnect the fragments properly. I'm not sure if this is what is actually happening, but I don't have any other ideas at the moment.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM