在Android上录制视频时拍照

7

我已经编写了下面的Android服务来在后台录制前置摄像头。这个服务非常好用。但现在我想在录制时每5秒钟拍一张照片,这是否可能?当我尝试打开第二个相机(在另一个服务中)时,会出现错误。

public class RecorderService extends Service implements SurfaceHolder.Callback {

    private WindowManager windowManager;
    private SurfaceView surfaceView;
    private Camera camera = null;
    private MediaRecorder mediaRecorder = null;

    @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;
    }

    // Method called right after Surface created (initializing and starting MediaRecorder)
    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        camera = Camera.open(1);
        mediaRecorder = new MediaRecorder();
        camera.unlock();

        mediaRecorder.setPreviewDisplay(surfaceHolder.getSurface());
        mediaRecorder.setCamera(camera);
        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
        mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
        mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_720P));

        FileUtil.createDir("/storage/emulated/0/Study/Camera");
        mediaRecorder.setOutputFile("/storage/emulated/0/Study/Camera/" + Long.toString(System.currentTimeMillis()) + ".mp4");

        try { mediaRecorder.prepare(); } catch (Exception e) {}
        mediaRecorder.start();

        try {
            camera.setPreviewDisplay(surfaceHolder);
        } catch (IOException e) {
            e.printStackTrace();
        }

        Runnable runnable = new PictureThread(camera);
        Thread thread = new Thread(runnable);
        thread.start();
    }

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

        camera.lock();
        camera.release();

        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; }
}

编辑:我现在写了一个PictureThread线程。该线程由RecorderService启动,并尝试在视频录制时拍下一张照片。

public class PictureThread implements Runnable {
    private final static String TAG = PictureThread.class.getSimpleName();

    private Camera camera;

    PictureThread(Camera camera) {
        this.camera = camera;
    }

    @Override
    public void run() {
        camera.startPreview();
        camera.takePicture(shutterCallback, rawCallback, jpegCallback);
    }

    Camera.ShutterCallback shutterCallback = new Camera.ShutterCallback() {
        public void onShutter() {
        }
    };

    Camera.PictureCallback rawCallback = new Camera.PictureCallback() {
        public void onPictureTaken(byte[] data, Camera camera) {
        }
    };

    Camera.PictureCallback jpegCallback = new Camera.PictureCallback() {
        public void onPictureTaken(byte[] data, Camera camera) {
            Log.i(TAG, "onPictureTaken - jpeg");
        }
    };
}

很遗憾,jpegCallback从未被调用(即日志消息从未打印)。 当我打开平板电脑的相机应用程序时,我可以在录制视频时拍照,因此这应该是可能的。

我还尝试了Alex Cohn建议的Camera2 API示例(https://github.com/mobapptuts/android_camera2_api_video_app)。 录制视频和拍照都可以正常工作,但是当我尝试在录制过程中拍照时,不会创建照片(但也没有错误)。 尽管如此,我发现这个示例应用程序不太可靠(也许有另一个示例应用程序)。

编辑2takePictureshutterCallbackrawCallback被调用,但rawCallback的数据为null。 jpegCallback从未被调用... 有什么想法以及如何解决? 我还尝试在线程中等待一段时间以便回调被调用,并尝试将回调函数设为我的主要活动的静态函数(以便它不会被垃圾回收)。 但是没有任何效果。


@AlexCohn 为什么jpegCallback不起作用?我犯了什么错误吗?使用Android自带的相机应用程序,我可以在录制视频时拍照,因此我认为它应该能够工作... - machinery
你尝试过开放相机吗? - Alex Cohn
@AlexCohn 我现在尝试了Open Camera。当我从视频录制切换到拍照时,视频录制会停止并存储视频。我认为该应用程序不支持在录制过程中拍照。 - machinery
@AlexCohn,但是你提供的camera2 API项目在我的平板电脑上无法运行...也许还有其他可用的带有免费源代码的项目吗? - machinery
@AlexCohn,结果发现当我执行Camera.unlock()时,jpegCallback从未被调用,否则就会被调用。但是对于录制视频,我必须首先调用Camera.unlock()... - machinery
显示剩余5条评论
2个回答

7

编辑:

根据澄清:

旧的相机API支持在录制视频时调用takePicture(),如果设备上的Camera.Parameters.isVideoSnapshotSupported报告为true。

只需保持传递到MediaRecorder中的相同相机实例,并在其上调用Camera.takePicture()。

Camera2也支持此功能,具有更高的灵活性,通过同时创建具有预览、录制和JPEG输出的会话。

原始答案:

如果您指的是在使用前置摄像头录制时拍摄后置摄像头的照片-这取决于设备。一些设备有足够的硬件资源来同时运行多个摄像头,但大多数设备不会这样做(它们在两个摄像头之间共享处理硬件)。

唯一的方法是尝试在已经打开一个相机时打开第二个相机。如果可以打开,则应该可以正常工作;如果不能,则该设备不支持同时使用多个相机。


我想从前置摄像头录制视频,同时也要从前置摄像头拍照。 - machinery
我已经对我的原始帖子进行了编辑。尽管我的设备应该能够在录制视频时拍照,但使用Camera.takePicture()并没有起作用。 - machinery

2

camera2 API听起来很有趣。如果我理解正确的话,我可以创建一个相机实例,然后使用这个实例从前置摄像头录制视频,同时也可以从前置摄像头拍照。您能提供一些代码示例来说明如何实现吗?那将非常好。 - machinery
我已经对我的原始帖子进行了编辑,使用了新的方法。 - machinery
1
谢谢您,我通过您的回答找到了一个带有项目的示例存储库! https://github.com/mobapptuts/android_camera2_api_video_app 您非常帮助了我! - Andrew G

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接