I test SDK v21 on a Samsung Galaxy A3 (2014), v23 on a Nexus 5X. I tested the Camera2Basic example, too, and it works on both devices. I wonder how my little code restructuring (I use an Activity instead of a Fragment) causes this error on only one of both devices:
// ...
protected void onCreate() {
// setContentView ...
viewfinder = (AutoFitTextureView)findViewById(R.id.viewfinder);
// Choose back camera device, choose maxPictureSize (for JPEG), find out whether dimensions must be swapped
if (!swappedDimensions)
viewfinder.setAspectRatio(maxPictureSize.getWidth(), maxPictureSize.getHeight());
else
viewfinder.setAspectRatio(maxPictureSize.getHeight(), maxPictureSize.getWidth());
// initialize image reader
}
protected void onResume() {
if (viewfinder.isAvailable()) {
// Alternative to call in onSurfaceTextureAvailable()
init();
}
else {
viewfinder.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
Log.d(tag(), "surfaceTexture available: " + width + " x " + height);
configureSurface(width, height);
init();
}
public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
Log.d(tag(), "surfaceTexture size changed: " + width + " x " + height);
// Currently, don't react to changes. In opposite to the surface change listener, this method is not called after onSurfaceTextureAvailable and only needs to be implemented if the texture view size will change during the app is running
}
public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
Log.d(tag(), "surfaceTexture destroyed");
return true;
}
public void onSurfaceTextureUpdated(SurfaceTexture texture) {
Log.d(tag(), "surfaceTexture updated");
}
});
}
try {
if (!openCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Time out waiting to lock camera opening.");
}
cameraManager.openCamera(deviceId, new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice camera) {
openCloseLock.release();
device = camera;
init();
}
// ...
}, null);
}
catch(InterruptedException e) {
// ...
}
}
protected void configureSurface(int view_width, int view_height) {
SurfaceTexture surfaceTexture = viewfinder.getSurfaceTexture();
if (surfaceTexture == null)
return;
// Can this be changed after the session has been started?:
Size rotatedSurfaceSize = swappedDimensions
? new Size(view_height, view_width)
: new Size(view_width, view_height);
Size previewSize = bestOutputSize(SurfaceTexture.class, rotatedSurfaceSize, Sizes.aspectRatio(maxPictureSize));
Log.i(tag(), "Preview size for " + rotatedSurfaceSize + " (" + Sizes.aspectRatio(maxPictureSize) + ":1): "+previewSize);
surfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
//
// Copied from Camera2Basic, just changed var names:
int rotation = getWindowManager().getDefaultDisplay().getRotation();
Matrix matrix = new Matrix();
RectF viewRect = new RectF(0, 0, view_width, view_height);
RectF bufferRect = new RectF(0, 0, previewSize.getHeight(), previewSize.getWidth());
float centerX = viewRect.centerX();
float centerY = viewRect.centerY();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
float scale = Math.max(
(float) view_height / previewSize.getHeight(),
(float) view_width / previewSize.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
} else if (Surface.ROTATION_180 == rotation) {
matrix.postRotate(180, centerX, centerY);
}
viewfinder.setTransform(matrix);
//
}
/**
* Prerequisites:
* - The device must be opened.
* - The surface texture must be available.
*/
protected void init() {
// Executed only after second call, loadingState makes sure both prerequisites are true
if (++loadingState != 2)
return;
final CameraDevice d = device;
final Surface surface = new Surface(viewfinder.getSurfaceTexture());
try {
previewRequestBuilder = d.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previewRequestBuilder.addTarget(surface);
} catch (CameraAccessException e) {
e.printStackTrace();
}
CameraCaptureSession.StateCallback cb = new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession cameraCaptureSession) {
// The camera is already closed
if (d == null) {
return;
}
Log.d(tag(), "Configured!");
// When the session is ready, we start displaying the preview.
captureSession = cameraCaptureSession;
try {
Log.d(tag(), "Surface: " + surface);
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_OFF);
previewRequest = previewRequestBuilder.build();
captureSession.setRepeatingRequest(previewRequest, previewCallback, null);
Log.d(tag(), "Preview started!");
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
Log.e(tag(), "Configure failed!");
}
};
try {
d.createCaptureSession(Arrays.asList(surface, imageReader.getSurface()), cb, null);
Log.d(tag(), "Session started");
}
catch (CameraAccessException e) {
}
}
// ...
My own log output is the same on both devices without errors or exceptions thrown, both CameraCaptureSession.CaptureCallback.onCaptureCompleted
and TextureView.SurfaceTextureListener.onSurfaceTextureUpdated
are regularly called. (Probably there are entries from the camera service that will tell another story, but then those errors should be reflected by explicit errors thrown in the activity.)
The preview is only live on my Samsung device, on my Nexus it has the right size but stays black.