4

我正在我的应用程序中集成照片/视频捕获,并且在视频捕获方面遇到问题。每当视频录制开始时,屏幕就会闪烁,我会短暂停顿,然后开始视频捕获。但是,使用手机的相机应用程序,根本没有闪烁/暂停。

此外,我的相机预览显示会在recorder.start()调用时立即调整大小。我不明白为什么会这样。它使预览失真(一切看起来都被挤压和变宽)。

我的问题:如何防止开始视频录制时出现闪烁/暂停?如何防止recorder.start()调整预览显示的大小?

每当启用“视频模式”时,initRecording()都会立即调用。一旦用户按下一个按钮,startRecording()就会被调用。最后,当按钮再次被按下时,stopRecording()被调用。不太重要的是,当切换回“图片模式”时,destroyRecorder()会调用它。

@Override
public void onResume() {

    super.onResume();

    Camera camera = null;
    try {
        camera = Camera.open();
    }
    catch (Exception e) {
        // Camera isn't available
        Toast.makeText( getActivity(), "Camera is not available at this time.", Toast.LENGTH_SHORT ).show();
        getActivity().finish();
        return;
    }

    if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD ) {
        setCameraDisplayOrientation( camera );
    }
    else {
        camera.setDisplayOrientation( 90 );
    }

    setCamera( camera );
    setCameraZoomDisplay( camera );

    if ( getSurfaceHolder() != null ) {
        startPreview();

        if ( getMode() == MODE_VIDEO ) {
            initRecording();
        }
    }
}

private void setCameraDisplayOrientation( Camera camera ) {

    CameraInfo info = new CameraInfo();
    Camera.getCameraInfo( 0, info );

    int rotation = getActivity().getWindowManager().getDefaultDisplay().getRotation();
    int degrees = 0;
    switch (rotation) {
        case Surface.ROTATION_0:
            degrees = 0;
            break;
        case Surface.ROTATION_90:
            degrees = 90;
            break;
        case Surface.ROTATION_180:
            degrees = 180;
            break;
        case Surface.ROTATION_270:
            degrees = 270;
            break;
    }

    int result = ( info.orientation - degrees + 360 ) % 360;
    camera.setDisplayOrientation( result );
}

private void initRecording() {

    MediaRecorder recorder = new MediaRecorder();
    setRecorder( recorder );

    Camera camera = getCamera();
    camera.unlock();
    recorder.setCamera( camera );

    recorder.setAudioSource( MediaRecorder.AudioSource.MIC );
    recorder.setVideoSource( MediaRecorder.VideoSource.CAMERA );

    CamcorderProfile cp = CamcorderProfile.get( CamcorderProfile.QUALITY_HIGH );
    recorder.setProfile( cp );

    String extension;
    switch (cp.fileFormat) {
        case MediaRecorder.OutputFormat.MPEG_4:
            extension = "mp4";
            break;
        case MediaRecorder.OutputFormat.THREE_GPP:
            extension = "3gp";
            break;
        default:
            extension = "tmp";
    }

    setVideoMimeType( MimeTypeMap.getSingleton().getMimeTypeFromExtension( extension ) );

    File toFile = new File( getActivity().getCacheDir(), "tempvideo.tmp" );
    if ( toFile.exists() ) {
        toFile.delete();
    }
    setTempFile( toFile );

    recorder.setOutputFile( toFile.getPath() );
    recorder.setPreviewDisplay( getSurfaceHolder().getSurface() );

    try {
        recorder.prepare();
        setRecorderInitialized( true );
    }
    catch (IllegalStateException e) {
        e.printStackTrace();
    }
    catch (IOException e) {
        e.printStackTrace();
    }
}

private boolean startRecording() {

    try {
        getRecorder().start();
        setRecording( true );

        ImageView actionImageView = getActionImageView();
        actionImageView.setImageResource( R.drawable.record_red );
    }
    catch (Exception e) {
        getCamera().lock();
    }

    return true;
}

