代码之家  ›  专栏  ›  技术社区  ›  Jishan

自动切换前后摄像头

  •  0
  • Jishan  · 技术社区  · 8 年前

    所以我有一个非常基本的摄像头实现。 我的目标是每10秒自动切换前后摄像头,直到点击按钮停止。

    MainActivity :

    public class MainActivity extends Activity {
        private Camera mCamera = null;
        private CameraView mCameraView = null;
        private CountDownTimer countDownTimer;
        private int mCamId = 0;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            startCam();
    
            if(mCamera != null) {
                mCameraView = new CameraView(this, mCamera);//create a SurfaceView to show camera data
                FrameLayout camera_view = (FrameLayout)findViewById(R.id.camera_view);
                camera_view.addView(mCameraView);//add the SurfaceView to the layout
            }
    
            //btn to close the application
            ImageButton imgClose = (ImageButton)findViewById(R.id.imgClose);
            imgClose.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    System.exit(0);
                }
            });
        }
    
        private void startCam() {
            try{
                //you can use open(int) to use different cameras
                mCamId = mCamId == 0 ? 1 : 0;
                mCamera = Camera.open(mCamId);
                switchCam();
    
            } catch (Exception e){
                Log.d("ERROR", "Failed to get camera: " + e.getMessage());
            }
        }
    
        private void switchCam() {
            //10 seconds
            countDownTimer = new CountDownTimer(10000, 1000) {
    
                @Override
                public void onTick(long l) {
                    Log.d(TAG, l + " left");
                }
    
                @Override
                public void onFinish() {
                    cleanup();
                    startCam();
                }
            }.start();
        }
    
        public void cleanup() {
            Log.i(TAG, "Switching Camera");
            if (mCamera != null) {
                mCamera.stopPreview();
                mCamera.release();
                mCamera = null;
    
            }
        }
    }
    

    这是我的 CameraView 类别:

    public class CameraView extends SurfaceView implements SurfaceHolder.Callback{
    
        private SurfaceHolder mHolder;
        private Camera mCamera;
    
        public CameraView(Context context, Camera camera){
            super(context);
    
            mCamera = camera;
            mCamera.setDisplayOrientation(90);
            //get the holder and set this class as the callback, so we can get camera data here
            mHolder = getHolder();
            mHolder.addCallback(this);
            mHolder.setType(SurfaceHolder.SURFACE_TYPE_NORMAL);
        }
    
        @Override
        public void surfaceCreated(SurfaceHolder surfaceHolder) {
            try{
                //when the surface is created, we can set the camera to draw images in this surfaceholder
                mCamera.setPreviewDisplay(surfaceHolder);
                mCamera.startPreview();
            } catch (IOException e) {
                Log.d("ERROR", "Camera error on surfaceCreated " + e.getMessage());
            }
        }
    
        @Override
        public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i2, int i3) {
            //before changing the application orientation, you need to stop the preview, rotate and then start it again
            if(mHolder.getSurface() == null)//check if the surface is ready to receive camera data
                return;
    
            try{
                mCamera.stopPreview();
            } catch (Exception e){
                //this will happen when you are trying the camera if it's not running
            }
    
            //now, recreate the camera preview
            try{
                mCamera.setPreviewDisplay(mHolder);
                mCamera.startPreview();
            } catch (IOException e) {
                Log.d("ERROR", "Camera error on surfaceChanged " + e.getMessage());
            }
        }
    
        @Override
        public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
            //our app has only one screen, so we'll destroy the camera in the surface
            //if you are unsing with more screens, please move this code your activity
            mCamera.stopPreview();
            mCamera.release();
        }
    

    第一台摄像机毫无问题地启动。然而,在切换第二个摄像机时,即10秒后,界面冻结为静态图像。我无法修复它。我哪里弄错了?

    最小、完整、可验证和可编译的代码 LINK

    2 回复  |  直到 8 年前
        1
  •  0
  •   Alex Cohn    8 年前

    你的 摄影师 不与同步 主要活动 . 两者都保持引用 麦卡梅拉 ,但当活动交换摄像机时,不会通知视图。

    对代码的最小更改是:

    1. 移动创建的代码 麦克阿梅拉维 startCam() .
    2. 在里面 清理() 而不是 麦卡梅拉。StopReview() 去除 麦克阿梅拉维 从FrameLayout。

    现在,当计时器事件发生时,框架将调用 摄影师。SurfaceDestroyed() 释放 麦卡梅拉 ,然后,您将为前向相机创建一个新的摄像机视图。

    其他注意事项:

    1. 你可以保持不变 摄影师 如果你交换它 麦卡梅拉 .
    2. 跑步是一种不好的习惯 照相机打开() 在UI线程上,此调用在某些设备上可能很慢,甚至会导致ANR。
    3. 首选方法是 use a background HandlerThread 因此摄像机回调也发生在背景上。
    4. 谷歌在新的camera2 API中修复了这个问题(在棒棒糖和更高版本上可用)。你的 MinSDK版本 21岁的你有充分的理由使用这个新的API。
    5. 当摄像机预览处于活动状态时,使用FLAG\u KEEP\u SCREEN\u。
        2
  •  -1
  •   Al0x    8 年前

    你现在做什么:

    在onCreate方法中,您在startCam()之后更新FrameLayout。

            if(mCamera != null) {
            mCameraView = new CameraView(this, mCamera);//create a SurfaceView to show camera data
            FrameLayout camera_view = (FrameLayout)findViewById(R.id.camera_view);
            camera_view.addView(mCameraView);//add the SurfaceView to the layout
        }
    

    当摄像机切换时,您释放旧摄像机并打开新摄像机,但您忘记了同时更新FrameLayout。所以问题不是任何东西都“冻结”,而是切换到新摄像头的操作没有在GUI中更新。

    如何解决您的问题:

    在方法switchCam()中,您还需要更新FrameLayout,就像在onCreate方法中一样。

    switchCam()方法的工作示例如下:

        private void switchCam() {
            //10 seconds
            countDownTimer = new CountDownTimer(10000, 1000) {
    
                @Override
                public void onTick(long l) {
                    Log.d(TAG, l + " left");
                }
    
                @Override
                public void onFinish() {
                    cleanup();
                    startCam();
                    if(mCamera != null) {
                        mCameraView = new CameraView(getApplicationContext(), mCamera);//create a SurfaceView to show camera data
                        FrameLayout camera_view =
     (FrameLayout)findViewById(R.id.camera_view);
        if(( camera_view).getChildCount() > 0)
                        {
                            camera_view.removeAllViews();
                        }
                        camera_view.addView(mCameraView);//add the SurfaceView to the layout
                    }
                }
            }.start();
        }
    

    如果单凭这一点还不能解决您的问题,您还可以尝试以下方法:

    更改AndroidManifest硬件中的使用功能。摄像头到硬件。摄像机2作为硬件。相机已弃用。

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="in.harjot.andorid.cameraswap">
    ...
        <uses-feature android:name="android.hardware.camera2" />
    ...
    </manifest>
    

    在我的Nexus5X上,应用程序似乎也有权限问题,所以在调用startCam()之前,我在onCreate方法中添加了以下内容(没有这个,我实际上什么都看不到):

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.CAMERA)
                != PackageManager.PERMISSION_GRANTED) {
    
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.CAMERA},
                        1);
        }
    
        startCam();
        ...
    }