日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

iOS开发——AVPlayer自定义播放器(持续更新,学习中)

發布時間:2023/12/31 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 iOS开发——AVPlayer自定义播放器(持续更新,学习中) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 一、 前言
  • 二、相關知識點
    • 2.1 AVplayerItem
    • 2.2 AVplayer
    • 2.3 AVPlayerLayer
  • 三、代碼部分
    • 3.1 單例
    • 3.2 將播放器封裝成view
  • 四、demo

一、 前言

邊學邊記錄AVPlayer封裝一個功能十分 全的自定義播放器,目前在學習階段,demo和文章會根據學習進度與總結情況去更新,歡迎各位批評指正。
2020年8月1日更新3.2

二、相關知識點

  • AVPlayer本身并不顯示視頻!需要一個AVPlayerLayer播放層來顯示視頻,然后添加到父視圖的layer中。
  • AVPlayer只負責視頻管理和調控!而視頻資源是由AVPlayerItem提供的,每個AVPlayerItem對應一個視頻地址。
  • 工程內需要引入AVFoundation庫
  • 持續補充……

2.1 AVplayerItem

(instancetype)playerItemWithURL:(NSURL *)URL; 初始化視頻資源方法
duration 視頻總時長
status 視頻資源的狀態 (需要監聽的屬性)
loadedTimeRanges 在線視頻的緩沖進度 (需要監聽的屬性)
playbackBufferEmpty 進行跳轉,無緩沖 (可選監聽)
playbackLikelyToKeepUp 進行跳轉,有緩沖 (可選監聽)

2.2 AVplayer

(void) play;
(void) pause; 播放暫停
(void) seekToTime:(CMTime)time;跳轉到指定時間
(CMTime) currentTime;獲取當前播放進度
(void)removeTimeObserver:(id)observer;移除監聽 (銷毀播放器的時候調用)
(void)replaceCurrentItemWithPlayerItem:(nullable AVPlayerItem *)item;切換視頻資源

2.3 AVPlayerLayer

