WebAssembly আমাদের নতুন বৈশিষ্ট্য সহ ব্রাউজার প্রসারিত করতে দেয়। এই নিবন্ধটি দেখায় কিভাবে AV1 ভিডিও ডিকোডার পোর্ট করতে হয় এবং যেকোনো আধুনিক ব্রাউজারে AV1 ভিডিও চালাতে হয়।
WebAssembly সম্পর্কে সেরা জিনিসগুলির মধ্যে একটি হল নতুন ক্ষমতার সাথে সক্ষমতা পরীক্ষা করা এবং ব্রাউজারটি সেই বৈশিষ্ট্যগুলি স্থানীয়ভাবে পাঠানোর আগে নতুন ধারণাগুলি বাস্তবায়ন করা (যদি থাকে)। আপনি এইভাবে WebAssembly ব্যবহার করার কথা ভাবতে পারেন একটি উচ্চ-পারফরম্যান্স পলিফিল মেকানিজম হিসেবে, যেখানে আপনি JavaScript-এর পরিবর্তে C/C++ বা Rust-এ আপনার বৈশিষ্ট্য লেখেন।
পোর্টিংয়ের জন্য উপলব্ধ বিদ্যমান কোডের আধিক্যের সাথে, ব্রাউজারে এমন কিছু করা সম্ভব যা WebAssembly না আসা পর্যন্ত কার্যকর ছিল না।
এই নিবন্ধটি কীভাবে বিদ্যমান AV1 ভিডিও কোডেক সোর্স কোড নিতে হয়, এটির জন্য একটি র্যাপার তৈরি করতে হয় এবং এটি আপনার ব্রাউজারের ভিতরে ব্যবহার করে দেখুন এবং র্যাপার ডিবাগ করার জন্য একটি পরীক্ষামূলক জোতা তৈরিতে সহায়তা করার জন্য টিপসগুলির একটি উদাহরণ দিয়ে হেঁটে যাবে৷ উদাহরণের জন্য সম্পূর্ণ সোর্স কোড এখানে রেফারেন্সের জন্য github.com/GoogleChromeLabs/wasm-av1 এ উপলব্ধ।
এই দুটি 24fps টেস্ট ভিডিও ফাইলগুলির একটি ডাউনলোড করুন এবং আমাদের তৈরি ডেমোতে চেষ্টা করুন৷
একটি আকর্ষণীয় কোড-বেস নির্বাচন করা
এখন বেশ কয়েক বছর ধরে, আমরা দেখেছি যে ওয়েবে ট্র্যাফিকের একটি বড় শতাংশ ভিডিও ডেটা নিয়ে গঠিত, সিসকো এটিকে প্রকৃতপক্ষে 80% হিসাবে অনুমান করে ! অবশ্যই, ব্রাউজার বিক্রেতা এবং ভিডিও সাইটগুলি এই সমস্ত ভিডিও সামগ্রীর দ্বারা ব্যবহৃত ডেটা হ্রাস করার ইচ্ছা সম্পর্কে ব্যাপকভাবে সচেতন। এর মূল চাবিকাঠি, অবশ্যই, আরও ভাল কম্প্রেশন, এবং আপনি যেমন আশা করছেন ইন্টারনেট জুড়ে ভিডিও শিপিংয়ের ডেটা বোঝা হ্রাস করার লক্ষ্যে পরবর্তী প্রজন্মের ভিডিও কম্প্রেশনে প্রচুর গবেষণা রয়েছে।
যেমনটি ঘটে, অ্যালায়েন্স ফর ওপেন মিডিয়া AV1 নামক একটি পরবর্তী প্রজন্মের ভিডিও কম্প্রেশন স্কিম নিয়ে কাজ করছে যা ভিডিও ডেটার আকারকে উল্লেখযোগ্যভাবে সঙ্কুচিত করার প্রতিশ্রুতি দেয়। ভবিষ্যতে, আমরা আশা করব ব্রাউজারগুলি AV1-এর জন্য নেটিভ সাপোর্ট পাঠাবে, কিন্তু সৌভাগ্যবশত কম্প্রেসার এবং ডিকম্প্রেসারের জন্য সোর্স কোড হল ওপেন সোর্স , যা এটিকে WebAssembly-এ কম্পাইল করার চেষ্টা করার জন্য একটি আদর্শ প্রার্থী করে তোলে যাতে আমরা ব্রাউজারে এটি নিয়ে পরীক্ষা করতে পারি।

