需要
还是上个学校的我的项目,须要实时播放音频。
拾音器有一个rtsp的地址,而后去找了很多插件,前端能够间接解析用的,尝试了很多办法,ff什么的都没有实现,可能是没有找对办法,我感觉再这么上来必定不能按期交付了,所以和后盾沟通了一下,还是通过websocket传输二进制,前端拿到后再解析,而后去播放。
ok,有思路了就开始实现。
实现步骤
- websocket连贯,这里websocket不做过多解释,具体可看上篇文章。
// 因为需要是须要点击之后开始播放,所以须要在useEffect中写,而是在点击事件里。 const playAudio = async (data: any) => { const ws2 = new WebSocket(url2); setAudioSocket(() => ws2) }
- 定义相干回调函数
useEffect(() => { if (audioSocket) { audioSocket.binaryType = 'blob'; audioSocket.onopen = () => { console.log('音频链接上拉!!') }; audioSocket.onmessage = (data: any) => audioAcceptMessage(data); audioSocket.onclose = () => audioWebSocketOnClose(); audioSocket.onerror = () => audioWebSocketOnError(); return () => { audioSocket.close(); receivedBlobs = [] } } }, [audioSocket])
3.解决音频数据,过后有尝试过各种缓存的api办法,然而都不现实。所以尝试了其余形式
// 该链接后盾返回的是blob对象,而且是不间断的始终传送,每次解析进去的音频只有0.05秒左右,所以须要思考做累加。const audioAcceptMessage = async ({ data }: any) => { if (data) { console.log(data, '数据失常传输中') // receivedBlobs为全局定义的数组,用来存储后盾的数据 receivedBlobs.push(data) } else { // 无接收数据 } }
4.实现形式1,手动缓存,并解决播放。
const renderAudio = async () => { if (receivedBlobs.length === 0) return false; // 如果数组的缓存过小,播放工夫会太短,所以这里做了2秒提早。 // 注:后盾是不间断的始终传输二进制数据的。 if (receivedBlobs.length < 10) { setAudioSrcLoading(true); setTimeout(() => { renderAudio(); }, 2000) return false; } const blob = new Blob([...receivedBlobs], { type: 'audio/mp3' }); receivedBlobs = []; // 这个url曾经能够被audio标签间接辨认播放了 const url = URL.createObjectURL(blob); setAudioSrc(url) // 以下步骤次要为了获取到这段音频的播放时长,以供触发下一个定时器 const temporaryAudio = new Audio(); temporaryAudio.src = url; temporaryAudio.addEventListener('loadedmetadata', () => { let time = temporaryAudio.duration * 1000; audioTimeout = setTimeout(() => { renderAudio(); }, time) }); }
<audio src={audioSrc} autoPlay></audio>
毛病:每播放几秒钟,就会缓冲一下(数据处理工夫和赋值url工夫)。好吧,体验不过关。然而的确实现了。
5.实现形式2:想到了一个偏方,如果1个播放器会卡顿,那两个呢,两个做连接播放呢,有想法了,开干。
// 实时音频播放地址const [audioSrc, setAudioSrc] = useState<any>();// 实时音频播放地址2const [audioSrc2, setAudioSrc2] = useState<any>();// 实时音频是否缓冲const [audioSrcLoading, setAudioSrcLoading] = useState<boolean>(false);const renderAudio = async () => { if (receivedBlobs.length === 0) return false; if (receivedBlobs.length < 10) { setAudioSrcLoading(true); setTimeout(() => { renderAudio(); }, 2000) return false; } const blob = new Blob([...receivedBlobs], { type: 'audio/mp3' }); receivedBlobs = []; const url = URL.createObjectURL(blob); // 这里用开关辨别,下一次赋值给哪一个url isAudioSrcFlag ? setAudioSrc(url) : setAudioSrc2(url); isAudioSrcFlag = !isAudioSrcFlag; const temporaryAudio = new Audio(); temporaryAudio.src = url; temporaryAudio.addEventListener('loadedmetadata', () => { let time = temporaryAudio.duration * 1000; // 这里在下一段完结之前 提前播放,如果不提前播放,那还是会和第一种形式一样,没有扭转。 const connectTime = 200; // 连接播放提前时长 time = time > connectTime ? time - connectTime : time; audioTimeout = setTimeout(() => { renderAudio(); }, time) console.log(`---------此段音频时长为:${temporaryAudio.duration} 秒,下段${time / 1000} 秒后播放}`); }); }
<audio src={audioSrc} autoPlay></audio><audio src={audioSrc2} autoPlay></audio>
稍微革新,性能实现了,嗯.... 还是有个问题,因为这200毫秒是通过测试当前,最不卡顿的临界值,所以每个下一次的播放都会提前,也就会导致缓存数据越来越少,在通过一段时间之后,还是须要缓冲一下,能力持续播放,还是没达到现实状态。好吧,看来这种形式不太能达到本人想要的。
那就持续优化!
6.实现形式3:这次采纳了api播放音频,不必audio标签了。
const renderAudio = async () => { if (receivedBlobs.length === 0) return false; if (receivedBlobs.length < 10) { setAudioSrcLoading(true); setTimeout(() => { renderAudio(); }, 2000) return false; } const fileReader = new FileReader() fileReader.onload = () => { const arrayBuffer = fileReader.result as ArrayBuffer; audioContext.decodeAudioData(arrayBuffer, (audioBuffer) => { const source = audioContext.createBufferSource(); source.buffer = audioBuffer; source.connect(audioContext.destination); source.start(); }); } setAudioSrcLoading(false); const blob = new Blob([...receivedBlobs], { type: 'audio/mp3' }); fileReader.readAsArrayBuffer(blob) receivedBlobs = []; const url = URL.createObjectURL(blob); const temporaryAudio = new Audio(); temporaryAudio.src = url; temporaryAudio.addEventListener('loadedmetadata', () => { let time = temporaryAudio.duration * 1000; // 采纳这种形式做连接播放,50毫秒就能够做到不卡顿,应该是省去了audio标签解决的工夫 const connectTime = 50; // 连接播放提前时长 time = time > connectTime ? time - connectTime : time; audioTimeout = setTimeout(() => { renderAudio(); }, time) console.log(`---------此段音频时长为:${temporaryAudio.duration} 秒,下段${time / 1000} 秒后播放}`); }); }
论断:性能实现,尽管也须要缓冲,然而做到了大家都能承受的范畴,很长很长一段时间后才会缓冲一次,缓冲的距离很长,所以也还算胜利吧。
over!
心愿能帮忙到你们。如果大家有更好的方法,能够交换,或者斧正。