Uncaught (in promise) DOMException: play() failed because the user didn't interact with the document first. 这个报错怎么解决

这个报错是由浏览器的自动播放策略引起的,现代浏览器通常会限制自动播放音视频以提升用户体验和节省带宽。为了解决这个问题,你可以考虑以下几种方法来处理:

1. 用户交互触发播放

浏览器要求用户与页面有过交互(例如点击、触摸)后才能自动播放音视频。你可以在用户与页面交互后开始播放视频。例如,可以添加一个按钮,用户点击按钮后开始加载和播放视频。

<button onclick="startVideo()">点击播放视频</button>
<video id="myVideo" controls></video>

<script>
  var video = document.getElementById('myVideo');
  var assetURL = 'http://127.0.0.1:8080/audio/loading';
  var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';

  function startVideo() {
    if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
      var mediaSource = new MediaSource();
      video.src = URL.createObjectURL(mediaSource);

      mediaSource.addEventListener('sourceopen', function() {
        var sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
        fetch(assetURL).then(response => {
          return response.body;
        }).then(body => {
          var reader = body.getReader();
          var pump = function() {
            reader.read().then(function(result) {
              if (result.done) {
                mediaSource.endOfStream();
              } else {
                sourceBuffer.appendBuffer(result.value);
                pump();
              }
            });
          };
          pump();
        });
      });

    } else {
      console.error('Unsupported MIME type or codec: ', mimeCodec);
    }
  }
</script>

2. 检查浏览器设置和权限

确保浏览器没有被设置为禁止自动播放音视频,有时用户可能会手动配置浏览器不允许自动播放,这会导致即使用户与页面有过交互,浏览器仍然会阻止自动播放。

3. 其他注意事项

  • 网络问题: 确保你的视频资源能够正常加载和播放,包括服务器的正确配置和视频文件本身的有效性。

  • 浏览器兼容性: 不同的浏览器可能对MediaSource API 和自动播放策略有不同的支持和限制,需要测试和适配不同的浏览器。

通过这些方法,你应该能够解决自动播放被阻止的问题,并且在用户与页面进行了交互后能够顺利开始播放视频。

流媒体MSE入门 给我个简单案例

Media Source Extensions (MSE) 允许我们通过 JavaScript 控制媒体流的播放过程,这对于实现直播、动态生成视频内容或者从服务器端流式传输视频数据非常有用。下面是一个简单的案例,展示了如何使用 MSE 来播放一个简单的视频流。

简单的流媒体MSE案例

HTML 结构
解释与注意事项:
  1. HTML 结构

    • <video id="myVideo" controls></video>:这是用于展示视频的 <video> 元素。它带有 controls 属性,允许用户通过播放控制条控制视频。
  2. JavaScript 部分

    • 首先检查浏览器是否支持 MediaSource API 和指定的视频编解码器 (mimeCodec)。如果支持,创建一个新的 MediaSource 对象并为 <video> 元素设置 src 属性为 URL.createObjectURL(mediaSource)

    • mediaSourcesourceopen 事件监听器中,创建一个 SourceBuffer 对象,并通过 fetch 获取视频流数据 (assetURL)。

    • fetch 返回的是一个 Response 对象,使用 response.body.getReader() 获取一个 ReadableStream 的读取器,以便逐步从视频流中读取数据。

    • 使用递归函数 pump() 不断读取数据并将其追加到 sourceBuffer 中,直到视频流结束 (result.done 为 true)。最后调用 mediaSource.endOfStream() 来标志视频流的结束。

  3. 注意事项

    • 确保你替换 assetURLmimeCodec 为你的实际视频流地址和视频编解码器。
    • 浏览器对自动播放的策略可能会阻止视频的自动播放。为了让视频可以播放,通常需要用户与页面进行交互(例如点击按钮)。
    • 这个例子假设视频流以 mp4 格式进行编码,你需要根据实际情况调整 mimeCodec 的值来匹配你的视频流格式和编解码器。

通过这个简单的示例,你可以理解如何使用 MSE 控制媒体流的加载和播放过程,这对于需要动态生成或者从服务器端流式传输视频数据的应用非常有帮助。

上面 代码 一分钟的视频,只播放了前两秒

ChatGPT

问题出现的原因可能是浏览器的自动播放策略。现代浏览器通常要求用户与页面有交互(例如点击)后才能自动播放音视频,以避免滥用带宽和提升用户体验。

为了确保视频能够完整播放,你可以修改代码,添加一个按钮或者其他形式的用户交互,使用户点击后开始加载和播放视频。这样可以绕过浏览器的自动播放限制。

修改建议:

  1. 添加播放按钮:在页面上添加一个按钮,当用户点击按钮时开始加载和播放视频。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Simple MSE Example</title>
</head>
<body>
  <button onclick="startVideo()">点击播放视频</button>
  <video id="myVideo" controls></video>

  <script>
    var video = document.getElementById('myVideo');
    var assetURL = 'https://example.com/your-video-stream'; // 替换成你的视频流地址
    var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'; // 视频编解码器

    function startVideo() {
      if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
        var mediaSource = new MediaSource();
        video.src = URL.createObjectURL(mediaSource);

        mediaSource.addEventListener('sourceopen', function() {
          var sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
          
          fetch(assetURL).then(response => {
            return response.body;
          }).then(body => {
            var reader = body.getReader();
            
            var pump = function() {
              reader.read().then(function(result) {
                if (result.done) {
                  mediaSource.endOfStream();
                } else {
                  sourceBuffer.appendBuffer(result.value);
                  pump();
                }
              });
            };
            pump();
          });
        });

      } else {
        console.error('Unsupported MIME type or codec: ', mimeCodec);
      }
    }
  </script>
</body>
</html>

修改后的效果:

  • 用户打开页面后看到一个按钮 "点击播放视频"。
  • 用户点击按钮后,调用 startVideo() 函数开始加载和播放视频。
  • 这样用户的主动操作将触发视频的加载和播放,绕过浏览器的自动播放策略。

注意事项:

  • 确保替换 assetURLmimeCodec 为你实际使用的视频流地址和视频编解码器。
  • 如果视频流长度超过几分钟,建议确保服务器端和网络传输能够稳定支持长时间的视频流传输。
  • 测试时请注意浏览器的兼容性和自动播放策略,不同浏览器可能有不同的行为和限制。

Uncaught (in promise) DOMException: Failed to execute 'appendBuffer' on 'SourceBuffer': This SourceBuffer is still processing an 'appendBuffer' or 'remove' operation. at file:///C:/Users/tom/Desktop/3.html:35:32 报了 这个错

这个错误表明在尝试向 SourceBuffer 添加数据时,该 SourceBuffer 对象仍在处理先前的 appendBufferremove 操作。这可能是由于代码中的异步操作或者数据处理速度过快导致的竞态条件。为了解决这个问题,可以通过适当的异步控制和错误处理来优化代码。

