如何通过Wavesurfer.js仅展示音视频的特定时间段波形?
实现WaveSurfer.js仅展示特定时间段波形
当然可行,你现有代码的问题在于仅通过缩放和定位播放位置,并没有真正限制波形的渲染范围或视图显示范围。下面提供两种可靠的实现方式:
方式一:仅限制视图显示(不修改原始音频数据)
这种方式会加载完整的音频波形,但通过视图缩放和滚动,让容器只显示指定时间段的内容,同时禁止用户查看其他区域(适合不需要处理音频数据,仅展示特定段波形的场景)。
修改你的JS代码如下:
// 目标时间段:10秒到20秒 const startPoint = 10; const endPoint = 20; const segmentDuration = endPoint - startPoint; const container = document.getElementById('waveform'); const wavesurfer = WaveSurfer.create({ container: container, waveColor: 'blue', progressColor: 'gray', height: 100, responsive: true, interact: false, // 禁止用户交互 cursorWidth: 0, // 隐藏光标 }); const videoSrc = jQuery('#video-element').find('source').attr('src'); wavesurfer.load(videoSrc); wavesurfer.on('ready', function() { const duration = wavesurfer.getDuration(); // 计算缩放级别:让目标时间段刚好填满容器宽度 const containerWidth = container.offsetWidth; const pxPerSecond = containerWidth / segmentDuration; // 设置缩放,使目标段占满容器 wavesurfer.zoom(pxPerSecond); // 滚动到起始点的位置(计算起始点对应的像素偏移) const scrollOffset = startPoint * pxPerSecond; wavesurfer.scrollTo(scrollOffset); });
方式二:仅渲染特定时间段的波形数据(真正截取音频段)
这种方式会先截取音频的指定时间段,生成新的音频Blob,再加载给WaveSurfer,这样波形只会渲染你需要的那段(适合需要完全隔离其他音频数据的场景)。
const startPoint = 10; const endPoint = 20; const container = document.getElementById('waveform'); const wavesurfer = WaveSurfer.create({ container: container, waveColor: 'blue', progressColor: 'gray', height: 100, responsive: true, interact: false, }); // 截取音频指定时间段的函数 async function trimAudio(src, start, end) { const audioContext = new (window.AudioContext || window.webkitAudioContext)(); const response = await fetch(src); const arrayBuffer = await response.arrayBuffer(); const audioBuffer = await audioContext.decodeAudioData(arrayBuffer); // 计算起始和结束的样本位置 const startSample = start * audioBuffer.sampleRate; const endSample = end * audioBuffer.sampleRate; const durationSample = endSample - startSample; // 创建新的音频Buffer const trimmedBuffer = audioContext.createBuffer( audioBuffer.numberOfChannels, durationSample, audioBuffer.sampleRate ); // 复制目标时间段的音频数据 for (let channel = 0; channel < audioBuffer.numberOfChannels; channel++) { trimmedBuffer.copyToChannel( audioBuffer.getChannelData(channel).subarray(startSample, endSample), channel ); } // 转换为Blob const wav = audioBufferToWav(trimmedBuffer); return new Blob([wav], { type: 'audio/wav' }); } // 将AudioBuffer转换为WAV格式的函数 function audioBufferToWav(buffer) { const length = buffer.length * buffer.numberOfChannels * 2 + 44; const arrayBuffer = new ArrayBuffer(length); const view = new DataView(arrayBuffer); const channels = []; let offset = 0; let pos = 0; // 写入WAV头 const setUint16 = (data) => { view.setUint16(pos, data, true); pos += 2; }; const setUint32 = (data) => { view.setUint32(pos, data, true); pos += 4; }; setUint32(0x46464952); // "RIFF" setUint32(length - 8); // 文件长度 setUint32(0x45564157); // "WAVE" setUint32(0x20746d66); // "fmt " setUint32(16); // PCM格式大小 setUint16(1); // PCM格式 setUint16(buffer.numberOfChannels); setUint32(buffer.sampleRate); setUint32(buffer.sampleRate * 2 * buffer.numberOfChannels); // 比特率 setUint16(buffer.numberOfChannels * 2); // 块对齐 setUint16(16); // 比特深度 setUint32(0x61746164); // "data" setUint32(length - pos - 4); // 数据长度 // 写入音频数据 for (let i = 0; i < buffer.numberOfChannels; i++) { channels.push(buffer.getChannelData(i)); } while (pos < length) { for (let i = 0; i < buffer.numberOfChannels; i++) { let sample = Math.max(-1, Math.min(1, channels[i][offset])); sample = sample < 0 ? sample * 0x8000 : sample * 0x7FFF; view.setInt16(pos, sample, true); pos += 2; } offset++; } return arrayBuffer; } // 加载并渲染截取后的音频波形 (async function() { const videoSrc = jQuery('#video-element').find('source').attr('src'); const trimmedBlob = await trimAudio(videoSrc, startPoint, endPoint); wavesurfer.loadBlob(trimmedBlob); })();
说明
- 方式一实现简单,适合快速展示特定段视图,但底层还是加载了完整音频数据。
- 方式二更彻底,仅加载目标时间段的音频,生成的波形也只包含该段,但需要处理音频格式转换,代码稍复杂。
内容的提问来源于stack exchange,提问作者CRAIG
相关产品推荐
相关产品推荐

