iOS-視頻播放器的簡單封裝

iOS-視頻播放器的簡單封裝

封裝視頻播放器,首先需要了解視頻播放器的實(shí)現(xiàn),iOS9之前可以使用MediaPlayer來進(jìn)行視頻的播放健爬,iOS9之后系統(tǒng)推薦使用AVFoundation框架實(shí)現(xiàn)視頻的播放放仗。
如果僅僅是播放視頻兩者的使用都非常簡單,但是相比MediaPlayer匙铡,AVPlayer對于視頻播放的可控制性更強(qiáng)一些,可以通過自定義的一些控件來實(shí)現(xiàn)視頻的播放暫停等等碍粥。因此這里使用AVPlayer的視頻播放鳖眼。

封裝視頻播放器,首先需要實(shí)現(xiàn)視頻播放器嚼摩,然后再去考慮怎樣封裝可以讓以后自己使用起來方便快捷钦讳。

視頻播放器布局

首先使用xib創(chuàng)建CLAVPlayerView繼承UIView用來承載播放器,這樣我們在外部使用的時候枕面,直接在控制器View或者Cell上添加CLAVPlayerView即可愿卒,至于播放器播放或者暫停等操作交給CLAVPlayerView來管理。下面來看一下CLAVPlayerView的結(jié)構(gòu)膊畴。

CLAVPlayerView的結(jié)構(gòu)

CLAVPlayerView的布局很簡單掘猿,重點(diǎn)在于約束的添加和控件層次關(guān)系,添加約束只要自己挨個細(xì)心添加就沒有問題唇跨,需要注意控件的層次關(guān)系稠通,從上圖中可以看出四個控件是分先后順序平行添加在CLAVPlayerView上的,要注意他們的層次關(guān)系买猖,避免相互遮擋改橘。

視頻播放器實(shí)現(xiàn)

布局完成之后,就是實(shí)現(xiàn)播放器功能玉控,我們把播放器功能大致分為四部分來完成

一. 通過播放按鈕實(shí)現(xiàn)視頻播放飞主。

首先CLAVPlayerView加載時需要將播放器layer添加到imageView的layer上,此時蒙版和底部工具條一定都是隱藏的,點(diǎn)擊中間播放按鈕碌识,視頻開始播放并隱藏播放按鈕碾篡。因此我們需要在CLAVPlayerView的awakeFromNib方法中,在加載CLAVPlayerView時對其做一些處理筏餐。

  1. 初始化AVPlayer和AVPlayerLayer开泽,并將AVPlayerLayer添加到imageView的layer上,在layoutSubviews中設(shè)置playerLayer的frame
    // 初始化player 和playerLayer
    self.player = [[AVPlayer alloc]init];
    self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
    // imageView上添加playerLayer
    [self.imageView.layer addSublayer:self.playerLayer];
 -(void)layoutSubviews
{
     [super layoutSubviews];
     self.playerLayer.frame = self.imageView.bounds;
}
  1. 根據(jù)播放視頻的url創(chuàng)建AVPlayerItem
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_02.mp4"];
self.playerItem = [AVPlayerItem playerItemWithURL:url];
  1. 設(shè)置Slider原點(diǎn)以及最大點(diǎn)最小點(diǎn)圖片
// 設(shè)置Slider
[self.progressSlider setThumbImage:[UIImage imageNamed:@"thumbImage"] forState:UIControlStateNormal];
[self.progressSlider setMaximumTrackImage:[UIImage imageNamed:@"MaximumTrackImage"] forState:UIControlStateNormal];
[self.progressSlider setMinimumTrackImage:[UIImage imageNamed:@"MinimumTrackImage"] forState:UIControlStateNormal];
  1. 給imageView添加tap手勢魁瞪,點(diǎn)擊imageView則顯示工具欄
//imageView添加手勢
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapAction:)];
[self.imageView addGestureRecognizer:tap];

注意:如果使用xib給imageVIew添加手勢穆律,則通過loadNibNamed加載xib的時候需要獲取返回?cái)?shù)組的firstObject,得到的才是xib的View导俘,如果獲取lastObject峦耘,得到是的tap手勢,會報(bào)錯tap手勢對象沒有View的方法旅薄。

  1. 其他控件顯示以及狀態(tài)的設(shè)置
