守听 发表于 2024-9-24 11:50:55

sourceBuffer failed to execute appendBuffer on sourceBuffer this sourc

sourceBuffer failed to execute appendBuffer on sourceBuffer this sourcebuffer is still processing an appendbugger or remove operation, 这个报错
这个错误通常是由于在 SourceBuffer 还在处理前一个 appendBuffer 或 remove 操作时,又尝试执行另一个 appendBuffer 操作。这意味着在 appendBuffer 调用之间没有正确处理并等候前一个操作完成。
要办理这个问题,可以利用一个队列来存储要追加的数据块,并在前一个操作完成后再执行下一个操作。这是通过监听 sourceBuffer 的 updateend 事件来实现的。
以下是一个更详细的示例,确保在一个操作完成后再举行下一个操作:
完备的前端代码示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Audio Stream with MediaSource</title>
</head>
<body>
    <audio id="audioElement" controls></audio>
    <script>
      if ('MediaSource' in window) {
            var audio = document.getElementById('audioElement');
            var mediaSource = new MediaSource();
            audio.src = URL.createObjectURL(mediaSource);

            mediaSource.addEventListener('sourceopen', function () {
                var sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');

                // 处理数据块追加的竞争问题
                let queue = [];
                let isAppending = false;

                function appendNextChunk() {
                  if (queue.length > 0 && !isAppending) {
                        isAppending = true;
                        sourceBuffer.appendBuffer(queue.shift());
                  }
                }

                sourceBuffer.addEventListener('updateend', function () {
                  isAppending = false;
                  appendNextChunk();
                });

                sourceBuffer.addEventListener('error', function (e) {
                  console.error('SourceBuffer error:', e);
                });

                fetchAudioData();
            });

            function fetchAudioData() {
                fetch('/audio') // 替换为你的音频流URL
                  .then(response => response.body.getReader())
                  .then(reader => {
                        function read() {
                            reader.read().then(({ done, value }) => {
                              if (done) {
                                    mediaSource.endOfStream();
                                    return;
                              }
                              queue.push(value);
                              appendNextChunk();
                              read();
                            });
                        }
                        read();
                  }).catch(error => {
                        console.error('Fetching audio data failed:', error);
                  });
            }
      } else {
            console.log('The MediaSource API is not supported in this browser.');
      }
    </script>
</body>
</html>
后端模拟(Java Servlet)

保持与之前的示例雷同的后端代码,模拟每秒钟返回1KB数据:
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;

@WebServlet("/audio")
public class SlowAudioStreamServlet extends HttpServlet {

    private static final int CHUNK_SIZE = 1024; // 1 KB
    private static final int HEADER_SIZE = 128; // 假设头信息大小为128字节

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      resp.setContentType("audio/mpeg");
      OutputStream out = resp.getOutputStream();

      // 构建假头信息
      byte[] v3Header = createFakeV3Header();
      out.write(v3Header);
      out.flush();

      // 模拟每秒返回1KB数据
      try {
            while (true) {
                byte[] chunk = createFakeAudioData(CHUNK_SIZE);
                out.write(chunk);
                out.flush();
                TimeUnit.SECONDS.sleep(1); // 每秒发送1KB数据
            }
      } catch (InterruptedException e) {
            e.printStackTrace();
      }
    }

    private byte[] createFakeV3Header() {
      byte[] header = new byte;
      header = 'V';
      header = '3';
      header = 'H';
      header = 'D';
      for (int i = 4; i < HEADER_SIZE; i++) {
            header = 0;
      }
      return header;
    }

    private byte[] createFakeAudioData(int size) {
      byte[] audioData = new byte;
      for (int i = 0; i < size; i++) {
            audioData = (byte) ((Math.sin(2 * Math.PI * i / 44100) * 127) + 128); // 生成模拟音频数据
      }
      return audioData;
    }
}
解释


[*] 创建 MediaSource 和 SourceBuffer:在 sourceopen 事件中创建 SourceBuffer。
[*] 队列和标志位:利用一个队列(queue)存储须要追加的数据块和一个标志位(isAppending)来表现当前是否有数据正在追加。
[*] 追加数据块:通过 appendNextChunk 函数查抄队列并追加数据块。假如 SourceBuffer 正在处理另一个操作,则数据块将被添加到队列中,等候当前操作完成后再追加。
[*] 监听 updateend 事件:在 SourceBuffer 完成当前操作时触发 updateend 事件,清除标志位并调用 appendNextChunk 以继续处理队列中的下一个数据块。
[*] 处理错误:添加对 error 事件的监听,以捕获并记录 SourceBuffer 错误。
[*] 数据读取和追加:通过 fetch 和 ReadableStream API 从服务器获取音频流数据,并将其逐块添加到队列中举行处理。
如许可以确保 appendBuffer 操作之间不会发生竞争,从而制止 sourceBuffer 错误。

再查抄一下前端代码是否正确,说fetchAudioData 找不到