优化建议:

  1. 添加异步控制

    在向 SourceBuffer 添加数据时,确保只有当前一个操作完成后再进行下一次操作。可以使用事件监听器或者 Promise 来控制操作的顺序和状态。

    var appendQueue = []; // 用于存放待添加的数据
    
    function appendNext() {
      if (sourceBuffer.updating || appendQueue.length === 0) return;
    
      var data = appendQueue.shift();
      sourceBuffer.appendBuffer(data);
    
      // 添加数据后继续处理下一个
      if (appendQueue.length > 0) {
        appendNext();
      }
    }
    
    sourceBuffer.addEventListener('updateend', function() {
      appendNext(); // 当前一个操作结束后继续添加下一个
    });
    
    function fetchDataAndAppend(url) {
      fetch(url)
        .then(response => response.arrayBuffer())
        .then(data => {
          appendQueue.push(data);
    
          // 如果当前没有正在处理的更新,立即开始添加
          if (!sourceBuffer.updating) {
            appendNext();
          }
        })
        .catch(error => {
          console.error('Failed to fetch data or append to SourceBuffer:', error);
        });
    }
    

    在这个示例中,appendQueue 用于存储待添加的数据,在 updateend 事件监听器中,检查是否还有待添加的数据,如果有则继续处理下一个数据

  2. 错误处理

    在数据获取或者添加过程中,添加适当的错误处理,以便能够捕获和处理可能发生的异常或者错误情况。

    function fetchDataAndAppend(url) {
      fetch(url)
        .then(response => {
          if (!response.ok) {
            throw new Error('Failed to fetch data');
          }
          return response.arrayBuffer();
        })
        .then(data => {
          // 添加数据到 SourceBuffer
          sourceBuffer.appendBuffer(data);
        })
        .catch(error => {
          console.error('Error fetching data or appending to SourceBuffer:', error);
        });
    }
    

  3. 注意事项

    • 确保 fetchappendBuffer 操作之间没有竞态条件,即在添加数据之前检查当前的 SourceBuffer 是否正在更新。
    • 考虑到网络和服务器响应的延迟,以及客户端处理数据的速度,要确保适当地控制和调度数据的添加操作。
    • 对于长时间的视频流,可能需要更复杂的流控制和数据管理,以确保稳定的视频播放和用户体验。

通过以上优化和错误处理,应该能够有效地解决 DOMException 错误,并确保 SourceBuffer 的稳定使用和视频流的正常播放。

给我完整的前端代码

理解了你的需求,我来为你提供一个完整的前端代码示例,包括了流媒体 MSE 的基本配置、数据加载和播放控制。这个示例假设你有一个可以访问的视频流地址,我们将使用 JavaScript 和 HTML 来实现流媒体的加载和播放。

完整的前端代码示例

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Media Source Extensions (MSE) Example</title>
</head>
<body>
  <button onclick="startVideo()">点击播放视频</button>
  <video id="myVideo" controls></video>

  <script>
    var video = document.getElementById('myVideo');
    var assetURL = 'https://example.com/your-video-stream'; // 替换成你的视频流地址
    var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'; // 视频编解码器
    var mediaSource;
    var sourceBuffer;

    function startVideo() {
      if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
        mediaSource = new MediaSource();
        video.src = URL.createObjectURL(mediaSource);

        mediaSource.addEventListener('sourceopen', function() {
          sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
          fetchAndAppendData(assetURL);
        });

      } else {
        console.error('Unsupported MIME type or codec: ', mimeCodec);
      }
    }

    function fetchAndAppendData(url) {
      fetch(url)
        .then(response => response.arrayBuffer())
        .then(data => {
          sourceBuffer.appendBuffer(data);
        })
        .catch(error => {
          console.error('Error fetching data or appending to SourceBuffer:', error);
        });
    }
  </script>
</body>
</html>

代码解释:

  1. HTML 结构

    • <button onclick="startVideo()">点击播放视频</button>:添加了一个按钮,点击按钮会触发 startVideo() 函数开始加载和播放视频。
    • <video id="myVideo" controls></video>:用于显示视频的 <video> 元素,带有 controls 属性,允许用户控制视频播放。
  2. JavaScript 部分

    • startVideo() 函数:在用户点击按钮后调用,检查浏览器是否支持 MediaSource API 和指定的视频编解码器 (mimeCodec)。如果支持,则创建一个新的 MediaSource 对象,并为 <video> 元素设置 src 属性为 URL.createObjectURL(mediaSource)
    • mediaSource.addEventListener('sourceopen', ...):监听 sourceopen 事件,在事件处理程序中创建 SourceBuffer 对象,并调用 fetchAndAppendData() 函数开始获取并追加视频数据。
    • fetchAndAppendData(url) 函数:使用 fetch API 获取指定 url 的视频数据,并将数据追加到 sourceBuffer 中。
  3. 注意事项

    • 确保替换 assetURLmimeCodec 为你实际使用的视频流地址和视频编解码器。
    • 这个示例假设视频流以 mp4 格式进行编码,你需要根据实际情况调整 mimeCodec 的值来匹配你的视频流格式和编解码器。
    • 测试时请注意浏览器的兼容性和自动播放策略,不同浏览器可能有不同的行为和限制。

通过这个示例,你可以实现基本的流媒体 MSE 功能,使视频在用户交互后开始加载和播放,从而避免浏览器的自动播放策略限制。

上面代码 若 后端的流 15分钟才能返回完毕,是否需要等待15分钟流加载完毕才能播放呢

对于流媒体播放,特别是通过 Media Source Extensions (MSE) 实现的动态加载和播放,通常不需要等待整个视频流加载完成才能开始播放。相反,你可以实现逐步加载和播放的功能,这样用户可以在视频流加载过程中开始观看。

优化加载和播放策略:

  1. 逐步加载数据

    • MSE 允许你动态地向 SourceBuffer 中添加数据,这意味着你可以在获取到数据的同时开始播放已经加载的部分,而不需要等待整个视频流全部加载完成。
    • fetchAndAppendData() 函数中,每次成功获取到数据后即时追加到 SourceBuffer 中,这样可以实现逐步加载和播放的效果。
  2. 播放控制

    • 当开始播放时,用户可以观看已经加载的部分,并且随着时间的推移,更多的数据会被动态添加到 SourceBuffer 中,以继续播放视频。
    • 可以通过监听视频的 timeupdate 事件来动态调整加载策略,例如在播放过程中检查缓冲区的状态,并根据需要继续加载新的数据。

调整代码:

下面是一个稍作调整的示例,展示了如何逐步加载并播放视频流。在这个示例中,我们添加了一些基本的播放控制和缓冲管理:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Media Source Extensions (MSE) Example</title>
</head>
<body>
  <button onclick="startVideo()">点击播放视频</button>
  <video id="myVideo" controls></video>

  <script>
    var video = document.getElementById('myVideo');
    var assetURL = 'https://example.com/your-video-stream'; // 替换成你的视频流地址
    var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'; // 视频编解码器
    var mediaSource;
    var sourceBuffer;

    function startVideo() {
      if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
        mediaSource = new MediaSource();
        video.src = URL.createObjectURL(mediaSource);

        mediaSource.addEventListener('sourceopen', function() {
          sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
          fetchAndAppendData(assetURL);
        });

      } else {
        console.error('Unsupported MIME type or codec: ', mimeCodec);
      }
    }

    function fetchAndAppendData(url) {
      fetch(url)
        .then(response => response.arrayBuffer())
        .then(data => {
          sourceBuffer.appendBuffer(data);
          video.play(); // 当开始加载数据时开始播放视频
        })
        .catch(error => {
          console.error('Error fetching data or appending to SourceBuffer:', error);
        });
    }
  </script>
