AVPlayer实现简单播放器

  在我们之前的项目,视频的播放都是使用MPMoviePlayerViewController(包含了播放的UI,并封装好了播放的一些函数),但是苹果的官网表示从IOS9版本开始,MPMoviePlayerViewController被弃用了,推荐使用AVPictureInPictureController或者AVPlayerViewController。

  但是一直使用苹果封装的ViewController可控性依旧比较差,如:

(1)播放状态无法获得;

(2)置于后台和唤醒时,在不同设备上视频播放展现状态不同;

(3)视频播放进度与画面不同步;

(4)播放的声音和画面不同步;

(5)无法直接加其他UI,如:分享按钮等。

基于以上几点考虑,我决定直接用AVPlayer直接实现一个播放器,完成一些简单的播放功能。

基本功能:

(1)进度条、视频播放画面、播放时间同步;

(2)可以播放和暂停,且两种状态有不同UI切换;

(3)进度条可以拖动,且视频播放画面同步;

(4)点击关闭按钮可以销毁播放器;

(5)增加了AutoLayout布局约束

(6)视频的横竖屏切换播放与设备的横竖屏切换同步。

(7)一些小细节,如:进度条拖拽、点击、抬起时,播放/暂停按钮UI的变化;置于后台和唤醒,重新设置视频播放状态等。

代码如下:

头文件:ViewController.h

#import 
#import 

@interface ViewController : UIViewController

@property (nonatomic,strong)AVPlayer *avPlayer;

@property (nonatomic,strong)AVPlayerItem *avPlayerItem;

@property (nonatomic,strong)AVPlayerLayer *avPlayerLayer;

//装载视频layer、进度条、时间、时长等UI的容器
@property (nonatomic,strong)UIView *videoContainer;

//进度条UI
@property (nonatomic,strong)UISlider *progress;

//当前播放时间UI
@property (nonatomic,strong) UILabel *startLabel;

//视频总时长UI
@property (nonatomic,strong) UILabel *endLabel;

//关闭按钮
@property (nonatomic,strong) UIButton *closeButton;

//控制播放和暂停的按钮
@property (nonatomic,strong) UIButton *playButton;

//当前播放时间
@property (nonatomic,assign) float playheadTime;

//是否处于播放状态
@property (nonatomic,assign) BOOL isPlaying;

//播放帧的监视器
@property (nonatomic,strong) id playTimeObserver;

@end

实现:ViewController.m

#import "ViewController.h"

@interface ViewController ()

@end
static NSBundle *playerBundle;
NSString * const XAdVideoStatusKey = @"status";
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSString *urlString = @"要播放的视频地址 http://....";
    [self configPlayerControllerWithURL:urlString];
    
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

#pragma mark - public
- (void) configPlayerControllerWithURL:(NSString *)urlString{
    [self initBundle];
    [self setProgressRefProperties];
    [self showUI];
    NSURL *url = [NSURL URLWithString:urlString];
    [self setupPlayerWithURL:url];
    
}
/*
 * 重头播放
 */
- (void)play{
    if (self.avPlayer) {
        self.isPlaying = YES;
        [self.avPlayer play];
    }
}
/*
 * 暂停
 */
- (void)pause
{
    if (self.avPlayer) {
        [self.avPlayer pause];
        self.isPlaying = NO;
    }
    
}
/*
 * 销毁对象
 */
- (void)cancel {
    if (self.avPlayer) {
        [self.avPlayer pause];
    }
    [self removeUIView];
    [self removePlayerItemObserver];
    [self removePlayerObserver];
    [self destroyAllProperties];
    
}
/*
 * 从某一帧播放
 */
- (void)playFromTime:(double)time
{
    NSLog(@"playFromTime = %f",time);
    if (self.avPlayer) {
        [self seekToTime:time];
        [self.avPlayer play];
        self.isPlaying = YES;
    }
}
/*
 * 在某一帧暂停
 */
