การขยายเบราว์เซอร์ด้วย 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 เป็นเส้นทางสำหรับไฟล์คำอธิบาย Toolchain ของ Emscripten.cmake

เราจะใช้สคริปต์ Shell เพื่อค้นหาไฟล์นั้นเพื่อความสะดวกดังนี้

#!/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 ในเบราว์เซอร์ ก็ควรสร้างการทดสอบหน่วยสัก 1 รูปแบบสำหรับอินเทอร์เฟซกับโค้ดที่กำลังใช้อยู่ เพื่อให้เราสามารถแก้ไขข้อบกพร่องนอกเบราว์เซอร์และยังทดสอบอินเทอร์เฟซที่เราสร้างขึ้นได้ด้วย

ในตัวอย่างนี้ เราจําลอง 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 ที่ให้รีวิวและความคิดเห็นที่มีคุณค่า