#AVFoundationで動画作成中にプログレスバーを表示する方法
動画編集について記事を記載していますが、作成中のプログレスバーを表示する方法について説明します。
AVAssetExportSession.progressで簡単に取得できます。例としてはこんな感じです。
class SampleController: UIViewController {
var assetExport: AVAssetExportSession!
let timeInterval = 0.1 // プログレスバーの表示間隔 (0.1秒)
@IBOutlet weak var progressBar: UIProgressView! // storyboardで作成
override func viewDidLoad() {
super.viewDidLoad()
// タイマーをセット
Timer.scheduledTimer(
timeInterval: timeInterval,
target: self,
selector: #selector(self.step),
userInfo: nil,
repeats: true
)
// 動画作成のメイン関数
self.makeMovie()
}
//タイマー関数
@objc func step() {
//プログレス表示
if assetExport != nil { // タイミングによってはnilの場合もあり
//動画作成処理中
progressBar.setProgress(assetExport.progress, animated: true)
}
else {
// 動画作成前
}
}
// 動画作成の関数
func makeMovie() {
//
// 色々省略
//
// 動画のコンポジションをベースにAVAssetExportを生成
assetExport = AVAssetExportSession.init(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)
// 合成用コンポジションを設定
assetExport?.videoComposition = videoComp
// エクスポートファイルの設定
assetExport?.outputFileType = AVFileType.mov
assetExport?.outputURL = exportUrl
assetExport?.shouldOptimizeForNetworkUse = true
// エクスポート実行
assetExport?.exportAsynchronously(completionHandler: {() -> Void in
//処理省略
//このあとプログレス情報が取得可能
})
}
}
##解説
AVAssetExportSession.exportAsynchronouslyで動画の作成を開始した後にAVAssetExportSession.progressで取得できるようになります。
progressは0.0から1.0の値で取得できるので、そのままUIProgressViewに突っ込めます。
取得できる値はそこそこ正確なので、特に細工をせずそのまま使って大丈夫と思います。
動画作成の処理時間は、作成する動画の長さと画質に比例する様子で、それ以外のアニメーション処理などによる処理時間の変動はほとんどない様子。(全てのパターンを試した訳ではないですが、今のところそのような感覚です)
動画の前処理が長い場合は少し工夫が必要です。
#動画の前処理が長い場合
ちょっと工夫してこんな感じです。
class SampleController: UIViewController {
var assetExport: AVAssetExportSession!
let timeInterval = 0.1 // プログレスバーの表示間隔 (0.1秒)
let processTime : Float = 0.10 // 前処理にかかる割合(10%の場合)
var progressTimer: Float = 0.0 // 前処理中のプログレスバーの数値
@IBOutlet weak var progressBar: UIProgressView! // storyboardで作成
override func viewDidLoad() {
super.viewDidLoad()
// タイマーをセット
Timer.scheduledTimer(
timeInterval: timeInterval,
target: self,
selector: #selector(self.step),
userInfo: nil,
repeats: true
)
// 動画作成のメイン関数
// 前処理をプログレスバーに反映させるために非同期処理にする
DispatchQueue.global(qos: .default).async {
self.makeMovie()
}
}
//タイマー関数
@objc func step() {
//プログレス表示
if assetExport != nil { // タイミングによってはnilの場合もあり
//動画作成処理中
progressBar.setProgress((assetExport.progress * (1.0 - processTime)) + processTime, animated: true)
}
else {
// 動画作成前
if self.progressTimer < processTime {
self.progressTimer = self.progressTimer + 0.01 // (数値はちょうどよくなるように調整)
progressBar.setProgress(self.progressTimer, animated: true)
}
}
}
// 動画作成の関数
func makeMovie() {
// ここは変更なし
}
}
##解説
まずはDispatchQueue.global(qos: .default).async
で時間がかかる部分を非同期処理に。
assetExportがnilかどうかで前処理中かどうかが分かるので、nilの場合のプログレスバーの計算をしています。
let processTime : Float = 0.10
は動画編集にかかる前処理の割合です。何度か実機で試してみて、この数値は出せば大丈夫。ただし、動画作成の長さや画質が動的に変えられるアプリの場合は、この比率が動的に変わるので、その計算もしないといけなくなるはずです。
いくつかの機種で試したところ、処理時間はCPU性能にのみ比例するようなので、どれか一つの機種で試せばOKと思われます。
self.progressTimer = self.progressTimer + 0.01
でインクリメントしているのですが、この数値はタイマーの更新間隔や実際に処理が終了する時間によって調整が必要です。一番処理がかかる機種をベースにして計算しておけば安心です。