ios 视频播放器:AVPlayer(附:seektotime精准定位)

Swift 3.1{

func playVideo() {

        var playerItem = AVPlayerItem(url: movieURL as URL)
        // 创建 AVPlayer 播放器
        var player = AVPlayer(playerItem: playerItem)
        // 将 AVPlayer 添加到 AVPlayerLayer 上
        let playerLayer = AVPlayerLayer(player: player)
        // 设置播放页面大小
        playerLayer.frame = CGRect(x: CGFloat(10), y: CGFloat(30), width: CGFloat(view.bounds.size.width - 20), height: CGFloat(200))
        // 设置画面缩放模式
        playerLayer.videoGravity = AVLayerVideoGravityResizeAspect
        // 在视图上添加播放器
        view.layer.addSublayer(playerLayer)
        // 开始播放
        player.play()


    }

}

网上有很多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 <AVFoundation/AVFoundation.h>

简单实用:通过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 开发的. 使用

https://objectivec2swift.com/#/home/main

可以转成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<Void>) {
        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
    }




}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值