背景:wma是微软推出的一种音频存储格式,由于版权问题,目前浏览器的audio/vedio标签都不支持播放wma文件,由于项目需求需要前端读取本地音频时长等相关信息传到后端,所以只能另辟蹊径。
思路:读取音频文件二进制数据,根据头文件解析相关信息
下图位通过windows media 工具来读取wma文件,进行分析
经过多个文件对比分析,得出如下结果
- 头对象由多个部分组成
- 不同文件头部大小不一样,组成部分个数也不一样
- 每个部分的排列顺序可能不一样
- 文件属性内容固定,Object ID都一样(文件时长相关信息在此)
- 前三十个字节固定,16个字节标识这个wma文件,然后8字节存储头文件大小,4个字节存储头组成部分个数,然后两个保留字节
根据上面的分析,我们只要拿到文件属性这个部分,就可以读出文件时长,文件属性内容如下:
1.截取0-16个字节,判断是否为wma文件
2.获取16-24个字节,判断wma文件头部大小
3.根据第二步获取的头部大小截取头部
4.从第30开始,取第一个头部对象的id,为前16个字节
5.判断id是否为文件属性id
6.如果不是,则取后面8个字节,获取当前头部大小
7.偏移30 + 当前头部大小,获取下一个头部组成部分
8.重复第四步
let input = document.getElementById('input')
input.addEventListener('change', e => {
let file = e.target.files[0]
file.slice(0, 16)
.arrayBuffer()
.then(buffer => {
console.log(buffer)
})
return
if (file && file instanceof Blob) {
let reader = new FileReader()
reader.onload = evt => {
let { result } = evt.target
console.log(result)
// parseFile(result)
}
reader.readAsArrayBuffer(file.slice(0, 16))
}
})
const FILE_PROPERTY_ID = [
0xa1,
0xdc,
0xab,
0x8c,
0x47,
0xa9,
0xcf,
0x11,
0x8e,
0xe4,
0x00,
0xc0,
0x0c,
0x20,
0x53,
0x65
]
const ID_BYTE_LEN = 16
const DURATION_OFFSET = 64
const START = 30
const BIT_LEN = 8
function parseFile(ab) {
let uint8 = new Uint8Array(ab)
let getFilePropertiesBytes = index => {
let start = index
let end = start + ID_BYTE_LEN
let idBytes = uint8.slice(start, end)
//id相等,则当前fram tag为文件属性
if (idBytes.toString() === FILE_PROPERTY_ID.toString()) {
start = start + DURATION_OFFSET
end = start + BIT_LEN
let durationBytes = uint8.slice(start, end)
return toNum(durationBytes)
} else {
let countBytes = uint8.slice(end, end + BIT_LEN)
let framLen = toNum(countBytes)
start = start + framLen
return getFilePropertiesBytes(start)
}
}
let duration = getFilePropertiesBytes(START)
console.log(duration)
}
function toNum(bytes) {
let total = 0
//采用的小端存储
bytes.forEach((num, i) => {
if (num > 0) {
total += num * Math.pow(2, i * BIT_LEN)
}
})
return total
}
上面为简单demo,没有去判断wma头文件。