Отключете силата на WebCodecs! Цялостно ръководство за достъп и манипулиране на данни от видео кадри чрез VideoFrame planes. Научете за пикселните формати, разположението в паметта и практическите приложения за напреднала обработка на видео в браузъра.
WebCodecs VideoFrame Plane: Подробен поглед върху достъпа до данните на видео кадрите
WebCodecs представлява промяна на парадигмата в уеб-базираната обработка на медия. Той предоставя достъп на ниско ниво до градивните елементи на медията, позволявайки на разработчиците да създават сложни приложения директно в браузъра. Една от най-мощните функции на WebCodecs е обектът VideoFrame, а в него – плоскостите (planes) на VideoFrame, които излагат суровите пикселни данни на видео кадрите. Тази статия предоставя цялостно ръководство за разбиране и използване на плоскостите на VideoFrame за напреднала манипулация на видео.
Разбиране на обекта VideoFrame
Преди да се потопим в плоскостите, нека припомним самия обект VideoFrame. Един VideoFrame представлява единичен кадър от видео. Той капсулира декодираните (или кодирани) видео данни, заедно със свързаните метаданни като времеви маркер, продължителност и информация за формата. API-то на VideoFrame предлага методи за:
- Четене на пикселни данни: Тук се намесват плоскостите.
- Копиране на кадри: Създаване на нови
VideoFrameобекти от съществуващи. - Затваряне на кадри: Освобождаване на основните ресурси, държани от кадъра.
Обектът VideoFrame се създава по време на процеса на декодиране, обикновено от VideoDecoder, или ръчно при създаване на персонализиран кадър.
Какво представляват VideoFrame Planes?
Пикселните данни на VideoFrame често са организирани в няколко плоскости, особено във формати като YUV. Всяка плоскост представлява различен компонент на изображението. Например, във формат YUV420 има три плоскости:
- Y (Luma): Представлява яркостта (луминанс) на изображението. Тази плоскост съдържа информацията в сивата гама.
- U (Cb): Представлява хроматичния компонент на разликата в синьото.
- V (Cr): Представлява хроматичния компонент на разликата в червеното.
RGB форматите, макар и привидно по-прости, в някои случаи също могат да използват множество плоскости. Броят на плоскостите и тяхното значение зависят изцяло от VideoPixelFormat на VideoFrame.
Предимството на използването на плоскости е, че позволява ефективен достъп и манипулация на конкретни цветови компоненти. Например, може да искате да регулирате само яркостта (Y плоскост), без да засягате цвета (U и V плоскости).
Достъп до VideoFrame Planes: API
API-то на VideoFrame предоставя следните методи за достъп до данните на плоскостите:
copyTo(destination, options): Копира съдържанието наVideoFrameв дестинация, която може да бъде другVideoFrame,CanvasImageBitmapилиArrayBufferView. Обектътoptionsконтролира кои плоскости се копират и как. Това е основният механизъм за достъп до плоскостите.
Обектът options в метода copyTo ви позволява да посочите разположението и целта за данните на видео кадъра. Ключовите свойства включват:
format: Желаният пикселен формат на копираните данни. Той може да бъде същият като оригиналнияVideoFrameили различен формат (напр. конвертиране от YUV в RGB).codedWidthиcodedHeight: Ширината и височината на видео кадъра в пиксели.layout: Масив от обекти, описващи разположението на всяка плоскост в паметта. Всеки обект в масива посочва:offset: Отместването, в байтове, от началото на буфера с данни до началото на данните на плоскостта.stride: Броят байтове между началото на всеки ред в плоскостта. Това е от решаващо значение за обработката на подплънки (padding).
Нека разгледаме пример за копиране на YUV420 VideoFrame в суров буфер:
async function copyYUV420ToBuffer(videoFrame, buffer) {
const width = videoFrame.codedWidth;
const height = videoFrame.codedHeight;
// YUV420 има 3 плоскости: Y, U, и V
const yPlaneSize = width * height;
const uvPlaneSize = width * height / 4;
const layout = [
{ offset: 0, stride: width }, // Y плоскост
{ offset: yPlaneSize, stride: width / 2 }, // U плоскост
{ offset: yPlaneSize + uvPlaneSize, stride: width / 2 } // V плоскост
];
await videoFrame.copyTo(buffer, {
format: 'I420',
codedWidth: width,
codedHeight: height,
layout: layout
});
videoFrame.close(); // Важно е да се освободят ресурсите
}
Обяснение:
- Изчисляваме размера на всяка плоскост въз основа на
widthиheight. Y е с пълна резолюция, докато U и V са с намалена резолюция (4:2:0). - Масивът
layoutдефинира разположението в паметта.offsetпосочва къде започва всяка плоскост в буфера, аstrideуказва броя байтове, които трябва да се прескочат, за да се стигне до следващия ред в тази плоскост. - Опцията
formatе зададена на 'I420', което е често срещан YUV420 формат. - Критично важно е след копирането да се извика
videoFrame.close(), за да се освободят ресурсите.
Пикселни формати: Свят от възможности
Разбирането на пикселните формати е от съществено значение за работата с плоскостите на VideoFrame. VideoPixelFormat определя как цветовата информация се кодира във видео кадъра. Ето някои често срещани пикселни формати, с които може да се сблъскате:
- I420 (YUV420p): Планарен YUV формат, при който Y, U и V компонентите се съхраняват в отделни плоскости. U и V са с намалена резолюция с коефициент 2 както по хоризонтала, така и по вертикала. Това е много разпространен и ефективен формат.
- NV12 (YUV420sp): Полу-планарен YUV формат, при който Y се съхранява в една плоскост, а U и V компонентите са вмъкнати последователно във втора плоскост.
- RGBA: Компонентите за червено, зелено, синьо и алфа се съхраняват в една плоскост, обикновено с по 8 бита на компонент (32 бита на пиксел). Редът на компонентите може да варира (напр. BGRA).
- RGB565: Компонентите за червено, зелено и синьо се съхраняват в една плоскост с 5 бита за червено, 6 бита за зелено и 5 бита за синьо (16 бита на пиксел).
- GRAYSCALE: Представлява изображения в сивата гама с една стойност за яркост (лума) за всеки пиксел.
Свойството VideoFrame.format ще ви информира за пикселния формат на даден кадър. Не забравяйте да проверите това свойство, преди да се опитате да получите достъп до плоскостите. Можете да се консултирате със спецификацията на WebCodecs за пълен списък на поддържаните формати.
Практически приложения
Достъпът до плоскостите на VideoFrame отваря широк спектър от възможности за напреднала обработка на видео в браузъра. Ето няколко примера:
1. Видео ефекти в реално време
Можете да прилагате видео ефекти в реално време, като манипулирате пикселните данни в VideoFrame. Например, можете да реализирате филтър за сива гама, като осредните R, G и B компонентите на всеки пиксел в RGBA кадър и след това зададете и трите компонента на тази средна стойност. Можете също така да създадете ефект на сепия или да регулирате яркостта и контраста.
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; // Червено
rgba[i + 1] = gray; // Зелено
rgba[i + 2] = gray; // Синьо
}
// Създаване на нов VideoFrame от модифицираните данни.
const newFrame = new VideoFrame(rgba, {
format: 'RGBA',
codedWidth: width,
codedHeight: height,
timestamp: videoFrame.timestamp,
duration: videoFrame.duration
});
videoFrame.close(); // Освобождаване на оригиналния кадър
return newFrame;
}
2. Приложения за компютърно зрение
Плоскостите на VideoFrame предоставят директен достъп до пикселните данни, необходими за задачи от областта на компютърното зрение. Можете да използвате тези данни за внедряване на алгоритми за откриване на обекти, разпознаване на лица, проследяване на движение и други. Можете да използвате WebAssembly за критичните по отношение на производителността части от вашия код.
Например, можете да конвертирате цветен VideoFrame в сива гама и след това да приложите алгоритъм за откриване на ръбове (напр. оператор на Sobel), за да идентифицирате ръбовете в изображението. Това може да се използва като стъпка за предварителна обработка при разпознаване на обекти.
3. Редактиране и композиране на видео
Можете да използвате плоскостите на VideoFrame за внедряване на функции за редактиране на видео като изрязване, мащабиране, завъртане и композиране. Чрез директна манипулация на пикселните данни можете да създавате персонализирани преходи и ефекти.
Например, можете да изрежете VideoFrame, като копирате само част от пикселните данни в нов VideoFrame. Ще трябва да коригирате съответно отместванията и стъпките в layout.
4. Персонализирани кодеци и транскодиране
Въпреки че WebCodecs предоставя вградена поддръжка за често срещани кодеци като AV1, VP9 и H.264, можете да го използвате и за внедряване на персонализирани кодеци или конвейери за транскодиране. Ще трябва сами да се справите с процеса на кодиране и декодиране, но плоскостите на VideoFrame ви позволяват да достъпвате и манипулирате суровите пикселни данни. Това може да бъде полезно за нишови видео формати или специализирани изисквания за кодиране.
5. Разширени анализи
Чрез достъп до основните пикселни данни можете да извършвате задълбочен анализ на видео съдържанието. Това включва задачи като измерване на средната яркост на сцената, идентифициране на доминиращи цветове или откриване на промени в съдържанието на сцената. Това може да даде възможност за напреднали приложения за видео анализ в областта на сигурността, наблюдението или анализа на съдържание.
Работа с Canvas и WebGL
Въпреки че можете директно да манипулирате пикселните данни в плоскостите на VideoFrame, често се налага да рендирате резултата на екрана. Интерфейсът CanvasImageBitmap предоставя мост между VideoFrame и елемента <canvas>. Можете да създадете CanvasImageBitmap от VideoFrame и след това да го нарисувате върху платното (canvas) с помощта на метода drawImage().
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(); // Освобождаване на ресурсите на bitmap
videoFrame.close(); // Освобождаване на ресурсите на VideoFrame
}
За по-напреднало рендиране можете да използвате WebGL. Можете да качите пикселните данни от плоскостите на VideoFrame в WebGL текстури и след това да използвате шейдъри за прилагане на ефекти и трансформации. Това ви позволява да използвате графичния процесор (GPU) за високопроизводителна обработка на видео.
Съображения за производителност
Работата със сурови пикселни данни може да бъде изчислително интензивна, затова е изключително важно да се има предвид оптимизацията на производителността. Ето няколко съвета:
- Минимизирайте копиранията: Избягвайте ненужното копиране на пикселни данни. Опитайте се да извършвате операциите на място, когато е възможно.
- Използвайте WebAssembly: За критичните по отношение на производителността части от вашия код, обмислете използването на WebAssembly. WebAssembly може да осигури производителност, близка до нативната, за изчислително интензивни задачи.
- Оптимизирайте разположението в паметта: Изберете правилния пикселен формат и разположение в паметта за вашето приложение. Обмислете използването на пакетирани формати (напр. RGBA), ако не се нуждаете от чест достъп до отделни цветови компоненти.
- Използвайте OffscreenCanvas: За обработка във фонов режим използвайте
OffscreenCanvas, за да избегнете блокиране на основната нишка. - Профилирайте кода си: Използвайте инструментите за разработчици в браузъра, за да профилирате кода си и да идентифицирате тесните места в производителността.
Съвместимост с браузъри
WebCodecs и API-то на VideoFrame се поддържат в повечето съвременни браузъри, включително Chrome, Firefox и Safari. Въпреки това, нивото на поддръжка може да варира в зависимост от версията на браузъра и операционната система. Проверете най-новите таблици за съвместимост на браузъри на сайтове като MDN Web Docs, за да се уверите, че функциите, които използвате, се поддържат във вашите целеви браузъри. За съвместимост между различните браузъри се препоръчва откриване на функции (feature detection).
Често срещани капани и отстраняване на проблеми
Ето някои често срещани капани, които трябва да избягвате при работа с плоскостите на VideoFrame:
- Неправилно разположение (layout): Уверете се, че масивът
layoutописва точно разположението на пикселните данни в паметта. Неправилни отмествания или стъпки могат да доведат до повредени изображения. - Несъответстващи пикселни формати: Уверете се, че пикселният формат, който посочвате в метода
copyTo, съответства на действителния формат наVideoFrame. - Изтичане на памет: Винаги затваряйте обектите
VideoFrameиCanvasImageBitmap, след като приключите с тях, за да освободите основните ресурси. Ако не го направите, това може да доведе до изтичане на памет. - Асинхронни операции: Не забравяйте, че
copyToе асинхронна операция. Използвайтеawait, за да се уверите, че операцията по копиране е завършена, преди да получите достъп до пикселните данни. - Ограничения за сигурност: Бъдете наясно с ограниченията за сигурност, които могат да се прилагат при достъп до пикселни данни от видеоклипове с друг произход (cross-origin).
Пример: Конвертиране от YUV в RGB
Нека разгледаме по-сложен пример: конвертиране на YUV420 VideoFrame в RGB VideoFrame. Това включва четене на Y, U и V плоскостите, конвертирането им в RGB стойности и след това създаване на нов RGB VideoFrame.
Това преобразуване може да бъде реализирано чрез следната формула:
R = Y + 1.402 * (Cr - 128)
G = Y - 0.34414 * (Cb - 128) - 0.71414 * (Cr - 128)
B = Y + 1.772 * (Cb - 128)
Ето го и кода:
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 плоскост
{ offset: yPlaneSize, stride: width / 2 }, // U плоскост
{ offset: yPlaneSize + uvPlaneSize, stride: width / 2 } // V плоскост
];
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; // Алфа
}
}
const newFrame = new VideoFrame(rgba, {
format: 'RGBA',
codedWidth: width,
codedHeight: height,
timestamp: videoFrame.timestamp,
duration: videoFrame.duration
});
videoFrame.close(); // Освобождаване на оригиналния кадър
return newFrame;
}
Този пример демонстрира силата и сложността на работата с плоскостите на VideoFrame. Той изисква добро разбиране на пикселните формати, разположението в паметта и преобразуванията на цветовите пространства.
Заключение
API-то за плоскости на VideoFrame в WebCodecs отключва ново ниво на контрол върху обработката на видео в браузъра. Разбирайки как да достъпвате и манипулирате пикселните данни директно, можете да създавате напреднали приложения за видео ефекти в реално време, компютърно зрение, редактиране на видео и други. Въпреки че работата с плоскостите на VideoFrame може да бъде предизвикателство, потенциалните ползи са значителни. С продължаващото развитие на WebCodecs, той несъмнено ще се превърне в основен инструмент за уеб разработчиците, работещи с медия.
Насърчаваме ви да експериментирате с API-то за плоскости на VideoFrame и да изследвате неговите възможности. Като разберете основните принципи и прилагате най-добрите практики, можете да създадете иновативни и производителни видео приложения, които разширяват границите на възможното в браузъра.
Допълнително обучение
- MDN Web Docs за WebCodecs
- Спецификация на WebCodecs
- Хранилища с примерен код за WebCodecs в GitHub.