Windows软件插件-音视频捕获

发布于:2025-05-17 ⋅ 阅读:(16) ⋅ 点赞:(0)

下载本插件
音视频捕获就是获取电脑外接的话筒,摄像头,或线路输入的音频和视频。
本插件捕获电脑外接的音频和视频。最多可以同时获取4个视频源和4个音频源。插件可以在win32和MFC程序中使用。

使用方法

首先,加载本“捕获”DLL,获得DLL模块句柄。

	HMODULE hCap = LoadLibrary(L"捕获.dll");//加载捕获模块

获取所有导出函数的地址。

typedef int(__cdecl *MYPROC_Sample)(LONGLONG star, LONGLONG end, BYTE* pB, LONG len);

struct CAP_INIT
{
	MYPROC_Sample VideoSample0 = NULL;//视频样本输出函数
	MYPROC_Sample VideoSample1 = NULL;
	MYPROC_Sample VideoSample2 = NULL;
	MYPROC_Sample VideoSample3 = NULL;
	MYPROC_Sample AudioSample0 = NULL;//音频样本输出函数
	MYPROC_Sample AudioSample1 = NULL;
	MYPROC_Sample AudioSample2 = NULL;
	MYPROC_Sample AudioSample3 = NULL;
};
struct CAP_VIDEO_INFO
{
	UINT32 Width=0;//视频宽,单位像素
	UINT32 Height=0;//视频高,单位像素
	double nFrames=0;//帧率
	WCHAR Name[MAX_PATH];//视频捕获设备名称
};
struct CAP_AUDIO_INFO
{
	UINT32 nCh;//声道数
	WCHAR Name[MAX_PATH];//音频捕获设备名称
};

typedef int(__cdecl *MYPROC_CAP_Init)(CAP_INIT init, CAP_VIDEO_INFO*& VideoInfo, CAP_AUDIO_INFO*& AudioInfo);
typedef int(__cdecl *MYPROC_VOID)();

MYPROC_CAP_Init CAP_Init=NULL;
MYPROC_VOID CAP_ReleaseDevice=NULL;

	if (hCap)
	{
		CAP_Init=(MYPROC_CAP_Init)GetProcAddress(hCap, "Init");//“初始化”函数
		CAP_ReleaseDevice=(MYPROC_VOID)GetProcAddress(hCap, "ReleaseDevice");//“释放所有捕获设备”函数
	}

定义8个全局函数,以提供给初始化函数;全局函数用于输出视频和音频样本。

