Skip to content

Commit fe820bc

Browse files
committed
feature: 支持WebAssembly
1 parent 0119391 commit fe820bc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1024
-34
lines changed

.github/workflows/build.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ jobs:
7676
build_option: BUILD_RUST_BINDING
7777
- lang: cangjie
7878
build_option: BUILD_CANGJIE_BINDING
79+
- lang: wasm
80+
build_option: BUILD_WASM_BINDING
7981
fail-fast: false
8082
steps:
8183
- name: Checkout
@@ -107,13 +109,22 @@ jobs:
107109
channel: lts
108110
version: auto
109111

112+
- name: Setup emsdk
113+
if: matrix.lang == 'wasm'
114+
uses: mymindstorm/setup-emsdk@v14
115+
110116
- name: Build ${{ matrix.lang }} binding
111117
run: |
112118
mkdir -p install
113-
cmake -B build -G Ninja -DCMAKE_INSTALL_PREFIX=install -DHTTPLIB_USE_ZSTD_IF_AVAILABLE=OFF -DBUILD_DEMO=OFF -DBUILD_TEST=OFF -D${{ matrix.build_option }}=ON
119+
BUILD_DEMO=OFF
120+
if [ "${{ matrix.lang }}" = "wasm" ]; then
121+
BUILD_DEMO=ON
122+
fi
123+
cmake -B build -G Ninja -DCMAKE_INSTALL_PREFIX=install -DHTTPLIB_USE_ZSTD_IF_AVAILABLE=OFF -DBUILD_DEMO=${BUILD_DEMO} -DBUILD_TEST=OFF -D${{ matrix.build_option }}=ON
114124
cmake --build build --target ${{ matrix.lang }}-package --parallel $(nproc)
115125
116126
- name: Test ${{ matrix.lang }} binding
127+
if: matrix.lang != 'wasm'
117128
run: |
118129
wget -q https://github.com/bluenviron/mediamtx/releases/download/v1.14.0/mediamtx_v1.14.0_linux_amd64.tar.gz
119130
tar -xzf mediamtx_v1.14.0_linux_amd64.tar.gz

CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ else()
3434
set(BUILD_MACOS_FRAMEWORK ON CACHE BOOL "Build library as Framework on macOS")
3535
set(BUILD_MACOS_BUNDLE ON CACHE BOOL "Build executable as App Bundle on macOS")
3636
endif()
37+
elseif(EMSCRIPTEN)
38+
add_compile_options("-pthread")
39+
add_link_options(
40+
"-sALLOW_MEMORY_GROWTH=1" "-pthread" "-sMALLOC=mimalloc"
41+
"-sFETCH" "-sFETCH_STREAMING" "-lwebsocket.js" "-sPROXY_POSIX_SOCKETS"
42+
)
3743
endif()
3844
endif()
3945

@@ -51,6 +57,8 @@ elseif(APPLE)
5157
if(BUILD_MACOS_BUNDLE)
5258
set(CMAKE_INSTALL_BINDIR "." CACHE PATH "")
5359
endif()
60+
elseif(EMSCRIPTEN)
61+
set(CMAKE_INSTALL_BINDIR "dist" CACHE PATH "")
5462
endif()
5563
include(GNUInstallDirs)
5664
foreach(dir LIB BIN INCLUDE)

CoralReefCam/CMakeLists.txt

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ if(WIN32)
99
LIBRARY DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}
1010
RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}
1111
)
12+
elseif(EMSCRIPTEN)
13+
set(SDL2_DIR "${EMSCRIPTEN_ROOT_PATH}/tools/ports/sdl2" CACHE PATH "")
14+
find_package(SDL2 REQUIRED)
15+
set(EMRUN "${EMSCRIPTEN_ROOT_PATH}/emrun${EMCC_SUFFIX}")
1216
else()
1317
find_package(SDL2 2.0.18 REQUIRED)
1418
endif()
@@ -54,6 +58,14 @@ target_compile_definitions(${PROJECT_NAME}
5458
ENABLE_OPENCV=$<BOOL:${ENABLE_OPENCV}>
5559
)
5660

