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

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

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

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

文章目錄

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

一、 前言

邊學(xué)邊記錄AVPlayer封裝一個(gè)功能十分 全的自定義播放器,目前在學(xué)習(xí)階段,demo和文章會(huì)根據(jù)學(xué)習(xí)進(jìn)度與總結(jié)情況去更新,歡迎各位批評(píng)指正。
2020年8月1日更新3.2

二、相關(guān)知識(shí)點(diǎn)

  • AVPlayer本身并不顯示視頻!需要一個(gè)AVPlayerLayer播放層來(lái)顯示視頻,然后添加到父視圖的layer中。
  • AVPlayer只負(fù)責(zé)視頻管理和調(diào)控!而視頻資源是由AVPlayerItem提供的,每個(gè)AVPlayerItem對(duì)應(yīng)一個(gè)視頻地址。
  • 工程內(nèi)需要引入AVFoundation庫(kù)
  • 持續(xù)補(bǔ)充……

2.1 AVplayerItem

(instancetype)playerItemWithURL:(NSURL *)URL; 初始化視頻資源方法
duration 視頻總時(shí)長(zhǎng)
status 視頻資源的狀態(tài) (需要監(jiān)聽(tīng)的屬性)
loadedTimeRanges 在線(xiàn)視頻的緩沖進(jìn)度 (需要監(jiān)聽(tīng)的屬性)
playbackBufferEmpty 進(jìn)行跳轉(zhuǎn),無(wú)緩沖 (可選監(jiān)聽(tīng))
playbackLikelyToKeepUp 進(jìn)行跳轉(zhuǎn),有緩沖 (可選監(jiān)聽(tīng))

2.2 AVplayer

(void) play;
(void) pause; 播放暫停
(void) seekToTime:(CMTime)time;跳轉(zhuǎn)到指定時(shí)間
(CMTime) currentTime;獲取當(dāng)前播放進(jìn)度
(void)removeTimeObserver:(id)observer;移除監(jiān)聽(tīng) (銷(xiāo)毀播放器的時(shí)候調(diào)用)
(void)replaceCurrentItemWithPlayerItem:(nullable AVPlayerItem *)item;切換視頻資源

2.3 AVPlayerLayer

videoGravity 視頻的填充方式

  • AVQueuePlayer:這個(gè)是用于處理播放列表的操作,待學(xué)習(xí)…
  • 三、代碼部分

    3.1 單例

  • 聲明相關(guān)屬性
  • // AVAsset對(duì)象 @property (nonatomic, strong) AVAsset *videoAsset; // 播放器對(duì)象 @property (nonatomic, strong) AVPlayer *player; // 播放屬性 @property (nonatomic, strong) AVPlayerItem *playerItem; // 播放容器,用于放playerlayer @property (nonatomic, strong) UIView *videoView; // 用于播放的layer @property (nonatomic, strong) AVPlayerLayer *playLayer;
  • 實(shí)現(xiàn)播放器
  • - (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];// 添加觀(guān)察者[self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];// 獲取當(dāng)前視頻的尺寸[self getVideoSize:self.videoAsset];self.player = [AVPlayer playerWithPlayerItem:self.playerItem];// 監(jiān)聽(tīng)是否 播放完畢[[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; }
  • 調(diào)用
  • - (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];// 也可以將視頻播放完畢的監(jiān)聽(tīng)加在這里,需要將manager中的移除掉[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playBackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:[[SMZVideoManager sharedInstance] getCurrentPlayerItem]];[[SMZVideoManager sharedInstance] startVideo]; }
  • 觀(guān)察者監(jiān)聽(tīng)相關(guān)
  • #pragma mark - 觀(guān)察者相關(guān) - (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 {// 未知錯(cuò)誤AVPlayerStatusUnknown}} }- (void)playBackFinished:(NSNotification *)notification withBlock:(void (^)(void))code{// 播放完畢的操作,這里 提供重新播放的能力self.playerItem = [notification object];[self.playerItem seekToTime:kCMTimeZero completionHandler:nil];code(); }

    3.2 將播放器封裝成view

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

    // 相關(guān)方法 - (instancetype)initWithFrame:(CGRect)frame;- (void)setupPlayerWith:(NSURL *)videoURL;- (void)play;- (void)pause;- (void)replay;- (void)destory; // 相關(guān)實(shí)現(xiàn) - (instancetype)initWithFrame:(CGRect)frame{self = [super initWithFrame:frame];if (self){self.backgroundColor = [UIColor whiteColor];}return self; }// 準(zhǔn)備播放器 - (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; }// 創(chuàng)建播放器 - (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];} }// 創(chuàng)建視圖 - (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];} }// 重新開(kāi)始 - (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 - 添加 監(jiān)控 // 給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(@"當(dāng)前播放進(jìn)度 %.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"]) {// 播放狀態(tài)[self handleStatusWithPlayerItem:item];} }- (void)handleStatusWithPlayerItem:(AVPlayerItem *)item {AVPlayerItemStatus status = item.status;switch (status) {case AVPlayerItemStatusReadyToPlay: // 準(zhǔn)備好播放NSLog(@"AVPlayerItemStatusReadyToPlay");break;case AVPlayerItemStatusFailed: // 播放出錯(cuò)NSLog(@"AVPlayerItemStatusFailed");break;case AVPlayerItemStatusUnknown: // 狀態(tài)未知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]; }// 視頻播放結(jié)束 - (void)videoPlayEnd:(NSNotification *)notification {NSLog(@"視頻播放結(jié)束"); // 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(@"視頻異常中斷"); } // 進(jìn)入后臺(tái) - (void)videoPlayEnterBack:(NSNotification *)notification {NSLog(@"進(jìn)入后臺(tái)"); } // 返回前臺(tái) - (void)videoPlayBecomeActive:(NSNotification *)notification {NSLog(@"返回前臺(tái)"); }#pragma mark - 銷(xiāo)毀 release - (void)dealloc {NSLog(@"--- %@ --- 銷(xiāo)毀了",[self class]);[self removeNotification];[self removePlayerObserver];[self removeObserverWithPlayItem:self.player.currentItem];}

    四、demo

    文章只是記錄一下相關(guān)部分代碼,會(huì)在慢慢進(jìn)行改善。
    demo會(huì)持續(xù)更新,完善功能 ,一次比一次好。

    總結(jié)

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

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。