int VideoSample0(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//视频流1样本输出函数
{
	return 1; 
}

int VideoSample1(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//视频流2样本输出函数
{
	return 1; 
}

int VideoSample2(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//视频流3样本输出函数
{
	return 1; 
}

int VideoSample3(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//视频流4样本输出函数
{
	return 1; 
}

int AudioSample0(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//音频流1样本输出函数
{
	return 1; 
}

int AudioSample1(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//音频流2样本输出函数
{
	return 1; 
}

int AudioSample2(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//音频流3样本输出函数
{
	return 1; 
}

int AudioSample3(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//音频流4样本输出函数
{
	return 1; 
}

		CAP_INIT CapInit;
		CapInit.VideoSample0 = &VideoSample0;//为视频,音频源指定输出函数
		CapInit.VideoSample1 = &VideoSample1;
		CapInit.VideoSample2 = &VideoSample2;
		CapInit.VideoSample3 = &VideoSample3;
		CapInit.AudioSample0 = &AudioSample0;
		CapInit.AudioSample1 = &AudioSample1;
		CapInit.AudioSample2 = &AudioSample2;
		CapInit.AudioSample3 = &AudioSample3;

捕获视频和音频。
CAP_Init函数为每一个捕获设备(接口为IMFMediaSource)创建1个源读取器(接口为IMFSourceReader),并指定输出媒体类型;
为每一个捕获设备创建1个线程,在线程中使用源读取器接口读取样本,反复调用CAP_INIT结构提供的样本输出函数输出视频和音频样本。

		CAP_VIDEO_INFO* pVInfo =  new CAP_VIDEO_INFO[4];//结构数组用于存储视频流,音频流信息
		CAP_VIDEO_INFO* pAInfo = new CAP_AUDIO_INFO[4];
		WORD w = CAP_Init(CapInit, pVInfo, pAInfo);//获取捕获设备,创建视频流,音频流
		VCount = LOBYTE(w); //获取视频流数量
		ACount = HIBYTE(w);//获取音频流数量	

释放所有获取到的捕获设备。
函数将终止所有捕获线程,并释放所有源读取器接口。

		CAP_ReleaseDevice();

有关本插件的使用示例,可以看后续文章“Windows应用-音视频捕获”。

“捕获”DLL的全部代码

DLL.h

#pragma once
#include <SDKDDKVer.h>

#define WIN32_LEAN_AND_MEAN             // 从 Windows 头中排除极少使用的资料
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS      // 某些 CString 构造函数将是显式的
#define _AFX_NO_MFC_CONTROLS_IN_DIALOGS         // 移除对话框中的 MFC 控件支持

#ifndef VC_EXTRALEAN
#define VC_EXTRALEAN            // 从 Windows 头中排除极少使用的资料
#endif

#include <afx.h>
#include <afxwin.h>         // MFC 核心组件和标准组件
#include <afxext.h>         // MFC 扩展
#ifndef _AFX_NO_OLE_SUPPORT
#include <afxdtctl.h>           // MFC 对 Internet Explorer 4 公共控件的支持
#endif
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h>                     // MFC 对 Windows 公共控件的支持
#endif // _AFX_NO_AFXCMN_SUPPORT

// Windows 头文件: 
#include <windows.h>

#include "mfidl.h"
#include "mfapi.h"
#pragma comment(lib, "mfuuid")
#pragma comment(lib, "mf")
#pragma comment(lib, "mfplat")

#include "mfreadwrite.h"
#pragma comment(lib, "mfreadwrite")

#ifndef  SAFE_RELEASE
#define SAFE_RELEASE

template <class T> void SafeRelease(T** ppT)
{
	if (*ppT)
	{
		(*ppT)->Release();
		*ppT = NULL;
	}
}

#endif //SAFE_RELEASE

typedef int(__cdecl *MYPROC_Sample)(LONGLONG star, LONGLONG end, BYTE* pB, LONG len);

struct CAP_INIT
{
	MYPROC_Sample VideoSample0 = NULL;//视频样本输出函数
	MYPROC_Sample VideoSample1 = NULL;
	MYPROC_Sample VideoSample2 = NULL;
	MYPROC_Sample VideoSample3 = NULL;
	MYPROC_Sample AudioSample0 = NULL;//音频样本输出函数
	MYPROC_Sample AudioSample1 = NULL;
	MYPROC_Sample AudioSample2 = NULL;
	MYPROC_Sample AudioSample3 = NULL;
};

struct CAP_VIDEO_INFO
{
	UINT32 Width=0;//视频宽,单位像素
	UINT32 Height=0;//视频高,单位像素
	double nFrames=0;//帧率
	WCHAR Name[MAX_PATH];//视频捕获设备名称
};

struct CAP_AUDIO_INFO
{
	UINT32 nCh;//声道数
	WCHAR Name[MAX_PATH];//音频捕获设备名称
};

class Capture
{
public:
	Capture();
	~Capture();
	CAP_INIT mInit;
	CAP_VIDEO_INFO mVideoInfo[4];
	HANDLE hEXIT = NULL;
	HANDLE hVThread[4];
	HANDLE hAThread[4];
	IMFSourceReader* pVideoReader[4];//视频源读取器指针数组
	IMFSourceReader* pAudioReader[4];//音频源读取器指针数组
	void GetDevice(int& mVideo, CAP_VIDEO_INFO* VideoInfo, int& mAudio, CAP_AUDIO_INFO* AudioInfo);//获取前4个音频,视频捕获设备
	void ReleaseDevice();//释放捕获的设备
	int Init(CAP_INIT init, CAP_VIDEO_INFO*& VideoInfo, CAP_AUDIO_INFO*& AudioInfo);//初始化捕获设备
};

struct PARAM
{
	Capture* pCapture = NULL;
	int index;
};

DLL.cpp

#include "DLL.h"


Capture::Capture()
{
	HRESULT hr = MFStartup(MF_VERSION);
	hEXIT = CreateEvent(NULL, TRUE, FALSE, NULL);//创建“退出”事件。手动重置,初始无信号
}

Capture::~Capture()
{
	CloseHandle(hEXIT);
	MFShutdown();
}

DWORD WINAPI AudioThread(LPVOID lp);
DWORD WINAPI VideoThread(LPVOID lp);

void Capture::GetDevice(int& mVideo, CAP_VIDEO_INFO* VideoInfo, int& mAudio, CAP_AUDIO_INFO* AudioInfo)//获取捕获设备
{
	HRESULT hr;
	IMFAttributes *pVideoAttributes = NULL;
	hr = MFCreateAttributes(&pVideoAttributes, 1);
	hr = pVideoAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
	UINT32 countV;
	IMFActivate **ppVideoDevices = NULL;
	hr = MFEnumDeviceSources(pVideoAttributes, &ppVideoDevices, &countV);//枚举视频捕获设备
	SafeRelease(&pVideoAttributes);
	if (countV > 4)
	{
		for (int i = 4; i < (int)countV; i++)//释放4个设备后面的设备
		{
			ppVideoDevices[i]->Release();
		}
		countV = 4;
	}
	mVideo = countV;
	for (DWORD i = 0; i < countV; i++)//获取所有视频源信息,为视频源创建读取样本线程
	{
		hr = S_OK;
		WCHAR *szFriendlyName = NULL;
		UINT32 cchName;
		hr = ppVideoDevices[i]->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &szFriendlyName, &cchName); //获取显示名称
		wmemset(VideoInfo[i].Name, 0, MAX_PATH);
		CopyMemory(VideoInfo[i].Name, szFriendlyName, 2 * (wcslen(szFriendlyName) + 1));
		CoTaskMemFree(szFriendlyName);
		IMFMediaSource *pSource = NULL;
		if (SUCCEEDED(hr))
		{
			hr = ppVideoDevices[i]->ActivateObject(IID_IMFMediaSource, (void**)&pSource);//激活对象
		}
		IMFAttributes* pIMFAttributes = NULL;
		if (SUCCEEDED(hr))
		{
			hr = MFCreateAttributes(&pIMFAttributes, 0);//创建空的属性
		}
		if (SUCCEEDED(hr))
		{
			hr = pIMFAttributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, (UINT32)1);//使用基于硬件的媒体基础转换
		}
		if (SUCCEEDED(hr))
		{
			hr = pIMFAttributes->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, (UINT32)1);//允许源读取器进行有限的视频处理
		}
		hr = MFCreateSourceReaderFromMediaSource(pSource, pIMFAttributes, &pVideoReader[i]);//从媒体源创建源读取器
		SafeRelease(&pIMFAttributes); SafeRelease(&pSource);
		IMFMediaType* pVideoMTV = NULL;
		if (SUCCEEDED(hr))
		{
			hr = MFCreateMediaType(&pVideoMTV);//创建空的媒体类型
		}
		if (SUCCEEDED(hr))
		{
			hr = pVideoMTV->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);//设置主要类型为视频
		}
		if (SUCCEEDED(hr))
		{
			hr = pVideoMTV->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);//设置子类型RGB32
		}
		if (SUCCEEDED(hr))
		{
			hr = pVideoReader[i]->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, pVideoMTV);//设置源读取器视频输出媒体类型
		}
		SafeRelease(&pVideoMTV);
		IMFMediaType* pMT = NULL;
		if (SUCCEEDED(hr))
		{
			hr = pVideoReader[i]->GetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, &pMT);//获取源读取器视频输出媒体类型。此时可以获得完整的输出媒体类型信息
		}
		UINT32 Width, Height;
		if (SUCCEEDED(hr))
		{
			hr = MFGetAttributeSize(pMT, MF_MT_FRAME_SIZE, &Width, &Height);//获取视频宽高
			VideoInfo[i].Width = Width; VideoInfo[i].Height = Height;
		}
		UINT32 Numerator, Denominator;
		if (SUCCEEDED(hr))
		{
			hr = MFGetAttributeRatio(pMT, MF_MT_FRAME_RATE, &Numerator, &Denominator);//获取帧率
			VideoInfo[i].nFrames = (double)Numerator / (double)Denominator;
		}
		SafeRelease(&pMT);
		mVideoInfo[i] = VideoInfo[i];
		PARAM* pParam = new PARAM();
		pParam->index = i;
		pParam->pCapture = this;
		hVThread[i] = CreateThread(NULL, 0, VideoThread, pParam, 0, NULL);//为视频源创建读取线程
	}
	CoTaskMemFree(ppVideoDevices);//释放视频捕获设备接口指针数组使用的内存

	IMFAttributes *pAudioAttributes = NULL;
	hr = MFCreateAttributes(&pAudioAttributes, 1);//创建空的属性
	hr = pAudioAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID);
	UINT32 countA;
	IMFActivate **ppAudioDevices = NULL;
	hr = MFEnumDeviceSources(pAudioAttributes, &ppAudioDevices, &countA);//枚举音频捕获设备
	SafeRelease(&pAudioAttributes);
	if (countA > 4)
	{
		for (int i = 4; i < (int)countA; i++)//释放4个设备后面的设备
		{
			ppAudioDevices[i]->Release();
		}
		countA = 4;
	}
	mAudio = countA;
	for (DWORD i = 0; i < countA; i++)
	{
		hr = S_OK;
		WCHAR *szFriendlyName = NULL;
		UINT32 cchName;
		hr = ppAudioDevices[i]->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &szFriendlyName, &cchName); //获取捕获设备名称
		wmemset(AudioInfo[i].Name, 0, MAX_PATH);
		CopyMemory(AudioInfo[i].Name, szFriendlyName, 2*(wcslen(szFriendlyName) + 1));
		CoTaskMemFree(szFriendlyName);
		IMFMediaSource *pSource = NULL;
		if (SUCCEEDED(hr))
		{
			hr = ppAudioDevices[i]->ActivateObject(IID_IMFMediaSource, (void**)&pSource);//激活对象
		}
		IMFAttributes* pIMFAttributes = NULL;
		if (SUCCEEDED(hr))
		{
			hr = MFCreateAttributes(&pIMFAttributes, 0);
		}
		if (SUCCEEDED(hr))
		{
			hr = pIMFAttributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, (UINT32)1);//使用基于硬件的媒体基础转换
		}
		if (SUCCEEDED(hr))
		{
			hr = pIMFAttributes->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, (UINT32)1);//允许源读取器进行有限的视频处理
		}
		if (SUCCEEDED(hr))
		{
			hr = MFCreateSourceReaderFromMediaSource(pSource, pIMFAttributes, &pAudioReader[i]);//从媒体源创建源读取器
		}
		SafeRelease(&pIMFAttributes);SafeRelease(&pSource);
		IMFMediaType* pAudioMTA = NULL;
		if (SUCCEEDED(hr))
		{
			hr = MFCreateMediaType(&pAudioMTA);//创建空的媒体类型
		}
		if (SUCCEEDED(hr))
		{
			hr = pAudioMTA->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);//设置主要类型音频
		}
		if (SUCCEEDED(hr))
		{
			hr = pAudioMTA->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);//设置子类型PCM
		}
		if (SUCCEEDED(hr))
		{
			hr = pAudioMTA->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, (UINT32)16);//设置样本16位
		}
		if (SUCCEEDED(hr))
		{
			hr = pAudioMTA->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, (UINT32)48000);//设置样本采样率48000
		}
		if (SUCCEEDED(hr))
		{
			hr = pAudioReader[i]->SetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, NULL, pAudioMTA);//设置音频输出媒体类型(此时未提供媒体类型全部信息)
		}
		SafeRelease(&pAudioMTA);
		IMFMediaType* pMT = NULL;
		if (SUCCEEDED(hr))
		{
			hr = pAudioReader[i]->GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, &pMT);//获取音频输出媒体类型,此时可以得到完整的媒体类型信息
		}
		UINT32 nch;
		if (SUCCEEDED(hr))
		{
			hr = pMT->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &nch);//获取声道数
			AudioInfo[i].nCh = nch;
		}
		SafeRelease(&pMT);
		PARAM* pParam = new PARAM();
		pParam->index = i;
		pParam->pCapture = this;
		hAThread[i] = CreateThread(NULL, 0, AudioThread, pParam, 0, NULL);//为音频源创建读取线程
	}
	CoTaskMemFree(ppAudioDevices);//释放音频捕获设备接口指针数组使用的内存
}

