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

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

Alex Danilo

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

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

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

ดาวน์โหลดไฟล์วิดีโอทดสอบ 24fps 2 ไฟล์ แล้วลองเล่นในการสาธิตที่เราสร้าง

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

หลายปีมานี้ เราพบว่าการเข้าชมเว็บในสัดส่วนสูงประกอบด้วยข้อมูลวิดีโอ แต่ 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

เพื่อความสะดวก เราจะใช้สคริปต์ 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 คือสิ่งที่เรียกว่าอินเทอร์เฟซสตรีม เราจึงสามารถกำหนดอินเทอร์เฟซของตัวเองให้มีลักษณะเป็น Stream 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-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

โค้ด Stream 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 เพื่อใช้ Blob ข้อมูลไบนารีในเบราว์เซอร์ และอินเทอร์เฟซกับไฟล์จริงเมื่อเราสร้างโค้ดเพื่อทดสอบจากบรรทัดคำสั่ง ดูโค้ดโปรแกรมใช้ประโยชน์จากการทดสอบได้ในไฟล์ต้นฉบับตัวอย่าง 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 หนึ่งไบต์ต่อช่องสี เอาต์พุตจากตัวถอดรหัส 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 รายการ) จะให้ความรู้ต่อไปนี้แก่เรา

  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 ที่ร่วมให้ข้อมูลรีวิวและความคิดเห็นที่มีประโยชน์