

teratailでも質問していますがこちらでも質問させていただきます。<https://teratail.com/questions/232090>

## 問題

Camera2 APIを使用してカメラを使用したいです。  
とりあえずカメラのプレビューをTextureViewに表示させることはできたのですが、プレビューが歪んで表示されてしまいます。

具体的にいますと、縦画面時は上下に伸びたような形となり、横画面時は横に伸びたように表示されます。

これをどうにかして正常に表示できるようにしたいです。

## 試したこと

端末が対応している撮影サイズを取得して最大サイズをプレビューサイズとすることでうまくいくとの情報がありましたので、撮影サイズのリストを取得し、その最大サイズ（配列の要素0個目）をsetDefaultBufferSizeしてみましたがうまくいきませんでした。

お知恵をお貸しください。

## コード

DriveRecorderActivity.kt

[code]

    package caios.android.drive_recorder
    
    import android.content.Context
    import android.content.Intent
    import android.content.pm.PackageManager
    import android.graphics.*
    import android.hardware.camera2.CameraCaptureSession
    import android.hardware.camera2.CameraCharacteristics
    import android.hardware.camera2.CameraDevice
    import android.hardware.camera2.CameraManager
    import android.net.Uri
    import android.os.Build
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.provider.Settings
    import android.util.Log
    import android.util.Size
    import android.view.Surface
    import android.view.TextureView
    import android.view.WindowManager
    import androidx.core.app.ActivityCompat
    import androidx.core.content.ContextCompat
    import kotlinx.android.synthetic.main.activity_drive_recorder.*
    import java.lang.Exception
    import java.util.jar.Manifest
    
    class DriveRecorderActivity : AppCompatActivity() {
    
        private lateinit var textureView: TextureView
    
        private var permissionRequestCount = 0
        private var cameraDevice: CameraDevice? = null
        private var cameraSize = Size(1920, 1080)
        private var cameraCaptureSession: CameraCaptureSession? = null
    
        private val cameraManager: CameraManager by lazy {
            getSystemService(Context.CAMERA_SERVICE) as CameraManager
        }
        private val cameraId: String by lazy {
            cameraManager.cameraIdList[0]
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_drive_recorder)
    
            textureView = ADR_TextureView
            configurationTransform()
        }
    
        override fun onResume() {
            super.onResume()
    
            if (textureView.isAvailable) {
                checkPermissionAndOpenCamera()
            }
            else {
                textureView.surfaceTextureListener = object : TextureView.SurfaceTextureListener {
                    override fun onSurfaceTextureAvailable(p0: SurfaceTexture?, p1: Int, p2: Int) {
                        checkPermissionAndOpenCamera()
                    }
    
                    override fun onSurfaceTextureDestroyed(p0: SurfaceTexture?): Boolean {
                        return true
                    }
    
                    override fun onSurfaceTextureUpdated(p0: SurfaceTexture?) {
    
                    }
    
                    override fun onSurfaceTextureSizeChanged(p0: SurfaceTexture?, p1: Int, p2: Int) {
                        configurationTransform()
                    }
                }
            }
        }
    
        override fun onPause() {
            super.onPause()
            closeCamera()
        }
    
        private fun openCamera() {
            if(ActivityCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) return
    
            setCameraSize()
    
            val cameraStateCallback = object : CameraDevice.StateCallback() {
                override fun onOpened(p0: CameraDevice) {
                    cameraDevice = p0
                    createCameraPreview()
                }
    
                override fun onDisconnected(p0: CameraDevice) {
                    cameraDevice?.close()
                    cameraDevice = null
                }
    
                override fun onError(p0: CameraDevice, p1: Int) {
                    cameraDevice?.close()
                    cameraDevice = null
                }
            }
    
            cameraManager.openCamera(cameraId, cameraStateCallback, handler)
        }
    
        private fun closeCamera() {
            cameraCaptureSession?.close()
            cameraDevice?.close()
        }
    
        private fun createCameraPreview(){
            try {
                val surfaceTexture = textureView.surfaceTexture
                surfaceTexture.setDefaultBufferSize(cameraSize.width, cameraSize.height)
    
                val surface = Surface(surfaceTexture)
                val previewRequestBuilder = cameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
                previewRequestBuilder.addTarget(surface)
    
                val captureSessionStateCallback = object : CameraCaptureSession.StateCallback() {
                    override fun onConfigured(p0: CameraCaptureSession) {
                        cameraCaptureSession = p0
                        cameraCaptureSession?.setRepeatingRequest(previewRequestBuilder.build(), null, null)
                    }
    
                    override fun onConfigureFailed(p0: CameraCaptureSession) {
    
                    }
                }
    
                cameraDevice?.createCaptureSession(listOf(surface), captureSessionStateCallback, null)
    
                configurationTransform()
            }
            catch (e: Exception){
                Log.d(G_TAG, "$e")
            }
        }
    
        private fun setCameraSize() {
            val characteristic = cameraManager.getCameraCharacteristics(cameraId)
            val scm = characteristic.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
            val mCameraSize = scm?.getOutputSizes(SurfaceTexture::class.java) ?: return
    
            for(cameraSize in mCameraSize){
                Log.d(G_TAG, cameraSize.toString())
            }
    
            cameraSize = mCameraSize[0]
    
            configurationTransform()
        }
    
        private fun configurationTransform() {
            val matrix = Matrix()
            val centerX = textureView.width / 2f
            val centerY = textureView.height / 2f
            val orientation = getTextureViewOrientation() ?: return
    
            matrix.postRotate(-orientation.toFloat(), centerX, centerY)
    
            textureView.setTransform(matrix)
        }
    
        private fun getTextureViewOrientation(): Int?{
            try {
                return when (textureView.display.rotation) {
                    Surface.ROTATION_0   -> 0
                    Surface.ROTATION_90  -> 90
                    Surface.ROTATION_180 -> 180
                    Surface.ROTATION_270 -> 270
                    else                 -> null
                }
            }
            catch (e: Exception){
                Log.d(G_TAG, "$e")
                return null
            }
        }
    
        private fun checkPermissionAndOpenCamera() {
            if (Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                if(ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.CAMERA) || permissionRequestCount == 0) {
                    ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.CAMERA), 92)
                    permissionRequestCount++
                }
                else {
                    val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts("package", packageName, null))
                    startActivity(intent)
                }
            }
            else {
                openCamera()
            }
        }
    
        override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
            when(requestCode){
                92 -> {
                    if(ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){
                        openCamera()
                    }
                    else {
                        global.toast(this, "権限不足")
                    }
                }
            }
        }
    }
    
