The opening
Recently, I encountered two dynamic link libraries in Python calling C/C++, but actually called the SDK library about RTSP video stream decoding. In the process, I step on a lot of holes, here is a record. It covers the use of the Ctypes module in Python and how to define and set callback functions in the dynamic library.
The dynamic library
C/C++ programs can be compiled into dynamically linked libraries for users to call, and can be compiled on Windows and Linux platforms to use the corresponding platform libraries. Use Python to call Windows and Linux dynamic libraries. Make sure you get the library files for the respective platforms. Example of loading a dynamic library:
# _*_coding:utf-8_*_
import os
import platform
from ctypes import *
linux_dll_name = 'xxx.so'
win_dll_name = 'xxx.dll'
sdk = None
sdk_dll_dir = os.getcwd()
sys_type = platform.system()
if sys_type == "Windows":
sdk = cdll.LoadLibrary(sdk_dll_dir + win_dll_name)
else:
sdk = cdll.LoadLibrary(sdk_dll_dir + linux_dll_name)
Copy the code
Note: when you write and compile a dynamic library in C++, the exported method names change because of method overloading in C++ and additional identifiers added at compile time. Extern “C” extern “C”
The ctypes module in Python
There is a Python module called Ctypes that calls C code. Ctypes defines the basic variable types in C and even specifies the length of the variable, taking into account platform differences. For example:
- c_int8
- c_int16
- c_int32
- c_int64
Common class variable type definition
When calling C functions in the dynamic library, note that the corresponding variable types need to be defined using c_xxx.
Struct definition (this is error-prone and requires extra attention)
Definition in C code
typedef struct _FrameHeader {
u16 Width;
u16 Height;
}FrameHeader;
Copy the code
Corresponds to the definition in Python
class FrameHeader(Structure):
_fields_ = [
('Width', c_uint16),
('Height', c_uint16)
]
Copy the code
Comparison of values
Calling C code in Python returns a value of type ctypes, such as the common c_int type. If you need to check this value, the code looks like this:
ret = init() Call the C function init() in the dynamic library and return c_int
if ret == 123 : Ret == c_int(123) ret == c_int(123)
dosomething()
Copy the code
In special cases, the structure contains itself, similar to the definition of a linked list node
typedef struct _DataFrame {
void* Head;
void* Bmp;
void* H264;
struct _DataFrame* Prev;
}DataFrame;
Copy the code
Corresponds to the definition in Python
class DataFrame(Structure):
pass
DataFrame._fields_ = [
('Head', POINTER(FrameHeader)),
('Bmp', POINTER(BmpData)),
('H264', c_void_p),
('Prev', POINTER(DataFrame))
]
Copy the code
On the representation of Pointers
Ctypes uses pointer and byref to correspond to Pointers in C. The pointer function pointer() can also be used where byref() is used. However, pointer() is often used as an argument to create an additional pointer object. Byref () is faster. Byref (&a); / / int *p (int *p);
C code content
#define Handle void*
void function(Handle *handle)
Copy the code
How Python code is written when the function is actually called
handle = c_void_p(0)
init(pointer(handle)) Byref (handle)
Copy the code
Pointer returns a type object that specifies the type of the argument and return value of a function, such as declaring a callback function:
CALLBACK_FUNC = CFUNCTYPE(c_int32, c_int32, POINTER(DataFrame), c_void_p) Where DataFrame is a structure
Copy the code
How to write a callback function
There are two main points about the use of callback functions:
- The definition of the callback function
- Registration of callback functions
Possible problems include:
When a callback function is declared, since Python functions do not need to specify a return value type, the first argument in the callback function definition is the return value type, or None if the return value is void
The declaration of a callback function in Python
Return value int32
DATA_CALLBACK_FUNC1 = CFUNCTYPE(c_int32, c_int32, POINTER(DataFrame), c_void_p)
# return void
DATA_CALLBACK_FUNC2 = CFUNCTYPE(None, c_int32, POINTER(DataFrame), c_void_p)
Copy the code
When registering a callback function, you need to assign the callback function to a variable and then register it, otherwise it will generate a segment error at run time because without the associated assignment reference, the callback function is fairly anonymous and will be reclaimed.
cb = CALLBACK_FUNC(frame_callback)
sdk.SetCallback(cb)
Copy the code
The callback returns a pointer to a buffer that needs to be defined in Python code
# 6220800 below is the buffer size of an image (1920*1080*3), which can be defined to be larger to meet higher resolution
# error if POINTER(c_char * 6220800) is changed to c_char_p
CALLBACK_FUNC = CFUNCTYPE(
c_int, c_int, c_void_p, c_int, POINTER(c_char * 6220800),POINTER(FRAME_INFO))
Copy the code
conclusion
Above are some of my experiences in calling the SDK of the camera using Python recently. It is not particularly detailed, but mainly records some pits that I have stepped on. There may be some pits that have not been stepped on.