我查抄并修正了一些可能导致 fetchAudioData 函数找不到的问题,确保代码正确并且函数定义和调用正确。下面是更新后的完备前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Audio Stream with MediaSource</title>
</head>
<body>
    <audio id="audioElement" controls></audio>
    <script>
      if ('MediaSource' in window) {
            var audio = document.getElementById('audioElement');
            var mediaSource = new MediaSource();
            audio.src = URL.createObjectURL(mediaSource);

            mediaSource.addEventListener('sourceopen', function () {
                var sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');

                // 处理数据块追加的竞争问题
                let queue = [];
                let isAppending = false;

                function appendNextChunk() {
                  if (queue.length > 0 && !isAppending) {
                        isAppending = true;
                        sourceBuffer.appendBuffer(queue.shift());
                  }
                }

                sourceBuffer.addEventListener('updateend', function () {
                  isAppending = false;
                  appendNextChunk();
                });

                sourceBuffer.addEventListener('error', function (e) {
                  console.error('SourceBuffer error:', e);
                });

                fetchAudioData();
            });

            function fetchAudioData() {
                fetch('/audio') // 替换为你的音频流URL
                  .then(response => response.body.getReader())
                  .then(reader => {
                        function read() {
                            reader.read().then(({ done, value }) => {
                              if (done) {
                                    mediaSource.endOfStream();
                                    return;
                              }
                              queue.push(value);
                              appendNextChunk();
                              read();
                            });
                        }
                        read();
                  }).catch(error => {
                        console.error('Fetching audio data failed:', error);
                  });
            }
      } else {
            console.log('The MediaSource API is not supported in this browser.');
      }
    </script>
</body>
</html>
解释


[*] 确保 fetchAudioData 函数在 sourceopen 事件处理步伐中正确调用:

[*]当 MediaSource 打开时,事件处理步伐调用 fetchAudioData。

[*] 定义 fetchAudioData 函数:

[*]在 fetchAudioData 函数中,利用 fetch API 获取音频数据流。
[*]利用 ReadableStream 的 getReader 方法逐块读取数据,并将每块数据添加到队列中。

[*] 定义 appendNextChunk 函数:

[*]appendNextChunk 函数负责从队列中获取下一个数据块并追加到 sourceBuffer。
[*]利用 isAppending 标志位防止在当前操作完成之前开始新的操作。

[*] 监听 updateend 事件:

[*]sourceBuffer 的 updateend 事件触发时,表现当前操作完成,清除标志位并调用 appendNextChunk 处理下一个数据块。

[*] 监听 error 事件:

[*]处理和记录 sourceBuffer 错误。

后端模拟(Java Servlet)

保持与之前的示例雷同的后端代码,用于模拟每秒钟返回1KB数据:
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;

@WebServlet("/audio")
public class SlowAudioStreamServlet extends HttpServlet {

    private static final int CHUNK_SIZE = 1024; // 1 KB
    private static final int HEADER_SIZE = 128; // 假设头信息大小为128字节

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      resp.setContentType("audio/mpeg");
      OutputStream out = resp.getOutputStream();

      // 构建假头信息
      byte[] v3Header = createFakeV3Header();
      out.write(v3Header);
      out.flush();

      // 模拟每秒返回1KB数据
      try {
            while (true) {
                byte[] chunk = createFakeAudioData(CHUNK_SIZE);
                out.write(chunk);
                out.flush();
                TimeUnit.SECONDS.sleep(1); // 每秒发送1KB数据
            }
      } catch (InterruptedException e) {
            e.printStackTrace();
      }
    }

    private byte[] createFakeV3Header() {
      byte[] header = new byte;
      header = 'V';
      header = '3';
      header = 'H';
      header = 'D';
      for (int i = 4; i < HEADER_SIZE; i++) {
            header = 0;
      }
      return header;
    }

    private byte[] createFakeAudioData(int size) {
      byte[] audioData = new byte;
      for (int i = 0; i < size; i++) {
            audioData = (byte) ((Math.sin(2 * Math.PI * i / 44100) * 127) + 128); // 生成模拟音频数据
      }
      return audioData;
    }
}
请确保后端代码正确部署并运行,以提供所需的音频数据流。这将确保前端代码能够正确获取并处理音频数据,制止 appendBuffer 竞争问题。

Uncaught (in promise) ReferenceError: queue is not defined 报错,前端

