IOS addPeriodicTimeObserverForInterval引发了太多次

时间:2012-11-15 15:52:15

标签: ios ios5 avplayer

我注意到我的应用程序发生了一件奇怪的事情。 这是一个使用AVFoundation类的视频应用程序。

我需要在特定时间发射一些事件。 我放了一些代码然后我评论它:

/* I prepare the movie clip */
AVMutableComposition *composition = [[AVMutableComposition alloc] init];
NSBundle *bundle = [NSBundle mainBundle];
NSDictionary *optionsDictionary = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:AVURLAssetPreferPreciseDurationAndTimingKey];
NSString *path = [bundle pathForResource:@"13.VIDEO_A (BAULE)" ofType:@"mp4"];
NSURL *videoUrl = [NSURL fileURLWithPath:path];
AVURLAsset* sourceAsset = [AVURLAsset URLAssetWithURL:videoUrl options:optionsDictionary];
[composition insertTimeRange:CMTimeRangeMake(kCMTimeZero, [sourceAsset duration]) ofAsset:sourceAsset atTime:currentTime error:NULL];

在我的viewDidLoad中,我准备剪辑。我使用AVUrlAsset能够使用AVURLAssetPreferPreciseDurationAndTimingKey的选项字典来更精确地使用。

/* I create the player */
AVPlayer *mPlayer = [AVPlayer playerWithPlayerItem:[AVPlayerItem playerItemWithAsset:composition]];
AVPlayerLayer *mPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:mPlayer];
mPlayerLayer.frame = CGRectMake(0.00, 96.00, 1024.00, 576.00);
[self.view.layer addSublayer: mPlayerLayer];

我使用AVUrlasset中的项目创建了一个播放器,然后在我的视图中创建了一个布局

/* I set the observer */
[mPlayer addPeriodicTimeObserverForInterval:CMTimeMake(5,25) queue:NULL usingBlock:^(CMTime time) { 
     NSLog(@"Event : value: %lld, timescale %d, seconds: %f",
         time.value, time.timescale,(float) time.value / time.timescale); }];

我设置观察者,每隔5/25秒,0,2秒(25是电影的帧速率)。 在我的块中,我现在只写日志。

/* Play the movie */
[mPlayer play];

最后我玩。

除了我的日志错误外,似乎一切正常:

2012-11-15 16:43:05.382 PerfectCircle Beta[6680:707] Evento : value: 0, timescale 1, seconds: 0.000000
2012-11-15 16:43:05.410 PerfectCircle Beta[6680:707] Evento : value: 0, timescale 1, seconds: 0.000000
2012-11-15 16:43:05.563 PerfectCircle Beta[6680:707] Evento : value: 0, timescale 1, seconds: 0.000000
2012-11-15 16:43:05.580 PerfectCircle Beta[6680:707] Evento : value: 0, timescale 1, seconds: 0.000000
2012-11-15 16:43:05.747 PerfectCircle Beta[6680:707] Evento : value: 5489807, timescale 1000000000, seconds: 0.005490
2012-11-15 16:43:05.751 PerfectCircle Beta[6680:707] Evento : value: 8949705, timescale 1000000000, seconds: 0.008950
2012-11-15 16:43:05.753 PerfectCircle Beta[6680:707] Evento : value: 10679967, timescale 1000000000, seconds: 0.010680
2012-11-15 16:43:05.990 PerfectCircle Beta[6680:707] Evento : value: 248121672, timescale 1000000000, seconds: 0.248122
2012-11-15 16:43:06.169 PerfectCircle Beta[6680:707] Evento : value: 426865945, timescale 1000000000, seconds: 0.426866

在随机数量的火灾之后它开始计时。但是在开始时它会使事件发生5/6次。我尝试过不同的电影和编解码器。 如果我提高率(es:CMTimeMake(25,25))没有任何改变。

我以这种方式开始与addBoundaryTimeObserverForTimes合作:

NSArray *starts = [NSArray arrayWithObjects:[NSValue valueWithCMTime:CMTimeMakeWithSeconds(0.2,25)],nil];
[_player addBoundaryTimeObserverForTimes:starts queue:NULL usingBlock:^{ log_function }];

但我遇到了同样的问题。但在这里,如果我提高率,我不再看到问题(但它对我的目标不好)。 我的问题是,我必须准确计算电影播放精确时刻的次数。我无法用if (currenttime==0.3)测试它,因为它不精确。

这是一个错误吗?我想念什么?你有没有听说过类似的东西?

感谢您的帮助。 丹尼尔

更新: 这似乎是一个开始和结束的问题。

2012-11-15 16:43:05.747 PerfectCircle Beta[6680:707] Evento : value: 0, timescale 1, seconds: 0.000000
2012-11-15 16:43:05.747 PerfectCircle Beta[6680:707] Evento : value: 5489807, timescale 1000000000, seconds: 0.005490

错误的日志与正确的日志有不同的时间表。播放结束时也会发生同样的情况。似乎在开始和结束时它执行计时器但电影尚未加载或已经关闭。 我试过把观察者放在比赛后但没有改变。

我也为我的CMTimeMake尝试了不同的更高时间刻度......但没有效果

2 个答案:

答案 0 :(得分:4)

我知道这个问题有点陈旧,但无论如何.......

首先要做的事情:

如果您查看documentation,则可能会看到以下声明。

  

以指定的间隔定期调用该块,   根据当前项目的时间线进行解释。 块   每当时间跳跃以及每当开始播放时也会调用   停止即可。如果间隔对应于实际的非常短的间隔   时间,玩家可以比请求的频率更少地调用该块。   即便如此,玩家也会经常足够地调用该区块   客户端在其中适当地更新当前时间的指示   最终用户界面。

表示在开始和结束播放时调用它的原因。

关于被多次调用的函数我猜它是因为玩家正在遭受的内部状态变化而发生的。我已经检查过'','状态'和' playerItem'玩家的属性似乎没有说出发生了什么。

你可以做的一件事是解决这个问题,只考虑玩家真正玩的事件。

在调用play方法之前添加以下代码。

__block AVPlayer* blockPlayer = self.player;
__block typeof(self) blockSelf = self;

// Setup boundary time observer to trigger when audio really begins,
// specifically after 1/3 of a second playback
self.startObserver = [self.player addBoundaryTimeObserverForTimes:
        @[[NSValue valueWithCMTime:CMTimeAdd(self.player.currentTime, CMTimeMake(1, 3))]]
                             queue:NULL
                        usingBlock:^{                    
                          blockSelf.isPlaying = YES;    
                          // Remove the boundary time observer
                          [blockPlayer removeTimeObserver:blockSelf.startObserver];
                        }];

现在在 addPeriodicTimeObserverForInterval 块中,您只需要检查我们刚刚分配的变量。

__block typeof(self) blockSelf = self;  
[self addPeriodicTimeObserverForInterval:CMTimeMake(60, 1)
                                   queue:dispatch_get_main_queue()
                              usingBlock:^(CMTime time)
                                  {
                                      if (blockSelf.isPlaying) {
                                         ... do some stuff here
                                      }
                                  }];

嗯,这不是更清洁的解决方案,但对我来说工作得很好。如果我找到更好的东西,我会来编辑。

答案 1 :(得分:0)

也许在你的CMTime调用中你需要设置更高的精度。目前,您将其设置为25秒,但不允许有浮动值舍入的余地。尝试使用25,000作为您的时间表,看看是否有效。