To get a list of available audio devices, video devices, and codecs on your system, you would require a starting point in DirectX or rather DirectShow. This extremely small program will give you a place to start with. Though, to play audio and videos, we need modifications to our program, and especially, if we are to write a program that would require instant playback and file-saving etc. This small program will, however, give the main starting point for beginners who are interested in leering DirectShow programming with access to input/output devices and audio/video codecs.
DirectX uses the term 'filters' to specify different hardware and software codecs. Whereas, here in the program, I have used the term 'Device'. When I started programming, I never liked to use the term 'filters', and always resorted to the term 'device', so I personally think that some of you feel like that too! There is limited exception handling, as with my previous tutorials. I deliberately did that so that the code would become more user-friendly, instead of being a collection of professional code.
As you know, DirectX is COM based, so a little COM understanding would be good.
There are two simple functions apart from
First, let us see what we have in
main() function of the program holds the initialization code of COM, and declares a couple of variables, '
hr' and '
ICreateDevEnum is the interface of which we maintain a reference in the pointer variable
*pDeviceEnum. What this interface does is explained after the explanation of the HR_Failed(hr) function.
HR_Failed() function receives an
HRESULT type; the function is called whenever an error occurs. For example:
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);if (SUCCEEDED(hr))
cout<<"COM Initialisation successful"<<endl;
In the above lines of code,
hr is returned after a call to initialize COM. If initialization fails, a call to
HR_Failed(hr) is made. The code inside the function will translate the value of '
hr' to a more human-understandable code (I found the code on MSDN, let me be honest here!).
void HR_Failed(HRESULT hr)
DWORD res = AMGetErrorText(hr, szErr, MAX_ERROR_TEXT_LEN);
if (res == 0)
L"Unknown Error: 0x%2x", hr);
MessageBox(0, szErr, TEXT("Error!"), MB_OK | MB_ICONERROR);
Device_Reader(GUID,ICreateDevEnum) is responsible for reading or extracting the 'filters', i.e., the audio, video, and codec filters installed on the system. Don't confuse the 'Device' with a codec. DirectX terms it 'filters' and I term it 'devices'. You can happily rename the function to
Filter_Reader if that makes you feel easy. In one of my previous articles, I have explained how to read the devices on a system. You can find that topic here, but I will still write a few lines here too as the code in the
Device_Reader function is almost the same.
First, we initialize the device enumeration with a call to
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void **)&pDeviceEnum);
We then need to create a device enumeration which allows us to get hold of or "read in" the audio devices that are available on the system. Here, we need to specify the category of devices that we are trying to read or get hold of. We do this enumeration by passing on the GUID for each category as defined by the MSDN documentation. In this case, I have used quite a few:
CLSID_AudioCompressorCategory. The name of each GUID refers to a specific category.
The other usefulness of using a device enumeration via Class ID is that suppose we have two sound cards and both of them support the same filters, using this enumeration we can treat them distinctively as separate instances. After the enumeration, we cannot simply use pin properties or access any other methods for the devices in the
AudioInputDeviceCategory. To do this, we have to use Monikers. A Moniker is an interface that refers to a specific device, and then we use
IPropertyBag to bind devices to a Moniker and read properties of the devices, respectively. The
IPropertyBag interface holds the keys to the device properties.
void Device_Reader(GUID DEVICE_CLSID,ICreateDevEnum *pDeviceEnum )
IMoniker *pDeviceMonik = NULL; IEnumMoniker *pEnumCat = NULL; VARIANT varName;
hr = pDeviceEnum->CreateClassEnumerator(DEVICE_CLSID, &pEnumCat, 0);
if (hr == S_OK)
while (pEnumCat->Next(1, &pDeviceMonik, &cFetched) == S_OK)
IPropertyBag *pPropBag = NULL;
hr = pDeviceMonik->BindToStorage(0, 0,
hr = pPropBag->Read(L"FriendlyName", &varName, 0);
The function accepts the GUID and the pointer to the
ICreateDevEnum interface. Using the different GUIDs, each call accessed the available audio, video, and codec filters on the system. Below is a screenshot of what is available on my system.
What happens when a filter is not found??
A call to
HR_Failed is made, and the following window pops up with the error message 'Incorrect Function'. Every time a whole 'category' of filters is not found, the call to
HR_Failed is made, which displays the windows.
Points of interest
GraphEdit has always been the tool for developers to get out of trouble. Though it looks very primitive, it is an absolute beauty. Only after using GraphEdit did I realise that it displays exactly the same "FriendlyName" as seen in the console output. So, be sure you have a go at it. If you cannot find GraphEdit on your system, make sure you have installed the Windows SDK, and look in the 'Bin' directory to find GrapEdit.exe.
The code is built with the following
- Windows Server 2008
- MS Visual Studio 2008
- DirectX 10.1
- Microsoft Windows SDK 6.1
First post. Will be updated if required.