使用Emscripten 将c/c++编译成js

背景

最近搞open cv识别物体的项目,因为客户需求,最终决定使用js来实现。

捕获摄像头 是通过navigator.mediaDevices,前提是 ios 需要11以上必须是https, android到还好。

识别物体通过opencv js 。

虽然坎坎坷坷 但是第一版还是上线了 除了https和默认显示后置摄像头(Android的枚举摄像头 根据label判断是否是后置的,IOS 中的facingMode:{exact:"environment"} 是有效的),其它还算顺利。

然后 然后 opencv js 10M 这个大小比较要命。。。

opencv是通过Emscripten 将c编译成js的 

所以打算ctrl+c, ctrl+v 精减下源码,再编译下,将js文件减少到500kb之内是没有问题的。

安装

安装 Emscripten 还是比较简单的 

https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html

git clone https://github.com/juj/emsdk.git

cd emsdk

git pull

emsdk install latest

我是windows下,安装的时候 下载LLMV的 很慢总失败,后来挂了代理 才下载成功。。。

编译

使用命令 emcc 可以把c++ 编译成js,如果你一上来就敲emcc 那么不好意思 是不存在的 先要运行下面的命令设置好当前进程的环境才能编译

C:\inno\libraries\emsdk\emsdk activate latest

C:\inno\libraries\emsdk\emsdk_env.bat


然后可以使用emcc了,说白了 每次打开cmd窗口 先要调用下上面那个命令

编译很简单 

emcc main.cpp -o main.js

main.cpp

void main()

{

print("Hello World");

}

输出的main.js 内容很多,内嵌了一些基础实现,但我搜索不到“Hello World”的字样,虽然没弄明白原理但觉得可以用来加密代码

折腾了很久 最后我选用的编译选项是

emcc --bind demo.cpp -o demo.js -s WASM=0 -O3 --memory-init-file 0 --pre-js pre.js --post-js post.js

我觉得这才是实战所需要的,实在看不上-s EXPORTED_FUNCTIONS="['_main', '_myfunc']" 然后用module.ccall。。。

--bind 是指可以EMSCRIPTEN_BINDINGS来声明需要导出的类以及单个函数

-s WASM=0 不要生成web assembly 即.wasm文件,因为这里暂时用不上

-O3 优化级别是3的 默认是不会优化的

--memory-init-file 这个完全是为了填-O3的坑,因为打开优化后生成文件除了.js还有一个 .js.mem, js加载进来之后还要去加载.js.mem文件之后才能用导出的函数 这里是异步的,需要注册Module.onRuntimeInitialized这个回调才能继续,那么这个参数是不要 生成mem文件,舍弃这些麻烦。

--pre-js pre.js  和 --post-js post.js 是加在生成的js首尾的js,本来是用来做匿名的,但看生成的话 有点搞笑。。。

可以欣赏这里所有源码

变量传递

https://kripken.github.io/emscripten-site/docs/api_reference/emscripten.h.html#inline-assembly-javascript 这里讲了使用EM_XX 怎么在c++里内联js代码,然后怎么交互,c++怎么传值(数字和指针)给js,js怎么返回值(数字和指针)给c++ 写的很详细 足够了

在EMSCRIPTEN_BINDINGS中传递Uint8Array

cpp

#include

#include

#include

#include

typedef  unsigned char uchar;

class MyClass

{


public:

    MyClass()

    {

        data = new uchar[100];

        size = 100;

    }

    emscripten::val getData () const

    {

        return emscripten::val(emscripten::memory_view(size,data));

    }

private:

    int size;

    uchar* data;

};

EMSCRIPTEN_BINDINGS(my_class) {

  emscripten::class_("MyClass")

    .constructor()

    .property("data", &MyClass::getData)

    ;

}

js

var my = new inno.MyClass();

var ctx = cin.getContext('2d');

my.data.set(ctx.getImageData(0, 0, w, h).data);

var imgData = new ImageData(new Uint8ClampedArray(my.data), w, h);

ctx.putImageData(imgData, 0, 0);

另一种方式

cpp

class MyClass{

public:

    MyClass(int w, int h){

    }

    void setData(intptr_t frame4b_ptr)

    {

    //frame4b_ptr[0]

    }

};

EMSCRIPTEN_BINDINGS(my_class) {

  emscripten::class_("MyClass")

    .constructor()

  .function("update", &MyClass::updasetDatate, emscripten::allow_raw_pointers())

    ;

}

JS

var data = this.ctx.getImageData(0, 0, w, h);

var bytes = arrayToHeap(data.data);

my.setData(bytes.byteOffset);

data.data.set(bytes);

this.ctx.putImageData(data, 0, 0)

;

function freeArray(heapBytes) {

    Module._free(heapBytes.byteOffset);

}

function arrayToHeap(typedArray) {

    var numBytes = typedArray.length * typedArray.BYTES_PER_ELEMENT;

    var ptr = Module._malloc(numBytes);

    heapBytes = Module.HEAPU8.subarray(ptr, ptr + numBytes);

    heapBytes.set(typedArray);

    return heapBytes;

}

你可能感兴趣的:(使用Emscripten 将c/c++编译成js)