I have to solve a problem with android hardware camera. I want my device always record landscape video even when its orientation is vertical. I'd drawn some pics to support my issue:
This is what I get: http://joxi.ru/9IHbU_3JTJDrRyVH-XA
This is what I want: http://joxi.ru/IYLbU4wyTJC1LhQcd4Q
I can't say is it even possible (I think it's not, because of hardware matrix can't rotate). But my client thinks it is. Please, judge us.
Update: there's my code.
//..import...
public class MainActivity extends Activity {
SurfaceView surfaceView;
Camera camera;
MediaRecorder mediaRecorder;
//...public and private variables...
#Override
protected void onCreate(Bundle savedInstanceState) {
//......
SurfaceHolder holder = surfaceView.getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
camera.setPreviewDisplay(holder);
setCameraDisplayOrientation(CAM_ID);
camera.startPreview();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
});
}
#Override
protected void onResume() {
super.onResume();
camera = Camera.open(this.CAM_ID);
}
#Override
protected void onPause() {
super.onPause();
releaseMediaRecorder();
if (camera != null)
camera.release();
camera = null;
}
public void setListeners() {/*....*/}
public void onClickStartRecord(View view) {
if (prepareVideoRecorder()) {
mediaRecorder.start();
} else releaseMediaRecorder();
}
public void onClickStopRecord(View view) {
if (mediaRecorder != null) {
mediaRecorder.stop();
releaseMediaRecorder();
}
}
private boolean prepareVideoRecorder() {
camera.unlock();
mediaRecorder = new MediaRecorder();
mediaRecorder.setCamera(camera);
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
CamcorderProfile profile = CamcorderProfile.get(this.CAM_ID, CamcorderProfile.QUALITY_LOW);
mediaRecorder.setProfile(profile);
if(this.orientation == 270 || this.orientation == 90 )
mediaRecorder.setOrientationHint(90);
mediaRecorder.setOutputFile(videoFile.getAbsolutePath());
mediaRecorder.setPreviewDisplay(surfaceView.getHolder().getSurface());
try {
mediaRecorder.prepare();
} catch (Exception e) {
e.printStackTrace();
releaseMediaRecorder();
return false;
}
return true;
}
private void releaseMediaRecorder() {
if (mediaRecorder != null) {
mediaRecorder.reset();
mediaRecorder.release();
mediaRecorder = null;
camera.lock();
}
}
private void setCameraDisplayOrientation(int cameraId) {
// определяем насколько повернут экран от нормального положения
int rotation = getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result = 0;
CameraInfo info = new CameraInfo();
Camera.getCameraInfo(cameraId, info);
if (info.facing == CameraInfo.CAMERA_FACING_BACK) {
result = ((360 - degrees) + info.orientation);
} else
if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
result = ((360 - degrees) - info.orientation);
result += 360;
}
result = result % 360;
camera.setDisplayOrientation(result);
this.orientation = result;
}
}
I believe it is pretty easy to achieve what your customer wants. So, you hold the device in Portrait mode, and you record video at resolution 1280x720. Obviously, it is portrait oriented: 720 is width, and 1280 is height (note that probably the encoded frames are still 1280 width and 720 height, but the video has a special rotation flag so that a compliant decoder will show the output at correct orientation).
Now, crop the video so that the result is 406x720 (yes, you loose a lot of pixels this way). But the result will have the desired landscape orientation. 406 is height, 720 is width.
Now to technical details: you can crop either live or after recording. In the latter case, you can run ffmpeg either on a server or on the device.
In the former case, you must receive the camera callbacks, crop the arriving data, and pass it to encoder. You can do it completely in Java, using the modern MediaCodec API. If you choose to write some native code, you can use ffmpeg libraries to perform the necessary operations.
You can even resize the resulting frames, such that video decoders will play the 406x720 originals at 1280x720 resolution. The quality will never be as the original (portrait) video.
Why I am speaking about the weird 406 height? To keep the standard aspect ratio. You may choose 540x720 if you can use 4:3 aspect ratio.
Related
I have an app that takes pictures on a timer. Normally it works fine, except when the screen is rotated while the camera is sleeping. Can anybody tell me why it fails to take a picture after rotating in sleep?
Normally, when the camera goes to sleep and wakes up it executes...
#Override
protected void onPause() {
super.onPause();
camera.stopPreview();
camera.release();
//camera = null; //this give me a null object message
}
#Override
protected void onResume() {
super.onResume();
camera = Camera.open(cameraId);
setCameraDisplayOrientation();
startCameraPreview(surfaceHolder, surfaceView.getWidth(), surfaceView.getHeight());
}
However, when it wakes up after the camera has been rotated in sleep, it first executes onResume(), surfaceChanged(), onPause(), onStop(empty), onDestroy(empty), then the following:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surfaceView = (SurfaceView) findViewById(R.id.surface);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
}
#Override
protected void onStart() {
super.onStart();
}
#Override
protected void onResume() {
super.onResume();
camera = Camera.open(cameraId);
setCameraDisplayOrientation();
startCameraPreview(surfaceHolder, surfaceView.getWidth(), surfaceView.getHeight());
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
camera.stopPreview();
setCameraDisplayOrientation();
startCameraPreview(holder, w, h);
}
public void setCameraDisplayOrientation() {
deviceRotation = getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch(deviceRotation) {
case Surface.ROTATION_0: degrees=0; break;//vertical
case Surface.ROTATION_90: degrees=-90; break; //left side
case Surface.ROTATION_180: degrees=180; break; //upside-down
case Surface.ROTATION_270: degrees=90; break; //right side
}
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
displayRotation = info.orientation + degrees;
camera.setDisplayOrientation(displayRotation); //changes orientation of camera's display
}
public void startCameraPreview(SurfaceHolder holder, int w, int h) {
double targetRatio = 0;
switch(deviceRotation) {
case Surface.ROTATION_0: targetRatio = (double)w/(double)h; break; //vertical
case Surface.ROTATION_90: targetRatio = (double)h/(double)w; break; //left side
case Surface.ROTATION_180: targetRatio = (double)w/(double)h; break; //upside-down
case Surface.ROTATION_270: targetRatio = (double)h/(double)w; break; //right side
}
Camera.Parameters p = camera.getParameters();
List<Camera.Size> previewSizes = p.getSupportedPreviewSizes();
int optimal_h = 2; //always refers to short length in PreviewSizes
int optimal_w = 1; //always refers to long length in PreviewSizes
for(Camera.Size previewSize : previewSizes) {
if (Math.abs((double)previewSize.height/(double)previewSize.width - targetRatio) <
Math.abs((double)optimal_h/(double)optimal_w - targetRatio)) {
optimal_h = previewSize.height;
optimal_w = previewSize.width;
}
}
p.setPreviewSize(optimal_w, optimal_h); //defines ratio of image preview - sizes can be larger than actual display
p.set("rotation", displayRotation); //required to orient final jpeg file correctly
camera.setParameters(p);
ViewGroup.LayoutParams surfaceParams = surfaceView.getLayoutParams();
switch(deviceRotation) { //aligns ratio of surface view to ratio of image preview
case Surface.ROTATION_0:
surfaceParams.width=ViewGroup.LayoutParams.MATCH_PARENT;
surfaceParams.height=(int)(w*(double)optimal_w/(double)optimal_h);
break; //vertical
case Surface.ROTATION_90:
surfaceParams.width=(int)(h*(double)optimal_w/(double)optimal_h);
surfaceParams.height=ViewGroup.LayoutParams.MATCH_PARENT;
break; //left side
case Surface.ROTATION_180:
surfaceParams.width=ViewGroup.LayoutParams.MATCH_PARENT;
surfaceParams.height=(int)(w*(double)optimal_w/(double)optimal_h);
break; //upside-down
case Surface.ROTATION_270:
surfaceParams.width=(int)(h*(double)optimal_w/(double)optimal_h);
surfaceParams.height=ViewGroup.LayoutParams.MATCH_PARENT;
break; //right side
}
surfaceView.setLayoutParams(surfaceParams);
camera.setPreviewDisplay(holder); //required to startPreview
camera.startPreview();
}
This is not the complete code. This code does produce a few minor image formatting errors, but it still demonstrates the issue. I have stripped some things out for simplicity. The main issue is that when it tries to take a picture after been rotated in sleep it returns an error "Camera is being used after Camera.release() was called". Why is this?
This is really overdue but as I managed to solve a similar problem of mine a minute ago, I thought I'd contribute for the benefit of yourself and anyone else who might be desperately searching Stack.
So when you rotate the device, you are calling onPause and onDestroy, where you are releasing your camera. I noticed you do have camera.open() in your onResume, without a look at your surface-related code I cannot comment. Heres what worked for me.
Firstly, the cameraPreview
`
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
try {
this.mCamera.setPreviewDisplay(surfaceHolder);
this.mCamera.startPreview();
} catch (Exception e) {
}
}
public void surfaceCreated(SurfaceHolder surfaceHolder) {
try {
//TODO we need this here too because on SurfaceCreated we always need to open the camera, in case its released
this.mCamera.setPreviewDisplay(surfaceHolder);
this.mCamera.setDisplayOrientation(90);
//this.mCamera.startPreview();
} catch (IOException e) {
}
}
Next, the CameraActivity's Lifecycle code
#Override
public void onResume() {
super.onResume();
try{
mCamera = openFrontFacingCameraGingerbread();
// Add to Framelayout
this.mCameraPreview = new CameraPreview(this, this.mCamera);
mImage.removeAllViews();
this.mImage.addView(this.mCameraPreview);
}catch (RuntimeException ex){
}
}
#Override
public void onPause() {
super.onPause();
captureButton.setText("Begin Capture");
if(CameraActivity.this.timer !=null) {
CameraActivity.this.timer.cancel();
CameraActivity.this.timer.purge();
CameraActivity.this.timer = null;
}
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCameraPreview.getHolder().removeCallback(mCameraPreview);
mCamera.release();
mCamera = null;
}
}
#Override
protected void onDestroy(){
super.onDestroy();
releaseCameraAndPreview();
}
private void releaseCameraAndPreview() {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
if(mCameraPreview != null){
mCameraPreview.destroyDrawingCache();
mCameraPreview.mCamera = null;
}
}
Trying to record video without default camera of android. I found some sample code to fix my solution. Now in that camera previewing orientation in the framelayout is little weired. When I start the record with portrait the preview is landscape and vice-versa.
I tried with setting rotation for both surfaceview and framelayout. Then I tried with setting orientation and nothing worked for me.
My app is to save the recorded video of front facing camera and save it sd card. All are working fine except that orientation problem.
My code goes below,
Layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<RelativeLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<FrameLayout
android:id="#+id/videoview"
android:layout_width="200dp"
android:layout_height="200dp"/>
<Button
android:id="#+id/mybutton"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:text="REC"
android:textSize="12dp"/>
</RelativeLayout>
Java File:
public class RecordVideo extends Activity{
private Camera myCamera;
private MyCameraSurfaceView myCameraSurfaceView;
private MediaRecorder mediaRecorder;
Button myButton;
SurfaceHolder surfaceHolder;
boolean recording;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR | ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
recording = false;
setContentView(R.layout.main);
//Get Camera for preview
myCamera = getCameraInstance();
if(myCamera == null){
Toast.makeText(RecordVideo.this,
"Fail to get Camera",
Toast.LENGTH_LONG).show();
}
myCameraSurfaceView = new MyCameraSurfaceView(this, myCamera);
//myCameraSurfaceView.setO
FrameLayout myCameraPreview = (FrameLayout)findViewById(R.id.videoview);
myCameraPreview.addView(myCameraSurfaceView);
myButton = (Button)findViewById(R.id.mybutton);
myButton.setOnClickListener(myButtonOnClickListener);
}
Button.OnClickListener myButtonOnClickListener
= new Button.OnClickListener(){
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(recording){
// stop recording and release camera
mediaRecorder.stop(); // stop the recording
releaseMediaRecorder(); // release the MediaRecorder object
//Exit after saved
finish();
}else{
//Release Camera before MediaRecorder start
releaseCamera();
if(!prepareMediaRecorder()){
Toast.makeText(RecordVideo.this,
"Fail in prepareMediaRecorder()!\n - Ended -",
Toast.LENGTH_LONG).show();
finish();
}
mediaRecorder.start();
recording = true;
myButton.setText("STOP");
}
}};
private Camera getCameraInstance(){
// TODO Auto-generated method stub
Camera c = null;
try {
c = Camera.open(); // attempt to get a Camera instance
}
catch (Exception e){
// Camera is not available (in use or does not exist)
}
return c; // returns null if camera is unavailable
}
private boolean prepareMediaRecorder(){
myCamera = getCameraInstance();
mediaRecorder = new MediaRecorder();
myCamera.unlock();
mediaRecorder.setCamera(myCamera);
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH));
mediaRecorder.setOutputFile("/sdcard/myvideo.mp4");
mediaRecorder.setMaxDuration(60000); // Set max duration 60 sec.
mediaRecorder.setMaxFileSize(5000000); // Set max file size 5M
mediaRecorder.setPreviewDisplay(myCameraSurfaceView.getHolder().getSurface());
try {
mediaRecorder.prepare();
} catch (IllegalStateException e) {
releaseMediaRecorder();
return false;
} catch (IOException e) {
releaseMediaRecorder();
return false;
}
return true;
}
#Override
protected void onPause() {
super.onPause();
releaseMediaRecorder(); // if you are using MediaRecorder, release it first
releaseCamera(); // release the camera immediately on pause event
}
private void releaseMediaRecorder(){
if (mediaRecorder != null) {
mediaRecorder.reset(); // clear recorder configuration
mediaRecorder.release(); // release the recorder object
mediaRecorder = null;
myCamera.lock(); // lock camera for later use
}
}
private void releaseCamera(){
if (myCamera != null){
myCamera.release(); // release the camera for other applications
myCamera = null;
}
}
public class MyCameraSurfaceView extends SurfaceView implements SurfaceHolder.Callback{
private SurfaceHolder mHolder;
private Camera mCamera;
public MyCameraSurfaceView(Context context, Camera camera) {
super(context);
mCamera = camera;
// 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);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int weight,
int height) {
// 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
}
// make any resize, rotate or reformatting changes here
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
// The Surface has been created, now tell the camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
}
}
}
This should fix your issue:
private static void setCameraDisplayOrientation(Context mContext, android.hardware.Camera.CameraInfo info) {
int rotation = ((MainActivity) mContext).getWindowManager().getDefaultDisplay()
.getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
previewRotation = (info.orientation + degrees) % 360;
previewRotation = (360 - previewRotation) % 360; // compensate the mirror
} else { // back-facing
previewRotation = (info.orientation - degrees + 360) % 360;
}
mCameraInstance.setDisplayOrientation(previewRotation);
}
I have a simple app that has an activity being called periodically by an alarm manager to display a preview frame and take a picture when the preview frame is built.After taking the picture, it is saved using a AsyncTask and the activity destroyed using finish().
The code works perfectly fine when I have the screen turned on.However it fails to take a picture with the screen off.I want to monitor a house and take pictures periodically using the app and in that case keeping the screen always on or turning it on manually is not a viable option.
Also the code for the camera activity has been copied from Commonsware library and works perfectly great.I am only having a problem with taking a picture with screen off.I can also see from the logs that the camera is opened by the activity.However the Runnable that is supposed to take picture when the preview frame is built, doesn't run and the camera goes to the pause state instead and stays there.
I have the necessary permissions set up perfectly as I am able to get the images with screen turned on.Maybe I am having trouble understanding the activity lifecylce when the screen is off and someone can shed light there.
I tried using the wakelocks to turn the screen on but that didnt do any good.
Below is the code for the Activity.
Also I am sorry but removing the comment for the license to make it short here.
package com.thopedia.snapper; /***
Copyright (c) 2008-2012 CommonsWare, LLC
*/
import all;
public class CameraActivity1 extends Activity {
private PreviewFrameLayout frame=null;
private SurfaceView preview=null;
private SurfaceHolder previewHolder=null;
private Camera camera=null;
private boolean inPreview=false;
private boolean cameraConfigured=false;
private PowerManager.WakeLock wakeLock;
private PowerManager powerManager;
#SuppressWarnings("deprecation")
#Override
public void onCreate(Bundle savedInstanceState) {
/* powerManager = (PowerManager) getSystemService(POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, getClass()
.getName());*/
Log.v(GlobalVariables.TAG,"CameraActivity On create called");
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
frame=(PreviewFrameLayout)findViewById(R.id.frame);
preview=(SurfaceView)findViewById(R.id.preview);
previewHolder=preview.getHolder();
previewHolder.addCallback(surfaceCallback);
previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
#TargetApi(Build.VERSION_CODES.GINGERBREAD)
#Override
public void onResume() {
// wakeLock.acquire();
Log.v(GlobalVariables.TAG,"camera activity onResume called");
super.onResume();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
Camera.CameraInfo info=new Camera.CameraInfo();
for (int i=0; i < Camera.getNumberOfCameras(); i++) {
Camera.getCameraInfo(i, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
try{
camera=Camera.open(i);
}catch (Exception e){
Log.v(GlobalVariables.TAG,"Camera Opening Exception");
if(!isFinishing()) {
finish();
}}}}}
if (camera == null) {
try{
camera=Camera.open();
}catch (Exception e){
if(!isFinishing()) {
finish();
}
Log.v(GlobalVariables.TAG,"Camera opening exception");
}
}
startPreview();
preview.post(new Runnable() {
#Override
public void run() {
if (inPreview) {
camera.takePicture(null, null, photoCallback);
inPreview=false;
}
}
});
}
#Override
public void onPause() {
super.onPause();
Log.v(GlobalVariables.TAG,"Camera activity onPause called");
if (inPreview) {
if(camera!=null) {
camera.stopPreview();
}
}
if(camera!=null) {
camera.release();
camera = null;
}
inPreview=false;
}
#Override
protected void onDestroy() {
Log.v(GlobalVariables.TAG,"Camera activity onDestroy called!");
super.onDestroy();
if(camera!=null){
camera.release();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
new MenuInflater(this).inflate(R.menu.options, menu);
return(super.onCreateOptionsMenu(menu));
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.camera) {
if (inPreview) {
camera.takePicture(null, null, photoCallback);
inPreview=false;
}
}
return(super.onOptionsItemSelected(item));
}
private Camera.Size getBestPreviewSize(int width, int height,
Camera.Parameters parameters) {
Camera.Size result=null;
for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
if (size.width <= width && size.height <= height) {
if (result == null) {
result=size;
}
else {
int resultArea=result.width * result.height;
int newArea=size.width * size.height;
if (newArea > resultArea) {
result=size;
}
}
}
}
return(result);
}
private Camera.Size getSmallestPictureSize(Camera.Parameters parameters) {
Camera.Size result=null;
for (Camera.Size size : parameters.getSupportedPictureSizes()) {
if (result == null) {
result=size;
}
else {
int resultArea=result.width * result.height;
int newArea=size.width * size.height;
if (newArea < resultArea) {
result=size;
}
}
}
return(result);
}
private void initPreview(int width, int height) {
if (camera != null && previewHolder.getSurface() != null) {
try {
camera.setPreviewDisplay(previewHolder);
}
catch (Throwable t) {
Log.e("PreviewDemo-surfaceCallback",
"Exception in setPreviewDisplay()", t);
Toast.makeText(CameraActivity1.this, t.getMessage(),
Toast.LENGTH_LONG).show();
}
if (!cameraConfigured) {
Camera.Parameters parameters=camera.getParameters();
Camera.Size size=getBestPreviewSize(width, height, parameters);
Camera.Size pictureSize=getSmallestPictureSize(parameters);
if (size != null && pictureSize != null) {
parameters.setPreviewSize(size.width, size.height);
parameters.setPictureSize(pictureSize.width,
pictureSize.height);
parameters.setPictureFormat(ImageFormat.JPEG);
frame.setAspectRatio((double)size.width / size.height);
camera.setParameters(parameters);
cameraConfigured=true;
}
}
}
}
private void startPreview() {
if (cameraConfigured && camera != null) {
camera.startPreview();
inPreview=true;
}
}
SurfaceHolder.Callback surfaceCallback=new SurfaceHolder.Callback() {
public void surfaceCreated(SurfaceHolder holder) {
// no-op -- wait until surfaceChanged()
}
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
initPreview(width, height);
startPreview();
}
public void surfaceDestroyed(SurfaceHolder holder) {
// no-op
}
};
Camera.PictureCallback photoCallback=new Camera.PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
new SavePhotoTask().execute(data);
camera.startPreview();
inPreview=true;
if(!isFinishing()) {
finish();
}
}
};
I use the following piece of code to click a picture after the preview surface is properly created in onResume().
preview.post(new Runnable() {
#Override
public void run() {
if (inPreview) {
camera.takePicture(null, null, photoCallback);
inPreview=false;
}
}
});
Any help is appreciated.Thanks
I think you can use WakeLock to make sure that Screen-Off does not occur. Below is the sample code/algorithm by which you can turn on screen whenever it goes off. Hope This Helps!
Register a broadcast receiver on Intent.ACTION_SCREEN_OFF.
Whenever you get screen off broadcast intent, wake-up by using below code.
PowerManager pm = (PowerManager) context
.getSystemService(Context.POWER_SERVICE);
WakeLock wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK
| PowerManager.ACQUIRE_CAUSES_WAKEUP
| PowerManager.ON_AFTER_RELEASE, "MyWakeLock");
wakeLock.acquire();
android:keepScreenOn="true"
you can use above line to your parent layout in XML which you are calling through activity
it will keep your screen on always so you will not get any issue hope it will match your requirement
I figured out what the problem was after making extensive use of the LogCat :).
It seems that when the screen is kept on, the onPause() is not called instantly which is the case with the SCREEN_OFF.When the screen is ON, the Runnable is executed before the onPause() method executed and as such the pictures are taken perfectly fine.However in case when the screen is OFF, the Runnable is being executed after the Activity has completed the onPause() method.By this time we have already released the camera in onPause() and so we don't get a picture.
It started working after I figured out the flow and moved the camera release to the onDestroy() which might not be ideal for all situations but works just fine for mine because the only purpose of my Activity is to take a picture and then destroy itself.
Also the WAKELOCKS didn;t change the behaviour of the code.I would expect the Activity to not execute without the WAKE_LOCK but its working perfectly fine.
Hope this helps someone stuck in a similar situation.
I am creating an app that shows a moving picture on top of camera preview. And the way I am doing it is adding two SurfaceView, one holding camera preview and one holding my moving picture, in a framelayout inside my main activity. So basically there are three public classes, and one inner thread class inside the moving picture class to control the animation.
It worked fine upon starting the app - camera is previewing and picture is moving. But then if I pause the activity, by going to home screen or by redirecting into another activity with clicking the picture, and resume, the camera preview would black out. The wierd part is if I rotate the phone into a different mode (landscape/portrait), thing comes back to normal.
I've read several post about camera not resuming, but the solutions were all about opening camera. I'm pretty much sure my problem is not about the camera instance after examining. And actually if I pause activity by going to home screen, when I resume the camera would appear for a second and then black out.
I've been trying all kinds of things including removing all views from my layout in OnPause() and specifying index number when adding the views. But the only method that kind of got a little progress was when I comment out the canvas lock in the following block. Without the lock, the picture would move randomly, but camera could resume and all. In fact, if I leave out all things about thread and just display a static picture, camera works fine too. So I am sensing something is wrong with my thread here but I couldn't figure out.
Here's the thread's run method:
public void run() {
Canvas canvas;
while (isRunning) { //When setRunning(false) occurs, isRunning is
canvas = null; //set to false and loop ends, stopping thread
try {
canvas = surfaceHolder.lockCanvas(null); //Lock
synchronized (surfaceHolder) {
//Insert methods to modify positions of items in onDraw()
animation();
postInvalidate();
}
} finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas); //Unlock
}
}
}
}
Here's the part starting the thread:
public void surfaceCreated(SurfaceHolder arg0) {
setWillNotDraw(false); //Allows us to use invalidate() to call onDraw()
thread = new BubbleThread(getHolder(), this); //Start the thread that
thread.setRunning(true); //will make calls to
thread.start(); //onDraw()
}
Here's the part finishing the thread:
public void surfaceDestroyed(SurfaceHolder arg0) {
try {
thread.setRunning(false); //Tells thread to stop
thread.join(); //Removes thread from mem.
} catch (InterruptedException e) {}
}
[UPDATE] main activity code:
public class MainActivity extends Activity {
... // Declarations
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/* Adjust app settings */
...
//Load();
}
public void Load(){
/* Try to get the camera */
Camera c = getCameraInstance();
/* If the camera was received, create the app */
if (c != null){
// Create the parent layout to layer the
// camera preview and bubble layer
parentLayout = new FrameLayout(this);
parentLayout.setLayoutParams(new LayoutParams(
LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
// Create a new camera view and add it to the layout
cameraPreview = new CameraPreview(this, c);
parentLayout.addView(cameraPreview, 0);
// Create a new draw view and add it to the layout
bubbleLayer = new BubbleLayer(this);
parentLayout.addView(bubbleLayer, 1);
// Set the layout as the apps content view
setContentView(parentLayout);
}
/* If the camera was not received, close the app */
else {
Toast toast = Toast.makeText(getApplicationContext(),
"Unable to find camera. Closing.", Toast.LENGTH_SHORT);
toast.show();
finish();
}
}
/** A safe way to get an instance of the Camera object. */
/** This method is strait from the Android API */
public static Camera getCameraInstance(){
Camera c = null;
try {
// Attempt to get a Camera instance
c = Camera.open();
}
catch (Exception e){
// Camera is not available (in use or does not exist)
e.printStackTrace();
}
return c;
}
/* Override the onPause method so that we
* can release the camera when the app is closing.
*/
#Override
protected void onPause() {
super.onPause();
if (cameraPreview != null){
cameraPreview.onPause();
cameraPreview = null;
}
}
/* We call Load in our Resume method, because
* the app will close if we call it in onCreate
*/
#Override
protected void onResume(){
super.onResume();
Load();
}
}
[/UPDATE]
[UPDATE2] camera preview code:
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
... // Declarations
public CameraPreview(Context context, Camera camera) {
super(context);
this.context = context;
mCamera = camera;
// 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);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}
Camera.Parameters parameters = mCamera.getParameters();
Size bestSize = getBestSize(parameters.getSupportedPreviewSizes(),
width,height);
parameters.setPreviewSize(bestSize.width, bestSize.height);
mCamera.setParameters(parameters);
// 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
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
setCameraDisplayOrientation();
mCamera.startPreview();
} catch (Exception e){
Log.d("CameraView", "Error starting camera preview: "
+ e.getMessage());
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the
// camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.d("CameraView", "Error setting camera preview: "
+ e.getMessage());
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
/* Find the best size for camera */
private Size getBestSize(List<Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.05;
double targetRatio = (double) w / h;
if (sizes == null) return null;
Size bestSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for (Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
bestSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
if (bestSize == null) {
minDiff = Double.MAX_VALUE;
for (Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
bestSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return bestSize;
}
private void setCameraDisplayOrientation() {
if (mCamera == null) return;
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(0, info);
WindowManager winManager = (WindowManager)
context.getSystemService(Context.WINDOW_SERVICE);
int rotation = winManager.getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0: degrees = 0; break;
case Surface.ROTATION_90: degrees = 90; break;
case Surface.ROTATION_180: degrees = 180; break;
case Surface.ROTATION_270: degrees = 270; break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
}
else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
mCamera.setDisplayOrientation(result);
}
public void onPause() {
if (mCamera == null) return;
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
[/UPDATE2]
Another thing I found out was that when I paused the activity, the unlock was seldom executed. Though when it was executed camera still didn't come back, but this behavior seemed wierd to me because the thread.join() was executed so I assume the finally block should have been executed as well.
Sorry I couldn't desribe my question with less words, but please leave any clue you have. Thanks in advance!
I still don't know exactly what was wrong with my program. But after researching, I found it seemed to be something tricky with SurfaceView's z order not following normal rules. So instead of using two SurfaceViews, one for camera and one for drawing, I switched to using one SurfaceView for both and it works fine now.
But according to my experiments, with either implementation, drawing on screen with camera preview on is anyways a slow implementation because of all the switching between threads. So one should probably try to avoid doing so in the design...
I'm displaying a camera preview to an user using surface view. Here's my method for starting it:
private void pickCam(){
if (Camera.getNumberOfCameras() < 1)
return;
if (Camera.getNumberOfCameras() == 1)
{
CameraInfo cInfo=new CameraInfo();
Camera.getCameraInfo(0, cInfo);
camera = Camera.open(0);
}
else
camera = Camera.open(currentCamera);
Camera.Parameters parameters = camera.getParameters();
parameters = camera.getParameters();
parameters.setRotation(90);
try {
Camera.Size mCameraSize = null;
for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
if (size.width <= surfaceWidth && size.height <= surfaceHeight) {
if (mCameraSize == null)
mCameraSize = size;
else {
int currentArea = mCameraSize.width
* mCameraSize.height;
int newArea = size.width * size.height;
if (newArea > currentArea) {
mCameraSize = size;
}
}
}
}
parameters.setPreviewSize(mCameraSize.width, mCameraSize.height);
camera.setParameters(parameters);
camera.setDisplayOrientation(90);
camera.setPreviewDisplay(surfaceHolder);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
camera.startPreview();
}
When user clicks a buton:
if (camera != null)
camera.takePicture(null, null,this);
This code works well on Sony Xperia(2 cameras), but preview fails on HTC Wildfire and Samsung Galaxy S+(both 1 cam). No exception beeing throw, it just displays nothing. I am able to take pictures on all of those devices.
LogCat output for pickCamMethod is empty for both devices:
04-23 14:25:22.987: I/System.out(5871): PickCamBegin
04-23 14:25:23.428: I/System.out(5871): PickCamEnd
How can i make it work in all cases?
Add permission's in the manifest file
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
The develoepr site has excellent documentation on the topic.
http://developer.android.com/guide/topics/media/camera.html
I used the code on the developer site and made a sample. Modify the below according to your needs.
In your activity
public class MainActivity extends Activity {
private static final int REQUEST_CODE = 1;
ImageView imageView;
Button b;
private Camera mCamera;
private CameraPreview mPreview;
private Bitmap bitmap;
private PictureCallback mPicture;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
boolean check =checkCameraHardware(MainActivity.this);
if(check)
{
mCamera = getCameraInstance();
// mCamera.setDisplayOrientation(90);
setCameraDisplayOrientation(this,
1, mCamera);//requires min sdk 9
}
// Create an instance of Camera
mPicture = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
File imagesFolder = new File(Environment.getExternalStorageDirectory(), "MyImages");
if(!imagesFolder.exists())
imagesFolder.mkdirs();
File pictureFile = new File(imagesFolder, "image.jpg");
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
System.out.println("hello");
fos.write(data);
fos.close();
} catch (FileNotFoundException e) {
Log.d("No File", "File not found: " + e.getMessage());
} catch (IOException e) {
//Log.d(TAG, "Error accessing file: " + e.getMessage());
}
}
};
// Create our Preview view and set it as the content of our activity.
mPreview = new CameraPreview(this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mPreview);
b = (Button) findViewById(R.id.button_capture);
b.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
mCamera.takePicture(null, null, mPicture);
Toast.makeText(MainActivity.this, "Called",1000).show();
}
});
}
public static void setCameraDisplayOrientation(Activity activity,
int cameraId, android.hardware.Camera camera) {
android.hardware.Camera.CameraInfo info =
new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay()
.getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0: degrees = 0; break;
case Surface.ROTATION_90: degrees = 90; break;
case Surface.ROTATION_180: degrees = 180; break;
case Surface.ROTATION_270: degrees = 270; break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
}
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
// this device has a camera
Toast.makeText(this, "Phone has camera", Toast.LENGTH_LONG).show();
return true;
} else {
// no camera on this device
Toast.makeText(this, "Phone has no camera", Toast.LENGTH_LONG).show();
return false;
}
}
public static Camera getCameraInstance(){
Camera c = null;
try {
c = Camera.open(); // attempt to get a Camera instance
}
catch (Exception e){
// Camera is not available (in use or does not exist)
}
return c; // returns null if camera is unavailable
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
mCamera.release();
}
}
CameraPreview class for preview
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
// 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) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.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
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<FrameLayout
android:id="#+id/camera_preview"
android:layout_width="fill_parent"
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>
Those devices HTC Wildfire and Samsung Galaxy S+ as you mentioned, might run on a lower version of Android which doesn't support Camera.getNumberOfCameras().
HTC Wildfire is announced in 2010, supports up to Android 2.2 (API Lv.8)
Samsung Galaxy S+, supports up to Android 2.3 (API Lv.9/Lv.10)
A Guide of API levels can be found here: Android Developers: What is API Level?
Therefore, the method you're using Camera.getNumberOfCameras() is added in API 9 (see the Documentation)
You might need to use Build.VERSION to handle them separately, so could you try to change your first lines of pickCam() like this:
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
// Means the device is using API Lv.9+
// Use the method that can work for Lv.9+
if (Camera.getNumberOfCameras() < 1)
return;
if (Camera.getNumberOfCameras() == 1)
{
CameraInfo cInfo=new CameraInfo();
Camera.getCameraInfo(0, cInfo);
camera = Camera.open(0);
}
else
camera = Camera.open(currentCamera);
} else {
// Use alternative method that support lower API levels
camera = Camera.open();
}
Camera.open() is the method that works on 2.2, and Camera.open(int) is for 2.3+ (see Documentation)
Please let me know if this works for you.