Android camera preview freezes when switching cameras? - android

I am writing a custom camera for my app. It's working fine when opening the activity with either of the back or front cameras. But I'm really struggling with switching cameras inside the activity.
When I tap the switching camera button, the preview freezes and nothing happens. I have tried the answers and tips suggested in other questions related to the camera and the preview, but nothing works.
Here is my activity:
public class CameraActivity extends Activity implements SurfaceHolder.Callback {
//================================================================================
// Properties
//================================================================================
// Tag
private final String TAG = "CameraActivity";
// Camera and CameraPreview objects
private Camera mCamera;
// SurfaceHolder object
private SurfaceHolder mSurfaceHolder;
// private CameraPreview mPreview;
// ImageButtons
private ImageButton captureButton;
private ImageButton camSwitchButton;
// Picture previewLayout
FrameLayout previewLayout;
SurfaceView preview;
// Current camera is facing FRONT or BACK
private int currentCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
// Picture taking callback
private Camera.PictureCallback mPictureCallback;
//================================================================================
// Methods
//================================================================================
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Activity with no notification bar
requestWindowFeature(Window.FEATURE_NO_TITLE);
// Set content view
setContentView(R.layout.activity_camera);
// Initialize activity and previews
InitActivity();
InitPreview();
// Set onClicks
}
/**
* Initialize activity views
*/
private void InitActivity() {
// Create an instance of Camera
mCamera = GetCameraInstance(currentCameraId);
// Set the SurfaceView
preview = (SurfaceView) findViewById(R.id.camera_preview);
mSurfaceHolder = preview.getHolder();
mSurfaceHolder.addCallback(this);
}
/**
* Initialize the camera preview
*/
private void InitPreview() {
if (mCamera != null) {
try {
mCamera.setPreviewDisplay(mSurfaceHolder);
} catch (IOException e) {
// log
}
mCamera.startPreview();
}
}
#Override
protected void onPause() {
super.onPause();
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release(); // release the camera for other applications
// mCamera = null;
}
}
#Override
protected void onResume() {
super.onResume();
if (mCamera == null) {
InitActivity();
InitPreview();
}
}
/**
* Get camera instance
* #return Camera instance (null if doesn't exist)
*/
private Camera GetCameraInstance(int camid) {
Camera c = null;
try {
c = Camera.open(camid); // attempt to get a Camera instance
Camera.Parameters campar = c.getParameters();
// Set auto-focus
if (getPackageManager().hasSystemFeature("android.hardware.camera.autofocus")) {
campar.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
}
c.setParameters(campar);
}
catch (Exception e){
// log
}
return c; // returns null if camera is unavailable
}
/**
* Switch the camera from front to back and vica verca
*/
private void SwitchCamera() {
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_FRONT;
// Remove the view and re-add it
ViewGroup rootView = (ViewGroup) preview.getParent();
rootView.removeView(previewLayout);
mCamera = GetCameraInstance(currentCameraId);
InitActivity();
}
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
Logger.Log(TAG, "SurfaceCreated");
InitPreview();
}
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
// Return if no surface found
if (mSurfaceHolder.getSurface() == null) {
return;
}
// Start preview with new settings
Camera.Parameters params = mCamera.getParameters();
boolean isPortrait = IsPortrait();
// Configure the parameters (so the image is showing correctly)
ConfigureCameraParameters(params, isPortrait);
// Start preview
InitPreview();
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
/**
* Configure the camer parameters
* #param cameraParams Camera parameters
* #param isPortrait Is the camera in portrait mode?
*/
protected void ConfigureCameraParameters(Camera.Parameters cameraParams, boolean isPortrait) {
int angle;
Display display = getWindowManager().getDefaultDisplay();
// Correct the orientation
switch (display.getRotation()) {
case Surface.ROTATION_0: // This is display orientation
angle = 90; // This is camera orientation
break;
case Surface.ROTATION_90:
angle = 0;
break;
case Surface.ROTATION_180:
angle = 270;
break;
case Surface.ROTATION_270:
angle = 180;
break;
default:
angle = 90;
break;
}
mCamera.setDisplayOrientation(angle);
mCamera.setParameters(cameraParams);
}
/**
* Check whether the camera is in portrait mode
* #return True|False
*/
private boolean IsPortrait() {
return (getResources().getConfiguration().orientation
== Configuration.ORIENTATION_PORTRAIT);
}
}
Permissions in manifest:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
I really appreciate it if someone could help.

You are pretty close, you dont need to destroy the surface view everytime you switch the camera, you only need to destroy the camera session and then make sure you re hook up the camera back to the surfaceview so it can be displayed. I was having some trouble with your front facing rear facing logic on a Razr Max so I just switched it to 1 and 0.
private void SwitchCamera() {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
//swap the id of the camera to be used
if (currentCameraId == Camera.CameraInfo.CAMERA_FACING_FRONT) {
currentCameraId = 0;
} else {
currentCameraId = 1;
}
mCamera = GetCameraInstance(currentCameraId);
InitPreview();
}

