पहले से लोड किए गए ऑडियो और वीडियो के साथ तेज़ी से वीडियो चलाना

संसाधनों को पहले से लोड करके, मीडिया कॉन्टेंट को तेज़ी से चलाने का तरीका.

François Beaufort
François Beaufort

तेज़ी से वीडियो चलाने का मतलब है कि ज़्यादा लोग आपका वीडियो देख रहे हैं या ऑडियो. यह एक जानी-पहचानी बात है. इस लेख में, आपको अपने ऑडियो और वीडियो को तेज़ी से चलाने के लिए, इन तकनीकों का इस्तेमाल किया जा सकता है आपके इस्तेमाल के उदाहरण के हिसाब से, संसाधनों को पहले से लोड करना.

क्रेडिट: कॉपीराइट ब्लेंडर फ़ाउंडेशन | www.blender.org पर जाएं.

मैं मीडिया फ़ाइलों को पहले से लोड करने के तीन तरीकों के बारे में बताऊँगी. इन तरीकों की शुरुआत इनके फ़ायदे से होगी और नुकसान.

यह बहुत बढ़िया है... लेकिन...
वीडियो पहले से लोड करने का एट्रिब्यूट वेब सर्वर पर होस्ट की गई किसी यूनीक फ़ाइल के लिए, इसे इस्तेमाल करना आसान होता है. ब्राउज़र इस एट्रिब्यूट को पूरी तरह अनदेखा कर सकते हैं.
रिसॉर्स फ़ेच करने की प्रोसेस तब शुरू होती है, जब एचटीएमएल दस्तावेज़ पूरी तरह से लोड हो जाता है और पार्स किया गया.
मीडिया सोर्स एक्सटेंशन (MSE), मीडिया एलिमेंट पर preload एट्रिब्यूट को अनदेखा कर देते हैं, क्योंकि ऐप्लिकेशन इन चीज़ों के लिए ज़िम्मेदार होता है MSE को मीडिया उपलब्ध करा रहा है.
लिंक को पहले से लोड करना ब्राउज़र को ब्लॉक किए बिना, किसी वीडियो रिसॉर्स का अनुरोध करने के लिए मजबूर करता है दस्तावेज़ का onload इवेंट. एचटीटीपी रेंज के अनुरोध काम नहीं करते.
MSE और फ़ाइल सेगमेंट के साथ काम करता है. पूरे संसाधन फ़ेच करते समय, सिर्फ़ छोटी मीडिया फ़ाइलों (5 एमबी से कम) के लिए इस्तेमाल किया जाना चाहिए.
मैन्युअल तरीके से बफ़र करना पूरा कंट्रोल जटिल गड़बड़ियों को ठीक करना वेबसाइट की ज़िम्मेदारी है.

वीडियो पहले से लोड करने का एट्रिब्यूट

अगर वीडियो सोर्स, वेब सर्वर पर होस्ट की गई एक यूनीक फ़ाइल है, तो वीडियो preload एट्रिब्यूट का इस्तेमाल करके ब्राउज़र को यह संकेत दें कि कैसे पहले से लोड करने के लिए ज़्यादा जानकारी या कॉन्टेंट. इसका मतलब है मीडिया सोर्स एक्सटेंशन (MSE) preload के साथ काम नहीं करता है.

रिसॉर्स फ़ेच करने की प्रोसेस सिर्फ़ तब शुरू होगी, जब शुरुआती एचटीएमएल दस्तावेज़ पूरी तरह से लोड और पार्स हो गया (उदाहरण के लिए, DOMContentLoaded इवेंट ट्रिगर हो गया है) जबकि संसाधन होने पर सबसे अलग load इवेंट सक्रिय होगा मिल गया है.

