AVPlayer简单使用

前言

  • 公司APP使用七牛的播放器,各种崩溃,各种卡顿问题,一怒之下抽时间,看看有没苹果自带框架比较好解决播放问题,又不想引入其他库增加APP大小。发现AVPlayer,确实效率不错,基本满足业务需求,和同事确认了APP某部分功能播放文件只使用了mp4,m3u8,flv (按使用量先后排序)。AVPlayer不支持flv,所以mp4,m3u8使用AVPlayer,其他使用七牛播放器SDK.

注意

  • 由于项目使用较多约束,但是AVPlayer默认渲染不能根据约束大小变化,所以需要重载UIView的一个方法,强制改变layerClass,作为AVPlayer的渲染层,以便动态适配约束。
.h 文件

#import <UIKit/UIKit.h>
@interface TTVAVPlayerView : UIView
@end



.m 文件

#import "TTVAVPlayerView.h"
#import <AVFoundation/AVFoundation.h>

@implementation TTVAVPlayerView

+ (Class)layerClass {
    return [AVPlayerLayer class];
}

@end

AVPlayer

使用库

#import <AVFoundation/AVFoundation.h>
#import<MediaPlayer/MediaPlayer.h>
#import<CoreMedia/CoreMedia.h>

初始化

- (NSDictionary*)setupPlayer:(NSString*)url{
    

    //防盗链headers
    NSMutableDictionary * headers = [NSMutableDictionary dictionary];
    [headers setObject:@"http://*.itouchtv.cn" forKey:@"Referer"];
    NSURL*liveURL = [NSURL URLWithString:url];
    AVAsset*liveAsset = [AVURLAsset URLAssetWithURL:liveURL options:@{@"AVURLAssetHTTPHeaderFieldsKey" : headers} ];
    AVPlayerItem* playerItem = [AVPlayerItem playerItemWithAsset:liveAsset];
    if (iOS9_OR_LATER) {
        playerItem.canUseNetworkResourcesForLiveStreamingWhilePaused = true;
    }
    if (iOS10_OR_LATER) {
        playerItem.preferredForwardBufferDuration = kPreferredForwardBufferDuration;
    }

    //播放器
    AVPlayer*player = [AVPlayer playerWithPlayerItem:playerItem];
    
    //渲染对象
    TTVAVPlayerView* avPlayerView = [[TTVAVPlayerView alloc ]init];
    AVPlayerLayer* playerLayer = (AVPlayerLayer *)avPlayerView.layer;
    [playerLayer setPlayer:player];
    
    
    return @{@"AVAsset":player,@"TTVAVPlayerView":avPlayerView};
}

状态监听

  • 方法一 KVO监听
static NSString* const kStatusKeyName = @"status";
static NSString* const kLoadedTimeRangesKeyName = @"loadedTimeRanges";
static NSString* const kPlaybackBufferEmptyKeyName = @"playbackBufferEmpty";
static NSString* const kPlaybackLikelyToKeepUpKeyName = @"playbackLikelyToKeepUp";

 - (void)addKVO:(AVPlayerItem*)playerItem{
    if (playerItem == nil) return;
    [playerItem addObserver:self forKeyPath:kStatusKeyName options:NSKeyValueObservingOptionNew context:nil];
    [playerItem addObserver:self forKeyPath:kLoadedTimeRangesKeyName options:NSKeyValueObservingOptionNew context:nil];
    [playerItem addObserver:self forKeyPath:kPlaybackBufferEmptyKeyName options:NSKeyValueObservingOptionNew context:nil];
    [playerItem addObserver:self forKeyPath:kPlaybackLikelyToKeepUpKeyName options:NSKeyValueObservingOptionNew context:nil];
}

- (void)delKVO:(AVPlayerItem*)playerItem{
    @try {
        [playerItem removeObserver:self forKeyPath:kStatusKeyName context:nil];
        [playerItem removeObserver:self forKeyPath:kLoadedTimeRangesKeyName context:nil];
        [playerItem removeObserver:self forKeyPath:kPlaybackBufferEmptyKeyName context:nil];
        [playerItem removeObserver:self forKeyPath:kPlaybackLikelyToKeepUpKeyName context:nil];
    }
    @catch (NSException *exception) {
        DLog(@"多次删除了");
    }
}

