WebCodecs'in gücünü keşfedin! VideoFrame düzlemlerini kullanarak video kare verilerine erişmek ve bu verileri işlemek için kapsamlı bir rehber. Tarayıcıda gelişmiş video işleme için piksel formatları, bellek düzeni ve pratik kullanım alanları hakkında bilgi edinin.
WebCodecs VideoFrame Düzlemi: Video Kare Veri Erişimine Derinlemesine Bir Bakış
WebCodecs, web tabanlı medya işlemede bir paradigma kaymasını temsil eder. Geliştiricilerin doğrudan tarayıcıda sofistike uygulamalar oluşturmasını sağlayan, medyanın yapı taşlarına düşük seviyeli erişim sunar. WebCodecs'in en güçlü özelliklerinden biri VideoFrame nesnesidir ve bu nesnenin içinde, video karelerinin ham piksel verilerini açığa çıkaran VideoFrame düzlemleri bulunur. Bu makale, gelişmiş video manipülasyonu için VideoFrame düzlemlerini anlama ve kullanma konusunda kapsamlı bir rehber sunmaktadır.
VideoFrame Nesnesini Anlamak
Düzlemlere dalmadan önce, VideoFrame nesnesinin kendisini özetleyelim. Bir VideoFrame, tek bir video karesini temsil eder. Kodlanmış (veya çözülmüş) video verilerini, zaman damgası, süre ve format bilgisi gibi ilişkili meta verilerle birlikte kapsar. VideoFrame API'si şu işlemler için yöntemler sunar:
- Piksel verilerini okuma: Düzlemlerin devreye girdiği yer burasıdır.
- Kareleri kopyalama: Mevcut
VideoFramenesnelerinden yeniVideoFramenesneleri oluşturma. - Kareleri kapatma: Karenin sahip olduğu temel kaynakları serbest bırakma.
VideoFrame nesnesi, kod çözme işlemi sırasında genellikle bir VideoDecoder tarafından veya özel bir kare oluşturulurken manuel olarak oluşturulur.
VideoFrame Düzlemleri Nedir?
Bir VideoFrame'in piksel verileri, özellikle YUV gibi formatlarda, genellikle birden çok düzleme ayrılmıştır. Her düzlem, görüntünün farklı bir bileşenini temsil eder. Örneğin, bir YUV420 formatında üç düzlem bulunur:
- Y (Luma): Görüntünün parlaklığını (luminans) temsil eder. Bu düzlem, gri tonlama bilgilerini içerir.
- U (Cb): Mavi-fark kroma bileşenini temsil eder.
- V (Cr): Kırmızı-fark kroma bileşenini temsil eder.
RGB formatları, daha basit görünseler de bazı durumlarda birden çok düzlem kullanabilirler. Düzlem sayısı ve anlamları tamamen VideoFrame'in VideoPixelFormat'ına bağlıdır.
Düzlemleri kullanmanın avantajı, belirli renk bileşenlerine verimli erişim ve manipülasyon sağlamasıdır. Örneğin, rengi (U ve V düzlemleri) etkilemeden yalnızca parlaklığı (Y düzlemi) ayarlamak isteyebilirsiniz.
VideoFrame Düzlemlerine Erişim: API
VideoFrame API'si, düzlem verilerine erişmek için aşağıdaki yöntemleri sunar:
copyTo(destination, options):VideoFrame'in içeriğini, başka birVideoFrame, birCanvasImageBitmapveya birArrayBufferViewolabilen bir hedefe kopyalar.optionsnesnesi, hangi düzlemlerin nasıl kopyalanacağını kontrol eder. Bu, düzlem erişimi için birincil mekanizmadır.
copyTo yöntemindeki options nesnesi, video kare verileri için düzeni ve hedefi belirtmenize olanak tanır. Anahtar özellikler şunları içerir:
format: Kopyalanan verinin istenen piksel formatı. Bu, orijinalVideoFrameile aynı format veya farklı bir format olabilir (örneğin, YUV'dan RGB'ye dönüştürme).codedWidthvecodedHeight: Video karesinin piksel cinsinden genişliği ve yüksekliği.layout: Bellekteki her düzlemin düzenini açıklayan bir nesne dizisi. Dizideki her nesne şunları belirtir:offset: Veri arabelleğinin başlangıcından düzlemin verilerinin başlangıcına kadar olan bayt cinsinden ofset.stride: Düzlemdeki her satırın başlangıcı arasındaki bayt sayısı. Bu, dolgu (padding) işlemlerini yönetmek için çok önemlidir.
Şimdi bir YUV420 VideoFrame'ini ham bir arabelleğe kopyalama örneğine bakalım:
async function copyYUV420ToBuffer(videoFrame, buffer) {
const width = videoFrame.codedWidth;
const height = videoFrame.codedHeight;
// YUV420 has 3 planes: Y, U, and V
const yPlaneSize = width * height;
const uvPlaneSize = width * height / 4;
const layout = [
{ offset: 0, stride: width }, // Y plane
{ offset: yPlaneSize, stride: width / 2 }, // U plane
{ offset: yPlaneSize + uvPlaneSize, stride: width / 2 } // V plane
];
await videoFrame.copyTo(buffer, {
format: 'I420',
codedWidth: width,
codedHeight: height,
layout: layout
});
videoFrame.close(); // Important to release resources
}
Açıklama:
- Her düzlemin boyutunu
widthveheight'e göre hesaplıyoruz. Y tam çözünürlüktedir, U ve V ise alt örneklenmiştir (4:2:0). layoutdizisi bellek düzenini tanımlar.offset, her düzlemin arabellekte nerede başladığını belirtir vestride, o düzlemdeki bir sonraki satıra atlamak için gereken bayt sayısını belirtir.formatseçeneği, yaygın bir YUV420 formatı olan 'I420' olarak ayarlanmıştır.- Kritik olarak, kopyalama işleminden sonra kaynakları serbest bırakmak için
videoFrame.close()çağrılır.
Piksel Formatları: Olasılıklar Dünyası
Piksel formatlarını anlamak, VideoFrame düzlemleriyle çalışmak için esastır. VideoPixelFormat, renk bilgisinin video karesi içinde nasıl kodlandığını tanımlar. İşte karşılaşabileceğiniz bazı yaygın piksel formatları:
- I420 (YUV420p): Y, U ve V bileşenlerinin ayrı düzlemlerde saklandığı düzlemsel bir YUV formatı. U ve V, hem yatay hem de dikey boyutlarda 2 faktörüyle alt örneklenmiştir. Çok yaygın ve verimli bir formattır.
- NV12 (YUV420sp): Y'nin bir düzlemde, U ve V bileşenlerinin ise ikinci bir düzlemde iç içe saklandığı yarı düzlemsel bir YUV formatı.
- RGBA: Kırmızı, Yeşil, Mavi ve Alfa bileşenleri tek bir düzlemde, genellikle bileşen başına 8 bit (piksel başına 32 bit) olacak şekilde saklanır. Bileşenlerin sırası değişebilir (örneğin, BGRA).
- RGB565: Kırmızı, Yeşil ve Mavi bileşenleri tek bir düzlemde Kırmızı için 5 bit, Yeşil için 6 bit ve Mavi için 5 bit (piksel başına 16 bit) olacak şekilde saklanır.
- GRAYSCALE: Her piksel için tek bir luma (parlaklık) değeri ile gri tonlamalı görüntüleri temsil eder.
VideoFrame.format özelliği, belirli bir karenin piksel formatını size söyler. Düzlemlere erişmeye çalışmadan önce bu özelliği kontrol ettiğinizden emin olun. Desteklenen formatların tam listesi için WebCodecs spesifikasyonuna başvurabilirsiniz.
Pratik Kullanım Alanları
VideoFrame düzlemlerine erişmek, tarayıcıda gelişmiş video işleme için çok çeşitli olasılıkların kapısını aralar. İşte bazı örnekler:
1. Gerçek Zamanlı Video Efektleri
VideoFrame'deki piksel verilerini işleyerek gerçek zamanlı video efektleri uygulayabilirsiniz. Örneğin, bir RGBA karesindeki her pikselin R, G ve B bileşenlerinin ortalamasını alıp ardından üç bileşeni de bu ortalama değere ayarlayarak bir gri tonlama filtresi uygulayabilirsiniz. Ayrıca sepya tonu efekti oluşturabilir veya parlaklığı ve kontrastı ayarlayabilirsiniz.
async function applyGrayscale(videoFrame) {
const width = videoFrame.codedWidth;
const height = videoFrame.codedHeight;
const buffer = new ArrayBuffer(width * height * 4); // RGBA
const rgba = new Uint8ClampedArray(buffer);
await videoFrame.copyTo(rgba, {
format: 'RGBA',
codedWidth: width,
codedHeight: height
});
for (let i = 0; i < rgba.length; i += 4) {
const r = rgba[i];
const g = rgba[i + 1];
const b = rgba[i + 2];
const gray = (r + g + b) / 3;
rgba[i] = gray; // Red
rgba[i + 1] = gray; // Green
rgba[i + 2] = gray; // Blue
}
// Create a new VideoFrame from the modified data.
const newFrame = new VideoFrame(rgba, {
format: 'RGBA',
codedWidth: width,
codedHeight: height,
timestamp: videoFrame.timestamp,
duration: videoFrame.duration
});
videoFrame.close(); // Release original frame
return newFrame;
}
2. Bilgisayarla Görü Uygulamaları
VideoFrame düzlemleri, bilgisayarla görü görevleri için gereken piksel verilerine doğrudan erişim sağlar. Bu verileri nesne tespiti, yüz tanıma, hareket takibi ve daha fazlası için algoritmalar uygulamak üzere kullanabilirsiniz. Kodunuzun performans açısından kritik bölümleri için WebAssembly'den yararlanabilirsiniz.
Örneğin, renkli bir VideoFrame'i gri tonlamaya dönüştürebilir ve ardından görüntüdeki kenarları belirlemek için bir kenar algılama algoritması (örneğin, Sobel operatörü) uygulayabilirsiniz. Bu, nesne tanıma için bir ön işleme adımı olarak kullanılabilir.
3. Video Düzenleme ve Birleştirme (Compositing)
VideoFrame düzlemlerini, kırpma, ölçekleme, döndürme ve birleştirme gibi video düzenleme özelliklerini uygulamak için kullanabilirsiniz. Piksel verilerini doğrudan işleyerek özel geçişler ve efektler oluşturabilirsiniz.
Örneğin, piksel verilerinin yalnızca bir kısmını yeni bir VideoFrame'e kopyalayarak bir VideoFrame'i kırpabilirsiniz. layout ofsetlerini ve adımlarını buna göre ayarlamanız gerekir.
4. Özel Codec'ler ve Kod Dönüştürme (Transcoding)
WebCodecs, AV1, VP9 ve H.264 gibi yaygın codec'ler için yerleşik destek sağlarken, özel codec'ler veya kod dönüştürme işlem hatları uygulamak için de kullanabilirsiniz. Kodlama ve kod çözme sürecini kendiniz yönetmeniz gerekir, ancak VideoFrame düzlemleri ham piksel verilerine erişmenize ve bunları işlemenize olanak tanır. Bu, niş video formatları veya özel kodlama gereksinimleri için yararlı olabilir.
5. Gelişmiş Analitik
Temeldeki piksel verilerine erişerek, video içeriğinin derinlemesine analizini yapabilirsiniz. Bu, bir sahnenin ortalama parlaklığını ölçme, baskın renkleri belirleme veya sahne içeriğindeki değişiklikleri tespit etme gibi görevleri içerir. Bu, güvenlik, gözetim veya içerik analizi için gelişmiş video analitik uygulamalarını mümkün kılabilir.
Canvas ve WebGL ile Çalışma
VideoFrame düzlemlerindeki piksel verilerini doğrudan işleyebilseniz de, sonucu genellikle ekrana yansıtmanız gerekir. CanvasImageBitmap arayüzü, VideoFrame ile <canvas> öğesi arasında bir köprü sağlar. Bir VideoFrame'den bir CanvasImageBitmap oluşturabilir ve ardından drawImage() yöntemini kullanarak tuvale çizebilirsiniz.
async function renderVideoFrameToCanvas(videoFrame, canvas) {
const bitmap = await createImageBitmap(videoFrame);
const ctx = canvas.getContext('2d');
ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
bitmap.close(); // Release bitmap resources
videoFrame.close(); // Release VideoFrame resources
}
Daha gelişmiş görüntüleme için WebGL kullanabilirsiniz. VideoFrame düzlemlerindeki piksel verilerini WebGL dokularına yükleyebilir ve ardından efektler ve dönüşümler uygulamak için gölgelendiriciler (shader) kullanabilirsiniz. Bu, yüksek performanslı video işleme için GPU'dan yararlanmanıza olanak tanır.
Performans Değerlendirmeleri
Ham piksel verileriyle çalışmak hesaplama açısından yoğun olabilir, bu nedenle performans optimizasyonunu göz önünde bulundurmak çok önemlidir. İşte bazı ipuçları:
- Kopyaları en aza indirin: Gereksiz piksel verisi kopyalamaktan kaçının. Mümkün olduğunda işlemleri yerinde (in-place) yapmaya çalışın.
- WebAssembly kullanın: Kodunuzun performans açısından kritik bölümleri için WebAssembly kullanmayı düşünün. WebAssembly, hesaplama açısından yoğun görevler için neredeyse yerel (native) performans sağlayabilir.
- Bellek düzenini optimize edin: Uygulamanız için doğru piksel formatını ve bellek düzenini seçin. Bireysel renk bileşenlerine sık sık erişmeniz gerekmiyorsa paketlenmiş formatları (örneğin, RGBA) kullanmayı düşünün.
- OffscreenCanvas kullanın: Arka plan işlemleri için ana iş parçacığını (main thread) engellememek adına
OffscreenCanvaskullanın. - Kodunuzu profilleyin: Kodunuzu profillemek ve performans darboğazlarını belirlemek için tarayıcı geliştirici araçlarını kullanın.
Tarayıcı Uyumluluğu
WebCodecs ve VideoFrame API'si, Chrome, Firefox ve Safari dahil olmak üzere çoğu modern tarayıcıda desteklenmektedir. Ancak, destek seviyesi tarayıcı sürümüne ve işletim sistemine bağlı olarak değişebilir. Kullandığınız özelliklerin hedef tarayıcılarınızda desteklendiğinden emin olmak için MDN Web Docs gibi sitelerdeki en son tarayıcı uyumluluk tablolarını kontrol edin. Tarayıcılar arası uyumluluk için özellik tespiti (feature detection) önerilir.
Sık Karşılaşılan Tuzaklar ve Sorun Giderme
VideoFrame düzlemleriyle çalışırken kaçınılması gereken bazı yaygın tuzaklar şunlardır:
- Yanlış düzen:
layoutdizisinin piksel verilerinin bellek düzenini doğru bir şekilde tanımladığından emin olun. Yanlış ofsetler veya adımlar (stride) bozuk görüntülere yol açabilir. - Uyuşmayan piksel formatları:
copyToyönteminde belirttiğiniz piksel formatınınVideoFrame'in gerçek formatıyla eşleştiğinden emin olun. - Bellek sızıntıları: Temeldeki kaynakları serbest bırakmak için işiniz bittikten sonra
VideoFrameveCanvasImageBitmapnesnelerini her zaman kapatın. Bunu yapmamak bellek sızıntılarına yol açabilir. - Asenkron işlemler:
copyTo'nun asenkron bir işlem olduğunu unutmayın. Piksel verilerine erişmeden önce kopyalama işleminin tamamlandığından emin olmak içinawaitkullanın. - Güvenlik kısıtlamaları: Farklı kaynaklardan (cross-origin) gelen videolardan piksel verilerine erişirken geçerli olabilecek güvenlik kısıtlamalarının farkında olun.
Örnek: YUV'dan RGB'ye Dönüşüm
Daha karmaşık bir örneği ele alalım: bir YUV420 VideoFrame'ini bir RGB VideoFrame'ine dönüştürmek. Bu, Y, U ve V düzlemlerini okumayı, bunları RGB değerlerine dönüştürmeyi ve ardından yeni bir RGB VideoFrame'i oluşturmayı içerir.
Bu dönüşüm aşağıdaki formül kullanılarak uygulanabilir:
R = Y + 1.402 * (Cr - 128)
G = Y - 0.34414 * (Cb - 128) - 0.71414 * (Cr - 128)
B = Y + 1.772 * (Cb - 128)
İşte kod:
async function convertYUV420ToRGBA(videoFrame) {
const width = videoFrame.codedWidth;
const height = videoFrame.codedHeight;
const yPlaneSize = width * height;
const uvPlaneSize = width * height / 4;
const yuvBuffer = new ArrayBuffer(yPlaneSize + 2 * uvPlaneSize);
const yuvPlanes = new Uint8ClampedArray(yuvBuffer);
const layout = [
{ offset: 0, stride: width }, // Y plane
{ offset: yPlaneSize, stride: width / 2 }, // U plane
{ offset: yPlaneSize + uvPlaneSize, stride: width / 2 } // V plane
];
await videoFrame.copyTo(yuvPlanes, {
format: 'I420',
codedWidth: width,
codedHeight: height,
layout: layout
});
const rgbaBuffer = new ArrayBuffer(width * height * 4);
const rgba = new Uint8ClampedArray(rgbaBuffer);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const yIndex = y * width + x;
const uIndex = Math.floor(y / 2) * (width / 2) + Math.floor(x / 2) + yPlaneSize;
const vIndex = Math.floor(y / 2) * (width / 2) + Math.floor(x / 2) + yPlaneSize + uvPlaneSize;
const Y = yuvPlanes[yIndex];
const U = yuvPlanes[uIndex] - 128;
const V = yuvPlanes[vIndex] - 128;
let R = Y + 1.402 * V;
let G = Y - 0.34414 * U - 0.71414 * V;
let B = Y + 1.772 * U;
R = Math.max(0, Math.min(255, R));
G = Math.max(0, Math.min(255, G));
B = Math.max(0, Math.min(255, B));
const rgbaIndex = y * width * 4 + x * 4;
rgba[rgbaIndex] = R;
rgba[rgbaIndex + 1] = G;
rgba[rgbaIndex + 2] = B;
rgba[rgbaIndex + 3] = 255; // Alpha
}
}
const newFrame = new VideoFrame(rgba, {
format: 'RGBA',
codedWidth: width,
codedHeight: height,
timestamp: videoFrame.timestamp,
duration: videoFrame.duration
});
videoFrame.close(); // Release original frame
return newFrame;
}
Bu örnek, VideoFrame düzlemleriyle çalışmanın gücünü ve karmaşıklığını göstermektedir. Piksel formatları, bellek düzeni ve renk uzayı dönüşümleri hakkında iyi bir anlayış gerektirir.
Sonuç
WebCodecs'teki VideoFrame düzlemi API'si, tarayıcıda video işleme üzerinde yeni bir kontrol seviyesinin kilidini açar. Piksel verilerine doğrudan nasıl erişileceğini ve işleneceğini anlayarak, gerçek zamanlı video efektleri, bilgisayarla görü, video düzenleme ve daha fazlası için gelişmiş uygulamalar oluşturabilirsiniz. VideoFrame düzlemleriyle çalışmak zorlayıcı olsa da, potansiyel ödüller önemlidir. WebCodecs gelişmeye devam ettikçe, şüphesiz medya ile çalışan web geliştiricileri için vazgeçilmez bir araç haline gelecektir.
Sizi VideoFrame düzlemi API'si ile denemeler yapmaya ve yeteneklerini keşfetmeye teşvik ediyoruz. Temel prensipleri anlayarak ve en iyi uygulamaları uygulayarak, tarayıcıda mümkün olanın sınırlarını zorlayan yenilikçi ve performanslı video uygulamaları oluşturabilirsiniz.
İleri Okuma
- WebCodecs üzerine MDN Web Docs
- WebCodecs Spesifikasyonu
- GitHub'daki WebCodecs örnek kod depoları.