对,之前的代码里,queue 和 isAppending 的作用域不正确,导致在某些情况下找不到这些变量。我们须要确保这些变量在得当的作用域内声明。以下是修正后的前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Audio Stream with MediaSource</title>
</head>
<body>
    <audio id="audioElement" controls></audio>
    <script>
      if ('MediaSource' in window) {
            var audio = document.getElementById('audioElement');
            var mediaSource = new MediaSource();
            audio.src = URL.createObjectURL(mediaSource);

            mediaSource.addEventListener('sourceopen', function () {
                var sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');

                // 处理数据块追加的竞争问题
                var queue = [];
                var isAppending = false;

                function appendNextChunk() {
                  if (queue.length > 0 && !isAppending) {
                        isAppending = true;
                        sourceBuffer.appendBuffer(queue.shift());
                  }
                }

                sourceBuffer.addEventListener('updateend', function () {
                  isAppending = false;
                  appendNextChunk();
                });

                sourceBuffer.addEventListener('error', function (e) {
                  console.error('SourceBuffer error:', e);
                });

                fetchAudioData(queue, appendNextChunk);
            });

            function fetchAudioData(queue, appendNextChunk) {
                fetch('/audio') // 替换为你的音频流URL
                  .then(response => response.body.getReader())
                  .then(reader => {
                        function read() {
                            reader.read().then(({ done, value }) => {
                              if (done) {
                                    mediaSource.endOfStream();
                                    return;
                              }
                              queue.push(value);
                              appendNextChunk();
                              read();
                            });
                        }
                        read();
                  }).catch(error => {
                        console.error('Fetching audio data failed:', error);
                  });
            }
      } else {
            console.log('The MediaSource API is not supported in this browser.');
      }
    </script>
</body>
</html>
解释


[*] 变量声明作用域:确保 queue 和 isAppending 在 sourceopen 事件处理步伐的作用域中声明,如许它们可以在 appendNextChunk 和 fetchAudioData 中正确引用。
[*] 参数传递:将 queue 和 appendNextChunk 作为参数传递给 fetchAudioData 函数,以便在该函数内部可以访问它们。
[*] 错误处理:添加对 fetch 和 SourceBuffer 的错误处理,以捕获并记录可能的错误。
[*] 重复读取数据:通过 read 函数不断从流中读取数据块,并将它们添加到队列中。
这种方式确保了变量的正确引用,并且能够制止 appendBuffer 操作之间的竞争。请确保后端代码正确部署并运行,以提供所需的音频数据流。

哀求完毕后,前端点了播放,没有效果啊,而且哀求的路径返回也是有数据的
为了确保前端代码能够正确处理音频流,并确保音频数据被成功附加到 SourceBuffer,以下是一些步调和改进的前端代码示例,以确保音频数据能够正确播放:

[*]确认音频格式:确保服务器返回的音频数据格式与 SourceBuffer 支持的格式一致。
[*]查抄数据类型:在 fetch 哀求中查抄返回的 MIME 类型是否正确。
以下是改进的前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Audio Stream with MediaSource</title>
</head>
<body>
    <audio id="audioElement" controls></audio>
    <script>
      if ('MediaSource' in window) {
            var audio = document.getElementById('audioElement');
            var mediaSource = new MediaSource();
            audio.src = URL.createObjectURL(mediaSource);

            mediaSource.addEventListener('sourceopen', function () {
                var sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');

                // 处理数据块追加的竞争问题
                var queue = [];
                var isAppending = false;

                function appendNextChunk() {
                  if (queue.length > 0 && !isAppending && !sourceBuffer.updating) {
                        isAppending = true;
                        sourceBuffer.appendBuffer(queue.shift());
                  }
                }

                sourceBuffer.addEventListener('updateend', function () {
                  isAppending = false;
                  appendNextChunk();
                });

                sourceBuffer.addEventListener('error', function (e) {
                  console.error('SourceBuffer error:', e);
                });

                fetchAudioData();
            });

            function fetchAudioData() {
                fetch('/audio') // 替换为你的音频流URL
                  .then(response => {
                        if (!response.ok) {
                            throw new Error('Network response was not ok');
                        }
                        return response.body.getReader();
                  })
                  .then(reader => {
                        function read() {
                            reader.read().then(({ done, value }) => {
                              if (done) {
                                    mediaSource.endOfStream();
                                    return;
                              }
                              queue.push(value);
                              appendNextChunk();
                              read();
                            }).catch(error => {
                              console.error('Reader read error:', error);
                            });
                        }
                        read();
                  }).catch(error => {
                        console.error('Fetching audio data failed:', error);
                  });
            }
      } else {
            console.log('The MediaSource API is not supported in this browser.');
      }
    </script>
</body>
</html>
进一步查抄


[*]音频流URL:确保 /audio URL 返回的是有效的音频数据,且 MIME 类型为 audio/mpeg。
[*]查抄控制台日记:查看浏览器控制台日记,确认是否有其他错误信息。
后端模拟(Java Servlet)

确保后端代码提供有效的音频流数据。这里是一个简化的示例:
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;

@WebServlet("/audio")
public class SlowAudioStreamServlet extends HttpServlet {

    private static final int CHUNK_SIZE = 1024; // 1 KB
    private static final int HEADER_SIZE = 128; // 假设头信息大小为128字节

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      resp.setContentType("audio/mpeg");
      OutputStream out = resp.getOutputStream();

