AVFoundationを使うと端末のカメラで撮っている映像をUIImageとして取得する事ができます。
その際にCMSampleBufferGetImageBufferというメソッドを使うんですが、これがnilを返した時の対処法です。
func captureOutput(_ output: AVCaptureOutput, didDrop sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { let buffer = CMSampleBufferGetImageBuffer(sampleBuffer) // → これがnilになる }
原因ですがメソッド名間違いでした。
didDropのメソッドではなくdidOutputを使う事で無事にUIImageを取得する事ができました。
// 間違い func captureOutput(_ output: AVCaptureOutput, didDrop sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { } // 正解 func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { }
didDropとdidOutputの違い
簡単に言うとdidDropはフレームをドロップした時に呼ばれるメソッドで、CMSampleBufferには映像データが入っていません。
そのため映像データが欲しい場合はdidOutputを使う必要があります。
フレームをドロップするとはどういうことか
フレームのドロップとは映像の時間の帳尻を合わせる為のものです。
動画は想定していた1分間辺りフレーム数と実際のフレーム数がずれる事があります。
フレーム数がずれてしまうと10分の映像だったつもりが実際には少し長くなったりしてしまったり、困った事態が起こります。
その対策として、帳尻を合わせるためにフレームをスキップしたりします。
これがフレームをドロップするということです。
詳しい挙動を調べるため、下のようにdidDropとdidOutputの呼び出しタイミングを調べてみました。
func captureOutput(_ output: AVCaptureOutput, didDrop sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { print(#function) } func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { print(#function) }
調べたところ、didDropとdidOutputは下のような呼ばれ方がしている事が分かりました。
まとめ
最初はCMSampleBufferGetImageBufferがnilを返す原因について探っていたんですが、動画のドロップフレームなど新しい事を学べて面白かったです。
この辺りはいざ不具合にぶつかった時にハマりやすいので今後も地道に勉強していこうと思います。