《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阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容