基於MobileVLCKit的視頻播放器的二次封裝

Demo地址:https://github.com/xttxqjfg/videoDemo,喜歡的可以星星~

爲了適應項目的需要,參照網上的例子,對MobileVLCKit做了一個二次封裝。

主要功能有全屏、關閉全屏、自動全屏、暫停、播放、快進等基本功能。

項目中用到的是pods來集成MobileVLCKit。pods中的包下載下來在本地看有700多M,集成時保證好網絡環境,不然要等很久,不過這700M不會全部加到ipa包中,編譯後只有很小的幾M。

platform:ios,’8.0’
target “videoDemo” do

pod 'MobileVLCKit', '~> 2.2.2'

end
下面是封裝的源代碼段

//
//  SMAVideoPlayerConst.h
//  videoDemo
//

#define kMediaLength self.player.media.length
#define kRGB(r,g,b) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1]
#define kSCREEN_BOUNDS [[UIScreen mainScreen] bounds]

static const CGFloat SMA_ProgressWidth = 3.0f;
static const CGFloat SMA_VideoControlBarHeight = 40.0;
static const CGFloat SMA_VideoControlSliderHeight = 10.0;
static const CGFloat SMA_VideoControlAnimationTimeinterval = 0.3;
static const CGFloat SMA_VideoControlTimeLabelFontSize = 10.0;
static const CGFloat SMA_VideoControlBarAutoFadeOutTimeinterval = 4.0;
static const CGFloat SMA_VideoControlCorrectValue = 3;
static const CGFloat SMA_VideoControlAlertAlpha = 0.75;
#import <UIKit/UIKit.h>

@interface SMAVideoToolsView : UIView

//頂部的工具欄,目前包含關閉按鈕
@property (nonatomic,strong) UIView *topBar;
//底部的工具欄,目前包含播放、暫停、全屏、進度條、時間等
@property (nonatomic,strong) UIView *bottomBar;
//播放按鈕
@property (nonatomic,strong) UIButton *playBtn;
//暫停按鈕
@property (nonatomic,strong) UIButton *pauseBtn;
//全屏按鈕
@property (nonatomic, strong) UIButton *fullScreenBtn;
//退出全屏按鈕
@property (nonatomic, strong) UIButton *smallScreenBtn;
//進度條
@property (nonatomic, strong) UISlider *progressSlider;
//退出按鈕
@property (nonatomic, strong) UIButton *closeBtn;
//時間
@property (nonatomic, strong) UILabel *timeLabel;
//背景層
@property (nonatomic, strong) CALayer *bgLayer;

//動畫消失
- (void)animateHide;
//動畫顯示
- (void)animateShow;

//取消延時執行
- (void)autoFadeOutControlBar;
//取消延時執行
- (void)cancelAutoFadeOutControlBar;

@end
//
//  SMAVideoToolsView.m
//  videoDemo
//

#import "SMAVideoToolsView.h"
#import <MediaPlayer/MediaPlayer.h>
#import "SMAVideoPlayerConst.h"

@interface SMAVideoToolsView()
//點擊手勢
@property (nonatomic, strong) UIPanGestureRecognizer *tapGesture;

@end

@implementation SMAVideoToolsView

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self setupView];
    }
    return self;
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    
    self.topBar.frame             = CGRectMake(CGRectGetMinX(self.bounds), CGRectGetMinY(self.bounds), CGRectGetWidth(self.bounds), SMA_VideoControlBarHeight);
    
    self.closeBtn.frame        = CGRectMake(0, CGRectGetMinX(self.topBar.bounds), CGRectGetWidth(self.closeBtn.bounds), CGRectGetHeight(self.closeBtn.bounds));
    
    self.bottomBar.frame          = CGRectMake(CGRectGetMinX(self.bounds), CGRectGetHeight(self.bounds) - SMA_VideoControlBarHeight, CGRectGetWidth(self.bounds), SMA_VideoControlBarHeight);
    
    self.progressSlider.frame     = CGRectMake(0, -0, CGRectGetWidth(self.bounds), SMA_VideoControlSliderHeight);
    
    self.playBtn.frame         = CGRectMake(CGRectGetMinX(self.bottomBar.bounds), CGRectGetHeight(self.bottomBar.bounds)/2 - CGRectGetHeight(self.playBtn.bounds)/2 + CGRectGetHeight(self.progressSlider.frame) * 0.6, CGRectGetWidth(self.playBtn.bounds), CGRectGetHeight(self.playBtn.bounds));
    
    self.pauseBtn.frame        = self.playBtn.frame;
    
    self.fullScreenBtn.frame   = CGRectMake(CGRectGetWidth(self.bottomBar.bounds) - CGRectGetWidth(self.fullScreenBtn.bounds) - 5, self.playBtn.frame.origin.y, CGRectGetWidth(self.fullScreenBtn.bounds), CGRectGetHeight(self.fullScreenBtn.bounds));
    
    self.smallScreenBtn.frame = self.fullScreenBtn.frame;
    
    self.timeLabel.frame          = CGRectMake(CGRectGetMaxX(self.playBtn.frame), self.playBtn.frame.origin.y, CGRectGetWidth(self.bottomBar.bounds), CGRectGetHeight(self.timeLabel.bounds));
    
}

