編輯:關於Android編程
#import因為AVPlayer屬於AVFoundation框架,所以要引入這個頭文件。其次,當然是要創建我們的視頻播放器AVPlayer了,這裡在.h文件中聲明了一個全局的Player對象,便於在不同的函數中進行相關操作。
@property (nonatomic,strong) AVPlayer * player;然後,在初始化方法中對其進行初始化。在初始化過程中,需要傳入視頻的URL,這個URL是NSURL類型的,這裡簡單說明一下,AVPlayer支持本地視頻播放和媒體視頻播放,因此,這個URL既可以是本地視頻的URL,也可以是網絡視頻的URL,本篇博客選取了一段網絡視頻:
//網絡視頻 NSString * urlStr = [NSString stringWithFormat:@"http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"]; urlStr = [urlStr stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]; NSURL * url = [NSURL URLWithString:urlStr];首先用字符串傳進來一個地址,然後,為了對字符串進行UTF-8編碼,之前是用的其他的方法,但是,在iOS9之後,已經被啟用了,所以這裡用上面的方法,進行編碼,前後,用編碼之後的字符串初始化URL。其實,如果不考慮比較多的內容的話,直接用下面的方法就可以創建一個AVPlayer了:
self.player = [[AVPlayer alloc] initWithURL:url];這其實是最簡單的方法,但是一般不推薦使用,操作起來會很不方便。其實,自定制音頻播放,也是用AVPlayer自定制,到這裡的話,基本上就能夠實現音頻的播放了。但是,視頻播放器的話,還要有畫面,因此,還需要用這個Player去初始化一個圖層,然後將圖層加到當前的view的Layer上,這樣,就有畫面了,這裡也是創建了一個全局的AVPlayerLayer對象:
@property (nonatomic,strong) AVPlayerLayer * playerLayer;然後在.m中,接著上面的方法寫入下面的代碼:
self.player = [[AVPlayer alloc] initWithURL:url]; self.playerLayer.frame = self.layer.bounds; self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect; [self.layer addSublayer:self.playerLayer];我在創建的時候,是在一個View中創建的,所以直接是self.layer,如果是在Controller中,則是self.view.layer,然後,設置一下playerLayer的大小和方向。這樣,就創建了一個Player。當然,上面說過,這只是簡單的創建方式,通常情況下使用下面將要介紹的方式進行創建: 首先在.h中定義了一個AVPlayerItem對象:
@property (nonatomic,strong) AVPlayerItem * playerItem;
AVURLAsset * movieAsset = [[AVURLAsset alloc] initWithURL:URL options:nil]; self.playerItem = [AVPlayerItem playerItemWithAsset:movieAsset]; self.player = [AVPlayer playerWithPlayerItem:self.playerItem]; self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; self.playerLayer.frame = self.layer.bounds; self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect; [self.layer addSublayer:self.playerLayer];
/** * 監聽AVPlayerItem的屬性 */ [self.playerItem addObserver:self forKeyPath:@"status" options:0 context:NULL]; [self.playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:0 context:NULL]; self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
/** * KVO監聽playItem的屬性變化 * * @param keyPath keyPath description * @param object object description * @param change change description * @param context context description */ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void *)context{ AVPlayerItem * item = self.player.currentItem; if ([keyPath isEqualToString:@"status"]) { //正在播放 if (AVPlayerItemStatusReadyToPlay == item.status) { NSLog(@"正在播放...,視頻總長度:%.2f",CMTimeGetSeconds(item.duration)); } else if (AVPlayerItemStatusUnknown == item.status){ NSLog(@"視頻加載中"); } else if (AVPlayerStatusFailed == item.status){ NSLog(@"視頻獲取失敗"); NSLog(@"%@",item.error); } } else if([keyPath isEqualToString:@"loadedTimeRanges"]){ NSArray *array=item.loadedTimeRanges; CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];//本次緩沖時間范圍 float startSeconds = CMTimeGetSeconds(timeRange.start); float durationSeconds = CMTimeGetSeconds(timeRange.duration); NSTimeInterval totalBuffer = startSeconds + durationSeconds;//緩沖總長度 NSLog(@"共緩沖:%.2f",totalBuffer); } }
[self.player play];來播放當前的視頻,player有一個屬性叫做currentItem,這個屬性,就是當前player的item,也就是前面初始化過程中的那個item。監聽item的loadedTimeRanges屬性,可以獲得當前緩沖了多少視頻以及視頻的總長度。item的loadedTimeRanges其實是一個數組,裡面存放了CMTimeRange類型的結構體,通過獲得該array的firstObject可以獲得本次緩沖的時間信息timeRange,timeRange.start表示本次緩沖的開始位置,timeRange.duration表示本次緩沖的視頻長度,兩者相加,就獲得了緩沖的總時長,這就是好多播放器中,底部進度條中緩沖的視頻長度的獲取方式。由於loadedTimeRanges經常要變化,所以,會反復出發這個KVO的監聽,因此,可以做到隨時刷新緩沖進度。此外,當退出播放器頁面的時候,要移除相關的觀察者。
//移除觀察者 -(void)removeObserverFromPlayerItem:(AVPlayerItem *)playerItem{ [playerItem removeObserver:self forKeyPath:@"status"]; [playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"]; }然後在dealloc調用這個方法該方法即可。 此外,當視頻播放完成之後,還會有相關的通知,在這裡,可以對其進行監聽,當播放完成之後,進行相關的UI刷新:
/** * 添加播放器通知 */ -(void)addNotification{ //給AVPlayerItem添加播放完成通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem]; }例如,可以更改播放按鈕的圖片:
- (void)playbackFinished:(NSNotification *)notification{ [self.playButton setImage:[UIImage imageNamed:@"button_normal"] forState:UIControlStateNormal]; }既然監聽通知了,就要在dealloc中移除監聽:
[self removeNotification];上面只是創建了視頻播放器,下面將講解視頻播放器的控制。我們在進行視頻自定制的時候,還要實現視頻播放器的播放、暫停、繼續播放、停止功能,因此,還要進行一些其他的操作,我這裡將播放和繼續播放進行了區分,這裡說的播放,是從頭開始播放,繼續播放,則是從上次暫停的位置進行播放,因此要設置一個屬性,來保存當前播放的位置:
//當前播放進度 @property (nonatomic,assign) double currentTime;下面是從頭播放的方法:
/** * 開始播放 */ - (void)play{ AVPlayerItem * item = self.player.currentItem; // [item seekToTime:CMTimeMakeWithSeconds(0, 1.0)]; [item seekToTime:CMTimeMakeWithSeconds(0, 1.0) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]; self.progressBar.value = 0; [self.player play]; //設置播放速度 }在該方法中,用到了seekToTime方法,該方法用來從指定位置開始播放,傳入一個CMTime類型的時間值,來指定比方的位置。我的項目中,添加了一個進度條(UISlider),來表示當前播放的進度,因此,當從0來說播放的時候,在這裡將slider的value設置成0。這裡還注釋掉了一個seek方法,下面簡單說一下,第一個seek方法,seek的時間沒有第二個精確,但第二個更好性能,但還是推薦使用第二個。 下面是暫停的方法:
/** * 暫停播放 */ - (void)pause{ self.currentTime = [self playableCurrentTime]; [self.player pause]; //設置播放按鈕 [self.playButton setImage:[UIImage imageNamed:@"button_normal"] forState:UIControlStateNormal]; }Avplayer自帶一個pause方法,因此,這裡可以直接調用,還有上面的play方法也是自帶的。下面是resume方法:
/** * 繼續播放 */ - (void)resume{ AVPlayerItem * item = self.player.currentItem; // [item seekToTime:CMTimeMakeWithSeconds(self.currentTime, 1.0)]; [item seekToTime:CMTimeMakeWithSeconds(self.currentTime, 1.0) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]; [self.player play]; //設置播放速度 self.player.rate = self.rate; }上面說到了player的rate屬性,其實這個屬性的取值並不一定非要在0-1之間,當大於1的時候,會加速播放,小於1會減緩,這裡可以通過這個屬性來控制播放的速度。下面是停止方法:
/** * 停止 */ - (void)stop{ AVPlayerItem * item = self.player.currentItem; // [item seekToTime:CMTimeMakeWithSeconds(0, 1.0)]; [item seekToTime:CMTimeMakeWithSeconds(0, 1.0) toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]; [self.player pause]; self.currentTime = 0; }這裡使用seek方法,seek到0,然後調用player的pause方法,將視頻pause,就實現了視頻的stop功能。 此外,有些情況下,還會遇到這樣的需求,就是將視頻靜音,在AVFoundation中,已經為我們提供了這樣一個方法,就是將play的volume值設置為0。但是,這裡要仔細考慮一下了,當設置為0之後,要想設置回來,怎麼辦呢?因此,要將當前的volume保存起來。定義一個變量保存當前的音量。
//音量 @property (nonatomic,assign) float volumn;當點擊靜音按鈕的時候,調用下面的方法即可:
/** * 設置靜音 * * @param mute 靜音傳入的一個BOOL值,YES為靜音,NO不靜音 */ - (void)playerMute:(BOOL)mute{ if (mute) { [self.player setVolume:0]; } else { [self.player setVolume:self.volumn]; } }這裡有一個問題需要注意,這裡設置的音量,只是應用中的音量,並不是系統音量,也就是說,當系統音量為0的時候,及時這個volume再大,也是沒有聲音的,因此,當想要恢復音量的時候,這裡的volume一般都設置1,即正常的音量。 想獲取系統音量,請跳轉到這個博客,了解一下就好,也可以查詢一下其他的博客。 下面,再講一些其他的控制。 自定制播放器的過程中,當視頻播放進度發生改變的時候,我們也希望對應的進度條也跟著變化,因此,要監聽視頻播放的進度,可以使用下面的方法進行實現:
/** * 進度更新設置,監聽視頻播放進度,同時更新進度條的value */ - (void)addProgressBarObserver{ AVPlayerItem *playerItem=self.player.currentItem; __weak typeof(self) weakSelf = self; [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) { float current = CMTimeGetSeconds(time); float total = CMTimeGetSeconds([playerItem duration]); if (current) { [weakSelf.progressBar setValue:(current/total) animated:YES]; } }]; }這裡將進度條更新的操作封裝成了一個方法,其實還是調用了AVPlayer自帶的方法
- (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(nullable dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block;
這個方法的前一個參數還是CMTime類型的,後面是一個block,表示每個interval的時間,就回調一下這個block,這樣的話,我們就可以在這裡通過播放進度和總時長,來設置進度條的value值了。
通過以上的方法,基本上就能實現一個簡單的視頻播放器了,可能有些地方說的不好或者說法有誤,歡迎大家在下面進行評論,指出我的錯誤,大家共同進步。
一、Android 提供了三種方式: android語音識別方法一:使用intent調用語音識別程序 1. 說明 以下例程功能為:在應用程序中使用inte
Android 的菜單機制,在 Android 3.0 之前和之後有很大的去別,Android 3.0 推出 ActionBar ,導航的 UI 交互有很大的變化,但菜單
屬性動畫---res/animator屬性動畫故名思議就是通過動畫的方式改變對象的屬性了,我們首先需要了解幾個屬性:Duration動畫的持續時間,默認300ms。Tim
都說程序員不爽產品經理,其實有的時候遇到一些奇葩的後台開發人員也會很不順心。最近項目有這樣一個要求,要生成一個excel然後發郵件給客戶。結果後台人員直接把這個功能扔給客