- (void)pauseOnTime:(double)time
{
    NSLog(@"pauseOnTime = %f",time);
    if (self.avPlayer) {
        [self seekToTime:time];
        [self.avPlayer pause];
        self.isPlaying = NO;
    }
}

#pragma mark - action event
/*
 * 点击播放或暂停按钮,控制播放状态
 */
- (void) onClickPlayOrPause{
    if (self.isPlaying) {
        [_playButton setImage:[self getImageFromBundle:playerBundle imageName:@"player_play"] forState:UIControlStateNormal];
        [self pauseOnTime:self.playheadTime];
    }else{
        [_playButton setImage:[self getImageFromBundle:playerBundle imageName:@"player_pause"] forState:UIControlStateNormal];
        if ([self playerReachedEnd]) {
            [self playFromTime:0];
        }else{
            [self playFromTime:self.playheadTime];
        }
    }
}
/*
 * 关闭按钮触发,销毁所有player和UI相关对象
 */
- (void) onClickClose{
    [self cancel];
}

#pragma mark - add observer
/*
 * 播放帧的观察者
 */
- (void)addPlayerObserver
{
    // add 应用置于后台、唤醒、屏幕旋转的监听器 (如果不需要此功能可以不添加)
    [self addAppObserver];
    CMTime interval = CMTimeMakeWithSeconds(0.1, NSEC_PER_USEC);
    __weak ViewController *selfWeak = self;
    self.playTimeObserver = [self.avPlayer addPeriodicTimeObserverForInterval:interval
                                                                        queue:NULL
                                                                   usingBlock:^(CMTime time) {
                                                                       // 当前的播放时间
                                                                       float currentTime = (float)CMTimeGetSeconds(time);
                                                                       // 刷新进度条以及时间Label的显示
                                                                       [selfWeak refreshSlide:currentTime];
                                                                   }];
}

- (void)addPlayerItemObserver{
    // 监测播放的状态(已就绪 / 失败 )
    [self.avPlayerItem addObserver:self forKeyPath:XAdVideoStatusKey options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidReachEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.avPlayerItem];
    
}

- (void)addAppObserver{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
    // 应用从后台唤醒
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];
    // 应用置于后台
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterBackground:) name:UIApplicationWillResignActiveNotification object:nil];
    // 屏幕旋转
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
}

#pragma mark - remove observer
- (void)removePlayerObserver{
    [self removeAppObserver];
    if (self.avPlayer && self.playTimeObserver) {
        [self.avPlayer removeTimeObserver:self.playTimeObserver];
    }
}

- (void)removePlayerItemObserver{
    [self.avPlayerItem removeObserver:self forKeyPath:XAdVideoStatusKey context:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:self.avPlayerItem];
}

- (void)removeAppObserver{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
}

#pragma mark - observer & notification
- (void)appDidBecomeActive:(NSNotification *)notification
{
    //后台唤醒继续播放
    [self playFromTime:self.playheadTime];
}

- (void)appDidEnterBackground:(NSNotification *)notification
{
    //置于后台停止播放
    [self pauseOnTime:self.playheadTime];
}

- (void)orientationChanged:(NSNotification *)notification{
    //屏幕旋转重新布局UI
    [self reloadVideoContainerView];
}
/*
 * 监测player的状态
 */
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
                        change:(NSDictionary *)change context:(void *)context
{
    if (object == self.avPlayerItem && [keyPath isEqualToString:XAdVideoStatusKey]) {
        if (self.avPlayerItem.status == AVPlayerItemStatusFailed) {
            NSLog(@"AVPlayerItemStatusFailed.");
        } else if (self.avPlayerItem.status == AVPlayerItemStatusReadyToPlay) {
            if (self.videoContainer) {
                self.avPlayerLayer.frame = [self.videoContainer bounds];
                NSLog(@"add playerLayer to video container.");
                [self.videoContainer.layer insertSublayer:self.avPlayerLayer atIndex:0];
                [self play];
            }
        }
    }
}
- (void) playerItemDidReachEnd:(NSNotification *)notification{
    // 播放结束,改变播放暂停按钮的状态
    [self changePlayOrPauseUI];
    
}
/*
 * 刷新进度条以及相关UI
 */
