मीडिया सोर्स एक्सटेंशन

François Beaufort
François Beaufort
Joe Medley
Joe Medley

मीडिया सोर्स एक्सटेंशन (एमएसई) एक JavaScript API है. इसकी मदद से, ऑडियो या वीडियो के सेगमेंट से प्लेबैक के लिए स्ट्रीम बनाई जा सकती हैं. इस लेख में एमएसई के बारे में नहीं बताया गया है. हालांकि, अगर आपको अपनी साइट पर ऐसे वीडियो एम्बेड करने हैं जो ये काम करते हैं, तो एमएसई को समझना ज़रूरी है:

  • अडैप्टिव स्ट्रीमिंग, जिसे डिवाइस की सुविधाओं और नेटवर्क की स्थितियों के हिसाब से अडजस्ट करने का दूसरा तरीका भी कहा जाता है
  • अडैप्टिव स्प्लिसिंग, जैसे कि विज्ञापन इंसर्शन
  • टाइम शिफ़्ट करना
  • परफ़ॉर्मेंस और डाउनलोड साइज़ को कंट्रोल करना
बेसिक एमएसई डेटा फ़्लो
पहली इमेज: एमएसई का बुनियादी डेटा फ़्लो

एमएसई को एक चेन के तौर पर देखा जा सकता है. जैसा कि इस इमेज में दिखाया गया है, डाउनलोड की गई फ़ाइल और मीडिया एलिमेंट के बीच कई लेयर होती हैं.

  • मीडिया चलाने के लिए <audio> या <video> एलिमेंट.
  • मीडिया एलिमेंट को फ़ीड करने के लिए, SourceBuffer वाला MediaSource इंस्टेंस.
  • Response ऑब्जेक्ट में मीडिया डेटा को वापस पाने के लिए, fetch() या XHR कॉल.
  • MediaSource.SourceBuffer को फ़ीड करने के लिए, Response.arrayBuffer() को किया गया कॉल.

असल में, यह चेन कुछ इस तरह दिखती है:

var vidElement = document.querySelector('video');

if (window.MediaSource) {
  var mediaSource = new MediaSource();
  vidElement.src = URL.createObjectURL(mediaSource);
  mediaSource.addEventListener('sourceopen', sourceOpen);
} else {
  console.log('The Media Source Extensions API is not supported.');
}

function sourceOpen(e) {
  URL.revokeObjectURL(vidElement.src);
  var mime = 'video/webm; codecs="opus, vp09.00.10.08"';
  var mediaSource = e.target;
  var sourceBuffer = mediaSource.addSourceBuffer(mime);
  var videoUrl = 'droid.webm';
  fetch(videoUrl)
    .then(function (response) {
      return response.arrayBuffer();
    })
    .then(function (arrayBuffer) {
      sourceBuffer.addEventListener('updateend', function (e) {
        if (!sourceBuffer.updating && mediaSource.readyState === 'open') {
          mediaSource.endOfStream();
        }
      });
      sourceBuffer.appendBuffer(arrayBuffer);
    });
}

अगर आपको अब तक दी गई जानकारी से समस्या हल हो गई है, तो अब पढ़ना बंद करें. अगर आपको ज़्यादा जानकारी चाहिए, तो कृपया आगे पढ़ें. हम एमएसई का एक बुनियादी उदाहरण बनाकर, इस चेन के बारे में बताएंगे. बाइल्ड के हर चरण में, पिछले चरण में कोड जोड़ा जाएगा.

साफ़ तौर पर जानकारी देने के बारे में जानकारी

क्या इस लेख में, वेब पेज पर मीडिया चलाने के बारे में आपको पूरी जानकारी मिलेगी? नहीं, इसका मकसद सिर्फ़ आपको ज़्यादा जटिल कोड को समझने में मदद करना है, जो आपको कहीं और मिल सकता है. साफ़ तौर पर कहा जाए, तो इस दस्तावेज़ में कई चीज़ों को आसान बनाया गया है और बताया गया है. हमें लगता है कि हम ऐसा कर सकते हैं, क्योंकि हम Google के Shaka Player जैसी लाइब्रेरी का इस्तेमाल करने का सुझाव भी देते हैं. मैं इस बात का ध्यान रखूंगा कि कहां जान-बूझकर चीज़ों को आसान बनाया जा रहा है.

इसमें कुछ चीज़ें शामिल नहीं हैं

