《微软:DirectShow开发指南》第4章 Capturing Audio with DirectShow

Directshow中的视频捕捉 本篇文档主要描述关于用Directshow进行视频开发的一些技术主要包括下面内容1关于视频捕捉(About Video Capture in Dshow) 2选择一个视频捕捉设备(Select capture device) 3预览视频(Previewing Video) 4如何捕捉视频流并保存到文件(Capture video to File) 5将设备从系统中移走时的事件通知(Device r 阅读详情

The next four chapters of this book deal with Microsoft DirectShow applications that capture media streams for manipulation by DirectShow. In most of the examples to follow, the streams will simply be written to disk for later playback or manipulation by another DirectShow application. The filter graphs we’ll build will be relatively simple, consisting of a capture source filter (a special class of source filter that talks directly to capture hardware on the computer), one or two transform filters, and a rendererfilter, which will write the stream to a file.

接下来的四个章节主要讨论MS应用程序通过DS来捕捉媒体流。为了使filter graph相对简单,这些章节中的示例程序都是将流写到文件以供以后的回放和其它DirectShow应用程序来操作。这些filter graph仅由capture source filter, 一个或两个transform filters,和一个renderer filter(用来写流到文件)组成。

 

Although building filter graphs to capture streams is a straightforward affair, at the code level a number of issues need to be addressed to successfully create a filter graph. Starting with a very simple filter graph, which captures an incoming audio source as an AVI file (containing sound but no video), we’ll progress through all the programming tricks you’ll need to employ to successfully build DirectShow applications capable of capturing any media stream coming into your computer.

尽管创建一个捕捉流的filter graph是一件很简单的任务,但是在代码级上还是有一些问题需要解决了才能成功地创建这样的filter graph。先从一个很简单的filter graph开始,它捕捉输入的音频源并将其写到一个AVI文件(只有音频没有视频)。下面将对这些编程的细节一一展开。

 

Capturing Audio with DSAudioCap

DSAudioCap is a console-based application that will record audio to a file (MyAVIFile.AVI) for as long as it runs. As a console application, it sends diagnostic information to the text-mode window it opens on the display, which is useful for debugging. This window also prompts the user to press the Enter key to stop recording, at which point the filter graph is stopped and the application terminates.

DSAudioCap是一个基于控制台的应用程序,当它运行时,将记录音频成文件(MyAVIFile.AVI)。作为一个控制台应用程序,它会将诊断信息发送到文本模式的窗口,以利于调试。这个窗口同样可以用于接收用户的按键命令以停止收录,这时filter graph会停止,并且应用程序结束。

 

Examining the main Function

Let’s begin our analysis of DSAudioCap by taking a look at the main function. As in the earlier examples of DSBuild and DSRender, main opens with the instantiating of the Filter Graph Manager object. This object is followed by the acquisition of the IMediaControl interface to the Filter Graph Manager, which controls execution of the filter graph.

函数main打开了一个Filter Graph Manager对象的实例,接着它请求了一个IMediaControl 类型的接口pControl,用它(pControl)来控制filter graph的执行。

点击(此处)折叠或打开

  1. // A very simple program to capture audio to a file using DirectShow
  2. //
  3. int main(int argc, char* argv[])
  4. {
  5.   IGraphBuilder *pGraph = NULL; // Filter graph builder object
  6.   IMediaControl *pControl = NULL; // Media control object
  7.   IFileSinkFilter *pSink = NULL; // Interface on file writer
  8.   IBaseFilter *pAudioInputFilter = NULL; // Audio capture filter
  9.   IBaseFilter *pFileWriter = NULL; // File writer filter
  10.   // Initialize the COM library.
  11.   HRESULT hr = CoInitialize(NULL);
  12.   if (FAILED(hr))
  13.   {
  14.     // We'll send our error messages to the console.
  15.     printf("ERROR - Could not initialize COM library");
  16.     return hr;
  17.   }
  18.   // Create the Filter Graph Manager and query for interfaces.
  19.   hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
  20.   IID_IGraphBuilder, (void **)&pGraph);
  21.   if (FAILED(hr)) // FAILED is a macro that tests the return value
  22.   {
  23.     printf("ERROR - Could not create the Filter Graph Manager.");
  24.     return hr;
  25.   }
  26.   // Using QueryInterface on the graph builder object,
  27.   // get the IMediaControl object.
  28.   hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl);
  29.   if (FAILED(hr))
  30.   {
  31.     printf("ERROR - Could not create the Media Control object.");
  32.     pGraph->Release(); // Clean up after ourselves
  33.     CoUninitialize(); // And uninitalize COM
  34.     return hr;
  35.   }

  36.   // OK, so now we want to build the filter graph
  37.   // using an AudioCapture filter.
  38.   // But there are several to choose from,
  39.   // so we need to enumerate them and then pick one.
  40.   hr = EnumerateAudioInputFilters((void**) &pAudioInputFilter);
  41.   hr = EnumerateAudioInputPins(pAudioInputFilter);
  42.   // Add the audio capture filter to the filter graph.
  43.   hr = pGraph->AddFilter(pAudioInputFilter, L"Capture");
  44.   // Next add the AVIMux. (You'll see why.)
  45.   IBaseFilter *pAVIMux = NULL;
  46.   hr = AddFilterByCLSID(pGraph, CLSID_AviDest, L"AVI Mux", &pAVIMux);
  47.   // Connect the filters.
  48.   hr = ConnectFilters(pGraph, pAudioInputFilter, pAVIMux);
  49.   // And now we instance a file writer filter.
  50.   hr = AddFilterByCLSID(pGraph, CLSID_FileWriter,
  51.   L"File Writer", &pFileWriter);
  52.   // Set the file name.
  53.   hr = pFileWriter->QueryInterface(IID_IFileSinkFilter, (void**)&pSink);
  54.   pSink->SetFileName(L"C:\\MyWAVFile.AVI", NULL);
  55.   // Connect the filters.
  56.   hr = ConnectFilters(pGraph, pAVIMux, pFileWriter);
  57.   if (SUCCEEDED(hr))
  58.   {
  59.     // Run the graph.
  60.     hr = pControl->Run();
  61.     if (SUCCEEDED(hr))
  62.     {
  63.       // Wait patiently for completion of the recording.
  64.       wprintf(L"Started recording...press Enter to stop recording.\n");
  65.       // Wait for completion.
  66.       char ch;
  67.       ch = getchar(); // We wait for keyboard input
  68.     }
  69.     // And stop the filter graph.
  70.     hr = pControl->Stop();
  71.     wprintf(L"Stopped recording.\n"); // To the console
  72.     // Before we finish, save the filter graph to a file.
  73.     SaveGraphFile(pGraph, L"C:\\MyGraph.GRF");
  74.   }
  75.   // Now release everything and clean up.
  76.   pSink->Release();
  77.   pAVIMux->Release();
  78.   pFileWriter->Release();
  79.   pAudioInputFilter->Release();
  80.   pControl->Release();
  81.   pGraph->Release();
  82.   CoUninitialize();
  83.   return 0;
  84. }

