媒体源扩展中的有趣行为



我正在尝试使用媒体源扩展构建一个相当标准的视频播放器;然而,我希望用户能够控制播放器何时移动到新的视频片段。例如,我们可能会看到以下行为:

  1. 视频播放器播放第一段
  2. 源缓冲区数据不足,导致视频显示暂停
  3. 当用户准备好后,他们点击一个按钮,将第二个段添加到源缓冲区
  4. 视频继续播放第二段

这很好,只是当视频在第2步暂停时,它不会停止在第一段的最后一帧。相反,它会在第一段结束前停止两帧。最后两帧并没有被丢弃,只是在用户点击按钮推进视频后播放。这是我的应用程序的一个问题,我正试图找到一种方法,确保在步骤2结束之前播放第一段中的所有帧。

我怀疑最后两帧被视频解码器缓冲区卡住了。特别是在将第一个片段添加到源缓冲区后,在我的媒体源上调用endOfStream((会导致第一个片段一直播放,没有留下任何帧。

附加信息

  • 我使用以下ffmpeg命令从一系列PNG创建了每个视频片段文件

ffmpeg -i %04d.png -movflags frag_keyframe+empty_moov+default_base_moof video_segment.mp4

  • 也许这是线索?未正确处理流结束情况(最后一帧被丢弃(
  • 另一件有趣的事情是,如果视频只有2帧或更少,MSE根本不播放它
  • 我使用的浏览器是Chrome。我的MSE播放器的代码只是取自谷歌开发者的例子,但为了完整起见,我会把它发布在这里。这段代码只涵盖了步骤2,因为这就是问题所在
<script>
const mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', sourceOpen, { once: true });
function sourceOpen() {
URL.revokeObjectURL(video.src);
const sourceBuffer = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.64001f"');
sourceBuffer.mode = 'sequence';
// Fetch the video and add it to the Source Buffer
fetch('https://s3.amazonaws.com/bucket_name/video_file.mp4')
.then(response => response.arrayBuffer())
.then(data => sourceBuffer.appendBuffer(data));
}

这很好,只是当视频在第二步中暂停时,它不会停止在第一段的最后一帧。相反,它会在第一段结束前停止两帧。最后两帧没有被丢弃,它们只是在。。。

此行为取决于浏览器。让我们从规格报价开始:

当媒体元素需要更多数据时,用户代理应尽早将其从HAVE_ENOUGH_data转换为HAVE_FUETURE_data,以便web应用程序能够在不中断播放的情况下做出响应。例如,当当前播放位置在缓冲数据结束前500ms时进行转换,会使应用程序在播放暂停前追加大约500ms的数据。

您看到的行为是,MSE兼容浏览器知道流尚未结束,但也知道数据即将耗尽。它通过改变其就绪状态来表明需要进一步的数据,但它没有义务播放它已经持有的每一帧。它根据当前播放的时钟时间与可用数据的结束时间进入缓冲状态。

尽管上面的链接说。。。

例如,在视频中,这对应于具有当前帧数据的用户代理,而不是下一帧

。。。实际的实现可能会对此进行不同的解释,并过早地切换到HAVE_CURRENT_DATA,即保留更多的视频帧,但知道它还没有结束流,并且缺少更多的帧。这是一种浏览器实现的特殊性,你只需要接受它。

最新更新