即时切换后置/前置摄像头

Posted

技术标签:

【中文标题】即时切换后置/前置摄像头【英文标题】:switch back/front camera on fly 【发布时间】:2011-09-29 18:40:23 【问题描述】:

如果我只使用 CAMERA_FACING_BACKCAMERA_FACING_FRONT 一切正常。

我在从 CAMERA_FACING_BACK 切换到 CAMERA_FACING_FRONT 时遇到问题。

我的代码sn-p:

public class PhotoCameraActivity extends Activity implements OnClickListener 
private SurfaceView cameraView;
private Button turnButton;
private Camera camera = null;
private Callback listener;
private static int camId = Camera.CameraInfo.CAMERA_FACING_BACK;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.photo_camera_main);
    prepareActivity();


private void prepareActivity() 
    cameraView = (SurfaceView) findViewById(R.id.photo_camera_surface_view);

    turnButton = (ImageButton) findViewById(R.id.turn_button);
    turnButton.setOnClickListener(this);


@Override
public void onClick(View v) 
    if (v.equals(turnButton)) 
        if (Camera.getNumberOfCameras() > 1 && camId < Camera.getNumberOfCameras() - 1) 
            startCamera(camId + 1);
         else 
            startCamera(Camera.CameraInfo.CAMERA_FACING_BACK);
        
     


@Override
protected void onResume() 
    startCamera(camId);
    super.onResume();


@Override
protected void onPause() 
    stopCamera();
    super.onPause();


private void startCamera(int cameraId) 
    if (camera != null) 
        stopCamera();
           
    holder = cameraView.getHolder();
    listener = new Callback() 

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) 
        

        @Override
        public void surfaceCreated(SurfaceHolder holder) 
            try 
                camera.setPreviewDisplay(holder);
                camera.startPreview();
             catch (IOException e) 
                e.printStackTrace();
            
        

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int           width, int height) 
    ;
    holder.addCallback(listener);


    camId = cameraId;
    camera = Camera.open(cameraId);
    Camera.Parameters params = camera.getParameters();
    if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) 
        params.setPreviewSize(1280, 800);
     else 
        params.setPreviewSize(640, 480);
    
    camera.setParameters(params);



private void stopCamera()
    System.out.println("stopCamera method");
    if (camera != null)
        camera.stopPreview();
        camera.setPreviewCallback(null);
        camera.release();
        camera = null;
        holder.removeCallback(listener);
        holder = null;
    



【问题讨论】:

您遇到的“麻烦”的性质是什么? 如果我尝试停止相机,取景器会冻结并且不显示来自其他相机的图像。但是,如果我按“返回”然后再次启动此活动,相机会切换到前面,它会再次正常工作。我认为我在关闭相机和释放资源方面遇到了问题。 我遇到了同样的问题 一次只能打开一个摄像头,所以如果要切换摄像头,请先调用 camera.recycle() 此代码是wrong… 摄像头朝向不一定与摄像头索引相同。例如,我的平板电脑只有一个摄像头(索引:0)但面向前方(面对索引:1)。因此使用像 Camera.open(CameraInfo.CAMERA_FACING_FRONT) 这样的简单代码是没有意义的 【参考方案1】:

在我的活动的 onCreate() 中,我将以下 onClick 侦听器添加到覆盖在我的 Preview SurfaceView 上的按钮(网络上有许多示例用于预览):

ImageButton useOtherCamera = (ImageButton) findViewById(R.id.useOtherCamera);
//if phone has only one camera, hide "switch camera" button
if(Camera.getNumberOfCameras() == 1)
    useOtherCamera.setVisibility(View.INVISIBLE);