- (void)refreshSlide:(float) time{
    CMTime duration = self.avPlayer.currentItem.asset.duration;
    self.playheadTime = time;
    int currentSeconds = (int)(self.playheadTime);
    int fullDuration = (int)(duration.value/duration.timescale);
    self.progress.minimumValue = 0;
    self.progress.maximumValue = fullDuration;
    // 刷新进度条的进度
    self.progress.value = currentSeconds;
    // 刷新时间的显示
    NSMutableAttributedString *startLabelText = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%02d:%02d",currentSeconds/60,currentSeconds%60]];
    NSMutableAttributedString *endLabelText = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%02d:%02d",fullDuration/60,fullDuration%60]];
    [startLabelText addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:NSMakeRange(0,startLabelText.length)];
    [endLabelText addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:NSMakeRange(0,endLabelText.length)];
    self.startLabel.attributedText = startLabelText;
    self.endLabel.attributedText = endLabelText;
}
/*
 * 手动滑动进度条继续播放或者暂停
 */
- (void) progressChanged{
    if ([self isPlaying]) {
        // 如果之前是播放状态,那么继续播放
        [self playFromTime:self.progress.value];
    }else{
        // 如果之前是是暂停状态,那么保持暂停
        [self pauseOnTime:self.progress.value];
    }
    
}
/*
 * 进度条被拖拽,改变播放暂停按钮的状态
 */
- (void) progressDragInside{
    NSLog(@"progressDragInside value:%f",self.progress.value);
    [_playButton setImage:[self getImageFromBundle:playerBundle imageName:@"player_play"] forState:UIControlStateNormal];
}
/*
 * 进度条被按住,改变播放暂停按钮的状态
 */
- (void) progressTouchDown{
    NSLog(@"progressTouchDown value:%f",self.progress.value);
    [_playButton setImage:[self getImageFromBundle:playerBundle imageName:@"player_play"] forState:UIControlStateNormal];
    
}
/*
 * 进度条按住后松开,改变播放暂停按钮的状态
 */
- (void)progressTouchUpInside{
    NSLog(@"progressTouchUpInside value:%f",self.progress.value);
    if ([self isPlaying]) {
        [_playButton setImage:[self getImageFromBundle:playerBundle imageName:@"player_pause"] forState:UIControlStateNormal];
    }else{
        [_playButton setImage:[self getImageFromBundle:playerBundle imageName:@"player_play"] forState:UIControlStateNormal];
    }
}

#pragma mark - private
/*
 * 屏幕旋转后刷新frame的大小
 */
- (void)reloadVideoContainerView{
    CGPoint currentOrigin = [UIScreen mainScreen].applicationFrame.origin;
    CGSize currentSize = [self getScreenSize];
    CGRect currentScreenFrame = CGRectMake(currentOrigin.x, currentOrigin.y, currentSize.width, currentSize.height);
    self.videoContainer.frame = currentScreenFrame;
    self.avPlayerLayer.frame = self.videoContainer.bounds;
}
/*
 * 进度条相关UI的布局(如果无此需求可忽略,但是在初始化时的CGRect要设置好)
 * 布局顺序:在app的底部 ==> 播放/暂停按钮、播放时间、进度条、播放时长
 *         在app的左上角 ==> 关闭按钮
 */
