Media Source Extensions (MSE), ses veya video segmentlerinden oynatma için akış oluşturmanıza olanak tanıyan bir JavaScript API'sidir. Bu makalede ele alınmasa da sitenize aşağıdaki gibi işlemler yapan videolar yerleştirmek istiyorsanız MSE'yi anlamanız gerekir:
- Uyarlanabilir akış (cihaz özelliklerine ve ağ koşullarına uyum sağlamanın başka bir yolu)
- Reklam ekleme gibi uyarlanabilir birleştirme
- Zaman kaydırma
- Performans ve indirme boyutunu kontrol etme
![Temel MSE veri akışı](https://web.developers.google.cn/static/articles/media-mse-basics/image/basic-mse-data-flow-9f377a6841412.png?authuser=8&hl=tr)
MSE'yi bir zincir olarak düşünebilirsiniz. Şekilde gösterildiği gibi, indirilen dosya ile medya öğeleri arasında birkaç katman vardır.
- Medyayı oynatmak için bir
<audio>
veya<video>
öğesi. - Medya öğesini beslemek için
SourceBuffer
içeren birMediaSource
örneği. - Bir
Response
nesnesinde medya verilerini almak içinfetch()
veya XHR çağrısı. MediaSource.SourceBuffer
feed'ineResponse.arrayBuffer()
tarafından yapılan bir arama.
Pratikte zincir şu şekilde görünür:
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);
});
}
Şimdiye kadarki açıklamaları anladıysanız okumayı bırakabilirsiniz. Daha ayrıntılı bir açıklama için lütfen okumaya devam edin. Temel bir MSE örneği oluşturarak bu zinciri adım adım açıklayacağım. Derleme adımlarının her biri önceki adıma kod ekler.
Anlaşılırlık hakkında bir not
Bu makalede, web sayfasında medya oynatma hakkında bilmeniz gereken her şey açıklanıyor mu? Hayır, yalnızca başka yerlerde bulabileceğiniz daha karmaşık kodları anlamanıza yardımcı olmak için tasarlanmıştır. Anlaşılırlık için bu dokümanda birçok şey basitleştirilmiş ve hariç tutulmuştur. Google'ın Shaka Player gibi bir kitaplık kullanmanızı da önerdiğimiz için bu durumdan sorunsuzca kurtulabileceğimizi düşünüyoruz. Nerede kasıtlı olarak basitleştirdiğimi belirteceğim.
Bu lisansın kapsamında olmayanlar
Belirli bir sırayla belirtmeyeceğim birkaç konuyu aşağıda bulabilirsiniz.
- Oynatma kontrolleri. HTML5
<audio>
ve<video>
öğelerini kullanarak bu bilgileri ücretsiz olarak elde ederiz. - Hata işleme.
Üretim ortamlarında kullanım için
MSE ile ilgili API'lerin üretimde kullanılmasıyla ilgili olarak önerdiğimiz bazı noktalar şunlardır:
- Bu API'lere çağrı yapmadan önce tüm hata etkinliklerini veya API istisnalarını ele alın ve
HTMLMediaElement.readyState
ileMediaSource.readyState
değerlerini kontrol edin. Bu değerler, ilişkili etkinlikler yayınlanmadan önce değişebilir. SourceBuffer
'ınmode
,timestampOffset
,appendWindowStart
,appendWindowEnd
özelliklerini güncellemeden veyaSourceBuffer
'daappendBuffer()
ya daremove()
'ı çağırmadan önceSourceBuffer.updating
boole değerini kontrol ederek öncekiappendBuffer()
veremove()
çağrılarının devam etmediğinden emin olun.MediaSource
'unuza eklenen tümSourceBuffer
örnekleri içinMediaSource.endOfStream()
çağrısını yapmadan veyaMediaSource.duration
öğesini güncellemeden önceupdating
değerlerinin hiçbirinin doğru olmadığından emin olun.MediaSource.readyState
değeriended
iseappendBuffer()
veremove()
gibi çağrılar veyaSourceBuffer.mode
ya daSourceBuffer.timestampOffset
ayarlaması bu değerinopen
değerine geçiş yapmasına neden olur. Bu nedenle, birden fazlasourceopen
etkinliğini yönetmeye hazır olmalısınız.HTMLMediaElement error
etkinlikleri işlenirken, özellikle test ortamlarında yeniden oluşturulması zor olan hatalar içinMediaError.message
içeriği, hatanın temel nedenini belirlemek amacıyla yararlı olabilir.
Bir medya öğesine MediaSource örneği ekleme
Günümüzde web geliştirmedeki birçok şeyde olduğu gibi, özellik algılamayla başlarsınız. Ardından bir medya öğesi (<audio>
veya <video>
öğesi) alın.
Son olarak MediaSource
örneği oluşturun. URL'ye dönüştürülür ve medya öğesinin kaynak özelliğine iletilir.
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.');
}
![Blob olarak bir kaynak özellik](https://web.developers.google.cn/static/articles/media-mse-basics/image/a-source-attribute-a-blo-8b1f76f582a22.png?authuser=8&hl=tr)
Bir MediaSource
nesnesinin src
özelliğine iletilebileceği biraz tuhaf görünebilir. Bunlar genellikle dizelerdir ancak blob da olabilirler.
Yerleşik medya içeren bir sayfayı inceleyip medya öğesini incelerseniz ne demek istediğimi anlayacaksınız.
MediaSource örneği hazır mı?
URL.createObjectURL()
eşzamanlı olsa da eki eşzamansız olarak işler. Bu durum, MediaSource
örneğiyle herhangi bir işlem yapmadan önce biraz gecikme yaşanmasına neden olur. Neyse ki bunu test etmenin yolları var.
En basit yöntem, readyState
adlı bir MediaSource
mülkü kullanmaktır. readyState
mülkü, bir MediaSource
örneği ile medya öğesi arasındaki ilişkiyi tanımlar. Aşağıdaki değerlerden biri olabilir:
closed
:MediaSource
örneği bir medya öğesine eklenmemiş.open
:MediaSource
örneği bir medya öğesine eklenmiştir ve veri almaya hazırdır veya veri almaktadır.ended
:MediaSource
örneği bir medya öğesine eklenmiştir ve tüm verileri bu öğeye iletilmiştir.
Bu seçenekleri doğrudan sorgulamak performansı olumsuz yönde etkileyebilir. Neyse ki MediaSource
, readyState
değiştiğinde (özellikle sourceopen
, sourceclosed
, sourceended
) de etkinlik tetikler. Oluşturacağım örnekte, videoyu ne zaman getireceğimi ve arabelleğe alacağımı belirtmek için sourceopen
etkinliğini kullanacağım.
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()
işlevini de çağırdığımı fark edin. Bunun erken olduğunun farkındayım ancak medya öğesinin src
özelliği bir MediaSource
örneğine bağlandıktan sonra bunu istediğim zaman yapabilirim. Bu yöntem çağrıldığında hiçbir nesne kaldırılmaz. Platformun uygun bir zamanda çöp toplamasını sağlamaktadır. Bu nedenle hemen çağırıyorum.
SourceBuffer Oluşturma
Artık SourceBuffer
nesnesini oluşturmanın zamanı geldi. Bu nesne, verileri medya kaynakları ile medya öğeleri arasında aktarma işini yapan nesnedir. SourceBuffer
, yüklediğiniz medya dosyasının türüne özel olmalıdır.
Uygulamada bunu, addSourceBuffer()
işlevini uygun değerle çağırarak yapabilirsiniz. Aşağıdaki örnekte, mime türü dizesinin bir mime türü ve iki codec içerdiğine dikkat edin. Bu, bir video dosyası için mime dizesidir ancak dosyanın video ve ses bölümleri için ayrı codec'ler kullanır.
MSE spesifikasyonunun 1. sürümü, kullanıcı aracılarının hem mime türü hem de codec gerektirip gerektirmeyeceği konusunda farklı olmasına olanak tanır. Bazı kullanıcı aracıları, mime türüne izin verir ancak codec'e izin vermez. Bazı kullanıcı aracıları (ör. Chrome), kendi codec'lerini tanımlamayan mime türleri için codec gerektirir. Tüm bunları ayırt etmek yerine her ikisini de eklemek daha iyidir.
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>;
}
Medya dosyasını alma
MSE örnekleri için internette arama yaparsanız XHR kullanarak medya dosyalarını alan birçok örnek bulursunuz. Daha da ileri gitmek için Fetch API'yi ve döndürdüğü Promise'i kullanacağım. Bunu Safari'de yapmaya çalışıyorsanız fetch()
polyfill olmadan çalışmaz.
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>;
}
Üretim kalitesinde bir oynatıcı, farklı tarayıcıları desteklemek için aynı dosyanın birden fazla sürümüne sahiptir. Ses ve video için ayrı dosyalar kullanarak sesin dil ayarlarına göre seçilmesine izin verebilir.
Gerçek dünyadaki kod, farklı cihaz özelliklerine ve ağ koşullarına uyum sağlayabilmek için medya dosyalarının farklı çözünürlüklerde birden fazla kopyasına da sahip olur. Bu tür bir uygulama, aralık istekleri veya segmentler kullanarak videoları parçalar halinde yükleyip oynatabilir. Bu, medya oynatılırken ağ koşullarına uyum sağlamaya olanak tanır. DASH veya HLS terimlerini duymuş olabilirsiniz. Bu iki yöntem, bu işlemi gerçekleştirmenin yollarından biridir. Bu konunun ayrıntılı bir şekilde ele alınması bu girişin kapsamı dışındadır.
Yanıt nesnesini işleme
Kod neredeyse tamamlanmış görünüyor ancak medya oynatılmıyor. Response
nesnesinden SourceBuffer
nesnesine medya verileri almamız gerekiyor.
Yanıt nesnesinden MediaSource
örneğine veri aktarmanın tipik yolu, yanıt nesnesinden bir ArrayBuffer
almak ve bunu SourceBuffer
'ye aktarmaktır. Tampona bir söz döndüren response.arrayBuffer()
işlevini çağırarak başlayın. Kodumda bu promise'i, SourceBuffer
'a eklediğim ikinci bir then()
yan tümcesine ilettim.
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() işlevini çağırın.
Tüm ArrayBuffers
eklendikten ve başka medya verisi beklenmedikten sonra MediaSource.endOfStream()
işlevini çağırın. Bu işlemle MediaSource.readyState
, ended
olarak değişir ve sourceended
etkinliği tetiklenir.
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);
});
}
Nihai sürüm
Kod örneğinin tamamını burada bulabilirsiniz. Medya Kaynağı Uzantıları hakkında bilgi edindiğinizi umuyoruz.
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);
});
}