下载本插件
音视频捕获就是获取电脑外接的话筒,摄像头,或线路输入的音频和视频。
本插件捕获电脑外接的音频和视频。最多可以同时获取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