volver atrás / cámara frontal en vuelo

Si utilizo CAMERA_FACING_BACK o CAMERA_FACING_FRONT todo funciona bien.

Tengo problemas con el cambio de CAMERA_FACING_BACK a CAMERA_FACING_FRONT .

Mi fragmento de código:

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

En onCreate () de mi actividad agrego lo siguiente onClick listener a un botón superpuesto en mi Preview SurfaceView (hay numerosos ejemplos en la web para la vista previa):

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

En mi dispositivo de prueba, la cámara trasera tiene una ID de 0 y la frontal tiene una identificación de 1. Sugiero usar variables estáticas de Camera.CameraInfo para los ID de su cámara en lugar de valores de encoding rígida. Estoy seguro de que solo causará problemas en otros dispositivos.

Reinicio Activity with cameraId = 2 y esto está funcionando.

Eliminé y volví a agregar SurfaceView en mi diseño y funcionó.

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

Tengo que eliminar la vista previa de la superficie antes de agregar una nueva:

 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 

Usa este código:

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