【问题标题】:Media Source Extensions Not Working媒体源扩展不起作用
【发布时间】:2014-06-12 23:41:21
【问题描述】:

我正在尝试使用 MediaSource API 将单独的 WebM 视频附加到单个源。

我发现Github project 正在尝试相同的操作,其中加载了 WebM 的播放列表,并且每个都作为 SourceBuffer 附加。但它最后一次提交是在一年前,因此与current spec 不同步。所以我forked it 并更新到最新的 API 属性/方法,加上一些重组。大部分现有代码直接取自规范的examplesEric Bidelman’s test page

但是,我无法让它按预期工作。我正在两个浏览器中进行测试,都在 Mac OS X 10.9.2 上:Chrome 35 stable(在撰写本文时最新)和 Firefox 30 beta,在 about:config 中将标志 media.mediasource.enabled 设置为 true(这个FF 25 之前不会引入该功能,当前稳定版是 24)。

这是我遇到的问题。

两种浏览器

我希望视频最终成为一个由 11 个 WebM(00.webm、01.webm、...、10.webm)组成的长视频。目前,每个浏览器只播放一段视频。

非常不一致的行为。似乎不可能可靠地重现这些错误。

  • 有时视频是空白的,或者中间有一个高高的黑条,并且无法播放。
  • 有时视频会在 01.webm 的第一帧加载并暂停。
  • 有时,视频会播放 02.webm 的几帧并暂停,只加载了前三个片段。
  • 播放按钮最初是灰色的。
  • 按下灰显的播放按钮会产生非常不一致的行为。有时,它会加载黑色、无法播放的视频。其他时候,它会播放第一段,然后,当你到达终点时,它会停止,当你再次按下播放/暂停时,它会加载下一段。即使这样,它有时也会跳过段并卡在 04.webm 上。无论如何,它永远不会播放最后一段,即使控制台会报告通过所有缓冲区。

老实说,每次都不同。我不能在这里一一列出。

已知警告:Chrome 目前没有实现sourceBuffer.mode,虽然我不知道这可能会产生什么影响。

火狐

  1. 仅播放 00.webm。总运行时间为 0:08,即该视频的长度。
  2. 视频搜索不起作用。 (这可能是预期行为,因为 onSeeking 事件处理程序中实际上没有发生任何事情。)
  3. 视频完成后无法重新启动。

我最初的理论是这与mediaSource.sourceBuffers[0].timestampOffset = durationduration = mediaSource.duration 有关。但我似乎无法从 mediaSource.duration 得到任何回报,除了 NaN,即使我正在附加新的片段。

完全迷失在这里。非常感谢您的指导。

编辑:我取消了代码的持续时间部分的注释,并在所有视频中从Aaron Colwell's Media Source Extension Tools 运行mse_webm_remuxer(感谢Adam Hart 的提示)。瞧,Chrome 中不再有不可预测的故障!但可惜的是,一旦媒体片段结束,它仍然会暂停,即使按下播放,它有时也会卡在一帧上。

在 Firefox Beta 中,它不会播放超过第一段,响应为:

TypeError: 分配给 SourceBuffer.timestampOffset 的值不是有限浮点值。

记录duration 的值会返回NaN(但仅限于FF)。

【问题讨论】:

标签: javascript video-streaming html5-video webm media-source


【解决方案1】:

主要问题在于视频文件。如果您打开chrome://media-internals/,您可以看到error Media segment did not begin with keyframe. 使用格式正确的视频,例如the one from Eric Bidelman's example(我希望他不会因为我一直直接链接到该视频而生气,但这是我发现的唯一示例视频工作),您的代码确实可以在appendNextMediaSegment() 中进行以下更改:

duration = mediaSource.duration;
mediaSource.sourceBuffers[0].timestampOffset = duration;
mediaSource.sourceBuffers[0].appendBuffer(mediaSegment);

您可以尝试Aaron Colwell's Media Source Extension Tools 尝试让您的视频正常工作,但我的成功有限。

