《AVFoundation》官方文檔翻譯04 --Playback

Playback

要控制assets的回放,您可以使用AVPlayer對象。在播放過程中,您可以使用AVPlayerItem實例來管理整個asset的呈現狀態,以AVPlayerItemTrack管理單個曲目的呈現狀態的對象。要顯示視頻,請使用AVPlayerLayer對象。

Playing Assets

播放器是用于管理asset的播放的控制器對象,例如啟動和停止播放,以及尋找特定時間。您使用一個實例AVPlayer來播放單個asset。您可以使用AVQueuePlayer對象按順序播放多個項目(AVQueuePlayer是一個子類AVPlayer)。在OS X上,您可以選擇使用AVKit框架的AVPlayerView類在視圖中播放內容。

播放器為您提供有關播放狀態的信息,因此,如果需要,您可以將用戶界面與播放器的狀態同步。您通常將播放器的輸出指向專門的核心動畫層(AVPlayerLayer或一個或多個實例AVSynchronizedLayer)。要了解有關圖層的更多信息,請參閱Core Animation Programming Guide

多個播放器層: 您可以AVPlayerLayer從單個AVPlayer實例創建許多對象,但只有最近創建的這樣的圖層才能在屏幕上顯示任何視頻內容。

盡管您最終想要創建asset,但是您不會直接向AVPlayer對象提供asset。而是提供一個實例AVPlayerItem。播放器物品管理與之相關聯的asset的呈現狀態。播放器項目包含播放器項目軌跡AVPlayerItemTrack- 與asset中的軌道對應的實例。各種對象之間的系如圖2-1所示。

圖2-1 播放asset

這種抽象意味著您可以同時使用不同的播放器播放一個給定的asset,但是每個播放器都以不同的方式呈現。圖2-2顯示了一種可能性,兩個不同的播放器播放同樣的asset,具有不同的設置。使用項目軌道,例如,您可以在播放期間禁用特定的軌道(例如,您可能不想播放聲音組件)。


圖2-2 以不同的方式播放相同的asset

您可以使用現有asset初始化player item,也可以直接從網址初始化player item,以便您可以在特定位置播放asset(AVPlayerItem然后為資源創建和配置資源)。至于AVAsset,不過,簡單地初始化播放器的項目并不一定意味著它已經準備好立即播放。您可以觀察(使用鍵值觀察)項目的status屬性來確定是否和何時可以播放。

Handling Different Types of Asset(處理不同類型的Asset)

配置資源以進行播放的方式可能取決于您要播放的asset類型。一般來說,有兩種主要類型:基于文件的asset,您可以隨機訪問(例如從本地文件,相機卷或媒體庫)以及基于流的asset(HTTP Live Streaming格式)。

加載和播放基于文件的asset。播放基于文件的asset有幾個步驟:

  • 創建assetAVURLAsset

  • 創建一個AVPlayerItem使用asset的實例。

  • 將項目與實例關聯AVPlayer。

  • 等待項目的status屬性指示它已準備好播放(通常,
    當狀態更改時,通常使用鍵值觀察接收通知)。

將它放在一起說明這種方法:使用AVPlayerLayer播放視頻文件

創建和準備一個HTTP直播流進行播放。初始化AVPlayerItem使用該URL 的實例。(您不能直接創建一個AVAsset實例來表示HTTP Live Stream中的媒體。)

NSURL * url = [NSURL URLWithString:@“<#Live Stream URL#>];
//您可以在<http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8>找到一個測試流。
self.playerItem = [AVPlayerItem playerItemWithURL:url];
[playerItem addObserver:self forKeyPath:@“status”options:0 context:&ItemStatusContext];
self.player = [AVPlayer playerWithPlayerItem:playerItem];

當您將播放器項目與播放器關聯時,它就開始播放了。當它準備播放時,player項目創建AVAsset和AVAssetTrack實例,您可以使用它們來檢查直播流的內容。要獲取流媒體項目的持續時間,您可以觀察duration播放器項目的屬性。當項目準備播放時,此屬性將更新為流的正確值。

注意: 使用duration播放器項目上的屬性需要iOS 4.3或更高版本。與所有iOS版本兼容的方法涉及觀察status播放器項目的屬性。當狀態變成時AVPlayerItemStatusReadyToPlay,可以用以下代碼行來獲取持續時間:
[[[[playerItem tracks] objectAtIndex:0] assetTrack] asset] duration];

