今更カメラ制御の基礎(STEP1:とりあえずプレビュー表示) の記事のように最小限のカメラ実装の情報で、Kotlinかつandroid.hardware.camera2のものが無かったのでまとめます。
やりたいこと
とりあえずプレビュー表示する
カメラ制御の主な流れ
- 対応APIを確認
- Manifestにuses-permissionを追加
- プレビュー表示用のTextureViewを配置
- TextureView.SurfaceTextureListener実装
- openCamera実装
- requestCameraPermission実装
- CameraDevice.StateCallback実装
- createCaptureSession実装
対応APIを確認
https://developer.android.com/reference/android/hardware/camera2/package-summary にある通り、API level 21が必要。 ってことで以下のようにbuild.gradle(Module: app)
を書く
build.gradle
android {
compileSdkVersion 27
defaultConfig {
applicationId "net.kboy.mlkitsample"
minSdkVersion 23
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
Manifestにuses-permissionを追加
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.kboy.mlkitsample">
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.hardware.camera.autofocus" />
<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">
<meta-data
android:name="com.google.firebase.ml.vision.DEPENDENCIES"
android:value="face" />
<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>
プレビュー表示用のTextureViewを配置
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextureView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/mySurfaceView" />
</FrameLayout>
TextureView.SurfaceTextureListener実装
MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
textureView = findViewById(R.id.mySurfaceView)
textureView.surfaceTextureListener = surfaceTextureListener
startBackgroundThread()
}
private val surfaceTextureListener = object : TextureView.SurfaceTextureListener {
override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
imageReader = ImageReader.newInstance(width, height,
ImageFormat.JPEG, /*maxImages*/ 2)
openCamera()
}
override fun onSurfaceTextureSizeChanged(p0: SurfaceTexture?, p1: Int, p2: Int) {
}
override fun onSurfaceTextureUpdated(p0: SurfaceTexture?) {
}
override fun onSurfaceTextureDestroyed(p0: SurfaceTexture?): Boolean {
return false
}
}
openCamera実装
MainActivity.kt
fun openCamera() {
var manager: CameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
try {
var camerId: String = manager.getCameraIdList()[0]
val permission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
if (permission != PackageManager.PERMISSION_GRANTED) {
requestCameraPermission()
return
}
manager.openCamera(camerId, stateCallback, null)
} catch (e: Exception) {
e.printStackTrace()
}
}
requestCameraPermission実装
これないと落ちるし、カメラが使えない。
private fun requestCameraPermission() {
requestPermissions(arrayOf(Manifest.permission.CAMERA), 200)
}
また、onRequestPermissionsResultでダイアログ許可後をハンドリングできる
https://developer.android.com/training/permissions/requesting?hl=ja
CameraDevice.StateCallback実装
MainActivity.kt
private val stateCallback = object : CameraDevice.StateCallback() {
override fun onOpened(cameraDevice: CameraDevice) {
this@MainActivity.cameraDevice = cameraDevice
createCameraPreviewSession()
}
override fun onDisconnected(cameraDevice: CameraDevice) {
cameraDevice.close()
this@MainActivity.cameraDevice = null
}
override fun onError(cameraDevice: CameraDevice, error: Int) {
onDisconnected(cameraDevice)
finish()
}
}
createCameraPreviewSession実装
MainActivity.kt
private fun createCameraPreviewSession() {
try {
val texture = textureView.surfaceTexture
texture.setDefaultBufferSize(previewSize.width, previewSize.height)
val surface = Surface(texture)
previewRequestBuilder = cameraDevice!!.createCaptureRequest(
CameraDevice.TEMPLATE_PREVIEW
)
previewRequestBuilder.addTarget(surface)
cameraDevice?.createCaptureSession(Arrays.asList(surface, imageReader?.surface),
object : CameraCaptureSession.StateCallback() {
override fun onConfigured(cameraCaptureSession: CameraCaptureSession) {
if (cameraDevice == null) return
captureSession = cameraCaptureSession
try {
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
previewRequest = previewRequestBuilder.build()
captureSession?.setRepeatingRequest(previewRequest,
null, Handler(backgroundThread?.looper))
} catch (e: CameraAccessException) {
Log.e("erfs", e.toString())
}
}
override fun onConfigureFailed(session: CameraCaptureSession) {
//Tools.makeToast(baseContext, "Failed")
}
}, null)
} catch (e: CameraAccessException) {
Log.e("erf", e.toString())
}
}
完成
本記事のGithubコード