      // 构建假头信息
      byte[] v3Header = createFakeV3Header();
      out.write(v3Header);
      out.flush();

      // 模拟每秒返回1KB数据
      try {
            while (true) {
                byte[] chunk = createFakeAudioData(CHUNK_SIZE);
                out.write(chunk);
                out.flush();
                TimeUnit.SECONDS.sleep(1); // 每秒发送1KB数据
            }
      } catch (InterruptedException e) {
            e.printStackTrace();
      }
    }

    private byte[] createFakeV3Header() {
      byte[] header = new byte;
      header = 'V';
      header = '3';
      header = 'H';
      header = 'D';
      for (int i = 4; i < HEADER_SIZE; i++) {
            header = 0;
      }
      return header;
    }

    private byte[] createFakeAudioData(int size) {
      byte[] audioData = new byte;
      for (int i = 0; i < size; i++) {
            audioData = (byte) ((Math.sin(2 * Math.PI * i / 44100) * 127) + 128); // 生成模拟音频数据
      }
      return audioData;
    }
}
请确保后端返回的数据能够被浏览器正确识别并播放。假如仍然无法播放,请查抄网络哀求的相应头、相应状态以及浏览器控制台中的详细错误信息。

那么我怎样在流轻微到达一定水平哪怕2s ,前端播放器就能播放的效果呢,
为了实现前端播放器在流到达一定水平(例如2秒)后就能开始播放的效果,可以通过以下步调举行优化:

[*]得当的缓冲大小:确保在前端接收到足够的数据块(例如2秒的音频数据)后再开始播放。
[*]利用得当的事件监听:通过事件监听器来确保在接收到足够的数据后立刻开始播放。
以下是更新的前端代码示例,确保在接收到2秒的音频数据后开始播放:
前端代码

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Audio Stream with MediaSource</title>
</head>
<body>
    <audio id="audioElement" controls></audio>
    <script>
      if ('MediaSource' in window) {
            var audio = document.getElementById('audioElement');
            var mediaSource = new MediaSource();
            audio.src = URL.createObjectURL(mediaSource);

            mediaSource.addEventListener('sourceopen', function () {
                var sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');

                // 处理数据块追加的竞争问题
                var queue = [];
                var isAppending = false;
                var totalBufferedDuration = 0; // 总缓冲的时长
                var requiredBufferDuration = 2; // 需要的缓冲时长(秒)

                function appendNextChunk() {
                  if (queue.length > 0 && !isAppending && !sourceBuffer.updating) {
                        isAppending = true;
                        sourceBuffer.appendBuffer(queue.shift());
                  }
                }

                sourceBuffer.addEventListener('updateend', function () {
                  isAppending = false;
                  totalBufferedDuration = sourceBuffer.buffered.end(0); // 更新总缓冲的时长

                  if (totalBufferedDuration >= requiredBufferDuration && audio.paused) {
                        audio.play(); // 在达到所需缓冲时长后开始播放
                  }
                  appendNextChunk();
                });

                sourceBuffer.addEventListener('error', function (e) {
                  console.error('SourceBuffer error:', e);
                });

                fetchAudioData();
            });

            function fetchAudioData() {
                fetch('/audio') // 替换为你的音频流URL
                  .then(response => {
                        if (!response.ok) {
                            throw new Error('Network response was not ok');
                        }
                        return response.body.getReader();
                  })
                  .then(reader => {
                        function read() {
                            reader.read().then(({ done, value }) => {
                              if (done) {
                                    mediaSource.endOfStream();
                                    return;
                              }
                              queue.push(value);
                              appendNextChunk();
                              read();
                            }).catch(error => {
                              console.error('Reader read error:', error);
                            });
                        }
                        read();
                  }).catch(error => {
                        console.error('Fetching audio data failed:', error);
                  });
            }
      } else {
            console.log('The MediaSource API is not supported in this browser.');
      }
    </script>
</body>
</html>
解释


[*] 总缓冲时长计算:

[*]利用 sourceBuffer.buffered.end(0) 获取当前缓冲区的竣事时间。
[*]在 updateend 事件触发时更新 totalBufferedDuration。

[*] 查抄缓冲时长并开始播放:

[*]当 totalBufferedDuration 大于或等于所需的缓冲时长(例如2秒)时,查抄 audio 是否处于暂停状态。
[*]假如处于暂停状态且到达所需缓冲时长,调用 audio.play() 开始播放。

[*] 事件监听和错误处理:

[*]监听 sourceopen、updateend 和 error 事件,确保在得当的时机执行相应的操作,并处理可能的错误。

后端模拟(Java Servlet)

保持与之前的示例雷同的后端代码,用于模拟每秒钟返回1KB数据:
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;

@WebServlet("/audio")
public class SlowAudioStreamServlet extends HttpServlet {

