6

MediaSource API を使用して、個別の WebM ビデオを単一のソースに追加しようとしています。

WebM のプレイリストが読み込まれ、それぞれが SourceBuffer として追加される、同じことを試みていたGithub プロジェクトを見つけました。しかし、最後にコミットされたのは 1 年前であるため、現在の仕様とは同期していません。そこで私はそれをフォークし、最新の API プロパティ/メソッドに更新し、さらにいくつかの再構築を行いました。既存のコードの多くは、仕様のEric Bidelman のテスト ページから直接取得されました。

ただし、期待どおりに動作させることはできません。Mac OS X 10.9.2 の 2 つのブラウザーでテストしています: Chrome 35 安定版 (この記事の執筆時点で最新) と、フラグが about:config にmedia.mediasource.enabled設定された Firefox 30 ベータ版 (この機能は、 trueFF 25、および現在の安定は 24 です)。

ここに私が遭遇している問題があります。

両方のブラウザ

最終的には、11 個の WebM (00.webm、01.webm、…、10.webm) で構成された 1 つの長いビデオにしたいと考えています。現在、各ブラウザはビデオの 1 つのセグメントのみを再生します。

クロム

非常に一貫性のない行動。これらのバグを確実に再現することは不可能のようです。

  • ビデオが空白になったり、途中に黒いバーが表示されたりして、再生できないことがあります。
  • ビデオがロードされ、01.webm の最初のフレームで一時停止することがあります。
  • 場合によっては、ビデオが 02.webm のいくつかのフレームを再生して一時停止し、最初の 3 つのセグメントのみが読み込まれることがあります。
  • [再生] ボタンは、最初はグレー表示されています。
  • グレー表示された再生ボタンを押すと、非常に一貫性のない動作が発生します。時々、再生できない黒いビデオがロードされます。それ以外の場合は、最初のセグメントを再生し、最後に到達すると停止し、もう一度 [再生/一時停止] を押すと、次のセグメントがロードされます。それでも、セグメントをスキップして 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 の Media Source Extension Tools (ヒントを提供してくれたAdam Hartmse_webm_remuxerに感謝)から実行しました。Chrome で予期せぬ不具合が発生することはもうありません。しかし残念なことに、メディア セグメントが終了すると一時停止し、再生を押しても 1 つのフレームで動かなくなることがあります。

Firefox Beta では、最初のセグメントを超えて再生されず、次のように応答します。

TypeError: SourceBuffer.timestampOffset に割り当てられている値は、有限の浮動小数点値ではありません。

duration戻り値のログNaN(ただし FF のみ)。

4

2 に答える 2

6

主な問題は、ビデオ ファイルにあります。開くと、Eric Bidelman の例の ような適切にフォーマットされたビデオの使用chrome://media-internals/を見ることができます(私がそのビデオに直接リンクし続けていることを彼が怒らないことを願っていますが、これが私が見つけた唯一のサンプル ビデオです)。あなたのコードは、 の次の変更で機能します。error Media segment did not begin with keyframe.appendNextMediaSegment()

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

Aaron Colwell の Media Source Extension Toolsを試してビデオを機能させることができますが、私はあまりうまくいきませんでした。

セグメントを追加する前にイベントを見ているのも少し奇妙にonProgress思えますが、ビデオが実際に再生されている場合にのみ追加したい場合はうまくいくと思います. ビデオの長さが不明であるため、シークバーの動作がおかしくなる可能性がありますが、いずれにしても問題になる可能性があります。

于 2014-06-13T18:45:57.757 に答える
0

Adam Hart さんの意見に賛成です。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 のようなエンコーダーの 1 つです。

変換されたファイルは 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>

ありがとう。

于 2016-04-05T20:16:01.033 に答える