C#Winform调用EasyDarwin项目中的libEasyPlayer.dll

来源:互联网 发布:网络配音社团有哪些 编辑:程序博客网 时间:2024/06/14 03:02

本想把这个测试项目优化完再写经验的,想想还是现在就记下来吧,到时候再写恐怕都忘的差不多了,言归正传.

最近在研究EasyDarwin项目,这个开源项目对车载视频监控项目开发来说真是个福音,起码让我这没有门路的人找到了头绪,下载资源学习了解也不短时间了,但由于当前项目的客户端是基于C#开发的,要想将RTSP播放器集成到当前项目里就得考虑C#调用C++的DLL问题了.现在说下引用libEasyPlayer.dll的注意事项:

我的编译环境是VS2017,C#项目基于.NET4.0.

1.下载EasyPlayer-2.0.17.0709版本代码,VS打开会提示升级到WIN10 SDK以及VC++141,升级即可,编译正常.

该项目中包含了libEasyPlayer项目,作者已经很好的将API给包装完好,对外调用的API文件为libEasyPlayer.h文件.

2.创建C# Winform项目,需要注意的是目标平台不要选择AnyCPU,选择X86,即32位应用,大部分C++的DLL都是32位应用,因为这样可以兼容大部分的平台.

3.现在将项目bin目录中的dll文件复制到C#项目的Debug目录下,ImageOle.dll以及npEasyPlayerPlugin.dll这两个DLL文件是不需要的,可以不复制.

4.将C++的API转为C#本地方法在这里需要使用一个工具,微软出的Invoke Interop Assistant,这个工具可以将.h头文件中的API接口函数转为C#的本地方法,但初次使用可能会摸不准门路,昨天我就忙活了半天,上截图

选中第三个TAB页,语言选择CSharp(默认的),在Native Code Snippet框中输入要转换的libEasyPlayer.h头文件代码,#define,#include之类的代码就不要复制进去了,可以看到太复杂的函数还是无法正常转换的,这里的EasyPlayer_OpenStream就不可以了,恰恰这个函数最麻烦...

5.创建一个PlayerMethods本地方法类,将生成的方法复制进去,DllImportAttribute中的<Unknown>改为DLL文件名,即libEasyPlayer.dll,将其它的结构体等新建对应的实体类并复制代码,这里我将一些结构名给改了,其实就是WIN32API中定义的数据,为了防止跟其它定义冲突,重新命名了,比如RECT,Point等...我打算将这些封装成一个C#的DLL偷笑

6.最后还少了一个OpenStream函数没有定义啊,仿照其它生成的方法写一个

[DllImport("libEasyPlayer.dll", EntryPoint = "EasyPlayer_OpenStream")]
public static extern int EasyPlayer_OpenStream(string url, IntPtr hWnd, RENDER_FORMAT renderFormat,
                                         int rtpovertcp, string username, string password, MediaSourceCallBack callback, IntPtr userPtr, bool bHardDecode);

该方法的原型是:

LIB_EASYPLAYER_API int EasyPlayer_OpenStream(const char *url, HWND hWnd, RENDER_FORMAT renderFormat,
    int rtpovertcp, const char *username, const char *password, MediaSourceCallBack callback=NULL, void *userPtr=NULL, bool bHardDecode=true);

其它的好说,但void *userPtr这个参数不好传入,示例应用传入的是窗体,那我先定义为IntPtr传入窗体句柄吧.

MediaSourceCallBack是个回调函数,在此我定义了个委托声明:
public delegate int MediaSourceCallBack(int channelId, IntPtr channelPtr, int frameType, string pBuf, [MarshalAs(UnmanagedType.LPArray)] RTSP_FRAME_INFO[] frameInfo);

原型为:
typedef int (CALLBACK *MediaSourceCallBack)( int _channelId, int *_channelPtr, int _frameType, char *pBuf, RTSP_FRAME_INFO* _frameInfo);

最后的RTSP_FRAME_INFO结构我为什么定义为数组呢?源代码中就没使用数组方式,因为RTSP_FRAME_INFO是结构体,无法传入空,还有一种方法可以定义为RTSP_FRAME_INFO?格式,这样可以为空,目前还未测试过,等测试看看.

到这里大体的代码就搭出来了,在Winform窗体的Load事件中调用EasyPlayer_Init();方法,编译通过,想想都激动啊,这期间我说的可能有遗漏,如果遇到问题请自行排除错误偷笑.

但是,老天爷会让你这么顺利的就调试通过吗?!很明显,不会~!

提示我找不到libEasyPlayer.dll模块,什么鬼,明明在运行目录下的,度娘得知这个DLL应该还有引用的DLL我没复制过去,一查,果然有....复制过去,然后嘞?

还是运行错误,不过错误提示变了,提示找不到EasyPlayer_Init入口点,怎么会?一样的不是,还是度娘啊,用DLL查看器查看入口方法是?XXX@ABC之类的格式,而且用的DLL查看器还没有办法将这些函数复制出来,咋办?其实VS自带了这种工具,打开VS开发人员命令工具,在开始菜单Visual Studio Tools里面找,VS里面直接打开的命令行不好使,应该是我技术问题,进入libEasyPlayer.dll目录,输入dumpbin /exports libEasyPlayer.dll /out:libEasyPlayer.txt命令,即可将入口函数输出到指定的txt目录中,当然如果不想输入文本而只想在命令行中查看的话,可以删掉/out及之后的代码即可.

接下来将正确的入口点写入到各方法的EntryPoint特性中,并整理一下:
        [DllImport("libEasyPlayer.dll", EntryPoint = "?EasyPlayer_Init@@YAHXZ")]
        public static extern int EasyPlayer_Init();

再运行,OK了~!

那么接下来就照搬作者的示例程序写一个窗口代码吧,照搬过来后,编译OK,激动啊,运行,没问题,哈哈~!~!输入RTSP链接,播放....问题来了....出现异常:

调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管的目标签名是否匹配。

这是什么鬼,难道我的OpenStream里定义的委托不对?改了又改,没用,试试其它方法,只要是带参数的都出这个异常,度娘吧,别说现在度娘还是挺给力的,有参数的需要添加一个特性CallingConvention = CallingConvention.Cdecl,这样就OK了,将OpenStream的方法贴出来

/// <summary>/// 打开媒体流/// </summary>/// <param name="url">RTSP流链接</param>/// <param name="hWnd">要绘制视频的窗口句柄</param>/// <param name="renderFormat">绘制方式</param>/// <param name="rtpovertcp">RTC是否为基于TCP方式,0为否,1为是</param>/// <param name="username">用户名</param>/// <param name="password">密码</param>/// <param name="callback">回调方法,用于播放事件通知</param>/// <param name="userPtr">用户自定义指针,一般传入当前的窗体,不是窗体句柄</param>/// <param name="bHardDecode">是否硬件解码,0为否,1为是</param>/// <returns></returns>[DllImport("libEasyPlayer.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "?EasyPlayer_OpenStream@@YAHPBDPAUHWND__@@W4__RENDER_FORMAT@@H00P6GHHPAHHPADPAURTSP_FRAME_INFO@@@ZPAX_N@Z")]public static extern int EasyPlayer_OpenStream(string url, IntPtr hWnd, RENDER_FORMAT renderFormat,int rtpovertcp, string username, string password, MediaSourceCallBack callback, IntPtr userPtr, bool bHardDecode);

至此,C#已经基本正常调用libEasyPlayer.dll库了,上个运行截图:


该项目代码等完善了再上传吧...


原创粉丝点击