Enumerating System Devices in DirectShow

At this point in the function, a call is made to EnumerateAudioInputFilters, which is local to the application. This function is required because before a capture source filter can be added to the filter graph, it must be identified and selected. Unlike other DirectShow filters (which can be identified by a GUID), capture source filters and other filters that are tied to hardware devices are identified as a class of device. Using DirectShow calls, you can walk through the list of all filters in this class and select the one you want to use for your capture application.

在main函数中,调用了函数EnumerateAudioInputFilters,它是一个局部域函数,用于在将捕捉源 filter 添加进filter graph之前,识别和选择输入的设备。不同于其它的DirectShow的filter(它们都能被GUID标识),capture source filter 和其它硬件设备相关的filter都要以一个设备类来进行识别。使用DirectShow的调用,程序可以遍历类中的所有filter的列表,并选择其中一个来用。

 

Being able to enumerate capture filters gives you ultimate flexibility in the design of your DirectShow application because all capture resources are available to the application, not just those that have been designated as default devices by the user or the system. This functionality also means that DirectShow remains open to new hardware devices as they’re introduced by manufacturers—you won’t need a revision of DirectShow to deal with every new card or interface that comes down the path. Some WDM drivers installed in conjunction with the installation of multimedia devices are recognized by DirectShow and added to its enumerated lists of available filters. Figure 4-1 shows the Insert Filters dialog box listing audio capture devices.

由于可以枚举捕捉filters,因此所有的捕捉源都可以被应用程序使用,从而给DirectShow的应用程序设计提供了极大的灵活性,而不会只能使用由用户或系统选择的默认设备。这个功能同样意味着DirectShow可以忽略细节来打开新硬件设备只要它们被制造商兼容—也不需要修改DS来每个新采集卡或接口。某些安装了WDM驱动的设备同样也可被DS识别和连接。图4-1显示了插入filter对话框列出的所有音频捕捉设备。

 

On the other hand, now is a good time to ask yourself an important question: are you relying on specific hardware components in your DirectShow application? You could specify which capture source filter will be used in your application, but if that source filter isn’t on the user’s system (because it relies on hardware the user hasn’t installed), your application will fail. It’s best to design a DirectShow application for the widest possible range of devices to ensure that a user won’t encounter any unexpected failures when he or she runs your code.

另外,现在需要回答一个关键性的问题:你的DirectShow应用程序依赖于一个特定的硬件组件吗?你可以指定哪个capture source filter用于应用程序,但是如果这个source filter不在用户的系统上(因为它依赖于用户的系统是否安装),此时,这个应用程序将会导致失败。最好的设计是应用程序能最大范围地确保用户在使用时不会出现意外的程序崩溃。

Figure 4-1. GraphEdit enumerating audio capture devices in the Insert Filters dialog box

Here’s the source code for EnumerateAudioInputFilters:

点击(此处)折叠或打开

  1. // Enumerate all of the audio input devices
  2. // Return the _first_ of these to the caller
  3. // That should be the one chosen in the control panel.
  4. HRESULT EnumerateAudioInputFilters(void** gottaFilter)
  5. {
  6.   // Once again, code stolen from the DX9 SDK.
  7.   // Create the System Device Enumerator.
  8.   ICreateDevEnum *pSysDevEnum = NULL;
  9.   HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
  10.   CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
  11.   (void **)&pSysDevEnum);
  12.   if (FAILED(hr))
  13.   {
  14.     return hr;
  15.   }
  16.   // Obtain a class enumerator for the audio input category.
  17.   IEnumMoniker *pEnumCat = NULL;
  18.   hr = pSysDevEnum->CreateClassEnumerator(CLSID_AudioInputDeviceCategory,
  19.   &pEnumCat, 0);
  20.   if (hr == S_OK)
  21.   {
  22.     // Enumerate the monikers.
  23.     IMoniker *pMoniker = NULL;
  24.     ULONG cFetched;
  25.     if (pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK)
  26.     {
  27.       // Bind the first moniker to an object.
  28.       IPropertyBag *pPropBag;
  29.       hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag,
  30.       (void **)&pPropBag);
  31.       if (SUCCEEDED(hr))
  32.       {
  33.         // To retrieve the filter's friendly name,
  34.         // do the following:
  35.         VARIANT varName;
  36.         VariantInit(&varName);
  37.         hr = pPropBag->Read(L"FriendlyName", &varName, 0);
  38.         if (SUCCEEDED(hr))
  39.         {
  40.           wprintf(L"Selecting Audio Input Device: %s\n",
  41.           varName.bstrVal);
  42.         }
  43.         VariantClear(&varName);
  44.         // To create an instance of the filter,
  45.         // do the following:
  46.         // Remember to release gottaFilter later.
  47.         hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter,gottaFilter);

  48.         pPropBag->Release();
  49.       }
  50.       pMoniker->Release();
  51.     }
  52.     pEnumCat->Release();
  53.   }
  54.   pSysDevEnum->Release();
  55.   return hr;
  56. }

This function begins by creating an instance of a COM object known as the System Device Enumerator. This object will enumerate all the hardware devices of a specified type once its CreateClassEnumerator method is invoked with a GUID indicating the class of devices to be enumerated. In this case, the GUID CLSID_AudioInputDeviceCategory is requesting an enumeration of all the audio capture source filters on the system, but it could also be CLSID_VideoInputDeviceCategory for video capture source filters, or even CLSID_AudioCompressorCategory for all audio compression filters. (Although audio compression isn’t generally performed in hardware, each compressor is considered a system device and is treated as if it were hardware rather than software.) The resulting list of devices is placed into an object of the IEnumMoniker class. An enumerated class (which generally includes the letters Enum in its class name) can be examined, element by element, by invoking its Next method. In this case, invoking Next will return an IMoniker object, which is a lightweight COM object used to obtain information about other objects without having to instantiate them. The IMoniker object returns references to a‘bag’of data related to the enumerated

DirectShow filter, which is then placed into an IPropertyBag object with an invocation of the IMoniker method BindToStorage.

