【问题标题】:onPreviewFrame not being called for every frame that is displayed on SurfaceView没有为 SurfaceView 上显示的每一帧调用 onPreviewFrame
【发布时间】:2013-09-17 20:25:56
【问题描述】:

我的SurfaceView 子类实现了Camera.PreviewCallbackSurfaceHolder.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());
        }
    }
}

FPSCounter

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;
}

即使相机预览非常流畅,onPreviewFrame() 方法每秒也只会调用大约 5 次。为什么不是每帧都调用它?

【问题讨论】:

  • 我不确定,但我相信这与缓冲区以及 Android 如何处理它有关。这是指向源代码grepcode.com/file/repository.grepcode.com/java/ext/… 的链接,您也可以查看 EventHandler 类。
  • @Raykud 快速浏览了一下类后,我仍然不明白为什么它不会每次都调用该函数。

标签: android android-camera surfaceview


【解决方案1】:

您可能已经想通了:Camera.setPreviewCallback() 对垃圾收集器施加了太大的压力。您可以改用 Camera.setPreviewCallbackWithBuffer()。

其次,如果 onPreviewFrame() 到达主 (UI) 线程,那么它会与触摸、布局甚至渲染等 UI 事件竞争单个 CPU 时间。要将 onPreviewFrame() 保留在单独的线程上,您应该在辅助 Looper 线程上 open() 相机,参见例如https://stackoverflow.com/a/19154438/192373.

第三,即使在这种情况下,预览回调也是序列化的。如果fpscounter.logFrame()Log().d花费X毫秒,那么FPS不会超过1000/X。

【讨论】:

    【解决方案2】:

    每帧都会调用它。可以参考Camera的在线reference。看看这句话“除了在屏幕上显示它们之外,还为每个预览帧安装一个要调用的回调。”

    【讨论】:

    • 是的,我已经看到了,因此提出了这个问题。您能否在您的设备上快速运行代码以检查是否相同。
    • 为什么你认为不是每一帧都调用 onPreviewFrame()?
    • 因为即使预览非常流畅,我每秒也只能收到大约 4 次 onPreviewFrame() 调用。
    猜你喜欢
    • 2012-06-02
    • 1970-01-01
    • 1970-01-01
    • 2013-01-09
    • 2012-11-09
    • 2013-11-24
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    相关资源
    最近更新 更多