Related

Two android camera previews in the same fragment

I'm trying to create two camera previews in the same fragment layout, but I've encountered some strange issues, like:
the preview freezes and the camera preview isn't changed,
sometimes camera doesn't start at all.
Here is my custom SurfaceView for the Camera:
#SuppressWarnings("deprecation")
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "CameraPreview";
private SurfaceHolder mHolder;
private Camera mCamera;
private List<Camera.Size> mSupportedPreviewSizes;
private Camera.Size mPreviewSize;
private int currentVolume;
private boolean isVolumeChanged = false;
public CameraPreview(Context context, Camera mCamera) {
super(context);
setCamera(mCamera);
init();
}
public CameraPreview(Context context) {
super(context);
}
public CameraPreview(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CameraPreview(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CameraPreview(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
/**
* Initialize the SurfaceView which will be used to display the camera preview.
* <p/>
* Only an general setup for the SurfaceView should be done in this method.
*/
private void init() {
setDrawingCacheEnabled(true);
}
/**
* Set the camera to the SurfaceView.
*
* #param camera
* The camera object which will be set to the SurfaceView.
*/
public void setCamera(Camera camera) {
mCamera = camera;
// supported preview sizes
mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
// empty. surfaceChanged will take care of stuff
// The Surface has been created, now tell the camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
LogUtils.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null) {
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e) {
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or reformatting changes here
try {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
// set the orientation of the pictures taken
//TODO: match this orientation value with the one used for display
parameters.setRotation(90);
// set the focus mode
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
mCamera.setParameters(parameters);
} catch (RuntimeException e) {
// This could happen when concurrency of the camera is happening (especially in dual shot mode)
//TODO: optimize the camera flow to avoid this issue
LogUtils.d(TAG, "Error setting camera parameters: " + e.getMessage());
}
// Force the orientation in Portrait mode
// TODO: get the orientation from the device
mCamera.setDisplayOrientation(90);
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (IOException e) {
LogUtils.e(TAG, "Error starting camera preview: " + e.getMessage());
}
}
/**
* Disable the default shutter sound when taking a picture.
*/
private void disableShutterSound() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
boolean shutterState = mCamera.enableShutterSound(false);
LogUtils.i(TAG, "Shutter sound was" + (!shutterState ? "not " : " ") + "disabled");
} else {
LogUtils.i(TAG, "Trying to disable shutter sound by altering the system audio manager.");
// Backward compatibility method for disabling the shutter sound
AudioManager audio = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
currentVolume = audio.getStreamVolume(AudioManager.STREAM_SYSTEM);
audio.setStreamVolume(AudioManager.STREAM_SYSTEM, 0, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
MediaPlayer media = MediaPlayer.create(getContext(), R.raw.camera_shutter_click);
media.setAudioStreamType(AudioManager.STREAM_NOTIFICATION);
isVolumeChanged = true;
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mCamera != null) {
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
if (mSupportedPreviewSizes != null) {
mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
}
float ratio;
if (mPreviewSize.height >= mPreviewSize.width) {
ratio = (float) mPreviewSize.height / (float) mPreviewSize.width;
} else {
ratio = (float) mPreviewSize.width / (float) mPreviewSize.height;
}
// One of these methods should be used, second method squishes preview slightly
// setMeasuredDimension(width, width);
setMeasuredDimension(width, (int) (width * ratio));
// setMeasuredDimension((int) (width * ratio), height);
} else {
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
}
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) h / w;
if (sizes == null) {
return null;
}
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
for (Camera.Size size : sizes) {
double ratio = (double) size.height / size.width;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) {
continue;
}
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
public void releaseCamera() {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
mCamera.release();
mCamera = null;
}
}
/**
* Check if the Camera object was passed and set to the SurfaceView.
*
* #return {#code true} if the Camera object is valid, {#code false} otherwise
*/
public boolean isCameraSet() {
return null != mCamera;
}
public void takePicture() {
if (null != mCamera) {
// Disable the shutter sound when taking an picture
disableShutterSound();
mCamera.takePicture(shutterCallback, rawCallback, postViewCallback, jpegCallback);
}
}
private void resetCam() {
if (null != mCamera) {
mCamera.startPreview();
setCamera(mCamera);
}
}
/**
* The shutter callback occurs after the image is captured
*/
private Camera.ShutterCallback shutterCallback = new Camera.ShutterCallback() {
public void onShutter() {
MediaPlayer.create(getContext(), R.raw.camera_shutter_click).start();
LogUtils.d(TAG, "onShutter");
}
};
/**
* The raw callback occurs when the raw image data is available.<br/>(<b>NOTE:</b> the data will be null if there is
* no raw image callback buffer available or the raw image callback buffer is not large enough to hold the raw
* image).
*/
private Camera.PictureCallback rawCallback = new Camera.PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
LogUtils.d(TAG, "onPictureTaken - raw");
}
};
/**
* The jpeg callback occurs when the compressed image is available.
*/
private Camera.PictureCallback jpegCallback = new Camera.PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
if (isVolumeChanged) {
// Reset the audio settings which where set before trying to take an picture
AudioManager audio = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
audio.setStreamVolume(AudioManager.STREAM_SYSTEM, currentVolume,
AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
// reset flag for volume control
isVolumeChanged = false;
}
new CameraSavePicture().execute(data);
resetCam();
LogUtils.d(TAG, "onPictureTaken - jpeg");
}
};
/**
* The postview callback occurs when a scaled, fully processed postview image is available. <br/> (<b>NOTE:</b> not
* all hardware supports this)
*/
private Camera.PictureCallback postViewCallback = new Camera.PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
LogUtils.d(TAG, "onPictureTaken - postview");
}
};
}
This is the relevant part of the layout for the fragment:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/black">
<FrameLayout
android:id="#+id/full_camera_preview"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<FrameLayout
android:id="#+id/small_camera_preview"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
/>
</RelativeLayout>
And this is the Camera fragment:
public class CameraFragment extends Fragment implements View.OnClickListener, EventBusInterface {
private static final String TAG = "Camera Fragment";
private ImageButton flipCameraButton;
private Button takePicture;
private RelativeLayout content;
private RelativeLayout controlPanel;
private CameraPreview fullCameraPreview;
private FrameLayout fullCameraLayout;
private CameraPreview smallCameraPreview;
private FrameLayout smallCameraLayout;
/**
* The mode of the camera which shoul initially be displayed.
*/
private CameraMode currentCameraMode = CameraMode.BACK;
/**
* Do not use to instantiate a fragment. Use newInstance method instead.
*/
public CameraFragment() {
}
/**
* Use this method to get a new instance of the fragment, and give eventually add parameters if you need to pass
* data to it and retrieve it in onCreate using getArguments.
*
* #return a new instance of the current fragment.
*/
public static CameraFragment newInstance() {
final CameraFragment cameraFragment = new CameraFragment();
Bundle arguments = new Bundle();
cameraFragment.setArguments(arguments);
return cameraFragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Get the argument passed on the custom initialization of the fragment
Bundle arguments = getArguments();
if (null != savedInstanceState) {
// Get the last camera mode set by the user
CameraMode savedCameraMode = (CameraMode) savedInstanceState.getSerializable("CAMERA_MODE");
if (null != savedCameraMode) {
currentCameraMode = savedCameraMode;
}
}
// setRetainInstance(true); //Will ignore onDestroy Method (Nested Fragments no need this if parent
// have it)
}
#Override
public void onSaveInstanceState(Bundle outState) {
outState.putSerializable("CAMERA_MODE", currentCameraMode);
super.onSaveInstanceState(outState);
}
#Override
public void onViewStateRestored(Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_timeline_camera, container, false);
// BUG FIX: To avoid the window background overlapping this fragment we set it's background to transparent so
// that when the fragment is moved(scrolled) it will still be visible
this.getActivity().getWindow().setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT));
initViews(view);
setListeners();
// Setup the camera mode which will initially be displayed.
setUpCameraMode(currentCameraMode);
return view;
}
#Override
public void onResume() {
super.onResume();
}
#SuppressWarnings("deprecation")
private void setUpCameraMode(final CameraMode cameraMode) {
// The front camera SurfaceView will need to be changed, release it first
releaseSmallCamera();
// The full SurfaceView has to be changed because the camera mode will be changed from Back to Front or
// Front to Back
releaseFullCamera();
resizeFullCamera(cameraMode == CameraMode.DUAL);
switch (cameraMode) {
case BACK:
setupFullCamera(CameraInfo.CAMERA_FACING_BACK);
break;
case FRONT:
setupFullCamera(CameraInfo.CAMERA_FACING_FRONT);
break;
case DUAL:
setUpDualCamera();
break;
}
}
#Override
public void onStart() {
super.onStart();
if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this);
}
// This is required because we need to resume the camera when we get back in the fragment.
if (null != getView()) {
// Setup the camera mode which will initially be displayed.
setUpCameraMode(currentCameraMode);
}
}
#Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
// The front camera SurfaceView will need to be changed, release it first
releaseSmallCamera();
// The full SurfaceView has to be changed because the camera mode will be changed from Back to Front or
// Front to Back
releaseFullCamera();
}
/**
* Initialize the view for the current fragment.
*
* #param view
* The view which was inflated for this fragment.
*/
private void initViews(View view) {
controlPanel = (RelativeLayout) view.findViewById(R.id.control_panel);
fullCameraLayout = (FrameLayout) view.findViewById(R.id.full_camera_preview);
smallCameraLayout = (FrameLayout) view.findViewById(R.id.small_camera_preview);
flipCameraButton = (ImageButton) view.findViewById(R.id.flip_camera_button);
takePicture = (Button) view.findViewById(R.id.camera_take_picture);
}
/**
* Setup the listeners required for this fragment.
*/
private void setListeners() {
flipCameraButton.setOnClickListener(this);
takePicture.setOnClickListener(this);
}
/**
* Sets up the SurfaceView where the Full preview will be display. <br/> The full SurfaceView can show the camera
* that faces the same direction as the screen and the camera tjat faces the opposite direction as the screen.
*
* #param facingOfTheCamera
* The facing of the camera that has to be shown in the Full SurfaceView. <br/>Usually {#link
* CameraInfo#CAMERA_FACING_BACK} or {#link CameraInfo#CAMERA_FACING_FRONT}.<br/> If an invalid value is
* set, the preview for the back camera will be setup.
*/
#SuppressWarnings("deprecation")
private void setupFullCamera(int facingOfTheCamera) {
// Full camera can show the back & front cameras
CameraMode cameraMode;
if (facingOfTheCamera == CameraInfo.CAMERA_FACING_BACK) {
cameraMode = CameraMode.BACK;
} else if (facingOfTheCamera == CameraInfo.CAMERA_FACING_FRONT) {
cameraMode = CameraMode.FRONT;
} else {
// Default to the back camera
cameraMode = CameraMode.BACK;
}
if (null == fullCameraPreview) {
fullCameraPreview = new CameraPreview(getActivity(), CameraUtils.getCamera(cameraMode));
}
if (fullCameraLayout.getVisibility() != View.VISIBLE) {
fullCameraLayout.setVisibility(View.VISIBLE);
}
if (fullCameraLayout.getChildCount() == 0) {
// The View for displaying the Camera preview was not added in the layout, we need to add it now.
fullCameraLayout.addView(fullCameraPreview);
}
}
/**
* Sets up the SurfaceView where the Small preview will be display. <br/> The small SurfaceView will always show the
* camera that faces the same direction as the screen.
*/
private void setupSmallCamera() {
// Small camera always shows the camera that faces the same as the screen
if (null == smallCameraPreview) {
smallCameraPreview = new CameraPreview(getActivity(), CameraUtils.getCamera(CameraMode.FRONT));
}
smallCameraPreview.setZOrderOnTop(true);
// smallCameraPreview.setZOrderMediaOverlay(true);
if (smallCameraLayout.getVisibility() != View.VISIBLE) {
smallCameraLayout.setVisibility(View.VISIBLE);
}
if (smallCameraLayout.getChildCount() == 0) {
// The View for displaying the Camera preview was not added in the layout, we need to add it now.
smallCameraLayout.addView(smallCameraPreview);
}
}
/**
* Setup Full SurfaceView preview (display Back camera) and Small SurfaceView preview (display Front camera).
*/
#SuppressWarnings("deprecation")
private void setUpDualCamera() {
// In dual mode camera preview display, the full preview is showing the back camera & the small preview
// displays the front camera
setupFullCamera(CameraInfo.CAMERA_FACING_BACK);
setupSmallCamera();
}
private void resizeFullCamera(boolean shrink) {
// The dual camera doesn't work if two SurfaceViews overlap - use this to resize the full camera so that it
// is not overlapped by the small camera
RelativeLayout.LayoutParams p = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
if (shrink) {
p.addRule(RelativeLayout.BELOW, R.id.small_camera_preview);
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
p.removeRule(RelativeLayout.BELOW);
} else {
p.addRule(RelativeLayout.BELOW, 0);
}
}
fullCameraLayout.setLayoutParams(p);
}
/**
* Release the Camera Object used for the full camera mode. <br/> Remove the SurfaceView from the layout and
* invalidate it.
*/
private void releaseFullCamera() {
if (null != fullCameraPreview) {
fullCameraPreview.releaseCamera();
}
if (null != fullCameraLayout) {
// Remove the SurfaceView from the layout
fullCameraLayout.removeAllViews();
fullCameraLayout.setVisibility(View.INVISIBLE);
}
// Invalidate the SurfaceView
fullCameraPreview = null;
}
/**
* Release the Camera Object used for the small camera mode. <br/> Remove the SurfaceView from the layout and
* invalidate it.
*/
private void releaseSmallCamera() {
if (null != smallCameraPreview) {
smallCameraPreview.releaseCamera();
}
if (null != smallCameraLayout) {
// Remove the SurfaceView from the layout
smallCameraLayout.removeAllViews();
smallCameraLayout.setVisibility(View.INVISIBLE);
}
// Invalidate the SurfaceView
smallCameraPreview = null;
}
/**
* Flip between the camera modes supported. <br/> The flip is like an infinite carousel.
*/
private void flipCamera() {
// Set up the next mode for the camera preview/s display
setUpCameraMode(currentCameraMode.getNext());
// Update the current mode of the camera preview/s display
currentCameraMode = currentCameraMode.getNext();
}
#Override
public void onClick(View v) {
final int id = v.getId();
switch (id) {
case R.id.flip_camera_button:
// Flip camera to the next mode available.
flipCamera();
break;
case R.id.camera_take_picture:
takePicture();
break;
}
}
private void takePicture() {
switch (currentCameraMode) {
case BACK:
case FRONT:
if (null != fullCameraPreview) {
fullCameraPreview.takePicture();
} else {
ToastModel toastModel = new ToastModel(ToastType.GENERAL, "Please wait for camera warming up!");
new CustomToast(getActivity()).displayToast(toastModel);
}
break;
case DUAL:
ToastModel toastModel = new ToastModel(ToastType.GENERAL, "Dual picture not supported yet!");
new CustomToast(getActivity()).displayToast(toastModel);
break;
}
}
#Override
public void onEvent(Object event) {
}
#Override
public void onEventMainThread(Object event) {
if (event instanceof String) {
ToastModel toastModel = new ToastModel(ToastType.GENERAL, (String) event);
new CustomToast(getActivity()).displayToast(toastModel);
}
}
#Override
public void onEventBackgroundThread(Object event) {
}
#Override
public void onEventAsync(Object event) {
}
}
What could be the issue for this weird behavior?
Currently I'm not trying to show both previews at the same time, but one by one.
LE:
I have updated the logic for how the flip between camera modes is done.
When only the front or back camera are displayed in the full SurfaceView everything works fine. The problem appears when I use both of them (not necessarily both Camera objects at the same time, but both SurfaceViews visible at the same time & simply change from one to another).
I noticed the following errors in the logs:
D/Camera﹕ [seungmin]_open start
D/Camera﹕ [China_security]_before
D/Camera﹕ [China_security]_after
D/Camera﹕ [seungmin]_open End
D/CameraPreview﹕ Error setting camera parameters: getParameters failed (empty parameters)
W/CameraBase﹕ mediaserver's remote binder Camera object died
W/CameraBase﹕ Camera service died!
W/CameraBase﹕ mediaserver's remote binder Camera object died
E/Camera﹕ Error 100
E/Camera﹕ Error 100
LE2:
I have updated my code & optimized the flow.
I have also figured out why the Camera dies - it was because the SurfaceView for the second Camera was placed on top of the first one. Once the SurfaceViews didn't overlap anymore everything started working fine.
My question now is how can I fix this issue? Why is this happening?
PS: Most of the tests where made on LG G2 device which supports dual shot directly.
You may have stumbled upon an Android limitation regarding overlapping SurfaceViews. You can read more about this limitation here.
Don't think of SurfaceViews as classic views, they function more like a placeholder for getting the screen area (position and size) you want to surrender control to a rendering component. Rendering is actually done on a separate window.
As a solution, you can try calling setZOrderOnTop(true) on the SurfaceView you want on top.This is false by default, so the 2 SurfaceViews will not be on the same z anymore - making them work; one drawback is that the one on top will also be on top of any view you have.
If they do end up working, this is not guaranteed to happen on all devices, so I recommend extensive testing on what devices you consider important.
Another thing you can research is to see if you can tap into the Camera feed and see if you can merge the 2 streams and feeding the result to 1 SurfaceView