    private static final int CHUNK_SIZE = 1024; // 1 KB
    private static final int HEADER_SIZE = 128; // 假设头信息大小为128字节

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      resp.setContentType("audio/mpeg");
      OutputStream out = resp.getOutputStream();

      // 构建假头信息
      byte[] v3Header = createFakeV3Header();
      out.write(v3Header);
      out.flush();

      // 模拟每秒返回1KB数据
      try {
            while (true) {
                byte[] chunk = createFakeAudioData(CHUNK_SIZE);
                out.write(chunk);
                out.flush();
                TimeUnit.SECONDS.sleep(1); // 每秒发送1KB数据
            }
      } catch (InterruptedException e) {
            e.printStackTrace();
      }
    }

    private byte[] createFakeV3Header() {
      byte[] header = new byte;
      header = 'V';
      header = '3';
      header = 'H';
      header = 'D';
      for (int i = 4; i < HEADER_SIZE; i++) {
            header = 0;
      }
      return header;
    }

    private byte[] createFakeAudioData(int size) {
      byte[] audioData = new byte;
      for (int i = 0; i < size; i++) {
            audioData = (byte) ((Math.sin(2 * Math.PI * i / 44100) * 127) + 128); // 生成模拟音频数据
      }
      return audioData;
    }
}
如许,前端代码会在音频缓冲到达所需的时长(例如2秒)后自动开始播放音频,确保用户体验的平滑性。

你上面的代码 点击播放 没有任何效果,我直接一个audio src标签的时候是确定能播放的

为了确保播放器能够在接收到足够的数据后开始播放,我们须要确保以下几点:

[*]正确的MIME类型:服务器返回的音频数据必须与SourceBuffer支持的MIME类型匹配。
[*]足够的缓冲:确保在appendBuffer中没有数据竞争,足够的数据被添加到SourceBuffer。
让我们详细调试一下代码,并举行得当的调整,以确保播放器能够正常工作:
前端代码

确保前端代码能够处理缓冲区更新并在足够的数据被缓冲后播放音频:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Audio Stream with MediaSource</title>
</head>
<body>
    <audio id="audioElement" controls></audio>
    <script>
      if ('MediaSource' in window) {
            var audio = document.getElementById('audioElement');
            var mediaSource = new MediaSource();
            audio.src = URL.createObjectURL(mediaSource);

            mediaSource.addEventListener('sourceopen', function () {
                var sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');

                var queue = [];
                var isAppending = false;
                var totalBufferedDuration = 0;
                var requiredBufferDuration = 2;

                function appendNextChunk() {
                  if (queue.length > 0 && !isAppending && !sourceBuffer.updating) {
                        isAppending = true;
                        sourceBuffer.appendBuffer(queue.shift());
                  }
                }

                sourceBuffer.addEventListener('updateend', function () {
                  isAppending = false;
                  if (sourceBuffer.buffered.length > 0) {
                        totalBufferedDuration = sourceBuffer.buffered.end(0);

                        if (totalBufferedDuration >= requiredBufferDuration && audio.paused) {
                            audio.play();
                        }
                  }
                  appendNextChunk();
                });

                sourceBuffer.addEventListener('error', function (e) {
                  console.error('SourceBuffer error:', e);
                });

                fetchAudioData();
            });

            function fetchAudioData() {
                fetch('/audio')
                  .then(response => {
                        if (!response.ok) {
                            throw new Error('Network response was not ok');
                        }
                        return response.body.getReader();
                  })
                  .then(reader => {
                        function read() {
                            reader.read().then(({ done, value }) => {
                              if (done) {
                                    mediaSource.endOfStream();
                                    return;
                              }
                              queue.push(value);
                              appendNextChunk();
                              read();
                            }).catch(error => {
                              console.error('Reader read error:', error);
                            });
                        }
                        read();
                  }).catch(error => {
                        console.error('Fetching audio data failed:', error);
                  });
            }
      } else {
            console.log('The MediaSource API is not supported in this browser.');
      }
    </script>
</body>
</html>
后端代码 确保后端正确返回音频数据:
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;

@WebServlet("/audio")
public class SlowAudioStreamServlet extends HttpServlet {

    private static final int CHUNK_SIZE = 1024; // 1 KB
    private static final int HEADER_SIZE = 128; // 假设头信息大小为128字节

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      resp.setContentType("audio/mpeg");
      OutputStream out = resp.getOutputStream();

      byte[] v3Header = createFakeV3Header();
      out.write(v3Header);
      out.flush();

      try {
            while (true) {
                byte[] chunk = createFakeAudioData(CHUNK_SIZE);
                out.write(chunk);
                out.flush();
                TimeUnit.SECONDS.sleep(1); // 每秒发送1KB数据
            }
      } catch (InterruptedException e) {
            e.printStackTrace();
      }
    }

    private byte[] createFakeV3Header() {
      byte[] header = new byte;
      header = 'V';
      header = '3';
      header = 'H';
      header = 'D';
      for (int i = 4; i < HEADER_SIZE; i++) {
            header = 0;
      }
      return header;
    }

    private byte[] createFakeAudioData(int size) {
      byte[] audioData = new byte;
      for (int i = 0; i < size; i++) {
            audioData = (byte) ((Math.sin(2 * Math.PI * i / 44100) * 127) + 128); // 生成模拟音频数据
      }
      return audioData;
    }
}
查抄和调试


