NRPhotoCapture, NRVideoCapture を読み解いてみる

概要

RGBCamera-CaptureRGBCamera-Record を理解すると NRPhotoCapture クラスと NRVideoCapture クラスが主要クラスだということが分かります。 NRPhotoCapture, NRVideoCapture の両クラスの動作を理解することで RGBCamera への理解が深まると考え、これらのソースコードを解読してみます。 個人的な読解結果であるため誤りが含まれている可能性があることをご了承ください。

目次

確認環境

  • NRSDK 1.2.1

解説

拾い読み

例のごとく、NRPhotoCapture を拾い読みすると、以下が要点になるのかなと思います。

  • NRPhotoCapture
    • CreateAsync
      • NRPhotoCapture インスタンスを生成する
      • FrameCaptureContext インスタンスを生成する
        • FrameCaptureContext 生成時に AbstractFrameProvider を渡す
          • Unity Editor では EditorFrameProvider
          • それ以外では RGBCameraFrameProvider
    • StartPhotoModeAsync
      • FrameCaptureContext.StartCaptureMode(CameraParameters)
        • CameraParameters の camMode は PhotoMode に設定される
        • CaptureBehaviourBase インスタンスを生成する
          • camMode が PhotoMode の場合は NRCaptureBehaviour (Record/Prefabs/NRCaptureBehaviour)
        • IEncoder インスタンスを生成する
          • camMode が PhotoMode の場合は ImageEncoder
          • Config(CameraParameters) を行う
        • FrameBlender インスタンスを生成する
          • CaptureBehaviourBase.CaptureCamera, IEncoder, CameraParameters で初期化する
        • FrameProvider.OnUpdate に CaptureBehaviourBase.OnFrame を登録する
      • FrameCaptureContext.StartCapture()
        • IEncoder.Start()
        • FrameProvider.Play()
    • TakePhotoAsync
      • NRCaptureBehaviour.DoAsyn
        • CaptureTask インスタンスを生成して、CameraParameters を設定する
        • SystemInfo.supportsAsyncGPUReadback が true の場合、ImageEncoder.Commit(CaptureTask)
          • 非同期実行で画像データが取得されて CaptureTask.OnReceive が呼ばれる
        • false の場合、ImageEncoder.Encode(CaptureTask)
          • RenderTexture を使用して Texture2D から画像データが取得されて CaptureTask.OnReceive が呼ばれる
        • OnReceive では PhotoCaptureFrame インスタンスが生成される
    • StopPhotoModeAsync
      • FrameCaptureContext.StopCaptureMode()
        • FrameCaptureContext.Release() を呼び出す
    • Dispose
      • FrameCaptureContext.Release()
        • FrameProvider.OnUpdate から CaptureBehaviourBase.OnFrame を削除する
        • FrameProvider.Release()
        • FrameBlender.Dispose()
        • IEncoder.Release()
        • Destroy(CaptureBehaviourBase)
      • FrameCaptureContext を破棄する
  • NRCaptureBehaviour
    • ImageEncoder の Commit, Encode により画像データを取得する
    • OnFrame では
      • NRFrame.GetHeadPoseByTime に成功した場合に RGBCameraRig の localPosition, localRotation を更新する
      • FrameBlender.OnFrame を呼び出す
  • ImageEncoder
    • 画像データを取得する
  • FrameBlender
    • OnFrame で BlendMode (RGBOnly/VirtualOnly/Blend/WidescreenBlend) に応じて Blit を行い画像データを作る
      • 作った画像データは ImageEncoder に渡される
  • RGBCameraFrameProvider
    • NRRGBCamTexture を使う
      • Play, Pause, Stop で NRRGBCamTexture の Play, Pause, Stop を呼び出す
      • NRRGBCamTexture.OnUpdate に AbstractFrameProvider.OnUpdate を登録する
  • EditorFrameProvider
    • AbstractFrameProvider.OnUpdate に毎フレーム RGBTextureFrame を渡す
      • RGBTextureFrame.texture に Texture2D (Record/Textures/captureDefault) を設定する

