私はBlobの配列(バイナリデータ、本当に-表現できますが、最も効率的です。私は今のところBlobを使用していますが、おそらくUint8Array
または何かが良いでしょう)。各Blobには、1秒のオーディオ/ビデオデータが含まれます。毎秒、新しいBlobが生成され、配列に追加されます。したがって、コードはおおよそ次のようになります。
var arrayOfBlobs = [];
setInterval(function() {
arrayOfBlobs.append(nextChunk());
}, 1000);
私の目標は、このオーディオ/ビデオデータをHTML5要素にストリーミングすることです。 Blob URLは次のように生成および再生できることを知っています。
var src = URL.createObjectURL(arrayOfBlobs[0]);
var video = document.getElementsByTagName("video")[0];
video.src = src;
もちろん、これはビデオの最初の1秒間しか再生しません。また、配列に現在含まれているすべてのBlobをどうにかして連結して、1秒以上再生できると仮定します。
// Something like this (untested)
var concatenatedBlob = new Blob(arrayOfBlobs);
var src = ...
ただし、これでも最終的にはデータが不足します。 Blobは不変なので、受信したデータを追加し続ける方法がわかりません。
YouTubeや他の多くの動画ストリーミングサービスは動画再生にBlob URLを利用しているので、これは可能だと確信しています。 theyどうやって行うのですか?
いくつかの重要なグーグルの後、私はパズルに欠けている部分を見つけることができました: MediaSource
効果的にプロセスは次のようになります。
MediaSource
を作成しますMediaSource
からオブジェクトURLを作成しますsrc
をオブジェクトのURLに設定しますsourceopen
イベントで、SourceBuffer
を作成しますSourceBuffer.appendBuffer()
を使用して、すべてのチャンクをビデオに追加しますこのようにして、オブジェクトURLを変更せずに新しいビデオを追加し続けることができます。
SourceBuffer
オブジェクトは、コーデックについてveryうるさいです。これらは宣言する必要があり、正確でなければなりません。そうでないと機能しません。SourceBuffer
に追加できるのは、ビデオデータのblobを1つだけです。最初のblobが(非同期に)処理を完了するまで、2番目のblobを追加することはできません。.remove()
を呼び出さずにSourceBuffer
に過剰なデータを追加すると、最終的にRAM)が不足し、ビデオの再生が停止します。私のラップトップでこの制限は約1時間設定によっては、この一部が不要になる場合があります(特に、SourceBuffer
を取得する前にビデオデータのキューを作成し、updateend
を使用してゆっくりとキューに追加する部分)。 SourceBuffer
が作成されてビデオデータの取得を開始するまで待つことができる場合、コードはより見栄えがよくなります。
<html>
<head>
</head>
<body>
<video id="video"></video>
<script>
// As before, I'm regularly grabbing blobs of video data
// The implementation of "nextChunk" could be various things:
// - reading from a MediaRecorder
// - reading from an XMLHttpRequest
// - reading from a local webcam
// - generating the files on the fly in JavaScript
// - etc
var arrayOfBlobs = [];
setInterval(function() {
arrayOfBlobs.append(nextChunk());
// NEW: Try to flush our queue of video data to the video element
appendToSourceBuffer();
}, 1000);
// 1. Create a `MediaSource`
var mediaSource = new MediaSource();
// 2. Create an object URL from the `MediaSource`
var url = URL.createObjectURL(mediaSource);
// 3. Set the video's `src` to the object URL
var video = document.getElementById("video");
video.src = url;
// 4. On the `sourceopen` event, create a `SourceBuffer`
var sourceBuffer = null;
mediaSource.addEventListener("sourceopen", function()
{
// NOTE: Browsers are VERY picky about the codec being EXACTLY
// right here. Make sure you know which codecs you're using!
sourceBuffer = mediaSource.addSourceBuffer("video/webm; codecs=\"opus,vp8\"");
// If we requested any video data prior to setting up the SourceBuffer,
// we want to make sure we only append one blob at a time
sourceBuffer.addEventListener("updateend", appendToSourceBuffer);
});
// 5. Use `SourceBuffer.appendBuffer()` to add all of your chunks to the video
function appendToSourceBuffer()
{
if (
mediaSource.readyState === "open" &&
sourceBuffer &&
sourceBuffer.updating === false
)
{
sourceBuffer.appendBuffer(arrayOfBlobs.shift());
}
// Limit the total buffer size to 20 minutes
// This way we don't run out of RAM
if (
video.buffered.length &&
video.buffered.end(0) - video.buffered.start(0) > 1200
)
{
sourceBuffer.remove(0, video.buffered.end(0) - 1200)
}
}
</script>
</body>
</html>
追加のボーナスとして、これにより、ライブストリームにDVR機能が自動的に提供されます。これは、バッファに20分のビデオデータを保持しているためです(video.currentTime = ...
)