我正在制作 Web 应用程序,它存储从大型视频文件中剪切下来的短音频文件。用户上传 .mp4 文件,选择声音长度,这里有一个小技巧。剪切音频只能在后端完成(如果我错了,请纠正我)并且发送 700MB 数据不是一个好的选择,所以我使用下面的代码从 .mp4 解码音频数据,然后使用 start 和 stop 参数发送它。后端(Node.js)使用 FFMPEG 剪切音频并保存。
这部分有效,但我意识到来自 60FPS 视频的解码音频听起来不太好(不可怕但在我的应用程序中完全没用)。我的目标是避免第三方,尤其是桌面应用程序(如 audacity),并允许用户从任何 mp4 视频中剪切相关的音频部分。有没有办法在浏览器中将 60FPS 视频转换为 30FPS 视频(ArrayBuffer)然后解码音频?
fileInput.onchange = event => {
this.file = event.target["files"][0];
//.mp4 file
this.fileURL = URL.createObjectURL(this.file)
let baseAudioContext = new AudioContext();
this.file.arrayBuffer().then(buff => {
baseAudioContext.decodeAudioData(buff,
success => {
console.log(success)
this.bufferToWave(success, 0, success.length);
},
err => console.log(err));
})
}
bufferToWave(abuffer, offset, len) {
var numOfChan = abuffer.numberOfChannels,
length = len * numOfChan * 2 + 44,
buffer = new ArrayBuffer(length),
view = new DataView(buffer),
channels = [], i, sample,
pos = 0;
// write WAVE header
setUint32(0x46464952); // "RIFF"
setUint32(length - 8); // file length - 8
setUint32(0x45564157); // "WAVE"
setUint32(0x20746d66); // "fmt " chunk
setUint32(16); // length = 16
setUint16(1); // PCM (uncompressed)
setUint16(numOfChan);
setUint32(abuffer.sampleRate);
setUint32(abuffer.sampleRate * 2 * numOfChan); // avg. bytes/sec
setUint16(numOfChan * 2); // block-align
setUint16(16); // 16-bit (hardcoded in this demo)
setUint32(0x61746164); // "data" - chunk
setUint32(length - pos - 4); // chunk length
// write interleaved data
for (i = 0; i < abuffer.numberOfChannels; i++)
channels.push(abuffer.getChannelData(i));
while (pos < length) {
for (i = 0; i < numOfChan; i++) { // interleave channels
sample = Math.max(-1, Math.min(1, channels[i][offset])); // clamp
sample = (0.5 + sample < 0 ? sample * 32768 : sample * 32767) | 0; // scale to 16-bit signed int
view.setInt16(pos, sample, true); // update data chunk
pos += 2;
}
offset++ // next source sample
}
// create Blob
//return (URL || webkitURL).createObjectURL(new Blob([buffer], { type: "audio/wav" }));
var u = (URL || webkitURL).createObjectURL(new Blob([buffer], { type: "audio/wav" }));
//temporary part
//downloading file to check quality
//in this part sound is already broken, no need to show backend code
const a = document.createElement('a');
a.style.display = 'none';
a.href = u;
a.download = name;
document.body.appendChild(a);
a.click();
function setUint16(data) {
view.setUint16(pos, data, true);
pos += 2;
}
function setUint32(data) {
view.setUint32(pos, data, true);
pos += 4;
}
}