// 隱藏遮蓋版
self.coverView.hidden = YES;
// 設(shè)置工具欄狀態(tài)
self.toolView.alpha = 0;
self.isShowToolView = NO;
// 設(shè)置工具欄播放按鈕狀態(tài)
self.playOrPauseBtn.selected = NO;

這蓋板只有播放完畢之后顯現(xiàn)辅髓,點(diǎn)擊重播之后又隱藏,因此使用hidden直接隱藏即可赋秀,而工具欄需要重復(fù)顯示利朵,并且我們?yōu)榱四茏尮ぞ邫诘娘@示有動畫效果,這里通過設(shè)置toolView的alpha來顯示或隱藏工具欄猎莲,并通過isShowToolView來記錄toolView的顯示或隱藏。

  1. 中間播放按鈕的點(diǎn)擊
    - (IBAction)playOrPauseBigBtnClick:(UIButton *)sender {
    // 隱藏中間播放按鈕技即,工具欄播放按鈕為選中狀態(tài)
    sender.hidden = YES;
    self.playOrPauseBtn.selected = YES;
    // 替換播放內(nèi)容
    [self.player replaceCurrentItemWithPlayerItem:self.playerItem];
    [self.player play];
    [self addProgressTimer];
    }

此時著洼,當(dāng)我們點(diǎn)擊中間播放按鈕播放器就可以播放視頻了。

二. 工具條的顯示與隱藏

在播放狀態(tài)時而叼,當(dāng)點(diǎn)擊imageView身笤,就會彈出底部工具條,可以查看當(dāng)前播放的時間葵陵,視頻總時間或進(jìn)行暫停視頻液荸、全屏播放等操作。如果沒有操作脱篙,工具欄會在5秒之后自動隱藏娇钱。而當(dāng)未播放狀態(tài)時,點(diǎn)擊imageView和中間播放按鈕效果一樣绊困,開始播放視頻文搂。

  1. 添加定時器,5秒鐘之后隱藏底部工具條秤朗,并提供移除定時器的方法煤蹭。
/** toolView顯示時開始計(jì)時,5s后隱藏toolView */
-(void)addShowTime
{
     self.showTime = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(upDateToolView) userInfo:nil repeats:NO];
     [[NSRunLoop mainRunLoop]addTimer:self.showTime forMode:NSRunLoopCommonModes];
}
/** 將toolView隱藏 */
-(void)upDateToolView
{
     self.isShowToolView = !self.isShowToolView;
     [UIView animateWithDuration:0.5 animations:^{
        self.toolView.alpha = 0;
     }];
}
/**移除定時器*/
-(void)removeShowTime
{
     [self.showTime invalidate];
     self.showTime = nil;
}
  1. imageView的tap手勢點(diǎn)擊方法實(shí)現(xiàn),這里分為幾種情況硝皂,當(dāng)視頻未播放的時候常挚,點(diǎn)擊imageView不會顯示工具欄,而是與點(diǎn)擊中間播放按鈕相同稽物,開始播放視頻奄毡,播放過程中點(diǎn)擊imageView會顯示工具欄,而如果此時點(diǎn)擊了工具欄中的暫停按鈕姨裸,播放暫停秧倾,則此時工具欄不會消失,重新開始播放視頻傀缩,工具欄在5秒內(nèi)消失那先。
/** imageView的tap手勢方法 */
-(void)tapAction:(UITapGestureRecognizer *)tap
{
    // 當(dāng)未播放狀態(tài),點(diǎn)擊imageView等同于點(diǎn)擊中間播放按鈕赡艰,開始播放視頻
    if (self.player.status == AVPlayerStatusUnknown) {
        [self playOrPauseBigBtnClick:self.playOrPauseBigBtn];
        return;
    }
    // 記錄底部工具欄顯示或隱藏的狀態(tài)
    self.isShowToolView = !self.isShowToolView;
    // 如果需要工具欄顯示售淡,添加動畫顯示
    if (self.isShowToolView){
        [UIView animateWithDuration:0.5 animations:^{
            self.toolView.alpha = 1;
        }];
        // 工具欄的播放按鈕為播放狀態(tài)的時候,添加計(jì)時器慷垮,5秒鐘之后工具欄隱藏
        if (self.playOrPauseBtn.selected) {
            [self addShowTime];
        }
    // 如果需要隱藏工具欄揖闸,移除計(jì)時器,并將工具欄隱藏
    }else{
        [self removeShowTime];
        [UIView animateWithDuration:0.5 animations:^{
            self.toolView.alpha = 0;
        }];
    }
}
  1. 工具欄中播放/暫停按鈕的點(diǎn)擊也需要做一些處理料身,當(dāng)處于暫停狀態(tài)時汤纸,工具欄alpha值設(shè)為1,并將定時器移除芹血,重新開始播放視頻時贮泞,則重新添加定時器開始計(jì)時,5秒鐘之后讓工具欄消失幔烛。具體代碼會在播放時間啃擦、Slider與視頻播放的同步中詳細(xì)貼出。