#pragma mark - 私有方法
- (void)setupView {
    
    self.backgroundColor = [UIColor clearColor];
    
    [self.layer addSublayer:self.bgLayer];
    [self addSubview:self.topBar];
    [self addSubview:self.bottomBar];
    
    [self.topBar    addSubview:self.closeBtn];
    [self.bottomBar addSubview:self.playBtn];
    [self.bottomBar addSubview:self.pauseBtn];
    [self.bottomBar addSubview:self.fullScreenBtn];
    [self.bottomBar addSubview:self.smallScreenBtn];
    [self.bottomBar addSubview:self.progressSlider];
    [self.bottomBar addSubview:self.timeLabel];
    
    self.pauseBtn.hidden = YES;
    self.smallScreenBtn.hidden = YES;
    
    [self addGestureRecognizer:self.tapGesture];
}

- (void)responseTapImmediately {
    self.bottomBar.alpha == 0 ? [self animateShow] : [self animateHide];
}

#pragma mark - 代理方法

#pragma mark - 接口方法

/**
 隱藏工具欄,全屏效果
 */
- (void)animateHide
{
    [UIView animateWithDuration:SMA_VideoControlAnimationTimeinterval animations:^{
        self.topBar.alpha = 0;
        self.bottomBar.alpha = 0;
    } completion:^(BOOL finished) {
    }];
}

/**
 顯示工具欄
 */
- (void)animateShow
{
    [UIView animateWithDuration:SMA_VideoControlAnimationTimeinterval animations:^{
        self.topBar.alpha = 1;
        self.bottomBar.alpha = 1;
    } completion:^(BOOL finished) {
        [self autoFadeOutControlBar];
    }];
}

- (void)autoFadeOutControlBar
{
    //如果有延遲執行在隊列中,則取消延時執行
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(animateHide) object:nil];
    //開始延遲執行
    [self performSelector:@selector(animateHide) withObject:nil afterDelay:SMA_VideoControlBarAutoFadeOutTimeinterval];
}

- (void)cancelAutoFadeOutControlBar
{
    //如果有延遲執行在隊列中,則取消延時執行
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(animateHide) object:nil];
}

#pragma mark - 屏幕觸摸事件
- (void)tapGestureAction:(UIPanGestureRecognizer *)pan {
    
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    if (touch.tapCount > 0) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self responseTapImmediately];
        });
    }
}

- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self responseTapImmediately];
}

#pragma mark - 懶加載
- (UIView *)topBar
{
    if (!_topBar) {
        _topBar = [UIView new];
        _topBar.backgroundColor = [UIColor clearColor];
    }
    return _topBar;
}

- (UIView *)bottomBar
{
    if (!_bottomBar) {
        _bottomBar = [UIView new];
        _bottomBar.backgroundColor = kRGB(27, 27, 27);
    }
    return _bottomBar;
}

- (UIButton *)playBtn
{
    if (!_playBtn) {
        _playBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        [_playBtn setImage:[UIImage imageNamed:@"Play"] forState:UIControlStateNormal];
        _playBtn.bounds = CGRectMake(0, 0, SMA_VideoControlBarHeight, SMA_VideoControlBarHeight);
    }
    return _playBtn;
}

- (UIButton *)pauseBtn
{
    if (!_pauseBtn) {
        _pauseBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        [_pauseBtn setImage:[UIImage imageNamed:@"Pause"] forState:UIControlStateNormal];
        _pauseBtn.bounds = CGRectMake(0, 0, SMA_VideoControlBarHeight, SMA_VideoControlBarHeight);
    }
    return _pauseBtn;
}

