openresty(nginx+lua)或者luajit调用C++模块的两种方法

一、概述

演示代码工程位置:lua_call_cpp

在编写OpenResty代码时,经常需要调用自己编写的C++模块。本文介绍了两种常用的调用方法:

  1. 基于LuaJIT的CFFI调用,适用于调用阻塞时间较短的C++模块。
  2. 基于OpenResty的shell模块调用,对应于Lua的os.execute,适用于调用阻塞时间较长的C++模块。

C++多进程master-worker工作机制较完整的实现,边端和云端协同工作实现博客中提到,在x86服务器端进行轨迹叠加时,需要Lua代码调用C++封装的视频和轨迹叠加模块。最初使用LuaJIT的CFFI机制,但由于视频上的轨迹叠加任务消耗大量CPU资源,导致阻塞了Nginx的工作进程。因此后来转而采用OpenResty的异步调用方法,避免了对Nginx工作进程的阻塞。

二、Lua基于CFFI调用C++模块,适用于OpenResty代码

在封装C++模块时,需要使用extern "C"将其封装成C模块。传入的C++参数只能使用POD格式,不支持C++的STL,支持C语言的结构体struct,但不支持含有指针的class

示例代码
// video_bbox_drawer.h
#pragma once

#ifdef __cplusplus 
extern "C" {
#endif

int drawBBoxOnVideo(const char *inputVideo, const char *bboxString, int bboxStrLen, const char *outputVideo);

#ifdef __cplusplus
}
#endif
-- Lua CFFI调用C++模块示例

local ffi = require("ffi")
ffi.cdef[[
    int drawBBoxOnVideo(const char *inputVideo, const char *bboxString, int bboxStrLen, const char *outputVideo, int outputWidth, int outputHeight, int frameRate);
]]

local lib = ffi.load('video_bbox_drawer')

local function read_file(fileName)
    local f = assert(io.open(fileName,'r'))
    local content = f:read('*all')
    f:close()
    return content
end

local function test_draw()
    local content = read_file("detect_result.txt")
    print(#content)
    lib.drawBBoxOnVideo("face_1280_720.h264", content , #content, "face_1280_720_lua.mp4")
end

test_draw()

三、Lua异步调用C++可执行程序(OpenResty代码使用shell模块调用)

使用Lua的os.execute调用方法如下:

local function read_file(fileName)
    local f = assert(io.open(fileName,'r'))
    local content = f:read('*all')
    f:close()
    return content
end

local function test_draw()
    local content = read_file("detect_result.txt")
    print(#content)
    local cmd = "./bin/video_bbox_drawer" .. " " .. 
                "face_1280_720.h264" .. " " .. 
                content .. " " .. 
                #content .. " ".. 
                "face_1280_720_output.h264"
    os.execute(cmd) 
    -- 在OpenResty代码中应使用shell.run调用,而非os.execute,以避免阻塞Nginx工作进程
    -- 如:local ok, stdout, stderr, reason, status = shell.run(cmd, nil, timeout)
end

test_draw()

推荐在OpenResty代码中使用官方提供的shell模块进行调用,使用方法类似于os.execute

local shell = require("resty.shell")

local function test_draw()
    local content = read_file("detect_result.txt")
    print(#content)
    local cmd = "./bin/video_bbox_drawer" .. " " .. 
                "face_1280_720.h264" .. " " .. 
                content .. " " .. 
                #content .. " ".. 
                "face_1280_720_output.h264"
    local ok, stdout, stderr, reason, status = shell.run(cmd, nil, timeout)
    -- 处理调用结果
end

test_draw()

四、同一份代码快速适配不同的调用方式

观察lua_call_cpp代码,可以看出,无论是通过CFFI调用的C++库还是通过C++可执行程序,实际上都是同一份C++代码。只需在编译时使用不同的CMake配置即可实现适配:

project(video_bbox_drawer)
cmake_minimum_required( VERSION 3.0 )

aux_source_directory(src DIR_SRCS)
include_directories(${PROJECT_SOURCE_DIR}/include)

# 编译为C++可执行程序供Lua调用
#add_executable(${PROJECT_NAME} ${DIR_SRCS})
# 编译为C动态库供Lua调用
add_library(${PROJECT_NAME} SHARED ${DIR_SRCS})

if(CMAKE_SYSTEM_NAME MATCHES "Linux")
    target_link_libraries(${PROJECT_NAME} PRIVATE
        pthread  
        opencv_core
        opencv_highgui
        opencv_imgproc
        opencv_videoio
    )
endif()

在CMakeLists.txt中,通过注释掉add_executable并启用add_library,即可分别编译出可执行程序和动态库供Lua调用。

# 编译为C++可执行程序供Lua调用
#add_executable(${PROJECT_NAME} ${DIR_SRCS})
# 编译为C动态库供Lua调用
add_library(${PROJECT_NAME} SHARED ${DIR_SRCS})
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘色的喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值