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:

  1. The definition of the callback function
  2. 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.