private void stopRecording() {

    MediaRecorder recorder = getRecorder();
    if ( recorder != null && isRecording() ) {
        recorder.stop();
        setRecording( false );
        setRecorderInitialized( false );

        try {
            insertVideo();
        }
        catch (IOException e) {
            e.printStackTrace();
        }

        initRecording();

        ImageView actionImageView = getActionImageView();
        actionImageView.setImageResource( R.drawable.record_green );
    }
}

private void destroyRecorder() {

    MediaRecorder recorder = getRecorder();
    recorder.release();
    setRecorder( null );
    getCamera().lock();
}
4

1 回答 1

7

调用 MediaRecorder.start() 时轻微缩放的原因是相机调整其预览大小以匹配正在录制的视频的分辨率。可以通过在设置时设置预览和视频分辨率来解决此问题。我想我也找到了一种停止闪烁的方法,尽管我发现在使用 Camera 和 MediaRecorder 时,一点点延迟或闪烁可能来自许多地方,所以这可能是有点难以追踪。 用于设置摄像机/录像机的 Android 文档是一个很好的起点,可以确保正确设置主要部分,但我发现有必要深入研究一些类 api 文档来调试和体验真的很顺利。

Camera.Parameters课程是保持流畅视频录制体验的关键。一旦你有了一个 Camera 对象,你就可以使用它Camera.getParameters()来获取当前的参数来修改它们。 Camera.setParameters(Camera.Parameters)然后可用于触发已进行的任何更改。

为了防止视频调整大小,我们需要确保参数的预览大小与要录制的视频分辨率一致。要获取支持的视频/预览尺寸列表,我们可以Camera.Parameters.getSupportedPreviewSizes()在当前的参数对象上使用,它将返回一个Camera.Size对象列表。每个对象都有一个 width 和 height 属性,可以通过Camera.Size.widthand直接访问Camera.Size.height(没有 getter 方法)。该getSupportedPreviewSizes()方法保证返回至少一个结果,并且似乎结果是从最高分辨率到最低排序的。

(对于 API 级别 > 11 也有一种getSupportedVideoSizes()方法,但似乎只有设备有一些与预览尺寸不同的视频尺寸,否则它返回 null。我没有成功使用此方法,所以我'现在会坚持使用 PreviewSizes,因为它保证返回对视频和预览都有好处的尺寸,但这是需要注意的事情。)

一旦我们有了与Camera.Size我们想要的视频分辨率相对应的分辨率,我们就可以使用设置该尺寸。Camera.Parameters.setPreviewSize(width, height). 此外,为了帮助解决闪烁问题,我使用了Camera.Parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO). 这些步骤,用于Camera.setParameters()通知相机您的更改。我在拿到相机后就成功设置了一次这些参数,因为在用户与此 Activity 交互时设置参数会导致一些延迟。如果您使用相同的 Activity 进行视频和图片捕获,您还可以在此处设置图片参数,Camera 对象将使用每种模式的正确参数进行处理。

快完成了!现在预览已经完成,剩下的就是确保MediaRecorder使用与预览相同的分辨率。在准备媒体记录器时,在调用MediaRecorder.setProfile()(或设置编码器,API 级别 < 8)和之间,使用与预览相同的值MediaRecorder.setOutputFile()进行调用。MediaRecorder.setVideoSize(width, height)现在,从预览到录制的过渡MediaRecorder.start()应该是无缝的,因为它们都使用相同的分辨率。

这是一些快速的代码片段,因此您可以查看所有操作:

获取和设置参数:

Camera.Parameters params = camera.getParameters();

params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);

//myVideoSize is an instance of Camera.Size
List<Camera.Size> previewSizes = params.getSupportedPreviewSizes();
myVideoSize = previewSizes.get(0);
params.setPreviewSize(myVideoSize.width, myVideoSize.height);

camera.setParameters(params);

然后在媒体记录器上设置大小:

//After setting the profile....
mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));

//Use myVideoSize from above
mediaRecorder.setVideoSize(myVideoSize.width, myVideoSize.height);

//Before setting the output file
mediaRecorder.setOutputFile(myFile);
于 2013-04-15T15:58:10.850 回答