三. 播放時間饿悬、Slider與視頻播放的同步

底部工具條中播放時間令蛉、視頻總時間以及Slider的滑動需要與視頻播放時間進(jìn)行同步。

  1. 添加視頻播放和Slider的定時器狡恬,每1秒鐘重復(fù)調(diào)用更新時間label和Slider滑塊
    /** slider定時器添加 /
    -(void)addProgressTimer
    {
    self.progressTimer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(updateProgressInfo) userInfo:nil repeats:YES];
    [[NSRunLoop mainRunLoop]addTimer:self.progressTimer forMode:NSRunLoopCommonModes];
    }
    /
    * 移除slider定時器 /
    -(void)removeProgressTimer
    {
    [self.progressTimer invalidate];
    self.progressTimer = nil;
    }
    /
    * 更新slider和timeLabel */
    - (void)updateProgressInfo
    {
    NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentTime);
    NSTimeInterval durationTime = CMTimeGetSeconds(self.player.currentItem.duration);

         self.timeLabel.text = [self timeToStringWithTimeInterval:currentTime];
         self.allTimeLabel.text = [self timeToStringWithTimeInterval:durationTime];
         self.progressSlider.value = CMTimeGetSeconds(self.player.currentTime) / CMTimeGetSeconds(self.player.currentItem.duration);
    
         if (self.progressSlider.value == 1) {
             [self removeProgressTimer];
             self.coverView.hidden = NO;
         } 
     }
    

獲取到的當(dāng)前播放時間和總時間是CMTime類型的珠叔,需要將他們轉(zhuǎn)化為NSTimeInterval并將秒轉(zhuǎn)化為分鐘和時間,將轉(zhuǎn)化方法提出來

/** 轉(zhuǎn)換播放時間和總時間的方法 */
-(NSString *)timeToStringWithTimeInterval:(NSTimeInterval)interval;
{
    NSInteger Min = interval / 60;
    NSInteger Sec = (NSInteger)interval % 60;
    NSString *intervalString = [NSString stringWithFormat:@"%02ld:%02ld",Min,Sec];
    return intervalString;
}
  1. 當(dāng)點(diǎn)擊中間播放按鈕開始播放的時候添加定時器傲宜,同步更新播放時間和Slider运杭,當(dāng)播放途中點(diǎn)擊工具欄暫停按鈕暫停播放,需要將視頻暫停函卒,并移除定時器辆憔,重新開始播放時在添加定時器撇眯,并開始播放
    /** toolView上暫停按鈕的點(diǎn)擊事件 */
    - (IBAction)playOrPauseBtnClick:(UIButton *)sender {
    // 播放狀態(tài)按鈕selected為YES,暫停狀態(tài)selected為NO。
    sender.selected = !sender.selected;
    if (!sender.selected) {
    self.toolView.alpha = 1;
    [self removeShowTime];
    [self.player pause];
    [self removeProgressTimer];
    }else{
    [self addShowTime];
    [self.player play];
    [self addProgressTimer];
    }
    }
  2. Slider的拖動跳躍播放視頻
    根據(jù)Slider滑動拖動滑動位置播放視頻需要監(jiān)聽Slider的按下虱咧,拖動(數(shù)據(jù)改變)熊榛,松開三個階段。按下時移除定時器腕巡,拖動時根據(jù)拖動的值即時的計(jì)算當(dāng)前播放時間并顯示在label上玄坦,松開時計(jì)算當(dāng)前播放時間,并跳轉(zhuǎn)到當(dāng)前播放時間進(jìn)行播放绘沉。
    /** slider拖動和點(diǎn)擊事件 */
    - (IBAction)touchDownSlider:(UISlider *)sender {
    // 按下去 移除監(jiān)聽器
    [self removeProgressTimer];
    [self removeShowTime];
    }
    - (IBAction)valueChangedSlider:(UISlider *)sender {
    // 計(jì)算slider拖動的點(diǎn)對應(yīng)的播放時間
    NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentItem.duration) * sender.value;
    self.timeLabel.text = [self timeToStringWithTimeInterval:currentTime];
    }
    - (IBAction)touchUpInside:(UISlider *)sender {
    [self addProgressTimer];
    //計(jì)算當(dāng)前slider拖動對應(yīng)的播放時間
    NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentItem.duration) * sender.value;
    // seekToTime:播放跳轉(zhuǎn)到當(dāng)前播放時間
    [self.player seekToTime:CMTimeMakeWithSeconds(currentTime, NSEC_PER_SEC) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
    [self addShowTime];
    }

