在Android上录制视频时拍摄照片

问题描述:

我已经编写了如下所示的Android服务,用于在后台录制前部凸轮。这工作得很好。但是现在我想在录制时每隔5秒拍摄一张照片。这是否有可能?当我尝试打开第二台摄像机(在另一个服务中)时,出现错误。在Android上录制视频时拍摄照片

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)。录制视频的工作原理也适用于拍摄照片,但是当我在录制过程中尝试拍摄照片时,不会创建照片(但也不会出现错误)。不过,我发现这个示例应用程序工作不可靠(可能还有另一个示例应用程序)。

编辑2:本shutterCallbacktakePicturerawCallback被调用但rawCallback的数据为空。 jpegCallback从来没有被称为..任何想法为什么以及如何解决这个问题?我也尝试在线程中等待一段时间,以便为被调用的回调时间做好准备,并且试图在我的主要活动中使回调保持静态(以便它不会被垃圾回收)。没有工作。

+0

即使没有记录,rawCallback上的数据也为null。在过去的10年中,我没有看到一个设备在这个回调中提供了有意义的数据。至于jpegCallback,是什么让你相信它应该起作用? –

+0

@AlexCohn为什么jpegCallback不起作用?我犯了一个错误吗?随着Android股票相机应用程序,我可以在录制时拍照,所以我相信它应该可以工作... – machinery

+0

这是哪种设备? –

编辑:

随着澄清:

老相机API支持调用takePicture()正在录制的视频,如果Camera.Parameters.isVideoSnapshotSupported报道属实的设备题。

只要保持传入MediaRecorder的同一个相机实例,并调用Camera.takePicture()就可以了。

Camera2还通过创建与预览,录制和JPEG输出同时创建会话更灵活的支持。

原来的答复:

如果你的意思是拍照与背部摄像头,具备前置摄像头录制时 - 这是依赖于设备。一些设备有足够的硬件资源来同时运行多台摄像机,但大多数不会(它们在两台摄像机之间共享处理硬件)。

要确定是否可以同时使用多台摄像机的唯一方法是尝试打开第二台摄像机,当其中一台已经打开时。如果它有效,你应该好好去;如果没有,该设备一次不支持多台摄像机。

+0

我想记录来自前置摄像头的视频,并同时从前置摄像头拍摄照片。 – machinery

+0

我已对原始帖子进行了修改。使用Camera.takePicture()不起作用,虽然我的设备应该能够在录制时捕捉图片。 – machinery

不,您无法打开单独的摄像机实例进行视频录制和剧照捕获。弃用的Camera API对于这些任务不可靠(有关Samsung S4,请参阅Android camera parameter IsVideoSnapshotSupported incorrectly set to false)。

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

+0

camera2 API听起来很有趣。如果我理解正确,我可以创建一个摄像头实例,然后使用此实例同时从前摄像头录制视频,同时从前摄像头拍摄照片。你能提供一些代码示例来说明如何实现这一点吗?那肯定很不错。 – machinery

+0

https://www.nigeapptuts.com/android-video-app-still-capture-recording/ –

+0

我用我的新方法对我原来的帖子进行了编辑。 – machinery