I was following a youtube tutorial, trying to learn Camera2 API. Of course, I was learning this at the same time that I was developing my own app. One inconsistency between the tutorial and my app is that the tutorial made the camera in portrait mode only while my app must be in landscape.
I'm currently able to view the preview of the camera, though while my app is in landscape or horizontal, the camera preview looks rotated 90 degrees. It almost feels like I can rotate the TextureView, but that just seems incorrect, like when I take a picture, it will be rotated incorrectly.
Below is the code that has to do with image sizes (the whole code is very long)
private void setupCamera(int width, int height) {
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(camera_id);
StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
mPreviewSize = getPreferredPreviewSize(map.getOutputSizes(SurfaceTexture.class), width, height);
mCameraId = camera_id;
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
//TODO Look for a way to make this horizontal
private Size getPreferredPreviewSize(Size[] mapSizes, int width, int height) {
List<Size> collectorSizes = new ArrayList<>();
for (Size option : mapSizes) {
if (width > height) { //If the screen is in landscape
Toast.makeText(getApplicationContext(), "Screen is Landscape", Toast.LENGTH_SHORT).show();
if (option.getWidth() > width && option.getHeight() > height) {
collectorSizes.add(option);
}
} else { //if the screen is in portrait
Toast.makeText(getApplicationContext(), "Screen is Portrait", Toast.LENGTH_SHORT).show();
if (option.getWidth() > height && option.getHeight() > width) {
collectorSizes.add(option);
}
}
}
if (collectorSizes.size() > 0) {
return Collections.min(collectorSizes, new Comparator<Size>() {
@Override
public int compare(Size lhs, Size rhs) {
return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getWidth() + rhs.getHeight());
}
});
}
return mapSizes[0];
}
private void openCamera() {
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
return;
}
cameraManager.openCamera(mCameraId, mCameraDeviceStateCallback, null);
} catch (CameraAccessException e){
e.printStackTrace();
}
}
private void createCameraPreviewSession() {
try {
SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
surfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
Surface previewSurface = new Surface(surfaceTexture);
mPreviewCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewCaptureRequestBuilder.addTarget(previewSurface);
mCameraDevice.createCaptureSession(Arrays.asList(previewSurface),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
if(mCameraDevice == null){
return;
}
try {
mPreviewCaptureRequest = mPreviewCaptureRequestBuilder.build();
mCameraCaptureSession = session;
mCameraCaptureSession.setRepeatingRequest(mPreviewCaptureRequest, mSessionCaptureCallback, null);
} catch (CameraAccessException e){
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
Toast.makeText(getApplicationContext(), "Preview Session Failed", Toast.LENGTH_SHORT).show();
}
}, null);
} catch (CameraAccessException e){
e.printStackTrace();
}
}
I've been playing with the getPreferredPreviewSize
method, but I don't understand it as well as I should. I'm not sure about the compare
at the end of that using lhs
and rhs
.
Am I missing something simple to have this rotated?
The image must be rotated 270 degrees counterclockwise so that the preview's orientation matches the device orientation: A back-facing camera would produce an image buffer with the same orientation as the buffer above, but SENSOR_ORIENTATION is 90 degrees. As a result, the buffer is rotated 90 degrees clockwise.
PreviewView is a subclass of FrameLayout . To display the camera feed, it uses either a SurfaceView or TextureView , provides a preview surface to the camera when it's ready, tries to keep it valid as long as the camera is using it, and when released prematurely, provides a new surface if the camera is still in use.
Camera2 is the latest low-level Android camera package and replaces the deprecated Camera class. Camera2 provides in-depth controls for complex use cases, but requires you to manage device-specific configurations. You can read about specific Camera2 classes and functions in the reference documentation.
private void transformImage (int width, int height)
{
if(mPreviewSize == null || mTextureView == null)
{
return;
}
Matrix matrix = new Matrix();
int rotation = getWindowManager().getDefaultDisplay().getRotation();
RectF textureRectF = new RectF(0,0,width,height);
RectF previewRectF = new RectF(0,0,mPreviewSize.getHeight(),mPreviewSize.getWidth());
float centerX = textureRectF.centerX();
float centery = textureRectF.centerY();
if(rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_270)
{}
else if(rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270)
{
previewRectF.offset(centerX - previewRectF.centerX(),centery-previewRectF.centerY());
matrix.setRectToRect(textureRectF,previewRectF,Matrix.ScaleToFit.FILL);
float scale = Math.max((float)width / mPreviewSize.getWidth(),(float)height/ mPreviewSize.getHeight());
matrix.postScale(scale,scale,centerX,centery);
matrix.postRotate(90*(rotation-2),centerX,centery);
mTextureView.setTransform(matrix );
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With