TagLib# 是 C# 中读取 MP3 时长最稳方案,它准确解析帧头与 Xing/VBRI 头并计算时长,避免依赖外部工具或手动解析导致的 VBR、APE 标签等兼容问题。

用 TagLib# 读取 MP3 时长是最稳的选择
直接结论:别自己解析 ID3 或 MPEG 帧,TagLib# 是 C# 生态里唯一能兼顾准确性、跨平台性和维护性的方案。它底层会真正解码帧头 + Xing/VBRI 头(如果存在),再结合采样率和总帧数算出精确时长,不是靠文件大小粗略估算。
常见错误是拿 MediaInfo CLI 封装或硬写二进制解析——前者要依赖外部进程,后者极易在 VBR、带封面、含 APE 标签的 MP3 上翻车。比如读到 "Invalid MPEG frame header" 或返回 0 秒,基本就是跳过了 Xing 头校验逻辑。
实操建议:
- 用 NuGet 安装
TagLibSharp(注意不是已归档的TagLib#旧包) - 路径必须是本地文件(
file://或绝对路径),不支持Stream直接传入——否则时长恒为 0 - 调用
TagLib.File.Create(path)后立刻访问Properties.Duration.TotalSeconds,别等Tag属性
var file = TagLib.File.Create(@"C:\music\song.mp3"); double seconds = file.Properties.Duration.TotalSeconds; // 精确到毫秒
为什么 Windows.Media.Core 在 .NET 6+ 上跑不通
有人试过用 UWP 的 MediaSource + MediaPlaybackItem 拿时长,结果在 .NET 6/7 桌面项目里抛 System.Runtime.InteropServices.COMException: 0x80040154。这不是代码问题,是 Windows Runtime API 强制绑定 UWP 沙箱环境,桌面应用默认没注册对应 COM 类。
即使加了 WindowsBase 引用或启用 EnablePreviewFeatures,也无法绕过系统级限制。这个路径只适用于打包成 MSIX 的 WinUI 3 应用,普通 Console/WPF/WinForms 项目请直接放弃。
替代思路:
- 坚持用
TagLib#—— 它纯托管,不依赖系统组件 - 若必须用系统解码器(比如要支持 M4A/FLAC),改用
FFmpeg.AutoGen调用 libavformat,但得自己处理 DLL 分发和平台差异
Properties.Duration 返回 0 的三个典型原因
不是库坏了,是文件或调用姿势踩了坑。最常遇到的三种情况:
- 路径是相对路径且工作目录不对,
TagLib#静默失败,Duration默认为TimeSpan.Zero - MP3 文件损坏或被截断(比如下载未完成),
TagLib#读不到有效帧头,不会抛异常,只设时长为 0 - 用了
TagLib.File.Create(stream)传入MemoryStream,但没设置stream.Position = 0,导致从中间开始读,帧同步失败
验证方法很简单:打印 file.AnalysisMode(应为 AnalysisMode.Accurate)和 file.Properties.AudioBitrate(非零说明解析成功)。
性能和线程安全要注意什么
TagLib.File.Create() 是 IO 密集型操作,单次调用平均耗时 5–20ms(SSD),但会打开文件句柄并缓存部分数据。频繁调用大量小文件时容易触发句柄泄漏或磁盘争用。
关键约束:
- 不要在循环里反复
Create同一文件——复用实例,它本身是线程安全的 - 大列表批量处理时加
Task.WhenAll不如用Parallel.ForEach,因为 IO 并发太高反而降低吞吐 - 如果只是查时长不改标签,创建时传
ReadStyle.Average可提速 30%,但对含 Xing 头的 VBR 文件可能误差 ±0.5 秒
复杂点在于:有些 MP3 的 Xing 头里 Duration 字段被厂商填错,TagLib# 会 fallback 到逐帧扫描,这时耗时飙升到几百毫秒。这种文件没法预判,只能靠超时控制或提前用 ffprobe -v quiet -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 file.mp3 快速兜底。











