美文网首页
OC-网络音频播放

OC-网络音频播放

作者: 紫云夕月 | 来源:发表于2021-08-18 10:48 被阅读0次
    #import <Foundation/Foundation.h>
    
    /**
     *  正在播放回调
     *  @param currentTime     时间显示 label.text
     *  @param currentProgress 播放进度 slider.value
     */
    typedef void(^OnPlayingBlock)(Float64 currentTime, CGFloat currentProgress);
    
    /**
     *  准备播放回调
     *  @param totalDuration 音频总时长
     */
    typedef void(^PrepareToPlayBlock)(CGFloat totalDuration);
    
    /**
     *  正在缓冲回调
     *  @param bufferDuration 已缓冲的时长
     */
    typedef void(^OnBufferingBlock)(CGFloat bufferDuration);
    
    /**
     *  播放完成回调
     *  @param flag `YES`播放完成, `NO`播放失败.
     */
    typedef void(^CompletePlayingBlock)(BOOL flag);
    
    /**
     *  缓存自动暂停回调, 用于更改播放按钮的样式
     *  @param isPlaying 是否正在播放, 如果没有播放表示正在缓冲
     */
    typedef void(^IsPlayingBlock)(BOOL isPlaying);
    
    
    @interface LilyNetPlayer : NSObject
    
    @property (nonatomic, assign) CGFloat volume;       //音量大小
    
    @property (nonatomic, assign) Float64 changedTime;  //改变播放进度
    
    @property (nonatomic, assign) Float64 timeOffset;   //之前播放的进度
    
    @property (nonatomic, copy) OnPlayingBlock playingBlock;
    @property (nonatomic, copy) PrepareToPlayBlock prepareToPlayBlock;
    @property (nonatomic, copy) OnBufferingBlock bufferingBlock;
    @property (nonatomic, copy) CompletePlayingBlock completePlayBlock;
    @property (nonatomic, copy) IsPlayingBlock isPlayingBlock;
    @property (nonatomic, copy) void(^playerRateStatus)(float rate);
    
    //初始化播放器
    -(void)createAudioPlayerWith:(NSString*)audioPath;
    //开始播放
    - (void)play;
    //暂停播放
    - (void)pause;
    //停止播放
    - (void)stop;
    
    @end
    
    #import "LilyNetPlayer.h"
    #import <AVFoundation/AVFoundation.h>
    
    @interface LilyNetPlayer ()
    @property (nonatomic, strong) AVPlayer * player;
    @property (nonatomic, strong) id periodicTimeObserver;
    @property (nonatomic, assign) Float64 currentTime;      //播放时间
    @property (nonatomic, assign) Float64 totalDuration;    //音频总时长
    @end
    
    @implementation LilyNetPlayer
    
    #pragma mark - 初始化播放器
    -(void)createAudioPlayerWith:(NSString*)audioPath
    {
        NSURL * url = [NSURL fileURLWithPath:audioPath];
        AVPlayerItem * item = [AVPlayerItem playerItemWithURL:url];
        _player = [[AVPlayer alloc] initWithPlayerItem:item];
        
        AVAudioSession * session = [AVAudioSession sharedInstance];
        [session setCategory:AVAudioSessionCategoryPlayback error:nil];
        [session setActive:YES error:nil];
        
        [self addObserverForPlayer];
        [self addObserverForPlayItem];
    }
    
    #pragma mark - 开始播放
    - (void)play {
        [self.player play];
    }
    
    #pragma mark - 暂停播放
    - (void)pause {
        [self.player pause];
    }
    
    #pragma mark - 停止播放
    - (void)stop {
        [self destroyPlayer];//释放 播放器
        if (self.completePlayBlock) {
            self.completePlayBlock(YES);
        }
    }
    
    #pragma mark - 释放播放器
    - (void)releasePlayer {
        [self destroyPlayer];
    }
    
    
    #pragma mark - 改变播放进度
    - (void)setChangedTime:(Float64)changedTime {
        _changedTime = changedTime;
        if (changedTime>0) {
            CMTime time = CMTimeMakeWithSeconds(changedTime, 1 * NSEC_PER_SEC);
            [self.player seekToTime:time toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
            [self.player play];
        }
    }
    
    #pragma mark - 音量大小
    - (void)setVolume:(CGFloat)volume {
        _volume = volume;
        self.player.volume = volume;
    }
    
    #pragma mark - Notification
    //播放完毕
    - (void)addObserverForPlayer {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioPlayCompletion:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
    }
    
    //播放完毕
    - (void)audioPlayCompletion:(NSNotification *)notification {
        Float64 timeInterval = fabs(self.currentTime - self.totalDuration);
        if (timeInterval > 1.0) {
            if (self.isPlayingBlock) self.isPlayingBlock(NO);
            return; // 下载完的音频已经播放完但是整个音频没播完
        }
        _timeOffset = 0.0;
        __weak typeof(self) weakSelf = self;
        if (self.player.currentItem.status == AVPlayerItemStatusReadyToPlay) {
            [self.player seekToTime:kCMTimeZero completionHandler:^(BOOL finished) {
                if (weakSelf.completePlayBlock) weakSelf.completePlayBlock(YES);
            }];
        }
        [self destroyPlayer];//释放 播放器
    }
    
    #pragma mark - Observer
    //观察 加载状态 加载进度
    - (void)addObserverForPlayItem {
        [self.player.currentItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
        [self.player.currentItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
    }
    
    #pragma mark - AVPlayerItemObserver
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
        AVPlayerItem * playerItem = object;
        
        if ([keyPath isEqualToString:@"status"]) {
            AVPlayerStatus status = [[change objectForKey:@"new"] intValue];
            if (status == AVPlayerStatusUnknown) {
                
            } else if (status == AVPlayerStatusReadyToPlay){//准备播放
                _totalDuration = CMTimeGetSeconds(playerItem.duration);
                _totalDuration = isnan(_totalDuration) ? 0.0 : _totalDuration;
                
                if (self.prepareToPlayBlock) {
                    self.prepareToPlayBlock(_totalDuration);
                }
                if (_timeOffset > 0) {//接着之前的进度播放
                    CMTime time = CMTimeMakeWithSeconds(_timeOffset, 1 * NSEC_PER_SEC);
                    [self.player seekToTime:time toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
                }
                [self updatePlayProgress];//播放进度
                
            } else if ([playerItem status] == AVPlayerStatusFailed) {//播放失败
                if (self.completePlayBlock) {
                    self.completePlayBlock(NO);
                }
                [self destroyPlayer];//释放 播放器
            }
            
        } else if([keyPath isEqualToString:@"loadedTimeRanges"]){//获取最新缓存的区间
            NSTimeInterval bufferInterval = [self bufferedDuration];
            
            if (self.bufferingBlock) {
                self.bufferingBlock(bufferInterval);
            }
            if (self.isPlayingBlock) {
                if (bufferInterval > self.currentTime + 1.0) {
                    self.isPlayingBlock(YES);
                } else if (self.player.rate == 0.0) {
                    self.isPlayingBlock(NO);
                }
                // 处理缓冲结束时,正好由于缓存而自动暂停的问题.
                if (bufferInterval >= _totalDuration) {
                    self.isPlayingBlock(YES);
                }
            }
        }
    }
    
    //播放进度
    - (void)updatePlayProgress {
        __weak typeof(self) weakSelf = self;
        dispatch_queue_t queue = dispatch_get_main_queue();
        CMTime timeInterval = CMTimeMakeWithSeconds(1.0f, NSEC_PER_SEC);
        
        self.periodicTimeObserver = [self.player addPeriodicTimeObserverForInterval:timeInterval queue:queue usingBlock:^(CMTime time) {
             weakSelf.currentTime = CMTimeGetSeconds(time);
            if (weakSelf.currentTime && weakSelf.playingBlock) {
                weakSelf.playingBlock(weakSelf.currentTime, weakSelf.currentTime);
            }
            if (weakSelf.playerRateStatus) {
                weakSelf.playerRateStatus(weakSelf.player.rate);
            }
        }];
    }
    
    // 计算缓冲进度
    - (NSTimeInterval)bufferedDuration {
        NSArray * array = [self.player.currentItem loadedTimeRanges];
        CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];
        float startSeconds = CMTimeGetSeconds(timeRange.start);
        float durationSeconds = CMTimeGetSeconds(timeRange.duration);
        NSTimeInterval totalBuffer = startSeconds + durationSeconds;
        return totalBuffer;
    }
    
    #pragma mark - 释放播放器
    - (void)destroyPlayer {
        if (!_player) return;
        [self.player pause];
        self.timeOffset = 0.0;
        [self.player.currentItem cancelPendingSeeks];
        [self.player.currentItem.asset cancelLoading];
        [self removeObserverFromPlayer];
    }
    
    - (void)removeObserverFromPlayer {
        @try {
            [self.player.currentItem removeObserver:self forKeyPath:@"status"];
            [self.player.currentItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
            [self.player removeTimeObserver:self.periodicTimeObserver];
            [self setPeriodicTimeObserver:nil]; // must set it nil value.
            [self.player replaceCurrentItemWithPlayerItem:nil];
        } @catch(NSException *__unused exception) { }
    }
    
    - (void)dealloc {
        if (_player != nil) {
            [self destroyPlayer];//释放 播放器
            [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
        }
    }
    
    @end
    
    //网络播放器
    LilyNetPlayer * netPlayer = [[LilyNetPlayer alloc] init];
    [netPlayer createAudioPlayerWith:@"网络音频路径"];
    
    #pragma mark - PlayerBlock
    - (void)addNetPlayerBlock {
        
        // 准备播放时更新UI
        netPlayer.prepareToPlayBlock = ^(CGFloat totalDuration) {
            NSLog(@"音频总时长---%@",convertTime(totalDuration));
        };
        
        // 缓冲时更新UI
        netPlayer.bufferingBlock = ^(CGFloat bufferDuration) {
            NSLog(@"缓冲时长---%@",convertTime(bufferDuration));
        };
        
        // 播放时更新UI
        netPlayer.playingBlock = ^(Float64 currentTime, CGFloat currentProgress) {
            NSLog(@"播放时长---%@",convertTime(roundf(currentTime)));
            NSLog(@"播放进度---%f",currentProgress);
        };
        
        // 播放完成更新UI
        netPlayer.completePlayBlock = ^(BOOL flag) {
            
        };
    }
    
    static inline NSString* convertTime(int totalSeconds) {
        int seconds = totalSeconds % 60;
        int minutes = (totalSeconds / 60) % 60;
        NSString *time = [NSString stringWithFormat:@"%02d:%02d", minutes, seconds];
        return time;
    }
    
    //开始播放
    [netPlayer play];
    
    //暂停播放
    [netPlayer pause];
    
    //停止播放
    [netPlayer stop];
    
    //修改音量大小
    netPlayer.volume = 1;
    
    //修改播放进度
    netPlayer.changedTime = slider.value
    

    相关文章

      网友评论

          本文标题:OC-网络音频播放

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