如果您只想播放直播,您可以使用快捷方式直接使用URL使用以下代碼:

self.player = [AVPlayer playerWithURL:<#Live stream URL#>];
[player addObserver:self forKeyPath:@“status”options:0 context:&PlayerStatusContext];

與assets和items一樣,初始化播放器并不意味著它可以播放。您應該觀察player的statusassets,AVPlayerStatusReadyToPlay當player準備播放時,該財產會發生變化。您還可以觀察該currentItem屬性以訪問為流創建的播放器項目。

如果您不知道有什么樣的網址,請按照下列步驟操作:

1:嘗試AVURLAsset使用URL 初始化一個URL,然后加載它的tracks密鑰。如果軌道成功加載,那么您將為該asset創建一個player item。

2:如果1失敗,請AVPlayerItem直接從URL 創建。觀察播放器statusasset以確定player是否可播放。

如果任一路線成功,您最終可以使用player item,然后您可以與播放器相關聯。

Playing an Item

要開始播放,您向play播放器發送消息。

- (IBAction)play:sender {
[player play];
}

除了簡單的播放之外,您還可以管理播放的各個方面,例如播放頭的速率和位置。您還可以監視播放器的播放狀態;
如果您想要將用戶界面同步到asset的呈現狀態,則此功能非常有用 - 請參閱Monitoring Playback

Changing the Playback Rate

您可以通過設置播放器的rate屬性來更改播放速度。

aPlayer.rate = 0.5;
aPlayer.rate = 2.0;

值為1.0表示“以當前項目的自然比例播放”。將速率設置為0.0與暫停播放相同 - 您也可以使用pause。支持反向播放的項目可以使用帶負號的速率屬性設置反向播放速率。您可以使用playerItem屬性canPlayReverse(支持速率值為-1.0)canPlaySlowReverse(支持0.0到-1.0之間的速率)和canPlayFastReverse(支持小于-1.0的速率值)來確定支持的反向播放類型。

Seeking—Repositioning the Playhead(尋求重新定位播放頭)

要將播放頭移動到特定的時間,您通常使用seekToTime:如下:

CMTime fiveSecondsIn = CMTimeMake(5,1);
[player seekToTime:fiveSecondsIn];

seekToTime:然而,該方法針對性能而不是精度進行調整。
如果您需要精確地移動播放頭,而是使用seekToTime:toleranceBefore:toleranceAfter:如下代碼片段:

CMTime fiveSecondsIn = CMTimeMake(5,1);
[player seekToTime:fiveSecondsIn toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];

使用公差為零可能需要框架來解碼大量的數據。例如,如果您正在編寫需要精確控制的復雜的媒體編輯應用程序,則應該使用零。

播放后,播放器的頭部設置為項目的末尾,進一步的調用play無效。要將播放頭放在項目的開始位置,您可以注冊以AVPlayerItemDidPlayToEndTimeNotification從該項目接收通知。在通知的回調方法中,您seekToTime:使用參數調用kCMTimeZero。

// Register with the notification center after creating the player item.

[[NSNotificationCenter defaultCenter]

addObserver:self   selector:@selector(playerItemDidReachEnd:)

name:AVPlayerItemDidPlayToEndTimeNotification

object:<#The player item#>];

- (void)playerItemDidReachEnd:(NSNotification *)notification {

[player seekToTime:kCMTimeZero];

}

Playing Multiple Items(播放多個項目)

您可以使用AVQueuePlayer對象依次播放多個項目。該AVQueuePlayer類是的子類AVPlayer。您可以使用播放器項目數組初始化隊列播放器。

NSArray * items = <#An player物品數組#>;
AVQueuePlayer * queuePlayer = [[AVQueuePlayer alloc] initWithItems:items];

然后,您可以play像使用AVPlayer對象一樣播放隊列。隊列播放器依次播放每個項目。如果你想跳到下一個項目,你發送隊列播放器一個advanceToNextItem消息。

您可以修改使用隊列insertItem:afterItem:removeItem:removeAllItems。添加新項目時,通常應該使用它來檢查是否可以插入到隊列中canInsertItem:afterItem:。你的nil作為第二個參數傳遞,以測試新項目是否可以追加到隊列中。

AVPlayerItem *anItem = <#Get a player item#>;
if ([queuePlayer canInsertItem:anItem afterItem:nil]) {
[queuePlayer insertItem:anItem afterItem:nil];
}

Monitoring Playback(監控播放)

您可以監視播放器的演示狀態和正在播放的播放器項目的多個方面。這對于不直接控制的狀態更改特別有用。例如:

  • 如果用戶使用多任務切換到其他應用程序,則播放器的rate屬性將會丟棄0.0。
  • 如果您正在播放遠程媒體,播放器項目loadedTimeRangesseekableTimeRanges
    屬性將隨著更多數據可用而改變。
  • 播放器的currentItem屬性隨著播放器項目被創建為HTTP直播流而改變。這些屬性告訴你播放器項目的時間軸的哪些部分可用。
  • 播放器項目的tracks屬性可能會在播放HTTP直播流時更改。
    如果流為內容提供不同的編碼,則可能會發生這種情況; 如果播放器切換到不同的編碼,曲目會改變。
  • status由于某種原因播放失敗,播放器或播放器項目的屬性可能會更改。

您可以使用鍵值觀察來監視這些屬性的值的更改。

重要提示: 您應該注冊KVO更改通知,并從主線程上的KVO更改通知中注銷。這避免了在另一個線程上進行更改時接收到部分通知的可能性。AV Foundation invokes
[observeValueForKeyPath:ofObject:change:context:](https://developer.apple.com/documentation/objectivec/nsobject/1416553-observevalueforkeypath)
在主線程上調用,即使在另一個線程上進行了更改操作。

Responding to a Change in Status(響應狀態變化)

當播放器或播放器item的狀態發生變化時,會發出鍵值觀察更改通知。如果由于某種原因對象無法播放(例如,如果媒體服務被重置),狀態將更改為AVPlayerStatusFailedAVPlayerItemStatusFailed適當。在這種情下對象error屬性的值將更改為描述為什么對象不再能播放的錯誤對象。

AV Foundation沒有指定發送通知的線程。如果要更新用戶界面,則必須確保在主線程上調用相關代碼。此示例用于dispatch_async在主線程上執行代碼。

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object

change:(NSDictionary *)change context:(void *)context {

if (context == <#Player status context#>) {

AVPlayer *thePlayer = (AVPlayer *)object;

if ([thePlayer status] == AVPlayerStatusFailed) {

NSError *error = [<#The AVPlayer object#> error];

// Respond to error: for example, display an alert sheet.

return;

}

// Deal with other status change if appropriate.

}

// Deal with other change notifications if appropriate.

[super observeValueForKeyPath:keyPath ofObject:object

change:change context:context];

return;

}

Tracking Readiness for Visual Display(跟蹤準備視覺顯示)

你可以觀察到一個AVPlayerLayer對象的readyForDisplay屬性時,該層具有用戶可見的內容通知。特別地,您可以將播放器層插入到圖層樹中,只有當用戶可以查看某些內容,然后執行轉換。

Tracking Time(跟蹤時間)

要跟蹤AVPlayer對象中播放頭位置的變化,可以使用addPeriodicTimeObserverForInterval:queue:usingBlock:
addBoundaryTimeObserverForTimes:queue:usingBlock:。您可以這樣做,例如,使用有關經過時間或剩余時間的信息更新用戶界面,或執行其他用戶界面同步。

這兩種方法都返回一個不透明對象,作為觀察者。只要您希望播放器調用時間觀察塊,您必須對返回的對象保持強烈的引用。您還必須將這些方法的每個調用與相應的調用進行平衡removeTimeObserver:

使用這兩種方法,AV Foundation不保證在每個間隔或邊界通過時調用您的塊。如果以前調用的塊的執行尚未完成,則AV Foundation不會調用塊。因此,您必須確保在塊中執行的工作不會對系統過度征稅。

// Assume a property: @property (strong) id playerObserver;

Float64 durationSeconds = CMTimeGetSeconds([<#An asset#> duration]);

CMTime firstThird = 
CMTimeMakeWithSeconds(durationSeconds/3.0, 1);

CMTime secondThird = 
CMTimeMakeWithSeconds(durationSeconds*2.0/3.0, 1);

NSArray *times = @[[NSValue valueWithCMTime:firstThird], [NSValue valueWithCMTime:secondThird]];

self.playerObserver = [<#A player#> addBoundaryTimeObserverForTimes:times queue:NULL usingBlock:^{

NSString *timeDescription = (NSString *)

CFBridgingRelease(CMTimeCopyDescription(NULL, [self.player currentTime]));

NSLog(@"Passed a boundary at %@", timeDescription);

}];

Reaching the End of an Item(到達項目結束)

當播放器項目完成播放時,您可以注冊接收AVPlayerItemDidPlayToEndTimeNotification 通知

[[NSNotificationCenter defaultCenter] addObserver:<#觀察者,通常是自己#>

selector:@selector(<#選擇器名稱>)

名稱:AVPlayerItemDidPlayToEndTimeNotification

對象:<#一個播放器項目#>];

Putting It All Together: Playing a Video File Using AVPlayerLayer(放在一起:使用AVPlayerLayer播放視頻文件)

這個簡短的代碼示例說明了如何使用AVPlayer對象來播放視頻文件。它顯示如何:

  • 配置視圖以使用AVPlayerLayer圖層
  • 創建一個AVPlayer對象
  • 創建一個AVPlayerItem基于文件的asset的對象,并使用鍵值觀察觀察其狀態
  • 通過啟用按鈕來響應已準備好播放的項目
  • 播放該項目,然后將播放器的頭恢復到開頭
注意: 為了專注于最相關的代碼,本示例省略了一個完整應用程序的幾個方面,例如作為觀察者的內存管理和注銷(用于鍵值觀察或通知中心)。要使用AV基金會,您希望有足夠的經驗與可可可以推斷出缺少的部分。

有關播放的概念介紹,請跳到播放資源

The Player View(播放器視圖)

要播放資源的可視化組件,您需要一個視圖,其中包含可以引導對象AVPlayerLayer的輸出的層AVPlayer。您可以創建一個簡單的子類UIView來適應這一點:

#import <UIKit/UIKit.h>

#import <AVFoundation/AVFoundation.h>

@interface PlayerView : UIView

@property (nonatomic) AVPlayer *player;

@end

@implementation PlayerView

+ (Class)layerClass {

return [AVPlayerLayer class];

}

- (AVPlayer*)player {

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

}

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

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

}

@end

A Simple View Controller(簡單的視圖控制器)

假設你有一個簡單的視圖控制器,聲明如下:
@class PlayerView;
@interface PlayerViewController : UIViewController

@property (nonatomic) AVPlayer *player;
@property (nonatomic) AVPlayerItem *playerItem;
@property (nonatomic, weak) IBOutlet PlayerView 
*playerView;
@property (nonatomic, weak) IBOutlet UIButton 
*playButton;
- (IBAction)loadAssetFromFile:sender;
- (IBAction)play:sender;
- (void)syncUI;
@end

該syncUI方法將按鈕的狀態與播放器的狀態同步:

- (void)syncUI {
    if ((self.player.currentItem != nil) &&
    ([self.player.currentItem status] == 
AVPlayerItemStatusReadyToPlay)) {
    self.playButton.enabled = YES;
    }
    else {
    self.playButton.enabled = NO;
}
}

您可以syncUI在視圖控制器的viewDidLoad方法中調用,以便在首次顯示視圖時確保一致的用戶界面。

- (void)viewDidLoad {

[super viewDidLoad];

[self syncUI];

}

其余的屬性和方法將在其余部分中進行描述。

Creating the Asset(創建Asset)

您可以使用URL從URL創建AssetAVURLAsset。(以下示例假定您的項目包含合適的視頻Asset)。

- (IBAction)loadAssetFromFile:sender {

NSURL *fileURL = [[NSBundle mainBundle]
    URLForResource:<#@"VideoFileName"#> withExtension:<#@"extension"#>];

AVURLAsset *asset = [AVURLAsset URLAssetWithURL:fileURL options:nil];
NSString *tracksKey = @"tracks";

[asset loadValuesAsynchronouslyForKeys:@[tracksKey] completionHandler:
 ^{
     // The completion block goes here.
 }];
}

在完成塊中,您創建一個AVPlayerItemasset的實例,并將其設置為播放器視圖的播放器。與創建asset一樣,只需創建播放器項目并不意味著它可以使用。要確定何時可以播放,您可以觀察該項目的status屬性。將播放器項目實例與播放器本身相關聯之前,應配置此觀察。

當您將播放器與播放器相關聯時,您可以觸發播放器項目的準備。

//為鍵值觀察上下文定義此常量。

static const NSString * ItemStatusContext;

//完成處理程序塊。

dispatch_async(dispatch_get_main_queue(),

^ {

NSError *錯誤;

AVKeyValueStatus status = [asset statusOfValueForKey:tracksKey error:&error];

if(status == AVKeyValueStatusLoaded){

self.playerItem = [AVPlayerItem playerItemWithAsset:asset];

//確保在playerItem與播放器相關聯之前完成此操作

[self.playerItem addObserver:self forKeyPath:@“status”

選項:NSKeyValueObservingOptionInitial上下文:&ItemStatusContext];

[[NSNotificationCenter defaultCenter] addObserver:self

選擇:@選擇(playerItemDidReachEnd :)

名稱:AVPlayerItemDidPlayToEndTimeNotification

對象:self.playerItem];

self.player = [AVPlayer playerWithPlayerItem:
self.playerItem];

[self.playerView setPlayer:self.player];

}

else {

//應該適當地處理錯誤。

NSLog(@“asset的軌道未加載:\ n%@”,[error localizedDescription]);

}

});

Responding to the Player Item’s Status Change(響應播放器項目的狀態更改)

當播放器項目的狀態改變時,視圖控制器接收鍵值觀察改變通知。
AV Foundation沒有指定發送通知的線程。如果要更新用戶界面,則必須確保在主線程上調用相關代碼。此示例用于dispatch_async對主線程上的消息進行排隊以同步用戶界面。

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object

change:(NSDictionary *)change context:(void *)context {

if (context == &ItemStatusContext) {

dispatch_async(dispatch_get_main_queue(),

^{

[self syncUI];

});

return;

}

[super observeValueForKeyPath:keyPath ofObject:object

change:change context:context];

return;

}

Playing the Item

播放該項目涉及向play播放器發送消息。

- (IBAction)play:sender {
    [player play];
}

該項目僅播放一次。播放后,播放器的頭部設置為項目的末尾,并且play方法的進一步調用將不起作用。要將播放頭放在項目的開始位置,您可以注冊以AVPlayerItemDidPlayToEndTimeNotification從該項目接收。在通知的回調方法中,seekToTime:使用參數進行調用kCMTimeZero

// Register with the notification center after creating the player item.
[[NSNotificationCenter defaultCenter]
    addObserver:self
    selector:@selector(playerItemDidReachEnd:)
    name:AVPlayerItemDidPlayToEndTimeNotification
    object:[self.player currentItem]];

- (void)playerItemDidReachEnd:(NSNotification *)notification {
[self.player seekToTime:kCMTimeZero];
}

下一頁
上一頁

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內容