美文网首页
音视频播放:AVPlayer/AVPlayerItem/AVPl

音视频播放:AVPlayer/AVPlayerItem/AVPl

作者: 大成小栈 | 来源:发表于2024-01-15 14:09 被阅读0次

原文地址:https://www.cnblogs.com/fengfeng159/articles/11005065.html

AVPlayer是一个可以播放任何格式的全功能影音播放器
支持视频格式: WMV,AVI,MKV,RMVB,RM,XVID,MP4,3GP,MPG等。
支持音频格式:MP3,WMA,RM,ACC,OGG,APE,FLAC,FLV等。

AVPlayer 播放器是基于AVFountion框架支持的。所以在使用AVPlayer 时候,需要引用

# import <AVFoundation/AVFoundation.h>

先了解播放器的类

AVPlayer:控制播放器的播放,暂停,播放速度
AVURLAsset : AVAsset 的一个子类,使用 URL 进行实例化,实例化对象包换 URL 对应视频资源的所有信息。
AVPlayerItem:管理资源对象,提供播放数据源
AVPlayerLayer:负责显示视频,如果没有添加该类,只有声音没有画面

那么最简单的一个播放器,其实很简单,就是初始化一个AVPlayer

player = try AVAudioPlayer(contentsOf: audioUrl)

但是它太简单了,仅可以完成音频的播放,连画面都没有。回看上面播放相关类的介绍,是因为缺少AVPlayerLayer;作为一个播放器,我不能只播放一条视频啊,我还想根据需要切换视频,那我们就得把AVPlayerItem也加上

NSURL *playUrl = [NSURL URLWithString:@"[http://baobab.wdjcdn.com/14573563182394.mp4](https://link.jianshu.com/?t=http%3A%2F%2Fbaobab.wdjcdn.com%2F14573563182394.mp4)"];
self.playerItem = [AVPlayerItem playerItemWithURL:playUrl];
//如果要切换视频需要调AVPlayer的replaceCurrentItemWithPlayerItem:方法
self.player = [AVPlayer playerWithPlayerItem:_playerItem];
self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
self.playerLayer.frame = _videoView.bounds;
//放置播放器的视图
[self.videoView.layer addSublayer:self.playerLayer];
[_player play];

这就是基本的播放视频的组成。

如果需要更多的扩展,那么我们可以更细化的了解:

1. AVPlayer常用的控制方法

前面讲过该类是控制视频播放行为的,他的使用比较简单。

// 播放视频:
[self.player play];

// 暂停视频:
[self.player pause];

// 更改速度:
self.player.rate = 1.5;//注意更改播放速度要在视频开始播放之后才会生效

2. AVPlayerItem的控制

AVPlayerItem作为资源管理对象,它控制着视频从创建到销毁的诸多状态。

2.1 播放状态 status

typedef NS_ENUM(NSInteger, AVPlayerItemStatus) {
AVPlayerItemStatusUnknown,//未知
AVPlayerItemStatusReadyToPlay,//准备播放
AVPlayerItemStatusFailed//播放失败
};

我们使用KVO监测playItem.status,可以获取播放状态的变化

[self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];

在监听回调中:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
if ([object isKindOfClass:[AVPlayerItem class]]) {
if ([keyPath isEqualToString:@"status"]) {
switch (_playerItem.status) {
case AVPlayerItemStatusReadyToPlay:
//推荐将视频播放放在这里
[self play];
break;

             case AVPlayerItemStatusUnknown:
                    NSLog(@"AVPlayerItemStatusUnknown");
                    break;

             case AVPlayerItemStatusFailed:
                    NSLog(@"AVPlayerItemStatusFailed")
                    break;

            default:
                    break;
           }

    }

}

虽然设置完播放配置我们可以直接调用[self.player play];进行播放,但是更稳妥的方法是在回调收到AVPlayerItemStatusReadyToPlay时进行播放

2.2 视频的时间信息

在AVPlayer中时间的表示有一个专门的结构体CMTime