[*]确保 MIME 类型匹配:确保 mediaSource.addSourceBuffer('audio/mpeg') 的类型和后端返回的 Content-Type 是 audio/mpeg。
[*]查抄浏览器控制台:确保没有其他错误信息。
[*]验证数据是否到达:在 sourceBuffer.appendBuffer 之前打印日记,验证数据块确实到达前端并被处理。
[*]尝试不同的数据块大小:测试不同的 CHUNK_SIZE 以确保数据能够顺利传输并缓冲。
以上步调应确保播放器在接收到足够的数据后开始播放音频。假如问题仍然存在,请查抄网络哀求和相应的详细信息,并确认数据格式和内容正确。



为了确保利用 MediaSource API 举行流式音频播放,前端代码和后端返回的数据须要正确匹配,并且前端代码须要正确处理缓冲区的填充和播放逻辑。以下是一个更详细的查抄和调试步调,以确保音频流能够正常播放。
前端代码

确保前端代码能够正确处理 MediaSource API 和 SourceBuffer,并能够在缓冲区填充足够数据后开始播放:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Audio Stream with MediaSource</title>
</head>
<body>
    <audio id="audioElement" controls></audio>
    <script>
      if ('MediaSource' in window) {
            var audio = document.getElementById('audioElement');
            var mediaSource = new MediaSource();
            audio.src = URL.createObjectURL(mediaSource);

            mediaSource.addEventListener('sourceopen', function () {
                var sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');

                var queue = [];
                var isAppending = false;
                var totalBufferedDuration = 0;
                var requiredBufferDuration = 2;

                function appendNextChunk() {
                  if (queue.length > 0 && !isAppending && !sourceBuffer.updating) {
                        isAppending = true;
                        sourceBuffer.appendBuffer(queue.shift());
                  }
                }

                sourceBuffer.addEventListener('updateend', function () {
                  isAppending = false;
                  if (sourceBuffer.buffered.length > 0) {
                        totalBufferedDuration = sourceBuffer.buffered.end(0);

                        if (totalBufferedDuration >= requiredBufferDuration && audio.paused) {
                            audio.play();
                        }
                  }
                  appendNextChunk();
                });

                sourceBuffer.addEventListener('error', function (e) {
                  console.error('SourceBuffer error:', e);
                });

                fetchAudioData();
            });

            function fetchAudioData() {
                fetch('http://127.0.0.1:8080/audio/loading')
                  .then(response => {
                        if (!response.ok) {
                            throw new Error('Network response was not ok');
                        }
                        return response.body.getReader();
                  })
                  .then(reader => {
                        function read() {
                            reader.read().then(({ done, value }) => {
                              if (done) {
                                    mediaSource.endOfStream();
                                    return;
                              }
                              queue.push(value);
                              appendNextChunk();
                              read();
                            }).catch(error => {
                              console.error('Reader read error:', error);
                            });
                        }
                        read();
                  }).catch(error => {
                        console.error('Fetching audio data failed:', error);
                  });
            }
      } else {
            console.log('The MediaSource API is not supported in this browser.');
      }
    </script>
</body>
</html>
查抄和调试步调