四. 重播按鈕和全屏播放按鈕的實(shí)現(xiàn)

  1. 在定時器每秒調(diào)用的更新Slider的方法中判斷當(dāng)視頻播放完畢之后煎楣,顯示遮蓋View,而重播按鈕的實(shí)現(xiàn)车伞,其實(shí)就是將Slider的value置為0并重新調(diào)用點(diǎn)擊Slider松開時的方法择懂,將當(dāng)前播放時間置為0,重新隱藏遮蓋View另玖,并調(diào)用中間播放按鈕開始播放困曙。
    /** 重播按鈕點(diǎn)擊 */
    - (IBAction)repeatBtnClick:(UIButton *)sender {
    self.progressSlider.value = 0;
    [self touchUpInside:self.progressSlider];
    self.coverView.hidden = YES;
    [self playOrPauseBigBtnClick:self.playOrPauseBigBtn];
    }

  2. 全屏播放的實(shí)現(xiàn)
    全屏播放需要控制器Moda出一個全屏播放的控制器進(jìn)行全屏播放,創(chuàng)建全屏播放控制器CLFullViewController谦去,并使其支持左右方向的旋轉(zhuǎn)慷丽,Moda出CLFullViewController控制器,并將CLAVPlayerView添加到CLFullViewController的View上并設(shè)置frame即可鳄哭,當(dāng)退出全屏?xí)r要糊,dismiss掉CLFullViewController然后將CLAVPlayerView的frame設(shè)置為原來的值。
    CLFullViewController中設(shè)置可以旋轉(zhuǎn)和旋轉(zhuǎn)方向
    - (UIInterfaceOrientationMask)supportedInterfaceOrientations
    {
    return UIInterfaceOrientationMaskLandscape;
    }
    - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
    {
    return YES;
    }
    全屏播放按鈕點(diǎn)擊事件
    /** 全屏按鈕點(diǎn)擊事件 /
    - (IBAction)fullViewBtnClick:(UIButton )sender {
    sender.selected = !sender.selected;
    [self videoplayViewSwitchOrientation:sender.selected];
    }
    /
    彈出全屏播放器 */
    - (void)videoplayViewSwitchOrientation:(BOOL)isFull
    {
    if (isFull) {
    [self.contrainerViewController presentViewController:self.fullVc animated:NO completion:^{
    [self.fullVc.view addSubview:self];
    self.center = self.fullVc.view.center;

                [UIView animateWithDuration:0.15 delay:0.0 options:UIViewAnimationOptionLayoutSubviews animations:^{
                    self.frame = self.fullVc.view.bounds;
                } completion:nil];
            }];
        } else {
            [self.fullVc dismissViewControllerAnimated:NO completion:^{
                [self.contrainerViewController.view addSubview:self];
         
                [UIView animateWithDuration:0.15 delay:0.0 options:UIViewAnimationOptionLayoutSubviews animations:^{
                    self.frame = CGRectMake(0, 200, self.contrainerViewController.view.bounds.size.width, self.contrainerViewController.view.bounds.size.width * 9 / 16);
                } completion:nil];
            }];
        }
    }
    