Surface View Camera Preview

Having a little trouble getting my SurfaceView to show my camera preview. I've looked at some questions on here and Google'd some tuts but I think it may be a small error on my end that I'm just not seeing.
Code
public class RoofPitchActivity extends Activity implements SensorEventListener {
...
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private Camera mCamera;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_roof_pitch);
initViews();
}
private void initViews() {
...
Preview preview = new Preview(this);
mCamera = Camera.open();
preview.setCamera(mCamera);
}
...
...
class Preview extends ViewGroup implements SurfaceHolder.Callback {
public Preview(Context context) {
super(context);
mSurfaceView = (SurfaceView) findViewById(R.id.preview);
mSurfaceView = new SurfaceView(context);
addView(mSurfaceView);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);
}
public void setCamera(Camera camera) {
if (mCamera == camera) {
return;
}
stopPreviewAndFreeCamera();
mCamera = camera;
if (mCamera != null) {
requestLayout();
try {
mCamera.setPreviewDisplay(mSurfaceHolder);
} catch (IOException e) {
e.printStackTrace();
}
mCamera.startPreview();
}
}
private void stopPreviewAndFreeCamera() {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
mCamera.startPreview();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mSurfaceView.getWidth(), mSurfaceView.getHeight());
requestLayout();
mCamera.setParameters(parameters);
mCamera.startPreview();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (mCamera != null) {
mCamera.stopPreview();
}
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
}
}
So when the activity is launched the SurfaceView is black. The camera does not appear to be rendering the preview onto the SurfaceView. I'm sure there's something small I'm missing, or maybe it's just a fundemental misunderstanding of how this works. A fresh set of eyes with a little explanation would be very much appreciated. Thanks
So the answer to my question ended up being device specific. The code was acceptable but my device I'm using to debug is a Nexus 7. This means the camera is front facing and the normal call to the camera does not work. An interesting and funny read about this can be found in this article
Camera.open();
does not work. You must use
Camera.open(0);
So my code ended up with a conditional check for the camera and now it works fine.
Like so
public void setCamera() {
stopPreviewAndFreeCamera();
PackageManager pm = getPackageManager();
boolean backCamera = pm.hasSystemFeature(PackageManager.FEATURE_CAMERA);
boolean frontCamera = pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT);
if (frontCamera) {
mCamera = Camera.open(0);
} else if (backCamera) {
mCamera = Camera.open();
}
if (mCamera != null) {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(mSurfaceView.getWidth(), mSurfaceView.getHeight());
requestLayout();
try {
mCamera.setParameters(parameters);
mCamera.setPreviewDisplay(mSurfaceHolder);
} catch (IOException e) {
e.printStackTrace();
}
mCamera.startPreview();
}
}

