javascript - Media Source Extensions fungerar inte
Jag försöker använda MediaSource API:et för att lägga till separata WebM-videor till en enda källa.
Jag hittade ett Github-projekt som försökte göra samma sak, där en spellista med WebM-filer laddas, och varje fil läggs till som en SourceBuffer. Men det senast gjordes en commit för ett år sedan, och är därför ur synk med den aktuella specifikationen. Så jag gjorde en fork och uppdaterade till de senaste API-egenskaperna/metoderna, plus lite omstrukturering. Mycket av den befintliga koden togs direkt från specifikationens exempel och Eric Bidelmans testsida.
Jag kan dock inte få det att fungera som förväntat. Jag testar i två webbläsare, båda på Mac OS X 10.9.2: Chrome 35 stable (den senaste vid tidpunkten för detta skrivande), och Firefox 30 beta med flaggan media.mediasource.enabled satt till true i about:config (denna funktion kommer inte att introduceras förrän FF 25, och nuvarande stabila version är 24).
Här är de problem jag stöter på.
Båda webbläsarna
Jag vill att videon i slutändan ska vara en lång video sammansatt av de 11 WebM-filerna (00.webm, 01.webm, …, 10.webm). Just nu spelar varje webbläsare bara upp 1 segment av videon.
Chrome
Vilt inkonsekvent beteende. Verkar omöjligt att reproducera någon av dessa buggar på ett pålitligt sätt.
- Ibland är videon tom, eller har en hög svart stapel i mitten av den, och går inte att spela.
- Ibland kommer videon att laddas och pausa på det första bildrutan av 01.webm.
- Ibland kommer videon att spela upp några få ramar av 02.webm och pausa, efter att endast ha laddat de första tre segmenten.
- Spela upp-knappen är initialt nedtonad.
- Att trycka på den nedtonade Spela-knappen ger mycket inkonsekventa beteenden. Ibland laddar den en svart, ospelbar video. Andra gånger spelar den första segmentet, sedan, när du når slutet, stannar det, och när du trycker på Spela/Pausa igen, laddar det nästa segment. Även då hoppar det ibland över segment och fastnar på 04.webm. Oavsett vad spelar det aldrig det sista segmentet, även om konsolen rapporterar att den har gått igenom alla buffrar.
Det är ärligt talat olika varje gång. Jag kan inte lista dem alla här.
Kända begränsningar: Chrome implementerar för närvarande inte sourceBuffer.mode, även om jag inte vet vilken effekt detta kan ha.
Firefox
- Spelar bara 00.webm. Total speltid är 0:08, längden på den videon.
- Videosökning fungerar inte. (Detta kan vara förväntat beteende, eftersom det egentligen inte händer något i onSeeking-händelsehanteraren.)
- Videon kan inte startas om när den är klar.
Min ursprungliga teori var att detta hade att göra med mediaSource.sourceBuffers[0].timestampOffset=duration och duration=mediaSource.duration. Men jag verkar inte kunna få något tillbaka från mediaSource.duration annat än NaN, även om jag lägger till nya segment.
Helt förlorad här. Vägledning uppskattas mycket.
REDIGERA: Jag tog bort kommentarerna från varaktighetsdelarna av koden och körde mse_webm_remuxer från Aaron Colwells Media Source Extension Tools (tack Adam Hart för tipsen) på alla videorna. Voila, inga fler oförutsägbara glitchar i Chrome! Men tyvärr pausar det fortfarande när ett mediasegment tar slut, och även när du trycker på play fastnar det ibland på en bildruta.
I Firefox Beta spelas det inte förbi den första segmentet, utan svarar med:
TypeError: Värdet som tilldelas SourceBuffer.timestampOffset är inte ett ändligt flyttal.
Att logga värdet av duration returnerar NaN (men bara i FF).
Svar #1
Huvudproblemet är med videofilerna. Om du öppnar chrome://media-internals/ kan du se error Media segment did not begin with keyframe. Med korrekt formaterade videor, som den från Eric Bidelmans exempel (jag hoppas att han inte blir arg över att jag fortsätter länka direkt till den videon, men det är den enda exempelvideon jag har hittat som fungerar), fungerar din kod med följande ändring i appendNextMediaSegment():
duration = mediaSource.duration;
mediaSource.sourceBuffers[0].timestampOffset = duration;
mediaSource.sourceBuffers[0].appendBuffer(mediaSegment);
Du kan prova Aaron Colwells Media Source Extension Tools för att försöka få dina videor att fungera, men jag har haft begränsad framgång.
Det verkar också lite konstigt att du tittar på onProgress-händelsen innan du lägger till segment, men jag antar att det kan fungera om du bara vill lägga till om videon faktiskt spelas. Det kan göra att sökfältet beter sig konstigt eftersom videons längd är okänd, men det kan vara ett problem i vilket fall som helst.
Svar #2
Jag håller med om den åsikt Adam Hart uttryckte. Med en webm-fil försökte jag implementera ett exempel som http://html5-demos.appspot.com/static/media-source.html och drog sedan slutsatsen att problemet orsakades av den källfil jag använde.
Om du har en pil kvar, varför inte prova att använda "samplemuxer" som introduceras på https://developer.mozilla.org/en-US/docs/Web/HTML/DASH_Adaptive_Streaming_for_HTML_5_Video. Enligt min åsikt är samplemuxer en av kodarna som FFMPEG.
Jag upptäckte att den konverterade filen fungerar med mediaSource API. Om du också ser att det fungerar, vänligen låt mig veta.
<!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>
ett annat testkod
<!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>
Tack.
