24

Web sitesinde gerçek zamanlı videoları göstermek için MSE (Medya Kaynağı Uzantıları) kullanmaya çalışıyoruz. Websocket üzerinden kareler gönderiyoruz ve gecikmeyi önlemek için en zorlarımızı deniyoruz. Mevcut prototipimiz IE, Edge, Chrome, Safari vb. Çok iyi akışa sahip. Elimizdeki sorun, IE ve Edge'in video oynatmaya başlamadan önce yaklaşık 3-5 saniye ara vermemesidir. Bu bizim kullanım durumumuzda kabul edilemez (güvenlik kameralarından canlı video). Bazı özellik ya da benzerler olup olmadığını merak ediyoruz (önyükleme ayarını denemedik = hiçbiri, hiçbir başarı olmadan) bu arabelleği kaldıran? İlk diğer çerçeve sourceBuffer'a eklendiğinde, diğer tüm tarayıcılar mutlu çalmaya başlar ve aynı davranışı IE/Edge'den istiyoruz. Bizim için önerebileceğiniz başka bir çözüm var mı?Medya Kaynak Uzantıları'nı kullanarak IE/Edge'de video arabelleğe alma

çerçeveleri Burada ISO BMFF format

içindedir Ben playing.It bir WebSocket'e üzerinden gelen veriyi taklit için bir aralık kullanılmaktadır başlar videoya eklenir birinci çerçeveden süreyi hesaplar oluşturduğunuz bir reproducing example olduğunu.

Sonuçlar: Bunu incelemek istiyorsanız

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

Here mp4 dosyasıdır.

+1

Örnek bağlantı 404 – totaam

+0

@totaam Dosyaları Google Drive'dan github'a taşıdım ve bağlantıları güncelledim. Şimdi tekrar çalışmalıyım – oskbor

cevap

7

Videoyu IE'ye veya Edge'e gönderirken, aşağıdaki Javascript'i kullanın. Benim için çalıştı. this MSDN example'un basitleştirilmiş bir sürümü olarak Here it is in GitHub. Bilgisayarımda video neredeyse anında oynatılıyor.

  • GPAC yükleyicilerini karşıdan yükleyin here.
  • Çalıştırın ve mp4box'ı yükleyin.
  • Run mp4box -dash 10000 -frag 1000 -rap path\to\ie_5s.mp4

Şimdi, orijinal .mp4 yanında dosyaların bir grup var olacaktır.

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

bir .xml dosyaya .mpd dosyasını yeniden adlandırın.

Daha sonra yeni bir .html dosyası oluşturun aynı dizini. Aşağıdaki kodu yapıştırın:

<!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> 

Ayrıca aynı dizinde yeni .js dosyası oluşturun.
/*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); 

}()); 

Ben Kenara Kadar Bir Web sunucusundan index.html dosyayı hizmet veya IE 11+ anında videosu gelir zaman. Zaman geçirme ve eğer ilgilenirseniz, demoyu sizin için görmeye ev sahipliği yapacağım.

+2

Merhaba, sadece örneğinizi inceledim ama bugün kurmak için zamanım olmadı.Tüm segmentler zaten sunucuda olduğundan, videonun oynatılmasının sebebi sadece IE/Edge'in oynatmaya başlamak için yeterince hızlı bir şekilde arabelleğe alınabilmesidir. Neler olup bittiğini takip ederek zor bir zaman geçiriyorum bu yüzden yanılmış olabilirim :) Benim durumumda, çerçeveler gerçek zamanlı olarak websistemin üzerinden geliyor, böylece sunucudan gelebilecek herhangi bir segment yok. – oskbor

+0

"... videonun oynatılmasının sadece IE/Edge'in oynatmaya başlamak için yeterince hızlı bir şekilde tamponlayabilmesinin nedeni değil mi?" Gayet haklı olabilirsiniz. Hmm. Ben de çok iyi anlamıyorum. :) –

+0

"... Benim durumumda, çerçeveler gerçek zamanlı olarak websistemin üzerinden geliyor ..." Bunun test edilmesi için bir örneğe sahip olmak harika olurdu. Kurulumunuzu nasıl yeniden üreteceğiniz hakkında daha fazla bilgi gönderir misiniz? [Gönderen örnek] (https://ea7e83c432c066554d3ef3e6580aa1af9bd594a1.googledrive.com/host/0BwqnHXhKkJ7mfjF5clFzSUFSY3pjSzhxZlA0TnpIeEo2bHlaY2FZd3RhWDFHY1o2eXZsYXc/ie_5s_buffer.html) gönderdiğiniz herhangi bir tarayıcıda benim için çalışmıyor. Söylemek istediğim, mp4 yerine canlı video beslemesi kullanmamız gerektiğidir. Merak etme, mp4'ü neden ilk etapta yayınladın? –

8

IE arabelleğe alma, MP4 TRUN kutusundaki örnek süresine bakarak gerçekleştirilir. Belki de 5 saniyelik sahte veri ekleyerek çözebilir ve sonra videoyu oynatmaya başladığında bunu kaldırabilirsiniz.

+2

Bu işe yarayabilir, ancak çok karmaşık bir çözüm gibi geliyor. Yine de harika bir fikir! – oskbor

+2

Referans bağlantınız var mı? – aergistal

+2

Örnek süre, TRUN kutusunda ayarlanır: https://msdn.microsoft.com/en-us/library/ff469478.aspx – Lekoaf

İlgili konular