

### 目的

以下の機能を持ったカメラアプリを作成すること。

  1. カメラのプレビューを全画面で表示する。
  2. カメラの露光感度、シャッタースピード、フレームレートをシークバーでリアルタイムに変更できる。
  3. プレビュー中に端末のLEDライトを点灯できる。
  4. ボタンを押したら録画が開始される。録画したデータは端末のメインストレージ直下に保存される。

### やったこと

1, 2, 3 の機能については、すでに実装が完了しています。  
4 については、ボタンの設定やパーミッションの設定、録画処理を作成しているのですが、現状うまくいっていません。

### コード

まず、コード全文を共有いたします。該当箇所については後述いたします。

＜MainActivity.kt＞ ※kotlinで作成しています。

[code]

    package com.example.mycamera2application2
    
    import android.Manifest
    import android.content.ContentValues.TAG
    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.media.*
    import android.net.Uri
    import android.os.Build
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.os.Environment
    import android.view.Surface
    import android.view.TextureView
    import android.widget.ImageButton
    import android.os.Handler
    import android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE
    import android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO
    import android.util.Log
    import androidx.annotation.RequiresApi
    import androidx.core.app.ActivityCompat
    import androidx.core.content.ContextCompat
    import android.widget.Button
    import android.widget.SeekBar
    import android.widget.TextView
    import java.io.File
    import java.io.IOException
    import java.nio.file.Files.exists
    import java.text.SimpleDateFormat
    import java.util.*
    import android.hardware.camera2.params.StreamConfigurationMap
    import android.media.CamcorderProfile
    import androidx.core.app.ActivityCompat.requestPermissions
    
    class MainActivity : AppCompatActivity() {
        // 定数を定義
        companion object {
            private const val REQUEST_CODE_PERMISSIONS = 10
            private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
        }
        // 変数を定義
        lateinit var configmap : StreamConfigurationMap
        lateinit var captureRequestBuilder : CaptureRequest.Builder
        lateinit var cameraCharacteristics: CameraCharacteristics
        lateinit var button_torch: ImageButton // LEDボタン
        lateinit var button_rec: Button // 録画ボタン
        lateinit var value_frame: TextView
        lateinit var value_speed: TextView
        lateinit var value_iso: TextView
        lateinit var cameraManager: CameraManager
        lateinit var seekbar_frame: SeekBar
        lateinit var seekbar_speed: SeekBar
        lateinit var seekbar_iso: SeekBar
        val textureView: TextureView by lazy {
            findViewById(R.id.textureView)
        }
        var cameraDevice: CameraDevice? = null
        var captureSession: CameraCaptureSession? = null
        var cameraId: String = "" // カメラのID取得用
        var torchSw = false // LED点灯消灯制御用
        // 音声の再生準備
        lateinit var soundPool: SoundPool // 再生機を設定
        var soundRecStart = 0 // 再生データを設定
        var soundRecStop = 0 // 再生データを設定
        var stateRecButton: Boolean = false // 録画ボタンの状態を管理する
        var recorder: MediaRecorder? = null
    
        //-------------------------------------------------------------------
    
        @RequiresApi(Build.VERSION_CODES.M)
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            // LED制御ボタンの設定
            torchCallback()
            // 録画ボタンの設定
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                configRecButton()
            }
            // soundpoolの初期化
            initSoundPool()
        }
    
        override fun onResume() {
            super.onResume()
            // ビューの準備ができている場合
            if (textureView.isAvailable) {
                // カメラ起動用関数を実行
                openCamera()
            } else {
                // ビューにイベントリスナーを設定
                textureView.surfaceTextureListener = object : TextureView.SurfaceTextureListener {
                    override fun onSurfaceTextureAvailable(p0: SurfaceTexture, p1: Int, p2: Int) {
                        // カメラ起動
                        openCamera()
                        // カメラの情報を元にシークバーを設定
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                            configSeekBar()
                        }
                    }
                    // ビューのサイズが変更された際の処理
                    override fun onSurfaceTextureSizeChanged(p0: SurfaceTexture, p1: Int, p2: Int) {}
                    // ビューが更新された際の処理
                    override fun onSurfaceTextureUpdated(p0: SurfaceTexture) {}
                    // ビューが破棄された際の処理
                    override fun onSurfaceTextureDestroyed(p0: SurfaceTexture): Boolean = true
                }
            }
        }
    
        // カメラのパーミッションの使用許諾を取得
        private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
            ContextCompat.checkSelfPermission(
                baseContext, it) == PackageManager.PERMISSION_GRANTED
        }
    
        // 外部ストレージの使用許諾を取得
        @RequiresApi(Build.VERSION_CODES.M)
        private fun storagePermission() {
            requestPermissions(
                arrayOf(
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
                ), 1 // requestCodeは0以上の値を好きに設定可能
            )
        }
    
        private fun openCamera() {
            // カメラの使用許可を取得する
            if (allPermissionsGranted()) {
                if (ActivityCompat.checkSelfPermission(
                        this,
                        Manifest.permission.CAMERA
                    ) != PackageManager.PERMISSION_GRANTED
                ) {
                    return
                }
                // カメラマネージャー（カメラへのアクセス機能）の取得
                cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
                // カメラの情報を取得
                cameraCharacteristics = cameraManager.getCameraCharacteristics("0")
                cameraManager.openCamera("0", object: CameraDevice.StateCallback() {
                    // カメラが起動できた場合
                    @RequiresApi(Build.VERSION_CODES.M)
                    override fun onOpened(camera: CameraDevice) {
                        cameraDevice = camera
                        createCameraPreviewSession()
                    }
                    // カメラから切断された場合
                    override fun onDisconnected(camera: CameraDevice) {
                        cameraDevice?.close()
                        cameraDevice = null
                    }
                    // カメラの接続でエラーが発生した場合
                    override fun onError(camera: CameraDevice, p1: Int) {
                        cameraDevice?.close()
                        cameraDevice = null
                    }
                }, null)
            } else {
                ActivityCompat.requestPermissions(
                    this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS
                )
            }
        }
    
        // カメラのisoやシャッタースピードなどの設定
        private fun configCamera() {
            // カメラのモード設定
            with(captureRequestBuilder) {
                // 設定値変更のために各モードを調整
                set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_OFF)
                set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF)
                set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_OFF)
                set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_OFF)
                set(CaptureRequest.CONTROL_CAPTURE_INTENT, CaptureRequest.CONTROL_CAPTURE_INTENT_MANUAL)
                // シークバーの現在値をフレームレートの値に設定(ナノ秒を秒に直して反映する -> *10^-9)
                set(CaptureRequest.SENSOR_FRAME_DURATION, 1000000000 / seekbar_frame.progress.toLong())
                // シークバーの現在値をシャッタースピードの値に設定(*10^-9)
                set(CaptureRequest.SENSOR_EXPOSURE_TIME,  1000000000000 / seekbar_speed.progress.toLong())
                // シークバーの現在値をisoの値に設定
                set(CaptureRequest.SENSOR_SENSITIVITY, seekbar_iso.progress)
            }
            // 設定状態でキャプチャーを継続
            captureSession?.setRepeatingRequest(captureRequestBuilder.build(), null, null)
            Log.i("configCamera","configCamera")
        }
    
        // SoundPoolの初期化処理
        private fun initSoundPool() {
            soundPool = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                SoundPool(5, AudioManager.STREAM_MUSIC, 0)
            } else {
                val attr = AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_MEDIA)
                    .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                    .build()
                SoundPool.Builder()
                    .setAudioAttributes(attr)
                    .setMaxStreams(5)
                    .build()
            }
            // 音声データの読み込み
            soundRecStart = soundPool.load(this, R.raw.rec_start, 1)
            soundRecStop  = soundPool.load(this, R.raw.rec_stop, 1)
        }
    
        // シークバーの設定
        @RequiresApi(Build.VERSION_CODES.O)
        private fun configSeekBar() {
            // フレームレートの現在値表示用テキスト
            value_frame = findViewById(R.id.text_frame)
            // フレームレートのシークバーの設定
            seekbar_frame = findViewById<SeekBar>(R.id.seekBar_frame)
            // フレームレートの限界値を取得
            var frame_range = cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_MAX_FRAME_DURATION)
            Log.i("frame_range",":${frame_range}")
            frame_range = 1000000000 / (frame_range!!/50)
            seekbar_frame.max = 30 // 最大値
            seekbar_frame.min = frame_range!!.toInt() // 最小値
            seekbar_frame.progress = (seekbar_frame.max + seekbar_frame.min) / 2 // 初期値（現在値）
            value_frame.text = "${seekbar_frame.progress} fps" // 現在値をテキストに反映
            // イベントリスナーの設定
            seekbar_frame.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
                // 値が変更された時に呼ばれる
                override fun onProgressChanged(seekbar_frame: SeekBar?, progress: Int, fromUser: Boolean) {
                    value_frame.text = "${seekbar_frame?.progress} fps" // 現在値をテキストに反映
                    configCamera() // カメラの設定値を変更
                    Log.i("seekbar_frame","seekbar_frame:${seekbar_frame?.progress}")
                }
                // つまみがタッチされた時に呼ばれる
                override fun onStartTrackingTouch(seekbar_frame: SeekBar?) {
                }
                // つまみが離された時に呼ばれる
                override fun onStopTrackingTouch(seekbar_frame: SeekBar?) {
                }
            })
    
            // isoの現在値表示用テキスト
            value_iso = findViewById(R.id.text_iso)
            // isoのシークバーの設定
            seekbar_iso = findViewById<SeekBar>(R.id.seekBar_iso)
            // isoの限界値を取得
            val iso_range = cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE)
            Log.i("iso_range",":${iso_range}")
            // 限界値をシークバーの限界値に反映
            seekbar_iso.max = iso_range!!.upper.toInt()
            seekbar_iso.min = iso_range!!.lower.toInt()
            seekbar_iso.progress = (seekbar_iso.max + seekbar_iso.min) / 2
            value_iso.text = "${seekbar_iso.progress}"
            // イベントリスナーの設定
            seekbar_iso.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
                // 値が変更された時に呼ばれる
                override fun onProgressChanged(seekbar_iso: SeekBar?, progress: Int, fromUser: Boolean) {
                    value_iso.text = "${seekbar_iso?.progress}"
                    configCamera() // カメラの設定値を変更
                    Log.i("seekbar_iso","seekbar_iso:${seekbar_iso?.progress}")
                }
                // つまみがタッチされた時に呼ばれる
                override fun onStartTrackingTouch(seekbar_iso: SeekBar?) {
                }
                // つまみが離された時に呼ばれる
                override fun onStopTrackingTouch(seekbar_iso: SeekBar?) {
                }
            })
    
            // シャッタースピードの現在値表示用テキスト
            value_speed = findViewById(R.id.text_speed)
            // シャッタースピードのシークバーの設定
            seekbar_speed = findViewById<SeekBar>(R.id.seekBar_speed)
            // シャッタースピードの限界値を取得
            val speed_range = cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE)
            Log.i("speed_range",":${speed_range}")
            // 限界値をシークバーの限界値に反映(isoの値を元に限界値を設定)
            seekbar_speed.max = iso_range!!.upper.toInt() * 1000
            seekbar_speed.min = iso_range!!.lower.toInt() * 1000
            seekbar_speed.progress = (seekbar_speed.max + seekbar_speed.min) / 2
            value_speed.text = "1/${seekbar_speed.progress / 1000} 秒"
            // イベントリスナーの設定
            seekbar_speed.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
                // 値が変更された時に呼ばれる
                override fun onProgressChanged(seekbar_speed: SeekBar?, progress: Int, fromUser: Boolean) {
                    value_speed.text = "1/${seekbar_speed?.progress!! / 1000} 秒"
                    configCamera() // カメラの設定値を変更
                    Log.i("seekbar_speed","seekbar_speed:${seekbar_speed?.progress}")
                }
                // つまみがタッチされた時に呼ばれる
                override fun onStartTrackingTouch(seekbar_speed: SeekBar?) {
                }
                // つまみが離された時に呼ばれる
                override fun onStopTrackingTouch(seekbar_speed: SeekBar?) {
                }
            })
        }
    
        // トーチモードのコールバックを登録する
        @RequiresApi(Build.VERSION_CODES.M)
        private fun torchCallback() {
            cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
            cameraManager.registerTorchCallback(@RequiresApi(Build.VERSION_CODES.M)
            object : CameraManager.TorchCallback() {
                override fun onTorchModeChanged(id: String, enabled: Boolean) {
                    super.onTorchModeChanged(id, enabled)
                    // 状態を保存する
                    cameraId = id
                    torchSw = enabled
                }
            }, Handler())
            // LEDボタンのイベント登録
            button_torch = findViewById(R.id.button_torch)
            button_torch.setOnClickListener {
                Log.i("config_light","config_light")
                switchLight()
            }
        }
    
        // LEDの点灯消灯切り替え
        @RequiresApi(Build.VERSION_CODES.M)
        private fun switchLight() {
            if(!torchSw) { // false(消灯中)の場合
                button_torch.setImageResource(R.drawable.flashlight_button_on) // 画像切り替え
                captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON)
                captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH)
            }
            else { // true(点灯中)の場合
                button_torch.setImageResource(R.drawable.flashlight_button_off) // 画像切り替え
                captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON)
                captureRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF)
            }
            // 設定状態でキャプチャーを継続
            captureSession?.setRepeatingRequest(captureRequestBuilder.build(), null, null)
            // 状態を反転させて、消灯なら点灯、点灯なら消灯させる
            torchSw = !torchSw
            Log.i("switchLight","torchSw : ${torchSw}")
        }
    
        // 録画ボタンの設定
        @RequiresApi(Build.VERSION_CODES.O)
        private fun configRecButton() {
            // 録画ボタンのイベント登録
            button_rec = findViewById(R.id.button_rec)
            button_rec.setOnClickListener {
                storagePermission() // 外部ストレージへの書き込み許可を取得
                stateRecButton = !stateRecButton // 値を反転
                if (stateRecButton){
                    if (prepareVideoRecorder()) {
                        Log.i("start","record")
    　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　recorder?.start()
                    } else {
                        releaseMediaRecorder()
                    }
                }
                else{
                    // stop recording and release camera
                    Log.i("stop","stop")
                    recorder?.stop() // stop the recording
                    Log.i("release","release")
                    releaseMediaRecorder() // release the MediaRecorder object
                }
            }
        }
    
        // プレビュー用のセッションを作成
        private fun createCameraPreviewSession() {
            if (cameraDevice == null) {
                return
            }
            val texture = textureView.surfaceTexture
            // プレビューのサイズを指定
            texture?.setDefaultBufferSize(640, 480)
            // プレビューの表示先となるsurfaceを設定
            val surface = Surface(texture)
    
            captureRequestBuilder = cameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
            captureRequestBuilder.addTarget(surface)
    
            cameraDevice?.createCaptureSession(listOf(surface), object : CameraCaptureSession.StateCallback() {
                // セッションが取得できた場合
                override fun onConfigured(session: CameraCaptureSession) {
                    // プレビュー画像をTextureViewにセット
                    captureSession = session
                    captureSession?.setRepeatingRequest(captureRequestBuilder.build(), null, null)
                    // カメラのモード設定と値の設定
                    configCamera()
                }
                // セッションが取得できなかった場合
                override fun onConfigureFailed(session: CameraCaptureSession) {}
            }, null)
        }
    
        // 録画クラスMediaRecorderの初期化　-> MediaRecorderが非推奨になってしまっているが問題ないか
        @RequiresApi(Build.VERSION_CODES.O)
        private fun prepareVideoRecorder(): Boolean {
            // MediaRecorderをインスタンス化
            recorder = MediaRecorder()
            cameraManager?.let { cameraDevice ->
                recorder?.run {
                    setVideoSource(MediaRecorder.VideoSource.CAMERA) // 録画の入力元となるカメラを設定
                    setOutputFormat(MediaRecorder.OutputFormat.DEFAULT) // ビデオのエンコーダを指定
                    setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT) // ファイルフォーマットを指定
                    val dir = Environment.getExternalStorageDirectory() // ディレクトリを取得
                    val file = File.createTempFile("video", ".mp4", dir) // 一時ファイルをディレクトリに保存
                    recorder?.setOutputFile(file.absolutePath) // 出力ファイル名を設定(ランダムな数字を付加)
                    // 録画準備
                    return try {
                        Log.i("start","prepare")
                        prepare()
                        Log.i("finish","prepare")
                        true
                    } catch (e: IllegalStateException) {
                        Log.d(TAG, "IllegalStateException preparing MediaRecorder: ${e.message}")
                        releaseMediaRecorder()
                        false
                    } catch (e: IOException) {
                        Log.d(TAG, "IOException preparing MediaRecorder: ${e.message}")
                        releaseMediaRecorder()
                        false
                    }
                }
            }
            return false
        }
    
        // 録画メソッド解放
        private fun releaseMediaRecorder() {
            recorder?.reset() // clear recorder configuration
            recorder?.release() // release the recorder object
            recorder = null
        }
    }
    