DWORD WINAPI VideoThread(LPVOID lp)//视频源读取样本线程共用函数
{
	PARAM* pParam = (PARAM*)lp;
	Capture* pCapture = pParam->pCapture;
	int index = pParam->index;
	HRESULT hr;
	IMFSourceReader* pIMFSourceReader = pCapture->pVideoReader[index];
	double dur = (double)10000000 / pCapture->mVideoInfo[index].nFrames;//计算1帧持续时间
	IMFSample* pMFSample = NULL; DWORD flags;
Agan:
	hr = pIMFSourceReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, NULL, &flags, NULL, &pMFSample);//读取视频样本
	if (hr == S_OK && pMFSample)
	{
		LONGLONG Cur, Dur;
		hr = pMFSample->GetSampleTime(&Cur);//获取显示时间
		hr = pMFSample->GetSampleDuration(&Dur);//获取持续时间
		DWORD Lt;
		hr = pMFSample->GetTotalLength(&Lt);//获取有效长度
		DWORD count;
		hr = pMFSample->GetBufferCount(&count);//获取缓冲区数量
		IMFMediaBuffer* pMFBuffer = NULL;
		if (count == 1)//如果只有1个缓冲区
		{
			hr = pMFSample->GetBufferByIndex(0, &pMFBuffer);
		}
		else//如果有多个缓冲区
		{
			hr = pMFSample->ConvertToContiguousBuffer(&pMFBuffer);
		}
		BYTE* pSB = NULL;
		hr = pMFBuffer->Lock(&pSB, NULL, NULL);//锁定缓冲区
		switch (index)
		{
		case 0://第1个视频源
			pCapture->mInit.VideoSample0(Cur, Cur + Dur, pSB, (LONG)Lt);//发送样本
			break;
		case 1://第2个
			pCapture->mInit.VideoSample1(Cur, Cur + Dur, pSB, (LONG)Lt);
			break;
		case 2:
			pCapture->mInit.VideoSample2(Cur, Cur + Dur, pSB, (LONG)Lt);
			break;
		case 3:
			pCapture->mInit.VideoSample3(Cur, Cur + Dur, pSB, (LONG)Lt);
			break;
		}
		hr = pMFBuffer->Unlock();//解锁缓冲区
		SafeRelease(&pMFBuffer); SafeRelease(&pMFSample);//释放接口
	}
	DWORD mExit = WaitForSingleObject(pCapture->hEXIT, 0);
	if (mExit == WAIT_OBJECT_0)//有“退出”信号
	{
		delete pParam;
		return 0;
	}
	goto Agan;
}