preload एट्रिब्यूट को metadata पर सेट करने से पता चलता है कि उपयोगकर्ता वीडियो की ज़रूरत होने की उम्मीद है, लेकिन वह उसका मेटाडेटा (डाइमेंशन, ट्रैक) फ़ेच कर रहा है सूची, अवधि वगैरह) पसंद की जाती है. ध्यान दें कि Chrome में शुरुआत करने से 64 है, preload की डिफ़ॉल्ट वैल्यू metadata है. (यह 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 एट्रिब्यूट को auto पर सेट करने से पता चलता है कि ब्राउज़र कैश मेमोरी में सेव कर सकता है इतना डेटा कि बिना किसी स्टॉप के भी प्लेबैक पूरा हो सके डेटा को और भी सुरक्षित रखें.

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

हालांकि, कुछ चेतावनियां भी हैं. यह सिर्फ़ एक संकेत है. इसलिए, ब्राउज़र पूरी तरह से preload एट्रिब्यूट को अनदेखा करें. लिखते समय, यहां कुछ नियम दिए गए हैं Chrome में लागू किए गए:

सलाह

अगर आपकी वेबसाइट पर एक ही डोमेन पर कई वीडियो रिसॉर्स हैं, तो मुझे सुझाव है कि आप preload वैल्यू को metadata पर सेट करें या poster को तय करें एट्रिब्यूट का इस्तेमाल करें और preload को none पर सेट करें. इस तरह, आप एक ही डोमेन पर HTTP कनेक्शन की अधिकतम संख्या ( एचटीटीपी 1.1 स्पेसिफ़िकेशन) बताए गए हैं. ध्यान दें कि यह तरीका अगर वीडियो आपके मुख्य उपयोगकर्ता अनुभव का हिस्सा नहीं हैं, तो पेज स्पीड को बेहतर बनाया जा सकता है.

जैसा कि दूसरे लेखों में शामिल किया गया है, लिंक पहले से लोड किए गए यूआरएल को फ़ेच करने की प्रोसेस, जानकारी देने के मकसद से की गई है आपको ब्राउज़र को बिना किसी संसाधन के संसाधन का अनुरोध करने के लिए ज़बरदस्ती करने की अनुमति देता है load इवेंट को ब्लॉक करने और पेज के डाउनलोड होने के दौरान. रिसोर्स <link rel="preload"> के ज़रिए लोड होने वाली फ़ाइलों को ब्राउज़र में स्थानीय तौर पर सेव किया जाता है और तब तक नहीं बदले जा सकते, जब तक कि DOM, JavaScript, या सीएसएस का इस्तेमाल करें.

पहले से लोड किया जाने वाला डेटा, प्रीफ़ेच से अलग होता है, क्योंकि यह मौजूदा नेविगेशन पर फ़ोकस करता है और संसाधनों को उनके टाइप (स्क्रिप्ट, स्टाइल, फ़ॉन्ट, वीडियो, ऑडियो वगैरह). इसका इस्तेमाल मौजूदा ब्राउज़र की कैश मेमोरी को गर्म करने के लिए किया जाना चाहिए सत्र.

पूरा वीडियो पहले से लोड करें

यहां बताया गया है कि अपनी वेबसाइट पर एक पूरा वीडियो पहले से लोड कैसे करें, ताकि जब JavaScript, वीडियो कॉन्टेंट फ़ेच करने की अनुमति मांगता है. इसलिए, इसे रिसॉर्स के तौर पर कैश मेमोरी से पढ़ा जाता है हो सकता है कि ब्राउज़र ने पहले से ही कैश मेमोरी में सेव किया हो. अगर पेजों को पहले से लोड करने का अनुरोध नहीं किया जाता है, तो अभी तक, नेटवर्क को नियमित तौर पर फ़ेच किया जाएगा.

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

ऐसा इसलिए, क्योंकि पहले से लोड किए गए संसाधन का इस्तेमाल, वीडियो एलिमेंट के ज़रिए उदाहरण के लिए, as प्रीलोड लिंक की वैल्यू video है. अगर यह एक ऑडियो होता एलिमेंट में, यह as="audio" होगा.

पहला सेगमेंट पहले से लोड करें

नीचे दिए गए उदाहरण में, <link rel="preload"> की मदद से वीडियो के पहले सेगमेंट को पहले से लोड करने और उसे मीडिया सोर्स एक्सटेंशन के साथ इस्तेमाल करने का तरीका बताया गया है. अगर आपको इसके बारे में नहीं पता है, तो MSE JavaScript API के साथ, MSE की बुनियादी बातें देखें.

इसे आसानी से समझने के लिए, मान लें कि पूरे वीडियो को अलग-अलग सेगमेंट में बांटा गया है छोटी फ़ाइलें जैसे file_1.webm, file_2.webm, file_3.webm वगैरह.

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

सहायता

<link rel=preload> के लिए कई तरह के as के साथ काम करने की सुविधा का पता लगाने के लिए, स्निपेट नीचे दिए गए हैं:

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');
}

