視音訊播放
視音訊播放可以說是視訊開發中比較簡單的了,只要建立幾個物件,監聽一些屬性值即可完成一個視音訊播放器。
我們需要了解下面幾個類:
- AVPlayer
- AVPlayerLayer
- AVPlayerItem
AVPlayer是播放器物件,用來控制視音訊的播放和暫停,還可以用來跳轉進度;
AVPlayerLayers用於展示視訊,AVPlayer需要在AVPlayerLayer上才能展示;
AVPlayerItem是資源管理器,用於載入資源;
播放器的建立
- (void)initializedPlayerWith:(NSURL *)url {
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:url]; //資源管理器
if (_player.currentItem != nil) {
[self removeObserverFromPlayerItem:_player.currentItem]; //移除之前的監聽
[_player replaceCurrentItemWithPlayerItem:playerItem]; //切換資源
[self pause];
}else {
_player = [AVPlayer playerWithPlayerItem:playerItem];
}
[_playerLayer removeFromSuperlayer];
_playerLayer = nil;
if (_containerView != nil) {
_playerLayer = [AVPlayerLayer playerLayerWithPlayer:_player];
_playerLayer.videoGravity = _videoGravity; //視訊填充模式
_playerLayer.frame = _containerView.bounds;
[_containerView.layer addSublayer:_playerLayer];
}
//監聽播放器
[self addObserverFromPlayerItem:playerItem];
[self addProgressNotification];
[self addPlayerFinishNotification];
}
當AVPlayerLayer父檢視的frame發生了變化,記得及時修改AVPlayerLayer的frame
其他的程式碼很簡單,關於監聽的函式下面會講
緩衝、播放、暫停和跳轉進度
緩衝
在播放器建立之後,會自動進行緩衝
//快取
- (void)buffer {
if (_playerLayer == nil && self.fileUrl) {
[self initializedPlayerWith:self.fileUrl];
}
}
播放
- (void)play {
[self buffer];
if (_player && _player.rate == 0) {
[_player play];
}
}
暫停
- (void)pause {
if (_player && _player.rate != 0) {
[_player pause];
}
}
跳轉進度
這裡value取值範圍是0-1,
說明一點,被註釋掉的跳轉函式seekToTime只能跳轉大於1秒的進度,要實現精確跳轉,使用後面那個
- (void)jumpProgressWith:(CGFloat)value {
if (_playerLayer == nil) return;
CMTime time = CMTimeMakeWithSeconds(value * CMTimeGetSeconds(_player.currentItem.duration), _player.currentItem.currentTime.timescale);
[self jumpProgressWithTime:time];
}
- (void)jumpProgressWithTime:(CMTime)time {
if (_playerLayer == nil) return;
// [_player seekToTime:time]; //該方法無法進行精確的跳轉,只能跳轉大於1秒後的進度
[self.player seekToTime:time toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]; //這樣才可以進行1秒內的精確跳轉
}
視訊填充模式
- AVLayerVideoGravityResizeAspect 預設,按視訊比例顯示,直到寬或高佔滿,未達到的地方顯示父檢視
- AVLayerVideoGravityResizeAspectFill 按原比例顯示視訊,直到兩邊螢幕佔滿,但視訊部分內容可能無法顯示
- AVLayerVideoGravityResize 按父檢視尺寸顯示,可能與原視訊比例不同
AVPlayerLayer.videoGravity
播放器狀態監聽
在建立播放器物件後,通過監聽資源管理器AVPlayerItem的屬性,可以得知視音訊的總長度和緩衝進度
//開始監聽
- (void)addObserverFromPlayerItem:(AVPlayerItem *)playerItem {
[playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil]; //開始或暫停
[playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil]; //快取進度
}
//監聽回撥
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
AVPlayerItem *playerItem = object;
if ([keyPath isEqualToString:@"status"]) {
AVPlayerStatus status = [[change valueForKey:@"new"] integerValue];
if (status ==AVPlayerStatusReadyToPlay) {
CGFloat totalTime = CMTimeGetSeconds(playerItem.duration);
NSLog(@"正在播放,視訊總長度為 %.2f",totalTime);
}
}else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
NSArray *array = playerItem.loadedTimeRanges;
//本次緩衝時間範圍
CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];
CGFloat startSecond = CMTimeGetSeconds(timeRange.start);
CGFloat durationSecond = CMTimeGetSeconds(timeRange.duration);
CGFloat totalTime = CMTimeGetSeconds(playerItem.duration);
//緩衝總長度
NSTimeInterval totalBuffer = startSecond + durationSecond;
CGFloat bufferProgress = totalBuffer / totalTime;
if (_bufferBlock) {
_bufferBlock(bufferProgress);
}
}
}
//別忘了移除監聽
- (void)removeObserverFromPlayerItem:(AVPlayerItem *)playerItem {
[playerItem removeObserver:self forKeyPath:@"status"];
[playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
}
播放進度
//監聽播放進度
- (void)addProgressNotification {
AVPlayerItem *playerItem = _player.currentItem;
if (playerItem == nil) return;
id playProgressObserver; //播放進度監聽物件
//先移除上一個視訊的監聽
if (playProgressObserver) {
[_player removeTimeObserver:playProgressObserver];
}
//每秒監聽一次播放進度
__weak typeof(self) weekSelf = self;
/*
CMTimeMake(value,timeScale):
value表示第幾幀,timeScale表示幀率,即每秒多少幀
CMTimeMake(1,10):第一幀,幀率為每秒10幀,轉換為時間公式:value/timeScale,即1/10=0.1,表示在視訊的0.1秒時刻
CMTimeMakeWithSeconds的第一個引數可以使float,其他都一樣,不過因為這個比較好用,所以我一般用這個
*/
playProgressObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMake(playerProgressTime, playerProgressTime) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
CGFloat currentTime = CMTimeGetSeconds(time);
CGFloat totalTime = CMTimeGetSeconds(playerItem.duration);
if (currentTime) {
CGFloat playProgress = currentTime / totalTime;
if (weekSelf.progressBlock) {
weekSelf.progressBlock(playProgress,currentTime,totalTime);
}
}
}];
}
關於CMTime的使用建議先去百度瞭解一下,對視訊開發的理解會很有好處
播放完成回撥
- (void)addPlayerFinishNotification {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playFinishNotification) name:AVPlayerItemDidPlayToEndTimeNotification object:_player.currentItem];
}
現在我們完成了視音訊的播放,使用本地/網路資源去試試吧。
相關文章
- iOS開發:音訊播放、錄音、視訊播放、拍照、視訊錄製iOS音訊
- Android音視訊之MediaPlayer音視訊播放Android
- iOS開發系列--音訊播放、錄音、視訊播放、拍照、視訊錄製(轉)iOS音訊
- 音視訊入門之音訊採集、編碼、播放音訊
- 帶你用AVPlayer實現音訊和視訊播放音訊
- Android 音視訊開發 - 使用AudioTrack播放音訊Android音訊
- 【秒懂音視訊開發】12_播放WAV
- iOS 9音訊應用播放音訊之iOS 9音訊播放進度iOS音訊
- android 音訊播放 SoundPoolAndroid音訊
- iOS AVAudioPlayer(音訊播放)iOS音訊
- hqplayer pro 4,音訊播放音訊
- Flutter(十) 音訊+影片播放Flutter音訊
- opencv視訊播放OpenCV
- 短視訊“音訊化”,音樂“視訊化”音訊
- Elmedia Video Player Pro 專業音視訊播放器IDE播放器
- iOS 9音訊應用播放音訊之控制播放速度iOS音訊
- 音視訊--音訊入門音訊
- 音視訊–音訊入門音訊
- Android開發 海康威視 多路視訊播放(同時播放視訊)Android
- 微信audio音訊不能播放音訊
- JS控制音訊順序播放JS音訊
- unity中播放視訊Unity
- iOS視訊播放(二)iOS
- 視訊播放學習
- 視訊播放器播放器
- 音視訊--視訊入門
- Elmedia Video Player Pro for Mac(專業音視訊播放器)IDEMac播放器
- Mac專業音視訊播放器:Elmedia Video Player ProMac播放器IDE
- AVFoundation 文字轉語音和音訊錄製 播放音訊
- Android音視訊之MediaRecorder音視訊錄製Android
- 使用MediaCodec硬解碼h.265視訊及音訊進行播放音訊
- FFmpeg+SDL2實現簡易音視訊同步播放器播放器
- Android音視訊播放器框架看這些就夠了Android播放器框架
- Android 音視訊 - MediaCodec 編解碼音視訊Android
- IOS音視訊(二)AVFoundation視訊捕捉iOS
- [Android多媒體技術] 播放Raw/Assets音視訊方法總結Android
- Vue + WebRTC 實現音視訊直播(附自定義播放器樣式)VueWeb播放器
- Android 音視訊開發 視訊編碼,音訊編碼格式Android音訊