DWORD WINAPI AudioThread(LPVOID lp)//读取音频源样本线程共用函数
{
	PARAM* pParam = (PARAM*)lp;
	Capture* pCapture = pParam->pCapture;
	int index = pParam->index;
	HRESULT hr;
	IMFSourceReader* pIMFSourceReader = pCapture->pAudioReader[index];
	IMFSample* pMFSample = NULL; DWORD flags;
Agan:
	hr = pIMFSourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, NULL, &flags, NULL, &pMFSample);//读取音频样本
	if (hr == S_OK && pMFSample)
	{
		LONGLONG Cur, Dur;
		hr = pMFSample->GetSampleTime(&Cur);//获取显示时间
		hr = pMFSample->GetSampleDuration(&Dur);//获取持续时间
		DWORD Lt;
		hr = pMFSample->GetTotalLength(&Lt);//获取有效长度
		DWORD count;
		hr = pMFSample->GetBufferCount(&count);//获取缓冲区数量
		IMFMediaBuffer* pMFBuffer = NULL;
		if (count == 1)//如果只有1个缓冲区
		{
			hr = pMFSample->GetBufferByIndex(0, &pMFBuffer);
		}
		else//如果有多个缓冲区
		{
			hr = pMFSample->ConvertToContiguousBuffer(&pMFBuffer);
		}
		BYTE* pSB = NULL;
		hr = pMFBuffer->Lock(&pSB, NULL, NULL);//锁定缓冲区
		switch (index)
		{
		case 0://第1个音频源
			pCapture->mInit.AudioSample0(Cur, Cur + Dur, pSB, (LONG)Lt);//发送样本
			break;
		case 1://第2个
			pCapture->mInit.AudioSample1(Cur, Cur + Dur, pSB, (LONG)Lt);
			break;
		case 2:
			pCapture->mInit.AudioSample2(Cur, Cur + Dur, pSB, (LONG)Lt);
			break;
		case 3:
			pCapture->mInit.AudioSample3(Cur, Cur + Dur, pSB, (LONG)Lt);
			break;
		}
		hr = pMFBuffer->Unlock();//解锁缓冲区
		SafeRelease(&pMFBuffer); SafeRelease(&pMFSample);//释放接口
	}
	DWORD mExit = WaitForSingleObject(pCapture->hEXIT, 0);
	if (mExit == WAIT_OBJECT_0)//有“退出”信号
	{
		delete pParam;
		return 0;
	}
	goto Agan;
}