这个函数以创建COM对象实例----系统设备Enumerator开始。在调用了它的方法CreateClassEnumerator后,该对象将会枚举出该类中所有硬件设备。此时,GUID CLSID_AudioInputDeviceCategory会请求系统上的所有音频capture source filters的枚举对象,当然,也可以用来通过CLSID_VideoInputDeviceCategory来枚举视频的capture source filters,还可以通过CLSID_AudioCompressorCategory来枚举所有音频压缩filters。(尽管音频压缩通常不由硬件实现,每个音频压缩可以认为是一个系统设备并以硬件方式来对待)。设备的结果列表放在IEnumMoniker类的对象pEnumCat中。枚举类(通常在类名中包含有Enum)可以通过它的方法Next来进行逐个元素检查。在这个例子中,激活Next方法将会返回一个IMoniker对象pMoniker----它是一个轻量级的COM对象,它不需要实例化,用来获得其它对象的信息。这个IMoniker对象返回一个枚举相关的数据的引用bag。它可以放在IPropertyBag对象中。

 

Once the IPropertyBag object has been instantiated, it can be queried using its Read method for string data labeled as its FriendlyName, which is to say the English-readable name (rather than the GUID) for the filter. That string is then printed to the console. The string identifies the first filter in the enumerated list, which is

also the default audio capture device, established by the user through his or her preferences in the Sound control panel. (It’s a bad idea to count on any particular device showing up first on the list of audio capture devices because, at any point, the user might go into the Sound control panel and muck things up.)

当IPropertyBag对象被实例化后,就可以使用Read方法(以字符串FriendlyName为参数,以varName为返回修正)来询问,它将会返回filter的英文名称。这个字符串然后指向控制台,这个字符中识别了枚举列表中的第一个filter,----它就是默认的音频捕捉设备(由用户通过Sound control panel设置的)。

 

Once the name of the device has been printed to the console, another method call to the IMoniker object BindToObject creates an instance of the filter object, and this object is returned to the calling function. It will later need to be destroyed with a call to its Release method.

当设备名打印到控制台后,就可以调用IMoniker对象的方法BindToObject来创建这个filter对象的实例,并返回这个对象给调用函数EnumerateAudioInputFilters(void** gottaFilter),以用于后面的资源释放。

Although this function returns an object representing the first filter in the enumerated list—the default selection as specified in the control panel—with a few modifications, the function could return a list of objects and names. These objects and names could then be used to build menus or other GUI features that would allow the user to have complete control over which of possibly several audio capture sources would be used within a DirectShow application.

尽管这个函数返回了一个表示枚举列表中的第一个filter的对象 ,但是经过一些小的修改,这个函数也可以返回一系列的对象和名称。这些对象和名称可以用来创建菜单或是其它允许用户完全控制的GUI功能。

 

Enumerating Input Pins on an Audio Capture Device

Immediately upon return to the main function, another call is made to a different local function, EnumerateAudioInputPins. This function examines all the input pins on the selected audio input filter object; we’ll need to do this to determine which input pin is active. If you examine of the Volume control panel (generally accessible through the system tray), you can see that several different sources of audio input usually exist on a PC. Most PCs have some sort of CD audio, line audio, microphone, and auxiliary inputs. Other sources will capture the sound data passing through the system, as though the PC had an internal microphone, capturing its own sound-making capabilities in real-time. Figure 4-2 shows various audio input pins on an audio capture filter.

返回主函数后,将会调用另一个本地函数EnumerateAudioInputPins。这个函数将会选定后的输入filter对象的所有输入pin。我们需要确认哪个输入pin是激活的。在使用音量控制面板(Volume control panel)时,可以看到PC上的多种不同音频输入源。大多数的PC都会有CD audio, line audio, microphone, 和附加输入等。其它的源将会通过系统来捕捉音频。

Figure 4-2. GraphEdit showing the various audio input pins on an audio capture filter

 

The local function EnumerateAudioInputPins allows us to examine the audio input filter’s pins, as shown here:

函数EnumerateAudioInputPins用来检查音频输入filter的pin


点击(此处)折叠或打开

  1. // Code adopted from example in the DX9 SDK.
  2. // This code allows us to find the input pins an audio input filter.
  3. // We'll print out a list of them, indicating the enabled one.
  4. HRESULT EnumerateAudioInputPins(IBaseFilter *pFilter)
  5. {
  6.   IEnumPins *pEnum = NULL;
  7.   IPin *pPin = NULL;
  8.   PIN_DIRECTION PinDirThis;
  9.   PIN_INFO pInfo;
  10.   IAMAudioInputMixer *pAMAIM = NULL;
  11.   BOOL pfEnable = FALSE;
  12.   // Begin by enumerating all the pins on a filter.
  13.   HRESULT hr = pFilter->EnumPins(&pEnum);
  14.   if (FAILED(hr))
  15.   {
  16.     return NULL;
  17.   }
  18.   // Now, look for a pin that matches the direction characteristic.
  19.   // When we've found it, we'll examine it.
  20.   while(pEnum->Next(1, &pPin, 0) == S_OK)
  21.   {
  22.     // Get the pin direction.
  23.     pPin->QueryDirection(&PinDirThis);
  24.     if (PinDirThis == PINDIR_INPUT) {
  25.       // OK, we've found an input pin on the filter.
  26.       // Now let's get the information on that pin
  27.       // so we can print the name of the pin to the console.
  28.       hr = pPin->QueryPinInfo(&pInfo);
  29.       if (SUCCEEDED(hr)) {
  30.         wprintf(L"Input pin: %s\n", pInfo.achName);
  31.         // Now let's get the correct interface.
  32.         hr = pPin->QueryInterface(IID_IAMAudioInputMixer,
  33.         (void**) &pAMAIM);
  34.         if (SUCCEEDED(hr)) {
  35.           // Find out whether the pin is enabled.
  36.           // Is it the active input pin on the filter?

  37.           hr = pAMAIM->get_Enable(&pfEnable);
  38.           if (SUCCEEDED(hr)) {
  39.             if (pfEnable) {
  40.               wprintf(L"\tENABLED\n");
  41.             }
  42.           }
  43.           pAMAIM->Release();
  44.         }
  45.         pInfo.bFilter->Release(); // from QueryPinInfo
  46.       }
  47.     }
  48.     pPin->Release();
  49.   }
  50.   pEnum->Release();
  51.   return hr;
  52. }

