在浏览器中播放音频流

问题描述 投票:0回答:1

我有可以传输音频数据的网络摄像机。该音频不是 MP3 或 WAV 等普通音频格式,并且无法使用 VLC 等播放器播放。设备流 ADPCM 块,每个块长度为 544 字节。每个音频块都有 32 字节的标头,应将其删除。因此,我正在研究浏览器端 Javascript 代码(我计划制作浏览器扩展),该代码消耗此流,删除标头,然后将其转换为可播放格式(如 PCM)并在浏览器中播放。

这是我在 Firefox 中成功完成一些工作的代码:

    <script>
        let form = document.forms.audioStreamSettings;
        let inputAudioStreamURL = form.elements.audioStreamURL;
        let inputAudioChunksLimit = form.elements.audioChunksLimit;
        let btnStartStream = form.elements.startStream;
        //Start to play audio when click to the button "Play audio stream:"
        btnStartStream.addEventListener('click', () => {
            let audioStreamURL = inputAudioStreamURL.value;
            let audioChunksLimit = inputAudioChunksLimit.value;
            (async () => {
                let response = await fetch(audioStreamURL);
                const reader = response.body.getReader();
                
                const audioCtx = new (window.AudioContext || window.webkitAudioContext)();

                let receivedLength = 0; // received that many bytes at the moment
                let pcmStream = new Float32Array();
                let audioDataChunks = new Uint8Array(reader.result); // array of received binary chunks (comprises the body)
                for (let step = 0; step < audioChunksLimit; step++) {
                    const {done, value} = await reader.read();
                    if (done) {
                    break;
                    }
                    let audioChunk = new Uint8Array(value);
                    console.log('Audio chunk:');
                    console.log(audioChunk);

                    //Removing header from every piece of audio data
                    adpcmRawChunk = audioChunk.slice(32,544);
                    console.log('Adpcm raw chunk:');
                    console.log(adpcmRawChunk);

                    //Device streams ADPCM audio, convert every piece to PCM
                    var pcmChunk = decodeAdpcm(adpcmRawChunk);

                    //Merge converted pieces to PCM stream
                    pcmStream = Float32Array.from([...pcmStream,...pcmChunk]);

                    receivedLength += audioChunk.length;
                    console.log(`Received ${receivedLength}`);
                }
                

                console.log("pcmStream:");
                console.log(pcmStream);

                
                    
                // Create an empty buffer at the sample rate of the AudioContext
                const audioBuffer = audioCtx.createBuffer(
                    1,
                    pcmStream.length,
                    8000
                );
                    
                // Fill the buffer with PCM stream data
                for (let channel = 0; channel < audioBuffer.numberOfChannels; channel++) {
                // This gives us the actual array that contains the data
                    const nowBuffering = audioBuffer.getChannelData(channel);
                    for (let i = 0; i < audioBuffer.length; i++) {
                        nowBuffering[i] = pcmStream[i];
                    }
                }

                // Get an AudioBufferSourceNode.
                // This is the AudioNode to use when we want to play an AudioBuffer
                const source = audioCtx.createBufferSource();

                // set the buffer in the AudioBufferSourceNode
                source.buffer = audioBuffer;

                // connect the AudioBufferSourceNode to the
                // destination so we can hear the sound
                source.connect(audioCtx.destination);

                // start the source playing
                source.start();
            })()
        })

    </script>

这段代码的问题是它捕获指定数量的块,将其保存到内存(数组)然后播放。 但我需要实时连续播放音频流。我还想要一个停止按钮来停止播放音频流。

请给我一个正确的方向,以进一步改进此代码并实现我想要的。

javascript audio-streaming
1个回答
0
投票

节省他人时间:

async function playAudioPCMStream(url: string){
    const res = await fetch(url)
    const reader = res.body!.getReader();
    const audioCtx = new AudioContext() // might need to create at user interaction on iOS
    await streamRawAudio(reader, audioCtx);
}

async function streamRawAudio(
  reader: ReadableStreamDefaultReader<Uint8Array>,
  audioCtx: AudioContext,
) {
  while (true) {
    const { done, value } = await reader.read();

    if (done) {
      break;
    }

    await playAudioData(value, audioCtx);
  }
}

async function playAudioData(
  audioData: Uint8Array,
  audioContext: AudioContext,
) {
  // https://stackoverflow.com/questions/60921018/web-audio-api-efficiently-play-a-pcm-stream
  const pcmAudio: Float32Array = new Float32Array(audioData.buffer);
  const audioBuffer = audioContext.createBuffer(1, pcmAudio.length, 24000);
  audioBuffer.copyToChannel(pcmAudio, 0);
  const source = audioContext.createBufferSource();
  source.buffer = audioBuffer;
  source.connect(audioContext.destination);
  source.start();
  await new Promise((resolve) => (source.onended = resolve));
}

© www.soinside.com 2019 - 2024. All rights reserved.