मैं इन कुछ चीज़ों के बारे में नहीं बताऊँगी. ये किसी खास क्रम में नहीं हैं.

  • प्लेबैक कंट्रोल. हमें ये एलिमेंट मुफ़्त में मिलते हैं. इसके लिए, हम HTML5 के <audio> और <video> एलिमेंट का इस्तेमाल करते हैं.
  • गड़बड़ी ठीक करना.

प्रोडक्शन एनवायरमेंट में इस्तेमाल करने के लिए

हमारा सुझाव है कि MSE से जुड़े एपीआई के प्रोडक्शन इस्तेमाल के दौरान, हम यहां बताई गई कुछ बातों का सुझाव देंगे:

  • इन एपीआई पर कॉल करने से पहले, किसी भी गड़बड़ी वाले इवेंट या एपीआई के अपवादों को मैनेज करें. साथ ही, HTMLMediaElement.readyState और MediaSource.readyState की जांच करें. जुड़े हुए इवेंट के डिलीवर होने से पहले, ये वैल्यू बदल सकती हैं.
  • SourceBuffer के mode, timestampOffset, appendWindowStart, appendWindowEnd या appendBuffer() या remove() को SourceBuffer पर कॉल करने से पहले, पक्का करें कि SourceBuffer.updating बूलियन वैल्यू जांचकर, appendBuffer() और remove() कॉल अब भी जारी न हों.
  • आपके MediaSource में जोड़े गए सभी SourceBuffer इंस्टेंस के लिए, MediaSource.endOfStream() को कॉल करने या MediaSource.duration को अपडेट करने से पहले, पक्का करें कि उनकी updating वैल्यू सही न हों.
  • अगर MediaSource.readyState की वैल्यू ended है, तो appendBuffer() और remove() जैसे कॉल या SourceBuffer.mode या SourceBuffer.timestampOffset को सेट करने पर, यह वैल्यू open पर ट्रांज़िशन हो जाएगी. इसका मतलब है कि आपको एक से ज़्यादा sourceopen इवेंट मैनेज करने के लिए तैयार रहना चाहिए.
  • HTMLMediaElement error इवेंट को हैंडल करते समय, MediaError.message के कॉन्टेंट से, गड़बड़ी की मुख्य वजह का पता लगाया जा सकता है. ऐसा खास तौर पर उन गड़बड़ियों के लिए किया जा सकता है जिन्हें टेस्टिंग एनवायरमेंट में दोहराना मुश्किल होता है.

मीडिया एलिमेंट में MediaSource इंस्टेंस अटैच करें

आज-कल वेब डेवलपमेंट के क्षेत्र में कई चीज़ों की तरह, सुविधा की पहचान करने से भी शुरुआत की जाती है. इसके बाद, कोई मीडिया एलिमेंट पाएं. यह <audio> या <video> एलिमेंट हो सकता है. आखिर में, MediaSource का एक इंस्टेंस बनाएं. इसे यूआरएल में बदल दिया जाता है और मीडिया एलिमेंट के सोर्स एट्रिब्यूट में भेज दिया जाता है.

var vidElement = document.querySelector('video');

if (window.MediaSource) {
  var mediaSource = new MediaSource();
  vidElement.src = URL.createObjectURL(mediaSource);
  // Is the MediaSource instance ready?
} else {
  console.log('The Media Source Extensions API is not supported.');
}
ब्लॉब के तौर पर सोर्स एट्रिब्यूट
पहली इमेज: ब्लॉब के तौर पर सोर्स एट्रिब्यूट

MediaSource ऑब्जेक्ट को src एट्रिब्यूट में पास किया जा सकता है, यह थोड़ा अजीब लग सकता है. आम तौर पर, ये स्ट्रिंग होती हैं, लेकिन ये ब्लॉब भी हो सकते हैं. एम्बेड किए गए मीडिया वाले पेज की जांच करने और उसके मीडिया एलिमेंट की जांच करने पर, आपको पता चलेगा कि मेरा क्या मतलब है.

क्या MediaSource इंस्टेंस तैयार है?