typedef struct{
CMTimeValue value; // 帧数
CMTimeScale timescale; // 帧率(影片每秒有几帧)
CMTimeFlags flags;
CMTimeEpoch epoch;
} CMTime;

CMTime是以分数的形式表示时间,value表示分子,timescale表示分母,flags是位掩码,表示时间的指定状态。

获取当前播放时间,可以用value/timescale的方式:

float currentTime = self.playItem.currentTime.value/item.currentTime.timescale;

还有一种利用系统提供的方法,我们用它获取视频总时间:

float totalTime = CMTimeGetSeconds(item.duration);

如果我们想要添加一个计时的标签不断更新当前的播放进度,有一个系统的方法:

  • (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(nullable dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block;

方法名如其意, “添加周期时间观察者” ,参数1 interal 为CMTime 类型的,参数2 queue为串行队列,如果传入NULL就是默认主线程,参数3 为CMTime 的block类型。
简而言之就是,每隔一段时间后执行 block。
比如:我们把interval设置成CMTimeMake(1, 10),在block里面刷新label,就是一秒钟刷新10次。

正常观察播放进度一秒钟一次就行了,所以可以这么写:

[self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:nil usingBlock:^(CMTime time) {

  AVPlayerItem *item = WeakSelf.playerItem;
  NSInteger currentTime = item.currentTime.value/item.currentTime.timescale;
  NSLog(@"当前播放时间:%ld",currentTime);

}];

2.3 loadedTimeRange 缓存时间

获取视频的缓存情况我们需要监听playerItem的loadedTimeRanges属性

[self.playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];

在KVO的回调里:

if ([keyPath isEqualToString:@"loadedTimeRanges"]){
NSArray *array = _playerItem.loadedTimeRanges;
CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];//本次缓冲时间范围
float startSeconds = CMTimeGetSeconds(timeRange.start);
float durationSeconds = CMTimeGetSeconds(timeRange.duration);
NSTimeInterval totalBuffer = startSeconds + durationSeconds;//缓冲总长度
NSLog(@"当前缓冲时间:%f",totalBuffer);
}

2.4 playbackBufferEmpty

表示视频缓存内容是否可以满足播放需求,YES:显示转圈动画提示。

监听playbackBufferEmpty我们可以获取当缓存不够,视频加载不出来的情况:

[self.playerItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];

在KVO回调里:

if ([keyPath isEqualToString:@"playbackBufferEmpty"]) {

 //some code show loading

}

2.5 playbackLikelyToKeepUp 缓存是否可以满足播放

playbackLikelyToKeepUp和playbackBufferEmpty是一对,用于监听缓存足够播放的状态

[self.playerItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
/* ... */
if([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) {
//由于 AVPlayer 缓存不足就会自动暂停,所以缓存充足了需要手动播放,才能继续播放
[_player play];
}

3. AVPlayerLayer 显示视频的画布

播放相关通知:

  • 声音类
    //声音被打断的通知(电话打来)
    AVAudioSessionInterruptionNotification
    //耳机插入和拔出的通知
    AVAudioSessionRouteChangeNotification

  • 播放类
    //播放完成
    AVPlayerItemDidPlayToEndTimeNotification
    //播放失败
    AVPlayerItemFailedToPlayToEndTimeNotification
    //异常中断
    AVPlayerItemPlaybackStalledNotification

对于播放完成的通知我们可以这么写:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerMovieFinish:) name:AVPlayerItemDidPlayToEndTimeNotification object:[self.player currentItem]];

  • 系统状态
    //进入后台
    UIApplicationWillResignActiveNotification
    //返回前台
    UIApplicationDidBecomeActiveNotification

提示:所有通知和KVO的使用我们都要记得在不用时remove掉。

以此,视频播放基本组件可以根据自己的需求进行自定义,如暂停、播放,滑动快进、倍速播放等操作。

相关文章

网友评论

      本文标题:音视频播放:AVPlayer/AVPlayerItem/AVPl

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