[/code]

＜AndroidManifest.xml＞

[code]

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.mycamera2application2">
    
        <uses-permission android:name="android.permission.CAMERA" />
        <!--DCIMも外部ストレージ扱いになるのでSDカードへの保存と同様にパーミッションは必要-->
        <uses-permission
            android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission
            android:name="android.permission.READ_EXTERNAL_STORAGE" />
        <uses-feature android:name="android.hardware.camera" />
        <uses-feature 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/Theme.MyCamera2Application2">
            <activity
                android:name=".MainActivity"
                android:exported="true">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    </manifest>
    
[/code]

### 該当部分

MainActivity.ktの以下の部分で録画の準備を行なっています。

[code]

        // 録画クラスMediaRecorderの初期化　-> MediaRecorderが非推奨になってしまっているが問題ないか
        @RequiresApi(Build.VERSION_CODES.O)
        private fun prepareVideoRecorder(): Boolean {
            // MediaRecorderをインスタンス化
            recorder = MediaRecorder()
            cameraManager?.let { cameraDevice ->
                recorder?.run {
                    setVideoSource(MediaRecorder.VideoSource.CAMERA) // 録画の入力元となるカメラを設定
                    setOutputFormat(MediaRecorder.OutputFormat.DEFAULT) // ビデオのエンコーダを指定
                    setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT) // ファイルフォーマットを指定
                    val dir = Environment.getExternalStorageDirectory() // ディレクトリを取得
                    val file = File.createTempFile("video", ".mp4", dir) // 一時ファイルをディレクトリに保存
                    recorder?.setOutputFile(file.absolutePath) // 出力ファイル名を設定(ランダムな数字を付加)
                    // 録画準備
                    return try {
                        Log.i("start","prepare")
                        prepare()
                        Log.i("finish","prepare")
                        true
                    } catch (e: IllegalStateException) {
                        Log.d(TAG, "IllegalStateException preparing MediaRecorder: ${e.message}")
                        releaseMediaRecorder()
                        false
                    } catch (e: IOException) {
                        Log.d(TAG, "IOException preparing MediaRecorder: ${e.message}")
                        releaseMediaRecorder()
                        false
                    }
                }
            }
            return false
        }
    