</body>
</html>

调整后的代码解释:

  • startVideo() 函数:点击按钮后开始加载视频流。在 sourceopen 事件处理程序中,创建 SourceBuffer 并调用 fetchAndAppendData() 函数开始加载数据。

  • fetchAndAppendData(url) 函数:使用 fetch 获取视频流数据,并将数据追加到 SourceBuffer 中。在成功获取并追加数据后,立即调用 video.play() 开始播放视频。

注意事项:

  • 播放控制和缓冲管理:你可以根据需要进一步优化播放控制,例如监控视频的缓冲状态,以确保在视频播放过程中及时加载新的数据,避免播放中断或者缓冲过长。

  • 网络和服务器响应:确保你的服务器能够稳定提供视频流,并根据需要进行适当的带宽管理和优化,以确保用户的良好体验。

通过以上优化,你可以实现在视频流加载过程中逐步播放视频,而不需要等待整个视频加载完成。这种方式能够显著提升用户的观看体验,特别是对于较大的视频文件或者长时间的视频流。

还是 等到 我流加载完毕后才开始播放了啊

理解你的需求,你希望能够在流加载过程中逐步播放视频,而不是等到整个视频流加载完毕后才开始播放。为了实现这一点,我们需要适当地管理数据的加载和播放控制,确保在有足够数据时就开始播放视频。

调整代码实现逐步加载和播放:

下面是一个优化后的示例代码,将根据视频流的加载状态逐步启动播放:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Media Source Extensions (MSE) Example</title>
</head>
<body>
  <button onclick="startVideo()">点击播放视频</button>
  <video id="myVideo" controls></video>

  <script>
    var video = document.getElementById('myVideo');
    var assetURL = 'https://example.com/your-video-stream'; // 替换成你的视频流地址
    var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'; // 视频编解码器
    var mediaSource;
    var sourceBuffer;

    function startVideo() {
      if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
        mediaSource = new MediaSource();
        video.src = URL.createObjectURL(mediaSource);

        mediaSource.addEventListener('sourceopen', function() {
          sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
          fetchAndAppendData(assetURL);
        });

      } else {
        console.error('Unsupported MIME type or codec: ', mimeCodec);
      }
    }

    function fetchAndAppendData(url) {
      fetch(url)
        .then(response => {
          if (!response.ok) {
            throw new Error('Failed to fetch data');
          }
          return response.body;
        })
        .then(body => {
          var reader = body.getReader();
          var pump = function() {
            reader.read().then(function(result) {
              if (result.done) {
                mediaSource.endOfStream();
              } else {
                sourceBuffer.appendBuffer(result.value);
                if (video.paused && !video.seeking) {
                  video.play().catch(function(error) {
                    console.error('Failed to start playback:', error);
                  });
                }
                pump();
              }
            }).catch(function(error) {
              console.error('Error reading stream:', error);
            });
          };
          pump();
        })
        .catch(error => {
          console.error('Error fetching data or appending to SourceBuffer:', error);
        });
    }
  </script>
</body>
</html>

代码解释与优化:

  1. startVideo() 函数

    • 当用户点击按钮时调用,检查浏览器是否支持 MediaSource API 和指定的视频编解码器 (mimeCodec)。
    • 创建一个新的 MediaSource 对象,并设置 <video> 元素的 src 属性为 URL.createObjectURL(mediaSource)
  2. fetchAndAppendData(url) 函数

    • 使用 fetch API 获取视频流数据,并逐步追加到 SourceBuffer 中。
    • fetchthen 函数中,通过 response.body.getReader() 获取一个 ReadableStream 的读取器,以便逐步从视频流中读取数据。
    • 使用递归函数 pump() 不断读取数据并将其追加到 sourceBuffer 中,同时检查视频是否已经暂停或者正在寻找新的播放位置 (video.paused && !video.seeking),如果是,则尝试开始播放视频。
  3. 优化播放控制

    • 在逐步加载数据的过程中,一旦有足够的数据被追加到 SourceBuffer 中,视频将会开始播放。
    • 这种方法可以确保用户在流加载过程中就可以开始观看视频,而不需要等待整个视频流完全加载。

通过以上优化,应该能够实现在视频流加载过程中逐步播放视频,提升用户的观看体验,同时符合浏览器的自动播放策略。

Error reading stream: DOMException: Failed to execute 'appendBuffer' on 'SourceBuffer': This SourceBuffer is still processing an 'appendBuffer' or 'remove' operation. at 49 30 报了这个错了,,给我个能运行的啊

理解你遇到的问题。这个错误通常是由于在向 SourceBuffer 添加数据时,之前的 appendBuffer 操作尚未完成,导致冲突。为了解决这个问题,我们可以通过适当的异步控制和错误处理来优化代码,确保 appendBuffer 操作顺利进行。

改进后的代码示例:

在以下示例中,我会结合使用 fetch API 和 ReadableStream,并通过适当的异步控制来确保 appendBuffer 操作的顺序执行。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Media Source Extensions (MSE) Example</title>
</head>
<body>
  <button onclick="startVideo()">点击播放视频</button>
  <video id="myVideo" controls></video>

  <script>
    var video = document.getElementById('myVideo');
    var assetURL = 'https://example.com/your-video-stream'; // 替换成你的视频流地址
    var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'; // 视频编解码器
    var mediaSource;
    var sourceBuffer;
    var mediaSourceReady = false; // 标记 MediaSource 是否已打开

    function startVideo() {
      if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
        mediaSource = new MediaSource();
        video.src = URL.createObjectURL(mediaSource);

        mediaSource.addEventListener('sourceopen', function() {
          mediaSourceReady = true; // 标记为已打开
          sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
          fetchAndAppendData(assetURL);
        });

      } else {
        console.error('Unsupported MIME type or codec: ', mimeCodec);
      }
    }

    function fetchAndAppendData(url) {
      fetch(url)
        .then(response => {
          if (!response.ok) {
            throw new Error('Failed to fetch data');
          }
          return response.body;
        })
        .then(body => {
          var reader = body.getReader();

          var pump = function() {
            reader.read().then(function(result) {
              if (result.done) {
                mediaSource.endOfStream();
              } else {
                appendData(result.value);
                pump(); // 继续读取下一段数据
              }
            }).catch(function(error) {
              console.error('Error reading stream:', error);
            });
          };

          pump(); // 开始读取数据并追加到 SourceBuffer
        })
        .catch(error => {
          console.error('Error fetching data or appending to SourceBuffer:', error);
        });
    }

    function appendData(data) {
      if (mediaSourceReady && !sourceBuffer.updating) {
        try {
          sourceBuffer.appendBuffer(data);
        } catch (error) {
          if (error.name === 'QuotaExceededError') {
            console.warn('QuotaExceededError: SourceBuffer buffer full');
            // 可以尝试使用 remove() 方法清空 SourceBuffer 或其他处理方式
          } else {
            console.error('Error appending buffer:', error);
          }
        }
      } else {
        console.warn('SourceBuffer is busy or MediaSource not ready');
        // 可以添加适当的处理逻辑
      }
    }
  </script>
