开发者社区 问答 正文

Android camera录制视频没有preview为什么不能成功?API21

http://developer.android.com/guide/topics/media/camera.html

Note: It is possible to use MediaRecorder without creating a camera preview first and skip the first few steps of this process. However, since users typically prefer to see a preview before starting a recording, that process is not discussed here.

Android的文档如上,我想不创建一个照相机的预览界面,按理来说是可行的。我参考文档里的步骤,省去startpreview.核心代码如下

  public void init(){
        CameraManager manager = (CameraManager)getSystemService(Context.CAMERA_SERVICE);
        Log.e(TAG, "OpenCamera-Video E");
        String cameraId = null;
        try {
            if(!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)){
                throw  new RuntimeException("Time out waiting to lock Camera opening");
            }
             cameraId = manager.getCameraIdList()[0];
             CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
             StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            mVideoSize = chooseVideoSize(map.getOutputSizes(MediaRecorder.class));
            mMediaRecorder = new MediaRecorder();
            manager.openCamera(cameraId,mStateCallback,null);
            setUpMediaRecorder();

        } catch (CameraAccessException e) {
            e.printStackTrace();
            Log.e(TAG,"cannot access the camera");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Log.e(TAG,"openCamera-video X");
                }

                    private void setUpMediaRecorder() throws IOException {

        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        mMediaRecorder.setVideoEncodingBitRate(10000000);
        mMediaRecorder.setVideoFrameRate(30);
        mMediaRecorder.setVideoSize(mVideoSize.getWidth(), mVideoSize.getHeight());
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);

        mMediaRecorder.setOutputFile(String.valueOf(getVideoFile()));
        //mMediaRecorder.setOrientationHint(orientation);
        mMediaRecorder.prepare();
    }


          private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(CameraDevice cameraDevice) {
            mCameraDevice = cameraDevice;
            mCameraOpenCloseLock.release();
        }

        @Override
        public void onDisconnected(CameraDevice cameraDevice) {
            mCameraOpenCloseLock.release();
            cameraDevice.close();
            mCameraDevice = null;
        }

        @Override
        public void onError(CameraDevice cameraDevice, int i) {
            mCameraOpenCloseLock.release();
            cameraDevice.close();
            mCameraDevice = null;
        }
    };


   private void startRecordingVideo() {
        try {
            // UI
       //      mButtonVideo.setText(R.string.stop);
            mIsRecordingVideo = true;
            Log.i("VideoService","在startRecordingVideo里");
            // Start recording
            mMediaRecorder.start();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        }
    }

    private  boolean mIsRecordingVideo = false;

    private void stopRecordingVideo() {
        // UI
        mIsRecordingVideo = false;
       // mButtonVideo.setText(R.string.record);
        // Stop recording
        mMediaRecorder.stop();
        mMediaRecorder.reset();
        mMediaRecorder.release();
       // Activity activity = getActivity();
            Toast.makeText(this, "Video saved: " + getVideoFile(),
                    Toast.LENGTH_SHORT).show();

       // startPreview();
    }

在初始化的时候执行init()函数,之后,点击button按钮,对mIsRecordingVideo值判断,若为false,进入startRecordingVideo(),启动录像;若为true,进入stopRecordingVideo(),停止录像。

展开
收起
爵霸 2016-06-12 13:09:47 3360 分享 版权
阿里云 AI 助理回答

根据您提供的代码和问题描述,您希望在不创建相机预览界面的情况下使用 MediaRecorder 进行视频录制。以下是针对您的需求的详细分析和解决方案。


1. 不创建相机预览的可行性

根据 Android 官方文档的说明,确实可以跳过相机预览步骤直接使用 MediaRecorder 进行录制。然而,这种操作需要确保以下几点: - Surface 配置正确:即使没有预览界面,MediaRecorder 仍然需要一个有效的 Surface 来接收视频帧数据。 - 权限处理:确保应用已动态申请并获得必要的权限(如摄像头、麦克风、存储等)。 - 生命周期管理:正确管理 CameraDeviceMediaRecorder 的生命周期,避免资源泄漏或异常崩溃。


2. 核心代码优化与补充

2.1 初始化相机和 MediaRecorder

在您的 init() 方法中,已经完成了大部分初始化工作,但缺少为 MediaRecorder 提供 Surface 的关键步骤。以下是改进后的代码:

private Surface mRecorderSurface;