同様に、NRVideoCapture を拾い読みすると、以下が要点になるのかなと思います。

  • NRVideoCapture
    • CreateAsync
    • StartVideoModeAsync
      • FrameCaptureContext.StartCaptureMode(CameraParameters)
        • CameraParameters の camMode は VideoMode に設定される
        • CameraParameters の hologramOpacity は 1 に設定される
        • 他は NRPhotoCapture と同様だが、camMode が異なるため以下が異なる
          • NRCaptureBehaviour は NRRecordBehaviour (Record/Prefabs/NRRecorderBehaviour) になる
          • IEncoder は VideoEncoder になる
    • StartRecordingAsync
      • IsRecording が true の場合は UnknownError とする (Unknown なんだ...)
      • NRRecordBehaviour.SetOutPutPath を行う
      • FrameCaptureContext.StartCapture()
        • NRPhotoCapture と同様
      • IsRecording を true にする
    • StopRecordingAsync
      • IsRecording が false の場合は UnknownError とする (Unknown なんだ...)
      • FrameCaptureContext.StopCapture()
        • IEncoder.Stop()
        • FrameProvider.Stop()
      • IsRecording を false にする
    • StopVideoModeAsync
      • NRPhotoCapture と同様
    • Dispose
      • NRPhotoCapture と同様

要点の整理

NRPhotoCapture, NRVideoCapture の基本的な使い方は似ています。以下の 5 つに分類して理解します。

  1. CreateAsync
  2. Start[Photo/Video]ModeAsync
  3. TakePhotoAsync or [Start/Stop]RecordingAsync
  4. Stop[Photo/Video]ModeAsync
  5. Dispose

CreateAsync

引数は showHolograms と callback の 2 つです。 showHolograms は使用されていません。 callback は処理が終了するときに呼び出されます。 非同期処理ではありません。 callback が呼び出されてから CreateAsync が終了します。

CreateAsync は NRPhotoCapture, NRVideoCapture インスタンスを生成します。 この時、FrameCaptureContext インスタンスを生成して設定します。 更に、FrameProvider (AbstractFrameProvider のサブクラス) インスタンスを生成して FrameCaptureContext インスタンスに設定します。 つまり、下記の階層構造になります。

  • NR[Photo/Video]Capture
    • FrameCaptureContext
      • FrameProvider

FrameProvider は実行環境により異なります。 Unity Editor では EditorFrameProvider、それ以外では RGBCameraFrameProvider が使われます。

Start[Photo/Video]ModeAsync

引数には CameraParameters, callback が渡されます。 Video では AudioState が追加で渡されます。 CameraParameters は諸々の場面で使用されます。 callback は処理が終了するときに呼び出されます。 非同期処理ではありません。 callback が呼び出されてから Start[Photo/Video]ModeAsync が終了します。 AudioState は使用されていません。

Start[Photo/Video]ModeAsync では PhotoMode/VideoMode で FrameCaptureContext を初期化します。 FrameCaptureContext.StartCaptureMode(CameraParameters) を呼び出し、以下に続くいくつかの処理を行います。

CameraParameters の値をいくつか上書きします。 Photo/Video に応じて、CameraParameters.camMode の値を PhotoMode/VideoMode で上書きします。 Video の時のみ、CameraParameters.hologramOpacity を 1 に上書きします。

Behaviour (CaptureBehaviourBase のサブクラス) インスタンス、Encoder (IEncoder を実装) インスタンス、FrameBlender インスタンスを生成します。 Behaviour と Encoder は Photo/Video により生成するインスタンスが異なります。

  • Photo
    • NRCaptureBehaviour (Record/Prefabs/NRCaptureBehaviour)
    • ImageEncoder
  • Video
    • NRRecordBehaviour (Record/Prefabs/NRRecorderBehaviour)
    • VideoEncoder