void Capture::ReleaseDevice()
{
	SetEvent(hEXIT);//发送“退出”信号
	WaitForMultipleObjects(4, hVThread, TRUE, INFINITE);//等待所有线程退出
	WaitForMultipleObjects(4, hAThread, TRUE, INFINITE);
	for (int i = 0; i < 4; i++)//释放所有视频音频源读取器接口
	{
		SafeRelease(&pVideoReader[i]); SafeRelease(&pAudioReader[i]);
	}
}

int Capture::Init(CAP_INIT init, CAP_VIDEO_INFO*& VideoInfo, CAP_AUDIO_INFO*& AudioInfo)
{
	mInit = init;
	ResetEvent(hEXIT);//设置“退出”无信号
	int mV, mA;
	GetDevice(mV,VideoInfo, mA,AudioInfo);//获取前4个音频,视频捕获设备
	return (int)MAKEWORD((BYTE)mA, (BYTE)mV);
}

Capture mCapture;

#ifdef __cplusplus    // If used by C++ code, 
extern "C" {          // we need to export the C interface
#endif

	__declspec(dllexport) int __cdecl Init(CAP_INIT init, CAP_VIDEO_INFO*& VideoInfo, CAP_AUDIO_INFO*& AudioInfo)//最多获取前4个视频音频捕获设备
	{
		return mCapture.Init(init, VideoInfo, AudioInfo);
	}

	__declspec(dllexport) int __cdecl ReleaseDevice()//释放获取到的捕获设备
	{
		mCapture.ReleaseDevice();
		return 0;
	}

#ifdef __cplusplus
}
#endif