在Android上录制视频时拍照

时间:2017-07-26 15:26:12

标签: android android-camera android-mediarecorder

我已经编写了下面显示的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(https://github.com/mobapptuts/android_camera2_api_video_app)建议的Camera2 API示例。录制视频可以正常工作并拍摄照片但是当我在录制时尝试拍照时,不会创建图片(但也没有错误)。不过,我发现这个示例应用程序工作不太可靠(也许还有另一个示例应用程序)。

修改2 shutterCallback的{​​{1}}和rawCallback被调用,但takePicture的数据为空。 rawCallback永远不会被调用..任何想法为什么以及如何解决这个问题?我还尝试在线程中等待一段时间来给出被调用的回调时间,并且我试图在我的主活动中使回调静态(这样它就不会被垃圾回收)。没有任何效果。

2 个答案:

答案 0 :(得分:5)

修改

澄清:

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

只需按住您传入MediaRecorder的同一个相机实例,并在其上调用Camera.takePicture()。

通过同时创建包含预览,录制和JPEG输出的会话,Camera2还可以更灵活地支持此功能。

原始回答:

如果您的意思是使用后置摄像头拍照,则使用前置摄像头进行录制 - 这取决于设备。有些设备有足够的硬件资源可以同时运行多个摄像头,但大多数设备都没有(他们在两个摄像头之间共享处理硬件)。

判断是否可以同时使用多个摄像头的唯一方法是尝试打开第二个摄像头。如果它有效,你应该好好去;如果没有,该设备不会同时支持多个摄像头。

答案 1 :(得分:2)

不,您无法打开单独的相机实例进行视频录制和静止拍摄。已弃用的Camera API对于此类任务并不可靠(例如,请参阅 Android camera parameter IsVideoSnapshotSupported incorrectly set to false 有关Samsung S4的信息)。

您可以使用 camera2 API(在支持此类模式的设备上)从同一个相机实例捕获不同的格式和分辨率。这是一个视频教程:https://www.nigeapptuts.com/android-video-app-still-capture-recording/