网上有很多 AVPlayer 的播放器,但对于 seekToTime,时间跳转都不是很精确,尤其是在使用定时器的情况下,本人找了半天,都是误人子弟,所以写了这篇精准定位的方法:self.player seekToTime(time, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)
- self.player seekToTime(time, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)
- 本文重点就是这句话,其他的是一些关于实现播放器的代码,大家可以不看
一直想做一个用 H5 写的播放器,实现跨平台,解决苹果不能播放 flv 的问题, 如哪位大神有经验可以指导一下 Xoxo_x@126.com
ObjectIve - c:
- 添加库文件:AVFoundation.framework包含头文件:#import简单实用: 通过avplayerItem创建
- // 加载网络视频
- NSURL * movieUrl = [NSURL URLWithString: @"http://bos.nj.bpc.baidu.com/tieba-smallvideo/11772_3c435014fb2dd9a5fd56a57cc369f6a0.mp4"];
- AVPlayerItem * playerItem = [AVPlayerItem playerItemWithURL: movieUrl];
- // 创建 AVPlayer 播放器
- AVPlayer * player = [AVPlayer playerWithPlayerItem: playerItem];
- // 将 AVPlayer 添加到 AVPlayerLayer 上
- AVPlayerLayer * playerLayer = [AVPlayerLayer playerLayerWithPlayer: player];
- // 设置播放页面大小
- playerLayer.frame = CGRectMake(10, 30, self.view.bounds.size.width - 20, 200);
- // 设置画面缩放模式
- playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
- // 在视图上添加播放器
- [self.view.layer addSublayer: playerLayer];
- // 开始播放
- [player play];
- AVPlayer本身并不能显示视频,而且它也不像MPMoviePlayerController有一个view属性。如果AVPlayer要显示必须创建一个播放器层AVPlayerLayer用于展示,播放器层继承于CALayer,有了AVPlayerLayer之添加到控制器视图的layer中即可。要使用AVPlayer首先了解一下几个常用的类:AVAsset:主要用于获取多媒体信息,是一个抽象类,不能直接使用。AVURLAsset:AVAsset的子类,可以根据一个URL路径创建一个包含媒体信息的AVURLAsset对象。AVPlayerItem:一个媒体资源管理对象,管理者视频的一些基本信息和状态,一个AVPlayerItem对应着一个视频资源。本文是swift开发的.使用可以转成Objective - C效果图
- MeidaPlayer框架中的MPMoviePlayerController类和MPMoviePlayerViewController类是iOS中视频播放的开发相关类和方法。但是界面封装的已经非常固化,不适合调整。在iOS9中,MPMoviePlayerController与MPMoviePlayerViewController类也被完全弃用,开发者使用AVPlayerViewController可以十分方便的实现视频播放的功能并在一些型号的iPad上集成画中画的功能介绍:本博客介绍如何使用AVPlayer进行播放,暂停,视频切换,循环播放,跳转到指定时间,并精准定位,包含对AVPlayer播放器的监听等添加播放器,播放视频
- /*
- 根据playerItem创建播放器player
- */
- if player == nil {
- playerItem = AVPlayerItem(URL: movieUrl) player = AVPlayer(playerItem: playerItem)
- /*
- 创建一个播放器层 AVPlayerLayer 用于展示
- */
- let playerLayer: AVPlayerLayer = AVPlayerLayer(player: player) playerLayer.frame = self.view.frame playerLayer.videoGravity = AVLayerVideoGravityResizeAspect self.view.layer.addSublayer(playerLayer)
- /*
- 设置playerLayer.videoGravity
- */
- playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
- /*
- 设置播放完成通知 Swift的方法有些特殊和oc不同#selector(SituationalEnglishVc.handleTapGesture)
- */
- NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(playerEnd), name: AVPlayerItemDidPlayToEndTimeNotification, object: playerItem) addSomeObserver() playState = true player.status let tapGesture = UITapGestureRecognizer(target: self, action: #selector(SituationalEnglishVc.handleTapGesture)) tapGesture.numberOfTapsRequired = 1 let tapView = UIView.init(frame: playerLayer.frame) avplayerView.addSubview(tapView) tapView.addGestureRecognizer(tapGesture) player.play()
- /*
- 创建定时器,用于在特定时间跳转
- */
- getCurrentTimer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: #selector(SituationalEnglishVc.getCurrentMovieTime), userInfo: nil, repeats: true) NSRunLoop.mainRunLoop().addTimer(getCurrentTimer, forMode: NSDefaultRunLoopMode)
- }
- 暂停播放player.pause()切换电影movieUrl = NSURL(string: "http://bos.nj.bpc.baidu.com/tieba-smallvideo/11772_3c435014fb2dd9a5fd56a57cc369f6a0.mp4") ! playerItem = AVPlayerItem(URL: movieUrl)
- //用这个方法就可以切换
- player.replaceCurrentItemWithPlayerItem(playerItem)备注:这是电影神灯的界面,神灯产品用户体验群:573431381大家可以下载一下,看看有什么技术可以探讨一下seekToTime精准定位关于这个seektotime,是个坑,我跳了好多次,比如我要调到10秒,结果跳到了6秒,我要调15秒,跳到了18秒,很奇怪,我以为是AVPlayer本身定位不准,后来发现这样解决是可以的我们不能用这个方法self.player.seekToTime(Time: CMTime)使用这个方法,通过设置偏差tolerance,来精确设定的时间是多少,很管用self.player.seekToTime(time, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero)还有一些冗余代码是对tableview的visibleCells进行操作的大家可以不看:如下func scrollToCurrentRow(idx: Int) {
- currentRow = idx
- if self.chineseArray.count > 0 {
- let indexPath = NSIndexPath(forRow: idx, inSection: 0) for item in sentenceTb.visibleCells {
- let cell = item as ! SentenceTableViewCell cell.EnglishLb.textColor = colorFromHex("666666") cell.ChineseLb.textColor = colorFromHex("666666")
- }
- let cell = sentenceTb.cellForRowAtIndexPath(indexPath) as ? SentenceTableViewCell
- if cell != nil {
- cell ! .EnglishLb.textColor = colorFromHex("5eecff") cell ! .ChineseLb.textColor = colorFromHex("5eecff")
- }
- switch currentRow {
- case chineseArray.count - 1 : sentenceTb.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Bottom, animated: true) break
- case chineseArray.count - 2 : sentenceTb.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Middle, animated: true) break
- default:
- sentenceTb.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Top, animated: true) break
- }
- }
- }最后附上所有代码
- //
- // SituationalEnglishVc.swift
- // eTimeMachine
- //
- // Created by fsk-0-1-n on 16/9/18.
- // Copyright © 2016年 smartit. All rights reserved.
- //
- import UIKit class SituationalEnglishVc: UIViewController,
- UITableViewDelegate,
- UITableViewDataSource {
- var movieUrl: NSURL !
- var player: AVPlayer !
- var playState: Bool !
- var playerItem: AVPlayerItem ! =nil
- var getCurrentTimer: NSTimer ! =nil
- var replayTimer: NSTimer ! =nil
- var wordStart: CGFloat !
- var replayNum: Int !
- var isCyclePlay: Bool !
- var isRePlayNow: Bool !
- var currentRow: Int = 0
- var xzlrc = XZLrc() var timeArray = [CGFloat]() var wordArray = [String]() var chineseArray = [String]()@IBOutlet weak
- var sentenceTb: UITableView ! @IBOutlet weak
- var wordView: UIView ! @IBOutlet weak
- var avplayerView: UIView ! @IBOutlet weak
- var controlView: UIView ! override func viewWillAppear(animated: Bool) {
- self.navigationController ? .navigationBarHidden = false setStatusBarDefault(false) setNavBarStyle() var barBtn = UIBarButtonItem.init(title: "<", style: UIBarButtonItemStyle.Done, target: nil, action: nil) self.navigationItem.leftBarButtonItem = barBtn
- }
- override func viewDidLoad() {
- super.viewDidLoad() movieUrl = NSURL(string: "http://bos.nj.bpc.baidu.com/tieba-smallvideo/11772_3c435014fb2dd9a5fd56a57cc369f6a0.mp4") ! self.navigationItem.title = "场景轰炸"self.hideTabBar() replayNum = 0 wordStart = 10.100000 isCyclePlay = false isRePlayNow = false getTitleNet(2) AVPlayerSetting() LoadingGif() AVPlayerControlView() setSentenceTableView() sentenceTb.separatorStyle = UITableViewCellSeparatorStyle.None
- }
- override func viewWillDisappear(animated: Bool) {
- self.player.removeObserver(self, forKeyPath: "status") if getCurrentTimer != nil {
- getCurrentTimer.invalidate() getCurrentTimer = nil
- }
- if replayTimer != nil {
- replayTimer.invalidate() replayTimer = nil
- }
- self.player.pause() if self.player != nil {
- avplayerView.removeFromSuperview() avplayerView = nil
- }
- }
- func getTitleNet(moiveId: Int) {
- //获取字幕信息
- xzlrc.getLrcWithUrl(moiveId) {
- self.timeArray = self.xzlrc.timeArray self.wordArray = self.xzlrc.wordArray self.chineseArray = self.xzlrc.chineseArray self.sentenceTb.reloadData()
- }
- }
- func AVPlayerControlView() {
- controlView.backgroundColor = colorFromHex("1b1a18", alpha: 0.6) controlView.hidden = false
- }
- func AVPlayerSetting() {
- if player == nil {
- playerItem = AVPlayerItem(URL: movieUrl) player = AVPlayer(playerItem: playerItem) let playerLayer: AVPlayerLayer = AVPlayerLayer(player: player) playerLayer.frame = avplayerView.frame playerLayer.videoGravity = AVLayerVideoGravityResizeAspect avplayerView.layer.addSublayer(playerLayer) playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(playerEnd), name: AVPlayerItemDidPlayToEndTimeNotification, object: playerItem) addSomeObserver() playState = true player.status let tapGesture = UITapGestureRecognizer(target: self, action: #selector(SituationalEnglishVc.handleTapGesture)) tapGesture.numberOfTapsRequired = 1 let tapView = UIView.init(frame: playerLayer.frame) avplayerView.addSubview(tapView) tapView.addGestureRecognizer(tapGesture) player.play() getCurrentTimer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: #selector(SituationalEnglishVc.getCurrentMovieTime), userInfo: nil, repeats: true) NSRunLoop.mainRunLoop().addTimer(getCurrentTimer, forMode: NSDefaultRunLoopMode)
- }
- }
- var webView: UIWebView ! func LoadingGif() {
- let path = NSBundle.mainBundle().pathForResource("catLittle", ofType: "gif") ! let gifData = NSData(contentsOfFile: path) webView = UIWebView(frame: avplayerView.frame) self.view.addSubview(webView) webView.layer.masksToBounds = true webView.layer.cornerRadius = 5 webView.scalesPageToFit = true webView.scrollView.scrollEnabled = false webView.backgroundColor = UIColor.clearColor() webView.opaque = false webView.loadData(gifData ! , MIMEType: "image/gif", textEncodingName: "", baseURL: NSURL.init()) self.view.addSubview(webView)
- }
- func addSomeObserver() {
- //监听PlayerItem这个类
- self.player.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.New, context: nil)
- }
- override func observeValueForKeyPath(keyPath: String ? , ofObject object: AnyObject ? , change: [String: AnyObject] ? , context: UnsafeMutablePointer) {
- if (keyPath == "status") {
- if player.status == AVPlayerStatus.ReadyToPlay {
- print("准备播放") if self.webView != nil {
- self.webView.removeFromSuperview() webView = nil
- }
- } else if player.status == AVPlayerStatus.Failed || player.status == AVPlayerStatus.Unknown {
- player.pause()
- }
- }
- }
- func setSentenceTableView() {
- sentenceTb.backgroundColor = colorFromHex("1b1a18") sentenceTb.delegate = self sentenceTb.dataSource = self
- }
- func tableView(tableView: UITableView, numberOfRowsInSection section: Int) - >Int {
- return timeArray.count
- }
- func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) - >CGFloat {
- var ss = CGSize() ss = strSize(chineseArray[indexPath.row], withMaxSize: CGSize(width: 200, height: 0), withFont: UIFont.systemFontOfSize(15), withLineBreakMode: NSLineBreakMode.ByCharWrapping) var sss = CGSize() sss = strSize(wordArray[indexPath.row], withMaxSize: CGSize(width: 200, height: 0), withFont: UIFont.systemFontOfSize(15), withLineBreakMode: NSLineBreakMode.ByCharWrapping) return ss.height + sss.height + 8
- }
- func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) - >UITableViewCell {
- var sentenceCell = tableView.dequeueReusableCellWithIdentifier("thirdID") as ? SentenceTableViewCell
- if sentenceCell == nil {
- sentenceCell = SentenceTableViewCell(style: .Default, reuseIdentifier: "thirdID")
- }
- sentenceCell ? .ChineseLb.text = self.chineseArray[indexPath.row] sentenceCell ? .EnglishLb.text = self.wordArray[indexPath.row]
- var ss = CGSize() ss = strSize(self.wordArray[indexPath.row], withMaxSize: CGSize(width: 200, height: 0), withFont: UIFont.systemFontOfSize(15), withLineBreakMode: NSLineBreakMode.ByCharWrapping) sentenceCell ? .EnglishLb.frame = CGRectMake(self.view.frame.size.width / 2 - 100, 4, 200, ss.height) var sss = CGSize() sss = strSize(self.chineseArray[indexPath.row], withMaxSize: CGSize(width: 200, height: 0), withFont: UIFont.systemFontOfSize(15), withLineBreakMode: NSLineBreakMode.ByCharWrapping) sentenceCell ? .ChineseLb.frame = CGRectMake(self.view.frame.size.width / 2 - 100, ss.height + 4, 200, sss.height) return sentenceCell !
- }
- func strSize(str: String, withMaxSize size: CGSize, withFont font: UIFont, withLineBreakMode mode: NSLineBreakMode) - >CGSize {
- var s: CGSize s = str.boundingRectWithSize(size, options: [.UsesLineFragmentOrigin, .UsesFontLeading], attributes: [NSFontAttributeName: font], context: nil).size
- return s
- }
- func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
- tableView.deselectRowAtIndexPath(indexPath, animated: true)
- }
- func handleTapGesture() {
- UIView.animateWithDuration(0.5) {
- self.controlView.hidden = !self.controlView.hidden
- }
- }
- func playerEnd() {
- if isCyclePlay == true {
- player.seekToTime(CMTimeMake(0, 1)) player.play()
- }
- }@IBAction func playOrPauseClick(sender: AnyObject) {
- if playState == true {
- player.pause() playState = false
- } else {
- player.play() playState = true
- }
- }@IBAction func leftMovieClick(sender: AnyObject) {
- getTitleNet(1) movieUrl = NSURL(string: "http://bos.nj.bpc.baidu.com/tieba-smallvideo/11772_3c435014fb2dd9a5fd56a57cc369f6a0.mp4") ! playerItem = AVPlayerItem(URL: movieUrl) player.replaceCurrentItemWithPlayerItem(playerItem)
- }@IBAction func cyclePlayClick(sender: AnyObject) {
- isCyclePlay = !isCyclePlay
- }@IBAction func rightMovieClick(sender: AnyObject) {
- getTitleNet(4) movieUrl = NSURL(string: "http://w2.dwstatic.com/1/5/1525/127352-100-1434554639.mp4") ! playerItem = AVPlayerItem(URL: movieUrl) player.replaceCurrentItemWithPlayerItem(playerItem)
- }@IBAction func wordListBook(sender: AnyObject) {}
- func getCurrentMovieTime() {
- scrollAccrodingTimeArray()
- }
- func scrollAccrodingTimeArray() {
- let currentSecond: Double = CMTimeGetSeconds(self.playerItem.currentTime()) let s = String(format: "%.1f", currentSecond) if isRePlayNow == false {
- //判断当前时间是否要重复强调
- if s == String(format: "%.1f", wordStart) {
- isRePlayNow = true replaySetting()
- }
- }
- for index in 0.. < timeArray.count {
- let ss = String(format: "%.1f", timeArray[index] / 1000)
- //判断当前时间是否要滚动句子
- if ss == s {
- scrollToCurrentRow(index)
- }
- }
- }
- func replaySetting() {
- replayTimer = NSTimer.scheduledTimerWithTimeInterval(1.5, target: self, selector: #selector(SituationalEnglishVc.replayThreeTimes), userInfo: nil, repeats: true) NSRunLoop.mainRunLoop().addTimer(replayTimer, forMode: NSDefaultRunLoopMode)
- }
- func replayThreeTimes() {
- if replayNum >= 3 {
- replayTimer.invalidate() replayTimer = nil isRePlayNow = false replayNum = 0
- return
- }
- replayNum = replayNum + 1 let timeScale = Int32(self.player.currentItem ! .asset.duration.timescale) let time = CMTimeMakeWithSeconds(Float64(wordStart), timeScale);
- self.player.seekToTime(time, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero) self.player.seekToTime( < #T##time: CMTime##CMTime# > ) let currentTime1: CMTime = self.playerItem.currentTime() let currentSecond1: Double = CMTimeGetSeconds(currentTime1) print("###### \(currentSecond1)")
- }
- func scrollToCurrentRow(idx: Int) {
- currentRow = idx
- if self.chineseArray.count > 0 {
- let indexPath = NSIndexPath(forRow: idx, inSection: 0) for item in sentenceTb.visibleCells {
- let cell = item as ! SentenceTableViewCell cell.EnglishLb.textColor = colorFromHex("666666") cell.ChineseLb.textColor = colorFromHex("666666")
- }
- let cell = sentenceTb.cellForRowAtIndexPath(indexPath) as ? SentenceTableViewCell
- if cell != nil {
- cell ! .EnglishLb.textColor = colorFromHex("5eecff") cell ! .ChineseLb.textColor = colorFromHex("5eecff")
- }
- switch currentRow {
- case chineseArray.count - 1 : sentenceTb.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Bottom, animated: true) break
- case chineseArray.count - 2 : sentenceTb.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Middle, animated: true) break
- default:
- sentenceTb.scrollToRowAtIndexPath(indexPath, atScrollPosition: UITableViewScrollPosition.Top, animated: true) break
- }
- }
- }
- override func didReceiveMemoryWarning() {
- super.didReceiveMemoryWarning()
- // Dispose of any resources that can be recreated.
- }
- }
- class SentenceTableViewCell: UITableViewCell {
- let EnglishLb = UILabel() let ChineseLb = UILabel() override init(style: UITableViewCellStyle, reuseIdentifier: String ? ) {
- super.init(style: style, reuseIdentifier: reuseIdentifier) self.contentView.addSubview(ChineseLb) self.contentView.addSubview(EnglishLb) self.contentView.backgroundColor = colorFromHex("1b1a18") setUpviews() self.backgroundColor = colorFromHex("1b1a18")
- }
- required init ? (coder aDecoder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
- func setUpviews() {
- EnglishLb.frame = CGRectMake(self.contentView.frame.size.width / 2 - 100, 0, 200, 20) EnglishLb.text = "['prlk(e)l]"EnglishLb.font = UIFont.systemFontOfSize(15) EnglishLb.textColor = UIColor.whiteColor() EnglishLb.textAlignment = NSTextAlignment.Center EnglishLb.numberOfLines = 0;
- ChineseLb.frame = CGRectMake(self.contentView.frame.size.width / 2 - 100, 20, 200, 20) ChineseLb.text = "n:规划;计划"ChineseLb.font = UIFont.systemFontOfSize(15) ChineseLb.textColor = UIColor.whiteColor() ChineseLb.textAlignment = NSTextAlignment.Center ChineseLb.numberOfLines = 0
- }
- }
来源: http://lib.csdn.net/article/ios/45391