【问题标题】:Android onPreviewFrame is not called未调用 Android onPreviewFrame
【发布时间】:2013-01-19 08:42:05
【问题描述】:

我的相机有问题。我想在 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 和 Sony Xperia)上尝试了此代码,一切正常。但在我的平板电脑上它不起作用。我很困惑。

【问题讨论】:

  • 我也面临同样的问题。某些 android 设备 onPreviewFrame() 被调用,但在某些设备上它没有被调用。你是如何解决这个问题的,你能指导我吗?谢谢

标签: android camera


【解决方案1】:

我一直在努力解决这个问题,我的解决方案是在 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 可能会失败。

我希望这会有所帮助...

【讨论】:

    【解决方案2】:

    我建议将surfaceCreated 中的代码移动到surfaceChanged 中,特别是因为您在onCreate 期间(在initialize() 中)覆盖了预览图面的布局。

    一般来说,只响应 surfaceChanged 会更安全,因为每次表面大小发生变化时都会调用它。您可能应该检查是否已经在 surfaceChanged 中运行了预览。

    正如其他人所说,预览表面的大小并不能决定您在预览回调中收到的预览缓冲区的大小;这是由 setPreviewSize 方法设置的,您必须使用从支持的预览尺寸列表中获取的值

      Camera.Parameters.getSupportedPreviewSizes().
    

    【讨论】:

      【解决方案3】:

      您必须在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 中搜索我的代码错误时发现的混合解决方案。

      【讨论】:

      • 我的预览自创建以来从未更改过。
      • 查看@Eddy 关于推荐进入surfaceChanged 的​​答案。我将编辑我的答案,显示我所有的工作代码。
      • @Neonigma 能否请您帮助我如何在多个框架布局中显示实时预览......我无法在第二个框架布局中显示我的实时相机预览
      【解决方案4】:

      您可能需要为低功耗设备设置较低的预览分辨率。将surfaceview大小设置为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);
      

      【讨论】:

        【解决方案5】:

        对我而言,某些设备(例如 lg Rebel 3)从不调用 onPreviewFrame() 而其他设备(任何三星)。问题原来是我使用的是setPreviewCallbackWithBuffer() 方法而不是setPreviewCallback()。我试图提高记忆效率。一旦我改回setPreviewCallback(),一切都在嗡嗡作响。

        【讨论】:

          猜你喜欢
          • 2011-10-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-01-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-06-02
          相关资源
          最近更新 更多