- (void)addConstraint{
    
    _closeButton.translatesAutoresizingMaskIntoConstraints = NO;
    _progress.translatesAutoresizingMaskIntoConstraints = NO;
    _playButton.translatesAutoresizingMaskIntoConstraints = NO;
    _startLabel.translatesAutoresizingMaskIntoConstraints = NO;
    _endLabel.translatesAutoresizingMaskIntoConstraints = NO;
    
    // Align play button left to video container
    NSLayoutConstraint* playbuttonLeftConstraint = [NSLayoutConstraint constraintWithItem:_playButton attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.videoContainer attribute:NSLayoutAttributeLeading multiplier:1.0f constant:10.0f];
    // Align play button buttom to video container
    NSLayoutConstraint* playbuttonButtomConstraint = [NSLayoutConstraint constraintWithItem:_playButton attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.videoContainer attribute:NSLayoutAttributeBottom multiplier:1.0f constant: -5.0f];
    
    // Align start label left to play button
    NSLayoutConstraint* startLabelLeftConstraint = [NSLayoutConstraint constraintWithItem:_startLabel attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.playButton attribute:NSLayoutAttributeTrailing multiplier:1.0f constant:10.0f];
    // Align start label centerY to video container
    NSLayoutConstraint* startLabelCenterYConstraint = [NSLayoutConstraint constraintWithItem:_startLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.progress attribute:NSLayoutAttributeCenterY multiplier:1.0f constant:0.0f];
    
    // Align progress slider left to start label
    NSLayoutConstraint* progressLeftConstraint = [NSLayoutConstraint constraintWithItem:_progress attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.startLabel attribute:NSLayoutAttributeTrailing multiplier:1.0f constant:10.0f];
    // Align progress slider right to end label
    NSLayoutConstraint* progressRightConstraint = [NSLayoutConstraint constraintWithItem:_progress attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.endLabel attribute:NSLayoutAttributeLeading multiplier:1.0f constant:-10.0f];
    // Align progress centerY to play button
    NSLayoutConstraint* progressCenterYConstraint = [NSLayoutConstraint constraintWithItem:_progress attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.playButton attribute:NSLayoutAttributeCenterY multiplier:1.0f constant:0.0f];
    
    // Align end label right to video contrainer
    NSLayoutConstraint* endLabelRightConstraint = [NSLayoutConstraint constraintWithItem:_endLabel attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.videoContainer attribute:NSLayoutAttributeTrailing multiplier:1.0f constant:-10.0f];
    // Align end label centerY to video container
    NSLayoutConstraint* endLabelCenterYConstraint = [NSLayoutConstraint constraintWithItem:_endLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.progress attribute:NSLayoutAttributeCenterY multiplier:1.0f constant:0.0f];
    
    // Align close button left to video container
    NSLayoutConstraint* closebuttonLeftConstraint = [NSLayoutConstraint constraintWithItem:_closeButton attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.videoContainer attribute:NSLayoutAttributeLeading multiplier:1.0f constant:3.0f];
    // Align close button top to video container
    NSLayoutConstraint* closebuttonTopConstraint = [NSLayoutConstraint constraintWithItem:_closeButton attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.videoContainer attribute:NSLayoutAttributeTop multiplier:1.0f constant:3.0f];
    
    [self.videoContainer addConstraints:@[playbuttonLeftConstraint, startLabelLeftConstraint, progressLeftConstraint, endLabelRightConstraint,progressRightConstraint,playbuttonButtomConstraint,closebuttonLeftConstraint,closebuttonTopConstraint,playbuttonButtomConstraint,startLabelCenterYConstraint,endLabelCenterYConstraint,progressCenterYConstraint]];
    
}
/*
 * 获取屏幕大小,IOS8以后屏幕宽高是真实宽高,IOS8之前需要翻转
 */
- (CGSize) getScreenSize{
    CGSize size;
    CGSize screenSize = [UIScreen mainScreen].applicationFrame.size;
    if ([self isLaterThanIOS8]) {
        size = screenSize;
        NSLog(@"ios8 or ios9, width = %f,height = %f",size.width,size.height);
    }else{
        if ( UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation))
        {
            // Landscape Orientation, reverse size values
            size.width = screenSize.height;
            size.height = screenSize.width;
        }
        else
        {
            // portrait orientation, use normal size values
            size.width = screenSize.width;
            size.height = screenSize.height;
        }
        NSLog(@" not ios8 or ios9, width = %f,height = %f",size.width,size.height);
    }
    return size;
}
/*
 * 初始化avplayerItem、avplayer、avplayerLayer
 */
