Android
 Computer >> コンピューター >  >> システム >> Android

Android Camera2 – Camera2 API を使用して写真やビデオを撮影する方法

私たちは皆、携帯電話のカメラを使用しており、それを頻繁に使用しています。カメラを機能として統合したアプリケーションもあります。

一方では、カメラを操作する標準的な方法があります。もう 1 つは、カメラとのやり取りをカスタマイズする方法です。この区別は重要です。そこで Camera2 の出番です。

カメラ 2 とは?

API レベル 21 から利用可能になりましたが、Camera2 API は、開発者が対処しなければならないアーキテクチャのより複雑な部分の 1 つでなければなりません。

この API とその前身は、開発者がアプリケーション内でカメラと対話する機能を活用できるように配置されました。

デバイスのマイクや音量を操作する方法と同様に、Camera2 API はデバイスのカメラを操作するためのツールを提供します。

一般に、Camera2 API を使用する場合は、写真を撮ったりビデオを録画したりするだけではありません。これは、API を使用すると、特定のデバイスごとに構成する必要があるさまざまなクラスを公開することで、カメラを詳細に制御できるためです。

以前にカメラを扱ったことがある場合でも、以前のカメラ API からの大幅な変更であるため、知っていることをすべて忘れてしまう可能性があります。

この API を直接使用する方法を紹介しようとするリソースはたくさんありますが、それらの一部は古くなっている可能性があり、一部は全体像を示していない可能性があります。

そのため、不足している部分を自分で埋めようとする代わりに、この記事が (うまくいけば) Camera2 API を操作するためのワンストップ ショップになるでしょう。

Camera2 の使用例

何かに飛び込む前に、カメラを使用して写真を撮ったりビデオを録画したりするだけの場合は、Camera2 API を気にする必要がないことを理解することが重要です。

Camera2 API を使用する主な理由は、アプリケーションがカメラまたはその機能との何らかのカスタム インタラクションを必要とする場合です。

後者ではなく前者に関心がある場合は、Google の次のドキュメントにアクセスすることをお勧めします:

<オール>
  • 写真を撮る
  • ビデオをキャプチャ
  • そこには、カメラで素晴らしい写真やビデオを撮影するために必要なすべての手順が記載されています。ただし、この記事では、主に Camera2 の使用方法に焦点を当てます。

    ここで、マニフェスト ファイルに追加する必要があるものがいくつかあります。

    カメラの許可:

    <uses-permission android:name="android.permission.CAMERA" />

    カメラ機能:

    <uses-feature android:name="android.hardware.camera" />

    カメラの許可が与えられているかどうかを確認する必要がありますが、このトピックは広く取り上げられているため、この記事では扱いません。

    Camera2 API コンポーネントの設定方法

    Camera2 API には、いくつかの新しいインターフェイスとクラスが導入されています。使用方法をよりよく理解できるように、それぞれを分解してみましょう。

    Android Camera2 – Camera2 API を使用して写真やビデオを撮影する方法
    これらすべてのコンポーネントを見てください

    まず、TextureView から始めます。

    Camera2 TextureView コンポーネント

    TextureView は、コンテンツ ストリーム (ビデオを考えてください) を表示するために使用する UI コンポーネントです。プレビューであろうと、写真/ビデオを撮る前であろうと、TextureView を使用してカメラからのフィードを表示する必要があります。

    TextureView に関して使用する重要な 2 つのプロパティは次のとおりです。

    • SurfaceTexture フィールド
    • SurfaceTextureListener インターフェース

    1 つ目はコンテンツが表示される場所で、2 つ目は 4 つのコールバックがあります:

    <オール>
  • onSurfaceTextureAvailable
  • onSurfaceTextureSizeChanged
  • onSurfaceTextureUpdated
  • onSurfaceTextureDestroyed
  • private val surfaceTextureListener = object : TextureView.SurfaceTextureListener {
            override fun onSurfaceTextureAvailable(texture: SurfaceTexture, width: Int, height: Int) {
    
            }
            override fun onSurfaceTextureSizeChanged(texture: SurfaceTexture, width: Int, height: Int) {
            
            }
            
            override fun onSurfaceTextureDestroyed(texture: SurfaceTexture) {
               
            }
            override fun onSurfaceTextureUpdated(texture: SurfaceTexture) {
              
            }
    }

    カメラを使用する場合、最初のコールバックは非常に重要です。これは、SurfaceTexture が利用可能になったときに通知を受けて、フィードの表示を開始できるようにするためです。

    TextureView がウィンドウにアタッチされて初めて使用可能になることに注意してください。

    カメラとの対話は、以前の API から変更されました。これで、CameraManager ができました。これは、CameraDevice オブジェクトと対話できるようにするシステム サービスです。

    細心の注意を払いたい方法は次のとおりです。

    • オープンカメラ
    • getCameraCharacteristics
    • getCameraIdList

    TextureView が利用可能で準備ができていることを確認したら、openCamera を呼び出してカメラへの接続を開く必要があります。このメソッドは 3 つの引数を取ります:

    <オール>
  • CameraId - 文字列
  • CameraDevice.StateCallback
  • ハンドラー
  • CameraId 引数は、接続するカメラを示します。お使いの携帯電話には、主に前面と背面の 2 つのカメラがあります。それぞれに固有の ID があります。通常、これは 0 または 1 です。

    カメラ ID を取得するにはどうすればよいですか? CameraManager の getCamerasIdList メソッドを使用します。デバイスから識別されたすべてのカメラ ID の文字列型の配列を返します。

    val cameraManager: CameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
    val cameraIds: Array<String> = cameraManager.cameraIdList
    var cameraId: String = ""
    for (id in cameraIds) {
        val cameraCharacteristics = cameraManager.getCameraCharacteristics(id)
        //If we want to choose the rear facing camera instead of the front facing one
        if (cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) 
          continue
        }
        
        val previewSize = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!.getOutputSizes(ImageFormat.JPEG).maxByOrNull { it.height * it.width }!!
        val imageReader = ImageReader.newInstance(previewSize.width, previewSize.height, ImageFormat.JPEG, 1)
        imageReader.setOnImageAvailableListener(onImageAvailableListener, backgroundHandler)
        cameraId = id
    }

    次の引数は、カメラを開こうとした後のカメラ状態へのコールバックです。考えてみれば、この行動にはいくつかの結果しかありません:

    • カメラが正常に開きました
    • カメラが切断されました
    • 何らかのエラーが発生しました

    それが CameraDevice.StateCallback の中にあります:

     private val cameraStateCallback = object : CameraDevice.StateCallback() {
            override fun onOpened(camera: CameraDevice) {
               
            }
    
            override fun onDisconnected(cameraDevice: CameraDevice) {
               
            }
    
            override fun onError(cameraDevice: CameraDevice, error: Int) {
                val errorMsg = when(error) {
                    ERROR_CAMERA_DEVICE -> "Fatal (device)"
                    ERROR_CAMERA_DISABLED -> "Device policy"
                    ERROR_CAMERA_IN_USE -> "Camera in use"
                    ERROR_CAMERA_SERVICE -> "Fatal (service)"
                    ERROR_MAX_CAMERAS_IN_USE -> "Maximum cameras in use"
                    else -> "Unknown"
                }
                Log.e(TAG, "Error when trying to connect camera $errorMsg")
            }
        }

    3 番目の引数は、この作業がどこで行われるかを扱います。メイン スレッドを占有したくないため、この作業はバックグラウンドで実行することをお勧めします。

    そのため、Handler を渡す必要があります。このハンドラー インスタンスを選択したスレッドでインスタンス化して、それに作業を委任できるようにするのが賢明です。

    private lateinit var backgroundHandlerThread: HandlerThread
    private lateinit var backgroundHandler: Handler
    
     private fun startBackgroundThread() {
        backgroundHandlerThread = HandlerThread("CameraVideoThread")
        backgroundHandlerThread.start()
        backgroundHandler = Handler(
            backgroundHandlerThread.looper)
    }
    
    private fun stopBackgroundThread() {
        backgroundHandlerThread.quitSafely()
        backgroundHandlerThread.join()
    }

    これまでのすべての作業で、openCamera を呼び出すことができます:

    cameraManager.openCamera(cameraId, cameraStateCallback,backgroundHandler)

    次に、onOpened コールバックを使用して、TextureView を介してカメラ フィードをユーザーに表示する方法に関するロジックの処理を開始できます。

    Android Camera2 – Camera2 API を使用して写真やビデオを撮影する方法
    Unsplash の Markus Spiske による写真

    フィードのプレビューを表示する方法

    フィードを表示するためのカメラ (cameraDevice) と TextureView があります。ただし、フィードのプレビューを表示できるように、それらを相互に接続する必要があります。

    そのために、TextureView の SurfaceTexture プロパティを使用し、CaptureRequest を構築します。

    val surfaceTexture : SurfaceTexture? = textureView.surfaceTexture // 1
    
    val cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId) //2
    val previewSize = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!
      .getOutputSizes(ImageFormat.JPEG).maxByOrNull { it.height * it.width }!!
    
    surfaceTexture?.setDefaultBufferSize(previewSize.width, previewSize.height) //3
    
    val previewSurface: Surface = Surface(surfaceTexture)
    
    captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) //4
    captureRequestBuilder.addTarget(previewSurface) //5
    
    cameraDevice.createCaptureSession(listOf(previewSurface, imageReader.surface), captureStateCallback, null) //6
    プレビューの作成

    上記のコードでは、まず TextureView から surfaceTexture を取得します。次に、cameraCharacteristics オブジェクトを使用して、すべての出力サイズのリストを取得します。目的のサイズを取得するために、それを surfaceTexture に設定します。

    次に、TEMPLATE_PREVIEW を渡す captureRequest を作成します。 .入力サーフェスを captureRequest に追加します。

    最後に、入力サーフェスと出力サーフェスである captureStateCallback を使用して captureSession を開始し、ハンドラに null を渡します

    では、この captureStateCallback とは何でしょう?この記事の最初の図を覚えていれば、これは開始しようとしている CameraCaptureSession の一部です。このオブジェクトは、次のコールバックで captureRequest の進行状況を追跡します:

    • onConfigured
    • onConfigureFailed
    private val captureStateCallback = object : CameraCaptureSession.StateCallback() {
            override fun onConfigureFailed(session: CameraCaptureSession) {
                
            }
            override fun onConfigured(session: CameraCaptureSession) {
             
            }
    }

    cameraCaptureSession が正常に構成されたら、セッションの繰り返し要求を設定して、プレビューを継続的に表示できるようにします。

    そのために、コールバックで取得したセッション オブジェクトを使用します:

     session.setRepeatingRequest(captureRequestBuilder.build(), null, backgroundHandler)

    このメソッドの最初の引数として以前に作成した captureRequestBuilder オブジェクトに気付くでしょう。 build メソッドを実行して、渡される最後のパラメーターが CaptureRequest になるようにします。

    2 番目の引数は CameraCaptureSession.captureCallback リスナーですが、キャプチャした画像に対して何もしたくないため (これはプレビューであるため)、null を渡します。

    3 番目の引数はハンドラーで、ここでは独自の backgroundHandler を使用します。これは、繰り返し要求がバックグラウンド スレッドで実行されるため、前のセクションで null を渡した理由でもあります。

    Android Camera2 – Camera2 API を使用して写真やビデオを撮影する方法
    Unsplash の Dicky Jiang による写真

    写真の撮り方

    カメラのライブプレビューは素晴らしいですが、ほとんどのユーザーはおそらくそれを使って何かをしたいと思うでしょう.写真を撮るために書くロジックの一部は、前のセクションで行ったものと似ています。

    <オール>
  • captureRequest を作成します
  • ImageReader とそのリスナーを使用して、撮影した写真を収集します
  • cameraCaptureSession を使用して、キャプチャ メソッドを呼び出します
  • val orientations : SparseIntArray = SparseIntArray(4).apply {
        append(Surface.ROTATION_0, 0)
        append(Surface.ROTATION_90, 90)
        append(Surface.ROTATION_180, 180)
        append(Surface.ROTATION_270, 270)
    }
    
    val captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
    captureRequestBuilder.addTarget(imageReader.surface)
    
    val rotation = windowManager.defaultDisplay.rotation
    captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, orientations.get(rotation))
    cameraCaptureSession.capture(captureRequestBuilder.build(), captureCallback, null)
    今回は、TEMPLATE_STILL_CAPTURE を使用してキャプチャ リクエストを作成します

    しかし、この ImageReader は何ですか? ImageReader は、サーフェスにレンダリングされた画像データへのアクセスを提供します。私たちの場合、それは TextureView の表面です。

    前のセクションのコード スニペットを見ると、既に ImageReader が定義されていることがわかります。

    val cameraManager: CameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
    val cameraIds: Array<String> = cameraManager.cameraIdList
    var cameraId: String = ""
    for (id in cameraIds) {
        val cameraCharacteristics = cameraManager.getCameraCharacteristics(id)
        //If we want to choose the rear facing camera instead of the front facing one
        if (cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) 
          continue
        }
        
        val previewSize = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!.getOutputSizes(ImageFormat.JPEG).maxByOrNull { it.height * it.width }!!
        val imageReader = ImageReader.newInstance(previewSize.width, previewSize.height, ImageFormat.JPEG, 1)
        imageReader.setOnImageAvailableListener(onImageAvailableListener, backgroundHandler)
        cameraId = id
    }
    通知行 12 ~ 14

    上記のように、幅と高さ、画像に使用する画像形式、キャプチャできる画像の数を渡して、ImageReader をインスタンス化します。

    ImageReader クラスが持つプロパティは、onImageAvailableListener というリスナーです。このリスナーは、写真が撮影されるとトリガーされます (キャプチャ リクエストの出力ソースとしてそのサーフェスを渡したため)。

    val onImageAvailableListener = object: ImageReader.OnImageAvailableListener{
            override fun onImageAvailable(reader: ImageReader) {
                val image: Image = reader.acquireLatestImage()
            }
        }

    ⚠️ 処理後に画像を閉じてください。そうしないと、別の写真を撮ることができなくなります。

    Android Camera2 – Camera2 API を使用して写真やビデオを撮影する方法
    Unsplash の Jakob Owens による写真

    ビデオを録画する方法

    ビデオを録画するには、MediaRecorder という新しいオブジェクトを操作する必要があります。メディア レコーダー オブジェクトは、オーディオとビデオの記録を担当し、まさにそのために使用します。

    何かを行う前に、メディア レコーダーをセットアップする必要があります。対処すべきさまざまな構成があり、それらは正しい順序である必要があり、さもないと例外がスローされます .

    以下は、ビデオ (音声なし) をキャプチャできる構成の選択例です。

    fun setupMediaRecorder(width: Int, height: Int) {
      val mediaRecorder: MediaRecorder = MediaRecorder()
      mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE)
      mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
      mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264)
      mediaRecorder.setVideoSize(videoSize.width, videoSize.height)
      mediaRecorder.setVideoFrameRate(30)
      mediaRecorder.setOutputFile(PATH_TO_FILE)
      mediaRecorder.setVideoEncodingBitRate(10_000_000)
      mediaRecorder.prepare()
    }
    セッターの順序は重要です

    setOutputFile に注意してください ビデオを保存するファイルへのパスを期待するメソッド。これらすべての構成を設定したら、prepare を呼び出す必要があります。

    mediaRecorder にも start メソッドがあり、呼び出す前に prepare を呼び出す必要があることに注意してください。

    mediaRecoder を設定したら、キャプチャ リクエストとキャプチャ セッションを作成する必要があります。

    fun startRecording() {
            val surfaceTexture : SurfaceTexture? = textureView.surfaceTexture
            surfaceTexture?.setDefaultBufferSize(previewSize.width, previewSize.height)
            val previewSurface: Surface = Surface(surfaceTexture)
            val recordingSurface = mediaRecorder.surface
            captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD)
            captureRequestBuilder.addTarget(previewSurface)
            captureRequestBuilder.addTarget(recordingSurface)
    
            cameraDevice.createCaptureSession(listOf(previewSurface, recordingSurface), captureStateVideoCallback, backgroundHandler)
        }

    プレビューの設定や写真の撮影と同様に、入力サーフェスと出力サーフェスを定義する必要があります。

    ここでは、TextureView の surfaceTexture から Surface オブジェクトを作成し、メディア レコーダーからもサーフェスを取得しています。 TEMPLATE_RECORD を渡します キャプチャ リクエストを作成するときの値。

    captureStateVideoCallback は、静止写真に使用したものと同じタイプですが、onConfigured コールバック内でメディア レコーダーの開始メソッドを呼び出します。

    val captureStateVideoCallback = object : CameraCaptureSession.StateCallback() {
          override fun onConfigureFailed(session: CameraCaptureSession) {
             
          }
          
          override fun onConfigured(session: CameraCaptureSession) {
              session.setRepeatingRequest(captureRequestBuilder.build(), null, backgroundHandler)
              mediaRecorder.start()
          }
      }
    ここでは、連続したビデオをキャプチャしたいので、繰り返しリクエストも設定しています

    今、ビデオを録画していますが、録画を停止するにはどうすればよいですか?そのために、mediaRecorder オブジェクトで stop メソッドと reset メソッドを使用します:

    mediaRecorder.stop()
    mediaRecorder.reset()

    結論

    それは処理するのが大変でした。ここにたどり着いた方、おめでとうございます!これを回避する方法はありません。コードに慣れることによってのみ、すべてがどのように接続されているかを理解し始めることができます。

    以下のこの記事で紹介されているすべてのコードを確認することをお勧めします:

    MediumArticles/Camrea2API at master · TomerPacific/MediumArticles私が書いたさまざまな Medium 記事に関連するコードを含むリポジトリ - MediumArticles/Camrea2API at master · TomerPacific/MediumArticles Android Camera2 – Camera2 API を使用して写真やビデオを撮影する方法 TomerPacificGitHub Android Camera2 – Camera2 API を使用して写真やビデオを撮影する方法

    Camera2 API に関して言えば、これは氷山の一角にすぎないことに注意してください。スロー モーション ビデオのキャプチャ、前面カメラと背面カメラの切り替え、フォーカスの制御など、他にも多くのことができます。


    1. iPhone で写真とビデオを非表示にする方法

      プライベートな写真を iPhone に安全に保管することは必須です。特に、iPhone で写真をクリックしないことはほぼ避けられないことがわかっている場合はなおさらです。問題は、iPhone 上の写真を隠して、覗き見されないようにする方法です。他の人に自分の写真のグループを見せている場合があるため、個人的な写真もスクロールしてしまう可能性があります。 心配しないで;解決策があります! したがって、この投稿では、iPhoneで写真をロックする方法について説明します。私たちは、iPhone の隠しアルバムを認識しています。これにより、できるだけ多くの写真やビデオを簡単に見ることができます。これ

    2. デジタル保管庫で秘密のカメラを使用して写真を非表示にする方法

      まったく新しい Android スマートフォンにはデジタル カメラが搭載されており、私たちのほとんどに隠れた写真家を発見するのに役立ちました。ただし、Android に欠けているのは、詮索好きな目から写真を隠すために使用できる秘密のデジタル保管庫サービスです。この投稿では、秘密の保管庫で写真を直接クリックする秘密のカメラ オプションと共に、Android スマートフォンで写真を非表示にするための最高のアプリの 1 つとして素晴らしいアプリについて説明します。 Google Play ストアで入手でき、Systweak Software によって開発された Android アプリ、Keep Ph