Javascript和C++数据传递(网页端视频播放器的设计实现总结)

网页端视频播放器的设计实现总结

  • 环境配置

1.了解Emscripten和WebAssembly技术,提供Emscripten编译环境(Emscripten安装地址),在cmd中查看emcc是否可用验证环境是否创建成功。

2.搭建本地服务器,首先我们要到Node.js官网下载对应版本的安装包(Node.js下载地址);接着就是安装,和安装普通软件类似,直接下一步下一步就可以了;之后我们来验证node是否安装成功,Win+R输入cmd来调出控制台并输入node -vnpm -v来查看node版本和npm(包管理工具)版本;最后执行如下命令便可以完成本地服务器的搭建。

npm install -g serve  

serve . 

注意:在本地直接点击html文件是以文件方式打开会报错,必须使用http协议的方式打开;同时必须保证浏览器支持webassembly。

二.文件的读取以及主线程与web worker之间数据的传递

         1.C/C++代码和web worker都不支持对文件的打开、读写,只能以HTML5的方法对文件进行操作,推荐使用FileReader对象。

         2.worker.js必须存放在服务器中才能加载执行。

         3.以定时器的方式分段读取文件,代替传统的while循环方式,解决主线程阻塞的问题,使用setInterval方法

         4.javaScript中主线程和worker之间的数据传递方式?

         主线程中的worker对象通过postMessage向indexedDB数据库发送数据,当indexedDB数据库接收到客户端发送的数据

  首先把数据的键值储存并记录到indexedDB数据库表里面,其实相当于把数据保存到一张结构完整的表内。接着,indexedDB

  数据库会把接收到的数据值扔给worker的onmessage进行处理。

 

三.javaScript对内存的操作以及与C/C++接口数据的传递

         ArrayBuffer对象、TypedArray视图和DataView视图是 JavaScript 操作二进制数据的接口。该接口设计之初的目的是为了满足 JavaScript

与显卡之间大量的、实时的数据交换,它们之间的数据通信必须是二进制的,而不能是传统的文本格式。现web worker与底层C/C++接口的二进制数据传递。

   

         二进制数组由三类对象组成。

(1)ArrayBuffer对象:代表内存之中的一段二进制数据,可以通过“视图”进行操作。“视图”部署了数组接口,这意味着,可以用数组的方法操作内存。

 

(2)TypedArray视图:共包括 9 种类型的视图,比如Uint8Array(无符号 8 位整数)数组视图, Int16Array(16 位整数)数组视图, Float32Array(32 位浮点数)数组视图等等。

 

(3)DataView视图:可以自定义复合格式的视图,比如第一个字节是 Uint8(无符号 8 位整数)、第二、三个字节是 Int16(16 位整数)、第四个字节开始是 Float32(32 位浮点数)等等,此外还可以自定义字节序。

 

         基本类型指针传递:

    你可以使用getValue(ptr,type)setValue(ptr,value,type)访问内存。第一个参数ptr是一个指针(代表一个内存地址的数字)。类型type必须为LLVM IR类型,i8i16i32i64floatdouble或类似i8 (或只有 )的指针类型。

 

C/C++中的二进制数据传递到js中:

         方式一:(通过返回值获取)

         var iyuv_size = iWidth*iHeight*3/2;

         var pYUV = Module._GetBuffer();//获取C/C++层二进制数据的地址

         var buf = new ArrayBuffer(iyuv_size);//通过二进制对象分配一块连续内存

         var ayuv_data = new Uint8Array(buf);//二进制对象绑定到视图,通过视图对内存进行读写操作

         ayuv_data.set(Module.HEAPU8.subarray(pYUV,pYUV+iyuv_size));//c/c++中的内存拷贝到刚分配的js内存中

        

         方式二:(通过参数获取)

         var iyuv_size = iWidth*iHeight*3/2;

         var yuvData = Module._malloc(iyuv_size);//传递地址时才需要malloc,malloc之后获得的是指向地址的指针

         var yuvLen = Module._malloc(4);

         Module.setValue(yuvLen,iyuv_size,”i32”);//可以设置长度初始值,注意这里的yuvLen是一个地址指针传递,而不是值传递

         var res = Module._GetBuffer(yuvData,yuvLen);//第二个参数传递的是指针,目的是从C++层获得yuv的长度,因此要malloc

         var nJLen = Module.getValue(yuvLen,”i32”);

         var pJData = new Uint8Array(nJLen);

         pJData.set(Module.HEAPU8.subarray(yuvData,yuvData+yuvLen));//二进制数据需要绑定视图才可以操作内存

        

        

