I have a problem with camera. I want to get picture in onPreviewFrame but it's never called. I opened a camera, set preview Display and preview Callback but nothing. I just want to understand where I was wrong.
public class VideoCall extends Activity implements View.OnClickListener, Callback, PreviewCallback
{
TabHost thVideoChat;
Button btnVideoUp, btnVideoDown;
Handler uiHandler;
SurfaceView videoPrev;
SurfaceHolder surfaceHolder;
Camera camera;
Timer timer;
boolean getPic;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.a_video);
initialize();
Log.d("RAYZ", "onCreate");
}
private void initialize()
{
thVideoChat = (TabHost) findViewById(R.id.thVideoChat);
thVideoChat.setup();
TabSpec specs = thVideoChat.newTabSpec("1");
specs.setContent(R.id.tabVideo);
specs.setIndicator("Видео", getResources().getDrawable(R.drawable.mcam));
thVideoChat.addTab(specs);
specs = thVideoChat.newTabSpec("2");
specs.setContent(R.id.tabChat);
specs.setIndicator("Чат", getResources().getDrawable(R.drawable.mchat));
thVideoChat.addTab(specs);
btnVideoUp = (Button) findViewById(R.id.btnVideoUp);
btnVideoDown = (Button) findViewById(R.id.btnVideoDown);
btnVideoUp.setOnClickListener(this);
btnVideoDown.setOnClickListener(this);
videoPrev = (SurfaceView) findViewById(R.id.videoPrev);
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
{
LayoutParams lp = videoPrev.getLayoutParams();
lp.height = 320;
lp.width = 240;
videoPrev.setLayoutParams(lp);
}
else
{
LayoutParams lp = videoPrev.getLayoutParams();
lp.height = 240;
lp.width = 320;
videoPrev.setLayoutParams(lp);
}
surfaceHolder = videoPrev.getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
uiHandler = new Handler();
getPic = false;
}
@Override
protected void onPause()
{
Log.d("RAYZ", "onPause");
if (camera != null)
{
camera.setPreviewCallback(null);
camera.stopPreview();
camera.release();
camera = null;
}
if (timer != null)
{
timer.cancel();
}
super.onPause();
}
@Override
protected void onResume()
{
super.onResume();
camera = Camera.open();
}
@Override
public void onClick(View v)
{
switch (v.getId())
{
case R.id.btnVideoUp:
{
btnVideoUp.setEnabled(false);
btnVideoDown.setEnabled(true);
timer = new Timer();
Log.d("RAYZ", "G_BTN");
timer.schedule(new TimerTask()
{
@Override
public void run()
{
uiHandler.post(new Runnable()
{
@Override
public void run()
{
getPic = true;
}
});
}
}, 0L, 1L * 500L);
break;
}
case R.id.btnVideoDown:
{
btnVideoUp.setEnabled(true);
btnVideoDown.setEnabled(false);
Log.d("RAYZ", "R_BTN");
timer.cancel();
timer = null;
break;
}
default:
break;
}
}
@Override
public void onPreviewFrame(byte[] data, Camera camera)
{
Log.d("RAYZ", "getPic");
// if (getPic)
// {
// }
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
}
@Override
public void surfaceCreated(SurfaceHolder holder)
{
try
{
camera.setPreviewDisplay(holder);
camera.setPreviewCallback(this);
camera.startPreview();
}
catch (IOException e)
{
e.printStackTrace();
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder)
{
}
}
Tried this code on 2 other devices (phones HTS and Sony Xperia) and everything worked fine. But on my tablet it does not work. I'm confused.
I have been struggling with this problem for sometime and the solution for me was to call Camera.setPreviewCallback just after Camera.setPreviewDisplay in SurfaceView.surfaceChanged:
public void onCreate(Bundle state) { log("onCreate");
try {
super.onCreate(state);
setContentView(R.layout.main);
text = (TextView) findViewById(R.id.text);
surface = (SurfaceView) findViewById(R.id.surface);
int type = SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS;
surface.getHolder().setType(type);//REQUIRED:API10
surface.getHolder().addCallback(this);
camera = Camera.open();
camera.setDisplayOrientation(90); // portrait mode only
} catch (Exception e) {
showException(e);
}
}
public void surfaceChanged(SurfaceHolder sh, int format, int w, int h) { log("surfaceChanged");
try {
camera.stopPreview();
Size s = camera.getParameters().getPreviewSize();
LayoutParams params = surface.getLayoutParams();
params.height = w*s.width/s.height; // portrait mode only
surface.setLayoutParams(params);
camera.setPreviewDisplay(sh);
camera.setPreviewCallback(this);
camera.startPreview();
} catch (Exception ex) {
showException(ex);
}
}
Reading numerous article I realized the setPreviewCallback may fail if the camera does not have a fully initialized SurfaceHolder.
I hope this helps ...
I'd recommend moving the code in surfaceCreated into surfaceChanged, especially since you're overriding the preview surface's layout during onCreate (in initialize()).
In general, it's safer to just respond to surfaceChanged, since it'll get called every time there's a size change to the surface. You should probably check whether you already have preview running in surfaceChanged.
As others have said, the size of the preview surface doesn't determine the size of the preview buffers you receive in the preview callback; that's set by the setPreviewSize method, and you have to use a value from the list of supported preview sizes that you get from
Camera.Parameters.getSupportedPreviewSizes().
You must to call setPreviewCallback in surfaceChanged method, not only in surfaceCreated. This is my main CameraActivity.java:
package com.example.cameraview;
import java.util.Hashtable;
import android.app.Activity;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
public class CameraActivity extends Activity implements Camera.PreviewCallback {
private Camera mCamera;
private CameraPreview mPreview;
private Result result;
private MultiFormatReader reader;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
reader = new MultiFormatReader();
Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>();
hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
reader.setHints(hints);
// Create an instance of Camera
mCamera = getCameraInstance();
// 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);
}
public void onPause() {
super.onPause();
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mPreview.getHolder().removeCallback(mPreview);
mCamera.release();
}
}
/** A safe way to get an instance of the Camera object. */
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
}
public void onPreviewFrame(byte[] data, Camera camera) {
Size size = mCamera.getParameters().getPreviewSize();
PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(data, size.width, size.height, 0, 0, size.width, size.height, false);
HybridBinarizer hybBin = new HybridBinarizer(source);
BinaryBitmap bitmap = new BinaryBitmap(hybBin);
ImageView myImage = (ImageView) findViewById(R.id.foto);
try {
result = reader.decode(bitmap);
Log.d("Result", "Result found!: " + String.valueOf(result));
myImage.setVisibility(View.VISIBLE);
if (String.valueOf(result).contentEquals("1"))
myImage.setImageResource(R.drawable.juan);
else if (String.valueOf(result).contentEquals("2"))
myImage.setImageResource(R.drawable.antonio);
} catch (NotFoundException e1) {
if (myImage != null)
myImage.setVisibility(View.INVISIBLE);
Log.d("NotFoundException", "NotFoundException");
} finally {
reader.reset();
}
}
}
And this is my CameraPreview.java:
package com.example.cameraview;
import java.io.IOException;
import java.util.List;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import android.hardware.Camera.Size;
import android.hardware.Camera.Parameters;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
/** A basic Camera preview class */
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
private String TAG = "CameraPreview";
private Context context;
@SuppressWarnings("deprecation")
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
this.context = context;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
// deprecated setting, but required on Android versions prior to 3.0
mHolder.addCallback(this);
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 (NullPointerException e) {
Log.d(TAG, "Error setting camera preview - nullpointerexception: " + e.getMessage());
} 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 {
Parameters parameters = mCamera.getParameters();
List<Size> sizes = parameters.getSupportedPreviewSizes();
Size optimalSize = getOptimalPreviewSize(sizes, w, h);
parameters.setPreviewSize(optimalSize.width, optimalSize.height);
if (context.getPackageManager().hasSystemFeature("android.hardware.camera.autofocus"))
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
mCamera.setParameters(parameters);
mCamera.setPreviewCallback((PreviewCallback) context);
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.05;
double targetRatio = (double) w / h;
if (sizes == null) return null;
Size optimalSize = 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) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
}
Ignore reader and zxing things, it's a proof of concept for show layouts over qr detection in ZXing library.
This is a mixed solution found while searching for my code errors in StackOverflow.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With