FrameProvider.OnUpdate に CaptureBehaviourBase.OnFrame を登録します。 FrameProvider.OnUpdate が呼び出されたときに Behaviour の OnFrame が呼び出される様になります。

終了時点では、階層構造は下記に更新されています。

  • NR[Photo/Video]Capture
    • FrameCaptureContext
      • FrameProvider
      • Behaviour
      • Encoder
      • FrameBlender

Photo の時のみ、FrameCaptureContext.StartCaptureMode() に続いて FrameCaptureContext.StartCapture() を呼び出します。 FrameCaptureContext.StartCapture の説明は StartRecordingAsync を参照してください。

Stop[Photo/Video]ModeAsync

引数には callback が渡されます。 callback は処理が終了するときに呼び出されます。 非同期処理ではありません。 callback が呼び出されてから Stop[Photo/Video]ModeAsync が終了します。

Stop[Photo/Video]ModeAsync では FrameCaptureContext を無効化します。 FrameCaptureContext.StopCaptureMode() を呼び出すことで行われます。

StopCaptureMode では FrameCaptureContext.Release() を呼び出します。 Release では以下を無効化します。

  • FrameProvider
  • Behaviour
  • Encoder
  • FrameBlender

また、FrameProvider.OnUpdate から CaptureBehaviourBase.OnFrame を削除します。

(Photo の時は FrameCaptureContext.StopCapture() を呼び出した方が良いと思うけどやっていない。)

Dispose

引数はありません。他とは違い Async が付かないため明確に同期呼び出しでの使用を想定していると思われます。

Dispose では FrameCaptureContext.Release() を呼び出した後に、FrameCaptureContext インスタンスを破棄します。 Release の処理内容は Stop[Photo/Video]ModeAsync を参照してください。

撮影/録画

StartRecordingAsync

引数には filename と callback が渡されます。 callback は処理が終了するときに呼び出されます。 非同期処理ではありません。 callback が呼び出されてから StartRecordingAsync が終了します。 録画中に呼び出したり、処理中に例外が発生すると callback に UnknownError が渡されます。

StartRecordingAsync では FrameCaptureContext.StartCapture() を呼び出して、録画を開始します。 StartCapture では、Encoder を開始(Start)し、FrameProvider を再生状態に(Play)します。

StartCapture の呼び出し前には NRRecordBehaviour に filename を設定します。 NRRecordBehaviour では設定された filename を VideoEncoder.EncodeConfig に設定します。

StopRecordingAsync

引数には callback が渡されます。 callback は処理が終了するときに呼び出されます。 非同期処理ではありません。 callback が呼び出されてから StopRecordingAsync が終了します。 未録画時に呼び出したり、処理中に例外が発生すると callback に UnknownError が渡されます。

StopRecordingAsync では FrameCaptureContext.StopCapture() を呼び出して、録画を停止します。 StopCapture では、Encoder を停止(Stop)し、FrameProvider を停止状態に(Stop)します。

TakePhotoAsync

引数のパターンが 2 種類あります。 filename と PhotoCaptureFileOutputFormat を指定するパターンと、指定しないパターンです。 いずれの場合も callback が渡されます。 filename を指定するパターンでは callback が使用されません。 filename を指定しないパターンでは画像が取得できたときに callback が呼ばれます。 SystemInfo.supportsAsyncGPUReadback が true の場合は非同期処理、false の場合は同期処理になります。

TakePhotoAsync は引数のパターンにより処理方法が異なります。

filename を指定するパターンでは、NRCaptureBehaviour.Do を呼び出します。 Do では ImageEncoder.Encode(width, height, captureFormat) を呼び出します。 Encode により画像データが取得し、ファイルに書き出します。

filename を指定しないパターンでは、NRCaptureBehaviour.DoAsyn を呼び出します。 DoAsyn では CaptureTask インスタンスを生成し、CaptureTask に応じた画像データ取得の処理を行います。 CaptureTask には OnReceive 関数を設定して、画像データの取得が完了した場合の処理を指定します。 OnReceive 関数は画像データが得られたときに PhotoCaptureFrame インスタンスを生成し、callback を呼び出します。 PhotoCaptureFrame は callback の引数として渡されます。

