python如何调用C, 如何注册成C的回调函数(python后台程序常用方法)

最近做一个项目,分析视频内容,用C语言开发,需要运行在linux服务器后台,被python调用,并且实时返回进度信息;

其实是python后台程序常用方法:

C开发完成底层的功能,python直接把C当做python模块进行调用。


需要做两个工作:

  • python能调用C语言的函数;
  • python通过调用C函数,并注册python的回调函数,C代码通过python回调函数告诉Python当前实时进度和状态;

1,python如何调用C语言

主要就是应用ctypes这个模块,too simple too naive。

python代码是这样滴:

from ctypes import *
dynamicLibString = './libcheckVideoFile.so'
mylib = cdll.LoadLibrary(dynamicLibString)
ulHandle = mylib.VideoAnalyzeInit(videoFilename)
    if ulHandle == 0:
        print 'VideoAnalyzeInit error.'
        print ''
        
    mylib.EnableBlackDetected(ulHandle)
    mylib.EnablePureColorDetected(ulHandle)
    mylib.EnableFrozenDetected(ulHandle)


    mylib.EnableMuteVoiceDetected(ulHandle)


C代码是这样滴:

unsigned long VideoAnalyzeInit(char* szFilename)
{
    VideoAnalyzeManage* pManager = new VideoAnalyzeManage(szFilename);
    if(pManager)
    {
        int iRet = pManager->Init();
        if(iRet != 0)
        {
            delete pManager;
            return 0;
        }
    }
    return (unsigned long)pManager;
}
void EnableBlackDetected(unsigned long ulHandle)
{
    VideoAnalyzeManage* pManager = (VideoAnalyzeManage*)ulHandle;
    if(pManager)
    {
        pManager->EnableBlackDetected();
    }
}


就像C语言编译出来的.so库只是python的一个模块,直接调用就可以了。


2,python注册C语言的回调函数

其实也不难,python的函数本身也是python的对象,实现也就简单了:


python的回调函数:

def OnPyVideoAnalyzeResultCallback(ulStartTS, ulEndTS, ulDetectedType, ulParam):
    fStartTS = ulStartTS/1000.0
    fEndTS   = ulEndTS/1000.0
    outputString = ''
    
    if ulDetectedType == ALL_BLACK_DETECTED :
        outputString = videoFilename + ': All black color detected: start(' + str(fStartTS) + ') end(' + str(fEndTS) + ')'
    elif ulDetectedType == SIMPLE_COLOR_DETECTED :
        outputString = videoFilename + ': All pure color detected: start(' + str(fStartTS) + ') end(' + str(fEndTS) + ')'
    elif ulDetectedType == FROZEN_VIDEO_DETECTED :
        outputString = videoFilename + ': Frozen image detected: start(' + str(fStartTS) + ') end(' + str(fEndTS) + ')'
    elif ulDetectedType == AUDIO_MUTE_DETECTED :
        outputString = videoFilename + ': Mute voice detected: start(' + str(fStartTS) + ') end(' + str(fEndTS) + ')'
    print outputString
    WriteLog(logFilename, outputString)

def OnPyVideoStateCallback(uiProgress, uiState, ulParam):
    global videoFilename
    outputString = ''
    if uiState == DECODE_START :
        outputString = '\r\n' + videoFilename + ': video analyze is starting......'
        WriteLog(logFilename, outputString)
    elif uiState == DECODE_RUNNING :
        outputString = videoFilename + ': video analyze is running, progress: ' + str(uiProgress) + '%'
    elif uiState == DECODE_END :
        outputString = videoFilename + ': video analyze is ended'
        WriteLog(logFilename, outputString)
    elif uiState == DECODE_ERROR :
        outputString = videoFilename + ': video analyze is error'
        WriteLog(logFilename, outputString)
    print outputString
python 两个回调函数:OnPyVideoAnalyzeResultCallback和OnPyVideoStateCallback。

如何把这两个python函数注册成C代码的回调函数呢?

python部分是这样注册滴:

    CMPRESULTFUNC = CFUNCTYPE(None, c_ulong, c_ulong, c_ulong, c_ulong)
    CMPSTATEFUNC = CFUNCTYPE(None, c_ulong, c_ulong, c_ulong)

    iRet = mylib.VideoAnalyzeStart(ulHandle, CMPRESULTFUNC(OnPyVideoAnalyzeResultCallback), CMPSTATEFUNC(OnPyVideoStateCallback))
应用这个来设置:CFUNCTYPE
第一个参数是python回调函数的返回值,如果没有就是None。

第二个及其以后的就是python回调函数的参数类型了。