[/code]

録画の開始、停止は以下の録画ボタンの処理に応じて実行しています。

[code]

    // 録画ボタンの設定
        @RequiresApi(Build.VERSION_CODES.O)
        private fun configRecButton() {
            // 録画ボタンのイベント登録
            button_rec = findViewById(R.id.button_rec)
            button_rec.setOnClickListener {
                storagePermission() // 外部ストレージへの書き込み許可を取得
                stateRecButton = !stateRecButton // 値を反転
                if (stateRecButton){
                    if (prepareVideoRecorder()) {
                        Log.i("start","record")
    　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　recorder?.start()
                    } else {
                        releaseMediaRecorder()
                    }
                }
                else{
                    Log.i("stop","stop")
                    recorder?.stop() // stop the recording
                    Log.i("release","release")
                    releaseMediaRecorder() // release the MediaRecorder object
                }
            }
        }
    
[/code]

### エラー内容

以上のコードを実行すると、以下のエラーが表示されます。

[code]

    W/Binder:28069_5: type=1400 audit(0.0:850327): avc: denied { read } for name="u:object_r:vendor_camera_prop:s0" dev="tmpfs" ino=18399 scontext=u:r:untrusted_app:s0:c213,c256,c512,c768 tcontext=u:object_r:vendor_camera_prop:s0 tclass=file permissive=0
    E/libc: Access denied finding property "persist.vendor.camera.privapp.list"
    E/libc: Access denied finding property "vendor.camera.aux.packagelist"
    E/libc: Access denied finding property "vendor.camera.aux.packagelist"
    E/libc: Access denied finding property "vendor.camera.aux.packagelist"
    E/libc: Access denied finding property "vendor.camera.aux.packagelist"
    E/MediaRecorder: start failed: -22
    D/AndroidRuntime: Shutting down VM
    E/AndroidRuntime: FATAL EXCEPTION: main
    