else 
    useOtherCamera.setOnClickListener(new View.OnClickListener() 
    @Override
    public void onClick(View v) 
    if (inPreview) 
        camera.stopPreview();
    
    //NB: if you don't release the current camera before switching, you app will crash
    camera.release();

    //swap the id of the camera to be used
    if(currentCameraId == Camera.CameraInfo.CAMERA_FACING_BACK)
        currentCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
    
    else 
        currentCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
    
    camera = Camera.open(currentCameraId);
    //Code snippet for this method from somewhere on android developers, i forget where
    setCameraDisplayOrientation(CameraActivity.this, currentCameraId, camera);
    try 
        //this step is critical or preview on new camera will no know where to render to
        camera.setPreviewDisplay(previewHolder);
     catch (IOException e) 
        e.printStackTrace();
    
    camera.startPreview();

在我的测试设备上,后置摄像头的 ID 为 0,前置摄像头的 ID 为 1。我建议使用 Camera.CameraInfo 静态变量作为摄像头 ID,而不是硬编码值。我确信这只会在其他设备上引起问题。

【讨论】:

+1 我错过了 camera.setPreviewDisplay(previewHolder); 此代码是wrong… 摄像头朝向不一定与摄像头索引相同。例如,我的平板电脑只有一个摄像头(索引:0)但面向前方(面对索引:1)。因此使用像 Camera.open(CameraInfo.CAMERA_FACING_FRONT) 这样的简单代码是没有意义的 @AlexCohn 我不会说这段代码是错误的,事实上它工作得很好,并展示了如何停止摄像头并开始一个新的摄像头。程序员需要理解代码并根据自己的需要进行调整。 @Merlevede:这段代码在非常具体的意义上是错误。请把我的评论读到最后,我不反对相机停止和重启的方式(尽管这不是最佳的,所以如果你想学习最佳实践,请随时搜索或询问)。 @Alex Cohn,您能否说明一下这样做的正确方法是什么?【参考方案2】:

我使用 cameraId = 2 重新启动 Activity,这可以正常工作。

【讨论】:

@Dmitriy_Boichenko..我也面临同样的问题..你能分享你的代码吗? 如何在按钮点击时重启活动 任何尝试过这个答案的人都应该记住,几年前这可能在一些未指定的设备上工作。这不是 cameraId 预期的行为方式。【参考方案3】:

我在布局中删除并重新添加了 SurfaceView,所以它起作用了。

public void switchCamera(int cameraType)

    if(context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT))
    
        if(Camera.getNumberOfCameras() > cameraType)
        
            // Set selected camera
            this.cameraType = cameraType;
        
        else
        
            // Set default camera (Rear)
            this.cameraType = Camera.CameraInfo.CAMERA_FACING_BACK;
        

        if(mCamera != null)
        
            // Destroy previuos Holder
            surfaceDestroyed(holder);
            holder.removeCallback(this);

            // Remove and re-Add SurfaceView
            ViewGroup rootLayout = (ViewGroup) surface.getParent();
            rootLayout.removeView(surface);
            surface = new SurfaceView(context);
            holder = surface.getHolder();
            holder.addCallback(this);
            rootLayout.addView(surface, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        
    

【讨论】:

我确实忘记删除视图所以我无法看到切换的相机视图。感谢这有帮助【参考方案4】:

在添加新的之前,我必须删除表面预览:

if (mPreview != null) 
        mPreview.surfaceDestroyed(mPreview.getHolder());
        mPreview.getHolder().removeCallback(mPreview);
        mPreview.destroyDrawingCache();
        FrameLayout preview = (FrameLayout) view.findViewById(R.id.camera_frame);
        preview.removeView(mPreview);
        mPreview.mCamera = null;
        mPreview = null;

    

//then add your preview

【讨论】:

【参考方案5】:

使用此代码:

if (mCamera != null) 
      mCamera.stopPreview();                  
      mCamera.release();
      mCamera = null;


//swap the id of the camera to be used
if (currentCameraId == Camera.CameraInfo.CAMERA_FACING_BACK)
  currentCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
else 
  currentCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;         

try                 
       mCamera = Camera.open(currentCameraId);   

       mCamera.setDisplayOrientation(90);

       mCamera.setPreviewDisplay(surfaceHolder);               

       mCamera.startPreview();

      
catch (Exception e)  e.printStackTrace();  

【讨论】:

此代码是wrong… 摄像头朝向不一定与摄像头索引相同。例如,我的平板电脑只有一个摄像头(索引:0)但面向前方(面对索引:1)。因此使用像 Camera.open(CameraInfo.CAMERA_FACING_FRONT) 这样的简单代码是没有意义的【参考方案6】:

尝试使用这个:

camera.stopPreview();
if(camera != null)
    camera.release();
try 
    camera = Camera.open(/*new id*/);
    camera.setPreviewDisplay(holder);
    camera.startPreview();
 catch (Exception ex)  ex.printStackTrace(); 

【讨论】:

以上是关于即时切换后置/前置摄像头的主要内容,如果未能解决你的问题,请参考以下文章

android Camera如何判断当前使用的摄像头是前置还是后置

如何检查设备是不是有分别用于前置和后置摄像头的闪光灯?

android Camera 如何判断当前使用的摄像头是前置还是后置

同时使用前置和后置摄像头android

如何为前置摄像头添加开关按钮?

前置摄像头的 AVCaptureSession canAddInput 在 iPad 上总是返回 false