AVQueuePlayer 在线播放一组网络音频文件

头文件

@interface ZNAudioPlayerUtil : NSObject

//播放一组网络音频文件
+ (void)playAudios:(NSArray*)audioArr;
//终止播放
+ (void)stopPlayer;
//暂停播放
+ (void)pausePlayer;
//恢复播放
+ (void)resumePlayer;
//拖动到某个时间点
+ (void)seekToTime:(CMTime)time;
//当前播放时间
+ (CGFloat)currentTime;
//总时长
+ (CGFloat)totalTime;

@end

.m

static void* const AVLoopPlayerQueuePlayerStatusObservationContext = (void*)&AVLoopPlayerQueuePlayerStatusObservationContext;
static void* const AVLoopPlayerCurrentItemObservationContext = (void*)&AVLoopPlayerCurrentItemObservationContext;
static void* const AVLoopPlayerCurrentItemStatusObservationContext = (void*)&AVLoopPlayerCurrentItemStatusObservationContext;

@interface ZNAudioPlayerUtil(){
BOOL _addedObservers;
}

@property (nonatomic, strong) AVQueuePlayer *queuePlayer;
@property (nonatomic, assign) CGFloat totalDuration;

  • (void)playbackInLoopWithURL:(NSString *)urlStr;
  • (void)stopPlayback;

@end

@implementation ZNAudioPlayerUtil

  • (instancetype)shareInstance{
    static ZNAudioPlayerUtil *player = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    player = [[ZNAudioPlayerUtil alloc]init];
    });
    return player;
    }

  • (void)playAudios:(NSArray *)audioArr{
    [[ZNAudioPlayerUtil shareInstance] stopPlayback];
    for (NSString *audioID in audioArr) {
    NSString *urlStr = [NSString stringWithFormat:@”%@%@?id=%@”,EvHRServerHost,kHR_DownloadAudioFileUrl,audioID];
    [[ZNAudioPlayerUtil shareInstance] playbackInLoopWithURL:urlStr];
    }
    }

  • (void)stopPlayer{
    [[ZNAudioPlayerUtil shareInstance] stopPlayback];
    }

  • (instancetype)init{
    if (self = [super init]) {
    _queuePlayer = [[AVQueuePlayer alloc]init];
    }
    return self;

}

  • (void)startObservingPlayerAndItem
    {
    if (_addedObservers == NO)
    {
    [_queuePlayer addObserver:self forKeyPath:@”status” options:NSKeyValueObservingOptionNew context:AVLoopPlayerQueuePlayerStatusObservationContext];
    [_queuePlayer addObserver:self forKeyPath:@”currentItem” options:NSKeyValueObservingOptionOld context:AVLoopPlayerCurrentItemObservationContext];
    [_queuePlayer addObserver:self forKeyPath:@”currentItem.status” options:NSKeyValueObservingOptionNew context:AVLoopPlayerCurrentItemStatusObservationContext];
    _addedObservers = YES;
    }
    }

  • (void)stopObservingPlayerAndItem
    {
    if (_addedObservers)
    {
    [_queuePlayer removeObserver:self forKeyPath:@”status” context:AVLoopPlayerQueuePlayerStatusObservationContext];
    [_queuePlayer removeObserver:self forKeyPath:@”currentItem” context:AVLoopPlayerCurrentItemObservationContext];
    [_queuePlayer removeObserver:self forKeyPath:@”currentItem.status” context:AVLoopPlayerCurrentItemStatusObservationContext];
    _addedObservers = NO;
    }
    }

  • (void)playbackInLoopWithURL:(NSString *)urlStr
    {
    AVURLAsset *asset = [AVURLAsset assetWithURL:[NSURL URLWithString:urlStr]];

    [asset loadValuesAsynchronouslyForKeys:@[@”duration”, @”playable”] completionHandler:^{
    /*
    The asset invokes its completion handler on an arbitrary queue when
    loading is complete. Because we want to access our AVQueuePlayer in our
    ensuing set-up, we must dispatch our handler to the main queue.
    */
    dispatch_async(dispatch_get_main_queue(), ^{
    NSError *durationError, *playableError;
    /*
    Check to make sure duration and playable properties are loaded
    before accessing them.
    */
    AVKeyValueStatus durationStatus = [asset statusOfValueForKey:@”duration” error:&durationError];
    AVKeyValueStatus playableStatus = [asset statusOfValueForKey:@”playable” error:&playableError];

        if (durationStatus == AVKeyValueStatusLoaded && playableStatus == AVKeyValueStatusLoaded )
        {
            if (CMTIME_COMPARE_INLINE([asset duration], >=, CMTimeMake(1,100)) && [asset isPlayable])
            {
                /*
                 Based on the duration of the asset, we decide the number of
                 player items to add to demonstrate gapless playback of the
                 same asset.
                 */
                NSUInteger countOfPlayerItems = (1.0 / CMTimeGetSeconds([asset duration])) + 2;
    

    // for (NSUInteger idx = 0; idx < countOfPlayerItems; ++idx)
    {
    AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
    if (playerItem)
    {
    [_queuePlayer insertItem:playerItem afterItem:nil];
    }
    }

                [self startObservingPlayerAndItem];
                [_queuePlayer play];
            }
            else
            {
                NSLog(@"Can't loop. Asset duration too short(%1.3f sec) or not playable(isPlayable: %s)",
                      CMTimeGetSeconds([asset duration]), ([asset isPlayable]?"YES":"NO"));
            }
        }
        else
        {
            if (durationStatus == AVKeyValueStatusFailed)
                NSLog(@"Failed to load duration property for asset: %@ with error: %@", asset, durationError);
    
            if (playableStatus == AVKeyValueStatusFailed)
                NSLog(@"Failed to load playable property for asset: %@ with error: %@", asset, playableError);
        }
    });
    

    }];
    }

  • (void)observeValueForKeyPath:(NSString )keyPath ofObject:(id)object change:(NSDictionary )changeDictionary context:(void *)context
    {
    return;
    if (context == AVLoopPlayerQueuePlayerStatusObservationContext)
    {
    AVPlayerStatus newPlayerStatus = (AVPlayerStatus)[[changeDictionary objectForKey:NSKeyValueChangeNewKey] unsignedIntegerValue];
    if (newPlayerStatus == AVPlayerStatusFailed) {
    AVQueuePlayer player = (AVQueuePlayer )object;
    NSLog(@”End looping since player has failed with error %@”, player.error);
    [self stopPlayback];
    }

    }
    else if (context == AVLoopPlayerCurrentItemObservationContext)
    {
    AVQueuePlayer player = (AVQueuePlayer )object;

    if ([[player items] count] == 0)
    {
        NSLog(@"Play queue emptied out due to bad player item. End looping.");
        [self stopPlayback];
    }
    else
    {
        // Append the previous current item to the player's queue.
    

    // AVPlayerItem *itemRemoved = changeDictionary[NSKeyValueChangeOldKey];

        /*
         An initial change from a nil currentItem yields NSNull here. Check
         to make sure the class is AVPlayerItem before appending it to the
         end of the queue.
         */
    

    // if ([itemRemoved isKindOfClass:[AVPlayerItem class]])
    // {
    // [itemRemoved seekToTime:kCMTimeZero];
    // [self stopObservingPlayerAndItem];
    // [player insertItem:itemRemoved afterItem:nil];
    // [self startObservingPlayerAndItem];
    // }
    }
    }
    else if (context == AVLoopPlayerCurrentItemStatusObservationContext)
    {
    AVPlayerItemStatus newItemStatus = (AVPlayerItemStatus)[[changeDictionary objectForKey:NSKeyValueChangeNewKey] unsignedIntegerValue];
    if (newItemStatus == AVPlayerItemStatusFailed) {
    AVQueuePlayer player = (AVQueuePlayer )object;
    NSLog(@”End looping since player item has failed with error %@”, player.currentItem.error);
    [self stopPlayback];
    }
    }
    }

  • (void)stopPlayback
    {
    [_queuePlayer pause];
    [self stopObservingPlayerAndItem];
    [_queuePlayer removeAllItems];
    }

