1、VLC代碼封裝
1.1 QT(C++)工程
首先需要配置可使用 VLC 正常播放的 QT(C++)工程,獲取VLC每一幀并渲染到Qwidget
Libvlcapi
public static class LIBVLCAPI
{
#region[libvlc.dll 導(dǎo)出函數(shù)]
// 創(chuàng)建一個libvlc實例,它是引用計數(shù)的
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
private static extern IntPtr libvlc_new(int argc, IntPtr argv);
// 釋放libvlc實例
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
public static extern void libvlc_release(IntPtr libvlc_instance);
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
public static extern String libvlc_get_version();
// 從視頻來源(例如Url)構(gòu)建一個libvlc_meida
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
private static extern IntPtr libvlc_media_new_location(IntPtr libvlc_instance, IntPtr path);
// 從視頻來源(例如Url)抓圖
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
private static extern int libvlc_video_take_snapshot(IntPtr libvlc_mediaplayer, int num, IntPtr filepath, int i_width, int i_height);
// 從本地文件路徑構(gòu)建一個libvlc_media
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
private static extern IntPtr libvlc_media_new_path(IntPtr libvlc_instance, IntPtr path);
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
public static extern void libvlc_media_release(IntPtr libvlc_media_inst);
// 創(chuàng)建libvlc_media_player(播放核心)
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
public static extern IntPtr libvlc_media_player_new(IntPtr libvlc_instance);
// 將視頻(libvlc_media)綁定到播放器上
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
public static extern void libvlc_media_player_set_media(IntPtr libvlc_media_player, IntPtr libvlc_media);
// 設(shè)置圖像輸出的窗口
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
public static extern void libvlc_media_player_set_hwnd(IntPtr libvlc_mediaplayer, Int32 drawable);
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
public static extern void libvlc_media_player_play(IntPtr libvlc_mediaplayer);
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
public static extern void libvlc_media_player_pause(IntPtr libvlc_mediaplayer);
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
public static extern void libvlc_media_player_stop(IntPtr libvlc_mediaplayer);
// 解析視頻資源的媒體信息(如時長等)
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
public static extern void libvlc_media_parse(IntPtr libvlc_media);
// 返回視頻的時長(必須先調(diào)用libvlc_media_parse之后,該函數(shù)才會生效)
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
public static extern Int64 libvlc_media_get_duration(IntPtr libvlc_media);
// 當(dāng)前播放的時間
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
public static extern Int64 libvlc_media_player_get_time(IntPtr libvlc_mediaplayer);
// 設(shè)置播放位置(拖動)
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
public static extern void libvlc_media_player_set_time(IntPtr libvlc_mediaplayer, Int64 time);
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
public static extern void libvlc_media_player_release(IntPtr libvlc_mediaplayer);
// 獲取和設(shè)置音量
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
public static extern int libvlc_audio_get_volume(IntPtr libvlc_media_player);
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
public static extern void libvlc_audio_set_volume(IntPtr libvlc_media_player, int volume);
// 設(shè)置全屏
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
public static extern void libvlc_set_fullscreen(IntPtr libvlc_media_player, int isFullScreen);
// 設(shè)置屏幕因子
[DllImport("libvlc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[SuppressUnmanagedCodeSecurity]
public static extern void libvlc_video_set_scale(IntPtr libvlc_media_player, float f_factor);
#endregion
#region[VLC方法]
public struct PointerToArrayOfPointerHelper
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)]
public IntPtr[] pointers;
}
public static IntPtr libvlc_new(string[] arguments)
{
PointerToArrayOfPointerHelper argv = new PointerToArrayOfPointerHelper();
argv.pointers = new IntPtr[11];
for (int i = 0; i < arguments.Length; i++)
{
argv.pointers[i] = Marshal.StringToHGlobalAnsi(arguments[i]);
}
IntPtr argvPtr = IntPtr.Zero;
try
{
int size = Marshal.SizeOf(typeof(PointerToArrayOfPointerHelper));
argvPtr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(argv, argvPtr, false);
return libvlc_new(arguments.Length, argvPtr);
}
finally
{
for (int i = 0; i < arguments.Length + 1; i++)
{
if (argv.pointers[i] != IntPtr.Zero)
{
Marshal.FreeHGlobal(argv.pointers[i]);
}
}
if (argvPtr != IntPtr.Zero)
{
Marshal.FreeHGlobal(argvPtr);
}
}
}
public static IntPtr libvlc_media_new_path(IntPtr libvlc_instance, string path)
{
IntPtr pMrl = IntPtr.Zero;
try
{
byte[] bytes = Encoding.UTF8.GetBytes(path);
pMrl = Marshal.AllocHGlobal(bytes.Length + 1);
Marshal.Copy(bytes, 0, pMrl, bytes.Length);
Marshal.WriteByte(pMrl, bytes.Length, 0);
return libvlc_media_new_path(libvlc_instance, pMrl);
}
finally
{
if (pMrl != IntPtr.Zero)
{
Marshal.FreeHGlobal(pMrl);
}
}
}
public static int libvlc_video_take_snapshot(IntPtr libvlc_mediaplayer, int num, string path, int width, int height)
{
IntPtr pMrl = IntPtr.Zero;
try
{
byte[] bytes = Encoding.UTF8.GetBytes(path);
pMrl = Marshal.AllocHGlobal(bytes.Length + 1);
Marshal.Copy(bytes, 0, pMrl, bytes.Length);
Marshal.WriteByte(pMrl, bytes.Length, 0);
return libvlc_video_take_snapshot(libvlc_mediaplayer, num, pMrl, width, height);
}
finally
{
if (pMrl != IntPtr.Zero)
{
Marshal.FreeHGlobal(pMrl);
}
}
}
public static IntPtr libvlc_media_new_location(IntPtr libvlc_instance, string path)
{
IntPtr pMrl = IntPtr.Zero;
try
{
byte[] bytes = Encoding.UTF8.GetBytes(path);
pMrl = Marshal.AllocHGlobal(bytes.Length + 1);
Marshal.Copy(bytes, 0, pMrl, bytes.Length);
Marshal.WriteByte(pMrl, bytes.Length, 0);
return libvlc_media_new_path(libvlc_instance, pMrl);
}
finally
{
if (pMrl != IntPtr.Zero)
{
Marshal.FreeHGlobal(pMrl);
}
}
}
#endregion
}
VLCPlayer
public class VLCPlayer
{
private IntPtr libvlc_instance_;
private IntPtr libvlc_media_player_;
private double duration_;
public VLCPlayer(string pluginPath, bool is_record)
{
if (is_record == true)
{
string plugin_arg = "--plugin-path=" + pluginPath;
string filename = "c:\\" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".mp4";
string record_paramter = "--sout=#duplicate{dst=display,dst=std{accs=file,mux=ts,dst=" + filename;
string[] arguments = { "-I", "--fullscreen", "dummy", "--ignore-config", "--no-video-title", "--width=100", "--height=100", plugin_arg, record_paramter };// "--sout=#duplicate{dst=display,dst=std{accs=file,mux=ts,dst=c:\\1.mp4" };
libvlc_instance_ = LIBVLCAPI.libvlc_new(arguments);
libvlc_media_player_ = LIBVLCAPI.libvlc_media_player_new(libvlc_instance_);
}
else
{
string plugin_arg = "--plugin-path=" + pluginPath;
string[] arguments = { "-I", "--fullscreen", "dummy", "--ignore-config", "--no-video-title", plugin_arg };
libvlc_instance_ = LIBVLCAPI.libvlc_new(arguments);
libvlc_media_player_ = LIBVLCAPI.libvlc_media_player_new(libvlc_instance_);
//float f1=0.1f;
//LIBVLCAPI.libvlc_video_set_scale(libvlc_media_player_,f1);
}
}
public void Vlc_release()
{
if (libvlc_instance_ != IntPtr.Zero)
LIBVLCAPI.libvlc_release(libvlc_instance_);
// if (libvlc_media_player_ != IntPtr.Zero)
// LIBVLCAPI.libvlc_media_release(libvlc_media_player_);
}
public void SetRenderWindow(int wndHandle)
{
if (libvlc_instance_ != IntPtr.Zero && wndHandle != 0)
{
LIBVLCAPI.libvlc_media_player_set_hwnd(libvlc_media_player_, wndHandle);
}
}
public void PlayFile(string filePath)
{
IntPtr libvlc_media = LIBVLCAPI.libvlc_media_new_path(libvlc_instance_, filePath);
if (libvlc_media != IntPtr.Zero)
{
LIBVLCAPI.libvlc_media_parse(libvlc_media);
duration_ = LIBVLCAPI.libvlc_media_get_duration(libvlc_media) / 1000.0;
LIBVLCAPI.libvlc_media_player_set_media(libvlc_media_player_, libvlc_media);
LIBVLCAPI.libvlc_media_release(libvlc_media);
LIBVLCAPI.libvlc_media_player_play(libvlc_media_player_);
}
}
public void PlayFile_rtsp(string filePath)//libvlc_media_new_location
{
IntPtr libvlc_media = LIBVLCAPI.libvlc_media_new_location(libvlc_instance_, filePath);
if (libvlc_media != IntPtr.Zero)
{
// LIBVLCAPI.libvlc_media_parse(libvlc_media);
// duration_ = LIBVLCAPI.libvlc_media_get_duration(libvlc_media) / 1000.0;
LIBVLCAPI.libvlc_media_player_set_media(libvlc_media_player_, libvlc_media);
LIBVLCAPI.libvlc_media_release(libvlc_media);
LIBVLCAPI.libvlc_media_player_play(libvlc_media_player_);
}
}
public void Pause()
{
if (libvlc_media_player_ != IntPtr.Zero)
{
LIBVLCAPI.libvlc_media_player_pause(libvlc_media_player_);
}
}
public void take_snapshot()
{
if (libvlc_media_player_ != IntPtr.Zero)
{
string filepath = "c:\\";
LIBVLCAPI.libvlc_video_take_snapshot(libvlc_media_player_, 0, filepath, 0, 0);
}
}
public void full_screen()
{
if (libvlc_media_player_ != IntPtr.Zero)
{
LIBVLCAPI.libvlc_set_fullscreen(libvlc_media_player_, 0);
}
}
public void Stop()
{
if (libvlc_media_player_ != IntPtr.Zero)
{
LIBVLCAPI.libvlc_media_player_stop(libvlc_media_player_);
}
}
public double GetPlayTime()
{
return LIBVLCAPI.libvlc_media_player_get_time(libvlc_media_player_) / 1000.0;
}
public void SetPlayTime(double seekTime)
{
LIBVLCAPI.libvlc_media_player_set_time(libvlc_media_player_, (Int64)(seekTime * 1000));
}
public int GetVolume()
{
return LIBVLCAPI.libvlc_audio_get_volume(libvlc_media_player_);
}
public void SetVolume(int volume)
{
LIBVLCAPI.libvlc_audio_set_volume(libvlc_media_player_, volume);
}
public void SetFullScreen(bool istrue)
{
LIBVLCAPI.libvlc_set_fullscreen(libvlc_media_player_, istrue ? 1 : 0);
}
public double Duration()
{
return duration_;
}
public string Version()
{
return LIBVLCAPI.libvlc_get_version();
}
}
1.2static 聲明 m_instance 優(yōu)化效率
如下:
#pragma once
#include <memory>
#include <basetsd.h>
typedef SSIZE_T ssize_t;
#include "vlc/vlc.h"
#include <mutex>
struct libvlc_media_track_info_t;
struct libvlc_media_t;
struct libvlc_instance_t;
struct libvlc_media_player_t;
struct libvlc_event_t;
class context;
enum MediaState {
NothingSpecial = 0,
Opening = 1,
Buffering = 2,
Playing = 3,
Paused = 4,
Stopped = 5,
Ended = 6,
Error = 7
};
class TestVlcVideo
{
public:
TestVlcVideo();
void init( std::function<void(int)> eventCallback);
void setHwnd(const int64_t iHwnd) ;
bool loadMedia(const char* &url) ;
int play() ;
void pause() ;
void stop() ;
void setRatio(const char* &ratio) ;
int getVolume() ;
int setVolume(const int volume) ;
int getMediaState() ;
libvlc_instance_t * getVlcInstance();
libvlc_media_player_t * getVlcMediaPlayer();
private:
static void vlcEvents(const libvlc_event_t *ev, void *param);
static libvlc_instance_t *m_instance;
libvlc_media_player_t *m_mediaPlayer = nullptr;
int64_t m_durationMS;
std::function<void(int)> m_eventCallback;
MediaState m_currentMediaState;
};
上面 static 聲明的 m_instance 是為了優(yōu)化效率,不必每次播放視頻的時候都新建。
這是第二步工作。
1.3封裝 DLL
需要封裝真正的 DLL 了,向C#暴露的也是這個類里面的方法。
#pragma once
typedef int(*CallBackMediaState)(int);
#ifdef DLLVLC_EXPORTS // 用來導(dǎo)出函數(shù)
#define DLLVLC_API __declspec(dllexport)
#else // 用來標(biāo)識為導(dǎo)入函數(shù),對于引用該頭文件的外部模塊來說dllimport這個標(biāo)記對編譯優(yōu)化有作用
#define DLLVLC_API __declspec(dllimport)
#endif
#include "Testvlcvideo.h"
namespace TestVLCDLL {
extern "C" {
/*
* @brief VLC Instance和Player實例初始化
* @param CallBackMediaState callback 回調(diào)函數(shù)為媒體播放狀態(tài)
* @return 每次vlcInit會返回一個VLC的Player ID,此ID唯一,后面的接口都需要此ID找到對應(yīng)的Player
*/
DLLVLC_API int vlcInit(CallBackMediaState callback);
/*
* @brief VLC 媒體加載接口
* @param int index PlayerID
* @param const char *path 媒體路徑
*/
DLLVLC_API bool vlcLoad(int index, const char *path);
/*
* @brief 設(shè)置句柄,如不設(shè)置則為默認(rèn)窗口播放
* @param const int64_t iHwnd windows窗口句柄
*/
DLLVLC_API bool vlcSetHwnd(int index,const int64_t iHwnd);
DLLVLC_API bool play(int index);
DLLVLC_API bool pause(int index);
DLLVLC_API bool stop(int index);
/*
* @brief 設(shè)置播放窗口比例
* @param 形如 16:9 4:3 等字符串
*/
DLLVLC_API bool setRatio(int index,const char* ratio);
/*
* @brief 設(shè)置媒體播放音量
*/
DLLVLC_API bool setVolume(int index, int volume);
/*
* @brief 獲取媒體總時長
*/
DLLVLC_API int64_t getMediaLength(int index);
/*
* @brief 獲取當(dāng)前播放狀態(tài)
*/
DLLVLC_API int getMediaState(int index);
/*
* @brief 銷毀VLC Player
*/
DLLVLC_API bool vlcDisponse(int index);
}
}
首先在最開始定義了 CallBackMediaState
回調(diào)函數(shù),對應(yīng)C++ 層使用函數(shù)指針和std::function
都可以。然后使用 DLLVLC_EXPORTS
指示本類為導(dǎo)出類,然后再使用 DLLVLC_API
宏定義導(dǎo)出函數(shù),這些方法都是 dll
暴露給外部調(diào)用的方法。
1.4應(yīng)用程序的導(dǎo)出函數(shù)
// DLLVLC.cpp : 定義 DLL 應(yīng)用程序的導(dǎo)出函數(shù)。
#define DLLVLC_EXPORTS
#include "DLLVLC.h"
#include "Testvlcvideo.h"
#include <iostream>
#include <map>
#include <mutex>
#include <atomic>
std::map<int, TestVlcVideo*> g_mapVLC;
std::atomic_int g_iIndex = 0;
std::mutex g_mt;
DLLVLC_API int TestVLCDLL::vlcInit(CallBackMediaState callback)
{
//如果是初次調(diào)用,則初始化instance,否則復(fù)用instance
std::lock_guard<std::mutex> l(g_mt);
++g_iIndex;
TestVlcVideo *vlcVideo = new TestVlcVideo;
g_mapVLC.emplace(g_iIndex, vlcVideo);
g_mapVLC.at(g_iIndex)->init(callback);
return g_iIndex;
}
DLLVLC_API bool TestVLCDLL::play(int index)
{
std::lock_guard<std::mutex> l(g_mt);
TestVlcVideo *vlcVideo = g_mapVLC.at(index);
if (nullptr == vlcVideo)
{
return false;
}
vlcVideo->play();
return true;
}
.......
因為我們采用的是導(dǎo)出接口方法,而不是導(dǎo)出類(導(dǎo)出類比較麻煩,自己測試未能成功),因此在制作 dll 庫時,使用靜態(tài) map 保存相關(guān)實例,使用對應(yīng)的 init方法和 dispose 方法借助 id 參數(shù)創(chuàng)建和銷毀對象。
1.5 vlc 簡單封裝的具體實現(xiàn)
下來再看下我們第一段代碼的 cpp 文件,就是 vlc 簡單封裝的具體實現(xiàn):
#include "Testvlcvideo.h"
#include <iostream>
libvlc_instance_t *TestVlcVideo::m_instance = nullptr;
TestVlcVideo::TestVlcVideo()
: m_mediaPlayer(nullptr)
, m_durationMS(0)
, m_eventCallback(nullptr)
{
}
void TestVlcVideo::init(std::function<void(int)> eventCallback)
{
getVlcInstance();
{
getVlcMediaPlayer();
libvlc_event_manager_t *em = libvlc_media_player_event_manager(m_mediaPlayer);
{
libvlc_event_attach(em, libvlc_MediaPlayerPlaying, vlcEvents, this);
libvlc_event_attach(em, libvlc_MediaPlayerPaused, vlcEvents, this);
libvlc_event_attach(em, libvlc_MediaPlayerStopped, vlcEvents, this);
libvlc_event_attach(em, libvlc_MediaPlayerNothingSpecial, vlcEvents, this);
libvlc_event_attach(em, libvlc_MediaPlayerOpening, vlcEvents, this);
libvlc_event_attach(em, libvlc_MediaPlayerBuffering, vlcEvents, this);
libvlc_event_attach(em, libvlc_MediaPlayerEndReached, vlcEvents, this);
libvlc_event_attach(em, libvlc_MediaPlayerPositionChanged, vlcEvents, this);
}
m_eventCallback = std::move(eventCallback);
}
}
void TestVlcVideo::setHwnd(const int64_t iHwnd)
{
libvlc_media_player_set_hwnd(m_mediaPlayer, (void *)iHwnd);
}
bool TestVlcVideo::loadMedia(const char* &url)
{
libvlc_media_t *m_media = nullptr;
std::string url_ = url;
if (url_.find("://") == std::string::npos)
{
m_media = libvlc_media_new_path(getVlcInstance (), url);
}
else
{
m_media = libvlc_media_new_location(getVlcInstance(), url);
}
if (nullptr == m_media)
{
m_currentMediaState = MediaState::Error;
return false;
}
libvlc_media_player_set_media(getVlcMediaPlayer (), m_media);
libvlc_media_parse(m_media);
m_durationMS = libvlc_media_get_duration(m_media);
libvlc_media_release(m_media);
return true;
}
libvlc_instance_t * TestVlcVideo::getVlcInstance()
{
if (nullptr == m_instance)
{
m_instance = libvlc_new(0, NULL);
}
return m_instance;
}
libvlc_media_player_t * TestVlcVideo::getVlcMediaPlayer()
{
if (nullptr == m_mediaPlayer)
{
m_mediaPlayer = libvlc_media_player_new(m_instance);
}
return m_mediaPlayer;
}
int TestVlcVideo::play()
{
return libvlc_media_player_play(m_mediaPlayer);
}
void TestVlcVideo::pause()
{
if(libvlc_media_player_is_playing(m_mediaPlayer))
{
libvlc_media_player_set_pause(m_mediaPlayer, 1);
}
else
{
libvlc_media_player_set_pause(m_mediaPlayer, 0);
}
}
到這兒,一般情況下我們還需要配置 def
文件,以避免導(dǎo)出的函數(shù)名被增加額外的信息,而不是簡短的“play
”等。但是可以看到我們在所有的導(dǎo)出函數(shù)前增加了“extern C
”標(biāo)識。意思是這些函數(shù)按照 C
標(biāo)準(zhǔn)進行編譯,由于C++ 的函數(shù)重載,再加上各個編譯器的不同,導(dǎo)致編譯而出的函數(shù)名被(mangled name
),且各不相同,但是C不支持重載,因此采用統(tǒng)一的編譯規(guī)定,同時也可以保證此函數(shù)被 C
正確調(diào)用,所以我們就無需寫 def
文件也可以保證函數(shù)名不被破壞。
2、C# 調(diào)用
上面簡要說完了 C++ 端關(guān)于 DLL 的封裝,再總結(jié)一下大概就是這幾點:
- 至少需要兩個文件,一個是自己對具體實現(xiàn)的封裝類,一個是導(dǎo)出方法文件,本文中我們沒有使用類,而是直接導(dǎo)出函數(shù)。
- 回調(diào)函數(shù)像這樣 typedef int(*CallBackMediaState)(int); 去定義。
- 導(dǎo)出文件添加宏 dllexport
#ifdef DLLVLC_EXPORTS // 用來導(dǎo)出函數(shù)
#define DLLVLC_API __declspec(dllexport)
#else // 用來標(biāo)識為導(dǎo)入函數(shù),對于引用該頭文件的外部模塊來說dllimport這個標(biāo)記對編譯優(yōu)化有作用
#define DLLVLC_API __declspec(dllimport)
#endif
導(dǎo)出函數(shù)添加 extern "C" DLLVLC_API 聲明。
2.1C# 回調(diào)函數(shù)聲明與定義
[DllImport(@"C:\Users\HiWin10\Desktop\DLLVLC\DLLVLC\DLLVLC\x64\Release\DLLVLC.dll", EntryPoint = "vlcInit",
SetLastError = true,
CharSet = CharSet.Ansi,
ExactSpelling = false,
CallingConvention = CallingConvention.Cdecl)]
public extern static int vlcInit(DllcallBack pfun);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int DllcallBack(int MediaState);
C# 的回調(diào)函數(shù)即為委托,需要提前定義委托 DllcallBack ,然后我們假定它是被 C++ 認(rèn)可的,作為參數(shù)傳入 vlcInit。在下面我們需要寫此委托函數(shù)具體的實現(xiàn):
public static int CsharpCall(int MediaState)
{
Console.WriteLine(MediaState);
return MediaState;
}
使用的時候:
static int index;
static void Main(string[] args)
{
DllcallBack mycall;
mycall = new DllcallBack(Program.CsharpCall);
index = vlcInit(mycall);
......
}
經(jīng)過驗證,此種方式的回調(diào)函數(shù)能被 C++ 承認(rèn),對應(yīng)于C++的 std::function。
2.2C# 導(dǎo)出普通函數(shù)調(diào)用
[DllImport(@"C:\Users\HiWin10\Desktop\DLLVLC\DLLVLC\DLLVLC\x64\Release\DLLVLC.dll", EntryPoint = "vlcLoad",
CallingConvention = CallingConvention.Cdecl)]
public extern static bool vlcLoad(int index, string path);
[DllImport(@"C:\Users\HiWin10\Desktop\DLLVLC\DLLVLC\DLLVLC\x64\Release\DLLVLC.dll", EntryPoint = "vlcSetHwnd",
CallingConvention = CallingConvention.Cdecl)]
public extern static bool vlcSetHwnd(int index, int iHwnd);
上面是 C# 關(guān)于普通導(dǎo)出函數(shù)的加載方法,在 main 函數(shù)中直接進行調(diào)用即可。
static int index;
static void Main(string[] args)
{
DllcallBack mycall;
mycall = new DllcallBack(Program.CsharpCall);
index = vlcInit(mycall);
Console.WriteLine(vlcLoad(index, @"D:\1.mp4"));
Console.WriteLine(getMediaLength(index));
play(index);
setRatio(index,"16:9");
其實 C#
端的調(diào)用還是比較簡單的,上面的方式是采用靜態(tài)加載的方式,需要將C++ 的 dll
放到 C# 工程 bin
目錄下,而動態(tài)加載的方式筆者未進行嘗試。
整個過程就完成了,使用 C++ 封裝的方式可以使用一些只支持 C++,只有 C++ API 的強大庫,也可以防止反編譯,還可以使代碼更好的分層等。
下面附上demo鏈接,有需要的小伙伴可下載運行(VS2015)
DLLVLCfor_jb51.rar
到此這篇關(guān)于C++ 封裝 DLL 供 C# 調(diào)用詳細(xì)介紹的文章就介紹到這了,更多相關(guān)C++ 封裝 DLL 供 C# 調(diào)用內(nèi)容請搜索html5模板網(wǎng)以前的文章希望大家以后多多支持html5模板網(wǎng)!