61+
if(EMSCRIPTEN)
62+
target_link_options(${PROJECT_NAME}
63+
PRIVATE
64+
"-sPTHREAD_POOL_SIZE=3"
65+
"-sASYNCIFY=1"
66+
)
67+
endif()
68+
5769
target_link_libraries(${PROJECT_NAME}
5870
PRIVATE
5971
CoralReefPlayer
@@ -82,8 +94,26 @@ if(APPLE AND BUILD_MACOS_BUNDLE)
8294
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist
8395
RESOURCE ${CMAKE_CURRENT_SOURCE_DIR}/icon.icns
8496
)
97+
elseif(EMSCRIPTEN)
98+
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "index")
99+
set(CMAKE_EXECUTABLE_SUFFIX ".html")
100+
101+
add_custom_target(preview
102+
COMMAND ${EMRUN} index.html
103+
DEPENDS ${PROJECT_NAME}
104+
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
105+
)
85106
endif()
86107

87-
install(TARGETS ${PROJECT_NAME}
88-
DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}
89-
)
108+
if (NOT EMSCRIPTEN)
109+
install(TARGETS ${PROJECT_NAME}
110+
DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}
111+
)
112+
else()
113+
install(FILES
114+
${CMAKE_CURRENT_BINARY_DIR}/index.html
115+
${CMAKE_CURRENT_BINARY_DIR}/index.js
116+
${CMAKE_CURRENT_BINARY_DIR}/index.wasm
117+
DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}
118+
)
119+
endif()

CoralReefCam/main.cpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
#include <string>
2+
#include <functional>
23
#include <stdio.h>
4+
#ifdef __EMSCRIPTEN__
5+
#include <emscripten.h>
6+
#include <emscripten/html5.h>
7+
#include <emscripten/threading.h>
8+
#include <emscripten/websocket.h>
9+
#include <emscripten/posix_socket.h>
10+
#endif
311
#include "SDL.h"
412
#include "imgui.h"
513
#include "imgui_stdlib.h"
@@ -23,6 +31,12 @@ crp_handle player;
2331
bool playing;
2432
uint64_t pts;
2533

34+
#ifdef __EMSCRIPTEN__
35+
static std::function<void()> EmscriptenMainLoopFunc;
36+
static void EmscriptenMainLoop() { EmscriptenMainLoopFunc(); }
37+
static EMSCRIPTEN_WEBSOCKET_T bridgeSocket = 0;
38+
#endif
39+
2640
SDL_PixelFormatEnum GetPixelFormat(Format format)
2741
{
2842
switch (format)
@@ -449,6 +463,11 @@ int main(int argc, char* argv[])
449463
height = 1080;
450464
}
451465

466+
#ifdef __EMSCRIPTEN__
467+
bridgeSocket = emscripten_init_websocket_to_posix_socket_bridge("ws://localhost:80");
468+
uint16_t readyState = 0;
469+
#endif
470+
452471
if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER))
453472
{
454473
printf("Could not initialize SDL: %s\n", SDL_GetError());
@@ -498,11 +517,11 @@ int main(int argc, char* argv[])
498517
ImGuiIO& io = ImGui::GetIO();
499518
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
500519
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
520+
ImGui::StyleColorsDark();
501521
ImFontConfig font_config;
502522
font_config.SizePixels = 16.0f;
503523
io.Fonts->AddFontDefault(&font_config);
504524
// io.Fonts->AddFontFromFileTTF("./unifont-15.0.06.ttf", 16.0f, nullptr, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
505-
ImGui::StyleColorsDark();
506525
ImGui_ImplSDL2_InitForSDLRenderer(window, renderer);
507526
ImGui_ImplSDLRenderer2_Init(renderer);
508527

@@ -513,8 +532,21 @@ int main(int argc, char* argv[])
513532
bool is_running = true;
514533
bool has_frame = false;
515534
Uint64 last_time = SDL_GetTicks64();
535+
#ifdef __EMSCRIPTEN__
536+
io.IniFilename = nullptr;
537+
EmscriptenMainLoopFunc = [&]() { do
538+
#else
516539
while (is_running)
540+
#endif
517541
{
542+
#ifdef __EMSCRIPTEN__
543+
if (readyState == 0)
544+
{
545+
emscripten_websocket_get_ready_state(bridgeSocket, &readyState);
546+
if (readyState == 0) continue;
547+
}
548+
#endif
549+
518550
bool force_update = false;
519551
SDL_Event event;
520552
while (SDL_PollEvent(&event))
@@ -585,6 +617,10 @@ int main(int argc, char* argv[])
585617
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), renderer);
586618
SDL_RenderPresent(renderer);
587619
}
620+
#ifdef __EMSCRIPTEN__
621+
while (0); };
622+
emscripten_set_main_loop(EmscriptenMainLoop, 0, true);
623+
#endif
588624