The EnumerateAudioInputPins function was adapted from the GetPin function used in the previous chapter. A pointer to a DirectShow IBaseFilter object is passed by the caller, and its pins are enumerated within an IEnumPins object by a call to the IBaseFilter method EnumPins. Using the Next method on the IEnumPins object, an IPin object is instantiated for every pin within the enumerated list. The IPin object is then tested with a call to the QueryDirection method: is the pin an input pin? If it is, the QueryPinInfo method of IPin is invoked. This method returns a PIN_INFO data structure, containing (in English) the name of the pin, which is then printed on the console.

函数EnumerateAudioInputPins继承自前面章节的函数GetPin。调用者传入DirectShow的IBaseFilter对象指针pFilter。通过调用IBaseFilter的方法EnumPins来枚举对象pFilter的所有Pin,并存在IEnumPins对象pEnum中。通过调用IEnumPins对象的Next方法,可以得到IPin对象的枚举列表实例。可以通过调用IPin对象的QueryDirection方法来测试某个Pin是否是一个输入pin?如果是,IPin的方法QueryPinInfo就可以调用,这个方法会返回一个PIN_INFO的数据结构----它包含有pin的名称(将会输出到控制台)。                                       

 

Now comes a bit of DirectShow magic. Each pin on an audio input filter is an object in its own right and presents an IAMAudioInputMixer interface as one of its properties. This interface allows you to control various parameters of the pin, such as its volume level. The IAMAudioInputMixer interface for each IPin object is retrieved with a QueryInterface call. (This call will fail if you’re not acting on an IPin that is part of an audio input filter.) Once this object is instanced, one of its properties is examined with a call to get_Enable. If TRUE is returned, the pin is enabled—that pin is the active input pin for the filter, and the corresponding audio input is enabled. (The default active input pin is set through the Volume control panel; it’s the enabled item in the list of recording inputs.) Figure 4-3 shows pin properties of the audio input pins.

现在开始介绍DirectShow的一些神奇的地方。在音频输入filter上的每个pin都是一个有自己权利的对象,并且有一个IAMAudioInputMixer的接口。这个接口可以控制pin的各种参数,例如音量。每个IPin对象的IAMAudioInputMixer接口可以通过QueryInterface来调用。当IPin对象实例化后,它的属性要使用get_Enable方法来启用。如果返回TURE,那么这个pin就开启了----对于filter来说,这是一个激活了的输入pin,并且相应的音频输入也是开启的。(默认的激活的输入pin是通过音量控制面板来设置的,它也开启了相应的输入设备列表)

Figure 4-3. GraphEdit showing pin properties for the audio input pins

 

Although this routine only reads whether a pin is enabled, it is possible, through a corresponding call to the put_Enable method, to change the value on the pin, thereby selecting or removing an audio input. (You should be very careful that you have only one input enabled at a time, unless you know that your audio hardware can handle mixing multiple inputs.) This function indicates only the currently enabled input, but it’s very easy to modify it so that it selects an alternative input. And, once again, this function could easily be rewritten to return an array of IPin objects to the caller. This list could then be used to build a GUI so that users could easily pick the audio pin themselves.

尽管这个例子仅读取了是否这个pin是开启的,但是,通过相应的put_Enable方法的调用来尽管pin上的值是可以的。(但是这种情况的使用只能针对一次一个输入允许的情形,除非你知道你的音频硬件能处理多个输入的合成)。这个函数仅对当前开启的输入有用,但是也可以很容易地修改成选择一个合适的输入。另外,这个函数也能很方便地重写以返回一个IPin对象给调用者。这个列表就可以用来由用户选择的音频输入pin的GUI.

 

Connecting DirectShow Filters

DSAudioCap adds a convenience function, ConnectFilters, which can be used by the programmer to handle all the nitty-gritty of connecting two filters together. It’s actually three separate functions, as shown here:

DSAudioCap添加了一个例程函数, ConnectFilters, 它用来处理两个filter之间的连接。它实际由三个函数组成,如下所示:

点击(此处)折叠或打开

  1. // Find an unconnected pin on a filter.
  2. // This too is stolen from the DX9 SDK.
  3. HRESULT GetUnconnectedPin(IBaseFilter *pFilter, // Pointer to the filter.
  4.                           PIN_DIRECTION PinDir, // Direction of the pin to find.
  5.                           IPin **ppPin) // Receives a pointer to the pin.
  6. {
  7.   *ppPin = 0;
  8.   IEnumPins *pEnum = 0;
  9.   IPin *pPin = 0;
  10.   HRESULT hr = pFilter->EnumPins(&pEnum);
  11.   if (FAILED(hr))
  12.   {
  13.     return hr;
  14.   }
  15.   while (pEnum->Next(1, &pPin, NULL) == S_OK)
  16.   {
  17.     PIN_DIRECTION ThisPinDir;
  18.     pPin->QueryDirection(&ThisPinDir);
  19.     if (ThisPinDir == PinDir)
  20.     {
  21.       IPin *pTmp = 0;
  22.       hr = pPin->ConnectedTo(&pTmp);
  23.       if (SUCCEEDED(hr)) // Already connected--not the pin we want
  24.       {
  25.         pTmp->Release();
  26.       }
  27.       else // Unconnected--this is the pin we want
  28.       {
  29.         pEnum->Release();
  30.         *ppPin = pPin;
  31.         return S_OK;
  32.       }
  33.     }
  34.     pPin->Release();
  35.   }
  36.   pEnum->Release();
  37.   // Did not find a matching pin.
  38.   return E_FAIL;
  39. }

  40. // Connect two filters together with the Filter Graph Manager,
  41. // Stolen from the DX9 SDK.
  42. // This is the base version.
  43. HRESULT ConnectFilters(IGraphBuilder *pGraph, // Filter Graph Manager.
  44.                        IPin *pOut, // Output pin on the upstream filter.
  45.                        IBaseFilter *pDest) // Downstream filter.
  46. {
  47.   if ((pGraph == NULL) || (pOut == NULL) || (pDest == NULL))
  48.   {
  49.     return E_POINTER;
  50.   }
  51.   // Find an input pin on the downstream filter.
  52.   IPin *pIn = 0;
  53.   HRESULT hr = GetUnconnectedPin(pDest, PINDIR_INPUT, &pIn);
  54.   if (FAILED(hr))
  55.   {
  56.     return hr;
  57.   }
  58.   // Try to connect them.
  59.   hr = pGraph->Connect(pOut, pIn);
  60.   pIn->Release();
  61.   return hr;
  62. }

  63. // Connect two filters together with the Filter Graph Manager.
  64. // Again, stolen from the DX9 SDK.
  65. // This is an overloaded version.
  66. HRESULT ConnectFilters(IGraphBuilder *pGraph,
  67.                        IBaseFilter *pSrc,
  68.                        IBaseFilter *pDest)
  69. {
  70.   if ((pGraph == NULL) || (pSrc == NULL) || (pDest == NULL))
  71.   {
  72.     return E_POINTER;
  73.   }
  74.   // Find an output pin on the first filter.
  75.   IPin *pOut = 0;
  76.   HRESULT hr = GetUnconnectedPin(pSrc, PINDIR_OUTPUT, &pOut);
  77.   if (FAILED(hr))
  78.   {
  79.     return hr;
  80.   }
  81.   hr = ConnectFilters(pGraph, pOut, pDest);
  82.   pOut->Release();
  83.   return hr;
  84. }

