OStack程序员社区-中国程序员成长平台

标题: IOS addPeriodicTimeObserverForInterval 触发太多次 [打印本页]

作者: 菜鸟教程小白    时间: 2022-12-12 23:43
标题: IOS addPeriodicTimeObserverForInterval 触发太多次

我注意到我的应用发生了一件奇怪的事情。 这是一个使用 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 optionsptionsDictionary];
[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 中的项目创建一个播放器,然后在我的 View 中创建一个布局

/* 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 是电影的帧率)。 在我的 block 中,我现在只写日志。

/* 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 尝试了不同且更高的时间尺度......但没有效果



Best Answer-推荐答案


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

第一件事:

如果您查看 documentation您可能会看到以下语句。

The block is invoked periodically at the interval specified, interpreted according to the timeline of the current item. The block is also invoked whenever time jumps and whenever playback starts or stops. If the interval corresponds to a very short interval in real time, the player may invoke the block less frequently than requested. Even so, the player will invoke the block sufficiently often for the client to update indications of the current time appropriately in its end-user interface.

这表明为什么在开始和结束播放时调用它。

关于函数被多次调用,我猜这是因为玩家正在遭受的内部状态变化。我检查了播放器的“rate”、“status”和“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 上,您只需检查我们刚刚分配的变量。

__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
                                      }
                                  }];

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

关于IOS addPeriodicTimeObserverForInterval 触发太多次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13401206/






欢迎光临 OStack程序员社区-中国程序员成长平台 (https://ostack.cn/) Powered by Discuz! X3.4