您在附加片段之前查看onProgress 事件似乎也有点奇怪,但我想如果您只想在视频实际播放时附加,这可能会起作用。由于视频长度未知,这可能会使搜索栏变得奇怪,但这无论如何都可能是个问题。

【讨论】:

  • 感谢您提供信息丰富的回复。然而,这只是一个视频——我不会像 Eric 那样把它分成几块。我想将单独的视频组合在一起。我刚刚从您链接到的所有视频的工具中运行mse_webm_remuxer。瞧,Chrome 中不再有不可预测的故障!但可惜的是,一旦媒体片段结束,它仍然会暂停,即使您按下播放,它有时也会卡在一个帧上。在 Firefox Beta 中,它不会播放超过第一段:TypeError: Value being assigned to SourceBuffer.timestampOffset is not a finite floating-point value. 会将其添加到 OP。
  • 我的意思是背靠背使用他的 12 个完整视频副本(而不是分块),这似乎对我来说可以无缝播放,并且是测试是视频还是 javascript 的好方法,因为它有正确的关键帧和其他任何东西。虽然是的,但我只在 Chrome 中测试过它。如果我有时间,我会在 Firefox 中处理它。
  • 好的,我试过了,它确实适用于 Chrome。仍然无法在 Firefox 中工作,但我的预感是它可能是一个实现问题,其中持续时间属性永远不会从默认值 NaN 更新。
  • 接受这个答案,因为它确实帮助我克服了最初的障碍,即使它不是跨浏览器。我还在 SO(找不到链接)的其他地方了解到,您可以通过使用所有关键帧对其进行编码来让任何 WebM 工作,例如通过在 ffmpeg 中使用 -g 1
  • 从规范看来,将 SourceBuffer 上的 mode 设置为 "sequence" 应该会自动完成此操作,但我还没有让它在 Chrome 中工作。
【解决方案2】:

我同意亚当·哈特所说的观点。使用webm文件,我尝试实现http://html5-demos.appspot.com/static/media-source.html之类的例子,然后得出结论是它的问题导致了我使用的源文件。

如果你有一个左箭头,尝试使用https://developer.mozilla.org/en-US/docs/Web/HTML/DASH_Adaptive_Streaming_for_HTML_5_Video 介绍的“samplemuxer”怎么样。 在我看来,samplemuxer 是 FFMPEG 之类的编码器之一。

我发现转换后的文件可以使用 mediaSource API。如果您也看到它有效,请告诉我。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>MediaSource API Demo</title>
</head>
<body>

<h3>Appending .webm video chunks using the Media Source API</h3>

<section>
    <video controls autoplay width="320" height="240"></video>
    <pre id="log"></pre>
</section>


<script>
    //ORIGINAL CODE http://html5-demos.appspot.com/static/media-source.html

    var FILE = 'IU_output2.webm';

    var NUM_CHUNKS = 5;
    var video = document.querySelector('video');

    var mediaSource = new MediaSource();

    video.src = window.URL.createObjectURL(mediaSource);

    function callback(e) {
        var sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vorbis,vp8"');

        logger.log('mediaSource readyState: ' + this.readyState);

        GET(FILE, function(uInt8Array) {
            var file = new Blob([uInt8Array], {type: 'video/webm'});
            var chunkSize = Math.ceil(file.size / NUM_CHUNKS);

            logger.log('num chunks:' + NUM_CHUNKS);
            logger.log('chunkSize:' + chunkSize + ', totalSize:' + file.size);

            // Slice the video into NUM_CHUNKS and append each to the media element.
            var i = 0;

            (function readChunk_(i) {
                var reader = new FileReader();

                // Reads aren't guaranteed to finish in the same order they're started in,
                // so we need to read + append the next chunk after the previous reader
                // is done (onload is fired).
                reader.onload = function(e) {

                    try {
                        sourceBuffer.appendBuffer(new Uint8Array(e.target.result));
                        logger.log('appending chunk:' + i);
                    }catch(e){
                        console.log(e);
                    }

                    if (i == NUM_CHUNKS - 1) {
                        if(!sourceBuffer.updating)
                             mediaSource.endOfStream();
                    } else {
                        if (video.paused) {
                            video.play(); // Start playing after 1st chunk is appended.
                        }

                        sourceBuffer.addEventListener('updateend', function(e){
                            if( i < NUM_CHUNKS - 1 )
                                readChunk_(++i);
                        });
                    } //end if
                };

                var startByte = chunkSize * i;
                var chunk = file.slice(startByte, startByte + chunkSize);

                reader.readAsArrayBuffer(chunk);
            })(i);  // Start the recursive call by self calling.
        });
    }

    mediaSource.addEventListener('sourceopen', callback, false);
