我的相机有问题。我想在onPreviewFrame中获取图片,但它从未被调用过。我打开相机,设置预览显示和预览回调但没有。我只是想了解我错在哪里。
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)
{
}
}
在其他2台设备(手机HTS和索尼Xperia)上尝试过此代码,一切正常。但在我的平板电脑上它不起作用。我很困惑。
答案 0 :(得分:17)
我一直在努力解决这个问题,我的解决方案就是在SurfaceView.surfaceChanged中的Camera.setPreviewDisplay之后调用Camera.setPreviewCallback:
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);
}
}
阅读大量文章我意识到如果相机没有完全初始化的SurfaceHolder,setPreviewCallback可能会失败。
我希望这会有所帮助......
答案 1 :(得分:4)
我建议将surfaceCreated中的代码移动到surfaceChanged中,特别是因为你在onCreate(在initialize()中)覆盖了预览表面的布局。
一般来说,只响应surfaceChanged会更安全,因为每次表面尺寸发生变化时都会调用它。您应该检查是否已在surfaceChanged中运行预览。
正如其他人所说,预览表面的大小不会决定您在预览回调中收到的预览缓冲区的大小;这是由setPreviewSize方法设置的,你必须使用你从中获得的支持预览尺寸列表中的值
Camera.Parameters.getSupportedPreviewSizes().
答案 2 :(得分:2)
您必须在surfaceChanged方法中调用setPreviewCallback,而不仅仅是在surfaceCreated中。这是我的主要 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();
}
}
}
这是我的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;
}
}
忽略读者和zxing事物,它是ZXing库中qr检测的显示布局的概念证明。
这是在StackOverflow中搜索我的代码错误时发现的混合解决方案。
答案 3 :(得分:0)
您可能需要为低功耗设备设置较低的预览分辨率。将曲面视图大小设置为320x240对相机本身的预览分辨率没有影响,必须明确设置预览分辨率。您可以尝试这样的事情:
List<Camera.Size> resList = camera.getParameters().getSupportedPreviewSizes();
int w=0, h=0;
final int desiredRes_W = 176;
for ( Camera.Size size : resList ) {
// find a supported res nearest to desired_Res
if ( w==0 ) {
w = size.width;
h = size.height;
}
else if ( size.width >= desiredRes_W && size.width <= w ) {
w=size.width;
h = size.height;
}
} // 176x144, 320x240 ...
Parameters par = camera.getParameters();
par.setPreviewSize(w, h);
// ALSO set width/height of the SurfaceView to the same aspect ratio.
camera.setParameters(par);
camera.setPreviewDisplay(holder);
答案 4 :(得分:0)
对我来说,某些设备(例如lg Rebel 3)从未调用过onPreviewFrame()
,而其他设备则是(任何三星)。原来是我在使用setPreviewCallbackWithBuffer()
方法而不是setPreviewCallback()
方法。我试图提高记忆效率。当我改回setPreviewCallback()
后,一切都在嗡嗡作响。