J'ai un tableau de Blobs (données binaires, vraiment - je peux l'exprimer cependant est le plus efficace. J'utilise Blobs pour l'instant mais peut-être un Uint8Array
ou quelque chose serait mieux). Chaque Blob contient 1 seconde de données audio/vidéo. Chaque seconde, un nouveau blob est généré et ajouté à mon tableau. Ainsi, le code ressemble à peu près à ceci:
var arrayOfBlobs = [];
setInterval(function() {
arrayOfBlobs.append(nextChunk());
}, 1000);
Mon objectif est de diffuser ces données audio/vidéo vers un élément HTML5. Je sais qu'une URL Blob peut être générée et lue comme suit:
var src = URL.createObjectURL(arrayOfBlobs[0]);
var video = document.getElementsByTagName("video")[0];
video.src = src;
Bien sûr, cela ne joue que la première 1 seconde de vidéo. Je suppose également que je peux concaténer trivialement tous les Blobs actuellement dans mon tableau pour jouer plus d'une seconde:
// Something like this (untested)
var concatenatedBlob = new Blob(arrayOfBlobs);
var src = ...
Cependant, cela finira toujours par manquer de données. Comme les blobs sont immuables, je ne sais pas comment ajouter des données à mesure qu'elles sont reçues.
Je suis certain que cela devrait être possible car YouTube et de nombreux autres services de streaming vidéo utilisent des URL Blob pour la lecture vidéo. Comment ils le font-ils?
Après quelques recherches importantes sur Google, j'ai réussi à trouver la pièce manquante au puzzle: MediaSource
Effectivement, le processus se déroule comme suit:
MediaSource
MediaSource
src
de la vidéo sur l'URL de l'objetsourceopen
, créez un SourceBuffer
SourceBuffer.appendBuffer()
pour ajouter tous vos morceaux à la vidéoDe cette façon, vous pouvez continuer à ajouter de nouveaux morceaux de vidéo sans modifier l'URL de l'objet.
SourceBuffer
est très pointilleux sur les codecs. Ceux-ci doivent être déclarés et doivent être exacts, sinon cela ne fonctionnera pasSourceBuffer
à la fois, et vous ne pouvez pas ajouter un deuxième blob tant que le premier n'a pas terminé (de manière asynchrone) le traitementSourceBuffer
sans appeler .remove()
, vous finirez par manquer de RAM et la vidéo s'arrêtera de jouer. cette limite d'environ 1 heure sur mon ordinateur portableSelon votre configuration, certaines d'entre elles peuvent être inutiles (en particulier la partie où nous construisons une file d'attente de données vidéo avant d'avoir un SourceBuffer
puis ajoutons lentement notre file d'attente à l'aide de updateend
). Si vous pouvez attendre que le SourceBuffer
ait été créé pour commencer à récupérer les données vidéo, votre code sera beaucoup plus agréable.
<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>
En prime, cela vous donne automatiquement DVR fonctionnalité pour les flux en direct, car vous conservez 20 minutes de données vidéo dans votre tampon (vous pouvez rechercher en utilisant simplement video.currentTime = ...
)