//监听获得消息
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    {
        AVPlayerItem *playerItem = (AVPlayerItem *)object;
        
        if ([keyPath isEqualToString:@"status"]) {
            if ([playerItem status] == AVPlayerStatusReadyToPlay) {
                self.playerStatus = TTVPlayerStatusReady;
                
                //status 点进去看 有三种状态
                CGFloat duration = playerItem.duration.value / playerItem.duration.timescale; //视频总时间
                DLog(@"准备好播放了,总时间:%.2f", duration);//还可以获得播放的进度,这里可以给播放进度条赋值了
                
                
            } else if ([playerItem status] == AVPlayerStatusFailed || [playerItem status] == AVPlayerStatusUnknown) {
    
                [_player pause];
            }
            
        } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {  //监听播放器的下载进度
            
            NSArray *loadedTimeRanges = [playerItem loadedTimeRanges];
            CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];// 获取缓冲区域
            float startSeconds = CMTimeGetSeconds(timeRange.start);
            float durationSeconds = CMTimeGetSeconds(timeRange.duration);
            NSTimeInterval timeInterval = startSeconds + durationSeconds;// 计算缓冲总进度
            CMTime duration = playerItem.duration;
            CGFloat totalDuration = CMTimeGetSeconds(duration);
            CGFloat bufferdDuration = round(timeInterval);
            
            DLog(@"下载进度:%.2f 当前下载时长:%.2f 总时长:%.2f", timeInterval / totalDuration,self.bufferedTime,totalDuration);
            
            
        } else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) { //监听播放器在缓冲数据的状态
            
            DLog(@"缓冲不足暂停了");
            
            
        } else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) {
            DLog(@"缓冲达到可播放程度了");
            
           

            //由于 AVPlayer 缓存不足就会自动暂停,所以缓存充足了需要手动播放,才能继续播放
            [_player play];
            
        }
 
    }
}
  • 方法二 通知监听
- (void)addNotification{
    
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [[NSNotificationCenter defaultCenter] removeObserver:self.player];
    
    @weakify(self)
    [[NSNotificationCenter defaultCenter]
     addObserverForName:AVPlayerItemDidPlayToEndTimeNotification
     object:nil queue:[NSOperationQueue mainQueue]
     usingBlock:^(NSNotification * _Nonnull note) {
        @strongify(self)
         DLog(@"播放完成");
    }];
    
    [[NSNotificationCenter defaultCenter]
     addObserverForName:AVPlayerItemFailedToPlayToEndTimeNotification
     object:nil queue:[NSOperationQueue mainQueue]
     usingBlock:^(NSNotification * _Nonnull note) {
         @strongify(self)
         DLog(@"播放出错");
     }];
}
  • 一些状态的判断
@property (nonatomic, strong) AVPlayer *player;


- (BOOL)playing {
    if (self.playerItem.isPlaybackLikelyToKeepUp &&  self.player.rate == 1) {
        return true;
    }
    return false;
}

- (NSUInteger)currentPlayingTime {
    CMTime time = self.player.currentTime;
    return CMTimeGetSeconds(time);
}

- (NSUInteger)totalPlayingTime {
    CMTime time = self.player.currentItem.duration;
    Float64 seconds = CMTimeGetSeconds(time);
    return seconds;
}

- (void)seekTo:(NSUInteger)sec{
    CMTime time = CMTimeMakeWithSeconds(sec, 1);
    [self.player seekToTime:time];
}

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

- (void)resume{
    [self seekTo:self.currentPlayingTime];
    [self.player play];
}

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

- (void)stop{
    [self.player pause];
    self.player.muted = true;
    self.player.rate = 0.0;
}

原文:http://raychow.linkfun.top/2017/12/28/archives/1_ios/AVPlayer/index/

猜你喜欢

转载自my.oschina.net/u/3729367/blog/1596358