最近有个需求是将海康摄像头在浏览器上进行实时显示。
然而海康目前的web接口无法满足我们拥有的设备,主要提到我们的设备不支持websocket取流,故不支持无插件开发,需要用到非常旧的chrome版本,方可使用它们的开发包,而且是有插件开发包。
有技术不求人,自己动手自己做。于是本项目立项,自己取流然后进行显示。*
小主出于napi为官方维护,且对node版本可以自动适配,故选之。
anyway, 本博客主要是简单介绍取流实现这一块用到的napi技术要点。
开发流程简介
1、如何初始化环境,制作一个简单的node扩展(本文不做赘述,网上示例一大把), 主要用到node 环境, node-gpy- 初始化完成后,进入开发阶段,小主是用windows进行开发,故使用的vs作为开发环境,使用记事本亦可。
接下来你将看到
- 获取napi错误信息。
- 获取js传来的参数,包含传入参数及一些回调函数。
- 多次调用js回调函数的实现(体现在回传视频流时)。
napi 官网的接口说明: http://nodejs.cn/api/n-api.html
1. 获取napi错误信息接口。无论何时进行开发,错误的出现是必然的,可能是一时疏忽,亦或是对参数的理解不到位都有可能造成错误的出现,所以获取和分析错误信息的能力非常重要。 根据官方提供的接口 napi_get_last_error_info, 由于每次代码相同,小主对其进一步定义成一个宏定义方便使用。
#define GET_NAPI_ERROR() const napi_extended_error_info* errorInfo; status = napi_get_last_error_info(env, &errorInfo); if (status == napi_ok) { printf("last error info is %sn", errorInfo->error_message); } else { printf("get last error erorn"); }
如上宏定义,使用时,只需插入 GET_NAPI_ERROR()即可打印出出错信息。
其中的status,一般是函数开头定义好,专门用来接收napi接口状态的变量。
napi_status status;
获取js参数, 我这边以C++实现的实时预览接口为例,简单介绍一下这一个接口的实现, 一共需要用到的有两个参数, 一个参数作为播放通道号进行传入,另一个参数作为视频流的返回方法——回调函数。
1) 主要用到的napi接口-
获取js传来的参数列表—— napi_get_cb_info
-
获取参数信息 ——napi_get_value_string_utf8
napi_value RealPlay(napi_env env, napi_callback_info info){// napi 标准头 napi_value napi_ret; napi_status status; // 变量声明 char strTemp[2] = {}; // 用于接收通道参数 int channelNum = 0; // 用于存储视频通道号 napi_value js_callback; // 用于存储回调函数 size_t argc = 2; // 用于napi接口的传入(我要取多少个参数)传出(实际有多少个参数)参数 napi_value argv[2]; // 得到js传来的所有参数,均为napi_value类型,之后在逐个进行解析 status = napi_get_cb_info(env, info, &argc, argv, NULL, NULL); assert(status == napi_ok); if (argc < 2) { napi_create_int32(env, 302, &napi_ret); // 传入参数过少,直接返回 return napi_ret; } // 至此获取到了传入的所有参数列表 argv. // 解析传入的int参数 begin status = napi_get_value_string_utf8(env, argv[0], strTemp, 2, nullptr); // 这里我直接使用 napi_get_value_int32接口获取不到通道号参数。故转了一下,先获取为char*,再进行char* to int 的转换。 channelNum = atoi(strTemp); // 解析传入的int参数 end // 保存获取到的回调函数 js_callback = argv[1]; // 如果需要直接调用回调函数返回数据,则调用下面的语句 // napi_value arg_ret; // status = napi_create_string_utf8(env, "hello world", NAPI_AUTO_LENGTH, arg_ret); // assert(status == napi_ok); // napi_value global; // status = napi_get_global(env, &global); // assert(status == napi_ok); // status = napi_call_function(env, global, js_callback, 1, arg_ret, nullptr); // assert(status == napi_ok); // ... 其他一些执行语句 return napi_ret; }3. 多次调用js回调函数的实现(体现在回传视频流时)。
js 如何多次调用回调函数呢,基于上面获取到的js_callback变量进行代码讲述。
1) 本小节用到的napi接口- 接口1:实际调用js函数的执行函数, 具体函数参考如下函数指针进行实现。该函数的参数由napi内部自动传入,相当于napi内部的一个回调。* 接口2: napi_call_threadsafe_function该函数在任何需要调用回调的地方调用, 本质上相当于Qt发出调用js函数的信号,告诉接口1,我需要调用js函数,并把data数据作为js函数的参数返回.
其中参数napi_threadsafe_function func由接口3提供。* 接口3: 根据已经得到的回调函数js_callback创建napi_threadsafe_function函数, 创建后就可以在接口2调用了。
首先实现接口1的那个函数,以备后用。
napi_call_function
void realPlayThreadSafe_func_callJs(napi_env env, napi_value js_callback, void* context, void* data) { napi_status status; napi_value argv[2]; napi_value undefined; status = napi_get_undefined(env, &undefined); // 如没必要对data进行处理,则可以直接调用js函数,并传入data参数 status = napi_call_function(env, undefined, js_callback, 2, data, nullptr); if (status != napi_ok) { printf("call js call back function failedn"); GET_NAPI_ERROR(); } }
之后创建全局js_cb函数。
// 由于需要在其他函数中对js_cb进行调用, 故声明为全局定义. napi_threadsafe_function realPlayCallbackFunc = nullptr; napi_value RealPlay(napi_env env, napi_callback_info info){ // ... 一些其他语句 js_callback = argv[1]; // 开始创建本地 threadsafe 回调函数 napi_value srcCbName; // napi接口需要,暂未明确其目的??,非可选参数 if (napi_create_string_utf8(env, "realpCb", NAPI_AUTO_LENGTH, &srcCbName) == napi_ok) { printf("create callbak source successn"); } status = napi_create_threadsafe_function(env, js_callback, // napi_value 回调函数 nullptr, srcCbName, 0, 1, nullptr, nullptr, nullptr, realPlayThreadSafe_func_callJs, // 实际的执行单元, 接口1 &realPlayCallbackFunc); // 全局threadsafe函数 if (status == napi_ok) { printf("threadsafe function create success n"); } else { printf("threadsafe function create failed, ret=%d n", status); GET_NAPI_ERROR(); } }
至此准备工作完成,找到合适的地方开始调用。
void i_need_call_js( ){ napi_status status; // ... int data = 666; status = napi_call_threadsafe_function(realPlayCallbackFunc, &data, napi_tsfn_nonblocking); // 调用后,将会自动调用接口1的实现方法进一步调用js回调函数。 assert(status == napi_ok); // ... }3) 逻辑图
如果代码看起来不够清晰,希望此图可助你理解。
本次经验分享到此结束,由于网上该部分资料尚未完善,结构基本由博主组织试验而成,如有不足之处还请海涵, 并可在评论区指出以示后人避坑。
有些术语可能不太准确,姑且先这样理解吧。
如果对你有帮助, 建议一键三连,不要下次一定。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)