[*] 查抄浏览器控制台:打开浏览器开辟者工具,查抄控制台是否有任何错误或警告信息。
[*] 验证网络哀求:在浏览器开辟者工具的网络标签中查抄/audio/loading哀求是否返回音频数据。确保相应状态是200,并且相应头包罗Content-Type: audio/mpeg。
[*] 确保正确的音频数据:确保从服务器返回的音频数据格式正确,并且能够被audio/mpeg解码。
[*] 打印调试信息:在关键位置添加调试信息,以便相识数据流动情况。下面是添加了一些调试信息的前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Audio Stream with MediaSource</title>
</head>
<body>
    <audio id="audioElement" controls></audio>
    <script>
      if ('MediaSource' in window) {
            var audio = document.getElementById('audioElement');
            var mediaSource = new MediaSource();
            audio.src = URL.createObjectURL(mediaSource);

            mediaSource.addEventListener('sourceopen', function () {
                console.log('MediaSource opened');
                var sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');

                var queue = [];
                var isAppending = false;
                var totalBufferedDuration = 0;
                var requiredBufferDuration = 2;

                function appendNextChunk() {
                  if (queue.length > 0 && !isAppending && !sourceBuffer.updating) {
                        console.log('Appending next chunk');
                        isAppending = true;
                        sourceBuffer.appendBuffer(queue.shift());
                  }
                }

                sourceBuffer.addEventListener('updateend', function () {
                  isAppending = false;
                  console.log('Update ended, total buffered duration:', sourceBuffer.buffered.end(0));
                  if (sourceBuffer.buffered.length > 0) {
                        totalBufferedDuration = sourceBuffer.buffered.end(0);

                        if (totalBufferedDuration >= requiredBufferDuration && audio.paused) {
                            console.log('Starting audio playback');
                            audio.play();
                        }
                  }
                  appendNextChunk();
                });

                sourceBuffer.addEventListener('error', function (e) {
                  console.error('SourceBuffer error:', e);
                });

                fetchAudioData();
            });

            function fetchAudioData() {
                fetch('http://127.0.0.1:8080/audio/loading')
                  .then(response => {
                        if (!response.ok) {
                            throw new Error('Network response was not ok');
                        }
                        return response.body.getReader();
                  })
                  .then(reader => {
                        function read() {
                            reader.read().then(({ done, value }) => {
                              if (done) {
                                    console.log('Stream ended');
                                    mediaSource.endOfStream();
                                    return;
                              }
                              queue.push(value);
                              console.log('Received data chunk, queue length:', queue.length);
                              appendNextChunk();
                              read();
                            }).catch(error => {
                              console.error('Reader read error:', error);
                            });
                        }
                        read();
                  }).catch(error => {
                        console.error('Fetching audio data failed:', error);
                  });
            }
      } else {
            console.log('The MediaSource API is not supported in this browser.');
      }
    </script>
</body>
</html>
后端代码

确保后端返回的音频数据能够被前端正确识别息争码:
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;

@WebServlet("/audio/loading")
public class SlowAudioStreamServlet extends HttpServlet {

    private static final int CHUNK_SIZE = 1024; // 1 KB
    private static final int HEADER_SIZE = 128; // 假设头信息大小为128字节

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      resp.setContentType("audio/mpeg");
      OutputStream out = resp.getOutputStream();

      byte[] v3Header = createFakeV3Header();
      out.write(v3Header);
      out.flush();

      try {
            while (true) {
                byte[] chunk = createFakeAudioData(CHUNK_SIZE);
                out.write(chunk);
                out.flush();
                TimeUnit.SECONDS.sleep(1); // 每秒发送1KB数据
            }
      } catch (InterruptedException e) {
            e.printStackTrace();
      }
    }

    private byte[] createFakeV3Header() {
      byte[] header = new byte;
      header = 'V';
      header = '3';
      header = 'H';
      header = 'D';
      for (int i = 4; i < HEADER_SIZE; i++) {
            header = 0;
      }
      return header;
    }

    private byte[] createFakeAudioData(int size) {
      byte[] audioData = new byte;
      for (int i = 0; i < size; i++) {
            audioData = (byte) ((Math.sin(2 * Math.PI * i / 44100) * 127) + 128); // 生成模拟音频数据
      }
      return audioData;
    }
}
通过上述步调和代码,你可以逐步排除可能的问题,确保音频流能够正确播放。假如问题仍然存在,请提供浏览器控制台中的详细错误信息,以便进一步调试。

前端 这种方式,不再利用 队列等等的方式,就给我一个简单的方式,直接播放
为了简化前端代码并直接播放音频流,可以移除队列机制,并且直接在数据读取时调用 appendBuffer。以下是一个简化版本的前端代码,它直接读取音频数据并播放:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Audio Stream with MediaSource</title>
</head>
<body>
    <audio id="audioElement" controls></audio>
    <script>
      if ('MediaSource' in window) {
            const audio = document.getElementById('audioElement');
            const mediaSource = new MediaSource();
            audio.src = URL.createObjectURL(mediaSource);

            mediaSource.addEventListener('sourceopen', () => {
                const sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');

                fetch('http://127.0.0.1:8080/audio/loading')
                  .then(response => {
                        if (!response.ok) {
                            throw new Error('Network response was not ok');
                        }
                        return response.body.getReader();
                  })
                  .then(reader => {
                        function read() {
                            reader.read().then(({ done, value }) => {
                              if (done) {
                                    mediaSource.endOfStream();
                                    return;
                              }
                              sourceBuffer.appendBuffer(value);
                              read();
                            }).catch(error => {
                              console.error('Reader read error:', error);
                            });
                        }
                        read();
                  }).catch(error => {
                        console.error('Fetching audio data failed:', error);
                  });
            });
      } else {
            console.log('The MediaSource API is not supported in this browser.');
      }
    </script>
</body>
</html>
后端代码

确保后端代码能够以连续的方式发送音频数据:
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;

@WebServlet("/audio/loading")
public class SlowAudioStreamServlet extends HttpServlet {

    private static final int CHUNK_SIZE = 1024; // 1 KB
    private static final int HEADER_SIZE = 128; // 假设头信息大小为128字节

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      resp.setContentType("audio/mpeg");
      OutputStream out = resp.getOutputStream();

