美文网首页
iOS AVPlayer封装的播放器

iOS AVPlayer封装的播放器

作者: 丘名山 | 来源:发表于2018-07-09 19:29 被阅读0次

    .h文件

    #import<Foundation/Foundation.h>

    #import<AVFoundation/AVFoundation.h>

    typedefNS_ENUM(NSInteger, XCMVPlayerStatus) {

        XCMVPlayerStatusReadyToPlay =0,// 准备好播放

        XCMVPlayerStatusLoadingVideo,    // 加载视频

        XCMVPlayerStatusPlayEnd,        // 播放结束

        XCMVPlayerStatusCacheData,      // 缓冲视频

        XCMVPlayerStatusCacheEnd,        // 缓冲结束

        XCMVPlayerStatusPlayStop,        // 播放中断 (多是没网)

        XCMVPlayerStatusItemFailed,      // 视频资源问题

        XCMVPlayerStatusEnterBack,      // 进入后台

        XCMVPlayerStatusBecomeActive,    // 从后台返回

    };

    @protocolXCMusicManagerDelegate

    @optional

    // 数据刷新

    - (void)refreshDataWith:(NSTimeInterval)totalTime CurrentTime:(NSTimeInterval)currentTime LoadRange:(NSTimeInterval)loadTime Progress:(CGFloat)progress;

    // 状态/错误 提示

    - (void)promptPlayerStatusOrErrorWith:(XCMVPlayerStatus)status;

    @end

    @interfaceXCMusicManager :NSObject

    /** 播放器 */

    @property (nonatomic, strong) AVPlayer *player;

    /** 视频资源 */

    @property (nonatomic, strong) AVPlayerItem *currentItem;

    // 音频/视频总长度

    @property (nonatomic, assign) NSTimeInterval totalTime;

    // 缓存数据

    @property (nonatomic, assign) NSTimeInterval loadRange;

    + (instancetype)manager;

    - (void)setPlayerWith:(NSURL*)url;

    /** 播放 */

    - (void)play;

    /** 暂停 */

    - (void)pause;

    /** 正在播放 */

    - (BOOL)isPlay;

    /** 拖动音频/视频进度 */

    - (void)seekPlayerTimeTo:(NSTimeInterval)time;

    //代理

    @property(nonatomic,weak)id XCMusicManagerDelegate;

    /** 快进 */

    - (void)fastForward;

    /** 快退 */

    - (void)fastBack;

    @end

    .m文件

    #import "XCMusicManager.h"

    @interface XCMusicManager()

    /** 播放器观察者 */

    @property (nonatomic ,strong)  id timeObser;

    // 是否正在拖动

    @property (nonatomic ,assign) BOOL isSeeking;

    // 是否能正常播放(缓冲)

    @property (nonatomic, assign) BOOL isCanPlay;

    // 是否需要缓冲

    @property (nonatomic, assign) BOOL needBuffer;

    @end

    @implementation XCMusicManager

    + (instancetype)manager {

        staticXCMusicManager* player;

        staticdispatch_once_tonceToken;

        dispatch_once(&onceToken, ^{

            player = [[XCMusicManageralloc]init];

        });

        returnplayer;

    }

    #pragma mark - 销毁 release

    - (void)dealloc

    {

        NSLog(@"--- %@ --- 销毁了",[self class]);

        [self removeNotification];

        [self removePlayerObserver];

        [self removeObserverWithPlayItem:self.player.currentItem];

    }

    #pragma mark - 属性和方法

    - (NSTimeInterval)totalTime

    {

        return CMTimeGetSeconds(self.player.currentItem.duration);

    }

    - (void)setPlayerWith:(NSURL*)url{

        //当前播放

        self.currentItem= [selfgetPlayerItem:url];

        //创建播放器

        _player = [AVPlayer playerWithPlayerItem:self.currentItem];

        //添加播放器监察者

        [self addPlayerObserver];

        //当前曲目添加监察者

        [self addObserverWithPlayItem:self.currentItem];

        //为播放器添加通知

        [self addNotificatonForPlayer];

    }

    #pragma mark - 创建播放器

    /**

     获取播放item

     @param videoURL 视频网址

     @return AVPlayerItem

     */

    - (AVPlayerItem*)getPlayerItem:(NSURL*)videoURL

    {

        // 转utf8 防止中文报错

    //        videoPath = [videoPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

    //        videoPath = [videoPath stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];

        //网络音乐 AVPlayerItem *item = [AVPlayerItem playerItemWithURL:url];

    //    NSString *path =[ [NSBundle mainBundle]  pathForResource:@"avengers" ofType:@"mp3"];

    //    NSURL  *url = [NSURL  fileURLWithPath:path];

    //    NSLog(@"sss-%@-%@",path,url);

        AVPlayerItem*item = [AVPlayerItemplayerItemWithURL:videoURL];

        returnitem;

    }

    #pragma mark - 添加 监控

    /** 给player 添加 time observer */

    - (void)addPlayerObserver

    {

        __weak typeof(self)weakSelf = self;

        _timeObser = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {

            AVPlayerItem*playerItem = weakSelf.player.currentItem;

            floatcurrent =CMTimeGetSeconds(time);

            floattotal =CMTimeGetSeconds([playerItemduration]);

            if(weakSelf.isSeeking) {

                return;

            }

            if(weakSelf.XCMusicManagerDelegate&& [weakSelf.XCMusicManagerDelegaterespondsToSelector:@selector(refreshDataWith:CurrentTime:LoadRange:Progress:)]) {

                [weakSelf.XCMusicManagerDelegaterefreshDataWith:totalCurrentTime:currentLoadRange:weakSelf.loadRangeProgress:current/total];

            }

            //        NSLog(@"当前播放进度 %.2f/%.2f.",current,total);

        }];

    }

    /** 移除 time observer */

    - (void)removePlayerObserver

    {

        [_player removeTimeObserver:_timeObser];

    }

    #pragma mark - 给当前播放的item 添加观察者

    /** 给当前播放的item 添加观察者

     需要监听的字段和状态

     status :  AVPlayerItemStatusUnknown,AVPlayerItemStatusReadyToPlay,AVPlayerItemStatusFailed

     loadedTimeRanges  :  缓冲进度

     playbackBufferEmpty : seekToTime后,缓冲数据为空,而且有效时间内数据无法补充,播放失败

     playbackLikelyToKeepUp : seekToTime后,可以正常播放,相当于readyToPlay,一般拖动滑竿菊花转,到了这个这个状态菊花隐藏

     */

    - (void)addObserverWithPlayItem:(AVPlayerItem*)item

    {

        [itemaddObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];

        [itemaddObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];

        [itemaddObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];

        [itemaddObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];

    }

    /** 移除 item 的 observer */

    - (void)removeObserverWithPlayItem:(AVPlayerItem*)item

    {

        [itemremoveObserver:self forKeyPath:@"status"];

        [itemremoveObserver:self forKeyPath:@"loadedTimeRanges"];

        [itemremoveObserver:self forKeyPath:@"playbackBufferEmpty"];

        [itemremoveObserver:self forKeyPath:@"playbackLikelyToKeepUp"];

    }

    /** 数据处理 获取到观察到的数据 并进行处理 */

    - (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context

    {

        AVPlayerItem*item = object;

        if([keyPathisEqualToString:@"status"]) {// 播放状态

            [self handleStatusWithPlayerItem:item];

        }elseif([keyPathisEqualToString:@"loadedTimeRanges"]) {// 缓冲进度

            [self handleLoadedTimeRangesWithPlayerItem:item];

        }elseif([keyPathisEqualToString:@"playbackBufferEmpty"]) {// 跳转后没数据

            // 转菊花

            if(self.isCanPlay) {

                NSLog(@"跳转后没数据");

                self.needBuffer=YES;

                [self useDelegateWith:XCMVPlayerStatusCacheData];

            }

        }else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) {// 跳转后有数据

            // 隐藏菊花

            if (self.isCanPlay && self.needBuffer) {

                NSLog(@"跳转后有数据");

                self.needBuffer=NO;

                [self useDelegateWith:XCMVPlayerStatusCacheEnd];

            }

        }

    }

    /**

     处理 AVPlayerItem 播放状态

     AVPlayerItemStatusUnknown          状态未知

     AVPlayerItemStatusReadyToPlay      准备好播放

     AVPlayerItemStatusFailed            播放出错

     */

    - (void)handleStatusWithPlayerItem:(AVPlayerItem*)item

    {

        AVPlayerItemStatus status = item.status;

        switch(status) {

            case AVPlayerItemStatusReadyToPlay:  // 准备好播放

                NSLog(@"AVPlayerItemStatusReadyToPlay");

                self.isCanPlay=YES;

                [self useDelegateWith:XCMVPlayerStatusReadyToPlay];

                break;

            case AVPlayerItemStatusFailed:        // 播放出错

                NSLog(@"AVPlayerItemStatusFailed");

                [self useDelegateWith:XCMVPlayerStatusItemFailed];

                break;

            case AVPlayerItemStatusUnknown:      // 状态未知

                NSLog(@"AVPlayerItemStatusUnknown");

                break;

            default:

                break;

        }

    }

    /** 处理缓冲进度 */

    - (void)handleLoadedTimeRangesWithPlayerItem:(AVPlayerItem*)item

    {

        NSArray*loadArray = item.loadedTimeRanges;

        CMTimeRangerange = [[loadArrayfirstObject]CMTimeRangeValue];

        floatstart =CMTimeGetSeconds(range.start);

        floatduration =CMTimeGetSeconds(range.duration);

        NSTimeIntervaltotalTime = start + duration;// 缓存总长度

        _loadRange= totalTime;

        //    NSLog(@"缓冲进度 -- %.2f",totalTime);

    }

    /**

     添加关键通知

     AVPlayerItemDidPlayToEndTimeNotification    视频播放结束通知

     AVPlayerItemTimeJumpedNotification          视频进行跳转通知

     AVPlayerItemPlaybackStalledNotification      视频异常中断通知

     UIApplicationDidEnterBackgroundNotification  进入后台

     UIApplicationDidBecomeActiveNotification    返回前台

     */

    - (void)addNotificatonForPlayer

    {

        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];

        [centeraddObserver:self selector:@selector(videoPlayEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];

        //    [center addObserver:self selector:@selector(videoPlayToJump:) name:AVPlayerItemTimeJumpedNotification object:nil];//没意义

        [centeraddObserver:self selector:@selector(videoPlayError:) name:AVPlayerItemPlaybackStalledNotification object:nil];

        if(@available(iOS8.2, *)) {

            [centeraddObserver:selfselector:@selector(videoPlayEnterBack:)name:NSExtensionHostDidEnterBackgroundNotificationobject:nil];

        }else{

            // Fallback on earlier versions

        }

        if(@available(iOS8.2, *)) {

            [centeraddObserver:selfselector:@selector(videoPlayBecomeActive:)name:NSExtensionHostDidBecomeActiveNotificationobject:nil];

        }else{

            // Fallback on earlier versions

        }

    }

    /** 移除 通知 */

    - (void)removeNotification

    {

        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];

        [centerremoveObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];

        //    [center removeObserver:self name:AVPlayerItemTimeJumpedNotification object:nil];

        [centerremoveObserver:self name:AVPlayerItemPlaybackStalledNotification object:nil];

        if(@available(iOS8.2, *)) {

            [centerremoveObserver:self name:NSExtensionHostDidEnterBackgroundNotification object:nil];

        }else{

            // Fallback on earlier versions

        }

        if(@available(iOS8.2, *)) {

            [centerremoveObserver:self name:NSExtensionHostDidBecomeActiveNotification object:nil];

        }else{

            // Fallback on earlier versions

        }

        [centerremoveObserver:self];

    }

    /** 视频播放结束 */

    - (void)videoPlayEnd:(NSNotification*)notic

    {

        NSLog(@"视频播放结束");

        [self useDelegateWith:XCMVPlayerStatusPlayEnd];

        [self.player seekToTime:kCMTimeZero];

    }

    /** 视频异常中断 */

    - (void)videoPlayError:(NSNotification*)notic

    {

        NSLog(@"视频异常中断");

        [self useDelegateWith:XCMVPlayerStatusPlayStop];

    }

    /** 进入后台 */

    - (void)videoPlayEnterBack:(NSNotification*)notic

    {

        NSLog(@"进入后台");

        [self useDelegateWith:XCMVPlayerStatusEnterBack];

    }

    /** 返回前台 */

    - (void)videoPlayBecomeActive:(NSNotification*)notic

    {

        NSLog(@"返回前台");

        [self useDelegateWith:XCMVPlayerStatusBecomeActive];

    }

    /**

     播放状态代理调用

     @param status 播放状态

     */

    - (void)useDelegateWith:(XCMVPlayerStatus)status

    {

        if(self.isCanPlay==NO) {

            return;

        }

        if(self.XCMusicManagerDelegate&& [self.XCMusicManagerDelegaterespondsToSelector:@selector(promptPlayerStatusOrErrorWith:)]) {

            [self.XCMusicManagerDelegate promptPlayerStatusOrErrorWith:status];

        }

    }

    /** 播放 */

    - (void)play

    {

        NSLog(@"播放");

        [self.playerplay];

    }

    /** 暂停 */

    - (void)pause

    {

        [self.playerpause];

    }

    /** 拖动视频进度 */

    - (void)seekPlayerTimeTo:(NSTimeInterval)time

    {

        [selfpause];

        [self startToSeek];

        __weak typeof(self)weakSelf = self;

        [self.player seekToTime:CMTimeMake(time, 1.0) completionHandler:^(BOOL finished) {

            [weakSelfendSeek];

            [weakSelfplay];

        }];

    }

    /** 跳动中不监听 */

    - (void)startToSeek

    {

        self.isSeeking = YES;

    }

    - (void)endSeek

    {

        self.isSeeking = NO;

    }

    /** 快进15s */

    - (void)fastForward{

        [selfpause];

        [self startToSeek];

        __weak typeof(self)weakSelf = self;

        [self.player seekToTime:CMTimeMakeWithSeconds(self.currentItem.currentTime.value/self.currentItem.currentTime.timescale+15, self.currentItem.currentTime.timescale) completionHandler:^(BOOL finished) {

            [weakSelfendSeek];

            [weakSelfplay];

        }];

    }

    /** 快退15s */

    - (void)fastBack{

        [selfpause];

        [self startToSeek];

        __weak typeof(self)weakSelf = self;

        [self.player seekToTime:CMTimeMakeWithSeconds(self.currentItem.currentTime.value/self.currentItem.currentTime.timescale-15, self.currentItem.currentTime.timescale) completionHandler:^(BOOL finished) {

            [weakSelfendSeek];

            [weakSelfplay];

        }];

    }

    /** 正在播放 */

    - (BOOL)isPlay{

        return  _player.rate==1?YES:NO;

    }


    使用

     NSString *path =[ [NSBundle mainBundle]  pathForResource:@"avengers" ofType:@"mp3"];

        NSURL  *url = [NSURL  fileURLWithPath:path];

        [musicManager setPlayerWith:url];

        musicManager.XCMusicManagerDelegate = self;

      [musicManager play];

    相关文章

      网友评论

          本文标题:iOS AVPlayer封装的播放器

          本文链接:https://www.haomeiwen.com/subject/hymtpftx.html