Custom trình duyệt video sử dụng AVPlayer-AVFoundation
Hiện nay có rất nhiều ứng dụng iOS có chức năng quay video và play video. iOS đã cung cấp các thư viện để chúng ta có thể dễ dàng play video, đơn giản nhất mà chúng ta hay sử dụng đó là MPMoviePlayerController. Tuy nhiên, từ iOS 9 trở đi thì đã không khuyến cáo sử dụng class này nữa, thay vào đó là ...
Hiện nay có rất nhiều ứng dụng iOS có chức năng quay video và play video. iOS đã cung cấp các thư viện để chúng ta có thể dễ dàng play video, đơn giản nhất mà chúng ta hay sử dụng đó là MPMoviePlayerController. Tuy nhiên, từ iOS 9 trở đi thì đã không khuyến cáo sử dụng class này nữa, thay vào đó là AVPictureInPictureController hoặc AVPictureInPictureController của AVKit framework, nhưng nếu muốn can thiệp sâu hơn vào video, ví dụ như play một danh sách video, chỉnh sửa layout hiển thị,... thì chúng ta phải sử dụng tới AVPlayer (AVFoundation framework). AVPlayer rất linh hoạt, tuy nhiên tài liệu thì lại rất hạn chế. Nó giúp bạn sử dụng rất nhiều Key-Value Observing (KVO) để kiểm tra các trạng thái và dễ dàng xây dựng một player theo cách của bạn. Hôm nay tôi sẽ hướng dẫn tự xây dựng 1 trình duyệt video đơn giản.
1 Tạo project mới
2. Tạo 1 cocoa touch class mới đặt tên là FRVideoController
- Tạo một control view gồm: slider, các playback cần thiết như play/pause, next, previous,...
Chú ý cần phải thiết lập App transport security settings trong info.plist
3. Hàm khởi tạo FRVideoController
init(withListVideo list: [String], playAtIndex index: Int = 0) { super.init(nibName: kControllerNibName, bundle: nil) listVideoUrl = list currentVideoIndex = index }
Dữ liệu khởi tạo sẽ là một mảng các đường dẫn video listVideoUrl, ngoài ra có cung cấp biến playAtIndex để chỉ định khi khởi tạo sẽ chạy video nào đầu tiên, mặc định sẽ là video ở vị trí 0
4. Bắt đầu video player
- Khởi tạo 1 video player với AVPlayerLayer
let currentPlayerItem = AVPlayerItem(URL: NSURL(string: listVideoUrl[currentVideoIndex])!) self.player = AVPlayer(playerItem: currentPlayerItem) self.playerLayer = AVPlayerLayer(player: self.player) self.playerLayer!.videoGravity = AVLayerVideoGravityResizeAspect //AVLayerVideoGravityResizeAspectFill self.view.layer.addSublayer(self.playerLayer!) self.playerLayer!.frame = CGRectMake(0, 0, kWidthScreen, kHeightScreen) //create control controlView.frame = CGRectMake(0, 0, kWidthScreen, kHeightScreen) self.view.addSubview(controlView)
Mỗi video sẽ tương ứng với một AVPlayerItem, sử dụng AVPlayerLayer để tạo 1 layer play video, và add control view vào controller view chính. Chú ý video có 2 mode hiển thị hay sử dụng là AVLayerVideoGravityResizeAspect và AVLayerVideoGravityResizeAspectFill
- startObservers là hàm quan trọng nhất trong project này, sử dụng để tính toán thời gian chạy của video (seeking)
func startObservers() { if (timeObserver == nil) { weak var wSelf = self timeObserver = player?.addPeriodicTimeObserverForInterval(CMTimeMake(1, 100), queue: dispatch_get_main_queue(), usingBlock: { (time: CMTime) -> Void in let currentItem = wSelf!.player?.currentItem! let endTime = CMTimeConvertScale(currentItem!.asset.duration, time.timescale, CMTimeRoundingMethod.RoundHalfAwayFromZero) let currentTimeF: Float = (Float)(CMTimeGetSeconds(time)) let endTimeF: Float = (Float)(CMTimeGetSeconds(endTime)) if (!isnan(currentTimeF) && !isinf(currentTimeF) && !isnan(endTimeF) && !isinf(endTimeF)) { wSelf?.currentEndTime = endTime wSelf!.updateTime(currentTimeF, endTime: endTimeF) } }) } player?.currentItem!.addObserver(self, forKeyPath: kPlayerStatus, options: NSKeyValueObservingOptions.New, context: nil) }
Khi đã add 1 Observing thì phải stop Observing khi không sử dụng đến nữa.
- Hàm kiểm tra thay đổi trạng thái của video: sẵn sàng để chạy, lỗi, ...
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>){ if keyPath! == kPlayerStatus { let status: AVPlayerStatus = self.player!.status switch (status) { case AVPlayerStatus.ReadyToPlay: self.loadingView.hidden = true self.loadingView.stopAnimating() self.playVideo() break case AVPlayerStatus.Unknown, AVPlayerStatus.Failed: break } } }
- Mỗi lần thay đổi video khác thì chỉ cần replace AVPlayerItem
let currentPlayerItem = AVPlayerItem(URL: NSURL(string: listVideoUrl[index])!) player?.replaceCurrentItemWithPlayerItem(currentPlayerItem) player?.currentItem!.addObserver(self, forKeyPath: kPlayerStatus, options: NSKeyValueObservingOptions.New, context: nil)
5. Gọi player từ viewcontroller chính
@IBAction func playVideoTouched() { var listVideoUrl: [String] = [] listVideoUrl.append("http://clips.vorwaerts-gmbh.de/VfE_html5.mp4") listVideoUrl.append("http://clips.vorwaerts-gmbh.de/VfE_html5.mp4") let videoController = FRVideoController(withListVideo: listVideoUrl) self.presentViewController(videoController, animated: true) { () -> Void in } }
Đây là kết quả của chúng ta
Tôi đã tóm tắt các hàm cơ bản như trên, ngoài ra các bạn có thể tham khảo code tại địa chỉ này https://github.com/phanthanhhai/FRVideoPlayer.git