w3schools tag tab page change javascript video-streaming internet-explorer-11 microsoft-edge media-source

javascript - tag - Búfer de video en IE/Edge usando Extensiones de origen de medios



title of page html (2)

Estamos intentando usar MSE (Extensiones de origen de medios) para mostrar videos en tiempo real en un sitio web. Estamos enviando marcos a través de websocket y estamos haciendo todo lo posible para mantener la latencia baja. Nuestro prototipo actual está transmitiendo muy bien en IE, Edge, Chrome, Safari, etc. El problema que tenemos es que IE y Edge insisten en almacenar en búfer entre 3-5 segundos antes de comenzar a reproducir el video. Esto no es aceptable en nuestro caso de uso (video en vivo de cámaras de seguridad). Nos preguntamos si hay alguna propiedad o similar (hemos intentado configurar preload = none, sin éxito) que elimina este almacenamiento en búfer. Todos los demás navegadores comienzan a reproducirse alegremente cuando se agrega el primer fotograma al sourceBuffer, y queremos el mismo comportamiento de IE / Edge. ¿Hay alguna otra solución que pueda sugerirnos?

Los marcos están en el formato ISO BMFF.

Este es un ejemplo de reproducción que he creado que mide el tiempo desde que se agrega el primer fotograma hasta que el video comienza a reproducirse. Utiliza un intervalo para falsificar los datos que llegan a través de un websocket.

Resultados:

Browser Delay(ms) ----------------------- Chrome: ~300 Safari @ Mac: ~7 Chrome @ Android: ~30 IE11 @ Win10: ~3200 Edge: ~3200

Here está el archivo mp4, si desea examinarlo.


Cuando sirves el video a IE o a Edge, usa el siguiente Javascript. Funcionó para mí. Aquí está en GitHub como una versión simplificada de este ejemplo de MSDN . En mi computadora, el video se reproduce casi instantáneamente.

  • Descarga los instaladores de GPAC here .
  • Ejecutalo e instala mp4box.
  • Ejecute mp4box -dash 10000 -frag 1000 -rap path/to/ie_5s.mp4

Ahora tendrá un montón de archivos junto con su .mp4 original.

ie_5s.mp4 ie_5s_dash.mpd ie_5s_dashinit.mp4 out_ie_5s.mp4

Cambie el nombre del archivo .mpd a un archivo .xml .

Luego crear un nuevo archivo .html es el mismo directorio. Pega el siguiente código:

<!DOCTYPE html> <html> <!-- Media streaming example Reads an .mpd file created using mp4box and plays the file --> <head> <meta charset="utf-8" /> <title>Media streaming example</title> </head> <body> <input type="text" id="filename" value="ie_5s_dash.xml" /> <button id="load">Play</button> <br /> <video id="myVideo" autoplay="autoplay">No video available</video> <script src="index.js"></script> </body> </html>

También cree un nuevo archivo .js en el mismo directorio.