ব্রাউজারে ব্যবহারের জন্য মানিয়ে নেওয়া
এই কোডটি ব্রাউজারে পাওয়ার জন্য আমাদের প্রথমে যা করতে হবে তা হল এপিআই কেমন তা বোঝার জন্য বিদ্যমান কোডটি জানা। এই কোডটি প্রথম দেখার সময়, দুটি জিনিস আলাদা হয়:
- উৎস গাছটি
cmake
নামক একটি টুল ব্যবহার করে নির্মিত হয়; এবং - এমন অনেকগুলি উদাহরণ রয়েছে যেগুলি সকলেই কিছু ধরণের ফাইল-ভিত্তিক ইন্টারফেস ধরে নেয়।
ডিফল্টরূপে তৈরি করা সমস্ত উদাহরণ কমান্ড লাইনে চালানো যেতে পারে এবং সম্প্রদায়ে উপলব্ধ অন্যান্য কোড বেসগুলিতে এটি সত্য হতে পারে। সুতরাং, আমরা ব্রাউজারে চালানোর জন্য যে ইন্টারফেসটি তৈরি করতে যাচ্ছি তা অন্য অনেক কমান্ড লাইন টুলের জন্য উপযোগী হতে পারে।
সোর্স কোড তৈরি করতে cmake
ব্যবহার করে
সৌভাগ্যবশত, AV1 লেখকরা Emscripten নিয়ে পরীক্ষা-নিরীক্ষা করছেন, SDK যা আমরা আমাদের WebAssembly সংস্করণ তৈরি করতে ব্যবহার করতে যাচ্ছি। AV1 সংগ্রহস্থলের রুটে, CMakeLists.txt
ফাইলটিতে এই বিল্ড নিয়মগুলি রয়েছে:
if(EMSCRIPTEN)
add_preproc_definition(_POSIX_SOURCE)
append_link_flag_to_target("inspect" "-s TOTAL_MEMORY=402653184")
append_link_flag_to_target("inspect" "-s MODULARIZE=1")
append_link_flag_to_target("inspect"
"-s EXPORT_NAME=\"\'DecoderModule\'\"")
append_link_flag_to_target("inspect" "--memory-init-file 0")
if("${CMAKE_BUILD_TYPE}" STREQUAL "")
# Default to -O3 when no build type is specified.
append_compiler_flag("-O3")
endif()
em_link_post_js(inspect "${AOM_ROOT}/tools/inspect-post.js")
endif()
Emscripten টুলচেইন দুটি ফরম্যাটে আউটপুট তৈরি করতে পারে, একটিকে বলা হয় asm.js
এবং অন্যটিকে WebAssembly। আমরা WebAssembly টার্গেট করব কারণ এটি ছোট আউটপুট তৈরি করে এবং দ্রুত চালাতে পারে। এই বিদ্যমান বিল্ড নিয়মগুলি একটি ইন্সপেক্টর অ্যাপ্লিকেশনে ব্যবহারের জন্য লাইব্রেরির একটি asm.js
সংস্করণ কম্পাইল করার জন্য বোঝানো হয়েছে যা একটি ভিডিও ফাইলের বিষয়বস্তু দেখার জন্য ব্যবহার করা হয়। আমাদের ব্যবহারের জন্য, আমাদের WebAssembly আউটপুট দরকার তাই আমরা উপরের নিয়মে ক্লোজিং endif()
স্টেটমেন্টের ঠিক আগে এই লাইনগুলি যোগ করি।
# Force generation of Wasm instead of asm.js
append_link_flag_to_target("inspect" "-s WASM=1")
append_compiler_flag("-s WASM=1")
cmake
দিয়ে বিল্ডিং করার অর্থ হল প্রথমে cmake
নিজে চালানোর মাধ্যমে কিছু Makefiles
তৈরি করা, তারপর make
কমান্ডটি চালানো যা কম্পাইলেশন ধাপটি সম্পাদন করবে। উল্লেখ্য, যেহেতু আমরা Emscripten ব্যবহার করছি আমাদেরকে ডিফল্ট হোস্ট কম্পাইলারের পরিবর্তে Emscripten কম্পাইলার টুলচেন ব্যবহার করতে হবে। এটি Emscripten.cmake
ব্যবহার করে অর্জন করা হয়েছে যা Emscripten SDK- এর অংশ এবং এটির পাথকে একটি প্যারামিটার হিসাবে নিজে cmake
জন্য পাস করে। নীচের কমান্ড লাইনটি আমরা মেকফাইলস তৈরি করতে ব্যবহার করি:
cmake path/to/aom \
-DENABLE_CCACHE=1 -DAOM_TARGET_CPU=generic -DENABLE_DOCS=0 \
-DCONFIG_ACCOUNTING=1 -DCONFIG_INSPECTION=1 -DCONFIG_MULTITHREAD=0 \
-DCONFIG_RUNTIME_CPU_DETECT=0 -DCONFIG_UNIT_TESTS=0
-DCONFIG_WEBM_IO=0 \
-DCMAKE_TOOLCHAIN_FILE=path/to/emsdk-portable/.../Emscripten.cmake
AV1 লাইব্রেরি সোর্স ফাইলের অবস্থানের সম্পূর্ণ পাথে প্যারামিটার path/to/aom
সেট করা উচিত। path/to/emsdk-portable/…/Emscripten.cmake
প্যারামিটারটিকে Emscripten.cmake টুলচেন বর্ণনা ফাইলের পাথে সেট করতে হবে।
সুবিধার জন্য আমরা সেই ফাইলটি সনাক্ত করতে একটি শেল স্ক্রিপ্ট ব্যবহার করি:
#!/bin/sh
EMCC_LOC=`which emcc`
EMSDK_LOC=`echo $EMCC_LOC | sed 's?/emscripten/[0-9.]*/emcc??'`
EMCMAKE_LOC=`find $EMSDK_LOC -name Emscripten.cmake -print`
echo $EMCMAKE_LOC
আপনি যদি এই প্রকল্পের জন্য শীর্ষ-স্তরের Makefile
দেখেন, তাহলে আপনি দেখতে পারবেন কিভাবে সেই স্ক্রিপ্টটি বিল্ড কনফিগার করতে ব্যবহৃত হয়।
এখন যেহেতু সমস্ত সেটআপ সম্পন্ন হয়েছে, আমরা কেবল make
কল করি যা নমুনা সহ সমগ্র উৎস গাছ তৈরি করবে, তবে সবচেয়ে গুরুত্বপূর্ণভাবে libaom.a
তৈরি করবে যাতে ভিডিও ডিকোডার সংকলিত হয় এবং আমাদের প্রকল্পে অন্তর্ভুক্ত করার জন্য প্রস্তুত থাকে।
লাইব্রেরিতে ইন্টারফেস করার জন্য একটি API ডিজাইন করা
একবার আমরা আমাদের লাইব্রেরি তৈরি করে ফেললে, এটিতে সংকুচিত ভিডিও ডেটা পাঠানোর জন্য কীভাবে এটির সাথে ইন্টারফেস করা যায় এবং তারপরে আমরা ব্রাউজারে প্রদর্শন করতে পারি এমন ভিডিওর ফ্রেমগুলি পড়তে পারি।
AV1 কোড ট্রির ভিতরে একবার নজর দিলে, একটি ভাল সূচনা পয়েন্ট হল একটি উদাহরণ ভিডিও ডিকোডার যা [simple_decoder.c](https://aomedia.googlesource.com/aom/+/master/examples/simple_decoder.c)
ফাইলে পাওয়া যাবে। সেই ডিকোডারটি একটি IVF ফাইলে পড়ে এবং ভিডিওর ফ্রেমগুলিকে প্রতিনিধিত্ব করে এমন চিত্রগুলির একটি সিরিজে এটিকে ডিকোড করে৷
আমরা সোর্স ফাইল [decode-av1.c](https://github.com/GoogleChromeLabs/wasm-av1/blob/master/decode-av1.c)
এ আমাদের ইন্টারফেস প্রয়োগ করি।
যেহেতু আমাদের ব্রাউজার ফাইল সিস্টেম থেকে ফাইলগুলি পড়তে পারে না, তাই আমাদের এমন কিছু ইন্টারফেস ডিজাইন করতে হবে যা আমাদের I/Oকে বিমূর্ত করতে দেয় যাতে আমরা আমাদের AV1 লাইব্রেরিতে ডেটা পেতে উদাহরণ ডিকোডারের মতো কিছু তৈরি করতে পারি।
কমান্ড লাইনে, ফাইল I/O হল যা একটি স্ট্রিম ইন্টারফেস হিসাবে পরিচিত, তাই আমরা কেবলমাত্র আমাদের নিজস্ব ইন্টারফেসটি সংজ্ঞায়িত করতে পারি যা স্ট্রিম I/O এর মতো দেখায় এবং অন্তর্নিহিত বাস্তবায়নে আমরা যা পছন্দ করি তা তৈরি করতে পারি।
আমরা আমাদের ইন্টারফেসকে এভাবে সংজ্ঞায়িত করি:
DATA_Source *DS_open(const char *what);
size_t DS_read(DATA_Source *ds,
unsigned char *buf, size_t bytes);
int DS_empty(DATA_Source *ds);
void DS_close(DATA_Source *ds);
// Helper function for blob support
void DS_set_blob(DATA_Source *ds, void *buf, size_t len);
open/read/empty/close
ফাংশনগুলি দেখতে অনেকটা সাধারণ ফাইল I/O অপারেশনের মতো যা আমাদেরকে কমান্ড লাইন অ্যাপ্লিকেশনের জন্য ফাইল I/O-এ সহজে ম্যাপ করতে দেয় বা ব্রাউজারের ভিতরে চালানোর সময় অন্য কোনো উপায়ে প্রয়োগ করতে দেয়। DATA_Source
প্রকারটি জাভাস্ক্রিপ্টের দিক থেকে অস্বচ্ছ, এবং শুধুমাত্র ইন্টারফেসকে এনক্যাপসুলেট করতে কাজ করে। দ্রষ্টব্য, যে একটি API তৈরি করা যা ফাইলের শব্দার্থবিদ্যাকে ঘনিষ্ঠভাবে অনুসরণ করে তা অন্য অনেক কোড-বেসগুলিতে পুনঃব্যবহার করা সহজ করে যা একটি কমান্ড লাইন (যেমন diff, sed, ইত্যাদি) থেকে ব্যবহার করার উদ্দেশ্যে করা হয়।
আমাদের DS_set_blob
নামে একটি সহায়ক ফাংশন সংজ্ঞায়িত করতে হবে যা আমাদের স্ট্রিম I/O ফাংশনে কাঁচা বাইনারি ডেটা আবদ্ধ করে। এটি ব্লবটিকে 'পড়া' করতে দেয় যেন এটি একটি স্ট্রিম (যেমন একটি ক্রমানুসারে পড়া ফাইলের মতো দেখাচ্ছে)।
আমাদের উদাহরণ বাস্তবায়ন ব্লব-এ পাস করাকে পড়তে সক্ষম করে যেন এটি একটি ক্রমানুসারে পঠিত ডেটা উৎস। রেফারেন্স কোডটি blob-api.c
ফাইলটিতে পাওয়া যেতে পারে এবং সম্পূর্ণ বাস্তবায়নটি হল এই:
struct DATA_Source {
void *ds_Buf;
size_t ds_Len;
size_t ds_Pos;
};
DATA_Source *
DS_open(const char *what) {
DATA_Source *ds;
ds = malloc(sizeof *ds);
if (ds != NULL) {
memset(ds, 0, sizeof *ds);
}
return ds;
}
size_t
DS_read(DATA_Source *ds, unsigned char *buf, size_t bytes) {
if (DS_empty(ds) || buf == NULL) {
return 0;
}
if (bytes > (ds->ds_Len - ds->ds_Pos)) {
bytes = ds->ds_Len - ds->ds_Pos;
}
memcpy(buf, &ds->ds_Buf[ds->ds_Pos], bytes);
ds->ds_Pos += bytes;
return bytes;
}
int
DS_empty(DATA_Source *ds) {
return ds->ds_Pos >= ds->ds_Len;
}
void
DS_close(DATA_Source *ds) {
free(ds);
}
void
DS_set_blob(DATA_Source *ds, void *buf, size_t len) {
ds->ds_Buf = buf;
ds->ds_Len = len;
ds->ds_Pos = 0;
}
ব্রাউজারের বাইরে পরীক্ষা করার জন্য একটি পরীক্ষার জোতা তৈরি করা
সফ্টওয়্যার প্রকৌশলের সর্বোত্তম অনুশীলনগুলির মধ্যে একটি হল ইন্টিগ্রেশন পরীক্ষার সাথে একত্রে কোডের জন্য ইউনিট পরীক্ষা তৈরি করা।
ব্রাউজারে WebAssembly দিয়ে তৈরি করার সময়, আমরা যে কোডের সাথে কাজ করছি তার ইন্টারফেসের জন্য ইউনিট পরীক্ষার কিছু ফর্ম তৈরি করা বোধগম্য হয় যাতে আমরা ব্রাউজারের বাইরে ডিবাগ করতে পারি এবং আমাদের তৈরি করা ইন্টারফেসটি পরীক্ষা করতেও সক্ষম হতে পারি।
এই উদাহরণে আমরা AV1 লাইব্রেরির ইন্টারফেস হিসাবে একটি স্ট্রিম ভিত্তিক API অনুকরণ করছি। সুতরাং, যৌক্তিকভাবে এটি একটি টেস্ট হার্নেস তৈরি করা বোধগম্য হয় যা আমরা আমাদের API-এর একটি সংস্করণ তৈরি করতে ব্যবহার করতে পারি যা কমান্ড লাইনে চলে এবং প্রকৃত ফাইল I/O কে আমাদের DATA_Source
API-এর নীচে I/O ফাইলটি প্রয়োগ করে হুডের নীচে কাজ করে।
আমাদের পরীক্ষার জোতা জন্য স্ট্রীম I/O কোড সোজা, এবং এই মত দেখায়:
DATA_Source *
DS_open(const char *what) {
return (DATA_Source *)fopen(what, "rb");
}
size_t
DS_read(DATA_Source *ds, unsigned char *buf, size_t bytes) {
return fread(buf, 1, bytes, (FILE *)ds);
}
int
DS_empty(DATA_Source *ds) {
return feof((FILE *)ds);
}
void
DS_close(DATA_Source *ds) {
fclose((FILE *)ds);
}
স্ট্রিম ইন্টারফেসকে বিমূর্ত করে আমরা ব্রাউজারে থাকাকালীন বাইনারি ডেটা ব্লব ব্যবহার করার জন্য আমাদের WebAssembly মডিউল তৈরি করতে পারি এবং কমান্ড লাইন থেকে পরীক্ষা করার জন্য কোড তৈরি করার সময় বাস্তব ফাইলগুলিতে ইন্টারফেস তৈরি করতে পারি। আমাদের পরীক্ষার জোতা কোড উদাহরণ উত্স ফাইল test.c
পাওয়া যাবে.
একাধিক ভিডিও ফ্রেমের জন্য একটি বাফারিং প্রক্রিয়া প্রয়োগ করা
ভিডিও ব্যাক প্লে করার সময়, মসৃণ প্লেব্যাকে সাহায্য করার জন্য কয়েকটি ফ্রেম বাফার করা সাধারণ অভ্যাস। আমাদের উদ্দেশ্যে আমরা শুধুমাত্র 10 ফ্রেমের ভিডিওর একটি বাফার প্রয়োগ করব, তাই আমরা প্লেব্যাক শুরু করার আগে 10টি ফ্রেম বাফার করব৷ তারপর প্রতিবার একটি ফ্রেম প্রদর্শিত হলে, আমরা অন্য ফ্রেমটি ডিকোড করার চেষ্টা করব যাতে আমরা বাফারটি পূর্ণ রাখি। এই পদ্ধতিটি নিশ্চিত করে যে ভিডিও তোতলানো বন্ধ করতে সাহায্য করার জন্য ফ্রেমগুলি আগেই উপলব্ধ রয়েছে৷
আমাদের সাধারণ উদাহরণের সাহায্যে, সম্পূর্ণ সংকুচিত ভিডিওটি পড়ার জন্য উপলব্ধ, তাই বাফারিংয়ের সত্যিই প্রয়োজন নেই। যাইহোক, যদি আমরা একটি সার্ভার থেকে স্ট্রিমিং ইনপুট সমর্থন করার জন্য সোর্স ডেটা ইন্টারফেস প্রসারিত করতে চাই, তাহলে আমাদের বাফারিং মেকানিজম থাকতে হবে।
AV1 লাইব্রেরি থেকে ভিডিও ডেটার ফ্রেম পড়ার জন্য এবং বাফারে এইভাবে সংরক্ষণ করার জন্য decode-av1.c
এর কোড:
void
AVX_Decoder_run(AVX_Decoder *ad) {
...
// Try to decode an image from the compressed stream, and buffer
while (ad->ad_NumBuffered < NUM_FRAMES_BUFFERED) {
ad->ad_Image = aom_codec_get_frame(&ad->ad_Codec,
&ad->ad_Iterator);
if (ad->ad_Image == NULL) {
break;
}
else {
buffer_frame(ad);
}
}
আমরা বাফারে 10টি ফ্রেম ভিডিও ধারণ করার জন্য বেছে নিয়েছি, যা শুধুমাত্র একটি নির্বিচারে পছন্দ। আরও ফ্রেম বাফার করার অর্থ হল ভিডিও প্লেব্যাক শুরু করার জন্য আরও অপেক্ষার সময়, যখন খুব কম ফ্রেম বাফার করার ফলে প্লেব্যাকের সময় স্টল হতে পারে৷ একটি নেটিভ ব্রাউজার বাস্তবায়নে, ফ্রেমের বাফারিং এই বাস্তবায়নের চেয়ে অনেক বেশি জটিল।
WebGL এর মাধ্যমে পৃষ্ঠায় ভিডিও ফ্রেম পাওয়া যাচ্ছে
আমরা বাফার করা ভিডিওর ফ্রেমগুলি আমাদের পৃষ্ঠায় প্রদর্শন করা প্রয়োজন৷ যেহেতু এটি গতিশীল ভিডিও বিষয়বস্তু, আমরা যত তাড়াতাড়ি সম্ভব এটি করতে সক্ষম হতে চাই। এর জন্য, আমরা WebGL- এ ফিরে যাই।
WebGL আমাদের একটি ছবি নিতে দেয়, যেমন ভিডিওর একটি ফ্রেম, এবং এটিকে একটি টেক্সচার হিসাবে ব্যবহার করতে দেয় যা কিছু জ্যামিতিতে আঁকা হয়। WebGL জগতে, সবকিছুই ত্রিভুজ নিয়ে গঠিত। সুতরাং, আমাদের ক্ষেত্রে আমরা WebGL-এর একটি সুবিধাজনক অন্তর্নির্মিত বৈশিষ্ট্য ব্যবহার করতে পারি, যার নাম gl.TRIANGLE_FAN।
যাইহোক, একটি ছোট সমস্যা আছে. WebGL টেক্সচার RGB ছবি, প্রতি রঙ চ্যানেলে এক বাইট। আমাদের AV1 ডিকোডার থেকে আউটপুট হল একটি তথাকথিত YUV ফর্ম্যাটে ছবি, যেখানে ডিফল্ট আউটপুট প্রতি চ্যানেলে 16 বিট থাকে, এবং প্রতিটি U বা V মান প্রকৃত আউটপুট চিত্রের 4 পিক্সেলের সাথে মিলে যায়। এর অর্থ হল আমরা ওয়েবজিএল-এ প্রদর্শনের জন্য পাস করার আগে ছবিটিকে রঙিন রূপান্তর করতে হবে।
এটি করার জন্য, আমরা একটি ফাংশন AVX_YUV_to_RGB()
প্রয়োগ করি যা আপনি yuv-to-rgb.c
সোর্স ফাইলে খুঁজে পেতে পারেন। এই ফাংশনটি AV1 ডিকোডার থেকে আউটপুটকে এমন কিছুতে রূপান্তর করে যা আমরা WebGL-এ পাস করতে পারি। দ্রষ্টব্য, যখন আমরা জাভাস্ক্রিপ্ট থেকে এই ফাংশনটিকে কল করি তখন আমাদের নিশ্চিত করতে হবে যে আমরা যে মেমরিতে রূপান্তরিত চিত্রটি লিখছি সেটি WebAssembly মডিউলের মেমরির মধ্যে বরাদ্দ করা হয়েছে - অন্যথায় এটি এটিতে অ্যাক্সেস পেতে পারে না। WebAssembly মডিউল থেকে একটি ইমেজ বের করে পর্দায় পেইন্ট করার ফাংশন হল:
function show_frame(af) {
if (rgb_image != 0) {
// Convert The 16-bit YUV to 8-bit RGB
let buf = Module._AVX_Video_Frame_get_buffer(af);
Module._AVX_YUV_to_RGB(rgb_image, buf, WIDTH, HEIGHT);
// Paint the image onto the canvas
drawImageToCanvas(new Uint8Array(Module.HEAPU8.buffer,
rgb_image, 3 * WIDTH * HEIGHT), WIDTH, HEIGHT);
}
}
drawImageToCanvas()
ফাংশন যা WebGL পেইন্টিং প্রয়োগ করে তা রেফারেন্সের জন্য উৎস ফাইল draw-image.js
এ পাওয়া যাবে।
ভবিষ্যত কাজ এবং takeaways
দুটি পরীক্ষামূলক ভিডিও ফাইলে (24 fps ভিডিও হিসাবে রেকর্ড করা) আমাদের ডেমো চেষ্টা করা আমাদের কয়েকটি জিনিস শেখায়:
- WebAssembly ব্যবহার করে ব্রাউজারে কার্যকরীভাবে চালানোর জন্য একটি জটিল কোড-বেস তৈরি করা সম্পূর্ণরূপে সম্ভব; এবং
- WebAssembly-এর মাধ্যমে উন্নত ভিডিও ডিকোডিংয়ের মতো CPU-এর মতো নিবিড় কিছু করা সম্ভব।
যদিও কিছু সীমাবদ্ধতা আছে: বাস্তবায়ন সবই প্রধান থ্রেডে চলছে এবং আমরা সেই একক থ্রেডে পেইন্টিং এবং ভিডিও ডিকোডিং আন্তঃলিভ করি। একটি ওয়েব কর্মীতে ডিকোডিং অফলোড করা আমাদের মসৃণ প্লেব্যাক প্রদান করতে পারে, কারণ ফ্রেমগুলি ডিকোড করার সময়টি সেই ফ্রেমের বিষয়বস্তুর উপর অত্যন্ত নির্ভরশীল এবং কখনও কখনও আমাদের বাজেটের চেয়ে বেশি সময় নিতে পারে৷
WebAssembly-এ সংকলন একটি জেনেরিক CPU প্রকারের জন্য AV1 কনফিগারেশন ব্যবহার করে। যদি আমরা একটি জেনেরিক সিপিইউ-এর জন্য কমান্ড লাইনে স্থানীয়ভাবে কম্পাইল করি আমরা WebAssembly সংস্করণের মতো ভিডিও ডিকোড করার জন্য একই রকম CPU লোড দেখতে পাই, তবে AV1 ডিকোডার লাইব্রেরিতে SIMD বাস্তবায়ন অন্তর্ভুক্ত রয়েছে যা 5 গুণ দ্রুত চলে। WebAssembly কমিউনিটি গ্রুপ বর্তমানে SIMD প্রিমিটিভস অন্তর্ভুক্ত করার জন্য স্ট্যান্ডার্ড প্রসারিত করার জন্য কাজ করছে, এবং যখন এটি আসে তখন এটি যথেষ্ট পরিমাণে ডিকোডিং গতি বাড়ানোর প্রতিশ্রুতি দেয়। যখন এটি ঘটবে, তখন একটি WebAssembly ভিডিও ডিকোডার থেকে রিয়েল-টাইমে 4k HD ভিডিও ডিকোড করা সম্পূর্ণরূপে সম্ভব হবে৷
যাই হোক না কেন, উদাহরণ কোডটি একটি নির্দেশিকা হিসাবে কার্যকর যেকোন বিদ্যমান কমান্ড লাইন ইউটিলিটিকে একটি WebAssembly মডিউল হিসাবে চালানোর জন্য পোর্ট করতে সাহায্য করে এবং দেখায় যে আজকে ওয়েবে কী সম্ভব।
ক্রেডিট
মূল্যবান পর্যালোচনা এবং প্রতিক্রিয়া প্রদানের জন্য জেফ পসনিক, এরিক বিডেলম্যান এবং টমাস স্টেইনারকে ধন্যবাদ।
,WebAssembly আমাদের নতুন বৈশিষ্ট্য সহ ব্রাউজার প্রসারিত করতে দেয়। এই নিবন্ধটি দেখায় কিভাবে AV1 ভিডিও ডিকোডার পোর্ট করতে হয় এবং যেকোনো আধুনিক ব্রাউজারে AV1 ভিডিও চালাতে হয়।
WebAssembly সম্পর্কে সেরা জিনিসগুলির মধ্যে একটি হল নতুন ক্ষমতার সাথে সক্ষমতা পরীক্ষা করা এবং ব্রাউজারটি সেই বৈশিষ্ট্যগুলি স্থানীয়ভাবে পাঠানোর আগে নতুন ধারণাগুলি বাস্তবায়ন করা (যদি থাকে)। আপনি এইভাবে WebAssembly ব্যবহার করার কথা ভাবতে পারেন একটি উচ্চ-পারফরম্যান্স পলিফিল মেকানিজম হিসেবে, যেখানে আপনি JavaScript-এর পরিবর্তে C/C++ বা Rust-এ আপনার বৈশিষ্ট্য লেখেন।
পোর্টিংয়ের জন্য উপলব্ধ বিদ্যমান কোডের আধিক্যের সাথে, ব্রাউজারে এমন কিছু করা সম্ভব যা WebAssembly না আসা পর্যন্ত কার্যকর ছিল না।
এই নিবন্ধটি কীভাবে বিদ্যমান AV1 ভিডিও কোডেক সোর্স কোড নিতে হয়, এটির জন্য একটি র্যাপার তৈরি করতে হয় এবং এটি আপনার ব্রাউজারের ভিতরে ব্যবহার করে দেখুন এবং র্যাপার ডিবাগ করার জন্য একটি পরীক্ষামূলক জোতা তৈরিতে সহায়তা করার জন্য টিপসগুলির একটি উদাহরণ দিয়ে হেঁটে যাবে৷ উদাহরণের জন্য সম্পূর্ণ সোর্স কোড এখানে রেফারেন্সের জন্য github.com/GoogleChromeLabs/wasm-av1 এ উপলব্ধ।
এই দুটি 24fps টেস্ট ভিডিও ফাইলগুলির একটি ডাউনলোড করুন এবং আমাদের তৈরি ডেমোতে চেষ্টা করুন৷
একটি আকর্ষণীয় কোড-বেস নির্বাচন করা
এখন বেশ কয়েক বছর ধরে, আমরা দেখেছি যে ওয়েবে ট্র্যাফিকের একটি বড় শতাংশ ভিডিও ডেটা নিয়ে গঠিত, সিসকো এটিকে প্রকৃতপক্ষে 80% হিসাবে অনুমান করে ! অবশ্যই, ব্রাউজার বিক্রেতা এবং ভিডিও সাইটগুলি এই সমস্ত ভিডিও সামগ্রীর দ্বারা ব্যবহৃত ডেটা হ্রাস করার ইচ্ছা সম্পর্কে ব্যাপকভাবে সচেতন। এর মূল চাবিকাঠি, অবশ্যই, আরও ভাল কম্প্রেশন, এবং আপনি যেমন আশা করছেন ইন্টারনেট জুড়ে ভিডিও শিপিংয়ের ডেটা বোঝা হ্রাস করার লক্ষ্যে পরবর্তী প্রজন্মের ভিডিও কম্প্রেশনে প্রচুর গবেষণা রয়েছে।
যেমনটি ঘটে, অ্যালায়েন্স ফর ওপেন মিডিয়া AV1 নামক একটি পরবর্তী প্রজন্মের ভিডিও কম্প্রেশন স্কিম নিয়ে কাজ করছে যা ভিডিও ডেটার আকারকে উল্লেখযোগ্যভাবে সঙ্কুচিত করার প্রতিশ্রুতি দেয়। ভবিষ্যতে, আমরা আশা করব ব্রাউজারগুলি AV1-এর জন্য নেটিভ সাপোর্ট পাঠাবে, কিন্তু সৌভাগ্যবশত কম্প্রেসার এবং ডিকম্প্রেসারের জন্য সোর্স কোড হল ওপেন সোর্স , যা এটিকে WebAssembly-এ কম্পাইল করার চেষ্টা করার জন্য একটি আদর্শ প্রার্থী করে তোলে যাতে আমরা ব্রাউজারে এটি নিয়ে পরীক্ষা করতে পারি।

ব্রাউজারে ব্যবহারের জন্য মানিয়ে নেওয়া
এই কোডটি ব্রাউজারে পাওয়ার জন্য আমাদের প্রথমে যা করতে হবে তা হল এপিআই কেমন তা বোঝার জন্য বিদ্যমান কোডটি জানা। এই কোডটি প্রথম দেখার সময়, দুটি জিনিস আলাদা হয়:
- উৎস গাছটি
cmake
নামক একটি টুল ব্যবহার করে নির্মিত হয়; এবং - এমন অনেকগুলি উদাহরণ রয়েছে যেগুলি সকলেই কিছু ধরণের ফাইল-ভিত্তিক ইন্টারফেস ধরে নেয়।
ডিফল্টরূপে তৈরি করা সমস্ত উদাহরণ কমান্ড লাইনে চালানো যেতে পারে এবং সম্প্রদায়ে উপলব্ধ অন্যান্য কোড বেসগুলিতে এটি সত্য হতে পারে। সুতরাং, ব্রাউজারে চালানোর জন্য আমরা যে ইন্টারফেসটি তৈরি করতে যাচ্ছি তা অন্য অনেক কমান্ড লাইন টুলের জন্য উপযোগী হতে পারে।
সোর্স কোড তৈরি করতে cmake
ব্যবহার করে
সৌভাগ্যবশত, AV1 লেখকরা Emscripten নিয়ে পরীক্ষা-নিরীক্ষা করছেন, SDK যা আমরা আমাদের WebAssembly সংস্করণ তৈরি করতে ব্যবহার করতে যাচ্ছি। AV1 সংগ্রহস্থলের রুটে, CMakeLists.txt
ফাইলটিতে এই বিল্ড নিয়মগুলি রয়েছে:
if(EMSCRIPTEN)
add_preproc_definition(_POSIX_SOURCE)
append_link_flag_to_target("inspect" "-s TOTAL_MEMORY=402653184")
append_link_flag_to_target("inspect" "-s MODULARIZE=1")
append_link_flag_to_target("inspect"
"-s EXPORT_NAME=\"\'DecoderModule\'\"")
append_link_flag_to_target("inspect" "--memory-init-file 0")
if("${CMAKE_BUILD_TYPE}" STREQUAL "")
# Default to -O3 when no build type is specified.
append_compiler_flag("-O3")
endif()
em_link_post_js(inspect "${AOM_ROOT}/tools/inspect-post.js")
endif()
Emscripten টুলচেইন দুটি ফরম্যাটে আউটপুট তৈরি করতে পারে, একটিকে বলা হয় asm.js
এবং অন্যটিকে WebAssembly। আমরা WebAssembly টার্গেট করব কারণ এটি ছোট আউটপুট তৈরি করে এবং দ্রুত চালাতে পারে। এই বিদ্যমান বিল্ড নিয়মগুলি একটি ইন্সপেক্টর অ্যাপ্লিকেশনে ব্যবহারের জন্য লাইব্রেরির একটি asm.js
সংস্করণ কম্পাইল করার জন্য বোঝানো হয়েছে যা একটি ভিডিও ফাইলের বিষয়বস্তু দেখার জন্য ব্যবহার করা হয়। আমাদের ব্যবহারের জন্য, আমাদের WebAssembly আউটপুট দরকার তাই আমরা উপরের নিয়মে ক্লোজিং endif()
স্টেটমেন্টের ঠিক আগে এই লাইনগুলি যোগ করি।
# Force generation of Wasm instead of asm.js
append_link_flag_to_target("inspect" "-s WASM=1")
append_compiler_flag("-s WASM=1")
cmake
দিয়ে বিল্ডিং করার অর্থ হল প্রথমে cmake
নিজে চালানোর মাধ্যমে কিছু Makefiles
তৈরি করা, তারপর make
কমান্ডটি চালানো যা কম্পাইলেশন ধাপটি সম্পাদন করবে। উল্লেখ্য, যেহেতু আমরা Emscripten ব্যবহার করছি আমাদেরকে ডিফল্ট হোস্ট কম্পাইলারের পরিবর্তে Emscripten কম্পাইলার টুলচেন ব্যবহার করতে হবে। এটি Emscripten.cmake
ব্যবহার করে অর্জন করা হয়েছে যা Emscripten SDK- এর অংশ এবং এটির পাথকে একটি প্যারামিটার হিসাবে নিজে cmake
জন্য পাস করে। নীচের কমান্ড লাইনটি আমরা মেকফাইলস তৈরি করতে ব্যবহার করি:
cmake path/to/aom \
-DENABLE_CCACHE=1 -DAOM_TARGET_CPU=generic -DENABLE_DOCS=0 \
-DCONFIG_ACCOUNTING=1 -DCONFIG_INSPECTION=1 -DCONFIG_MULTITHREAD=0 \
-DCONFIG_RUNTIME_CPU_DETECT=0 -DCONFIG_UNIT_TESTS=0
-DCONFIG_WEBM_IO=0 \
-DCMAKE_TOOLCHAIN_FILE=path/to/emsdk-portable/.../Emscripten.cmake
AV1 লাইব্রেরি সোর্স ফাইলের অবস্থানের সম্পূর্ণ পাথে প্যারামিটার path/to/aom
সেট করা উচিত। path/to/emsdk-portable/…/Emscripten.cmake
প্যারামিটারটিকে Emscripten.cmake টুলচেন বর্ণনা ফাইলের পাথে সেট করতে হবে।
সুবিধার জন্য আমরা সেই ফাইলটি সনাক্ত করতে একটি শেল স্ক্রিপ্ট ব্যবহার করি:
#!/bin/sh
EMCC_LOC=`which emcc`
EMSDK_LOC=`echo $EMCC_LOC | sed 's?/emscripten/[0-9.]*/emcc??'`
EMCMAKE_LOC=`find $EMSDK_LOC -name Emscripten.cmake -print`
echo $EMCMAKE_LOC
আপনি যদি এই প্রকল্পের জন্য শীর্ষ-স্তরের Makefile
দেখেন, তাহলে আপনি দেখতে পারবেন কিভাবে সেই স্ক্রিপ্টটি বিল্ড কনফিগার করতে ব্যবহৃত হয়।
এখন যেহেতু সমস্ত সেটআপ সম্পন্ন হয়েছে, আমরা কেবল make
কল করি যা নমুনা সহ সমগ্র উৎস গাছ তৈরি করবে, তবে সবচেয়ে গুরুত্বপূর্ণভাবে libaom.a
তৈরি করবে যাতে ভিডিও ডিকোডার সংকলিত হয় এবং আমাদের প্রকল্পে অন্তর্ভুক্ত করার জন্য প্রস্তুত থাকে।
লাইব্রেরিতে ইন্টারফেস করার জন্য একটি API ডিজাইন করা
একবার আমরা আমাদের লাইব্রেরি তৈরি করে ফেললে, এটিতে সংকুচিত ভিডিও ডেটা পাঠানোর জন্য কীভাবে এটির সাথে ইন্টারফেস করা যায় এবং তারপরে আমরা ব্রাউজারে প্রদর্শন করতে পারি এমন ভিডিওর ফ্রেমগুলি পড়তে পারি।
AV1 কোড ট্রির ভিতরে একবার নজর দিলে, একটি ভাল সূচনা পয়েন্ট হল একটি উদাহরণ ভিডিও ডিকোডার যা [simple_decoder.c](https://aomedia.googlesource.com/aom/+/master/examples/simple_decoder.c)
ফাইলে পাওয়া যাবে। সেই ডিকোডারটি একটি IVF ফাইলে পড়ে এবং ভিডিওর ফ্রেমগুলিকে প্রতিনিধিত্ব করে এমন চিত্রগুলির একটি সিরিজে এটিকে ডিকোড করে৷
আমরা সোর্স ফাইল [decode-av1.c](https://github.com/GoogleChromeLabs/wasm-av1/blob/master/decode-av1.c)
এ আমাদের ইন্টারফেস প্রয়োগ করি।
যেহেতু আমাদের ব্রাউজার ফাইল সিস্টেম থেকে ফাইলগুলি পড়তে পারে না, তাই আমাদের এমন কিছু ইন্টারফেস ডিজাইন করতে হবে যা আমাদের I/Oকে বিমূর্ত করতে দেয় যাতে আমরা আমাদের AV1 লাইব্রেরিতে ডেটা পেতে উদাহরণ ডিকোডারের মতো কিছু তৈরি করতে পারি।
কমান্ড লাইনে, ফাইল I/O হল যা একটি স্ট্রিম ইন্টারফেস হিসাবে পরিচিত, তাই আমরা কেবলমাত্র আমাদের নিজস্ব ইন্টারফেসটি সংজ্ঞায়িত করতে পারি যা স্ট্রিম I/O এর মতো দেখায় এবং অন্তর্নিহিত বাস্তবায়নে আমরা যা পছন্দ করি তা তৈরি করতে পারি।
আমরা আমাদের ইন্টারফেসকে এভাবে সংজ্ঞায়িত করি:
DATA_Source *DS_open(const char *what);
size_t DS_read(DATA_Source *ds,
unsigned char *buf, size_t bytes);
int DS_empty(DATA_Source *ds);
void DS_close(DATA_Source *ds);
// Helper function for blob support
void DS_set_blob(DATA_Source *ds, void *buf, size_t len);
open/read/empty/close
ফাংশনগুলি দেখতে অনেকটা সাধারণ ফাইল I/O অপারেশনের মতো যা আমাদেরকে কমান্ড লাইন অ্যাপ্লিকেশনের জন্য ফাইল I/O-এ সহজে ম্যাপ করতে দেয় বা ব্রাউজারের ভিতরে চালানোর সময় অন্য কোনো উপায়ে প্রয়োগ করতে দেয়। DATA_Source
প্রকারটি জাভাস্ক্রিপ্টের দিক থেকে অস্বচ্ছ, এবং শুধুমাত্র ইন্টারফেসকে এনক্যাপসুলেট করতে কাজ করে। দ্রষ্টব্য, যে একটি API তৈরি করা যা ফাইলের শব্দার্থবিদ্যাকে ঘনিষ্ঠভাবে অনুসরণ করে তা অন্য অনেক কোড-বেসগুলিতে পুনঃব্যবহার করা সহজ করে যা একটি কমান্ড লাইন (যেমন diff, sed, ইত্যাদি) থেকে ব্যবহার করার উদ্দেশ্যে করা হয়।
আমাদের DS_set_blob
নামে একটি সহায়ক ফাংশন সংজ্ঞায়িত করতে হবে যা আমাদের স্ট্রিম I/O ফাংশনে কাঁচা বাইনারি ডেটা আবদ্ধ করে। এটি ব্লবটিকে 'পড়া' করতে দেয় যেন এটি একটি স্ট্রিম (যেমন একটি ক্রমানুসারে পড়া ফাইলের মতো দেখাচ্ছে)।
আমাদের উদাহরণ বাস্তবায়ন ব্লব-এ পাস করাকে পড়তে সক্ষম করে যেন এটি একটি ক্রমানুসারে পঠিত ডেটা উৎস। রেফারেন্স কোডটি blob-api.c
ফাইলটিতে পাওয়া যেতে পারে এবং সম্পূর্ণ বাস্তবায়নটি হল এই:
struct DATA_Source {
void *ds_Buf;
size_t ds_Len;
size_t ds_Pos;
};
DATA_Source *
DS_open(const char *what) {
DATA_Source *ds;
ds = malloc(sizeof *ds);
if (ds != NULL) {
memset(ds, 0, sizeof *ds);
}
return ds;
}
size_t
DS_read(DATA_Source *ds, unsigned char *buf, size_t bytes) {
if (DS_empty(ds) || buf == NULL) {
return 0;
}
if (bytes > (ds->ds_Len - ds->ds_Pos)) {
bytes = ds->ds_Len - ds->ds_Pos;
}
memcpy(buf, &ds->ds_Buf[ds->ds_Pos], bytes);
ds->ds_Pos += bytes;
return bytes;
}
int
DS_empty(DATA_Source *ds) {
return ds->ds_Pos >= ds->ds_Len;
}
void
DS_close(DATA_Source *ds) {
free(ds);
}
void
DS_set_blob(DATA_Source *ds, void *buf, size_t len) {
ds->ds_Buf = buf;
ds->ds_Len = len;
ds->ds_Pos = 0;
}
ব্রাউজারের বাইরে পরীক্ষা করার জন্য একটি পরীক্ষার জোতা তৈরি করা
সফ্টওয়্যার প্রকৌশলের সর্বোত্তম অনুশীলনগুলির মধ্যে একটি হল ইন্টিগ্রেশন পরীক্ষার সাথে একত্রে কোডের জন্য ইউনিট পরীক্ষা তৈরি করা।
ব্রাউজারে WebAssembly দিয়ে তৈরি করার সময়, আমরা যে কোডের সাথে কাজ করছি তার ইন্টারফেসের জন্য ইউনিট পরীক্ষার কিছু ফর্ম তৈরি করা বোধগম্য হয় যাতে আমরা ব্রাউজারের বাইরে ডিবাগ করতে পারি এবং আমাদের তৈরি করা ইন্টারফেসটি পরীক্ষা করতেও সক্ষম হতে পারি।
এই উদাহরণে আমরা AV1 লাইব্রেরির ইন্টারফেস হিসাবে একটি স্ট্রিম ভিত্তিক API অনুকরণ করছি। সুতরাং, যৌক্তিকভাবে এটি একটি টেস্ট হার্নেস তৈরি করা বোধগম্য হয় যা আমরা আমাদের API-এর একটি সংস্করণ তৈরি করতে ব্যবহার করতে পারি যা কমান্ড লাইনে চলে এবং প্রকৃত ফাইল I/O কে আমাদের DATA_Source
API-এর নীচে I/O ফাইলটি প্রয়োগ করে হুডের নীচে কাজ করে।
আমাদের পরীক্ষার জোতা জন্য স্ট্রীম I/O কোড সোজা, এবং এই মত দেখায়:
DATA_Source *
DS_open(const char *what) {
return (DATA_Source *)fopen(what, "rb");
}
size_t
DS_read(DATA_Source *ds, unsigned char *buf, size_t bytes) {
return fread(buf, 1, bytes, (FILE *)ds);
}
int
DS_empty(DATA_Source *ds) {
return feof((FILE *)ds);
}
void
DS_close(DATA_Source *ds) {
fclose((FILE *)ds);
}
স্ট্রিম ইন্টারফেসকে বিমূর্ত করে আমরা ব্রাউজারে থাকাকালীন বাইনারি ডেটা ব্লব ব্যবহার করার জন্য আমাদের WebAssembly মডিউল তৈরি করতে পারি এবং কমান্ড লাইন থেকে পরীক্ষা করার জন্য কোড তৈরি করার সময় বাস্তব ফাইলগুলিতে ইন্টারফেস তৈরি করতে পারি। আমাদের পরীক্ষার জোতা কোড উদাহরণ উত্স ফাইল test.c
পাওয়া যাবে.
একাধিক ভিডিও ফ্রেমের জন্য একটি বাফারিং প্রক্রিয়া প্রয়োগ করা
ভিডিও ব্যাক প্লে করার সময়, মসৃণ প্লেব্যাকে সাহায্য করার জন্য কয়েকটি ফ্রেম বাফার করা সাধারণ অভ্যাস। আমাদের উদ্দেশ্যে আমরা শুধুমাত্র 10 ফ্রেমের ভিডিওর একটি বাফার প্রয়োগ করব, তাই আমরা প্লেব্যাক শুরু করার আগে 10টি ফ্রেম বাফার করব৷ তারপর প্রতিবার একটি ফ্রেম প্রদর্শিত হলে, আমরা অন্য ফ্রেমটি ডিকোড করার চেষ্টা করব যাতে আমরা বাফারটি পূর্ণ রাখি। এই পদ্ধতিটি নিশ্চিত করে যে ভিডিও তোতলানো বন্ধ করতে সাহায্য করার জন্য ফ্রেমগুলি আগেই উপলব্ধ রয়েছে৷
আমাদের সাধারণ উদাহরণের সাহায্যে, সম্পূর্ণ সংকুচিত ভিডিওটি পড়ার জন্য উপলব্ধ, তাই বাফারিংয়ের সত্যিই প্রয়োজন নেই। যাইহোক, যদি আমরা একটি সার্ভার থেকে স্ট্রিমিং ইনপুট সমর্থন করার জন্য সোর্স ডেটা ইন্টারফেস প্রসারিত করতে চাই, তাহলে আমাদের বাফারিং মেকানিজম থাকতে হবে।
AV1 লাইব্রেরি থেকে ভিডিও ডেটার ফ্রেম পড়ার জন্য এবং বাফারে এইভাবে সংরক্ষণ করার জন্য decode-av1.c
এর কোড:
void
AVX_Decoder_run(AVX_Decoder *ad) {
...
// Try to decode an image from the compressed stream, and buffer
while (ad->ad_NumBuffered < NUM_FRAMES_BUFFERED) {
ad->ad_Image = aom_codec_get_frame(&ad->ad_Codec,
&ad->ad_Iterator);
if (ad->ad_Image == NULL) {
break;
}
else {
buffer_frame(ad);
}
}
আমরা বাফারে 10টি ফ্রেম ভিডিও ধারণ করার জন্য বেছে নিয়েছি, যা শুধুমাত্র একটি নির্বিচারে পছন্দ। আরও ফ্রেম বাফার করার অর্থ হল ভিডিও প্লেব্যাক শুরু করার জন্য আরও অপেক্ষার সময়, যখন খুব কম ফ্রেম বাফার করার ফলে প্লেব্যাকের সময় স্টল হতে পারে৷ একটি নেটিভ ব্রাউজার বাস্তবায়নে, ফ্রেমের বাফারিং এই বাস্তবায়নের চেয়ে অনেক বেশি জটিল।
WebGL এর মাধ্যমে পৃষ্ঠায় ভিডিও ফ্রেম পাওয়া যাচ্ছে
আমরা বাফার করা ভিডিওর ফ্রেমগুলি আমাদের পৃষ্ঠায় প্রদর্শন করা প্রয়োজন৷ যেহেতু এটি গতিশীল ভিডিও বিষয়বস্তু, আমরা যত তাড়াতাড়ি সম্ভব এটি করতে সক্ষম হতে চাই। এর জন্য, আমরা WebGL- এ ফিরে যাই।
WebGL আমাদের একটি ছবি নিতে দেয়, যেমন ভিডিওর একটি ফ্রেম, এবং এটিকে একটি টেক্সচার হিসাবে ব্যবহার করতে দেয় যা কিছু জ্যামিতিতে আঁকা হয়। WebGL জগতে, সবকিছুই ত্রিভুজ নিয়ে গঠিত। সুতরাং, আমাদের ক্ষেত্রে আমরা WebGL-এর একটি সুবিধাজনক অন্তর্নির্মিত বৈশিষ্ট্য ব্যবহার করতে পারি, যার নাম gl.TRIANGLE_FAN।
যাইহোক, একটি ছোট সমস্যা আছে. WebGL টেক্সচার RGB ছবি, প্রতি রঙ চ্যানেলে এক বাইট। আমাদের AV1 ডিকোডার থেকে আউটপুট হল একটি তথাকথিত YUV ফর্ম্যাটে ছবি, যেখানে ডিফল্ট আউটপুট প্রতি চ্যানেলে 16 বিট থাকে, এবং প্রতিটি U বা V মান প্রকৃত আউটপুট চিত্রের 4 পিক্সেলের সাথে মিলে যায়। এর অর্থ হল আমরা ওয়েবজিএল-এ প্রদর্শনের জন্য পাস করার আগে ছবিটিকে রঙিন রূপান্তর করতে হবে।
এটি করার জন্য, আমরা একটি ফাংশন AVX_YUV_to_RGB()
প্রয়োগ করি যা আপনি yuv-to-rgb.c
সোর্স ফাইলে খুঁজে পেতে পারেন। এই ফাংশনটি AV1 ডিকোডার থেকে আউটপুটকে এমন কিছুতে রূপান্তর করে যা আমরা WebGL-এ পাস করতে পারি। দ্রষ্টব্য, যখন আমরা জাভাস্ক্রিপ্ট থেকে এই ফাংশনটিকে কল করি তখন আমাদের নিশ্চিত করতে হবে যে আমরা যে মেমরিতে রূপান্তরিত চিত্রটি লিখছি সেটি WebAssembly মডিউলের মেমরির মধ্যে বরাদ্দ করা হয়েছে - অন্যথায় এটি এটিতে অ্যাক্সেস পেতে পারে না। WebAssembly মডিউল থেকে একটি ইমেজ বের করে পর্দায় পেইন্ট করার ফাংশন হল:
function show_frame(af) {
if (rgb_image != 0) {
// Convert The 16-bit YUV to 8-bit RGB
let buf = Module._AVX_Video_Frame_get_buffer(af);
Module._AVX_YUV_to_RGB(rgb_image, buf, WIDTH, HEIGHT);
// Paint the image onto the canvas
drawImageToCanvas(new Uint8Array(Module.HEAPU8.buffer,
rgb_image, 3 * WIDTH * HEIGHT), WIDTH, HEIGHT);
}
}
drawImageToCanvas()
ফাংশন যা WebGL পেইন্টিং প্রয়োগ করে তা রেফারেন্সের জন্য উৎস ফাইল draw-image.js
এ পাওয়া যাবে।
ভবিষ্যত কাজ এবং takeaways
দুটি পরীক্ষামূলক ভিডিও ফাইলে (24 fps ভিডিও হিসাবে রেকর্ড করা) আমাদের ডেমো চেষ্টা করা আমাদের কয়েকটি জিনিস শেখায়:
- WebAssembly ব্যবহার করে ব্রাউজারে কার্যকরীভাবে চালানোর জন্য একটি জটিল কোড-বেস তৈরি করা সম্পূর্ণরূপে সম্ভব; এবং
- WebAssembly-এর মাধ্যমে উন্নত ভিডিও ডিকোডিংয়ের মতো CPU-এর মতো নিবিড় কিছু করা সম্ভব।
যদিও কিছু সীমাবদ্ধতা আছে: বাস্তবায়ন সবই প্রধান থ্রেডে চলছে এবং আমরা সেই একক থ্রেডে পেইন্টিং এবং ভিডিও ডিকোডিং আন্তঃলিভ করি। একটি ওয়েব কর্মীতে ডিকোডিং অফলোড করা আমাদের মসৃণ প্লেব্যাক প্রদান করতে পারে, কারণ ফ্রেমগুলি ডিকোড করার সময়টি সেই ফ্রেমের বিষয়বস্তুর উপর অত্যন্ত নির্ভরশীল এবং কখনও কখনও আমাদের বাজেটের চেয়ে বেশি সময় নিতে পারে৷
WebAssembly-এ সংকলন একটি জেনেরিক CPU প্রকারের জন্য AV1 কনফিগারেশন ব্যবহার করে। যদি আমরা একটি জেনেরিক সিপিইউ-এর জন্য কমান্ড লাইনে স্থানীয়ভাবে কম্পাইল করি আমরা WebAssembly সংস্করণের মতো ভিডিও ডিকোড করার জন্য একই রকম CPU লোড দেখতে পাই, তবে AV1 ডিকোডার লাইব্রেরিতে SIMD বাস্তবায়ন অন্তর্ভুক্ত রয়েছে যা 5 গুণ দ্রুত চলে। WebAssembly কমিউনিটি গ্রুপ বর্তমানে SIMD প্রিমিটিভস অন্তর্ভুক্ত করার জন্য স্ট্যান্ডার্ড প্রসারিত করার জন্য কাজ করছে, এবং যখন এটি আসে তখন এটি যথেষ্ট পরিমাণে ডিকোডিং গতি বাড়ানোর প্রতিশ্রুতি দেয়। যখন এটি ঘটবে, তখন একটি WebAssembly ভিডিও ডিকোডার থেকে রিয়েল-টাইমে 4k HD ভিডিও ডিকোড করা সম্পূর্ণরূপে সম্ভব হবে৷
যাই হোক না কেন, উদাহরণ কোডটি একটি নির্দেশিকা হিসাবে কার্যকর যেকোন বিদ্যমান কমান্ড লাইন ইউটিলিটিকে একটি WebAssembly মডিউল হিসাবে চালানোর জন্য পোর্ট করতে সাহায্য করে এবং দেখায় যে আজকে ওয়েবে কী সম্ভব।
ক্রেডিট
মূল্যবান পর্যালোচনা এবং প্রতিক্রিয়া প্রদানের জন্য জেফ পসনিক, এরিক বিডেলম্যান এবং টমাস স্টেইনারকে ধন্যবাদ।
,WebAssembly আমাদের নতুন বৈশিষ্ট্য সহ ব্রাউজার প্রসারিত করতে দেয়। এই নিবন্ধটি দেখায় কিভাবে AV1 ভিডিও ডিকোডার পোর্ট করতে হয় এবং যেকোনো আধুনিক ব্রাউজারে AV1 ভিডিও চালাতে হয়।
WebAssembly সম্পর্কে সেরা জিনিসগুলির মধ্যে একটি হল নতুন ক্ষমতার সাথে সক্ষমতা পরীক্ষা করা এবং ব্রাউজারটি সেই বৈশিষ্ট্যগুলি স্থানীয়ভাবে পাঠানোর আগে নতুন ধারণাগুলি বাস্তবায়ন করা (যদি থাকে)। আপনি এইভাবে WebAssembly ব্যবহার করার কথা ভাবতে পারেন একটি উচ্চ-পারফরম্যান্স পলিফিল মেকানিজম হিসেবে, যেখানে আপনি JavaScript-এর পরিবর্তে C/C++ বা Rust-এ আপনার বৈশিষ্ট্য লেখেন।
পোর্টিংয়ের জন্য উপলব্ধ বিদ্যমান কোডের আধিক্যের সাথে, ব্রাউজারে এমন কিছু করা সম্ভব যা WebAssembly না আসা পর্যন্ত কার্যকর ছিল না।
এই নিবন্ধটি কীভাবে বিদ্যমান AV1 ভিডিও কোডেক সোর্স কোড নিতে হয়, এটির জন্য একটি র্যাপার তৈরি করতে হয় এবং এটি আপনার ব্রাউজারের ভিতরে ব্যবহার করে দেখুন এবং র্যাপার ডিবাগ করার জন্য একটি পরীক্ষামূলক জোতা তৈরিতে সহায়তা করার জন্য টিপসগুলির একটি উদাহরণ দিয়ে হেঁটে যাবে৷ উদাহরণের জন্য সম্পূর্ণ সোর্স কোড এখানে রেফারেন্সের জন্য github.com/GoogleChromeLabs/wasm-av1 এ উপলব্ধ।
এই দুটি 24fps টেস্ট ভিডিও ফাইলগুলির একটি ডাউনলোড করুন এবং আমাদের তৈরি ডেমোতে চেষ্টা করুন৷
একটি আকর্ষণীয় কোড-বেস নির্বাচন করা
এখন বেশ কয়েক বছর ধরে, আমরা দেখেছি যে ওয়েবে ট্র্যাফিকের একটি বড় শতাংশ ভিডিও ডেটা নিয়ে গঠিত, সিসকো এটিকে প্রকৃতপক্ষে 80% হিসাবে অনুমান করে ! অবশ্যই, ব্রাউজার বিক্রেতা এবং ভিডিও সাইটগুলি এই সমস্ত ভিডিও সামগ্রীর দ্বারা ব্যবহৃত ডেটা হ্রাস করার ইচ্ছা সম্পর্কে ব্যাপকভাবে সচেতন। এর মূল চাবিকাঠি, অবশ্যই, আরও ভাল কম্প্রেশন, এবং আপনি যেমন আশা করছেন ইন্টারনেট জুড়ে ভিডিও শিপিংয়ের ডেটা বোঝা হ্রাস করার লক্ষ্যে পরবর্তী প্রজন্মের ভিডিও কম্প্রেশনে প্রচুর গবেষণা রয়েছে।
যেমনটি ঘটে, অ্যালায়েন্স ফর ওপেন মিডিয়া AV1 নামক একটি পরবর্তী প্রজন্মের ভিডিও কম্প্রেশন স্কিম নিয়ে কাজ করছে যা ভিডিও ডেটার আকারকে উল্লেখযোগ্যভাবে সঙ্কুচিত করার প্রতিশ্রুতি দেয়। ভবিষ্যতে, আমরা আশা করব ব্রাউজারগুলি AV1-এর জন্য নেটিভ সাপোর্ট পাঠাবে, কিন্তু সৌভাগ্যবশত কম্প্রেসার এবং ডিকম্প্রেসারের জন্য সোর্স কোড হল ওপেন সোর্স , যা এটিকে WebAssembly-এ কম্পাইল করার চেষ্টা করার জন্য একটি আদর্শ প্রার্থী করে তোলে যাতে আমরা ব্রাউজারে এটি নিয়ে পরীক্ষা করতে পারি।

ব্রাউজারে ব্যবহারের জন্য মানিয়ে নেওয়া
এই কোডটি ব্রাউজারে পাওয়ার জন্য আমাদের প্রথমে যা করতে হবে তা হল এপিআই কেমন তা বোঝার জন্য বিদ্যমান কোডটি জানা। এই কোডটি প্রথম দেখার সময়, দুটি জিনিস আলাদা হয়:
- উৎস গাছটি
cmake
নামক একটি টুল ব্যবহার করে নির্মিত হয়; এবং - এমন অনেকগুলি উদাহরণ রয়েছে যেগুলি সকলেই কিছু ধরণের ফাইল-ভিত্তিক ইন্টারফেস ধরে নেয়।
ডিফল্টরূপে তৈরি করা সমস্ত উদাহরণ কমান্ড লাইনে চালানো যেতে পারে এবং সম্প্রদায়ে উপলব্ধ অন্যান্য কোড বেসগুলিতে এটি সত্য হতে পারে। সুতরাং, ব্রাউজারে চালানোর জন্য আমরা যে ইন্টারফেসটি তৈরি করতে যাচ্ছি তা অন্য অনেক কমান্ড লাইন টুলের জন্য উপযোগী হতে পারে।
সোর্স কোড তৈরি করতে cmake
ব্যবহার করে
সৌভাগ্যবশত, AV1 লেখকরা Emscripten নিয়ে পরীক্ষা-নিরীক্ষা করছেন, SDK যা আমরা আমাদের WebAssembly সংস্করণ তৈরি করতে ব্যবহার করতে যাচ্ছি। AV1 সংগ্রহস্থলের রুটে, CMakeLists.txt
ফাইলটিতে এই বিল্ড নিয়মগুলি রয়েছে:
if(EMSCRIPTEN)
add_preproc_definition(_POSIX_SOURCE)
append_link_flag_to_target("inspect" "-s TOTAL_MEMORY=402653184")
append_link_flag_to_target("inspect" "-s MODULARIZE=1")
append_link_flag_to_target("inspect"
"-s EXPORT_NAME=\"\'DecoderModule\'\"")
append_link_flag_to_target("inspect" "--memory-init-file 0")
if("${CMAKE_BUILD_TYPE}" STREQUAL "")
# Default to -O3 when no build type is specified.
append_compiler_flag("-O3")
endif()
em_link_post_js(inspect "${AOM_ROOT}/tools/inspect-post.js")
endif()
Emscripten টুলচেইন দুটি ফরম্যাটে আউটপুট তৈরি করতে পারে, একটিকে বলা হয় asm.js
এবং অন্যটিকে WebAssembly। আমরা WebAssembly টার্গেট করব কারণ এটি ছোট আউটপুট তৈরি করে এবং দ্রুত চালাতে পারে। এই বিদ্যমান বিল্ড নিয়মগুলি একটি ইন্সপেক্টর অ্যাপ্লিকেশনে ব্যবহারের জন্য লাইব্রেরির একটি asm.js
সংস্করণ কম্পাইল করার জন্য বোঝানো হয়েছে যা একটি ভিডিও ফাইলের বিষয়বস্তু দেখার জন্য ব্যবহার করা হয়। আমাদের ব্যবহারের জন্য, আমাদের WebAssembly আউটপুট দরকার তাই আমরা উপরের নিয়মে ক্লোজিং endif()
স্টেটমেন্টের ঠিক আগে এই লাইনগুলি যোগ করি।
# Force generation of Wasm instead of asm.js
append_link_flag_to_target("inspect" "-s WASM=1")
append_compiler_flag("-s WASM=1")
cmake
দিয়ে বিল্ডিং করার অর্থ হল প্রথমে cmake
নিজে চালানোর মাধ্যমে কিছু Makefiles
তৈরি করা, তারপর make
কমান্ডটি চালানো যা কম্পাইলেশন ধাপটি সম্পাদন করবে। উল্লেখ্য, যেহেতু আমরা Emscripten ব্যবহার করছি আমাদেরকে ডিফল্ট হোস্ট কম্পাইলারের পরিবর্তে Emscripten কম্পাইলার টুলচেন ব্যবহার করতে হবে। এটি Emscripten.cmake
ব্যবহার করে অর্জন করা হয়েছে যা Emscripten SDK- এর অংশ এবং এটির পাথকে একটি প্যারামিটার হিসাবে নিজে cmake
জন্য পাস করে। নীচের কমান্ড লাইনটি আমরা মেকফাইলস তৈরি করতে ব্যবহার করি:
cmake path/to/aom \
-DENABLE_CCACHE=1 -DAOM_TARGET_CPU=generic -DENABLE_DOCS=0 \
-DCONFIG_ACCOUNTING=1 -DCONFIG_INSPECTION=1 -DCONFIG_MULTITHREAD=0 \
-DCONFIG_RUNTIME_CPU_DETECT=0 -DCONFIG_UNIT_TESTS=0
-DCONFIG_WEBM_IO=0 \
-DCMAKE_TOOLCHAIN_FILE=path/to/emsdk-portable/.../Emscripten.cmake
AV1 লাইব্রেরি সোর্স ফাইলের অবস্থানের সম্পূর্ণ পাথে প্যারামিটার path/to/aom
সেট করা উচিত। path/to/emsdk-portable/…/Emscripten.cmake
প্যারামিটারটিকে Emscripten.cmake টুলচেন বর্ণনা ফাইলের পাথে সেট করতে হবে।
সুবিধার জন্য আমরা সেই ফাইলটি সনাক্ত করতে একটি শেল স্ক্রিপ্ট ব্যবহার করি:
#!/bin/sh
EMCC_LOC=`which emcc`
EMSDK_LOC=`echo $EMCC_LOC | sed 's?/emscripten/[0-9.]*/emcc??'`
EMCMAKE_LOC=`find $EMSDK_LOC -name Emscripten.cmake -print`
echo $EMCMAKE_LOC
আপনি যদি এই প্রকল্পের জন্য শীর্ষ-স্তরের Makefile
দেখেন, তাহলে আপনি দেখতে পারবেন কিভাবে সেই স্ক্রিপ্টটি বিল্ড কনফিগার করতে ব্যবহৃত হয়।
এখন যেহেতু সমস্ত সেটআপ সম্পন্ন হয়েছে, আমরা কেবল make
কল করি যা নমুনা সহ সমগ্র উৎস গাছ তৈরি করবে, তবে সবচেয়ে গুরুত্বপূর্ণভাবে libaom.a
তৈরি করবে যাতে ভিডিও ডিকোডার সংকলিত হয় এবং আমাদের প্রকল্পে অন্তর্ভুক্ত করার জন্য প্রস্তুত থাকে।
লাইব্রেরিতে ইন্টারফেস করার জন্য একটি API ডিজাইন করা
একবার আমরা আমাদের লাইব্রেরি তৈরি করে ফেললে, এটিতে সংকুচিত ভিডিও ডেটা পাঠানোর জন্য কীভাবে এটির সাথে ইন্টারফেস করা যায় এবং তারপরে আমরা ব্রাউজারে প্রদর্শন করতে পারি এমন ভিডিওর ফ্রেমগুলি পড়তে পারি।
AV1 কোড ট্রির ভিতরে একবার নজর দিলে, একটি ভাল সূচনা পয়েন্ট হল একটি উদাহরণ ভিডিও ডিকোডার যা [simple_decoder.c](https://aomedia.googlesource.com/aom/+/master/examples/simple_decoder.c)
ফাইলে পাওয়া যাবে। সেই ডিকোডারটি একটি IVF ফাইলে পড়ে এবং ভিডিওর ফ্রেমগুলিকে প্রতিনিধিত্ব করে এমন চিত্রগুলির একটি সিরিজে এটিকে ডিকোড করে৷
আমরা সোর্স ফাইল [decode-av1.c](https://github.com/GoogleChromeLabs/wasm-av1/blob/master/decode-av1.c)
এ আমাদের ইন্টারফেস প্রয়োগ করি।
যেহেতু আমাদের ব্রাউজার ফাইল সিস্টেম থেকে ফাইলগুলি পড়তে পারে না, তাই আমাদের এমন কিছু ইন্টারফেস ডিজাইন করতে হবে যা আমাদের I/Oকে বিমূর্ত করতে দেয় যাতে আমরা আমাদের AV1 লাইব্রেরিতে ডেটা পেতে উদাহরণ ডিকোডারের মতো কিছু তৈরি করতে পারি।
কমান্ড লাইনে, ফাইল I/O হল যা একটি স্ট্রিম ইন্টারফেস হিসাবে পরিচিত, তাই আমরা কেবলমাত্র আমাদের নিজস্ব ইন্টারফেসটি সংজ্ঞায়িত করতে পারি যা স্ট্রিম I/O এর মতো দেখায় এবং অন্তর্নিহিত বাস্তবায়নে আমরা যা পছন্দ করি তা তৈরি করতে পারি।
আমরা আমাদের ইন্টারফেসকে এভাবে সংজ্ঞায়িত করি:
DATA_Source *DS_open(const char *what);
size_t DS_read(DATA_Source *ds,
unsigned char *buf, size_t bytes);
int DS_empty(DATA_Source *ds);
void DS_close(DATA_Source *ds);
// Helper function for blob support
void DS_set_blob(DATA_Source *ds, void *buf, size_t len);
open/read/empty/close
ফাংশনগুলি দেখতে অনেকটা সাধারণ ফাইল I/O অপারেশনের মতো যা আমাদেরকে কমান্ড লাইন অ্যাপ্লিকেশনের জন্য ফাইল I/O-এ সহজে ম্যাপ করতে দেয় বা ব্রাউজারের ভিতরে চালানোর সময় অন্য কোনো উপায়ে প্রয়োগ করতে দেয়। DATA_Source
প্রকারটি জাভাস্ক্রিপ্টের দিক থেকে অস্বচ্ছ, এবং শুধুমাত্র ইন্টারফেসকে এনক্যাপসুলেট করতে কাজ করে। দ্রষ্টব্য, যে একটি API তৈরি করা যা ফাইলের শব্দার্থবিদ্যাকে ঘনিষ্ঠভাবে অনুসরণ করে তা অন্য অনেক কোড-বেসগুলিতে পুনঃব্যবহার করা সহজ করে যা একটি কমান্ড লাইন (যেমন diff, sed, ইত্যাদি) থেকে ব্যবহার করার উদ্দেশ্যে করা হয়।
আমাদের DS_set_blob
নামক একটি সহায়ক ফাংশনও সংজ্ঞায়িত করতে হবে যা আমাদের স্ট্রিম I/O ফাংশনগুলিতে কাঁচা বাইনারি ডেটা আবদ্ধ করে। এটি ব্লবটিকে 'পড়ুন' হতে দেয় যেন এটি কোনও স্ট্রিম (অর্থাত্ ক্রমানুসারে পড়ার ফাইলের মতো দেখাচ্ছে)।
আমাদের উদাহরণ বাস্তবায়ন ব্লব -এ পাস করা পড়তে সক্ষম করে যেন এটি ক্রমানুসারে পড়ার ডেটা উত্স। রেফারেন্স কোডটি blob-api.c
ফাইলটিতে পাওয়া যাবে এবং পুরো বাস্তবায়নটি কেবল এটি:
struct DATA_Source {
void *ds_Buf;
size_t ds_Len;
size_t ds_Pos;
};
DATA_Source *
DS_open(const char *what) {
DATA_Source *ds;
ds = malloc(sizeof *ds);
if (ds != NULL) {
memset(ds, 0, sizeof *ds);
}
return ds;
}
size_t
DS_read(DATA_Source *ds, unsigned char *buf, size_t bytes) {
if (DS_empty(ds) || buf == NULL) {
return 0;
}
if (bytes > (ds->ds_Len - ds->ds_Pos)) {
bytes = ds->ds_Len - ds->ds_Pos;
}
memcpy(buf, &ds->ds_Buf[ds->ds_Pos], bytes);
ds->ds_Pos += bytes;
return bytes;
}
int
DS_empty(DATA_Source *ds) {
return ds->ds_Pos >= ds->ds_Len;
}
void
DS_close(DATA_Source *ds) {
free(ds);
}
void
DS_set_blob(DATA_Source *ds, void *buf, size_t len) {
ds->ds_Buf = buf;
ds->ds_Len = len;
ds->ds_Pos = 0;
}
ব্রাউজারের বাইরে পরীক্ষার জন্য একটি পরীক্ষার জোতা তৈরি করা
সফ্টওয়্যার ইঞ্জিনিয়ারিংয়ের অন্যতম সেরা অনুশীলন হ'ল ইন্টিগ্রেশন পরীক্ষার সাথে একত্রে কোডের জন্য ইউনিট পরীক্ষা তৈরি করা।
ব্রাউজারে ওয়েবসেম্বলি দিয়ে তৈরি করার সময়, আমরা যে কোডটি নিয়ে কাজ করছি তার ইন্টারফেসের জন্য ইউনিট পরীক্ষার কিছু ফর্ম তৈরি করা বোধগম্য হয় যাতে আমরা ব্রাউজারের বাইরে ডিবাগ করতে পারি এবং আমরা যে ইন্টারফেসটি তৈরি করেছি তা পরীক্ষা করতে সক্ষম হতে পারি।
এই উদাহরণে আমরা এভি 1 লাইব্রেরির ইন্টারফেস হিসাবে একটি স্ট্রিম ভিত্তিক এপিআই অনুকরণ করছি। সুতরাং, যৌক্তিকভাবে এটি একটি পরীক্ষার জোতা তৈরি করা বোধগম্য হয় যা আমরা আমাদের এপিআইয়ের একটি সংস্করণ তৈরি করতে ব্যবহার করতে পারি যা কমান্ড লাইনে চলে এবং আমাদের DATA_Source
এপিআইয়ের নীচে আই/ও নিজেই ফাইলটি প্রয়োগ করে হুডের নীচে প্রকৃত ফাইল আই/ও করে।
আমাদের পরীক্ষার জোতাটির জন্য স্ট্রিম আই/ও কোডটি সোজা, এবং এর মতো দেখাচ্ছে:
DATA_Source *
DS_open(const char *what) {
return (DATA_Source *)fopen(what, "rb");
}
size_t
DS_read(DATA_Source *ds, unsigned char *buf, size_t bytes) {
return fread(buf, 1, bytes, (FILE *)ds);
}
int
DS_empty(DATA_Source *ds) {
return feof((FILE *)ds);
}
void
DS_close(DATA_Source *ds) {
fclose((FILE *)ds);
}
স্ট্রিম ইন্টারফেসটি বিমূর্ত করে আমরা ব্রাউজারে থাকাকালীন বাইনারি ডেটা ব্লবগুলি ব্যবহার করতে আমাদের ওয়েবসেম্বলি মডিউলটি তৈরি করতে পারি এবং যখন আমরা কমান্ড লাইন থেকে পরীক্ষা করার জন্য কোডটি তৈরি করি তখন বাস্তব ফাইলগুলিতে ইন্টারফেস করতে পারি। আমাদের পরীক্ষার জোতা কোডটি উদাহরণ উত্স ফাইল test.c
. তে পাওয়া যাবে।
একাধিক ভিডিও ফ্রেমের জন্য একটি বাফারিং প্রক্রিয়া প্রয়োগ করা
পিছনে ভিডিও বাজানোর সময়, মসৃণ প্লেব্যাকটিতে সহায়তা করার জন্য কয়েকটি ফ্রেম বাফার করা সাধারণ অনুশীলন। আমাদের উদ্দেশ্যগুলির জন্য আমরা কেবল 10 টি ফ্রেমের ভিডিওর একটি বাফার প্রয়োগ করব, তাই আমরা প্লেব্যাক শুরু করার আগে 10 টি ফ্রেম বাফার করব। তারপরে প্রতিবার কোনও ফ্রেম প্রদর্শিত হয়, আমরা অন্য ফ্রেম ডিকোড করার চেষ্টা করব যাতে আমরা বাফারটি পূর্ণ রাখি। এই পদ্ধতির বিষয়টি নিশ্চিত করে যে ভিডিওটি স্টুটারিং বন্ধ করতে সহায়তা করার জন্য ফ্রেমগুলি আগাম উপলব্ধ।
আমাদের সাধারণ উদাহরণের সাথে, সম্পূর্ণ সংকুচিত ভিডিওটি পড়ার জন্য উপলব্ধ, তাই বাফারিংয়ের প্রয়োজন নেই। তবে, যদি আমরা কোনও সার্ভার থেকে স্ট্রিমিং ইনপুট সমর্থন করার জন্য উত্স ডেটা ইন্টারফেসটি প্রসারিত করতে পারি, তবে আমাদের জায়গায় বাফারিং প্রক্রিয়াটি থাকা দরকার।
এভি 1 লাইব্রেরি থেকে ভিডিও ডেটার ফ্রেমগুলি পড়ার জন্য এবং এটি হিসাবে বাফারে সংরক্ষণের জন্য decode-av1.c
এর কোড:
void
AVX_Decoder_run(AVX_Decoder *ad) {
...
// Try to decode an image from the compressed stream, and buffer
while (ad->ad_NumBuffered < NUM_FRAMES_BUFFERED) {
ad->ad_Image = aom_codec_get_frame(&ad->ad_Codec,
&ad->ad_Iterator);
if (ad->ad_Image == NULL) {
break;
}
else {
buffer_frame(ad);
}
}
আমরা বাফারটিতে 10 টি ফ্রেম ভিডিও ধারণ করতে বেছে নিয়েছি, এটি কেবল একটি স্বেচ্ছাসেবী পছন্দ। আরও ফ্রেম বাফারিং মানে ভিডিওটি প্লেব্যাক শুরু করার জন্য আরও অপেক্ষা করার সময়, যখন খুব কম ফ্রেম বাফারিং প্লেব্যাকের সময় স্টলিংয়ের কারণ হতে পারে। নেটিভ ব্রাউজার বাস্তবায়নে ফ্রেমের বাফারিং এই বাস্তবায়নের চেয়ে অনেক জটিল।
ওয়েবজিএল সহ পৃষ্ঠায় ভিডিও ফ্রেমগুলি পাওয়া
ভিডিওর ফ্রেমগুলি যা আমরা বাফার করেছি তা আমাদের পৃষ্ঠায় প্রদর্শিত হবে। যেহেতু এটি গতিশীল ভিডিও সামগ্রী, তাই আমরা যত তাড়াতাড়ি সম্ভব এটি করতে সক্ষম হতে চাই। তার জন্য, আমরা ওয়েবজিএল -তে ফিরে যাই।
ওয়েবজিএল আমাদের একটি চিত্র যেমন ভিডিওর ফ্রেমের মতো নিতে দেয় এবং এটি এমন একটি টেক্সচার হিসাবে ব্যবহার করে যা কিছু জ্যামিতিতে আঁকা হয়। ওয়েবজিএল বিশ্বে, সমস্ত কিছু ত্রিভুজ নিয়ে গঠিত। সুতরাং, আমাদের ক্ষেত্রে আমরা gl.triangle_fan নামে পরিচিত ওয়েবজিএল এর বৈশিষ্ট্যযুক্ত একটি সুবিধাজনক অন্তর্নির্মিত ব্যবহার করতে পারি।
তবে একটি ছোটখাটো সমস্যা আছে। ওয়েবজিএল টেক্সচারগুলি আরজিবি চিত্রগুলি বলে মনে করা হয়, প্রতি রঙ চ্যানেল প্রতি একটি বাইট। আমাদের এভি 1 ডিকোডার থেকে আউটপুটটি তথাকথিত ইউইউভি ফর্ম্যাটে চিত্রগুলি, যেখানে ডিফল্ট আউটপুটটিতে চ্যানেল প্রতি 16 বিট রয়েছে এবং প্রতিটি ইউ বা ভি মানও প্রকৃত আউটপুট চিত্রটিতে 4 পিক্সেলের সাথে মিলে যায়। এর সবার অর্থ আমরা চিত্রটি প্রদর্শন করার জন্য ওয়েবজিজিএলে পাস করার আগে আমাদের রঙটি রঙ করতে হবে।
এটি করার জন্য, আমরা একটি ফাংশনটি প্রয়োগ করি AVX_YUV_to_RGB()
যা আপনি উত্স ফাইলটিতে yuv-to-rgb.c
খুঁজে পেতে পারেন। এই ফাংশনটি এভি 1 ডিকোডার থেকে আউটপুটটিকে এমন কিছুতে রূপান্তর করে যা আমরা ওয়েবজিএল করতে পারি। দ্রষ্টব্য, যে আমরা যখন জাভাস্ক্রিপ্ট থেকে এই ফাংশনটি কল করি তখন আমাদের নিশ্চিত করা দরকার যে আমরা রূপান্তরিত চিত্রটি যে মেমোরিটি লিখছি তা ওয়েবসেম্বলি মডিউলটির মেমরির ভিতরে বরাদ্দ করা হয়েছে - অন্যথায় এটি এতে অ্যাক্সেস পেতে পারে না। ওয়েবসেম্বলি মডিউল থেকে একটি চিত্র বের করার এবং এটি স্ক্রিনে আঁকার জন্য ফাংশনটি এটি:
function show_frame(af) {
if (rgb_image != 0) {
// Convert The 16-bit YUV to 8-bit RGB
let buf = Module._AVX_Video_Frame_get_buffer(af);
Module._AVX_YUV_to_RGB(rgb_image, buf, WIDTH, HEIGHT);
// Paint the image onto the canvas
drawImageToCanvas(new Uint8Array(Module.HEAPU8.buffer,
rgb_image, 3 * WIDTH * HEIGHT), WIDTH, HEIGHT);
}
}
ওয়েবজিএল পেইন্টিং প্রয়োগ করে এমন drawImageToCanvas()
ফাংশনটি রেফারেন্সের জন্য উত্স ফাইল draw-image.js
পাওয়া যাবে।
ভবিষ্যতের কাজ এবং টেকওয়েস
দুটি পরীক্ষার ভিডিও ফাইলগুলিতে আমাদের ডেমো চেষ্টা করে (24 এফপিএস ভিডিও হিসাবে রেকর্ড করা) আমাদের কয়েকটি জিনিস শেখায়:
- ওয়েবসেম্বলি ব্যবহার করে ব্রাউজারে পারফরম্যান্সে চালানোর জন্য একটি জটিল কোড-বেস তৈরি করা সম্পূর্ণরূপে সম্ভব; এবং
- উন্নত ভিডিও ডিকোডিং হিসাবে সিপিইউর মতো নিবিড় কিছু ওয়েবসেম্বলির মাধ্যমে সম্ভব।
যদিও কিছু সীমাবদ্ধতা রয়েছে: বাস্তবায়নটি সমস্ত মূল থ্রেডে চলছে এবং আমরা সেই একক থ্রেডে পেইন্টিং এবং ভিডিও ডিকোডিংকে ইন্টারলিভ করি। কোনও ওয়েব কর্মীর মধ্যে ডিকোডিংকে অফলোড করা আমাদের মসৃণ প্লেব্যাক সরবরাহ করতে পারে, কারণ ফ্রেমগুলি ডিকোড করার সময় সেই ফ্রেমের সামগ্রীর উপর অত্যন্ত নির্ভরশীল এবং কখনও কখনও আমাদের বাজেটের চেয়ে বেশি সময় নিতে পারে।
ওয়েবসেম্ব্লিউজে সংকলনটি জেনেরিক সিপিইউ ধরণের জন্য AV1 কনফিগারেশন ব্যবহার করে। যদি আমরা জেনেরিক সিপিইউর জন্য কমান্ড লাইনে স্থানীয়ভাবে সংকলন করি তবে আমরা ভিডিওটি ডিকোড করতে একই ধরণের সিপিইউ লোড দেখতে পাই ওয়েবসেম্বলি সংস্করণ হিসাবে, তবে এভি 1 ডিকোডার লাইব্রেরিতে সিমডি বাস্তবায়নও অন্তর্ভুক্ত রয়েছে যা 5 গুণ বেশি দ্রুত চলে। ওয়েবসেম্বলি কমিউনিটি গ্রুপ বর্তমানে সিমডি আদিমকে অন্তর্ভুক্ত করার জন্য মান বাড়ানোর কাজ করছে এবং যখন এটি আসে তখন এটি ডিকোডিংয়ের যথেষ্ট গতি বাড়ানোর প্রতিশ্রুতি দেয়। যখন এটি ঘটে, তখন ওয়েবসেম্বল ভিডিও ডিকোডার থেকে রিয়েল-টাইমে 4K এইচডি ভিডিওটি ডিকোড করা সম্পূর্ণরূপে সম্ভব হবে।
যাই হোক না কেন, উদাহরণ কোডটি কোনও ওয়েবসেম্বলি মডিউল হিসাবে চালানোর জন্য যে কোনও বিদ্যমান কমান্ড লাইন ইউটিলিটি পোর্ট করতে সহায়তা করার জন্য গাইড হিসাবে কার্যকর এবং ইতিমধ্যে ওয়েবে ইতিমধ্যে কী সম্ভব তা দেখায়।
ক্রেডিট
মূল্যবান পর্যালোচনা এবং প্রতিক্রিয়া সরবরাহের জন্য জেফ পজনিক, এরিক বিডলম্যান এবং টমাস স্টেইনারকে ধন্যবাদ।
,ওয়েবসেম্বলি আমাদের নতুন বৈশিষ্ট্যগুলির সাথে ব্রাউজারটি প্রসারিত করতে দেয়। এই নিবন্ধটি দেখায় যে কীভাবে এভি 1 ভিডিও ডিকোডারটি পোর্ট করতে হবে এবং যে কোনও আধুনিক ব্রাউজারে এভি 1 ভিডিও খেলতে হবে।
ওয়েবসেম্বল সম্পর্কে সেরা জিনিসগুলির মধ্যে একটি হ'ল নতুন ক্ষমতা নিয়ে দক্ষতা পরীক্ষা এবং ব্রাউজারগুলি স্থানীয়ভাবে সেই বৈশিষ্ট্যগুলি জাহাজে পাঠানোর আগে নতুন ধারণাগুলি প্রয়োগ করে (যদি আদৌ হয় তবে)। আপনি এইভাবে একটি উচ্চ-পারফরম্যান্স পলিফিল মেকানিজম হিসাবে ওয়েবসেম্বলিটি ব্যবহার করার কথা ভাবতে পারেন, যেখানে আপনি জাভাস্ক্রিপ্টের পরিবর্তে আপনার বৈশিষ্ট্যটি সি/সি ++ বা মরিচা লিখেছেন।
পোর্টিংয়ের জন্য উপলব্ধ বিদ্যমান কোডের আধিক্য সহ, ব্রাউজারে এমন জিনিসগুলি করা সম্ভব যা ওয়েবসেমিগুলি না আসা পর্যন্ত কার্যকর ছিল না।
এই নিবন্ধটি কীভাবে বিদ্যমান এভি 1 ভিডিও কোডেক সোর্স কোডটি গ্রহণ করবে, এর জন্য একটি মোড়ক তৈরি করবে এবং এটি আপনার ব্রাউজারের ভিতরে এবং র্যাপারটি ডিবাগ করার জন্য পরীক্ষার জোড় তৈরিতে সহায়তা করার জন্য টিপসের ভিতরে চেষ্টা করে দেখুন। এখানে উদাহরণের জন্য সম্পূর্ণ উত্স কোডটি github.com/googlechromelabs/wasm-av1 এ রেফারেন্সের জন্য উপলব্ধ।
এই দুটি 24fps পরীক্ষার ভিডিও ফাইলগুলির মধ্যে একটি ডাউনলোড করুন এবং সেগুলি আমাদের বিল্ট ডেমোতে ব্যবহার করে দেখুন।
একটি আকর্ষণীয় কোড-বেস নির্বাচন করা
এখন বেশ কয়েক বছর ধরে, আমরা দেখেছি যে ওয়েবের একটি বিশাল শতাংশ ট্র্যাফিক ভিডিও ডেটা নিয়ে গঠিত, সিসকো এটি আসলে 80% হিসাবে অনুমান করে ! অবশ্যই, ব্রাউজার বিক্রেতারা এবং ভিডিও সাইটগুলি এই সমস্ত ভিডিও সামগ্রী দ্বারা ব্যবহৃত ডেটা হ্রাস করার আকাঙ্ক্ষা সম্পর্কে ব্যাপক সচেতন। অবশ্যই এর মূল চাবিকাঠি আরও ভাল সংক্ষেপণ, এবং আপনি যেমন আশা করছেন যে পরবর্তী প্রজন্মের ভিডিও সংক্ষেপণ সম্পর্কে প্রচুর গবেষণা রয়েছে যার লক্ষ্য ইন্টারনেটে ভিডিও শিপিংয়ের ডেটা বোঝা হ্রাস করা।
যেমনটি ঘটে, ওপেন মিডিয়া ফর ওপেন মিডিয়া এভি 1 নামক একটি পরবর্তী প্রজন্মের ভিডিও সংক্ষেপণ প্রকল্পে কাজ করছে যা ভিডিও ডেটা আকারকে যথেষ্ট সঙ্কুচিত করার প্রতিশ্রুতি দেয়। ভবিষ্যতে, আমরা আশা করতাম যে ব্রাউজারগুলি এভি 1 এর জন্য নেটিভ সমর্থনটি প্রেরণ করবে, তবে ভাগ্যক্রমে কমপ্রেসার এবং ডিকম্প্রেসারের জন্য উত্স কোডটি ওপেন সোর্স , যা এটি ওয়েবসেম্বারে সংকলন করার চেষ্টা করার জন্য আদর্শ প্রার্থীকে তৈরি করে যাতে আমরা ব্রাউজারে এটি নিয়ে পরীক্ষা করতে পারি।

ব্রাউজারে ব্যবহারের জন্য অভিযোজিত
ব্রাউজারে এই কোডটি পেতে আমাদের প্রথম যে কাজ করতে হবে তার মধ্যে একটি হ'ল এপিআই কেমন তা বুঝতে বিদ্যমান কোডটি জানতে। এই কোডটি প্রথমে তাকানোর সময়, দুটি জিনিস দাঁড়িয়ে:
- উত্স গাছটি
cmake
নামক একটি সরঞ্জাম ব্যবহার করে নির্মিত হয়; এবং - এমন অনেকগুলি উদাহরণ রয়েছে যা সমস্ত কিছু ধরণের ফাইল-ভিত্তিক ইন্টারফেস গ্রহণ করে।
ডিফল্টরূপে নির্মিত সমস্ত উদাহরণ কমান্ড লাইনে চালিত হতে পারে এবং এটি সম্প্রদায়ের মধ্যে উপলব্ধ অন্যান্য অনেক কোড ঘাঁটিতে সত্য হতে পারে। সুতরাং, ব্রাউজারে এটি চালানোর জন্য আমরা যে ইন্টারফেসটি তৈরি করতে যাচ্ছি তা অন্যান্য অনেক কমান্ড লাইন সরঞ্জামের জন্য কার্যকর হতে পারে।
উত্স কোড তৈরি করতে cmake
ব্যবহার করে
ভাগ্যক্রমে, এভি 1 লেখকরা এমএসক্রিপ্টেন নিয়ে পরীক্ষা -নিরীক্ষা করছেন, এসডিকে আমরা আমাদের ওয়েবসাম্বলি সংস্করণটি তৈরি করতে ব্যবহার করতে যাচ্ছি। এভি 1 সংগ্রহস্থলের মূলে, CMakeLists.txt
ফাইলটিতে এই বিল্ড বিধি রয়েছে:
if(EMSCRIPTEN)
add_preproc_definition(_POSIX_SOURCE)
append_link_flag_to_target("inspect" "-s TOTAL_MEMORY=402653184")
append_link_flag_to_target("inspect" "-s MODULARIZE=1")
append_link_flag_to_target("inspect"
"-s EXPORT_NAME=\"\'DecoderModule\'\"")
append_link_flag_to_target("inspect" "--memory-init-file 0")
if("${CMAKE_BUILD_TYPE}" STREQUAL "")
# Default to -O3 when no build type is specified.
append_compiler_flag("-O3")
endif()
em_link_post_js(inspect "${AOM_ROOT}/tools/inspect-post.js")
endif()
এমস্ক্রিপ্টেন টুলচেইন দুটি ফর্ম্যাটে আউটপুট উত্পন্ন করতে পারে, একটিকে asm.js
বলা হয় এবং অন্যটি ওয়েবসেম্বলি। এটি ছোট আউটপুট উত্পাদন করে এবং আরও দ্রুত চালাতে পারে বলে আমরা ওয়েবসাম্বলিকে লক্ষ্য করব। এই বিদ্যমান বিল্ড বিধিগুলি একটি ভিডিও ফাইলের সামগ্রীটি দেখার জন্য উত্তোলন করা একটি পরিদর্শক অ্যাপ্লিকেশনটিতে ব্যবহারের জন্য লাইব্রেরির একটি asm.js
সংস্করণ সংকলন করার জন্য বোঝানো হয়েছে। আমাদের ব্যবহারের জন্য, আমাদের ওয়েবসেম্বলি আউটপুট প্রয়োজন তাই আমরা উপরের নিয়মগুলিতে সমাপ্তি endif()
বিবৃতিটির ঠিক আগে এই লাইনগুলি যুক্ত করি।
# Force generation of Wasm instead of asm.js
append_link_flag_to_target("inspect" "-s WASM=1")
append_compiler_flag("-s WASM=1")
cmake
দিয়ে বিল্ডিং মানে প্রথমে cmake
নিজেই চালিয়ে কিছু Makefiles
তৈরি করা, তারপরে কমান্ডটি চালানোর মাধ্যমে make
যা সংকলন পদক্ষেপটি সম্পাদন করবে। দ্রষ্টব্য, যেহেতু আমরা এমএসক্রিপ্টন ব্যবহার করছি আমাদের ডিফল্ট হোস্ট সংকলকটির চেয়ে এমএসক্রিপ্টন সংকলক সরঞ্জামচেইন ব্যবহার করতে হবে। এটি Emscripten.cmake
ব্যবহার করে অর্জন করা হয়েছে যা এমএসক্রিপ্টেন এসডিকে অংশ এবং এটি cmake
নিজেই প্যারামিটার হিসাবে পাস করে। নীচের কমান্ড লাইনটি হ'ল আমরা মেকফাইলগুলি তৈরি করতে যা ব্যবহার করি:
cmake path/to/aom \
-DENABLE_CCACHE=1 -DAOM_TARGET_CPU=generic -DENABLE_DOCS=0 \
-DCONFIG_ACCOUNTING=1 -DCONFIG_INSPECTION=1 -DCONFIG_MULTITHREAD=0 \
-DCONFIG_RUNTIME_CPU_DETECT=0 -DCONFIG_UNIT_TESTS=0
-DCONFIG_WEBM_IO=0 \
-DCMAKE_TOOLCHAIN_FILE=path/to/emsdk-portable/.../Emscripten.cmake
প্যারামিটার path/to/aom
এভি 1 লাইব্রেরি উত্স ফাইলগুলির অবস্থানের পুরো পথে সেট করা উচিত। path/to/emsdk-portable/…/Emscripten.cmake
প্যারামিটারটি এমএসক্রিপ্টেন.মেক টুলচেইন বিবরণ ফাইলের জন্য পাথটিতে সেট করা দরকার।
সুবিধার জন্য আমরা সেই ফাইলটি সনাক্ত করতে একটি শেল স্ক্রিপ্ট ব্যবহার করি:
#!/bin/sh
EMCC_LOC=`which emcc`
EMSDK_LOC=`echo $EMCC_LOC | sed 's?/emscripten/[0-9.]*/emcc??'`
EMCMAKE_LOC=`find $EMSDK_LOC -name Emscripten.cmake -print`
echo $EMCMAKE_LOC
আপনি যদি এই প্রকল্পের জন্য শীর্ষ-স্তরের Makefile
দেখেন তবে আপনি দেখতে পাবেন যে কীভাবে সেই স্ক্রিপ্টটি বিল্ডটি কনফিগার করতে ব্যবহৃত হয়।
এখন যেহেতু সমস্ত সেটআপ হয়ে গেছে, আমরা কেবল make
কল করি যা নমুনাগুলি সহ পুরো উত্স গাছটি তৈরি করবে, তবে সবচেয়ে গুরুত্বপূর্ণভাবে libaom.a
তৈরি করে যা ভিডিও ডিকোডার সংকলিত এবং আমাদের প্রকল্পে অন্তর্ভুক্ত করার জন্য আমাদের জন্য প্রস্তুত রয়েছে।
লাইব্রেরিতে ইন্টারফেসে একটি এপিআই ডিজাইন করা
একবার আমরা আমাদের গ্রন্থাগারটি তৈরি করার পরে, আমাদের কীভাবে এটির সাথে ইন্টারফেস করতে হবে তা নিয়ে কীভাবে ইন্টারফেস করতে হবে তা নিয়ে কাজ করতে হবে এবং তারপরে ভিডিওর ফ্রেমগুলি পড়তে হবে যা আমরা ব্রাউজারে প্রদর্শন করতে পারি।
এভি 1 কোড ট্রিটির ভিতরে একবার দেখে নেওয়া, একটি ভাল সূচনা পয়েন্ট হ'ল একটি উদাহরণ ভিডিও ডিকোডার যা ফাইলটিতে পাওয়া যায় [simple_decoder.c](https://aomedia.googlesource.com/aom/+/master/examples/simple_decoder.c)
। এই ডিকোডার একটি আইভিএফ ফাইলে পড়ে এবং এটি ভিডিওতে ফ্রেমের প্রতিনিধিত্ব করে এমন একটি সিরিজের চিত্রগুলিতে ডিকোড করে।
আমরা উত্স ফাইলটিতে আমাদের ইন্টারফেসটি প্রয়োগ করি [decode-av1.c](https://github.com/GoogleChromeLabs/wasm-av1/blob/master/decode-av1.c)
।
যেহেতু আমাদের ব্রাউজার ফাইল সিস্টেম থেকে ফাইলগুলি পড়তে পারে না, তাই আমাদের এমন কিছু ইন্টারফেস ডিজাইন করতে হবে যা আমাদের আই/ওকে বিমূর্ত করতে দেয় যাতে আমরা আমাদের এভি 1 লাইব্রেরিতে ডেটা পেতে উদাহরণ ডিকোডারের অনুরূপ কিছু তৈরি করতে পারি।
কমান্ড লাইনে, ফাইল I/O যা স্ট্রিম ইন্টারফেস হিসাবে পরিচিত, তাই আমরা কেবল আমাদের নিজস্ব ইন্টারফেসটি সংজ্ঞায়িত করতে পারি যা স্ট্রিম আই/ও এর মতো দেখায় এবং অন্তর্নিহিত বাস্তবায়নে আমরা যা পছন্দ করি তা তৈরি করতে পারি।
আমরা আমাদের ইন্টারফেসটিকে এটি হিসাবে সংজ্ঞায়িত করি:
DATA_Source *DS_open(const char *what);
size_t DS_read(DATA_Source *ds,
unsigned char *buf, size_t bytes);
int DS_empty(DATA_Source *ds);
void DS_close(DATA_Source *ds);
// Helper function for blob support
void DS_set_blob(DATA_Source *ds, void *buf, size_t len);
open/read/empty/close
ফাংশনগুলি দেখতে অনেকটা সাধারণ ফাইল আই/ও অপারেশনগুলির মতো দেখায় যা আমাদের কমান্ড লাইন অ্যাপ্লিকেশনটির জন্য সহজেই ফাইল আই/ও -তে ম্যাপ করতে দেয় বা ব্রাউজারের অভ্যন্তরে চালানোর সময় এগুলি অন্য কোনও উপায়ে প্রয়োগ করতে দেয়। DATA_Source
টাইপটি জাভাস্ক্রিপ্ট দিক থেকে অস্বচ্ছ, এবং কেবল ইন্টারফেসটি এনপ্যাপুলেট করতে কাজ করে। দ্রষ্টব্য, ফাইলের শব্দার্থবিজ্ঞানের সাথে ঘনিষ্ঠভাবে অনুসরণ করে এমন একটি এপিআই তৈরি করা কমান্ড লাইন (যেমন ডিফ, সেড ইত্যাদি) থেকে ব্যবহার করার উদ্দেশ্যে করা অন্যান্য অনেক কোড-বেসগুলিতে পুনরায় ব্যবহার করা সহজ করে তোলে।
আমাদের DS_set_blob
নামক একটি সহায়ক ফাংশনও সংজ্ঞায়িত করতে হবে যা আমাদের স্ট্রিম I/O ফাংশনগুলিতে কাঁচা বাইনারি ডেটা আবদ্ধ করে। এটি ব্লবটিকে 'পড়ুন' হতে দেয় যেন এটি কোনও স্ট্রিম (অর্থাত্ ক্রমানুসারে পড়ার ফাইলের মতো দেখাচ্ছে)।
আমাদের উদাহরণ বাস্তবায়ন ব্লব -এ পাস করা পড়তে সক্ষম করে যেন এটি ক্রমানুসারে পড়ার ডেটা উত্স। রেফারেন্স কোডটি blob-api.c
ফাইলটিতে পাওয়া যাবে এবং পুরো বাস্তবায়নটি কেবল এটি:
struct DATA_Source {
void *ds_Buf;
size_t ds_Len;
size_t ds_Pos;
};
DATA_Source *
DS_open(const char *what) {
DATA_Source *ds;
ds = malloc(sizeof *ds);
if (ds != NULL) {
memset(ds, 0, sizeof *ds);
}
return ds;
}
size_t
DS_read(DATA_Source *ds, unsigned char *buf, size_t bytes) {
if (DS_empty(ds) || buf == NULL) {
return 0;
}
if (bytes > (ds->ds_Len - ds->ds_Pos)) {
bytes = ds->ds_Len - ds->ds_Pos;
}
memcpy(buf, &ds->ds_Buf[ds->ds_Pos], bytes);
ds->ds_Pos += bytes;
return bytes;
}
int
DS_empty(DATA_Source *ds) {
return ds->ds_Pos >= ds->ds_Len;
}
void
DS_close(DATA_Source *ds) {
free(ds);
}
void
DS_set_blob(DATA_Source *ds, void *buf, size_t len) {
ds->ds_Buf = buf;
ds->ds_Len = len;
ds->ds_Pos = 0;
}
ব্রাউজারের বাইরে পরীক্ষার জন্য একটি পরীক্ষার জোতা তৈরি করা
সফ্টওয়্যার ইঞ্জিনিয়ারিংয়ের অন্যতম সেরা অনুশীলন হ'ল ইন্টিগ্রেশন পরীক্ষার সাথে একত্রে কোডের জন্য ইউনিট পরীক্ষা তৈরি করা।
ব্রাউজারে ওয়েবসেম্বলি দিয়ে তৈরি করার সময়, আমরা যে কোডটি নিয়ে কাজ করছি তার ইন্টারফেসের জন্য ইউনিট পরীক্ষার কিছু ফর্ম তৈরি করা বোধগম্য হয় যাতে আমরা ব্রাউজারের বাইরে ডিবাগ করতে পারি এবং আমরা যে ইন্টারফেসটি তৈরি করেছি তা পরীক্ষা করতে সক্ষম হতে পারি।
এই উদাহরণে আমরা এভি 1 লাইব্রেরির ইন্টারফেস হিসাবে একটি স্ট্রিম ভিত্তিক এপিআই অনুকরণ করছি। সুতরাং, যৌক্তিকভাবে এটি একটি পরীক্ষার জোতা তৈরি করা বোধগম্য হয় যা আমরা আমাদের এপিআইয়ের একটি সংস্করণ তৈরি করতে ব্যবহার করতে পারি যা কমান্ড লাইনে চলে এবং আমাদের DATA_Source
এপিআইয়ের নীচে আই/ও নিজেই ফাইলটি প্রয়োগ করে হুডের নীচে প্রকৃত ফাইল আই/ও করে।
আমাদের পরীক্ষার জোতাটির জন্য স্ট্রিম আই/ও কোডটি সোজা, এবং এর মতো দেখাচ্ছে:
DATA_Source *
DS_open(const char *what) {
return (DATA_Source *)fopen(what, "rb");
}
size_t
DS_read(DATA_Source *ds, unsigned char *buf, size_t bytes) {
return fread(buf, 1, bytes, (FILE *)ds);
}
int
DS_empty(DATA_Source *ds) {
return feof((FILE *)ds);
}
void
DS_close(DATA_Source *ds) {
fclose((FILE *)ds);
}
স্ট্রিম ইন্টারফেসটি বিমূর্ত করে আমরা ব্রাউজারে থাকাকালীন বাইনারি ডেটা ব্লবগুলি ব্যবহার করতে আমাদের ওয়েবসেম্বলি মডিউলটি তৈরি করতে পারি এবং যখন আমরা কমান্ড লাইন থেকে পরীক্ষা করার জন্য কোডটি তৈরি করি তখন বাস্তব ফাইলগুলিতে ইন্টারফেস করতে পারি। আমাদের পরীক্ষার জোতা কোডটি উদাহরণ উত্স ফাইল test.c
. তে পাওয়া যাবে।
একাধিক ভিডিও ফ্রেমের জন্য একটি বাফারিং প্রক্রিয়া প্রয়োগ করা
পিছনে ভিডিও বাজানোর সময়, মসৃণ প্লেব্যাকটিতে সহায়তা করার জন্য কয়েকটি ফ্রেম বাফার করা সাধারণ অনুশীলন। আমাদের উদ্দেশ্যগুলির জন্য আমরা কেবল 10 টি ফ্রেমের ভিডিওর একটি বাফার প্রয়োগ করব, তাই আমরা প্লেব্যাক শুরু করার আগে 10 টি ফ্রেম বাফার করব। তারপরে প্রতিবার কোনও ফ্রেম প্রদর্শিত হয়, আমরা অন্য ফ্রেম ডিকোড করার চেষ্টা করব যাতে আমরা বাফারটি পূর্ণ রাখি। এই পদ্ধতির বিষয়টি নিশ্চিত করে যে ভিডিওটি স্টুটারিং বন্ধ করতে সহায়তা করার জন্য ফ্রেমগুলি আগাম উপলব্ধ।
আমাদের সাধারণ উদাহরণের সাথে, সম্পূর্ণ সংকুচিত ভিডিওটি পড়ার জন্য উপলব্ধ, তাই বাফারিংয়ের প্রয়োজন নেই। তবে, যদি আমরা কোনও সার্ভার থেকে স্ট্রিমিং ইনপুট সমর্থন করার জন্য উত্স ডেটা ইন্টারফেসটি প্রসারিত করতে পারি, তবে আমাদের জায়গায় বাফারিং প্রক্রিয়াটি থাকা দরকার।
এভি 1 লাইব্রেরি থেকে ভিডিও ডেটার ফ্রেমগুলি পড়ার জন্য এবং এটি হিসাবে বাফারে সংরক্ষণের জন্য decode-av1.c
এর কোড:
void
AVX_Decoder_run(AVX_Decoder *ad) {
...
// Try to decode an image from the compressed stream, and buffer
while (ad->ad_NumBuffered < NUM_FRAMES_BUFFERED) {
ad->ad_Image = aom_codec_get_frame(&ad->ad_Codec,
&ad->ad_Iterator);
if (ad->ad_Image == NULL) {
break;
}
else {
buffer_frame(ad);
}
}
আমরা বাফারটিতে 10 টি ফ্রেম ভিডিও ধারণ করতে বেছে নিয়েছি, এটি কেবল একটি স্বেচ্ছাসেবী পছন্দ। আরও ফ্রেম বাফারিং মানে ভিডিওটি প্লেব্যাক শুরু করার জন্য আরও অপেক্ষা করার সময়, যখন খুব কম ফ্রেম বাফারিং প্লেব্যাকের সময় স্টলিংয়ের কারণ হতে পারে। নেটিভ ব্রাউজার বাস্তবায়নে ফ্রেমের বাফারিং এই বাস্তবায়নের চেয়ে অনেক জটিল।
ওয়েবজিএল সহ পৃষ্ঠায় ভিডিও ফ্রেমগুলি পাওয়া
ভিডিওর ফ্রেমগুলি যা আমরা বাফার করেছি তা আমাদের পৃষ্ঠায় প্রদর্শিত হবে। যেহেতু এটি গতিশীল ভিডিও সামগ্রী, তাই আমরা যত তাড়াতাড়ি সম্ভব এটি করতে সক্ষম হতে চাই। তার জন্য, আমরা ওয়েবজিএল -তে ফিরে যাই।
ওয়েবজিএল আমাদের একটি চিত্র যেমন ভিডিওর ফ্রেমের মতো নিতে দেয় এবং এটি এমন একটি টেক্সচার হিসাবে ব্যবহার করে যা কিছু জ্যামিতিতে আঁকা হয়। ওয়েবজিএল বিশ্বে, সমস্ত কিছু ত্রিভুজ নিয়ে গঠিত। সুতরাং, আমাদের ক্ষেত্রে আমরা gl.triangle_fan নামে পরিচিত ওয়েবজিএল এর বৈশিষ্ট্যযুক্ত একটি সুবিধাজনক অন্তর্নির্মিত ব্যবহার করতে পারি।
তবে একটি ছোটখাটো সমস্যা আছে। ওয়েবজিএল টেক্সচারগুলি আরজিবি চিত্রগুলি বলে মনে করা হয়, প্রতি রঙ চ্যানেল প্রতি একটি বাইট। আমাদের এভি 1 ডিকোডার থেকে আউটপুটটি তথাকথিত ইউইউভি ফর্ম্যাটে চিত্রগুলি, যেখানে ডিফল্ট আউটপুটটিতে চ্যানেল প্রতি 16 বিট রয়েছে এবং প্রতিটি ইউ বা ভি মানও প্রকৃত আউটপুট চিত্রটিতে 4 পিক্সেলের সাথে মিলে যায়। এর সবার অর্থ আমরা চিত্রটি প্রদর্শন করার জন্য ওয়েবজিজিএলে পাস করার আগে আমাদের রঙটি রঙ করতে হবে।
এটি করার জন্য, আমরা একটি ফাংশনটি প্রয়োগ করি AVX_YUV_to_RGB()
যা আপনি উত্স ফাইলটিতে yuv-to-rgb.c
খুঁজে পেতে পারেন। এই ফাংশনটি এভি 1 ডিকোডার থেকে আউটপুটটিকে এমন কিছুতে রূপান্তর করে যা আমরা ওয়েবজিএল করতে পারি। দ্রষ্টব্য, যে আমরা যখন জাভাস্ক্রিপ্ট থেকে এই ফাংশনটি কল করি তখন আমাদের নিশ্চিত করা দরকার যে আমরা রূপান্তরিত চিত্রটি যে মেমোরিটি লিখছি তা ওয়েবসেম্বলি মডিউলটির মেমরির ভিতরে বরাদ্দ করা হয়েছে - অন্যথায় এটি এতে অ্যাক্সেস পেতে পারে না। ওয়েবসেম্বলি মডিউল থেকে একটি চিত্র বের করার এবং এটি স্ক্রিনে আঁকার জন্য ফাংশনটি এটি:
function show_frame(af) {
if (rgb_image != 0) {
// Convert The 16-bit YUV to 8-bit RGB
let buf = Module._AVX_Video_Frame_get_buffer(af);
Module._AVX_YUV_to_RGB(rgb_image, buf, WIDTH, HEIGHT);
// Paint the image onto the canvas
drawImageToCanvas(new Uint8Array(Module.HEAPU8.buffer,
rgb_image, 3 * WIDTH * HEIGHT), WIDTH, HEIGHT);
}
}
ওয়েবজিএল পেইন্টিং প্রয়োগ করে এমন drawImageToCanvas()
ফাংশনটি রেফারেন্সের জন্য উত্স ফাইল draw-image.js
পাওয়া যাবে।
ভবিষ্যতের কাজ এবং টেকওয়েস
দুটি পরীক্ষার ভিডিও ফাইলগুলিতে আমাদের ডেমো চেষ্টা করে (24 এফপিএস ভিডিও হিসাবে রেকর্ড করা) আমাদের কয়েকটি জিনিস শেখায়:
- ওয়েবসেম্বলি ব্যবহার করে ব্রাউজারে পারফরম্যান্সে চালানোর জন্য একটি জটিল কোড-বেস তৈরি করা সম্পূর্ণরূপে সম্ভব; এবং
- উন্নত ভিডিও ডিকোডিং হিসাবে সিপিইউর মতো নিবিড় কিছু ওয়েবসেম্বলির মাধ্যমে সম্ভব।
যদিও কিছু সীমাবদ্ধতা রয়েছে: বাস্তবায়নটি সমস্ত মূল থ্রেডে চলছে এবং আমরা সেই একক থ্রেডে পেইন্টিং এবং ভিডিও ডিকোডিংকে ইন্টারলিভ করি। কোনও ওয়েব কর্মীর মধ্যে ডিকোডিংকে অফলোড করা আমাদের মসৃণ প্লেব্যাক সরবরাহ করতে পারে, কারণ ফ্রেমগুলি ডিকোড করার সময় সেই ফ্রেমের সামগ্রীর উপর অত্যন্ত নির্ভরশীল এবং কখনও কখনও আমাদের বাজেটের চেয়ে বেশি সময় নিতে পারে।
ওয়েবসেম্ব্লিউজে সংকলনটি জেনেরিক সিপিইউ ধরণের জন্য AV1 কনফিগারেশন ব্যবহার করে। যদি আমরা জেনেরিক সিপিইউর জন্য কমান্ড লাইনে স্থানীয়ভাবে সংকলন করি তবে আমরা ভিডিওটি ডিকোড করতে একই ধরণের সিপিইউ লোড দেখতে পাই ওয়েবসেম্বলি সংস্করণ হিসাবে, তবে এভি 1 ডিকোডার লাইব্রেরিতে সিমডি বাস্তবায়নও অন্তর্ভুক্ত রয়েছে যা 5 গুণ বেশি দ্রুত চলে। ওয়েবসেম্বলি কমিউনিটি গ্রুপ বর্তমানে সিমডি আদিমকে অন্তর্ভুক্ত করার জন্য মান বাড়ানোর কাজ করছে এবং যখন এটি আসে তখন এটি ডিকোডিংয়ের যথেষ্ট গতি বাড়ানোর প্রতিশ্রুতি দেয়। যখন এটি ঘটে, তখন ওয়েবসেম্বল ভিডিও ডিকোডার থেকে রিয়েল-টাইমে 4K এইচডি ভিডিওটি ডিকোড করা সম্পূর্ণরূপে সম্ভব হবে।
যাই হোক না কেন, উদাহরণ কোডটি কোনও ওয়েবসেম্বলি মডিউল হিসাবে চালানোর জন্য যে কোনও বিদ্যমান কমান্ড লাইন ইউটিলিটি পোর্ট করতে সহায়তা করার জন্য গাইড হিসাবে কার্যকর এবং ইতিমধ্যে ওয়েবে ইতিমধ্যে কী সম্ভব তা দেখায়।
ক্রেডিট
মূল্যবান পর্যালোচনা এবং প্রতিক্রিয়া সরবরাহের জন্য জেফ পজনিক, এরিক বিডলম্যান এবং টমাস স্টেইনারকে ধন্যবাদ।