Two versions of ConnectFilters are included as local functions in DSAudioCap, which is possible because the two have different calling parameters and C++ can handle them differently through its capability with overloaded functions. The bottom version of ConnectFilters is the one invoked by main, and it calls GetUnconnectedPin, which is a function very much like GetPin, except that it returns the first unconnected output pin on the source filter. The results are passed along to the other version of ConnectFilters, which calls GetUnconnectedPin again, this time searching for an input pin on the destination filter. Once everything’s been discovered, a call is made to the Filter Graph Manager method Connect, which connects the two pins.

在DSAudioCap中有两个版本的ConnectFilters,这是因为有两个不同的调用参数,并且C++可以通过重载来处理。后面的ConnectFilters是由main函数调用的,它调用了GetUnconnectedPin----它和GetPin很像,不同之处在于它返回源filter中第一个未连接的输出pin。这个结果将传送给另一个版本的ConnectFilters,它会再次调用GetUnconnectedPin,这次会搜索目的filter中一个输入pin。当这两个pin都被发现后,就可以调用Filter Graph Manager的方法Connect来连接两个pin。

 

Adding a Filter by Its Class ID

 

One more convenience function exists in DSAudioCap, AddFilterByCLSID, which adds a filter to the graph by its unique class ID. It will be used frequently in future examples.

AddFilterByCLSID,函数用于添加一个filter到graph。

 

Although AddFilterByCLSID doesn’t do anything spectacular, it does save some time in coding because it encapsulates the object creation and filter addition actions into a single function call. You’ll probably want to use it in your own DirectShow applications.

尽管AddFilterByCLSID并没有做什么大动作,但是它是出现 了多次,因为它封装了对象的创建和filter的添加在一个函数调用中,它同样可用于别的DS应用程序。

 

 

Using the Audio Capture Filter Graph

The filter graph for DSAudioCap is composed of three filters. First is an audio input filter, as explored earlier. Next comes an AVI multiplexer (or AVI mux). This filter takes a number of media streams and multiplexes them into a single stream formatted as an AVI file. These streams can be video, audio, or a combination of the two. An AVI mux is one of the ways to create a movie with synchronized video and audio portions; as streams arrive at the multiplexer, they’re combined into a single, synchronized stream. The final component in the filter graph is a File Writer filter, which writes the AVI stream to disk.

DSAudioCap的filter graph由三个filter组成。第一个是音频输入filter,接着的是AVI合成器,这个filter用来将多个流合成一个AVI文件流,这些被合成的流可以是视频,音频,或是两者的组合。AVI合成器也是创建同步的音视频电影的方法。当流到达合成器时,它们被合成一个单一的,同步过的流。最后一个组件是File Writer filter,它是用来写AVI流到磁盘。

 

Adding a File Writer Filter

Nearly all capture applications write their captured streams to a disk file. Several objects can serve as file “sinks,” meaning that they write stream data. The most common of these is the DirectShow File Writer filter object. In DSAudioCap, the File Writer filter is instantiated and added to the filter graph, and then its IFileSink interface is instantiated through a call to QueryInterface. This object exposes methods that allow you to programmatically set the name and path of the file to be written to disk, through a call to its SetFileName method. Figure 4-4 shows the filter graph created by DSAudioCap.

几乎所有捕捉应用都是将它们捕捉到的流写成文件。有多个对象都可以作为文件的“接收器模块,意即它们都可以写流数据。最常用的是DirectShow的File Writer filter对象。在DSAudioCap中,这个File Writer filter是一个实例,并被添加到filter graph中,它的IFileSink接口是一个实例,可以通过QueryInterface来调用。这个对象的可以通过SetFileName方法来设置保存的文件名和路径。

Executing the DSAudioCap Filter Graph

Once the file name has been set and all the filters have been connected together, the Run method is sent to the filter graph’s control object. From this point, the filter graph captures audio and writes it to an ever-growing AVI file until the Enter key is pressed. Watch out: this file can get very big very quickly. (We’re using an old but venerable call to getchar to watch for keyboard input.) Once the Enter key has been pressed, the filter graph control object makes a call to its Stop method, which terminates execution of the filter graph.

当设置好文件名,并且所有的filter都连接好后,就可以给filter graph的控制对象来发送Run方法。从此时开始,filter graph就能捕捉音频并写到AVI文件中,直到回车键被按下。

Figure 4-5 shows DSAudioCap running.

 

We’ve kept the call to SaveGraphFile in this program (and will continue to do so), which means that after the program terminates its execution, you can use GraphEdit to examine the filter graph created by DSAudioCap.

本程序仍保留了SaveGraphFile的调用,用于生成filter graph的文件。

 

After you execute this application, there should be a file named MyAVIFile.AVI on disk. Double-clicking the file (or opening it from Windows Media Player) will allow you to hear the sound that was captured by DSAudioCap.

应用程序执行完成后,将生成MyAVIFile.AVI文件,可以在GraphEdit中查看。

 

Adding Audio Compression with DSAudioCapCom

The sound that’s captured by DSAudioCap is uncompressed, so file sizes build very quickly, at just about the same rate they do on a standard audio CD—about 700 MB per hour. However, numerous transform filters are available to DirectShow that can compress audio to a fraction of its uncompressed size. We’ve already covered everything you need to know to add a compressor filter to the filter graph. Like audio input devices, audio compressor filters must be enumerated and selected from a list.

DSAudioCap的音频是未压缩的,因此文件增长得很快。然而,绝大多数的transform filters可以用来压缩音频数据大小。这里我们就要添加下个压缩filter。

 

The only major difference between DSAudioCap and DSAudioCapCom (the version with compression) is the

addition of one function, EnumerateAudioCompressorFilters, as shown in the following code:

DSAudioCap和DSAudioCapCom的主要的差别在于EnumerateAudioCompressorFilters这个函数。代码如下所示:

点击(此处)折叠或打开

  1. // Enumerate all of the audio compressors.
  2. // Return the one with the matching name to the caller.
  3. HRESULT EnumerateAudioCompressorFilters(void** gottaCompressor,
  4. wchar_t* matchName)
  5. {
  6.   // Once again, code stolen from the DX9 SDK.
  7.   // Create the System Device Enumerator.
  8.   ICreateDevEnum *pSysDevEnum = NULL;
  9.   HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
  10.   CLSCTX_INPROC_SERVER,
  11.   IID_ICreateDevEnum,
  12.   (void **)&pSysDevEnum);
  13.   if (FAILED(hr))
  14.   {
  15.     return hr;
  16.   }
  17.   // Obtain a class enumerator for the audio input category.
  18.   IEnumMoniker *pEnumCat = NULL;
  19.   hr = pSysDevEnum->CreateClassEnumerator(CLSID_AudioCompressorCategory,
  20.   &pEnumCat, 0);

  21.   if (hr == S_OK)
  22.   {
  23.     // Enumerate the monikers.
  24.     IMoniker *pMoniker = NULL;
  25.     ULONG cFetched;
  26.     BOOL done = false;
  27.     while ((pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) && (!done))
  28.     {
  29.       // Bind the first moniker to an object.
  30.       IPropertyBag *pPropBag;
  31.       hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag,
  32.       (void **)&pPropBag);
  33.       if (SUCCEEDED(hr))
  34.       {
  35.         // Retrieve the filter's friendly name.
  36.         VARIANT varName;
  37.         VariantInit(&varName);
  38.         hr = pPropBag->Read(L"FriendlyName", &varName, 0);
  39.         if (SUCCEEDED(hr))
  40.         {
  41.           wprintf(L"Testing Audio Compressor: %s\n",
  42.           varName.bstrVal);
  43.           // Is it the right one?
  44.           if (wcsncmp(varName.bstrVal, matchName,
  45.           wcslen(matchName)) == 0)
  46.           {
  47.             // We found it; send it back to the caller
  48.             hr = pMoniker->BindToObject(NULL, NULL,
  49.             IID_IBaseFilter,
  50.             gottaCompressor);
  51.             done = true;
  52.           }
  53.         }
  54.         VariantClear(&varName);
  55.         pPropBag->Release();
  56.       }
  57.       pMoniker->Release();
  58.     }
  59.     pEnumCat->Release();
  60.   }
  61.   pSysDevEnum->Release();
  62.   return hr;
  63. }

To add an audio compressor to this filter graph, begin by enumerating all of the CLSID_AudioCompressorCategory filters in a method call to CreateClassEnumerator. You do this because

DirectShow treats the audio compressors as individual objects with their own class IDs. However, we have no

way of knowing the class ID of a given audio compressor—something we’ll need to instantiate a DirectShow

filter. In this case, we use the call to CreateClassEnumerator as a way of learning the class IDs of all audio

compressors available to DirectShow.

为了在filter graph中添加一个音频压缩filter,以调用CreateClassEnumerator.方法来枚举所有CLSID_AudioCompressorCategory filters开始。因为DirectShow将音频压缩作为独立的,有它自己的类ID来处理。然而,我们没有办法知道一个给定站音频压缩的class ID---有进我们需要实例一个DirectShow filter。但是我们可以通过调用CreateClassEnumerator来音频压缩的class ID。

 

Once we have the enumerated list, we step through it using the Next method, until we find an audio

compressor whose FriendlyName property matches the name we’re looking for. In this program, we’re

matching the name Windows Media Audio V2, a codec that should be included on your system. (If it’s not,

use GraphEdit to list the audio compressors installed on your system—see Figure 4-6—and change the

string to match one of those entries.)

当获得了枚举列表后,可以通过Next方法来逐个查找FriendlyName的匹配项。本例中,我们要的匹配名为“Windows Media Audio V2----这个应该是系统默认有的。(如果没有,则得要GraphEdit去查看下)。

When the matching object is found, it’s instantiated and returned to the caller. This function is nearly identical to EnumerateAudioInputFilters, so you can begin to see how these functions can easily be repurposed for your own application needs.

当找到匹配对象后,这个函数将返回调用者一个实例。这个函数和EnumerateAudioInputFilters,很像,因此可以将它们都应用到你自己的应用程序中

 

The only major difference in this version of the main function is that the returned audio compressor filter is

added to the filter graph using AddFilter and ConnectFilter, as shown here:

在main函数中这个版本的主要不同在下调用了AddFilter和ConnectFilter将生成了音频压缩filter并添加到了filter graph。代码如下所示:

点击(此处)折叠或打开

  1. // Now, enumerate the audio compressors.
  2. // Look for a match, which we'll return.
  3. hr = EnumerateAudioCompressorFilters((void**) &pAudioCompressor,
  4. L"Windows Media Audio V2");
  5. // Then add it to the filter graph.
  6. hr = pGraph->AddFilter(pAudioCompressor, L"Windows Media Audio V2");
  7. // Connect it to the audio input filter.
  8. hr = ConnectFilters(pGraph, (IBaseFilter*) pAudioInputFilter,
  9. pAudioCompressor);

We’ve also added a bit of substance to the File Writer filter. The File Writer filter exposes a number of

interfaces, including IFileSink, which can be used to set the name of the file, and IFileSink2, which can be

used to set the mode parameter for the File Writer filter. If the mode is AM_FILE_OVERWRITE, the file will

be deleted and re-created every time the filter graph begins execution. That’s the behavior we want, so we’ve included a few extra lines of code:

本例同样添加了写文件filter的代码。写文件filter提供了一些接口,包括有IFileSink,它用来设置文件名;和IFileSink2, 它用来设置写文件filter的模式参数。如果这个模式是AM_FILE_OVERWRITE, 那么每次的filter graph的执行都会删除原文件并重新生成一个新的文件。这种方式正是本例子中想要的,因此增加了额外的代码:

  1. // And we'll also destroy any existing file.
  2. hr = pFileWriter->QueryInterface(IID_IFileSinkFilter2, (void**)&pSink2);
  3. pSink2->SetMode(AM_FILE_OVERWRITE);

After you’ve run DSAudioCapCom, you’ll see that its file sizes increase much more slowly than DSAudioCap because the files are compressed. As you become familiar with the different types of audio

compression—some are better for voice, others for music—you might consider modifying the compression