- (void)setupPlayerWithURL:(NSURL *)URL
{
//        dispatch_async(dispatch_get_global_queue(0, 0), ^{
    self.avPlayerItem = [AVPlayerItem playerItemWithURL:URL];
    [self addPlayerItemObserver];
    self.avPlayer = [AVPlayer playerWithPlayerItem:self.avPlayerItem];
    [self addPlayerObserver];
    self.avPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
    [self.avPlayerLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
    self.avPlayerLayer.needsDisplayOnBoundsChange = YES;
//        });
    
}

- (void) showUI{
    [self addConstraint];
    [self.view addSubview:self.videoContainer];
    [self.videoContainer addSubview:self.progress];
    [self.videoContainer addSubview:self.startLabel];
    [self.videoContainer addSubview:self.endLabel];
    [self.videoContainer addSubview:self.playButton];
    [self.videoContainer addSubview:self.closeButton];
    
    
}

- (void)setProgressRefProperties{
    // 获取不包括状态栏的屏幕frame
    CGRect appViewFrame = [ UIScreen mainScreen ].applicationFrame;
    _videoContainer = [[UIView alloc]initWithFrame:appViewFrame];
    self.videoContainer.backgroundColor = [UIColor blackColor];
    // 初始化进度条(尺寸没有生效,因为后面用了布局约束)
    _progress = [[UISlider alloc] initWithFrame:CGRectMake(0, 0, 100, 5)];
    // 设置进度条播放按钮的图片
    [_progress setThumbImage:[self getImageFromBundle:playerBundle imageName:@"player_dot"] forState:UIControlStateNormal];
    [_progress setThumbImage:[self getImageFromBundle:playerBundle imageName:@"player_dot"] forState:UIControlStateHighlighted];
    // 设置进度条已播放和未播放部分的颜色
    [_progress setMinimumTrackTintColor:[UIColor whiteColor]];
    [_progress setMaximumTrackTintColor:[UIColor darkGrayColor]];
    // 设置进度条的事件函数
    [self.progress addTarget:self action:@selector(progressChanged) forControlEvents:(UIControlEventValueChanged)];
    [self.progress addTarget:self action:@selector(progressDragInside) forControlEvents:(UIControlEventTouchDragInside)];
    [self.progress addTarget:self action:@selector(progressTouchDown) forControlEvents:(UIControlEventTouchDown)];
    [self.progress addTarget:self action:@selector(progressTouchUpInside) forControlEvents:(UIControlEventTouchUpInside)];
    // 初始化进度条(尺寸没有生效,因为后面用了布局约束)
    _startLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 50, 15)];
    _endLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 50, 15)];
    // 设置时间Lable字体样式和颜色
    NSMutableAttributedString *startLabelText = [[NSMutableAttributedString alloc] initWithString:@"00:00"];
    NSMutableAttributedString *endLabelText = [[NSMutableAttributedString alloc] initWithString:@"00:00"];
    [startLabelText addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:NSMakeRange(0,startLabelText.length)];
    [endLabelText addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:NSMakeRange(0,endLabelText.length)];
    self.startLabel.attributedText = startLabelText;
    self.endLabel.attributedText = endLabelText;
    //初始化播放暂停按钮,设置图片
    _playButton = [[UIButton alloc] initWithFrame:CGRectMake(10, 50, 20, 20)];
    [_playButton setImage:[self getImageFromBundle:playerBundle imageName:@"player_pause"] forState:UIControlStateNormal];
    [_playButton addTarget:self action:@selector(onClickPlayOrPause) forControlEvents:UIControlEventTouchUpInside];
    //初始化关闭按钮,设置相应图片
    _closeButton = [[UIButton alloc]initWithFrame:CGRectMake(50, 50, 20, 20)];
    [_closeButton setImage:[self getImageFromBundle:playerBundle imageName:@"player_close"] forState:UIControlStateNormal];
    [_closeButton addTarget:self action:@selector(onClickClose) forControlEvents:UIControlEventTouchUpInside];
    
}
/*
 * 改变播放或暂停按钮的状态
 */
