Ses ve video önceden yükleme özelliğiyle hızlı oynatma

Kaynakları etkin bir şekilde önceden yükleyerek medya oynatma işlemlerinizi nasıl hızlandırabilirsiniz?

François Beaufort
François Beaufort

Oynatmanın daha hızlı başlatılması videonuzu izleyen veya dinleyen daha fazla kişinin olacağı anlamına gelir. ses'e dokunun. Bu bilinen bir gerçek. Bu makalede, ses ve video oynatmanızı hızlandırmak için kullanabileceğiniz teknikler kullanım alanınıza bağlı olarak kaynakları önceden yükleme.

Katkıda bulunanlar: telif hakkı Blender Foundation | www.blender.org

Medya dosyalarını önceden yüklemenin üç yöntemini anlatacağım. İlk olarak bir profesyonelden bahsedeceğim. ve dezavantajları.

Harika... Ama...
Video önceden yükleme özelliği Web sunucusunda barındırılan benzersiz bir dosya için kullanımı basittir. Tarayıcılar bu özelliği tamamen yoksayabilir.
Kaynak getirme işlemi, HTML belgesi tamamen yüklendiğinde ve ayrıştırılır.
Medya Kaynağı Uzantıları (MSE), medya öğelerinde preload özelliğini yoksayar MSE'ye medya içeriği sağlıyor.
Bağlantı önceden yükleme Tarayıcıyı engellemeden bir video kaynağı için istekte bulunmaya zorlar. dokümanın onload etkinliği. HTTP Aralığı istekleri uyumlu değildir.
MSE ve dosya segmentleriyle uyumludur. Tam kaynaklar alınırken yalnızca küçük medya dosyaları (5 MB'tan küçük) için kullanılmalıdır.
Manuel arabelleğe alma Tam kontrol Karmaşık hataların ele alınması web sitesinin sorumluluğundadır.

Videonun önceden yüklenmesi özelliği

Video kaynağı, web sunucusunda barındırılan benzersiz bir dosyaysa video preload özelliğini kullanarak tarayıcıya nasıl fazla bilgi veya içerik olmasına dikkat edin. Bu, Medya Kaynağı Uzantılarının (MSE), preload ile uyumlu değildir.

Kaynak getirme işlemi, yalnızca ilk HTML belgesi hazır olduğunda tamamen yüklendi ve ayrıştırıldı (ör. DOMContentLoaded etkinliği tetiklendi) Diğer yandan, kaynak olduğunda çok farklı load etkinliği tetiklenir getirildiğinden emin olun.

preload özelliğinin metadata olarak ayarlanması, kullanıcının olması beklenen ancak videonun meta verilerini (boyutlar, parça (ör. liste, süre vb.) kullanılabilir. Chrome'dan başlayarak 64, preload için varsayılan değer metadata şeklindedir. (Tarih: auto ).

<video id="video" preload="metadata" src="file.mp4" controls></video>

<script>
  video.addEventListener('loadedmetadata', function() {
    if (video.buffered.length === 0) return;

    const bufferedSeconds = video.buffered.end(0) - video.buffered.start(0);
    console.log(`${bufferedSeconds} seconds of video are ready to play.`);
  });
</script>

preload özelliğinin auto olarak ayarlanması, tarayıcının önbelleğe alabileceğini gösterir ve oynatmanın durdurulmasına gerek kalmadan tamamlanabilmesi için yeterli veri arabelleğe alma.

<video id="video" preload="auto" src="file.mp4" controls></video>

<script>
  video.addEventListener('loadedmetadata', function() {
    if (video.buffered.length === 0) return;

    const bufferedSeconds = video.buffered.end(0) - video.buffered.start(0);
    console.log(`${bufferedSeconds} seconds of video are ready to play.`);
  });
</script>

Yine de bazı uyarılar var. Bu sadece bir ipucu olduğu için, tarayıcı preload özelliğini yoksayın. Bu yazının hazırlandığı sırada bazı kurallar Chrome'da uygulandı:

İpuçları

Web siteniz aynı alanda birçok video kaynağı içeriyorsa preload değerini metadata olarak ayarlamanızı veya poster değerini tanımlamanızı öneririz. özelliğini kullanarak preload özelliğini none olarak ayarlayın. Bu şekilde, ilgili riskleri aynı alan adına yönelik maksimum HTTP bağlantısı sayısı (sırasına göre 6 HTTP 1.1 spesifikasyonu). Bu işlemin Videolar temel kullanıcı deneyiminizin bir parçası değilse sayfa hızını artırabilirsiniz.

Diğer makalelerde incelendiği üzere bağlantı önceden yükleme, bildirime dayalı bir getirme işlemidir. tarayıcı, herhangi bir bağlantı olmadan bir kaynak için istekte bulunmaya zorlamanıza load etkinliğini engelliyor ve sayfa indirilirken. Kaynaklar <link rel="preload"> aracılığıyla yüklenen kaynaklar tarayıcıda yerel olarak depolanır DOM, JavaScript veya İYS'de açıkça referans verilene kadar kullanabilirsiniz.

Önceden yükleme, geçerli gezinmeye ve yoğunluğa odaklanması açısından kaynakları türlerine (komut dosyası, stil, yazı tipi, video, ses vb.). Tarayıcı önbelleğini, mevcut sunucu önbelleğine anlamına gelir.

Videonun tamamını önceden yükle

Videonun tamamındaki içerikleri web sitenize önceden yükleyerek JavaScript video içeriğinin getirilmesini istiyor, kaynak olarak önbellekten okunur tarayıcı tarafından önceden önbelleğe alınmış olabilir. Önceden yükleme isteği normal bir ağ getirmesi gerçekleşir.

<link rel="preload" as="video" href="https://cdn.com/small-file.mp4">

<video id="video" controls></video>

<script>
  // Later on, after some condition has been met, set video source to the
  // preloaded video URL.
  video.src = 'https://cdn.com/small-file.mp4';
  video.play().then(() => {
    // If preloaded video URL was already cached, playback started immediately.
  });
</script>

Çünkü önceden yüklenmiş kaynak, bir video öğesi tarafından örnekte, as ön yükleme bağlantısının değeri video şeklindedir. Sesli olsaydı öğesi, as="audio" olacaktır.

İlk segmenti önceden yükle

Aşağıdaki örnekte, bir videonun ilk segmentinin <link rel="preload"> ile nasıl önceden yükleneceği ve Medya Kaynağı Uzantıları ile nasıl kullanılacağı gösterilmektedir. Bilmiyorsanız MSE JavaScript API ile MSE ile ilgili temel bilgileri inceleyin.

Basitlik açısından, tüm videonun bölündüğünü ve file_1.webm, file_2.webm, file_3.webm vb. daha küçük dosyalar

<link rel="preload" as="fetch" href="https://cdn.com/file_1.webm">

<video id="video" controls></video>

<script>
  const mediaSource = new MediaSource();
  video.src = URL.createObjectURL(mediaSource);
  mediaSource.addEventListener('sourceopen', sourceOpen, { once: true });

  function sourceOpen() {
    URL.revokeObjectURL(video.src);
    const sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp09.00.10.08"');

    // If video is preloaded already, fetch will return immediately a response
    // from the browser cache (memory cache). Otherwise, it will perform a
    // regular network fetch.
    fetch('https://cdn.com/file_1.webm')
    .then(response => response.arrayBuffer())
    .then(data => {
      // Append the data into the new sourceBuffer.
      sourceBuffer.appendBuffer(data);
      // TODO: Fetch file_2.webm when user starts playing video.
    })
    .catch(error => {
      // TODO: Show "Video is not available" message to user.
    });
  }
</script>

Destek

Şu parametreyle <link rel=preload> için çeşitli as türlerinin desteklendiğini tespit edebilirsiniz: aşağıdaki snippet'leri içerir:

function preloadFullVideoSupported() {
  const link = document.createElement('link');
  link.as = 'video';
  return (link.as === 'video');
}

function preloadFirstSegmentSupported() {
  const link = document.createElement('link');
  link.as = 'fetch';
  return (link.as === 'fetch');
}

Manuel arabelleğe alma

Cache API'ye ve hizmet çalışanlarına geçmeden önce MSE ile bir videonun manuel olarak nasıl arabelleğe alınacağına. Aşağıdaki örnekte, web sitenizin sunucu HTTP'yi destekliyor Range olacaktır, ancak bu, dosya içine benzer ve emin olun. Google'ın Shaka'sı gibi bazı ara katman yazılımı kitaplıkları Player, JW Player ve Video.js sizin için hazırlandı.

<video id="video" controls></video>

<script>
  const mediaSource = new MediaSource();
  video.src = URL.createObjectURL(mediaSource);
  mediaSource.addEventListener('sourceopen', sourceOpen, { once: true });

  function sourceOpen() {
    URL.revokeObjectURL(video.src);
    const sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp09.00.10.08"');

    // Fetch beginning of the video by setting the Range HTTP request header.
    fetch('file.webm', { headers: { range: 'bytes=0-567139' } })
    .then(response => response.arrayBuffer())
    .then(data => {
      sourceBuffer.appendBuffer(data);
      sourceBuffer.addEventListener('updateend', updateEnd, { once: true });
    });
  }

  function updateEnd() {
    // Video is now ready to play!
    const bufferedSeconds = video.buffered.end(0) - video.buffered.start(0);
    console.log(`${bufferedSeconds} seconds of video are ready to play.`);

    // Fetch the next segment of video when user starts playing the video.
    video.addEventListener('playing', fetchNextSegment, { once: true });
  }

  function fetchNextSegment() {
    fetch('file.webm', { headers: { range: 'bytes=567140-1196488' } })
    .then(response => response.arrayBuffer())
    .then(data => {
      const sourceBuffer = mediaSource.sourceBuffers[0];
      sourceBuffer.appendBuffer(data);
      // TODO: Fetch further segment and append it.
    });
  }
</script>

Dikkat edilmesi gereken noktalar

Medya arabelleğe alma deneyiminin tamamının kontrolü artık sizde olduğundan, cihazın pil seviyesini, "Veri Tasarrufu Modu"nu kullanıcının tercihi ve ağ bilgilerini kullanabilirsiniz.

Pil farkındalığı

Kullanıcıların pil seviyesini dikkate alın hangi cihazları tercih edeceğimizi videoları önceden yükleme hakkındadır. Güç seviyesi devre dışı bırakıldığında pil ömrünü korur. düşük.

Şu durumlarda önceden yüklemeyi devre dışı bırakın veya en azından daha düşük çözünürlüklü bir videoyu önceden yükleyin: cihazın pili bitmek üzere.

if ('getBattery' in navigator) {
  navigator.getBattery()
  .then(battery => {
    // If battery is charging or battery level is high enough
    if (battery.charging || battery.level > 0.15) {
      // TODO: Preload the first segment of a video.
    }
  });
}

"Veri Tasarrufu"nu algılama

Hızlı ve hafif içerik yayınlamak için Save-Data istemci ipucu isteği başlığını kullanın "veri tasarrufu" özelliğini etkinleştiren kullanıcılara yönelik uygulamalar kendi modlarında emin olun. Bu istek başlığını tanımlayarak, uygulamanız Maliyet ve performans kısıtlamalı bir şekilde optimize edilmiş bir kullanıcı deneyimi sunun yardımcı olur.

Daha fazla bilgi edinmek için Save-Data ile Hızlı ve Hafif Uygulamalar Yayınlama bölümüne bakın.

Ağ bilgilerine dayalı akıllı yükleme

Önceden yükleme işleminden önce navigator.connection.type sayfasını kontrol etmenizi öneririz. Zaman cellular olarak ayarlanırsa önceden yüklemeyi engelleyebilir ve kullanıcılara mobil şebeke operatörü, bant genişliği için şarj ediyor olabilir ve önceden önbelleğe alınmış içeriğin otomatik olarak oynatılması.

if ('connection' in navigator) {
  if (navigator.connection.type == 'cellular') {
    // TODO: Prompt user before preloading video
  } else {
    // TODO: Preload the first segment of a video.
  }
}

Ağa nasıl tepki vereceğinizi öğrenmek için Ağ Bilgileri örneğine göz atın kabullenmek de önemlidir.

Birden çok ilk segmenti önbelleğe al

Şimdi, bir sürü medya içeriği olmadan spekülatif olarak önceden yüklemek kullanıcının en sonunda hangi medyayı seçeceğini biliyor musunuz? Kullanıcı bir web sayfasını ziyaret ederseniz muhtemelen o videoyu getirmeye yetecek kadar belleğimiz vardır. segment dosyası oluşturacağız, ancak kesinlikle 10 gizli <video> oluşturmamalıyız öğeleri ve 10 MediaSource nesne oluşturur ve bu verileri yayınlamaya başlar.

Aşağıdaki iki bölümden oluşan örnekte, GCLID'nin birden fazla ilk segmentini önbelleğe almanın güçlü ve kullanımı kolay Cache API'den yararlanarak video ile ilgili daha fazla bilgi edinebilirsiniz. Benzer bir şeyin IndexedDB ile de elde edilebilir. Henüz Service Worker'ları kullanmıyoruz. Cache API'ye window nesnesinden de erişilebilir.

Getirme ve önbelleğe alma

const videoFileUrls = [
  'bat_video_file_1.webm',
  'cow_video_file_1.webm',
  'dog_video_file_1.webm',
  'fox_video_file_1.webm',
];

// Let's create a video pre-cache and store all first segments of videos inside.
window.caches.open('video-pre-cache')
.then(cache => Promise.all(videoFileUrls.map(videoFileUrl => fetchAndCache(videoFileUrl, cache))));

function fetchAndCache(videoFileUrl, cache) {
  // Check first if video is in the cache.
  return cache.match(videoFileUrl)
  .then(cacheResponse => {
    // Let's return cached response if video is already in the cache.
    if (cacheResponse) {
      return cacheResponse;
    }
    // Otherwise, fetch the video from the network.
    return fetch(videoFileUrl)
    .then(networkResponse => {
      // Add the response to the cache and return network response in parallel.
      cache.put(videoFileUrl, networkResponse.clone());
      return networkResponse;
    });
  });
}

HTTP Range istekleri kullanırsam manuel olarak yeniden oluşturmam gerekeceğini unutmayın. Önbellek API'si Range yanıtlarını henüz desteklemediğinden bir Response nesnesi. networkResponse.arrayBuffer() çağrısının tüm içeriği getireceğine dikkat edin oluşturmak için kullanılabilir. Bu nedenle, birden fazla fark edebilirsiniz.

Referans olması amacıyla, yukarıdaki örneğin bir bölümünü HTTP Aralığını kaydedecek şekilde değiştirdim video önbelleğine aktarmanızı sağlar.

    ...
    return fetch(videoFileUrl, { headers: { range: 'bytes=0-567139' } })
    .then(networkResponse => networkResponse.arrayBuffer())
    .then(data => {
      const response = new Response(data);
      // Add the response to the cache and return network response in parallel.
      cache.put(videoFileUrl, response.clone());
      return response;
    });

Videoyu oynat

Kullanıcı oynat düğmesini tıkladığında videonun ilk segmentini alırız Cache API'sinde mevcut, böylece oynatma varsa hemen başlar. Aksi takdirde, dosyayı ağdan alırız. Tarayıcıların Kullanıcılar Önbelleği temizlemeye karar verebilir.

Daha önce gördüğümüz gibi, MSE'yi kullanarak videonun ilk segmentini videoya aktarıyoruz. öğesine dokunun.

function onPlayButtonClick(videoFileUrl) {
  video.load(); // Used to be able to play video later.

  window.caches.open('video-pre-cache')
  .then(cache => fetchAndCache(videoFileUrl, cache)) // Defined above.
  .then(response => response.arrayBuffer())
  .then(data => {
    const mediaSource = new MediaSource();
    video.src = URL.createObjectURL(mediaSource);
    mediaSource.addEventListener('sourceopen', sourceOpen, { once: true });

    function sourceOpen() {
      URL.revokeObjectURL(video.src);

      const sourceBuffer = mediaSource.addSourceBuffer('video/webm; codecs="vp09.00.10.08"');
      sourceBuffer.appendBuffer(data);

      video.play().then(() => {
        // TODO: Fetch the rest of the video when user starts playing video.
      });
    }
  });
}

Hizmet çalışanı ile aralık yanıtları oluşturma

Peki bir video dosyasının tamamını getirip var mı? Tarayıcı bir HTTP Range isteği gönderdiğinde Cache API'si çalışmadığı için videonun tamamını oluşturucu belleğine taşımak istiyorsanız Range yanıtları henüz desteklenmektedir.

Şimdi, bu isteklere nasıl müdahale edeceğimi ve özelleştirilmiş bir Range döndüreceğimi göstereyim yanıt vermesi gerekir.

addEventListener('fetch', event => {
  event.respondWith(loadFromCacheOrFetch(event.request));
});

function loadFromCacheOrFetch(request) {
  // Search through all available caches for this request.
  return caches.match(request)
  .then(response => {

    // Fetch from network if it's not already in the cache.
    if (!response) {
      return fetch(request);
      // Note that we may want to add the response to the cache and return
      // network response in parallel as well.
    }

    // Browser sends a HTTP Range request. Let's provide one reconstructed
    // manually from the cache.
    if (request.headers.has('range')) {
      return response.blob()
      .then(data => {

        // Get start position from Range request header.
        const pos = Number(/^bytes\=(\d+)\-/g.exec(request.headers.get('range'))[1]);
        const options = {
          status: 206,
          statusText: 'Partial Content',
          headers: response.headers
        }
        const slicedResponse = new Response(data.slice(pos), options);
        slicedResponse.setHeaders('Content-Range': 'bytes ' + pos + '-' +
            (data.size - 1) + '/' + data.size);
        slicedResponse.setHeaders('X-From-Cache': 'true');

        return slicedResponse;
      });
    }

    return response;
  }
}

Bu dilimlenmiş dosyayı yeniden oluşturmak için response.blob() kullandığımı unutmamak önemlidir. yanıtı verdiğinden, bu işlem bana dosyayı response.arrayBuffer(), tüm dosyayı oluşturucu belleğine getirir.

Özel X-From-Cache HTTP başlığım, bu isteğin olup olmadığını öğrenmek için kullanılabilir veya ağdan geldiğini öğrenebilirsiniz. Şu gibi oyuncular tarafından kullanılabilir: ShakaPlayer'ın kontrol ederek yanıt süresini ağ hızı.

Resmi Sample Media App'i, özellikle de uygulamayı Range ile ilgili eksiksiz bir çözüm için ranged-response.js dosyasını kullanabilirsiniz. kabul edersiniz.