मैन्युअल तरीके से बफ़र करना

कैश एपीआई और सर्विस वर्कर के बारे में ज़्यादा जानने से पहले, आइए देखते हैं कि MSE का इस्तेमाल करके वीडियो को मैन्युअल तरीके से कैसे बफ़र करें. नीचे दिए गए उदाहरण में माना गया है कि आपका वेब सर्वर, एचटीटीपी Range पर काम करता है किया जाता है, लेकिन यह फ़ाइल सेगमेंट. ध्यान दें कि कुछ मिडलवेयर लाइब्रेरी, जैसे कि Google की Shaka Player, JW Player, और Video.js आपके लिए बनाया गया है.

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

ज़रूरी बातें

जैसा कि अब आपके पास पूरे मीडिया बफ़रिंग अनुभव पर कंट्रोल है, इसलिए हमारा सुझाव है कि आप डिवाइस के बैटरी लेवल, "डेटा बचाने की सेटिंग" मोड उपयोगकर्ता की पसंद और पहले से लोड करने के बारे में सोचते समय नेटवर्क की जानकारी.

बैटरी की जानकारी

उपयोगकर्ताओं के बैटरी लेवल का ध्यान रखना डिवाइसों से जुड़ने से पहले सोचें वीडियो को पहले से लोड करने के बारे में है. पावर लेवल कम होने पर भी, बैटरी लाइफ़ बची रहेगी कम है.

वीडियो को पहले से लोड करने की सुविधा बंद करें या कम रिज़ॉल्यूशन वाले वीडियो को पहले से लोड करें. ऐसा तब करें, जब डिवाइस की बैटरी खत्म हो गई है.

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.
    }
  });
}

"डेटा-सेवर" का पता लगाएं

तेज़ी और आसानी से डिलीवरी करने के लिए, Save-Data क्लाइंट संकेत के अनुरोध हेडर का इस्तेमाल करें "डेटा बचत" के लिए ऑप्ट-इन करने वाले उपयोगकर्ताओं को लागू किए गए ऐप्लिकेशन अपने ब्राउज़र खोलें. इस अनुरोध के हेडर की पहचान करके, आपका ऐप्लिकेशन कस्टमाइज़ किया जा सकता है. साथ ही, कम लागत और सीमित परफ़ॉर्मेंस वाले उपयोगकर्ताओं को ऑप्टिमाइज़ किया गया अनुभव देना उपयोगकर्ता.

ज़्यादा जानने के लिए, सेव-डेटा की मदद से, तेज़ी से काम करने वाले और कम डेटा वाले ऐप्लिकेशन डिलीवर करना देखें.

नेटवर्क की जानकारी के आधार पर स्मार्ट लोडिंग

पेजों को पहले से लोड करने से पहले, navigator.connection.type की जांच कर लें. टास्क कब शुरू होगा इसे cellular पर सेट किया जाता है, तो आप पेजों को पहले से लोड होने से रोक सकते हैं. साथ ही, उपयोगकर्ताओं को सलाह दे सकते हैं कि हो सकता है कि उसका मोबाइल नेटवर्क ऑपरेटर बैंडविथ के लिए शुल्क ले रहा हो और वह पहले से कैश मेमोरी में सेव किए गए कॉन्टेंट को अपने-आप चलाने की सुविधा.

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

नेटवर्क पर प्रतिक्रिया देने का तरीका जानने के लिए, नेटवर्क की जानकारी का सैंपल देखें बदलाव भी किए जा सकते हैं.

एक से ज़्यादा प्रथम सेगमेंट को प्री-कैश करें

अब क्या होगा अगर मुझे बिना किसी अनुमान के कुछ मीडिया कॉन्टेंट को पहले से लोड करने के लिए यह जानने के बाद कि उपयोगकर्ता कौनसा मीडिया चुनेगा? अगर उपयोगकर्ता किसी जिसमें 10 वीडियो हैं, तो शायद हमारे पास एक वीडियो फ़ेच करने के लिए काफ़ी मेमोरी है सेगमेंट फ़ाइल का इस्तेमाल कर सकते हैं, लेकिन हमें 10 छिपे हुए <video> नहीं बनाने चाहिए एलिमेंट और 10 MediaSource ऑब्जेक्ट इकट्ठा करना शुरू कर देते हैं और उस डेटा को फ़ीड करना शुरू कर देते हैं.

