AVPlayerItemDidPlayToEndTimeNotification 仅在强制时有效
AVPlayerItemDidPlayToEndTimeNotification only works if forced
我到处搜索,仍然找不到解决这个问题的方法。我有一个 AVQueuePlayer
用于我正在制作的广播应用程序。一次大约有 20 AVPlayerItems
排队。我将 AVPlayerItemDidPlayToEndTimeNotification
观察者添加到每个项目中。通知被触发并执行代码以删除元数据和状态的关键观察者,因此它不会崩溃并前进到队列中的下一个项目。但是它不想玩。没有错误,状态为可以播放,AVPlayer
URL 完美加载。如果我单击按钮调用 advancenextitem,它可以完美运行并且也可以完美播放。
现在最奇怪的是:如果我 post 手动通知,通知代码将完美运行。我将不胜感激任何帮助或意见,因为我一直在尽一切努力使它正常工作。
if (playlist != nil) {
playlistInterval = [playlist count]/4.0;
NSMutableArray *playerItems = [[NSMutableArray alloc] initWithCapacity:playlistInterval];
for(int i = 0; i < playlistInterval; i++) {
NSString *tempString = [playlist objectAtIndex:i];
//NSLog(@"%@", tempString);
NSURL *temp = [[NSURL alloc] initWithString:tempString];
AVPlayerItem *itemTemp = [AVPlayerItem playerItemWithURL:temp];
[itemTemp addObserver:self forKeyPath:@"timedMetadata" options:NSKeyValueObservingOptionNew context:nil];
[itemTemp addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(playerItemDidReachEnd:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: itemTemp];
[playerItems addObject:itemTemp];
playlistCounter++;
}
qPlayer = [AVQueuePlayer queuePlayerWithItems:playerItems];
qPlayer.actionAtItemEnd = AVPlayerActionAtItemEndAdvance;
[ qPlayer addObserver:self
forKeyPath:@"rate"
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:rateDidChangeKVO];
[qPlayer addObserver:self forKeyPath:@"currentItem.duration"
options:0
context:durationDidChangeKVO];
return YES;
}
return NO;
}
- (IBAction)advancePlayer:(id)sender {
unsigned long size = [[qPlayer items] count];
NSLog(@"%tu",size);
if (size <= 0) {
[self initializePlayerWithItems:currentKey];
[qPlayer play];
} else {
//NSLog(@"%@",[qPlayer currentItem]);
[[qPlayer currentItem] removeObserver:self forKeyPath:@"status"];
[[qPlayer currentItem] removeObserver:self forKeyPath:@"timedMetadata"];
[qPlayer advanceToNextItem];
[qPlayer play];
}
}
- (void)playerItemDidReachEnd:(NSNotification *)notification {
[self advancePlayer:nil];
NSLog(@"IT REACHED THE END");
}
现在,如果我调用它,一切都可以通过按钮或其他方式完美运行:
[[NSNotificationCenter defaultCenter]
postNotificationName:@"AVPlayerItemDidPlayToEndTimeNotification"
object:[qPlayer currentItem]];
我没有测试这个理论的测试项目,但是我猜通知会丢失,因为它们不是播放器的当前项目,并且只是添加观察者时的局部变量。
我建议添加观察者 "after" 你像这样初始化播放器。
...
qPlayer = [AVQueuePlayer queuePlayerWithItems:playerItems];
qPlayer.actionAtItemEnd = AVPlayerActionAtItemEndAdvance;
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(playerItemDidReachEnd:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: [qPlayer currentItem]];
然后在你推进玩家之后你可以为下一个当前项目再次添加观察者。
- (IBAction)advancePlayer:(id)sender {
unsigned long size = [[qPlayer items] count];
NSLog(@"%tu",size);
if (size <= 0) {
[self initializePlayerWithItems:currentKey];
[qPlayer play];
} else {
//NSLog(@"%@",[qPlayer currentItem]);
[[qPlayer currentItem] removeObserver:self forKeyPath:@"status"];
[[qPlayer currentItem] removeObserver:self forKeyPath:@"timedMetadata"];
[qPlayer advanceToNextItem];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(playerItemDidReachEnd:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: [qPlayer currentItem]];
[qPlayer play];
}
希望能帮助解决问题。
经过几天不同的代码组合和研究,我发现了以下内容:
问题:有一个带有注册观察者的 AVQueuePlayer 一次包含大约 20 多个项目。当 AVPlayerItemDidPlayToEndTimeNotification 被调用时,代码将前进到下一个项目,但该项目不会播放。
因为我有观察者附加到我的 avplayeritems 我不能只使用 AVPlayer 的 advanctonextitem 属性。我需要使用 AVPlayerItemDidPlayToEndTimeNotification。现在,当调用此方法时,它工作得很好,但是当 AVPlayerItem 发布通知时,它会在 [templayer advanceToNextItem] 处被调用,队列中的下一个 AVPlayerItem,其数据将丢失。否则,如果我通过自己的代码发布通知,它会完美地前进到下一个项目。为了解决这个问题,我尝试了一切,但这是我最终的结果,它运行良好,它只是我需要的,所以如果其他人被卡住,它可能会帮助他们。我使用了一个可变数组来存储 "queue" 并且一次只加载一个 AVPlayerItem。
if (playlist != nil) {
playlistInterval = [playlist count]/4.0;
//Array to hold AVPlayerItems
_playerItems = [[NSMutableArray alloc] initWithCapacity:playlistInterval];
for(int i = 0; i < playlistInterval; i++) {
AVPlayerItem *itemTemp = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:[playlist objectAtIndex:playlistCounter]]];
[itemTemp addObserver:self forKeyPath:@"timedMetadata" options:NSKeyValueObservingOptionNew context:nil];
[itemTemp addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(playerItemDidReachEnd:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: itemTemp];
[_playerItems addObject:itemTemp];
playlistCounter++;
}
//Init player to only one item
self.player = [[AVPlayer alloc] initWithPlayerItem:[_playerItems objectAtIndex:0]];
//Don't do anything since will be handling advancing
self.player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[ self.player addObserver:self
forKeyPath:@"rate"
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:rateDidChangeKVO];
[self.player addObserver:self forKeyPath:@"currentItem.duration"
options:0
context:durationDidChangeKVO];
return YES;
}
- (void)playerItemDidReachEnd:(NSNotification *)notification {
//NSLog(@"notifiacation thread: %d", [NSThread isMainThread]);
if ([currentKey isEqualToString:@"morning slokha"] || selection != 0)
return;
dispatch_async(dispatch_get_main_queue(), ^{
unsigned long size = [_playerItems count];
NSLog(@"%tu",size);
[self resetSlider];
if (_playerItems.count <= 1) {
[self initializePlayerWithItems:currentKey];
[self.player play];
} else {
//NSLog(@"%@",[qPlayer currentItem]);
@try{
[[_playerItems objectAtIndex:0] removeObserver:self forKeyPath:@"status"];
[[_playerItems objectAtIndex:0] removeObserver:self forKeyPath:@"timedMetadata"];
[[self.player currentItem] removeObserver:self forKeyPath:@"status"];
[[self.player currentItem] removeObserver:self forKeyPath:@"timedMetadata"];
}@catch(id anException){
//do nothing, obviously it wasn't attached because an exception was thrown
}
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(playerItemDidReachEnd:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: [_playerItems objectAtIndex:0]];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(playerItemDidReachEnd:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: [self.player currentItem]];
[_playerItems removeObjectAtIndex:0];
//Replace AVPlayerItem manually
[self.player replaceCurrentItemWithPlayerItem:[_playerItems objectAtIndex:0]];
[self.player seekToTime:kCMTimeZero];
[self.player play];
}
});
}
确保您在主线程上注册您的通知。
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(itemDidFinishPlaying:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:self.playerItem];
});
1 - 如果 PlayerItems 丢失,我会感到非常惊讶。它们被玩家保留在某个地方,在一个数组中,...
2 - Appledoc 明确指出您可以在任何线程上发送通知。
在这种情况下,一切都应该工作,但没有,我建议检查上下文。我经历过的一个简单案例(我已经浪费了几个小时!!)是持有播放器的视图被处理掉了。在这种情况下,视频仍在播放。所以很难调试。
我终于放弃使用Notification系统,改用KVO了。 ( [玩家 addObserver:self keypath:@"status" … )
我在处理视图时遇到异常,因为视图仍然注册为玩家观察者。
我已经解决了视图错误,然后我的视图按预期收到了 playerItem 的通知。
经常是这样。苹果告诉我们这很简单。
如果它不起作用,我们会做很多变通办法和个人补丁,而不是试图了解我们所做的可能会产生副作用的事情……
我到处搜索,仍然找不到解决这个问题的方法。我有一个 AVQueuePlayer
用于我正在制作的广播应用程序。一次大约有 20 AVPlayerItems
排队。我将 AVPlayerItemDidPlayToEndTimeNotification
观察者添加到每个项目中。通知被触发并执行代码以删除元数据和状态的关键观察者,因此它不会崩溃并前进到队列中的下一个项目。但是它不想玩。没有错误,状态为可以播放,AVPlayer
URL 完美加载。如果我单击按钮调用 advancenextitem,它可以完美运行并且也可以完美播放。
现在最奇怪的是:如果我 post 手动通知,通知代码将完美运行。我将不胜感激任何帮助或意见,因为我一直在尽一切努力使它正常工作。
if (playlist != nil) {
playlistInterval = [playlist count]/4.0;
NSMutableArray *playerItems = [[NSMutableArray alloc] initWithCapacity:playlistInterval];
for(int i = 0; i < playlistInterval; i++) {
NSString *tempString = [playlist objectAtIndex:i];
//NSLog(@"%@", tempString);
NSURL *temp = [[NSURL alloc] initWithString:tempString];
AVPlayerItem *itemTemp = [AVPlayerItem playerItemWithURL:temp];
[itemTemp addObserver:self forKeyPath:@"timedMetadata" options:NSKeyValueObservingOptionNew context:nil];
[itemTemp addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(playerItemDidReachEnd:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: itemTemp];
[playerItems addObject:itemTemp];
playlistCounter++;
}
qPlayer = [AVQueuePlayer queuePlayerWithItems:playerItems];
qPlayer.actionAtItemEnd = AVPlayerActionAtItemEndAdvance;
[ qPlayer addObserver:self
forKeyPath:@"rate"
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:rateDidChangeKVO];
[qPlayer addObserver:self forKeyPath:@"currentItem.duration"
options:0
context:durationDidChangeKVO];
return YES;
}
return NO;
}
- (IBAction)advancePlayer:(id)sender {
unsigned long size = [[qPlayer items] count];
NSLog(@"%tu",size);
if (size <= 0) {
[self initializePlayerWithItems:currentKey];
[qPlayer play];
} else {
//NSLog(@"%@",[qPlayer currentItem]);
[[qPlayer currentItem] removeObserver:self forKeyPath:@"status"];
[[qPlayer currentItem] removeObserver:self forKeyPath:@"timedMetadata"];
[qPlayer advanceToNextItem];
[qPlayer play];
}
}
- (void)playerItemDidReachEnd:(NSNotification *)notification {
[self advancePlayer:nil];
NSLog(@"IT REACHED THE END");
}
现在,如果我调用它,一切都可以通过按钮或其他方式完美运行:
[[NSNotificationCenter defaultCenter]
postNotificationName:@"AVPlayerItemDidPlayToEndTimeNotification"
object:[qPlayer currentItem]];
我没有测试这个理论的测试项目,但是我猜通知会丢失,因为它们不是播放器的当前项目,并且只是添加观察者时的局部变量。
我建议添加观察者 "after" 你像这样初始化播放器。
...
qPlayer = [AVQueuePlayer queuePlayerWithItems:playerItems];
qPlayer.actionAtItemEnd = AVPlayerActionAtItemEndAdvance;
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(playerItemDidReachEnd:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: [qPlayer currentItem]];
然后在你推进玩家之后你可以为下一个当前项目再次添加观察者。
- (IBAction)advancePlayer:(id)sender {
unsigned long size = [[qPlayer items] count];
NSLog(@"%tu",size);
if (size <= 0) {
[self initializePlayerWithItems:currentKey];
[qPlayer play];
} else {
//NSLog(@"%@",[qPlayer currentItem]);
[[qPlayer currentItem] removeObserver:self forKeyPath:@"status"];
[[qPlayer currentItem] removeObserver:self forKeyPath:@"timedMetadata"];
[qPlayer advanceToNextItem];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(playerItemDidReachEnd:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: [qPlayer currentItem]];
[qPlayer play];
}
希望能帮助解决问题。
经过几天不同的代码组合和研究,我发现了以下内容: 问题:有一个带有注册观察者的 AVQueuePlayer 一次包含大约 20 多个项目。当 AVPlayerItemDidPlayToEndTimeNotification 被调用时,代码将前进到下一个项目,但该项目不会播放。 因为我有观察者附加到我的 avplayeritems 我不能只使用 AVPlayer 的 advanctonextitem 属性。我需要使用 AVPlayerItemDidPlayToEndTimeNotification。现在,当调用此方法时,它工作得很好,但是当 AVPlayerItem 发布通知时,它会在 [templayer advanceToNextItem] 处被调用,队列中的下一个 AVPlayerItem,其数据将丢失。否则,如果我通过自己的代码发布通知,它会完美地前进到下一个项目。为了解决这个问题,我尝试了一切,但这是我最终的结果,它运行良好,它只是我需要的,所以如果其他人被卡住,它可能会帮助他们。我使用了一个可变数组来存储 "queue" 并且一次只加载一个 AVPlayerItem。
if (playlist != nil) {
playlistInterval = [playlist count]/4.0;
//Array to hold AVPlayerItems
_playerItems = [[NSMutableArray alloc] initWithCapacity:playlistInterval];
for(int i = 0; i < playlistInterval; i++) {
AVPlayerItem *itemTemp = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:[playlist objectAtIndex:playlistCounter]]];
[itemTemp addObserver:self forKeyPath:@"timedMetadata" options:NSKeyValueObservingOptionNew context:nil];
[itemTemp addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(playerItemDidReachEnd:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: itemTemp];
[_playerItems addObject:itemTemp];
playlistCounter++;
}
//Init player to only one item
self.player = [[AVPlayer alloc] initWithPlayerItem:[_playerItems objectAtIndex:0]];
//Don't do anything since will be handling advancing
self.player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[ self.player addObserver:self
forKeyPath:@"rate"
options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew
context:rateDidChangeKVO];
[self.player addObserver:self forKeyPath:@"currentItem.duration"
options:0
context:durationDidChangeKVO];
return YES;
}
- (void)playerItemDidReachEnd:(NSNotification *)notification {
//NSLog(@"notifiacation thread: %d", [NSThread isMainThread]);
if ([currentKey isEqualToString:@"morning slokha"] || selection != 0)
return;
dispatch_async(dispatch_get_main_queue(), ^{
unsigned long size = [_playerItems count];
NSLog(@"%tu",size);
[self resetSlider];
if (_playerItems.count <= 1) {
[self initializePlayerWithItems:currentKey];
[self.player play];
} else {
//NSLog(@"%@",[qPlayer currentItem]);
@try{
[[_playerItems objectAtIndex:0] removeObserver:self forKeyPath:@"status"];
[[_playerItems objectAtIndex:0] removeObserver:self forKeyPath:@"timedMetadata"];
[[self.player currentItem] removeObserver:self forKeyPath:@"status"];
[[self.player currentItem] removeObserver:self forKeyPath:@"timedMetadata"];
}@catch(id anException){
//do nothing, obviously it wasn't attached because an exception was thrown
}
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(playerItemDidReachEnd:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: [_playerItems objectAtIndex:0]];
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(playerItemDidReachEnd:)
name: AVPlayerItemDidPlayToEndTimeNotification
object: [self.player currentItem]];
[_playerItems removeObjectAtIndex:0];
//Replace AVPlayerItem manually
[self.player replaceCurrentItemWithPlayerItem:[_playerItems objectAtIndex:0]];
[self.player seekToTime:kCMTimeZero];
[self.player play];
}
});
}
确保您在主线程上注册您的通知。
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(itemDidFinishPlaying:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:self.playerItem];
});
1 - 如果 PlayerItems 丢失,我会感到非常惊讶。它们被玩家保留在某个地方,在一个数组中,...
2 - Appledoc 明确指出您可以在任何线程上发送通知。
在这种情况下,一切都应该工作,但没有,我建议检查上下文。我经历过的一个简单案例(我已经浪费了几个小时!!)是持有播放器的视图被处理掉了。在这种情况下,视频仍在播放。所以很难调试。
我终于放弃使用Notification系统,改用KVO了。 ( [玩家 addObserver:self keypath:@"status" … )
我在处理视图时遇到异常,因为视图仍然注册为玩家观察者。
我已经解决了视图错误,然后我的视图按预期收到了 playerItem 的通知。
经常是这样。苹果告诉我们这很简单。 如果它不起作用,我们会做很多变通办法和个人补丁,而不是试图了解我们所做的可能会产生副作用的事情……