js中的数据传递到C/C++中:

    方式一:

         var iLen = data.len;

         var pBuffer = Module._malloc(iLen)//通过emscripten分配C/C++中的堆内存

         var Data = Module.HEAPU8.subarray(pBuffer, pBuffer + iLen);//堆内存绑定到视图对象

         Data.set(new Uint8Array(data.buf));//数据写入到emscripten分配的内存中去

         InputData(pBuffer,iLen);

 

         方式二:

        var iLen = data.len;

        var pBuffer = Module._malloc(iLen);

        Module.writeArrayToMemory(new Uint8Array(data.buf),pBuffer);//js中的数据拷贝到刚分配的C++缓存中;

        InputData(pBuffer,iLen);

 

Js和C++之间的结构体传递:

         https://blog.csdn.net/pkx1993/article/details/82015315

 

四.Emscripten编译C/C++到js和wasm文件

         1.Emscripten编译优化分为两个步骤:

         (1)每个源文件编译成目标文件(.bc文件),通过LLVM优化

         (2)目标文件编译成js文件

 

         2.emscripten正确的优化方式:

        

         ./emcc -O2 a.cpp -o a.bc//编译成bitcode

         ./emcc -O2 b.cpp -o b.bc//编译成bitcode

         ./emcc -O2 a.bc b.bc -o project.js//把bitcode编译成js

 

    注意:各个步骤的优化级别必须设置为一样

   

         3.Js使用库的两种方式:

         (1)多个bitcode直接变成js

         emcc project.bc libstuff.bc -o final.js

         (2)多个bitcode先编译成一个,然后在变成js

         emcc project.bc libstuff.bc -o allproject.bc

         emcc allproject.bc -o final.js

 

    4.编译过程中遇到的问题及解决方案:

         (1)emcc方式编译js文件时,提示C文件调用cpp文件找不到函数时,尝试使用em++方式进行编译

         (2) 编译的C/C++文件中的函数不能和平台相关,并且不支持多线程

         (3)bind/emscripten.h只对C++文件有效,如果C文件中引入该头文件进行接口的绑定会报错

 

五.Js文件的导入

1.html中导入js文件

 <script src="play.js"></script>

 

2.web worker中导入js文件

importScripts('decode.js');

Module.postRun.push(function () {

        postMessage({'function': "loaded"});

});

//它是以阻塞方法加载js的,只有所有文件加载完成之后,接下来的脚本才能继续执行

 

3.js包中导入js文件

         异步方式:

    var _script = document.createElement("script");

 

    _script.type = "text/javascript";

 

         _script.src = "SuperRender.js";

 

    document.getElementsByTagName("head")[0].appendChild(_script);

         _script.onload = function(){

                   console.log("SuperRender.js load sucess");

         }

该加载方式是异步加载的,注意要等到js文件加载完,才能调用其中的类或方法,否则会出错。

 

         同步方式:

         加载类:import  {文件名(不加后缀)}  from  “类名(加路径)”;

    加载非类:import  from 文件名;

 

视频丢帧和花屏问题

1.利用ffmpeg解码得到一帧数据后,需要将数据保存到缓存buffer中,等到送入到解码库中的数据全都处理完,再返回给上层。

2.解码后的YUV数据时由三个分量分开存储的,在送入渲染库webGL之前,需要将yuv三个分量的数据拼接成一块连续的数据再送入到渲染库中进行渲染。

 

猜你喜欢

转载自blog.csdn.net/pkx1993/article/details/79927887