【问题标题】:precise timing with AVMutableComposition使用 AVMutableComposition 进行精确计时
【发布时间】:2014-10-14 12:33:52
【问题描述】:

我正在尝试使用 AVMutableComposition 在精确的时间播放一系列声音文件。

当视图加载时,我创建乐曲的目的是在 1 秒内均匀地播放 4 个声音。声音的长短无关紧要,我只想在 0、0.25、0.5 和 0.75 秒时触发它们:

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

乐曲创建成功,没有错误。后来,当我想播放序列时,我会这样做:

[self.avPlayer seekToTime:CMTimeMakeWithSeconds(0, 1)];
[self.avPlayer play];

由于某种原因,声音的间隔根本不均匀 - 但几乎同时播放所有声音。我尝试了同样的事情,间隔超过 4 秒,替换了这样的时间计算:

Float64 t = i * 1.0;

这玩得很完美。任何低于 1 秒的时间间隔似乎都会产生意想不到的结果。我错过了什么? AVCompositions 不应该用于 1 秒以下的时间间隔吗?还是我误解了时间间隔?

【问题讨论】:

    标签: ios avfoundation core-audio avmutablecomposition avcomposition


    【解决方案1】:

    我修改了你的代码,抱歉我没有时间测试它。

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

    附:使用kCMTimeZero 而不是CMTimeMakeWithSeconds(0, 1)

    【讨论】:

    • 这不会成功创建合成。 AVFoundationErrorDomain 代码=-11800
    • 而且这里的时间计算是错误的。对于索引 1 到 3,CMTime 为 (value = 0, timescale = 0, flags = 0, epoch = 0),这是不对的。
    【解决方案2】:

    除非每首曲目的长度恰好为 0.25 秒,否则这是您的问题:

    Float64 t = i * 0.25;
    NSError *error;
    BOOL success = [track insertTimeRange:timeRange ofTrack:assetTrack atTime:CMTimeMakeWithSeconds(t, 1) error:&error];
    

    您需要跟踪到目前为止添加的累积时间范围,并在那时插入下一首曲目:

    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);
    }
    

    【讨论】:

    • 感谢您的回答。您的代码中的一项更正,trackTimeRange 在 currentTimeRange 的计算中需要是 trackTimeRange.duration(需要是 CCTime)。然而,这实际上不是我想要做的:我想以 0、0.25、0.5 和 0.75 秒的时间播放样本,无论它们的持续时间如何。它们可能重叠,或者它们之间可能有空间。您的解决方案按顺序播放声音,但在触发下一个之前等待每个完成。
    • 啊,是的,对不起...我还将currentTimeRange 重命名为currentTime,因为它实际上是CMTime 对象。根据您更新的问题,答案相当复杂。我会发布一个更新的解决方案。
    【解决方案3】:

    您的 CMTimeMakeWithSeconds(t, 1) 是整秒“切片”,因为您的时间刻度设置为 1。无论 t 是什么分数,atTime: 将始终以 0 结束。这就是为什么当您增加它时它会起作用到 1 秒 (t=i*1)。

    您需要将时间刻度设置为 4 以获得所需的 0.25 秒切片。由于 CMTime 现在以 0.25 秒为单位,因此您不需要 i * 0.25 计算。直接使用i即可; atTime:CMTimeMake(i, 4)

    如果您将来可能需要更精确,您现在应该考虑到它,这样您以后就不必调整您的代码。 Apple 建议使用 600 的时间刻度,因为它是常见视频帧速率(24、25 和 30 FPS)的倍数,但它也适用于纯音频。因此,对于您的情况,您将使用 24 个切片来获得 0.25 秒的值; Float64 t = i * 24; atTime:CMTimeMake(t, 600)

    关于您几乎同时播放所有 4 种声音的问题,请注意 this unanswered SO question,它只在第一次播放时发生。即使进行了上述更改,您仍可能会遇到此问题。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多