</body>
</html>

代码解释与优化:

  1. 改进的 fetchAndAppendData() 函数

    • 使用 fetch API 获取视频流数据,并通过 response.body.getReader() 获取 ReadableStream 的读取器。
    • 使用递归函数 pump() 来不断读取数据,并调用 appendData() 函数将数据追加到 SourceBuffer 中。
  2. appendData(data) 函数

    • 在向 SourceBuffer 添加数据之前,检查 MediaSource 是否已经打开并且 SourceBuffer 不处于更新状态 (sourceBuffer.updating)。
    • 如果满足条件,则尝试将数据追加到 SourceBuffer 中。
    • 如果 SourceBuffer 正在更新或者 MediaSource 还未准备好,将会发出警告并跳过本次追加操作。
  3. 错误处理

    • appendData() 函数中添加了错误处理逻辑,例如当 SourceBuffer 的缓冲区已满时可能会抛出 QuotaExceededError,可以根据需要进行处理。

通过这种方式,应该能够更好地管理数据的追加操作,避免因为并发追加操作而导致的 DOMException 错误。请确保服务器能够稳定地提供视频流,并根据实际需求调整和优化代码。

Error appending buffer: DOMException: Failed to execute 'appendBuffer' on 'SourceBuffer': The HTMLMediaElement.error attribute is not null. at appendData 上面代码又报了这个错

DOMException: Failed to execute 'appendBuffer' on 'SourceBuffer': The HTMLMediaElement.error attribute is not null 这个错误表示 HTMLMediaElement(即 <video> 元素)遇到了一个错误,无法继续播放或缓冲数据。通常,这种情况可能是由于数据格式不正确或其他播放相关问题导致的。

为了处理这个问题,我们需要:

  1. 确保数据格式正确:确保你传递给 appendBuffer 的数据是正确的、符合预期的媒体格式。
  2. 错误处理和恢复:在播放过程中检测并处理错误,以确保能够正确恢复或重新加载数据。

下面是一个更完整的示例,包括了详细的错误处理和恢复逻辑:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Media Source Extensions (MSE) Example</title>
</head>
<body>
  <button onclick="startVideo()">点击播放视频</button>
  <video id="myVideo" controls></video>

  <script>
    var video = document.getElementById('myVideo');
    var assetURL = 'https://example.com/your-video-stream'; // 替换成你的视频流地址
    var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'; // 视频编解码器
    var mediaSource;
    var sourceBuffer;
    var queue = []; // 队列用于存储待添加的数据

    function startVideo() {
      if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
        mediaSource = new MediaSource();
        video.src = URL.createObjectURL(mediaSource);

        mediaSource.addEventListener('sourceopen', function() {
          sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
          sourceBuffer.addEventListener('updateend', onUpdateEnd);
          fetchAndAppendData(assetURL);
        });

        video.addEventListener('error', onError);
      } else {
        console.error('Unsupported MIME type or codec: ', mimeCodec);
      }
    }

    function fetchAndAppendData(url) {
      fetch(url)
        .then(response => {
          if (!response.ok) {
            throw new Error('Failed to fetch data');
          }
          return response.body;
        })
        .then(body => {
          var reader = body.getReader();

          var pump = function() {
            reader.read().then(function(result) {
              if (result.done) {
                mediaSource.endOfStream();
              } else {
                queue.push(result.value);
                appendNextBuffer();
                pump(); // 继续读取下一段数据
              }
            }).catch(function(error) {
              console.error('Error reading stream:', error);
            });
          };

          pump(); // 开始读取数据并追加到 SourceBuffer
        })
        .catch(error => {
          console.error('Error fetching data or appending to SourceBuffer:', error);
        });
    }

    function appendNextBuffer() {
      if (sourceBuffer && !sourceBuffer.updating && queue.length > 0) {
        try {
          sourceBuffer.appendBuffer(queue.shift());
        } catch (error) {
          console.error('Error appending buffer:', error);
        }
      }
    }

    function onUpdateEnd() {
      appendNextBuffer();
      if (video.paused && !video.seeking) {
        video.play().catch(function(error) {
          console.error('Failed to start playback:', error);
        });
      }
    }

    function onError(event) {
      var mediaError = event.target.error;
      if (mediaError) {
        console.error('MediaError:', mediaError);
        // 可以根据需要添加恢复逻辑,例如重新加载视频或提示用户
      }
    }
  </script>
</body>
</html>

代码解释:

  1. 队列机制

    • queue 数组用于存储待添加到 SourceBuffer 的数据。
    • 使用 appendNextBuffer() 函数从队列中取出数据并添加到 SourceBuffer 中。
  2. onUpdateEnd 事件处理程序

    • 每次 SourceBuffer 更新完成时,调用 appendNextBuffer() 继续添加数据。
    • 确保在视频暂停且未在寻找新播放位置时自动播放视频。
  3. 错误处理

    • video 元素上监听 error 事件,捕获并处理媒体播放过程中出现的错误。
    • 添加了基本的错误处理逻辑,可以根据需要进行扩展,例如重新加载视频或提示用户。

通过这种方式,确保在数据加载过程中顺序地向 SourceBuffer 添加数据,并处理可能出现的错误,应该能够实现逐步加载和播放视频流。

使用流媒体MES 播放 mp3 文件

Media Source Extensions (MSE) 主要用于流媒体的动态加载和播放,通常用于处理视频流,不过在某些情况下也可以用于音频流,如 MP3 文件。以下是一个简单的示例代码,演示如何使用 MSE 播放 MP3 文件:

HTML 和 JavaScript 代码示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Media Source Extensions (MSE) Example for MP3</title>
</head>
<body>
  <button onclick="startAudio()">播放音频</button>
  <audio id="myAudio" controls></audio>

  <script>
    var audio = document.getElementById('myAudio');
    var assetURL = 'https://example.com/your-audio-stream.mp3'; // 替换成你的 MP3 文件地址
    var mimeCodec = 'audio/mpeg'; // MP3 文件的 MIME 类型

    var mediaSource;
    var sourceBuffer;

    function startAudio() {
      if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
        mediaSource = new MediaSource();
        audio.src = URL.createObjectURL(mediaSource);

        mediaSource.addEventListener('sourceopen', function() {
          sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
          fetchAndAppendData(assetURL);
        });

      } else {
        console.error('Unsupported MIME type: ', mimeCodec);
      }
    }

    function fetchAndAppendData(url) {
      fetch(url)
        .then(response => {
          if (!response.ok) {
            throw new Error('Failed to fetch data');
          }
          return response.arrayBuffer();
        })
        .then(data => {
          sourceBuffer.appendBuffer(data);
          audio.play().catch(function(error) {
            console.error('Failed to start playback:', error);
          });
        })
        .catch(error => {
          console.error('Error fetching data or appending to SourceBuffer:', error);
        });
    }
  </script>
