最近作一個小軟件須要用到虛擬攝像頭,在網上找了找虛擬攝像頭軟件 發現 Vcam 軟件有個API 能夠用,有API固然是最好的啦,可是這個API只有C++和C#的。都說 **「人生苦短,得用python」**能用Python解決的事情儘可能別用C++,因而萌生了本身寫個模塊的想法。
值得慶幸的是以前研究過一段時間C++。
先貼兩個python官方文檔連接 C API 第三方模塊開發指南html
創建win32 DLL工程
python
調整工程屬性ios
頭文件windows
寫Python的C++擴展必須包含Python.h 和 structmember.h兩個頭文件api
#include <windows.h> #include <iostream> #include <sstream> #include <Python.h> #include <structmember.h>
API文件導入略過數據結構
首先建立一個struct 用來存放類的各項屬性.ide
struct IVCamRenderer; # 這個IVCamRenderer在VCam API文件裏面有定義 這裏從新聲明下 typedef struct _VCam { PyObject_HEAD // 結構體的第一個元素必須是 PyObject_HEAD 宏 IBaseFilter * __vcam_renderer; //VCam類的第一個成員 IVCamRenderer * __my_vcam; //第二個成員 因爲要處理圖片用到了GDI+ 此屬性用來存放 }VCam; static PyMemberDef VCam_DataMembers[] = { //類/結構的數據成員類說明 表. 根據官方文檔說明此類表必需要要以一個元素全爲NULL的數據結構結尾,後面還有一個Method 表也是如此 { "__vcam_renderer", T_OBJECT, offsetof(VCam, __vcam_renderer), 0, "The vcam_renderer of instance" }, { "__my_vcam", T_OBJECT, offsetof(VCam, __my_vcam), 0, "The vcam of instance." }, { NULL, NULL, NULL, 0, NULL } };
咱們來看一下PyMemberDef 的定義函數
/* An array of PyMemberDef structures defines the name, type and offset of selected members of a C structure. These can be read by PyMember_GetOne() and set by PyMember_SetOne() (except if their READONLY flag is set). The array must be terminated with an entry whose name pointer is NULL. */ typedef struct PyMemberDef { char *name; // 在Python中顯示的名稱 int type; // 變量類型 Py_ssize_t offset; // offset 變量在前面爲模塊類定義的模塊中的offset int flags; //讀寫權限標記 char *doc; //幫助文檔內容 } PyMemberDef; /* Types */ #define T_SHORT 0 #define T_INT 1 #define T_LONG 2 #define T_FLOAT 3 #define T_DOUBLE 4 #define T_STRING 5 #define T_OBJECT 6 /* XXX the ordering here is weird for binary compatibility */ #define T_CHAR 7 /* 1-character string */ #define T_BYTE 8 /* 8-bit signed int */ /* unsigned variants: */ #define T_UBYTE 9 #define T_USHORT 10 #define T_UINT 11 #define T_ULONG 12 /* Added by Jack: strings contained in the structure */ #define T_STRING_INPLACE 13 /* Added by Lillo: bools contained in the structure (assumed char) */ #define T_BOOL 14 #define T_OBJECT_EX 16 /* Like T_OBJECT, but raises AttributeError when the value is NULL, instead of converting to None. */ #define T_LONGLONG 17 #define T_ULONGLONG 18 #define T_PYSSIZET 19 /* Py_ssize_t */ #define T_NONE 20 /* Value is always None */ /* Flags */ #define READONLY 1 #define READ_RESTRICTED 2 #define PY_WRITE_RESTRICTED 4 #define RESTRICTED (READ_RESTRICTED | PY_WRITE_RESTRICTED)
寫兩個函數用來處理python類初始化資源申請和和類析構時資源釋放工具
初始化函數ui
static void VCam_init(VCam* Self, PyObject* pArgs) //構造方法. { Self->__vcam_renderer = nullptr; Self->__my_vcam = nullptr; HRESULT hr=::CoInitialize(nullptr); if (FAILED(hr = CoCreateInstance(CLSID_VCamRenderer, NULL, CLSCTX_INPROC, IID_IBaseFilter, reinterpret_cast<void**>(&(Self->__vcam_renderer))))) { PyErr_SetString(PyExc_OSError, "driver not installed!"); return; } // get [IVCamRender] interface from VCam Renderer filter if (FAILED(hr = Self->__vcam_renderer->QueryInterface(&(Self->__my_vcam)))) { PyErr_SetString(PyExc_OSError, "driver not installed!"); return; } }
請不要在乎構造函數中一堆亂七八糟的代碼 那些代碼是VcamAPI初始化取對象的代碼 正常簡單點寫就是假如類體內聲明 一個 成員
XXType * instance;
構造時將其實例化一下申請一塊內存
self->instance = new xxx;
析構函數
static void VCam_Destruct(VCam* Self) //析構方法. { if (Self->__my_vcam) Self->__my_vcam->SetConnectionNotificationEvent(reinterpret_cast<__int64>(nullptr)); if (Self->__vcam_renderer) Self->__vcam_renderer->Release(), Self->__vcam_renderer = nullptr; if (Self->__my_vcam) Self->__my_vcam->Release(), Self->__my_vcam = nullptr; Py_TYPE(Self)->tp_free((PyObject*)Self); //釋放對象/實例. }
析構時候 shift鍵構造時候申請的內存防止內存泄漏便可
delete self->instance; self->instance = nullptr;
最後須要掉將Python對象釋放
Py_TYPE(Self)->tp_free((PyObject*)Self); //釋放對象/
寫供Python調用的類中的各類方法
舉例:寫一個將虛擬攝像頭顯示 調整爲鏡像顯示的方法
static PyObject* VCam_Mirror(VCam* Self, PyObject* Argvs) { Py_INCREF(Py_None); int mode=1; if (!PyArg_ParseTuple(Argvs, "|i", &mode)) { cout << "Parse the argument FAILED! You should pass correct values!" << endl; return Py_None; } Self->__my_vcam->SetMirror(mode); //Mirror the output video (0: no mirror, others: mirror), non-persistent. return Py_None; }
看完引用計數問題感受有點明白了 CPython 垃圾回收機制原理了有沒有
先寫到這 後面再開一篇 如何用C++ 寫Python模塊擴展(二)