骨格検出アプリ作成 1
動画の読み込ませが完了したので、記録を残しておきたいと思います。
(もっと良い方法があれば知りたいです。)
できたもの
Qiitaでの動画投稿不慣れで見づらく申し訳ないです。
やったこと
動画再生用のViewを用意しました
class PlayerView: UIView {
// The player assigned to this view, if any.
var videoPlayer: AVPlayer? {
get { return playerLayer.player }
set { playerLayer.player = newValue }
}
// The layer used by the player.
var playerLayer: AVPlayerLayer {
return layer as! AVPlayerLayer
}
// Set the class of the layer for this view.
override static var layerClass: AnyClass {
return AVPlayerLayer.self
}
}
iPhone内の動画を選択するコードを用意しました(selectボタンクリックで動作する)
// selectボタンクリック時の動作設定
@IBAction func buttonClick(_ sender: Any) {
// カメラロール設定
var configuration = PHPickerConfiguration()
configuration.selectionLimit = 1 // 選択数
//configuration.filter = .images // 写真
configuration.filter = .videos // 動画
configuration.preferredAssetRepresentationMode = .current // これがないとJPEGが選択できなかった
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self
// アクセス許可ステータス
switch PHPhotoLibrary.authorizationStatus(for: .addOnly) {
// 未設定
case .notDetermined:
// アクセス許可をリクエスト
PHPhotoLibrary.requestAuthorization(for: .addOnly) {status in
switch status {
// カメラロール表示
case .authorized:
DispatchQueue.main.async { // UIの更新
self.present(picker, animated: true, completion: nil)
}
// カメラへのアクセスを拒否
default:
print("denied")
}
}
// カメラロール表示
case .authorized:
DispatchQueue.main.async { // UIの更新
self.present(picker, animated: true)
}
// カメラへのアクセスが拒否されている
case .denied:
// ダイアログを表示する
let alert = UIAlertController(title: "写真にアクセスできません", message: "設定からアクセス許可をしてください", preferredStyle: .alert)
let settings = UIAlertAction(title: "設定", style: .default, handler: { (_) -> Void in
let settingsURL = URL(string: UIApplication.openSettingsURLString)
UIApplication.shared.open(settingsURL!, options: [:], completionHandler: nil)
})
let close: UIAlertAction = UIAlertAction(title: "キャンセル", style: .cancel, handler: nil)
alert.addAction(settings)
alert.addAction(close)
self.present(alert, animated: true, completion: nil)
// 本体から制限されていて、アプリのアクセス許可を変更できない
case .restricted:
// ダイアログを表示する
let alert = UIAlertController(title: "写真にアクセスできません", message: "", preferredStyle: .alert)
let close: UIAlertAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alert.addAction(close)
self.present(alert, animated: true, completion: nil)
// デフォルトの場合はbreak
default: break
}
}
// ビデオ選択の関数
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true, completion: nil)
guard let provider = results.first?.itemProvider else {
return
}
guard let typeIdentifier = provider.registeredTypeIdentifiers.first else { return }
if provider.hasItemConformingToTypeIdentifier(typeIdentifier) {
provider.loadFileRepresentation(forTypeIdentifier: typeIdentifier){ [weak self] (url, error) in
//if let url = url as? URL {
if let url = url {
let avAsset = AVURLAsset(url: url)
// AVPlayerに再生させるアイテムを生成.
self?.playerItem = AVPlayerItem(asset: avAsset)
// AVPlayerを生成.
self?.videoPlayer = AVPlayer(playerItem: self?.playerItem)
self?.playerView.videoPlayer = self?.videoPlayer
self?.setupPlayer()
}
}
}
picker.dismiss(animated: true) // カメラロールを閉じる
}
あとはseekBar、再生/停止ボタンを用意しました。
調整項目
スライダーの設定コード
// スライダーの同期
@IBAction func onSliderValueChange(_ sender: UISlider) {
removePeriodicTimeObserver()
videoPlayer?.pause()
stopButoon.isEnabled = false
startButton.isEnabled = true
//動画の再生時間をシークバーとシンクロ-.
videoPlayer!.seek(to: CMTimeMakeWithSeconds(Float64(seekBar.value), preferredTimescale: Int32(NSEC_PER_SEC)), toleranceBefore: CMTime.zero, toleranceAfter: CMTime.zero)
}
// 再生に合わせてシークバーを更新
func addPeriodicTimeObserver(){
let timeScale = CMTimeScale(NSEC_PER_SEC)
let time = CMTime(seconds: 0.01, preferredTimescale: timeScale)
// if スライダーを操作している間は動かさないとか?
timeObserverToken = videoPlayer?.addPeriodicTimeObserver(forInterval: time, queue: nil, using: { time in
// 総再生時間を取得.
let duration = CMTimeGetSeconds((self.videoPlayer?.currentItem!.duration)!)
// 現在の時間を取得.
let time = CMTimeGetSeconds((self.videoPlayer?.currentTime())!)
// シークバーの位置を変更.
let value = Float(self.seekBar.maximumValue - self.seekBar.minimumValue) * Float(time) / Float(duration) + Float(self.seekBar.minimumValue)
self.seekBar.value = value
})
}
seekbarの設定がうまく動作していません。
エラー挙動
●スライダーを操作していくとスライダー後半で空白or動画最後のフレームが表示され続ける問題
●動画再生中にスライダーに触れるとスライダーが戻ろうとする挙動がある
原因として考えられるもの
●スライダーの最大値=動画の総再生時間をリンクさせられていない?
●スライダーに触れている設定と動画再生中にスライダーが同期しながら動く設定が噛み合っていない、片方作動時は切るようにしないといけない?
色々と悩んでいますが、未解決状態です。
今後の流れ
1.Swiftで動画を読み込ませるようにする←今回
2.読み込んだ動画に対して骨格検出を行う←次回
調整が必要な項目はありますが、メインの動画を読み込ませるところは達成できたかと思います。
次回は調整を行いつつ解決案が出れば追記しながら、メインパートの骨格検出やっていきたいと思います。