</body>
</html>
代码解释与注意事项:
  1. HTML 结构

    • 页面中有一个 <audio> 元素用于播放音频,并有一个按钮触发播放操作。
  2. JavaScript 代码

    • startAudio() 函数:检查浏览器是否支持 MediaSource 和指定的 MP3 文件 MIME 类型 (audio/mpeg)。如果支持,创建一个新的 MediaSource 对象并为 <audio> 元素设置 src 属性。
    • mediaSource.addEventListener('sourceopen', ...):在 MediaSource 打开时,添加一个 SourceBuffer 并调用 fetchAndAppendData() 函数开始加载数据。
    • fetchAndAppendData(url) 函数:使用 fetch API 获取 MP3 文件的数据,并将数据追加到 SourceBuffer 中。一旦数据追加完毕,调用 audio.play() 开始播放音频。
  3. 错误处理

    • 在每个异步操作 (fetch, appendBuffer, play) 中添加了适当的错误处理逻辑,确保在出现问题时能够及时捕获和处理错误。
  4. 注意事项

    • 确保 MP3 文件的地址 (assetURL) 是有效的,并且服务器能够正确地处理请求并提供正确的数据格式。
    • 在某些浏览器和设备上,可能需要额外的浏览器支持或配置来确保 MSE 能够正常工作,特别是对于音频流的处理。

通过这个示例代码,你可以在网页上使用 MSE 来动态加载和播放 MP3 文件。确保在实际部署中适当调整和测试,以确保在不同环境下都能正常工作。

上面的代码 只有当我后端的流 全部输出完毕后才能播放,我需要后端给了一部分的时候就直接能播放

为了实现在后端给予部分数据时就能开始播放音频,而不是等待整个流全部输出完毕,我们需要调整前端和后端的交互方式,确保在数据逐步加载到客户端时就能够播放。

改进的前端代码示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Media Source Extensions (MSE) Example for MP3</title>
</head>
<body>
  <button onclick="startAudio()">播放音频</button>
  <audio id="myAudio" controls></audio>

  <script>
    var audio = document.getElementById('myAudio');
    var assetURL = 'https://example.com/your-audio-stream.mp3'; // 替换成你的 MP3 文件地址
    var mimeCodec = 'audio/mpeg'; // MP3 文件的 MIME 类型

    var mediaSource;
    var sourceBuffer;
    var receivedLength = 0; // 已接收的数据长度
    var chunkSize = 1024 * 1024; // 每次请求的数据块大小,可以根据实际情况调整

    function startAudio() {
      if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
        mediaSource = new MediaSource();
        audio.src = URL.createObjectURL(mediaSource);

        mediaSource.addEventListener('sourceopen', function() {
          sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
          fetchAndAppendData(assetURL, receivedLength, chunkSize);
        });

      } else {
        console.error('Unsupported MIME type: ', mimeCodec);
      }
    }

    function fetchAndAppendData(url, start, length) {
      var headers = new Headers();
      headers.append('Range', 'bytes=' + start + '-' + (start + length - 1));

      fetch(url, { headers: headers })
        .then(response => {
          if (!response.ok) {
            throw new Error('Failed to fetch data');
          }
          return response.arrayBuffer();
        })
        .then(data => {
          receivedLength += data.byteLength;
          sourceBuffer.appendBuffer(data);

          if (!mediaSource.readyState || mediaSource.readyState === 'ended') {
            mediaSource.endOfStream();
          }

          if (!audio.paused && !audio.ended) {
            audio.play().catch(function(error) {
              console.error('Failed to start playback:', error);
            });
          }
          
          // 继续请求下一段数据
          if (response.headers.has('Content-Range')) {
            var contentRange = response.headers.get('Content-Range');
            var contentLength = parseInt(contentRange.split('/')[1], 10);
            if (receivedLength < contentLength) {
              fetchAndAppendData(url, receivedLength, chunkSize);
            }
          }
        })
        .catch(error => {
          console.error('Error fetching data or appending to SourceBuffer:', error);
        });
    }
  </script>
</body>
</html>

改进后的代码解释:

  1. 改进的 fetchAndAppendData() 函数

    • 使用 Range 头部来请求服务器上的特定字节范围,而不是一次性请求整个 MP3 文件。
    • 当接收到数据后,将数据追加到 SourceBuffer 中,并更新 receivedLength 变量以跟踪已接收的数据量。
    • 如果 MediaSource 的状态为 ended,调用 mediaSource.endOfStream() 结束流。
    • 如果音频不是暂停状态且未结束,尝试播放音频。
    • 如果响应头部包含 Content-Range 信息,并且尚未接收完整个文件,继续请求下一段数据。
  2. 注意事项

    • 后端支持: 后端需要支持分块传输(Range 请求),并能够正确处理这些请求。
    • 数据块大小: chunkSize 可以根据实际情况进行调整,确保每次请求的数据量合适,既能满足播放的需要,又能减少不必要的请求次数和延迟。

通过这种方式,你可以在前端实现逐步加载和播放 MP3 文件的功能,即使在后端仍在传输数据的过程中,也能够让用户在接收到部分数据时就开始听到音频。

还是需要等待整个流全部输出完毕,才能播放啊,你再检查一下,

明白了,我们需要确保在数据逐步加载到 SourceBuffer 时就能开始播放,而不是等待整个流完全加载完毕。这需要改进数据的分块加载和播放控制逻辑。以下是详细的实现:

改进的前端代码示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Media Source Extensions (MSE) Example for MP3</title>
</head>
<body>
  <button onclick="startAudio()">播放音频</button>
  <audio id="myAudio" controls></audio>

  <script>
    var audio = document.getElementById('myAudio');
    var assetURL = 'https://example.com/your-audio-stream.mp3'; // 替换成你的 MP3 文件地址
    var mimeCodec = 'audio/mpeg'; // MP3 文件的 MIME 类型

    var mediaSource;
    var sourceBuffer;
    var queue = []; // 队列用于存储待添加的数据
    var fetchController; // 用于取消 fetch 请求的控制器

    function startAudio() {
      if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
        mediaSource = new MediaSource();
        audio.src = URL.createObjectURL(mediaSource);

        mediaSource.addEventListener('sourceopen', function() {
          sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
          sourceBuffer.addEventListener('updateend', onUpdateEnd);
          fetchAndAppendData(assetURL);
        });

        audio.addEventListener('error', onError);
      } else {
        console.error('Unsupported MIME type: ', mimeCodec);
      }
    }

    function fetchAndAppendData(url) {
      fetchController = new AbortController();
      fetch(url, { signal: fetchController.signal })
        .then(response => {
          if (!response.ok) {
            throw new Error('Failed to fetch data');
          }
          return response.body.getReader();
        })
        .then(reader => {
          function read() {
            reader.read().then(({ done, value }) => {
              if (done) {
                mediaSource.endOfStream();
                return;
              }
              queue.push(value);
              appendNextBuffer();
              read(); // 继续读取下一段数据
            }).catch(error => {
              console.error('Error reading stream:', error);
            });
          }
          read(); // 开始读取数据并追加到 SourceBuffer
        })
        .catch(error => {
          console.error('Error fetching data or appending to SourceBuffer:', error);
        });
    }

    function appendNextBuffer() {
      if (sourceBuffer && !sourceBuffer.updating && queue.length > 0) {
        try {
          sourceBuffer.appendBuffer(queue.shift());
          if (audio.paused && !audio.seeking) {
            audio.play().catch(error => {
              console.error('Failed to start playback:', error);
            });
          }
        } catch (error) {
          console.error('Error appending buffer:', error);
        }
      }
    }

    function onUpdateEnd() {
      appendNextBuffer();
    }

    function onError(event) {
      var mediaError = event.target.error;
      if (mediaError) {
        console.error('MediaError:', mediaError);
        if (fetchController) {
          fetchController.abort();
        }
        // 可以根据需要添加恢复逻辑,例如重新加载音频或提示用户
      }
    }
  </script>
</body>
</html>

改进后的代码解释:

  1. 流式读取数据

    • 使用 fetch API 和 ReadableStream 的读取器逐步读取数据,并在读取到数据块时将其存储在 queue 队列中。
    • 每次读取到数据后,调用 appendNextBuffer() 尝试将数据追加到 SourceBuffer 中。
  2. 异步追加数据

    • appendNextBuffer() 函数检查 SourceBuffer 是否可用且不处于更新状态,如果满足条件,则从 queue 队列中取出数据块并追加到 SourceBuffer 中。
    • 在追加数据时,确保音频播放。如果音频处于暂停状态且没有正在寻找新播放位置,则尝试播放音频。
  3. 错误处理

    • audio 元素上监听 error 事件,捕获并处理媒体播放过程中出现的错误。
    • 如果出现错误,调用 fetchController.abort() 取消当前的 fetch 请求,并可以根据需要添加恢复逻辑。

通过这种方式,确保在数据逐步加载到 SourceBuffer 时就能开始播放音频,而不需要等待整个流加载完毕。这种方法能够提高用户体验,使用户在数据加载过程中也能开始听到音频。

if (audio.paused && !audio.seeking) { audio.play().catch(error => { console.error('Failed to start playback:', error); }); } 只要达到可以播放的条件,哪怕只有1s的时长也播放,怎么来控制

为了实现只要达到一定的数据量就播放音频(例如确保缓冲到一定的秒数或者字节数),可以通过监听缓冲的进度并在缓冲到一定程度时启动播放。这里我们可以使用 audio.buffered 属性来检查当前缓冲的范围。

我们可以设定一个最小缓冲时间,当缓冲时间达到这个值时才开始播放,并在数据追加过程中不断检查缓冲的状态。

改进后的前端代码示例:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Media Source Extensions (MSE) Example for MP3</title>
</head>
<body>
  <button onclick="startAudio()">播放音频</button>
  <audio id="myAudio" controls></audio>

  <script>
    var audio = document.getElementById('myAudio');
    var assetURL = 'https://example.com/your-audio-stream.mp3'; // 替换成你的 MP3 文件地址
    var mimeCodec = 'audio/mpeg'; // MP3 文件的 MIME 类型

    var mediaSource;
    var sourceBuffer;
    var queue = []; // 队列用于存储待添加的数据
    var fetchController; // 用于取消 fetch 请求的控制器
    var minBufferTime = 5; // 设置最小缓冲时间(秒)

    function startAudio() {
      if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
        mediaSource = new MediaSource();
        audio.src = URL.createObjectURL(mediaSource);

        mediaSource.addEventListener('sourceopen', function() {
          sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
          sourceBuffer.addEventListener('updateend', onUpdateEnd);
          fetchAndAppendData(assetURL);
        });

        audio.addEventListener('error', onError);
      } else {
        console.error('Unsupported MIME type: ', mimeCodec);
      }
    }

    function fetchAndAppendData(url) {
      fetchController = new AbortController();
      fetch(url, { signal: fetchController.signal })
        .then(response => {
          if (!response.ok) {
            throw new Error('Failed to fetch data');
          }
          return response.body.getReader();
        })
        .then(reader => {
          function read() {
            reader.read().then(({ done, value }) => {
              if (done) {
                mediaSource.endOfStream();
                return;
              }
              queue.push(value);
              appendNextBuffer();
              read(); // 继续读取下一段数据
            }).catch(error => {
              console.error('Error reading stream:', error);
            });
          }
          read(); // 开始读取数据并追加到 SourceBuffer
        })
        .catch(error => {
          console.error('Error fetching data or appending to SourceBuffer:', error);
        });
    }

    function appendNextBuffer() {
      if (sourceBuffer && !sourceBuffer.updating && queue.length > 0) {
        try {
          sourceBuffer.appendBuffer(queue.shift());
          checkBufferAndPlay(); // 检查缓冲状态并尝试播放
        } catch (error) {
          console.error('Error appending buffer:', error);
        }
      }
    }

    function onUpdateEnd() {
      appendNextBuffer();
    }

    function checkBufferAndPlay() {
      if (audio.buffered.length > 0) {
        var bufferedEnd = audio.buffered.end(0);
        if (bufferedEnd > minBufferTime && audio.paused && !audio.seeking) {
          audio.play().catch(error => {
            console.error('Failed to start playback:', error);
          });
        }
      }
    }

    function onError(event) {
      var mediaError = event.target.error;
      if (mediaError) {
        console.error('MediaError:', mediaError);
        if (fetchController) {
          fetchController.abort();
        }
        // 可以根据需要添加恢复逻辑,例如重新加载音频或提示用户
      }
    }
  </script>
</body>
</html>

代码解释:

  1. 最小缓冲时间 minBufferTime

    • 定义一个变量 minBufferTime,表示播放前至少需要缓冲的数据时间(秒)。
  2. checkBufferAndPlay() 函数

    • 每次数据追加后调用该函数,检查当前缓冲的时间。
    • 使用 audio.buffered 属性获取缓冲的范围,检查第一个缓冲区的结束时间。
    • 如果缓冲的时间超过 minBufferTime,并且音频处于暂停状态且未在寻找新播放位置,则尝试播放音频。

通过这种方式,确保音频在缓冲到一定时间后开始播放,而不是立即播放,即使只缓冲了一小部分数据。这样可以提高播放的平滑性,避免频繁的暂停和缓冲。

audio.src = URL.createObjectURL(mediaSource); 在另外一台机器上运行的时候报错,cannot set properties of null

