LoginSignup
46
42

More than 3 years have passed since last update.

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

Last updated at Posted at 2018-07-18

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

参考

46
42
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
46
42