[/code]

見たところ、「Access denied finding property」とあり、何かしらのアクセスがうまくいっていないのかと考えています。  
カメラのプレビューは表示されておりますので、ストレージへのアクセスで問題が発生しているのかもしれません。

### 質問内容

確認したところ、prepareを実行したタイミングでエラーが発生しているようなので、設定項目に何か間違いがあるのか、権限の付与がうまくいっていないのだと思いますが、調査をしても明確な原因がわかっていない状態です。

ちなみに、アプリの権限の状態を確認すると、「カメラ」と「ストレージ」の項目はONになっており、権限が付与されていることが確認できております。

私と同じように録画機能を実装する中で同じ問題に直面している例が見当たらず、解決に至れていない状態です。  
調査すると、中には似たような例もあるのですが、そこに記載されている内容を試しても解決に至れておりません。

そのため、お手数ですが、コードをご確認の上、どの部分に間違いがあるのかをご指摘いただけますと幸いです。。。  
（カメラを使用するため、検証にはandroidの実機が必要になります。）

### 端末情報

以下の端末で動作の確認をしています。  
SHARP AQUOS SHV48（Android 9）

### 参考にしたサイト

[Qiita -
Camera2apiを使って動画を撮影する](https://qiita.com/ohwada/items/4ef112bf6758ceed46c3)  
[Qiita -
DCIMに保存してギャラリーで表示させる方法](https://qiita.com/naoi/items/70ac5826cc6de86d6b78)  
[Softbank -
録画アプリの作り方](https://www.support.softbankmobile.co.jp/partner_st/home_tech9/column6-3.cfm)  
[StackOverFlow -
ストレージへのファイルの書き込みがうまくいかない](https://ja.stackoverflow.com/questions/42853/android%E3%81%A7%E3%82%B9%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B8%E3%81%B8%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E6%9B%B8%E3%81%8D%E8%BE%BC%E3%81%BF%E3%81%8C%E5%87%BA%E6%9D%A5%E3%81%AA%E3%81%84-%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E6%8B%92%E5%90%A6)  
[TECHBOOSTER -
MediaRecorderで録画する](https://techbooster.org/android/multimedia/4265/)  
[Android - camera2での録画サンプル](https://github.com/googlearchive/android-
Camera2Video/tree/master/kotlinApp)  
[Android -
MediaRecorderについて](https://developer.android.com/reference/android/media/MediaRecorder)  
[StackOverFlow -
同様のエラー内容に関する質問1](https://stackoverflow.com/questions/55990262/access-denied-
for-property-vendor-camera-aux-packagelist)  
[GitHub - 同様のエラー内容に関する質問2](https://github.com/mintware-
de/flutter_barcode_reader/issues/212)  
[TitanWolf -
同様のエラー内容に関する質問3](https://www.titanwolf.org/Network/q/6625c59a-8180-43f0-8b05-d8c639346676/y)


CC BY-SA 4.0
2021-10-06T02:38:11.520
82905
2021-10-14T04:24:17.803
2021-10-06T04:33:32.563
3060
46924
question
java android kotlin
Androidで録画機能が実装できない。


【自己解決】  
より多くの方々にご確認いただくために、以下のリンク先でも同様の質問をしておりました。  
回答についてはそちらに掲載させていただきました。  
[マルチポストリンク](https://teratail.com/questions/363036?reply=true)


CC BY-SA 4.0
2021-10-14T04:24:17.803
83088
2021-10-14T04:24:17.803
46924
82905
answer