报错 "cannot set properties of null" 通常是因为 JavaScript 试图访问未定义或为空的对象的属性。根据你的描述,问题可能出在尝试设置 audio.srcaudio 元素没有正确引用。请确保页面加载完毕后再运行 JavaScript 代码,或者确保 audio 元素在 DOM 中存在且已加载。

我们可以通过在 DOMContentLoaded 事件触发后再执行 JavaScript 代码来解决这个问题。

改进后的前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Media Source Extensions (MSE) Example for MP3</title>
</head>
<body>
  <button onclick="startAudio()">播放音频</button>
  <audio id="myAudio" controls></audio>

  <script>
    document.addEventListener('DOMContentLoaded', function () {
      var audio = document.getElementById('myAudio');
      var assetURL = 'https://example.com/your-audio-stream.mp3'; // 替换成你的 MP3 文件地址
      var mimeCodec = 'audio/mpeg'; // MP3 文件的 MIME 类型

      var mediaSource;
      var sourceBuffer;
      var queue = []; // 队列用于存储待添加的数据
      var fetchController; // 用于取消 fetch 请求的控制器
      var minBufferTime = 5; // 设置最小缓冲时间(秒)

      function startAudio() {
        if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
          mediaSource = new MediaSource();
          audio.src = URL.createObjectURL(mediaSource);

          mediaSource.addEventListener('sourceopen', function() {
            sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
            sourceBuffer.addEventListener('updateend', onUpdateEnd);
            fetchAndAppendData(assetURL);
          });

          audio.addEventListener('error', onError);
        } else {
          console.error('Unsupported MIME type: ', mimeCodec);
        }
      }

      function fetchAndAppendData(url) {
        fetchController = new AbortController();
        fetch(url, { signal: fetchController.signal })
          .then(response => {
            if (!response.ok) {
              throw new Error('Failed to fetch data');
            }
            return response.body.getReader();
          })
          .then(reader => {
            function read() {
              reader.read().then(({ done, value }) => {
                if (done) {
                  mediaSource.endOfStream();
                  return;
                }
                queue.push(value);
                appendNextBuffer();
                read(); // 继续读取下一段数据
              }).catch(error => {
                console.error('Error reading stream:', error);
              });
            }
            read(); // 开始读取数据并追加到 SourceBuffer
          })
          .catch(error => {
            console.error('Error fetching data or appending to SourceBuffer:', error);
          });
      }

      function appendNextBuffer() {
        if (sourceBuffer && !sourceBuffer.updating && queue.length > 0) {
          try {
            sourceBuffer.appendBuffer(queue.shift());
            checkBufferAndPlay(); // 检查缓冲状态并尝试播放
          } catch (error) {
            console.error('Error appending buffer:', error);
          }
        }
      }

      function onUpdateEnd() {
        appendNextBuffer();
      }

      function checkBufferAndPlay() {
        if (audio.buffered.length > 0) {
          var bufferedEnd = audio.buffered.end(0);
          if (bufferedEnd > minBufferTime && audio.paused && !audio.seeking) {
            audio.play().catch(error => {
              console.error('Failed to start playback:', error);
            });
          }
        }
      }

      function onError(event) {
        var mediaError = event.target.error;
        if (mediaError) {
          console.error('MediaError:', mediaError);
          if (fetchController) {
            fetchController.abort();
          }
          // 可以根据需要添加恢复逻辑,例如重新加载音频或提示用户
        }
      }
    });
  </script>
</body>
</html>

代码解释:

  1. DOMContentLoaded 事件监听

    • 将所有的 JavaScript 代码包装在 document.addEventListener('DOMContentLoaded', function () { ... }) 中,以确保 DOM 内容完全加载后再执行代码。
  2. 其他部分保持不变

    • 之前的逻辑,包括最小缓冲时间控制、分块加载和播放等保持不变。

通过这种方式,确保 audio 元素在 JavaScript 代码运行时已经存在于 DOM 中,避免了试图访问未定义的元素属性的问题。这样可以解决 "cannot set properties of null" 的错误。