注意:這里需要拿到外面控制器來Moda出全屏播放控制器妆丘,所以給CLAVPlayerView添加contrainerViewController屬性來拿到控制器杨耙。

簡單封裝

此時已經(jīng)實(shí)現(xiàn)了播放器基本的功能,接下來考慮如何封裝能使我們使用起來更加方便飘痛,其實(shí)我們已經(jīng)將大部分封裝完成,接下來需要做的就是提供簡單易用的接口容握,使外部可以輕松調(diào)用實(shí)現(xiàn)播放器宣脉。

  1. 提供類方法快速創(chuàng)建播放器
    + (instancetype)videoPlayView
    {
    return [[[NSBundle mainBundle]loadNibNamed:@"CLAVPlayerView" owner:nil options:nil]lastObject];
    }
  2. 播放視頻的資源應(yīng)該由外部決定,因此我們提供urlString屬性用來接收視頻的資源剔氏,然后通過重寫其set方法來播放視頻
/** 需要播放的視頻資源set方法 */
-(void)setUrlString:(NSString *)urlString
{
    _urlString = urlString;
    NSURL *url = [NSURL URLWithString:urlString];
    self.playerItem = [AVPlayerItem playerItemWithURL:url];
}

此時我們在外部使用播放器就非常簡單了塑猖,無需考慮內(nèi)部邏輯,只需快速創(chuàng)建CLAVPlayerView谈跛,添加到控制器View羊苟,設(shè)置其frame,然后指定其播放視頻資源就可以了感憾。

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setUpVideoPlayView];    
    self.playView.urlString = @"http://120.25.226.186:32812/resources/videos/minion_02.mp4";
}
-(void)setUpVideoPlayView
{
    self.playView = [CLAVPlayerView videoPlayView];
    self.playView.frame = CGRectMake(0, 200, self.view.frame.size.width, self.view.frame.size.width * 9 / 16);
    self.playView.contrainerViewController = self;
    [self.view addSubview:self.playView];
}

最后蜡励,視頻播放器大致這個樣子

視頻播放器

其中還有許多需要完善的地方,一些功能也沒有實(shí)現(xiàn),例如兩個占位的Button凉倚,將來可以用來下載視頻和控制彈幕的開關(guān)兼都,播放結(jié)束之后分享按鈕也沒有實(shí)現(xiàn)。源碼已上傳至CLxxcc-GitHub稽寒,歡迎下載扮碧,并提出意見。


文中如果有不對的地方歡迎指出杏糙。我是xx_cc慎王,一只長大很久但還沒有二夠的家伙。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宏侍,一起剝皮案震驚了整個濱河市赖淤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌负芋,老刑警劉巖漫蛔,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異旧蛾,居然都是意外死亡莽龟,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門锨天,熙熙樓的掌柜王于貴愁眉苦臉地迎上來毯盈,“玉大人,你說我怎么就攤上這事病袄÷Ц常” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵益缠,是天一觀的道長脑奠。 經(jīng)常有香客問我,道長幅慌,這世上最難降的妖魔是什么宋欺? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮胰伍,結(jié)果婚禮上齿诞,老公的妹妹穿的比我還像新娘。我一直安慰自己骂租,他們只是感情好祷杈,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著渗饮,像睡著了一般但汞。 火紅的嫁衣襯著肌膚如雪宿刮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天特占,我揣著相機(jī)與錄音糙置,去河邊找鬼。 笑死是目,一個胖子當(dāng)著我的面吹牛谤饭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播懊纳,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼揉抵,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了嗤疯?” 一聲冷哼從身側(cè)響起冤今,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎茂缚,沒想到半個月后戏罢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡脚囊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年龟糕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悔耘。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡讲岁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出衬以,到底是詐尸還是另有隱情缓艳,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布看峻,位于F島的核電站阶淘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏互妓。R本人自食惡果不足惜舶治,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望车猬。 院中可真熱鬧,春花似錦尺锚、人聲如沸珠闰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽伏嗜。三九已至坛悉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間承绸,已是汗流浹背裸影。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留军熏,地道東北人轩猩。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像荡澎,于是被迫代替她去往敵國和親均践。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容