基于swift3.0實現視頻播放、屏幕旋轉自適應、倍速播放、手勢調節(jié)進度音量等功能

最近在學習swift,恰巧現在負責的項目中有關于視頻播放的一些東西,就想著用swift去實現,視頻播放在原OC項目中已實現基本功能,所以就模仿OC去寫swift視頻播放,在寫的過程中發(fā)現與OC的實現方法出入還是比較大的,網上關于swift視頻播放資料不是很全面,所以想給大家分享一下知識點。

demo下載地址

先來看一下實現的效果,沒實現效果我知道同學肯定看不下去

播放演示.gif

簡單說一下工程結構,所有關于播放的東西以及布局都是在AVPlayer文件夾下,視頻播放的布局是基于SnapKit三方庫來布局了,因為在OC里用慣了Masonry所以工程里依然沿用這個庫。因為項目里面有線路切換和音視頻切換功能,,如果你有未加密的視頻鏈接或者音頻鏈接直接把sdk刪掉也是可以的,也是可以正常播放的。

關鍵代碼是放在MPlayerView這個文件中,輔助視圖布局的三個文件分別是:RateView音視頻倍速切換ResolutionView高清度切換SwitchCircuitView線路切換。

剛開始做的時候,把所有的功能代碼都全部放入MPlayerView這個文件中,發(fā)現耦合度太多,代碼可讀性差,所以我將代碼拆出來各自負責的模塊放入各自的功能,比如高清度切換功能,實現的功能就是放在ResolutionView文件下。

在這里就不貼太多的代碼,我將在本文末放demo的下載地址。現在就簡單聊一下實現過程吧。視頻播放界面我用的是一個單例實現的,剛開始不是用單例實現,但是為了把代碼拆出來放到各自的功能區(qū)所以用單例實現是最好的方法。由于swift放棄了OC里的dispatch_once實現單例方法,swift3.0以后的單例寫法:

/// 創(chuàng)建播放器單例
static let shared = MPlayerView()
private override init(frame: CGRect) {
    super.init(frame: frame)
 }
required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

在swift3.0之后重寫init方法必須實現required init方法,這么做也是為了安全,因為在OC里init方法并不能保證子類完成初始化,增加required“這是由初始化方法的完備性需求所決定的,以保證類型的安全。

由于swift里面有嚴格的類型檢查,就比如在做手勢滑動的時候,手勢剛開始滑動的時候肯定需要記錄一下當前播放器的位置我在項目中是定義的sumTime屬性是一個CMTime類型,如果在OC里大可不必這樣,來看一下swift與OC代碼的區(qū)別

swift寫法

/// 給sumTime初值
let time = self.player?.currentTime()
self.sumTime = CMTimeMake((time?.value)!, (time?.timescale)!)

OC寫法

// 給sumTime初值
CMTime time = self.player.currentTime;
self.sumTime  = time.value/time.timescale;

滑動的距離是一個Double類型,而self.sumTime是CMTime類型,倆者肯定不能想加算出結束滑動的距離,所以將double類型轉換成CMTime類型用以下方法:

CMTime.init(seconds: Double.init(value/200), preferredTimescale: CMTimeScale(NSEC_PER_SEC))

如果是OC的話直接括號強轉類型即可實現。

知道滑動的距離和記錄滑動前的距離倆者想加即是當前位置,轉化成CMTime類型:

self.sumTime = CMTimeAdd(self.sumTime!, addend)

手勢是滑動了,但是進度條也是要跟著一起滑動的,有人說我把進度條刷新放到player的代理里面,手勢滑動完只需要把時間傳給播放器,播放器根據當前時間和總時間去更新進度條,這樣做也對,但是有一點就是,如果網速不好,手勢已經滑動到5分鐘了,而進度條還停留在1分鐘的地方,播放器緩存完畢了,進度條會瞬間跳到5分鐘,從而造成卡頓的假象體驗也不是很好,所以解決這個方法是手勢滑動的時候也更新進度條,但是手勢滑動的時候都是CMTime類型,怎么轉成Float類型,因為slider?.value是float類型。可以這樣:通過CMTimeGetSeconds方法得到一個Float64再通過Float.init方法得到一個float類型,看一下實現:

let sliderTime = CMTimeGetSeconds(self.sumTime!)/CMTimeGetSeconds(totalMovieDuration)
self.slider?.value = Float.init(sliderTime)

想查看整個過程可以看播放器手勢添加與創(chuàng)建這一塊,我已經用MARK:標記起來了。

一個視頻播放實現起來并不困難,只要處理好playerplatitem就行了。最難得就是,如果手機屏幕旋轉,怎么能讓視頻跟著屏幕自適應呢,我在工程里面通過UIDevice變化添加的是屏幕旋轉監(jiān)聽:

/**
  *  監(jiān)聽設備旋轉通知
  */
 private func listeningRotating()  {
        UIDevice.current.beginGeneratingDeviceOrientationNotifications()
        NotificationCenter.default.addObserver(self, selector: #selector(onDeviceOrientationChange), name:NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}

如果用戶把屏幕旋轉關掉,就是控制中心那個開關,用戶旋轉屏幕,怎么能讓畫面跟著跑呢,我百度的很多資料,試了也很多方法,但是都不理想,用的還是OC的代碼,因為swift里面移除了NSInvocation屬性,用的依然是OC的屏幕強制旋轉,只能使用橋接文件:

+ (void)interfaceOrientation:(UIInterfaceOrientation)orientation{
    if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
        SEL selector = NSSelectorFromString(@"setOrientation:");
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
        [invocation setSelector:selector];
        [invocation setTarget:[UIDevice currentDevice]];
        int val  = orientation;
        // 從2開始是因為0 1 兩個參數已經被selector和target占用
        [invocation setArgument:&val atIndex:2];
        [invocation invoke];
    }
}

大概就介紹這么多吧,主要就是記錄一下自己的學習過程,如果還有不明白的知識點可以去demo中自己去查.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 發(fā)現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,200評論 4 61
  • 一、他的秘密 他們的愛情,在她的眼里是完美的。 工作中,他上進,生活中,他細心耐心,每逢紀念日,給她買的禮物都是她...
    念衡閱讀 1,392評論 4 17
  • 4月23日閉幕的北京電影節(jié)的展映影片中,《末代皇帝》的4K修復版可謂最受矚目的熱門影片之一。 意大利名導貝納爾多·...
    雪城不下雪閱讀 531評論 0 2
  • T
    EXC_BAD_ACCESS閱讀 166評論 0 0
  • 今天 洪洪我是非常擔心的 我也沒有跑過全馬吖 怎么敢推送跑全馬技巧的文章呢 “故事與三位男神有關” 男神是誰? 故...
    洪hong閱讀 1,925評論 0 5