การขยายเบราว์เซอร์ด้วย WebAssembly

WebAssembly ช่วยให้เราขยายความสามารถของเบราว์เซอร์ด้วยฟีเจอร์ใหม่ๆ บทความนี้แสดงวิธีพอร์ตโปรแกรมถอดรหัสวิดีโอ AV1 และเล่นวิดีโอ AV1 ในเบราว์เซอร์สมัยใหม่

Alex Danilo

สิ่งที่ดีที่สุดอย่างหนึ่งเกี่ยวกับ WebAssembly คือความสามารถในการทดลองใช้ความสามารถใหม่ๆ และนำแนวคิดใหม่ๆ ไปใช้ก่อนที่เบราว์เซอร์จะเปิดตัวฟีเจอร์เหล่านั้นโดยค่าเริ่มต้น (หากมี) คุณอาจมองว่าการใช้ WebAssembly ในลักษณะนี้เป็นกลไกการทดแทนประสิทธิภาพสูง ซึ่งคุณเขียนฟีเจอร์เป็น C/C++ หรือ Rust แทน JavaScript

การมีโค้ดที่มีอยู่มากมายสำหรับการพอร์ตช่วยให้คุณทําสิ่งต่างๆ ในเบราว์เซอร์ได้ ซึ่งก่อนหน้านี้ไม่สามารถทำได้จนกว่าจะมี WebAssembly

บทความนี้จะอธิบายตัวอย่างวิธีใช้ซอร์สโค้ดตัวแปลงรหัสวิดีโอ AV1 ที่มีอยู่ สร้างตัวแฝงสำหรับโค้ดดังกล่าว และลองใช้ภายในเบราว์เซอร์ รวมถึงเคล็ดลับที่จะช่วยสร้างชุดทดสอบเพื่อแก้ไขข้อบกพร่องของตัวแฝง ดูซอร์สโค้ดแบบเต็มของตัวอย่างได้ที่นี่ github.com/GoogleChromeLabs/wasm-av1

ดาวน์โหลดไฟล์วิดีโอทดสอบ 24 fps รายการใดรายการหนึ่งต่อไปนี้ แล้วลองใช้กับเดโมที่สร้างไว้

การเลือกฐานโค้ดที่น่าสนใจ

ในช่วงหลายปีที่ผ่านมา เราพบว่าการเข้าชมส่วนใหญ่บนเว็บประกอบด้วยข้อมูลวิดีโอ โดย Ciscoประมาณว่าสูงถึง 80% แน่นอนว่าผู้ให้บริการเบราว์เซอร์และเว็บไซต์วิดีโอต่างตระหนักดีถึงความต้องการในการลดปริมาณข้อมูลที่ใช้ในการรับชมเนื้อหาวิดีโอทั้งหมดนี้ แน่นอนว่าหัวใจสำคัญคือการบีบอัดที่ดียิ่งขึ้น และอย่างที่คาดไว้ มีการวิจัยมากมายเกี่ยวกับการบีบอัดวิดีโอรุ่นถัดไปที่มีจุดมุ่งหมายเพื่อลดภาระของการส่งข้อมูลวิดีโอผ่านอินเทอร์เน็ต

บังเอิญว่า Alliance for Open Media กำลังพัฒนารูปแบบการบีบอัดวิดีโอรุ่นถัดไปที่เรียกว่า AV1 ซึ่งสัญญาว่าจะลดขนาดข้อมูลวิดีโอได้อย่างมาก ในอนาคต เราคาดว่าเบราว์เซอร์จะรองรับ AV1 แบบเนทีฟ แต่โชคดีที่ซอร์สโค้ดสำหรับโปรแกรมบีบอัดและโปรแกรมขยายไฟล์เป็นโอเพนซอร์ส ซึ่งเหมาะอย่างยิ่งสำหรับการพยายามคอมไพล์เป็น WebAssembly เพื่อให้เราทดสอบในเบราว์เซอร์ได้

รูปภาพภาพยนตร์กระต่ายน้อย

การปรับเปลี่ยนให้ใช้ในเบราว์เซอร์

สิ่งแรกที่เราต้องทำเพื่อนำโค้ดนี้ไปไว้ในเบราว์เซอร์คือการทำความรู้จักกับโค้ดที่มีอยู่เพื่อทําความเข้าใจว่า API เป็นอย่างไร เมื่อดูโค้ดนี้เป็นครั้งแรก สิ่งที่เห็นได้ชัดมี 2 อย่างดังนี้

  1. ต้นไม้ซอร์สโค้ดสร้างขึ้นโดยใช้เครื่องมือชื่อ cmake และ
  2. ตัวอย่างทั้งหมดจะถือว่ามีอินเทอร์เฟซแบบไฟล์

