Edited at

Android, Kotlinでカメラ(camera2)を表示するための最小実装

今更カメラ制御の基礎(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コード

https://github.com/kboy-silvergym/MLKitSample/blob/6efa8acced3cf2d56bf98e65505a1873c0ccb37f/Android/app/src/main/java/net/kboy/mlkitsample/MainActivity.kt


参考