//环境:win10 + DirectX9.0bSDK
一.准备工作
1.WavDestFilter
//采集音频写入WAV文件需要用到DirectShow的一个Filter,这个Filter用于将音频数据写入完毕时加入WAV格式数据!
//DirectXSDK目录\Samples\C++\DirectShow\Filters\WavDest
//拷贝wavdest.h,wavdest.cpp 这里采用源码拷贝走修改方式使用DirectShow提供的Filter!
2.创建Win32项目
//删除Win32自动生成代码
// DirectShowAudioCaptureWav.cpp : 定义应用程序的入口点。
//
#include "stdafx.h"
#include "DirectShowAudioCaptureWav.h"
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
return 0;
}
//环境配置
// stdafx.h : 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 特定于项目的包含文件
//
#pragma once
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的信息
// Windows 头文件:
#include <windows.h>
// C 运行时头文件
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
// TODO: 在此处引用程序需要的其他头文件
#include "streams.h"
#ifdef _DEBUG
#pragma comment(lib,"strmbasd.lib")
#else
#pragma comment(lib,"STRMBASE.lib")
#endif
//添加wavdest.h,wavdest.cpp
1.修改wavdest.cpp
#include <streams.h> #include "wavdest.h" -> 改成 #include "stdafx.h"
stdafx.h 添加 #include "wavdest.h"
2.挪走GUID代码至wavdest.h
// {3C78B8E2-6C4D-11d1-ADE2-0000F8754B99}
static const GUID CLSID_WavDest =
{ 0x3c78b8e2, 0x6c4d, 0x11d1, { 0xad, 0xe2, 0x0, 0x0, 0xf8, 0x75, 0x4b, 0x99 } };
const AMOVIESETUP_FILTER sudWavDest =
{
&CLSID_WavDest, // clsID
L"WAV Dest", // strName
MERIT_DO_NOT_USE, // dwMerit
0, // nPins
0 // lpPin
};
3.挪走wavdest.cpp 中全局数据
// Global data
CFactoryTemplate g_Templates[]= {
{L"WAV Dest", &CLSID_WavDest, CWavDestFilter::CreateInstance, NULL, &sudWavDest},
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
//此时编译一下看看
我们会发现报错:
stdafx.h 加入:#pragma comment(lib,"winmm.lib")
这里我们就可以正式编写我们的音频采集代码!
二.代码实现
// DirectShowAudioCaptureWav.cpp : 定义应用程序的入口点。
//
#include "stdafx.h"
#include "DirectShowAudioCaptureWav.h"
#include <vector>
#include <map>
using namespace std;
// Global data
CFactoryTemplate g_Templates[] = {
{ L"WAV Dest", &CLSID_WavDest, CWavDestFilter::CreateInstance, NULL, &sudWavDest },
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
///AddFilterToGraph
HRESULT AddFilterToGraph(IGraphBuilder* pGraph, const GUID& clsId, LPCWSTR wstrName, IBaseFilter** pFilter)
{
if (!pGraph || !pFilter)
return E_POINTER;
*pFilter = NULL;
//CreateFilter
IBaseFilter* pBaseFilter = NULL;
HRESULT hr = CoCreateInstance(clsId, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pBaseFilter);
if (SUCCEEDED(hr))
{
//AddFilterToGraph
hr = pGraph->AddFilter(pBaseFilter, wstrName);
if (SUCCEEDED(hr))
{
//SaveFilter
*pFilter = pBaseFilter;
}
else
{
//FreeFilter
pBaseFilter->Release();
}
}
return hr;
}
///GetFilterDirectionPin
HRESULT GetUnconnectedPin(IBaseFilter* pFilter, PIN_DIRECTION pinDirection, IPin** ppPin)
{
if (!pFilter || !ppPin)
return E_POINTER;
//获取pin枚举器
*ppPin = NULL;
IEnumPins* pEnumPin = NULL;
HRESULT hr = pFilter->EnumPins(&pEnumPin);
if (FAILED(hr))
{
return hr;
}
//遍历pin枚举器
IPin* pPin = NULL;
while (pEnumPin->Next(1, &pPin, NULL) == S_OK)
{
//查pin方向
PIN_DIRECTION curPinDirection;
pPin->QueryDirection(&curPinDirection);
if (curPinDirection == pinDirection)
{
IPin* tmpPin = NULL;
//尝试连接
hr = pPin->ConnectedTo(&tmpPin);
if (SUCCEEDED(hr))
{
//可以连接
tmpPin->Release();
}
else
{
pEnumPin->Release();
*ppPin = pPin;
return S_OK;
}
}
}
//释放pin枚举器
pEnumPin->Release();
return E_FAIL;
}
///FilterConnectedToFilter
//PinToFilterPinConnected
HRESULT ConnectedFilters(IGraphBuilder* pGraph, IPin* pOutPin, IBaseFilter* pDstFilter)
{
if (!pGraph || !pOutPin || !pDstFilter)
return E_POINTER;
//获取输入pin
IPin* pInPin = NULL;
HRESULT hr = GetUnconnectedPin(pDstFilter, PINDIR_INPUT, &pInPin);
if (FAILED(hr))
return hr;
//连接输入pin
hr = pGraph->Connect(pOutPin, pInPin);
pInPin->Release();
return hr;
}
//FilterToFilterConnected
HRESULT ConnectedFilters(IGraphBuilder* pGraph, IBaseFilter* pSrcFilter, IBaseFilter* pDstFilter)
{
if (!pGraph || !pSrcFilter || !pDstFilter)
return E_POINTER;
//获取输入pin
IPin* pOutPin = NULL;
HRESULT hr = GetUnconnectedPin(pSrcFilter, PINDIR_OUTPUT, &pOutPin);
if (FAILED(hr))
return hr;
//连接输入pin
hr = ConnectedFilters(pGraph, pOutPin, pDstFilter);
pOutPin->Release();
return hr;
}
HRESULT EnumDeviceInfo(vector<wstring>& devicesNames, map<wstring, IMoniker*>& devicesInfo, const IID InputDeviceCategory)
{
//创建系统设备枚举器
ICreateDevEnum* pSystemDevEnum = NULL;
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pSystemDevEnum);
if (FAILED(hr))
{
return hr;
}
//创建设备枚举器
IEnumMoniker* pEnumMoniker = NULL;
hr = pSystemDevEnum->CreateClassEnumerator(InputDeviceCategory, &pEnumMoniker, 0);
if (hr == S_OK)
{
IMoniker* pMoniker = NULL;
ULONG cFetched;
while (pEnumMoniker->Next(1, &pMoniker, &cFetched) == S_OK)
{
IPropertyBag* pPropertyBag = NULL;
hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pPropertyBag);
if (SUCCEEDED(hr))
{
//获取设备信息
VARIANT varName;
VariantInit(&varName);
hr = pPropertyBag->Read(L"FriendlyName", &varName, 0); //获取友好名
if (SUCCEEDED(hr))
{
//保存设备信息
devicesNames.push_back(varName.bstrVal);
devicesInfo.insert(pair<wstring, IMoniker*>(varName.bstrVal, pMoniker));
}
VariantClear(&varName);
}
else
{
return hr;
}
pPropertyBag->Release();
}
}
pEnumMoniker->Release();
pSystemDevEnum->Release();
return hr;
}
HRESULT FindAudioCaptureFilter(IMoniker* pMoniker, IBaseFilter** pFilter)
{
if (!pFilter || !pMoniker)
return E_POINTER;
*pFilter = NULL;
HRESULT hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)pFilter);
if (FAILED(hr))
{
return E_FAIL;
}
return S_OK;
}
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
///COM_Begin:
if (FAILED(::CoInitialize(NULL)))
{
MessageBox(NULL, L"CoInitialize failed!", L"ErrWnd", NULL);
return 0;
}
///FilterGraph IGraphBuilder
IGraphBuilder* pGraph = NULL;
IBaseFilter* pAudioCaptureFilter = NULL,*pDestWavFilter = NULL, *pWriteFilter = NULL;
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&pGraph);
if (FAILED(hr))
{
MessageBox(NULL, L"IFilterGraph failed!", L"ErrWnd", NULL);
return 0;
}
///AudioCaptureFilter
vector<wstring> audioDeviceNamesVector;
map<wstring, IMoniker*> audioDeviceFriendlyMap;
EnumDeviceInfo(audioDeviceNamesVector, audioDeviceFriendlyMap, CLSID_AudioInputDeviceCategory);
hr = FindAudioCaptureFilter(audioDeviceFriendlyMap[audioDeviceNamesVector[0]], &pAudioCaptureFilter);
if (FAILED(hr))
{
MessageBox(NULL, L"FindAudioCaptureFilter failed!", L"ErrWnd", NULL);
return 0;
}
pGraph->AddFilter(pAudioCaptureFilter, L"AudioCaptureFilter");
///WavDestFilter
pDestWavFilter = new CWavDestFilter(NULL, &hr);
if (FAILED(hr))
{
MessageBox(NULL, L"WavDestFilter failed!", L"ErrWnd", NULL);
return 0;
}
pGraph->AddFilter(pDestWavFilter, L"WavDestFilter");
///WriteFilter
hr = AddFilterToGraph(pGraph, CLSID_FileWriter, L"WriteFilter", &pWriteFilter);
if (FAILED(hr))
{
MessageBox(NULL, L"WriteFilter failed!", L"ErrWnd", NULL);
return 0;
}
IFileSinkFilter* pFileSinkFilter;
hr = pWriteFilter->QueryInterface(IID_IFileSinkFilter, (void**)&pFileSinkFilter);
if (FAILED(hr))
{
MessageBox(NULL, L"IFileSinkFilter failed!", L"ErrWnd", NULL);
return 0;
}
pFileSinkFilter->SetFileName(L"E:\\TestFile\\AudioCapture.wav", NULL);
///ConnectedFilters
ConnectedFilters(pGraph, pAudioCaptureFilter, pDestWavFilter);
ConnectedFilters(pGraph, pDestWavFilter, pWriteFilter);
///MediaControl
IMediaControl* pControl = NULL;
hr = pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl);
if (FAILED(hr))
{
MessageBox(NULL, L"QueryInterface IMediaControl Failed!", L"ErrWnd", NULL);
return 0;
}
//
hr = pControl->Run();
if (SUCCEEDED(hr))
{
Sleep(10000);
pControl->Stop();
}
///COM_End:
::CoUninitialize();
return 0;
}
//上面API函数:均是DirectShow教程中封装的函数,我们可以封装起来拿来用!