CMPRESULTFUNC = CFUNCTYPE(None, c_ulong, c_ulong, c_ulong, c_ulong)//创建一个c函数类型的对象工厂,该函数返回值为None,有三个入参,都为unsigned long。

CMPRESULTFUNC(OnPyVideoAnalyzeResultCallback)根据Python可调用对象生成函数。


mylib.VideoAnalyzeStart(ulHandle, CMPRESULTFUNC(OnPyVideoAnalyzeResultCallback), CMPSTATEFUNC(OnPyVideoStateCallback))//设置回调函数

C部分是这样的:

int VideoAnalyzeStart(unsigned long ulHandle, AnalyzeDetectedCallback resultCallback, AnalyzeStateCallback stateCallback)
{
    VideoAnalyzeManage* pManager = (VideoAnalyzeManage*)ulHandle;
    if(pManager)
    {
        pManager->SetAnalyzeResultCallback(resultCallback, 0);
        pManager->SetStateNotifyCallback(stateCallback, 0);
        int iRet = pManager->Start();
        return iRet;
    }
    return -1;
}
C部分不用管。

但是如何确定python函数参数与C函数参数的对应关系呢?

python函数参数与C函数参数的对应表(其实也可以叫ctypes类型表):


一个大坑:需要注意CMPRESULTFUNC(OnPyVideoAnalyzeResultCallback)这个指针函数是有自己的生存空间的,如果生存空间已过,会被释放,C代码再回调的时候,就会使用一个过期指针。

这里建议使用一个全局的python指针。

CMPRESULTFUNC = CFUNCTYPE(c_int, c_ulong, c_ulong, c_ulong, c_ulong)
CMPSTATEFUNC = CFUNCTYPE(c_int, c_ulong, c_ulong, c_ulong)

pResutFunc = CMPRESULTFUNC(OnPyVideoAnalyzeResultCallback)
pStateFunc = CMPSTATEFUNC(OnPyVideoStateCallback)


def main():
    global pResutFunc
    global pStateFunc
    ....
    iRet = mylib.VideoAnalyzeStart(ulHandle, pResutFunc, pStateFunc)
见官网的解释: https://docs.python.org/3/library/ctypes.html#ctypes.c_long

Note

 

Make sure you keep references to CFUNCTYPE() objects as long as they are used from C code. ctypes doesn’t, and if you don’t, they may be garbage collected, crashing your program when a callback is made.

Also, note that if the callback function is called in a thread created outside of Python’s control (e.g. by the foreign code that calls the callback), ctypes creates a new dummy Python thread on every invocation. This behavior is correct for most purposes, but it means that values stored with threading.local will not survive across different callbacks, even when those calls are made from the same C thread.

Python Dash是一款用于创建交互式Web应用的框架,它结合了Python的数据科学库(如Pandas、NumPy等)和JavaScript的前端交互能力。如果你想在Dash应用中进行并行计算,通常是在处理大量数据或耗时操作时为了提高用户体验而采用的一种技术。 在 Dash 中并行计算回调函数,通常不会直接支持原生的并行执行,因为 Dash 的回调系统是单线程的,目的是保持界面的响应性。但是你可以通过以下几种方式间接实现类似的效果: 1. **使用 Background Jobs**(后台任务):Dash 提供了一个叫做 `dash.dependencies.Output` 的机制,可以让你将计算放到单独的进程或线程中,然后更新前端视图。例如,可以利用 Python 的 `concurrent.futures` 库来异步执行计算任务,并在回调中更新结果。 ```python import dash from dash.dependencies import Output, Input, State import concurrent.futures app = dash.Dash() def long_running_computation(): # ... 执行耗时计算 ... executor = concurrent.futures.ThreadPoolExecutor(max_workers=1) @app.callback( Output('result', 'children'), [Input('run-button', 'n_clicks')], [State('input-box', 'value')] ) def update_result(n_clicks, value): if n_clicks is not None: future = executor.submit(long_running_computation, value) result = future.result() # 等待计算完 return result ``` 2. **使用 Celery 或其他消息队列**:如果你的应用规模较大,可以考虑使用 Celery 这样的分布式任务队列服务,将长时间运行的任务放入队列,由专门的 worker 来执行,然后通知 Dash 更新前端。 3. **利用 Web Workers**: 虽然 Dash 直接不支持 Web Workers,但你可以借助浏览器提供的这个功能,比如 PWA(Progressive Web App)工具库,如 Workbox,将计算放到 Web Worker 中执行,然后更新页面状态。 尽管如此,由于 Dash 本身的限制,最好还是尽量优化你的代码,避免不必要的长时间计算,因为过多的并行计算可能会导致前端界面响应变慢。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值