filters used in your DirectShow programs on a task-dependent basis: different compression for different

sounds.

当运行DSAudiaCapCom后,可以看到相对于DSAudioCap,文件大小的增长要缓慢多了,这是因为文件进行了压缩。如果很熟悉不同类型的音频压缩—有些更适应于声音,而有些更适于音乐—就可以考虑使用这些效率更好的压缩filter。

 

Summary

In this chapter, we’ve covered how to discover and enumerate the various audio capture devices connected to a PC. Once we’ve learned the list of audio capture devices available to DirectShow, we’ve been able to build capture filter graphs and write these audio streams to disk files. DirectShow can be used as the basis of a powerful audio capture and editing application. (Editing is covered in Chapter 8.) Alternatively, you can use DirectShow for audio playback—although there are easier ways to do that, using DirectSound.

本章介绍了如何发现和枚举连接到本机的各种音频捕捉设备。一旦对于DS来说这些音频捕捉设备可用时,就可以用来创建捕捉filter graph并将音频流写到文件。DS可以用来作为基本且功能强大的的音频播放器或编辑应用中。(编辑应用在第八章中讨论)。同样的,也可以使用DS来实现音频播放—即使使用DirectSound会更简单。

Now we’ll move along to video capture with webcams. Here compression isn’t just an option; it’s a

necessity because video files can easily fill your entire hard disk in just a few minutes.

下章将会开始讨论捕捉摄像头的视频,这时压缩就很必要了。

 

最简单的基于DirectShow的示例:视频播放器自定义版 ===================================================== 最简单的基于DirectShow的示例文章列表: 最简单的基于DirectShow的示例:视频播放器 最简单的基于DirectShow的示例:视频播放器图形界面版 最简单的基于DirectShow的示例:视频播放器自定义版 最简单的基于DirectShow的示例:获取Filter信息 ===================================================== 阅读详情

相关推荐

DirectShow开发指南PDF及源码 陆其明(著)

本书以DirectX SDK 9.0版为蓝本,涉及的内容几乎涵盖了在Windows平台上使 用DirectShow进行C++编码的方方面面。全书共分4个部分。第1部分详细介绍了 DirectShow的基础知识。第2部分重点讨论了Filter的开发,以及DirectShow 应用程序的开发,包括目前非常流行的如音视频采集、数码摄像机的支持、非线性 编辑等应用。第3部深入分析了DirectShow SDK提供的部分典型源代码例子。第 4部分结合作者个人的一些开发实践,通过案例和开放源码分析,进一步介绍 DirectShow的实务应用。 本书完全忠实于DirectX SDK 9.0的帮助文档以及基类源代码,并结合作者多年 的实践,经过提炼而成。内容丰富,条理清晰,实用性强。适合广大的流媒体应用 开发人员,以及对Windows平台上多媒体处理感兴趣的编程爱好者、学生学习和参 考。

构建Filter的知识

