Video akışı bileşenlerini değiştirme.
Modern web teknolojileri, videolarla çalışmak için bol miktarda yol sunar. Media Stream API, Medya Kaydı API'si Medya Kaynağı API'si ve WebRTC API'lerin toplamı video akışlarını kaydetmek, aktarmak ve oynatmak için zengin bir araç setine dönüştürüyor. Bu API'ler, belirli üst düzey görevleri çözerken web'in Programcılar video akışının farklı bileşenleriyle (ör. kareler) kodlanmamış video veya ses parçalarından oluşur. Geliştiriciler bu temel bileşenlere alt düzey erişim elde etmek için uzun bir süredir WebAssembly ile video ve ses codec'lerini tarayıcıya ekleyin. Ama verilen modern tarayıcıların zaten çeşitli codec'ler (genellikle donanımla hızlandırılıyorsa), bunları WebAssembly olarak yeniden paketlemek ise insan ve bilgisayar kaynakları.
WebCodecs API bu verimsizliği ortadan kaldırır programcılara halihazırda mevcut medya bileşenlerini kullanmalarını sağlayarak tarayıcı. Özellikle:
- Video ve ses kod çözücüleri
- Video ve ses kodlayıcıları
- Ham video kareleri
- Resim kod çözücüler
WebCodecs API, tüm siteler üzerinde tam denetim gerektiren web video düzenleyiciler, video konferans, video konferans gibi medya içeriğinin işlenme şekli akış vb.
Video işleme iş akışı
Kareler, video işleme sürecinin en önemli unsurudur. Dolayısıyla, WebCodecs'te çoğu ya da kare tüketir. Video kodlayıcılar, kareleri kodlanmış biçime dönüştürür parçalar. Video kod çözücüler ise tam tersini yapar.
Ayrıca VideoFrame
, bir CanvasImageSource
olarak ve CanvasImageSource
kabul eden bir oluşturucu sayesinde diğer Web API'leriyle uyumlu şekilde çalışır.
Dolayısıyla, drawImage()
ve texImage2D()
gibi işlevlerde kullanılabilir. Ayrıca tuvallerden, bit eşlemlerden, video öğelerinden ve diğer video karelerinden de oluşturulabilir.
WebCodecs API, Insertable Streams API'deki sınıflarla birlikte iyi bir şekilde çalışır WebCodecs'i medya akış kanallarına bağlayan
MediaStreamTrackProcessor
, medya kanallarını bağımsız karelere ayırır.MediaStreamTrackGenerator
, bir kare akışından medya kanalı oluşturur.
WebCodecs ve web çalışanları
WebCodecs API, tasarımı gereği tüm ağır işleri eşzamansız olarak ve ana iş parçacığı dışında yapar. Ancak çerçeve ve parça geri çağırmaları genellikle saniyede birden fazla kez çağrılabildiğinden, ana ileti dizisini karmaşık hale getirip web sitesini daha az duyarlı hale getirebilirler. Dolayısıyla, tek tek karelerin ve kodlanmış parçaların işlenmesinin tek bir kareye taşınması tercih edilir. yardımcı olur.
Bu konuda yardımcı olmak için ReadableStream
bir medya kaynağından gelen tüm kareleri otomatik olarak aktarmak için uygun bir yol sunar.
takip etmeniz gerekir. Örneğin MediaStreamTrackProcessor
,
Web kamerasından gelen medya akışı parçası için ReadableStream
. Ondan sonra
Akış, karelerin tek tek okunduğu ve sıraya alındığı bir web çalışanına aktarılır
VideoEncoder
dönüştürüldü.
HTMLCanvasElement.transferControlToOffscreen
ile ana iş parçacığından eşit oluşturma işlemi de yapılabilir. Ancak tüm üst düzey araçlar
rahatsızlık verdiğinden, VideoFrame
alanının kendisi aktarılabilir ve
nasıl yürütüleceğini planlayabilirsiniz.
WebCodecs'in işleyiş şekli
Kodlama
Her şey bir VideoFrame
ile başlar.
Video kareleri oluşturmanın üç yolu vardır.
Tuval, resim bit eşlemi veya video öğesi gibi bir resim kaynağından.
const canvas = document.createElement("canvas"); // Draw something on the canvas... const frameFromCanvas = new VideoFrame(canvas, { timestamp: 0 });
MediaStreamTrack
öğesinden kare almak içinMediaStreamTrackProcessor
işlemini kullanınconst stream = await navigator.mediaDevices.getUserMedia({…}); const track = stream.getTracks()[0]; const trackProcessor = new MediaStreamTrackProcessor(track); const reader = trackProcessor.readable.getReader(); while (true) { const result = await reader.read(); if (result.done) break; const frameFromCamera = result.value; }
BufferSource
'da ikili piksel gösteriminden bir kare oluşturmaconst pixelSize = 4; const init = { timestamp: 0, codedWidth: 320, codedHeight: 200, format: "RGBA", }; const data = new Uint8Array(init.codedWidth * init.codedHeight * pixelSize); for (let x = 0; x < init.codedWidth; x++) { for (let y = 0; y < init.codedHeight; y++) { const offset = (y * init.codedWidth + x) * pixelSize; data[offset] = 0x7f; // Red data[offset + 1] = 0xff; // Green data[offset + 2] = 0xd4; // Blue data[offset + 3] = 0x0ff; // Alpha } } const frame = new VideoFrame(data, init);
Nereden gelirlerse görsün, kareler otomatik olarak
VideoEncoder
içeren EncodedVideoChunk
nesne.
Kodlamadan önce VideoEncoder
öğesine iki JavaScript nesnesi verilmesi gerekir:
- Kodlanmış parçaları işlemek için iki işleve ve
hatalar. Bu işlevler geliştirici tarafından tanımlanır ve daha sonra değiştirilemez
VideoEncoder
oluşturucuya geçirilirler. - Çıkışla ilgili parametreleri içeren kodlayıcı yapılandırma nesnesi
video akışını izleyin. Bu parametreleri daha sonra
configure()
çağrısı yaparak değiştirebilirsiniz.
Yapılandırma uygun değilse configure()
yöntemi NotSupportedError
değerini atar
tarayıcı tarafından destekleniyor. Statik yöntemi çağırmanız önerilir.
yapılandırma ile VideoEncoder.isConfigSupported()
uyumlu olup olmadığını önceden kontrol etmek için
destekleniyorsa yapılandırmanın size ulaşmasını bekleyin.
const init = {
output: handleChunk,
error: (e) => {
console.log(e.message);
},
};
const config = {
codec: "vp8",
width: 640,
height: 480,
bitrate: 2_000_000, // 2 Mbps
framerate: 30,
};
const { supported } = await VideoEncoder.isConfigSupported(config);
if (supported) {
const encoder = new VideoEncoder(init);
encoder.configure(config);
} else {
// Try another config.
}
Kodlayıcı ayarlandıktan sonra encode()
yöntemiyle kareleri kabul edebilir.
Hem configure()
hem de encode()
, beklemeden hemen geri döner.
gerçek işin tamamlanma tarihidir. Birkaç karenin aynı karede kodlama için sıraya girmesine olanak
aynı anda, encodeQueueSize
ise sırada bekleyen kaç istek olduğunu gösterir
son kodlamaya göre test edin.
Hatalar, bağımsız değişkenlerin sıfırlanması veya
veya yöntem çağrılarının sırası API sözleşmesini ihlal ediyor ya da error()
codec uygulamasında karşılaşılan sorunlar için geri çağırma olanağı sunar.
Kodlama başarıyla tamamlanırsa output()
geri çağırma, bağımsız değişken olarak yeni bir kodlanmış parçayla çağrılır.
Buradaki önemli bir ayrıntı da, çerçevelerin
daha uzun süre gerekiyor. close()
let frameCounter = 0;
const track = stream.getVideoTracks()[0];
const trackProcessor = new MediaStreamTrackProcessor(track);
const reader = trackProcessor.readable.getReader();
while (true) {
const result = await reader.read();
if (result.done) break;
const frame = result.value;
if (encoder.encodeQueueSize > 2) {
// Too many frames in flight, encoder is overwhelmed
// let's drop this frame.
frame.close();
} else {
frameCounter++;
const keyFrame = frameCounter % 150 == 0;
encoder.encode(frame, { keyFrame });
frame.close();
}
}
Son olarak, kodu işleyen bir fonksiyon yazarak parçaları kodlayıcıdan çıktıktan sonra gösterir. Genellikle bu işlev, veri parçalarını ağ üzerinden gönderir veya mux bir medyaya gönderir bir depolama konteyneri.
function handleChunk(chunk, metadata) {
if (metadata.decoderConfig) {
// Decoder needs to be configured (or reconfigured) with new parameters
// when metadata has a new decoderConfig.
// Usually it happens in the beginning or when the encoder has a new
// codec specific binary configuration. (VideoDecoderConfig.description).
fetch("/upload_extra_data", {
method: "POST",
headers: { "Content-Type": "application/octet-stream" },
body: metadata.decoderConfig.description,
});
}
// actual bytes of encoded data
const chunkData = new Uint8Array(chunk.byteLength);
chunk.copyTo(chunkData);
fetch(`/upload_chunk?timestamp=${chunk.timestamp}&type=${chunk.type}`, {
method: "POST",
headers: { "Content-Type": "application/octet-stream" },
body: chunkData,
});
}
Herhangi bir noktada, bekleyen tüm kodlama isteklerinde
tamamlandıysa flush()
numaralı telefonu arayıp söz vermesini bekleyebilirsiniz.
await encoder.flush();
Kod çözme
VideoDecoder
kurulumu,
VideoEncoder
: Kod çözücü oluşturulduğunda iki işlev aktarılır ve codec
parametreleri configure()
değerine verilir.
Codec parametreleri grubu, codec'ten codec'e değişir. Örneğin, H.264 codec'i
ikili blob gerekebilir
Ek B biçiminde (encoderConfig.avc = { format: "annexb" }
) kodlanmadığı sürece
const init = {
output: handleFrame,
error: (e) => {
console.log(e.message);
},
};
const config = {
codec: "vp8",
codedWidth: 640,
codedHeight: 480,
};
const { supported } = await VideoDecoder.isConfigSupported(config);
if (supported) {
const decoder = new VideoDecoder(init);
decoder.configure(config);
} else {
// Try another config.
}
Kod çözücü başlatıldıktan sonra EncodedVideoChunk
nesneyle beslemeye başlayabilirsiniz.
Yığın oluşturmak için gerekenler:
- Kodlanmış video verilerinin
BufferSource
değeri - parçanın mikrosaniye cinsinden başlangıç zaman damgası (parçadaki ilk kodlanmış karenin medya zamanı)
- şunlardan biri:
- Parçanın kodu önceki parçalardan bağımsız olarak çözülebiliyorsa
key
- Parçanın kodu yalnızca bir veya daha fazla önceki parçanın kodu çözüldükten sonra çözülebiliyorsa
delta
- Parçanın kodu önceki parçalardan bağımsız olarak çözülebiliyorsa
Ayrıca kodlayıcı tarafından yayınlanan parçalar, kod çözücü için olduğu gibi hazırdır. Hata raporlama ve eşzamansız doğa hakkında yukarıda söylenenlerin tümü yöntem, kod çözücüler için de aynı şekilde geçerlidir.
const responses = await downloadVideoChunksFromServer(timestamp);
for (let i = 0; i < responses.length; i++) {
const chunk = new EncodedVideoChunk({
timestamp: responses[i].timestamp,
type: responses[i].key ? "key" : "delta",
data: new Uint8Array(responses[i].body),
});
decoder.decode(chunk);
}
await decoder.flush();
Şimdi sıra kodu çözülmüş yeni bir karenin sayfada nasıl gösterilebileceğini göstermeye geldi. İnsanların
kod çözücü çıkışı geri çağırmasının (handleFrame()
) sağlandığından emin olun
geri dönüyor. Aşağıdaki örnekte, yalnızca
kareler oluşturulmaya hazır.
Oluşturma işlemi ayrı olarak gerçekleşir ve iki adımdan oluşur:
- Karenin gösterilmesi için doğru zaman bekleniyor.
- Çerçeve tuvale çiziliyor.
Bir kareye artık ihtiyaç kalmadığında temel belleği serbest bırakmak için close()
çağrısı yapın
daha kendisine ulaşmadan önce depolama alanının
web uygulaması tarafından kullanılan bellek.
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let pendingFrames = [];
let underflow = true;
let baseTime = 0;
function handleFrame(frame) {
pendingFrames.push(frame);
if (underflow) setTimeout(renderFrame, 0);
}
function calculateTimeUntilNextFrame(timestamp) {
if (baseTime == 0) baseTime = performance.now();
let mediaTime = performance.now() - baseTime;
return Math.max(0, timestamp / 1000 - mediaTime);
}
async function renderFrame() {
underflow = pendingFrames.length == 0;
if (underflow) return;
const frame = pendingFrames.shift();
// Based on the frame's timestamp calculate how much of real time waiting
// is needed before showing the next frame.
const timeUntilNextFrame = calculateTimeUntilNextFrame(frame.timestamp);
await new Promise((r) => {
setTimeout(r, timeUntilNextFrame);
});
ctx.drawImage(frame, 0, 0);
frame.close();
// Immediately schedule rendering of the next frame
setTimeout(renderFrame, 0);
}
Geliştiriciler İpuçları
Medya Paneli'ni kullanın Chrome Geliştirici Araçları'nı kullanabilirsiniz.
Demo
Aşağıdaki demoda, tuvaldeki animasyon karelerinin nasıl olduğu gösterilmektedir:
MediaStreamTrackProcessor
tarafındanReadableStream
video ile 25 fps'de yakalandı- bir web çalışanına aktarıldı
- H.264 video biçiminde kodlanmış
- kodu bir dizi video karesine dönüştürüldü
- ve
transferControlToOffscreen()
kullanılarak ikinci tuvalde oluşturulur
Diğer demolar
Diğer demolarımıza da göz atın:
WebCodecs API'yi kullanma
Özellik algılama
WebCodecs desteğini kontrol etmek için:
if ('VideoEncoder' in window) {
// WebCodecs API is supported.
}
WebCodecs API'nin yalnızca güvenli bağlamlarda kullanılabildiğini unutmayın.
dolayısıyla self.isSecureContext
yanlış değerine ayarlanırsa algılama başarısız olur.
Geri bildirim
Chrome ekibi, WebCodecs API ile ilgili deneyimlerinizi öğrenmek istiyor.
Bize API tasarımı hakkında bilgi verin
API'de beklediğiniz gibi çalışmayan bir şey mi var? Veya fikrinizi uygulamak için gereken yöntemler veya özellikler var mı? Bir hakkında soru sormak veya yorum yapmak mı istiyorsunuz? Spesifikasyon sorununu GitHub kod deposu'na gidin veya aklınızdan çıkarabilirsiniz.
Uygulamayla ilgili bir sorunu bildirin
Chrome'un uygulanmasıyla ilgili bir hata buldunuz mu? Yoksa
ve spesifikasyondan farklı mı? new.crbug.com adresinden hata bildiriminde bulunun.
Açıklamanıza mümkün olduğunca fazla ayrıntı, basit talimatlar
oluşturup Bileşenler kutusuna Blink>Media>WebCodecs
yazın.
Glitch, hızlı ve kolay yeniden oluşturmalar paylaşmak için idealdir.
API'ye desteğinizi gösterin
WebCodecs API'yi kullanmayı planlıyor musunuz? Herkese açık desteğiniz, Chrome ekibinin özellikleri önceliklendirmesi ve diğer tarayıcı tedarikçilerine asıl amaç onları desteklemek.
media-dev@chromium.org adresine e-posta veya tweet gönderin
@ChromiumDev hashtag'ini kullanarak
#WebCodecs
ve nerede ve nasıl kullandığınızı bize bildirin.
Hero resim: Deniz Can Unsplash'i açın.