//    mediaSource.addEventListener('webkitsourceopen', callback, false);
//
//    mediaSource.addEventListener('webkitsourceended', function(e) {
//        logger.log('mediaSource readyState: ' + this.readyState);
//    }, false);

    function GET(url, callback) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.responseType = 'arraybuffer';
        xhr.send();

        xhr.onload = function(e) {
            if (xhr.status != 200) {
                alert("Unexpected status code " + xhr.status + " for " + url);
                return false;
            }
            callback(new Uint8Array(xhr.response));
        };
    }
</script>
<script>
    function Logger(id) {
        this.el = document.getElementById('log');
    }
    Logger.prototype.log = function(msg) {
        var fragment = document.createDocumentFragment();
        fragment.appendChild(document.createTextNode(msg));
        fragment.appendChild(document.createElement('br'));
        this.el.appendChild(fragment);
    };

    Logger.prototype.clear = function() {
        this.el.textContent = '';
    };

    var logger = new Logger('log');
</script>
</body>
</html>

另一个测试代码

<!DOCTYPE html>
<html>
<head>
    <title>MediaSource API Demo</title>
</head>
<body>

<h3>Appending .webm video chunks using the Media Source API</h3>

<section>
    <video controls autoplay width="320" height="240"></video>
    <pre id="log"></pre>
</section>


<script>
    //ORIGINAL CODE http://html5-demos.appspot.com/static/media-source.html

    var FILE = 'IU_output2.webm';
    //    var FILE =  'test_movie_output.webm';

    var NUM_CHUNKS = 10;
    var video = document.querySelector('video');

    var mediaSource = new MediaSource();

    video.src = window.URL.createObjectURL(mediaSource);

    function callback(e) {
        var sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vorbis,vp8"');

        logger.log('mediaSource readyState: ' + this.readyState);

        GET(FILE, function(uInt8Array) {
            logger.log('byteLength:' + uInt8Array.byteLength );

            sourceBuffer.appendBuffer(uInt8Array);

        });
    }

    mediaSource.addEventListener('sourceopen', callback, false);
    //    mediaSource.addEventListener('webkitsourceopen', callback, false);
    //
    //    mediaSource.addEventListener('webkitsourceended', function(e) {
    //        logger.log('mediaSource readyState: ' + this.readyState);
    //    }, false);

    function GET(url, callback) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.responseType = 'arraybuffer';
        xhr.send();

        xhr.onload = function(e) {
            if (xhr.status != 200) {
                alert("Unexpected status code " + xhr.status + " for " + url);
                return false;
            }
            callback(new Uint8Array(xhr.response));
        };
    }
</script>
<script>
    function Logger(id) {
        this.el = document.getElementById('log');
    }
    Logger.prototype.log = function(msg) {
        var fragment = document.createDocumentFragment();
        fragment.appendChild(document.createTextNode(msg));
        fragment.appendChild(document.createElement('br'));
        this.el.appendChild(fragment);
    };

    Logger.prototype.clear = function() {
        this.el.textContent = '';
    };

    var logger = new Logger('log');
</script>
</body>
</html>

谢谢。

【讨论】:

    猜你喜欢
    • 2018-05-13
    • 2016-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-15
    • 2019-11-05
    相关资源
    最近更新 更多