[Swift]骨格検出アプリ作成 備忘録　1.動画を読み込ませるようにする

# 骨格検出アプリ作成 1
動画の読み込ませが完了したので、記録を残しておきたいと思います。
（もっと良い方法があれば知りたいです。）

# できたもの
![douga_select.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3571351/38e21175-3be0-aaf0-b8b9-39650ebda5bf.gif)

Qiitaでの動画投稿不慣れで見づらく申し訳ないです。

# やったこと
動画再生用のViewを用意しました
```swift
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ボタンクリックで動作する)
```swift
// 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、再生/停止ボタンを用意しました。

# 調整項目
スライダーの設定コード
```swift
// スライダーの同期
    @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.読み込んだ動画に対して骨格検出を行う←次回

調整が必要な項目はありますが、メインの動画を読み込ませるところは達成できたかと思います。

次回は調整を行いつつ解決案が出れば追記しながら、メインパートの骨格検出やっていきたいと思います。