如何解析WMA(ASF)格式音乐文件时长以用于初代Xbox音轨描述文件生成
提问:跨平台无依赖获取WMA音频时长方案
我正在开发一款适配初代Xbox音乐菜单的文件生成工具,该工具仅读取WMA格式的音乐文件,基于这些歌曲生成音轨描述文件ST.DB以及对应的文件系统。
目前我需要在ST.DB的对应字段中存储每首歌曲的时长,因此想要编写一个小型工具专门解析WMA文件的音乐时长。我在网上查找了WMA格式的相关说明和文档,目前能找到的方案大多需要调用Windows SDK,对我的需求来说太过冗余,且我的工具需要支持跨平台,无法使用Windows SDK相关接口。
我也尝试过使用ffmpeg/ffprobe实现该需求,但存在两处缺陷:一是二者的输出需要额外二次解析,不够便捷;二是我不希望工具依赖外部程序。
恳请各位提供相关的WMA格式文档或者示例代码,非常感谢!
回答
WMA格式核心逻辑说明
WMA属于ASF容器格式的子类,音频时长不需要解码音频流,仅需读取文件头部的固定字段即可获取,完全不需要依赖系统SDK或者外部程序。
ASF文件头部以固定GUID开头,遍历头部块找到File Properties Object块后,读取其中的Play Duration字段即可,该字段为64位整数,单位是100纳秒,除以10^7即可得到秒级时长,精度完全满足初代Xbox ST.DB的存储要求。
无依赖示例代码(C语言,全平台可用)
#include <stdio.h> #include <stdint.h> #include <string.h> // File Properties Object 固定GUID,小端存储 static const uint8_t wma_fp_guid[16] = {0xA1, 0xDC, 0xAB, 0x8C, 0x47, 0xA9, 0xCF, 0x11, 0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}; double get_wma_duration(const char* file_path) { FILE* fp = fopen(file_path, "rb"); if (!fp) return -1.0; uint8_t guid_buf[16]; uint64_t obj_size; uint64_t header_remain; // 跳过ASF头部对象的固定GUID段 if (fread(guid_buf, 1, 16, fp) != 16) goto err; // 读取头部总大小 if (fread(&obj_size, 8, 1, fp) != 1) goto err; // 跳过头部对象的计数和保留字段 if (fseek(fp, 5, SEEK_CUR) != 0) goto err; header_remain = obj_size - 29; // 已读取16+8+5=29字节 while (header_remain > 24) { // 每个对象至少有16字节GUID + 8字节大小共24字节 if (fread(guid_buf, 1, 16, fp) != 16) goto err; if (fread(&obj_size, 8, 1, fp) != 1) goto err; header_remain -= 24; if (memcmp(guid_buf, wma_fp_guid, 16) == 0) { uint64_t play_duration; // 跳过File ID(16字节) + File Size(8字节) if (fseek(fp, 24, SEEK_CUR) != 0) goto err; if (fread(&play_duration, 8, 1, fp) != 1) goto err; fclose(fp); // 100纳秒转换为秒 return (double)play_duration / 10000000.0; } // 跳转到下一个对象 if (obj_size < 24 || header_remain < obj_size -24) goto err; if (fseek(fp, obj_size -24, SEEK_CUR) != 0) goto err; header_remain -= (obj_size -24); } err: fclose(fp); return -1.0; }
你可以直接把这段代码嵌入到你的工具中,仅需读取文件头部几十到几百字节即可拿到时长,不需要解析整个文件,也没有任何第三方依赖。
使用注意点
- 所有ASF结构的数值均为小端存储,x86、ARM等主流架构都是小端,可直接使用;如果需要适配大端架构,自行添加字节序转换逻辑即可
- 函数解析失败返回-1.0,你可以根据需要添加对应的错误处理逻辑
- 你可以根据ST.DB的字段要求,把返回的秒级时长转换为毫秒、帧或者Xbox要求的任意单位存储
内容的提问来源于stack exchange,提问作者Aureal
相关产品推荐
相关产品推荐