audio.play().catch(error => { 报错 Failed to load because no supported source was found,是什么原因

报错 "Failed to load because no supported source was found" 通常是因为浏览器无法识别或播放提供的媒体文件格式或内容。以下几个方面可以帮助你解决这个问题:

  1. 检查 MIME 类型:确保 mimeCodec 变量的值正确。
  2. 检查媒体文件 URL:确保 assetURL 变量指向一个有效的 MP3 文件。
  3. 检查网络请求:确保 fetch 请求成功,并且服务器正确返回音频数据。

改进后的代码:

  1. 确保 MIME 类型和媒体文件 URL 正确

    • 确认 assetURL 指向一个有效的 MP3 文件。
    • 确认 mimeCodec 的值为 audio/mpeg
  2. 添加更多的调试信息

    • 添加更多的 console.log 语句,帮助确认哪个步骤出现了问题。

完整的前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Media Source Extensions (MSE) Example for MP3</title>
</head>
<body>
  <button onclick="startAudio()">播放音频</button>
  <audio id="myAudio" controls></audio>

  <script>
    document.addEventListener('DOMContentLoaded', function () {
      var audio = document.getElementById('myAudio');
      var assetURL = 'https://example.com/your-audio-stream.mp3'; // 替换成你的 MP3 文件地址
      var mimeCodec = 'audio/mpeg'; // MP3 文件的 MIME 类型

      var mediaSource;
      var sourceBuffer;
      var queue = []; // 队列用于存储待添加的数据
      var fetchController; // 用于取消 fetch 请求的控制器
      var minBufferTime = 5; // 设置最小缓冲时间(秒)

      function startAudio() {
        console.log('Starting audio...');
        if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
          mediaSource = new MediaSource();
          audio.src = URL.createObjectURL(mediaSource);
          console.log('MediaSource created and URL set:', audio.src);

          mediaSource.addEventListener('sourceopen', function() {
            sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
            sourceBuffer.addEventListener('updateend', onUpdateEnd);
            console.log('SourceBuffer created');
            fetchAndAppendData(assetURL);
          });

          audio.addEventListener('error', onError);
        } else {
          console.error('Unsupported MIME type: ', mimeCodec);
        }
      }

      function fetchAndAppendData(url) {
        console.log('Fetching data from:', url);
        fetchController = new AbortController();
        fetch(url, { signal: fetchController.signal })
          .then(response => {
            if (!response.ok) {
              throw new Error('Failed to fetch data');
            }
            console.log('Fetch successful');
            return response.body.getReader();
          })
          .then(reader => {
            function read() {
              reader.read().then(({ done, value }) => {
                if (done) {
                  mediaSource.endOfStream();
                  console.log('End of stream');
                  return;
                }
                queue.push(value);
                appendNextBuffer();
                read(); // 继续读取下一段数据
              }).catch(error => {
                console.error('Error reading stream:', error);
              });
            }
            read(); // 开始读取数据并追加到 SourceBuffer
          })
          .catch(error => {
            console.error('Error fetching data or appending to SourceBuffer:', error);
          });
      }

      function appendNextBuffer() {
        if (sourceBuffer && !sourceBuffer.updating && queue.length > 0) {
          try {
            console.log('Appending buffer...');
            sourceBuffer.appendBuffer(queue.shift());
            checkBufferAndPlay(); // 检查缓冲状态并尝试播放
          } catch (error) {
            console.error('Error appending buffer:', error);
          }
        }
      }

      function onUpdateEnd() {
        console.log('Update end');
        appendNextBuffer();
      }

      function checkBufferAndPlay() {
        if (audio.buffered.length > 0) {
          var bufferedEnd = audio.buffered.end(0);
          console.log('Buffered end:', bufferedEnd);
          if (bufferedEnd > minBufferTime && audio.paused && !audio.seeking) {
            console.log('Starting playback...');
            audio.play().catch(error => {
              console.error('Failed to start playback:', error);
            });
          }
        }
      }

      function onError(event) {
        var mediaError = event.target.error;
        if (mediaError) {
          console.error('MediaError:', mediaError);
          if (fetchController) {
            fetchController.abort();
          }
          // 可以根据需要添加恢复逻辑,例如重新加载音频或提示用户
        }
      }
    });
  </script>
</body>
</html>

进一步调试步骤:

  1. 确认 URL

    • 在浏览器中直接打开 assetURL,确保 URL 指向有效的 MP3 文件。
  2. 查看控制台输出

    • 运行页面后,查看浏览器控制台中的输出信息,以确定哪个步骤出现了问题。
  3. 检查网络请求

    • 使用浏览器的开发者工具检查网络请求,确认 MP3 文件正确返回。

通过这些步骤和改进,可以确保音频在缓冲到一定时间后开始播放,并捕获和处理潜在的错误。如果还是出现问题,请检查 MP3 文件的格式和服务器响应的头部信息,以确保文件格式和 MIME 类型正确。

我要播放是V3 类型的录音文件,需要怎么来更改呢,

要播放 V3 类型的录音文件,我们需要确保 MIME 类型和 codecs 正确。在确保录音文件的 MIME 类型和 codecs 的情况下,我们可以调整代码来处理 V3 文件。

假设 V3 文件的 MIME 类型和 codecs 是 audio/v3,我们需要做以下更改:

  1. 更改 MIME 类型:将 mimeCodec 变量的值更改为适合 V3 文件的 MIME 类型和 codecs。
  2. 检查浏览器支持:确保浏览器支持播放 V3 文件。

以下是更新后的代码示例:

更新的前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Media Source Extensions (MSE) Example for V3 Audio</title>
</head>
<body>
  <button onclick="startAudio()">播放音频</button>
  <audio id="myAudio" controls></audio>

  <script>
    document.addEventListener('DOMContentLoaded', function () {
      var audio = document.getElementById('myAudio');
      var assetURL = 'https://example.com/your-audio-stream.v3'; // 替换成你的 V3 文件地址
      var mimeCodec = 'audio/v3'; // V3 文件的 MIME 类型

      var mediaSource;
      var sourceBuffer;
      var queue = []; // 队列用于存储待添加的数据
      var fetchController; // 用于取消 fetch 请求的控制器
      var minBufferTime = 5; // 设置最小缓冲时间(秒)

      function startAudio() {
        console.log('Starting audio...');
        if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
          mediaSource = new MediaSource();
          audio.src = URL.createObjectURL(mediaSource);
          console.log('MediaSource created and URL set:', audio.src);

          mediaSource.addEventListener('sourceopen', function() {
            sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
            sourceBuffer.addEventListener('updateend', onUpdateEnd);
            console.log('SourceBuffer created');
            fetchAndAppendData(assetURL);
          });

          audio.addEventListener('error', onError);
        } else {
          console.error('Unsupported MIME type: ', mimeCodec);
        }
      }

      function fetchAndAppendData(url) {
        console.log('Fetching data from:', url);
        fetchController = new AbortController();
        fetch(url, { signal: fetchController.signal })
          .then(response => {
            if (!response.ok) {
              throw new Error('Failed to fetch data');
            }
            console.log('Fetch successful');
            return response.body.getReader();
          })
          .then(reader => {
            function read() {
              reader.read().then(({ done, value }) => {
                if (done) {
                  mediaSource.endOfStream();
                  console.log('End of stream');
                  return;
                }
                queue.push(value);
                appendNextBuffer();
                read(); // 继续读取下一段数据
              }).catch(error => {
                console.error('Error reading stream:', error);
              });
            }
            read(); // 开始读取数据并追加到 SourceBuffer
          })
          .catch(error => {
            console.error('Error fetching data or appending to SourceBuffer:', error);
          });
      }

      function appendNextBuffer() {
        if (sourceBuffer && !sourceBuffer.updating && queue.length > 0) {
          try {
            console.log('Appending buffer...');
            sourceBuffer.appendBuffer(queue.shift());
            checkBufferAndPlay(); // 检查缓冲状态并尝试播放
          } catch (error) {
            console.error('Error appending buffer:', error);
          }
        }
      }

      function onUpdateEnd() {
        console.log('Update end');
        appendNextBuffer();
      }

      function checkBufferAndPlay() {
        if (audio.buffered.length > 0) {
          var bufferedEnd = audio.buffered.end(0);
          console.log('Buffered end:', bufferedEnd);
          if (bufferedEnd > minBufferTime && audio.paused && !audio.seeking) {
            console.log('Starting playback...');
            audio.play().catch(error => {
              console.error('Failed to start playback:', error);
            });
          }
        }
      }

      function onError(event) {
        var mediaError = event.target.error;
        if (mediaError) {
          console.error('MediaError:', mediaError);
          if (fetchController) {
            fetchController.abort();
          }
          // 可以根据需要添加恢复逻辑,例如重新加载音频或提示用户
        }
      }
    });
  </script>
</body>
</html>

代码解释:

  1. 更改 MIME 类型

    • mimeCodec 设置为 'audio/v3'(假设这是 V3 文件的 MIME 类型)。如果你知道确切的 MIME 类型和 codecs,请替换为正确的值。
  2. 确保正确的 URL

    • 确保 assetURL 指向一个有效的 V3 文件。
  3. 调试信息

    • 添加了更多的 console.log 语句以帮助调试。

进一步调试步骤:

  1. 确认 URL

    • 在浏览器中直接打开 assetURL,确保 URL 指向有效的 V3 文件。
  2. 查看控制台输出

    • 运行页面后,查看浏览器控制台中的输出信息,以确定哪个步骤出现了问题。
  3. 检查网络请求

    • 使用浏览器的开发者工具检查网络请求,确认 V3 文件正确返回。

通过这些步骤和改进,可以确保 V3 文件在缓冲到一定时间后开始播放,并捕获和处理潜在的错误。如果 MIME 类型和 codecs 不正确,请根据 V3 文件的实际类型进行调整。

Logo

新一代开源开发者平台 GitCode,通过集成代码托管服务、代码仓库以及可信赖的开源组件库,让开发者可以在云端进行代码托管和开发。旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