这篇文章介绍一下基于
AVPlayer
的视频播放器。
先上Demo,点击我的GitHub下载Demo。
AVPlayer介绍
AVPlayer最大的优势是能够定制播放器的界面样式。AVPlayer本身是不能播放视频的,要播放视频,必须创建一个AVPlayerLayer,将AVPlayerLayer添加到控制器视图才能播放。除了AVPlayer和AVPlayerLayer,还需要一个类AVPlayerItem。AVPlayerItem是媒体资源管理对象,管理视频的一些基本信息和状态,一个AVPlayerItem对应着一个视频资源。
创建好AVPlayer、AVPlayerLayer、AVPlayerItem对象之后就能开始播放视频。下面一一介绍播放暂停、播放时间、缓冲进度等功能。
ToolBar.png1、播放/暂停
使用视频URL初始化一个AVPlayerItem,把AVPlayerItem设置为AVPlayer的currentItem,然后通过KVO监听AVPlayerItem的属性[playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil]
,当属性变为AVPlayerStatusReadyToPlay
时,通过AVPlayer调用- (void)play
方法即可播放视频。
AVPlayer的- (void)play
和- (void)pause
分别控制播放和暂停,根据AVPlayer的播放速度rate可以判断当前是否为播放状态,rate=0暂停,rate=1播放。视频播放完成后AVPlayerItem会发送AVPlayerItemDidPlayToEndTimeNotification
通知。
2、视频时间
视频时间包含两部分:视频总时间和视频当前播放时间。视频总时间通过CMTimeGetSeconds(_player.currentItem.duration)
获取,当前播放时间通过CMTimeGetSeconds(_player.currentTime)
获取。
获取到这两个时间,就可以展示视频播放进度。播放进度需要一秒更新一次,可以用定时器来更新,也可以用AVPlayer的方法- (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(nullable dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block
在Block里更新。建议使用这个方法更新时间,因为它更新时间更加准确,使用block的参数time
获得准确的播放进度。
获取播放进度之后,分别给显示时间的Label和UISlider赋值,用户可以拖动UISlider对视频进行拖动播放,在拖拽结束之后,使用- (void)seekToTime:(CMTime)time toleranceBefore:(CMTime)toleranceBefore toleranceAfter:(CMTime)toleranceAfter
让视频从拖拽到的地方开始播放。
3、缓冲进度
通过KVO监听AVPlayerItem的loadedTimeRanges属性来监听缓冲进度更新,在KVO中添加下面代码获取当前缓冲进度。
NSArray *array = _player.currentItem.loadedTimeRanges;
CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];//本次缓冲时间范围
NSTimeInterval startSeconds = CMTimeGetSeconds(timeRange.start);//本次缓冲起始时间
NSTimeInterval durationSeconds = CMTimeGetSeconds(timeRange.duration);//缓冲时间
NSTimeInterval totalBuffer = startSeconds + durationSeconds;//缓冲总长度
float totalTime = CMTimeGetSeconds(_player.currentItem.duration);//视频总长度
float progress = totalBuffer/totalTime;//缓冲进度
4、视频切换
每个AVPlayer只能播放一个视频,切换视频可以用- (void)replaceCurrentItemWithPlayerItem:(AVPlayerItem *)item
来实现。
播放器代码已经上传了我的GitHub,欢迎下载,这里就不贴代码了。
** 全屏播放效果:**
全屏播放.png
网友评论
/*!
@property rate
@abstract Indicates the desired rate of playback; 0.0 means "paused", 1.0 indicates a desire to play at the natural rate of the current item.
@discussion
Setting the value of rate to 0.0 pauses playback, causing the value of timeControlStatus to change to AVPlayerTimeControlStatusPaused.
Setting the rate to a non-zero value causes the value of timeControlStatus to become either AVPlayerTimeControlStatusWaitingToPlayAtSpecifiedRate or AVPlayerTimeControlStatusPlaying, depending on whether sufficient media data has been buffered for playback to occur and whether the player's default behavior of waiting in order to minimize stalling is permitted. See discussion of AVPlayerTimeControlStatus for more details.
AVPlayer can reset the desired rate to 0.0 when a change in overall state requires playback to be halted, such as when an interruption occurs on iOS, as announced by AVAudioSession, or when the playback buffer becomes empty and playback stalls while automaticallyWaitsToMinimizeStalling is NO.
The effective rate of playback may differ from the desired rate even while timeControlStatus is AVPlayerTimeControlStatusPlaying, if the processing algorithm in use for managing audio pitch requires quantization of playback rate. For information about quantization of rates for audio processing, see AVAudioProcessingSettings.h. You can always obtain the effective rate of playback from the currentItem's timebase; see the timebase property of AVPlayerItem.
*/
/*! @property rate
@abstract Changes the playback rate of the input signal
@discussion
A value of 2.0 results in the output audio playing one octave higher.
A value of 0.5, results in the output audio playing one octave lower.
Range: 0.5 -> 2.0
Default: 1.0
Mixer: AVAudioEnvironmentNode
*/
这是苹果API对rate的描述,看样子只能设置0.5到2.0之间的速率。。
异常断点停在:[self.player removeTimeObserver:self];这一句
具体错误:Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'You did not supply a reference to an object returned by either -addPeriodicTimeObserverForInterval:queue:usingBlock: or -addBoundaryTimeObserverForTimes:queue:usingBlock:'
请帮忙解决一下