public void init() {
    CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    Log.e(TAG, "OpenCamera-Video E");
    String cameraId = null;
    try {
        if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
            throw new RuntimeException("Time out waiting to lock Camera opening");
        }
        cameraId = manager.getCameraIdList()[0];
        CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
        StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
        mVideoSize = chooseVideoSize(map.getOutputSizes(MediaRecorder.class));

        // 创建 MediaRecorder 并设置 Surface
        mMediaRecorder = new MediaRecorder();
        setUpMediaRecorder();

        // 创建 Surface 用于 MediaRecorder
        mRecorderSurface = mMediaRecorder.getSurface();

        // 打开相机
        manager.openCamera(cameraId, mStateCallback, null);

    } catch (CameraAccessException | InterruptedException | IOException e) {
        e.printStackTrace();
        Log.e(TAG, "Cannot access the camera or set up MediaRecorder");
    }
    Log.e(TAG, "openCamera-video X");
}

关键点: - 使用 mMediaRecorder.getSurface() 获取 Surface,并将其传递给相机作为输出目标。 - 确保 setUpMediaRecorder() 方法中配置了正确的输出格式、编码器和文件路径。


2.2 设置 MediaRecorder

setUpMediaRecorder() 方法中,您已经完成了大部分配置。以下是完整的代码示例:

private void setUpMediaRecorder() throws IOException {
    mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
    mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
    mMediaRecorder.setVideoEncodingBitRate(10000000);
    mMediaRecorder.setVideoFrameRate(30);
    mMediaRecorder.setVideoSize(mVideoSize.getWidth(), mVideoSize.getHeight());
    mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
    mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);

    // 设置输出文件路径
    mMediaRecorder.setOutputFile(getVideoFile().getAbsolutePath());

    // 准备 MediaRecorder
    mMediaRecorder.prepare();
}

注意: - getVideoFile() 方法应返回一个有效的文件路径,例如:

private File getVideoFile() {
    return new File(getExternalFilesDir(null), "video.mp4");
}

2.3 相机状态回调

CameraDevice.StateCallback 中,需要将 Surface 绑定到相机会话。以下是改进后的代码:

private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
    @Override
    public void onOpened(CameraDevice cameraDevice) {
        mCameraDevice = cameraDevice;
        mCameraOpenCloseLock.release();

        // 创建 CaptureRequest 并绑定 Surface
        try {
            final CaptureRequest.Builder captureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
            captureBuilder.addTarget(mRecorderSurface);

            // 创建 CameraCaptureSession
            mCameraDevice.createCaptureSession(
                Arrays.asList(mRecorderSurface),
                new CameraCaptureSession.StateCallback() {
                    @Override
                    public void onConfigured(CameraCaptureSession session) {
                        try {
                            session.setRepeatingRequest(captureBuilder.build(), null, null);
                        } catch (CameraAccessException e) {
                            e.printStackTrace();
                        }
                    }

                    @Override
                    public void onConfigureFailed(CameraCaptureSession session) {
                        Log.e(TAG, "CameraCaptureSession configuration failed");
                    }
                },
                null
            );
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onDisconnected(CameraDevice cameraDevice) {
        mCameraOpenCloseLock.release();
        cameraDevice.close();
        mCameraDevice = null;
    }

    @Override
    public void onError(CameraDevice cameraDevice, int error) {
        mCameraOpenCloseLock.release();
        cameraDevice.close();
        mCameraDevice = null;
    }
};

关键点: - 使用 createCaptureRequest(CameraDevice.TEMPLATE_RECORD) 创建录制请求,并将 mRecorderSurface 添加为目标。 - 调用 createCaptureSession 创建会话,并设置重复请求以持续捕获视频帧。


2.4 开始和停止录制

startRecordingVideo()stopRecordingVideo() 方法中,您已经实现了基本逻辑。以下是完整代码:

private void startRecordingVideo() {
    try {
        mIsRecordingVideo = true;
        mMediaRecorder.start();
        Log.i("VideoService", "Recording started");
    } catch (IllegalStateException e) {
        e.printStackTrace();
        Log.e(TAG, "Failed to start recording");
    }
}

private void stopRecordingVideo() {
    mIsRecordingVideo = false;
    try {
        mMediaRecorder.stop();
        mMediaRecorder.reset();
        mMediaRecorder.release();
        Toast.makeText(this, "Video saved: " + getVideoFile().getAbsolutePath(), Toast.LENGTH_SHORT).show();
    } catch (Exception e) {
        e.printStackTrace();
        Log.e(TAG, "Failed to stop recording");
    }
}

3. 注意事项

  • 动态权限:确保在 Android 6.0 及以上版本中动态申请以下权限:
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    
  • 异常处理:在实际开发中,建议对所有可能抛出异常的操作进行捕获和处理,避免应用崩溃。
  • 资源释放:在 onPause()onDestroy() 中释放 CameraDeviceMediaRecorder 资源。

通过上述改进,您可以实现无需预览界面的视频录制功能,同时确保代码的稳定性和可维护性。您可以复制页面截图提供更多信息,我可以进一步帮您分析问题原因。

有帮助
无帮助
AI 助理回答生成答案可能存在不准确,仅供参考
0 条回答
写回答
取消 提交回答