0

我已经使用Camera2API 实现了作为服务运行并从前置摄像头录制的后台录像机。为此,我创建了一个新的SurfaceView,将其大小设置为 1x1 并将其移动到左上角。我的代码如下所示。我使用的是安卓 5.1。

使用 Camera API 它工作得非常好,但不幸的是帧速率仅为 20 fps(当我增加曝光补偿时,它下降得更多),尽管使用该Open Camera应用程序我有 30 fps 也增加了曝光补偿。这就是为什么我想尝试Camera2API(希望获得更高的 fps)。不幸的是,我收到以下错误:

MediaRecoder: setOutputFile called in an invalid state(2)
MediaRecorder: start called in an invalid state: 2

这是我的代码:

public class RecorderServiceCamera2 extends Service implements SurfaceHolder.Callback {
    private WindowManager windowManager;
    private SurfaceView surfaceView;
    private CameraDevice mCamera;
    private MediaRecorder mediaRecorder = null;
    private CaptureRequest mCaptureRequest;
    private CameraCaptureSession mSession;

    @Override
    public void onCreate() {
        // Create new SurfaceView, set its size to 1x1, move it to the top left corner and set this service as a callback
        windowManager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);

        surfaceView = new SurfaceView(this);
        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
                1, 1,
                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
                WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                PixelFormat.TRANSLUCENT
        );
        layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
        windowManager.addView(surfaceView, layoutParams);
        surfaceView.getHolder().addCallback(this);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Intent notificationIntent = new Intent(this, MainActivity.class);

        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
                notificationIntent, 0);

        Notification notification = new NotificationCompat.Builder(this)
                //.setSmallIcon(R.mipmap.app_icon)
                .setContentTitle("Background Video Recorder")
                .setContentText("")
                .setContentIntent(pendingIntent).build();

        startForeground(MainActivity.NOTIFICATION_ID_RECORDER_SERVICE, notification);

        return Service.START_NOT_STICKY;
    }

    @Override
    public void surfaceCreated(final SurfaceHolder surfaceHolder) {
        mediaRecorder = new MediaRecorder();

        try {
            CameraManager manager = (CameraManager) getSystemService(CAMERA_SERVICE);
            String[] cameras = manager.getCameraIdList();
            CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameras[1]);
            StreamConfigurationMap configs = characteristics.get(
                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            Size[] sizes = configs.getOutputSizes(MediaCodec.class);

            final Size sizeHigh = sizes[0];

            manager.openCamera(cameras[1], new CameraDevice.StateCallback() {
                @Override
                public void onOpened(@NonNull CameraDevice camera) {
                    mCamera = camera;
                    mediaRecorder.setPreviewDisplay(surfaceHolder.getSurface());
                    mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
                    mediaRecorder.setMaxFileSize(0);
                    mediaRecorder.setOrientationHint(0);

                    mediaRecorder.setOutputFile("test.mp4");
                    try { mediaRecorder.prepare(); } catch (Exception ignored) {}
                    List<Surface> list = new ArrayList<>();
                    list.add(surfaceHolder.getSurface());

                    try {
                        CaptureRequest.Builder captureRequest = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
                        captureRequest.addTarget(surfaceHolder.getSurface());
                        mCaptureRequest = captureRequest.build();
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }

                    try {
                        mCamera.createCaptureSession(list, new CameraCaptureSession.StateCallback() {
                            @Override
                            public void onConfigured(@NonNull CameraCaptureSession session) {
                                mSession = session;
                            }

                            @Override
                            public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                                mSession = session;
                            }
                        }, null);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }

                    mediaRecorder.start();

                    try {
                        mSession.setRepeatingRequest(mCaptureRequest,
                                new CameraCaptureSession.CaptureCallback() {
                                    @Override
                                    public void onCaptureStarted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, long timestamp, long frameNumber) {
                                        super.onCaptureStarted(session, request, timestamp, frameNumber);
                                    }

                                    @Override
                                    public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
                                        super.onCaptureCompleted(session, request, result);
                                    }

                                    @Override
                                    public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
                                    }
                                }, null);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onDisconnected(@NonNull CameraDevice camera) {
                }

                @Override
                public void onError(@NonNull CameraDevice camera, int error) {
                }
            }, null);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    // Stop recording and remove SurfaceView
    @Override
    public void onDestroy() {
        mediaRecorder.stop();
        mediaRecorder.reset();
        mediaRecorder.release();
        mCamera.close();
        mediaRecorder= null;

        windowManager.removeView(surfaceView);
    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {}

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {}

    @Override
    public IBinder onBind(Intent intent) { return null; }
}
4

1 回答 1

1

您不需要使用 Camera2 或什至使用 Camera1 设置 SurfaceView(使用后者,只需创建一个 SurfaceTexture 以从中获取 Surface,并且永远不要调用 updateTexImage;使用前者,只是不要在全部,不需要)。

也就是说,您正在尝试在 Camera2 和 MediaRecorder 中使用相同的 Surface;那是行不通的。MediaRecorder 根本不需要绘制到 Surface 来记录,您可以关闭该部分。它只是允许 MediaRecorder 独立于相机 API 使用,它在幕后管理相机。

我怀疑对 MediaRecorder.prepare() 的调用引发了一个您忽略的错误,关于预览 Surface 已在使用中,这就是 start 稍后不起作用的原因。

是的,正如 CommonsWare 所说,您可能需要 Android P 中的前台服务。

于 2018-02-28T00:48:10.990 回答