把播放视图内嵌到View,官方文档上已经有了比较好的说明,继承UIView 设置Layer层,然后把AVPlayer设置到AVPlayerLayer即可:
import UIKit
import AVFoundation
public class DOVPlayerView: UIView {
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
public var player:AVPlayer?{
get{
return playerLayer.player
}
set{
playerLayer.player = newValue
}
}
public var playerLayer:AVPlayerLayer {
return layer as! AVPlayerLayer
}
override public class var layerClass: AnyClass {
return AVPlayerLayer.self
}
}
在storyboard中绘制视图的时候,直接拖入一个UIView,然后修改为继承的playerView即可,如下图所示:
要实现单帧控制也很简单,AVPlayerItem中有一个step(stepCount: Int)的函数,stepCount为前进或者后退的帧数,设置为正整数则前进,负整数则后退:
//前后移动一帧
public func reverseOneFrame() -> CMTime{
self.getPlayerItem()?.step(byCount: 1)
return self.getCurrentTime()
}
public func forwardOneFrame() -> CMTime{
self.getPlayerItem()?.step(byCount: -1)
return self.getCurrentTime()
}
然后当直接调用AVPlayer的seek(to:CMTime)操作的时候,会发现无法seek到设置的位置上,可改为如下操作,即可正确的seek到设定的位置:
//seek
public func seekToTime(time:CMTime){
self.seek(to: time, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)
}
当素材在播放的时候,我想要实时的获取到播放的当前位置,并做对应的同步处理,这里可以设置一个定时器来实时的触发消息通知,将获取到的当前素材播放位置通知出去,操作如下:
fileprivate var timer: Timer?
extension AVPlayer : DOVPlayerProtocol{
//发送通知
public func postCurrentTime(){
let currentTime = CMTimeGetSeconds(self.currentTime())
NotificationCenter.default.post(name: PlaybackCurrentTimeChangeNotifaction, object: self, userInfo: ["currentTime": currentTime])
}
//播放控制
public func startPlay(){
if self.status == AVPlayerStatus.readyToPlay && (self.currentItem?.status)! == AVPlayerItemStatus.readyToPlay{
self.play()
//设置定时器:50ms
timer = Timer.scheduledTimer(timeInterval: 0.05, target: self, selector: #selector(self.postCurrentTime), userInfo: nil, repeats: true)
}
}
因为扩展了AVPlayer类来继承相应的协议,在扩展中不能定义存储属性,所以定义在了类外,加上fileprivate表示只能在该文件类访问。
接收通知的代码可以做如下操作:
class ViewController: UIViewController {
@IBOutlet weak var playerView: DOVPlayerView!
@IBOutlet weak var playOrPauseButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let url = Bundle.main.url(forResource:"Movie", withExtension: "m4v")
let video = AVURLAsset(url: url!)
let playerItem = AVPlayerItem(asset: video)
let player = DOVPlayer(playerItem: playerItem)
playerView.player = player
NotificationCenter.default.addObserver(self, selector: #selector(self.printNotification(notification:)), name: PlaybackCurrentTimeChangeNotifaction, object: nil)
}
func printNotification(notification:Notification){
let userInfo = notification.userInfo as! [String : AnyObject]
let time = userInfo["currentTime"] as! Double
print(time)
}
当素材播放的时候就会每隔50ms通知一次当前位置,其他对象可以接收该通知,并做相应的处理。
运行界面如下: