使用 AVPlayer 進(jìn)行多視頻播放

使用 AVPlayer 進(jìn)行多視頻播放

鏈接:http://ios.jobbole.com/84287/

從前……不赎离。我在 nKey 從事一個為 Hyundai(現(xiàn)代) 進(jìn)行的項(xiàng)目,當(dāng)進(jìn)入某個場景時罐寨,兩個視頻需要同時播放陪每,以便用戶能夠看到當(dāng)汽車是否存在某種特性(比如電子穩(wěn)定控制系統(tǒng))時會如何運(yùn)作。作為一名經(jīng)驗(yàn)豐富的開發(fā)者,我立即告訴客戶我們應(yīng)該合并兩個視頻放妈,以便能在 iOS 上“同時播放”湾盒。我向他解釋了湿右,為了在 iOS 上播放視頻,蘋果已經(jīng)在很久之前發(fā)布了 MediaPlayer.framework罚勾,根據(jù)相關(guān)文檔(并且實(shí)際上 :P )在同一時間不能夠播放一個以上的視頻(盡管你可以有兩個 MPMoviePlayerController 實(shí)例)毅人。

他說好的,然后我們就像之前那樣做了播放功能尖殃。但是丈莺,需求發(fā)生了改變,并且我們不得不添加一個在循環(huán)播放的背景視頻…… 結(jié)果我在如何協(xié)調(diào)多個視頻時遇到了問題送丰,我要同時只播放一個視頻并且不引起用戶的注意缔俄。

幸運(yùn)的是在這個星期一,nKey 把派我到在巴西圣保羅瓜魯柳斯機(jī)場舉行的 iOS Tech Talk器躏,我加入了 Media Technologies Evangelist 討論中俐载, Eryk Vershen 正在討論 AVFoundation.framework 以及 MediaPlayer.framework (又稱 MPMoviePlayerController )是如何用它來播放視頻的。在伴隨著紅酒和奶酪的交談后登失,我開始和 Eryk 講述我的問題并向他闡述準(zhǔn)備如何解決這個問題遏佣。他的回答類似于“當(dāng)然!大膽去干揽浙! iOS 肯定能在同一時間播放多個視的状婶!……呃……大約 4 個是極限∧笃迹”這個回答讓我很高興并感到好奇太抓,所以我又問他,既然不受框架限制令杈,為什么 MediaPlayer.framework 不能同時播放多個視頻……他告訴我 MPMoviePlayerController 之前是被用來在游戲中做場景切換的走敌。。逗噩。這就是為什么之前的 iOS 版本只能全屏播放掉丽,該局限是個歷史遺留問題。

當(dāng)我回到我的筆記本前异雁,我用 AVFoundation.framework 寫了一個非炒氛希基礎(chǔ)的視頻播放版本。顯然纲刀,當(dāng)我回到公司后项炼,我需要寫一個更加詳細(xì)的版本才能用在項(xiàng)目中。

好了,故事講完了锭部。讓我們回到工作中來暂论!

AVFoundation 框架提供了 AVPlayer 對象來實(shí)現(xiàn)單視頻或多視頻播放的控制器和用戶接口。由 AVPlayer 對象生成的可視結(jié)果可以顯示在 AVPlayerLayer 類的 CoreAnimation 層上拌禾。 在 AVFoundation 中取胎,AVAsset 對象用來表示定時影音媒體,比如視頻和音頻湃窍。根據(jù)相關(guān)文檔闻蛀,每個資源包含一個用來一起呈現(xiàn)或處理的軌道集合,每個統(tǒng)一媒體類型您市,包括不僅限于音頻觉痛、視頻、文本墨坚、隱藏式字幕秧饮、字幕映挂。因?yàn)槎〞r影音媒體的性質(zhì)泽篮,在成功初始化一個資源后,某些或全部鍵值可能不會立即可用柑船。為了避免阻塞主線程帽撑,你可以在特定鍵注冊你感興趣的內(nèi)容,以在可用時被通知到鞍时。

考慮到這一點(diǎn)亏拉,繼承 UIViewController 并命名為 VideoPlayerViewController。就像 MPMoviePlayerController 逆巍,讓我們添加一個 NSURL 屬性及塘,用于告訴我們應(yīng)該從哪里抓取視頻。就像上面描述的那樣锐极,添加下面的代碼笙僚,一旦 URL 被賦值 AVAsset 就會被加載。

#pragma mark - Public methods

- (void)setURL:(NSURL*)URL {

[_URL release];

_URL = [URL copy];

AVURLAsset *asset = [AVURLAsset URLAssetWithURL:_URL options:nil];

NSArray *requestedKeys = [NSArray arrayWithObjects:kTracksKey,

kPlayableKey, nil];

[asset loadValuesAsynchronouslyForKeys:requestedKeys

completionHandler: ^{ dispatch_async(

dispatch_get_main_queue(), ^{

[self prepareToPlayAsset:asset

withKeys:requestedKeys];

});

}];

}

- (NSURL*)URL {

return _URL;

}

所以灵再,一旦視頻的 URL 被賦值后肋层,創(chuàng)建一個資源來檢查被指定的URL引用的源并且異步的加載這個資源的 “tracks” 和 “playable” 鍵值。等加載結(jié)束后翎迁,我們就可以在主線程操作 AVPlayer(當(dāng)播放狀態(tài)動態(tài)改變時栋猖,主線程可以確保安全的獲取播放器的非原子屬性)。

#pragma mark - Private methods

- (void)prepareToPlayAsset: (AVURLAsset *)asset withKeys:

(NSArray *)requestedKeys {

for (NSString *thisKey in requestedKeys) {

NSError *error = nil;

AVKeyValueStatus keyStatus = [asset

statusOfValueForKey:thisKey

error:&error];

if (keyStatus == AVKeyValueStatusFailed) {

return;

}

}

if (!asset.playable) {

return;

}

if (self.playerItem) {

[self.playerItem removeObserver:self forKeyPath:kStatusKey];

[[NSNotificationCenter defaultCenter] removeObserver:self

name:AVPlayerItemDidPlayToEndTimeNotification

object:self.playerItem];

}

self.playerItem = [AVPlayerItem playerItemWithAsset:asset];

[self.playerItem addObserver:self forKeyPath:kStatusKey

options:NSKeyValueObservingOptionInitial |

NSKeyValueObservingOptionNew

context:

AVPlayerDemoPlaybackViewControllerStatusObservationContext];

if (![self player]) {

[self setPlayer:[AVPlayer playerWithPlayerItem:self.playerItem]];

[self.player addObserver:self forKeyPath:kCurrentItemKey

options:NSKeyValueObservingOptionInitial |

NSKeyValueObservingOptionNew

context:

AVPlayerDemoPlaybackViewControllerCurrentItemObservationContext];

}

if (self.player.currentItem != self.playerItem) {

[[self player] replaceCurrentItemWithPlayerItem:self.playerItem];

}

}

在資源所有需要的鍵值加載完成后汪榔,我們檢查是否加載成功以及該資源是否可以播放蒲拉。如果這樣,我們初始化一個 AVPlayerItem (用來表示能被 AVPlayer 對象播放的資源的表示狀態(tài))和一個 AVPlayer 來播放的資源。請注意雌团,我在這一點(diǎn)上沒有添加任何錯誤處理爆班。在這里,我們應(yīng)該創(chuàng)建一個委托并讓視圖控制器或正在使用你的播放器的用戶辱姨,決定如何以最好的方式來處理可能出現(xiàn)的錯誤柿菩。

我們也添加了一些鍵值監(jiān)聽以便于當(dāng)我們的視圖被綁定到播放器時和 AVPlayerItem 準(zhǔn)備好播放時收到通知。

#pragma mark - Key Valye Observing

- (void)observeValueForKeyPath: (NSString*) path

? ? ? ? ? ? ? ? ? ? ?ofObject: (id)object

? ? ? ? ? ? ? ? ? ? ? ?change: (NSDictionary*)change

? ? ? ? ? ? ? ? ? ? ? context: (void*)context {

? ?if (context == AVPlayerDemoPlaybackViewControllerStatusObservation

? ? ? ? ? ? Context) {

? ? ? ? ? ? ?AVPlayerStatus status = [[change objectForKey:

? ? ? ? ? ? ? ?NSKeyValueChangeNewKey] integerValue];

? ? ? ? ? ? ?if (status == AVPlayerStatusReadyToPlay) {

? ? ? ? ? ? ? ? ? [self.player play];

? ? ? ? ? ? ?}

? ?} else if (context == AVPlayerDemoPlaybackViewControllerCurrentItem

? ? ? ? ? ? ObservationContext) {

? ? ? ? ? ? ?AVPlayerItem *newPlayerItem = [change objectForKey:

? ? ? ? ? ? ? ? NSKeyValueChangeNewKey];

if (newPlayerItem) {

[self.playerView setPlayer:self.player];

[self.playerView setVideoFillMode:

AVLayerVideoGravityResizeAspect];

}

} else {

[super observeValueForKeyPath:path ofObject: object

change:change context:context];

}

}

一旦 AVPlayerItem 設(shè)置好后雨涛,我們可以自由的將 AVPlayer 添加到用來展示可視輸出的播放器層枢舶。我們也會確保保留視頻的長寬比和適合視頻圖層的邊界內(nèi)。

一旦 AVPlayer 準(zhǔn)備好了替久,就讓它開始播放凉泄!讓 iOS 來完成艱巨的任務(wù) :)

正如我前面所說,為了播放資源可視組件蚯根,您需要一個包含 AVPlayerLayer 的視圖后众,來指揮 AVPlayer 對象的輸出。下面演示了如何子類化 UIView 來滿足要求︰

@implementation VideoPlayerView

+ (Class)layerClass {

return [AVPlayerLayer class];

}

- (AVPlayer*)player {

return [(AVPlayerLayer*)[self layer] player];

}

- (void)setPlayer: (AVPlayer*)player {

[(AVPlayerLayer*)[self layer] setPlayer:player];

}

- (void)setVideoFillMode: (NSString *)fillMode {

AVPlayerLayer *playerLayer = (AVPlayerLayer*)[self layer];

playerLayer.videoGravity = fillMode;

}

@end

到這里就結(jié)束了颅拦!

當(dāng)然蒂誉,我沒有貼上所有用來編譯和運(yùn)行的項(xiàng)目代碼,但我不會讓你失望 距帅!轉(zhuǎn)到 GitHub 并下載完整的源代碼 右锨!

?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市碌秸,隨后出現(xiàn)的幾起案子绍移,更是在濱河造成了極大的恐慌,老刑警劉巖讥电,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蹂窖,死亡現(xiàn)場離奇詭異,居然都是意外死亡恩敌,警方通過查閱死者的電腦和手機(jī)瞬测,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來潮剪,“玉大人涣楷,你說我怎么就攤上這事】古觯” “怎么了狮斗?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長弧蝇。 經(jīng)常有香客問我碳褒,道長折砸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任沙峻,我火速辦了婚禮睦授,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘摔寨。我一直安慰自己去枷,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布是复。 她就那樣靜靜地躺著删顶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪淑廊。 梳的紋絲不亂的頭發(fā)上逗余,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天,我揣著相機(jī)與錄音季惩,去河邊找鬼录粱。 笑死,一個胖子當(dāng)著我的面吹牛画拾,可吹牛的內(nèi)容都是我干的啥繁。 我是一名探鬼主播,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼碾阁,長吁一口氣:“原來是場噩夢啊……” “哼输虱!你這毒婦竟也來了些楣?” 一聲冷哼從身側(cè)響起脂凶,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎愁茁,沒想到半個月后蚕钦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鹅很,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年嘶居,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片促煮。...
    茶點(diǎn)故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡邮屁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出菠齿,到底是詐尸還是另有隱情佑吝,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布绳匀,位于F島的核電站芋忿,受9級特大地震影響炸客,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜戈钢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一痹仙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧殉了,春花似錦开仰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至痕囱,卻和暖如春田轧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鞍恢。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工傻粘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人帮掉。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓弦悉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蟆炊。 傳聞我的和親對象是個殘疾皇子稽莉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評論 2 348

推薦閱讀更多精彩內(nèi)容