VLC集成與使用

VLC的集成和使用

VLC介紹

VLC Media Player (VideoLAN) 為 Windows、Linux、OS X、Android、iOS、Windows Phone等平臺提供一個視頻播放器、解碼器。它可以播放來自網絡、攝像頭、磁盤、光驅的文件,支持包括MPEG 1/2/4, H264, VC-1, DivX, WMV, Vorbis, AC3, AAC等格式的解碼。在 Windows 和 Linux 上的 VLC 是使用C++/Qt寫成,提供了一致的用戶體驗。同時 VLC 還專門為 OS X 提供了原生版本,OS X 版的 VLC 的用戶界面使用Cocoa框架編寫,在 OS X 下擁有卓越的原生體驗。

  • VLC還有一個非常好的功能——播放那些沒有下載完整的視頻文件。
  • VLC幾乎覆蓋所有媒體格式。

VLC集成

在iOS下,我們可以很方便的使用VLC,因為它經行了優秀的封裝,源碼中最核心的部分被封裝成了獨立的庫(MoblieVLVKit.framework庫),它是基于FFmpeg,Live555提供完整的媒體播放庫,所以整一個庫體積還是比較大(目前已經超過600M了),不過并不會太影響App的大小,經行App打包發布的是會自動超級壓縮的。經過測試它比使用FFmpeg庫僅僅是多7M左右的大小。

  • VLC cocoapods方式集成

VLC可以有多種的方式安裝,目前最簡單的方式就是cocoapods安裝,以前需要下載源碼后經行編譯,編譯后的還分模擬器版和真機版,最后還得使用lipo 命令經行合并,比較麻煩,這里我就不進行介紹了,因為沒必要(喜歡折騰的可以自行搜索相關資料),最終編譯得到的結果一樣是MoblieVLCKit.framework庫,即使項目不允許使用cocoapods,也可以建立空項目后使用cocoapods得到MoblieVLCKit.framework在經行移植。

  • 步驟
```objc
1.在podfile中寫入:pod 'MobileVLCKit'
2.終端執行pod install即可(成功后會在項目里看到MoblieVLCKit.framework庫)
3.添加依賴庫:libz.tbd、libbz2.tbd、libiconv.tbd、libstdc++.6.0.9.tbd
```

VLC使用