CaptureTask は SystemInfo.supportsAsyncGPUReadback の値により処理方法が異なります。

supportsAsyncGPUReadback が true の場合、CaptureTask をキューに積んで非同期に処理を行います。 CaptureTask は Update のタイミングで処理されます。 UnityEngine.Rendering.AsyncGPUReadbackRequest を使用して画像データを取得します。 画像データ (Texture2D) が取得されると OnReceive を呼び出します。 呼び出す際に CaptureTask.CaptureFormat に従って JPG/PNGエンコードしたデータが渡されます。

supportsAsyncGPUReadback が false の場合、ImageEncoder.Encode(width, height, captureFormat) を呼び出します。 width, height, captureFormat は CaptureTask から取得します。 Encode により画像データが取得されたら OnReceive を呼び出します。 呼び出す際に Encode で得たデータが渡されます。

ImageEncoder.Encode は filename を指定するパターンと filename を指定せずに supportsAsyncGPUReadback が false の場合に呼び出されます。 Encode では FrameBlender.OnFrame で生成された RenderTexture から得られた画像データ (Texture2D) を CaptureTask.CaptureFormat に従って JPG/PNGエンコードします。 FrameBlender.OnFrame については次に説明します。

FrameBlender

FrameBlender.OnFrame では BlendMode (RGBOnly/VirtualOnly/Blend/WidescreenBlend) に応じて Texture を Blit します。 OnFrame は引数に RGBTextureFrame を受け取ります。 Blit した結果の RenderTexture は Encoder に設定(Commit)されます。

FrameBlender.OnFrame は CaptureBehaviourBase.OnFrame から呼ばれます。 CaptureBehaviourBase.OnFrame は FrameProvider.OnUpdate に登録されるため、FrameProvider.OnUpdate が呼び出されたときに FrameBlender.OnFrame が呼ばれることになります。

FrameProvider.OnUpdate は引数に RGBTextureFrame を受け取っており、これが FrameBlender.OnFrame の引数になります。

RGBCameraFrameProvider

Unity Editor 以外で動かした場合に使われる FrameProvider です。 NRRGBCamTexture を使って RGBTextureFrame を取得します。

FrameCaptureContext の操作に応じて以下の様に NRRGBCamTexture の操作が使われます。

FrameCaptureContext RGBCameraFrameProvider NRRGBCamTexture
StartCapture Play Play
StopCapture Stop Pause
Release Release Stop

NRRGBCamTexture では NRRgbCamera.GetRGBFrame を呼び出して RGBTextureFrame を取得しています。 この RGBTextureFrame が FrameProvider.OnUpdate を呼ぶときに渡されます。

FrameProvider.OnUpdate は NRKernalUpdater.OnUpdate が呼ばれて NRRGBCamTexture.OnUpdate が呼ばれたときに呼ばれます。 OnUpdate, OnFrame の呼び出し関係を整理すると下記の様になります。

NRKernalUpdater.OnUpdate
  NRRGBCamTexture.OnUpdate
    RGBCameraFrameProvider.OnUpdate
      Behaviour.OnFrame
        FrameBlender.OnFrame
          Encoder.Commit

EditorFrameProvider

Unity Editor で動かした場合に使われる FrameProvider です。 RGBTextureFrame インスタンスを生成し、インスタンスに Texture2D (Record/Textures/captureDefault) を設定します。

コルーチンで FrameProvider.OnUpdate を呼んでいます。その際に生成した RGBTextureFrame を渡します。 但し、isPlay が true のときのみ呼ばれます。isPlay は FrameCaptureContext の操作に応じて以下の様に設定されます。

FrameCaptureContext EditorFrameProvider isPlay
StartCapture Play true
StopCapture Stop false
Release Release false