iOS AVPlayer封装的播放器

.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];

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容