videoGravity 視頻的填充方式

  • AVQueuePlayer:這個是用于處理播放列表的操作,待學習…
  • 三、代碼部分

    3.1 單例

  • 聲明相關屬性
  • // AVAsset對象 @property (nonatomic, strong) AVAsset *videoAsset; // 播放器對象 @property (nonatomic, strong) AVPlayer *player; // 播放屬性 @property (nonatomic, strong) AVPlayerItem *playerItem; // 播放容器,用于放playerlayer @property (nonatomic, strong) UIView *videoView; // 用于播放的layer @property (nonatomic, strong) AVPlayerLayer *playLayer;
  • 實現播放器
  • - (UIView *)createPlayViewFrame:(CGRect)frame withStringURL:(NSString *)stringURL isLocalFile:(BOOL)isTrue{if (isTrue == YES){self.videoAsset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:stringURL] options:nil];} else {self.videoAsset = [[AVURLAsset alloc] initWithURL:[NSURL URLWithString:stringURL] options:nil];}self.playerItem = [AVPlayerItem playerItemWithAsset:self.videoAsset];// 添加觀察者[self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];// 獲取當前視頻的尺寸[self getVideoSize:self.videoAsset];self.player = [AVPlayer playerWithPlayerItem:self.playerItem];// 監聽是否 播放完畢[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playBackFinished:withBlock:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.playerItem];self.playLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];self.playLayer.frame = frame;// 填充模式self.playLayer.videoGravity = AVLayerVideoGravityResizeAspect;UIView *videoView = [[UIView alloc] initWithFrame:frame];[videoView.layer addSublayer:self.playLayer];return videoView; }
  • 調用
  • - (void)viewDidLoad {[super viewDidLoad];UIView *videoView = [[UIView alloc] initWithFrame:self.view.frame];videoView = [[SMZVideoManager sharedInstance] createPlayViewFrame:CGRectMake(0, 0, creenWidth, creenHeight) withStringURL:videoUrl isLocalFile:NO];[self.view addSubview:videoView];// 也可以將視頻播放完畢的監聽加在這里,需要將manager中的移除掉[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playBackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:[[SMZVideoManager sharedInstance] getCurrentPlayerItem]];[[SMZVideoManager sharedInstance] startVideo]; }
  • 觀察者監聽相關
  • #pragma mark - 觀察者相關 - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{if ([keyPath isEqualToString:@"status"]){AVPlayerItem *playItem = (AVPlayerItem *)object;if (playItem.status == AVPlayerStatusReadyToPlay){// 可以播放} else if (playItem.status == AVPlayerStatusFailed){// 失敗} else {// 未知錯誤AVPlayerStatusUnknown}} }- (void)playBackFinished:(NSNotification *)notification withBlock:(void (^)(void))code{// 播放完畢的操作,這里 提供重新播放的能力self.playerItem = [notification object];[self.playerItem seekToTime:kCMTimeZero completionHandler:nil];code(); }

    3.2 將播放器封裝成view

    單例形式適合多個視頻由一個播放器控制,但是上述寫法明顯存在問題 ,控制不了每個視圖,所以現在想讓每個視圖有自己的播放器

    // 相關方法 - (instancetype)initWithFrame:(CGRect)frame;- (void)setupPlayerWith:(NSURL *)videoURL;- (void)play;- (void)pause;- (void)replay;- (void)destory; // 相關實現 - (instancetype)initWithFrame:(CGRect)frame{self = [super initWithFrame:frame];if (self){self.backgroundColor = [UIColor whiteColor];}return self; }// 準備播放器 - (void)setupPlayerWith:(NSURL *)videoURL{AVURLAsset *sset = [[AVURLAsset alloc] initWithURL:videoURL options:nil];self.videoScale = [self getVideoSize:sset];[self creatPlayer:videoURL]; }// 獲取播放item - (AVPlayerItem *)getPlayerItem:(NSURL *)videoURL {AVPlayerItem *item = [AVPlayerItem playerItemWithURL:videoURL];return item; }// 創建播放器 - (void)creatPlayer:(NSURL *)videoURL {if (!_player) {self.currentItem = [self getPlayerItem:videoURL];_player = [AVPlayer playerWithPlayerItem:self.currentItem];[self creatPlayerLayer];[self addPlayerObserver];[self addObserverWithPlayItem:self.currentItem];[self addNotificatonForPlayer];} }// 創建視圖 - (void)creatPlayerLayer {CGFloat origin_x = 15;CGFloat main_width = kScreenWidth - (origin_x * 2);self.videoView = [[UIView alloc] initWithFrame:CGRectMake(origin_x, 85, main_width, main_width * self.videoScale)];AVPlayerLayer *layer = [AVPlayerLayer playerLayerWithPlayer:self.player];layer.frame = CGRectMake(0, 0, self.videoView.frame.size.width, self.videoView.frame.size.height);layer.videoGravity = AVLayerVideoGravityResizeAspect;[self.videoView.layer addSublayer:layer];[self addSubview:self.videoView];}// 獲取視頻的比例 - (CGFloat)getVideoSize:(AVAsset *)videoAsset{NSArray *array = videoAsset.tracks;CGSize videoSize = CGSizeZero;for(AVAssetTrack *track in array){if([track.mediaType isEqualToString:AVMediaTypeVideo]){videoSize = track.naturalSize;}}CGFloat videoH = videoSize.height;CGFloat videoW = videoSize.width;return videoH / videoW; }#pragma mark - 播放器功能 // 播放 - (void)play {if (self.player.rate == 0) {[self addNotificatonForPlayer];[self addPlayerObserver];}[self.player play]; }// 暫停 - (void)pause {if (self.player.rate == 1.0) {[self.player pause];[self.player seekToTime:kCMTimeZero];} }// 重新開始 - (void)replay {if (self.player.rate == 1.0) {[self.player pause];[self.player seekToTime:kCMTimeZero];[self removeNotification];} else if (self.player.rate == 0){[self addNotificatonForPlayer];[self play];} }- (void)destory {[self pause];[self removeNotification];[self removePlayerObserver]; }#pragma mark - 添加 監控 // 給player 添加 time observer - (void)addPlayerObserver {__weak typeof(self)weakSelf = self;_timeObser = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {AVPlayerItem *playerItem = weakSelf.player.currentItem;float current = CMTimeGetSeconds(time);float total = CMTimeGetSeconds([playerItem duration]);NSLog(@"當前播放進度 %.2f/%.2f.",current,total);}]; } // 移除 time observer - (void)removePlayerObserver {[_player removeTimeObserver:_timeObser]; }- (void)addObserverWithPlayItem:(AVPlayerItem *)item {[item addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];[item addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];[item addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];[item addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil]; } // 移除 item 的 observer - (void)removeObserverWithPlayItem:(AVPlayerItem *)item {[item removeObserver:self forKeyPath:@"status"];[item removeObserver:self forKeyPath:@"loadedTimeRanges"];[item removeObserver:self forKeyPath:@"playbackBufferEmpty"];[item removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"]; }- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {AVPlayerItem *item = object;if ([keyPath isEqualToString:@"status"]) {// 播放狀態[self handleStatusWithPlayerItem:item];} }- (void)handleStatusWithPlayerItem:(AVPlayerItem *)item {AVPlayerItemStatus status = item.status;switch (status) {case AVPlayerItemStatusReadyToPlay: // 準備好播放NSLog(@"AVPlayerItemStatusReadyToPlay");break;case AVPlayerItemStatusFailed: // 播放出錯NSLog(@"AVPlayerItemStatusFailed");break;case AVPlayerItemStatusUnknown: // 狀態未知NSLog(@"AVPlayerItemStatusUnknown");break;default:break;}}- (void)addNotificatonForPlayer {NSNotificationCenter *center = [NSNotificationCenter defaultCenter];[center addObserver:self selector:@selector(videoPlayEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];[center addObserver:self selector:@selector(videoPlayError:) name:AVPlayerItemPlaybackStalledNotification object:nil];[center addObserver:self selector:@selector(videoPlayEnterBack:) name:UIApplicationDidEnterBackgroundNotification object:nil];[center addObserver:self selector:@selector(videoPlayBecomeActive:) name:UIApplicationDidBecomeActiveNotification object:nil]; } // 移除 通知 - (void)removeNotification {NSNotificationCenter *center = [NSNotificationCenter defaultCenter];[center removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil]; // [center removeObserver:self name:AVPlayerItemTimeJumpedNotification object:nil];[center removeObserver:self name:AVPlayerItemPlaybackStalledNotification object:nil];[center removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];[center removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];[center removeObserver:self]; }// 視頻播放結束 - (void)videoPlayEnd:(NSNotification *)notification {NSLog(@"視頻播放結束"); // self.currentItem = [notic object]; // [self.currentItem seekToTime:kCMTimeZero completionHandler:nil]; // [self.player play];[self.player seekToTime:kCMTimeZero]; // [self.player play]; }// 視頻異常中斷 - (void)videoPlayError:(NSNotification *)notification {NSLog(@"視頻異常中斷"); } // 進入后臺 - (void)videoPlayEnterBack:(NSNotification *)notification {NSLog(@"進入后臺"); } // 返回前臺 - (void)videoPlayBecomeActive:(NSNotification *)notification {NSLog(@"返回前臺"); }#pragma mark - 銷毀 release - (void)dealloc {NSLog(@"--- %@ --- 銷毀了",[self class]);[self removeNotification];[self removePlayerObserver];[self removeObserverWithPlayItem:self.player.currentItem];}

    四、demo

    文章只是記錄一下相關部分代碼,會在慢慢進行改善。
    demo會持續更新,完善功能 ,一次比一次好。

    總結

    以上是生活随笔為你收集整理的iOS开发——AVPlayer自定义播放器(持续更新,学习中)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。