ตัวอย่างทั้งหมดที่สร้างโดยค่าเริ่มต้นจะเรียกใช้ได้ในบรรทัดคำสั่ง และก็น่าจะใช้ได้กับฐานโค้ดอื่นๆ อีกมากมายที่มีอยู่ในชุมชน ดังนั้นอินเทอร์เฟซที่เรากำลังจะสร้างเพื่อให้ทำงานในเบราว์เซอร์จึงอาจมีประโยชน์สำหรับเครื่องมือบรรทัดคำสั่งอื่นๆ อีกมากมาย

การใช้ 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 สามารถสร้างเอาต์พุตได้ 2 รูปแบบ รูปแบบหนึ่งเรียกว่า 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 หมายถึงการสร้าง Makefiles บางส่วนก่อนโดยเรียกใช้ cmake เอง จากนั้นจึงเรียกใช้คำสั่ง make ซึ่งจะดำเนินการขั้นตอนคอมไพล์ โปรดทราบว่าเนื่องจากเราใช้ Emscripten เราจึงต้องใช้ชุดเครื่องมือคอมไพเลอร์ Emscripten แทนคอมไพเลอร์โฮสต์เริ่มต้น ซึ่งทำได้โดยใช้ Emscripten.cmake ซึ่งเป็นส่วนหนึ่งของ Emscripten SDK และส่งผ่านเส้นทางเป็นพารามิเตอร์ไปยัง cmake โดยตรง บรรทัดคำสั่งด้านล่างคือสิ่งที่เราใช้สร้าง Makefile

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 เป็นเส้นทางแบบเต็มของตำแหน่งไฟล์ต้นทางไลบรารี AV1 พารามิเตอร์ 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 เป็นแบบทึบจากฝั่ง JavaScript และมีหน้าที่เพียงรวมอินเทอร์เฟซ โปรดทราบว่าการสร้าง API ที่เป็นไปตามความหมายของไฟล์อย่างใกล้ชิดจะช่วยให้นํากลับมาใช้ใหม่ในฐานโค้ดอื่นๆ อีกมากมายที่มีไว้เพื่อใช้งานจากบรรทัดคําสั่งได้ง่ายๆ (เช่น diff, sed ฯลฯ)

นอกจากนี้ เรายังต้องกำหนดฟังก์ชันตัวช่วยชื่อ DS_set_blob ซึ่งจะเชื่อมโยงข้อมูลไบนารีดิบกับฟังก์ชัน I/O สตรีม ซึ่งจะช่วยให้สามารถ "อ่าน" Blob ได้ราวกับเป็นสตรีม (กล่าวคือ มีลักษณะเหมือนไฟล์ที่อ่านตามลำดับ)

ตัวอย่างการใช้งานของเราช่วยให้อ่าน Blob ที่ส่งมาราวกับว่าเป็นแหล่งข้อมูลที่อ่านตามลําดับ รหัสอ้างอิงอยู่ในไฟล์ 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 หน่วยสําหรับโค้ดควบคู่กับการทดสอบการผสานรวม

เมื่อสร้างด้วย WebAssembly ในเบราว์เซอร์ คุณควรสร้างการทดสอบหน่วยรูปแบบใดรูปแบบหนึ่งสำหรับอินเทอร์เฟซกับโค้ดที่เรากำลังใช้งานเพื่อให้แก้ไขข้อบกพร่องนอกเบราว์เซอร์ได้ และทดสอบอินเทอร์เฟซที่เราสร้างขึ้นได้ด้วย

ในตัวอย่างนี้ เราจําลอง API ที่อิงตามสตรีมเป็นอินเทอร์เฟซกับคลัง AV1 ดังนั้น ตรรกะจึงบอกให้เราสร้างชุดทดสอบที่เราสามารถใช้เพื่อสร้าง API เวอร์ชันที่ทำงานบนบรรทัดคำสั่งและทำการ I/O ไฟล์จริงภายใต้การทำงานด้วยการใช้ I/O ไฟล์เองภายใต้DATA_Source API

โค้ด 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

การใช้กลไกการบัฟเฟอร์สำหรับเฟรมวิดีโอหลายเฟรม

เมื่อเล่นวิดีโอ โดยทั่วไประบบจะบัฟเฟอร์วิดีโอ 2-3 เฟรมเพื่อช่วยในการเล่นที่ราบรื่นยิ่งขึ้น เราจะใช้บัฟเฟอร์วิดีโอ 10 เฟรมเพื่อวัตถุประสงค์ของเรา ดังนั้นเราจะบัฟเฟอร์ 10 เฟรมก่อนที่จะเริ่มเล่น จากนั้นทุกครั้งที่แสดงเฟรม เราจะพยายามถอดรหัสเฟรมอื่นเพื่อให้บัฟเฟอร์เต็มอยู่เสมอ วิธีนี้ช่วยให้มั่นใจได้ว่าจะมีเฟรมพร้อมใช้งานล่วงหน้าเพื่อช่วยหยุดวิดีโอไม่ให้กระตุก

