QA@IT

swift4.2 AVFoundationフレームワークを利用して、動画ファイルを入力する

526 PV

swift4.2 (iOS 12 + Xcode 10)でアプリを制作しようとしています。

次のようにして、カメラからビデオ画像を入力して、最終的に1フレームごとに画像処理を行うことを目標にしております。

var session = AVCaptureSession()
let device = AVcaptureDevice.default(AVCaptureDevice.DeviceType.builtInWideAngleCamera, for: AVMediaType.video, position: AVCaptureDevice.Position.back)
do{
  let input = try AVCaptureDeviceInput(device: device!)
  if session.canAddInput(input){
    session.addInput(input)
  }else{ 
    return false 
  }
}catch let error{
  return false
}

... この後、captureOutput で画像処理を行う...

ここで、カメラからではなく、すでに準備されている動画ファイルを読み込むようにするにはどうすればよいのでしょうか?

回答

結局、ある動画ファイルを読み込んで、1フレームごとに何らかの画像処理をした後にUIImageView
にその都度再描画するというコードが以下のようになってきました。
しかし、この方法ですと、処理がだんだん重くなってきて、1フレームを処理するのに1秒以上かかって
しまうようになってしまいます。
どなたか、良い解決方法をご存知の方はおられないでしょうか?


var count = 0

@IBOutlet weak var view: UIImageView!

@IBAction func startShow(_ sender: Any ){

  Timer.scheduledTimer( timeInterval: 0.01, target: self, selector: #selector( self.step ), userInfo: nil, repeats: true ) 

}

@objc func step(){

  showMovie()
  self.view.setNeedsLayout()
  count += 1

}

func showMovie() -> Bool{

  if let path = Bundle.main.path( forResource: "sample", ofType: "mp4" ){
    let fileURL = URL( fileURLWithPath: path )
    let asset = AVAsset( url: fileURL )
    let reader = try! AVAssetReader( asset: asset )
    let videoTrack = asset.tracks(withMediaType: AVMediaType.video )[0]
    let trackReaderOutput = AVAssetReaderTrackOutput( track: videoTrack, outputSettings: [string(kCVPixelBufferPixelFormatTypeKey) : NSNumber( value: kCVPixelFormatType_32BGRA)])
reader.add( trackReaderOutput )
    reader.startReading()

    var num = 0
    while let sampleBuffer = trackReaderOutput.copyNextSampleBuffer(){

      if num == count {
        let image = self.cm2UIImage( sampleBuffer )

       //この場所で OpneCV を使って任意の画像処理を行う(今はまだ非実装)

        self.view.image = image
        break
      }
      num += 1
    } 
  }
}
編集 履歴 (0)
  • 毎回、ファイルを開いて前回読みこんだフレームまで移動するというのが明らかに無駄っぽいのですが、その無駄を省くようにコードに反映させるというのが swift 素人なためできない状態です...気が狂いそうです... -

AVAsset を使えば良いらしいことが分かってきました。
ファイルのURLを fileURL に入れて、
CMSampleBuffer を UIImage に変換する関数を cm2UIImage() とします。
最後の行の view は、@IBOutlet weak var view: UIImageView! です。

let asset = AVAsset( url: fileURL )
let reader = try! AVAssetReader( asset: asset )
let videoTrack = asset.tracks(withMediaType: AVMediaType.video )[0]
let trackReaderOutput = AVAssetReaderTrackOutput( track: videoTrack, outputSettings: [string(kCVPixelBufferPixelFormatTypeKey) : NSNumber( value: kCVPixelFormatType_32BGRA)])
reader.add( trackReaderOutput )
reader.startReading()
while let sampleBuffer = trackReaderOutput.copyNextSampleBuffer(){

  let image = self.cm2UIImage( sampleBuffer )

 //この場所で OpneCV を使って任意の画像処理を行う(今はまだ非実装)

  self.view.image = image
}

しかし、問題は画像処理をした後の UIImage が UIImageView に全く表示されず、while終了後の最後のフレームだけが表示されます。

編集 履歴 (0)
  • そもそも、自分はUIImageView の描画のタイミングを理解できていないということが分かってきました。self.view.image = image としても直ちに描画されるわけではないのですね。どうすればよいのだろうか?
    self.view.display()を使う? ...どなたか御教示をお願いいたします。
    -
  • timer を使って次の回答のようにしてみました。
    しかし、この方法だと、だんだん処理が重くなってきて、1フレームを表示するのに1秒以上かかるようになってしまいます...
    根本を理解していないので、なぜ処理が重くなるかもわかりません。
    どなたか解決方法がわかる方はおられないでしょうか?
    -
ウォッチ

この質問への回答やコメントをメールでお知らせします。