【音频】远程链接音频播放(AVPlayer)

感觉没啥说的,直接看代码。

WebMusicTool是控制单个音频的类。

WebMusicOperationTool是控制流程播放控制的类。

#import <Foundation/Foundation.h>
#import "DenglSingleton.h"
#import "WebMusicTool.h"
#import "NLessonM.h"

#import "NMusicMessageModel.h"

@interface WebMusicOperationTool : NSObject
DenglSingletonH(WebMusicOperationTool)

@property(nonatomic,copy) NSArray *musicMs;

-(void)playMusic:(NLessonM *)musicM;

-(void)playCurrentMusic;

-(void)pauseCurrentMusic;

-(void)seekToTime:(float)p;

-(NMusicMessageModel*)getMusicMessageModel;

@end
#import "WebMusicOperationTool.h"

@interface WebMusicOperationTool ()
{
    
}

@property(nonatomic,strong) WebMusicTool *tool;

@property(nonatomic,assign) NSInteger currentPlayIndex;

@property(nonatomic,strong) NMusicMessageModel *musicModel;

@end

@implementation WebMusicOperationTool
DenglSingletonM(WebMusicOperationTool)

#pragma mark - GET SET
-(WebMusicTool *)tool{
    if (!_tool) {
        _tool = [[WebMusicTool alloc]init];
    }
    return _tool;
}

-(NMusicMessageModel *)musicModel{
    if (!_musicModel) {
        _musicModel = [[NMusicMessageModel alloc]init];
    }
    return _musicModel;
}

-(NMusicMessageModel*)getMusicMessageModel{
    // 在这里, 保持, 数据的最新状态, 就可以
    // 当前正在播放的歌曲信息
    self.musicModel.lessonmodel = self.musicMs[_currentPlayIndex];
    
    NSTimeInterval costTime = CMTimeGetSeconds(self.tool.player.currentItem.currentTime);
    if (isnan(costTime)) {
        costTime = 0;
    }
    self.musicModel.costTime = costTime;
    
    NSTimeInterval totalTime = CMTimeGetSeconds(self.tool.player.currentItem.duration);
    if (isnan(totalTime)) {
        totalTime = 0;
    }
    self.musicModel.totalTime = totalTime;
    
    self.musicModel.status = self.tool.status;
    
    self.musicModel.isPlaying = self.tool.isPlaying;
    
    return self.musicModel;
}

#pragma mark - play
-(void)playMusic:(NLessonM *)musicM{
    
    // 播放数据模型对应的音乐
    [self.tool playMusic:musicM.main_audio[@"src"]];
    _currentPlayIndex = [self.musicMs indexOfObject:musicM];
    
}

-(void)playCurrentMusic{
    
    // 取出需要播放的音乐数据模型
    NLessonM * model = self.musicMs[_currentPlayIndex];
    
    // 播放音乐模型
    [self playMusic:model];
}

-(void)pauseCurrentMusic{
    [self.tool pauseMusic];
}

-(void)seekToTime:(float)p{
    [self.tool seekToTime:p];
}
@end
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>

//播放状态枚举值
typedef NS_ENUM(NSInteger,NPlayerStatus){
    NPlayerStatusFailed,
    NPlayerStatusReadyToPlay,
    NPlayerStatusUnknown,
    NPlayerStatusBuffering,
    NPlayerStatusPlaying,
    NPlayerStatusStopped,
};
@interface WebMusicTool : NSObject

@property(nonatomic,strong) AVPlayer *player;

@property(nonatomic,assign) BOOL isPlaying;
//播放状态
@property (nonatomic,assign,readonly) NPlayerStatus status;

-(void)playMusic:(NSString *)musicUrl;

-(void)seekToTime:(float)p;

-(void)pauseMusic;

@end
#import "WebMusicTool.h"

@interface WebMusicTool (){
    id playbackTimerObserver;
}

//记录当前正在播放的曲目url
@property(nonatomic,strong) NSString *currentUrl;

//当前歌曲进度监听者
@property(nonatomic,strong) id timeObserver;

@end

@implementation WebMusicTool

- (instancetype)init
{
    self = [super init];
    if (self) {
        //后台播放三步走:1.background mode 2.setCategory 3.setActive
        AVAudioSession *session = [AVAudioSession sharedInstance];
        [session setCategory:AVAudioSessionCategoryPlayback error:nil];
        [session setActive:YES error:nil];

        //
//        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AVPlayerItemNotification:) name:AVPlayerItemTimeJumpedNotification object:nil];
//        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AVPlayerItemNotification:) name:AVPlayerItemFailedToPlayToEndTimeNotification object:nil];
//        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AVPlayerItemNotification:) name:AVPlayerItemPlaybackStalledNotification object:nil];
//        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AVPlayerItemNotification:) name:AVPlayerItemFailedToPlayToEndTimeErrorKey object:nil];
//        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AVPlayerItemNotification:) name:AVPlayerItemNewErrorLogEntryNotification object:nil];
//        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AVPlayerItemNotification:) name:AVPlayerItemNewAccessLogEntryNotification object:nil];
    }
    return self;
}

