Android Jetpack架构组件(五)CameraX视频录制

添加Gradle依赖

// Use the most recent version of CameraX, currently that is alpha04
def camerax_core_version = "1.0.0-beta03"
def camerax_version = "1.0.0-alpha10"
implementation "androidx.camera:camera-core:${camerax_core_version}"
implementation "androidx.camera:camera-camera2:${camerax_core_version}"
// If you want to use the CameraX View class
implementation "androidx.camera:camera-view:${camerax_version}"
// If you want to use the CameraX Extensions library
implementation "androidx.camera:camera-extensions:${camerax_version}"
// If you want to use the CameraX Lifecycle library
implementation "androidx.camera:camera-lifecycle:${camerax_version}"

//视频播放组件
implementation 'com.google.android.exoplayer:exoplayer-core:2.10.4'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.10.4'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.10.4'

implementation 'com.github.chrisbanes:PhotoView:2.3.0'
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'

权限声明

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
private static final String[] PERMISSIONS = new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO};
private ArrayList<String> deniedPermission = new ArrayList<>();

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
    ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSION_CODE);
    ...
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == PERMISSION_CODE) {
        deniedPermission.clear();
        for (int i = 0; i < permissions.length; i++) {
            String permission = permissions[i];
            int result = grantResults[i];
            if (result != PackageManager.PERMISSION_GRANTED) {
                deniedPermission.add(permission);
            }
        }

        if (deniedPermission.isEmpty()) {
            //Toast.makeText(CaptureActivity.this, "all permission allowed", Toast.LENGTH_SHORT).show();
            initCamera();
        } else {
            new AlertDialog.Builder(this)
                    .setMessage(getString(R.string.capture_permission_message))
                    .setNegativeButton(getString(R.string.capture_permission_no), new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.dismiss();
                            CameraXCaptureActivity.this.finish();
                        }
                    })
                    .setPositiveButton(getString(R.string.capture_permission_ok), new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            String[] denied = new String[deniedPermission.size()];
                            ActivityCompat.requestPermissions(CameraXCaptureActivity.this, deniedPermission.toArray(denied), PERMISSION_CODE);
                        }
                    }).create().show();
        }
    }
}

配置CameraX

private void initCamera() {

    executor = ContextCompat.getMainExecutor(this);

    cameraProviderFuture = ProcessCameraProvider.getInstance(this);
    cameraProviderFuture.addListener(() -> {
        try {
            ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
            bindPreview(cameraProvider);
        } catch (ExecutionException | InterruptedException e) {
            // No errors need to be handled for this Future.
            // This should never be reached.
        }
    }, executor);
}
@SuppressLint("RestrictedApi")
private void bindPreview(ProcessCameraProvider cameraProvider) {
    CameraX.unbindAll();
    cameraSelector = new CameraSelector.Builder()
            .requireLensFacing(CameraSelector.LENS_FACING_BACK)
            .build();
    Preview preview = new Preview.Builder()
            .setCameraSelector(cameraSelector) //前后摄像头
            .setTargetAspectRatio(AspectRatio.RATIO_16_9) //宽高比
            .setTargetRotation(rotation) //旋转角度
            //.setTargetResolution(resolution) //分辨率
            .build();

    imageCapture = new ImageCapture.Builder()
            .setCameraSelector(cameraSelector)
            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
            .setTargetRotation(rotation)
            //.setTargetResolution(resolution)
            .build();

    videoCapture = new VideoCaptureConfig.Builder()
            .setCameraSelector(cameraSelector)
            .setTargetAspectRatio(AspectRatio.RATIO_16_9)
            .setTargetRotation(rotation)
            //.setTargetResolution(resolution)
            //视频帧率
            .setVideoFrameRate(25)
            //bit率
            .setBitRate(10440).build();

    //Caused by: java.lang.IllegalArgumentException: No supported surface combination is found for camera device - Id : 0.  May be attempting to bind too many use cases.
    //cameraSelector与videoCapture不能同时绑定
    //Camera camera = cameraProvider.bindToLifecycle(this, cameraSelector, imageCapture, videoCapture, preview);

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

    CameraInfo cameraInfo = camera.getCameraInfo();

    preview.setSurfaceProvider(mBinding.previewView.createSurfaceProvider(cameraInfo));
}

若报错:IllegalArgumentException: No supported surface combination is found,如下:

java.lang.IllegalArgumentException: No supported surface combination is found for camera device - Id : 0.  May be attempting to bind too many use cases.
    at androidx.camera.camera2.impl.Camera2DeviceSurfaceManager.getSuggestedResolutions(Camera2DeviceSurfaceManager.java:176)

解决:cameraSelector与videoCapture不能同时绑定

参考:https://github.com/android/camera-samples/issues/38
参考:https://stackoverflow.com/questions/57126429/an-error-occurred-by-camerax-bindtolifecycle

拍照和录像

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mBinding = DataBindingUtil.setContentView(this, R.layout.activity_camerax_capture);

    mBinding.recordView.setOnRecordListener(new RecordView.onRecordListener() {

        @Override
        public void onClick() {
            takingPicture = true;
            File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), System.currentTimeMillis() + ".jpeg");
            ImageCapture.OutputFileOptions outputFileOptions =
                    new ImageCapture.OutputFileOptions.Builder(file).build();
            mBinding.captureTips.setVisibility(View.INVISIBLE);
            imageCapture.takePicture(outputFileOptions, executor, new ImageCapture.OnImageSavedCallback() {
                @Override
                public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
                    String msg = "图片保存成功: " + file.getAbsolutePath();
                    Toast.makeText(CameraXCaptureActivity.this, msg, Toast.LENGTH_SHORT).show();
                    Log.i(CommonConstants.TAG, msg);
                    onFileSaved(file);
                }

                @Override
                public void onError(@NonNull ImageCaptureException exception) {
                    String msg = "图片保存失败: " + exception.getMessage();
                    showErrorToast(msg);
                    Log.e(CommonConstants.TAG, msg);
                }
            });
        }

        @SuppressLint("RestrictedApi")
        @Override
        public void onLongClick() {
            takingPicture = false;
            File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), System.currentTimeMillis() + ".mp4");
            videoCapture.startRecording(file, executor, new VideoCapture.OnVideoSavedCallback() {
                @Override
                public void onVideoSaved(@NonNull File file) {
                    onFileSaved(file);
                }

                @Override
                public void onError(int videoCaptureError, @NonNull String message, @Nullable Throwable cause) {
                    String msg = "图片保存失败: " + message;
                    showErrorToast(msg);
                    Log.e(CommonConstants.TAG, msg);
                }
            });
        }

        @SuppressLint("RestrictedApi")
        @Override
        public void onFinish() {
            videoCapture.stopRecording();
        }
    });

}

保存和预览

private void onFileSaved(File file) {
    outputFilePath = file.getAbsolutePath();
    String mimeType = takingPicture ? "image/jpeg" : "video/mp4";
    MediaScannerConnection.scanFile(this, new String[]{outputFilePath}, new String[]{mimeType}, null);
    CameraXPreviewActivity.startActivityForResult(this, outputFilePath, !takingPicture, "完成");
}

预览照片和录像

private void previewImage(String previewUrl) {
    mPreviewBinding.photoView.setVisibility(View.VISIBLE);
    GlideApp.with(this).load(previewUrl).into(mPreviewBinding.photoView);
}

private void previewVideo(String previewUrl) {
    mPreviewBinding.playerView.setVisibility(View.VISIBLE);
    player = ExoPlayerFactory.newSimpleInstance(this, new DefaultRenderersFactory(this), new DefaultTrackSelector(), new DefaultLoadControl());

    Uri uri = null;
    File file = new File(previewUrl);
    if (file.exists()) {
        DataSpec dataSpec = new DataSpec(Uri.fromFile(file));
        FileDataSource fileDataSource = new FileDataSource();
        try {
            fileDataSource.open(dataSpec);
            uri = fileDataSource.getUri();
        } catch (FileDataSource.FileDataSourceException e) {
            e.printStackTrace();
        }
    } else {
        uri = Uri.parse(previewUrl);
    }

    ProgressiveMediaSource.Factory factory = new ProgressiveMediaSource.Factory(
            new DefaultDataSourceFactory(this, Util.getUserAgent(this, getPackageName())));
    ProgressiveMediaSource mediaSource = factory.createMediaSource(uri);
    player.prepare(mediaSource);
    player.setPlayWhenReady(true);
    mPreviewBinding.playerView.setPlayer(player);
}

版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/03/25/android-jetpack-architecture-components-camerax-video-recording/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
海报
Android Jetpack架构组件(五)CameraX视频录制
添加Gradle依赖 // Use the most recent version of CameraX, currently that is alpha04 def camerax_core_version = "1.0.0-beta03" def camerax_……
<<上一篇
下一篇>>
文章目录
关闭
目 录