/*globals window, console, XMLHttpRequest, document, Uint8Array, DOMParser, URL*/ (function () { /* code */ ''use strict''; // Global Parameters from .mpd file var file; // MP4 file var width; // Native width and height var height; // Elements var videoElement = document.getElementById(''myVideo''); var playButton = document.getElementById("load"); videoElement.poster = "poster.png"; // Description of initialization segment, and approx segment lengths var initialization; // Video parameters var bandwidth; // bitrate of video // Parameters to drive segment loop var index = 0; // Segment to get var segments; // Source and buffers var mediaSource; var videoSource; // Parameters to drive fetch loop var segCheck; var lastTime = 0; var bufferUpdated = false; // Flags to keep things going var lastMpd = ""; var requestId = 0; // Logs messages to the console function log(s) { // send to console // you can also substitute UI here console.log(s); } // Clears the log function clearLog() { console.clear(); } function timeToDownload(range) { var vidDur = range.split("-"); // Time = size * 8 / bitrate return (((vidDur[1] - vidDur[0]) * 8) / bandwidth); } // Play segment plays a byte range (format nnnn-nnnnn) of a media file function playSegment(range, url) { var xhr = new XMLHttpRequest(); if (range || url) { // Make sure we''ve got incoming params xhr.open(''GET'', url); xhr.setRequestHeader("Range", "bytes=" + range); xhr.send(); xhr.responseType = ''arraybuffer''; try { xhr.addEventListener("readystatechange", function () { if (xhr.readyState === xhr.DONE) { //wait for video to load // Calculate when to get next segment based on time of current one segCheck = (timeToDownload(range) * 0.8).toFixed(3); // Use point eight as fudge factor // Add received content to the buffer try { videoSource.appendBuffer(new Uint8Array(xhr.response)); } catch (e) { log(''Exception while appending'', e); } } }, false); } catch (e) { log(e); return; // No value for range } } } // Get video segments function fileChecks() { // If we''re ok on the buffer, then continue if (bufferUpdated === true) { if (index < segments.length) { // Loads next segment when time is close to the end of the last loaded segment if ((videoElement.currentTime - lastTime) >= segCheck) { playSegment(segments[index].getAttribute("mediaRange").toString(), file); lastTime = videoElement.currentTime; index++; } } else { videoElement.removeEventListener("timeupdate", fileChecks, false); } } } // Play our file segments function getStarted(url) { // Start by loading the first segment of media playSegment(segments[index].getAttribute("mediaRange").toString(), url); // Display current index index++; // Continue in a loop where approximately every x seconds reload the buffer videoElement.addEventListener("timeupdate", fileChecks, false); } function updateFunct() { // This is a one shot function, when init segment finishes loading, // update the buffer flag, call getStarted, and then remove this event. bufferUpdated = true; getStarted(file); // Get video playback started // Now that video has started, remove the event listener videoSource.removeEventListener("update", updateFunct); } // Load video''s initialization segment function initVideo(range, url) { var xhr = new XMLHttpRequest(); if (range || url) { // make sure we''ve got incoming params // Set the desired range of bytes we want from the mp4 video file xhr.open(''GET'', url); xhr.setRequestHeader("Range", "bytes=" + range); segCheck = (timeToDownload(range) * 0.8).toFixed(3); // use point eight as fudge factor xhr.send(); xhr.responseType = ''arraybuffer''; try { xhr.addEventListener("readystatechange", function () { if (xhr.readyState === xhr.DONE) { // wait for video to load // Add response to buffer try { videoSource.appendBuffer(new Uint8Array(xhr.response)); // Wait for the update complete event before continuing videoSource.addEventListener("update", updateFunct, false); } catch (e) { log(''Exception while appending initialization content'', e); } } }, false); } catch (e) { log(e); } } else { return; // No value for range or url } } // Create mediaSource and initialize video function setupVideo() { clearLog(); // Clear console log // Create the media source if (window.MediaSource) { mediaSource = new window.MediaSource(); } else { log("mediasource or syntax not supported"); return; } var url = URL.createObjectURL(mediaSource); videoElement.pause(); videoElement.src = url; videoElement.width = width; videoElement.height = height; // Wait for event that tells us that our media source object is // ready for a buffer to be added. mediaSource.addEventListener(''sourceopen'', function (e) { try { videoSource = mediaSource.addSourceBuffer(''video/mp4''); initVideo(initialization, file); } catch (ex) { log(''Exception calling addSourceBuffer for video'', ex); return; } }, false); // Handler to switch button text to Play videoElement.addEventListener("pause", function () { playButton.innerText = "Play"; }, false); // Handler to switch button text to pause videoElement.addEventListener("playing", function () { playButton.innerText = "Pause"; }, false); } // Retrieve parameters from our stored .mpd file function getFileType(data) { try { file = data.querySelectorAll("BaseURL")[0].textContent.toString(); var rep = data.querySelectorAll("Representation"); width = rep[0].getAttribute("width"); height = rep[0].getAttribute("height"); bandwidth = rep[0].getAttribute("bandwidth"); var ini = data.querySelectorAll("Initialization"); initialization = ini[0].getAttribute("range"); segments = data.querySelectorAll("SegmentURL"); } catch (er) { log(er); return; } } // Gets the mpd file and parses it function getData(url) { if (url !== "") { var xhr = new XMLHttpRequest(); // Set up xhr request xhr.open("GET", url, true); // Open the request xhr.responseType = "text"; // Set the type of response expected xhr.send(); // Asynchronously wait for the data to return xhr.onreadystatechange = function () { if (xhr.readyState === xhr.DONE) { var tempoutput = xhr.response; var parser = new DOMParser(); // Create a parser object // Create an xml document from the .mpd file for searching var xmlData = parser.parseFromString(tempoutput, "text/xml", 0); log("parsing mpd file"); // Get and display the parameters of the .mpd file getFileType(xmlData); // Set up video object, buffers, etc setupVideo(); } }; // Report errors if they happen during xhr xhr.addEventListener("error", function (e) { log("Error: " + e + " Could not load url."); }, false); } } // Click event handler for load button playButton.addEventListener("click", function () { // If video is paused then check for file change if (videoElement.paused === true) { // Retrieve mpd file, and set up video var curMpd = document.getElementById("filename").value; // If current mpd file is different then last mpd file, load it. if (curMpd !== lastMpd) { // Cancel display of current video position window.cancelAnimationFrame(requestId); lastMpd = curMpd; getData(curMpd); } else { // No change, just play videoElement.play(); } } else { // Video was playing, now pause it videoElement.pause(); } }, false); // Do a little trickery, start video when you click the video element videoElement.addEventListener("click", function () { playButton.click(); }, false); // Event handler for the video element errors document.getElementById("myVideo").addEventListener("error", function (e) { log("video error: " + e.message); }, false); }());

Cuando sirvo el archivo index.html desde un servidor web a Edge o IE 11+, el video se muestra instantáneamente. Si el tiempo lo permite, y si está interesado, presentaré la demostración en vivo para que la vean.


El almacenamiento en búfer de IE se realiza observando la duración de la muestra en el cuadro MP4 TRUN. Tal vez podría resolverlo agregando ~ 5 segundos de datos falsos y luego eliminar eso una vez que el video comience a reproducirse.