I'm developing an android application which uses the camera. I got a problem with surfaceChanged() method. Here is my code.
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Log.e(TAG, "surfaceChanged");
if (mPreviewRunning) {
mCamera.stopPreview();
}
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(w, h);
mCamera.setParameters(parameters);
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
mCamera.startPreview();
mPreviewRunning = true;
}
the variable mPreviewRunning is initialized as false at the beginning. program runs just fine with horizontal orientation. but when I rotates the phone to the vertical orientation the screen is rotated and is stretched. I can't understand why it's happened. Please help me to solve this problem.
Camera does not change orientation at all - CCD is soldered to motherboard , and delivers pixels as if it were in landscape mode ( most probably ) however, your activity could be restarted by OS on orientation change, unless you disable it in manifest.
( and your surface view is recreated on this restart )
Look in this project for android demos handling camera management:
https://sourceforge.net/p/javaocr/source/ci/5cb9b4176f40ada57296cce79addd205e4c1405c/tree/demos/camera-utils/src/main/java/net/sf/javaocr/demos/android/utils/camera/CameraManager.java#l85
Your mistake is to set preview size from surface size - do not do this. Camera provides limited set of acceptable preview sizes and is free to ignore other settings ( exact behaviour is device depending)
Preview size means used CCD resolution, and camera software will render it on your surface view in size of the surface view doung scaling as necessary.
Related
I have some troubles with displaying camera viewfinder on Galaxy S2 (android 2.3) and LG P500 (android 2.3). I have some noise (interference) on SurfaceView. Nevertheless, result photo hasn't such defects.
Other devices (include devices with 2.3 and Galaxy S2 with android 4) working fine.
Unfortunatly, I haven't such devices, so I can't test it after every changing in source code. Maybe someone already had this problem?
Also, I tried to test this app using Samsung Remote Test Lab, but, unfortunatly, Galaxy S2 with android 2.3 was placed on the table and his camera takes blackness (splash do nothing). In this case there are no any white strips.
onCreate():
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mSurfaceHolder.addCallback(this);
...
mSurfaceView.setLayoutParams(lp);
onResume():
mCamera = Camera.open();
if (mCamera == null) {
Toast.makeText(this, R.string.t_no_camera, Toast.LENGTH_SHORT).show();
finish();
} else {
mCamera.setDisplayOrientation(90);
Parameters p = mCamera.getParameters();
p.setFocusMode(Parameters.FOCUS_MODE_AUTO);
prepareFlash();
if (prepareResolution()) {
Camera.Size size = getBestPreviewSize(mDisplay.getWidth(), mDisplay.getHeight(), p);
if (size != null) {
p.setPreviewSize(size.width, size.height);
}
}
mCamera.setParameters(p);
try {
mCamera.setPreviewDisplay(mHolder);
} catch (IOException e) {
e.printStackTrace();
}
mCamera.startPreview();
}
surfaceCreated(SurfaceHolder holder):
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
Any idea? What could it be? I will be grateful for any hint.
You start camera preview in Portrait mode; bugs with such preview occur on different devices. Sometimes, these bugs get fixed with software upgrade from the manufacturer, sometimes, they are not. Sometimes, crazy workarounds can resolve the problem.
To be on the safe side, stick with Landscape mode. Maybe your app can keep a black list of devices/build versions where the preview activity will not allow vertical (portrait) orientation.
Note that you can usually simulate portrait orientation by using rotated icons, bitmaps, and even text labels.
I am trying to get a camera preview with a color effect applied to it, such as for example the NEGATIVE effect. There are no errors, and the preview is visible without problems, but independent of the ColorEffect I set - the camera preview remains unchanged. I tested if the effects I am trying to use are available to my phone by running params.getSupportedColorEffects() (also these effects also work in the built in photo app).
I have no idea what is wrong with the code - I am posting it below. Perhaps someone here has an idea what could make this work? Thanks in advance.
public class CustomCameraView extends SurfaceView{
Camera mCamera;
SurfaceHolder mHolder;
public CustomCameraView(Context context){
super(context);
mHolder = this.getHolder();
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mHolder.addCallback(mSurfaceHolderListener);
}
SurfaceHolder.Callback mSurfaceHolderListener = new SurfaceHolder.Callback() {
public void surfaceCreated(SurfaceHolder holder) {
mCamera=Camera.open();
try {
mCamera.setPreviewDisplay(mHolder);
}
catch (Exception e){ }
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height)
{
Camera.Parameters params = mCamera.getParameters();
params.setColorEffect(Camera.Parameters.EFFECT_NEGATIVE);
mCamera.setParameters(params);
mCamera.startPreview();
}
public void surfaceDestroyed(SurfaceHolder arg0)
{
mCamera.stopPreview();
mCamera.release();
}
};
}
After some testing it turned out the problem could be related to the HTC Desire I was testing on (or maybe its OS version). The code works correctly on some other Samsung phones. I haven't figured out what could be the problem on the HTC.
UPDATE:
I have managed to get the effects working, but truly by accident, and I still don't understand why. But I will give the answer here - perhaps someone will find it useful, or maybe will be able to explain why it happens this way:
I added the following line to the surfaceChanged method because I was trying to decrease the size of the preview:
previewHolder.setFixedSize(width, height-1);
This had the result of making the selected effect visible.
When I changed this line to:
previewHolder.setFixedSize(width, height);
the effect was not visible any more once again. So odd.... it works for set height being anything less than the received height parameter.
I have been struggling with this as well. I found out that the HTC Desire its camera needs a strange order of executing the setParameters, setPreviewDisplay and startPreview for the color effect to work. The order is:
Camera.Parameters parameters = camera.getParameters();
//set the parameters
camera.setParameters(parameters);
camera.startPreview();
camera.setParameters(parameters);
camera.setPreviewDisplay(surfaceHolder);
Calling startPreview before setPreviewDisplay is documented in the Android SDK as a way of initializing the camera and the surfaceView in parallel.
Regarding your update about getting the effects to work by accident, the same happend to me! I assume for the same reason, some of my code got called twice in quick succesion (in my case due to a changing database object). This caused the method to (re)set the parameters and (re)start the preview to be called twice producing the desired result. After realising this and some more experimenting the above order seemed to work on both my HTC Desire and Acer Iconia A500 and I was quite happy with it.
However I have just received a comment for my application saying it produces corrupted images on the HTC Desire HD so I would recommend not using this order of camera initialization as a default but rather as a fix for the HTC Desire.
After setting new parameters to camera and starting preview invalidate() are calling on your SurfaceView . But it only Invalidate the whole view. If the view is visible, onDraw(android.graphics.Canvas) will be called at some point in the future. So there is no guarantees that onDraw() will be called immediately. But onDraw() are always invoking after calling onMeasure() with size differs from current. So it can be a reason of this odd behavior.
Simple answers use following type :
Camera camera = null;
camera = Camera.open();
if (camera != null) {
try {
Camera.Parameters parameters = camera.getParameters();
// Set all kind of stuffs here..
parameters.setSceneMode(Camera.Parameters.FLASH_MODE_AUTO);
parameters.setColorEffect(Camera.Parameters.EFFECT_SEPIA); // whatever effect you want
camera.setParameters(parameters);
camera.setPreviewDisplay(surface_holder);
camera.startPreview();
} catch (IOException exception) {
camera.release();
camera = null;
}
}
So many LED flashlight API questions for Android. I'm afraid to ask yet another, but here goes..
Using the tried and true FLASH_MODE_TORCH I am able to achieve satisfaction with my Samsung Galaxy SII and get the LED flash turned on. On my friend's Galaxy Nexus, no such luck. Nor on my other friend's Droid X.
I'm noticing for a not insignificant number of devices specific native IOCTL calls seem to be required. Is this the case for the Galaxy Nexus? How do I find a reference to program it?
I am doing the standard FLASH_MODE_TORCH/"flash-mode"="torch", startPreview() chain.
Kind of disappointing that this seemingly standard API doesn't appear to be so universal after all.
What I found out is that some devices need a SurfaceView to turn on the LED.
SurfaceView preview = (SurfaceView) findViewById(R.id.PREVIEW);
SurfaceHolder mHolder = preview.getHolder();
mHolder.addCallback(this);
Camera mCamera = Camera.open();
mCamera.setPreviewDisplay(mHolder);
// Turn on LED
Parameters params = mCamera.getParameters();
params.setFlashMode(Parameters.FLASH_MODE_TORCH);
mCamera.setParameters(params);
mCamera.startPreview();
...
// Turn off LED
Parameters params = mCamera.getParameters();
params.setFlashMode(Parameters.FLASH_MODE_OFF);
mCamera.setParameters(params);
mCamera.stopPreview();
mCamera.release();
Your activity needs to implement SurfaceHolder.Callback:
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {}
public void surfaceCreated(SurfaceHolder holder) {
mHolder = holder;
mCamera.setPreviewDisplay(mHolder);
}
public void surfaceDestroyed(SurfaceHolder holder) {
mCamera.stopPreview();
mHolder = null;
}
The surfaceview has to be visible, android:visibility="invisible" or a height and width of 0 won't work. Unfortunately I didn't find a good solution to hide it, so I
just gave it a size of 1x1dip and positioned it underneath a button..
**(To expand on above paragraph [Don't have enough rep to reply, but felt it was useful]) Somewhere in the XML of your current Content View, you want:
<SurfaceView
android:id="#+id/PREVIEW"
android:layout_width="1dip"
android:layout_height="1dip"/>
If you have it inside a RelativeLayout (preferred), you can also do alignParentLeft/Bottom to tuck it in a corner. This method works for my Galaxy Nexus, but it's a known problem (phone-side) for Droid X (with the .621 update, anyway), and doesn't work on mine. Great answer, timosch!
I'm running into an issue with camera input in Android. After searching over the internet for half a day I'm not getting any further.
Wat I want to achieve at this stage is correctly displaying the current camera input. Nothing more and nothing less for now. When holding the device in landscape mode, all seems fine. However as soon as I switch the device to portrait the display seems to be horizontally scaled in order to match the width of the screen on the device.
In order to clarify this issue a bit more, I've attached two photographs:
Landscape (looks correct)
Portrait (looks incorrect)
It seems like this issue also occurs with the camera example from the API examples, but it doesn't seem to occur when I use the camera application from my device. So clearly something else is happening there.
The code that I'm using is based on examples that can be found over the internet and has no modifications:
class Preview extends SurfaceView implements SurfaceHolder.Callback {
SurfaceHolder mHolder;
Camera mCamera;
Preview(Context context) {
super(context);
mHolder = getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
mCamera = Camera.open();
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
mCamera.stopPreview();
mCamera = null;
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setPreviewSize(w, h);
mCamera.setParameters(parameters);
mCamera.startPreview();
}
}
The API examples make use of a method "getOptimalPreviewSize(...)" which sounds like the method I need, but that does not change anything. I left it out here in order to be as brief as possible.
Am I missing something obvious or is this the expected behavior?
Hopefully someone can point me in the correct direction.
Thanks in advance!
Paul
Okay, I just found the answer to my own question.
The way I solved it was by going fullscreen with my application:
requestWindowFeature(Window.FEATURE_NO_TITLE);
This makes a little bit of sense, as I guess we can not use just any resolution as the preview size on the camera. I guess it will internally snap to the closest allowed resolution/aspect ratio and it doesn't do any smart things like cropping the image to match the requested resolution while maintaining aspect ratio. In my case I was displaying a title bar while the source image was fullscreen and then downsized to fit on the screen.
I think it is kind of stupid that the aspect ratio is not maintained and images are not cropped by default,
I'm trying to develop an app which uses the Camera. So far it's been working well, except that I'm unable to force the orientation to be "portrait". It seems to work well if I force all activities to "landscape", because the camera preview seems to fit in landscape.
Is there anyway to use the Camera in portrait mode?
Android devices v2.2 and above contain and API to rotate the display to portrait. Devices below 2.2 are landscape only. Your best bet is to detect if the device is 2.2 and rotate 90 degrees. Fall back on landscape for devices under 2.2. The good news is most Android devices are on 2.2 and above.
Check out my answer here for more info:
Camera is wrong unless keyboard is open
public void surfaceCreated(SurfaceHolder holder)
{
// The Surface has been created, acquire the camera and tell it where to draw.
mCamera = Camera.open();
Parameters params = mCamera.getParameters();
if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE)
{
params.set("orientation", "portrait");
mCamera.setDisplayOrientation(90);
}
try
{
mCamera.setPreviewDisplay(holder);
}
catch (IOException exception)
{
mCamera.release();
mCamera = null;
}
}
edit: I was in the midst of Adobe AIR for Android development when I answered this question, and looking back at it, I realize this question didn't pertain to Adobe AIR.
Adobe says:
On devices that can change the screen orientation, such as mobile phones, a Video object attached to the camera will only show upright video in a landscape-aspect orientation. Thus, mobile apps should use a landscape orientation when displaying video and should not auto-rotate.
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/Camera.html
If you do really want to use the camera in portrait mode, my suggestion is to rotate the video object.
Here's some sample code that rotates the video object (_video) by an angle in degrees (source was pulled from elsewhere on stackoverflow):
var matrix:Matrix = _video.transform.matrix;
var rect:Rectangle = _video.getBounds(this);
matrix.translate(- (rect.left + (rect.width/2)), - (rect.top + (rect.height/2)));
matrix.rotate((angle/180)*Math.PI);
matrix.translate(rect.left + (rect.width/2), rect.top + (rect.height/2));
_video.transform.matrix = matrix;