ในตัวอย่างง่ายๆ นี้ วิดีโอที่บีบอัดทั้งหมดพร้อมให้อ่านแล้ว จึงไม่จำเป็นต้องมีการบัฟเฟอร์ อย่างไรก็ตาม หากต้องการขยายอินเทอร์เฟซข้อมูลต้นทางเพื่อรองรับอินพุตสตรีมมิงจากเซิร์ฟเวอร์ เราจะต้องมีกลไกการบัฟเฟอร์

โค้ดใน decode-av1.c สําหรับการอ่านเฟรมของข้อมูลวิดีโอจากคลัง AV1 และจัดเก็บไว้ในบัฟเฟอร์มีดังนี้

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 1 ไบต์ต่อช่องสี เอาต์พุตจากโปรแกรมถอดรหัส AV1 คือรูปภาพในรูปแบบที่เรียกว่า YUV ซึ่งเอาต์พุตเริ่มต้นมี 16 บิตต่อช่อง และค่า U หรือ V แต่ละค่าจะสอดคล้องกับ 4 พิกเซลในรูปภาพเอาต์พุตจริง ซึ่งหมายความว่าเราต้องแปลงสีรูปภาพก่อนจึงจะส่งไปยัง WebGL เพื่อแสดงผลได้

โดยเราใช้ฟังก์ชัน AVX_YUV_to_RGB() ซึ่งคุณจะพบได้ในไฟล์ต้นฉบับ yuv-to-rgb.c ฟังก์ชันดังกล่าวจะแปลงเอาต์พุตจากโปรแกรมถอดรหัส AV1 ให้เป็นรูปแบบที่เราส่งไปยัง WebGL ได้ โปรดทราบว่าเมื่อเราเรียกใช้ฟังก์ชันนี้จาก JavaScript เราจำเป็นต้องตรวจสอบว่าหน่วยความจำที่เราเขียนรูปภาพที่แปลงแล้วได้รับการจัดสรรภายในหน่วยความจำของโมดูล 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 เพื่อใช้เป็นข้อมูลอ้างอิง

งานและสิ่งที่ได้จากอนาคต

การทดสอบเดโมกับไฟล์ วิดีโอทดสอบ 2 รายการ (บันทึกเป็นวิดีโอ 24 f.p.s.) ทำให้เราทราบข้อมูลบางอย่างดังนี้

  1. คุณสามารถสร้างฐานโค้ดที่ซับซ้อนให้ทำงานได้อย่างมีประสิทธิภาพในเบราว์เซอร์โดยใช้ WebAssembly ได้ และ
  2. การดำเนินการที่ต้องใช้ CPU อย่างการถอดรหัสวิดีโอขั้นสูงสามารถทำได้ผ่าน WebAssembly

แต่มีข้อจำกัดบางอย่างคือ การใช้งานทั้งหมดจะทำงานบนเธรดหลัก และเราจะสลับกันแสดงภาพวาดและการถอดรหัสวิดีโอในเธรดเดียว การย้ายงานถอดรหัสไปยัง Web Worker อาจช่วยให้การเล่นราบรื่นขึ้น เนื่องจากเวลาในการถอดรหัสเฟรมขึ้นอยู่กับเนื้อหาของเฟรมนั้นๆ เป็นอย่างมาก และบางครั้งอาจใช้เวลามากกว่าที่เราคาดไว้

การคอมไพล์เป็น WebAssembly จะใช้การกำหนดค่า AV1 สำหรับ CPU ประเภททั่วไป หากคอมไพล์แบบเนทีฟในบรรทัดคำสั่งสำหรับ CPU ทั่วไป เราจะเห็นภาระของ CPU ที่คล้ายกันในการถอดรหัสวิดีโอกับเวอร์ชัน WebAssembly อย่างไรก็ตาม ไลบรารีโปรแกรมถอดรหัส AV1 ยังมีการใช้งาน SIMD ที่ทำงานได้เร็วขึ้นถึง 5 เท่า ปัจจุบันกลุ่มชุมชน WebAssembly กำลังดำเนินการขยายมาตรฐานให้รวมพรอมิเตอ SIMD และเมื่อดำเนินการเสร็จแล้ว คาดว่าจะช่วยเพิ่มความเร็วในการถอดรหัสได้อย่างมาก เมื่อถึงเวลานั้น คุณจะถอดรหัสวิดีโอความละเอียด 4K HD ได้แบบเรียลไทม์จากโปรแกรมถอดรหัสวิดีโอ WebAssembly

ไม่ว่าในกรณีใด ตัวอย่างโค้ดจะมีประโยชน์เป็นแนวทางในการพอร์ตยูทิลิตีบรรทัดคำสั่งที่มีอยู่ให้ทำงานเป็นโมดูล WebAssembly และแสดงสิ่งที่เป็นไปได้บนเว็บในปัจจุบัน

เครดิต

ขอขอบคุณ Jeff Posnick, Eric Bidelman และ Thomas Steiner ที่ให้รีวิวและความคิดเห็นที่มีคุณค่า