MoblieVLCKit為我們提供了很多功能接口,不過我們不需要全部了解,只需要懂得幾個常用的API接口就可以輕輕松松的寫出一個播放器出來。因為它支持定制化UI,所以有心的開發者可以結合漂亮的UI設計一套專屬的播放器

  • MoblieVLCKit 常用API介紹
    • VLCMediaPlayer
      VCL對象,管理著播放的開始暫停等操作,有著幾個代理方法可以經行狀態和時間的監聽回調
      
    • VLCMediaPlayer屬性
      // 播放設置,比如設置播放路徑是本地播放還是網絡播放,以及播放的畫面映射到哪個View
      
    @property (NS_NONATOMIC_IOSONLY, strong) VLCMedia *media;
    - VLCMediaPlayer方法
    ```objc
    // 開始播放
    -(BOOL)play;
    // 暫停播放
    - (void)pause;
    // 停止播放
    - (void)stop;
    /**
      *快進播放
      *interval:需要快進的秒數
    */
    - (void)jumpForward:(int)interval;
    /**
      *快退播放
      *interval:需要快退的秒數
    */
    - (void)jumpBackward:(int)interval;
    // 短時間的快退(10秒)
    - (void)shortJumpBackward;
    // 短時間的快進(10秒)
    - (void)shortJumpForward;
    /**
      * 以一定倍速播放
      * rate:倍速
    */
    - (void)fastForwardAtRate:(float)rate;
    
    • VLCMediaPlayer代理
      // 播放狀態改變的回調
      - (void)mediaPlayerStateChanged:(NSNotification *)aNotification;
      // 播放時間改變的回調
      - (void)mediaPlayerTimeChanged:(NSNotification *)aNotification;
      
  • MoblieVLCKit使用demo

我就拋磚迎玉貼上自己寫的一份播放器demo封裝(有時間我在放進github)


效果預覽

IMG_1717.jpg

  • VLCPlayer.h和VLCPlayer.m(VLC創建)

        //
    //  VLCPlayer.h
    //  VLCDemo
    //
    //  Created by pocket on 16/6/27.
    //  Copyright ? 2016年 pocket. All rights reserved.
    //
    
    // VLC播放對象
    #import <Foundation/Foundation.h>
    #import <MobileVLCKit/MobileVLCKit.h>
    #import <UIKit/UIKit.h>
    
    @interface VLCPlayer : NSObject
    /**
     *  VCL對象
     */
    @property (nonatomic, strong) VLCMediaPlayer *player;
    /**
     *  根據路徑初始化VCL對象
     *
     *  @param playView 播放承載View
     *  @param path     本地路徑
     *
     *  @return VLCPlayer 類
     */
    - (id)initWithView:(UIView *)playView andMediaPath:(NSString *)path;
    /**
     *  根據URL初始化VCL對象
     *
     *  @param playView 播放承載View
     *  @param url      url路徑
     *
     *  @return VLCPlayer 類
     */
    - (id)initWithView:(UIView *)playView andMediaURL:(NSURL *)url;
    
    /**
     *  開始播放
     */
    - (void)playMedia;
    /**
     *  暫停播放
     */
    - (void)stopPlaying;
    @end
    
        //
    //  VLCPlayer.m
    //  VLCDemo
    //
    //  Created by pocket on 16/6/27.
    //  Copyright ? 2016年 pocket. All rights reserved.
    //
    
    #import "VLCPlayer.h"
    
    @implementation VLCPlayer
    
    - (id)initWithView:(UIView *)playView andMediaPath:(NSString *)path {
        self = [super init];
        if (self) {
            // 創建VCL對象
            _player = [[VLCMediaPlayer alloc] init];
            // 設置VCL播放界面的View
            _player.drawable = playView;
            // 設置需要加載的路徑
            VLCMedia *media = [VLCMedia mediaWithPath:path];
            [_player setMedia:media];
        }
        return self;
    }
    
    - (id)initWithView:(UIView *)playView andMediaURL:(NSURL *)url {
        self = [super init];
        if (self) {
            _player = [[VLCMediaPlayer alloc] init];
            _player.drawable = playView;
            // 設置需要加載的url
            VLCMedia *media = [VLCMedia mediaWithURL:url];
            [_player setMedia:media];
        }
        return self;
    }
    
    // 播放
    - (void)playMedia {
        [_player play];
    }
    
    // 暫停
    - (void)stopPlaying {
        [_player stop];
    }
    @end
    
  • VLCViewController.h/VLCViewController.m(播放控制器)

    //  VLCViewController.h
    //  VLCDemo
    //
    //  Created by pocket on 16/6/27.
    //  Copyright ? 2016年 pocket. All rights reserved.
    //
    
    // VLC播放控制器
    #import <UIKit/UIKit.h>
    #import "VLCPlayer.h"
    
    @interface VLCViewController : UIViewController
    /**
     *  文件名字
     */
    @property (nonatomic, strong) NSString *playName;
    /**
     *  文件路徑(本地資源路徑)
     */
    @property (nonatomic, strong) NSString *playPath;
    /**
     *  網絡URL路徑
     */
    @property (nonatomic, strong) NSURL *playURL;
    
    @property (nonatomic, strong) VLCPlayer *player;
    @end
    
    
    //  VLCViewController.m
    //  VLCDemo
    //
    //  Created by pocket on 16/6/27.
    //  Copyright ? 2016年 pocket. All rights reserved.
    //
    #import "VLCViewController.h"
    #import <AVFoundation/AVFoundation.h>
    #import<MobileVLCKit/MobileVLCKit.h>
    #import "VLCPlayerView.h"
    @interface VLCViewController ()<VLCMediaPlayerDelegate>
    /**
     *  VLC承載視圖
     */
    @property (nonatomic,strong) VLCPlayerView *vlcPlayerView;
    /**
     *  是否本地路徑
     */
    @property (nonatomic,assign) BOOL isLocal;
    /**
     *  視頻總時間(秒)
     */
    @property (nonatomic,assign) int videoAllTime;
    /**
     *  當前播放時間(秒)
     */
    @property (nonatomic,assign) int videoCurrentTime;
    /**
     *  當前進度
     */
    @property (nonatomic,assign) float currentProgress;
    /**
     *  菊花加載
     */
    @property (nonatomic,strong) UIActivityIndicatorView *activityView;
    @end
    
    @implementation VLCViewController
    - (void)viewDidLoad {
      [super viewDidLoad];
    
      self.title = self.playName;
      self.videoAllTime = 0;
    
      [self tarnsformView];
      // 添加播放的承載View
      [self addVLCPalyerView];
      // 菊花
      self.activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
      self.activityView.center = self.vlcPlayerView.center;
      [self.activityView setHidesWhenStopped:YES]; // 旋轉時隱藏
      [self.activityView startAnimating]; // 開始旋轉
      [self.view addSubview:self.activityView];
      //視頻播放
      if (_playPath) { // 本地
          _player = [[VLCPlayer alloc] initWithView:self.vlcPlayerView.playView andMediaPath:_playPath];
          _player.player.delegate = self;
          [_player playMedia]; // 播放
          self.isLocal = YES;
      }
      if (_playURL) { // 網絡
          _player = [[VLCPlayer alloc] initWithView:self.vlcPlayerView.playView andMediaURL:_playURL];
          _player.player.delegate = self;
          [_player playMedia];
          self.isLocal = NO;
      }
    

}

  • (void)dealloc
    {
    [_player stopPlaying];
    _player = nil;
    }
+ (void)initialize
{
    AVAudioSession *session = [AVAudioSession sharedInstance];
    [session setCategory:AVAudioSessionCategoryPlayback error:nil];
    [session setActive:YES error:nil];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    [self addObserver];
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];

    [self removeObserver];
}

#pragma mark - -- 屏幕旋轉控制處理
// 這里采用假旋轉,因為其他界面并不需要支持橫屏(缺點是狀態欄不支持轉動,所以采取presentView方式最好)
- (void)tarnsformView
{
    // 旋轉view
    self.view.transform = CGAffineTransformMakeRotation(M_PI/2); // 旋轉90°
    CGRect frame = [UIScreen mainScreen].applicationFrame; // 獲取當前屏幕大小
    // 重新設置所有view的frame
    self.view.bounds = CGRectMake(0, 0,frame.size.height + 20,frame.size.width);
    self.vlcPlayerView.frame = self.view.bounds;
}
// 隱藏狀態欄顯得和諧
- (BOOL)prefersStatusBarHidden
{
    return YES;
}

#pragma mark - -- 播放的承載View的設置
- (void)addVLCPalyerView
{
    //承載播放的視圖初始化(自定義播放界面可在這里做UI定制)
    self.vlcPlayerView = [[VLCPlayerView alloc] initWithFrame:self.view.bounds];
    // 設置視頻名稱
    self.vlcPlayerView.videoName = self.playName;
    // 設置播放監聽回調
    __weak typeof(self) weakSelf = self;
    self.vlcPlayerView.playBlock = ^(UIButton *playBtn){
        [weakSelf playClick:playBtn];
    };
    // 設置進度條監聽回調
    self.vlcPlayerView.changeSliderBlock = ^(UISlider *sliderView){
        [weakSelf changeProgress:sliderView];
    };
    // 設置屏幕鎖監聽回調
    self.vlcPlayerView.lockBlock = ^(UIButton *lockBtn){
        // 屏幕鎖操作邏輯
        // ...后續
    };
    // 設置返回按鈕回調
    self.vlcPlayerView.backBlock = ^{
        // 關閉視圖控制器
        [weakSelf.player stopPlaying];
        [weakSelf dismissViewControllerAnimated:YES completion:^{

        }];
    };

    // 左右滑動手勢結束回調
    self.vlcPlayerView.endPanGesture = ^(float progress,int type){
        if (type == 4) { // 快退
            [weakSelf.player.player shortJumpBackward];
        } else if (type == 3) { // 快進
            [weakSelf.player.player shortJumpForward];
        }
    };

    [self.view addSubview:self.vlcPlayerView];
}
    #pragma mark - -- vlcPlayerView播放操作
// 播放(暫停)監聽
- (void)playClick:(UIButton *)playBtn
{
    if ([_player.player isPlaying]) { // 正在播放
        [_player.player pause]; // 暫停
    } else {
        [_player.player play]; // 播放
    }
}

// 倍率速度播放(一般很少使用)
- (void)fastForwardAtRate:(float)rate
{
    [_player.player fastForwardAtRate:rate];
}
// 進度條拖拽
- (void)changeProgress:(UISlider *)sliderView
{
    if (!_player.player.isPlaying) { // 防止暫停狀態拖動(拖動觸發播放)
        [self.vlcPlayerView changePlayBtnState:YES];
        [_player.player play];
    }
    // 根據拖動比例計算開始到播放節點的總秒數
    int allSec = (int)(self.videoAllTime * sliderView.value);
    // 根據當前播放秒數計算需要seek的秒數
    int sec = abs(allSec - self.videoCurrentTime);
    // 如果為獲取到時間信息
    if (sec == 0 && allSec == 0) {
        [[AlertViewManager shareInstance] showPromptText:self.view context:CustomLocalizedString(@"未獲取到視頻總時間,請嘗試手勢快進", nil) minSize:CGSizeMake(60,44) afterDelay:2.0];
        return;
    }
    NSLog(@"sec:%d",sec);
    if (sec==0) { // 剛好等于視頻總時間
        [_player.player stop];
        return;
    }
    if (self.currentProgress<=sliderView.value) { // 快進滑動
        [_player.player jumpForward:sec]; // 快進播放
    } else {
        [_player.player jumpBackward:sec]; // 快退播放
    }

}

#pragma  mark - -- vlcPlayerView時間和進度刷新
- (void)updateTime
{
    // 設置剩余時間
    self.vlcPlayerView.remainingTime = [[_player.player remainingTime] stringValue];
    // 設置當前時間
    self.vlcPlayerView.currentTime = [[_player.player time] stringValue];
    // 設置當前進度
    self.vlcPlayerView.sliderValue = [_player.player position];
}

#pragma mark - -- KVO監聽
// 添加監聽
- (void)addObserver
{
    // 監聽VLC對象屬性(時間和播放)
    [_player.player addObserver:self forKeyPath:@"remainingTime" options:0 context:nil];
    [_player.player addObserver:self forKeyPath:@"isPlaying" options:0 context:nil];
}

// 移除監聽
- (void)removeObserver
{
    [_player.player removeObserver:self forKeyPath:@"remainingTime"];
    [_player.player removeObserver:self forKeyPath:@"isPlaying"];
}

// kvo監聽回調
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {

    // 可以在這里設置顯示的時間和進度
    // position:VLCMediaPlayer對象中的進度比例,可用此值設置播放進度
//    NSLog(@"[_player.player position]:%lf",[_player.player position]);
    // remainingTime:VLCMediaPlayer對象中的剩余時間,stringValue可以轉化為時間格式字符串
//    NSLog(@"[[_player.player remainingTime] stringValue]:%@",[[_player.player remainingTime] stringValue]);
    // time:VLCMediaPlayer對象中的當前時間
//    NSLog(@"[[_player.player time] stringValue]:%@", [[_player.player time] stringValue]);

//    NSLog(@"剩余的分鐘:%@",[[_player.player remainingTime] minuteStringValue]);
//    NSLog(@"播放的分鐘:%@",[[_player.player time] minuteStringValue]);
    // 記錄當前進度
    self.currentProgress = [_player.player position];
    // 根據分鐘計算播放的秒數(這里不夠嚴格,還得加上秒數)
    self.videoCurrentTime = [[[_player.player time] minuteStringValue] intValue] * 60;
    // 根據剩余時間和已經播放的計算總秒數(這里不夠嚴格,還得加上秒數)
    self.videoAllTime = [[[_player.player remainingTime] minuteStringValue] intValue]*60 + self.videoCurrentTime;

    // 有時候獲取不到時間(個人想法是結合定時器和進度比例計算總時間等)
    // ...

    // 刷新最新時間和播放進度
    [self updateTime];
    // 停止菊花加載
    if (self.activityView.isAnimating) {
        [self.activityView stopAnimating];
    }
}

#pragma mark - 監聽程序進入前臺和后臺
#pragma mark - VLCMediaPlayerDelegate
// 播放狀態改變的回調
- (void)mediaPlayerStateChanged:(NSNotification *)aNotification
{
    /**
     *  VLCMediaPlayerStateStopped,        //< Player has stopped
     VLCMediaPlayerStateOpening,        //< Stream is opening
     VLCMediaPlayerStateBuffering,      //< Stream is buffering
     VLCMediaPlayerStateEnded,          //< Stream has ended
     VLCMediaPlayerStateError,          //< Player has generated an error
     VLCMediaPlayerStatePlaying,        //< Stream is playing
     VLCMediaPlayerStatePaused          //< Stream is paused
     */
    NSLog(@"mediaPlayerStateChanged");
    NSLog(@"狀態:%ld",(long)_player.player.state);
    switch ((int)_player.player.state) {
        case VLCMediaPlayerStateStopped: // 停止播放(播放完畢或手動stop)
        {
            [_player.player stop]; // 手動調用一次停止(一遍再次點擊播放)
            [self.vlcPlayerView changePlayBtnState:NO];
            if (self.activityView.isAnimating) {
                [self.activityView stopAnimating];
            }
        }
            break;
        case VLCMediaPlayerStateBuffering: // 播放中緩沖狀態
        {
            // 顯示菊花
            if (!self.activityView.isAnimating) {
                [self.activityView startAnimating];
            }
        }
            break;
        case VLCMediaPlayerStatePlaying: // 被暫停后開始播放
        {
            if (self.activityView.isAnimating) {
                [self.activityView stopAnimating];
            }
        }
            break;
        case VLCMediaPlayerStatePaused:  // 播放后被暫停
        {
            if (self.activityView.isAnimating) {
                [self.activityView stopAnimating];
            }
        }
            break;

    }
}

// 播放時間改變的回調
- (void)mediaPlayerTimeChanged:(NSNotification *)aNotification
{
//    NSLog(@"mediaPlayerTimeChanged");
}

@end

```
  • VLCPlayerView.h/VLCPlayerView.m(播放View設計)

    //
    //  VLCPlayerView.h
    //  VLCDemo
    //
    //  Created by pocket on 16/6/28.
    //  Copyright ? 2016年 pocket. All rights reserved.
    //
    
    // VLC播放界面
    #import <UIKit/UIKit.h>
    
    @interface VLCPlayerView : UIView
    /**
     *  承載視頻的View
     */
    @property (nonatomic,strong) UIView *playView;
    /**
     *  視頻名稱
     */
    @property (nonatomic,copy) NSString *videoName;
    /**
     *  剩余時間
     */
    @property (nonatomic,copy) NSString *remainingTime;
    /**
     *  當前時間
     */
    @property (nonatomic,copy) NSString *currentTime;
    /**
     *  當前進度
     */
    @property (nonatomic,assign) float sliderValue;
    /**
     *  返回按鈕監聽回調
     */
    @property (nonatomic,copy) void (^backBlock)(void);
    /**
     *  屏幕鎖監聽回調
     */
    @property (nonatomic,copy) void (^lockBlock)(UIButton *lockBtn);
    /**
     *  播放(暫停)監聽回調
     */
    @property (nonatomic,copy) void (^playBlock)(UIButton *playBtn);
    /**
     *  進度條監聽回調
     */
    @property (nonatomic,copy) void (^changeSliderBlock)(UISlider *sliderView);
    /**
     *  拖動結束監聽(progress:幅度,type: 3:右 ,4:左)
     */
    @property (nonatomic,copy) void (^endPanGesture)(float progress,int type);
    /**
     *  變更播放(暫停)按鈕狀態
     */
    - (void)changePlayBtnState:(BOOL)select;
    @end
    
    
    //
    //  VLCPlayerView.m
    //  VLCDemo
    //
    //  Created by pocket on 16/6/28.
    //  Copyright ? 2016年 pocket. All rights reserved.
    //
    
    #import "VLCPlayerView.h"
    #import "CustomSlider.h"
    
    #define titleFontSize 15.0
    #define titleColor [UIColor whiteColor]
    #define backgroundViewColor RGB(0, 0, 0, 0.6)
    #define space 15.0
    #define alphaDef 0.5
    #define viewHeight 60.0
    #define gestureMinimumTranslation 20.0
    // 定義滑動手勢類型
    typedef enum {
        PlayerMoveDirectionNone = 0,
        PlayerMoveDirectionUp,
        PlayerMoveDirectionDown,
        PlayerMoveDirectionRight,
        PlayerMoveDirectionLeft
    }PlayerMoveDirection;
    
    @interface VLCPlayerView()
    /*********************頂部欄*************************************/
    @property (nonatomic,strong) UIView *topView; // 頂部view
    @property (nonatomic,strong) UIButton *backBtn; // 返回按鈕
    @property (nonatomic,strong) UILabel *nameLabel; // 名字
    
    /*********************底部欄*************************************/
    @property (nonatomic,strong) UIView *bottomView;// 底部View
    // 開始播放(暫停)按鈕
    @property (nonatomic,strong) UIButton *playBtn;
    @property (nonatomic,strong) CustomSlider *sliderView; // 滑動條
    // 當前時間標簽
    @property (nonatomic,strong) UILabel *currentTimeLabel;
    // 結束時間標簽
    @property (nonatomic,strong) UILabel *endTimeLabel;
    
    /*********************快進/快退顯示********************************/
    @property (nonatomic,strong) UIView *baseView;// 快進/快退底View
    @property (nonatomic,strong) UIButton *changeBtn;
    @property (nonatomic,strong) UILabel *progressTitle;
    @property (nonatomic,strong) UIProgressView *progressView;
    
    // 屏幕鎖
    @property (nonatomic,strong) UIButton *lockBtn;
    
    // sliderView是否正在滑動
    @property (nonatomic,assign) BOOL isSliding;
    
    // 滑動手勢類型
    @property (nonatomic,assign) PlayerMoveDirection moveDirection;
    // 開始拖動的位置
    @property (nonatomic,assign) CGPoint beginPoint;
    // 當前拖動的位置
    @property (nonatomic,assign) CGPoint currentPoint;
    @end
    
    @implementation VLCPlayerView
    
    - (instancetype)initWithFrame:(CGRect)frame
    {
        if (self = [super initWithFrame:frame]) {
            [self setUI];
            // 添加手勢
            [self addGesture];
    
            // 5秒后自動隱藏
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                if (self.topView.alpha>0.0) {
                    [self oneGestureClick];
                }
            });
        }
    
        return self;
    }
    
    - (void)setUI
    {
        // 承載視頻view
        self.playView = [[UIView alloc] init];
        self.playView.backgroundColor = [UIColor blackColor];
        [self addSubview:self.playView];
    
        // 頂部View
        self.topView = [[UIView alloc] init];
        self.topView.backgroundColor = backgroundViewColor;
        [self addSubview:self.topView];
        // 名字
        self.nameLabel = [[UILabel alloc] init];
        self.nameLabel.textColor = titleColor;
        self.nameLabel.textAlignment = NSTextAlignmentCenter;
        self.nameLabel.font = [UIFont systemFontOfSize:titleFontSize];
        [self.topView addSubview:self.nameLabel];
        // 返回按鈕
        self.backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        [self.backBtn setImage:[UIImage imageNamed:@"ic_jt_bf"] forState:UIControlStateNormal];
        [self.backBtn addTarget:self action:@selector(backClick) forControlEvents:UIControlEventTouchUpInside];
        [self.topView addSubview:self.backBtn];
    
        // 底部view
        self.bottomView = [[UIView alloc] init];
        self.bottomView.backgroundColor = backgroundViewColor;
        [self addSubview:self.bottomView];
        // 暫停
        self.playBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        self.playBtn.selected = YES;
        [self.playBtn setImage:[UIImage imageNamed:@"ic_bf_zt"] forState:UIControlStateSelected];
        [self.playBtn setImage:[UIImage imageNamed:@"ic_bf_zt"] forState:UIControlStateNormal];
        [self.playBtn setAdjustsImageWhenHighlighted:NO]; // 設置無高亮狀態
        [self.playBtn addTarget:self action:@selector(playBtnClick) forControlEvents:UIControlEventTouchUpInside];
        [self.bottomView addSubview:self.playBtn];
        // 當前標簽
        self.currentTimeLabel = [[UILabel alloc] init];
        self.currentTimeLabel.textColor = titleColor;
        self.currentTimeLabel.text = @"--:--";
        self.currentTimeLabel.textAlignment = NSTextAlignmentLeft;
        self.currentTimeLabel.font = [UIFont systemFontOfSize:titleFontSize];
        [self.bottomView addSubview:self.currentTimeLabel];
        // 結束標簽
        self.endTimeLabel = [[UILabel alloc] init];
        self.endTimeLabel.textColor = titleColor;
        self.endTimeLabel.text = @"--:--";
        self.endTimeLabel.textAlignment = NSTextAlignmentRight;
        self.endTimeLabel.font = [UIFont systemFontOfSize:titleFontSize];
        [self.bottomView addSubview:self.endTimeLabel];
        // 滑動條
        self.sliderView = [[CustomSlider alloc] init];
        self.sliderView.minimumTrackTintColor = TedcallStorageProgressColor; // 設置滑動過的顏色
        self.sliderView.maximumTrackTintColor = [UIColor grayColor]; // 設置總長度顏色
        self.sliderView.thumbTintColor = RGB(255, 255, 255, 0.7); // 設置滑塊顏色
        [self.sliderView addTarget:self action:@selector(slideringListening) forControlEvents:UIControlEventValueChanged];
        [self.sliderView addTarget:self action:@selector(sliderChange) forControlEvents:UIControlEventTouchUpInside];
        [self.bottomView addSubview:self.sliderView];
    
        // 快進/快退view
        self.baseView = [[UIView alloc] init];
        self.baseView.backgroundColor = backgroundViewColor;
        self.baseView.layer.cornerRadius = 8.0;
        self.baseView.layer.masksToBounds = YES;
        self.baseView.alpha = 0.0;
        [self addSubview:self.baseView];
    
        self.changeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        [self.changeBtn setImage:[UIImage imageNamed:@"ic_kj_bf"] forState:UIControlStateNormal];
        [self.changeBtn setImage:[UIImage imageNamed:@"ic_kt_bf"] forState:UIControlStateSelected];
        [self.baseView addSubview:self.changeBtn];
    
        self.progressTitle = [[UILabel alloc] init];
        self.progressTitle.textColor = titleColor;
        self.progressTitle.textAlignment = NSTextAlignmentCenter;
        self.progressTitle.font = [UIFont systemFontOfSize:titleFontSize];
    //    self.progressTitle.text = @"30秒";
        [self.baseView addSubview:self.progressTitle];
    
        self.progressView = [[UIProgressView alloc] init];
        self.progressView.trackTintColor = [UIColor clearColor];
        self.progressView.progressTintColor = TedcallStorageProgressColor;
        [self.progressView setProgress:0.0];
        [self.baseView addSubview:self.progressView];
    
        // 屏幕鎖
        self.lockBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        [self.lockBtn setImage:[UIImage imageNamed:@"ic_sp_bf"] forState:UIControlStateNormal];
        [self.lockBtn setImage:[UIImage imageNamed:@"ic_ks_bf"] forState:UIControlStateSelected];
        [self.lockBtn addTarget:self action:@selector(lockClick) forControlEvents:UIControlEventTouchUpInside];
        [self addSubview:self.lockBtn];
    }
    
    - (void)setVideoName:(NSString *)videoName
    {
        _videoName = videoName;
        self.nameLabel.text = videoName;
    }
    
    - (void)setCurrentTime:(NSString *)currentTime
    {
        _currentTime = currentTime;
        self.currentTimeLabel.text = currentTime;
    }
    
    - (void)setRemainingTime:(NSString *)remainingTime
    {
        _remainingTime = remainingTime;
        self.endTimeLabel.text = remainingTime;
    }
    
    - (void)setSliderValue:(float)sliderValue
    {
        _sliderValue = sliderValue;
        if (!self.isSliding) { // 防止滑動過程中的手動設值
            [self .sliderView setValue:sliderValue animated:YES];
        }
    }
    
    // 返回按鈕監聽
    - (void)backClick
    {
        if (self.backBlock) {
            self.backBlock(); // block返回回調
        }
    }
    
    // 播放(暫停)監聽
    - (void)playBtnClick
    {
        NSLog(@"twoGesture");
        self.playBtn.selected = !self.playBtn.selected;
        [self changePlayBtnState:self.playBtn.selected];
        if (self.playBlock) {
            self.playBlock(self.playBtn);
        }
    }
    
    // 屏幕鎖監聽
    - (void)lockClick
    {
        self.lockBtn.selected = !self.lockBtn.selected;
        if (self.lockBtn.selected) {
            self.playView.userInteractionEnabled = NO;
            [UIView transitionWithView:self.lockBtn duration:1.0 options:0 animations:^{
                self.topView.alpha = 0.0;
                self.bottomView.alpha = 0.0;
                self.lockBtn.alpha = 0.0;
            } completion:^(BOOL finished) {
    
            }];
        } else {
            self.topView.alpha = 1.0;
            self.bottomView.alpha = 1.0;
            self.playView.userInteractionEnabled = YES;
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [UIView transitionWithView:self.lockBtn duration:1.0 options:0 animations:^{
                    self.topView.alpha = 0.0;
                    self.bottomView.alpha = 0.0;
                    self.lockBtn.alpha = 0.0;
                } completion:^(BOOL finished) {
    
                }];
            });
        }
    }
    
    - (void)changePlayBtnState:(BOOL)select
    {
        self.playBtn.selected = select;
        if (select) {
            [self.playBtn setImage:[UIImage imageNamed:@"ic_bf_zt"] forState:UIControlStateNormal];
        } else {
            [self.playBtn setImage:[UIImage imageNamed:@"ic_bf_ks"] forState:UIControlStateNormal];
        }
    }
    
    // 滑動結束監聽
    - (void)sliderChange
    {
    //    NSLog(@"滑動結束");
        self.isSliding = NO;
        if (self.changeSliderBlock) {
            self.changeSliderBlock(self.sliderView);
        }
    }
    
    // 滑動監聽
    - (void)slideringListening
    {
    //    NSLog(@"正在滑動");
        if (!self.isSliding) {
            self.isSliding = YES;
        }
    
    }
    
    - (void)layoutSubviews
    {
        [super layoutSubviews];
    
        CGFloat width = self.frame.size.width;
        CGFloat height = self.frame.size.height;
    
        // 承載視頻View
        self.playView.frame = self.bounds;
    
        // 頂部view
        CGFloat topX = 0;
        CGFloat topY = 0;
        CGFloat topW = width;
        CGFloat topH = viewHeight - 10;
        self.topView.frame = CGRectMake(topX, topY, topW, topH);
        // 返回按鈕
        CGFloat backW = 30.0;
        CGFloat backH = backW;
        CGFloat backX = space;
        CGFloat backY = (topH - backH)/2;
        self.backBtn.frame = CGRectMake(backX, backY, backW, backH);
        // 視頻名稱
        CGFloat nameX = space + CGRectGetMaxX(self.backBtn.frame);
        CGFloat nameH = 20.0;
        CGFloat nameW = width - 2*nameX;
        CGFloat nameY = (topH - nameH)/2;
        self.nameLabel.frame = CGRectMake(nameX, nameY, nameW, nameH);
        // 底部view
        CGFloat bottomX = 0;
        CGFloat bottomY = height - viewHeight;
        CGFloat bottomW = width;
        CGFloat bottomH = viewHeight;
        self.bottomView.frame = CGRectMake(bottomX, bottomY, bottomW, bottomH);
        // 播放(暫停)按鈕
        CGFloat playW = 40.0;
        CGFloat playH = playW;
        CGFloat playX = space;
        CGFloat playY = (viewHeight - playH)/2;
        self.playBtn.frame = CGRectMake(playX, playY, playW, playH);
        // 滑動條
        CGFloat sliderX = space + CGRectGetMaxY(self.playBtn.frame);
        CGFloat sliderH = 6;
        CGFloat sliderW = width - sliderX - space;
        CGFloat sliderY = playY + playH/2 - 4;
        self.sliderView.frame = CGRectMake(sliderX, sliderY, sliderW, sliderH);
        // 當前標簽
        CGFloat currentX = sliderX;
        CGFloat currentY = CGRectGetMaxY(self.sliderView.frame);
        CGFloat currentH = 20.0;
        CGFloat currentW = sliderW/2;
        self.currentTimeLabel.frame = CGRectMake(currentX, currentY, currentW, currentH);
        // 結束標簽
        CGFloat endW = currentW;
        CGFloat endX = CGRectGetMaxX(self.sliderView.frame) - endW;
        CGFloat endY = currentY;
        CGFloat endH = currentH;
        self.endTimeLabel.frame = CGRectMake(endX, endY, endW, endH);
    
        // 快進/快退view
        CGFloat baseW = 140.0;
        CGFloat baseH = 74.0;
        CGFloat baseY = CGRectGetMaxY(self.topView.frame) + 20.0;
        CGFloat baseX = (width - baseW)/2;
        self.baseView.frame = CGRectMake(baseX, baseY, baseW, baseH);
    
        CGFloat changeW = 30.0;
        CGFloat changeH = 20.0;
        CGFloat changeX = (baseW - changeW)/2;
        CGFloat changeY = 8.0;
        self.changeBtn.frame = CGRectMake(changeX, changeY, changeW, changeH);
    
        CGFloat titleY = 8.0 + CGRectGetMaxY(self.changeBtn.frame);
        CGFloat titleX = 0;
        CGFloat titleW = baseW;
        CGFloat titleH = 20.0;
        self.progressTitle.frame = CGRectMake(titleX, titleY, titleW, titleH);
    
        CGFloat progressX = 3.0;
        CGFloat progressY = 8.0 + CGRectGetMaxY(self.progressTitle.frame);
        CGFloat progressW = baseW - 2*progressX;
        CGFloat progressH = 0.0;
        self.progressView.frame = CGRectMake(progressX, progressY, progressW, progressH);
    
        // 屏幕鎖
        CGFloat lockX = space;
        CGFloat lockW = 50.0;
        CGFloat lockH = 50.0;
        CGFloat lockY = (height - lockH)/2;
        self.lockBtn.frame = CGRectMake(lockX, lockY, lockW, lockH);
    }
    #pragma mark - -- 手勢操作
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        NSLog(@"touchesBegan");
        if (self.lockBtn.selected) { // 鎖屏狀態
            [UIView transitionWithView:self.lockBtn duration:1.0 options:0 animations:^{
                self.lockBtn.alpha = 1.0;
            } completion:^(BOOL finished) {
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    if (self.lockBtn.selected) {
                        [UIView transitionWithView:self.lockBtn duration:1.0 options:0 animations:^{
                            self.lockBtn.alpha = 0.0;
                        } completion:^(BOOL finished) {
    
                        }];
                    }
                });
            }];
        }
    }
    // 添加手勢處理
    - (void)addGesture
    {
        // 單點
        UITapGestureRecognizer *oneGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(oneGestureClick)];
        oneGesture.numberOfTapsRequired = 1; // 單擊
        oneGesture.numberOfTouchesRequired = 1; // 單指單擊
        [self.playView addGestureRecognizer:oneGesture];
    
        // 雙擊
        UITapGestureRecognizer *twoGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(playBtnClick)];
        twoGesture.numberOfTapsRequired = 2; // 雙擊
        twoGesture.numberOfTouchesRequired = 1; // 單指雙擊
        [self.playView addGestureRecognizer:twoGesture];
    
        // 長按
        UILongPressGestureRecognizer *longGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longGestureClick:)];
        longGesture.minimumPressDuration = 3.0; // 長按3秒觸發
        [self.playView addGestureRecognizer:longGesture];
    
        // 拖動
        UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureClick:)];
        [self.playView addGestureRecognizer:panGesture];
    
        //解決拖動和長按手勢之間的沖突
        [longGesture requireGestureRecognizerToFail:panGesture];
        // 解決單擊和雙擊手勢的沖突
        [oneGesture requireGestureRecognizerToFail:twoGesture];
    }
    // 單擊手勢監聽
    - (void)oneGestureClick
    {
        NSLog(@"oneGestureClick");
        if (self.topView.alpha<=0.0) { // 顯示
            // 動畫顯示
            [UIView transitionWithView:self.topView duration:1.0 options:0 animations:^{
                self.topView.alpha = 1.0;
                self.bottomView.alpha = 1.0;
                self.lockBtn.alpha = 1.0;
            } completion:^(BOOL finished) {
                // 5秒后自動隱藏
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    if (self.topView.alpha>0.0) {
                        [self oneGestureClick];
                    }
                });
            }];
        } else {
            // 動畫隱藏
            [UIView transitionWithView:self.topView duration:1.0 options:0 animations:^{
                self.topView.alpha = 0.0;
                self.bottomView.alpha = 0.0;
                self.lockBtn.alpha = 0.0;
            } completion:^(BOOL finished) {
    
            }];
        }
    }
    
    // 長按監聽
    - (void)longGestureClick:(UILongPressGestureRecognizer *)longGesture
    {
        // 長按手勢會調用多次監聽方法,先判斷手勢狀態
        if (longGesture.state == UIGestureRecognizerStateBegan) {
            NSLog(@"長按開始");
        }
    }
    
    // 拖動監聽
    - (void)panGestureClick:(UIPanGestureRecognizer *)panGesture
    {
        CGPoint translation = [panGesture translationInView:self.playView]; // 取得相對位置的偏移點(相對位置為手指第一次在屏幕的點)
        NSLog(@"translation:%@",NSStringFromCGPoint(translation));
        if (panGesture.state == UIGestureRecognizerStateBegan){
            NSLog (@"滑動開始");
            self.beginPoint = translation;
            self.currentPoint = translation;
        }else if (panGesture.state == UIGestureRecognizerStateChanged){
            self.moveDirection = [self determineCameraDirectionIfNeeded:translation];
            switch (self.moveDirection) {
                case PlayerMoveDirectionDown:
                    NSLog(@"PlayerMoveDirectionDown");
                    break;
                case PlayerMoveDirectionUp:
                    NSLog(@"PlayerMoveDirectionUp");
                    break;
                case PlayerMoveDirectionRight:
                {
                    NSLog(@"PlayerMoveDirectionRight");
                    self.changeBtn.selected = NO;
                    self.progressTitle.text = CustomLocalizedString(@"快進", nil);
                    if (self.baseView.alpha == 0.0) {
                        self.baseView.alpha = 1.0;
                    }
                }
                    break;
                case PlayerMoveDirectionLeft:
                {
                    NSLog(@"PlayerMoveDirectionLeft");
                    self.changeBtn.selected = YES;
                    self.progressTitle.text = CustomLocalizedString(@"快退", nil);
                    if (self.baseView.alpha == 0.0) {
                        self.baseView.alpha = 1.0;
                    }
                }
                    break;
                default :
                    break;
            }
            self.currentPoint = translation; // 刷新當前位置
    
        }else if (panGesture.state == UIGestureRecognizerStateEnded){
            NSLog (@"滑動結束");
            [UIView transitionWithView:self.baseView duration:1.0 options:0 animations:^{
                self.baseView.alpha = 0.0;
            } completion:^(BOOL finished) {
    
            }];
            if (self.endPanGesture) {
                self.endPanGesture(0.5,self.moveDirection);
            }
        }
    }
    
    - (PlayerMoveDirection) determineCameraDirectionIfNeeded:(CGPoint)translation
    {
        // 設定一個幅度使拖動在不夠水平(略微有點偏差)的方向上的處理,上下一般不處理
        if (translation.x > self.currentPoint.x && (fabs(translation.y - self.currentPoint.y) <= gestureMinimumTranslation)){ // 說明水平向右拖動了
            return PlayerMoveDirectionRight;
        }else if(translation.x < self.currentPoint.x && (fabs(translation.y - self.currentPoint.y) <= gestureMinimumTranslation)){ // 說明水平向左
            return PlayerMoveDirectionLeft;
        }else if (translation.x == self.currentPoint.x && translation.y > self.currentPoint.y) { // 向下
            return PlayerMoveDirectionDown;
        } else if (translation.x == self.currentPoint.x && translation.y < self.currentPoint.y) { // 向上
            return PlayerMoveDirectionUp;
        } else{
            return PlayerMoveDirectionNone;
        }
    }
    
    - (void)updateFastImage:(int)type
    {
        if (type == 4) { // 左
    
        } else if (type == 3) { // 右
    
        }
    }
    
    @end
    
    
  • CustomSlider.h/CustomSlider.m(自定義滑塊)

    //
    //  CustomSlider.h
    //  TedcallStorage
    //
    //  Created by  tedcall on 16/6/28.
    //  Copyright ? 2016年 pocket. All rights reserved.
    //
    
    #import <UIKit/UIKit.h>
    
    @interface CustomSlider : UISlider
    
    @end
    
    
    //
    //  CustomSlider.m
    //  TedcallStorage
    //
    //  Created by  tedcall on 16/6/28.
    //  Copyright ? 2016年 pocket. All rights reserved.
    //
    
    #import "CustomSlider.h"
    
    @implementation CustomSlider
    
    - (void)drawRect:(CGRect)rect
    {
        UIImageView *imageView = nil;
        // 取得滑塊View
        for (UIView *view in self.subviews) {
            if ([view isKindOfClass:[UIImageView class]]) {
                if (view.frame.size.width == view.frame.size.height) {
                    imageView = (UIImageView *)view;
                }
            }
        }
    
        if (imageView) { // 有值
            CGFloat redViewW = 8.0;
            CGFloat redViewH =redViewW;
            CGFloat redViewX = (imageView.frame.size.width - redViewW)/2;
            CGFloat redViewY = (imageView.frame.size.height - redViewH)/2;
            UIView *redView = [[UIView alloc] initWithFrame:CGRectMake(redViewX, redViewY, redViewW, redViewH)];
            redView.backgroundColor = RGB(255, 255, 255, 0.7);
            redView.layer.cornerRadius = redViewW/2;
            redView.layer.masksToBounds = YES;
            [imageView addSubview:redView];
        }
    }
    
    // 重寫進度條frame
    - (CGRect)trackRectForBounds:(CGRect)bounds {
        return CGRectMake(0,0,bounds.size.width,6.0);
    }
    
    // 解決兩邊空隙問題
    - (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value
    {
        rect.origin.x = rect.origin.x - 10 ;
        rect.size.width = rect.size.width +20;
        return CGRectInset ([super thumbRectForBounds:bounds trackRect:rect value:value], 10 , 10);
    }
    
    @end
    
    

VLC Demo

因為VLC庫比較大所以github 上的demo里并不包含這個庫以及測試時的視頻。我另外提供了下載地址供需要的朋友自行下載。
demo地址:https://github.com/pocket-live/VLC-Demo
VLC庫和測試資源下載地址:http://pan.baidu.com/s/1pLiBQKb

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,412評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,514評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,373評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,975評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,743評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,199評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,262評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,414評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,951評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,780評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,527評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,218評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,649評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,889評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,673評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容