नीचे दिए गए दो हिस्सों के उदाहरण में, एक से ज़्यादा पहले सेगमेंट को प्री-कैश मेमोरी में सेव करने का तरीका बताया गया है कैश एपीआई का इस्तेमाल करके, वीडियो में अपने हिसाब से बदलाव करना. ध्यान दें कि कुछ इसके जैसा को IndexedDB से भी हासिल करना चाहिए. हम अभी सर्विस वर्कर का इस्तेमाल कैश एपीआई को window ऑब्जेक्ट से भी ऐक्सेस किया जा सकता है.

फ़ेच और कैश मेमोरी

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;
    });
  });
}

ध्यान दें कि अगर मुझे एचटीटीपी Range अनुरोध का इस्तेमाल करना है, तो मुझे मैन्युअल तरीके से फिर से बनाना होगा Response ऑब्जेक्ट, क्योंकि कैश एपीआई पर अब तक Range जवाब काम नहीं करते. होना ध्यान रखें कि networkResponse.arrayBuffer() को कॉल करने पर पूरा कॉन्टेंट फ़ेच किया जाता है रिस्पॉन्स को रेंडरर की मेमोरी में भेज दिया जाता है. इसलिए, आपको छोटी रेंज.

रेफ़रंस के लिए, मैंने एचटीटीपी रेंज को सेव करने के लिए ऊपर दिए गए उदाहरण के कुछ हिस्से में बदलाव कर दिया है वीडियो प्री-कैश मेमोरी में सेव होने के लिए अनुरोध करता है.

    ...
    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;
    });

वीडियो चलाएं

जब कोई उपयोगकर्ता 'चलाएं' बटन पर क्लिक करता है, तब हम वीडियो का पहला सेगमेंट फ़ेच करेंगे कैश एपीआई में उपलब्ध होना चाहिए, ताकि उपलब्ध होने पर वीडियो तुरंत शुरू हो जाए. अगर ऐसा नहीं है, तो हम इसे नेटवर्क से फ़ेच करेंगे. ध्यान रखें कि ब्राउज़र और उपयोगकर्ता कैश को मिटा सकते हैं.

जैसा कि हमने पहले देखा है, हम वीडियो के पहले सेगमेंट को वीडियो में फ़ीड करने के लिए MSE का इस्तेमाल करते हैं एलिमेंट.

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.
      });
    }
  });
}

सर्विस वर्कर की मदद से रेंज रिस्पॉन्स बनाना

अब क्या होगा अगर आपने एक पूरी वीडियो फ़ाइल फ़ेच की हो और उसे कैश एपीआई को मैन्युअल तरीके से कॉन्फ़िगर करना है? जब ब्राउज़र एक HTTP Range अनुरोध भेजता है, तो आप निश्चित रूप से रेंडरर की मेमोरी में पूरे वीडियो को ट्रांसफ़र करना चाहते हैं, क्योंकि कैश एपीआई को अभी Range जवाबों का समर्थन करें.

आइए, मुझे दिखाते हैं कि इन अनुरोधों को कैसे रोका जा सकता है और पसंद के मुताबिक बनाया गया Range कैसे वापस किया जाता है सर्विस वर्कर से मिली प्रतिक्रिया.

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;
  }
}

ध्यान दें कि मैंने इस स्लाइस को फिर से बनाने के लिए, response.blob() का इस्तेमाल किया है क्योंकि इससे मुझे फ़ाइल का हैंडल मिल जाता है जबकि response.arrayBuffer() पूरी फ़ाइल को रेंडरर की मेमोरी में ले आता है.

मेरे पसंद के मुताबिक बनाए गए X-From-Cache एचटीटीपी हेडर का इस्तेमाल, यह जानने के लिए किया जा सकता है कि यह अनुरोध कैश मेमोरी से आया हो या नेटवर्क से. इसका इस्तेमाल कोई भी खिलाड़ी कर सकता है, जैसे कि ShakaPlayer, जवाब मिलने में लगने वाले समय को इस तरह से अनदेखा करे कि नेटवर्क की स्पीड.

आधिकारिक Sample Media App पर एक नज़र डालें और विशेष रूप से इसके Range को मैनेज करने के तरीके की पूरी जानकारी के लिए, ranged-response.js फ़ाइल अनुरोध.