- (UIButton *)fullScreenBtn
{
    if (!_fullScreenBtn) {
        _fullScreenBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        [_fullScreenBtn setImage:[UIImage imageNamed:@"FullScreen"] forState:UIControlStateNormal];
        _fullScreenBtn.bounds = CGRectMake(0, 0, SMA_VideoControlBarHeight, SMA_VideoControlBarHeight);
    }
    return _fullScreenBtn;
}

- (UIButton *)smallScreenBtn
{
    if (!_smallScreenBtn) {
        _smallScreenBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        [_smallScreenBtn setImage:[UIImage imageNamed:@"MinScreen"] forState:UIControlStateNormal];
        _smallScreenBtn.bounds = CGRectMake(0, 0, SMA_VideoControlBarHeight, SMA_VideoControlBarHeight);
    }
    return _smallScreenBtn;
}


- (UISlider *)progressSlider
{
    if (!_progressSlider) {
        _progressSlider = [[UISlider alloc] init];
        [_progressSlider setThumbImage:[UIImage imageNamed:@"PlayerNob"] forState:UIControlStateNormal];
        [_progressSlider setMinimumTrackTintColor:kRGB(239, 71, 94)];
        [_progressSlider setMaximumTrackTintColor:kRGB(157, 157, 157)];
        [_progressSlider setBackgroundColor:[UIColor clearColor]];
        _progressSlider.value = 0.f;
        _progressSlider.continuous = YES;
    }
    return _progressSlider;
}

- (UIButton *)closeBtn
{
    if (!_closeBtn) {
        _closeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        [_closeBtn setImage:[UIImage imageNamed:@"PlayerClose"] forState:UIControlStateNormal];
        _closeBtn.bounds = CGRectMake(0, 0, SMA_VideoControlBarHeight, SMA_VideoControlBarHeight);
    }
    return _closeBtn;
}

- (UILabel *)timeLabel
{
    if (!_timeLabel) {
        _timeLabel = [UILabel new];
        _timeLabel.backgroundColor = [UIColor clearColor];
        _timeLabel.font = [UIFont systemFontOfSize:SMA_VideoControlTimeLabelFontSize];
        _timeLabel.textColor = [UIColor lightGrayColor];
        _timeLabel.textAlignment = NSTextAlignmentLeft;
        _timeLabel.bounds = CGRectMake(0, 0, SMA_VideoControlTimeLabelFontSize, SMA_VideoControlBarHeight);
    }
    return _timeLabel;
}

- (CALayer *)bgLayer {
    if (!_bgLayer) {
        _bgLayer = [CALayer layer];
        _bgLayer.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"VideoBg"]].CGColor;
        _bgLayer.bounds = self.frame;
        _bgLayer.position = CGPointMake(CGRectGetWidth(self.bounds) / 2, CGRectGetHeight(self.bounds) / 2);
    }
    return _bgLayer;
}

- (UIPanGestureRecognizer *)tapGesture {
    if (!_tapGesture) {
        _tapGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureAction:)];
    }
    return _tapGesture;
}


@end
#import <UIKit/UIKit.h>

@interface SMAVideoPlayer : UIView

@property (nonatomic,strong) NSURL *mediaURL;
@property (nonatomic,assign) BOOL isFullscreenModel;

- (void)showInView:(UIView *)view;

@end
//
//  SMAVideoPlayer.m
//  videoDemo
//

#import "SMAVideoPlayer.h"
#import <MobileVLCKit/MobileVLCKit.h>
#import "SMAVideoToolsView.h"
#import <MediaPlayer/MediaPlayer.h>
#import <AVFoundation/AVFoundation.h>
#import "SMAVideoPlayerConst.h"

@interface SMAVideoPlayer()<VLCMediaPlayerDelegate>
{
    CGRect _originFrame;
}
@property (nonatomic,strong) VLCMediaPlayer *player;
@property (nonatomic, nonnull,strong) SMAVideoToolsView *toolsView;
@end

@implementation SMAVideoPlayer

- (instancetype)init {
    if (self = [super init]) {
        [self setupNotification];
    }
    return self;
}

