I am trying to understand this new camera2 API, and it is just not easy. I looked at Google's Camera2Basic sample code, it is still daunting for Android beginners like myself. Well, here is my attempt to trim down this fat sample code into the bare minimum so that it is easier to understand the fundamentals of the camera2 API.
All it does is to connect to a camera and display its preview to the screen. No capture, no manual focus, no error checks, etc. I am not happy that even this bare-bone app requires so many lines. Anyways, I will add more functionalities in the upcoming post.
All it does is to connect to a camera and display its preview to the screen. No capture, no manual focus, no error checks, etc. I am not happy that even this bare-bone app requires so many lines. Anyways, I will add more functionalities in the upcoming post.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:app="http://schemas.android.com/apk/res-auto" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
tools:context="com.unixnme.camera2.MainActivity"> | |
<TextureView | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:id="@+id/preview"/> | |
</android.support.constraint.ConstraintLayout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | |
package="com.unixnme.camera2"> | |
<uses-permission android:name="android.permission.CAMERA" /> | |
<uses-feature android:name="android.hardware.camera.level.full" /> | |
<application | |
android:allowBackup="true" | |
android:icon="@mipmap/ic_launcher" | |
android:label="@string/app_name" | |
android:roundIcon="@mipmap/ic_launcher_round" | |
android:supportsRtl="true" | |
android:theme="@style/AppTheme"> | |
<activity android:name=".MainActivity"> | |
<intent-filter> | |
<action android:name="android.intent.action.MAIN" /> | |
<category android:name="android.intent.category.LAUNCHER" /> | |
</intent-filter> | |
</activity> | |
</application> | |
</manifest> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.unixnme.camera2; | |
import android.Manifest; | |
import android.content.Context; | |
import android.content.pm.PackageManager; | |
import android.graphics.SurfaceTexture; | |
import android.hardware.camera2.CameraCaptureSession; | |
import android.hardware.camera2.CameraCharacteristics; | |
import android.hardware.camera2.CameraDevice; | |
import android.hardware.camera2.CameraManager; | |
import android.hardware.camera2.CaptureRequest; | |
import android.hardware.camera2.params.StreamConfigurationMap; | |
import android.support.annotation.NonNull; | |
import android.support.v4.content.ContextCompat; | |
import android.support.v7.app.AppCompatActivity; | |
import android.os.Bundle; | |
import android.util.Log; | |
import android.util.Size; | |
import android.view.Surface; | |
import android.view.TextureView; | |
import java.util.Arrays; | |
import java.util.Collections; | |
public class MainActivity extends AppCompatActivity { | |
private TextureView mTextureView; | |
final private static String TAG = MainActivity.class.getSimpleName(); | |
// preview related //////////////////////////////////////////////////////////////// | |
private CaptureRequest.Builder mCaptureRequestbuilder; | |
private final TextureView.SurfaceTextureListener mSurfaceTextureListener | |
= new TextureView.SurfaceTextureListener() { | |
@Override | |
public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) { | |
openCamera(width, height); | |
} | |
@Override | |
public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) { | |
} | |
@Override | |
public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) { | |
return true; | |
} | |
@Override | |
public void onSurfaceTextureUpdated(SurfaceTexture texture) { | |
} | |
}; | |
final private CameraCaptureSession.StateCallback mCaptureStateCallback = new CameraCaptureSession.StateCallback() { | |
@Override | |
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) { | |
try { | |
cameraCaptureSession.setRepeatingRequest(mCaptureRequestbuilder.build(), null, null); | |
} catch (Throwable t) { | |
handleException(t); | |
} | |
} | |
@Override | |
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { | |
} | |
}; | |
private void createCameraPreviewSession() { | |
SurfaceTexture texture = mTextureView.getSurfaceTexture(); | |
texture.setDefaultBufferSize(mSize.getWidth(), mSize.getHeight()); | |
Surface surface = new Surface(texture); | |
try { | |
mCaptureRequestbuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); | |
mCaptureRequestbuilder.addTarget(surface); | |
mCameraDevice.createCaptureSession(Collections.singletonList(surface), mCaptureStateCallback, null); | |
} catch (Throwable t) { | |
handleException(t); | |
} | |
} | |
// camera related ///////////////////////////////////////////////////////////// | |
private CameraDevice mCameraDevice; | |
private Size mSize; | |
final private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { | |
@Override | |
public void onOpened(@NonNull CameraDevice cameraDevice) { | |
mCameraDevice = cameraDevice; | |
createCameraPreviewSession(); | |
} | |
@Override | |
public void onDisconnected(@NonNull CameraDevice cameraDevice) { | |
mCameraDevice = null; | |
} | |
@Override | |
public void onError(@NonNull CameraDevice cameraDevice, int i) { | |
mCameraDevice = null; | |
} | |
}; | |
private void openCamera(int width, int height) { | |
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) | |
!= PackageManager.PERMISSION_GRANTED) { | |
requestCameraPermission(); | |
return; | |
} | |
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); | |
try { | |
String cameraId = cameraManager.getCameraIdList()[0]; | |
CameraCharacteristics characteristics | |
= cameraManager.getCameraCharacteristics(cameraId); | |
StreamConfigurationMap map = characteristics.get( | |
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); | |
mSize = map.getOutputSizes(SurfaceTexture.class)[0]; | |
cameraManager.openCamera(cameraId, mStateCallback, null); | |
} catch (Throwable t) { | |
handleException(t); | |
} | |
} | |
private void closeCamera() { | |
try { | |
if (null != mCameraDevice) | |
mCameraDevice.close(); | |
} catch (Throwable t) { | |
handleException(t); | |
} | |
} | |
// activity lifecycle related ///////////////////////////////////////////////// | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_main); | |
mTextureView = findViewById(R.id.preview); | |
} | |
@Override | |
protected void onResume() { | |
super.onResume(); | |
// we open up camera after the textureview is available | |
// either way, we will call openCamera | |
if (mTextureView.isAvailable()) { | |
// call now | |
int width = mTextureView.getWidth(); | |
int height = mTextureView.getHeight(); | |
openCamera(width, height); | |
} else { | |
// call from the listener callback | |
mTextureView.setSurfaceTextureListener(mSurfaceTextureListener); | |
} | |
} | |
@Override | |
public void onPause() { | |
super.onPause(); | |
closeCamera(); | |
} | |
// request related /////////////////////////////////////////////////////////////// | |
private static final int REQUEST_CAMERA_PERMISSION = 1; | |
private void requestCameraPermission() { | |
requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION); | |
} | |
@Override | |
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, | |
@NonNull int[] grantResults) { | |
if (requestCode == REQUEST_CAMERA_PERMISSION) { | |
if (grantResults.length != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) { | |
// permissio not granted; quit the app | |
this.finish(); | |
} | |
} else { | |
super.onRequestPermissionsResult(requestCode, permissions, grantResults); | |
} | |
} | |
// Exceptions //////////////////////////////////////////////////////////////////////// | |
private void handleException(Throwable t) { | |
Log.d(TAG, t.getMessage()); | |
t.printStackTrace(); | |
} | |
} |
No comments:
Post a Comment