如何在 iOS 中获取音频文件的持续时间?

Posted

技术标签:

【中文标题】如何在 iOS 中获取音频文件的持续时间?【英文标题】:How to get the duration of an audio file in iOS? 【发布时间】:2011-07-10 07:34:01 【问题描述】:
NSDictionary* fileAttributes = 
    [[NSFileManager defaultManager] attributesOfItemAtPath:filename 
                                                     error:nil]

从文件属性keys中可以得到日期、大小等,但是如何得到时长呢?

【问题讨论】:

你从哪里得到 NSDictionary? 它是 ios 内置的数据类型。它包含文件的日期、大小等键值对。 我知道什么是 NSDictionary。你从哪里检索这个 NSDictionary 的实例?您需要提供更多关于您在问题中尝试做什么的信息。 附注NSDictionary 不一定包含特定文件的数据和大小的值,它只包含一组键到一组值的一对一映射。 我已经编辑了我的问题以包含该信息。感谢并为给您带来的不便深表歉意。 【参考方案1】:

在'File Attribute Keys' of the NSFileManager class reference 中,您可以看到没有可用于返回歌曲时长的键。 NSFileManager 实例获取的有关文件的所有信息都与操作系统中实际文件本身的属性有关,例如文件大小。 NSFileManager 实际上并不解释文件。

为了获取文件的持续时间,您需要使用一个知道如何解释文件的类。 AVFoundation 框架提供了您需要的确切类,AVAsset。您可以使用具体的子类 AVURLAsset 实例化这个抽象类的一个实例,然后为它提供一个 NSURL,它指向您希望获得持续时间的音频文件。然后,您可以通过查询 AVAsset 实例的duration 属性来获取持续时间。

例如:

AVURLAsset* audioAsset = [AVURLAsset URLAssetWithURL:audioFileURL options:nil];
CMTime audioDuration = audioAsset.duration;
float audioDurationSeconds = CMTimeGetSeconds(audioDuration);

请注意,AVFoundation 被设计为高度异步的框架,以提高性能和整体用户体验。即使执行简单的任务(例如查询媒体文件的持续时间)也可能需要很长时间,并可能导致您的应用程序挂起。您应该使用AVAsynchronousKeyValueLoading protocol 异步加载歌曲的持续时间,然后在完成处理程序块中更新您的 UI。您应该观看'Block Programming Guide' 以及标题为“发现 AV 基金会”的 WWDC2010 视频,该视频可在https://developer.apple.com/videos/wwdc/2010 免费获得。

【讨论】:

我总是得到 0 持续时间 这种做法给了我错误:FigByteFlumeCustomURLOpen signalled err=-12936 (kFigByteFlumeError_BadState) (no provider) at /SourceCache/CoreMedia/CoreMedia-1562.235/Prototypes/FigHTTP/FigByteFlumeCustomURL.c line 1486。不过,下面 John Goodstadt 的回答对我有用。 优秀的答案!无需创建音频播放器实例即可获得持续时间的非常简洁的方法。 这种获取持续时间的方法可能会返回无效值。始终使用 AVAsynchronousKeyValueLoading 中的方法包装您的 AVAssetAVAssetTrack 访问。 确保除了 AVFoundation 之外还添加和导入 CoreMedia 框架。感谢 James 提供的这种有效方法。【参考方案2】:

对于仍在寻找此内容的任何人。 根据答案,Swift 4 的代码(包括从 Apple 文档中获取的异步加载值):

let audioAsset = AVURLAsset.init(url: yourURL, options: nil)

audioAsset.loadValuesAsynchronously(forKeys: ["duration"]) 
    var error: NSError? = nil
    let status = audioAsset.statusOfValue(forKey: "duration", error: &error)
    switch status 
    case .loaded: // Sucessfully loaded. Continue processing.
        let duration = audioAsset.duration
        let durationInSeconds = CMTimeGetSeconds(duration)
        print(Int(durationInSeconds))
        break              
    case .failed: break // Handle error
    case .cancelled: break // Terminate processing
    default: break // Handle all other cases
    

【讨论】:

【参考方案3】:

为了完整性 - 还有另一种获取 mp3 文件持续时间的方法:

NSURL * pathToMp3File = ...
NSError *error = nil;
AVAudioPlayer* avAudioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:pathToMp3File error:&error];

double duration = avAudioPlayer.duration; 
avAudioPlayer = nil;

我已经使用了这个,没有明显的延迟。

【讨论】:

我知道问题是关于音频文件的,但以防万一:这对我来说不适用于 .mov 文件(另一个答案是这样),否则这很好。 你需要有“loadValuesAsynchronouslyForKeys” 这种方法不准确,很多时候会秒掉持续时间 @KhaledAnnajar 我也遇到了同样的事情。【参考方案4】:

你可以在 Swift 中使用:

let audioAsset = AVURLAsset.init(url: audioFileURL, options: nil)
let duration = audioAsset.duration
let durationInSeconds = CMTimeGetSeconds(duration)

【讨论】:

这对我不起作用。目前我不知道为什么,但音频文件的持续时间完全不同。 此方法导致延迟 如前所述,如果你有很多资产,这会造成严重的延迟。【参考方案5】:

Swift 5.0 + iOS 13:这是它对我有用的唯一方法(Swift 中的@John Goodstadt 解决方案)。目前我不确定为什么,但是使用以下代码录制的音频文件(在我的情况下是语音备忘录)和接收到的音频文件之间平均存在 0.2 秒的差异。

    do 
        let audioPlayer = try AVAudioPlayer(contentsOf: fileURL)
        return CGFloat(audioPlayer.duration)
     catch 
        assertionFailure("Failed crating audio player: \(error).")
        return nil
    

【讨论】:

这太棒了。我在代码中稍作修改:定义 audioPlayer 后返回 CGFloat(audioPlayer?.duration ?? 0) 这会导致像android这样的阻塞主线程【参考方案6】:

我通过AVAudioRecorder 录制了一个线性 PCM 文件 (.pcm)。我在 Farhad Malekpour 的帮助下获得了持续时间。也许这可以帮助你: iPhone: get duration of an audio file

NSURL *fileUrl = [NSURL fileURLWithPath:yourFilePath];
AudioFileID fileID;
OSStatus result = AudioFileOpenURL((__bridge CFURLRef)fileUrl,kAudioFileReadPermission, 0, &fileID);
Float64 duration = 0; //seconds. the duration of the audio.
UInt32 ioDataSize = sizeof(Float64);

result = AudioFileGetProperty(fileID, kAudioFilePropertyEstimatedDuration, 
&ioDataSize, &duration);
AudioFileClose(fileID);
if(0 == result) 
   //success

else 
  switch (result) 
    case kAudioFileUnspecifiedError:
        //
     break;
        // other cases...
    default:
        break;
  

【讨论】:

【参考方案7】:

我实际上发现您可以使用“从音乐中获取详细信息”块并将其设置为“获取持续时间”,其中可以是来自提示的 mp3 输入、指向 mp3 的链接、来自共享表的 mp3等

【讨论】:

以上是关于如何在 iOS 中获取音频文件的持续时间?的主要内容,如果未能解决你的问题,请参考以下文章

ios中的音频流持续时间

如何获取音频文件的持续时间?

如何将 JProgressBar 设置为音频文件的持续时间?

如何在 iOS 中读取音频文件并获取样本?

如何获取音频文件的长度而不在 Flutter/Dart 中播放

如何从麦克风实时获取原始音频帧或从 iOS 中保存的音频文件获取原始音频帧?