1如何根据CLSID向graph中添加filter下面的代码演示了如何利用CLSID生成一个filter,然后将其加入到graph图中HRESULT AddFilterByCLSID( IGraphBuilder *pGraph, // Pointer to the Filter Graph Manager. const GUID& clsid, // CLSID of the f

lukeliuli的专栏 1846

DirectShow开发指南 pdf完整版附随书源码

DirectShow开发指南以DirectX SDK 9.0版为蓝本,内容几乎涵盖了在Windows平台上使用DirectShow进行C++编程的方方面面。 DirectShow开发指南共分4个部分。第1部分详细介绍了DirectShow的基础知识。第2部分重点讨论了Filter的开发,以及DirectShow应用程序的开发,包括目前非常流行的音视频采集、数码摄像机的支持、非线性编辑等应用。第3部分深入分析了DirectShow SDK提供的部分典型源代码。第4部分结合作者个人的一些开发实践,通过案例和开放源代码分析,进一步介绍DirectShow的实务应用。

《微软:DirectShow开发指南》第5章 Capturing Audio and Video from a Webcam

Webcams have become nearly ubiquitous peripherals for today’s PCs. They’re inexpensive and produce video imagery of varying quality—often dependent on how much the webcam cost or how fast your compute

北雨南萍 5972

DirectShow开发指南_部分2

DirectShow开发指南_部分2DirectShow开发指南_部分2DirectShow开发指南_部分2

国央企创新负责人如何通过平台实现科研成果的快速识别与转化?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

编写DirectShow Filters—Filters如何连接

1. PIN连接filters通过IPin接口连接pin。output pin连接到input pin。每个pin连接有一个media sample,通过am_media_type描述。一个应用程序通过调用在filter graph manager上的方法来连接filter,而不是通过filters或者pins上的方法。应用程序可以直接指定哪些filters连接,通过IFilterGraph::C

CP9的专栏 4764

实例讲解Simulink CAN通讯丢失故障判定模型搭建及仿真测试验证方法

本文通过ABS通讯丢失故障触发、判定条件Simulink模型的搭建和仿真的讲解,介绍了一种控制器CAN通讯丢失故障判定的方法和建模方法,希望能给相关技术人员带来一定参考和帮助。

qq_43011586的博客 3685

directshow开发指南 pdf_Java之父全新力作,亲自著作java学习书籍,pdf免费送

本书称第二谁敢称第一!java之父亲自著作的这本java学习书籍,你确定不看?这本书就是《java编程规范(第三版)》本书由java技术的发明者James Gosling编写,是java tm编程语言的权威性技术指南。如果你想知道语言之构造的精确含义,本书是最好的资源。全面、准确而详细地论及了java编程语言。它提供了对于自前一版以来添加的所有新特性的完整描述,包括泛型、注释、断言、自动装箱、枚举...

weixin_39721924的博客 201

安卓逆向实战:用Batch Apktool 3.8.0快速汉化APK的完整流程

本文详细解析了如何使用Batch Apktool 3.8.0进行安卓APK汉化的完整流程,包括环境配置、反编译、资源解析、深度汉化、重构建与签名优化等关键步骤。通过这款集成化逆向工具,开发者可以高效完成APK汉化,提升工作效率。

s1t2u3的博客 1099

国外无人机蜂群作战典型战例及发展趋势

参与袭击的无人机为简易组装的固定翼飞机,采用小排量活塞发动机和手机GPS通信模块零件,机翼为轻木加泡沫板混合结构,尾梁为铝合金方管,主要依靠胶带粘接,以上全部配件均可在商用市场买到,成本较低,可在短时间内装配完成。③战场纵深短、瞬时性强。蜂群作战的参战数量显著增加,这一方面得益于无人机技术的飞速发展使得廉价无人机可以大量生产和仿制,另一方面凭借迅速发展的投放和回收技术以及集群控制技术,使得无人机蜂群可在极短时间内实现编队饱和攻击,在快速消耗敌方高价值防空武器系统的同时,保证己方物资系统的突防概率。

renhongxia1的博客 2479

directshow开发指南 pdf_Web开发书籍整理

HTML5+canvas开发详解第2版.epub JavaScript - 框架设计 司徒正美.pdf JavaScript - 权威指南(第6版)(中文版) -(笔记版).pdf JavaScript - 权威指南(第6版)(中文版).pdf JavaScript - The Definitive Guide 6th.pdf 学习JavaScript数据结构与算法(第2版).epub JavaS...

weixin_39902472的博客 277

如何判断一个程序员的技术比你强?

技术大佬与普通程序员之间存在明显的认知差异:前者善于将复杂问题抽象为经典计算机科学问题,用声明式代码替代过程式逻辑;对异常和边界条件极度敏感,具备系统化的排查方法论;代码追求高可读性而非炫技,且执着于底层原理研究。这种技术压制体现在解决问题的确定性上——通过扎实的理论功底和工程直觉,快速定位问题本质,而非依赖试错或搜索。真正的技术实力源于对底层逻辑的深刻理解和对系统稳定性的敬畏。

wldtzzming的专栏 1088

directshow开发指南 pdf_《Django Web开发指南》初识Python框架!PythonPDF学习资料分享...

近期很多小伙伴私信小编帮忙推荐一本Diango的书籍!经过三四天的寻找,终于找到了这本《Django Web开发指南》的PDF电子版,现将之分享给大家,希望对大家有所帮助!同时小编也会赠送大家一套Python的视频学习教程!仅限100名哦本书讲述如何用Python框架Django构建出强大的Web解决方案,《Django Web开发指南》讲解了使用新的Django 1.0版的各种主要特性所需要的技...

weixin_39898011的博客 213

关于SVPWM七段式五段式调制后的相电压和线电压波形——大疆DJI技术笔试

1.SVPWM七段式/五段式调制的相电压和线电压波形 脉宽调制(PWM)在并网变流技术中得到了广泛的应用。与正弦脉宽调制(SPWM)的控制方法相比,SVPWM方法具有直流侧电压利用率高,电压谐波小,易于数字化实现等显著的优点,从而使SVPWM在实际的应用中越来越普遍。TMS320F28335是32位浮点的DSP控制器,具有150MHz的高速处理能力,18路PWM输出,与前几代数字信号控制器相比,性能平均提高了50%。其中具体的SVPWM理论实现的原理,请参考我的博客永磁同步电机的矢量控制策略(七)一一一SV

昔时扬尘处 2万+

Unity零基础到入门 ☀️| 小万字教程 对 Unity 中的 协程 ❤️全面解析+实战演练❤️

Unity,为了让Unity基础知识专栏更加完善,又肝了一篇协程来学习。 协程在Unity中还是很常用的,一起来看一下协程的基本概念和使用方法吧!

努力前行,总会成为自己心中的那道光 8万+

opencv VideoCapture 不能采集uvc摄像头的MJPEG 数据

bool bRet = capture.set(CV_CAP_PROP_FOURCC, CV_FOURCC('M', 'J', 'P', 'G')); if (!bRet) { printf("set format failed \n"); ...

smilestone322的专栏 3846

针对小缺陷目标:在YOLOv11中引入小目标检测层

摘要: 针对YOLOv11在工业检测中漏检微小目标(如焊点气泡)的问题,研究发现深层特征下采样导致小目标信息丢失。通过在YOLOv11中增加P2检测层(4倍下采样)并融合浅层高分辨率特征,可显著提升小目标召回率。关键优化包括:调整模型结构、谨慎设计数据增强策略(减少MixUp、增加小目标粘贴)、重新分配损失权重(提升P2层权重)、优化推理速度(动态分辨率、通道裁剪)。同时强调数据质量的重要性,建议根据具体场景特性(目标尺寸、分布)定制方案,而非盲目调整模型。

as1225120794的博客 366

Windows下用DirectShow查找摄像头(含分辨率)和麦克风

在视频聊天、视频会议、在线监控和视频展台等项目中,需要查找出本地电脑上连接的所有摄像头,网上流传比较多的方式是ffmpeg的方式,这种方式可以跨平台,不同的平台下调用不同的库。这种方式在控制台直接打印了摄像头的信息,无法(或者说我暂时没找到)在内存中获取,因此直接采用了DirectShow的方式,DirectShow枚举IMoniker和Ipin。因为网上的文档,不是特别详尽,所以...

架构和代码 8869

【云原生进阶之容器】第六章容器网络6.6.2--Cilium部署

部署Cilium非常简单,可以通过单独的yaml文件部署全部组件(目前我使用了这个方式部署了1.7.1版本),也可以通过Helm Chart一键完成。重要的是部署环境和时机:官方建议所有部署节点都使用Linux最新稳定内核版本,这样所有的功能都能启用。作为一个Kubernetes网络组件,它应该在部署Kubernetes其他基础组件之后,才进行部署。

junbaozi的专栏 792

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

directshow的中文资料之设备列举和捕捉接口

这篇解释和示例如何通过DirectShow的接口去初始化和访问系统的硬件设备。代表性的,DirectShow应用程序使用下面类型的硬件。   音/视频捕捉卡   音频或视频回放卡   音频或视频压缩或解压卡(象MPEG解码器)   下面将以AV设备作参考。   如何列举设备   包括在DirectShowSDK中的接口,类,和例子提供了音/视频捕捉和回放的功能。因为文件源过滤器和fil

L_yangliu的专栏 957

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Directshow获取系统默认设备

下面函数为通过Directshow API 获取设备列表的函数,pEnumCat->Next的第0个设备为系统默认设备 REFGUID guidValue : CLSID_AudioInputDeviceCategory(麦克风);  CLSID_AudioRendererCategory(扬声器);  CLSID_VideoInputDeviceCategory(Camera)

cffishappy的专栏 1755

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

上一篇: 《微软:DirectShow开发指南》第三章 Programming DirectShow Applications
下一篇: 《微软:DirectShow开发指南》第5章 Capturing Audio and Video from a Webcam
北雨南萍
北雨南萍 领域专家: 操作系统技术领域 领域专家: 操作系统技术领域
博客等级 码龄20年 2665粉丝 453原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值