3

我注意到我的应用发生了一件奇怪的事情。这是一个使用 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 尝试了不同且更高的时间尺度......但没有效果

4

2 回答 2

4

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

第一件事:

如果您查看文档,您可能会看到以下声明。

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

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

关于函数被多次调用,我猜这是因为玩家正在遭受的内部状态变化。我检查了播放器的“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 typeof(self) blockSelf = self;  
[self addPeriodicTimeObserverForInterval:CMTimeMake(60, 1)
                                   queue:dispatch_get_main_queue()
                              usingBlock:^(CMTime time)
                                  {
                                      if (blockSelf.isPlaying) {
                                         ... do some stuff here
                                      }
                                  }];

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

于 2014-12-15T18:57:03.457 回答
0

也许在您的 CMTime 调用中,您需要设置更高的精度。目前,您将其设置为 25 秒,但这不允许对浮点值进行舍入。尝试使用 25,000 作为您的时间表,看看是否可行。

于 2012-11-16T21:11:10.870 回答