- (void)drawRect:(CGRect)rect {
    [super drawRect:rect];
    [self setupPlayer];
    [self setupView];
    [self setupControlView];
}

#pragma mark - 接口方法
- (void)showInView:(UIView *)view {
    
    NSAssert(_mediaURL != nil, @"MRVLCPlayer Exception: mediaURL could not be nil!");
    
    [view addSubview:self];
    
    self.alpha = 0.0;
    [UIView animateWithDuration:SMA_VideoControlAnimationTimeinterval animations:^{
        self.alpha = 1.0;
    } completion:^(BOOL finished) {
        [self play];
    }];
}

- (void)dismiss {
    [self.player stop];
    self.player.delegate = nil;
    self.player.drawable = nil;
    self.player = nil;
    
    // 註銷通知
    [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    [self removeFromSuperview];
}

#pragma mark - Private Method
- (void)setupView {
    [self setBackgroundColor:[UIColor blackColor]];
}

- (void)setupPlayer {
    [self.player setDrawable:self];
    self.player.media = [[VLCMedia alloc] initWithURL:self.mediaURL];
}

- (void)setupControlView {
    
    [self addSubview:self.toolsView];
    
    //添加控制界面的監聽方法
    [self.toolsView.playBtn addTarget:self action:@selector(playButtonClick) forControlEvents:UIControlEventTouchUpInside];
    [self.toolsView.pauseBtn addTarget:self action:@selector(pauseButtonClick) forControlEvents:UIControlEventTouchUpInside];
    [self.toolsView.closeBtn addTarget:self action:@selector(closeButtonClick) forControlEvents:UIControlEventTouchUpInside];
    [self.toolsView.fullScreenBtn addTarget:self action:@selector(fullScreenButtonClick) forControlEvents:UIControlEventTouchUpInside];
    [self.toolsView.smallScreenBtn addTarget:self action:@selector(shrinkScreenButtonClick) forControlEvents:UIControlEventTouchUpInside];
    [self.toolsView.progressSlider addTarget:self action:@selector(progressClick) forControlEvents:UIControlEventTouchUpInside];
}

- (void)setupNotification {
    
    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(orientationHandler)
                                                 name:UIDeviceOrientationDidChangeNotification
                                               object:nil
     ];
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(applicationWillEnterForeground)
                                                 name:UIApplicationWillEnterForegroundNotification
                                               object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(applicationWillResignActive)
                                                 name:UIApplicationWillResignActiveNotification
                                               object:nil];
}

/**
 *    強制橫屏
 *
 *    @param orientation 橫屏方向
 */
- (void)forceChangeOrientation:(UIInterfaceOrientation)orientation
{
    int val = 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]];
        [invocation setArgument:&val atIndex:2];
        [invocation invoke];
    }
}

#pragma mark Notification Handler
/**
 *    屏幕旋轉處理
 */
- (void)orientationHandler {
    
    if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) {
        self.isFullscreenModel = YES;
        
    }else {
        self.isFullscreenModel = NO;
    }
    [self.toolsView autoFadeOutControlBar];
}

/**
 *    即將進入後臺的處理
 */
- (void)applicationWillEnterForeground {
    [self play];
}

/**
 *    即將返回前臺的處理
 */
- (void)applicationWillResignActive {
    [self pause];
}


#pragma mark Button Event
- (void)playButtonClick {
    
    [self play];
    
}

- (void)pauseButtonClick {
    
    [self pause];
}

- (void)closeButtonClick {
    [self dismiss];
}

- (void)fullScreenButtonClick {
    
    [self forceChangeOrientation:UIInterfaceOrientationLandscapeRight];
}

- (void)shrinkScreenButtonClick {
    
    [self forceChangeOrientation:UIInterfaceOrientationPortrait];;
}

- (void)progressClick {
    
    int targetIntvalue = (int)(self.toolsView.progressSlider.value * (float)kMediaLength.intValue);
    
    VLCTime *targetTime = [[VLCTime alloc] initWithInt:targetIntvalue];
    
    [self.player setTime:targetTime];
    
    [self.toolsView autoFadeOutControlBar];
}

#pragma mark Player Logic
- (void)play {
    [self.player play];
    self.toolsView.playBtn.hidden = YES;
    self.toolsView.pauseBtn.hidden = NO;
    [self.toolsView autoFadeOutControlBar];
}

