I have a class that extends the SurfaceView and implements Camera.PreviewCallback. In this class I setup the camera to preview to supplied buffers (setPreviewCallbackWithBuffer) and a couple of buffers (addCallbackBuffer). After I call startPreview the onPreviewFrame callback is successfully called. In onPreviewFrame I hand the work over to a different thread that does some processing and eventually visualizes the data.
However, I noticed that occasionally the onPreviewFrame is not called anymore. During exploration of this issue, I observed that it is most likely to occur when processing 1280x720 frames - which the camera supports) but it also happens on lower resolutions but less frequently. I eventually stripped down the code down to an almost empty onPreviewFrame (it only logs receiving the call and calling addCallbackBuffer again; nothing more). The processing thread is not started. The same behavior can be observed.
In this case, with 3 buffers for the preview at 1280x720 this runs for about 20 minutes and then onPreviewFrame is not being called anymore. Logcat doesn't show any other issues. This 20 minutes varies, sometimes its 5 minutes, sometimes under a minute. Using logging I verified that the buffer sequence is rather clean (buffer1, buffer2, buffer3, buffer1, buffer2, ...) on each onPreviewFrame invocation.
The device I'm working on is a Galaxy Tab 2 (using Android 4.2.2 CyanogenMod); but I've seen this on other devices using stock roms also - so I doubt that it is due to the rom.
So I guess it comes to these questions:
- how many buffers do I need to supply (given a certain resolution and a certain processing time)?
- Why does the onPreviewFrame not get called anymore (the are free buffer at that time)?
The relevant code boils down to this:
private void startPreview()
{
Camera.Parameters parameters = mCamera.getParameters();
int width = 1280;
int height = 720;
Resolution bestCameraResolution = getBestCameraResolutionMatch(width, height);
width = bestCameraResolution.width();
height = bestCameraResolution.height();
parameters.setPreviewSize(width, height);
mCamera.setParameters(parameters);
try {
mCamera.setPreviewDisplay(mSurfaceHolder);
} catch (IOException e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
// calculate imageBufferSize here...
mCamera.addCallbackBuffer(new byte[imageBufferSize]);
mCamera.addCallbackBuffer(new byte[imageBufferSize]);
mCamera.addCallbackBuffer(new byte[imageBufferSize]);
mCamera.setPreviewCallbackWithBuffer(this);
mCamera.startPreview();
Log.d(TAG, "Start preview with " + width + "x" + height);
}
@Override
public void onPreviewFrame(byte[] frameData, Camera camera)
{
// Log to a separate tag to allow better filtering
Log.d(TAG + "Frame", "preview frame on buffer " + frameData.toString());
// Give used buffer back for future grabbing
if (mCamera != null) {
mCamera.addCallbackBuffer(frameData);
}
}