URL.createObjectURL() सिंक्रोनस है. हालांकि, यह अटैचमेंट को एसिंक्रोनस तरीके से प्रोसेस करता है. इससे, MediaSource इंस्टेंस के साथ कुछ भी करने से पहले थोड़ी देरी होती है. अच्छी बात यह है कि इसकी जांच करने के तरीके मौजूद हैं. सबसे आसान तरीका, readyState नाम की MediaSource प्रॉपर्टी का इस्तेमाल करना है. readyState प्रॉपर्टी, MediaSource इंस्टेंस और मीडिया एलिमेंट के बीच के संबंध के बारे में बताती है. इसमें इनमें से कोई एक वैल्यू हो सकती है:

  • closed - MediaSource इंस्टेंस, मीडिया एलिमेंट से अटैच नहीं है.
  • open - MediaSource इंस्टेंस, मीडिया एलिमेंट से जुड़ा है और डेटा पाने या डेटा पाने के लिए तैयार है.
  • ended - MediaSource इंस्टेंस को मीडिया एलिमेंट से अटैच किया गया है और उसका पूरा डेटा उसी एलिमेंट में भेज दिया गया है.

इन विकल्पों को सीधे तौर पर क्वेरी करने से, परफ़ॉर्मेंस पर बुरा असर पड़ सकता है. MediaSource में readyState के बदलने पर भी इवेंट ट्रिगर होते हैं. खास तौर पर, sourceopen, sourceclosed, और sourceended के बदलने पर. उदाहरण के लिए, मैं sourceopen इवेंट का इस्तेमाल करके यह तय करूंगा कि वीडियो को कब फ़ेच और बफ़र करना है.

var vidElement = document.querySelector('video');

if (window.MediaSource) {
  var mediaSource = new MediaSource();
  vidElement.src = URL.createObjectURL(mediaSource);
  <strong>mediaSource.addEventListener('sourceopen', sourceOpen);</strong>
} else {
  console.log("The Media Source Extensions API is not supported.")
}

<strong>function sourceOpen(e) {
  URL.revokeObjectURL(vidElement.src);
  // Create a SourceBuffer and get the media file.
}</strong>

ध्यान दें कि मैंने revokeObjectURL() को भी कॉल किया है. मुझे पता है कि ऐसा करना जल्दबाजी है, लेकिन मीडिया एलिमेंट के src एट्रिब्यूट के MediaSource इंस्टेंस से कनेक्ट होने के बाद, ऐसा कभी भी किया जा सकता है. इस तरीके को कॉल करने से, कोई ऑब्जेक्ट नष्ट नहीं होता. इससे प्लैटफ़ॉर्म को सही समय पर, ग़ैर-ज़रूरी डेटा हटाने की सुविधा मिलती है. इसलिए, मैं इसे तुरंत लागू कर रहा हूं.

SourceBuffer बनाना

अब SourceBuffer बनाने का समय आ गया है. यह वह ऑब्जेक्ट है जो मीडिया सोर्स और मीडिया एलिमेंट के बीच डेटा को शटल करने का काम करता है. SourceBuffer को उस मीडिया फ़ाइल के हिसाब से तय करना चाहिए जिसे लोड किया जा रहा है.

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

एमएसई स्पेसिफ़िकेशन के वर्शन 1 में, उपयोगकर्ता एजेंट के लिए यह तय करने का विकल्प होता है कि MIME टाइप और कोडेक, दोनों की ज़रूरत है या नहीं. कुछ उपयोगकर्ता एजेंट के लिए, सिर्फ़ MIME टाइप की ज़रूरत होती है. कुछ उपयोगकर्ता एजेंट, जैसे कि Chrome को उन mime टाइप के लिए कोडेक की ज़रूरत होती है जो अपने कोडेक के बारे में जानकारी नहीं देते. इन सभी को अलग-अलग करने के बजाय, दोनों को शामिल करना बेहतर होगा.

var vidElement = document.querySelector('video');

if (window.MediaSource) {
  var mediaSource = new MediaSource();
  vidElement.src = URL.createObjectURL(mediaSource);
  mediaSource.addEventListener('sourceopen', sourceOpen);
} else {
  console.log('The Media Source Extensions API is not supported.');
}

function sourceOpen(e) {
  URL.revokeObjectURL(vidElement.src);
  <strong>
    var mime = 'video/webm; codecs="opus, vp09.00.10.08"'; // e.target refers to
    the mediaSource instance. // Store it in a variable so it can be used in a
    closure. var mediaSource = e.target; var sourceBuffer =
    mediaSource.addSourceBuffer(mime); // Fetch and process the video.
  </strong>;
}

मीडिया फ़ाइल डाउनलोड करें

अगर इंटरनेट पर एमएसई के उदाहरण खोजे जाते हैं, तो आपको ऐसे कई उदाहरण मिलेंगे जो XHR का इस्तेमाल करके मीडिया फ़ाइलें हासिल करते हैं. ज़्यादा बेहतर तरीके से काम करने के लिए, मैं Fetch एपीआई और इससे मिलने वाले Promise का इस्तेमाल करने जा रहा हूं. अगर आपको Safari में ऐसा करना है, तो fetch() पॉलीफ़िल के बिना यह काम नहीं करेगा.

