I'm currently trying to build an android application to take pictures and I need to freeze the camera preview on a given event (i.e. picture taken) and restart it only after another event.
What I want, in other words, is for the view to display whatever the camera sees until the freeze event occurs and then to freeze the image (i.e. display whatever was on screen at the time of this event -- as if a picture was taken) until the unfreeze event occurs.
Now, I'm currently using a SurfaceView with a SurfaceHolder.Callback to do this and I tried to use a PreviewCallback to freeze the screen, but unfortunately, I can't find an example or a tutorial and I'm really stuck at this point.
If anyone has a guide or some pointers on how to get this done, I would really appreciate the help...
I'm pasting the relevant parts of my code below:
public class CustomCameraView extends SurfaceView {
Camera camera;
SurfaceHolder previewHolder;
//Callback for the surfaceholder
SurfaceHolder.Callback surfaceHolderListener = new SurfaceHolder.Callback() {
public void surfaceCreated(SurfaceHolder holder) {
camera=Camera.open();
try
{
camera.setPreviewDisplay(previewHolder);
}
catch (Throwable t) {
}
}
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int w, int h)
{
Parameters params = camera.getParameters();
params.setPictureFormat(PixelFormat.JPEG);
camera.setParameters(params);
camera.startPreview();
}
public void surfaceDestroyed(SurfaceHolder arg0)
{
camera.stopPreview();
camera.release();
}
};
public CustomCameraView(Context ctx)
{
super(ctx);
previewHolder = this.getHolder();
previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
previewHolder.addCallback(surfaceHolderListener);
setBackgroundColor(Color.TRANSPARENT);
}
public CustomCameraView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
protected void onDraw (Canvas canvas)
{
}
public void closeCamera()
{
if(camera != null)
camera.release();
}
public void dispatchDraw(Canvas c)
{
super.dispatchDraw(c);
}
}
Thank you very much for your help!
-Billy
Old question, I know, but answering for posterity. You should be able to simply call
camera.stopPreview();
The preview will freeze on whatever you're looking at until you call startPreview() again.
Related
My subclass of SurfaceView implements Camera.PreviewCallback & SurfaceHolder.Callback.
private SurfaceHolder mHolder;
private Camera mCamera;
private final FPSCounter fpscounter = new FPSCounter();
public MySurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
mHolder = getHolder();
mHolder.addCallback(this);
}
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
fpscounter.logFrame();
Log.d("fps", String.valueOf(fpscounter.getLastFrameCount()));
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
synchronized (this) {
mCamera.stopPreview();
Camera.Parameters parameters = mCamera.getParameters();
parameters.setRecordingHint(true);
parameters.setPreviewFormat(ImageFormat.NV21);
mCamera.setParameters(parameters);
try {
mCamera.setPreviewDisplay(holder);
mCamera.setPreviewCallback(this);
mCamera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
synchronized (this) {
setWillNotDraw(false);
mCamera = Camera.open();
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
synchronized (this) {
try {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
}
} catch (Exception e) {
Log.e("cam error", e.getMessage());
}
}
}
and the FPSCounter class
private long startTime;
private int frames, lastFrameCount;
public void logFrame() {
frames++;
if (System.nanoTime() - startTime >= 1000000000) {
lastFrameCount = frames;
frames = 0;
startTime = System.nanoTime();
}
}
public int getLastFrameCount() {
return lastFrameCount;
}
Even though the camera preview is extremely smooth, the onPreviewFrame() method is only called about 5 times a second. Why isn't it being called for every frame?
You probably figured it out already: Camera.setPreviewCallback() puts too much pressure on Garbage Collector. You can use Camera.setPreviewCallbackWithBuffer() instead.
Second, if onPreviewFrame() arrives on the main (UI) thread, then it competes for single CPU time with UI events like touch, layout, or even rendering. To keep onPreviewFrame() on a separate thread, you should open() the camera on a secondary Looper thread, see e.g. https://stackoverflow.com/a/19154438/192373.
Third, even in this case, the preview callbacks are serialized. If fpscounter.logFrame() and Log().d take X milliseconds, then the FPS will not exceed 1000/X.
It is called for every frame. You can refer to online reference of Camera. Look at this sentence "Installs a callback to be invoked for every preview frame in addition to displaying them on the screen."
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();
}
I want add a take picture function in lockscreen , and I need to open the preview on screen , but it's fail all the time , logcat say the Error is about "setPreviewWindow - Null ANativeWindow passed to setPreviewWindow" and "startPreview - Preview not started. Preview in progress flag set"
In keyguard_screen_tab_unlock.xml after the digital clock view, put
<FrameLayout
android:id="#+id/mPreview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
In LockScreen.java constructor:
FrameLayout mFrameLayout = (FrameLayout)findViewById(R.id.mPreview);
SurfacePreview preview = new SurfacePreview(mContext);
preview.setZOrderOnTop(true);
mFrameLayout.addView(preview);
SurfacePreview class :
public class SurfacePreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mSurfaceHolder;
SurfacePreview(Context context) {
super(context);
mSurfaceHolder = this.getHolder();
mSurfaceHolder.addCallback(this);
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
mCamera = Camera.open(CameraInfo.CAMERA_FACING_BACK);
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
mCamera.startPreview();
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Camera.Parameters parameters = mCamera.getParameters();
mCamera.setParameters(parameters);
mCamera.setDisplayOrientation(90);
}
public void surfaceDestroyed(SurfaceHolder holder) {
mCamera.release();
}
}
Anyone kown how to resolve this issue and start prview on LockScreen? Thanks a lot.
Hi
I am trying to use the camera to capture an image in one of my application. What is special is that I need a square preview area (and picture in the end). I tried defining the size of both picture and preview to 1:1 pixel ratios, but nothing seams to work. No matter what I does the picture looks "squashed" on a square.
Anyone who has any idea about how to resolve this?
code:
public class AddFromCameraActivity extends Activity implements SurfaceHolder.Callback {
private Camera mCamera;
private Parameters mParameters;
private SurfaceView mCameraPreview;
private SurfaceHolder mSurfaceHolder;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.addimagefromcameramain);
initialise();
//Testing area
mCamera = Camera.open();
mParameters = mCamera.getParameters();
mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
mParameters.setJpegQuality(50);
mParameters.setJpegThumbnailQuality(50);
mParameters.setPictureSize(1024, 1024);
//mParameters.setPreviewFormat(ImageFormat.JPEG);
mParameters.setJpegThumbnailSize(256, 256);
mParameters.setPreviewSize(500, 500);
mCamera.setParameters(mParameters);
}
private void initialise()
{
mCameraPreview = (SurfaceView)findViewById(R.id.cameraSurfaceView);
mSurfaceHolder = mCameraPreview.getHolder();
mSurfaceHolder.addCallback(this);
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
mSurfaceHolder.setFixedSize(500, 500);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(mSurfaceHolder);
mCamera.startPreview();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
mCamera.stopPreview();
mCamera.release();
}
#Override
public void onPause()
{
mCamera.release();
}
}
thanks
You can take a look at the CameraPreview sample code from the Android SDK. The getOptimalPreviewSize method shows how to deal with different camera sizes and the onLayout method shows how to layout the preview surface in the activity.
I'm trying to modify the SurfaceView I use for doing a camera preview in order to display an overlaying square. However, the onDraw method of the extended SurfaceView is never called.
Here is the source :
public class CameraPreviewView extends SurfaceView {
protected final Paint rectanglePaint = new Paint();
public CameraPreviewView(Context context, AttributeSet attrs) {
super(context, attrs);
rectanglePaint.setARGB(255, 200, 0, 0);
rectanglePaint.setStyle(Paint.Style.FILL);
rectanglePaint.setStrokeWidth(2);
}
#Override
protected void onDraw(Canvas canvas){
canvas.drawRect(new Rect(10,10,200,200), rectanglePaint);
Log.w(this.getClass().getName(), "On Draw Called");
}
}
public class CameraPreview extends Activity implements SurfaceHolder.Callback{
private SurfaceHolder holder;
private Camera camera;
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
// We remove the status bar, title bar and make the application fullscreen
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
// We set the content view to be the layout we made
setContentView(R.layout.camera_preview);
// We register the activity to handle the callbacks of the SurfaceView
CameraPreviewView surfaceView = (CameraPreviewView) findViewById(R.id.camera_surface);
holder = surfaceView.getHolder();
holder.addCallback(this);
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
Camera.Parameters params = camera.getParameters();
params.setPreviewSize(width, height);
camera.setParameters(params);
try {
camera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
camera.startPreview();
}
public void surfaceCreated(SurfaceHolder holder) {
camera = Camera.open();
}
public void surfaceDestroyed(SurfaceHolder holder) {
camera.stopPreview();
camera.release();
}
}
Found it on the android-developers Google group. You simply have to add :
setWillNotDraw(false)
To the constructor. Now if someone could explain me why, that would be greatly appreciated.
Minor correction:
Adding setWillNotDraw(false) to the constructor will cause crashes, because the underlying Surface object is not created yet.
Instead, put the setWillNotDraw(false) statement into the surfaceCreated() method. This delays the call until there's a real object to work with, and works properly.
(and many thanks to the users who posted this solution, it solved a major problem for me)
Romain Guy explained it:
For efficiency, layouts do not get their onDraw() method called. To
enable it, call setWillNotDrawEnabled(false) (or set the equivalent
XML attribute to false.)
Based on all above comments I used :
Add setWillNotDraw(false) in the onAttachedToWindow() event.
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
setWillNotDraw(false);
}
and it worked.