افزونه های منبع رسانه (MSE) یک API جاوا اسکریپت است که به شما امکان می دهد جریان هایی را برای پخش از بخش های صوتی یا تصویری بسازید. اگرچه در این مقاله پوشش داده نشده است، اما اگر میخواهید ویدیوهایی را در سایت خود جاسازی کنید که کارهایی مانند زیر را انجام میدهند، به درک MSE نیاز است:
- جریان تطبیقی، که راه دیگری برای گفتن سازگاری با قابلیت های دستگاه و شرایط شبکه است
- پیوند تطبیقی، مانند درج آگهی
- جابجایی زمان
- کنترل عملکرد و اندازه دانلود
تقریباً می توانید MSE را به عنوان یک زنجیره در نظر بگیرید. همانطور که در شکل نشان داده شده است، بین فایل دانلود شده و عناصر رسانه چندین لایه وجود دارد.
- یک عنصر
<audio>
یا<video>
برای پخش رسانه. - یک نمونه
MediaSource
با یکSourceBuffer
برای تغذیه عنصر رسانه. - فراخوانی
fetch()
یا XHR برای بازیابی داده های رسانه در یک شیResponse
. - فراخوانی به
Response.arrayBuffer()
برای تغذیهMediaSource.SourceBuffer
.
در عمل، زنجیره به صورت زیر است:
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);
});
}
اگر میتوانید موارد را از توضیحات تا به اینجا جدا کنید، همین الان خواندن را متوقف کنید. اگر توضیح دقیق تری می خواهید، لطفا به خواندن ادامه دهید. من قصد دارم با ساختن یک مثال اولیه MSE از این زنجیره عبور کنم. هر یک از مراحل ساخت کد را به مرحله قبل اضافه می کند.
نکته ای در مورد وضوح
آیا این مقاله همه آنچه را که باید در مورد پخش رسانه در یک صفحه وب بدانید به شما می گوید؟ نه، این فقط برای کمک به درک کدهای پیچیده تری است که ممکن است در جاهای دیگر پیدا کنید. برای وضوح، این سند بسیاری از چیزها را ساده و حذف می کند. ما فکر میکنیم که میتوانیم از این موضوع خلاص شویم زیرا استفاده از کتابخانهای مانند Shaka Player Google را نیز توصیه میکنیم. جاهایی را که عمداً سادهسازی میکنم، در سرتاسر یادداشت میکنم.
چند مورد که پوشش داده نشده است
در اینجا، بدون ترتیب خاصی، چند مورد وجود دارد که نمیپردازم.
- کنترل های پخش با استفاده از عناصر
<audio>
و<video>
HTML5 آنها را به صورت رایگان دریافت می کنیم. - رسیدگی به خطا.
برای استفاده در محیط های تولید
در اینجا مواردی وجود دارد که من در استفاده تولیدی از APIهای مرتبط با MSE توصیه می کنم:
- قبل از برقراری تماس با این APIها، هرگونه رویداد خطا یا استثناهای API را مدیریت کنید و
HTMLMediaElement.readyState
وMediaSource.readyState
را بررسی کنید. این مقادیر می توانند قبل از تحویل رویدادهای مرتبط تغییر کنند. - قبل از بهروزرسانی
mode
SourceBuffer
،timestampOffset
،appendWindowStart
،appendWindowEnd
، یا فراخوانیappendBuffer()
یاremove()
درSourceBuffer
مطمئن شوید که تماسهای قبلیappendBuffer()
وremove()
هنوز در حال انجام نیستند، با بررسی مقدار منطقیSourceBuffer.updating
. - برای همه نمونههای
SourceBuffer
که بهMediaSource
شما اضافه شدهاند، قبل از فراخوانیMediaSource.endOfStream()
یا بهروزرسانیMediaSource.duration
مطمئن شوید که هیچ یک از مقادیرupdating
آنها درست نیست. - اگر مقدار
MediaSource.readyState
بهended
، فراخوانی هایی مانندappendBuffer()
وremove()
یا تنظیمSourceBuffer.mode
یاSourceBuffer.timestampOffset
باعث می شود که این مقدارopen
شود. این بدان معنی است که شما باید برای مدیریت چندین رویدادsourceopen
آماده باشید. - هنگام مدیریت رویدادهای
HTMLMediaElement error
، محتویاتMediaError.message
می تواند برای تعیین علت اصلی خرابی مفید باشد، به خصوص برای خطاهایی که بازتولید آن ها در محیط های آزمایشی دشوار است.
یک نمونه MediaSource را به یک عنصر رسانه متصل کنید
مانند بسیاری از موارد در توسعه وب این روزها، شما با تشخیص ویژگی شروع می کنید. در مرحله بعد، یک عنصر رسانه، یا عنصر <audio>
یا <video>
را دریافت کنید. در نهایت یک نمونه از MediaSource
ایجاد کنید. به یک URL تبدیل می شود و به ویژگی منبع عنصر رسانه منتقل می شود.
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
انجام دهید، کمی تاخیر ایجاد می کند. خوشبختانه راه هایی برای آزمایش این موضوع وجود دارد. ساده ترین راه با یک ویژگی MediaSource
به نام readyState
است. ویژگی 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 شامل یک نوع mime و دو کدک است. این یک رشته mime برای یک فایل ویدیویی است، اما از کدک های جداگانه برای بخش های ویدیویی و صوتی فایل استفاده می کند.
نسخه 1 مشخصات MSE به عوامل کاربر اجازه می دهد تا در مورد نیاز به نوع mime و کدک متفاوت باشند. برخی از عوامل کاربر نیازی ندارند، اما فقط به نوع mime اجازه می دهند. برخی از عوامل کاربر، برای مثال کروم، به یک کدک برای انواع 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>;
}
فایل رسانه را دریافت کنید
اگر در اینترنت برای نمونه های MSE جستجو کنید، تعداد زیادی فایل های رسانه ای را با استفاده از XHR بازیابی خواهید کرد. برای پیشرفت بیشتر، من قصد دارم از Fetch API و Promise it استفاده کنم. اگر میخواهید این کار را در Safari انجام دهید، بدون fetch()
polyfill کار نمیکند.
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>;
}
یک پخش کننده با کیفیت تولید، یک فایل را در چندین نسخه برای پشتیبانی از مرورگرهای مختلف خواهد داشت. می تواند از فایل های جداگانه برای صدا و تصویر استفاده کند تا امکان انتخاب صدا بر اساس تنظیمات زبان را فراهم کند.
کد دنیای واقعی همچنین دارای چندین نسخه از فایلهای رسانهای با وضوحهای مختلف است تا بتواند با قابلیتهای دستگاه و شرایط شبکه متفاوت سازگار شود. چنین برنامهای میتواند ویدیوها را با استفاده از درخواستهای محدوده یا بخشها بارگیری و پخش کند. این امکان انطباق با شرایط شبکه را در حین پخش رسانه فراهم می کند. ممکن است اصطلاحات DASH یا HLS را شنیده باشید که دو روش برای انجام این کار هستند. بحث کامل درباره این موضوع از حوصله این مقدمه خارج است.
شی پاسخ را پردازش کنید
به نظر می رسد کد تقریباً تمام شده است، اما رسانه پخش نمی شود. ما باید داده های رسانه را از شی 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);
});
}