我正在尝试将音频功能添加到捕获源过滤器,以便制作带有音频的虚拟摄像头。从TMH和rdp的代码开始,我用另一个称为“音频”的引脚对其进行了扩展:
CUnknown * WINAPI CVCam::CreateInstance(LPUNKNOWN lpunk, HRESULT *phr)
{
ASSERT(phr);
CUnknown *punk = new CVCam(lpunk, phr);
return punk;
}
CVCam::CVCam(LPUNKNOWN lpunk, HRESULT *phr) : CSource(LPCSTR(FILTER_NAME), lpunk, CLSID_VirtualCam)
{
ASSERT(phr);
CAutoLock cAutoLock(&m_cStateLock);
m_paStreams = (CSourceStream **) new CVCamStream*[2];
m_paStreams[0] = new CVCamStream(phr, this, L"Video");
m_paStreams[1] = new CVAudioStream(phr, this, L"Audio");
}
HRESULT CVCam::QueryInterface(REFIID riid, void **ppv)
{
if (riid == _uuidof(IAMStreamConfig) || riid == _uuidof(IKsPropertySet))
{
HRESULT hr;
hr = m_paStreams[0]->QueryInterface(riid, ppv);
if (hr != S_OK) return hr;
hr = m_paStreams[1]->QueryInterface(riid, ppv);
if (hr != S_OK) return hr;
}
else return CSource::QueryInterface(riid, ppv);
return S_OK;
}
CVAudioStream::CVAudioStream(HRESULT *phr, CVCam *pParent, LPCWSTR pPinName) : CSourceStream(LPCSTR(pPinName), phr, pParent, pPinName), m_pParent(pParent)
{
GetMediaType(0, &m_mt);
}
CVAudioStream::~CVAudioStream()
{
}
HRESULT CVAudioStream::QueryInterface(REFIID riid, void **ppv)
{
if (riid == _uuidof(IAMStreamConfig)) *ppv = (IAMStreamConfig*)this;
else if (riid == _uuidof(IKsPropertySet)) *ppv = (IKsPropertySet*)this;
else if (riid == _uuidof(IAMBufferNegotiation)) *ppv = (IAMBufferNegotiation*)this;
else return CSourceStream::QueryInterface(riid, ppv);
AddRef();
return S_OK;
}
HRESULT CVAudioStream::FillBuffer(IMediaSample *pms)
{
// fill buffer with Windows audio samples
return NOERROR;
}
STDMETHODIMP CVAudioStream::Notify(IBaseFilter * pSender, Quality q)
{
return E_NOTIMPL;
}
HRESULT CVAudioStream::SetMediaType(const CMediaType *pmt)
{
HRESULT hr = CSourceStream::SetMediaType(pmt);
return hr;
}
HRESULT setupPwfex(WAVEFORMATEX *pwfex, AM_MEDIA_TYPE *pmt) {
pwfex->wFormatTag = WAVE_FORMAT_PCM;
pwfex->cbSize = 0;
pwfex->nChannels = 2;
HRESULT hr;
pwfex->nSamplesPerSec = 11025;
pwfex->wBitsPerSample = 16;
pwfex->nBlockAlign = (WORD)((pwfex->wBitsPerSample * pwfex->nChannels) / 8);
pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nBlockAlign;
hr = ::CreateAudioMediaType(pwfex, pmt, FALSE);
return hr;
}
/*HRESULT CVAudioStream::setAsNormal(CMediaType *pmt)
{
WAVEFORMATEX *pwfex;
pwfex = (WAVEFORMATEX *)pmt->AllocFormatBuffer(sizeof(WAVEFORMATEX));
ZeroMemory(pwfex, sizeof(WAVEFORMATEX));
if (NULL == pwfex) return E_OUTOFMEMORY;
return setupPwfex(pwfex, pmt);
}*/
HRESULT CVAudioStream::GetMediaType(int iPosition, CMediaType *pmt)
{
if (iPosition < 0) return E_INVALIDARG;
if (iPosition > 0) return VFW_S_NO_MORE_ITEMS;
if (iPosition == 0)
{
*pmt = m_mt;
return S_OK;
}
WAVEFORMATEX *pwfex = (WAVEFORMATEX *)pmt->AllocFormatBuffer(sizeof(WAVEFORMATEX));
setupPwfex(pwfex, pmt);
return S_OK;
}
HRESULT CVAudioStream::CheckMediaType(const CMediaType *pMediaType)
{
int cbFormat = pMediaType->cbFormat;
if (*pMediaType != m_mt) return E_INVALIDARG;
return S_OK;
}
const int WaveBufferChunkSize = 16 * 1024;
HRESULT CVAudioStream::DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProperties)
{
CheckPointer(pAlloc, E_POINTER);
CheckPointer(pProperties, E_POINTER);
WAVEFORMATEX *pwfexCurrent = (WAVEFORMATEX*)m_mt.Format();
pProperties->cBuffers = 1;
pProperties->cbBuffer = expectedMaxBufferSize;
ALLOCATOR_PROPERTIES Actual;
HRESULT hr = pAlloc->SetProperties(pProperties, &Actual);
if (FAILED(hr)) return hr;
if (Actual.cbBuffer < pProperties->cbBuffer) return E_FAIL;
return NOERROR;
}
HRESULT CVAudioStream::OnThreadCreate()
{
//GetMediaType(0, &m_mt);
//HRESULT hr = LoopbackCaptureSetup();
//if (FAILED(hr)) return hr;
return NOERROR;
}
HRESULT STDMETHODCALLTYPE CVAudioStream::SetFormat(AM_MEDIA_TYPE *pmt)
{
if (!pmt) return S_OK;
if (CheckMediaType((CMediaType *)pmt) != S_OK) return E_FAIL;
m_mt = *pmt;
IPin* pin;
ConnectedTo(&pin);
if (pin)
{
IFilterGraph *pGraph = m_pParent->GetGraph();
pGraph->Reconnect(this);
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE CVAudioStream::GetFormat(AM_MEDIA_TYPE **ppmt)
{
*ppmt = CreateMediaType(&m_mt);
return S_OK;
}
HRESULT STDMETHODCALLTYPE CVAudioStream::GetNumberOfCapabilities(int *piCount, int *piSize)
{
*piCount = 1;
*piSize = sizeof(AUDIO_STREAM_CONFIG_CAPS);
return S_OK;
}
HRESULT STDMETHODCALLTYPE CVAudioStream::GetStreamCaps(int iIndex, AM_MEDIA_TYPE **pmt, BYTE *pSCC)
{
if (iIndex < 0) return E_INVALIDARG;
if (iIndex > 0) return S_FALSE;
if (pSCC == NULL) return E_POINTER;
*pmt = CreateMediaType(&m_mt);
if (*pmt == NULL) return E_OUTOFMEMORY;
DECLARE_PTR(WAVEFORMATEX, pAudioFormat, (*pmt)->pbFormat);
AM_MEDIA_TYPE * pm = *pmt;
setupPwfex(pAudioFormat, pm);
AUDIO_STREAM_CONFIG_CAPS* pASCC = (AUDIO_STREAM_CONFIG_CAPS*)pSCC;
ZeroMemory(pSCC, sizeof(AUDIO_STREAM_CONFIG_CAPS));
pASCC->guid = MEDIATYPE_Audio;
pASCC->MaximumChannels = pAudioFormat->nChannels;
pASCC->MinimumChannels = pAudioFormat->nChannels;
pASCC->ChannelsGranularity = 1; // doesn't matter
pASCC->MaximumSampleFrequency = pAudioFormat->nSamplesPerSec;
pASCC->MinimumSampleFrequency = pAudioFormat->nSamplesPerSec;
pASCC->SampleFrequencyGranularity = 11025; // doesn't matter
pASCC->MaximumBitsPerSample = pAudioFormat->wBitsPerSample;
pASCC->MinimumBitsPerSample = pAudioFormat->wBitsPerSample;
pASCC->BitsPerSampleGranularity = 16; // doesn't matter
return S_OK;
}
HRESULT CVAudioStream::Set(REFGUID guidPropSet, DWORD dwID, void *pInstanceData, DWORD cbInstanceData, void *pPropData, DWORD cbPropData)
{
return E_NOTIMPL;
}
HRESULT CVAudioStream::Get(
REFGUID guidPropSet,
DWORD dwPropID,
void *pInstanceData,
DWORD cbInstanceData,
void *pPropData,
DWORD cbPropData,
DWORD *pcbReturned
)
{
if (guidPropSet != AMPROPSETID_Pin) return E_PROP_SET_UNSUPPORTED;
if (dwPropID != AMPROPERTY_PIN_CATEGORY) return E_PROP_ID_UNSUPPORTED;
if (pPropData == NULL && pcbReturned == NULL) return E_POINTER;
if (pcbReturned) *pcbReturned = sizeof(GUID);
if (pPropData == NULL) return S_OK;
if (cbPropData < sizeof(GUID)) return E_UNEXPECTED;
*(GUID *)pPropData = PIN_CATEGORY_CAPTURE;
return S_OK;
}
HRESULT CVAudioStream::QuerySupported(REFGUID guidPropSet, DWORD dwPropID, DWORD *pTypeSupport)
{
if (guidPropSet != AMPROPSETID_Pin) return E_PROP_SET_UNSUPPORTED;
if (dwPropID != AMPROPERTY_PIN_CATEGORY) return E_PROP_ID_UNSUPPORTED;
if (pTypeSupport) *pTypeSupport = KSPROPERTY_SUPPORT_GET;
return S_OK;
}
我的第一个问题是当我在 GraphStudioNext 中插入过滤器并打开其属性页面时。音频引脚显示以下(不正确的)信息:
majorType = GUID_NULL
subType = GUID_NULL
formattype = GUID_NULL
当然,我不能将任何东西连接到该引脚,因为它无效。我期待这样的事情,MEDIATYPE_Audio
因为我设置了它:
DEFINE_GUID(CLSID_VirtualCam, 0x8e14549a, 0xdb61, 0x4309, 0xaf, 0xa1, 0x35, 0x78, 0xe9, 0x27, 0xe9, 0x33);
const AMOVIESETUP_MEDIATYPE AMSMediaTypesVideo =
{
&MEDIATYPE_Video,
&MEDIASUBTYPE_NULL
};
const AMOVIESETUP_MEDIATYPE AMSMediaTypesAudio =
{
&MEDIATYPE_Audio,
&MEDIASUBTYPE_NULL
};
const AMOVIESETUP_PIN AMSPinVCam[] =
{
{
L"Video", // Pin string name
FALSE, // Is it rendered
TRUE, // Is it an output
FALSE, // Can we have none
FALSE, // Can we have many
&CLSID_NULL, // Connects to filter
NULL, // Connects to pin
1, // Number of types
&AMSMediaTypesVideo // Pin Media types
},
{
L"Audio", // Pin string name
FALSE, // Is it rendered
TRUE, // Is it an output
FALSE, // Can we have none
FALSE, // Can we have many
&CLSID_NULL, // Connects to filter
NULL, // Connects to pin
1, // Number of types
&AMSMediaTypesAudio // Pin Media types
}
};
const AMOVIESETUP_FILTER AMSFilterVCam =
{
&CLSID_VirtualCam, // Filter CLSID
FILTER_NAME, // String name
MERIT_DO_NOT_USE, // Filter merit
2, // Number pins
AMSPinVCam // Pin details
};
CFactoryTemplate g_Templates[] =
{
{
FILTER_NAME,
&CLSID_VirtualCam,
CVCam::CreateInstance,
NULL,
&AMSFilterVCam
},
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
STDAPI RegisterFilters( BOOL bRegister )
{
HRESULT hr = NOERROR;
WCHAR achFileName[MAX_PATH];
char achTemp[MAX_PATH];
ASSERT(g_hInst != 0);
if( 0 == GetModuleFileNameA(g_hInst, achTemp, sizeof(achTemp))) return AmHresultFromWin32(GetLastError());
MultiByteToWideChar(CP_ACP, 0L, achTemp, lstrlenA(achTemp) + 1, achFileName, NUMELMS(achFileName));
hr = CoInitialize(0);
if(bRegister)
{
hr = AMovieSetupRegisterServer(CLSID_VirtualCam, FILTER_NAME, achFileName, L"Both", L"InprocServer32");
}
if( SUCCEEDED(hr) )
{
IFilterMapper2 *fm = 0;
hr = CreateComObject( CLSID_FilterMapper2, IID_IFilterMapper2, fm );
if( SUCCEEDED(hr) )
{
if(bRegister)
{
IMoniker *pMoniker = 0;
REGFILTER2 rf2;
rf2.dwVersion = 1;
rf2.dwMerit = MERIT_DO_NOT_USE;
rf2.cPins = 2;
rf2.rgPins = AMSPinVCam;
hr = fm->RegisterFilter(CLSID_VirtualCam, FILTER_NAME, &pMoniker, &CLSID_VideoInputDeviceCategory, NULL, &rf2);
}
else
{
hr = fm->UnregisterFilter(&CLSID_VideoInputDeviceCategory, 0, CLSID_VirtualCam);
}
}
if(fm) fm->Release();
}
if( SUCCEEDED(hr) && !bRegister ) hr = AMovieSetupUnregisterServer( CLSID_VirtualCam );
CoFreeUnusedLibraries();
CoUninitialize();
return hr;
}
第二个问题:还有一个“延迟”选项卡,但是当我单击它时,GraphStudioNext永远挂起,VS 调试器(附加到该进程)什么也没说。哪段代码控制这个选项卡?
更新
解决了第一个问题:
HRESULT CVAudioStream::GetMediaType(int iPosition, CMediaType *pmt)
{
if (iPosition < 0) return E_INVALIDARG;
if (iPosition > 0) return VFW_S_NO_MORE_ITEMS;
WAVEFORMATEX *pwfex = (WAVEFORMATEX *)pmt->AllocFormatBuffer(sizeof(WAVEFORMATEX));
setupPwfex(pwfex, pmt);
pmt->SetType(&MEDIATYPE_Audio);
pmt->SetFormatType(&FORMAT_WaveFormatEx);
pmt->SetTemporalCompression(FALSE);
pmt->SetSubtype(&MEDIASUBTYPE_PCM);
pmt->SetSampleSize(pwfex->nBlockAlign);
return S_OK;
}