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库了,上个运行截图:
该项目代码等完善了再上传吧...
- C#Winform调用EasyDarwin项目中的libEasyPlayer.dll
- C#Winform调用libEasyPlayer总结
- winform(C#)调用dll
- winform(C#)调用dll
- C++项目调用C#dll项目
- C++/CLI Winform中调用DLL的三种方法
- EasyClient libEasyPlayer中的回调机制介绍
- winform中的调用SQLite
- C#(.net)中的DllImport 调用C/VC DLL
- VS2017 Winform调用dll程序打包
- C# 调用C++dll中的结构体的定义
- C#调用非托管C++DLL中的函数
- Java JNI调用dll中的C代码的流程
- 调用DLL中的窗体
- 调用DLL中的窗体
- 调用DLL中的窗体
- c#系统DLL调用
- C#调用C++DLL
- 跟我一起写 Makefile
- BZOJ 4894: 天赋 有向图生成树计数
- C++类中指针成员的管理(值型类、智能指针)
- 机器学习笔记week5(Andrew NG)
- 解题报告:LightOJ_1406 状压DP
- C#Winform调用EasyDarwin项目中的libEasyPlayer.dll
- matlab的c程序接口mexFunction函数剖析
- 可以删除吗QAQ
- 阿里云旺自定义消息和首次打开聊天界面自动发送消息的实现
- 每日英语阅读(四十五)
- 0—1背包问题
- Android 中能够作为 Log 开关的一些操作以及安全性浅谈
- jsday08(大小写 跟随广告 需要加上"px" 级联菜单 添加附件 删除附件)
- MFC学习笔记-界面01