Exit onPreviewFrame and notify user

I am working the onPreviewFrame(byte[] data, Camera camera) on Android. Within the onPreviewFrame I do some image processing. At a point inside the onPreviewFrame I want to stop the preview (I know the point by an if statement) and play a sound - perhaps the phone ringtome. I think you can not play a sound in the preview.
How do I exit the onPreviewFrame and where do I add the code for playing the sound?
Is it on Surface Destroyed?
Here is my code:
public class MyCameraPreview extends Activity {
private Preview mPreview;
public TextView results;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Hide the window title.
requestWindowFeature(Window.FEATURE_NO_TITLE);
// Create our Preview view and set it as the content of our activity.
mPreview = new Preview(this);
setContentView(mPreview);
}
}
class Preview extends SurfaceView implements SurfaceHolder.Callback, PreviewCallback {
SurfaceHolder mHolder;
Camera mCamera;
public TextView results;
public TextView txt;
private Parameters parameters;
//this variable stores the camera preview size
private Size previewSize;
//this array stores the pixels as hexadecimal pairs
private int[] pixels;
public int[] argb8888;
Preview(Context context) {
super(context);
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, acquire the camera and tell it where
// to draw.
mCamera = Camera.open();
try {
mCamera.setPreviewDisplay(holder);
//sets the camera callback to be the one defined in this class
mCamera.setPreviewCallback(this);
parameters = mCamera.getParameters();
parameters.setZoom(parameters.getMaxZoom());
mCamera.setParameters(parameters);
parameters = mCamera.getParameters();
previewSize = parameters.getPreviewSize();
pixels = new int[previewSize.width * previewSize.height];
} catch (IOException exception) {
mCamera.release();
mCamera = null;
// TODO: add more exception handling logic here
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// Surface will be destroyed when we return, so stop the preview.
// Because the CameraDevice object is not a shared resource, it's very
// important to release it when the activity is paused.
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// Now that the size is known, set up the camera parameters and begin
// the preview.
parameters.setPreviewSize(w, h);
//set the camera's settings
mCamera.setParameters(parameters);
mCamera.startPreview();
}
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
Do some image processing.
If condition == true {
Exit the preview and then play the ringtone and exit the application.
}
}
In Preview you can add a definition of a listener and use it to not doint the sound in the Preview.
Something like this :
public static interface OnPreviewListener {
void onImageMakeSound();
}
public void setListener(OnPreviewListener listener) {
this.listener = listener;
}
Then in your onPreviewFrame method :
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
if (countFrame > 5) {
imageBytes = data;
countFrame = 0;
if (listener != null)
listener.onImageMakeSound();
}
countFrame++;
camera.addCallbackBuffer(data);
return;
}
In your activity, that must implements OnPreviewListener :
#Override
public void onImageMakeSound() {
alarmSoundOn();
}