- (void)pause {
    [self.player pause];
    self.toolsView.playBtn.hidden = NO;
    self.toolsView.pauseBtn.hidden = YES;
    [self.toolsView autoFadeOutControlBar];
}

- (void)stop {
    [self.player stop];
    self.toolsView.progressSlider.value = 1;
    self.toolsView.playBtn.hidden = NO;
    self.toolsView.pauseBtn.hidden = YES;
}

#pragma mark - Delegate
#pragma mark VLC
- (void)mediaPlayerStateChanged:(NSNotification *)aNotification {
    // Every Time change the state,The VLC will draw video layer on this layer.
    [self bringSubviewToFront:self.toolsView];
    if (self.player.media.state == VLCMediaStateBuffering) {
        self.toolsView.bgLayer.hidden = NO;
    }else if (self.player.media.state == VLCMediaStatePlaying) {
        self.toolsView.bgLayer.hidden = YES;
    }else if (self.player.state == VLCMediaPlayerStateStopped) {
        [self stop];
    }else {
        self.toolsView.bgLayer.hidden = NO;
    }
    
}

- (void)mediaPlayerTimeChanged:(NSNotification *)aNotification {
    
    [self bringSubviewToFront:self.toolsView];
    
    if (self.toolsView.progressSlider.state != UIControlStateNormal) {
        return;
    }
    
    float precentValue = ([self.player.time.numberValue floatValue]) / ([kMediaLength.numberValue floatValue]);
    
    [self.toolsView.progressSlider setValue:precentValue animated:YES];
    
    [self.toolsView.timeLabel setText:[NSString stringWithFormat:@"%@/%@",_player.time.stringValue,kMediaLength.stringValue]];
}

#pragma mark - 懶加載
- (VLCMediaPlayer *)player {
    if (!_player) {
        _player = [[VLCMediaPlayer alloc] init];
        _player.delegate = self;
    }
    return _player;
}

- (SMAVideoToolsView *)toolsView {
    if (!_toolsView) {
        _toolsView = [[SMAVideoToolsView alloc] initWithFrame:self.bounds];
    }
    return _toolsView;
}


- (void)setIsFullscreenModel:(BOOL)isFullscreenModel {
    
    if (_isFullscreenModel == isFullscreenModel) {
        return;
    }
    
    _isFullscreenModel = isFullscreenModel;
    
    if (isFullscreenModel) {
        _originFrame = self.frame;
        CGFloat height = kSCREEN_BOUNDS.size.width;
        CGFloat width = kSCREEN_BOUNDS.size.height;
        CGRect frame = CGRectMake((height - width) / 2, (width - height) / 2, width, height);
        [UIView animateWithDuration:SMA_VideoControlAnimationTimeinterval animations:^{
            /**
             *    此判斷是爲了適配項目在Deployment Info中是否勾選了橫屏
             */
            if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) {
                self.frame = frame;
                self.transform = CGAffineTransformMakeRotation(M_PI_2);
            }else {
                self.frame = self.frame = kSCREEN_BOUNDS;
            }
            self.toolsView.frame = self.bounds;
            [self.toolsView layoutIfNeeded];
            self.toolsView.fullScreenBtn.hidden = YES;
            self.toolsView.smallScreenBtn.hidden = NO;
        } completion:^(BOOL finished) {}];
        
    }else {
        [UIView animateWithDuration:SMA_VideoControlAnimationTimeinterval animations:^{
            self.transform = CGAffineTransformIdentity;
            self.frame = _originFrame;
            self.toolsView.frame = self.bounds;
            [self.toolsView layoutIfNeeded];
            self.toolsView.fullScreenBtn.hidden = NO;
            self.toolsView.smallScreenBtn.hidden = YES;
            
        } completion:^(BOOL finished) {}];
        
        
    }
    
}
@end


vc中使用

//播放遠端視頻
- (IBAction)playRemoteVideo:(UIButton *)sender {
    SMAVideoPlayer *player = [[SMAVideoPlayer alloc] init];
    
    player.bounds = CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.width / 16 * 9);
    player.center = self.view.center;
    player.mediaURL = [NSURL URLWithString:@"http://172.20.90.117/www2/video/1.mp4"];
    
    [player showInView:self.view.window];
    
    [self prefersStatusBarHidden];
}

-(BOOL)prefersStatusBarHidden
{
    return YES;
}


效果圖如下






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章