I. Обзор
- Расположение проекта демо-кода: https://gitee.com/liudegui/lua_call_cpp
При написании кода openresty иногда вам нужно вызвать свой собственный модуль C++.
В этой статье представлены два часто используемых метода:
- 1. Вызов cffi на основе luajit в основном используется для вызова модулей C++ с относительно коротким временем блокировки;
- 2. Вызов модуля оболочки на основе Openresty , который соответствует вызову os.execute в lua, который можно использовать для вызова модулей C++ с относительно длительным временем блокировки.
В полной реализации многопроцессорного рабочего механизма C++ master-worker, блоге реализации совместной работы на стороне и в облаке я упомянул, что при выполнении суперпозиции траекторий на стороне сервера x86 код lua должен вызывать видео, инкапсулированное в C++. модуль наложения траекторий.
Мой первоначальный метод реализации заключается в использовании механизма cffi luajit для вызова модуля C++, но поскольку отрисовка дорожки на видео является относительно ресурсоемким процессом, он будет блокировать рабочий процесс nginx, поэтому позже я переключился на shell.run от openresty. для асинхронного вызова модуля C++, это не будет блокировать рабочие процессы nginx.
2. Lua вызывает модули C++ на основе cffi, что подходит для кода openresty
Вот некоторые моменты, на которые следует обратить внимание
- При инкапсуляции модулей C++ вам необходимо использовать
extern "C"
инкапсуляцию в модули C; - Входящие параметры C++ могут использовать только формат POD, а не C++ STL.Структура структуры языка C поддерживается, но класс, содержащий указатели, не поддерживается.
// 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()
3. Lua асинхронно вызывает исполняемую программу C++ (код openresty использует вызов оболочки)
Метод вызова os.execute с использованием lua:
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, он аналогичен os.execute, например: local ok , стандартный вывод, стандартный вывод, причина, статус = shell.run(cmd, nil, timeout)
4. Один и тот же код быстро адаптируется к разным методам вызова
Внимательно изучите предоставленный мной код lua_call_cpp , и нетрудно обнаружить, что библиотека C++, вызываемая cffi, и исполняемая программа 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 соответственно во время компиляции, и основная функция в коде не повлияет на компиляцию библиотеки C.
# 将C++代码编译成可执行程序供lua调用
#add_executable(${PROJECT_NAME} ${DIR_SRCS})
# 将C++代码编译成C动态库供lua调用
add_library(${PROJECT_NAME} SHARED ${DIR_SRCS})