android camera preview blank screen

I want to capture a photo manually not by using existing camera apps. So i made this Activity:
public class CameraActivity extends Activity {
private Camera mCamera;
private Preview mPreview;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
// Create an instance of Camera
mCamera = Camera.open(0); if(mCamera !=null) Log.i("CameraActivity.onCreate()", "mCamera initialized");
// Create our Preview view and set it as the content of our activity.
mPreview = new Preview(this); if(mPreview !=null) Log.i("CameraActivity.onCreate()", "mPreview initialized");
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mPreview); Log.i("CameraActivity.onCreate()", "mPreview added to FrameLayout");
mPreview.setCamera(mCamera); Log.i("CameraActivity.onCreate()", "mPreview.setCamera(mCamera)");
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_camera, menu);
return true;
}
#Override
public void onPause() {
super.onPause();
mPreview.stopPreviewAndFreeCamera();
}
}
This is the CameraPreview which i want to use:
public class Preview extends ViewGroup implements SurfaceHolder.Callback {
SurfaceView mSurfaceView;
SurfaceHolder mHolder;
private Camera mCamera;
List<Size> mSupportedPreviewSizes;
/**
* #param context
*/
public Preview(Context context) {
super(context);
mSurfaceView = new SurfaceView(context); Log.i("CameraPreview.constructor(...)", "mSurfaceView initialized");
if(mSurfaceView==null) {Log.i("CameraPreview.constructor(...)", "mSurfaceView is null");}
addView(mSurfaceView);
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = mSurfaceView.getHolder(); Log.i("CameraPreview.constructor(...)", "mHolder setup");
if(mHolder==null) {Log.e("PreviewCamera", "mHolder is null");}
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
/**
* #param context
* #param attrs
*/
public Preview(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
/**
* #param context
* #param attrs
* #param defStyle
*/
public Preview(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
/* (non-Javadoc)
* #see android.view.ViewGroup#onLayout(boolean, int, int, int, int)
*/
#Override
protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
// TODO Auto-generated method stub
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// Now that the size is known, set up the camera parameters and begin
// the preview.
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(width, height);
requestLayout();
mCamera.setParameters(parameters);
/*
Important: Call startPreview() to start updating the preview surface. Preview must be
started before you can take a picture.
*/
mCamera.startPreview(); Log.i("CameraPreview.surfaceChanged(...)", "mCamera.startPreview()");
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// Surface will be destroyed when we return, so stop the preview.
if (mCamera != null) {
/*
Call stopPreview() to stop updating the preview surface.
*/
mCamera.stopPreview(); Log.i("CameraPreview.surfaceDestroyed(...)", "mCamera.stopPreview()");
}
}
/**
* When this function returns, mCamera will be null.
*/
public void stopPreviewAndFreeCamera() {
if (mCamera != null) {
/*
Call stopPreview() to stop updating the preview surface.
*/
mCamera.stopPreview(); Log.i("CameraPreview.stopPreviewAndFreeCamera()", "mCamera.stopPreview()");
/*
Important: Call release() to release the camera for use by other applications.
Applications should release the camera immediately in onPause() (and re-open() it in
onResume()).
*/
mCamera.release(); Log.i("CameraPreview.stopPreviewAndFreeCamera()", "mCamera.release()");
mCamera = null;
}
}
/***
*
* #param camera
*/
public void setCamera(Camera camera) {
if (mCamera == camera) { Log.i("CameraPreview.setCamera()", "mCamera equals the Camera you want to set"); return; }
stopPreviewAndFreeCamera(); Log.i("CameraPreview.setCamera()", "stopPreviewAndFreeCamera()");
mCamera = camera; Log.i("CameraPreview.setCamera()", "setup new Camera");
if (mCamera != null) {
List<Size> localSizes = mCamera.getParameters().getSupportedPreviewSizes();
mSupportedPreviewSizes = localSizes;
requestLayout();
try {
mCamera.setPreviewDisplay(mHolder);
Log.i("CameraPreview.setCamera()", "mCamera.setPreviewDisplay(mHolder)");
} catch (IOException e) {
e.printStackTrace();
}
/*
Important: Call startPreview() to start updating the preview surface. Preview must
be started before you can take a picture.
*/
mCamera.startPreview(); Log.i("CameraPreview.setCamera()", "mCamera.startPreview()");
}
}
/***
*
* #param id
* #return
*/
public boolean safeCameraOpen(int id) {
boolean qOpened = false;
try {
releaseCameraAndPreview();
mCamera = Camera.open(id);
qOpened = (mCamera != null);
} catch (Exception e) {
// Log.e(R.string.app_name, "failed to open Camera");
e.printStackTrace();
}
return qOpened;
}
/**
*
*/
Camera.PictureCallback mPicCallback = new Camera.PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
// TODO Auto-generated method stub
Log.d("lala", "pic is taken");
}
};
/**
*
*/
public void takePic() {
// mCamera.takePicture(null, mPicCallback, mPicCallback);
}
/**
*
*/
private void releaseCameraAndPreview() {
this.setCamera(null);
if (mCamera != null) {
mCamera.release();
mCamera = null;
}
}
}
This is the Layout of CameraActivity:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CameraActivity" >
<FrameLayout
android:id="#+id/camera_preview"
android:layout_width="0dip"
android:layout_height="fill_parent"
android:layout_weight="1"
/>
<Button
android:id="#+id/button_capture"
android:text="Capture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/>
</LinearLayout>
But when i start the CameraActivity, i just see a blank white background and the Capture-Button??? Why i dont see the Camera-Screen?
EDIT: Logcat:
12-16 13:09:39.941: I/CameraActivity.onCreate()(439): mCamera initialized
12-16 13:09:39.941: I/CameraPreview.constructor(...)(439): mSurfaceView initialized
12-16 13:09:39.941: I/CameraPreview.constructor(...)(439): mHolder setup
12-16 13:09:39.941: I/CameraActivity.onCreate()(439): mPreview initialized
12-16 13:09:39.952: I/CameraActivity.onCreate()(439): mPreview added to FrameLayout
12-16 13:09:39.952: I/CameraPreview.setCamera()(439): stopPreviewAndFreeCamera()
12-16 13:09:39.952: I/CameraPreview.setCamera()(439): setup new Camera
12-16 13:09:39.961: D/Camera(439): app passed NULL surface
12-16 13:09:39.961: I/CameraPreview.setCamera()(439): mCamera.setPreviewDisplay(mHolder)
12-16 13:09:39.971: I/CameraPreview.setCamera()(439): mCamera.startPreview()
12-16 13:09:39.971: I/CameraActivity.onCreate()(439): mPreview.setCamera(mCamera)
12-16 13:09:40.622: I/ActivityManager(60): Displayed com.example.popup/.CameraActivity: +810ms
I think you should call open() method after addView().
Camera preview size is set by addView().
You aren't calling setCamera with the camera (only null), so you never set the preview display.
I was having same problem. Then I extended it to SurfaceView instead of ViewGroup. And in a magical moment it worked...
You can check whether the layout_width and layout_height properties of Preview is set to match_parent.
It looks like the OP followed the 'Controlling the camera' tutorial from http://developer.android.com/training/camera/cameradirect.html. I tried the same, but soon found out that this tutorial doesn't work.
To save other beginners the same frustration of trying to get the tutorial to work, here's another tutorial which does work: http://www.tutorialspoint.com/android/android_camera.htm.