pragma mark - public methods

  • (void)pausePlayer{
    AVQueuePlayer *queuePalyer = [ZNAudioPlayerUtil shareInstance].queuePlayer;
    if (queuePalyer.timeControlStatus == AVPlayerTimeControlStatusPlaying) {
    [queuePalyer pause];
    }
    }

  • (void)resumePlayer{
    AVQueuePlayer *queuePalyer = [ZNAudioPlayerUtil shareInstance].queuePlayer;
    if (queuePalyer.timeControlStatus == AVPlayerTimeControlStatusPaused) {
    [queuePalyer play];
    }
    }

  • (void)seekToTime:(CMTime)time{
    AVQueuePlayer *queuePlayer = [ZNAudioPlayerUtil shareInstance].queuePlayer;
    if (queuePlayer) {
    [queuePlayer seekToTime:time];
    }
    }

  • (CGFloat)currentTime{
    AVQueuePlayer *queuePlayer = [ZNAudioPlayerUtil shareInstance].queuePlayer;
    if (queuePlayer) {
    return [ZNAudioPlayerUtil totalTime] - CMTimeGetSeconds(queuePlayer.currentItem.currentTime);
    }
    return 0;
    }

  • (CGFloat)totalTime{
    AVQueuePlayer *queuePlayer = [ZNAudioPlayerUtil shareInstance].queuePlayer;
    if (queuePlayer) {
    CGFloat totalDuration = 0;
    for (AVPlayerItem *item in queuePlayer.items) {
    CGFloat duration = CMTimeGetSeconds(item.duration);
    totalDuration += duration;
    }
    return totalDuration;
    }
    return 0;
    }

@end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值