[/code]

activity_drive_recorder.xml

[code]

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout 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"
        android:id="@+id/ADR_RootView"
        tools:context=".DriveRecorderActivity">
    
        <TextureView
            android:id="@+id/ADR_TextureView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    
    </FrameLayout>
    
[/code]


CC BY-SA 4.0
2019-12-27T12:32:30.773
61837
2022-01-19T02:00:50.097
question
java android api kotlin camera
Camera2 APIでプレビュー画像がゆがむ


コードを確認させていただきました。'0番目のサイズ(最大)'とご質問にありますが、ここに入る値はデバイスごとで変化し、かならずしも最大にはなりません。歪みをなくすためには同一の比率で表示して適切にトリムを行うなどの対処が必要になります。以下の二点の修正を試してみてください

  * setCameraSizeで0番目でなく最大のプレビューサイズ(通常、デバイスのスクリーンサイズ)を取得するようにする
  * configurationTransformでプレビューサイズのアスペクト比を計算して、得られた値でtextureViewをpostScaleやpreScaleする

記述がシンプルになりますのでCameraXなどの利用もご検討ください。以下のサイト様ではご希望の実装をされているかと思います。

<https://dev.classmethod.jp/smartphone/android/android-camerax-preview/>


CC BY-SA 4.0
2020-02-10T05:53:40.513
62921
2020-02-10T05:53:40.513
37777
61837
answer