996 lines
20 KiB
C++
996 lines
20 KiB
C++
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
MSPutil.cpp
|
|
|
|
Abstract:
|
|
|
|
This module contains implementation of msp utility functions.
|
|
|
|
Author:
|
|
|
|
Mu Han (muhan) 1-November-1997
|
|
|
|
--*/
|
|
#include "stdafx.h"
|
|
#include "common.h"
|
|
#include <amrtpnet.h> // rtp guilds
|
|
#include <amrtpdmx.h> // demux guild
|
|
#include <amrtpuid.h> // AMRTP media types
|
|
|
|
HRESULT
|
|
AddFilter(
|
|
IN IGraphBuilder * pIGraph,
|
|
IN const CLSID & Clsid,
|
|
IN LPCWSTR pwstrName,
|
|
OUT IBaseFilter ** ppIBaseFilter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a filter and add it into the filtergraph.
|
|
|
|
Arguments:
|
|
|
|
pIGraph - the filter graph.
|
|
|
|
Clsid - reference to the CLSID of the filter
|
|
|
|
pwstrName - The name of ther filter added.
|
|
|
|
ppIBaseFilter - pointer to a pointer that stores the returned IBaseFilter
|
|
interface pointer to the newly created filter.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
LOG((MSP_TRACE, "AddFilter %ws", pwstrName));
|
|
|
|
_ASSERTE(ppIBaseFilter != NULL);
|
|
|
|
HRESULT hr;
|
|
|
|
if (FAILED(hr = CoCreateInstance(
|
|
Clsid,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IBaseFilter,
|
|
(void **) ppIBaseFilter
|
|
)))
|
|
{
|
|
LOG((MSP_ERROR, "create filter %x", hr));
|
|
return hr;
|
|
}
|
|
|
|
if (FAILED(hr = pIGraph->AddFilter(*ppIBaseFilter, pwstrName)))
|
|
{
|
|
LOG((MSP_ERROR, "add filter. %x", hr));
|
|
(*ppIBaseFilter)->Release();
|
|
*ppIBaseFilter = NULL;
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
EnableRTCPEvents(
|
|
IN IBaseFilter *pIBaseFilter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the address of a rtp stream
|
|
|
|
Arguments:
|
|
|
|
pIBaseFilter - an rtp source filters.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
LOG((MSP_TRACE, "EnableRTCPEvents"));
|
|
|
|
HRESULT hr;
|
|
|
|
// Get the IRTCPStream interface pointer on the filter.
|
|
CComQIPtr<IRTCPStream,
|
|
&IID_IRTCPStream> pIRTCPStream(pIBaseFilter);
|
|
if (pIRTCPStream == NULL)
|
|
{
|
|
LOG((MSP_ERROR, "get RTCP Stream interface"));
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
// enable events.
|
|
if (FAILED(hr = pIRTCPStream->ModifyRTCPEventMask(
|
|
(1 << DXMRTP_NEW_SOURCE_EVENT) |
|
|
(1 << DXMRTP_RECV_RTCP_SNDR_REPORT_EVENT) |
|
|
(1 << DXMRTP_RECV_RTCP_RECV_REPORT_EVENT) |
|
|
(1 << DXMRTP_TIMEOUT_EVENT) |
|
|
(1 << DXMRTP_BYE_EVENT)
|
|
, 1
|
|
)))
|
|
{
|
|
LOG((MSP_ERROR, "set Address. %x", hr));
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
SetLoopbackOption(
|
|
IN IBaseFilter *pIBaseFilter,
|
|
IN BOOL bLoopback
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enable of disable loopback based on registry settings.
|
|
|
|
Arguments:
|
|
|
|
pIBaseFilter - rtp source filter.
|
|
|
|
bLoopback - enable loopback or not.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
LOG((MSP_TRACE, "SetLoopbackOption"));
|
|
|
|
HRESULT hr;
|
|
|
|
// Get the IRTPStream interface pointer on the filter.
|
|
CComQIPtr<IRTPStream,
|
|
&IID_IRTPStream> pIRTPStream(pIBaseFilter);
|
|
if (pIRTPStream == NULL)
|
|
{
|
|
LOG((MSP_ERROR, "get RTP Stream interface"));
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
// Set the TTL used in the filter.
|
|
if (FAILED(hr = pIRTPStream->SetMulticastLoopBack(bLoopback)))
|
|
{
|
|
LOG((MSP_ERROR, "set loopback. %x", hr));
|
|
return hr;
|
|
}
|
|
|
|
LOG((MSP_INFO, "loopback enabled."));
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
SetQOSOption(
|
|
IN IBaseFilter * pIBaseFilter,
|
|
IN DWORD dwPayloadType,
|
|
IN DWORD dwMaxBitRate,
|
|
IN BOOL bFailIfNoQOS,
|
|
IN BOOL bReceive,
|
|
IN DWORD dwNumStreams,
|
|
IN BOOL bCIF
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Enable QOS.
|
|
|
|
Arguments:
|
|
|
|
pIBaseFilter - rtp source filter.
|
|
|
|
dwPayloadType - the rtp payload type of this stream.
|
|
|
|
bFailIfNoQOS - fail the stream is QOS is not available.
|
|
|
|
bReceive - if this stream is a receiving stream.
|
|
|
|
dwNumStreams - the number of streams reserved.
|
|
|
|
bCIF - CIF or QCIF.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
LOG((MSP_TRACE, "SetQOSOption"));
|
|
|
|
char * szQOSName;
|
|
DWORD fSharedStyle = DXMRTP_RESERVE_EXPLICIT;
|
|
|
|
switch (dwPayloadType)
|
|
{
|
|
case PAYLOAD_G711U:
|
|
case PAYLOAD_G711A:
|
|
szQOSName = "G711";
|
|
fSharedStyle = DXMRTP_RESERVE_WILCARD;
|
|
|
|
break;
|
|
|
|
case PAYLOAD_GSM:
|
|
|
|
szQOSName = "GSM6.10";
|
|
fSharedStyle = DXMRTP_RESERVE_WILCARD;
|
|
|
|
break;
|
|
|
|
case PAYLOAD_G723:
|
|
|
|
szQOSName = "G723";
|
|
fSharedStyle = DXMRTP_RESERVE_WILCARD;
|
|
|
|
break;
|
|
|
|
case PAYLOAD_H261:
|
|
szQOSName = (bCIF) ? "H261CIF" : "H261QCIF";
|
|
break;
|
|
|
|
case PAYLOAD_H263:
|
|
szQOSName = (bCIF) ? "H263CIF" : "H263QCIF";
|
|
break;
|
|
|
|
default:
|
|
LOG((MSP_WARN, "Don't know the QOS name for payload type: %d",
|
|
dwPayloadType));
|
|
return S_FALSE;
|
|
}
|
|
|
|
// Get the IRTPStream interface pointer on the filter.
|
|
CComQIPtr<IRTPStream,
|
|
&IID_IRTPStream> pIRTPStream(pIBaseFilter);
|
|
if (pIRTPStream == NULL)
|
|
{
|
|
LOG((MSP_ERROR, "get RTP Stream interface"));
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
HRESULT hr;
|
|
|
|
// Enable QOS,
|
|
if (FAILED(hr = pIRTPStream->SetQOSByName(szQOSName, bFailIfNoQOS)))
|
|
{
|
|
LOG((MSP_ERROR, "set QOS by name. %x", hr));
|
|
return hr;
|
|
}
|
|
|
|
// Get the IRTPParticipant interface pointer on the filter.
|
|
CComQIPtr<IRTPParticipant,
|
|
&IID_IRTPParticipant> pIRTPParticipant(pIBaseFilter);
|
|
if (pIRTPParticipant == NULL)
|
|
{
|
|
LOG((MSP_ERROR, "get RTP participant interface"));
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
if (FAILED(hr = pIRTPParticipant->SetMaxQOSEnabledParticipants(
|
|
(bReceive) ? dwNumStreams : 1,
|
|
dwMaxBitRate,
|
|
fSharedStyle
|
|
)))
|
|
{
|
|
LOG((MSP_ERROR, "SetMaxQOSEnabledParticipants. %x", hr));
|
|
return hr;
|
|
}
|
|
|
|
DWORD dwQOSEventMask =
|
|
(1 << DXMRTP_QOSEVENT_NOQOS) |
|
|
(1 << DXMRTP_QOSEVENT_REQUEST_CONFIRMED) |
|
|
(1 << DXMRTP_QOSEVENT_ADMISSION_FAILURE) |
|
|
(1 << DXMRTP_QOSEVENT_POLICY_FAILURE) |
|
|
(1 << DXMRTP_QOSEVENT_BAD_STYLE) |
|
|
(1 << DXMRTP_QOSEVENT_BAD_OBJECT) |
|
|
(1 << DXMRTP_QOSEVENT_TRAFFIC_CTRL_ERROR) |
|
|
(1 << DXMRTP_QOSEVENT_GENERIC_ERROR);
|
|
|
|
if (bReceive)
|
|
{
|
|
dwQOSEventMask |=
|
|
(1 << DXMRTP_QOSEVENT_SENDERS) |
|
|
(1 << DXMRTP_QOSEVENT_NO_SENDERS);
|
|
}
|
|
else
|
|
{
|
|
dwQOSEventMask |=
|
|
(1 << DXMRTP_QOSEVENT_RECEIVERS) |
|
|
(1 << DXMRTP_QOSEVENT_NO_RECEIVERS) |
|
|
(1 << DXMRTP_QOSEVENT_NOT_ALLOWEDTOSEND) |
|
|
(1 << DXMRTP_QOSEVENT_ALLOWEDTOSEND);
|
|
}
|
|
|
|
// enable events.
|
|
if (FAILED(hr = pIRTPStream->ModifyQOSEventMask(dwQOSEventMask, 1)))
|
|
{
|
|
LOG((MSP_ERROR, "set QOSEventMask. %x", hr));
|
|
return hr;
|
|
}
|
|
|
|
LOG((MSP_INFO, "enabled qos for %s.", szQOSName));
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
FindPin(
|
|
IN IBaseFilter * pIFilter,
|
|
OUT IPin ** ppIPin,
|
|
IN PIN_DIRECTION direction,
|
|
IN BOOL bFree
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find a input pin or output pin on a filter.
|
|
|
|
Arguments:
|
|
|
|
pIFilter - the filter that has pins.
|
|
|
|
ppIPin - the place to store the returned interface pointer.
|
|
|
|
direction - PINDIR_INPUT or PINDIR_OUTPUT.
|
|
|
|
bFree - look for a free pin or not.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
_ASSERTE(ppIPin != NULL);
|
|
|
|
HRESULT hr;
|
|
DWORD dwFeched;
|
|
|
|
// Get the enumerator of pins on the filter.
|
|
CComPtr<IEnumPins> pIEnumPins;
|
|
if (FAILED(hr = pIFilter->EnumPins(&pIEnumPins)))
|
|
{
|
|
LOG((MSP_ERROR, "enumerate pins on the filter %x", hr));
|
|
return hr;
|
|
}
|
|
|
|
IPin * pIPin = NULL;
|
|
|
|
// Enumerate all the pins and break on the
|
|
// first pin that meets requirement.
|
|
for (;;)
|
|
{
|
|
if (pIEnumPins->Next(1, &pIPin, &dwFeched) != S_OK)
|
|
{
|
|
LOG((MSP_ERROR, "find pin on filter."));
|
|
return E_FAIL;
|
|
}
|
|
if (0 == dwFeched)
|
|
{
|
|
LOG((MSP_ERROR, "get 0 pin from filter."));
|
|
return E_FAIL;
|
|
}
|
|
|
|
PIN_DIRECTION dir;
|
|
if (FAILED(hr = pIPin->QueryDirection(&dir)))
|
|
{
|
|
LOG((MSP_ERROR, "query pin direction. %x", hr));
|
|
pIPin->Release();
|
|
return hr;
|
|
}
|
|
if (direction == dir)
|
|
{
|
|
if (!bFree)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Check to see if the pin is free.
|
|
CComPtr<IPin> pIPinConnected;
|
|
hr = pIPin->ConnectedTo(&pIPinConnected);
|
|
if (pIPinConnected == NULL)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
pIPin->Release();
|
|
}
|
|
|
|
*ppIPin = pIPin;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
ConnectFilters(
|
|
IN IGraphBuilder * pIGraph,
|
|
IN IBaseFilter * pIFilter1,
|
|
IN IBaseFilter * pIFilter2,
|
|
IN BOOL fDirect,
|
|
IN AM_MEDIA_TYPE * pmt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Connect the output pin of the first filter to the input pin of the
|
|
second filter.
|
|
|
|
Arguments:
|
|
|
|
pIGraph - the filter graph.
|
|
|
|
pIFilter1 - the filter that has the output pin.
|
|
|
|
pIFilter2 - the filter that has the input pin.
|
|
|
|
pmt - a pointer to a AM_MEDIA_TYPE used in the connection.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
LOG((MSP_TRACE, "ConnectFilters"));
|
|
|
|
HRESULT hr;
|
|
|
|
CComPtr<IPin> pIPinOutput;
|
|
if (FAILED(hr = ::FindPin(pIFilter1, &pIPinOutput, PINDIR_OUTPUT)))
|
|
{
|
|
LOG((MSP_ERROR, "find output pin on filter1. %x", hr));
|
|
return hr;
|
|
}
|
|
|
|
CComPtr<IPin> pIPinInput;
|
|
if (FAILED(hr = ::FindPin(pIFilter2, &pIPinInput, PINDIR_INPUT)))
|
|
{
|
|
LOG((MSP_ERROR, "find input pin on filter2. %x", hr));
|
|
return hr;
|
|
}
|
|
|
|
if (fDirect)
|
|
{
|
|
if (FAILED(hr = pIGraph->ConnectDirect(pIPinOutput, pIPinInput, pmt)))
|
|
{
|
|
LOG((MSP_ERROR, "connect pins direct failed: %x", hr));
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (FAILED(hr = pIGraph->Connect(pIPinOutput, pIPinInput)))
|
|
{
|
|
LOG((MSP_ERROR, "connect pins %x", hr));
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
ConnectFilters(
|
|
IN IGraphBuilder * pIGraph,
|
|
IN IPin * pIPinOutput,
|
|
IN IBaseFilter * pIFilter,
|
|
IN BOOL fDirect,
|
|
IN AM_MEDIA_TYPE * pmt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Connect an output pin to the input pin of a filter.
|
|
|
|
Arguments:
|
|
|
|
pIGraph - the filter graph.
|
|
|
|
pIPinOutput - an output pin.
|
|
|
|
pIFilter - a filter that has the input pin.
|
|
|
|
pmt - a pointer to a AM_MEDIA_TYPE used in the connection.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
LOG((MSP_TRACE, "ConnectFilters"));
|
|
|
|
HRESULT hr;
|
|
CComPtr<IPin> pIPinInput;
|
|
|
|
if (FAILED(hr = ::FindPin(pIFilter, &pIPinInput, PINDIR_INPUT)))
|
|
{
|
|
LOG((MSP_ERROR, "find input pin on filter. %x", hr));
|
|
return hr;
|
|
}
|
|
|
|
if (fDirect)
|
|
{
|
|
if (FAILED(hr = pIGraph->ConnectDirect(pIPinOutput, pIPinInput, pmt)))
|
|
{
|
|
LOG((MSP_ERROR, "connect pins direct failed: %x", hr));
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (FAILED(hr = pIGraph->Connect(pIPinOutput, pIPinInput)))
|
|
{
|
|
LOG((MSP_ERROR, "connect pins %x", hr));
|
|
return hr;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
ConnectFilters(
|
|
IN IGraphBuilder * pIGraph,
|
|
IN IBaseFilter * pIFilter,
|
|
IN IPin * pIPinInput,
|
|
IN BOOL fDirect,
|
|
IN AM_MEDIA_TYPE * pmt
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Connect an filter to the input pin of a filter.
|
|
|
|
Arguments:
|
|
|
|
pIGraph - the filter graph.
|
|
|
|
pIPinOutput - an output pin.
|
|
|
|
pIFilter - a filter that has the input pin.
|
|
|
|
pmt - a pointer to a AM_MEDIA_TYPE used in the connection.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
LOG((MSP_TRACE, "ConnectFilters"));
|
|
|
|
HRESULT hr;
|
|
CComPtr<IPin> pIPinOutput;
|
|
|
|
if (FAILED(hr = ::FindPin(pIFilter, &pIPinOutput, PINDIR_OUTPUT)))
|
|
{
|
|
LOG((MSP_ERROR, "find input pin on filter. %x", hr));
|
|
return hr;
|
|
}
|
|
|
|
if (fDirect)
|
|
{
|
|
if (FAILED(hr = pIGraph->ConnectDirect(pIPinOutput, pIPinInput, pmt)))
|
|
{
|
|
LOG((MSP_ERROR, "connect pins direct failed: %x", hr));
|
|
return hr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (FAILED(hr = pIGraph->Connect(pIPinOutput, pIPinInput)))
|
|
{
|
|
LOG((MSP_ERROR, "connect pins %x", hr));
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
void WINAPI MSPDeleteMediaType(AM_MEDIA_TYPE *pmt)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete a AM media type returned by the filters.
|
|
|
|
Arguments:
|
|
|
|
pmt - a pointer to a AM_MEDIA_TYPE structure.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
// allow NULL pointers for coding simplicity
|
|
|
|
if (pmt == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pmt->cbFormat != 0) {
|
|
CoTaskMemFree((PVOID)pmt->pbFormat);
|
|
|
|
// Strictly unnecessary but tidier
|
|
pmt->cbFormat = 0;
|
|
pmt->pbFormat = NULL;
|
|
}
|
|
if (pmt->pUnk != NULL) {
|
|
pmt->pUnk->Release();
|
|
pmt->pUnk = NULL;
|
|
}
|
|
|
|
CoTaskMemFree((PVOID)pmt);
|
|
}
|
|
|
|
|
|
BOOL
|
|
GetRegValue(
|
|
IN LPCWSTR szName,
|
|
OUT DWORD *pdwValue
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get a dword from the registry in the ipconfmsp key.
|
|
|
|
Arguments:
|
|
|
|
szName - The name of the value.
|
|
|
|
pdwValue - a pointer to the dword returned.
|
|
|
|
Return Value:
|
|
|
|
TURE - SUCCEED.
|
|
|
|
FALSE - MSP_ERROR
|
|
|
|
--*/
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwDataSize, dwDataType, dwValue;
|
|
|
|
if (::RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
gszSDPMSPKey,
|
|
0,
|
|
KEY_READ,
|
|
&hKey) != NOERROR)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
dwDataSize = sizeof(DWORD);
|
|
if (::RegQueryValueExW(
|
|
hKey,
|
|
szName,
|
|
0,
|
|
&dwDataType,
|
|
(LPBYTE) &dwValue,
|
|
&dwDataSize) != NOERROR)
|
|
{
|
|
RegCloseKey (hKey);
|
|
return FALSE;
|
|
}
|
|
|
|
*pdwValue = dwValue;
|
|
|
|
RegCloseKey (hKey);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
FindACMAudioCodec(
|
|
IN DWORD dwPayloadType,
|
|
OUT IBaseFilter **ppIBaseFilter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find the audio codec filter based on the payload type.
|
|
|
|
Arguments:
|
|
|
|
dwPayloadType - The rtp payload type.
|
|
|
|
ppIBaseFilter - The returned interface pointer.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
LOG((MSP_TRACE, "Find audio codec Called."));
|
|
|
|
_ASSERTE(ppIBaseFilter != NULL);
|
|
|
|
HRESULT hr;
|
|
|
|
int AcmId;
|
|
|
|
switch (dwPayloadType)
|
|
{
|
|
case PAYLOAD_G711A:
|
|
AcmId = WAVE_FORMAT_ALAW;
|
|
break;
|
|
|
|
case PAYLOAD_G711U:
|
|
AcmId = WAVE_FORMAT_MULAW;
|
|
break;
|
|
|
|
case PAYLOAD_GSM:
|
|
AcmId = WAVE_FORMAT_GSM610;
|
|
break;
|
|
|
|
case PAYLOAD_MSAUDIO:
|
|
AcmId = WAVE_FORMAT_MSAUDIO1;
|
|
break;
|
|
|
|
case PAYLOAD_G721:
|
|
AcmId = WAVE_FORMAT_ADPCM;
|
|
break;
|
|
|
|
case PAYLOAD_DVI4_8:
|
|
AcmId = WAVE_FORMAT_DVI_ADPCM;
|
|
break;
|
|
|
|
default:
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// Create the DirectShow Category enumerator Creator
|
|
//
|
|
CComPtr<ICreateDevEnum> pCreateDevEnum;
|
|
CComPtr<IEnumMoniker> pCatEnum;
|
|
|
|
hr = CoCreateInstance(
|
|
CLSID_SystemDeviceEnum,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_ICreateDevEnum,
|
|
(void**)&pCreateDevEnum);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
LOG((MSP_ERROR, "Create system device enum - hr: %8x", hr));
|
|
return hr;
|
|
}
|
|
|
|
hr = pCreateDevEnum->CreateClassEnumerator(
|
|
CLSID_CAcmCoClassManager,
|
|
&pCatEnum,
|
|
0
|
|
);
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
LOG((MSP_ERROR, "CreateClassEnumerator - hr: %8x", hr));
|
|
return hr;
|
|
}
|
|
|
|
// find the acm wrapper we want to use.
|
|
for (;;)
|
|
{
|
|
ULONG cFetched;
|
|
CComPtr<IMoniker> pMoniker;
|
|
|
|
if (S_OK != (hr = pCatEnum->Next(1, &pMoniker, &cFetched)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Get the ACMid for this filter out of the property bag.
|
|
CComPtr<IPropertyBag> pBag;
|
|
hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG((MSP_ERROR, "get property bag - hr: %8x", hr));
|
|
continue;
|
|
}
|
|
|
|
VARIANT var;
|
|
var.vt = VT_I4;
|
|
hr = pBag->Read(L"AcmId", &var, 0);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG((MSP_ERROR, "read acmid - hr: %8x", hr));
|
|
continue;
|
|
}
|
|
|
|
if (AcmId == V_I4(&var))
|
|
{
|
|
// Now make the filter for this.
|
|
hr = pMoniker->BindToObject(
|
|
0,
|
|
0,
|
|
IID_IBaseFilter,
|
|
(void**)ppIBaseFilter
|
|
);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
LOG((MSP_ERROR, "BindToObject - hr: %8x", hr));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT SetAudioFormat(
|
|
IN IUnknown* pIUnknown,
|
|
IN WORD wBitPerSample,
|
|
IN DWORD dwSampleRate
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the IAMStreamConfig interface on the pin and config the
|
|
audio format by using WAVEFORMATEX.
|
|
|
|
Arguments:
|
|
|
|
pIUnknown - an object to configure.
|
|
|
|
wBitPerSample - the number of bits in each sample.
|
|
|
|
dwSampleRate - number of samples per second.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
LOG((MSP_TRACE, "SetAudioFormat entered"));
|
|
|
|
HRESULT hr;
|
|
|
|
CComPtr<IAMStreamConfig> pIAMStreamConfig;
|
|
|
|
if (FAILED(hr = pIUnknown->QueryInterface(
|
|
IID_IAMStreamConfig,
|
|
(void **)&pIAMStreamConfig
|
|
)))
|
|
{
|
|
LOG((MSP_ERROR, "Can't get IAMStreamConfig interface.%8x", hr));
|
|
return hr;
|
|
}
|
|
|
|
AM_MEDIA_TYPE mt;
|
|
WAVEFORMATEX wfx;
|
|
|
|
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
|
wfx.wBitsPerSample = wBitPerSample;
|
|
wfx.nChannels = 1;
|
|
wfx.nSamplesPerSec = dwSampleRate;
|
|
wfx.nBlockAlign = wfx.wBitsPerSample * wfx.nChannels / 8;
|
|
wfx.nAvgBytesPerSec = ((DWORD) wfx.nBlockAlign * wfx.nSamplesPerSec);
|
|
wfx.cbSize = 0;
|
|
|
|
mt.majortype = MEDIATYPE_Audio;
|
|
mt.subtype = MEDIASUBTYPE_PCM;
|
|
mt.bFixedSizeSamples = TRUE;
|
|
mt.bTemporalCompression = FALSE;
|
|
mt.lSampleSize = 0;
|
|
mt.formattype = FORMAT_WaveFormatEx;
|
|
mt.pUnk = NULL;
|
|
mt.cbFormat = sizeof(WAVEFORMATEX);
|
|
mt.pbFormat = (BYTE*)&wfx;
|
|
|
|
// set the format of the audio capture terminal.
|
|
if (FAILED(hr = pIAMStreamConfig->SetFormat(&mt)))
|
|
{
|
|
LOG((MSP_ERROR, "SetFormat returns error: %8x", hr));
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT SetAudioBufferSize(
|
|
IN IUnknown* pIUnknown,
|
|
IN DWORD dwNumBuffers,
|
|
IN DWORD dwBufferSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the audio capture output pin's buffer size. The buffer size
|
|
determins how many milliseconds worth of samples are contained
|
|
in a buffer.
|
|
|
|
Arguments:
|
|
|
|
pIUnknown - an object to configure.
|
|
|
|
dwNumBuffers - the number of buffers to be allocated. Too few buffers
|
|
might cause starvation on the capture device.
|
|
|
|
dwBufferSize - The size of each buffer.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
LOG((MSP_TRACE, "SetAudioBufferSize, dwNumBuffers %d, dwBuffersize %d",
|
|
dwNumBuffers, dwBufferSize));
|
|
|
|
_ASSERTE(dwNumBuffers != 0 && dwBufferSize != 0);
|
|
|
|
HRESULT hr;
|
|
|
|
CComPtr<IAMBufferNegotiation> pBN;
|
|
if (FAILED(hr = pIUnknown->QueryInterface(
|
|
IID_IAMBufferNegotiation,
|
|
(void **)&pBN
|
|
)))
|
|
{
|
|
LOG((MSP_ERROR, "Can't get buffer negotiation.%8x", hr));
|
|
return hr;
|
|
}
|
|
|
|
ALLOCATOR_PROPERTIES prop;
|
|
|
|
// Set the number of buffers.
|
|
prop.cBuffers = dwNumBuffers;
|
|
prop.cbBuffer = dwBufferSize;
|
|
|
|
prop.cbAlign = -1;
|
|
prop.cbPrefix = -1;
|
|
|
|
if (FAILED(hr = pBN->SuggestAllocatorProperties(&prop)))
|
|
{
|
|
LOG((MSP_ERROR, "SuggestAllocatorProperties returns error: %8x", hr));
|
|
}
|
|
else
|
|
{
|
|
LOG((MSP_INFO,
|
|
"SetAudioBuffersize"
|
|
" buffers: %d, buffersize: %d, align: %d, Prefix: %d",
|
|
prop.cBuffers,
|
|
prop.cbBuffer,
|
|
prop.cbAlign,
|
|
prop.cbPrefix
|
|
));
|
|
}
|
|
return hr;
|
|
}
|
|
|