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

一、概述

  • 演示代码工程位置:https://gitee.com/liudegui/lua_call_cpp

在编写openresty代码时,有时候要调用自己编写的C++模块。
本文给出了两种常用的方法:

  • 1、基于luajit的cffi调用,这主要用于调用阻塞时间比较短的C++模块;
  • 2、基于openresty的shell模块调用,这对应于lua的os.execute调用,这可用于调用阻塞时间比较长的C++模块。

C++多进程master-worker工作机制较完整的实现,边端和云端协同工作实现 这篇博客中,我提到在x86服务器端进行轨迹叠加时,需要lua代码调用C++封装的视频和轨迹叠加模块。
我开始的实现方法是使用luajit的cffi机制调用C++模块,但由于在视频上画轨迹是一个比较耗CPU,这会阻塞nginx的工作进程;因此后来改用openresty的shell.run异步调用C++模块,这将不会阻塞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调用
    --如:local ok, stdout, stderr, reason, status = shell.run(cmd, nil, timeout)
end

test_draw()

在openresty代码中是不建议使用os.execute调用cmd的,因为这会阻塞nginx工作进程;而是使用openresty官方提供的shell模块调用;使用起来和os.execute类似,如:local ok, stdout, stderr, reason, status = shell.run(cmd, nil, timeout)

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

仔细观察我提供的lua_call_cpp代码,不难发现,其实cffi调用的C++库和C++可执行程序其实是同一份C++代码,只需要在编译的CMakeLists.txt中做区分即可。

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++代码编译成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()

如以上,只是在编译的时候分别启用add_executable和add_library,代码中的main函数对编译C库没有影响。

# 将C++代码编译成可执行程序供lua调用
#add_executable(${PROJECT_NAME} ${DIR_SRCS})
# 将C++代码编译成C动态库供lua调用
add_library(${PROJECT_NAME} SHARED ${DIR_SRCS})

猜你喜欢

转载自blog.csdn.net/stallion5632/article/details/125955805
今日推荐