- (void) changePlayOrPauseUI{
    if (self.isPlaying) {
        [_playButton setImage:[self getImageFromBundle:playerBundle imageName:@"player_play"] forState:UIControlStateNormal];
        self.isPlaying = NO;
    }else{
        [_playButton setImage:[self getImageFromBundle:playerBundle imageName:@"player_pause"] forState:UIControlStateNormal];
        self.isPlaying = YES;
    }
}
/*
 * 判断播放是否结束
 */
- (BOOL)playerReachedEnd {
    CMTime duration = self.avPlayer.currentItem.asset.duration;
    CMTime currentTime = self.avPlayer.currentItem.currentTime;
    // CMTime包含value和timescale两个属性,比值用来获取真正的播放时间
    int currentSeconds = (int)(currentTime.value/currentTime.timescale);
    int fullDuration = (int)(duration.value/duration.timescale);
    return (currentSeconds == fullDuration) ? YES : NO;
}
/*
 * 跳到某一播放帧
 */
- (void)seekToTime:(double)time
{
    if (time >= 0) {
        CMTime timeStruct = CMTimeMake(time * 1000, 1000);
        [self.avPlayer seekToTime:timeStruct
                  toleranceBefore:kCMTimeZero
                   toleranceAfter:kCMTimePositiveInfinity];
    }
}
/*
 * 获取资源包
 */
- (void) initBundle{
    NSString * bundlePath = [[ NSBundle mainBundle] pathForResource: @"playicon" ofType :@"bundle"];
    playerBundle = [NSBundle bundleWithPath:bundlePath];
}
/*
 * 获取资源包中的图片资源
 */
- (UIImage *)getImageFromBundle:(NSBundle *) bundle imageName:(NSString *)imageName{
    NSString *imagePath = [playerBundle pathForResource:imageName ofType:@"png"];
    UIImage *image = [[UIImage alloc]init];
    if (imagePath) {
        image = [UIImage imageWithContentsOfFile:imagePath];
    }
    return image;
}
/*
 * 是否系统版本高于IOS8
 */
- (BOOL) isLaterThanIOS8{
    return ([[[UIDevice currentDevice] systemVersion] hasPrefix:@"8."] || [[[UIDevice currentDevice] systemVersion] hasPrefix:@"9."]);
}

- (void)destroyAllProperties{
    _avPlayer = nil;
    _avPlayerItem = nil;
    _avPlayerLayer = nil;
    _progress = nil;
    _startLabel = nil;
    _endLabel = nil;
    _closeButton = nil;
    _playButton = nil;
    _playTimeObserver = nil;
}

- (void) removeUIView{
    if (self.avPlayerLayer) {
        [self.avPlayerLayer removeFromSuperlayer];
    }
    if (self.videoContainer) {
        [self.videoContainer removeFromSuperview];
    }
    if (self.progress) {
        [self.progress removeFromSuperview];
    }
    if (self.closeButton) {
        [self.closeButton removeFromSuperview];
    }
    if (self.startLabel) {
        [self.startLabel removeFromSuperview];
    }
    if (self.endLabel) {
        [self.endLabel removeFromSuperview];
    }
    if (self.playButton) {
        [self.playButton removeFromSuperview];
    }
    
}

- (void)dealloc
{
    [self cancel];
}


@end

源码地址:

 https://github.com/MsSmart/VideoPlayerDemo.git

 

未解决的问题:

(1)视频播放进度与画面不同步

(2)播放的声音和画面不同步

这两个问题是偶尔出现的,猜测是苹果AVPlayer底层的原因导致,有人提出解决方案是监控音频进度,图像线程跟音频保持一致,不同步的时候等待。但是如何实现还在研究中,如果有哪位牛人解决过类似问题,还多多请指教。

你可能感兴趣的:(IOS)