-(void)playMusic:(NSString *)musicUrl{
    // 1. 创建播放器
    if (!musicUrl) {
        return;
    }
    //2.
    if ([musicUrl isEqualToString:_currentUrl]) {
        [self.player play];
        return;
    }
    
    //3.
    [self stop];
    
    AVPlayerItem *item = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:musicUrl]];
    self.player = nil;
    self.player = [AVPlayer playerWithPlayerItem:item];
    _currentUrl = musicUrl;
    [self.player play];
    
    [self addKVO];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(AVPlayerItemNotification:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
    
    
    
}

-(void)seekToTime:(float)p{
    NSTimeInterval currentTime = CMTimeGetSeconds(self.player.currentItem.duration) * p;
    [self.player seekToTime:CMTimeMakeWithSeconds(currentTime, NSEC_PER_SEC) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
    [self.player play];
}

-(void)pauseMusic{
    [self.player pause];
}

#pragma mark - Notification
-(void)AVPlayerItemNotification:(NSNotification* )noti{
    //
    if (noti.object == self.player.currentItem) {
        if ([noti.name isEqualToString:@"AVPlayerItemDidPlayToEndTimeNotification"]) {
            [self stop];
            [[NSNotificationCenter defaultCenter] postNotificationName:@"NPlayerFinishNotification" object:nil];
        }
    }
}
#pragma mark - 监听音乐各种状态 KVO
//添加KVO
-(void)addKVO{
    //监听状态属性
    [self.player.currentItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    //监听网络加载情况属性
    [self.player.currentItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
    //监听播放的区域缓存是否为空
    [self.player.currentItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
    //缓存可以播放的时候调用
    [self.player.currentItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
    //监听暂停或者播放中
    [self.player addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:nil];
}
-(void)stop{
    [self.player.currentItem removeObserver:self forKeyPath:@"status"];
    //    [self.player removeTimeObserver:playbackTimerObserver];
    [self.player.currentItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
    [self.player.currentItem removeObserver:self forKeyPath:@"playbackBufferEmpty"];
    [self.player.currentItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];
    [self.player removeObserver:self forKeyPath:@"rate"];
    [[NSNotificationCenter defaultCenter]removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:[self.player currentItem]];
    if (self.player) {
        
        _currentUrl = nil;
        self.player = nil;
    }
}
//KVO
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    if ([keyPath isEqualToString:@"status"]) {
        AVPlayerItemStatus itemStatus = [[change objectForKey:NSKeyValueChangeNewKey]integerValue];
        
        switch (itemStatus) {
            case AVPlayerItemStatusUnknown:
            {
                _status = NPlayerStatusUnknown;
                NSLog(@"AVPlayerItemStatusUnknown");
            }
                break;
            case AVPlayerItemStatusReadyToPlay:
            {
                _status = NPlayerStatusReadyToPlay;
                [self.player play];
                NSLog(@"AVPlayerItemStatusReadyToPlay");
                
            }
                break;
            case AVPlayerItemStatusFailed:
            {
                _status = NPlayerStatusFailed;
                NSLog(@"AVPlayerItemStatusFailed");
                [self stop];
                
            }
                break;
            default:
                break;
        }
    }else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {  //监听播放器的下载进度
        NSArray *loadedTimeRanges = [self.player.currentItem loadedTimeRanges];
        CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];// 获取缓冲区域
        float startSeconds = CMTimeGetSeconds(timeRange.start);
        float durationSeconds = CMTimeGetSeconds(timeRange.duration);
        NSTimeInterval timeInterval = startSeconds + durationSeconds;// 计算缓冲总进度
        CMTime duration = self.player.currentItem.duration;
        CGFloat totalDuration = CMTimeGetSeconds(duration);
        //缓存值
        CGFloat p = timeInterval / totalDuration;
        NSLog(@"%f",p);
    } else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) { //监听播放器在缓冲数据的状态
        _status = NPlayerStatusBuffering;
        NSLog(@"缓冲中");
//        if (!self.activityIndeView.isAnimating) {
//            [self.activityIndeView startAnimating];
//        }
    } else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) {
        NSLog(@"缓冲达到可播放");
//        [self.activityIndeView stopAnimating];
    } else if ([keyPath isEqualToString:@"rate"]){//当rate==0时为暂停,rate==1时为播放,当rate等于负数时为回放
        if ([[change objectForKey:NSKeyValueChangeNewKey]integerValue]==1) {
            _isPlaying=YES;
            _status = NPlayerStatusPlaying;
        }else{
            _isPlaying=NO;
            _status = NPlayerStatusStopped;
        }
    }
    
}

 

posted @ 2017-04-17 17:53  Nuius  阅读(1280)  评论(0编辑  收藏  举报