precise timing with AVMutableComposition
我正在尝试使用 AVMutableComposition 在精确的时间播放一系列声音文件。
当视图加载时,我创建乐曲的目的是在 1 秒内均匀地播放 4 个声音。声音的长短无关紧要,我只想在 0、0.25、0.5 和 0.75 秒时触发它们:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | AVMutableComposition *composition = [[AVMutableComposition alloc] init]; NSDictionary *options = @{AVURLAssetPreferPreciseDurationAndTimingKey : @YES}; for (NSInteger i = 0; i < 4; i++) { AVMutableCompositionTrack* track = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; NSURL *url = [[NSBundle mainBundle] URLForResource:[NSString stringWithFormat:@"sound_file_%i", i] withExtension:@"caf"]; AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:options]; AVAssetTrack *assetTrack = [asset tracksWithMediaType:AVMediaTypeAudio].firstObject; CMTimeRange timeRange = [assetTrack timeRange]; Float64 t = i * 0.25; NSError *error; BOOL success = [track insertTimeRange:timeRange ofTrack:assetTrack atTime:CMTimeMakeWithSeconds(t, 1) error:&error]; if (!success) { NSLog(@"unsuccesful creation of composition"); } if (error) { NSLog(@"composition creation error: %@", error); } } AVPlayerItem* playerItem = [AVPlayerItem playerItemWithAsset:composition]; self.avPlayer = [[AVPlayer alloc] initWithPlayerItem:playerItem]; |
乐曲创建成功,没有错误。稍后,当我想播放序列时,我会这样做:
1 2 | [self.avPlayer seekToTime:CMTimeMakeWithSeconds(0, 1)]; [self.avPlayer play]; |
由于某种原因,声音的间隔根本不均匀 - 但几乎同时播放所有声音。我尝试了同样的事情,间隔超过 4 秒,替换了这样的时间计算:
1 | Float64 t = i * 1.0; |
这玩得很完美。任何低于 1 秒的时间间隔似乎都会产生意想不到的结果。我错过了什么? AVCompositions 不应该用于 1 秒以下的时间间隔吗?还是我误解了时间间隔?
您的
您需要将时间刻度设置为 4 以获得所需的 0.25 秒切片。由于 CMTime 现在以 0.25 秒为单位,因此您不需要 i * 0.25 计算。直接使用
如果您将来可能需要更精确,您现在应该考虑到它,这样您以后就不必调整您的代码。 Apple 建议使用 600 的时间刻度,因为它是常见视频帧速率(24、25 和 30 FPS)的倍数,但它也适用于纯音频。因此,对于您的情况,您将使用 24 个切片来获得 0.25 秒的值;
至于您几乎同时播放所有 4 种声音的问题,请注意这个未回答的 SO 问题,它仅在第一次播放时发生。即使进行了上述更改,您仍可能遇到此问题。
除非每首曲目的长度正好为 0.25 秒,否则这就是你的问题:
1 2 3 | Float64 t = i * 0.25; NSError *error; BOOL success = [track insertTimeRange:timeRange ofTrack:assetTrack atTime:CMTimeMakeWithSeconds(t, 1) error:&error]; |
您需要跟踪到目前为止添加的累积时间范围,并在那时插入下一首曲目:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | CMTime currentTime = kCMTimeZero; for (NSInteger i = 0; i < 4; i++) { /* Code to create track for insertion */ CMTimeRange trackTimeRange = [assetTrack timeRange]; BOOL success = [track insertTimeRange:trackTimeRange ofTrack:assetTrack atTime:currentTime error:&error]; /* Error checking code */ //Update time range for insertion currentTime = CMTimeAdd(currentTime,trackTimeRange.duration); } |
我修改了你的代码,抱歉我没有时间测试它。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | AVMutableComposition *composition = [AVMutableComposition composition]; NSDictionary *options = @{AVURLAssetPreferPreciseDurationAndTimingKey : @YES}; CMTime totalDuration = kCMTimeZero; for (NSInteger i = 0; i < 4; i++) { AVMutableCompositionTrack* track = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"Record_%i", i] ofType:@"caf"]]; AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:url options:options]; AVAssetTrack *assetTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]; CMTimeRange timeRange = [assetTrack timeRange]; NSError *error; BOOL success = [track insertTimeRange:timeRange ofTrack:assetTrack atTime:CMTIME_COMPARE_INLINE(totalDuration, >, kCMTimeZero)? CMTimeAdd(totalDuration, CMTimeMake(1, 4)): totalDuration error:&error]; if (!success) { NSLog(@"unsuccesful creation of composition"); } if (error) { NSLog(@"composition creation error: %@", error); } totalDuration = CMTimeAdd(CMTimeAdd(totalDuration,CMTimeMake(1, 4)), asset.duration); } AVPlayerItem* playerItem = [AVPlayerItem playerItemWithAsset:composition]; self.avPlayer = [[AVPlayer alloc] initWithPlayerItem:playerItem]; |
附言使用