അഡ്വാൻസ്ഡ് ബ്രൗസർ-അധിഷ്ഠിത വീഡിയോ പ്രോസസ്സിംഗ് സാധ്യമാക്കുക. കസ്റ്റം ഇഫക്റ്റുകൾക്കും വിശകലനത്തിനുമായി WebCodecs API ഉപയോഗിച്ച് റോ VideoFrame പ്ലെയിൻ ഡാറ്റ നേരിട്ട് ആക്സസ് ചെയ്യാനും കൈകാര്യം ചെയ്യാനും പഠിക്കുക.
WebCodecs വീഡിയോഫ്രെയിം പ്ലെയിൻ ആക്സസ്: റോ വീഡിയോ ഡാറ്റ കൈകാര്യം ചെയ്യുന്നതിനെക്കുറിച്ചുള്ള ആഴത്തിലുള്ള പഠനം
വർഷങ്ങളായി, വെബ് ബ്രൗസറിലെ ഉയർന്ന പ്രകടനമുള്ള വീഡിയോ പ്രോസസ്സിംഗ് ഒരു വിദൂര സ്വപ്നം പോലെയായിരുന്നു. ഡെവലപ്പർമാർ പലപ്പോഴും <video> എലമെന്റിന്റെയും 2D ക്യാൻവാസ് API-യുടെയും പരിമിതികളിൽ ഒതുങ്ങിയിരുന്നു. ഇവ ശക്തമായിരുന്നെങ്കിലും, പ്രകടനത്തിൽ തടസ്സങ്ങൾ സൃഷ്ടിക്കുകയും അടിസ്ഥാന റോ വീഡിയോ ഡാറ്റയിലേക്കുള്ള ആക്സസ് പരിമിതപ്പെടുത്തുകയും ചെയ്തു. WebCodecs API-യുടെ വരവ് ഈ രംഗത്ത് അടിസ്ഥാനപരമായ മാറ്റം വരുത്തി, ബ്രൗസറിന്റെ ഇൻ-ബിൽറ്റ് മീഡിയ കോഡെക്കുകളിലേക്ക് ലോ-ലെവൽ ആക്സസ് നൽകുന്നു. അതിന്റെ ഏറ്റവും വിപ്ലവകരമായ സവിശേഷതകളിലൊന്ന് VideoFrame ഒബ്ജക്റ്റിലൂടെ ഓരോ വീഡിയോ ഫ്രെയിമുകളുടെയും റോ ഡാറ്റ നേരിട്ട് ആക്സസ് ചെയ്യാനും കൈകാര്യം ചെയ്യാനുമുള്ള കഴിവാണ്.
ലളിതമായ വീഡിയോ പ്ലേബാക്കിനപ്പുറത്തേക്ക് പോകാൻ ആഗ്രഹിക്കുന്ന ഡെവലപ്പർമാർക്കുള്ള ഒരു സമഗ്രമായ വഴികാട്ടിയാണ് ഈ ലേഖനം. ഞങ്ങൾ VideoFrame പ്ലെയിൻ ആക്സസ്സിന്റെ സങ്കീർണ്ണതകൾ പര്യവേക്ഷണം ചെയ്യും, കളർ സ്പേസുകൾ, മെമ്മറി ലേഔട്ട് തുടങ്ങിയ ആശയങ്ങളെ ലളിതമായി വിശദീകരിക്കും, കൂടാതെ തത്സമയ ഫിൽട്ടറുകൾ മുതൽ സങ്കീർണ്ണമായ കമ്പ്യൂട്ടർ വിഷൻ ടാസ്ക്കുകൾ വരെ, അടുത്ത തലമുറയിലെ ഇൻ-ബ്രൗസർ വീഡിയോ ആപ്ലിക്കേഷനുകൾ നിർമ്മിക്കാൻ നിങ്ങളെ പ്രാപ്തരാക്കുന്നതിനുള്ള പ്രായോഗിക ഉദാഹരണങ്ങൾ നൽകും.
മുൻവ്യവസ്ഥകൾ
ഈ ഗൈഡിൽ നിന്ന് പരമാവധി പ്രയോജനം നേടുന്നതിന്, നിങ്ങൾക്ക് താഴെ പറയുന്ന കാര്യങ്ങളെക്കുറിച്ച് വ്യക്തമായ ധാരണയുണ്ടായിരിക്കണം:
- ആധുനിക ജാവാസ്ക്രിപ്റ്റ്: അസിൻക്രണസ് പ്രോഗ്രാമിംഗ് ഉൾപ്പെടെ (
async/await, Promises). - അടിസ്ഥാന വീഡിയോ ആശയങ്ങൾ: ഫ്രെയിമുകൾ, റെസല്യൂഷൻ, കോഡെക്കുകൾ തുടങ്ങിയ പദങ്ങളുമായി പരിചയം സഹായകമാകും.
- ബ്രൗസർ API-കൾ: ക്യാൻവാസ് 2D അല്ലെങ്കിൽ WebGL പോലുള്ള API-കളിലുള്ള അനുഭവം പ്രയോജനകരമാണ്, പക്ഷേ നിർബന്ധമില്ല.
വീഡിയോ ഫ്രെയിമുകൾ, കളർ സ്പേസുകൾ, പ്ലെയിനുകൾ എന്നിവ മനസ്സിലാക്കുന്നു
API-യിലേക്ക് കടക്കുന്നതിന് മുമ്പ്, ഒരു വീഡിയോ ഫ്രെയിമിന്റെ ഡാറ്റ യഥാർത്ഥത്തിൽ എങ്ങനെയിരിക്കുമെന്നതിനെക്കുറിച്ച് വ്യക്തമായ ഒരു മാനസിക മാതൃക നാം രൂപപ്പെടുത്തണം. ഒരു ഡിജിറ്റൽ വീഡിയോ എന്നത് നിശ്ചല ചിത്രങ്ങളുടെ ഒരു ശ്രേണിയാണ്, അഥവാ ഫ്രെയിമുകളാണ്. ഓരോ ഫ്രെയിമും പിക്സലുകളുടെ ഒരു ഗ്രിഡാണ്, ഓരോ പിക്സലിനും ഒരു നിറമുണ്ട്. ആ നിറം എങ്ങനെ സംഭരിക്കുന്നു എന്നത് കളർ സ്പേസ്, പിക്സൽ ഫോർമാറ്റ് എന്നിവയാൽ നിർവചിക്കപ്പെടുന്നു.
RGBA: വെബിന്റെ സ്വന്തം ഭാഷ
മിക്ക വെബ് ഡെവലപ്പർമാർക്കും RGBA കളർ മോഡൽ പരിചിതമാണ്. ഓരോ പിക്സലിനെയും നാല് ഘടകങ്ങൾ പ്രതിനിധീകരിക്കുന്നു: റെഡ്, ഗ്രീൻ, ബ്ലൂ, ആൽഫ (സുതാര്യത). ഡാറ്റ സാധാരണയായി മെമ്മറിയിൽ ഇന്റർലീവ്ഡ് ആയി സംഭരിക്കുന്നു, അതായത് ഒരു പിക്സലിന്റെ R, G, B, A മൂല്യങ്ങൾ തുടർച്ചയായി സംഭരിക്കുന്നു:
[R1, G1, B1, A1, R2, G2, B2, A2, ...]
ഈ മോഡലിൽ, മുഴുവൻ ചിത്രവും ഒരൊറ്റ, തുടർച്ചയായ മെമ്മറി ബ്ലോക്കിൽ സംഭരിക്കുന്നു. ഇതിനെ ഡാറ്റയുടെ ഒരൊറ്റ "പ്ലെയിൻ" ഉള്ളതായി നമുക്ക് കണക്കാക്കാം.
YUV: വീഡിയോ കംപ്രഷന്റെ ഭാഷ
എന്നിരുന്നാലും, വീഡിയോ കോഡെക്കുകൾ അപൂർവ്വമായി മാത്രമേ RGBA-യുമായി നേരിട്ട് പ്രവർത്തിക്കാറുള്ളൂ. അവ YUV (അല്ലെങ്കിൽ കൂടുതൽ കൃത്യമായി, Y'CbCr) കളർ സ്പേസുകളാണ് ഇഷ്ടപ്പെടുന്നത്. ഈ മോഡൽ ചിത്രത്തിന്റെ വിവരങ്ങളെ ഇങ്ങനെ വേർതിരിക്കുന്നു:
- Y (Luma): തെളിച്ചം അല്ലെങ്കിൽ ഗ്രേസ്കെയിൽ വിവരങ്ങൾ. മനുഷ്യന്റെ കണ്ണ് ല്യൂമയിലെ മാറ്റങ്ങളോട് ഏറ്റവും സംവേദനക്ഷമമാണ്.
- U (Cb), V (Cr): ക്രോമിനൻസ് അല്ലെങ്കിൽ വർണ്ണ-വ്യത്യാസ വിവരങ്ങൾ. മനുഷ്യന്റെ കണ്ണ് തെളിച്ചത്തിന്റെ വിശദാംശങ്ങളെക്കാൾ വർണ്ണ വിശദാംശങ്ങളോട് സംവേദനക്ഷമത കുറവാണ്.
കാര്യക്ഷമമായ കംപ്രഷന് ഈ വേർതിരിവ് പ്രധാനമാണ്. U, V ഘടകങ്ങളുടെ റെസല്യൂഷൻ കുറയ്ക്കുന്നതിലൂടെ - ക്രോമ സബ്സാംപ്ലിംഗ് എന്നറിയപ്പെടുന്ന ഒരു സാങ്കേതികത - ഗുണനിലവാരത്തിൽ കാര്യമായ നഷ്ടം വരാതെ ഫയൽ വലുപ്പം ഗണ്യമായി കുറയ്ക്കാൻ നമുക്ക് കഴിയും. ഇത് പ്ലാനാർ പിക്സൽ ഫോർമാറ്റുകളിലേക്ക് നയിക്കുന്നു, അവിടെ Y, U, V ഘടകങ്ങൾ വെവ്വേറെ മെമ്മറി ബ്ലോക്കുകളിൽ അഥവാ "പ്ലെയിനുകളിൽ" സംഭരിക്കുന്നു.
ഒരു സാധാരണ ഫോർമാറ്റാണ് I420 (YUV 4:2:0-യുടെ ഒരു തരം), ഇവിടെ ഓരോ 2x2 പിക്സൽ ബ്ലോക്കിനും നാല് Y സാമ്പിളുകൾ ഉണ്ടാകും, എന്നാൽ ഒരു U, ഒരു V സാമ്പിൾ മാത്രമേ ഉണ്ടാകൂ. ഇതിനർത്ഥം U, V പ്ലെയിനുകൾക്ക് Y പ്ലെയിനിന്റെ പകുതി വീതിയും പകുതി ഉയരവുമാണുള്ളത്.
ഈ വ്യത്യാസം മനസ്സിലാക്കുന്നത് നിർണ്ണായകമാണ്, കാരണം WebCodecs നിങ്ങൾക്ക് ഈ പ്ലെയിനുകളിലേക്ക് ഡീകോഡർ നൽകുന്ന അതേ രീതിയിൽ നേരിട്ടുള്ള ആക്സസ് നൽകുന്നു.
VideoFrame ഒബ്ജക്റ്റ്: പിക്സൽ ഡാറ്റയിലേക്കുള്ള നിങ്ങളുടെ കവാടം
ഈ പസിലിന്റെ കേന്ദ്രഭാഗം VideoFrame ഒബ്ജക്റ്റാണ്. ഇത് വീഡിയോയുടെ ഒരൊറ്റ ഫ്രെയിമിനെ പ്രതിനിധീകരിക്കുന്നു, കൂടാതെ പിക്സൽ ഡാറ്റ മാത്രമല്ല, പ്രധാനപ്പെട്ട മെറ്റാഡാറ്റയും ഇതിൽ അടങ്ങിയിരിക്കുന്നു.
VideoFrame-ന്റെ പ്രധാന പ്രോപ്പർട്ടികൾ
format: പിക്സൽ ഫോർമാറ്റ് സൂചിപ്പിക്കുന്ന ഒരു സ്ട്രിംഗ് (ഉദാ. 'I420', 'NV12', 'RGBA').codedWidth/codedHeight: കോഡെക്കിന് ആവശ്യമായ ഏതെങ്കിലും പാഡിംഗ് ഉൾപ്പെടെ മെമ്മറിയിൽ സംഭരിച്ചിരിക്കുന്ന ഫ്രെയിമിന്റെ പൂർണ്ണ അളവുകൾ.displayWidth/displayHeight: ഫ്രെയിം പ്രദർശിപ്പിക്കുന്നതിന് ഉപയോഗിക്കേണ്ട അളവുകൾ.timestamp: മൈക്രോസെക്കൻഡിലുള്ള ഫ്രെയിമിന്റെ പ്രസന്റേഷൻ ടൈംസ്റ്റാമ്പ്.duration: മൈക്രോസെക്കൻഡിലുള്ള ഫ്രെയിമിന്റെ ദൈർഘ്യം.
മാന്ത്രിക മെത്തേഡ്: copyTo()
റോ പിക്സൽ ഡാറ്റ ആക്സസ് ചെയ്യുന്നതിനുള്ള പ്രാഥമിക മെത്തേഡ് videoFrame.copyTo(destination, options) ആണ്. ഈ അസിൻക്രണസ് മെത്തേഡ് ഫ്രെയിമിന്റെ പ്ലെയിൻ ഡാറ്റ നിങ്ങൾ നൽകുന്ന ഒരു ബഫറിലേക്ക് പകർത്തുന്നു.
destination: ഡാറ്റ സൂക്ഷിക്കാൻ ആവശ്യമായ വലുപ്പമുള്ള ഒരുArrayBufferഅല്ലെങ്കിൽ ടൈപ്പ്ഡ് അറേ (Uint8Arrayപോലുള്ളവ).options: ഏതൊക്കെ പ്ലെയിനുകൾ പകർത്തണം, അവയുടെ മെമ്മറി ലേഔട്ട് എന്നിവ വ്യക്തമാക്കുന്ന ഒരു ഒബ്ജക്റ്റ്. ഒഴിവാക്കുകയാണെങ്കിൽ, എല്ലാ പ്ലെയിനുകളും ഒരൊറ്റ തുടർച്ചയായ ബഫറിലേക്ക് പകർത്തുന്നു.
ഈ മെത്തേഡ് ഒരു പ്രോമിസ് നൽകുന്നു, അത് ഫ്രെയിമിലെ ഓരോ പ്ലെയിനിനും ഒന്നുവീതം PlaneLayout ഒബ്ജക്റ്റുകളുടെ ഒരു അറേയുമായി റിസോൾവ് ചെയ്യുന്നു. ഓരോ PlaneLayout ഒബ്ജക്റ്റിലും രണ്ട് നിർണ്ണായക വിവരങ്ങൾ അടങ്ങിയിരിക്കുന്നു:
offset: ഡെസ്റ്റിനേഷൻ ബഫറിനുള്ളിൽ ഈ പ്ലെയിനിന്റെ ഡാറ്റ ആരംഭിക്കുന്ന ബൈറ്റ് ഓഫ്സെറ്റ്.stride: ഒരു നിര പിക്സലുകളുടെ തുടക്കവും അടുത്ത നിരയുടെ തുടക്കവും തമ്മിലുള്ള ബൈറ്റുകളുടെ എണ്ണം.
ഒരു നിർണ്ണായക ആശയം: സ്ട്രൈഡ് vs. വിഡ്ത്ത്
ലോ-ലെവൽ ഗ്രാഫിക്സ് പ്രോഗ്രാമിംഗിൽ പുതിയവരായ ഡെവലപ്പർമാർക്ക് ആശയക്കുഴപ്പമുണ്ടാക്കുന്ന ഏറ്റവും സാധാരണമായ ഒന്നാണിത്. ഓരോ നിര പിക്സൽ ഡാറ്റയും ഒന്നിനുപുറകെ ഒന്നായി അടുക്കി വെച്ചിരിക്കുന്നു എന്ന് നിങ്ങൾക്ക് അനുമാനിക്കാൻ കഴിയില്ല.
- വിഡ്ത്ത് (Width) ഒരു ചിത്രത്തിലെ ഒരു നിരയിലുള്ള പിക്സലുകളുടെ എണ്ണമാണ്.
- സ്ട്രൈഡ് (Stride) (പിച്ച് അല്ലെങ്കിൽ ലൈൻ സ്റ്റെപ്പ് എന്നും അറിയപ്പെടുന്നു) മെമ്മറിയിൽ ഒരു നിരയുടെ തുടക്കം മുതൽ അടുത്ത നിരയുടെ തുടക്കം വരെയുള്ള ബൈറ്റുകളുടെ എണ്ണമാണ്.
പലപ്പോഴും, stride എന്നത് width * bytes_per_pixel എന്നതിനേക്കാൾ കൂടുതലായിരിക്കും. കാരണം, സിപിയു അല്ലെങ്കിൽ ജിപിയു വേഗത്തിൽ പ്രോസസ്സ് ചെയ്യുന്നതിനായി മെമ്മറി പലപ്പോഴും ഹാർഡ്വെയർ അതിർത്തികളുമായി (ഉദാ. 32 അല്ലെങ്കിൽ 64-ബൈറ്റ് അതിർത്തികൾ) വിന്യസിക്കാൻ പാഡ് ചെയ്യപ്പെടുന്നു. ഒരു പ്രത്യേക നിരയിലെ ഒരു പിക്സലിന്റെ മെമ്മറി വിലാസം കണക്കാക്കാൻ നിങ്ങൾ എപ്പോഴും സ്ട്രൈഡ് ഉപയോഗിക്കണം.
സ്ട്രൈഡ് അവഗണിക്കുന്നത് ചിത്രങ്ങൾ വളഞ്ഞോ വികലമായോ കാണപ്പെടുന്നതിനും തെറ്റായ ഡാറ്റാ ആക്സസ്സിനും കാരണമാകും.
പ്രായോഗിക ഉദാഹരണം 1: ഗ്രേസ്കെയിൽ പ്ലെയിൻ ആക്സസ് ചെയ്യുകയും പ്രദർശിപ്പിക്കുകയും ചെയ്യുക
ലളിതവും എന്നാൽ ശക്തവുമായ ഒരു ഉദാഹരണത്തോടെ നമുക്ക് ആരംഭിക്കാം. വെബിലെ മിക്ക വീഡിയോകളും I420 പോലുള്ള YUV ഫോർമാറ്റിലാണ് എൻകോഡ് ചെയ്തിരിക്കുന്നത്. 'Y' പ്ലെയിൻ ഫലത്തിൽ ചിത്രത്തിന്റെ ഒരു സമ്പൂർണ്ണ ഗ്രേസ്കെയിൽ പ്രാതിനിധ്യമാണ്. നമുക്ക് ഈ പ്ലെയിൻ മാത്രം വേർതിരിച്ചെടുത്ത് ഒരു ക്യാൻവാസിലേക്ക് റെൻഡർ ചെയ്യാം.
async function displayGrayscale(videoFrame) {
// വീഡിയോഫ്രെയിം 'I420' അല്ലെങ്കിൽ 'NV12' പോലുള്ള ഒരു YUV ഫോർമാറ്റിലാണെന്ന് ഞങ്ങൾ അനുമാനിക്കുന്നു.
if (!videoFrame.format.startsWith('I4')) {
console.error('ഈ ഉദാഹരണത്തിന് ഒരു YUV 4:2:0 പ്ലാനാർ ഫോർമാറ്റ് ആവശ്യമാണ്.');
videoFrame.close();
return;
}
const yPlaneInfo = videoFrame.layout[0]; // Y പ്ലെയിൻ എപ്പോഴും ആദ്യത്തേതാണ്.
// Y പ്ലെയിൻ ഡാറ്റ മാത്രം സൂക്ഷിക്കാൻ ഒരു ബഫർ ഉണ്ടാക്കുക.
const yPlaneData = new Uint8Array(yPlaneInfo.stride * videoFrame.codedHeight);
// Y പ്ലെയിൻ നമ്മുടെ ബഫറിലേക്ക് പകർത്തുക.
await videoFrame.copyTo(yPlaneData, {
rect: { x: 0, y: 0, width: videoFrame.codedWidth, height: videoFrame.codedHeight },
layout: [yPlaneInfo]
});
// ഇപ്പോൾ, yPlaneData-ൽ റോ ഗ്രേസ്കെയിൽ പിക്സലുകൾ അടങ്ങിയിരിക്കുന്നു.
// നമുക്ക് ഇത് റെൻഡർ ചെയ്യണം. ക്യാൻവാസിനായി ഒരു RGBA ബഫർ ഉണ്ടാക്കാം.
const canvas = document.getElementById('my-canvas');
canvas.width = videoFrame.displayWidth;
canvas.height = videoFrame.displayHeight;
const ctx = canvas.getContext('2d');
const imageData = ctx.createImageData(canvas.width, canvas.height);
// ക്യാൻവാസ് പിക്സലുകളിലൂടെ കടന്നുപോയി Y പ്ലെയിൻ ഡാറ്റയിൽ നിന്ന് അവ പൂരിപ്പിക്കുക.
for (let y = 0; y < videoFrame.displayHeight; y++) {
for (let x = 0; x < videoFrame.displayWidth; x++) {
// പ്രധാനം: ശരിയായ സോഴ്സ് ഇൻഡെക്സ് കണ്ടെത്താൻ സ്ട്രൈഡ് ഉപയോഗിക്കുക!
const yIndex = y * yPlaneInfo.stride + x;
const luma = yPlaneData[yIndex];
// RGBA ImageData ബഫറിലെ ഡെസ്റ്റിനേഷൻ ഇൻഡെക്സ് കണക്കാക്കുക.
const rgbaIndex = (y * canvas.width + x) * 4;
imageData.data[rgbaIndex] = luma; // റെഡ്
imageData.data[rgbaIndex + 1] = luma; // ഗ്രീൻ
imageData.data[rgbaIndex + 2] = luma; // ബ്ലൂ
imageData.data[rgbaIndex + 3] = 255; // ആൽഫ
}
}
ctx.putImageData(imageData, 0, 0);
// നിർണ്ണായകം: മെമ്മറി റിലീസ് ചെയ്യുന്നതിന് എപ്പോഴും VideoFrame ക്ലോസ് ചെയ്യുക.
videoFrame.close();
}
ഈ ഉദാഹരണം നിരവധി പ്രധാന ഘട്ടങ്ങൾ എടുത്തുകാണിക്കുന്നു: ശരിയായ പ്ലെയിൻ ലേഔട്ട് തിരിച്ചറിയുക, ഒരു ഡെസ്റ്റിനേഷൻ ബഫർ അനുവദിക്കുക, ഡാറ്റ എക്സ്ട്രാക്റ്റുചെയ്യാൻ copyTo ഉപയോഗിക്കുക, ഒരു പുതിയ ചിത്രം നിർമ്മിക്കുന്നതിന് stride ഉപയോഗിച്ച് ഡാറ്റ ശരിയായി ആവർത്തിക്കുക.
പ്രായോഗിക ഉദാഹരണം 2: ഇൻ-പ്ലേസ് മാനിപുലേഷൻ (സെപ്പിയ ഫിൽട്ടർ)
ഇനി നമുക്ക് ഒരു നേരിട്ടുള്ള ഡാറ്റാ മാനിപുലേഷൻ നടത്താം. ഒരു സെപ്പിയ ഫിൽട്ടർ നടപ്പിലാക്കാൻ എളുപ്പമുള്ള ഒരു ക്ലാസിക് ഇഫക്റ്റാണ്. ഈ ഉദാഹരണത്തിന്, ഒരു RGBA ഫ്രെയിമുമായി പ്രവർത്തിക്കുന്നത് എളുപ്പമാണ്, അത് നിങ്ങൾക്ക് ഒരു ക്യാൻവാസിൽ നിന്നോ WebGL കോൺടെക്സ്റ്റിൽ നിന്നോ ലഭിച്ചേക്കാം.
async function applySepiaFilter(videoFrame) {
// ഈ ഉദാഹരണം ഇൻപുട്ട് ഫ്രെയിം 'RGBA' അല്ലെങ്കിൽ 'BGRA' ആണെന്ന് അനുമാനിക്കുന്നു.
if (videoFrame.format !== 'RGBA' && videoFrame.format !== 'BGRA') {
console.error('സെപ്പിയ ഫിൽട്ടർ ഉദാഹരണത്തിന് ഒരു RGBA ഫ്രെയിം ആവശ്യമാണ്.');
videoFrame.close();
return null;
}
// പിക്സൽ ഡാറ്റ സൂക്ഷിക്കാൻ ഒരു ബഫർ അനുവദിക്കുക.
const frameDataSize = videoFrame.allocationSize();
const frameData = new Uint8Array(frameDataSize);
await videoFrame.copyTo(frameData);
const layout = videoFrame.layout[0]; // RGBA ഒരു പ്ലെയിൻ ആണ്
// ഇപ്പോൾ, ബഫറിലെ ഡാറ്റ കൈകാര്യം ചെയ്യുക.
for (let y = 0; y < videoFrame.codedHeight; y++) {
for (let x = 0; x < videoFrame.codedWidth; x++) {
const pixelIndex = y * layout.stride + x * 4; // ഒരു പിക്സലിന് 4 ബൈറ്റുകൾ (R,G,B,A)
const r = frameData[pixelIndex];
const g = frameData[pixelIndex + 1];
const b = frameData[pixelIndex + 2];
const tr = 0.393 * r + 0.769 * g + 0.189 * b;
const tg = 0.349 * r + 0.686 * g + 0.168 * b;
const tb = 0.272 * r + 0.534 * g + 0.131 * b;
frameData[pixelIndex] = Math.min(255, tr);
frameData[pixelIndex + 1] = Math.min(255, tg);
frameData[pixelIndex + 2] = Math.min(255, tb);
// ആൽഫ (frameData[pixelIndex + 3]) മാറ്റമില്ലാതെ തുടരുന്നു.
}
}
// പരിഷ്കരിച്ച ഡാറ്റ ഉപയോഗിച്ച് ഒരു *പുതിയ* VideoFrame ഉണ്ടാക്കുക.
const newFrame = new VideoFrame(frameData, {
format: videoFrame.format,
codedWidth: videoFrame.codedWidth,
codedHeight: videoFrame.codedHeight,
timestamp: videoFrame.timestamp,
duration: videoFrame.duration
});
// യഥാർത്ഥ ഫ്രെയിം ക്ലോസ് ചെയ്യാൻ മറക്കരുത്!
videoFrame.close();
return newFrame;
}
ഇതൊരു സമ്പൂർണ്ണ റീഡ്-മോഡിഫൈ-റൈറ്റ് സൈക്കിൾ പ്രകടമാക്കുന്നു: ഡാറ്റ പുറത്തേക്ക് പകർത്തുക, സ്ട്രൈഡ് ഉപയോഗിച്ച് അതിലൂടെ ലൂപ്പ് ചെയ്യുക, ഓരോ പിക്സലിലും ഒരു ഗണിതശാസ്ത്ര പരിവർത്തനം പ്രയോഗിക്കുക, തത്ഫലമായുണ്ടാകുന്ന ഡാറ്റ ഉപയോഗിച്ച് ഒരു പുതിയ VideoFrame നിർമ്മിക്കുക. ഈ പുതിയ ഫ്രെയിം ഒരു ക്യാൻവാസിലേക്ക് റെൻഡർ ചെയ്യാനോ, ഒരു VideoEncoder-ലേക്ക് അയയ്ക്കാനോ, അല്ലെങ്കിൽ മറ്റൊരു പ്രോസസ്സിംഗ് ഘട്ടത്തിലേക്ക് കൈമാറാനോ കഴിയും.
പ്രകടനം പ്രധാനമാണ്: ജാവാസ്ക്രിപ്റ്റ് vs. വെബ്അസെംബ്ലി (WASM)
ഓരോ ഫ്രെയിമിനും ദശലക്ഷക്കണക്കിന് പിക്സലുകളിലൂടെ (ഒരു 1080p ഫ്രെയിമിന് 2 ദശലക്ഷത്തിലധികം പിക്സലുകൾ, അല്ലെങ്കിൽ RGBA-യിൽ 8 ദശലക്ഷം ഡാറ്റാ പോയിന്റുകൾ ഉണ്ട്) ജാവാസ്ക്രിപ്റ്റിൽ ആവർത്തിക്കുന്നത് വേഗത കുറഞ്ഞേക്കാം. ആധുനിക JS എഞ്ചിനുകൾ അവിശ്വസനീയമാംവിധം വേഗതയുള്ളതാണെങ്കിലും, ഉയർന്ന റെസല്യൂഷൻ വീഡിയോയുടെ (HD, 4K) തത്സമയ പ്രോസസ്സിംഗിനായി, ഈ സമീപനം പ്രധാന ത്രെഡിനെ എളുപ്പത്തിൽ തളർത്തുകയും, ഇത് ഒരു തടസ്സപ്പെട്ട ഉപയോക്തൃ അനുഭവത്തിലേക്ക് നയിക്കുകയും ചെയ്യും.
ഇവിടെയാണ് വെബ്അസെംബ്ലി (WASM) ഒരു അത്യാവശ്യ ഉപകരണമായി മാറുന്നത്. C++, Rust, അല്ലെങ്കിൽ Go പോലുള്ള ഭാഷകളിൽ എഴുതിയ കോഡ് ബ്രൗസറിനുള്ളിൽ നേറ്റീവ് വേഗതയിൽ പ്രവർത്തിപ്പിക്കാൻ WASM നിങ്ങളെ അനുവദിക്കുന്നു. വീഡിയോ പ്രോസസ്സിംഗിനായുള്ള വർക്ക്ഫ്ലോ ഇങ്ങനെയാകുന്നു:
- ജാവാസ്ക്രിപ്റ്റിൽ: റോ പിക്സൽ ഡാറ്റ ഒരു
ArrayBuffer-ലേക്ക് ലഭിക്കുന്നതിന്videoFrame.copyTo()ഉപയോഗിക്കുക. - WASM-ലേക്ക് കൈമാറുക: ഈ ബഫറിന്റെ ഒരു റഫറൻസ് നിങ്ങളുടെ കംപൈൽ ചെയ്ത WASM മൊഡ്യൂളിലേക്ക് കൈമാറുക. ഇത് ഡാറ്റ പകർത്താത്തതിനാൽ വളരെ വേഗതയേറിയ ഒരു പ്രവർത്തനമാണ്.
- WASM-ൽ (C++/Rust): നിങ്ങളുടെ ഉയർന്ന തോതിൽ ഒപ്റ്റിമൈസ് ചെയ്ത ഇമേജ് പ്രോസസ്സിംഗ് അൽഗോരിതങ്ങൾ മെമ്മറി ബഫറിൽ നേരിട്ട് നടപ്പിലാക്കുക. ഇത് ഒരു ജാവാസ്ക്രിപ്റ്റ് ലൂപ്പിനേക്കാൾ പലമടങ്ങ് വേഗതയുള്ളതാണ്.
- ജാവാസ്ക്രിപ്റ്റിലേക്ക് മടങ്ങുക: WASM പൂർത്തിയാകുമ്പോൾ, നിയന്ത്രണം ജാവാസ്ക്രിപ്റ്റിലേക്ക് മടങ്ങുന്നു. തുടർന്ന് നിങ്ങൾക്ക് പരിഷ്കരിച്ച ബഫർ ഉപയോഗിച്ച് ഒരു പുതിയ
VideoFrameഉണ്ടാക്കാം.
ഏതൊരു ഗൗരവമേറിയ, തത്സമയ വീഡിയോ മാനിപുലേഷൻ ആപ്ലിക്കേഷനും - വെർച്വൽ പശ്ചാത്തലങ്ങൾ, ഒബ്ജക്റ്റ് ഡിറ്റക്ഷൻ, അല്ലെങ്കിൽ സങ്കീർണ്ണമായ ഫിൽട്ടറുകൾ പോലുള്ളവയ്ക്ക് - വെബ്അസെംബ്ലി പ്രയോജനപ്പെടുത്തുന്നത് ഒരു ഓപ്ഷൻ മാത്രമല്ല; അതൊരു ആവശ്യകതയാണ്.
വ്യത്യസ്ത പിക്സൽ ഫോർമാറ്റുകൾ കൈകാര്യം ചെയ്യൽ (ഉദാ. I420, NV12)
RGBA ലളിതമാണെങ്കിലും, നിങ്ങൾക്ക് മിക്കപ്പോഴും ഒരു VideoDecoder-ൽ നിന്ന് പ്ലാനാർ YUV ഫോർമാറ്റുകളിൽ ഫ്രെയിമുകൾ ലഭിക്കും. I420 പോലുള്ള ഒരു പൂർണ്ണ പ്ലാനാർ ഫോർമാറ്റ് എങ്ങനെ കൈകാര്യം ചെയ്യാമെന്ന് നോക്കാം.
I420 ഫോർമാറ്റിലുള്ള ഒരു VideoFrame-ന്റെ layout അറേയിൽ മൂന്ന് ലേഔട്ട് ഡിസ്ക്രിപ്റ്ററുകൾ ഉണ്ടാകും:
layout[0]: Y പ്ലെയിൻ (ല്യൂമ). അളവുകൾcodedWidthxcodedHeight.layout[1]: U പ്ലെയിൻ (ക്രോമ). അളവുകൾcodedWidth/2xcodedHeight/2.layout[2]: V പ്ലെയിൻ (ക്രോമ). അളവുകൾcodedWidth/2xcodedHeight/2.
മൂന്ന് പ്ലെയിനുകളും ഒരൊറ്റ ബഫറിലേക്ക് എങ്ങനെ പകർത്താമെന്ന് താഴെ നൽകുന്നു:
async function extractI420Planes(videoFrame) {
const totalSize = videoFrame.allocationSize({ format: 'I420' });
const allPlanesData = new Uint8Array(totalSize);
const layouts = await videoFrame.copyTo(allPlanesData);
// layouts 3 PlaneLayout ഒബ്ജക്റ്റുകളുടെ ഒരു അറേയാണ്
console.log('Y Plane Layout:', layouts[0]); // { offset: 0, stride: ... }
console.log('U Plane Layout:', layouts[1]); // { offset: ..., stride: ... }
console.log('V Plane Layout:', layouts[2]); // { offset: ..., stride: ... }
// ഇപ്പോൾ നിങ്ങൾക്ക് `allPlanesData` ബഫറിനുള്ളിലെ ഓരോ പ്ലെയിനും
// അതിന്റെ പ്രത്യേക ഓഫ്സെറ്റും സ്ട്രൈഡും ഉപയോഗിച്ച് ആക്സസ് ചെയ്യാൻ കഴിയും.
const yPlaneView = new Uint8Array(
allPlanesData.buffer,
layouts[0].offset,
layouts[0].stride * videoFrame.codedHeight
);
// ക്രോമയുടെ അളവുകൾ പകുതിയാണെന്ന് ശ്രദ്ധിക്കുക!
const uPlaneView = new Uint8Array(
allPlanesData.buffer,
layouts[1].offset,
layouts[1].stride * (videoFrame.codedHeight / 2)
);
const vPlaneView = new Uint8Array(
allPlanesData.buffer,
layouts[2].offset,
layouts[2].stride * (videoFrame.codedHeight / 2)
);
console.log('ആക്സസ് ചെയ്ത Y പ്ലെയിൻ വലുപ്പം:', yPlaneView.byteLength);
console.log('ആക്സസ് ചെയ്ത U പ്ലെയിൻ വലുപ്പം:', uPlaneView.byteLength);
videoFrame.close();
}
മറ്റൊരു സാധാരണ ഫോർമാറ്റാണ് NV12, ഇത് സെമി-പ്ലാനാർ ആണ്. ഇതിന് രണ്ട് പ്ലെയിനുകൾ ഉണ്ട്: ഒന്ന് Y-ക്ക്, രണ്ടാമത്തേതിൽ U, V മൂല്യങ്ങൾ ഇന്റർലീവ് ചെയ്തിരിക്കുന്നു (ഉദാ. [U1, V1, U2, V2, ...]). WebCodecs API ഇത് സുതാര്യമായി കൈകാര്യം ചെയ്യുന്നു; NV12 ഫോർമാറ്റിലുള്ള ഒരു VideoFrame-ന്റെ layout അറേയിൽ രണ്ട് ലേഔട്ടുകൾ ഉണ്ടാകും.
വെല്ലുവിളികളും മികച്ച രീതികളും
ഈ ലോ-ലെവലിൽ പ്രവർത്തിക്കുന്നത് ശക്തമാണ്, പക്ഷേ അതിന് ഉത്തരവാദിത്തങ്ങളും ഉണ്ട്.
മെമ്മറി മാനേജ്മെന്റ് പരമപ്രധാനമാണ്
ഒരു VideoFrame ഗണ്യമായ അളവിൽ മെമ്മറി സൂക്ഷിക്കുന്നു, ഇത് പലപ്പോഴും ജാവാസ്ക്രിപ്റ്റ് ഗാർബേജ് കളക്ടറുടെ ഹീപ്പിന് പുറത്താണ് കൈകാര്യം ചെയ്യുന്നത്. നിങ്ങൾ ഈ മെമ്മറി വ്യക്തമായി റിലീസ് ചെയ്യുന്നില്ലെങ്കിൽ, ബ്രൗസർ ടാബ് ക്രാഷാകാൻ കാരണമായേക്കാവുന്ന ഒരു മെമ്മറി ലീക്കിന് കാരണമാകും.
ഒരു ഫ്രെയിമുമായി പൂർത്തിയാകുമ്പോൾ എപ്പോഴും, എപ്പോഴും videoFrame.close() എന്ന് വിളിക്കുക.
അസിൻക്രണസ് സ്വഭാവം
എല്ലാ ഡാറ്റാ ആക്സസ്സും അസിൻക്രണസ് ആണ്. റേസ് കണ്ടീഷനുകൾ ഒഴിവാക്കുന്നതിനും സുഗമമായ ഒരു പ്രോസസ്സിംഗ് പൈപ്പ്ലൈൻ ഉറപ്പാക്കുന്നതിനും നിങ്ങളുടെ ആപ്ലിക്കേഷന്റെ ആർക്കിടെക്ചർ Promises, async/await എന്നിവയുടെ ഒഴുക്ക് ശരിയായി കൈകാര്യം ചെയ്യണം.
ബ്രൗസർ അനുയോജ്യത
WebCodecs ഒരു ആധുനിക API ആണ്. എല്ലാ പ്രധാന ബ്രൗസറുകളിലും പിന്തുണയ്ക്കുന്നുണ്ടെങ്കിലും, അതിന്റെ ലഭ്യത എപ്പോഴും പരിശോധിക്കുകയും ഏതെങ്കിലും വെണ്ടർ-നിർദ്ദിഷ്ട നടപ്പാക്കൽ വിശദാംശങ്ങളെക്കുറിച്ചോ പരിമിതികളെക്കുറിച്ചോ അറിഞ്ഞിരിക്കുകയും ചെയ്യുക. API ഉപയോഗിക്കാൻ ശ്രമിക്കുന്നതിന് മുമ്പ് ഫീച്ചർ ഡിറ്റക്ഷൻ ഉപയോഗിക്കുക.
ഉപസംഹാരം: വെബ് വീഡിയോയ്ക്ക് ഒരു പുതിയ അതിർത്തി
WebCodecs API വഴി ഒരു VideoFrame-ന്റെ റോ പ്ലെയിൻ ഡാറ്റ നേരിട്ട് ആക്സസ് ചെയ്യാനും കൈകാര്യം ചെയ്യാനുമുള്ള കഴിവ് വെബ് അധിഷ്ഠിത മീഡിയ ആപ്ലിക്കേഷനുകൾക്ക് ഒരു മാതൃകാപരമായ മാറ്റമാണ്. ഇത് <video> എലമെന്റിന്റെ ബ്ലാക്ക് ബോക്സ് നീക്കംചെയ്യുകയും ഡെവലപ്പർമാർക്ക് മുമ്പ് നേറ്റീവ് ആപ്ലിക്കേഷനുകൾക്ക് മാത്രം ലഭ്യമായിരുന്ന സൂക്ഷ്മമായ നിയന്ത്രണം നൽകുകയും ചെയ്യുന്നു.
വീഡിയോ മെമ്മറി ലേഔട്ടിന്റെ അടിസ്ഥാനതത്വങ്ങൾ - പ്ലെയിനുകൾ, സ്ട്രൈഡ്, കളർ ഫോർമാറ്റുകൾ - മനസ്സിലാക്കുന്നതിലൂടെയും, പ്രകടനം-നിർണ്ണായകമായ പ്രവർത്തനങ്ങൾക്കായി വെബ്അസെംബ്ലിയുടെ ശക്തി പ്രയോജനപ്പെടുത്തുന്നതിലൂടെയും, നിങ്ങൾക്ക് ഇപ്പോൾ അവിശ്വസനീയമാംവിധം സങ്കീർണ്ണമായ വീഡിയോ പ്രോസസ്സിംഗ് ടൂളുകൾ നേരിട്ട് ബ്രൗസറിൽ നിർമ്മിക്കാൻ കഴിയും. തത്സമയ കളർ ഗ്രേഡിംഗ്, കസ്റ്റം വിഷ്വൽ ഇഫക്റ്റുകൾ മുതൽ ക്ലയിന്റ്-സൈഡ് മെഷീൻ ലേണിംഗ്, വീഡിയോ അനാലിസിസ് വരെ, സാധ്യതകൾ വളരെ വലുതാണ്. വെബിലെ ഉയർന്ന പ്രകടനമുള്ള, ലോ-ലെവൽ വീഡിയോയുടെ യുഗം യഥാർത്ഥത്തിൽ ആരംഭിച്ചിരിക്കുന്നു.