      byte[] v3Header = createFakeV3Header();
      out.write(v3Header);
      out.flush();

      try {
            while (true) {
                byte[] chunk = createFakeAudioData(CHUNK_SIZE);
                out.write(chunk);
                out.flush();
                TimeUnit.SECONDS.sleep(1); // 每秒发送1KB数据
            }
      } catch (InterruptedException e) {
            e.printStackTrace();
      }
    }

    private byte[] createFakeV3Header() {
      byte[] header = new byte;
      header = 'V';
      header = '3';
      header = 'H';
      header = 'D';
      for (int i = 4; i < HEADER_SIZE; i++) {
            header = 0;
      }
      return header;
    }

    private byte[] createFakeAudioData(int size) {
      byte[] audioData = new byte;
      for (int i = 0; i < size; i++) {
            audioData = (byte) ((Math.sin(2 * Math.PI * i / 44100) * 127) + 128); // 生成模拟音频数据
      }
      return audioData;
    }
}
关键点


[*]直接读取并追加:前端代码在读取到数据后直接调用 sourceBuffer.appendBuffer。
[*]保持简单:不再利用队列和复杂的状态管理,只是简单地读取和追加数据。
[*]自动播放:移除播放控制逻辑,让音频在足够数据被缓冲后自动播放。
通过这种方式,简化了前端逻辑,使音频流能够更加直接地举行播放。

前端照旧播放不了,再简单一些
为了简化实现,我们将移除所有复杂的逻辑,保持前端代码尽可能简单,直接将接收到的音频数据传递给播放器。
前端代码

确保前端代码尽量简单,直接读取和追加数据:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Audio Stream with MediaSource</title>
</head>
<body>
    <audio id="audioElement" controls></audio>
    <script>
      if ('MediaSource' in window) {
            const audio = document.getElementById('audioElement');
            const mediaSource = new MediaSource();
            audio.src = URL.createObjectURL(mediaSource);

            mediaSource.addEventListener('sourceopen', () => {
                const sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');

                fetch('http://127.0.0.1:8080/audio/loading')
                  .then(response => {
                        if (!response.ok) {
                            throw new Error('Network response was not ok');
                        }
                        return response.body.getReader();
                  })
                  .then(reader => {
                        function read() {
                            reader.read().then(({ done, value }) => {
                              if (done) {
                                    mediaSource.endOfStream();
                                    return;
                              }
                              sourceBuffer.appendBuffer(value);
                              read();
                            }).catch(error => {
                              console.error('Reader read error:', error);
                            });
                        }
                        read();
                  }).catch(error => {
                        console.error('Fetching audio data failed:', error);
                  });
            });

            mediaSource.addEventListener('error', function (e) {
                console.error('MediaSource error:', e);
            });
      } else {
            console.log('The MediaSource API is not supported in this browser.');
      }
    </script>
</body>
</html>
后端代码

确保后端以连续的方式发送音频数据:
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.TimeUnit;

@WebServlet("/audio/loading")
public class SlowAudioStreamServlet extends HttpServlet {

    private static final int CHUNK_SIZE = 1024; // 1 KB
    private static final int HEADER_SIZE = 128; // 假设头信息大小为128字节

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
      resp.setContentType("audio/mpeg");
      OutputStream out = resp.getOutputStream();

      byte[] v3Header = createFakeV3Header();
      out.write(v3Header);
      out.flush();

      try {
            while (true) {
                byte[] chunk = createFakeAudioData(CHUNK_SIZE);
                out.write(chunk);
                out.flush();
                TimeUnit.SECONDS.sleep(1); // 每秒发送1KB数据
            }
      } catch (InterruptedException e) {
            e.printStackTrace();
      }
    }

    private byte[] createFakeV3Header() {
      byte[] header = new byte;
      header = 'V';
      header = '3';
      header = 'H';
      header = 'D';
      for (int i = 4; i < HEADER_SIZE; i++) {
            header = 0;
      }
      return header;
    }

    private byte[] createFakeAudioData(int size) {
      byte[] audioData = new byte;
      for (int i = 0; i < size; i++) {
            audioData = (byte) ((Math.sin(2 * Math.PI * i / 44100) * 127) + 128); // 生成模拟音频数据
      }
      return audioData;
    }
}
关键点


[*]直接读取并追加:前端代码在读取到数据后直接调用 sourceBuffer.appendBuffer。
[*]自动播放:移除播放控制逻辑,让音频在足够数据被缓冲后自动播放。
[*]简单化:保持前端代码尽量简单,镌汰逻辑和状态管理。
通过这种方式,简化前端逻辑,使音频流能够更加直接地举行播放。假如问题仍然存在,请查抄网络哀求、相应的详细信息,并确认数据格式和内容正确。假如须要进一步调试,请查看浏览器控制台中的详细错误信息。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: sourceBuffer failed to execute appendBuffer on sourceBuffer this sourc