function sourceOpen(e) {
  URL.revokeObjectURL(vidElement.src);
  var mime = 'video/webm; codecs="opus, vp09.00.10.08"';
  var mediaSource = e.target;
  var sourceBuffer = mediaSource.addSourceBuffer(mime);
  var videoUrl = 'droid.webm';
  <strong>
    fetch(videoUrl) .then(function(response){' '}
    {
      // Process the response object.
    }
    );
  </strong>;
}

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

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

रिस्पॉन्स ऑब्जेक्ट को प्रोसेस करना

कोड पूरा हो गया है, लेकिन मीडिया नहीं चल रहा है. हमें Response ऑब्जेक्ट से SourceBuffer में, मीडिया डेटा लाना होगा.

रिस्पॉन्स ऑब्जेक्ट से MediaSource इंस्टेंस में डेटा पास करने का सामान्य तरीका यह है कि रिस्पॉन्स ऑब्जेक्ट से ArrayBuffer पाएं और उसे SourceBuffer में पास करें. response.arrayBuffer() को कॉल करके शुरू करें, जो बफ़र का प्रॉमिस दिखाता है. मैंने अपने कोड में, इस वादे को दूसरे then() क्लॉज़ में पास किया है, जहां मैंने इसे SourceBuffer में जोड़ा है.

function sourceOpen(e) {
  URL.revokeObjectURL(vidElement.src);
  var mime = 'video/webm; codecs="opus, vp09.00.10.08"';
  var mediaSource = e.target;
  var sourceBuffer = mediaSource.addSourceBuffer(mime);
  var videoUrl = 'droid.webm';
  fetch(videoUrl)
    .then(function(response) {
      <strong>return response.arrayBuffer();</strong>
    })
    <strong>.then(function(arrayBuffer) {
      sourceBuffer.appendBuffer(arrayBuffer);
    });</strong>
}

endOfStream() को कॉल करें

सभी ArrayBuffers जोड़ने के बाद, अगर आपको लगता है कि अब मीडिया का कोई और डेटा नहीं जोड़ना है, तो MediaSource.endOfStream() को कॉल करें. इससे MediaSource.readyState, ended में बदल जाएगा और sourceended इवेंट ट्रिगर हो जाएगा.

function sourceOpen(e) {
  URL.revokeObjectURL(vidElement.src);
  var mime = 'video/webm; codecs="opus, vp09.00.10.08"';
  var mediaSource = e.target;
  var sourceBuffer = mediaSource.addSourceBuffer(mime);
  var videoUrl = 'droid.webm';
  fetch(videoUrl)
    .then(function(response) {
      return response.arrayBuffer();
    })
    .then(function(arrayBuffer) {
      <strong>sourceBuffer.addEventListener('updateend', function(e) {
        if (!sourceBuffer.updating && mediaSource.readyState === 'open') {
          mediaSource.endOfStream();
        }
      });</strong>
      sourceBuffer.appendBuffer(arrayBuffer);
    });
}

फ़ाइनल वर्शन

यहां उदाहरण के तौर पर पूरा कोड दिया गया है. हमें उम्मीद है कि आपको मीडिया सोर्स एक्सटेंशन के बारे में कुछ जानकारी मिली होगी.

var vidElement = document.querySelector('video');

if (window.MediaSource) {
  var mediaSource = new MediaSource();
  vidElement.src = URL.createObjectURL(mediaSource);
  mediaSource.addEventListener('sourceopen', sourceOpen);
} else {
  console.log('The Media Source Extensions API is not supported.');
}

function sourceOpen(e) {
  URL.revokeObjectURL(vidElement.src);
  var mime = 'video/webm; codecs="opus, vp09.00.10.08"';
  var mediaSource = e.target;
  var sourceBuffer = mediaSource.addSourceBuffer(mime);
  var videoUrl = 'droid.webm';
  fetch(videoUrl)
    .then(function (response) {
      return response.arrayBuffer();
    })
    .then(function (arrayBuffer) {
      sourceBuffer.addEventListener('updateend', function (e) {
        if (!sourceBuffer.updating && mediaSource.readyState === 'open') {
          mediaSource.endOfStream();
        }
      });
      sourceBuffer.appendBuffer(arrayBuffer);
    });
}

सुझाव/राय दें या शिकायत करें