Android Moto Droid Camera Hangs EVERY Time onTakePicture

All, I've googled over and over again to find a solution and while I found a bug regarding camera release, etc I can not for the life of me seem to get the cam code to work. Every time I executed takePicture the system simply hangs, sometimes it calls the PictureCallback, but most of the time it simply hangs.
Weird issues about not being able to read /data/ap_gain.bin files, etc
Below is the code:
public class CameraActivity extends Activity implements Camera.PictureCallback, RequestConstants {
private static final String TAG = "Camera";
private Preview preview;
private boolean previewRunning;
private int addressNotificationId;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addressNotificationId = getIntent().getIntExtra(REQ_RA_ID, 0);
getWindow().setFormat(PixelFormat.TRANSLUCENT);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
if (preview == null) {
preview = new Preview(this);
}
setContentView(preview);
}
#Override
protected void onDestroy() {
if (isFinishing()) {
preview.cleanup();
}
super.onDestroy();
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_CAMERA) {
/*
preview.setDrawingCacheEnabled(true);
Bitmap ss = preview.getDrawingCache();
byte[] data = ImageUtility.getImageData(ss,75,1);
Log.v(TAG, "Pic with size: " + data.length);
ApplicationManager.getInstance().createPacketRecord(PacketConstants.PT_FLAG_ADDRESS_PHOTO, ApplicationDatabaseManager.getInstance().getRouteAddressBySystemId(addressNotificationId), data);
finish();
*/
preview.getCamera().takePicture(new Camera.ShutterCallback() {
#Override
public void onShutter() {
}
}, null, this);
return true;
}
return super.onKeyDown(keyCode, event);
}
#Override
public void onPictureTaken(byte[] data, Camera camera) {
/*
if (data == null || isFinishing())
return;
camera.stopPreview();
previewRunning = false;
camera.release();
*/
//Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0,data.length);
//data = null;
//data = ImageUtility.getImageData(bitmap, 75,1);
Log.v(TAG, "Pic with size: " + data.length);
ApplicationManager.getInstance().createPacketRecord(PacketConstants.PT_FLAG_ADDRESS_PHOTO, ApplicationDatabaseManager.getInstance().getRouteAddressBySystemId(addressNotificationId), data);
finish();
}
}
class Preview extends SurfaceView implements SurfaceHolder.Callback {
SurfaceHolder mHolder;
Camera mCamera;
Preview(Context context) {
super(context);
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
Camera getCamera() {
return mCamera;
}
void cleanup() {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
public void surfaceCreated(SurfaceHolder holder) {
if (mCamera == null)
mCamera = Camera.open();
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException exception) {
mCamera.release();
mCamera = null;
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(w, h);
parameters.setPictureSize(w, h);
mCamera.setParameters(parameters);
mCamera.startPreview();
}
}
You are sometimes taking a picture and sometimes not, because you are releasing the camera before the callback has occurred.
When the callback does fire, depending on whether the camera has released or not it will or will not be able to access the taken photo.
I suggest making sure that you are not releasing the camera or closing the form when you take a photo.
Better yet, close the form from the photo callback.
Also, when a photo is taken, the default action of android is to stop the Preview. this is not a bug, but the expected nature.
After you JpegPictureCallback is called, you need to call mCamera.startPreview again.
There isn't enough info in the API Camera sample to really write a camera app, but you can get the source code for Google's own camera app here. Camera.java contains a lot of important, useful code:
git://android.git.kernel.org/platform/packages/apps/Camera.git

Categories

Resources