589625
crp_destroy(player);
590626
ImGui_ImplSDLRenderer2_Shutdown();

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ CoralReefPlayer 即珊瑚礁播放器,是一款使用 C++20 开发的跨平台
99
CoralReefPlayer 支持 Windows、Linux、MacOS、Android、iOS 和纯血鸿蒙等主流操作系统,并且提供 C#、Java 和 Python 等语言的 binding,方便使用各种语言和框架开发上位机。
1010

1111
与现存的各种播放器库相比,CoralReefPlayer 具有以下特点:
12+
1213
- 使用 C++20 标准开发,代码简洁、高效
1314
- 接口简单易用
1415
- 所有主流操作系统和编程语言支持
@@ -21,6 +22,8 @@ CoralReefPlayer 支持 Windows、Linux、MacOS、Android、iOS 和纯血鸿蒙
2122
2223
CoralReefCam,中文名珊瑚礁™嘻屁屁高性能版,是 CoralReefPlayer 的示例项目,集成了 SDL、imgui、OpenCV 等库,可基于此项目开发高性能机器人上位机。目前作为监控软件使用,其最初的开发目的是用于解决拉流延迟问题。
2324

25+
CoralReefCam 现已支持 WebAssembly,可直接在浏览器中播放 MJPEG over HTTP 流,请前往[示例网站](http://crp.dawncraft.cc)体验。
26+
2427
<div style="display: flex; justify-content: space-around; align-items: center; flex-wrap: wrap;">
2528
<img src="doc/urpc_2025.jpg" alt="urpc_2025" style="width: 50%;">
2629
<img src="doc/urpc_cctv13.jpg" alt="urpc_cctv13" style="width: 50%;">
@@ -134,6 +137,21 @@ CoralReefCam,中文名珊瑚礁™嘻屁屁高性能版,是 CoralReefPlayer
134137
1. 使用 DevEco Studio 打开 Harmony 目录
135138
2. 编译并运行 `entry` 项目
136139

140+
### WASM(Emscripten)
141+
142+
前置条件:
143+
144+
- CMake 3.24.0 或更高版本
145+
- Emscripten 4.0.21 或更高版本
146+
- 自 4.0.21 版本起支持通过 fetch 流式下载数据,因此如需拉取 MJPEG over HTTP 流则必须高于此版本
147+
148+
编译步骤:
149+
150+
1. 执行 `embuilder build sdl2` 构建 SDL2 库
151+
2. (可选)进入 thirdparty/ffmpeg/Emscripten 目录,执行 `build -t ffmpeg .` 构建 ffmpeg 库
152+
3. 使用 `emcmake cmake` 配置项目,然后进行生成
153+
4. 其余步骤同 Linux
154+
137155
### 交叉编译
138156

139157
CoralReefPlayer 支持交叉编译,可使用 CMake 的工具链文件进行交叉编译,工具链文件的编写请参考 `cmake/toolchains/aarch64-linux-gnu.toolchain.cmake`
@@ -178,6 +196,10 @@ CoralReefPlayer 支持交叉编译,可使用 CMake 的工具链文件进行交
178196

179197
启用 `BUILD_CANGJIE_BINDING` 选项,然后运行 `cangjie-package` 目标即可在 install 目录中生成包含仓颉代码的 zip 压缩文件。
180198

199+
### WASM
200+
201+
启用 `BUILD_WASM_BINDING` 选项,然后运行 `wasm-package` 目标即可在 install 目录中生成 npm 包。
202+
181203
## 文档
182204

183205
CoralReefPlayer 总体架构图:

binding/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,11 @@ set(BUILD_CANGJIE_BINDING OFF CACHE BOOL "Build cangjie binding")
2626
if(BUILD_CANGJIE_BINDING)
2727
add_subdirectory(cangjie)
2828
endif()
29+
set(BUILD_WASM_BINDING OFF CACHE BOOL "Build WebAssembly binding")
30+
if(NOT EMSCRIPTEN)
31+
message(WARNING "WebAssembly binding requires Emscripten toolchain!")
32+
set(BUILD_WASM_BINDING OFF)
33+
endif()
34+
if(BUILD_WASM_BINDING)
35+
add_subdirectory(wasm)
36+
endif()

binding/java/CMakeLists.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ target_compile_definitions(crp_native
4141
)
4242

4343
if(ANDROID)
44-
target_link_options(crp_native
45-
PRIVATE
46-
"-Wl,-z,max-page-size=16384"
47-
)
44+
target_link_options(crp_native
45+
PRIVATE
46+
"-Wl,-z,max-page-size=16384"
47+
)
4848
endif()
4949

5050
target_link_libraries(crp_native

binding/wasm/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules
2+
build
3+
*.log
4+
*.gz
5+
*.tgz

binding/wasm/CMakeLists.txt

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
project(coralreefplayer-wasm VERSION ${CoralReefPlayer_VERSION})
2+
3+
set(CMAKE_CXX_STANDARD 20)
4+
5+
include(copy_libs)
6+
7+
find_program(NodeJS_EXECUTABLE NAMES node)
8+
if(NOT NodeJS_EXECUTABLE)
9+
message(FATAL_ERROR "WebAssembly binding needs Node.js >= 12 to build!")
10+
return()
11+
endif()
12+
13+
set(USE_MODULE ON CACHE BOOL "Export UMD or ES6 module for wasm binding")
14+
set(USE_ES6_MODULE OFF CACHE BOOL "Export ES6 module for wasm binding")
15+
16+
file(
17+
COPY ${CMAKE_CURRENT_SOURCE_DIR}/
18+
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
19+
PATTERN "main.cpp" EXCLUDE
20+
)
21+
22+
configure_file(
23+
${CMAKE_CURRENT_BINARY_DIR}/package.json.in
24+
${CMAKE_CURRENT_BINARY_DIR}/package.json
25+
@ONLY
26+
)
27+
28+
add_executable(${PROJECT_NAME}
29+
main.cpp
30+
)
31+
32+
target_link_options(${PROJECT_NAME}
33+
PRIVATE
34+
"-sPTHREAD_POOL_SIZE=3"
35+
"-sEXPORTED_FUNCTIONS=[\"_malloc\",\"_free\"]"
36+
"-sPURE_WASI=1"
37+
"$<$<BOOL:${USE_MODULE}>:-sMODULARIZE=1>"
38+
"$<$<BOOL:${USE_ES6_MODULE}>:-sEXPORT_ES6=1>"
39+
"--no-entry"
40+
)
41+
42+
target_link_libraries(${PROJECT_NAME}
43+
PRIVATE
44+
embind
45+
"$<LINK_LIBRARY:WHOLE_ARCHIVE,CoralReefPlayer>"
46+
)
47+
48+
add_dependencies(${PROJECT_NAME}
49+
CoralReefPlayer
50+
)
51+
52+
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "coralreefplayer")
53+
54+
add_custom_target(wasm-package
55+
COMMAND npm run package -- --pack-destination ${CMAKE_INSTALL_PREFIX}
56+
DEPENDS ${PROJECT_NAME}
57+
)

binding/wasm/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# coralreefplayer-wasm

0 commit comments

Comments
 (0)